@paybox-sh/sdk 0.1.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.
@@ -0,0 +1,621 @@
1
+ // src/auth.ts
2
+ import { spawn } from "node:child_process";
3
+ import { createHash, randomBytes } from "node:crypto";
4
+ import { createServer } from "node:http";
5
+ function mcpResource(baseUrl) {
6
+ return `${baseUrl.replace(/\/+$/, "")}/mcp`;
7
+ }
8
+ async function metadata(baseUrl) {
9
+ const url = `${baseUrl.replace(/\/+$/, "")}/.well-known/oauth-authorization-server`;
10
+ const res = await fetch(url);
11
+ if (!res.ok) {
12
+ throw new Error(`could not read OAuth metadata from ${url} (${res.status})`);
13
+ }
14
+ return await res.json();
15
+ }
16
+ function base64url(b) {
17
+ return b.toString("base64url");
18
+ }
19
+ function pkce() {
20
+ const verifier = base64url(randomBytes(32));
21
+ const challenge = base64url(createHash("sha256").update(verifier).digest());
22
+ return { verifier, challenge };
23
+ }
24
+ function openBrowser(url) {
25
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : ["xdg-open", [url]];
26
+ try {
27
+ spawn(cmd, [...args], { stdio: "ignore", detached: true }).unref();
28
+ } catch {
29
+ }
30
+ }
31
+ 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>';
32
+ async function loginLoopback(baseUrl, opts = {}) {
33
+ const scope = opts.scope ?? "mcp offline_access";
34
+ const clientName = opts.clientName ?? "Paybox CLI";
35
+ const log = opts.log ?? ((m) => console.error(m));
36
+ const meta = await metadata(baseUrl);
37
+ const resource = mcpResource(baseUrl);
38
+ let resolveCode;
39
+ let rejectCode;
40
+ const codePromise = new Promise((res, rej) => {
41
+ resolveCode = res;
42
+ rejectCode = rej;
43
+ });
44
+ const server = createServer((req, res) => {
45
+ const u = new URL(req.url ?? "/", "http://127.0.0.1");
46
+ if (u.pathname !== "/callback") {
47
+ res.writeHead(404).end();
48
+ return;
49
+ }
50
+ res.writeHead(200, { "content-type": "text/html" }).end(CALLBACK_PAGE);
51
+ const code = u.searchParams.get("code");
52
+ if (code) resolveCode({ code, state: u.searchParams.get("state") });
53
+ else rejectCode(new Error(u.searchParams.get("error") ?? "authorization failed"));
54
+ });
55
+ await new Promise((r) => server.listen(0, "127.0.0.1", r));
56
+ const port = server.address().port;
57
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
58
+ const timeoutMs = opts.timeoutMs ?? 3e5;
59
+ const timer = setTimeout(
60
+ () => rejectCode(new Error(`login timed out after ${Math.round(timeoutMs / 1e3)}s \u2014 no approval received`)),
61
+ timeoutMs
62
+ );
63
+ timer.unref();
64
+ try {
65
+ const reg = await fetch(meta.registration_endpoint, {
66
+ method: "POST",
67
+ headers: { "content-type": "application/json" },
68
+ body: JSON.stringify({
69
+ client_name: clientName,
70
+ redirect_uris: [redirectUri],
71
+ token_endpoint_auth_method: "none",
72
+ grant_types: ["authorization_code", "refresh_token"],
73
+ response_types: ["code"]
74
+ })
75
+ });
76
+ if (!reg.ok) throw new Error(`client registration failed (${reg.status}): ${await reg.text()}`);
77
+ const { client_id: clientId } = await reg.json();
78
+ const { verifier, challenge } = pkce();
79
+ const state = base64url(randomBytes(16));
80
+ const authUrl = `${meta.authorization_endpoint}?response_type=code&client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${challenge}&code_challenge_method=S256&scope=${encodeURIComponent(scope)}&state=${state}&resource=${encodeURIComponent(resource)}`;
81
+ log(`Opening your browser to approve access:
82
+ ${authUrl}
83
+ `);
84
+ openBrowser(authUrl);
85
+ const { code, state: returnedState } = await codePromise;
86
+ if (returnedState !== state) throw new Error("OAuth state mismatch \u2014 aborting");
87
+ const form = new URLSearchParams({
88
+ grant_type: "authorization_code",
89
+ code,
90
+ redirect_uri: redirectUri,
91
+ client_id: clientId,
92
+ code_verifier: verifier,
93
+ resource
94
+ });
95
+ const tok = await fetch(meta.token_endpoint, {
96
+ method: "POST",
97
+ headers: { "content-type": "application/x-www-form-urlencoded" },
98
+ body: form
99
+ });
100
+ if (!tok.ok) throw new Error(`token exchange failed (${tok.status}): ${await tok.text()}`);
101
+ const t = await tok.json();
102
+ return {
103
+ clientId,
104
+ accessToken: t.access_token,
105
+ refreshToken: t.refresh_token,
106
+ expiresAt: t.expires_in ? Date.now() + t.expires_in * 1e3 : void 0,
107
+ resource
108
+ };
109
+ } finally {
110
+ clearTimeout(timer);
111
+ server.close();
112
+ }
113
+ }
114
+ async function refreshTokens(baseUrl, current) {
115
+ if (!current.refreshToken) throw new Error("no refresh token \u2014 run `paybox login` again");
116
+ const meta = await metadata(baseUrl);
117
+ const resource = current.resource ?? mcpResource(baseUrl);
118
+ const form = new URLSearchParams({
119
+ grant_type: "refresh_token",
120
+ refresh_token: current.refreshToken,
121
+ client_id: current.clientId,
122
+ resource
123
+ });
124
+ const res = await fetch(meta.token_endpoint, {
125
+ method: "POST",
126
+ headers: { "content-type": "application/x-www-form-urlencoded" },
127
+ body: form
128
+ });
129
+ if (!res.ok) throw new Error(`token refresh failed (${res.status}): ${await res.text()}`);
130
+ const t = await res.json();
131
+ return {
132
+ ...current,
133
+ accessToken: t.access_token,
134
+ refreshToken: t.refresh_token ?? current.refreshToken,
135
+ expiresAt: t.expires_in ? Date.now() + t.expires_in * 1e3 : void 0,
136
+ resource
137
+ };
138
+ }
139
+
140
+ // src/config.ts
141
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
142
+ import { homedir } from "node:os";
143
+ import { join } from "node:path";
144
+ function configDir() {
145
+ if (process.env.PAYBOX_CONFIG_DIR) return process.env.PAYBOX_CONFIG_DIR;
146
+ const base = process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
147
+ return join(base, "paybox");
148
+ }
149
+ function configFile() {
150
+ return join(configDir(), "config.json");
151
+ }
152
+ function loadConfig() {
153
+ try {
154
+ return JSON.parse(readFileSync(configFile(), "utf8"));
155
+ } catch {
156
+ return {};
157
+ }
158
+ }
159
+ function saveConfig(config) {
160
+ mkdirSync(configDir(), { recursive: true, mode: 448 });
161
+ writeFileSync(configFile(), `${JSON.stringify(config, null, 2)}
162
+ `, { mode: 384 });
163
+ if (process.platform !== "win32") chmodSync(configFile(), 384);
164
+ }
165
+
166
+ // 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
167
+ var TOKEN_PREFIX = "pbxk1.";
168
+ function validateHex(s, label) {
169
+ const trimmed = s.trim();
170
+ if (!trimmed) return `${label} is required`;
171
+ const body = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed.slice(2) : trimmed;
172
+ if (body.length === 0) return `${label} is empty`;
173
+ if (body.length % 2 !== 0) return `${label} must have an even number of hex digits`;
174
+ if (!/^[0-9a-fA-F]+$/.test(body)) return `${label} must be hexadecimal`;
175
+ return null;
176
+ }
177
+ function b64urlDecode(s) {
178
+ let b = s.replace(/-/g, "+").replace(/_/g, "/");
179
+ while (b.length % 4) b += "=";
180
+ return atob(b);
181
+ }
182
+ function parseToken(input) {
183
+ const t = input.trim();
184
+ if (!t) return null;
185
+ const body = t.startsWith(TOKEN_PREFIX) ? t.slice(TOKEN_PREFIX.length) : t;
186
+ let obj;
187
+ try {
188
+ obj = JSON.parse(b64urlDecode(body));
189
+ } catch {
190
+ return null;
191
+ }
192
+ if (!obj || typeof obj !== "object") return null;
193
+ const o = obj;
194
+ const apiPubHex = String(o.p ?? o.apiPubHex ?? "").trim();
195
+ const apiPrivHex = String(o.s ?? o.apiPrivHex ?? "").trim();
196
+ if (!apiPubHex || !apiPrivHex) return null;
197
+ if (validateHex(apiPubHex, "key") || validateHex(apiPrivHex, "key")) return null;
198
+ return { apiPubHex, apiPrivHex };
199
+ }
200
+
201
+ // 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
202
+ import { Buffer } from "buffer";
203
+ import nacl from "tweetnacl";
204
+ import { MoonXEvmSigner, EvmSchemeMismatchError } from "@moon-x/evm-adapter";
205
+ import { MoonXSolanaSigner, SolanaSchemeMismatchError } from "@moon-x/solana-adapter";
206
+ import { Transaction } from "@solana/web3.js";
207
+ import { keccak256, serializeTransaction } from "viem";
208
+ function reviveTx(w) {
209
+ return {
210
+ type: "eip1559",
211
+ to: w.to,
212
+ value: BigInt(w.value),
213
+ data: w.data,
214
+ chainId: w.chainId,
215
+ gas: BigInt(w.gas),
216
+ maxPriorityFeePerGas: BigInt(w.maxPriorityFeePerGas),
217
+ maxFeePerGas: BigInt(w.maxFeePerGas),
218
+ nonce: w.nonce
219
+ };
220
+ }
221
+ function hexToBytes(hex) {
222
+ return new Uint8Array(Buffer.from(hex.replace(/^0x/, ""), "hex"));
223
+ }
224
+ function bytesToHex(bytes) {
225
+ return Buffer.from(bytes).toString("hex");
226
+ }
227
+ function buildEnvelope(rawSigningPayloadHex, binding, creds) {
228
+ const signedPayload = {
229
+ raw_signing_payload: rawSigningPayloadHex.replace(/^0x/, "").toLowerCase(),
230
+ key_id: binding.keyId,
231
+ derivation_path: binding.derivationPath,
232
+ issued_at: (/* @__PURE__ */ new Date()).toISOString()
233
+ };
234
+ const signedBodyBytes = new TextEncoder().encode(JSON.stringify(signedPayload));
235
+ const keyPair = nacl.sign.keyPair.fromSeed(hexToBytes(creds.apiPrivHex));
236
+ const agentSig = nacl.sign.detached(signedBodyBytes, keyPair.secretKey);
237
+ return {
238
+ signed_body: bytesToHex(signedBodyBytes),
239
+ agent_signature: bytesToHex(agentSig)
240
+ };
241
+ }
242
+ function eip1559SigningHash(tx) {
243
+ return keccak256(serializeTransaction(reviveTx(tx)));
244
+ }
245
+ function solanaSigningPayload(base64Tx) {
246
+ const raw = Buffer.from(base64Tx, "base64");
247
+ let i = 0;
248
+ let sigCount = 0;
249
+ let shift = 0;
250
+ for (; ; ) {
251
+ const b = raw[i++];
252
+ sigCount |= (b & 127) << shift;
253
+ if ((b & 128) === 0) break;
254
+ shift += 7;
255
+ }
256
+ if (sigCount !== 1) {
257
+ throw new Error(`expected a single-signer solana tx, got ${sigCount} signature slot(s)`);
258
+ }
259
+ return bytesToHex(new Uint8Array(raw.subarray(i + 64)));
260
+ }
261
+ function buildSwapEnvelopes(txs, binding, creds) {
262
+ if (!binding.walletId) {
263
+ throw new Error("paybox did not supply a wallet id for this operation.");
264
+ }
265
+ if (!binding.keyId || !binding.derivationPath) {
266
+ throw new Error(
267
+ "This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the paybox app, then retry."
268
+ );
269
+ }
270
+ return txs.map((t) => {
271
+ const payload = t.vm === "solana" ? solanaSigningPayload(t.transaction) : eip1559SigningHash(t.transaction);
272
+ const { signed_body, agent_signature } = buildEnvelope(payload, binding, creds);
273
+ return { api_pub: creds.apiPubHex, signed_body, agent_signature };
274
+ });
275
+ }
276
+ function buildIssuedAtEnvelope(creds) {
277
+ const signedBodyBytes = new TextEncoder().encode(
278
+ JSON.stringify({ issued_at: (/* @__PURE__ */ new Date()).toISOString() })
279
+ );
280
+ const keyPair = nacl.sign.keyPair.fromSeed(hexToBytes(creds.apiPrivHex));
281
+ const agentSig = nacl.sign.detached(signedBodyBytes, keyPair.secretKey);
282
+ return {
283
+ signed_body: bytesToHex(signedBodyBytes),
284
+ agent_signature: bytesToHex(agentSig)
285
+ };
286
+ }
287
+ async function signIntent(intent, creds, binding, transport) {
288
+ if (!binding.walletId) {
289
+ return { ok: false, error: "paybox did not supply a wallet id for this operation." };
290
+ }
291
+ if (!binding.keyId || !binding.derivationPath) {
292
+ return {
293
+ ok: false,
294
+ error: "This wallet isn't provisioned for signing yet (missing key binding). Re-provision it in the paybox app, then retry."
295
+ };
296
+ }
297
+ const ecdsaSign = async (digest) => {
298
+ const result = await transport(buildEnvelope(digest, binding, creds));
299
+ const { r, s, recovery_id } = result.signature;
300
+ if (r == null || s == null || recovery_id == null) {
301
+ throw new Error("MoonX returned an ECDSA result without {r, s, recovery_id}");
302
+ }
303
+ return {
304
+ r: `0x${r}`,
305
+ s: `0x${s}`,
306
+ yParity: recovery_id
307
+ };
308
+ };
309
+ const ed25519Sign = async (message) => {
310
+ const result = await transport(buildEnvelope(bytesToHex(message), binding, creds));
311
+ const sig = result.signature.signature;
312
+ if (!sig) throw new Error("MoonX returned an Ed25519 result without a signature");
313
+ return hexToBytes(sig);
314
+ };
315
+ try {
316
+ switch (intent.op) {
317
+ case "message":
318
+ return {
319
+ ok: true,
320
+ artifact: { signature: await new MoonXEvmSigner(ecdsaSign).signMessage(intent.message) }
321
+ };
322
+ case "typedData":
323
+ return {
324
+ ok: true,
325
+ artifact: {
326
+ signature: await new MoonXEvmSigner(ecdsaSign).signTypedData(intent.typedData)
327
+ }
328
+ };
329
+ case "transaction":
330
+ return {
331
+ ok: true,
332
+ artifact: {
333
+ serializedTransaction: await new MoonXEvmSigner(ecdsaSign).signTransaction(
334
+ reviveTx(intent.transaction)
335
+ )
336
+ }
337
+ };
338
+ case "authorization":
339
+ return {
340
+ ok: true,
341
+ artifact: {
342
+ authorization: await new MoonXEvmSigner(ecdsaSign).signAuthorization(
343
+ intent.authorization
344
+ )
345
+ }
346
+ };
347
+ case "solanaMessage": {
348
+ const sig = await new MoonXSolanaSigner(ed25519Sign, intent.address).signMessage(
349
+ intent.message
350
+ );
351
+ return { ok: true, artifact: { signature: bytesToHex(sig) } };
352
+ }
353
+ case "solanaTransaction": {
354
+ const tx = Transaction.from(Buffer.from(intent.transactionBase64, "base64"));
355
+ const signed = await new MoonXSolanaSigner(ed25519Sign, intent.address).signTransaction(
356
+ tx
357
+ );
358
+ return {
359
+ ok: true,
360
+ artifact: { signedTransactionBase64: Buffer.from(signed).toString("base64") }
361
+ };
362
+ }
363
+ case "raw": {
364
+ const result = await transport(buildEnvelope(intent.rawSigningPayloadHex, binding, creds));
365
+ return { ok: true, artifact: { scheme: result.scheme, signature: result.signature } };
366
+ }
367
+ default:
368
+ return { ok: false, error: `unknown op: ${intent.op}` };
369
+ }
370
+ } catch (err) {
371
+ if (err instanceof EvmSchemeMismatchError || err instanceof SolanaSchemeMismatchError) {
372
+ return { ok: false, error: err.message };
373
+ }
374
+ const message = err instanceof Error ? err.message : String(err);
375
+ return { ok: false, error: message };
376
+ }
377
+ }
378
+
379
+ // src/signer.ts
380
+ function credsFromToken(token) {
381
+ const kp = parseToken(token);
382
+ if (!kp) {
383
+ throw new Error("invalid signing key: expected a `pbxk1.` token (keypair only)");
384
+ }
385
+ return { apiPubHex: kp.apiPubHex, apiPrivHex: kp.apiPrivHex, savedAt: Date.now() };
386
+ }
387
+ function toBinding(b) {
388
+ return { walletId: b.wallet_id, keyId: b.key_id ?? "", derivationPath: b.derivation_path ?? "" };
389
+ }
390
+ async function resolveBinding(post, requestId, creds) {
391
+ const env = buildIssuedAtEnvelope(creds);
392
+ const r = await post(`/agent/requests/${requestId}/binding`, {
393
+ api_pub: creds.apiPubHex,
394
+ signed_body: env.signed_body,
395
+ agent_signature: env.agent_signature
396
+ });
397
+ return toBinding(r);
398
+ }
399
+ async function signWalletRequest(post, requestId, intent, creds, binding) {
400
+ const resolved = binding && binding.key_id && binding.derivation_path ? toBinding(binding) : await resolveBinding(post, requestId, creds);
401
+ const transport = (envelope) => post(`/agent/requests/${requestId}/moonx-sign`, {
402
+ api_pub: creds.apiPubHex,
403
+ ...envelope
404
+ });
405
+ const result = await signIntent(intent, creds, resolved, transport);
406
+ if (!result.ok) throw new Error(result.error);
407
+ await post(`/agent/requests/${requestId}/signature`, { artifact: result.artifact });
408
+ return result.artifact;
409
+ }
410
+ async function signSwapRequest(post, requestId, plan, creds) {
411
+ let binding = {
412
+ walletId: plan.binding.wallet_id,
413
+ keyId: plan.binding.key_id ?? "",
414
+ derivationPath: plan.binding.derivation_path ?? ""
415
+ };
416
+ if (binding.walletId && (!binding.keyId || !binding.derivationPath)) {
417
+ binding = await resolveBinding(post, requestId, creds);
418
+ }
419
+ const envelopes = buildSwapEnvelopes(plan.txs, binding, creds);
420
+ await post(`/agent/requests/${requestId}/envelopes`, { envelopes });
421
+ }
422
+
423
+ // src/client.ts
424
+ var DEFAULT_BASE_URL = "https://api.paybox.sh";
425
+ var EXPIRY_SKEW_MS = 6e4;
426
+ var PayboxError = class extends Error {
427
+ constructor(status, body, context) {
428
+ super(`paybox ${context} failed (${status}): ${body}`);
429
+ this.status = status;
430
+ this.body = body;
431
+ this.name = "PayboxError";
432
+ }
433
+ };
434
+ var PayboxClient = class _PayboxClient {
435
+ baseUrl;
436
+ opts;
437
+ fetchImpl;
438
+ oauth;
439
+ configBacked = false;
440
+ constructor(options = {}) {
441
+ this.opts = options;
442
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
443
+ this.fetchImpl = options.fetchImpl ?? fetch;
444
+ }
445
+ /** Build a client from `~/.config/paybox/config.json` + env, with overrides. */
446
+ static fromConfig(overrides = {}) {
447
+ const cfg = loadConfig();
448
+ const client = new _PayboxClient({
449
+ baseUrl: overrides.baseUrl ?? process.env.PAYBOX_API_URL ?? cfg.baseUrl,
450
+ apiKey: overrides.apiKey ?? process.env.PAYBOX_API_KEY ?? cfg.apiKey,
451
+ token: overrides.token ?? process.env.PAYBOX_ACCESS_TOKEN,
452
+ signingKey: overrides.signingKey ?? process.env.PAYBOX_SIGNING_KEY ?? cfg.signingKey,
453
+ fetchImpl: overrides.fetchImpl
454
+ });
455
+ if (!client.opts.apiKey && !client.opts.token && cfg.oauth) {
456
+ client.oauth = cfg.oauth;
457
+ client.configBacked = true;
458
+ }
459
+ return client;
460
+ }
461
+ /** Whether in-process signing is available (a `pbxk1.` key is configured). */
462
+ get canSign() {
463
+ return Boolean(this.opts.signingKey);
464
+ }
465
+ async bearer() {
466
+ if (this.opts.token) return this.opts.token;
467
+ if (this.opts.apiKey) return this.opts.apiKey;
468
+ if (this.oauth) {
469
+ if (this.oauth.expiresAt && Date.now() > this.oauth.expiresAt - EXPIRY_SKEW_MS) {
470
+ this.oauth = await refreshTokens(this.baseUrl, this.oauth);
471
+ if (this.configBacked) {
472
+ const cfg = loadConfig();
473
+ cfg.oauth = this.oauth;
474
+ saveConfig(cfg);
475
+ }
476
+ }
477
+ return this.oauth.accessToken;
478
+ }
479
+ throw new Error(
480
+ "not authenticated \u2014 run `paybox login`, set PAYBOX_API_KEY, or pass { apiKey } / { token }"
481
+ );
482
+ }
483
+ async request(method, path, init = {}) {
484
+ const url = new URL(this.baseUrl + path);
485
+ for (const [k, v] of Object.entries(init.query ?? {})) {
486
+ if (v != null) url.searchParams.set(k, v);
487
+ }
488
+ const headers = { authorization: `Bearer ${await this.bearer()}` };
489
+ let body;
490
+ if (init.body !== void 0) {
491
+ headers["content-type"] = "application/json";
492
+ body = JSON.stringify(init.body);
493
+ }
494
+ const res = await this.fetchImpl(url, { method, headers, body });
495
+ const text = await res.text();
496
+ if (!res.ok) throw new PayboxError(res.status, text, `${method} ${path}`);
497
+ return text ? JSON.parse(text) : void 0;
498
+ }
499
+ poster = (path, body) => this.request("POST", path, { body });
500
+ /** List the credentials this client may use (the `list_credentials` tool). */
501
+ listCredentials() {
502
+ return this.request("GET", "/agent/grants");
503
+ }
504
+ /** Token balances across chains (the `get_portfolio` tool). */
505
+ getPortfolio(args) {
506
+ return this.request("GET", "/agent/portfolio", {
507
+ query: { address: args.address, network_ids: args.networkIds }
508
+ });
509
+ }
510
+ /** Charge a card credential (the `request_payment` tool). */
511
+ requestPayment(args) {
512
+ const operation = {
513
+ type: "payment",
514
+ credential_id: args.credentialId,
515
+ merchant: args.merchant,
516
+ merchant_url: args.merchantUrl,
517
+ amount_cents: args.amountCents,
518
+ currency: args.currency ?? "USD"
519
+ };
520
+ return this.request("POST", "/agent/request", { body: { operation } });
521
+ }
522
+ /** Reveal a secret credential (the `request_secret` tool). */
523
+ requestSecret(args) {
524
+ const operation = {
525
+ type: "secret",
526
+ credential_id: args.credentialId,
527
+ raw: args.raw ?? false,
528
+ purpose: args.purpose ?? null
529
+ };
530
+ return this.request("POST", "/agent/request", { body: { operation } });
531
+ }
532
+ /** Sign with a wallet credential (the `request_wallet_sign` tool). Signs
533
+ * in-process and returns the completed request when a signing key is set and
534
+ * the request clears immediately; otherwise returns the pending response.
535
+ * Uses `/agent/wallet-sign`, which echoes the wallet binding so the signer
536
+ * need not resolve it separately. */
537
+ async requestWalletSign(args, opts = {}) {
538
+ const operation = {
539
+ type: "wallet_sign",
540
+ credential_id: args.credentialId,
541
+ chain: args.chain,
542
+ contract: args.contract ?? null,
543
+ function: args.fn ?? null,
544
+ value_cents: args.valueCents ?? null,
545
+ intent: args.intent
546
+ };
547
+ const { response, binding } = await this.request(
548
+ "POST",
549
+ "/agent/wallet-sign",
550
+ { body: { operation } }
551
+ );
552
+ if ((opts.autoSign ?? true) && response.status === "pending_signature" && this.opts.signingKey) {
553
+ await signWalletRequest(
554
+ this.poster,
555
+ response.request_id,
556
+ args.intent,
557
+ credsFromToken(this.opts.signingKey),
558
+ binding
559
+ );
560
+ return this.getRequest(response.request_id);
561
+ }
562
+ return response;
563
+ }
564
+ /** Swap tokens from a wallet credential (the `request_swap` tool). Returns the
565
+ * parked response + sign-ready plan; signs + submits in-process when a signing
566
+ * key is set and the swap cleared immediately. */
567
+ async requestSwap(args, opts = {}) {
568
+ const operation = {
569
+ type: "swap",
570
+ credential_id: args.credentialId,
571
+ src_chain: args.srcChain,
572
+ dst_chain: args.dstChain ?? args.srcChain,
573
+ src_token: args.srcToken,
574
+ dst_token: args.dstToken,
575
+ amount: args.amount,
576
+ swap_direction: args.swapDirection ?? "exact-amount-in",
577
+ slippage_bps: args.slippageBps ?? 50,
578
+ recipient: args.recipient ?? null,
579
+ value_cents: args.valueCents ?? null
580
+ };
581
+ const result = await this.request("POST", "/agent/swap", { body: { operation } });
582
+ if ((opts.autoSign ?? true) && result.response.status === "pending_signature" && this.opts.signingKey) {
583
+ await signSwapRequest(
584
+ this.poster,
585
+ result.response.request_id,
586
+ result.plan,
587
+ credsFromToken(this.opts.signingKey)
588
+ );
589
+ return { ...result, response: await this.getRequest(result.response.request_id) };
590
+ }
591
+ return result;
592
+ }
593
+ /** Poll a request's status (the `get_request` tool). */
594
+ getRequest(requestId) {
595
+ return this.request("GET", `/agent/requests/${requestId}`);
596
+ }
597
+ /** Poll until the request leaves a pending state (or the timeout elapses). */
598
+ async waitForRequest(requestId, opts = {}) {
599
+ const interval = opts.intervalMs ?? 2e3;
600
+ const deadline = Date.now() + (opts.timeoutMs ?? 3e5);
601
+ for (; ; ) {
602
+ const r = await this.getRequest(requestId);
603
+ const pending = r.status === "pending_approval" || r.status === "pending_signature";
604
+ if (!pending || Date.now() > deadline) return r;
605
+ await new Promise((res) => setTimeout(res, interval));
606
+ }
607
+ }
608
+ };
609
+
610
+ export {
611
+ mcpResource,
612
+ loginLoopback,
613
+ refreshTokens,
614
+ configFile,
615
+ loadConfig,
616
+ saveConfig,
617
+ credsFromToken,
618
+ PayboxError,
619
+ PayboxClient
620
+ };
621
+ //# sourceMappingURL=chunk-23N5RHOB.js.map