opentool 0.7.2 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,9 +6,9 @@ import * as path5 from 'path';
6
6
  import { fileURLToPath, pathToFileURL } from 'url';
7
7
  import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
8
8
  import { z } from 'zod';
9
- import { zeroAddress, createPublicClient, http, createWalletClient } from 'viem';
10
- import { baseSepolia, mainnet, base } from 'viem/chains';
9
+ import { zeroAddress, createWalletClient, http, createPublicClient } from 'viem';
11
10
  import { privateKeyToAccount } from 'viem/accounts';
11
+ import { baseSepolia, mainnet, base } from 'viem/chains';
12
12
  import { Turnkey } from '@turnkey/sdk-server';
13
13
  import { createAccount } from '@turnkey/viem';
14
14
  import { tmpdir } from 'os';
@@ -21,136 +21,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
21
21
  if (typeof require !== "undefined") return require.apply(this, arguments);
22
22
  throw Error('Dynamic require of "' + x + '" is not supported');
23
23
  });
24
- var PAYMENT_SCHEMA_VERSION = 1;
25
- var paymentSchemaVersionSchema = z.literal(PAYMENT_SCHEMA_VERSION);
26
- var decimalStringSchema = z.string().regex(/^(?:0|[1-9]\d*)(?:\.\d+)?$/, "Value must be a positive decimal string");
27
- var currencySchema = z.object({
28
- code: z.string().min(2).max(12).transform((value) => value.toUpperCase()),
29
- symbol: z.string().min(1).max(6).optional(),
30
- decimals: z.number().int().min(0).max(36).optional(),
31
- kind: z.enum(["fiat", "crypto"]).default("crypto").optional(),
32
- description: z.string().optional()
33
- });
34
- var paymentAmountSchema = z.object({
35
- value: decimalStringSchema,
36
- currency: currencySchema,
37
- display: z.string().optional()
38
- });
39
- var cryptoAssetSchema = z.object({
40
- symbol: z.string().min(2).max(12),
41
- network: z.string().min(1).optional(),
42
- chainId: z.number().int().min(0).optional(),
43
- address: z.string().min(1).optional(),
44
- decimals: z.number().int().min(0).max(36).optional(),
45
- standard: z.enum(["erc20", "spl", "custom"]).default("erc20").optional(),
46
- description: z.string().optional()
47
- });
48
- var facilitatorConfigSchema = z.object({
49
- url: z.string().url(),
50
- vendor: z.string().optional(),
51
- verifyPath: z.string().default("/verify").optional(),
52
- settlePath: z.string().default("/settle").optional(),
53
- apiKey: z.string().optional(),
54
- apiKeyEnv: z.string().optional(),
55
- apiKeyHeader: z.string().default("Authorization").optional(),
56
- headers: z.record(z.string(), z.string()).optional(),
57
- timeoutMs: z.number().int().positive().optional()
58
- });
59
- var settlementTermsSchema = z.object({
60
- windowSeconds: z.number().int().positive().optional(),
61
- targetConfirmations: z.number().int().positive().optional(),
62
- finalityDescription: z.string().optional(),
63
- slaDescription: z.string().optional()
64
- });
65
- var paymentFieldSchema = z.object({
66
- key: z.string().min(1),
67
- label: z.string().min(1),
68
- required: z.boolean().default(true).optional(),
69
- description: z.string().optional(),
70
- example: z.string().optional()
71
- });
72
- var x402ProofSchema = z.object({
73
- mode: z.literal("x402"),
74
- scheme: z.string().min(1),
75
- network: z.string().min(1),
76
- version: z.number().int().min(1).optional(),
77
- facilitator: facilitatorConfigSchema.optional(),
78
- verifier: z.string().optional()
79
- });
80
- var directProofSchema = z.object({
81
- mode: z.literal("direct"),
82
- proofTypes: z.array(z.string().min(1)).nonempty(),
83
- verifier: z.string().optional(),
84
- instructions: z.string().optional(),
85
- fields: z.array(paymentFieldSchema).optional(),
86
- allowsManualReview: z.boolean().optional()
87
- });
88
- var paymentProofSchema = z.discriminatedUnion("mode", [
89
- x402ProofSchema,
90
- directProofSchema
91
- ]);
92
- var paymentOptionSchema = z.object({
93
- id: z.string().min(1),
94
- title: z.string().min(1),
95
- description: z.string().optional(),
96
- amount: paymentAmountSchema,
97
- asset: cryptoAssetSchema,
98
- payTo: z.string().min(1),
99
- resource: z.string().url().optional(),
100
- proof: paymentProofSchema,
101
- settlement: settlementTermsSchema.optional(),
102
- metadata: z.record(z.string(), z.unknown()).optional()
103
- });
104
- var paymentRequirementsSchema = z.object({
105
- schemaVersion: paymentSchemaVersionSchema,
106
- message: z.string().optional(),
107
- title: z.string().optional(),
108
- resource: z.string().url().optional(),
109
- accepts: z.array(paymentOptionSchema).nonempty(),
110
- metadata: z.record(z.string(), z.unknown()).optional(),
111
- fallbackText: z.string().optional()
112
- });
113
- var x402PaymentHeaderSchema = z.object({
114
- x402Version: z.number().int().min(1),
115
- scheme: z.string().min(1),
116
- network: z.string().min(1),
117
- payload: z.unknown(),
118
- correlationId: z.string().optional()
119
- });
120
- var directPaymentPayloadSchema = z.object({
121
- schemaVersion: z.literal(1),
122
- optionId: z.string().min(1),
123
- proofType: z.string().min(1),
124
- payload: z.unknown(),
125
- metadata: z.record(z.string(), z.unknown()).optional()
126
- });
127
- var paymentSuccessMetadataSchema = z.object({
128
- optionId: z.string().min(1),
129
- verifier: z.string().optional(),
130
- txHash: z.string().optional(),
131
- networkId: z.string().optional(),
132
- amount: paymentAmountSchema.optional(),
133
- settledAt: z.string().datetime().optional(),
134
- payload: z.unknown().optional()
135
- });
136
- var paymentFailureSchema = z.object({
137
- reason: z.string().min(1),
138
- code: z.enum([
139
- "verifier_not_found",
140
- "verification_failed",
141
- "invalid_payload",
142
- "unsupported_option",
143
- "missing_header",
144
- "unknown"
145
- ]).default("unknown").optional(),
146
- retryable: z.boolean().optional(),
147
- detail: z.unknown().optional()
148
- });
149
-
150
- // src/helpers/payment.ts
151
- var X402_VERSION_DEFAULT = 1;
24
+ var X402_VERSION = 1;
152
25
  var HEADER_X402 = "X-PAYMENT";
153
- var HEADER_DIRECT = "X-PAYMENT-PROOF";
154
26
  var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
155
27
  var x402RequirementSchema = z.object({
156
28
  scheme: z.string().min(1),
@@ -165,272 +37,209 @@ var x402RequirementSchema = z.object({
165
37
  maxTimeoutSeconds: z.number().int().positive().optional(),
166
38
  extra: z.record(z.string(), z.unknown()).nullable().optional()
167
39
  });
168
- function createPaymentRequiredBody(definition) {
169
- const parsed = paymentRequirementsSchema.parse(definition);
170
- const x402Accepts = parsed.accepts.filter((option) => option.proof.mode === "x402").map(
171
- (option) => toX402Requirement(option, parsed.resource, option.settlement)
172
- ).filter((value) => Boolean(value));
173
- const x402Body = x402Accepts.length > 0 ? {
174
- x402Version: resolveX402Version(parsed.accepts),
175
- error: parsed.message ?? "Payment required",
176
- accepts: x402Accepts
177
- } : void 0;
178
- if (x402Body) {
179
- return {
180
- ...parsed,
181
- x402: x402Body
182
- };
183
- }
184
- return parsed;
185
- }
186
- function paymentRequiredResponse(definition, init) {
187
- const body = createPaymentRequiredBody(definition);
188
- const headers = new Headers(init?.headers);
189
- if (!headers.has("content-type")) {
190
- headers.set("content-type", "application/json; charset=utf-8");
40
+ var x402PaymentHeaderSchema = z.object({
41
+ x402Version: z.number().int().positive(),
42
+ scheme: z.string().min(1),
43
+ network: z.string().min(1),
44
+ correlationId: z.string().optional(),
45
+ payload: z.unknown()
46
+ });
47
+ var SUPPORTED_CURRENCIES = {
48
+ USDC: {
49
+ decimals: 6,
50
+ symbol: "USDC",
51
+ network: "base",
52
+ assetAddress: "0x833589fCD6eDb6E08f4c7C37b7b4c6e997E08A43"
191
53
  }
54
+ };
55
+ var DEFAULT_FACILITATOR = {
56
+ url: "https://facilitator.x402.rs",
57
+ verifyPath: "/verify",
58
+ settlePath: "/settle",
59
+ apiKeyHeader: "Authorization"
60
+ };
61
+
62
+ // src/x402/helpers.ts
63
+ function createX402PaymentRequired(definition) {
64
+ const requirement = toX402Requirement(definition);
65
+ const body = {
66
+ schemaVersion: 1,
67
+ message: definition.description ?? "Payment required",
68
+ resource: definition.resource,
69
+ accepts: [
70
+ {
71
+ id: "x402",
72
+ title: `Pay ${definition.amount} ${definition.currency.code}`,
73
+ description: definition.description,
74
+ amount: {
75
+ value: definition.amount,
76
+ currency: {
77
+ code: definition.currency.code,
78
+ symbol: definition.currency.symbol,
79
+ decimals: definition.currency.decimals,
80
+ kind: "crypto"
81
+ }
82
+ },
83
+ asset: {
84
+ symbol: definition.asset.symbol,
85
+ network: definition.asset.network,
86
+ address: definition.asset.address,
87
+ decimals: definition.asset.decimals,
88
+ standard: "erc20"
89
+ },
90
+ payTo: definition.payTo,
91
+ resource: definition.resource,
92
+ proof: {
93
+ mode: "x402",
94
+ scheme: definition.scheme,
95
+ network: definition.network,
96
+ version: X402_VERSION,
97
+ facilitator: definition.facilitator,
98
+ verifier: "x402:facilitator"
99
+ }
100
+ }
101
+ ],
102
+ metadata: definition.metadata ?? {},
103
+ x402: {
104
+ x402Version: X402_VERSION,
105
+ error: definition.description ?? "Payment required",
106
+ accepts: [requirement]
107
+ }
108
+ };
192
109
  return new Response(JSON.stringify(body), {
193
- ...init,
194
- status: init?.status ?? 402,
195
- headers
110
+ status: 402,
111
+ headers: {
112
+ "Content-Type": "application/json"
113
+ }
196
114
  });
197
115
  }
198
- function extractPaymentAttempts(source) {
199
- const attempts = [];
200
- const failures = [];
201
- const x402Header = source.headers.get(HEADER_X402);
202
- if (x402Header) {
203
- const { attempt, failure } = parseX402Header(x402Header);
204
- if (attempt) {
205
- attempts.push(attempt);
206
- } else if (failure) {
207
- failures.push(failure);
208
- }
209
- }
210
- const directHeader = source.headers.get(HEADER_DIRECT);
211
- if (directHeader) {
212
- const { attempt, failure } = parseDirectHeader(directHeader);
213
- if (attempt) {
214
- attempts.push(attempt);
215
- } else if (failure) {
216
- failures.push(failure);
217
- }
218
- }
219
- if (attempts.length === 0 && failures.length === 0) {
220
- failures.push(
221
- paymentFailureSchema.parse({
222
- reason: "No payment headers present",
223
- code: "missing_header",
224
- retryable: false
225
- })
226
- );
116
+ function extractX402Attempt(request) {
117
+ const raw = request.headers.get(HEADER_X402);
118
+ if (!raw) {
119
+ return null;
227
120
  }
228
- return { attempts, failures };
229
- }
230
- async function verifyPayment(options) {
231
- const definition = paymentRequirementsSchema.parse(options.definition);
232
- const attempts = options.attempts ? options.attempts : options.request ? extractPaymentAttempts(options.request).attempts : [];
233
- if (attempts.length === 0) {
121
+ try {
122
+ const payload = decodeJson(raw, x402PaymentHeaderSchema);
234
123
  return {
235
- success: false,
236
- optionId: "",
237
- attemptType: "direct",
238
- failure: paymentFailureSchema.parse({
239
- reason: "No payment attempt found",
240
- code: "missing_header",
241
- retryable: false
242
- })
124
+ type: "x402",
125
+ headerName: HEADER_X402,
126
+ raw,
127
+ payload
243
128
  };
129
+ } catch {
130
+ return null;
244
131
  }
245
- for (const attempt of attempts) {
246
- const option = findMatchingOption(definition, attempt);
247
- if (!option) {
248
- continue;
132
+ }
133
+ async function verifyX402Payment(attempt, definition, options = {}) {
134
+ const fetchImpl = options.fetchImpl ?? fetch;
135
+ const facilitator = definition.facilitator;
136
+ const verifierUrl = new URL(
137
+ facilitator.verifyPath ?? "/verify",
138
+ ensureTrailingSlash(facilitator.url)
139
+ ).toString();
140
+ const requirement = toX402Requirement(definition);
141
+ const headers = buildFacilitatorHeaders(facilitator);
142
+ try {
143
+ const verifyResponse = await fetchImpl(verifierUrl, {
144
+ method: "POST",
145
+ headers,
146
+ body: JSON.stringify({
147
+ x402Version: attempt.payload.x402Version,
148
+ paymentPayload: attempt.payload,
149
+ paymentRequirements: requirement
150
+ })
151
+ });
152
+ if (!verifyResponse.ok) {
153
+ return {
154
+ success: false,
155
+ failure: {
156
+ reason: `Facilitator verify request failed: ${verifyResponse.status}`,
157
+ code: "verification_failed"
158
+ }
159
+ };
249
160
  }
250
- if (attempt.type === "x402") {
251
- const proof = option.proof;
252
- const verifierId = proof.verifier ?? (proof.facilitator ? "x402:facilitator" : void 0);
253
- if (verifierId === "x402:facilitator" && proof.facilitator) {
254
- const context2 = {
255
- attempt,
256
- option,
257
- definition
258
- };
259
- if (options.settle !== void 0) {
260
- context2.settle = options.settle;
161
+ const verifyPayload = await verifyResponse.json();
162
+ if (!verifyPayload.isValid) {
163
+ return {
164
+ success: false,
165
+ failure: {
166
+ reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
167
+ code: "verification_failed"
261
168
  }
262
- return runFacilitatorVerifier({
263
- ...context2,
264
- fetchImpl: options.fetchImpl ?? fetch
265
- });
266
- }
267
- const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
268
- if (!verifier) {
269
- return {
270
- success: false,
271
- optionId: option.id,
272
- attemptType: attempt.type,
273
- failure: paymentFailureSchema.parse({
274
- reason: `No verifier registered for id: ${verifierId ?? "(missing)"}`,
275
- code: "verifier_not_found",
276
- retryable: false
277
- })
278
- };
279
- }
280
- const context = {
281
- attempt,
282
- option,
283
- definition
284
169
  };
285
- if (options.settle !== void 0) {
286
- context.settle = options.settle;
287
- }
288
- return verifier(context);
289
170
  }
290
- if (attempt.type === "direct") {
291
- const proof = option.proof;
292
- const verifierId = proof.verifier ?? `direct:${attempt.payload.proofType}`;
293
- const verifier = verifierId ? options.verifiers?.[verifierId] : void 0;
294
- if (!verifier) {
295
- return {
296
- success: false,
297
- optionId: option.id,
298
- attemptType: attempt.type,
299
- failure: paymentFailureSchema.parse({
300
- reason: `No verifier registered for id: ${verifierId}`,
301
- code: "verifier_not_found",
302
- retryable: false
171
+ const responseHeaders = {};
172
+ if (options.settle) {
173
+ const settleUrl = new URL(
174
+ facilitator.settlePath ?? "/settle",
175
+ ensureTrailingSlash(facilitator.url)
176
+ ).toString();
177
+ try {
178
+ const settleResponse = await fetchImpl(settleUrl, {
179
+ method: "POST",
180
+ headers,
181
+ body: JSON.stringify({
182
+ x402Version: attempt.payload.x402Version,
183
+ paymentPayload: attempt.payload,
184
+ paymentRequirements: requirement
303
185
  })
304
- };
305
- }
306
- const context = {
307
- attempt,
308
- option,
309
- definition
310
- };
311
- if (options.settle !== void 0) {
312
- context.settle = options.settle;
186
+ });
187
+ if (settleResponse.ok) {
188
+ const settlePayload = await settleResponse.json();
189
+ if (settlePayload.txHash) {
190
+ responseHeaders[HEADER_PAYMENT_RESPONSE] = JSON.stringify({
191
+ settled: true,
192
+ txHash: settlePayload.txHash
193
+ });
194
+ }
195
+ }
196
+ } catch {
313
197
  }
314
- return verifier(context);
315
198
  }
316
- }
317
- return {
318
- success: false,
319
- optionId: "",
320
- attemptType: attempts[0]?.type ?? "direct",
321
- failure: paymentFailureSchema.parse({
322
- reason: "No matching payment option for attempt",
323
- code: "unsupported_option",
324
- retryable: false
325
- })
326
- };
327
- }
328
- function createPaymentResponseHeader(metadata) {
329
- const parsed = paymentSuccessMetadataSchema.parse(metadata);
330
- return encodeJson(parsed);
331
- }
332
- function parseX402Header(value) {
333
- try {
334
- const payload = decodeJson(value, x402PaymentHeaderSchema);
335
- return {
336
- attempt: {
337
- type: "x402",
338
- headerName: HEADER_X402,
339
- raw: value,
340
- payload
199
+ const result = {
200
+ success: true,
201
+ metadata: {
202
+ optionId: "x402",
203
+ verifier: "x402:facilitator",
204
+ amount: definition.amount,
205
+ currency: definition.currency.code,
206
+ network: definition.network
341
207
  }
342
208
  };
209
+ if (Object.keys(responseHeaders).length > 0) {
210
+ result.responseHeaders = responseHeaders;
211
+ }
212
+ return result;
343
213
  } catch (error) {
344
214
  return {
345
- failure: paymentFailureSchema.parse({
346
- reason: `Invalid X-PAYMENT header: ${error.message}`,
347
- code: "invalid_payload",
348
- retryable: false
349
- })
350
- };
351
- }
352
- }
353
- function parseDirectHeader(value) {
354
- try {
355
- const payload = decodeJson(value, directPaymentPayloadSchema);
356
- return {
357
- attempt: {
358
- type: "direct",
359
- headerName: HEADER_DIRECT,
360
- raw: value,
361
- payload
215
+ success: false,
216
+ failure: {
217
+ reason: error instanceof Error ? error.message : "Unknown error",
218
+ code: "verification_failed"
362
219
  }
363
220
  };
364
- } catch (error) {
365
- return {
366
- failure: paymentFailureSchema.parse({
367
- reason: `Invalid X-PAYMENT-PROOF header: ${error.message}`,
368
- code: "invalid_payload",
369
- retryable: false
370
- })
371
- };
372
221
  }
373
222
  }
374
- function findMatchingOption(definition, attempt) {
375
- return definition.accepts.find((candidate) => {
376
- const option = paymentOptionSchema.parse(candidate);
377
- if (attempt.type === "x402" && option.proof.mode === "x402") {
378
- return option.proof.scheme === attempt.payload.scheme && option.proof.network === attempt.payload.network;
379
- }
380
- if (attempt.type === "direct" && option.proof.mode === "direct") {
381
- return option.id === attempt.payload.optionId;
382
- }
383
- return false;
384
- });
385
- }
386
- function resolveX402Version(options) {
387
- const versions = [];
388
- for (const option of options) {
389
- if (option.proof.mode === "x402" && option.proof.version) {
390
- versions.push(option.proof.version);
391
- }
392
- }
393
- return versions.length > 0 ? Math.max(...versions) : X402_VERSION_DEFAULT;
394
- }
395
- function toX402Requirement(option, fallbackResource, settlement) {
396
- if (option.proof.mode !== "x402") {
397
- return void 0;
398
- }
399
- const decimals = resolveDecimals(option);
400
- const assetAddress = option.asset.address;
401
- if (!assetAddress) {
402
- throw new Error(
403
- `x402 payment option '${option.id}' is missing asset.address`
404
- );
405
- }
406
- const units = decimalToBaseUnits(option.amount.value, decimals);
223
+ function toX402Requirement(definition) {
224
+ const decimals = definition.asset.decimals;
225
+ const units = decimalToBaseUnits(definition.amount, decimals);
407
226
  return x402RequirementSchema.parse({
408
- scheme: option.proof.scheme,
409
- network: option.proof.network,
227
+ scheme: definition.scheme,
228
+ network: definition.network,
410
229
  maxAmountRequired: units,
411
- asset: assetAddress,
412
- payTo: option.payTo,
413
- resource: option.resource ?? fallbackResource,
414
- description: option.description,
415
- maxTimeoutSeconds: settlement?.windowSeconds,
230
+ asset: definition.asset.address,
231
+ payTo: definition.payTo,
232
+ resource: definition.resource,
233
+ description: definition.description,
234
+ mimeType: "application/json",
235
+ maxTimeoutSeconds: 900,
416
236
  extra: {
417
- symbol: option.asset.symbol,
418
- currencyCode: option.amount.currency.code,
237
+ symbol: definition.asset.symbol,
238
+ currencyCode: definition.currency.code,
419
239
  decimals
420
240
  }
421
241
  });
422
242
  }
423
- function resolveDecimals(option) {
424
- if (typeof option.asset.decimals === "number") {
425
- return option.asset.decimals;
426
- }
427
- if (typeof option.amount.currency.decimals === "number") {
428
- return option.amount.currency.decimals;
429
- }
430
- throw new Error(
431
- `Payment option '${option.id}' must specify asset.decimals or currency.decimals`
432
- );
433
- }
434
243
  function decimalToBaseUnits(value, decimals) {
435
244
  const [whole, fraction = ""] = value.split(".");
436
245
  const sanitizedFraction = fraction.slice(0, decimals);
@@ -444,10 +253,6 @@ function decodeJson(value, schema) {
444
253
  const parsed = JSON.parse(json);
445
254
  return schema.parse(parsed);
446
255
  }
447
- function encodeJson(value) {
448
- const json = JSON.stringify(value);
449
- return Buffer.from(json, "utf-8").toString("base64");
450
- }
451
256
  function normalizeBase64(input) {
452
257
  if (/^[A-Za-z0-9+/=]+$/.test(input)) {
453
258
  return input;
@@ -456,221 +261,313 @@ function normalizeBase64(input) {
456
261
  const paddingNeeded = (4 - restored.length % 4) % 4;
457
262
  return restored + "=".repeat(paddingNeeded);
458
263
  }
459
- async function runFacilitatorVerifier({
460
- attempt,
461
- option,
462
- definition,
463
- settle,
464
- fetchImpl
465
- }) {
466
- if (option.proof.mode !== "x402" || attempt.type !== "x402" || !option.proof.facilitator) {
467
- return {
468
- success: false,
469
- optionId: option.id,
470
- attemptType: attempt.type,
471
- failure: paymentFailureSchema.parse({
472
- reason: "Facilitator verifier invoked for non-x402 option",
473
- code: "verification_failed",
474
- retryable: false
475
- })
476
- };
477
- }
478
- const facilitator = option.proof.facilitator;
479
- const verifierUrl = new URL(
480
- facilitator.verifyPath ?? "/verify",
481
- ensureTrailingSlash(facilitator.url)
482
- ).toString();
483
- const requirement = toX402Requirement(option, definition.resource, option.settlement);
484
- if (!requirement) {
485
- return {
486
- success: false,
487
- optionId: option.id,
488
- attemptType: attempt.type,
489
- failure: paymentFailureSchema.parse({
490
- reason: "Unable to derive x402 requirement for facilitator",
491
- code: "verification_failed",
492
- retryable: false
493
- })
494
- };
264
+ function buildFacilitatorHeaders(facilitator) {
265
+ const headers = {
266
+ "Content-Type": "application/json"
267
+ };
268
+ if (facilitator.apiKeyHeader && process.env.X402_FACILITATOR_API_KEY) {
269
+ headers[facilitator.apiKeyHeader] = process.env.X402_FACILITATOR_API_KEY;
495
270
  }
496
- const headers = buildFacilitatorHeaders(facilitator);
497
- const controller = facilitator.timeoutMs ? new AbortController() : void 0;
498
- const timeout = facilitator.timeoutMs ? setTimeout(() => controller?.abort(), facilitator.timeoutMs) : void 0;
499
- try {
500
- const verifyResponse = await fetchImpl(verifierUrl, {
501
- method: "POST",
502
- headers,
503
- body: JSON.stringify({
504
- x402Version: attempt.payload.x402Version,
505
- paymentHeader: attempt.raw,
506
- paymentRequirements: requirement
507
- }),
508
- signal: controller?.signal ?? null
271
+ return headers;
272
+ }
273
+ function ensureTrailingSlash(url) {
274
+ return url.endsWith("/") ? url : `${url}/`;
275
+ }
276
+ var PAYMENT_HEADERS = [HEADER_X402, HEADER_PAYMENT_RESPONSE];
277
+ var X402Client = class {
278
+ constructor(config) {
279
+ this.account = privateKeyToAccount(config.privateKey);
280
+ const chain = baseSepolia;
281
+ this.walletClient = createWalletClient({
282
+ account: this.account,
283
+ chain,
284
+ transport: http(config.rpcUrl)
509
285
  });
510
- if (!verifyResponse.ok) {
511
- return {
512
- success: false,
513
- optionId: option.id,
514
- attemptType: attempt.type,
515
- failure: paymentFailureSchema.parse({
516
- reason: `Facilitator verify request failed: ${verifyResponse.status}`,
517
- code: "verification_failed",
518
- retryable: verifyResponse.status >= 500
519
- })
520
- };
521
- }
522
- const verifyPayload = await verifyResponse.json();
523
- if (!verifyPayload.isValid) {
524
- return {
525
- success: false,
526
- optionId: option.id,
527
- attemptType: attempt.type,
528
- failure: paymentFailureSchema.parse({
529
- reason: verifyPayload.invalidReason ?? "Facilitator verification failed",
530
- code: "verification_failed",
531
- retryable: false
532
- })
286
+ }
287
+ async pay(request) {
288
+ try {
289
+ const initialResponse = await fetch(request.url, {
290
+ method: request.method ?? "POST",
291
+ headers: {
292
+ "Content-Type": "application/json",
293
+ ...request.headers
294
+ },
295
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
296
+ });
297
+ if (initialResponse.status !== 402) {
298
+ return {
299
+ success: initialResponse.ok,
300
+ response: initialResponse
301
+ };
302
+ }
303
+ const paymentRequirements = await initialResponse.json();
304
+ const x402Requirements = paymentRequirements.x402?.accepts?.[0];
305
+ if (!x402Requirements) {
306
+ return {
307
+ success: false,
308
+ error: "No x402 payment requirements found in 402 response"
309
+ };
310
+ }
311
+ const authorization = await this.signTransferAuthorization({
312
+ from: this.account.address,
313
+ to: x402Requirements.payTo,
314
+ value: BigInt(x402Requirements.maxAmountRequired),
315
+ validAfter: BigInt(Math.floor(Date.now() / 1e3)),
316
+ validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
317
+ // 15 min
318
+ nonce: `0x${Array.from(
319
+ { length: 32 },
320
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
321
+ ).join("")}`,
322
+ tokenAddress: x402Requirements.asset
323
+ });
324
+ const paymentProof = {
325
+ x402Version: 1,
326
+ scheme: x402Requirements.scheme,
327
+ network: x402Requirements.network,
328
+ correlationId: "",
329
+ payload: {
330
+ signature: authorization.signature,
331
+ authorization: {
332
+ from: authorization.from,
333
+ to: authorization.to,
334
+ value: authorization.value.toString(),
335
+ validAfter: authorization.validAfter.toString(),
336
+ validBefore: authorization.validBefore.toString(),
337
+ nonce: authorization.nonce
338
+ }
339
+ }
533
340
  };
534
- }
535
- if (!settle) {
341
+ const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString("base64");
342
+ const paidResponse = await fetch(request.url, {
343
+ method: request.method ?? "POST",
344
+ headers: {
345
+ "Content-Type": "application/json",
346
+ "X-PAYMENT": paymentHeader,
347
+ ...request.headers
348
+ },
349
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
350
+ });
536
351
  return {
537
- success: true,
538
- optionId: option.id,
539
- attemptType: attempt.type,
540
- metadata: paymentSuccessMetadataSchema.parse({
541
- optionId: option.id,
542
- verifier: facilitator.vendor ?? "facilitator"
543
- })
352
+ success: paidResponse.ok,
353
+ response: paidResponse,
354
+ paymentDetails: {
355
+ amount: x402Requirements.maxAmountRequired,
356
+ currency: x402Requirements.extra?.currencyCode ?? "USDC",
357
+ network: x402Requirements.network,
358
+ signature: authorization.signature
359
+ }
544
360
  };
545
- }
546
- const settleUrl = new URL(
547
- facilitator.settlePath ?? "/settle",
548
- ensureTrailingSlash(facilitator.url)
549
- ).toString();
550
- const settleResponse = await fetchImpl(settleUrl, {
551
- method: "POST",
552
- headers,
553
- body: JSON.stringify({
554
- x402Version: attempt.payload.x402Version,
555
- paymentHeader: attempt.raw,
556
- paymentRequirements: requirement
557
- }),
558
- signal: controller?.signal ?? null
559
- });
560
- if (!settleResponse.ok) {
361
+ } catch (error) {
561
362
  return {
562
363
  success: false,
563
- optionId: option.id,
564
- attemptType: attempt.type,
565
- failure: paymentFailureSchema.parse({
566
- reason: `Facilitator settle request failed: ${settleResponse.status}`,
567
- code: "verification_failed",
568
- retryable: settleResponse.status >= 500
569
- })
364
+ error: error instanceof Error ? error.message : String(error)
570
365
  };
571
366
  }
572
- const settlePayload = await settleResponse.json();
573
- if (!settlePayload.success) {
574
- return {
575
- success: false,
576
- optionId: option.id,
577
- attemptType: attempt.type,
578
- failure: paymentFailureSchema.parse({
579
- reason: settlePayload.error ?? "Facilitator settlement failed",
580
- code: "verification_failed",
581
- retryable: false
582
- })
583
- };
367
+ }
368
+ async signTransferAuthorization(params) {
369
+ if (!this.walletClient.chain) {
370
+ throw new Error("Wallet client chain not configured");
584
371
  }
585
- const metadata = paymentSuccessMetadataSchema.parse({
586
- optionId: option.id,
587
- verifier: facilitator.vendor ?? "facilitator",
588
- txHash: settlePayload.txHash ?? void 0,
589
- networkId: settlePayload.networkId ?? void 0
590
- });
591
- return {
592
- success: true,
593
- optionId: option.id,
594
- attemptType: attempt.type,
595
- metadata,
596
- responseHeaders: {
597
- [HEADER_PAYMENT_RESPONSE]: createPaymentResponseHeader(metadata)
598
- }
372
+ const domain = {
373
+ name: "USD Coin",
374
+ version: "2",
375
+ chainId: this.walletClient.chain.id,
376
+ verifyingContract: params.tokenAddress
599
377
  };
600
- } catch (error) {
378
+ const types = {
379
+ TransferWithAuthorization: [
380
+ { name: "from", type: "address" },
381
+ { name: "to", type: "address" },
382
+ { name: "value", type: "uint256" },
383
+ { name: "validAfter", type: "uint256" },
384
+ { name: "validBefore", type: "uint256" },
385
+ { name: "nonce", type: "bytes32" }
386
+ ]
387
+ };
388
+ const message = {
389
+ from: params.from,
390
+ to: params.to,
391
+ value: params.value,
392
+ validAfter: params.validAfter,
393
+ validBefore: params.validBefore,
394
+ nonce: params.nonce
395
+ };
396
+ const signature = await this.walletClient.signTypedData({
397
+ account: this.account,
398
+ domain,
399
+ types,
400
+ primaryType: "TransferWithAuthorization",
401
+ message
402
+ });
601
403
  return {
602
- success: false,
603
- optionId: option.id,
604
- attemptType: attempt.type,
605
- failure: paymentFailureSchema.parse({
606
- reason: `Facilitator request error: ${error.message}`,
607
- code: "verification_failed",
608
- retryable: false
609
- })
404
+ signature,
405
+ from: params.from,
406
+ to: params.to,
407
+ value: params.value,
408
+ validAfter: params.validAfter,
409
+ validBefore: params.validBefore,
410
+ nonce: params.nonce
610
411
  };
611
- } finally {
612
- if (timeout) {
613
- clearTimeout(timeout);
614
- }
615
412
  }
616
- }
617
- function buildFacilitatorHeaders(config) {
618
- const headers = {
619
- "content-type": "application/json"
620
- };
621
- if (config?.headers) {
622
- Object.assign(headers, config.headers);
623
- }
624
- const apiKey = resolveFacilitatorApiKey(config);
625
- if (apiKey) {
626
- const headerName = config?.apiKeyHeader ?? "Authorization";
627
- headers[headerName] = apiKey;
413
+ getAddress() {
414
+ return this.account.address;
628
415
  }
629
- return headers;
416
+ };
417
+ async function payX402(config) {
418
+ const client = new X402Client({
419
+ privateKey: config.privateKey,
420
+ ...config.rpcUrl ? { rpcUrl: config.rpcUrl } : {}
421
+ });
422
+ return client.pay({
423
+ url: config.url,
424
+ body: config.body
425
+ });
630
426
  }
631
- function resolveFacilitatorApiKey(config) {
632
- if (!config) {
633
- return void 0;
427
+ var X402BrowserClient = class {
428
+ constructor(config) {
429
+ this.walletClient = config.walletClient;
430
+ this.chainId = config.chainId;
634
431
  }
635
- if (config.apiKey) {
636
- return config.apiKey;
637
- }
638
- if (config.apiKeyEnv && typeof process !== "undefined") {
639
- return process.env?.[config.apiKeyEnv];
432
+ async pay(request) {
433
+ try {
434
+ const initialResponse = await fetch(request.url, {
435
+ method: request.method ?? "POST",
436
+ headers: {
437
+ "Content-Type": "application/json",
438
+ ...request.headers
439
+ },
440
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
441
+ });
442
+ if (initialResponse.status !== 402) {
443
+ return {
444
+ success: initialResponse.ok,
445
+ response: initialResponse
446
+ };
447
+ }
448
+ const paymentRequirements = await initialResponse.json();
449
+ const x402Requirements = paymentRequirements.x402?.accepts?.[0];
450
+ if (!x402Requirements) {
451
+ return {
452
+ success: false,
453
+ error: "No x402 payment requirements found in 402 response"
454
+ };
455
+ }
456
+ const account = this.walletClient.account;
457
+ if (!account) {
458
+ return {
459
+ success: false,
460
+ error: "No account connected to wallet"
461
+ };
462
+ }
463
+ const authorization = {
464
+ from: account.address,
465
+ to: x402Requirements.payTo,
466
+ value: BigInt(x402Requirements.maxAmountRequired),
467
+ validAfter: BigInt(Math.floor(Date.now() / 1e3)),
468
+ validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
469
+ nonce: `0x${Array.from(
470
+ { length: 32 },
471
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
472
+ ).join("")}`
473
+ };
474
+ const signature = await this.signTransferAuthorization(
475
+ authorization,
476
+ x402Requirements.asset
477
+ );
478
+ const paymentProof = {
479
+ x402Version: 1,
480
+ scheme: x402Requirements.scheme,
481
+ network: x402Requirements.network,
482
+ correlationId: "",
483
+ payload: {
484
+ signature,
485
+ authorization: {
486
+ from: authorization.from,
487
+ to: authorization.to,
488
+ value: authorization.value.toString(),
489
+ validAfter: authorization.validAfter.toString(),
490
+ validBefore: authorization.validBefore.toString(),
491
+ nonce: authorization.nonce
492
+ }
493
+ }
494
+ };
495
+ const paymentHeader = btoa(JSON.stringify(paymentProof));
496
+ const paidResponse = await fetch(request.url, {
497
+ method: request.method ?? "POST",
498
+ headers: {
499
+ "Content-Type": "application/json",
500
+ "X-PAYMENT": paymentHeader,
501
+ ...request.headers
502
+ },
503
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
504
+ });
505
+ return {
506
+ success: paidResponse.ok,
507
+ response: paidResponse,
508
+ paymentDetails: {
509
+ amount: x402Requirements.maxAmountRequired,
510
+ currency: x402Requirements.extra?.currencyCode ?? "USDC",
511
+ network: x402Requirements.network,
512
+ signature
513
+ }
514
+ };
515
+ } catch (error) {
516
+ return {
517
+ success: false,
518
+ error: error instanceof Error ? error.message : String(error)
519
+ };
520
+ }
640
521
  }
641
- return void 0;
642
- }
643
- function ensureTrailingSlash(value) {
644
- return value.endsWith("/") ? value : `${value}/`;
645
- }
646
- var PAYMENT_HEADERS = {
647
- x402: HEADER_X402,
648
- direct: HEADER_DIRECT,
649
- response: HEADER_PAYMENT_RESPONSE
650
- };
651
-
652
- // src/payment/index.ts
653
- var DEFAULT_ID_X402 = "x402";
654
- var DEFAULT_ID_402 = "402";
655
- var SUPPORTED_CURRENCIES = {
656
- USDC: {
657
- decimals: 6,
658
- symbol: "USDC",
659
- x402: {
660
- network: "base",
661
- assetAddress: "0x833589fCD6eDb6E08f4c7C37b7b4c6e997E08A43"
522
+ async signTransferAuthorization(authorization, tokenAddress) {
523
+ const account = this.walletClient.account;
524
+ if (!account) {
525
+ throw new Error("No account connected to wallet");
662
526
  }
527
+ const domain = {
528
+ name: "USD Coin",
529
+ version: "2",
530
+ chainId: this.chainId,
531
+ verifyingContract: tokenAddress
532
+ };
533
+ const types = {
534
+ TransferWithAuthorization: [
535
+ { name: "from", type: "address" },
536
+ { name: "to", type: "address" },
537
+ { name: "value", type: "uint256" },
538
+ { name: "validAfter", type: "uint256" },
539
+ { name: "validBefore", type: "uint256" },
540
+ { name: "nonce", type: "bytes32" }
541
+ ]
542
+ };
543
+ const message = {
544
+ from: authorization.from,
545
+ to: authorization.to,
546
+ value: authorization.value,
547
+ validAfter: authorization.validAfter,
548
+ validBefore: authorization.validBefore,
549
+ nonce: authorization.nonce
550
+ };
551
+ return await this.walletClient.signTypedData({
552
+ account,
553
+ domain,
554
+ types,
555
+ primaryType: "TransferWithAuthorization",
556
+ message
557
+ });
663
558
  }
664
559
  };
665
- var DEFAULT_FACILITATORS = {
666
- opentool: "https://facilitator.opentool.dev/x402",
667
- coinbase: "https://payments.coinbase.com/x402"
668
- };
669
- var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.payment.context");
670
- var PaymentRequiredError = class extends Error {
560
+ async function payX402WithWallet(walletClient, chainId, request) {
561
+ const client = new X402BrowserClient({ walletClient, chainId });
562
+ return client.pay(request);
563
+ }
564
+
565
+ // src/x402/index.ts
566
+ var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.x402.context");
567
+ var X402PaymentRequiredError = class extends Error {
671
568
  constructor(response, verification) {
672
- super("Payment required");
673
- this.name = "PaymentRequiredError";
569
+ super("X402 Payment required");
570
+ this.name = "X402PaymentRequiredError";
674
571
  this.response = response;
675
572
  this.verification = verification;
676
573
  }
@@ -687,246 +584,133 @@ function setPaymentContext(request, context) {
687
584
  request[PAYMENT_CONTEXT_SYMBOL] = context;
688
585
  }
689
586
  }
690
- function getPaymentContext(request) {
587
+ function getX402PaymentContext(request) {
691
588
  return request[PAYMENT_CONTEXT_SYMBOL];
692
589
  }
693
- function applyPaymentHeaders(response, headers) {
694
- const entries = Object.entries(headers ?? {});
695
- if (entries.length === 0) {
696
- return response;
697
- }
698
- let mutated = false;
699
- const merged = new Headers(response.headers);
700
- for (const [key, value] of entries) {
701
- if (!merged.has(key)) {
702
- merged.set(key, value);
703
- mutated = true;
704
- }
705
- }
706
- if (!mutated) {
707
- return response;
708
- }
709
- return new Response(response.body, {
710
- status: response.status,
711
- statusText: response.statusText,
712
- headers: merged
713
- });
714
- }
715
- function withPaymentRequirement(handler, payment, options = {}) {
716
- return async (request) => {
717
- const verification = await requirePayment(request, payment, options);
718
- if (verification instanceof Response) {
719
- return verification;
720
- }
721
- setPaymentContext(request, verification);
722
- const response = await Promise.resolve(handler(request));
723
- return applyPaymentHeaders(response, verification.headers);
724
- };
725
- }
726
- function definePayment(config) {
727
- const verifiers = {
728
- ...config.verifiers ?? {}
729
- };
730
- const methods = config.acceptedMethods ?? ["402"];
731
- const includeX402 = methods.includes("x402");
732
- const includePlain402 = methods.includes("402");
733
- if (!includeX402 && !includePlain402) {
734
- throw new Error(
735
- "definePayment requires at least one payment transport (x402 or 402)"
736
- );
737
- }
590
+ function defineX402Payment(config) {
738
591
  const currencyCode = normalizeCurrency(config.currency);
739
592
  const currencySpec = SUPPORTED_CURRENCIES[currencyCode];
740
593
  if (!currencySpec) {
741
- throw new Error(`Unsupported currency for payments: ${currencyCode}`);
594
+ throw new Error(`Unsupported currency for x402 payments: ${currencyCode}`);
742
595
  }
743
- const decimals = currencySpec.decimals;
744
- const symbol = currencySpec.symbol;
745
- const value = toDecimalString(config.amount);
746
- const accepts = [];
747
- if (includeX402) {
748
- const overrides = config.x402 ?? {};
749
- const defaults = currencySpec.x402;
750
- if (!defaults && (!overrides.network || !overrides.assetAddress)) {
751
- throw new Error(
752
- "x402 payments require a network and assetAddress; supply them or choose a supported currency."
753
- );
754
- }
755
- const facilitator = resolveFacilitator(
756
- config.facilitator ?? overrides.facilitator ?? "opentool"
757
- );
758
- accepts.push(
759
- paymentOptionSchema.parse({
760
- id: overrides.id ?? DEFAULT_ID_X402,
761
- title: `Pay ${value} ${currencyCode}`,
762
- amount: {
763
- value,
764
- currency: { code: currencyCode, symbol, decimals }
765
- },
766
- asset: {
767
- symbol,
768
- network: overrides.network ?? defaults?.network ?? "",
769
- address: overrides.assetAddress ?? defaults?.assetAddress ?? "",
770
- decimals,
771
- standard: "erc20"
772
- },
773
- payTo: config.payTo,
774
- proof: {
775
- mode: "x402",
776
- network: overrides.network ?? defaults?.network ?? "",
777
- scheme: overrides.scheme ?? "exact",
778
- version: overrides.version ?? 1,
779
- facilitator,
780
- verifier: facilitator ? "x402:facilitator" : void 0
781
- },
782
- settlement: overrides.settlement
783
- })
784
- );
785
- }
786
- if (includePlain402) {
787
- const overrides = config.direct ?? {};
788
- const id = overrides.id ?? DEFAULT_ID_402;
789
- const verifierId = overrides.verifierId ?? `direct:${id}`;
790
- const proofType = overrides.proofType ?? id;
791
- const verifier = overrides.verify ?? buildDefaultDirectVerifier(overrides.token, verifierId, id);
792
- verifiers[verifierId] = verifier;
793
- accepts.push(
794
- paymentOptionSchema.parse({
795
- id,
796
- title: `Pay ${value} ${currencyCode}`,
797
- amount: {
798
- value,
799
- currency: { code: currencyCode, symbol, decimals }
800
- },
801
- asset: {
802
- symbol,
803
- decimals,
804
- standard: "erc20"
805
- },
806
- payTo: config.payTo,
807
- proof: {
808
- mode: "direct",
809
- proofTypes: [proofType],
810
- verifier: verifierId,
811
- instructions: overrides.instructions,
812
- fields: overrides.fields,
813
- allowsManualReview: overrides.allowsManualReview
814
- },
815
- settlement: overrides.settlement
816
- })
596
+ const network = config.network ?? currencySpec.network;
597
+ const assetAddress = config.assetAddress ?? currencySpec.assetAddress;
598
+ if (!network || !assetAddress) {
599
+ throw new Error(
600
+ "x402 payments require a network and assetAddress; supply them or choose a supported currency."
817
601
  );
818
602
  }
819
- const facilitatorLabel = includeX402 ? resolveFacilitatorLabel(config.facilitator ?? config.x402?.facilitator) : void 0;
820
- const baseMetadata = {};
821
- if (currencyCode === "USDC") {
822
- baseMetadata.amountUSDC = Number(value);
823
- }
824
- baseMetadata.x402 = includeX402;
825
- baseMetadata.plain402 = includePlain402;
826
- baseMetadata.acceptedMethods = methods;
827
- baseMetadata.acceptedCurrencies = config.acceptedCurrencies ?? [currencyCode];
828
- if (config.chains) {
829
- baseMetadata.chains = config.chains;
830
- }
831
- if (facilitatorLabel) {
832
- baseMetadata.facilitator = facilitatorLabel;
833
- }
834
- const metadata = config.metadata ? { ...baseMetadata, ...config.metadata } : baseMetadata;
603
+ const facilitator = resolveFacilitator(config.facilitator);
604
+ const value = toDecimalString(config.amount);
835
605
  const definition = {
836
- schemaVersion: PAYMENT_SCHEMA_VERSION,
837
- accepts,
838
- metadata
606
+ amount: value,
607
+ currency: {
608
+ code: currencyCode,
609
+ symbol: currencySpec.symbol,
610
+ decimals: currencySpec.decimals
611
+ },
612
+ asset: {
613
+ symbol: currencySpec.symbol,
614
+ network,
615
+ address: assetAddress,
616
+ decimals: currencySpec.decimals
617
+ },
618
+ payTo: config.payTo,
619
+ scheme: config.scheme ?? "exact",
620
+ network,
621
+ facilitator
839
622
  };
840
- if (config.message !== void 0) {
841
- definition.message = config.message;
842
- }
843
- if (config.resource !== void 0) {
623
+ if (config.resource) {
844
624
  definition.resource = config.resource;
845
625
  }
846
- const defined = {
626
+ if (config.message) {
627
+ definition.description = config.message;
628
+ }
629
+ if (config.metadata) {
630
+ definition.metadata = config.metadata;
631
+ }
632
+ const baseMetadata = {
633
+ amountUSDC: currencyCode === "USDC" ? Number(value) : void 0,
634
+ facilitator: "x402rs",
635
+ network
636
+ };
637
+ const metadata = config.metadata ? { ...baseMetadata, ...config.metadata } : baseMetadata;
638
+ return {
847
639
  definition,
848
- verifiers,
849
640
  metadata
850
641
  };
851
- if (config.message !== void 0) {
852
- defined.message = config.message;
853
- }
854
- return defined;
855
642
  }
856
- async function requirePayment(request, payment, options = {}) {
857
- const { definition, verifiers } = normalizePayment(payment);
858
- const mergedVerifiers = {
859
- ...verifiers,
860
- ...options.verifiers ?? {}
861
- };
643
+ async function requireX402Payment(request, payment, options = {}) {
644
+ const definition = isX402Payment(payment) ? payment.definition : payment;
645
+ const attempt = extractX402Attempt(request);
646
+ if (!attempt) {
647
+ const response = createX402PaymentRequired(definition);
648
+ throw new X402PaymentRequiredError(response);
649
+ }
862
650
  const verifyOptions = {
863
- definition,
864
- request
651
+ settle: options.settle !== void 0 ? options.settle : true
865
652
  };
866
- if (Object.keys(mergedVerifiers).length > 0) {
867
- verifyOptions.verifiers = mergedVerifiers;
868
- }
869
- if (options.settle !== void 0) {
870
- verifyOptions.settle = options.settle;
871
- }
872
- if (options.fetchImpl) {
653
+ if (options.fetchImpl !== void 0) {
873
654
  verifyOptions.fetchImpl = options.fetchImpl;
874
655
  }
875
- const verification = await verifyPayment(verifyOptions);
656
+ const verification = await verifyX402Payment(attempt, definition, verifyOptions);
876
657
  if (!verification.success || !verification.metadata) {
877
658
  if (options.onFailure) {
878
659
  return options.onFailure(verification);
879
660
  }
880
- const response = paymentRequiredResponse(definition);
881
- throw new PaymentRequiredError(response, verification);
661
+ const response = createX402PaymentRequired(definition);
662
+ throw new X402PaymentRequiredError(response, verification);
882
663
  }
883
664
  return {
884
665
  payment: verification.metadata,
885
666
  headers: verification.responseHeaders ?? {},
886
- optionId: verification.optionId,
887
667
  result: verification
888
668
  };
889
669
  }
890
- function normalizePayment(payment) {
891
- if (isDefinedPayment(payment)) {
892
- return {
893
- definition: payment.definition,
894
- verifiers: payment.verifiers ?? {}
895
- };
896
- }
897
- return {
898
- definition: payment,
899
- verifiers: {}
670
+ function withX402Payment(handler, payment, options = {}) {
671
+ return async (request) => {
672
+ const verification = await requireX402Payment(request, payment, options);
673
+ if (verification instanceof Response) {
674
+ return verification;
675
+ }
676
+ setPaymentContext(request, verification);
677
+ const response = await Promise.resolve(handler(request));
678
+ return applyPaymentHeaders(response, verification.headers);
900
679
  };
901
680
  }
902
- function isDefinedPayment(value) {
903
- return !!value && typeof value === "object" && "definition" in value && value.definition !== void 0;
904
- }
905
- function resolveFacilitator(value) {
906
- if (!value) {
907
- return void 0;
681
+ function applyPaymentHeaders(response, headers) {
682
+ const entries = Object.entries(headers ?? {});
683
+ if (entries.length === 0) {
684
+ return response;
908
685
  }
909
- if (typeof value === "string") {
910
- if (value in DEFAULT_FACILITATORS) {
911
- return {
912
- url: DEFAULT_FACILITATORS[value]
913
- };
686
+ let mutated = false;
687
+ const merged = new Headers(response.headers);
688
+ for (const [key, value] of entries) {
689
+ if (!merged.has(key)) {
690
+ merged.set(key, value);
691
+ mutated = true;
914
692
  }
915
- return { url: value };
916
693
  }
917
- return value;
694
+ if (!mutated) {
695
+ return response;
696
+ }
697
+ return new Response(response.body, {
698
+ status: response.status,
699
+ statusText: response.statusText,
700
+ headers: merged
701
+ });
918
702
  }
919
- function resolveFacilitatorLabel(value) {
703
+ function isX402Payment(value) {
704
+ return !!value && typeof value === "object" && "definition" in value && value.definition !== void 0;
705
+ }
706
+ function resolveFacilitator(value) {
920
707
  if (!value) {
921
- return "opentool";
708
+ return DEFAULT_FACILITATOR;
922
709
  }
923
710
  if (typeof value === "string") {
924
- if (value === "opentool" || value === "coinbase") {
925
- return value;
926
- }
927
- return "custom";
711
+ return { ...DEFAULT_FACILITATOR, url: value };
928
712
  }
929
- return "custom";
713
+ return value;
930
714
  }
931
715
  function normalizeCurrency(currency) {
932
716
  return (currency ?? "USDC").toUpperCase();
@@ -934,55 +718,6 @@ function normalizeCurrency(currency) {
934
718
  function toDecimalString(value) {
935
719
  return typeof value === "number" ? value.toString() : value;
936
720
  }
937
- function buildDefaultDirectVerifier(expectedToken, verifierId, optionId) {
938
- return async ({ attempt, option }) => {
939
- if (attempt.type !== "direct") {
940
- return {
941
- success: false,
942
- optionId: option.id,
943
- attemptType: attempt.type,
944
- failure: {
945
- reason: "Expected direct payment payload",
946
- code: "invalid_payload"
947
- }
948
- };
949
- }
950
- const payload = attempt.payload.payload;
951
- if (expectedToken) {
952
- if (payload?.token !== expectedToken) {
953
- return {
954
- success: false,
955
- optionId: option.id,
956
- attemptType: attempt.type,
957
- failure: {
958
- reason: "Invalid or missing payment proof",
959
- code: "verification_failed"
960
- }
961
- };
962
- }
963
- } else if (!payload) {
964
- return {
965
- success: false,
966
- optionId: option.id,
967
- attemptType: attempt.type,
968
- failure: {
969
- reason: "Payment proof is required",
970
- code: "verification_failed"
971
- }
972
- };
973
- }
974
- return {
975
- success: true,
976
- optionId,
977
- attemptType: attempt.type,
978
- metadata: {
979
- optionId,
980
- verifier: verifierId,
981
- payload
982
- }
983
- };
984
- };
985
- }
986
721
 
987
722
  // src/adapters/mcp.ts
988
723
  var HTTP_METHODS = [
@@ -1010,7 +745,7 @@ function createMcpAdapter(options) {
1010
745
  const response = await Promise.resolve(httpHandler(request));
1011
746
  return await responseToToolResponse(response);
1012
747
  } catch (error) {
1013
- if (error instanceof PaymentRequiredError) {
748
+ if (error instanceof X402PaymentRequiredError) {
1014
749
  return await responseToToolResponse(error.response);
1015
750
  }
1016
751
  throw error;
@@ -1249,7 +984,7 @@ async function loadToolsFromDirectory(metadataMap) {
1249
984
  const entry = httpHandlers[index];
1250
985
  httpHandlers[index] = {
1251
986
  ...entry,
1252
- handler: withPaymentRequirement(entry.handler, payment)
987
+ handler: withX402Payment(entry.handler, payment)
1253
988
  };
1254
989
  }
1255
990
  }
@@ -2505,23 +2240,45 @@ var McpAnnotationsSchema = z.object({
2505
2240
  openWorldHint: z.boolean().optional(),
2506
2241
  requiresPayment: z.boolean().optional()
2507
2242
  }).strict();
2508
- var PaymentConfigSchema = z.object({
2509
- amountUSDC: z.number().nonnegative().optional(),
2510
- description: z.string().optional(),
2511
- x402: z.boolean().optional(),
2512
- plain402: z.boolean().optional(),
2513
- acceptedMethods: z.array(z.union([z.literal("x402"), z.literal("402")])).optional(),
2514
- acceptedCurrencies: z.array(z.string()).optional(),
2515
- chains: z.array(z.union([z.string(), z.number()])).optional(),
2516
- facilitator: z.string().optional()
2517
- }).strict();
2243
+ var X402PaymentSchema = z.object({
2244
+ definition: z.object({
2245
+ amount: z.string(),
2246
+ currency: z.object({
2247
+ code: z.string(),
2248
+ symbol: z.string(),
2249
+ decimals: z.number()
2250
+ }),
2251
+ asset: z.object({
2252
+ symbol: z.string(),
2253
+ network: z.string(),
2254
+ address: z.string(),
2255
+ decimals: z.number()
2256
+ }),
2257
+ payTo: z.string(),
2258
+ resource: z.string().optional(),
2259
+ description: z.string().optional(),
2260
+ scheme: z.string(),
2261
+ network: z.string(),
2262
+ facilitator: z.object({
2263
+ url: z.string(),
2264
+ verifyPath: z.string().optional(),
2265
+ settlePath: z.string().optional(),
2266
+ apiKeyHeader: z.string().optional()
2267
+ }),
2268
+ metadata: z.record(z.string(), z.unknown()).optional()
2269
+ }),
2270
+ metadata: z.record(z.string(), z.unknown()).optional()
2271
+ }).passthrough();
2272
+ var PaymentConfigSchema = z.union([
2273
+ X402PaymentSchema,
2274
+ z.record(z.string(), z.unknown())
2275
+ ]);
2518
2276
  var DiscoveryMetadataSchema = z.object({
2519
2277
  keywords: z.array(z.string()).optional(),
2520
2278
  category: z.string().optional(),
2521
2279
  useCases: z.array(z.string()).optional(),
2522
2280
  capabilities: z.array(z.string()).optional(),
2523
2281
  requirements: z.record(z.string(), z.any()).optional(),
2524
- pricing: z.record(z.string(), z.any()).optional(),
2525
2282
  compatibility: z.record(z.string(), z.any()).optional(),
2526
2283
  documentation: z.union([z.string(), z.array(z.string())]).optional()
2527
2284
  }).catchall(z.any());
@@ -2542,7 +2299,7 @@ var ToolSchema = z.object({
2542
2299
  discovery: DiscoveryMetadataSchema.optional(),
2543
2300
  chains: z.array(z.union([z.string(), z.number()])).optional()
2544
2301
  }).strict();
2545
- var AuthoredMetadataSchema = z.object({
2302
+ var MetadataSchema = z.object({
2546
2303
  metadataSpecVersion: z.string().optional(),
2547
2304
  name: z.string().optional(),
2548
2305
  displayName: z.string().optional(),
@@ -2566,11 +2323,10 @@ var AuthoredMetadataSchema = z.object({
2566
2323
  useCases: z.array(z.string()).optional(),
2567
2324
  capabilities: z.array(z.string()).optional(),
2568
2325
  requirements: z.record(z.string(), z.any()).optional(),
2569
- pricing: z.record(z.string(), z.any()).optional(),
2570
2326
  compatibility: z.record(z.string(), z.any()).optional(),
2571
2327
  chains: z.array(z.union([z.string(), z.number()])).optional()
2572
2328
  }).catchall(z.any());
2573
- var MetadataSchema = z.object({
2329
+ var BuildMetadataSchema = z.object({
2574
2330
  metadataSpecVersion: z.string().default(METADATA_SPEC_VERSION),
2575
2331
  name: z.string(),
2576
2332
  displayName: z.string(),
@@ -2672,7 +2428,7 @@ async function importFresh(modulePath) {
2672
2428
 
2673
2429
  // src/cli/shared/metadata.ts
2674
2430
  var METADATA_ENTRY = "metadata.ts";
2675
- async function loadAuthoredMetadata(projectRoot) {
2431
+ async function loadMetadata2(projectRoot) {
2676
2432
  const absPath = path5.join(projectRoot, METADATA_ENTRY);
2677
2433
  if (!fs2.existsSync(absPath)) {
2678
2434
  throw new Error(
@@ -2693,7 +2449,7 @@ async function loadAuthoredMetadata(projectRoot) {
2693
2449
  const compiledPath = resolveCompiledPath(outDir, METADATA_ENTRY);
2694
2450
  const moduleExports = await importFresh(compiledPath);
2695
2451
  const metadataExport = extractMetadataExport(moduleExports);
2696
- const parsed = AuthoredMetadataSchema.parse(metadataExport);
2452
+ const parsed = MetadataSchema.parse(metadataExport);
2697
2453
  return { metadata: parsed, sourcePath: absPath };
2698
2454
  } finally {
2699
2455
  cleanup();
@@ -2734,7 +2490,7 @@ function readPackageJson(projectRoot) {
2734
2490
  async function buildMetadataArtifact(options) {
2735
2491
  const projectRoot = options.projectRoot;
2736
2492
  const packageInfo = readPackageJson(projectRoot);
2737
- const { metadata: authored, sourcePath } = await loadAuthoredMetadata(projectRoot);
2493
+ const { metadata: authored, sourcePath } = await loadMetadata2(projectRoot);
2738
2494
  const defaultsApplied = [];
2739
2495
  const folderName = path5.basename(projectRoot);
2740
2496
  const name = resolveField(
@@ -2779,7 +2535,7 @@ async function buildMetadataArtifact(options) {
2779
2535
  if (!authored.website && packageInfo.homepage) {
2780
2536
  defaultsApplied.push("website \u2192 package.json homepage");
2781
2537
  }
2782
- const payment = resolvePayment(authored, defaultsApplied);
2538
+ const payment = resolvePayment(authored);
2783
2539
  const baseImage = authored.image ?? authored.iconPath;
2784
2540
  const animation = authored.animation_url ?? authored.videoPath;
2785
2541
  const discovery = buildDiscovery(authored);
@@ -2812,7 +2568,7 @@ async function buildMetadataArtifact(options) {
2812
2568
  }
2813
2569
  return toolDefinition;
2814
2570
  });
2815
- const metadata = MetadataSchema.parse({
2571
+ const metadata = BuildMetadataSchema.parse({
2816
2572
  metadataSpecVersion: authored.metadataSpecVersion ?? METADATA_SPEC_VERSION,
2817
2573
  name,
2818
2574
  displayName,
@@ -2868,32 +2624,8 @@ function extractRepository(repository) {
2868
2624
  }
2869
2625
  return repository.url;
2870
2626
  }
2871
- function resolvePayment(authored, defaults) {
2872
- if (authored.payment) {
2873
- return authored.payment;
2874
- }
2875
- const discoveryPricing = authored.discovery?.pricing;
2876
- const legacyPricing = authored.pricing;
2877
- const pricing = discoveryPricing ?? legacyPricing;
2878
- if (!pricing) {
2879
- return void 0;
2880
- }
2881
- const amount = typeof pricing.defaultAmount === "number" ? pricing.defaultAmount : 0;
2882
- const sourceLabel = discoveryPricing ? "discovery.pricing.defaultAmount" : "pricing.defaultAmount";
2883
- defaults.push(`payment \u2192 synthesized from ${sourceLabel}`);
2884
- const acceptedMethodsRaw = Array.isArray(pricing.acceptedMethods) ? pricing.acceptedMethods : ["402"];
2885
- const acceptedMethods = acceptedMethodsRaw.map(
2886
- (method) => method === "x402" ? "x402" : "402"
2887
- );
2888
- return {
2889
- amountUSDC: amount,
2890
- description: typeof pricing.description === "string" ? pricing.description : void 0,
2891
- x402: acceptedMethods.includes("x402"),
2892
- plain402: acceptedMethods.includes("402"),
2893
- acceptedMethods,
2894
- acceptedCurrencies: Array.isArray(pricing.acceptedCurrencies) ? pricing.acceptedCurrencies : ["USDC"],
2895
- chains: Array.isArray(pricing.chains) ? pricing.chains : [8453]
2896
- };
2627
+ function resolvePayment(authored, _defaults) {
2628
+ return authored.payment ?? void 0;
2897
2629
  }
2898
2630
  function buildDiscovery(authored) {
2899
2631
  const legacyDiscovery = {};
@@ -2915,9 +2647,6 @@ function buildDiscovery(authored) {
2915
2647
  if (Array.isArray(authored.categories) && authored.categories.length > 0) {
2916
2648
  legacyDiscovery.category = authored.categories[0];
2917
2649
  }
2918
- if (authored.pricing) {
2919
- legacyDiscovery.pricing = authored.pricing;
2920
- }
2921
2650
  const merged = {
2922
2651
  ...legacyDiscovery,
2923
2652
  ...authored.discovery ?? {}
@@ -3000,7 +2729,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
3000
2729
  const entry = httpHandlers[index];
3001
2730
  httpHandlers[index] = {
3002
2731
  ...entry,
3003
- handler: withPaymentRequirement(entry.handler, paymentExport)
2732
+ handler: withX402Payment(entry.handler, paymentExport)
3004
2733
  };
3005
2734
  }
3006
2735
  }
@@ -3013,11 +2742,11 @@ async function loadAndValidateTools(toolsDir, options = {}) {
3013
2742
  ...schema ? { schema } : {}
3014
2743
  });
3015
2744
  let metadataOverrides = toolModule.metadata ?? null;
3016
- if (paymentExport?.metadata) {
2745
+ if (paymentExport) {
3017
2746
  if (metadataOverrides) {
3018
2747
  metadataOverrides = {
3019
2748
  ...metadataOverrides,
3020
- payment: metadataOverrides.payment ?? paymentExport.metadata,
2749
+ payment: metadataOverrides.payment ?? paymentExport,
3021
2750
  annotations: {
3022
2751
  ...metadataOverrides.annotations ?? {},
3023
2752
  requiresPayment: metadataOverrides.annotations?.requiresPayment ?? true
@@ -3025,7 +2754,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
3025
2754
  };
3026
2755
  } else {
3027
2756
  metadataOverrides = {
3028
- payment: paymentExport.metadata,
2757
+ payment: paymentExport,
3029
2758
  annotations: { requiresPayment: true }
3030
2759
  };
3031
2760
  }
@@ -3271,6 +3000,6 @@ function timestamp() {
3271
3000
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
3272
3001
  }
3273
3002
 
3274
- export { AIAbortError, AIError, AIFetchError, AIResponseError, DEFAULT_BASE_URL, DEFAULT_CHAIN, DEFAULT_MODEL, DEFAULT_TIMEOUT_MS, DEFAULT_TOKENS, HEADER_PAYMENT_RESPONSE, HTTP_METHODS2 as HTTP_METHODS, PAYMENT_HEADERS, PAYMENT_SCHEMA_VERSION, PaymentRequiredError, WEBSEARCH_TOOL_DEFINITION, WEBSEARCH_TOOL_NAME, chains, createAIClient, createDevServer, createMcpAdapter, createPaymentRequiredBody, createPaymentResponseHeader, createStdioServer, cryptoAssetSchema, currencySchema, decimalStringSchema, definePayment, directPaymentPayloadSchema, directProofSchema, ensureTextContent, extractPaymentAttempts, facilitatorConfigSchema, flattenMessageContent, generateMetadata, generateMetadataCommand, generateText, getModelConfig, getPaymentContext, getRpcUrl, isStreamingSupported, isToolCallingSupported, listModels, loadAndValidateTools, normalizeModelName, paymentAmountSchema, paymentFailureSchema, paymentFieldSchema, paymentOptionSchema, paymentProofSchema, paymentRequiredResponse, paymentRequirementsSchema, paymentSchemaVersionSchema, paymentSuccessMetadataSchema, registry, requirePayment, resolveConfig, resolveRuntimePath, resolveToolset, responseToToolResponse, settlementTermsSchema, streamText, tokens, validateCommand, verifyPayment, wallet, walletToolkit, withPaymentRequirement, x402PaymentHeaderSchema, x402ProofSchema };
3003
+ export { AIAbortError, AIError, AIFetchError, AIResponseError, DEFAULT_BASE_URL, DEFAULT_CHAIN, DEFAULT_FACILITATOR, DEFAULT_MODEL, DEFAULT_TIMEOUT_MS, DEFAULT_TOKENS, HTTP_METHODS2 as HTTP_METHODS, PAYMENT_HEADERS, SUPPORTED_CURRENCIES, WEBSEARCH_TOOL_DEFINITION, WEBSEARCH_TOOL_NAME, X402BrowserClient, X402Client, X402PaymentRequiredError, chains, createAIClient, createDevServer, createMcpAdapter, createStdioServer, defineX402Payment, ensureTextContent, flattenMessageContent, generateMetadata, generateMetadataCommand, generateText, getModelConfig, getRpcUrl, getX402PaymentContext, isStreamingSupported, isToolCallingSupported, listModels, loadAndValidateTools, normalizeModelName, payX402, payX402WithWallet, registry, requireX402Payment, resolveConfig, resolveRuntimePath, resolveToolset, responseToToolResponse, streamText, tokens, validateCommand, wallet, walletToolkit, withX402Payment };
3275
3004
  //# sourceMappingURL=index.js.map
3276
3005
  //# sourceMappingURL=index.js.map