lua-cli 3.0.0-alpha.1 → 3.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/api/job.api.service.d.ts +16 -7
  2. package/dist/api/job.api.service.js +21 -5
  3. package/dist/api/postprocessor.api.service.d.ts +61 -1
  4. package/dist/api/postprocessor.api.service.js +35 -0
  5. package/dist/api/preprocessor.api.service.d.ts +61 -1
  6. package/dist/api/preprocessor.api.service.js +35 -0
  7. package/dist/api-exports.d.ts +26 -6
  8. package/dist/api-exports.js +42 -29
  9. package/dist/commands/chat.js +32 -5
  10. package/dist/commands/compile.js +16 -2
  11. package/dist/commands/dev.js +23 -2
  12. package/dist/commands/push.js +5 -3
  13. package/dist/commands/test.js +18 -2
  14. package/dist/common/job.instance.d.ts +3 -0
  15. package/dist/common/job.instance.js +8 -0
  16. package/dist/config/constants.d.ts +6 -5
  17. package/dist/config/constants.js +12 -10
  18. package/dist/interfaces/chat.d.ts +30 -1
  19. package/dist/interfaces/jobs.d.ts +21 -0
  20. package/dist/types/skill.d.ts +75 -56
  21. package/dist/types/skill.js +53 -59
  22. package/dist/utils/bundling.d.ts +13 -4
  23. package/dist/utils/bundling.js +83 -26
  24. package/dist/utils/compile.js +27 -6
  25. package/dist/utils/dev-api.d.ts +42 -2
  26. package/dist/utils/dev-api.js +177 -4
  27. package/dist/utils/dev-server.d.ts +1 -1
  28. package/dist/utils/dev-server.js +4 -4
  29. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  30. package/dist/utils/dynamic-job-bundler.js +143 -0
  31. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  32. package/dist/utils/pre-bundle-jobs.js +176 -0
  33. package/dist/utils/sandbox-storage.d.ts +48 -0
  34. package/dist/utils/sandbox-storage.js +114 -0
  35. package/dist/utils/sandbox.d.ts +2 -2
  36. package/dist/utils/sandbox.js +23 -7
  37. package/package.json +1 -1
  38. package/template/lua.skill.yaml +47 -0
  39. package/template/package-lock.json +10505 -0
  40. package/template/package.json +2 -1
  41. package/template/src/index.ts +65 -3
  42. package/template/src/tools/CreateInlineJob.ts +42 -0
  43. package/API_REFERENCE.md +0 -1408
  44. package/CHANGELOG.md +0 -236
  45. package/CLI_REFERENCE.md +0 -908
  46. package/GETTING_STARTED.md +0 -1040
  47. package/INSTANCE_TYPES.md +0 -1158
  48. package/README.md +0 -865
  49. package/TEMPLATE_GUIDE.md +0 -1398
  50. package/USER_DATA_INSTANCE.md +0 -621
  51. package/template/AGENT_CONFIGURATION.md +0 -251
  52. package/template/COMPLEX_JOB_EXAMPLES.md +0 -795
  53. package/template/DYNAMIC_JOB_CREATION.md +0 -371
  54. package/template/TOOL_EXAMPLES.md +0 -655
  55. package/template/WEBHOOKS_JOBS_QUICKSTART.md +0 -318
  56. package/template/WEBHOOK_JOB_EXAMPLES.md +0 -817
  57. package/template/src/index-agent-example.ts +0 -201
  58. package/template/src/postprocessors/ResponseFormatter.ts +0 -151
  59. package/template/src/preprocessors/MessageFilter.ts +0 -91
@@ -4,12 +4,14 @@
4
4
  */
5
5
  import ChatApi from '../api/chat.api.service.js';
6
6
  import SkillApi from '../api/skills.api.service.js';
7
+ import PreProcessorApi from '../api/preprocessor.api.service.js';
8
+ import PostProcessorApi from '../api/postprocessor.api.service.js';
7
9
  import { BASE_URLS } from '../config/constants.js';
8
10
  import { writeProgress, writeSuccess } from './cli.js';
9
11
  import { loadEnvironmentVariables } from './sandbox.js';
10
- import { getSandboxSkillId, setSandboxSkillId, getAllSandboxSkillIds } from './sandbox-storage.js';
12
+ import { getSandboxSkillId, setSandboxSkillId, getAllSandboxSkillIds, getSandboxPreProcessorId, setSandboxPreProcessorId, getSandboxPostProcessorId, setSandboxPostProcessorId } from './sandbox-storage.js';
11
13
  /**
12
- * Sends a chat message to the API with skill override for sandbox testing.
14
+ * Sends a chat message to the API with skill and processor overrides for sandbox testing.
13
15
  *
14
16
  * @param apiKey - User's API key
15
17
  * @param agentId - Agent ID
@@ -18,9 +20,10 @@ import { getSandboxSkillId, setSandboxSkillId, getAllSandboxSkillIds } from './s
18
20
  * @param message - Message to send
19
21
  * @param persona - Optional persona override
20
22
  * @param deployData - Deploy data containing all skills
23
+ * @param config - Optional config for processor overrides
21
24
  * @returns The chat response text or null if failed
22
25
  */
23
- export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona, deployData) {
26
+ export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona, deployData, config) {
24
27
  try {
25
28
  // Get all sandbox skill IDs for skill override
26
29
  const allSkillOverrides = await getAllSandboxSkillIds(deployData);
@@ -31,6 +34,10 @@ export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, messa
31
34
  sandboxId: sandboxId
32
35
  }
33
36
  ];
37
+ // Get sandbox processor overrides if config provided
38
+ const { getAllSandboxPreProcessorIds, getAllSandboxPostProcessorIds } = await import('./sandbox-storage.js');
39
+ const preprocessorOverride = config ? await getAllSandboxPreProcessorIds(config) : [];
40
+ const postprocessorOverride = config ? await getAllSandboxPostProcessorIds(config) : [];
34
41
  const chatRequest = {
35
42
  messages: [
36
43
  {
@@ -39,7 +46,9 @@ export async function sendChatMessage(apiKey, agentId, skillId, sandboxId, messa
39
46
  }
40
47
  ],
41
48
  navigate: true,
42
- skillOverride: skillOverride
49
+ skillOverride: skillOverride,
50
+ preprocessorOverride: preprocessorOverride,
51
+ postprocessorOverride: postprocessorOverride
43
52
  };
44
53
  // Add persona override if provided
45
54
  if (persona) {
@@ -260,3 +269,167 @@ export async function pushSkillsToSandbox(apiKey, agentId, deployData, isInitial
260
269
  }
261
270
  return sandboxIds;
262
271
  }
272
+ /**
273
+ * Pushes a single preprocessor to the sandbox.
274
+ * Attempts to update existing sandbox version, creates new one if update fails.
275
+ *
276
+ * @param apiKey - User's API key
277
+ * @param agentId - Agent ID
278
+ * @param preprocessorId - PreProcessor ID
279
+ * @param preprocessorData - PreProcessor data to push
280
+ * @param isInitial - Whether this is the initial push (affects logging)
281
+ * @returns True if successful, false otherwise
282
+ */
283
+ export async function pushSinglePreProcessorToSandbox(apiKey, agentId, preprocessorId, preprocessorData, isInitial = false) {
284
+ const preprocessorName = preprocessorData.name || 'Unknown PreProcessor';
285
+ const sandboxPreProcessorId = await getSandboxPreProcessorId(preprocessorName);
286
+ // Ensure version has -sandbox postfix
287
+ const baseVersion = preprocessorData.version || '1.0.0';
288
+ const sandboxVersion = baseVersion.endsWith('-sandbox') ? baseVersion : `${baseVersion}-sandbox`;
289
+ const payload = {
290
+ name: preprocessorName,
291
+ version: sandboxVersion,
292
+ description: preprocessorData.description,
293
+ context: preprocessorData.context,
294
+ code: preprocessorData.code,
295
+ executeFunction: preprocessorData.executeFunction,
296
+ async: Boolean(preprocessorData.async ?? false)
297
+ };
298
+ if (sandboxPreProcessorId) {
299
+ // Try to update existing sandbox version
300
+ if (!isInitial) {
301
+ writeProgress(`🔄 Updating existing preprocessor sandbox version...`);
302
+ }
303
+ const api = new PreProcessorApi(BASE_URLS.API, apiKey, agentId);
304
+ const updateResult = await api.updateDevPreProcessor(preprocessorId, sandboxPreProcessorId, payload);
305
+ if (updateResult.success && updateResult.data) {
306
+ return true;
307
+ }
308
+ else {
309
+ if (!isInitial) {
310
+ writeProgress("⚠️ Failed to update preprocessor sandbox, creating new one...");
311
+ }
312
+ }
313
+ }
314
+ // Create new sandbox version
315
+ if (!isInitial) {
316
+ writeProgress(`🔄 Creating new preprocessor sandbox version...`);
317
+ }
318
+ const api = new PreProcessorApi(BASE_URLS.API, apiKey, agentId);
319
+ const result = await api.pushDevPreProcessor(preprocessorId, payload);
320
+ if (result.success && result.data) {
321
+ // Store the new sandbox preprocessor ID
322
+ await setSandboxPreProcessorId(result.data.versionId, preprocessorName);
323
+ return true;
324
+ }
325
+ else if (result.error) {
326
+ console.error(`❌ PreProcessor sandbox push failed: ${result.error.message}`);
327
+ return false;
328
+ }
329
+ return false;
330
+ }
331
+ /**
332
+ * Pushes a single postprocessor to the sandbox.
333
+ * Attempts to update existing sandbox version, creates new one if update fails.
334
+ *
335
+ * @param apiKey - User's API key
336
+ * @param agentId - Agent ID
337
+ * @param postprocessorId - PostProcessor ID
338
+ * @param postprocessorData - PostProcessor data to push
339
+ * @param isInitial - Whether this is the initial push (affects logging)
340
+ * @returns True if successful, false otherwise
341
+ */
342
+ export async function pushSinglePostProcessorToSandbox(apiKey, agentId, postprocessorId, postprocessorData, isInitial = false) {
343
+ const postprocessorName = postprocessorData.name || 'Unknown PostProcessor';
344
+ const sandboxPostProcessorId = await getSandboxPostProcessorId(postprocessorName);
345
+ // Ensure version has -sandbox postfix
346
+ const baseVersion = postprocessorData.version || '1.0.0';
347
+ const sandboxVersion = baseVersion.endsWith('-sandbox') ? baseVersion : `${baseVersion}-sandbox`;
348
+ const payload = {
349
+ name: postprocessorName,
350
+ version: sandboxVersion,
351
+ description: postprocessorData.description,
352
+ context: postprocessorData.context,
353
+ code: postprocessorData.code,
354
+ executeFunction: postprocessorData.executeFunction,
355
+ async: Boolean(postprocessorData.async ?? false)
356
+ };
357
+ if (sandboxPostProcessorId) {
358
+ // Try to update existing sandbox version
359
+ if (!isInitial) {
360
+ writeProgress(`🔄 Updating existing postprocessor sandbox version...`);
361
+ }
362
+ const api = new PostProcessorApi(BASE_URLS.API, apiKey, agentId);
363
+ const updateResult = await api.updateDevPostProcessor(postprocessorId, sandboxPostProcessorId, payload);
364
+ if (updateResult.success && updateResult.data) {
365
+ return true;
366
+ }
367
+ else {
368
+ if (!isInitial) {
369
+ writeProgress("⚠️ Failed to update postprocessor sandbox, creating new one...");
370
+ }
371
+ }
372
+ }
373
+ // Create new sandbox version
374
+ if (!isInitial) {
375
+ writeProgress(`🔄 Creating new postprocessor sandbox version...`);
376
+ }
377
+ const api = new PostProcessorApi(BASE_URLS.API, apiKey, agentId);
378
+ const result = await api.pushDevPostProcessor(postprocessorId, payload);
379
+ if (result.success && result.data) {
380
+ // Store the new sandbox postprocessor ID
381
+ await setSandboxPostProcessorId(result.data.versionId, postprocessorName);
382
+ return true;
383
+ }
384
+ else if (result.error) {
385
+ console.error(`❌ PostProcessor sandbox push failed: ${result.error.message}`);
386
+ return false;
387
+ }
388
+ return false;
389
+ }
390
+ /**
391
+ * Pushes preprocessors and postprocessors to sandbox.
392
+ *
393
+ * @param apiKey - User's API key
394
+ * @param agentId - Agent ID
395
+ * @param config - Configuration with preprocessors and postprocessors
396
+ * @param bundledPreProcessors - Bundled preprocessor data from compilation
397
+ * @param bundledPostProcessors - Bundled postprocessor data from compilation
398
+ * @param isInitial - Whether this is the initial push
399
+ * @returns Object with counts of pushed processors
400
+ */
401
+ export async function pushProcessorsToSandbox(apiKey, agentId, config, bundledPreProcessors, bundledPostProcessors, isInitial = false) {
402
+ let preprocessorCount = 0;
403
+ let postprocessorCount = 0;
404
+ // Push preprocessors
405
+ const configPreProcessors = config.preprocessors || [];
406
+ for (const preprocessor of configPreProcessors) {
407
+ const bundled = bundledPreProcessors.find(p => p.name === preprocessor.name);
408
+ if (bundled && bundled.code && preprocessor.preprocessorId) {
409
+ try {
410
+ const success = await pushSinglePreProcessorToSandbox(apiKey, agentId, preprocessor.preprocessorId, bundled, isInitial);
411
+ if (success)
412
+ preprocessorCount++;
413
+ }
414
+ catch (error) {
415
+ console.error(`❌ Failed to push preprocessor ${preprocessor.name}:`, error);
416
+ }
417
+ }
418
+ }
419
+ // Push postprocessors
420
+ const configPostProcessors = config.postprocessors || [];
421
+ for (const postprocessor of configPostProcessors) {
422
+ const bundled = bundledPostProcessors.find(p => p.name === postprocessor.name);
423
+ if (bundled && bundled.code && postprocessor.postprocessorId) {
424
+ try {
425
+ const success = await pushSinglePostProcessorToSandbox(apiKey, agentId, postprocessor.postprocessorId, bundled, isInitial);
426
+ if (success)
427
+ postprocessorCount++;
428
+ }
429
+ catch (error) {
430
+ console.error(`❌ Failed to push postprocessor ${postprocessor.name}:`, error);
431
+ }
432
+ }
433
+ }
434
+ return { preprocessors: preprocessorCount, postprocessors: postprocessorCount };
435
+ }
@@ -21,4 +21,4 @@ export interface DevServer {
21
21
  * @param port - Port to run server on
22
22
  * @returns Server instance with WebSocket and broadcast function
23
23
  */
24
- export declare function createChatServer(apiKey: string, agentId: string, skillId: string, sandboxId: string, port: number): DevServer;
24
+ export declare function createChatServer(apiKey: string, agentId: string, skillId: string, sandboxId: string, port: number, config?: any): DevServer;
@@ -42,7 +42,7 @@ function readDeployJson() {
42
42
  * @param port - Port to run server on
43
43
  * @returns Server instance with WebSocket and broadcast function
44
44
  */
45
- export function createChatServer(apiKey, agentId, skillId, sandboxId, port) {
45
+ export function createChatServer(apiKey, agentId, skillId, sandboxId, port, config) {
46
46
  const deployData = readDeployJson();
47
47
  const server = createServer((req, res) => {
48
48
  // Enable CORS
@@ -56,7 +56,7 @@ export function createChatServer(apiKey, agentId, skillId, sandboxId, port) {
56
56
  }
57
57
  // Route handlers
58
58
  if (req.method === 'POST' && req.url === '/chat') {
59
- handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData);
59
+ handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData, config);
60
60
  return;
61
61
  }
62
62
  if (req.method === 'GET' && req.url === '/persona') {
@@ -173,7 +173,7 @@ export function createChatServer(apiKey, agentId, skillId, sandboxId, port) {
173
173
  /**
174
174
  * Handles POST /chat - Send chat messages
175
175
  */
176
- function handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData) {
176
+ function handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deployData, config) {
177
177
  let body = '';
178
178
  req.on('data', (chunk) => {
179
179
  body += chunk.toString();
@@ -181,7 +181,7 @@ function handleChatEndpoint(req, res, apiKey, agentId, skillId, sandboxId, deplo
181
181
  req.on('end', async () => {
182
182
  try {
183
183
  const { message, persona } = JSON.parse(body);
184
- const response = await sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona, deployData);
184
+ const response = await sendChatMessage(apiKey, agentId, skillId, sandboxId, message, persona, deployData, config);
185
185
  res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
186
186
  res.end(JSON.stringify({
187
187
  success: response !== null,
@@ -0,0 +1,17 @@
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;
@@ -0,0 +1,143 @@
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
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pre-Bundle Jobs - Extract and bundle Jobs.create() before tool bundling
3
+ *
4
+ * Flow:
5
+ * 1. Detect Jobs.create() in source
6
+ * 2. Extract execute function
7
+ * 3. Bundle execute with ALL dependencies
8
+ * 4. Replace execute with placeholder
9
+ * 5. Bundle tool/webhook/job
10
+ * 6. Replace placeholder with bundled job code
11
+ * 7. Compress
12
+ */
13
+ import { Project } from 'ts-morph';
14
+ /**
15
+ * Pre-processes source file to extract and bundle Jobs.create() execute functions
16
+ * Returns modified source code with placeholders
17
+ */
18
+ export declare function preBundleJobsInSource(sourceFilePath: string, project: Project, distDir: string): Promise<{
19
+ modifiedSource: string;
20
+ jobBundles: Map<string, string>;
21
+ }>;
22
+ /**
23
+ * Replaces placeholders in bundled code with actual job bundles
24
+ * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
25
+ */
26
+ export declare function replaceJobPlaceholders(bundledCode: string, jobBundles: Map<string, string>): string;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Pre-Bundle Jobs - Extract and bundle Jobs.create() before tool bundling
3
+ *
4
+ * Flow:
5
+ * 1. Detect Jobs.create() in source
6
+ * 2. Extract execute function
7
+ * 3. Bundle execute with ALL dependencies
8
+ * 4. Replace execute with placeholder
9
+ * 5. Bundle tool/webhook/job
10
+ * 6. Replace placeholder with bundled job code
11
+ * 7. Compress
12
+ */
13
+ import { Node } from 'ts-morph';
14
+ import { build } from 'esbuild';
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import { sandboxGlobalsPlugin } from './bundling.js';
18
+ const JOB_PLACEHOLDER = '__BUNDLED_JOB_EXECUTE__';
19
+ /**
20
+ * Pre-processes source file to extract and bundle Jobs.create() execute functions
21
+ * Returns modified source code with placeholders
22
+ */
23
+ export async function preBundleJobsInSource(sourceFilePath, project, distDir) {
24
+ const sourceFile = project.getSourceFile(sourceFilePath);
25
+ if (!sourceFile) {
26
+ return { modifiedSource: fs.readFileSync(sourceFilePath, 'utf8'), jobBundles: new Map() };
27
+ }
28
+ const jobBundles = new Map();
29
+ const bundlingPromises = [];
30
+ let jobIndex = 0;
31
+ let modifiedSource = sourceFile.getFullText();
32
+ // Find all Jobs.create() calls
33
+ sourceFile.forEachDescendant((node) => {
34
+ if (Node.isCallExpression(node)) {
35
+ const expression = node.getExpression();
36
+ // Check if this is Jobs.create()
37
+ if (Node.isPropertyAccessExpression(expression)) {
38
+ const object = expression.getExpression();
39
+ const property = expression.getName();
40
+ if (object.getText() === 'Jobs' && property === 'create') {
41
+ jobIndex++;
42
+ const placeholder = `${JOB_PLACEHOLDER}_${jobIndex}`;
43
+ console.log(`[PreBundleJobs] Found Jobs.create() #${jobIndex}`);
44
+ // Get the config object argument
45
+ const args = node.getArguments();
46
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
47
+ const configObj = args[0];
48
+ // Find the execute property
49
+ const executeProperty = configObj.getProperty('execute');
50
+ if (executeProperty && Node.isPropertyAssignment(executeProperty)) {
51
+ const executeInitializer = executeProperty.getInitializer();
52
+ if (executeInitializer) {
53
+ const executeCode = executeInitializer.getText();
54
+ console.log(`[PreBundleJobs] Extracting execute function for bundling...`);
55
+ // Bundle the execute function with dependencies (async)
56
+ const bundlePromise = bundleJobExecuteFunction(executeCode, `job_${jobIndex}`, distDir, sourceFilePath).then(bundledCode => {
57
+ if (bundledCode) {
58
+ jobBundles.set(placeholder, bundledCode);
59
+ console.log(`[PreBundleJobs] ✅ Bundled job #${jobIndex} with dependencies`);
60
+ }
61
+ });
62
+ bundlingPromises.push(bundlePromise);
63
+ // Replace execute function with a function that references the bundled code
64
+ // Don't use string placeholder - use a reference
65
+ const originalText = executeProperty.getText();
66
+ const replacementText = `execute: ${placeholder}`;
67
+ modifiedSource = modifiedSource.replace(originalText, replacementText);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ });
75
+ // Wait for all bundling to complete
76
+ if (bundlingPromises.length > 0) {
77
+ console.log(`[PreBundleJobs] Waiting for ${bundlingPromises.length} job(s) to bundle...`);
78
+ await Promise.all(bundlingPromises);
79
+ console.log(`[PreBundleJobs] ✅ All job bundles complete`);
80
+ }
81
+ return { modifiedSource, jobBundles };
82
+ }
83
+ /**
84
+ * Bundles a job execute function with all its dependencies
85
+ */
86
+ async function bundleJobExecuteFunction(executeCode, jobName, distDir, sourceFilePath) {
87
+ const tempDir = path.join(distDir, '.temp');
88
+ if (!fs.existsSync(tempDir)) {
89
+ fs.mkdirSync(tempDir, { recursive: true });
90
+ }
91
+ const tempOutput = path.join(tempDir, `${jobName}-bundled.js`);
92
+ try {
93
+ // Read the original source file to get its imports
94
+ const sourceContent = fs.readFileSync(sourceFilePath, 'utf8');
95
+ // Extract all import statements from the source
96
+ const importPattern = /^import\s+.+\s+from\s+['"][^'"]+['"];?$/gm;
97
+ const imports = sourceContent.match(importPattern) || [];
98
+ // Filter to only imports that might be used (exclude lua-cli internal classes and APIs)
99
+ const relevantImports = imports.filter(imp => !imp.includes('LuaTool') &&
100
+ !imp.includes('LuaWebhook') &&
101
+ !imp.includes('LuaJob') &&
102
+ !imp.includes('lua-cli') && // Exclude all lua-cli imports (they're available in sandbox)
103
+ !imp.includes('api-exports') && // Exclude api-exports
104
+ !imp.includes('../../../dist/api-exports') // Exclude direct api-exports imports
105
+ );
106
+ console.log(`[PreBundleJobs] Including ${relevantImports.length} import(s) in job bundle`);
107
+ // Create a TypeScript module with the execute function and all imports
108
+ const moduleCode = `
109
+ // Auto-generated job execute bundle
110
+ ${relevantImports.join('\n')}
111
+
112
+ // The execute function with all dependencies available
113
+ const executeFunc = ${executeCode};
114
+
115
+ export default executeFunc;
116
+ `;
117
+ // Bundle with esbuild using stdin to preserve import resolution
118
+ await build({
119
+ stdin: {
120
+ contents: moduleCode,
121
+ resolveDir: path.dirname(sourceFilePath),
122
+ sourcefile: sourceFilePath,
123
+ loader: 'ts',
124
+ },
125
+ bundle: true,
126
+ platform: 'node',
127
+ target: 'node16',
128
+ format: 'cjs',
129
+ minify: true,
130
+ outfile: tempOutput,
131
+ external: [],
132
+ plugins: [sandboxGlobalsPlugin],
133
+ });
134
+ // Read bundled code
135
+ const bundledCode = fs.readFileSync(tempOutput, 'utf8');
136
+ console.log(`[PreBundleJobs] Bundle size: ${(bundledCode.length / 1024).toFixed(2)}KB`);
137
+ // DON'T compress - just wrap for VM execution
138
+ // Compression happens later when the entire tool is bundled
139
+ const wrappedCode = `(async (job) => {
140
+ ${bundledCode}
141
+ const executeFunction = module.exports.default || module.exports;
142
+ return await executeFunction(job);
143
+ })`;
144
+ // Cleanup temp files
145
+ try {
146
+ fs.unlinkSync(tempOutput);
147
+ }
148
+ catch { }
149
+ // Return uncompressed bundled code
150
+ return wrappedCode;
151
+ }
152
+ catch (error) {
153
+ console.warn(`[PreBundleJobs] Error bundling job ${jobName}:`, error);
154
+ try {
155
+ if (fs.existsSync(tempOutput))
156
+ fs.unlinkSync(tempOutput);
157
+ }
158
+ catch { }
159
+ return '';
160
+ }
161
+ }
162
+ /**
163
+ * Replaces placeholders in bundled code with actual job bundles
164
+ * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
165
+ */
166
+ export function replaceJobPlaceholders(bundledCode, jobBundles) {
167
+ let result = bundledCode;
168
+ jobBundles.forEach((bundledJobCode, placeholder) => {
169
+ // The placeholder is used as an identifier (not a string), just replace it directly
170
+ // It appears as: execute: __BUNDLED_JOB_EXECUTE___1
171
+ const placeholderPattern = new RegExp(placeholder, 'g');
172
+ result = result.replace(placeholderPattern, bundledJobCode);
173
+ console.log(`[PreBundleJobs] ✅ Replaced ${placeholder} with bundled code (${(bundledJobCode.length / 1024).toFixed(1)}KB)`);
174
+ });
175
+ return result;
176
+ }
@@ -25,3 +25,51 @@ export declare function setSandboxSkillId(sandboxId: string, skillName?: string)
25
25
  * @returns Array of skill overrides with skillId and sandboxId pairs
26
26
  */
27
27
  export declare function getAllSandboxSkillIds(deployData: any): Promise<SkillOverride[]>;
28
+ /**
29
+ * Retrieves sandbox preprocessor ID from secure storage.
30
+ *
31
+ * @param preprocessorName - Preprocessor name
32
+ * @returns The sandbox preprocessor ID or null if not found
33
+ */
34
+ export declare function getSandboxPreProcessorId(preprocessorName: string): Promise<string | null>;
35
+ /**
36
+ * Stores sandbox preprocessor ID in secure storage.
37
+ *
38
+ * @param sandboxId - The sandbox preprocessor ID to store
39
+ * @param preprocessorName - Preprocessor name
40
+ */
41
+ export declare function setSandboxPreProcessorId(sandboxId: string, preprocessorName: string): Promise<void>;
42
+ /**
43
+ * Retrieves sandbox postprocessor ID from secure storage.
44
+ *
45
+ * @param postprocessorName - Postprocessor name
46
+ * @returns The sandbox postprocessor ID or null if not found
47
+ */
48
+ export declare function getSandboxPostProcessorId(postprocessorName: string): Promise<string | null>;
49
+ /**
50
+ * Stores sandbox postprocessor ID in secure storage.
51
+ *
52
+ * @param sandboxId - The sandbox postprocessor ID to store
53
+ * @param postprocessorName - Postprocessor name
54
+ */
55
+ export declare function setSandboxPostProcessorId(sandboxId: string, postprocessorName: string): Promise<void>;
56
+ /**
57
+ * Gets all sandbox preprocessor overrides for the current config.
58
+ *
59
+ * @param config - Configuration with preprocessors
60
+ * @returns Array of preprocessor overrides
61
+ */
62
+ export declare function getAllSandboxPreProcessorIds(config: any): Promise<Array<{
63
+ preprocessorId: string;
64
+ sandboxId: string;
65
+ }>>;
66
+ /**
67
+ * Gets all sandbox postprocessor overrides for the current config.
68
+ *
69
+ * @param config - Configuration with postprocessors
70
+ * @returns Array of postprocessor overrides
71
+ */
72
+ export declare function getAllSandboxPostProcessorIds(config: any): Promise<Array<{
73
+ postprocessorId: string;
74
+ sandboxId: string;
75
+ }>>;