@paybox-sh/sdk 0.7.0 → 0.8.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 +25 -14
- package/SKILL.md +118 -0
- package/dist/{chunk-EBB4HNOB.js → chunk-7VW4QMV4.js} +304 -125
- package/dist/chunk-7VW4QMV4.js.map +1 -0
- package/dist/cli.js +24 -34
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +131 -51
- package/dist/index.js +1 -1
- package/package.json +12 -4
- package/dist/chunk-EBB4HNOB.js.map +0 -1
|
@@ -43,10 +43,10 @@ function openBrowser(url) {
|
|
|
43
43
|
} catch {
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
var CALLBACK_PAGE = '<!doctype html><meta charset=utf-8><title>
|
|
46
|
+
var CALLBACK_PAGE = '<!doctype html><meta charset=utf-8><title>PayBox</title><body style="font:16px system-ui;padding:3rem;text-align:center"><h2>PayBox CLI connected</h2><p>You can close this tab and return to the terminal.</p>';
|
|
47
47
|
async function loginLoopback(baseUrl, opts = {}) {
|
|
48
48
|
const scope = opts.scope ?? "mcp offline_access";
|
|
49
|
-
const clientName = opts.clientName ?? "
|
|
49
|
+
const clientName = opts.clientName ?? "PayBox CLI";
|
|
50
50
|
const log = opts.log ?? ((m) => console.error(m));
|
|
51
51
|
const meta = await metadata(baseUrl);
|
|
52
52
|
const resource = mcpResource(baseUrl);
|
|
@@ -126,6 +126,91 @@ async function loginLoopback(baseUrl, opts = {}) {
|
|
|
126
126
|
server.close();
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
async function loginDevice(baseUrl, opts = {}) {
|
|
130
|
+
const scope = opts.scope ?? "mcp offline_access";
|
|
131
|
+
const clientName = opts.clientName ?? "PayBox CLI";
|
|
132
|
+
const log = opts.log ?? ((m) => console.error(m));
|
|
133
|
+
const meta = await metadata(baseUrl);
|
|
134
|
+
if (!meta.device_authorization_endpoint) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
"this Paybox API does not advertise device_authorization_endpoint \u2014 upgrade the API and retry `paybox login`"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const resource = mcpResource(baseUrl);
|
|
140
|
+
const reg = await fetch(meta.registration_endpoint, {
|
|
141
|
+
method: "POST",
|
|
142
|
+
headers: { "content-type": "application/json" },
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
client_name: clientName,
|
|
145
|
+
redirect_uris: ["http://127.0.0.1/device"],
|
|
146
|
+
token_endpoint_auth_method: "none",
|
|
147
|
+
grant_types: ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"],
|
|
148
|
+
response_types: ["code"],
|
|
149
|
+
// Must match (or be a superset of) what we ask for below — the server
|
|
150
|
+
// only grants scope that was declared at registration.
|
|
151
|
+
scope
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
if (!reg.ok) throw new Error(`client registration failed (${reg.status}): ${await reg.text()}`);
|
|
155
|
+
const { client_id: clientId } = await reg.json();
|
|
156
|
+
const started = await fetch(meta.device_authorization_endpoint, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
159
|
+
body: new URLSearchParams({ client_id: clientId, scope, resource })
|
|
160
|
+
});
|
|
161
|
+
if (!started.ok) {
|
|
162
|
+
throw new Error(`device authorization failed (${started.status}): ${await started.text()}`);
|
|
163
|
+
}
|
|
164
|
+
const da = await started.json();
|
|
165
|
+
log(
|
|
166
|
+
`Open this URL on your phone (or this computer) to approve access:
|
|
167
|
+
${da.verification_uri_complete}
|
|
168
|
+
Code: ${da.user_code}
|
|
169
|
+
`
|
|
170
|
+
);
|
|
171
|
+
openBrowser(da.verification_uri_complete);
|
|
172
|
+
const timeoutMs = opts.timeoutMs ?? da.expires_in * 1e3;
|
|
173
|
+
const deadline = Date.now() + timeoutMs;
|
|
174
|
+
let intervalSec = da.interval && da.interval > 0 ? da.interval : 5;
|
|
175
|
+
while (Date.now() < deadline) {
|
|
176
|
+
await new Promise((r) => setTimeout(r, intervalSec * 1e3));
|
|
177
|
+
const tok = await fetch(meta.token_endpoint, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
180
|
+
body: new URLSearchParams({
|
|
181
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
182
|
+
device_code: da.device_code,
|
|
183
|
+
client_id: clientId
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
const raw = await tok.text();
|
|
187
|
+
if (tok.ok) {
|
|
188
|
+
const t = JSON.parse(raw);
|
|
189
|
+
return {
|
|
190
|
+
clientId,
|
|
191
|
+
accessToken: t.access_token,
|
|
192
|
+
refreshToken: t.refresh_token,
|
|
193
|
+
expiresAt: t.expires_in ? Date.now() + t.expires_in * 1e3 : void 0,
|
|
194
|
+
resource
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
let err = raw || "authorization failed";
|
|
198
|
+
let code = "";
|
|
199
|
+
try {
|
|
200
|
+
const body = JSON.parse(raw);
|
|
201
|
+
code = body.error ?? "";
|
|
202
|
+
err = body.error_description ?? body.error ?? err;
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
if (code === "authorization_pending") continue;
|
|
206
|
+
if (code === "slow_down") {
|
|
207
|
+
intervalSec += 5;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
throw new Error(err);
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`login timed out after ${Math.round(timeoutMs / 1e3)}s \u2014 no approval received`);
|
|
213
|
+
}
|
|
129
214
|
async function refreshTokens(baseUrl, current) {
|
|
130
215
|
if (!current.refreshToken) throw new Error("no refresh token \u2014 run `paybox login` again");
|
|
131
216
|
const meta = await metadata(baseUrl);
|
|
@@ -178,7 +263,7 @@ function saveConfig(config) {
|
|
|
178
263
|
if (process.platform !== "win32") chmodSync(configFile(), 384);
|
|
179
264
|
}
|
|
180
265
|
|
|
181
|
-
// 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/creds.ts
|
|
266
|
+
// 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
|
|
182
267
|
var TOKEN_PREFIX = "pbxk1.";
|
|
183
268
|
function validateHex(s, label) {
|
|
184
269
|
const trimmed = s.trim();
|
|
@@ -213,7 +298,7 @@ function parseToken(input) {
|
|
|
213
298
|
return { apiPubHex, apiPrivHex };
|
|
214
299
|
}
|
|
215
300
|
|
|
216
|
-
// 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/sign.ts
|
|
301
|
+
// 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/sign.ts
|
|
217
302
|
import { Buffer as Buffer2 } from "buffer";
|
|
218
303
|
import nacl from "tweetnacl";
|
|
219
304
|
import { MoonXEvmSigner, EvmSchemeMismatchError } from "@moon-x/evm-adapter";
|
|
@@ -275,11 +360,11 @@ function solanaSigningPayload(base64Tx) {
|
|
|
275
360
|
}
|
|
276
361
|
function buildSwapEnvelopes(txs, binding, creds) {
|
|
277
362
|
if (!binding.walletId) {
|
|
278
|
-
throw new Error("
|
|
363
|
+
throw new Error("PayBox did not supply a wallet id for this operation.");
|
|
279
364
|
}
|
|
280
365
|
if (!binding.keyId || !binding.derivationPath) {
|
|
281
366
|
throw new Error(
|
|
282
|
-
"This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the
|
|
367
|
+
"This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the PayBox app, then retry."
|
|
283
368
|
);
|
|
284
369
|
}
|
|
285
370
|
return txs.map((t) => {
|
|
@@ -293,11 +378,11 @@ function eip712SigningHash(typedData) {
|
|
|
293
378
|
}
|
|
294
379
|
function buildX402Envelope(typedData, binding, creds) {
|
|
295
380
|
if (!binding.walletId) {
|
|
296
|
-
throw new Error("
|
|
381
|
+
throw new Error("PayBox did not supply a wallet id for this payment.");
|
|
297
382
|
}
|
|
298
383
|
if (!binding.keyId || !binding.derivationPath) {
|
|
299
384
|
throw new Error(
|
|
300
|
-
"This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the
|
|
385
|
+
"This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the PayBox app, then retry."
|
|
301
386
|
);
|
|
302
387
|
}
|
|
303
388
|
const { signed_body, agent_signature } = buildEnvelope(
|
|
@@ -320,19 +405,19 @@ function buildIssuedAtEnvelope(creds) {
|
|
|
320
405
|
}
|
|
321
406
|
async function signIntent(intent, creds, binding, transport) {
|
|
322
407
|
if (!binding.walletId) {
|
|
323
|
-
return { ok: false, error: "
|
|
408
|
+
return { ok: false, error: "PayBox did not supply a wallet id for this operation." };
|
|
324
409
|
}
|
|
325
410
|
if (!binding.keyId || !binding.derivationPath) {
|
|
326
411
|
return {
|
|
327
412
|
ok: false,
|
|
328
|
-
error: "This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the
|
|
413
|
+
error: "This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the PayBox app, then retry."
|
|
329
414
|
};
|
|
330
415
|
}
|
|
331
416
|
const ecdsaSign = async (digest) => {
|
|
332
417
|
const result = await transport(buildEnvelope(digest, binding, creds));
|
|
333
418
|
const { r, s, recovery_id } = result.signature;
|
|
334
419
|
if (r == null || s == null || recovery_id == null) {
|
|
335
|
-
throw new Error("
|
|
420
|
+
throw new Error("the signing service returned an ECDSA result without {r, s, recovery_id}");
|
|
336
421
|
}
|
|
337
422
|
return {
|
|
338
423
|
r: `0x${r}`,
|
|
@@ -343,7 +428,7 @@ async function signIntent(intent, creds, binding, transport) {
|
|
|
343
428
|
const ed25519Sign = async (message) => {
|
|
344
429
|
const result = await transport(buildEnvelope(bytesToHex(message), binding, creds));
|
|
345
430
|
const sig = result.signature.signature;
|
|
346
|
-
if (!sig) throw new Error("
|
|
431
|
+
if (!sig) throw new Error("the signing service returned an Ed25519 result without a signature");
|
|
347
432
|
return hexToBytes(sig);
|
|
348
433
|
};
|
|
349
434
|
try {
|
|
@@ -412,6 +497,11 @@ async function signIntent(intent, creds, binding, transport) {
|
|
|
412
497
|
|
|
413
498
|
// src/signer.ts
|
|
414
499
|
function credsFromToken(token) {
|
|
500
|
+
if (token.trim().startsWith("pbx_live_")) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
"invalid signing key: got a `pbx_live_` API key; expected a `pbxk1.` token (keypair only)"
|
|
503
|
+
);
|
|
504
|
+
}
|
|
415
505
|
const kp = parseToken(token);
|
|
416
506
|
if (!kp) {
|
|
417
507
|
throw new Error("invalid signing key: expected a `pbxk1.` token (keypair only)");
|
|
@@ -444,7 +534,9 @@ async function signWalletRequest(post, requestId, intent, creds, binding) {
|
|
|
444
534
|
function isSolanaRefreshablePlan(plan) {
|
|
445
535
|
const txs = plan.txs;
|
|
446
536
|
const solana = txs.some((t) => t.vm === "solana");
|
|
447
|
-
return solana && txs.some(
|
|
537
|
+
return solana && txs.some(
|
|
538
|
+
(t) => t.kind === "swap" || t.kind === "prediction_trade" || t.kind === "jupiter_swap"
|
|
539
|
+
);
|
|
448
540
|
}
|
|
449
541
|
async function refreshSwapPlan(post, requestId, plan) {
|
|
450
542
|
const r = await post(`/agent/requests/${requestId}/refresh`, {});
|
|
@@ -490,6 +582,10 @@ async function signX402Request(post, requestId, plan, creds) {
|
|
|
490
582
|
// src/client.ts
|
|
491
583
|
var DEFAULT_BASE_URL = "https://api.paybox.sh";
|
|
492
584
|
var EXPIRY_SKEW_MS = 6e4;
|
|
585
|
+
function signingResultForResponse(result, response) {
|
|
586
|
+
const exposesPlan = response.status === "pending_approval" || response.status === "pending_signature";
|
|
587
|
+
return { ...result, response, plan: exposesPlan ? result.plan : null };
|
|
588
|
+
}
|
|
493
589
|
var PayboxError = class extends Error {
|
|
494
590
|
constructor(status, body, context) {
|
|
495
591
|
super(`paybox ${context} failed (${status}): ${body}`);
|
|
@@ -525,9 +621,17 @@ var PayboxClient = class _PayboxClient {
|
|
|
525
621
|
}
|
|
526
622
|
return client;
|
|
527
623
|
}
|
|
528
|
-
|
|
624
|
+
signingCreds() {
|
|
625
|
+
if (!this.opts.signingKey) return null;
|
|
626
|
+
try {
|
|
627
|
+
return credsFromToken(this.opts.signingKey);
|
|
628
|
+
} catch {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/** Whether in-process signing is available (a parseable `pbxk1.` key is set). */
|
|
529
633
|
get canSign() {
|
|
530
|
-
return
|
|
634
|
+
return this.signingCreds() !== null;
|
|
531
635
|
}
|
|
532
636
|
async bearer() {
|
|
533
637
|
if (this.opts.token) return this.opts.token;
|
|
@@ -564,7 +668,8 @@ var PayboxClient = class _PayboxClient {
|
|
|
564
668
|
return text ? JSON.parse(text) : void 0;
|
|
565
669
|
}
|
|
566
670
|
poster = (path, body) => this.request("POST", path, { body });
|
|
567
|
-
/** List
|
|
671
|
+
/** List granted credentials plus the `ungranted` credentials (id, name, kind,
|
|
672
|
+
* safe metadata) the user owns but hasn't granted this connector. */
|
|
568
673
|
listCredentials() {
|
|
569
674
|
return this.request("GET", "/agent/grants");
|
|
570
675
|
}
|
|
@@ -574,6 +679,16 @@ var PayboxClient = class _PayboxClient {
|
|
|
574
679
|
query: { address: args.address, network_ids: args.networkIds }
|
|
575
680
|
});
|
|
576
681
|
}
|
|
682
|
+
/** Verify one confirmed Solana transaction's exact wallet/mint effect, then
|
|
683
|
+
* read the current balance at or after its slot (`verify_solana_balance`). */
|
|
684
|
+
verifySolanaBalance(args) {
|
|
685
|
+
const body = {
|
|
686
|
+
address: args.address,
|
|
687
|
+
token_mint: args.tokenMint,
|
|
688
|
+
transaction_signature: args.transactionSignature
|
|
689
|
+
};
|
|
690
|
+
return this.request("POST", "/agent/portfolio/verify-solana-balance", { body });
|
|
691
|
+
}
|
|
577
692
|
/** Charge a card credential (the `request_payment` tool). */
|
|
578
693
|
requestPayment(args) {
|
|
579
694
|
const operation = {
|
|
@@ -599,72 +714,88 @@ var PayboxClient = class _PayboxClient {
|
|
|
599
714
|
/** Sign with a wallet credential (the `request_wallet_sign` tool). Signs
|
|
600
715
|
* in-process and returns the completed request when a signing key is set and
|
|
601
716
|
* the request clears immediately; otherwise returns the pending response.
|
|
602
|
-
* Uses `/agent/wallet-sign`, which echoes the wallet binding
|
|
603
|
-
* need not resolve it
|
|
717
|
+
* Uses `/agent/wallet-sign`, which echoes the wallet binding only while
|
|
718
|
+
* approval or signature is pending so the signer need not resolve it
|
|
719
|
+
* separately. Terminal responses carry a null binding. */
|
|
604
720
|
async requestWalletSign(args, opts = {}) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
721
|
+
return this.withExpiryRetry(
|
|
722
|
+
async () => {
|
|
723
|
+
const operation = {
|
|
724
|
+
type: "wallet_sign",
|
|
725
|
+
credential_id: args.credentialId,
|
|
726
|
+
intent: args.intent
|
|
727
|
+
};
|
|
728
|
+
const { response, binding } = await this.request(
|
|
729
|
+
"POST",
|
|
730
|
+
"/agent/wallet-sign",
|
|
731
|
+
{ body: { operation } }
|
|
732
|
+
);
|
|
733
|
+
const creds = this.signingCreds();
|
|
734
|
+
if ((opts.autoSign ?? true) && response.status === "pending_signature" && creds) {
|
|
735
|
+
const sign = () => signWalletRequest(this.poster, response.request_id, args.intent, creds, binding);
|
|
736
|
+
await this.signWithReconcile(response.request_id, sign);
|
|
737
|
+
return this.getRequest(response.request_id);
|
|
738
|
+
}
|
|
739
|
+
return response;
|
|
740
|
+
},
|
|
741
|
+
(r) => r
|
|
614
742
|
);
|
|
615
|
-
if ((opts.autoSign ?? true) && response.status === "pending_signature" && this.opts.signingKey) {
|
|
616
|
-
await signWalletRequest(
|
|
617
|
-
this.poster,
|
|
618
|
-
response.request_id,
|
|
619
|
-
args.intent,
|
|
620
|
-
credsFromToken(this.opts.signingKey),
|
|
621
|
-
binding
|
|
622
|
-
);
|
|
623
|
-
return this.getRequest(response.request_id);
|
|
624
|
-
}
|
|
625
|
-
return response;
|
|
626
743
|
}
|
|
627
744
|
/** Swap tokens from a wallet credential (the `request_swap` tool). Returns the
|
|
628
|
-
* parked response
|
|
629
|
-
*
|
|
745
|
+
* parked response and its sign-ready plan while approval or signature is
|
|
746
|
+
* pending; terminal responses carry a null plan. Signs + submits in-process
|
|
747
|
+
* when a signing key is set and the swap cleared immediately. */
|
|
630
748
|
async requestSwap(args, opts = {}) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
749
|
+
return this.withExpiryRetry(
|
|
750
|
+
async () => {
|
|
751
|
+
const operation = {
|
|
752
|
+
type: "swap",
|
|
753
|
+
credential_id: args.credentialId,
|
|
754
|
+
src_chain: args.srcChain,
|
|
755
|
+
dst_chain: args.dstChain ?? args.srcChain,
|
|
756
|
+
src_token: args.srcToken,
|
|
757
|
+
dst_token: args.dstToken,
|
|
758
|
+
amount: args.amount,
|
|
759
|
+
swap_direction: args.swapDirection ?? "exact-amount-in",
|
|
760
|
+
// Keep in sync with the request_swap MCP tool default (server side).
|
|
761
|
+
slippage_bps: args.slippageBps ?? 150,
|
|
762
|
+
recipient: args.recipient ?? null,
|
|
763
|
+
value_cents: args.valueCents ?? null
|
|
764
|
+
};
|
|
765
|
+
const result = await this.request("POST", "/agent/swap", {
|
|
766
|
+
body: { operation }
|
|
767
|
+
});
|
|
768
|
+
return this.completeSwap(result, opts);
|
|
769
|
+
},
|
|
770
|
+
(r) => r.response
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
/** Complete a swap request: optionally wait out a `pending_approval` dwell,
|
|
774
|
+
* then sign the plan in-process and submit envelopes. Signing first refreshes
|
|
775
|
+
* the parked request via `/agent/requests/{id}/refresh`, so a long approval
|
|
776
|
+
* dwell never signs stale bytes. */
|
|
652
777
|
async completeSwap(result, opts) {
|
|
653
|
-
|
|
778
|
+
const creds = this.signingCreds();
|
|
779
|
+
if (!(opts.autoSign ?? true) || !creds) {
|
|
780
|
+
return signingResultForResponse(result, result.response);
|
|
781
|
+
}
|
|
654
782
|
let response = result.response;
|
|
655
783
|
if (response.status === "pending_approval" && opts.waitForApproval) {
|
|
656
784
|
response = await this.waitWhileApproval(response.request_id, opts.waitForApproval);
|
|
657
785
|
}
|
|
658
786
|
if (response.status !== "pending_signature") {
|
|
659
|
-
return
|
|
787
|
+
return signingResultForResponse(result, response);
|
|
788
|
+
}
|
|
789
|
+
const plan = result.plan;
|
|
790
|
+
if (!plan) {
|
|
791
|
+
throw new Error("paybox returned pending_signature without a swap signing plan");
|
|
660
792
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
result
|
|
665
|
-
|
|
793
|
+
const sign = () => signSwapRequest(this.poster, response.request_id, plan, creds);
|
|
794
|
+
await this.signWithReconcile(response.request_id, sign);
|
|
795
|
+
return signingResultForResponse(
|
|
796
|
+
result,
|
|
797
|
+
await this.requestAfterSign(response.request_id, opts.waitForApproval)
|
|
666
798
|
);
|
|
667
|
-
return { ...result, response: await this.getRequest(response.request_id) };
|
|
668
799
|
}
|
|
669
800
|
/** Poll while the request sits in `pending_approval` (the user is approving
|
|
670
801
|
* with their passkey); returns the first non-approval state. */
|
|
@@ -678,63 +809,121 @@ var PayboxClient = class _PayboxClient {
|
|
|
678
809
|
await new Promise((res) => setTimeout(res, interval));
|
|
679
810
|
}
|
|
680
811
|
}
|
|
681
|
-
/**
|
|
682
|
-
* `
|
|
683
|
-
*
|
|
684
|
-
*
|
|
812
|
+
/** Run an operation and, if it terminates on a server-marked expiry
|
|
813
|
+
* (`retryable` on a terminal `error`/`denied`: the window elapsed and nothing
|
|
814
|
+
* was signed or spent), re-issue it exactly once (SPE-456). Never loops: the
|
|
815
|
+
* second attempt's result is returned as-is. */
|
|
816
|
+
async withExpiryRetry(run, responseOf) {
|
|
817
|
+
const first = await run();
|
|
818
|
+
const r = responseOf(first);
|
|
819
|
+
if ((r.status === "error" || r.status === "denied") && r.retryable) {
|
|
820
|
+
return run();
|
|
821
|
+
}
|
|
822
|
+
return first;
|
|
823
|
+
}
|
|
824
|
+
/** Sign a parked request; if the sign/submit call fails, reconcile against the
|
|
825
|
+
* authoritative state once (SPE-456). The server leaves a pre-broadcast
|
|
826
|
+
* completion failure recoverable — the claim is released and the row stays
|
|
827
|
+
* `pending_signature` — so re-sign a still-open request a single time; a
|
|
828
|
+
* terminal or already-broadcast row is left as-is for the caller to read (and
|
|
829
|
+
* for `withExpiryRetry` to re-issue if it is retryable). Bounded to one extra
|
|
830
|
+
* attempt: a second failure propagates. */
|
|
831
|
+
async signWithReconcile(requestId, sign) {
|
|
832
|
+
try {
|
|
833
|
+
await sign();
|
|
834
|
+
} catch {
|
|
835
|
+
const reconciled = await this.getRequest(requestId);
|
|
836
|
+
if (reconciled.status !== "pending_signature") return;
|
|
837
|
+
await sign();
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
/** Discover x402 services with the original array-returning response. */
|
|
685
841
|
async discoverServices(query, opts = {}) {
|
|
686
|
-
const
|
|
687
|
-
query: {
|
|
842
|
+
const result = await this.request("GET", "/agent/services", {
|
|
843
|
+
query: {
|
|
844
|
+
query,
|
|
845
|
+
network: opts.network === "all" ? void 0 : opts.network,
|
|
846
|
+
raw: opts.raw ? "true" : void 0
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
return result.services;
|
|
850
|
+
}
|
|
851
|
+
/** Discover one bounded page of x402 services. Reuse the returned cursor with
|
|
852
|
+
* the same query and network while `has_more` is true. */
|
|
853
|
+
async discoverServicesPage(query, opts = {}) {
|
|
854
|
+
return this.request("GET", "/agent/services", {
|
|
855
|
+
query: {
|
|
856
|
+
query,
|
|
857
|
+
network: opts.network === "all" ? void 0 : opts.network,
|
|
858
|
+
limit: (opts.limit ?? 10).toString(),
|
|
859
|
+
cursor: opts.cursor,
|
|
860
|
+
raw: opts.raw ? "true" : void 0
|
|
861
|
+
}
|
|
688
862
|
});
|
|
689
|
-
return r.services;
|
|
690
863
|
}
|
|
691
864
|
/** Pay a paid x402 endpoint from a wallet credential (the `pay_x402` tool,
|
|
692
865
|
* header mode). Signs in-process when a signing key is set and the payment
|
|
693
866
|
* clears immediately — then the X-PAYMENT header is on the completed
|
|
694
867
|
* response's `output.value.x_payment` (replay it on the original request). */
|
|
695
868
|
async payX402(args, opts = {}) {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
869
|
+
return this.withExpiryRetry(
|
|
870
|
+
async () => {
|
|
871
|
+
const result = await this.request("POST", "/agent/x402", {
|
|
872
|
+
body: {
|
|
873
|
+
credential_id: args.credentialId,
|
|
874
|
+
accepts: args.accepts,
|
|
875
|
+
resource_url: args.resourceUrl
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
return this.completeX402(result, opts);
|
|
879
|
+
},
|
|
880
|
+
(r) => r.response
|
|
881
|
+
);
|
|
704
882
|
}
|
|
705
883
|
/** Fetch a paid x402 resource and have paybox pay for it (the `use_service`
|
|
706
884
|
* tool, gateway mode): paybox probes the url, pays the 402, re-fetches with the
|
|
707
885
|
* payment, and returns the resource's reply on `output.value.response`. */
|
|
708
886
|
async useService(args, opts = {}) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
887
|
+
return this.withExpiryRetry(
|
|
888
|
+
async () => {
|
|
889
|
+
const result = await this.request("POST", "/agent/use-service", {
|
|
890
|
+
body: {
|
|
891
|
+
credential_id: args.credentialId,
|
|
892
|
+
url: args.url,
|
|
893
|
+
method: args.method,
|
|
894
|
+
body: args.body
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
return this.completeX402(result, opts);
|
|
898
|
+
},
|
|
899
|
+
(r) => r.response
|
|
900
|
+
);
|
|
718
901
|
}
|
|
719
902
|
/** Shared completion for the two x402 entry points: optionally wait out a
|
|
720
903
|
* `pending_approval` dwell, then sign the parked typed data in-process and
|
|
721
904
|
* return the finished request, exactly like the swap path. */
|
|
722
905
|
async completeX402(result, opts) {
|
|
723
|
-
|
|
906
|
+
const creds = this.signingCreds();
|
|
907
|
+
if (!(opts.autoSign ?? true) || !creds) {
|
|
908
|
+
return signingResultForResponse(result, result.response);
|
|
909
|
+
}
|
|
724
910
|
let response = result.response;
|
|
725
911
|
if (response.status === "pending_approval" && opts.waitForApproval) {
|
|
726
912
|
response = await this.waitWhileApproval(response.request_id, opts.waitForApproval);
|
|
727
913
|
}
|
|
728
914
|
if (response.status !== "pending_signature") {
|
|
729
|
-
return
|
|
915
|
+
return signingResultForResponse(result, response);
|
|
730
916
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
917
|
+
const plan = result.plan;
|
|
918
|
+
if (!plan) {
|
|
919
|
+
throw new Error("paybox returned pending_signature without an x402 signing plan");
|
|
920
|
+
}
|
|
921
|
+
const sign = () => signX402Request(this.poster, response.request_id, plan, creds);
|
|
922
|
+
await this.signWithReconcile(response.request_id, sign);
|
|
923
|
+
return signingResultForResponse(
|
|
924
|
+
result,
|
|
925
|
+
await this.requestAfterSign(response.request_id, opts.waitForApproval)
|
|
736
926
|
);
|
|
737
|
-
return { ...result, response: await this.getRequest(response.request_id) };
|
|
738
927
|
}
|
|
739
928
|
/** Claim the usable card credentials of an approved payment (the
|
|
740
929
|
* `claim_payment_credentials` tool). One-time: the claim is consumed. */
|
|
@@ -754,15 +943,13 @@ var PayboxClient = class _PayboxClient {
|
|
|
754
943
|
return this.request("GET", "/agent/buy-link", {
|
|
755
944
|
query: {
|
|
756
945
|
credential_id: args.credentialId,
|
|
946
|
+
destination_chain: args.destinationChain,
|
|
757
947
|
currency_code: args.currencyCode,
|
|
758
948
|
amount_usd: args.amountUsd?.toString()
|
|
759
949
|
}
|
|
760
950
|
});
|
|
761
951
|
}
|
|
762
|
-
// ---- World
|
|
763
|
-
// the `world` plugin in the paybox app. Reads are discovery-only; `worldTrade`
|
|
764
|
-
// parks a force-approved PredictionTrade the SDK signs in-process after the
|
|
765
|
-
// user approves (pass `waitForApproval` to wait + sign headlessly). ----
|
|
952
|
+
// ---- World REST discovery; money tools require the geofenced MCP window. ----
|
|
766
953
|
/** Browse World prediction markets (the `world_find_markets` tool). */
|
|
767
954
|
worldMarkets(args = {}) {
|
|
768
955
|
return this.request("GET", "/agent/world/markets", {
|
|
@@ -812,28 +999,13 @@ var PayboxClient = class _PayboxClient {
|
|
|
812
999
|
body: { addresses }
|
|
813
1000
|
});
|
|
814
1001
|
}
|
|
815
|
-
/**
|
|
1002
|
+
/** A wallet's World prediction-market positions: classified `positions` /
|
|
1003
|
+
* `settled` entries (each with a `position_result`) plus `settlement_cash`
|
|
1004
|
+
* for a settled winner's CASH cash-out (swap CASH to USDC via request_swap).
|
|
1005
|
+
* The `world_positions` tool. */
|
|
816
1006
|
worldPositions(address) {
|
|
817
1007
|
return this.request("GET", "/agent/world/positions", { query: { address } });
|
|
818
1008
|
}
|
|
819
|
-
/** Trade a World prediction market (the `world_buy_outcome` /
|
|
820
|
-
* `world_change_position` / `world_redeem` tools). Always parks as
|
|
821
|
-
* `pending_approval` (trades are force-approved); pass
|
|
822
|
-
* `{ waitForApproval: true }` to wait for the user's passkey approval and
|
|
823
|
-
* sign in-process. */
|
|
824
|
-
async worldTrade(args, opts = {}) {
|
|
825
|
-
const result = await this.request("POST", "/agent/world/trade", {
|
|
826
|
-
body: {
|
|
827
|
-
credential_id: args.credentialId,
|
|
828
|
-
market_mint: args.marketMint,
|
|
829
|
-
side: args.side,
|
|
830
|
-
size: args.size,
|
|
831
|
-
slippage_bps: args.slippageBps,
|
|
832
|
-
value_cents: args.valueCents
|
|
833
|
-
}
|
|
834
|
-
});
|
|
835
|
-
return this.completeSwap(result, opts);
|
|
836
|
-
}
|
|
837
1009
|
// ---- Hyperliquid plugin (read-only spot discovery) — requires the user to
|
|
838
1010
|
// have enabled the `hyperliquid` plugin in the paybox app. ----
|
|
839
1011
|
/** Hyperliquid spot markets (the `hyperliquid_markets` tool). */
|
|
@@ -852,6 +1024,12 @@ var PayboxClient = class _PayboxClient {
|
|
|
852
1024
|
getRequest(requestId) {
|
|
853
1025
|
return this.request("GET", `/agent/requests/${requestId}`);
|
|
854
1026
|
}
|
|
1027
|
+
/** After in-process sign, `--wait` polls through settlement/confirmation;
|
|
1028
|
+
* without it, return the first snapshot (the historical no-wait contract). */
|
|
1029
|
+
requestAfterSign(requestId, wait) {
|
|
1030
|
+
if (!wait) return this.getRequest(requestId);
|
|
1031
|
+
return this.waitForRequest(requestId, typeof wait === "object" ? wait : {});
|
|
1032
|
+
}
|
|
855
1033
|
/** Poll until the request leaves a pending state (or the timeout elapses). */
|
|
856
1034
|
async waitForRequest(requestId, opts = {}) {
|
|
857
1035
|
const interval = opts.intervalMs ?? 2e3;
|
|
@@ -871,6 +1049,7 @@ export {
|
|
|
871
1049
|
agentClientId,
|
|
872
1050
|
openBrowser,
|
|
873
1051
|
loginLoopback,
|
|
1052
|
+
loginDevice,
|
|
874
1053
|
refreshTokens,
|
|
875
1054
|
configDir,
|
|
876
1055
|
configFile,
|
|
@@ -880,4 +1059,4 @@ export {
|
|
|
880
1059
|
PayboxError,
|
|
881
1060
|
PayboxClient
|
|
882
1061
|
};
|
|
883
|
-
//# sourceMappingURL=chunk-
|
|
1062
|
+
//# sourceMappingURL=chunk-7VW4QMV4.js.map
|