routstrd 0.2.21 → 0.3.0
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/.dockerignore +12 -0
- package/Dockerfile +11 -0
- package/bun.lock +79 -8
- package/dist/daemon/index.js +32564 -18466
- package/dist/index.js +271 -30
- package/docker-compose.yml +15 -0
- package/new-task.md +60 -0
- package/package.json +4 -3
- package/src/cli.ts +140 -1
- package/src/daemon/config-store.ts +13 -1
- package/src/daemon/wallet/auto-refill.ts +192 -0
- package/src/start-daemon.ts +5 -5
- package/src/utils/config.ts +24 -0
- package/src/utils/logger.ts +3 -3
package/src/cli.ts
CHANGED
|
@@ -231,7 +231,8 @@ program
|
|
|
231
231
|
"Mint URL to refund to (defaults to first mint in wallet)",
|
|
232
232
|
)
|
|
233
233
|
.option("-y, --yes", "Skip confirmation prompt", false)
|
|
234
|
-
.
|
|
234
|
+
.option("--xcashu", "Refund xcashu tokens only (uses refundXcashuTokens)", false)
|
|
235
|
+
.action(async (options: { mintUrl?: string; yes: boolean; xcashu: boolean }) => {
|
|
235
236
|
await ensureDaemonRunning();
|
|
236
237
|
|
|
237
238
|
let mintUrl = options.mintUrl;
|
|
@@ -257,6 +258,36 @@ program
|
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
try {
|
|
261
|
+
if (options.xcashu) {
|
|
262
|
+
// xcashu path: only refund xcashu tokens
|
|
263
|
+
const result = await callDaemon("/refund/xcashu", {
|
|
264
|
+
method: "POST",
|
|
265
|
+
body: { mintUrl },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (result.error) {
|
|
269
|
+
console.log(result.error);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const output = result.output as
|
|
274
|
+
| {
|
|
275
|
+
message: string;
|
|
276
|
+
results: Array<{ baseUrl: string; token: string; success: boolean; error?: string }>;
|
|
277
|
+
}
|
|
278
|
+
| undefined;
|
|
279
|
+
|
|
280
|
+
if (output) {
|
|
281
|
+
console.log(output.message);
|
|
282
|
+
console.log("\nResults:");
|
|
283
|
+
for (const r of output.results) {
|
|
284
|
+
const status = r.success ? "success" : `failed: ${r.error || "unknown"}`;
|
|
285
|
+
console.log(` - ${r.baseUrl}: ${status}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
260
291
|
const result = await callDaemon("/refund", {
|
|
261
292
|
method: "POST",
|
|
262
293
|
body: { mintUrl },
|
|
@@ -1253,6 +1284,114 @@ walletMintsCmd
|
|
|
1253
1284
|
});
|
|
1254
1285
|
});
|
|
1255
1286
|
|
|
1287
|
+
// ── NWC (Nostr Wallet Connect) commands ─────────────────────────
|
|
1288
|
+
|
|
1289
|
+
const nwcCmd = program
|
|
1290
|
+
.command("nwc")
|
|
1291
|
+
.description("Manage NWC (Nostr Wallet Connect) integration");
|
|
1292
|
+
|
|
1293
|
+
nwcCmd
|
|
1294
|
+
.command("connect")
|
|
1295
|
+
.description("Connect to a Lightning wallet via NWC")
|
|
1296
|
+
.argument("[connection-string]", "NWC connection string (nostr+walletconnect://...)")
|
|
1297
|
+
.action(async (connectionString?: string) => {
|
|
1298
|
+
if (!connectionString) {
|
|
1299
|
+
// Interactive mode: prompt for connection string
|
|
1300
|
+
const rl = require("readline").createInterface({
|
|
1301
|
+
input: process.stdin,
|
|
1302
|
+
output: process.stdout,
|
|
1303
|
+
});
|
|
1304
|
+
connectionString = await new Promise<string>((resolve) => {
|
|
1305
|
+
rl.question("Paste your NWC connection string: ", (answer: string) => {
|
|
1306
|
+
rl.close();
|
|
1307
|
+
resolve(answer.trim());
|
|
1308
|
+
});
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// Quick validation: must be nostr+walletconnect:// with a 64-char hex pubkey
|
|
1313
|
+
if (!/^nostr\+walletconnect:\/\/[0-9a-fA-F]{64}\?relay=/.test(connectionString)) {
|
|
1314
|
+
console.error("Invalid NWC connection string: expected nostr+walletconnect://<64-char-hex>?relay=...");
|
|
1315
|
+
process.exit(1);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
await handleDaemonCommand("/nwc/connect", {
|
|
1319
|
+
method: "POST",
|
|
1320
|
+
body: { connectionString },
|
|
1321
|
+
});
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
nwcCmd
|
|
1325
|
+
.command("disconnect")
|
|
1326
|
+
.description("Disconnect from NWC wallet")
|
|
1327
|
+
.action(async () => {
|
|
1328
|
+
await handleDaemonCommand("/nwc/disconnect", {
|
|
1329
|
+
method: "POST",
|
|
1330
|
+
});
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
nwcCmd
|
|
1334
|
+
.command("status")
|
|
1335
|
+
.description("Show NWC connection status and wallet info")
|
|
1336
|
+
.action(async () => {
|
|
1337
|
+
await handleDaemonCommand("/nwc/status");
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
nwcCmd
|
|
1341
|
+
.command("fund <amount>")
|
|
1342
|
+
.description("Manually fund the Cashu wallet from the connected NWC wallet")
|
|
1343
|
+
.action(async (amount: string) => {
|
|
1344
|
+
const parsedAmount = parsePositiveIntOrExit(amount, "amount");
|
|
1345
|
+
await handleDaemonCommand("/nwc/fund", {
|
|
1346
|
+
method: "POST",
|
|
1347
|
+
body: { amount: parsedAmount },
|
|
1348
|
+
});
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
const autoRefillCmd = nwcCmd
|
|
1352
|
+
.command("auto-refill")
|
|
1353
|
+
.description("Manage automatic wallet refill from NWC");
|
|
1354
|
+
|
|
1355
|
+
autoRefillCmd
|
|
1356
|
+
.command("on")
|
|
1357
|
+
.description("Enable auto-refill")
|
|
1358
|
+
.option(
|
|
1359
|
+
"--threshold <sats>",
|
|
1360
|
+
"Refill when Cashu balance drops below this many sats",
|
|
1361
|
+
"500",
|
|
1362
|
+
)
|
|
1363
|
+
.option("--amount <sats>", "Refill this many sats at a time", "1000")
|
|
1364
|
+
.option(
|
|
1365
|
+
"--cooldown <seconds>",
|
|
1366
|
+
"Minimum time between refills in seconds",
|
|
1367
|
+
"300",
|
|
1368
|
+
)
|
|
1369
|
+
.action(async (options: { threshold: string; amount: string; cooldown: string }) => {
|
|
1370
|
+
const threshold = parsePositiveIntOrExit(options.threshold, "threshold");
|
|
1371
|
+
const amount = parsePositiveIntOrExit(options.amount, "amount");
|
|
1372
|
+
const cooldownSec = parsePositiveIntOrExit(options.cooldown, "cooldown");
|
|
1373
|
+
|
|
1374
|
+
await handleDaemonCommand("/nwc/auto-refill", {
|
|
1375
|
+
method: "POST",
|
|
1376
|
+
body: {
|
|
1377
|
+
enabled: true,
|
|
1378
|
+
threshold,
|
|
1379
|
+
amount,
|
|
1380
|
+
cooldownMs: cooldownSec * 1000,
|
|
1381
|
+
},
|
|
1382
|
+
});
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
autoRefillCmd
|
|
1386
|
+
.command("off")
|
|
1387
|
+
.description("Disable auto-refill")
|
|
1388
|
+
.action(async () => {
|
|
1389
|
+
await handleDaemonCommand("/nwc/auto-refill", {
|
|
1390
|
+
method: "POST",
|
|
1391
|
+
body: { enabled: false },
|
|
1392
|
+
});
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1256
1395
|
// Stop
|
|
1257
1396
|
program
|
|
1258
1397
|
.command("stop")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mkdir } from "fs/promises";
|
|
2
|
-
import { existsSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import {
|
|
4
4
|
CONFIG_DIR,
|
|
5
5
|
CONFIG_FILE,
|
|
@@ -31,6 +31,18 @@ export async function loadDaemonConfig(): Promise<RoutstrdConfig> {
|
|
|
31
31
|
return DEFAULT_CONFIG;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export function loadDaemonConfigSync(): RoutstrdConfig {
|
|
35
|
+
try {
|
|
36
|
+
if (existsSync(CONFIG_FILE)) {
|
|
37
|
+
const content = readFileSync(CONFIG_FILE, "utf-8");
|
|
38
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error("Failed to load config:", error);
|
|
42
|
+
}
|
|
43
|
+
return DEFAULT_CONFIG;
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
export function saveDaemonConfig(config: RoutstrdConfig): void {
|
|
35
47
|
Bun.write(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
36
48
|
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Auto-refill loop for routstrd
|
|
2
|
+
// Monitors Cocod Cashu balance and triggers NWC funding when below threshold.
|
|
3
|
+
// This runs server-side, so refills happen regardless of whether a frontend is open.
|
|
4
|
+
//
|
|
5
|
+
// Uses applesauce-wallet-connect (same approach as nwc_integration/pay_invoice.mts).
|
|
6
|
+
|
|
7
|
+
import type { CocodClient } from "./cocod-client";
|
|
8
|
+
import type { WalletConnect } from "applesauce-wallet-connect";
|
|
9
|
+
import { logger } from "../../utils/logger";
|
|
10
|
+
|
|
11
|
+
export interface AutoRefillConfig {
|
|
12
|
+
/** Minimum sats balance before triggering a refill */
|
|
13
|
+
threshold: number;
|
|
14
|
+
/** Amount of sats to refill each time */
|
|
15
|
+
amount: number;
|
|
16
|
+
/** Minimum time between refills (milliseconds) */
|
|
17
|
+
cooldownMs: number;
|
|
18
|
+
/** Maximum consecutive failures before stopping (0 = never stop on failures) */
|
|
19
|
+
maxConsecutiveFailures?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Errors that cannot be resolved by retrying — the user must intervene.
|
|
23
|
+
const FATAL_ERROR_PATTERNS = [
|
|
24
|
+
/insufficient (?:balance|funds)/i,
|
|
25
|
+
/invalid (?:credentials|auth)/i,
|
|
26
|
+
/forbidden/i,
|
|
27
|
+
/unauthorized/i,
|
|
28
|
+
/invoice.*expired/i,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function isFatalError(message: string): boolean {
|
|
32
|
+
return FATAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function startAutoRefillLoop(
|
|
36
|
+
cocod: CocodClient,
|
|
37
|
+
getWallet: () => WalletConnect | undefined,
|
|
38
|
+
getConfig: () => AutoRefillConfig | undefined,
|
|
39
|
+
intervalMs: number = 5000,
|
|
40
|
+
): () => void {
|
|
41
|
+
let lastRefillAt = 0;
|
|
42
|
+
let lastAttemptAt = 0; // tracks last attempt (success or failure) for backoff
|
|
43
|
+
let running = true;
|
|
44
|
+
let timeout: ReturnType<typeof setInterval> | null = null;
|
|
45
|
+
let checkInProgress = false;
|
|
46
|
+
let consecutiveFailures = 0;
|
|
47
|
+
|
|
48
|
+
async function checkAndRefill(): Promise<void> {
|
|
49
|
+
if (!running) return;
|
|
50
|
+
if (checkInProgress) return;
|
|
51
|
+
const wallet = getWallet();
|
|
52
|
+
if (!wallet?.service) {
|
|
53
|
+
// NWC not connected — nothing to do
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Read config fresh each cycle so changes apply without restart
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
if (!config) {
|
|
60
|
+
// Auto-refill disabled
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
if (now - lastRefillAt < config.cooldownMs) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
checkInProgress = true;
|
|
70
|
+
|
|
71
|
+
// If we've been failing too much, back off rather than retrying at full speed.
|
|
72
|
+
// This prevents tight loops on transient errors.
|
|
73
|
+
const backoffInterval = Math.min(
|
|
74
|
+
intervalMs * Math.pow(2, consecutiveFailures),
|
|
75
|
+
5 * 60 * 1000, // cap at 5 minutes
|
|
76
|
+
);
|
|
77
|
+
if (now - lastAttemptAt < backoffInterval) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const balances = await cocod.getBalances();
|
|
83
|
+
const totalBalance = Object.values(balances).reduce<number>(
|
|
84
|
+
(sum, b) => sum + (typeof b === "number" ? b : 0),
|
|
85
|
+
0,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (totalBalance >= config.threshold) {
|
|
89
|
+
// Balance is sufficient
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
logger.log(
|
|
94
|
+
`[auto-refill] Balance ${totalBalance} sats < threshold ${config.threshold}. Refilling ${config.amount} sats...`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Get active mint
|
|
98
|
+
const mints = await cocod.listMints();
|
|
99
|
+
const mintUrl = mints[0];
|
|
100
|
+
if (!mintUrl) {
|
|
101
|
+
logger.error("[auto-refill] No active mint configured");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Step 1: Create a BOLT-11 invoice via cocod to fund the Cashu wallet
|
|
106
|
+
logger.log(
|
|
107
|
+
`[auto-refill] Creating BOLT-11 invoice for ${config.amount} sats via ${mintUrl}...`,
|
|
108
|
+
);
|
|
109
|
+
const invoice = await cocod.receiveBolt11(config.amount, mintUrl);
|
|
110
|
+
|
|
111
|
+
// Step 2: Pay the invoice via NWC (applesauce)
|
|
112
|
+
logger.log(`[auto-refill] Paying invoice via NWC...`);
|
|
113
|
+
const currentWallet = getWallet();
|
|
114
|
+
if (!currentWallet?.service) {
|
|
115
|
+
logger.log("[auto-refill] Wallet disconnected during refill check");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const payment = await currentWallet.payInvoice(invoice);
|
|
119
|
+
|
|
120
|
+
// Step 3: The Cashu mint should automatically detect the paid invoice
|
|
121
|
+
// and issue tokens. We don't need to explicitly mint here; cocod
|
|
122
|
+
// handles this on its end when the mint sees the payment.
|
|
123
|
+
const preimage = payment.preimage;
|
|
124
|
+
if (preimage) {
|
|
125
|
+
logger.log(
|
|
126
|
+
`[auto-refill] Successfully refilled ${config.amount} sats. Preimage: ${preimage.slice(0, 16)}...`,
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
logger.log(
|
|
130
|
+
`[auto-refill] Successfully refilled ${config.amount} sats (no preimage returned).`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
if (payment.fees_paid !== undefined) {
|
|
134
|
+
logger.log(`[auto-refill] Fees paid: ${payment.fees_paid} msats`);
|
|
135
|
+
}
|
|
136
|
+
lastRefillAt = now;
|
|
137
|
+
lastAttemptAt = now;
|
|
138
|
+
consecutiveFailures = 0; // reset on success
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
+
lastAttemptAt = now; // track for backoff regardless of success/failure
|
|
142
|
+
consecutiveFailures++;
|
|
143
|
+
|
|
144
|
+
if (isFatalError(message)) {
|
|
145
|
+
// Cooldown for 10 minutes — the user likely needs to fund their NWC wallet.
|
|
146
|
+
// We don't stop permanently so it auto-recovers without a restart.
|
|
147
|
+
// Set lastRefillAt into the future so all time-gate checks block.
|
|
148
|
+
const FATAL_COOLDOWN_MS = 10 * 60 * 1000;
|
|
149
|
+
logger.error(
|
|
150
|
+
`[auto-refill] FATAL: ${message}. The funding wallet (NWC) cannot pay. ` +
|
|
151
|
+
`Cooling down for ${FATAL_COOLDOWN_MS / 60000} minutes. ` +
|
|
152
|
+
`Fund your NWC wallet and auto-refill will retry automatically.`,
|
|
153
|
+
);
|
|
154
|
+
lastRefillAt = now + FATAL_COOLDOWN_MS;
|
|
155
|
+
consecutiveFailures = 0; // reset — no point counting these as transient backoffs
|
|
156
|
+
checkInProgress = false;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const maxFailures = config.maxConsecutiveFailures ?? 0;
|
|
161
|
+
if (maxFailures > 0 && consecutiveFailures >= maxFailures) {
|
|
162
|
+
logger.error(
|
|
163
|
+
`[auto-refill] Stopping after ${consecutiveFailures} consecutive failures (maxConsecutiveFailures=${maxFailures}). ` +
|
|
164
|
+
`Last error: ${message}`,
|
|
165
|
+
);
|
|
166
|
+
running = false;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
logger.error(
|
|
171
|
+
`[auto-refill] Error (attempt ${consecutiveFailures}, next in ${Math.round(backoffInterval / 1000)}s): ${message}`,
|
|
172
|
+
);
|
|
173
|
+
} finally {
|
|
174
|
+
checkInProgress = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check immediately on start
|
|
179
|
+
checkAndRefill();
|
|
180
|
+
|
|
181
|
+
// Then poll on interval
|
|
182
|
+
timeout = setInterval(checkAndRefill, intervalMs);
|
|
183
|
+
|
|
184
|
+
return () => {
|
|
185
|
+
running = false;
|
|
186
|
+
if (timeout) {
|
|
187
|
+
clearInterval(timeout);
|
|
188
|
+
timeout = null;
|
|
189
|
+
}
|
|
190
|
+
logger.log("[auto-refill] Stopped");
|
|
191
|
+
};
|
|
192
|
+
}
|
package/src/start-daemon.ts
CHANGED
|
@@ -28,7 +28,7 @@ async function startDaemonUnlocked(
|
|
|
28
28
|
const startupTimeoutMs = 10 * 60 * 1000;
|
|
29
29
|
|
|
30
30
|
if (await isDaemonHealthy(port)) {
|
|
31
|
-
|
|
31
|
+
console.log(`Routstr daemon already running on http://localhost:${port}/v1`);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -43,8 +43,8 @@ async function startDaemonUnlocked(
|
|
|
43
43
|
const shellCmd = `bun run "${daemonScript}" ${args.map((a) => `'${a}'`).join(" ")}`;
|
|
44
44
|
|
|
45
45
|
const proc = Bun.spawn(["sh", "-c", shellCmd], {
|
|
46
|
-
stdout: "
|
|
47
|
-
stderr: "
|
|
46
|
+
stdout: "ignore",
|
|
47
|
+
stderr: "ignore",
|
|
48
48
|
stdin: "ignore",
|
|
49
49
|
detached: true,
|
|
50
50
|
});
|
|
@@ -67,7 +67,7 @@ async function startDaemonUnlocked(
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (await isDaemonHealthy(port)) {
|
|
70
|
-
|
|
70
|
+
console.log(`Routstr daemon started (PID: ${proc.pid}).`);
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -84,7 +84,7 @@ export async function startDaemon(
|
|
|
84
84
|
const startupTimeoutMs = 10 * 60 * 1000;
|
|
85
85
|
|
|
86
86
|
if (await isDaemonHealthy(port)) {
|
|
87
|
-
|
|
87
|
+
console.log(`Routstr daemon already running on http://localhost:${port}/v1`);
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
|
package/src/utils/config.ts
CHANGED
|
@@ -7,6 +7,28 @@ export const DB_PATH = `${CONFIG_DIR}/routstr.db`;
|
|
|
7
7
|
export const CONFIG_FILE = `${CONFIG_DIR}/config.json`;
|
|
8
8
|
export const LOGS_DIR = `${CONFIG_DIR}/logs`;
|
|
9
9
|
|
|
10
|
+
/** NWC auto-refill configuration */
|
|
11
|
+
export interface NwcAutoRefillConfig {
|
|
12
|
+
/** Whether auto-refill is enabled */
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** Refill when Cashu balance drops below this many sats */
|
|
15
|
+
threshold: number;
|
|
16
|
+
/** Refill this many sats at a time */
|
|
17
|
+
amount: number;
|
|
18
|
+
/** Minimum time between refills in milliseconds */
|
|
19
|
+
cooldownMs: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** NWC configuration section */
|
|
23
|
+
export interface NwcConfig {
|
|
24
|
+
/** NWC mode: "funding_source" = NWC funds the cocod Cashu wallet */
|
|
25
|
+
mode: "funding_source" | "standalone";
|
|
26
|
+
/** NWC connection string (nostr+walletconnect://...) */
|
|
27
|
+
connectionString?: string;
|
|
28
|
+
/** Auto-refill settings */
|
|
29
|
+
autoRefill?: NwcAutoRefillConfig;
|
|
30
|
+
}
|
|
31
|
+
|
|
10
32
|
export interface RoutstrdConfig {
|
|
11
33
|
port: number;
|
|
12
34
|
provider: string | null;
|
|
@@ -16,6 +38,8 @@ export interface RoutstrdConfig {
|
|
|
16
38
|
nsec?: string;
|
|
17
39
|
/** Nostr hex pubkey for routstr review/model events (kind 38425/38423). */
|
|
18
40
|
routstrPubkey?: string;
|
|
41
|
+
/** NWC integration configuration */
|
|
42
|
+
nwc?: NwcConfig;
|
|
19
43
|
}
|
|
20
44
|
|
|
21
45
|
export const DEFAULT_CONFIG: RoutstrdConfig = {
|
package/src/utils/logger.ts
CHANGED
|
@@ -48,18 +48,18 @@ async function writeLog(level: string, ...args: unknown[]) {
|
|
|
48
48
|
|
|
49
49
|
export const logger = {
|
|
50
50
|
log: (...args: unknown[]) => {
|
|
51
|
-
console.log(...args);
|
|
52
51
|
writeLog("INFO", ...args);
|
|
53
52
|
},
|
|
54
53
|
debug: (...args: unknown[]) => {
|
|
55
54
|
writeLog("DEBUG", ...args);
|
|
56
55
|
},
|
|
56
|
+
warn: (...args: unknown[]) => {
|
|
57
|
+
writeLog("WARN", ...args);
|
|
58
|
+
},
|
|
57
59
|
error: (...args: unknown[]) => {
|
|
58
|
-
console.error(...args);
|
|
59
60
|
writeLog("ERROR", ...args);
|
|
60
61
|
},
|
|
61
62
|
info: (...args: unknown[]) => {
|
|
62
|
-
console.log(...args);
|
|
63
63
|
writeLog("INFO", ...args);
|
|
64
64
|
},
|
|
65
65
|
};
|