routstrd 0.2.2 → 0.2.4

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
@@ -113,11 +113,36 @@ List all registered clients with their ID, name, API key, and creation date.
113
113
 
114
114
  #### `routstrd clients add`
115
115
 
116
- Add a new client.
116
+ Add a new client or set up a client integration.
117
117
 
118
118
  | Option | Description |
119
119
  |--------|-------------|
120
- | `-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
+ ```
121
146
 
122
147
  Returns the client ID and API key for use with the OpenAI-compatible API.
123
148
 
@@ -30266,6 +30266,8 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
30266
30266
  const msg = error.message.toLowerCase();
30267
30267
  if (msg.includes("fetch failed"))
30268
30268
  return true;
30269
+ if (msg.includes("429"))
30270
+ return true;
30269
30271
  if (msg.includes("502"))
30270
30272
  return true;
30271
30273
  if (msg.includes("503"))
@@ -30474,6 +30476,8 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
30474
30476
  const msg = error.message.toLowerCase();
30475
30477
  if (msg.includes("fetch failed"))
30476
30478
  return true;
30479
+ if (msg.includes("429"))
30480
+ return true;
30477
30481
  if (msg.includes("502"))
30478
30482
  return true;
30479
30483
  if (msg.includes("503"))
@@ -33881,9 +33885,10 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
33881
33885
  try {
33882
33886
  const currentBalanceInfo = await this.balanceManager.getTokenBalance(params.token, baseUrl);
33883
33887
  const currentBalance = currentBalanceInfo.unit === "msat" ? currentBalanceInfo.amount / 1000 : currentBalanceInfo.amount;
33884
- const shortfall = Math.max(0, params.requiredSats - currentBalance);
33888
+ const reservedBalance = currentBalanceInfo.unit === "msat" ? (currentBalanceInfo.reserved ?? 0) / 1000 : currentBalanceInfo.reserved ?? 0;
33889
+ const shortfall = Math.max(0, params.requiredSats - currentBalance + reservedBalance);
33885
33890
  topupAmount = shortfall > 0.21 * params.requiredSats ? shortfall : 0.21 * params.requiredSats;
33886
- this._log("DEBUG", `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance} `);
33891
+ this._log("DEBUG", `The shortfall is: ${shortfall}. requiredSats: ${params.requiredSats}. Current Balance: ${currentBalance}. Reserved Balance: ${reservedBalance}. Available Balance: ${currentBalance - reservedBalance}`);
33887
33892
  } catch (e) {
33888
33893
  this._log("WARN", "Could not get current token balance for topup calculation:", e);
33889
33894
  }
@@ -33974,8 +33979,8 @@ var import_rxjs24, InsufficientBalanceError, ProviderError, MintUnreachableError
33974
33979
  tryNextProvider = true;
33975
33980
  }
33976
33981
  }
33977
- if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
33978
- this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Status ${status} (auth/server error), attempting refund for ${baseUrl}, mode=${this.mode}`);
33982
+ if ((status === 401 || status === 403 || status === 413 || status === 400 || status === 429 || status === 500 || status === 502 || status === 503 || status === 504 || status === 521) && !tryNextProvider) {
33983
+ this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Status ${status} (${status === 429 ? "rate limited" : "auth/server error"}), attempting refund for ${baseUrl}, mode=${this.mode}`);
33979
33984
  if (this.mode === "apikeys") {
33980
33985
  this._log("DEBUG", `[RoutstrClient] _handleErrorResponse: Attempting API key refund for ${baseUrl}, key preview=${token}`);
33981
33986
  const latestBalanceInfo = await this.balanceManager.getTokenBalance(token, baseUrl);
package/dist/index.js CHANGED
@@ -29058,6 +29058,12 @@ var CLIENT_CONFIGS = {
29058
29058
  configPath: join2(process.env.HOME || "", ".claude/settings.json")
29059
29059
  }
29060
29060
  };
29061
+ var CLIENT_INTEGRATIONS = {
29062
+ opencode: installOpencodeIntegration,
29063
+ "pi-agent": installPiIntegration,
29064
+ openclaw: installOpenClawIntegration,
29065
+ "claude-code": installClaudeCodeIntegration
29066
+ };
29061
29067
 
29062
29068
  // src/integrations/opencode.ts
29063
29069
  var OPENCODE_SMALL_MODEL = "routstr/minimax-m2.5";
@@ -37300,9 +37306,49 @@ clientsCmd.command("list").description("List all clients").action(async () => {
37300
37306
  console.log("");
37301
37307
  }
37302
37308
  });
37303
- 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) => {
37304
37310
  await ensureDaemonRunning();
37305
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
+ }
37306
37352
  const result = await callDaemon("/clients/add", {
37307
37353
  method: "POST",
37308
37354
  body: { name: options.name }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routstrd",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "module": "src/index.ts",
5
5
  "type": "module",
6
6
  "private": false,
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@cashu/cashu-ts": "^3.1.1",
27
- "@routstr/sdk": "^0.3.0",
27
+ "@routstr/sdk": "^0.3.1",
28
28
  "applesauce-core": "^5.1.0",
29
29
  "applesauce-relay": "^5.1.0",
30
30
  "commander": "^14.0.2",
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";
@@ -657,12 +661,71 @@ clientsCmd
657
661
 
658
662
  clientsCmd
659
663
  .command("add")
660
- .description("Add a new client")
661
- .requiredOption("-n, --name <name>", "Client name")
662
- .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
+ }) => {
663
677
  await ensureDaemonRunning();
664
678
  const config = await loadConfig();
665
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
+
666
729
  const result = await callDaemon("/clients/add", {
667
730
  method: "POST",
668
731
  body: { name: options.name },