@@ -4,7 +4,7 @@ import path from 'node:path';
44
55import { getAppId } from './constants' ;
66
7- export type ProbeTargetType = 'lightningAddress' | 'lnurlCallback' ;
7+ export type ProbeTargetType = 'lightningAddress' | 'lnurlCallback' | 'nodeId' ;
88
99export type ProbeTarget = {
1010 name : string ;
@@ -14,11 +14,13 @@ export type ProbeTarget = {
1414 amountsMsat ?: number [ ] ;
1515 address ?: string ;
1616 url ?: string ;
17+ nodeId ?: string ;
1718} ;
1819
1920export type ProbeResult = {
2021 targetName : string ;
2122 targetType : ProbeTargetType ;
23+ probeMode : 'invoice' | 'keysend' ;
2224 amountMsat : number ;
2325 amountSats : number ;
2426 required : boolean ;
@@ -28,6 +30,7 @@ export type ProbeResult = {
2830 success : boolean ;
2931 durationMs : number ;
3032 bolt11 ?: string ;
33+ nodeId ?: string ;
3134 rawProviderResult ?: string ;
3235 error ?: string ;
3336} ;
@@ -129,9 +132,17 @@ export async function fetchBolt11ForProbe(
129132 return response . pr ;
130133}
131134
132- export function runProbeCommand ( target : ProbeTarget , amountMsat : number , bolt11 : string ) : string {
135+ export function probeModeForTargetType ( type : ProbeTargetType ) : 'invoice' | 'keysend' {
136+ return type === 'nodeId' ? 'keysend' : 'invoice' ;
137+ }
138+
139+ export function runProbeInvoiceCommand (
140+ target : ProbeTarget ,
141+ amountMsat : number ,
142+ bolt11 : string
143+ ) : string {
133144 const amountSats = amountMsat / 1000 ;
134- const method = process . env . PROBE_CONTENT_METHOD ?? 'probeInvoice' ;
145+ const method = process . env . PROBE_INVOICE_METHOD ?? 'probeInvoice' ;
135146 const timeoutSeconds =
136147 parsePositiveIntEnv ( 'PROBE_TIMEOUT_SECONDS' ) ?? DEFAULT_PROBE_TIMEOUT_SECONDS ;
137148 const payload = {
@@ -141,21 +152,29 @@ export function runProbeCommand(target: ProbeTarget, amountMsat: number, bolt11:
141152 amountSats,
142153 timeoutSeconds,
143154 } ;
144- const command = [
145- 'content' ,
146- 'call' ,
147- '--uri' ,
148- shellQuote ( `content://${ getAppId ( ) } .devtools` ) ,
149- '--method' ,
150- shellQuote ( method ) ,
151- '--arg' ,
152- shellQuote ( JSON . stringify ( payload ) ) ,
153- ] . join ( ' ' ) ;
154155
155- return execFileSync ( 'adb' , [ 'shell' , command ] , {
156- encoding : 'utf8' ,
157- timeout : ( timeoutSeconds + 10 ) * 1000 ,
158- } ) ;
156+ return runDevToolsCommand ( method , payload , timeoutSeconds ) ;
157+ }
158+
159+ export function runProbeNodeCommand ( target : ProbeTarget , amountMsat : number ) : string {
160+ const nodeId = target . nodeId ;
161+ if ( ! nodeId ) {
162+ throw new Error ( `Probe target '${ target . name } ' is missing nodeId` ) ;
163+ }
164+
165+ const amountSats = amountMsat / 1000 ;
166+ const method = process . env . PROBE_NODE_METHOD ?? 'probeNode' ;
167+ const timeoutSeconds =
168+ parsePositiveIntEnv ( 'PROBE_TIMEOUT_SECONDS' ) ?? DEFAULT_PROBE_TIMEOUT_SECONDS ;
169+ const payload = {
170+ targetName : target . name ,
171+ nodeId,
172+ amountMsat,
173+ amountSats,
174+ timeoutSeconds,
175+ } ;
176+
177+ return runDevToolsCommand ( method , payload , timeoutSeconds ) ;
159178}
160179
161180export function parseProbeCommandSuccess ( raw : string ) : boolean {
@@ -356,17 +375,18 @@ export function renderProbeReport(
356375 `Required failures: ${ failedRequired . length } ` ,
357376 `Readiness at probe start: ${ readiness ? summarizeProbeReadiness ( readiness ) : 'not captured' } ` ,
358377 '' ,
359- '| Target | Amount sats | Required | Invoice | Probe | Retries | Duration ms | Failure |' ,
360- '| --- | ---: | --- | --- | --- | ---: | ---: | --- |' ,
378+ '| Target | Type | Amount sats | Required | Fetch | Probe | Retries | Duration ms | Failure |' ,
379+ '| --- | --- | --- : | --- | --- | --- | ---: | ---: | --- |' ,
361380 ] ;
362381
363382 for ( const result of results ) {
364383 lines . push (
365384 `| ${ [
366385 result . targetName ,
386+ result . probeMode ,
367387 result . amountSats . toString ( ) ,
368388 result . required ? 'yes' : 'no' ,
369- result . invoiceFetched ? 'ok' : 'failed' ,
389+ formatFetchCell ( result ) ,
370390 result . success ? '✅' : '❌' ,
371391 result . retries . toString ( ) ,
372392 result . durationMs . toString ( ) ,
@@ -387,15 +407,22 @@ function parseProbeTarget(value: unknown): ProbeTarget {
387407 if ( ! target . name || typeof target . name !== 'string' ) {
388408 throw new Error ( 'Each probe target must define a string name' ) ;
389409 }
390- if ( target . type !== 'lightningAddress' && target . type !== 'lnurlCallback' ) {
391- throw new Error ( `Probe target '${ target . name } ' has unsupported type '${ target . type } '` ) ;
410+ if (
411+ target . type !== 'lightningAddress' &&
412+ target . type !== 'lnurlCallback' &&
413+ target . type !== 'nodeId'
414+ ) {
415+ throw new Error ( `Probe target '${ target . name } ' has unsupported type '${ String ( target . type ) } '` ) ;
392416 }
393417 if ( target . type === 'lightningAddress' && ! target . address ) {
394418 throw new Error ( `Probe target '${ target . name } ' must define address` ) ;
395419 }
396420 if ( target . type === 'lnurlCallback' && ! target . url ) {
397421 throw new Error ( `Probe target '${ target . name } ' must define url` ) ;
398422 }
423+ if ( target . type === 'nodeId' && ! target . nodeId ) {
424+ throw new Error ( `Probe target '${ target . name } ' must define nodeId` ) ;
425+ }
399426
400427 return {
401428 name : target . name ,
@@ -405,6 +432,7 @@ function parseProbeTarget(value: unknown): ProbeTarget {
405432 amountsMsat : target . amountsMsat ,
406433 address : target . address ,
407434 url : target . url ,
435+ nodeId : target . nodeId ,
408436 } ;
409437}
410438
@@ -518,6 +546,33 @@ function formatFailureCell(error: string): string {
518546 return `\`${ sanitized } \`` ;
519547}
520548
549+ function formatFetchCell ( result : ProbeResult ) : string {
550+ if ( result . probeMode === 'keysend' ) return 'n/a' ;
551+ return result . invoiceFetched ? 'ok' : 'failed' ;
552+ }
553+
554+ function runDevToolsCommand (
555+ method : string ,
556+ payload : Record < string , unknown > ,
557+ timeoutSeconds : number
558+ ) : string {
559+ const command = [
560+ 'content' ,
561+ 'call' ,
562+ '--uri' ,
563+ shellQuote ( `content://${ getAppId ( ) } .devtools` ) ,
564+ '--method' ,
565+ shellQuote ( method ) ,
566+ '--arg' ,
567+ shellQuote ( JSON . stringify ( payload ) ) ,
568+ ] . join ( ' ' ) ;
569+
570+ return execFileSync ( 'adb' , [ 'shell' , command ] , {
571+ encoding : 'utf8' ,
572+ timeout : ( timeoutSeconds + 10 ) * 1000 ,
573+ } ) ;
574+ }
575+
521576function shellQuote ( value : string ) : string {
522577 return `'${ value . replace ( / ' / g, "'\\''" ) } '` ;
523578}
0 commit comments