lua-cli 3.2.0 → 3.4.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.
- package/dist/api/logs.api.service.d.ts +1 -1
- package/dist/api/logs.api.service.js.map +1 -1
- package/dist/api/products.api.service.d.ts +17 -5
- package/dist/api/products.api.service.js +21 -9
- package/dist/api/products.api.service.js.map +1 -1
- package/dist/api/webhook.api.service.d.ts +4 -0
- package/dist/api/webhook.api.service.js.map +1 -1
- package/dist/api-exports.d.ts +19 -7
- package/dist/api-exports.js +20 -5
- package/dist/api-exports.js.map +1 -1
- package/dist/cli/command-definitions.js +323 -88
- package/dist/cli/command-definitions.js.map +1 -1
- package/dist/commands/apiKey.d.ts +5 -2
- package/dist/commands/apiKey.js +8 -2
- package/dist/commands/apiKey.js.map +1 -1
- package/dist/commands/channels.d.ts +4 -9
- package/dist/commands/channels.js +140 -84
- package/dist/commands/channels.js.map +1 -1
- package/dist/commands/chat.d.ts +4 -2
- package/dist/commands/chat.js +126 -32
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/chatClear.d.ts +3 -2
- package/dist/commands/chatClear.js +16 -15
- package/dist/commands/chatClear.js.map +1 -1
- package/dist/commands/compile.d.ts +5 -4
- package/dist/commands/compile.js +73 -9
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/deploy.d.ts +5 -24
- package/dist/commands/deploy.js +75 -48
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/destroy.d.ts +5 -2
- package/dist/commands/destroy.js +14 -2
- package/dist/commands/destroy.js.map +1 -1
- package/dist/commands/env.d.ts +3 -1
- package/dist/commands/env.js +322 -122
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/features.d.ts +5 -9
- package/dist/commands/features.js +249 -129
- package/dist/commands/features.js.map +1 -1
- package/dist/commands/init.d.ts +7 -1
- package/dist/commands/init.js +242 -59
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/jobs.d.ts +5 -13
- package/dist/commands/jobs.js +523 -360
- package/dist/commands/jobs.js.map +1 -1
- package/dist/commands/logs.d.ts +5 -10
- package/dist/commands/logs.js +259 -103
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/marketplace.d.ts +23 -2
- package/dist/commands/marketplace.js +530 -7
- package/dist/commands/marketplace.js.map +1 -1
- package/dist/commands/mcp.d.ts +5 -11
- package/dist/commands/mcp.js +304 -294
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/persona.d.ts +5 -9
- package/dist/commands/persona.js +349 -232
- package/dist/commands/persona.js.map +1 -1
- package/dist/commands/postprocessors.d.ts +6 -2
- package/dist/commands/postprocessors.js +387 -280
- package/dist/commands/postprocessors.js.map +1 -1
- package/dist/commands/preprocessors.d.ts +6 -2
- package/dist/commands/preprocessors.js +387 -280
- package/dist/commands/preprocessors.js.map +1 -1
- package/dist/commands/production.d.ts +5 -8
- package/dist/commands/production.js +317 -228
- package/dist/commands/production.js.map +1 -1
- package/dist/commands/push.js +385 -427
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/resources.d.ts +5 -10
- package/dist/commands/resources.js +219 -154
- package/dist/commands/resources.js.map +1 -1
- package/dist/commands/skills.d.ts +5 -9
- package/dist/commands/skills.js +435 -275
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/sync.d.ts +10 -8
- package/dist/commands/sync.js +110 -19
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/test.d.ts +1 -11
- package/dist/commands/test.js +395 -438
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/webhooks.d.ts +5 -11
- package/dist/commands/webhooks.js +431 -287
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/interfaces/index.d.ts +1 -1
- package/dist/interfaces/mcp.d.ts +39 -19
- package/dist/interfaces/mcp.js +3 -0
- package/dist/interfaces/mcp.js.map +1 -1
- package/dist/interfaces/product.d.ts +26 -0
- package/dist/interfaces/skills.d.ts +5 -0
- package/dist/types/api-contracts.d.ts +8 -4
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/skill.d.ts +146 -35
- package/dist/types/skill.js +31 -37
- package/dist/types/skill.js.map +1 -1
- package/dist/utils/bundling.d.ts +17 -0
- package/dist/utils/bundling.js +96 -0
- package/dist/utils/bundling.js.map +1 -1
- package/dist/utils/compile.d.ts +4 -0
- package/dist/utils/compile.js +5 -0
- package/dist/utils/compile.js.map +1 -1
- package/dist/utils/dev-helpers.d.ts +3 -2
- package/dist/utils/dev-helpers.js +3 -5
- package/dist/utils/dev-helpers.js.map +1 -1
- package/dist/utils/job-management.d.ts +4 -1
- package/dist/utils/job-management.js +15 -29
- package/dist/utils/job-management.js.map +1 -1
- package/dist/utils/mcp-server-management.d.ts +5 -2
- package/dist/utils/mcp-server-management.js +27 -43
- package/dist/utils/mcp-server-management.js.map +1 -1
- package/dist/utils/push-helpers.d.ts +1 -1
- package/dist/utils/push-helpers.js +5 -1
- package/dist/utils/push-helpers.js.map +1 -1
- package/dist/utils/skill-management.d.ts +7 -2
- package/dist/utils/skill-management.js +21 -30
- package/dist/utils/skill-management.js.map +1 -1
- package/dist/utils/webhook-management.d.ts +4 -1
- package/dist/utils/webhook-management.js +15 -29
- package/dist/utils/webhook-management.js.map +1 -1
- package/package.json +1 -1
- package/template/package.json +1 -1
package/dist/commands/jobs.js
CHANGED
|
@@ -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
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
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,411 @@ export async function jobsCommand() {
|
|
|
44
43
|
agentId,
|
|
45
44
|
apiKey,
|
|
46
45
|
};
|
|
47
|
-
//
|
|
48
|
-
|
|
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
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
58
|
/**
|
|
52
|
-
*
|
|
59
|
+
* Fetches and displays all jobs with their current status.
|
|
53
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
|
+
* Deletes a job from the server.
|
|
188
|
+
* If the job has versions, it will be deactivated instead of deleted.
|
|
189
|
+
*/
|
|
190
|
+
async function deleteJobCore(context, job) {
|
|
191
|
+
writeProgress(`🗑️ Deleting job "${job.name}"...`);
|
|
192
|
+
const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
|
|
193
|
+
const response = await jobApi.deleteJob(job.jobId);
|
|
194
|
+
if (!response.success || !response.data) {
|
|
195
|
+
console.error(`❌ Delete Error: ${response.error?.message || 'Unknown error'}`);
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (response.data.deleted) {
|
|
199
|
+
writeSuccess(`✅ Job "${job.name}" deleted successfully`);
|
|
200
|
+
writeInfo("💡 Remove this job from your lua.skill.yaml to keep it in sync.");
|
|
201
|
+
}
|
|
202
|
+
else if (response.data.deactivated) {
|
|
203
|
+
writeSuccess(`⚠️ Job "${job.name}" has versions and was deactivated instead of deleted`);
|
|
204
|
+
writeInfo("💡 The job is now inactive and won't run on schedule.");
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Triggers a job execution.
|
|
210
|
+
*/
|
|
211
|
+
async function triggerJobCore(context, job) {
|
|
212
|
+
writeProgress(`⚡ Triggering job "${job.name}"...`);
|
|
213
|
+
const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
|
|
214
|
+
const response = await jobApi.triggerJob(job.jobId);
|
|
215
|
+
if (response.success && response.data) {
|
|
216
|
+
writeSuccess(`✅ Job "${job.name}" triggered successfully`);
|
|
217
|
+
writeInfo(`📋 Execution ID: ${response.data.id || 'N/A'}`);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
console.error(`❌ Failed to trigger job: ${response.error?.message || 'Unknown error'}`);
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Fetches and displays execution history for a job.
|
|
227
|
+
*/
|
|
228
|
+
async function fetchAndDisplayHistoryCore(context, job) {
|
|
229
|
+
const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
|
|
230
|
+
const response = await jobApi.getJobExecutions(job.jobId, 20);
|
|
231
|
+
if (!response.success || !response.data) {
|
|
232
|
+
console.error(`❌ Failed to fetch history: ${response.error?.message || 'Unknown error'}`);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
const executions = response.data.executions || [];
|
|
236
|
+
if (executions.length === 0) {
|
|
237
|
+
console.log(`ℹ️ No execution history found for ${job.name}.`);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
console.log("\n" + "=".repeat(60));
|
|
241
|
+
console.log(`📊 Execution History for ${job.name}`);
|
|
242
|
+
console.log("=".repeat(60) + "\n");
|
|
243
|
+
executions.forEach((execution, index) => {
|
|
244
|
+
const startedAt = new Date(execution.startedAt);
|
|
245
|
+
const status = execution.status;
|
|
246
|
+
const statusEmoji = status === 'completed' ? '✅' :
|
|
247
|
+
status === 'failed' ? '❌' :
|
|
248
|
+
status === 'running' ? '🔄' : '⏳';
|
|
249
|
+
console.log(`${index + 1}. ${statusEmoji} ${status.toUpperCase()}`);
|
|
250
|
+
console.log(` Execution ID: ${execution.executionId}`);
|
|
251
|
+
console.log(` Started: ${startedAt.toLocaleString()}`);
|
|
252
|
+
if (execution.completedAt) {
|
|
253
|
+
const completedAt = new Date(execution.completedAt);
|
|
254
|
+
const duration = completedAt.getTime() - startedAt.getTime();
|
|
255
|
+
console.log(` Completed: ${completedAt.toLocaleString()}`);
|
|
256
|
+
console.log(` Duration: ${Math.round(duration / 1000)}s`);
|
|
257
|
+
}
|
|
258
|
+
if (execution.result) {
|
|
259
|
+
console.log(` Result: ${JSON.stringify(execution.result).substring(0, 100)}...`);
|
|
260
|
+
}
|
|
261
|
+
if (execution.error) {
|
|
262
|
+
console.log(` Error: ${execution.error}`);
|
|
263
|
+
}
|
|
264
|
+
console.log();
|
|
265
|
+
});
|
|
266
|
+
console.log("=".repeat(60));
|
|
267
|
+
console.log(`Showing last ${executions.length} executions`);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Resolves a version string to an actual version, supporting "latest".
|
|
272
|
+
*/
|
|
273
|
+
function resolveVersion(versions, versionArg) {
|
|
274
|
+
if (versionArg.toLowerCase() === 'latest') {
|
|
275
|
+
return versions[0].version;
|
|
276
|
+
}
|
|
277
|
+
const exists = versions.some((v) => v.version === versionArg);
|
|
278
|
+
if (!exists) {
|
|
279
|
+
console.error(`❌ Version "${versionArg}" not found`);
|
|
280
|
+
console.log('\nAvailable versions:');
|
|
281
|
+
versions.slice(0, 5).forEach((v) => console.log(` - ${v.version}`));
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
return versionArg;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Prompts user to select a job from a list.
|
|
288
|
+
*/
|
|
289
|
+
async function promptJobSelection(jobs, message) {
|
|
290
|
+
const jobAnswer = await safePrompt([
|
|
291
|
+
{
|
|
292
|
+
type: 'list',
|
|
293
|
+
name: 'selectedJob',
|
|
294
|
+
message,
|
|
295
|
+
choices: jobs.map((job) => ({
|
|
296
|
+
name: `${job.name} (${job.jobId})`,
|
|
297
|
+
value: job
|
|
298
|
+
}))
|
|
299
|
+
}
|
|
300
|
+
]);
|
|
301
|
+
return jobAnswer?.selectedJob || null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Prompts user to select a version from a list.
|
|
305
|
+
*/
|
|
306
|
+
async function promptVersionSelection(versions, activeVersionId) {
|
|
307
|
+
const versionAnswer = await safePrompt([
|
|
308
|
+
{
|
|
309
|
+
type: 'list',
|
|
310
|
+
name: 'selectedVersion',
|
|
311
|
+
message: 'Select a version to deploy:',
|
|
312
|
+
choices: versions.map((version) => {
|
|
313
|
+
const isActive = version.jobId === activeVersionId;
|
|
314
|
+
return {
|
|
315
|
+
name: `Version ${version.version} (${new Date(version.createdAt).toLocaleDateString()})${isActive ? ' ⭐ CURRENT' : ''}`,
|
|
316
|
+
value: version
|
|
317
|
+
};
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
]);
|
|
321
|
+
return versionAnswer?.selectedVersion || null;
|
|
322
|
+
}
|
|
323
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
324
|
+
// Non-Interactive Mode
|
|
325
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
326
|
+
async function executeNonInteractive(context, config, action, options) {
|
|
327
|
+
const validActions = ['view', 'versions', 'deploy', 'activate', 'deactivate', 'trigger', 'history', 'delete'];
|
|
328
|
+
const normalizedAction = action.toLowerCase();
|
|
329
|
+
if (!validActions.includes(normalizedAction)) {
|
|
330
|
+
console.error(`❌ Invalid action: "${action}"`);
|
|
331
|
+
console.log('\nValid actions: view, versions, deploy, activate, deactivate, trigger, history, delete');
|
|
332
|
+
console.log('\nExamples:');
|
|
333
|
+
console.log(' lua jobs view List all jobs');
|
|
334
|
+
console.log(' lua jobs trigger -i healthCheck Trigger a job');
|
|
335
|
+
console.log(' lua jobs deploy -i myJob -v 1.0.3 Deploy specific version');
|
|
336
|
+
console.log(' lua jobs delete -i myJob Delete a job');
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
const jobs = config.jobs || [];
|
|
340
|
+
// View action doesn't require job selection
|
|
341
|
+
if (normalizedAction === 'view') {
|
|
342
|
+
if (jobs.length === 0) {
|
|
343
|
+
console.log("ℹ️ No jobs found in configuration.");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
await displayJobsCore(context, jobs);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// All other actions require job selection
|
|
350
|
+
if (!options.jobName) {
|
|
351
|
+
console.error(`❌ --job-name is required for action "${normalizedAction}"`);
|
|
352
|
+
console.log(`\nUsage: lua jobs ${normalizedAction} --job-name <name>`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
let selectedJob = jobs.find((j) => j.jobId === options.jobName || j.name === options.jobName);
|
|
356
|
+
// For delete action, also check server jobs (for orphaned jobs not in local YAML)
|
|
357
|
+
if (!selectedJob && normalizedAction === 'delete') {
|
|
358
|
+
const jobApi = new JobApi(BASE_URLS.API, context.apiKey, context.agentId);
|
|
359
|
+
const serverResponse = await jobApi.getJobs();
|
|
360
|
+
if (serverResponse.success && serverResponse.data?.jobs) {
|
|
361
|
+
const serverJob = serverResponse.data.jobs.find((j) => j.id === options.jobName || j.name === options.jobName);
|
|
362
|
+
if (serverJob) {
|
|
363
|
+
// Map server job format to local format for deleteJobCore
|
|
364
|
+
selectedJob = {
|
|
365
|
+
name: serverJob.name,
|
|
366
|
+
jobId: serverJob.id,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (!selectedJob) {
|
|
372
|
+
console.error(`❌ Job "${options.jobName}" not found`);
|
|
373
|
+
console.log('\nAvailable jobs in local config:');
|
|
374
|
+
jobs.forEach((j) => console.log(` - ${j.name} (${j.jobId})`));
|
|
375
|
+
if (normalizedAction === 'delete') {
|
|
376
|
+
console.log('\n💡 Tip: The job may have already been deleted from the server.');
|
|
377
|
+
}
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
switch (normalizedAction) {
|
|
381
|
+
case 'versions': {
|
|
382
|
+
const versions = await fetchVersionsCore(context, selectedJob);
|
|
383
|
+
if (!versions)
|
|
384
|
+
process.exit(1);
|
|
385
|
+
if (versions.length === 0) {
|
|
386
|
+
console.log(`ℹ️ No versions found for ${selectedJob.name}.`);
|
|
387
|
+
console.log("💡 Push a version first using 'lua push job'.");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
displayVersionsCore(selectedJob, versions);
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
case 'deploy': {
|
|
394
|
+
if (!options.jobVersion) {
|
|
395
|
+
console.error('❌ --job-version is required for deploy action');
|
|
396
|
+
console.log('\nUsage: lua jobs deploy --job-name myJob --job-version 1.0.3');
|
|
397
|
+
console.log(' lua jobs deploy -i myJob -v latest');
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
const versions = await fetchVersionsCore(context, selectedJob);
|
|
401
|
+
if (!versions)
|
|
402
|
+
process.exit(1);
|
|
403
|
+
if (versions.length === 0) {
|
|
404
|
+
console.error(`❌ No versions found for ${selectedJob.name}.`);
|
|
405
|
+
console.log("💡 Push a version first using 'lua push job'.");
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
const resolvedVersion = resolveVersion(versions, options.jobVersion);
|
|
409
|
+
if (!resolvedVersion)
|
|
410
|
+
process.exit(1);
|
|
411
|
+
const success = await deployVersionCore(context, selectedJob, resolvedVersion);
|
|
412
|
+
if (!success)
|
|
413
|
+
process.exit(1);
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case 'activate': {
|
|
417
|
+
const success = await activateJobCore(context, selectedJob);
|
|
418
|
+
if (!success)
|
|
419
|
+
process.exit(1);
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case 'deactivate': {
|
|
423
|
+
const success = await deactivateJobCore(context, selectedJob);
|
|
424
|
+
if (!success)
|
|
425
|
+
process.exit(1);
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
case 'trigger': {
|
|
429
|
+
const success = await triggerJobCore(context, selectedJob);
|
|
430
|
+
if (!success)
|
|
431
|
+
process.exit(1);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case 'history': {
|
|
435
|
+
const success = await fetchAndDisplayHistoryCore(context, selectedJob);
|
|
436
|
+
if (!success)
|
|
437
|
+
process.exit(1);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case 'delete': {
|
|
441
|
+
const success = await deleteJobCore(context, selectedJob);
|
|
442
|
+
if (!success)
|
|
443
|
+
process.exit(1);
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
449
|
+
// Interactive Mode
|
|
450
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
54
451
|
async function manageProductionJobs(context, config) {
|
|
55
452
|
let continueManaging = true;
|
|
56
453
|
while (continueManaging) {
|
|
@@ -70,6 +467,7 @@ async function manageProductionJobs(context, config) {
|
|
|
70
467
|
{ name: '🚫 Deactivate a job', value: 'deactivate' },
|
|
71
468
|
{ name: '⚡ Trigger job now', value: 'trigger' },
|
|
72
469
|
{ name: '📊 View execution history', value: 'history' },
|
|
470
|
+
{ name: '🗑️ Delete a job', value: 'delete' },
|
|
73
471
|
{ name: '❌ Exit', value: 'exit' }
|
|
74
472
|
]
|
|
75
473
|
}
|
|
@@ -79,25 +477,28 @@ async function manageProductionJobs(context, config) {
|
|
|
79
477
|
const { action } = actionAnswer;
|
|
80
478
|
switch (action) {
|
|
81
479
|
case 'view':
|
|
82
|
-
await
|
|
480
|
+
await viewDeployedJobsInteractive(context, config);
|
|
83
481
|
break;
|
|
84
482
|
case 'versions':
|
|
85
|
-
await
|
|
483
|
+
await viewJobVersionsInteractive(context, config);
|
|
86
484
|
break;
|
|
87
485
|
case 'deploy':
|
|
88
|
-
await
|
|
486
|
+
await deployJobVersionInteractive(context, config);
|
|
89
487
|
break;
|
|
90
488
|
case 'activate':
|
|
91
|
-
await
|
|
489
|
+
await activateJobInteractive(context, config);
|
|
92
490
|
break;
|
|
93
491
|
case 'deactivate':
|
|
94
|
-
await
|
|
492
|
+
await deactivateJobInteractive(context, config);
|
|
95
493
|
break;
|
|
96
494
|
case 'trigger':
|
|
97
|
-
await
|
|
495
|
+
await triggerJobInteractive(context, config);
|
|
98
496
|
break;
|
|
99
497
|
case 'history':
|
|
100
|
-
await
|
|
498
|
+
await viewExecutionHistoryInteractive(context, config);
|
|
499
|
+
break;
|
|
500
|
+
case 'delete':
|
|
501
|
+
await deleteJobInteractive(context, config);
|
|
101
502
|
break;
|
|
102
503
|
case 'exit':
|
|
103
504
|
continueManaging = false;
|
|
@@ -106,292 +507,115 @@ async function manageProductionJobs(context, config) {
|
|
|
106
507
|
}
|
|
107
508
|
}
|
|
108
509
|
}
|
|
109
|
-
|
|
110
|
-
* View deployed jobs in production
|
|
111
|
-
*/
|
|
112
|
-
async function viewDeployedJobs(context, config) {
|
|
510
|
+
async function viewDeployedJobsInteractive(context, config) {
|
|
113
511
|
writeProgress("🔄 Loading job information...");
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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...' }]);
|
|
512
|
+
const jobs = config.jobs || [];
|
|
513
|
+
if (jobs.length === 0) {
|
|
514
|
+
console.log("\nℹ️ No jobs found in configuration.\n");
|
|
168
515
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
516
|
+
else {
|
|
517
|
+
await displayJobsCore(context, jobs);
|
|
518
|
+
console.log();
|
|
172
519
|
}
|
|
520
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
173
521
|
}
|
|
174
|
-
|
|
175
|
-
* View all versions for a specific job
|
|
176
|
-
*/
|
|
177
|
-
async function viewJobVersions(context, config) {
|
|
522
|
+
async function viewJobVersionsInteractive(context, config) {
|
|
178
523
|
const jobs = config.jobs || [];
|
|
179
524
|
if (jobs.length === 0) {
|
|
180
525
|
console.log("\nℹ️ No jobs found in configuration.\n");
|
|
181
526
|
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
182
527
|
return;
|
|
183
528
|
}
|
|
184
|
-
|
|
185
|
-
|
|
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)
|
|
529
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to view versions:');
|
|
530
|
+
if (!selectedJob)
|
|
197
531
|
return;
|
|
198
|
-
const selectedJob = jobAnswer.selectedJob;
|
|
199
532
|
writeProgress(`🔄 Loading versions for ${selectedJob.name}...`);
|
|
200
|
-
|
|
201
|
-
|
|
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");
|
|
533
|
+
const versions = await fetchVersionsCore(context, selectedJob);
|
|
534
|
+
if (!versions) {
|
|
234
535
|
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
536
|
+
return;
|
|
235
537
|
}
|
|
236
|
-
|
|
237
|
-
console.
|
|
238
|
-
|
|
538
|
+
if (versions.length === 0) {
|
|
539
|
+
console.log(`\nℹ️ No versions found for ${selectedJob.name}.\n`);
|
|
540
|
+
console.log("💡 Push a version first using 'lua push job'.\n");
|
|
239
541
|
}
|
|
542
|
+
else {
|
|
543
|
+
displayVersionsCore(selectedJob, versions);
|
|
544
|
+
console.log();
|
|
545
|
+
}
|
|
546
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
240
547
|
}
|
|
241
|
-
|
|
242
|
-
* Deploy a job version to production
|
|
243
|
-
*/
|
|
244
|
-
async function deployJobVersion(context, config) {
|
|
548
|
+
async function deployJobVersionInteractive(context, config) {
|
|
245
549
|
const jobs = config.jobs || [];
|
|
246
550
|
if (jobs.length === 0) {
|
|
247
551
|
console.log("\nℹ️ No jobs found in configuration.\n");
|
|
248
552
|
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
249
553
|
return;
|
|
250
554
|
}
|
|
251
|
-
|
|
252
|
-
|
|
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)
|
|
555
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to deploy:');
|
|
556
|
+
if (!selectedJob)
|
|
264
557
|
return;
|
|
265
|
-
const selectedJob = jobAnswer.selectedJob;
|
|
266
558
|
writeProgress(`🔄 Loading versions for ${selectedJob.name}...`);
|
|
267
|
-
|
|
268
|
-
|
|
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.");
|
|
559
|
+
const versions = await fetchVersionsCore(context, selectedJob);
|
|
560
|
+
if (!versions) {
|
|
333
561
|
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
562
|
+
return;
|
|
334
563
|
}
|
|
335
|
-
|
|
336
|
-
console.
|
|
564
|
+
if (versions.length === 0) {
|
|
565
|
+
console.log(`\nℹ️ No versions found for ${selectedJob.name}.\n`);
|
|
566
|
+
console.log("💡 Push a version first using 'lua push job'.\n");
|
|
337
567
|
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
568
|
return;
|
|
345
569
|
}
|
|
346
|
-
const
|
|
570
|
+
const selectedVersion = await promptVersionSelection(versions, selectedJob.activeVersionId);
|
|
571
|
+
if (!selectedVersion)
|
|
572
|
+
return;
|
|
573
|
+
// Show warning and confirm
|
|
574
|
+
console.log("\n⚠️ WARNING: You are about to deploy to PRODUCTION!");
|
|
575
|
+
console.log("⚠️ This will affect ALL users immediately.\n");
|
|
576
|
+
console.log(`Job: ${selectedJob.name}`);
|
|
577
|
+
console.log(`Version: ${selectedVersion.version}`);
|
|
578
|
+
console.log(`Schedule: ${formatSchedule(selectedJob.schedule)}\n`);
|
|
579
|
+
const confirmAnswer = await safePrompt([
|
|
347
580
|
{
|
|
348
|
-
type: '
|
|
349
|
-
name: '
|
|
350
|
-
message: '
|
|
351
|
-
|
|
352
|
-
name: `${job.name} (${job.jobId})`,
|
|
353
|
-
value: job
|
|
354
|
-
}))
|
|
581
|
+
type: 'confirm',
|
|
582
|
+
name: 'confirm',
|
|
583
|
+
message: 'Are you absolutely sure you want to deploy this version?',
|
|
584
|
+
default: false
|
|
355
585
|
}
|
|
356
586
|
]);
|
|
357
|
-
if (!
|
|
587
|
+
if (!confirmAnswer || !confirmAnswer.confirm) {
|
|
588
|
+
console.log("\n❌ Deployment cancelled.\n");
|
|
358
589
|
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
590
|
}
|
|
366
|
-
|
|
367
|
-
|
|
591
|
+
await deployVersionCore(context, selectedJob, selectedVersion.version);
|
|
592
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
593
|
+
}
|
|
594
|
+
async function activateJobInteractive(context, config) {
|
|
595
|
+
const jobs = config.jobs || [];
|
|
596
|
+
if (jobs.length === 0) {
|
|
597
|
+
console.log("\nℹ️ No jobs found.\n");
|
|
598
|
+
return;
|
|
368
599
|
}
|
|
600
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to activate:');
|
|
601
|
+
if (!selectedJob)
|
|
602
|
+
return;
|
|
603
|
+
await activateJobCore(context, selectedJob);
|
|
369
604
|
}
|
|
370
|
-
async function
|
|
605
|
+
async function deactivateJobInteractive(context, config) {
|
|
371
606
|
const jobs = config.jobs || [];
|
|
372
607
|
if (jobs.length === 0) {
|
|
373
608
|
console.log("\nℹ️ No jobs found.\n");
|
|
374
609
|
return;
|
|
375
610
|
}
|
|
376
|
-
const
|
|
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)
|
|
611
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to deactivate:');
|
|
612
|
+
if (!selectedJob)
|
|
388
613
|
return;
|
|
389
|
-
// Confirm deactivation
|
|
390
614
|
const confirmAnswer = await safePrompt([
|
|
391
615
|
{
|
|
392
616
|
type: 'confirm',
|
|
393
617
|
name: 'confirm',
|
|
394
|
-
message: `Are you sure you want to deactivate "${
|
|
618
|
+
message: `Are you sure you want to deactivate "${selectedJob.name}"? It will stop running on schedule.`,
|
|
395
619
|
default: false
|
|
396
620
|
}
|
|
397
621
|
]);
|
|
@@ -399,124 +623,63 @@ async function deactivateJob(context, config) {
|
|
|
399
623
|
console.log("\n❌ Deactivation cancelled.\n");
|
|
400
624
|
return;
|
|
401
625
|
}
|
|
402
|
-
|
|
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
|
-
}
|
|
626
|
+
await deactivateJobCore(context, selectedJob);
|
|
412
627
|
}
|
|
413
|
-
async function
|
|
628
|
+
async function triggerJobInteractive(context, config) {
|
|
414
629
|
const jobs = config.jobs || [];
|
|
415
630
|
if (jobs.length === 0) {
|
|
416
631
|
console.log("\nℹ️ No jobs found.\n");
|
|
417
632
|
return;
|
|
418
633
|
}
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
]);
|
|
430
|
-
if (!jobAnswer)
|
|
634
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to trigger:');
|
|
635
|
+
if (!selectedJob)
|
|
636
|
+
return;
|
|
637
|
+
await triggerJobCore(context, selectedJob);
|
|
638
|
+
}
|
|
639
|
+
async function viewExecutionHistoryInteractive(context, config) {
|
|
640
|
+
const jobs = config.jobs || [];
|
|
641
|
+
if (jobs.length === 0) {
|
|
642
|
+
console.log("\nℹ️ No jobs found in configuration.\n");
|
|
643
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
431
644
|
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
645
|
}
|
|
646
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to view execution history:');
|
|
647
|
+
if (!selectedJob)
|
|
648
|
+
return;
|
|
649
|
+
writeProgress(`🔄 Loading execution history for ${selectedJob.name}...`);
|
|
650
|
+
await fetchAndDisplayHistoryCore(context, selectedJob);
|
|
651
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
442
652
|
}
|
|
443
|
-
|
|
444
|
-
* View execution history for a job
|
|
445
|
-
*/
|
|
446
|
-
async function viewExecutionHistory(context, config) {
|
|
653
|
+
async function deleteJobInteractive(context, config) {
|
|
447
654
|
const jobs = config.jobs || [];
|
|
448
655
|
if (jobs.length === 0) {
|
|
449
656
|
console.log("\nℹ️ No jobs found in configuration.\n");
|
|
450
657
|
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
451
658
|
return;
|
|
452
659
|
}
|
|
453
|
-
|
|
454
|
-
|
|
660
|
+
const selectedJob = await promptJobSelection(jobs, 'Select a job to delete:');
|
|
661
|
+
if (!selectedJob)
|
|
662
|
+
return;
|
|
663
|
+
console.log("\n⚠️ WARNING: You are about to delete a job!");
|
|
664
|
+
console.log("⚠️ If the job has versions, it will be deactivated instead.\n");
|
|
665
|
+
const confirmAnswer = await safePrompt([
|
|
455
666
|
{
|
|
456
|
-
type: '
|
|
457
|
-
name: '
|
|
458
|
-
message:
|
|
459
|
-
|
|
460
|
-
name: `${job.name} (${job.jobId})`,
|
|
461
|
-
value: job
|
|
462
|
-
}))
|
|
667
|
+
type: 'confirm',
|
|
668
|
+
name: 'confirm',
|
|
669
|
+
message: `Are you sure you want to delete "${selectedJob.name}"?`,
|
|
670
|
+
default: false
|
|
463
671
|
}
|
|
464
672
|
]);
|
|
465
|
-
if (!
|
|
673
|
+
if (!confirmAnswer || !confirmAnswer.confirm) {
|
|
674
|
+
console.log("\n❌ Deletion cancelled.\n");
|
|
466
675
|
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
676
|
}
|
|
677
|
+
await deleteJobCore(context, selectedJob);
|
|
678
|
+
await safePrompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
|
|
516
679
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
680
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
681
|
+
// Utility Functions
|
|
682
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
520
683
|
function formatSchedule(schedule) {
|
|
521
684
|
if (!schedule)
|
|
522
685
|
return 'Not configured';
|