fix: improve credential guidance in error messages and quick-start hints (#427)

- Show cloud provider URL alongside credential env vars in quick-start
  sections (both `spawn <agent>` and `spawn <cloud>` info views)
- Restructure script failure errors: separate credential issues from
  other causes, inline the `spawn <cloud>` hint next to cloud credentials
- Replace "Check cloud-specific READMEs" with actionable `spawn <cloud>`
  in help troubleshooting section
- Show concise 4-line guidance instead of full help dump when spawn is
  run without a TTY (e.g. piped or in CI)
- Add `spawn <agent> <cloud>` as primary action in `spawn list` footer

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-11 03:42:55 -08:00 committed by GitHub
parent bb4f0c29df
commit 5c4f830fea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 21 additions and 14 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "0.2.34",
"version": "0.2.35",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -434,12 +434,12 @@ async function execScript(cloud: string, agent: string, prompt?: string): Promis
}
p.log.error("Spawn script failed");
console.error("\nError:", errMsg);
console.error("\nCommon causes:");
console.error(" - Missing OPENROUTER_API_KEY (get one at https://openrouter.ai/settings/keys)");
console.error(" - Missing cloud provider credentials (API key, token, etc.)");
console.error("\nCheck your credentials:");
console.error(` - OPENROUTER_API_KEY ${pc.dim("https://openrouter.ai/settings/keys")}`);
console.error(` - Cloud credentials ${pc.dim(`run ${pc.cyan(`spawn ${cloud}`)} for setup instructions`)}`);
console.error("\nOther causes:");
console.error(" - Cloud provider API rate limit or quota exceeded");
console.error(" - Missing local dependencies (SSH, curl, etc.)");
console.error(`\nRun ${pc.cyan(`spawn ${cloud}`)} to see setup instructions for this provider.`);
process.exit(1);
}
}
@ -594,7 +594,7 @@ export async function cmdList(): Promise<void> {
console.log(`${pc.green("+")} implemented ${pc.dim("-")} not yet available`);
}
console.log(pc.green(`${impl}/${total} combinations implemented`));
console.log(pc.dim(`Run ${pc.cyan("spawn <agent>")} or ${pc.cyan("spawn <cloud>")} for details.`));
console.log(pc.dim(`Launch: ${pc.cyan("spawn <agent> <cloud>")} | Details: ${pc.cyan("spawn <agent>")} or ${pc.cyan("spawn <cloud>")}`));
console.log();
}
@ -707,13 +707,14 @@ export async function cmdAgentInfo(agent: string): Promise<void> {
// Show quick-start with first available cloud
if (implClouds.length > 0) {
const exampleCloud = implClouds[0];
const authVars = parseAuthEnvVars(manifest.clouds[exampleCloud].auth);
const cloudName = manifest.clouds[exampleCloud].name;
const cloudDef = manifest.clouds[exampleCloud];
const authVars = parseAuthEnvVars(cloudDef.auth);
console.log();
console.log(pc.bold("Quick start:"));
console.log(` ${pc.cyan("export OPENROUTER_API_KEY=sk-or-v1-...")} ${pc.dim("# https://openrouter.ai/settings/keys")}`);
if (authVars.length > 0) {
console.log(` ${pc.cyan(`export ${authVars[0]}=...`)} ${pc.dim(`# ${cloudName} credential`)}`);
const hint = cloudDef.url ? ` ${pc.dim(`# ${cloudDef.url}`)}` : ` ${pc.dim(`# ${cloudDef.name} credential`)}`;
console.log(` ${pc.cyan(`export ${authVars[0]}=...`)}${hint}`);
}
console.log(` ${pc.cyan(`spawn ${agentKey} ${exampleCloud}`)}`);
}
@ -757,7 +758,7 @@ async function validateAndGetCloud(cloud: string): Promise<[manifest: Manifest,
/** Print quick-start auth instructions for a cloud provider */
function printCloudQuickStart(
cloud: { auth: string },
cloud: { auth: string; url?: string },
authVars: string[],
exampleAgent: string | undefined,
cloudKey: string
@ -766,8 +767,9 @@ function printCloudQuickStart(
console.log(pc.bold("Quick start:"));
console.log(` ${pc.cyan("export OPENROUTER_API_KEY=sk-or-v1-...")} ${pc.dim("# https://openrouter.ai/settings/keys")}`);
if (authVars.length > 0) {
const hint = cloud.url ? ` ${pc.dim(`# ${cloud.url}`)}` : "";
for (const v of authVars) {
console.log(` ${pc.cyan(`export ${v}=your-${v.toLowerCase().replace(/_/g, "-")}-here`)}`);
console.log(` ${pc.cyan(`export ${v}=...`)}${hint}`);
}
} else if (cloud.auth.toLowerCase() !== "none") {
console.log(` ${pc.dim(`Auth: ${cloud.auth}`)}`);
@ -921,7 +923,7 @@ ${pc.bold("INSTALL")}
${pc.bold("TROUBLESHOOTING")}
${pc.dim("*")} Script not found: Run ${pc.cyan("spawn list")} to verify the combination exists
${pc.dim("*")} Missing credentials: Check cloud-specific READMEs in the repo
${pc.dim("*")} Missing credentials: Run ${pc.cyan("spawn <cloud>")} to see setup instructions
${pc.dim("*")} Update issues: Try ${pc.cyan("spawn update")} or reinstall manually
${pc.dim("*")} Garbled unicode: Set ${pc.cyan("SPAWN_NO_UNICODE=1")} for ASCII-only output
${pc.dim("*")} Slow startup: Set ${pc.cyan("SPAWN_NO_UPDATE_CHECK=1")} to skip auto-update

View file

@ -237,8 +237,13 @@ async function handleNoCommand(prompt: string | undefined): Promise<void> {
await cmdInteractive();
} else {
console.error(pc.yellow("No interactive terminal detected."));
console.error(pc.dim(`To launch directly: ${pc.cyan("spawn <agent> <cloud>")}\n`));
cmdHelp();
console.error();
console.error(` Launch directly: ${pc.cyan("spawn <agent> <cloud>")}`);
console.error(` Browse agents: ${pc.cyan("spawn agents")}`);
console.error(` Browse clouds: ${pc.cyan("spawn clouds")}`);
console.error(` Full help: ${pc.cyan("spawn help")}`);
console.error();
process.exit(1);
}
}