lua-cli 2.5.7 → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/api/agent.api.service.d.ts +45 -0
  2. package/dist/api/agent.api.service.js +54 -0
  3. package/dist/api/job.api.service.d.ts +210 -0
  4. package/dist/api/job.api.service.js +200 -0
  5. package/dist/api/lazy-instances.d.ts +24 -0
  6. package/dist/api/lazy-instances.js +48 -0
  7. package/dist/api/postprocessor.api.service.d.ts +98 -0
  8. package/dist/api/postprocessor.api.service.js +76 -0
  9. package/dist/api/preprocessor.api.service.d.ts +98 -0
  10. package/dist/api/preprocessor.api.service.js +76 -0
  11. package/dist/api/user.data.api.service.d.ts +28 -0
  12. package/dist/api/user.data.api.service.js +51 -0
  13. package/dist/api/webhook.api.service.d.ts +151 -0
  14. package/dist/api/webhook.api.service.js +134 -0
  15. package/dist/api-exports.d.ts +156 -41
  16. package/dist/api-exports.js +182 -21
  17. package/dist/cli/command-definitions.js +149 -7
  18. package/dist/commands/compile.js +124 -5
  19. package/dist/commands/completion.d.ts +11 -0
  20. package/dist/commands/completion.js +209 -0
  21. package/dist/commands/env.d.ts +3 -2
  22. package/dist/commands/env.js +42 -17
  23. package/dist/commands/features.d.ts +16 -0
  24. package/dist/commands/features.js +352 -0
  25. package/dist/commands/index.d.ts +7 -0
  26. package/dist/commands/index.js +7 -0
  27. package/dist/commands/init.js +53 -7
  28. package/dist/commands/jobs.d.ts +20 -0
  29. package/dist/commands/jobs.js +533 -0
  30. package/dist/commands/logs.js +2 -5
  31. package/dist/commands/persona.d.ts +3 -2
  32. package/dist/commands/persona.js +43 -18
  33. package/dist/commands/postprocessors.d.ts +8 -0
  34. package/dist/commands/postprocessors.js +431 -0
  35. package/dist/commands/preprocessors.d.ts +8 -0
  36. package/dist/commands/preprocessors.js +431 -0
  37. package/dist/commands/push.d.ts +9 -13
  38. package/dist/commands/push.js +937 -69
  39. package/dist/commands/skills.d.ts +16 -0
  40. package/dist/commands/skills.js +438 -0
  41. package/dist/commands/test.d.ts +9 -18
  42. package/dist/commands/test.js +558 -82
  43. package/dist/commands/webhooks.d.ts +18 -0
  44. package/dist/commands/webhooks.js +424 -0
  45. package/dist/common/data.entry.instance.d.ts +7 -0
  46. package/dist/common/data.entry.instance.js +15 -0
  47. package/dist/common/job.instance.d.ts +77 -0
  48. package/dist/common/job.instance.js +108 -0
  49. package/dist/common/order.instance.d.ts +6 -0
  50. package/dist/common/order.instance.js +14 -0
  51. package/dist/common/product.instance.d.ts +6 -0
  52. package/dist/common/product.instance.js +14 -0
  53. package/dist/common/user.instance.d.ts +15 -0
  54. package/dist/common/user.instance.js +38 -0
  55. package/dist/config/constants.d.ts +2 -2
  56. package/dist/config/constants.js +4 -4
  57. package/dist/index.js +14 -3
  58. package/dist/interfaces/agent.d.ts +33 -1
  59. package/dist/interfaces/chat.d.ts +22 -0
  60. package/dist/interfaces/index.d.ts +10 -0
  61. package/dist/interfaces/index.js +7 -0
  62. package/dist/interfaces/jobs.d.ts +172 -0
  63. package/dist/interfaces/jobs.js +5 -0
  64. package/dist/interfaces/message.d.ts +18 -0
  65. package/dist/interfaces/message.js +1 -0
  66. package/dist/interfaces/postprocessors.d.ts +35 -0
  67. package/dist/interfaces/postprocessors.js +4 -0
  68. package/dist/interfaces/preprocessors.d.ts +35 -0
  69. package/dist/interfaces/preprocessors.js +4 -0
  70. package/dist/interfaces/webhooks.d.ts +104 -0
  71. package/dist/interfaces/webhooks.js +5 -0
  72. package/dist/types/api-contracts.d.ts +14 -0
  73. package/dist/types/api-contracts.js +0 -7
  74. package/dist/types/compile.types.d.ts +49 -0
  75. package/dist/types/index.d.ts +1 -1
  76. package/dist/types/index.js +1 -1
  77. package/dist/types/skill.d.ts +502 -0
  78. package/dist/types/skill.js +477 -0
  79. package/dist/utils/agent-management.d.ts +25 -0
  80. package/dist/utils/agent-management.js +67 -0
  81. package/dist/utils/bundling.d.ts +31 -1
  82. package/dist/utils/bundling.js +653 -10
  83. package/dist/utils/compile.d.ts +63 -0
  84. package/dist/utils/compile.js +691 -36
  85. package/dist/utils/deployment.d.ts +2 -1
  86. package/dist/utils/deployment.js +16 -2
  87. package/dist/utils/init-agent.d.ts +3 -1
  88. package/dist/utils/init-agent.js +6 -4
  89. package/dist/utils/init-prompts.d.ts +2 -1
  90. package/dist/utils/init-prompts.js +14 -9
  91. package/dist/utils/job-management.d.ts +24 -0
  92. package/dist/utils/job-management.js +264 -0
  93. package/dist/utils/postprocessor-management.d.ts +9 -0
  94. package/dist/utils/postprocessor-management.js +118 -0
  95. package/dist/utils/preprocessor-management.d.ts +9 -0
  96. package/dist/utils/preprocessor-management.js +118 -0
  97. package/dist/utils/sandbox.d.ts +61 -1
  98. package/dist/utils/sandbox.js +283 -72
  99. package/dist/utils/tool-detection.d.ts +3 -2
  100. package/dist/utils/tool-detection.js +18 -4
  101. package/dist/utils/webhook-management.d.ts +24 -0
  102. package/dist/utils/webhook-management.js +256 -0
  103. package/dist/web/app.css +152 -736
  104. package/dist/web/app.js +45 -45
  105. package/package.json +2 -2
  106. package/template/AGENT_CONFIGURATION.md +251 -0
  107. package/template/COMPLEX_JOB_EXAMPLES.md +795 -0
  108. package/template/DYNAMIC_JOB_CREATION.md +371 -0
  109. package/template/README.md +30 -2
  110. package/template/WEBHOOKS_JOBS_QUICKSTART.md +318 -0
  111. package/template/WEBHOOK_JOB_EXAMPLES.md +817 -0
  112. package/template/package.json +1 -1
  113. package/template/src/index-agent-example.ts +201 -0
  114. package/template/src/index.ts +39 -0
  115. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  116. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  117. package/template/src/jobs/DataMigrationJob.ts +133 -0
  118. package/template/src/jobs/HealthCheckJob.ts +87 -0
  119. package/template/src/postprocessors/ResponseFormatter.ts +151 -0
  120. package/template/src/preprocessors/MessageFilter.ts +91 -0
  121. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  122. package/template/src/tools/SmartBasketTool.ts +188 -0
  123. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  124. package/template/src/webhooks/UserEventWebhook.ts +77 -0
@@ -2,6 +2,9 @@
2
2
  * Push Command
3
3
  * Orchestrates pushing skill versions to the server
4
4
  */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import yaml from 'js-yaml';
5
8
  import { compileCommand } from './compile.js';
6
9
  import { checkApiKey, loadApiKey } from '../services/auth.js';
7
10
  import { readSkillConfig } from '../utils/files.js';
@@ -10,10 +13,95 @@ import { safePrompt } from '../utils/prompt-handler.js';
10
13
  import { readDeployJson, validatePushConfig, validateDeployData, promptSkillSelection, promptVersionConfirmOrUpdate, getAvailableSkills, updateSkillVersionInYaml, getSkillDeployData, } from '../utils/push-helpers.js';
11
14
  import { pushVersion } from '../utils/push-api.js';
12
15
  import { fetchVersions, publishVersion, } from '../utils/deploy-api.js';
16
+ import { BASE_URLS } from '../config/constants.js';
17
+ import PreProcessorApi from '../api/preprocessor.api.service.js';
18
+ import PostProcessorApi from '../api/postprocessor.api.service.js';
13
19
  /**
14
- * Main push command - pushes a skill version to the server.
20
+ * Main push command - pushes a skill or persona version to the server.
15
21
  *
16
22
  * This command performs the following steps:
23
+ * 1. If type argument provided, uses it directly
24
+ * 2. Otherwise, prompts user to select 'skill' or 'persona'
25
+ * 3. Routes to appropriate push flow
26
+ *
27
+ * Use this command to:
28
+ * - Upload a new version of your skill or persona
29
+ * - Make your skill/persona available for testing in dev mode
30
+ * - Prepare your skill/persona for deployment
31
+ *
32
+ * Note: This does NOT deploy to production. Use `lua deploy` for that.
33
+ *
34
+ * @param type - Optional type argument ('skill' or 'persona')
35
+ * @returns Promise that resolves when push completes
36
+ */
37
+ export async function pushCommand(type) {
38
+ return withErrorHandling(async () => {
39
+ let selectedType;
40
+ // Step 1: Check if type was provided as argument
41
+ if (type) {
42
+ // Validate the provided type
43
+ if (type !== 'skill' && type !== 'persona' && type !== 'webhook' && type !== 'job' && type !== 'preprocessor' && type !== 'postprocessor') {
44
+ console.error(`❌ Invalid type: "${type}". Must be "skill", "persona", "webhook", "job", "preprocessor", or "postprocessor".`);
45
+ console.log('\nUsage:');
46
+ console.log(' lua push - Interactive selection');
47
+ console.log(' lua push skill - Push a skill directly');
48
+ console.log(' lua push persona - Push a persona directly');
49
+ console.log(' lua push webhook - Push a webhook directly');
50
+ console.log(' lua push job - Push a job directly');
51
+ console.log(' lua push preprocessor - Push a preprocessor directly');
52
+ console.log(' lua push postprocessor - Push a postprocessor directly');
53
+ process.exit(1);
54
+ }
55
+ selectedType = type;
56
+ }
57
+ else {
58
+ // Step 2: Prompt for type selection
59
+ const typeAnswer = await safePrompt([
60
+ {
61
+ type: 'list',
62
+ name: 'type',
63
+ message: 'What would you like to push?',
64
+ choices: [
65
+ { name: '🛠️ Skill', value: 'skill' },
66
+ { name: '🪝 Webhook', value: 'webhook' },
67
+ { name: '⏰ Job', value: 'job' },
68
+ { name: '📥 PreProcessor', value: 'preprocessor' },
69
+ { name: '📤 PostProcessor', value: 'postprocessor' },
70
+ { name: '🌙 Persona', value: 'persona' }
71
+ ]
72
+ }
73
+ ]);
74
+ if (!typeAnswer) {
75
+ console.log("Push cancelled.");
76
+ return;
77
+ }
78
+ selectedType = typeAnswer.type;
79
+ }
80
+ // Step 3: Route to appropriate function
81
+ if (selectedType === 'skill') {
82
+ await pushSkillVersion();
83
+ }
84
+ else if (selectedType === 'webhook') {
85
+ await pushWebhookVersion();
86
+ }
87
+ else if (selectedType === 'job') {
88
+ await pushJobVersion();
89
+ }
90
+ else if (selectedType === 'preprocessor') {
91
+ await pushPreProcessorVersion();
92
+ }
93
+ else if (selectedType === 'postprocessor') {
94
+ await pushPostProcessorVersion();
95
+ }
96
+ else {
97
+ await pushPersonaVersion();
98
+ }
99
+ }, "push");
100
+ }
101
+ /**
102
+ * Push skill version to the server.
103
+ *
104
+ * This function performs the following steps:
17
105
  * 1. Validates configuration has required fields
18
106
  * 2. Prompts user to select a skill (if multiple skills exist)
19
107
  * 3. Prompts to confirm or update version
@@ -23,98 +111,878 @@ import { fetchVersions, publishVersion, } from '../utils/deploy-api.js';
23
111
  * 7. Extracts the specific skill's deploy data
24
112
  * 8. Pushes version to server
25
113
  *
26
- * Use this command to:
27
- * - Upload a new version of your skill
28
- * - Make your skill available for testing in dev mode
29
- * - Prepare your skill for deployment
114
+ * @returns Promise that resolves when push completes
115
+ */
116
+ async function pushSkillVersion() {
117
+ // Step 1: Validate configuration
118
+ const config = readSkillConfig();
119
+ validatePushConfig(config);
120
+ // Step 2: Get available skills and prompt for selection
121
+ const availableSkills = getAvailableSkills(config);
122
+ if (availableSkills.length === 0) {
123
+ console.error("❌ No skills found in configuration. Please compile your skill first using 'lua compile'.");
124
+ process.exit(1);
125
+ }
126
+ let selectedSkill;
127
+ if (availableSkills.length === 1) {
128
+ // Only one skill, use it automatically
129
+ selectedSkill = availableSkills[0];
130
+ writeInfo(`📦 Pushing skill: ${selectedSkill.name}`);
131
+ }
132
+ else {
133
+ // Multiple skills, prompt for selection
134
+ selectedSkill = await promptSkillSelection(availableSkills);
135
+ writeInfo(`📦 Selected skill: ${selectedSkill.name}`);
136
+ }
137
+ // Step 3: Confirm or update version
138
+ const confirmedVersion = await promptVersionConfirmOrUpdate(selectedSkill.version);
139
+ let versionUpdated = false;
140
+ if (confirmedVersion !== selectedSkill.version) {
141
+ writeInfo(`📝 Updating version from ${selectedSkill.version} to ${confirmedVersion}`);
142
+ updateSkillVersionInYaml(selectedSkill.name, confirmedVersion);
143
+ selectedSkill.version = confirmedVersion;
144
+ versionUpdated = true;
145
+ }
146
+ // Step 4: Authenticate
147
+ const apiKey = await loadApiKey();
148
+ if (!apiKey) {
149
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
150
+ process.exit(1);
151
+ }
152
+ const userData = await checkApiKey(apiKey);
153
+ writeProgress("✅ Authenticated");
154
+ // Step 5: Compile the skill (always compile to ensure up-to-date, or if version was updated)
155
+ writeProgress("🔄 Compiling skill...");
156
+ await compileCommand();
157
+ // Step 6: Validate deploy data for the selected skill
158
+ const deployData = readDeployJson();
159
+ validateDeployData(deployData, selectedSkill);
160
+ // Step 7: Extract the specific skill's deploy data
161
+ const skillDeployData = getSkillDeployData(deployData, selectedSkill.name);
162
+ // Step 8: Push version to server
163
+ const agentId = config.agent.agentId;
164
+ const skillId = selectedSkill.skillId;
165
+ writeProgress("🔄 Pushing version to server...");
166
+ const result = await pushVersion(apiKey, agentId, skillId, skillDeployData);
167
+ if (result.success && result.data) {
168
+ const pushedVersion = result.data.version;
169
+ writeSuccess(`✅ Version ${pushedVersion} of "${selectedSkill.name}" pushed successfully`);
170
+ // Update YAML with the version returned from server (in case it's different)
171
+ if (pushedVersion !== selectedSkill.version) {
172
+ writeInfo(`📝 Updating YAML with server version: ${pushedVersion}`);
173
+ updateSkillVersionInYaml(selectedSkill.name, pushedVersion);
174
+ selectedSkill.version = pushedVersion;
175
+ }
176
+ // Ask if user wants to deploy now
177
+ const deployAnswer = await safePrompt([
178
+ {
179
+ type: 'confirm',
180
+ name: 'deployNow',
181
+ message: 'Would you like to deploy this version to production now?',
182
+ default: false
183
+ }
184
+ ]);
185
+ if (deployAnswer && deployAnswer.deployNow) {
186
+ await deployVersionAfterPush(apiKey, config.agent.agentId, selectedSkill, pushedVersion);
187
+ }
188
+ }
189
+ else if (result.error) {
190
+ console.error(`❌ ${result.error.message}`);
191
+ process.exit(1);
192
+ }
193
+ else {
194
+ console.error("❌ Failed to push version. Please try again.");
195
+ process.exit(1);
196
+ }
197
+ }
198
+ /**
199
+ * Push persona version to the server.
30
200
  *
31
- * Note: This does NOT deploy to production. Use `lua deploy` for that.
201
+ * This function performs the following steps:
202
+ * 1. Validates configuration has required fields
203
+ * 2. Loads current persona from lua.skill.yaml
204
+ * 3. Authenticates the user
205
+ * 4. Pushes persona version to server
206
+ * 5. Optionally deploys to production
32
207
  *
33
208
  * @returns Promise that resolves when push completes
34
209
  */
35
- export async function pushCommand() {
36
- return withErrorHandling(async () => {
37
- // Step 1: Validate configuration
210
+ async function pushPersonaVersion() {
211
+ // Step 1: Validate configuration
212
+ const config = readSkillConfig();
213
+ if (!config || !config.agent?.agentId) {
214
+ console.error("❌ No agent configuration found. Please run 'lua init' first.");
215
+ process.exit(1);
216
+ }
217
+ // Step 2: Load current persona
218
+ const currentPersona = config.agent?.persona || '';
219
+ if (!currentPersona || !currentPersona.trim()) {
220
+ console.error("❌ No persona found in configuration. Please edit your persona first using 'lua persona'.");
221
+ process.exit(1);
222
+ }
223
+ writeInfo(`📦 Pushing persona for agent: ${config.agent.agentId}`);
224
+ // Show preview of persona
225
+ const preview = currentPersona.length > 200
226
+ ? currentPersona.substring(0, 200) + '...'
227
+ : currentPersona;
228
+ console.log("\nPersona preview:");
229
+ console.log(preview);
230
+ console.log();
231
+ // Step 3: Confirm push
232
+ const confirmAnswer = await safePrompt([
233
+ {
234
+ type: 'confirm',
235
+ name: 'confirm',
236
+ message: 'Create new persona version?',
237
+ default: true
238
+ }
239
+ ]);
240
+ if (!confirmAnswer || !confirmAnswer.confirm) {
241
+ console.log("\n❌ Push cancelled.\n");
242
+ return;
243
+ }
244
+ // Step 4: Authenticate
245
+ const apiKey = await loadApiKey();
246
+ if (!apiKey) {
247
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
248
+ process.exit(1);
249
+ }
250
+ await checkApiKey(apiKey);
251
+ writeProgress("✅ Authenticated");
252
+ // Step 5: Push persona version to server
253
+ writeProgress("🔄 Creating persona version...");
254
+ try {
255
+ const response = await fetch(`${BASE_URLS.API}/developer/agents/${config.agent.agentId}/persona/version`, {
256
+ method: 'POST',
257
+ headers: {
258
+ 'Authorization': `Bearer ${apiKey}`,
259
+ 'Content-Type': 'application/json'
260
+ },
261
+ body: JSON.stringify({ persona: currentPersona })
262
+ });
263
+ if (!response.ok) {
264
+ const errorText = await response.text();
265
+ throw new Error(`HTTP error! status: ${response.status}, ${errorText}`);
266
+ }
267
+ const data = await response.json();
268
+ const versionNum = data.version || data.data?.version || 'N/A';
269
+ writeSuccess(`✅ Persona version ${versionNum} created successfully`);
270
+ // Step 6: Ask if user wants to deploy now
271
+ const deployAnswer = await safePrompt([
272
+ {
273
+ type: 'confirm',
274
+ name: 'deployNow',
275
+ message: 'Would you like to deploy this persona version to production now?',
276
+ default: false
277
+ }
278
+ ]);
279
+ if (deployAnswer && deployAnswer.deployNow) {
280
+ await deployPersonaVersionAfterPush(apiKey, config.agent.agentId, versionNum);
281
+ }
282
+ else {
283
+ writeInfo("💡 You can deploy this version later using: lua deploy");
284
+ }
285
+ }
286
+ catch (error) {
287
+ console.error('❌ Error creating persona version:', error);
288
+ process.exit(1);
289
+ }
290
+ }
291
+ /**
292
+ * Deploy a persona version immediately after pushing
293
+ */
294
+ async function deployPersonaVersionAfterPush(apiKey, agentId, versionNum) {
295
+ try {
296
+ // Show warning
297
+ console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
298
+ console.log("⚠️ This will affect ALL users immediately.\n");
299
+ const confirmAnswer = await safePrompt([
300
+ {
301
+ type: 'confirm',
302
+ name: 'confirm',
303
+ message: 'Are you absolutely sure you want to deploy this persona version?',
304
+ default: false
305
+ }
306
+ ]);
307
+ if (!confirmAnswer || !confirmAnswer.confirm) {
308
+ console.log("\n❌ Deployment cancelled. Persona version created but not deployed.\n");
309
+ return;
310
+ }
311
+ writeProgress("🔄 Deploying persona version...");
312
+ const deployResponse = await fetch(`${BASE_URLS.API}/developer/agents/${agentId}/persona/version/${versionNum}`, {
313
+ method: 'POST',
314
+ headers: {
315
+ 'Authorization': `Bearer ${apiKey}`,
316
+ 'Content-Type': 'application/json'
317
+ },
318
+ body: JSON.stringify({})
319
+ });
320
+ if (!deployResponse.ok) {
321
+ const errorText = await deployResponse.text();
322
+ console.error(`\n❌ Deploy Error: ${deployResponse.status} - ${errorText}\n`);
323
+ throw new Error(`HTTP error! status: ${deployResponse.status}`);
324
+ }
325
+ writeSuccess(`\n✅ Persona version ${versionNum} deployed successfully to production\n`);
326
+ writeInfo("💡 The new persona is now active for all users.");
327
+ }
328
+ catch (error) {
329
+ console.error('\n❌ Error deploying persona version:', error);
330
+ console.log('💡 You can deploy later using: lua persona\n');
331
+ }
332
+ }
333
+ /**
334
+ * Push webhook version to the server.
335
+ *
336
+ * Steps:
337
+ * 1. Run compilation
338
+ * 2. Validate config and deploy data
339
+ * 3. Select webhook to push
340
+ * 4. Confirm or update version
341
+ * 5. Push to server
342
+ * 6. Optionally deploy immediately
343
+ */
344
+ async function pushWebhookVersion() {
345
+ try {
346
+ // Step 1: Run compilation
347
+ writeProgress("📦 Compiling project...");
348
+ await compileCommand();
349
+ writeSuccess("✅ Compilation complete");
350
+ // Step 2: Authenticate
351
+ const apiKey = await loadApiKey();
352
+ if (!apiKey) {
353
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
354
+ process.exit(1);
355
+ }
356
+ await checkApiKey(apiKey);
357
+ writeSuccess("✅ Authentication verified");
358
+ // Step 3: Read configuration
38
359
  const config = readSkillConfig();
39
- validatePushConfig(config);
40
- // Step 2: Get available skills and prompt for selection
41
- const availableSkills = getAvailableSkills(config);
42
- if (availableSkills.length === 0) {
43
- console.error("❌ No skills found in configuration. Please compile your skill first using 'lua compile'.");
360
+ if (!config?.agent?.agentId) {
361
+ console.error("❌ No agent ID found in lua.skill.yaml. Please run 'lua init' first.");
362
+ process.exit(1);
363
+ }
364
+ const webhooks = config.webhooks || [];
365
+ if (webhooks.length === 0) {
366
+ console.error("❌ No webhooks found in lua.skill.yaml.");
367
+ console.log("💡 Make sure you have created a webhook using LuaWebhook in your code.");
368
+ process.exit(1);
369
+ }
370
+ // Step 4: Select webhook to push
371
+ const webhookAnswer = await safePrompt([
372
+ {
373
+ type: 'list',
374
+ name: 'selectedWebhook',
375
+ message: 'Select a webhook to push:',
376
+ choices: webhooks.map((webhook) => ({
377
+ name: `${webhook.name} (v${webhook.version})`,
378
+ value: webhook
379
+ }))
380
+ }
381
+ ]);
382
+ if (!webhookAnswer) {
383
+ console.log("Push cancelled.");
384
+ return;
385
+ }
386
+ const selectedWebhook = webhookAnswer.selectedWebhook;
387
+ // Step 4.5: Load bundled webhook data
388
+ const bundledWebhooksPath = path.join(process.cwd(), 'dist', 'webhooks.json');
389
+ let bundledWebhookData = {};
390
+ if (fs.existsSync(bundledWebhooksPath)) {
391
+ const bundledWebhooks = JSON.parse(fs.readFileSync(bundledWebhooksPath, 'utf8'));
392
+ bundledWebhookData = bundledWebhooks.find((w) => w.name === selectedWebhook.name) || {};
393
+ // Validate that the webhook exists in compiled code
394
+ if (!bundledWebhookData || Object.keys(bundledWebhookData).length === 0) {
395
+ console.error(`\n❌ Webhook "${selectedWebhook.name}" not found in compiled code.`);
396
+ console.log("💡 This webhook may have been removed or commented out in your code.");
397
+ console.log("💡 Please uncomment the webhook or remove it from lua.skill.yaml.");
398
+ process.exit(1);
399
+ }
400
+ }
401
+ else {
402
+ console.error('❌ Bundled webhook data not found.');
403
+ console.log("💡 Please ensure your webhook code is properly compiled.");
44
404
  process.exit(1);
45
405
  }
46
- let selectedSkill;
47
- if (availableSkills.length === 1) {
48
- // Only one skill, use it automatically
49
- selectedSkill = availableSkills[0];
50
- writeInfo(`📦 Pushing skill: ${selectedSkill.name}`);
406
+ // Step 5: Confirm or update version (same as skills)
407
+ const confirmedVersion = await promptVersionConfirmOrUpdate(selectedWebhook.version);
408
+ let versionToPush = confirmedVersion;
409
+ if (confirmedVersion !== selectedWebhook.version) {
410
+ writeInfo(`📝 Updating version from ${selectedWebhook.version} to ${confirmedVersion}`);
411
+ updateWebhookVersionInYaml(selectedWebhook.name, confirmedVersion);
412
+ versionToPush = confirmedVersion;
413
+ }
414
+ // Step 6: Prepare webhook data for push (include bundled data)
415
+ const webhookData = {
416
+ name: selectedWebhook.name,
417
+ version: versionToPush,
418
+ description: bundledWebhookData.description || selectedWebhook.description || `Webhook: ${selectedWebhook.name}`,
419
+ context: bundledWebhookData.context || selectedWebhook.context || '',
420
+ webhookId: selectedWebhook.webhookId,
421
+ querySchema: bundledWebhookData.querySchema,
422
+ headerSchema: bundledWebhookData.headerSchema,
423
+ bodySchema: bundledWebhookData.bodySchema,
424
+ code: bundledWebhookData.code, // Bundled and compressed code for execution
425
+ executeFunction: bundledWebhookData.executeFunction // Raw function for display/editing
426
+ };
427
+ // Step 7: Push to server
428
+ writeProgress(`\n🚀 Pushing ${selectedWebhook.name} v${versionToPush} to server...`);
429
+ const response = await fetch(`${BASE_URLS.API}/developer/webhooks/${config.agent.agentId}/${selectedWebhook.webhookId}/version`, {
430
+ method: 'POST',
431
+ headers: {
432
+ 'Authorization': `Bearer ${apiKey}`,
433
+ 'Content-Type': 'application/json'
434
+ },
435
+ body: JSON.stringify(webhookData)
436
+ });
437
+ if (!response.ok) {
438
+ const errorText = await response.text();
439
+ console.error(`\n❌ Push Error: ${response.status} - ${errorText}\n`);
440
+ throw new Error(`HTTP error! status: ${response.status}`);
441
+ }
442
+ const result = await response.json();
443
+ writeSuccess(`\n✅ Successfully pushed ${selectedWebhook.name} v${versionToPush}\n`);
444
+ writeInfo(`📦 Webhook URL: ${BASE_URLS.API}/webhooks/${config.agent.agentId}/${selectedWebhook.webhookId}`);
445
+ // Step 8: Ask if user wants to deploy now
446
+ const deployAnswer = await safePrompt([
447
+ {
448
+ type: 'confirm',
449
+ name: 'deployNow',
450
+ message: 'Would you like to deploy this webhook version to production now?',
451
+ default: false
452
+ }
453
+ ]);
454
+ if (deployAnswer && deployAnswer.deployNow) {
455
+ await deployWebhookVersionAfterPush(apiKey, config.agent.agentId, selectedWebhook, versionToPush);
51
456
  }
52
457
  else {
53
- // Multiple skills, prompt for selection
54
- selectedSkill = await promptSkillSelection(availableSkills);
55
- writeInfo(`📦 Selected skill: ${selectedSkill.name}`);
56
- }
57
- // Step 3: Confirm or update version
58
- const confirmedVersion = await promptVersionConfirmOrUpdate(selectedSkill.version);
59
- let versionUpdated = false;
60
- if (confirmedVersion !== selectedSkill.version) {
61
- writeInfo(`📝 Updating version from ${selectedSkill.version} to ${confirmedVersion}`);
62
- updateSkillVersionInYaml(selectedSkill.name, confirmedVersion);
63
- selectedSkill.version = confirmedVersion;
64
- versionUpdated = true;
65
- }
66
- // Step 4: Authenticate
458
+ writeInfo("💡 You can deploy this version later using: lua webhooks production");
459
+ }
460
+ }
461
+ catch (error) {
462
+ console.error('❌ Error pushing webhook version:', error);
463
+ process.exit(1);
464
+ }
465
+ }
466
+ /**
467
+ * Deploy a webhook version immediately after pushing
468
+ */
469
+ async function deployWebhookVersionAfterPush(apiKey, agentId, selectedWebhook, pushedVersion) {
470
+ try {
471
+ // Show warning
472
+ console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
473
+ console.log("⚠️ This will affect ALL users immediately.\n");
474
+ const confirmAnswer = await safePrompt([
475
+ {
476
+ type: 'confirm',
477
+ name: 'confirm',
478
+ message: 'Are you absolutely sure you want to deploy?',
479
+ default: false
480
+ }
481
+ ]);
482
+ if (!confirmAnswer || !confirmAnswer.confirm) {
483
+ console.log("\n❌ Deployment cancelled. Version pushed but not deployed.\n");
484
+ return;
485
+ }
486
+ writeProgress("🔄 Publishing webhook version...");
487
+ const publishResponse = await fetch(`${BASE_URLS.API}/developer/webhooks/${agentId}/${selectedWebhook.webhookId}/${pushedVersion}/publish`, {
488
+ method: 'POST',
489
+ headers: {
490
+ 'Authorization': `Bearer ${apiKey}`,
491
+ 'Content-Type': 'application/json'
492
+ },
493
+ body: JSON.stringify({})
494
+ });
495
+ if (!publishResponse.ok) {
496
+ const errorText = await publishResponse.text();
497
+ console.error(`\n❌ Deploy Error: ${publishResponse.status} - ${errorText}\n`);
498
+ throw new Error(`HTTP error! status: ${publishResponse.status}`);
499
+ }
500
+ writeSuccess(`\n✅ Webhook ${selectedWebhook.name} v${pushedVersion} deployed successfully to production\n`);
501
+ writeInfo("💡 The new webhook version is now active for all users.");
502
+ }
503
+ catch (error) {
504
+ console.error('\n❌ Error deploying webhook version:', error);
505
+ console.log('💡 You can deploy later using: lua webhooks production\n');
506
+ }
507
+ }
508
+ /**
509
+ * Updates webhook version in lua.skill.yaml
510
+ */
511
+ function updateWebhookVersionInYaml(webhookName, newVersion) {
512
+ try {
513
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
514
+ const yamlContent = fs.readFileSync(yamlPath, 'utf8');
515
+ const config = yaml.load(yamlContent);
516
+ if (config.webhooks) {
517
+ const webhook = config.webhooks.find((w) => w.name === webhookName);
518
+ if (webhook) {
519
+ webhook.version = newVersion;
520
+ fs.writeFileSync(yamlPath, yaml.dump(config, { indent: 2, lineWidth: 120, noRefs: true }));
521
+ }
522
+ }
523
+ }
524
+ catch (error) {
525
+ console.warn('⚠️ Could not update webhook version in YAML:', error);
526
+ }
527
+ }
528
+ /**
529
+ * Updates job version in lua.skill.yaml
530
+ */
531
+ function updateJobVersionInYaml(jobName, newVersion) {
532
+ try {
533
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
534
+ const yamlContent = fs.readFileSync(yamlPath, 'utf8');
535
+ const config = yaml.load(yamlContent);
536
+ if (config.jobs) {
537
+ const job = config.jobs.find((j) => j.name === jobName);
538
+ if (job) {
539
+ job.version = newVersion;
540
+ fs.writeFileSync(yamlPath, yaml.dump(config, { indent: 2, lineWidth: 120, noRefs: true }));
541
+ }
542
+ }
543
+ }
544
+ catch (error) {
545
+ console.warn('⚠️ Could not update job version in YAML:', error);
546
+ }
547
+ }
548
+ /**
549
+ * Push job version to the server (simplified version)
550
+ */
551
+ async function pushJobVersion() {
552
+ try {
553
+ writeProgress("📦 Compiling project...");
554
+ await compileCommand();
555
+ writeSuccess("✅ Compilation complete");
67
556
  const apiKey = await loadApiKey();
68
557
  if (!apiKey) {
69
558
  console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
70
559
  process.exit(1);
71
560
  }
72
- const userData = await checkApiKey(apiKey);
73
- writeProgress("✅ Authenticated");
74
- // Step 5: Compile the skill (always compile to ensure up-to-date, or if version was updated)
75
- writeProgress("🔄 Compiling skill...");
561
+ await checkApiKey(apiKey);
562
+ const config = readSkillConfig();
563
+ if (!config?.agent?.agentId) {
564
+ console.error(" No agent ID found in lua.skill.yaml.");
565
+ process.exit(1);
566
+ }
567
+ const jobs = config.jobs || [];
568
+ if (jobs.length === 0) {
569
+ console.error("❌ No jobs found in lua.skill.yaml.");
570
+ console.log("💡 Make sure you have created a job using LuaJob in your code.");
571
+ process.exit(1);
572
+ }
573
+ const jobAnswer = await safePrompt([
574
+ {
575
+ type: 'list',
576
+ name: 'selectedJob',
577
+ message: 'Select a job to push:',
578
+ choices: jobs.map((job) => ({
579
+ name: `${job.name} (v${job.version})`,
580
+ value: job
581
+ }))
582
+ }
583
+ ]);
584
+ if (!jobAnswer)
585
+ return;
586
+ const selectedJob = jobAnswer.selectedJob;
587
+ // Load bundled job data
588
+ const bundledJobsPath = path.join(process.cwd(), 'dist', 'jobs.json');
589
+ let bundledJobData = {};
590
+ if (fs.existsSync(bundledJobsPath)) {
591
+ const bundledJobs = JSON.parse(fs.readFileSync(bundledJobsPath, 'utf8'));
592
+ bundledJobData = bundledJobs.find((j) => j.name === selectedJob.name) || {};
593
+ // Validate that the job exists in compiled code
594
+ if (!bundledJobData || Object.keys(bundledJobData).length === 0) {
595
+ console.error(`\n❌ Job "${selectedJob.name}" not found in compiled code.`);
596
+ console.log("💡 This job may have been removed or commented out in your code.");
597
+ console.log("💡 Please uncomment the job or remove it from lua.skill.yaml.");
598
+ process.exit(1);
599
+ }
600
+ }
601
+ else {
602
+ console.error('❌ Bundled job data not found.');
603
+ console.log("💡 Please ensure your job code is properly compiled.");
604
+ process.exit(1);
605
+ }
606
+ // Confirm or update version (same as skills and webhooks)
607
+ const confirmedVersion = await promptVersionConfirmOrUpdate(selectedJob.version);
608
+ let versionToPush = confirmedVersion;
609
+ if (confirmedVersion !== selectedJob.version) {
610
+ writeInfo(`📝 Updating version from ${selectedJob.version} to ${confirmedVersion}`);
611
+ updateJobVersionInYaml(selectedJob.name, confirmedVersion);
612
+ versionToPush = confirmedVersion;
613
+ }
614
+ const jobData = {
615
+ name: selectedJob.name,
616
+ version: versionToPush,
617
+ description: bundledJobData.description || selectedJob.description || `Job: ${selectedJob.name}`,
618
+ context: bundledJobData.context || selectedJob.context || '',
619
+ jobId: selectedJob.jobId,
620
+ schedule: bundledJobData.schedule || selectedJob.schedule,
621
+ timeout: bundledJobData.timeout,
622
+ retry: bundledJobData.retry,
623
+ code: bundledJobData.code, // Bundled and compressed code for execution
624
+ executeFunction: bundledJobData.executeFunction, // Raw function for display/editing
625
+ metadata: bundledJobData.metadata || selectedJob.metadata // Optional metadata
626
+ };
627
+ writeProgress(`\n🚀 Pushing ${selectedJob.name} v${selectedJob.version} to server...`);
628
+ const response = await fetch(`${BASE_URLS.API}/developer/jobs/${config.agent.agentId}/${selectedJob.jobId}/version`, {
629
+ method: 'POST',
630
+ headers: {
631
+ 'Authorization': `Bearer ${apiKey}`,
632
+ 'Content-Type': 'application/json'
633
+ },
634
+ body: JSON.stringify(jobData)
635
+ });
636
+ if (!response.ok) {
637
+ const errorText = await response.text();
638
+ console.error(`\n❌ Push Error: ${response.status} - ${errorText}\n`);
639
+ throw new Error(`HTTP error! status: ${response.status}`);
640
+ }
641
+ writeSuccess(`\n✅ Successfully pushed ${selectedJob.name} v${versionToPush}\n`);
642
+ // Ask if user wants to deploy now
643
+ const deployAnswer = await safePrompt([
644
+ {
645
+ type: 'confirm',
646
+ name: 'deployNow',
647
+ message: 'Would you like to deploy this job version to production now?',
648
+ default: false
649
+ }
650
+ ]);
651
+ if (deployAnswer && deployAnswer.deployNow) {
652
+ await deployJobVersionAfterPush(apiKey, config.agent.agentId, selectedJob, versionToPush);
653
+ }
654
+ else {
655
+ writeInfo("💡 You can deploy this version later using: lua jobs production");
656
+ }
657
+ }
658
+ catch (error) {
659
+ console.error('❌ Error pushing job version:', error);
660
+ process.exit(1);
661
+ }
662
+ }
663
+ /**
664
+ * Deploy a job version immediately after pushing
665
+ */
666
+ async function deployJobVersionAfterPush(apiKey, agentId, selectedJob, pushedVersion) {
667
+ try {
668
+ // Show warning
669
+ console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
670
+ console.log("⚠️ This will affect ALL users immediately.\n");
671
+ const confirmAnswer = await safePrompt([
672
+ {
673
+ type: 'confirm',
674
+ name: 'confirm',
675
+ message: 'Are you absolutely sure you want to deploy?',
676
+ default: false
677
+ }
678
+ ]);
679
+ if (!confirmAnswer || !confirmAnswer.confirm) {
680
+ console.log("\n❌ Deployment cancelled. Version pushed but not deployed.\n");
681
+ return;
682
+ }
683
+ writeProgress("🔄 Publishing job version...");
684
+ const publishResponse = await fetch(`${BASE_URLS.API}/developer/jobs/${agentId}/${selectedJob.jobId}/${pushedVersion}/publish`, {
685
+ method: 'POST',
686
+ headers: {
687
+ 'Authorization': `Bearer ${apiKey}`,
688
+ 'Content-Type': 'application/json'
689
+ },
690
+ body: JSON.stringify({})
691
+ });
692
+ if (!publishResponse.ok) {
693
+ const errorText = await publishResponse.text();
694
+ console.error(`\n❌ Deploy Error: ${publishResponse.status} - ${errorText}\n`);
695
+ throw new Error(`HTTP error! status: ${publishResponse.status}`);
696
+ }
697
+ writeSuccess(`\n✅ Job ${selectedJob.name} v${pushedVersion} deployed successfully to production\n`);
698
+ writeInfo("💡 The new job version is now active and will run on schedule.");
699
+ }
700
+ catch (error) {
701
+ console.error('\n❌ Error deploying job version:', error);
702
+ console.log('💡 You can deploy later using: lua jobs production\n');
703
+ }
704
+ }
705
+ /**
706
+ * Push preprocessor version to the server.
707
+ */
708
+ async function pushPreProcessorVersion() {
709
+ try {
710
+ writeProgress("📦 Compiling project...");
76
711
  await compileCommand();
77
- // Step 6: Validate deploy data for the selected skill
78
- const deployData = readDeployJson();
79
- validateDeployData(deployData, selectedSkill);
80
- // Step 7: Extract the specific skill's deploy data
81
- const skillDeployData = getSkillDeployData(deployData, selectedSkill.name);
82
- // Step 8: Push version to server
83
- const agentId = config.agent.agentId;
84
- const skillId = selectedSkill.skillId;
85
- writeProgress("🔄 Pushing version to server...");
86
- const result = await pushVersion(apiKey, agentId, skillId, skillDeployData);
87
- if (result.success && result.data) {
88
- const pushedVersion = result.data.version;
89
- writeSuccess(`✅ Version ${pushedVersion} of "${selectedSkill.name}" pushed successfully`);
90
- // Update YAML with the version returned from server (in case it's different)
91
- if (pushedVersion !== selectedSkill.version) {
92
- writeInfo(`📝 Updating YAML with server version: ${pushedVersion}`);
93
- updateSkillVersionInYaml(selectedSkill.name, pushedVersion);
94
- selectedSkill.version = pushedVersion;
95
- }
96
- // Ask if user wants to deploy now
97
- const deployAnswer = await safePrompt([
712
+ writeSuccess("✅ Compilation complete");
713
+ const apiKey = await loadApiKey();
714
+ if (!apiKey) {
715
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
716
+ process.exit(1);
717
+ }
718
+ await checkApiKey(apiKey);
719
+ const config = readSkillConfig();
720
+ if (!config?.agent?.agentId) {
721
+ console.error("❌ No agent ID found in lua.skill.yaml.");
722
+ process.exit(1);
723
+ }
724
+ const preprocessors = config.preprocessors || [];
725
+ if (preprocessors.length === 0) {
726
+ console.error("❌ No preprocessors found in lua.skill.yaml.");
727
+ process.exit(1);
728
+ }
729
+ // Load bundled preprocessor data
730
+ const preprocessorsPath = path.join(process.cwd(), 'dist', 'preprocessors.json');
731
+ let bundledData = {};
732
+ if (fs.existsSync(preprocessorsPath)) {
733
+ const bundled = JSON.parse(fs.readFileSync(preprocessorsPath, 'utf8'));
734
+ // Select preprocessor
735
+ const answer = await safePrompt([
98
736
  {
99
- type: 'confirm',
100
- name: 'deployNow',
101
- message: 'Would you like to deploy this version to production now?',
102
- default: false
737
+ type: 'list',
738
+ name: 'selected',
739
+ message: 'Select a preprocessor to push:',
740
+ choices: preprocessors.map((p) => ({
741
+ name: `${p.name} (v${p.version})`,
742
+ value: p
743
+ }))
103
744
  }
104
745
  ]);
105
- if (deployAnswer && deployAnswer.deployNow) {
106
- await deployVersionAfterPush(apiKey, config.agent.agentId, selectedSkill, pushedVersion);
746
+ if (!answer)
747
+ return;
748
+ const selected = answer.selected;
749
+ bundledData = bundled.find((p) => p.name === selected.name) || {};
750
+ if (!bundledData || !bundledData.code) {
751
+ console.error(`❌ PreProcessor "${selected.name}" not found in compiled code.`);
752
+ console.log("💡 Please uncomment the preprocessor or remove it from lua.skill.yaml.");
753
+ process.exit(1);
754
+ }
755
+ // Version confirmation
756
+ const confirmedVersion = await promptVersionConfirmOrUpdate(selected.version);
757
+ if (confirmedVersion !== selected.version) {
758
+ writeInfo(`📝 Updating version from ${selected.version} to ${confirmedVersion}`);
759
+ updateProcessorVersionInYaml('preprocessors', selected.name, confirmedVersion);
760
+ }
761
+ const versionData = {
762
+ name: selected.name,
763
+ version: confirmedVersion,
764
+ description: bundledData.description || selected.description,
765
+ context: bundledData.context || selected.context,
766
+ preprocessorId: selected.preprocessorId,
767
+ code: bundledData.code,
768
+ executeFunction: bundledData.executeFunction
769
+ };
770
+ writeProgress(`\n🚀 Pushing ${selected.name} v${confirmedVersion}...`);
771
+ const api = new PreProcessorApi(BASE_URLS.API, apiKey, config.agent.agentId);
772
+ const result = await api.pushPreProcessor(selected.preprocessorId, versionData);
773
+ if (result.success) {
774
+ writeSuccess(`\n✅ PreProcessor "${selected.name}" v${confirmedVersion} pushed successfully\n`);
775
+ // Ask if user wants to deploy now
776
+ const deployAnswer = await safePrompt([
777
+ {
778
+ type: 'confirm',
779
+ name: 'deployNow',
780
+ message: 'Would you like to deploy this preprocessor version to production now?',
781
+ default: false
782
+ }
783
+ ]);
784
+ if (deployAnswer && deployAnswer.deployNow) {
785
+ await deployPreProcessorVersionAfterPush(apiKey, config.agent.agentId, selected, confirmedVersion);
786
+ }
787
+ else {
788
+ writeInfo("💡 You can deploy this version later using: lua preprocessors production");
789
+ }
790
+ }
791
+ else {
792
+ console.error(`❌ Failed to push: ${result.error?.message}`);
107
793
  }
108
794
  }
109
- else if (result.error) {
110
- console.error(`❌ ${result.error.message}`);
795
+ }
796
+ catch (error) {
797
+ console.error('❌ Error pushing preprocessor:', error);
798
+ process.exit(1);
799
+ }
800
+ }
801
+ /**
802
+ * Deploy a preprocessor version immediately after pushing
803
+ */
804
+ async function deployPreProcessorVersionAfterPush(apiKey, agentId, selected, pushedVersion) {
805
+ try {
806
+ // Show warning
807
+ console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
808
+ console.log("⚠️ This will affect ALL users immediately.\n");
809
+ const confirmAnswer = await safePrompt([
810
+ {
811
+ type: 'confirm',
812
+ name: 'confirm',
813
+ message: 'Are you absolutely sure you want to deploy?',
814
+ default: false
815
+ }
816
+ ]);
817
+ if (!confirmAnswer || !confirmAnswer.confirm) {
818
+ console.log("\n❌ Deployment cancelled. Version pushed but not deployed.\n");
819
+ return;
820
+ }
821
+ writeProgress("🔄 Publishing preprocessor version...");
822
+ const api = new PreProcessorApi(BASE_URLS.API, apiKey, agentId);
823
+ const publishResponse = await api.publishPreProcessorVersion(selected.preprocessorId, pushedVersion);
824
+ if (!publishResponse.success) {
825
+ console.error(`\n❌ Deploy Error: ${publishResponse.error?.message || 'Unknown error'}\n`);
826
+ throw new Error(publishResponse.error?.message || 'Failed to publish preprocessor version');
827
+ }
828
+ writeSuccess(`\n✅ PreProcessor ${selected.name} v${pushedVersion} deployed successfully to production\n`);
829
+ writeInfo("💡 The new preprocessor version is now active for all users.");
830
+ }
831
+ catch (error) {
832
+ console.error('\n❌ Error deploying preprocessor version:', error);
833
+ console.log('💡 You can deploy later using: lua preprocessors production\n');
834
+ }
835
+ }
836
+ /**
837
+ * Push postprocessor version to the server.
838
+ */
839
+ async function pushPostProcessorVersion() {
840
+ try {
841
+ writeProgress("📦 Compiling project...");
842
+ await compileCommand();
843
+ writeSuccess("✅ Compilation complete");
844
+ const apiKey = await loadApiKey();
845
+ if (!apiKey) {
846
+ console.error("❌ No API key found. Please run 'lua auth configure' to set up your API key.");
111
847
  process.exit(1);
112
848
  }
113
- else {
114
- console.error("❌ Failed to push version. Please try again.");
849
+ await checkApiKey(apiKey);
850
+ const config = readSkillConfig();
851
+ if (!config?.agent?.agentId) {
852
+ console.error("❌ No agent ID found in lua.skill.yaml.");
115
853
  process.exit(1);
116
854
  }
117
- }, "push");
855
+ const postprocessors = config.postprocessors || [];
856
+ if (postprocessors.length === 0) {
857
+ console.error("❌ No postprocessors found in lua.skill.yaml.");
858
+ process.exit(1);
859
+ }
860
+ // Load bundled postprocessor data
861
+ const postprocessorsPath = path.join(process.cwd(), 'dist', 'postprocessors.json');
862
+ let bundledData = {};
863
+ if (fs.existsSync(postprocessorsPath)) {
864
+ const bundled = JSON.parse(fs.readFileSync(postprocessorsPath, 'utf8'));
865
+ // Select postprocessor
866
+ const answer = await safePrompt([
867
+ {
868
+ type: 'list',
869
+ name: 'selected',
870
+ message: 'Select a postprocessor to push:',
871
+ choices: postprocessors.map((p) => ({
872
+ name: `${p.name} (v${p.version})`,
873
+ value: p
874
+ }))
875
+ }
876
+ ]);
877
+ if (!answer)
878
+ return;
879
+ const selected = answer.selected;
880
+ bundledData = bundled.find((p) => p.name === selected.name) || {};
881
+ if (!bundledData || !bundledData.code) {
882
+ console.error(`❌ PostProcessor "${selected.name}" not found in compiled code.`);
883
+ console.log("💡 Please uncomment the postprocessor or remove it from lua.skill.yaml.");
884
+ process.exit(1);
885
+ }
886
+ // Version confirmation
887
+ const confirmedVersion = await promptVersionConfirmOrUpdate(selected.version);
888
+ if (confirmedVersion !== selected.version) {
889
+ writeInfo(`📝 Updating version from ${selected.version} to ${confirmedVersion}`);
890
+ updateProcessorVersionInYaml('postprocessors', selected.name, confirmedVersion);
891
+ }
892
+ const versionData = {
893
+ name: selected.name,
894
+ version: confirmedVersion,
895
+ description: bundledData.description || selected.description,
896
+ context: bundledData.context || selected.context,
897
+ postprocessorId: selected.postprocessorId,
898
+ code: bundledData.code,
899
+ executeFunction: bundledData.executeFunction
900
+ };
901
+ writeProgress(`\n🚀 Pushing ${selected.name} v${confirmedVersion}...`);
902
+ const api = new PostProcessorApi(BASE_URLS.API, apiKey, config.agent.agentId);
903
+ const result = await api.pushPostProcessor(selected.postprocessorId, versionData);
904
+ if (result.success) {
905
+ writeSuccess(`\n✅ PostProcessor "${selected.name}" v${confirmedVersion} pushed successfully\n`);
906
+ // Ask if user wants to deploy now
907
+ const deployAnswer = await safePrompt([
908
+ {
909
+ type: 'confirm',
910
+ name: 'deployNow',
911
+ message: 'Would you like to deploy this postprocessor version to production now?',
912
+ default: false
913
+ }
914
+ ]);
915
+ if (deployAnswer && deployAnswer.deployNow) {
916
+ await deployPostProcessorVersionAfterPush(apiKey, config.agent.agentId, selected, confirmedVersion);
917
+ }
918
+ else {
919
+ writeInfo("💡 You can deploy this version later using: lua postprocessors production");
920
+ }
921
+ }
922
+ else {
923
+ console.error(`❌ Failed to push: ${result.error?.message}`);
924
+ }
925
+ }
926
+ }
927
+ catch (error) {
928
+ console.error('❌ Error pushing postprocessor:', error);
929
+ process.exit(1);
930
+ }
931
+ }
932
+ /**
933
+ * Deploy a postprocessor version immediately after pushing
934
+ */
935
+ async function deployPostProcessorVersionAfterPush(apiKey, agentId, selected, pushedVersion) {
936
+ try {
937
+ // Show warning
938
+ console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
939
+ console.log("⚠️ This will affect ALL users immediately.\n");
940
+ const confirmAnswer = await safePrompt([
941
+ {
942
+ type: 'confirm',
943
+ name: 'confirm',
944
+ message: 'Are you absolutely sure you want to deploy?',
945
+ default: false
946
+ }
947
+ ]);
948
+ if (!confirmAnswer || !confirmAnswer.confirm) {
949
+ console.log("\n❌ Deployment cancelled. Version pushed but not deployed.\n");
950
+ return;
951
+ }
952
+ writeProgress("🔄 Publishing postprocessor version...");
953
+ const api = new PostProcessorApi(BASE_URLS.API, apiKey, agentId);
954
+ const publishResponse = await api.publishPostProcessorVersion(selected.postprocessorId, pushedVersion);
955
+ if (!publishResponse.success) {
956
+ console.error(`\n❌ Deploy Error: ${publishResponse.error?.message || 'Unknown error'}\n`);
957
+ throw new Error(publishResponse.error?.message || 'Failed to publish postprocessor version');
958
+ }
959
+ writeSuccess(`\n✅ PostProcessor ${selected.name} v${pushedVersion} deployed successfully to production\n`);
960
+ writeInfo("💡 The new postprocessor version is now active for all users.");
961
+ }
962
+ catch (error) {
963
+ console.error('\n❌ Error deploying postprocessor version:', error);
964
+ console.log('💡 You can deploy later using: lua postprocessors production\n');
965
+ }
966
+ }
967
+ /**
968
+ * Updates processor version in lua.skill.yaml
969
+ */
970
+ function updateProcessorVersionInYaml(processorType, processorName, newVersion) {
971
+ try {
972
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
973
+ const yamlContent = fs.readFileSync(yamlPath, 'utf8');
974
+ const config = yaml.load(yamlContent);
975
+ if (config[processorType]) {
976
+ const processor = config[processorType].find((p) => p.name === processorName);
977
+ if (processor) {
978
+ processor.version = newVersion;
979
+ fs.writeFileSync(yamlPath, yaml.dump(config, { indent: 2, lineWidth: 120, noRefs: true }));
980
+ }
981
+ }
982
+ }
983
+ catch (error) {
984
+ console.warn('⚠️ Could not update processor version in YAML:', error);
985
+ }
118
986
  }
119
987
  /**
120
988
  * Deploy a version immediately after pushing