routstrd 0.2.8 → 0.2.10
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/README.md +17 -20
- package/SKILL.md +46 -13
- package/dist/daemon/index.js +137 -28
- package/dist/index.js +300 -181
- package/package.json +1 -1
- package/src/daemon/wallet/cocod-client.ts +22 -6
- package/src/start-daemon.ts +52 -30
- package/src/utils/clients.ts +8 -9
- package/src/utils/daemon-client.ts +6 -26
- package/src/utils/process-lock.ts +136 -0
package/README.md
CHANGED
|
@@ -23,44 +23,41 @@ curl -fsSL https://bun.com/install | bash
|
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### Step 1: Install
|
|
27
27
|
|
|
28
|
+
**Global with bun:**
|
|
28
29
|
```sh
|
|
30
|
+
bun i -g routstrd
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**OR - From source:**
|
|
34
|
+
```sh
|
|
35
|
+
git clone https://github.com/routstr/routstrd.git
|
|
29
36
|
cd routstrd
|
|
30
37
|
bun install
|
|
31
38
|
bun link
|
|
32
|
-
routstrd onboard
|
|
33
39
|
```
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### Initialize
|
|
38
|
-
|
|
39
|
-
Initialize routstrd (creates config directory and sets up cocod):
|
|
41
|
+
### Step 2: Setup & Fund
|
|
40
42
|
|
|
41
43
|
```sh
|
|
42
44
|
routstrd onboard
|
|
45
|
+
routstrd receive <cashu> # receive a Cashu token
|
|
46
|
+
routstrd receive 2100 # to top up 2100 sats with lightning
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
- Create `~/.routstrd/` directory
|
|
47
|
-
- Create config file at `~/.routstrd/config.json`
|
|
48
|
-
- Run `cocod init` to set up the wallet
|
|
49
|
-
|
|
49
|
+
### Step 3: Integrate with Claude Code
|
|
50
50
|
|
|
51
|
-
Then fund with Cashu/Lightning:
|
|
52
51
|
```sh
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
or
|
|
56
|
-
```sh
|
|
57
|
-
cocod receive bolt11 <amount>
|
|
52
|
+
routstrd clients add --claude-code # or --pi-agent / --opencode
|
|
58
53
|
```
|
|
59
54
|
|
|
60
|
-
|
|
55
|
+
## Use Routstrd Skill
|
|
56
|
+
|
|
57
|
+
> **Tip:** You can also install the [routstrd skill](https://github.com/Routstr/routstrd/blob/main/SKILL.md) so the agent can manage routstrd for you.
|
|
61
58
|
|
|
62
59
|
## More Commands
|
|
63
|
-
###Start Daemon
|
|
60
|
+
### Start Daemon
|
|
64
61
|
|
|
65
62
|
Start the background daemon:
|
|
66
63
|
|
package/SKILL.md
CHANGED
|
@@ -111,6 +111,7 @@ List and manage API clients (subcommand required).
|
|
|
111
111
|
|
|
112
112
|
List all registered clients with their ID, name, API key, and creation date.
|
|
113
113
|
|
|
114
|
+
|
|
114
115
|
#### `routstrd clients add`
|
|
115
116
|
|
|
116
117
|
Add a new client or set up a client integration.
|
|
@@ -123,28 +124,53 @@ Add a new client or set up a client integration.
|
|
|
123
124
|
| `--pi-agent` | Set up Pi Agent integration |
|
|
124
125
|
| `--claude-code` | Set up Claude Code integration |
|
|
125
126
|
|
|
126
|
-
Set up a specific client integration (creates API key and writes config to the client's config file):
|
|
127
|
-
|
|
128
127
|
```sh
|
|
129
|
-
routstrd clients add --opencode
|
|
130
|
-
routstrd clients add
|
|
131
|
-
routstrd clients add --pi-agent
|
|
132
|
-
routstrd clients add --openclaw
|
|
128
|
+
routstrd clients add --opencode --pi-agent --claude-code # multiple integrations
|
|
129
|
+
routstrd clients add -n "My App" # generic client
|
|
133
130
|
```
|
|
134
131
|
|
|
135
|
-
|
|
132
|
+
Returns the client ID and API key for use with the OpenAI-compatible API.
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
#### `routstrd clients delete <id>`
|
|
135
|
+
|
|
136
|
+
Delete a registered client by its ID.
|
|
137
|
+
|
|
138
|
+
### `routstrd npubs`
|
|
140
139
|
|
|
141
|
-
|
|
140
|
+
|
|
141
|
+
Manage admin npubs (subcommand required).
|
|
142
|
+
|
|
143
|
+
| Command | Description |
|
|
144
|
+
|---------|-------------|
|
|
145
|
+
| `routstrd npubs list` | List configured admin npubs |
|
|
146
|
+
| `routstrd npubs add <npub>` | Add an admin npub (accepts hex or npub1...) |
|
|
147
|
+
| `routstrd npubs delete <npub>` | Delete an admin npub |
|
|
148
|
+
|
|
149
|
+
### `routstrd remote <url>`
|
|
150
|
+
|
|
151
|
+
Configure a remote daemon URL. Generates a Nostr identity (nsec/npub) for NIP-98 authentication automatically.
|
|
142
152
|
|
|
143
153
|
```sh
|
|
144
|
-
routstrd
|
|
154
|
+
routstrd remote https://your-remote-daemon.com
|
|
145
155
|
```
|
|
146
156
|
|
|
147
|
-
|
|
157
|
+
### `routstrd refresh`
|
|
158
|
+
|
|
159
|
+
Refresh routstr21 models from Nostr and re-run integrations for all registered clients.
|
|
160
|
+
|
|
161
|
+
| Field | Type | Default | Description |
|
|
162
|
+
|-------|------|---------|-------------|
|
|
163
|
+
| `port` | number | 8008 | Daemon HTTP port |
|
|
164
|
+
| `provider` | string\|null | null | Default provider URL |
|
|
165
|
+
| `daemonUrl` | string\|null | null | Remote daemon URL |
|
|
166
|
+
| `nsec` | string\|null | null | Nostr secret key for NIP-98 auth |
|
|
167
|
+
| `cocodPath` | string\|null | null | Custom path to cocod executable |
|
|
168
|
+
| `mode` | string | `"apikeys"` | Client mode (`apikeys` or `xcashu`) |
|
|
169
|
+
|
|
170
|
+
| Variable | Default | Description |
|
|
171
|
+
|----------|---------|-------------|
|
|
172
|
+
| `ROUTSTRD_DIR` | `~/.routstrd` | Config directory |
|
|
173
|
+
| `COCOD_DIR` | `~/.cocod` | Wallet config directory |
|
|
148
174
|
|
|
149
175
|
### `routstrd mode`
|
|
150
176
|
|
|
@@ -266,6 +292,13 @@ Config file: `~/.routstrd/config.json`
|
|
|
266
292
|
| `ROUTSTRD_SOCKET` | `~/.routstrd/routstrd.sock` | IPC socket path |
|
|
267
293
|
| `ROUTSTRD_PID` | `~/.routstrd/routstrd.pid` | PID file path |
|
|
268
294
|
|
|
295
|
+
## Remote Mode
|
|
296
|
+
|
|
297
|
+
When `daemonUrl` is configured, commands connect to a remote daemon instead of a local one:
|
|
298
|
+
- Client names are suffixed with the last 7 chars of your npub
|
|
299
|
+
- All requests are automatically NIP-98 signed using your local nsec
|
|
300
|
+
- Local-only commands (`onboard`, `start`, `restart`, `mode`, `logs`, `service`) are disabled
|
|
301
|
+
|
|
269
302
|
## Pi Integration
|
|
270
303
|
|
|
271
304
|
When `routstrd onboard` runs, it automatically configures a `routstr` provider in `pi`'s `models.json` with an OpenAI-compatible base URL and API key. This allows pi (the AI coding agent) to use Routstr providers seamlessly.
|
package/dist/daemon/index.js
CHANGED
|
@@ -34900,6 +34900,102 @@ init_cashu_ts_es();
|
|
|
34900
34900
|
|
|
34901
34901
|
// src/daemon/wallet/cocod-client.ts
|
|
34902
34902
|
import { createHash } from "crypto";
|
|
34903
|
+
|
|
34904
|
+
// src/utils/process-lock.ts
|
|
34905
|
+
import { randomUUID } from "crypto";
|
|
34906
|
+
import { mkdir as mkdir3, readFile, rm, stat, writeFile } from "fs/promises";
|
|
34907
|
+
import { dirname } from "path";
|
|
34908
|
+
function delay(ms) {
|
|
34909
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
34910
|
+
}
|
|
34911
|
+
function isProcessRunning(pid) {
|
|
34912
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
34913
|
+
return false;
|
|
34914
|
+
}
|
|
34915
|
+
try {
|
|
34916
|
+
process.kill(pid, 0);
|
|
34917
|
+
return true;
|
|
34918
|
+
} catch (error) {
|
|
34919
|
+
const code = error.code;
|
|
34920
|
+
return code === "EPERM";
|
|
34921
|
+
}
|
|
34922
|
+
}
|
|
34923
|
+
async function readLockOwner(lockDir) {
|
|
34924
|
+
try {
|
|
34925
|
+
const raw = await readFile(`${lockDir}/owner.json`, "utf8");
|
|
34926
|
+
const parsed = JSON.parse(raw);
|
|
34927
|
+
if (typeof parsed.pid === "number" && typeof parsed.createdAt === "number") {
|
|
34928
|
+
return {
|
|
34929
|
+
pid: parsed.pid,
|
|
34930
|
+
createdAt: parsed.createdAt,
|
|
34931
|
+
token: typeof parsed.token === "string" ? parsed.token : undefined
|
|
34932
|
+
};
|
|
34933
|
+
}
|
|
34934
|
+
} catch {
|
|
34935
|
+
}
|
|
34936
|
+
return null;
|
|
34937
|
+
}
|
|
34938
|
+
async function isLockStale(lockDir, staleAfterMs) {
|
|
34939
|
+
const owner = await readLockOwner(lockDir);
|
|
34940
|
+
if (owner) {
|
|
34941
|
+
return !isProcessRunning(owner.pid) || Date.now() - owner.createdAt > staleAfterMs;
|
|
34942
|
+
}
|
|
34943
|
+
try {
|
|
34944
|
+
const info = await stat(lockDir);
|
|
34945
|
+
return Date.now() - info.mtimeMs > staleAfterMs;
|
|
34946
|
+
} catch {
|
|
34947
|
+
return false;
|
|
34948
|
+
}
|
|
34949
|
+
}
|
|
34950
|
+
async function acquireCrossProcessLock(lockDir, options = {}) {
|
|
34951
|
+
const acquireTimeoutMs = options.acquireTimeoutMs ?? 120000;
|
|
34952
|
+
const retryIntervalMs = options.retryIntervalMs ?? 100;
|
|
34953
|
+
const staleAfterMs = options.staleAfterMs ?? 120000;
|
|
34954
|
+
const deadline = Date.now() + acquireTimeoutMs;
|
|
34955
|
+
await mkdir3(dirname(lockDir), { recursive: true });
|
|
34956
|
+
while (true) {
|
|
34957
|
+
try {
|
|
34958
|
+
await mkdir3(lockDir);
|
|
34959
|
+
const token = randomUUID();
|
|
34960
|
+
const owner = { pid: process.pid, createdAt: Date.now(), token };
|
|
34961
|
+
await writeFile(`${lockDir}/owner.json`, JSON.stringify(owner), "utf8");
|
|
34962
|
+
let released = false;
|
|
34963
|
+
return async () => {
|
|
34964
|
+
if (released)
|
|
34965
|
+
return;
|
|
34966
|
+
released = true;
|
|
34967
|
+
const currentOwner = await readLockOwner(lockDir);
|
|
34968
|
+
if (currentOwner?.token === token) {
|
|
34969
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
34970
|
+
}
|
|
34971
|
+
};
|
|
34972
|
+
} catch (error) {
|
|
34973
|
+
const code = error.code;
|
|
34974
|
+
if (code !== "EEXIST") {
|
|
34975
|
+
throw error;
|
|
34976
|
+
}
|
|
34977
|
+
if (await isLockStale(lockDir, staleAfterMs)) {
|
|
34978
|
+
options.log?.(`Removing stale lock at ${lockDir}`);
|
|
34979
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
34980
|
+
continue;
|
|
34981
|
+
}
|
|
34982
|
+
if (Date.now() >= deadline) {
|
|
34983
|
+
throw new Error(`Timed out waiting to acquire lock ${lockDir}`);
|
|
34984
|
+
}
|
|
34985
|
+
await delay(retryIntervalMs);
|
|
34986
|
+
}
|
|
34987
|
+
}
|
|
34988
|
+
}
|
|
34989
|
+
async function withCrossProcessLock(lockDir, fn, options = {}) {
|
|
34990
|
+
const release = await acquireCrossProcessLock(lockDir, options);
|
|
34991
|
+
try {
|
|
34992
|
+
return await fn();
|
|
34993
|
+
} finally {
|
|
34994
|
+
await release();
|
|
34995
|
+
}
|
|
34996
|
+
}
|
|
34997
|
+
|
|
34998
|
+
// src/daemon/wallet/cocod-client.ts
|
|
34903
34999
|
var DEFAULT_CONFIG_DIR = process.env.COCOD_DIR || `${process.env.HOME || process.env.USERPROFILE || ""}/.cocod`;
|
|
34904
35000
|
var DEFAULT_SOCKET_PATH = process.env.COCOD_SOCKET || `${DEFAULT_CONFIG_DIR}/cocod.sock`;
|
|
34905
35001
|
|
|
@@ -34929,7 +35025,7 @@ function parseMintList(output4) {
|
|
|
34929
35025
|
return (output4 || "").split(`
|
|
34930
35026
|
`).map((line) => line.trim()).filter(Boolean);
|
|
34931
35027
|
}
|
|
34932
|
-
function
|
|
35028
|
+
function delay2(ms) {
|
|
34933
35029
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
34934
35030
|
}
|
|
34935
35031
|
function toErrorText(value) {
|
|
@@ -34951,6 +35047,7 @@ function tokenFingerprint(token) {
|
|
|
34951
35047
|
function createCocodClient(options = {}) {
|
|
34952
35048
|
const executable = resolveCocodExecutable(options.cocodPath);
|
|
34953
35049
|
const socketPath = options.socketPath || DEFAULT_SOCKET_PATH;
|
|
35050
|
+
const startupLockPath = `${socketPath}.startup.lock`;
|
|
34954
35051
|
const fetchImpl = options.fetchImpl || fetch;
|
|
34955
35052
|
const pollIntervalMs = options.pollIntervalMs ?? 100;
|
|
34956
35053
|
const startupTimeoutMs = options.startupTimeoutMs ?? 5000;
|
|
@@ -35005,31 +35102,40 @@ function createCocodClient(options = {}) {
|
|
|
35005
35102
|
}
|
|
35006
35103
|
async function startDaemon() {
|
|
35007
35104
|
const env = { ...process.env, COCOD_SOCKET: socketPath };
|
|
35008
|
-
const proc = spawnDaemon([executable, "
|
|
35105
|
+
const proc = spawnDaemon([executable, "init"], env);
|
|
35009
35106
|
const maxPolls = Math.ceil(startupTimeoutMs / pollIntervalMs);
|
|
35010
35107
|
let exitCode = null;
|
|
35011
35108
|
proc.exited.then((code) => {
|
|
35012
35109
|
exitCode = code;
|
|
35013
35110
|
});
|
|
35014
35111
|
for (let i4 = 0;i4 < maxPolls; i4++) {
|
|
35015
|
-
await
|
|
35016
|
-
if (exitCode !== null) {
|
|
35017
|
-
throw new Error(`cocod
|
|
35112
|
+
await delay2(pollIntervalMs);
|
|
35113
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
35114
|
+
throw new Error(`cocod init exited early with code ${exitCode}`);
|
|
35018
35115
|
}
|
|
35019
35116
|
if (await pingInternal()) {
|
|
35020
35117
|
logger3.debug(`Connected to cocod daemon on ${socketPath}`);
|
|
35021
35118
|
return;
|
|
35022
35119
|
}
|
|
35023
35120
|
}
|
|
35024
|
-
throw new Error(`cocod
|
|
35121
|
+
throw new Error(`cocod failed to start within ${Math.round(startupTimeoutMs / 1000)} seconds`);
|
|
35025
35122
|
}
|
|
35026
35123
|
async function ensureDaemonRunning() {
|
|
35027
35124
|
if (await pingInternal()) {
|
|
35028
35125
|
return;
|
|
35029
35126
|
}
|
|
35030
35127
|
if (!startPromise) {
|
|
35031
|
-
|
|
35032
|
-
|
|
35128
|
+
startPromise = withCrossProcessLock(startupLockPath, async () => {
|
|
35129
|
+
if (await pingInternal()) {
|
|
35130
|
+
return;
|
|
35131
|
+
}
|
|
35132
|
+
logger3.debug(`Starting cocod daemon via ${executable} init...`);
|
|
35133
|
+
await startDaemon();
|
|
35134
|
+
}, {
|
|
35135
|
+
acquireTimeoutMs: startupTimeoutMs + 30000,
|
|
35136
|
+
staleAfterMs: startupTimeoutMs + 30000,
|
|
35137
|
+
log: (message) => logger3.debug(message)
|
|
35138
|
+
}).finally(() => {
|
|
35033
35139
|
startPromise = null;
|
|
35034
35140
|
});
|
|
35035
35141
|
}
|
|
@@ -35257,6 +35363,9 @@ import { Readable } from "stream";
|
|
|
35257
35363
|
// src/utils/daemon-client.ts
|
|
35258
35364
|
import { existsSync as existsSync3 } from "fs";
|
|
35259
35365
|
|
|
35366
|
+
// src/start-daemon.ts
|
|
35367
|
+
var DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`;
|
|
35368
|
+
|
|
35260
35369
|
// src/utils/nip98.ts
|
|
35261
35370
|
init_esm2();
|
|
35262
35371
|
var NIP98_KIND = 27235;
|
|
@@ -35363,8 +35472,8 @@ import { join as join4 } from "path";
|
|
|
35363
35472
|
|
|
35364
35473
|
// src/integrations/opencode.ts
|
|
35365
35474
|
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
35366
|
-
import { readFile, writeFile } from "fs/promises";
|
|
35367
|
-
import { dirname } from "path";
|
|
35475
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
35476
|
+
import { dirname as dirname2 } from "path";
|
|
35368
35477
|
var OPENCODE_SMALL_MODEL = "routstr/minimax-m2.5";
|
|
35369
35478
|
async function installOpencodeIntegration(config, apiKey, integrationConfig) {
|
|
35370
35479
|
const { name, configPath } = integrationConfig;
|
|
@@ -35375,7 +35484,7 @@ Installing routstr models in opencode.json...`);
|
|
|
35375
35484
|
let opencodeConfig;
|
|
35376
35485
|
try {
|
|
35377
35486
|
if (existsSync4(configPath)) {
|
|
35378
|
-
const content2 = await
|
|
35487
|
+
const content2 = await readFile2(configPath, "utf-8");
|
|
35379
35488
|
opencodeConfig = JSON.parse(content2);
|
|
35380
35489
|
} else {
|
|
35381
35490
|
opencodeConfig = { provider: {} };
|
|
@@ -35387,7 +35496,7 @@ Installing routstr models in opencode.json...`);
|
|
|
35387
35496
|
opencodeConfig.provider = {};
|
|
35388
35497
|
}
|
|
35389
35498
|
try {
|
|
35390
|
-
mkdirSync(
|
|
35499
|
+
mkdirSync(dirname2(configPath), { recursive: true });
|
|
35391
35500
|
const data = await callDaemon("/models");
|
|
35392
35501
|
const models = data.output?.models || [];
|
|
35393
35502
|
if (models.length === 0) {
|
|
@@ -35409,7 +35518,7 @@ Installing routstr models in opencode.json...`);
|
|
|
35409
35518
|
models: modelsObj
|
|
35410
35519
|
};
|
|
35411
35520
|
opencodeConfig.small_model = OPENCODE_SMALL_MODEL;
|
|
35412
|
-
await
|
|
35521
|
+
await writeFile2(configPath, JSON.stringify(opencodeConfig, null, 2));
|
|
35413
35522
|
logger3.log(`Added "routstr" provider with ${models.length} models to opencode.json`);
|
|
35414
35523
|
} catch (error) {
|
|
35415
35524
|
logger3.error("Failed to install models in opencode.json:", error);
|
|
@@ -35418,8 +35527,8 @@ Installing routstr models in opencode.json...`);
|
|
|
35418
35527
|
|
|
35419
35528
|
// src/integrations/pi.ts
|
|
35420
35529
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
35421
|
-
import { readFile as
|
|
35422
|
-
import { dirname as
|
|
35530
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
35531
|
+
import { dirname as dirname3 } from "path";
|
|
35423
35532
|
async function installPiIntegration(config, apiKey, integrationConfig) {
|
|
35424
35533
|
const { name, configPath } = integrationConfig;
|
|
35425
35534
|
logger3.log(`
|
|
@@ -35429,7 +35538,7 @@ Installing routstr models in pi models.json...`);
|
|
|
35429
35538
|
let piConfig = {};
|
|
35430
35539
|
try {
|
|
35431
35540
|
if (existsSync5(configPath)) {
|
|
35432
|
-
const content2 = await
|
|
35541
|
+
const content2 = await readFile3(configPath, "utf-8");
|
|
35433
35542
|
piConfig = JSON.parse(content2);
|
|
35434
35543
|
}
|
|
35435
35544
|
} catch {
|
|
@@ -35439,7 +35548,7 @@ Installing routstr models in pi models.json...`);
|
|
|
35439
35548
|
piConfig.providers = {};
|
|
35440
35549
|
}
|
|
35441
35550
|
try {
|
|
35442
|
-
mkdirSync2(
|
|
35551
|
+
mkdirSync2(dirname3(configPath), { recursive: true });
|
|
35443
35552
|
const data = await callDaemon("/models");
|
|
35444
35553
|
const models = data.output?.models || [];
|
|
35445
35554
|
if (models.length === 0) {
|
|
@@ -35455,7 +35564,7 @@ Installing routstr models in pi models.json...`);
|
|
|
35455
35564
|
apiKey,
|
|
35456
35565
|
models: providerModels
|
|
35457
35566
|
};
|
|
35458
|
-
await
|
|
35567
|
+
await writeFile3(configPath, JSON.stringify(piConfig, null, 2));
|
|
35459
35568
|
logger3.log(`Added "routstr" provider with ${models.length} models to pi models.json`);
|
|
35460
35569
|
} catch (error) {
|
|
35461
35570
|
logger3.error("Failed to install models in pi models.json:", error);
|
|
@@ -35464,8 +35573,8 @@ Installing routstr models in pi models.json...`);
|
|
|
35464
35573
|
|
|
35465
35574
|
// src/integrations/openclaw.ts
|
|
35466
35575
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
35467
|
-
import { readFile as
|
|
35468
|
-
import { dirname as
|
|
35576
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
35577
|
+
import { dirname as dirname4 } from "path";
|
|
35469
35578
|
var OPENCLAW_PROVIDER_ID = "routstr";
|
|
35470
35579
|
var OPENCLAW_DEFAULT_PRIMARY_MODEL = "routstr/minimax-m2.5";
|
|
35471
35580
|
var OPENCLAW_DEFAULT_FALLBACK_MODEL = "routstr/kimi-k2.5";
|
|
@@ -35478,7 +35587,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
35478
35587
|
let openclawConfig = {};
|
|
35479
35588
|
try {
|
|
35480
35589
|
if (existsSync6(configPath)) {
|
|
35481
|
-
const content2 = await
|
|
35590
|
+
const content2 = await readFile4(configPath, "utf-8");
|
|
35482
35591
|
openclawConfig = JSON.parse(content2);
|
|
35483
35592
|
}
|
|
35484
35593
|
} catch {
|
|
@@ -35497,7 +35606,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
35497
35606
|
openclawConfig.agents.defaults = {};
|
|
35498
35607
|
}
|
|
35499
35608
|
try {
|
|
35500
|
-
mkdirSync3(
|
|
35609
|
+
mkdirSync3(dirname4(configPath), { recursive: true });
|
|
35501
35610
|
const data = await callDaemon("/models");
|
|
35502
35611
|
const models = data.output?.models || [];
|
|
35503
35612
|
if (models.length === 0) {
|
|
@@ -35529,7 +35638,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
35529
35638
|
fallbacks: [OPENCLAW_DEFAULT_FALLBACK_MODEL]
|
|
35530
35639
|
};
|
|
35531
35640
|
}
|
|
35532
|
-
await
|
|
35641
|
+
await writeFile4(configPath, JSON.stringify(openclawConfig, null, 2));
|
|
35533
35642
|
logger3.log(`Added "${OPENCLAW_PROVIDER_ID}" provider with ${models.length} models to openclaw.json`);
|
|
35534
35643
|
} catch (error) {
|
|
35535
35644
|
logger3.error("Failed to install models in openclaw.json:", error);
|
|
@@ -35538,8 +35647,8 @@ Installing routstr models in openclaw.json...`);
|
|
|
35538
35647
|
|
|
35539
35648
|
// src/integrations/claudecode.ts
|
|
35540
35649
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
35541
|
-
import { readFile as
|
|
35542
|
-
import { dirname as
|
|
35650
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
35651
|
+
import { dirname as dirname5 } from "path";
|
|
35543
35652
|
async function installClaudeCodeIntegration(config, apiKey, integrationConfig) {
|
|
35544
35653
|
const { name, configPath } = integrationConfig;
|
|
35545
35654
|
logger3.log(`
|
|
@@ -35549,7 +35658,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
35549
35658
|
let settings = {};
|
|
35550
35659
|
try {
|
|
35551
35660
|
if (existsSync7(configPath)) {
|
|
35552
|
-
const content2 = await
|
|
35661
|
+
const content2 = await readFile5(configPath, "utf-8");
|
|
35553
35662
|
settings = JSON.parse(content2);
|
|
35554
35663
|
}
|
|
35555
35664
|
} catch (error) {
|
|
@@ -35584,8 +35693,8 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
35584
35693
|
logger3.error("Failed to fetch models for Claude Code integration:", error);
|
|
35585
35694
|
}
|
|
35586
35695
|
try {
|
|
35587
|
-
mkdirSync4(
|
|
35588
|
-
await
|
|
35696
|
+
mkdirSync4(dirname5(configPath), { recursive: true });
|
|
35697
|
+
await writeFile5(configPath, JSON.stringify(settings, null, 2));
|
|
35589
35698
|
logger3.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
35590
35699
|
} catch (error) {
|
|
35591
35700
|
logger3.error(`Failed to write to ${configPath}:`, error);
|