lua-cli 1.3.2-alpha.2 → 1.3.2-alpha.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/commands/compile.js +569 -974
- package/dist/commands/init.js +10 -0
- package/dist/utils/files.js +4 -6
- package/dist/utils/sandbox.d.ts +1 -70
- package/dist/utils/sandbox.js +113 -3
- package/package.json +2 -1
- package/template/create-test.cjs +39 -0
- package/template/lua.skill.yaml +4 -3
- package/template/package-lock.json +34 -2
- package/template/package.json +3 -1
- package/template/src/index.ts +13 -2
- package/template/src/seed.ts +46 -0
- package/template/src/tools/GetWeatherTool.ts +32 -15
- package/template/src/tools/PaymentTool.ts +52 -0
- package/template/src/tools/SearchProducts.ts +43 -0
package/dist/commands/compile.js
CHANGED
|
@@ -1,1066 +1,661 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { gzipSync
|
|
4
|
-
import { Buffer } from "buffer";
|
|
3
|
+
import { gzipSync } from "zlib";
|
|
5
4
|
import { withErrorHandling, writeProgress, writeSuccess } from "../utils/cli.js";
|
|
6
5
|
import { readSkillConfig } from '../utils/files.js';
|
|
7
|
-
import
|
|
6
|
+
import { Project, Node } from "ts-morph";
|
|
7
|
+
import { build } from "esbuild";
|
|
8
8
|
// Compression utilities
|
|
9
9
|
function compressCode(code) {
|
|
10
10
|
const compressed = gzipSync(code);
|
|
11
11
|
return compressed.toString('base64');
|
|
12
12
|
}
|
|
13
|
-
function decompressCode(compressedCode) {
|
|
14
|
-
const buffer = Buffer.from(compressedCode, 'base64');
|
|
15
|
-
return gunzipSync(buffer).toString('utf8');
|
|
16
|
-
}
|
|
17
13
|
export async function compileCommand() {
|
|
18
14
|
return withErrorHandling(async () => {
|
|
19
15
|
writeProgress("🔨 Compiling Lua skill...");
|
|
20
|
-
// Clean up old
|
|
21
|
-
const
|
|
22
|
-
if (fs.existsSync(oldLuaDir)) {
|
|
23
|
-
fs.rmSync(oldLuaDir, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
// Read package.json to get version and name
|
|
26
|
-
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
27
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
28
|
-
throw new Error("package.json not found in current directory");
|
|
29
|
-
}
|
|
30
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
31
|
-
const packageVersion = packageJson.version || "1.0.0";
|
|
32
|
-
// Get skill name from config file, fallback to package.json name
|
|
33
|
-
const config = readSkillConfig();
|
|
34
|
-
const skillName = config?.skill?.name || packageJson.name || "lua-skill";
|
|
35
|
-
// Read index.ts file (check both root and src directory)
|
|
36
|
-
let indexPath = path.join(process.cwd(), "index.ts");
|
|
37
|
-
if (!fs.existsSync(indexPath)) {
|
|
38
|
-
indexPath = path.join(process.cwd(), "src", "index.ts");
|
|
39
|
-
if (!fs.existsSync(indexPath)) {
|
|
40
|
-
throw new Error("index.ts not found in current directory or src/ directory");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const indexContent = fs.readFileSync(indexPath, "utf8");
|
|
44
|
-
// Extract skill information
|
|
45
|
-
const skillInfo = await extractSkillInfo(indexContent, indexPath);
|
|
46
|
-
// Extract skill metadata from LuaSkill constructor and config file
|
|
47
|
-
const skillMetadata = await extractSkillMetadata(indexContent);
|
|
48
|
-
// Use version from config file, fallback to package.json version
|
|
49
|
-
const version = skillMetadata.version || packageVersion || "1.0.0";
|
|
50
|
-
// Create deployment data with compressed execute code
|
|
51
|
-
const deployData = {
|
|
52
|
-
version,
|
|
53
|
-
name: skillName,
|
|
54
|
-
skillsName: skillName,
|
|
55
|
-
skillId: skillMetadata.skillId,
|
|
56
|
-
description: skillMetadata.description,
|
|
57
|
-
context: skillMetadata.context,
|
|
58
|
-
tools: skillInfo.map(tool => ({
|
|
59
|
-
...tool,
|
|
60
|
-
execute: compressCode(tool.execute)
|
|
61
|
-
}))
|
|
62
|
-
};
|
|
63
|
-
// Create .lua directory
|
|
16
|
+
// Clean up old directories
|
|
17
|
+
const distDir = path.join(process.cwd(), "dist");
|
|
64
18
|
const luaDir = path.join(process.cwd(), ".lua");
|
|
65
|
-
if (
|
|
66
|
-
fs.
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
19
|
+
if (fs.existsSync(distDir)) {
|
|
20
|
+
fs.rmSync(distDir, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
if (fs.existsSync(luaDir)) {
|
|
23
|
+
fs.rmSync(luaDir, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
// Create directory structures
|
|
26
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
27
|
+
fs.mkdirSync(path.join(distDir, "tools"), { recursive: true });
|
|
28
|
+
fs.mkdirSync(luaDir, { recursive: true });
|
|
29
|
+
// Find index.ts file
|
|
30
|
+
const indexPath = findIndexFile();
|
|
31
|
+
// Use ts-morph to analyze the TypeScript project
|
|
32
|
+
const project = new Project({
|
|
33
|
+
tsConfigFilePath: path.join(process.cwd(), "tsconfig.json"),
|
|
34
|
+
});
|
|
35
|
+
// Add the index file to the project
|
|
36
|
+
const indexFile = project.addSourceFileAtPath(indexPath);
|
|
37
|
+
// Detect tools from skill.addTools calls
|
|
38
|
+
const tools = await detectTools(indexFile, project);
|
|
39
|
+
writeProgress(`📦 Found ${tools.length} tools to bundle...`);
|
|
40
|
+
// Bundle each tool individually and extract execute code
|
|
41
|
+
for (const tool of tools) {
|
|
42
|
+
await bundleTool(tool, distDir);
|
|
43
|
+
await extractExecuteCode(tool, project);
|
|
44
|
+
}
|
|
45
|
+
// Bundle the main index file
|
|
46
|
+
await bundleMainIndex(indexPath, distDir);
|
|
47
|
+
// Create both deployment formats
|
|
48
|
+
await createDeploymentData(tools, distDir);
|
|
49
|
+
await createLegacyDeploymentData(tools, luaDir, indexFile);
|
|
50
|
+
writeSuccess(`✅ Skill compiled successfully - ${tools.length} tools bundled`);
|
|
77
51
|
}, "compilation");
|
|
78
52
|
}
|
|
79
|
-
|
|
53
|
+
function findIndexFile() {
|
|
54
|
+
// Check for index.ts in current directory
|
|
55
|
+
let indexPath = path.join(process.cwd(), "index.ts");
|
|
56
|
+
if (fs.existsSync(indexPath)) {
|
|
57
|
+
return indexPath;
|
|
58
|
+
}
|
|
59
|
+
// Check for index.ts in src directory
|
|
60
|
+
indexPath = path.join(process.cwd(), "src", "index.ts");
|
|
61
|
+
if (fs.existsSync(indexPath)) {
|
|
62
|
+
return indexPath;
|
|
63
|
+
}
|
|
64
|
+
throw new Error("index.ts not found in current directory or src/ directory");
|
|
65
|
+
}
|
|
66
|
+
async function detectTools(indexFile, project) {
|
|
80
67
|
const tools = [];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
68
|
+
// Find all call expressions in the file
|
|
69
|
+
indexFile.forEachDescendant((node) => {
|
|
70
|
+
if (Node.isCallExpression(node)) {
|
|
71
|
+
const expression = node.getExpression();
|
|
72
|
+
// Check if this is skill.addTools or skill.addTool
|
|
73
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
74
|
+
const object = expression.getExpression();
|
|
75
|
+
const property = expression.getName();
|
|
76
|
+
if (object.getText() === 'skill' && (property === 'addTools' || property === 'addTool')) {
|
|
77
|
+
const args = node.getArguments();
|
|
78
|
+
if (property === 'addTools' && args.length > 0) {
|
|
79
|
+
// Handle skill.addTools([...]) - array of tools
|
|
80
|
+
const arrayArg = args[0];
|
|
81
|
+
if (Node.isArrayLiteralExpression(arrayArg)) {
|
|
82
|
+
const elements = arrayArg.getElements();
|
|
83
|
+
for (const element of elements) {
|
|
84
|
+
if (Node.isNewExpression(element)) {
|
|
85
|
+
const toolInfo = extractToolFromNewExpressionSync(element, project);
|
|
86
|
+
if (toolInfo) {
|
|
87
|
+
tools.push(toolInfo);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
103
92
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
else if (property === 'addTool' && args.length > 0) {
|
|
94
|
+
// Handle skill.addTool(new ToolClass()) - single tool
|
|
95
|
+
const arg = args[0];
|
|
96
|
+
if (Node.isNewExpression(arg)) {
|
|
97
|
+
const toolInfo = extractToolFromNewExpressionSync(arg, project);
|
|
98
|
+
if (toolInfo) {
|
|
99
|
+
tools.push(toolInfo);
|
|
100
|
+
}
|
|
110
101
|
}
|
|
111
102
|
}
|
|
112
103
|
}
|
|
113
104
|
}
|
|
114
|
-
// Continue traversing
|
|
115
|
-
ts.forEachChild(node, visit);
|
|
116
105
|
}
|
|
117
|
-
|
|
118
|
-
// Process all collected tool arguments
|
|
119
|
-
for (const toolArgument of toolArguments) {
|
|
120
|
-
await processToolArgument(toolArgument, sourceFile, indexContent, tools);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
console.warn('Warning: Could not parse TypeScript AST:', error);
|
|
125
|
-
return [];
|
|
126
|
-
}
|
|
106
|
+
});
|
|
127
107
|
return tools;
|
|
128
108
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
else if (ts.isNewExpression(toolArgument)) {
|
|
147
|
-
// Class-based tool: skill.addTool(new SomeTool())
|
|
148
|
-
await processClassBasedTool(toolArgument, sourceFile, indexContent, tools);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
async function processInlineTool(objectNode, sourceFile, indexContent, tools) {
|
|
152
|
-
const toolProperties = {};
|
|
153
|
-
// Extract properties from the object
|
|
154
|
-
for (const property of objectNode.properties) {
|
|
155
|
-
if (ts.isPropertyAssignment(property) &&
|
|
156
|
-
ts.isIdentifier(property.name) &&
|
|
157
|
-
property.initializer) {
|
|
158
|
-
const key = property.name.text;
|
|
159
|
-
const value = property.initializer;
|
|
160
|
-
if (key === 'name' && ts.isStringLiteral(value)) {
|
|
161
|
-
toolProperties.name = value.text;
|
|
162
|
-
}
|
|
163
|
-
else if (key === 'description' && ts.isStringLiteral(value)) {
|
|
164
|
-
toolProperties.description = value.text;
|
|
165
|
-
}
|
|
166
|
-
else if (key === 'inputSchema' && ts.isIdentifier(value)) {
|
|
167
|
-
toolProperties.inputSchemaVar = value.text;
|
|
168
|
-
}
|
|
169
|
-
else if (key === 'execute' && ts.isArrowFunction(value)) {
|
|
170
|
-
// Extract the function body
|
|
171
|
-
if (ts.isBlock(value.body)) {
|
|
172
|
-
const bodyText = extractFunctionBody(value.body, sourceFile);
|
|
173
|
-
toolProperties.executeBody = bodyText;
|
|
109
|
+
function extractToolFromNewExpressionSync(newExpr, project) {
|
|
110
|
+
try {
|
|
111
|
+
const expression = newExpr.getExpression();
|
|
112
|
+
const className = expression.getText();
|
|
113
|
+
// Find the import declaration for this class
|
|
114
|
+
const sourceFile = newExpr.getSourceFile();
|
|
115
|
+
const imports = sourceFile.getImportDeclarations();
|
|
116
|
+
for (const importDecl of imports) {
|
|
117
|
+
const namedImports = importDecl.getNamedImports();
|
|
118
|
+
const defaultImport = importDecl.getDefaultImport();
|
|
119
|
+
let toolFilePath = null;
|
|
120
|
+
// Check named imports
|
|
121
|
+
for (const namedImport of namedImports) {
|
|
122
|
+
if (namedImport.getName() === className) {
|
|
123
|
+
toolFilePath = resolveImportPath(importDecl.getModuleSpecifierValue(), sourceFile.getFilePath());
|
|
124
|
+
break;
|
|
174
125
|
}
|
|
175
126
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (toolProperties.name &&
|
|
180
|
-
toolProperties.description &&
|
|
181
|
-
toolProperties.inputSchemaVar &&
|
|
182
|
-
toolProperties.executeBody) {
|
|
183
|
-
// For inline tools, we need to find the schema definitions in the index file
|
|
184
|
-
const inputSchema = extractSchemaFromIndex(toolProperties.inputSchemaVar, indexContent);
|
|
185
|
-
const selfContainedExecute = await createSelfContainedExecute(toolProperties.executeBody, indexContent);
|
|
186
|
-
tools.push({
|
|
187
|
-
name: toolProperties.name,
|
|
188
|
-
description: toolProperties.description,
|
|
189
|
-
inputSchema: inputSchema || { type: "object" },
|
|
190
|
-
execute: selfContainedExecute
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async function processClassBasedTool(newNode, sourceFile, indexContent, tools) {
|
|
195
|
-
if (ts.isIdentifier(newNode.expression)) {
|
|
196
|
-
const className = newNode.expression.text;
|
|
197
|
-
const toolInfo = await extractToolFromClass(className, indexContent, sourceFile.fileName);
|
|
198
|
-
if (toolInfo) {
|
|
199
|
-
tools.push(toolInfo);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function extractFunctionBody(blockStatement, sourceFile) {
|
|
204
|
-
// Extract the function body text from the AST
|
|
205
|
-
const start = blockStatement.getStart(sourceFile);
|
|
206
|
-
const end = blockStatement.getEnd();
|
|
207
|
-
// Get the text between the braces
|
|
208
|
-
const fullText = sourceFile.text.substring(start, end);
|
|
209
|
-
// Remove the outer braces and return the inner content
|
|
210
|
-
if (fullText.startsWith('{') && fullText.endsWith('}')) {
|
|
211
|
-
return fullText.slice(1, -1).trim();
|
|
212
|
-
}
|
|
213
|
-
return fullText;
|
|
214
|
-
}
|
|
215
|
-
function convertZodTypeToJSONSchema(zodType) {
|
|
216
|
-
switch (zodType) {
|
|
217
|
-
case 'string':
|
|
218
|
-
return { type: 'string' };
|
|
219
|
-
case 'number':
|
|
220
|
-
return { type: 'number' };
|
|
221
|
-
case 'boolean':
|
|
222
|
-
return { type: 'boolean' };
|
|
223
|
-
default:
|
|
224
|
-
return { type: 'string' };
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function extractZodSchemaFromAST(node) {
|
|
228
|
-
// Handle z.object({...}) calls
|
|
229
|
-
if (ts.isCallExpression(node) &&
|
|
230
|
-
ts.isPropertyAccessExpression(node.expression) &&
|
|
231
|
-
ts.isIdentifier(node.expression.expression) &&
|
|
232
|
-
node.expression.expression.text === 'z' &&
|
|
233
|
-
ts.isIdentifier(node.expression.name) &&
|
|
234
|
-
node.expression.name.text === 'object') {
|
|
235
|
-
const objectLiteral = node.arguments[0];
|
|
236
|
-
if (ts.isObjectLiteralExpression(objectLiteral)) {
|
|
237
|
-
const properties = {};
|
|
238
|
-
const required = [];
|
|
239
|
-
for (const property of objectLiteral.properties) {
|
|
240
|
-
if (ts.isPropertyAssignment(property) &&
|
|
241
|
-
ts.isIdentifier(property.name) &&
|
|
242
|
-
property.initializer) {
|
|
243
|
-
const propertyName = property.name.text;
|
|
244
|
-
const zodType = extractZodTypeFromAST(property.initializer);
|
|
245
|
-
properties[propertyName] = zodType;
|
|
246
|
-
// Only add to required if it's not optional
|
|
247
|
-
if (!isOptionalZodType(property.initializer)) {
|
|
248
|
-
required.push(propertyName);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
127
|
+
// Check default import
|
|
128
|
+
if (!toolFilePath && defaultImport && defaultImport.getText() === className) {
|
|
129
|
+
toolFilePath = resolveImportPath(importDecl.getModuleSpecifierValue(), sourceFile.getFilePath());
|
|
251
130
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
case 'array':
|
|
276
|
-
// Handle z.array(z.string()) etc.
|
|
277
|
-
if (node.arguments.length > 0) {
|
|
278
|
-
const elementType = extractZodTypeFromAST(node.arguments[0]);
|
|
279
|
-
return { type: 'array', items: elementType };
|
|
280
|
-
}
|
|
281
|
-
return { type: 'array' };
|
|
282
|
-
case 'object':
|
|
283
|
-
// Handle nested z.object({...}) calls
|
|
284
|
-
if (node.arguments.length > 0) {
|
|
285
|
-
return extractZodSchemaFromAST(node);
|
|
286
|
-
}
|
|
287
|
-
return { type: 'object' };
|
|
288
|
-
case 'enum':
|
|
289
|
-
// Handle z.enum(['a', 'b', 'c'])
|
|
290
|
-
if (node.arguments.length > 0 && ts.isArrayLiteralExpression(node.arguments[0])) {
|
|
291
|
-
const enumValues = [];
|
|
292
|
-
for (const element of node.arguments[0].elements) {
|
|
293
|
-
if (ts.isStringLiteral(element)) {
|
|
294
|
-
enumValues.push(element.text);
|
|
131
|
+
if (toolFilePath && fs.existsSync(toolFilePath)) {
|
|
132
|
+
// Extract tool metadata from the class file
|
|
133
|
+
try {
|
|
134
|
+
const toolSourceFile = project.addSourceFileAtPath(toolFilePath);
|
|
135
|
+
const classDecl = toolSourceFile.getClass(className);
|
|
136
|
+
if (classDecl) {
|
|
137
|
+
const nameProperty = classDecl.getProperty('name');
|
|
138
|
+
const descProperty = classDecl.getProperty('description');
|
|
139
|
+
let toolName = className.replace(/Tool$/, '').toLowerCase();
|
|
140
|
+
let description = '';
|
|
141
|
+
// Extract name from property if available
|
|
142
|
+
if (nameProperty && nameProperty.getInitializer()) {
|
|
143
|
+
const nameValue = nameProperty.getInitializer()?.getText();
|
|
144
|
+
if (nameValue) {
|
|
145
|
+
toolName = nameValue.replace(/['"]/g, '');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Extract description from property if available
|
|
149
|
+
if (descProperty && descProperty.getInitializer()) {
|
|
150
|
+
const descValue = descProperty.getInitializer()?.getText();
|
|
151
|
+
if (descValue) {
|
|
152
|
+
description = descValue.replace(/['"]/g, '');
|
|
153
|
+
}
|
|
295
154
|
}
|
|
155
|
+
return {
|
|
156
|
+
name: toolName,
|
|
157
|
+
className,
|
|
158
|
+
filePath: toolFilePath,
|
|
159
|
+
description
|
|
160
|
+
};
|
|
296
161
|
}
|
|
297
|
-
return { type: 'string', enum: enumValues };
|
|
298
162
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
163
|
+
catch (fileError) {
|
|
164
|
+
console.warn(`Warning: Could not load tool file ${toolFilePath}:`, fileError);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
302
167
|
}
|
|
168
|
+
return null;
|
|
303
169
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
ts.isCallExpression(node.expression.expression)) {
|
|
308
|
-
const baseType = extractZodTypeFromAST(node.expression.expression);
|
|
309
|
-
const method = node.expression.name.text;
|
|
310
|
-
if (method === 'optional') {
|
|
311
|
-
// Optional fields are handled by not including them in required array
|
|
312
|
-
return baseType;
|
|
313
|
-
}
|
|
314
|
-
return baseType;
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.warn(`Warning: Could not extract tool info for ${newExpr.getText()}:`, error);
|
|
172
|
+
return null;
|
|
315
173
|
}
|
|
316
|
-
return { type: 'string' };
|
|
317
174
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
node.expression.name.text === 'optional') {
|
|
324
|
-
return true;
|
|
175
|
+
function resolveImportPath(moduleSpecifier, currentFilePath) {
|
|
176
|
+
if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../')) {
|
|
177
|
+
// Relative import - resolve relative to current file
|
|
178
|
+
const currentDir = path.dirname(currentFilePath);
|
|
179
|
+
return path.resolve(currentDir, moduleSpecifier + '.ts');
|
|
325
180
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// Create a source file from the index content to parse schemas using AST
|
|
330
|
-
const sourceFile = ts.createSourceFile('index.ts', indexContent, ts.ScriptTarget.Latest, true);
|
|
331
|
-
let schema = null;
|
|
332
|
-
function findSchema(node) {
|
|
333
|
-
// Look for const schemaVar = z.object({...}) declarations
|
|
334
|
-
if (ts.isVariableDeclaration(node) &&
|
|
335
|
-
ts.isIdentifier(node.name) &&
|
|
336
|
-
node.name.text === schemaVar &&
|
|
337
|
-
node.initializer) {
|
|
338
|
-
schema = extractZodSchemaFromAST(node.initializer);
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
ts.forEachChild(node, findSchema);
|
|
181
|
+
else {
|
|
182
|
+
// Absolute import - assume it's in the project
|
|
183
|
+
return path.resolve(process.cwd(), 'src', moduleSpecifier + '.ts');
|
|
342
184
|
}
|
|
343
|
-
findSchema(sourceFile);
|
|
344
|
-
return schema;
|
|
345
185
|
}
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
186
|
+
async function bundleTool(tool, distDir) {
|
|
187
|
+
writeProgress(`📦 Bundling ${tool.className}...`);
|
|
188
|
+
try {
|
|
189
|
+
const outputPath = path.join(distDir, 'tools', `${tool.className}.js`);
|
|
190
|
+
await build({
|
|
191
|
+
entryPoints: [tool.filePath],
|
|
192
|
+
bundle: true,
|
|
193
|
+
format: 'cjs',
|
|
194
|
+
platform: 'node',
|
|
195
|
+
target: 'node16',
|
|
196
|
+
outfile: outputPath,
|
|
197
|
+
external: ['lua-cli/skill', 'lua-cli/user-data-api', 'zod'], // Exclude lua-cli modules and zod - injected into VM
|
|
198
|
+
minify: true, // Minify for smaller file sizes
|
|
199
|
+
sourcemap: false,
|
|
200
|
+
resolveExtensions: ['.ts', '.js', '.json'],
|
|
201
|
+
define: {
|
|
202
|
+
'process.env.NODE_ENV': '"production"'
|
|
203
|
+
},
|
|
204
|
+
// Ensure all other dependencies are bundled
|
|
205
|
+
packages: 'bundle',
|
|
206
|
+
// Handle different module formats
|
|
207
|
+
mainFields: ['main', 'module'],
|
|
208
|
+
conditions: ['node']
|
|
209
|
+
});
|
|
210
|
+
// Add VM-compatible wrapper
|
|
211
|
+
await wrapToolForVM(outputPath, tool);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.warn(`Warning: Failed to bundle ${tool.className}:`, error);
|
|
356
215
|
}
|
|
357
|
-
return envVars;
|
|
358
216
|
}
|
|
359
|
-
async function
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
//
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
continue;
|
|
372
|
-
}
|
|
373
|
-
// Skip lua-cli imports (these are handled separately)
|
|
374
|
-
if (packagePath.startsWith('lua-cli')) {
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
// Skip zod - assume it's always available on target machine
|
|
378
|
-
if (packagePath === 'zod') {
|
|
379
|
-
// Add require statement for zod instead of bundling
|
|
380
|
-
if (namedImports) {
|
|
381
|
-
const importsList = namedImports.split(',').map(imp => imp.trim());
|
|
382
|
-
const usedImports = importsList.filter(imp => executeBody.includes(imp) || indexContent.includes(`${imp}.`));
|
|
383
|
-
if (usedImports.length > 0) {
|
|
384
|
-
const requireStatement = usedImports.length === 1
|
|
385
|
-
? `const { ${usedImports[0]} } = require('zod');`
|
|
386
|
-
: `const { ${usedImports.join(', ')} } = require('zod');`;
|
|
387
|
-
bundledPackages.add(requireStatement);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
else if (defaultImport) {
|
|
391
|
-
bundledPackages.add(`const ${defaultImport} = require('zod');`);
|
|
392
|
-
}
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
// Bundle other external packages
|
|
396
|
-
if (namedImports || defaultImport) {
|
|
397
|
-
const packageCode = await bundlePackageCode(packagePath, namedImports, defaultImport);
|
|
398
|
-
if (packageCode) {
|
|
399
|
-
bundledPackages.add(packageCode);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
217
|
+
async function wrapToolForVM(outputPath, tool) {
|
|
218
|
+
const bundledCode = fs.readFileSync(outputPath, 'utf8');
|
|
219
|
+
// Create a wrapper that's compatible with the existing sandbox.ts VM system
|
|
220
|
+
// The sandbox expects: const executeFunction = ${toolCode}; module.exports = async (input) => { return await executeFunction(input); };
|
|
221
|
+
const wrappedCode = `async (input) => {
|
|
222
|
+
// Mock lua-cli/skill module for external dependencies
|
|
223
|
+
const luaCliSkill = {
|
|
224
|
+
env: function(key) {
|
|
225
|
+
if (typeof env !== 'undefined') {
|
|
226
|
+
return env(key);
|
|
227
|
+
}
|
|
228
|
+
return process.env[key] || '';
|
|
402
229
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
for (let i = classStart; i < indexContent.length; i++) {
|
|
414
|
-
if (indexContent[i] === '{') {
|
|
415
|
-
braceCount++;
|
|
416
|
-
}
|
|
417
|
-
else if (indexContent[i] === '}') {
|
|
418
|
-
braceCount--;
|
|
419
|
-
if (braceCount === 0) {
|
|
420
|
-
classEnd = i;
|
|
421
|
-
found = true;
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (found) {
|
|
427
|
-
const fullClass = indexContent.substring(classStart, classEnd + 1);
|
|
428
|
-
// Check if this class is used in the execute function
|
|
429
|
-
let isUsed = false;
|
|
430
|
-
// Direct usage in execute body
|
|
431
|
-
if (executeBody.includes(`new ${className}`) ||
|
|
432
|
-
executeBody.includes(`${className}.`) ||
|
|
433
|
-
executeBody.includes(`${className}(`)) {
|
|
434
|
-
isUsed = true;
|
|
435
|
-
}
|
|
436
|
-
// Check if any variable that uses this class is referenced in execute body
|
|
437
|
-
const variableRegex = new RegExp(`(?:const|let|var)\\s+(\\w+)\\s*=\\s*new\\s+${className}\\s*\\([^)]*\\);`, 'g');
|
|
438
|
-
let varMatch;
|
|
439
|
-
while ((varMatch = variableRegex.exec(indexContent)) !== null) {
|
|
440
|
-
const varName = varMatch[1];
|
|
441
|
-
if (executeBody.includes(varName)) {
|
|
442
|
-
isUsed = true;
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (isUsed) {
|
|
447
|
-
dependencies.push(fullClass);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Mock lua-cli/user-data-api module
|
|
233
|
+
const luaCliUserDataApi = {
|
|
234
|
+
user: typeof user !== 'undefined' ? user : {
|
|
235
|
+
data: {
|
|
236
|
+
get: async () => ({}),
|
|
237
|
+
update: async (data) => data,
|
|
238
|
+
create: async (data) => data
|
|
239
|
+
}
|
|
450
240
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Mock zod module
|
|
244
|
+
const zodModule = (() => {
|
|
245
|
+
try {
|
|
246
|
+
if (typeof require !== 'undefined') {
|
|
247
|
+
return require('zod');
|
|
248
|
+
}
|
|
249
|
+
} catch (e) {
|
|
250
|
+
// Fallback zod implementation
|
|
460
251
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
252
|
+
return {
|
|
253
|
+
z: {
|
|
254
|
+
object: (schema) => ({
|
|
255
|
+
parse: (data) => data,
|
|
256
|
+
safeParse: (data) => ({ success: true, data })
|
|
257
|
+
}),
|
|
258
|
+
string: () => ({
|
|
259
|
+
parse: (data) => String(data),
|
|
260
|
+
safeParse: (data) => ({ success: true, data: String(data) })
|
|
261
|
+
}),
|
|
262
|
+
number: () => ({
|
|
263
|
+
parse: (data) => Number(data),
|
|
264
|
+
safeParse: (data) => ({ success: true, data: Number(data) })
|
|
265
|
+
}),
|
|
266
|
+
boolean: () => ({
|
|
267
|
+
parse: (data) => Boolean(data),
|
|
268
|
+
safeParse: (data) => ({ success: true, data: Boolean(data) })
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
})();
|
|
273
|
+
|
|
274
|
+
// Override require for external modules during execution
|
|
275
|
+
const originalRequire = require;
|
|
276
|
+
require = function(id) {
|
|
277
|
+
if (id === 'lua-cli/skill') {
|
|
278
|
+
return luaCliSkill;
|
|
480
279
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
let instantiationMatch;
|
|
484
|
-
while ((instantiationMatch = instantiationRegex.exec(indexContent)) !== null) {
|
|
485
|
-
const instanceName = instantiationMatch[1];
|
|
486
|
-
const className = instantiationMatch[2];
|
|
487
|
-
// Skip if already declared
|
|
488
|
-
if (declaredVars.has(instanceName)) {
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
if (executeBody.includes(instanceName)) {
|
|
492
|
-
declaredVars.add(instanceName);
|
|
493
|
-
dependencies.push(`const ${instanceName} = new ${className}();`);
|
|
494
|
-
}
|
|
280
|
+
if (id === 'lua-cli/user-data-api') {
|
|
281
|
+
return luaCliUserDataApi;
|
|
495
282
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
.replace(/:\s*Record<[^>]+>/g, ''); // Remove : Record<Type>
|
|
515
|
-
}).join('\n');
|
|
516
|
-
const cleanExecuteBody = executeBody
|
|
517
|
-
.replace(/:\s*string/g, '') // Remove : string
|
|
518
|
-
.replace(/:\s*number/g, '') // Remove : number
|
|
519
|
-
.replace(/:\s*boolean/g, '') // Remove : boolean
|
|
520
|
-
.replace(/:\s*any/g, '') // Remove : any
|
|
521
|
-
.replace(/:\s*void/g, '') // Remove : void
|
|
522
|
-
.replace(/:\s*object/g, '') // Remove : object
|
|
523
|
-
.replace(/:\s*Array<[^>]+>/g, '') // Remove : Array<Type>
|
|
524
|
-
.replace(/:\s*Promise<[^>]+>/g, '') // Remove : Promise<Type>
|
|
525
|
-
.replace(/:\s*Record<[^>]+>/g, ''); // Remove : Record<Type>
|
|
526
|
-
const selfContainedExecute = `async (input) => {
|
|
527
|
-
${cleanDependencyCode ? ` ${cleanDependencyCode.split('\n').join('\n ')}\n` : ''} ${cleanExecuteBody.trim()}
|
|
283
|
+
if (id === 'zod') {
|
|
284
|
+
return zodModule;
|
|
285
|
+
}
|
|
286
|
+
return originalRequire(id);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Execute the bundled tool code
|
|
290
|
+
${bundledCode}
|
|
291
|
+
|
|
292
|
+
// Restore original require
|
|
293
|
+
require = originalRequire;
|
|
294
|
+
|
|
295
|
+
// Get the tool class from exports
|
|
296
|
+
const ToolClass = module.exports.default || module.exports.${tool.className} || module.exports;
|
|
297
|
+
|
|
298
|
+
// Create and execute the tool
|
|
299
|
+
const toolInstance = new ToolClass();
|
|
300
|
+
return await toolInstance.execute(input);
|
|
528
301
|
}`;
|
|
529
|
-
|
|
302
|
+
fs.writeFileSync(outputPath, wrappedCode);
|
|
530
303
|
}
|
|
531
|
-
async function
|
|
304
|
+
async function bundleMainIndex(indexPath, distDir) {
|
|
305
|
+
writeProgress("📦 Bundling main index...");
|
|
532
306
|
try {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
const luaDir = path.join(process.cwd(), '.lua');
|
|
536
|
-
if (!fs.existsSync(luaDir)) {
|
|
537
|
-
fs.mkdirSync(luaDir, { recursive: true });
|
|
538
|
-
}
|
|
539
|
-
const entryFile = path.join(luaDir, `${packagePath}-entry.cjs`);
|
|
540
|
-
const outputFile = path.join(luaDir, `${packagePath}-bundle.cjs`);
|
|
541
|
-
// Create entry file based on import type
|
|
542
|
-
let entryContent = '';
|
|
543
|
-
if (defaultImport) {
|
|
544
|
-
// For default imports like `import axios from 'axios'`
|
|
545
|
-
entryContent = `import ${defaultImport} from '${packagePath}';\nmodule.exports = ${defaultImport};`;
|
|
546
|
-
}
|
|
547
|
-
else if (namedImports) {
|
|
548
|
-
// For named imports like `import { z } from 'zod'`
|
|
549
|
-
const importsList = namedImports.split(',').map(imp => imp.trim());
|
|
550
|
-
entryContent = `import { ${importsList.join(', ')} } from '${packagePath}';\nmodule.exports = { ${importsList.join(', ')} };`;
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
// Fallback - import everything
|
|
554
|
-
entryContent = `import * as ${packagePath.replace(/[^a-zA-Z0-9]/g, '_')} from '${packagePath}';\nmodule.exports = ${packagePath.replace(/[^a-zA-Z0-9]/g, '_')};`;
|
|
555
|
-
}
|
|
556
|
-
// Write entry file
|
|
557
|
-
fs.writeFileSync(entryFile, entryContent);
|
|
558
|
-
// Bundle with esbuild
|
|
559
|
-
const result = await build({
|
|
560
|
-
entryPoints: [entryFile],
|
|
307
|
+
await build({
|
|
308
|
+
entryPoints: [indexPath],
|
|
561
309
|
bundle: true,
|
|
562
|
-
format: 'cjs',
|
|
310
|
+
format: 'cjs',
|
|
563
311
|
platform: 'node',
|
|
564
312
|
target: 'node16',
|
|
565
|
-
outfile:
|
|
566
|
-
external: [], //
|
|
567
|
-
minify:
|
|
313
|
+
outfile: path.join(distDir, 'index.js'),
|
|
314
|
+
external: ['lua-cli/skill', 'lua-cli/user-data-api', 'zod'], // Exclude lua-cli modules and zod
|
|
315
|
+
minify: true, // Minify for smaller file sizes
|
|
568
316
|
sourcemap: false,
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
path.join(process.cwd(), '..', '..', 'node_modules')
|
|
577
|
-
],
|
|
578
|
-
absWorkingDir: process.cwd(),
|
|
317
|
+
resolveExtensions: ['.ts', '.js', '.json'],
|
|
318
|
+
define: {
|
|
319
|
+
'process.env.NODE_ENV': '"production"'
|
|
320
|
+
},
|
|
321
|
+
packages: 'bundle',
|
|
322
|
+
mainFields: ['main', 'module'],
|
|
323
|
+
conditions: ['node']
|
|
579
324
|
});
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.warn("Warning: Failed to bundle main index:", error);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function extractExecuteCode(tool, project) {
|
|
331
|
+
try {
|
|
332
|
+
const toolSourceFile = project.getSourceFile(tool.filePath);
|
|
333
|
+
if (!toolSourceFile) {
|
|
334
|
+
console.warn(`Warning: Could not find source file for ${tool.className}`);
|
|
335
|
+
return;
|
|
588
336
|
}
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
fs.unlinkSync(outputFile);
|
|
337
|
+
const classDecl = toolSourceFile.getClass(tool.className);
|
|
338
|
+
if (!classDecl) {
|
|
339
|
+
console.warn(`Warning: Could not find class ${tool.className} in ${tool.filePath}`);
|
|
340
|
+
return;
|
|
594
341
|
}
|
|
595
|
-
|
|
596
|
-
|
|
342
|
+
// Extract the execute method
|
|
343
|
+
const executeMethod = classDecl.getMethod('execute');
|
|
344
|
+
if (!executeMethod) {
|
|
345
|
+
console.warn(`Warning: Could not find execute method in ${tool.className}`);
|
|
346
|
+
return;
|
|
597
347
|
}
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
if (
|
|
601
|
-
|
|
348
|
+
// Get the execute method body
|
|
349
|
+
const executeBody = executeMethod.getBodyText();
|
|
350
|
+
if (!executeBody) {
|
|
351
|
+
console.warn(`Warning: Execute method has no body in ${tool.className}`);
|
|
352
|
+
return;
|
|
602
353
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
354
|
+
// Read the bundled tool file to get the self-contained code
|
|
355
|
+
const bundledPath = path.join(process.cwd(), 'dist', 'tools', `${tool.className}.js`);
|
|
356
|
+
if (fs.existsSync(bundledPath)) {
|
|
357
|
+
const bundledCode = fs.readFileSync(bundledPath, 'utf8');
|
|
358
|
+
// Extract just the tool execution function from the bundled code
|
|
359
|
+
// The bundled code contains the VM wrapper, we need to extract the core functionality
|
|
360
|
+
const executeFunction = createExecuteFunction(bundledCode, tool);
|
|
361
|
+
tool.executeCode = executeFunction;
|
|
362
|
+
}
|
|
363
|
+
// Extract input schema from zod definition
|
|
364
|
+
const inputSchemaProperty = classDecl.getProperty('inputSchema');
|
|
365
|
+
if (inputSchemaProperty && inputSchemaProperty.getInitializer()) {
|
|
366
|
+
const initializer = inputSchemaProperty.getInitializer();
|
|
367
|
+
if (initializer) {
|
|
368
|
+
// Parse the zod schema to extract JSON schema
|
|
369
|
+
tool.inputSchema = parseZodSchemaToJsonSchema(initializer.getText());
|
|
370
|
+
}
|
|
607
371
|
}
|
|
608
|
-
|
|
609
|
-
|
|
372
|
+
if (!tool.inputSchema) {
|
|
373
|
+
tool.inputSchema = { type: "object" };
|
|
610
374
|
}
|
|
611
|
-
return finalCode;
|
|
612
375
|
}
|
|
613
376
|
catch (error) {
|
|
614
|
-
console.warn(`Warning: Could not
|
|
615
|
-
// For test environments or when esbuild fails, provide a fallback
|
|
616
|
-
if (packagePath === 'axios') {
|
|
617
|
-
return createWorkingAxiosImplementation();
|
|
618
|
-
}
|
|
619
|
-
return null;
|
|
377
|
+
console.warn(`Warning: Could not extract execute code for ${tool.className}:`, error);
|
|
620
378
|
}
|
|
621
379
|
}
|
|
622
|
-
function
|
|
623
|
-
|
|
624
|
-
//
|
|
625
|
-
|
|
626
|
-
get: async (url, config = {}) => {
|
|
627
|
-
const searchParams = new URLSearchParams(config.params || {});
|
|
628
|
-
const fullUrl = searchParams.toString() ? \`\${url}?\${searchParams}\` : url;
|
|
629
|
-
|
|
630
|
-
const response = await fetch(fullUrl, {
|
|
631
|
-
method: 'GET',
|
|
632
|
-
headers: {
|
|
633
|
-
'Content-Type': 'application/json',
|
|
634
|
-
...config.headers
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
if (!response.ok) {
|
|
639
|
-
const error = new Error(\`Request failed with status \${response.status}\`);
|
|
640
|
-
error.response = { status: response.status, statusText: response.statusText };
|
|
641
|
-
throw error;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const data = await response.json();
|
|
645
|
-
return {
|
|
646
|
-
data,
|
|
647
|
-
status: response.status,
|
|
648
|
-
statusText: response.statusText,
|
|
649
|
-
headers: response.headers,
|
|
650
|
-
config: config
|
|
651
|
-
};
|
|
652
|
-
},
|
|
653
|
-
|
|
654
|
-
post: async (url, data, config = {}) => {
|
|
655
|
-
const response = await fetch(url, {
|
|
656
|
-
method: 'POST',
|
|
657
|
-
headers: {
|
|
658
|
-
'Content-Type': 'application/json',
|
|
659
|
-
...config.headers
|
|
660
|
-
},
|
|
661
|
-
body: JSON.stringify(data)
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
if (!response.ok) {
|
|
665
|
-
const error = new Error(\`Request failed with status \${response.status}\`);
|
|
666
|
-
error.response = { status: response.status, statusText: response.statusText };
|
|
667
|
-
throw error;
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const responseData = await response.json();
|
|
671
|
-
return {
|
|
672
|
-
data: responseData,
|
|
673
|
-
status: response.status,
|
|
674
|
-
statusText: response.statusText,
|
|
675
|
-
headers: response.headers,
|
|
676
|
-
config: config
|
|
677
|
-
};
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
put: async (url, data, config = {}) => {
|
|
681
|
-
const response = await fetch(url, {
|
|
682
|
-
method: 'PUT',
|
|
683
|
-
headers: {
|
|
684
|
-
'Content-Type': 'application/json',
|
|
685
|
-
...config.headers
|
|
686
|
-
},
|
|
687
|
-
body: JSON.stringify(data)
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
if (!response.ok) {
|
|
691
|
-
const error = new Error(\`Request failed with status \${response.status}\`);
|
|
692
|
-
error.response = { status: response.status, statusText: response.statusText };
|
|
693
|
-
throw error;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const responseData = await response.json();
|
|
697
|
-
return {
|
|
698
|
-
data: responseData,
|
|
699
|
-
status: response.status,
|
|
700
|
-
statusText: response.statusText,
|
|
701
|
-
headers: response.headers,
|
|
702
|
-
config: config
|
|
703
|
-
};
|
|
704
|
-
},
|
|
705
|
-
|
|
706
|
-
delete: async (url, config = {}) => {
|
|
707
|
-
const response = await fetch(url, {
|
|
708
|
-
method: 'DELETE',
|
|
709
|
-
headers: {
|
|
710
|
-
'Content-Type': 'application/json',
|
|
711
|
-
...config.headers
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
if (!response.ok) {
|
|
716
|
-
const error = new Error(\`Request failed with status \${response.status}\`);
|
|
717
|
-
error.response = { status: response.status, statusText: response.statusText };
|
|
718
|
-
throw error;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
const responseData = await response.json();
|
|
722
|
-
return {
|
|
723
|
-
data: responseData,
|
|
724
|
-
status: response.status,
|
|
725
|
-
statusText: response.statusText,
|
|
726
|
-
headers: response.headers,
|
|
727
|
-
config: config
|
|
728
|
-
};
|
|
729
|
-
},
|
|
730
|
-
|
|
731
|
-
patch: async (url, data, config = {}) => {
|
|
732
|
-
const response = await fetch(url, {
|
|
733
|
-
method: 'PATCH',
|
|
734
|
-
headers: {
|
|
735
|
-
'Content-Type': 'application/json',
|
|
736
|
-
...config.headers
|
|
737
|
-
},
|
|
738
|
-
body: JSON.stringify(data)
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
if (!response.ok) {
|
|
742
|
-
const error = new Error(\`Request failed with status \${response.status}\`);
|
|
743
|
-
error.response = { status: response.status, statusText: response.statusText };
|
|
744
|
-
throw error;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
const responseData = await response.json();
|
|
748
|
-
return {
|
|
749
|
-
data: responseData,
|
|
750
|
-
status: response.status,
|
|
751
|
-
statusText: response.statusText,
|
|
752
|
-
headers: response.headers,
|
|
753
|
-
config: config
|
|
754
|
-
};
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
`;
|
|
380
|
+
function createExecuteFunction(bundledCode, tool) {
|
|
381
|
+
// The bundled code is already wrapped for VM execution by wrapToolForVM
|
|
382
|
+
// Just return it directly since it's already an async function
|
|
383
|
+
return bundledCode;
|
|
758
384
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
for (const element of node.importClause.namedBindings.elements) {
|
|
774
|
-
if (element.name.text === className) {
|
|
775
|
-
importPath = moduleSpecifier.text;
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
385
|
+
function parseZodSchemaToJsonSchema(zodSchemaText) {
|
|
386
|
+
try {
|
|
387
|
+
// Use ts-morph to properly parse the Zod schema
|
|
388
|
+
const tempProject = new Project();
|
|
389
|
+
const tempFile = tempProject.createSourceFile('temp.ts', `
|
|
390
|
+
import { z } from 'zod';
|
|
391
|
+
const schema = ${zodSchemaText};
|
|
392
|
+
`);
|
|
393
|
+
// Find the schema variable declaration
|
|
394
|
+
const schemaDeclaration = tempFile.getVariableDeclaration('schema');
|
|
395
|
+
if (schemaDeclaration && schemaDeclaration.getInitializer()) {
|
|
396
|
+
const initializer = schemaDeclaration.getInitializer();
|
|
397
|
+
if (initializer) {
|
|
398
|
+
return parseZodASTToJsonSchema(initializer);
|
|
779
399
|
}
|
|
780
400
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
findImport(sourceFile);
|
|
784
|
-
if (!importPath) {
|
|
785
|
-
console.warn(`Warning: Could not find import for class ${className}`);
|
|
786
|
-
return null;
|
|
401
|
+
// Fallback to direct parsing if AST approach fails
|
|
402
|
+
return parseZodTextToJsonSchema(zodSchemaText);
|
|
787
403
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
// Relative path - resolve from the index file directory
|
|
792
|
-
const indexDir = path.dirname(indexFilePath);
|
|
793
|
-
toolFilePath = path.join(indexDir, importPath + '.ts');
|
|
794
|
-
}
|
|
795
|
-
else {
|
|
796
|
-
// Absolute path or just filename
|
|
797
|
-
toolFilePath = path.join(process.cwd(), importPath + '.ts');
|
|
798
|
-
}
|
|
799
|
-
if (!fs.existsSync(toolFilePath)) {
|
|
800
|
-
console.warn(`Warning: Tool file not found: ${toolFilePath}`);
|
|
801
|
-
return null;
|
|
404
|
+
catch (error) {
|
|
405
|
+
console.warn('Warning: Could not parse Zod schema with AST, falling back to text parsing:', error);
|
|
406
|
+
return parseZodTextToJsonSchema(zodSchemaText);
|
|
802
407
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
408
|
+
}
|
|
409
|
+
function parseZodASTToJsonSchema(node) {
|
|
410
|
+
try {
|
|
411
|
+
// Handle z.object({ ... })
|
|
412
|
+
if (Node.isCallExpression(node)) {
|
|
413
|
+
const expression = node.getExpression();
|
|
414
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
415
|
+
const object = expression.getExpression();
|
|
416
|
+
const property = expression.getName();
|
|
417
|
+
if (object.getText() === 'z' && property === 'object') {
|
|
418
|
+
const args = node.getArguments();
|
|
419
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
420
|
+
const objectLiteral = args[0];
|
|
421
|
+
const properties = {};
|
|
422
|
+
const required = [];
|
|
423
|
+
objectLiteral.getProperties().forEach((prop) => {
|
|
424
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
425
|
+
const name = prop.getName();
|
|
426
|
+
const value = prop.getInitializer();
|
|
427
|
+
if (value) {
|
|
428
|
+
const propSchema = parseZodASTToJsonSchema(value);
|
|
429
|
+
properties[name] = propSchema;
|
|
430
|
+
// Check if it's required (not optional)
|
|
431
|
+
if (!isOptionalZodType(value)) {
|
|
432
|
+
required.push(name);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return {
|
|
438
|
+
type: 'object',
|
|
439
|
+
properties,
|
|
440
|
+
required
|
|
441
|
+
};
|
|
824
442
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
443
|
+
}
|
|
444
|
+
// Handle other z.* types
|
|
445
|
+
if (object.getText() === 'z') {
|
|
446
|
+
switch (property) {
|
|
447
|
+
case 'string':
|
|
448
|
+
return { type: 'string' };
|
|
449
|
+
case 'number':
|
|
450
|
+
return { type: 'number' };
|
|
451
|
+
case 'boolean':
|
|
452
|
+
return { type: 'boolean' };
|
|
453
|
+
case 'any':
|
|
454
|
+
return { type: 'object' };
|
|
455
|
+
case 'array':
|
|
456
|
+
const arrayArgs = node.getArguments();
|
|
457
|
+
if (arrayArgs.length > 0) {
|
|
458
|
+
const itemSchema = parseZodASTToJsonSchema(arrayArgs[0]);
|
|
459
|
+
return { type: 'array', items: itemSchema };
|
|
460
|
+
}
|
|
461
|
+
return { type: 'array' };
|
|
462
|
+
case 'enum':
|
|
463
|
+
const enumArgs = node.getArguments();
|
|
464
|
+
if (enumArgs.length > 0 && Node.isArrayLiteralExpression(enumArgs[0])) {
|
|
465
|
+
const enumValues = enumArgs[0].getElements().map((element) => {
|
|
466
|
+
if (Node.isStringLiteral(element)) {
|
|
467
|
+
return element.getLiteralValue();
|
|
468
|
+
}
|
|
469
|
+
return element.getText().replace(/['"]/g, '');
|
|
470
|
+
});
|
|
471
|
+
return { type: 'string', enum: enumValues };
|
|
472
|
+
}
|
|
473
|
+
return { type: 'string' };
|
|
828
474
|
}
|
|
829
475
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
476
|
+
}
|
|
477
|
+
// Handle method chaining like z.string().optional()
|
|
478
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
479
|
+
const baseExpression = expression.getExpression();
|
|
480
|
+
const method = expression.getName();
|
|
481
|
+
if (method === 'optional' && Node.isCallExpression(baseExpression)) {
|
|
482
|
+
// This is an optional field, parse the base type
|
|
483
|
+
return parseZodASTToJsonSchema(baseExpression);
|
|
833
484
|
}
|
|
834
485
|
}
|
|
835
486
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
extractToolInfo(toolSourceFile);
|
|
839
|
-
if (!toolName || !toolDescription) {
|
|
840
|
-
console.warn(`Warning: Could not extract name or description from ${className}`);
|
|
841
|
-
return null;
|
|
487
|
+
// Fallback
|
|
488
|
+
return { type: 'object' };
|
|
842
489
|
}
|
|
843
|
-
|
|
844
|
-
console.warn(
|
|
845
|
-
return
|
|
846
|
-
}
|
|
847
|
-
if (!executeMethod) {
|
|
848
|
-
console.warn(`Warning: Could not find execute method in ${className}`);
|
|
849
|
-
return null;
|
|
850
|
-
}
|
|
851
|
-
// Extract execute method body
|
|
852
|
-
if (!executeMethod.body) {
|
|
853
|
-
console.warn(`Warning: Execute method has no body in ${className}`);
|
|
854
|
-
return null;
|
|
490
|
+
catch (error) {
|
|
491
|
+
console.warn('Warning: Could not parse Zod AST node:', error);
|
|
492
|
+
return { type: 'object' };
|
|
855
493
|
}
|
|
856
|
-
const executeBody = extractFunctionBody(executeMethod.body, toolSourceFile);
|
|
857
|
-
// For class-based tools, we need to create a self-contained function that includes:
|
|
858
|
-
// 1. The service classes
|
|
859
|
-
// 2. Class instantiation
|
|
860
|
-
// 3. The execute logic
|
|
861
|
-
const selfContainedExecute = await createClassBasedExecute(executeBody, toolContent, className, toolFilePath);
|
|
862
|
-
return {
|
|
863
|
-
name: toolName,
|
|
864
|
-
description: toolDescription,
|
|
865
|
-
inputSchema: inputSchema || { type: "object" },
|
|
866
|
-
execute: selfContainedExecute
|
|
867
|
-
};
|
|
868
494
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const namedImports = importMatch[1];
|
|
877
|
-
const defaultImport = importMatch[2];
|
|
878
|
-
const packagePath = importMatch[3];
|
|
879
|
-
// Skip lua-cli imports
|
|
880
|
-
if (packagePath.startsWith('lua-cli')) {
|
|
881
|
-
continue;
|
|
495
|
+
function isOptionalZodType(node) {
|
|
496
|
+
// Check if this is a method call ending with .optional()
|
|
497
|
+
if (Node.isCallExpression(node)) {
|
|
498
|
+
const expression = node.getExpression();
|
|
499
|
+
if (Node.isPropertyAccessExpression(expression)) {
|
|
500
|
+
const method = expression.getName();
|
|
501
|
+
return method === 'optional';
|
|
882
502
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
function parseZodTextToJsonSchema(zodSchemaText) {
|
|
507
|
+
try {
|
|
508
|
+
// Fallback text-based parsing for when AST parsing fails
|
|
509
|
+
if (zodSchemaText.includes('z.object({')) {
|
|
510
|
+
const properties = {};
|
|
511
|
+
const required = [];
|
|
512
|
+
// Extract object properties - enhanced pattern matching
|
|
513
|
+
const propertyPattern = /(\w+):\s*z\.(\w+)\(\)/g;
|
|
514
|
+
let match;
|
|
515
|
+
while ((match = propertyPattern.exec(zodSchemaText)) !== null) {
|
|
516
|
+
const [, propName, zodType] = match;
|
|
517
|
+
switch (zodType) {
|
|
518
|
+
case 'string':
|
|
519
|
+
properties[propName] = { type: 'string' };
|
|
520
|
+
break;
|
|
521
|
+
case 'number':
|
|
522
|
+
properties[propName] = { type: 'number' };
|
|
523
|
+
break;
|
|
524
|
+
case 'boolean':
|
|
525
|
+
properties[propName] = { type: 'boolean' };
|
|
526
|
+
break;
|
|
527
|
+
case 'any':
|
|
528
|
+
properties[propName] = { type: 'object' };
|
|
529
|
+
break;
|
|
530
|
+
default:
|
|
531
|
+
properties[propName] = { type: 'string' };
|
|
532
|
+
}
|
|
533
|
+
// Check if it's optional
|
|
534
|
+
if (!zodSchemaText.includes(`${propName}: z.${zodType}().optional()`)) {
|
|
535
|
+
required.push(propName);
|
|
893
536
|
}
|
|
894
537
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
// Resolve the service file path relative to the tool file location
|
|
911
|
-
const toolDir = path.dirname(toolFilePath);
|
|
912
|
-
const serviceFilePath = path.resolve(toolDir, packagePath + '.ts');
|
|
913
|
-
if (fs.existsSync(serviceFilePath)) {
|
|
914
|
-
const serviceContent = fs.readFileSync(serviceFilePath, 'utf8');
|
|
915
|
-
// Process all imports in the service file
|
|
916
|
-
const serviceImportRegex = /import\s+(?:(?:\{([^}]+)\})|(\w+))\s+from\s+["']([^"']+)["']/g;
|
|
917
|
-
let serviceImportMatch;
|
|
918
|
-
while ((serviceImportMatch = serviceImportRegex.exec(serviceContent)) !== null) {
|
|
919
|
-
const namedImports = serviceImportMatch[1];
|
|
920
|
-
const defaultImport = serviceImportMatch[2];
|
|
921
|
-
const packagePath = serviceImportMatch[3];
|
|
922
|
-
// Skip lua-cli imports
|
|
923
|
-
if (packagePath.startsWith('lua-cli')) {
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
|
-
// Handle zod
|
|
927
|
-
if (packagePath === 'zod') {
|
|
928
|
-
if (namedImports) {
|
|
929
|
-
const importsList = namedImports.split(',').map(imp => imp.trim());
|
|
930
|
-
const usedImports = importsList.filter(imp => serviceContent.includes(`${imp}.`));
|
|
931
|
-
if (usedImports.length > 0) {
|
|
932
|
-
const requireStatement = usedImports.length === 1
|
|
933
|
-
? `const { ${usedImports[0]} } = require('zod');`
|
|
934
|
-
: `const { ${usedImports.join(', ')} } = require('zod');`;
|
|
935
|
-
bundledPackages.add(requireStatement);
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
else if (defaultImport) {
|
|
939
|
-
bundledPackages.add(`const ${defaultImport} = require('zod');`);
|
|
940
|
-
}
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
// Handle axios - bundle it properly
|
|
944
|
-
if (packagePath === 'axios') {
|
|
945
|
-
const packageCode = await bundlePackageCode(packagePath, namedImports, defaultImport);
|
|
946
|
-
if (packageCode) {
|
|
947
|
-
bundledPackages.add(packageCode);
|
|
948
|
-
}
|
|
949
|
-
continue;
|
|
950
|
-
}
|
|
951
|
-
// Bundle other external packages
|
|
952
|
-
if (namedImports || defaultImport) {
|
|
953
|
-
const packageCode = await bundlePackageCode(packagePath, namedImports, defaultImport);
|
|
954
|
-
if (packageCode) {
|
|
955
|
-
bundledPackages.add(packageCode);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
538
|
+
// Handle arrays
|
|
539
|
+
const arrayPattern = /(\w+):\s*z\.array\(z\.(\w+)\(\)\)/g;
|
|
540
|
+
while ((match = arrayPattern.exec(zodSchemaText)) !== null) {
|
|
541
|
+
const [, propName, itemType] = match;
|
|
542
|
+
let itemSchema = { type: 'string' };
|
|
543
|
+
switch (itemType) {
|
|
544
|
+
case 'string':
|
|
545
|
+
itemSchema = { type: 'string' };
|
|
546
|
+
break;
|
|
547
|
+
case 'number':
|
|
548
|
+
itemSchema = { type: 'number' };
|
|
549
|
+
break;
|
|
550
|
+
case 'boolean':
|
|
551
|
+
itemSchema = { type: 'boolean' };
|
|
552
|
+
break;
|
|
958
553
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
if (classMatch) {
|
|
963
|
-
const serviceClassName = classMatch[1];
|
|
964
|
-
const startIndex = classMatch.index + classMatch[0].length - 1; // Position of opening brace
|
|
965
|
-
// Find matching closing brace
|
|
966
|
-
let braceCount = 1;
|
|
967
|
-
let endIndex = startIndex + 1;
|
|
968
|
-
while (endIndex < serviceContent.length && braceCount > 0) {
|
|
969
|
-
if (serviceContent[endIndex] === '{')
|
|
970
|
-
braceCount++;
|
|
971
|
-
else if (serviceContent[endIndex] === '}')
|
|
972
|
-
braceCount--;
|
|
973
|
-
endIndex++;
|
|
974
|
-
}
|
|
975
|
-
if (braceCount === 0) {
|
|
976
|
-
const serviceClassBody = serviceContent.substring(startIndex + 1, endIndex - 1);
|
|
977
|
-
// Clean up the class body (remove TypeScript types)
|
|
978
|
-
const cleanClassBody = serviceClassBody
|
|
979
|
-
.replace(/:\s*string/g, '')
|
|
980
|
-
.replace(/:\s*number/g, '')
|
|
981
|
-
.replace(/:\s*boolean/g, '')
|
|
982
|
-
.replace(/:\s*any/g, '')
|
|
983
|
-
.replace(/:\s*void/g, '')
|
|
984
|
-
.replace(/:\s*Promise<[^>]+>/g, '')
|
|
985
|
-
.replace(/:\s*Record<[^>]+>/g, '');
|
|
986
|
-
// Create the service class
|
|
987
|
-
const serviceClass = `class ${serviceClassName} {\n${cleanClassBody}\n}`;
|
|
988
|
-
dependencies.push(serviceClass);
|
|
989
|
-
}
|
|
554
|
+
properties[propName] = { type: 'array', items: itemSchema };
|
|
555
|
+
if (!zodSchemaText.includes(`${propName}: z.array(z.${itemType}()).optional()`)) {
|
|
556
|
+
required.push(propName);
|
|
990
557
|
}
|
|
991
558
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
if (packageCode) {
|
|
998
|
-
bundledPackages.add(packageCode);
|
|
999
|
-
}
|
|
559
|
+
return {
|
|
560
|
+
type: 'object',
|
|
561
|
+
properties,
|
|
562
|
+
required
|
|
563
|
+
};
|
|
1000
564
|
}
|
|
565
|
+
// Handle simple types
|
|
566
|
+
if (zodSchemaText.includes('z.string()'))
|
|
567
|
+
return { type: 'string' };
|
|
568
|
+
if (zodSchemaText.includes('z.number()'))
|
|
569
|
+
return { type: 'number' };
|
|
570
|
+
if (zodSchemaText.includes('z.boolean()'))
|
|
571
|
+
return { type: 'boolean' };
|
|
572
|
+
if (zodSchemaText.includes('z.array('))
|
|
573
|
+
return { type: 'array' };
|
|
574
|
+
// Fallback
|
|
575
|
+
return { type: 'object' };
|
|
1001
576
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
const constructorBody = constructorMatch[1];
|
|
1006
|
-
// Extract service instantiation
|
|
1007
|
-
const serviceInstantiationMatch = constructorBody.match(/this\.(\w+)\s*=\s*new\s+(\w+)\([^)]*\);/);
|
|
1008
|
-
if (serviceInstantiationMatch) {
|
|
1009
|
-
const serviceProperty = serviceInstantiationMatch[1];
|
|
1010
|
-
const serviceClass = serviceInstantiationMatch[2];
|
|
1011
|
-
dependencies.push(`const ${serviceProperty} = new ${serviceClass}();`);
|
|
1012
|
-
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
console.warn('Warning: Could not parse Zod schema text:', error);
|
|
579
|
+
return { type: 'object' };
|
|
1013
580
|
}
|
|
1014
|
-
// 3. Create the self-contained execute function
|
|
1015
|
-
const allDependencies = [...Array.from(bundledPackages), ...dependencies];
|
|
1016
|
-
const dependencyCode = allDependencies.join('\n');
|
|
1017
|
-
// Clean the execute body (remove TypeScript types)
|
|
1018
|
-
const cleanExecuteBody = executeBody
|
|
1019
|
-
.replace(/:\s*string/g, '')
|
|
1020
|
-
.replace(/:\s*number/g, '')
|
|
1021
|
-
.replace(/:\s*boolean/g, '')
|
|
1022
|
-
.replace(/:\s*any/g, '')
|
|
1023
|
-
.replace(/:\s*void/g, '')
|
|
1024
|
-
.replace(/:\s*Promise<[^>]+>/g, '')
|
|
1025
|
-
.replace(/:\s*Record<[^>]+>/g, '');
|
|
1026
|
-
// Replace this.serviceProperty with the instantiated service
|
|
1027
|
-
const finalExecuteBody = cleanExecuteBody.replace(/this\.(\w+)/g, '$1');
|
|
1028
|
-
return `async (input) => {
|
|
1029
|
-
${dependencyCode ? dependencyCode + '\n' : ''}${finalExecuteBody}
|
|
1030
|
-
}`;
|
|
1031
581
|
}
|
|
1032
|
-
async function
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
582
|
+
async function createDeploymentData(tools, distDir) {
|
|
583
|
+
const config = readSkillConfig();
|
|
584
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
585
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
586
|
+
const deploymentData = {
|
|
587
|
+
name: config?.skill?.name || packageJson.name || "lua-skill",
|
|
588
|
+
version: config?.skill?.version || packageJson.version || "1.0.0",
|
|
589
|
+
skillId: config?.skill?.skillId || "",
|
|
590
|
+
description: config?.skill?.description || packageJson.description || "",
|
|
591
|
+
context: config?.skill?.context || "",
|
|
592
|
+
tools: tools.map(tool => ({
|
|
593
|
+
name: tool.name,
|
|
594
|
+
className: tool.className,
|
|
595
|
+
description: tool.description || "",
|
|
596
|
+
filePath: `tools/${tool.className}.js`
|
|
597
|
+
}))
|
|
598
|
+
};
|
|
599
|
+
fs.writeFileSync(path.join(distDir, 'deployment.json'), JSON.stringify(deploymentData, null, 2));
|
|
600
|
+
}
|
|
601
|
+
async function createLegacyDeploymentData(tools, luaDir, indexFile) {
|
|
1036
602
|
const config = readSkillConfig();
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
603
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
604
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
605
|
+
// Extract skill metadata from index.ts
|
|
606
|
+
const skillMetadata = extractSkillMetadata(indexFile);
|
|
607
|
+
const deployData = {
|
|
608
|
+
version: config?.skill?.version || packageJson.version || "1.0.0",
|
|
609
|
+
name: config?.skill?.name || packageJson.name || "lua-skill",
|
|
610
|
+
skillsName: config?.skill?.name || packageJson.name || "lua-skill",
|
|
611
|
+
skillId: config?.skill?.skillId || skillMetadata.skillId || "",
|
|
612
|
+
description: config?.skill?.description || skillMetadata.description || "",
|
|
613
|
+
context: config?.skill?.context || skillMetadata.context || "",
|
|
614
|
+
tools: tools.map(tool => ({
|
|
615
|
+
name: tool.name,
|
|
616
|
+
description: tool.description || "",
|
|
617
|
+
inputSchema: tool.inputSchema || { type: "object" },
|
|
618
|
+
execute: compressCode(tool.executeCode || "")
|
|
619
|
+
}))
|
|
620
|
+
};
|
|
621
|
+
// Write legacy deploy.json to .lua directory
|
|
622
|
+
fs.writeFileSync(path.join(luaDir, "deploy.json"), JSON.stringify(deployData, null, 2));
|
|
623
|
+
// Write individual tool files to .lua directory (uncompressed)
|
|
624
|
+
for (const tool of tools) {
|
|
625
|
+
if (tool.executeCode) {
|
|
626
|
+
const toolFilePath = path.join(luaDir, `${tool.name}.js`);
|
|
627
|
+
fs.writeFileSync(toolFilePath, tool.executeCode);
|
|
628
|
+
}
|
|
1040
629
|
}
|
|
1041
|
-
|
|
630
|
+
}
|
|
631
|
+
function extractSkillMetadata(indexFile) {
|
|
632
|
+
let skillId = '';
|
|
1042
633
|
let description = '';
|
|
1043
634
|
let context = '';
|
|
1044
|
-
//
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
635
|
+
// Find LuaSkill constructor call
|
|
636
|
+
indexFile.forEachDescendant((node) => {
|
|
637
|
+
if (Node.isNewExpression(node)) {
|
|
638
|
+
const expression = node.getExpression();
|
|
639
|
+
if (expression.getText() === 'LuaSkill') {
|
|
640
|
+
const args = node.getArguments();
|
|
641
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
642
|
+
const configObj = args[0];
|
|
643
|
+
// Extract properties
|
|
644
|
+
configObj.getProperties().forEach((prop) => {
|
|
645
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
646
|
+
const name = prop.getName();
|
|
647
|
+
const value = prop.getInitializer()?.getText();
|
|
648
|
+
if (name === 'description' && value) {
|
|
649
|
+
description = value.replace(/['"]/g, '');
|
|
650
|
+
}
|
|
651
|
+
else if (name === 'context' && value) {
|
|
652
|
+
context = value.replace(/['"]/g, '');
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|
|
1058
658
|
}
|
|
1059
|
-
}
|
|
1060
|
-
return {
|
|
1061
|
-
skillId,
|
|
1062
|
-
version,
|
|
1063
|
-
description,
|
|
1064
|
-
context
|
|
1065
|
-
};
|
|
659
|
+
});
|
|
660
|
+
return { skillId, description, context };
|
|
1066
661
|
}
|