routstrd 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 callDaemon("/status");
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 existsSync4, mkdirSync as mkdirSync2 } from "fs";
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 (existsSync4(configPath)) {
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
- mkdirSync2(dirname2(configPath), { recursive: true });
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 existsSync5, mkdirSync as mkdirSync3 } from "fs";
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 (existsSync5(configPath)) {
22415
+ if (existsSync6(configPath)) {
22359
22416
  const content = await readFile3(configPath, "utf-8");
22360
22417
  piConfig = JSON.parse(content);
22361
22418
  }
@@ -22367,21 +22424,39 @@ Installing routstr models in pi models.json...`);
22367
22424
  piConfig.providers = {};
22368
22425
  }
22369
22426
  try {
22370
- mkdirSync3(dirname3(configPath), { recursive: true });
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 entry = { id: model.id };
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;
22441
+ } else {
22442
+ delete entry.contextWindow;
22381
22443
  }
22444
+ if (model.name) {
22445
+ entry.name = model.name;
22446
+ } else {
22447
+ delete entry.name;
22448
+ }
22449
+ const mods = model.architecture?.input_modalities ?? [];
22450
+ const input = [];
22451
+ if (mods.includes("text"))
22452
+ input.push("text");
22453
+ if (mods.includes("image"))
22454
+ input.push("image");
22455
+ entry.input = input;
22382
22456
  return entry;
22383
22457
  });
22384
22458
  piConfig.providers["routstr"] = {
22459
+ ...piConfig.providers["routstr"] ?? {},
22385
22460
  baseUrl,
22386
22461
  api: "openai-completions",
22387
22462
  apiKey,
@@ -22396,7 +22471,7 @@ Installing routstr models in pi models.json...`);
22396
22471
 
22397
22472
  // src/integrations/openclaw.ts
22398
22473
  init_daemon_client();
22399
- import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
22474
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
22400
22475
  import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
22401
22476
  import { dirname as dirname4 } from "path";
22402
22477
  var OPENCLAW_PROVIDER_ID = "routstr";
@@ -22410,7 +22485,7 @@ Installing routstr models in openclaw.json...`);
22410
22485
  const baseUrl = getDaemonBaseUrl(config);
22411
22486
  let openclawConfig = {};
22412
22487
  try {
22413
- if (existsSync6(configPath)) {
22488
+ if (existsSync7(configPath)) {
22414
22489
  const content = await readFile4(configPath, "utf-8");
22415
22490
  openclawConfig = JSON.parse(content);
22416
22491
  }
@@ -22431,7 +22506,7 @@ Installing routstr models in openclaw.json...`);
22431
22506
  openclawConfig.agents.defaults = {};
22432
22507
  }
22433
22508
  try {
22434
- mkdirSync4(dirname4(configPath), { recursive: true });
22509
+ mkdirSync5(dirname4(configPath), { recursive: true });
22435
22510
  const data = await callDaemon("/models");
22436
22511
  const models = data.output?.models || [];
22437
22512
  if (models.length === 0) {
@@ -22472,7 +22547,7 @@ Installing routstr models in openclaw.json...`);
22472
22547
 
22473
22548
  // src/integrations/claudecode.ts
22474
22549
  init_daemon_client();
22475
- import { existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
22550
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
22476
22551
  import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
22477
22552
  import { dirname as dirname5 } from "path";
22478
22553
  async function installClaudeCodeIntegration(config, apiKey, integrationConfig) {
@@ -22483,7 +22558,7 @@ Installing routstr configuration in ${configPath}...`);
22483
22558
  const baseUrl = getDaemonBaseUrl(config);
22484
22559
  let settings = {};
22485
22560
  try {
22486
- if (existsSync7(configPath)) {
22561
+ if (existsSync8(configPath)) {
22487
22562
  const content = await readFile5(configPath, "utf-8");
22488
22563
  settings = JSON.parse(content);
22489
22564
  }
@@ -22520,7 +22595,7 @@ Installing routstr configuration in ${configPath}...`);
22520
22595
  console.error("Failed to fetch models for Claude Code integration:", error);
22521
22596
  }
22522
22597
  try {
22523
- mkdirSync5(dirname5(configPath), { recursive: true });
22598
+ mkdirSync6(dirname5(configPath), { recursive: true });
22524
22599
  await writeFile5(configPath, JSON.stringify(settings, null, 2));
22525
22600
  console.log(`Successfully updated ${configPath} with routstr settings.`);
22526
22601
  } catch (error) {
@@ -22529,7 +22604,7 @@ Installing routstr configuration in ${configPath}...`);
22529
22604
  }
22530
22605
 
22531
22606
  // src/integrations/hermes.ts
22532
- import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
22607
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7 } from "fs";
22533
22608
  import { readFile as readFile6, writeFile as writeFile6 } from "fs/promises";
22534
22609
  import { dirname as dirname6 } from "path";
22535
22610
 
@@ -22652,7 +22727,7 @@ Installing routstr configuration in ${configPath}...`);
22652
22727
  }
22653
22728
  let content = "";
22654
22729
  try {
22655
- if (existsSync8(configPath)) {
22730
+ if (existsSync9(configPath)) {
22656
22731
  content = await readFile6(configPath, "utf-8");
22657
22732
  }
22658
22733
  } catch (error) {
@@ -22675,7 +22750,7 @@ Installing routstr configuration in ${configPath}...`);
22675
22750
  console.log(`${configPath} already contains current routstr settings.`);
22676
22751
  return;
22677
22752
  }
22678
- mkdirSync6(dirname6(configPath), { recursive: true });
22753
+ mkdirSync7(dirname6(configPath), { recursive: true });
22679
22754
  await writeFile6(configPath, newContent);
22680
22755
  console.log(`Successfully updated ${configPath} with routstr settings.`);
22681
22756
  } catch (error) {
@@ -22912,7 +22987,7 @@ async function addClientAction(options) {
22912
22987
  // src/cli.ts
22913
22988
  init_config();
22914
22989
  init_logger();
22915
- import { chmodSync as chmodSync2, existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
22990
+ import { chmodSync as chmodSync3, existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync5, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
22916
22991
  import { execSync } from "child_process";
22917
22992
  import { dirname as dirname8, join as join9 } from "path";
22918
22993
 
@@ -24194,13 +24269,13 @@ k("Cashu_P2BK_v1");
24194
24269
  // src/daemon/wallet/coco-client.ts
24195
24270
  import {
24196
24271
  closeSync as closeSync2,
24197
- existsSync as existsSync9,
24198
- mkdirSync as mkdirSync7,
24272
+ existsSync as existsSync10,
24273
+ mkdirSync as mkdirSync8,
24199
24274
  openSync as openSync2,
24200
- readFileSync,
24201
- renameSync,
24202
- unlinkSync,
24203
- writeFileSync
24275
+ readFileSync as readFileSync2,
24276
+ renameSync as renameSync2,
24277
+ unlinkSync as unlinkSync2,
24278
+ writeFileSync as writeFileSync2
24204
24279
  } from "fs";
24205
24280
 
24206
24281
  // node_modules/@scure/bip39/index.js
@@ -24290,7 +24365,7 @@ var SAFE_COCO_LOG_FIELDS = new Set([
24290
24365
  "rollingBackOperations",
24291
24366
  "orphanedReservations"
24292
24367
  ]);
24293
- function isZombieProcess(pid, readFile7 = (path) => readFileSync(path, "utf-8")) {
24368
+ function isZombieProcess(pid, readFile7 = (path) => readFileSync2(path, "utf-8")) {
24294
24369
  try {
24295
24370
  const stat2 = readFile7(`/proc/${pid}/stat`);
24296
24371
  const commandEnd = stat2.lastIndexOf(")");
@@ -24322,7 +24397,7 @@ function hasErrorCode(error, codes) {
24322
24397
  }
24323
24398
  async function assertLegacyCocodNotRunning(options = {}) {
24324
24399
  const socketPath = options.socketPath || legacyCocodSocketPath();
24325
- const pathExists = options.pathExists || existsSync9;
24400
+ const pathExists = options.pathExists || existsSync10;
24326
24401
  if (!pathExists(socketPath))
24327
24402
  return;
24328
24403
  const fetchImpl = options.fetchImpl || fetch;
@@ -24345,8 +24420,8 @@ async function assertLegacyCocodNotRunning(options = {}) {
24345
24420
  async function stopLegacyCocod(options = {}) {
24346
24421
  const socketPath = options.socketPath || legacyCocodSocketPath();
24347
24422
  const pidFilePath = options.pidFilePath || legacyCocodPidPath();
24348
- const pathExists = options.pathExists || existsSync9;
24349
- const readFile7 = options.readFile || ((path) => readFileSync(path, "utf-8"));
24423
+ const pathExists = options.pathExists || existsSync10;
24424
+ const readFile7 = options.readFile || ((path) => readFileSync2(path, "utf-8"));
24350
24425
  const isProcessRunning2 = options.isProcessRunning || defaultIsProcessRunning;
24351
24426
  const fetchImpl = options.fetchImpl || fetch;
24352
24427
  const killProcess = options.killProcess || ((pid2, signal) => process.kill(pid2, signal));
@@ -24409,10 +24484,10 @@ function claimPidFile(options) {
24409
24484
  const pid = options.pid ?? process.pid;
24410
24485
  const label = options.label || "wallet process lock";
24411
24486
  const openExclusive = options.openExclusive || ((path) => openSync2(path, "wx", 384));
24412
- const writePid = options.writePid || ((fd2, ownerPid) => writeFileSync(fd2, String(ownerPid)));
24487
+ const writePid = options.writePid || ((fd2, ownerPid) => writeFileSync2(fd2, String(ownerPid)));
24413
24488
  const closeFile = options.closeFile || closeSync2;
24414
- const readFile7 = options.readFile || ((path) => readFileSync(path, "utf-8"));
24415
- const removeFile = options.removeFile || unlinkSync;
24489
+ const readFile7 = options.readFile || ((path) => readFileSync2(path, "utf-8"));
24490
+ const removeFile = options.removeFile || unlinkSync2;
24416
24491
  const isProcessRunning2 = options.isProcessRunning || defaultIsProcessRunning;
24417
24492
  let fd;
24418
24493
  try {
@@ -24469,11 +24544,11 @@ function claimPidFile(options) {
24469
24544
 
24470
24545
  // src/daemon/wallet/migration.ts
24471
24546
  import {
24472
- chmodSync,
24547
+ chmodSync as chmodSync2,
24473
24548
  copyFileSync,
24474
- existsSync as existsSync12,
24475
- mkdirSync as mkdirSync8,
24476
- renameSync as renameSync2,
24549
+ existsSync as existsSync13,
24550
+ mkdirSync as mkdirSync9,
24551
+ renameSync as renameSync3,
24477
24552
  rmSync,
24478
24553
  statSync as statSync3
24479
24554
  } from "fs";
@@ -24482,16 +24557,16 @@ import { basename, dirname as dirname7, join as join8 } from "path";
24482
24557
 
24483
24558
  // src/daemon/wallet/diagnostics.ts
24484
24559
  import { createHash } from "crypto";
24485
- import { existsSync as existsSync11, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
24560
+ import { existsSync as existsSync12, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
24486
24561
  import { Database } from "bun:sqlite";
24487
24562
  import { join as join7 } from "path";
24488
24563
 
24489
24564
  // src/daemon/wallet/wallet-state.ts
24490
- import { existsSync as existsSync10, readFileSync as readFileSync2, statSync } from "fs";
24565
+ import { existsSync as existsSync11, readFileSync as readFileSync3, statSync } from "fs";
24491
24566
  import { join as join6 } from "path";
24492
24567
  function walletState(configPath, dbPath) {
24493
- const hasConfig = existsSync10(configPath);
24494
- const hasDb = existsSync10(dbPath);
24568
+ const hasConfig = existsSync11(configPath);
24569
+ const hasDb = existsSync11(dbPath);
24495
24570
  if (hasConfig)
24496
24571
  return "initialized";
24497
24572
  if (hasDb)
@@ -24499,11 +24574,11 @@ function walletState(configPath, dbPath) {
24499
24574
  return "absent";
24500
24575
  }
24501
24576
  function filesEqual(left, right) {
24502
- if (!existsSync10(left) || !existsSync10(right))
24577
+ if (!existsSync11(left) || !existsSync11(right))
24503
24578
  return false;
24504
24579
  if (statSync(left).size !== statSync(right).size)
24505
24580
  return false;
24506
- return readFileSync2(left).equals(readFileSync2(right));
24581
+ return readFileSync3(left).equals(readFileSync3(right));
24507
24582
  }
24508
24583
  function classifyWalletMigration(targetDir, sourceDir) {
24509
24584
  const targetConfig = join6(targetDir, "config.json");
@@ -24516,14 +24591,14 @@ function classifyWalletMigration(targetDir, sourceDir) {
24516
24591
  const sourceState = walletState(sourceConfig, sourceDb);
24517
24592
  if (targetState === "initialized" && sourceState === "initialized") {
24518
24593
  const configsMatch = filesEqual(targetConfig, sourceConfig);
24519
- const databasesMatch = !existsSync10(targetDb) && !existsSync10(sourceDb) ? true : filesEqual(targetDb, sourceDb);
24594
+ const databasesMatch = !existsSync11(targetDb) && !existsSync11(sourceDb) ? true : filesEqual(targetDb, sourceDb);
24520
24595
  if (configsMatch && databasesMatch)
24521
24596
  return { kind: "already-current" };
24522
24597
  return { kind: "conflict" };
24523
24598
  }
24524
24599
  if (targetState === "initialized")
24525
24600
  return { kind: "already-current" };
24526
- if (!existsSync10(sourceDb) && (existsSync10(sourceWal) || existsSync10(sourceShm))) {
24601
+ if (!existsSync11(sourceDb) && (existsSync11(sourceWal) || existsSync11(sourceShm))) {
24527
24602
  return { kind: "orphaned-sidecars" };
24528
24603
  }
24529
24604
  if (targetState === "database-only" || sourceState === "database-only") {
@@ -24598,11 +24673,11 @@ function summarizeDbReadonly(dbPath) {
24598
24673
  function summarizeConfig(configPath) {
24599
24674
  const result = { exists: false, path: configPath };
24600
24675
  try {
24601
- if (!existsSync11(configPath))
24676
+ if (!existsSync12(configPath))
24602
24677
  return result;
24603
24678
  result.exists = true;
24604
24679
  result.mtimeMs = statSync2(configPath).mtimeMs;
24605
- const parsed = JSON.parse(readFileSync3(configPath, "utf-8"));
24680
+ const parsed = JSON.parse(readFileSync4(configPath, "utf-8"));
24606
24681
  result.encrypted = parsed.encrypted === true;
24607
24682
  if (typeof parsed.createdAt === "string")
24608
24683
  result.createdAt = parsed.createdAt;
@@ -24624,7 +24699,7 @@ function summarizeConfig(configPath) {
24624
24699
  function summarizeDb(dbPath) {
24625
24700
  const result = { exists: false, path: dbPath };
24626
24701
  try {
24627
- if (!existsSync11(dbPath))
24702
+ if (!existsSync12(dbPath))
24628
24703
  return result;
24629
24704
  result.exists = true;
24630
24705
  const stats = statSync2(dbPath);
@@ -24933,21 +25008,21 @@ async function migrateLegacyWallet(options = {}) {
24933
25008
  const stagingDir = join8(dirname7(targetDir), `.${targetDir.split(/[\\/]/).at(-1) || "wallet"}.staging-${process.pid}-${Date.now()}`);
24934
25009
  try {
24935
25010
  await options.assertLegacyStopped?.();
24936
- mkdirSync8(dirname7(targetDir), { recursive: true, mode: 448 });
24937
- mkdirSync8(stagingDir, { mode: 448 });
25011
+ mkdirSync9(dirname7(targetDir), { recursive: true, mode: 448 });
25012
+ mkdirSync9(stagingDir, { mode: 448 });
24938
25013
  const stagedConfig = join8(stagingDir, "config.json");
24939
25014
  const stagedDb = join8(stagingDir, "coco.db");
24940
25015
  copyFileSync(sourceConfig, stagedConfig);
24941
- chmodSync(stagedConfig, 384);
24942
- const hasSourceDb = existsSync12(sourceDb);
25016
+ chmodSync2(stagedConfig, 384);
25017
+ const hasSourceDb = existsSync13(sourceDb);
24943
25018
  if (hasSourceDb) {
24944
25019
  snapshotDatabase(sourceDb, stagedDb);
24945
- chmodSync(stagedDb, 384);
25020
+ chmodSync2(stagedDb, 384);
24946
25021
  }
24947
25022
  if (statSync3(stagedConfig).size !== statSync3(sourceConfig).size) {
24948
25023
  throw new Error("Cannot migrate wallet: staged config failed size validation.");
24949
25024
  }
24950
- renameSync2(stagingDir, targetDir);
25025
+ renameSync3(stagingDir, targetDir);
24951
25026
  } catch (error) {
24952
25027
  rmSync(stagingDir, { recursive: true, force: true });
24953
25028
  throw error;
@@ -24955,13 +25030,13 @@ async function migrateLegacyWallet(options = {}) {
24955
25030
  releaseLegacyLock?.();
24956
25031
  }
24957
25032
  const cleanupWarnings = [];
24958
- const sourceFiles = [sourceConfig, sourceDb, sourceWal, sourceShm].filter(existsSync12);
25033
+ const sourceFiles = [sourceConfig, sourceDb, sourceWal, sourceShm].filter(existsSync13);
24959
25034
  if (sourceFiles.length > 0) {
24960
25035
  const archiveDir = join8(sourceDir, `wallet-migrated-${Date.now()}`);
24961
25036
  try {
24962
- mkdirSync8(archiveDir, { mode: 448 });
25037
+ mkdirSync9(archiveDir, { mode: 448 });
24963
25038
  for (const path of sourceFiles)
24964
- renameSync2(path, join8(archiveDir, basename(path)));
25039
+ renameSync3(path, join8(archiveDir, basename(path)));
24965
25040
  } catch (error) {
24966
25041
  const message = error instanceof Error ? error.message : String(error);
24967
25042
  cleanupWarnings.push(`Could not archive legacy wallet files: ${message}`);
@@ -27095,7 +27170,7 @@ zoo`.split(`
27095
27170
  // package.json
27096
27171
  var package_default = {
27097
27172
  name: "routstrd",
27098
- version: "0.4.1",
27173
+ version: "0.4.3",
27099
27174
  module: "src/index.ts",
27100
27175
  type: "module",
27101
27176
  private: false,
@@ -27121,7 +27196,7 @@ var package_default = {
27121
27196
  "@cashu/cashu-ts": "^4.3.0",
27122
27197
  "@cashu/coco-core": "^1.0.1",
27123
27198
  "@cashu/coco-sqlite-bun": "^1.0.1",
27124
- "@routstr/sdk": "^0.4.0",
27199
+ "@routstr/sdk": "^0.4.1",
27125
27200
  "@scure/bip39": "^2.2.0",
27126
27201
  "applesauce-core": "^5.1.0",
27127
27202
  "applesauce-relay": "^5.1.0",
@@ -27162,10 +27237,10 @@ ${invoice}`);
27162
27237
  }
27163
27238
  function initializeWallet(walletDir2 = walletDir()) {
27164
27239
  const walletConfig = join9(walletDir2, "config.json");
27165
- mkdirSync9(walletDir2, { recursive: true, mode: 448 });
27166
- chmodSync2(walletDir2, 448);
27167
- if (existsSync13(walletConfig)) {
27168
- chmodSync2(walletConfig, 384);
27240
+ mkdirSync10(walletDir2, { recursive: true, mode: 448 });
27241
+ chmodSync3(walletDir2, 448);
27242
+ if (existsSync14(walletConfig)) {
27243
+ chmodSync3(walletConfig, 384);
27169
27244
  console.log("Wallet already initialized.");
27170
27245
  return;
27171
27246
  }
@@ -27176,7 +27251,7 @@ function initializeWallet(walletDir2 = walletDir()) {
27176
27251
  encrypted: false,
27177
27252
  createdAt: new Date().toISOString()
27178
27253
  };
27179
- writeFileSync2(walletConfig, JSON.stringify(config, null, 2), {
27254
+ writeFileSync3(walletConfig, JSON.stringify(config, null, 2), {
27180
27255
  mode: 384,
27181
27256
  flag: "wx"
27182
27257
  });
@@ -27204,7 +27279,7 @@ Restarting routstrd daemon...`);
27204
27279
  for (let i2 = 0;i2 < 100; i2++) {
27205
27280
  await new Promise((resolve) => setTimeout(resolve, 100));
27206
27281
  const healthDown = !await isDaemonRunning();
27207
- const pidFileReleased = !existsSync13(pidFilePath);
27282
+ const pidFileReleased = !existsSync14(pidFilePath);
27208
27283
  if (healthDown && pidFileReleased)
27209
27284
  break;
27210
27285
  }
@@ -27247,16 +27322,16 @@ async function requireLocalDaemon() {
27247
27322
  }
27248
27323
  async function initDaemon() {
27249
27324
  console.log("Initializing routstrd...");
27250
- if (!existsSync13(CONFIG_DIR)) {
27251
- mkdirSync9(CONFIG_DIR, { recursive: true });
27325
+ if (!existsSync14(CONFIG_DIR)) {
27252
27326
  console.log(`Created config directory: ${CONFIG_DIR}`);
27253
27327
  }
27254
- if (!existsSync13(CONFIG_FILE)) {
27328
+ ensureDirsSync();
27329
+ if (!existsSync14(CONFIG_FILE)) {
27255
27330
  const config2 = {
27256
27331
  ...DEFAULT_CONFIG,
27257
27332
  cocodPath: null
27258
27333
  };
27259
- await Bun.write(CONFIG_FILE, JSON.stringify(config2, null, 2));
27334
+ saveDaemonConfig(config2);
27260
27335
  console.log(`Created config file: ${CONFIG_FILE}`);
27261
27336
  }
27262
27337
  const config = await loadConfig();
@@ -27265,7 +27340,7 @@ async function initDaemon() {
27265
27340
  const nsec = nip19_exports.nsecEncode(secretKey);
27266
27341
  const npub = npubFromSecretKey(secretKey);
27267
27342
  config.nsec = nsec;
27268
- await Bun.write(CONFIG_FILE, JSON.stringify(config, null, 2));
27343
+ saveDaemonConfig(config);
27269
27344
  console.log(`
27270
27345
  A new Nostr identity has been generated for authentication.`);
27271
27346
  console.log(`Your npub: ${npub}`);
@@ -27279,7 +27354,7 @@ A new Nostr identity has been generated for authentication.`);
27279
27354
  socketPath: legacyCocodSocketPath()
27280
27355
  }),
27281
27356
  acquireLegacyLock: () => {
27282
- mkdirSync9(dirname8(legacyCocodPidPath()), {
27357
+ mkdirSync10(dirname8(legacyCocodPidPath()), {
27283
27358
  recursive: true,
27284
27359
  mode: 448
27285
27360
  });
@@ -27307,10 +27382,7 @@ To ensure routstrd persists across system restarts, run: 'routstrd service insta
27307
27382
  }
27308
27383
  program.name("routstrd").description("Routstr daemon - Manage routstr processes").version(package_default.version, "--version", "output the version number");
27309
27384
  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
- ];
27385
+ const packages = [{ name: "routstrd", label: "routstrd" }];
27314
27386
  let updatedAny = false;
27315
27387
  for (const { name, label } of packages) {
27316
27388
  const installed = await getGlobalPackageVersion(name);
@@ -27444,8 +27516,8 @@ program.command("remote [url]").description("Show the configured remote daemon,
27444
27516
  process.exit(1);
27445
27517
  }
27446
27518
  }
27447
- if (!existsSync13(CONFIG_DIR)) {
27448
- mkdirSync9(CONFIG_DIR, { recursive: true });
27519
+ if (!existsSync14(CONFIG_DIR)) {
27520
+ mkdirSync10(CONFIG_DIR, { recursive: true });
27449
27521
  }
27450
27522
  const updates = { daemonUrl: url };
27451
27523
  if (options.authUrl) {
@@ -27463,7 +27535,7 @@ program.command("remote [url]").description("Show the configured remote daemon,
27463
27535
  ...config,
27464
27536
  ...updates
27465
27537
  };
27466
- await Bun.write(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
27538
+ saveDaemonConfig(updatedConfig);
27467
27539
  console.log(`Remote daemon URL set to: ${url}`);
27468
27540
  if (options.authUrl) {
27469
27541
  console.log(`Auth proxy URL set to: ${options.authUrl}`);
@@ -27485,7 +27557,7 @@ program.command("local").description("Switch back to local daemon mode (removes
27485
27557
  const previousAuthUrl = config.authUrl;
27486
27558
  const { daemonUrl: _daemonUrl, authUrl: _authUrl, ...rest } = config;
27487
27559
  const updatedConfig = rest;
27488
- await Bun.write(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
27560
+ saveDaemonConfig(updatedConfig);
27489
27561
  console.log("Switched back to local daemon mode.");
27490
27562
  if (previousDaemonUrl) {
27491
27563
  console.log(` Removed remote daemon URL: ${previousDaemonUrl}`);
@@ -27816,6 +27888,46 @@ providersCmd.command("enable <indices...>").description("Enable providers by the
27816
27888
  }
27817
27889
  }
27818
27890
  });
27891
+ providersCmd.command("reviews").description("Show all known providers with their stored review events and event IDs").action(async () => {
27892
+ await ensureDaemonRunning();
27893
+ const result = await callDaemon("/providers/reviews");
27894
+ if (result.error) {
27895
+ console.log(result.error);
27896
+ process.exit(1);
27897
+ }
27898
+ const output = result.output;
27899
+ if (!output) {
27900
+ console.log("No provider review data available.");
27901
+ return;
27902
+ }
27903
+ console.log(`Providers (${output.totalCount} total, ${output.reviewEventCount} stored review events):
27904
+ `);
27905
+ for (const provider of output.providers) {
27906
+ const status = provider.disabled ? "DISABLED" : "enabled ";
27907
+ console.log(` [${provider.index}] ${status} ${provider.baseUrl} (${provider.reviewCount} review${provider.reviewCount === 1 ? "" : "s"})`);
27908
+ if (provider.nodePubkeys.length > 0) {
27909
+ console.log(` node pubkeys: ${provider.nodePubkeys.join(", ")}`);
27910
+ }
27911
+ for (const review of provider.reviews) {
27912
+ const label = review.label || "review";
27913
+ const created = new Date(review.createdAt * 1000).toISOString();
27914
+ console.log(` - ${label} eventId: ${review.eventId}`);
27915
+ console.log(` node: ${review.nodePubkey ?? "(none)"}`);
27916
+ console.log(` author: ${review.authorPubkey ?? "(none)"}`);
27917
+ console.log(` at: ${created}`);
27918
+ }
27919
+ }
27920
+ if (output.unmatchedReviews.length > 0) {
27921
+ console.log(`
27922
+ Unmatched review events (${output.unmatchedReviews.length} \u2014 node pubkey not in any known provider):`);
27923
+ for (const review of output.unmatchedReviews) {
27924
+ const created = new Date(review.createdAt * 1000).toISOString();
27925
+ console.log(` - eventId: ${review.eventId}`);
27926
+ console.log(` node: ${review.nodePubkey ?? "(none)"}`);
27927
+ console.log(` at: ${created}`);
27928
+ }
27929
+ }
27930
+ });
27819
27931
  var clientsCmd = program.command("clients").description("List and manage clients");
27820
27932
  clientsCmd.command("list").description("List all clients").action(async () => {
27821
27933
  await listClientsAction();
@@ -28338,7 +28450,7 @@ serviceCmd.command("install").description("Install and start routstrd using PM2
28338
28450
  const path = __require("path");
28339
28451
  daemonPath = path.join(path.dirname(import.meta.url).replace("file://", ""), "daemon", "index.js");
28340
28452
  }
28341
- if (!existsSync13(daemonPath)) {
28453
+ if (!existsSync14(daemonPath)) {
28342
28454
  console.error(`Could not find daemon at ${daemonPath}. Did you run 'bun run build'?`);
28343
28455
  process.exit(1);
28344
28456
  }
@@ -28386,7 +28498,7 @@ program.command("restart").description("Restart the background daemon").option("
28386
28498
  for (let i2 = 0;i2 < 100; i2++) {
28387
28499
  await new Promise((resolve) => setTimeout(resolve, 100));
28388
28500
  const healthDown = !await isDaemonRunning();
28389
- const pidFileReleased = !existsSync13(pidFilePath);
28501
+ const pidFileReleased = !existsSync14(pidFilePath);
28390
28502
  if (healthDown && pidFileReleased) {
28391
28503
  break;
28392
28504
  }
@@ -28444,7 +28556,7 @@ xcashu mode is coming soon! Only lazyrefund/apikeys is available at this time.`)
28444
28556
  ...config,
28445
28557
  mode: selectedMode
28446
28558
  };
28447
- await Bun.write(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
28559
+ saveDaemonConfig(updatedConfig);
28448
28560
  console.log(`Mode set to '${selectedMode}'. Restarting daemon...`);
28449
28561
  const wasRunning = await isDaemonRunning();
28450
28562
  if (wasRunning) {
@@ -28477,7 +28589,7 @@ function getLogFileForDate2(logsDir, date = new Date) {
28477
28589
  return `${logsDir}/${year}-${month}-${day}.log`;
28478
28590
  }
28479
28591
  function readLastLines(file, lines) {
28480
- const content = readFileSync4(file, "utf8");
28592
+ const content = readFileSync5(file, "utf8");
28481
28593
  const allLines = content.replace(/\r\n/g, `
28482
28594
  `).split(`
28483
28595
  `);
@@ -28494,7 +28606,7 @@ async function followLogFile(file, lines) {
28494
28606
  let position = statSync4(file).size;
28495
28607
  while (true) {
28496
28608
  await new Promise((resolve) => setTimeout(resolve, 1000));
28497
- if (!existsSync13(file)) {
28609
+ if (!existsSync14(file)) {
28498
28610
  continue;
28499
28611
  }
28500
28612
  const size = statSync4(file).size;
@@ -28516,17 +28628,17 @@ program.command("logs").description("View daemon logs").option("-f, --follow", "
28516
28628
  const yesterday = new Date;
28517
28629
  yesterday.setDate(yesterday.getDate() - 1);
28518
28630
  const yesterdayFile = getLogFileForDate2(logsDir, yesterday);
28519
- if (!existsSync13(todayFile) && !existsSync13(yesterdayFile)) {
28631
+ if (!existsSync14(todayFile) && !existsSync14(yesterdayFile)) {
28520
28632
  console.log("No log files found. Daemon may not have started yet.");
28521
28633
  console.log(`Logs directory: ${logsDir}`);
28522
28634
  process.exit(1);
28523
28635
  }
28524
28636
  const lines = parseInt(options.lines, 10);
28525
28637
  const logFiles = [yesterdayFile, todayFile].filter((file, index, files) => {
28526
- return existsSync13(file) && files.indexOf(file) === index;
28638
+ return existsSync14(file) && files.indexOf(file) === index;
28527
28639
  });
28528
28640
  if (options.follow) {
28529
- if (existsSync13(todayFile)) {
28641
+ if (existsSync14(todayFile)) {
28530
28642
  await followLogFile(todayFile, lines);
28531
28643
  } else {
28532
28644
  console.log("No log file for today to follow.");