c8ctl-plugin-nano 1.15.0 → 1.17.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 +46 -35
  2. package/package.json +8 -8
package/c8ctl-plugin.js CHANGED
@@ -120,15 +120,6 @@ 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
-
132
123
  // Passive update notifier (npm-style): refresh the latest published version
133
124
  // from the registry in a detached background process at most once per day, and
134
125
  // surface a one-line "update available" notice at most once per day. Never
@@ -459,27 +450,28 @@ function liveNodeCount(state) {
459
450
  }
460
451
 
461
452
  /**
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.
453
+ * Compose a web-console URL for a node's base URL. Never hardcodes a port:
454
+ * `baseUrl` is the real address the node came up on.
455
+ *
456
+ * No longer carries a `?tour=<id>` deep link: onboarding is chosen in the
457
+ * console's own startup persona panel (nano-bpm #464), not sprayed across every
458
+ * command's output.
465
459
  */
466
- function webConsoleUrl(baseUrl, journey) {
467
- const base = `${baseUrl}/console`;
468
- return journey ? `${base}?tour=${encodeURIComponent(journey)}` : base;
460
+ function webConsoleUrl(baseUrl) {
461
+ return `${baseUrl}/console`;
469
462
  }
470
463
 
471
464
  /**
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.
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.
476
471
  */
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;
472
+ function consoleLinkLabel(profile) {
473
+ if (profile === 'off') return null;
474
+ return profile === 'studio' ? 'Web IDE (Studio)' : `Web console (${profile})`;
483
475
  }
484
476
 
485
477
  /** Probe a node's always-on GET /v2/topology endpoint for reachability. */
@@ -809,6 +801,7 @@ async function startCluster(req) {
809
801
  inMemory,
810
802
  historyMax: historyMax ?? null,
811
803
  basePort,
804
+ consoleProfile,
812
805
  nodes,
813
806
  };
814
807
  writeState(state);
@@ -855,15 +848,22 @@ async function printSummary(state) {
855
848
  // The landing page (and the /docs user guide + /console) only exist in builds
856
849
  // compiled with the web console; probe so we advertise the right entry point.
857
850
  const hasConsole = await probePath(entry.url, '/');
858
- if (hasConsole) {
859
- console.log(` Start here ${entry.url}/ (landing: console, user guide & API docs)`);
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('');
860
860
  }
861
- console.log(` REST API ${entry.url}/v2`);
862
- console.log(` Topology ${entry.url}/v2/topology`);
863
861
  if (hasConsole) {
864
- console.log(` Web console ${webConsoleUrl(entry.url, JOURNEY_LOCALDEV)}`);
862
+ console.log(` Landing ${entry.url}/ (console, user guide & API docs)`);
865
863
  console.log(` User guide ${entry.url}/docs`);
866
864
  }
865
+ console.log(` REST API ${entry.url}/v2`);
866
+ console.log(` Topology ${entry.url}/v2/topology`);
867
867
  if (state.workspaceDir) {
868
868
  console.log(` Workspace ${state.workspaceDir} (models/, workers/)`);
869
869
  }
@@ -1073,6 +1073,21 @@ async function statusCluster(req) {
1073
1073
  }
1074
1074
  console.log('');
1075
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
+
1076
1091
  // Enrich with the live topology when reachable — the authoritative view of
1077
1092
  // partition leadership across the cluster.
1078
1093
  if (topo) {
@@ -1662,7 +1677,6 @@ async function hireWorker(req, flags) {
1662
1677
  if (envKeys.length > 0) logger.info(` env: ${envKeys.join(', ')}`);
1663
1678
  logger.info(` job types (${matrix.length}): ${matrix.join(' ')}`);
1664
1679
  logger.info(`Put it to work with: c8ctl nano work ${name}`);
1665
- logger.info(`Open the console: ${webConsoleUrl(runningConsoleBaseUrl(), JOURNEY_AGENTIC_AUTHOR)}`);
1666
1680
  }
1667
1681
 
1668
1682
  /**
@@ -2707,7 +2721,6 @@ async function workAgent(req, flags) {
2707
2721
  const extraNote = extraJobTypes.length > 0 ? ` (${extraJobTypes.length} via --job-type)` : '';
2708
2722
  logger.info(` listening on ${jobTypes.length} job type(s)${extraNote}: ${jobTypes.join(' ')}`);
2709
2723
  logger.info(` max parallel: ${maxParallelJobs}; job timeout: ${jobTimeoutMs}ms`);
2710
- logger.info(` console: ${webConsoleUrl(runningConsoleBaseUrl(), JOURNEY_AGENTIC_AUTHOR)}`);
2711
2724
  logger.info('Polling for work — press Ctrl-C to stop.');
2712
2725
 
2713
2726
  const workers = jobTypes.map((jobType) =>
@@ -4240,10 +4253,8 @@ export { resolveBinary, findBinary, launcherEnvMarkers };
4240
4253
  export { buildNpmInvocation };
4241
4254
  export {
4242
4255
  webConsoleUrl,
4243
- runningConsoleBaseUrl,
4256
+ consoleLinkLabel,
4244
4257
  hireWorker,
4245
- JOURNEY_LOCALDEV,
4246
- JOURNEY_AGENTIC_AUTHOR,
4247
4258
  };
4248
4259
  export {
4249
4260
  normalizeTaskEnvelope,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c8ctl-plugin-nano",
3
- "version": "1.15.0",
3
+ "version": "1.17.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.15.0",
53
- "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.15.0",
54
- "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.15.0",
55
- "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.15.0",
56
- "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.15.0",
57
- "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.15.0",
58
- "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.15.0"
52
+ "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.17.0",
53
+ "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.17.0",
54
+ "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.17.0",
55
+ "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.17.0",
56
+ "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.17.0",
57
+ "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.17.0",
58
+ "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.17.0"
59
59
  }
60
60
  }