Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 14 additions & 12 deletions src/commands/instruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,26 @@ Global flags (apply to every command):
- \`routstr models list [--provider <name>]\` — pricing for all enabled models

### Providers (admin-only)
- \`routstr providers list -t <admin>\` — upstream providers
- \`routstr providers show <id> -t <admin>\` — full provider details
- \`routstr providers add <type> --base-url <url> --api-key <key> -t <admin>\`
- \`routstr providers update <id> [--type|--base-url|--api-key|--api-version|--enabled <bool>|--fee <mult>|--settings <json>] -t <admin>\`
- \`routstr providers enable <id> -t <admin>\` / \`disable <id>\`
- \`routstr providers remove <id> -t <admin>\`
- \`routstr providers test <id> -t <admin>\` — health-check upstream
Provider refs accept either numeric ID or stable slug. Prefer slugs in automation.
- \`routstr providers list -t <admin>\` — upstream providers, including stable \`slug\`
- \`routstr providers show <provider> -t <admin>\` — full provider details by ID or slug
- \`routstr providers add <type> --base-url <url> --api-key <key> [--slug <slug>] -t <admin>\`
- \`routstr providers update <provider> [--type|--base-url|--api-key|--api-version|--enabled <bool>|--fee <mult>|--settings <json>|--slug <slug>] -t <admin>\`
- \`routstr providers enable <provider> -t <admin>\` / \`disable <provider>\`
- \`routstr providers remove <provider> -t <admin>\`
- \`routstr providers test <provider> -t <admin>\` — health-check upstream

### Provider models (per-model edits scoped to a provider; admin-only)
- \`routstr providers models list <pid> [--source all|db|remote] -t <admin>\` — DB + upstream-discovered models
- \`routstr providers models show <pid> <model_id> -t <admin>\` — full model detail
- \`routstr providers models update <pid> <model_id> [--forwarded-model-id <id>|--enabled <bool>|--name|--description] -t <admin>\`
Use the provider slug from \`routstr providers list -t <admin> -o json\` as \`<provider>\` when possible.
- \`routstr providers models list <provider> [--source all|db|remote] -t <admin>\` — DB + upstream-discovered models
- \`routstr providers models show <provider> <model_id> -t <admin>\` — full model detail
- \`routstr providers models update <provider> <model_id> [--forwarded-model-id <id>|--enabled <bool>|--name|--description] -t <admin>\`

Batch update example (loop over ids):
\`\`\`
routstr providers models list <pid> --source db -o json -t <admin> \\
routstr providers models list <provider-slug> --source db -o json -t <admin> \\
| jq -r '.db_models[].id' \\
| xargs -I{} routstr providers models update <pid> {} --forwarded-model-id {} -t <admin>
| xargs -I{} routstr providers models update <provider-slug> {} --forwarded-model-id {} -t <admin>
\`\`\`

### Wallet
Expand Down
8 changes: 5 additions & 3 deletions src/commands/provider-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function parseBool(value: string): boolean {
}

interface ProviderModelsResponse {
provider: { id: number; provider_type: string; base_url: string };
provider: { id: number; slug?: string | null; provider_type: string; base_url: string };
db_models: AdminModel[];
remote_models: AdminModel[];
}
Expand Down Expand Up @@ -79,13 +79,15 @@ export async function providerModelsListCommand(
rows.push([m.id, "remote", "—", "—", m.name ?? ""]);
}

const providerLabel = d.provider.slug ?? `#${d.provider.id}`;

if (!rows.length) {
printInfo(`No models for provider #${d.provider.id} (source=${source}).`);
printInfo(`No models for provider ${providerLabel} (source=${source}).`);
return;
}

printTable(
`Models for provider #${d.provider.id} — ${d.provider.provider_type} (${rows.length})`,
`Models for provider ${providerLabel} — ${d.provider.provider_type} (${rows.length})`,
["ID", "Source", "Enabled", "Forwarded ID", "Name"],
rows,
);
Expand Down
13 changes: 10 additions & 3 deletions src/commands/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export async function providersListCommand(opts: { adminToken?: string }): Promi
const resp = await fetchJson<
Array<{
id: number;
slug?: string | null;
provider_type: string;
base_url: string;
enabled: boolean;
Expand All @@ -34,14 +35,15 @@ export async function providersListCommand(opts: { adminToken?: string }): Promi
}
const rows = items.map((p) => [
String(p.id),
p.slug ?? "—",
p.provider_type,
p.base_url,
p.enabled ? "yes" : "no",
String(p.provider_fee),
]);
printTable(
`Upstream Providers (${items.length})`,
["ID", "Type", "URL", "Enabled", "Fee"],
["ID", "Slug", "Type", "URL", "Enabled", "Fee"],
rows,
);
});
Expand Down Expand Up @@ -77,7 +79,7 @@ export async function providersListCommand(opts: { adminToken?: string }): Promi

export async function providersAddCommand(
name: string,
opts: { apiKey?: string; baseUrl?: string; adminToken?: string },
opts: { apiKey?: string; baseUrl?: string; adminToken?: string; slug?: string },
): Promise<void> {
const token = resolveToken(opts.adminToken);
const url = `${nodeUrl()}/admin/api/upstream-providers`;
Expand All @@ -86,6 +88,7 @@ export async function providersAddCommand(
base_url: opts.baseUrl ?? "",
api_key: opts.apiKey ?? "",
};
if (opts.slug !== undefined) body.slug = opts.slug;

const headers: Record<string, string> = {
"Content-Type": "application/json",
Expand Down Expand Up @@ -162,6 +165,7 @@ export async function providersTestCommand(

interface UpstreamProviderDetail {
id: number;
slug?: string | null;
provider_type: string;
base_url: string;
api_key: string;
Expand All @@ -186,6 +190,7 @@ export async function providersShowCommand(

render(data, (p) => {
printInfo(`\x1b[1mProvider #${p.id}\x1b[0m`);
if (p.slug) printInfo(` Slug: ${p.slug}`);
printInfo(` Type: ${p.provider_type}`);
printInfo(` Base URL: ${p.base_url}`);
printInfo(` API key: ${p.api_key || "(none)"}`);
Expand All @@ -207,6 +212,7 @@ interface UpdateProviderOptions {
enabled?: string;
fee?: string;
settings?: string;
slug?: string;
}

function parseBool(value: string): boolean {
Expand Down Expand Up @@ -251,10 +257,11 @@ export async function providersUpdateCommand(
process.exit(1);
}
}
if (opts.slug !== undefined) body.slug = opts.slug;

if (Object.keys(body).length === 0) {
printError(
"No fields to update. Pass one of: --type, --base-url, --api-key, --api-version, --enabled, --fee, --settings.",
"No fields to update. Pass one of: --type, --base-url, --api-key, --api-version, --enabled, --fee, --settings, --slug.",
);
process.exit(1);
}
Expand Down
42 changes: 24 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,27 @@ providersCmd
.description("Add a new upstream provider (requires admin token)")
.option("--api-key <key>", "Provider API key")
.option("--base-url <url>", "Provider base URL")
.option("--slug <slug>", "Stable provider slug (auto-generated if omitted)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersAddCommand);
providersCmd
.command("remove <id>")
.description("Remove an upstream provider by ID (requires admin token)")
.command("remove <provider>")
.description("Remove an upstream provider by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersRemoveCommand);
providersCmd
.command("test <id>")
.description("Health check a provider by ID (requires admin token)")
.command("test <provider>")
.description("Health check a provider by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersTestCommand);
providersCmd
.command("show <id>")
.description("Show full details for a provider (requires admin token)")
.command("show <provider>")
.description("Show full details for a provider by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersShowCommand);
providersCmd
.command("update <id>")
.description("Update provider fields (requires admin token)")
.command("update <provider>")
.description("Update provider fields by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.option("--type <type>", "Provider type (openai, anthropic, azure, openrouter, custom, ...)")
.option("--base-url <url>", "Upstream base URL")
Expand All @@ -129,15 +130,16 @@ providersCmd
.option("--enabled <bool>", "Enable/disable provider (true|false)")
.option("--fee <multiplier>", "Provider fee multiplier (e.g. 1.01 for 1%)")
.option("--settings <json>", "Provider-specific settings as JSON")
.option("--slug <slug>", "Set/rename the stable provider slug")
.action(providersUpdateCommand);
providersCmd
.command("enable <id>")
.description("Enable a provider (requires admin token)")
.command("enable <provider>")
.description("Enable a provider by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersEnableCommand);
providersCmd
.command("disable <id>")
.description("Disable a provider (requires admin token)")
.command("disable <provider>")
.description("Disable a provider by ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providersDisableCommand);

Expand All @@ -146,19 +148,23 @@ const providerModelsCmd = providersCmd
.command("models")
.description("Manage models attached to a provider");
providerModelsCmd
.command("list <providerId>")
.description("List models for a provider — DB + remote upstream models (requires admin token)")
.command("list <provider>")
.description(
"List models for a provider ID or slug — DB + remote upstream models (requires admin token)",
)
.option("-t, --admin-token <token>", "Admin session token")
.option("--source <source>", "Filter: all | db | remote", "all")
.action(providerModelsListCommand);
providerModelsCmd
.command("show <providerId> <modelId>")
.description("Show full details for a model (requires admin token)")
.command("show <provider> <modelId>")
.description("Show full details for a model by provider ID or slug (requires admin token)")
.option("-t, --admin-token <token>", "Admin session token")
.action(providerModelsShowCommand);
providerModelsCmd
.command("update <providerId> <modelId>")
.description("Update a single model's mutable fields (requires admin token)")
.command("update <provider> <modelId>")
.description(
"Update a single model's mutable fields by provider ID or slug (requires admin token)",
)
.option("-t, --admin-token <token>", "Admin session token")
.option(
"--forwarded-model-id <id>",
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ModelsResponse {
export interface Provider {
name?: string;
id?: string;
slug?: string | null;
base_url?: string;
url?: string;
model_count?: number;
Expand Down
Loading