c8ctl-plugin-nano 1.16.0 → 1.18.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 +21 -0
- package/c8ctl-plugin.js +124 -15
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -150,6 +150,9 @@ c8ctl nano hire
|
|
|
150
150
|
c8ctl nano hire --name reviewer --rank senior --command copilot \
|
|
151
151
|
--model gpt-5 --capabilities code-review,testing
|
|
152
152
|
|
|
153
|
+
# Give the harness command-line switches (e.g. run copilot with --allow-all)
|
|
154
|
+
c8ctl nano hire --name coder --rank senior --command copilot --arg --allow-all
|
|
155
|
+
|
|
153
156
|
# List profiles
|
|
154
157
|
c8ctl nano hire --list
|
|
155
158
|
```
|
|
@@ -327,6 +330,24 @@ envelope layers on top (job-specific tuning wins), and the reserved `AGENT_*`
|
|
|
327
330
|
variables and resolved secrets always win over user-supplied env so they can't be
|
|
328
331
|
shadowed. For **secret** values use `secretRefs`, not `--env`.
|
|
329
332
|
|
|
333
|
+
**Command-line switches.** Some harnesses take switches rather than env vars —
|
|
334
|
+
e.g. `copilot --allow-all`. Append them to the harness command with a repeatable
|
|
335
|
+
`--arg` (each `--arg` is one argv token). They are persisted on the profile at
|
|
336
|
+
hire time and can be extended at work time:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
c8ctl nano hire --name coder --rank senior --command copilot --arg --allow-all
|
|
340
|
+
c8ctl nano work coder --arg --verbose # appends to the profile args
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
The command is spawned through a shell (so `command` still resolves on `PATH`),
|
|
344
|
+
but each `--arg` is shell-quoted as a single literal token, so a value with
|
|
345
|
+
spaces or shell metacharacters can't break out or inject. They apply on the
|
|
346
|
+
container path on any OS, and on the host path on POSIX systems. **On a Windows
|
|
347
|
+
host (`sandbox=none`), `--arg` is rejected** with a clear error — the POSIX
|
|
348
|
+
quoting isn't honoured by `cmd.exe` — so use a container sandbox
|
|
349
|
+
(`--sandbox docker|podman`) or bake the switches into `--command` there.
|
|
350
|
+
|
|
330
351
|
**Disk hygiene.** Host job **workspaces** and container sandboxes both get
|
|
331
352
|
automatic cleanup so leaked artifacts can't fill the disk. Workspaces under
|
|
332
353
|
`<state>/agent-runs` are removed after each job and swept at startup + on
|
package/c8ctl-plugin.js
CHANGED
|
@@ -461,6 +461,19 @@ function webConsoleUrl(baseUrl) {
|
|
|
461
461
|
return `${baseUrl}/console`;
|
|
462
462
|
}
|
|
463
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Human label for the console link, keyed on the runtime console profile.
|
|
466
|
+
* The default `studio` profile IS the full web IDE, so name it as such — users
|
|
467
|
+
* kept missing that Nano ships a browser IDE when it was labelled "Web console".
|
|
468
|
+
* `observe` is the read-only console. `off` serves no console, so the label is
|
|
469
|
+
* meaningless there; callers must guard `profile !== 'off'` before rendering it,
|
|
470
|
+
* and this helper returns null for `off` to enforce that contract.
|
|
471
|
+
*/
|
|
472
|
+
function consoleLinkLabel(profile) {
|
|
473
|
+
if (profile === 'off') return null;
|
|
474
|
+
return profile === 'studio' ? 'Web IDE (Studio)' : `Web console (${profile})`;
|
|
475
|
+
}
|
|
476
|
+
|
|
464
477
|
/** Probe a node's always-on GET /v2/topology endpoint for reachability. */
|
|
465
478
|
async function probeHealthy(url) {
|
|
466
479
|
const controller = new AbortController();
|
|
@@ -788,6 +801,7 @@ async function startCluster(req) {
|
|
|
788
801
|
inMemory,
|
|
789
802
|
historyMax: historyMax ?? null,
|
|
790
803
|
basePort,
|
|
804
|
+
consoleProfile,
|
|
791
805
|
nodes,
|
|
792
806
|
};
|
|
793
807
|
writeState(state);
|
|
@@ -834,15 +848,22 @@ async function printSummary(state) {
|
|
|
834
848
|
// The landing page (and the /docs user guide + /console) only exist in builds
|
|
835
849
|
// compiled with the web console; probe so we advertise the right entry point.
|
|
836
850
|
const hasConsole = await probePath(entry.url, '/');
|
|
837
|
-
|
|
838
|
-
|
|
851
|
+
const profile = state.consoleProfile ?? 'studio';
|
|
852
|
+
// Lead with the web IDE — it is Nano's headline surface and the thing users
|
|
853
|
+
// most often did not realise was there. Only advertise it when this build
|
|
854
|
+
// actually serves a console and the profile is not 'off'.
|
|
855
|
+
if (hasConsole && profile !== 'off') {
|
|
856
|
+
console.log(` ${consoleLinkLabel(profile)} ${webConsoleUrl(entry.url)}`);
|
|
857
|
+
const surface = profile === 'studio' ? 'the Nano web IDE' : 'the Nano web console';
|
|
858
|
+
console.log(` ^ open this in your browser: ${surface}`);
|
|
859
|
+
console.log('');
|
|
839
860
|
}
|
|
840
|
-
console.log(` REST API ${entry.url}/v2`);
|
|
841
|
-
console.log(` Topology ${entry.url}/v2/topology`);
|
|
842
861
|
if (hasConsole) {
|
|
843
|
-
console.log(`
|
|
862
|
+
console.log(` Landing ${entry.url}/ (console, user guide & API docs)`);
|
|
844
863
|
console.log(` User guide ${entry.url}/docs`);
|
|
845
864
|
}
|
|
865
|
+
console.log(` REST API ${entry.url}/v2`);
|
|
866
|
+
console.log(` Topology ${entry.url}/v2/topology`);
|
|
846
867
|
if (state.workspaceDir) {
|
|
847
868
|
console.log(` Workspace ${state.workspaceDir} (models/, workers/)`);
|
|
848
869
|
}
|
|
@@ -1052,6 +1073,21 @@ async function statusCluster(req) {
|
|
|
1052
1073
|
}
|
|
1053
1074
|
console.log('');
|
|
1054
1075
|
|
|
1076
|
+
// Surface the web IDE again here so it stays discoverable long after the
|
|
1077
|
+
// initial `start` scrolled off — probe a healthy node so we only advertise a
|
|
1078
|
+
// console that is actually served (API-only builds 404 `/`).
|
|
1079
|
+
const profile = state.consoleProfile ?? 'studio';
|
|
1080
|
+
if (overall !== 'stopped' && profile !== 'off') {
|
|
1081
|
+
// Only probe a HEALTHY node: it already answered /v2/topology this run, so
|
|
1082
|
+
// GET / returns fast. Falling back to a merely-alive (unreachable) node
|
|
1083
|
+
// would add a full probe timeout to every `nano status` in a degraded state.
|
|
1084
|
+
const consoleNode = checks.find((c) => c.healthy);
|
|
1085
|
+
if (consoleNode && (await probePath(consoleNode.url, '/'))) {
|
|
1086
|
+
console.log(` ${consoleLinkLabel(profile)} ${webConsoleUrl(consoleNode.url)}`);
|
|
1087
|
+
console.log('');
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1055
1091
|
// Enrich with the live topology when reachable — the authoritative view of
|
|
1056
1092
|
// partition leadership across the cluster.
|
|
1057
1093
|
if (topo) {
|
|
@@ -1380,6 +1416,39 @@ function parseEnvPairs(input) {
|
|
|
1380
1416
|
return { env, errors };
|
|
1381
1417
|
}
|
|
1382
1418
|
|
|
1419
|
+
// Normalize a stored/CLI argument list (string | string[]) into a clean string[]:
|
|
1420
|
+
// each entry is one whole argv token (e.g. "--allow-all"), coerced to a string,
|
|
1421
|
+
// with null/undefined and empty tokens dropped. Interior whitespace is preserved
|
|
1422
|
+
// so a single arg may carry a value like "--foo=a b" intact.
|
|
1423
|
+
function normalizeArgList(input) {
|
|
1424
|
+
const list = input == null ? [] : (Array.isArray(input) ? input : [input]);
|
|
1425
|
+
const out = [];
|
|
1426
|
+
for (const item of list) {
|
|
1427
|
+
if (item == null) continue;
|
|
1428
|
+
const s = String(item);
|
|
1429
|
+
if (s.length === 0) continue;
|
|
1430
|
+
out.push(s);
|
|
1431
|
+
}
|
|
1432
|
+
return out;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// POSIX single-quote a string so it survives `sh -c`/shell:true as one literal
|
|
1436
|
+
// argv token, no matter what it contains (spaces, $, quotes, globs). Empty
|
|
1437
|
+
// string → ''. This is what keeps structured `--arg` values injection-safe even
|
|
1438
|
+
// though the harness is spawned through a shell (for PATH resolution).
|
|
1439
|
+
function shQuote(s) {
|
|
1440
|
+
return `'${String(s).replace(/'/g, `'\\''`)}'`;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// Build the shell command line for the agent harness: the base command followed
|
|
1444
|
+
// by each structured argument, shell-quoted. With no args the command is used
|
|
1445
|
+
// verbatim (preserving pre-existing hires that baked switches into the command).
|
|
1446
|
+
function buildAgentCommandLine(command, args) {
|
|
1447
|
+
const list = normalizeArgList(args);
|
|
1448
|
+
if (list.length === 0) return command;
|
|
1449
|
+
return `${command} ${list.map(shQuote).join(' ')}`;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1383
1452
|
// A worker job-type token: rank/capability tokens use `:` (rank↔cap) and `+`
|
|
1384
1453
|
// (combined caps) as delimiters, and code-first `@nanobpm/workflow` job types
|
|
1385
1454
|
// are `<flowId>:<taskName>` or an explicit override. The first character must be
|
|
@@ -1477,6 +1546,7 @@ function normalizeStoredProfile(name, profile) {
|
|
|
1477
1546
|
name,
|
|
1478
1547
|
rank,
|
|
1479
1548
|
command,
|
|
1549
|
+
args: normalizeArgList(profile.args),
|
|
1480
1550
|
model: typeof profile.model === 'string' ? profile.model.trim() : '',
|
|
1481
1551
|
capabilities: normalizeCapabilities(profile.capabilities),
|
|
1482
1552
|
sandbox,
|
|
@@ -1505,7 +1575,7 @@ async function hireWorker(req, flags) {
|
|
|
1505
1575
|
logger.info('Hired agent profiles:');
|
|
1506
1576
|
for (const name of names.sort()) {
|
|
1507
1577
|
const p = hires[name];
|
|
1508
|
-
logger.info(` ${name} [${p.rank}] ${p.command} (model: ${p.model || '-'}; caps: ${normalizeCapabilities(p.capabilities).join(', ') || '-'})`);
|
|
1578
|
+
logger.info(` ${name} [${p.rank}] ${buildAgentCommandLine(p.command, p.args)} (model: ${p.model || '-'}; caps: ${normalizeCapabilities(p.capabilities).join(', ') || '-'})`);
|
|
1509
1579
|
}
|
|
1510
1580
|
logger.info('');
|
|
1511
1581
|
logger.info('Put one to work with: c8ctl nano work <name>');
|
|
@@ -1521,11 +1591,14 @@ async function hireWorker(req, flags) {
|
|
|
1521
1591
|
let capabilities = flags?.capabilities !== undefined ? flags.capabilities : undefined;
|
|
1522
1592
|
let sandbox = flags?.sandbox !== undefined ? String(flags.sandbox).trim().toLowerCase() : undefined;
|
|
1523
1593
|
let image = flags?.image !== undefined ? String(flags.image).trim() : undefined;
|
|
1594
|
+
// Structured command-line switches appended to the command when spawned, e.g.
|
|
1595
|
+
// `--arg --allow-all` for `copilot`. Repeatable; each --arg is one argv token.
|
|
1596
|
+
const commandArgs = normalizeArgList(flags?.arg);
|
|
1524
1597
|
const envFromFlags = flags?.env !== undefined;
|
|
1525
1598
|
const { env: profileEnv, errors: envErrors } = parseEnvPairs(flags?.env);
|
|
1526
1599
|
if (envErrors.length > 0) {
|
|
1527
1600
|
logger.error(envErrors.join('; '));
|
|
1528
|
-
logger.info('Example: c8ctl nano hire --name coder --rank senior --command copilot --
|
|
1601
|
+
logger.info('Example: c8ctl nano hire --name coder --rank senior --command copilot --arg --allow-all --env COPILOT_ENABLE_ALL_TOOLS=1');
|
|
1529
1602
|
process.exit(1);
|
|
1530
1603
|
}
|
|
1531
1604
|
|
|
@@ -1623,6 +1696,7 @@ async function hireWorker(req, flags) {
|
|
|
1623
1696
|
name,
|
|
1624
1697
|
rank,
|
|
1625
1698
|
command,
|
|
1699
|
+
args: commandArgs,
|
|
1626
1700
|
model: model || '',
|
|
1627
1701
|
capabilities: normalizeCapabilities(capabilities),
|
|
1628
1702
|
sandbox,
|
|
@@ -1633,9 +1707,10 @@ async function hireWorker(req, flags) {
|
|
|
1633
1707
|
writeHire(profile);
|
|
1634
1708
|
|
|
1635
1709
|
const matrix = jobTypeMatrix(profile.rank, profile.capabilities);
|
|
1636
|
-
logger.info(`${existed ? 'Updated' : 'Hired'} "${name}" [${profile.rank}] → ${profile.command}`);
|
|
1710
|
+
logger.info(`${existed ? 'Updated' : 'Hired'} "${name}" [${profile.rank}] → ${buildAgentCommandLine(profile.command, profile.args)}`);
|
|
1637
1711
|
logger.info(` model: ${profile.model || '(none)'}`);
|
|
1638
1712
|
logger.info(` capabilities: ${profile.capabilities.join(', ') || '(none)'}`);
|
|
1713
|
+
if (profile.args.length > 0) logger.info(` args: ${profile.args.map(shQuote).join(' ')}`);
|
|
1639
1714
|
logger.info(` sandbox: ${profile.sandbox}${CONTAINER_SANDBOXES.has(profile.sandbox) ? ` (image ${profile.image})` : ''}`);
|
|
1640
1715
|
const envKeys = Object.keys(profile.env);
|
|
1641
1716
|
if (envKeys.length > 0) logger.info(` env: ${envKeys.join(', ')}`);
|
|
@@ -2414,9 +2489,13 @@ function baseAgentEnv(profile, job) {
|
|
|
2414
2489
|
* Both paths resolve to the same result contract.
|
|
2415
2490
|
*/
|
|
2416
2491
|
function runAgentJob(profile, job, opts = {}) {
|
|
2417
|
-
const { timeoutMs, envelope, sandbox = 'none', image, runId, secretEnv = {}, passThroughSecretNames = [], cwd, extraEnv = {}, profileEnv = {}, resultFile, stream = false, streamPrefix = '', onStreamOut, onStreamErr } = opts;
|
|
2492
|
+
const { timeoutMs, envelope, sandbox = 'none', image, runId, secretEnv = {}, passThroughSecretNames = [], cwd, extraEnv = {}, profileEnv = {}, resultFile, stream = false, streamPrefix = '', onStreamOut, onStreamErr, args: commandArgs } = opts;
|
|
2418
2493
|
const payload = JSON.stringify(buildAgentPayload(profile, job, envelope));
|
|
2419
2494
|
const agentEnv = baseAgentEnv(profile, job);
|
|
2495
|
+
// The harness command line: the profile command plus its structured switches
|
|
2496
|
+
// (persisted `--arg`s, possibly extended at work time via opts.args), each
|
|
2497
|
+
// shell-quoted. Spawned through a shell so `command` still resolves on PATH.
|
|
2498
|
+
const commandLine = buildAgentCommandLine(profile.command, commandArgs ?? profile.args);
|
|
2420
2499
|
// Static, non-secret env for the harness: the worker/profile's env (e.g. a
|
|
2421
2500
|
// harness's permission toggles) plus the per-job envelope's setup.env
|
|
2422
2501
|
// (job-specific tuning wins over the profile default). Reserved AGENT_* and
|
|
@@ -2425,9 +2504,16 @@ function runAgentJob(profile, job, opts = {}) {
|
|
|
2425
2504
|
|
|
2426
2505
|
if (!CONTAINER_SANDBOXES.has(sandbox)) {
|
|
2427
2506
|
// Host: hand the agent the result file by its real path.
|
|
2507
|
+
// Defense in depth: --arg tokens are POSIX single-quoted, which cmd.exe on
|
|
2508
|
+
// a Windows host does not honour, so args would be mis-parsed under the
|
|
2509
|
+
// shell:true spawn. workAgent already rejects this at startup, but guard the
|
|
2510
|
+
// spawn site too so the invariant holds for any direct caller of runAgentJob.
|
|
2511
|
+
if (commandLine !== profile.command && process.platform === 'win32') {
|
|
2512
|
+
return Promise.resolve({ ok: false, exitCode: null, stdout: '', stderr: '', error: 'command-line args (--arg) are not supported for host execution on Windows; use a container sandbox or bake switches into the command', truncated: false, stderrTruncated: false });
|
|
2513
|
+
}
|
|
2428
2514
|
const resultEnv = resultFile ? { [AGENT_RESULT_FILE_ENV]: resultFile } : {};
|
|
2429
2515
|
return spawnCaptureOneShot({
|
|
2430
|
-
command:
|
|
2516
|
+
command: commandLine,
|
|
2431
2517
|
shell: true,
|
|
2432
2518
|
// Own process group so the timeout handler can kill the whole tree.
|
|
2433
2519
|
detached: process.platform !== 'win32',
|
|
@@ -2480,7 +2566,7 @@ function runAgentJob(profile, job, opts = {}) {
|
|
|
2480
2566
|
...mountArgs,
|
|
2481
2567
|
...envArgs,
|
|
2482
2568
|
image,
|
|
2483
|
-
'sh', '-c',
|
|
2569
|
+
'sh', '-c', commandLine,
|
|
2484
2570
|
];
|
|
2485
2571
|
|
|
2486
2572
|
return spawnCaptureOneShot({
|
|
@@ -2582,6 +2668,11 @@ async function workAgent(req, flags) {
|
|
|
2582
2668
|
}
|
|
2583
2669
|
const profileEnv = { ...profile.env, ...workEnv };
|
|
2584
2670
|
|
|
2671
|
+
// Structured command-line switches: the profile's persisted `--arg`s, extended
|
|
2672
|
+
// by any work-time `--arg` (appended). Lets an operator add switches (e.g.
|
|
2673
|
+
// `--allow-all`) at dispatch time without re-hiring.
|
|
2674
|
+
const effectiveArgs = [...profile.args, ...normalizeArgList(flags?.arg)];
|
|
2675
|
+
|
|
2585
2676
|
const intFlag = (v, dflt) => {
|
|
2586
2677
|
const n = Number.parseInt(String(v ?? ''), 10);
|
|
2587
2678
|
return Number.isFinite(n) && n > 0 ? n : dflt;
|
|
@@ -2602,6 +2693,17 @@ async function workAgent(req, flags) {
|
|
|
2602
2693
|
logger.error(`--sandbox ${sandbox} requires an --image (or hire the profile with --image).`);
|
|
2603
2694
|
process.exit(1);
|
|
2604
2695
|
}
|
|
2696
|
+
// Structured --arg tokens are POSIX single-quoted (shQuote) for the harness
|
|
2697
|
+
// shell. On the host path that shell is the platform default — cmd.exe on
|
|
2698
|
+
// Windows, which does not honour single quotes — so the quoting would leak
|
|
2699
|
+
// literal quote characters and mis-parse the switches. The container path
|
|
2700
|
+
// always targets the image's `sh`, so it stays correct regardless of host OS.
|
|
2701
|
+
// Fail fast with actionable guidance rather than silently corrupting argv.
|
|
2702
|
+
if (!isContainer && effectiveArgs.length > 0 && process.platform === 'win32') {
|
|
2703
|
+
logger.error('--arg is not supported for host execution on Windows (cmd.exe does not honour POSIX quoting).');
|
|
2704
|
+
logger.error('Use a container sandbox (--sandbox docker|podman --image <ref>) or bake the switches into --command.');
|
|
2705
|
+
process.exit(1);
|
|
2706
|
+
}
|
|
2605
2707
|
|
|
2606
2708
|
const secretResolver = makeSecretResolver(flags?.['secret-resolver']);
|
|
2607
2709
|
if (!secretResolver) {
|
|
@@ -2677,7 +2779,7 @@ async function workAgent(req, flags) {
|
|
|
2677
2779
|
const jobTypes = [...new Set([...matrix, ...extraJobTypes])];
|
|
2678
2780
|
const camunda = globalThis.c8ctl.createClient();
|
|
2679
2781
|
|
|
2680
|
-
logger.info(`Putting "${name}" [${profile.rank}] to work → ${profile.command}`);
|
|
2782
|
+
logger.info(`Putting "${name}" [${profile.rank}] to work → ${buildAgentCommandLine(profile.command, effectiveArgs)}`);
|
|
2681
2783
|
logger.info(` model: ${profile.model || '(none)'}; capabilities: ${profile.capabilities.join(', ') || '(none)'}`);
|
|
2682
2784
|
logger.info(` sandbox: ${sandbox}${isContainer ? ` (image ${image})` : ''}`);
|
|
2683
2785
|
const profileEnvKeys = Object.keys(profileEnv);
|
|
@@ -2694,7 +2796,7 @@ async function workAgent(req, flags) {
|
|
|
2694
2796
|
maxParallelJobs,
|
|
2695
2797
|
jobTimeoutMs,
|
|
2696
2798
|
jobHandler: async (job) => {
|
|
2697
|
-
logger.info(`[${jobType}] job ${job.jobKey} (instance ${job.processInstanceKey ?? '-'}) → ${profile.command}`);
|
|
2799
|
+
logger.info(`[${jobType}] job ${job.jobKey} (instance ${job.processInstanceKey ?? '-'}) → ${buildAgentCommandLine(profile.command, effectiveArgs)}`);
|
|
2698
2800
|
|
|
2699
2801
|
// Disk-budget admission shed: if the engine data root is below the free
|
|
2700
2802
|
// floor, don't start a container — fail (retryable) so work sheds until
|
|
@@ -2788,6 +2890,7 @@ async function workAgent(req, flags) {
|
|
|
2788
2890
|
resultFile,
|
|
2789
2891
|
stream,
|
|
2790
2892
|
streamPrefix: `[${jobType} ${job.jobKey}] `,
|
|
2893
|
+
args: effectiveArgs,
|
|
2791
2894
|
// Route the --stream tee through c8ctl's output-mode-aware logger so
|
|
2792
2895
|
// spying never corrupts a structured/JSON output mode.
|
|
2793
2896
|
onStreamOut: stream ? (line) => logger.info(line) : undefined,
|
|
@@ -4217,6 +4320,7 @@ export { resolveBinary, findBinary, launcherEnvMarkers };
|
|
|
4217
4320
|
export { buildNpmInvocation };
|
|
4218
4321
|
export {
|
|
4219
4322
|
webConsoleUrl,
|
|
4323
|
+
consoleLinkLabel,
|
|
4220
4324
|
hireWorker,
|
|
4221
4325
|
};
|
|
4222
4326
|
export {
|
|
@@ -4236,6 +4340,9 @@ export {
|
|
|
4236
4340
|
sanitizeResultVars,
|
|
4237
4341
|
parseEnvPairs,
|
|
4238
4342
|
normalizeEnvMap,
|
|
4343
|
+
normalizeArgList,
|
|
4344
|
+
shQuote,
|
|
4345
|
+
buildAgentCommandLine,
|
|
4239
4346
|
reapAgentContainers,
|
|
4240
4347
|
diskBudgetOk,
|
|
4241
4348
|
containerEngineAvailable,
|
|
@@ -4290,6 +4397,7 @@ export const metadata = {
|
|
|
4290
4397
|
{ command: 'c8ctl nano update --check', description: 'Check whether a newer nano release is available' },
|
|
4291
4398
|
{ command: 'c8ctl nano hire', description: 'Interactively create a CLI agent worker profile (name, rank, command, model, capabilities)' },
|
|
4292
4399
|
{ command: 'c8ctl nano hire --name reviewer --rank senior --command copilot --model gpt-5 --capabilities code-review,testing', description: 'Create a profile non-interactively' },
|
|
4400
|
+
{ command: 'c8ctl nano hire --name coder --rank senior --command copilot --arg --allow-all', description: 'Hire copilot with a command-line switch (copilot --allow-all)' },
|
|
4293
4401
|
{ command: 'c8ctl nano hire --name coder --rank senior --command copilot --env COPILOT_ENABLE_ALL_TOOLS=1', description: 'Persist a harness startup env var (e.g. permissions) on the profile' },
|
|
4294
4402
|
{ command: 'c8ctl nano hire --list', description: 'List hired agent profiles' },
|
|
4295
4403
|
{ command: 'c8ctl nano hire --name coder --rank senior --command "agent-harness" --sandbox docker --image ghcr.io/acme/agent:1', description: 'Create a profile that runs each job in a throwaway Docker container' },
|
|
@@ -4338,6 +4446,7 @@ export const commands = {
|
|
|
4338
4446
|
name: { type: 'string', description: 'hire/work: agent profile name (alt to positional arg)' },
|
|
4339
4447
|
rank: { type: 'string', description: 'hire: agent rank (principal|senior|junior|decider)' },
|
|
4340
4448
|
command: { type: 'string', description: 'hire: CLI command that runs the agent harness (e.g. copilot, claude, pi)' },
|
|
4449
|
+
arg: { type: 'string', multiple: true, description: 'hire/work: command-line switch/arg appended to the harness command (repeatable), e.g. --arg --allow-all. Persisted on hire; work appends more.' },
|
|
4341
4450
|
model: { type: 'string', description: 'hire: model name passed to the harness (AGENT_MODEL)' },
|
|
4342
4451
|
capabilities: { type: 'string', description: 'hire: comma-separated capability list' },
|
|
4343
4452
|
sandbox: { type: 'string', description: 'hire/work: execution sandbox none|docker|podman (default none). Containers isolate each job.' },
|
|
@@ -4496,8 +4605,8 @@ function printUsage() {
|
|
|
4496
4605
|
console.log(' c8ctl nano set <bin|model-dir> <path>');
|
|
4497
4606
|
console.log(' c8ctl nano config');
|
|
4498
4607
|
console.log(' c8ctl nano update [--check]');
|
|
4499
|
-
console.log(' c8ctl nano hire [--name <n>] [--rank <r>] [--command <c>] [--model <m>] [--capabilities <a,b>] [--sandbox none|docker|podman] [--image <ref>] [--env NAME=VALUE ...] [--list]');
|
|
4500
|
-
console.log(' c8ctl nano work <profileName> [--max-parallel <n>] [--job-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]');
|
|
4608
|
+
console.log(' c8ctl nano hire [--name <n>] [--rank <r>] [--command <c>] [--arg <switch> ...] [--model <m>] [--capabilities <a,b>] [--sandbox none|docker|podman] [--image <ref>] [--env NAME=VALUE ...] [--list]');
|
|
4609
|
+
console.log(' c8ctl nano work <profileName> [--arg <switch> ...] [--max-parallel <n>] [--job-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]');
|
|
4501
4610
|
console.log('');
|
|
4502
4611
|
console.log('Subcommands:');
|
|
4503
4612
|
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.18.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",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
"semantic-release": "^25.0.3"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.
|
|
53
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.
|
|
54
|
-
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.
|
|
55
|
-
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.
|
|
56
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.
|
|
57
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.
|
|
58
|
-
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.
|
|
52
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.18.0",
|
|
53
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.18.0",
|
|
54
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.18.0",
|
|
55
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.18.0",
|
|
56
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.18.0",
|
|
57
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.18.0",
|
|
58
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.18.0"
|
|
59
59
|
}
|
|
60
60
|
}
|