routstrd 0.2.8 → 0.2.9
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 +293 -177
- package/package.json +1 -1
- package/src/daemon/wallet/cocod-client.ts +22 -6
- package/src/start-daemon.ts +52 -30
- package/src/utils/daemon-client.ts +6 -26
- package/src/utils/process-lock.ts +136 -0
package/dist/index.js
CHANGED
|
@@ -2122,6 +2122,71 @@ var require_commander = __commonJS((exports) => {
|
|
|
2122
2122
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2123
2123
|
});
|
|
2124
2124
|
|
|
2125
|
+
// src/utils/logger.ts
|
|
2126
|
+
import { appendFile, mkdir } from "fs/promises";
|
|
2127
|
+
import { existsSync } from "fs";
|
|
2128
|
+
import { join } from "path";
|
|
2129
|
+
function getLogFileForDate(date = new Date) {
|
|
2130
|
+
const year = date.getFullYear();
|
|
2131
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
2132
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2133
|
+
return join(LOGS_DIR, `${year}-${month}-${day}.log`);
|
|
2134
|
+
}
|
|
2135
|
+
async function ensureLogDir() {
|
|
2136
|
+
if (!existsSync(LOGS_DIR)) {
|
|
2137
|
+
await mkdir(LOGS_DIR, { recursive: true });
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
async function writeLog(level, ...args) {
|
|
2141
|
+
await ensureLogDir();
|
|
2142
|
+
const timestamp = new Date().toISOString();
|
|
2143
|
+
const message = args.map((a) => {
|
|
2144
|
+
if (a instanceof Error) {
|
|
2145
|
+
return `${a.message}${a.stack ? `
|
|
2146
|
+
${a.stack}` : ""}`;
|
|
2147
|
+
}
|
|
2148
|
+
if (typeof a === "object") {
|
|
2149
|
+
try {
|
|
2150
|
+
return JSON.stringify(a);
|
|
2151
|
+
} catch {
|
|
2152
|
+
return String(a);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
return String(a);
|
|
2156
|
+
}).join(" ");
|
|
2157
|
+
const line = `[${timestamp}] [${level}] ${message}
|
|
2158
|
+
`;
|
|
2159
|
+
const logFile = getLogFileForDate(new Date(timestamp));
|
|
2160
|
+
try {
|
|
2161
|
+
await appendFile(logFile, line);
|
|
2162
|
+
} catch (error) {
|
|
2163
|
+
console.error("Failed to write log:", error);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
var HOME, LOG_DIR, LOGS_DIR, logger;
|
|
2167
|
+
var init_logger = __esm(() => {
|
|
2168
|
+
HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
2169
|
+
LOG_DIR = process.env.ROUTSTRD_DIR || `${HOME}/.routstrd`;
|
|
2170
|
+
LOGS_DIR = join(LOG_DIR, "logs");
|
|
2171
|
+
logger = {
|
|
2172
|
+
log: (...args) => {
|
|
2173
|
+
console.log(...args);
|
|
2174
|
+
writeLog("INFO", ...args);
|
|
2175
|
+
},
|
|
2176
|
+
debug: (...args) => {
|
|
2177
|
+
writeLog("DEBUG", ...args);
|
|
2178
|
+
},
|
|
2179
|
+
error: (...args) => {
|
|
2180
|
+
console.error(...args);
|
|
2181
|
+
writeLog("ERROR", ...args);
|
|
2182
|
+
},
|
|
2183
|
+
info: (...args) => {
|
|
2184
|
+
console.log(...args);
|
|
2185
|
+
writeLog("INFO", ...args);
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2125
2190
|
// src/utils/config.ts
|
|
2126
2191
|
var HOME2, CONFIG_DIR, SOCKET_PATH, PID_FILE, DB_PATH, CONFIG_FILE, LOGS_DIR2, DEFAULT_CONFIG;
|
|
2127
2192
|
var init_config = __esm(() => {
|
|
@@ -2140,6 +2205,193 @@ var init_config = __esm(() => {
|
|
|
2140
2205
|
};
|
|
2141
2206
|
});
|
|
2142
2207
|
|
|
2208
|
+
// src/utils/process-lock.ts
|
|
2209
|
+
import { randomUUID } from "crypto";
|
|
2210
|
+
import { mkdir as mkdir2, readFile, rm, stat, writeFile } from "fs/promises";
|
|
2211
|
+
import { dirname } from "path";
|
|
2212
|
+
function delay(ms) {
|
|
2213
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2214
|
+
}
|
|
2215
|
+
function isProcessRunning(pid) {
|
|
2216
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
2217
|
+
return false;
|
|
2218
|
+
}
|
|
2219
|
+
try {
|
|
2220
|
+
process.kill(pid, 0);
|
|
2221
|
+
return true;
|
|
2222
|
+
} catch (error) {
|
|
2223
|
+
const code = error.code;
|
|
2224
|
+
return code === "EPERM";
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
async function readLockOwner(lockDir) {
|
|
2228
|
+
try {
|
|
2229
|
+
const raw = await readFile(`${lockDir}/owner.json`, "utf8");
|
|
2230
|
+
const parsed = JSON.parse(raw);
|
|
2231
|
+
if (typeof parsed.pid === "number" && typeof parsed.createdAt === "number") {
|
|
2232
|
+
return {
|
|
2233
|
+
pid: parsed.pid,
|
|
2234
|
+
createdAt: parsed.createdAt,
|
|
2235
|
+
token: typeof parsed.token === "string" ? parsed.token : undefined
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
} catch {
|
|
2239
|
+
}
|
|
2240
|
+
return null;
|
|
2241
|
+
}
|
|
2242
|
+
async function isLockStale(lockDir, staleAfterMs) {
|
|
2243
|
+
const owner = await readLockOwner(lockDir);
|
|
2244
|
+
if (owner) {
|
|
2245
|
+
return !isProcessRunning(owner.pid) || Date.now() - owner.createdAt > staleAfterMs;
|
|
2246
|
+
}
|
|
2247
|
+
try {
|
|
2248
|
+
const info = await stat(lockDir);
|
|
2249
|
+
return Date.now() - info.mtimeMs > staleAfterMs;
|
|
2250
|
+
} catch {
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
async function acquireCrossProcessLock(lockDir, options = {}) {
|
|
2255
|
+
const acquireTimeoutMs = options.acquireTimeoutMs ?? 120000;
|
|
2256
|
+
const retryIntervalMs = options.retryIntervalMs ?? 100;
|
|
2257
|
+
const staleAfterMs = options.staleAfterMs ?? 120000;
|
|
2258
|
+
const deadline = Date.now() + acquireTimeoutMs;
|
|
2259
|
+
await mkdir2(dirname(lockDir), { recursive: true });
|
|
2260
|
+
while (true) {
|
|
2261
|
+
try {
|
|
2262
|
+
await mkdir2(lockDir);
|
|
2263
|
+
const token = randomUUID();
|
|
2264
|
+
const owner = { pid: process.pid, createdAt: Date.now(), token };
|
|
2265
|
+
await writeFile(`${lockDir}/owner.json`, JSON.stringify(owner), "utf8");
|
|
2266
|
+
let released = false;
|
|
2267
|
+
return async () => {
|
|
2268
|
+
if (released)
|
|
2269
|
+
return;
|
|
2270
|
+
released = true;
|
|
2271
|
+
const currentOwner = await readLockOwner(lockDir);
|
|
2272
|
+
if (currentOwner?.token === token) {
|
|
2273
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
} catch (error) {
|
|
2277
|
+
const code = error.code;
|
|
2278
|
+
if (code !== "EEXIST") {
|
|
2279
|
+
throw error;
|
|
2280
|
+
}
|
|
2281
|
+
if (await isLockStale(lockDir, staleAfterMs)) {
|
|
2282
|
+
options.log?.(`Removing stale lock at ${lockDir}`);
|
|
2283
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
2284
|
+
continue;
|
|
2285
|
+
}
|
|
2286
|
+
if (Date.now() >= deadline) {
|
|
2287
|
+
throw new Error(`Timed out waiting to acquire lock ${lockDir}`);
|
|
2288
|
+
}
|
|
2289
|
+
await delay(retryIntervalMs);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
async function withCrossProcessLock(lockDir, fn, options = {}) {
|
|
2294
|
+
const release = await acquireCrossProcessLock(lockDir, options);
|
|
2295
|
+
try {
|
|
2296
|
+
return await fn();
|
|
2297
|
+
} finally {
|
|
2298
|
+
await release();
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
var init_process_lock = () => {
|
|
2302
|
+
};
|
|
2303
|
+
|
|
2304
|
+
// src/start-daemon.ts
|
|
2305
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2306
|
+
function getTodayLogFile() {
|
|
2307
|
+
const now = new Date;
|
|
2308
|
+
const year = now.getFullYear();
|
|
2309
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2310
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
2311
|
+
return `${LOGS_DIR2}/${year}-${month}-${day}.log`;
|
|
2312
|
+
}
|
|
2313
|
+
async function isDaemonHealthy(port) {
|
|
2314
|
+
const controller = new AbortController;
|
|
2315
|
+
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
2316
|
+
try {
|
|
2317
|
+
const existing = await fetch(`http://localhost:${port}/health`, {
|
|
2318
|
+
signal: controller.signal
|
|
2319
|
+
});
|
|
2320
|
+
return existing.ok;
|
|
2321
|
+
} catch {
|
|
2322
|
+
return false;
|
|
2323
|
+
} finally {
|
|
2324
|
+
clearTimeout(timeoutId);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
async function startDaemonUnlocked(options) {
|
|
2328
|
+
const args = [];
|
|
2329
|
+
const port = options.port || "8008";
|
|
2330
|
+
const pollIntervalMs = 250;
|
|
2331
|
+
const startupTimeoutMs = 10 * 60 * 1000;
|
|
2332
|
+
if (await isDaemonHealthy(port)) {
|
|
2333
|
+
logger.log(`Routstr daemon already running on http://localhost:${port}/v1`);
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (options.port) {
|
|
2337
|
+
args.push("--port", options.port);
|
|
2338
|
+
}
|
|
2339
|
+
if (options.provider) {
|
|
2340
|
+
args.push("--provider", options.provider);
|
|
2341
|
+
}
|
|
2342
|
+
if (!existsSync2(LOGS_DIR2)) {
|
|
2343
|
+
await Bun.$`mkdir -p ${LOGS_DIR2}`;
|
|
2344
|
+
}
|
|
2345
|
+
const daemonScript = new URL("./daemon/index.js", import.meta.url).pathname;
|
|
2346
|
+
const todayLogFile = getTodayLogFile();
|
|
2347
|
+
const shellCmd = `bun run "${daemonScript}" ${args.map((a) => `'${a}'`).join(" ")} >> "${todayLogFile}" 2>&1`;
|
|
2348
|
+
const proc = Bun.spawn(["sh", "-c", shellCmd], {
|
|
2349
|
+
stdout: "inherit",
|
|
2350
|
+
stderr: "inherit",
|
|
2351
|
+
stdin: "ignore",
|
|
2352
|
+
detached: true
|
|
2353
|
+
});
|
|
2354
|
+
proc.unref();
|
|
2355
|
+
let exitCode = null;
|
|
2356
|
+
proc.exited.then((code) => {
|
|
2357
|
+
exitCode = code;
|
|
2358
|
+
});
|
|
2359
|
+
const maxPolls = Math.ceil(startupTimeoutMs / pollIntervalMs);
|
|
2360
|
+
for (let i = 0;i < maxPolls; i++) {
|
|
2361
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2362
|
+
if (exitCode !== null) {
|
|
2363
|
+
throw new Error(`Daemon process exited early with code ${exitCode}. Check logs in ${LOGS_DIR2}`);
|
|
2364
|
+
}
|
|
2365
|
+
if (await isDaemonHealthy(port)) {
|
|
2366
|
+
logger.log(`Routstr daemon started (PID: ${proc.pid}).`);
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
throw new Error(`Daemon failed to start within ${Math.round(startupTimeoutMs / 1000)} seconds. Check logs in ${LOGS_DIR2}`);
|
|
2371
|
+
}
|
|
2372
|
+
async function startDaemon(options = {}) {
|
|
2373
|
+
const port = options.port || "8008";
|
|
2374
|
+
const startupTimeoutMs = 10 * 60 * 1000;
|
|
2375
|
+
if (await isDaemonHealthy(port)) {
|
|
2376
|
+
logger.log(`Routstr daemon already running on http://localhost:${port}/v1`);
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
await withCrossProcessLock(DAEMON_STARTUP_LOCK_PATH, async () => {
|
|
2380
|
+
await startDaemonUnlocked(options);
|
|
2381
|
+
}, {
|
|
2382
|
+
acquireTimeoutMs: startupTimeoutMs + 30000,
|
|
2383
|
+
staleAfterMs: startupTimeoutMs + 30000,
|
|
2384
|
+
log: (message) => logger.debug(message)
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
var DAEMON_STARTUP_LOCK_PATH;
|
|
2388
|
+
var init_start_daemon = __esm(() => {
|
|
2389
|
+
init_logger();
|
|
2390
|
+
init_config();
|
|
2391
|
+
init_process_lock();
|
|
2392
|
+
DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`;
|
|
2393
|
+
});
|
|
2394
|
+
|
|
2143
2395
|
// node_modules/nostr-tools/node_modules/@noble/curves/node_modules/@noble/hashes/esm/_assert.js
|
|
2144
2396
|
function number(n) {
|
|
2145
2397
|
if (!Number.isSafeInteger(n) || n < 0)
|
|
@@ -9325,23 +9577,11 @@ function getNpubSuffix(config) {
|
|
|
9325
9577
|
return npub.slice(-7);
|
|
9326
9578
|
}
|
|
9327
9579
|
async function startDaemonProcess() {
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
stdout: "inherit",
|
|
9333
|
-
stderr: "inherit",
|
|
9334
|
-
stdin: "ignore",
|
|
9335
|
-
detached: true
|
|
9580
|
+
const config = await loadConfig();
|
|
9581
|
+
await startDaemon({
|
|
9582
|
+
port: String(config.port || 8008),
|
|
9583
|
+
provider: config.provider || undefined
|
|
9336
9584
|
});
|
|
9337
|
-
proc.unref();
|
|
9338
|
-
for (let i2 = 0;i2 < 50; i2++) {
|
|
9339
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
9340
|
-
if (await isDaemonRunning()) {
|
|
9341
|
-
return;
|
|
9342
|
-
}
|
|
9343
|
-
}
|
|
9344
|
-
throw new Error("Daemon failed to start within 5 seconds");
|
|
9345
9585
|
}
|
|
9346
9586
|
async function ensureDaemonRunning() {
|
|
9347
9587
|
if (await isDaemonRunning()) {
|
|
@@ -9386,6 +9626,7 @@ async function handleDaemonCommand(path, options = {}) {
|
|
|
9386
9626
|
}
|
|
9387
9627
|
}
|
|
9388
9628
|
var init_daemon_client = __esm(() => {
|
|
9629
|
+
init_start_daemon();
|
|
9389
9630
|
init_config();
|
|
9390
9631
|
init_nip98();
|
|
9391
9632
|
});
|
|
@@ -14667,157 +14908,24 @@ var {
|
|
|
14667
14908
|
Help
|
|
14668
14909
|
} = import__.default;
|
|
14669
14910
|
|
|
14670
|
-
// src/utils/logger.ts
|
|
14671
|
-
import { appendFile, mkdir } from "fs/promises";
|
|
14672
|
-
import { existsSync } from "fs";
|
|
14673
|
-
import { join } from "path";
|
|
14674
|
-
var HOME = process.env.HOME || process.env.USERPROFILE || "";
|
|
14675
|
-
var LOG_DIR = process.env.ROUTSTRD_DIR || `${HOME}/.routstrd`;
|
|
14676
|
-
var LOGS_DIR = join(LOG_DIR, "logs");
|
|
14677
|
-
function getLogFileForDate(date = new Date) {
|
|
14678
|
-
const year = date.getFullYear();
|
|
14679
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
14680
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
14681
|
-
return join(LOGS_DIR, `${year}-${month}-${day}.log`);
|
|
14682
|
-
}
|
|
14683
|
-
async function ensureLogDir() {
|
|
14684
|
-
if (!existsSync(LOGS_DIR)) {
|
|
14685
|
-
await mkdir(LOGS_DIR, { recursive: true });
|
|
14686
|
-
}
|
|
14687
|
-
}
|
|
14688
|
-
async function writeLog(level, ...args) {
|
|
14689
|
-
await ensureLogDir();
|
|
14690
|
-
const timestamp = new Date().toISOString();
|
|
14691
|
-
const message = args.map((a) => {
|
|
14692
|
-
if (a instanceof Error) {
|
|
14693
|
-
return `${a.message}${a.stack ? `
|
|
14694
|
-
${a.stack}` : ""}`;
|
|
14695
|
-
}
|
|
14696
|
-
if (typeof a === "object") {
|
|
14697
|
-
try {
|
|
14698
|
-
return JSON.stringify(a);
|
|
14699
|
-
} catch {
|
|
14700
|
-
return String(a);
|
|
14701
|
-
}
|
|
14702
|
-
}
|
|
14703
|
-
return String(a);
|
|
14704
|
-
}).join(" ");
|
|
14705
|
-
const line = `[${timestamp}] [${level}] ${message}
|
|
14706
|
-
`;
|
|
14707
|
-
const logFile = getLogFileForDate(new Date(timestamp));
|
|
14708
|
-
try {
|
|
14709
|
-
await appendFile(logFile, line);
|
|
14710
|
-
} catch (error) {
|
|
14711
|
-
console.error("Failed to write log:", error);
|
|
14712
|
-
}
|
|
14713
|
-
}
|
|
14714
|
-
var logger = {
|
|
14715
|
-
log: (...args) => {
|
|
14716
|
-
console.log(...args);
|
|
14717
|
-
writeLog("INFO", ...args);
|
|
14718
|
-
},
|
|
14719
|
-
debug: (...args) => {
|
|
14720
|
-
writeLog("DEBUG", ...args);
|
|
14721
|
-
},
|
|
14722
|
-
error: (...args) => {
|
|
14723
|
-
console.error(...args);
|
|
14724
|
-
writeLog("ERROR", ...args);
|
|
14725
|
-
},
|
|
14726
|
-
info: (...args) => {
|
|
14727
|
-
console.log(...args);
|
|
14728
|
-
writeLog("INFO", ...args);
|
|
14729
|
-
}
|
|
14730
|
-
};
|
|
14731
|
-
|
|
14732
|
-
// src/start-daemon.ts
|
|
14733
|
-
init_config();
|
|
14734
|
-
import { existsSync as existsSync2 } from "fs";
|
|
14735
|
-
function getTodayLogFile() {
|
|
14736
|
-
const now = new Date;
|
|
14737
|
-
const year = now.getFullYear();
|
|
14738
|
-
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
14739
|
-
const day = String(now.getDate()).padStart(2, "0");
|
|
14740
|
-
return `${LOGS_DIR2}/${year}-${month}-${day}.log`;
|
|
14741
|
-
}
|
|
14742
|
-
async function startDaemon(options = {}) {
|
|
14743
|
-
const args = [];
|
|
14744
|
-
const port = options.port || "8008";
|
|
14745
|
-
const pollIntervalMs = 250;
|
|
14746
|
-
const startupTimeoutMs = 10 * 60 * 1000;
|
|
14747
|
-
try {
|
|
14748
|
-
const controller = new AbortController;
|
|
14749
|
-
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
14750
|
-
const existing = await fetch(`http://localhost:${port}/health`, {
|
|
14751
|
-
signal: controller.signal
|
|
14752
|
-
});
|
|
14753
|
-
clearTimeout(timeoutId);
|
|
14754
|
-
if (existing.ok) {
|
|
14755
|
-
logger.log(`Routstr daemon already running on http://localhost:${port}/v1`);
|
|
14756
|
-
return;
|
|
14757
|
-
}
|
|
14758
|
-
} catch {
|
|
14759
|
-
}
|
|
14760
|
-
if (options.port) {
|
|
14761
|
-
args.push("--port", options.port);
|
|
14762
|
-
}
|
|
14763
|
-
if (options.provider) {
|
|
14764
|
-
args.push("--provider", options.provider);
|
|
14765
|
-
}
|
|
14766
|
-
if (!existsSync2(LOGS_DIR2)) {
|
|
14767
|
-
await Bun.$`mkdir -p ${LOGS_DIR2}`;
|
|
14768
|
-
}
|
|
14769
|
-
const daemonScript = new URL("./daemon/index.js", import.meta.url).pathname;
|
|
14770
|
-
const todayLogFile = getTodayLogFile();
|
|
14771
|
-
const shellCmd = `bun run "${daemonScript}" ${args.map((a) => `'${a}'`).join(" ")} >> "${todayLogFile}" 2>&1`;
|
|
14772
|
-
const proc = Bun.spawn(["sh", "-c", shellCmd], {
|
|
14773
|
-
stdout: "inherit",
|
|
14774
|
-
stderr: "inherit",
|
|
14775
|
-
stdin: "ignore",
|
|
14776
|
-
detached: true
|
|
14777
|
-
});
|
|
14778
|
-
proc.unref();
|
|
14779
|
-
let exitCode = null;
|
|
14780
|
-
proc.exited.then((code) => {
|
|
14781
|
-
exitCode = code;
|
|
14782
|
-
});
|
|
14783
|
-
const maxPolls = Math.ceil(startupTimeoutMs / pollIntervalMs);
|
|
14784
|
-
for (let i = 0;i < maxPolls; i++) {
|
|
14785
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
14786
|
-
if (exitCode !== null) {
|
|
14787
|
-
throw new Error(`Daemon process exited early with code ${exitCode}. Check logs in ${LOGS_DIR2}`);
|
|
14788
|
-
}
|
|
14789
|
-
try {
|
|
14790
|
-
const controller = new AbortController;
|
|
14791
|
-
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
14792
|
-
const res = await fetch(`http://localhost:${port}/health`, {
|
|
14793
|
-
signal: controller.signal
|
|
14794
|
-
});
|
|
14795
|
-
clearTimeout(timeoutId);
|
|
14796
|
-
if (res.ok) {
|
|
14797
|
-
logger.log(`Routstr daemon started (PID: ${proc.pid}).`);
|
|
14798
|
-
return;
|
|
14799
|
-
}
|
|
14800
|
-
} catch {
|
|
14801
|
-
}
|
|
14802
|
-
}
|
|
14803
|
-
throw new Error(`Daemon failed to start within ${Math.round(startupTimeoutMs / 1000)} seconds. Check logs in ${LOGS_DIR2}`);
|
|
14804
|
-
}
|
|
14805
|
-
|
|
14806
14911
|
// src/cli.ts
|
|
14912
|
+
init_start_daemon();
|
|
14807
14913
|
init_daemon_client();
|
|
14808
14914
|
|
|
14809
14915
|
// src/utils/clients.ts
|
|
14810
14916
|
init_daemon_client();
|
|
14811
14917
|
init_nip98();
|
|
14918
|
+
init_logger();
|
|
14812
14919
|
|
|
14813
14920
|
// src/integrations/registry.ts
|
|
14814
14921
|
import { join as join3 } from "path";
|
|
14815
14922
|
|
|
14816
14923
|
// src/integrations/opencode.ts
|
|
14817
|
-
|
|
14818
|
-
import { readFile, writeFile } from "fs/promises";
|
|
14819
|
-
import { dirname } from "path";
|
|
14924
|
+
init_logger();
|
|
14820
14925
|
init_daemon_client();
|
|
14926
|
+
import { existsSync as existsSync4, mkdirSync } from "fs";
|
|
14927
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
14928
|
+
import { dirname as dirname2 } from "path";
|
|
14821
14929
|
var OPENCODE_SMALL_MODEL = "routstr/minimax-m2.5";
|
|
14822
14930
|
async function installOpencodeIntegration(config, apiKey, integrationConfig) {
|
|
14823
14931
|
const { name, configPath } = integrationConfig;
|
|
@@ -14828,7 +14936,7 @@ Installing routstr models in opencode.json...`);
|
|
|
14828
14936
|
let opencodeConfig;
|
|
14829
14937
|
try {
|
|
14830
14938
|
if (existsSync4(configPath)) {
|
|
14831
|
-
const content = await
|
|
14939
|
+
const content = await readFile2(configPath, "utf-8");
|
|
14832
14940
|
opencodeConfig = JSON.parse(content);
|
|
14833
14941
|
} else {
|
|
14834
14942
|
opencodeConfig = { provider: {} };
|
|
@@ -14840,7 +14948,7 @@ Installing routstr models in opencode.json...`);
|
|
|
14840
14948
|
opencodeConfig.provider = {};
|
|
14841
14949
|
}
|
|
14842
14950
|
try {
|
|
14843
|
-
mkdirSync(
|
|
14951
|
+
mkdirSync(dirname2(configPath), { recursive: true });
|
|
14844
14952
|
const data = await callDaemon("/models");
|
|
14845
14953
|
const models = data.output?.models || [];
|
|
14846
14954
|
if (models.length === 0) {
|
|
@@ -14862,7 +14970,7 @@ Installing routstr models in opencode.json...`);
|
|
|
14862
14970
|
models: modelsObj
|
|
14863
14971
|
};
|
|
14864
14972
|
opencodeConfig.small_model = OPENCODE_SMALL_MODEL;
|
|
14865
|
-
await
|
|
14973
|
+
await writeFile2(configPath, JSON.stringify(opencodeConfig, null, 2));
|
|
14866
14974
|
logger.log(`Added "routstr" provider with ${models.length} models to opencode.json`);
|
|
14867
14975
|
} catch (error) {
|
|
14868
14976
|
logger.error("Failed to install models in opencode.json:", error);
|
|
@@ -14870,10 +14978,11 @@ Installing routstr models in opencode.json...`);
|
|
|
14870
14978
|
}
|
|
14871
14979
|
|
|
14872
14980
|
// src/integrations/pi.ts
|
|
14873
|
-
|
|
14874
|
-
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
14875
|
-
import { dirname as dirname2 } from "path";
|
|
14981
|
+
init_logger();
|
|
14876
14982
|
init_daemon_client();
|
|
14983
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
14984
|
+
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
14985
|
+
import { dirname as dirname3 } from "path";
|
|
14877
14986
|
async function installPiIntegration(config, apiKey, integrationConfig) {
|
|
14878
14987
|
const { name, configPath } = integrationConfig;
|
|
14879
14988
|
logger.log(`
|
|
@@ -14883,7 +14992,7 @@ Installing routstr models in pi models.json...`);
|
|
|
14883
14992
|
let piConfig = {};
|
|
14884
14993
|
try {
|
|
14885
14994
|
if (existsSync5(configPath)) {
|
|
14886
|
-
const content = await
|
|
14995
|
+
const content = await readFile3(configPath, "utf-8");
|
|
14887
14996
|
piConfig = JSON.parse(content);
|
|
14888
14997
|
}
|
|
14889
14998
|
} catch {
|
|
@@ -14893,7 +15002,7 @@ Installing routstr models in pi models.json...`);
|
|
|
14893
15002
|
piConfig.providers = {};
|
|
14894
15003
|
}
|
|
14895
15004
|
try {
|
|
14896
|
-
mkdirSync2(
|
|
15005
|
+
mkdirSync2(dirname3(configPath), { recursive: true });
|
|
14897
15006
|
const data = await callDaemon("/models");
|
|
14898
15007
|
const models = data.output?.models || [];
|
|
14899
15008
|
if (models.length === 0) {
|
|
@@ -14909,7 +15018,7 @@ Installing routstr models in pi models.json...`);
|
|
|
14909
15018
|
apiKey,
|
|
14910
15019
|
models: providerModels
|
|
14911
15020
|
};
|
|
14912
|
-
await
|
|
15021
|
+
await writeFile3(configPath, JSON.stringify(piConfig, null, 2));
|
|
14913
15022
|
logger.log(`Added "routstr" provider with ${models.length} models to pi models.json`);
|
|
14914
15023
|
} catch (error) {
|
|
14915
15024
|
logger.error("Failed to install models in pi models.json:", error);
|
|
@@ -14917,10 +15026,11 @@ Installing routstr models in pi models.json...`);
|
|
|
14917
15026
|
}
|
|
14918
15027
|
|
|
14919
15028
|
// src/integrations/openclaw.ts
|
|
14920
|
-
|
|
14921
|
-
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
14922
|
-
import { dirname as dirname3 } from "path";
|
|
15029
|
+
init_logger();
|
|
14923
15030
|
init_daemon_client();
|
|
15031
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
15032
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
15033
|
+
import { dirname as dirname4 } from "path";
|
|
14924
15034
|
var OPENCLAW_PROVIDER_ID = "routstr";
|
|
14925
15035
|
var OPENCLAW_DEFAULT_PRIMARY_MODEL = "routstr/minimax-m2.5";
|
|
14926
15036
|
var OPENCLAW_DEFAULT_FALLBACK_MODEL = "routstr/kimi-k2.5";
|
|
@@ -14933,7 +15043,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
14933
15043
|
let openclawConfig = {};
|
|
14934
15044
|
try {
|
|
14935
15045
|
if (existsSync6(configPath)) {
|
|
14936
|
-
const content = await
|
|
15046
|
+
const content = await readFile4(configPath, "utf-8");
|
|
14937
15047
|
openclawConfig = JSON.parse(content);
|
|
14938
15048
|
}
|
|
14939
15049
|
} catch {
|
|
@@ -14952,7 +15062,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
14952
15062
|
openclawConfig.agents.defaults = {};
|
|
14953
15063
|
}
|
|
14954
15064
|
try {
|
|
14955
|
-
mkdirSync3(
|
|
15065
|
+
mkdirSync3(dirname4(configPath), { recursive: true });
|
|
14956
15066
|
const data = await callDaemon("/models");
|
|
14957
15067
|
const models = data.output?.models || [];
|
|
14958
15068
|
if (models.length === 0) {
|
|
@@ -14984,7 +15094,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
14984
15094
|
fallbacks: [OPENCLAW_DEFAULT_FALLBACK_MODEL]
|
|
14985
15095
|
};
|
|
14986
15096
|
}
|
|
14987
|
-
await
|
|
15097
|
+
await writeFile4(configPath, JSON.stringify(openclawConfig, null, 2));
|
|
14988
15098
|
logger.log(`Added "${OPENCLAW_PROVIDER_ID}" provider with ${models.length} models to openclaw.json`);
|
|
14989
15099
|
} catch (error) {
|
|
14990
15100
|
logger.error("Failed to install models in openclaw.json:", error);
|
|
@@ -14992,10 +15102,11 @@ Installing routstr models in openclaw.json...`);
|
|
|
14992
15102
|
}
|
|
14993
15103
|
|
|
14994
15104
|
// src/integrations/claudecode.ts
|
|
14995
|
-
|
|
14996
|
-
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
14997
|
-
import { dirname as dirname4 } from "path";
|
|
15105
|
+
init_logger();
|
|
14998
15106
|
init_daemon_client();
|
|
15107
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
15108
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
15109
|
+
import { dirname as dirname5 } from "path";
|
|
14999
15110
|
async function installClaudeCodeIntegration(config, apiKey, integrationConfig) {
|
|
15000
15111
|
const { name, configPath } = integrationConfig;
|
|
15001
15112
|
logger.log(`
|
|
@@ -15005,7 +15116,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
15005
15116
|
let settings = {};
|
|
15006
15117
|
try {
|
|
15007
15118
|
if (existsSync7(configPath)) {
|
|
15008
|
-
const content = await
|
|
15119
|
+
const content = await readFile5(configPath, "utf-8");
|
|
15009
15120
|
settings = JSON.parse(content);
|
|
15010
15121
|
}
|
|
15011
15122
|
} catch (error) {
|
|
@@ -15040,8 +15151,8 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
15040
15151
|
logger.error("Failed to fetch models for Claude Code integration:", error);
|
|
15041
15152
|
}
|
|
15042
15153
|
try {
|
|
15043
|
-
mkdirSync4(
|
|
15044
|
-
await
|
|
15154
|
+
mkdirSync4(dirname5(configPath), { recursive: true });
|
|
15155
|
+
await writeFile5(configPath, JSON.stringify(settings, null, 2));
|
|
15045
15156
|
logger.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
15046
15157
|
} catch (error) {
|
|
15047
15158
|
logger.error(`Failed to write to ${configPath}:`, error);
|
|
@@ -15267,9 +15378,12 @@ async function addClientAction(options) {
|
|
|
15267
15378
|
|
|
15268
15379
|
// src/cli.ts
|
|
15269
15380
|
init_config();
|
|
15381
|
+
init_logger();
|
|
15270
15382
|
import { existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
15271
15383
|
import { execSync } from "child_process";
|
|
15384
|
+
|
|
15272
15385
|
// src/integrations/index.ts
|
|
15386
|
+
init_logger();
|
|
15273
15387
|
function ask(question) {
|
|
15274
15388
|
process.stdout.write(question);
|
|
15275
15389
|
if (!process.stdin.isTTY) {
|
|
@@ -15413,6 +15527,8 @@ init_esm2();
|
|
|
15413
15527
|
|
|
15414
15528
|
// src/daemon/wallet/cocod-client.ts
|
|
15415
15529
|
import { existsSync as existsSync8 } from "fs";
|
|
15530
|
+
init_logger();
|
|
15531
|
+
init_process_lock();
|
|
15416
15532
|
var DEFAULT_CONFIG_DIR = process.env.COCOD_DIR || `${process.env.HOME || process.env.USERPROFILE || ""}/.cocod`;
|
|
15417
15533
|
var DEFAULT_SOCKET_PATH = process.env.COCOD_SOCKET || `${DEFAULT_CONFIG_DIR}/cocod.sock`;
|
|
15418
15534
|
function resolveCocodExecutable(cocodPath) {
|
|
@@ -15438,7 +15554,7 @@ async function isCocodInstalled(cocodPath) {
|
|
|
15438
15554
|
// package.json
|
|
15439
15555
|
var package_default = {
|
|
15440
15556
|
name: "routstrd",
|
|
15441
|
-
version: "0.2.
|
|
15557
|
+
version: "0.2.9",
|
|
15442
15558
|
module: "src/index.ts",
|
|
15443
15559
|
type: "module",
|
|
15444
15560
|
private: false,
|