c8ctl-plugin-nano 1.43.1 → 1.44.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 +7 -5
- package/c8ctl-plugin.js +81 -28
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -900,9 +900,10 @@ c8ctl nano workforce stop # remove this manifest's workers (+ st
|
|
|
900
900
|
c8ctl nano workforce remove qwen # drop an entry ("all" clears the manifest)
|
|
901
901
|
```
|
|
902
902
|
|
|
903
|
-
Every subcommand takes `--
|
|
904
|
-
manifest it operates on, so you can keep several — `--
|
|
905
|
-
`--
|
|
903
|
+
Every subcommand takes `--manifest <name>` (default `default`) to select which
|
|
904
|
+
manifest it operates on, so you can keep several — `--manifest review-only`,
|
|
905
|
+
`--manifest full-fleet` — side by side. (This flag was renamed from `--profile`,
|
|
906
|
+
which now collides with c8ctl's global connection-profile flag.)
|
|
906
907
|
|
|
907
908
|
### The manifest
|
|
908
909
|
|
|
@@ -991,8 +992,9 @@ hired with `--capabilities ""`.
|
|
|
991
992
|
- `start` with an empty/absent manifest → a friendly pointer at `workforce add`,
|
|
992
993
|
exit 0.
|
|
993
994
|
|
|
994
|
-
`--json` on `list`/`status` emits machine-readable output (
|
|
995
|
-
|
|
995
|
+
`--json` on `list`/`status` emits machine-readable output (via c8ctl's global
|
|
996
|
+
`--json` flag, whose parsed value is passed through to the handler) so the
|
|
997
|
+
install script and CI can consume it.
|
|
996
998
|
|
|
997
999
|
## Cleaning up disk
|
|
998
1000
|
|
package/c8ctl-plugin.js
CHANGED
|
@@ -2588,13 +2588,64 @@ function resolveAutoRestConfig(camunda, env = process.env) {
|
|
|
2588
2588
|
// dot-grammar.
|
|
2589
2589
|
// ---------------------------------------------------------------------------
|
|
2590
2590
|
|
|
2591
|
+
// Legacy (pre-nano-workforce#203) agent-task marker: a service task carried the
|
|
2592
|
+
// agent's prompt in an `io.nanobpm.agentTask.*` `<zeebe:header>` rather than a
|
|
2593
|
+
// `linkName="prompt"` linked resource. The current package detector
|
|
2594
|
+
// (`scanTaskDefinitions`, whose `agentic` flag keys SOLELY off the linked-prompt
|
|
2595
|
+
// side-car) therefore reports `agentic:false` for such tasks. We keep a narrow,
|
|
2596
|
+
// self-contained fallback so `--auto` still discovers agent job types against an
|
|
2597
|
+
// engine still holding a pre-#203 deployment (issue #120 acceptance criterion:
|
|
2598
|
+
// "Legacy header-based BPMN still discovers correctly"). This is a supplement to
|
|
2599
|
+
// — never a replacement for — the package flag: the authoritative task-type /
|
|
2600
|
+
// process derivation still comes from `scanTaskDefinitions`; this only answers
|
|
2601
|
+
// "is this leaf an agent task?" for the legacy shape.
|
|
2602
|
+
//
|
|
2603
|
+
// True when `body` (a service task's inner XML) declares any
|
|
2604
|
+
// `io.nanobpm.agentTask*` `<zeebe:header>` key — the sole such header on a
|
|
2605
|
+
// pre-#203 agent service task. The regex is compiled once (it is called once
|
|
2606
|
+
// per matched `<serviceTask>` during `--auto` scans, so recompiling per call
|
|
2607
|
+
// would allocate needlessly across many deployed definitions).
|
|
2608
|
+
const AGENT_TASK_HEADER_RE = new RegExp(
|
|
2609
|
+
`<(?:\\w+:)?header\\b[^>]*\\bkey\\s*=\\s*(["'])${AGENT_TASK_NS.replace(/[.]/g, '\\.')}(?:\\.[^"']*)?\\1`,
|
|
2610
|
+
'i'
|
|
2611
|
+
);
|
|
2612
|
+
function serviceTaskHasAgentHeader(body) {
|
|
2613
|
+
return AGENT_TASK_HEADER_RE.test(String(body || ''));
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
// The set of service-task element ids in `xml` bearing the legacy agent-task
|
|
2617
|
+
// header. Correlates back to `scanTaskDefinitions` leaves by `elementId`, so a
|
|
2618
|
+
// leaf is treated as legacy-agentic only when it ALSO has a `taskDefinition type`
|
|
2619
|
+
// (the package only yields leaves that do) — matching the issue rule "prompt link
|
|
2620
|
+
// OR legacy header, AND a non-empty task type".
|
|
2621
|
+
function legacyAgentHeaderElementIds(xml) {
|
|
2622
|
+
const ids = new Set();
|
|
2623
|
+
const src = String(xml || '');
|
|
2624
|
+
// Cheap guard: a legacy agent-task header always contains the literal
|
|
2625
|
+
// `io.nanobpm.agentTask` namespace, so a document lacking that substring
|
|
2626
|
+
// cannot match — skip the full `<serviceTask>` walk entirely. This avoids
|
|
2627
|
+
// parsing every deployed definition in `--auto` enrolment loops when none
|
|
2628
|
+
// carry the legacy marker.
|
|
2629
|
+
if (!src.includes(AGENT_TASK_NS)) return ids;
|
|
2630
|
+
const taskRe =
|
|
2631
|
+
/<(?:\w+:)?serviceTask\b[^>]*?\bid\s*=\s*(["'])(.*?)\1[^>]*?>([\s\S]*?)<\/(?:\w+:)?serviceTask>/g;
|
|
2632
|
+
let m;
|
|
2633
|
+
while ((m = taskRe.exec(src)) !== null) {
|
|
2634
|
+
if (serviceTaskHasAgentHeader(m[3])) ids.add(m[2]);
|
|
2635
|
+
}
|
|
2636
|
+
return ids;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2591
2639
|
// Scan one deployed BPMN document for its *agent* task-definition leaves: the
|
|
2592
2640
|
// subset of `@nanobpm/agentic` `demand.scanTaskDefinitions(xml)` leaves whose
|
|
2593
2641
|
// canonical `agentic` flag is set (i.e. the service task declares a
|
|
2594
|
-
// `linkName="prompt"` linked resource)
|
|
2595
|
-
//
|
|
2596
|
-
//
|
|
2597
|
-
//
|
|
2642
|
+
// `linkName="prompt"` linked resource) OR — for backward compatibility with
|
|
2643
|
+
// pre-#203 deployments — which carry the legacy `io.nanobpm.agentTask` header.
|
|
2644
|
+
// Returns `{ taskType, process }` leaves in first-occurrence order; a task
|
|
2645
|
+
// matched by both signals is the same leaf, so it is emitted once. The published
|
|
2646
|
+
// `scanTaskDefinitions` is INJECTED so this stays a pure, synchronous function;
|
|
2647
|
+
// `readDeployedAgentJobTypes` supplies the real one from the lazily-imported
|
|
2648
|
+
// demand surface (`agentic.mjs`).
|
|
2598
2649
|
function scanAgentTaskLeaves(xml, scanTaskDefinitions) {
|
|
2599
2650
|
if (typeof scanTaskDefinitions !== 'function') {
|
|
2600
2651
|
throw new TypeError(
|
|
@@ -2602,8 +2653,10 @@ function scanAgentTaskLeaves(xml, scanTaskDefinitions) {
|
|
|
2602
2653
|
`(got ${typeof scanTaskDefinitions}); pass demand.scanTaskDefinitions from ./agentic.mjs`
|
|
2603
2654
|
);
|
|
2604
2655
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2656
|
+
const src = String(xml || '');
|
|
2657
|
+
const legacyIds = legacyAgentHeaderElementIds(src);
|
|
2658
|
+
return scanTaskDefinitions(src)
|
|
2659
|
+
.filter((leaf) => leaf.agentic || legacyIds.has(leaf.elementId))
|
|
2607
2660
|
.map((leaf) => ({ taskType: leaf.taskType, process: leaf.process }));
|
|
2608
2661
|
}
|
|
2609
2662
|
|
|
@@ -8709,7 +8762,7 @@ function formatWorkforceStatus(report) {
|
|
|
8709
8762
|
const missing = entries.filter((e) => e.running < e.desired);
|
|
8710
8763
|
if (missing.length > 0) {
|
|
8711
8764
|
lines.push('');
|
|
8712
|
-
lines.push(` Missing: ${missing.map((e) => `${e.profile} (${e.running}/${e.desired})`).join(', ')} — run: c8ctl nano workforce start${report.name === DEFAULT_WORKFORCE_MANIFEST ? '' : ` --
|
|
8765
|
+
lines.push(` Missing: ${missing.map((e) => `${e.profile} (${e.running}/${e.desired})`).join(', ')} — run: c8ctl nano workforce start${report.name === DEFAULT_WORKFORCE_MANIFEST ? '' : ` --manifest ${report.name}`}`);
|
|
8713
8766
|
}
|
|
8714
8767
|
if (Array.isArray(report.extra) && report.extra.length > 0) {
|
|
8715
8768
|
lines.push('');
|
|
@@ -8718,16 +8771,16 @@ function formatWorkforceStatus(report) {
|
|
|
8718
8771
|
return lines.join('\n');
|
|
8719
8772
|
}
|
|
8720
8773
|
|
|
8721
|
-
/** Resolve the manifest name from `--
|
|
8722
|
-
function
|
|
8723
|
-
// A repeated `--
|
|
8724
|
-
let
|
|
8725
|
-
if (Array.isArray(
|
|
8726
|
-
return typeof
|
|
8774
|
+
/** Resolve the manifest name from `--manifest` (default `default`). Pure. */
|
|
8775
|
+
function lastManifestValue(flags) {
|
|
8776
|
+
// A repeated `--manifest` flag arrives as a string[]; honor the last value.
|
|
8777
|
+
let manifest = flags?.manifest;
|
|
8778
|
+
if (Array.isArray(manifest)) manifest = manifest.length ? manifest[manifest.length - 1] : '';
|
|
8779
|
+
return typeof manifest === 'string' ? manifest.trim() : '';
|
|
8727
8780
|
}
|
|
8728
8781
|
|
|
8729
8782
|
function workforceManifestName(flags) {
|
|
8730
|
-
return
|
|
8783
|
+
return lastManifestValue(flags) || DEFAULT_WORKFORCE_MANIFEST;
|
|
8731
8784
|
}
|
|
8732
8785
|
|
|
8733
8786
|
/** Fetch the live supervisor worker set, or `[]` when no daemon is running. */
|
|
@@ -8745,7 +8798,7 @@ async function workforceAddCmd(req, flags, manifestName) {
|
|
|
8745
8798
|
const logger = getLogger();
|
|
8746
8799
|
const profile = req.positional[1];
|
|
8747
8800
|
if (!profile) {
|
|
8748
|
-
logger.error('Usage: c8ctl nano workforce add <profile> [--instances <n>] [--auto [--auto-scope <s>] | --roles a,b,c] [--arg <flag> ...] [--
|
|
8801
|
+
logger.error('Usage: c8ctl nano workforce add <profile> [--instances <n>] [--auto [--auto-scope <s>] | --roles a,b,c] [--arg <flag> ...] [--manifest <manifest>]');
|
|
8749
8802
|
process.exit(1);
|
|
8750
8803
|
}
|
|
8751
8804
|
if (!isValidProfileName(profile)) {
|
|
@@ -8819,14 +8872,14 @@ async function workforceAddCmd(req, flags, manifestName) {
|
|
|
8819
8872
|
manifest = upsertManifestEntry(manifest, entry);
|
|
8820
8873
|
writeWorkforceManifest(manifest);
|
|
8821
8874
|
logger.info(`${existed ? 'Updated' : 'Added'} "${profile}" in workforce "${manifestName}": instances ${count}, roles ${describeEntryRoles(entry)}${extraArgs.length ? `, args ${redactWorkArgs(extraArgs).join(' ')}` : ''}.`);
|
|
8822
|
-
logger.info(`Bring it up with: c8ctl nano workforce start${manifestName === DEFAULT_WORKFORCE_MANIFEST ? '' : ` --
|
|
8875
|
+
logger.info(`Bring it up with: c8ctl nano workforce start${manifestName === DEFAULT_WORKFORCE_MANIFEST ? '' : ` --manifest ${manifestName}`}`);
|
|
8823
8876
|
}
|
|
8824
8877
|
|
|
8825
8878
|
async function workforceRemoveCmd(req, flags, manifestName) {
|
|
8826
8879
|
const logger = getLogger();
|
|
8827
8880
|
const profile = req.positional[1];
|
|
8828
8881
|
if (!profile) {
|
|
8829
|
-
logger.error('Usage: c8ctl nano workforce remove <profile|all> [--
|
|
8882
|
+
logger.error('Usage: c8ctl nano workforce remove <profile|all> [--manifest <manifest>]');
|
|
8830
8883
|
process.exit(1);
|
|
8831
8884
|
}
|
|
8832
8885
|
const manifest = readWorkforceManifestStrict(manifestName);
|
|
@@ -8841,9 +8894,9 @@ async function workforceRemoveCmd(req, flags, manifestName) {
|
|
|
8841
8894
|
async function workforceListCmd(req, flags, manifestName) {
|
|
8842
8895
|
const logger = getLogger();
|
|
8843
8896
|
const json = coerceBool(flags?.json, false);
|
|
8844
|
-
const
|
|
8897
|
+
const explicitManifest = lastManifestValue(flags) !== '';
|
|
8845
8898
|
const manifest = readWorkforceManifestStrict(manifestName);
|
|
8846
|
-
const others =
|
|
8899
|
+
const others = explicitManifest ? null : listWorkforceManifestNames();
|
|
8847
8900
|
if (json) {
|
|
8848
8901
|
const payload = { manifest: manifest || null };
|
|
8849
8902
|
if (others) payload.manifests = others;
|
|
@@ -9042,15 +9095,15 @@ async function workforceStopCmd(req, flags, manifestName) {
|
|
|
9042
9095
|
async function workforceCommand(req, flags) {
|
|
9043
9096
|
const logger = getLogger();
|
|
9044
9097
|
const action = (req.positional[0] || '').toLowerCase();
|
|
9045
|
-
// A bare `--
|
|
9098
|
+
// A bare `--manifest` (no value) is parsed as boolean `true`; reject it so we
|
|
9046
9099
|
// fail fast instead of silently operating on the default manifest.
|
|
9047
|
-
if (flags?.
|
|
9048
|
-
logger.error('--
|
|
9100
|
+
if (flags?.manifest === true) {
|
|
9101
|
+
logger.error('--manifest requires a manifest name.');
|
|
9049
9102
|
process.exit(1);
|
|
9050
9103
|
}
|
|
9051
9104
|
const manifestName = workforceManifestName(flags);
|
|
9052
9105
|
if (!isValidManifestName(manifestName)) {
|
|
9053
|
-
logger.error(`Invalid --
|
|
9106
|
+
logger.error(`Invalid --manifest "${manifestName}". Use letters, digits, dot, dash or underscore.`);
|
|
9054
9107
|
process.exit(1);
|
|
9055
9108
|
}
|
|
9056
9109
|
switch (action) {
|
|
@@ -10820,6 +10873,7 @@ export {
|
|
|
10820
10873
|
diffJobTypes,
|
|
10821
10874
|
parseJobTypeFlags,
|
|
10822
10875
|
scanAgentTaskLeaves,
|
|
10876
|
+
serviceTaskHasAgentHeader,
|
|
10823
10877
|
readDeployedAgentJobTypes,
|
|
10824
10878
|
resolveAutoJobTypes,
|
|
10825
10879
|
workAgent,
|
|
@@ -10903,7 +10957,7 @@ export {
|
|
|
10903
10957
|
buildWorkforceStatus,
|
|
10904
10958
|
formatWorkforceStatus,
|
|
10905
10959
|
workforceManifestName,
|
|
10906
|
-
|
|
10960
|
+
lastManifestValue,
|
|
10907
10961
|
};
|
|
10908
10962
|
|
|
10909
10963
|
export const metadata = {
|
|
@@ -10961,7 +11015,7 @@ export const metadata = {
|
|
|
10961
11015
|
{ command: 'c8ctl nano workforce add copilot --instances 5 --auto', description: 'Compose a reusable fleet: 5 copilot workers serving every deployed agent job type (--auto)' },
|
|
10962
11016
|
{ command: 'c8ctl nano workforce add qwen --instances 2 --roles pr-review,feature', description: "Add an entry mapped to explicit job types (<rank>:pr-review, <rank>:feature, where <rank> is the qwen hire's rank at start) — does not mutate the hired profile" },
|
|
10963
11017
|
{ command: 'c8ctl nano workforce start', description: "Ensure the daemon is up, then reconcile running workers to the 'default' manifest (idempotent — a second run changes nothing)" },
|
|
10964
|
-
{ command: 'c8ctl nano workforce start --
|
|
11018
|
+
{ command: 'c8ctl nano workforce start --manifest review-only', description: 'Bring up a named manifest (<stateHome>/workforce/review-only.json)' },
|
|
10965
11019
|
{ command: 'c8ctl nano workforce status --json', description: 'Manifest entries joined against live supervisor status (desired vs actual), machine-readable for the install script / CI' },
|
|
10966
11020
|
{ command: 'c8ctl nano workforce list', description: 'Print the default manifest and list the manifests that exist on this machine' },
|
|
10967
11021
|
{ command: 'c8ctl nano workforce stop', description: "Remove this manifest's workers; stop the daemon too if no supervised workers remain" },
|
|
@@ -11036,9 +11090,8 @@ export const commands = {
|
|
|
11036
11090
|
worker: { type: 'string', multiple: true, description: 'supervisor start: profile to launch as a supervised worker (repeatable)' },
|
|
11037
11091
|
instances: { type: 'string', description: `supervisor add / workforce add: spawn/compose N distinct instances of the profile in one call (default 1, max ${MAX_ADD_INSTANCES}; for supervisor add cannot combine with --name)` },
|
|
11038
11092
|
attach: { type: 'boolean', description: 'supervisor start: attach the interactive console after starting the daemon' },
|
|
11039
|
-
|
|
11093
|
+
manifest: { type: 'string', description: `workforce: manifest name to operate on (default ${DEFAULT_WORKFORCE_MANIFEST}); each subcommand reads/writes <stateHome>/workforce/<name>.json. Renamed from --profile (which now collides with c8ctl's global connection-profile flag).` },
|
|
11040
11094
|
roles: { type: 'string', description: 'workforce add: comma-separated role list for the entry (→ --job-type <rank>:<role> at start); mutually exclusive with --auto' },
|
|
11041
|
-
json: { type: 'boolean', description: 'workforce list/status: emit machine-readable JSON (for the install script / CI)' },
|
|
11042
11095
|
},
|
|
11043
11096
|
handler: async (args, flags) => {
|
|
11044
11097
|
const logger = getLogger();
|
|
@@ -11198,7 +11251,7 @@ function printUsage() {
|
|
|
11198
11251
|
console.log(' c8ctl nano assign <profileName> <cap[,cap...]> [--name <n>] [--capabilities <a,b>]');
|
|
11199
11252
|
console.log(' c8ctl nano work <profileName> [--auto [--auto-scope <p>]] [--arg <switch> ...] [--recovery-window <ms>] [--idle-timeout <ms>] [--job-timeout <ms>] [--poll-timeout <ms>] [--job-type <token> ...] [--sandbox none|docker|podman] [--image <ref>] [--env NAME=VALUE ...] [--secret-resolver host] [--min-free-mb <n>] [--clone-timeout <ms>] [--keep-runs] [--stream]');
|
|
11200
11253
|
console.log(' c8ctl nano supervisor [start|status|add|remove|restart|stop|logs|attach] ... (manage many workers from one terminal)');
|
|
11201
|
-
console.log(' c8ctl nano workforce [add|remove|list|start|status|stop] ... [--
|
|
11254
|
+
console.log(' c8ctl nano workforce [add|remove|list|start|status|stop] ... [--manifest <manifest>] (declarative, reusable fleet manifests)');
|
|
11202
11255
|
console.log('');
|
|
11203
11256
|
console.log('Subcommands:');
|
|
11204
11257
|
console.log(' start Spawn an N-node local cluster wired to talk to each other on localhost');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8ctl-plugin-nano",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.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",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
},
|
|
58
58
|
"optionalDependencies": {
|
|
59
59
|
"node-pty": "^1.0.0",
|
|
60
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.
|
|
61
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.
|
|
62
|
-
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.
|
|
63
|
-
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.
|
|
64
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.
|
|
65
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.
|
|
66
|
-
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.
|
|
60
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.44.0",
|
|
61
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.44.0",
|
|
62
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.44.0",
|
|
63
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.44.0",
|
|
64
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.44.0",
|
|
65
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.44.0",
|
|
66
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.44.0"
|
|
67
67
|
}
|
|
68
68
|
}
|