Files
clawdbot/src/cli/models-cli.ts

439 lines
12 KiB
TypeScript

import type { Command } from "commander";
import {
modelsAliasesAddCommand,
modelsAliasesListCommand,
modelsAliasesRemoveCommand,
modelsAuthAddCommand,
modelsAuthOrderClearCommand,
modelsAuthOrderGetCommand,
modelsAuthOrderSetCommand,
modelsAuthPasteTokenCommand,
modelsAuthSetupTokenCommand,
modelsFallbacksAddCommand,
modelsFallbacksClearCommand,
modelsFallbacksListCommand,
modelsFallbacksRemoveCommand,
modelsImageFallbacksAddCommand,
modelsImageFallbacksClearCommand,
modelsImageFallbacksListCommand,
modelsImageFallbacksRemoveCommand,
modelsListCommand,
modelsScanCommand,
modelsSetCommand,
modelsSetImageCommand,
modelsStatusCommand,
} from "../commands/models.js";
import { defaultRuntime } from "../runtime.js";
export function registerModelsCli(program: Command) {
const models = program
.command("models")
.description("Model discovery, scanning, and configuration")
.option(
"--status-json",
"Output JSON (alias for `models status --json`)",
false,
)
.option(
"--status-plain",
"Plain output (alias for `models status --plain`)",
false,
);
models
.command("list")
.description("List models (configured by default)")
.option("--all", "Show full model catalog", false)
.option("--local", "Filter to local models", false)
.option("--provider <name>", "Filter by provider")
.option("--json", "Output JSON", false)
.option("--plain", "Plain line output", false)
.action(async (opts) => {
try {
await modelsListCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
models
.command("status")
.description("Show configured model state")
.option("--json", "Output JSON", false)
.option("--plain", "Plain output", false)
.option(
"--check",
"Exit non-zero if auth is expiring/expired (1=expired/missing, 2=expiring)",
false,
)
.action(async (opts) => {
try {
await modelsStatusCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
models
.command("set")
.description("Set the default model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsSetCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
models
.command("set-image")
.description("Set the image model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsSetImageCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
const aliases = models.command("aliases").description("Manage model aliases");
aliases
.command("list")
.description("List model aliases")
.option("--json", "Output JSON", false)
.option("--plain", "Plain output", false)
.action(async (opts) => {
try {
await modelsAliasesListCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
aliases
.command("add")
.description("Add or update a model alias")
.argument("<alias>", "Alias name")
.argument("<model>", "Model id or alias")
.action(async (alias: string, model: string) => {
try {
await modelsAliasesAddCommand(alias, model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
aliases
.command("remove")
.description("Remove a model alias")
.argument("<alias>", "Alias name")
.action(async (alias: string) => {
try {
await modelsAliasesRemoveCommand(alias, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
const fallbacks = models
.command("fallbacks")
.description("Manage model fallback list");
fallbacks
.command("list")
.description("List fallback models")
.option("--json", "Output JSON", false)
.option("--plain", "Plain output", false)
.action(async (opts) => {
try {
await modelsFallbacksListCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
fallbacks
.command("add")
.description("Add a fallback model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsFallbacksAddCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
fallbacks
.command("remove")
.description("Remove a fallback model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsFallbacksRemoveCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
fallbacks
.command("clear")
.description("Clear all fallback models")
.action(async () => {
try {
await modelsFallbacksClearCommand(defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
const imageFallbacks = models
.command("image-fallbacks")
.description("Manage image model fallback list");
imageFallbacks
.command("list")
.description("List image fallback models")
.option("--json", "Output JSON", false)
.option("--plain", "Plain output", false)
.action(async (opts) => {
try {
await modelsImageFallbacksListCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
imageFallbacks
.command("add")
.description("Add an image fallback model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsImageFallbacksAddCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
imageFallbacks
.command("remove")
.description("Remove an image fallback model")
.argument("<model>", "Model id or alias")
.action(async (model: string) => {
try {
await modelsImageFallbacksRemoveCommand(model, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
imageFallbacks
.command("clear")
.description("Clear all image fallback models")
.action(async () => {
try {
await modelsImageFallbacksClearCommand(defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
models
.command("scan")
.description("Scan OpenRouter free models for tools + images")
.option("--min-params <b>", "Minimum parameter size (billions)")
.option("--max-age-days <days>", "Skip models older than N days")
.option("--provider <name>", "Filter by provider prefix")
.option("--max-candidates <n>", "Max fallback candidates", "6")
.option("--timeout <ms>", "Per-probe timeout in ms")
.option("--concurrency <n>", "Probe concurrency")
.option("--no-probe", "Skip live probes; list free candidates only")
.option("--yes", "Accept defaults without prompting", false)
.option("--no-input", "Disable prompts (use defaults)")
.option(
"--set-default",
"Set agents.defaults.model to the first selection",
false,
)
.option(
"--set-image",
"Set agents.defaults.imageModel to the first image selection",
false,
)
.option("--json", "Output JSON", false)
.action(async (opts) => {
try {
await modelsScanCommand(opts, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
models.action(async (opts) => {
try {
await modelsStatusCommand(
{
json: Boolean(opts?.statusJson),
plain: Boolean(opts?.statusPlain),
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
const auth = models.command("auth").description("Manage model auth profiles");
auth
.command("add")
.description("Interactive auth helper (setup-token or paste token)")
.action(async () => {
try {
await modelsAuthAddCommand({}, defaultRuntime);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
auth
.command("setup-token")
.description("Run a provider CLI to create/sync a token (TTY required)")
.option("--provider <name>", "Provider id (default: anthropic)")
.option("--yes", "Skip confirmation", false)
.action(async (opts) => {
try {
await modelsAuthSetupTokenCommand(
{
provider: opts.provider as string | undefined,
yes: Boolean(opts.yes),
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
auth
.command("paste-token")
.description("Paste a token into auth-profiles.json and update config")
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
.option("--profile-id <id>", "Auth profile id (default: <provider>:manual)")
.option(
"--expires-in <duration>",
"Optional expiry duration (e.g. 365d, 12h). Stored as absolute expiresAt.",
)
.action(async (opts) => {
try {
await modelsAuthPasteTokenCommand(
{
provider: opts.provider as string | undefined,
profileId: opts.profileId as string | undefined,
expiresIn: opts.expiresIn as string | undefined,
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
const order = auth
.command("order")
.description("Manage per-agent auth profile order overrides");
order
.command("get")
.description("Show per-agent auth order override (from auth-profiles.json)")
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
.option("--agent <id>", "Agent id (default: configured default agent)")
.option("--json", "Output JSON", false)
.action(async (opts) => {
try {
await modelsAuthOrderGetCommand(
{
provider: opts.provider as string,
agent: opts.agent as string | undefined,
json: Boolean(opts.json),
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
order
.command("set")
.description(
"Set per-agent auth order override (locks rotation to this list)",
)
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
.option("--agent <id>", "Agent id (default: configured default agent)")
.argument("<profileIds...>", "Auth profile ids (e.g. anthropic:claude-cli)")
.action(async (profileIds: string[], opts) => {
try {
await modelsAuthOrderSetCommand(
{
provider: opts.provider as string,
agent: opts.agent as string | undefined,
order: profileIds,
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
order
.command("clear")
.description(
"Clear per-agent auth order override (fall back to config/round-robin)",
)
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
.option("--agent <id>", "Agent id (default: configured default agent)")
.action(async (opts) => {
try {
await modelsAuthOrderClearCommand(
{
provider: opts.provider as string,
agent: opts.agent as string | undefined,
},
defaultRuntime,
);
} catch (err) {
defaultRuntime.error(String(err));
defaultRuntime.exit(1);
}
});
}