c8ctl-plugin-nano 1.38.1 → 1.39.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/c8ctl-plugin.js +69 -77
- package/package.json +9 -9
package/c8ctl-plugin.js
CHANGED
|
@@ -2483,82 +2483,43 @@ function resolveAutoRestConfig(camunda, env = process.env) {
|
|
|
2483
2483
|
// exist" is answerable from that engine alone.
|
|
2484
2484
|
//
|
|
2485
2485
|
// `@nanobpm/agentic/demand` already reads deployed `taskDefinition` leaves over
|
|
2486
|
-
// C8 REST (`process-definitions/search` → `/{key}/xml`)
|
|
2487
|
-
//
|
|
2488
|
-
//
|
|
2489
|
-
//
|
|
2490
|
-
//
|
|
2491
|
-
//
|
|
2492
|
-
//
|
|
2493
|
-
//
|
|
2486
|
+
// C8 REST (`process-definitions/search` → `/{key}/xml`). As of
|
|
2487
|
+
// `@nanobpm/agentic@0.4.0` its `scanTaskDefinitions(xml)` tags every leaf with a
|
|
2488
|
+
// canonical `agentic: boolean` — true iff the service task declares a
|
|
2489
|
+
// `<zeebe:linkedResource … linkName="prompt">` base-prompt side-car (its internal
|
|
2490
|
+
// `hasPromptLink`). That flag is the SINGLE SOURCE OF TRUTH for agentic-ness (see
|
|
2491
|
+
// the package's `demand/taskdef.d.ts` and nano-workforce SPEC "Agent job
|
|
2492
|
+
// contract"): every external agent task delivers its base prompt through a
|
|
2493
|
+
// `linkName="prompt"` linked resource, and no in-process worker task does. Not
|
|
2494
|
+
// every service task is an agent task — plain connectors and record-keepers
|
|
2495
|
+
// (e.g. `pr.record-plan`) are ordinary workers, and they carry no prompt link.
|
|
2496
|
+
//
|
|
2497
|
+
// Per AGENTS.md "Derivation Over Duplication: No Drift Surfaces", this plugin
|
|
2498
|
+
// CONSUMES that flag rather than re-implementing the scan, so the detector can
|
|
2499
|
+
// never drift out of lock-step with the package again (as it did in #95, when a
|
|
2500
|
+
// local copy keyed on the legacy `io.nanobpm.agentTask` header missed the current
|
|
2501
|
+
// linked-prompt marker). Advertise the raw job-type string the engine matches
|
|
2502
|
+
// (`senior:plan`) verbatim — colon-named types are NOT forced through the agentic
|
|
2503
|
+
// dot-grammar.
|
|
2494
2504
|
// ---------------------------------------------------------------------------
|
|
2495
2505
|
|
|
2496
|
-
//
|
|
2497
|
-
// `
|
|
2498
|
-
//
|
|
2499
|
-
// connector / record-keeper. Matches the exact `io.nanobpm.agentTask` key and
|
|
2500
|
-
// any flattened `io.nanobpm.agentTask.*` dotpath key (element templates emit the
|
|
2501
|
-
// latter, e.g. `io.nanobpm.agentTask.task.prompt`). Older deployments carry this;
|
|
2502
|
-
// the current `@nanobpm/workflow` toolchain emits the linked-prompt marker below
|
|
2503
|
-
// instead, so BOTH must be recognised (jwulf/c8ctl-plugin-nano#95).
|
|
2504
|
-
function serviceTaskHasAgentHeader(body) {
|
|
2505
|
-
const headerRe = /<zeebe:header\b[^>]*\bkey\s*=\s*"([^"]*)"/g;
|
|
2506
|
-
let m;
|
|
2507
|
-
while ((m = headerRe.exec(body)) !== null) {
|
|
2508
|
-
const key = m[1];
|
|
2509
|
-
if (key === AGENT_TASK_NS || key.startsWith(`${AGENT_TASK_NS}.`)) return true;
|
|
2510
|
-
}
|
|
2511
|
-
return false;
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
// True iff a serviceTask body links a prompt resource — the CURRENT canonical
|
|
2515
|
-
// agent-task marker emitted by `@nanobpm/workflow` / the Urban toolchain:
|
|
2516
|
-
// `<zeebe:linkedResource … resourceType="GenericScript" linkName="prompt" />`.
|
|
2517
|
-
// An agent task links the model/prompt the harness runs; a plain connector /
|
|
2518
|
-
// record-keeper does not. Matched by the `linkName="prompt"` binding (attribute
|
|
2519
|
-
// order-independent) so a compiled model with no `io.nanobpm.agentTask` header
|
|
2520
|
-
// is still discovered (jwulf/c8ctl-plugin-nano#95). Kept in lock-step with the
|
|
2521
|
-
// authored nano-workforce models (resources/processes/*.bpmn), where every
|
|
2522
|
-
// `senior:*` service task carries this binding and none carry the legacy header.
|
|
2523
|
-
// NOTE: this mirrors `@nanobpm/agentic@0.4.0`'s released `scanTaskDefinitions`
|
|
2524
|
-
// `agentic` flag (its internal `hasPromptLink`); #102 tracks replacing this local
|
|
2525
|
-
// copy by consuming that detector once the `^0.1.0 → ^0.4.0` bump is vetted.
|
|
2526
|
-
function serviceTaskHasLinkedPrompt(body) {
|
|
2527
|
-
const re = new RegExp(`<zeebe:linkedResource\\b[^>]*\\blinkName\\s*=\\s*"${DEFAULT_PROMPT_LINK_NAME}"`);
|
|
2528
|
-
return re.test(String(body || ''));
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
|
-
// True iff a serviceTask is an *agent* task — by EITHER the legacy
|
|
2532
|
-
// `io.nanobpm.agentTask.*` header OR the current linked-prompt marker. Either
|
|
2533
|
-
// alone is sufficient; deployments in the wild carry one or the other.
|
|
2534
|
-
function serviceTaskIsAgentTask(body) {
|
|
2535
|
-
return serviceTaskHasAgentHeader(body) || serviceTaskHasLinkedPrompt(body);
|
|
2536
|
-
}
|
|
2537
|
-
|
|
2538
|
-
// Scan one deployed BPMN document for its *agent* task-definition leaves: every
|
|
2539
|
-
// `<bpmn:serviceTask>` carrying BOTH a non-empty `<zeebe:taskDefinition type>`
|
|
2540
|
-
// AND an agent-task marker (legacy `io.nanobpm.agentTask.` header OR a
|
|
2506
|
+
// Scan one deployed BPMN document for its *agent* task-definition leaves: the
|
|
2507
|
+
// subset of `@nanobpm/agentic` `demand.scanTaskDefinitions(xml)` leaves whose
|
|
2508
|
+
// canonical `agentic` flag is set (i.e. the service task declares a
|
|
2541
2509
|
// `linkName="prompt"` linked resource). Returns `{ taskType, process }` leaves in
|
|
2542
|
-
// first-occurrence order.
|
|
2543
|
-
//
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
while ((block = blockRe.exec(source)) !== null) {
|
|
2552
|
-
const body = block[1];
|
|
2553
|
-
const tdMatch = body.match(/<zeebe:taskDefinition\b[^>]*>/);
|
|
2554
|
-
if (!tdMatch) continue;
|
|
2555
|
-
const typeMatch = tdMatch[0].match(/\btype\s*=\s*"([^"]*)"/);
|
|
2556
|
-
const taskType = typeMatch ? typeMatch[1] : '';
|
|
2557
|
-
if (!taskType) continue;
|
|
2558
|
-
if (!serviceTaskIsAgentTask(body)) continue;
|
|
2559
|
-
out.push({ taskType, process: proc });
|
|
2510
|
+
// first-occurrence order. The published `scanTaskDefinitions` is INJECTED so this
|
|
2511
|
+
// stays a pure, synchronous function; `readDeployedAgentJobTypes` supplies the
|
|
2512
|
+
// real one from the lazily-imported demand surface (`agentic.mjs`).
|
|
2513
|
+
function scanAgentTaskLeaves(xml, scanTaskDefinitions) {
|
|
2514
|
+
if (typeof scanTaskDefinitions !== 'function') {
|
|
2515
|
+
throw new TypeError(
|
|
2516
|
+
'scanAgentTaskLeaves: `scanTaskDefinitions` must be an injected function ' +
|
|
2517
|
+
`(got ${typeof scanTaskDefinitions}); pass demand.scanTaskDefinitions from ./agentic.mjs`
|
|
2518
|
+
);
|
|
2560
2519
|
}
|
|
2561
|
-
return
|
|
2520
|
+
return scanTaskDefinitions(String(xml || ''))
|
|
2521
|
+
.filter((leaf) => leaf.agentic)
|
|
2522
|
+
.map((leaf) => ({ taskType: leaf.taskType, process: leaf.process }));
|
|
2562
2523
|
}
|
|
2563
2524
|
|
|
2564
2525
|
// Read the distinct deployed *agent* job types through a demand C8RestReader
|
|
@@ -2567,12 +2528,13 @@ function scanAgentTaskLeaves(xml) {
|
|
|
2567
2528
|
// optional `scope` narrows to one app/network — kept only when the leaf's
|
|
2568
2529
|
// `bpmn:process` id equals or is prefixed by the scope string.
|
|
2569
2530
|
async function readDeployedAgentJobTypes(reader, { scope = '' } = {}) {
|
|
2531
|
+
const { demand } = await import('./agentic.mjs');
|
|
2570
2532
|
const keys = await reader.searchProcessDefinitionKeys();
|
|
2571
2533
|
const seen = new Set();
|
|
2572
2534
|
const out = [];
|
|
2573
2535
|
for (const key of keys) {
|
|
2574
2536
|
const xml = await reader.getProcessDefinitionXml(key);
|
|
2575
|
-
for (const leaf of scanAgentTaskLeaves(xml)) {
|
|
2537
|
+
for (const leaf of scanAgentTaskLeaves(xml, demand.scanTaskDefinitions)) {
|
|
2576
2538
|
if (scope && !(leaf.process === scope || leaf.process.startsWith(scope))) continue;
|
|
2577
2539
|
if (seen.has(leaf.taskType)) continue;
|
|
2578
2540
|
seen.add(leaf.taskType);
|
|
@@ -4730,7 +4692,9 @@ async function workAgent(req, flags) {
|
|
|
4730
4692
|
// no capability, no app enrol endpoint, no channel connection. It is the
|
|
4731
4693
|
// mutually-exclusive counterpart to capability-resolved SERVE: in `--auto`
|
|
4732
4694
|
// the rank×capability matrix is bypassed entirely (any deployed agent job is
|
|
4733
|
-
// served, gated only by the `
|
|
4695
|
+
// served, gated only by the leaf's canonical `agentic` flag — the
|
|
4696
|
+
// `linkName="prompt"` linked-resource marker read by
|
|
4697
|
+
// `@nanobpm/agentic`'s demand scanner), and the
|
|
4734
4698
|
// desired set is reconciled by polling the engine rather than watching the
|
|
4735
4699
|
// profile. `--auto-scope <process-id|prefix>` narrows the blast radius to one
|
|
4736
4700
|
// app/network; without it, every agent job type on the engine is served.
|
|
@@ -5864,6 +5828,10 @@ function summarizeSupervisorWorker(w, now = Date.now()) {
|
|
|
5864
5828
|
startedAtMs,
|
|
5865
5829
|
lastExit: w.lastExit ?? null,
|
|
5866
5830
|
args: Array.isArray(w.args) ? w.args : [],
|
|
5831
|
+
// The per-worker log path (logs/supervisor/worker-<id>.log). Carried through
|
|
5832
|
+
// so `supervisor status` can surface it (see formatSupervisorLogsLines);
|
|
5833
|
+
// null when the source record predates it (e.g. an older persisted state).
|
|
5834
|
+
logFile: w.logFile ?? null,
|
|
5867
5835
|
activity,
|
|
5868
5836
|
engine,
|
|
5869
5837
|
agentic,
|
|
@@ -6070,6 +6038,30 @@ function createSupervisorLiveView({
|
|
|
6070
6038
|
};
|
|
6071
6039
|
}
|
|
6072
6040
|
|
|
6041
|
+
/**
|
|
6042
|
+
* The `Logs:` block for `supervisor status`, derived purely from the status
|
|
6043
|
+
* payload so it renders identically for a live daemon status and a synthesized
|
|
6044
|
+
* one. Lists the daemon log and each worker's log file, plus a hint at the
|
|
6045
|
+
* existing tailer. Returns an empty array (no section) when no log path is
|
|
6046
|
+
* known — an older persisted state, or a dead daemon whose frame carried none.
|
|
6047
|
+
*/
|
|
6048
|
+
function formatSupervisorLogsLines(status) {
|
|
6049
|
+
const daemonLog = status?.daemon?.logFile || null;
|
|
6050
|
+
const workers = Array.isArray(status?.workers) ? status.workers : [];
|
|
6051
|
+
const workerLogs = workers
|
|
6052
|
+
.filter((w) => w && w.logFile)
|
|
6053
|
+
.map((w) => ({ label: String(w.id), path: String(w.logFile) }));
|
|
6054
|
+
const entries = [];
|
|
6055
|
+
if (daemonLog) entries.push({ label: 'daemon', path: String(daemonLog) });
|
|
6056
|
+
for (const w of workerLogs) entries.push(w);
|
|
6057
|
+
if (entries.length === 0) return [];
|
|
6058
|
+
const labelWidth = Math.max(...entries.map((e) => e.label.length));
|
|
6059
|
+
const out = ['', 'Logs:'];
|
|
6060
|
+
for (const e of entries) out.push(` ${e.label.padEnd(labelWidth)} ${e.path}`);
|
|
6061
|
+
out.push(' View: c8ctl nano supervisor logs [<id>] [--follow]');
|
|
6062
|
+
return out;
|
|
6063
|
+
}
|
|
6064
|
+
|
|
6073
6065
|
/** Render a supervisor status object as an aligned text table. */
|
|
6074
6066
|
function formatSupervisorStatus(status) {
|
|
6075
6067
|
const lines = [];
|
|
@@ -6083,6 +6075,7 @@ function formatSupervisorStatus(status) {
|
|
|
6083
6075
|
lines.push('');
|
|
6084
6076
|
if (workers.length === 0) {
|
|
6085
6077
|
lines.push(' No workers. Add one with: c8ctl nano supervisor add <profile>');
|
|
6078
|
+
lines.push(...formatSupervisorLogsLines(status));
|
|
6086
6079
|
return lines.join('\n');
|
|
6087
6080
|
}
|
|
6088
6081
|
const rows = workers.map((w) => ({
|
|
@@ -6107,6 +6100,7 @@ function formatSupervisorStatus(status) {
|
|
|
6107
6100
|
const fmt = (r) => ' ' + cols.map((c) => r[c].padEnd(width[c])).join(' ');
|
|
6108
6101
|
lines.push(fmt(head));
|
|
6109
6102
|
for (const r of rows) lines.push(fmt(r));
|
|
6103
|
+
lines.push(...formatSupervisorLogsLines(status));
|
|
6110
6104
|
return lines.join('\n');
|
|
6111
6105
|
}
|
|
6112
6106
|
|
|
@@ -8909,9 +8903,6 @@ export {
|
|
|
8909
8903
|
jobTypeMatrix,
|
|
8910
8904
|
diffJobTypes,
|
|
8911
8905
|
parseJobTypeFlags,
|
|
8912
|
-
serviceTaskHasAgentHeader,
|
|
8913
|
-
serviceTaskHasLinkedPrompt,
|
|
8914
|
-
serviceTaskIsAgentTask,
|
|
8915
8906
|
scanAgentTaskLeaves,
|
|
8916
8907
|
readDeployedAgentJobTypes,
|
|
8917
8908
|
resolveAutoJobTypes,
|
|
@@ -8942,6 +8933,7 @@ export {
|
|
|
8942
8933
|
formatDuration,
|
|
8943
8934
|
summarizeSupervisorWorker,
|
|
8944
8935
|
formatSupervisorStatus,
|
|
8936
|
+
formatSupervisorLogsLines,
|
|
8945
8937
|
reageSupervisorStatus,
|
|
8946
8938
|
clampToWidth,
|
|
8947
8939
|
createSupervisorLiveView,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8ctl-plugin-nano",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.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",
|
|
@@ -52,17 +52,17 @@
|
|
|
52
52
|
"semantic-release": "^25.0.3"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@nanobpm/agentic": "^0.
|
|
55
|
+
"@nanobpm/agentic": "^0.4.0",
|
|
56
56
|
"@nanobpm/urban-agent-client": "^0.1.4"
|
|
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.39.0",
|
|
61
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.39.0",
|
|
62
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.39.0",
|
|
63
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.39.0",
|
|
64
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.39.0",
|
|
65
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.39.0",
|
|
66
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.39.0"
|
|
67
67
|
}
|
|
68
68
|
}
|