@fiber-pay/runtime 0.1.0-rc.4 → 0.1.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -126,10 +126,10 @@ fiber-pay logs --source runtime --follow
126
126
 
127
127
  When started from `fiber-pay node start` or `fiber-pay runtime start`, runtime/fnn logs are persisted to:
128
128
 
129
- - `<data-dir>/logs/fnn.stdout.log`
130
- - `<data-dir>/logs/fnn.stderr.log`
131
- - `<data-dir>/logs/runtime.alerts.jsonl`
129
+ - `<data-dir>/logs/<YYYY-MM-DD>/fnn.stdout.log`
130
+ - `<data-dir>/logs/<YYYY-MM-DD>/fnn.stderr.log`
131
+ - `<data-dir>/logs/<YYYY-MM-DD>/runtime.alerts.jsonl`
132
132
 
133
- `<data-dir>/runtime.meta.json` stores these paths so agents can read files directly during troubleshooting.
133
+ `<data-dir>/runtime.meta.json` is written at startup and stores the log file paths for that startup day, along with `logsBaseDir`, so agents can read those files directly during troubleshooting. Because logs rotate daily at UTC midnight while `runtime.meta.json` is not rewritten, its stored per-file paths can become stale; for other days' logs, use `logsBaseDir` together with the appropriate `<YYYY-MM-DD>` directory (or the `fiber-pay logs --date/--list-dates` options).
134
134
 
135
135
  You can view these files directly via CLI without `cat` using `fiber-pay logs` (alias: `fiber-pay log`).
package/dist/index.d.ts CHANGED
@@ -88,7 +88,10 @@ interface ChannelJobAlertData {
88
88
  idempotencyKey: string;
89
89
  retryCount: number;
90
90
  action?: string;
91
+ peerId?: string;
92
+ temporaryChannelId?: `0x${string}`;
91
93
  channelId?: `0x${string}`;
94
+ fundingAmount?: string;
92
95
  error?: string;
93
96
  }
94
97
  interface AlertBackend {
@@ -228,7 +231,12 @@ interface FileAlertConfig {
228
231
  type: 'file';
229
232
  path: string;
230
233
  }
231
- type AlertBackendConfig = StdoutAlertConfig | WebhookAlertConfig | WebsocketAlertConfig | FileAlertConfig;
234
+ interface DailyFileAlertConfig {
235
+ type: 'daily-file';
236
+ baseLogsDir: string;
237
+ filename?: string;
238
+ }
239
+ type AlertBackendConfig = StdoutAlertConfig | WebhookAlertConfig | WebsocketAlertConfig | FileAlertConfig | DailyFileAlertConfig;
232
240
  interface RuntimeConfig {
233
241
  fiberRpcUrl: string;
234
242
  channelPollIntervalMs: number;
@@ -305,6 +313,9 @@ declare class FiberMonitorService extends EventEmitter {
305
313
  private extractPaymentHash;
306
314
  private extractInvoiceHash;
307
315
  private extractChannelId;
316
+ private extractChannelPeerId;
317
+ private extractTemporaryChannelId;
318
+ private extractChannelFundingAmount;
308
319
  private normalizeHash;
309
320
  }
310
321
 
@@ -658,4 +669,4 @@ declare class PaymentProofManager {
658
669
  private ensureDirectory;
659
670
  }
660
671
 
661
- export { type Alert, type AlertBackend, type AlertBackendConfig, type AlertFilter, type AlertInput, type AlertPriority, type AlertType, type ChannelBalanceChangedData, type ChannelJob, type ChannelJobAction, type ChannelJobAlertData, type ChannelJobParams, type ChannelJobResult, type ChannelStateChangedData, type ClassifiedError, type ErrorCategory, FiberMonitorService, type FileAlertConfig, type InvoiceJob, type InvoiceJobAction, type InvoiceJobAlertData, type InvoiceJobParams, type InvoiceJobResult, type Job, type JobEvent, type JobEventType, type JobFilter, JobManager, type JobState, type JobType, MemoryStore, type PaymentJob, type PaymentJobAlertData, type PaymentJobParams, type PaymentJobResult, type PaymentProof, PaymentProofManager, type PaymentProofSummary, type PeerEventData, type PendingTlcChangedData, type RetryPolicy, type RpcHealthData, RpcMonitorProxy, type RuntimeConfig, type RuntimeConfigInput, type RuntimeJob, SqliteJobStore, type StdoutAlertConfig, TERMINAL_JOB_STATES, type TrackedInvoiceState, type TrackedPaymentState, type WebhookAlertConfig, type WebsocketAlertConfig, alertPriorityOrder, alertTypeValues, classifyRpcError, computeRetryDelay, createRuntimeConfig, defaultPaymentRetryPolicy, defaultRuntimeConfig, formatRuntimeAlert, isAlertPriority, isAlertType, parseListenAddress, paymentStateMachine, shouldRetry, startRuntimeService };
672
+ export { type Alert, type AlertBackend, type AlertBackendConfig, type AlertFilter, type AlertInput, type AlertPriority, type AlertType, type ChannelBalanceChangedData, type ChannelJob, type ChannelJobAction, type ChannelJobAlertData, type ChannelJobParams, type ChannelJobResult, type ChannelStateChangedData, type ClassifiedError, type DailyFileAlertConfig, type ErrorCategory, FiberMonitorService, type FileAlertConfig, type InvoiceJob, type InvoiceJobAction, type InvoiceJobAlertData, type InvoiceJobParams, type InvoiceJobResult, type Job, type JobEvent, type JobEventType, type JobFilter, JobManager, type JobState, type JobType, MemoryStore, type PaymentJob, type PaymentJobAlertData, type PaymentJobParams, type PaymentJobResult, type PaymentProof, PaymentProofManager, type PaymentProofSummary, type PeerEventData, type PendingTlcChangedData, type RetryPolicy, type RpcHealthData, RpcMonitorProxy, type RuntimeConfig, type RuntimeConfigInput, type RuntimeJob, SqliteJobStore, type StdoutAlertConfig, TERMINAL_JOB_STATES, type TrackedInvoiceState, type TrackedPaymentState, type WebhookAlertConfig, type WebsocketAlertConfig, alertPriorityOrder, alertTypeValues, classifyRpcError, computeRetryDelay, createRuntimeConfig, defaultPaymentRetryPolicy, defaultRuntimeConfig, formatRuntimeAlert, isAlertPriority, isAlertType, parseListenAddress, paymentStateMachine, shouldRetry, startRuntimeService };
package/dist/index.js CHANGED
@@ -219,7 +219,7 @@ var AlertManager = class {
219
219
 
220
220
  // src/alerts/backends/file-jsonl.ts
221
221
  import { appendFileSync, mkdirSync } from "fs";
222
- import { dirname } from "path";
222
+ import { dirname, join } from "path";
223
223
  var JsonlFileAlertBackend = class {
224
224
  path;
225
225
  constructor(path) {
@@ -231,6 +231,27 @@ var JsonlFileAlertBackend = class {
231
231
  `, "utf-8");
232
232
  }
233
233
  };
234
+ function todayDateString() {
235
+ const now = /* @__PURE__ */ new Date();
236
+ const y = now.getUTCFullYear();
237
+ const m = String(now.getUTCMonth() + 1).padStart(2, "0");
238
+ const d = String(now.getUTCDate()).padStart(2, "0");
239
+ return `${y}-${m}-${d}`;
240
+ }
241
+ var DailyJsonlFileAlertBackend = class {
242
+ baseLogsDir;
243
+ filename;
244
+ constructor(baseLogsDir, filename = "runtime.alerts.jsonl") {
245
+ this.baseLogsDir = baseLogsDir;
246
+ this.filename = filename;
247
+ }
248
+ async send(alert) {
249
+ const dateDir = join(this.baseLogsDir, todayDateString());
250
+ mkdirSync(dateDir, { recursive: true });
251
+ appendFileSync(join(dateDir, this.filename), `${JSON.stringify(alert)}
252
+ `, "utf-8");
253
+ }
254
+ };
234
255
 
235
256
  // src/alerts/backends/stdout.ts
236
257
  var StdoutAlertBackend = class {
@@ -953,7 +974,8 @@ async function* runChannelJob(job, rpc, policy, signal) {
953
974
  }
954
975
  });
955
976
  yield current;
956
- if (!current.params.waitForClosed) {
977
+ const waitForClosed = current.params.waitForClosed ?? Boolean(shutdownParams.force);
978
+ if (!waitForClosed) {
957
979
  current = transitionJobState(current, channelStateMachine, "payment_success");
958
980
  yield current;
959
981
  return;
@@ -1379,6 +1401,19 @@ async function* runPaymentJob(job, rpc, policy, signal) {
1379
1401
  yield current;
1380
1402
  continue;
1381
1403
  }
1404
+ if (current.params.sendPaymentParams.dry_run) {
1405
+ current = transitionJobState(current, paymentStateMachine, "payment_success", {
1406
+ patch: {
1407
+ result: {
1408
+ paymentHash: sendResult.payment_hash,
1409
+ status: "DryRunSuccess",
1410
+ fee: sendResult.fee
1411
+ }
1412
+ }
1413
+ });
1414
+ yield current;
1415
+ return;
1416
+ }
1382
1417
  current = transitionJobState(current, paymentStateMachine, "payment_inflight");
1383
1418
  if (paymentHash) {
1384
1419
  current = {
@@ -2132,11 +2167,13 @@ var HealthMonitor = class extends BaseMonitor {
2132
2167
  };
2133
2168
 
2134
2169
  // src/monitors/tracker-utils.ts
2170
+ function isNotFoundError(error) {
2171
+ const message = error instanceof Error ? error.message : String(error);
2172
+ return /not found|does not exist|no such/i.test(message);
2173
+ }
2135
2174
  function isExpectedTrackerError(error) {
2136
2175
  const message = error instanceof Error ? error.message : String(error);
2137
- return /not found|does not exist|no such|temporarily unavailable|connection refused|timed out|timeout/i.test(
2138
- message
2139
- );
2176
+ return /temporarily unavailable|connection refused|timed out|timeout/i.test(message);
2140
2177
  }
2141
2178
 
2142
2179
  // src/monitors/invoice-tracker.ts
@@ -2208,6 +2245,21 @@ var InvoiceTracker = class extends BaseMonitor {
2208
2245
  }
2209
2246
  }
2210
2247
  } catch (error) {
2248
+ if (isNotFoundError(error)) {
2249
+ this.store.updateTrackedInvoice(invoice.paymentHash, "Cancelled");
2250
+ await this.alerts.emit({
2251
+ type: "invoice_cancelled",
2252
+ priority: "medium",
2253
+ source: this.name,
2254
+ data: {
2255
+ paymentHash: invoice.paymentHash,
2256
+ previousStatus: invoice.status,
2257
+ currentStatus: "Cancelled",
2258
+ reason: "not_found"
2259
+ }
2260
+ });
2261
+ continue;
2262
+ }
2211
2263
  if (isExpectedTrackerError(error)) {
2212
2264
  continue;
2213
2265
  }
@@ -2277,6 +2329,21 @@ var PaymentTracker = class extends BaseMonitor {
2277
2329
  }
2278
2330
  }
2279
2331
  } catch (error) {
2332
+ if (isNotFoundError(error)) {
2333
+ this.store.updateTrackedPayment(payment.paymentHash, "Failed");
2334
+ await this.alerts.emit({
2335
+ type: "outgoing_payment_failed",
2336
+ priority: "high",
2337
+ source: this.name,
2338
+ data: {
2339
+ paymentHash: payment.paymentHash,
2340
+ previousStatus: payment.status,
2341
+ currentStatus: "Failed",
2342
+ reason: "not_found"
2343
+ }
2344
+ });
2345
+ continue;
2346
+ }
2280
2347
  if (isExpectedTrackerError(error)) {
2281
2348
  continue;
2282
2349
  }
@@ -2515,7 +2582,8 @@ function collectJsonRpcMethods(requestBody) {
2515
2582
  const methods = /* @__PURE__ */ new Map();
2516
2583
  for (const item of normalizeJsonRpcRequest(requestBody)) {
2517
2584
  if (item.id !== void 0 && typeof item.method === "string") {
2518
- methods.set(item.id, item.method);
2585
+ const dryRun = isDryRunRequest(item);
2586
+ methods.set(item.id, { method: item.method, dryRun });
2519
2587
  }
2520
2588
  }
2521
2589
  return methods;
@@ -2526,17 +2594,17 @@ function captureTrackedHashes(methodById, responseBody, handlers) {
2526
2594
  if (message.error || message.id === void 0) {
2527
2595
  continue;
2528
2596
  }
2529
- const method = methodById.get(message.id);
2530
- if (!method) {
2597
+ const meta = methodById.get(message.id);
2598
+ if (!meta) {
2531
2599
  continue;
2532
2600
  }
2533
- if (method === "new_invoice") {
2601
+ if (meta.method === "new_invoice") {
2534
2602
  const paymentHash = extractInvoicePaymentHash(message.result);
2535
2603
  if (paymentHash) {
2536
2604
  handlers.onInvoiceTracked(paymentHash);
2537
2605
  }
2538
2606
  }
2539
- if (method === "send_payment") {
2607
+ if (meta.method === "send_payment" && !meta.dryRun) {
2540
2608
  const paymentHash = extractPaymentHash(message.result);
2541
2609
  if (paymentHash) {
2542
2610
  handlers.onPaymentTracked(paymentHash);
@@ -2588,6 +2656,13 @@ function extractPaymentHash(result) {
2588
2656
  }
2589
2657
  return typeof result.payment_hash === "string" ? result.payment_hash : void 0;
2590
2658
  }
2659
+ function isDryRunRequest(message) {
2660
+ if (!Array.isArray(message.params) || message.params.length === 0) {
2661
+ return false;
2662
+ }
2663
+ const firstParam = message.params[0];
2664
+ return isObject(firstParam) && firstParam.dry_run === true;
2665
+ }
2591
2666
 
2592
2667
  // src/proxy/monitor-routes.ts
2593
2668
  function handleMonitorEndpoint(req, res, deps) {
@@ -3352,6 +3427,8 @@ var FiberMonitorService = class extends EventEmitter2 {
3352
3427
  this.alerts.onEmit((alert) => {
3353
3428
  this.emit("alert", alert);
3354
3429
  });
3430
+ const jobManager = this.jobManager;
3431
+ const jobStore = this.jobStore;
3355
3432
  this.proxy = new RpcMonitorProxy(
3356
3433
  {
3357
3434
  listen: this.config.proxy.listen,
@@ -3368,13 +3445,13 @@ var FiberMonitorService = class extends EventEmitter2 {
3368
3445
  listTrackedPayments: () => this.store.listTrackedPayments(),
3369
3446
  listAlerts: (filters) => this.store.listAlerts(filters),
3370
3447
  getStatus: () => this.getStatus(),
3371
- createPaymentJob: this.jobManager ? (params, options) => this.jobManager.ensurePayment(params, options) : void 0,
3372
- createInvoiceJob: this.jobManager ? (params, options) => this.jobManager.manageInvoice(params, options) : void 0,
3373
- createChannelJob: this.jobManager ? (params, options) => this.jobManager.manageChannel(params, options) : void 0,
3374
- getJob: this.jobManager ? (id) => this.jobManager.getJob(id) : void 0,
3375
- listJobs: this.jobManager ? (filter) => this.jobManager.listJobs(filter) : void 0,
3376
- cancelJob: this.jobManager ? (id) => this.jobManager.cancelJob(id) : void 0,
3377
- listJobEvents: this.jobStore ? (jobId) => this.jobStore.listJobEvents(jobId) : void 0
3448
+ createPaymentJob: jobManager ? (params, options) => jobManager.ensurePayment(params, options) : void 0,
3449
+ createInvoiceJob: jobManager ? (params, options) => jobManager.manageInvoice(params, options) : void 0,
3450
+ createChannelJob: jobManager ? (params, options) => jobManager.manageChannel(params, options) : void 0,
3451
+ getJob: jobManager ? (id) => jobManager.getJob(id) : void 0,
3452
+ listJobs: jobManager ? (filter) => jobManager.listJobs(filter) : void 0,
3453
+ cancelJob: jobManager ? (id) => jobManager.cancelJob(id) : void 0,
3454
+ listJobEvents: jobStore ? (jobId) => jobStore.listJobEvents(jobId) : void 0
3378
3455
  }
3379
3456
  );
3380
3457
  }
@@ -3449,6 +3526,9 @@ var FiberMonitorService = class extends EventEmitter2 {
3449
3526
  if (alertConfig.type === "file") {
3450
3527
  return new JsonlFileAlertBackend(alertConfig.path);
3451
3528
  }
3529
+ if (alertConfig.type === "daily-file") {
3530
+ return new DailyJsonlFileAlertBackend(alertConfig.baseLogsDir, alertConfig.filename);
3531
+ }
3452
3532
  const [host, portText] = alertConfig.listen.split(":");
3453
3533
  return new WebsocketAlertBackend({
3454
3534
  host,
@@ -3522,7 +3602,10 @@ var FiberMonitorService = class extends EventEmitter2 {
3522
3602
  idempotencyKey: channelJob.idempotencyKey,
3523
3603
  retryCount: channelJob.retryCount,
3524
3604
  action: channelJob.params.action,
3605
+ peerId: this.extractChannelPeerId(channelJob),
3606
+ temporaryChannelId: this.extractTemporaryChannelId(channelJob),
3525
3607
  channelId: this.extractChannelId(channelJob),
3608
+ fundingAmount: this.extractChannelFundingAmount(channelJob),
3526
3609
  error
3527
3610
  };
3528
3611
  }
@@ -3554,6 +3637,17 @@ var FiberMonitorService = class extends EventEmitter2 {
3554
3637
  job.result?.channelId ?? job.result?.acceptedChannelId ?? job.params.channelId ?? job.params.shutdownChannelParams?.channel_id
3555
3638
  );
3556
3639
  }
3640
+ extractChannelPeerId(job) {
3641
+ return job.params.peerId ?? job.params.openChannelParams?.peer_id;
3642
+ }
3643
+ extractTemporaryChannelId(job) {
3644
+ return this.normalizeHash(
3645
+ job.result?.temporaryChannelId ?? job.params.acceptChannelParams?.temporary_channel_id
3646
+ );
3647
+ }
3648
+ extractChannelFundingAmount(job) {
3649
+ return job.params.openChannelParams?.funding_amount;
3650
+ }
3557
3651
  normalizeHash(value) {
3558
3652
  if (!value || !value.startsWith("0x")) {
3559
3653
  return void 0;