diff --git a/packages/cli/src/commands/agent/open.ts b/packages/cli/src/commands/agent/open.ts index 2119bd130b..2c1eab36d2 100644 --- a/packages/cli/src/commands/agent/open.ts +++ b/packages/cli/src/commands/agent/open.ts @@ -1,5 +1,6 @@ import type { Command } from "commander"; import { connectToDaemon } from "../../utils/client.js"; +import type { AgentDeepLinkTarget } from "@getpaseo/protocol/agent-deep-link"; import { openDesktopWithAgent } from "../open.js"; import type { CommandError, @@ -30,10 +31,21 @@ export function addOpenOptions(command: Command): Command { .option("--server ", "Server ID (defaults to the local daemon)"); } -async function resolveServerId(options: CommandOptions): Promise { +function toOpenError(err: unknown): CommandError { + if (err && typeof err === "object" && "code" in err) { + return err as CommandError; + } + const message = err instanceof Error ? err.message : String(err); + return { code: "OPEN_FAILED", message: `Failed to open agent: ${message}` }; +} + +async function resolveAgentTarget( + agentId: string, + options: CommandOptions, +): Promise { const explicitServerId = typeof options.server === "string" ? options.server.trim() : ""; if (explicitServerId) { - return explicitServerId; + return { serverId: explicitServerId, agentId }; } const client = await connectToDaemon({ target: options.daemonTarget }); @@ -46,7 +58,19 @@ async function resolveServerId(options: CommandOptions): Promise { }; throw error; } - return serverId; + + const fetchResult = await client.fetchAgent({ agentId }); + if (!fetchResult) { + const error: CommandError = { + code: "AGENT_NOT_FOUND", + message: `Agent not found: ${agentId}`, + details: 'Use "paseo ls" to list available agents', + }; + throw error; + } + return { serverId, agentId: fetchResult.agent.id }; + } catch (err) { + throw toOpenError(err); } finally { await client.close().catch(() => {}); } @@ -66,12 +90,12 @@ export async function runOpenCommand( throw error; } - const serverId = await resolveServerId(options); - await openDesktopWithAgent({ serverId, agentId }); + const target = await resolveAgentTarget(agentId, options); + await openDesktopWithAgent(target); return { type: "single", - data: { agentId, serverId, status: "opened" }, + data: { ...target, status: "opened" }, schema: openAgentSchema, }; } diff --git a/packages/cli/tests/42-agent-open.test.ts b/packages/cli/tests/42-agent-open.test.ts new file mode 100644 index 0000000000..d468dfe16d --- /dev/null +++ b/packages/cli/tests/42-agent-open.test.ts @@ -0,0 +1,52 @@ +#!/usr/bin/env npx tsx + +import assert from "node:assert"; +import { chmod, mkdir, readFile, writeFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; +import { runPaseoCli, startTestDaemon } from "./helpers/test-daemon.ts"; + +console.log("=== Agent Open Command Tests ===\n"); + +if (process.platform !== "linux") { + console.log("Skipped: the fake Desktop app is a Linux AppImage path."); + process.exit(0); +} + +const daemon = await startTestDaemon(); + +// runPaseoCli sets HOME to the daemon's home, where the CLI looks for +// ~/Applications/Paseo.AppImage on Linux. The fake app records its launch. +const launchRecord = join(daemon.paseoHome, "desktop-launches.txt"); +const fakeDesktop = join(daemon.paseoHome, "Applications", "Paseo.AppImage"); +await mkdir(join(daemon.paseoHome, "Applications"), { recursive: true }); +await writeFile(fakeDesktop, `#!/bin/sh\necho "$@" >> "${launchRecord}"\n`); +await chmod(fakeDesktop, 0o755); + +try { + { + console.log("Test 1: an unknown agent ID fails without opening Desktop"); + const result = await runPaseoCli(daemon, [ + "agent", + "open", + "does-not-exist", + "--host", + `127.0.0.1:${daemon.port}`, + "--json", + ]); + + assert.notStrictEqual(result.exitCode, 0, "open should fail for an unknown agent"); + assert.match(JSON.parse(result.stderr).error.message, /Agent not found: does-not-exist/); + // Give a wrongly spawned detached app time to write its record. + await new Promise((resolve) => setTimeout(resolve, 500)); + assert.ok( + !existsSync(launchRecord), + `Desktop should not open, but was launched with: ${existsSync(launchRecord) ? await readFile(launchRecord, "utf8") : ""}`, + ); + console.log("✓ unknown agent ID fails without opening Desktop\n"); + } +} finally { + await daemon.stop(); +} + +console.log("=== All agent open tests passed ===");