@cmdoss/suipay-mcp 0.2.2-dev.3 → 0.2.2-dev.4
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 +8 -48
- package/dist/bin/suipay.js +2 -2
- package/dist/{chunk-SRMAH6MK.js → chunk-KDOGSB5I.js} +305 -1849
- package/dist/chunk-XAM2YQC6.js +39 -0
- package/dist/{http-B4YTVw0k.d.ts → http-C8xW9F1c.d.ts} +4 -29
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +9 -34
- package/dist/index.js +2 -10
- package/package.json +1 -1
- package/dist/chunk-C2IYSV43.js +0 -46
|
@@ -151,46 +151,6 @@ import { normalizeStructTag as normalizeStructTag2 } from "@mysten/sui/utils";
|
|
|
151
151
|
|
|
152
152
|
// ../core/dist/assets/testnet.js
|
|
153
153
|
import { normalizeStructTag } from "@mysten/sui/utils";
|
|
154
|
-
var TESTNET_SUI_COIN_TYPE = "0x2::sui::SUI";
|
|
155
|
-
var TESTNET_USDC_COIN_TYPE = "0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC";
|
|
156
|
-
var TESTNET_SUI_DECIMALS = 9;
|
|
157
|
-
var TESTNET_USDC_DECIMALS = 6;
|
|
158
|
-
var TESTNET_ASSETS = [
|
|
159
|
-
{
|
|
160
|
-
id: "sui",
|
|
161
|
-
symbol: "SUI",
|
|
162
|
-
coinType: TESTNET_SUI_COIN_TYPE,
|
|
163
|
-
decimals: TESTNET_SUI_DECIMALS,
|
|
164
|
-
network: "sui:testnet"
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
id: "usdc",
|
|
168
|
-
symbol: "USDC",
|
|
169
|
-
coinType: TESTNET_USDC_COIN_TYPE,
|
|
170
|
-
decimals: TESTNET_USDC_DECIMALS,
|
|
171
|
-
network: "sui:testnet"
|
|
172
|
-
}
|
|
173
|
-
];
|
|
174
|
-
function supportedAssetByCoinType(coinType) {
|
|
175
|
-
const normalized = tryNormalizeStructTag(coinType);
|
|
176
|
-
if (!normalized)
|
|
177
|
-
return null;
|
|
178
|
-
for (const asset of TESTNET_ASSETS) {
|
|
179
|
-
if (tryNormalizeStructTag(asset.coinType) === normalized)
|
|
180
|
-
return asset;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
function tryNormalizeStructTag(coinType) {
|
|
185
|
-
const trimmed = coinType.trim();
|
|
186
|
-
if (!trimmed)
|
|
187
|
-
return null;
|
|
188
|
-
try {
|
|
189
|
-
return normalizeStructTag(trimmed);
|
|
190
|
-
} catch {
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
154
|
|
|
195
155
|
// ../core/dist/service-state/index.js
|
|
196
156
|
var STATE_DIMENSIONS = Object.freeze([
|
|
@@ -411,101 +371,6 @@ var SERVICE_STATE_TRANSITIONS = Object.freeze([
|
|
|
411
371
|
]);
|
|
412
372
|
|
|
413
373
|
// ../core/dist/policy/target.js
|
|
414
|
-
var AUTHORIZATION_PROTOCOL_VERSION = "suipay-target-v1";
|
|
415
|
-
function normalizeHexAddress(address) {
|
|
416
|
-
const raw = address.trim().toLowerCase();
|
|
417
|
-
if (!raw.startsWith("0x")) {
|
|
418
|
-
throw new Error("Address must be a 0x-prefixed Sui address.");
|
|
419
|
-
}
|
|
420
|
-
const hex = raw.slice(2);
|
|
421
|
-
if (!/^[0-9a-f]+$/.test(hex) || hex.length === 0 || hex.length > 64) {
|
|
422
|
-
throw new Error("Invalid Sui address.");
|
|
423
|
-
}
|
|
424
|
-
if (/^0+$/.test(hex)) {
|
|
425
|
-
throw new Error("Recipient must be a non-zero address.");
|
|
426
|
-
}
|
|
427
|
-
return `0x${hex.padStart(64, "0")}`;
|
|
428
|
-
}
|
|
429
|
-
function normalizePath(path) {
|
|
430
|
-
const p = path.trim();
|
|
431
|
-
if (!p.startsWith("/")) {
|
|
432
|
-
throw new Error("Path must be an absolute path starting with /.");
|
|
433
|
-
}
|
|
434
|
-
if (p.length > 1 && p.endsWith("/"))
|
|
435
|
-
return p.replace(/\/+$/, "");
|
|
436
|
-
return p;
|
|
437
|
-
}
|
|
438
|
-
function normalizeMethod(method) {
|
|
439
|
-
const m = method.trim().toUpperCase();
|
|
440
|
-
if (!m)
|
|
441
|
-
throw new Error("HTTP method is required.");
|
|
442
|
-
return m;
|
|
443
|
-
}
|
|
444
|
-
function normalizeNetwork(network) {
|
|
445
|
-
const n = network.trim().toLowerCase();
|
|
446
|
-
if (n !== "sui:testnet") {
|
|
447
|
-
throw new Error(`Unsupported network: ${network}`);
|
|
448
|
-
}
|
|
449
|
-
return n;
|
|
450
|
-
}
|
|
451
|
-
function normalizeAsset(asset) {
|
|
452
|
-
const trimmed = asset.trim();
|
|
453
|
-
let normalized;
|
|
454
|
-
try {
|
|
455
|
-
normalized = normalizeStructTag2(trimmed);
|
|
456
|
-
} catch {
|
|
457
|
-
throw new Error(`Invalid coin type: ${asset}`);
|
|
458
|
-
}
|
|
459
|
-
if (!supportedAssetByCoinType(normalized)) {
|
|
460
|
-
throw new Error(`Unsupported asset: ${asset}`);
|
|
461
|
-
}
|
|
462
|
-
return normalized;
|
|
463
|
-
}
|
|
464
|
-
function normalizeId(id, label) {
|
|
465
|
-
const v = id.trim();
|
|
466
|
-
if (!v)
|
|
467
|
-
throw new Error(`${label} is required.`);
|
|
468
|
-
if (/[\n\r\0]/.test(v))
|
|
469
|
-
throw new Error(`${label} contains control characters.`);
|
|
470
|
-
return v;
|
|
471
|
-
}
|
|
472
|
-
function normalizeEpoch(epoch) {
|
|
473
|
-
if (!Number.isInteger(epoch) || epoch < 1) {
|
|
474
|
-
throw new Error("authorizationEpoch must be a positive integer.");
|
|
475
|
-
}
|
|
476
|
-
return epoch;
|
|
477
|
-
}
|
|
478
|
-
function canonicalizeTargetFields(input) {
|
|
479
|
-
return {
|
|
480
|
-
network: normalizeNetwork(input.network),
|
|
481
|
-
providerId: normalizeHexAddress(input.providerId),
|
|
482
|
-
serviceId: normalizeId(input.serviceId, "serviceId"),
|
|
483
|
-
endpointId: normalizeId(input.endpointId, "endpointId"),
|
|
484
|
-
method: normalizeMethod(input.method),
|
|
485
|
-
path: normalizePath(input.path),
|
|
486
|
-
asset: normalizeAsset(input.asset),
|
|
487
|
-
recipient: normalizeHexAddress(input.recipient),
|
|
488
|
-
authorizationEpoch: normalizeEpoch(input.authorizationEpoch)
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
function authorizationFingerprint(input) {
|
|
492
|
-
const c = canonicalizeTargetFields(input);
|
|
493
|
-
return [
|
|
494
|
-
AUTHORIZATION_PROTOCOL_VERSION,
|
|
495
|
-
c.network,
|
|
496
|
-
c.providerId,
|
|
497
|
-
c.serviceId,
|
|
498
|
-
c.endpointId,
|
|
499
|
-
c.method,
|
|
500
|
-
c.path,
|
|
501
|
-
c.asset,
|
|
502
|
-
c.recipient,
|
|
503
|
-
String(c.authorizationEpoch)
|
|
504
|
-
].join("\n");
|
|
505
|
-
}
|
|
506
|
-
function targetHashFromFingerprint(fingerprint) {
|
|
507
|
-
return createHash("sha256").update(fingerprint, "utf8").digest("hex");
|
|
508
|
-
}
|
|
509
374
|
function targetHashBytes(hexHash) {
|
|
510
375
|
if (!/^[0-9a-f]{64}$/i.test(hexHash)) {
|
|
511
376
|
throw new Error("targetHash must be 32-byte hex.");
|
|
@@ -4467,10 +4332,8 @@ import {
|
|
|
4467
4332
|
fsyncSync,
|
|
4468
4333
|
openSync,
|
|
4469
4334
|
closeSync,
|
|
4470
|
-
writeSync
|
|
4471
|
-
linkSync
|
|
4335
|
+
writeSync
|
|
4472
4336
|
} from "fs";
|
|
4473
|
-
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
4474
4337
|
import { Ed25519Keypair as Ed25519Keypair2 } from "@mysten/sui/keypairs/ed25519";
|
|
4475
4338
|
import { fromBase64 as fromBase644 } from "@mysten/sui/utils";
|
|
4476
4339
|
import { bytesToHex as bytesToHex4 } from "@noble/hashes/utils.js";
|
|
@@ -4484,16 +4347,6 @@ function mcpCredentialsPath() {
|
|
|
4484
4347
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? homedir();
|
|
4485
4348
|
return join(home, ".suipay", "credentials.json");
|
|
4486
4349
|
}
|
|
4487
|
-
var MCP_TARGET_PRESETS = {
|
|
4488
|
-
dev: {
|
|
4489
|
-
consoleUrl: "https://suipay-dev.up.railway.app",
|
|
4490
|
-
gatewayUrl: "https://gateway-dev-ba84.up.railway.app"
|
|
4491
|
-
},
|
|
4492
|
-
local: {
|
|
4493
|
-
consoleUrl: "http://localhost:3000",
|
|
4494
|
-
gatewayUrl: "http://127.0.0.1:4340"
|
|
4495
|
-
}
|
|
4496
|
-
};
|
|
4497
4350
|
var GRPC_URLS = {
|
|
4498
4351
|
mainnet: "https://fullnode.mainnet.sui.io:443",
|
|
4499
4352
|
testnet: "https://fullnode.testnet.sui.io:443",
|
|
@@ -4537,56 +4390,11 @@ function resolveActiveMovePackageId(env = process.env) {
|
|
|
4537
4390
|
}
|
|
4538
4391
|
return configured[0]?.value;
|
|
4539
4392
|
}
|
|
4540
|
-
function
|
|
4541
|
-
let target;
|
|
4542
|
-
for (const arg of argv) {
|
|
4543
|
-
const next = arg === "--local" || arg === "-local" ? "local" : arg === "--dev" || arg === "-dev" ? "dev" : void 0;
|
|
4544
|
-
if (!next) continue;
|
|
4545
|
-
if (target && target !== next) {
|
|
4546
|
-
throw new Error("pass only one of --local or --dev");
|
|
4547
|
-
}
|
|
4548
|
-
target = next;
|
|
4549
|
-
}
|
|
4550
|
-
return target;
|
|
4551
|
-
}
|
|
4552
|
-
function parseMcpTargetName(value) {
|
|
4553
|
-
const trimmed = value?.trim().toLowerCase();
|
|
4554
|
-
if (!trimmed) return void 0;
|
|
4555
|
-
if (trimmed === "dev" || trimmed === "local") return trimmed;
|
|
4556
|
-
throw new Error(`SUIPAY_MCP_TARGET must be "dev" or "local", got ${JSON.stringify(value)}`);
|
|
4557
|
-
}
|
|
4558
|
-
function resolveMcpTarget(env = process.env, argv = []) {
|
|
4559
|
-
return parseMcpTargetFlag(argv) ?? parseMcpTargetName(env.SUIPAY_MCP_TARGET) ?? "dev";
|
|
4560
|
-
}
|
|
4561
|
-
function loopbackKey(url) {
|
|
4562
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
4563
|
-
if (url.hostname !== "127.0.0.1" && url.hostname !== "localhost") return null;
|
|
4564
|
-
const port = url.port || (url.protocol === "https:" ? "443" : "80");
|
|
4565
|
-
return `${url.protocol}//loopback:${port}`;
|
|
4566
|
-
}
|
|
4567
|
-
function sameMcpGateway(a, b) {
|
|
4568
|
-
let ua;
|
|
4569
|
-
let ub;
|
|
4570
|
-
try {
|
|
4571
|
-
ua = new URL(a);
|
|
4572
|
-
ub = new URL(b);
|
|
4573
|
-
} catch {
|
|
4574
|
-
return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
|
|
4575
|
-
}
|
|
4576
|
-
if (ua.origin === ub.origin) return true;
|
|
4577
|
-
const left = loopbackKey(ua);
|
|
4578
|
-
const right = loopbackKey(ub);
|
|
4579
|
-
return left !== null && left === right;
|
|
4580
|
-
}
|
|
4581
|
-
function loadMcpConfig(env = process.env, argv = []) {
|
|
4393
|
+
function loadMcpConfig(env = process.env) {
|
|
4582
4394
|
const network = env.SUIPAY_NETWORK ?? "testnet";
|
|
4583
|
-
const
|
|
4584
|
-
const preset = MCP_TARGET_PRESETS[target];
|
|
4585
|
-
const gatewayUrl = env.SUIPAY_GATEWAY_URL?.trim() || preset.gatewayUrl;
|
|
4586
|
-
const consoleUrl = env.SUIPAY_CONSOLE_URL?.trim() || preset.consoleUrl;
|
|
4395
|
+
const gatewayUrl = env.SUIPAY_GATEWAY_URL ?? "http://localhost:4340";
|
|
4587
4396
|
return {
|
|
4588
|
-
|
|
4589
|
-
consoleUrl,
|
|
4397
|
+
consoleUrl: env.SUIPAY_CONSOLE_URL ?? "http://localhost:3000",
|
|
4590
4398
|
gatewayUrl,
|
|
4591
4399
|
rpcUrl: env.SUIPAY_RPC_URL ?? GRPC_URLS[network],
|
|
4592
4400
|
network,
|
|
@@ -4635,23 +4443,10 @@ function isValid(obj) {
|
|
|
4635
4443
|
const c = obj;
|
|
4636
4444
|
return typeof c.delegatePrivateKey === "string" && HEX64.test(c.delegatePrivateKey) && typeof c.delegatePublicKeyHex === "string" && HEX64.test(c.delegatePublicKeyHex) && typeof c.delegateAddress === "string" && ADDR.test(c.delegateAddress) && typeof c.ownerAddress === "string" && ADDR.test(c.ownerAddress) && typeof c.allowanceId === "string" && ADDR.test(c.allowanceId) && typeof c.packageId === "string" && ADDR.test(c.packageId) && typeof c.recipient === "string" && ADDR.test(c.recipient) && typeof c.coinType === "string" && c.coinType.length > 0 && typeof c.gatewayUrl === "string" && typeof c.network === "string" && typeof c.maxPerPayment === "string" && /^\d+$/.test(c.maxPerPayment) && typeof c.createdAt === "string" && c.version === 1;
|
|
4637
4445
|
}
|
|
4638
|
-
function isHttpUrl(value) {
|
|
4639
|
-
if (typeof value !== "string" || !value.trim()) return false;
|
|
4640
|
-
try {
|
|
4641
|
-
const parsed = new URL(value);
|
|
4642
|
-
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
4643
|
-
} catch {
|
|
4644
|
-
return false;
|
|
4645
|
-
}
|
|
4646
|
-
}
|
|
4647
4446
|
function isLocalSigningKeyRecord(obj) {
|
|
4648
4447
|
if (!obj || typeof obj !== "object") return false;
|
|
4649
4448
|
const c = obj;
|
|
4650
|
-
|
|
4651
|
-
return false;
|
|
4652
|
-
}
|
|
4653
|
-
if (c.gatewayUrl !== void 0 && !isHttpUrl(c.gatewayUrl)) return false;
|
|
4654
|
-
return true;
|
|
4449
|
+
return c.version === 2 && typeof c.delegatePrivateKey === "string" && HEX64.test(c.delegatePrivateKey) && typeof c.delegatePublicKeyHex === "string" && HEX64.test(c.delegatePublicKeyHex) && typeof c.delegateAddress === "string" && ADDR.test(c.delegateAddress) && typeof c.createdAt === "string";
|
|
4655
4450
|
}
|
|
4656
4451
|
function readCredentialsJson() {
|
|
4657
4452
|
const path = credsPath();
|
|
@@ -4687,19 +4482,15 @@ function loadCreds() {
|
|
|
4687
4482
|
}
|
|
4688
4483
|
return parsed;
|
|
4689
4484
|
}
|
|
4690
|
-
function
|
|
4485
|
+
function writeCredentialsFile(payload) {
|
|
4486
|
+
const dir = credsDir();
|
|
4487
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4691
4488
|
try {
|
|
4692
|
-
|
|
4693
|
-
try {
|
|
4694
|
-
fsyncSync(fd);
|
|
4695
|
-
} finally {
|
|
4696
|
-
closeSync(fd);
|
|
4697
|
-
}
|
|
4489
|
+
chmodSync(dir, 448);
|
|
4698
4490
|
} catch {
|
|
4699
4491
|
}
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
const tmp = join2(dir, `.credentials.${process.pid}.${randomSuffix()}.tmp`);
|
|
4492
|
+
const path = credsPath();
|
|
4493
|
+
const tmp = join2(dir, `.credentials.${process.pid}.tmp`);
|
|
4703
4494
|
const fd = openSync(tmp, "w", 384);
|
|
4704
4495
|
try {
|
|
4705
4496
|
writeSync(fd, JSON.stringify(payload, null, 2));
|
|
@@ -4707,23 +4498,7 @@ function writeTmpCredentials(dir, payload) {
|
|
|
4707
4498
|
} finally {
|
|
4708
4499
|
closeSync(fd);
|
|
4709
4500
|
}
|
|
4710
|
-
return tmp;
|
|
4711
|
-
}
|
|
4712
|
-
function randomSuffix() {
|
|
4713
|
-
return Math.random().toString(16).slice(2, 10);
|
|
4714
|
-
}
|
|
4715
|
-
function writeCredentialsFile(payload) {
|
|
4716
|
-
const dir = credsDir();
|
|
4717
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4718
|
-
try {
|
|
4719
|
-
chmodSync(dir, 448);
|
|
4720
|
-
} catch {
|
|
4721
|
-
}
|
|
4722
|
-
const path = credsPath();
|
|
4723
|
-
const tmp = writeTmpCredentials(dir, payload);
|
|
4724
|
-
fsyncDir(dir);
|
|
4725
4501
|
renameSync(tmp, path);
|
|
4726
|
-
fsyncDir(dir);
|
|
4727
4502
|
try {
|
|
4728
4503
|
chmodSync(path, 384);
|
|
4729
4504
|
} catch {
|
|
@@ -4737,23 +4512,19 @@ function createCredentialsFileExclusive(payload) {
|
|
|
4737
4512
|
} catch {
|
|
4738
4513
|
}
|
|
4739
4514
|
const path = credsPath();
|
|
4740
|
-
|
|
4741
|
-
fsyncDir(dir);
|
|
4515
|
+
let fd;
|
|
4742
4516
|
try {
|
|
4743
|
-
|
|
4517
|
+
fd = openSync(path, "wx", 384);
|
|
4744
4518
|
} catch (error2) {
|
|
4745
|
-
try {
|
|
4746
|
-
unlinkSync(tmp);
|
|
4747
|
-
} catch {
|
|
4748
|
-
}
|
|
4749
4519
|
if (error2.code === "EEXIST") return false;
|
|
4750
4520
|
throw error2;
|
|
4751
4521
|
}
|
|
4752
4522
|
try {
|
|
4753
|
-
|
|
4754
|
-
|
|
4523
|
+
writeSync(fd, JSON.stringify(payload, null, 2));
|
|
4524
|
+
fsyncSync(fd);
|
|
4525
|
+
} finally {
|
|
4526
|
+
closeSync(fd);
|
|
4755
4527
|
}
|
|
4756
|
-
fsyncDir(dir);
|
|
4757
4528
|
try {
|
|
4758
4529
|
chmodSync(path, 384);
|
|
4759
4530
|
} catch {
|
|
@@ -4842,90 +4613,12 @@ function rewriteV1ToLocalSigningKey(creds, opts = {}) {
|
|
|
4842
4613
|
delegatePublicKeyHex: creds.delegatePublicKeyHex,
|
|
4843
4614
|
delegateAddress: creds.delegateAddress,
|
|
4844
4615
|
createdAt: creds.createdAt,
|
|
4845
|
-
...isHttpUrl(creds.gatewayUrl) ? { gatewayUrl: creds.gatewayUrl } : {},
|
|
4846
4616
|
...opts.label?.trim() ? { label: opts.label.trim() } : creds.label?.trim() ? { label: creds.label.trim() } : {}
|
|
4847
4617
|
};
|
|
4848
4618
|
writeCredentialsFile(record);
|
|
4849
4619
|
return identity;
|
|
4850
4620
|
}
|
|
4851
|
-
function keypairFromOperatorSecret(raw) {
|
|
4852
|
-
const key = raw.trim();
|
|
4853
|
-
if (!key) throw new Error("SUIPAY_MCP_KEY is set but empty");
|
|
4854
|
-
try {
|
|
4855
|
-
return /^(0x)?[0-9a-fA-F]{64}$/.test(key) ? Ed25519Keypair2.fromSecretKey(
|
|
4856
|
-
Uint8Array.from(Buffer.from(key.replace(/^0x/i, ""), "hex"))
|
|
4857
|
-
) : Ed25519Keypair2.fromSecretKey(key);
|
|
4858
|
-
} catch {
|
|
4859
|
-
throw new Error(
|
|
4860
|
-
"SUIPAY_MCP_KEY is malformed (expected 64-hex or suiprivkey1\u2026 Ed25519 secret)"
|
|
4861
|
-
);
|
|
4862
|
-
}
|
|
4863
|
-
}
|
|
4864
|
-
function seedHexFromKeypair(kp, operatorSecret) {
|
|
4865
|
-
const hex = operatorSecret.trim().replace(/^0x/i, "");
|
|
4866
|
-
if (/^[0-9a-fA-F]{64}$/.test(hex)) return hex.toLowerCase();
|
|
4867
|
-
try {
|
|
4868
|
-
return bytesToHex4(decodeSuiPrivateKey(kp.getSecretKey()).secretKey);
|
|
4869
|
-
} catch {
|
|
4870
|
-
throw new Error(
|
|
4871
|
-
"SUIPAY_MCP_KEY is malformed (expected 64-hex or suiprivkey1\u2026 Ed25519 secret)"
|
|
4872
|
-
);
|
|
4873
|
-
}
|
|
4874
|
-
}
|
|
4875
|
-
function recordFromKeypair(kp, opts = {}, existing, operatorSecret) {
|
|
4876
|
-
const seed = seedHexFromKeypair(kp, operatorSecret);
|
|
4877
|
-
const publicKeyHex = bytesToHex4(kp.getPublicKey().toRawBytes());
|
|
4878
|
-
const delegateAddress = kp.getPublicKey().toSuiAddress();
|
|
4879
|
-
const label = opts.label?.trim() || existing?.label?.trim();
|
|
4880
|
-
return {
|
|
4881
|
-
version: 2,
|
|
4882
|
-
delegatePrivateKey: seed,
|
|
4883
|
-
delegatePublicKeyHex: publicKeyHex,
|
|
4884
|
-
delegateAddress,
|
|
4885
|
-
createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4886
|
-
...existing && isHttpUrl(existing.gatewayUrl ?? "") ? { gatewayUrl: existing.gatewayUrl } : {},
|
|
4887
|
-
...label ? { label } : {}
|
|
4888
|
-
};
|
|
4889
|
-
}
|
|
4890
|
-
var ENV_KEY_MISMATCH = "SUIPAY_MCP_KEY does not match the stored delegate key - refusing to overwrite";
|
|
4891
|
-
function existingRecordForEnv(parsed) {
|
|
4892
|
-
if (parsed === null) return null;
|
|
4893
|
-
if (isLocalSigningKeyRecord(parsed) || isValid(parsed)) return parsed;
|
|
4894
|
-
throw new Error(
|
|
4895
|
-
`credentials file at ${credsPath()} failed schema validation - refusing to continue`
|
|
4896
|
-
);
|
|
4897
|
-
}
|
|
4898
4621
|
async function ensureLocalSigningKey(opts = {}) {
|
|
4899
|
-
const envKey = process.env.SUIPAY_MCP_KEY?.trim();
|
|
4900
|
-
if (envKey) {
|
|
4901
|
-
const kp2 = keypairFromOperatorSecret(envKey);
|
|
4902
|
-
const parsed2 = readCredentialsJson();
|
|
4903
|
-
const existing = existingRecordForEnv(parsed2);
|
|
4904
|
-
const asV2 = existing ? isLocalSigningKeyRecord(existing) ? existing : {
|
|
4905
|
-
version: 2,
|
|
4906
|
-
delegatePrivateKey: existing.delegatePrivateKey,
|
|
4907
|
-
delegatePublicKeyHex: existing.delegatePublicKeyHex,
|
|
4908
|
-
delegateAddress: existing.delegateAddress,
|
|
4909
|
-
createdAt: existing.createdAt,
|
|
4910
|
-
...isHttpUrl(existing.gatewayUrl) ? { gatewayUrl: existing.gatewayUrl } : {},
|
|
4911
|
-
...existing.label?.trim() ? { label: existing.label.trim() } : {}
|
|
4912
|
-
} : null;
|
|
4913
|
-
const record2 = recordFromKeypair(kp2, opts, asV2, envKey);
|
|
4914
|
-
if (existing) {
|
|
4915
|
-
if (existing.delegateAddress !== record2.delegateAddress || existing.delegatePrivateKey !== record2.delegatePrivateKey) {
|
|
4916
|
-
throw new Error(ENV_KEY_MISMATCH);
|
|
4917
|
-
}
|
|
4918
|
-
if (!isLocalSigningKeyRecord(existing)) {
|
|
4919
|
-
writeCredentialsFile(record2);
|
|
4920
|
-
}
|
|
4921
|
-
} else {
|
|
4922
|
-
writeCredentialsFile(record2);
|
|
4923
|
-
}
|
|
4924
|
-
return {
|
|
4925
|
-
delegateAddress: record2.delegateAddress,
|
|
4926
|
-
publicKeyHex: record2.delegatePublicKeyHex
|
|
4927
|
-
};
|
|
4928
|
-
}
|
|
4929
4622
|
const parsed = readCredentialsJson();
|
|
4930
4623
|
if (parsed !== null) {
|
|
4931
4624
|
if (isLocalSigningKeyRecord(parsed)) {
|
|
@@ -4995,67 +4688,6 @@ function loadLocalSigner() {
|
|
|
4995
4688
|
}
|
|
4996
4689
|
};
|
|
4997
4690
|
}
|
|
4998
|
-
function loadLocalSigningKeyRecord() {
|
|
4999
|
-
const parsed = readCredentialsJson();
|
|
5000
|
-
if (parsed === null) return null;
|
|
5001
|
-
if (isLocalSigningKeyRecord(parsed)) {
|
|
5002
|
-
identityFromStored(
|
|
5003
|
-
parsed.delegatePrivateKey,
|
|
5004
|
-
parsed.delegatePublicKeyHex,
|
|
5005
|
-
parsed.delegateAddress
|
|
5006
|
-
);
|
|
5007
|
-
return parsed;
|
|
5008
|
-
}
|
|
5009
|
-
if (isValid(parsed)) return null;
|
|
5010
|
-
throw new Error(
|
|
5011
|
-
`credentials file at ${credsPath()} failed schema validation - refusing to continue`
|
|
5012
|
-
);
|
|
5013
|
-
}
|
|
5014
|
-
function loadLocalKeypair() {
|
|
5015
|
-
const parsed = readCredentialsJson();
|
|
5016
|
-
if (parsed === null) return null;
|
|
5017
|
-
if (!isLocalSigningKeyRecord(parsed) && !isValid(parsed)) {
|
|
5018
|
-
throw new Error(
|
|
5019
|
-
`credentials file at ${credsPath()} failed schema validation - refusing to continue`
|
|
5020
|
-
);
|
|
5021
|
-
}
|
|
5022
|
-
identityFromStored(
|
|
5023
|
-
parsed.delegatePrivateKey,
|
|
5024
|
-
parsed.delegatePublicKeyHex,
|
|
5025
|
-
parsed.delegateAddress
|
|
5026
|
-
);
|
|
5027
|
-
return Ed25519Keypair2.fromSecretKey(
|
|
5028
|
-
Uint8Array.from(Buffer.from(parsed.delegatePrivateKey, "hex"))
|
|
5029
|
-
);
|
|
5030
|
-
}
|
|
5031
|
-
function persistGatewayUrl(gatewayUrl) {
|
|
5032
|
-
if (!isHttpUrl(gatewayUrl)) {
|
|
5033
|
-
throw new Error("gatewayUrl must be an http(s) origin");
|
|
5034
|
-
}
|
|
5035
|
-
const parsed = readCredentialsJson();
|
|
5036
|
-
if (parsed === null) {
|
|
5037
|
-
throw new Error(
|
|
5038
|
-
`credentials file at ${credsPath()} is missing - refusing to persist gatewayUrl`
|
|
5039
|
-
);
|
|
5040
|
-
}
|
|
5041
|
-
if (isLocalSigningKeyRecord(parsed)) {
|
|
5042
|
-
identityFromStored(
|
|
5043
|
-
parsed.delegatePrivateKey,
|
|
5044
|
-
parsed.delegatePublicKeyHex,
|
|
5045
|
-
parsed.delegateAddress
|
|
5046
|
-
);
|
|
5047
|
-
writeCredentialsFile({ ...parsed, gatewayUrl });
|
|
5048
|
-
return;
|
|
5049
|
-
}
|
|
5050
|
-
if (isValid(parsed)) {
|
|
5051
|
-
rewriteV1ToLocalSigningKey(parsed);
|
|
5052
|
-
persistGatewayUrl(gatewayUrl);
|
|
5053
|
-
return;
|
|
5054
|
-
}
|
|
5055
|
-
throw new Error(
|
|
5056
|
-
`credentials file at ${credsPath()} failed schema validation - refusing to continue`
|
|
5057
|
-
);
|
|
5058
|
-
}
|
|
5059
4691
|
|
|
5060
4692
|
// src/agent-held.ts
|
|
5061
4693
|
var processLedger = null;
|
|
@@ -5083,6 +4715,38 @@ function offerSecret(env) {
|
|
|
5083
4715
|
const secret = env.SUIPAY_SECRET?.trim();
|
|
5084
4716
|
return secret ? secret : null;
|
|
5085
4717
|
}
|
|
4718
|
+
function settlementFromLocalSigner(cfg, local, packageId, env = process.env) {
|
|
4719
|
+
const chain = createSpendAccountChain({
|
|
4720
|
+
packageId,
|
|
4721
|
+
network: cfg.network,
|
|
4722
|
+
url: cfg.rpcUrl
|
|
4723
|
+
});
|
|
4724
|
+
const finality = createSpendAccountFinalityReader({ client: chain.client });
|
|
4725
|
+
const getObjectJson = async (objectId) => {
|
|
4726
|
+
const { object } = await chain.client.core.getObject({
|
|
4727
|
+
objectId,
|
|
4728
|
+
include: { json: true }
|
|
4729
|
+
});
|
|
4730
|
+
return objectWithOwner(object);
|
|
4731
|
+
};
|
|
4732
|
+
const secret = offerSecret(env);
|
|
4733
|
+
return {
|
|
4734
|
+
signer: {
|
|
4735
|
+
address: local.address,
|
|
4736
|
+
signTransaction: local.signTransaction,
|
|
4737
|
+
signPersonalMessage: local.signPersonalMessage
|
|
4738
|
+
},
|
|
4739
|
+
getObjectJson,
|
|
4740
|
+
payerDeps: {
|
|
4741
|
+
...secret ? { offerSecret: secret } : {},
|
|
4742
|
+
paymentLedger: sharedProcessLedger(),
|
|
4743
|
+
sponsor: { sponsorUrl: cfg.gatewayUrl },
|
|
4744
|
+
gasMode: "sponsored",
|
|
4745
|
+
resolveSharedObjectVersions: (ids) => resolveSharedObjectVersionsFromGetObject(getObjectJson, ids),
|
|
4746
|
+
getFinalizedTx: (digest) => finality.getFinalizedTx(digest)
|
|
4747
|
+
}
|
|
4748
|
+
};
|
|
4749
|
+
}
|
|
5086
4750
|
function resolveAgentHeldSettlement(cfg, spendAccountPay, env = process.env) {
|
|
5087
4751
|
const secret = offerSecret(env);
|
|
5088
4752
|
if (!secret) return null;
|
|
@@ -5176,311 +4840,52 @@ function toolAllowedByScope(toolName, grantedScope) {
|
|
|
5176
4840
|
}
|
|
5177
4841
|
|
|
5178
4842
|
// src/tools.ts
|
|
5179
|
-
import { isValidSuiAddress, normalizeSuiAddress as
|
|
4843
|
+
import { isValidSuiAddress, normalizeSuiAddress as normalizeSuiAddress10 } from "@mysten/sui/utils";
|
|
5180
4844
|
|
|
5181
|
-
//
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
session: args.session,
|
|
5226
|
-
cfg: args.cfg,
|
|
5227
|
-
deps: args.deps,
|
|
5228
|
-
preparedTerms: {
|
|
5229
|
-
recipient: args.expected.recipient,
|
|
5230
|
-
amount: args.expected.amount,
|
|
5231
|
-
asset: args.signingRequest.coinType,
|
|
5232
|
-
targetHash: args.signingRequest.targetHash
|
|
5233
|
-
},
|
|
5234
|
-
...args.intent ? { intent: args.intent } : {}
|
|
5235
|
-
});
|
|
5236
|
-
}
|
|
5237
|
-
|
|
5238
|
-
// ../sdk/dist/agent-sdk/delegate-agent.js
|
|
5239
|
-
import { normalizeSuiAddress as normalizeSuiAddress10 } from "@mysten/sui/utils";
|
|
5240
|
-
|
|
5241
|
-
// ../sdk/dist/agent-sdk/verify-prepared-settlement.js
|
|
5242
|
-
import { normalizeSuiAddress as normalizeSuiAddress9 } from "@mysten/sui/utils";
|
|
5243
|
-
import { Transaction as Transaction5 } from "@mysten/sui/transactions";
|
|
5244
|
-
import { fromBase64 as fromBase645 } from "@mysten/sui/utils";
|
|
5245
|
-
|
|
5246
|
-
// ../sdk/dist/protocol/prepared-settlement.js
|
|
5247
|
-
var PREPARED_SETTLEMENT_VERSION = 1;
|
|
5248
|
-
function asPreparedSettlement(value) {
|
|
5249
|
-
if (!value || typeof value !== "object")
|
|
5250
|
-
return null;
|
|
5251
|
-
const v = value;
|
|
5252
|
-
if (v.version !== PREPARED_SETTLEMENT_VERSION)
|
|
5253
|
-
return null;
|
|
5254
|
-
const accessMode = v.accessMode;
|
|
5255
|
-
if (accessMode !== "open" && accessMode !== "scoped")
|
|
5256
|
-
return null;
|
|
5257
|
-
const strings = [
|
|
5258
|
-
"transactionBytes",
|
|
5259
|
-
"transactionKindBytes",
|
|
5260
|
-
"sender",
|
|
5261
|
-
"packageId",
|
|
5262
|
-
"moveTarget",
|
|
5263
|
-
"poolObjectId",
|
|
5264
|
-
"grantObjectId",
|
|
5265
|
-
"poolInitialSharedVersion",
|
|
5266
|
-
"grantInitialSharedVersion",
|
|
5267
|
-
"clockInitialSharedVersion",
|
|
5268
|
-
"policyId",
|
|
5269
|
-
"coinType",
|
|
5270
|
-
"amount",
|
|
5271
|
-
"recipient",
|
|
5272
|
-
"payUrl"
|
|
5273
|
-
];
|
|
5274
|
-
for (const key of strings) {
|
|
5275
|
-
const got = v[key];
|
|
5276
|
-
if (typeof got !== "string" || !got.trim())
|
|
5277
|
-
return null;
|
|
5278
|
-
}
|
|
5279
|
-
return {
|
|
5280
|
-
version: PREPARED_SETTLEMENT_VERSION,
|
|
5281
|
-
accessMode,
|
|
5282
|
-
transactionBytes: v.transactionBytes,
|
|
5283
|
-
transactionKindBytes: v.transactionKindBytes,
|
|
5284
|
-
sender: v.sender,
|
|
5285
|
-
packageId: v.packageId,
|
|
5286
|
-
moveTarget: v.moveTarget,
|
|
5287
|
-
poolObjectId: v.poolObjectId,
|
|
5288
|
-
grantObjectId: v.grantObjectId,
|
|
5289
|
-
poolInitialSharedVersion: v.poolInitialSharedVersion,
|
|
5290
|
-
grantInitialSharedVersion: v.grantInitialSharedVersion,
|
|
5291
|
-
clockInitialSharedVersion: v.clockInitialSharedVersion,
|
|
5292
|
-
policyId: v.policyId,
|
|
5293
|
-
coinType: v.coinType,
|
|
5294
|
-
amount: v.amount,
|
|
5295
|
-
recipient: v.recipient,
|
|
5296
|
-
payUrl: v.payUrl
|
|
5297
|
-
};
|
|
5298
|
-
}
|
|
5299
|
-
|
|
5300
|
-
// ../sdk/dist/agent-sdk/verify-prepared-settlement.js
|
|
5301
|
-
var PreparedSettlementRejected = class extends Error {
|
|
5302
|
-
code = "PREPARED_SETTLEMENT_REJECTED";
|
|
5303
|
-
/** Which check failed, for a caller that logs rather than reads prose. */
|
|
5304
|
-
field;
|
|
5305
|
-
constructor(field, message) {
|
|
5306
|
-
super(message);
|
|
5307
|
-
this.name = "PreparedSettlementRejected";
|
|
5308
|
-
this.field = field;
|
|
5309
|
-
}
|
|
5310
|
-
};
|
|
5311
|
-
function reject(field, message) {
|
|
5312
|
-
throw new PreparedSettlementRejected(field, message);
|
|
5313
|
-
}
|
|
5314
|
-
function sameAddress3(a, b) {
|
|
5315
|
-
try {
|
|
5316
|
-
return normalizeSuiAddress9(a) === normalizeSuiAddress9(b);
|
|
5317
|
-
} catch {
|
|
5318
|
-
return false;
|
|
5319
|
-
}
|
|
5320
|
-
}
|
|
5321
|
-
function sameCoinType(a, b) {
|
|
5322
|
-
const norm = (value) => {
|
|
5323
|
-
const [pkg, ...rest] = value.trim().split("::");
|
|
5324
|
-
if (pkg === void 0 || rest.length !== 2)
|
|
5325
|
-
return value.trim();
|
|
5326
|
-
try {
|
|
5327
|
-
return `${normalizeSuiAddress9(pkg)}::${rest.join("::")}`;
|
|
5328
|
-
} catch {
|
|
5329
|
-
return value.trim();
|
|
5330
|
-
}
|
|
5331
|
-
};
|
|
5332
|
-
return norm(a) === norm(b);
|
|
5333
|
-
}
|
|
5334
|
-
function sameAmount(a, b) {
|
|
5335
|
-
try {
|
|
5336
|
-
return BigInt(a) === BigInt(b);
|
|
5337
|
-
} catch {
|
|
5338
|
-
return false;
|
|
5339
|
-
}
|
|
5340
|
-
}
|
|
5341
|
-
async function verifyPreparedPayment(input) {
|
|
5342
|
-
const prepared = asPreparedSettlement(input.prepared);
|
|
5343
|
-
if (!prepared) {
|
|
5344
|
-
reject("shape", "the 402 carried no usable prepared settlement");
|
|
5345
|
-
}
|
|
5346
|
-
const { offer, delegateAddress } = input;
|
|
5347
|
-
if (!sameAddress3(prepared.sender, delegateAddress)) {
|
|
5348
|
-
reject("sender", `prepared sender ${prepared.sender} is not this agent's address`);
|
|
5349
|
-
}
|
|
5350
|
-
let data;
|
|
5351
|
-
try {
|
|
5352
|
-
data = Transaction5.from(fromBase645(prepared.transactionBytes)).getData();
|
|
5353
|
-
} catch {
|
|
5354
|
-
reject("transactionBytes", "prepared transaction bytes are not a Sui transaction");
|
|
5355
|
-
}
|
|
5356
|
-
const gasOwner = data.gasData?.owner;
|
|
5357
|
-
if (!gasOwner || sameAddress3(gasOwner, delegateAddress)) {
|
|
5358
|
-
reject("gasOwner", "prepared transaction is not sponsored: gas would be paid by this agent");
|
|
5359
|
-
}
|
|
5360
|
-
if (input.expectedGrantObjectId && !sameAddress3(prepared.grantObjectId, input.expectedGrantObjectId)) {
|
|
5361
|
-
reject("grantObjectId", `prepared settlement draws on grant ${prepared.grantObjectId}, not the one this agent resolved`);
|
|
5362
|
-
}
|
|
5363
|
-
if (input.expectedPoolObjectId && !sameAddress3(prepared.poolObjectId, input.expectedPoolObjectId)) {
|
|
5364
|
-
reject("poolObjectId", `prepared settlement draws on pool ${prepared.poolObjectId}, not the one this agent resolved`);
|
|
5365
|
-
}
|
|
5366
|
-
if (!offer.packageId || !sameAddress3(prepared.packageId, offer.packageId)) {
|
|
5367
|
-
reject("packageId", "prepared package id does not match the offer");
|
|
5368
|
-
}
|
|
5369
|
-
const wantedTarget = payMoveCallTarget(offer.packageId, prepared.accessMode);
|
|
5370
|
-
if (prepared.moveTarget !== wantedTarget) {
|
|
5371
|
-
reject("moveTarget", `prepared Move target ${prepared.moveTarget} is not ${wantedTarget}`);
|
|
5372
|
-
}
|
|
5373
|
-
if (!sameCoinType(prepared.coinType, offer.asset)) {
|
|
5374
|
-
reject("coinType", "prepared coin type does not match the offer asset");
|
|
5375
|
-
}
|
|
5376
|
-
if (!sameAmount(prepared.amount, offer.amount)) {
|
|
5377
|
-
reject("amount", "prepared amount does not match the offer amount");
|
|
5378
|
-
}
|
|
5379
|
-
if (!sameAddress3(prepared.recipient, offer.payTo)) {
|
|
5380
|
-
reject("recipient", "prepared recipient does not match the offer payTo");
|
|
5381
|
-
}
|
|
5382
|
-
const targetHash = offer.targetHash?.trim();
|
|
5383
|
-
if (!targetHash) {
|
|
5384
|
-
reject("targetHash", "offer carries no targetHash to bind the settlement to");
|
|
5385
|
-
}
|
|
5386
|
-
if (!offer.digest?.trim()) {
|
|
5387
|
-
reject("digest", "offer carries no digest, so its terms hash cannot be derived");
|
|
5388
|
-
}
|
|
5389
|
-
const common = {
|
|
5390
|
-
packageId: offer.packageId,
|
|
5391
|
-
delegate: prepared.sender,
|
|
5392
|
-
coinType: prepared.coinType,
|
|
5393
|
-
poolObjectId: prepared.poolObjectId,
|
|
5394
|
-
grantObjectId: prepared.grantObjectId,
|
|
5395
|
-
poolInitialSharedVersion: prepared.poolInitialSharedVersion,
|
|
5396
|
-
grantInitialSharedVersion: prepared.grantInitialSharedVersion,
|
|
5397
|
-
clockInitialSharedVersion: prepared.clockInitialSharedVersion,
|
|
5398
|
-
targetHash,
|
|
5399
|
-
amount: offer.amount,
|
|
5400
|
-
paymentIdHash: paymentIdHash(offer.challengeId),
|
|
5401
|
-
termsHash: termsHash(offer)
|
|
5402
|
-
};
|
|
5403
|
-
let rebuilt;
|
|
5404
|
-
try {
|
|
5405
|
-
rebuilt = prepared.accessMode === "open" ? (await buildSpendAccountSettleOpenRecipientKind({
|
|
5406
|
-
...common,
|
|
5407
|
-
recipient: prepared.recipient
|
|
5408
|
-
})).kindBytes : (await buildSpendAccountPayKind({
|
|
5409
|
-
...common,
|
|
5410
|
-
policyId: prepared.policyId
|
|
5411
|
-
})).kindBytes;
|
|
5412
|
-
} catch (err) {
|
|
5413
|
-
reject("rebuild", `the prepared settlement could not be rebuilt offline: ${err instanceof Error ? err.message : String(err)}`);
|
|
5414
|
-
}
|
|
5415
|
-
if (rebuilt !== prepared.transactionKindBytes) {
|
|
5416
|
-
reject("transactionKindBytes", "the prepared transaction kind is not the one this offer describes");
|
|
5417
|
-
}
|
|
5418
|
-
try {
|
|
5419
|
-
assertPayTransaction(prepared.transactionBytes, {
|
|
5420
|
-
packageId: offer.packageId,
|
|
5421
|
-
delegate: prepared.sender,
|
|
5422
|
-
poolObjectId: prepared.poolObjectId,
|
|
5423
|
-
grantObjectId: prepared.grantObjectId,
|
|
5424
|
-
policyId: prepared.policyId,
|
|
5425
|
-
targetHash,
|
|
5426
|
-
amount: offer.amount,
|
|
5427
|
-
paymentIdHash: common.paymentIdHash,
|
|
5428
|
-
termsHash: common.termsHash,
|
|
5429
|
-
coinType: prepared.coinType,
|
|
5430
|
-
kindBytes: rebuilt,
|
|
5431
|
-
accessMode: prepared.accessMode,
|
|
5432
|
-
recipient: prepared.recipient
|
|
5433
|
-
});
|
|
5434
|
-
} catch (err) {
|
|
5435
|
-
reject("transactionBytes", `prepared transaction does not reconstruct the intended settlement: ${err instanceof Error ? err.message : String(err)}`);
|
|
5436
|
-
}
|
|
5437
|
-
return prepared;
|
|
5438
|
-
}
|
|
5439
|
-
|
|
5440
|
-
// ../sdk/dist/agent-sdk/manifest.js
|
|
5441
|
-
var MANIFEST_ERROR_CODES = {
|
|
5442
|
-
MALFORMED_MANIFEST: "MALFORMED_MANIFEST",
|
|
5443
|
-
MISSING_NETWORK: "MISSING_NETWORK",
|
|
5444
|
-
MISSING_PACKAGE: "MISSING_PACKAGE",
|
|
5445
|
-
MISSING_DELEGATE: "MISSING_DELEGATE",
|
|
5446
|
-
NO_GRANTS: "NO_GRANTS",
|
|
5447
|
-
MISSING_COIN_TYPE: "MISSING_COIN_TYPE",
|
|
5448
|
-
MISSING_POOL_OBJECT: "MISSING_POOL_OBJECT",
|
|
5449
|
-
MISSING_GRANT_OBJECT: "MISSING_GRANT_OBJECT",
|
|
5450
|
-
MISSING_POLICY_BINDING: "MISSING_POLICY_BINDING",
|
|
5451
|
-
INVALID_ACCESS_MODE: "INVALID_ACCESS_MODE",
|
|
5452
|
-
GRANT_NOT_FOUND: "GRANT_NOT_FOUND",
|
|
5453
|
-
AMBIGUOUS_GRANT: "AMBIGUOUS_GRANT",
|
|
5454
|
-
/**
|
|
5455
|
-
* The chain does not agree that this grant belongs to this delegate. The
|
|
5456
|
-
* gateway said it did; an unsigned `getObject` says otherwise, and the chain
|
|
5457
|
-
* wins (ADR-0016 §3).
|
|
5458
|
-
*/
|
|
5459
|
-
GRANT_NOT_VERIFIED: "GRANT_NOT_VERIFIED",
|
|
5460
|
-
/**
|
|
5461
|
-
* The on-chain check could not run to a verdict — the fullnode read was
|
|
5462
|
-
* unreachable or returned nothing. "Could not verify" is not "verified and it
|
|
5463
|
-
* failed": both refuse, but only one is a security event.
|
|
5464
|
-
*/
|
|
5465
|
-
GRANT_VERIFY_UNAVAILABLE: "GRANT_VERIFY_UNAVAILABLE"
|
|
5466
|
-
};
|
|
5467
|
-
function fail(code, detail) {
|
|
5468
|
-
return { ok: false, error: { code, detail } };
|
|
5469
|
-
}
|
|
5470
|
-
var OBJECT_ID_RE = /^0x[0-9a-fA-F]{1,64}$/;
|
|
5471
|
-
var HEX32_RE = /^(0x)?[0-9a-fA-F]{64}$/;
|
|
5472
|
-
var U64_MAX = (1n << 64n) - 1n;
|
|
5473
|
-
function isNonEmptyString(v) {
|
|
5474
|
-
return typeof v === "string" && v.trim().length > 0;
|
|
5475
|
-
}
|
|
5476
|
-
function isObjectId(v) {
|
|
5477
|
-
return typeof v === "string" && OBJECT_ID_RE.test(v.trim());
|
|
5478
|
-
}
|
|
5479
|
-
function parseU64PolicyId(onChain) {
|
|
5480
|
-
if (typeof onChain === "number") {
|
|
5481
|
-
if (!Number.isSafeInteger(onChain) || onChain < 0)
|
|
5482
|
-
return null;
|
|
5483
|
-
return String(onChain);
|
|
4845
|
+
// ../sdk/dist/agent-sdk/manifest.js
|
|
4846
|
+
var MANIFEST_ERROR_CODES = {
|
|
4847
|
+
MALFORMED_MANIFEST: "MALFORMED_MANIFEST",
|
|
4848
|
+
MISSING_NETWORK: "MISSING_NETWORK",
|
|
4849
|
+
MISSING_PACKAGE: "MISSING_PACKAGE",
|
|
4850
|
+
MISSING_DELEGATE: "MISSING_DELEGATE",
|
|
4851
|
+
NO_GRANTS: "NO_GRANTS",
|
|
4852
|
+
MISSING_COIN_TYPE: "MISSING_COIN_TYPE",
|
|
4853
|
+
MISSING_POOL_OBJECT: "MISSING_POOL_OBJECT",
|
|
4854
|
+
MISSING_GRANT_OBJECT: "MISSING_GRANT_OBJECT",
|
|
4855
|
+
MISSING_POLICY_BINDING: "MISSING_POLICY_BINDING",
|
|
4856
|
+
INVALID_ACCESS_MODE: "INVALID_ACCESS_MODE",
|
|
4857
|
+
GRANT_NOT_FOUND: "GRANT_NOT_FOUND",
|
|
4858
|
+
AMBIGUOUS_GRANT: "AMBIGUOUS_GRANT",
|
|
4859
|
+
/**
|
|
4860
|
+
* The chain does not agree that this grant belongs to this delegate. The
|
|
4861
|
+
* gateway said it did; an unsigned `getObject` says otherwise, and the chain
|
|
4862
|
+
* wins (ADR-0016 §3).
|
|
4863
|
+
*/
|
|
4864
|
+
GRANT_NOT_VERIFIED: "GRANT_NOT_VERIFIED",
|
|
4865
|
+
/**
|
|
4866
|
+
* The on-chain check could not run to a verdict — the fullnode read was
|
|
4867
|
+
* unreachable or returned nothing. "Could not verify" is not "verified and it
|
|
4868
|
+
* failed": both refuse, but only one is a security event.
|
|
4869
|
+
*/
|
|
4870
|
+
GRANT_VERIFY_UNAVAILABLE: "GRANT_VERIFY_UNAVAILABLE"
|
|
4871
|
+
};
|
|
4872
|
+
function fail(code, detail) {
|
|
4873
|
+
return { ok: false, error: { code, detail } };
|
|
4874
|
+
}
|
|
4875
|
+
var OBJECT_ID_RE = /^0x[0-9a-fA-F]{1,64}$/;
|
|
4876
|
+
var HEX32_RE = /^(0x)?[0-9a-fA-F]{64}$/;
|
|
4877
|
+
var U64_MAX = (1n << 64n) - 1n;
|
|
4878
|
+
function isNonEmptyString(v) {
|
|
4879
|
+
return typeof v === "string" && v.trim().length > 0;
|
|
4880
|
+
}
|
|
4881
|
+
function isObjectId(v) {
|
|
4882
|
+
return typeof v === "string" && OBJECT_ID_RE.test(v.trim());
|
|
4883
|
+
}
|
|
4884
|
+
function parseU64PolicyId(onChain) {
|
|
4885
|
+
if (typeof onChain === "number") {
|
|
4886
|
+
if (!Number.isSafeInteger(onChain) || onChain < 0)
|
|
4887
|
+
return null;
|
|
4888
|
+
return String(onChain);
|
|
5484
4889
|
}
|
|
5485
4890
|
if (typeof onChain === "string" && /^\d+$/.test(onChain.trim())) {
|
|
5486
4891
|
const s = onChain.trim();
|
|
@@ -5867,489 +5272,71 @@ async function resolvePayerSession(options) {
|
|
|
5867
5272
|
});
|
|
5868
5273
|
}
|
|
5869
5274
|
|
|
5870
|
-
//
|
|
5871
|
-
|
|
5872
|
-
"privateKey",
|
|
5873
|
-
"private_key",
|
|
5874
|
-
"privateKeyHex",
|
|
5875
|
-
"secretKey",
|
|
5876
|
-
"secret_key",
|
|
5877
|
-
"secret",
|
|
5878
|
-
"seed",
|
|
5879
|
-
"seedPhrase",
|
|
5880
|
-
"seed_phrase",
|
|
5881
|
-
"mnemonic",
|
|
5882
|
-
"privkey",
|
|
5883
|
-
"suiprivkey"
|
|
5884
|
-
];
|
|
5885
|
-
var FORBIDDEN_SECRET_KEY_SET = new Set(FORBIDDEN_SECRET_KEYS.map((k) => k.toLowerCase()));
|
|
5886
|
-
|
|
5887
|
-
// ../sdk/dist/agent-sdk/smoke.js
|
|
5888
|
-
function mergeObjectEnvelope(object) {
|
|
5889
|
-
if (!object || typeof object.json !== "object" || object.json === null) {
|
|
5890
|
-
return null;
|
|
5891
|
-
}
|
|
5892
|
-
return { ...object.json, owner: object.owner };
|
|
5893
|
-
}
|
|
5894
|
-
var SECRET_KEY_SET = new Set(FORBIDDEN_SECRET_KEYS.map((k) => k.toLowerCase()));
|
|
5275
|
+
// src/tools.ts
|
|
5276
|
+
import open from "open";
|
|
5895
5277
|
|
|
5896
|
-
//
|
|
5897
|
-
async function
|
|
5898
|
-
let state;
|
|
5899
|
-
try {
|
|
5900
|
-
state = await readPublicGrantState(input.getObjectJson, input.grantObjectId);
|
|
5901
|
-
} catch {
|
|
5902
|
-
return {
|
|
5903
|
-
ok: false,
|
|
5904
|
-
error: {
|
|
5905
|
-
code: MANIFEST_ERROR_CODES.GRANT_VERIFY_UNAVAILABLE,
|
|
5906
|
-
detail: `the on-chain read of grant ${input.grantObjectId} did not complete, so this session was not verified and no payment was prepared`
|
|
5907
|
-
}
|
|
5908
|
-
};
|
|
5909
|
-
}
|
|
5910
|
-
if (!state) {
|
|
5911
|
-
return {
|
|
5912
|
-
ok: false,
|
|
5913
|
-
error: {
|
|
5914
|
-
code: MANIFEST_ERROR_CODES.GRANT_VERIFY_UNAVAILABLE,
|
|
5915
|
-
detail: `grant ${input.grantObjectId} returned no readable on-chain state`
|
|
5916
|
-
}
|
|
5917
|
-
};
|
|
5918
|
-
}
|
|
5919
|
-
const notVerified = (detail) => ({
|
|
5920
|
-
ok: false,
|
|
5921
|
-
error: { code: MANIFEST_ERROR_CODES.GRANT_NOT_VERIFIED, detail }
|
|
5922
|
-
});
|
|
5923
|
-
if (!state.delegateAddress || normalizeSuiAddress10(state.delegateAddress) !== normalizeSuiAddress10(input.delegateAddress)) {
|
|
5924
|
-
return notVerified(`grant ${input.grantObjectId} does not name this delegate on chain`);
|
|
5925
|
-
}
|
|
5926
|
-
if (!state.poolObjectId || normalizeSuiAddress10(state.poolObjectId) !== normalizeSuiAddress10(input.poolObjectId)) {
|
|
5927
|
-
return notVerified(`grant ${input.grantObjectId} draws on a different pool than the one resolved`);
|
|
5928
|
-
}
|
|
5929
|
-
if (state.revoked === true) {
|
|
5930
|
-
return notVerified(`grant ${input.grantObjectId} is revoked on chain`);
|
|
5931
|
-
}
|
|
5932
|
-
if (state.paused === true) {
|
|
5933
|
-
return notVerified(`grant ${input.grantObjectId} is paused on chain`);
|
|
5934
|
-
}
|
|
5935
|
-
return { ok: true, value: null };
|
|
5936
|
-
}
|
|
5937
|
-
async function createPayer(options) {
|
|
5938
|
-
const keypair = options.keypair;
|
|
5939
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
5940
|
-
const resolved = await resolvePayerSession({
|
|
5941
|
-
gatewayUrl: options.gatewayUrl,
|
|
5942
|
-
delegateAddress: address,
|
|
5943
|
-
// The bootstrap signature and the payment signature come from the same key.
|
|
5944
|
-
// A caller never wires a second signer for either this or the sponsor personal-message.
|
|
5945
|
-
signPersonalMessage: async (message) => {
|
|
5946
|
-
const { signature } = await keypair.signPersonalMessage(message);
|
|
5947
|
-
return signature;
|
|
5948
|
-
},
|
|
5949
|
-
signer: keypair,
|
|
5950
|
-
...options.select ? { select: options.select } : {},
|
|
5951
|
-
...options.deployment ? { deployment: options.deployment } : {},
|
|
5952
|
-
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
|
|
5953
|
-
});
|
|
5954
|
-
if (!resolved.ok)
|
|
5955
|
-
return resolved;
|
|
5956
|
-
const { session, binding } = resolved.value;
|
|
5957
|
-
const deployment = {
|
|
5958
|
-
network: session.network,
|
|
5959
|
-
packageId: session.packageId
|
|
5960
|
-
};
|
|
5961
|
-
let network;
|
|
5962
|
-
try {
|
|
5963
|
-
network = toGrpcNetwork(deployment.network);
|
|
5964
|
-
} catch {
|
|
5965
|
-
return {
|
|
5966
|
-
ok: false,
|
|
5967
|
-
error: {
|
|
5968
|
-
code: MANIFEST_ERROR_CODES.MISSING_NETWORK,
|
|
5969
|
-
detail: `the gateway reports network "${deployment.network}", which has no Sui fullnode this SDK can read; pass fullnodeUrl to name one`
|
|
5970
|
-
}
|
|
5971
|
-
};
|
|
5972
|
-
}
|
|
5973
|
-
let chain;
|
|
5278
|
+
// src/settle.ts
|
|
5279
|
+
async function settlePreparedMcpPayment(args) {
|
|
5974
5280
|
try {
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
}
|
|
5988
|
-
};
|
|
5989
|
-
}
|
|
5990
|
-
const finality = createSpendAccountFinalityReader({ client: chain.client });
|
|
5991
|
-
const getObjectJson = async (objectId) => {
|
|
5992
|
-
const { object } = await chain.client.core.getObject({
|
|
5993
|
-
objectId,
|
|
5994
|
-
include: { json: true }
|
|
5281
|
+
await verifyPreparedSettlementKind({
|
|
5282
|
+
kindBytes: args.signingRequest.unsignedKindBytes,
|
|
5283
|
+
expected: {
|
|
5284
|
+
packageId: args.signingRequest.packageId,
|
|
5285
|
+
delegateAddress: args.expected.delegateAddress,
|
|
5286
|
+
amount: args.expected.amount,
|
|
5287
|
+
recipient: args.expected.recipient,
|
|
5288
|
+
accessMode: args.signingRequest.submit.moveCallTarget.endsWith(
|
|
5289
|
+
"settle_open_recipient_payment"
|
|
5290
|
+
) ? "open" : "scoped"
|
|
5291
|
+
},
|
|
5292
|
+
getObjectJson: args.getObjectJson
|
|
5995
5293
|
});
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
const verified = await verifyGrantOnChain({
|
|
5999
|
-
getObjectJson,
|
|
6000
|
-
grantObjectId: binding.grantObjectId,
|
|
6001
|
-
poolObjectId: binding.poolObjectId,
|
|
6002
|
-
delegateAddress: address
|
|
6003
|
-
});
|
|
6004
|
-
if (!verified.ok)
|
|
6005
|
-
return verified;
|
|
6006
|
-
const deps = {
|
|
6007
|
-
paymentLedger: options.paymentLedger ?? createMemoryLedger(),
|
|
6008
|
-
// Sponsorship IS a gateway service: the agent holds no SUI for gas, and the
|
|
6009
|
-
// sponsor can only attach gas to a transaction this agent signed.
|
|
6010
|
-
sponsor: { sponsorUrl: options.gatewayUrl },
|
|
6011
|
-
gasMode: "sponsored",
|
|
6012
|
-
resolveSharedObjectVersions: (ids) => resolveSharedObjectVersionsFromGetObject(getObjectJson, ids),
|
|
6013
|
-
getFinalizedTx: finality.getFinalizedTx,
|
|
6014
|
-
...options.offerSecret ? { offerSecret: options.offerSecret } : {},
|
|
6015
|
-
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
|
|
6016
|
-
...options.logError ? { logError: options.logError } : {}
|
|
6017
|
-
};
|
|
6018
|
-
async function pay2(url, input) {
|
|
6019
|
-
let request;
|
|
6020
|
-
try {
|
|
6021
|
-
request = normalizePaidHttpRequest({
|
|
6022
|
-
url,
|
|
6023
|
-
method: input.method,
|
|
6024
|
-
...input.body !== void 0 ? { body: input.body } : {},
|
|
6025
|
-
...input.contentType !== void 0 ? { contentType: input.contentType } : {}
|
|
6026
|
-
});
|
|
6027
|
-
} catch (err) {
|
|
6028
|
-
options.logError?.("normalize paid request failed", err);
|
|
5294
|
+
} catch (err) {
|
|
5295
|
+
if (err instanceof PreparedVerificationUnavailableError) {
|
|
6029
5296
|
return {
|
|
6030
5297
|
status: "failed",
|
|
6031
5298
|
paid: false,
|
|
6032
|
-
code: DELEGATED_PAY_ERROR_CODES.
|
|
6033
|
-
detail:
|
|
6034
|
-
};
|
|
6035
|
-
}
|
|
6036
|
-
return payDelegatedSpendAccount({
|
|
6037
|
-
url,
|
|
6038
|
-
request,
|
|
6039
|
-
session,
|
|
6040
|
-
cfg: { gatewayUrl: options.gatewayUrl },
|
|
6041
|
-
deps,
|
|
6042
|
-
intent: input.intent,
|
|
6043
|
-
...input.preparedTerms ? { preparedTerms: input.preparedTerms } : {}
|
|
6044
|
-
});
|
|
6045
|
-
}
|
|
6046
|
-
async function verifySettlement(prepared, offer) {
|
|
6047
|
-
return verifyPreparedPayment({
|
|
6048
|
-
prepared,
|
|
6049
|
-
offer,
|
|
6050
|
-
delegateAddress: address,
|
|
6051
|
-
expectedGrantObjectId: binding.grantObjectId,
|
|
6052
|
-
expectedPoolObjectId: binding.poolObjectId
|
|
6053
|
-
});
|
|
6054
|
-
}
|
|
6055
|
-
return {
|
|
6056
|
-
ok: true,
|
|
6057
|
-
value: { address, deployment, grant: binding, session, pay: pay2, verifySettlement }
|
|
6058
|
-
};
|
|
6059
|
-
}
|
|
6060
|
-
|
|
6061
|
-
// src/stdio-login.ts
|
|
6062
|
-
import { createServer } from "http";
|
|
6063
|
-
import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
6064
|
-
import open from "open";
|
|
6065
|
-
var HEX642 = /^[0-9a-f]{64}$/;
|
|
6066
|
-
var PUBHEX = /^[0-9a-fA-F]{64}$/;
|
|
6067
|
-
function isPreflight(obj) {
|
|
6068
|
-
if (!obj || typeof obj !== "object") return false;
|
|
6069
|
-
const o = obj;
|
|
6070
|
-
return typeof o.state === "string" && HEX642.test(o.state) && typeof o.publicKey === "string" && PUBHEX.test(o.publicKey) && typeof o.delegateAddress === "string" && /^0x[0-9a-fA-F]{64}$/.test(o.delegateAddress) && typeof o.gateway === "string" && o.gateway.length > 0;
|
|
6071
|
-
}
|
|
6072
|
-
function isCallback(obj) {
|
|
6073
|
-
if (!obj || typeof obj !== "object") return false;
|
|
6074
|
-
const o = obj;
|
|
6075
|
-
return typeof o.state === "string" && HEX642.test(o.state) && typeof o.gatewayUrl === "string" && o.gatewayUrl.length > 0;
|
|
6076
|
-
}
|
|
6077
|
-
function stateEquals(a, b) {
|
|
6078
|
-
if (a.length !== b.length) return false;
|
|
6079
|
-
try {
|
|
6080
|
-
return timingSafeEqual2(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
|
|
6081
|
-
} catch {
|
|
6082
|
-
return false;
|
|
6083
|
-
}
|
|
6084
|
-
}
|
|
6085
|
-
function normalizeOrigin(url) {
|
|
6086
|
-
try {
|
|
6087
|
-
return new URL(url).origin;
|
|
6088
|
-
} catch {
|
|
6089
|
-
return url.replace(/\/+$/, "");
|
|
6090
|
-
}
|
|
6091
|
-
}
|
|
6092
|
-
function loopbackAlternateOrigin(origin) {
|
|
6093
|
-
try {
|
|
6094
|
-
const url = new URL(origin);
|
|
6095
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
6096
|
-
if (url.hostname === "localhost") {
|
|
6097
|
-
url.hostname = "127.0.0.1";
|
|
6098
|
-
return url.origin;
|
|
6099
|
-
}
|
|
6100
|
-
if (url.hostname === "127.0.0.1") {
|
|
6101
|
-
url.hostname = "localhost";
|
|
6102
|
-
return url.origin;
|
|
6103
|
-
}
|
|
6104
|
-
return null;
|
|
6105
|
-
} catch {
|
|
6106
|
-
return null;
|
|
6107
|
-
}
|
|
6108
|
-
}
|
|
6109
|
-
function normalizeUrl(url) {
|
|
6110
|
-
return url.replace(/\/+$/, "");
|
|
6111
|
-
}
|
|
6112
|
-
function readBody2(req, maxBytes = 16 * 1024) {
|
|
6113
|
-
return new Promise((resolve, reject2) => {
|
|
6114
|
-
const chunks = [];
|
|
6115
|
-
let len = 0;
|
|
6116
|
-
req.on("data", (c) => {
|
|
6117
|
-
len += c.length;
|
|
6118
|
-
if (len > maxBytes) {
|
|
6119
|
-
reject2(new Error("body too large"));
|
|
6120
|
-
req.destroy();
|
|
6121
|
-
return;
|
|
6122
|
-
}
|
|
6123
|
-
chunks.push(c);
|
|
6124
|
-
});
|
|
6125
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
6126
|
-
req.on("error", reject2);
|
|
6127
|
-
});
|
|
6128
|
-
}
|
|
6129
|
-
var SECURITY_HEADERS = {
|
|
6130
|
-
"cache-control": "no-store",
|
|
6131
|
-
"x-content-type-options": "nosniff",
|
|
6132
|
-
"referrer-policy": "no-referrer",
|
|
6133
|
-
"content-security-policy": "default-src 'none'"
|
|
6134
|
-
};
|
|
6135
|
-
var SUCCESS_HTML = `<!doctype html><meta charset=utf-8><title>SuiPay MCP - Connected</title><body style="font:16px system-ui;max-width:480px;margin:80px auto;padding:0 24px"><h1>\u2713 SuiPay MCP connected</h1><p>Credentials saved to <code>~/.suipay/credentials.json</code>. You can close this tab \u2014 your MCP client will pick up the new credentials automatically.</p>`;
|
|
6136
|
-
var failHtml = (m) => `<!doctype html><meta charset=utf-8><title>SuiPay MCP - Failed</title><body style="font:16px system-ui;max-width:480px;margin:80px auto;padding:0 24px"><h1>\xD7 Login failed</h1><p>${m}</p>`;
|
|
6137
|
-
async function startStdioLogin(opts) {
|
|
6138
|
-
const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
|
|
6139
|
-
const openBrowser = opts.openBrowser ?? false;
|
|
6140
|
-
const label = opts.label ?? "SuiPay MCP";
|
|
6141
|
-
const expectedOrigin = normalizeOrigin(opts.consoleUrl);
|
|
6142
|
-
const allowedCorsOrigins = /* @__PURE__ */ new Set([expectedOrigin]);
|
|
6143
|
-
const loopbackAlt = loopbackAlternateOrigin(expectedOrigin);
|
|
6144
|
-
if (loopbackAlt) allowedCorsOrigins.add(loopbackAlt);
|
|
6145
|
-
const expectedGateway = normalizeUrl(opts.gatewayUrl);
|
|
6146
|
-
const connectState = randomBytes3(32).toString("hex");
|
|
6147
|
-
const server = createServer();
|
|
6148
|
-
server.requestTimeout = 1e4;
|
|
6149
|
-
server.headersTimeout = 1e4;
|
|
6150
|
-
server.keepAliveTimeout = 5e3;
|
|
6151
|
-
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
6152
|
-
const port = server.address().port;
|
|
6153
|
-
const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
|
|
6154
|
-
const base = opts.consoleUrl.replace(/\/+$/, "");
|
|
6155
|
-
const connectUrl = `${base}/connect/agent?port=${port}&publicKey=${encodeURIComponent(opts.identity.publicKeyHex)}&delegateAddress=${encodeURIComponent(opts.identity.delegateAddress)}&label=${encodeURIComponent(label)}&gateway=${encodeURIComponent(opts.gatewayUrl)}&connectState=${connectState}`;
|
|
6156
|
-
try {
|
|
6157
|
-
opts.onUrl?.(connectUrl);
|
|
6158
|
-
} catch {
|
|
6159
|
-
}
|
|
6160
|
-
let preflightVerified = false;
|
|
6161
|
-
let completed = false;
|
|
6162
|
-
let error2 = null;
|
|
6163
|
-
const done = new Promise((resolve) => {
|
|
6164
|
-
const timer = setTimeout(() => {
|
|
6165
|
-
if (!completed) {
|
|
6166
|
-
error2 = new Error(`login timed out after ${timeoutMs}ms`);
|
|
6167
|
-
server.close();
|
|
6168
|
-
resolve();
|
|
6169
|
-
}
|
|
6170
|
-
}, timeoutMs);
|
|
6171
|
-
timer.unref?.();
|
|
6172
|
-
server.on("request", async (req, res) => {
|
|
6173
|
-
const hostHeader = (req.headers.host ?? "").toLowerCase();
|
|
6174
|
-
const origin = req.headers.origin;
|
|
6175
|
-
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
6176
|
-
const okHost = allowedHosts.has(hostHeader);
|
|
6177
|
-
let requestOrigin = null;
|
|
6178
|
-
if (typeof origin === "string") {
|
|
6179
|
-
try {
|
|
6180
|
-
requestOrigin = new URL(origin).origin;
|
|
6181
|
-
} catch {
|
|
6182
|
-
requestOrigin = null;
|
|
6183
|
-
}
|
|
6184
|
-
}
|
|
6185
|
-
const okOrigin = requestOrigin !== null && allowedCorsOrigins.has(requestOrigin);
|
|
6186
|
-
const cors = {
|
|
6187
|
-
"access-control-allow-origin": okOrigin && requestOrigin ? requestOrigin : expectedOrigin,
|
|
6188
|
-
"access-control-allow-methods": "POST, OPTIONS",
|
|
6189
|
-
"access-control-allow-headers": "content-type",
|
|
6190
|
-
vary: "origin"
|
|
6191
|
-
};
|
|
6192
|
-
const send = (code, body, type = "text/plain", extra = {}) => {
|
|
6193
|
-
res.writeHead(code, { "content-type": type, ...SECURITY_HEADERS, ...extra });
|
|
6194
|
-
res.end(body);
|
|
6195
|
-
};
|
|
6196
|
-
const okPath = url.pathname === "/preflight" || url.pathname === "/callback";
|
|
6197
|
-
if (req.method === "OPTIONS") {
|
|
6198
|
-
const extra = { ...cors };
|
|
6199
|
-
if (okHost && okOrigin && okPath && req.headers["access-control-request-private-network"] === "true") {
|
|
6200
|
-
extra["access-control-allow-private-network"] = "true";
|
|
6201
|
-
}
|
|
6202
|
-
res.writeHead(204, { ...SECURITY_HEADERS, ...extra });
|
|
6203
|
-
res.end();
|
|
6204
|
-
return;
|
|
6205
|
-
}
|
|
6206
|
-
if (req.method !== "POST" || !okPath) {
|
|
6207
|
-
send(404, "Not found");
|
|
6208
|
-
return;
|
|
6209
|
-
}
|
|
6210
|
-
if (!okHost) {
|
|
6211
|
-
send(403, "Forbidden");
|
|
6212
|
-
return;
|
|
6213
|
-
}
|
|
6214
|
-
if (!okOrigin) {
|
|
6215
|
-
send(403, "Forbidden");
|
|
6216
|
-
return;
|
|
6217
|
-
}
|
|
6218
|
-
const ct = (req.headers["content-type"] ?? "").toLowerCase();
|
|
6219
|
-
if (!ct.startsWith("application/json")) {
|
|
6220
|
-
send(415, "Content-Type must be application/json");
|
|
6221
|
-
return;
|
|
6222
|
-
}
|
|
6223
|
-
try {
|
|
6224
|
-
const parsed = JSON.parse(await readBody2(req));
|
|
6225
|
-
if (url.pathname === "/preflight") {
|
|
6226
|
-
if (!isPreflight(parsed)) {
|
|
6227
|
-
res.writeHead(400, {
|
|
6228
|
-
"content-type": "application/json",
|
|
6229
|
-
...cors,
|
|
6230
|
-
...SECURITY_HEADERS
|
|
6231
|
-
});
|
|
6232
|
-
res.end(JSON.stringify({ ok: false, error: "invalid preflight payload" }));
|
|
6233
|
-
return;
|
|
6234
|
-
}
|
|
6235
|
-
const derived = deriveSuiAddress(
|
|
6236
|
-
Uint8Array.from(Buffer.from(parsed.publicKey, "hex"))
|
|
6237
|
-
);
|
|
6238
|
-
const valid = stateEquals(parsed.state, connectState) && stateEquals(
|
|
6239
|
-
parsed.publicKey.toLowerCase(),
|
|
6240
|
-
opts.identity.publicKeyHex.toLowerCase()
|
|
6241
|
-
) && parsed.delegateAddress.toLowerCase() === opts.identity.delegateAddress.toLowerCase() && derived.toLowerCase() === opts.identity.delegateAddress.toLowerCase() && normalizeUrl(parsed.gateway) === expectedGateway;
|
|
6242
|
-
if (!valid) {
|
|
6243
|
-
res.writeHead(403, {
|
|
6244
|
-
"content-type": "application/json",
|
|
6245
|
-
...cors,
|
|
6246
|
-
...SECURITY_HEADERS
|
|
6247
|
-
});
|
|
6248
|
-
res.end(JSON.stringify({ ok: false, error: "preflight mismatch" }));
|
|
6249
|
-
return;
|
|
6250
|
-
}
|
|
6251
|
-
preflightVerified = true;
|
|
6252
|
-
res.writeHead(200, {
|
|
6253
|
-
"content-type": "application/json",
|
|
6254
|
-
...cors,
|
|
6255
|
-
...SECURITY_HEADERS
|
|
6256
|
-
});
|
|
6257
|
-
res.end(
|
|
6258
|
-
JSON.stringify({
|
|
6259
|
-
ok: true,
|
|
6260
|
-
publicKey: opts.identity.publicKeyHex,
|
|
6261
|
-
label,
|
|
6262
|
-
gateway: opts.gatewayUrl
|
|
6263
|
-
})
|
|
6264
|
-
);
|
|
6265
|
-
return;
|
|
6266
|
-
}
|
|
6267
|
-
if (!preflightVerified) {
|
|
6268
|
-
res.writeHead(428, {
|
|
6269
|
-
"content-type": "text/html",
|
|
6270
|
-
...cors,
|
|
6271
|
-
...SECURITY_HEADERS
|
|
6272
|
-
});
|
|
6273
|
-
res.end(
|
|
6274
|
-
failHtml("Local MCP bridge verification is required before the callback.")
|
|
6275
|
-
);
|
|
6276
|
-
return;
|
|
6277
|
-
}
|
|
6278
|
-
if (!isCallback(parsed)) {
|
|
6279
|
-
res.writeHead(400, {
|
|
6280
|
-
"content-type": "text/html",
|
|
6281
|
-
...cors,
|
|
6282
|
-
...SECURITY_HEADERS
|
|
6283
|
-
});
|
|
6284
|
-
res.end(failHtml("Callback payload missing required fields."));
|
|
6285
|
-
return;
|
|
6286
|
-
}
|
|
6287
|
-
if (!stateEquals(parsed.state, connectState)) {
|
|
6288
|
-
res.writeHead(403, {
|
|
6289
|
-
"content-type": "text/html",
|
|
6290
|
-
...cors,
|
|
6291
|
-
...SECURITY_HEADERS
|
|
6292
|
-
});
|
|
6293
|
-
res.end(failHtml("Callback state mismatch \u2014 refusing to save."));
|
|
6294
|
-
return;
|
|
6295
|
-
}
|
|
6296
|
-
if (normalizeUrl(parsed.gatewayUrl) !== expectedGateway) {
|
|
6297
|
-
res.writeHead(403, {
|
|
6298
|
-
"content-type": "text/html",
|
|
6299
|
-
...cors,
|
|
6300
|
-
...SECURITY_HEADERS
|
|
6301
|
-
});
|
|
6302
|
-
res.end(failHtml("Callback gateway mismatch \u2014 refusing to save."));
|
|
6303
|
-
return;
|
|
6304
|
-
}
|
|
6305
|
-
preflightVerified = false;
|
|
6306
|
-
persistGatewayUrl(opts.gatewayUrl);
|
|
6307
|
-
completed = true;
|
|
6308
|
-
res.writeHead(200, {
|
|
6309
|
-
"content-type": "text/html",
|
|
6310
|
-
...cors,
|
|
6311
|
-
...SECURITY_HEADERS
|
|
6312
|
-
});
|
|
6313
|
-
res.end(SUCCESS_HTML);
|
|
6314
|
-
setTimeout(() => server.close(), 100);
|
|
6315
|
-
resolve();
|
|
6316
|
-
} catch (err) {
|
|
6317
|
-
error2 = err instanceof Error ? err : new Error(String(err));
|
|
6318
|
-
res.writeHead(500, {
|
|
6319
|
-
"content-type": "text/html",
|
|
6320
|
-
...cors,
|
|
6321
|
-
...SECURITY_HEADERS
|
|
6322
|
-
});
|
|
6323
|
-
res.end(failHtml("Internal error parsing callback."));
|
|
6324
|
-
setTimeout(() => server.close(), 100);
|
|
6325
|
-
resolve();
|
|
6326
|
-
}
|
|
6327
|
-
});
|
|
6328
|
-
});
|
|
6329
|
-
if (openBrowser) {
|
|
6330
|
-
open(connectUrl).catch(() => {
|
|
6331
|
-
});
|
|
6332
|
-
}
|
|
6333
|
-
void done.then(() => {
|
|
6334
|
-
if (error2) {
|
|
5299
|
+
code: DELEGATED_PAY_ERROR_CODES.PREPARED_VERIFY_UNAVAILABLE,
|
|
5300
|
+
detail: err.message
|
|
5301
|
+
};
|
|
6335
5302
|
}
|
|
5303
|
+
return {
|
|
5304
|
+
status: "failed",
|
|
5305
|
+
paid: false,
|
|
5306
|
+
code: DELEGATED_PAY_ERROR_CODES.TX_MISMATCH,
|
|
5307
|
+
detail: err instanceof Error ? err.message : "prepared transaction mismatch"
|
|
5308
|
+
};
|
|
5309
|
+
}
|
|
5310
|
+
const prepared = args.signingRequest.resource;
|
|
5311
|
+
if (args.request.url !== prepared.url || args.request.method !== prepared.method) {
|
|
5312
|
+
return {
|
|
5313
|
+
status: "failed",
|
|
5314
|
+
paid: false,
|
|
5315
|
+
code: DELEGATED_PAY_ERROR_CODES.UNSUPPORTED_REQUEST,
|
|
5316
|
+
detail: "prepared resource request cannot be replayed"
|
|
5317
|
+
};
|
|
5318
|
+
}
|
|
5319
|
+
return payDelegatedSpendAccount({
|
|
5320
|
+
url: args.signingRequest.resource.url,
|
|
5321
|
+
request: args.request,
|
|
5322
|
+
session: args.session,
|
|
5323
|
+
cfg: args.cfg,
|
|
5324
|
+
deps: args.deps,
|
|
5325
|
+
preparedTerms: {
|
|
5326
|
+
recipient: args.expected.recipient,
|
|
5327
|
+
amount: args.expected.amount,
|
|
5328
|
+
asset: args.signingRequest.coinType,
|
|
5329
|
+
targetHash: args.signingRequest.targetHash
|
|
5330
|
+
},
|
|
5331
|
+
...args.intent ? { intent: args.intent } : {}
|
|
6336
5332
|
});
|
|
6337
|
-
return {
|
|
6338
|
-
connectUrl,
|
|
6339
|
-
port,
|
|
6340
|
-
connectState,
|
|
6341
|
-
done,
|
|
6342
|
-
close: () => {
|
|
6343
|
-
server.close();
|
|
6344
|
-
}
|
|
6345
|
-
};
|
|
6346
5333
|
}
|
|
6347
5334
|
|
|
6348
5335
|
// src/spend-account-pay.ts
|
|
6349
|
-
import { Transaction as
|
|
5336
|
+
import { Transaction as Transaction5 } from "@mysten/sui/transactions";
|
|
6350
5337
|
import {
|
|
6351
|
-
fromBase64 as
|
|
6352
|
-
normalizeSuiAddress as
|
|
5338
|
+
fromBase64 as fromBase645,
|
|
5339
|
+
normalizeSuiAddress as normalizeSuiAddress9,
|
|
6353
5340
|
toBase64 as toBase644
|
|
6354
5341
|
} from "@mysten/sui/utils";
|
|
6355
5342
|
var SUI_CLOCK3 = "0x6";
|
|
@@ -6387,11 +5374,11 @@ async function buildSpendAccountPayKind2(input) {
|
|
|
6387
5374
|
if (!input.termsHash.length) throw new Error("termsHash must be non-empty");
|
|
6388
5375
|
if (!input.paymentIdHash.length) throw new Error("paymentIdHash must be non-empty");
|
|
6389
5376
|
const auth = defaultAuthorityForRail("delegate", input.packageId);
|
|
6390
|
-
const target = `${
|
|
5377
|
+
const target = `${normalizeSuiAddress9(auth.packageId)}::${auth.module}::${auth.payFunction}`;
|
|
6391
5378
|
const hashHex = input.targetHash.replace(/^0x/i, "").toLowerCase();
|
|
6392
5379
|
if (!/^[0-9a-f]{64}$/.test(hashHex)) throw new Error("targetHash must be 32-byte hex");
|
|
6393
5380
|
const hash = Uint8Array.from(Buffer.from(hashHex, "hex"));
|
|
6394
|
-
const tx = new
|
|
5381
|
+
const tx = new Transaction5();
|
|
6395
5382
|
tx.setSender(input.delegate);
|
|
6396
5383
|
tx.moveCall({
|
|
6397
5384
|
target,
|
|
@@ -6425,29 +5412,29 @@ async function buildSpendAccountPayKind2(input) {
|
|
|
6425
5412
|
function verifySpendAccountPayReconstructs(bytes, expected) {
|
|
6426
5413
|
let data;
|
|
6427
5414
|
try {
|
|
6428
|
-
data =
|
|
5415
|
+
data = Transaction5.from(fromBase645(bytes)).getData();
|
|
6429
5416
|
} catch {
|
|
6430
5417
|
throw new Error("sponsored transaction is not valid Sui transaction bytes");
|
|
6431
5418
|
}
|
|
6432
|
-
if (!data.sender ||
|
|
5419
|
+
if (!data.sender || normalizeSuiAddress9(data.sender) !== normalizeSuiAddress9(expected.delegate)) {
|
|
6433
5420
|
throw new Error("sponsored transaction sender does not match delegate");
|
|
6434
5421
|
}
|
|
6435
5422
|
if (!data.commands || data.commands.length !== 1) {
|
|
6436
5423
|
throw new Error("sponsored transaction must contain exactly one command");
|
|
6437
5424
|
}
|
|
6438
5425
|
const auth = defaultAuthorityForRail("delegate", expected.packageId);
|
|
6439
|
-
const wanted = `${
|
|
5426
|
+
const wanted = `${normalizeSuiAddress9(auth.packageId)}::${auth.module}::${auth.payFunction}`;
|
|
6440
5427
|
const cmd = data.commands[0];
|
|
6441
5428
|
if (cmd.$kind !== "MoveCall" || !cmd.MoveCall) {
|
|
6442
5429
|
throw new Error("sponsored transaction is not a MoveCall");
|
|
6443
5430
|
}
|
|
6444
|
-
const got = `${
|
|
5431
|
+
const got = `${normalizeSuiAddress9(String(cmd.MoveCall.package))}::${cmd.MoveCall.module}::${cmd.MoveCall.function}`;
|
|
6445
5432
|
if (got !== wanted) {
|
|
6446
5433
|
throw new Error(`sponsored transaction does not reconstruct ${wanted}`);
|
|
6447
5434
|
}
|
|
6448
5435
|
let expectedKind;
|
|
6449
5436
|
try {
|
|
6450
|
-
expectedKind =
|
|
5437
|
+
expectedKind = Transaction5.fromKind(fromBase645(expected.kindBytes)).getData();
|
|
6451
5438
|
} catch {
|
|
6452
5439
|
throw new Error("sponsored transaction kind baseline invalid");
|
|
6453
5440
|
}
|
|
@@ -6604,7 +5591,7 @@ async function prepareSpendAccountPayment(args) {
|
|
|
6604
5591
|
const signingRequest = {
|
|
6605
5592
|
unsignedKindBytes: kindBytes,
|
|
6606
5593
|
delegateAddress: resolved.delegateAddress,
|
|
6607
|
-
packageId:
|
|
5594
|
+
packageId: normalizeSuiAddress9(authority.packageId),
|
|
6608
5595
|
poolObjectId: resolved.poolObjectId,
|
|
6609
5596
|
grantObjectId: resolved.grantObjectId,
|
|
6610
5597
|
policyId: resolved.onChainPolicyId,
|
|
@@ -6685,249 +5672,28 @@ function mapErrorCode(code) {
|
|
|
6685
5672
|
}
|
|
6686
5673
|
}
|
|
6687
5674
|
|
|
6688
|
-
// src/
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
}
|
|
6703
|
-
if (typeof value !== "string") return null;
|
|
6704
|
-
const raw = value.trim();
|
|
6705
|
-
if (!/^[0-9]+$/.test(raw)) return null;
|
|
6706
|
-
return BigInt(raw);
|
|
6707
|
-
}
|
|
6708
|
-
function catalogPriceAtomic(row) {
|
|
6709
|
-
return parseAtomic(row.price);
|
|
6710
|
-
}
|
|
6711
|
-
function catalogDecimals(row) {
|
|
6712
|
-
if (typeof row.decimals !== "number" || !Number.isInteger(row.decimals)) return null;
|
|
6713
|
-
if (row.decimals < 0 || row.decimals > 18) return null;
|
|
6714
|
-
return row.decimals;
|
|
6715
|
-
}
|
|
6716
|
-
function formatAtomic(amount, decimals) {
|
|
6717
|
-
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18) {
|
|
6718
|
-
return amount.toString();
|
|
6719
|
-
}
|
|
6720
|
-
const negative = amount < 0n;
|
|
6721
|
-
const abs = negative ? -amount : amount;
|
|
6722
|
-
const scale = 10n ** BigInt(decimals);
|
|
6723
|
-
const whole = abs / scale;
|
|
6724
|
-
const frac = abs % scale;
|
|
6725
|
-
const sign = negative ? "-" : "";
|
|
6726
|
-
if (decimals === 0 || frac === 0n) return `${sign}${whole.toString()}`;
|
|
6727
|
-
const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
|
|
6728
|
-
return `${sign}${whole.toString()}.${fracStr}`;
|
|
6729
|
-
}
|
|
6730
|
-
function minAtomic(left, right) {
|
|
6731
|
-
if (left == null) return right;
|
|
6732
|
-
if (right == null) return left;
|
|
6733
|
-
return left <= right ? left : right;
|
|
6734
|
-
}
|
|
6735
|
-
function isAffordable(price, budget, opts = {}) {
|
|
6736
|
-
if (price == null) return void 0;
|
|
6737
|
-
if (!sameCoinType2(opts.asset, budget.coinType)) return void 0;
|
|
6738
|
-
const remaining = parseAtomic(budget.sessionRemaining);
|
|
6739
|
-
if (remaining == null) return void 0;
|
|
6740
|
-
if (price > remaining) return false;
|
|
6741
|
-
const cap = minAtomic(
|
|
6742
|
-
parseAtomic(budget.maxPerPayment),
|
|
6743
|
-
parseAtomic(opts.targetMaxPerPayment ?? null)
|
|
6744
|
-
);
|
|
6745
|
-
if (cap != null && price > cap) return false;
|
|
6746
|
-
return true;
|
|
6747
|
-
}
|
|
6748
|
-
function withCatalogAffordability(service, budget) {
|
|
6749
|
-
if (!budget) return service;
|
|
6750
|
-
const price = catalogPriceAtomic(service);
|
|
6751
|
-
const decimals = catalogDecimals(service);
|
|
6752
|
-
const targetMax = typeof service.maxPerPayment === "string" ? service.maxPerPayment : null;
|
|
6753
|
-
const sameAsset = sameCoinType2(service.asset, budget.coinType);
|
|
6754
|
-
const affordable = isAffordable(price, budget, {
|
|
6755
|
-
targetMaxPerPayment: targetMax,
|
|
6756
|
-
asset: service.asset
|
|
6757
|
-
});
|
|
6758
|
-
const remaining = sameAsset ? parseAtomic(budget.sessionRemaining) : null;
|
|
6759
|
-
const out = { ...service };
|
|
6760
|
-
if (price != null) out.price = price.toString();
|
|
6761
|
-
if (decimals != null) out.decimals = decimals;
|
|
6762
|
-
if (remaining != null) out.sessionRemaining = remaining.toString();
|
|
6763
|
-
if (affordable !== void 0) out.affordable = affordable;
|
|
6764
|
-
if (decimals != null && price != null) out.priceDisplay = formatAtomic(price, decimals);
|
|
6765
|
-
if (decimals != null && remaining != null) {
|
|
6766
|
-
out.remainingDisplay = formatAtomic(remaining, decimals);
|
|
6767
|
-
}
|
|
6768
|
-
return out;
|
|
6769
|
-
}
|
|
6770
|
-
|
|
6771
|
-
// src/discover-catalog.ts
|
|
6772
|
-
function asString(value) {
|
|
6773
|
-
return typeof value === "string" ? value.trim() : "";
|
|
6774
|
-
}
|
|
6775
|
-
function catalogRows(catalog) {
|
|
6776
|
-
if (Array.isArray(catalog)) return catalog.filter((row) => row && typeof row === "object");
|
|
6777
|
-
if (!catalog || typeof catalog !== "object") return [];
|
|
6778
|
-
const services = catalog.services;
|
|
6779
|
-
if (!Array.isArray(services)) return [];
|
|
6780
|
-
return services.filter((row) => row && typeof row === "object");
|
|
6781
|
-
}
|
|
6782
|
-
function authorizationEpochOf(row) {
|
|
6783
|
-
if (typeof row.authorizationEpoch === "number" && Number.isInteger(row.authorizationEpoch)) {
|
|
6784
|
-
return row.authorizationEpoch;
|
|
6785
|
-
}
|
|
6786
|
-
const state = row.state;
|
|
6787
|
-
if (state && typeof state === "object") {
|
|
6788
|
-
const revision = state.revision;
|
|
6789
|
-
if (typeof revision?.authorizationEpoch === "number" && Number.isInteger(revision.authorizationEpoch)) {
|
|
6790
|
-
return revision.authorizationEpoch;
|
|
6791
|
-
}
|
|
6792
|
-
}
|
|
6793
|
-
return 1;
|
|
6794
|
-
}
|
|
6795
|
-
function catalogRowTargetHash(row) {
|
|
6796
|
-
const id = asString(row.id);
|
|
6797
|
-
const path = asString(row.path);
|
|
6798
|
-
const method = asString(row.method) || "GET";
|
|
6799
|
-
const network = asString(row.network);
|
|
6800
|
-
const asset = asString(row.asset);
|
|
6801
|
-
const recipient = asString(row.payTo) || asString(row.recipient);
|
|
6802
|
-
const providerId = asString(row.ownerAddress) || asString(row.providerId);
|
|
6803
|
-
const endpointId = asString(row.endpointId) || id;
|
|
6804
|
-
if (!id || !path || !network || !asset || !recipient || !providerId) return null;
|
|
6805
|
-
try {
|
|
6806
|
-
return targetHashFromFingerprint(
|
|
6807
|
-
authorizationFingerprint({
|
|
6808
|
-
network,
|
|
6809
|
-
providerId,
|
|
6810
|
-
serviceId: id,
|
|
6811
|
-
endpointId,
|
|
6812
|
-
method,
|
|
6813
|
-
path,
|
|
6814
|
-
asset,
|
|
6815
|
-
recipient,
|
|
6816
|
-
authorizationEpoch: authorizationEpochOf(row)
|
|
6817
|
-
})
|
|
6818
|
-
).toLowerCase();
|
|
6819
|
-
} catch {
|
|
6820
|
-
return null;
|
|
6821
|
-
}
|
|
6822
|
-
}
|
|
6823
|
-
function catalogRowResourceUrl(row, gatewayUrl) {
|
|
6824
|
-
const listed = asString(row.resourceUrl);
|
|
6825
|
-
if (/^https?:\/\//i.test(listed)) return listed.replace(/\/+$/, "");
|
|
6826
|
-
const path = asString(row.path);
|
|
6827
|
-
const base = gatewayUrl.replace(/\/+$/, "");
|
|
6828
|
-
if (!path) return base;
|
|
6829
|
-
return `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
6830
|
-
}
|
|
6831
|
-
function matchesQuery(row, query) {
|
|
6832
|
-
if (!query) return true;
|
|
6833
|
-
return JSON.stringify(row).toLowerCase().includes(query);
|
|
6834
|
-
}
|
|
6835
|
-
function joinDiscoverCatalog(opts) {
|
|
6836
|
-
const query = opts.query?.trim().toLowerCase() ?? "";
|
|
6837
|
-
const rows = catalogRows(opts.catalog);
|
|
6838
|
-
const scoped = Array.isArray(opts.policies);
|
|
6839
|
-
const byHash = /* @__PURE__ */ new Map();
|
|
6840
|
-
const allowedTargetHashes = [];
|
|
6841
|
-
if (scoped) {
|
|
6842
|
-
for (const policy of opts.policies) {
|
|
6843
|
-
for (const target of policy.targets) {
|
|
6844
|
-
const hash = asString(target.targetHash).toLowerCase();
|
|
6845
|
-
if (!hash) continue;
|
|
6846
|
-
allowedTargetHashes.push(hash);
|
|
6847
|
-
if (!byHash.has(hash)) byHash.set(hash, { ...policy, target });
|
|
6848
|
-
}
|
|
6849
|
-
}
|
|
6850
|
-
}
|
|
6851
|
-
const services = [];
|
|
6852
|
-
for (const row of rows) {
|
|
6853
|
-
const targetHash = catalogRowTargetHash(row);
|
|
6854
|
-
const url = catalogRowResourceUrl(row, opts.gatewayUrl);
|
|
6855
|
-
const bound = targetHash && scoped ? byHash.get(targetHash) : void 0;
|
|
6856
|
-
if (scoped && !bound) continue;
|
|
6857
|
-
const service = {
|
|
6858
|
-
...row,
|
|
6859
|
-
...targetHash ? { targetHash } : {},
|
|
6860
|
-
url
|
|
6861
|
-
};
|
|
6862
|
-
if (bound) {
|
|
6863
|
-
service.policyId = bound.policyId;
|
|
6864
|
-
service.onChainPolicyId = bound.onChainPolicyId;
|
|
6865
|
-
service.policyStatus = bound.status;
|
|
6866
|
-
if (bound.target.recipient) service.recipient = bound.target.recipient;
|
|
6867
|
-
if (bound.target.maxPerPayment) service.maxPerPayment = bound.target.maxPerPayment;
|
|
6868
|
-
}
|
|
6869
|
-
if (!matchesQuery(service, query)) continue;
|
|
6870
|
-
services.push(withCatalogAffordability(service, opts.budget));
|
|
6871
|
-
}
|
|
6872
|
-
return scoped ? { accessMode: "scoped", count: services.length, services, allowedTargetHashes } : { accessMode: "open", count: services.length, services };
|
|
6873
|
-
}
|
|
6874
|
-
|
|
6875
|
-
// src/tool-result.ts
|
|
6876
|
-
import { pathToFileURL } from "url";
|
|
6877
|
-
|
|
6878
|
-
// src/delivery-file.ts
|
|
6879
|
-
import { mkdirSync as mkdirSync2, writeFileSync, chmodSync as chmodSync2 } from "fs";
|
|
6880
|
-
import { dirname as dirname2, join as join3 } from "path";
|
|
6881
|
-
function mcpDeliveriesDir() {
|
|
6882
|
-
return join3(dirname2(mcpCredentialsPath()), "deliveries");
|
|
6883
|
-
}
|
|
6884
|
-
var EXT = {
|
|
6885
|
-
"image/png": ".png",
|
|
6886
|
-
"image/jpeg": ".jpg",
|
|
6887
|
-
"image/jpg": ".jpg",
|
|
6888
|
-
"image/webp": ".webp",
|
|
6889
|
-
"image/gif": ".gif",
|
|
6890
|
-
"image/svg+xml": ".svg",
|
|
6891
|
-
"audio/mpeg": ".mp3",
|
|
6892
|
-
"audio/wav": ".wav",
|
|
6893
|
-
"audio/ogg": ".ogg",
|
|
6894
|
-
"video/mp4": ".mp4",
|
|
6895
|
-
"application/pdf": ".pdf",
|
|
6896
|
-
"application/json": ".json",
|
|
6897
|
-
"text/plain": ".txt"
|
|
6898
|
-
};
|
|
6899
|
-
function extensionFor(contentType) {
|
|
6900
|
-
const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
6901
|
-
return EXT[type] ?? ".bin";
|
|
6902
|
-
}
|
|
6903
|
-
function fileStem(digest, challengeId) {
|
|
6904
|
-
const raw = (digest ?? challengeId ?? `delivery-${Date.now()}`).trim();
|
|
6905
|
-
const safe = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6906
|
-
return (safe.slice(0, 80) || `delivery-${Date.now()}`).replace(/^\.+$/, "delivery");
|
|
6907
|
-
}
|
|
6908
|
-
function saveDeliveredBody(input) {
|
|
6909
|
-
if (input.bytes.byteLength === 0) return void 0;
|
|
6910
|
-
const dir = mcpDeliveriesDir();
|
|
6911
|
-
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
6912
|
-
const path = join3(
|
|
6913
|
-
dir,
|
|
6914
|
-
`${fileStem(input.digest, input.challengeId)}${extensionFor(input.contentType)}`
|
|
6915
|
-
);
|
|
6916
|
-
writeFileSync(path, input.bytes, { mode: 384 });
|
|
6917
|
-
chmodSync2(path, 384);
|
|
6918
|
-
return path;
|
|
5675
|
+
// src/tools.ts
|
|
5676
|
+
function payerTrace(deps) {
|
|
5677
|
+
const hooks = deps.trace;
|
|
5678
|
+
const buyerAccountId = deps.auth?.buyerAccountId;
|
|
5679
|
+
if (!hooks || !buyerAccountId) return null;
|
|
5680
|
+
return {
|
|
5681
|
+
sink: hooks.sink,
|
|
5682
|
+
traceId: hooks.context.traceId,
|
|
5683
|
+
requestId: hooks.context.requestId,
|
|
5684
|
+
buyerAccountId,
|
|
5685
|
+
connectionId: hooks.runtime.connectionId,
|
|
5686
|
+
...hooks.runtime.secret ? { secret: hooks.runtime.secret } : {},
|
|
5687
|
+
...hooks.runtime.now ? { now: hooks.runtime.now } : {}
|
|
5688
|
+
};
|
|
6919
5689
|
}
|
|
6920
|
-
|
|
6921
|
-
// src/tool-result.ts
|
|
6922
|
-
var MAX_MCP_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
5690
|
+
var AUTH_REQUIRED = "No SuiPay credentials - run suipay_login to create a local delegate key, then bind a spend-account grant in the console.";
|
|
6923
5691
|
function text(value) {
|
|
6924
5692
|
return {
|
|
6925
|
-
content: [
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
}
|
|
6930
|
-
]
|
|
5693
|
+
content: [{
|
|
5694
|
+
type: "text",
|
|
5695
|
+
text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
|
|
5696
|
+
}]
|
|
6931
5697
|
};
|
|
6932
5698
|
}
|
|
6933
5699
|
function error(value) {
|
|
@@ -6939,11 +5705,7 @@ function closedError(code, detail) {
|
|
|
6939
5705
|
function closedPayError(code, detail) {
|
|
6940
5706
|
return error({ status: "failed", paid: false, code, detail });
|
|
6941
5707
|
}
|
|
6942
|
-
|
|
6943
|
-
if (!receipt || typeof receipt !== "object") return void 0;
|
|
6944
|
-
const id = receipt.challengeId;
|
|
6945
|
-
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
6946
|
-
}
|
|
5708
|
+
var MAX_MCP_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
6947
5709
|
function paidResult(result) {
|
|
6948
5710
|
if (!isBinaryBody(result.body)) return text(result);
|
|
6949
5711
|
const body = result.body;
|
|
@@ -6952,28 +5714,8 @@ function paidResult(result) {
|
|
|
6952
5714
|
contentType: body.contentType,
|
|
6953
5715
|
byteLength: body.byteLength
|
|
6954
5716
|
};
|
|
6955
|
-
let savedTo;
|
|
6956
|
-
try {
|
|
6957
|
-
savedTo = saveDeliveredBody({
|
|
6958
|
-
bytes: body.bytes,
|
|
6959
|
-
contentType: body.contentType,
|
|
6960
|
-
digest: result.txDigest,
|
|
6961
|
-
challengeId: receiptChallengeId(result.receipt)
|
|
6962
|
-
});
|
|
6963
|
-
} catch {
|
|
6964
|
-
savedTo = void 0;
|
|
6965
|
-
}
|
|
6966
|
-
const payload = savedTo ? {
|
|
6967
|
-
savedTo,
|
|
6968
|
-
savedToUrl: pathToFileURL(savedTo).href,
|
|
6969
|
-
...result,
|
|
6970
|
-
body: shape
|
|
6971
|
-
} : { ...result, body: shape };
|
|
6972
5717
|
const content = [
|
|
6973
|
-
{
|
|
6974
|
-
type: "text",
|
|
6975
|
-
text: JSON.stringify(payload, null, 2)
|
|
6976
|
-
}
|
|
5718
|
+
{ type: "text", text: JSON.stringify({ ...result, body: shape }, null, 2) }
|
|
6977
5719
|
];
|
|
6978
5720
|
if (body.contentType.startsWith("image/") && body.bytes.byteLength > 0 && body.bytes.byteLength <= MAX_MCP_IMAGE_BYTES) {
|
|
6979
5721
|
content.push({
|
|
@@ -6984,28 +5726,6 @@ function paidResult(result) {
|
|
|
6984
5726
|
}
|
|
6985
5727
|
return { content };
|
|
6986
5728
|
}
|
|
6987
|
-
function settlePayResult(result) {
|
|
6988
|
-
if (result.paid === true) return paidResult(result);
|
|
6989
|
-
return error(result);
|
|
6990
|
-
}
|
|
6991
|
-
|
|
6992
|
-
// src/tools.ts
|
|
6993
|
-
function payerTrace(deps) {
|
|
6994
|
-
const hooks = deps.trace;
|
|
6995
|
-
const buyerAccountId = deps.auth?.buyerAccountId;
|
|
6996
|
-
if (!hooks || !buyerAccountId) return null;
|
|
6997
|
-
return {
|
|
6998
|
-
sink: hooks.sink,
|
|
6999
|
-
traceId: hooks.context.traceId,
|
|
7000
|
-
requestId: hooks.context.requestId,
|
|
7001
|
-
buyerAccountId,
|
|
7002
|
-
connectionId: hooks.runtime.connectionId,
|
|
7003
|
-
...hooks.runtime.secret ? { secret: hooks.runtime.secret } : {},
|
|
7004
|
-
...hooks.runtime.now ? { now: hooks.runtime.now } : {}
|
|
7005
|
-
};
|
|
7006
|
-
}
|
|
7007
|
-
var AUTH_REQUIRED = "No SuiPay credentials - call suipay_login first. It returns a console URL; approve the spend-account grant in the browser. The next tool call picks up ~/.suipay/credentials.json automatically.";
|
|
7008
|
-
var LOGIN_BG_TIMEOUT_MS = 5 * 6e4;
|
|
7009
5729
|
function preparedResult(result) {
|
|
7010
5730
|
return text({
|
|
7011
5731
|
status: "prepared",
|
|
@@ -7025,7 +5745,7 @@ function normalizePayIntent(args) {
|
|
|
7025
5745
|
if (!/^0x[0-9a-fA-F]{1,64}$/.test(raw)) {
|
|
7026
5746
|
throw new Error("recipient must be a 0x-prefixed Sui address");
|
|
7027
5747
|
}
|
|
7028
|
-
const normalized =
|
|
5748
|
+
const normalized = normalizeSuiAddress10(raw);
|
|
7029
5749
|
if (!isValidSuiAddress(normalized) || /^0x0+$/.test(normalized)) {
|
|
7030
5750
|
throw new Error("recipient must be a non-zero Sui address");
|
|
7031
5751
|
}
|
|
@@ -7044,7 +5764,7 @@ function intentViolation(intent, prepared) {
|
|
|
7044
5764
|
if (intent.recipient) {
|
|
7045
5765
|
let preparedRecipient;
|
|
7046
5766
|
try {
|
|
7047
|
-
preparedRecipient =
|
|
5767
|
+
preparedRecipient = normalizeSuiAddress10(prepared.recipient);
|
|
7048
5768
|
} catch {
|
|
7049
5769
|
return "prepared payment does not name a valid recipient";
|
|
7050
5770
|
}
|
|
@@ -7150,10 +5870,10 @@ async function pay(args, cfg, deps = {}) {
|
|
|
7150
5870
|
...deps.fetchImpl && !deps.agentHeld.payerDeps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7151
5871
|
}
|
|
7152
5872
|
});
|
|
7153
|
-
return
|
|
5873
|
+
return settled.status === "ok" ? paidResult(settled) : error(settled);
|
|
7154
5874
|
}
|
|
7155
5875
|
if (result.status === "prepared") return preparedResult(result);
|
|
7156
|
-
return
|
|
5876
|
+
return result.status === "ok" ? paidResult(result) : error(result);
|
|
7157
5877
|
}
|
|
7158
5878
|
if (deps.auth) {
|
|
7159
5879
|
return error(
|
|
@@ -7168,259 +5888,122 @@ async function pay(args, cfg, deps = {}) {
|
|
|
7168
5888
|
}
|
|
7169
5889
|
const local = loadLocalSigner();
|
|
7170
5890
|
if (!local) return error(AUTH_REQUIRED);
|
|
7171
|
-
return payStdioWithLocalKey({ request, intent, cfg, deps });
|
|
5891
|
+
return payStdioWithLocalKey({ request, intent, cfg, deps, local });
|
|
7172
5892
|
}
|
|
7173
|
-
function
|
|
7174
|
-
return
|
|
5893
|
+
function snapshotFromBinding(binding, delegateAddress) {
|
|
5894
|
+
return {
|
|
5895
|
+
grantId: binding.grantObjectId,
|
|
5896
|
+
status: "active",
|
|
5897
|
+
grantObjectId: binding.grantObjectId,
|
|
5898
|
+
poolObjectId: binding.poolObjectId,
|
|
5899
|
+
coinType: binding.coinType,
|
|
5900
|
+
delegateAddress,
|
|
5901
|
+
policies: binding.policies,
|
|
5902
|
+
accessMode: binding.accessMode,
|
|
5903
|
+
...binding.maxPerPayment != null ? { maxPerPayment: binding.maxPerPayment } : {},
|
|
5904
|
+
...binding.sessionCap != null ? { sessionCap: binding.sessionCap } : {}
|
|
5905
|
+
};
|
|
7175
5906
|
}
|
|
7176
|
-
function
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
const live = [];
|
|
7187
|
-
for (const grant of grants) {
|
|
7188
|
-
if (!grant || typeof grant !== "object") continue;
|
|
7189
|
-
const g = grant;
|
|
7190
|
-
const id = g.grantObjectId;
|
|
7191
|
-
if (typeof id !== "string" || !id.trim()) continue;
|
|
7192
|
-
named += 1;
|
|
7193
|
-
if (isExpiredBootstrapGrant(g.expiresAtMs, nowMs)) continue;
|
|
7194
|
-
live.push(id.trim());
|
|
7195
|
-
}
|
|
7196
|
-
if (live.length === 1) return { kind: "live", grantObjectId: live[0] };
|
|
7197
|
-
if (live.length > 1) return { kind: "ambiguous" };
|
|
7198
|
-
if (named > 0) return { kind: "absent" };
|
|
7199
|
-
if (grants.length > 0) return { kind: "unknown", detail: "delegate grants list was not usable" };
|
|
7200
|
-
return { kind: "absent" };
|
|
7201
|
-
}
|
|
7202
|
-
async function checkLiveDelegateGrant(args) {
|
|
7203
|
-
const deployment = await fetchGatewayDeployment({
|
|
7204
|
-
gatewayUrl: args.gatewayUrl,
|
|
7205
|
-
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}
|
|
7206
|
-
});
|
|
7207
|
-
if (!deployment.ok) {
|
|
7208
|
-
return {
|
|
7209
|
-
kind: "unknown",
|
|
7210
|
-
detail: "gateway deployment identity could not be verified"
|
|
7211
|
-
};
|
|
7212
|
-
}
|
|
7213
|
-
const listed = await bootstrapPayerGrants({
|
|
7214
|
-
gatewayUrl: args.gatewayUrl,
|
|
7215
|
-
context: {
|
|
7216
|
-
network: deployment.value.network,
|
|
7217
|
-
packageId: deployment.value.packageId,
|
|
7218
|
-
delegateAddress: args.delegateAddress
|
|
5907
|
+
async function payStdioWithLocalKey(args) {
|
|
5908
|
+
const { request, intent, cfg, deps, local } = args;
|
|
5909
|
+
const resolved = await resolvePayerSession({
|
|
5910
|
+
gatewayUrl: cfg.gatewayUrl,
|
|
5911
|
+
delegateAddress: local.address,
|
|
5912
|
+
signPersonalMessage: local.signPersonalMessage,
|
|
5913
|
+
signer: {
|
|
5914
|
+
address: local.address,
|
|
5915
|
+
signTransaction: local.signTransaction,
|
|
5916
|
+
signPersonalMessage: local.signPersonalMessage
|
|
7219
5917
|
},
|
|
7220
|
-
|
|
7221
|
-
...args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}
|
|
5918
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7222
5919
|
});
|
|
7223
|
-
if (!
|
|
7224
|
-
return
|
|
7225
|
-
kind: "unknown",
|
|
7226
|
-
detail: `delegate grants read ${listed.error} (HTTP ${listed.status})`
|
|
7227
|
-
};
|
|
7228
|
-
}
|
|
7229
|
-
if (!Array.isArray(listed.data.grants) || listed.data.grants.length === 0) {
|
|
7230
|
-
return { kind: "absent" };
|
|
7231
|
-
}
|
|
7232
|
-
return grantObjectIdFromBootstrap(listed.data.grants, Date.now());
|
|
7233
|
-
}
|
|
7234
|
-
async function payStdioWithLocalKey(args) {
|
|
7235
|
-
const { request, intent, cfg, deps } = args;
|
|
7236
|
-
const built = await stdioPayerAndReader(cfg, deps);
|
|
7237
|
-
if (!built.ok) return built.result;
|
|
7238
|
-
const payer = built.payer;
|
|
7239
|
-
const grantCeiling = payer.grant.maxPerPayment;
|
|
7240
|
-
const maxAmount = intent.maxAmount !== void 0 ? intent.maxAmount.toString() : grantCeiling != null && grantCeiling !== "" ? grantCeiling : null;
|
|
7241
|
-
if (maxAmount === null) {
|
|
7242
|
-
return error({
|
|
7243
|
-
status: "failed",
|
|
7244
|
-
paid: false,
|
|
7245
|
-
code: "POLICY_REJECTED",
|
|
7246
|
-
detail: "stdio pay requires maxAmount or a grant maxPerPayment; refusing to default the ceiling to the offer"
|
|
7247
|
-
});
|
|
5920
|
+
if (!resolved.ok) {
|
|
5921
|
+
return closedPayError(resolved.error.code, resolved.error.detail);
|
|
7248
5922
|
}
|
|
7249
|
-
const
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
5923
|
+
const snapshot = snapshotFromBinding(resolved.value.binding, local.address);
|
|
5924
|
+
const packageId = resolved.value.session.packageId;
|
|
5925
|
+
const result = await prepareSpendAccountPayment({
|
|
5926
|
+
url: request.url,
|
|
5927
|
+
request,
|
|
5928
|
+
session: {
|
|
5929
|
+
snapshot,
|
|
5930
|
+
packageId,
|
|
5931
|
+
...resolved.value.session.assertLive ? { assertLive: resolved.value.session.assertLive } : {}
|
|
5932
|
+
},
|
|
5933
|
+
cfg,
|
|
5934
|
+
deps: {
|
|
5935
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {},
|
|
5936
|
+
...deps.buildPayKind ? { buildPayKind: deps.buildPayKind } : {}
|
|
7257
5937
|
}
|
|
7258
5938
|
});
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
if (local && shouldSelect) {
|
|
7269
|
-
const check = await checkLiveDelegateGrant({
|
|
7270
|
-
gatewayUrl,
|
|
7271
|
-
delegateAddress: local.address,
|
|
7272
|
-
signPersonalMessage: local.signPersonalMessage,
|
|
7273
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7274
|
-
});
|
|
7275
|
-
if (check.kind === "ambiguous") {
|
|
7276
|
-
return {
|
|
7277
|
-
ok: false,
|
|
7278
|
-
result: closedError(
|
|
7279
|
-
"AMBIGUOUS_GRANT",
|
|
7280
|
-
"multiple live grants name this key; pass a selector (grantObjectId / asset / coinType)"
|
|
7281
|
-
)
|
|
7282
|
-
};
|
|
5939
|
+
if (result.status === "prepared") {
|
|
5940
|
+
const violation = intentViolation(intent, result.signingRequest);
|
|
5941
|
+
if (violation) {
|
|
5942
|
+
return error({
|
|
5943
|
+
status: "failed",
|
|
5944
|
+
paid: false,
|
|
5945
|
+
code: "POLICY_REJECTED",
|
|
5946
|
+
detail: violation
|
|
5947
|
+
});
|
|
7283
5948
|
}
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
5949
|
+
const agentHeld = deps.agentHeld ?? settlementFromLocalSigner(cfg, local, packageId);
|
|
5950
|
+
const grantCeiling = resolved.value.binding.maxPerPayment;
|
|
5951
|
+
const maxAmount = intent.maxAmount !== void 0 ? intent.maxAmount.toString() : grantCeiling != null && grantCeiling !== "" ? grantCeiling : null;
|
|
5952
|
+
if (maxAmount === null) {
|
|
5953
|
+
return error({
|
|
5954
|
+
status: "failed",
|
|
5955
|
+
paid: false,
|
|
5956
|
+
code: "POLICY_REJECTED",
|
|
5957
|
+
detail: "stdio pay requires maxAmount or a grant maxPerPayment; refusing to default the ceiling to the offer"
|
|
5958
|
+
});
|
|
5959
|
+
}
|
|
5960
|
+
const spendIntent = {
|
|
5961
|
+
maxAmount,
|
|
5962
|
+
asset: resolved.value.binding.coinType,
|
|
5963
|
+
...intent.recipient ? { recipient: intent.recipient } : {}
|
|
7297
5964
|
};
|
|
5965
|
+
const settled = await settlePreparedMcpPayment({
|
|
5966
|
+
signingRequest: result.signingRequest,
|
|
5967
|
+
request,
|
|
5968
|
+
expected: {
|
|
5969
|
+
amount: result.signingRequest.amount,
|
|
5970
|
+
recipient: intent.recipient ?? result.signingRequest.recipient,
|
|
5971
|
+
delegateAddress: local.address
|
|
5972
|
+
},
|
|
5973
|
+
signer: local,
|
|
5974
|
+
getObjectJson: agentHeld.getObjectJson,
|
|
5975
|
+
session: {
|
|
5976
|
+
...resolved.value.session,
|
|
5977
|
+
signer: local
|
|
5978
|
+
},
|
|
5979
|
+
cfg: { gatewayUrl: cfg.gatewayUrl },
|
|
5980
|
+
deps: {
|
|
5981
|
+
...agentHeld.payerDeps,
|
|
5982
|
+
...deps.fetchImpl && !agentHeld.payerDeps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
5983
|
+
},
|
|
5984
|
+
intent: spendIntent
|
|
5985
|
+
});
|
|
5986
|
+
return settled.status === "ok" ? paidResult(settled) : error(settled);
|
|
7298
5987
|
}
|
|
7299
|
-
return
|
|
7300
|
-
}
|
|
7301
|
-
function stdioReaderForPayer(cfg, deps, payer) {
|
|
7302
|
-
return (deps.createReader ?? createReader)({
|
|
7303
|
-
gatewayUrl: stdioGatewayUrl(cfg),
|
|
7304
|
-
context: {
|
|
7305
|
-
network: payer.deployment.network,
|
|
7306
|
-
packageId: payer.deployment.packageId,
|
|
7307
|
-
grantObjectId: payer.grant.grantObjectId,
|
|
7308
|
-
delegateAddress: payer.address
|
|
7309
|
-
},
|
|
7310
|
-
signPersonalMessage: async (message) => {
|
|
7311
|
-
const { signature } = await loadLocalKeypair().signPersonalMessage(message);
|
|
7312
|
-
return signature;
|
|
7313
|
-
},
|
|
7314
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7315
|
-
});
|
|
7316
|
-
}
|
|
7317
|
-
function budgetFromBalance(balance, grant) {
|
|
7318
|
-
if (!balance.ok || !balance.data) return void 0;
|
|
7319
|
-
return {
|
|
7320
|
-
sessionRemaining: balance.data.sessionRemaining ?? null,
|
|
7321
|
-
maxPerPayment: balance.data.maxPerPayment ?? grant.maxPerPayment ?? null,
|
|
7322
|
-
coinType: balance.data.coinType ?? grant.coinType ?? null
|
|
7323
|
-
};
|
|
7324
|
-
}
|
|
7325
|
-
async function fetchPublicCatalog(gatewayUrl, fetchImpl) {
|
|
7326
|
-
const catalogUrl = new URL("/v1/services", gatewayUrl);
|
|
7327
|
-
const response = await (fetchImpl ?? fetch)(catalogUrl);
|
|
7328
|
-
if (!response.ok) return { ok: false, result: error(`gateway unavailable: HTTP ${response.status}`) };
|
|
7329
|
-
return { ok: true, catalog: await response.json() };
|
|
7330
|
-
}
|
|
7331
|
-
async function publicCatalog(gatewayUrl, query, fetchImpl) {
|
|
7332
|
-
const loaded = await fetchPublicCatalog(gatewayUrl, fetchImpl);
|
|
7333
|
-
if (!loaded.ok) return loaded.result;
|
|
7334
|
-
return text(
|
|
7335
|
-
joinDiscoverCatalog({
|
|
7336
|
-
catalog: loaded.catalog,
|
|
7337
|
-
gatewayUrl,
|
|
7338
|
-
query
|
|
7339
|
-
})
|
|
7340
|
-
);
|
|
5988
|
+
return result.status === "ok" ? paidResult(result) : error(result);
|
|
7341
5989
|
}
|
|
7342
5990
|
async function discover(args, cfg, deps = {}) {
|
|
7343
|
-
|
|
7344
|
-
|
|
5991
|
+
const url = new URL("/v1/services", cfg.gatewayUrl);
|
|
5992
|
+
if (args.query) url.searchParams.set("q", args.query);
|
|
5993
|
+
const response = await (deps.fetchImpl ?? fetch)(url);
|
|
5994
|
+
if (!response.ok) return error(`gateway unavailable: HTTP ${response.status}`);
|
|
5995
|
+
return text(await response.json());
|
|
5996
|
+
}
|
|
5997
|
+
async function accessContext(deps = {}) {
|
|
5998
|
+
if (!deps.auth) return error(AUTH_REQUIRED);
|
|
5999
|
+
if (!deps.auth.access) {
|
|
6000
|
+
return error(
|
|
6001
|
+
"SuiPay access context unavailable. Reconnect this MCP session or inspect the connection in buyer console."
|
|
6002
|
+
);
|
|
7345
6003
|
}
|
|
7346
|
-
|
|
7347
|
-
if (local) {
|
|
7348
|
-
const check = await checkLiveDelegateGrant({
|
|
7349
|
-
gatewayUrl: stdioGatewayUrl(cfg),
|
|
7350
|
-
delegateAddress: local.address,
|
|
7351
|
-
signPersonalMessage: local.signPersonalMessage,
|
|
7352
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7353
|
-
});
|
|
7354
|
-
if (check.kind !== "live") {
|
|
7355
|
-
return publicCatalog(stdioGatewayUrl(cfg), args.query, deps.fetchImpl);
|
|
7356
|
-
}
|
|
7357
|
-
}
|
|
7358
|
-
const resolved = await stdioPayerAndReader(cfg, deps);
|
|
7359
|
-
if (!resolved.ok) return resolved.result;
|
|
7360
|
-
const gatewayUrl = stdioGatewayUrl(cfg);
|
|
7361
|
-
const loaded = await fetchPublicCatalog(gatewayUrl, deps.fetchImpl);
|
|
7362
|
-
if (!loaded.ok) return loaded.result;
|
|
7363
|
-
const scoped = resolved.payer.grant.accessMode === "scoped";
|
|
7364
|
-
const reader = stdioReaderForPayer(cfg, deps, resolved.payer);
|
|
7365
|
-
const [policies, balance] = await Promise.all([
|
|
7366
|
-
scoped ? reader.policies() : Promise.resolve({ ok: true, data: { policies: void 0 } }),
|
|
7367
|
-
reader.balance()
|
|
7368
|
-
]);
|
|
7369
|
-
if (scoped && !policies.ok) {
|
|
7370
|
-
return closedError("POLICIES_UNAVAILABLE", "grant policies could not be loaded");
|
|
7371
|
-
}
|
|
7372
|
-
return text(
|
|
7373
|
-
joinDiscoverCatalog({
|
|
7374
|
-
catalog: loaded.catalog,
|
|
7375
|
-
gatewayUrl,
|
|
7376
|
-
query: args.query,
|
|
7377
|
-
...scoped && policies.ok ? { policies: policies.data.policies } : {},
|
|
7378
|
-
budget: budgetFromBalance(balance, resolved.payer.grant)
|
|
7379
|
-
})
|
|
7380
|
-
);
|
|
7381
|
-
}
|
|
7382
|
-
async function accessContext(deps = {}, cfg) {
|
|
7383
|
-
if (deps.auth) {
|
|
7384
|
-
if (!deps.auth.access) {
|
|
7385
|
-
return error(
|
|
7386
|
-
"SuiPay access context unavailable. Reconnect this MCP session or inspect the connection in buyer console."
|
|
7387
|
-
);
|
|
7388
|
-
}
|
|
7389
|
-
return text(deps.auth.access);
|
|
7390
|
-
}
|
|
7391
|
-
if (!cfg) return error(AUTH_REQUIRED);
|
|
7392
|
-
const resolved = await stdioPayerAndReader(cfg, deps);
|
|
7393
|
-
if (!resolved.ok) return resolved.result;
|
|
7394
|
-
const gatewayUrl = stdioGatewayUrl(cfg);
|
|
7395
|
-
const reader = stdioReaderForPayer(cfg, deps, resolved.payer);
|
|
7396
|
-
const scoped = resolved.payer.grant.accessMode === "scoped";
|
|
7397
|
-
const [policies, balance, loaded] = await Promise.all([
|
|
7398
|
-
reader.policies(),
|
|
7399
|
-
reader.balance(),
|
|
7400
|
-
fetchPublicCatalog(gatewayUrl, deps.fetchImpl)
|
|
7401
|
-
]);
|
|
7402
|
-
const budget = budgetFromBalance(balance, resolved.payer.grant);
|
|
7403
|
-
const joined = loaded.ok && (!scoped || policies.ok) ? joinDiscoverCatalog({
|
|
7404
|
-
catalog: loaded.catalog,
|
|
7405
|
-
gatewayUrl,
|
|
7406
|
-
...scoped && policies.ok ? { policies: policies.data.policies } : {},
|
|
7407
|
-
budget
|
|
7408
|
-
}) : null;
|
|
7409
|
-
return text({
|
|
7410
|
-
accessMode: resolved.payer.grant.accessMode,
|
|
7411
|
-
grant: {
|
|
7412
|
-
id: resolved.payer.grant.grantObjectId,
|
|
7413
|
-
poolObjectId: resolved.payer.grant.poolObjectId,
|
|
7414
|
-
coinType: resolved.payer.grant.coinType,
|
|
7415
|
-
maxPerPayment: resolved.payer.grant.maxPerPayment ?? null,
|
|
7416
|
-
sessionCap: resolved.payer.grant.sessionCap ?? null
|
|
7417
|
-
},
|
|
7418
|
-
policies: policies.ok ? policies.data : { error: policies.error },
|
|
7419
|
-
balance: balance.ok ? balance.data : { error: balance.error },
|
|
7420
|
-
...joined ? { services: joined.services, count: joined.count } : {}
|
|
7421
|
-
});
|
|
6004
|
+
return text(deps.auth.access);
|
|
7422
6005
|
}
|
|
7423
|
-
async function receipts(args,
|
|
6006
|
+
async function receipts(args, _cfg, deps = {}) {
|
|
7424
6007
|
if (deps.auth) {
|
|
7425
6008
|
if (!deps.auth.listReceipts) {
|
|
7426
6009
|
return error(
|
|
@@ -7434,144 +6017,28 @@ async function receipts(args, cfg, deps = {}) {
|
|
|
7434
6017
|
return closedError("RECEIPTS_UNAVAILABLE", "receipts could not be loaded");
|
|
7435
6018
|
}
|
|
7436
6019
|
}
|
|
7437
|
-
if (profile(deps))
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
}
|
|
7442
|
-
const resolved = await stdioPayerAndReader(cfg, deps);
|
|
7443
|
-
if (!resolved.ok) return resolved.result;
|
|
7444
|
-
const reader = (deps.createReader ?? createReader)({
|
|
7445
|
-
gatewayUrl: stdioGatewayUrl(cfg),
|
|
7446
|
-
context: {
|
|
7447
|
-
network: resolved.payer.deployment.network,
|
|
7448
|
-
packageId: resolved.payer.deployment.packageId,
|
|
7449
|
-
grantObjectId: resolved.payer.grant.grantObjectId,
|
|
7450
|
-
delegateAddress: resolved.payer.address
|
|
7451
|
-
},
|
|
7452
|
-
signPersonalMessage: async (message) => {
|
|
7453
|
-
const { signature } = await loadLocalKeypair().signPersonalMessage(message);
|
|
7454
|
-
return signature;
|
|
7455
|
-
},
|
|
7456
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7457
|
-
});
|
|
7458
|
-
const listed = await reader.receipts();
|
|
7459
|
-
if (!listed.ok) {
|
|
7460
|
-
return closedError("RECEIPTS_UNAVAILABLE", "receipts could not be loaded");
|
|
7461
|
-
}
|
|
7462
|
-
const receiptsOut = listed.data.receipts;
|
|
7463
|
-
const filtered = args.challengeId && Array.isArray(receiptsOut) ? receiptsOut.filter((row) => {
|
|
7464
|
-
if (!row || typeof row !== "object") return false;
|
|
7465
|
-
const rec = row;
|
|
7466
|
-
return rec.challengeId === args.challengeId || rec.receipt?.challengeId === args.challengeId;
|
|
7467
|
-
}) : receiptsOut;
|
|
7468
|
-
return text({ count: Array.isArray(filtered) ? filtered.length : 0, receipts: filtered });
|
|
7469
|
-
}
|
|
7470
|
-
function parseLoginTarget(value) {
|
|
7471
|
-
if (value === void 0 || value === null || value === "") return void 0;
|
|
7472
|
-
if (typeof value !== "string") return { error: 'target must be "dev" or "local"' };
|
|
7473
|
-
const target = value.trim().toLowerCase();
|
|
7474
|
-
if (target === "dev" || target === "local") return target;
|
|
7475
|
-
return { error: 'target must be "dev" or "local"' };
|
|
6020
|
+
if (!profile(deps)) return error(AUTH_REQUIRED);
|
|
6021
|
+
return error(
|
|
6022
|
+
"receipts is available on remote OAuth (/mcp) sessions only; the local allowance profile has no settlement feed access"
|
|
6023
|
+
);
|
|
7476
6024
|
}
|
|
7477
6025
|
async function suipayLogin(args, cfg, deps = {}) {
|
|
7478
|
-
const parsedTarget = parseLoginTarget(args.target);
|
|
7479
|
-
if (parsedTarget && typeof parsedTarget === "object" && "error" in parsedTarget) {
|
|
7480
|
-
return error({
|
|
7481
|
-
status: "failed",
|
|
7482
|
-
code: "INVALID_TARGET",
|
|
7483
|
-
detail: parsedTarget.error
|
|
7484
|
-
});
|
|
7485
|
-
}
|
|
7486
|
-
const target = parsedTarget;
|
|
7487
6026
|
const label = args.label?.trim() || cfg.label.trim();
|
|
7488
6027
|
const identity = await ensureLocalSigningKey(label ? { label } : {});
|
|
7489
|
-
const
|
|
7490
|
-
|
|
7491
|
-
const
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
const live = await checkLiveDelegateGrant({
|
|
7497
|
-
gatewayUrl: endpoints.gatewayUrl,
|
|
7498
|
-
delegateAddress: local.address,
|
|
7499
|
-
signPersonalMessage: local.signPersonalMessage,
|
|
7500
|
-
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
|
|
7501
|
-
});
|
|
7502
|
-
if (live.kind === "ambiguous") {
|
|
7503
|
-
return error({
|
|
7504
|
-
status: "failed",
|
|
7505
|
-
code: "AMBIGUOUS_GRANT",
|
|
7506
|
-
detail: "multiple live grants name this key; revoke extras or pass a selector before paying"
|
|
7507
|
-
});
|
|
7508
|
-
}
|
|
7509
|
-
if (live.kind === "live") {
|
|
7510
|
-
const storedUrl = stored?.gatewayUrl;
|
|
7511
|
-
if (!storedUrl || storedUrl !== endpoints.gatewayUrl && (sameMcpGateway(storedUrl, endpoints.gatewayUrl) || target !== void 0)) {
|
|
7512
|
-
persistGatewayUrl(endpoints.gatewayUrl);
|
|
7513
|
-
}
|
|
7514
|
-
return text({
|
|
7515
|
-
status: "ok",
|
|
7516
|
-
reused: true,
|
|
7517
|
-
target: target ?? cfg.target,
|
|
7518
|
-
delegateAddress: identity.delegateAddress,
|
|
7519
|
-
gatewayUrl: loadLocalSigningKeyRecord()?.gatewayUrl ?? endpoints.gatewayUrl,
|
|
7520
|
-
grantObjectId: live.grantObjectId,
|
|
7521
|
-
message: "Existing spend-account grant is live for this key. No browser round-trip."
|
|
7522
|
-
});
|
|
7523
|
-
}
|
|
7524
|
-
if (live.kind === "unknown") {
|
|
7525
|
-
return error({
|
|
7526
|
-
status: "failed",
|
|
7527
|
-
code: "GRANT_STATUS_UNKNOWN",
|
|
7528
|
-
detail: "Could not verify the existing grant. Credentials were not modified. Retry later; do not start a new consent (that would mint a second grant)."
|
|
7529
|
-
});
|
|
7530
|
-
}
|
|
6028
|
+
const connect = new URL("/connect/agent", cfg.consoleUrl);
|
|
6029
|
+
connect.searchParams.set("delegateAddress", identity.delegateAddress);
|
|
6030
|
+
const url = connect.toString();
|
|
6031
|
+
deps.onUrl?.(url);
|
|
6032
|
+
try {
|
|
6033
|
+
await open(url);
|
|
6034
|
+
} catch {
|
|
7531
6035
|
}
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
timeoutMs: LOGIN_BG_TIMEOUT_MS,
|
|
7538
|
-
openBrowser: false,
|
|
7539
|
-
onUrl: deps.onUrl
|
|
6036
|
+
return text({
|
|
6037
|
+
status: "ok",
|
|
6038
|
+
delegateAddress: identity.delegateAddress,
|
|
6039
|
+
connectUrl: url,
|
|
6040
|
+
message: "Open the console to bind a spend-account grant to this delegate address. The private key never leaves this process."
|
|
7540
6041
|
});
|
|
7541
|
-
const url = started.connectUrl;
|
|
7542
|
-
return text(
|
|
7543
|
-
[
|
|
7544
|
-
`## ACTION REQUIRED: User must click this URL to sign in`,
|
|
7545
|
-
``,
|
|
7546
|
-
`**Target:** ${target ?? cfg.target ?? "custom"} \xB7 ${endpoints.gatewayUrl}`,
|
|
7547
|
-
``,
|
|
7548
|
-
`**URL:** ${url}`,
|
|
7549
|
-
``,
|
|
7550
|
-
"```",
|
|
7551
|
-
url,
|
|
7552
|
-
"```",
|
|
7553
|
-
``,
|
|
7554
|
-
`[Click here to open SuiPay sign-in](${url})`,
|
|
7555
|
-
``,
|
|
7556
|
-
`**IMPORTANT for the assistant**: do NOT summarize or omit the URL above.`,
|
|
7557
|
-
`The user CANNOT proceed without seeing the exact URL. Surface it verbatim.`,
|
|
7558
|
-
``,
|
|
7559
|
-
`1. Open the URL in any browser`,
|
|
7560
|
-
`2. Connect the owner wallet and approve the spend-account grant (Access keys / policies)`,
|
|
7561
|
-
`3. Once Connected appears, retry the original request \u2014 credentials at \`~/.suipay/credentials.json\` are picked up on the next tool call`,
|
|
7562
|
-
``,
|
|
7563
|
-
`_The login link stays valid for 5 minutes. If it expires, call \`suipay_login\` again._`,
|
|
7564
|
-
``,
|
|
7565
|
-
JSON.stringify({
|
|
7566
|
-
status: "ok",
|
|
7567
|
-
target: target ?? cfg.target,
|
|
7568
|
-
delegateAddress: identity.delegateAddress,
|
|
7569
|
-
connectUrl: url,
|
|
7570
|
-
consoleUrl: endpoints.consoleUrl,
|
|
7571
|
-
gatewayUrl: endpoints.gatewayUrl
|
|
7572
|
-
})
|
|
7573
|
-
].join("\n")
|
|
7574
|
-
);
|
|
7575
6042
|
}
|
|
7576
6043
|
async function logout(args, _cfg, deps = {}) {
|
|
7577
6044
|
if (args.force) {
|
|
@@ -7597,7 +6064,7 @@ var TOOLS = [
|
|
|
7597
6064
|
},
|
|
7598
6065
|
{
|
|
7599
6066
|
name: "pay",
|
|
7600
|
-
description: "Pay a HTTP resource via delegate. On hosted /mcp the gateway PREPARES an unsigned transaction and returns it (prepare-only \u2014 it never holds the delegate key and cannot settle). Only a process holding the delegate key the grant names can complete settlement (e.g. local stdio MCP, or any self-hosted process with the matching key). Pass recipient and/or maxAmount to state who you intend to pay and what you will spend; both are checked before anything is signed or handed back. Local V1 allowance pay is retired.
|
|
6067
|
+
description: "Pay a HTTP resource via delegate. On hosted /mcp the gateway PREPARES an unsigned transaction and returns it (prepare-only \u2014 it never holds the delegate key and cannot settle). Only a process holding the delegate key the grant names can complete settlement (e.g. local stdio MCP, or any self-hosted process with the matching key). Pass recipient and/or maxAmount to state who you intend to pay and what you will spend; both are checked before anything is signed or handed back. Local V1 allowance pay is retired.",
|
|
7601
6068
|
inputSchema: {
|
|
7602
6069
|
type: "object",
|
|
7603
6070
|
properties: {
|
|
@@ -7625,7 +6092,7 @@ var TOOLS = [
|
|
|
7625
6092
|
},
|
|
7626
6093
|
{
|
|
7627
6094
|
name: "discover",
|
|
7628
|
-
description: "Search SuiPay gateway resources.
|
|
6095
|
+
description: "Search SuiPay gateway resources.",
|
|
7629
6096
|
inputSchema: {
|
|
7630
6097
|
type: "object",
|
|
7631
6098
|
properties: {
|
|
@@ -7651,16 +6118,11 @@ var TOOLS = [
|
|
|
7651
6118
|
},
|
|
7652
6119
|
{
|
|
7653
6120
|
name: "suipay_login",
|
|
7654
|
-
description:
|
|
6121
|
+
description: "Mint or reuse the local delegate key (version-2, mode 0600) and open the console with the public address only so the owner can bind a spend-account grant. Stdio only; hosted /mcp never holds a spendable key.",
|
|
7655
6122
|
inputSchema: {
|
|
7656
6123
|
type: "object",
|
|
7657
6124
|
properties: {
|
|
7658
6125
|
label: { type: "string", description: "Label shown in SuiPay console." },
|
|
7659
|
-
target: {
|
|
7660
|
-
type: "string",
|
|
7661
|
-
enum: ["dev", "local"],
|
|
7662
|
-
description: 'Where to bind the grant. Default is Railway suipay-dev. Pass "local" for localhost:3000 / 127.0.0.1:4340.'
|
|
7663
|
-
},
|
|
7664
6126
|
gatewayResourceUrl: {
|
|
7665
6127
|
type: "string",
|
|
7666
6128
|
description: "Gateway resource URL whose 402 defines recipient, asset, and package."
|
|
@@ -7716,7 +6178,7 @@ function toolError(value) {
|
|
|
7716
6178
|
isError: true
|
|
7717
6179
|
};
|
|
7718
6180
|
}
|
|
7719
|
-
function
|
|
6181
|
+
function createServer(cfg, auth, trace, options = {}) {
|
|
7720
6182
|
const resolveAgentHeld = options.resolveAgentHeld ?? resolveAgentHeldSettlement;
|
|
7721
6183
|
const server = new Server(
|
|
7722
6184
|
{ name: "suipay", version: "1.0.0" },
|
|
@@ -7741,7 +6203,7 @@ function createServer2(cfg, auth, trace, options = {}) {
|
|
|
7741
6203
|
}
|
|
7742
6204
|
switch (name) {
|
|
7743
6205
|
case "access_context":
|
|
7744
|
-
return await accessContext({ auth }
|
|
6206
|
+
return await accessContext({ auth });
|
|
7745
6207
|
case "pay": {
|
|
7746
6208
|
const agentHeld = auth?.spendAccountPay ? resolveAgentHeld(cfg, auth.spendAccountPay) : null;
|
|
7747
6209
|
return await pay(
|
|
@@ -7785,7 +6247,6 @@ function createServer2(cfg, auth, trace, options = {}) {
|
|
|
7785
6247
|
return await suipayLogin(
|
|
7786
6248
|
{
|
|
7787
6249
|
label: typeof args.label === "string" ? args.label : void 0,
|
|
7788
|
-
target: typeof args.target === "string" ? args.target : void 0,
|
|
7789
6250
|
gatewayResourceUrl: typeof args.gatewayResourceUrl === "string" ? args.gatewayResourceUrl : void 0
|
|
7790
6251
|
},
|
|
7791
6252
|
cfg,
|
|
@@ -8284,7 +6745,7 @@ async function handleSuipayMcpHttpRequest(req, cfg, auth, trace, options) {
|
|
|
8284
6745
|
sessionIdGenerator: void 0,
|
|
8285
6746
|
enableJsonResponse: true
|
|
8286
6747
|
});
|
|
8287
|
-
const server =
|
|
6748
|
+
const server = createServer(cfg, auth, hooks, options);
|
|
8288
6749
|
await server.connect(transport);
|
|
8289
6750
|
return transport.handleRequest(request);
|
|
8290
6751
|
}
|
|
@@ -8293,15 +6754,10 @@ async function handleSuipayMcpHttpRequest(req, cfg, auth, trace, options) {
|
|
|
8293
6754
|
|
|
8294
6755
|
export {
|
|
8295
6756
|
resolveGrantTarget,
|
|
8296
|
-
MCP_TARGET_PRESETS,
|
|
8297
|
-
parseMcpTargetFlag,
|
|
8298
|
-
resolveMcpTarget,
|
|
8299
|
-
sameMcpGateway,
|
|
8300
6757
|
loadMcpConfig,
|
|
8301
6758
|
loadPaymentProfile,
|
|
8302
6759
|
ensureLocalSigningKey,
|
|
8303
6760
|
loadLocalSigner,
|
|
8304
|
-
loadLocalSigningKeyRecord,
|
|
8305
6761
|
resetAgentHeldProcessLedger,
|
|
8306
6762
|
resolveAgentHeldSettlement,
|
|
8307
6763
|
settlePreparedMcpPayment,
|
|
@@ -8312,7 +6768,7 @@ export {
|
|
|
8312
6768
|
receipts,
|
|
8313
6769
|
PACKAGE_NAME,
|
|
8314
6770
|
TOOLS,
|
|
8315
|
-
|
|
6771
|
+
createServer,
|
|
8316
6772
|
canonicalMcpRequestId,
|
|
8317
6773
|
inspectMcpEnvelope,
|
|
8318
6774
|
traceMcpHttpRequest,
|