@paybox-sh/sdk 2.1.0 → 2.3.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.
- package/README.md +72 -11
- package/SKILL.md +97 -58
- package/dist/{chunk-OZ7KVOBT.js → chunk-YTHYNUMM.js} +338 -49
- package/dist/chunk-YTHYNUMM.js.map +1 -0
- package/dist/cli.js +10 -274
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +42 -16
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OZ7KVOBT.js.map +0 -1
|
@@ -372,7 +372,7 @@ import { Buffer as Buffer2 } from "buffer";
|
|
|
372
372
|
import nacl from "tweetnacl";
|
|
373
373
|
import { MoonXEvmSigner, EvmSchemeMismatchError } from "@moon-x/evm-adapter";
|
|
374
374
|
import { MoonXSolanaSigner, SolanaSchemeMismatchError } from "@moon-x/solana-adapter";
|
|
375
|
-
import { Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
375
|
+
import { PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
376
376
|
import { hashTypedData, keccak256, serializeTransaction } from "viem";
|
|
377
377
|
function parseWireU128(value, field) {
|
|
378
378
|
if (value === void 0 || value === null) {
|
|
@@ -445,8 +445,42 @@ function buildEnvelope(rawSigningPayloadHex, binding, creds) {
|
|
|
445
445
|
function eip1559SigningHash(tx) {
|
|
446
446
|
return keccak256(serializeTransaction(reviveTx(tx)));
|
|
447
447
|
}
|
|
448
|
+
var SOLANA_V1_PREFIX = 129;
|
|
449
|
+
var SOLANA_SIGNATURE_BYTES = 64;
|
|
450
|
+
var SOLANA_V1_FEE_PAYER_OFFSET = 42;
|
|
451
|
+
var SOLANA_PUBKEY_BYTES = 32;
|
|
452
|
+
function solanaV1Message(raw) {
|
|
453
|
+
const signatureCount = raw[1];
|
|
454
|
+
if (!signatureCount || raw.length <= signatureCount * SOLANA_SIGNATURE_BYTES) {
|
|
455
|
+
throw new Error("solana v1 tx has no signature slots");
|
|
456
|
+
}
|
|
457
|
+
return raw.subarray(0, raw.length - signatureCount * SOLANA_SIGNATURE_BYTES);
|
|
458
|
+
}
|
|
459
|
+
async function signSolanaV1(raw, address, sign) {
|
|
460
|
+
if (raw[1] !== 1) {
|
|
461
|
+
throw new Error("solana v1 transactions with more than one signer are not supported");
|
|
462
|
+
}
|
|
463
|
+
const feePayer = new PublicKey(
|
|
464
|
+
raw.subarray(SOLANA_V1_FEE_PAYER_OFFSET, SOLANA_V1_FEE_PAYER_OFFSET + SOLANA_PUBKEY_BYTES)
|
|
465
|
+
);
|
|
466
|
+
if (!feePayer.equals(new PublicKey(address))) {
|
|
467
|
+
throw new Error("solana v1 transaction signer is not the wallet address");
|
|
468
|
+
}
|
|
469
|
+
const message = solanaV1Message(raw);
|
|
470
|
+
const signature = await sign(message);
|
|
471
|
+
if (signature.length !== SOLANA_SIGNATURE_BYTES) {
|
|
472
|
+
throw new Error("the signing service returned an Ed25519 signature that is not 64 bytes");
|
|
473
|
+
}
|
|
474
|
+
const signed = new Uint8Array(message.length + SOLANA_SIGNATURE_BYTES);
|
|
475
|
+
signed.set(message);
|
|
476
|
+
signed.set(signature, message.length);
|
|
477
|
+
return signed;
|
|
478
|
+
}
|
|
448
479
|
function solanaSigningPayload(base64Tx) {
|
|
449
480
|
const raw = Buffer2.from(base64Tx, "base64");
|
|
481
|
+
if (raw[0] === SOLANA_V1_PREFIX) {
|
|
482
|
+
return bytesToHex(new Uint8Array(solanaV1Message(raw)));
|
|
483
|
+
}
|
|
450
484
|
let i = 0;
|
|
451
485
|
let sigCount = 0;
|
|
452
486
|
let shift = 0;
|
|
@@ -479,8 +513,8 @@ function buildSwapEnvelopes(txs, binding, creds) {
|
|
|
479
513
|
);
|
|
480
514
|
}
|
|
481
515
|
return txs.map((t) => {
|
|
482
|
-
const
|
|
483
|
-
const { signed_body, agent_signature } = buildEnvelope(
|
|
516
|
+
const payload2 = t.vm === "eip712" ? eip712SigningHash(t.transaction) : t.vm === "solana" ? solanaSigningPayload(t.transaction) : eip1559SigningHash(t.transaction);
|
|
517
|
+
const { signed_body, agent_signature } = buildEnvelope(payload2, binding, creds);
|
|
484
518
|
return { api_pub: creds.apiPubHex, signed_body, agent_signature };
|
|
485
519
|
});
|
|
486
520
|
}
|
|
@@ -597,6 +631,14 @@ async function signIntent(intent, creds, binding, transport) {
|
|
|
597
631
|
return { ok: true, artifact: { signature: bytesToHex(sig) } };
|
|
598
632
|
}
|
|
599
633
|
case "solanaTransaction": {
|
|
634
|
+
const raw = Buffer2.from(intent.transactionBase64, "base64");
|
|
635
|
+
if (raw[0] === SOLANA_V1_PREFIX) {
|
|
636
|
+
const signed2 = await signSolanaV1(raw, intent.address, ed25519Sign);
|
|
637
|
+
return {
|
|
638
|
+
ok: true,
|
|
639
|
+
artifact: { signedTransactionBase64: Buffer2.from(signed2).toString("base64") }
|
|
640
|
+
};
|
|
641
|
+
}
|
|
600
642
|
const tx = deserializeSolanaTx(intent.transactionBase64);
|
|
601
643
|
const signed = await new MoonXSolanaSigner(ed25519Sign, intent.address).signTransaction(
|
|
602
644
|
tx
|
|
@@ -638,42 +680,43 @@ function credsFromToken(token) {
|
|
|
638
680
|
function toBinding(b) {
|
|
639
681
|
return { walletId: b.wallet_id, keyId: b.key_id ?? "", derivationPath: b.derivation_path ?? "" };
|
|
640
682
|
}
|
|
641
|
-
async function resolveBinding(post,
|
|
683
|
+
async function resolveBinding(post, requestId2, creds) {
|
|
642
684
|
const env = buildIssuedAtEnvelope(creds);
|
|
643
|
-
const r = await post(`/agent/requests/${
|
|
685
|
+
const r = await post(`/agent/requests/${requestId2}/binding`, {
|
|
644
686
|
api_pub: creds.apiPubHex,
|
|
645
687
|
signed_body: env.signed_body,
|
|
646
688
|
agent_signature: env.agent_signature
|
|
647
689
|
});
|
|
648
690
|
return toBinding(r);
|
|
649
691
|
}
|
|
650
|
-
async function signWalletRequest(post,
|
|
651
|
-
const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post,
|
|
652
|
-
const transport = (envelope) => post(`/agent/requests/${
|
|
692
|
+
async function signWalletRequest(post, requestId2, intent, creds, binding) {
|
|
693
|
+
const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post, requestId2, creds);
|
|
694
|
+
const transport = (envelope) => post(`/agent/requests/${requestId2}/moonx-sign`, {
|
|
653
695
|
api_pub: creds.apiPubHex,
|
|
654
696
|
...envelope
|
|
655
697
|
});
|
|
656
698
|
const result = await signIntent(intent, creds, resolved, transport);
|
|
657
699
|
if (!result.ok) throw new Error(result.error);
|
|
658
|
-
await post(`/agent/requests/${
|
|
700
|
+
await post(`/agent/requests/${requestId2}/signature`, { artifact: result.artifact });
|
|
659
701
|
return result.artifact;
|
|
660
702
|
}
|
|
661
|
-
function
|
|
703
|
+
function isRefreshablePlan(plan) {
|
|
704
|
+
if (typeof plan.refresh_before_sign === "boolean") return plan.refresh_before_sign;
|
|
662
705
|
const txs = plan.txs;
|
|
663
706
|
const solana = txs.some((t) => t.vm === "solana");
|
|
664
707
|
return solana && txs.some(
|
|
665
|
-
(t) => t.kind === "swap" || t.kind === "prediction_trade"
|
|
708
|
+
(t) => t.kind === "swap" || t.kind === "prediction_trade"
|
|
666
709
|
);
|
|
667
710
|
}
|
|
668
|
-
async function refreshPlan(post,
|
|
669
|
-
const r = await post(`/agent/requests/${
|
|
711
|
+
async function refreshPlan(post, requestId2) {
|
|
712
|
+
const r = await post(`/agent/requests/${requestId2}/refresh`, {});
|
|
670
713
|
if (!r || typeof r.plan !== "object" || r.plan === null) {
|
|
671
714
|
throw new Error("refresh returned no plan");
|
|
672
715
|
}
|
|
673
716
|
return r.plan;
|
|
674
717
|
}
|
|
675
|
-
async function refreshSwapPlan(post,
|
|
676
|
-
const refreshed = await refreshPlan(post,
|
|
718
|
+
async function refreshSwapPlan(post, requestId2) {
|
|
719
|
+
const refreshed = await refreshPlan(post, requestId2);
|
|
677
720
|
if (!Array.isArray(refreshed.txs) || refreshed.txs.length === 0) {
|
|
678
721
|
throw new Error("swap refresh returned a plan with no txs");
|
|
679
722
|
}
|
|
@@ -682,42 +725,43 @@ async function refreshSwapPlan(post, requestId) {
|
|
|
682
725
|
function isSolanaRailX402(plan) {
|
|
683
726
|
return plan.x402?.rail === "solana";
|
|
684
727
|
}
|
|
685
|
-
async function refreshX402SolanaTx(post,
|
|
686
|
-
const refreshed = await refreshPlan(post,
|
|
728
|
+
async function refreshX402SolanaTx(post, requestId2) {
|
|
729
|
+
const refreshed = await refreshPlan(post, requestId2);
|
|
687
730
|
const tx = refreshed.solana?.transaction;
|
|
688
731
|
if (typeof tx !== "string" || tx.length === 0) {
|
|
689
732
|
throw new Error("x402 refresh returned a plan with no Solana transaction");
|
|
690
733
|
}
|
|
691
734
|
return tx;
|
|
692
735
|
}
|
|
693
|
-
async function signSwapRequest(post,
|
|
694
|
-
const signPlan =
|
|
736
|
+
async function signSwapRequest(post, requestId2, plan, creds, validate) {
|
|
737
|
+
const signPlan = isRefreshablePlan(plan) ? await refreshSwapPlan(post, requestId2) : plan;
|
|
738
|
+
validate?.(signPlan);
|
|
695
739
|
let binding = {
|
|
696
740
|
walletId: signPlan.binding.wallet_id,
|
|
697
741
|
keyId: signPlan.binding.key_id ?? "",
|
|
698
742
|
derivationPath: signPlan.binding.derivation_path ?? ""
|
|
699
743
|
};
|
|
700
744
|
if (binding.walletId && (!binding.keyId || !binding.derivationPath)) {
|
|
701
|
-
binding = await resolveBinding(post,
|
|
745
|
+
binding = await resolveBinding(post, requestId2, creds);
|
|
702
746
|
}
|
|
703
747
|
const envelopes = buildSwapEnvelopes(signPlan.txs, binding, creds);
|
|
704
|
-
await post(`/agent/requests/${
|
|
748
|
+
await post(`/agent/requests/${requestId2}/envelopes`, { envelopes });
|
|
705
749
|
}
|
|
706
|
-
async function signX402Request(post,
|
|
750
|
+
async function signX402Request(post, requestId2, plan, creds) {
|
|
707
751
|
let binding = {
|
|
708
752
|
walletId: plan.binding.wallet_id,
|
|
709
753
|
keyId: plan.binding.key_id ?? "",
|
|
710
754
|
derivationPath: plan.binding.derivation_path ?? ""
|
|
711
755
|
};
|
|
712
756
|
if (binding.walletId && (!binding.keyId || !binding.derivationPath)) {
|
|
713
|
-
binding = await resolveBinding(post,
|
|
757
|
+
binding = await resolveBinding(post, requestId2, creds);
|
|
714
758
|
}
|
|
715
|
-
const envelope = isSolanaRailX402(plan) ? buildX402SolanaEnvelope(await refreshX402SolanaTx(post,
|
|
759
|
+
const envelope = isSolanaRailX402(plan) ? buildX402SolanaEnvelope(await refreshX402SolanaTx(post, requestId2), binding, creds) : buildX402Envelope(
|
|
716
760
|
plan.typed_data,
|
|
717
761
|
binding,
|
|
718
762
|
creds
|
|
719
763
|
);
|
|
720
|
-
await post(`/agent/requests/${
|
|
764
|
+
await post(`/agent/requests/${requestId2}/envelopes`, { envelopes: [envelope] });
|
|
721
765
|
}
|
|
722
766
|
|
|
723
767
|
// src/mcp.ts
|
|
@@ -769,7 +813,7 @@ var McpTransport = class {
|
|
|
769
813
|
throw error;
|
|
770
814
|
}
|
|
771
815
|
}
|
|
772
|
-
async post(
|
|
816
|
+
async post(payload2, method, signingSurface) {
|
|
773
817
|
const response = await this.fetchImpl(this.endpoint, {
|
|
774
818
|
method: "POST",
|
|
775
819
|
headers: {
|
|
@@ -779,7 +823,7 @@ var McpTransport = class {
|
|
|
779
823
|
"mcp-protocol-version": PROTOCOL_VERSION,
|
|
780
824
|
...signingSurface ? { "x-paybox-signing-surface": signingSurface } : {}
|
|
781
825
|
},
|
|
782
|
-
body: JSON.stringify(
|
|
826
|
+
body: JSON.stringify(payload2)
|
|
783
827
|
});
|
|
784
828
|
const text = await response.text();
|
|
785
829
|
if (!response.ok) {
|
|
@@ -816,6 +860,220 @@ var McpTransport = class {
|
|
|
816
860
|
}
|
|
817
861
|
};
|
|
818
862
|
|
|
863
|
+
// src/execution.ts
|
|
864
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
865
|
+
|
|
866
|
+
// src/plugins/signing.ts
|
|
867
|
+
function supportedSigningAction(value) {
|
|
868
|
+
if (value.operation != null) {
|
|
869
|
+
const operation = value.operation;
|
|
870
|
+
const intent = operation.intent;
|
|
871
|
+
if (operation.type !== "wallet_sign" || value.next_action?.kind !== "sign" || !intent || !["transaction", "solanaTransaction"].includes(intent.op)) return null;
|
|
872
|
+
try {
|
|
873
|
+
if (intent.op === "transaction") eip1559SigningHash(intent.transaction);
|
|
874
|
+
else if (intent.op === "solanaTransaction" && typeof intent.address === "string" && intent.address) deserializeSolanaTx(intent.transactionBase64);
|
|
875
|
+
else return null;
|
|
876
|
+
} catch {
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
const binding = operation.wallet_id == null ? null : {
|
|
880
|
+
wallet_id: operation.wallet_id,
|
|
881
|
+
key_id: operation.key_id ?? null,
|
|
882
|
+
derivation_path: operation.derivation_path ?? null
|
|
883
|
+
};
|
|
884
|
+
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;
|
|
885
|
+
return { type: "wallet_sign", intent, binding };
|
|
886
|
+
}
|
|
887
|
+
const plan = supportedSigningPlan(value.plan);
|
|
888
|
+
return plan ? { type: "transactions", plan } : null;
|
|
889
|
+
}
|
|
890
|
+
function supportedSigningPlan(value) {
|
|
891
|
+
if (!value || typeof value !== "object") return null;
|
|
892
|
+
const plan = value;
|
|
893
|
+
if (plan.version !== void 0 || plan.schema_version !== void 0 || plan.gasless != null || typeof plan.refresh_before_sign !== "boolean") return null;
|
|
894
|
+
const binding = plan.binding;
|
|
895
|
+
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;
|
|
896
|
+
try {
|
|
897
|
+
for (const tx of plan.txs) {
|
|
898
|
+
if (!tx || tx.vm !== plan.txs[0].vm) return null;
|
|
899
|
+
if (tx.vm === "solana" && typeof tx.transaction === "string") deserializeSolanaTx(tx.transaction);
|
|
900
|
+
else if (tx.vm === "evm" && tx.transaction && typeof tx.transaction === "object" && !Array.isArray(tx.transaction)) eip1559SigningHash(tx.transaction);
|
|
901
|
+
else return null;
|
|
902
|
+
}
|
|
903
|
+
} catch {
|
|
904
|
+
return null;
|
|
905
|
+
}
|
|
906
|
+
return value;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/execution.ts
|
|
910
|
+
var UnsupportedPlanError = class extends Error {
|
|
911
|
+
};
|
|
912
|
+
var invocationIdMeta = "com.moonpay.paybox/invocationId";
|
|
913
|
+
var requestIdMeta = "com.moonpay.paybox/requestId";
|
|
914
|
+
function requestId(value) {
|
|
915
|
+
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;
|
|
916
|
+
}
|
|
917
|
+
function payload(result) {
|
|
918
|
+
const value = result.structuredContent ?? toolJson("plugin execution", { ...result, isError: false });
|
|
919
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid response");
|
|
920
|
+
return value;
|
|
921
|
+
}
|
|
922
|
+
var statuses = /* @__PURE__ */ new Set([
|
|
923
|
+
"pending_approval",
|
|
924
|
+
"pending_funding",
|
|
925
|
+
"pending_signature",
|
|
926
|
+
"pending_settlement",
|
|
927
|
+
"pending_confirmation",
|
|
928
|
+
"success",
|
|
929
|
+
"denied",
|
|
930
|
+
"error"
|
|
931
|
+
]);
|
|
932
|
+
async function executePlugin(start, options, connect) {
|
|
933
|
+
const timeoutMs = options.timeoutMs ?? 3e5;
|
|
934
|
+
const intervalMs = options.intervalMs ?? 2e3;
|
|
935
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647 || !Number.isInteger(intervalMs) || intervalMs <= 0 || intervalMs > 3e4) {
|
|
936
|
+
throw new RangeError("timeoutMs must be 1..2147483647 and intervalMs must be 1..30000");
|
|
937
|
+
}
|
|
938
|
+
let id = typeof start === "string" ? requestId(start) : null;
|
|
939
|
+
if (typeof start === "string" && !id) throw new TypeError("requestId must be a UUID");
|
|
940
|
+
let response = null;
|
|
941
|
+
const result = (outcome, reason) => ({
|
|
942
|
+
outcome,
|
|
943
|
+
requestId: id,
|
|
944
|
+
response,
|
|
945
|
+
...reason ? { reason } : {}
|
|
946
|
+
});
|
|
947
|
+
const controller = new AbortController();
|
|
948
|
+
let stopped = "cancelled";
|
|
949
|
+
const cancel = () => controller.abort();
|
|
950
|
+
options.signal?.addEventListener("abort", cancel, { once: true });
|
|
951
|
+
if (options.signal?.aborted) cancel();
|
|
952
|
+
const timer = setTimeout(() => {
|
|
953
|
+
if (!controller.signal.aborted) {
|
|
954
|
+
stopped = "deadline_exceeded";
|
|
955
|
+
controller.abort();
|
|
956
|
+
}
|
|
957
|
+
}, timeoutMs);
|
|
958
|
+
let onAbort;
|
|
959
|
+
const aborted = new Promise((resolve) => {
|
|
960
|
+
onAbort = () => resolve(result(stopped));
|
|
961
|
+
if (controller.signal.aborted) onAbort();
|
|
962
|
+
else controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
963
|
+
});
|
|
964
|
+
const ports = connect(controller.signal);
|
|
965
|
+
const read = async () => {
|
|
966
|
+
controller.signal.throwIfAborted();
|
|
967
|
+
const next = await ports.getRequest(id);
|
|
968
|
+
controller.signal.throwIfAborted();
|
|
969
|
+
if (!next || requestId(next.request_id) !== id || !statuses.has(next.status)) {
|
|
970
|
+
throw new Error("invalid request response");
|
|
971
|
+
}
|
|
972
|
+
response = pendingFundingResponse(next) ?? next;
|
|
973
|
+
return response;
|
|
974
|
+
};
|
|
975
|
+
const terminal = (current) => {
|
|
976
|
+
if (current.status === "error" || current.status === "denied") return result("terminal");
|
|
977
|
+
if (current.status !== "success") return null;
|
|
978
|
+
const value = current.output?.value;
|
|
979
|
+
const fields = current.output?.output_type === "tx_hash" ? ["tx_hash"] : current.output?.output_type === "signature" ? ["signature", "serializedTransaction", "signedTransactionBase64"] : [];
|
|
980
|
+
return fields.some((field) => typeof value?.[field] === "string" && value[field].trim()) ? result("terminal") : result("unsupported", "success_output_not_supported");
|
|
981
|
+
};
|
|
982
|
+
const drive = async () => {
|
|
983
|
+
try {
|
|
984
|
+
controller.signal.throwIfAborted();
|
|
985
|
+
let toolError = false;
|
|
986
|
+
if (typeof start !== "string") {
|
|
987
|
+
const raw = await ports.callTool("use_plugin", {
|
|
988
|
+
plugin_id: start.pluginId,
|
|
989
|
+
tool_id: start.toolId,
|
|
990
|
+
input: start.input,
|
|
991
|
+
invocation_id: start.invocationId
|
|
992
|
+
});
|
|
993
|
+
controller.signal.throwIfAborted();
|
|
994
|
+
const funding = pendingFundingToolResponse(raw);
|
|
995
|
+
if (funding) {
|
|
996
|
+
response = funding;
|
|
997
|
+
id = requestId(funding.request_id);
|
|
998
|
+
return result("unsupported", "funding_required");
|
|
999
|
+
}
|
|
1000
|
+
let data;
|
|
1001
|
+
try {
|
|
1002
|
+
data = payload(raw);
|
|
1003
|
+
} catch {
|
|
1004
|
+
return result("reconcile_required", raw.isError ? "tool_error" : "invalid_plugin_result");
|
|
1005
|
+
}
|
|
1006
|
+
toolError = raw.isError === true || data.status === "error";
|
|
1007
|
+
const nextId = requestId(data.request_id);
|
|
1008
|
+
if (!nextId) return result(toolError ? "reconcile_required" : "unsupported", toolError ? "tool_error" : "no_request_id");
|
|
1009
|
+
if (requestId(start.pluginId)) {
|
|
1010
|
+
const invocationId = requestId(raw._meta?.[invocationIdMeta]);
|
|
1011
|
+
const expectedInvocationId = requestId(start.invocationId);
|
|
1012
|
+
const boundRequestId = requestId(raw._meta?.[requestIdMeta]);
|
|
1013
|
+
if (!invocationId || start.invocationId !== void 0 && invocationId !== expectedInvocationId || boundRequestId !== nextId) {
|
|
1014
|
+
return result("reconcile_required", "request_id_mismatch");
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
id = nextId;
|
|
1018
|
+
}
|
|
1019
|
+
let current = await read();
|
|
1020
|
+
let attempted = false;
|
|
1021
|
+
let pauseMs = intervalMs;
|
|
1022
|
+
for (; ; ) {
|
|
1023
|
+
controller.signal.throwIfAborted();
|
|
1024
|
+
const done = terminal(current);
|
|
1025
|
+
if (done) return done;
|
|
1026
|
+
if (current.status === "pending_funding") return result("unsupported", "funding_required");
|
|
1027
|
+
if (toolError) return result("reconcile_required", "tool_error");
|
|
1028
|
+
if (current.status === "pending_approval" && !options.waitForApproval) {
|
|
1029
|
+
return result("approval_required");
|
|
1030
|
+
}
|
|
1031
|
+
if (current.status === "pending_signature") {
|
|
1032
|
+
if (attempted) return result("reconcile_required", "submission_not_confirmed");
|
|
1033
|
+
if (options.autoSign === false || !ports.sign) return result("signing_required");
|
|
1034
|
+
try {
|
|
1035
|
+
const raw = await ports.callTool("reopen_signing_window", { request_id: id });
|
|
1036
|
+
if (raw.isError) throw new Error("reopen failed");
|
|
1037
|
+
const reopened = payload(raw);
|
|
1038
|
+
if (requestId(reopened.request_id) !== id) return result("unsupported", "request_id_mismatch");
|
|
1039
|
+
current = await read();
|
|
1040
|
+
if (current.status !== "pending_signature") continue;
|
|
1041
|
+
if (reopened.status !== "pending_signature") return result("reconcile_required", "request_changed");
|
|
1042
|
+
const action = supportedSigningAction(reopened);
|
|
1043
|
+
if (!action) return result("unsupported", "signing_plan_not_supported");
|
|
1044
|
+
attempted = true;
|
|
1045
|
+
await ports.sign(id, action, (refreshed) => {
|
|
1046
|
+
controller.signal.throwIfAborted();
|
|
1047
|
+
if (!supportedSigningPlan(refreshed)) throw new UnsupportedPlanError();
|
|
1048
|
+
});
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
current = await read();
|
|
1051
|
+
const done2 = terminal(current);
|
|
1052
|
+
if (done2) return done2;
|
|
1053
|
+
if (error instanceof UnsupportedPlanError) return result("unsupported", "refreshed_plan_not_supported");
|
|
1054
|
+
if (current.status === "pending_signature") return result("reconcile_required", "signing_interrupted");
|
|
1055
|
+
}
|
|
1056
|
+
current = await read();
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
await delay(pauseMs, void 0, { signal: controller.signal });
|
|
1060
|
+
const previous = current.status;
|
|
1061
|
+
current = await read();
|
|
1062
|
+
pauseMs = current.status === previous ? Math.min(pauseMs * 2, 3e4) : intervalMs;
|
|
1063
|
+
}
|
|
1064
|
+
} catch {
|
|
1065
|
+
return result(controller.signal.aborted ? stopped : "reconcile_required", "execution_interrupted");
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
try {
|
|
1069
|
+
return await Promise.race([drive(), aborted]);
|
|
1070
|
+
} finally {
|
|
1071
|
+
clearTimeout(timer);
|
|
1072
|
+
options.signal?.removeEventListener("abort", cancel);
|
|
1073
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
819
1077
|
// src/client.ts
|
|
820
1078
|
var DEFAULT_BASE_URL = "https://api.paybox.sh";
|
|
821
1079
|
var EXPIRY_SKEW_MS = 6e4;
|
|
@@ -907,6 +1165,7 @@ var PayboxClient = class _PayboxClient {
|
|
|
907
1165
|
);
|
|
908
1166
|
}
|
|
909
1167
|
async request(method, path, init = {}) {
|
|
1168
|
+
init.signal?.throwIfAborted();
|
|
910
1169
|
const url = new URL(this.baseUrl + path);
|
|
911
1170
|
for (const [k, v] of Object.entries(init.query ?? {})) {
|
|
912
1171
|
if (v != null) url.searchParams.set(k, v);
|
|
@@ -917,8 +1176,10 @@ var PayboxClient = class _PayboxClient {
|
|
|
917
1176
|
headers["content-type"] = "application/json";
|
|
918
1177
|
body = JSON.stringify(init.body);
|
|
919
1178
|
}
|
|
920
|
-
|
|
1179
|
+
init.signal?.throwIfAborted();
|
|
1180
|
+
const res = await this.fetchImpl(url, { method, headers, body, signal: init.signal });
|
|
921
1181
|
const text = await res.text();
|
|
1182
|
+
init.signal?.throwIfAborted();
|
|
922
1183
|
if (!res.ok) throw new PayboxError(res.status, text, `${method} ${path}`);
|
|
923
1184
|
return nonSigningFundingResult(text ? JSON.parse(text) : void 0);
|
|
924
1185
|
}
|
|
@@ -991,21 +1252,21 @@ var PayboxClient = class _PayboxClient {
|
|
|
991
1252
|
(r) => r
|
|
992
1253
|
);
|
|
993
1254
|
}
|
|
994
|
-
async completeWalletSignRequest(
|
|
1255
|
+
async completeWalletSignRequest(requestId2) {
|
|
995
1256
|
const creds = this.signingCreds();
|
|
996
1257
|
if (!creds) {
|
|
997
1258
|
throw new Error("cannot complete wallet-sign request without a valid local signing key");
|
|
998
1259
|
}
|
|
999
1260
|
const result = await this.mcp.callTool(
|
|
1000
1261
|
"reopen_signing_window",
|
|
1001
|
-
{ request_id:
|
|
1262
|
+
{ request_id: requestId2 },
|
|
1002
1263
|
"in-process"
|
|
1003
1264
|
);
|
|
1004
1265
|
const parked = toolJson("reopen_signing_window", result);
|
|
1005
|
-
if (parked.request_id !==
|
|
1266
|
+
if (parked.request_id !== requestId2) {
|
|
1006
1267
|
throw new Error("paybox returned a different request_id for the parked signature");
|
|
1007
1268
|
}
|
|
1008
|
-
if (parked.status === "success") return this.getRequest(
|
|
1269
|
+
if (parked.status === "success") return this.getRequest(requestId2);
|
|
1009
1270
|
if (parked.status !== "pending_signature") {
|
|
1010
1271
|
throw new Error(`cannot complete wallet-sign request while it is ${parked.status ?? "unknown"}`);
|
|
1011
1272
|
}
|
|
@@ -1019,10 +1280,10 @@ var PayboxClient = class _PayboxClient {
|
|
|
1019
1280
|
derivation_path: parked.operation.derivation_path ?? null
|
|
1020
1281
|
};
|
|
1021
1282
|
await this.signWithReconcile(
|
|
1022
|
-
|
|
1023
|
-
() => signWalletRequest(this.poster,
|
|
1283
|
+
requestId2,
|
|
1284
|
+
() => signWalletRequest(this.poster, requestId2, intent, creds, binding)
|
|
1024
1285
|
);
|
|
1025
|
-
return this.getRequest(
|
|
1286
|
+
return this.getRequest(requestId2);
|
|
1026
1287
|
}
|
|
1027
1288
|
async requestSwap(args, opts = {}) {
|
|
1028
1289
|
return this.withExpiryRetry(
|
|
@@ -1071,12 +1332,12 @@ var PayboxClient = class _PayboxClient {
|
|
|
1071
1332
|
await this.requestAfterSign(response.request_id, opts.waitForApproval)
|
|
1072
1333
|
);
|
|
1073
1334
|
}
|
|
1074
|
-
async waitWhileApproval(
|
|
1335
|
+
async waitWhileApproval(requestId2, wait) {
|
|
1075
1336
|
const o = typeof wait === "object" ? wait : {};
|
|
1076
1337
|
const interval = o.intervalMs ?? 2e3;
|
|
1077
1338
|
const deadline = Date.now() + (o.timeoutMs ?? 3e5);
|
|
1078
1339
|
for (; ; ) {
|
|
1079
|
-
const r = await this.getRequest(
|
|
1340
|
+
const r = await this.getRequest(requestId2);
|
|
1080
1341
|
if (r.status !== "pending_approval" || Date.now() > deadline) return r;
|
|
1081
1342
|
await new Promise((res) => setTimeout(res, interval));
|
|
1082
1343
|
}
|
|
@@ -1089,11 +1350,11 @@ var PayboxClient = class _PayboxClient {
|
|
|
1089
1350
|
}
|
|
1090
1351
|
return first;
|
|
1091
1352
|
}
|
|
1092
|
-
async signWithReconcile(
|
|
1353
|
+
async signWithReconcile(requestId2, sign) {
|
|
1093
1354
|
try {
|
|
1094
1355
|
await sign();
|
|
1095
1356
|
} catch {
|
|
1096
|
-
const reconciled = await this.getRequest(
|
|
1357
|
+
const reconciled = await this.getRequest(requestId2);
|
|
1097
1358
|
if (reconciled.status !== "pending_signature") return;
|
|
1098
1359
|
await sign();
|
|
1099
1360
|
}
|
|
@@ -1145,6 +1406,34 @@ var PayboxClient = class _PayboxClient {
|
|
|
1145
1406
|
invocation_id: args.invocationId
|
|
1146
1407
|
}));
|
|
1147
1408
|
}
|
|
1409
|
+
runPlugin(args, options = {}) {
|
|
1410
|
+
return this.executePlugin(args, options);
|
|
1411
|
+
}
|
|
1412
|
+
resumeRequest(requestId2, options = {}) {
|
|
1413
|
+
return this.executePlugin(requestId2, options);
|
|
1414
|
+
}
|
|
1415
|
+
executePlugin(start, options) {
|
|
1416
|
+
return executePlugin(start, options, (signal) => {
|
|
1417
|
+
const mcp = new McpTransport({
|
|
1418
|
+
baseUrl: this.baseUrl,
|
|
1419
|
+
bearer: () => this.bearer(),
|
|
1420
|
+
fetchImpl: (input, init) => {
|
|
1421
|
+
signal.throwIfAborted();
|
|
1422
|
+
return this.fetchImpl(input, { ...init, signal });
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
const post = (path, body) => this.request("POST", path, { body, signal });
|
|
1426
|
+
const creds = this.signingCreds();
|
|
1427
|
+
return {
|
|
1428
|
+
callTool: (name, args) => mcp.callTool(name, args, name === "reopen_signing_window" ? "in-process" : void 0),
|
|
1429
|
+
getRequest: (id) => this.request("GET", `/agent/requests/${id}`, { signal }),
|
|
1430
|
+
sign: creds ? async (id, action, validate) => {
|
|
1431
|
+
if (action.type === "transactions") await signSwapRequest(post, id, action.plan, creds, validate);
|
|
1432
|
+
else await signWalletRequest(post, id, action.intent, creds, action.binding);
|
|
1433
|
+
} : void 0
|
|
1434
|
+
};
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1148
1437
|
async payX402(args, opts = {}) {
|
|
1149
1438
|
return this.withExpiryRetry(
|
|
1150
1439
|
async () => {
|
|
@@ -1199,8 +1488,8 @@ var PayboxClient = class _PayboxClient {
|
|
|
1199
1488
|
await this.requestAfterSign(response.request_id, opts.waitForApproval)
|
|
1200
1489
|
);
|
|
1201
1490
|
}
|
|
1202
|
-
claimPaymentCredentials(
|
|
1203
|
-
return this.request("POST", `/agent/requests/${
|
|
1491
|
+
claimPaymentCredentials(requestId2) {
|
|
1492
|
+
return this.request("POST", `/agent/requests/${requestId2}/credentials`);
|
|
1204
1493
|
}
|
|
1205
1494
|
requestAccountChange(note) {
|
|
1206
1495
|
return this.request("POST", "/agent/account-change", { body: { note } });
|
|
@@ -1223,18 +1512,18 @@ var PayboxClient = class _PayboxClient {
|
|
|
1223
1512
|
}
|
|
1224
1513
|
});
|
|
1225
1514
|
}
|
|
1226
|
-
getRequest(
|
|
1227
|
-
return this.request("GET", `/agent/requests/${
|
|
1515
|
+
getRequest(requestId2) {
|
|
1516
|
+
return this.request("GET", `/agent/requests/${requestId2}`);
|
|
1228
1517
|
}
|
|
1229
|
-
requestAfterSign(
|
|
1230
|
-
if (!wait) return this.getRequest(
|
|
1231
|
-
return this.waitForRequest(
|
|
1518
|
+
requestAfterSign(requestId2, wait) {
|
|
1519
|
+
if (!wait) return this.getRequest(requestId2);
|
|
1520
|
+
return this.waitForRequest(requestId2, typeof wait === "object" ? wait : {});
|
|
1232
1521
|
}
|
|
1233
|
-
async waitForRequest(
|
|
1522
|
+
async waitForRequest(requestId2, opts = {}) {
|
|
1234
1523
|
const interval = opts.intervalMs ?? 2e3;
|
|
1235
1524
|
const deadline = Date.now() + (opts.timeoutMs ?? 3e5);
|
|
1236
1525
|
for (; ; ) {
|
|
1237
|
-
const r = await this.getRequest(
|
|
1526
|
+
const r = await this.getRequest(requestId2);
|
|
1238
1527
|
const pending = r.status === "pending_approval" || r.status === "pending_funding" || r.status === "pending_signature" || r.status === "pending_settlement" || r.status === "pending_confirmation";
|
|
1239
1528
|
if (!pending || Date.now() > deadline) return r;
|
|
1240
1529
|
await new Promise((res) => setTimeout(res, interval));
|
|
@@ -1262,4 +1551,4 @@ export {
|
|
|
1262
1551
|
PayboxError,
|
|
1263
1552
|
PayboxClient
|
|
1264
1553
|
};
|
|
1265
|
-
//# sourceMappingURL=chunk-
|
|
1554
|
+
//# sourceMappingURL=chunk-YTHYNUMM.js.map
|