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