opentool 0.7.2 → 0.7.3
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 +42 -15
- package/dist/cli/index.d.ts +4 -3
- package/dist/cli/index.js +264 -669
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +595 -865
- package/dist/index.js.map +1 -1
- package/dist/validate-BrOwtVYW.d.ts +382 -0
- package/dist/x402/index.d.ts +154 -0
- package/dist/x402/index.js +708 -0
- package/dist/x402/index.js.map +1 -0
- package/package.json +4 -4
- package/dist/index-D_bCF2Bf.d.ts +0 -487
- package/dist/payment/index.d.ts +0 -2
- package/dist/payment/index.js +0 -969
- package/dist/payment/index.js.map +0 -1
- package/dist/validate-CqB2Juma.d.ts +0 -216
package/dist/cli/index.js
CHANGED
|
@@ -8,10 +8,13 @@ import { z } from 'zod';
|
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
10
10
|
import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
|
|
11
|
+
import 'viem';
|
|
12
|
+
import 'viem/accounts';
|
|
13
|
+
import 'viem/chains';
|
|
11
14
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
15
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
16
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
-
import * as
|
|
17
|
+
import * as http2 from 'http';
|
|
15
18
|
|
|
16
19
|
function resolveTsconfig(projectRoot) {
|
|
17
20
|
const candidate = path5.join(projectRoot, "tsconfig.json");
|
|
@@ -88,23 +91,45 @@ var McpAnnotationsSchema = z.object({
|
|
|
88
91
|
openWorldHint: z.boolean().optional(),
|
|
89
92
|
requiresPayment: z.boolean().optional()
|
|
90
93
|
}).strict();
|
|
91
|
-
var
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
var X402PaymentSchema = z.object({
|
|
95
|
+
definition: z.object({
|
|
96
|
+
amount: z.string(),
|
|
97
|
+
currency: z.object({
|
|
98
|
+
code: z.string(),
|
|
99
|
+
symbol: z.string(),
|
|
100
|
+
decimals: z.number()
|
|
101
|
+
}),
|
|
102
|
+
asset: z.object({
|
|
103
|
+
symbol: z.string(),
|
|
104
|
+
network: z.string(),
|
|
105
|
+
address: z.string(),
|
|
106
|
+
decimals: z.number()
|
|
107
|
+
}),
|
|
108
|
+
payTo: z.string(),
|
|
109
|
+
resource: z.string().optional(),
|
|
110
|
+
description: z.string().optional(),
|
|
111
|
+
scheme: z.string(),
|
|
112
|
+
network: z.string(),
|
|
113
|
+
facilitator: z.object({
|
|
114
|
+
url: z.string(),
|
|
115
|
+
verifyPath: z.string().optional(),
|
|
116
|
+
settlePath: z.string().optional(),
|
|
117
|
+
apiKeyHeader: z.string().optional()
|
|
118
|
+
}),
|
|
119
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
120
|
+
}),
|
|
121
|
+
metadata: z.record(z.string(), z.unknown()).optional()
|
|
122
|
+
}).passthrough();
|
|
123
|
+
var PaymentConfigSchema = z.union([
|
|
124
|
+
X402PaymentSchema,
|
|
125
|
+
z.record(z.string(), z.unknown())
|
|
126
|
+
]);
|
|
101
127
|
var DiscoveryMetadataSchema = z.object({
|
|
102
128
|
keywords: z.array(z.string()).optional(),
|
|
103
129
|
category: z.string().optional(),
|
|
104
130
|
useCases: z.array(z.string()).optional(),
|
|
105
131
|
capabilities: z.array(z.string()).optional(),
|
|
106
132
|
requirements: z.record(z.string(), z.any()).optional(),
|
|
107
|
-
pricing: z.record(z.string(), z.any()).optional(),
|
|
108
133
|
compatibility: z.record(z.string(), z.any()).optional(),
|
|
109
134
|
documentation: z.union([z.string(), z.array(z.string())]).optional()
|
|
110
135
|
}).catchall(z.any());
|
|
@@ -125,7 +150,7 @@ var ToolSchema = z.object({
|
|
|
125
150
|
discovery: DiscoveryMetadataSchema.optional(),
|
|
126
151
|
chains: z.array(z.union([z.string(), z.number()])).optional()
|
|
127
152
|
}).strict();
|
|
128
|
-
var
|
|
153
|
+
var MetadataSchema = z.object({
|
|
129
154
|
metadataSpecVersion: z.string().optional(),
|
|
130
155
|
name: z.string().optional(),
|
|
131
156
|
displayName: z.string().optional(),
|
|
@@ -149,11 +174,10 @@ var AuthoredMetadataSchema = z.object({
|
|
|
149
174
|
useCases: z.array(z.string()).optional(),
|
|
150
175
|
capabilities: z.array(z.string()).optional(),
|
|
151
176
|
requirements: z.record(z.string(), z.any()).optional(),
|
|
152
|
-
pricing: z.record(z.string(), z.any()).optional(),
|
|
153
177
|
compatibility: z.record(z.string(), z.any()).optional(),
|
|
154
178
|
chains: z.array(z.union([z.string(), z.number()])).optional()
|
|
155
179
|
}).catchall(z.any());
|
|
156
|
-
var
|
|
180
|
+
var BuildMetadataSchema = z.object({
|
|
157
181
|
metadataSpecVersion: z.string().default(METADATA_SPEC_VERSION),
|
|
158
182
|
name: z.string(),
|
|
159
183
|
displayName: z.string(),
|
|
@@ -189,7 +213,7 @@ async function importFresh(modulePath) {
|
|
|
189
213
|
|
|
190
214
|
// src/cli/shared/metadata.ts
|
|
191
215
|
var METADATA_ENTRY = "metadata.ts";
|
|
192
|
-
async function
|
|
216
|
+
async function loadMetadata(projectRoot) {
|
|
193
217
|
const absPath = path5.join(projectRoot, METADATA_ENTRY);
|
|
194
218
|
if (!fs4.existsSync(absPath)) {
|
|
195
219
|
throw new Error(
|
|
@@ -210,7 +234,7 @@ async function loadAuthoredMetadata(projectRoot) {
|
|
|
210
234
|
const compiledPath = resolveCompiledPath(outDir, METADATA_ENTRY);
|
|
211
235
|
const moduleExports = await importFresh(compiledPath);
|
|
212
236
|
const metadataExport = extractMetadataExport(moduleExports);
|
|
213
|
-
const parsed =
|
|
237
|
+
const parsed = MetadataSchema.parse(metadataExport);
|
|
214
238
|
return { metadata: parsed, sourcePath: absPath };
|
|
215
239
|
} finally {
|
|
216
240
|
cleanup();
|
|
@@ -251,7 +275,7 @@ function readPackageJson(projectRoot) {
|
|
|
251
275
|
async function buildMetadataArtifact(options) {
|
|
252
276
|
const projectRoot = options.projectRoot;
|
|
253
277
|
const packageInfo = readPackageJson(projectRoot);
|
|
254
|
-
const { metadata: authored, sourcePath } = await
|
|
278
|
+
const { metadata: authored, sourcePath } = await loadMetadata(projectRoot);
|
|
255
279
|
const defaultsApplied = [];
|
|
256
280
|
const folderName = path5.basename(projectRoot);
|
|
257
281
|
const name = resolveField(
|
|
@@ -296,7 +320,7 @@ async function buildMetadataArtifact(options) {
|
|
|
296
320
|
if (!authored.website && packageInfo.homepage) {
|
|
297
321
|
defaultsApplied.push("website \u2192 package.json homepage");
|
|
298
322
|
}
|
|
299
|
-
const payment = resolvePayment(authored
|
|
323
|
+
const payment = resolvePayment(authored);
|
|
300
324
|
const baseImage = authored.image ?? authored.iconPath;
|
|
301
325
|
const animation = authored.animation_url ?? authored.videoPath;
|
|
302
326
|
const discovery = buildDiscovery(authored);
|
|
@@ -329,7 +353,7 @@ async function buildMetadataArtifact(options) {
|
|
|
329
353
|
}
|
|
330
354
|
return toolDefinition;
|
|
331
355
|
});
|
|
332
|
-
const metadata =
|
|
356
|
+
const metadata = BuildMetadataSchema.parse({
|
|
333
357
|
metadataSpecVersion: authored.metadataSpecVersion ?? METADATA_SPEC_VERSION,
|
|
334
358
|
name,
|
|
335
359
|
displayName,
|
|
@@ -385,32 +409,8 @@ function extractRepository(repository) {
|
|
|
385
409
|
}
|
|
386
410
|
return repository.url;
|
|
387
411
|
}
|
|
388
|
-
function resolvePayment(authored,
|
|
389
|
-
|
|
390
|
-
return authored.payment;
|
|
391
|
-
}
|
|
392
|
-
const discoveryPricing = authored.discovery?.pricing;
|
|
393
|
-
const legacyPricing = authored.pricing;
|
|
394
|
-
const pricing = discoveryPricing ?? legacyPricing;
|
|
395
|
-
if (!pricing) {
|
|
396
|
-
return void 0;
|
|
397
|
-
}
|
|
398
|
-
const amount = typeof pricing.defaultAmount === "number" ? pricing.defaultAmount : 0;
|
|
399
|
-
const sourceLabel = discoveryPricing ? "discovery.pricing.defaultAmount" : "pricing.defaultAmount";
|
|
400
|
-
defaults.push(`payment \u2192 synthesized from ${sourceLabel}`);
|
|
401
|
-
const acceptedMethodsRaw = Array.isArray(pricing.acceptedMethods) ? pricing.acceptedMethods : ["402"];
|
|
402
|
-
const acceptedMethods = acceptedMethodsRaw.map(
|
|
403
|
-
(method) => method === "x402" ? "x402" : "402"
|
|
404
|
-
);
|
|
405
|
-
return {
|
|
406
|
-
amountUSDC: amount,
|
|
407
|
-
description: typeof pricing.description === "string" ? pricing.description : void 0,
|
|
408
|
-
x402: acceptedMethods.includes("x402"),
|
|
409
|
-
plain402: acceptedMethods.includes("402"),
|
|
410
|
-
acceptedMethods,
|
|
411
|
-
acceptedCurrencies: Array.isArray(pricing.acceptedCurrencies) ? pricing.acceptedCurrencies : ["USDC"],
|
|
412
|
-
chains: Array.isArray(pricing.chains) ? pricing.chains : [8453]
|
|
413
|
-
};
|
|
412
|
+
function resolvePayment(authored, _defaults) {
|
|
413
|
+
return authored.payment ?? void 0;
|
|
414
414
|
}
|
|
415
415
|
function buildDiscovery(authored) {
|
|
416
416
|
const legacyDiscovery = {};
|
|
@@ -432,145 +432,14 @@ function buildDiscovery(authored) {
|
|
|
432
432
|
if (Array.isArray(authored.categories) && authored.categories.length > 0) {
|
|
433
433
|
legacyDiscovery.category = authored.categories[0];
|
|
434
434
|
}
|
|
435
|
-
if (authored.pricing) {
|
|
436
|
-
legacyDiscovery.pricing = authored.pricing;
|
|
437
|
-
}
|
|
438
435
|
const merged = {
|
|
439
436
|
...legacyDiscovery,
|
|
440
437
|
...authored.discovery ?? {}
|
|
441
438
|
};
|
|
442
439
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
443
440
|
}
|
|
444
|
-
var
|
|
445
|
-
var paymentSchemaVersionSchema = z.literal(PAYMENT_SCHEMA_VERSION);
|
|
446
|
-
var decimalStringSchema = z.string().regex(/^(?:0|[1-9]\d*)(?:\.\d+)?$/, "Value must be a positive decimal string");
|
|
447
|
-
var currencySchema = z.object({
|
|
448
|
-
code: z.string().min(2).max(12).transform((value) => value.toUpperCase()),
|
|
449
|
-
symbol: z.string().min(1).max(6).optional(),
|
|
450
|
-
decimals: z.number().int().min(0).max(36).optional(),
|
|
451
|
-
kind: z.enum(["fiat", "crypto"]).default("crypto").optional(),
|
|
452
|
-
description: z.string().optional()
|
|
453
|
-
});
|
|
454
|
-
var paymentAmountSchema = z.object({
|
|
455
|
-
value: decimalStringSchema,
|
|
456
|
-
currency: currencySchema,
|
|
457
|
-
display: z.string().optional()
|
|
458
|
-
});
|
|
459
|
-
var cryptoAssetSchema = z.object({
|
|
460
|
-
symbol: z.string().min(2).max(12),
|
|
461
|
-
network: z.string().min(1).optional(),
|
|
462
|
-
chainId: z.number().int().min(0).optional(),
|
|
463
|
-
address: z.string().min(1).optional(),
|
|
464
|
-
decimals: z.number().int().min(0).max(36).optional(),
|
|
465
|
-
standard: z.enum(["erc20", "spl", "custom"]).default("erc20").optional(),
|
|
466
|
-
description: z.string().optional()
|
|
467
|
-
});
|
|
468
|
-
var facilitatorConfigSchema = z.object({
|
|
469
|
-
url: z.string().url(),
|
|
470
|
-
vendor: z.string().optional(),
|
|
471
|
-
verifyPath: z.string().default("/verify").optional(),
|
|
472
|
-
settlePath: z.string().default("/settle").optional(),
|
|
473
|
-
apiKey: z.string().optional(),
|
|
474
|
-
apiKeyEnv: z.string().optional(),
|
|
475
|
-
apiKeyHeader: z.string().default("Authorization").optional(),
|
|
476
|
-
headers: z.record(z.string(), z.string()).optional(),
|
|
477
|
-
timeoutMs: z.number().int().positive().optional()
|
|
478
|
-
});
|
|
479
|
-
var settlementTermsSchema = z.object({
|
|
480
|
-
windowSeconds: z.number().int().positive().optional(),
|
|
481
|
-
targetConfirmations: z.number().int().positive().optional(),
|
|
482
|
-
finalityDescription: z.string().optional(),
|
|
483
|
-
slaDescription: z.string().optional()
|
|
484
|
-
});
|
|
485
|
-
var paymentFieldSchema = z.object({
|
|
486
|
-
key: z.string().min(1),
|
|
487
|
-
label: z.string().min(1),
|
|
488
|
-
required: z.boolean().default(true).optional(),
|
|
489
|
-
description: z.string().optional(),
|
|
490
|
-
example: z.string().optional()
|
|
491
|
-
});
|
|
492
|
-
var x402ProofSchema = z.object({
|
|
493
|
-
mode: z.literal("x402"),
|
|
494
|
-
scheme: z.string().min(1),
|
|
495
|
-
network: z.string().min(1),
|
|
496
|
-
version: z.number().int().min(1).optional(),
|
|
497
|
-
facilitator: facilitatorConfigSchema.optional(),
|
|
498
|
-
verifier: z.string().optional()
|
|
499
|
-
});
|
|
500
|
-
var directProofSchema = z.object({
|
|
501
|
-
mode: z.literal("direct"),
|
|
502
|
-
proofTypes: z.array(z.string().min(1)).nonempty(),
|
|
503
|
-
verifier: z.string().optional(),
|
|
504
|
-
instructions: z.string().optional(),
|
|
505
|
-
fields: z.array(paymentFieldSchema).optional(),
|
|
506
|
-
allowsManualReview: z.boolean().optional()
|
|
507
|
-
});
|
|
508
|
-
var paymentProofSchema = z.discriminatedUnion("mode", [
|
|
509
|
-
x402ProofSchema,
|
|
510
|
-
directProofSchema
|
|
511
|
-
]);
|
|
512
|
-
var paymentOptionSchema = z.object({
|
|
513
|
-
id: z.string().min(1),
|
|
514
|
-
title: z.string().min(1),
|
|
515
|
-
description: z.string().optional(),
|
|
516
|
-
amount: paymentAmountSchema,
|
|
517
|
-
asset: cryptoAssetSchema,
|
|
518
|
-
payTo: z.string().min(1),
|
|
519
|
-
resource: z.string().url().optional(),
|
|
520
|
-
proof: paymentProofSchema,
|
|
521
|
-
settlement: settlementTermsSchema.optional(),
|
|
522
|
-
metadata: z.record(z.string(), z.unknown()).optional()
|
|
523
|
-
});
|
|
524
|
-
var paymentRequirementsSchema = z.object({
|
|
525
|
-
schemaVersion: paymentSchemaVersionSchema,
|
|
526
|
-
message: z.string().optional(),
|
|
527
|
-
title: z.string().optional(),
|
|
528
|
-
resource: z.string().url().optional(),
|
|
529
|
-
accepts: z.array(paymentOptionSchema).nonempty(),
|
|
530
|
-
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
531
|
-
fallbackText: z.string().optional()
|
|
532
|
-
});
|
|
533
|
-
var x402PaymentHeaderSchema = z.object({
|
|
534
|
-
x402Version: z.number().int().min(1),
|
|
535
|
-
scheme: z.string().min(1),
|
|
536
|
-
network: z.string().min(1),
|
|
537
|
-
payload: z.unknown(),
|
|
538
|
-
correlationId: z.string().optional()
|
|
539
|
-
});
|
|
540
|
-
var directPaymentPayloadSchema = z.object({
|
|
541
|
-
schemaVersion: z.literal(1),
|
|
542
|
-
optionId: z.string().min(1),
|
|
543
|
-
proofType: z.string().min(1),
|
|
544
|
-
payload: z.unknown(),
|
|
545
|
-
metadata: z.record(z.string(), z.unknown()).optional()
|
|
546
|
-
});
|
|
547
|
-
var paymentSuccessMetadataSchema = z.object({
|
|
548
|
-
optionId: z.string().min(1),
|
|
549
|
-
verifier: z.string().optional(),
|
|
550
|
-
txHash: z.string().optional(),
|
|
551
|
-
networkId: z.string().optional(),
|
|
552
|
-
amount: paymentAmountSchema.optional(),
|
|
553
|
-
settledAt: z.string().datetime().optional(),
|
|
554
|
-
payload: z.unknown().optional()
|
|
555
|
-
});
|
|
556
|
-
var paymentFailureSchema = z.object({
|
|
557
|
-
reason: z.string().min(1),
|
|
558
|
-
code: z.enum([
|
|
559
|
-
"verifier_not_found",
|
|
560
|
-
"verification_failed",
|
|
561
|
-
"invalid_payload",
|
|
562
|
-
"unsupported_option",
|
|
563
|
-
"missing_header",
|
|
564
|
-
"unknown"
|
|
565
|
-
]).default("unknown").optional(),
|
|
566
|
-
retryable: z.boolean().optional(),
|
|
567
|
-
detail: z.unknown().optional()
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
// src/helpers/payment.ts
|
|
571
|
-
var X402_VERSION_DEFAULT = 1;
|
|
441
|
+
var X402_VERSION = 1;
|
|
572
442
|
var HEADER_X402 = "X-PAYMENT";
|
|
573
|
-
var HEADER_DIRECT = "X-PAYMENT-PROOF";
|
|
574
443
|
var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
|
|
575
444
|
var x402RequirementSchema = z.object({
|
|
576
445
|
scheme: z.string().min(1),
|
|
@@ -585,272 +454,195 @@ var x402RequirementSchema = z.object({
|
|
|
585
454
|
maxTimeoutSeconds: z.number().int().positive().optional(),
|
|
586
455
|
extra: z.record(z.string(), z.unknown()).nullable().optional()
|
|
587
456
|
});
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
457
|
+
var x402PaymentHeaderSchema = z.object({
|
|
458
|
+
x402Version: z.number().int().positive(),
|
|
459
|
+
scheme: z.string().min(1),
|
|
460
|
+
network: z.string().min(1),
|
|
461
|
+
correlationId: z.string().optional(),
|
|
462
|
+
payload: z.unknown()
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// src/x402/helpers.ts
|
|
466
|
+
function createX402PaymentRequired(definition) {
|
|
467
|
+
const requirement = toX402Requirement(definition);
|
|
468
|
+
const body = {
|
|
469
|
+
schemaVersion: 1,
|
|
470
|
+
message: definition.description ?? "Payment required",
|
|
471
|
+
resource: definition.resource,
|
|
472
|
+
accepts: [
|
|
473
|
+
{
|
|
474
|
+
id: "x402",
|
|
475
|
+
title: `Pay ${definition.amount} ${definition.currency.code}`,
|
|
476
|
+
description: definition.description,
|
|
477
|
+
amount: {
|
|
478
|
+
value: definition.amount,
|
|
479
|
+
currency: {
|
|
480
|
+
code: definition.currency.code,
|
|
481
|
+
symbol: definition.currency.symbol,
|
|
482
|
+
decimals: definition.currency.decimals,
|
|
483
|
+
kind: "crypto"
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
asset: {
|
|
487
|
+
symbol: definition.asset.symbol,
|
|
488
|
+
network: definition.asset.network,
|
|
489
|
+
address: definition.asset.address,
|
|
490
|
+
decimals: definition.asset.decimals,
|
|
491
|
+
standard: "erc20"
|
|
492
|
+
},
|
|
493
|
+
payTo: definition.payTo,
|
|
494
|
+
resource: definition.resource,
|
|
495
|
+
proof: {
|
|
496
|
+
mode: "x402",
|
|
497
|
+
scheme: definition.scheme,
|
|
498
|
+
network: definition.network,
|
|
499
|
+
version: X402_VERSION,
|
|
500
|
+
facilitator: definition.facilitator,
|
|
501
|
+
verifier: "x402:facilitator"
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
],
|
|
505
|
+
metadata: definition.metadata ?? {},
|
|
506
|
+
x402: {
|
|
507
|
+
x402Version: X402_VERSION,
|
|
508
|
+
error: definition.description ?? "Payment required",
|
|
509
|
+
accepts: [requirement]
|
|
510
|
+
}
|
|
511
|
+
};
|
|
612
512
|
return new Response(JSON.stringify(body), {
|
|
613
|
-
...init,
|
|
614
513
|
status: 402,
|
|
615
|
-
headers
|
|
514
|
+
headers: {
|
|
515
|
+
"Content-Type": "application/json"
|
|
516
|
+
}
|
|
616
517
|
});
|
|
617
518
|
}
|
|
618
|
-
function
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
if (x402Header) {
|
|
623
|
-
const { attempt, failure } = parseX402Header(x402Header);
|
|
624
|
-
if (attempt) {
|
|
625
|
-
attempts.push(attempt);
|
|
626
|
-
} else if (failure) {
|
|
627
|
-
failures.push(failure);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
const directHeader = source.headers.get(HEADER_DIRECT);
|
|
631
|
-
if (directHeader) {
|
|
632
|
-
const { attempt, failure } = parseDirectHeader(directHeader);
|
|
633
|
-
if (attempt) {
|
|
634
|
-
attempts.push(attempt);
|
|
635
|
-
} else if (failure) {
|
|
636
|
-
failures.push(failure);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
if (attempts.length === 0 && failures.length === 0) {
|
|
640
|
-
failures.push(
|
|
641
|
-
paymentFailureSchema.parse({
|
|
642
|
-
reason: "No payment headers present",
|
|
643
|
-
code: "missing_header",
|
|
644
|
-
retryable: false
|
|
645
|
-
})
|
|
646
|
-
);
|
|
519
|
+
function extractX402Attempt(request) {
|
|
520
|
+
const raw = request.headers.get(HEADER_X402);
|
|
521
|
+
if (!raw) {
|
|
522
|
+
return null;
|
|
647
523
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
async function verifyPayment(options) {
|
|
651
|
-
const definition = paymentRequirementsSchema.parse(options.definition);
|
|
652
|
-
const attempts = options.attempts ? options.attempts : options.request ? extractPaymentAttempts(options.request).attempts : [];
|
|
653
|
-
if (attempts.length === 0) {
|
|
524
|
+
try {
|
|
525
|
+
const payload = decodeJson(raw, x402PaymentHeaderSchema);
|
|
654
526
|
return {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
reason: "No payment attempt found",
|
|
660
|
-
code: "missing_header",
|
|
661
|
-
retryable: false
|
|
662
|
-
})
|
|
527
|
+
type: "x402",
|
|
528
|
+
headerName: HEADER_X402,
|
|
529
|
+
raw,
|
|
530
|
+
payload
|
|
663
531
|
};
|
|
532
|
+
} catch {
|
|
533
|
+
return null;
|
|
664
534
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
535
|
+
}
|
|
536
|
+
async function verifyX402Payment(attempt, definition, options = {}) {
|
|
537
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
538
|
+
const facilitator = definition.facilitator;
|
|
539
|
+
const verifierUrl = new URL(
|
|
540
|
+
facilitator.verifyPath ?? "/verify",
|
|
541
|
+
ensureTrailingSlash(facilitator.url)
|
|
542
|
+
).toString();
|
|
543
|
+
const requirement = toX402Requirement(definition);
|
|
544
|
+
const headers = buildFacilitatorHeaders(facilitator);
|
|
545
|
+
try {
|
|
546
|
+
const verifyResponse = await fetchImpl(verifierUrl, {
|
|
547
|
+
method: "POST",
|
|
548
|
+
headers,
|
|
549
|
+
body: JSON.stringify({
|
|
550
|
+
x402Version: attempt.payload.x402Version,
|
|
551
|
+
paymentPayload: attempt.payload,
|
|
552
|
+
paymentRequirements: requirement
|
|
553
|
+
})
|
|
554
|
+
});
|
|
555
|
+
if (!verifyResponse.ok) {
|
|
556
|
+
return {
|
|
557
|
+
success: false,
|
|
558
|
+
failure: {
|
|
559
|
+
reason: `Facilitator verify request failed: ${verifyResponse.status}`,
|
|
560
|
+
code: "verification_failed"
|
|
561
|
+
}
|
|
562
|
+
};
|
|
669
563
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
definition
|
|
678
|
-
};
|
|
679
|
-
if (options.settle !== void 0) {
|
|
680
|
-
context2.settle = options.settle;
|
|
564
|
+
const verifyPayload = await verifyResponse.json();
|
|
565
|
+
if (!verifyPayload.isValid) {
|
|
566
|
+
return {
|
|
567
|
+
success: false,
|
|
568
|
+
failure: {
|
|
569
|
+
reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
|
|
570
|
+
code: "verification_failed"
|
|
681
571
|
}
|
|
682
|
-
return runFacilitatorVerifier({
|
|
683
|
-
...context2,
|
|
684
|
-
fetchImpl: options.fetchImpl ?? fetch
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
|
|
688
|
-
if (!verifier) {
|
|
689
|
-
return {
|
|
690
|
-
success: false,
|
|
691
|
-
optionId: option.id,
|
|
692
|
-
attemptType: attempt.type,
|
|
693
|
-
failure: paymentFailureSchema.parse({
|
|
694
|
-
reason: `No verifier registered for id: ${verifierId ?? "(missing)"}`,
|
|
695
|
-
code: "verifier_not_found",
|
|
696
|
-
retryable: false
|
|
697
|
-
})
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
const context = {
|
|
701
|
-
attempt,
|
|
702
|
-
option,
|
|
703
|
-
definition
|
|
704
572
|
};
|
|
705
|
-
if (options.settle !== void 0) {
|
|
706
|
-
context.settle = options.settle;
|
|
707
|
-
}
|
|
708
|
-
return verifier(context);
|
|
709
573
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
574
|
+
const responseHeaders = {};
|
|
575
|
+
if (options.settle) {
|
|
576
|
+
const settleUrl = new URL(
|
|
577
|
+
facilitator.settlePath ?? "/settle",
|
|
578
|
+
ensureTrailingSlash(facilitator.url)
|
|
579
|
+
).toString();
|
|
580
|
+
try {
|
|
581
|
+
const settleResponse = await fetchImpl(settleUrl, {
|
|
582
|
+
method: "POST",
|
|
583
|
+
headers,
|
|
584
|
+
body: JSON.stringify({
|
|
585
|
+
x402Version: attempt.payload.x402Version,
|
|
586
|
+
paymentPayload: attempt.payload,
|
|
587
|
+
paymentRequirements: requirement
|
|
723
588
|
})
|
|
724
|
-
};
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
589
|
+
});
|
|
590
|
+
if (settleResponse.ok) {
|
|
591
|
+
const settlePayload = await settleResponse.json();
|
|
592
|
+
if (settlePayload.txHash) {
|
|
593
|
+
responseHeaders[HEADER_PAYMENT_RESPONSE] = JSON.stringify({
|
|
594
|
+
settled: true,
|
|
595
|
+
txHash: settlePayload.txHash
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
} catch {
|
|
733
600
|
}
|
|
734
|
-
return verifier(context);
|
|
735
601
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
retryable: false
|
|
745
|
-
})
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
function createPaymentResponseHeader(metadata) {
|
|
749
|
-
const parsed = paymentSuccessMetadataSchema.parse(metadata);
|
|
750
|
-
return encodeJson(parsed);
|
|
751
|
-
}
|
|
752
|
-
function parseX402Header(value) {
|
|
753
|
-
try {
|
|
754
|
-
const payload = decodeJson(value, x402PaymentHeaderSchema);
|
|
755
|
-
return {
|
|
756
|
-
attempt: {
|
|
757
|
-
type: "x402",
|
|
758
|
-
headerName: HEADER_X402,
|
|
759
|
-
raw: value,
|
|
760
|
-
payload
|
|
602
|
+
const result = {
|
|
603
|
+
success: true,
|
|
604
|
+
metadata: {
|
|
605
|
+
optionId: "x402",
|
|
606
|
+
verifier: "x402:facilitator",
|
|
607
|
+
amount: definition.amount,
|
|
608
|
+
currency: definition.currency.code,
|
|
609
|
+
network: definition.network
|
|
761
610
|
}
|
|
762
611
|
};
|
|
612
|
+
if (Object.keys(responseHeaders).length > 0) {
|
|
613
|
+
result.responseHeaders = responseHeaders;
|
|
614
|
+
}
|
|
615
|
+
return result;
|
|
763
616
|
} catch (error) {
|
|
764
617
|
return {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
})
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
function parseDirectHeader(value) {
|
|
774
|
-
try {
|
|
775
|
-
const payload = decodeJson(value, directPaymentPayloadSchema);
|
|
776
|
-
return {
|
|
777
|
-
attempt: {
|
|
778
|
-
type: "direct",
|
|
779
|
-
headerName: HEADER_DIRECT,
|
|
780
|
-
raw: value,
|
|
781
|
-
payload
|
|
618
|
+
success: false,
|
|
619
|
+
failure: {
|
|
620
|
+
reason: error instanceof Error ? error.message : "Unknown error",
|
|
621
|
+
code: "verification_failed"
|
|
782
622
|
}
|
|
783
623
|
};
|
|
784
|
-
} catch (error) {
|
|
785
|
-
return {
|
|
786
|
-
failure: paymentFailureSchema.parse({
|
|
787
|
-
reason: `Invalid X-PAYMENT-PROOF header: ${error.message}`,
|
|
788
|
-
code: "invalid_payload",
|
|
789
|
-
retryable: false
|
|
790
|
-
})
|
|
791
|
-
};
|
|
792
624
|
}
|
|
793
625
|
}
|
|
794
|
-
function
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (attempt.type === "x402" && option.proof.mode === "x402") {
|
|
798
|
-
return option.proof.scheme === attempt.payload.scheme && option.proof.network === attempt.payload.network;
|
|
799
|
-
}
|
|
800
|
-
if (attempt.type === "direct" && option.proof.mode === "direct") {
|
|
801
|
-
return option.id === attempt.payload.optionId;
|
|
802
|
-
}
|
|
803
|
-
return false;
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
function resolveX402Version(options) {
|
|
807
|
-
const versions = [];
|
|
808
|
-
for (const option of options) {
|
|
809
|
-
if (option.proof.mode === "x402" && option.proof.version) {
|
|
810
|
-
versions.push(option.proof.version);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
return versions.length > 0 ? Math.max(...versions) : X402_VERSION_DEFAULT;
|
|
814
|
-
}
|
|
815
|
-
function toX402Requirement(option, fallbackResource, settlement) {
|
|
816
|
-
if (option.proof.mode !== "x402") {
|
|
817
|
-
return void 0;
|
|
818
|
-
}
|
|
819
|
-
const decimals = resolveDecimals(option);
|
|
820
|
-
const assetAddress = option.asset.address;
|
|
821
|
-
if (!assetAddress) {
|
|
822
|
-
throw new Error(
|
|
823
|
-
`x402 payment option '${option.id}' is missing asset.address`
|
|
824
|
-
);
|
|
825
|
-
}
|
|
826
|
-
const units = decimalToBaseUnits(option.amount.value, decimals);
|
|
626
|
+
function toX402Requirement(definition) {
|
|
627
|
+
const decimals = definition.asset.decimals;
|
|
628
|
+
const units = decimalToBaseUnits(definition.amount, decimals);
|
|
827
629
|
return x402RequirementSchema.parse({
|
|
828
|
-
scheme:
|
|
829
|
-
network:
|
|
630
|
+
scheme: definition.scheme,
|
|
631
|
+
network: definition.network,
|
|
830
632
|
maxAmountRequired: units,
|
|
831
|
-
asset:
|
|
832
|
-
payTo:
|
|
833
|
-
resource:
|
|
834
|
-
description:
|
|
835
|
-
|
|
633
|
+
asset: definition.asset.address,
|
|
634
|
+
payTo: definition.payTo,
|
|
635
|
+
resource: definition.resource,
|
|
636
|
+
description: definition.description,
|
|
637
|
+
mimeType: "application/json",
|
|
638
|
+
maxTimeoutSeconds: 900,
|
|
836
639
|
extra: {
|
|
837
|
-
symbol:
|
|
838
|
-
currencyCode:
|
|
640
|
+
symbol: definition.asset.symbol,
|
|
641
|
+
currencyCode: definition.currency.code,
|
|
839
642
|
decimals
|
|
840
643
|
}
|
|
841
644
|
});
|
|
842
645
|
}
|
|
843
|
-
function resolveDecimals(option) {
|
|
844
|
-
if (typeof option.asset.decimals === "number") {
|
|
845
|
-
return option.asset.decimals;
|
|
846
|
-
}
|
|
847
|
-
if (typeof option.amount.currency.decimals === "number") {
|
|
848
|
-
return option.amount.currency.decimals;
|
|
849
|
-
}
|
|
850
|
-
throw new Error(
|
|
851
|
-
`Payment option '${option.id}' must specify asset.decimals or currency.decimals`
|
|
852
|
-
);
|
|
853
|
-
}
|
|
854
646
|
function decimalToBaseUnits(value, decimals) {
|
|
855
647
|
const [whole, fraction = ""] = value.split(".");
|
|
856
648
|
const sanitizedFraction = fraction.slice(0, decimals);
|
|
@@ -864,10 +656,6 @@ function decodeJson(value, schema) {
|
|
|
864
656
|
const parsed = JSON.parse(json);
|
|
865
657
|
return schema.parse(parsed);
|
|
866
658
|
}
|
|
867
|
-
function encodeJson(value) {
|
|
868
|
-
const json = JSON.stringify(value);
|
|
869
|
-
return Buffer.from(json, "utf-8").toString("base64");
|
|
870
|
-
}
|
|
871
659
|
function normalizeBase64(input) {
|
|
872
660
|
if (/^[A-Za-z0-9+/=]+$/.test(input)) {
|
|
873
661
|
return input;
|
|
@@ -876,200 +664,25 @@ function normalizeBase64(input) {
|
|
|
876
664
|
const paddingNeeded = (4 - restored.length % 4) % 4;
|
|
877
665
|
return restored + "=".repeat(paddingNeeded);
|
|
878
666
|
}
|
|
879
|
-
|
|
880
|
-
attempt,
|
|
881
|
-
option,
|
|
882
|
-
definition,
|
|
883
|
-
settle,
|
|
884
|
-
fetchImpl
|
|
885
|
-
}) {
|
|
886
|
-
if (option.proof.mode !== "x402" || attempt.type !== "x402" || !option.proof.facilitator) {
|
|
887
|
-
return {
|
|
888
|
-
success: false,
|
|
889
|
-
optionId: option.id,
|
|
890
|
-
attemptType: attempt.type,
|
|
891
|
-
failure: paymentFailureSchema.parse({
|
|
892
|
-
reason: "Facilitator verifier invoked for non-x402 option",
|
|
893
|
-
code: "verification_failed",
|
|
894
|
-
retryable: false
|
|
895
|
-
})
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
const facilitator = option.proof.facilitator;
|
|
899
|
-
const verifierUrl = new URL(
|
|
900
|
-
facilitator.verifyPath ?? "/verify",
|
|
901
|
-
ensureTrailingSlash(facilitator.url)
|
|
902
|
-
).toString();
|
|
903
|
-
const requirement = toX402Requirement(option, definition.resource, option.settlement);
|
|
904
|
-
if (!requirement) {
|
|
905
|
-
return {
|
|
906
|
-
success: false,
|
|
907
|
-
optionId: option.id,
|
|
908
|
-
attemptType: attempt.type,
|
|
909
|
-
failure: paymentFailureSchema.parse({
|
|
910
|
-
reason: "Unable to derive x402 requirement for facilitator",
|
|
911
|
-
code: "verification_failed",
|
|
912
|
-
retryable: false
|
|
913
|
-
})
|
|
914
|
-
};
|
|
915
|
-
}
|
|
916
|
-
const headers = buildFacilitatorHeaders(facilitator);
|
|
917
|
-
const controller = facilitator.timeoutMs ? new AbortController() : void 0;
|
|
918
|
-
const timeout = facilitator.timeoutMs ? setTimeout(() => controller?.abort(), facilitator.timeoutMs) : void 0;
|
|
919
|
-
try {
|
|
920
|
-
const verifyResponse = await fetchImpl(verifierUrl, {
|
|
921
|
-
method: "POST",
|
|
922
|
-
headers,
|
|
923
|
-
body: JSON.stringify({
|
|
924
|
-
x402Version: attempt.payload.x402Version,
|
|
925
|
-
paymentHeader: attempt.raw,
|
|
926
|
-
paymentRequirements: requirement
|
|
927
|
-
}),
|
|
928
|
-
signal: controller?.signal ?? null
|
|
929
|
-
});
|
|
930
|
-
if (!verifyResponse.ok) {
|
|
931
|
-
return {
|
|
932
|
-
success: false,
|
|
933
|
-
optionId: option.id,
|
|
934
|
-
attemptType: attempt.type,
|
|
935
|
-
failure: paymentFailureSchema.parse({
|
|
936
|
-
reason: `Facilitator verify request failed: ${verifyResponse.status}`,
|
|
937
|
-
code: "verification_failed",
|
|
938
|
-
retryable: verifyResponse.status >= 500
|
|
939
|
-
})
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
const verifyPayload = await verifyResponse.json();
|
|
943
|
-
if (!verifyPayload.isValid) {
|
|
944
|
-
return {
|
|
945
|
-
success: false,
|
|
946
|
-
optionId: option.id,
|
|
947
|
-
attemptType: attempt.type,
|
|
948
|
-
failure: paymentFailureSchema.parse({
|
|
949
|
-
reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
|
|
950
|
-
code: "verification_failed",
|
|
951
|
-
retryable: false
|
|
952
|
-
})
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
if (!settle) {
|
|
956
|
-
return {
|
|
957
|
-
success: true,
|
|
958
|
-
optionId: option.id,
|
|
959
|
-
attemptType: attempt.type,
|
|
960
|
-
metadata: paymentSuccessMetadataSchema.parse({
|
|
961
|
-
optionId: option.id,
|
|
962
|
-
verifier: facilitator.vendor ?? "facilitator"
|
|
963
|
-
})
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
const settleUrl = new URL(
|
|
967
|
-
facilitator.settlePath ?? "/settle",
|
|
968
|
-
ensureTrailingSlash(facilitator.url)
|
|
969
|
-
).toString();
|
|
970
|
-
const settleResponse = await fetchImpl(settleUrl, {
|
|
971
|
-
method: "POST",
|
|
972
|
-
headers,
|
|
973
|
-
body: JSON.stringify({
|
|
974
|
-
x402Version: attempt.payload.x402Version,
|
|
975
|
-
paymentHeader: attempt.raw,
|
|
976
|
-
paymentRequirements: requirement
|
|
977
|
-
}),
|
|
978
|
-
signal: controller?.signal ?? null
|
|
979
|
-
});
|
|
980
|
-
if (!settleResponse.ok) {
|
|
981
|
-
return {
|
|
982
|
-
success: false,
|
|
983
|
-
optionId: option.id,
|
|
984
|
-
attemptType: attempt.type,
|
|
985
|
-
failure: paymentFailureSchema.parse({
|
|
986
|
-
reason: `Facilitator settle request failed: ${settleResponse.status}`,
|
|
987
|
-
code: "verification_failed",
|
|
988
|
-
retryable: settleResponse.status >= 500
|
|
989
|
-
})
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
const settlePayload = await settleResponse.json();
|
|
993
|
-
if (!settlePayload.success) {
|
|
994
|
-
return {
|
|
995
|
-
success: false,
|
|
996
|
-
optionId: option.id,
|
|
997
|
-
attemptType: attempt.type,
|
|
998
|
-
failure: paymentFailureSchema.parse({
|
|
999
|
-
reason: settlePayload.error ?? "Facilitator settlement failed",
|
|
1000
|
-
code: "verification_failed",
|
|
1001
|
-
retryable: false
|
|
1002
|
-
})
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
const metadata = paymentSuccessMetadataSchema.parse({
|
|
1006
|
-
optionId: option.id,
|
|
1007
|
-
verifier: facilitator.vendor ?? "facilitator",
|
|
1008
|
-
txHash: settlePayload.txHash ?? void 0,
|
|
1009
|
-
networkId: settlePayload.networkId ?? void 0
|
|
1010
|
-
});
|
|
1011
|
-
return {
|
|
1012
|
-
success: true,
|
|
1013
|
-
optionId: option.id,
|
|
1014
|
-
attemptType: attempt.type,
|
|
1015
|
-
metadata,
|
|
1016
|
-
responseHeaders: {
|
|
1017
|
-
[HEADER_PAYMENT_RESPONSE]: createPaymentResponseHeader(metadata)
|
|
1018
|
-
}
|
|
1019
|
-
};
|
|
1020
|
-
} catch (error) {
|
|
1021
|
-
return {
|
|
1022
|
-
success: false,
|
|
1023
|
-
optionId: option.id,
|
|
1024
|
-
attemptType: attempt.type,
|
|
1025
|
-
failure: paymentFailureSchema.parse({
|
|
1026
|
-
reason: `Facilitator request error: ${error.message}`,
|
|
1027
|
-
code: "verification_failed",
|
|
1028
|
-
retryable: false
|
|
1029
|
-
})
|
|
1030
|
-
};
|
|
1031
|
-
} finally {
|
|
1032
|
-
if (timeout) {
|
|
1033
|
-
clearTimeout(timeout);
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
function buildFacilitatorHeaders(config) {
|
|
667
|
+
function buildFacilitatorHeaders(facilitator) {
|
|
1038
668
|
const headers = {
|
|
1039
|
-
"
|
|
669
|
+
"Content-Type": "application/json"
|
|
1040
670
|
};
|
|
1041
|
-
if (
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1044
|
-
const apiKey = resolveFacilitatorApiKey(config);
|
|
1045
|
-
if (apiKey) {
|
|
1046
|
-
const headerName = config?.apiKeyHeader ?? "Authorization";
|
|
1047
|
-
headers[headerName] = apiKey;
|
|
671
|
+
if (facilitator.apiKeyHeader && process.env.X402_FACILITATOR_API_KEY) {
|
|
672
|
+
headers[facilitator.apiKeyHeader] = process.env.X402_FACILITATOR_API_KEY;
|
|
1048
673
|
}
|
|
1049
674
|
return headers;
|
|
1050
675
|
}
|
|
1051
|
-
function
|
|
1052
|
-
|
|
1053
|
-
return void 0;
|
|
1054
|
-
}
|
|
1055
|
-
if (config.apiKey) {
|
|
1056
|
-
return config.apiKey;
|
|
1057
|
-
}
|
|
1058
|
-
if (config.apiKeyEnv && typeof process !== "undefined") {
|
|
1059
|
-
return process.env?.[config.apiKeyEnv];
|
|
1060
|
-
}
|
|
1061
|
-
return void 0;
|
|
1062
|
-
}
|
|
1063
|
-
function ensureTrailingSlash(value) {
|
|
1064
|
-
return value.endsWith("/") ? value : `${value}/`;
|
|
676
|
+
function ensureTrailingSlash(url) {
|
|
677
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
1065
678
|
}
|
|
1066
679
|
|
|
1067
|
-
// src/
|
|
1068
|
-
var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.
|
|
1069
|
-
var
|
|
680
|
+
// src/x402/index.ts
|
|
681
|
+
var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.x402.context");
|
|
682
|
+
var X402PaymentRequiredError = class extends Error {
|
|
1070
683
|
constructor(response, verification) {
|
|
1071
|
-
super("Payment required");
|
|
1072
|
-
this.name = "
|
|
684
|
+
super("X402 Payment required");
|
|
685
|
+
this.name = "X402PaymentRequiredError";
|
|
1073
686
|
this.response = response;
|
|
1074
687
|
this.verification = verification;
|
|
1075
688
|
}
|
|
@@ -1086,6 +699,45 @@ function setPaymentContext(request, context) {
|
|
|
1086
699
|
request[PAYMENT_CONTEXT_SYMBOL] = context;
|
|
1087
700
|
}
|
|
1088
701
|
}
|
|
702
|
+
async function requireX402Payment(request, payment, options = {}) {
|
|
703
|
+
const definition = isX402Payment(payment) ? payment.definition : payment;
|
|
704
|
+
const attempt = extractX402Attempt(request);
|
|
705
|
+
if (!attempt) {
|
|
706
|
+
const response = createX402PaymentRequired(definition);
|
|
707
|
+
throw new X402PaymentRequiredError(response);
|
|
708
|
+
}
|
|
709
|
+
const verifyOptions = {};
|
|
710
|
+
if (options.settle !== void 0) {
|
|
711
|
+
verifyOptions.settle = options.settle;
|
|
712
|
+
}
|
|
713
|
+
if (options.fetchImpl !== void 0) {
|
|
714
|
+
verifyOptions.fetchImpl = options.fetchImpl;
|
|
715
|
+
}
|
|
716
|
+
const verification = await verifyX402Payment(attempt, definition, verifyOptions);
|
|
717
|
+
if (!verification.success || !verification.metadata) {
|
|
718
|
+
if (options.onFailure) {
|
|
719
|
+
return options.onFailure(verification);
|
|
720
|
+
}
|
|
721
|
+
const response = createX402PaymentRequired(definition);
|
|
722
|
+
throw new X402PaymentRequiredError(response, verification);
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
payment: verification.metadata,
|
|
726
|
+
headers: verification.responseHeaders ?? {},
|
|
727
|
+
result: verification
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function withX402Payment(handler, payment, options = {}) {
|
|
731
|
+
return async (request) => {
|
|
732
|
+
const verification = await requireX402Payment(request, payment, options);
|
|
733
|
+
if (verification instanceof Response) {
|
|
734
|
+
return verification;
|
|
735
|
+
}
|
|
736
|
+
setPaymentContext(request, verification);
|
|
737
|
+
const response = await Promise.resolve(handler(request));
|
|
738
|
+
return applyPaymentHeaders(response, verification.headers);
|
|
739
|
+
};
|
|
740
|
+
}
|
|
1089
741
|
function applyPaymentHeaders(response, headers) {
|
|
1090
742
|
const entries = Object.entries(headers ?? {});
|
|
1091
743
|
if (entries.length === 0) {
|
|
@@ -1108,64 +760,7 @@ function applyPaymentHeaders(response, headers) {
|
|
|
1108
760
|
headers: merged
|
|
1109
761
|
});
|
|
1110
762
|
}
|
|
1111
|
-
function
|
|
1112
|
-
return async (request) => {
|
|
1113
|
-
const verification = await requirePayment(request, payment, options);
|
|
1114
|
-
if (verification instanceof Response) {
|
|
1115
|
-
return verification;
|
|
1116
|
-
}
|
|
1117
|
-
setPaymentContext(request, verification);
|
|
1118
|
-
const response = await Promise.resolve(handler(request));
|
|
1119
|
-
return applyPaymentHeaders(response, verification.headers);
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
async function requirePayment(request, payment, options = {}) {
|
|
1123
|
-
const { definition, verifiers } = normalizePayment(payment);
|
|
1124
|
-
const mergedVerifiers = {
|
|
1125
|
-
...verifiers,
|
|
1126
|
-
...options.verifiers ?? {}
|
|
1127
|
-
};
|
|
1128
|
-
const verifyOptions = {
|
|
1129
|
-
definition,
|
|
1130
|
-
request
|
|
1131
|
-
};
|
|
1132
|
-
if (Object.keys(mergedVerifiers).length > 0) {
|
|
1133
|
-
verifyOptions.verifiers = mergedVerifiers;
|
|
1134
|
-
}
|
|
1135
|
-
if (options.settle !== void 0) {
|
|
1136
|
-
verifyOptions.settle = options.settle;
|
|
1137
|
-
}
|
|
1138
|
-
if (options.fetchImpl) {
|
|
1139
|
-
verifyOptions.fetchImpl = options.fetchImpl;
|
|
1140
|
-
}
|
|
1141
|
-
const verification = await verifyPayment(verifyOptions);
|
|
1142
|
-
if (!verification.success || !verification.metadata) {
|
|
1143
|
-
if (options.onFailure) {
|
|
1144
|
-
return options.onFailure(verification);
|
|
1145
|
-
}
|
|
1146
|
-
const response = paymentRequiredResponse(definition);
|
|
1147
|
-
throw new PaymentRequiredError(response, verification);
|
|
1148
|
-
}
|
|
1149
|
-
return {
|
|
1150
|
-
payment: verification.metadata,
|
|
1151
|
-
headers: verification.responseHeaders ?? {},
|
|
1152
|
-
optionId: verification.optionId,
|
|
1153
|
-
result: verification
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
function normalizePayment(payment) {
|
|
1157
|
-
if (isDefinedPayment(payment)) {
|
|
1158
|
-
return {
|
|
1159
|
-
definition: payment.definition,
|
|
1160
|
-
verifiers: payment.verifiers ?? {}
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
return {
|
|
1164
|
-
definition: payment,
|
|
1165
|
-
verifiers: {}
|
|
1166
|
-
};
|
|
1167
|
-
}
|
|
1168
|
-
function isDefinedPayment(value) {
|
|
763
|
+
function isX402Payment(value) {
|
|
1169
764
|
return !!value && typeof value === "object" && "definition" in value && value.definition !== void 0;
|
|
1170
765
|
}
|
|
1171
766
|
|
|
@@ -1186,7 +781,7 @@ function createMcpAdapter(options) {
|
|
|
1186
781
|
const response = await Promise.resolve(httpHandler(request));
|
|
1187
782
|
return await responseToToolResponse(response);
|
|
1188
783
|
} catch (error) {
|
|
1189
|
-
if (error instanceof
|
|
784
|
+
if (error instanceof X402PaymentRequiredError) {
|
|
1190
785
|
return await responseToToolResponse(error.response);
|
|
1191
786
|
}
|
|
1192
787
|
throw error;
|
|
@@ -1406,7 +1001,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
1406
1001
|
const entry = httpHandlers[index];
|
|
1407
1002
|
httpHandlers[index] = {
|
|
1408
1003
|
...entry,
|
|
1409
|
-
handler:
|
|
1004
|
+
handler: withX402Payment(entry.handler, paymentExport)
|
|
1410
1005
|
};
|
|
1411
1006
|
}
|
|
1412
1007
|
}
|
|
@@ -1419,11 +1014,11 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
1419
1014
|
...schema ? { schema } : {}
|
|
1420
1015
|
});
|
|
1421
1016
|
let metadataOverrides = toolModule.metadata ?? null;
|
|
1422
|
-
if (paymentExport
|
|
1017
|
+
if (paymentExport) {
|
|
1423
1018
|
if (metadataOverrides) {
|
|
1424
1019
|
metadataOverrides = {
|
|
1425
1020
|
...metadataOverrides,
|
|
1426
|
-
payment: metadataOverrides.payment ?? paymentExport
|
|
1021
|
+
payment: metadataOverrides.payment ?? paymentExport,
|
|
1427
1022
|
annotations: {
|
|
1428
1023
|
...metadataOverrides.annotations ?? {},
|
|
1429
1024
|
requiresPayment: metadataOverrides.annotations?.requiresPayment ?? true
|
|
@@ -1431,7 +1026,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
|
|
|
1431
1026
|
};
|
|
1432
1027
|
} else {
|
|
1433
1028
|
metadataOverrides = {
|
|
1434
|
-
payment: paymentExport
|
|
1029
|
+
payment: paymentExport,
|
|
1435
1030
|
annotations: { requiresPayment: true }
|
|
1436
1031
|
};
|
|
1437
1032
|
}
|
|
@@ -2119,7 +1714,7 @@ Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`
|
|
|
2119
1714
|
});
|
|
2120
1715
|
}
|
|
2121
1716
|
}
|
|
2122
|
-
const server =
|
|
1717
|
+
const server = http2.createServer(async (req, res) => {
|
|
2123
1718
|
const method = (req.method || "GET").toUpperCase();
|
|
2124
1719
|
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
2125
1720
|
const routePath = url.pathname;
|
|
@@ -2309,7 +1904,7 @@ async function handleRequest(params) {
|
|
|
2309
1904
|
try {
|
|
2310
1905
|
response = await route.handler(request);
|
|
2311
1906
|
} catch (error) {
|
|
2312
|
-
if (error instanceof
|
|
1907
|
+
if (error instanceof X402PaymentRequiredError) {
|
|
2313
1908
|
response = error.response;
|
|
2314
1909
|
} else {
|
|
2315
1910
|
throw error;
|