@fiber-pay/runtime 0.1.0-rc.1

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 ADDED
@@ -0,0 +1,110 @@
1
+ # @fiber-pay/runtime
2
+
3
+ Runtime monitor + job orchestration for Fiber (`fnn v0.6.1`).
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ fiber-pay runtime start --daemon --json
9
+ fiber-pay runtime status --json
10
+ ```
11
+
12
+ ## Manual payment flow (runtime jobs)
13
+
14
+ 前置:`jq`、`curl`、两边 profile 已有资金(示例 `rt-a` / `rt-b`)。
15
+
16
+ ### 1) 启动两边节点(带 runtime proxy)
17
+
18
+ ```bash
19
+ fiber-pay --profile rt-a config init --network testnet --proxy-port 9729
20
+ fiber-pay --profile rt-b config init --network testnet --rpc-port 9827 --p2p-port 9828 --proxy-port 9829
21
+
22
+ fiber-pay --profile rt-a node start --json
23
+ fiber-pay --profile rt-b node start --json
24
+ ```
25
+
26
+ ### 2) 连接 peer
27
+
28
+ ```bash
29
+ A_MULTIADDR="$(fiber-pay --profile rt-a node status --json | jq -r '.data.multiaddr')"
30
+ B_MULTIADDR="$(fiber-pay --profile rt-b node status --json | jq -r '.data.multiaddr')"
31
+
32
+ fiber-pay --profile rt-a peer connect "$B_MULTIADDR" --timeout 12 --json
33
+ fiber-pay --profile rt-b peer connect "$A_MULTIADDR" --timeout 12 --json
34
+ ```
35
+
36
+ ### 3) 提交 job:open -> invoice -> payment -> shutdown
37
+
38
+ ```bash
39
+ PROXY_A='http://127.0.0.1:9729'
40
+ PROXY_B='http://127.0.0.1:9829'
41
+ PEER_B="$(fiber-pay --profile rt-b node status --json | jq -r '.data.peerId')"
42
+
43
+ wait_job() {
44
+ local proxy="$1"; local job_id="$2"
45
+ while true; do
46
+ local state="$(curl -fsS "$proxy/jobs/$job_id" | jq -r '.state')"
47
+ echo "job=$job_id state=$state"
48
+ case "$state" in succeeded|failed|cancelled) break;; esac
49
+ sleep 2
50
+ done
51
+ }
52
+
53
+ # open channel (200 CKB)
54
+ OPEN_JOB_ID="$(curl -fsS -X POST "$PROXY_A/jobs/channel" -H 'content-type: application/json' \
55
+ -d "{\"params\":{\"action\":\"open\",\"openChannelParams\":{\"peer_id\":\"$PEER_B\",\"funding_amount\":\"0x2e90edd000\"},\"waitForReady\":true,\"pollIntervalMs\":1500},\"options\":{\"idempotencyKey\":\"manual-open-$(date +%s)\"}}" | jq -r '.id')"
56
+ wait_job "$PROXY_A" "$OPEN_JOB_ID"
57
+ CHANNEL_ID="$(curl -fsS "$PROXY_A/jobs/$OPEN_JOB_ID" | jq -r '.result.channelId')"
58
+
59
+ # create invoice (5 CKB)
60
+ INVOICE_JOB_ID="$(curl -fsS -X POST "$PROXY_B/jobs/invoice" -H 'content-type: application/json' \
61
+ -d "{\"params\":{\"action\":\"create\",\"newInvoiceParams\":{\"amount\":\"0x1dcd6500\",\"currency\":\"Fibt\"},\"waitForTerminal\":false,\"pollIntervalMs\":1500},\"options\":{\"idempotencyKey\":\"manual-invoice-$(date +%s)\"}}" | jq -r '.id')"
62
+ wait_job "$PROXY_B" "$INVOICE_JOB_ID"
63
+ INVOICE_ADDRESS="$(curl -fsS "$PROXY_B/jobs/$INVOICE_JOB_ID" | jq -r '.result.invoiceAddress')"
64
+
65
+ # pay invoice
66
+ PAYMENT_JOB_ID="$(curl -fsS -X POST "$PROXY_A/jobs/payment" -H 'content-type: application/json' \
67
+ -d "{\"params\":{\"invoice\":\"$INVOICE_ADDRESS\",\"sendPaymentParams\":{\"invoice\":\"$INVOICE_ADDRESS\"}},\"options\":{\"idempotencyKey\":\"manual-pay-$(date +%s)\"}}" | jq -r '.id')"
68
+ wait_job "$PROXY_A" "$PAYMENT_JOB_ID"
69
+
70
+ # shutdown channel
71
+ SHUTDOWN_JOB_ID="$(curl -fsS -X POST "$PROXY_A/jobs/channel" -H 'content-type: application/json' \
72
+ -d "{\"params\":{\"action\":\"shutdown\",\"channelId\":\"$CHANNEL_ID\",\"shutdownChannelParams\":{\"channel_id\":\"$CHANNEL_ID\",\"force\":false},\"waitForClosed\":true,\"pollIntervalMs\":1500},\"options\":{\"idempotencyKey\":\"manual-close-$(date +%s)\"}}" | jq -r '.id')"
73
+ wait_job "$PROXY_A" "$SHUTDOWN_JOB_ID"
74
+
75
+ # inspect payment events
76
+ curl -fsS "$PROXY_A/jobs/$PAYMENT_JOB_ID/events" | jq
77
+ ```
78
+
79
+ ## One-command regression (recommended)
80
+
81
+ ```bash
82
+ pnpm e2e:runtime-jobs
83
+ JOB_TIMEOUT_SEC=420 CHANNEL_CLEANUP_TIMEOUT_SEC=120 pnpm e2e:runtime-jobs -- --json
84
+ ```
85
+
86
+ ## Handy job commands
87
+
88
+ ```bash
89
+ fiber-pay job list --json
90
+ fiber-pay job get <jobId> --json
91
+ fiber-pay job trace <jobId>
92
+ fiber-pay job events <jobId> --json
93
+ fiber-pay job events <jobId> --with-data
94
+ fiber-pay job cancel <jobId> --json
95
+ fiber-pay logs --source all --tail 80
96
+ fiber-pay logs --source runtime --tail 50
97
+ fiber-pay logs --source runtime --follow
98
+ ```
99
+
100
+ ## Persistent logs (for agent debugging)
101
+
102
+ When started from `fiber-pay node start` or `fiber-pay runtime start`, runtime/fnn logs are persisted to:
103
+
104
+ - `<data-dir>/logs/fnn.stdout.log`
105
+ - `<data-dir>/logs/fnn.stderr.log`
106
+ - `<data-dir>/logs/runtime.alerts.jsonl`
107
+
108
+ `<data-dir>/runtime.meta.json` stores these paths so agents can read files directly during troubleshooting.
109
+
110
+ You can view these files directly via CLI without `cat` using `fiber-pay logs` (alias: `fiber-pay log`).
@@ -0,0 +1,523 @@
1
+ import { ChannelId, Channel, PaymentHash, PeerInfo, OpenChannelParams, ShutdownChannelParams, AcceptChannelParams, AbandonChannelParams, UpdateChannelParams, OpenChannelResult, AcceptChannelResult, NewInvoiceParams, CancelInvoiceParams, SettleInvoiceParams, GetInvoiceResult, SendPaymentParams, FiberRpcClient } from '@fiber-pay/sdk';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ type AlertPriority = 'critical' | 'high' | 'medium' | 'low';
5
+ type AlertType = 'channel_state_changed' | 'new_inbound_channel_request' | 'channel_became_ready' | 'channel_closing' | 'incoming_payment_received' | 'invoice_expired' | 'invoice_cancelled' | 'outgoing_payment_completed' | 'outgoing_payment_failed' | 'channel_balance_changed' | 'new_pending_tlc' | 'peer_connected' | 'peer_disconnected' | 'node_offline' | 'node_online' | 'payment_job_started' | 'payment_job_retrying' | 'payment_job_succeeded' | 'payment_job_failed' | 'invoice_job_started' | 'invoice_job_retrying' | 'invoice_job_succeeded' | 'invoice_job_failed' | 'channel_job_started' | 'channel_job_retrying' | 'channel_job_succeeded' | 'channel_job_failed';
6
+ declare const alertTypeValues: AlertType[];
7
+ declare const alertPriorityOrder: Record<AlertPriority, number>;
8
+ interface AlertFilter {
9
+ limit?: number;
10
+ minPriority?: AlertPriority;
11
+ type?: AlertType;
12
+ source?: string;
13
+ }
14
+ declare function isAlertPriority(value: string): value is AlertPriority;
15
+ declare function isAlertType(value: string): value is AlertType;
16
+ interface Alert<T = unknown> {
17
+ id: string;
18
+ type: AlertType;
19
+ priority: AlertPriority;
20
+ timestamp: string;
21
+ source: string;
22
+ data: T;
23
+ }
24
+ interface AlertInput<T = unknown> {
25
+ type: AlertType;
26
+ priority: AlertPriority;
27
+ source: string;
28
+ data: T;
29
+ }
30
+ interface TrackedInvoiceState {
31
+ paymentHash: PaymentHash;
32
+ status: string;
33
+ trackedAt: number;
34
+ updatedAt: number;
35
+ completedAt?: number;
36
+ }
37
+ interface TrackedPaymentState {
38
+ paymentHash: PaymentHash;
39
+ status: string;
40
+ trackedAt: number;
41
+ updatedAt: number;
42
+ completedAt?: number;
43
+ }
44
+ interface ChannelBalanceChangedData {
45
+ channelId: ChannelId;
46
+ localBalanceBefore: string;
47
+ localBalanceAfter: string;
48
+ remoteBalanceBefore: string;
49
+ remoteBalanceAfter: string;
50
+ channel: Channel;
51
+ }
52
+ interface PendingTlcChangedData {
53
+ channelId: ChannelId;
54
+ newPendingTlcCount: number;
55
+ previousPendingTlcCount: number;
56
+ channel: Channel;
57
+ }
58
+ interface ChannelStateChangedData {
59
+ channelId: ChannelId;
60
+ previousState: string;
61
+ currentState: string;
62
+ channel: Channel;
63
+ }
64
+ interface PeerEventData {
65
+ peer: PeerInfo;
66
+ }
67
+ interface RpcHealthData {
68
+ message: string;
69
+ }
70
+ interface PaymentJobAlertData {
71
+ jobId: string;
72
+ idempotencyKey: string;
73
+ retryCount: number;
74
+ error?: string;
75
+ fee?: string;
76
+ }
77
+ interface InvoiceJobAlertData {
78
+ jobId: string;
79
+ idempotencyKey: string;
80
+ retryCount: number;
81
+ action?: string;
82
+ status?: string;
83
+ paymentHash?: `0x${string}`;
84
+ error?: string;
85
+ }
86
+ interface ChannelJobAlertData {
87
+ jobId: string;
88
+ idempotencyKey: string;
89
+ retryCount: number;
90
+ action?: string;
91
+ channelId?: `0x${string}`;
92
+ error?: string;
93
+ }
94
+ interface AlertBackend {
95
+ start?(): Promise<void>;
96
+ send(alert: Alert): Promise<void>;
97
+ stop?(): Promise<void>;
98
+ }
99
+
100
+ type JobType = 'payment' | 'invoice' | 'channel';
101
+ type JobState = 'queued' | 'executing' | 'inflight' | 'waiting_retry' | 'invoice_created' | 'invoice_active' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_awaiting_ready' | 'channel_ready' | 'channel_closing' | 'channel_closed' | 'succeeded' | 'failed' | 'cancelled';
102
+ declare const TERMINAL_JOB_STATES: Set<JobState>;
103
+ interface Job<TParams = unknown, TResult = unknown, TType extends JobType = JobType> {
104
+ id: string;
105
+ type: TType;
106
+ state: JobState;
107
+ params: TParams;
108
+ result?: TResult;
109
+ error?: ClassifiedError;
110
+ retryCount: number;
111
+ maxRetries: number;
112
+ nextRetryAt?: number;
113
+ /** For deduplication — derived from invoice payment_hash or caller-supplied */
114
+ idempotencyKey: string;
115
+ createdAt: number;
116
+ updatedAt: number;
117
+ completedAt?: number;
118
+ }
119
+ type JobEventType = 'created' | 'executing' | 'inflight' | 'invoice_created' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_awaiting_ready' | 'channel_ready' | 'channel_closing' | 'channel_closed' | 'retry_scheduled' | 'retrying' | 'succeeded' | 'failed' | 'cancelled';
120
+ interface JobEvent {
121
+ id: string;
122
+ jobId: string;
123
+ eventType: JobEventType;
124
+ fromState?: JobState;
125
+ toState?: JobState;
126
+ data?: Record<string, unknown>;
127
+ createdAt: number;
128
+ }
129
+ interface PaymentJobParams {
130
+ /** Raw invoice string (if paying by invoice) */
131
+ invoice?: string;
132
+ /** SDK send_payment params — merged from invoice or supplied directly */
133
+ sendPaymentParams: SendPaymentParams;
134
+ }
135
+ interface PaymentJobResult {
136
+ paymentHash: string;
137
+ /** Final Fiber payment status: Success | Failed */
138
+ status: string;
139
+ /** Fee in shannon (hex string from Fiber) */
140
+ fee: string;
141
+ failedError?: string;
142
+ }
143
+ type PaymentJob = Job<PaymentJobParams, PaymentJobResult, 'payment'>;
144
+ type InvoiceJobAction = 'create' | 'watch' | 'cancel' | 'settle';
145
+ interface InvoiceJobParams {
146
+ action: InvoiceJobAction;
147
+ newInvoiceParams?: NewInvoiceParams;
148
+ getInvoicePaymentHash?: `0x${string}`;
149
+ cancelInvoiceParams?: CancelInvoiceParams;
150
+ settleInvoiceParams?: SettleInvoiceParams;
151
+ /** Wait for lifecycle transitions to complete before succeeding */
152
+ waitForTerminal?: boolean;
153
+ /** Poll interval while waiting for invoice state changes */
154
+ pollIntervalMs?: number;
155
+ }
156
+ interface InvoiceJobResult {
157
+ paymentHash?: `0x${string}`;
158
+ invoiceAddress?: string;
159
+ status?: GetInvoiceResult['status'];
160
+ invoice?: GetInvoiceResult['invoice'];
161
+ }
162
+ type InvoiceJob = Job<InvoiceJobParams, InvoiceJobResult, 'invoice'>;
163
+ type ChannelJobAction = 'open' | 'shutdown' | 'accept' | 'abandon' | 'update';
164
+ interface ChannelJobParams {
165
+ action: ChannelJobAction;
166
+ openChannelParams?: OpenChannelParams;
167
+ shutdownChannelParams?: ShutdownChannelParams;
168
+ acceptChannelParams?: AcceptChannelParams;
169
+ abandonChannelParams?: AbandonChannelParams;
170
+ updateChannelParams?: UpdateChannelParams;
171
+ /** Optional peer filter for locating channel after open */
172
+ peerId?: string;
173
+ /** Optional channel id to wait for */
174
+ channelId?: `0x${string}`;
175
+ waitForReady?: boolean;
176
+ waitForClosed?: boolean;
177
+ pollIntervalMs?: number;
178
+ }
179
+ interface ChannelJobResult {
180
+ temporaryChannelId?: OpenChannelResult['temporary_channel_id'];
181
+ acceptedChannelId?: AcceptChannelResult['channel_id'];
182
+ channelId?: `0x${string}`;
183
+ state?: string;
184
+ }
185
+ type ChannelJob = Job<ChannelJobParams, ChannelJobResult, 'channel'>;
186
+ type RuntimeJob = PaymentJob | InvoiceJob | ChannelJob;
187
+ type ErrorCategory = 'no_route' | 'insufficient_balance' | 'invoice_expired' | 'invoice_cancelled' | 'peer_offline' | 'timeout' | 'temporary_failure' | 'amount_too_large' | 'invalid_payment' | 'unknown';
188
+ interface ClassifiedError {
189
+ category: ErrorCategory;
190
+ retryable: boolean;
191
+ message: string;
192
+ rawError?: string;
193
+ }
194
+ interface RetryPolicy {
195
+ maxRetries: number;
196
+ baseDelayMs: number;
197
+ maxDelayMs: number;
198
+ backoffMultiplier: number;
199
+ jitterMs: number;
200
+ }
201
+ interface JobFilter {
202
+ type?: JobType;
203
+ state?: JobState | JobState[];
204
+ limit?: number;
205
+ offset?: number;
206
+ }
207
+
208
+ interface StdoutAlertConfig {
209
+ type: 'stdout';
210
+ }
211
+ interface WebhookAlertConfig {
212
+ type: 'webhook';
213
+ url: string;
214
+ timeoutMs?: number;
215
+ headers?: Record<string, string>;
216
+ }
217
+ interface WebsocketAlertConfig {
218
+ type: 'websocket';
219
+ listen: string;
220
+ }
221
+ interface FileAlertConfig {
222
+ type: 'file';
223
+ path: string;
224
+ }
225
+ type AlertBackendConfig = StdoutAlertConfig | WebhookAlertConfig | WebsocketAlertConfig | FileAlertConfig;
226
+ interface RuntimeConfig {
227
+ fiberRpcUrl: string;
228
+ channelPollIntervalMs: number;
229
+ invoicePollIntervalMs: number;
230
+ paymentPollIntervalMs: number;
231
+ peerPollIntervalMs: number;
232
+ healthPollIntervalMs: number;
233
+ includeClosedChannels: boolean;
234
+ completedItemTtlSeconds: number;
235
+ requestTimeoutMs: number;
236
+ alerts: AlertBackendConfig[];
237
+ proxy: {
238
+ enabled: boolean;
239
+ listen: string;
240
+ };
241
+ storage: {
242
+ stateFilePath: string;
243
+ flushIntervalMs: number;
244
+ maxAlertHistory: number;
245
+ };
246
+ jobs: {
247
+ /** Enable the payment job execution engine. Default: false */
248
+ enabled: boolean;
249
+ /** Path to the SQLite file for job persistence. Default: derived from storage.stateFilePath */
250
+ dbPath: string;
251
+ maxConcurrentJobs: number;
252
+ schedulerIntervalMs: number;
253
+ retryPolicy: RetryPolicy;
254
+ };
255
+ }
256
+ type RuntimeConfigInput = Omit<Partial<RuntimeConfig>, 'proxy' | 'storage' | 'jobs'> & {
257
+ proxy?: Partial<RuntimeConfig['proxy']>;
258
+ storage?: Partial<RuntimeConfig['storage']>;
259
+ jobs?: Partial<RuntimeConfig['jobs']>;
260
+ };
261
+ declare const defaultRuntimeConfig: RuntimeConfig;
262
+ declare function createRuntimeConfig(input?: RuntimeConfigInput): RuntimeConfig;
263
+ declare function parseListenAddress(listen: string): {
264
+ host: string;
265
+ port: number;
266
+ };
267
+
268
+ declare class FiberMonitorService extends EventEmitter {
269
+ private readonly config;
270
+ private readonly startedAt;
271
+ private readonly client;
272
+ private readonly store;
273
+ private readonly alerts;
274
+ private readonly monitors;
275
+ private readonly proxy;
276
+ private readonly jobStore;
277
+ private readonly jobManager;
278
+ private running;
279
+ constructor(configInput?: RuntimeConfigInput);
280
+ start(): Promise<void>;
281
+ stop(): Promise<void>;
282
+ getStatus(): {
283
+ startedAt: string;
284
+ proxyListen: string;
285
+ targetUrl: string;
286
+ running: boolean;
287
+ };
288
+ listAlerts(filters?: AlertFilter): Alert[];
289
+ listTrackedInvoices(): TrackedInvoiceState[];
290
+ listTrackedPayments(): TrackedPaymentState[];
291
+ trackInvoice(paymentHash: `0x${string}`): void;
292
+ trackPayment(paymentHash: `0x${string}`): void;
293
+ private createAlertBackends;
294
+ private wireJobAlerts;
295
+ private emitJobAlert;
296
+ private toJobAlertType;
297
+ private toJobAlertData;
298
+ private trackJobArtifacts;
299
+ private extractPaymentHash;
300
+ private extractInvoiceHash;
301
+ private extractChannelId;
302
+ private normalizeHash;
303
+ }
304
+
305
+ interface RuntimeBootstrap {
306
+ service: FiberMonitorService;
307
+ stop: () => Promise<void>;
308
+ waitForShutdownSignal: () => Promise<NodeJS.Signals>;
309
+ }
310
+ declare function startRuntimeService(configInput?: RuntimeConfigInput): Promise<RuntimeBootstrap>;
311
+
312
+ interface RpcMonitorProxyConfig {
313
+ listen: string;
314
+ targetUrl: string;
315
+ }
316
+ interface RpcMonitorProxyStatus {
317
+ startedAt: string;
318
+ proxyListen: string;
319
+ targetUrl: string;
320
+ running: boolean;
321
+ }
322
+ interface RpcMonitorProxyDeps {
323
+ onInvoiceTracked: (paymentHash: string) => void;
324
+ onPaymentTracked: (paymentHash: string) => void;
325
+ listTrackedInvoices: () => TrackedInvoiceState[];
326
+ listTrackedPayments: () => TrackedPaymentState[];
327
+ listAlerts: (filters?: AlertFilter) => Alert[];
328
+ getStatus: () => RpcMonitorProxyStatus;
329
+ createPaymentJob?: (params: PaymentJobParams, options?: {
330
+ idempotencyKey?: string;
331
+ maxRetries?: number;
332
+ }) => Promise<RuntimeJob>;
333
+ createInvoiceJob?: (params: InvoiceJobParams, options?: {
334
+ idempotencyKey?: string;
335
+ maxRetries?: number;
336
+ }) => Promise<RuntimeJob>;
337
+ createChannelJob?: (params: ChannelJobParams, options?: {
338
+ idempotencyKey?: string;
339
+ maxRetries?: number;
340
+ reuseTerminal?: boolean;
341
+ }) => Promise<RuntimeJob>;
342
+ getJob?: (id: string) => RuntimeJob | undefined;
343
+ listJobs?: (filter?: JobFilter) => RuntimeJob[];
344
+ cancelJob?: (id: string) => void;
345
+ listJobEvents?: (jobId: string) => unknown[];
346
+ }
347
+
348
+ declare class RpcMonitorProxy {
349
+ private readonly config;
350
+ private readonly deps;
351
+ private server;
352
+ constructor(config: RpcMonitorProxyConfig, deps: RpcMonitorProxyDeps);
353
+ start(): Promise<void>;
354
+ stop(): Promise<void>;
355
+ private handleRequest;
356
+ }
357
+
358
+ interface Store {
359
+ load(): Promise<void>;
360
+ flush(): Promise<void>;
361
+ startAutoFlush(): void;
362
+ stopAutoFlush(): void;
363
+ pruneCompleted(ttlMs: number): void;
364
+ getChannelSnapshot(): Channel[];
365
+ setChannelSnapshot(channels: Channel[]): void;
366
+ getPeerSnapshot(): PeerInfo[];
367
+ setPeerSnapshot(peers: PeerInfo[]): void;
368
+ addTrackedInvoice(hash: PaymentHash, status?: string): void;
369
+ listTrackedInvoices(): TrackedInvoiceState[];
370
+ getTrackedInvoice(hash: PaymentHash): TrackedInvoiceState | undefined;
371
+ updateTrackedInvoice(hash: PaymentHash, status: string): void;
372
+ addTrackedPayment(hash: PaymentHash, status?: string): void;
373
+ listTrackedPayments(): TrackedPaymentState[];
374
+ getTrackedPayment(hash: PaymentHash): TrackedPaymentState | undefined;
375
+ updateTrackedPayment(hash: PaymentHash, status: string): void;
376
+ addAlert(alert: Alert): void;
377
+ listAlerts(filters?: AlertFilter): Alert[];
378
+ }
379
+
380
+ interface MemoryStoreConfig {
381
+ stateFilePath: string;
382
+ flushIntervalMs: number;
383
+ maxAlertHistory: number;
384
+ }
385
+ declare class MemoryStore implements Store {
386
+ private readonly config;
387
+ private channelSnapshot;
388
+ private peerSnapshot;
389
+ private trackedInvoices;
390
+ private trackedPayments;
391
+ private alerts;
392
+ private flushTimer;
393
+ constructor(config: MemoryStoreConfig);
394
+ load(): Promise<void>;
395
+ flush(): Promise<void>;
396
+ startAutoFlush(): void;
397
+ stopAutoFlush(): void;
398
+ pruneCompleted(ttlMs: number): void;
399
+ getChannelSnapshot(): Channel[];
400
+ setChannelSnapshot(channels: Channel[]): void;
401
+ getPeerSnapshot(): PeerInfo[];
402
+ setPeerSnapshot(peers: PeerInfo[]): void;
403
+ addTrackedInvoice(hash: PaymentHash, status?: string): void;
404
+ listTrackedInvoices(): TrackedInvoiceState[];
405
+ getTrackedInvoice(hash: PaymentHash): TrackedInvoiceState | undefined;
406
+ updateTrackedInvoice(hash: PaymentHash, status: string): void;
407
+ addTrackedPayment(hash: PaymentHash, status?: string): void;
408
+ listTrackedPayments(): TrackedPaymentState[];
409
+ getTrackedPayment(hash: PaymentHash): TrackedPaymentState | undefined;
410
+ updateTrackedPayment(hash: PaymentHash, status: string): void;
411
+ addAlert(alert: Alert): void;
412
+ listAlerts(filters?: AlertFilter): Alert[];
413
+ }
414
+
415
+ declare class SqliteJobStore {
416
+ private readonly db;
417
+ constructor(dbPath: string);
418
+ private runMigrations;
419
+ createJob<P, R>(job: Omit<Job<P, R>, 'id' | 'createdAt' | 'updatedAt'>): Job<P, R>;
420
+ updateJob<P, R>(id: string, updates: Partial<Job<P, R>>): Job<P, R>;
421
+ getJob<P = unknown, R = unknown>(id: string): Job<P, R> | undefined;
422
+ getJobByIdempotencyKey<P = unknown, R = unknown>(key: string): Job<P, R> | undefined;
423
+ listJobs<P = unknown, R = unknown>(filter?: JobFilter): Job<P, R>[];
424
+ deleteJob(id: string): void;
425
+ /** Return jobs that are ready to be retried right now. */
426
+ getRetryableJobs<P = unknown, R = unknown>(now?: number): Job<P, R>[];
427
+ /** Return jobs in non-terminal states (for recovery after daemon restart). */
428
+ getInProgressJobs<P = unknown, R = unknown>(): Job<P, R>[];
429
+ addJobEvent(jobId: string, eventType: JobEventType, fromState?: JobState, toState?: JobState, data?: Record<string, unknown>): JobEvent;
430
+ listJobEvents(jobId: string): JobEvent[];
431
+ close(): void;
432
+ private rowToJob;
433
+ }
434
+
435
+ interface JobManagerEvents {
436
+ 'job:created': [job: RuntimeJob];
437
+ 'job:state_changed': [job: RuntimeJob, from: JobState];
438
+ 'job:succeeded': [job: RuntimeJob];
439
+ 'job:failed': [job: RuntimeJob];
440
+ 'job:cancelled': [job: RuntimeJob];
441
+ }
442
+ interface JobManagerConfig {
443
+ schedulerIntervalMs?: number;
444
+ maxConcurrentJobs?: number;
445
+ retryPolicy?: RetryPolicy;
446
+ }
447
+ declare class JobManager extends EventEmitter<JobManagerEvents> {
448
+ private readonly rpc;
449
+ private readonly store;
450
+ private readonly retryPolicy;
451
+ private readonly schedulerIntervalMs;
452
+ private readonly maxConcurrentJobs;
453
+ private running;
454
+ private schedulerTimer;
455
+ private readonly active;
456
+ constructor(rpc: FiberRpcClient, store: SqliteJobStore, config?: JobManagerConfig);
457
+ ensurePayment(params: PaymentJobParams, options?: {
458
+ idempotencyKey?: string;
459
+ maxRetries?: number;
460
+ }): Promise<PaymentJob>;
461
+ manageInvoice(params: InvoiceJobParams, options?: {
462
+ idempotencyKey?: string;
463
+ maxRetries?: number;
464
+ reuseTerminal?: boolean;
465
+ }): Promise<InvoiceJob>;
466
+ manageChannel(params: ChannelJobParams, options?: {
467
+ idempotencyKey?: string;
468
+ maxRetries?: number;
469
+ reuseTerminal?: boolean;
470
+ }): Promise<ChannelJob>;
471
+ getJob(id: string): RuntimeJob | undefined;
472
+ listJobs(filter?: JobFilter): RuntimeJob[];
473
+ cancelJob(id: string): void;
474
+ start(): void;
475
+ stop(): Promise<void>;
476
+ private createOrReuseJob;
477
+ private tick;
478
+ private schedule;
479
+ private recover;
480
+ private execute;
481
+ private buildEventData;
482
+ }
483
+
484
+ type MachineEvent = 'send_issued' | 'payment_inflight' | 'payment_success' | 'payment_failed_retryable' | 'payment_failed_permanent' | 'invoice_created' | 'invoice_received' | 'invoice_settled' | 'invoice_expired' | 'invoice_cancelled' | 'channel_opening' | 'channel_accepting' | 'channel_abandoning' | 'channel_updating' | 'channel_closing' | 'channel_ready' | 'channel_closed' | 'channel_failed' | 'retry_delay_elapsed' | 'cancel';
485
+ interface Transition {
486
+ from: JobState | JobState[];
487
+ event: MachineEvent;
488
+ to: JobState;
489
+ }
490
+ declare class JobStateMachine {
491
+ private readonly table;
492
+ constructor(transitions: Transition[]);
493
+ transition(current: JobState, event: MachineEvent): JobState | null;
494
+ isTerminal(state: JobState): boolean;
495
+ }
496
+ declare const paymentStateMachine: JobStateMachine;
497
+
498
+ /**
499
+ * Classify an RPC / job failure into a structured error.
500
+ *
501
+ * `failedError` is the raw `failed_error` string from Fiber's `get_payment` response.
502
+ * `error` is any caught JS exception.
503
+ */
504
+ declare function classifyRpcError(error: unknown, failedError?: string): ClassifiedError;
505
+
506
+ declare const defaultPaymentRetryPolicy: RetryPolicy;
507
+ /**
508
+ * Decide whether a job should be retried given its error and current retry count.
509
+ */
510
+ declare function shouldRetry(error: ClassifiedError, retryCount: number, policy: RetryPolicy): boolean;
511
+ /**
512
+ * Compute the next retry delay using exponential backoff with random jitter.
513
+ * retryCount is the number of retries already attempted (0-based before this retry).
514
+ */
515
+ declare function computeRetryDelay(retryCount: number, policy: RetryPolicy): number;
516
+
517
+ interface FormatRuntimeAlertOptions {
518
+ color?: 'auto' | 'always' | 'never';
519
+ prefix?: string;
520
+ }
521
+ declare function formatRuntimeAlert(alert: Alert, options?: FormatRuntimeAlertOptions): string;
522
+
523
+ 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 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 };