@qodalis/cli-server-jobs 2.0.0-beta.9 → 2.0.0-rc.2
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/package.json +2 -2
- package/public-api.d.mts +18 -2
- package/public-api.d.ts +18 -2
- package/public-api.js +460 -256
- package/public-api.js.map +1 -1
- package/public-api.mjs +461 -257
- package/public-api.mjs.map +1 -1
- package/umd/index.global.js +763 -321
package/public-api.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultLibraryAuthor, CliIcon, CliForegroundColor, ICliCompletionProvider_TOKEN } from '@qodalis/cli-core';
|
|
1
|
+
import { DefaultLibraryAuthor, CliIcon, writeWrapped, writeUsage, writeExamples, CliForegroundColor, joinShellWords, resolveServerWebSocketUrl, resolveServerHeaders, ICliCompletionProvider_TOKEN } from '@qodalis/cli-core';
|
|
2
2
|
|
|
3
3
|
// src/lib/processors/cli-jobs-command-processor.ts
|
|
4
4
|
|
|
@@ -91,7 +91,7 @@ var CliJobsService = class {
|
|
|
91
91
|
|
|
92
92
|
// src/lib/processors/cli-jobs-command-processor.ts
|
|
93
93
|
function getServices(context, args) {
|
|
94
|
-
const manager = context.services.
|
|
94
|
+
const manager = context.services.getRequired("cli-server-manager");
|
|
95
95
|
if (!manager || !manager.connections) {
|
|
96
96
|
return [];
|
|
97
97
|
}
|
|
@@ -102,7 +102,7 @@ function getServices(context, args) {
|
|
|
102
102
|
if (!connection.connected) continue;
|
|
103
103
|
const config = connection.config;
|
|
104
104
|
const baseUrl = config.url.endsWith("/") ? config.url.slice(0, -1) : config.url;
|
|
105
|
-
const headers = config.headers
|
|
105
|
+
const headers = resolveServerHeaders(context.services, name, config.headers);
|
|
106
106
|
results.push({ name, service: new CliJobsService(baseUrl, headers) });
|
|
107
107
|
}
|
|
108
108
|
return results;
|
|
@@ -115,10 +115,41 @@ async function resolveJobId(service, nameOrId) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
const jobs = await service.listJobs();
|
|
118
|
-
const
|
|
119
|
-
|
|
118
|
+
const lower = nameOrId.toLowerCase();
|
|
119
|
+
return jobs.find((j) => j.id === nameOrId) ?? jobs.find((j) => j.name === nameOrId) ?? jobs.find((j) => j.name.toLowerCase() === lower) ?? null;
|
|
120
|
+
}
|
|
121
|
+
async function findJobOnServers(context, services, nameOrId) {
|
|
122
|
+
const matches = [];
|
|
123
|
+
const missing = [];
|
|
124
|
+
for (const { name, service } of services) {
|
|
125
|
+
try {
|
|
126
|
+
const job = await resolveJobId(service, nameOrId);
|
|
127
|
+
if (job) {
|
|
128
|
+
matches.push({ name, service, job });
|
|
129
|
+
} else {
|
|
130
|
+
missing.push(name);
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
context.writer.writeError(`Error on server ${name}: ${e.message}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (matches.length === 0 && missing.length > 0) {
|
|
137
|
+
if (missing.length === services.length) {
|
|
138
|
+
context.writer.writeError(
|
|
139
|
+
services.length === 1 ? `Job "${nameOrId}" not found on server ${missing[0]}.` : `Job "${nameOrId}" not found on any of the ${services.length} connected servers.`
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
context.writer.writeError(
|
|
143
|
+
`Job "${nameOrId}" not found on ${missing.length === 1 ? "server" : "servers"} ${missing.join(", ")}.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return matches;
|
|
148
|
+
}
|
|
149
|
+
function writeServerHeader(context, name) {
|
|
150
|
+
context.writer.writeln(
|
|
151
|
+
context.writer.wrapInColor(`Server: ${name}`, CliForegroundColor.Cyan)
|
|
120
152
|
);
|
|
121
|
-
return match ?? null;
|
|
122
153
|
}
|
|
123
154
|
function formatRelativeTime(isoString) {
|
|
124
155
|
if (!isoString) return "-";
|
|
@@ -193,6 +224,26 @@ var CliJobsCommandProcessor = class {
|
|
|
193
224
|
command: "jobs",
|
|
194
225
|
description: "Manage background jobs on connected servers",
|
|
195
226
|
author: DefaultLibraryAuthor,
|
|
227
|
+
writeDescription: (context) => {
|
|
228
|
+
const t = context.translator;
|
|
229
|
+
writeWrapped(
|
|
230
|
+
context,
|
|
231
|
+
t.t("cli.server.jobs.help.see_control_background_jobs", "See and control the background jobs that run on connected servers: list them, run one now, pause or stop it, read its history and logs, change its settings, or watch events live. A job is picked by name (any case) or ID and looked up on every connected server; actions apply to each server that has the job, and --server limits a command to one server.")
|
|
232
|
+
);
|
|
233
|
+
context.writer.writeln();
|
|
234
|
+
writeUsage(context, [
|
|
235
|
+
["server jobs", t.t("cli.server.jobs.help.same_server_jobs_list", "Same as server jobs list")],
|
|
236
|
+
["server jobs <action> <name|id>", "info, trigger, pause, resume, stop, cancel, history, logs, edit"],
|
|
237
|
+
["server jobs watch", t.t("cli.server.jobs.help.live_job_events", "Live job events")]
|
|
238
|
+
]);
|
|
239
|
+
context.writer.writeln();
|
|
240
|
+
writeExamples(context, [
|
|
241
|
+
["server jobs", t.t("cli.server.jobs.help.all_jobs", "All jobs")],
|
|
242
|
+
["server jobs trigger cleanup", t.t("cli.server.jobs.help.run_cleanup_job_now", "Run the cleanup job now")],
|
|
243
|
+
["server jobs history cleanup --status failed", t.t("cli.server.jobs.help.failed_runs", "Its failed runs")],
|
|
244
|
+
["server jobs logs cleanup --server node", t.t("cli.server.jobs.help.last_run_output_node", "Last run output, on node")]
|
|
245
|
+
]);
|
|
246
|
+
},
|
|
196
247
|
metadata: {
|
|
197
248
|
icon: CliIcon.Gear,
|
|
198
249
|
module: "@qodalis/cli-server-jobs"
|
|
@@ -203,6 +254,23 @@ var CliJobsCommandProcessor = class {
|
|
|
203
254
|
{
|
|
204
255
|
command: "list",
|
|
205
256
|
description: "List all jobs across connected servers",
|
|
257
|
+
writeDescription: (context) => {
|
|
258
|
+
const t = context.translator;
|
|
259
|
+
writeWrapped(
|
|
260
|
+
context,
|
|
261
|
+
t.t("cli.server.jobs.list.help.list_jobs_every_connected", "List the jobs of every connected server: group, status, schedule, last and next run, and how long the last run took. With several servers, each gets its own table.")
|
|
262
|
+
);
|
|
263
|
+
context.writer.writeln();
|
|
264
|
+
writeUsage(context, [
|
|
265
|
+
["server jobs list", t.t("cli.server.jobs.list.help.jobs_all_connected_servers", "Jobs on all connected servers")],
|
|
266
|
+
["server jobs ls --group <name>", t.t("cli.server.jobs.list.help.only_one_group", "Only one group")]
|
|
267
|
+
]);
|
|
268
|
+
context.writer.writeln();
|
|
269
|
+
writeExamples(context, [
|
|
270
|
+
["server jobs ls --server node", t.t("cli.server.jobs.list.help.jobs_node_server", "Jobs on the node server")],
|
|
271
|
+
["server jobs list --group reports", t.t("cli.server.jobs.list.help.jobs_reports_group", "Jobs in the reports group")]
|
|
272
|
+
]);
|
|
273
|
+
},
|
|
206
274
|
aliases: ["ls"],
|
|
207
275
|
parameters: [
|
|
208
276
|
serverParam,
|
|
@@ -221,6 +289,21 @@ var CliJobsCommandProcessor = class {
|
|
|
221
289
|
{
|
|
222
290
|
command: "info",
|
|
223
291
|
description: "Show detailed information about a job",
|
|
292
|
+
writeDescription: (context) => {
|
|
293
|
+
const t = context.translator;
|
|
294
|
+
writeWrapped(
|
|
295
|
+
context,
|
|
296
|
+
t.t("cli.server.jobs.info.help.show_everything_about_one", "Show everything about one job: schedule, retries, timeout, overlap policy, the execution running now and the result of the last run. The job is found by name (any case) or ID.")
|
|
297
|
+
);
|
|
298
|
+
context.writer.writeln();
|
|
299
|
+
writeUsage(context, [
|
|
300
|
+
["server jobs info <name|id>", ""]
|
|
301
|
+
]);
|
|
302
|
+
context.writer.writeln();
|
|
303
|
+
writeExamples(context, [
|
|
304
|
+
["server jobs info cleanup", t.t("cli.server.jobs.info.help.details_cleanup_job", "Details of the cleanup job")]
|
|
305
|
+
]);
|
|
306
|
+
},
|
|
224
307
|
valueRequired: true,
|
|
225
308
|
parameters: [serverParam],
|
|
226
309
|
processCommand: async (cmd, context) => {
|
|
@@ -231,6 +314,22 @@ var CliJobsCommandProcessor = class {
|
|
|
231
314
|
{
|
|
232
315
|
command: "trigger",
|
|
233
316
|
description: "Trigger immediate execution of a job",
|
|
317
|
+
writeDescription: (context) => {
|
|
318
|
+
const t = context.translator;
|
|
319
|
+
writeWrapped(
|
|
320
|
+
context,
|
|
321
|
+
t.t("cli.server.jobs.trigger.help.run_job_now_without", "Run a job now, without waiting for its next scheduled time. Its schedule does not change.")
|
|
322
|
+
);
|
|
323
|
+
context.writer.writeln();
|
|
324
|
+
writeUsage(context, [
|
|
325
|
+
["server jobs trigger <name|id>", ""]
|
|
326
|
+
]);
|
|
327
|
+
context.writer.writeln();
|
|
328
|
+
writeExamples(context, [
|
|
329
|
+
["server jobs trigger cleanup", t.t("cli.server.jobs.trigger.help.run_cleanup_now", "Run cleanup now")],
|
|
330
|
+
["server jobs logs cleanup", t.t("cli.server.jobs.trigger.help.then_read_output", "Then read its output")]
|
|
331
|
+
]);
|
|
332
|
+
},
|
|
234
333
|
valueRequired: true,
|
|
235
334
|
parameters: [serverParam],
|
|
236
335
|
processCommand: async (cmd, context) => {
|
|
@@ -241,6 +340,22 @@ var CliJobsCommandProcessor = class {
|
|
|
241
340
|
{
|
|
242
341
|
command: "pause",
|
|
243
342
|
description: "Pause scheduled execution of a job",
|
|
343
|
+
writeDescription: (context) => {
|
|
344
|
+
const t = context.translator;
|
|
345
|
+
writeWrapped(
|
|
346
|
+
context,
|
|
347
|
+
t.t("cli.server.jobs.pause.help.stop_running_job_schedule", "Stop running a job on its schedule until it is resumed. Use cancel or stop for a run that is already in progress.")
|
|
348
|
+
);
|
|
349
|
+
context.writer.writeln();
|
|
350
|
+
writeUsage(context, [
|
|
351
|
+
["server jobs pause <name|id>", ""]
|
|
352
|
+
]);
|
|
353
|
+
context.writer.writeln();
|
|
354
|
+
writeExamples(context, [
|
|
355
|
+
["server jobs pause cleanup", t.t("cli.server.jobs.pause.help.hold_cleanup", "Hold cleanup")],
|
|
356
|
+
["server jobs resume cleanup", t.t("cli.server.jobs.pause.help.put_back_schedule", "Put it back on schedule")]
|
|
357
|
+
]);
|
|
358
|
+
},
|
|
244
359
|
valueRequired: true,
|
|
245
360
|
parameters: [serverParam],
|
|
246
361
|
processCommand: async (cmd, context) => {
|
|
@@ -251,6 +366,21 @@ var CliJobsCommandProcessor = class {
|
|
|
251
366
|
{
|
|
252
367
|
command: "resume",
|
|
253
368
|
description: "Resume a paused job",
|
|
369
|
+
writeDescription: (context) => {
|
|
370
|
+
const t = context.translator;
|
|
371
|
+
writeWrapped(
|
|
372
|
+
context,
|
|
373
|
+
t.t("cli.server.jobs.resume.help.put_paused_job_back", "Put a paused job back on its schedule.")
|
|
374
|
+
);
|
|
375
|
+
context.writer.writeln();
|
|
376
|
+
writeUsage(context, [
|
|
377
|
+
["server jobs resume <name|id>", ""]
|
|
378
|
+
]);
|
|
379
|
+
context.writer.writeln();
|
|
380
|
+
writeExamples(context, [
|
|
381
|
+
["server jobs resume cleanup", t.t("cli.server.jobs.resume.help.run_cleanup_schedule_again", "Run cleanup on schedule again")]
|
|
382
|
+
]);
|
|
383
|
+
},
|
|
254
384
|
valueRequired: true,
|
|
255
385
|
parameters: [serverParam],
|
|
256
386
|
processCommand: async (cmd, context) => {
|
|
@@ -261,6 +391,21 @@ var CliJobsCommandProcessor = class {
|
|
|
261
391
|
{
|
|
262
392
|
command: "stop",
|
|
263
393
|
description: "Stop a job and cancel current execution if running",
|
|
394
|
+
writeDescription: (context) => {
|
|
395
|
+
const t = context.translator;
|
|
396
|
+
writeWrapped(
|
|
397
|
+
context,
|
|
398
|
+
t.t("cli.server.jobs.stop.help.take_job_off_schedule", "Take a job off its schedule and cancel the run in progress, if there is one.")
|
|
399
|
+
);
|
|
400
|
+
context.writer.writeln();
|
|
401
|
+
writeUsage(context, [
|
|
402
|
+
["server jobs stop <name|id>", ""]
|
|
403
|
+
]);
|
|
404
|
+
context.writer.writeln();
|
|
405
|
+
writeExamples(context, [
|
|
406
|
+
["server jobs stop cleanup", t.t("cli.server.jobs.stop.help.stop_cleanup_completely", "Stop cleanup completely")]
|
|
407
|
+
]);
|
|
408
|
+
},
|
|
264
409
|
valueRequired: true,
|
|
265
410
|
parameters: [serverParam],
|
|
266
411
|
processCommand: async (cmd, context) => {
|
|
@@ -271,6 +416,21 @@ var CliJobsCommandProcessor = class {
|
|
|
271
416
|
{
|
|
272
417
|
command: "cancel",
|
|
273
418
|
description: "Cancel the current execution of a job",
|
|
419
|
+
writeDescription: (context) => {
|
|
420
|
+
const t = context.translator;
|
|
421
|
+
writeWrapped(
|
|
422
|
+
context,
|
|
423
|
+
t.t("cli.server.jobs.cancel.help.cancel_run_progress_job", "Cancel the run in progress of a job. The job stays on its schedule and runs again at the next time.")
|
|
424
|
+
);
|
|
425
|
+
context.writer.writeln();
|
|
426
|
+
writeUsage(context, [
|
|
427
|
+
["server jobs cancel <name|id>", ""]
|
|
428
|
+
]);
|
|
429
|
+
context.writer.writeln();
|
|
430
|
+
writeExamples(context, [
|
|
431
|
+
["server jobs cancel cleanup", t.t("cli.server.jobs.cancel.help.abort_current_cleanup_run", "Abort the current cleanup run")]
|
|
432
|
+
]);
|
|
433
|
+
},
|
|
274
434
|
valueRequired: true,
|
|
275
435
|
parameters: [serverParam],
|
|
276
436
|
processCommand: async (cmd, context) => {
|
|
@@ -281,6 +441,23 @@ var CliJobsCommandProcessor = class {
|
|
|
281
441
|
{
|
|
282
442
|
command: "history",
|
|
283
443
|
description: "Show execution history for a job",
|
|
444
|
+
writeDescription: (context) => {
|
|
445
|
+
const t = context.translator;
|
|
446
|
+
writeWrapped(
|
|
447
|
+
context,
|
|
448
|
+
t.t("cli.server.jobs.history.help.list_past_runs_job", "List the past runs of a job: status, start time, duration, retry attempt and the start of the error message. Use the short ID with logs --exec to read the output of one run.")
|
|
449
|
+
);
|
|
450
|
+
context.writer.writeln();
|
|
451
|
+
writeUsage(context, [
|
|
452
|
+
["server jobs history <name|id>", t.t("cli.server.jobs.history.help.last_20_runs", "The last 20 runs")],
|
|
453
|
+
["server jobs history <name|id> --limit <n> --offset <n>", t.t("cli.server.jobs.history.help.page_through_older_runs", "Page through older runs")]
|
|
454
|
+
]);
|
|
455
|
+
context.writer.writeln();
|
|
456
|
+
writeExamples(context, [
|
|
457
|
+
["server jobs history cleanup --status failed", t.t("cli.server.jobs.history.help.only_failed_runs", "Only failed runs")],
|
|
458
|
+
["server jobs history cleanup --limit 5", t.t("cli.server.jobs.history.help.last_5_runs", "The last 5 runs")]
|
|
459
|
+
]);
|
|
460
|
+
},
|
|
284
461
|
valueRequired: true,
|
|
285
462
|
parameters: [
|
|
286
463
|
serverParam,
|
|
@@ -313,6 +490,22 @@ var CliJobsCommandProcessor = class {
|
|
|
313
490
|
{
|
|
314
491
|
command: "logs",
|
|
315
492
|
description: "Show logs from the latest or a specific execution",
|
|
493
|
+
writeDescription: (context) => {
|
|
494
|
+
const t = context.translator;
|
|
495
|
+
writeWrapped(
|
|
496
|
+
context,
|
|
497
|
+
t.t("cli.server.jobs.logs.help.print_log_entries_latest", "Print the log entries of the latest run of a job, or of one run picked by its execution ID (from history).")
|
|
498
|
+
);
|
|
499
|
+
context.writer.writeln();
|
|
500
|
+
writeUsage(context, [
|
|
501
|
+
["server jobs logs <name|id>", t.t("cli.server.jobs.logs.help.logs_latest_run", "Logs of the latest run")],
|
|
502
|
+
["server jobs logs <name|id> --exec <id>", t.t("cli.server.jobs.logs.help.logs_one_run", "Logs of one run")]
|
|
503
|
+
]);
|
|
504
|
+
context.writer.writeln();
|
|
505
|
+
writeExamples(context, [
|
|
506
|
+
["server jobs logs cleanup", t.t("cli.server.jobs.logs.help.what_last_cleanup_run", "What the last cleanup run printed")]
|
|
507
|
+
]);
|
|
508
|
+
},
|
|
316
509
|
valueRequired: true,
|
|
317
510
|
parameters: [
|
|
318
511
|
serverParam,
|
|
@@ -331,6 +524,21 @@ var CliJobsCommandProcessor = class {
|
|
|
331
524
|
{
|
|
332
525
|
command: "edit",
|
|
333
526
|
description: "Interactively edit job settings",
|
|
527
|
+
writeDescription: (context) => {
|
|
528
|
+
const t = context.translator;
|
|
529
|
+
writeWrapped(
|
|
530
|
+
context,
|
|
531
|
+
t.t("cli.server.jobs.edit.help.change_job_step_step", "Change a job step by step: description, group, cron schedule or interval, max retries, timeout and overlap policy (skip, queue or cancel). Press Enter to keep a value and Ctrl+C or Escape to cancel; nothing changes until you confirm. A job on several servers needs --server.")
|
|
532
|
+
);
|
|
533
|
+
context.writer.writeln();
|
|
534
|
+
writeUsage(context, [
|
|
535
|
+
["server jobs edit <name|id>", ""]
|
|
536
|
+
]);
|
|
537
|
+
context.writer.writeln();
|
|
538
|
+
writeExamples(context, [
|
|
539
|
+
["server jobs edit cleanup", t.t("cli.server.jobs.edit.help.edit_cleanup_server_has", "Edit cleanup on the server that has it")]
|
|
540
|
+
]);
|
|
541
|
+
},
|
|
334
542
|
valueRequired: true,
|
|
335
543
|
parameters: [serverParam],
|
|
336
544
|
processCommand: async (cmd, context) => {
|
|
@@ -341,6 +549,21 @@ var CliJobsCommandProcessor = class {
|
|
|
341
549
|
{
|
|
342
550
|
command: "watch",
|
|
343
551
|
description: "Live view of job events via WebSocket",
|
|
552
|
+
writeDescription: (context) => {
|
|
553
|
+
const t = context.translator;
|
|
554
|
+
writeWrapped(
|
|
555
|
+
context,
|
|
556
|
+
t.t("cli.server.jobs.watch.help.follow_job_events_they", "Follow job events as they happen (started, completed, failed and so on) on every connected server, one line per event. Press Ctrl+C to stop.")
|
|
557
|
+
);
|
|
558
|
+
context.writer.writeln();
|
|
559
|
+
writeUsage(context, [
|
|
560
|
+
["server jobs watch", t.t("cli.server.jobs.watch.help.events_all_connected_servers", "Events from all connected servers")]
|
|
561
|
+
]);
|
|
562
|
+
context.writer.writeln();
|
|
563
|
+
writeExamples(context, [
|
|
564
|
+
["server jobs watch --server node", t.t("cli.server.jobs.watch.help.only_events_node", "Only events from node")]
|
|
565
|
+
]);
|
|
566
|
+
},
|
|
344
567
|
parameters: [serverParam],
|
|
345
568
|
processCommand: async (cmd, context) => {
|
|
346
569
|
await this.handleWatch(cmd, context);
|
|
@@ -414,9 +637,9 @@ var CliJobsCommandProcessor = class {
|
|
|
414
637
|
}
|
|
415
638
|
// ── Info ──────────────────────────────────────────────────────────
|
|
416
639
|
async handleInfo(cmd, context) {
|
|
417
|
-
const nameOrId = (cmd.value ?? "")
|
|
640
|
+
const nameOrId = joinShellWords(cmd.value ?? "");
|
|
418
641
|
if (!nameOrId) {
|
|
419
|
-
context.writer.writeError("Usage: jobs info <id|name>");
|
|
642
|
+
context.writer.writeError("Usage: server jobs info <id|name>");
|
|
420
643
|
return;
|
|
421
644
|
}
|
|
422
645
|
const services = getServices(context, cmd.args);
|
|
@@ -424,74 +647,53 @@ var CliJobsCommandProcessor = class {
|
|
|
424
647
|
context.writer.writeInfo("No connected servers.");
|
|
425
648
|
return;
|
|
426
649
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
],
|
|
461
|
-
["Overlap Policy", job.overlapPolicy],
|
|
462
|
-
[
|
|
463
|
-
"Current Execution",
|
|
464
|
-
job.currentExecutionId ?? "none"
|
|
465
|
-
],
|
|
466
|
-
["Next Run", formatRelativeTime(job.nextRunAt)],
|
|
467
|
-
["Last Run", formatRelativeTime(job.lastRunAt)],
|
|
468
|
-
[
|
|
469
|
-
"Last Run Status",
|
|
470
|
-
job.lastRunStatus ? statusColor(job.lastRunStatus, context.writer) : "-"
|
|
471
|
-
],
|
|
472
|
-
["Last Run Duration", formatDuration(job.lastRunDuration)]
|
|
473
|
-
];
|
|
474
|
-
for (const [label, value] of lines) {
|
|
475
|
-
context.writer.writeln(
|
|
476
|
-
` ${context.writer.wrapInColor(
|
|
477
|
-
label.padEnd(20),
|
|
478
|
-
CliForegroundColor.Yellow
|
|
479
|
-
)} ${value}`
|
|
480
|
-
);
|
|
481
|
-
}
|
|
482
|
-
return;
|
|
483
|
-
} catch (e) {
|
|
484
|
-
context.writer.writeError(
|
|
485
|
-
`Error on server ${name}: ${e.message}`
|
|
650
|
+
const matches = await findJobOnServers(context, services, nameOrId);
|
|
651
|
+
for (const { name, job } of matches) {
|
|
652
|
+
if (services.length > 1) {
|
|
653
|
+
writeServerHeader(context, name);
|
|
654
|
+
}
|
|
655
|
+
const lines = [
|
|
656
|
+
["ID", job.id],
|
|
657
|
+
["Name", job.name],
|
|
658
|
+
["Description", job.description],
|
|
659
|
+
["Group", job.group ?? "-"],
|
|
660
|
+
["Status", statusColor(job.status, context.writer)],
|
|
661
|
+
[
|
|
662
|
+
"Schedule",
|
|
663
|
+
job.schedule ?? (job.interval ? `every ${job.interval}ms` : "-")
|
|
664
|
+
],
|
|
665
|
+
["Max Retries", String(job.maxRetries)],
|
|
666
|
+
["Timeout", job.timeout ? `${job.timeout}ms` : "none"],
|
|
667
|
+
["Overlap Policy", job.overlapPolicy],
|
|
668
|
+
["Current Execution", job.currentExecutionId ?? "none"],
|
|
669
|
+
["Next Run", formatRelativeTime(job.nextRunAt)],
|
|
670
|
+
["Last Run", formatRelativeTime(job.lastRunAt)],
|
|
671
|
+
[
|
|
672
|
+
"Last Run Status",
|
|
673
|
+
job.lastRunStatus ? statusColor(job.lastRunStatus, context.writer) : "-"
|
|
674
|
+
],
|
|
675
|
+
["Last Run Duration", formatDuration(job.lastRunDuration)]
|
|
676
|
+
];
|
|
677
|
+
for (const [label, value] of lines) {
|
|
678
|
+
context.writer.writeln(
|
|
679
|
+
` ${context.writer.wrapInColor(
|
|
680
|
+
label.padEnd(20),
|
|
681
|
+
CliForegroundColor.Yellow
|
|
682
|
+
)} ${value}`
|
|
486
683
|
);
|
|
487
684
|
}
|
|
488
685
|
}
|
|
489
686
|
}
|
|
490
687
|
// ── Action (trigger/pause/resume/stop/cancel) ────────────────────
|
|
688
|
+
/**
|
|
689
|
+
* Without --server the action applies to every connected server that has
|
|
690
|
+
* the job, each reported on its own line. When several servers have it,
|
|
691
|
+
* a note says so (and how to target one).
|
|
692
|
+
*/
|
|
491
693
|
async handleAction(cmd, context, action) {
|
|
492
|
-
const nameOrId = (cmd.value ?? "")
|
|
694
|
+
const nameOrId = joinShellWords(cmd.value ?? "");
|
|
493
695
|
if (!nameOrId) {
|
|
494
|
-
context.writer.writeError(`Usage: jobs ${action} <id|name>`);
|
|
696
|
+
context.writer.writeError(`Usage: server jobs ${action} <id|name>`);
|
|
495
697
|
return;
|
|
496
698
|
}
|
|
497
699
|
const services = getServices(context, cmd.args);
|
|
@@ -499,17 +701,14 @@ var CliJobsCommandProcessor = class {
|
|
|
499
701
|
context.writer.writeInfo("No connected servers.");
|
|
500
702
|
return;
|
|
501
703
|
}
|
|
502
|
-
|
|
704
|
+
const matches = await findJobOnServers(context, services, nameOrId);
|
|
705
|
+
if (matches.length > 1) {
|
|
706
|
+
context.writer.writeInfo(
|
|
707
|
+
`Job "${nameOrId}" is on ${matches.length} servers (${matches.map((m) => m.name).join(", ")}); applying ${action} to each. Use --server <name> to target one.`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
for (const { name, service, job } of matches) {
|
|
503
711
|
try {
|
|
504
|
-
const job = await resolveJobId(service, nameOrId);
|
|
505
|
-
if (!job) {
|
|
506
|
-
if (services.length === 1) {
|
|
507
|
-
context.writer.writeError(
|
|
508
|
-
`Job "${nameOrId}" not found on server ${name}.`
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
continue;
|
|
512
|
-
}
|
|
513
712
|
switch (action) {
|
|
514
713
|
case "trigger":
|
|
515
714
|
await service.triggerJob(job.id);
|
|
@@ -529,9 +728,8 @@ var CliJobsCommandProcessor = class {
|
|
|
529
728
|
}
|
|
530
729
|
const serverSuffix = services.length > 1 ? ` on server ${name}` : "";
|
|
531
730
|
context.writer.writeSuccess(
|
|
532
|
-
`Job "${job.name}" ${action
|
|
731
|
+
`Job "${job.name}" ${PAST_TENSE[action] ?? `${action}ed`}${serverSuffix}.`
|
|
533
732
|
);
|
|
534
|
-
return;
|
|
535
733
|
} catch (e) {
|
|
536
734
|
context.writer.writeError(
|
|
537
735
|
`Failed to ${action} job on ${name}: ${e.message}`
|
|
@@ -541,9 +739,9 @@ var CliJobsCommandProcessor = class {
|
|
|
541
739
|
}
|
|
542
740
|
// ── History ──────────────────────────────────────────────────────
|
|
543
741
|
async handleHistory(cmd, context) {
|
|
544
|
-
const nameOrId = (cmd.value ?? "")
|
|
742
|
+
const nameOrId = joinShellWords(cmd.value ?? "");
|
|
545
743
|
if (!nameOrId) {
|
|
546
|
-
context.writer.writeError("Usage: jobs history <id|name>");
|
|
744
|
+
context.writer.writeError("Usage: server jobs history <id|name>");
|
|
547
745
|
return;
|
|
548
746
|
}
|
|
549
747
|
const services = getServices(context, cmd.args);
|
|
@@ -554,24 +752,11 @@ var CliJobsCommandProcessor = class {
|
|
|
554
752
|
const limit = cmd.args["limit"] ?? 20;
|
|
555
753
|
const offset = cmd.args["offset"] ?? 0;
|
|
556
754
|
const status = cmd.args["status"];
|
|
557
|
-
|
|
755
|
+
const matches = await findJobOnServers(context, services, nameOrId);
|
|
756
|
+
for (const { name, service, job } of matches) {
|
|
558
757
|
try {
|
|
559
|
-
const job = await resolveJobId(service, nameOrId);
|
|
560
|
-
if (!job) {
|
|
561
|
-
if (services.length === 1) {
|
|
562
|
-
context.writer.writeError(
|
|
563
|
-
`Job "${nameOrId}" not found on server ${name}.`
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
758
|
if (services.length > 1) {
|
|
569
|
-
context
|
|
570
|
-
context.writer.wrapInColor(
|
|
571
|
-
`Server: ${name}`,
|
|
572
|
-
CliForegroundColor.Cyan
|
|
573
|
-
)
|
|
574
|
-
);
|
|
759
|
+
writeServerHeader(context, name);
|
|
575
760
|
}
|
|
576
761
|
const history = await service.getHistory(job.id, {
|
|
577
762
|
limit,
|
|
@@ -582,7 +767,7 @@ var CliJobsCommandProcessor = class {
|
|
|
582
767
|
context.writer.writeInfo(
|
|
583
768
|
`No execution history for "${job.name}".`
|
|
584
769
|
);
|
|
585
|
-
|
|
770
|
+
continue;
|
|
586
771
|
}
|
|
587
772
|
context.writer.writeln(
|
|
588
773
|
`Showing ${history.items.length} of ${history.total} executions for "${job.name}":`
|
|
@@ -604,7 +789,6 @@ var CliJobsCommandProcessor = class {
|
|
|
604
789
|
exec.error ? exec.error.substring(0, 40) : "-"
|
|
605
790
|
]);
|
|
606
791
|
context.writer.writeTable(headers, rows);
|
|
607
|
-
return;
|
|
608
792
|
} catch (e) {
|
|
609
793
|
context.writer.writeError(
|
|
610
794
|
`Error on server ${name}: ${e.message}`
|
|
@@ -614,9 +798,9 @@ var CliJobsCommandProcessor = class {
|
|
|
614
798
|
}
|
|
615
799
|
// ── Logs ─────────────────────────────────────────────────────────
|
|
616
800
|
async handleLogs(cmd, context) {
|
|
617
|
-
const nameOrId = (cmd.value ?? "")
|
|
801
|
+
const nameOrId = joinShellWords(cmd.value ?? "");
|
|
618
802
|
if (!nameOrId) {
|
|
619
|
-
context.writer.writeError("Usage: jobs logs <id|name>");
|
|
803
|
+
context.writer.writeError("Usage: server jobs logs <id|name>");
|
|
620
804
|
return;
|
|
621
805
|
}
|
|
622
806
|
const services = getServices(context, cmd.args);
|
|
@@ -625,16 +809,11 @@ var CliJobsCommandProcessor = class {
|
|
|
625
809
|
return;
|
|
626
810
|
}
|
|
627
811
|
const execId = cmd.args["exec"];
|
|
628
|
-
|
|
812
|
+
const matches = await findJobOnServers(context, services, nameOrId);
|
|
813
|
+
for (const { name, service, job } of matches) {
|
|
629
814
|
try {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
if (services.length === 1) {
|
|
633
|
-
context.writer.writeError(
|
|
634
|
-
`Job "${nameOrId}" not found on server ${name}.`
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
continue;
|
|
815
|
+
if (services.length > 1) {
|
|
816
|
+
writeServerHeader(context, name);
|
|
638
817
|
}
|
|
639
818
|
let execution;
|
|
640
819
|
if (execId) {
|
|
@@ -647,27 +826,19 @@ var CliJobsCommandProcessor = class {
|
|
|
647
826
|
context.writer.writeInfo(
|
|
648
827
|
`No executions found for "${job.name}".`
|
|
649
828
|
);
|
|
650
|
-
|
|
829
|
+
continue;
|
|
651
830
|
}
|
|
652
831
|
execution = await service.getExecution(
|
|
653
832
|
job.id,
|
|
654
833
|
history.items[0].id
|
|
655
834
|
);
|
|
656
835
|
}
|
|
657
|
-
if (services.length > 1) {
|
|
658
|
-
context.writer.writeln(
|
|
659
|
-
context.writer.wrapInColor(
|
|
660
|
-
`Server: ${name}`,
|
|
661
|
-
CliForegroundColor.Cyan
|
|
662
|
-
)
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
836
|
context.writer.writeln(
|
|
666
837
|
`Logs for "${job.name}" execution ${execution.id.substring(0, 8)} (${statusColor(execution.status, context.writer)}):`
|
|
667
838
|
);
|
|
668
839
|
if (!execution.logs || execution.logs.length === 0) {
|
|
669
840
|
context.writer.writeInfo(" No log entries.");
|
|
670
|
-
|
|
841
|
+
continue;
|
|
671
842
|
}
|
|
672
843
|
for (const entry of execution.logs) {
|
|
673
844
|
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
@@ -679,7 +850,6 @@ var CliJobsCommandProcessor = class {
|
|
|
679
850
|
` ${context.writer.wrapInColor(ts, CliForegroundColor.White)} ${level} ${entry.message}`
|
|
680
851
|
);
|
|
681
852
|
}
|
|
682
|
-
return;
|
|
683
853
|
} catch (e) {
|
|
684
854
|
context.writer.writeError(
|
|
685
855
|
`Error on server ${name}: ${e.message}`
|
|
@@ -688,10 +858,15 @@ var CliJobsCommandProcessor = class {
|
|
|
688
858
|
}
|
|
689
859
|
}
|
|
690
860
|
// ── Edit ─────────────────────────────────────────────────────────
|
|
861
|
+
/**
|
|
862
|
+
* Prompt for each setting; Enter keeps the current value. Ctrl+C or
|
|
863
|
+
* Escape at any prompt (the reader resolves null) cancels the whole edit
|
|
864
|
+
* and nothing is sent. A job on several servers needs --server.
|
|
865
|
+
*/
|
|
691
866
|
async handleEdit(cmd, context) {
|
|
692
|
-
const nameOrId = (cmd.value ?? "")
|
|
867
|
+
const nameOrId = joinShellWords(cmd.value ?? "");
|
|
693
868
|
if (!nameOrId) {
|
|
694
|
-
context.writer.writeError("Usage: jobs edit <id|name>");
|
|
869
|
+
context.writer.writeError("Usage: server jobs edit <id|name>");
|
|
695
870
|
return;
|
|
696
871
|
}
|
|
697
872
|
const services = getServices(context, cmd.args);
|
|
@@ -699,118 +874,113 @@ var CliJobsCommandProcessor = class {
|
|
|
699
874
|
context.writer.writeInfo("No connected servers.");
|
|
700
875
|
return;
|
|
701
876
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
context.writer.writeInfo("Edit cancelled.");
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
if (newSchedule.trim()) {
|
|
750
|
-
update.schedule = newSchedule.trim();
|
|
751
|
-
}
|
|
752
|
-
if (!newSchedule.trim()) {
|
|
753
|
-
const newInterval = await context.reader.readLine(
|
|
754
|
-
`Interval (e.g. 30s, 5m) [${job.interval ? `${job.interval}ms` : "none"}]: `
|
|
755
|
-
);
|
|
756
|
-
if (newInterval != null && newInterval.trim()) {
|
|
757
|
-
update.interval = newInterval.trim();
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
const newRetries = await context.reader.readLine(
|
|
761
|
-
`Max retries [${job.maxRetries}]: `
|
|
762
|
-
);
|
|
763
|
-
if (newRetries != null && newRetries.trim()) {
|
|
764
|
-
const parsed = parseInt(newRetries.trim(), 10);
|
|
765
|
-
if (!isNaN(parsed)) {
|
|
766
|
-
update.maxRetries = parsed;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
const newTimeout = await context.reader.readLine(
|
|
770
|
-
`Timeout (e.g. 5m, 1h) [${job.timeout ? `${job.timeout}ms` : "none"}]: `
|
|
771
|
-
);
|
|
772
|
-
if (newTimeout != null && newTimeout.trim()) {
|
|
773
|
-
update.timeout = newTimeout.trim();
|
|
774
|
-
}
|
|
775
|
-
const newOverlap = await context.reader.readLine(
|
|
776
|
-
`Overlap policy (skip/queue/cancel) [${job.overlapPolicy}]: `
|
|
777
|
-
);
|
|
778
|
-
if (newOverlap != null && newOverlap.trim() && ["skip", "queue", "cancel"].includes(
|
|
779
|
-
newOverlap.trim().toLowerCase()
|
|
780
|
-
)) {
|
|
781
|
-
update.overlapPolicy = newOverlap.trim().toLowerCase();
|
|
782
|
-
}
|
|
783
|
-
if (Object.keys(update).length === 0) {
|
|
784
|
-
context.writer.writeInfo("No changes made.");
|
|
785
|
-
return;
|
|
786
|
-
}
|
|
787
|
-
const confirm = await context.reader.readConfirm(
|
|
788
|
-
"Apply changes?"
|
|
877
|
+
const matches = await findJobOnServers(context, services, nameOrId);
|
|
878
|
+
if (matches.length === 0) {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (matches.length > 1) {
|
|
882
|
+
context.writer.writeError(
|
|
883
|
+
`Job "${nameOrId}" is on ${matches.length} servers (${matches.map((m) => m.name).join(", ")}). Use --server <name> to choose the one to edit.`
|
|
884
|
+
);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const { name, service, job } = matches[0];
|
|
888
|
+
const cancelled = () => {
|
|
889
|
+
context.writer.writeInfo("Edit cancelled.");
|
|
890
|
+
};
|
|
891
|
+
const aborted = () => !!context.signal?.aborted;
|
|
892
|
+
const ask = async (prompt) => {
|
|
893
|
+
if (aborted()) return null;
|
|
894
|
+
const answer = await context.reader.readLine(prompt);
|
|
895
|
+
return answer == null || aborted() ? null : answer.trim();
|
|
896
|
+
};
|
|
897
|
+
try {
|
|
898
|
+
context.writer.writeln(
|
|
899
|
+
`Editing job "${context.writer.wrapInColor(job.name, CliForegroundColor.Cyan)}" on server ${name}`
|
|
900
|
+
);
|
|
901
|
+
context.writer.writeln(
|
|
902
|
+
"Press Enter to keep current value, or type a new value. Ctrl+C cancels."
|
|
903
|
+
);
|
|
904
|
+
context.writer.writeln("");
|
|
905
|
+
const update = {};
|
|
906
|
+
const newDesc = await ask(`Description [${job.description}]: `);
|
|
907
|
+
if (newDesc == null) return cancelled();
|
|
908
|
+
if (newDesc) update.description = newDesc;
|
|
909
|
+
const newGroup = await ask(`Group [${job.group ?? "none"}]: `);
|
|
910
|
+
if (newGroup == null) return cancelled();
|
|
911
|
+
if (newGroup) update.group = newGroup;
|
|
912
|
+
const currentSchedule = job.schedule ?? (job.interval ? `interval: ${job.interval}ms` : "none");
|
|
913
|
+
const newSchedule = await ask(
|
|
914
|
+
`Cron schedule [${currentSchedule}]: `
|
|
915
|
+
);
|
|
916
|
+
if (newSchedule == null) return cancelled();
|
|
917
|
+
if (newSchedule) update.schedule = newSchedule;
|
|
918
|
+
if (!newSchedule) {
|
|
919
|
+
const newInterval = await ask(
|
|
920
|
+
`Interval (e.g. 30s, 5m) [${job.interval ? `${job.interval}ms` : "none"}]: `
|
|
789
921
|
);
|
|
790
|
-
if (
|
|
791
|
-
|
|
792
|
-
|
|
922
|
+
if (newInterval == null) return cancelled();
|
|
923
|
+
if (newInterval) update.interval = newInterval;
|
|
924
|
+
}
|
|
925
|
+
const newRetries = await ask(`Max retries [${job.maxRetries}]: `);
|
|
926
|
+
if (newRetries == null) return cancelled();
|
|
927
|
+
if (newRetries) {
|
|
928
|
+
const parsed = parseInt(newRetries, 10);
|
|
929
|
+
if (!isNaN(parsed)) {
|
|
930
|
+
update.maxRetries = parsed;
|
|
793
931
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
932
|
+
}
|
|
933
|
+
const newTimeout = await ask(
|
|
934
|
+
`Timeout (e.g. 5m, 1h) [${job.timeout ? `${job.timeout}ms` : "none"}]: `
|
|
935
|
+
);
|
|
936
|
+
if (newTimeout == null) return cancelled();
|
|
937
|
+
if (newTimeout) update.timeout = newTimeout;
|
|
938
|
+
const newOverlap = await ask(
|
|
939
|
+
`Overlap policy (skip/queue/cancel) [${job.overlapPolicy}]: `
|
|
940
|
+
);
|
|
941
|
+
if (newOverlap == null) return cancelled();
|
|
942
|
+
if (newOverlap && ["skip", "queue", "cancel"].includes(newOverlap.toLowerCase())) {
|
|
943
|
+
update.overlapPolicy = newOverlap.toLowerCase();
|
|
944
|
+
}
|
|
945
|
+
if (Object.keys(update).length === 0) {
|
|
946
|
+
context.writer.writeInfo("No changes made.");
|
|
798
947
|
return;
|
|
799
|
-
} catch (e) {
|
|
800
|
-
context.writer.writeError(
|
|
801
|
-
`Error on server ${name}: ${e.message}`
|
|
802
|
-
);
|
|
803
948
|
}
|
|
949
|
+
const confirm = aborted() ? null : await context.reader.readConfirm("Apply changes?");
|
|
950
|
+
if (!confirm || aborted()) return cancelled();
|
|
951
|
+
await service.updateJob(job.id, update);
|
|
952
|
+
context.writer.writeSuccess(
|
|
953
|
+
`Job "${job.name}" updated successfully.`
|
|
954
|
+
);
|
|
955
|
+
} catch (e) {
|
|
956
|
+
context.writer.writeError(`Error on server ${name}: ${e.message}`);
|
|
804
957
|
}
|
|
805
958
|
}
|
|
806
959
|
// ── Watch ────────────────────────────────────────────────────────
|
|
960
|
+
/**
|
|
961
|
+
* Stream job events until Ctrl+C (context.onAbort / context.signal) or
|
|
962
|
+
* until every watched server has closed its connection. On exit the
|
|
963
|
+
* sockets are closed and every subscription and listener is removed.
|
|
964
|
+
*/
|
|
807
965
|
async handleWatch(cmd, context) {
|
|
808
|
-
const manager = context.services.
|
|
966
|
+
const manager = context.services.getRequired("cli-server-manager");
|
|
809
967
|
if (!manager || !manager.connections) {
|
|
810
968
|
context.writer.writeInfo("No connected servers.");
|
|
811
969
|
return;
|
|
812
970
|
}
|
|
813
971
|
const targetServer = cmd.args["server"];
|
|
972
|
+
const targets = [];
|
|
973
|
+
for (const [serverName, connection] of manager.connections) {
|
|
974
|
+
if (targetServer && serverName !== targetServer) continue;
|
|
975
|
+
if (!connection.connected) continue;
|
|
976
|
+
targets.push([serverName, connection]);
|
|
977
|
+
}
|
|
978
|
+
if (targets.length === 0) {
|
|
979
|
+
context.writer.writeInfo(
|
|
980
|
+
targetServer ? `Server "${targetServer}" is not connected.` : "No connected servers."
|
|
981
|
+
);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
814
984
|
context.writer.writeln(
|
|
815
985
|
context.writer.wrapInColor(
|
|
816
986
|
"Watching job events... (press Ctrl+C to stop)",
|
|
@@ -818,33 +988,60 @@ var CliJobsCommandProcessor = class {
|
|
|
818
988
|
)
|
|
819
989
|
);
|
|
820
990
|
context.writer.writeln("");
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
991
|
+
const open = /* @__PURE__ */ new Set();
|
|
992
|
+
let stopped = false;
|
|
993
|
+
let reason = "aborted";
|
|
994
|
+
let resolveDone;
|
|
995
|
+
const done = new Promise((resolve) => resolveDone = resolve);
|
|
996
|
+
const closeSocket = (ws) => {
|
|
997
|
+
ws.onmessage = null;
|
|
998
|
+
ws.onerror = null;
|
|
999
|
+
ws.onclose = null;
|
|
1000
|
+
try {
|
|
1001
|
+
ws.close();
|
|
1002
|
+
} catch {
|
|
828
1003
|
}
|
|
829
1004
|
};
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
1005
|
+
const onAbortSignal = () => stop("aborted");
|
|
1006
|
+
const abortSub = context.onAbort?.subscribe(onAbortSignal);
|
|
1007
|
+
context.signal?.addEventListener("abort", onAbortSignal);
|
|
1008
|
+
function stop(why) {
|
|
1009
|
+
if (stopped) return;
|
|
1010
|
+
stopped = true;
|
|
1011
|
+
reason = why;
|
|
1012
|
+
abortSub?.unsubscribe();
|
|
1013
|
+
context.signal?.removeEventListener("abort", onAbortSignal);
|
|
1014
|
+
for (const ws of open) closeSocket(ws);
|
|
1015
|
+
open.clear();
|
|
1016
|
+
resolveDone();
|
|
1017
|
+
}
|
|
1018
|
+
if (context.signal?.aborted) {
|
|
1019
|
+
stop("aborted");
|
|
1020
|
+
}
|
|
1021
|
+
const multiServer = targets.length > 1;
|
|
1022
|
+
for (const [serverName, connection] of targets) {
|
|
1023
|
+
if (stopped) break;
|
|
834
1024
|
const config = connection.config;
|
|
835
1025
|
const baseUrl = config.url.endsWith("/") ? config.url.slice(0, -1) : config.url;
|
|
836
|
-
const wsUrl = baseUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:") + "/ws/v1/qcli/events";
|
|
837
1026
|
try {
|
|
1027
|
+
const wsUrl = await resolveServerWebSocketUrl(
|
|
1028
|
+
context.services,
|
|
1029
|
+
serverName,
|
|
1030
|
+
baseUrl.replace(/^https:/, "wss:").replace(/^http:/, "ws:") + "/ws/v1/qcli/events",
|
|
1031
|
+
config.headers
|
|
1032
|
+
);
|
|
1033
|
+
if (stopped) break;
|
|
838
1034
|
const ws = new WebSocket(wsUrl);
|
|
839
|
-
|
|
1035
|
+
open.add(ws);
|
|
840
1036
|
ws.onmessage = (event) => {
|
|
1037
|
+
if (stopped) return;
|
|
841
1038
|
try {
|
|
842
1039
|
const data = JSON.parse(event.data);
|
|
843
1040
|
if (!data.type || !data.type.startsWith("job:")) {
|
|
844
1041
|
return;
|
|
845
1042
|
}
|
|
846
1043
|
const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
847
|
-
const serverPrefix =
|
|
1044
|
+
const serverPrefix = multiServer ? context.writer.wrapInColor(
|
|
848
1045
|
`[${serverName}] `,
|
|
849
1046
|
CliForegroundColor.Cyan
|
|
850
1047
|
) : "";
|
|
@@ -872,11 +1069,19 @@ var CliJobsCommandProcessor = class {
|
|
|
872
1069
|
}
|
|
873
1070
|
};
|
|
874
1071
|
ws.onerror = () => {
|
|
1072
|
+
if (stopped) return;
|
|
875
1073
|
context.writer.writeError(
|
|
876
1074
|
`WebSocket error on server ${serverName}`
|
|
877
1075
|
);
|
|
878
1076
|
};
|
|
879
1077
|
ws.onclose = () => {
|
|
1078
|
+
if (stopped || !open.delete(ws)) return;
|
|
1079
|
+
context.writer.writeWarning(
|
|
1080
|
+
`Connection to server ${serverName} closed.`
|
|
1081
|
+
);
|
|
1082
|
+
if (open.size === 0) {
|
|
1083
|
+
stop("closed");
|
|
1084
|
+
}
|
|
880
1085
|
};
|
|
881
1086
|
} catch (e) {
|
|
882
1087
|
context.writer.writeError(
|
|
@@ -884,24 +1089,23 @@ var CliJobsCommandProcessor = class {
|
|
|
884
1089
|
);
|
|
885
1090
|
}
|
|
886
1091
|
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
});
|
|
892
|
-
if (context.signal) {
|
|
893
|
-
context.signal.addEventListener("abort", () => {
|
|
894
|
-
sub.unsubscribe();
|
|
895
|
-
resolve();
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
});
|
|
1092
|
+
if (!stopped && open.size === 0) {
|
|
1093
|
+
stop("closed");
|
|
1094
|
+
}
|
|
1095
|
+
await done;
|
|
899
1096
|
context.writer.writeln("");
|
|
900
|
-
context.writer.writeInfo(
|
|
1097
|
+
context.writer.writeInfo(
|
|
1098
|
+
reason === "closed" ? "Stopped watching job events: no server connection left." : "Stopped watching job events."
|
|
1099
|
+
);
|
|
901
1100
|
}
|
|
902
1101
|
};
|
|
903
|
-
|
|
904
|
-
|
|
1102
|
+
var PAST_TENSE = {
|
|
1103
|
+
trigger: "triggered",
|
|
1104
|
+
pause: "paused",
|
|
1105
|
+
resume: "resumed",
|
|
1106
|
+
stop: "stopped",
|
|
1107
|
+
cancel: "cancelled"
|
|
1108
|
+
};
|
|
905
1109
|
var JOB_NAME_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
906
1110
|
"info",
|
|
907
1111
|
"trigger",
|
|
@@ -941,16 +1145,16 @@ var CliJobNameCompletionProvider = class {
|
|
|
941
1145
|
}
|
|
942
1146
|
const lowerPrefix = token.toLowerCase();
|
|
943
1147
|
try {
|
|
944
|
-
const manager = this.services.
|
|
1148
|
+
const manager = this.services.getRequired("cli-server-manager");
|
|
945
1149
|
if (!manager?.connections) {
|
|
946
1150
|
return [];
|
|
947
1151
|
}
|
|
948
1152
|
const names = [];
|
|
949
|
-
for (const [, connection] of manager.connections) {
|
|
1153
|
+
for (const [name, connection] of manager.connections) {
|
|
950
1154
|
if (!connection.connected) continue;
|
|
951
1155
|
const config = connection.config;
|
|
952
1156
|
const baseUrl = config.url.endsWith("/") ? config.url.slice(0, -1) : config.url;
|
|
953
|
-
const headers = config.headers
|
|
1157
|
+
const headers = resolveServerHeaders(this.services, name, config.headers);
|
|
954
1158
|
const service = new CliJobsService(baseUrl, headers);
|
|
955
1159
|
try {
|
|
956
1160
|
const jobs = await service.listJobs();
|
|
@@ -970,7 +1174,7 @@ var CliJobNameCompletionProvider = class {
|
|
|
970
1174
|
};
|
|
971
1175
|
|
|
972
1176
|
// src/lib/version.ts
|
|
973
|
-
var LIBRARY_VERSION = "2.0.0-
|
|
1177
|
+
var LIBRARY_VERSION = "2.0.0-rc.2";
|
|
974
1178
|
var API_VERSION = 2;
|
|
975
1179
|
var completionProvider = new CliJobNameCompletionProvider();
|
|
976
1180
|
var jobsModule = {
|