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.
Files changed (98) hide show
  1. package/dist/api/job.api.service.d.ts +210 -0
  2. package/dist/api/job.api.service.js +200 -0
  3. package/dist/api/lazy-instances.d.ts +24 -0
  4. package/dist/api/lazy-instances.js +48 -0
  5. package/dist/api/postprocessor.api.service.d.ts +98 -0
  6. package/dist/api/postprocessor.api.service.js +76 -0
  7. package/dist/api/preprocessor.api.service.d.ts +98 -0
  8. package/dist/api/preprocessor.api.service.js +76 -0
  9. package/dist/api/user.data.api.service.d.ts +13 -0
  10. package/dist/api/user.data.api.service.js +20 -0
  11. package/dist/api/webhook.api.service.d.ts +151 -0
  12. package/dist/api/webhook.api.service.js +134 -0
  13. package/dist/api-exports.d.ts +156 -41
  14. package/dist/api-exports.js +182 -21
  15. package/dist/cli/command-definitions.js +75 -5
  16. package/dist/commands/compile.js +124 -5
  17. package/dist/commands/index.d.ts +4 -0
  18. package/dist/commands/index.js +4 -0
  19. package/dist/commands/init.js +53 -7
  20. package/dist/commands/jobs.d.ts +20 -0
  21. package/dist/commands/jobs.js +533 -0
  22. package/dist/commands/logs.js +2 -5
  23. package/dist/commands/postprocessors.d.ts +8 -0
  24. package/dist/commands/postprocessors.js +431 -0
  25. package/dist/commands/preprocessors.d.ts +8 -0
  26. package/dist/commands/preprocessors.js +431 -0
  27. package/dist/commands/push.js +684 -5
  28. package/dist/commands/test.d.ts +9 -18
  29. package/dist/commands/test.js +558 -82
  30. package/dist/commands/webhooks.d.ts +18 -0
  31. package/dist/commands/webhooks.js +424 -0
  32. package/dist/common/job.instance.d.ts +77 -0
  33. package/dist/common/job.instance.js +108 -0
  34. package/dist/common/user.instance.d.ts +1 -0
  35. package/dist/common/user.instance.js +9 -0
  36. package/dist/config/constants.d.ts +2 -2
  37. package/dist/config/constants.js +4 -4
  38. package/dist/interfaces/agent.d.ts +2 -1
  39. package/dist/interfaces/chat.d.ts +22 -0
  40. package/dist/interfaces/index.d.ts +10 -0
  41. package/dist/interfaces/index.js +7 -0
  42. package/dist/interfaces/jobs.d.ts +172 -0
  43. package/dist/interfaces/jobs.js +5 -0
  44. package/dist/interfaces/postprocessors.d.ts +35 -0
  45. package/dist/interfaces/postprocessors.js +4 -0
  46. package/dist/interfaces/preprocessors.d.ts +35 -0
  47. package/dist/interfaces/preprocessors.js +4 -0
  48. package/dist/interfaces/webhooks.d.ts +104 -0
  49. package/dist/interfaces/webhooks.js +5 -0
  50. package/dist/types/api-contracts.d.ts +5 -0
  51. package/dist/types/compile.types.d.ts +49 -0
  52. package/dist/types/index.d.ts +1 -1
  53. package/dist/types/index.js +1 -1
  54. package/dist/types/skill.d.ts +502 -0
  55. package/dist/types/skill.js +477 -0
  56. package/dist/utils/agent-management.d.ts +25 -0
  57. package/dist/utils/agent-management.js +67 -0
  58. package/dist/utils/bundling.d.ts +31 -1
  59. package/dist/utils/bundling.js +653 -10
  60. package/dist/utils/compile.d.ts +63 -0
  61. package/dist/utils/compile.js +691 -36
  62. package/dist/utils/deployment.d.ts +2 -1
  63. package/dist/utils/deployment.js +16 -2
  64. package/dist/utils/init-agent.d.ts +3 -1
  65. package/dist/utils/init-agent.js +6 -4
  66. package/dist/utils/init-prompts.d.ts +2 -1
  67. package/dist/utils/init-prompts.js +14 -9
  68. package/dist/utils/job-management.d.ts +24 -0
  69. package/dist/utils/job-management.js +264 -0
  70. package/dist/utils/postprocessor-management.d.ts +9 -0
  71. package/dist/utils/postprocessor-management.js +118 -0
  72. package/dist/utils/preprocessor-management.d.ts +9 -0
  73. package/dist/utils/preprocessor-management.js +118 -0
  74. package/dist/utils/sandbox.d.ts +61 -1
  75. package/dist/utils/sandbox.js +283 -72
  76. package/dist/utils/tool-detection.d.ts +3 -2
  77. package/dist/utils/tool-detection.js +18 -4
  78. package/dist/utils/webhook-management.d.ts +24 -0
  79. package/dist/utils/webhook-management.js +256 -0
  80. package/package.json +1 -1
  81. package/template/AGENT_CONFIGURATION.md +251 -0
  82. package/template/COMPLEX_JOB_EXAMPLES.md +795 -0
  83. package/template/DYNAMIC_JOB_CREATION.md +371 -0
  84. package/template/README.md +30 -2
  85. package/template/WEBHOOKS_JOBS_QUICKSTART.md +318 -0
  86. package/template/WEBHOOK_JOB_EXAMPLES.md +817 -0
  87. package/template/src/index-agent-example.ts +201 -0
  88. package/template/src/index.ts +39 -0
  89. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  90. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  91. package/template/src/jobs/DataMigrationJob.ts +133 -0
  92. package/template/src/jobs/HealthCheckJob.ts +87 -0
  93. package/template/src/postprocessors/ResponseFormatter.ts +151 -0
  94. package/template/src/preprocessors/MessageFilter.ts +91 -0
  95. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  96. package/template/src/tools/SmartBasketTool.ts +188 -0
  97. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  98. package/template/src/webhooks/UserEventWebhook.ts +77 -0
@@ -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 (Product, User, Data, Baskets, Order)
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: [tool.filePath],
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
- if (!executeMethod) {
143
- console.warn(`Warning: Could not find execute method in ${tool.className}`);
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
- const executeBody = executeMethod.getBodyText();
147
- if (!executeBody) {
148
- console.warn(`Warning: Execute method has no body in ${tool.className}`);
149
- return;
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);