opentool 0.10.1 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import * as path6 from 'path';
2
- import { fileURLToPath, pathToFileURL } from 'url';
3
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
3
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
6
- import * as fs2 from 'fs';
4
+ import * as fs4 from 'fs';
5
+ import * as path5 from 'path';
6
+ import { fileURLToPath, pathToFileURL } from 'url';
7
7
  import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
8
8
  import { z } from 'zod';
9
9
  import { zeroAddress, createWalletClient, http, createPublicClient, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
@@ -25,8 +25,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
25
25
  if (typeof require !== "undefined") return require.apply(this, arguments);
26
26
  throw Error('Dynamic require of "' + x + '" is not supported');
27
27
  });
28
- var getFilename = () => fileURLToPath(import.meta.url);
29
- var __filename$1 = /* @__PURE__ */ getFilename();
30
28
  var X402_VERSION = 1;
31
29
  var HEADER_X402 = "X-PAYMENT";
32
30
  var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
@@ -331,295 +329,8 @@ function ensureTrailingSlash(url) {
331
329
  return url.endsWith("/") ? url : `${url}/`;
332
330
  }
333
331
  var PAYMENT_HEADERS = [HEADER_X402, HEADER_PAYMENT_RESPONSE];
334
- var X402Client = class {
335
- constructor(config) {
336
- this.account = privateKeyToAccount(config.privateKey);
337
- const chain = baseSepolia;
338
- this.walletClient = createWalletClient({
339
- account: this.account,
340
- chain,
341
- transport: http(config.rpcUrl)
342
- });
343
- }
344
- async pay(request) {
345
- try {
346
- const initialResponse = await fetch(request.url, {
347
- method: request.method ?? "POST",
348
- headers: {
349
- "Content-Type": "application/json",
350
- ...request.headers
351
- },
352
- ...request.body ? { body: JSON.stringify(request.body) } : {}
353
- });
354
- if (initialResponse.status !== 402) {
355
- return {
356
- success: initialResponse.ok,
357
- response: initialResponse
358
- };
359
- }
360
- const paymentRequirements = await initialResponse.json();
361
- const x402Requirements = paymentRequirements.x402?.accepts?.[0];
362
- if (!x402Requirements) {
363
- return {
364
- success: false,
365
- error: "No x402 payment requirements found in 402 response"
366
- };
367
- }
368
- const authorization = await this.signTransferAuthorization({
369
- from: this.account.address,
370
- to: x402Requirements.payTo,
371
- value: BigInt(x402Requirements.maxAmountRequired),
372
- validAfter: BigInt(Math.floor(Date.now() / 1e3)),
373
- validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
374
- // 15 min
375
- nonce: `0x${Array.from(
376
- { length: 32 },
377
- () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
378
- ).join("")}`,
379
- tokenAddress: x402Requirements.asset
380
- });
381
- const paymentProof = {
382
- x402Version: 1,
383
- scheme: x402Requirements.scheme,
384
- network: x402Requirements.network,
385
- correlationId: "",
386
- payload: {
387
- signature: authorization.signature,
388
- authorization: {
389
- from: authorization.from,
390
- to: authorization.to,
391
- value: authorization.value.toString(),
392
- validAfter: authorization.validAfter.toString(),
393
- validBefore: authorization.validBefore.toString(),
394
- nonce: authorization.nonce
395
- }
396
- }
397
- };
398
- const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString("base64");
399
- const paidResponse = await fetch(request.url, {
400
- method: request.method ?? "POST",
401
- headers: {
402
- "Content-Type": "application/json",
403
- "X-PAYMENT": paymentHeader,
404
- ...request.headers
405
- },
406
- ...request.body ? { body: JSON.stringify(request.body) } : {}
407
- });
408
- return {
409
- success: paidResponse.ok,
410
- response: paidResponse,
411
- paymentDetails: {
412
- amount: x402Requirements.maxAmountRequired,
413
- currency: x402Requirements.extra?.currencyCode ?? "USDC",
414
- network: x402Requirements.network,
415
- signature: authorization.signature
416
- }
417
- };
418
- } catch (error) {
419
- return {
420
- success: false,
421
- error: error instanceof Error ? error.message : String(error)
422
- };
423
- }
424
- }
425
- async signTransferAuthorization(params) {
426
- if (!this.walletClient.chain) {
427
- throw new Error("Wallet client chain not configured");
428
- }
429
- const domain = {
430
- name: "USD Coin",
431
- version: "2",
432
- chainId: this.walletClient.chain.id,
433
- verifyingContract: params.tokenAddress
434
- };
435
- const types = {
436
- TransferWithAuthorization: [
437
- { name: "from", type: "address" },
438
- { name: "to", type: "address" },
439
- { name: "value", type: "uint256" },
440
- { name: "validAfter", type: "uint256" },
441
- { name: "validBefore", type: "uint256" },
442
- { name: "nonce", type: "bytes32" }
443
- ]
444
- };
445
- const message = {
446
- from: params.from,
447
- to: params.to,
448
- value: params.value,
449
- validAfter: params.validAfter,
450
- validBefore: params.validBefore,
451
- nonce: params.nonce
452
- };
453
- const signature = await this.walletClient.signTypedData({
454
- account: this.account,
455
- domain,
456
- types,
457
- primaryType: "TransferWithAuthorization",
458
- message
459
- });
460
- return {
461
- signature,
462
- from: params.from,
463
- to: params.to,
464
- value: params.value,
465
- validAfter: params.validAfter,
466
- validBefore: params.validBefore,
467
- nonce: params.nonce
468
- };
469
- }
470
- getAddress() {
471
- return this.account.address;
472
- }
473
- };
474
- async function payX402(config) {
475
- const client = new X402Client({
476
- privateKey: config.privateKey,
477
- ...config.rpcUrl ? { rpcUrl: config.rpcUrl } : {}
478
- });
479
- return client.pay({
480
- url: config.url,
481
- body: config.body
482
- });
483
- }
484
- var X402BrowserClient = class {
485
- constructor(config) {
486
- this.walletClient = config.walletClient;
487
- this.chainId = config.chainId;
488
- }
489
- async pay(request) {
490
- try {
491
- const initialResponse = await fetch(request.url, {
492
- method: request.method ?? "POST",
493
- headers: {
494
- "Content-Type": "application/json",
495
- ...request.headers
496
- },
497
- ...request.body ? { body: JSON.stringify(request.body) } : {}
498
- });
499
- if (initialResponse.status !== 402) {
500
- return {
501
- success: initialResponse.ok,
502
- response: initialResponse
503
- };
504
- }
505
- const paymentRequirements = await initialResponse.json();
506
- const x402Requirements = paymentRequirements.x402?.accepts?.[0];
507
- if (!x402Requirements) {
508
- return {
509
- success: false,
510
- error: "No x402 payment requirements found in 402 response"
511
- };
512
- }
513
- const account = this.walletClient.account;
514
- if (!account) {
515
- return {
516
- success: false,
517
- error: "No account connected to wallet"
518
- };
519
- }
520
- const authorization = {
521
- from: account.address,
522
- to: x402Requirements.payTo,
523
- value: BigInt(x402Requirements.maxAmountRequired),
524
- validAfter: BigInt(Math.floor(Date.now() / 1e3)),
525
- validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
526
- nonce: `0x${Array.from(
527
- { length: 32 },
528
- () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
529
- ).join("")}`
530
- };
531
- const signature = await this.signTransferAuthorization(
532
- authorization,
533
- x402Requirements.asset
534
- );
535
- const paymentProof = {
536
- x402Version: 1,
537
- scheme: x402Requirements.scheme,
538
- network: x402Requirements.network,
539
- correlationId: "",
540
- payload: {
541
- signature,
542
- authorization: {
543
- from: authorization.from,
544
- to: authorization.to,
545
- value: authorization.value.toString(),
546
- validAfter: authorization.validAfter.toString(),
547
- validBefore: authorization.validBefore.toString(),
548
- nonce: authorization.nonce
549
- }
550
- }
551
- };
552
- const paymentHeader = btoa(JSON.stringify(paymentProof));
553
- const paidResponse = await fetch(request.url, {
554
- method: request.method ?? "POST",
555
- headers: {
556
- "Content-Type": "application/json",
557
- "X-PAYMENT": paymentHeader,
558
- ...request.headers
559
- },
560
- ...request.body ? { body: JSON.stringify(request.body) } : {}
561
- });
562
- return {
563
- success: paidResponse.ok,
564
- response: paidResponse,
565
- paymentDetails: {
566
- amount: x402Requirements.maxAmountRequired,
567
- currency: x402Requirements.extra?.currencyCode ?? "USDC",
568
- network: x402Requirements.network,
569
- signature
570
- }
571
- };
572
- } catch (error) {
573
- return {
574
- success: false,
575
- error: error instanceof Error ? error.message : String(error)
576
- };
577
- }
578
- }
579
- async signTransferAuthorization(authorization, tokenAddress) {
580
- const account = this.walletClient.account;
581
- if (!account) {
582
- throw new Error("No account connected to wallet");
583
- }
584
- const domain = {
585
- name: "USD Coin",
586
- version: "2",
587
- chainId: this.chainId,
588
- verifyingContract: tokenAddress
589
- };
590
- const types = {
591
- TransferWithAuthorization: [
592
- { name: "from", type: "address" },
593
- { name: "to", type: "address" },
594
- { name: "value", type: "uint256" },
595
- { name: "validAfter", type: "uint256" },
596
- { name: "validBefore", type: "uint256" },
597
- { name: "nonce", type: "bytes32" }
598
- ]
599
- };
600
- const message = {
601
- from: authorization.from,
602
- to: authorization.to,
603
- value: authorization.value,
604
- validAfter: authorization.validAfter,
605
- validBefore: authorization.validBefore,
606
- nonce: authorization.nonce
607
- };
608
- return await this.walletClient.signTypedData({
609
- account,
610
- domain,
611
- types,
612
- primaryType: "TransferWithAuthorization",
613
- message
614
- });
615
- }
616
- };
617
- async function payX402WithWallet(walletClient, chainId, request) {
618
- const client = new X402BrowserClient({ walletClient, chainId });
619
- return client.pay(request);
620
- }
621
332
 
622
- // src/x402/index.ts
333
+ // src/x402/payment.ts
623
334
  var PAYMENT_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("opentool.x402.context");
624
335
  var X402PaymentRequiredError = class extends Error {
625
336
  constructor(response, verification) {
@@ -988,16 +699,16 @@ function buildAdapters(tools) {
988
699
  }
989
700
  async function loadToolsFromDirectory(metadataMap) {
990
701
  const tools = [];
991
- const toolsDir = path6.join(process.cwd(), "tools");
992
- if (!fs2.existsSync(toolsDir)) {
702
+ const toolsDir = path5.join(process.cwd(), "tools");
703
+ if (!fs4.existsSync(toolsDir)) {
993
704
  return tools;
994
705
  }
995
- const files = fs2.readdirSync(toolsDir);
706
+ const files = fs4.readdirSync(toolsDir);
996
707
  for (const file of files) {
997
708
  if (!isSupportedToolFile(file)) {
998
709
  continue;
999
710
  }
1000
- const toolPath = path6.join(toolsDir, file);
711
+ const toolPath = path5.join(toolsDir, file);
1001
712
  try {
1002
713
  const exportsObject = __require(toolPath);
1003
714
  const candidate = resolveModuleCandidate(exportsObject);
@@ -1061,12 +772,12 @@ async function loadToolsFromDirectory(metadataMap) {
1061
772
  return tools;
1062
773
  }
1063
774
  function loadMetadata() {
1064
- const metadataPath = path6.join(process.cwd(), "metadata.json");
1065
- if (!fs2.existsSync(metadataPath)) {
775
+ const metadataPath = path5.join(process.cwd(), "metadata.json");
776
+ if (!fs4.existsSync(metadataPath)) {
1066
777
  return null;
1067
778
  }
1068
779
  try {
1069
- const contents = fs2.readFileSync(metadataPath, "utf8");
780
+ const contents = fs4.readFileSync(metadataPath, "utf8");
1070
781
  return JSON.parse(contents);
1071
782
  } catch (error) {
1072
783
  console.warn(`Failed to parse metadata.json: ${error}`);
@@ -1120,74 +831,361 @@ function collectHttpHandlers(module) {
1120
831
  handler: async (request) => handler.call(module, request)
1121
832
  });
1122
833
  }
1123
- });
1124
- return handlers;
1125
- }
1126
- function toHttpHandlerMap(handlers) {
1127
- return handlers.reduce((acc, handler) => {
1128
- acc[handler.method.toUpperCase()] = handler.handler;
1129
- return acc;
1130
- }, {});
1131
- }
1132
- function normalizeInputSchema(schema) {
1133
- if (!schema || typeof schema !== "object") {
1134
- return schema;
834
+ });
835
+ return handlers;
836
+ }
837
+ function toHttpHandlerMap(handlers) {
838
+ return handlers.reduce((acc, handler) => {
839
+ acc[handler.method.toUpperCase()] = handler.handler;
840
+ return acc;
841
+ }, {});
842
+ }
843
+ function normalizeInputSchema(schema) {
844
+ if (!schema || typeof schema !== "object") {
845
+ return schema;
846
+ }
847
+ const clone = JSON.parse(JSON.stringify(schema));
848
+ if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
849
+ const refKey = clone.$ref.replace("#/definitions/", "");
850
+ if (clone.definitions && typeof clone.definitions[refKey] === "object") {
851
+ return normalizeInputSchema(clone.definitions[refKey]);
852
+ }
853
+ }
854
+ delete clone.$ref;
855
+ delete clone.definitions;
856
+ if (!clone.type) {
857
+ clone.type = "object";
858
+ }
859
+ return clone;
860
+ }
861
+ function normalizeRuntimeMcpConfig(rawConfig) {
862
+ if (isPlainObject(rawConfig) && rawConfig.enabled === true) {
863
+ let normalizedMode;
864
+ if (typeof rawConfig.mode === "string") {
865
+ const candidate = rawConfig.mode.toLowerCase();
866
+ if (candidate === "stdio" || candidate === "lambda" || candidate === "dual") {
867
+ normalizedMode = candidate;
868
+ } else {
869
+ throw new Error('mcp.mode must be one of "stdio", "lambda", or "dual"');
870
+ }
871
+ }
872
+ const metadataOverrides = isPlainObject(rawConfig.metadataOverrides) ? rawConfig.metadataOverrides : void 0;
873
+ const config = { enabled: true };
874
+ if (normalizedMode) {
875
+ config.mode = normalizedMode;
876
+ }
877
+ if (typeof rawConfig.defaultMethod === "string") {
878
+ config.defaultMethod = rawConfig.defaultMethod.toUpperCase();
879
+ }
880
+ if (metadataOverrides) {
881
+ config.metadataOverrides = metadataOverrides;
882
+ }
883
+ return config;
884
+ }
885
+ return null;
886
+ }
887
+ function isPlainObject(value) {
888
+ return typeof value === "object" && value !== null && !Array.isArray(value);
889
+ }
890
+ function isMcpEnabled(tool) {
891
+ return Boolean(tool.mcpConfig?.enabled);
892
+ }
893
+ function resolveRuntimePath(value) {
894
+ if (value.startsWith("file://")) {
895
+ return fileURLToPath(value);
896
+ }
897
+ return path5.resolve(value);
898
+ }
899
+
900
+ // src/types/index.ts
901
+ var HTTP_METHODS2 = ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
902
+ var X402Client = class {
903
+ constructor(config) {
904
+ this.account = privateKeyToAccount(config.privateKey);
905
+ const chain = baseSepolia;
906
+ this.walletClient = createWalletClient({
907
+ account: this.account,
908
+ chain,
909
+ transport: http(config.rpcUrl)
910
+ });
911
+ }
912
+ async pay(request) {
913
+ try {
914
+ const initialResponse = await fetch(request.url, {
915
+ method: request.method ?? "POST",
916
+ headers: {
917
+ "Content-Type": "application/json",
918
+ ...request.headers
919
+ },
920
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
921
+ });
922
+ if (initialResponse.status !== 402) {
923
+ return {
924
+ success: initialResponse.ok,
925
+ response: initialResponse
926
+ };
927
+ }
928
+ const paymentRequirements = await initialResponse.json();
929
+ const x402Requirements = paymentRequirements.x402?.accepts?.[0];
930
+ if (!x402Requirements) {
931
+ return {
932
+ success: false,
933
+ error: "No x402 payment requirements found in 402 response"
934
+ };
935
+ }
936
+ const authorization = await this.signTransferAuthorization({
937
+ from: this.account.address,
938
+ to: x402Requirements.payTo,
939
+ value: BigInt(x402Requirements.maxAmountRequired),
940
+ validAfter: BigInt(Math.floor(Date.now() / 1e3)),
941
+ validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
942
+ // 15 min
943
+ nonce: `0x${Array.from(
944
+ { length: 32 },
945
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
946
+ ).join("")}`,
947
+ tokenAddress: x402Requirements.asset
948
+ });
949
+ const paymentProof = {
950
+ x402Version: 1,
951
+ scheme: x402Requirements.scheme,
952
+ network: x402Requirements.network,
953
+ correlationId: "",
954
+ payload: {
955
+ signature: authorization.signature,
956
+ authorization: {
957
+ from: authorization.from,
958
+ to: authorization.to,
959
+ value: authorization.value.toString(),
960
+ validAfter: authorization.validAfter.toString(),
961
+ validBefore: authorization.validBefore.toString(),
962
+ nonce: authorization.nonce
963
+ }
964
+ }
965
+ };
966
+ const paymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString("base64");
967
+ const paidResponse = await fetch(request.url, {
968
+ method: request.method ?? "POST",
969
+ headers: {
970
+ "Content-Type": "application/json",
971
+ "X-PAYMENT": paymentHeader,
972
+ ...request.headers
973
+ },
974
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
975
+ });
976
+ return {
977
+ success: paidResponse.ok,
978
+ response: paidResponse,
979
+ paymentDetails: {
980
+ amount: x402Requirements.maxAmountRequired,
981
+ currency: x402Requirements.extra?.currencyCode ?? "USDC",
982
+ network: x402Requirements.network,
983
+ signature: authorization.signature
984
+ }
985
+ };
986
+ } catch (error) {
987
+ return {
988
+ success: false,
989
+ error: error instanceof Error ? error.message : String(error)
990
+ };
991
+ }
1135
992
  }
1136
- const clone = JSON.parse(JSON.stringify(schema));
1137
- if (typeof clone.$ref === "string" && clone.$ref.startsWith("#/definitions/")) {
1138
- const refKey = clone.$ref.replace("#/definitions/", "");
1139
- if (clone.definitions && typeof clone.definitions[refKey] === "object") {
1140
- return normalizeInputSchema(clone.definitions[refKey]);
993
+ async signTransferAuthorization(params) {
994
+ if (!this.walletClient.chain) {
995
+ throw new Error("Wallet client chain not configured");
1141
996
  }
997
+ const domain = {
998
+ name: "USD Coin",
999
+ version: "2",
1000
+ chainId: this.walletClient.chain.id,
1001
+ verifyingContract: params.tokenAddress
1002
+ };
1003
+ const types = {
1004
+ TransferWithAuthorization: [
1005
+ { name: "from", type: "address" },
1006
+ { name: "to", type: "address" },
1007
+ { name: "value", type: "uint256" },
1008
+ { name: "validAfter", type: "uint256" },
1009
+ { name: "validBefore", type: "uint256" },
1010
+ { name: "nonce", type: "bytes32" }
1011
+ ]
1012
+ };
1013
+ const message = {
1014
+ from: params.from,
1015
+ to: params.to,
1016
+ value: params.value,
1017
+ validAfter: params.validAfter,
1018
+ validBefore: params.validBefore,
1019
+ nonce: params.nonce
1020
+ };
1021
+ const signature = await this.walletClient.signTypedData({
1022
+ account: this.account,
1023
+ domain,
1024
+ types,
1025
+ primaryType: "TransferWithAuthorization",
1026
+ message
1027
+ });
1028
+ return {
1029
+ signature,
1030
+ from: params.from,
1031
+ to: params.to,
1032
+ value: params.value,
1033
+ validAfter: params.validAfter,
1034
+ validBefore: params.validBefore,
1035
+ nonce: params.nonce
1036
+ };
1142
1037
  }
1143
- delete clone.$ref;
1144
- delete clone.definitions;
1145
- if (!clone.type) {
1146
- clone.type = "object";
1038
+ getAddress() {
1039
+ return this.account.address;
1147
1040
  }
1148
- return clone;
1041
+ };
1042
+ async function payX402(config) {
1043
+ const client = new X402Client({
1044
+ privateKey: config.privateKey,
1045
+ ...config.rpcUrl ? { rpcUrl: config.rpcUrl } : {}
1046
+ });
1047
+ return client.pay({
1048
+ url: config.url,
1049
+ body: config.body
1050
+ });
1149
1051
  }
1150
- function normalizeRuntimeMcpConfig(rawConfig) {
1151
- if (isPlainObject(rawConfig) && rawConfig.enabled === true) {
1152
- let normalizedMode;
1153
- if (typeof rawConfig.mode === "string") {
1154
- const candidate = rawConfig.mode.toLowerCase();
1155
- if (candidate === "stdio" || candidate === "lambda" || candidate === "dual") {
1156
- normalizedMode = candidate;
1157
- } else {
1158
- throw new Error('mcp.mode must be one of "stdio", "lambda", or "dual"');
1052
+ var X402BrowserClient = class {
1053
+ constructor(config) {
1054
+ this.walletClient = config.walletClient;
1055
+ this.chainId = config.chainId;
1056
+ }
1057
+ async pay(request) {
1058
+ try {
1059
+ const initialResponse = await fetch(request.url, {
1060
+ method: request.method ?? "POST",
1061
+ headers: {
1062
+ "Content-Type": "application/json",
1063
+ ...request.headers
1064
+ },
1065
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
1066
+ });
1067
+ if (initialResponse.status !== 402) {
1068
+ return {
1069
+ success: initialResponse.ok,
1070
+ response: initialResponse
1071
+ };
1159
1072
  }
1073
+ const paymentRequirements = await initialResponse.json();
1074
+ const x402Requirements = paymentRequirements.x402?.accepts?.[0];
1075
+ if (!x402Requirements) {
1076
+ return {
1077
+ success: false,
1078
+ error: "No x402 payment requirements found in 402 response"
1079
+ };
1080
+ }
1081
+ const account = this.walletClient.account;
1082
+ if (!account) {
1083
+ return {
1084
+ success: false,
1085
+ error: "No account connected to wallet"
1086
+ };
1087
+ }
1088
+ const authorization = {
1089
+ from: account.address,
1090
+ to: x402Requirements.payTo,
1091
+ value: BigInt(x402Requirements.maxAmountRequired),
1092
+ validAfter: BigInt(Math.floor(Date.now() / 1e3)),
1093
+ validBefore: BigInt(Math.floor(Date.now() / 1e3) + 900),
1094
+ nonce: `0x${Array.from(
1095
+ { length: 32 },
1096
+ () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")
1097
+ ).join("")}`
1098
+ };
1099
+ const signature = await this.signTransferAuthorization(
1100
+ authorization,
1101
+ x402Requirements.asset
1102
+ );
1103
+ const paymentProof = {
1104
+ x402Version: 1,
1105
+ scheme: x402Requirements.scheme,
1106
+ network: x402Requirements.network,
1107
+ correlationId: "",
1108
+ payload: {
1109
+ signature,
1110
+ authorization: {
1111
+ from: authorization.from,
1112
+ to: authorization.to,
1113
+ value: authorization.value.toString(),
1114
+ validAfter: authorization.validAfter.toString(),
1115
+ validBefore: authorization.validBefore.toString(),
1116
+ nonce: authorization.nonce
1117
+ }
1118
+ }
1119
+ };
1120
+ const paymentHeader = btoa(JSON.stringify(paymentProof));
1121
+ const paidResponse = await fetch(request.url, {
1122
+ method: request.method ?? "POST",
1123
+ headers: {
1124
+ "Content-Type": "application/json",
1125
+ "X-PAYMENT": paymentHeader,
1126
+ ...request.headers
1127
+ },
1128
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
1129
+ });
1130
+ return {
1131
+ success: paidResponse.ok,
1132
+ response: paidResponse,
1133
+ paymentDetails: {
1134
+ amount: x402Requirements.maxAmountRequired,
1135
+ currency: x402Requirements.extra?.currencyCode ?? "USDC",
1136
+ network: x402Requirements.network,
1137
+ signature
1138
+ }
1139
+ };
1140
+ } catch (error) {
1141
+ return {
1142
+ success: false,
1143
+ error: error instanceof Error ? error.message : String(error)
1144
+ };
1160
1145
  }
1161
- const metadataOverrides = isPlainObject(rawConfig.metadataOverrides) ? rawConfig.metadataOverrides : void 0;
1162
- const config = { enabled: true };
1163
- if (normalizedMode) {
1164
- config.mode = normalizedMode;
1165
- }
1166
- if (typeof rawConfig.defaultMethod === "string") {
1167
- config.defaultMethod = rawConfig.defaultMethod.toUpperCase();
1168
- }
1169
- if (metadataOverrides) {
1170
- config.metadataOverrides = metadataOverrides;
1171
- }
1172
- return config;
1173
1146
  }
1174
- return null;
1175
- }
1176
- function isPlainObject(value) {
1177
- return typeof value === "object" && value !== null && !Array.isArray(value);
1178
- }
1179
- function isMcpEnabled(tool) {
1180
- return Boolean(tool.mcpConfig?.enabled);
1181
- }
1182
- function resolveRuntimePath(value) {
1183
- if (value.startsWith("file://")) {
1184
- return fileURLToPath(value);
1147
+ async signTransferAuthorization(authorization, tokenAddress) {
1148
+ const account = this.walletClient.account;
1149
+ if (!account) {
1150
+ throw new Error("No account connected to wallet");
1151
+ }
1152
+ const domain = {
1153
+ name: "USD Coin",
1154
+ version: "2",
1155
+ chainId: this.chainId,
1156
+ verifyingContract: tokenAddress
1157
+ };
1158
+ const types = {
1159
+ TransferWithAuthorization: [
1160
+ { name: "from", type: "address" },
1161
+ { name: "to", type: "address" },
1162
+ { name: "value", type: "uint256" },
1163
+ { name: "validAfter", type: "uint256" },
1164
+ { name: "validBefore", type: "uint256" },
1165
+ { name: "nonce", type: "bytes32" }
1166
+ ]
1167
+ };
1168
+ const message = {
1169
+ from: authorization.from,
1170
+ to: authorization.to,
1171
+ value: authorization.value,
1172
+ validAfter: authorization.validAfter,
1173
+ validBefore: authorization.validBefore,
1174
+ nonce: authorization.nonce
1175
+ };
1176
+ return await this.walletClient.signTypedData({
1177
+ account,
1178
+ domain,
1179
+ types,
1180
+ primaryType: "TransferWithAuthorization",
1181
+ message
1182
+ });
1185
1183
  }
1186
- return path6.resolve(value);
1184
+ };
1185
+ async function payX402WithWallet(walletClient, chainId, request) {
1186
+ const client = new X402BrowserClient({ walletClient, chainId });
1187
+ return client.pay(request);
1187
1188
  }
1188
-
1189
- // src/types/index.ts
1190
- var HTTP_METHODS2 = ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
1191
1189
  var BASE_ALCHEMY_HOST = "https://base-mainnet.g.alchemy.com/v2/";
1192
1190
  var ETHEREUM_ALCHEMY_HOST = "https://eth-mainnet.g.alchemy.com/v2/";
1193
1191
  var BASE_SEPOLIA_ALCHEMY_HOST = "https://base-sepolia.g.alchemy.com/v2/";
@@ -1734,8 +1732,8 @@ async function store(input, options) {
1734
1732
  );
1735
1733
  }
1736
1734
  const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
1737
- const path8 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
1738
- const url = `${baseUrl}${path8}`;
1735
+ const path7 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
1736
+ const url = `${baseUrl}${path7}`;
1739
1737
  let response;
1740
1738
  try {
1741
1739
  response = await fetchFn(url, {
@@ -1775,8 +1773,8 @@ async function store(input, options) {
1775
1773
  async function retrieve(params, options) {
1776
1774
  const { baseUrl, apiKey, fetchFn } = resolveConfig(options);
1777
1775
  const mode = params?.mode ?? "live";
1778
- const path8 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
1779
- const url = new URL(`${baseUrl}${path8}`);
1776
+ const path7 = mode === "backtest" ? "/apps/backtests/tx" : "/apps/positions/tx";
1777
+ const url = new URL(`${baseUrl}${path7}`);
1780
1778
  if (params?.source) url.searchParams.set("source", params.source);
1781
1779
  if (params?.walletAddress) url.searchParams.set("walletAddress", params.walletAddress);
1782
1780
  if (params?.symbol) url.searchParams.set("symbol", params.symbol);
@@ -4803,9 +4801,9 @@ function parseOptionalDate(value) {
4803
4801
  function buildHmacSignature(args) {
4804
4802
  const timestamp2 = args.timestamp.toString();
4805
4803
  const method = args.method.toUpperCase();
4806
- const path8 = args.path;
4804
+ const path7 = args.path;
4807
4805
  const body = args.body == null ? "" : typeof args.body === "string" ? args.body : JSON.stringify(args.body);
4808
- const payload = `${timestamp2}${method}${path8}${body}`;
4806
+ const payload = `${timestamp2}${method}${path7}${body}`;
4809
4807
  const key = Buffer.from(args.secret, "base64");
4810
4808
  return createHmac("sha256", key).update(payload).digest("hex");
4811
4809
  }
@@ -6158,9 +6156,9 @@ function assignIfDefined(target, key, value) {
6158
6156
  target[key] = value;
6159
6157
  }
6160
6158
  }
6161
- function buildUrl(baseUrl, path8) {
6159
+ function buildUrl(baseUrl, path7) {
6162
6160
  const sanitizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
6163
- return `${sanitizedBase}${path8}`;
6161
+ return `${sanitizedBase}${path7}`;
6164
6162
  }
6165
6163
  function createAbortBundle(upstreamSignal, timeoutMs) {
6166
6164
  const controller = new AbortController();
@@ -6430,8 +6428,8 @@ var BuildMetadataSchema = z.object({
6430
6428
  chains: z.array(z.union([z.string(), z.number()])).optional()
6431
6429
  }).strict();
6432
6430
  function resolveTsconfig(projectRoot) {
6433
- const candidate = path6.join(projectRoot, "tsconfig.json");
6434
- if (fs2.existsSync(candidate)) {
6431
+ const candidate = path5.join(projectRoot, "tsconfig.json");
6432
+ if (fs4.existsSync(candidate)) {
6435
6433
  return candidate;
6436
6434
  }
6437
6435
  return void 0;
@@ -6441,9 +6439,9 @@ async function transpileWithEsbuild(options) {
6441
6439
  throw new Error("No entry points provided for esbuild transpilation");
6442
6440
  }
6443
6441
  const projectRoot = options.projectRoot;
6444
- const tempBase = options.outDir ?? fs2.mkdtempSync(path6.join(tmpdir(), "opentool-"));
6445
- if (!fs2.existsSync(tempBase)) {
6446
- fs2.mkdirSync(tempBase, { recursive: true });
6442
+ const tempBase = options.outDir ?? fs4.mkdtempSync(path5.join(tmpdir(), "opentool-"));
6443
+ if (!fs4.existsSync(tempBase)) {
6444
+ fs4.mkdirSync(tempBase, { recursive: true });
6447
6445
  }
6448
6446
  const tsconfig = resolveTsconfig(projectRoot);
6449
6447
  const buildOptions = {
@@ -6474,6 +6472,9 @@ async function transpileWithEsbuild(options) {
6474
6472
  if (options.external && options.external.length > 0) {
6475
6473
  buildOptions.external = options.external;
6476
6474
  }
6475
+ if (options.nodePaths && options.nodePaths.length > 0) {
6476
+ buildOptions.nodePaths = options.nodePaths;
6477
+ }
6477
6478
  if (options.outBase) {
6478
6479
  buildOptions.outbase = options.outBase;
6479
6480
  }
@@ -6485,25 +6486,25 @@ async function transpileWithEsbuild(options) {
6485
6486
  }
6486
6487
  await build(buildOptions);
6487
6488
  if (options.format === "esm") {
6488
- const packageJsonPath = path6.join(tempBase, "package.json");
6489
- if (!fs2.existsSync(packageJsonPath)) {
6490
- fs2.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
6489
+ const packageJsonPath = path5.join(tempBase, "package.json");
6490
+ if (!fs4.existsSync(packageJsonPath)) {
6491
+ fs4.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
6491
6492
  }
6492
6493
  }
6493
6494
  const cleanup = () => {
6494
6495
  if (options.outDir) {
6495
6496
  return;
6496
6497
  }
6497
- fs2.rmSync(tempBase, { recursive: true, force: true });
6498
+ fs4.rmSync(tempBase, { recursive: true, force: true });
6498
6499
  };
6499
6500
  return { outDir: tempBase, cleanup };
6500
6501
  }
6501
6502
  createRequire(
6502
- typeof __filename$1 !== "undefined" ? __filename$1 : import.meta.url
6503
+ typeof __filename !== "undefined" ? __filename : import.meta.url
6503
6504
  );
6504
6505
  function resolveCompiledPath(outDir, originalFile, extension = ".js") {
6505
- const baseName = path6.basename(originalFile).replace(/\.[^.]+$/, "");
6506
- return path6.join(outDir, `${baseName}${extension}`);
6506
+ const baseName = path5.basename(originalFile).replace(/\.[^.]+$/, "");
6507
+ return path5.join(outDir, `${baseName}${extension}`);
6507
6508
  }
6508
6509
  async function importFresh(modulePath) {
6509
6510
  const fileUrl = pathToFileURL(modulePath).href;
@@ -6515,16 +6516,16 @@ async function importFresh(modulePath) {
6515
6516
  // src/cli/shared/metadata.ts
6516
6517
  var METADATA_ENTRY = "metadata.ts";
6517
6518
  async function loadMetadata2(projectRoot) {
6518
- const absPath = path6.join(projectRoot, METADATA_ENTRY);
6519
- if (!fs2.existsSync(absPath)) {
6519
+ const absPath = path5.join(projectRoot, METADATA_ENTRY);
6520
+ if (!fs4.existsSync(absPath)) {
6520
6521
  return {
6521
6522
  metadata: MetadataSchema.parse({}),
6522
6523
  sourcePath: "smart defaults (metadata.ts missing)"
6523
6524
  };
6524
6525
  }
6525
- const tempDir = path6.join(projectRoot, ".opentool-temp");
6526
- if (fs2.existsSync(tempDir)) {
6527
- fs2.rmSync(tempDir, { recursive: true, force: true });
6526
+ const tempDir = path5.join(projectRoot, ".opentool-temp");
6527
+ if (fs4.existsSync(tempDir)) {
6528
+ fs4.rmSync(tempDir, { recursive: true, force: true });
6528
6529
  }
6529
6530
  const { outDir, cleanup } = await transpileWithEsbuild({
6530
6531
  entryPoints: [absPath],
@@ -6540,8 +6541,8 @@ async function loadMetadata2(projectRoot) {
6540
6541
  return { metadata: parsed, sourcePath: absPath };
6541
6542
  } finally {
6542
6543
  cleanup();
6543
- if (fs2.existsSync(tempDir)) {
6544
- fs2.rmSync(tempDir, { recursive: true, force: true });
6544
+ if (fs4.existsSync(tempDir)) {
6545
+ fs4.rmSync(tempDir, { recursive: true, force: true });
6545
6546
  }
6546
6547
  }
6547
6548
  }
@@ -6563,12 +6564,12 @@ function extractMetadataExport(moduleExports) {
6563
6564
  return moduleExports;
6564
6565
  }
6565
6566
  function readPackageJson(projectRoot) {
6566
- const packagePath = path6.join(projectRoot, "package.json");
6567
- if (!fs2.existsSync(packagePath)) {
6567
+ const packagePath = path5.join(projectRoot, "package.json");
6568
+ if (!fs4.existsSync(packagePath)) {
6568
6569
  return {};
6569
6570
  }
6570
6571
  try {
6571
- const content = fs2.readFileSync(packagePath, "utf8");
6572
+ const content = fs4.readFileSync(packagePath, "utf8");
6572
6573
  return JSON.parse(content);
6573
6574
  } catch (error) {
6574
6575
  throw new Error(`Failed to read package.json: ${error}`);
@@ -6579,7 +6580,7 @@ async function buildMetadataArtifact(options) {
6579
6580
  const packageInfo = readPackageJson(projectRoot);
6580
6581
  const { metadata: authored, sourcePath } = await loadMetadata2(projectRoot);
6581
6582
  const defaultsApplied = [];
6582
- const folderName = path6.basename(projectRoot);
6583
+ const folderName = path5.basename(projectRoot);
6583
6584
  const name = resolveField(
6584
6585
  "name",
6585
6586
  authored.name,
@@ -6791,6 +6792,8 @@ function validateCronTokens(fields, context) {
6791
6792
 
6792
6793
  // src/cli/validate.ts
6793
6794
  var SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
6795
+ var OPENTOOL_ROOT = path5.resolve(path5.dirname(fileURLToPath(import.meta.url)), "../..");
6796
+ var OPENTOOL_NODE_MODULES = path5.join(OPENTOOL_ROOT, "node_modules");
6794
6797
  var MIN_TEMPLATE_CONFIG_VERSION = 2;
6795
6798
  var TEMPLATE_PREVIEW_TITLE_MAX = 80;
6796
6799
  var TEMPLATE_PREVIEW_SUBTITLE_MAX = 120;
@@ -6877,37 +6880,37 @@ function normalizeTemplatePreview(value, file, toolName, requirePreview) {
6877
6880
  };
6878
6881
  }
6879
6882
  async function validateCommand(options) {
6880
- console.log("\u{1F50D} Validating OpenTool metadata...");
6883
+ console.log("\u{1F50D} Validating OpenTool project...");
6881
6884
  try {
6882
- const toolsDir = path6.resolve(options.input);
6883
- if (!fs2.existsSync(toolsDir)) {
6885
+ const toolsDir = path5.resolve(options.input);
6886
+ if (!fs4.existsSync(toolsDir)) {
6884
6887
  throw new Error(`Tools directory not found: ${toolsDir}`);
6885
6888
  }
6886
- const projectRoot = path6.dirname(toolsDir);
6889
+ const projectRoot = path5.dirname(toolsDir);
6887
6890
  const tools = await loadAndValidateTools(toolsDir, { projectRoot });
6888
6891
  if (tools.length === 0) {
6889
- throw new Error("No valid tools found - metadata validation aborted");
6892
+ throw new Error("No valid tools found - validation aborted");
6890
6893
  }
6891
6894
  const { metadata, defaultsApplied, sourceMetadataPath } = await buildMetadataArtifact({
6892
6895
  projectRoot,
6893
6896
  tools
6894
6897
  });
6895
6898
  logMetadataSummary(metadata, defaultsApplied, sourceMetadataPath);
6896
- console.log("\n\u2705 Metadata validation passed!\n");
6899
+ console.log("\n\u2705 OpenTool validation passed!\n");
6897
6900
  } catch (error) {
6898
- console.error("\u274C Metadata validation failed:", error);
6901
+ console.error("\u274C OpenTool validation failed:", error);
6899
6902
  process.exit(1);
6900
6903
  }
6901
6904
  }
6902
6905
  async function loadAndValidateTools(toolsDir, options = {}) {
6903
- const files = fs2.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path6.extname(file)));
6906
+ const files = fs4.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path5.extname(file)));
6904
6907
  if (files.length === 0) {
6905
6908
  return [];
6906
6909
  }
6907
- const projectRoot = options.projectRoot ?? path6.dirname(toolsDir);
6908
- const tempDir = path6.join(toolsDir, ".opentool-temp");
6909
- if (fs2.existsSync(tempDir)) {
6910
- fs2.rmSync(tempDir, { recursive: true, force: true });
6910
+ const projectRoot = options.projectRoot ?? path5.dirname(toolsDir);
6911
+ const tempDir = path5.join(toolsDir, ".opentool-temp");
6912
+ if (fs4.existsSync(tempDir)) {
6913
+ fs4.rmSync(tempDir, { recursive: true, force: true });
6911
6914
  }
6912
6915
  const kebabCase = /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z]+$/;
6913
6916
  for (const f of files) {
@@ -6915,20 +6918,23 @@ async function loadAndValidateTools(toolsDir, options = {}) {
6915
6918
  throw new Error(`Tool filename must be kebab-case: ${f}`);
6916
6919
  }
6917
6920
  }
6918
- const entryPoints = files.map((file) => path6.join(toolsDir, file));
6921
+ const entryPoints = files.map((file) => path5.join(toolsDir, file));
6922
+ const fallbackNodePaths = [OPENTOOL_NODE_MODULES].filter((dir) => fs4.existsSync(dir));
6919
6923
  const { outDir, cleanup } = await transpileWithEsbuild({
6920
6924
  entryPoints,
6921
6925
  projectRoot,
6922
6926
  format: "esm",
6923
6927
  outDir: tempDir,
6924
6928
  bundle: true,
6925
- external: ["opentool", "opentool/*"]
6929
+ external: ["opentool", "opentool/*"],
6930
+ ...fallbackNodePaths.length > 0 ? { nodePaths: fallbackNodePaths } : {}
6926
6931
  });
6927
6932
  const tools = [];
6928
6933
  try {
6934
+ ensureLocalRuntimeLinks(tempDir);
6929
6935
  for (const file of files) {
6930
6936
  const compiledPath = resolveCompiledPath(outDir, file);
6931
- if (!fs2.existsSync(compiledPath)) {
6937
+ if (!fs4.existsSync(compiledPath)) {
6932
6938
  throw new Error(`Failed to compile ${file}`);
6933
6939
  }
6934
6940
  const moduleExports = await importFresh(compiledPath);
@@ -7079,11 +7085,6 @@ async function loadAndValidateTools(toolsDir, options = {}) {
7079
7085
  if (!schema) {
7080
7086
  throw new Error(`${file}: POST tools must export a Zod schema as 'schema'`);
7081
7087
  }
7082
- if (schedule && typeof schedule.cron === "string") {
7083
- throw new Error(
7084
- `${file}: POST tools must not define profile.schedule; use GET + cron for scheduled tasks.`
7085
- );
7086
- }
7087
7088
  }
7088
7089
  const httpHandlers = [...httpHandlersRaw];
7089
7090
  if (httpHandlers.length === 0) {
@@ -7131,7 +7132,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
7131
7132
  httpHandlers,
7132
7133
  mcpConfig: normalizeMcpConfig(toolModule.mcp, file),
7133
7134
  filename: toBaseName(file),
7134
- sourcePath: path6.join(toolsDir, file),
7135
+ sourcePath: path5.join(toolsDir, file),
7135
7136
  handler: async (params) => adapter(params),
7136
7137
  payment: paymentExport ?? null,
7137
7138
  schedule: normalizedSchedule,
@@ -7144,12 +7145,30 @@ async function loadAndValidateTools(toolsDir, options = {}) {
7144
7145
  }
7145
7146
  } finally {
7146
7147
  cleanup();
7147
- if (fs2.existsSync(tempDir)) {
7148
- fs2.rmSync(tempDir, { recursive: true, force: true });
7148
+ if (fs4.existsSync(tempDir)) {
7149
+ fs4.rmSync(tempDir, { recursive: true, force: true });
7149
7150
  }
7150
7151
  }
7151
7152
  return tools;
7152
7153
  }
7154
+ function ensureLocalRuntimeLinks(tempDir) {
7155
+ const nodeModulesDir = path5.join(tempDir, "node_modules");
7156
+ fs4.mkdirSync(nodeModulesDir, { recursive: true });
7157
+ const packageLinks = [
7158
+ { name: "opentool", target: OPENTOOL_ROOT },
7159
+ { name: "zod", target: path5.join(OPENTOOL_NODE_MODULES, "zod") }
7160
+ ];
7161
+ for (const { name, target } of packageLinks) {
7162
+ if (!fs4.existsSync(target)) {
7163
+ continue;
7164
+ }
7165
+ const linkPath = path5.join(nodeModulesDir, name);
7166
+ if (fs4.existsSync(linkPath)) {
7167
+ continue;
7168
+ }
7169
+ fs4.symlinkSync(target, linkPath, "junction");
7170
+ }
7171
+ }
7153
7172
  function extractToolModule(exportsObject, filename) {
7154
7173
  const candidates = [exportsObject, exportsObject?.default];
7155
7174
  for (const candidate of candidates) {
@@ -7347,18 +7366,18 @@ async function generateMetadataCommand(options) {
7347
7366
  }
7348
7367
  }
7349
7368
  async function generateMetadata(options) {
7350
- const toolsDir = path6.resolve(options.input);
7351
- if (!fs2.existsSync(toolsDir)) {
7369
+ const toolsDir = path5.resolve(options.input);
7370
+ if (!fs4.existsSync(toolsDir)) {
7352
7371
  throw new Error(`Tools directory not found: ${toolsDir}`);
7353
7372
  }
7354
- const projectRoot = path6.dirname(toolsDir);
7373
+ const projectRoot = path5.dirname(toolsDir);
7355
7374
  const tools = await loadAndValidateTools(toolsDir, { projectRoot });
7356
7375
  const { metadata, defaultsApplied } = await buildMetadataArtifact({
7357
7376
  projectRoot,
7358
7377
  tools
7359
7378
  });
7360
- const outputPath = options.output ? path6.resolve(options.output) : path6.join(projectRoot, "metadata.json");
7361
- fs2.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
7379
+ const outputPath = options.output ? path5.resolve(options.output) : path5.join(projectRoot, "metadata.json");
7380
+ fs4.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
7362
7381
  return {
7363
7382
  metadata,
7364
7383
  defaultsApplied,