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
@@ -17,3 +17,10 @@ export { adminCommand } from "./admin.js";
17
17
  export { docsCommand } from "./docs.js";
18
18
  export { channelsCommand } from "./channels.js";
19
19
  export { logsCommand } from "./logs.js";
20
+ export { completionCommand } from "./completion.js";
21
+ export { skillsCommand } from "./skills.js";
22
+ export { webhooksCommand } from "./webhooks.js";
23
+ export { jobsCommand } from "./jobs.js";
24
+ export { featuresCommand } from "./features.js";
25
+ export { preprocessorsCommand } from "./preprocessors.js";
26
+ export { postprocessorsCommand } from "./postprocessors.js";
@@ -17,3 +17,10 @@ export { adminCommand } from "./admin.js";
17
17
  export { docsCommand } from "./docs.js";
18
18
  export { channelsCommand } from "./channels.js";
19
19
  export { logsCommand } from "./logs.js";
20
+ export { completionCommand } from "./completion.js";
21
+ export { skillsCommand } from "./skills.js";
22
+ export { webhooksCommand } from "./webhooks.js";
23
+ export { jobsCommand } from "./jobs.js";
24
+ export { featuresCommand } from "./features.js";
25
+ export { preprocessorsCommand } from "./preprocessors.js";
26
+ export { postprocessorsCommand } from "./postprocessors.js";
@@ -119,7 +119,7 @@ export async function initCommand() {
119
119
  }
120
120
  else {
121
121
  // Step 4b: Create new agent
122
- const result = await createNewAgentFlow(apiKey);
122
+ const result = await createNewAgentFlow(apiKey, userData);
123
123
  selectedAgent = result.agent;
124
124
  selectedOrg = result.org;
125
125
  persona = result.persona;
@@ -172,14 +172,59 @@ async function selectExistingAgent(userData, apiKey) {
172
172
  * Handles the flow for creating a new agent.
173
173
  *
174
174
  * @param apiKey - User's API key
175
+ * @param userData - User data containing organizations
175
176
  * @returns Created agent, organization, and optional persona/welcome message
176
177
  */
177
- async function createNewAgentFlow(apiKey) {
178
+ async function createNewAgentFlow(apiKey, userData) {
178
179
  // Fetch and select agent type
179
180
  const agentTypes = await fetchAgentTypes(apiKey);
180
181
  const selectedAgentType = selectBaseAgentType(agentTypes);
182
+ // Ask about organization selection FIRST
183
+ const { orgChoice } = await inquirer.prompt([
184
+ {
185
+ type: 'list',
186
+ name: 'orgChoice',
187
+ message: 'Organization:',
188
+ choices: [
189
+ { name: 'šŸ¢ Use existing organization', value: 'existing' },
190
+ { name: 'āž• Create new organization', value: 'create' }
191
+ ]
192
+ }
193
+ ]);
194
+ let orgId;
195
+ let orgName;
196
+ let usingExistingOrg = false;
197
+ if (orgChoice === 'existing') {
198
+ // Select from existing organizations
199
+ const orgs = userData.admin?.orgs || [];
200
+ if (orgs.length === 0) {
201
+ writeInfo("\nāš ļø No existing organizations found. Creating new organization instead.\n");
202
+ }
203
+ else {
204
+ const selectedOrg = await promptOrganizationSelection(orgs);
205
+ orgId = selectedOrg.id;
206
+ usingExistingOrg = true;
207
+ }
208
+ }
209
+ if (!orgId) {
210
+ // Create new organization - ask for org name
211
+ const { organizationName } = await inquirer.prompt([
212
+ {
213
+ type: 'input',
214
+ name: 'organizationName',
215
+ message: 'Organization name:',
216
+ validate: (input) => {
217
+ if (!input || input.trim() === '') {
218
+ return 'Organization name is required';
219
+ }
220
+ return true;
221
+ }
222
+ }
223
+ ]);
224
+ orgName = organizationName.trim();
225
+ }
181
226
  // Collect required metadata
182
- let linesToClear = 1;
227
+ let linesToClear = 2; // orgChoice + (org selection OR org name)
183
228
  const metadata = selectedAgentType.requiredSubAgentMetadata?.length
184
229
  ? await promptMetadataCollection(selectedAgentType.requiredSubAgentMetadata)
185
230
  : {};
@@ -190,12 +235,13 @@ async function createNewAgentFlow(apiKey) {
190
235
  : {};
191
236
  linesToClear += availableFeatures.length;
192
237
  // Collect business configuration
193
- const businessConfig = await promptBusinessConfiguration();
194
- linesToClear += 5; // businessName, agentName, businessType, brandPersonality, brandTraits
238
+ // If using existing org, don't ask for business name (use org name)
239
+ const businessConfig = await promptBusinessConfiguration(usingExistingOrg);
240
+ linesToClear += usingExistingOrg ? 4 : 5; // Skip businessName prompt if using existing org
195
241
  // Clear all prompts
196
242
  clearLinesIfNeeded(linesToClear);
197
243
  // Create the agent
198
- const result = await createNewAgent(apiKey, selectedAgentType, businessConfig, metadata, features);
244
+ const result = await createNewAgent(apiKey, selectedAgentType, businessConfig, metadata, features, orgId, orgName);
199
245
  return result;
200
246
  }
201
247
  /**
@@ -232,7 +278,7 @@ async function handleAgentSwitch(userData, apiKey, existingYaml) {
232
278
  }
233
279
  else {
234
280
  // Create new agent
235
- const result = await createNewAgentFlow(apiKey);
281
+ const result = await createNewAgentFlow(apiKey, userData);
236
282
  selectedAgent = result.agent;
237
283
  selectedOrg = result.org;
238
284
  persona = result.persona;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Jobs Command
3
+ * Manages agent scheduled jobs for sandbox and production environments
4
+ */
5
+ /**
6
+ * Main jobs command - manages agent jobs
7
+ *
8
+ * Features:
9
+ * - View deployed jobs
10
+ * - View job versions
11
+ * - Deploy job versions to production
12
+ * - Activate/deactivate, pause/resume jobs
13
+ * - Trigger jobs manually
14
+ * - View execution history
15
+ *
16
+ * Note: For local testing, use `lua test job`
17
+ *
18
+ * @returns Promise that resolves when command completes
19
+ */
20
+ export declare function jobsCommand(): Promise<void>;
@@ -0,0 +1,533 @@
1
+ /**
2
+ * Jobs Command
3
+ * Manages agent scheduled jobs for sandbox and production environments
4
+ */
5
+ import { loadApiKey, checkApiKey } from '../services/auth.js';
6
+ import { readSkillConfig } from '../utils/files.js';
7
+ import { withErrorHandling, writeProgress, writeSuccess, writeInfo } from '../utils/cli.js';
8
+ import { BASE_URLS } from '../config/constants.js';
9
+ import { safePrompt } from '../utils/prompt-handler.js';
10
+ import { validateConfig, validateAgentConfig, } from '../utils/dev-helpers.js';
11
+ import JobApi from '../api/job.api.service.js';
12
+ /**
13
+ * Main jobs command - manages agent jobs
14
+ *
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
26
+ */
27
+ export async function jobsCommand() {
28
+ return withErrorHandling(async () => {
29
+ // Step 1: Load configuration
30
+ const config = readSkillConfig();
31
+ validateConfig(config);
32
+ validateAgentConfig(config);
33
+ const agentId = config.agent.agentId;
34
+ // Step 2: Authenticate
35
+ const apiKey = await loadApiKey();
36
+ if (!apiKey) {
37
+ console.error("āŒ No API key found. Please run 'lua auth configure' to set up your API key.");
38
+ process.exit(1);
39
+ }
40
+ await checkApiKey(apiKey);
41
+ writeProgress("āœ… Authenticated");
42
+ const context = {
43
+ environment: 'production',
44
+ agentId,
45
+ apiKey,
46
+ };
47
+ // Start job management
48
+ await manageProductionJobs(context, config);
49
+ }, "jobs");
50
+ }
51
+ /**
52
+ * Manage production jobs - view, deploy, pause/resume, trigger
53
+ */
54
+ async function manageProductionJobs(context, config) {
55
+ let continueManaging = true;
56
+ while (continueManaging) {
57
+ console.log("\n" + "=".repeat(60));
58
+ console.log("šŸš€ Production Jobs");
59
+ console.log("=".repeat(60) + "\n");
60
+ const actionAnswer = await safePrompt([
61
+ {
62
+ type: 'list',
63
+ name: 'action',
64
+ message: 'What would you like to do?',
65
+ choices: [
66
+ { name: 'šŸ‘ļø View deployed jobs', value: 'view' },
67
+ { name: 'šŸ“œ View job versions', value: 'versions' },
68
+ { name: 'šŸš€ Deploy a version', value: 'deploy' },
69
+ { name: 'āœ… Activate a job', value: 'activate' },
70
+ { name: '🚫 Deactivate a job', value: 'deactivate' },
71
+ { name: '⚔ Trigger job now', value: 'trigger' },
72
+ { name: 'šŸ“Š View execution history', value: 'history' },
73
+ { name: 'āŒ Exit', value: 'exit' }
74
+ ]
75
+ }
76
+ ]);
77
+ if (!actionAnswer)
78
+ return;
79
+ const { action } = actionAnswer;
80
+ switch (action) {
81
+ case 'view':
82
+ await viewDeployedJobs(context, config);
83
+ break;
84
+ case 'versions':
85
+ await viewJobVersions(context, config);
86
+ break;
87
+ case 'deploy':
88
+ await deployJobVersion(context, config);
89
+ break;
90
+ case 'activate':
91
+ await activateJob(context, config);
92
+ break;
93
+ case 'deactivate':
94
+ await deactivateJob(context, config);
95
+ break;
96
+ case 'trigger':
97
+ await triggerJob(context, config);
98
+ break;
99
+ case 'history':
100
+ await viewExecutionHistory(context, config);
101
+ break;
102
+ case 'exit':
103
+ continueManaging = false;
104
+ console.log("\nšŸ‘‹ Goodbye!\n");
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * View deployed jobs in production
111
+ */
112
+ async function viewDeployedJobs(context, config) {
113
+ 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...' }]);
168
+ }
169
+ catch (error) {
170
+ console.error('āŒ Error loading job information:', error);
171
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
172
+ }
173
+ }
174
+ /**
175
+ * View all versions for a specific job
176
+ */
177
+ async function viewJobVersions(context, config) {
178
+ const jobs = config.jobs || [];
179
+ if (jobs.length === 0) {
180
+ console.log("\nā„¹ļø No jobs found in configuration.\n");
181
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
182
+ return;
183
+ }
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)
197
+ return;
198
+ const selectedJob = jobAnswer.selectedJob;
199
+ 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.versions || [];
207
+ const activeVersionId = response.data.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");
234
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
235
+ }
236
+ catch (error) {
237
+ console.error('\nāŒ Error loading versions:', error);
238
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
239
+ }
240
+ }
241
+ /**
242
+ * Deploy a job version to production
243
+ */
244
+ async function deployJobVersion(context, config) {
245
+ const jobs = config.jobs || [];
246
+ if (jobs.length === 0) {
247
+ console.log("\nā„¹ļø No jobs found in configuration.\n");
248
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
249
+ return;
250
+ }
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)
264
+ return;
265
+ const selectedJob = jobAnswer.selectedJob;
266
+ 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.versions || [];
274
+ const activeVersionId = response.data.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.");
333
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
334
+ }
335
+ catch (error) {
336
+ console.error('\nāŒ Error deploying version:', error);
337
+ 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
+ return;
345
+ }
346
+ const jobAnswer = await safePrompt([
347
+ {
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
+ }))
355
+ }
356
+ ]);
357
+ if (!jobAnswer)
358
+ 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
+ }
366
+ else {
367
+ console.error(`āŒ Failed to activate job: ${response.error?.message || 'Unknown error'}`);
368
+ }
369
+ }
370
+ async function deactivateJob(context, config) {
371
+ const jobs = config.jobs || [];
372
+ if (jobs.length === 0) {
373
+ console.log("\nā„¹ļø No jobs found.\n");
374
+ return;
375
+ }
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)
388
+ return;
389
+ // Confirm deactivation
390
+ const confirmAnswer = await safePrompt([
391
+ {
392
+ type: 'confirm',
393
+ name: 'confirm',
394
+ message: `Are you sure you want to deactivate "${jobAnswer.selectedJob.name}"? It will stop running on schedule.`,
395
+ default: false
396
+ }
397
+ ]);
398
+ if (!confirmAnswer || !confirmAnswer.confirm) {
399
+ console.log("\nāŒ Deactivation cancelled.\n");
400
+ return;
401
+ }
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
+ }
412
+ }
413
+ async function triggerJob(context, config) {
414
+ const jobs = config.jobs || [];
415
+ if (jobs.length === 0) {
416
+ console.log("\nā„¹ļø No jobs found.\n");
417
+ return;
418
+ }
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)
431
+ 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.executionId || 'N/A'}`);
438
+ }
439
+ else {
440
+ console.error(`āŒ Failed to trigger job: ${response.error?.message || 'Unknown error'}`);
441
+ }
442
+ }
443
+ /**
444
+ * View execution history for a job
445
+ */
446
+ async function viewExecutionHistory(context, config) {
447
+ const jobs = config.jobs || [];
448
+ if (jobs.length === 0) {
449
+ console.log("\nā„¹ļø No jobs found in configuration.\n");
450
+ await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
451
+ return;
452
+ }
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)
466
+ return;
467
+ const selectedJob = jobAnswer.selectedJob;
468
+ 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
+ }
516
+ }
517
+ /**
518
+ * Format schedule object for display
519
+ */
520
+ function formatSchedule(schedule) {
521
+ if (!schedule)
522
+ return 'Not configured';
523
+ switch (schedule.type) {
524
+ case 'cron':
525
+ return `Cron: ${schedule.expression}${schedule.timezone ? ` (${schedule.timezone})` : ''}`;
526
+ case 'once':
527
+ return `Once at: ${new Date(schedule.executeAt).toLocaleString()}`;
528
+ case 'interval':
529
+ return `Every ${schedule.seconds} seconds`;
530
+ default:
531
+ return 'Unknown schedule type';
532
+ }
533
+ }
@@ -176,11 +176,8 @@ function displayLogs(logs, pagination, title) {
176
176
  if (log.duration !== undefined) {
177
177
  console.log(chalk.gray(` Duration: ${log.duration}ms`));
178
178
  }
179
- // Display message (truncate if too long)
180
- const message = log.message.length > 200
181
- ? log.message.substring(0, 200) + '...'
182
- : log.message;
183
- console.log(` ${message}`);
179
+ // Display full message without truncation
180
+ console.log(` ${log.message}`);
184
181
  if (index < reversedLogs.length - 1) {
185
182
  console.log(chalk.gray(` ${'-'.repeat(78)}`));
186
183
  }