c8ctl-plugin-nano 1.24.1 → 1.25.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/README.md +8 -3
- package/c8ctl-plugin.js +87 -8
- package/nanobpmn-binary.json +3 -3
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -479,6 +479,7 @@ c8ctl nano supervisor
|
|
|
479
479
|
c8ctl nano supervisor status # id, state, pid, restarts, uptime
|
|
480
480
|
c8ctl nano supervisor add reviewer --max-parallel 2 # add + spawn a worker (forwards work flags)
|
|
481
481
|
c8ctl nano supervisor add reviewer --name reviewer-2 # a SECOND reviewer, named so it stays distinct
|
|
482
|
+
c8ctl nano supervisor add reviewer --instances 3 # add 3 distinct auto-named reviewers in one call
|
|
482
483
|
c8ctl nano supervisor restart reviewer # by worker id or profile name
|
|
483
484
|
c8ctl nano supervisor remove coder # stop + drop a worker (also: `all`)
|
|
484
485
|
c8ctl nano supervisor logs reviewer --follow # tail a worker's log (or the daemon's)
|
|
@@ -489,9 +490,13 @@ Each worker has a **name** — its supervisor id and the broker `workerName` it
|
|
|
489
490
|
registers under. Pass `--name` on `supervisor add` (or `work`) to set it;
|
|
490
491
|
omit it and one is auto-generated as `‹host›-‹profile›-‹random›`, so you can
|
|
491
492
|
run **several instances of the same profile** and they stay distinct
|
|
492
|
-
end-to-end (status, logs, and at the broker).
|
|
493
|
-
|
|
494
|
-
|
|
493
|
+
end-to-end (status, logs, and at the broker). To scale a hire to several
|
|
494
|
+
instances in one call, pass `--instances N` on `supervisor add` — it spawns N
|
|
495
|
+
distinct auto-named workers of the profile at once (default 1, capped per call).
|
|
496
|
+
Because each instance needs its own distinct name, `--instances N` (for N > 1)
|
|
497
|
+
cannot be combined with `--name`; omit `--name` to let them auto-name.
|
|
498
|
+
`restart`/`remove` accept either a worker id **or** a profile name — targeting a
|
|
499
|
+
profile affects *every* instance of it.
|
|
495
500
|
|
|
496
501
|
Each worker takes the **same flags as `nano work`** (`--max-parallel`,
|
|
497
502
|
`--recovery-window`, `--idle-timeout`, `--job-timeout`, `--poll-timeout`,
|
package/c8ctl-plugin.js
CHANGED
|
@@ -3701,6 +3701,58 @@ function extractNameFlag(parts) {
|
|
|
3701
3701
|
return { name: name != null && name.trim() !== '' ? name.trim() : undefined, rest };
|
|
3702
3702
|
}
|
|
3703
3703
|
|
|
3704
|
+
/**
|
|
3705
|
+
* Upper bound on how many workers a single `supervisor add --instances N` may
|
|
3706
|
+
* spawn. Not a fleet cap (add again to grow further) — a typo guard, so a
|
|
3707
|
+
* fat-fingered `--instances 100000` can't fork-bomb the host in one keystroke.
|
|
3708
|
+
*/
|
|
3709
|
+
const MAX_ADD_INSTANCES = 64;
|
|
3710
|
+
|
|
3711
|
+
/**
|
|
3712
|
+
* Parse the `--instances N` count for `supervisor add`. Accepts undefined/blank
|
|
3713
|
+
* (defaults to 1), and a whole number in `[1, MAX_ADD_INSTANCES]`. Rejects
|
|
3714
|
+
* non-integers, zero/negatives, and anything over the cap with a clear message.
|
|
3715
|
+
* When a flag is repeated the last occurrence wins (arrays are tolerated).
|
|
3716
|
+
* Returns `{ count }` on success or `{ error }` on rejection. Pure.
|
|
3717
|
+
*/
|
|
3718
|
+
function parseInstancesCount(raw) {
|
|
3719
|
+
const v = Array.isArray(raw) ? raw[raw.length - 1] : raw;
|
|
3720
|
+
if (v === undefined || v === null || (typeof v === 'string' && v.trim() === '')) {
|
|
3721
|
+
return { count: 1 };
|
|
3722
|
+
}
|
|
3723
|
+
const s = String(v).trim();
|
|
3724
|
+
if (!/^\d+$/.test(s)) return { error: `Invalid --instances "${v}": use a whole number between 1 and ${MAX_ADD_INSTANCES}.` };
|
|
3725
|
+
const n = Number.parseInt(s, 10);
|
|
3726
|
+
if (n < 1) return { error: `Invalid --instances "${v}": use a whole number between 1 and ${MAX_ADD_INSTANCES}.` };
|
|
3727
|
+
if (n > MAX_ADD_INSTANCES) return { error: `--instances ${n} exceeds the ${MAX_ADD_INSTANCES}-per-command cap; run "supervisor add" again to add more.` };
|
|
3728
|
+
return { count: n };
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
/**
|
|
3732
|
+
* Split `--instances N` / `--instances=N` out of a raw token list, returning
|
|
3733
|
+
* `{ count, rest, error }` where `rest` is the remaining work flags (with the
|
|
3734
|
+
* flag removed so it is never forwarded to `nano work`). Used by the interactive
|
|
3735
|
+
* console's `add`, whose tokens aren't parsed by the CLI flag layer. Last
|
|
3736
|
+
* occurrence wins; a trailing `--instances` with no value is treated as absent
|
|
3737
|
+
* (count 1), mirroring `extractNameFlag`. Pure.
|
|
3738
|
+
*/
|
|
3739
|
+
function extractInstancesFlag(parts) {
|
|
3740
|
+
const rest = [];
|
|
3741
|
+
let raw;
|
|
3742
|
+
const list = Array.isArray(parts) ? parts : [];
|
|
3743
|
+
for (let i = 0; i < list.length; i++) {
|
|
3744
|
+
const tok = String(list[i]);
|
|
3745
|
+
const eq = /^--instances=(.*)$/.exec(tok);
|
|
3746
|
+
if (eq) { raw = eq[1]; continue; }
|
|
3747
|
+
if (tok === '--instances') {
|
|
3748
|
+
if (i + 1 < list.length) { raw = String(list[i + 1]); i++; }
|
|
3749
|
+
continue;
|
|
3750
|
+
}
|
|
3751
|
+
rest.push(tok);
|
|
3752
|
+
}
|
|
3753
|
+
return { ...parseInstancesCount(raw), rest };
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3704
3756
|
/** Assign a unique, stable worker id from a profile name (pure). */
|
|
3705
3757
|
function supervisorWorkerId(profile, taken) {
|
|
3706
3758
|
const base = String(profile || '').trim() || 'worker';
|
|
@@ -4479,14 +4531,31 @@ async function supervisorAddCmd(req, flags) {
|
|
|
4479
4531
|
const logger = getLogger();
|
|
4480
4532
|
// The positional profile is what runs; `--name` names this worker instance
|
|
4481
4533
|
// (forwarded to the child as `nano work … --name`, and used as its supervisor
|
|
4482
|
-
// id). Omit `--name` to auto-generate ‹host›-‹profile›-‹random›.
|
|
4534
|
+
// id). Omit `--name` to auto-generate ‹host›-‹profile›-‹random›. `--instances N`
|
|
4535
|
+
// spawns N distinct auto-named workers of the profile in one call.
|
|
4483
4536
|
const profile = req.positional[1];
|
|
4484
|
-
if (!profile) { logger.error('Usage: c8ctl nano supervisor add <profile> [--name <worker>] [work flags]'); process.exit(1); }
|
|
4537
|
+
if (!profile) { logger.error('Usage: c8ctl nano supervisor add <profile> [--name <worker>] [--instances <n>] [work flags]'); process.exit(1); }
|
|
4538
|
+
const { count, error } = parseInstancesCount(flags?.instances);
|
|
4539
|
+
if (error) { logger.error(error); process.exit(1); }
|
|
4485
4540
|
const name = flags?.name ? String(flags.name).trim() : undefined;
|
|
4541
|
+
// A single `--name` can't apply to several distinct workers (each needs its
|
|
4542
|
+
// own broker workerName / supervisor id), so reject the combination and steer
|
|
4543
|
+
// the operator to auto-naming.
|
|
4544
|
+
if (name && count > 1) {
|
|
4545
|
+
logger.error('--name cannot be combined with --instances > 1 (each instance needs a distinct name); omit --name to auto-name them.');
|
|
4546
|
+
process.exit(1);
|
|
4547
|
+
}
|
|
4486
4548
|
await startSupervisorDaemon();
|
|
4487
|
-
const
|
|
4488
|
-
|
|
4489
|
-
|
|
4549
|
+
const workArgs = reconstructWorkArgs(flags);
|
|
4550
|
+
let added = 0;
|
|
4551
|
+
let failed = 0;
|
|
4552
|
+
for (let i = 0; i < count; i++) {
|
|
4553
|
+
const res = await supervisorRequest({ op: 'add', profile, name, args: workArgs });
|
|
4554
|
+
if (res.ok) { added++; logger.info(`Added worker "${res.worker.id}" (profile ${profile}); pid ${res.worker.pid ?? 'starting'}.`); }
|
|
4555
|
+
else { failed++; logger.error(`Could not add "${profile}": ${res.error}`); }
|
|
4556
|
+
}
|
|
4557
|
+
if (count > 1) logger.info(`Added ${added}/${count} instance(s) of "${profile}".`);
|
|
4558
|
+
if (failed > 0) process.exit(1);
|
|
4490
4559
|
}
|
|
4491
4560
|
|
|
4492
4561
|
async function supervisorRemoveCmd(req) {
|
|
@@ -4636,16 +4705,21 @@ async function attachSupervisorConsole(state) {
|
|
|
4636
4705
|
switch (cmd) {
|
|
4637
4706
|
case '': break;
|
|
4638
4707
|
case 'help':
|
|
4639
|
-
out('Commands: status | add <profile> [--name <worker>] [work flags] |');
|
|
4708
|
+
out('Commands: status | add <profile> [--name <worker>] [--instances <n>] [work flags] |');
|
|
4640
4709
|
out(' remove <id|profile|all> | restart <id|profile|all> |');
|
|
4641
4710
|
out(' logs [id] | detach | stop | help');
|
|
4642
4711
|
break;
|
|
4643
4712
|
case 'status': sock.write(encodeFrame({ op: 'status' })); break;
|
|
4644
4713
|
case 'add': {
|
|
4645
4714
|
const profile = parts.shift();
|
|
4646
|
-
if (!profile) { out('usage: add <profile> [--name <worker>] [work flags]'); break; }
|
|
4715
|
+
if (!profile) { out('usage: add <profile> [--name <worker>] [--instances <n>] [work flags]'); break; }
|
|
4647
4716
|
const { name, rest } = extractNameFlag(parts);
|
|
4648
|
-
|
|
4717
|
+
const { count, rest: workArgs, error } = extractInstancesFlag(rest);
|
|
4718
|
+
if (error) { out(error); break; }
|
|
4719
|
+
if (name && count > 1) { out('--name cannot be combined with --instances > 1; omit --name to auto-name them.'); break; }
|
|
4720
|
+
for (let i = 0; i < count; i++) {
|
|
4721
|
+
sock.write(encodeFrame({ op: 'add', profile, name, args: workArgs }));
|
|
4722
|
+
}
|
|
4649
4723
|
break;
|
|
4650
4724
|
}
|
|
4651
4725
|
case 'remove': case 'rm': {
|
|
@@ -6107,6 +6181,8 @@ export {
|
|
|
6107
6181
|
isValidWorkerName,
|
|
6108
6182
|
randomNameSuffix,
|
|
6109
6183
|
extractNameFlag,
|
|
6184
|
+
parseInstancesCount,
|
|
6185
|
+
extractInstancesFlag,
|
|
6110
6186
|
redactWorkArgs,
|
|
6111
6187
|
supervisorBackoffMs,
|
|
6112
6188
|
encodeFrame,
|
|
@@ -6120,6 +6196,7 @@ export {
|
|
|
6120
6196
|
runSupervisorDaemon,
|
|
6121
6197
|
startSupervisorDaemon,
|
|
6122
6198
|
supervisorRequest,
|
|
6199
|
+
supervisorAddCmd,
|
|
6123
6200
|
runningSupervisor,
|
|
6124
6201
|
readSupervisorState,
|
|
6125
6202
|
clearSupervisorState,
|
|
@@ -6169,6 +6246,7 @@ export const metadata = {
|
|
|
6169
6246
|
{ command: 'c8ctl nano supervisor', description: 'Attach an interactive console to the supervisor (detach with Ctrl-D, leaving it running)' },
|
|
6170
6247
|
{ command: 'c8ctl nano supervisor status', description: 'List supervised workers (pid, state, serviced job / idle, restarts, uptime) without the console' },
|
|
6171
6248
|
{ command: 'c8ctl nano supervisor add decider --max-parallel 2', description: 'Add a supervised worker (forwarding work flags) to the running supervisor' },
|
|
6249
|
+
{ command: 'c8ctl nano supervisor add reviewer --instances 3', description: 'Add 3 distinct auto-named instances of a profile in one call' },
|
|
6172
6250
|
{ command: 'c8ctl nano supervisor restart reviewer', description: 'Restart a supervised worker by id or profile' },
|
|
6173
6251
|
{ command: 'c8ctl nano supervisor stop', description: 'Stop the supervisor daemon and all its workers' },
|
|
6174
6252
|
],
|
|
@@ -6236,6 +6314,7 @@ export const commands = {
|
|
|
6236
6314
|
'poll-timeout': { type: 'string', description: 'work: broker long-poll window in ms each activateJobs request is held open (fewer reconnects → fewer transient connect errors); default 30000, 0 = broker default, negative = return immediately' },
|
|
6237
6315
|
'job-type': { type: 'string', multiple: true, description: 'work: extra job type to service alongside the rank×capability matrix (repeatable)' },
|
|
6238
6316
|
worker: { type: 'string', multiple: true, description: 'supervisor start: profile to launch as a supervised worker (repeatable)' },
|
|
6317
|
+
instances: { type: 'string', description: `supervisor add: spawn N distinct auto-named instances of the profile in one call (default 1, max ${MAX_ADD_INSTANCES}; cannot combine with --name)` },
|
|
6239
6318
|
attach: { type: 'boolean', description: 'supervisor start: attach the interactive console after starting the daemon' },
|
|
6240
6319
|
},
|
|
6241
6320
|
handler: async (args, flags) => {
|
package/nanobpmn-binary.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8ctl-plugin-nano",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "c8ctl plugin to start, inspect, and stop a local Nano BPM (nanobpmn) cluster",
|
|
6
6
|
"main": "c8ctl-plugin.js",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"semantic-release": "^25.0.3"
|
|
48
48
|
},
|
|
49
49
|
"optionalDependencies": {
|
|
50
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.
|
|
51
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.
|
|
52
|
-
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.
|
|
53
|
-
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.
|
|
54
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.
|
|
55
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.
|
|
56
|
-
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.
|
|
50
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.25.0",
|
|
51
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.25.0",
|
|
52
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.25.0",
|
|
53
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.25.0",
|
|
54
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.25.0",
|
|
55
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.25.0",
|
|
56
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.25.0"
|
|
57
57
|
}
|
|
58
58
|
}
|