lua-cli 2.5.8 → 3.0.0-alpha.4
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/api/job.api.service.d.ts +219 -0
- package/dist/api/job.api.service.js +216 -0
- package/dist/api/lazy-instances.d.ts +24 -0
- package/dist/api/lazy-instances.js +48 -0
- package/dist/api/postprocessor.api.service.d.ts +158 -0
- package/dist/api/postprocessor.api.service.js +111 -0
- package/dist/api/preprocessor.api.service.d.ts +158 -0
- package/dist/api/preprocessor.api.service.js +111 -0
- package/dist/api/user.data.api.service.d.ts +13 -0
- package/dist/api/user.data.api.service.js +20 -0
- package/dist/api/webhook.api.service.d.ts +151 -0
- package/dist/api/webhook.api.service.js +134 -0
- package/dist/api-exports.d.ts +176 -41
- package/dist/api-exports.js +195 -21
- package/dist/cli/command-definitions.js +75 -5
- package/dist/commands/chat.js +32 -5
- package/dist/commands/compile.js +140 -7
- package/dist/commands/dev.js +23 -2
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/init.js +53 -7
- package/dist/commands/jobs.d.ts +20 -0
- package/dist/commands/jobs.js +533 -0
- package/dist/commands/logs.js +2 -5
- package/dist/commands/postprocessors.d.ts +8 -0
- package/dist/commands/postprocessors.js +431 -0
- package/dist/commands/preprocessors.d.ts +8 -0
- package/dist/commands/preprocessors.js +431 -0
- package/dist/commands/push.js +686 -5
- package/dist/commands/test.d.ts +9 -18
- package/dist/commands/test.js +574 -82
- package/dist/commands/webhooks.d.ts +18 -0
- package/dist/commands/webhooks.js +424 -0
- package/dist/common/job.instance.d.ts +80 -0
- package/dist/common/job.instance.js +116 -0
- package/dist/common/user.instance.d.ts +1 -0
- package/dist/common/user.instance.js +9 -0
- package/dist/config/constants.d.ts +4 -3
- package/dist/config/constants.js +10 -8
- package/dist/interfaces/agent.d.ts +2 -1
- package/dist/interfaces/chat.d.ts +52 -1
- package/dist/interfaces/index.d.ts +10 -0
- package/dist/interfaces/index.js +7 -0
- package/dist/interfaces/jobs.d.ts +193 -0
- package/dist/interfaces/jobs.js +5 -0
- package/dist/interfaces/postprocessors.d.ts +35 -0
- package/dist/interfaces/postprocessors.js +4 -0
- package/dist/interfaces/preprocessors.d.ts +35 -0
- package/dist/interfaces/preprocessors.js +4 -0
- package/dist/interfaces/webhooks.d.ts +104 -0
- package/dist/interfaces/webhooks.js +5 -0
- package/dist/types/api-contracts.d.ts +5 -0
- package/dist/types/compile.types.d.ts +49 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/skill.d.ts +521 -0
- package/dist/types/skill.js +471 -0
- package/dist/utils/agent-management.d.ts +25 -0
- package/dist/utils/agent-management.js +67 -0
- package/dist/utils/bundling.d.ts +44 -5
- package/dist/utils/bundling.js +723 -23
- package/dist/utils/compile.d.ts +63 -0
- package/dist/utils/compile.js +712 -36
- package/dist/utils/deployment.d.ts +2 -1
- package/dist/utils/deployment.js +16 -2
- package/dist/utils/dev-api.d.ts +42 -2
- package/dist/utils/dev-api.js +177 -4
- package/dist/utils/dev-server.d.ts +1 -1
- package/dist/utils/dev-server.js +4 -4
- package/dist/utils/dynamic-job-bundler.d.ts +17 -0
- package/dist/utils/dynamic-job-bundler.js +143 -0
- package/dist/utils/init-agent.d.ts +3 -1
- package/dist/utils/init-agent.js +6 -4
- package/dist/utils/init-prompts.d.ts +2 -1
- package/dist/utils/init-prompts.js +14 -9
- package/dist/utils/job-management.d.ts +24 -0
- package/dist/utils/job-management.js +264 -0
- package/dist/utils/postprocessor-management.d.ts +9 -0
- package/dist/utils/postprocessor-management.js +118 -0
- package/dist/utils/pre-bundle-jobs.d.ts +26 -0
- package/dist/utils/pre-bundle-jobs.js +176 -0
- package/dist/utils/preprocessor-management.d.ts +9 -0
- package/dist/utils/preprocessor-management.js +118 -0
- package/dist/utils/sandbox-storage.d.ts +48 -0
- package/dist/utils/sandbox-storage.js +114 -0
- package/dist/utils/sandbox.d.ts +61 -1
- package/dist/utils/sandbox.js +299 -72
- package/dist/utils/tool-detection.d.ts +3 -2
- package/dist/utils/tool-detection.js +18 -4
- package/dist/utils/webhook-management.d.ts +24 -0
- package/dist/utils/webhook-management.js +256 -0
- package/package.json +1 -1
- package/template/README.md +30 -2
- package/template/lua.skill.yaml +47 -0
- package/template/package-lock.json +10505 -0
- package/template/package.json +2 -1
- package/template/src/index.ts +103 -2
- package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
- package/template/src/jobs/DailyCleanupJob.ts +100 -0
- package/template/src/jobs/DataMigrationJob.ts +133 -0
- package/template/src/jobs/HealthCheckJob.ts +87 -0
- package/template/src/tools/CreateInlineJob.ts +42 -0
- package/template/src/tools/GameScoreTrackerTool.ts +356 -0
- package/template/src/tools/SmartBasketTool.ts +188 -0
- package/template/src/webhooks/PaymentWebhook.ts +113 -0
- package/template/src/webhooks/UserEventWebhook.ts +77 -0
- package/API_REFERENCE.md +0 -1408
- package/CHANGELOG.md +0 -236
- package/CLI_REFERENCE.md +0 -908
- package/GETTING_STARTED.md +0 -1040
- package/INSTANCE_TYPES.md +0 -1158
- package/README.md +0 -865
- package/TEMPLATE_GUIDE.md +0 -1398
- package/USER_DATA_INSTANCE.md +0 -621
- package/template/TOOL_EXAMPLES.md +0 -655
package/dist/utils/bundling.js
CHANGED
|
@@ -5,15 +5,37 @@
|
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { build } from "esbuild";
|
|
8
|
+
import { Project, Node } from "ts-morph";
|
|
8
9
|
import { writeProgress } from "./cli.js";
|
|
9
10
|
import { COMPILE_DIRS, COMPILE_FILES, ESBUILD_TOOL_CONFIG, ESBUILD_INDEX_CONFIG, DEFAULT_INPUT_SCHEMA, } from '../config/compile.constants.js';
|
|
10
|
-
import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema
|
|
11
|
+
import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema } from './compile.js';
|
|
12
|
+
/**
|
|
13
|
+
* Helper function to remove lua-cli and api-exports imports from source code.
|
|
14
|
+
* Used when bundling via stdin or temp files where the plugin can't process them.
|
|
15
|
+
*/
|
|
16
|
+
function stripLuaCliImports(sourceCode) {
|
|
17
|
+
return sourceCode
|
|
18
|
+
// Remove api-exports imports
|
|
19
|
+
.replace(/import\s+{([^}]+)}\s+from\s+["'][^"']*api-exports["'];?/g, '// api-exports imports removed - using sandbox globals')
|
|
20
|
+
// Remove lua-cli imports
|
|
21
|
+
.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli["'];?/g, '// lua-cli imports removed - using sandbox globals')
|
|
22
|
+
// Remove lua-cli/skill imports (but keep LuaTool, etc. for type checking in temp files)
|
|
23
|
+
.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli\/skill["'];?/g, (match, imports) => {
|
|
24
|
+
// Keep only LuaTool, LuaWebhook, LuaJob types for temp files
|
|
25
|
+
const typeImports = imports.split(',')
|
|
26
|
+
.map((imp) => imp.trim())
|
|
27
|
+
.filter((imp) => /^(LuaTool|LuaWebhook|LuaJob|LuaPreProcessor|LuaPostProcessor)$/.test(imp));
|
|
28
|
+
return typeImports.length > 0
|
|
29
|
+
? `// lua-cli/skill imports removed - using sandbox globals (types kept for compilation)`
|
|
30
|
+
: '// lua-cli/skill imports removed - using sandbox globals';
|
|
31
|
+
});
|
|
32
|
+
}
|
|
11
33
|
/**
|
|
12
34
|
* esbuild plugin to inject sandbox globals instead of requiring lua-cli
|
|
13
35
|
* This removes require("lua-cli") statements and injects the global API objects
|
|
14
|
-
* that are available in the sandbox (
|
|
36
|
+
* that are available in the sandbox (Products, User, Data, Baskets, Orders, Webhooks, Jobs)
|
|
15
37
|
*/
|
|
16
|
-
const sandboxGlobalsPlugin = {
|
|
38
|
+
export const sandboxGlobalsPlugin = {
|
|
17
39
|
name: 'sandbox-globals',
|
|
18
40
|
setup(build) {
|
|
19
41
|
// Only process user files, not node_modules
|
|
@@ -23,12 +45,18 @@ const sandboxGlobalsPlugin = {
|
|
|
23
45
|
return null;
|
|
24
46
|
}
|
|
25
47
|
const contents = await fs.promises.readFile(args.path, 'utf8');
|
|
26
|
-
// Only transform files that import from lua-cli
|
|
27
|
-
if (!contents.includes('lua-cli')) {
|
|
48
|
+
// Only transform files that import from lua-cli or api-exports
|
|
49
|
+
if (!contents.includes('lua-cli') && !contents.includes('api-exports')) {
|
|
28
50
|
return null;
|
|
29
51
|
}
|
|
30
52
|
// Replace lua-cli imports with global references
|
|
31
53
|
let transformedContents = contents;
|
|
54
|
+
// Replace named imports from api-exports (e.g., ../../../dist/api-exports)
|
|
55
|
+
// Match: import { Jobs, Products, etc. } from "../../../dist/api-exports"
|
|
56
|
+
transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["'][^"']*api-exports["'];?/g, (match, imports) => {
|
|
57
|
+
// Just remove the import, globals will be available in sandbox
|
|
58
|
+
return '// api-exports imports removed - using sandbox globals';
|
|
59
|
+
});
|
|
32
60
|
// Replace named imports from lua-cli
|
|
33
61
|
// Match: import { Products, User, Data, etc. } from "lua-cli"
|
|
34
62
|
transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli["'];?/g, (match, imports) => {
|
|
@@ -50,7 +78,9 @@ const sandboxGlobalsPlugin = {
|
|
|
50
78
|
'User': 'User',
|
|
51
79
|
'Data': 'Data',
|
|
52
80
|
'Baskets': 'Baskets',
|
|
53
|
-
'Orders': 'Orders'
|
|
81
|
+
'Orders': 'Orders',
|
|
82
|
+
'Webhooks': 'Webhooks',
|
|
83
|
+
'Jobs': 'Jobs'
|
|
54
84
|
};
|
|
55
85
|
// Replace usage of imported names with globals
|
|
56
86
|
for (const [importName, globalName] of Object.entries(globalMappings)) {
|
|
@@ -75,21 +105,85 @@ const sandboxGlobalsPlugin = {
|
|
|
75
105
|
* - Minify code for production
|
|
76
106
|
* - Wrap for VM execution
|
|
77
107
|
*
|
|
108
|
+
* For inline tools (defined in index.ts), creates a temporary export file first.
|
|
109
|
+
*
|
|
78
110
|
* @param tool - The tool to bundle
|
|
79
111
|
* @param distDir - The distribution directory for output
|
|
80
112
|
*/
|
|
81
|
-
export async function bundleTool(tool, distDir) {
|
|
113
|
+
export async function bundleTool(tool, distDir, modifiedSource) {
|
|
82
114
|
writeProgress(`📦 Bundling ${tool.className}...`);
|
|
83
115
|
try {
|
|
84
116
|
const outputPath = path.join(distDir, COMPILE_DIRS.TOOLS, `${tool.className}.js`);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
117
|
+
let entryPoint = tool.filePath;
|
|
118
|
+
let tempFile = null;
|
|
119
|
+
// Note: modifiedSource is handled via stdin below, no temp file needed
|
|
120
|
+
// Check if tool is inline (in index.ts or src/index.ts)
|
|
121
|
+
const isInlineInIndex = tool.filePath.endsWith('index.ts') || tool.filePath.endsWith('index.tsx');
|
|
122
|
+
if (isInlineInIndex) {
|
|
123
|
+
// Create a temporary file that exports just this tool class
|
|
124
|
+
const tempDir = path.join(distDir, '.temp');
|
|
125
|
+
if (!fs.existsSync(tempDir)) {
|
|
126
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
127
|
+
}
|
|
128
|
+
tempFile = path.join(tempDir, `${tool.className}.ts`);
|
|
129
|
+
// Read the source file and extract just the tool class
|
|
130
|
+
const sourceContent = fs.readFileSync(tool.filePath, 'utf8');
|
|
131
|
+
const project = new Project();
|
|
132
|
+
const sourceFile = project.addSourceFileAtPath(tool.filePath);
|
|
133
|
+
const classDecl = sourceFile.getClass(tool.className);
|
|
134
|
+
if (classDecl) {
|
|
135
|
+
// Get all imports from the original file that this class might need
|
|
136
|
+
const imports = sourceFile.getImportDeclarations();
|
|
137
|
+
let importsText = '';
|
|
138
|
+
// Include imports from lua-cli and zod (tools commonly need these)
|
|
139
|
+
imports.forEach(importDecl => {
|
|
140
|
+
const moduleSpec = importDecl.getModuleSpecifierValue();
|
|
141
|
+
if (moduleSpec === 'lua-cli' || moduleSpec === 'zod' || moduleSpec.startsWith('./')) {
|
|
142
|
+
importsText += importDecl.getText() + '\n';
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
// Create temporary file with imports and the tool class
|
|
146
|
+
const toolClassText = classDecl.getText();
|
|
147
|
+
const tempContent = `${importsText}\n${toolClassText}\n\nexport default ${tool.className};\n`;
|
|
148
|
+
fs.writeFileSync(tempFile, tempContent);
|
|
149
|
+
entryPoint = tempFile;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Use stdin for modified source to preserve import resolution
|
|
153
|
+
if (modifiedSource && !isInlineInIndex) {
|
|
154
|
+
// Remove api-exports and lua-cli imports (they're available in sandbox)
|
|
155
|
+
const transformedSource = stripLuaCliImports(modifiedSource);
|
|
156
|
+
await build({
|
|
157
|
+
...ESBUILD_TOOL_CONFIG,
|
|
158
|
+
stdin: {
|
|
159
|
+
contents: transformedSource,
|
|
160
|
+
resolveDir: path.dirname(tool.filePath),
|
|
161
|
+
sourcefile: tool.filePath,
|
|
162
|
+
loader: 'ts',
|
|
163
|
+
},
|
|
164
|
+
outfile: outputPath,
|
|
165
|
+
plugins: [sandboxGlobalsPlugin],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
await build({
|
|
170
|
+
...ESBUILD_TOOL_CONFIG,
|
|
171
|
+
entryPoints: [entryPoint],
|
|
172
|
+
outfile: outputPath,
|
|
173
|
+
plugins: [sandboxGlobalsPlugin],
|
|
174
|
+
});
|
|
175
|
+
}
|
|
91
176
|
// Wrap the bundled code for VM execution environment
|
|
92
177
|
await wrapToolForVM(outputPath, tool);
|
|
178
|
+
// Clean up temp file if created
|
|
179
|
+
if (tempFile && fs.existsSync(tempFile)) {
|
|
180
|
+
try {
|
|
181
|
+
fs.unlinkSync(tempFile);
|
|
182
|
+
}
|
|
183
|
+
catch (cleanupError) {
|
|
184
|
+
// Ignore cleanup errors
|
|
185
|
+
}
|
|
186
|
+
}
|
|
93
187
|
}
|
|
94
188
|
catch (error) {
|
|
95
189
|
console.warn(`Warning: Failed to bundle ${tool.className}:`, error);
|
|
@@ -115,17 +209,610 @@ export async function bundleMainIndex(indexPath, distDir) {
|
|
|
115
209
|
console.warn("Warning: Failed to bundle main index:", error);
|
|
116
210
|
}
|
|
117
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Extracts and bundles webhook execute function and schemas.
|
|
214
|
+
*
|
|
215
|
+
* @param webhook - Webhook metadata with name and version
|
|
216
|
+
* @param indexFile - The TypeScript source file containing the webhook
|
|
217
|
+
* @param distDir - Distribution directory for output
|
|
218
|
+
* @param project - Optional ts-morph Project for resolving imports
|
|
219
|
+
* @returns Webhook with bundled code and schemas
|
|
220
|
+
*/
|
|
221
|
+
export async function bundleWebhook(webhook, indexFile, distDir, project) {
|
|
222
|
+
writeProgress(`📦 Bundling webhook ${webhook.name}...`);
|
|
223
|
+
try {
|
|
224
|
+
// Find the LuaWebhook constructor in the AST
|
|
225
|
+
let executeFunction = '';
|
|
226
|
+
let querySchema = undefined;
|
|
227
|
+
let headerSchema = undefined;
|
|
228
|
+
let bodySchema = undefined;
|
|
229
|
+
// Helper function to search for webhook in a file
|
|
230
|
+
const searchFileForWebhook = (file) => {
|
|
231
|
+
file.forEachDescendant((node) => {
|
|
232
|
+
if (Node.isNewExpression(node)) {
|
|
233
|
+
const expression = node.getExpression();
|
|
234
|
+
if (expression.getText() === 'LuaWebhook') {
|
|
235
|
+
const args = node.getArguments();
|
|
236
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
237
|
+
const configObj = args[0];
|
|
238
|
+
// First pass: check if this is the webhook we're looking for
|
|
239
|
+
let isMatchingWebhook = false;
|
|
240
|
+
let foundExecute = '';
|
|
241
|
+
let foundQuerySchema = undefined;
|
|
242
|
+
let foundHeaderSchema = undefined;
|
|
243
|
+
let foundBodySchema = undefined;
|
|
244
|
+
configObj.getProperties().forEach((prop) => {
|
|
245
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
246
|
+
const name = prop.getName();
|
|
247
|
+
const value = prop.getInitializer();
|
|
248
|
+
if (name === 'name' && value) {
|
|
249
|
+
const nameValue = value.getText().replace(/['"]/g, '');
|
|
250
|
+
if (nameValue === webhook.name) {
|
|
251
|
+
isMatchingWebhook = true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (name === 'execute' && value) {
|
|
255
|
+
foundExecute = value.getText();
|
|
256
|
+
}
|
|
257
|
+
else if (name === 'querySchema' && value) {
|
|
258
|
+
foundQuerySchema = value.getText();
|
|
259
|
+
}
|
|
260
|
+
else if (name === 'headerSchema' && value) {
|
|
261
|
+
foundHeaderSchema = value.getText();
|
|
262
|
+
}
|
|
263
|
+
else if (name === 'bodySchema' && value) {
|
|
264
|
+
foundBodySchema = value.getText();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// Only set values if this is the matching webhook
|
|
269
|
+
if (isMatchingWebhook) {
|
|
270
|
+
executeFunction = foundExecute;
|
|
271
|
+
querySchema = foundQuerySchema;
|
|
272
|
+
headerSchema = foundHeaderSchema;
|
|
273
|
+
bodySchema = foundBodySchema;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
// Search in index file first
|
|
281
|
+
searchFileForWebhook(indexFile);
|
|
282
|
+
// If not found and project is available, search in imported files
|
|
283
|
+
if (!executeFunction && project) {
|
|
284
|
+
const imports = indexFile.getImportDeclarations();
|
|
285
|
+
for (const importDecl of imports) {
|
|
286
|
+
try {
|
|
287
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
288
|
+
const { resolveImportPath } = await import('./compile.js');
|
|
289
|
+
const importPath = resolveImportPath(moduleSpecifier, indexFile.getFilePath());
|
|
290
|
+
if (fs.existsSync(importPath)) {
|
|
291
|
+
const importedFile = project.addSourceFileAtPath(importPath);
|
|
292
|
+
searchFileForWebhook(importedFile);
|
|
293
|
+
// If found, stop searching
|
|
294
|
+
if (executeFunction) {
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
// Continue searching other imports
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Convert Zod schemas to JSON Schema
|
|
305
|
+
const convertedSchemas = {};
|
|
306
|
+
if (querySchema) {
|
|
307
|
+
convertedSchemas.querySchema = await evaluateZodSchemaToJsonSchema(querySchema);
|
|
308
|
+
}
|
|
309
|
+
if (headerSchema) {
|
|
310
|
+
convertedSchemas.headerSchema = await evaluateZodSchemaToJsonSchema(headerSchema);
|
|
311
|
+
}
|
|
312
|
+
if (bodySchema) {
|
|
313
|
+
convertedSchemas.bodySchema = await evaluateZodSchemaToJsonSchema(bodySchema);
|
|
314
|
+
}
|
|
315
|
+
// Bundle and compress the execute function (like tools)
|
|
316
|
+
let compressedCode = '';
|
|
317
|
+
if (executeFunction) {
|
|
318
|
+
compressedCode = await bundleAndCompressWebhookCode(executeFunction, webhook.name, distDir);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
...webhook,
|
|
322
|
+
executeFunction,
|
|
323
|
+
code: compressedCode, // Add compressed bundled code
|
|
324
|
+
...convertedSchemas
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
console.warn(`Warning: Could not bundle webhook ${webhook.name}:`, error);
|
|
329
|
+
return webhook;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Bundles and compresses webhook execute function code.
|
|
334
|
+
* Creates a temporary file, bundles with esbuild, compresses with gzip.
|
|
335
|
+
*
|
|
336
|
+
* @param executeFunction - Raw execute function code
|
|
337
|
+
* @param webhookName - Name of the webhook
|
|
338
|
+
* @param distDir - Distribution directory
|
|
339
|
+
* @returns Compressed base64-encoded bundled code
|
|
340
|
+
*/
|
|
341
|
+
async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDir) {
|
|
342
|
+
const { compressCode } = await import('./compile.js');
|
|
343
|
+
// Create temporary file with the execute function wrapped as a module
|
|
344
|
+
const tempDir = path.join(distDir, '.temp');
|
|
345
|
+
if (!fs.existsSync(tempDir)) {
|
|
346
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
347
|
+
}
|
|
348
|
+
const tempFile = path.join(tempDir, `${webhookName}-webhook.ts`);
|
|
349
|
+
const tempOutput = path.join(tempDir, `${webhookName}-webhook.js`);
|
|
350
|
+
try {
|
|
351
|
+
// Write execute function as a module export
|
|
352
|
+
const moduleCode = stripLuaCliImports(`
|
|
353
|
+
// Webhook execute function for ${webhookName}
|
|
354
|
+
export default ${executeFunction};
|
|
355
|
+
`);
|
|
356
|
+
fs.writeFileSync(tempFile, moduleCode);
|
|
357
|
+
// Bundle with esbuild
|
|
358
|
+
await build({
|
|
359
|
+
entryPoints: [tempFile],
|
|
360
|
+
bundle: true,
|
|
361
|
+
platform: 'node',
|
|
362
|
+
target: 'node16',
|
|
363
|
+
format: 'cjs',
|
|
364
|
+
minify: true,
|
|
365
|
+
outfile: tempOutput,
|
|
366
|
+
external: [], // Bundle everything
|
|
367
|
+
plugins: [sandboxGlobalsPlugin],
|
|
368
|
+
});
|
|
369
|
+
// Read bundled code
|
|
370
|
+
let bundledCode = fs.readFileSync(tempOutput, 'utf8');
|
|
371
|
+
// Wrap for webhook VM execution (similar to tools)
|
|
372
|
+
const wrappedCode = `(async (query, headers, body) => {
|
|
373
|
+
|
|
374
|
+
// Execute the bundled webhook code
|
|
375
|
+
${bundledCode}
|
|
376
|
+
|
|
377
|
+
// Get the execute function from exports
|
|
378
|
+
const executeFunction = module.exports.default || module.exports;
|
|
379
|
+
|
|
380
|
+
// Execute with three separate parameters (not an object)
|
|
381
|
+
return await executeFunction(query, headers, body);
|
|
382
|
+
})`;
|
|
383
|
+
// Compress the wrapped code
|
|
384
|
+
const compressed = compressCode(wrappedCode);
|
|
385
|
+
// Clean up temp files
|
|
386
|
+
try {
|
|
387
|
+
fs.unlinkSync(tempFile);
|
|
388
|
+
fs.unlinkSync(tempOutput);
|
|
389
|
+
}
|
|
390
|
+
catch (cleanupError) {
|
|
391
|
+
// Ignore cleanup errors
|
|
392
|
+
}
|
|
393
|
+
return compressed;
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
console.warn(`Warning: Could not bundle webhook ${webhookName} code:`, error);
|
|
397
|
+
// Clean up on error
|
|
398
|
+
try {
|
|
399
|
+
if (fs.existsSync(tempFile))
|
|
400
|
+
fs.unlinkSync(tempFile);
|
|
401
|
+
if (fs.existsSync(tempOutput))
|
|
402
|
+
fs.unlinkSync(tempOutput);
|
|
403
|
+
}
|
|
404
|
+
catch (cleanupError) {
|
|
405
|
+
// Ignore cleanup errors
|
|
406
|
+
}
|
|
407
|
+
return '';
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Extracts and bundles job execute function.
|
|
412
|
+
*
|
|
413
|
+
* @param job - Job metadata with name and version
|
|
414
|
+
* @param indexFile - The TypeScript source file containing the job
|
|
415
|
+
* @param distDir - Distribution directory for output
|
|
416
|
+
* @param project - Optional ts-morph Project for resolving imports
|
|
417
|
+
* @returns Job with bundled code
|
|
418
|
+
*/
|
|
419
|
+
export async function bundleJob(job, indexFile, distDir, project) {
|
|
420
|
+
writeProgress(`📦 Bundling job ${job.name}...`);
|
|
421
|
+
try {
|
|
422
|
+
// Find the LuaJob constructor in the AST
|
|
423
|
+
let executeFunction = '';
|
|
424
|
+
// Helper function to search for job in a file
|
|
425
|
+
const searchFileForJob = (file) => {
|
|
426
|
+
file.forEachDescendant((node) => {
|
|
427
|
+
if (Node.isNewExpression(node)) {
|
|
428
|
+
const expression = node.getExpression();
|
|
429
|
+
if (expression.getText() === 'LuaJob') {
|
|
430
|
+
const args = node.getArguments();
|
|
431
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
432
|
+
const configObj = args[0];
|
|
433
|
+
// First pass: check if this is the job we're looking for
|
|
434
|
+
let isMatchingJob = false;
|
|
435
|
+
let foundExecute = '';
|
|
436
|
+
configObj.getProperties().forEach((prop) => {
|
|
437
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
438
|
+
const name = prop.getName();
|
|
439
|
+
const value = prop.getInitializer();
|
|
440
|
+
if (name === 'name' && value) {
|
|
441
|
+
const nameValue = value.getText().replace(/['"]/g, '');
|
|
442
|
+
if (nameValue === job.name) {
|
|
443
|
+
isMatchingJob = true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (name === 'execute' && value) {
|
|
447
|
+
foundExecute = value.getText();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
// Only set executeFunction if this is the matching job
|
|
452
|
+
if (isMatchingJob && foundExecute) {
|
|
453
|
+
executeFunction = foundExecute;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
// Search in index file first
|
|
461
|
+
searchFileForJob(indexFile);
|
|
462
|
+
// If not found and project is available, search in imported files
|
|
463
|
+
if (!executeFunction && project) {
|
|
464
|
+
const imports = indexFile.getImportDeclarations();
|
|
465
|
+
for (const importDecl of imports) {
|
|
466
|
+
try {
|
|
467
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
468
|
+
const { resolveImportPath } = await import('./compile.js');
|
|
469
|
+
const importPath = resolveImportPath(moduleSpecifier, indexFile.getFilePath());
|
|
470
|
+
if (fs.existsSync(importPath)) {
|
|
471
|
+
const importedFile = project.addSourceFileAtPath(importPath);
|
|
472
|
+
searchFileForJob(importedFile);
|
|
473
|
+
// If found, stop searching
|
|
474
|
+
if (executeFunction) {
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
// Continue searching other imports
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Bundle and compress the execute function (like tools)
|
|
485
|
+
let compressedCode = '';
|
|
486
|
+
if (executeFunction) {
|
|
487
|
+
compressedCode = await bundleAndCompressJobCode(executeFunction, job.name, distDir);
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
...job,
|
|
491
|
+
executeFunction,
|
|
492
|
+
code: compressedCode // Add compressed bundled code
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
console.warn(`Warning: Could not bundle job ${job.name}:`, error);
|
|
497
|
+
return job;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Bundles and compresses job execute function code.
|
|
502
|
+
* Creates a temporary file, bundles with esbuild, compresses with gzip.
|
|
503
|
+
*
|
|
504
|
+
* @param executeFunction - Raw execute function code
|
|
505
|
+
* @param jobName - Name of the job
|
|
506
|
+
* @param distDir - Distribution directory
|
|
507
|
+
* @returns Compressed base64-encoded bundled code
|
|
508
|
+
*/
|
|
509
|
+
async function bundleAndCompressJobCode(executeFunction, jobName, distDir) {
|
|
510
|
+
const { compressCode } = await import('./compile.js');
|
|
511
|
+
// Create temporary file with the execute function wrapped as a module
|
|
512
|
+
const tempDir = path.join(distDir, '.temp');
|
|
513
|
+
if (!fs.existsSync(tempDir)) {
|
|
514
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
515
|
+
}
|
|
516
|
+
const tempFile = path.join(tempDir, `${jobName}-job.ts`);
|
|
517
|
+
const tempOutput = path.join(tempDir, `${jobName}-job.js`);
|
|
518
|
+
try {
|
|
519
|
+
// Write execute function as a module export
|
|
520
|
+
const moduleCode = stripLuaCliImports(`
|
|
521
|
+
// Job execute function for ${jobName}
|
|
522
|
+
export default ${executeFunction};
|
|
523
|
+
`);
|
|
524
|
+
fs.writeFileSync(tempFile, moduleCode);
|
|
525
|
+
// Bundle with esbuild
|
|
526
|
+
await build({
|
|
527
|
+
entryPoints: [tempFile],
|
|
528
|
+
bundle: true,
|
|
529
|
+
platform: 'node',
|
|
530
|
+
target: 'node16',
|
|
531
|
+
format: 'cjs',
|
|
532
|
+
minify: true,
|
|
533
|
+
outfile: tempOutput,
|
|
534
|
+
external: [], // Bundle everything
|
|
535
|
+
plugins: [sandboxGlobalsPlugin],
|
|
536
|
+
});
|
|
537
|
+
// Read bundled code
|
|
538
|
+
let bundledCode = fs.readFileSync(tempOutput, 'utf8');
|
|
539
|
+
// Wrap for job VM execution (similar to tools, but accepts job parameter)
|
|
540
|
+
const wrappedCode = `(async (job) => {
|
|
541
|
+
// Execute the bundled job code
|
|
542
|
+
${bundledCode}
|
|
543
|
+
|
|
544
|
+
// Get the execute function from exports
|
|
545
|
+
const executeFunction = module.exports.default || module.exports;
|
|
546
|
+
|
|
547
|
+
// Execute job with job instance parameter
|
|
548
|
+
return await executeFunction(job);
|
|
549
|
+
})`;
|
|
550
|
+
// Compress the wrapped code
|
|
551
|
+
const compressed = compressCode(wrappedCode);
|
|
552
|
+
// Clean up temp files
|
|
553
|
+
try {
|
|
554
|
+
fs.unlinkSync(tempFile);
|
|
555
|
+
fs.unlinkSync(tempOutput);
|
|
556
|
+
}
|
|
557
|
+
catch (cleanupError) {
|
|
558
|
+
// Ignore cleanup errors
|
|
559
|
+
}
|
|
560
|
+
return compressed;
|
|
561
|
+
}
|
|
562
|
+
catch (error) {
|
|
563
|
+
console.warn(`Warning: Could not bundle job ${jobName} code:`, error);
|
|
564
|
+
// Clean up on error
|
|
565
|
+
try {
|
|
566
|
+
if (fs.existsSync(tempFile))
|
|
567
|
+
fs.unlinkSync(tempFile);
|
|
568
|
+
if (fs.existsSync(tempOutput))
|
|
569
|
+
fs.unlinkSync(tempOutput);
|
|
570
|
+
}
|
|
571
|
+
catch (cleanupError) {
|
|
572
|
+
// Ignore cleanup errors
|
|
573
|
+
}
|
|
574
|
+
return '';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Bundles and compresses preprocessor execute function code.
|
|
579
|
+
*/
|
|
580
|
+
export async function bundlePreProcessor(preprocessor, indexFile, distDir, project) {
|
|
581
|
+
writeProgress(`📦 Bundling preprocessor ${preprocessor.name}...`);
|
|
582
|
+
try {
|
|
583
|
+
let executeFunction = '';
|
|
584
|
+
const searchFileForPreProcessor = (file) => {
|
|
585
|
+
file.forEachDescendant((node) => {
|
|
586
|
+
if (Node.isNewExpression(node)) {
|
|
587
|
+
const expression = node.getExpression();
|
|
588
|
+
if (expression.getText() === 'PreProcessor') {
|
|
589
|
+
const args = node.getArguments();
|
|
590
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
591
|
+
const configObj = args[0];
|
|
592
|
+
let isMatching = false;
|
|
593
|
+
let foundExecute = '';
|
|
594
|
+
configObj.getProperties().forEach((prop) => {
|
|
595
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
596
|
+
const name = prop.getName();
|
|
597
|
+
const value = prop.getInitializer();
|
|
598
|
+
if (name === 'name' && value) {
|
|
599
|
+
const nameValue = value.getText().replace(/['"]/g, '');
|
|
600
|
+
if (nameValue === preprocessor.name) {
|
|
601
|
+
isMatching = true;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (name === 'execute' && value) {
|
|
605
|
+
foundExecute = value.getText();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
if (isMatching && foundExecute) {
|
|
610
|
+
executeFunction = foundExecute;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
};
|
|
617
|
+
// Search in index file first
|
|
618
|
+
searchFileForPreProcessor(indexFile);
|
|
619
|
+
// If not found and project is available, search in imported files
|
|
620
|
+
if (!executeFunction && project) {
|
|
621
|
+
const imports = indexFile.getImportDeclarations();
|
|
622
|
+
for (const importDecl of imports) {
|
|
623
|
+
try {
|
|
624
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
625
|
+
const { resolveImportPath } = await import('./compile.js');
|
|
626
|
+
const importPath = resolveImportPath(moduleSpecifier, indexFile.getFilePath());
|
|
627
|
+
if (fs.existsSync(importPath)) {
|
|
628
|
+
const importedFile = project.addSourceFileAtPath(importPath);
|
|
629
|
+
searchFileForPreProcessor(importedFile);
|
|
630
|
+
if (executeFunction) {
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
catch (error) {
|
|
636
|
+
// Continue searching
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
let compressedCode = '';
|
|
641
|
+
if (executeFunction) {
|
|
642
|
+
compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir);
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
name: preprocessor.name,
|
|
646
|
+
version: preprocessor.version,
|
|
647
|
+
description: preprocessor.description,
|
|
648
|
+
context: preprocessor.context,
|
|
649
|
+
async: preprocessor.async === true ? true : false, // Explicitly set boolean
|
|
650
|
+
executeFunction,
|
|
651
|
+
code: compressedCode
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
console.warn(`Warning: Could not bundle preprocessor ${preprocessor.name}:`, error);
|
|
656
|
+
return preprocessor;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Bundles and compresses postprocessor execute function code.
|
|
661
|
+
*/
|
|
662
|
+
export async function bundlePostProcessor(postprocessor, indexFile, distDir, project) {
|
|
663
|
+
writeProgress(`📦 Bundling postprocessor ${postprocessor.name}...`);
|
|
664
|
+
try {
|
|
665
|
+
let executeFunction = '';
|
|
666
|
+
const searchFileForPostProcessor = (file) => {
|
|
667
|
+
file.forEachDescendant((node) => {
|
|
668
|
+
if (Node.isNewExpression(node)) {
|
|
669
|
+
const expression = node.getExpression();
|
|
670
|
+
if (expression.getText() === 'PostProcessor') {
|
|
671
|
+
const args = node.getArguments();
|
|
672
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
673
|
+
const configObj = args[0];
|
|
674
|
+
let isMatching = false;
|
|
675
|
+
let foundExecute = '';
|
|
676
|
+
configObj.getProperties().forEach((prop) => {
|
|
677
|
+
if (Node.isPropertyAssignment(prop)) {
|
|
678
|
+
const name = prop.getName();
|
|
679
|
+
const value = prop.getInitializer();
|
|
680
|
+
if (name === 'name' && value) {
|
|
681
|
+
const nameValue = value.getText().replace(/['"]/g, '');
|
|
682
|
+
if (nameValue === postprocessor.name) {
|
|
683
|
+
isMatching = true;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (name === 'execute' && value) {
|
|
687
|
+
foundExecute = value.getText();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
if (isMatching && foundExecute) {
|
|
692
|
+
executeFunction = foundExecute;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
// Search in index file first
|
|
700
|
+
searchFileForPostProcessor(indexFile);
|
|
701
|
+
// If not found and project is available, search in imported files
|
|
702
|
+
if (!executeFunction && project) {
|
|
703
|
+
const imports = indexFile.getImportDeclarations();
|
|
704
|
+
for (const importDecl of imports) {
|
|
705
|
+
try {
|
|
706
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
707
|
+
const { resolveImportPath } = await import('./compile.js');
|
|
708
|
+
const importPath = resolveImportPath(moduleSpecifier, indexFile.getFilePath());
|
|
709
|
+
if (fs.existsSync(importPath)) {
|
|
710
|
+
const importedFile = project.addSourceFileAtPath(importPath);
|
|
711
|
+
searchFileForPostProcessor(importedFile);
|
|
712
|
+
if (executeFunction) {
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
catch (error) {
|
|
718
|
+
// Continue searching
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
let compressedCode = '';
|
|
723
|
+
if (executeFunction) {
|
|
724
|
+
compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir);
|
|
725
|
+
}
|
|
726
|
+
return {
|
|
727
|
+
name: postprocessor.name,
|
|
728
|
+
version: postprocessor.version,
|
|
729
|
+
description: postprocessor.description,
|
|
730
|
+
context: postprocessor.context,
|
|
731
|
+
async: postprocessor.async === true ? true : false, // Explicitly set boolean
|
|
732
|
+
executeFunction,
|
|
733
|
+
code: compressedCode
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
console.warn(`Warning: Could not bundle postprocessor ${postprocessor.name}:`, error);
|
|
738
|
+
return postprocessor;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Bundles and compresses processor execute function code.
|
|
743
|
+
*/
|
|
744
|
+
async function bundleAndCompressProcessorCode(executeFunction, processorName, type, distDir) {
|
|
745
|
+
const { compressCode } = await import('./compile.js');
|
|
746
|
+
const tempDir = path.join(distDir, '.temp');
|
|
747
|
+
if (!fs.existsSync(tempDir)) {
|
|
748
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
749
|
+
}
|
|
750
|
+
const tempFile = path.join(tempDir, `${processorName}-${type}processor.ts`);
|
|
751
|
+
const tempOutput = path.join(tempDir, `${processorName}-${type}processor.js`);
|
|
752
|
+
try {
|
|
753
|
+
// Processor execute functions receive: (user, message, [response], channel)
|
|
754
|
+
const paramList = type === 'pre' ? 'user, message, channel' : 'user, message, response, channel';
|
|
755
|
+
const moduleCode = stripLuaCliImports(`
|
|
756
|
+
// ${type === 'pre' ? 'Pre' : 'Post'}Processor execute function for ${processorName}
|
|
757
|
+
export default ${executeFunction};
|
|
758
|
+
`);
|
|
759
|
+
fs.writeFileSync(tempFile, moduleCode);
|
|
760
|
+
await build({
|
|
761
|
+
entryPoints: [tempFile],
|
|
762
|
+
bundle: true,
|
|
763
|
+
platform: 'node',
|
|
764
|
+
target: 'node16',
|
|
765
|
+
format: 'cjs',
|
|
766
|
+
minify: true,
|
|
767
|
+
outfile: tempOutput,
|
|
768
|
+
external: [],
|
|
769
|
+
plugins: [sandboxGlobalsPlugin],
|
|
770
|
+
});
|
|
771
|
+
let bundledCode = fs.readFileSync(tempOutput, 'utf8');
|
|
772
|
+
const wrappedCode = `(async (${paramList}) => {
|
|
773
|
+
// Execute the bundled processor code
|
|
774
|
+
${bundledCode}
|
|
775
|
+
|
|
776
|
+
const executeFunction = module.exports.default || module.exports;
|
|
777
|
+
return await executeFunction(${paramList});
|
|
778
|
+
})`;
|
|
779
|
+
const compressed = compressCode(wrappedCode);
|
|
780
|
+
try {
|
|
781
|
+
fs.unlinkSync(tempFile);
|
|
782
|
+
fs.unlinkSync(tempOutput);
|
|
783
|
+
}
|
|
784
|
+
catch (cleanupError) {
|
|
785
|
+
// Ignore
|
|
786
|
+
}
|
|
787
|
+
return compressed;
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
console.warn(`Warning: Could not bundle ${type}processor ${processorName} code:`, error);
|
|
791
|
+
try {
|
|
792
|
+
if (fs.existsSync(tempFile))
|
|
793
|
+
fs.unlinkSync(tempFile);
|
|
794
|
+
if (fs.existsSync(tempOutput))
|
|
795
|
+
fs.unlinkSync(tempOutput);
|
|
796
|
+
}
|
|
797
|
+
catch (cleanupError) {
|
|
798
|
+
// Ignore
|
|
799
|
+
}
|
|
800
|
+
return '';
|
|
801
|
+
}
|
|
802
|
+
}
|
|
118
803
|
/**
|
|
119
804
|
* Extracts execute code and input schema from a tool.
|
|
120
805
|
* This function:
|
|
121
806
|
* 1. Reads the bundled tool code
|
|
122
|
-
* 2.
|
|
123
|
-
* 3.
|
|
807
|
+
* 2. Detects and bundles any Jobs.create() calls within execute
|
|
808
|
+
* 3. Extracts the execute function
|
|
809
|
+
* 4. Converts Zod schema to JSON Schema format
|
|
124
810
|
*
|
|
125
811
|
* @param tool - The tool to extract metadata from (mutated with executeCode and inputSchema)
|
|
126
812
|
* @param project - The ts-morph Project instance for AST analysis
|
|
813
|
+
* @param distDir - Distribution directory for bundling nested jobs
|
|
127
814
|
*/
|
|
128
|
-
export async function extractExecuteCode(tool, project) {
|
|
815
|
+
export async function extractExecuteCode(tool, project, distDir) {
|
|
129
816
|
try {
|
|
130
817
|
const toolSourceFile = project.getSourceFile(tool.filePath);
|
|
131
818
|
if (!toolSourceFile) {
|
|
@@ -137,19 +824,32 @@ export async function extractExecuteCode(tool, project) {
|
|
|
137
824
|
console.warn(`Warning: Could not find class ${tool.className} in ${tool.filePath}`);
|
|
138
825
|
return;
|
|
139
826
|
}
|
|
140
|
-
// Verify execute method exists
|
|
827
|
+
// Verify execute method or property exists
|
|
141
828
|
const executeMethod = classDecl.getMethod('execute');
|
|
142
|
-
|
|
143
|
-
|
|
829
|
+
const executeProperty = classDecl.getProperty('execute');
|
|
830
|
+
if (!executeMethod && !executeProperty) {
|
|
831
|
+
console.warn(`Warning: Could not find execute method or property in ${tool.className}`);
|
|
144
832
|
return;
|
|
145
833
|
}
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
834
|
+
// Validate execute has a body (for methods) or initializer (for arrow function properties)
|
|
835
|
+
if (executeMethod) {
|
|
836
|
+
const executeBody = executeMethod.getBodyText();
|
|
837
|
+
if (!executeBody) {
|
|
838
|
+
console.warn(`Warning: Execute method has no body in ${tool.className}`);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
else if (executeProperty) {
|
|
843
|
+
const initializer = executeProperty.getInitializer();
|
|
844
|
+
if (!initializer) {
|
|
845
|
+
console.warn(`Warning: Execute property has no initializer in ${tool.className}`);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
150
848
|
}
|
|
151
849
|
// Extract execute code from bundled file
|
|
152
850
|
tool.executeCode = extractExecuteCodeFromBundledFile(tool);
|
|
851
|
+
// NOTE: Dynamic job bundling moved to source-level processing
|
|
852
|
+
// Attempting to extract from minified bundled code is too fragile
|
|
153
853
|
// Extract and convert input schema
|
|
154
854
|
tool.inputSchema = await extractInputSchema(classDecl, tool);
|
|
155
855
|
}
|