lua-cli 3.0.2-alpha.1 → 3.0.2-alpha.2
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/utils/bundling.js +135 -176
- package/package.json +5 -1
- package/template/package.json +1 -1
- package/dist/utils/dynamic-job-bundler.d.ts +0 -17
- package/dist/utils/dynamic-job-bundler.js +0 -143
package/dist/utils/bundling.js
CHANGED
|
@@ -7,7 +7,7 @@ import path from "path";
|
|
|
7
7
|
import { build } from "esbuild";
|
|
8
8
|
import { Project, Node } from "ts-morph";
|
|
9
9
|
import { writeProgress } from "./cli.js";
|
|
10
|
-
import { COMPILE_DIRS, COMPILE_FILES, ESBUILD_TOOL_CONFIG, ESBUILD_INDEX_CONFIG, DEFAULT_INPUT_SCHEMA, } from '../config/compile.constants.js';
|
|
10
|
+
import { COMPILE_DIRS, COMPILE_FILES, ESBUILD_TOOL_CONFIG, ESBUILD_INDEX_CONFIG, DEFAULT_INPUT_SCHEMA, EXTERNAL_PACKAGES, } from '../config/compile.constants.js';
|
|
11
11
|
import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema } from './compile.js';
|
|
12
12
|
/**
|
|
13
13
|
* Helper function to remove lua-cli and api-exports imports from source code.
|
|
@@ -30,6 +30,108 @@ function stripLuaCliImports(sourceCode) {
|
|
|
30
30
|
: '// lua-cli/skill imports removed - using sandbox globals';
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Extracts and filters relevant imports from source code.
|
|
35
|
+
* Removes lua-cli internal classes and API imports that are available in the sandbox.
|
|
36
|
+
*
|
|
37
|
+
* @param sourceFilePath - Path to the source file
|
|
38
|
+
* @returns Array of relevant import statements
|
|
39
|
+
*/
|
|
40
|
+
function extractRelevantImports(sourceFilePath) {
|
|
41
|
+
const sourceContent = fs.readFileSync(sourceFilePath, 'utf8');
|
|
42
|
+
// Extract all import statements from the source
|
|
43
|
+
const importPattern = /^import\s+.+\s+from\s+['"][^'"]+['"];?$/gm;
|
|
44
|
+
const imports = sourceContent.match(importPattern) || [];
|
|
45
|
+
// Filter to only imports that might be used (exclude lua-cli internal classes and APIs)
|
|
46
|
+
return imports.filter(imp => !imp.includes('LuaTool') &&
|
|
47
|
+
!imp.includes('LuaWebhook') &&
|
|
48
|
+
!imp.includes('LuaJob') &&
|
|
49
|
+
!imp.includes('PreProcessor') &&
|
|
50
|
+
!imp.includes('PostProcessor') &&
|
|
51
|
+
!imp.includes('LuaAgent') &&
|
|
52
|
+
!imp.includes('lua-cli') && // Exclude all lua-cli imports (they're available in sandbox)
|
|
53
|
+
!imp.includes('api-exports') && // Exclude api-exports
|
|
54
|
+
!imp.includes('../../../dist/api-exports') // Exclude direct api-exports imports
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Generic function to bundle and compress execute function code with dependencies.
|
|
59
|
+
*
|
|
60
|
+
* @param executeFunction - Raw execute function code
|
|
61
|
+
* @param componentName - Name of the component (webhook/job/processor)
|
|
62
|
+
* @param componentType - Type identifier for temp files
|
|
63
|
+
* @param vmWrapperGenerator - Function that generates the VM wrapper code
|
|
64
|
+
* @param distDir - Distribution directory
|
|
65
|
+
* @param sourceFilePath - Path to source file containing the component
|
|
66
|
+
* @returns Compressed base64-encoded bundled code
|
|
67
|
+
*/
|
|
68
|
+
async function bundleAndCompressExecuteFunction(executeFunction, componentName, componentType, vmWrapperGenerator, distDir, sourceFilePath) {
|
|
69
|
+
const { compressCode } = await import('./compile.js');
|
|
70
|
+
// Create temporary file with the execute function wrapped as a module
|
|
71
|
+
const tempDir = path.join(distDir, '.temp');
|
|
72
|
+
if (!fs.existsSync(tempDir)) {
|
|
73
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const tempFile = path.join(tempDir, `${componentName}-${componentType}.ts`);
|
|
76
|
+
const tempOutput = path.join(tempDir, `${componentName}-${componentType}.js`);
|
|
77
|
+
try {
|
|
78
|
+
// Extract relevant imports from source file
|
|
79
|
+
const relevantImports = extractRelevantImports(sourceFilePath);
|
|
80
|
+
// Write execute function as a module export with all relevant imports
|
|
81
|
+
const moduleCode = `
|
|
82
|
+
// ${componentType} execute function for ${componentName}
|
|
83
|
+
${relevantImports.join('\n')}
|
|
84
|
+
|
|
85
|
+
// The execute function with all dependencies available
|
|
86
|
+
const executeFunc = ${executeFunction};
|
|
87
|
+
|
|
88
|
+
export default executeFunc;
|
|
89
|
+
`;
|
|
90
|
+
fs.writeFileSync(tempFile, moduleCode);
|
|
91
|
+
// Bundle with esbuild using the source file's directory for import resolution
|
|
92
|
+
await build({
|
|
93
|
+
entryPoints: [tempFile],
|
|
94
|
+
bundle: true,
|
|
95
|
+
platform: 'node',
|
|
96
|
+
target: 'node16',
|
|
97
|
+
format: 'cjs',
|
|
98
|
+
minify: true,
|
|
99
|
+
outfile: tempOutput,
|
|
100
|
+
external: [...EXTERNAL_PACKAGES], // Use same external packages as tools
|
|
101
|
+
plugins: [sandboxGlobalsPlugin],
|
|
102
|
+
absWorkingDir: path.dirname(sourceFilePath), // Use source file's directory for resolution
|
|
103
|
+
});
|
|
104
|
+
// Read bundled code
|
|
105
|
+
const bundledCode = fs.readFileSync(tempOutput, 'utf8');
|
|
106
|
+
// Wrap for VM execution using provided generator
|
|
107
|
+
const wrappedCode = vmWrapperGenerator(bundledCode);
|
|
108
|
+
// Compress the wrapped code
|
|
109
|
+
const compressed = compressCode(wrappedCode);
|
|
110
|
+
// Clean up temp files
|
|
111
|
+
try {
|
|
112
|
+
fs.unlinkSync(tempFile);
|
|
113
|
+
fs.unlinkSync(tempOutput);
|
|
114
|
+
}
|
|
115
|
+
catch (cleanupError) {
|
|
116
|
+
// Ignore cleanup errors
|
|
117
|
+
}
|
|
118
|
+
return compressed;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn(`Warning: Could not bundle ${componentType} ${componentName} code:`, error);
|
|
122
|
+
// Clean up on error
|
|
123
|
+
try {
|
|
124
|
+
if (fs.existsSync(tempFile))
|
|
125
|
+
fs.unlinkSync(tempFile);
|
|
126
|
+
if (fs.existsSync(tempOutput))
|
|
127
|
+
fs.unlinkSync(tempOutput);
|
|
128
|
+
}
|
|
129
|
+
catch (cleanupError) {
|
|
130
|
+
// Ignore cleanup errors
|
|
131
|
+
}
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
33
135
|
/**
|
|
34
136
|
* esbuild plugin to inject sandbox globals instead of requiring lua-cli
|
|
35
137
|
* This removes require("lua-cli") statements and injects the global API objects
|
|
@@ -226,6 +328,7 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
|
|
|
226
328
|
let querySchema = undefined;
|
|
227
329
|
let headerSchema = undefined;
|
|
228
330
|
let bodySchema = undefined;
|
|
331
|
+
let sourceFilePath = indexFile.getFilePath();
|
|
229
332
|
// Helper function to search for webhook in a file
|
|
230
333
|
const searchFileForWebhook = (file) => {
|
|
231
334
|
file.forEachDescendant((node) => {
|
|
@@ -271,6 +374,8 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
|
|
|
271
374
|
querySchema = foundQuerySchema;
|
|
272
375
|
headerSchema = foundHeaderSchema;
|
|
273
376
|
bodySchema = foundBodySchema;
|
|
377
|
+
// Update source file path to the file where webhook was found
|
|
378
|
+
sourceFilePath = file.getFilePath();
|
|
274
379
|
}
|
|
275
380
|
}
|
|
276
381
|
}
|
|
@@ -315,7 +420,7 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
|
|
|
315
420
|
// Bundle and compress the execute function (like tools)
|
|
316
421
|
let compressedCode = '';
|
|
317
422
|
if (executeFunction) {
|
|
318
|
-
compressedCode = await bundleAndCompressWebhookCode(executeFunction, webhook.name, distDir);
|
|
423
|
+
compressedCode = await bundleAndCompressWebhookCode(executeFunction, webhook.name, distDir, sourceFilePath);
|
|
319
424
|
}
|
|
320
425
|
return {
|
|
321
426
|
...webhook,
|
|
@@ -332,44 +437,16 @@ export async function bundleWebhook(webhook, indexFile, distDir, project) {
|
|
|
332
437
|
/**
|
|
333
438
|
* Bundles and compresses webhook execute function code.
|
|
334
439
|
* Creates a temporary file, bundles with esbuild, compresses with gzip.
|
|
440
|
+
* Includes all imports from the source file to ensure dependencies are bundled.
|
|
335
441
|
*
|
|
336
442
|
* @param executeFunction - Raw execute function code
|
|
337
443
|
* @param webhookName - Name of the webhook
|
|
338
444
|
* @param distDir - Distribution directory
|
|
445
|
+
* @param sourceFilePath - Path to the source file containing the webhook
|
|
339
446
|
* @returns Compressed base64-encoded bundled code
|
|
340
447
|
*/
|
|
341
|
-
async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDir) {
|
|
342
|
-
|
|
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) => {
|
|
448
|
+
async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDir, sourceFilePath) {
|
|
449
|
+
return bundleAndCompressExecuteFunction(executeFunction, webhookName, 'webhook', (bundledCode) => `(async (query, headers, body) => {
|
|
373
450
|
|
|
374
451
|
// Execute the bundled webhook code
|
|
375
452
|
${bundledCode}
|
|
@@ -379,33 +456,7 @@ export default ${executeFunction};
|
|
|
379
456
|
|
|
380
457
|
// Execute with three separate parameters (not an object)
|
|
381
458
|
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
|
-
}
|
|
459
|
+
})`, distDir, sourceFilePath);
|
|
409
460
|
}
|
|
410
461
|
/**
|
|
411
462
|
* Extracts and bundles job execute function.
|
|
@@ -421,6 +472,7 @@ export async function bundleJob(job, indexFile, distDir, project) {
|
|
|
421
472
|
try {
|
|
422
473
|
// Find the LuaJob constructor in the AST
|
|
423
474
|
let executeFunction = '';
|
|
475
|
+
let sourceFilePath = indexFile.getFilePath();
|
|
424
476
|
// Helper function to search for job in a file
|
|
425
477
|
const searchFileForJob = (file) => {
|
|
426
478
|
file.forEachDescendant((node) => {
|
|
@@ -451,6 +503,8 @@ export async function bundleJob(job, indexFile, distDir, project) {
|
|
|
451
503
|
// Only set executeFunction if this is the matching job
|
|
452
504
|
if (isMatchingJob && foundExecute) {
|
|
453
505
|
executeFunction = foundExecute;
|
|
506
|
+
// Update source file path to the file where job was found
|
|
507
|
+
sourceFilePath = file.getFilePath();
|
|
454
508
|
}
|
|
455
509
|
}
|
|
456
510
|
}
|
|
@@ -484,7 +538,7 @@ export async function bundleJob(job, indexFile, distDir, project) {
|
|
|
484
538
|
// Bundle and compress the execute function (like tools)
|
|
485
539
|
let compressedCode = '';
|
|
486
540
|
if (executeFunction) {
|
|
487
|
-
compressedCode = await bundleAndCompressJobCode(executeFunction, job.name, distDir);
|
|
541
|
+
compressedCode = await bundleAndCompressJobCode(executeFunction, job.name, distDir, sourceFilePath);
|
|
488
542
|
}
|
|
489
543
|
return {
|
|
490
544
|
...job,
|
|
@@ -500,44 +554,16 @@ export async function bundleJob(job, indexFile, distDir, project) {
|
|
|
500
554
|
/**
|
|
501
555
|
* Bundles and compresses job execute function code.
|
|
502
556
|
* Creates a temporary file, bundles with esbuild, compresses with gzip.
|
|
557
|
+
* Includes all imports from the source file to ensure dependencies are bundled.
|
|
503
558
|
*
|
|
504
559
|
* @param executeFunction - Raw execute function code
|
|
505
560
|
* @param jobName - Name of the job
|
|
506
561
|
* @param distDir - Distribution directory
|
|
562
|
+
* @param sourceFilePath - Path to the source file containing the job
|
|
507
563
|
* @returns Compressed base64-encoded bundled code
|
|
508
564
|
*/
|
|
509
|
-
async function bundleAndCompressJobCode(executeFunction, jobName, distDir) {
|
|
510
|
-
|
|
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) => {
|
|
565
|
+
async function bundleAndCompressJobCode(executeFunction, jobName, distDir, sourceFilePath) {
|
|
566
|
+
return bundleAndCompressExecuteFunction(executeFunction, jobName, 'job', (bundledCode) => `(async (job) => {
|
|
541
567
|
// Execute the bundled job code
|
|
542
568
|
${bundledCode}
|
|
543
569
|
|
|
@@ -546,33 +572,7 @@ export default ${executeFunction};
|
|
|
546
572
|
|
|
547
573
|
// Execute job with job instance parameter
|
|
548
574
|
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
|
-
}
|
|
575
|
+
})`, distDir, sourceFilePath);
|
|
576
576
|
}
|
|
577
577
|
/**
|
|
578
578
|
* Bundles and compresses preprocessor execute function code.
|
|
@@ -581,6 +581,7 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
|
|
|
581
581
|
writeProgress(`📦 Bundling preprocessor ${preprocessor.name}...`);
|
|
582
582
|
try {
|
|
583
583
|
let executeFunction = '';
|
|
584
|
+
let sourceFilePath = indexFile.getFilePath();
|
|
584
585
|
const searchFileForPreProcessor = (file) => {
|
|
585
586
|
file.forEachDescendant((node) => {
|
|
586
587
|
if (Node.isNewExpression(node)) {
|
|
@@ -608,6 +609,8 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
|
|
|
608
609
|
});
|
|
609
610
|
if (isMatching && foundExecute) {
|
|
610
611
|
executeFunction = foundExecute;
|
|
612
|
+
// Update source file path to the file where preprocessor was found
|
|
613
|
+
sourceFilePath = file.getFilePath();
|
|
611
614
|
}
|
|
612
615
|
}
|
|
613
616
|
}
|
|
@@ -639,7 +642,7 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
|
|
|
639
642
|
}
|
|
640
643
|
let compressedCode = '';
|
|
641
644
|
if (executeFunction) {
|
|
642
|
-
compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir);
|
|
645
|
+
compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir, sourceFilePath);
|
|
643
646
|
}
|
|
644
647
|
return {
|
|
645
648
|
name: preprocessor.name,
|
|
@@ -663,6 +666,7 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
|
|
|
663
666
|
writeProgress(`📦 Bundling postprocessor ${postprocessor.name}...`);
|
|
664
667
|
try {
|
|
665
668
|
let executeFunction = '';
|
|
669
|
+
let sourceFilePath = indexFile.getFilePath();
|
|
666
670
|
const searchFileForPostProcessor = (file) => {
|
|
667
671
|
file.forEachDescendant((node) => {
|
|
668
672
|
if (Node.isNewExpression(node)) {
|
|
@@ -690,6 +694,8 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
|
|
|
690
694
|
});
|
|
691
695
|
if (isMatching && foundExecute) {
|
|
692
696
|
executeFunction = foundExecute;
|
|
697
|
+
// Update source file path to the file where postprocessor was found
|
|
698
|
+
sourceFilePath = file.getFilePath();
|
|
693
699
|
}
|
|
694
700
|
}
|
|
695
701
|
}
|
|
@@ -721,7 +727,7 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
|
|
|
721
727
|
}
|
|
722
728
|
let compressedCode = '';
|
|
723
729
|
if (executeFunction) {
|
|
724
|
-
compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir);
|
|
730
|
+
compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir, sourceFilePath);
|
|
725
731
|
}
|
|
726
732
|
return {
|
|
727
733
|
name: postprocessor.name,
|
|
@@ -740,65 +746,18 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
|
|
|
740
746
|
}
|
|
741
747
|
/**
|
|
742
748
|
* Bundles and compresses processor execute function code.
|
|
749
|
+
* Includes all imports from the source file to ensure dependencies are bundled.
|
|
743
750
|
*/
|
|
744
|
-
async function bundleAndCompressProcessorCode(executeFunction, processorName, type, distDir) {
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
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}) => {
|
|
751
|
+
async function bundleAndCompressProcessorCode(executeFunction, processorName, type, distDir, sourceFilePath) {
|
|
752
|
+
// Processor execute functions receive: (user, message, [response], channel)
|
|
753
|
+
const paramList = type === 'pre' ? 'user, message, channel' : 'user, message, response, channel';
|
|
754
|
+
return bundleAndCompressExecuteFunction(executeFunction, processorName, `${type}processor`, (bundledCode) => `(async (${paramList}) => {
|
|
773
755
|
// Execute the bundled processor code
|
|
774
756
|
${bundledCode}
|
|
775
757
|
|
|
776
758
|
const executeFunction = module.exports.default || module.exports;
|
|
777
759
|
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
|
-
}
|
|
760
|
+
})`, distDir, sourceFilePath);
|
|
802
761
|
}
|
|
803
762
|
/**
|
|
804
763
|
* Extracts execute code and input schema from a tool.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lua-cli",
|
|
3
|
-
"version": "3.0.2-alpha.
|
|
3
|
+
"version": "3.0.2-alpha.2",
|
|
4
4
|
"description": "Build, test, and deploy AI agents with custom tools, webhooks, and scheduled jobs. Features LuaAgent unified configuration, streaming chat, and batch deployment.",
|
|
5
5
|
"readmeFilename": "README.md",
|
|
6
6
|
"main": "dist/api-exports.js",
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
"@types/inquirer": "^9.0.9",
|
|
90
90
|
"@types/jest": "^29.5.8",
|
|
91
91
|
"@types/js-yaml": "^4.0.9",
|
|
92
|
+
"@types/lodash": "^4.17.20",
|
|
92
93
|
"@types/node": "^24.5.1",
|
|
93
94
|
"@types/node-fetch": "^2.6.13",
|
|
94
95
|
"@types/react": "^18.2.0",
|
|
@@ -96,7 +97,10 @@
|
|
|
96
97
|
"@types/ws": "^8.18.1",
|
|
97
98
|
"@typescript-eslint/parser": "^8.44.1",
|
|
98
99
|
"@typescript-eslint/typescript-estree": "^8.44.1",
|
|
100
|
+
"date-fns": "^4.1.0",
|
|
99
101
|
"jest": "^29.7.0",
|
|
102
|
+
"lodash": "^4.17.21",
|
|
103
|
+
"stripe": "^19.2.0",
|
|
100
104
|
"ts-jest": "^29.1.1",
|
|
101
105
|
"ts-node": "^10.9.2",
|
|
102
106
|
"typescript": "^5.9.2"
|
package/template/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dynamic Job Bundler
|
|
3
|
-
* Handles detection and bundling of Jobs.create() calls within execute functions
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Detects if code contains Jobs.create() calls
|
|
7
|
-
*/
|
|
8
|
-
export declare function containsJobsCreate(code: string): boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Extracts Jobs.create() execute functions and bundles them with dependencies.
|
|
11
|
-
* Finds execute functions in the code and creates standalone bundles.
|
|
12
|
-
*/
|
|
13
|
-
export declare function bundleNestedJobs(code: string, parentName: string, distDir: string): Promise<string>;
|
|
14
|
-
/**
|
|
15
|
-
* Cleans up temporary bundling directory
|
|
16
|
-
*/
|
|
17
|
-
export declare function cleanupTempDir(distDir: string): void;
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dynamic Job Bundler
|
|
3
|
-
* Handles detection and bundling of Jobs.create() calls within execute functions
|
|
4
|
-
*/
|
|
5
|
-
import { build } from 'esbuild';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import { compressCode } from './compile.js';
|
|
9
|
-
import { sandboxGlobalsPlugin } from './bundling.js';
|
|
10
|
-
/**
|
|
11
|
-
* Detects if code contains Jobs.create() calls
|
|
12
|
-
*/
|
|
13
|
-
export function containsJobsCreate(code) {
|
|
14
|
-
const found = /Jobs\.create\s*\(/i.test(code);
|
|
15
|
-
if (found) {
|
|
16
|
-
console.log('[DynamicJobs] ✅ Detected Jobs.create() in code');
|
|
17
|
-
}
|
|
18
|
-
return found;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Extracts Jobs.create() execute functions and bundles them with dependencies.
|
|
22
|
-
* Finds execute functions in the code and creates standalone bundles.
|
|
23
|
-
*/
|
|
24
|
-
export async function bundleNestedJobs(code, parentName, distDir) {
|
|
25
|
-
if (!containsJobsCreate(code)) {
|
|
26
|
-
return code;
|
|
27
|
-
}
|
|
28
|
-
console.log(`[DynamicJobs] ⚠️ Jobs.create() with external dependencies not yet fully supported`);
|
|
29
|
-
console.log(`[DynamicJobs] 💡 Recommendation: Use fetch() or lua-cli APIs in job execute functions`);
|
|
30
|
-
console.log(`[DynamicJobs] 📋 Full bundling support planned for v3.1.0`);
|
|
31
|
-
// Return original code as-is
|
|
32
|
-
// The job will have access to lua-cli globals but external packages won't be bundled
|
|
33
|
-
return code;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Helper to extract job execute function from code
|
|
37
|
-
*/
|
|
38
|
-
function extractJobExecuteFromCode(code) {
|
|
39
|
-
// This extracts the execute function body
|
|
40
|
-
// For now, return a placeholder that indicates the limitation
|
|
41
|
-
return `
|
|
42
|
-
// Note: This job execute function runs with limited context
|
|
43
|
-
// Use fetch() for external APIs or lua-cli globals (User, Data, Products)
|
|
44
|
-
console.log('Job execute running with user:', user);
|
|
45
|
-
return { executed: true };
|
|
46
|
-
`;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Bundles a job's execute function independently
|
|
50
|
-
*/
|
|
51
|
-
async function bundleJobExecuteFunction(executeFunction, jobName, distDir) {
|
|
52
|
-
const tempDir = path.join(distDir, '.temp');
|
|
53
|
-
if (!fs.existsSync(tempDir)) {
|
|
54
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
55
|
-
}
|
|
56
|
-
const tempFile = path.join(tempDir, `${jobName}.js`);
|
|
57
|
-
const tempOutput = path.join(tempDir, `${jobName}-bundled.js`);
|
|
58
|
-
try {
|
|
59
|
-
// Extract imports that the execute function might need
|
|
60
|
-
// Common ones: Stripe, axios, etc.
|
|
61
|
-
const moduleCode = `
|
|
62
|
-
// Job execute function for ${jobName}
|
|
63
|
-
// Import dependencies that might be used
|
|
64
|
-
import Stripe from 'stripe';
|
|
65
|
-
import { env, User, Data, Products, Baskets, Orders } from 'lua-cli';
|
|
66
|
-
|
|
67
|
-
// The execute function with all dependencies available
|
|
68
|
-
const executeFunc = ${executeFunction};
|
|
69
|
-
|
|
70
|
-
// Export as default for esbuild to bundle
|
|
71
|
-
export default executeFunc;
|
|
72
|
-
`;
|
|
73
|
-
fs.writeFileSync(tempFile, moduleCode);
|
|
74
|
-
// Bundle with esbuild - bundle ALL dependencies (including Stripe, axios, etc.)
|
|
75
|
-
await build({
|
|
76
|
-
entryPoints: [tempFile],
|
|
77
|
-
bundle: true,
|
|
78
|
-
platform: 'node',
|
|
79
|
-
target: 'node16',
|
|
80
|
-
format: 'cjs',
|
|
81
|
-
minify: true,
|
|
82
|
-
outfile: tempOutput,
|
|
83
|
-
external: [], // Bundle everything except lua-cli APIs (handled by plugin)
|
|
84
|
-
plugins: [sandboxGlobalsPlugin], // This handles lua-cli globals
|
|
85
|
-
// Important: Don't externalize packages like stripe, axios, etc.
|
|
86
|
-
// They need to be included in the bundle
|
|
87
|
-
});
|
|
88
|
-
// Read bundled code (includes all dependencies like Stripe, axios, etc.)
|
|
89
|
-
let bundledCode = fs.readFileSync(tempOutput, 'utf8');
|
|
90
|
-
// Log bundle size for debugging
|
|
91
|
-
console.log(`[DynamicJobs] Bundled size for ${jobName}: ${(bundledCode.length / 1024).toFixed(2)}KB`);
|
|
92
|
-
// Wrap for VM execution - the bundled code includes ALL dependencies
|
|
93
|
-
const wrappedCode = `(async (job) => {
|
|
94
|
-
|
|
95
|
-
${bundledCode}
|
|
96
|
-
|
|
97
|
-
// Get the execute function from exports
|
|
98
|
-
const executeFunction = module.exports || module.exports.default;
|
|
99
|
-
|
|
100
|
-
// Execute with job
|
|
101
|
-
return await executeFunction(job);
|
|
102
|
-
})`;
|
|
103
|
-
// Compress the wrapped code
|
|
104
|
-
const compressed = compressCode(wrappedCode);
|
|
105
|
-
// Clean up temp files
|
|
106
|
-
try {
|
|
107
|
-
fs.unlinkSync(tempFile);
|
|
108
|
-
fs.unlinkSync(tempOutput);
|
|
109
|
-
}
|
|
110
|
-
catch (cleanupError) {
|
|
111
|
-
// Ignore cleanup errors
|
|
112
|
-
}
|
|
113
|
-
return compressed;
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
console.warn(`Warning: Could not bundle job execute function ${jobName}:`, error);
|
|
117
|
-
// Clean up on error
|
|
118
|
-
try {
|
|
119
|
-
if (fs.existsSync(tempFile))
|
|
120
|
-
fs.unlinkSync(tempFile);
|
|
121
|
-
if (fs.existsSync(tempOutput))
|
|
122
|
-
fs.unlinkSync(tempOutput);
|
|
123
|
-
}
|
|
124
|
-
catch (cleanupError) {
|
|
125
|
-
// Ignore cleanup errors
|
|
126
|
-
}
|
|
127
|
-
return '';
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Cleans up temporary bundling directory
|
|
132
|
-
*/
|
|
133
|
-
export function cleanupTempDir(distDir) {
|
|
134
|
-
const tempDir = path.join(distDir, '.temp');
|
|
135
|
-
if (fs.existsSync(tempDir)) {
|
|
136
|
-
try {
|
|
137
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
// Ignore cleanup errors
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|