mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
feat: Add cloud info command (spawn <cloud>) for cloud-first discovery (#192)
Users who know their preferred cloud but not which agents are available had no way to find out. Now `spawn hetzner` shows all agents available on Hetzner, mirroring how `spawn claude` shows all clouds for Claude. - Add cmdCloudInfo() showing cloud details + available agents - handleDefaultCommand detects cloud names and routes to cloud info - Update help text and clouds list footer to document the new command - Bump CLI version to 0.2.15 Agent: ux-engineer Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
17e78b9f04
commit
d1ecbaf88c
3 changed files with 67 additions and 4 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.2.14",
|
||||
"version": "0.2.15",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -459,7 +459,7 @@ export async function cmdClouds(): Promise<void> {
|
|||
console.log(` ${pc.green(key.padEnd(NAME_COLUMN_WIDTH))} ${c.name.padEnd(NAME_COLUMN_WIDTH)} ${pc.dim(`${implCount} agent${implCount !== 1 ? "s" : ""} ${c.description}`)}`);
|
||||
}
|
||||
console.log();
|
||||
console.log(pc.dim(` Run ${pc.cyan("spawn <agent> <cloud>")} to launch.`));
|
||||
console.log(pc.dim(` Run ${pc.cyan("spawn <cloud>")} for details, or ${pc.cyan("spawn <agent> <cloud>")} to launch.`));
|
||||
console.log();
|
||||
}
|
||||
|
||||
|
|
@ -491,6 +491,53 @@ export async function cmdAgentInfo(agent: string): Promise<void> {
|
|||
console.log();
|
||||
}
|
||||
|
||||
// ── Cloud Info ─────────────────────────────────────────────────────────────────
|
||||
|
||||
// Validate and load cloud - consolidates the pattern used by cmdCloudInfo
|
||||
async function validateAndGetCloud(cloud: string): Promise<[manifest: Manifest, cloudKey: string]> {
|
||||
try {
|
||||
validateIdentifier(cloud, "Cloud name");
|
||||
} catch (err) {
|
||||
p.log.error(getErrorMessage(err));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
validateNonEmptyString(cloud, "Cloud name", "spawn clouds");
|
||||
const manifest = await loadManifestWithSpinner();
|
||||
validateCloud(manifest, cloud);
|
||||
|
||||
return [manifest, cloud];
|
||||
}
|
||||
|
||||
export async function cmdCloudInfo(cloud: string): Promise<void> {
|
||||
const [manifest, cloudKey] = await validateAndGetCloud(cloud);
|
||||
|
||||
const c = manifest.clouds[cloudKey];
|
||||
console.log();
|
||||
console.log(`${pc.bold(c.name)} ${pc.dim("--")} ${c.description}`);
|
||||
if (c.notes) {
|
||||
console.log(pc.dim(` ${c.notes}`));
|
||||
}
|
||||
console.log();
|
||||
console.log(pc.bold("Available agents:"));
|
||||
console.log();
|
||||
|
||||
let found = false;
|
||||
for (const agent of agentKeys(manifest)) {
|
||||
const status = matrixStatus(manifest, cloudKey, agent);
|
||||
if (status === "implemented") {
|
||||
const a = manifest.agents[agent];
|
||||
console.log(` ${pc.green(agent.padEnd(NAME_COLUMN_WIDTH))} ${a.name.padEnd(NAME_COLUMN_WIDTH)} ${pc.dim("spawn " + agent + " " + cloudKey)}`);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
console.log(pc.dim(" No implemented agents yet."));
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
|
||||
// ── Update ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
export async function cmdUpdate(): Promise<void> {
|
||||
|
|
@ -549,6 +596,7 @@ ${pc.bold("USAGE")}
|
|||
spawn <agent> <cloud> --prompt-file <file>
|
||||
Execute agent with prompt from file
|
||||
spawn <agent> Show available clouds for agent
|
||||
spawn <cloud> Show available agents for cloud
|
||||
spawn list Full matrix table
|
||||
spawn agents List all agents with descriptions
|
||||
spawn clouds List all cloud providers
|
||||
|
|
@ -566,6 +614,7 @@ ${pc.bold("EXAMPLES")}
|
|||
spawn claude sprite --prompt-file instructions.txt
|
||||
${pc.dim("# Read prompt from file")}
|
||||
spawn claude ${pc.dim("# Show which clouds support Claude")}
|
||||
spawn hetzner ${pc.dim("# Show which agents run on Hetzner")}
|
||||
spawn list ${pc.dim("# See the full agent x cloud matrix")}
|
||||
|
||||
${pc.bold("AUTHENTICATION")}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import {
|
|||
cmdAgents,
|
||||
cmdClouds,
|
||||
cmdAgentInfo,
|
||||
cmdCloudInfo,
|
||||
cmdUpdate,
|
||||
cmdHelp,
|
||||
} from "./commands.js";
|
||||
import pkg from "../package.json" with { type: "json" };
|
||||
import { checkForUpdates } from "./update-check.js";
|
||||
import { loadManifest } from "./manifest.js";
|
||||
|
||||
const VERSION = pkg.version;
|
||||
|
||||
|
|
@ -56,7 +58,13 @@ const HELP_FLAGS = ["--help", "-h", "help"];
|
|||
async function handleDefaultCommand(agent: string, cloud: string | undefined, prompt?: string): Promise<void> {
|
||||
// Handle "spawn <agent> --help" / "spawn <agent> -h" / "spawn <agent> help"
|
||||
if (cloud && HELP_FLAGS.includes(cloud)) {
|
||||
await cmdAgentInfo(agent);
|
||||
// Could be "spawn <agent> --help" or "spawn <cloud> --help"
|
||||
const manifest = await loadManifest();
|
||||
if (!manifest.agents[agent] && manifest.clouds[agent]) {
|
||||
await cmdCloudInfo(agent);
|
||||
} else {
|
||||
await cmdAgentInfo(agent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cloud) {
|
||||
|
|
@ -67,7 +75,13 @@ async function handleDefaultCommand(agent: string, cloud: string | undefined, pr
|
|||
console.error(`\nUsage: spawn ${agent} <cloud> --prompt "your prompt here"`);
|
||||
process.exit(1);
|
||||
}
|
||||
await cmdAgentInfo(agent);
|
||||
// "spawn <name>" with no second arg: show agent info, or cloud info if it's a cloud name
|
||||
const manifest = await loadManifest();
|
||||
if (!manifest.agents[agent] && manifest.clouds[agent]) {
|
||||
await cmdCloudInfo(agent);
|
||||
} else {
|
||||
await cmdAgentInfo(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue