import { Command } from "commander"; import chalk from "chalk"; import { ensureAuth } from "../lib/config.js"; import { apiRequest } from "../lib/api-client.js"; import { ORG_TOKEN_SCOPES } from "@skills-hub-ai/shared"; interface OrgTokenRow { id: string; name: string; keyPrefix: string; scopes: string[]; lastUsedAt: string | null; expiresAt: string | null; createdAt: string; } interface OrgTokenCreated extends OrgTokenRow { token: string; } const listCommand = new Command("list") .alias("ls") .description("List org tokens (admin)") .argument("", "organization slug") .action(async (org: string) => { ensureAuth(); try { const tokens = await apiRequest( `/api/v1/orgs/${encodeURIComponent(org)}/tokens`, ); if (tokens.length === 0) { console.log(chalk.yellow("No org tokens.")); console.log( ` Create one: ${chalk.cyan(`skills-hub org tokens create ${org} --name ci --scope registry:read`)}`, ); return; } console.log(chalk.bold(`\nOrg tokens for ${org} (${tokens.length}):\n`)); for (const t of tokens) { console.log(` ${chalk.bold(t.name)} ${chalk.gray(`(${t.id})`)}`); console.log(` Prefix: ${t.keyPrefix}`); console.log(` Scopes: ${t.scopes.join(", ")}`); console.log( ` Last used: ${t.lastUsedAt ?? "never"} Expires: ${t.expiresAt ?? "never"}`, ); } } catch (err) { console.error( chalk.red(err instanceof Error ? err.message : "Failed to list tokens"), ); process.exit(1); } }); const createCommand = new Command("create") .description("Create an org token (admin). The secret is shown exactly once.") .argument("", "organization slug") .requiredOption("--name ", "token name (e.g. ci, siem-export)") .requiredOption( "--scope ", `one or more of: ${ORG_TOKEN_SCOPES.join(", ")}`, ) .option("--expires-in-days ", "expiry in days (default: never)") .action( async ( org: string, opts: { name: string; scope: string[]; expiresInDays?: string }, ) => { ensureAuth(); try { const created = await apiRequest( `/api/v1/orgs/${encodeURIComponent(org)}/tokens`, { method: "POST", body: JSON.stringify({ name: opts.name, scopes: opts.scope, ...(opts.expiresInDays ? { expiresInDays: Number(opts.expiresInDays) } : {}), }), }, ); console.log(chalk.green(`\nToken "${created.name}" created.\n`)); console.log(` ${chalk.bold(created.token)}\n`); console.log( chalk.yellow( "This secret is shown ONCE and cannot be recovered — store it in your secrets manager now.", ), ); console.log( chalk.gray( `Use it with: Authorization: ApiKey ${created.keyPrefix}…`, ), ); } catch (err) { console.error( chalk.red( err instanceof Error ? err.message : "Failed to create token", ), ); process.exit(1); } }, ); const revokeCommand = new Command("revoke") .description("Revoke an org token (admin)") .argument("", "organization slug") .argument("", "token id (from `org tokens list`)") .action(async (org: string, tokenId: string) => { ensureAuth(); try { await apiRequest( `/api/v1/orgs/${encodeURIComponent(org)}/tokens/${encodeURIComponent(tokenId)}`, { method: "DELETE" }, ); console.log(chalk.green("Token revoked.")); } catch (err) { console.error( chalk.red(err instanceof Error ? err.message : "Failed to revoke"), ); process.exit(1); } }); export const orgTokensCommand = new Command("tokens") .description("Manage org API tokens (CI / SIEM / MCP clients)") .addCommand(listCommand) .addCommand(createCommand) .addCommand(revokeCommand);