c8ctl-plugin-nano 1.13.1 → 1.14.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.
Files changed (2) hide show
  1. package/c8ctl-plugin.js +57 -1
  2. package/package.json +8 -8
package/c8ctl-plugin.js CHANGED
@@ -120,6 +120,15 @@ const PROCESSOS_STATE_FILE = 'processos.json';
120
120
  const PROCESSOS_DEFAULT_PORT = 8090;
121
121
  const DEFAULT_NANO_URL = 'http://localhost:8080';
122
122
 
123
+ // Guided-journey deep links (ADR 0049 §2). The console renders a first-run tour
124
+ // chosen by `…/console?tour=<journeyId>`, and the *command the user ran* already
125
+ // encodes the persona — so the CLI bakes the journey into the console URL it
126
+ // prints rather than making the console guess. These ids are a contract with the
127
+ // console (`console/src/lib/tour`): an unknown id is silently ignored, so they
128
+ // are asserted verbatim in the tests.
129
+ const JOURNEY_LOCALDEV = 'localdev';
130
+ const JOURNEY_AGENTIC_AUTHOR = 'agentic-author';
131
+
123
132
  // Passive update notifier (npm-style): refresh the latest published version
124
133
  // from the registry in a detached background process at most once per day, and
125
134
  // surface a one-line "update available" notice at most once per day. Never
@@ -449,6 +458,30 @@ function liveNodeCount(state) {
449
458
  return state.nodes.filter((n) => isPidAlive(n.pid)).length;
450
459
  }
451
460
 
461
+ /**
462
+ * Compose a web-console URL for a node's base URL, optionally carrying a guided
463
+ * journey (`?tour=<id>`, ADR 0049 §2). Never hardcodes a port: `baseUrl` is the
464
+ * real address the node came up on.
465
+ */
466
+ function webConsoleUrl(baseUrl, journey) {
467
+ const base = `${baseUrl}/console`;
468
+ return journey ? `${base}?tour=${encodeURIComponent(journey)}` : base;
469
+ }
470
+
471
+ /**
472
+ * Base URL of the running cluster the user would actually open: the first
473
+ * still-alive node, falling back to the first recorded node, then to the default
474
+ * gateway URL when no cluster has been started. Used by hire/work, which surface
475
+ * a console link for the agentic-SDLC journey.
476
+ */
477
+ function runningConsoleBaseUrl(state = readState()) {
478
+ if (state && Array.isArray(state.nodes) && state.nodes.length > 0) {
479
+ const alive = state.nodes.find((n) => isPidAlive(n.pid));
480
+ return (alive || state.nodes[0]).url;
481
+ }
482
+ return DEFAULT_NANO_URL;
483
+ }
484
+
452
485
  /** Probe a node's always-on GET /v2/topology endpoint for reachability. */
453
486
  async function probeHealthy(url) {
454
487
  const controller = new AbortController();
@@ -828,7 +861,7 @@ async function printSummary(state) {
828
861
  console.log(` REST API ${entry.url}/v2`);
829
862
  console.log(` Topology ${entry.url}/v2/topology`);
830
863
  if (hasConsole) {
831
- console.log(` Web console ${entry.url}/console`);
864
+ console.log(` Web console ${webConsoleUrl(entry.url, JOURNEY_LOCALDEV)}`);
832
865
  console.log(` User guide ${entry.url}/docs`);
833
866
  }
834
867
  if (state.workspaceDir) {
@@ -1119,6 +1152,20 @@ function logsCluster(req) {
1119
1152
 
1120
1153
  function controlNode(req, { signal, verb, paused }) {
1121
1154
  const logger = getLogger();
1155
+
1156
+ // pause/resume rely on SIGSTOP/SIGCONT, which are POSIX-only. On Windows
1157
+ // Node throws "Unknown signal: SIGSTOP" from process.kill, so fail fast with
1158
+ // a clear message instead of a raw crash (see nano-bpm#390).
1159
+ if (process.platform === 'win32') {
1160
+ logger.error(
1161
+ `"c8ctl nano ${verb}" isn't supported on Windows yet — it relies on ` +
1162
+ `SIGSTOP/SIGCONT, which Windows doesn't have. To simulate a node ` +
1163
+ `failing and recovering, use "c8ctl nano stop <id>" then ` +
1164
+ `"c8ctl nano start" instead.`,
1165
+ );
1166
+ process.exit(1);
1167
+ }
1168
+
1122
1169
  const state = readState();
1123
1170
 
1124
1171
  if (!state || !Array.isArray(state.nodes) || state.nodes.length === 0) {
@@ -1586,6 +1633,7 @@ async function hireWorker(req, flags) {
1586
1633
  if (envKeys.length > 0) logger.info(` env: ${envKeys.join(', ')}`);
1587
1634
  logger.info(` job types (${matrix.length}): ${matrix.join(' ')}`);
1588
1635
  logger.info(`Put it to work with: c8ctl nano work ${name}`);
1636
+ logger.info(`Open the console: ${webConsoleUrl(runningConsoleBaseUrl(), JOURNEY_AGENTIC_AUTHOR)}`);
1589
1637
  }
1590
1638
 
1591
1639
  /**
@@ -2619,6 +2667,7 @@ async function workAgent(req, flags) {
2619
2667
  if (profileEnvKeys.length > 0) logger.info(` harness env: ${profileEnvKeys.join(', ')}`);
2620
2668
  logger.info(` listening on ${matrix.length} job type(s): ${matrix.join(' ')}`);
2621
2669
  logger.info(` max parallel: ${maxParallelJobs}; job timeout: ${jobTimeoutMs}ms`);
2670
+ logger.info(` console: ${webConsoleUrl(runningConsoleBaseUrl(), JOURNEY_AGENTIC_AUTHOR)}`);
2622
2671
  logger.info('Polling for work — press Ctrl-C to stop.');
2623
2672
 
2624
2673
  const workers = matrix.map((jobType) =>
@@ -4149,6 +4198,13 @@ function parseProcessosRequest(args, flags) {
4149
4198
  // `metadata` and `commands`; these named exports are inert to it.
4150
4199
  export { resolveBinary, findBinary, launcherEnvMarkers };
4151
4200
  export { buildNpmInvocation };
4201
+ export {
4202
+ webConsoleUrl,
4203
+ runningConsoleBaseUrl,
4204
+ hireWorker,
4205
+ JOURNEY_LOCALDEV,
4206
+ JOURNEY_AGENTIC_AUTHOR,
4207
+ };
4152
4208
  export {
4153
4209
  normalizeTaskEnvelope,
4154
4210
  collectEnvelopeFrom,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c8ctl-plugin-nano",
3
- "version": "1.13.1",
3
+ "version": "1.14.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.13.1",
53
- "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.13.1",
54
- "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.13.1",
55
- "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.13.1",
56
- "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.13.1",
57
- "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.13.1",
58
- "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.13.1"
52
+ "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.14.0",
53
+ "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.14.0",
54
+ "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.14.0",
55
+ "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.14.0",
56
+ "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.14.0",
57
+ "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.14.0",
58
+ "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.14.0"
59
59
  }
60
60
  }