routstrd 0.4.1 → 0.4.2
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/bun.lock +316 -2
- package/dist/daemon/index.js +69744 -54589
- package/dist/index.js +183 -85
- package/package.json +2 -2
- package/src/cli.ts +98 -12
- package/src/daemon/config-store.ts +79 -4
- package/src/daemon/models.test.ts +81 -0
- package/src/daemon/models.ts +18 -2
- package/src/integrations/pi.ts +17 -1
- package/src/tui/usage/data.ts +13 -2
- package/src/tui/usage/render.ts +8 -0
- package/src/utils/config.ts +3 -1
- package/tests/daemon/config-store.perms.test.ts +55 -0
- package/tests/daemon/config-store.scenario.ts +113 -0
package/dist/index.js
CHANGED
|
@@ -21234,7 +21234,10 @@ var init_constants = __esm(() => {
|
|
|
21234
21234
|
// src/tui/usage/data.ts
|
|
21235
21235
|
async function fetchStatus() {
|
|
21236
21236
|
try {
|
|
21237
|
-
const result = await
|
|
21237
|
+
const [result, config] = await Promise.all([
|
|
21238
|
+
callDaemon("/status"),
|
|
21239
|
+
loadConfig()
|
|
21240
|
+
]);
|
|
21238
21241
|
if (result.error)
|
|
21239
21242
|
return null;
|
|
21240
21243
|
const output = result.output;
|
|
@@ -21242,6 +21245,7 @@ async function fetchStatus() {
|
|
|
21242
21245
|
daemon: output?.daemon || "unknown",
|
|
21243
21246
|
wallet: output?.wallet || "unknown",
|
|
21244
21247
|
mode: output?.mode || "apikeys",
|
|
21248
|
+
url: getDaemonBaseUrl(config),
|
|
21245
21249
|
error: output?.error
|
|
21246
21250
|
};
|
|
21247
21251
|
} catch {
|
|
@@ -21559,6 +21563,12 @@ function renderOverview(stats, balance, status, width) {
|
|
|
21559
21563
|
const walletColor = status.wallet === "connected" ? COLORS.green : COLORS.red;
|
|
21560
21564
|
const modeColor = COLORS.cyan;
|
|
21561
21565
|
statusLines.push(`${COLORS.bold}Daemon:${COLORS.reset} ${daemonColor}${status.daemon}${COLORS.reset}`);
|
|
21566
|
+
if (status.url) {
|
|
21567
|
+
const label = "Running on: ";
|
|
21568
|
+
const maxUrlLen = Math.max(8, halfWidth2 - 4 - label.length);
|
|
21569
|
+
const url = status.url.length > maxUrlLen ? `${status.url.slice(0, Math.max(0, maxUrlLen - 1))}\u2026` : status.url;
|
|
21570
|
+
statusLines.push(`${COLORS.bold}${label}${COLORS.reset} ${COLORS.cyan}${url}${COLORS.reset}`);
|
|
21571
|
+
}
|
|
21562
21572
|
statusLines.push(`${COLORS.bold}Wallet:${COLORS.reset} ${walletColor}${status.wallet}${COLORS.reset}`);
|
|
21563
21573
|
statusLines.push(`${COLORS.bold}Mode:${COLORS.reset} ${modeColor}${status.mode}${COLORS.reset}`);
|
|
21564
21574
|
if (status.error) {
|
|
@@ -22276,6 +22286,53 @@ var {
|
|
|
22276
22286
|
|
|
22277
22287
|
// src/cli.ts
|
|
22278
22288
|
init_start_daemon();
|
|
22289
|
+
|
|
22290
|
+
// src/daemon/config-store.ts
|
|
22291
|
+
init_config();
|
|
22292
|
+
init_logger();
|
|
22293
|
+
import {
|
|
22294
|
+
chmodSync,
|
|
22295
|
+
existsSync as existsSync4,
|
|
22296
|
+
mkdirSync as mkdirSync2,
|
|
22297
|
+
readFileSync,
|
|
22298
|
+
renameSync,
|
|
22299
|
+
unlinkSync,
|
|
22300
|
+
writeFileSync
|
|
22301
|
+
} from "fs";
|
|
22302
|
+
var REQUESTS_DIR = `${CONFIG_DIR}/requests`;
|
|
22303
|
+
function chmodIgnoreErrors(path, mode) {
|
|
22304
|
+
try {
|
|
22305
|
+
chmodSync(path, mode);
|
|
22306
|
+
} catch {}
|
|
22307
|
+
}
|
|
22308
|
+
function ensureDirsSync() {
|
|
22309
|
+
try {
|
|
22310
|
+
mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
22311
|
+
} catch {}
|
|
22312
|
+
chmodSync(CONFIG_DIR, 448);
|
|
22313
|
+
try {
|
|
22314
|
+
mkdirSync2(REQUESTS_DIR, { recursive: true, mode: 448 });
|
|
22315
|
+
} catch {}
|
|
22316
|
+
chmodSync(REQUESTS_DIR, 448);
|
|
22317
|
+
}
|
|
22318
|
+
function saveDaemonConfig(config) {
|
|
22319
|
+
ensureDirsSync();
|
|
22320
|
+
const temporaryFile = `${CONFIG_FILE}.${process.pid}.tmp`;
|
|
22321
|
+
try {
|
|
22322
|
+
writeFileSync(temporaryFile, JSON.stringify(config, null, 2), {
|
|
22323
|
+
mode: 384
|
|
22324
|
+
});
|
|
22325
|
+
renameSync(temporaryFile, CONFIG_FILE);
|
|
22326
|
+
} catch (error) {
|
|
22327
|
+
try {
|
|
22328
|
+
unlinkSync(temporaryFile);
|
|
22329
|
+
} catch {}
|
|
22330
|
+
throw error;
|
|
22331
|
+
}
|
|
22332
|
+
chmodIgnoreErrors(CONFIG_FILE, 384);
|
|
22333
|
+
}
|
|
22334
|
+
|
|
22335
|
+
// src/cli.ts
|
|
22279
22336
|
init_daemon_client();
|
|
22280
22337
|
|
|
22281
22338
|
// src/utils/clients.ts
|
|
@@ -22287,7 +22344,7 @@ import { join as join3 } from "path";
|
|
|
22287
22344
|
|
|
22288
22345
|
// src/integrations/opencode.ts
|
|
22289
22346
|
init_daemon_client();
|
|
22290
|
-
import { existsSync as
|
|
22347
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
22291
22348
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
22292
22349
|
import { dirname as dirname2 } from "path";
|
|
22293
22350
|
var OPENCODE_SMALL_MODEL = "routstr/minimax-m2.5";
|
|
@@ -22299,7 +22356,7 @@ Installing routstr models in opencode.json...`);
|
|
|
22299
22356
|
const baseUrl = getDaemonBaseUrl(config);
|
|
22300
22357
|
let opencodeConfig;
|
|
22301
22358
|
try {
|
|
22302
|
-
if (
|
|
22359
|
+
if (existsSync5(configPath)) {
|
|
22303
22360
|
const content = await readFile2(configPath, "utf-8");
|
|
22304
22361
|
opencodeConfig = JSON.parse(content);
|
|
22305
22362
|
} else {
|
|
@@ -22313,7 +22370,7 @@ Installing routstr models in opencode.json...`);
|
|
|
22313
22370
|
opencodeConfig.provider = {};
|
|
22314
22371
|
}
|
|
22315
22372
|
try {
|
|
22316
|
-
|
|
22373
|
+
mkdirSync3(dirname2(configPath), { recursive: true });
|
|
22317
22374
|
const data = await callDaemon("/models");
|
|
22318
22375
|
const models = data.output?.models || [];
|
|
22319
22376
|
if (models.length === 0) {
|
|
@@ -22344,7 +22401,7 @@ Installing routstr models in opencode.json...`);
|
|
|
22344
22401
|
|
|
22345
22402
|
// src/integrations/pi.ts
|
|
22346
22403
|
init_daemon_client();
|
|
22347
|
-
import { existsSync as
|
|
22404
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
22348
22405
|
import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
22349
22406
|
import { dirname as dirname3 } from "path";
|
|
22350
22407
|
async function installPiIntegration(config, apiKey, integrationConfig) {
|
|
@@ -22355,7 +22412,7 @@ Installing routstr models in pi models.json...`);
|
|
|
22355
22412
|
const baseUrl = `${getDaemonBaseUrl(config)}/v1`;
|
|
22356
22413
|
let piConfig = {};
|
|
22357
22414
|
try {
|
|
22358
|
-
if (
|
|
22415
|
+
if (existsSync6(configPath)) {
|
|
22359
22416
|
const content = await readFile3(configPath, "utf-8");
|
|
22360
22417
|
piConfig = JSON.parse(content);
|
|
22361
22418
|
}
|
|
@@ -22367,21 +22424,25 @@ Installing routstr models in pi models.json...`);
|
|
|
22367
22424
|
piConfig.providers = {};
|
|
22368
22425
|
}
|
|
22369
22426
|
try {
|
|
22370
|
-
|
|
22427
|
+
mkdirSync4(dirname3(configPath), { recursive: true });
|
|
22371
22428
|
const data = await callDaemon("/models");
|
|
22372
22429
|
const models = data.output?.models || [];
|
|
22373
22430
|
if (models.length === 0) {
|
|
22374
22431
|
console.log("No models found from routstr daemon.");
|
|
22375
22432
|
return;
|
|
22376
22433
|
}
|
|
22434
|
+
const existingModels = new Map((piConfig.providers["routstr"]?.models ?? []).map((m) => [m.id, m]));
|
|
22377
22435
|
const providerModels = models.map((model) => {
|
|
22378
|
-
const
|
|
22436
|
+
const previous = existingModels.get(model.id);
|
|
22437
|
+
const entry = previous ? { ...previous } : { id: model.id };
|
|
22438
|
+
entry.id = model.id;
|
|
22379
22439
|
if (model.context_length !== undefined && model.context_length > 0) {
|
|
22380
22440
|
entry.contextWindow = model.context_length;
|
|
22381
22441
|
}
|
|
22382
22442
|
return entry;
|
|
22383
22443
|
});
|
|
22384
22444
|
piConfig.providers["routstr"] = {
|
|
22445
|
+
...piConfig.providers["routstr"] ?? {},
|
|
22385
22446
|
baseUrl,
|
|
22386
22447
|
api: "openai-completions",
|
|
22387
22448
|
apiKey,
|
|
@@ -22396,7 +22457,7 @@ Installing routstr models in pi models.json...`);
|
|
|
22396
22457
|
|
|
22397
22458
|
// src/integrations/openclaw.ts
|
|
22398
22459
|
init_daemon_client();
|
|
22399
|
-
import { existsSync as
|
|
22460
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
22400
22461
|
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
22401
22462
|
import { dirname as dirname4 } from "path";
|
|
22402
22463
|
var OPENCLAW_PROVIDER_ID = "routstr";
|
|
@@ -22410,7 +22471,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
22410
22471
|
const baseUrl = getDaemonBaseUrl(config);
|
|
22411
22472
|
let openclawConfig = {};
|
|
22412
22473
|
try {
|
|
22413
|
-
if (
|
|
22474
|
+
if (existsSync7(configPath)) {
|
|
22414
22475
|
const content = await readFile4(configPath, "utf-8");
|
|
22415
22476
|
openclawConfig = JSON.parse(content);
|
|
22416
22477
|
}
|
|
@@ -22431,7 +22492,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
22431
22492
|
openclawConfig.agents.defaults = {};
|
|
22432
22493
|
}
|
|
22433
22494
|
try {
|
|
22434
|
-
|
|
22495
|
+
mkdirSync5(dirname4(configPath), { recursive: true });
|
|
22435
22496
|
const data = await callDaemon("/models");
|
|
22436
22497
|
const models = data.output?.models || [];
|
|
22437
22498
|
if (models.length === 0) {
|
|
@@ -22472,7 +22533,7 @@ Installing routstr models in openclaw.json...`);
|
|
|
22472
22533
|
|
|
22473
22534
|
// src/integrations/claudecode.ts
|
|
22474
22535
|
init_daemon_client();
|
|
22475
|
-
import { existsSync as
|
|
22536
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
22476
22537
|
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
22477
22538
|
import { dirname as dirname5 } from "path";
|
|
22478
22539
|
async function installClaudeCodeIntegration(config, apiKey, integrationConfig) {
|
|
@@ -22483,7 +22544,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
22483
22544
|
const baseUrl = getDaemonBaseUrl(config);
|
|
22484
22545
|
let settings = {};
|
|
22485
22546
|
try {
|
|
22486
|
-
if (
|
|
22547
|
+
if (existsSync8(configPath)) {
|
|
22487
22548
|
const content = await readFile5(configPath, "utf-8");
|
|
22488
22549
|
settings = JSON.parse(content);
|
|
22489
22550
|
}
|
|
@@ -22520,7 +22581,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
22520
22581
|
console.error("Failed to fetch models for Claude Code integration:", error);
|
|
22521
22582
|
}
|
|
22522
22583
|
try {
|
|
22523
|
-
|
|
22584
|
+
mkdirSync6(dirname5(configPath), { recursive: true });
|
|
22524
22585
|
await writeFile5(configPath, JSON.stringify(settings, null, 2));
|
|
22525
22586
|
console.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
22526
22587
|
} catch (error) {
|
|
@@ -22529,7 +22590,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
22529
22590
|
}
|
|
22530
22591
|
|
|
22531
22592
|
// src/integrations/hermes.ts
|
|
22532
|
-
import { existsSync as
|
|
22593
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7 } from "fs";
|
|
22533
22594
|
import { readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
|
|
22534
22595
|
import { dirname as dirname6 } from "path";
|
|
22535
22596
|
|
|
@@ -22652,7 +22713,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
22652
22713
|
}
|
|
22653
22714
|
let content = "";
|
|
22654
22715
|
try {
|
|
22655
|
-
if (
|
|
22716
|
+
if (existsSync9(configPath)) {
|
|
22656
22717
|
content = await readFile6(configPath, "utf-8");
|
|
22657
22718
|
}
|
|
22658
22719
|
} catch (error) {
|
|
@@ -22675,7 +22736,7 @@ Installing routstr configuration in ${configPath}...`);
|
|
|
22675
22736
|
console.log(`${configPath} already contains current routstr settings.`);
|
|
22676
22737
|
return;
|
|
22677
22738
|
}
|
|
22678
|
-
|
|
22739
|
+
mkdirSync7(dirname6(configPath), { recursive: true });
|
|
22679
22740
|
await writeFile6(configPath, newContent);
|
|
22680
22741
|
console.log(`Successfully updated ${configPath} with routstr settings.`);
|
|
22681
22742
|
} catch (error) {
|
|
@@ -22912,7 +22973,7 @@ async function addClientAction(options) {
|
|
|
22912
22973
|
// src/cli.ts
|
|
22913
22974
|
init_config();
|
|
22914
22975
|
init_logger();
|
|
22915
|
-
import { chmodSync as
|
|
22976
|
+
import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync5, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
22916
22977
|
import { execSync } from "child_process";
|
|
22917
22978
|
import { dirname as dirname8, join as join9 } from "path";
|
|
22918
22979
|
|
|
@@ -24194,13 +24255,13 @@ k("Cashu_P2BK_v1");
|
|
|
24194
24255
|
// src/daemon/wallet/coco-client.ts
|
|
24195
24256
|
import {
|
|
24196
24257
|
closeSync as closeSync2,
|
|
24197
|
-
existsSync as
|
|
24198
|
-
mkdirSync as
|
|
24258
|
+
existsSync as existsSync10,
|
|
24259
|
+
mkdirSync as mkdirSync8,
|
|
24199
24260
|
openSync as openSync2,
|
|
24200
|
-
readFileSync,
|
|
24201
|
-
renameSync,
|
|
24202
|
-
unlinkSync,
|
|
24203
|
-
writeFileSync
|
|
24261
|
+
readFileSync as readFileSync2,
|
|
24262
|
+
renameSync as renameSync2,
|
|
24263
|
+
unlinkSync as unlinkSync2,
|
|
24264
|
+
writeFileSync as writeFileSync2
|
|
24204
24265
|
} from "fs";
|
|
24205
24266
|
|
|
24206
24267
|
// node_modules/@scure/bip39/index.js
|
|
@@ -24290,7 +24351,7 @@ var SAFE_COCO_LOG_FIELDS = new Set([
|
|
|
24290
24351
|
"rollingBackOperations",
|
|
24291
24352
|
"orphanedReservations"
|
|
24292
24353
|
]);
|
|
24293
|
-
function isZombieProcess(pid, readFile7 = (path) =>
|
|
24354
|
+
function isZombieProcess(pid, readFile7 = (path) => readFileSync2(path, "utf-8")) {
|
|
24294
24355
|
try {
|
|
24295
24356
|
const stat2 = readFile7(`/proc/${pid}/stat`);
|
|
24296
24357
|
const commandEnd = stat2.lastIndexOf(")");
|
|
@@ -24322,7 +24383,7 @@ function hasErrorCode(error, codes) {
|
|
|
24322
24383
|
}
|
|
24323
24384
|
async function assertLegacyCocodNotRunning(options = {}) {
|
|
24324
24385
|
const socketPath = options.socketPath || legacyCocodSocketPath();
|
|
24325
|
-
const pathExists = options.pathExists ||
|
|
24386
|
+
const pathExists = options.pathExists || existsSync10;
|
|
24326
24387
|
if (!pathExists(socketPath))
|
|
24327
24388
|
return;
|
|
24328
24389
|
const fetchImpl = options.fetchImpl || fetch;
|
|
@@ -24345,8 +24406,8 @@ async function assertLegacyCocodNotRunning(options = {}) {
|
|
|
24345
24406
|
async function stopLegacyCocod(options = {}) {
|
|
24346
24407
|
const socketPath = options.socketPath || legacyCocodSocketPath();
|
|
24347
24408
|
const pidFilePath = options.pidFilePath || legacyCocodPidPath();
|
|
24348
|
-
const pathExists = options.pathExists ||
|
|
24349
|
-
const readFile7 = options.readFile || ((path) =>
|
|
24409
|
+
const pathExists = options.pathExists || existsSync10;
|
|
24410
|
+
const readFile7 = options.readFile || ((path) => readFileSync2(path, "utf-8"));
|
|
24350
24411
|
const isProcessRunning2 = options.isProcessRunning || defaultIsProcessRunning;
|
|
24351
24412
|
const fetchImpl = options.fetchImpl || fetch;
|
|
24352
24413
|
const killProcess = options.killProcess || ((pid2, signal) => process.kill(pid2, signal));
|
|
@@ -24409,10 +24470,10 @@ function claimPidFile(options) {
|
|
|
24409
24470
|
const pid = options.pid ?? process.pid;
|
|
24410
24471
|
const label = options.label || "wallet process lock";
|
|
24411
24472
|
const openExclusive = options.openExclusive || ((path) => openSync2(path, "wx", 384));
|
|
24412
|
-
const writePid = options.writePid || ((fd2, ownerPid) =>
|
|
24473
|
+
const writePid = options.writePid || ((fd2, ownerPid) => writeFileSync2(fd2, String(ownerPid)));
|
|
24413
24474
|
const closeFile = options.closeFile || closeSync2;
|
|
24414
|
-
const readFile7 = options.readFile || ((path) =>
|
|
24415
|
-
const removeFile = options.removeFile ||
|
|
24475
|
+
const readFile7 = options.readFile || ((path) => readFileSync2(path, "utf-8"));
|
|
24476
|
+
const removeFile = options.removeFile || unlinkSync2;
|
|
24416
24477
|
const isProcessRunning2 = options.isProcessRunning || defaultIsProcessRunning;
|
|
24417
24478
|
let fd;
|
|
24418
24479
|
try {
|
|
@@ -24469,11 +24530,11 @@ function claimPidFile(options) {
|
|
|
24469
24530
|
|
|
24470
24531
|
// src/daemon/wallet/migration.ts
|
|
24471
24532
|
import {
|
|
24472
|
-
chmodSync,
|
|
24533
|
+
chmodSync as chmodSync2,
|
|
24473
24534
|
copyFileSync,
|
|
24474
|
-
existsSync as
|
|
24475
|
-
mkdirSync as
|
|
24476
|
-
renameSync as
|
|
24535
|
+
existsSync as existsSync13,
|
|
24536
|
+
mkdirSync as mkdirSync9,
|
|
24537
|
+
renameSync as renameSync3,
|
|
24477
24538
|
rmSync,
|
|
24478
24539
|
statSync as statSync3
|
|
24479
24540
|
} from "fs";
|
|
@@ -24482,16 +24543,16 @@ import { basename, dirname as dirname7, join as join8 } from "path";
|
|
|
24482
24543
|
|
|
24483
24544
|
// src/daemon/wallet/diagnostics.ts
|
|
24484
24545
|
import { createHash } from "crypto";
|
|
24485
|
-
import { existsSync as
|
|
24546
|
+
import { existsSync as existsSync12, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
24486
24547
|
import { Database } from "bun:sqlite";
|
|
24487
24548
|
import { join as join7 } from "path";
|
|
24488
24549
|
|
|
24489
24550
|
// src/daemon/wallet/wallet-state.ts
|
|
24490
|
-
import { existsSync as
|
|
24551
|
+
import { existsSync as existsSync11, readFileSync as readFileSync3, statSync } from "fs";
|
|
24491
24552
|
import { join as join6 } from "path";
|
|
24492
24553
|
function walletState(configPath, dbPath) {
|
|
24493
|
-
const hasConfig =
|
|
24494
|
-
const hasDb =
|
|
24554
|
+
const hasConfig = existsSync11(configPath);
|
|
24555
|
+
const hasDb = existsSync11(dbPath);
|
|
24495
24556
|
if (hasConfig)
|
|
24496
24557
|
return "initialized";
|
|
24497
24558
|
if (hasDb)
|
|
@@ -24499,11 +24560,11 @@ function walletState(configPath, dbPath) {
|
|
|
24499
24560
|
return "absent";
|
|
24500
24561
|
}
|
|
24501
24562
|
function filesEqual(left, right) {
|
|
24502
|
-
if (!
|
|
24563
|
+
if (!existsSync11(left) || !existsSync11(right))
|
|
24503
24564
|
return false;
|
|
24504
24565
|
if (statSync(left).size !== statSync(right).size)
|
|
24505
24566
|
return false;
|
|
24506
|
-
return
|
|
24567
|
+
return readFileSync3(left).equals(readFileSync3(right));
|
|
24507
24568
|
}
|
|
24508
24569
|
function classifyWalletMigration(targetDir, sourceDir) {
|
|
24509
24570
|
const targetConfig = join6(targetDir, "config.json");
|
|
@@ -24516,14 +24577,14 @@ function classifyWalletMigration(targetDir, sourceDir) {
|
|
|
24516
24577
|
const sourceState = walletState(sourceConfig, sourceDb);
|
|
24517
24578
|
if (targetState === "initialized" && sourceState === "initialized") {
|
|
24518
24579
|
const configsMatch = filesEqual(targetConfig, sourceConfig);
|
|
24519
|
-
const databasesMatch = !
|
|
24580
|
+
const databasesMatch = !existsSync11(targetDb) && !existsSync11(sourceDb) ? true : filesEqual(targetDb, sourceDb);
|
|
24520
24581
|
if (configsMatch && databasesMatch)
|
|
24521
24582
|
return { kind: "already-current" };
|
|
24522
24583
|
return { kind: "conflict" };
|
|
24523
24584
|
}
|
|
24524
24585
|
if (targetState === "initialized")
|
|
24525
24586
|
return { kind: "already-current" };
|
|
24526
|
-
if (!
|
|
24587
|
+
if (!existsSync11(sourceDb) && (existsSync11(sourceWal) || existsSync11(sourceShm))) {
|
|
24527
24588
|
return { kind: "orphaned-sidecars" };
|
|
24528
24589
|
}
|
|
24529
24590
|
if (targetState === "database-only" || sourceState === "database-only") {
|
|
@@ -24598,11 +24659,11 @@ function summarizeDbReadonly(dbPath) {
|
|
|
24598
24659
|
function summarizeConfig(configPath) {
|
|
24599
24660
|
const result = { exists: false, path: configPath };
|
|
24600
24661
|
try {
|
|
24601
|
-
if (!
|
|
24662
|
+
if (!existsSync12(configPath))
|
|
24602
24663
|
return result;
|
|
24603
24664
|
result.exists = true;
|
|
24604
24665
|
result.mtimeMs = statSync2(configPath).mtimeMs;
|
|
24605
|
-
const parsed = JSON.parse(
|
|
24666
|
+
const parsed = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
24606
24667
|
result.encrypted = parsed.encrypted === true;
|
|
24607
24668
|
if (typeof parsed.createdAt === "string")
|
|
24608
24669
|
result.createdAt = parsed.createdAt;
|
|
@@ -24624,7 +24685,7 @@ function summarizeConfig(configPath) {
|
|
|
24624
24685
|
function summarizeDb(dbPath) {
|
|
24625
24686
|
const result = { exists: false, path: dbPath };
|
|
24626
24687
|
try {
|
|
24627
|
-
if (!
|
|
24688
|
+
if (!existsSync12(dbPath))
|
|
24628
24689
|
return result;
|
|
24629
24690
|
result.exists = true;
|
|
24630
24691
|
const stats = statSync2(dbPath);
|
|
@@ -24933,21 +24994,21 @@ async function migrateLegacyWallet(options = {}) {
|
|
|
24933
24994
|
const stagingDir = join8(dirname7(targetDir), `.${targetDir.split(/[\\/]/).at(-1) || "wallet"}.staging-${process.pid}-${Date.now()}`);
|
|
24934
24995
|
try {
|
|
24935
24996
|
await options.assertLegacyStopped?.();
|
|
24936
|
-
|
|
24937
|
-
|
|
24997
|
+
mkdirSync9(dirname7(targetDir), { recursive: true, mode: 448 });
|
|
24998
|
+
mkdirSync9(stagingDir, { mode: 448 });
|
|
24938
24999
|
const stagedConfig = join8(stagingDir, "config.json");
|
|
24939
25000
|
const stagedDb = join8(stagingDir, "coco.db");
|
|
24940
25001
|
copyFileSync(sourceConfig, stagedConfig);
|
|
24941
|
-
|
|
24942
|
-
const hasSourceDb =
|
|
25002
|
+
chmodSync2(stagedConfig, 384);
|
|
25003
|
+
const hasSourceDb = existsSync13(sourceDb);
|
|
24943
25004
|
if (hasSourceDb) {
|
|
24944
25005
|
snapshotDatabase(sourceDb, stagedDb);
|
|
24945
|
-
|
|
25006
|
+
chmodSync2(stagedDb, 384);
|
|
24946
25007
|
}
|
|
24947
25008
|
if (statSync3(stagedConfig).size !== statSync3(sourceConfig).size) {
|
|
24948
25009
|
throw new Error("Cannot migrate wallet: staged config failed size validation.");
|
|
24949
25010
|
}
|
|
24950
|
-
|
|
25011
|
+
renameSync3(stagingDir, targetDir);
|
|
24951
25012
|
} catch (error) {
|
|
24952
25013
|
rmSync(stagingDir, { recursive: true, force: true });
|
|
24953
25014
|
throw error;
|
|
@@ -24955,13 +25016,13 @@ async function migrateLegacyWallet(options = {}) {
|
|
|
24955
25016
|
releaseLegacyLock?.();
|
|
24956
25017
|
}
|
|
24957
25018
|
const cleanupWarnings = [];
|
|
24958
|
-
const sourceFiles = [sourceConfig, sourceDb, sourceWal, sourceShm].filter(
|
|
25019
|
+
const sourceFiles = [sourceConfig, sourceDb, sourceWal, sourceShm].filter(existsSync13);
|
|
24959
25020
|
if (sourceFiles.length > 0) {
|
|
24960
25021
|
const archiveDir = join8(sourceDir, `wallet-migrated-${Date.now()}`);
|
|
24961
25022
|
try {
|
|
24962
|
-
|
|
25023
|
+
mkdirSync9(archiveDir, { mode: 448 });
|
|
24963
25024
|
for (const path of sourceFiles)
|
|
24964
|
-
|
|
25025
|
+
renameSync3(path, join8(archiveDir, basename(path)));
|
|
24965
25026
|
} catch (error) {
|
|
24966
25027
|
const message = error instanceof Error ? error.message : String(error);
|
|
24967
25028
|
cleanupWarnings.push(`Could not archive legacy wallet files: ${message}`);
|
|
@@ -27095,7 +27156,7 @@ zoo`.split(`
|
|
|
27095
27156
|
// package.json
|
|
27096
27157
|
var package_default = {
|
|
27097
27158
|
name: "routstrd",
|
|
27098
|
-
version: "0.4.
|
|
27159
|
+
version: "0.4.2",
|
|
27099
27160
|
module: "src/index.ts",
|
|
27100
27161
|
type: "module",
|
|
27101
27162
|
private: false,
|
|
@@ -27121,7 +27182,7 @@ var package_default = {
|
|
|
27121
27182
|
"@cashu/cashu-ts": "^4.3.0",
|
|
27122
27183
|
"@cashu/coco-core": "^1.0.1",
|
|
27123
27184
|
"@cashu/coco-sqlite-bun": "^1.0.1",
|
|
27124
|
-
"@routstr/sdk": "^0.4.
|
|
27185
|
+
"@routstr/sdk": "^0.4.1",
|
|
27125
27186
|
"@scure/bip39": "^2.2.0",
|
|
27126
27187
|
"applesauce-core": "^5.1.0",
|
|
27127
27188
|
"applesauce-relay": "^5.1.0",
|
|
@@ -27162,10 +27223,10 @@ ${invoice}`);
|
|
|
27162
27223
|
}
|
|
27163
27224
|
function initializeWallet(walletDir2 = walletDir()) {
|
|
27164
27225
|
const walletConfig = join9(walletDir2, "config.json");
|
|
27165
|
-
|
|
27166
|
-
|
|
27167
|
-
if (
|
|
27168
|
-
|
|
27226
|
+
mkdirSync10(walletDir2, { recursive: true, mode: 448 });
|
|
27227
|
+
chmodSync3(walletDir2, 448);
|
|
27228
|
+
if (existsSync14(walletConfig)) {
|
|
27229
|
+
chmodSync3(walletConfig, 384);
|
|
27169
27230
|
console.log("Wallet already initialized.");
|
|
27170
27231
|
return;
|
|
27171
27232
|
}
|
|
@@ -27176,7 +27237,7 @@ function initializeWallet(walletDir2 = walletDir()) {
|
|
|
27176
27237
|
encrypted: false,
|
|
27177
27238
|
createdAt: new Date().toISOString()
|
|
27178
27239
|
};
|
|
27179
|
-
|
|
27240
|
+
writeFileSync3(walletConfig, JSON.stringify(config, null, 2), {
|
|
27180
27241
|
mode: 384,
|
|
27181
27242
|
flag: "wx"
|
|
27182
27243
|
});
|
|
@@ -27204,7 +27265,7 @@ Restarting routstrd daemon...`);
|
|
|
27204
27265
|
for (let i2 = 0;i2 < 100; i2++) {
|
|
27205
27266
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
27206
27267
|
const healthDown = !await isDaemonRunning();
|
|
27207
|
-
const pidFileReleased = !
|
|
27268
|
+
const pidFileReleased = !existsSync14(pidFilePath);
|
|
27208
27269
|
if (healthDown && pidFileReleased)
|
|
27209
27270
|
break;
|
|
27210
27271
|
}
|
|
@@ -27247,16 +27308,16 @@ async function requireLocalDaemon() {
|
|
|
27247
27308
|
}
|
|
27248
27309
|
async function initDaemon() {
|
|
27249
27310
|
console.log("Initializing routstrd...");
|
|
27250
|
-
if (!
|
|
27251
|
-
mkdirSync9(CONFIG_DIR, { recursive: true });
|
|
27311
|
+
if (!existsSync14(CONFIG_DIR)) {
|
|
27252
27312
|
console.log(`Created config directory: ${CONFIG_DIR}`);
|
|
27253
27313
|
}
|
|
27254
|
-
|
|
27314
|
+
ensureDirsSync();
|
|
27315
|
+
if (!existsSync14(CONFIG_FILE)) {
|
|
27255
27316
|
const config2 = {
|
|
27256
27317
|
...DEFAULT_CONFIG,
|
|
27257
27318
|
cocodPath: null
|
|
27258
27319
|
};
|
|
27259
|
-
|
|
27320
|
+
saveDaemonConfig(config2);
|
|
27260
27321
|
console.log(`Created config file: ${CONFIG_FILE}`);
|
|
27261
27322
|
}
|
|
27262
27323
|
const config = await loadConfig();
|
|
@@ -27265,7 +27326,7 @@ async function initDaemon() {
|
|
|
27265
27326
|
const nsec = nip19_exports.nsecEncode(secretKey);
|
|
27266
27327
|
const npub = npubFromSecretKey(secretKey);
|
|
27267
27328
|
config.nsec = nsec;
|
|
27268
|
-
|
|
27329
|
+
saveDaemonConfig(config);
|
|
27269
27330
|
console.log(`
|
|
27270
27331
|
A new Nostr identity has been generated for authentication.`);
|
|
27271
27332
|
console.log(`Your npub: ${npub}`);
|
|
@@ -27279,7 +27340,7 @@ A new Nostr identity has been generated for authentication.`);
|
|
|
27279
27340
|
socketPath: legacyCocodSocketPath()
|
|
27280
27341
|
}),
|
|
27281
27342
|
acquireLegacyLock: () => {
|
|
27282
|
-
|
|
27343
|
+
mkdirSync10(dirname8(legacyCocodPidPath()), {
|
|
27283
27344
|
recursive: true,
|
|
27284
27345
|
mode: 448
|
|
27285
27346
|
});
|
|
@@ -27307,10 +27368,7 @@ To ensure routstrd persists across system restarts, run: 'routstrd service insta
|
|
|
27307
27368
|
}
|
|
27308
27369
|
program.name("routstrd").description("Routstr daemon - Manage routstr processes").version(package_default.version, "--version", "output the version number");
|
|
27309
27370
|
program.command("update").description("Update routstrd to the latest version").action(async () => {
|
|
27310
|
-
const packages = [
|
|
27311
|
-
{ name: "routstrd", label: "routstrd" },
|
|
27312
|
-
{ name: "@routstr/cocod", label: "cocod" }
|
|
27313
|
-
];
|
|
27371
|
+
const packages = [{ name: "routstrd", label: "routstrd" }];
|
|
27314
27372
|
let updatedAny = false;
|
|
27315
27373
|
for (const { name, label } of packages) {
|
|
27316
27374
|
const installed = await getGlobalPackageVersion(name);
|
|
@@ -27444,8 +27502,8 @@ program.command("remote [url]").description("Show the configured remote daemon,
|
|
|
27444
27502
|
process.exit(1);
|
|
27445
27503
|
}
|
|
27446
27504
|
}
|
|
27447
|
-
if (!
|
|
27448
|
-
|
|
27505
|
+
if (!existsSync14(CONFIG_DIR)) {
|
|
27506
|
+
mkdirSync10(CONFIG_DIR, { recursive: true });
|
|
27449
27507
|
}
|
|
27450
27508
|
const updates = { daemonUrl: url };
|
|
27451
27509
|
if (options.authUrl) {
|
|
@@ -27463,7 +27521,7 @@ program.command("remote [url]").description("Show the configured remote daemon,
|
|
|
27463
27521
|
...config,
|
|
27464
27522
|
...updates
|
|
27465
27523
|
};
|
|
27466
|
-
|
|
27524
|
+
saveDaemonConfig(updatedConfig);
|
|
27467
27525
|
console.log(`Remote daemon URL set to: ${url}`);
|
|
27468
27526
|
if (options.authUrl) {
|
|
27469
27527
|
console.log(`Auth proxy URL set to: ${options.authUrl}`);
|
|
@@ -27485,7 +27543,7 @@ program.command("local").description("Switch back to local daemon mode (removes
|
|
|
27485
27543
|
const previousAuthUrl = config.authUrl;
|
|
27486
27544
|
const { daemonUrl: _daemonUrl, authUrl: _authUrl, ...rest } = config;
|
|
27487
27545
|
const updatedConfig = rest;
|
|
27488
|
-
|
|
27546
|
+
saveDaemonConfig(updatedConfig);
|
|
27489
27547
|
console.log("Switched back to local daemon mode.");
|
|
27490
27548
|
if (previousDaemonUrl) {
|
|
27491
27549
|
console.log(` Removed remote daemon URL: ${previousDaemonUrl}`);
|
|
@@ -27816,6 +27874,46 @@ providersCmd.command("enable <indices...>").description("Enable providers by the
|
|
|
27816
27874
|
}
|
|
27817
27875
|
}
|
|
27818
27876
|
});
|
|
27877
|
+
providersCmd.command("reviews").description("Show all known providers with their stored review events and event IDs").action(async () => {
|
|
27878
|
+
await ensureDaemonRunning();
|
|
27879
|
+
const result = await callDaemon("/providers/reviews");
|
|
27880
|
+
if (result.error) {
|
|
27881
|
+
console.log(result.error);
|
|
27882
|
+
process.exit(1);
|
|
27883
|
+
}
|
|
27884
|
+
const output = result.output;
|
|
27885
|
+
if (!output) {
|
|
27886
|
+
console.log("No provider review data available.");
|
|
27887
|
+
return;
|
|
27888
|
+
}
|
|
27889
|
+
console.log(`Providers (${output.totalCount} total, ${output.reviewEventCount} stored review events):
|
|
27890
|
+
`);
|
|
27891
|
+
for (const provider of output.providers) {
|
|
27892
|
+
const status = provider.disabled ? "DISABLED" : "enabled ";
|
|
27893
|
+
console.log(` [${provider.index}] ${status} ${provider.baseUrl} (${provider.reviewCount} review${provider.reviewCount === 1 ? "" : "s"})`);
|
|
27894
|
+
if (provider.nodePubkeys.length > 0) {
|
|
27895
|
+
console.log(` node pubkeys: ${provider.nodePubkeys.join(", ")}`);
|
|
27896
|
+
}
|
|
27897
|
+
for (const review of provider.reviews) {
|
|
27898
|
+
const label = review.label || "review";
|
|
27899
|
+
const created = new Date(review.createdAt * 1000).toISOString();
|
|
27900
|
+
console.log(` - ${label} eventId: ${review.eventId}`);
|
|
27901
|
+
console.log(` node: ${review.nodePubkey ?? "(none)"}`);
|
|
27902
|
+
console.log(` author: ${review.authorPubkey ?? "(none)"}`);
|
|
27903
|
+
console.log(` at: ${created}`);
|
|
27904
|
+
}
|
|
27905
|
+
}
|
|
27906
|
+
if (output.unmatchedReviews.length > 0) {
|
|
27907
|
+
console.log(`
|
|
27908
|
+
Unmatched review events (${output.unmatchedReviews.length} \u2014 node pubkey not in any known provider):`);
|
|
27909
|
+
for (const review of output.unmatchedReviews) {
|
|
27910
|
+
const created = new Date(review.createdAt * 1000).toISOString();
|
|
27911
|
+
console.log(` - eventId: ${review.eventId}`);
|
|
27912
|
+
console.log(` node: ${review.nodePubkey ?? "(none)"}`);
|
|
27913
|
+
console.log(` at: ${created}`);
|
|
27914
|
+
}
|
|
27915
|
+
}
|
|
27916
|
+
});
|
|
27819
27917
|
var clientsCmd = program.command("clients").description("List and manage clients");
|
|
27820
27918
|
clientsCmd.command("list").description("List all clients").action(async () => {
|
|
27821
27919
|
await listClientsAction();
|
|
@@ -28338,7 +28436,7 @@ serviceCmd.command("install").description("Install and start routstrd using PM2
|
|
|
28338
28436
|
const path = __require("path");
|
|
28339
28437
|
daemonPath = path.join(path.dirname(import.meta.url).replace("file://", ""), "daemon", "index.js");
|
|
28340
28438
|
}
|
|
28341
|
-
if (!
|
|
28439
|
+
if (!existsSync14(daemonPath)) {
|
|
28342
28440
|
console.error(`Could not find daemon at ${daemonPath}. Did you run 'bun run build'?`);
|
|
28343
28441
|
process.exit(1);
|
|
28344
28442
|
}
|
|
@@ -28386,7 +28484,7 @@ program.command("restart").description("Restart the background daemon").option("
|
|
|
28386
28484
|
for (let i2 = 0;i2 < 100; i2++) {
|
|
28387
28485
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
28388
28486
|
const healthDown = !await isDaemonRunning();
|
|
28389
|
-
const pidFileReleased = !
|
|
28487
|
+
const pidFileReleased = !existsSync14(pidFilePath);
|
|
28390
28488
|
if (healthDown && pidFileReleased) {
|
|
28391
28489
|
break;
|
|
28392
28490
|
}
|
|
@@ -28444,7 +28542,7 @@ xcashu mode is coming soon! Only lazyrefund/apikeys is available at this time.`)
|
|
|
28444
28542
|
...config,
|
|
28445
28543
|
mode: selectedMode
|
|
28446
28544
|
};
|
|
28447
|
-
|
|
28545
|
+
saveDaemonConfig(updatedConfig);
|
|
28448
28546
|
console.log(`Mode set to '${selectedMode}'. Restarting daemon...`);
|
|
28449
28547
|
const wasRunning = await isDaemonRunning();
|
|
28450
28548
|
if (wasRunning) {
|
|
@@ -28477,7 +28575,7 @@ function getLogFileForDate2(logsDir, date = new Date) {
|
|
|
28477
28575
|
return `${logsDir}/${year}-${month}-${day}.log`;
|
|
28478
28576
|
}
|
|
28479
28577
|
function readLastLines(file, lines) {
|
|
28480
|
-
const content =
|
|
28578
|
+
const content = readFileSync5(file, "utf8");
|
|
28481
28579
|
const allLines = content.replace(/\r\n/g, `
|
|
28482
28580
|
`).split(`
|
|
28483
28581
|
`);
|
|
@@ -28494,7 +28592,7 @@ async function followLogFile(file, lines) {
|
|
|
28494
28592
|
let position = statSync4(file).size;
|
|
28495
28593
|
while (true) {
|
|
28496
28594
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
28497
|
-
if (!
|
|
28595
|
+
if (!existsSync14(file)) {
|
|
28498
28596
|
continue;
|
|
28499
28597
|
}
|
|
28500
28598
|
const size = statSync4(file).size;
|
|
@@ -28516,17 +28614,17 @@ program.command("logs").description("View daemon logs").option("-f, --follow", "
|
|
|
28516
28614
|
const yesterday = new Date;
|
|
28517
28615
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
28518
28616
|
const yesterdayFile = getLogFileForDate2(logsDir, yesterday);
|
|
28519
|
-
if (!
|
|
28617
|
+
if (!existsSync14(todayFile) && !existsSync14(yesterdayFile)) {
|
|
28520
28618
|
console.log("No log files found. Daemon may not have started yet.");
|
|
28521
28619
|
console.log(`Logs directory: ${logsDir}`);
|
|
28522
28620
|
process.exit(1);
|
|
28523
28621
|
}
|
|
28524
28622
|
const lines = parseInt(options.lines, 10);
|
|
28525
28623
|
const logFiles = [yesterdayFile, todayFile].filter((file, index, files) => {
|
|
28526
|
-
return
|
|
28624
|
+
return existsSync14(file) && files.indexOf(file) === index;
|
|
28527
28625
|
});
|
|
28528
28626
|
if (options.follow) {
|
|
28529
|
-
if (
|
|
28627
|
+
if (existsSync14(todayFile)) {
|
|
28530
28628
|
await followLogFile(todayFile, lines);
|
|
28531
28629
|
} else {
|
|
28532
28630
|
console.log("No log file for today to follow.");
|