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

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 (61) 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/cli/command-definitions.js +13 -6
  10. package/dist/commands/chat.js +32 -5
  11. package/dist/commands/compile.js +16 -2
  12. package/dist/commands/dev.js +23 -2
  13. package/dist/commands/push.d.ts +6 -2
  14. package/dist/commands/push.js +412 -6
  15. package/dist/commands/test.js +18 -2
  16. package/dist/common/job.instance.d.ts +3 -0
  17. package/dist/common/job.instance.js +8 -0
  18. package/dist/config/constants.d.ts +6 -5
  19. package/dist/config/constants.js +12 -10
  20. package/dist/interfaces/chat.d.ts +30 -1
  21. package/dist/interfaces/jobs.d.ts +21 -0
  22. package/dist/types/skill.d.ts +75 -56
  23. package/dist/types/skill.js +53 -59
  24. package/dist/utils/bundling.d.ts +13 -4
  25. package/dist/utils/bundling.js +83 -26
  26. package/dist/utils/compile.js +27 -6
  27. package/dist/utils/dev-api.d.ts +42 -2
  28. package/dist/utils/dev-api.js +177 -4
  29. package/dist/utils/dev-server.d.ts +1 -1
  30. package/dist/utils/dev-server.js +4 -4
  31. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  32. package/dist/utils/dynamic-job-bundler.js +143 -0
  33. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  34. package/dist/utils/pre-bundle-jobs.js +176 -0
  35. package/dist/utils/sandbox-storage.d.ts +48 -0
  36. package/dist/utils/sandbox-storage.js +114 -0
  37. package/dist/utils/sandbox.d.ts +2 -2
  38. package/dist/utils/sandbox.js +23 -7
  39. package/package.json +1 -1
  40. package/template/lua.skill.yaml +47 -0
  41. package/template/package-lock.json +10505 -0
  42. package/template/package.json +2 -1
  43. package/template/src/index.ts +65 -3
  44. package/template/src/tools/CreateInlineJob.ts +42 -0
  45. package/API_REFERENCE.md +0 -1408
  46. package/CHANGELOG.md +0 -236
  47. package/CLI_REFERENCE.md +0 -908
  48. package/GETTING_STARTED.md +0 -1040
  49. package/INSTANCE_TYPES.md +0 -1158
  50. package/README.md +0 -865
  51. package/TEMPLATE_GUIDE.md +0 -1398
  52. package/USER_DATA_INSTANCE.md +0 -621
  53. package/template/AGENT_CONFIGURATION.md +0 -251
  54. package/template/COMPLEX_JOB_EXAMPLES.md +0 -795
  55. package/template/DYNAMIC_JOB_CREATION.md +0 -371
  56. package/template/TOOL_EXAMPLES.md +0 -655
  57. package/template/WEBHOOKS_JOBS_QUICKSTART.md +0 -318
  58. package/template/WEBHOOK_JOB_EXAMPLES.md +0 -817
  59. package/template/src/index-agent-example.ts +0 -201
  60. package/template/src/postprocessors/ResponseFormatter.ts +0 -151
  61. package/template/src/preprocessors/MessageFilter.ts +0 -91
@@ -8,13 +8,34 @@ import { build } from "esbuild";
8
8
  import { Project, Node } from "ts-morph";
9
9
  import { writeProgress } from "./cli.js";
10
10
  import { COMPILE_DIRS, COMPILE_FILES, ESBUILD_TOOL_CONFIG, ESBUILD_INDEX_CONFIG, DEFAULT_INPUT_SCHEMA, } from '../config/compile.constants.js';
11
- import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema, } from './compile.js';
11
+ import { wrapToolForVM, createExecuteFunction, evaluateZodSchemaToJsonSchema } from './compile.js';
12
+ /**
13
+ * Helper function to remove lua-cli and api-exports imports from source code.
14
+ * Used when bundling via stdin or temp files where the plugin can't process them.
15
+ */
16
+ function stripLuaCliImports(sourceCode) {
17
+ return sourceCode
18
+ // Remove api-exports imports
19
+ .replace(/import\s+{([^}]+)}\s+from\s+["'][^"']*api-exports["'];?/g, '// api-exports imports removed - using sandbox globals')
20
+ // Remove lua-cli imports
21
+ .replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli["'];?/g, '// lua-cli imports removed - using sandbox globals')
22
+ // Remove lua-cli/skill imports (but keep LuaTool, etc. for type checking in temp files)
23
+ .replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli\/skill["'];?/g, (match, imports) => {
24
+ // Keep only LuaTool, LuaWebhook, LuaJob types for temp files
25
+ const typeImports = imports.split(',')
26
+ .map((imp) => imp.trim())
27
+ .filter((imp) => /^(LuaTool|LuaWebhook|LuaJob|LuaPreProcessor|LuaPostProcessor)$/.test(imp));
28
+ return typeImports.length > 0
29
+ ? `// lua-cli/skill imports removed - using sandbox globals (types kept for compilation)`
30
+ : '// lua-cli/skill imports removed - using sandbox globals';
31
+ });
32
+ }
12
33
  /**
13
34
  * esbuild plugin to inject sandbox globals instead of requiring lua-cli
14
35
  * This removes require("lua-cli") statements and injects the global API objects
15
36
  * that are available in the sandbox (Products, User, Data, Baskets, Orders, Webhooks, Jobs)
16
37
  */
17
- const sandboxGlobalsPlugin = {
38
+ export const sandboxGlobalsPlugin = {
18
39
  name: 'sandbox-globals',
19
40
  setup(build) {
20
41
  // Only process user files, not node_modules
@@ -24,12 +45,18 @@ const sandboxGlobalsPlugin = {
24
45
  return null;
25
46
  }
26
47
  const contents = await fs.promises.readFile(args.path, 'utf8');
27
- // Only transform files that import from lua-cli
28
- if (!contents.includes('lua-cli')) {
48
+ // Only transform files that import from lua-cli or api-exports
49
+ if (!contents.includes('lua-cli') && !contents.includes('api-exports')) {
29
50
  return null;
30
51
  }
31
52
  // Replace lua-cli imports with global references
32
53
  let transformedContents = contents;
54
+ // Replace named imports from api-exports (e.g., ../../../dist/api-exports)
55
+ // Match: import { Jobs, Products, etc. } from "../../../dist/api-exports"
56
+ transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["'][^"']*api-exports["'];?/g, (match, imports) => {
57
+ // Just remove the import, globals will be available in sandbox
58
+ return '// api-exports imports removed - using sandbox globals';
59
+ });
33
60
  // Replace named imports from lua-cli
34
61
  // Match: import { Products, User, Data, etc. } from "lua-cli"
35
62
  transformedContents = transformedContents.replace(/import\s+{([^}]+)}\s+from\s+["']lua-cli["'];?/g, (match, imports) => {
@@ -83,12 +110,13 @@ const sandboxGlobalsPlugin = {
83
110
  * @param tool - The tool to bundle
84
111
  * @param distDir - The distribution directory for output
85
112
  */
86
- export async function bundleTool(tool, distDir) {
113
+ export async function bundleTool(tool, distDir, modifiedSource) {
87
114
  writeProgress(`📦 Bundling ${tool.className}...`);
88
115
  try {
89
116
  const outputPath = path.join(distDir, COMPILE_DIRS.TOOLS, `${tool.className}.js`);
90
117
  let entryPoint = tool.filePath;
91
118
  let tempFile = null;
119
+ // Note: modifiedSource is handled via stdin below, no temp file needed
92
120
  // Check if tool is inline (in index.ts or src/index.ts)
93
121
  const isInlineInIndex = tool.filePath.endsWith('index.ts') || tool.filePath.endsWith('index.tsx');
94
122
  if (isInlineInIndex) {
@@ -121,12 +149,30 @@ export async function bundleTool(tool, distDir) {
121
149
  entryPoint = tempFile;
122
150
  }
123
151
  }
124
- await build({
125
- ...ESBUILD_TOOL_CONFIG,
126
- entryPoints: [entryPoint],
127
- outfile: outputPath,
128
- plugins: [sandboxGlobalsPlugin],
129
- });
152
+ // Use stdin for modified source to preserve import resolution
153
+ if (modifiedSource && !isInlineInIndex) {
154
+ // Remove api-exports and lua-cli imports (they're available in sandbox)
155
+ const transformedSource = stripLuaCliImports(modifiedSource);
156
+ await build({
157
+ ...ESBUILD_TOOL_CONFIG,
158
+ stdin: {
159
+ contents: transformedSource,
160
+ resolveDir: path.dirname(tool.filePath),
161
+ sourcefile: tool.filePath,
162
+ loader: 'ts',
163
+ },
164
+ outfile: outputPath,
165
+ plugins: [sandboxGlobalsPlugin],
166
+ });
167
+ }
168
+ else {
169
+ await build({
170
+ ...ESBUILD_TOOL_CONFIG,
171
+ entryPoints: [entryPoint],
172
+ outfile: outputPath,
173
+ plugins: [sandboxGlobalsPlugin],
174
+ });
175
+ }
130
176
  // Wrap the bundled code for VM execution environment
131
177
  await wrapToolForVM(outputPath, tool);
132
178
  // Clean up temp file if created
@@ -303,10 +349,10 @@ async function bundleAndCompressWebhookCode(executeFunction, webhookName, distDi
303
349
  const tempOutput = path.join(tempDir, `${webhookName}-webhook.js`);
304
350
  try {
305
351
  // Write execute function as a module export
306
- const moduleCode = `
352
+ const moduleCode = stripLuaCliImports(`
307
353
  // Webhook execute function for ${webhookName}
308
354
  export default ${executeFunction};
309
- `;
355
+ `);
310
356
  fs.writeFileSync(tempFile, moduleCode);
311
357
  // Bundle with esbuild
312
358
  await build({
@@ -323,8 +369,7 @@ export default ${executeFunction};
323
369
  // Read bundled code
324
370
  let bundledCode = fs.readFileSync(tempOutput, 'utf8');
325
371
  // Wrap for webhook VM execution (similar to tools)
326
- const wrappedCode = `(async (input) => {
327
- const { query, headers, body } = input;
372
+ const wrappedCode = `(async (query, headers, body) => {
328
373
 
329
374
  // Execute the bundled webhook code
330
375
  ${bundledCode}
@@ -332,8 +377,8 @@ export default ${executeFunction};
332
377
  // Get the execute function from exports
333
378
  const executeFunction = module.exports.default || module.exports;
334
379
 
335
- // Execute with proper input structure
336
- return await executeFunction({ query, headers, body });
380
+ // Execute with three separate parameters (not an object)
381
+ return await executeFunction(query, headers, body);
337
382
  })`;
338
383
  // Compress the wrapped code
339
384
  const compressed = compressCode(wrappedCode);
@@ -472,10 +517,10 @@ async function bundleAndCompressJobCode(executeFunction, jobName, distDir) {
472
517
  const tempOutput = path.join(tempDir, `${jobName}-job.js`);
473
518
  try {
474
519
  // Write execute function as a module export
475
- const moduleCode = `
520
+ const moduleCode = stripLuaCliImports(`
476
521
  // Job execute function for ${jobName}
477
522
  export default ${executeFunction};
478
- `;
523
+ `);
479
524
  fs.writeFileSync(tempFile, moduleCode);
480
525
  // Bundle with esbuild
481
526
  await build({
@@ -597,7 +642,11 @@ export async function bundlePreProcessor(preprocessor, indexFile, distDir, proje
597
642
  compressedCode = await bundleAndCompressProcessorCode(executeFunction, preprocessor.name, 'pre', distDir);
598
643
  }
599
644
  return {
600
- ...preprocessor,
645
+ name: preprocessor.name,
646
+ version: preprocessor.version,
647
+ description: preprocessor.description,
648
+ context: preprocessor.context,
649
+ async: preprocessor.async === true ? true : false, // Explicitly set boolean
601
650
  executeFunction,
602
651
  code: compressedCode
603
652
  };
@@ -675,7 +724,11 @@ export async function bundlePostProcessor(postprocessor, indexFile, distDir, pro
675
724
  compressedCode = await bundleAndCompressProcessorCode(executeFunction, postprocessor.name, 'post', distDir);
676
725
  }
677
726
  return {
678
- ...postprocessor,
727
+ name: postprocessor.name,
728
+ version: postprocessor.version,
729
+ description: postprocessor.description,
730
+ context: postprocessor.context,
731
+ async: postprocessor.async === true ? true : false, // Explicitly set boolean
679
732
  executeFunction,
680
733
  code: compressedCode
681
734
  };
@@ -699,10 +752,10 @@ async function bundleAndCompressProcessorCode(executeFunction, processorName, ty
699
752
  try {
700
753
  // Processor execute functions receive: (user, message, [response], channel)
701
754
  const paramList = type === 'pre' ? 'user, message, channel' : 'user, message, response, channel';
702
- const moduleCode = `
755
+ const moduleCode = stripLuaCliImports(`
703
756
  // ${type === 'pre' ? 'Pre' : 'Post'}Processor execute function for ${processorName}
704
757
  export default ${executeFunction};
705
- `;
758
+ `);
706
759
  fs.writeFileSync(tempFile, moduleCode);
707
760
  await build({
708
761
  entryPoints: [tempFile],
@@ -751,13 +804,15 @@ export default ${executeFunction};
751
804
  * Extracts execute code and input schema from a tool.
752
805
  * This function:
753
806
  * 1. Reads the bundled tool code
754
- * 2. Extracts the execute function
755
- * 3. Converts Zod schema to JSON Schema format
807
+ * 2. Detects and bundles any Jobs.create() calls within execute
808
+ * 3. Extracts the execute function
809
+ * 4. Converts Zod schema to JSON Schema format
756
810
  *
757
811
  * @param tool - The tool to extract metadata from (mutated with executeCode and inputSchema)
758
812
  * @param project - The ts-morph Project instance for AST analysis
813
+ * @param distDir - Distribution directory for bundling nested jobs
759
814
  */
760
- export async function extractExecuteCode(tool, project) {
815
+ export async function extractExecuteCode(tool, project, distDir) {
761
816
  try {
762
817
  const toolSourceFile = project.getSourceFile(tool.filePath);
763
818
  if (!toolSourceFile) {
@@ -793,6 +848,8 @@ export async function extractExecuteCode(tool, project) {
793
848
  }
794
849
  // Extract execute code from bundled file
795
850
  tool.executeCode = extractExecuteCodeFromBundledFile(tool);
851
+ // NOTE: Dynamic job bundling moved to source-level processing
852
+ // Attempting to extract from minified bundled code is too fragile
796
853
  // Extract and convert input schema
797
854
  tool.inputSchema = await extractInputSchema(classDecl, tool);
798
855
  }
@@ -423,6 +423,7 @@ export function extractPreProcessorsMetadata(indexFile) {
423
423
  let version = '';
424
424
  let description = '';
425
425
  let context = '';
426
+ let asyncMode = false;
426
427
  configObj.getProperties().forEach((prop) => {
427
428
  if (Node.isPropertyAssignment(prop)) {
428
429
  const propName = prop.getName();
@@ -439,6 +440,11 @@ export function extractPreProcessorsMetadata(indexFile) {
439
440
  else if (propName === 'context' && value) {
440
441
  context = value.getText().replace(/['"]/g, '');
441
442
  }
443
+ else if (propName === 'async' && value) {
444
+ // Properly extract boolean value by checking node kind
445
+ const nodeKind = value.getKind();
446
+ asyncMode = nodeKind === 110; // TrueKeyword = 110, FalseKeyword = 95
447
+ }
442
448
  }
443
449
  });
444
450
  if (name) {
@@ -446,7 +452,8 @@ export function extractPreProcessorsMetadata(indexFile) {
446
452
  name,
447
453
  version: version || '1.0.0',
448
454
  description,
449
- context
455
+ context,
456
+ async: asyncMode
450
457
  });
451
458
  }
452
459
  }
@@ -472,6 +479,7 @@ export function extractPostProcessorsMetadata(indexFile) {
472
479
  let version = '';
473
480
  let description = '';
474
481
  let context = '';
482
+ let asyncMode = false;
475
483
  configObj.getProperties().forEach((prop) => {
476
484
  if (Node.isPropertyAssignment(prop)) {
477
485
  const propName = prop.getName();
@@ -488,6 +496,11 @@ export function extractPostProcessorsMetadata(indexFile) {
488
496
  else if (propName === 'context' && value) {
489
497
  context = value.getText().replace(/['"]/g, '');
490
498
  }
499
+ else if (propName === 'async' && value) {
500
+ // Properly extract boolean value by checking node kind
501
+ const nodeKind = value.getKind();
502
+ asyncMode = nodeKind === 110; // TrueKeyword = 110, FalseKeyword = 95
503
+ }
491
504
  }
492
505
  });
493
506
  if (name) {
@@ -495,7 +508,8 @@ export function extractPostProcessorsMetadata(indexFile) {
495
508
  name,
496
509
  version: version || '1.0.0',
497
510
  description,
498
- context
511
+ context,
512
+ async: asyncMode
499
513
  });
500
514
  }
501
515
  }
@@ -674,10 +688,12 @@ function extractValueFromNode(node) {
674
688
  if (Node.isNumericLiteral(node)) {
675
689
  return node.getLiteralValue();
676
690
  }
677
- // Boolean literal
678
- if (node.getKind() === 110 || node.getKind() === 95) { // TrueKeyword or FalseKeyword
679
- return node.getText() === 'true';
680
- }
691
+ // Boolean literal (TrueKeyword = 110, FalseKeyword = 95)
692
+ const nodeKind = node.getKind();
693
+ if (nodeKind === 110)
694
+ return true; // TrueKeyword
695
+ if (nodeKind === 95)
696
+ return false; // FalseKeyword
681
697
  // New expression (for tools: [new ToolClass()])
682
698
  // Extract just the class name for compatibility with buildSkillsArray
683
699
  if (Node.isNewExpression(node)) {
@@ -699,6 +715,11 @@ function extractValueFromNode(node) {
699
715
  // Template literal or other complex expressions
700
716
  // For persona, context, etc. that might be multiline
701
717
  const text = node.getText();
718
+ // Handle boolean strings (fallback if node kind check didn't catch it)
719
+ if (text === 'true')
720
+ return true;
721
+ if (text === 'false')
722
+ return false;
702
723
  // If it starts with a quote, remove quotes
703
724
  if ((text.startsWith('"') && text.endsWith('"')) ||
704
725
  (text.startsWith("'") && text.endsWith("'")) ||
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { DevVersionResponse, UpdateDevVersionResponse, SandboxIdsMap } from '../interfaces/dev.js';
6
6
  /**
7
- * Sends a chat message to the API with skill override for sandbox testing.
7
+ * Sends a chat message to the API with skill and processor overrides for sandbox testing.
8
8
  *
9
9
  * @param apiKey - User's API key
10
10
  * @param agentId - Agent ID
@@ -13,9 +13,10 @@ import { DevVersionResponse, UpdateDevVersionResponse, SandboxIdsMap } from '../
13
13
  * @param message - Message to send
14
14
  * @param persona - Optional persona override
15
15
  * @param deployData - Deploy data containing all skills
16
+ * @param config - Optional config for processor overrides
16
17
  * @returns The chat response text or null if failed
17
18
  */
18
- export declare function sendChatMessage(apiKey: string, agentId: string, skillId: string, sandboxId: string, message: string, persona: string | undefined, deployData: any): Promise<string | null>;
19
+ export declare function sendChatMessage(apiKey: string, agentId: string, skillId: string, sandboxId: string, message: string, persona: string | undefined, deployData: any, config?: any): Promise<string | null>;
19
20
  /**
20
21
  * Updates an existing sandbox version.
21
22
  *
@@ -59,3 +60,42 @@ export declare function pushSingleSkillToSandbox(apiKey: string, agentId: string
59
60
  * @returns Map of skill names to their sandbox IDs
60
61
  */
61
62
  export declare function pushSkillsToSandbox(apiKey: string, agentId: string, deployData: any, isInitial?: boolean): Promise<SandboxIdsMap>;
63
+ /**
64
+ * Pushes a single preprocessor to the sandbox.
65
+ * Attempts to update existing sandbox version, creates new one if update fails.
66
+ *
67
+ * @param apiKey - User's API key
68
+ * @param agentId - Agent ID
69
+ * @param preprocessorId - PreProcessor ID
70
+ * @param preprocessorData - PreProcessor data to push
71
+ * @param isInitial - Whether this is the initial push (affects logging)
72
+ * @returns True if successful, false otherwise
73
+ */
74
+ export declare function pushSinglePreProcessorToSandbox(apiKey: string, agentId: string, preprocessorId: string, preprocessorData: any, isInitial?: boolean): Promise<boolean>;
75
+ /**
76
+ * Pushes a single postprocessor to the sandbox.
77
+ * Attempts to update existing sandbox version, creates new one if update fails.
78
+ *
79
+ * @param apiKey - User's API key
80
+ * @param agentId - Agent ID
81
+ * @param postprocessorId - PostProcessor ID
82
+ * @param postprocessorData - PostProcessor data to push
83
+ * @param isInitial - Whether this is the initial push (affects logging)
84
+ * @returns True if successful, false otherwise
85
+ */
86
+ export declare function pushSinglePostProcessorToSandbox(apiKey: string, agentId: string, postprocessorId: string, postprocessorData: any, isInitial?: boolean): Promise<boolean>;
87
+ /**
88
+ * Pushes preprocessors and postprocessors to sandbox.
89
+ *
90
+ * @param apiKey - User's API key
91
+ * @param agentId - Agent ID
92
+ * @param config - Configuration with preprocessors and postprocessors
93
+ * @param bundledPreProcessors - Bundled preprocessor data from compilation
94
+ * @param bundledPostProcessors - Bundled postprocessor data from compilation
95
+ * @param isInitial - Whether this is the initial push
96
+ * @returns Object with counts of pushed processors
97
+ */
98
+ export declare function pushProcessorsToSandbox(apiKey: string, agentId: string, config: any, bundledPreProcessors: any[], bundledPostProcessors: any[], isInitial?: boolean): Promise<{
99
+ preprocessors: number;
100
+ postprocessors: number;
101
+ }>;
@@ -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;