lua-cli 2.5.8 → 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 (115) hide show
  1. package/dist/api/job.api.service.d.ts +219 -0
  2. package/dist/api/job.api.service.js +216 -0
  3. package/dist/api/lazy-instances.d.ts +24 -0
  4. package/dist/api/lazy-instances.js +48 -0
  5. package/dist/api/postprocessor.api.service.d.ts +158 -0
  6. package/dist/api/postprocessor.api.service.js +111 -0
  7. package/dist/api/preprocessor.api.service.d.ts +158 -0
  8. package/dist/api/preprocessor.api.service.js +111 -0
  9. package/dist/api/user.data.api.service.d.ts +13 -0
  10. package/dist/api/user.data.api.service.js +20 -0
  11. package/dist/api/webhook.api.service.d.ts +151 -0
  12. package/dist/api/webhook.api.service.js +134 -0
  13. package/dist/api-exports.d.ts +176 -41
  14. package/dist/api-exports.js +195 -21
  15. package/dist/cli/command-definitions.js +75 -5
  16. package/dist/commands/chat.js +32 -5
  17. package/dist/commands/compile.js +140 -7
  18. package/dist/commands/dev.js +23 -2
  19. package/dist/commands/index.d.ts +4 -0
  20. package/dist/commands/index.js +4 -0
  21. package/dist/commands/init.js +53 -7
  22. package/dist/commands/jobs.d.ts +20 -0
  23. package/dist/commands/jobs.js +533 -0
  24. package/dist/commands/logs.js +2 -5
  25. package/dist/commands/postprocessors.d.ts +8 -0
  26. package/dist/commands/postprocessors.js +431 -0
  27. package/dist/commands/preprocessors.d.ts +8 -0
  28. package/dist/commands/preprocessors.js +431 -0
  29. package/dist/commands/push.js +686 -5
  30. package/dist/commands/test.d.ts +9 -18
  31. package/dist/commands/test.js +574 -82
  32. package/dist/commands/webhooks.d.ts +18 -0
  33. package/dist/commands/webhooks.js +424 -0
  34. package/dist/common/job.instance.d.ts +80 -0
  35. package/dist/common/job.instance.js +116 -0
  36. package/dist/common/user.instance.d.ts +1 -0
  37. package/dist/common/user.instance.js +9 -0
  38. package/dist/config/constants.d.ts +4 -3
  39. package/dist/config/constants.js +10 -8
  40. package/dist/interfaces/agent.d.ts +2 -1
  41. package/dist/interfaces/chat.d.ts +52 -1
  42. package/dist/interfaces/index.d.ts +10 -0
  43. package/dist/interfaces/index.js +7 -0
  44. package/dist/interfaces/jobs.d.ts +193 -0
  45. package/dist/interfaces/jobs.js +5 -0
  46. package/dist/interfaces/postprocessors.d.ts +35 -0
  47. package/dist/interfaces/postprocessors.js +4 -0
  48. package/dist/interfaces/preprocessors.d.ts +35 -0
  49. package/dist/interfaces/preprocessors.js +4 -0
  50. package/dist/interfaces/webhooks.d.ts +104 -0
  51. package/dist/interfaces/webhooks.js +5 -0
  52. package/dist/types/api-contracts.d.ts +5 -0
  53. package/dist/types/compile.types.d.ts +49 -0
  54. package/dist/types/index.d.ts +1 -1
  55. package/dist/types/index.js +1 -1
  56. package/dist/types/skill.d.ts +521 -0
  57. package/dist/types/skill.js +471 -0
  58. package/dist/utils/agent-management.d.ts +25 -0
  59. package/dist/utils/agent-management.js +67 -0
  60. package/dist/utils/bundling.d.ts +44 -5
  61. package/dist/utils/bundling.js +723 -23
  62. package/dist/utils/compile.d.ts +63 -0
  63. package/dist/utils/compile.js +712 -36
  64. package/dist/utils/deployment.d.ts +2 -1
  65. package/dist/utils/deployment.js +16 -2
  66. package/dist/utils/dev-api.d.ts +42 -2
  67. package/dist/utils/dev-api.js +177 -4
  68. package/dist/utils/dev-server.d.ts +1 -1
  69. package/dist/utils/dev-server.js +4 -4
  70. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  71. package/dist/utils/dynamic-job-bundler.js +143 -0
  72. package/dist/utils/init-agent.d.ts +3 -1
  73. package/dist/utils/init-agent.js +6 -4
  74. package/dist/utils/init-prompts.d.ts +2 -1
  75. package/dist/utils/init-prompts.js +14 -9
  76. package/dist/utils/job-management.d.ts +24 -0
  77. package/dist/utils/job-management.js +264 -0
  78. package/dist/utils/postprocessor-management.d.ts +9 -0
  79. package/dist/utils/postprocessor-management.js +118 -0
  80. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  81. package/dist/utils/pre-bundle-jobs.js +176 -0
  82. package/dist/utils/preprocessor-management.d.ts +9 -0
  83. package/dist/utils/preprocessor-management.js +118 -0
  84. package/dist/utils/sandbox-storage.d.ts +48 -0
  85. package/dist/utils/sandbox-storage.js +114 -0
  86. package/dist/utils/sandbox.d.ts +61 -1
  87. package/dist/utils/sandbox.js +299 -72
  88. package/dist/utils/tool-detection.d.ts +3 -2
  89. package/dist/utils/tool-detection.js +18 -4
  90. package/dist/utils/webhook-management.d.ts +24 -0
  91. package/dist/utils/webhook-management.js +256 -0
  92. package/package.json +1 -1
  93. package/template/README.md +30 -2
  94. package/template/lua.skill.yaml +47 -0
  95. package/template/package-lock.json +10505 -0
  96. package/template/package.json +2 -1
  97. package/template/src/index.ts +103 -2
  98. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  99. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  100. package/template/src/jobs/DataMigrationJob.ts +133 -0
  101. package/template/src/jobs/HealthCheckJob.ts +87 -0
  102. package/template/src/tools/CreateInlineJob.ts +42 -0
  103. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  104. package/template/src/tools/SmartBasketTool.ts +188 -0
  105. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  106. package/template/src/webhooks/UserEventWebhook.ts +77 -0
  107. package/API_REFERENCE.md +0 -1408
  108. package/CHANGELOG.md +0 -236
  109. package/CLI_REFERENCE.md +0 -908
  110. package/GETTING_STARTED.md +0 -1040
  111. package/INSTANCE_TYPES.md +0 -1158
  112. package/README.md +0 -865
  113. package/TEMPLATE_GUIDE.md +0 -1398
  114. package/USER_DATA_INSTANCE.md +0 -621
  115. package/template/TOOL_EXAMPLES.md +0 -655
@@ -26,17 +26,11 @@
26
26
  * const products = await Products.search('laptop');
27
27
  * ```
28
28
  */
29
- import { LuaSkill, env } from "./types/skill.js";
29
+ import { LuaSkill, LuaWebhook, LuaJob, PreProcessor, PostProcessor, LuaAgent, env } from "./types/skill.js";
30
30
  import { BasketStatus } from "./interfaces/baskets.js";
31
31
  import { OrderStatus } from "./interfaces/orders.js";
32
- import { getUserInstance, getDataInstance, getProductsInstance, getBasketsInstance, getOrderInstance, } from "./api/lazy-instances.js";
33
- // ============================================================================
34
- // USER DATA API
35
- // ============================================================================
36
- /**
37
- * User Data API
38
- * Access user information and preferences
39
- */
32
+ import { getUserInstance, getDataInstance, getProductsInstance, getBasketsInstance, getOrderInstance, getJobInstance, getChatInstance, } from "./api/lazy-instances.js";
33
+ import { JobInstance } from "./common/job.instance.js";
40
34
  export const User = {
41
35
  /**
42
36
  * Retrieves current user data.
@@ -46,6 +40,24 @@ export const User = {
46
40
  async get() {
47
41
  const instance = await getUserInstance();
48
42
  return instance.get();
43
+ },
44
+ /**
45
+ * Gets the chat history for the current user.
46
+ *
47
+ * @returns Promise resolving to array of chat messages
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const history = await User.getChatHistory();
52
+ * // Returns: [
53
+ * // { role: 'user', content: [{type: 'text', text: 'hello'}], createdAt: '...', id: '...', threadId: '...' },
54
+ * // { role: 'assistant', content: [{type: 'text', text: 'hi'}], createdAt: '...', id: '...', threadId: '...' }
55
+ * // ]
56
+ * ```
57
+ */
58
+ async getChatHistory() {
59
+ const instance = await getUserInstance();
60
+ return instance.getChatHistory();
49
61
  }
50
62
  };
51
63
  // ============================================================================
@@ -158,17 +170,6 @@ export const Products = {
158
170
  const instance = await getProductsInstance();
159
171
  return instance.create(product);
160
172
  },
161
- /**
162
- * Updates an existing product.
163
- *
164
- * @param data - Updated product data
165
- * @param id - Product ID
166
- * @returns Promise resolving to updated product
167
- */
168
- async update(data, id) {
169
- const instance = await getProductsInstance();
170
- return instance.update(data, id);
171
- },
172
173
  /**
173
174
  * Deletes a product.
174
175
  *
@@ -366,7 +367,180 @@ export const Orders = {
366
367
  }
367
368
  };
368
369
  // ============================================================================
370
+ // JOBS API
371
+ // ============================================================================
372
+ /**
373
+ * Jobs API
374
+ * Manage and trigger scheduled jobs from within your tools
375
+ */
376
+ export const Jobs = {
377
+ /**
378
+ * Creates a new job dynamically from within a tool.
379
+ * This allows tools to schedule one-time or recurring jobs programmatically.
380
+ *
381
+ * **What this does:**
382
+ * The server handles everything in ONE API call:
383
+ * 1. Creates the job
384
+ * 2. Creates version 1.0.0 with your execute function
385
+ * 3. Optionally activates the job (if activate: true)
386
+ * 4. Returns a JobInstance for manipulation
387
+ *
388
+ * @param config - Job configuration
389
+ * @param config.name - Unique job name
390
+ * @param config.description - Job description
391
+ * @param config.schedule - Schedule configuration (cron, once, or interval)
392
+ * @param config.execute - Async function to execute
393
+ * @param config.timeout - Optional timeout in seconds
394
+ * @param config.retry - Optional retry configuration
395
+ * @param config.metadata - Optional metadata
396
+ * @returns Promise resolving to JobInstance (already created, versioned, and activated)
397
+ *
398
+ * @example
399
+ * ```typescript
400
+ * // Create a one-time job to check basket in 3 hours
401
+ * const job = await Jobs.create({
402
+ * name: `check-basket-${basketId}`,
403
+ * description: 'Check if basket was abandoned',
404
+ * schedule: {
405
+ * type: 'once',
406
+ * executeAt: new Date(Date.now() + 3 * 60 * 60 * 1000)
407
+ * },
408
+ * metadata: {
409
+ * basketId: basketId,
410
+ * checkType: 'abandoned-cart'
411
+ * },
412
+ * execute: async (job, user) => {
413
+ * // Access user context and metadata
414
+ * console.log('Checking basket for user:', user.name);
415
+ * console.log('Basket ID from metadata:', metadata?.basketId);
416
+ *
417
+ * const basket = await Baskets.getById(metadata.basketId);
418
+ * if (basket.status === 'active') {
419
+ * // Send reminder
420
+ * }
421
+ * return { checked: true };
422
+ * }
423
+ * });
424
+ * // Job is now created, versioned as 1.0.0, and activated!
425
+ * console.log(`Job ${job.jobId} is active: ${job.active}`);
426
+ * ```
427
+ */
428
+ async create(config) {
429
+ const instance = await getJobInstance();
430
+ // Convert the execute function to a string
431
+ const executeString = config.execute.toString();
432
+ console.log('Creating Job');
433
+ // Create the job with initial version and activation in one call
434
+ const createResult = await instance.createJobInstance({
435
+ dynamic: true,
436
+ name: config.name,
437
+ description: config.description,
438
+ context: config.description || '',
439
+ schedule: config.schedule,
440
+ timeout: config.timeout,
441
+ retry: config.retry,
442
+ metadata: config.metadata,
443
+ // Include initial version data for automatic version creation
444
+ version: {
445
+ version: '1.0.0',
446
+ description: config.description,
447
+ context: config.description || '',
448
+ code: '', // Will be set by server
449
+ executeFunction: executeString,
450
+ timeout: config.timeout,
451
+ retry: config.retry,
452
+ metadata: config.metadata
453
+ },
454
+ // Activate immediately
455
+ activate: config.activate ?? true
456
+ });
457
+ const jobId = createResult.jobId;
458
+ // Server has created the job, version, and activated it
459
+ // Return a JobInstance for easy manipulation
460
+ return new JobInstance(instance, {
461
+ id: jobId,
462
+ jobId: jobId,
463
+ name: config.name,
464
+ schedule: config.schedule,
465
+ metadata: config.metadata || {},
466
+ active: config.activate ?? true
467
+ });
468
+ },
469
+ /**
470
+ * Retrieves a job by its unique identifier
471
+ * @param jobId - The unique identifier of the job to retrieve
472
+ * @returns Promise resolving to an JobInstance representing the job
473
+ * @throws Error if the job is not found or the request fails
474
+ */
475
+ async getJob(jobId) {
476
+ const instance = await getJobInstance();
477
+ return instance.getJob(jobId);
478
+ }
479
+ };
480
+ // ============================================================================
481
+ // AI GENERATION API
482
+ // ============================================================================
483
+ /**
484
+ * AI API
485
+ * Generate AI responses with custom context
486
+ */
487
+ export const AI = {
488
+ /**
489
+ * Generates an AI response with custom runtime context.
490
+ *
491
+ * @param context - Runtime context to provide to the AI
492
+ * @param messages - Array of messages (text, image, file, etc.)
493
+ * @param agentId - Optional agent ID to use (defaults to current agent)
494
+ * @returns Promise resolving to AI response text
495
+ *
496
+ * @example
497
+ * ```typescript
498
+ * // Simple text message with current agent
499
+ * const response = await AI.generate(
500
+ * 'You are a helpful sales assistant for our e-commerce store.',
501
+ * [{ type: 'text', text: 'What products do you recommend for beginners?' }]
502
+ * );
503
+ * console.log(response); // AI-generated recommendation
504
+ *
505
+ * // Multiple messages with images
506
+ * const response2 = await AI.generate(
507
+ * 'You are an image analysis expert.',
508
+ * [
509
+ * { type: 'text', text: 'What do you see in this image?' },
510
+ * { type: 'image', url: 'https://example.com/image.jpg' }
511
+ * ]
512
+ * );
513
+ *
514
+ * // Use a different agent
515
+ * const response3 = await AI.generate(
516
+ * 'You are a specialized assistant.',
517
+ * [{ type: 'text', text: 'Help me with this task' }],
518
+ * 'agent_different_123'
519
+ * );
520
+ * ```
521
+ */
522
+ async generate(context, messages, agentId) {
523
+ const chatInstance = await getChatInstance();
524
+ const { getCredentials } = await import('./api/credentials.js');
525
+ const credentials = await getCredentials();
526
+ // Use provided agentId or default to current agent
527
+ const targetAgentId = agentId || 'luaAgent';
528
+ const result = await chatInstance.sendMessage(targetAgentId, {
529
+ messages,
530
+ navigate: false,
531
+ skillOverride: [],
532
+ runtimeContext: context
533
+ });
534
+ if (!result.success) {
535
+ throw new Error(result.error?.message || 'AI generation failed');
536
+ }
537
+ return result.data?.text || '';
538
+ }
539
+ };
540
+ // ============================================================================
369
541
  // EXPORTS
370
542
  // ============================================================================
371
543
  // Export skill classes and utilities
372
- export { LuaSkill, BasketStatus, OrderStatus, env };
544
+ export { LuaSkill, LuaWebhook, LuaJob, PreProcessor, PostProcessor, LuaAgent, BasketStatus, OrderStatus, env };
545
+ // Export instance classes
546
+ export { JobInstance };
@@ -2,7 +2,7 @@
2
2
  * Command Definitions
3
3
  * Centralized command structure for the CLI
4
4
  */
5
- import { configureCommand, initCommand, destroyCommand, apiKeyCommand, compileCommand, testCommand, pushCommand, deployCommand, chatCommand, chatClearCommand, envCommand, personaCommand, productionCommand, resourcesCommand, adminCommand, docsCommand, channelsCommand, logsCommand, completionCommand, skillsCommand, featuresCommand } from "../commands/index.js";
5
+ import { configureCommand, initCommand, destroyCommand, apiKeyCommand, compileCommand, testCommand, pushCommand, deployCommand, chatCommand, chatClearCommand, envCommand, personaCommand, productionCommand, resourcesCommand, adminCommand, docsCommand, channelsCommand, logsCommand, completionCommand, skillsCommand, webhooksCommand, jobsCommand, featuresCommand, preprocessorsCommand, postprocessorsCommand } from "../commands/index.js";
6
6
  /**
7
7
  * Sets up authentication-related commands.
8
8
  *
@@ -53,20 +53,32 @@ export function setupSkillCommands(program) {
53
53
  .description("📦 Compile skill to deployable format")
54
54
  .action(compileCommand);
55
55
  program
56
- .command("test")
57
- .description("🧪 Test skill tools interactively")
56
+ .command("test [type]")
57
+ .description("🧪 Test skills, webhooks, or jobs interactively")
58
+ .addHelpText('after', `
59
+ Arguments:
60
+ type Optional: 'skill', 'webhook', or 'job' (prompts if not provided)
61
+
62
+ Examples:
63
+ $ lua test Interactive selection
64
+ $ lua test skill Test a skill/tool directly
65
+ $ lua test webhook Test a webhook with query/headers/body
66
+ $ lua test job Test a job by executing it
67
+ `)
58
68
  .action(testCommand);
59
69
  // Deployment Commands
60
70
  program
61
71
  .command("push [type]")
62
- .description("☁️ Push skill or persona version to server")
72
+ .description("☁️ Push skill, webhook, job, or persona version to server")
63
73
  .addHelpText('after', `
64
74
  Arguments:
65
- type Optional: 'skill' or 'persona' (prompts if not provided)
75
+ type Optional: 'skill', 'webhook', 'job', or 'persona' (prompts if not provided)
66
76
 
67
77
  Examples:
68
78
  $ lua push Interactive selection
69
79
  $ lua push skill Push a skill directly
80
+ $ lua push webhook Push a webhook directly
81
+ $ lua push job Push a job directly
70
82
  $ lua push persona Push a persona directly
71
83
  `)
72
84
  .action(pushCommand);
@@ -160,6 +172,34 @@ Examples:
160
172
  $ lua skills production Manage production skills directly
161
173
  `)
162
174
  .action(skillsCommand);
175
+ program
176
+ .command("webhooks [env]")
177
+ .description("🪝 Manage and view webhooks")
178
+ .addHelpText('after', `
179
+ Arguments:
180
+ env Optional: 'sandbox', 'staging', or 'production' (prompts if not provided)
181
+
182
+ Examples:
183
+ $ lua webhooks Interactive selection
184
+ $ lua webhooks sandbox View sandbox webhooks directly
185
+ $ lua webhooks staging View staging webhooks (alias for sandbox)
186
+ $ lua webhooks production Manage production webhooks directly
187
+ `)
188
+ .action(webhooksCommand);
189
+ program
190
+ .command("jobs [env]")
191
+ .description("⏰ Manage and view scheduled jobs")
192
+ .addHelpText('after', `
193
+ Arguments:
194
+ env Optional: 'sandbox', 'staging', or 'production' (prompts if not provided)
195
+
196
+ Examples:
197
+ $ lua jobs Interactive selection
198
+ $ lua jobs sandbox View sandbox jobs directly
199
+ $ lua jobs staging View staging jobs (alias for sandbox)
200
+ $ lua jobs production Manage production jobs (pause/resume/trigger)
201
+ `)
202
+ .action(jobsCommand);
163
203
  program
164
204
  .command("features")
165
205
  .description("🎯 Manage agent features (tickets, RAG, etc.)")
@@ -174,6 +214,36 @@ Examples:
174
214
  $ lua features View and manage all agent features
175
215
  `)
176
216
  .action(featuresCommand);
217
+ program
218
+ .command("preprocessors")
219
+ .description("📥 Manage message preprocessors")
220
+ .addHelpText('after', `
221
+ Features:
222
+ • View deployed preprocessors
223
+ • View preprocessor versions
224
+ • Deploy versions to production
225
+ • Activate/deactivate preprocessors
226
+ • Delete preprocessors
227
+
228
+ Examples:
229
+ $ lua preprocessors Manage production preprocessors
230
+ `)
231
+ .action(preprocessorsCommand);
232
+ program
233
+ .command("postprocessors")
234
+ .description("📤 Manage response postprocessors")
235
+ .addHelpText('after', `
236
+ Features:
237
+ • View deployed postprocessors
238
+ • View postprocessor versions
239
+ • Deploy versions to production
240
+ • Activate/deactivate postprocessors
241
+ • Delete postprocessors
242
+
243
+ Examples:
244
+ $ lua postprocessors Manage production postprocessors
245
+ `)
246
+ .action(postprocessorsCommand);
177
247
  program
178
248
  .command("completion [shell]")
179
249
  .description("🎯 Generate shell completion script")
@@ -8,8 +8,8 @@ import { loadApiKey, checkApiKey } from '../services/auth.js';
8
8
  import { readSkillConfig } from '../utils/files.js';
9
9
  import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
10
10
  import { compileCommand } from './compile.js';
11
- import { pushSkillsToSandbox } from '../utils/dev-api.js';
12
- import { getAllSandboxSkillIds } from '../utils/sandbox-storage.js';
11
+ import { pushSkillsToSandbox, pushProcessorsToSandbox } from '../utils/dev-api.js';
12
+ import { getAllSandboxSkillIds, getAllSandboxPreProcessorIds, getAllSandboxPostProcessorIds } from '../utils/sandbox-storage.js';
13
13
  import ChatApi from '../api/chat.api.service.js';
14
14
  import { BASE_URLS } from '../config/constants.js';
15
15
  import { readDeployJson, validateConfig, validateDeployData, validateAgentConfig, } from '../utils/dev-helpers.js';
@@ -66,7 +66,7 @@ export async function chatCommand() {
66
66
  }, "chat");
67
67
  }
68
68
  /**
69
- * Sets up the sandbox environment by compiling and pushing skills
69
+ * Sets up the sandbox environment by compiling and pushing skills, preprocessors, and postprocessors
70
70
  */
71
71
  async function setupSandboxEnvironment(chatEnv, config) {
72
72
  writeProgress("🔄 Setting up sandbox environment...");
@@ -84,8 +84,31 @@ async function setupSandboxEnvironment(chatEnv, config) {
84
84
  process.exit(1);
85
85
  }
86
86
  writeSuccess(`✅ Pushed ${Object.keys(sandboxIds).length} skills to sandbox`);
87
+ // Push preprocessors and postprocessors to sandbox
88
+ const fs = await import('fs');
89
+ const path = await import('path');
90
+ const preprocessorsPath = path.join(process.cwd(), 'dist', 'preprocessors.json');
91
+ const postprocessorsPath = path.join(process.cwd(), 'dist', 'postprocessors.json');
92
+ let bundledPreProcessors = [];
93
+ let bundledPostProcessors = [];
94
+ if (fs.existsSync(preprocessorsPath)) {
95
+ bundledPreProcessors = JSON.parse(fs.readFileSync(preprocessorsPath, 'utf8'));
96
+ }
97
+ if (fs.existsSync(postprocessorsPath)) {
98
+ bundledPostProcessors = JSON.parse(fs.readFileSync(postprocessorsPath, 'utf8'));
99
+ }
100
+ if (bundledPreProcessors.length > 0 || bundledPostProcessors.length > 0) {
101
+ writeProgress("🔄 Pushing processors to sandbox...");
102
+ const processorCounts = await pushProcessorsToSandbox(chatEnv.apiKey, chatEnv.agentId, config, bundledPreProcessors, bundledPostProcessors, true);
103
+ if (processorCounts.preprocessors > 0 || processorCounts.postprocessors > 0) {
104
+ writeSuccess(`✅ Pushed ${processorCounts.preprocessors} preprocessor(s) and ${processorCounts.postprocessors} postprocessor(s) to sandbox`);
105
+ }
106
+ }
87
107
  // Store deploy data for skill overrides
88
108
  chatEnv.deployData = deployData;
109
+ // Get processor overrides for sandbox
110
+ chatEnv.preprocessorOverrides = await getAllSandboxPreProcessorIds(config);
111
+ chatEnv.postprocessorOverrides = await getAllSandboxPostProcessorIds(config);
89
112
  // Check for persona in config
90
113
  if (config.agent?.persona) {
91
114
  chatEnv.persona = config.agent.persona;
@@ -198,7 +221,9 @@ async function sendSandboxMessage(chatEnv, message) {
198
221
  }
199
222
  ],
200
223
  navigate: true,
201
- skillOverride: allSkillOverrides
224
+ skillOverride: allSkillOverrides,
225
+ preprocessorOverride: chatEnv.preprocessorOverrides || [],
226
+ postprocessorOverride: chatEnv.postprocessorOverrides || []
202
227
  };
203
228
  // Add persona override if available
204
229
  if (chatEnv.persona) {
@@ -224,7 +249,9 @@ async function sendProductionMessage(chatEnv, message) {
224
249
  }
225
250
  ],
226
251
  navigate: true,
227
- skillOverride: []
252
+ skillOverride: [],
253
+ preprocessorOverride: [],
254
+ postprocessorOverride: []
228
255
  };
229
256
  const chatApi = new ChatApi(BASE_URLS.CHAT, chatEnv.apiKey);
230
257
  const response = await chatApi.sendMessage(chatEnv.agentId, chatRequest);
@@ -6,13 +6,18 @@ import fs from "fs";
6
6
  import path from "path";
7
7
  import { Project } from "ts-morph";
8
8
  import { withErrorHandling, writeProgress, writeSuccess } from "../utils/cli.js";
9
- import { findIndexFile } from '../utils/compile.js';
9
+ import { findIndexFile, extractWebhooksMetadata, extractJobsMetadata, extractPreProcessorsMetadata, extractPostProcessorsMetadata } from '../utils/compile.js';
10
10
  import { detectTools } from '../utils/tool-detection.js';
11
- import { bundleTool, bundleMainIndex, extractExecuteCode } from '../utils/bundling.js';
11
+ import { bundleTool, bundleMainIndex, extractExecuteCode, bundleWebhook, bundleJob, bundlePreProcessor, bundlePostProcessor } from '../utils/bundling.js';
12
12
  import { createDeploymentData, createLegacyDeploymentData } from '../utils/deployment.js';
13
13
  import { syncYamlWithDeployJson, syncServerSkillsWithYaml } from '../utils/skill-management.js';
14
+ import { ensureWebhooksExistInYaml, syncServerWebhooksWithYaml } from '../utils/webhook-management.js';
15
+ import { ensureJobsExistInYaml, syncServerJobsWithYaml } from '../utils/job-management.js';
16
+ import { ensurePreProcessorsExistInYaml, syncServerPreProcessorsWithYaml } from '../utils/preprocessor-management.js';
17
+ import { ensurePostProcessorsExistInYaml, syncServerPostProcessorsWithYaml } from '../utils/postprocessor-management.js';
14
18
  import { readSkillConfig } from '../utils/files.js';
15
19
  import { COMPILE_DIRS, COMPILE_FILES } from '../config/compile.constants.js';
20
+ import { syncAgentPersonaWithYaml } from '../utils/agent-management.js';
16
21
  /**
17
22
  * Main compile command - orchestrates the entire skill compilation process.
18
23
  *
@@ -49,18 +54,53 @@ export async function compileCommand() {
49
54
  const indexPath = findIndexFile();
50
55
  const project = createTypeScriptProject();
51
56
  const indexFile = project.addSourceFileAtPath(indexPath);
52
- const tools = await detectTools(indexFile, project);
57
+ // Step 2a: Check for LuaAgent (unified agent configuration)
58
+ writeProgress("🔍 Checking for LuaAgent configuration...");
59
+ const { extractLuaAgentMetadata, resolveLuaAgentReferences, getSkillFilePaths } = await import('../utils/compile.js');
60
+ const agentMetadata = extractLuaAgentMetadata(indexFile);
61
+ let skillFilePaths = [];
62
+ let resolvedAgentData = null;
63
+ if (agentMetadata) {
64
+ writeProgress(`✨ Found LuaAgent: ${agentMetadata.name}`);
65
+ writeProgress(` Using new unified agent configuration approach`);
66
+ // Sync persona and welcome message with YAML
67
+ await syncAgentPersonaWithYaml(agentMetadata);
68
+ // Resolve references to get actual metadata for all components
69
+ resolvedAgentData = resolveLuaAgentReferences(agentMetadata, indexFile, project);
70
+ // Get file paths where skills are defined so we can scan them for tools
71
+ skillFilePaths = getSkillFilePaths(agentMetadata, indexFile);
72
+ writeProgress(`📦 Agent contains: ${resolvedAgentData.skills.length} skill(s), ${resolvedAgentData.webhooks.length} webhook(s), ${resolvedAgentData.jobs.length} job(s), ${resolvedAgentData.preProcessors.length} preprocessor(s), ${resolvedAgentData.postProcessors.length} postprocessor(s)`);
73
+ }
74
+ else {
75
+ writeProgress(`ℹ️ No LuaAgent found, using legacy detection for individual components`);
76
+ }
77
+ // Step 2b: Detect tools from index file and skill files
78
+ const tools = await detectTools(indexFile, project, skillFilePaths);
53
79
  writeProgress(`📦 Found ${tools.length} tools to bundle...`);
54
80
  // Step 3: Bundle each tool and extract metadata
81
+ const { preBundleJobsInSource, replaceJobPlaceholders } = await import('../utils/pre-bundle-jobs.js');
55
82
  for (const tool of tools) {
56
- await bundleTool(tool, distDir);
57
- await extractExecuteCode(tool, project);
83
+ // Step 3a: Pre-bundle any Jobs.create() in the tool source
84
+ const { modifiedSource, jobBundles } = await preBundleJobsInSource(tool.filePath, project, distDir);
85
+ // Step 3b: Bundle the tool (with placeholders for job execute functions)
86
+ await bundleTool(tool, distDir, modifiedSource);
87
+ // Step 3c: Replace placeholders in the bundled tool file
88
+ if (jobBundles.size > 0) {
89
+ const toolBundlePath = path.join(distDir, 'tools', `${tool.className}.js`);
90
+ if (fs.existsSync(toolBundlePath)) {
91
+ let bundledToolCode = fs.readFileSync(toolBundlePath, 'utf8');
92
+ bundledToolCode = replaceJobPlaceholders(bundledToolCode, jobBundles);
93
+ fs.writeFileSync(toolBundlePath, bundledToolCode);
94
+ }
95
+ }
96
+ // Step 3d: Extract execute code
97
+ await extractExecuteCode(tool, project, distDir);
58
98
  }
59
99
  // Step 4: Bundle the main index file
60
100
  await bundleMainIndex(indexPath, distDir);
61
101
  // Step 5: Create deployment data in both formats
62
102
  await createDeploymentData(tools, distDir);
63
- await createLegacyDeploymentData(tools, luaDir, indexFile);
103
+ await createLegacyDeploymentData(tools, luaDir, indexFile, resolvedAgentData);
64
104
  // Step 6: Sync YAML with deploy.json
65
105
  writeProgress("🔄 Syncing YAML with deploy.json...");
66
106
  const deployJsonPath = path.join(luaDir, COMPILE_FILES.DEPLOY_JSON);
@@ -70,7 +110,100 @@ export async function compileCommand() {
70
110
  writeProgress("🔄 Syncing server with YAML...");
71
111
  const updatedConfig = readSkillConfig(); // Re-read config after YAML sync
72
112
  await syncServerSkillsWithYaml(updatedConfig);
73
- writeSuccess(`✅ Skill compiled successfully - ${tools.length} tools bundled`);
113
+ // Step 8: Detect and process webhooks
114
+ writeProgress("🔍 Detecting webhooks...");
115
+ // Use resolved agent webhooks if available, otherwise scan index file
116
+ let webhooksMetadata = resolvedAgentData?.webhooks || extractWebhooksMetadata(indexFile);
117
+ let bundledWebhooks = [];
118
+ if (webhooksMetadata.length > 0) {
119
+ writeProgress(`📦 Found ${webhooksMetadata.length} webhook(s)...`);
120
+ // Bundle each webhook (extract execute function and schemas)
121
+ for (const webhook of webhooksMetadata) {
122
+ const bundled = await bundleWebhook(webhook, indexFile, distDir, project);
123
+ bundledWebhooks.push(bundled);
124
+ }
125
+ // Ensure webhooks exist in YAML with valid IDs
126
+ const configForWebhooks = readSkillConfig(); // Re-read for webhooks
127
+ await ensureWebhooksExistInYaml(webhooksMetadata, configForWebhooks);
128
+ // Sync server webhooks with YAML
129
+ writeProgress("🔄 Syncing server webhooks with YAML...");
130
+ const webhookConfig = readSkillConfig(); // Re-read after webhook updates
131
+ await syncServerWebhooksWithYaml(webhookConfig);
132
+ // Write bundled webhooks to file for reference
133
+ fs.writeFileSync(path.join(distDir, 'webhooks.json'), JSON.stringify(bundledWebhooks, null, 2));
134
+ }
135
+ // Step 9: Detect and process jobs
136
+ writeProgress("🔍 Detecting jobs...");
137
+ // Use resolved agent jobs if available, otherwise scan index file
138
+ let jobsMetadata = resolvedAgentData?.jobs || extractJobsMetadata(indexFile);
139
+ let bundledJobs = [];
140
+ if (jobsMetadata.length > 0) {
141
+ writeProgress(`📦 Found ${jobsMetadata.length} job(s)...`);
142
+ // Bundle each job (extract execute function)
143
+ for (const job of jobsMetadata) {
144
+ const bundled = await bundleJob(job, indexFile, distDir, project);
145
+ bundledJobs.push(bundled);
146
+ }
147
+ // Ensure jobs exist in YAML with valid IDs
148
+ const configForJobs = readSkillConfig(); // Re-read for jobs
149
+ await ensureJobsExistInYaml(jobsMetadata, configForJobs);
150
+ // Sync server jobs with YAML
151
+ writeProgress("🔄 Syncing server jobs with YAML...");
152
+ const jobConfig = readSkillConfig(); // Re-read after job updates
153
+ await syncServerJobsWithYaml(jobConfig);
154
+ // Write bundled jobs to file for reference
155
+ fs.writeFileSync(path.join(distDir, 'jobs.json'), JSON.stringify(bundledJobs, null, 2));
156
+ }
157
+ // Step 10: Detect and process preprocessors
158
+ writeProgress("🔍 Detecting preprocessors...");
159
+ // Use resolved agent preprocessors if available, otherwise scan index file
160
+ let preprocessorsMetadata = resolvedAgentData?.preProcessors || extractPreProcessorsMetadata(indexFile);
161
+ let bundledPreProcessors = [];
162
+ if (preprocessorsMetadata.length > 0) {
163
+ writeProgress(`📦 Found ${preprocessorsMetadata.length} preprocessor(s)...`);
164
+ for (const preprocessor of preprocessorsMetadata) {
165
+ const bundled = await bundlePreProcessor(preprocessor, indexFile, distDir, project);
166
+ bundledPreProcessors.push(bundled);
167
+ }
168
+ // Ensure preprocessors exist in YAML with valid IDs
169
+ const configForPreProcessors = readSkillConfig();
170
+ await ensurePreProcessorsExistInYaml(preprocessorsMetadata, configForPreProcessors);
171
+ // Sync server preprocessors with YAML
172
+ writeProgress("🔄 Syncing server preprocessors with YAML...");
173
+ const preprocessorConfig = readSkillConfig();
174
+ await syncServerPreProcessorsWithYaml(preprocessorConfig);
175
+ fs.writeFileSync(path.join(distDir, 'preprocessors.json'), JSON.stringify(bundledPreProcessors, null, 2));
176
+ }
177
+ // Step 11: Detect and process postprocessors
178
+ writeProgress("🔍 Detecting postprocessors...");
179
+ // Use resolved agent postprocessors if available, otherwise scan index file
180
+ let postprocessorsMetadata = resolvedAgentData?.postProcessors || extractPostProcessorsMetadata(indexFile);
181
+ let bundledPostProcessors = [];
182
+ if (postprocessorsMetadata.length > 0) {
183
+ writeProgress(`📦 Found ${postprocessorsMetadata.length} postprocessor(s)...`);
184
+ for (const postprocessor of postprocessorsMetadata) {
185
+ const bundled = await bundlePostProcessor(postprocessor, indexFile, distDir, project);
186
+ bundledPostProcessors.push(bundled);
187
+ }
188
+ // Ensure postprocessors exist in YAML with valid IDs
189
+ const configForPostProcessors = readSkillConfig();
190
+ await ensurePostProcessorsExistInYaml(postprocessorsMetadata, configForPostProcessors);
191
+ // Sync server postprocessors with YAML
192
+ writeProgress("🔄 Syncing server postprocessors with YAML...");
193
+ const postprocessorConfig = readSkillConfig();
194
+ await syncServerPostProcessorsWithYaml(postprocessorConfig);
195
+ fs.writeFileSync(path.join(distDir, 'postprocessors.json'), JSON.stringify(bundledPostProcessors, null, 2));
196
+ }
197
+ const summaryParts = [`${tools.length} tools bundled`];
198
+ if (webhooksMetadata.length > 0)
199
+ summaryParts.push(`${webhooksMetadata.length} webhook(s) registered`);
200
+ if (jobsMetadata.length > 0)
201
+ summaryParts.push(`${jobsMetadata.length} job(s) registered`);
202
+ if (preprocessorsMetadata.length > 0)
203
+ summaryParts.push(`${preprocessorsMetadata.length} preprocessor(s) registered`);
204
+ if (postprocessorsMetadata.length > 0)
205
+ summaryParts.push(`${postprocessorsMetadata.length} postprocessor(s) registered`);
206
+ writeSuccess(`✅ Skill compiled successfully - ${summaryParts.join(', ')}`);
74
207
  }, "compilation");
75
208
  }
76
209
  /**
@@ -7,7 +7,7 @@ import { compileCommand } from './compile.js';
7
7
  import { checkApiKey, loadApiKey } from '../services/auth.js';
8
8
  import { readSkillConfig } from '../utils/files.js';
9
9
  import { withErrorHandling, writeProgress, writeSuccess } from '../utils/cli.js';
10
- import { pushSkillsToSandbox } from '../utils/dev-api.js';
10
+ import { pushSkillsToSandbox, pushProcessorsToSandbox } from '../utils/dev-api.js';
11
11
  import { createChatServer } from '../utils/dev-server.js';
12
12
  import { createFileWatcher } from '../utils/dev-watcher.js';
13
13
  import { readConfigVersion, readDeployJson, extractSkillId, validateConfig, validateDeployData, validateAgentConfig, } from '../utils/dev-helpers.js';
@@ -75,6 +75,26 @@ export async function devCommand() {
75
75
  process.exit(1);
76
76
  }
77
77
  writeSuccess(`✅ Pushed ${Object.keys(sandboxIds).length} skills to sandbox`);
78
+ // Step 6b: Push preprocessors and postprocessors to sandbox
79
+ const fs = await import('fs');
80
+ const path = await import('path');
81
+ const preprocessorsPath = path.join(process.cwd(), 'dist', 'preprocessors.json');
82
+ const postprocessorsPath = path.join(process.cwd(), 'dist', 'postprocessors.json');
83
+ let bundledPreProcessors = [];
84
+ let bundledPostProcessors = [];
85
+ if (fs.existsSync(preprocessorsPath)) {
86
+ bundledPreProcessors = JSON.parse(fs.readFileSync(preprocessorsPath, 'utf8'));
87
+ }
88
+ if (fs.existsSync(postprocessorsPath)) {
89
+ bundledPostProcessors = JSON.parse(fs.readFileSync(postprocessorsPath, 'utf8'));
90
+ }
91
+ if (bundledPreProcessors.length > 0 || bundledPostProcessors.length > 0) {
92
+ writeProgress("🔄 Pushing processors to sandbox...");
93
+ const processorCounts = await pushProcessorsToSandbox(apiKey, agentId, updatedConfig, bundledPreProcessors, bundledPostProcessors, true);
94
+ if (processorCounts.preprocessors > 0 || processorCounts.postprocessors > 0) {
95
+ writeSuccess(`✅ Pushed ${processorCounts.preprocessors} preprocessor(s) and ${processorCounts.postprocessors} postprocessor(s) to sandbox`);
96
+ }
97
+ }
78
98
  // Use the first skill's sandbox ID for the web interface
79
99
  const firstSkillName = Object.keys(sandboxIds)[0];
80
100
  const sandboxSkillId = sandboxIds[firstSkillName];
@@ -83,7 +103,8 @@ export async function devCommand() {
83
103
  process.exit(1);
84
104
  }
85
105
  // Step 7: Start web server for chat interface
86
- const { server, wss, broadcastLog } = createChatServer(apiKey, agentId, skillId, sandboxSkillId, DEV_SERVER_PORT);
106
+ const { server, wss, broadcastLog } = createChatServer(apiKey, agentId, skillId, sandboxSkillId, DEV_SERVER_PORT, updatedConfig // Pass config for processor overrides
107
+ );
87
108
  // Step 8: Open browser to chat interface
88
109
  try {
89
110
  await open(`http://localhost:${DEV_SERVER_PORT}`);