opentool 0.4.6 → 0.5.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.
Files changed (39) hide show
  1. package/README.md +11 -8
  2. package/dist/cli/build.d.ts +15 -0
  3. package/dist/cli/build.d.ts.map +1 -1
  4. package/dist/cli/build.js +127 -412
  5. package/dist/cli/build.js.map +1 -1
  6. package/dist/cli/dev.d.ts.map +1 -1
  7. package/dist/cli/dev.js +31 -76
  8. package/dist/cli/dev.js.map +1 -1
  9. package/dist/cli/generate-metadata.d.ts +7 -7
  10. package/dist/cli/generate-metadata.d.ts.map +1 -1
  11. package/dist/cli/generate-metadata.js +37 -238
  12. package/dist/cli/generate-metadata.js.map +1 -1
  13. package/dist/cli/shared/metadata.d.ts +19 -0
  14. package/dist/cli/shared/metadata.d.ts.map +1 -0
  15. package/dist/cli/shared/metadata.js +283 -0
  16. package/dist/cli/shared/metadata.js.map +1 -0
  17. package/dist/cli/validate.d.ts +5 -1
  18. package/dist/cli/validate.d.ts.map +1 -1
  19. package/dist/cli/validate.js +146 -237
  20. package/dist/cli/validate.js.map +1 -1
  21. package/dist/runtime/index.d.ts +1 -0
  22. package/dist/runtime/index.d.ts.map +1 -1
  23. package/dist/runtime/index.js +118 -69
  24. package/dist/runtime/index.js.map +1 -1
  25. package/dist/types/index.d.ts +5 -22
  26. package/dist/types/index.d.ts.map +1 -1
  27. package/dist/types/metadata.d.ts +975 -59
  28. package/dist/types/metadata.d.ts.map +1 -1
  29. package/dist/types/metadata.js +105 -0
  30. package/dist/types/metadata.js.map +1 -1
  31. package/dist/utils/esbuild.d.ts +13 -0
  32. package/dist/utils/esbuild.d.ts.map +1 -0
  33. package/dist/utils/esbuild.js +95 -0
  34. package/dist/utils/esbuild.js.map +1 -0
  35. package/dist/utils/module-loader.d.ts +3 -0
  36. package/dist/utils/module-loader.d.ts.map +1 -0
  37. package/dist/utils/module-loader.js +49 -0
  38. package/dist/utils/module-loader.js.map +1 -0
  39. package/package.json +5 -1
package/README.md CHANGED
@@ -188,10 +188,9 @@ When you push a project with `opentool` dependency to GitHub and connect it to O
188
188
 
189
189
  ## Examples
190
190
 
191
- See the `examples/` directory for complete examples:
191
+ See the `examples/` directory for a comprehensive example:
192
192
 
193
- - **`examples/full-metadata/`** - Complete metadata configuration with payment and discovery features
194
- - **`examples/minimal/`** - Minimal setup demonstrating smart defaults
193
+ - **`examples/full-metadata/`** - Metadata configuration with payment and discovery features
195
194
 
196
195
  ### Testing Examples Locally
197
196
 
@@ -207,16 +206,16 @@ cd examples/full-metadata
207
206
  npm link opentool
208
207
  npm run build
209
208
 
210
- # Test the minimal example
211
- cd ../minimal
212
- npm link opentool
213
- npm run build
214
-
215
209
  # Examine generated output
216
210
  cat dist/metadata.json
217
211
 
218
212
  # Test the MCP server
219
213
  echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | node dist/mcp-server.js
214
+
215
+ # Quick regression helpers from the repo root
216
+ npm run examples:build # Build full metadata example (CJS+ESM)
217
+ npm run examples:validate # Validate example metadata and tools
218
+ npm run examples:metadata # Regenerate metadata.json without rebuilding tools
220
219
  ```
221
220
 
222
221
  ## Metadata System
@@ -229,6 +228,10 @@ OpenTool features a sophisticated **three-tier metadata system**:
229
228
 
230
229
  See [`METADATA.md`](./METADATA.md) for the complete guide to configuring metadata for on-chain registration and payments.
231
230
 
231
+ ## Future Work
232
+
233
+ - Explore an esbuild-powered watch mode that keeps metadata and tool artifacts up to date for the dev server. This remains on the follow-up list once the new pipeline settles.
234
+
232
235
  ## Contributing
233
236
 
234
237
  We welcome contributions! Please see our [Contributing Guide](https://github.com/openpond/opentool/blob/master/CONTRIBUTING.md) for details.
@@ -1,8 +1,23 @@
1
+ import { InternalToolDefinition } from "../types";
2
+ import { Metadata } from "../types/metadata";
1
3
  export interface BuildOptions {
2
4
  input: string;
3
5
  output: string;
4
6
  name?: string;
5
7
  version?: string;
6
8
  }
9
+ interface BuildArtifacts {
10
+ metadata: Metadata;
11
+ defaultsApplied: string[];
12
+ tools: InternalToolDefinition[];
13
+ compiledTools: CompiledToolArtifact[];
14
+ }
15
+ interface CompiledToolArtifact {
16
+ name: string;
17
+ filename: string;
18
+ modulePath: string;
19
+ }
7
20
  export declare function buildCommand(options: BuildOptions): Promise<void>;
21
+ export declare function buildProject(options: BuildOptions): Promise<BuildArtifacts>;
22
+ export {};
8
23
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EvE"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAK7C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAc;IACtB,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,aAAa,EAAE,oBAAoB,EAAE,CAAC;CACvC;AAED,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAYvE;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CA6CjF"}
package/dist/cli/build.js CHANGED
@@ -34,101 +34,121 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.buildCommand = buildCommand;
37
+ exports.buildProject = buildProject;
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
39
- const child_process_1 = require("child_process");
40
- const util_1 = require("util");
40
+ const esbuild_1 = require("../utils/esbuild");
41
+ const metadata_1 = require("./shared/metadata");
41
42
  const validate_1 = require("./validate");
42
- const zod_to_json_schema_1 = require("zod-to-json-schema");
43
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
44
43
  async function buildCommand(options) {
45
- const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
46
- console.log(`[${timestamp}] Building OpenTool project...`);
47
- const config = {
48
- toolsDir: path.resolve(options.input),
49
- outputDir: path.resolve(options.output),
50
- serverName: options.name || "opentool-server",
51
- serverVersion: options.version || "1.0.0",
52
- };
44
+ const start = timestamp();
45
+ console.log(`[${start}] Building OpenTool project...`);
53
46
  try {
54
- // Validate tools directory exists
55
- if (!fs.existsSync(config.toolsDir)) {
56
- throw new Error(`Tools directory not found: ${config.toolsDir}`);
57
- }
58
- // Load and validate tools (validation integrated into build)
59
- console.log(`[${new Date()
60
- .toISOString()
61
- .replace("T", " ")
62
- .slice(0, 19)}] 🔍 Validating tools...`);
63
- const tools = await (0, validate_1.loadAndValidateTools)(config.toolsDir);
64
- if (tools.length === 0) {
65
- throw new Error("No valid tools found - build aborted");
66
- }
67
- console.log(`[${new Date().toISOString().replace("T", " ").slice(0, 19)}] ✅ Found ${tools.length} valid tools`);
68
- // Create output directory
69
- if (!fs.existsSync(config.outputDir)) {
70
- fs.mkdirSync(config.outputDir, { recursive: true });
71
- }
72
- // Generate JavaScript MCP server
73
- await generateJavaScriptMcpServer(tools, config);
74
- // Copy and compile tools to output directory
75
- await copyAndCompileTools(config.toolsDir, config.outputDir, tools);
76
- // Generate metadata JSON
77
- await generateMetadataJson(tools, config);
78
- const endTimestamp = new Date()
79
- .toISOString()
80
- .replace("T", " ")
81
- .slice(0, 19);
82
- console.log(`[${endTimestamp}] Build completed successfully!`);
83
- console.log(`Output directory: ${config.outputDir}`);
84
- console.log("Generated files:");
85
- console.log(" mcp-server.js - JavaScript MCP server");
86
- console.log(` tools/ - ${tools.length} compiled JavaScript tool files`);
87
- console.log(" metadata.json - Metadata for on-chain registration");
88
- console.log("\\nTest your MCP server:");
89
- console.log(` echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | node ${config.outputDir}/mcp-server.js`);
47
+ const artifacts = await buildProject(options);
48
+ logBuildSummary(artifacts, options);
90
49
  }
91
50
  catch (error) {
92
- console.error(`[${new Date()
93
- .toISOString()
94
- .replace("T", " ")
95
- .slice(0, 19)}] Build failed:`, error);
51
+ const end = timestamp();
52
+ console.error(`[${end}] Build failed:`, error);
96
53
  process.exit(1);
97
54
  }
98
55
  }
99
- async function generateJavaScriptMcpServer(tools, config) {
100
- // Generate JavaScript MCP server for stdio transport
101
- await generateJavaScriptMcpServerFile(tools, config);
56
+ async function buildProject(options) {
57
+ const toolsDir = path.resolve(options.input);
58
+ if (!fs.existsSync(toolsDir)) {
59
+ throw new Error(`Tools directory not found: ${toolsDir}`);
60
+ }
61
+ const projectRoot = path.dirname(toolsDir);
62
+ const outputDir = path.resolve(options.output);
63
+ fs.mkdirSync(outputDir, { recursive: true });
64
+ const serverName = options.name ?? "opentool-server";
65
+ const serverVersion = options.version ?? "1.0.0";
66
+ const tools = await (0, validate_1.loadAndValidateTools)(toolsDir, { projectRoot });
67
+ if (tools.length === 0) {
68
+ throw new Error("No valid tools found - build aborted");
69
+ }
70
+ const { metadata, defaultsApplied } = await (0, metadata_1.buildMetadataArtifact)({
71
+ projectRoot,
72
+ tools,
73
+ });
74
+ const metadataPath = path.join(outputDir, "metadata.json");
75
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
76
+ const compiledTools = await emitTools(tools, {
77
+ projectRoot,
78
+ outputDir,
79
+ });
80
+ await writeServer({
81
+ outputDir,
82
+ serverName,
83
+ serverVersion,
84
+ metadata,
85
+ compiledTools,
86
+ });
87
+ return {
88
+ metadata,
89
+ defaultsApplied,
90
+ tools,
91
+ compiledTools,
92
+ };
93
+ }
94
+ async function emitTools(tools, config) {
95
+ const toolsOutDir = path.join(config.outputDir, "tools");
96
+ if (fs.existsSync(toolsOutDir)) {
97
+ fs.rmSync(toolsOutDir, { recursive: true, force: true });
98
+ }
99
+ fs.mkdirSync(toolsOutDir, { recursive: true });
100
+ const entryPoints = tools.map((tool) => {
101
+ if (!tool.sourcePath) {
102
+ throw new Error(`Missing sourcePath for tool ${tool.filename}`);
103
+ }
104
+ return tool.sourcePath;
105
+ });
106
+ await (0, esbuild_1.transpileWithEsbuild)({
107
+ entryPoints,
108
+ projectRoot: config.projectRoot,
109
+ format: "cjs",
110
+ outDir: toolsOutDir,
111
+ });
112
+ const compiled = tools.map((tool) => {
113
+ if (!tool.sourcePath) {
114
+ throw new Error(`Missing sourcePath for tool ${tool.filename}`);
115
+ }
116
+ const base = path.basename(tool.sourcePath).replace(/\.[^.]+$/, "");
117
+ const modulePath = path.join("tools", `${base}.js`);
118
+ if (!fs.existsSync(path.join(config.outputDir, modulePath))) {
119
+ throw new Error(`Expected compiled output missing: ${modulePath}`);
120
+ }
121
+ return {
122
+ name: tool.metadata?.name ?? tool.filename,
123
+ filename: base,
124
+ modulePath,
125
+ };
126
+ });
127
+ return compiled;
102
128
  }
103
- async function generateJavaScriptMcpServerFile(tools, config) {
104
- const mcpServerCode = `#!/usr/bin/env node
105
- // Auto-generated JavaScript MCP server with stdio transport
129
+ function renderServer(options) {
130
+ const toolImports = options.compiledTools
131
+ .map((tool, index) => `const tool${index} = require('./${tool.modulePath}');`)
132
+ .join("\n");
133
+ const registry = options.compiledTools
134
+ .map((_, index) => ` { meta: metadata.tools[${index}], module: tool${index} },`)
135
+ .join("\n");
136
+ return `#!/usr/bin/env node
106
137
  const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
107
138
  const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
108
139
  const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
109
- const { zodToJsonSchema } = require('zod-to-json-schema');
140
+ const metadata = require('./metadata.json');
110
141
 
111
- // Import tools
112
- ${tools
113
- .map((tool, index) => `const tool${index} = require('./tools/${tool.filename}.js');`)
114
- .join("\n")}
142
+ ${toolImports}
115
143
 
116
- const tools = [
117
- ${tools
118
- .map((tool, index) => ` {
119
- schema: tool${index}.schema,
120
- metadata: tool${index}.metadata,
121
- handler: tool${index}.TOOL,
122
- filename: '${tool.filename}'
123
- },`)
124
- .join("\n")}
144
+ const toolRegistry = [
145
+ ${registry}
125
146
  ];
126
147
 
127
- // Create MCP server
128
148
  const server = new Server(
129
149
  {
130
- name: '${config.serverName}',
131
- version: '${config.serverVersion}',
150
+ name: '${escapeForJs(options.serverName)}',
151
+ version: '${escapeForJs(options.serverVersion)}',
132
152
  },
133
153
  {
134
154
  capabilities: {
@@ -137,79 +157,32 @@ const server = new Server(
137
157
  }
138
158
  );
139
159
 
140
- // Register list tools handler
141
160
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
142
- tools: tools.map((tool, index) => {
143
- // Convert Zod schema to JSON Schema format
144
- let inputSchema = { type: 'object' };
145
- const toolName = tool.metadata?.name || tool.filename;
146
-
147
- try {
148
- inputSchema = zodToJsonSchema(tool.schema, {
149
- name: \`\${toolName}Schema\`,
150
- target: 'jsonSchema7',
151
- $refStrategy: 'none'
152
- });
153
- } catch (error) {
154
- console.warn(\`Failed to convert schema for tool \${toolName}:\`, error);
155
- inputSchema = { type: 'object' };
156
- }
157
-
158
- const toolDef = {
159
- name: toolName,
160
- description: tool.metadata?.description || \`Tool \${index}\`,
161
- inputSchema
162
- };
163
-
164
- // Add annotations if they exist
165
- if (tool.metadata?.annotations) {
166
- toolDef.annotations = tool.metadata.annotations;
167
- }
168
-
169
- return toolDef;
170
- }),
161
+ tools: toolRegistry.map(({ meta }) => meta),
171
162
  }));
172
163
 
173
- // Register call tool handler
174
164
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
175
- const tool = tools.find(t => {
176
- const toolName = t.metadata?.name || t.filename;
177
- return toolName === request.params.name;
178
- });
179
- if (!tool) {
180
- throw new Error(\`Tool \${request.params.name} not found\`);
165
+ const entry = toolRegistry.find(({ meta }) => meta.name === request.params.name);
166
+ if (!entry) {
167
+ throw new Error('Tool ' + request.params.name + ' not found');
181
168
  }
182
169
 
183
170
  try {
184
- const validatedParams = tool.schema.parse(request.params.arguments);
185
- const result = await tool.handler(validatedParams);
186
-
187
- // Handle both string and object responses
171
+ const validated = entry.module.schema.parse(request.params.arguments);
172
+ const result = await entry.module.TOOL(validated);
188
173
  if (typeof result === 'string') {
189
- return {
190
- content: [{ type: 'text', text: result }],
191
- isError: false,
192
- };
193
- } else if (result && typeof result === 'object' && result.content) {
194
- return {
195
- content: result.content,
196
- isError: result.isError || false,
197
- };
198
- } else {
199
- return {
200
- content: [{ type: 'text', text: String(result) }],
201
- isError: false,
202
- };
174
+ return { content: [{ type: 'text', text: result }], isError: false };
203
175
  }
176
+ return { content: result.content, isError: result.isError || false };
204
177
  } catch (error) {
178
+ const message = (error && error.message) || String(error);
205
179
  return {
206
- content: [{ type: 'text', text: \`Error: \${error.message || error}\` }],
180
+ content: [{ type: 'text', text: 'Error: ' + message }],
207
181
  isError: true,
208
182
  };
209
183
  }
210
184
  });
211
185
 
212
- // Start server with stdio transport
213
186
  async function main() {
214
187
  const transport = new StdioServerTransport();
215
188
  await server.connect(transport);
@@ -219,290 +192,32 @@ if (require.main === module) {
219
192
  main().catch(console.error);
220
193
  }
221
194
 
222
- module.exports = { server, tools };
195
+ module.exports = { server };
223
196
  `;
224
- const mcpServerPath = path.join(config.outputDir, "mcp-server.js");
225
- fs.writeFileSync(mcpServerPath, mcpServerCode);
226
- // Make executable
227
- fs.chmodSync(mcpServerPath, 0o755);
228
197
  }
229
- async function copyAndCompileTools(sourceDir, outputDir, _tools) {
230
- const toolsOutputDir = path.join(outputDir, "tools");
231
- if (!fs.existsSync(toolsOutputDir)) {
232
- fs.mkdirSync(toolsOutputDir, { recursive: true });
233
- }
234
- // Use the same temp compilation approach as in loadTools
235
- const tempDir = path.join(sourceDir, ".opentool-temp");
236
- if (fs.existsSync(tempDir)) {
237
- fs.rmSync(tempDir, { recursive: true, force: true });
238
- }
239
- fs.mkdirSync(tempDir, { recursive: true });
240
- try {
241
- const files = fs.readdirSync(sourceDir);
242
- const tsFiles = files.filter((file) => file.endsWith(".ts"));
243
- if (tsFiles.length > 0) {
244
- console.log(` Compiling ${tsFiles.length} TypeScript files to JavaScript...`);
245
- // Copy all files to temp directory
246
- for (const file of files) {
247
- if (file.endsWith(".ts") || file.endsWith(".js")) {
248
- fs.copyFileSync(path.join(sourceDir, file), path.join(tempDir, file));
249
- }
250
- }
251
- // Compile TypeScript files
252
- try {
253
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --outDir ${tempDir} ${tsFiles
254
- .map((f) => path.join(tempDir, f))
255
- .join(" ")}`);
256
- }
257
- catch (tscError) {
258
- console.warn("TypeScript compilation failed, trying with relaxed settings...");
259
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --noImplicitAny false --strict false --outDir ${tempDir} ${tsFiles
260
- .map((f) => path.join(tempDir, f))
261
- .join(" ")}`);
262
- }
263
- }
264
- // Copy compiled JavaScript files to output
265
- for (const file of files) {
266
- if (file.endsWith(".ts")) {
267
- const jsFile = file.replace(".ts", ".js");
268
- const sourcePath = path.join(tempDir, jsFile);
269
- const outputPath = path.join(toolsOutputDir, jsFile);
270
- if (fs.existsSync(sourcePath)) {
271
- fs.copyFileSync(sourcePath, outputPath);
272
- console.log(` Compiled and copied ${jsFile}`);
273
- }
274
- }
275
- else if (file.endsWith(".js")) {
276
- const sourcePath = path.join(sourceDir, file);
277
- const outputPath = path.join(toolsOutputDir, file);
278
- fs.copyFileSync(sourcePath, outputPath);
279
- console.log(` Copied ${file}`);
280
- }
281
- }
282
- }
283
- finally {
284
- // Clean up temp directory
285
- if (fs.existsSync(tempDir)) {
286
- fs.rmSync(tempDir, { recursive: true, force: true });
287
- }
288
- }
198
+ async function writeServer(options) {
199
+ const serverCode = renderServer(options);
200
+ const serverPath = path.join(options.outputDir, "mcp-server.js");
201
+ fs.writeFileSync(serverPath, serverCode);
202
+ fs.chmodSync(serverPath, 0o755);
289
203
  }
290
- // Helper function to read package.json for fallback values
291
- function readPackageJson(projectRoot) {
292
- try {
293
- const packagePath = path.join(projectRoot, "package.json");
294
- if (fs.existsSync(packagePath)) {
295
- const packageContent = fs.readFileSync(packagePath, "utf8");
296
- return JSON.parse(packageContent);
297
- }
204
+ function logBuildSummary(artifacts, options) {
205
+ const end = timestamp();
206
+ console.log(`[${end}] Build completed successfully!`);
207
+ console.log(`Output directory: ${path.resolve(options.output)}`);
208
+ console.log("Generated files:");
209
+ console.log(" • mcp-server.js (stdio server)");
210
+ console.log(` • tools/ (${artifacts.compiledTools.length} compiled tools)`);
211
+ console.log(" • metadata.json (registry artifact)");
212
+ if (artifacts.defaultsApplied.length > 0) {
213
+ console.log("\nDefaults applied during metadata synthesis:");
214
+ artifacts.defaultsApplied.forEach((entry) => console.log(` • ${entry}`));
298
215
  }
299
- catch (error) {
300
- console.warn(" Failed to read package.json:", error);
301
- }
302
- return {};
303
216
  }
304
- async function generateMetadataJson(tools, config) {
305
- console.log(`[${new Date()
306
- .toISOString()
307
- .replace("T", " ")
308
- .slice(0, 19)}] Generating metadata JSON...`);
309
- const projectRoot = path.dirname(config.toolsDir);
310
- // Try to load metadata from metadata.ts (or fall back to discovery.ts for backwards compatibility)
311
- let rootMetadata = {};
312
- const metadataTsPath = path.join(projectRoot, "metadata.ts");
313
- const metadataJsPath = path.join(projectRoot, "metadata.js");
314
- const discoveryPath = path.join(projectRoot, "discovery.ts"); // backwards compatibility
315
- const discoveryJsPath = path.join(projectRoot, "discovery.js");
316
- // Check for metadata.ts first, then discovery.ts for backwards compatibility
317
- const metadataFilePath = fs.existsSync(metadataTsPath)
318
- ? metadataTsPath
319
- : fs.existsSync(discoveryPath)
320
- ? discoveryPath
321
- : null;
322
- const metadataJsFilePath = fs.existsSync(metadataJsPath)
323
- ? metadataJsPath
324
- : fs.existsSync(discoveryJsPath)
325
- ? discoveryJsPath
326
- : null;
327
- if (metadataFilePath) {
328
- try {
329
- // Use the same temp compilation approach as tools
330
- const tempDir = path.join(projectRoot, ".opentool-temp");
331
- if (!fs.existsSync(tempDir)) {
332
- fs.mkdirSync(tempDir, { recursive: true });
333
- }
334
- // Copy metadata file to temp directory
335
- const tempFileName = path.basename(metadataFilePath);
336
- const tempFilePath = path.join(tempDir, tempFileName);
337
- fs.copyFileSync(metadataFilePath, tempFilePath);
338
- // Compile TypeScript file
339
- try {
340
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --outDir ${tempDir} ${tempFilePath}`);
341
- }
342
- catch (tscError) {
343
- console.warn(`TypeScript compilation failed for ${tempFileName}, trying with relaxed settings...`);
344
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --noImplicitAny false --strict false --outDir ${tempDir} ${tempFilePath}`);
345
- }
346
- // Load the compiled JS file
347
- const compiledFileName = tempFileName.replace(".ts", ".js");
348
- const compiledPath = path.join(tempDir, compiledFileName);
349
- if (fs.existsSync(compiledPath)) {
350
- delete require.cache[require.resolve(compiledPath)];
351
- const metadataModule = require(compiledPath);
352
- // Support both 'metadata' and 'discovery' exports for backwards compatibility
353
- rootMetadata =
354
- metadataModule.metadata || metadataModule.discovery || {};
355
- console.log(` Loaded metadata from ${tempFileName}`);
356
- }
357
- // Clean up temp directory
358
- fs.rmSync(tempDir, { recursive: true, force: true });
359
- }
360
- catch (error) {
361
- console.warn(` Failed to load ${path.basename(metadataFilePath)}:`, error);
362
- }
363
- }
364
- else if (metadataJsFilePath) {
365
- try {
366
- // For JavaScript files, use require directly
367
- delete require.cache[require.resolve(metadataJsFilePath)];
368
- const metadataModule = require(metadataJsFilePath);
369
- // Support both 'metadata' and 'discovery' exports for backwards compatibility
370
- rootMetadata = metadataModule.metadata || metadataModule.discovery || {};
371
- console.log(` Loaded metadata from ${path.basename(metadataJsFilePath)}`);
372
- }
373
- catch (error) {
374
- console.warn(` Failed to load ${path.basename(metadataJsFilePath)}:`, error);
375
- }
376
- }
377
- else {
378
- console.log(" No metadata.ts found, using smart defaults");
379
- }
380
- // Read package.json for fallback values
381
- const packageInfo = readPackageJson(projectRoot);
382
- // Generate smart defaults from folder name and package.json
383
- const folderName = path.basename(projectRoot);
384
- const smartDefaults = {
385
- name: rootMetadata.name || packageInfo.name || folderName,
386
- displayName: rootMetadata.displayName ||
387
- (packageInfo.name
388
- ? packageInfo.name
389
- .split("-")
390
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
391
- .join(" ")
392
- : folderName
393
- .split("-")
394
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
395
- .join(" ")),
396
- version: rootMetadata.version || parseFloat(packageInfo.version || "1.0"),
397
- description: rootMetadata.description ||
398
- packageInfo.description ||
399
- `OpenTool agent built from ${folderName}`,
400
- author: rootMetadata.author || packageInfo.author || "Unknown",
401
- repository: rootMetadata.repository ||
402
- packageInfo.repository?.url ||
403
- packageInfo.repository ||
404
- "",
405
- website: rootMetadata.website || packageInfo.homepage || "",
406
- category: rootMetadata.category || rootMetadata.categories?.[0] || "utility",
407
- termsOfService: rootMetadata.termsOfService || "Please review terms before use.",
408
- };
409
- // Convert tools to metadata format
410
- const metadataTools = tools.map((tool) => {
411
- // Use tool metadata name, fallback to filename without extension
412
- const toolName = tool.metadata?.name || tool.filename.replace(/\.(ts|js)$/, "");
413
- const toolDescription = tool.metadata?.description || `${toolName} tool`;
414
- // Generate clean schema for metadata (same as MCP server)
415
- let metadataInputSchema = { type: 'object' };
416
- try {
417
- metadataInputSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(tool.schema, {
418
- name: `${toolName}Schema`,
419
- target: 'jsonSchema7',
420
- $refStrategy: 'none'
421
- });
422
- }
423
- catch (error) {
424
- console.warn(`Failed to convert schema for metadata tool ${toolName}:`, error);
425
- metadataInputSchema = { type: 'object' };
426
- }
427
- // Build metadata tool object
428
- const metadataTool = {
429
- name: toolName,
430
- description: toolDescription,
431
- inputSchema: metadataInputSchema,
432
- };
433
- // Add annotations if they exist
434
- if (tool.metadata?.annotations) {
435
- metadataTool.annotations = tool.metadata.annotations;
436
- }
437
- // Add payment config (tool-level overrides agent-level)
438
- if (tool.metadata?.payment) {
439
- metadataTool.payment = tool.metadata.payment;
440
- }
441
- else if (rootMetadata.payment) {
442
- // Use agent-level payment as default
443
- metadataTool.payment = rootMetadata.payment;
444
- }
445
- // Add discovery metadata if it exists
446
- if (tool.metadata?.discovery) {
447
- metadataTool.discovery = tool.metadata.discovery;
448
- }
449
- return metadataTool;
450
- });
451
- // Build complete metadata JSON with new structure
452
- const metadataJson = {
453
- // Core fields using smart defaults
454
- name: smartDefaults.name,
455
- displayName: smartDefaults.displayName,
456
- version: smartDefaults.version,
457
- description: smartDefaults.description,
458
- author: smartDefaults.author,
459
- repository: smartDefaults.repository,
460
- website: smartDefaults.website,
461
- category: smartDefaults.category,
462
- termsOfService: smartDefaults.termsOfService,
463
- // Tools array (always populated by build process)
464
- tools: metadataTools,
465
- // UI Enhancement fields
466
- ...(rootMetadata.promptExamples && {
467
- promptExamples: rootMetadata.promptExamples,
468
- }),
469
- ...(rootMetadata.iconPath && { iconPath: rootMetadata.iconPath }),
470
- ...(rootMetadata.videoPath && { videoPath: rootMetadata.videoPath }),
471
- // Agent-level payment defaults (create from pricing if exists)
472
- ...(rootMetadata.pricing && {
473
- payment: {
474
- amountUSDC: rootMetadata.pricing.defaultAmount || 0.01,
475
- description: rootMetadata.pricing.description || "",
476
- x402: true,
477
- openpondDirect: true,
478
- acceptedMethods: ["ETH", "USDC"],
479
- chainIds: [8453], // Base
480
- },
481
- }),
482
- // Discovery section (only include if metadata has discovery fields)
483
- ...((rootMetadata.keywords ||
484
- rootMetadata.categories ||
485
- rootMetadata.useCases ||
486
- rootMetadata.capabilities ||
487
- rootMetadata.requirements ||
488
- rootMetadata.pricing ||
489
- rootMetadata.compatibility ||
490
- rootMetadata.discovery) && {
491
- discovery: {
492
- keywords: rootMetadata.keywords || [],
493
- category: rootMetadata.categories?.[0] || smartDefaults.category,
494
- useCases: rootMetadata.useCases || [],
495
- capabilities: rootMetadata.capabilities || [],
496
- requirements: rootMetadata.requirements || {},
497
- pricing: rootMetadata.pricing || {},
498
- compatibility: rootMetadata.compatibility || {},
499
- ...(rootMetadata.discovery || {}), // Include any nested discovery fields too
500
- },
501
- }),
502
- };
503
- // Write metadata JSON to output directory
504
- const outputMetadataPath = path.join(config.outputDir, "metadata.json");
505
- fs.writeFileSync(outputMetadataPath, JSON.stringify(metadataJson, null, 2));
506
- console.log(` Generated metadata.json with ${metadataTools.length} tools`);
217
+ function timestamp() {
218
+ return new Date().toISOString().replace("T", " ").slice(0, 19);
219
+ }
220
+ function escapeForJs(value) {
221
+ return value.replace(/'/g, "\\'");
507
222
  }
508
223
  //# sourceMappingURL=build.js.map