opentool 0.5.0 → 0.6.1

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.
Files changed (60) hide show
  1. package/README.md +87 -22
  2. package/dist/ai/index.d.ts +237 -0
  3. package/dist/ai/index.js +759 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/cli/index.d.ts +38 -5
  6. package/dist/cli/index.js +2218 -67
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/index-D3DaM5Rs.d.ts +1693 -0
  9. package/dist/index.d.ts +33 -5
  10. package/dist/index.js +3258 -25
  11. package/dist/index.js.map +1 -1
  12. package/dist/payment/index.d.ts +2 -0
  13. package/dist/payment/index.js +969 -0
  14. package/dist/payment/index.js.map +1 -0
  15. package/dist/{types/metadata.d.ts → validate-DiIOFUU5.d.ts} +262 -415
  16. package/dist/wallets/index.d.ts +117 -0
  17. package/dist/wallets/index.js +337 -0
  18. package/dist/wallets/index.js.map +1 -0
  19. package/package.json +35 -4
  20. package/dist/cli/build.d.ts +0 -23
  21. package/dist/cli/build.d.ts.map +0 -1
  22. package/dist/cli/build.js +0 -223
  23. package/dist/cli/build.js.map +0 -1
  24. package/dist/cli/dev.d.ts +0 -6
  25. package/dist/cli/dev.d.ts.map +0 -1
  26. package/dist/cli/dev.js +0 -123
  27. package/dist/cli/dev.js.map +0 -1
  28. package/dist/cli/generate-metadata.d.ts +0 -15
  29. package/dist/cli/generate-metadata.d.ts.map +0 -1
  30. package/dist/cli/generate-metadata.js +0 -90
  31. package/dist/cli/generate-metadata.js.map +0 -1
  32. package/dist/cli/index.d.ts.map +0 -1
  33. package/dist/cli/shared/metadata.d.ts +0 -19
  34. package/dist/cli/shared/metadata.d.ts.map +0 -1
  35. package/dist/cli/shared/metadata.js +0 -283
  36. package/dist/cli/shared/metadata.js.map +0 -1
  37. package/dist/cli/validate.d.ts +0 -12
  38. package/dist/cli/validate.d.ts.map +0 -1
  39. package/dist/cli/validate.js +0 -237
  40. package/dist/cli/validate.js.map +0 -1
  41. package/dist/index.d.ts.map +0 -1
  42. package/dist/runtime/index.d.ts +0 -12
  43. package/dist/runtime/index.d.ts.map +0 -1
  44. package/dist/runtime/index.js +0 -241
  45. package/dist/runtime/index.js.map +0 -1
  46. package/dist/types/index.d.ts +0 -33
  47. package/dist/types/index.d.ts.map +0 -1
  48. package/dist/types/index.js +0 -3
  49. package/dist/types/index.js.map +0 -1
  50. package/dist/types/metadata.d.ts.map +0 -1
  51. package/dist/types/metadata.js +0 -108
  52. package/dist/types/metadata.js.map +0 -1
  53. package/dist/utils/esbuild.d.ts +0 -13
  54. package/dist/utils/esbuild.d.ts.map +0 -1
  55. package/dist/utils/esbuild.js +0 -95
  56. package/dist/utils/esbuild.js.map +0 -1
  57. package/dist/utils/module-loader.d.ts +0 -3
  58. package/dist/utils/module-loader.d.ts.map +0 -1
  59. package/dist/utils/module-loader.js +0 -49
  60. package/dist/utils/module-loader.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,26 +1,3259 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.validateCommand = exports.loadAndValidateTools = exports.generateMetadataCommand = exports.generateMetadata = void 0;
18
- __exportStar(require("./runtime"), exports);
19
- __exportStar(require("./types"), exports);
20
- var generate_metadata_1 = require("./cli/generate-metadata");
21
- Object.defineProperty(exports, "generateMetadata", { enumerable: true, get: function () { return generate_metadata_1.generateMetadata; } });
22
- Object.defineProperty(exports, "generateMetadataCommand", { enumerable: true, get: function () { return generate_metadata_1.generateMetadataCommand; } });
23
- var validate_1 = require("./cli/validate");
24
- Object.defineProperty(exports, "loadAndValidateTools", { enumerable: true, get: function () { return validate_1.loadAndValidateTools; } });
25
- Object.defineProperty(exports, "validateCommand", { enumerable: true, get: function () { return validate_1.validateCommand; } });
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
+ import * as fs2 from 'fs';
5
+ import * as path5 from 'path';
6
+ import { fileURLToPath, pathToFileURL } from 'url';
7
+ import { zodToJsonSchema } from 'zod-to-json-schema';
8
+ import { z } from 'zod';
9
+ import { zeroAddress, createPublicClient, http, createWalletClient } from 'viem';
10
+ import { baseSepolia, mainnet, base } from 'viem/chains';
11
+ import { privateKeyToAccount } from 'viem/accounts';
12
+ import { Turnkey } from '@turnkey/sdk-server';
13
+ import { createAccount } from '@turnkey/viem';
14
+ import { tmpdir } from 'os';
15
+ import { build } from 'esbuild';
16
+ import { createRequire } from 'module';
17
+
18
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
19
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
20
+ }) : x)(function(x) {
21
+ if (typeof require !== "undefined") return require.apply(this, arguments);
22
+ throw Error('Dynamic require of "' + x + '" is not supported');
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;
152
+ var HEADER_X402 = "X-PAYMENT";
153
+ var HEADER_DIRECT = "X-PAYMENT-PROOF";
154
+ var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
155
+ var x402RequirementSchema = z.object({
156
+ scheme: z.string().min(1),
157
+ network: z.string().min(1),
158
+ maxAmountRequired: z.string().min(1),
159
+ asset: z.string().min(1),
160
+ payTo: z.string().min(1),
161
+ resource: z.string().optional(),
162
+ description: z.string().optional(),
163
+ mimeType: z.string().optional(),
164
+ outputSchema: z.unknown().optional(),
165
+ maxTimeoutSeconds: z.number().int().positive().optional(),
166
+ extra: z.record(z.string(), z.unknown()).nullable().optional()
167
+ });
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");
191
+ }
192
+ return new Response(JSON.stringify(body), {
193
+ ...init,
194
+ status: init?.status ?? 402,
195
+ headers
196
+ });
197
+ }
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
+ );
227
+ }
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) {
234
+ 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
+ })
243
+ };
244
+ }
245
+ for (const attempt of attempts) {
246
+ const option = findMatchingOption(definition, attempt);
247
+ if (!option) {
248
+ continue;
249
+ }
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;
261
+ }
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
+ };
285
+ if (options.settle !== void 0) {
286
+ context.settle = options.settle;
287
+ }
288
+ return verifier(context);
289
+ }
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
303
+ })
304
+ };
305
+ }
306
+ const context = {
307
+ attempt,
308
+ option,
309
+ definition
310
+ };
311
+ if (options.settle !== void 0) {
312
+ context.settle = options.settle;
313
+ }
314
+ return verifier(context);
315
+ }
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
341
+ }
342
+ };
343
+ } catch (error) {
344
+ 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
362
+ }
363
+ };
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
+ }
373
+ }
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);
407
+ return x402RequirementSchema.parse({
408
+ scheme: option.proof.scheme,
409
+ network: option.proof.network,
410
+ maxAmountRequired: units,
411
+ asset: assetAddress,
412
+ payTo: option.payTo,
413
+ resource: option.resource ?? fallbackResource,
414
+ description: option.description,
415
+ maxTimeoutSeconds: settlement?.windowSeconds,
416
+ extra: {
417
+ symbol: option.asset.symbol,
418
+ currencyCode: option.amount.currency.code,
419
+ decimals
420
+ }
421
+ });
422
+ }
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
+ function decimalToBaseUnits(value, decimals) {
435
+ const [whole, fraction = ""] = value.split(".");
436
+ const sanitizedFraction = fraction.slice(0, decimals);
437
+ const paddedFraction = sanitizedFraction.padEnd(decimals, "0");
438
+ const combined = `${whole}${paddedFraction}`.replace(/^0+/, "");
439
+ return combined.length > 0 ? combined : "0";
440
+ }
441
+ function decodeJson(value, schema) {
442
+ const base64 = normalizeBase64(value);
443
+ const json = Buffer.from(base64, "base64").toString("utf-8");
444
+ const parsed = JSON.parse(json);
445
+ return schema.parse(parsed);
446
+ }
447
+ function encodeJson(value) {
448
+ const json = JSON.stringify(value);
449
+ return Buffer.from(json, "utf-8").toString("base64");
450
+ }
451
+ function normalizeBase64(input) {
452
+ if (/^[A-Za-z0-9+/=]+$/.test(input)) {
453
+ return input;
454
+ }
455
+ const restored = input.replace(/-/g, "+").replace(/_/g, "/");
456
+ const paddingNeeded = (4 - restored.length % 4) % 4;
457
+ return restored + "=".repeat(paddingNeeded);
458
+ }
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
+ };
495
+ }
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
509
+ });
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
+ })
533
+ };
534
+ }
535
+ if (!settle) {
536
+ 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
+ })
544
+ };
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) {
561
+ return {
562
+ 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
+ })
570
+ };
571
+ }
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
+ };
584
+ }
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
+ }
599
+ };
600
+ } catch (error) {
601
+ 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
+ })
610
+ };
611
+ } finally {
612
+ if (timeout) {
613
+ clearTimeout(timeout);
614
+ }
615
+ }
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;
628
+ }
629
+ return headers;
630
+ }
631
+ function resolveFacilitatorApiKey(config) {
632
+ if (!config) {
633
+ return void 0;
634
+ }
635
+ if (config.apiKey) {
636
+ return config.apiKey;
637
+ }
638
+ if (config.apiKeyEnv && typeof process !== "undefined") {
639
+ return process.env?.[config.apiKeyEnv];
640
+ }
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"
662
+ }
663
+ }
664
+ };
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 {
671
+ constructor(response, verification) {
672
+ super("Payment required");
673
+ this.name = "PaymentRequiredError";
674
+ this.response = response;
675
+ this.verification = verification;
676
+ }
677
+ };
678
+ function setPaymentContext(request, context) {
679
+ try {
680
+ Object.defineProperty(request, PAYMENT_CONTEXT_SYMBOL, {
681
+ value: context,
682
+ configurable: true,
683
+ enumerable: false,
684
+ writable: true
685
+ });
686
+ } catch {
687
+ request[PAYMENT_CONTEXT_SYMBOL] = context;
688
+ }
689
+ }
690
+ function getPaymentContext(request) {
691
+ return request[PAYMENT_CONTEXT_SYMBOL];
692
+ }
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
+ }
738
+ const currencyCode = normalizeCurrency(config.currency);
739
+ const currencySpec = SUPPORTED_CURRENCIES[currencyCode];
740
+ if (!currencySpec) {
741
+ throw new Error(`Unsupported currency for payments: ${currencyCode}`);
742
+ }
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
+ })
817
+ );
818
+ }
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.chainIds) {
829
+ baseMetadata.chainIds = config.chainIds;
830
+ }
831
+ if (facilitatorLabel) {
832
+ baseMetadata.facilitator = facilitatorLabel;
833
+ }
834
+ const metadata = config.metadata ? { ...baseMetadata, ...config.metadata } : baseMetadata;
835
+ const definition = {
836
+ schemaVersion: PAYMENT_SCHEMA_VERSION,
837
+ accepts,
838
+ metadata
839
+ };
840
+ if (config.message !== void 0) {
841
+ definition.message = config.message;
842
+ }
843
+ if (config.resource !== void 0) {
844
+ definition.resource = config.resource;
845
+ }
846
+ const defined = {
847
+ definition,
848
+ verifiers,
849
+ metadata
850
+ };
851
+ if (config.message !== void 0) {
852
+ defined.message = config.message;
853
+ }
854
+ return defined;
855
+ }
856
+ async function requirePayment(request, payment, options = {}) {
857
+ const { definition, verifiers } = normalizePayment(payment);
858
+ const mergedVerifiers = {
859
+ ...verifiers,
860
+ ...options.verifiers ?? {}
861
+ };
862
+ const verifyOptions = {
863
+ definition,
864
+ request
865
+ };
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) {
873
+ verifyOptions.fetchImpl = options.fetchImpl;
874
+ }
875
+ const verification = await verifyPayment(verifyOptions);
876
+ if (!verification.success || !verification.metadata) {
877
+ if (options.onFailure) {
878
+ return options.onFailure(verification);
879
+ }
880
+ const response = paymentRequiredResponse(definition);
881
+ throw new PaymentRequiredError(response, verification);
882
+ }
883
+ return {
884
+ payment: verification.metadata,
885
+ headers: verification.responseHeaders ?? {},
886
+ optionId: verification.optionId,
887
+ result: verification
888
+ };
889
+ }
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: {}
900
+ };
901
+ }
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;
908
+ }
909
+ if (typeof value === "string") {
910
+ if (value in DEFAULT_FACILITATORS) {
911
+ return {
912
+ url: DEFAULT_FACILITATORS[value]
913
+ };
914
+ }
915
+ return { url: value };
916
+ }
917
+ return value;
918
+ }
919
+ function resolveFacilitatorLabel(value) {
920
+ if (!value) {
921
+ return "opentool";
922
+ }
923
+ if (typeof value === "string") {
924
+ if (value === "opentool" || value === "coinbase") {
925
+ return value;
926
+ }
927
+ return "custom";
928
+ }
929
+ return "custom";
930
+ }
931
+ function normalizeCurrency(currency) {
932
+ return (currency ?? "USDC").toUpperCase();
933
+ }
934
+ function toDecimalString(value) {
935
+ return typeof value === "number" ? value.toString() : value;
936
+ }
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
+
987
+ // src/adapters/mcp.ts
988
+ var HTTP_METHODS = [
989
+ "GET",
990
+ "HEAD",
991
+ "POST",
992
+ "PUT",
993
+ "DELETE",
994
+ "PATCH",
995
+ "OPTIONS"
996
+ ];
997
+ function createMcpAdapter(options) {
998
+ const normalizedSchema = ensureSchema(options.schema);
999
+ const defaultMethod = resolveDefaultMethod(options);
1000
+ const httpHandler = options.httpHandlers[defaultMethod];
1001
+ if (!httpHandler) {
1002
+ throw new Error(
1003
+ `Tool "${options.name}" does not export an HTTP handler for ${defaultMethod}`
1004
+ );
1005
+ }
1006
+ return async function invoke(rawArguments) {
1007
+ const validated = normalizedSchema ? normalizedSchema.parse(rawArguments ?? {}) : rawArguments;
1008
+ const request = buildRequest(options.name, defaultMethod, validated);
1009
+ try {
1010
+ const response = await Promise.resolve(httpHandler(request));
1011
+ return await responseToToolResponse(response);
1012
+ } catch (error) {
1013
+ if (error instanceof PaymentRequiredError) {
1014
+ return await responseToToolResponse(error.response);
1015
+ }
1016
+ throw error;
1017
+ }
1018
+ };
1019
+ }
1020
+ function resolveDefaultMethod(options) {
1021
+ const explicit = options.defaultMethod?.toUpperCase();
1022
+ if (explicit && typeof options.httpHandlers[explicit] === "function") {
1023
+ return explicit;
1024
+ }
1025
+ const preferredOrder = ["POST", "PUT", "PATCH", "GET", "DELETE", "OPTIONS", "HEAD"];
1026
+ for (const method of preferredOrder) {
1027
+ if (typeof options.httpHandlers[method] === "function") {
1028
+ return method;
1029
+ }
1030
+ }
1031
+ const available = Object.keys(options.httpHandlers).filter(
1032
+ (method) => typeof options.httpHandlers[method] === "function"
1033
+ );
1034
+ if (available.length > 0) {
1035
+ return available[0];
1036
+ }
1037
+ throw new Error(`No HTTP handlers available for tool "${options.name}"`);
1038
+ }
1039
+ function ensureSchema(schema) {
1040
+ if (!schema) {
1041
+ return void 0;
1042
+ }
1043
+ if (schema instanceof z.ZodType) {
1044
+ return schema;
1045
+ }
1046
+ if (typeof schema?.parse === "function") {
1047
+ return schema;
1048
+ }
1049
+ throw new Error("MCP adapter requires a valid Zod schema to validate arguments");
1050
+ }
1051
+ function buildRequest(name, method, params) {
1052
+ const url = new URL(`https://opentool.local/${encodeURIComponent(name)}`);
1053
+ const headers = new Headers({
1054
+ "x-opentool-invocation": "mcp",
1055
+ "x-opentool-tool": name
1056
+ });
1057
+ if (method === "GET" || method === "HEAD") {
1058
+ if (params && typeof params === "object") {
1059
+ Object.entries(params).forEach(([key, value]) => {
1060
+ if (value == null) {
1061
+ return;
1062
+ }
1063
+ url.searchParams.set(key, String(value));
1064
+ });
1065
+ }
1066
+ return new Request(url, { method, headers });
1067
+ }
1068
+ headers.set("Content-Type", "application/json");
1069
+ const init = { method, headers };
1070
+ if (params != null) {
1071
+ init.body = JSON.stringify(params);
1072
+ }
1073
+ return new Request(url, init);
1074
+ }
1075
+ async function responseToToolResponse(response) {
1076
+ const statusIsError = response.status >= 400;
1077
+ const contentType = response.headers.get("content-type") ?? "";
1078
+ const text = await response.text();
1079
+ if (contentType.includes("application/json")) {
1080
+ try {
1081
+ const payload = text ? JSON.parse(text) : {};
1082
+ if (payload && typeof payload === "object" && Array.isArray(payload.content)) {
1083
+ return {
1084
+ content: payload.content,
1085
+ isError: payload.isError ?? statusIsError
1086
+ };
1087
+ }
1088
+ return {
1089
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
1090
+ isError: statusIsError
1091
+ };
1092
+ } catch {
1093
+ return {
1094
+ content: [{ type: "text", text }],
1095
+ isError: statusIsError
1096
+ };
1097
+ }
1098
+ }
1099
+ if (!text) {
1100
+ return {
1101
+ content: [],
1102
+ isError: statusIsError
1103
+ };
1104
+ }
1105
+ return {
1106
+ content: [{ type: "text", text }],
1107
+ isError: statusIsError
1108
+ };
1109
+ }
1110
+
1111
+ // src/runtime/index.ts
1112
+ function createDevServer(tools) {
1113
+ const metadata = loadMetadata();
1114
+ const metadataMap = buildMetadataMap(metadata);
1115
+ const adapters = buildAdapters(tools);
1116
+ const server = new Server(
1117
+ {
1118
+ name: "opentool-dev",
1119
+ version: "1.0.0"
1120
+ },
1121
+ {
1122
+ capabilities: {
1123
+ tools: {}
1124
+ }
1125
+ }
1126
+ );
1127
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1128
+ tools: adapters.map(({ tool }) => serializeTool(tool, metadataMap))
1129
+ }));
1130
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1131
+ const entry = adapters.find(({ tool }) => {
1132
+ const toolName = tool.metadata?.name || tool.filename;
1133
+ return toolName === request.params.name;
1134
+ });
1135
+ if (!entry) {
1136
+ throw new Error(`Tool ${request.params.name} not found or not MCP-enabled`);
1137
+ }
1138
+ try {
1139
+ return await entry.invoke(request.params.arguments);
1140
+ } catch (error) {
1141
+ const message = error && error.message || String(error);
1142
+ return {
1143
+ content: [{ type: "text", text: `Error: ${message}` }],
1144
+ isError: true
1145
+ };
1146
+ }
1147
+ });
1148
+ return server;
1149
+ }
1150
+ async function createStdioServer(tools) {
1151
+ const metadata = loadMetadata();
1152
+ const metadataMap = buildMetadataMap(metadata);
1153
+ const toolDefinitions = tools || await loadToolsFromDirectory(metadataMap);
1154
+ const adapters = buildAdapters(toolDefinitions);
1155
+ const server = new Server(
1156
+ {
1157
+ name: "opentool-runtime",
1158
+ version: "1.0.0"
1159
+ },
1160
+ {
1161
+ capabilities: {
1162
+ tools: {}
1163
+ }
1164
+ }
1165
+ );
1166
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
1167
+ tools: adapters.map(({ tool }) => serializeTool(tool, metadataMap))
1168
+ }));
1169
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1170
+ const entry = adapters.find(({ tool }) => {
1171
+ const toolName = tool.metadata?.name || tool.filename;
1172
+ return toolName === request.params.name;
1173
+ });
1174
+ if (!entry) {
1175
+ throw new Error(`Tool ${request.params.name} not found or not MCP-enabled`);
1176
+ }
1177
+ try {
1178
+ return await entry.invoke(request.params.arguments);
1179
+ } catch (error) {
1180
+ const message = error && error.message || String(error);
1181
+ return {
1182
+ content: [{ type: "text", text: `Error: ${message}` }],
1183
+ isError: true
1184
+ };
1185
+ }
1186
+ });
1187
+ const transport = new StdioServerTransport();
1188
+ await server.connect(transport);
1189
+ console.error("MCP stdio server started");
1190
+ }
1191
+ function buildAdapters(tools) {
1192
+ return tools.filter((tool) => isMcpEnabled(tool)).map((tool) => {
1193
+ const httpHandlers = toHttpHandlerMap(tool.httpHandlers);
1194
+ const adapterOptions = {
1195
+ name: tool.metadata?.name || tool.filename,
1196
+ httpHandlers,
1197
+ ...tool.schema ? { schema: tool.schema } : {},
1198
+ ...tool.mcpConfig?.defaultMethod ? { defaultMethod: tool.mcpConfig.defaultMethod } : {}
1199
+ };
1200
+ const adapter = createMcpAdapter(adapterOptions);
1201
+ return {
1202
+ tool,
1203
+ invoke: adapter
1204
+ };
1205
+ });
1206
+ }
1207
+ async function loadToolsFromDirectory(metadataMap) {
1208
+ const tools = [];
1209
+ const toolsDir = path5.join(process.cwd(), "tools");
1210
+ if (!fs2.existsSync(toolsDir)) {
1211
+ return tools;
1212
+ }
1213
+ const files = fs2.readdirSync(toolsDir);
1214
+ for (const file of files) {
1215
+ if (!isSupportedToolFile(file)) {
1216
+ continue;
1217
+ }
1218
+ const toolPath = path5.join(toolsDir, file);
1219
+ try {
1220
+ const exportsObject = __require(toolPath);
1221
+ const candidate = resolveModuleCandidate(exportsObject);
1222
+ if (!candidate?.schema) {
1223
+ continue;
1224
+ }
1225
+ const baseName = file.replace(/\.[^.]+$/, "");
1226
+ const name = candidate.metadata?.name || baseName;
1227
+ const meta = metadataMap.get(name);
1228
+ let inputSchema = meta?.inputSchema;
1229
+ if (!inputSchema) {
1230
+ try {
1231
+ inputSchema = zodToJsonSchema(candidate.schema, {
1232
+ name: `${name}Schema`,
1233
+ target: "jsonSchema7",
1234
+ $refStrategy: "none"
1235
+ });
1236
+ } catch (error) {
1237
+ inputSchema = { type: "object" };
1238
+ }
1239
+ }
1240
+ inputSchema = normalizeInputSchema(inputSchema);
1241
+ const payment = candidate.payment ?? null;
1242
+ const httpHandlersRaw = collectHttpHandlers(candidate);
1243
+ const httpHandlers = [...httpHandlersRaw];
1244
+ if (httpHandlers.length === 0) {
1245
+ continue;
1246
+ }
1247
+ if (payment) {
1248
+ for (let index = 0; index < httpHandlers.length; index += 1) {
1249
+ const entry = httpHandlers[index];
1250
+ httpHandlers[index] = {
1251
+ ...entry,
1252
+ handler: withPaymentRequirement(entry.handler, payment)
1253
+ };
1254
+ }
1255
+ }
1256
+ const mcpConfig = normalizeRuntimeMcpConfig(candidate.mcp);
1257
+ const adapterOptions = {
1258
+ name,
1259
+ httpHandlers: toHttpHandlerMap(httpHandlers),
1260
+ ...candidate.schema ? { schema: candidate.schema } : {},
1261
+ ...typeof candidate.mcp?.defaultMethod === "string" ? { defaultMethod: candidate.mcp.defaultMethod } : {}
1262
+ };
1263
+ const adapter = createMcpAdapter(adapterOptions);
1264
+ const tool = {
1265
+ ...candidate.schema ? { schema: candidate.schema } : {},
1266
+ inputSchema,
1267
+ metadata: candidate.metadata || meta || null,
1268
+ filename: baseName,
1269
+ httpHandlers,
1270
+ mcpConfig,
1271
+ handler: async (params) => adapter(params),
1272
+ payment
1273
+ };
1274
+ tools.push(tool);
1275
+ } catch (error) {
1276
+ console.warn(`Failed to load tool from ${file}: ${error}`);
1277
+ }
1278
+ }
1279
+ return tools;
1280
+ }
1281
+ function loadMetadata() {
1282
+ const metadataPath = path5.join(process.cwd(), "metadata.json");
1283
+ if (!fs2.existsSync(metadataPath)) {
1284
+ return null;
1285
+ }
1286
+ try {
1287
+ const contents = fs2.readFileSync(metadataPath, "utf8");
1288
+ return JSON.parse(contents);
1289
+ } catch (error) {
1290
+ console.warn(`Failed to parse metadata.json: ${error}`);
1291
+ return null;
1292
+ }
1293
+ }
1294
+ function buildMetadataMap(metadata) {
1295
+ const map = /* @__PURE__ */ new Map();
1296
+ if (!metadata?.tools) {
1297
+ return map;
1298
+ }
1299
+ metadata.tools.forEach((tool) => {
1300
+ map.set(tool.name, tool);
1301
+ });
1302
+ return map;
1303
+ }
1304
+ function serializeTool(tool, metadataMap) {
1305
+ const name = tool.metadata?.name || tool.filename;
1306
+ const meta = metadataMap.get(name);
1307
+ return {
1308
+ name,
1309
+ description: meta?.description || tool.metadata?.description || `${tool.filename} tool`,
1310
+ inputSchema: meta?.inputSchema || tool.inputSchema,
1311
+ annotations: meta?.annotations || tool.metadata?.annotations,
1312
+ payment: meta?.payment || tool.metadata?.payment,
1313
+ discovery: meta?.discovery || tool.metadata?.discovery
1314
+ };
1315
+ }
1316
+ function isSupportedToolFile(file) {
1317
+ return /\.(cjs|mjs|js|ts)$/i.test(file);
1318
+ }
1319
+ function resolveModuleCandidate(exportsObject) {
1320
+ if (!exportsObject) {
1321
+ return null;
1322
+ }
1323
+ if (exportsObject.schema) {
1324
+ return exportsObject;
1325
+ }
1326
+ if (exportsObject.default && exportsObject.default.schema) {
1327
+ return exportsObject.default;
1328
+ }
1329
+ return exportsObject;
1330
+ }
1331
+ function collectHttpHandlers(module) {
1332
+ const handlers = [];
1333
+ HTTP_METHODS.forEach((method) => {
1334
+ const handler = module?.[method];
1335
+ if (typeof handler === "function") {
1336
+ handlers.push({
1337
+ method,
1338
+ handler: async (request) => handler.call(module, request)
1339
+ });
1340
+ }
1341
+ });
1342
+ return handlers;
1343
+ }
1344
+ function toHttpHandlerMap(handlers) {
1345
+ return handlers.reduce((acc, handler) => {
1346
+ acc[handler.method.toUpperCase()] = handler.handler;
1347
+ return acc;
1348
+ }, {});
1349
+ }
1350
+ function normalizeInputSchema(schema) {
1351
+ if (!schema || typeof schema !== "object") {
1352
+ return schema;
1353
+ }
1354
+ const clone = JSON.parse(JSON.stringify(schema));
1355
+ if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
1356
+ const refKey = clone.$ref.replace("#/definitions/", "");
1357
+ if (clone.definitions && typeof clone.definitions[refKey] === "object") {
1358
+ return normalizeInputSchema(clone.definitions[refKey]);
1359
+ }
1360
+ }
1361
+ delete clone.$ref;
1362
+ delete clone.definitions;
1363
+ if (!clone.type) {
1364
+ clone.type = "object";
1365
+ }
1366
+ return clone;
1367
+ }
1368
+ function normalizeRuntimeMcpConfig(rawConfig) {
1369
+ if (isPlainObject(rawConfig) && rawConfig.enabled === true) {
1370
+ let normalizedMode;
1371
+ if (typeof rawConfig.mode === "string") {
1372
+ const candidate = rawConfig.mode.toLowerCase();
1373
+ if (candidate === "stdio" || candidate === "lambda" || candidate === "dual") {
1374
+ normalizedMode = candidate;
1375
+ } else {
1376
+ throw new Error('mcp.mode must be one of "stdio", "lambda", or "dual"');
1377
+ }
1378
+ }
1379
+ const metadataOverrides = isPlainObject(rawConfig.metadataOverrides) ? rawConfig.metadataOverrides : void 0;
1380
+ const config = { enabled: true };
1381
+ if (normalizedMode) {
1382
+ config.mode = normalizedMode;
1383
+ }
1384
+ if (typeof rawConfig.defaultMethod === "string") {
1385
+ config.defaultMethod = rawConfig.defaultMethod.toUpperCase();
1386
+ }
1387
+ if (metadataOverrides) {
1388
+ config.metadataOverrides = metadataOverrides;
1389
+ }
1390
+ return config;
1391
+ }
1392
+ return null;
1393
+ }
1394
+ function isPlainObject(value) {
1395
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1396
+ }
1397
+ function isMcpEnabled(tool) {
1398
+ return Boolean(tool.mcpConfig?.enabled);
1399
+ }
1400
+ function resolveRuntimePath(value) {
1401
+ if (value.startsWith("file://")) {
1402
+ return fileURLToPath(value);
1403
+ }
1404
+ return path5.resolve(value);
1405
+ }
1406
+
1407
+ // src/types/index.ts
1408
+ var HTTP_METHODS2 = [
1409
+ "GET",
1410
+ "HEAD",
1411
+ "POST",
1412
+ "PUT",
1413
+ "DELETE",
1414
+ "PATCH",
1415
+ "OPTIONS"
1416
+ ];
1417
+ var BASE_ALCHEMY_HOST = "https://base-mainnet.g.alchemy.com/v2/";
1418
+ var ETHEREUM_ALCHEMY_HOST = "https://eth-mainnet.g.alchemy.com/v2/";
1419
+ var BASE_SEPOLIA_ALCHEMY_HOST = "https://base-sepolia.g.alchemy.com/v2/";
1420
+ function buildRpcResolver(host, fallbackUrls) {
1421
+ return (options) => {
1422
+ if (options?.url) {
1423
+ return options.url;
1424
+ }
1425
+ if (options?.apiKey) {
1426
+ return `${host}${options.apiKey}`;
1427
+ }
1428
+ if (fallbackUrls.length > 0) {
1429
+ return fallbackUrls[0];
1430
+ }
1431
+ throw new Error(
1432
+ "No RPC URL available: supply a full url via options or an apiKey for the default host"
1433
+ );
1434
+ };
1435
+ }
1436
+ var chains = {
1437
+ base: {
1438
+ id: base.id,
1439
+ slug: "base",
1440
+ name: "Base",
1441
+ chain: base,
1442
+ rpcUrl: buildRpcResolver(BASE_ALCHEMY_HOST, base.rpcUrls.default.http),
1443
+ publicRpcUrls: base.rpcUrls.default.http
1444
+ },
1445
+ ethereum: {
1446
+ id: mainnet.id,
1447
+ slug: "ethereum",
1448
+ name: "Ethereum",
1449
+ chain: mainnet,
1450
+ rpcUrl: buildRpcResolver(
1451
+ ETHEREUM_ALCHEMY_HOST,
1452
+ mainnet.rpcUrls.default.http
1453
+ ),
1454
+ publicRpcUrls: mainnet.rpcUrls.default.http
1455
+ },
1456
+ baseSepolia: {
1457
+ id: baseSepolia.id,
1458
+ slug: "base-sepolia",
1459
+ name: "Base Sepolia",
1460
+ chain: baseSepolia,
1461
+ rpcUrl: buildRpcResolver(
1462
+ BASE_SEPOLIA_ALCHEMY_HOST,
1463
+ baseSepolia.rpcUrls.default.http
1464
+ )
1465
+ }
1466
+ };
1467
+ function createNativeToken(chainId, symbol, name) {
1468
+ return {
1469
+ [symbol]: {
1470
+ symbol,
1471
+ name,
1472
+ decimals: 18,
1473
+ address: zeroAddress,
1474
+ chainId,
1475
+ isNative: true
1476
+ }
1477
+ };
1478
+ }
1479
+ function token(chainId, symbol, name, address, decimals) {
1480
+ return {
1481
+ symbol,
1482
+ name,
1483
+ decimals,
1484
+ address,
1485
+ chainId
1486
+ };
1487
+ }
1488
+ var tokens = {
1489
+ base: {
1490
+ ...createNativeToken(base.id, "ETH", "Ether"),
1491
+ USDC: token(
1492
+ base.id,
1493
+ "USDC",
1494
+ "USD Coin",
1495
+ "0x833589fCD6eDb6E08f4c7C31c9A8Ba32D74b86B2",
1496
+ 6
1497
+ )
1498
+ },
1499
+ ethereum: {
1500
+ ...createNativeToken(mainnet.id, "ETH", "Ether"),
1501
+ USDC: token(
1502
+ mainnet.id,
1503
+ "USDC",
1504
+ "USD Coin",
1505
+ "0xA0b86991c6218b36c1d19d4a2e9Eb0cE3606eB48",
1506
+ 6
1507
+ )
1508
+ }
1509
+ };
1510
+ var DEFAULT_CHAIN = chains.base;
1511
+ var DEFAULT_TOKENS = tokens.base;
1512
+ var registry = {
1513
+ chains,
1514
+ tokens
1515
+ };
1516
+ function normalizePrivateKey(raw) {
1517
+ const trimmed = raw.trim();
1518
+ const withPrefix = trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
1519
+ if (!/^0x[0-9a-fA-F]{64}$/.test(withPrefix)) {
1520
+ throw new Error("wallet() privateKey must be a 32-byte hex string");
1521
+ }
1522
+ return withPrefix;
1523
+ }
1524
+ function createPrivateKeyProvider(config) {
1525
+ const privateKey = normalizePrivateKey(config.privateKey);
1526
+ const account = privateKeyToAccount(privateKey);
1527
+ const transport = http(config.rpcUrl);
1528
+ const publicClient = createPublicClient({
1529
+ chain: config.chain.chain,
1530
+ transport
1531
+ });
1532
+ const walletClient = createWalletClient({
1533
+ account,
1534
+ chain: config.chain.chain,
1535
+ transport
1536
+ });
1537
+ async function sendTransaction(params) {
1538
+ const tx = {
1539
+ account
1540
+ };
1541
+ if (params.to) {
1542
+ tx.to = params.to;
1543
+ }
1544
+ if (params.value !== void 0) {
1545
+ tx.value = params.value;
1546
+ }
1547
+ if (params.data !== void 0) {
1548
+ tx.data = params.data;
1549
+ }
1550
+ return walletClient.sendTransaction(tx);
1551
+ }
1552
+ async function getNativeBalance() {
1553
+ return publicClient.getBalance({ address: account.address });
1554
+ }
1555
+ async function transfer(params) {
1556
+ return sendTransaction({
1557
+ to: params.to,
1558
+ value: params.amount,
1559
+ ...params.data !== void 0 ? { data: params.data } : {}
1560
+ });
1561
+ }
1562
+ return {
1563
+ address: account.address,
1564
+ account,
1565
+ walletClient,
1566
+ publicClient,
1567
+ sendTransaction,
1568
+ getNativeBalance,
1569
+ transfer
1570
+ };
1571
+ }
1572
+ async function createTurnkeyProvider(config) {
1573
+ const turnkey = new Turnkey({
1574
+ apiBaseUrl: config.apiBaseUrl ?? "https://api.turnkey.com",
1575
+ // The delegated sub-organization the API key pair belongs to.
1576
+ defaultOrganizationId: config.organizationId,
1577
+ apiPublicKey: config.apiPublicKey,
1578
+ apiPrivateKey: config.apiPrivateKey
1579
+ });
1580
+ const account = await createAccount({
1581
+ client: turnkey.apiClient(),
1582
+ organizationId: config.organizationId,
1583
+ signWith: config.signWith
1584
+ });
1585
+ const transport = http(config.rpcUrl);
1586
+ const publicClient = createPublicClient({
1587
+ chain: config.chain.chain,
1588
+ transport
1589
+ });
1590
+ const walletClient = createWalletClient({
1591
+ account,
1592
+ chain: config.chain.chain,
1593
+ transport
1594
+ });
1595
+ async function sendTransaction(params) {
1596
+ const tx = {
1597
+ account
1598
+ };
1599
+ if (params.to) {
1600
+ tx.to = params.to;
1601
+ }
1602
+ if (params.value !== void 0) {
1603
+ tx.value = params.value;
1604
+ }
1605
+ if (params.data !== void 0) {
1606
+ tx.data = params.data;
1607
+ }
1608
+ return walletClient.sendTransaction(tx);
1609
+ }
1610
+ async function getNativeBalance() {
1611
+ return publicClient.getBalance({ address: account.address });
1612
+ }
1613
+ async function transfer(params) {
1614
+ return sendTransaction({
1615
+ to: params.to,
1616
+ value: params.amount,
1617
+ ...params.data !== void 0 ? { data: params.data } : {}
1618
+ });
1619
+ }
1620
+ return {
1621
+ address: account.address,
1622
+ account,
1623
+ walletClient,
1624
+ publicClient,
1625
+ sendTransaction,
1626
+ getNativeBalance,
1627
+ transfer
1628
+ };
1629
+ }
1630
+
1631
+ // src/wallets/index.ts
1632
+ function resolveChainSlug(reference) {
1633
+ if (reference === void 0) {
1634
+ return Object.entries(chains).find(([, meta]) => meta.id === DEFAULT_CHAIN.id)?.[0] || DEFAULT_CHAIN.slug;
1635
+ }
1636
+ if (typeof reference === "number") {
1637
+ const match = Object.entries(chains).find(([, meta]) => meta.id === reference);
1638
+ if (match) {
1639
+ return match[0];
1640
+ }
1641
+ } else if (typeof reference === "string") {
1642
+ const sanitize = (value) => value.toLowerCase().replace(/[^a-z0-9]/g, "");
1643
+ if (reference in chains) {
1644
+ return reference;
1645
+ }
1646
+ const normalized = sanitize(reference);
1647
+ const keyMatch = Object.entries(chains).find(([key]) => sanitize(key) === normalized);
1648
+ if (keyMatch) {
1649
+ return keyMatch[0];
1650
+ }
1651
+ const slugMatch = Object.entries(chains).find(([, meta]) => {
1652
+ return meta.slug && sanitize(meta.slug) === normalized;
1653
+ });
1654
+ if (slugMatch) {
1655
+ return slugMatch[0];
1656
+ }
1657
+ const asNumber = Number.parseInt(normalized, 10);
1658
+ if (!Number.isNaN(asNumber)) {
1659
+ const match = Object.entries(chains).find(([, meta]) => meta.id === asNumber);
1660
+ if (match) {
1661
+ return match[0];
1662
+ }
1663
+ }
1664
+ }
1665
+ throw new Error(`Unknown chain reference: ${reference}`);
1666
+ }
1667
+ function getRpcUrl(chain, options) {
1668
+ const slug = resolveChainSlug(chain);
1669
+ const entry = chains[slug];
1670
+ return entry.rpcUrl(options);
1671
+ }
1672
+ async function wallet(options = {}) {
1673
+ if (options.privateKey && options.turnkey) {
1674
+ throw new Error("wallet() cannot be initialized with both privateKey and turnkey credentials");
1675
+ }
1676
+ const slug = resolveChainSlug(options.chain);
1677
+ const chain = chains[slug];
1678
+ const tokens2 = tokens[slug] ?? {};
1679
+ const overrides = {};
1680
+ if (options.rpcUrl) {
1681
+ overrides.url = options.rpcUrl;
1682
+ }
1683
+ if (options.apiKey) {
1684
+ overrides.apiKey = options.apiKey;
1685
+ }
1686
+ const rpcUrl = getRpcUrl(slug, overrides);
1687
+ let providerType = "readonly";
1688
+ let signerProvider;
1689
+ if (options.privateKey) {
1690
+ signerProvider = createPrivateKeyProvider({
1691
+ chain,
1692
+ rpcUrl,
1693
+ privateKey: options.privateKey
1694
+ });
1695
+ providerType = "privateKey";
1696
+ } else if (options.turnkey) {
1697
+ const turnkeyConfig = {
1698
+ chain,
1699
+ rpcUrl,
1700
+ organizationId: options.turnkey.organizationId,
1701
+ apiPublicKey: options.turnkey.apiPublicKey,
1702
+ apiPrivateKey: options.turnkey.apiPrivateKey,
1703
+ signWith: options.turnkey.signWith
1704
+ };
1705
+ if (options.turnkey.apiBaseUrl) {
1706
+ turnkeyConfig.apiBaseUrl = options.turnkey.apiBaseUrl;
1707
+ }
1708
+ signerProvider = await createTurnkeyProvider(turnkeyConfig);
1709
+ providerType = "turnkey";
1710
+ }
1711
+ const publicClient = signerProvider?.publicClient ?? createPublicClient({
1712
+ chain: chain.chain,
1713
+ transport: http(rpcUrl)
1714
+ });
1715
+ const baseContext = {
1716
+ chain,
1717
+ tokens: tokens2,
1718
+ rpcUrl,
1719
+ providerType,
1720
+ publicClient,
1721
+ getRpcUrl: (override) => getRpcUrl(slug, override)
1722
+ };
1723
+ if (signerProvider) {
1724
+ const { publicClient: _ignored, ...rest } = signerProvider;
1725
+ return {
1726
+ ...baseContext,
1727
+ ...rest
1728
+ };
1729
+ }
1730
+ return baseContext;
1731
+ }
1732
+ var walletToolkit = {
1733
+ chains,
1734
+ tokens,
1735
+ registry,
1736
+ defaults: {
1737
+ chain: DEFAULT_CHAIN,
1738
+ tokens: DEFAULT_TOKENS
1739
+ },
1740
+ getRpcUrl,
1741
+ wallet
1742
+ };
1743
+
1744
+ // src/ai/errors.ts
1745
+ var AIError = class extends Error {
1746
+ constructor(message, options) {
1747
+ super(message);
1748
+ this.name = "AIError";
1749
+ if (options && "cause" in options) {
1750
+ this.cause = options.cause;
1751
+ }
1752
+ }
1753
+ };
1754
+ var AIFetchError = class extends AIError {
1755
+ constructor(message, options) {
1756
+ super(message, options);
1757
+ this.name = "AIFetchError";
1758
+ }
1759
+ };
1760
+ var AIResponseError = class extends AIError {
1761
+ constructor(details, message) {
1762
+ super(message ?? `AI response error: ${details.status} ${details.statusText}`);
1763
+ this.name = "AIResponseError";
1764
+ this.status = details.status;
1765
+ this.statusText = details.statusText;
1766
+ this.body = details.body;
1767
+ this.headers = details.headers ?? {};
1768
+ }
1769
+ };
1770
+ var AIAbortError = class extends AIError {
1771
+ constructor(message = "AI request aborted") {
1772
+ super(message);
1773
+ this.name = "AIAbortError";
1774
+ }
1775
+ };
1776
+
1777
+ // src/ai/config.ts
1778
+ var DEFAULT_BASE_URL = "https://gateway.openpond.dev";
1779
+ var DEFAULT_TIMEOUT_MS = 6e4;
1780
+ var DEFAULT_MODEL = "openai/gpt-5-mini";
1781
+ function assertFetchAvailable(fetchImplementation) {
1782
+ if (!fetchImplementation) {
1783
+ throw new Error(
1784
+ "No fetch implementation available. Provide one via AIClientConfig.fetchImplementation."
1785
+ );
1786
+ }
1787
+ }
1788
+ function resolveConfig(config = {}) {
1789
+ const fetchImplementation = config.fetchImplementation ?? globalThis.fetch;
1790
+ assertFetchAvailable(fetchImplementation);
1791
+ const resolved = {
1792
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
1793
+ defaultModel: config.defaultModel ?? DEFAULT_MODEL,
1794
+ defaultHeaders: {
1795
+ "Content-Type": "application/json",
1796
+ ...config.defaultHeaders
1797
+ },
1798
+ fetchImplementation,
1799
+ timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
1800
+ };
1801
+ if (config.apiKey !== void 0) {
1802
+ resolved.apiKey = config.apiKey;
1803
+ }
1804
+ return resolved;
1805
+ }
1806
+ function mergeHeaders(base2, overrides) {
1807
+ if (!overrides) {
1808
+ return { ...base2 };
1809
+ }
1810
+ const merged = { ...base2 };
1811
+ for (const [key, value] of Object.entries(overrides)) {
1812
+ if (value === void 0) {
1813
+ continue;
1814
+ }
1815
+ merged[key] = value;
1816
+ }
1817
+ return merged;
1818
+ }
1819
+
1820
+ // src/ai/models.ts
1821
+ var MODEL_REGISTRY = [
1822
+ {
1823
+ name: "openai/gpt-5-mini",
1824
+ label: "OpenAI GPT-5 Mini",
1825
+ provider: "openai",
1826
+ supportsStreaming: true,
1827
+ supportsTools: true,
1828
+ reasoning: true,
1829
+ aliases: ["gpt-5-mini", "gpt5-mini", "gpt-5.0-mini"],
1830
+ default: true
1831
+ },
1832
+ {
1833
+ name: "anthropic/claude-4-sonnet-20250514",
1834
+ label: "Claude 4 Sonnet (20250514)",
1835
+ provider: "anthropic",
1836
+ supportsStreaming: true,
1837
+ supportsTools: true,
1838
+ aliases: ["claude-4-sonnet", "claude-sonnet"]
1839
+ },
1840
+ {
1841
+ name: "google/gemini-2.0-flash-001",
1842
+ label: "Gemini 2.0 Flash",
1843
+ provider: "google",
1844
+ supportsStreaming: true,
1845
+ supportsTools: true,
1846
+ aliases: ["gemini-2.0-flash", "gemini-flash"]
1847
+ },
1848
+ {
1849
+ name: "deepseek/deepseek-chat",
1850
+ label: "DeepSeek Chat",
1851
+ provider: "deepseek",
1852
+ supportsStreaming: true,
1853
+ supportsTools: true,
1854
+ aliases: ["deepseek-chat", "deepseek"]
1855
+ }
1856
+ ];
1857
+ var ALIAS_LOOKUP = MODEL_REGISTRY.reduce(
1858
+ (accumulator, model) => {
1859
+ accumulator[model.name.toLowerCase()] = model.name;
1860
+ if (model.aliases) {
1861
+ for (const alias of model.aliases) {
1862
+ accumulator[alias.toLowerCase()] = model.name;
1863
+ }
1864
+ }
1865
+ return accumulator;
1866
+ },
1867
+ {}
1868
+ );
1869
+ var DEFAULT_MODEL_NAME = MODEL_REGISTRY.find((model) => model.default)?.name ?? MODEL_REGISTRY[0].name;
1870
+ function listModels() {
1871
+ return [...MODEL_REGISTRY];
1872
+ }
1873
+ function getModelConfig(modelName) {
1874
+ if (!modelName) {
1875
+ return MODEL_REGISTRY.find((model) => model.default) ?? MODEL_REGISTRY[0];
1876
+ }
1877
+ const normalized = normalizeModelName(modelName);
1878
+ return MODEL_REGISTRY.find((model) => model.name === normalized);
1879
+ }
1880
+ function normalizeModelName(modelName) {
1881
+ if (!modelName) {
1882
+ return DEFAULT_MODEL_NAME;
1883
+ }
1884
+ const trimmed = modelName.trim();
1885
+ if (!trimmed) {
1886
+ return DEFAULT_MODEL_NAME;
1887
+ }
1888
+ const directMatch = ALIAS_LOOKUP[trimmed.toLowerCase()];
1889
+ if (directMatch) {
1890
+ return directMatch;
1891
+ }
1892
+ if (trimmed.includes("/")) {
1893
+ return trimmed;
1894
+ }
1895
+ return `openai/${trimmed}`;
1896
+ }
1897
+ function isStreamingSupported(modelName) {
1898
+ const config = getModelConfig(modelName);
1899
+ return config ? config.supportsStreaming : true;
1900
+ }
1901
+ function isToolCallingSupported(modelName) {
1902
+ const config = getModelConfig(modelName);
1903
+ return config ? config.supportsTools : true;
1904
+ }
1905
+
1906
+ // src/ai/tools.ts
1907
+ var WEBSEARCH_TOOL_NAME = "websearch";
1908
+ var WEBSEARCH_TOOL_DEFINITION = {
1909
+ type: "function",
1910
+ function: {
1911
+ name: WEBSEARCH_TOOL_NAME,
1912
+ description: "Search the web using the OpenPond search engine. Returns relevant results with titles, URLs, and text content.",
1913
+ parameters: {
1914
+ type: "object",
1915
+ properties: {
1916
+ query: {
1917
+ type: "string",
1918
+ description: "The search query"
1919
+ },
1920
+ limit: {
1921
+ type: "number",
1922
+ description: "Maximum number of results to return (default: 5)"
1923
+ }
1924
+ },
1925
+ required: ["query"]
1926
+ }
1927
+ }
1928
+ };
1929
+ function resolveToolset(tools, policy) {
1930
+ if (!policy) {
1931
+ return tools;
1932
+ }
1933
+ const resolved = tools ? [...tools] : [];
1934
+ if (policy.webSearch) {
1935
+ const alreadyIncluded = resolved.some(
1936
+ (tool) => tool.type === "function" && tool.function?.name === WEBSEARCH_TOOL_NAME
1937
+ );
1938
+ if (!alreadyIncluded) {
1939
+ resolved.push(materializeWebSearchTool(policy.webSearch));
1940
+ }
1941
+ }
1942
+ return resolved.length > 0 ? resolved : void 0;
1943
+ }
1944
+ function materializeWebSearchTool(options) {
1945
+ if (!options || Object.keys(options).length === 0) {
1946
+ return WEBSEARCH_TOOL_DEFINITION;
1947
+ }
1948
+ const baseParameters = WEBSEARCH_TOOL_DEFINITION.function.parameters ?? {};
1949
+ const baseProperties = baseParameters.properties ?? {};
1950
+ const properties = { ...baseProperties };
1951
+ if (options.limit !== void 0) {
1952
+ const existingLimit = baseProperties["limit"];
1953
+ const limitSchema = typeof existingLimit === "object" && existingLimit !== null ? { ...existingLimit } : {
1954
+ type: "number",
1955
+ description: "Maximum number of results to return (default: 5)"
1956
+ };
1957
+ limitSchema.default = options.limit;
1958
+ properties.limit = limitSchema;
1959
+ }
1960
+ if (options.includeImages) {
1961
+ properties.includeImages = {
1962
+ type: "boolean",
1963
+ description: "Whether to include representative images in results.",
1964
+ default: true
1965
+ };
1966
+ }
1967
+ return {
1968
+ ...WEBSEARCH_TOOL_DEFINITION,
1969
+ function: {
1970
+ ...WEBSEARCH_TOOL_DEFINITION.function,
1971
+ parameters: {
1972
+ ...WEBSEARCH_TOOL_DEFINITION.function.parameters,
1973
+ properties
1974
+ }
1975
+ }
1976
+ };
1977
+ }
1978
+
1979
+ // src/ai/messages.ts
1980
+ function flattenMessageContent(content, options = {}) {
1981
+ if (typeof content === "string") {
1982
+ return content;
1983
+ }
1984
+ if (!Array.isArray(content)) {
1985
+ return void 0;
1986
+ }
1987
+ const separator = options.separator ?? "";
1988
+ const collected = [];
1989
+ for (const part of content) {
1990
+ const text = extractTextPart(part, options);
1991
+ if (text) {
1992
+ collected.push(text);
1993
+ }
1994
+ }
1995
+ if (collected.length === 0) {
1996
+ return void 0;
1997
+ }
1998
+ return collected.join(separator);
1999
+ }
2000
+ function ensureTextContent(message, options) {
2001
+ const flattened = flattenMessageContent(message.content, options);
2002
+ if (flattened !== void 0) {
2003
+ return flattened;
2004
+ }
2005
+ throw new AIError(
2006
+ options?.errorMessage ?? "Assistant response did not contain textual content."
2007
+ );
2008
+ }
2009
+ function extractTextPart(part, options) {
2010
+ if (!part || typeof part !== "object") {
2011
+ return void 0;
2012
+ }
2013
+ if ("text" in part && typeof part.text === "string") {
2014
+ return part.text;
2015
+ }
2016
+ if (options.includeUnknown) {
2017
+ try {
2018
+ return JSON.stringify(part);
2019
+ } catch (error) {
2020
+ return `[unserializable_part: ${String(error)}]`;
2021
+ }
2022
+ }
2023
+ return void 0;
2024
+ }
2025
+
2026
+ // src/ai/client.ts
2027
+ var CHAT_COMPLETIONS_PATH = "/v1/chat/completions";
2028
+ function createAIClient(config = {}) {
2029
+ const resolved = resolveConfig(config);
2030
+ return {
2031
+ get config() {
2032
+ return resolved;
2033
+ },
2034
+ async generateText(options) {
2035
+ return generateText(options, config);
2036
+ },
2037
+ async streamText(options) {
2038
+ return streamText(options, config);
2039
+ },
2040
+ listModels
2041
+ };
2042
+ }
2043
+ async function generateText(options, clientConfig = {}) {
2044
+ const resolved = resolveConfig(clientConfig);
2045
+ const model = normalizeModelName(options.model ?? resolved.defaultModel);
2046
+ const payload = buildRequestPayload(options, model, {
2047
+ allowTools: isToolCallingSupported(model)
2048
+ });
2049
+ const headers = mergeHeaders(resolved.defaultHeaders, options.headers);
2050
+ if (resolved.apiKey) {
2051
+ headers.Authorization = `Bearer ${resolved.apiKey}`;
2052
+ }
2053
+ const endpoint = buildUrl(resolved.baseUrl, CHAT_COMPLETIONS_PATH);
2054
+ const abortBundle = createAbortBundle(
2055
+ options.abortSignal,
2056
+ options.timeoutMs ?? resolved.timeoutMs
2057
+ );
2058
+ let response;
2059
+ try {
2060
+ response = await resolved.fetchImplementation(endpoint, {
2061
+ method: "POST",
2062
+ headers,
2063
+ body: JSON.stringify(payload),
2064
+ signal: abortBundle.signal
2065
+ });
2066
+ } catch (error) {
2067
+ if (abortBundle.signal.aborted) {
2068
+ throw toAbortError(abortBundle.signal.reason ?? error);
2069
+ }
2070
+ throw new AIFetchError("Failed to reach AI gateway", { cause: error });
2071
+ } finally {
2072
+ abortBundle.cleanup();
2073
+ }
2074
+ if (!response.ok) {
2075
+ const errorBody = await safeParseJson(response);
2076
+ throw new AIResponseError({
2077
+ status: response.status,
2078
+ statusText: response.statusText,
2079
+ body: errorBody,
2080
+ headers: collectHeaders(response.headers)
2081
+ });
2082
+ }
2083
+ const data = await response.json();
2084
+ const primaryChoice = data.choices.find(isPrimaryChoice);
2085
+ if (!primaryChoice) {
2086
+ throw new AIResponseError(
2087
+ {
2088
+ status: response.status,
2089
+ statusText: response.statusText,
2090
+ body: data
2091
+ },
2092
+ "Gateway response did not contain a valid choice"
2093
+ );
2094
+ }
2095
+ const result = {
2096
+ id: data.id,
2097
+ model: data.model,
2098
+ message: primaryChoice.message,
2099
+ raw: data
2100
+ };
2101
+ if (primaryChoice.finish_reason !== void 0) {
2102
+ result.finishReason = primaryChoice.finish_reason;
2103
+ }
2104
+ if (data.usage) {
2105
+ result.usage = data.usage;
2106
+ }
2107
+ return result;
2108
+ }
2109
+ async function streamText(options, clientConfig = {}) {
2110
+ const resolved = resolveConfig(clientConfig);
2111
+ const model = normalizeModelName(options.model ?? resolved.defaultModel);
2112
+ const streamExtras = buildStreamMetadataExtras(options);
2113
+ const payload = buildRequestPayload(
2114
+ options,
2115
+ model,
2116
+ {
2117
+ allowTools: isToolCallingSupported(model)
2118
+ },
2119
+ streamExtras
2120
+ );
2121
+ payload.stream = true;
2122
+ if (options.includeUsage) {
2123
+ payload.stream_options = { include_usage: true };
2124
+ }
2125
+ const headers = mergeHeaders(resolved.defaultHeaders, options.headers);
2126
+ if (resolved.apiKey) {
2127
+ headers.Authorization = `Bearer ${resolved.apiKey}`;
2128
+ }
2129
+ const endpoint = buildUrl(resolved.baseUrl, CHAT_COMPLETIONS_PATH);
2130
+ const abortBundle = createAbortBundle(
2131
+ options.abortSignal,
2132
+ options.timeoutMs ?? resolved.timeoutMs
2133
+ );
2134
+ let response;
2135
+ try {
2136
+ response = await resolved.fetchImplementation(endpoint, {
2137
+ method: "POST",
2138
+ headers,
2139
+ body: JSON.stringify(payload),
2140
+ signal: abortBundle.signal
2141
+ });
2142
+ } catch (error) {
2143
+ if (abortBundle.signal.aborted) {
2144
+ throw toAbortError(abortBundle.signal.reason ?? error);
2145
+ }
2146
+ throw new AIFetchError("Failed to reach AI gateway", { cause: error });
2147
+ }
2148
+ if (!response.ok) {
2149
+ const errorBody = await safeParseJson(response);
2150
+ abortBundle.cleanup();
2151
+ throw new AIResponseError({
2152
+ status: response.status,
2153
+ statusText: response.statusText,
2154
+ body: errorBody,
2155
+ headers: collectHeaders(response.headers)
2156
+ });
2157
+ }
2158
+ if (!response.body) {
2159
+ abortBundle.cleanup();
2160
+ throw new AIFetchError("Streaming response did not include a readable body");
2161
+ }
2162
+ const reader = response.body.getReader();
2163
+ const decoder = new TextDecoder();
2164
+ const handlers = options.handlers ?? {};
2165
+ let finishedResolve;
2166
+ let finishedReject;
2167
+ const finished = new Promise((resolve4, reject) => {
2168
+ finishedResolve = resolve4;
2169
+ finishedReject = reject;
2170
+ });
2171
+ let settled = false;
2172
+ const resolveStream = () => {
2173
+ if (settled) {
2174
+ return;
2175
+ }
2176
+ settled = true;
2177
+ try {
2178
+ handlers.onDone?.();
2179
+ finishedResolve();
2180
+ } catch (error) {
2181
+ settled = false;
2182
+ rejectStream(error);
2183
+ }
2184
+ };
2185
+ const rejectStream = (reason) => {
2186
+ if (settled) {
2187
+ return;
2188
+ }
2189
+ settled = true;
2190
+ try {
2191
+ handlers.onError?.(reason);
2192
+ } catch (handlerError) {
2193
+ reason = handlerError;
2194
+ }
2195
+ finishedReject(reason);
2196
+ };
2197
+ const abort = () => abortBundle.abort();
2198
+ (async () => {
2199
+ let buffer = "";
2200
+ try {
2201
+ while (true) {
2202
+ const { done, value } = await reader.read();
2203
+ if (done) {
2204
+ buffer += decoder.decode();
2205
+ buffer = buffer.replace(/\r\n/g, "\n");
2206
+ if (buffer.trim().length > 0) {
2207
+ if (processStreamEventChunk(buffer, handlers)) {
2208
+ break;
2209
+ }
2210
+ }
2211
+ resolveStream();
2212
+ break;
2213
+ }
2214
+ buffer += decoder.decode(value, { stream: true });
2215
+ buffer = buffer.replace(/\r\n/g, "\n");
2216
+ let boundaryIndex;
2217
+ while ((boundaryIndex = buffer.indexOf("\n\n")) !== -1) {
2218
+ const chunk = buffer.slice(0, boundaryIndex);
2219
+ buffer = buffer.slice(boundaryIndex + 2);
2220
+ if (!chunk) {
2221
+ continue;
2222
+ }
2223
+ if (processStreamEventChunk(chunk, handlers)) {
2224
+ await reader.cancel().catch(() => void 0);
2225
+ resolveStream();
2226
+ return;
2227
+ }
2228
+ }
2229
+ }
2230
+ } catch (error) {
2231
+ if (abortBundle.signal.aborted) {
2232
+ rejectStream(toAbortError(abortBundle.signal.reason ?? error));
2233
+ } else {
2234
+ rejectStream(error);
2235
+ }
2236
+ } finally {
2237
+ try {
2238
+ reader.releaseLock();
2239
+ } catch (error) {
2240
+ }
2241
+ abortBundle.cleanup();
2242
+ }
2243
+ })().catch((error) => {
2244
+ rejectStream(error);
2245
+ });
2246
+ return {
2247
+ abort,
2248
+ finished
2249
+ };
2250
+ function processStreamEventChunk(chunk, eventHandlers) {
2251
+ const dataString = extractSseData(chunk);
2252
+ if (dataString == null) {
2253
+ return false;
2254
+ }
2255
+ const trimmed = dataString.trim();
2256
+ if (trimmed === "[DONE]") {
2257
+ return true;
2258
+ }
2259
+ let payload2;
2260
+ try {
2261
+ payload2 = JSON.parse(dataString);
2262
+ } catch (error) {
2263
+ rejectStream(new AIError("Failed to parse streaming payload", { cause: error }));
2264
+ return true;
2265
+ }
2266
+ try {
2267
+ handleStreamPayload(payload2, eventHandlers);
2268
+ } catch (error) {
2269
+ rejectStream(error);
2270
+ return true;
2271
+ }
2272
+ return false;
2273
+ }
2274
+ function handleStreamPayload(payload2, eventHandlers) {
2275
+ if (!payload2 || typeof payload2 !== "object") {
2276
+ return;
2277
+ }
2278
+ if ("error" in payload2 && payload2.error) {
2279
+ const message = typeof payload2.error === "string" ? payload2.error : payload2.error.message;
2280
+ throw new AIError(message ?? "AI stream returned an error payload");
2281
+ }
2282
+ const structured = payload2;
2283
+ if (Array.isArray(structured.choices)) {
2284
+ for (const choice of structured.choices) {
2285
+ if (!choice || typeof choice !== "object") {
2286
+ continue;
2287
+ }
2288
+ const delta = choice.delta;
2289
+ if (!delta || typeof delta !== "object") {
2290
+ continue;
2291
+ }
2292
+ const deltaObject = delta;
2293
+ const textDelta = extractDeltaText(deltaObject.content);
2294
+ if (textDelta) {
2295
+ eventHandlers.onTextDelta?.(textDelta);
2296
+ }
2297
+ const reasoningDelta = extractDeltaText(deltaObject.reasoning);
2298
+ if (reasoningDelta) {
2299
+ eventHandlers.onReasoningDelta?.(reasoningDelta);
2300
+ }
2301
+ if (deltaObject.tool_calls !== void 0) {
2302
+ eventHandlers.onToolCallDelta?.(deltaObject.tool_calls);
2303
+ }
2304
+ }
2305
+ }
2306
+ if (structured.usage) {
2307
+ eventHandlers.onUsage?.(structured.usage);
2308
+ }
2309
+ }
2310
+ function extractDeltaText(value) {
2311
+ if (!value) {
2312
+ return void 0;
2313
+ }
2314
+ if (typeof value === "string") {
2315
+ return value;
2316
+ }
2317
+ if (Array.isArray(value)) {
2318
+ return flattenMessageContent(value);
2319
+ }
2320
+ if (typeof value === "object" && value !== null && "content" in value && Array.isArray(value.content)) {
2321
+ return flattenMessageContent(
2322
+ value.content ?? []
2323
+ );
2324
+ }
2325
+ return void 0;
2326
+ }
2327
+ function extractSseData(chunk) {
2328
+ const lines = chunk.split("\n");
2329
+ const dataLines = [];
2330
+ for (const rawLine of lines) {
2331
+ if (!rawLine) {
2332
+ continue;
2333
+ }
2334
+ const match = /^data:(.*)$/.exec(rawLine);
2335
+ if (!match) {
2336
+ continue;
2337
+ }
2338
+ const value = match[1];
2339
+ dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
2340
+ }
2341
+ if (dataLines.length === 0) {
2342
+ return null;
2343
+ }
2344
+ return dataLines.join("\n");
2345
+ }
2346
+ }
2347
+ function buildStreamMetadataExtras(options) {
2348
+ const streamConfig = {};
2349
+ if (options.sendReasoning !== void 0) {
2350
+ streamConfig.sendReasoning = options.sendReasoning;
2351
+ }
2352
+ if (options.includeUsage !== void 0) {
2353
+ streamConfig.includeUsage = options.includeUsage;
2354
+ }
2355
+ if (Object.keys(streamConfig).length === 0) {
2356
+ return void 0;
2357
+ }
2358
+ return {
2359
+ openpond: {
2360
+ stream: streamConfig
2361
+ }
2362
+ };
2363
+ }
2364
+ function buildRequestPayload(options, model, capabilities, metadataExtras) {
2365
+ const payload = {
2366
+ model,
2367
+ messages: options.messages
2368
+ };
2369
+ const generation = options.generation ?? {};
2370
+ assignIfDefined(payload, "temperature", generation.temperature);
2371
+ assignIfDefined(payload, "top_p", generation.topP);
2372
+ assignIfDefined(payload, "max_tokens", generation.maxTokens);
2373
+ assignIfDefined(payload, "stop", generation.stop);
2374
+ assignIfDefined(
2375
+ payload,
2376
+ "frequency_penalty",
2377
+ generation.frequencyPenalty
2378
+ );
2379
+ assignIfDefined(payload, "presence_penalty", generation.presencePenalty);
2380
+ assignIfDefined(payload, "response_format", generation.responseFormat);
2381
+ const toolExecution = options.toolExecution;
2382
+ const enableTools = toolExecution?.enableTools ?? true;
2383
+ if (enableTools && capabilities.allowTools) {
2384
+ const resolvedTools = resolveToolset(options.tools, toolExecution);
2385
+ assignIfDefined(payload, "tools", resolvedTools);
2386
+ assignIfDefined(payload, "tool_choice", options.toolChoice);
2387
+ } else if (options.toolChoice && options.toolChoice !== "none") {
2388
+ payload.tool_choice = "none";
2389
+ }
2390
+ const metadataPayload = buildMetadataPayload(
2391
+ options.metadata,
2392
+ toolExecution,
2393
+ metadataExtras
2394
+ );
2395
+ if (metadataPayload) {
2396
+ payload.metadata = metadataPayload;
2397
+ }
2398
+ return payload;
2399
+ }
2400
+ function assignIfDefined(target, key, value) {
2401
+ if (value !== void 0) {
2402
+ target[key] = value;
2403
+ }
2404
+ }
2405
+ function buildUrl(baseUrl, path7) {
2406
+ const sanitizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
2407
+ return `${sanitizedBase}${path7}`;
2408
+ }
2409
+ function createAbortBundle(upstreamSignal, timeoutMs) {
2410
+ const controller = new AbortController();
2411
+ const cleanupCallbacks = [];
2412
+ if (upstreamSignal) {
2413
+ if (upstreamSignal.aborted) {
2414
+ controller.abort(upstreamSignal.reason);
2415
+ } else {
2416
+ const onAbort = () => controller.abort(upstreamSignal.reason);
2417
+ upstreamSignal.addEventListener("abort", onAbort, { once: true });
2418
+ cleanupCallbacks.push(
2419
+ () => upstreamSignal.removeEventListener("abort", onAbort)
2420
+ );
2421
+ }
2422
+ }
2423
+ if (timeoutMs && timeoutMs > 0) {
2424
+ const timeoutId = setTimeout(() => {
2425
+ controller.abort(new Error("AI request timed out"));
2426
+ }, timeoutMs);
2427
+ cleanupCallbacks.push(() => clearTimeout(timeoutId));
2428
+ }
2429
+ return {
2430
+ signal: controller.signal,
2431
+ abort: () => controller.abort(),
2432
+ cleanup: () => {
2433
+ cleanupCallbacks.forEach((fn) => fn());
2434
+ }
2435
+ };
2436
+ }
2437
+ function collectHeaders(headers) {
2438
+ const result = {};
2439
+ headers.forEach((value, key) => {
2440
+ result[key] = value;
2441
+ });
2442
+ return result;
2443
+ }
2444
+ function buildMetadataPayload(base2, toolExecution, extras) {
2445
+ const metadata = base2 ? { ...base2 } : {};
2446
+ if (extras) {
2447
+ for (const [key, value] of Object.entries(extras)) {
2448
+ if (value === void 0) {
2449
+ continue;
2450
+ }
2451
+ if (key === "openpond" && typeof value === "object" && value !== null) {
2452
+ const existing = {
2453
+ ...metadata.openpond ?? {}
2454
+ };
2455
+ metadata.openpond = {
2456
+ ...existing,
2457
+ ...value
2458
+ };
2459
+ } else {
2460
+ metadata[key] = value;
2461
+ }
2462
+ }
2463
+ }
2464
+ if (toolExecution) {
2465
+ const openpond = {
2466
+ ...metadata.openpond ?? {},
2467
+ toolExecution
2468
+ };
2469
+ metadata.openpond = openpond;
2470
+ }
2471
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
2472
+ }
2473
+ async function safeParseJson(response) {
2474
+ const contentType = response.headers.get("content-type");
2475
+ if (!contentType || !contentType.includes("application/json")) {
2476
+ return void 0;
2477
+ }
2478
+ try {
2479
+ return await response.json();
2480
+ } catch (error) {
2481
+ return { error: "Failed to parse error body", cause: String(error) };
2482
+ }
2483
+ }
2484
+ function isPrimaryChoice(choice) {
2485
+ return choice.index === 0 || choice.message !== void 0;
2486
+ }
2487
+ function toAbortError(reason) {
2488
+ if (reason instanceof AIAbortError) {
2489
+ return reason;
2490
+ }
2491
+ if (reason instanceof Error) {
2492
+ if (reason.name === "AbortError") {
2493
+ return new AIAbortError(reason.message || "AI request aborted");
2494
+ }
2495
+ return new AIAbortError(reason.message);
2496
+ }
2497
+ return new AIAbortError(String(reason ?? "AI request aborted"));
2498
+ }
2499
+ var METADATA_SPEC_VERSION = "1.0.0";
2500
+ var McpAnnotationsSchema = z.object({
2501
+ title: z.string().optional(),
2502
+ readOnlyHint: z.boolean().optional(),
2503
+ destructiveHint: z.boolean().optional(),
2504
+ idempotentHint: z.boolean().optional(),
2505
+ openWorldHint: z.boolean().optional(),
2506
+ requiresPayment: z.boolean().optional()
2507
+ }).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
+ chainIds: z.array(z.number().int()).optional(),
2516
+ facilitator: z.string().optional()
2517
+ }).strict();
2518
+ var DiscoveryMetadataSchema = z.object({
2519
+ keywords: z.array(z.string()).optional(),
2520
+ category: z.string().optional(),
2521
+ useCases: z.array(z.string()).optional(),
2522
+ capabilities: z.array(z.string()).optional(),
2523
+ requirements: z.record(z.any()).optional(),
2524
+ pricing: z.record(z.any()).optional(),
2525
+ compatibility: z.record(z.any()).optional(),
2526
+ documentation: z.union([z.string(), z.array(z.string())]).optional()
2527
+ }).catchall(z.any());
2528
+ var ToolMetadataOverridesSchema = z.object({
2529
+ name: z.string().optional(),
2530
+ description: z.string().optional(),
2531
+ annotations: McpAnnotationsSchema.optional(),
2532
+ payment: PaymentConfigSchema.optional(),
2533
+ discovery: DiscoveryMetadataSchema.optional()
2534
+ }).catchall(z.any());
2535
+ var ToolSchema = z.object({
2536
+ name: z.string(),
2537
+ description: z.string(),
2538
+ inputSchema: z.any(),
2539
+ annotations: McpAnnotationsSchema.optional(),
2540
+ payment: PaymentConfigSchema.optional(),
2541
+ discovery: DiscoveryMetadataSchema.optional()
2542
+ }).strict();
2543
+ var AuthoredMetadataSchema = z.object({
2544
+ metadataSpecVersion: z.string().optional(),
2545
+ name: z.string().optional(),
2546
+ displayName: z.string().optional(),
2547
+ version: z.string().optional(),
2548
+ description: z.string().optional(),
2549
+ author: z.string().optional(),
2550
+ repository: z.string().optional(),
2551
+ website: z.string().optional(),
2552
+ category: z.string().optional(),
2553
+ categories: z.array(z.string()).optional(),
2554
+ termsOfService: z.string().optional(),
2555
+ mcpUrl: z.string().optional(),
2556
+ payment: PaymentConfigSchema.optional(),
2557
+ discovery: DiscoveryMetadataSchema.optional(),
2558
+ promptExamples: z.array(z.string()).optional(),
2559
+ iconPath: z.string().optional(),
2560
+ videoPath: z.string().optional(),
2561
+ image: z.string().optional(),
2562
+ animation_url: z.string().optional(),
2563
+ keywords: z.array(z.string()).optional(),
2564
+ useCases: z.array(z.string()).optional(),
2565
+ capabilities: z.array(z.string()).optional(),
2566
+ requirements: z.record(z.any()).optional(),
2567
+ pricing: z.record(z.any()).optional(),
2568
+ compatibility: z.record(z.any()).optional()
2569
+ }).catchall(z.any());
2570
+ var MetadataSchema = z.object({
2571
+ metadataSpecVersion: z.string().default(METADATA_SPEC_VERSION),
2572
+ name: z.string(),
2573
+ displayName: z.string(),
2574
+ version: z.string(),
2575
+ description: z.string().optional(),
2576
+ author: z.string().optional(),
2577
+ repository: z.string().optional(),
2578
+ website: z.string().optional(),
2579
+ category: z.string(),
2580
+ termsOfService: z.string().optional(),
2581
+ mcpUrl: z.string().optional(),
2582
+ payment: PaymentConfigSchema.optional(),
2583
+ tools: z.array(ToolSchema).min(1),
2584
+ discovery: DiscoveryMetadataSchema.optional(),
2585
+ promptExamples: z.array(z.string()).optional(),
2586
+ iconPath: z.string().optional(),
2587
+ videoPath: z.string().optional(),
2588
+ image: z.string().optional(),
2589
+ animation_url: z.string().optional()
2590
+ }).strict();
2591
+ function resolveTsconfig(projectRoot) {
2592
+ const candidate = path5.join(projectRoot, "tsconfig.json");
2593
+ if (fs2.existsSync(candidate)) {
2594
+ return candidate;
2595
+ }
2596
+ return void 0;
2597
+ }
2598
+ async function transpileWithEsbuild(options) {
2599
+ if (options.entryPoints.length === 0) {
2600
+ throw new Error("No entry points provided for esbuild transpilation");
2601
+ }
2602
+ const projectRoot = options.projectRoot;
2603
+ const tempBase = options.outDir ?? fs2.mkdtempSync(path5.join(tmpdir(), "opentool-"));
2604
+ if (!fs2.existsSync(tempBase)) {
2605
+ fs2.mkdirSync(tempBase, { recursive: true });
2606
+ }
2607
+ const tsconfig = resolveTsconfig(projectRoot);
2608
+ const buildOptions = {
2609
+ entryPoints: options.entryPoints,
2610
+ outdir: tempBase,
2611
+ bundle: false,
2612
+ format: options.format,
2613
+ platform: "node",
2614
+ target: "node20",
2615
+ logLevel: "warning",
2616
+ sourcesContent: false,
2617
+ sourcemap: false,
2618
+ packages: "external",
2619
+ loader: {
2620
+ ".ts": "ts",
2621
+ ".tsx": "tsx",
2622
+ ".cts": "ts",
2623
+ ".mts": "ts",
2624
+ ".js": "js",
2625
+ ".jsx": "jsx",
2626
+ ".mjs": "js",
2627
+ ".cjs": "js"
2628
+ },
2629
+ metafile: false,
2630
+ allowOverwrite: true,
2631
+ absWorkingDir: projectRoot
2632
+ };
2633
+ if (tsconfig) {
2634
+ buildOptions.tsconfig = tsconfig;
2635
+ }
2636
+ await build(buildOptions);
2637
+ if (options.format === "esm") {
2638
+ const packageJsonPath = path5.join(tempBase, "package.json");
2639
+ if (!fs2.existsSync(packageJsonPath)) {
2640
+ fs2.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
2641
+ }
2642
+ }
2643
+ const cleanup = () => {
2644
+ if (options.outDir) {
2645
+ return;
2646
+ }
2647
+ fs2.rmSync(tempBase, { recursive: true, force: true });
2648
+ };
2649
+ return { outDir: tempBase, cleanup };
2650
+ }
2651
+ createRequire(import.meta.url);
2652
+ function resolveCompiledPath(outDir, originalFile, extension = ".js") {
2653
+ const baseName = path5.basename(originalFile).replace(/\.[^.]+$/, "");
2654
+ return path5.join(outDir, `${baseName}${extension}`);
2655
+ }
2656
+ async function importFresh(modulePath) {
2657
+ const fileUrl = pathToFileURL(modulePath).href;
2658
+ const cacheBuster = `t=${Date.now()}-${Math.random()}`;
2659
+ const separator = fileUrl.includes("?") ? "&" : "?";
2660
+ return import(`${fileUrl}${separator}${cacheBuster}`);
2661
+ }
2662
+
2663
+ // src/cli/shared/metadata.ts
2664
+ var METADATA_ENTRY = "metadata.ts";
2665
+ async function loadAuthoredMetadata(projectRoot) {
2666
+ const absPath = path5.join(projectRoot, METADATA_ENTRY);
2667
+ if (!fs2.existsSync(absPath)) {
2668
+ throw new Error(
2669
+ `metadata.ts not found in ${projectRoot}. Create metadata.ts to describe your agent.`
2670
+ );
2671
+ }
2672
+ const tempDir = path5.join(projectRoot, ".opentool-temp");
2673
+ if (fs2.existsSync(tempDir)) {
2674
+ fs2.rmSync(tempDir, { recursive: true, force: true });
2675
+ }
2676
+ const { outDir, cleanup } = await transpileWithEsbuild({
2677
+ entryPoints: [absPath],
2678
+ projectRoot,
2679
+ format: "esm",
2680
+ outDir: tempDir
2681
+ });
2682
+ try {
2683
+ const compiledPath = resolveCompiledPath(outDir, METADATA_ENTRY);
2684
+ const moduleExports = await importFresh(compiledPath);
2685
+ const metadataExport = extractMetadataExport(moduleExports);
2686
+ const parsed = AuthoredMetadataSchema.parse(metadataExport);
2687
+ return { metadata: parsed, sourcePath: absPath };
2688
+ } finally {
2689
+ cleanup();
2690
+ if (fs2.existsSync(tempDir)) {
2691
+ fs2.rmSync(tempDir, { recursive: true, force: true });
2692
+ }
2693
+ }
2694
+ }
2695
+ function extractMetadataExport(moduleExports) {
2696
+ if (!moduleExports || typeof moduleExports !== "object") {
2697
+ throw new Error("metadata.ts must export a metadata object");
2698
+ }
2699
+ const exportsObject = moduleExports;
2700
+ if (exportsObject.metadata) {
2701
+ return exportsObject.metadata;
2702
+ }
2703
+ if (exportsObject.default && typeof exportsObject.default === "object") {
2704
+ const defaultExport = exportsObject.default;
2705
+ if (defaultExport.metadata) {
2706
+ return defaultExport.metadata;
2707
+ }
2708
+ return defaultExport;
2709
+ }
2710
+ return moduleExports;
2711
+ }
2712
+ function readPackageJson(projectRoot) {
2713
+ const packagePath = path5.join(projectRoot, "package.json");
2714
+ if (!fs2.existsSync(packagePath)) {
2715
+ return {};
2716
+ }
2717
+ try {
2718
+ const content = fs2.readFileSync(packagePath, "utf8");
2719
+ return JSON.parse(content);
2720
+ } catch (error) {
2721
+ throw new Error(`Failed to read package.json: ${error}`);
2722
+ }
2723
+ }
2724
+ async function buildMetadataArtifact(options) {
2725
+ const projectRoot = options.projectRoot;
2726
+ const packageInfo = readPackageJson(projectRoot);
2727
+ const { metadata: authored, sourcePath } = await loadAuthoredMetadata(projectRoot);
2728
+ const defaultsApplied = [];
2729
+ const folderName = path5.basename(projectRoot);
2730
+ const name = resolveField(
2731
+ "name",
2732
+ authored.name,
2733
+ () => packageInfo.name ?? folderName,
2734
+ defaultsApplied,
2735
+ "package.json name"
2736
+ );
2737
+ const displayName = resolveField(
2738
+ "displayName",
2739
+ authored.displayName,
2740
+ () => {
2741
+ const source = packageInfo.name ?? folderName;
2742
+ return source.split(/[-_]/).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
2743
+ },
2744
+ defaultsApplied,
2745
+ "package.json name"
2746
+ );
2747
+ const versionRaw = resolveField(
2748
+ "version",
2749
+ authored.version,
2750
+ () => packageInfo.version ?? "0.1.0",
2751
+ defaultsApplied,
2752
+ "package.json version"
2753
+ );
2754
+ const version = typeof versionRaw === "number" ? String(versionRaw) : versionRaw;
2755
+ const category = determineCategory(authored, defaultsApplied);
2756
+ const description = authored.description ?? packageInfo.description;
2757
+ if (!authored.description && packageInfo.description) {
2758
+ defaultsApplied.push("description \u2192 package.json description");
2759
+ }
2760
+ const author = authored.author ?? packageInfo.author;
2761
+ if (!authored.author && packageInfo.author) {
2762
+ defaultsApplied.push("author \u2192 package.json author");
2763
+ }
2764
+ const repository = authored.repository ?? extractRepository(packageInfo.repository);
2765
+ if (!authored.repository && repository) {
2766
+ defaultsApplied.push("repository \u2192 package.json repository");
2767
+ }
2768
+ const website = authored.website ?? packageInfo.homepage;
2769
+ if (!authored.website && packageInfo.homepage) {
2770
+ defaultsApplied.push("website \u2192 package.json homepage");
2771
+ }
2772
+ const payment = resolvePayment(authored, defaultsApplied);
2773
+ const baseImage = authored.image ?? authored.iconPath;
2774
+ const animation = authored.animation_url ?? authored.videoPath;
2775
+ const discovery = buildDiscovery(authored);
2776
+ const metadataTools = options.tools.map((tool) => {
2777
+ const overrides = tool.metadata ? ToolMetadataOverridesSchema.parse(tool.metadata) : {};
2778
+ const toolName = overrides.name ?? tool.filename;
2779
+ const toolDescription = overrides.description ?? `${toolName} tool`;
2780
+ const toolPayment = overrides.payment ?? payment ?? void 0;
2781
+ if (!overrides.payment && toolPayment && payment && toolPayment === payment) {
2782
+ defaultsApplied.push(`tool ${toolName} payment \u2192 agent payment`);
2783
+ }
2784
+ const toolDiscovery = overrides.discovery ?? void 0;
2785
+ const toolDefinition = {
2786
+ name: toolName,
2787
+ description: toolDescription,
2788
+ inputSchema: tool.inputSchema
2789
+ };
2790
+ if (overrides.annotations) {
2791
+ toolDefinition.annotations = overrides.annotations;
2792
+ }
2793
+ if (toolPayment) {
2794
+ toolDefinition.payment = toolPayment;
2795
+ }
2796
+ if (toolDiscovery) {
2797
+ toolDefinition.discovery = toolDiscovery;
2798
+ }
2799
+ return toolDefinition;
2800
+ });
2801
+ const metadata = MetadataSchema.parse({
2802
+ metadataSpecVersion: authored.metadataSpecVersion ?? METADATA_SPEC_VERSION,
2803
+ name,
2804
+ displayName,
2805
+ version,
2806
+ description,
2807
+ author,
2808
+ repository,
2809
+ website,
2810
+ category,
2811
+ termsOfService: authored.termsOfService,
2812
+ mcpUrl: authored.mcpUrl,
2813
+ payment: payment ?? void 0,
2814
+ tools: metadataTools,
2815
+ discovery,
2816
+ promptExamples: authored.promptExamples,
2817
+ iconPath: authored.iconPath,
2818
+ videoPath: authored.videoPath,
2819
+ image: baseImage,
2820
+ animation_url: animation
2821
+ });
2822
+ return {
2823
+ metadata,
2824
+ defaultsApplied,
2825
+ sourceMetadataPath: sourcePath
2826
+ };
2827
+ }
2828
+ function resolveField(field, value, fallback, defaultsApplied, fallbackLabel) {
2829
+ if (value !== void 0 && value !== null && value !== "") {
2830
+ return value;
2831
+ }
2832
+ const resolved = fallback();
2833
+ defaultsApplied.push(`${field} \u2192 ${fallbackLabel}`);
2834
+ return resolved;
2835
+ }
2836
+ function determineCategory(authored, defaultsApplied) {
2837
+ if (authored.category) {
2838
+ return authored.category;
2839
+ }
2840
+ if (Array.isArray(authored.categories) && authored.categories.length > 0) {
2841
+ defaultsApplied.push("category \u2192 metadata.categories[0]");
2842
+ return authored.categories[0];
2843
+ }
2844
+ defaultsApplied.push("category \u2192 default category");
2845
+ return "utility";
2846
+ }
2847
+ function extractRepository(repository) {
2848
+ if (!repository) {
2849
+ return void 0;
2850
+ }
2851
+ if (typeof repository === "string") {
2852
+ return repository;
2853
+ }
2854
+ return repository.url;
2855
+ }
2856
+ function resolvePayment(authored, defaults) {
2857
+ if (authored.payment) {
2858
+ return authored.payment;
2859
+ }
2860
+ const discoveryPricing = authored.discovery?.pricing;
2861
+ const legacyPricing = authored.pricing;
2862
+ const pricing = discoveryPricing ?? legacyPricing;
2863
+ if (!pricing) {
2864
+ return void 0;
2865
+ }
2866
+ const amount = typeof pricing.defaultAmount === "number" ? pricing.defaultAmount : 0;
2867
+ const sourceLabel = discoveryPricing ? "discovery.pricing.defaultAmount" : "pricing.defaultAmount";
2868
+ defaults.push(`payment \u2192 synthesized from ${sourceLabel}`);
2869
+ const acceptedMethodsRaw = Array.isArray(pricing.acceptedMethods) ? pricing.acceptedMethods : ["402"];
2870
+ const acceptedMethods = acceptedMethodsRaw.map(
2871
+ (method) => method === "x402" ? "x402" : "402"
2872
+ );
2873
+ return {
2874
+ amountUSDC: amount,
2875
+ description: typeof pricing.description === "string" ? pricing.description : void 0,
2876
+ x402: acceptedMethods.includes("x402"),
2877
+ plain402: acceptedMethods.includes("402"),
2878
+ acceptedMethods,
2879
+ acceptedCurrencies: Array.isArray(pricing.acceptedCurrencies) ? pricing.acceptedCurrencies : ["USDC"],
2880
+ chainIds: Array.isArray(pricing.chainIds) ? pricing.chainIds : [8453]
2881
+ };
2882
+ }
2883
+ function buildDiscovery(authored) {
2884
+ const legacyDiscovery = {};
2885
+ if (Array.isArray(authored.keywords) && authored.keywords.length > 0) {
2886
+ legacyDiscovery.keywords = authored.keywords;
2887
+ }
2888
+ if (Array.isArray(authored.useCases) && authored.useCases.length > 0) {
2889
+ legacyDiscovery.useCases = authored.useCases;
2890
+ }
2891
+ if (Array.isArray(authored.capabilities) && authored.capabilities.length > 0) {
2892
+ legacyDiscovery.capabilities = authored.capabilities;
2893
+ }
2894
+ if (authored.requirements) {
2895
+ legacyDiscovery.requirements = authored.requirements;
2896
+ }
2897
+ if (authored.compatibility) {
2898
+ legacyDiscovery.compatibility = authored.compatibility;
2899
+ }
2900
+ if (Array.isArray(authored.categories) && authored.categories.length > 0) {
2901
+ legacyDiscovery.category = authored.categories[0];
2902
+ }
2903
+ if (authored.pricing) {
2904
+ legacyDiscovery.pricing = authored.pricing;
2905
+ }
2906
+ const merged = {
2907
+ ...legacyDiscovery,
2908
+ ...authored.discovery ?? {}
2909
+ };
2910
+ return Object.keys(merged).length > 0 ? merged : void 0;
2911
+ }
2912
+ var SUPPORTED_EXTENSIONS = [
2913
+ ".ts",
2914
+ ".tsx",
2915
+ ".js",
2916
+ ".jsx",
2917
+ ".mjs",
2918
+ ".cjs"
2919
+ ];
2920
+ async function validateCommand(options) {
2921
+ console.log("\u{1F50D} Validating OpenTool metadata...");
2922
+ try {
2923
+ const toolsDir = path5.resolve(options.input);
2924
+ if (!fs2.existsSync(toolsDir)) {
2925
+ throw new Error(`Tools directory not found: ${toolsDir}`);
2926
+ }
2927
+ const projectRoot = path5.dirname(toolsDir);
2928
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
2929
+ if (tools.length === 0) {
2930
+ throw new Error("No valid tools found - metadata validation aborted");
2931
+ }
2932
+ const { metadata, defaultsApplied, sourceMetadataPath } = await buildMetadataArtifact({
2933
+ projectRoot,
2934
+ tools
2935
+ });
2936
+ logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath);
2937
+ console.log("\n\u2705 Metadata validation passed!\n");
2938
+ } catch (error) {
2939
+ console.error("\u274C Metadata validation failed:", error);
2940
+ process.exit(1);
2941
+ }
2942
+ }
2943
+ async function loadAndValidateTools(toolsDir, options = {}) {
2944
+ const files = fs2.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path5.extname(file)));
2945
+ if (files.length === 0) {
2946
+ return [];
2947
+ }
2948
+ const projectRoot = options.projectRoot ?? path5.dirname(toolsDir);
2949
+ const tempDir = path5.join(toolsDir, ".opentool-temp");
2950
+ if (fs2.existsSync(tempDir)) {
2951
+ fs2.rmSync(tempDir, { recursive: true, force: true });
2952
+ }
2953
+ const entryPoints = files.map((file) => path5.join(toolsDir, file));
2954
+ const { outDir, cleanup } = await transpileWithEsbuild({
2955
+ entryPoints,
2956
+ projectRoot,
2957
+ format: "esm",
2958
+ outDir: tempDir
2959
+ });
2960
+ const tools = [];
2961
+ try {
2962
+ for (const file of files) {
2963
+ const compiledPath = resolveCompiledPath(outDir, file);
2964
+ if (!fs2.existsSync(compiledPath)) {
2965
+ throw new Error(`Failed to compile ${file}`);
2966
+ }
2967
+ const moduleExports = await importFresh(compiledPath);
2968
+ const toolModule = extractToolModule(moduleExports, file);
2969
+ const schema = ensureZodSchema(toolModule.schema, file);
2970
+ const paymentExport = toolModule.payment;
2971
+ const toolName = toolModule.metadata?.name ?? toolModule.metadata?.title ?? toBaseName(file);
2972
+ const inputSchemaRaw = schema ? toJsonSchema(toolName, schema) : void 0;
2973
+ const inputSchema = normalizeInputSchema2(inputSchemaRaw);
2974
+ const httpHandlersRaw = collectHttpHandlers2(toolModule, file);
2975
+ const httpHandlers = [...httpHandlersRaw];
2976
+ if (httpHandlers.length === 0) {
2977
+ throw new Error(
2978
+ `${file} must export at least one HTTP handler (e.g. POST)`
2979
+ );
2980
+ }
2981
+ if (paymentExport) {
2982
+ for (let index = 0; index < httpHandlers.length; index += 1) {
2983
+ const entry = httpHandlers[index];
2984
+ httpHandlers[index] = {
2985
+ ...entry,
2986
+ handler: withPaymentRequirement(entry.handler, paymentExport)
2987
+ };
2988
+ }
2989
+ }
2990
+ const httpHandlerMap = toHttpHandlerMap2(httpHandlers);
2991
+ const defaultMethod = typeof toolModule.mcp?.defaultMethod === "string" ? toolModule.mcp.defaultMethod : void 0;
2992
+ const adapter = createMcpAdapter({
2993
+ name: toolName,
2994
+ httpHandlers: httpHandlerMap,
2995
+ ...defaultMethod ? { defaultMethod } : {},
2996
+ ...schema ? { schema } : {}
2997
+ });
2998
+ let metadataOverrides = toolModule.metadata ?? null;
2999
+ if (paymentExport?.metadata) {
3000
+ if (metadataOverrides) {
3001
+ metadataOverrides = {
3002
+ ...metadataOverrides,
3003
+ payment: metadataOverrides.payment ?? paymentExport.metadata,
3004
+ annotations: {
3005
+ ...metadataOverrides.annotations ?? {},
3006
+ requiresPayment: metadataOverrides.annotations?.requiresPayment ?? true
3007
+ }
3008
+ };
3009
+ } else {
3010
+ metadataOverrides = {
3011
+ payment: paymentExport.metadata,
3012
+ annotations: { requiresPayment: true }
3013
+ };
3014
+ }
3015
+ }
3016
+ const tool = {
3017
+ schema: schema ?? void 0,
3018
+ inputSchema,
3019
+ metadata: metadataOverrides,
3020
+ httpHandlers,
3021
+ mcpConfig: normalizeMcpConfig(toolModule.mcp, file),
3022
+ filename: toBaseName(file),
3023
+ sourcePath: path5.join(toolsDir, file),
3024
+ handler: async (params) => adapter(params),
3025
+ payment: paymentExport ?? null
3026
+ };
3027
+ tools.push(tool);
3028
+ }
3029
+ } finally {
3030
+ cleanup();
3031
+ if (fs2.existsSync(tempDir)) {
3032
+ fs2.rmSync(tempDir, { recursive: true, force: true });
3033
+ }
3034
+ }
3035
+ return tools;
3036
+ }
3037
+ function extractToolModule(exportsObject, filename) {
3038
+ const candidates = [exportsObject, exportsObject?.default];
3039
+ for (const candidate of candidates) {
3040
+ if (candidate && typeof candidate === "object") {
3041
+ const hasSchema = candidate.schema && typeof candidate.schema === "object";
3042
+ const hasHttp = HTTP_METHODS2.some((method) => typeof candidate[method] === "function");
3043
+ if (hasSchema || hasHttp) {
3044
+ return candidate;
3045
+ }
3046
+ }
3047
+ }
3048
+ throw new Error(
3049
+ `${filename} must export a tool definition. Expected a Zod schema plus HTTP handlers (export async function POST).`
3050
+ );
3051
+ }
3052
+ function toJsonSchema(name, schema) {
3053
+ if (!schema) {
3054
+ return void 0;
3055
+ }
3056
+ try {
3057
+ return zodToJsonSchema(schema, {
3058
+ name: `${name}Schema`,
3059
+ target: "jsonSchema7",
3060
+ $refStrategy: "none"
3061
+ });
3062
+ } catch (error) {
3063
+ throw new Error(`Failed to convert Zod schema for ${name}: ${error}`);
3064
+ }
3065
+ }
3066
+ function toBaseName(file) {
3067
+ return file.replace(/\.[^.]+$/, "");
3068
+ }
3069
+ function ensureZodSchema(schemaCandidate, filename) {
3070
+ if (schemaCandidate == null) {
3071
+ return void 0;
3072
+ }
3073
+ if (schemaCandidate instanceof z.ZodType) {
3074
+ return schemaCandidate;
3075
+ }
3076
+ const schema = schemaCandidate;
3077
+ if (typeof schema?.parse !== "function") {
3078
+ throw new Error(`${filename} schema export must be a Zod schema (missing parse method)`);
3079
+ }
3080
+ return schema;
3081
+ }
3082
+ function collectHttpHandlers2(module, filename) {
3083
+ const handlers = [];
3084
+ for (const method of HTTP_METHODS2) {
3085
+ const handler = module?.[method];
3086
+ if (typeof handler === "function") {
3087
+ handlers.push({
3088
+ method,
3089
+ handler: async (request) => handler.call(module, request)
3090
+ });
3091
+ }
3092
+ }
3093
+ handlers.sort((a, b) => HTTP_METHODS2.indexOf(a.method) - HTTP_METHODS2.indexOf(b.method));
3094
+ const duplicates = findDuplicates(handlers.map((h) => h.method));
3095
+ if (duplicates.length > 0) {
3096
+ throw new Error(
3097
+ `${filename} exports multiple handlers for HTTP method(s): ${duplicates.join(", ")}`
3098
+ );
3099
+ }
3100
+ return handlers;
3101
+ }
3102
+ function toHttpHandlerMap2(handlers) {
3103
+ return handlers.reduce((acc, handler) => {
3104
+ acc[handler.method.toUpperCase()] = handler.handler;
3105
+ return acc;
3106
+ }, {});
3107
+ }
3108
+ function normalizeInputSchema2(schema) {
3109
+ if (!schema || typeof schema !== "object") {
3110
+ return schema;
3111
+ }
3112
+ const clone = JSON.parse(JSON.stringify(schema));
3113
+ if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
3114
+ const refName = clone.$ref.replace("#/definitions/", "");
3115
+ const definitions = clone.definitions;
3116
+ if (definitions && typeof definitions[refName] === "object") {
3117
+ return normalizeInputSchema2(definitions[refName]);
3118
+ }
3119
+ }
3120
+ delete clone.$ref;
3121
+ delete clone.definitions;
3122
+ if (!("type" in clone)) {
3123
+ clone.type = "object";
3124
+ }
3125
+ return clone;
3126
+ }
3127
+ function normalizeMcpConfig(rawConfig, filename) {
3128
+ if (rawConfig == null) {
3129
+ return null;
3130
+ }
3131
+ if (rawConfig === false) {
3132
+ return null;
3133
+ }
3134
+ if (rawConfig === true) {
3135
+ return { enabled: true };
3136
+ }
3137
+ if (!isPlainObject2(rawConfig)) {
3138
+ throw new Error(`${filename} export \\"mcp\\" must be an object with an enabled flag`);
3139
+ }
3140
+ const enabledRaw = rawConfig.enabled;
3141
+ if (enabledRaw === false) {
3142
+ return null;
3143
+ }
3144
+ if (enabledRaw !== true) {
3145
+ throw new Error(`${filename} mcp.enabled must be explicitly set to true to opt-in to MCP`);
3146
+ }
3147
+ const modeRaw = rawConfig.mode;
3148
+ let mode;
3149
+ if (typeof modeRaw === "string") {
3150
+ const normalized = modeRaw.toLowerCase();
3151
+ if (["stdio", "lambda", "dual"].includes(normalized)) {
3152
+ mode = normalized;
3153
+ } else {
3154
+ throw new Error(
3155
+ `${filename} mcp.mode must be one of "stdio", "lambda", or "dual" if specified`
3156
+ );
3157
+ }
3158
+ }
3159
+ const defaultMethodRaw = rawConfig.defaultMethod;
3160
+ const defaultMethod = typeof defaultMethodRaw === "string" ? defaultMethodRaw.toUpperCase() : void 0;
3161
+ const overridesRaw = rawConfig.metadataOverrides;
3162
+ const metadataOverrides = isPlainObject2(overridesRaw) ? overridesRaw : void 0;
3163
+ const config = {
3164
+ enabled: true
3165
+ };
3166
+ if (mode) {
3167
+ config.mode = mode;
3168
+ }
3169
+ if (defaultMethod) {
3170
+ config.defaultMethod = defaultMethod;
3171
+ }
3172
+ if (metadataOverrides) {
3173
+ config.metadataOverrides = metadataOverrides;
3174
+ }
3175
+ return config;
3176
+ }
3177
+ function isPlainObject2(value) {
3178
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3179
+ }
3180
+ function findDuplicates(values) {
3181
+ const seen = /* @__PURE__ */ new Map();
3182
+ const duplicates = /* @__PURE__ */ new Set();
3183
+ values.forEach((value) => {
3184
+ const count = seen.get(value) ?? 0;
3185
+ seen.set(value, count + 1);
3186
+ if (count >= 1) {
3187
+ duplicates.add(value);
3188
+ }
3189
+ });
3190
+ return Array.from(duplicates.values());
3191
+ }
3192
+ function logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath) {
3193
+ console.log(`\u{1F4C4} metadata loaded from ${sourceMetadataPath}`);
3194
+ console.log("\n\u{1F4CA} Metadata Summary:");
3195
+ console.log(` \u2022 Name: ${metadata.name}`);
3196
+ console.log(` \u2022 Display Name: ${metadata.displayName}`);
3197
+ console.log(` \u2022 Version: ${metadata.version}`);
3198
+ console.log(` \u2022 Category: ${metadata.category}`);
3199
+ console.log(` \u2022 Tools: ${metadata.tools.length}`);
3200
+ console.log(` \u2022 Spec Version: ${metadata.metadataSpecVersion}`);
3201
+ if (metadata.payment) {
3202
+ console.log(` \u2022 Payment: $${metadata.payment.amountUSDC} USDC`);
3203
+ }
3204
+ if (defaultsApplied.length > 0) {
3205
+ console.log("\nDefaults applied during metadata synthesis:");
3206
+ defaultsApplied.forEach((entry) => console.log(` \u2022 ${entry}`));
3207
+ }
3208
+ }
3209
+
3210
+ // src/cli/generate-metadata.ts
3211
+ async function generateMetadataCommand(options) {
3212
+ const startTimestamp = timestamp();
3213
+ console.log(`[${startTimestamp}] Generating OpenTool metadata...`);
3214
+ try {
3215
+ const result = await generateMetadata(options);
3216
+ const endTimestamp = timestamp();
3217
+ console.log(`[${endTimestamp}] Metadata generation completed successfully!`);
3218
+ console.log(`Output file: ${result.outputPath}`);
3219
+ console.log(`Spec version: ${result.metadata.metadataSpecVersion}`);
3220
+ console.log(`Tools included: ${result.tools.length}`);
3221
+ if (result.defaultsApplied.length > 0) {
3222
+ console.log("Applied defaults:");
3223
+ for (const entry of result.defaultsApplied) {
3224
+ console.log(` \u2022 ${entry}`);
3225
+ }
3226
+ }
3227
+ } catch (error) {
3228
+ const endTimestamp = timestamp();
3229
+ console.error(`[${endTimestamp}] Metadata generation failed:`, error);
3230
+ process.exit(1);
3231
+ }
3232
+ }
3233
+ async function generateMetadata(options) {
3234
+ const toolsDir = path5.resolve(options.input);
3235
+ if (!fs2.existsSync(toolsDir)) {
3236
+ throw new Error(`Tools directory not found: ${toolsDir}`);
3237
+ }
3238
+ const projectRoot = path5.dirname(toolsDir);
3239
+ const tools = await loadAndValidateTools(toolsDir, { projectRoot });
3240
+ const { metadata, defaultsApplied } = await buildMetadataArtifact({
3241
+ projectRoot,
3242
+ tools
3243
+ });
3244
+ const outputPath = options.output ? path5.resolve(options.output) : path5.join(projectRoot, "metadata.json");
3245
+ fs2.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
3246
+ return {
3247
+ metadata,
3248
+ defaultsApplied,
3249
+ tools,
3250
+ outputPath
3251
+ };
3252
+ }
3253
+ function timestamp() {
3254
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
3255
+ }
3256
+
3257
+ 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 };
3258
+ //# sourceMappingURL=index.js.map
26
3259
  //# sourceMappingURL=index.js.map