From c98709e38378e497d77ee187811fefbb8227751c Mon Sep 17 00:00:00 2001 From: redshift <213178690+1ftredsh@users.noreply.github.com> Date: Sat, 23 May 2026 22:37:43 +0800 Subject: [PATCH] feat: auto-refill no longer requires daemon restart Changed startAutoRefillLoop to accept a config getter instead of a static config object. The getter reads config from disk on every check cycle, so CLI changes (nwc auto-refill on/off) take effect immediately. - auto-refill.ts: config parameter replaced with getConfig() callback - wallet/index.ts: always start loop when wallet exists, use getter - daemon/index.ts: pass getAutoRefillConfig reading from disk sync - config-store.ts: added loadDaemonConfigSync() for sync disk reads - http/index.ts + cli.ts: removed 'Restart daemon to apply' messages --- src/cli.ts | 12 +++--------- src/daemon/config-store.ts | 14 +++++++++++++- src/daemon/http/index.ts | 2 +- src/daemon/index.ts | 26 ++++++++++++++++---------- src/daemon/wallet/auto-refill.ts | 9 ++++++++- src/daemon/wallet/index.ts | 28 ++++++++++++++++++++-------- 6 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 4ba3c66..c682760 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1309,13 +1309,9 @@ nwcCmd }); } - // Validate the connection string format - const { validateConnectionString } = await import( - "./daemon/wallet/nwc-client" - ); - const validation = validateConnectionString(connectionString); - if (!validation.valid) { - console.error(`Invalid NWC connection string: ${validation.error}`); + // Quick validation: must be nostr+walletconnect:// with a 64-char hex pubkey + if (!/^nostr\+walletconnect:\/\/[0-9a-fA-F]{64}\?relay=/.test(connectionString)) { + console.error("Invalid NWC connection string: expected nostr+walletconnect://<64-char-hex>?relay=..."); process.exit(1); } @@ -1387,7 +1383,6 @@ autoRefillCmd cooldownMs: cooldownSec * 1000, }, }); - console.log("\nRun 'routstrd restart' to apply."); }); autoRefillCmd @@ -1398,7 +1393,6 @@ autoRefillCmd method: "POST", body: { enabled: false }, }); - console.log("\nRun 'routstrd restart' to apply."); }); // Stop diff --git a/src/daemon/config-store.ts b/src/daemon/config-store.ts index cf02846..6b0c4db 100644 --- a/src/daemon/config-store.ts +++ b/src/daemon/config-store.ts @@ -1,5 +1,5 @@ import { mkdir } from "fs/promises"; -import { existsSync } from "fs"; +import { existsSync, readFileSync } from "fs"; import { CONFIG_DIR, CONFIG_FILE, @@ -31,6 +31,18 @@ export async function loadDaemonConfig(): Promise { return DEFAULT_CONFIG; } +export function loadDaemonConfigSync(): RoutstrdConfig { + try { + if (existsSync(CONFIG_FILE)) { + const content = readFileSync(CONFIG_FILE, "utf-8"); + return { ...DEFAULT_CONFIG, ...JSON.parse(content) }; + } + } catch (error) { + logger.error("Failed to load config:", error); + } + return DEFAULT_CONFIG; +} + export function saveDaemonConfig(config: RoutstrdConfig): void { Bun.write(CONFIG_FILE, JSON.stringify(config, null, 2)); } diff --git a/src/daemon/http/index.ts b/src/daemon/http/index.ts index 192979d..23efeb2 100644 --- a/src/daemon/http/index.ts +++ b/src/daemon/http/index.ts @@ -558,7 +558,7 @@ export function createDaemonRequestHandler(deps: { return { output: { - message: `Auto-refill ${enabled ? "enabled" : "disabled"}. Restart daemon to apply.`, + message: `Auto-refill ${enabled ? "enabled" : "disabled"}.`, autoRefill: config.nwc.autoRefill, }, }; diff --git a/src/daemon/index.ts b/src/daemon/index.ts index 570cccf..59cad39 100644 --- a/src/daemon/index.ts +++ b/src/daemon/index.ts @@ -26,12 +26,13 @@ function makeSdkLogger(prefix?: string): SdkLogger { } const daemonSdkLogger: SdkLogger = makeSdkLogger(); import { parseArgs } from "./args"; -import { ensureDirs, loadDaemonConfig, saveDaemonConfig } from "./config-store"; +import { ensureDirs, loadDaemonConfig, loadDaemonConfigSync, saveDaemonConfig } from "./config-store"; import { createBunSqliteDriver, createBunSqliteUsageTrackingDriver, } from "@routstr/sdk/storage"; import { createWalletAdapter } from "./wallet"; +import type { AutoRefillConfig } from "./wallet/auto-refill"; import { createCocodClient } from "./wallet/cocod-client"; import { createModelService } from "./models"; import { createDaemonRequestHandler } from "./http"; @@ -72,20 +73,25 @@ async function main(): Promise { const walletClient = createCocodClient({ cocodPath: config.cocodPath }); // ── Auto-refill configuration ──────────────────────────────── + // Uses a getter that reads config from disk each cycle, so + // CLI changes take effect immediately without a daemon restart. - const nwcAutoRefill = - config.nwc?.autoRefill?.enabled && config.nwc?.connectionString - ? { - threshold: config.nwc.autoRefill.threshold, - amount: config.nwc.autoRefill.amount, - cooldownMs: config.nwc.autoRefill.cooldownMs, - } - : undefined; + const getAutoRefillConfig = (): AutoRefillConfig | undefined => { + const cfg = loadDaemonConfigSync(); + if (cfg.nwc?.autoRefill?.enabled && cfg.nwc?.connectionString) { + return { + threshold: cfg.nwc.autoRefill.threshold, + amount: cfg.nwc.autoRefill.amount, + cooldownMs: cfg.nwc.autoRefill.cooldownMs, + }; + } + return undefined; + }; const walletAdapter = await createWalletAdapter({ cocodPath: config.cocodPath, walletClient, - autoRefill: nwcAutoRefill, + getAutoRefillConfig, nwcConnectionString: config.nwc?.connectionString, }); diff --git a/src/daemon/wallet/auto-refill.ts b/src/daemon/wallet/auto-refill.ts index 780247b..dddb191 100644 --- a/src/daemon/wallet/auto-refill.ts +++ b/src/daemon/wallet/auto-refill.ts @@ -20,7 +20,7 @@ export interface AutoRefillConfig { export function startAutoRefillLoop( cocod: CocodClient, wallet: WalletConnect, - config: AutoRefillConfig, + getConfig: () => AutoRefillConfig | undefined, intervalMs: number = 5000, ): () => void { let lastRefillAt = 0; @@ -36,6 +36,13 @@ export function startAutoRefillLoop( return; } + // Read config fresh each cycle so changes apply without restart + const config = getConfig(); + if (!config) { + // Auto-refill disabled + return; + } + const now = Date.now(); if (now - lastRefillAt < config.cooldownMs) { return; diff --git a/src/daemon/wallet/index.ts b/src/daemon/wallet/index.ts index 866e6f7..fce5e77 100644 --- a/src/daemon/wallet/index.ts +++ b/src/daemon/wallet/index.ts @@ -22,8 +22,14 @@ export interface WalletAdapterOptions { walletClient?: CocodClient; /** NWC connection string for Lightning funding (uses applesauce-wallet-connect) */ nwcConnectionString?: string; - /** Auto-refill configuration */ + /** Auto-refill configuration (static, for startup only) */ autoRefill?: AutoRefillConfig; + /** + * Config getter called on every check cycle to allow live updates. + * Return undefined to disable auto-refill, or a config to use. + * When provided, this replaces the static `autoRefill` option. + */ + getAutoRefillConfig?: () => AutoRefillConfig | undefined; } export async function createWalletAdapter( @@ -266,15 +272,21 @@ export async function createWalletAdapter( let stopAutoRefill: (() => void) | undefined; - if (options.autoRefill && wallet) { - stopAutoRefill = startAutoRefillLoop( - client, - wallet, - options.autoRefill, - ); + const autoRefillConfig = options.getAutoRefillConfig + ? options.getAutoRefillConfig() + : options.autoRefill; + + if (autoRefillConfig && wallet) { + const getConfig = options.getAutoRefillConfig ?? (() => options.autoRefill); + stopAutoRefill = startAutoRefillLoop(client, wallet, getConfig); logger.log( - `[wallet] Auto-refill enabled: threshold=${options.autoRefill.threshold} sats, amount=${options.autoRefill.amount} sats, cooldown=${options.autoRefill.cooldownMs}ms`, + `[wallet] Auto-refill enabled: threshold=${autoRefillConfig.threshold} sats, amount=${autoRefillConfig.amount} sats, cooldown=${autoRefillConfig.cooldownMs}ms`, ); + } else if (wallet && options.getAutoRefillConfig) { + // Wallet exists but auto-refill is not currently enabled. + // Start the loop anyway so it can pick up changes without a restart. + stopAutoRefill = startAutoRefillLoop(client, wallet, options.getAutoRefillConfig); + logger.log("[wallet] Auto-refill loop started (currently disabled — enable via CLI to activate)"); } try {