@agent-score/commerce 1.8.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -9
- package/dist/{_response-9yp6Fit2.d.mts → _response-BFYN3b6i.d.mts} +17 -19
- package/dist/{_response-CC6jNb8q.d.ts → _response-_iPD5AIj.d.ts} +17 -19
- package/dist/challenge/index.d.mts +106 -198
- package/dist/challenge/index.d.ts +106 -198
- package/dist/challenge/index.js +238 -111
- package/dist/challenge/index.js.map +1 -1
- package/dist/challenge/index.mjs +238 -111
- package/dist/challenge/index.mjs.map +1 -1
- package/dist/checkout-BoFwnVsj.d.ts +931 -0
- package/dist/checkout-DRbQ0Fsh.d.mts +931 -0
- package/dist/core.d.mts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/core.mjs.map +1 -1
- package/dist/discovery/index.d.mts +453 -51
- package/dist/discovery/index.d.ts +453 -51
- package/dist/discovery/index.js +1092 -58
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/index.mjs +1060 -57
- package/dist/discovery/index.mjs.map +1 -1
- package/dist/identity/express.d.mts +3 -3
- package/dist/identity/express.d.ts +3 -3
- package/dist/identity/express.js +30 -19
- package/dist/identity/express.js.map +1 -1
- package/dist/identity/express.mjs +30 -19
- package/dist/identity/express.mjs.map +1 -1
- package/dist/identity/fastify.d.mts +4 -4
- package/dist/identity/fastify.d.ts +4 -4
- package/dist/identity/fastify.js +30 -19
- package/dist/identity/fastify.js.map +1 -1
- package/dist/identity/fastify.mjs +30 -19
- package/dist/identity/fastify.mjs.map +1 -1
- package/dist/identity/hono.d.mts +3 -3
- package/dist/identity/hono.d.ts +3 -3
- package/dist/identity/hono.js +30 -19
- package/dist/identity/hono.js.map +1 -1
- package/dist/identity/hono.mjs +30 -19
- package/dist/identity/hono.mjs.map +1 -1
- package/dist/identity/nextjs.d.mts +6 -7
- package/dist/identity/nextjs.d.ts +6 -7
- package/dist/identity/nextjs.js +30 -19
- package/dist/identity/nextjs.js.map +1 -1
- package/dist/identity/nextjs.mjs +30 -19
- package/dist/identity/nextjs.mjs.map +1 -1
- package/dist/identity/policy.d.mts +41 -4
- package/dist/identity/policy.d.ts +41 -4
- package/dist/identity/policy.js +3662 -18
- package/dist/identity/policy.js.map +1 -1
- package/dist/identity/policy.mjs +3648 -3
- package/dist/identity/policy.mjs.map +1 -1
- package/dist/identity/web.d.mts +3 -3
- package/dist/identity/web.d.ts +3 -3
- package/dist/identity/web.js +30 -19
- package/dist/identity/web.js.map +1 -1
- package/dist/identity/web.mjs +30 -19
- package/dist/identity/web.mjs.map +1 -1
- package/dist/index.d.mts +72 -329
- package/dist/index.d.ts +72 -329
- package/dist/index.js +3651 -373
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3628 -361
- package/dist/index.mjs.map +1 -1
- package/dist/payment/index.d.mts +256 -265
- package/dist/payment/index.d.ts +256 -265
- package/dist/payment/index.js +586 -149
- package/dist/payment/index.js.map +1 -1
- package/dist/payment/index.mjs +573 -148
- package/dist/payment/index.mjs.map +1 -1
- package/dist/{agent_instructions-DiMSGkdm.d.mts → pricing-CQ9DIFaw.d.ts} +109 -56
- package/dist/{agent_instructions-DiMSGkdm.d.ts → pricing-CxzwyiO6.d.mts} +109 -56
- package/dist/rail_spec-XP0wKgJV.d.mts +132 -0
- package/dist/rail_spec-XP0wKgJV.d.ts +132 -0
- package/dist/{signer-CFVQsWjL.d.mts → signer-3FAit11j.d.mts} +27 -1
- package/dist/{signer-CFVQsWjL.d.ts → signer-3FAit11j.d.ts} +27 -1
- package/dist/solana-Cds87OTu.d.mts +67 -0
- package/dist/solana-Cds87OTu.d.ts +67 -0
- package/dist/stripe-multichain/index.d.mts +55 -66
- package/dist/stripe-multichain/index.d.ts +55 -66
- package/dist/stripe-multichain/index.js +68 -42
- package/dist/stripe-multichain/index.js.map +1 -1
- package/dist/stripe-multichain/index.mjs +68 -41
- package/dist/stripe-multichain/index.mjs.map +1 -1
- package/dist/{wwwauthenticate-CU1eNvMQ.d.mts → wwwauthenticate-D_FMnPgU.d.mts} +9 -10
- package/dist/{wwwauthenticate-CU1eNvMQ.d.ts → wwwauthenticate-D_FMnPgU.d.ts} +9 -10
- package/dist/x402_server-hgQzWQwB.d.mts +81 -0
- package/dist/x402_server-hgQzWQwB.d.ts +81 -0
- package/package.json +9 -7
package/dist/discovery/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,24 +17,42 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/discovery/index.ts
|
|
21
31
|
var discovery_exports = {};
|
|
22
32
|
__export(discovery_exports, {
|
|
33
|
+
PURCHASE_MODE_NOTES: () => PURCHASE_MODE_NOTES,
|
|
23
34
|
agentscoreDenialSchemas: () => agentscoreDenialSchemas,
|
|
24
35
|
agentscoreOpenApiSnippets: () => agentscoreOpenApiSnippets,
|
|
25
36
|
agentscorePaymentRequiredSchema: () => agentscorePaymentRequiredSchema,
|
|
26
37
|
agentscoreSecuritySchemes: () => agentscoreSecuritySchemes,
|
|
27
38
|
applyNoindexHeader: () => applyNoindexHeader,
|
|
39
|
+
bootstrapUcpSigningKey: () => bootstrapUcpSigningKey,
|
|
40
|
+
buildAgentscoreOnboardingSteps: () => buildAgentscoreOnboardingSteps,
|
|
28
41
|
buildDiscoveryProbeResponse: () => buildDiscoveryProbeResponse,
|
|
29
42
|
buildLlmsTxt: () => buildLlmsTxt,
|
|
43
|
+
buildMerchantIndexJson: () => buildMerchantIndexJson,
|
|
44
|
+
buildRedemptionSkillMd: () => buildRedemptionSkillMd,
|
|
45
|
+
buildSignedJwksResponse: () => buildSignedJwksResponse,
|
|
46
|
+
buildSignedUcpResponse: () => buildSignedUcpResponse,
|
|
30
47
|
buildSkillMd: () => buildSkillMd,
|
|
48
|
+
buildSuccessNextSteps: () => buildSuccessNextSteps,
|
|
31
49
|
buildWellKnownMpp: () => buildWellKnownMpp,
|
|
32
50
|
buildWellKnownX402: () => buildWellKnownX402,
|
|
33
51
|
compatibleClientsByRails: () => compatibleClientsByRails,
|
|
34
52
|
createBazaarDiscovery: () => createBazaarDiscovery,
|
|
53
|
+
defaultA2aServices: () => defaultA2aServices,
|
|
35
54
|
defaultDiscoveryPaths: () => defaultDiscoveryPaths,
|
|
55
|
+
echoRequestIdHeaderHono: () => echoRequestIdHeaderHono,
|
|
36
56
|
isDiscoveryPath: () => isDiscoveryPath,
|
|
37
57
|
isDiscoveryProbeRequest: () => isDiscoveryProbeRequest,
|
|
38
58
|
llmsTxtIdentitySection: () => llmsTxtIdentitySection,
|
|
@@ -40,11 +60,22 @@ __export(discovery_exports, {
|
|
|
40
60
|
noindexNonDiscoveryPaths: () => noindexNonDiscoveryPaths,
|
|
41
61
|
noindexNonDiscoveryPathsExpress: () => noindexNonDiscoveryPathsExpress,
|
|
42
62
|
noindexNonDiscoveryPathsFastify: () => noindexNonDiscoveryPathsFastify,
|
|
63
|
+
purchaseModeNote: () => purchaseModeNote,
|
|
43
64
|
sampleX402AcceptForNetwork: () => sampleX402AcceptForNetwork,
|
|
65
|
+
signedResponseExpress: () => signedResponseExpress,
|
|
66
|
+
signedResponseFastify: () => signedResponseFastify,
|
|
67
|
+
signedResponseHono: () => signedResponseHono,
|
|
68
|
+
signedResponseNextjs: () => signedResponseNextjs,
|
|
69
|
+
signedResponseWeb: () => signedResponseWeb,
|
|
44
70
|
siwxSecurityScheme: () => siwxSecurityScheme,
|
|
71
|
+
standardEndpointDescriptions: () => standardEndpointDescriptions,
|
|
72
|
+
wellKnownCorsPreflightHeaders: () => wellKnownCorsPreflightHeaders,
|
|
73
|
+
wellKnownPreflightResponse: () => wellKnownPreflightResponse,
|
|
45
74
|
wrapNoindexResponse: () => wrapNoindexResponse,
|
|
46
75
|
xGuidanceExtension: () => xGuidanceExtension,
|
|
47
|
-
xPaymentInfoExtension: () => xPaymentInfoExtension
|
|
76
|
+
xPaymentInfoExtension: () => xPaymentInfoExtension,
|
|
77
|
+
xPaymentInfoFromCheckout: () => xPaymentInfoFromCheckout,
|
|
78
|
+
xServiceInfoExtension: () => xServiceInfoExtension
|
|
48
79
|
});
|
|
49
80
|
module.exports = __toCommonJS(discovery_exports);
|
|
50
81
|
|
|
@@ -158,32 +189,52 @@ function lookupRail(name) {
|
|
|
158
189
|
}
|
|
159
190
|
|
|
160
191
|
// src/payment/directive.ts
|
|
161
|
-
function buildPaymentRequestBlob(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
function buildPaymentRequestBlob({
|
|
193
|
+
rail,
|
|
194
|
+
amountUsd,
|
|
195
|
+
currency,
|
|
196
|
+
decimals,
|
|
197
|
+
recipient,
|
|
198
|
+
chainId,
|
|
199
|
+
networkId
|
|
200
|
+
}) {
|
|
201
|
+
const railDef = rail ? lookupRail(rail) : void 0;
|
|
202
|
+
const decimalsResolved = decimals ?? railDef?.decimals ?? 6;
|
|
203
|
+
const currencyResolved = currency ?? railDef?.currency ?? "usd";
|
|
204
|
+
const chainIdResolved = chainId ?? railDef?.chainId;
|
|
205
|
+
const amountNum = typeof amountUsd === "string" ? Number(amountUsd) : amountUsd;
|
|
206
|
+
const amountRaw = BigInt(Math.round(amountNum * 10 ** decimalsResolved)).toString();
|
|
207
|
+
const blob = { amount: amountRaw, currency: currencyResolved, decimals: decimalsResolved };
|
|
208
|
+
if (recipient) blob.recipient = recipient;
|
|
170
209
|
const methodDetails = {};
|
|
171
|
-
if (
|
|
172
|
-
if (
|
|
210
|
+
if (chainIdResolved !== void 0) methodDetails.chainId = chainIdResolved;
|
|
211
|
+
if (networkId) methodDetails.networkId = networkId;
|
|
173
212
|
if (Object.keys(methodDetails).length > 0) blob.methodDetails = methodDetails;
|
|
174
213
|
return Buffer.from(JSON.stringify(blob)).toString("base64url");
|
|
175
214
|
}
|
|
176
|
-
function paymentDirective(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
215
|
+
function paymentDirective({
|
|
216
|
+
rail,
|
|
217
|
+
id,
|
|
218
|
+
realm,
|
|
219
|
+
method,
|
|
220
|
+
intent,
|
|
221
|
+
expires,
|
|
222
|
+
request
|
|
223
|
+
}) {
|
|
224
|
+
const railDef = rail ? lookupRail(rail) : void 0;
|
|
225
|
+
const methodResolved = method ?? railDef?.method ?? "unknown";
|
|
226
|
+
const intentResolved = intent ?? "charge";
|
|
227
|
+
const expiresResolved = expires ?? new Date(Date.now() + 5 * 60 * 1e3).toISOString();
|
|
228
|
+
return `Payment id="${id}", realm="${realm}", method="${methodResolved}", intent="${intentResolved}", expires="${expiresResolved}", request="${request}"`;
|
|
182
229
|
}
|
|
183
230
|
|
|
184
231
|
// src/payment/wwwauthenticate.ts
|
|
185
|
-
function paymentRequiredHeader(
|
|
186
|
-
|
|
232
|
+
function paymentRequiredHeader({
|
|
233
|
+
x402Version,
|
|
234
|
+
accepts,
|
|
235
|
+
resource
|
|
236
|
+
}) {
|
|
237
|
+
return Buffer.from(JSON.stringify({ x402Version, accepts, ...resource ? { resource } : {} })).toString("base64");
|
|
187
238
|
}
|
|
188
239
|
|
|
189
240
|
// src/discovery/probe.ts
|
|
@@ -311,26 +362,36 @@ async function dynamicImport(moduleName) {
|
|
|
311
362
|
}
|
|
312
363
|
|
|
313
364
|
// src/discovery/well_known_mpp.ts
|
|
314
|
-
function buildWellKnownMpp(
|
|
365
|
+
function buildWellKnownMpp({
|
|
366
|
+
name,
|
|
367
|
+
description,
|
|
368
|
+
url,
|
|
369
|
+
openapi,
|
|
370
|
+
endpoints,
|
|
371
|
+
catalog,
|
|
372
|
+
purchase,
|
|
373
|
+
shipping,
|
|
374
|
+
extra
|
|
375
|
+
}) {
|
|
315
376
|
return {
|
|
316
|
-
name
|
|
317
|
-
...
|
|
318
|
-
url
|
|
319
|
-
...
|
|
320
|
-
endpoints
|
|
321
|
-
...
|
|
377
|
+
name,
|
|
378
|
+
...description ? { description } : {},
|
|
379
|
+
url,
|
|
380
|
+
...openapi ? { openapi } : {},
|
|
381
|
+
endpoints,
|
|
382
|
+
...catalog ? { catalog } : {},
|
|
322
383
|
purchase: {
|
|
323
|
-
...
|
|
324
|
-
...
|
|
325
|
-
...
|
|
326
|
-
...
|
|
327
|
-
...
|
|
328
|
-
payment_methods:
|
|
329
|
-
...
|
|
330
|
-
...
|
|
384
|
+
...purchase.required_fields ? { required_fields: purchase.required_fields } : {},
|
|
385
|
+
...purchase.optional_fields ? { optional_fields: purchase.optional_fields } : {},
|
|
386
|
+
...purchase.extra ?? {},
|
|
387
|
+
...purchase.identity ? { identity: purchase.identity } : {},
|
|
388
|
+
...purchase.identity_paths ? { identity_paths: purchase.identity_paths } : {},
|
|
389
|
+
payment_methods: purchase.methods,
|
|
390
|
+
...purchase.x402 ? { x402: purchase.x402 } : {},
|
|
391
|
+
...purchase.compliance ? { compliance: purchase.compliance } : {}
|
|
331
392
|
},
|
|
332
|
-
...
|
|
333
|
-
...
|
|
393
|
+
...shipping ? { shipping } : {},
|
|
394
|
+
...extra ?? {}
|
|
334
395
|
};
|
|
335
396
|
}
|
|
336
397
|
|
|
@@ -343,11 +404,13 @@ function buildWellKnownX402(input) {
|
|
|
343
404
|
}
|
|
344
405
|
|
|
345
406
|
// src/discovery/llms_txt.ts
|
|
346
|
-
function llmsTxtIdentitySection(
|
|
347
|
-
|
|
407
|
+
function llmsTxtIdentitySection({
|
|
408
|
+
agentscore,
|
|
409
|
+
compliance
|
|
410
|
+
} = {}) {
|
|
411
|
+
if (!agentscore) {
|
|
348
412
|
return "";
|
|
349
413
|
}
|
|
350
|
-
const compliance = input.compliance;
|
|
351
414
|
const complianceNote = compliance ? `
|
|
352
415
|
|
|
353
416
|
Compliance: ${[
|
|
@@ -452,31 +515,37 @@ function llmsTxtPaymentSectionVerbose(input) {
|
|
|
452
515
|
lines.push("Mint a SharedPaymentToken scoped to the `profile_id` from the 402 body, then submit via `Authorization: Payment` with `method=stripe/charge`. Either your own Stripe account or `link-cli spend-request create --credential-type shared_payment_token --network-id <profileId> ...` for Stripe Link wallets.");
|
|
453
516
|
lines.push("");
|
|
454
517
|
}
|
|
455
|
-
lines.push("IMPORTANT: Use the CLIs above. Raw on-chain transfers (e.g. `tempo wallet transfer`, sending USDC manually to deposit addresses) bypass the protocol handshake and the
|
|
518
|
+
lines.push("IMPORTANT: Use the CLIs above. Raw on-chain transfers (e.g. `tempo wallet transfer`, sending USDC manually to deposit addresses) bypass the protocol handshake and the request will not complete.");
|
|
456
519
|
if (hasBase || hasSolana) {
|
|
457
520
|
lines.push("IMPORTANT: Pay the exact amount in the 402 challenge. Overpayments and underpayments cannot be matched.");
|
|
458
521
|
}
|
|
459
522
|
lines.push("");
|
|
460
523
|
return lines.join("\n");
|
|
461
524
|
}
|
|
462
|
-
function buildLlmsTxt(
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
525
|
+
function buildLlmsTxt({
|
|
526
|
+
merchantName,
|
|
527
|
+
tagline,
|
|
528
|
+
sections,
|
|
529
|
+
agentscoreIdentity,
|
|
530
|
+
payment
|
|
531
|
+
}) {
|
|
532
|
+
const parts = [`# ${merchantName}`];
|
|
533
|
+
if (tagline) {
|
|
534
|
+
parts.push(`> ${tagline}`);
|
|
466
535
|
}
|
|
467
536
|
parts.push("");
|
|
468
|
-
for (const s of
|
|
537
|
+
for (const s of sections) {
|
|
469
538
|
parts.push(`## ${s.heading}`);
|
|
470
539
|
parts.push("");
|
|
471
540
|
parts.push(s.content);
|
|
472
541
|
parts.push("");
|
|
473
542
|
}
|
|
474
|
-
if (
|
|
475
|
-
parts.push(llmsTxtIdentitySection(
|
|
543
|
+
if (agentscoreIdentity) {
|
|
544
|
+
parts.push(llmsTxtIdentitySection(agentscoreIdentity));
|
|
476
545
|
parts.push("");
|
|
477
546
|
}
|
|
478
|
-
if (
|
|
479
|
-
parts.push(llmsTxtPaymentSection(
|
|
547
|
+
if (payment) {
|
|
548
|
+
parts.push(llmsTxtPaymentSection(payment));
|
|
480
549
|
}
|
|
481
550
|
return parts.join("\n");
|
|
482
551
|
}
|
|
@@ -577,21 +646,88 @@ function agentscorePaymentRequiredSchema() {
|
|
|
577
646
|
}
|
|
578
647
|
};
|
|
579
648
|
}
|
|
580
|
-
function xPaymentInfoExtension(
|
|
581
|
-
|
|
649
|
+
function xPaymentInfoExtension({
|
|
650
|
+
price,
|
|
651
|
+
protocols,
|
|
652
|
+
description
|
|
653
|
+
}) {
|
|
654
|
+
return {
|
|
655
|
+
"x-payment-info": {
|
|
656
|
+
authMode: "payment",
|
|
657
|
+
price,
|
|
658
|
+
protocols,
|
|
659
|
+
...description !== void 0 && { description }
|
|
660
|
+
}
|
|
661
|
+
};
|
|
582
662
|
}
|
|
583
663
|
function xGuidanceExtension(text) {
|
|
584
664
|
return { "x-guidance": text };
|
|
585
665
|
}
|
|
586
|
-
function
|
|
666
|
+
function xServiceInfoExtension(opts) {
|
|
667
|
+
return {
|
|
668
|
+
"x-service-info": {
|
|
669
|
+
categories: opts.categories,
|
|
670
|
+
...opts.docs !== void 0 && { docs: opts.docs }
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function xPaymentInfoFromCheckout(opts) {
|
|
675
|
+
const protocols = [];
|
|
676
|
+
const extras = opts.protocolExtras ?? {};
|
|
677
|
+
for (const spec of Object.values(opts.checkout.rails)) {
|
|
678
|
+
const isStripe2 = !("recipient" in spec);
|
|
679
|
+
const network = typeof spec.network === "string" ? spec.network : "";
|
|
680
|
+
const tokenCurrency = (typeof spec.currency === "string" ? spec.currency : "") || (typeof spec.token === "string" ? spec.token : "");
|
|
681
|
+
if (isStripe2) {
|
|
682
|
+
protocols.push({ mpp: { method: "stripe", intent: "charge", currency: "usd", ...extras.stripe ?? {} } });
|
|
683
|
+
} else if (network.startsWith("eip155:")) {
|
|
684
|
+
protocols.push({
|
|
685
|
+
x402: {
|
|
686
|
+
scheme: "exact",
|
|
687
|
+
network: "base",
|
|
688
|
+
asset: "USDC",
|
|
689
|
+
...extras.base ?? {}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
} else if (network.startsWith("solana:")) {
|
|
693
|
+
protocols.push({
|
|
694
|
+
mpp: {
|
|
695
|
+
method: "solana",
|
|
696
|
+
intent: "charge",
|
|
697
|
+
...tokenCurrency ? { currency: tokenCurrency } : {},
|
|
698
|
+
...extras.solana ?? {}
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
} else {
|
|
702
|
+
protocols.push({
|
|
703
|
+
mpp: {
|
|
704
|
+
method: "tempo",
|
|
705
|
+
intent: "charge",
|
|
706
|
+
...tokenCurrency ? { currency: tokenCurrency } : {},
|
|
707
|
+
...extras.tempo ?? {}
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return xPaymentInfoExtension({
|
|
713
|
+
price: opts.price,
|
|
714
|
+
protocols,
|
|
715
|
+
...opts.description !== void 0 && { description: opts.description }
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
function agentscoreOpenApiSnippets({
|
|
719
|
+
security = true,
|
|
720
|
+
denials = true,
|
|
721
|
+
paymentRequired = true
|
|
722
|
+
} = {}) {
|
|
587
723
|
const out = {};
|
|
588
|
-
if (
|
|
724
|
+
if (security) {
|
|
589
725
|
out.securitySchemes = agentscoreSecuritySchemes();
|
|
590
726
|
}
|
|
591
|
-
if (
|
|
727
|
+
if (denials || paymentRequired) {
|
|
592
728
|
out.schemas = {
|
|
593
|
-
...
|
|
594
|
-
...
|
|
729
|
+
...denials ? agentscoreDenialSchemas() : {},
|
|
730
|
+
...paymentRequired ? agentscorePaymentRequiredSchema() : {}
|
|
595
731
|
};
|
|
596
732
|
}
|
|
597
733
|
return out;
|
|
@@ -869,21 +1005,908 @@ function buildSkillMd(input) {
|
|
|
869
1005
|
].filter((s) => s !== "");
|
|
870
1006
|
return sections.join("\n\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
871
1007
|
}
|
|
1008
|
+
|
|
1009
|
+
// src/discovery/agentscore_content.ts
|
|
1010
|
+
var PURCHASE_MODE_NOTES = Object.freeze({
|
|
1011
|
+
redemption_only: "Requires a single-use redemption code (printed on a mailer or other out-of-band delivery). Submit the code in the request body as `redemption_code`. Without a valid code the order is rejected.",
|
|
1012
|
+
coupon_applicable: "Codes are optional. Without one, settle at list price. With a valid code the discount is applied automatically (percent_off, fixed_off, or fixed_settle).",
|
|
1013
|
+
paid_only: "Codes are NOT accepted. Settle at the listed price. Submitting a `redemption_code` field returns 400 codes_not_accepted."
|
|
1014
|
+
});
|
|
1015
|
+
function purchaseModeNote(mode) {
|
|
1016
|
+
return PURCHASE_MODE_NOTES[mode] ?? "";
|
|
1017
|
+
}
|
|
1018
|
+
function buildAgentscoreOnboardingSteps(opts) {
|
|
1019
|
+
const { merchantName, appUrl, acceptedRails, requiresKyc = false, vendorType = "goods" } = opts;
|
|
1020
|
+
const railWordMap = {
|
|
1021
|
+
tempo: "Tempo USDC",
|
|
1022
|
+
"x402-base": "x402 USDC on Base",
|
|
1023
|
+
"solana-mpp": "Solana SPL USDC",
|
|
1024
|
+
"stripe-spt": "Stripe Shared Payment Token"
|
|
1025
|
+
};
|
|
1026
|
+
const railsHuman = acceptedRails.map((r) => railWordMap[r] ?? r).join(", ");
|
|
1027
|
+
const chainPairs = [
|
|
1028
|
+
["tempo", "tempo"],
|
|
1029
|
+
["x402-base", "base"],
|
|
1030
|
+
["solana-mpp", "solana"]
|
|
1031
|
+
];
|
|
1032
|
+
const flags = chainPairs.filter(([rail]) => acceptedRails.includes(rail)).map(([, flag]) => flag);
|
|
1033
|
+
const chainFlags = flags.length > 0 ? flags.join(" | ") : "tempo|base";
|
|
1034
|
+
const compatibleHints = [
|
|
1035
|
+
["tempo", "`tempo request` works for tempo USDC.e"],
|
|
1036
|
+
["x402-base", "`x402-proxy` / `purl` work for Base x402"],
|
|
1037
|
+
["stripe-spt", "`@stripe/link-cli` works for Stripe SPT"]
|
|
1038
|
+
];
|
|
1039
|
+
const compatibleFragment = compatibleHints.filter(([rail]) => acceptedRails.includes(rail)).map(([, hint]) => hint).join(", ");
|
|
1040
|
+
const installStep = `Install agentscore-pay if you don't already have a compatible client for your funded chain: \`npm i -g @agent-score/pay\` (or \`brew install agentscore/tap/agentscore-pay\`). ${merchantName} accepts: ${railsHuman}. agentscore-pay speaks every supported rail; ` + (compatibleFragment ? `the rails table also lists per-rail \`compatible_clients\` \u2014 ${compatibleFragment}. ` : "") + "Any spec-compliant client for an individual rail works too.";
|
|
1041
|
+
const bootstrapStep = `First-run only: bootstrap wallet + Passport. Run \`agentscore-pay agent-guide --json\` for the canonical cold-start path \u2014 it walks \`agentscore-pay init\` (creates keystore + per-chain wallet), \`agentscore-pay passport login\` (one-time KYC${requiresKyc ? "; required for this merchant" : ""}; the human completes a verify URL once and pay caches the operator_token), and \`agentscore-pay balance\` to see which chain has USDC. Skip if your wallet+Passport are already provisioned.`;
|
|
1042
|
+
const stripeFallbackStep = "If your only payment method is a Stripe / Link card (no crypto), install `@stripe/link-cli` instead of agentscore-pay and use it on the SPT rail. Identity gating still applies \u2014 the merchant's 403 with `verify_url` lets you bootstrap a Passport even with no crypto wallet involved.";
|
|
1043
|
+
const returningUserStep = "Returning user note: if you've paid an AgentScore-gated merchant before from this wallet, the wallet is already in your Passport's `linked_wallets[]` and identity flows through automatically with no re-KYC prompt. Paying from a NEW wallet while you already hold an `opc_...` token returns 403 `wallet_signer_mismatch`; the body lists `linked_wallets[]` and `agent_instructions.action: resign_or_switch_to_operator_token` with three deterministic recoveries (switch to a linked wallet, drop the operator_token to re-KYC the new wallet, or pre-claim the new wallet via SIWE on agentscore.sh/verify).";
|
|
1044
|
+
const pickRailStep = `Pick the rail your wallet is funded for. The 402 advertises ${acceptedRails.length} rail${acceptedRails.length === 1 ? "" : "s"}. \`agentscore-pay balance\` (without \`--chain\`) lists every chain's USDC; pay rejects with \`multi_rail_ambiguity\` if you don't pass \`--chain\` on a multi-rail challenge.`;
|
|
1045
|
+
const placeOrderStep = `Place the order: \`agentscore-pay pay POST ${appUrl}/purchase --chain <${chainFlags}> -d '<body>' --max-spend <amount>\` for crypto rails. For Stripe SPT, follow the handoff hint pay emits and use \`@stripe/link-cli\` instead. Either way pay handles the 402 retry, signing, and Passport attachment; branch on the structured CliError \`code\` on non-zero exit (insufficient_balance, multi_rail_ambiguity, config_error for missing wallet/Passport, etc.).`;
|
|
1046
|
+
const makeCallStep = `Make the paid call: \`agentscore-pay pay POST ${appUrl}/<endpoint> --chain <${chainFlags}> --max-spend <amount>\`; pay handles 402 retry, rail selection, signing, and Passport attachment. Branch on the structured CliError \`code\` on non-zero exit (insufficient_balance, multi_rail_ambiguity, config_error for missing wallet/Passport, etc.).`;
|
|
1047
|
+
const acceptsStripe = acceptedRails.includes("stripe-spt");
|
|
1048
|
+
if (vendorType === "api") {
|
|
1049
|
+
return [
|
|
1050
|
+
installStep,
|
|
1051
|
+
bootstrapStep,
|
|
1052
|
+
...acceptsStripe ? [stripeFallbackStep] : [],
|
|
1053
|
+
returningUserStep,
|
|
1054
|
+
pickRailStep,
|
|
1055
|
+
makeCallStep
|
|
1056
|
+
];
|
|
1057
|
+
}
|
|
1058
|
+
return [
|
|
1059
|
+
installStep,
|
|
1060
|
+
bootstrapStep,
|
|
1061
|
+
...acceptsStripe ? [stripeFallbackStep] : [],
|
|
1062
|
+
returningUserStep,
|
|
1063
|
+
`Browse the catalog: \`curl ${appUrl}/catalog\`.`,
|
|
1064
|
+
"Read each product's `purchase_mode` and `purchase_note` to decide whether a redemption code is required, optional, or rejected.",
|
|
1065
|
+
pickRailStep,
|
|
1066
|
+
placeOrderStep
|
|
1067
|
+
];
|
|
1068
|
+
}
|
|
1069
|
+
function standardEndpointDescriptions(opts) {
|
|
1070
|
+
if (opts?.kind === "api") {
|
|
1071
|
+
return {
|
|
1072
|
+
"POST /<endpoint>": "Per-call paid endpoint. Returns 402 on the discovery leg with payment rails; 400 on body rejection; 403 + recovery payload when identity is required; 200 with the call result on success.",
|
|
1073
|
+
"GET /usage": "Per-credential usage / billing summary. Identity-scoped."
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
const out = {
|
|
1077
|
+
"GET /catalog": "List purchasable products.",
|
|
1078
|
+
"GET /catalog/{slug}": "Single product detail.",
|
|
1079
|
+
"POST /purchase": "Place an order. Returns 402 on the discovery leg with payment rails; 400 on body rejection; 403 + recovery payload when identity is required; 200 with order confirmation on success.",
|
|
1080
|
+
"GET /orders/{id}": "Order detail (PII). Identity-scoped."
|
|
1081
|
+
};
|
|
1082
|
+
if (opts?.includeOrderStatusRoute) {
|
|
1083
|
+
out["GET /orders/{id}/status"] = "Payment status only (no PII).";
|
|
1084
|
+
}
|
|
1085
|
+
return out;
|
|
1086
|
+
}
|
|
1087
|
+
function buildMerchantIndexJson(opts) {
|
|
1088
|
+
return {
|
|
1089
|
+
name: opts.name,
|
|
1090
|
+
description: opts.description,
|
|
1091
|
+
docs: opts.docs,
|
|
1092
|
+
endpoints: opts.endpoints,
|
|
1093
|
+
audience: "agents",
|
|
1094
|
+
supported_rails: opts.supportedRails,
|
|
1095
|
+
...opts.extra ?? {}
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
function buildSuccessNextSteps(opts) {
|
|
1099
|
+
const out = {
|
|
1100
|
+
action: "done",
|
|
1101
|
+
user_message: opts.userMessage ?? "Payment complete. Your AgentScore Passport is now active across every AgentScore-gated merchant."
|
|
1102
|
+
};
|
|
1103
|
+
if (opts.orderStatusUrl) out.order_status_url = opts.orderStatusUrl;
|
|
1104
|
+
if (opts.fulfillmentEta !== void 0) out.fulfillment_eta = opts.fulfillmentEta;
|
|
1105
|
+
return out;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// src/discovery/redemption_md.ts
|
|
1109
|
+
var DEFAULT_BODY_SHAPE = `{
|
|
1110
|
+
"product_slug": "<slug>",
|
|
1111
|
+
"redemption_code": "<code>",
|
|
1112
|
+
"email": "user@example.com",
|
|
1113
|
+
"shipping": { "name": "...", "address_1": "...", "city": "...", "state": "CA", "zip": "94573" }
|
|
1114
|
+
}`;
|
|
1115
|
+
var DEFAULT_BODY_RULES = `## Body rules
|
|
1116
|
+
|
|
1117
|
+
- \`quantity\` is fixed at 1; one product per code.
|
|
1118
|
+
- \`shipping.country\` defaults to \`"US"\`; non-US shipping is rejected for
|
|
1119
|
+
redemption-eligible products.
|
|
1120
|
+
- \`shipping.state\` must be a 2-letter US state code; \`unsupported_jurisdiction\`
|
|
1121
|
+
400 if the state isn't on the merchant's allowlist.
|
|
1122
|
+
- \`email\` must be valid; the merchant returns 422 on malformed input.`;
|
|
1123
|
+
function buildRedemptionSkillMd(opts) {
|
|
1124
|
+
const {
|
|
1125
|
+
merchantName,
|
|
1126
|
+
appUrl,
|
|
1127
|
+
endpointPath = "/purchase",
|
|
1128
|
+
skuIntro,
|
|
1129
|
+
deliveryIntro,
|
|
1130
|
+
bodyShape,
|
|
1131
|
+
bodyRules,
|
|
1132
|
+
extraRecoveryRows,
|
|
1133
|
+
peerMerchantPointer
|
|
1134
|
+
} = opts;
|
|
1135
|
+
const skuText = skuIntro ?? "The code redeems a product or paid call at this merchant which you'll find in the merchant's catalog or per-endpoint documentation with `purchase_mode = redemption_only`.";
|
|
1136
|
+
const deliveryText = deliveryIntro ?? `You're reading this because the user you're working for received a single-use redemption code from ${merchantName} (printed on a mailer, emailed, surfaced in-app, or distributed out-of-band). This page tells you, the agent, exactly how to turn that code into a settled call.`;
|
|
1137
|
+
const renderedBodyShape = bodyShape ?? DEFAULT_BODY_SHAPE;
|
|
1138
|
+
const renderedBodyRules = bodyRules !== void 0 ? bodyRules : DEFAULT_BODY_RULES;
|
|
1139
|
+
const bodyRulesSection = renderedBodyRules ? `
|
|
1140
|
+
${renderedBodyRules}
|
|
1141
|
+
` : "";
|
|
1142
|
+
const extraRows = extraRecoveryRows ? `
|
|
1143
|
+
${extraRecoveryRows.trimEnd()}` : "";
|
|
1144
|
+
const peerSection = peerMerchantPointer ? `
|
|
1145
|
+
## Don't have a code?
|
|
1146
|
+
|
|
1147
|
+
This page is the redemption flow for single-use codes. If you're looking to buy or call without a code, see: ${peerMerchantPointer}
|
|
1148
|
+
` : "";
|
|
1149
|
+
return `# Redeeming an AgentScore code at ${merchantName}
|
|
1150
|
+
|
|
1151
|
+
${deliveryText}
|
|
1152
|
+
|
|
1153
|
+
${skuText} The 402 challenge on ${endpointPath} tells you the actual settle amount
|
|
1154
|
+
after the code is applied; discounts can range from a partial amount off list
|
|
1155
|
+
down to free.
|
|
1156
|
+
|
|
1157
|
+
## Cold-start bootstrap (skip if your wallet + Passport are already set up)
|
|
1158
|
+
|
|
1159
|
+
If \`agentscore-pay\` isn't installed yet, install it (\`npm i -g @agent-score/pay\`
|
|
1160
|
+
or \`brew install agentscore/tap/agentscore-pay\`), then run \`agentscore-pay
|
|
1161
|
+
agent-guide --json\` for the canonical cold-start path. That walks
|
|
1162
|
+
\`agentscore-pay init\` (creates keystore + per-chain wallet),
|
|
1163
|
+
\`agentscore-pay passport login\` (one-time KYC; opens a verify URL the human
|
|
1164
|
+
completes, after which pay caches the \`operator_token\`), and
|
|
1165
|
+
\`agentscore-pay balance\` to confirm funds. Fund enough to cover the
|
|
1166
|
+
post-discount settle amount the 402 advertises; for $0 codes the merchant
|
|
1167
|
+
skips the on-chain settle entirely so funds aren't required, but the wallet
|
|
1168
|
+
still needs to exist so the credential can be signed.
|
|
1169
|
+
|
|
1170
|
+
You don't have to use \`agentscore-pay\` specifically; any spec-compliant client
|
|
1171
|
+
for the merchant's accepted rails (Tempo MPP, x402 Base, Solana MPP, Stripe SPT)
|
|
1172
|
+
works. The 402 challenge lists every accepted rail in \`accepted_methods\`.
|
|
1173
|
+
|
|
1174
|
+
## TL;DR
|
|
1175
|
+
|
|
1176
|
+
1. Ask the user for their redemption code, plus any merchant-specific fields the
|
|
1177
|
+
body requires (email, shipping address for goods merchants, identifiers for
|
|
1178
|
+
API merchants, etc.).
|
|
1179
|
+
2. Discover the redemption-eligible target. Goods merchants: \`GET ${appUrl}/catalog\`
|
|
1180
|
+
and find the product whose \`purchase_mode\` is \`redemption_only\`. API merchants:
|
|
1181
|
+
read the per-endpoint docs for the route that accepts \`redemption_code\`.
|
|
1182
|
+
Read any \`purchase_note\` for product-specific rules.
|
|
1183
|
+
3. \`POST ${appUrl}${endpointPath}\` with body:
|
|
1184
|
+
\`\`\`json
|
|
1185
|
+
${renderedBodyShape}
|
|
1186
|
+
\`\`\`
|
|
1187
|
+
4. If you get **403 \`operator_verification_required\`**, surface the body's
|
|
1188
|
+
\`verify_url\` to the user for one-time KYC and poll \`poll_url\` with
|
|
1189
|
+
\`poll_secret\`. After verification, retry with \`X-Operator-Token\` attached.
|
|
1190
|
+
If you already have an \`opc_...\` from a prior AgentScore-gated merchant,
|
|
1191
|
+
attach it on the first call and skip this step.
|
|
1192
|
+
5. On **402**, the body carries \`accepted_methods\` and \`agent_instructions.how_to_pay\`.
|
|
1193
|
+
Settle with \`agentscore-pay pay POST ${appUrl}${endpointPath} --chain <rail> -d '<body>'
|
|
1194
|
+
--max-spend <amount>\`; pay handles 402 retry, rail selection, signing, and
|
|
1195
|
+
Passport attachment. Pass \`--max-spend\` >= the amount in the 402.
|
|
1196
|
+
6. **200**; the call settled. Response carries a receipt \`id\` (order id for goods,
|
|
1197
|
+
request id for API), \`next_steps.order_status_url\` (or usage dashboard URL),
|
|
1198
|
+
and an \`agent_memory\` block you should persist (the cross-merchant pattern
|
|
1199
|
+
hint, NOT the operator_token or poll_secret). For $0 redemptions \`tx_hash\`
|
|
1200
|
+
is \`null\`; the credential is still authenticated and the code is burned
|
|
1201
|
+
single-use.
|
|
1202
|
+
${bodyRulesSection}
|
|
1203
|
+
## Code rules
|
|
1204
|
+
|
|
1205
|
+
- Codes are case-insensitive (server uppercases on receipt), single-use, and
|
|
1206
|
+
burned atomically against \`(code, operator_token)\` OR \`(code, signer_address)\`
|
|
1207
|
+
for token-less wallet flows. A second attempt returns 400 \`redemption_already_used\`.
|
|
1208
|
+
- Submit the code in the JSON body as \`"redemption_code"\`; never as a header.
|
|
1209
|
+
|
|
1210
|
+
## Recovery on common errors
|
|
1211
|
+
|
|
1212
|
+
| HTTP | error.code | What it means | What to do |
|
|
1213
|
+
|---|---|---|---|
|
|
1214
|
+
| 403 | \`operator_verification_required\` | User has no Passport / KYC pending | Surface \`verify_url\`; poll \`poll_url\` with \`poll_secret\`; retry with \`X-Operator-Token\` |
|
|
1215
|
+
| 403 | \`wallet_signer_mismatch\` | Operator token + signer wallet aren't linked to the same identity | Switch to a wallet in \`linked_wallets[]\`, or drop the operator_token to re-KYC the new wallet |
|
|
1216
|
+
| 400 | \`invalid_body\` | JSON parse failed | Fix the JSON and retry |
|
|
1217
|
+
| 400 | \`missing_fields\` | Required field absent | Add the field per \`error.message\` and retry |
|
|
1218
|
+
| 400 | \`product_not_found\` | Identifier doesn't match an active product / endpoint | Re-check the catalog or endpoint docs and use the exact slug / route |
|
|
1219
|
+
| 400 | \`product_out_of_stock\` | Goods-only: stock 0 | Tell the user; no retry possible |
|
|
1220
|
+
| 400 | \`invalid_redemption_code\` | Code unknown / expired | Ask the user for the code as printed; do not invent variants |
|
|
1221
|
+
| 400 | \`redemption_already_used\` | Code burned | Tell the user; codes are single-use |
|
|
1222
|
+
| 400 | \`codes_not_accepted\` | Target is \`paid_only\` and rejects codes | Drop \`redemption_code\` and retry, or pick a different target |
|
|
1223
|
+
| 402 | (challenge) | Identity OK; payment required | Run \`agentscore-pay pay\` against the same URL |${extraRows}
|
|
1224
|
+
${peerSection}`;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/identity/ucp.ts
|
|
1228
|
+
function ucpSigningKeyFromJWKImpl(jwk) {
|
|
1229
|
+
if (!jwk || typeof jwk !== "object") {
|
|
1230
|
+
throw new Error(`UCPSigningKey.fromJWK expected a non-null object; got ${typeof jwk}.`);
|
|
1231
|
+
}
|
|
1232
|
+
if (typeof jwk.kid !== "string" || !jwk.kid) {
|
|
1233
|
+
throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kid` (or non-string).");
|
|
1234
|
+
}
|
|
1235
|
+
if (typeof jwk.kty !== "string" || !jwk.kty) {
|
|
1236
|
+
throw new Error("UCPSigningKey.fromJWK: JWK missing required field `kty` (or non-string).");
|
|
1237
|
+
}
|
|
1238
|
+
if (jwk.kty !== "OKP" && jwk.kty !== "EC" && jwk.kty !== "RSA") {
|
|
1239
|
+
throw new Error(
|
|
1240
|
+
`UCPSigningKey.fromJWK: kty=${JSON.stringify(jwk.kty)} is not a supported asymmetric key type (expected OKP, EC, or RSA). Symmetric \`oct\` keys are rejected because they cannot publicly verify a JWS in the trust-mode UCP flow.`
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
if ((jwk.kty === "EC" || jwk.kty === "OKP") && (typeof jwk.crv !== "string" || !jwk.crv)) {
|
|
1244
|
+
throw new Error(`UCPSigningKey.fromJWK: kty=${jwk.kty} requires a non-empty \`crv\` field (e.g., "P-256" for EC, "Ed25519" for OKP).`);
|
|
1245
|
+
}
|
|
1246
|
+
return jwk;
|
|
1247
|
+
}
|
|
1248
|
+
var UCPSigningKey = {
|
|
1249
|
+
fromJWK: ucpSigningKeyFromJWKImpl
|
|
1250
|
+
};
|
|
1251
|
+
var DEFAULT_VERSION = "2026-04-08";
|
|
1252
|
+
var AGENTSCORE_CAPABILITY_NAME = "sh.agentscore.identity";
|
|
1253
|
+
var AGENTSCORE_CAPABILITY_VERSION = "2026-04-08";
|
|
1254
|
+
var AGENTSCORE_DEFAULT_SPEC_URL = "https://agentscore.sh/specification/identity";
|
|
1255
|
+
var AGENTSCORE_DEFAULT_SCHEMA_URL = "https://agentscore.sh/schemas/ucp/sh-agentscore-identity-v1.json";
|
|
1256
|
+
var AGENTSCORE_EXTENDS = ["dev.ucp.shopping.checkout", "dev.ucp.shopping.cart"];
|
|
1257
|
+
var RESERVED_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
1258
|
+
"ucp",
|
|
1259
|
+
"signing_keys",
|
|
1260
|
+
"signature",
|
|
1261
|
+
"__proto__",
|
|
1262
|
+
"constructor",
|
|
1263
|
+
"prototype"
|
|
1264
|
+
]);
|
|
1265
|
+
var RESERVED_UCP_FIELDS = /* @__PURE__ */ new Set([
|
|
1266
|
+
"version",
|
|
1267
|
+
"name",
|
|
1268
|
+
"services",
|
|
1269
|
+
"capabilities",
|
|
1270
|
+
"payment_handlers",
|
|
1271
|
+
"supported_versions",
|
|
1272
|
+
"__proto__",
|
|
1273
|
+
"constructor",
|
|
1274
|
+
"prototype"
|
|
1275
|
+
]);
|
|
1276
|
+
function buildUCPProfile(input) {
|
|
1277
|
+
for (const [name, bindings] of Object.entries(input.services ?? {})) {
|
|
1278
|
+
for (const binding of bindings) {
|
|
1279
|
+
if ((binding.transport === "rest" || binding.transport === "mcp" || binding.transport === "a2a") && (binding.endpoint === void 0 || binding.endpoint === null || binding.endpoint === "")) {
|
|
1280
|
+
throw new Error(
|
|
1281
|
+
`buildUCPProfile: service "${name}" transport=${binding.transport} requires \`endpoint\`. Per UCP spec service.json business_schema, rest/mcp/a2a bindings MUST carry an endpoint URL.`
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
const paymentHandlers = {};
|
|
1287
|
+
for (const [name, bindings] of Object.entries(input.payment_handlers ?? {})) {
|
|
1288
|
+
paymentHandlers[name] = bindings.map((binding) => {
|
|
1289
|
+
if (Array.isArray(binding.available_instruments) && binding.available_instruments.length === 0) {
|
|
1290
|
+
const { available_instruments: _drop, ...rest } = binding;
|
|
1291
|
+
return rest;
|
|
1292
|
+
}
|
|
1293
|
+
return binding;
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
const capabilities = {};
|
|
1297
|
+
for (const [name, bindings] of Object.entries(input.capabilities ?? {})) {
|
|
1298
|
+
capabilities[name] = [...bindings];
|
|
1299
|
+
}
|
|
1300
|
+
if (input.agentscore_gate) {
|
|
1301
|
+
const gateConfig = { ...input.agentscore_gate };
|
|
1302
|
+
const agentscoreBinding = {
|
|
1303
|
+
version: AGENTSCORE_CAPABILITY_VERSION,
|
|
1304
|
+
spec: input.agentscore_spec_url ?? AGENTSCORE_DEFAULT_SPEC_URL,
|
|
1305
|
+
schema: input.agentscore_schema_url ?? AGENTSCORE_DEFAULT_SCHEMA_URL,
|
|
1306
|
+
extends: AGENTSCORE_EXTENDS
|
|
1307
|
+
};
|
|
1308
|
+
if (Object.keys(gateConfig).length > 0) agentscoreBinding.config = gateConfig;
|
|
1309
|
+
const existing = capabilities[AGENTSCORE_CAPABILITY_NAME];
|
|
1310
|
+
if (existing) existing.push(agentscoreBinding);
|
|
1311
|
+
else capabilities[AGENTSCORE_CAPABILITY_NAME] = [agentscoreBinding];
|
|
1312
|
+
}
|
|
1313
|
+
const ucp = {
|
|
1314
|
+
version: input.version ?? DEFAULT_VERSION,
|
|
1315
|
+
services: input.services ?? {},
|
|
1316
|
+
capabilities,
|
|
1317
|
+
payment_handlers: paymentHandlers
|
|
1318
|
+
};
|
|
1319
|
+
if (input.name !== void 0) ucp.name = input.name;
|
|
1320
|
+
if (input.supported_versions !== void 0) ucp.supported_versions = input.supported_versions;
|
|
1321
|
+
if (input.ucp_extras) {
|
|
1322
|
+
for (const k of Object.keys(input.ucp_extras)) {
|
|
1323
|
+
if (RESERVED_UCP_FIELDS.has(k)) {
|
|
1324
|
+
throw new Error(`buildUCPProfile: ucp_extras key "${k}" collides with a reserved \`ucp\` field; rejected.`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
Object.assign(ucp, input.ucp_extras);
|
|
1328
|
+
}
|
|
1329
|
+
const profile = {
|
|
1330
|
+
ucp,
|
|
1331
|
+
signing_keys: input.signing_keys
|
|
1332
|
+
};
|
|
1333
|
+
if (input.extras) {
|
|
1334
|
+
for (const k of Object.keys(input.extras)) {
|
|
1335
|
+
if (RESERVED_TOP_LEVEL.has(k)) {
|
|
1336
|
+
throw new Error(`buildUCPProfile: extras key "${k}" collides with a reserved profile field; rejected.`);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
Object.assign(profile, input.extras);
|
|
1340
|
+
}
|
|
1341
|
+
return profile;
|
|
1342
|
+
}
|
|
1343
|
+
var HANDLER_VERSION = "2026-04-08";
|
|
1344
|
+
var SPEC_BASE = "https://agentscore.sh/specification/payment-handlers";
|
|
1345
|
+
var SCHEMA_BASE = "https://agentscore.sh/schemas/payment-handlers";
|
|
1346
|
+
var CAIP2_TO_UCP_NETWORK = {
|
|
1347
|
+
"eip155:8453": "base-8453",
|
|
1348
|
+
"eip155:84532": "base-84532",
|
|
1349
|
+
"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "solana-mainnet-beta",
|
|
1350
|
+
"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "solana-devnet"
|
|
1351
|
+
};
|
|
1352
|
+
function ucpNetworkName(caip2OrUcp, fallback) {
|
|
1353
|
+
if (caip2OrUcp === void 0) return fallback;
|
|
1354
|
+
return CAIP2_TO_UCP_NETWORK[caip2OrUcp] ?? caip2OrUcp;
|
|
1355
|
+
}
|
|
1356
|
+
function staticRecipient(r) {
|
|
1357
|
+
return typeof r === "string" && r.length > 0 ? r : void 0;
|
|
1358
|
+
}
|
|
1359
|
+
function tempoToNetworkEntry(spec) {
|
|
1360
|
+
const entry = {
|
|
1361
|
+
network: spec.testnet ? "tempo-testnet" : spec.network ?? "tempo-mainnet",
|
|
1362
|
+
chain_id: spec.chainId ?? 4217
|
|
1363
|
+
};
|
|
1364
|
+
const recipient = staticRecipient(spec.recipient);
|
|
1365
|
+
if (recipient !== void 0) entry.recipient = recipient;
|
|
1366
|
+
return entry;
|
|
1367
|
+
}
|
|
1368
|
+
function solanaMppToNetworkEntry(spec) {
|
|
1369
|
+
const entry = {
|
|
1370
|
+
network: ucpNetworkName(spec.network, "solana-mainnet-beta")
|
|
1371
|
+
};
|
|
1372
|
+
const recipient = staticRecipient(spec.recipient);
|
|
1373
|
+
if (recipient !== void 0) entry.recipient = recipient;
|
|
1374
|
+
return entry;
|
|
1375
|
+
}
|
|
1376
|
+
function tempoSessionToNetworkEntry(spec) {
|
|
1377
|
+
const entry = {
|
|
1378
|
+
network: spec.testnet ? "tempo-testnet" : "tempo-mainnet",
|
|
1379
|
+
escrow_contract: spec.escrowContract
|
|
1380
|
+
};
|
|
1381
|
+
const recipient = staticRecipient(spec.recipient);
|
|
1382
|
+
if (recipient !== void 0) entry.recipient = recipient;
|
|
1383
|
+
return entry;
|
|
1384
|
+
}
|
|
1385
|
+
function isTempoRailSpec(s) {
|
|
1386
|
+
return !("escrowContract" in s) && !("rpcUrl" in s) && !("tokenProgram" in s);
|
|
1387
|
+
}
|
|
1388
|
+
function isTempoSessionRailSpec(s) {
|
|
1389
|
+
return "escrowContract" in s && "store" in s;
|
|
1390
|
+
}
|
|
1391
|
+
function mppRailToNetworkEntry(spec) {
|
|
1392
|
+
if (isTempoSessionRailSpec(spec)) return tempoSessionToNetworkEntry(spec);
|
|
1393
|
+
if ("rpcUrl" in spec || "tokenProgram" in spec || (spec.network?.startsWith("solana:") ?? false)) {
|
|
1394
|
+
return solanaMppToNetworkEntry(spec);
|
|
1395
|
+
}
|
|
1396
|
+
if (isTempoRailSpec(spec)) return tempoToNetworkEntry(spec);
|
|
1397
|
+
return tempoToNetworkEntry(spec);
|
|
1398
|
+
}
|
|
1399
|
+
function mppPaymentHandler({
|
|
1400
|
+
networks: networks2
|
|
1401
|
+
}) {
|
|
1402
|
+
return {
|
|
1403
|
+
"sh.agentscore.payment.mpp": [{
|
|
1404
|
+
id: "mpp",
|
|
1405
|
+
version: HANDLER_VERSION,
|
|
1406
|
+
spec: `${SPEC_BASE}/mpp`,
|
|
1407
|
+
schema: `${SCHEMA_BASE}/mpp.json`,
|
|
1408
|
+
config: { networks: networks2.map(mppRailToNetworkEntry) }
|
|
1409
|
+
}]
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
function x402RailToNetworkEntry(spec) {
|
|
1413
|
+
const entry = {
|
|
1414
|
+
network: ucpNetworkName(spec.network, "base-8453")
|
|
1415
|
+
};
|
|
1416
|
+
const recipient = staticRecipient(spec.recipient);
|
|
1417
|
+
if (recipient !== void 0) entry.recipient = recipient;
|
|
1418
|
+
return entry;
|
|
1419
|
+
}
|
|
1420
|
+
function x402PaymentHandler({
|
|
1421
|
+
networks: networks2
|
|
1422
|
+
}) {
|
|
1423
|
+
return {
|
|
1424
|
+
"sh.agentscore.payment.x402": [{
|
|
1425
|
+
id: "x402",
|
|
1426
|
+
version: HANDLER_VERSION,
|
|
1427
|
+
spec: `${SPEC_BASE}/x402`,
|
|
1428
|
+
schema: `${SCHEMA_BASE}/x402.json`,
|
|
1429
|
+
config: { networks: networks2.map(x402RailToNetworkEntry) }
|
|
1430
|
+
}]
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
function stripeSptPaymentHandler({
|
|
1434
|
+
spec
|
|
1435
|
+
}) {
|
|
1436
|
+
return {
|
|
1437
|
+
"sh.agentscore.payment.stripe_spt": [{
|
|
1438
|
+
id: "stripe-spt",
|
|
1439
|
+
version: HANDLER_VERSION,
|
|
1440
|
+
spec: `${SPEC_BASE}/stripe_spt`,
|
|
1441
|
+
schema: `${SCHEMA_BASE}/stripe_spt.json`,
|
|
1442
|
+
config: { rail: "stripe-spt", profile_id: spec.profileId ?? null }
|
|
1443
|
+
}]
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// src/identity/ucp-jwks.ts
|
|
1448
|
+
var JOSE_INSTALL_HINT = "Install the optional peer dependency: `npm install jose@^6` (or `bun add jose`). Tested against jose v6.x.";
|
|
1449
|
+
var ALLOWED_ALGS = ["EdDSA", "ES256"];
|
|
1450
|
+
var PROFILE_TYP = "agentscore-profile+jws";
|
|
1451
|
+
async function loadJose() {
|
|
1452
|
+
try {
|
|
1453
|
+
return await import("jose");
|
|
1454
|
+
} catch (err) {
|
|
1455
|
+
throw new Error(
|
|
1456
|
+
`UCP signing requires the \`jose\` library, which is an optional peer dependency. ${JOSE_INSTALL_HINT}
|
|
1457
|
+
Original error: ${err instanceof Error ? err.message : String(err)}`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
function canonicalizeProfile(profile) {
|
|
1462
|
+
const stripped = { ...profile };
|
|
1463
|
+
delete stripped.signature;
|
|
1464
|
+
return stableStringify(stripped);
|
|
1465
|
+
}
|
|
1466
|
+
function stableStringify(value) {
|
|
1467
|
+
if (value === void 0) {
|
|
1468
|
+
throw new Error(
|
|
1469
|
+
"stableStringify: undefined values are not allowed in canonicalized JSON. Object fields with no value must be omitted."
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
1473
|
+
throw new Error(`stableStringify: ${typeof value} values are not allowed in canonicalized JSON.`);
|
|
1474
|
+
}
|
|
1475
|
+
if (typeof value === "bigint") {
|
|
1476
|
+
throw new Error("stableStringify: BigInt values are not allowed; use a decimal string.");
|
|
1477
|
+
}
|
|
1478
|
+
if (value instanceof Date) {
|
|
1479
|
+
throw new Error(
|
|
1480
|
+
"stableStringify: Date instances are not allowed; serialize to an ISO string before passing."
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
if (value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet) {
|
|
1484
|
+
throw new Error(
|
|
1485
|
+
`stableStringify: ${value.constructor.name} values are not allowed; convert to a plain object/array first.`
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
if (ArrayBuffer.isView(value)) {
|
|
1489
|
+
throw new Error("stableStringify: typed arrays are not allowed; convert to a plain array first.");
|
|
1490
|
+
}
|
|
1491
|
+
if (typeof value === "number") {
|
|
1492
|
+
if (!Number.isFinite(value)) {
|
|
1493
|
+
throw new Error(
|
|
1494
|
+
`UCP profile canonicalization rejects non-finite Number ${value}. Use a decimal string for any value that may be NaN/Infinity.`
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
if (!Number.isInteger(value)) {
|
|
1498
|
+
throw new Error(
|
|
1499
|
+
`UCP profile canonicalization rejects non-integer Number ${value}. Use a decimal string (e.g. "9.99") for monetary or fractional fields to preserve cross-language byte-parity.`
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
if (!Number.isSafeInteger(value)) {
|
|
1503
|
+
throw new Error(
|
|
1504
|
+
`stableStringify: integer ${value} exceeds Number.MAX_SAFE_INTEGER. For values >2^53, use a decimal string to preserve cross-language byte parity.`
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
if (typeof value === "string") {
|
|
1509
|
+
if (value.includes("\u2028") || value.includes("\u2029")) {
|
|
1510
|
+
throw new Error(
|
|
1511
|
+
"stableStringify: strings containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity requires neither be present (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
return JSON.stringify(value);
|
|
1515
|
+
}
|
|
1516
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
1517
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
1518
|
+
const obj = value;
|
|
1519
|
+
const keys = Object.keys(obj).sort((a, b) => {
|
|
1520
|
+
const aPoints = [...a].map((c) => c.codePointAt(0));
|
|
1521
|
+
const bPoints = [...b].map((c) => c.codePointAt(0));
|
|
1522
|
+
const len = Math.min(aPoints.length, bPoints.length);
|
|
1523
|
+
for (let i = 0; i < len; i += 1) {
|
|
1524
|
+
if (aPoints[i] !== bPoints[i]) return aPoints[i] - bPoints[i];
|
|
1525
|
+
}
|
|
1526
|
+
return aPoints.length - bPoints.length;
|
|
1527
|
+
});
|
|
1528
|
+
for (const k of keys) {
|
|
1529
|
+
if (k.includes("\u2028") || k.includes("\u2029")) {
|
|
1530
|
+
throw new Error(
|
|
1531
|
+
"stableStringify: object keys containing U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) are not allowed; cross-language byte parity (Node JSON.stringify on older V8 escapes them; Python json.dumps with ensure_ascii=False does not)."
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
const pairs = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
1536
|
+
return `{${pairs.join(",")}}`;
|
|
1537
|
+
}
|
|
1538
|
+
async function generateUCPSigningKey(opts) {
|
|
1539
|
+
const jose = await loadJose();
|
|
1540
|
+
const alg = opts.alg ?? "EdDSA";
|
|
1541
|
+
const { privateKey, publicKey } = await jose.generateKeyPair(alg, { extractable: true });
|
|
1542
|
+
const exportedJwk = await jose.exportJWK(publicKey);
|
|
1543
|
+
const publicJWK = {
|
|
1544
|
+
kid: opts.kid,
|
|
1545
|
+
alg,
|
|
1546
|
+
use: "sig",
|
|
1547
|
+
...exportedJwk
|
|
1548
|
+
};
|
|
1549
|
+
return { privateKey, publicJWK };
|
|
1550
|
+
}
|
|
1551
|
+
async function signUCPProfile(profile, {
|
|
1552
|
+
signingKey,
|
|
1553
|
+
kid,
|
|
1554
|
+
alg = "EdDSA"
|
|
1555
|
+
}) {
|
|
1556
|
+
const jose = await loadJose();
|
|
1557
|
+
if (!ALLOWED_ALGS.includes(alg)) {
|
|
1558
|
+
throw new Error(
|
|
1559
|
+
`signUCPProfile: alg ${JSON.stringify(alg)} is not in the supported set [${ALLOWED_ALGS.join(", ")}].`
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
if (typeof kid !== "string" || !kid) {
|
|
1563
|
+
throw new Error("signUCPProfile: kid must be a non-empty string.");
|
|
1564
|
+
}
|
|
1565
|
+
const kids = (profile.signing_keys ?? []).map((k) => k.kid);
|
|
1566
|
+
if (!kids.includes(kid)) {
|
|
1567
|
+
throw new Error(
|
|
1568
|
+
`signUCPProfile: kid ${JSON.stringify(kid)} is not present in profile.signing_keys[] (declared kids: ${JSON.stringify(kids)}). Verifiers will not find the key.`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
const canonicalBody = canonicalizeProfile(profile);
|
|
1572
|
+
const payloadBytes = new TextEncoder().encode(canonicalBody);
|
|
1573
|
+
const signature = await new jose.CompactSign(payloadBytes).setProtectedHeader({ alg, kid, typ: PROFILE_TYP }).sign(signingKey);
|
|
1574
|
+
return { ...profile, signature };
|
|
1575
|
+
}
|
|
1576
|
+
function buildJWKSResponse(keys) {
|
|
1577
|
+
return { keys };
|
|
1578
|
+
}
|
|
1579
|
+
var DEFAULT_LOAD_OPTS = {
|
|
1580
|
+
envJwkVar: "UCP_SIGNING_KEY_JWK_PRIVATE",
|
|
1581
|
+
envKidVar: "UCP_SIGNING_KEY_KID",
|
|
1582
|
+
envAlgVar: "UCP_SIGNING_KEY_ALG",
|
|
1583
|
+
defaultKid: "merchant-default",
|
|
1584
|
+
defaultAlg: "EdDSA"
|
|
1585
|
+
};
|
|
1586
|
+
function readEnvTrimmed(name) {
|
|
1587
|
+
const raw = process.env[name];
|
|
1588
|
+
if (raw === void 0) return void 0;
|
|
1589
|
+
const trimmed = raw.trim();
|
|
1590
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
1591
|
+
}
|
|
1592
|
+
function detectAlgFromJwk(jwk) {
|
|
1593
|
+
if (jwk.kty === "OKP" && jwk.crv === "Ed25519") return "EdDSA";
|
|
1594
|
+
if (jwk.kty === "EC" && jwk.crv === "P-256") return "ES256";
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
var envLoaderCache = /* @__PURE__ */ new Map();
|
|
1598
|
+
function cacheKey(opts) {
|
|
1599
|
+
return `${opts.envJwkVar}|${opts.envKidVar}|${opts.envAlgVar}|${opts.defaultKid}|${opts.defaultAlg}`;
|
|
1600
|
+
}
|
|
1601
|
+
async function buildEnvSigningKey(opts) {
|
|
1602
|
+
const kidDefault = readEnvTrimmed(opts.envKidVar) ?? opts.defaultKid;
|
|
1603
|
+
const rawAlg = (readEnvTrimmed(opts.envAlgVar) ?? "").toUpperCase();
|
|
1604
|
+
const algFallback = rawAlg === "ES256" ? "ES256" : opts.defaultAlg;
|
|
1605
|
+
const envJwk = readEnvTrimmed(opts.envJwkVar);
|
|
1606
|
+
if (envJwk) {
|
|
1607
|
+
let jwkDict;
|
|
1608
|
+
try {
|
|
1609
|
+
jwkDict = JSON.parse(envJwk);
|
|
1610
|
+
} catch (err) {
|
|
1611
|
+
throw new Error(
|
|
1612
|
+
`${opts.envJwkVar} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
if (!jwkDict || typeof jwkDict !== "object" || Array.isArray(jwkDict) || Object.keys(jwkDict).length === 0) {
|
|
1616
|
+
throw new Error(`${opts.envJwkVar} must be a non-empty JWK object.`);
|
|
1617
|
+
}
|
|
1618
|
+
const detectedAlg = detectAlgFromJwk(jwkDict);
|
|
1619
|
+
if (!detectedAlg) {
|
|
1620
|
+
throw new Error(
|
|
1621
|
+
`${opts.envJwkVar} has unsupported kty/crv (got kty=${String(jwkDict.kty)} crv=${String(jwkDict.crv)}); expected OKP+Ed25519 or EC+P-256.`
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
const canonicalPrivateJwk = detectedAlg === "EdDSA" ? { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, d: jwkDict.d } : { kty: jwkDict.kty, crv: jwkDict.crv, x: jwkDict.x, y: jwkDict.y, d: jwkDict.d };
|
|
1625
|
+
const { importJWK } = await import("jose");
|
|
1626
|
+
const { createPublicKey } = await import("crypto");
|
|
1627
|
+
let privateKey;
|
|
1628
|
+
let publicNodeKey;
|
|
1629
|
+
try {
|
|
1630
|
+
privateKey = await importJWK(
|
|
1631
|
+
canonicalPrivateJwk,
|
|
1632
|
+
detectedAlg
|
|
1633
|
+
);
|
|
1634
|
+
publicNodeKey = createPublicKey({ key: canonicalPrivateJwk, format: "jwk" });
|
|
1635
|
+
} catch (err) {
|
|
1636
|
+
const className = err instanceof Error ? err.constructor.name : typeof err;
|
|
1637
|
+
const code = err && typeof err === "object" && "code" in err ? String(err.code) : null;
|
|
1638
|
+
const codeSuffix = code ? ` [${code}]` : "";
|
|
1639
|
+
throw new Error(
|
|
1640
|
+
`${opts.envJwkVar} has malformed key material (${className}${codeSuffix}). Verify the JWK is well-formed and matches the declared kty/crv. Underlying details suppressed to avoid leaking key bytes.`
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
const publicJWK = publicNodeKey.export({ format: "jwk" });
|
|
1644
|
+
publicJWK.kid = jwkDict.kid || kidDefault;
|
|
1645
|
+
publicJWK.alg = detectedAlg;
|
|
1646
|
+
publicJWK.use = "sig";
|
|
1647
|
+
return { privateKey, publicJWK };
|
|
1648
|
+
}
|
|
1649
|
+
return generateUCPSigningKey({ kid: kidDefault, alg: algFallback });
|
|
1650
|
+
}
|
|
1651
|
+
async function loadUCPSigningKeyFromEnv({
|
|
1652
|
+
envJwkVar,
|
|
1653
|
+
envKidVar,
|
|
1654
|
+
envAlgVar,
|
|
1655
|
+
defaultKid,
|
|
1656
|
+
defaultAlg
|
|
1657
|
+
} = {}) {
|
|
1658
|
+
const resolved = {
|
|
1659
|
+
...DEFAULT_LOAD_OPTS,
|
|
1660
|
+
...envJwkVar !== void 0 && { envJwkVar },
|
|
1661
|
+
...envKidVar !== void 0 && { envKidVar },
|
|
1662
|
+
...envAlgVar !== void 0 && { envAlgVar },
|
|
1663
|
+
...defaultKid !== void 0 && { defaultKid },
|
|
1664
|
+
...defaultAlg !== void 0 && { defaultAlg }
|
|
1665
|
+
};
|
|
1666
|
+
const key = cacheKey(resolved);
|
|
1667
|
+
let cached = envLoaderCache.get(key);
|
|
1668
|
+
if (cached) return cached;
|
|
1669
|
+
cached = buildEnvSigningKey(resolved).catch((err) => {
|
|
1670
|
+
envLoaderCache.delete(key);
|
|
1671
|
+
throw err;
|
|
1672
|
+
});
|
|
1673
|
+
envLoaderCache.set(key, cached);
|
|
1674
|
+
return cached;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// src/discovery/well_known.ts
|
|
1678
|
+
var UCP_CACHE_SECONDS = 60;
|
|
1679
|
+
var JWKS_CACHE_SECONDS = 300;
|
|
1680
|
+
var UCP_SHOPPING_SPEC_2026_04_08 = "https://ucp.dev/2026-04-08/specification/overview";
|
|
1681
|
+
function requestId(headers) {
|
|
1682
|
+
if (headers === void 0) return void 0;
|
|
1683
|
+
if (headers instanceof Headers) return headers.get("x-request-id") ?? void 0;
|
|
1684
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
1685
|
+
if (k.toLowerCase() === "x-request-id") return v;
|
|
1686
|
+
}
|
|
1687
|
+
return void 0;
|
|
1688
|
+
}
|
|
1689
|
+
function attachRequestId(headers, requestHeaders) {
|
|
1690
|
+
const rid = requestId(requestHeaders);
|
|
1691
|
+
if (rid !== void 0) headers["X-Request-ID"] = rid;
|
|
1692
|
+
}
|
|
1693
|
+
function isTempoSession(s) {
|
|
1694
|
+
return "escrowContract" in s && "store" in s;
|
|
1695
|
+
}
|
|
1696
|
+
function isStripe(s) {
|
|
1697
|
+
return !("recipient" in s);
|
|
1698
|
+
}
|
|
1699
|
+
function railHasRecipientField(spec) {
|
|
1700
|
+
return Object.hasOwn(spec, "recipient");
|
|
1701
|
+
}
|
|
1702
|
+
function composeHandlers(checkout) {
|
|
1703
|
+
const handlers = {};
|
|
1704
|
+
const mpp = [];
|
|
1705
|
+
const x402 = [];
|
|
1706
|
+
const stripe = [];
|
|
1707
|
+
for (const spec of Object.values(checkout.rails)) {
|
|
1708
|
+
if (isStripe(spec)) {
|
|
1709
|
+
stripe.push(spec);
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
if (isTempoSession(spec)) {
|
|
1713
|
+
if (railHasRecipientField(spec)) mpp.push(spec);
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
const network = spec.network ?? "";
|
|
1717
|
+
if (network.startsWith("eip155:") || "mode" in spec) {
|
|
1718
|
+
if (railHasRecipientField(spec)) x402.push(spec);
|
|
1719
|
+
} else if (network.startsWith("solana:") || "rpcUrl" in spec) {
|
|
1720
|
+
if (railHasRecipientField(spec)) mpp.push(spec);
|
|
1721
|
+
} else {
|
|
1722
|
+
if (railHasRecipientField(spec)) mpp.push(spec);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
if (mpp.length > 0) Object.assign(handlers, mppPaymentHandler({ networks: mpp }));
|
|
1726
|
+
if (x402.length > 0) Object.assign(handlers, x402PaymentHandler({ networks: x402 }));
|
|
1727
|
+
for (const spec of stripe) Object.assign(handlers, stripeSptPaymentHandler({ spec }));
|
|
1728
|
+
return handlers;
|
|
1729
|
+
}
|
|
1730
|
+
function misconfiguredResponse(requestHeaders) {
|
|
1731
|
+
const body = {
|
|
1732
|
+
error: {
|
|
1733
|
+
code: "ucp_misconfigured",
|
|
1734
|
+
message: "Merchant has no configured payment handlers."
|
|
1735
|
+
},
|
|
1736
|
+
next_steps: {
|
|
1737
|
+
action: "contact_merchant",
|
|
1738
|
+
user_message: "This merchant is temporarily unable to accept agent payments."
|
|
1739
|
+
},
|
|
1740
|
+
agent_instructions: {
|
|
1741
|
+
action: "contact_merchant",
|
|
1742
|
+
steps: [
|
|
1743
|
+
"Surface a transient error to the user.",
|
|
1744
|
+
"Retry later; the merchant operator will repair the configuration."
|
|
1745
|
+
],
|
|
1746
|
+
user_message: "Merchant temporarily offline for agent payments."
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
const headers = {
|
|
1750
|
+
"Access-Control-Allow-Origin": "*",
|
|
1751
|
+
"Cache-Control": `public, max-age=${UCP_CACHE_SECONDS}`
|
|
1752
|
+
};
|
|
1753
|
+
attachRequestId(headers, requestHeaders);
|
|
1754
|
+
return {
|
|
1755
|
+
body: JSON.stringify(body),
|
|
1756
|
+
mediaType: "application/json",
|
|
1757
|
+
headers,
|
|
1758
|
+
status: 503
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
async function buildSignedUcpResponse(opts) {
|
|
1762
|
+
const {
|
|
1763
|
+
checkout,
|
|
1764
|
+
name,
|
|
1765
|
+
wellKnownUcpUrl,
|
|
1766
|
+
services,
|
|
1767
|
+
requestHeaders,
|
|
1768
|
+
signingKid = "merchant-default",
|
|
1769
|
+
agentscoreGate
|
|
1770
|
+
} = opts;
|
|
1771
|
+
const handlers = composeHandlers(checkout);
|
|
1772
|
+
if (Object.keys(handlers).length === 0) {
|
|
1773
|
+
return misconfiguredResponse(requestHeaders);
|
|
1774
|
+
}
|
|
1775
|
+
const key = await loadUCPSigningKeyFromEnv({ defaultKid: signingKid });
|
|
1776
|
+
const signingKeyEntry = UCPSigningKey.fromJWK(key.publicJWK);
|
|
1777
|
+
const profile = buildUCPProfile({
|
|
1778
|
+
name,
|
|
1779
|
+
supported_versions: { "2026-04-08": wellKnownUcpUrl },
|
|
1780
|
+
agentscore_gate: agentscoreGate,
|
|
1781
|
+
services,
|
|
1782
|
+
payment_handlers: handlers,
|
|
1783
|
+
signing_keys: [signingKeyEntry]
|
|
1784
|
+
});
|
|
1785
|
+
const signed = await signUCPProfile(profile, {
|
|
1786
|
+
signingKey: key.privateKey,
|
|
1787
|
+
kid: key.publicJWK.kid,
|
|
1788
|
+
alg: key.publicJWK.alg ?? "EdDSA"
|
|
1789
|
+
});
|
|
1790
|
+
const headers = {
|
|
1791
|
+
"Cache-Control": `public, max-age=${UCP_CACHE_SECONDS}`,
|
|
1792
|
+
"Access-Control-Allow-Origin": "*"
|
|
1793
|
+
};
|
|
1794
|
+
attachRequestId(headers, requestHeaders);
|
|
1795
|
+
return {
|
|
1796
|
+
body: JSON.stringify(signed),
|
|
1797
|
+
mediaType: "application/json",
|
|
1798
|
+
headers,
|
|
1799
|
+
status: 200
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
async function buildSignedJwksResponse(opts) {
|
|
1803
|
+
const { requestHeaders, signingKid = "merchant-default" } = opts ?? {};
|
|
1804
|
+
const key = await loadUCPSigningKeyFromEnv({ defaultKid: signingKid });
|
|
1805
|
+
const jwks = buildJWKSResponse([UCPSigningKey.fromJWK(key.publicJWK)]);
|
|
1806
|
+
const headers = {
|
|
1807
|
+
"Cache-Control": `public, max-age=${JWKS_CACHE_SECONDS}`,
|
|
1808
|
+
"Access-Control-Allow-Origin": "*"
|
|
1809
|
+
};
|
|
1810
|
+
attachRequestId(headers, requestHeaders);
|
|
1811
|
+
return {
|
|
1812
|
+
body: JSON.stringify(jwks),
|
|
1813
|
+
mediaType: "application/jwk-set+json",
|
|
1814
|
+
headers,
|
|
1815
|
+
status: 200
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
function wellKnownCorsPreflightHeaders(requestHeaders) {
|
|
1819
|
+
const headers = {
|
|
1820
|
+
"Access-Control-Allow-Origin": "*",
|
|
1821
|
+
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
|
1822
|
+
"Access-Control-Max-Age": "86400",
|
|
1823
|
+
Vary: "Access-Control-Request-Headers"
|
|
1824
|
+
};
|
|
1825
|
+
if (requestHeaders === void 0) return headers;
|
|
1826
|
+
const acrh = requestHeaders instanceof Headers ? requestHeaders.get("access-control-request-headers") : Object.entries(requestHeaders).find(([k]) => k.toLowerCase() === "access-control-request-headers")?.[1];
|
|
1827
|
+
if (acrh) headers["Access-Control-Allow-Headers"] = acrh;
|
|
1828
|
+
return headers;
|
|
1829
|
+
}
|
|
1830
|
+
function wellKnownPreflightResponse(requestHeaders) {
|
|
1831
|
+
return new Response(null, {
|
|
1832
|
+
status: 204,
|
|
1833
|
+
headers: wellKnownCorsPreflightHeaders(requestHeaders)
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
function defaultA2aServices(opts) {
|
|
1837
|
+
return {
|
|
1838
|
+
"dev.ucp.shopping": [
|
|
1839
|
+
{
|
|
1840
|
+
version: "2026-04-08",
|
|
1841
|
+
spec: UCP_SHOPPING_SPEC_2026_04_08,
|
|
1842
|
+
transport: "a2a",
|
|
1843
|
+
endpoint: opts.agentCardUrl
|
|
1844
|
+
}
|
|
1845
|
+
]
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
async function bootstrapUcpSigningKey(opts) {
|
|
1849
|
+
const defaultKid = opts?.defaultKid ?? "merchant-default";
|
|
1850
|
+
await loadUCPSigningKeyFromEnv({ defaultKid });
|
|
1851
|
+
}
|
|
1852
|
+
function signedResponseHono(resp) {
|
|
1853
|
+
return new Response(resp.body, {
|
|
1854
|
+
status: resp.status,
|
|
1855
|
+
headers: { ...resp.headers, "Content-Type": resp.mediaType }
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
function signedResponseNextjs(resp) {
|
|
1859
|
+
return signedResponseHono(resp);
|
|
1860
|
+
}
|
|
1861
|
+
function signedResponseWeb(resp) {
|
|
1862
|
+
return signedResponseHono(resp);
|
|
1863
|
+
}
|
|
1864
|
+
function signedResponseExpress(res, resp) {
|
|
1865
|
+
res.status(resp.status);
|
|
1866
|
+
res.set(resp.headers);
|
|
1867
|
+
res.type(resp.mediaType);
|
|
1868
|
+
res.send(resp.body);
|
|
1869
|
+
}
|
|
1870
|
+
function signedResponseFastify(reply, resp) {
|
|
1871
|
+
reply.code(resp.status);
|
|
1872
|
+
for (const [k, v] of Object.entries(resp.headers)) reply.header(k, v);
|
|
1873
|
+
reply.type(resp.mediaType);
|
|
1874
|
+
return reply.send(resp.body);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/discovery/request_id.ts
|
|
1878
|
+
function echoRequestIdHeaderHono() {
|
|
1879
|
+
return async (c, next) => {
|
|
1880
|
+
await next();
|
|
1881
|
+
const id = c.get("requestId");
|
|
1882
|
+
if (id) c.header("X-Request-ID", id);
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
872
1885
|
// Annotate the CommonJS export names for ESM import in node:
|
|
873
1886
|
0 && (module.exports = {
|
|
1887
|
+
PURCHASE_MODE_NOTES,
|
|
874
1888
|
agentscoreDenialSchemas,
|
|
875
1889
|
agentscoreOpenApiSnippets,
|
|
876
1890
|
agentscorePaymentRequiredSchema,
|
|
877
1891
|
agentscoreSecuritySchemes,
|
|
878
1892
|
applyNoindexHeader,
|
|
1893
|
+
bootstrapUcpSigningKey,
|
|
1894
|
+
buildAgentscoreOnboardingSteps,
|
|
879
1895
|
buildDiscoveryProbeResponse,
|
|
880
1896
|
buildLlmsTxt,
|
|
1897
|
+
buildMerchantIndexJson,
|
|
1898
|
+
buildRedemptionSkillMd,
|
|
1899
|
+
buildSignedJwksResponse,
|
|
1900
|
+
buildSignedUcpResponse,
|
|
881
1901
|
buildSkillMd,
|
|
1902
|
+
buildSuccessNextSteps,
|
|
882
1903
|
buildWellKnownMpp,
|
|
883
1904
|
buildWellKnownX402,
|
|
884
1905
|
compatibleClientsByRails,
|
|
885
1906
|
createBazaarDiscovery,
|
|
1907
|
+
defaultA2aServices,
|
|
886
1908
|
defaultDiscoveryPaths,
|
|
1909
|
+
echoRequestIdHeaderHono,
|
|
887
1910
|
isDiscoveryPath,
|
|
888
1911
|
isDiscoveryProbeRequest,
|
|
889
1912
|
llmsTxtIdentitySection,
|
|
@@ -891,10 +1914,21 @@ function buildSkillMd(input) {
|
|
|
891
1914
|
noindexNonDiscoveryPaths,
|
|
892
1915
|
noindexNonDiscoveryPathsExpress,
|
|
893
1916
|
noindexNonDiscoveryPathsFastify,
|
|
1917
|
+
purchaseModeNote,
|
|
894
1918
|
sampleX402AcceptForNetwork,
|
|
1919
|
+
signedResponseExpress,
|
|
1920
|
+
signedResponseFastify,
|
|
1921
|
+
signedResponseHono,
|
|
1922
|
+
signedResponseNextjs,
|
|
1923
|
+
signedResponseWeb,
|
|
895
1924
|
siwxSecurityScheme,
|
|
1925
|
+
standardEndpointDescriptions,
|
|
1926
|
+
wellKnownCorsPreflightHeaders,
|
|
1927
|
+
wellKnownPreflightResponse,
|
|
896
1928
|
wrapNoindexResponse,
|
|
897
1929
|
xGuidanceExtension,
|
|
898
|
-
xPaymentInfoExtension
|
|
1930
|
+
xPaymentInfoExtension,
|
|
1931
|
+
xPaymentInfoFromCheckout,
|
|
1932
|
+
xServiceInfoExtension
|
|
899
1933
|
});
|
|
900
1934
|
//# sourceMappingURL=index.js.map
|