opentool 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -128,10 +128,7 @@ var X402PaymentSchema = z.object({
128
128
  }),
129
129
  metadata: z.record(z.string(), z.unknown()).optional()
130
130
  }).passthrough();
131
- var PaymentConfigSchema = z.union([
132
- X402PaymentSchema,
133
- z.record(z.string(), z.unknown())
134
- ]);
131
+ var PaymentConfigSchema = z.union([X402PaymentSchema, z.record(z.string(), z.unknown())]);
135
132
  var DiscoveryMetadataSchema = z.object({
136
133
  keywords: z.array(z.string()).optional(),
137
134
  category: z.string().optional(),
@@ -460,7 +457,7 @@ function buildDiscovery(authored) {
460
457
  }
461
458
  const merged = {
462
459
  ...legacyDiscovery,
463
- ...authored.discovery ?? {}
460
+ ...authored.discovery
464
461
  };
465
462
  return Object.keys(merged).length > 0 ? merged : void 0;
466
463
  }
@@ -592,7 +589,10 @@ async function verifyX402Payment(attempt, definition, options = {}) {
592
589
  console.log("[x402] Facilitator /verify response", { status: verifyResponse.status });
593
590
  if (!verifyResponse.ok) {
594
591
  const errorText = await verifyResponse.text().catch(() => "");
595
- console.error("[x402] Facilitator /verify error", { status: verifyResponse.status, body: errorText });
592
+ console.error("[x402] Facilitator /verify error", {
593
+ status: verifyResponse.status,
594
+ body: errorText
595
+ });
596
596
  return {
597
597
  success: false,
598
598
  failure: {
@@ -640,7 +640,10 @@ async function verifyX402Payment(attempt, definition, options = {}) {
640
640
  console.log("[x402] Facilitator /settle response", { status: settleResponse.status });
641
641
  if (!settleResponse.ok) {
642
642
  const errorText = await settleResponse.text().catch(() => "");
643
- console.error("[x402] Facilitator /settle error", { status: settleResponse.status, body: errorText });
643
+ console.error("[x402] Facilitator /settle error", {
644
+ status: settleResponse.status,
645
+ body: errorText
646
+ });
644
647
  return {
645
648
  success: false,
646
649
  failure: {
@@ -658,7 +661,9 @@ async function verifyX402Payment(attempt, definition, options = {}) {
658
661
  });
659
662
  }
660
663
  } catch (error) {
661
- console.error("[x402] Settlement exception", { error: error instanceof Error ? error.message : String(error) });
664
+ console.error("[x402] Settlement exception", {
665
+ error: error instanceof Error ? error.message : String(error)
666
+ });
662
667
  return {
663
668
  success: false,
664
669
  failure: {
@@ -747,7 +752,7 @@ function ensureTrailingSlash(url) {
747
752
  }
748
753
 
749
754
  // src/x402/index.ts
750
- var PAYMENT_CONTEXT_SYMBOL = Symbol.for("opentool.x402.context");
755
+ var PAYMENT_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("opentool.x402.context");
751
756
  var X402PaymentRequiredError = class extends Error {
752
757
  constructor(response, verification) {
753
758
  super("X402 Payment required");
@@ -838,9 +843,7 @@ function createMcpAdapter(options) {
838
843
  const defaultMethod = resolveDefaultMethod(options);
839
844
  const httpHandler = options.httpHandlers[defaultMethod];
840
845
  if (!httpHandler) {
841
- throw new Error(
842
- `Tool "${options.name}" does not export an HTTP handler for ${defaultMethod}`
843
- );
846
+ throw new Error(`Tool "${options.name}" does not export an HTTP handler for ${defaultMethod}`);
844
847
  }
845
848
  return async function invoke(rawArguments) {
846
849
  const validated = normalizedSchema ? normalizedSchema.parse(rawArguments ?? {}) : rawArguments;
@@ -948,15 +951,7 @@ async function responseToToolResponse(response) {
948
951
  }
949
952
 
950
953
  // src/types/index.ts
951
- var HTTP_METHODS = [
952
- "GET",
953
- "HEAD",
954
- "POST",
955
- "PUT",
956
- "DELETE",
957
- "PATCH",
958
- "OPTIONS"
959
- ];
954
+ var HTTP_METHODS = ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"];
960
955
 
961
956
  // src/utils/schedule.ts
962
957
  var CRON_WRAPPED_REGEX = /^cron\((.*)\)$/i;
@@ -969,7 +964,9 @@ function normalizeScheduleExpression(raw, context) {
969
964
  const cronBody = extractCronBody(value);
970
965
  const cronFields = cronBody.trim().split(/\s+/).filter(Boolean);
971
966
  if (cronFields.length !== 5 && cronFields.length !== 6) {
972
- throw new Error(`${context}: cron expression must have 5 or 6 fields (got ${cronFields.length})`);
967
+ throw new Error(
968
+ `${context}: cron expression must have 5 or 6 fields (got ${cronFields.length})`
969
+ );
973
970
  }
974
971
  validateCronTokens(cronFields, context);
975
972
  return {
@@ -993,14 +990,7 @@ function validateCronTokens(fields, context) {
993
990
  }
994
991
 
995
992
  // src/cli/validate.ts
996
- var SUPPORTED_EXTENSIONS = [
997
- ".ts",
998
- ".tsx",
999
- ".js",
1000
- ".jsx",
1001
- ".mjs",
1002
- ".cjs"
1003
- ];
993
+ var SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
1004
994
  var MIN_TEMPLATE_CONFIG_VERSION = 2;
1005
995
  var TEMPLATE_PREVIEW_TITLE_MAX = 80;
1006
996
  var TEMPLATE_PREVIEW_SUBTITLE_MAX = 120;
@@ -1070,14 +1060,10 @@ function normalizeTemplatePreview(value, file, toolName, requirePreview) {
1070
1060
  required: true,
1071
1061
  max: TEMPLATE_PREVIEW_SUBTITLE_MAX
1072
1062
  });
1073
- const description = parseNonEmptyString(
1074
- record.description,
1075
- `${pathPrefix}.description`,
1076
- {
1077
- required: true,
1078
- max: TEMPLATE_PREVIEW_DESCRIPTION_MAX
1079
- }
1080
- );
1063
+ const description = parseNonEmptyString(record.description, `${pathPrefix}.description`, {
1064
+ required: true,
1065
+ max: TEMPLATE_PREVIEW_DESCRIPTION_MAX
1066
+ });
1081
1067
  const descriptionLineCount = description.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).length;
1082
1068
  if (descriptionLineCount < TEMPLATE_PREVIEW_MIN_LINES || descriptionLineCount > TEMPLATE_PREVIEW_MAX_LINES) {
1083
1069
  throw new Error(
@@ -1204,15 +1190,13 @@ async function loadAndValidateTools(toolsDir, options = {}) {
1204
1190
  const profileRaw = toolModule?.profile && typeof toolModule.profile === "object" ? toolModule.profile : null;
1205
1191
  const schedule = profileRaw?.schedule ?? null;
1206
1192
  const profileNotifyEmail = typeof profileRaw?.notifyEmail === "boolean" ? profileRaw.notifyEmail : void 0;
1207
- const allowedProfileCategories = [
1208
- "strategy",
1209
- "tracker",
1210
- "orchestrator"
1211
- ];
1193
+ const allowedProfileCategories = ["strategy", "tracker", "orchestrator"];
1212
1194
  const profileCategoryCandidate = typeof profileRaw?.category === "string" ? profileRaw.category : void 0;
1213
1195
  let profileCategoryRaw;
1214
1196
  if (profileCategoryCandidate !== void 0) {
1215
- const isAllowed = allowedProfileCategories.includes(profileCategoryCandidate);
1197
+ const isAllowed = allowedProfileCategories.includes(
1198
+ profileCategoryCandidate
1199
+ );
1216
1200
  if (!isAllowed) {
1217
1201
  throw new Error(
1218
1202
  `${file}: profile.category must be one of ${allowedProfileCategories.join(", ")}`
@@ -1227,22 +1211,16 @@ async function loadAndValidateTools(toolsDir, options = {}) {
1227
1211
  }
1228
1212
  profileAssetsRaw.forEach((entry, index) => {
1229
1213
  if (!entry || typeof entry !== "object") {
1230
- throw new Error(
1231
- `${file}: profile.assets[${index}] must be an object.`
1232
- );
1214
+ throw new Error(`${file}: profile.assets[${index}] must be an object.`);
1233
1215
  }
1234
1216
  const record = entry;
1235
1217
  const venue = typeof record.venue === "string" ? record.venue.trim() : "";
1236
1218
  if (!venue) {
1237
- throw new Error(
1238
- `${file}: profile.assets[${index}].venue must be a non-empty string.`
1239
- );
1219
+ throw new Error(`${file}: profile.assets[${index}].venue must be a non-empty string.`);
1240
1220
  }
1241
1221
  const chain = record.chain;
1242
1222
  if (typeof chain !== "string" && typeof chain !== "number") {
1243
- throw new Error(
1244
- `${file}: profile.assets[${index}].chain must be a string or number.`
1245
- );
1223
+ throw new Error(`${file}: profile.assets[${index}].chain must be a string or number.`);
1246
1224
  }
1247
1225
  const symbols = record.assetSymbols;
1248
1226
  if (!Array.isArray(symbols) || symbols.length === 0) {
@@ -1336,14 +1314,14 @@ async function loadAndValidateTools(toolsDir, options = {}) {
1336
1314
  throw new Error(`${file}: POST tools must export a Zod schema as 'schema'`);
1337
1315
  }
1338
1316
  if (schedule && typeof schedule.cron === "string") {
1339
- throw new Error(`${file}: POST tools must not define profile.schedule; use GET + cron for scheduled tasks.`);
1317
+ throw new Error(
1318
+ `${file}: POST tools must not define profile.schedule; use GET + cron for scheduled tasks.`
1319
+ );
1340
1320
  }
1341
1321
  }
1342
1322
  const httpHandlers = [...httpHandlersRaw];
1343
1323
  if (httpHandlers.length === 0) {
1344
- throw new Error(
1345
- `${file} must export at least one HTTP handler (e.g. POST)`
1346
- );
1324
+ throw new Error(`${file} must export at least one HTTP handler (e.g. POST)`);
1347
1325
  }
1348
1326
  if (paymentExport) {
1349
1327
  for (let index = 0; index < httpHandlers.length; index += 1) {
@@ -1369,7 +1347,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
1369
1347
  ...metadataOverrides,
1370
1348
  payment: metadataOverrides.payment ?? paymentExport,
1371
1349
  annotations: {
1372
- ...metadataOverrides.annotations ?? {},
1350
+ ...metadataOverrides.annotations,
1373
1351
  requiresPayment: metadataOverrides.annotations?.requiresPayment ?? true
1374
1352
  }
1375
1353
  };
@@ -1831,19 +1809,12 @@ async function writeMcpServer(options) {
1831
1809
  }
1832
1810
  function writeToolsManifest(options) {
1833
1811
  const manifestPath = path6.join(options.outputDir, "tools.json");
1834
- const legacyManifestPath = path6.join(
1835
- options.outputDir,
1836
- ".well-known",
1837
- "opentool",
1838
- "cron.json"
1839
- );
1812
+ const legacyManifestPath = path6.join(options.outputDir, ".well-known", "opentool", "cron.json");
1840
1813
  if (fs4.existsSync(legacyManifestPath)) {
1841
1814
  fs4.rmSync(legacyManifestPath, { force: true });
1842
1815
  }
1843
1816
  const entries = options.tools.map((tool) => {
1844
- const compiled = options.compiledTools.find(
1845
- (artifact) => artifact.filename === tool.filename
1846
- );
1817
+ const compiled = options.compiledTools.find((artifact) => artifact.filename === tool.filename);
1847
1818
  if (!compiled) {
1848
1819
  throw new Error(`Internal error: missing compiled artifact for ${tool.filename}`);
1849
1820
  }
@@ -1947,10 +1918,8 @@ async function devCommand(options) {
1947
1918
  return;
1948
1919
  }
1949
1920
  reloading = true;
1950
- log(
1951
- `${dim}
1952
- Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`
1953
- );
1921
+ log(`${dim}
1922
+ Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`);
1954
1923
  try {
1955
1924
  toolDefinitions = await loadToolDefinitions(toolsDir, projectRoot);
1956
1925
  routes = expandRoutes(toolDefinitions);
@@ -1983,9 +1952,7 @@ Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`
1983
1952
  log(`${dim}[request] ${method} ${routePath}${reset}`);
1984
1953
  try {
1985
1954
  await handleRequest({ req, res, port, routes });
1986
- log(
1987
- `${dim}[response] ${method} ${routePath} ${res.statusCode}${reset}`
1988
- );
1955
+ log(`${dim}[response] ${method} ${routePath} ${res.statusCode}${reset}`);
1989
1956
  } catch (error) {
1990
1957
  console.error("Error handling request:", error);
1991
1958
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -1995,9 +1962,7 @@ Detected change in ${changedPath ?? "tools directory"}, reloading...${reset}`
1995
1962
  });
1996
1963
  server.listen(port, () => {
1997
1964
  log(`${bold}${dim}> dev opentool${reset}`);
1998
- log(
1999
- ` * ${bold}opentool${reset} ${cyan}v${packageJson.version}${reset}`
2000
- );
1965
+ log(` * ${bold}opentool${reset} ${cyan}v${packageJson.version}${reset}`);
2001
1966
  log(` * ${bold}HTTP:${reset} http://localhost:${port}`);
2002
1967
  logStartup(toolDefinitions, enableStdio, log);
2003
1968
  });
@@ -2050,9 +2015,7 @@ async function startMcpServer(getTools) {
2050
2015
  throw new Error(`Tool ${request.params.name} not found`);
2051
2016
  }
2052
2017
  try {
2053
- const validatedParams = tool.schema.parse(
2054
- request.params.arguments
2055
- );
2018
+ const validatedParams = tool.schema.parse(request.params.arguments);
2056
2019
  const handler = tool.handler ?? createMcpAdapter({
2057
2020
  name: tool.metadata?.name ?? tool.filename,
2058
2021
  httpHandlers: toHttpHandlerMap2(tool.httpHandlers),
@@ -2136,10 +2099,7 @@ function printToolList(tools, log) {
2136
2099
  async function handleRequest(params) {
2137
2100
  const { req, res, port, routes } = params;
2138
2101
  res.setHeader("Access-Control-Allow-Origin", "*");
2139
- res.setHeader(
2140
- "Access-Control-Allow-Methods",
2141
- HTTP_METHODS.join(", ") + ", OPTIONS"
2142
- );
2102
+ res.setHeader("Access-Control-Allow-Methods", HTTP_METHODS.join(", ") + ", OPTIONS");
2143
2103
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
2144
2104
  if (req.method === "OPTIONS") {
2145
2105
  res.writeHead(200);
@@ -2192,9 +2152,7 @@ function findRoute(toolName, method, routes) {
2192
2152
  return direct;
2193
2153
  }
2194
2154
  if (method === "HEAD") {
2195
- return routes.find(
2196
- (route) => routeName(route.tool) === toolName && route.method === "GET"
2197
- );
2155
+ return routes.find((route) => routeName(route.tool) === toolName && route.method === "GET");
2198
2156
  }
2199
2157
  return void 0;
2200
2158
  }
@@ -2241,13 +2199,10 @@ function createWebRequest(params) {
2241
2199
  return new Request(url, init);
2242
2200
  }
2243
2201
  function toHttpHandlerMap2(handlers) {
2244
- return handlers.reduce(
2245
- (acc, handler) => {
2246
- acc[handler.method.toUpperCase()] = handler.handler;
2247
- return acc;
2248
- },
2249
- {}
2250
- );
2202
+ return handlers.reduce((acc, handler) => {
2203
+ acc[handler.method.toUpperCase()] = handler.handler;
2204
+ return acc;
2205
+ }, {});
2251
2206
  }
2252
2207
  function isMcpEnabled(tool) {
2253
2208
  return Boolean(tool.mcpConfig?.enabled);
@@ -2347,7 +2302,7 @@ async function updateMetadata(targetDir, name, description) {
2347
2302
  const raw = await promises.readFile(filePath, "utf-8");
2348
2303
  const displayName = toDisplayName(name);
2349
2304
  const resolvedDescription = description || "OpenTool project";
2350
- const updated = raw.replace(/name:\s*\".*?\"/, `name: "${toPackageName(name)}"`).replace(/displayName:\s*\".*?\"/, `displayName: "${displayName}"`).replace(/description:\s*\".*?\"/, `description: "${resolvedDescription}"`);
2305
+ const updated = raw.replace(/name:\s*".*?"/, `name: "${toPackageName(name)}"`).replace(/displayName:\s*".*?"/, `displayName: "${displayName}"`).replace(/description:\s*".*?"/, `description: "${resolvedDescription}"`);
2351
2306
  await promises.writeFile(filePath, updated, "utf-8");
2352
2307
  }
2353
2308
  async function initCommand(options) {
@@ -2355,9 +2310,7 @@ async function initCommand(options) {
2355
2310
  const templateDir = resolveTemplateDir();
2356
2311
  const empty = await directoryIsEmpty(targetDir);
2357
2312
  if (!empty && !options.force) {
2358
- throw new Error(
2359
- `Directory not empty: ${targetDir}. Use --force to overwrite.`
2360
- );
2313
+ throw new Error(`Directory not empty: ${targetDir}. Use --force to overwrite.`);
2361
2314
  }
2362
2315
  await copyDir(templateDir, targetDir);
2363
2316
  const projectName = options.name || path6__default.basename(targetDir);
@@ -2379,11 +2332,7 @@ program.command("dev").description("Start HTTP dev server (optional MCP stdio)")
2379
2332
  program.command("build").description("Build tools for deployment").option("-i, --input <dir>", "Input directory containing tools", "tools").option("-o, --output <dir>", "Output directory for built tools", "dist").option("--name <name>", "Server name", "opentool-server").option("--version <version>", "Server version", "1.0.0").action(buildCommand);
2380
2333
  program.command("validate").description("Validate metadata for registry submission").option("-i, --input <dir>", "Input directory containing tools", "tools").action(validateCommand);
2381
2334
  program.command("validate-full").description("Full validation of tools and metadata").option("-i, --input <dir>", "Input directory containing tools", "tools").action(validateFullCommand);
2382
- program.command("metadata").description("Generate OpenTool metadata JSON without building").option("-i, --input <dir>", "Input directory containing tools", "tools").option(
2383
- "-o, --output <file>",
2384
- "Output file path for metadata.json",
2385
- "metadata.json"
2386
- ).option("--name <name>", "Server name", "opentool-server").option("--version <version>", "Server version", "1.0.0").action(generateMetadataCommand);
2335
+ program.command("metadata").description("Generate OpenTool metadata JSON without building").option("-i, --input <dir>", "Input directory containing tools", "tools").option("-o, --output <file>", "Output file path for metadata.json", "metadata.json").option("--name <name>", "Server name", "opentool-server").option("--version <version>", "Server version", "1.0.0").action(generateMetadataCommand);
2387
2336
  program.command("init").description("Create a new OpenTool project in the target directory").option("-d, --dir <dir>", "Target directory", ".").option("-n, --name <name>", "Project name").option("--description <description>", "Project description").option("--force", "Overwrite existing files", false).action(async (cmdOptions) => {
2388
2337
  await initCommand({
2389
2338
  dir: cmdOptions.dir,