lua-cli 3.2.0 → 3.3.0

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 (102) hide show
  1. package/dist/api/logs.api.service.d.ts +1 -1
  2. package/dist/api/logs.api.service.js.map +1 -1
  3. package/dist/api/products.api.service.d.ts +17 -5
  4. package/dist/api/products.api.service.js +21 -9
  5. package/dist/api/products.api.service.js.map +1 -1
  6. package/dist/api-exports.d.ts +17 -5
  7. package/dist/api-exports.js +20 -5
  8. package/dist/api-exports.js.map +1 -1
  9. package/dist/cli/command-definitions.js +320 -88
  10. package/dist/cli/command-definitions.js.map +1 -1
  11. package/dist/commands/apiKey.d.ts +5 -2
  12. package/dist/commands/apiKey.js +8 -2
  13. package/dist/commands/apiKey.js.map +1 -1
  14. package/dist/commands/channels.d.ts +4 -9
  15. package/dist/commands/channels.js +140 -84
  16. package/dist/commands/channels.js.map +1 -1
  17. package/dist/commands/chat.d.ts +4 -2
  18. package/dist/commands/chat.js +126 -32
  19. package/dist/commands/chat.js.map +1 -1
  20. package/dist/commands/chatClear.d.ts +3 -2
  21. package/dist/commands/chatClear.js +16 -15
  22. package/dist/commands/chatClear.js.map +1 -1
  23. package/dist/commands/compile.js +67 -4
  24. package/dist/commands/compile.js.map +1 -1
  25. package/dist/commands/deploy.d.ts +5 -24
  26. package/dist/commands/deploy.js +75 -48
  27. package/dist/commands/deploy.js.map +1 -1
  28. package/dist/commands/destroy.d.ts +5 -2
  29. package/dist/commands/destroy.js +14 -2
  30. package/dist/commands/destroy.js.map +1 -1
  31. package/dist/commands/env.d.ts +3 -1
  32. package/dist/commands/env.js +322 -122
  33. package/dist/commands/env.js.map +1 -1
  34. package/dist/commands/features.d.ts +5 -9
  35. package/dist/commands/features.js +249 -129
  36. package/dist/commands/features.js.map +1 -1
  37. package/dist/commands/init.d.ts +7 -1
  38. package/dist/commands/init.js +242 -59
  39. package/dist/commands/init.js.map +1 -1
  40. package/dist/commands/jobs.d.ts +5 -13
  41. package/dist/commands/jobs.js +449 -364
  42. package/dist/commands/jobs.js.map +1 -1
  43. package/dist/commands/logs.d.ts +5 -10
  44. package/dist/commands/logs.js +259 -103
  45. package/dist/commands/logs.js.map +1 -1
  46. package/dist/commands/marketplace.d.ts +23 -2
  47. package/dist/commands/marketplace.js +530 -7
  48. package/dist/commands/marketplace.js.map +1 -1
  49. package/dist/commands/mcp.d.ts +5 -11
  50. package/dist/commands/mcp.js +303 -288
  51. package/dist/commands/mcp.js.map +1 -1
  52. package/dist/commands/persona.d.ts +5 -9
  53. package/dist/commands/persona.js +349 -232
  54. package/dist/commands/persona.js.map +1 -1
  55. package/dist/commands/postprocessors.d.ts +6 -2
  56. package/dist/commands/postprocessors.js +387 -280
  57. package/dist/commands/postprocessors.js.map +1 -1
  58. package/dist/commands/preprocessors.d.ts +6 -2
  59. package/dist/commands/preprocessors.js +387 -280
  60. package/dist/commands/preprocessors.js.map +1 -1
  61. package/dist/commands/production.d.ts +5 -8
  62. package/dist/commands/production.js +317 -228
  63. package/dist/commands/production.js.map +1 -1
  64. package/dist/commands/push.js +385 -427
  65. package/dist/commands/push.js.map +1 -1
  66. package/dist/commands/resources.d.ts +5 -10
  67. package/dist/commands/resources.js +219 -154
  68. package/dist/commands/resources.js.map +1 -1
  69. package/dist/commands/skills.d.ts +5 -9
  70. package/dist/commands/skills.js +354 -277
  71. package/dist/commands/skills.js.map +1 -1
  72. package/dist/commands/sync.d.ts +10 -8
  73. package/dist/commands/sync.js +110 -19
  74. package/dist/commands/sync.js.map +1 -1
  75. package/dist/commands/test.d.ts +1 -11
  76. package/dist/commands/test.js +395 -438
  77. package/dist/commands/test.js.map +1 -1
  78. package/dist/commands/webhooks.d.ts +5 -11
  79. package/dist/commands/webhooks.js +356 -290
  80. package/dist/commands/webhooks.js.map +1 -1
  81. package/dist/interfaces/mcp.d.ts +28 -1
  82. package/dist/interfaces/product.d.ts +26 -0
  83. package/dist/types/api-contracts.d.ts +8 -4
  84. package/dist/types/index.d.ts +1 -1
  85. package/dist/types/index.js.map +1 -1
  86. package/dist/types/skill.d.ts +92 -17
  87. package/dist/types/skill.js +15 -11
  88. package/dist/types/skill.js.map +1 -1
  89. package/dist/utils/bundling.d.ts +17 -0
  90. package/dist/utils/bundling.js +96 -0
  91. package/dist/utils/bundling.js.map +1 -1
  92. package/dist/utils/compile.d.ts +4 -0
  93. package/dist/utils/compile.js +5 -0
  94. package/dist/utils/compile.js.map +1 -1
  95. package/dist/utils/dev-helpers.d.ts +3 -2
  96. package/dist/utils/dev-helpers.js +3 -5
  97. package/dist/utils/dev-helpers.js.map +1 -1
  98. package/dist/utils/mcp-server-management.d.ts +1 -1
  99. package/dist/utils/mcp-server-management.js +13 -14
  100. package/dist/utils/mcp-server-management.js.map +1 -1
  101. package/package.json +1 -1
  102. package/template/package.json +1 -1
@@ -9,23 +9,22 @@ import { BASE_URLS } from '../config/constants.js';
9
9
  import { safePrompt } from '../utils/prompt-handler.js';
10
10
  import { validateConfig, validateAgentConfig, } from '../utils/dev-helpers.js';
11
11
  import JobApi from '../api/job.api.service.js';
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+ // Main Command Entry
14
+ // ─────────────────────────────────────────────────────────────────────────────
12
15
  /**
13
- * Main jobs command - manages agent jobs
16
+ * Main jobs command - manages agent jobs.
14
17
  *
15
- * Features:
16
- * - View deployed jobs
17
- * - View job versions
18
- * - Deploy job versions to production
19
- * - Activate/deactivate, pause/resume jobs
20
- * - Trigger jobs manually
21
- * - View execution history
22
- *
23
- * Note: For local testing, use `lua test job`
24
- *
25
- * @returns Promise that resolves when command completes
18
+ * Supports both interactive and non-interactive modes:
19
+ * - Interactive: prompts for action and job selection
20
+ * - Non-interactive: use action argument with -i and -v flags
26
21
  */
27
- export async function jobsCommand() {
22
+ export async function jobsCommand(action, cmdObj) {
28
23
  return withErrorHandling(async () => {
24
+ const options = {
25
+ jobName: cmdObj?.jobName || null,
26
+ jobVersion: cmdObj?.jobVersion || null,
27
+ };
29
28
  // Step 1: Load configuration
30
29
  const config = readSkillConfig();
31
30
  validateConfig(config);
@@ -44,13 +43,364 @@ export async function jobsCommand() {
44
43
  agentId,
45
44
  apiKey,
46
45
  };
47
- // Start job management
48
- await manageProductionJobs(context, config);
46
+ // Determine if non-interactive mode
47
+ if (action) {
48
+ await executeNonInteractive(context, config, action, options);
49
+ }
50
+ else {
51
+ await manageProductionJobs(context, config);
52
+ }
49
53
  }, "jobs");
50
54
  }
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+ // Shared Core Functions (used by both interactive and non-interactive modes)
57
+ // ─────────────────────────────────────────────────────────────────────────────
58
+ /**
59
+ * Fetches and displays all jobs with their current status.
60
+ */
61
+ async function displayJobsCore(context, jobs) {
62
+ console.log("\n" + "=".repeat(60));
63
+ console.log("āš™ļø Production Jobs");
64
+ console.log("=".repeat(60) + "\n");
65
+ for (const job of jobs) {
66
+ try {
67
+ const response = await fetch(`${BASE_URLS.API}/developer/jobs/${context.agentId}/${job.jobId}`, {
68
+ method: 'GET',
69
+ headers: {
70
+ 'Authorization': `Bearer ${context.apiKey}`,
71
+ 'Content-Type': 'application/json'
72
+ }
73
+ });
74
+ if (response.ok) {
75
+ const data = await response.json();
76
+ const jobData = data.data || data;
77
+ console.log(`ā° ${job.name}`);
78
+ console.log(` Job ID: ${job.jobId}`);
79
+ console.log(` Status: ${jobData.status || 'active'}`);
80
+ if (jobData.schedule) {
81
+ console.log(` Schedule: ${formatSchedule(jobData.schedule)}`);
82
+ }
83
+ if (jobData.lastRunAt) {
84
+ console.log(` Last Run: ${new Date(jobData.lastRunAt).toLocaleString()}`);
85
+ }
86
+ if (jobData.nextRunAt) {
87
+ console.log(` Next Run: ${new Date(jobData.nextRunAt).toLocaleString()}`);
88
+ }
89
+ console.log();
90
+ }
91
+ else {
92
+ displayJobError(job, "Unable to fetch info");
93
+ }
94
+ }
95
+ catch {
96
+ displayJobError(job, "Error loading info");
97
+ }
98
+ }
99
+ console.log("=".repeat(60));
100
+ }
101
+ function displayJobError(job, status) {
102
+ console.log(`ā° ${job.name}`);
103
+ console.log(` Job ID: ${job.jobId}`);
104
+ console.log(` Status: ${status}`);
105
+ console.log();
106
+ }
107
+ /**
108
+ * Fetches and returns sorted job versions.
109
+ */
110
+ async function fetchVersionsCore(context, job) {
111
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
112
+ const response = await jobApi.getJobVersions(job.jobId);
113
+ if (!response.success || !response.data) {
114
+ console.error(`āŒ Failed to fetch versions: ${response.error?.message || 'Unknown error'}`);
115
+ return null;
116
+ }
117
+ return response.data.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
118
+ }
119
+ /**
120
+ * Displays versions for a job.
121
+ */
122
+ function displayVersionsCore(job, versions) {
123
+ console.log("\n" + "=".repeat(60));
124
+ console.log(`šŸ“œ Versions for ${job.name}`);
125
+ console.log("=".repeat(60) + "\n");
126
+ versions.forEach((version) => {
127
+ const isActive = version.jobId === job.activeVersionId;
128
+ console.log(`${isActive ? '⭐' : ' '} Version ${version.version}`);
129
+ console.log(` Created: ${new Date(version.createdAt).toLocaleString()}`);
130
+ if (version.schedule) {
131
+ console.log(` Schedule: ${formatSchedule(version.schedule)}`);
132
+ }
133
+ console.log();
134
+ });
135
+ console.log("=".repeat(60));
136
+ }
137
+ /**
138
+ * Deploys a specific version of a job.
139
+ */
140
+ async function deployVersionCore(context, job, version) {
141
+ writeProgress(`šŸ”„ Deploying version ${version} of "${job.name}"...`);
142
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
143
+ const deployResponse = await jobApi.publishJobVersion(job.jobId, version);
144
+ if (!deployResponse.success) {
145
+ console.error(`āŒ Deploy Error: ${deployResponse.error?.message || 'Unknown error'}`);
146
+ return false;
147
+ }
148
+ writeSuccess(`āœ… Version ${version} of "${job.name}" deployed successfully`);
149
+ writeInfo("šŸ’” The new version is now active and will run on schedule.");
150
+ return true;
151
+ }
152
+ /**
153
+ * Activates a job.
154
+ */
155
+ async function activateJobCore(context, job) {
156
+ writeProgress(`šŸ”„ Activating job "${job.name}"...`);
157
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
158
+ const response = await jobApi.activateJob(job.jobId);
159
+ if (response.success) {
160
+ writeSuccess(`āœ… Job "${job.name}" activated successfully`);
161
+ writeInfo("šŸ’” The job is now enabled and will run on schedule.");
162
+ return true;
163
+ }
164
+ else {
165
+ console.error(`āŒ Failed to activate job: ${response.error?.message || 'Unknown error'}`);
166
+ return false;
167
+ }
168
+ }
169
+ /**
170
+ * Deactivates a job.
171
+ */
172
+ async function deactivateJobCore(context, job) {
173
+ writeProgress(`šŸ”„ Deactivating job "${job.name}"...`);
174
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
175
+ const response = await jobApi.deactivateJob(job.jobId);
176
+ if (response.success) {
177
+ writeSuccess(`āœ… Job "${job.name}" deactivated successfully`);
178
+ writeInfo("šŸ’” The job is now disabled and will not run.");
179
+ return true;
180
+ }
181
+ else {
182
+ console.error(`āŒ Failed to deactivate job: ${response.error?.message || 'Unknown error'}`);
183
+ return false;
184
+ }
185
+ }
186
+ /**
187
+ * Triggers a job execution.
188
+ */
189
+ async function triggerJobCore(context, job) {
190
+ writeProgress(`⚔ Triggering job "${job.name}"...`);
191
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
192
+ const response = await jobApi.triggerJob(job.jobId);
193
+ if (response.success && response.data) {
194
+ writeSuccess(`āœ… Job "${job.name}" triggered successfully`);
195
+ writeInfo(`šŸ“‹ Execution ID: ${response.data.id || 'N/A'}`);
196
+ return true;
197
+ }
198
+ else {
199
+ console.error(`āŒ Failed to trigger job: ${response.error?.message || 'Unknown error'}`);
200
+ return false;
201
+ }
202
+ }
203
+ /**
204
+ * Fetches and displays execution history for a job.
205
+ */
206
+ async function fetchAndDisplayHistoryCore(context, job) {
207
+ const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
208
+ const response = await jobApi.getJobExecutions(job.jobId, 20);
209
+ if (!response.success || !response.data) {
210
+ console.error(`āŒ Failed to fetch history: ${response.error?.message || 'Unknown error'}`);
211
+ return false;
212
+ }
213
+ const executions = response.data.executions || [];
214
+ if (executions.length === 0) {
215
+ console.log(`ā„¹ļø No execution history found for ${job.name}.`);
216
+ return true;
217
+ }
218
+ console.log("\n" + "=".repeat(60));
219
+ console.log(`šŸ“Š Execution History for ${job.name}`);
220
+ console.log("=".repeat(60) + "\n");
221
+ executions.forEach((execution, index) => {
222
+ const startedAt = new Date(execution.startedAt);
223
+ const status = execution.status;
224
+ const statusEmoji = status === 'completed' ? 'āœ…' :
225
+ status === 'failed' ? 'āŒ' :
226
+ status === 'running' ? 'šŸ”„' : 'ā³';
227
+ console.log(`${index + 1}. ${statusEmoji} ${status.toUpperCase()}`);
228
+ console.log(` Execution ID: ${execution.executionId}`);
229
+ console.log(` Started: ${startedAt.toLocaleString()}`);
230
+ if (execution.completedAt) {
231
+ const completedAt = new Date(execution.completedAt);
232
+ const duration = completedAt.getTime() - startedAt.getTime();
233
+ console.log(` Completed: ${completedAt.toLocaleString()}`);
234
+ console.log(` Duration: ${Math.round(duration / 1000)}s`);
235
+ }
236
+ if (execution.result) {
237
+ console.log(` Result: ${JSON.stringify(execution.result).substring(0, 100)}...`);
238
+ }
239
+ if (execution.error) {
240
+ console.log(` Error: ${execution.error}`);
241
+ }
242
+ console.log();
243
+ });
244
+ console.log("=".repeat(60));
245
+ console.log(`Showing last ${executions.length} executions`);
246
+ return true;
247
+ }
248
+ /**
249
+ * Resolves a version string to an actual version, supporting "latest".
250
+ */
251
+ function resolveVersion(versions, versionArg) {
252
+ if (versionArg.toLowerCase() === 'latest') {
253
+ return versions[0].version;
254
+ }
255
+ const exists = versions.some((v) => v.version === versionArg);
256
+ if (!exists) {
257
+ console.error(`āŒ Version "${versionArg}" not found`);
258
+ console.log('\nAvailable versions:');
259
+ versions.slice(0, 5).forEach((v) => console.log(` - ${v.version}`));
260
+ return null;
261
+ }
262
+ return versionArg;
263
+ }
51
264
  /**
52
- * Manage production jobs - view, deploy, pause/resume, trigger
265
+ * Prompts user to select a job from a list.
53
266
  */
267
+ async function promptJobSelection(jobs, message) {
268
+ const jobAnswer = await safePrompt([
269
+ {
270
+ type: 'list',
271
+ name: 'selectedJob',
272
+ message,
273
+ choices: jobs.map((job) => ({
274
+ name: `${job.name} (${job.jobId})`,
275
+ value: job
276
+ }))
277
+ }
278
+ ]);
279
+ return jobAnswer?.selectedJob || null;
280
+ }
281
+ /**
282
+ * Prompts user to select a version from a list.
283
+ */
284
+ async function promptVersionSelection(versions, activeVersionId) {
285
+ const versionAnswer = await safePrompt([
286
+ {
287
+ type: 'list',
288
+ name: 'selectedVersion',
289
+ message: 'Select a version to deploy:',
290
+ choices: versions.map((version) => {
291
+ const isActive = version.jobId === activeVersionId;
292
+ return {
293
+ name: `Version ${version.version} (${new Date(version.createdAt).toLocaleDateString()})${isActive ? ' ⭐ CURRENT' : ''}`,
294
+ value: version
295
+ };
296
+ })
297
+ }
298
+ ]);
299
+ return versionAnswer?.selectedVersion || null;
300
+ }
301
+ // ─────────────────────────────────────────────────────────────────────────────
302
+ // Non-Interactive Mode
303
+ // ─────────────────────────────────────────────────────────────────────────────
304
+ async function executeNonInteractive(context, config, action, options) {
305
+ const validActions = ['view', 'versions', 'deploy', 'activate', 'deactivate', 'trigger', 'history'];
306
+ const normalizedAction = action.toLowerCase();
307
+ if (!validActions.includes(normalizedAction)) {
308
+ console.error(`āŒ Invalid action: "${action}"`);
309
+ console.log('\nValid actions: view, versions, deploy, activate, deactivate, trigger, history');
310
+ console.log('\nExamples:');
311
+ console.log(' lua jobs view List all jobs');
312
+ console.log(' lua jobs trigger -i healthCheck Trigger a job');
313
+ console.log(' lua jobs deploy -i myJob -v 1.0.3 Deploy specific version');
314
+ process.exit(1);
315
+ }
316
+ const jobs = config.jobs || [];
317
+ // View action doesn't require job selection
318
+ if (normalizedAction === 'view') {
319
+ if (jobs.length === 0) {
320
+ console.log("ā„¹ļø No jobs found in configuration.");
321
+ return;
322
+ }
323
+ await displayJobsCore(context, jobs);
324
+ return;
325
+ }
326
+ // All other actions require job selection
327
+ if (!options.jobName) {
328
+ console.error(`āŒ --job-name is required for action "${normalizedAction}"`);
329
+ console.log(`\nUsage: lua jobs ${normalizedAction} --job-name <name>`);
330
+ process.exit(1);
331
+ }
332
+ const selectedJob = jobs.find((j) => j.jobId === options.jobName || j.name === options.jobName);
333
+ if (!selectedJob) {
334
+ console.error(`āŒ Job "${options.jobName}" not found`);
335
+ console.log('\nAvailable jobs:');
336
+ jobs.forEach((j) => console.log(` - ${j.name} (${j.jobId})`));
337
+ process.exit(1);
338
+ }
339
+ switch (normalizedAction) {
340
+ case 'versions': {
341
+ const versions = await fetchVersionsCore(context, selectedJob);
342
+ if (!versions)
343
+ process.exit(1);
344
+ if (versions.length === 0) {
345
+ console.log(`ā„¹ļø No versions found for ${selectedJob.name}.`);
346
+ console.log("šŸ’” Push a version first using 'lua push job'.");
347
+ return;
348
+ }
349
+ displayVersionsCore(selectedJob, versions);
350
+ break;
351
+ }
352
+ case 'deploy': {
353
+ if (!options.jobVersion) {
354
+ console.error('āŒ --job-version is required for deploy action');
355
+ console.log('\nUsage: lua jobs deploy --job-name myJob --job-version 1.0.3');
356
+ console.log(' lua jobs deploy -i myJob -v latest');
357
+ process.exit(1);
358
+ }
359
+ const versions = await fetchVersionsCore(context, selectedJob);
360
+ if (!versions)
361
+ process.exit(1);
362
+ if (versions.length === 0) {
363
+ console.error(`āŒ No versions found for ${selectedJob.name}.`);
364
+ console.log("šŸ’” Push a version first using 'lua push job'.");
365
+ process.exit(1);
366
+ }
367
+ const resolvedVersion = resolveVersion(versions, options.jobVersion);
368
+ if (!resolvedVersion)
369
+ process.exit(1);
370
+ const success = await deployVersionCore(context, selectedJob, resolvedVersion);
371
+ if (!success)
372
+ process.exit(1);
373
+ break;
374
+ }
375
+ case 'activate': {
376
+ const success = await activateJobCore(context, selectedJob);
377
+ if (!success)
378
+ process.exit(1);
379
+ break;
380
+ }
381
+ case 'deactivate': {
382
+ const success = await deactivateJobCore(context, selectedJob);
383
+ if (!success)
384
+ process.exit(1);
385
+ break;
386
+ }
387
+ case 'trigger': {
388
+ const success = await triggerJobCore(context, selectedJob);
389
+ if (!success)
390
+ process.exit(1);
391
+ break;
392
+ }
393
+ case 'history': {
394
+ const success = await fetchAndDisplayHistoryCore(context, selectedJob);
395
+ if (!success)
396
+ process.exit(1);
397
+ break;
398
+ }
399
+ }
400
+ }
401
+ // ─────────────────────────────────────────────────────────────────────────────
402
+ // Interactive Mode
403
+ // ─────────────────────────────────────────────────────────────────────────────
54
404
  async function manageProductionJobs(context, config) {
55
405
  let continueManaging = true;
56
406
  while (continueManaging) {
@@ -79,25 +429,25 @@ async function manageProductionJobs(context, config) {
79
429
  const { action } = actionAnswer;
80
430
  switch (action) {
81
431
  case 'view':
82
- await viewDeployedJobs(context, config);
432
+ await viewDeployedJobsInteractive(context, config);
83
433
  break;
84
434
  case 'versions':
85
- await viewJobVersions(context, config);
435
+ await viewJobVersionsInteractive(context, config);
86
436
  break;
87
437
  case 'deploy':
88
- await deployJobVersion(context, config);
438
+ await deployJobVersionInteractive(context, config);
89
439
  break;
90
440
  case 'activate':
91
- await activateJob(context, config);
441
+ await activateJobInteractive(context, config);
92
442
  break;
93
443
  case 'deactivate':
94
- await deactivateJob(context, config);
444
+ await deactivateJobInteractive(context, config);
95
445
  break;
96
446
  case 'trigger':
97
- await triggerJob(context, config);
447
+ await triggerJobInteractive(context, config);
98
448
  break;
99
449
  case 'history':
100
- await viewExecutionHistory(context, config);
450
+ await viewExecutionHistoryInteractive(context, config);
101
451
  break;
102
452
  case 'exit':
103
453
  continueManaging = false;
@@ -106,292 +456,115 @@ async function manageProductionJobs(context, config) {
106
456
  }
107
457
  }
108
458
  }
109
- /**
110
- * View deployed jobs in production
111
- */
112
- async function viewDeployedJobs(context, config) {
459
+ async function viewDeployedJobsInteractive(context, config) {
113
460
  writeProgress("šŸ”„ Loading job information...");
114
- try {
115
- const jobs = config.jobs || [];
116
- if (jobs.length === 0) {
117
- console.log("\nā„¹ļø No jobs found in configuration.\n");
118
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
119
- return;
120
- }
121
- console.log("\n" + "=".repeat(60));
122
- console.log("āš™ļø Production Jobs");
123
- console.log("=".repeat(60) + "\n");
124
- for (const job of jobs) {
125
- try {
126
- const response = await fetch(`${BASE_URLS.API}/developer/jobs/${context.agentId}/${job.jobId}`, {
127
- method: 'GET',
128
- headers: {
129
- 'Authorization': `Bearer ${context.apiKey}`,
130
- 'Content-Type': 'application/json'
131
- }
132
- });
133
- if (response.ok) {
134
- const data = await response.json();
135
- const jobData = data.data || data;
136
- console.log(`ā° ${job.name}`);
137
- console.log(` Job ID: ${job.jobId}`);
138
- console.log(` Status: ${jobData.status || 'active'}`);
139
- if (jobData.schedule) {
140
- console.log(` Schedule: ${formatSchedule(jobData.schedule)}`);
141
- }
142
- if (jobData.lastRunAt) {
143
- const lastRun = new Date(jobData.lastRunAt);
144
- console.log(` Last Run: ${lastRun.toLocaleString()}`);
145
- }
146
- if (jobData.nextRunAt) {
147
- const nextRun = new Date(jobData.nextRunAt);
148
- console.log(` Next Run: ${nextRun.toLocaleString()}`);
149
- }
150
- console.log();
151
- }
152
- else {
153
- console.log(`ā° ${job.name}`);
154
- console.log(` Job ID: ${job.jobId}`);
155
- console.log(` Status: Unable to fetch info`);
156
- console.log();
157
- }
158
- }
159
- catch (error) {
160
- console.log(`ā° ${job.name}`);
161
- console.log(` Job ID: ${job.jobId}`);
162
- console.log(` Status: Error loading info`);
163
- console.log();
164
- }
165
- }
166
- console.log("=".repeat(60) + "\n");
167
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
461
+ const jobs = config.jobs || [];
462
+ if (jobs.length === 0) {
463
+ console.log("\nā„¹ļø No jobs found in configuration.\n");
168
464
  }
169
- catch (error) {
170
- console.error('āŒ Error loading job information:', error);
171
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
465
+ else {
466
+ await displayJobsCore(context, jobs);
467
+ console.log();
172
468
  }
469
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
173
470
  }
174
- /**
175
- * View all versions for a specific job
176
- */
177
- async function viewJobVersions(context, config) {
471
+ async function viewJobVersionsInteractive(context, config) {
178
472
  const jobs = config.jobs || [];
179
473
  if (jobs.length === 0) {
180
474
  console.log("\nā„¹ļø No jobs found in configuration.\n");
181
475
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
182
476
  return;
183
477
  }
184
- // Prompt to select a job
185
- const jobAnswer = await safePrompt([
186
- {
187
- type: 'list',
188
- name: 'selectedJob',
189
- message: 'Select a job to view versions:',
190
- choices: jobs.map((job) => ({
191
- name: `${job.name} (${job.jobId})`,
192
- value: job
193
- }))
194
- }
195
- ]);
196
- if (!jobAnswer)
478
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to view versions:');
479
+ if (!selectedJob)
197
480
  return;
198
- const selectedJob = jobAnswer.selectedJob;
199
481
  writeProgress(`šŸ”„ Loading versions for ${selectedJob.name}...`);
200
- try {
201
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
202
- const response = await jobApi.getJobVersions(selectedJob.jobId);
203
- if (!response.success || !response.data) {
204
- throw new Error(response.error?.message || 'Failed to fetch versions');
205
- }
206
- const versions = response.data;
207
- const activeVersionId = selectedJob.activeVersionId;
208
- if (versions.length === 0) {
209
- console.log(`\nā„¹ļø No versions found for ${selectedJob.name}.\n`);
210
- console.log("šŸ’” Push a version first using 'lua push job'.\n");
211
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
212
- return;
213
- }
214
- // Sort versions by date (newest first)
215
- const sortedVersions = versions.sort((a, b) => {
216
- const dateA = new Date(a.createdAt).getTime();
217
- const dateB = new Date(b.createdAt).getTime();
218
- return dateB - dateA;
219
- });
220
- console.log("\n" + "=".repeat(60));
221
- console.log(`šŸ“œ Versions for ${selectedJob.name}`);
222
- console.log("=".repeat(60) + "\n");
223
- sortedVersions.forEach((version) => {
224
- const isActive = version.jobId === activeVersionId;
225
- const date = new Date(version.createdAt);
226
- console.log(`${isActive ? '⭐' : ' '} Version ${version.version}`);
227
- console.log(` Created: ${date.toLocaleString()}`);
228
- if (version.schedule) {
229
- console.log(` Schedule: ${formatSchedule(version.schedule)}`);
230
- }
231
- console.log();
232
- });
233
- console.log("=".repeat(60) + "\n");
482
+ const versions = await fetchVersionsCore(context, selectedJob);
483
+ if (!versions) {
234
484
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
485
+ return;
235
486
  }
236
- catch (error) {
237
- console.error('\nāŒ Error loading versions:', error);
238
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
487
+ if (versions.length === 0) {
488
+ console.log(`\nā„¹ļø No versions found for ${selectedJob.name}.\n`);
489
+ console.log("šŸ’” Push a version first using 'lua push job'.\n");
239
490
  }
491
+ else {
492
+ displayVersionsCore(selectedJob, versions);
493
+ console.log();
494
+ }
495
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
240
496
  }
241
- /**
242
- * Deploy a job version to production
243
- */
244
- async function deployJobVersion(context, config) {
497
+ async function deployJobVersionInteractive(context, config) {
245
498
  const jobs = config.jobs || [];
246
499
  if (jobs.length === 0) {
247
500
  console.log("\nā„¹ļø No jobs found in configuration.\n");
248
501
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
249
502
  return;
250
503
  }
251
- // Prompt to select a job
252
- const jobAnswer = await safePrompt([
253
- {
254
- type: 'list',
255
- name: 'selectedJob',
256
- message: 'Select a job to deploy:',
257
- choices: jobs.map((job) => ({
258
- name: `${job.name} (${job.jobId})`,
259
- value: job
260
- }))
261
- }
262
- ]);
263
- if (!jobAnswer)
504
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to deploy:');
505
+ if (!selectedJob)
264
506
  return;
265
- const selectedJob = jobAnswer.selectedJob;
266
507
  writeProgress(`šŸ”„ Loading versions for ${selectedJob.name}...`);
267
- try {
268
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
269
- const response = await jobApi.getJobVersions(selectedJob.jobId);
270
- if (!response.success || !response.data) {
271
- throw new Error(response.error?.message || 'Failed to fetch versions');
272
- }
273
- const versions = response.data;
274
- const activeVersionId = selectedJob.activeVersionId;
275
- if (versions.length === 0) {
276
- console.log(`\nā„¹ļø No versions found for ${selectedJob.name}.\n`);
277
- console.log("šŸ’” Push a version first using 'lua push job'.\n");
278
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
279
- return;
280
- }
281
- // Sort versions by date (newest first)
282
- const sortedVersions = versions.sort((a, b) => {
283
- const dateA = new Date(a.createdAt).getTime();
284
- const dateB = new Date(b.createdAt).getTime();
285
- return dateB - dateA;
286
- });
287
- // Prompt to select a version
288
- const versionAnswer = await safePrompt([
289
- {
290
- type: 'list',
291
- name: 'selectedVersion',
292
- message: 'Select a version to deploy:',
293
- choices: sortedVersions.map((version) => {
294
- const isActive = version.jobId === activeVersionId;
295
- const date = new Date(version.createdAt);
296
- return {
297
- name: `Version ${version.version} (${date.toLocaleDateString()})${isActive ? ' ⭐ CURRENT' : ''}`,
298
- value: version
299
- };
300
- })
301
- }
302
- ]);
303
- if (!versionAnswer)
304
- return;
305
- const selectedVersion = versionAnswer.selectedVersion;
306
- // Show warning
307
- console.log("\nāš ļø WARNING: You are about to deploy to PRODUCTION!");
308
- console.log("āš ļø This will affect ALL users immediately.\n");
309
- console.log(`Job: ${selectedJob.name}`);
310
- console.log(`Version: ${selectedVersion.version}`);
311
- console.log(`Schedule: ${formatSchedule(selectedJob.schedule)}\n`);
312
- const confirmAnswer = await safePrompt([
313
- {
314
- type: 'confirm',
315
- name: 'confirm',
316
- message: 'Are you absolutely sure you want to deploy this version?',
317
- default: false
318
- }
319
- ]);
320
- if (!confirmAnswer || !confirmAnswer.confirm) {
321
- console.log("\nāŒ Deployment cancelled.\n");
322
- return;
323
- }
324
- writeProgress("šŸ”„ Deploying version...");
325
- // Reuse the same jobApi instance
326
- const deployResponse = await jobApi.publishJobVersion(selectedJob.jobId, selectedVersion.version);
327
- if (!deployResponse.success) {
328
- console.error(`\nāŒ Deploy Error: ${deployResponse.error?.message || 'Unknown error'}\n`);
329
- throw new Error(deployResponse.error?.message || 'Failed to publish job version');
330
- }
331
- writeSuccess(`\nāœ… Version ${selectedVersion.version} of "${selectedJob.name}" deployed successfully to production\n`);
332
- writeInfo("šŸ’” The new version is now active and will run on schedule.");
508
+ const versions = await fetchVersionsCore(context, selectedJob);
509
+ if (!versions) {
333
510
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
511
+ return;
334
512
  }
335
- catch (error) {
336
- console.error('\nāŒ Error deploying version:', error);
513
+ if (versions.length === 0) {
514
+ console.log(`\nā„¹ļø No versions found for ${selectedJob.name}.\n`);
515
+ console.log("šŸ’” Push a version first using 'lua push job'.\n");
337
516
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
338
- }
339
- }
340
- async function activateJob(context, config) {
341
- const jobs = config.jobs || [];
342
- if (jobs.length === 0) {
343
- console.log("\nā„¹ļø No jobs found.\n");
344
517
  return;
345
518
  }
346
- const jobAnswer = await safePrompt([
519
+ const selectedVersion = await promptVersionSelection(versions, selectedJob.activeVersionId);
520
+ if (!selectedVersion)
521
+ return;
522
+ // Show warning and confirm
523
+ console.log("\nāš ļø WARNING: You are about to deploy to PRODUCTION!");
524
+ console.log("āš ļø This will affect ALL users immediately.\n");
525
+ console.log(`Job: ${selectedJob.name}`);
526
+ console.log(`Version: ${selectedVersion.version}`);
527
+ console.log(`Schedule: ${formatSchedule(selectedJob.schedule)}\n`);
528
+ const confirmAnswer = await safePrompt([
347
529
  {
348
- type: 'list',
349
- name: 'selectedJob',
350
- message: 'Select a job to activate:',
351
- choices: jobs.map((job) => ({
352
- name: `${job.name} (${job.jobId})`,
353
- value: job
354
- }))
530
+ type: 'confirm',
531
+ name: 'confirm',
532
+ message: 'Are you absolutely sure you want to deploy this version?',
533
+ default: false
355
534
  }
356
535
  ]);
357
- if (!jobAnswer)
536
+ if (!confirmAnswer || !confirmAnswer.confirm) {
537
+ console.log("\nāŒ Deployment cancelled.\n");
358
538
  return;
359
- writeProgress(`šŸ”„ Activating job "${jobAnswer.selectedJob.name}"...`);
360
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
361
- const response = await jobApi.activateJob(jobAnswer.selectedJob.jobId);
362
- if (response.success) {
363
- writeSuccess(`āœ… Job "${jobAnswer.selectedJob.name}" activated successfully`);
364
- writeInfo("šŸ’” The job is now enabled and will run on schedule.");
365
539
  }
366
- else {
367
- console.error(`āŒ Failed to activate job: ${response.error?.message || 'Unknown error'}`);
540
+ await deployVersionCore(context, selectedJob, selectedVersion.version);
541
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
542
+ }
543
+ async function activateJobInteractive(context, config) {
544
+ const jobs = config.jobs || [];
545
+ if (jobs.length === 0) {
546
+ console.log("\nā„¹ļø No jobs found.\n");
547
+ return;
368
548
  }
549
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to activate:');
550
+ if (!selectedJob)
551
+ return;
552
+ await activateJobCore(context, selectedJob);
369
553
  }
370
- async function deactivateJob(context, config) {
554
+ async function deactivateJobInteractive(context, config) {
371
555
  const jobs = config.jobs || [];
372
556
  if (jobs.length === 0) {
373
557
  console.log("\nā„¹ļø No jobs found.\n");
374
558
  return;
375
559
  }
376
- const jobAnswer = await safePrompt([
377
- {
378
- type: 'list',
379
- name: 'selectedJob',
380
- message: 'Select a job to deactivate:',
381
- choices: jobs.map((job) => ({
382
- name: `${job.name} (${job.jobId})`,
383
- value: job
384
- }))
385
- }
386
- ]);
387
- if (!jobAnswer)
560
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to deactivate:');
561
+ if (!selectedJob)
388
562
  return;
389
- // Confirm deactivation
390
563
  const confirmAnswer = await safePrompt([
391
564
  {
392
565
  type: 'confirm',
393
566
  name: 'confirm',
394
- message: `Are you sure you want to deactivate "${jobAnswer.selectedJob.name}"? It will stop running on schedule.`,
567
+ message: `Are you sure you want to deactivate "${selectedJob.name}"? It will stop running on schedule.`,
395
568
  default: false
396
569
  }
397
570
  ]);
@@ -399,124 +572,36 @@ async function deactivateJob(context, config) {
399
572
  console.log("\nāŒ Deactivation cancelled.\n");
400
573
  return;
401
574
  }
402
- writeProgress(`šŸ”„ Deactivating job "${jobAnswer.selectedJob.name}"...`);
403
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
404
- const response = await jobApi.deactivateJob(jobAnswer.selectedJob.jobId);
405
- if (response.success) {
406
- writeSuccess(`āœ… Job "${jobAnswer.selectedJob.name}" deactivated successfully`);
407
- writeInfo("šŸ’” The job is now disabled and will not run.");
408
- }
409
- else {
410
- console.error(`āŒ Failed to deactivate job: ${response.error?.message || 'Unknown error'}`);
411
- }
575
+ await deactivateJobCore(context, selectedJob);
412
576
  }
413
- async function triggerJob(context, config) {
577
+ async function triggerJobInteractive(context, config) {
414
578
  const jobs = config.jobs || [];
415
579
  if (jobs.length === 0) {
416
580
  console.log("\nā„¹ļø No jobs found.\n");
417
581
  return;
418
582
  }
419
- const jobAnswer = await safePrompt([
420
- {
421
- type: 'list',
422
- name: 'selectedJob',
423
- message: 'Select a job to trigger:',
424
- choices: jobs.map((job) => ({
425
- name: `${job.name} (${job.jobId})`,
426
- value: job
427
- }))
428
- }
429
- ]);
430
- if (!jobAnswer)
583
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to trigger:');
584
+ if (!selectedJob)
431
585
  return;
432
- writeProgress("⚔ Triggering job...");
433
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
434
- const response = await jobApi.triggerJob(jobAnswer.selectedJob.jobId);
435
- if (response.success && response.data) {
436
- writeSuccess(`āœ… Job "${jobAnswer.selectedJob.name}" triggered successfully`);
437
- writeInfo(`šŸ“‹ Execution ID: ${response.data.id || 'N/A'}`);
438
- }
439
- else {
440
- console.error(`āŒ Failed to trigger job: ${response.error?.message || 'Unknown error'}`);
441
- }
586
+ await triggerJobCore(context, selectedJob);
442
587
  }
443
- /**
444
- * View execution history for a job
445
- */
446
- async function viewExecutionHistory(context, config) {
588
+ async function viewExecutionHistoryInteractive(context, config) {
447
589
  const jobs = config.jobs || [];
448
590
  if (jobs.length === 0) {
449
591
  console.log("\nā„¹ļø No jobs found in configuration.\n");
450
592
  await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
451
593
  return;
452
594
  }
453
- // Prompt to select a job
454
- const jobAnswer = await safePrompt([
455
- {
456
- type: 'list',
457
- name: 'selectedJob',
458
- message: 'Select a job to view execution history:',
459
- choices: jobs.map((job) => ({
460
- name: `${job.name} (${job.jobId})`,
461
- value: job
462
- }))
463
- }
464
- ]);
465
- if (!jobAnswer)
595
+ const selectedJob = await promptJobSelection(jobs, 'Select a job to view execution history:');
596
+ if (!selectedJob)
466
597
  return;
467
- const selectedJob = jobAnswer.selectedJob;
468
598
  writeProgress(`šŸ”„ Loading execution history for ${selectedJob.name}...`);
469
- try {
470
- const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
471
- const response = await jobApi.getJobExecutions(selectedJob.jobId, 20); // Get last 20 executions
472
- if (!response.success || !response.data) {
473
- throw new Error(response.error?.message || 'Failed to fetch execution history');
474
- }
475
- const executions = response.data.executions || [];
476
- if (executions.length === 0) {
477
- console.log(`\nā„¹ļø No execution history found for ${selectedJob.name}.\n`);
478
- console.log("šŸ’” The job hasn't run yet or has no recorded executions.\n");
479
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
480
- return;
481
- }
482
- console.log("\n" + "=".repeat(60));
483
- console.log(`šŸ“Š Execution History for ${selectedJob.name}`);
484
- console.log("=".repeat(60) + "\n");
485
- executions.forEach((execution, index) => {
486
- const startedAt = new Date(execution.startedAt);
487
- const status = execution.status;
488
- const statusEmoji = status === 'completed' ? 'āœ…' :
489
- status === 'failed' ? 'āŒ' :
490
- status === 'running' ? 'šŸ”„' : 'ā³';
491
- console.log(`${index + 1}. ${statusEmoji} ${status.toUpperCase()}`);
492
- console.log(` Execution ID: ${execution.executionId}`);
493
- console.log(` Started: ${startedAt.toLocaleString()}`);
494
- if (execution.completedAt) {
495
- const completedAt = new Date(execution.completedAt);
496
- const duration = completedAt.getTime() - startedAt.getTime();
497
- console.log(` Completed: ${completedAt.toLocaleString()}`);
498
- console.log(` Duration: ${Math.round(duration / 1000)}s`);
499
- }
500
- if (execution.result) {
501
- console.log(` Result: ${JSON.stringify(execution.result).substring(0, 100)}...`);
502
- }
503
- if (execution.error) {
504
- console.log(` Error: ${execution.error}`);
505
- }
506
- console.log();
507
- });
508
- console.log("=".repeat(60) + "\n");
509
- console.log(`Showing last ${executions.length} executions\n`);
510
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
511
- }
512
- catch (error) {
513
- console.error('\nāŒ Error loading execution history:', error);
514
- await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
515
- }
599
+ await fetchAndDisplayHistoryCore(context, selectedJob);
600
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
516
601
  }
517
- /**
518
- * Format schedule object for display
519
- */
602
+ // ─────────────────────────────────────────────────────────────────────────────
603
+ // Utility Functions
604
+ // ─────────────────────────────────────────────────────────────────────────────
520
605
  function formatSchedule(schedule) {
521
606
  if (!schedule)
522
607
  return 'Not configured';