@paybox-sh/sdk 2.0.0 → 2.2.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.
@@ -156,7 +156,7 @@ async function loginDevice(baseUrl, opts = {}) {
156
156
  Code: ${da.user_code}
157
157
  `
158
158
  );
159
- openBrowser(da.verification_uri_complete);
159
+ if (opts.open !== false) openBrowser(da.verification_uri_complete);
160
160
  const timeoutMs = opts.timeoutMs ?? da.expires_in * 1e3;
161
161
  const deadline = Date.now() + timeoutMs;
162
162
  let intervalSec = da.interval && da.interval > 0 ? da.interval : 5;
@@ -251,6 +251,81 @@ function saveConfig(config) {
251
251
  if (process.platform !== "win32") chmodSync(configFile(), 384);
252
252
  }
253
253
 
254
+ // src/next-action.ts
255
+ function nextActionFor(response, capabilities) {
256
+ switch (response.status) {
257
+ case "pending_signature":
258
+ if (capabilities.canSignInProcess) return { kind: "sign", reason: "signer_ready" };
259
+ if (capabilities.canRenderSigningUi) {
260
+ return { kind: "await_human", reason: "signing_ui_required" };
261
+ }
262
+ return { kind: "stop", reason: "signer_unavailable" };
263
+ case "pending_approval":
264
+ if (capabilities.canOpenApprovalUi) {
265
+ return { kind: "await_human", reason: "approval_required" };
266
+ }
267
+ return { kind: "stop", reason: "approval_ui_unavailable" };
268
+ case "pending_confirmation":
269
+ case "pending_settlement":
270
+ case "pending_funding":
271
+ return capabilities.canPoll ? { kind: "poll", reason: response.status } : { kind: "stop", reason: "polling_unavailable" };
272
+ case "success":
273
+ return { kind: "stop", reason: "completed" };
274
+ case "denied":
275
+ case "error":
276
+ return response.retryable && capabilities.canReconcile ? { kind: "reconcile", reason: "retryable_terminal" } : { kind: "stop", reason: response.status };
277
+ }
278
+ }
279
+
280
+ // src/pending-funding.ts
281
+ function record(value) {
282
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
283
+ }
284
+ function pendingFundingResponse(value) {
285
+ const data = record(value);
286
+ const response = data?.status === "pending_funding" ? data : record(data?.response);
287
+ if (response?.status !== "pending_funding") {
288
+ return null;
289
+ }
290
+ return {
291
+ request_id: typeof response.request_id === "string" ? response.request_id : "",
292
+ status: "pending_funding",
293
+ output: null,
294
+ approval_id: null,
295
+ error: null
296
+ };
297
+ }
298
+ function nonSigningFundingResult(value) {
299
+ const response = pendingFundingResponse(value);
300
+ if (!response) return value;
301
+ return record(value)?.status === "pending_funding" ? response : { response, plan: null };
302
+ }
303
+ function pendingFundingToolResponse(result) {
304
+ const structured = pendingFundingResponse(result.structuredContent);
305
+ if (structured) return structured;
306
+ for (const item of result.content) {
307
+ if (item.type !== "text" || typeof item.text !== "string") continue;
308
+ let value;
309
+ try {
310
+ value = JSON.parse(item.text);
311
+ } catch {
312
+ continue;
313
+ }
314
+ const response = pendingFundingResponse(value);
315
+ if (response) return response;
316
+ }
317
+ return null;
318
+ }
319
+ function nonSigningFundingToolResult(result) {
320
+ const response = pendingFundingToolResponse(result);
321
+ if (!response) return result;
322
+ return {
323
+ content: [{ type: "text", text: JSON.stringify(response) }],
324
+ structuredContent: response,
325
+ isError: false
326
+ };
327
+ }
328
+
254
329
  // node_modules/.pnpm/@paybox+mcp-app@file+..+mcp-app_@modelcontextprotocol+sdk@1.29.0_zod@4.4.3__bn.js@5.2.3_ac1e09a20c76a1143611be5dd3d4f9db/node_modules/@paybox/mcp-app/src/signing/creds.ts
255
330
  var TOKEN_PREFIX = "pbxk1.";
256
331
  function validateHex(s, label) {
@@ -404,8 +479,8 @@ function buildSwapEnvelopes(txs, binding, creds) {
404
479
  );
405
480
  }
406
481
  return txs.map((t) => {
407
- const payload = t.vm === "solana" ? solanaSigningPayload(t.transaction) : eip1559SigningHash(t.transaction);
408
- const { signed_body, agent_signature } = buildEnvelope(payload, binding, creds);
482
+ const payload2 = t.vm === "solana" ? solanaSigningPayload(t.transaction) : eip1559SigningHash(t.transaction);
483
+ const { signed_body, agent_signature } = buildEnvelope(payload2, binding, creds);
409
484
  return { api_pub: creds.apiPubHex, signed_body, agent_signature };
410
485
  });
411
486
  }
@@ -563,42 +638,43 @@ function credsFromToken(token) {
563
638
  function toBinding(b) {
564
639
  return { walletId: b.wallet_id, keyId: b.key_id ?? "", derivationPath: b.derivation_path ?? "" };
565
640
  }
566
- async function resolveBinding(post, requestId, creds) {
641
+ async function resolveBinding(post, requestId2, creds) {
567
642
  const env = buildIssuedAtEnvelope(creds);
568
- const r = await post(`/agent/requests/${requestId}/binding`, {
643
+ const r = await post(`/agent/requests/${requestId2}/binding`, {
569
644
  api_pub: creds.apiPubHex,
570
645
  signed_body: env.signed_body,
571
646
  agent_signature: env.agent_signature
572
647
  });
573
648
  return toBinding(r);
574
649
  }
575
- async function signWalletRequest(post, requestId, intent, creds, binding) {
576
- const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post, requestId, creds);
577
- const transport = (envelope) => post(`/agent/requests/${requestId}/moonx-sign`, {
650
+ async function signWalletRequest(post, requestId2, intent, creds, binding) {
651
+ const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post, requestId2, creds);
652
+ const transport = (envelope) => post(`/agent/requests/${requestId2}/moonx-sign`, {
578
653
  api_pub: creds.apiPubHex,
579
654
  ...envelope
580
655
  });
581
656
  const result = await signIntent(intent, creds, resolved, transport);
582
657
  if (!result.ok) throw new Error(result.error);
583
- await post(`/agent/requests/${requestId}/signature`, { artifact: result.artifact });
658
+ await post(`/agent/requests/${requestId2}/signature`, { artifact: result.artifact });
584
659
  return result.artifact;
585
660
  }
586
- function isSolanaRefreshablePlan(plan) {
661
+ function isRefreshablePlan(plan) {
662
+ if (typeof plan.refresh_before_sign === "boolean") return plan.refresh_before_sign;
587
663
  const txs = plan.txs;
588
664
  const solana = txs.some((t) => t.vm === "solana");
589
665
  return solana && txs.some(
590
- (t) => t.kind === "swap" || t.kind === "prediction_trade" || t.kind === "jupiter_swap"
666
+ (t) => t.kind === "swap" || t.kind === "prediction_trade"
591
667
  );
592
668
  }
593
- async function refreshPlan(post, requestId) {
594
- const r = await post(`/agent/requests/${requestId}/refresh`, {});
669
+ async function refreshPlan(post, requestId2) {
670
+ const r = await post(`/agent/requests/${requestId2}/refresh`, {});
595
671
  if (!r || typeof r.plan !== "object" || r.plan === null) {
596
672
  throw new Error("refresh returned no plan");
597
673
  }
598
674
  return r.plan;
599
675
  }
600
- async function refreshSwapPlan(post, requestId) {
601
- const refreshed = await refreshPlan(post, requestId);
676
+ async function refreshSwapPlan(post, requestId2) {
677
+ const refreshed = await refreshPlan(post, requestId2);
602
678
  if (!Array.isArray(refreshed.txs) || refreshed.txs.length === 0) {
603
679
  throw new Error("swap refresh returned a plan with no txs");
604
680
  }
@@ -607,42 +683,43 @@ async function refreshSwapPlan(post, requestId) {
607
683
  function isSolanaRailX402(plan) {
608
684
  return plan.x402?.rail === "solana";
609
685
  }
610
- async function refreshX402SolanaTx(post, requestId) {
611
- const refreshed = await refreshPlan(post, requestId);
686
+ async function refreshX402SolanaTx(post, requestId2) {
687
+ const refreshed = await refreshPlan(post, requestId2);
612
688
  const tx = refreshed.solana?.transaction;
613
689
  if (typeof tx !== "string" || tx.length === 0) {
614
690
  throw new Error("x402 refresh returned a plan with no Solana transaction");
615
691
  }
616
692
  return tx;
617
693
  }
618
- async function signSwapRequest(post, requestId, plan, creds) {
619
- const signPlan = isSolanaRefreshablePlan(plan) ? await refreshSwapPlan(post, requestId) : plan;
694
+ async function signSwapRequest(post, requestId2, plan, creds, validate) {
695
+ const signPlan = isRefreshablePlan(plan) ? await refreshSwapPlan(post, requestId2) : plan;
696
+ validate?.(signPlan);
620
697
  let binding = {
621
698
  walletId: signPlan.binding.wallet_id,
622
699
  keyId: signPlan.binding.key_id ?? "",
623
700
  derivationPath: signPlan.binding.derivation_path ?? ""
624
701
  };
625
702
  if (binding.walletId && (!binding.keyId || !binding.derivationPath)) {
626
- binding = await resolveBinding(post, requestId, creds);
703
+ binding = await resolveBinding(post, requestId2, creds);
627
704
  }
628
705
  const envelopes = buildSwapEnvelopes(signPlan.txs, binding, creds);
629
- await post(`/agent/requests/${requestId}/envelopes`, { envelopes });
706
+ await post(`/agent/requests/${requestId2}/envelopes`, { envelopes });
630
707
  }
631
- async function signX402Request(post, requestId, plan, creds) {
708
+ async function signX402Request(post, requestId2, plan, creds) {
632
709
  let binding = {
633
710
  walletId: plan.binding.wallet_id,
634
711
  keyId: plan.binding.key_id ?? "",
635
712
  derivationPath: plan.binding.derivation_path ?? ""
636
713
  };
637
714
  if (binding.walletId && (!binding.keyId || !binding.derivationPath)) {
638
- binding = await resolveBinding(post, requestId, creds);
715
+ binding = await resolveBinding(post, requestId2, creds);
639
716
  }
640
- const envelope = isSolanaRailX402(plan) ? buildX402SolanaEnvelope(await refreshX402SolanaTx(post, requestId), binding, creds) : buildX402Envelope(
717
+ const envelope = isSolanaRailX402(plan) ? buildX402SolanaEnvelope(await refreshX402SolanaTx(post, requestId2), binding, creds) : buildX402Envelope(
641
718
  plan.typed_data,
642
719
  binding,
643
720
  creds
644
721
  );
645
- await post(`/agent/requests/${requestId}/envelopes`, { envelopes: [envelope] });
722
+ await post(`/agent/requests/${requestId2}/envelopes`, { envelopes: [envelope] });
646
723
  }
647
724
 
648
725
  // src/mcp.ts
@@ -677,16 +754,16 @@ var McpTransport = class {
677
754
  this.bearer = options.bearer;
678
755
  this.fetchImpl = options.fetchImpl;
679
756
  }
680
- async callTool(name, args) {
681
- await this.initialize();
682
- return this.rpc("tools/call", { name, arguments: args });
757
+ async callTool(name, args, signingSurface) {
758
+ await this.initialize(signingSurface);
759
+ return this.rpc("tools/call", { name, arguments: args }, signingSurface);
683
760
  }
684
- async initialize() {
761
+ async initialize(signingSurface) {
685
762
  this.initialization ??= this.rpc("initialize", {
686
763
  protocolVersion: PROTOCOL_VERSION,
687
764
  capabilities: {},
688
765
  clientInfo: { name: "paybox-sdk", version: "1" }
689
- }).then(() => this.notify("notifications/initialized", {}));
766
+ }, signingSurface).then(() => this.notify("notifications/initialized", {}, signingSurface));
690
767
  try {
691
768
  await this.initialization;
692
769
  } catch (error) {
@@ -694,16 +771,17 @@ var McpTransport = class {
694
771
  throw error;
695
772
  }
696
773
  }
697
- async post(payload, method) {
774
+ async post(payload2, method, signingSurface) {
698
775
  const response = await this.fetchImpl(this.endpoint, {
699
776
  method: "POST",
700
777
  headers: {
701
778
  accept: "application/json, text/event-stream",
702
779
  authorization: `Bearer ${await this.bearer()}`,
703
780
  "content-type": "application/json",
704
- "mcp-protocol-version": PROTOCOL_VERSION
781
+ "mcp-protocol-version": PROTOCOL_VERSION,
782
+ ...signingSurface ? { "x-paybox-signing-surface": signingSurface } : {}
705
783
  },
706
- body: JSON.stringify(payload)
784
+ body: JSON.stringify(payload2)
707
785
  });
708
786
  const text = await response.text();
709
787
  if (!response.ok) {
@@ -713,10 +791,10 @@ var McpTransport = class {
713
791
  }
714
792
  return text;
715
793
  }
716
- async notify(method, params) {
717
- await this.post({ jsonrpc: "2.0", method, params }, method);
794
+ async notify(method, params, signingSurface) {
795
+ await this.post({ jsonrpc: "2.0", method, params }, method, signingSurface);
718
796
  }
719
- async rpc(method, params) {
797
+ async rpc(method, params, signingSurface) {
720
798
  const text = await this.post(
721
799
  {
722
800
  jsonrpc: "2.0",
@@ -724,7 +802,8 @@ var McpTransport = class {
724
802
  method,
725
803
  params
726
804
  },
727
- method
805
+ method,
806
+ signingSurface
728
807
  );
729
808
  const parsed = JSON.parse(responseBody(text));
730
809
  if (parsed.error) {
@@ -739,10 +818,226 @@ var McpTransport = class {
739
818
  }
740
819
  };
741
820
 
821
+ // src/execution.ts
822
+ import { setTimeout as delay } from "node:timers/promises";
823
+
824
+ // src/plugins/signing.ts
825
+ function supportedSigningAction(value) {
826
+ if (value.operation != null) {
827
+ const operation = value.operation;
828
+ const intent = operation.intent;
829
+ if (operation.type !== "wallet_sign" || value.next_action?.kind !== "sign" || !intent || !["transaction", "solanaTransaction"].includes(intent.op)) return null;
830
+ try {
831
+ if (intent.op === "transaction") eip1559SigningHash(intent.transaction);
832
+ else if (intent.op === "solanaTransaction" && typeof intent.address === "string" && intent.address) deserializeSolanaTx(intent.transactionBase64);
833
+ else return null;
834
+ } catch {
835
+ return null;
836
+ }
837
+ const binding = operation.wallet_id == null ? null : {
838
+ wallet_id: operation.wallet_id,
839
+ key_id: operation.key_id ?? null,
840
+ derivation_path: operation.derivation_path ?? null
841
+ };
842
+ if (binding && (typeof binding.wallet_id !== "string" || binding.key_id !== null && typeof binding.key_id !== "string" || binding.derivation_path !== null && typeof binding.derivation_path !== "string")) return null;
843
+ return { type: "wallet_sign", intent, binding };
844
+ }
845
+ const plan = supportedSigningPlan(value.plan);
846
+ return plan ? { type: "transactions", plan } : null;
847
+ }
848
+ function supportedSigningPlan(value) {
849
+ if (!value || typeof value !== "object") return null;
850
+ const plan = value;
851
+ if (plan.version !== void 0 || plan.schema_version !== void 0 || plan.gasless != null || typeof plan.refresh_before_sign !== "boolean") return null;
852
+ const binding = plan.binding;
853
+ if (!binding || typeof binding.wallet_id !== "string" || !binding.wallet_id || binding.key_id != null && typeof binding.key_id !== "string" || binding.derivation_path != null && typeof binding.derivation_path !== "string" || typeof plan.sender !== "string" || !plan.sender || !Array.isArray(plan.txs) || plan.txs.length === 0) return null;
854
+ try {
855
+ for (const tx of plan.txs) {
856
+ if (!tx || tx.vm !== plan.txs[0].vm) return null;
857
+ if (tx.vm === "solana" && typeof tx.transaction === "string") deserializeSolanaTx(tx.transaction);
858
+ else if (tx.vm === "evm" && tx.transaction && typeof tx.transaction === "object" && !Array.isArray(tx.transaction)) eip1559SigningHash(tx.transaction);
859
+ else return null;
860
+ }
861
+ } catch {
862
+ return null;
863
+ }
864
+ return value;
865
+ }
866
+
867
+ // src/execution.ts
868
+ var UnsupportedPlanError = class extends Error {
869
+ };
870
+ var invocationIdMeta = "com.moonpay.paybox/invocationId";
871
+ var requestIdMeta = "com.moonpay.paybox/requestId";
872
+ function requestId(value) {
873
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value) ? value.toLowerCase() : null;
874
+ }
875
+ function payload(result) {
876
+ const value = result.structuredContent ?? toolJson("plugin execution", { ...result, isError: false });
877
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid response");
878
+ return value;
879
+ }
880
+ var statuses = /* @__PURE__ */ new Set([
881
+ "pending_approval",
882
+ "pending_funding",
883
+ "pending_signature",
884
+ "pending_settlement",
885
+ "pending_confirmation",
886
+ "success",
887
+ "denied",
888
+ "error"
889
+ ]);
890
+ async function executePlugin(start, options, connect) {
891
+ const timeoutMs = options.timeoutMs ?? 3e5;
892
+ const intervalMs = options.intervalMs ?? 2e3;
893
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647 || !Number.isInteger(intervalMs) || intervalMs <= 0 || intervalMs > 3e4) {
894
+ throw new RangeError("timeoutMs must be 1..2147483647 and intervalMs must be 1..30000");
895
+ }
896
+ let id = typeof start === "string" ? requestId(start) : null;
897
+ if (typeof start === "string" && !id) throw new TypeError("requestId must be a UUID");
898
+ let response = null;
899
+ const result = (outcome, reason) => ({
900
+ outcome,
901
+ requestId: id,
902
+ response,
903
+ ...reason ? { reason } : {}
904
+ });
905
+ const controller = new AbortController();
906
+ let stopped = "cancelled";
907
+ const cancel = () => controller.abort();
908
+ options.signal?.addEventListener("abort", cancel, { once: true });
909
+ if (options.signal?.aborted) cancel();
910
+ const timer = setTimeout(() => {
911
+ if (!controller.signal.aborted) {
912
+ stopped = "deadline_exceeded";
913
+ controller.abort();
914
+ }
915
+ }, timeoutMs);
916
+ let onAbort;
917
+ const aborted = new Promise((resolve) => {
918
+ onAbort = () => resolve(result(stopped));
919
+ if (controller.signal.aborted) onAbort();
920
+ else controller.signal.addEventListener("abort", onAbort, { once: true });
921
+ });
922
+ const ports = connect(controller.signal);
923
+ const read = async () => {
924
+ controller.signal.throwIfAborted();
925
+ const next = await ports.getRequest(id);
926
+ controller.signal.throwIfAborted();
927
+ if (!next || requestId(next.request_id) !== id || !statuses.has(next.status)) {
928
+ throw new Error("invalid request response");
929
+ }
930
+ response = pendingFundingResponse(next) ?? next;
931
+ return response;
932
+ };
933
+ const terminal = (current) => {
934
+ if (current.status === "error" || current.status === "denied") return result("terminal");
935
+ if (current.status !== "success") return null;
936
+ const value = current.output?.value;
937
+ const fields = current.output?.output_type === "tx_hash" ? ["tx_hash"] : current.output?.output_type === "signature" ? ["signature", "serializedTransaction", "signedTransactionBase64"] : [];
938
+ return fields.some((field) => typeof value?.[field] === "string" && value[field].trim()) ? result("terminal") : result("unsupported", "success_output_not_supported");
939
+ };
940
+ const drive = async () => {
941
+ try {
942
+ controller.signal.throwIfAborted();
943
+ let toolError = false;
944
+ if (typeof start !== "string") {
945
+ const raw = await ports.callTool("use_plugin", {
946
+ plugin_id: start.pluginId,
947
+ tool_id: start.toolId,
948
+ input: start.input,
949
+ invocation_id: start.invocationId
950
+ });
951
+ controller.signal.throwIfAborted();
952
+ const funding = pendingFundingToolResponse(raw);
953
+ if (funding) {
954
+ response = funding;
955
+ id = requestId(funding.request_id);
956
+ return result("unsupported", "funding_required");
957
+ }
958
+ let data;
959
+ try {
960
+ data = payload(raw);
961
+ } catch {
962
+ return result("reconcile_required", raw.isError ? "tool_error" : "invalid_plugin_result");
963
+ }
964
+ toolError = raw.isError === true || data.status === "error";
965
+ const nextId = requestId(data.request_id);
966
+ if (!nextId) return result(toolError ? "reconcile_required" : "unsupported", toolError ? "tool_error" : "no_request_id");
967
+ if (requestId(start.pluginId)) {
968
+ const invocationId = requestId(raw._meta?.[invocationIdMeta]);
969
+ const expectedInvocationId = requestId(start.invocationId);
970
+ const boundRequestId = requestId(raw._meta?.[requestIdMeta]);
971
+ if (!invocationId || start.invocationId !== void 0 && invocationId !== expectedInvocationId || boundRequestId !== nextId) {
972
+ return result("reconcile_required", "request_id_mismatch");
973
+ }
974
+ }
975
+ id = nextId;
976
+ }
977
+ let current = await read();
978
+ let attempted = false;
979
+ let pauseMs = intervalMs;
980
+ for (; ; ) {
981
+ controller.signal.throwIfAborted();
982
+ const done = terminal(current);
983
+ if (done) return done;
984
+ if (current.status === "pending_funding") return result("unsupported", "funding_required");
985
+ if (toolError) return result("reconcile_required", "tool_error");
986
+ if (current.status === "pending_approval" && !options.waitForApproval) {
987
+ return result("approval_required");
988
+ }
989
+ if (current.status === "pending_signature") {
990
+ if (attempted) return result("reconcile_required", "submission_not_confirmed");
991
+ if (options.autoSign === false || !ports.sign) return result("signing_required");
992
+ try {
993
+ const raw = await ports.callTool("reopen_signing_window", { request_id: id });
994
+ if (raw.isError) throw new Error("reopen failed");
995
+ const reopened = payload(raw);
996
+ if (requestId(reopened.request_id) !== id) return result("unsupported", "request_id_mismatch");
997
+ current = await read();
998
+ if (current.status !== "pending_signature") continue;
999
+ if (reopened.status !== "pending_signature") return result("reconcile_required", "request_changed");
1000
+ const action = supportedSigningAction(reopened);
1001
+ if (!action) return result("unsupported", "signing_plan_not_supported");
1002
+ attempted = true;
1003
+ await ports.sign(id, action, (refreshed) => {
1004
+ controller.signal.throwIfAborted();
1005
+ if (!supportedSigningPlan(refreshed)) throw new UnsupportedPlanError();
1006
+ });
1007
+ } catch (error) {
1008
+ current = await read();
1009
+ const done2 = terminal(current);
1010
+ if (done2) return done2;
1011
+ if (error instanceof UnsupportedPlanError) return result("unsupported", "refreshed_plan_not_supported");
1012
+ if (current.status === "pending_signature") return result("reconcile_required", "signing_interrupted");
1013
+ }
1014
+ current = await read();
1015
+ continue;
1016
+ }
1017
+ await delay(pauseMs, void 0, { signal: controller.signal });
1018
+ const previous = current.status;
1019
+ current = await read();
1020
+ pauseMs = current.status === previous ? Math.min(pauseMs * 2, 3e4) : intervalMs;
1021
+ }
1022
+ } catch {
1023
+ return result(controller.signal.aborted ? stopped : "reconcile_required", "execution_interrupted");
1024
+ }
1025
+ };
1026
+ try {
1027
+ return await Promise.race([drive(), aborted]);
1028
+ } finally {
1029
+ clearTimeout(timer);
1030
+ options.signal?.removeEventListener("abort", cancel);
1031
+ controller.signal.removeEventListener("abort", onAbort);
1032
+ }
1033
+ }
1034
+
742
1035
  // src/client.ts
743
1036
  var DEFAULT_BASE_URL = "https://api.paybox.sh";
744
1037
  var EXPIRY_SKEW_MS = 6e4;
745
1038
  function signingResultForResponse(result, response) {
1039
+ const funding = pendingFundingResponse(response);
1040
+ if (funding) return { response: funding, plan: null };
746
1041
  const exposesPlan = response.status === "pending_approval" || response.status === "pending_signature";
747
1042
  return { ...result, response, plan: exposesPlan ? result.plan : null };
748
1043
  }
@@ -800,6 +1095,15 @@ var PayboxClient = class _PayboxClient {
800
1095
  get canSign() {
801
1096
  return this.signingCreds() !== null;
802
1097
  }
1098
+ nextAction(response) {
1099
+ return nextActionFor(response, {
1100
+ canSignInProcess: this.canSign,
1101
+ canRenderSigningUi: false,
1102
+ canOpenApprovalUi: false,
1103
+ canPoll: true,
1104
+ canReconcile: true
1105
+ });
1106
+ }
803
1107
  async bearer() {
804
1108
  if (this.opts.token) return this.opts.token;
805
1109
  if (this.opts.apiKey) return this.opts.apiKey;
@@ -819,6 +1123,7 @@ var PayboxClient = class _PayboxClient {
819
1123
  );
820
1124
  }
821
1125
  async request(method, path, init = {}) {
1126
+ init.signal?.throwIfAborted();
822
1127
  const url = new URL(this.baseUrl + path);
823
1128
  for (const [k, v] of Object.entries(init.query ?? {})) {
824
1129
  if (v != null) url.searchParams.set(k, v);
@@ -829,10 +1134,12 @@ var PayboxClient = class _PayboxClient {
829
1134
  headers["content-type"] = "application/json";
830
1135
  body = JSON.stringify(init.body);
831
1136
  }
832
- const res = await this.fetchImpl(url, { method, headers, body });
1137
+ init.signal?.throwIfAborted();
1138
+ const res = await this.fetchImpl(url, { method, headers, body, signal: init.signal });
833
1139
  const text = await res.text();
1140
+ init.signal?.throwIfAborted();
834
1141
  if (!res.ok) throw new PayboxError(res.status, text, `${method} ${path}`);
835
- return text ? JSON.parse(text) : void 0;
1142
+ return nonSigningFundingResult(text ? JSON.parse(text) : void 0);
836
1143
  }
837
1144
  poster = (path, body) => this.request("POST", path, { body });
838
1145
  listCredentials() {
@@ -903,6 +1210,39 @@ var PayboxClient = class _PayboxClient {
903
1210
  (r) => r
904
1211
  );
905
1212
  }
1213
+ async completeWalletSignRequest(requestId2) {
1214
+ const creds = this.signingCreds();
1215
+ if (!creds) {
1216
+ throw new Error("cannot complete wallet-sign request without a valid local signing key");
1217
+ }
1218
+ const result = await this.mcp.callTool(
1219
+ "reopen_signing_window",
1220
+ { request_id: requestId2 },
1221
+ "in-process"
1222
+ );
1223
+ const parked = toolJson("reopen_signing_window", result);
1224
+ if (parked.request_id !== requestId2) {
1225
+ throw new Error("paybox returned a different request_id for the parked signature");
1226
+ }
1227
+ if (parked.status === "success") return this.getRequest(requestId2);
1228
+ if (parked.status !== "pending_signature") {
1229
+ throw new Error(`cannot complete wallet-sign request while it is ${parked.status ?? "unknown"}`);
1230
+ }
1231
+ const intent = parked.operation?.intent;
1232
+ if (parked.next_action?.kind !== "sign" || parked.operation?.type !== "wallet_sign" || !intent || typeof intent.op !== "string") {
1233
+ throw new Error("paybox returned no signable wallet intent for this request");
1234
+ }
1235
+ const binding = parked.operation && {
1236
+ wallet_id: parked.operation.wallet_id ?? "",
1237
+ key_id: parked.operation.key_id ?? null,
1238
+ derivation_path: parked.operation.derivation_path ?? null
1239
+ };
1240
+ await this.signWithReconcile(
1241
+ requestId2,
1242
+ () => signWalletRequest(this.poster, requestId2, intent, creds, binding)
1243
+ );
1244
+ return this.getRequest(requestId2);
1245
+ }
906
1246
  async requestSwap(args, opts = {}) {
907
1247
  return this.withExpiryRetry(
908
1248
  async () => {
@@ -950,12 +1290,12 @@ var PayboxClient = class _PayboxClient {
950
1290
  await this.requestAfterSign(response.request_id, opts.waitForApproval)
951
1291
  );
952
1292
  }
953
- async waitWhileApproval(requestId, wait) {
1293
+ async waitWhileApproval(requestId2, wait) {
954
1294
  const o = typeof wait === "object" ? wait : {};
955
1295
  const interval = o.intervalMs ?? 2e3;
956
1296
  const deadline = Date.now() + (o.timeoutMs ?? 3e5);
957
1297
  for (; ; ) {
958
- const r = await this.getRequest(requestId);
1298
+ const r = await this.getRequest(requestId2);
959
1299
  if (r.status !== "pending_approval" || Date.now() > deadline) return r;
960
1300
  await new Promise((res) => setTimeout(res, interval));
961
1301
  }
@@ -968,11 +1308,11 @@ var PayboxClient = class _PayboxClient {
968
1308
  }
969
1309
  return first;
970
1310
  }
971
- async signWithReconcile(requestId, sign) {
1311
+ async signWithReconcile(requestId2, sign) {
972
1312
  try {
973
1313
  await sign();
974
1314
  } catch {
975
- const reconciled = await this.getRequest(requestId);
1315
+ const reconciled = await this.getRequest(requestId2);
976
1316
  if (reconciled.status !== "pending_signature") return;
977
1317
  await sign();
978
1318
  }
@@ -1016,12 +1356,40 @@ var PayboxClient = class _PayboxClient {
1016
1356
  await this.mcp.callTool("get_contract", { contract_uri: contractUri })
1017
1357
  );
1018
1358
  }
1019
- usePlugin(args) {
1020
- return this.mcp.callTool("use_plugin", {
1359
+ async usePlugin(args) {
1360
+ return nonSigningFundingToolResult(await this.mcp.callTool("use_plugin", {
1021
1361
  plugin_id: args.pluginId,
1022
1362
  tool_id: args.toolId,
1023
1363
  input: args.input,
1024
1364
  invocation_id: args.invocationId
1365
+ }));
1366
+ }
1367
+ runPlugin(args, options = {}) {
1368
+ return this.executePlugin(args, options);
1369
+ }
1370
+ resumeRequest(requestId2, options = {}) {
1371
+ return this.executePlugin(requestId2, options);
1372
+ }
1373
+ executePlugin(start, options) {
1374
+ return executePlugin(start, options, (signal) => {
1375
+ const mcp = new McpTransport({
1376
+ baseUrl: this.baseUrl,
1377
+ bearer: () => this.bearer(),
1378
+ fetchImpl: (input, init) => {
1379
+ signal.throwIfAborted();
1380
+ return this.fetchImpl(input, { ...init, signal });
1381
+ }
1382
+ });
1383
+ const post = (path, body) => this.request("POST", path, { body, signal });
1384
+ const creds = this.signingCreds();
1385
+ return {
1386
+ callTool: (name, args) => mcp.callTool(name, args, name === "reopen_signing_window" ? "in-process" : void 0),
1387
+ getRequest: (id) => this.request("GET", `/agent/requests/${id}`, { signal }),
1388
+ sign: creds ? async (id, action, validate) => {
1389
+ if (action.type === "transactions") await signSwapRequest(post, id, action.plan, creds, validate);
1390
+ else await signWalletRequest(post, id, action.intent, creds, action.binding);
1391
+ } : void 0
1392
+ };
1025
1393
  });
1026
1394
  }
1027
1395
  async payX402(args, opts = {}) {
@@ -1078,8 +1446,8 @@ var PayboxClient = class _PayboxClient {
1078
1446
  await this.requestAfterSign(response.request_id, opts.waitForApproval)
1079
1447
  );
1080
1448
  }
1081
- claimPaymentCredentials(requestId) {
1082
- return this.request("POST", `/agent/requests/${requestId}/credentials`);
1449
+ claimPaymentCredentials(requestId2) {
1450
+ return this.request("POST", `/agent/requests/${requestId2}/credentials`);
1083
1451
  }
1084
1452
  requestAccountChange(note) {
1085
1453
  return this.request("POST", "/agent/account-change", { body: { note } });
@@ -1102,19 +1470,19 @@ var PayboxClient = class _PayboxClient {
1102
1470
  }
1103
1471
  });
1104
1472
  }
1105
- getRequest(requestId) {
1106
- return this.request("GET", `/agent/requests/${requestId}`);
1473
+ getRequest(requestId2) {
1474
+ return this.request("GET", `/agent/requests/${requestId2}`);
1107
1475
  }
1108
- requestAfterSign(requestId, wait) {
1109
- if (!wait) return this.getRequest(requestId);
1110
- return this.waitForRequest(requestId, typeof wait === "object" ? wait : {});
1476
+ requestAfterSign(requestId2, wait) {
1477
+ if (!wait) return this.getRequest(requestId2);
1478
+ return this.waitForRequest(requestId2, typeof wait === "object" ? wait : {});
1111
1479
  }
1112
- async waitForRequest(requestId, opts = {}) {
1480
+ async waitForRequest(requestId2, opts = {}) {
1113
1481
  const interval = opts.intervalMs ?? 2e3;
1114
1482
  const deadline = Date.now() + (opts.timeoutMs ?? 3e5);
1115
1483
  for (; ; ) {
1116
- const r = await this.getRequest(requestId);
1117
- const pending = r.status === "pending_approval" || r.status === "pending_signature" || r.status === "pending_settlement" || r.status === "pending_confirmation";
1484
+ const r = await this.getRequest(requestId2);
1485
+ const pending = r.status === "pending_approval" || r.status === "pending_funding" || r.status === "pending_signature" || r.status === "pending_settlement" || r.status === "pending_confirmation";
1118
1486
  if (!pending || Date.now() > deadline) return r;
1119
1487
  await new Promise((res) => setTimeout(res, interval));
1120
1488
  }
@@ -1132,10 +1500,13 @@ export {
1132
1500
  configFile,
1133
1501
  loadConfig,
1134
1502
  saveConfig,
1503
+ nextActionFor,
1504
+ pendingFundingResponse,
1505
+ pendingFundingToolResponse,
1135
1506
  encodeToken,
1136
1507
  credsFromToken,
1137
1508
  didNotHappen,
1138
1509
  PayboxError,
1139
1510
  PayboxClient
1140
1511
  };
1141
- //# sourceMappingURL=chunk-66FQ2R7G.js.map
1512
+ //# sourceMappingURL=chunk-DWDC6W5P.js.map