diff --git a/packages/cli/src/utils/client.ts b/packages/cli/src/utils/client.ts index 38d5b26571..022979876e 100644 --- a/packages/cli/src/utils/client.ts +++ b/packages/cli/src/utils/client.ts @@ -52,13 +52,20 @@ export function buildDaemonConnectionCommandError(options: ConnectOptions & { er return { code, message: `Cannot connect to daemon at ${describeDaemonTarget(options.target)}: ${message}`, - details: - options.target.kind === "instance" - ? `Start with: paseo daemon start --home ${JSON.stringify(options.target.home)}` - : "Check the selected endpoint and credentials. SSH transport does not install or start the daemon.", + details: describeConnectionRemedy(code, options.target), }; } +function describeConnectionRemedy(code: string, target: DaemonTarget): string { + if (code === "AUTH_REQUIRED") + return "The daemon requires a password. Set PASEO_PASSWORD and retry."; + if (code === "AUTH_FAILED") + return "The daemon rejected the password. Check PASEO_PASSWORD and retry."; + if (target.kind === "instance") + return `Start with: paseo daemon start --home ${JSON.stringify(target.home)}`; + return "Check the selected endpoint and credentials. SSH transport does not install or start the daemon."; +} + export function normalizeDaemonHost(raw: string): string | null { const trimmed = raw.trim(); if (!trimmed) { diff --git a/packages/cli/tests/41-daemon-auth-command-errors.test.ts b/packages/cli/tests/41-daemon-auth-command-errors.test.ts new file mode 100644 index 0000000000..a3891f3bf7 --- /dev/null +++ b/packages/cli/tests/41-daemon-auth-command-errors.test.ts @@ -0,0 +1,51 @@ +#!/usr/bin/env npx tsx + +import assert from "node:assert"; +import { runLocalPaseo } from "./helpers/local-cli.ts"; +import { startTestDaemon } from "./helpers/test-daemon.ts"; + +console.log("=== Daemon Auth Command Errors ===\n"); + +const daemon = await startTestDaemon({ + env: { PASEO_PASSWORD: "shared-secret" }, +}); + +async function lsError(password: string) { + const result = await runLocalPaseo(["ls", "--json"], { + PASEO_HOME: daemon.paseoHome, + PASEO_HOST: "", + PASEO_PASSWORD: password, + }); + assert.notStrictEqual(result.exitCode, 0, "ls should fail without a valid password"); + return JSON.parse(result.stderr).error as { + code: string; + message: string; + details: string; + }; +} + +try { + { + console.log("Test 1: missing password asks for PASEO_PASSWORD, not a daemon start"); + const error = await lsError(""); + assert.strictEqual(error.code, "AUTH_REQUIRED"); + assert.match(error.message, /Password required/); + assert.match(error.details, /PASEO_PASSWORD/); + assert.doesNotMatch(error.details, /daemon start/); + console.log("✓ missing password points at PASEO_PASSWORD\n"); + } + + { + console.log("Test 2: wrong password asks for PASEO_PASSWORD, not a daemon start"); + const error = await lsError("wrong-secret"); + assert.strictEqual(error.code, "AUTH_FAILED"); + assert.match(error.message, /Incorrect password/); + assert.match(error.details, /PASEO_PASSWORD/); + assert.doesNotMatch(error.details, /daemon start/); + console.log("✓ wrong password points at PASEO_PASSWORD\n"); + } +} finally { + await daemon.stop(); +} + +console.log("=== Daemon Auth Command Errors Tests Passed ===");