diff --git a/SKILL.md b/SKILL.md index b4dfd5d..f1e10d9 100644 --- a/SKILL.md +++ b/SKILL.md @@ -49,10 +49,6 @@ Restart the daemon (stops if running, then starts). Check daemon and wallet status. Returns JSON with current state. -### `routstrd ping` - -Test connection to the daemon. - ### `routstrd balance` Get wallet and API key balances. Shows per-mint wallet balances, per-key API balances, and a grand total (all in sats). @@ -117,11 +113,36 @@ List all registered clients with their ID, name, API key, and creation date. #### `routstrd clients add` -Add a new client. +Add a new client or set up a client integration. | Option | Description | |--------|-------------| -| `-n, --name ` | **Required.** Client name | +| `-n, --name ` | Client name (required when not using integration flags) | +| `--opencode` | Set up OpenCode integration | +| `--openclaw` | Set up OpenClaw integration | +| `--pi-agent` | Set up Pi Agent integration | +| `--claude-code` | Set up Claude Code integration | + +Set up a specific client integration (creates API key and writes config to the client's config file): + +```sh +routstrd clients add --opencode +routstrd clients add --claude-code +routstrd clients add --pi-agent +routstrd clients add --openclaw +``` + +You can also set up multiple integrations at once: + +```sh +routstrd clients add --opencode --pi-agent --claude-code +``` + +Add a generic client manually: + +```sh +routstrd clients add -n "My App" +``` Returns the client ID and API key for use with the OpenAI-compatible API. diff --git a/package.json b/package.json index 325408a..a364874 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "routstrd", - "version": "0.2.1", + "version": "0.2.3", "module": "src/index.ts", "type": "module", "private": false, diff --git a/src/cli.ts b/src/cli.ts index 2eceec0..184031b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -18,7 +18,11 @@ import { type RoutstrdConfig, } from "./utils/config"; import { logger } from "./utils/logger"; -import { setupIntegration } from "./integrations"; +import { + setupIntegration, + CLIENT_CONFIGS, + CLIENT_INTEGRATIONS, +} from "./integrations"; import { createSdkStore } from "@routstr/sdk"; import { createBunSqliteDriver } from "@routstr/sdk/storage"; import * as QRCode from "qrcode"; @@ -76,7 +80,7 @@ async function printLightningInvoice(invoice: string): Promise { async function installCocodOrExit(): Promise { logger.log("cocod not found. Installing globally with bun..."); - const installProc = Bun.spawn(["bun", "install", "--global", "cocod"], { + const installProc = Bun.spawn(["bun", "install", "--global", "@routstr/cocod"], { stdout: "inherit", stderr: "inherit", }); @@ -84,7 +88,7 @@ async function installCocodOrExit(): Promise { const installCode = await installProc.exited; if (installCode !== 0 || !(await isCocodInstalled())) { logger.error( - "Failed to install cocod. Please run 'bun install --global cocod' manually.", + "Failed to install cocod. Please run 'bun install --global @routstr/cocod' manually.", ); throw new Error("cocod installation failed"); } @@ -657,12 +661,71 @@ clientsCmd clientsCmd .command("add") - .description("Add a new client") - .requiredOption("-n, --name ", "Client name") - .action(async (options: { name: string }) => { + .description("Add a new client or set up client integrations") + .option("-n, --name ", "Client name") + .option("--opencode", "Set up OpenCode integration") + .option("--openclaw", "Set up OpenClaw integration") + .option("--pi-agent", "Set up Pi Agent integration") + .option("--claude-code", "Set up Claude Code integration") + .action(async (options: { + name?: string; + opencode?: boolean; + openclaw?: boolean; + piAgent?: boolean; + claudeCode?: boolean; + }) => { await ensureDaemonRunning(); const config = await loadConfig(); + const integrationKeys: string[] = []; + if (options.opencode) integrationKeys.push("opencode"); + if (options.openclaw) integrationKeys.push("openclaw"); + if (options.piAgent) integrationKeys.push("pi-agent"); + if (options.claudeCode) integrationKeys.push("claude-code"); + + if (integrationKeys.length > 0) { + const sqliteDriver = await createBunSqliteDriver(DB_PATH); + const { store } = await createSdkStore({ driver: sqliteDriver }); + + for (const key of integrationKeys) { + const integrationFn = CLIENT_INTEGRATIONS[key]; + const integrationConfig = CLIENT_CONFIGS[key]; + if (!integrationFn || !integrationConfig) continue; + + try { + await integrationFn(config, store, integrationConfig); + } catch (error) { + logger.error( + `Failed to set up ${integrationConfig.name} integration:`, + error, + ); + continue; + } + + const state = store.getState(); + const client = (state.clientIds || []).find( + (c: { clientId: string }) => c.clientId === key, + ); + if (client) { + console.log(`\n ${integrationConfig.name}:`); + console.log(` Client ID: ${client.clientId}`); + console.log(` API Key: ${client.apiKey}`); + } + } + + console.log( + `\n Access Routstr at: http://localhost:${config.port || 8008}`, + ); + return; + } + + if (!options.name) { + console.error( + "error: required option '-n, --name ' not specified", + ); + process.exit(1); + } + const result = await callDaemon("/clients/add", { method: "POST", body: { name: options.name },