routstrd 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -49,10 +49,6 @@ Restart the daemon (stops if running, then starts).
49
49
 
50
50
  Check daemon and wallet status. Returns JSON with current state.
51
51
 
52
- ### `routstrd ping`
53
-
54
- Test connection to the daemon.
55
-
56
52
  ### `routstrd balance`
57
53
 
58
54
  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.
117
113
 
118
114
  #### `routstrd clients add`
119
115
 
120
- Add a new client.
116
+ Add a new client or set up a client integration.
121
117
 
122
118
  | Option | Description |
123
119
  |--------|-------------|
124
- | `-n, --name <name>` | **Required.** Client name |
120
+ | `-n, --name <name>` | Client name (required when not using integration flags) |
121
+ | `--opencode` | Set up OpenCode integration |
122
+ | `--openclaw` | Set up OpenClaw integration |
123
+ | `--pi-agent` | Set up Pi Agent integration |
124
+ | `--claude-code` | Set up Claude Code integration |
125
+
126
+ Set up a specific client integration (creates API key and writes config to the client's config file):
127
+
128
+ ```sh
129
+ routstrd clients add --opencode
130
+ routstrd clients add --claude-code
131
+ routstrd clients add --pi-agent
132
+ routstrd clients add --openclaw
133
+ ```
134
+
135
+ You can also set up multiple integrations at once:
136
+
137
+ ```sh
138
+ routstrd clients add --opencode --pi-agent --claude-code
139
+ ```
140
+
141
+ Add a generic client manually:
142
+
143
+ ```sh
144
+ routstrd clients add -n "My App"
145
+ ```
125
146
 
126
147
  Returns the client ID and API key for use with the OpenAI-compatible API.
127
148
 
@@ -41534,9 +41534,26 @@ Installing routstr configuration in ${configPath}...`);
41534
41534
  }
41535
41535
  settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
41536
41536
  settings.env["ANTHROPIC_BASE_URL"] = `http://localhost:${port}`;
41537
- settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = "gpt-5.4";
41538
- settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "claude-opus-4.7";
41539
- settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "minimax-m2.7";
41537
+ try {
41538
+ const response = await fetch(`http://localhost:${port}/models`);
41539
+ const data = await response.json();
41540
+ const models = data.output?.models || [];
41541
+ if (models.length >= 3) {
41542
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
41543
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[1].id;
41544
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[2].id;
41545
+ logger3.log(`Set Claude models: Opus=${models[0].id}, Sonnet=${models[1].id}, Haiku=${models[2].id}`);
41546
+ } else if (models.length > 0) {
41547
+ logger3.log(`Only ${models.length} models available, falling back to defaults.`);
41548
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
41549
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[0].id;
41550
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[0].id;
41551
+ } else {
41552
+ logger3.log("No models available from routstr daemon.");
41553
+ }
41554
+ } catch (error) {
41555
+ logger3.error("Failed to fetch models for Claude Code integration:", error);
41556
+ }
41540
41557
  try {
41541
41558
  mkdirSync3(dirname3(configPath), { recursive: true });
41542
41559
  await writeFile3(configPath, JSON.stringify(settings, null, 2));
package/dist/index.js CHANGED
@@ -29002,9 +29002,26 @@ Installing routstr configuration in ${configPath}...`);
29002
29002
  }
29003
29003
  settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
29004
29004
  settings.env["ANTHROPIC_BASE_URL"] = `http://localhost:${port}`;
29005
- settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = "gpt-5.4";
29006
- settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "claude-opus-4.7";
29007
- settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "minimax-m2.7";
29005
+ try {
29006
+ const response = await fetch(`http://localhost:${port}/models`);
29007
+ const data = await response.json();
29008
+ const models = data.output?.models || [];
29009
+ if (models.length >= 3) {
29010
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
29011
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[1].id;
29012
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[2].id;
29013
+ logger.log(`Set Claude models: Opus=${models[0].id}, Sonnet=${models[1].id}, Haiku=${models[2].id}`);
29014
+ } else if (models.length > 0) {
29015
+ logger.log(`Only ${models.length} models available, falling back to defaults.`);
29016
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
29017
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[0].id;
29018
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[0].id;
29019
+ } else {
29020
+ logger.log("No models available from routstr daemon.");
29021
+ }
29022
+ } catch (error) {
29023
+ logger.error("Failed to fetch models for Claude Code integration:", error);
29024
+ }
29008
29025
  try {
29009
29026
  mkdirSync3(dirname3(configPath), { recursive: true });
29010
29027
  await writeFile3(configPath, JSON.stringify(settings, null, 2));
@@ -29041,6 +29058,12 @@ var CLIENT_CONFIGS = {
29041
29058
  configPath: join2(process.env.HOME || "", ".claude/settings.json")
29042
29059
  }
29043
29060
  };
29061
+ var CLIENT_INTEGRATIONS = {
29062
+ opencode: installOpencodeIntegration,
29063
+ "pi-agent": installPiIntegration,
29064
+ openclaw: installOpenClawIntegration,
29065
+ "claude-code": installClaudeCodeIntegration
29066
+ };
29044
29067
 
29045
29068
  // src/integrations/opencode.ts
29046
29069
  var OPENCODE_SMALL_MODEL = "routstr/minimax-m2.5";
@@ -36927,21 +36950,21 @@ async function printLightningInvoice(invoice) {
36927
36950
  Invoice:
36928
36951
  ${invoice}`);
36929
36952
  }
36953
+ async function installCocodOrExit() {
36954
+ logger.log("cocod not found. Installing globally with bun...");
36955
+ const installProc = Bun.spawn(["bun", "install", "--global", "@routstr/cocod"], {
36956
+ stdout: "inherit",
36957
+ stderr: "inherit"
36958
+ });
36959
+ const installCode = await installProc.exited;
36960
+ if (installCode !== 0 || !await isCocodInstalled()) {
36961
+ logger.error("Failed to install cocod. Please run 'bun install --global @routstr/cocod' manually.");
36962
+ throw new Error("cocod installation failed");
36963
+ }
36964
+ logger.log("cocod installed successfully.");
36965
+ }
36930
36966
  async function initDaemon() {
36931
36967
  logger.log("Initializing routstrd...");
36932
- if (!await checkCocodInstalled()) {
36933
- logger.log("cocod not found. Installing globally with bun...");
36934
- const installProc = Bun.spawn(["bun", "install", "--global", "cocod"], {
36935
- stdout: "inherit",
36936
- stderr: "inherit"
36937
- });
36938
- const installCode = await installProc.exited;
36939
- if (installCode !== 0 || !await checkCocodInstalled()) {
36940
- logger.error("Failed to install cocod. Please run 'bun install --global cocod' manually.");
36941
- return;
36942
- }
36943
- logger.log("cocod installed successfully.");
36944
- }
36945
36968
  if (!existsSync9(CONFIG_DIR)) {
36946
36969
  mkdirSync5(CONFIG_DIR, { recursive: true });
36947
36970
  logger.log(`Created config directory: ${CONFIG_DIR}`);
@@ -36955,24 +36978,14 @@ async function initDaemon() {
36955
36978
  logger.log(`Created config file: ${CONFIG_FILE}`);
36956
36979
  }
36957
36980
  const config = await loadConfig();
36958
- const cocodExecutable = resolveCocodExecutable(config.cocodPath);
36959
36981
  if (!await isCocodInstalled(config.cocodPath)) {
36960
36982
  if (config.cocodPath) {
36961
36983
  logger.error(`Configured cocod executable was not found: ${config.cocodPath}`);
36962
36984
  return;
36963
36985
  }
36964
- logger.log("cocod not found. Installing globally with bun...");
36965
- const installProc = Bun.spawn(["bun", "install", "--global", "cocod"], {
36966
- stdout: "inherit",
36967
- stderr: "inherit"
36968
- });
36969
- const installCode = await installProc.exited;
36970
- if (installCode !== 0 || !await isCocodInstalled(config.cocodPath)) {
36971
- logger.error("Failed to install cocod. Please run 'bun install --global cocod' manually.");
36972
- return;
36973
- }
36974
- logger.log("cocod installed successfully.");
36986
+ await installCocodOrExit();
36975
36987
  }
36988
+ const cocodExecutable = resolveCocodExecutable(config.cocodPath);
36976
36989
  console.log(`Database will be stored at: ${DB_PATH}`);
36977
36990
  console.log(`
36978
36991
  Initializing cocod...`);
@@ -37026,18 +37039,6 @@ Initialization complete!`);
37026
37039
  logger.log(`
37027
37040
  To ensure routstrd persists across system restarts, run: 'routstrd service install'`);
37028
37041
  }
37029
- async function checkCocodInstalled() {
37030
- try {
37031
- const proc = Bun.spawn({
37032
- cmd: ["which", "cocod"],
37033
- stdout: "pipe"
37034
- });
37035
- const code = await proc.exited;
37036
- return code === 0;
37037
- } catch {
37038
- return false;
37039
- }
37040
- }
37041
37042
  program.name("routstrd").description("Routstr daemon - Manage routstr processes").version(cliVersion, "--version", "output the version number");
37042
37043
  program.command("onboard").description("Initialize routstrd (creates config directory and initializes cocod)").action(async () => {
37043
37044
  await initDaemon();
@@ -37305,9 +37306,49 @@ clientsCmd.command("list").description("List all clients").action(async () => {
37305
37306
  console.log("");
37306
37307
  }
37307
37308
  });
37308
- clientsCmd.command("add").description("Add a new client").requiredOption("-n, --name <name>", "Client name").action(async (options) => {
37309
+ clientsCmd.command("add").description("Add a new client or set up client integrations").option("-n, --name <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) => {
37309
37310
  await ensureDaemonRunning();
37310
37311
  const config = await loadConfig();
37312
+ const integrationKeys = [];
37313
+ if (options.opencode)
37314
+ integrationKeys.push("opencode");
37315
+ if (options.openclaw)
37316
+ integrationKeys.push("openclaw");
37317
+ if (options.piAgent)
37318
+ integrationKeys.push("pi-agent");
37319
+ if (options.claudeCode)
37320
+ integrationKeys.push("claude-code");
37321
+ if (integrationKeys.length > 0) {
37322
+ const sqliteDriver = await createBunSqliteDriver(DB_PATH);
37323
+ const { store } = await createSdkStore({ driver: sqliteDriver });
37324
+ for (const key of integrationKeys) {
37325
+ const integrationFn = CLIENT_INTEGRATIONS[key];
37326
+ const integrationConfig = CLIENT_CONFIGS[key];
37327
+ if (!integrationFn || !integrationConfig)
37328
+ continue;
37329
+ try {
37330
+ await integrationFn(config, store, integrationConfig);
37331
+ } catch (error) {
37332
+ logger.error(`Failed to set up ${integrationConfig.name} integration:`, error);
37333
+ continue;
37334
+ }
37335
+ const state = store.getState();
37336
+ const client2 = (state.clientIds || []).find((c) => c.clientId === key);
37337
+ if (client2) {
37338
+ console.log(`
37339
+ ${integrationConfig.name}:`);
37340
+ console.log(` Client ID: ${client2.clientId}`);
37341
+ console.log(` API Key: ${client2.apiKey}`);
37342
+ }
37343
+ }
37344
+ console.log(`
37345
+ Access Routstr at: http://localhost:${config.port || 8008}`);
37346
+ return;
37347
+ }
37348
+ if (!options.name) {
37349
+ console.error("error: required option '-n, --name <name>' not specified");
37350
+ process.exit(1);
37351
+ }
37311
37352
  const result = await callDaemon("/clients/add", {
37312
37353
  method: "POST",
37313
37354
  body: { name: options.name }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routstrd",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "module": "src/index.ts",
5
5
  "type": "module",
6
6
  "private": false,
package/src/cli.ts CHANGED
@@ -18,7 +18,11 @@ import {
18
18
  type RoutstrdConfig,
19
19
  } from "./utils/config";
20
20
  import { logger } from "./utils/logger";
21
- import { setupIntegration } from "./integrations";
21
+ import {
22
+ setupIntegration,
23
+ CLIENT_CONFIGS,
24
+ CLIENT_INTEGRATIONS,
25
+ } from "./integrations";
22
26
  import { createSdkStore } from "@routstr/sdk";
23
27
  import { createBunSqliteDriver } from "@routstr/sdk/storage";
24
28
  import * as QRCode from "qrcode";
@@ -73,27 +77,27 @@ async function printLightningInvoice(invoice: string): Promise<void> {
73
77
  console.log(`${qr}\nInvoice:\n${invoice}`);
74
78
  }
75
79
 
76
- async function initDaemon(): Promise<void> {
77
- logger.log("Initializing routstrd...");
80
+ async function installCocodOrExit(): Promise<void> {
81
+ logger.log("cocod not found. Installing globally with bun...");
78
82
 
79
- if (!(await checkCocodInstalled())) {
80
- logger.log("cocod not found. Installing globally with bun...");
83
+ const installProc = Bun.spawn(["bun", "install", "--global", "@routstr/cocod"], {
84
+ stdout: "inherit",
85
+ stderr: "inherit",
86
+ });
81
87
 
82
- const installProc = Bun.spawn(["bun", "install", "--global", "cocod"], {
83
- stdout: "inherit",
84
- stderr: "inherit",
85
- });
88
+ const installCode = await installProc.exited;
89
+ if (installCode !== 0 || !(await isCocodInstalled())) {
90
+ logger.error(
91
+ "Failed to install cocod. Please run 'bun install --global @routstr/cocod' manually.",
92
+ );
93
+ throw new Error("cocod installation failed");
94
+ }
86
95
 
87
- const installCode = await installProc.exited;
88
- if (installCode !== 0 || !(await checkCocodInstalled())) {
89
- logger.error(
90
- "Failed to install cocod. Please run 'bun install --global cocod' manually.",
91
- );
92
- return;
93
- }
96
+ logger.log("cocod installed successfully.");
97
+ }
94
98
 
95
- logger.log("cocod installed successfully.");
96
- }
99
+ async function initDaemon(): Promise<void> {
100
+ logger.log("Initializing routstrd...");
97
101
 
98
102
  // Create config directory
99
103
  if (!existsSync(CONFIG_DIR)) {
@@ -112,7 +116,6 @@ async function initDaemon(): Promise<void> {
112
116
  }
113
117
 
114
118
  const config = await loadConfig();
115
- const cocodExecutable = resolveCocodExecutable(config.cocodPath);
116
119
 
117
120
  if (!(await isCocodInstalled(config.cocodPath))) {
118
121
  if (config.cocodPath) {
@@ -122,24 +125,11 @@ async function initDaemon(): Promise<void> {
122
125
  return;
123
126
  }
124
127
 
125
- logger.log("cocod not found. Installing globally with bun...");
126
-
127
- const installProc = Bun.spawn(["bun", "install", "--global", "cocod"], {
128
- stdout: "inherit",
129
- stderr: "inherit",
130
- });
131
-
132
- const installCode = await installProc.exited;
133
- if (installCode !== 0 || !(await isCocodInstalled(config.cocodPath))) {
134
- logger.error(
135
- "Failed to install cocod. Please run 'bun install --global cocod' manually.",
136
- );
137
- return;
138
- }
139
-
140
- logger.log("cocod installed successfully.");
128
+ await installCocodOrExit();
141
129
  }
142
130
 
131
+ const cocodExecutable = resolveCocodExecutable(config.cocodPath);
132
+
143
133
  console.log(`Database will be stored at: ${DB_PATH}`);
144
134
  console.log("\nInitializing cocod...");
145
135
 
@@ -216,19 +206,6 @@ async function initDaemon(): Promise<void> {
216
206
  );
217
207
  }
218
208
 
219
- async function checkCocodInstalled(): Promise<boolean> {
220
- try {
221
- const proc = Bun.spawn({
222
- cmd: ["which", "cocod"],
223
- stdout: "pipe",
224
- });
225
- const code = await proc.exited;
226
- return code === 0;
227
- } catch {
228
- return false;
229
- }
230
- }
231
-
232
209
  program
233
210
  .name("routstrd")
234
211
  .description("Routstr daemon - Manage routstr processes")
@@ -684,12 +661,71 @@ clientsCmd
684
661
 
685
662
  clientsCmd
686
663
  .command("add")
687
- .description("Add a new client")
688
- .requiredOption("-n, --name <name>", "Client name")
689
- .action(async (options: { name: string }) => {
664
+ .description("Add a new client or set up client integrations")
665
+ .option("-n, --name <name>", "Client name")
666
+ .option("--opencode", "Set up OpenCode integration")
667
+ .option("--openclaw", "Set up OpenClaw integration")
668
+ .option("--pi-agent", "Set up Pi Agent integration")
669
+ .option("--claude-code", "Set up Claude Code integration")
670
+ .action(async (options: {
671
+ name?: string;
672
+ opencode?: boolean;
673
+ openclaw?: boolean;
674
+ piAgent?: boolean;
675
+ claudeCode?: boolean;
676
+ }) => {
690
677
  await ensureDaemonRunning();
691
678
  const config = await loadConfig();
692
679
 
680
+ const integrationKeys: string[] = [];
681
+ if (options.opencode) integrationKeys.push("opencode");
682
+ if (options.openclaw) integrationKeys.push("openclaw");
683
+ if (options.piAgent) integrationKeys.push("pi-agent");
684
+ if (options.claudeCode) integrationKeys.push("claude-code");
685
+
686
+ if (integrationKeys.length > 0) {
687
+ const sqliteDriver = await createBunSqliteDriver(DB_PATH);
688
+ const { store } = await createSdkStore({ driver: sqliteDriver });
689
+
690
+ for (const key of integrationKeys) {
691
+ const integrationFn = CLIENT_INTEGRATIONS[key];
692
+ const integrationConfig = CLIENT_CONFIGS[key];
693
+ if (!integrationFn || !integrationConfig) continue;
694
+
695
+ try {
696
+ await integrationFn(config, store, integrationConfig);
697
+ } catch (error) {
698
+ logger.error(
699
+ `Failed to set up ${integrationConfig.name} integration:`,
700
+ error,
701
+ );
702
+ continue;
703
+ }
704
+
705
+ const state = store.getState();
706
+ const client = (state.clientIds || []).find(
707
+ (c: { clientId: string }) => c.clientId === key,
708
+ );
709
+ if (client) {
710
+ console.log(`\n ${integrationConfig.name}:`);
711
+ console.log(` Client ID: ${client.clientId}`);
712
+ console.log(` API Key: ${client.apiKey}`);
713
+ }
714
+ }
715
+
716
+ console.log(
717
+ `\n Access Routstr at: http://localhost:${config.port || 8008}`,
718
+ );
719
+ return;
720
+ }
721
+
722
+ if (!options.name) {
723
+ console.error(
724
+ "error: required option '-n, --name <name>' not specified",
725
+ );
726
+ process.exit(1);
727
+ }
728
+
693
729
  const result = await callDaemon("/clients/add", {
694
730
  method: "POST",
695
731
  body: { name: options.name },
@@ -4,7 +4,7 @@ import { dirname } from "path";
4
4
  import type { RoutstrdConfig } from "../utils/config";
5
5
  import { logger } from "../utils/logger";
6
6
  import type { SdkStore } from "@routstr/sdk";
7
- import type { IntegrationConfig } from "./registry";
7
+ import type { IntegrationConfig, RoutstrModel } from "./registry";
8
8
  import { generateApiKey } from "./registry";
9
9
 
10
10
  export async function installClaudeCodeIntegration(
@@ -61,11 +61,28 @@ export async function installClaudeCodeIntegration(
61
61
 
62
62
  settings.env["ANTHROPIC_AUTH_TOKEN"] = apiKey;
63
63
  settings.env["ANTHROPIC_BASE_URL"] = `http://localhost:${port}`;
64
-
65
- // Default models as requested
66
- settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = "gpt-5.4";
67
- settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = "claude-opus-4.7";
68
- settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "minimax-m2.7";
64
+
65
+ try {
66
+ const response = await fetch(`http://localhost:${port}/models`);
67
+ const data = await response.json() as { output?: { models: RoutstrModel[] } };
68
+ const models = data.output?.models || [];
69
+
70
+ if (models.length >= 3) {
71
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
72
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[1].id;
73
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[2].id;
74
+ logger.log(`Set Claude models: Opus=${models[0].id}, Sonnet=${models[1].id}, Haiku=${models[2].id}`);
75
+ } else if (models.length > 0) {
76
+ logger.log(`Only ${models.length} models available, falling back to defaults.`);
77
+ settings.env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = models[0].id;
78
+ settings.env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = models[0].id;
79
+ settings.env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = models[0].id;
80
+ } else {
81
+ logger.log("No models available from routstr daemon.");
82
+ }
83
+ } catch (error) {
84
+ logger.error("Failed to fetch models for Claude Code integration:", error);
85
+ }
69
86
 
70
87
  try {
71
88
  mkdirSync(dirname(configPath), { recursive: true });