@@ -62,6 +62,7 @@ private sealed interface DevCommand {
6262 fun parse (method : String , arg : String? ): DevCommand ? = when (method) {
6363 CreateInvoice .METHOD -> CreateInvoice .parse(arg)
6464 ProbeInvoice .METHOD -> ProbeInvoice .parse(arg)
65+ ProbeNode .METHOD -> ProbeNode .parse(arg)
6566 ProbeReadiness .METHOD -> ProbeReadiness
6667 else -> null
6768 }
@@ -126,6 +127,45 @@ private sealed interface DevCommand {
126127 }
127128 }
128129
130+ data class ProbeNode (val args : Args ) : DevCommand {
131+ companion object {
132+ const val METHOD = " probeNode"
133+ fun parse (arg : String? ) = ProbeNode (arg.deserialize<Args >())
134+ }
135+
136+ @Serializable
137+ data class Args (
138+ val targetName : String? = null ,
139+ val nodeId : String ,
140+ val amountMsat : ULong? = null ,
141+ val amountSats : ULong? = null ,
142+ val timeoutSeconds : Long = 90 ,
143+ )
144+
145+ override suspend fun execute (deps : DevToolsProvider .Dependencies ): DevResult {
146+ val amountSats = args.amountSats ? : args.amountMsat?.let { msatCeilOf(it) }
147+ ? : return DevResult .Error (" Probe node requires amountSats or amountMsat" )
148+ val timeout = args.timeoutSeconds.coerceAtLeast(1 ).seconds
149+
150+ Logger .info(
151+ " Sending keysend probe for target '${args.targetName ? : " unknown" } ' nodeId='${args.nodeId} ' amountSats='$amountSats '" ,
152+ context = TAG ,
153+ )
154+
155+ return deps.lightningRepo().sendProbeForNode(args.nodeId, amountSats)
156+ .fold(
157+ onSuccess = {
158+ deps.lightningRepo().waitForProbeOutcome(it.paymentIds, timeout)
159+ .fold(
160+ onSuccess = { outcome -> outcome.toDevResult(it.paymentIds) },
161+ onFailure = { error -> DevResult .ProbeFailure .from(error, it.paymentIds) },
162+ )
163+ },
164+ onFailure = { DevResult .ProbeFailure .from(it) },
165+ )
166+ }
167+ }
168+
129169 data object ProbeReadiness : DevCommand {
130170 const val METHOD = " probeReadiness"
131171
0 commit comments