@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.
- package/README.md +83 -9
- package/SKILL.md +269 -228
- package/dist/{chunk-66FQ2R7G.js → chunk-DWDC6W5P.js} +427 -56
- package/dist/chunk-DWDC6W5P.js.map +1 -0
- package/dist/cli.js +33 -21
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +61 -18
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-66FQ2R7G.js.map +0 -1
|
@@ -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
|
|
408
|
-
const { signed_body, agent_signature } = buildEnvelope(
|
|
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,
|
|
641
|
+
async function resolveBinding(post, requestId2, creds) {
|
|
567
642
|
const env = buildIssuedAtEnvelope(creds);
|
|
568
|
-
const r = await post(`/agent/requests/${
|
|
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,
|
|
576
|
-
const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post,
|
|
577
|
-
const transport = (envelope) => post(`/agent/requests/${
|
|
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/${
|
|
658
|
+
await post(`/agent/requests/${requestId2}/signature`, { artifact: result.artifact });
|
|
584
659
|
return result.artifact;
|
|
585
660
|
}
|
|
586
|
-
function
|
|
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"
|
|
666
|
+
(t) => t.kind === "swap" || t.kind === "prediction_trade"
|
|
591
667
|
);
|
|
592
668
|
}
|
|
593
|
-
async function refreshPlan(post,
|
|
594
|
-
const r = await post(`/agent/requests/${
|
|
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,
|
|
601
|
-
const refreshed = await refreshPlan(post,
|
|
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,
|
|
611
|
-
const refreshed = await refreshPlan(post,
|
|
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,
|
|
619
|
-
const signPlan =
|
|
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,
|
|
703
|
+
binding = await resolveBinding(post, requestId2, creds);
|
|
627
704
|
}
|
|
628
705
|
const envelopes = buildSwapEnvelopes(signPlan.txs, binding, creds);
|
|
629
|
-
await post(`/agent/requests/${
|
|
706
|
+
await post(`/agent/requests/${requestId2}/envelopes`, { envelopes });
|
|
630
707
|
}
|
|
631
|
-
async function signX402Request(post,
|
|
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,
|
|
715
|
+
binding = await resolveBinding(post, requestId2, creds);
|
|
639
716
|
}
|
|
640
|
-
const envelope = isSolanaRailX402(plan) ? buildX402SolanaEnvelope(await refreshX402SolanaTx(post,
|
|
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/${
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
1311
|
+
async signWithReconcile(requestId2, sign) {
|
|
972
1312
|
try {
|
|
973
1313
|
await sign();
|
|
974
1314
|
} catch {
|
|
975
|
-
const reconciled = await this.getRequest(
|
|
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(
|
|
1082
|
-
return this.request("POST", `/agent/requests/${
|
|
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(
|
|
1106
|
-
return this.request("GET", `/agent/requests/${
|
|
1473
|
+
getRequest(requestId2) {
|
|
1474
|
+
return this.request("GET", `/agent/requests/${requestId2}`);
|
|
1107
1475
|
}
|
|
1108
|
-
requestAfterSign(
|
|
1109
|
-
if (!wait) return this.getRequest(
|
|
1110
|
-
return this.waitForRequest(
|
|
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(
|
|
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(
|
|
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-
|
|
1512
|
+
//# sourceMappingURL=chunk-DWDC6W5P.js.map
|