@qodalis/cli-server-jobs 2.0.0-beta.3
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 +34 -0
- package/public-api.d.mts +112 -0
- package/public-api.d.ts +112 -0
- package/public-api.js +1001 -0
- package/public-api.js.map +1 -0
- package/public-api.mjs +994 -0
- package/public-api.mjs.map +1 -0
- package/umd/index.global.js +3799 -0
package/public-api.mjs
ADDED
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
import { DefaultLibraryAuthor, CliIcon, CliForegroundColor, ICliCompletionProvider_TOKEN } from '@qodalis/cli-core';
|
|
2
|
+
|
|
3
|
+
// src/lib/processors/cli-jobs-command-processor.ts
|
|
4
|
+
|
|
5
|
+
// src/lib/services/cli-jobs-service.ts
|
|
6
|
+
var CliJobsService = class {
|
|
7
|
+
constructor(baseUrl, headers = {}) {
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
this.headers = headers;
|
|
10
|
+
}
|
|
11
|
+
async request(path, init) {
|
|
12
|
+
const url = `${this.baseUrl}/api/v1/qcli/jobs${path}`;
|
|
13
|
+
const response = await fetch(url, {
|
|
14
|
+
...init,
|
|
15
|
+
headers: {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
...this.headers,
|
|
18
|
+
...init?.headers ?? {}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
let errorMessage = `${response.status} ${response.statusText}`;
|
|
23
|
+
try {
|
|
24
|
+
const body = await response.json();
|
|
25
|
+
if (body.error) {
|
|
26
|
+
errorMessage = body.error;
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
throw new Error(errorMessage);
|
|
31
|
+
}
|
|
32
|
+
const text = await response.text();
|
|
33
|
+
if (!text) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
return JSON.parse(text);
|
|
37
|
+
}
|
|
38
|
+
async listJobs() {
|
|
39
|
+
return this.request("");
|
|
40
|
+
}
|
|
41
|
+
async getJob(id) {
|
|
42
|
+
return this.request(`/${encodeURIComponent(id)}`);
|
|
43
|
+
}
|
|
44
|
+
async triggerJob(id) {
|
|
45
|
+
await this.request(`/${encodeURIComponent(id)}/trigger`, {
|
|
46
|
+
method: "POST"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async pauseJob(id) {
|
|
50
|
+
await this.request(`/${encodeURIComponent(id)}/pause`, {
|
|
51
|
+
method: "POST"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async resumeJob(id) {
|
|
55
|
+
await this.request(`/${encodeURIComponent(id)}/resume`, {
|
|
56
|
+
method: "POST"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async stopJob(id) {
|
|
60
|
+
await this.request(`/${encodeURIComponent(id)}/stop`, {
|
|
61
|
+
method: "POST"
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async cancelJob(id) {
|
|
65
|
+
await this.request(`/${encodeURIComponent(id)}/cancel`, {
|
|
66
|
+
method: "POST"
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async updateJob(id, request) {
|
|
70
|
+
await this.request(`/${encodeURIComponent(id)}`, {
|
|
71
|
+
method: "PUT",
|
|
72
|
+
body: JSON.stringify(request)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async getHistory(id, opts) {
|
|
76
|
+
const params = new URLSearchParams();
|
|
77
|
+
if (opts?.limit != null) params.set("limit", String(opts.limit));
|
|
78
|
+
if (opts?.offset != null) params.set("offset", String(opts.offset));
|
|
79
|
+
if (opts?.status) params.set("status", opts.status);
|
|
80
|
+
const qs = params.toString();
|
|
81
|
+
return this.request(
|
|
82
|
+
`/${encodeURIComponent(id)}/history${qs ? `?${qs}` : ""}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
async getExecution(jobId, execId) {
|
|
86
|
+
return this.request(
|
|
87
|
+
`/${encodeURIComponent(jobId)}/history/${encodeURIComponent(execId)}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// src/lib/processors/cli-jobs-command-processor.ts
|
|
93
|
+
function getServices(context, args) {
|
|
94
|
+
const manager = context.services.get("cli-server-manager");
|
|
95
|
+
if (!manager || !manager.connections) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const targetServer = args["server"];
|
|
99
|
+
const results = [];
|
|
100
|
+
for (const [name, connection] of manager.connections) {
|
|
101
|
+
if (targetServer && name !== targetServer) continue;
|
|
102
|
+
if (!connection.connected) continue;
|
|
103
|
+
const config = connection.config;
|
|
104
|
+
const baseUrl = config.url.endsWith("/") ? config.url.slice(0, -1) : config.url;
|
|
105
|
+
const headers = config.headers ?? {};
|
|
106
|
+
results.push({ name, service: new CliJobsService(baseUrl, headers) });
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
async function resolveJobId(service, nameOrId) {
|
|
111
|
+
if (/^[0-9a-f-]{8,}$/i.test(nameOrId)) {
|
|
112
|
+
try {
|
|
113
|
+
return await service.getJob(nameOrId);
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const jobs = await service.listJobs();
|
|
118
|
+
const match = jobs.find(
|
|
119
|
+
(j) => j.name.toLowerCase() === nameOrId.toLowerCase() || j.id === nameOrId
|
|
120
|
+
);
|
|
121
|
+
return match ?? null;
|
|
122
|
+
}
|
|
123
|
+
function formatRelativeTime(isoString) {
|
|
124
|
+
if (!isoString) return "-";
|
|
125
|
+
const diff = Date.now() - new Date(isoString).getTime();
|
|
126
|
+
if (diff < 0) {
|
|
127
|
+
const absDiff = -diff;
|
|
128
|
+
if (absDiff < 6e4) return `in ${Math.round(absDiff / 1e3)}s`;
|
|
129
|
+
if (absDiff < 36e5) return `in ${Math.round(absDiff / 6e4)}m`;
|
|
130
|
+
if (absDiff < 864e5) return `in ${Math.round(absDiff / 36e5)}h`;
|
|
131
|
+
return `in ${Math.round(absDiff / 864e5)}d`;
|
|
132
|
+
}
|
|
133
|
+
if (diff < 6e4) return `${Math.round(diff / 1e3)}s ago`;
|
|
134
|
+
if (diff < 36e5) return `${Math.round(diff / 6e4)}m ago`;
|
|
135
|
+
if (diff < 864e5) return `${Math.round(diff / 36e5)}h ago`;
|
|
136
|
+
return `${Math.round(diff / 864e5)}d ago`;
|
|
137
|
+
}
|
|
138
|
+
function formatDuration(ms) {
|
|
139
|
+
if (ms == null) return "-";
|
|
140
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
141
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
142
|
+
return `${(ms / 6e4).toFixed(1)}m`;
|
|
143
|
+
}
|
|
144
|
+
function statusColor(status, writer) {
|
|
145
|
+
switch (status) {
|
|
146
|
+
case "active":
|
|
147
|
+
case "completed":
|
|
148
|
+
case "running":
|
|
149
|
+
return writer.wrapInColor(status, CliForegroundColor.Green);
|
|
150
|
+
case "paused":
|
|
151
|
+
return writer.wrapInColor(status, CliForegroundColor.Yellow);
|
|
152
|
+
case "stopped":
|
|
153
|
+
case "failed":
|
|
154
|
+
case "timed_out":
|
|
155
|
+
case "cancelled":
|
|
156
|
+
return writer.wrapInColor(status, CliForegroundColor.Red);
|
|
157
|
+
default:
|
|
158
|
+
return status;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function logLevelColor(level, writer) {
|
|
162
|
+
switch (level) {
|
|
163
|
+
case "debug":
|
|
164
|
+
return writer.wrapInColor(level, CliForegroundColor.Cyan);
|
|
165
|
+
case "info":
|
|
166
|
+
return writer.wrapInColor(level, CliForegroundColor.Green);
|
|
167
|
+
case "warning":
|
|
168
|
+
return writer.wrapInColor(level, CliForegroundColor.Yellow);
|
|
169
|
+
case "error":
|
|
170
|
+
return writer.wrapInColor(level, CliForegroundColor.Red);
|
|
171
|
+
default:
|
|
172
|
+
return level;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
var serverParam = {
|
|
176
|
+
name: "server",
|
|
177
|
+
description: "Target a specific server by name",
|
|
178
|
+
required: false,
|
|
179
|
+
type: "string"
|
|
180
|
+
};
|
|
181
|
+
var CliJobsCommandProcessor = class {
|
|
182
|
+
constructor() {
|
|
183
|
+
this.command = "server";
|
|
184
|
+
this.extendsProcessor = true;
|
|
185
|
+
this.description = "Manage background jobs on connected servers";
|
|
186
|
+
this.author = DefaultLibraryAuthor;
|
|
187
|
+
this.metadata = {
|
|
188
|
+
icon: CliIcon.Gear,
|
|
189
|
+
module: "@qodalis/cli-server-jobs"
|
|
190
|
+
};
|
|
191
|
+
this.processors = [
|
|
192
|
+
{
|
|
193
|
+
command: "jobs",
|
|
194
|
+
description: "Manage background jobs on connected servers",
|
|
195
|
+
author: DefaultLibraryAuthor,
|
|
196
|
+
metadata: {
|
|
197
|
+
icon: CliIcon.Gear,
|
|
198
|
+
module: "@qodalis/cli-server-jobs"
|
|
199
|
+
},
|
|
200
|
+
parameters: [serverParam],
|
|
201
|
+
processors: [
|
|
202
|
+
// list
|
|
203
|
+
{
|
|
204
|
+
command: "list",
|
|
205
|
+
description: "List all jobs across connected servers",
|
|
206
|
+
aliases: ["ls"],
|
|
207
|
+
parameters: [
|
|
208
|
+
serverParam,
|
|
209
|
+
{
|
|
210
|
+
name: "group",
|
|
211
|
+
description: "Filter jobs by group",
|
|
212
|
+
required: false,
|
|
213
|
+
type: "string"
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
processCommand: async (cmd, context) => {
|
|
217
|
+
await this.handleList(cmd, context);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
// info
|
|
221
|
+
{
|
|
222
|
+
command: "info",
|
|
223
|
+
description: "Show detailed information about a job",
|
|
224
|
+
valueRequired: true,
|
|
225
|
+
parameters: [serverParam],
|
|
226
|
+
processCommand: async (cmd, context) => {
|
|
227
|
+
await this.handleInfo(cmd, context);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
// trigger
|
|
231
|
+
{
|
|
232
|
+
command: "trigger",
|
|
233
|
+
description: "Trigger immediate execution of a job",
|
|
234
|
+
valueRequired: true,
|
|
235
|
+
parameters: [serverParam],
|
|
236
|
+
processCommand: async (cmd, context) => {
|
|
237
|
+
await this.handleAction(cmd, context, "trigger");
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
// pause
|
|
241
|
+
{
|
|
242
|
+
command: "pause",
|
|
243
|
+
description: "Pause scheduled execution of a job",
|
|
244
|
+
valueRequired: true,
|
|
245
|
+
parameters: [serverParam],
|
|
246
|
+
processCommand: async (cmd, context) => {
|
|
247
|
+
await this.handleAction(cmd, context, "pause");
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
// resume
|
|
251
|
+
{
|
|
252
|
+
command: "resume",
|
|
253
|
+
description: "Resume a paused job",
|
|
254
|
+
valueRequired: true,
|
|
255
|
+
parameters: [serverParam],
|
|
256
|
+
processCommand: async (cmd, context) => {
|
|
257
|
+
await this.handleAction(cmd, context, "resume");
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
// stop
|
|
261
|
+
{
|
|
262
|
+
command: "stop",
|
|
263
|
+
description: "Stop a job and cancel current execution if running",
|
|
264
|
+
valueRequired: true,
|
|
265
|
+
parameters: [serverParam],
|
|
266
|
+
processCommand: async (cmd, context) => {
|
|
267
|
+
await this.handleAction(cmd, context, "stop");
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
// cancel
|
|
271
|
+
{
|
|
272
|
+
command: "cancel",
|
|
273
|
+
description: "Cancel the current execution of a job",
|
|
274
|
+
valueRequired: true,
|
|
275
|
+
parameters: [serverParam],
|
|
276
|
+
processCommand: async (cmd, context) => {
|
|
277
|
+
await this.handleAction(cmd, context, "cancel");
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
// history
|
|
281
|
+
{
|
|
282
|
+
command: "history",
|
|
283
|
+
description: "Show execution history for a job",
|
|
284
|
+
valueRequired: true,
|
|
285
|
+
parameters: [
|
|
286
|
+
serverParam,
|
|
287
|
+
{
|
|
288
|
+
name: "limit",
|
|
289
|
+
description: "Number of entries to show (default 20)",
|
|
290
|
+
required: false,
|
|
291
|
+
type: "number",
|
|
292
|
+
defaultValue: 20
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: "offset",
|
|
296
|
+
description: "Offset for pagination",
|
|
297
|
+
required: false,
|
|
298
|
+
type: "number",
|
|
299
|
+
defaultValue: 0
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "status",
|
|
303
|
+
description: "Filter by execution status",
|
|
304
|
+
required: false,
|
|
305
|
+
type: "string"
|
|
306
|
+
}
|
|
307
|
+
],
|
|
308
|
+
processCommand: async (cmd, context) => {
|
|
309
|
+
await this.handleHistory(cmd, context);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
// logs
|
|
313
|
+
{
|
|
314
|
+
command: "logs",
|
|
315
|
+
description: "Show logs from the latest or a specific execution",
|
|
316
|
+
valueRequired: true,
|
|
317
|
+
parameters: [
|
|
318
|
+
serverParam,
|
|
319
|
+
{
|
|
320
|
+
name: "exec",
|
|
321
|
+
description: "Specific execution ID to show logs for",
|
|
322
|
+
required: false,
|
|
323
|
+
type: "string"
|
|
324
|
+
}
|
|
325
|
+
],
|
|
326
|
+
processCommand: async (cmd, context) => {
|
|
327
|
+
await this.handleLogs(cmd, context);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
// edit
|
|
331
|
+
{
|
|
332
|
+
command: "edit",
|
|
333
|
+
description: "Interactively edit job settings",
|
|
334
|
+
valueRequired: true,
|
|
335
|
+
parameters: [serverParam],
|
|
336
|
+
processCommand: async (cmd, context) => {
|
|
337
|
+
await this.handleEdit(cmd, context);
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
// watch
|
|
341
|
+
{
|
|
342
|
+
command: "watch",
|
|
343
|
+
description: "Live view of job events via WebSocket",
|
|
344
|
+
parameters: [serverParam],
|
|
345
|
+
processCommand: async (cmd, context) => {
|
|
346
|
+
await this.handleWatch(cmd, context);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
processCommand: async (cmd, context) => {
|
|
351
|
+
await this.handleList(cmd, context);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
async processCommand(_cmd, _context) {
|
|
357
|
+
}
|
|
358
|
+
// ── List ──────────────────────────────────────────────────────────
|
|
359
|
+
async handleList(cmd, context) {
|
|
360
|
+
const services = getServices(context, cmd.args);
|
|
361
|
+
if (services.length === 0) {
|
|
362
|
+
context.writer.writeInfo(
|
|
363
|
+
'No connected servers. Use "server list" to check connections.'
|
|
364
|
+
);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const groupFilter = cmd.args["group"];
|
|
368
|
+
const multiServer = services.length > 1;
|
|
369
|
+
for (const { name, service } of services) {
|
|
370
|
+
try {
|
|
371
|
+
let jobs = await service.listJobs();
|
|
372
|
+
if (groupFilter) {
|
|
373
|
+
jobs = jobs.filter(
|
|
374
|
+
(j) => j.group?.toLowerCase() === groupFilter.toLowerCase()
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
if (multiServer) {
|
|
378
|
+
context.writer.writeln(
|
|
379
|
+
context.writer.wrapInColor(
|
|
380
|
+
`Server: ${name}`,
|
|
381
|
+
CliForegroundColor.Cyan
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (jobs.length === 0) {
|
|
386
|
+
context.writer.writeInfo(" No jobs found.");
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const headers = [
|
|
390
|
+
"Name",
|
|
391
|
+
"Group",
|
|
392
|
+
"Status",
|
|
393
|
+
"Schedule",
|
|
394
|
+
"Last Run",
|
|
395
|
+
"Next Run",
|
|
396
|
+
"Duration"
|
|
397
|
+
];
|
|
398
|
+
const rows = jobs.map((j) => [
|
|
399
|
+
j.name,
|
|
400
|
+
j.group ?? "-",
|
|
401
|
+
statusColor(j.status, context.writer),
|
|
402
|
+
j.schedule ?? (j.interval ? `every ${j.interval}ms` : "-"),
|
|
403
|
+
formatRelativeTime(j.lastRunAt),
|
|
404
|
+
formatRelativeTime(j.nextRunAt),
|
|
405
|
+
formatDuration(j.lastRunDuration)
|
|
406
|
+
]);
|
|
407
|
+
context.writer.writeTable(headers, rows);
|
|
408
|
+
} catch (e) {
|
|
409
|
+
context.writer.writeError(
|
|
410
|
+
`Failed to list jobs on ${name}: ${e.message}`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// ── Info ──────────────────────────────────────────────────────────
|
|
416
|
+
async handleInfo(cmd, context) {
|
|
417
|
+
const nameOrId = (cmd.value ?? "").trim();
|
|
418
|
+
if (!nameOrId) {
|
|
419
|
+
context.writer.writeError("Usage: jobs info <id|name>");
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const services = getServices(context, cmd.args);
|
|
423
|
+
if (services.length === 0) {
|
|
424
|
+
context.writer.writeInfo("No connected servers.");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
for (const { name, service } of services) {
|
|
428
|
+
try {
|
|
429
|
+
const job = await resolveJobId(service, nameOrId);
|
|
430
|
+
if (!job) {
|
|
431
|
+
if (services.length === 1) {
|
|
432
|
+
context.writer.writeError(
|
|
433
|
+
`Job "${nameOrId}" not found on server ${name}.`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (services.length > 1) {
|
|
439
|
+
context.writer.writeln(
|
|
440
|
+
context.writer.wrapInColor(
|
|
441
|
+
`Server: ${name}`,
|
|
442
|
+
CliForegroundColor.Cyan
|
|
443
|
+
)
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
const lines = [
|
|
447
|
+
["ID", job.id],
|
|
448
|
+
["Name", job.name],
|
|
449
|
+
["Description", job.description],
|
|
450
|
+
["Group", job.group ?? "-"],
|
|
451
|
+
["Status", statusColor(job.status, context.writer)],
|
|
452
|
+
[
|
|
453
|
+
"Schedule",
|
|
454
|
+
job.schedule ?? (job.interval ? `every ${job.interval}ms` : "-")
|
|
455
|
+
],
|
|
456
|
+
["Max Retries", String(job.maxRetries)],
|
|
457
|
+
[
|
|
458
|
+
"Timeout",
|
|
459
|
+
job.timeout ? `${job.timeout}ms` : "none"
|
|
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}`
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// ── Action (trigger/pause/resume/stop/cancel) ────────────────────
|
|
491
|
+
async handleAction(cmd, context, action) {
|
|
492
|
+
const nameOrId = (cmd.value ?? "").trim();
|
|
493
|
+
if (!nameOrId) {
|
|
494
|
+
context.writer.writeError(`Usage: jobs ${action} <id|name>`);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const services = getServices(context, cmd.args);
|
|
498
|
+
if (services.length === 0) {
|
|
499
|
+
context.writer.writeInfo("No connected servers.");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
for (const { name, service } of services) {
|
|
503
|
+
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
|
+
switch (action) {
|
|
514
|
+
case "trigger":
|
|
515
|
+
await service.triggerJob(job.id);
|
|
516
|
+
break;
|
|
517
|
+
case "pause":
|
|
518
|
+
await service.pauseJob(job.id);
|
|
519
|
+
break;
|
|
520
|
+
case "resume":
|
|
521
|
+
await service.resumeJob(job.id);
|
|
522
|
+
break;
|
|
523
|
+
case "stop":
|
|
524
|
+
await service.stopJob(job.id);
|
|
525
|
+
break;
|
|
526
|
+
case "cancel":
|
|
527
|
+
await service.cancelJob(job.id);
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
const serverSuffix = services.length > 1 ? ` on server ${name}` : "";
|
|
531
|
+
context.writer.writeSuccess(
|
|
532
|
+
`Job "${job.name}" ${action}${action.endsWith("e") ? "d" : "ed"}${serverSuffix}.`
|
|
533
|
+
);
|
|
534
|
+
return;
|
|
535
|
+
} catch (e) {
|
|
536
|
+
context.writer.writeError(
|
|
537
|
+
`Failed to ${action} job on ${name}: ${e.message}`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// ── History ──────────────────────────────────────────────────────
|
|
543
|
+
async handleHistory(cmd, context) {
|
|
544
|
+
const nameOrId = (cmd.value ?? "").trim();
|
|
545
|
+
if (!nameOrId) {
|
|
546
|
+
context.writer.writeError("Usage: jobs history <id|name>");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const services = getServices(context, cmd.args);
|
|
550
|
+
if (services.length === 0) {
|
|
551
|
+
context.writer.writeInfo("No connected servers.");
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const limit = cmd.args["limit"] ?? 20;
|
|
555
|
+
const offset = cmd.args["offset"] ?? 0;
|
|
556
|
+
const status = cmd.args["status"];
|
|
557
|
+
for (const { name, service } of services) {
|
|
558
|
+
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
|
+
if (services.length > 1) {
|
|
569
|
+
context.writer.writeln(
|
|
570
|
+
context.writer.wrapInColor(
|
|
571
|
+
`Server: ${name}`,
|
|
572
|
+
CliForegroundColor.Cyan
|
|
573
|
+
)
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const history = await service.getHistory(job.id, {
|
|
577
|
+
limit,
|
|
578
|
+
offset,
|
|
579
|
+
status
|
|
580
|
+
});
|
|
581
|
+
if (history.items.length === 0) {
|
|
582
|
+
context.writer.writeInfo(
|
|
583
|
+
`No execution history for "${job.name}".`
|
|
584
|
+
);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
context.writer.writeln(
|
|
588
|
+
`Showing ${history.items.length} of ${history.total} executions for "${job.name}":`
|
|
589
|
+
);
|
|
590
|
+
const headers = [
|
|
591
|
+
"ID",
|
|
592
|
+
"Status",
|
|
593
|
+
"Started",
|
|
594
|
+
"Duration",
|
|
595
|
+
"Retry",
|
|
596
|
+
"Error"
|
|
597
|
+
];
|
|
598
|
+
const rows = history.items.map((exec) => [
|
|
599
|
+
exec.id.substring(0, 8),
|
|
600
|
+
statusColor(exec.status, context.writer),
|
|
601
|
+
formatRelativeTime(exec.startedAt),
|
|
602
|
+
formatDuration(exec.duration),
|
|
603
|
+
String(exec.retryAttempt),
|
|
604
|
+
exec.error ? exec.error.substring(0, 40) : "-"
|
|
605
|
+
]);
|
|
606
|
+
context.writer.writeTable(headers, rows);
|
|
607
|
+
return;
|
|
608
|
+
} catch (e) {
|
|
609
|
+
context.writer.writeError(
|
|
610
|
+
`Error on server ${name}: ${e.message}`
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// ── Logs ─────────────────────────────────────────────────────────
|
|
616
|
+
async handleLogs(cmd, context) {
|
|
617
|
+
const nameOrId = (cmd.value ?? "").trim();
|
|
618
|
+
if (!nameOrId) {
|
|
619
|
+
context.writer.writeError("Usage: jobs logs <id|name>");
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const services = getServices(context, cmd.args);
|
|
623
|
+
if (services.length === 0) {
|
|
624
|
+
context.writer.writeInfo("No connected servers.");
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const execId = cmd.args["exec"];
|
|
628
|
+
for (const { name, service } of services) {
|
|
629
|
+
try {
|
|
630
|
+
const job = await resolveJobId(service, nameOrId);
|
|
631
|
+
if (!job) {
|
|
632
|
+
if (services.length === 1) {
|
|
633
|
+
context.writer.writeError(
|
|
634
|
+
`Job "${nameOrId}" not found on server ${name}.`
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
let execution;
|
|
640
|
+
if (execId) {
|
|
641
|
+
execution = await service.getExecution(job.id, execId);
|
|
642
|
+
} else {
|
|
643
|
+
const history = await service.getHistory(job.id, {
|
|
644
|
+
limit: 1
|
|
645
|
+
});
|
|
646
|
+
if (history.items.length === 0) {
|
|
647
|
+
context.writer.writeInfo(
|
|
648
|
+
`No executions found for "${job.name}".`
|
|
649
|
+
);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
execution = await service.getExecution(
|
|
653
|
+
job.id,
|
|
654
|
+
history.items[0].id
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
if (services.length > 1) {
|
|
658
|
+
context.writer.writeln(
|
|
659
|
+
context.writer.wrapInColor(
|
|
660
|
+
`Server: ${name}`,
|
|
661
|
+
CliForegroundColor.Cyan
|
|
662
|
+
)
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
context.writer.writeln(
|
|
666
|
+
`Logs for "${job.name}" execution ${execution.id.substring(0, 8)} (${statusColor(execution.status, context.writer)}):`
|
|
667
|
+
);
|
|
668
|
+
if (!execution.logs || execution.logs.length === 0) {
|
|
669
|
+
context.writer.writeInfo(" No log entries.");
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
for (const entry of execution.logs) {
|
|
673
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
674
|
+
const level = logLevelColor(
|
|
675
|
+
entry.level,
|
|
676
|
+
context.writer
|
|
677
|
+
).padEnd(12);
|
|
678
|
+
context.writer.writeln(
|
|
679
|
+
` ${context.writer.wrapInColor(ts, CliForegroundColor.White)} ${level} ${entry.message}`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
return;
|
|
683
|
+
} catch (e) {
|
|
684
|
+
context.writer.writeError(
|
|
685
|
+
`Error on server ${name}: ${e.message}`
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
// ── Edit ─────────────────────────────────────────────────────────
|
|
691
|
+
async handleEdit(cmd, context) {
|
|
692
|
+
const nameOrId = (cmd.value ?? "").trim();
|
|
693
|
+
if (!nameOrId) {
|
|
694
|
+
context.writer.writeError("Usage: jobs edit <id|name>");
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const services = getServices(context, cmd.args);
|
|
698
|
+
if (services.length === 0) {
|
|
699
|
+
context.writer.writeInfo("No connected servers.");
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
for (const { name, service } of services) {
|
|
703
|
+
try {
|
|
704
|
+
const job = await resolveJobId(service, nameOrId);
|
|
705
|
+
if (!job) {
|
|
706
|
+
if (services.length === 1) {
|
|
707
|
+
context.writer.writeError(
|
|
708
|
+
`Job "${nameOrId}" not found on server ${name}.`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
context.writer.writeln(
|
|
714
|
+
`Editing job "${context.writer.wrapInColor(job.name, CliForegroundColor.Cyan)}" on server ${name}`
|
|
715
|
+
);
|
|
716
|
+
context.writer.writeln(
|
|
717
|
+
"Press Enter to keep current value, or type a new value."
|
|
718
|
+
);
|
|
719
|
+
context.writer.writeln("");
|
|
720
|
+
const update = {};
|
|
721
|
+
const newDesc = await context.reader.readLine(
|
|
722
|
+
`Description [${job.description}]: `
|
|
723
|
+
);
|
|
724
|
+
if (newDesc == null) {
|
|
725
|
+
context.writer.writeInfo("Edit cancelled.");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (newDesc.trim()) {
|
|
729
|
+
update.description = newDesc.trim();
|
|
730
|
+
}
|
|
731
|
+
const newGroup = await context.reader.readLine(
|
|
732
|
+
`Group [${job.group ?? "none"}]: `
|
|
733
|
+
);
|
|
734
|
+
if (newGroup == null) {
|
|
735
|
+
context.writer.writeInfo("Edit cancelled.");
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (newGroup.trim()) {
|
|
739
|
+
update.group = newGroup.trim();
|
|
740
|
+
}
|
|
741
|
+
const currentSchedule = job.schedule ?? (job.interval ? `interval: ${job.interval}ms` : "none");
|
|
742
|
+
const newSchedule = await context.reader.readLine(
|
|
743
|
+
`Cron schedule [${currentSchedule}]: `
|
|
744
|
+
);
|
|
745
|
+
if (newSchedule == null) {
|
|
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?"
|
|
789
|
+
);
|
|
790
|
+
if (!confirm) {
|
|
791
|
+
context.writer.writeInfo("Edit cancelled.");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
await service.updateJob(job.id, update);
|
|
795
|
+
context.writer.writeSuccess(
|
|
796
|
+
`Job "${job.name}" updated successfully.`
|
|
797
|
+
);
|
|
798
|
+
return;
|
|
799
|
+
} catch (e) {
|
|
800
|
+
context.writer.writeError(
|
|
801
|
+
`Error on server ${name}: ${e.message}`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
// ── Watch ────────────────────────────────────────────────────────
|
|
807
|
+
async handleWatch(cmd, context) {
|
|
808
|
+
const manager = context.services.get("cli-server-manager");
|
|
809
|
+
if (!manager || !manager.connections) {
|
|
810
|
+
context.writer.writeInfo("No connected servers.");
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
const targetServer = cmd.args["server"];
|
|
814
|
+
context.writer.writeln(
|
|
815
|
+
context.writer.wrapInColor(
|
|
816
|
+
"Watching job events... (press Ctrl+C to stop)",
|
|
817
|
+
CliForegroundColor.Cyan
|
|
818
|
+
)
|
|
819
|
+
);
|
|
820
|
+
context.writer.writeln("");
|
|
821
|
+
const sockets = [];
|
|
822
|
+
const abortHandler = () => {
|
|
823
|
+
for (const ws of sockets) {
|
|
824
|
+
try {
|
|
825
|
+
ws.close();
|
|
826
|
+
} catch {
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
context.onAbort.subscribe(abortHandler);
|
|
831
|
+
for (const [serverName, connection] of manager.connections) {
|
|
832
|
+
if (targetServer && serverName !== targetServer) continue;
|
|
833
|
+
if (!connection.connected) continue;
|
|
834
|
+
const config = connection.config;
|
|
835
|
+
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
|
+
try {
|
|
838
|
+
const ws = new WebSocket(wsUrl);
|
|
839
|
+
sockets.push(ws);
|
|
840
|
+
ws.onmessage = (event) => {
|
|
841
|
+
try {
|
|
842
|
+
const data = JSON.parse(event.data);
|
|
843
|
+
if (!data.type || !data.type.startsWith("job:")) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
847
|
+
const serverPrefix = manager.connections.size > 1 ? context.writer.wrapInColor(
|
|
848
|
+
`[${serverName}] `,
|
|
849
|
+
CliForegroundColor.Cyan
|
|
850
|
+
) : "";
|
|
851
|
+
let detail = "";
|
|
852
|
+
if (data.jobId) {
|
|
853
|
+
detail += ` jobId=${data.jobId}`;
|
|
854
|
+
}
|
|
855
|
+
if (data.executionId) {
|
|
856
|
+
detail += ` execId=${data.executionId.substring(0, 8)}`;
|
|
857
|
+
}
|
|
858
|
+
if (data.duration != null) {
|
|
859
|
+
detail += ` duration=${formatDuration(data.duration)}`;
|
|
860
|
+
}
|
|
861
|
+
if (data.error) {
|
|
862
|
+
detail += ` error="${data.error}"`;
|
|
863
|
+
}
|
|
864
|
+
const eventType = statusColor(
|
|
865
|
+
data.type.replace("job:", ""),
|
|
866
|
+
context.writer
|
|
867
|
+
);
|
|
868
|
+
context.writer.writeln(
|
|
869
|
+
`${context.writer.wrapInColor(ts, CliForegroundColor.White)} ${serverPrefix}${eventType}${detail}`
|
|
870
|
+
);
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
ws.onerror = () => {
|
|
875
|
+
context.writer.writeError(
|
|
876
|
+
`WebSocket error on server ${serverName}`
|
|
877
|
+
);
|
|
878
|
+
};
|
|
879
|
+
ws.onclose = () => {
|
|
880
|
+
};
|
|
881
|
+
} catch (e) {
|
|
882
|
+
context.writer.writeError(
|
|
883
|
+
`Failed to connect to ${serverName}: ${e.message}`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
await new Promise((resolve) => {
|
|
888
|
+
const sub = context.onAbort.subscribe(() => {
|
|
889
|
+
sub.unsubscribe();
|
|
890
|
+
resolve();
|
|
891
|
+
});
|
|
892
|
+
if (context.signal) {
|
|
893
|
+
context.signal.addEventListener("abort", () => {
|
|
894
|
+
sub.unsubscribe();
|
|
895
|
+
resolve();
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
context.writer.writeln("");
|
|
900
|
+
context.writer.writeInfo("Stopped watching job events.");
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
// src/lib/completion/cli-job-name-completion-provider.ts
|
|
905
|
+
var JOB_NAME_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
906
|
+
"info",
|
|
907
|
+
"trigger",
|
|
908
|
+
"pause",
|
|
909
|
+
"resume",
|
|
910
|
+
"stop",
|
|
911
|
+
"cancel",
|
|
912
|
+
"history",
|
|
913
|
+
"logs",
|
|
914
|
+
"edit"
|
|
915
|
+
]);
|
|
916
|
+
var CliJobNameCompletionProvider = class {
|
|
917
|
+
constructor() {
|
|
918
|
+
this.priority = 50;
|
|
919
|
+
this.services = null;
|
|
920
|
+
}
|
|
921
|
+
setServices(services) {
|
|
922
|
+
this.services = services;
|
|
923
|
+
}
|
|
924
|
+
async getCompletions(context) {
|
|
925
|
+
const { tokens, tokenIndex, token } = context;
|
|
926
|
+
if (tokenIndex !== 3 || tokens.length < 3) {
|
|
927
|
+
return [];
|
|
928
|
+
}
|
|
929
|
+
if (tokens[0].toLowerCase() !== "server") {
|
|
930
|
+
return [];
|
|
931
|
+
}
|
|
932
|
+
if (tokens[1].toLowerCase() !== "jobs") {
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
const subCommand = tokens[2].toLowerCase();
|
|
936
|
+
if (!JOB_NAME_SUBCOMMANDS.has(subCommand)) {
|
|
937
|
+
return [];
|
|
938
|
+
}
|
|
939
|
+
if (!this.services) {
|
|
940
|
+
return [];
|
|
941
|
+
}
|
|
942
|
+
const lowerPrefix = token.toLowerCase();
|
|
943
|
+
try {
|
|
944
|
+
const manager = this.services.get("cli-server-manager");
|
|
945
|
+
if (!manager?.connections) {
|
|
946
|
+
return [];
|
|
947
|
+
}
|
|
948
|
+
const names = [];
|
|
949
|
+
for (const [, connection] of manager.connections) {
|
|
950
|
+
if (!connection.connected) continue;
|
|
951
|
+
const config = connection.config;
|
|
952
|
+
const baseUrl = config.url.endsWith("/") ? config.url.slice(0, -1) : config.url;
|
|
953
|
+
const headers = config.headers ?? {};
|
|
954
|
+
const service = new CliJobsService(baseUrl, headers);
|
|
955
|
+
try {
|
|
956
|
+
const jobs = await service.listJobs();
|
|
957
|
+
for (const job of jobs) {
|
|
958
|
+
if (!names.includes(job.name)) {
|
|
959
|
+
names.push(job.name);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
} catch {
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return names.filter((name) => name.toLowerCase().startsWith(lowerPrefix)).sort();
|
|
966
|
+
} catch {
|
|
967
|
+
return [];
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
// src/lib/version.ts
|
|
973
|
+
var LIBRARY_VERSION = "2.0.0-beta.3";
|
|
974
|
+
var API_VERSION = 2;
|
|
975
|
+
var completionProvider = new CliJobNameCompletionProvider();
|
|
976
|
+
var jobsModule = {
|
|
977
|
+
apiVersion: API_VERSION,
|
|
978
|
+
name: "@qodalis/cli-server-jobs",
|
|
979
|
+
processors: [new CliJobsCommandProcessor()],
|
|
980
|
+
services: [
|
|
981
|
+
{
|
|
982
|
+
provide: ICliCompletionProvider_TOKEN,
|
|
983
|
+
useValue: completionProvider,
|
|
984
|
+
multi: true
|
|
985
|
+
}
|
|
986
|
+
],
|
|
987
|
+
async onInit(context) {
|
|
988
|
+
completionProvider.setServices(context.services);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
export { API_VERSION, CliJobNameCompletionProvider, CliJobsCommandProcessor, CliJobsService, LIBRARY_VERSION, jobsModule };
|
|
993
|
+
//# sourceMappingURL=public-api.mjs.map
|
|
994
|
+
//# sourceMappingURL=public-api.mjs.map
|