opentool 0.4.5 → 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 -398
  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":"AAYA,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,100 +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 execAsync = (0, util_1.promisify)(child_process_1.exec);
43
43
  async function buildCommand(options) {
44
- const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
45
- console.log(`[${timestamp}] Building OpenTool project...`);
46
- const config = {
47
- toolsDir: path.resolve(options.input),
48
- outputDir: path.resolve(options.output),
49
- serverName: options.name || "opentool-server",
50
- serverVersion: options.version || "1.0.0",
51
- };
44
+ const start = timestamp();
45
+ console.log(`[${start}] Building OpenTool project...`);
52
46
  try {
53
- // Validate tools directory exists
54
- if (!fs.existsSync(config.toolsDir)) {
55
- throw new Error(`Tools directory not found: ${config.toolsDir}`);
56
- }
57
- // Load and validate tools (validation integrated into build)
58
- console.log(`[${new Date()
59
- .toISOString()
60
- .replace("T", " ")
61
- .slice(0, 19)}] 🔍 Validating tools...`);
62
- const tools = await (0, validate_1.loadAndValidateTools)(config.toolsDir);
63
- if (tools.length === 0) {
64
- throw new Error("No valid tools found - build aborted");
65
- }
66
- console.log(`[${new Date().toISOString().replace("T", " ").slice(0, 19)}] ✅ Found ${tools.length} valid tools`);
67
- // Create output directory
68
- if (!fs.existsSync(config.outputDir)) {
69
- fs.mkdirSync(config.outputDir, { recursive: true });
70
- }
71
- // Generate JavaScript MCP server
72
- await generateJavaScriptMcpServer(tools, config);
73
- // Copy and compile tools to output directory
74
- await copyAndCompileTools(config.toolsDir, config.outputDir, tools);
75
- // Generate metadata JSON
76
- await generateMetadataJson(tools, config);
77
- const endTimestamp = new Date()
78
- .toISOString()
79
- .replace("T", " ")
80
- .slice(0, 19);
81
- console.log(`[${endTimestamp}] Build completed successfully!`);
82
- console.log(`Output directory: ${config.outputDir}`);
83
- console.log("Generated files:");
84
- console.log(" mcp-server.js - JavaScript MCP server");
85
- console.log(` tools/ - ${tools.length} compiled JavaScript tool files`);
86
- console.log(" metadata.json - Metadata for on-chain registration");
87
- console.log("\\nTest your MCP server:");
88
- 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);
89
49
  }
90
50
  catch (error) {
91
- console.error(`[${new Date()
92
- .toISOString()
93
- .replace("T", " ")
94
- .slice(0, 19)}] Build failed:`, error);
51
+ const end = timestamp();
52
+ console.error(`[${end}] Build failed:`, error);
95
53
  process.exit(1);
96
54
  }
97
55
  }
98
- async function generateJavaScriptMcpServer(tools, config) {
99
- // Generate JavaScript MCP server for stdio transport
100
- 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;
101
128
  }
102
- async function generateJavaScriptMcpServerFile(tools, config) {
103
- const mcpServerCode = `#!/usr/bin/env node
104
- // 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
105
137
  const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
106
138
  const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
107
139
  const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
108
- const { zodToJsonSchema } = require('zod-to-json-schema');
140
+ const metadata = require('./metadata.json');
109
141
 
110
- // Import tools
111
- ${tools
112
- .map((tool, index) => `const tool${index} = require('./tools/${tool.filename}.js');`)
113
- .join("\n")}
142
+ ${toolImports}
114
143
 
115
- const tools = [
116
- ${tools
117
- .map((tool, index) => ` {
118
- schema: tool${index}.schema,
119
- metadata: tool${index}.metadata,
120
- handler: tool${index}.TOOL,
121
- filename: '${tool.filename}'
122
- },`)
123
- .join("\n")}
144
+ const toolRegistry = [
145
+ ${registry}
124
146
  ];
125
147
 
126
- // Create MCP server
127
148
  const server = new Server(
128
149
  {
129
- name: '${config.serverName}',
130
- version: '${config.serverVersion}',
150
+ name: '${escapeForJs(options.serverName)}',
151
+ version: '${escapeForJs(options.serverVersion)}',
131
152
  },
132
153
  {
133
154
  capabilities: {
@@ -136,79 +157,32 @@ const server = new Server(
136
157
  }
137
158
  );
138
159
 
139
- // Register list tools handler
140
160
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
141
- tools: tools.map((tool, index) => {
142
- // Convert Zod schema to JSON Schema format
143
- let inputSchema = { type: 'object' };
144
- const toolName = tool.metadata?.name || tool.filename;
145
-
146
- try {
147
- inputSchema = zodToJsonSchema(tool.schema, {
148
- name: \`\${toolName}Schema\`,
149
- target: 'jsonSchema7',
150
- $refStrategy: 'none'
151
- });
152
- } catch (error) {
153
- console.warn(\`Failed to convert schema for tool \${toolName}:\`, error);
154
- inputSchema = { type: 'object' };
155
- }
156
-
157
- const toolDef = {
158
- name: toolName,
159
- description: tool.metadata?.description || \`Tool \${index}\`,
160
- inputSchema
161
- };
162
-
163
- // Add annotations if they exist
164
- if (tool.metadata?.annotations) {
165
- toolDef.annotations = tool.metadata.annotations;
166
- }
167
-
168
- return toolDef;
169
- }),
161
+ tools: toolRegistry.map(({ meta }) => meta),
170
162
  }));
171
163
 
172
- // Register call tool handler
173
164
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
174
- const tool = tools.find(t => {
175
- const toolName = t.metadata?.name || t.filename;
176
- return toolName === request.params.name;
177
- });
178
- if (!tool) {
179
- 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');
180
168
  }
181
169
 
182
170
  try {
183
- const validatedParams = tool.schema.parse(request.params.arguments);
184
- const result = await tool.handler(validatedParams);
185
-
186
- // Handle both string and object responses
171
+ const validated = entry.module.schema.parse(request.params.arguments);
172
+ const result = await entry.module.TOOL(validated);
187
173
  if (typeof result === 'string') {
188
- return {
189
- content: [{ type: 'text', text: result }],
190
- isError: false,
191
- };
192
- } else if (result && typeof result === 'object' && result.content) {
193
- return {
194
- content: result.content,
195
- isError: result.isError || false,
196
- };
197
- } else {
198
- return {
199
- content: [{ type: 'text', text: String(result) }],
200
- isError: false,
201
- };
174
+ return { content: [{ type: 'text', text: result }], isError: false };
202
175
  }
176
+ return { content: result.content, isError: result.isError || false };
203
177
  } catch (error) {
178
+ const message = (error && error.message) || String(error);
204
179
  return {
205
- content: [{ type: 'text', text: \`Error: \${error.message || error}\` }],
180
+ content: [{ type: 'text', text: 'Error: ' + message }],
206
181
  isError: true,
207
182
  };
208
183
  }
209
184
  });
210
185
 
211
- // Start server with stdio transport
212
186
  async function main() {
213
187
  const transport = new StdioServerTransport();
214
188
  await server.connect(transport);
@@ -218,277 +192,32 @@ if (require.main === module) {
218
192
  main().catch(console.error);
219
193
  }
220
194
 
221
- module.exports = { server, tools };
195
+ module.exports = { server };
222
196
  `;
223
- const mcpServerPath = path.join(config.outputDir, "mcp-server.js");
224
- fs.writeFileSync(mcpServerPath, mcpServerCode);
225
- // Make executable
226
- fs.chmodSync(mcpServerPath, 0o755);
227
197
  }
228
- async function copyAndCompileTools(sourceDir, outputDir, _tools) {
229
- const toolsOutputDir = path.join(outputDir, "tools");
230
- if (!fs.existsSync(toolsOutputDir)) {
231
- fs.mkdirSync(toolsOutputDir, { recursive: true });
232
- }
233
- // Use the same temp compilation approach as in loadTools
234
- const tempDir = path.join(sourceDir, ".opentool-temp");
235
- if (fs.existsSync(tempDir)) {
236
- fs.rmSync(tempDir, { recursive: true, force: true });
237
- }
238
- fs.mkdirSync(tempDir, { recursive: true });
239
- try {
240
- const files = fs.readdirSync(sourceDir);
241
- const tsFiles = files.filter((file) => file.endsWith(".ts"));
242
- if (tsFiles.length > 0) {
243
- console.log(` Compiling ${tsFiles.length} TypeScript files to JavaScript...`);
244
- // Copy all files to temp directory
245
- for (const file of files) {
246
- if (file.endsWith(".ts") || file.endsWith(".js")) {
247
- fs.copyFileSync(path.join(sourceDir, file), path.join(tempDir, file));
248
- }
249
- }
250
- // Compile TypeScript files
251
- try {
252
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --outDir ${tempDir} ${tsFiles
253
- .map((f) => path.join(tempDir, f))
254
- .join(" ")}`);
255
- }
256
- catch (tscError) {
257
- console.warn("TypeScript compilation failed, trying with relaxed settings...");
258
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --noImplicitAny false --strict false --outDir ${tempDir} ${tsFiles
259
- .map((f) => path.join(tempDir, f))
260
- .join(" ")}`);
261
- }
262
- }
263
- // Copy compiled JavaScript files to output
264
- for (const file of files) {
265
- if (file.endsWith(".ts")) {
266
- const jsFile = file.replace(".ts", ".js");
267
- const sourcePath = path.join(tempDir, jsFile);
268
- const outputPath = path.join(toolsOutputDir, jsFile);
269
- if (fs.existsSync(sourcePath)) {
270
- fs.copyFileSync(sourcePath, outputPath);
271
- console.log(` Compiled and copied ${jsFile}`);
272
- }
273
- }
274
- else if (file.endsWith(".js")) {
275
- const sourcePath = path.join(sourceDir, file);
276
- const outputPath = path.join(toolsOutputDir, file);
277
- fs.copyFileSync(sourcePath, outputPath);
278
- console.log(` Copied ${file}`);
279
- }
280
- }
281
- }
282
- finally {
283
- // Clean up temp directory
284
- if (fs.existsSync(tempDir)) {
285
- fs.rmSync(tempDir, { recursive: true, force: true });
286
- }
287
- }
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);
288
203
  }
289
- // Helper function to read package.json for fallback values
290
- function readPackageJson(projectRoot) {
291
- try {
292
- const packagePath = path.join(projectRoot, "package.json");
293
- if (fs.existsSync(packagePath)) {
294
- const packageContent = fs.readFileSync(packagePath, "utf8");
295
- return JSON.parse(packageContent);
296
- }
297
- }
298
- catch (error) {
299
- console.warn(" Failed to read package.json:", error);
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}`));
300
215
  }
301
- return {};
302
216
  }
303
- async function generateMetadataJson(tools, config) {
304
- console.log(`[${new Date()
305
- .toISOString()
306
- .replace("T", " ")
307
- .slice(0, 19)}] Generating metadata JSON...`);
308
- const projectRoot = path.dirname(config.toolsDir);
309
- // Try to load metadata from metadata.ts (or fall back to discovery.ts for backwards compatibility)
310
- let rootMetadata = {};
311
- const metadataTsPath = path.join(projectRoot, "metadata.ts");
312
- const metadataJsPath = path.join(projectRoot, "metadata.js");
313
- const discoveryPath = path.join(projectRoot, "discovery.ts"); // backwards compatibility
314
- const discoveryJsPath = path.join(projectRoot, "discovery.js");
315
- // Check for metadata.ts first, then discovery.ts for backwards compatibility
316
- const metadataFilePath = fs.existsSync(metadataTsPath)
317
- ? metadataTsPath
318
- : fs.existsSync(discoveryPath)
319
- ? discoveryPath
320
- : null;
321
- const metadataJsFilePath = fs.existsSync(metadataJsPath)
322
- ? metadataJsPath
323
- : fs.existsSync(discoveryJsPath)
324
- ? discoveryJsPath
325
- : null;
326
- if (metadataFilePath) {
327
- try {
328
- // Use the same temp compilation approach as tools
329
- const tempDir = path.join(projectRoot, ".opentool-temp");
330
- if (!fs.existsSync(tempDir)) {
331
- fs.mkdirSync(tempDir, { recursive: true });
332
- }
333
- // Copy metadata file to temp directory
334
- const tempFileName = path.basename(metadataFilePath);
335
- const tempFilePath = path.join(tempDir, tempFileName);
336
- fs.copyFileSync(metadataFilePath, tempFilePath);
337
- // Compile TypeScript file
338
- try {
339
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --outDir ${tempDir} ${tempFilePath}`);
340
- }
341
- catch (tscError) {
342
- console.warn(`TypeScript compilation failed for ${tempFileName}, trying with relaxed settings...`);
343
- await execAsync(`npx tsc --target es2020 --module commonjs --esModuleInterop --skipLibCheck --moduleResolution node --noImplicitAny false --strict false --outDir ${tempDir} ${tempFilePath}`);
344
- }
345
- // Load the compiled JS file
346
- const compiledFileName = tempFileName.replace(".ts", ".js");
347
- const compiledPath = path.join(tempDir, compiledFileName);
348
- if (fs.existsSync(compiledPath)) {
349
- delete require.cache[require.resolve(compiledPath)];
350
- const metadataModule = require(compiledPath);
351
- // Support both 'metadata' and 'discovery' exports for backwards compatibility
352
- rootMetadata =
353
- metadataModule.metadata || metadataModule.discovery || {};
354
- console.log(` Loaded metadata from ${tempFileName}`);
355
- }
356
- // Clean up temp directory
357
- fs.rmSync(tempDir, { recursive: true, force: true });
358
- }
359
- catch (error) {
360
- console.warn(` Failed to load ${path.basename(metadataFilePath)}:`, error);
361
- }
362
- }
363
- else if (metadataJsFilePath) {
364
- try {
365
- // For JavaScript files, use require directly
366
- delete require.cache[require.resolve(metadataJsFilePath)];
367
- const metadataModule = require(metadataJsFilePath);
368
- // Support both 'metadata' and 'discovery' exports for backwards compatibility
369
- rootMetadata = metadataModule.metadata || metadataModule.discovery || {};
370
- console.log(` Loaded metadata from ${path.basename(metadataJsFilePath)}`);
371
- }
372
- catch (error) {
373
- console.warn(` Failed to load ${path.basename(metadataJsFilePath)}:`, error);
374
- }
375
- }
376
- else {
377
- console.log(" No metadata.ts found, using smart defaults");
378
- }
379
- // Read package.json for fallback values
380
- const packageInfo = readPackageJson(projectRoot);
381
- // Generate smart defaults from folder name and package.json
382
- const folderName = path.basename(projectRoot);
383
- const smartDefaults = {
384
- name: rootMetadata.name || packageInfo.name || folderName,
385
- displayName: rootMetadata.displayName ||
386
- (packageInfo.name
387
- ? packageInfo.name
388
- .split("-")
389
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
390
- .join(" ")
391
- : folderName
392
- .split("-")
393
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
394
- .join(" ")),
395
- version: rootMetadata.version || parseFloat(packageInfo.version || "1.0"),
396
- description: rootMetadata.description ||
397
- packageInfo.description ||
398
- `OpenTool agent built from ${folderName}`,
399
- author: rootMetadata.author || packageInfo.author || "Unknown",
400
- repository: rootMetadata.repository ||
401
- packageInfo.repository?.url ||
402
- packageInfo.repository ||
403
- "",
404
- website: rootMetadata.website || packageInfo.homepage || "",
405
- category: rootMetadata.category || rootMetadata.categories?.[0] || "utility",
406
- termsOfService: rootMetadata.termsOfService || "Please review terms before use.",
407
- };
408
- // Convert tools to metadata format
409
- const metadataTools = tools.map((tool) => {
410
- // Use tool metadata name, fallback to filename without extension
411
- const toolName = tool.metadata?.name || tool.filename.replace(/\.(ts|js)$/, "");
412
- const toolDescription = tool.metadata?.description || `${toolName} tool`;
413
- // Build metadata tool object
414
- const metadataTool = {
415
- name: toolName,
416
- description: toolDescription,
417
- inputSchema: tool.inputSchema,
418
- };
419
- // Add annotations if they exist
420
- if (tool.metadata?.annotations) {
421
- metadataTool.annotations = tool.metadata.annotations;
422
- }
423
- // Add payment config (tool-level overrides agent-level)
424
- if (tool.metadata?.payment) {
425
- metadataTool.payment = tool.metadata.payment;
426
- }
427
- else if (rootMetadata.payment) {
428
- // Use agent-level payment as default
429
- metadataTool.payment = rootMetadata.payment;
430
- }
431
- // Add discovery metadata if it exists
432
- if (tool.metadata?.discovery) {
433
- metadataTool.discovery = tool.metadata.discovery;
434
- }
435
- return metadataTool;
436
- });
437
- // Build complete metadata JSON with new structure
438
- const metadataJson = {
439
- // Core fields using smart defaults
440
- name: smartDefaults.name,
441
- displayName: smartDefaults.displayName,
442
- version: smartDefaults.version,
443
- description: smartDefaults.description,
444
- author: smartDefaults.author,
445
- repository: smartDefaults.repository,
446
- website: smartDefaults.website,
447
- category: smartDefaults.category,
448
- termsOfService: smartDefaults.termsOfService,
449
- // Tools array (always populated by build process)
450
- tools: metadataTools,
451
- // UI Enhancement fields
452
- ...(rootMetadata.promptExamples && {
453
- promptExamples: rootMetadata.promptExamples,
454
- }),
455
- ...(rootMetadata.iconPath && { iconPath: rootMetadata.iconPath }),
456
- ...(rootMetadata.videoPath && { videoPath: rootMetadata.videoPath }),
457
- // Agent-level payment defaults (create from pricing if exists)
458
- ...(rootMetadata.pricing && {
459
- payment: {
460
- amountUSDC: rootMetadata.pricing.defaultAmount || 0.01,
461
- description: rootMetadata.pricing.description || "",
462
- x402: true,
463
- openpondDirect: true,
464
- acceptedMethods: ["ETH", "USDC"],
465
- chainIds: [8453], // Base
466
- },
467
- }),
468
- // Discovery section (only include if metadata has discovery fields)
469
- ...((rootMetadata.keywords ||
470
- rootMetadata.categories ||
471
- rootMetadata.useCases ||
472
- rootMetadata.capabilities ||
473
- rootMetadata.requirements ||
474
- rootMetadata.pricing ||
475
- rootMetadata.compatibility ||
476
- rootMetadata.discovery) && {
477
- discovery: {
478
- keywords: rootMetadata.keywords || [],
479
- category: rootMetadata.categories?.[0] || smartDefaults.category,
480
- useCases: rootMetadata.useCases || [],
481
- capabilities: rootMetadata.capabilities || [],
482
- requirements: rootMetadata.requirements || {},
483
- pricing: rootMetadata.pricing || {},
484
- compatibility: rootMetadata.compatibility || {},
485
- ...(rootMetadata.discovery || {}), // Include any nested discovery fields too
486
- },
487
- }),
488
- };
489
- // Write metadata JSON to output directory
490
- const outputMetadataPath = path.join(config.outputDir, "metadata.json");
491
- fs.writeFileSync(outputMetadataPath, JSON.stringify(metadataJson, null, 2));
492
- 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, "\\'");
493
222
  }
494
223
  //# sourceMappingURL=build.js.map