@qodalis/cli-server-jobs 2.2.0 → 2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qodalis/cli-server-jobs",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "Background job management",
5
5
  "author": "Qodalis Solutions",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "umd": "./umd/index.global.js",
19
19
  "unpkg": "./umd/index.global.js",
20
20
  "dependencies": {
21
- "@qodalis/cli-core": "2.2.0"
21
+ "@qodalis/cli-core": "2.4.0"
22
22
  },
23
23
  "sideEffects": false,
24
24
  "main": "./public-api.js",
package/public-api.d.mts CHANGED
@@ -1,52 +1,5 @@
1
1
  import { ICliCommandProcessor, ICliCommandAuthor, CliProcessorMetadata, CliProcessCommand, ICliExecutionContext, ICliCompletionProvider, ICliServiceProvider, ICliCompletionContext, ICliModule } from '@qodalis/cli-core';
2
2
 
3
- declare class CliJobsCommandProcessor implements ICliCommandProcessor {
4
- command: string;
5
- extendsProcessor: boolean;
6
- description: string;
7
- author: ICliCommandAuthor;
8
- metadata: CliProcessorMetadata;
9
- processors?: ICliCommandProcessor[];
10
- processCommand(_cmd: CliProcessCommand, _context: ICliExecutionContext): Promise<void>;
11
- private handleList;
12
- private handleInfo;
13
- /**
14
- * Without --server the action applies to every connected server that has
15
- * the job, each reported on its own line. When several servers have it,
16
- * a note says so (and how to target one).
17
- */
18
- private handleAction;
19
- private handleHistory;
20
- private handleLogs;
21
- /**
22
- * Prompt for each setting; Enter keeps the current value. Ctrl+C or
23
- * Escape at any prompt (the reader resolves null) cancels the whole edit
24
- * and nothing is sent. A job on several servers needs --server.
25
- */
26
- private handleEdit;
27
- /**
28
- * Stream job events until Ctrl+C (context.onAbort / context.signal) or
29
- * until every watched server has closed its connection. On exit the
30
- * sockets are closed and every subscription and listener is removed.
31
- */
32
- private handleWatch;
33
- }
34
-
35
- /**
36
- * Provides tab-completion for job names when typing
37
- * `server jobs <subcommand> <name>`.
38
- *
39
- * Priority 50 — checked before command and parameter completion so that
40
- * job names are suggested instead of treating the value position as
41
- * a sub-command or flag.
42
- */
43
- declare class CliJobNameCompletionProvider implements ICliCompletionProvider {
44
- priority: number;
45
- private services;
46
- setServices(services: ICliServiceProvider): void;
47
- getCompletions(context: ICliCompletionContext): Promise<string[]>;
48
- }
49
-
50
3
  interface JobDto {
51
4
  id: string;
52
5
  name: string;
@@ -54,11 +7,20 @@ interface JobDto {
54
7
  group?: string;
55
8
  status: 'active' | 'paused' | 'stopped';
56
9
  schedule?: string;
57
- interval?: number;
10
+ /** Milliseconds (.NET, Node) or a span such as `30s` (Python). */
11
+ interval?: number | string;
58
12
  maxRetries: number;
59
- timeout?: number;
13
+ /** Milliseconds (.NET, Node) or a span such as `5m` (Python). */
14
+ timeout?: number | string;
60
15
  overlapPolicy: string;
16
+ /** Runs of the job that may go at the same time (servers 1.0.0-beta.23; 1 before). */
17
+ maxConcurrentRuns?: number;
18
+ /** The newest running run. */
61
19
  currentExecutionId?: string;
20
+ /** Every running run, oldest first (servers 1.0.0-beta.23). */
21
+ runningExecutionIds?: string[];
22
+ /** A run waits for a free slot (overlap policy `queue`). */
23
+ queued?: boolean;
62
24
  nextRunAt?: string;
63
25
  lastRunAt?: string;
64
26
  lastRunStatus?: string;
@@ -96,6 +58,61 @@ interface UpdateJobRequest {
96
58
  maxRetries?: number;
97
59
  timeout?: string;
98
60
  overlapPolicy?: string;
61
+ maxConcurrentRuns?: number;
62
+ }
63
+
64
+ /**
65
+ * An interval or timeout as servers send it: milliseconds (.NET, Node) or a
66
+ * span such as `30s` (Python), shown as `30s`, `5m`, `1h`.
67
+ */
68
+ declare function formatSpan(value?: number | string | null): string | null;
69
+ /** Runs going now, when more than one may run: `2/3 running`, `1/3 running, 1 queued`. */
70
+ declare function formatRunning(job: JobDto): string | null;
71
+ declare class CliJobsCommandProcessor implements ICliCommandProcessor {
72
+ command: string;
73
+ extendsProcessor: boolean;
74
+ description: string;
75
+ author: ICliCommandAuthor;
76
+ metadata: CliProcessorMetadata;
77
+ processors?: ICliCommandProcessor[];
78
+ processCommand(_cmd: CliProcessCommand, _context: ICliExecutionContext): Promise<void>;
79
+ private handleList;
80
+ private handleInfo;
81
+ /**
82
+ * Without --server the action applies to every connected server that has
83
+ * the job, each reported on its own line. When several servers have it,
84
+ * a note says so (and how to target one).
85
+ */
86
+ private handleAction;
87
+ private handleHistory;
88
+ private handleLogs;
89
+ /**
90
+ * Prompt for each setting; Enter keeps the current value. Ctrl+C or
91
+ * Escape at any prompt (the reader resolves null) cancels the whole edit
92
+ * and nothing is sent. A job on several servers needs --server.
93
+ */
94
+ private handleEdit;
95
+ /**
96
+ * Stream job events until Ctrl+C (context.onAbort / context.signal) or
97
+ * until every watched server has closed its connection. On exit the
98
+ * sockets are closed and every subscription and listener is removed.
99
+ */
100
+ private handleWatch;
101
+ }
102
+
103
+ /**
104
+ * Provides tab-completion for job names when typing
105
+ * `server jobs <subcommand> <name>`.
106
+ *
107
+ * Priority 50 — checked before command and parameter completion so that
108
+ * job names are suggested instead of treating the value position as
109
+ * a sub-command or flag.
110
+ */
111
+ declare class CliJobNameCompletionProvider implements ICliCompletionProvider {
112
+ priority: number;
113
+ private services;
114
+ setServices(services: ICliServiceProvider): void;
115
+ getCompletions(context: ICliCompletionContext): Promise<string[]>;
99
116
  }
100
117
 
101
118
  declare class CliJobsService {
@@ -119,10 +136,10 @@ declare class CliJobsService {
119
136
  getExecution(jobId: string, execId: string): Promise<JobExecutionDto>;
120
137
  }
121
138
 
122
- declare const LIBRARY_VERSION = "2.2.0";
139
+ declare const LIBRARY_VERSION = "2.4.0";
123
140
  declare const API_VERSION = 2;
124
141
 
125
142
  declare const jobsModule: ICliModule;
126
143
 
127
- export { API_VERSION, CliJobNameCompletionProvider, CliJobsCommandProcessor, CliJobsService, LIBRARY_VERSION, jobsModule };
144
+ export { API_VERSION, CliJobNameCompletionProvider, CliJobsCommandProcessor, CliJobsService, LIBRARY_VERSION, formatRunning, formatSpan, jobsModule };
128
145
  export type { JobDto, JobExecutionDto, JobHistoryResponse, JobLogEntryDto, UpdateJobRequest };
package/public-api.d.ts CHANGED
@@ -1,52 +1,5 @@
1
1
  import { ICliCommandProcessor, ICliCommandAuthor, CliProcessorMetadata, CliProcessCommand, ICliExecutionContext, ICliCompletionProvider, ICliServiceProvider, ICliCompletionContext, ICliModule } from '@qodalis/cli-core';
2
2
 
3
- declare class CliJobsCommandProcessor implements ICliCommandProcessor {
4
- command: string;
5
- extendsProcessor: boolean;
6
- description: string;
7
- author: ICliCommandAuthor;
8
- metadata: CliProcessorMetadata;
9
- processors?: ICliCommandProcessor[];
10
- processCommand(_cmd: CliProcessCommand, _context: ICliExecutionContext): Promise<void>;
11
- private handleList;
12
- private handleInfo;
13
- /**
14
- * Without --server the action applies to every connected server that has
15
- * the job, each reported on its own line. When several servers have it,
16
- * a note says so (and how to target one).
17
- */
18
- private handleAction;
19
- private handleHistory;
20
- private handleLogs;
21
- /**
22
- * Prompt for each setting; Enter keeps the current value. Ctrl+C or
23
- * Escape at any prompt (the reader resolves null) cancels the whole edit
24
- * and nothing is sent. A job on several servers needs --server.
25
- */
26
- private handleEdit;
27
- /**
28
- * Stream job events until Ctrl+C (context.onAbort / context.signal) or
29
- * until every watched server has closed its connection. On exit the
30
- * sockets are closed and every subscription and listener is removed.
31
- */
32
- private handleWatch;
33
- }
34
-
35
- /**
36
- * Provides tab-completion for job names when typing
37
- * `server jobs <subcommand> <name>`.
38
- *
39
- * Priority 50 — checked before command and parameter completion so that
40
- * job names are suggested instead of treating the value position as
41
- * a sub-command or flag.
42
- */
43
- declare class CliJobNameCompletionProvider implements ICliCompletionProvider {
44
- priority: number;
45
- private services;
46
- setServices(services: ICliServiceProvider): void;
47
- getCompletions(context: ICliCompletionContext): Promise<string[]>;
48
- }
49
-
50
3
  interface JobDto {
51
4
  id: string;
52
5
  name: string;
@@ -54,11 +7,20 @@ interface JobDto {
54
7
  group?: string;
55
8
  status: 'active' | 'paused' | 'stopped';
56
9
  schedule?: string;
57
- interval?: number;
10
+ /** Milliseconds (.NET, Node) or a span such as `30s` (Python). */
11
+ interval?: number | string;
58
12
  maxRetries: number;
59
- timeout?: number;
13
+ /** Milliseconds (.NET, Node) or a span such as `5m` (Python). */
14
+ timeout?: number | string;
60
15
  overlapPolicy: string;
16
+ /** Runs of the job that may go at the same time (servers 1.0.0-beta.23; 1 before). */
17
+ maxConcurrentRuns?: number;
18
+ /** The newest running run. */
61
19
  currentExecutionId?: string;
20
+ /** Every running run, oldest first (servers 1.0.0-beta.23). */
21
+ runningExecutionIds?: string[];
22
+ /** A run waits for a free slot (overlap policy `queue`). */
23
+ queued?: boolean;
62
24
  nextRunAt?: string;
63
25
  lastRunAt?: string;
64
26
  lastRunStatus?: string;
@@ -96,6 +58,61 @@ interface UpdateJobRequest {
96
58
  maxRetries?: number;
97
59
  timeout?: string;
98
60
  overlapPolicy?: string;
61
+ maxConcurrentRuns?: number;
62
+ }
63
+
64
+ /**
65
+ * An interval or timeout as servers send it: milliseconds (.NET, Node) or a
66
+ * span such as `30s` (Python), shown as `30s`, `5m`, `1h`.
67
+ */
68
+ declare function formatSpan(value?: number | string | null): string | null;
69
+ /** Runs going now, when more than one may run: `2/3 running`, `1/3 running, 1 queued`. */
70
+ declare function formatRunning(job: JobDto): string | null;
71
+ declare class CliJobsCommandProcessor implements ICliCommandProcessor {
72
+ command: string;
73
+ extendsProcessor: boolean;
74
+ description: string;
75
+ author: ICliCommandAuthor;
76
+ metadata: CliProcessorMetadata;
77
+ processors?: ICliCommandProcessor[];
78
+ processCommand(_cmd: CliProcessCommand, _context: ICliExecutionContext): Promise<void>;
79
+ private handleList;
80
+ private handleInfo;
81
+ /**
82
+ * Without --server the action applies to every connected server that has
83
+ * the job, each reported on its own line. When several servers have it,
84
+ * a note says so (and how to target one).
85
+ */
86
+ private handleAction;
87
+ private handleHistory;
88
+ private handleLogs;
89
+ /**
90
+ * Prompt for each setting; Enter keeps the current value. Ctrl+C or
91
+ * Escape at any prompt (the reader resolves null) cancels the whole edit
92
+ * and nothing is sent. A job on several servers needs --server.
93
+ */
94
+ private handleEdit;
95
+ /**
96
+ * Stream job events until Ctrl+C (context.onAbort / context.signal) or
97
+ * until every watched server has closed its connection. On exit the
98
+ * sockets are closed and every subscription and listener is removed.
99
+ */
100
+ private handleWatch;
101
+ }
102
+
103
+ /**
104
+ * Provides tab-completion for job names when typing
105
+ * `server jobs <subcommand> <name>`.
106
+ *
107
+ * Priority 50 — checked before command and parameter completion so that
108
+ * job names are suggested instead of treating the value position as
109
+ * a sub-command or flag.
110
+ */
111
+ declare class CliJobNameCompletionProvider implements ICliCompletionProvider {
112
+ priority: number;
113
+ private services;
114
+ setServices(services: ICliServiceProvider): void;
115
+ getCompletions(context: ICliCompletionContext): Promise<string[]>;
99
116
  }
100
117
 
101
118
  declare class CliJobsService {
@@ -119,10 +136,10 @@ declare class CliJobsService {
119
136
  getExecution(jobId: string, execId: string): Promise<JobExecutionDto>;
120
137
  }
121
138
 
122
- declare const LIBRARY_VERSION = "2.2.0";
139
+ declare const LIBRARY_VERSION = "2.4.0";
123
140
  declare const API_VERSION = 2;
124
141
 
125
142
  declare const jobsModule: ICliModule;
126
143
 
127
- export { API_VERSION, CliJobNameCompletionProvider, CliJobsCommandProcessor, CliJobsService, LIBRARY_VERSION, jobsModule };
144
+ export { API_VERSION, CliJobNameCompletionProvider, CliJobsCommandProcessor, CliJobsService, LIBRARY_VERSION, formatRunning, formatSpan, jobsModule };
128
145
  export type { JobDto, JobExecutionDto, JobHistoryResponse, JobLogEntryDto, UpdateJobRequest };
package/public-api.js CHANGED
@@ -168,6 +168,24 @@ function formatRelativeTime(isoString) {
168
168
  if (diff < 864e5) return `${Math.round(diff / 36e5)}h ago`;
169
169
  return `${Math.round(diff / 864e5)}d ago`;
170
170
  }
171
+ function formatSpan(value) {
172
+ if (value === null || value === void 0 || value === "") return null;
173
+ if (typeof value === "string") return value;
174
+ if (!Number.isFinite(value) || value < 0) return null;
175
+ const units = [[864e5, "d"], [36e5, "h"], [6e4, "m"], [1e3, "s"]];
176
+ for (const [size, unit] of units) {
177
+ if (value >= size && value % size === 0) return `${value / size}${unit}`;
178
+ }
179
+ return value >= 1e3 ? `${(value / 1e3).toFixed(1)}s` : `${value}ms`;
180
+ }
181
+ function formatRunning(job) {
182
+ const running = job.runningExecutionIds?.length ?? (job.currentExecutionId ? 1 : 0);
183
+ const max = job.maxConcurrentRuns ?? 1;
184
+ if (running === 0 && !job.queued) return null;
185
+ const parts = [max > 1 ? `${running}/${max} running` : `${running} running`];
186
+ if (job.queued) parts.push("1 queued");
187
+ return parts.join(", ");
188
+ }
171
189
  function formatDuration(ms) {
172
190
  if (ms == null) return "-";
173
191
  if (ms < 1e3) return `${ms}ms`;
@@ -533,6 +551,11 @@ var CliJobsCommandProcessor = class {
533
551
  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.")
534
552
  );
535
553
  context.writer.writeln();
554
+ cliCore.writeWrapped(
555
+ context,
556
+ t.t("cli.server.jobs.edit.help.max_concurrent_runs", "Max concurrent runs: how many runs of the job may go at the same time (1 by default). When that many are running, the overlap policy decides what happens to the next one.")
557
+ );
558
+ context.writer.writeln();
536
559
  cliCore.writeUsage(context, [
537
560
  ["server jobs edit <name|id>", ""]
538
561
  ]);
@@ -624,7 +647,7 @@ var CliJobsCommandProcessor = class {
624
647
  j.name,
625
648
  j.group ?? "-",
626
649
  statusColor(j.status, context.writer),
627
- j.schedule ?? (j.interval ? `every ${j.interval}ms` : "-"),
650
+ j.schedule ?? (formatSpan(j.interval) ? `every ${formatSpan(j.interval)}` : "-"),
628
651
  formatRelativeTime(j.lastRunAt),
629
652
  formatRelativeTime(j.nextRunAt),
630
653
  formatDuration(j.lastRunDuration)
@@ -662,12 +685,16 @@ var CliJobsCommandProcessor = class {
662
685
  ["Status", statusColor(job.status, context.writer)],
663
686
  [
664
687
  "Schedule",
665
- job.schedule ?? (job.interval ? `every ${job.interval}ms` : "-")
688
+ job.schedule ?? (formatSpan(job.interval) ? `every ${formatSpan(job.interval)}` : "-")
666
689
  ],
667
690
  ["Max Retries", String(job.maxRetries)],
668
- ["Timeout", job.timeout ? `${job.timeout}ms` : "none"],
691
+ ["Timeout", formatSpan(job.timeout) ?? "none"],
669
692
  ["Overlap Policy", job.overlapPolicy],
670
- ["Current Execution", job.currentExecutionId ?? "none"],
693
+ ["Max Concurrent Runs", String(job.maxConcurrentRuns ?? 1)],
694
+ [
695
+ "Running",
696
+ job.runningExecutionIds && job.runningExecutionIds.length > 1 ? `${formatRunning(job)}: ${job.runningExecutionIds.join(", ")}` : formatRunning(job) && job.currentExecutionId ? `${formatRunning(job)} (${job.currentExecutionId})` : formatRunning(job) ?? "none"
697
+ ],
671
698
  ["Next Run", formatRelativeTime(job.nextRunAt)],
672
699
  ["Last Run", formatRelativeTime(job.lastRunAt)],
673
700
  [
@@ -911,7 +938,7 @@ var CliJobsCommandProcessor = class {
911
938
  const newGroup = await ask(`Group [${job.group ?? "none"}]: `);
912
939
  if (newGroup == null) return cancelled();
913
940
  if (newGroup) update.group = newGroup;
914
- const currentSchedule = job.schedule ?? (job.interval ? `interval: ${job.interval}ms` : "none");
941
+ const currentSchedule = job.schedule ?? (formatSpan(job.interval) ? `interval: ${formatSpan(job.interval)}` : "none");
915
942
  const newSchedule = await ask(
916
943
  `Cron schedule [${currentSchedule}]: `
917
944
  );
@@ -919,7 +946,7 @@ var CliJobsCommandProcessor = class {
919
946
  if (newSchedule) update.schedule = newSchedule;
920
947
  if (!newSchedule) {
921
948
  const newInterval = await ask(
922
- `Interval (e.g. 30s, 5m) [${job.interval ? `${job.interval}ms` : "none"}]: `
949
+ `Interval (e.g. 30s, 5m) [${formatSpan(job.interval) ?? "none"}]: `
923
950
  );
924
951
  if (newInterval == null) return cancelled();
925
952
  if (newInterval) update.interval = newInterval;
@@ -933,7 +960,7 @@ var CliJobsCommandProcessor = class {
933
960
  }
934
961
  }
935
962
  const newTimeout = await ask(
936
- `Timeout (e.g. 5m, 1h) [${job.timeout ? `${job.timeout}ms` : "none"}]: `
963
+ `Timeout (e.g. 5m, 1h) [${formatSpan(job.timeout) ?? "none"}]: `
937
964
  );
938
965
  if (newTimeout == null) return cancelled();
939
966
  if (newTimeout) update.timeout = newTimeout;
@@ -944,6 +971,20 @@ var CliJobsCommandProcessor = class {
944
971
  if (newOverlap && ["skip", "queue", "cancel"].includes(newOverlap.toLowerCase())) {
945
972
  update.overlapPolicy = newOverlap.toLowerCase();
946
973
  }
974
+ const newMax = await ask(
975
+ `Max concurrent runs [${job.maxConcurrentRuns ?? 1}]: `
976
+ );
977
+ if (newMax == null) return cancelled();
978
+ if (newMax) {
979
+ const parsed = Number(newMax);
980
+ if (Number.isInteger(parsed) && parsed >= 1) {
981
+ update.maxConcurrentRuns = parsed;
982
+ } else {
983
+ context.writer.writeWarning(
984
+ "Max concurrent runs must be a whole number of at least 1; kept as it was."
985
+ );
986
+ }
987
+ }
947
988
  if (Object.keys(update).length === 0) {
948
989
  context.writer.writeInfo("No changes made.");
949
990
  return;
@@ -1177,7 +1218,7 @@ var CliJobNameCompletionProvider = class {
1177
1218
  };
1178
1219
 
1179
1220
  // src/lib/version.ts
1180
- var LIBRARY_VERSION = "2.2.0";
1221
+ var LIBRARY_VERSION = "2.4.0";
1181
1222
  var API_VERSION = 2;
1182
1223
  var completionProvider = new CliJobNameCompletionProvider();
1183
1224
  var jobsModule = {
@@ -1201,6 +1242,8 @@ exports.CliJobNameCompletionProvider = CliJobNameCompletionProvider;
1201
1242
  exports.CliJobsCommandProcessor = CliJobsCommandProcessor;
1202
1243
  exports.CliJobsService = CliJobsService;
1203
1244
  exports.LIBRARY_VERSION = LIBRARY_VERSION;
1245
+ exports.formatRunning = formatRunning;
1246
+ exports.formatSpan = formatSpan;
1204
1247
  exports.jobsModule = jobsModule;
1205
1248
  //# sourceMappingURL=public-api.js.map
1206
1249
  //# sourceMappingURL=public-api.js.map