c8ctl-plugin-nano 1.1.0 → 1.3.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 +55 -7
- package/c8ctl-plugin.js +527 -25
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -145,6 +145,22 @@ cluster (`c8ctl nano restart`) so it picks up the new binary.
|
|
|
145
145
|
If the plugin is running from a local checkout rather than a global npm install,
|
|
146
146
|
`update` prints the manual command instead of reinstalling in place.
|
|
147
147
|
|
|
148
|
+
### Automatic "update available" notice
|
|
149
|
+
|
|
150
|
+
You don't have to remember to run `update --check`: any `nano` or `processos`
|
|
151
|
+
command also surfaces a one-line notice when a newer release is published. It is
|
|
152
|
+
deliberately unobtrusive:
|
|
153
|
+
|
|
154
|
+
- The registry lookup runs in a **detached background process**, so a command is
|
|
155
|
+
never slowed down — the fresh result is used on the next invocation.
|
|
156
|
+
- npm is queried at most **once per day**, and the notice is shown at most **once
|
|
157
|
+
per day** (state is cached under the plugin's state home in `update-check.json`).
|
|
158
|
+
- The notice prints to **stderr**, so it never corrupts machine-readable stdout,
|
|
159
|
+
and is suppressed when stdout is not a TTY (piped/scripted) or when `CI` is set.
|
|
160
|
+
|
|
161
|
+
To turn it off entirely, set `NANO_NO_UPDATE_NOTIFIER=1` (or the conventional
|
|
162
|
+
`NO_UPDATE_NOTIFIER=1`). The explicit `c8ctl nano update` command is unaffected.
|
|
163
|
+
|
|
148
164
|
## Checking status
|
|
149
165
|
|
|
150
166
|
`c8ctl nano status` queries each node's always-on `GET /v2/topology`, which is the
|
|
@@ -354,14 +370,31 @@ ProcessOS is the optimization-plane server that analyses a running Nano BPM
|
|
|
354
370
|
engine. The plugin can manage a single local ProcessOS instance with the same
|
|
355
371
|
start/stop/status/logs lifecycle as `nano`.
|
|
356
372
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
> **ProcessOS is a closed alpha.** The operational commands (`start`, `stop`,
|
|
374
|
+
> `status`, `logs`, `restart`) stay locked with a *"not available yet"* notice
|
|
375
|
+
> until you opt in. Only `set` and `config` work before then. Opt in either by
|
|
376
|
+
> setting the download URL you were given by the Nano BPM team, or by pointing
|
|
377
|
+
> the plugin at a binary you already have.
|
|
360
378
|
|
|
361
379
|
```bash
|
|
362
|
-
#
|
|
380
|
+
# Closed-alpha channel: persist the download URL, then start
|
|
381
|
+
c8ctl processos set download-url <url you were given>
|
|
382
|
+
c8ctl processos start # fetches processos-<os>-<arch> on first run
|
|
383
|
+
|
|
384
|
+
# …or point the plugin at a binary you already have
|
|
363
385
|
c8ctl processos set bin ~/Downloads/processos
|
|
386
|
+
c8ctl processos start
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
The download URL is the prefix the release binaries live under (e.g. the
|
|
390
|
+
`…/processos/latest/` bucket URL). Persist it with `c8ctl processos set
|
|
391
|
+
download-url <url>`, or set `PROCESSOS_DOWNLOAD_URL` in your environment (the env
|
|
392
|
+
var wins when both are present). The plugin appends the per-platform asset name
|
|
393
|
+
(`processos-darwin-arm64`, `processos-linux-x64`, `processos-win32-x64.exe`, …),
|
|
394
|
+
downloads it to `<stateHome>/bin/`, marks it executable, and runs it. The cached
|
|
395
|
+
download is reused on subsequent starts.
|
|
364
396
|
|
|
397
|
+
```bash
|
|
365
398
|
# Start ProcessOS against the local Nano BPM engine (http://localhost:8080)
|
|
366
399
|
c8ctl processos start
|
|
367
400
|
|
|
@@ -374,6 +407,18 @@ c8ctl processos logs --follow
|
|
|
374
407
|
c8ctl processos stop
|
|
375
408
|
```
|
|
376
409
|
|
|
410
|
+
### Automatic update notice
|
|
411
|
+
|
|
412
|
+
When you're on the closed-alpha channel (download URL configured), the
|
|
413
|
+
plugin checks for newer ProcessOS builds in the background and prints a short
|
|
414
|
+
one-line notice (at most **once per day**) when the published version is newer
|
|
415
|
+
than the one you're running. It compares your installed binary's version against
|
|
416
|
+
the `version.json` the release pipeline publishes next to the binaries, never
|
|
417
|
+
blocks the command (the check runs detached), and is suppressed on
|
|
418
|
+
non-interactive shells, in CI, and when `NO_UPDATE_NOTIFIER` /
|
|
419
|
+
`NANO_NO_UPDATE_NOTIFIER` is set. To update, stop and start ProcessOS again — a
|
|
420
|
+
downloaded binary re-fetches the latest build; a `set bin` binary updates itself.
|
|
421
|
+
|
|
377
422
|
On a successful `start` the summary leads with the landing page:
|
|
378
423
|
|
|
379
424
|
```
|
|
@@ -414,6 +459,7 @@ Settings persist under a `processos` key in the same `config.json` as `nano`.
|
|
|
414
459
|
|
|
415
460
|
```bash
|
|
416
461
|
c8ctl processos set bin <path> # path to the downloaded ProcessOS binary
|
|
462
|
+
c8ctl processos set download-url <url> # closed-alpha binary download URL (enables ProcessOS)
|
|
417
463
|
c8ctl processos set port <n> # listen port (default 8090)
|
|
418
464
|
c8ctl processos set nano-url <url> # target Nano BPM engine (default http://localhost:8080)
|
|
419
465
|
c8ctl processos set data-dir <path> # PROCESSOS_DATA_DIR (default <stateHome>/processos-data)
|
|
@@ -423,9 +469,11 @@ c8ctl processos config # show current settings and on-disk path
|
|
|
423
469
|
```
|
|
424
470
|
|
|
425
471
|
The binary is resolved in this order: `--binary` flag → `set bin` →
|
|
426
|
-
`$PROCESSOS_BINARY` → a
|
|
427
|
-
|
|
428
|
-
|
|
472
|
+
`$PROCESSOS_BINARY` → a cached download under `<stateHome>/bin/` → a local
|
|
473
|
+
`processos/target/{release,debug}/processos` build → a fresh download from the
|
|
474
|
+
configured download URL (`set download-url` / `$PROCESSOS_DOWNLOAD_URL`). Typed
|
|
475
|
+
settings (`port`, `nano-url`, `data-dir`) always
|
|
476
|
+
win over generic `env` passthrough values when launching.
|
|
429
477
|
|
|
430
478
|
## Installing
|
|
431
479
|
|
package/c8ctl-plugin.js
CHANGED
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
writeFileSync,
|
|
35
35
|
rmSync,
|
|
36
36
|
readdirSync,
|
|
37
|
+
chmodSync,
|
|
38
|
+
renameSync,
|
|
37
39
|
} from 'node:fs';
|
|
38
40
|
import { homedir, platform as osPlatform } from 'node:os';
|
|
39
41
|
import { join, isAbsolute, resolve as resolvePath, dirname } from 'node:path';
|
|
@@ -94,6 +96,23 @@ const PROCESSOS_STATE_FILE = 'processos.json';
|
|
|
94
96
|
const PROCESSOS_DEFAULT_PORT = 8090;
|
|
95
97
|
const DEFAULT_NANO_URL = 'http://localhost:8080';
|
|
96
98
|
|
|
99
|
+
// Passive update notifier (npm-style): refresh the latest published version
|
|
100
|
+
// from the registry in a detached background process at most once per day, and
|
|
101
|
+
// surface a one-line "update available" notice at most once per day. Never
|
|
102
|
+
// blocks a command and never fails one.
|
|
103
|
+
const UPDATE_CACHE_FILE = 'update-check.json';
|
|
104
|
+
const UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
105
|
+
const UPDATE_NOTIFY_TTL_MS = 24 * 60 * 60 * 1000;
|
|
106
|
+
|
|
107
|
+
// ProcessOS is a closed alpha distributed out-of-band: the binary lives in an
|
|
108
|
+
// S3 bucket whose base URL is handed to enabled users via PROCESSOS_DOWNLOAD_URL.
|
|
109
|
+
// `<base>/processos-<os>-<arch>[.exe]` is the per-platform binary and
|
|
110
|
+
// `<base>/version.json` is the {version,commit,updated} metadata the CI writes
|
|
111
|
+
// next to it (the analogue of npm's latest-version lookup for the nano plugin).
|
|
112
|
+
const PROCESSOS_VERSION_META = 'version.json';
|
|
113
|
+
const PROCESSOS_BINARY_META_FILE = 'processos-binary.json';
|
|
114
|
+
const PROCESSOS_UPDATE_CACHE_FILE = 'processos-update-check.json';
|
|
115
|
+
|
|
97
116
|
function getLogger() {
|
|
98
117
|
if (globalThis.c8ctl) {
|
|
99
118
|
return globalThis.c8ctl.getLogger();
|
|
@@ -1221,6 +1240,119 @@ function updatePlugin(req) {
|
|
|
1221
1240
|
console.log(' c8ctl nano restart');
|
|
1222
1241
|
}
|
|
1223
1242
|
|
|
1243
|
+
// ---------------------------------------------------------------------------
|
|
1244
|
+
// Passive "update available" notice. Modelled on npm's update-notifier: the
|
|
1245
|
+
// actual registry lookup runs in a detached background process (so a command is
|
|
1246
|
+
// never slowed), and we only print a notice — at most once per day — from a
|
|
1247
|
+
// cached result. The explicit `c8ctl nano update[ --check]` path is unchanged.
|
|
1248
|
+
// ---------------------------------------------------------------------------
|
|
1249
|
+
|
|
1250
|
+
function getUpdateCacheFile() {
|
|
1251
|
+
return join(getStateHome(), UPDATE_CACHE_FILE);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function readUpdateCache() {
|
|
1255
|
+
try {
|
|
1256
|
+
return JSON.parse(readFileSync(getUpdateCacheFile(), 'utf8'));
|
|
1257
|
+
} catch {
|
|
1258
|
+
return {};
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function writeUpdateCache(obj) {
|
|
1263
|
+
try {
|
|
1264
|
+
mkdirSync(getStateHome(), { recursive: true });
|
|
1265
|
+
writeFileSync(getUpdateCacheFile(), JSON.stringify(obj));
|
|
1266
|
+
} catch {
|
|
1267
|
+
/* a best-effort cache; ignore write failures */
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* True when the notifier should stay silent: an explicit opt-out, CI, or a
|
|
1273
|
+
* non-interactive stdout (piped/scripted), so we never pollute machine-read
|
|
1274
|
+
* output or nag in automation.
|
|
1275
|
+
*/
|
|
1276
|
+
function updateNotifierDisabled() {
|
|
1277
|
+
if (process.env.NANO_NO_UPDATE_NOTIFIER || process.env.NO_UPDATE_NOTIFIER) return true;
|
|
1278
|
+
if (process.env.CI) return true;
|
|
1279
|
+
if (!process.stdout.isTTY) return true;
|
|
1280
|
+
return false;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Refresh the cached latest version in the background. Spawns a detached Node
|
|
1285
|
+
* process that runs `npm view <name> version` and writes the result to the
|
|
1286
|
+
* cache file, then exits — the current command does not wait on it, so the
|
|
1287
|
+
* fresh result is used on the *next* invocation.
|
|
1288
|
+
*/
|
|
1289
|
+
function spawnUpdateRefresh(name, cacheFile) {
|
|
1290
|
+
const script =
|
|
1291
|
+
'const{spawnSync}=require("child_process");' +
|
|
1292
|
+
'const{readFileSync,writeFileSync}=require("fs");' +
|
|
1293
|
+
`let prev={};try{prev=JSON.parse(readFileSync(${JSON.stringify(cacheFile)},"utf8"))}catch{}` +
|
|
1294
|
+
'const out=Object.assign({},prev,{lastCheck:Date.now()});' +
|
|
1295
|
+
`const r=spawnSync("npm",["view",${JSON.stringify(name)},"version"],{encoding:"utf8"});` +
|
|
1296
|
+
'if(r.status===0){out.latest=String(r.stdout||"").trim()}' +
|
|
1297
|
+
`try{writeFileSync(${JSON.stringify(cacheFile)},JSON.stringify(out))}catch{}`;
|
|
1298
|
+
try {
|
|
1299
|
+
const child = spawn(process.execPath, ['-e', script], { detached: true, stdio: 'ignore' });
|
|
1300
|
+
child.unref();
|
|
1301
|
+
} catch {
|
|
1302
|
+
/* if we can't spawn, just skip this cycle */
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function printUpdateNotice(name, current, latest) {
|
|
1307
|
+
const lines = [
|
|
1308
|
+
'',
|
|
1309
|
+
`╭─ Update available: ${name} v${current} → v${latest}`,
|
|
1310
|
+
'│ A newer nano release (plugin + bundled server) is published on npm.',
|
|
1311
|
+
'│ Install it: c8ctl nano update',
|
|
1312
|
+
`│ Or manually: npm install -g ${name}@latest`,
|
|
1313
|
+
'╰─ Then restart any running cluster: c8ctl nano restart',
|
|
1314
|
+
'',
|
|
1315
|
+
];
|
|
1316
|
+
// stderr so it never corrupts parseable stdout.
|
|
1317
|
+
for (const l of lines) console.error(l);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Best-effort, non-blocking update check run at the end of a command. Triggers
|
|
1322
|
+
* a background registry refresh when the cache is stale, and prints a notice
|
|
1323
|
+
* (at most once per day) when the cached latest version is newer than installed.
|
|
1324
|
+
*/
|
|
1325
|
+
function maybeNotifyUpdate(subcommand) {
|
|
1326
|
+
try {
|
|
1327
|
+
if (updateNotifierDisabled()) return;
|
|
1328
|
+
if (subcommand === 'update') return; // the explicit command reports its own state
|
|
1329
|
+
const { name, version: current } = pluginPackage();
|
|
1330
|
+
if (!current || current === '0.0.0-dev') return;
|
|
1331
|
+
|
|
1332
|
+
const cacheFile = getUpdateCacheFile();
|
|
1333
|
+
const cache = readUpdateCache();
|
|
1334
|
+
const now = Date.now();
|
|
1335
|
+
|
|
1336
|
+
if (!cache.lastCheck || now - cache.lastCheck > UPDATE_CHECK_TTL_MS) {
|
|
1337
|
+
try {
|
|
1338
|
+
mkdirSync(getStateHome(), { recursive: true });
|
|
1339
|
+
} catch {
|
|
1340
|
+
/* ignore */
|
|
1341
|
+
}
|
|
1342
|
+
spawnUpdateRefresh(name, cacheFile);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
const latest = cache.latest;
|
|
1346
|
+
if (!latest || compareSemver(current, latest) >= 0) return;
|
|
1347
|
+
if (cache.lastNotified && now - cache.lastNotified <= UPDATE_NOTIFY_TTL_MS) return;
|
|
1348
|
+
|
|
1349
|
+
printUpdateNotice(name, current, latest);
|
|
1350
|
+
writeUpdateCache({ ...cache, lastNotified: now });
|
|
1351
|
+
} catch {
|
|
1352
|
+
/* the notifier must never break a command */
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1224
1356
|
// ---------------------------------------------------------------------------
|
|
1225
1357
|
// processos — manage a single local ProcessOS instance (the optimization-plane
|
|
1226
1358
|
// server that analyses a running Nano BPM engine). Unlike nano, the ProcessOS
|
|
@@ -1268,6 +1400,18 @@ function getProcessosNanoUrl() {
|
|
|
1268
1400
|
return cfg.nanoUrl || process.env.NANO_BASE_URL || DEFAULT_NANO_URL;
|
|
1269
1401
|
}
|
|
1270
1402
|
|
|
1403
|
+
/**
|
|
1404
|
+
* The closed-alpha download URL: env var (PROCESSOS_DOWNLOAD_URL) wins, then the
|
|
1405
|
+
* persisted `processos set download-url` config value. Null when neither is set.
|
|
1406
|
+
*/
|
|
1407
|
+
function getProcessosDownloadUrl() {
|
|
1408
|
+
const fromEnv = process.env.PROCESSOS_DOWNLOAD_URL;
|
|
1409
|
+
if (fromEnv && String(fromEnv).trim()) return String(fromEnv).trim();
|
|
1410
|
+
const cfg = readProcessosConfig();
|
|
1411
|
+
if (cfg.downloadUrl && String(cfg.downloadUrl).trim()) return String(cfg.downloadUrl).trim();
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1271
1415
|
/** The listen port (flag overrides configured value, which overrides default). */
|
|
1272
1416
|
function getProcessosPort(req) {
|
|
1273
1417
|
const cfg = readProcessosConfig();
|
|
@@ -1297,15 +1441,17 @@ function clearProcessosState() {
|
|
|
1297
1441
|
}
|
|
1298
1442
|
|
|
1299
1443
|
/**
|
|
1300
|
-
* Locate
|
|
1444
|
+
* Locate a ProcessOS binary the user already has, WITHOUT downloading. Order:
|
|
1301
1445
|
* 1. --binary flag
|
|
1302
1446
|
* 2. configured path ("processos set bin <path>")
|
|
1303
1447
|
* 3. PROCESSOS_BINARY env var
|
|
1304
|
-
* 4.
|
|
1305
|
-
* 5. debug build under the nanobpmn repo
|
|
1306
|
-
*
|
|
1448
|
+
* 4. a previously auto-downloaded binary cached under the state home
|
|
1449
|
+
* 5. release / debug build under the nanobpmn repo (local dev)
|
|
1450
|
+
* Returns an absolute path, or null when nothing is configured/present. Throws
|
|
1451
|
+
* only when an *explicitly* configured source points at a missing file (so the
|
|
1452
|
+
* user gets an actionable error rather than a silent fallthrough).
|
|
1307
1453
|
*/
|
|
1308
|
-
function
|
|
1454
|
+
function findConfiguredProcessosBinary(req) {
|
|
1309
1455
|
const cfg = readProcessosConfig();
|
|
1310
1456
|
const sources = [
|
|
1311
1457
|
{ val: req?.binary && String(req.binary), from: '--binary' },
|
|
@@ -1321,24 +1467,333 @@ function findProcessosBinary(req) {
|
|
|
1321
1467
|
return abs;
|
|
1322
1468
|
}
|
|
1323
1469
|
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1470
|
+
const cached = getProcessosCachedBinaryPath();
|
|
1471
|
+
if (existsSync(cached)) return cached;
|
|
1472
|
+
|
|
1473
|
+
let repo = null;
|
|
1474
|
+
try {
|
|
1475
|
+
repo = getRepoRoot();
|
|
1476
|
+
} catch {
|
|
1477
|
+
repo = null;
|
|
1478
|
+
}
|
|
1479
|
+
if (repo) {
|
|
1480
|
+
const candidates = [
|
|
1481
|
+
join(repo, 'processos', 'target', 'release', 'processos'),
|
|
1482
|
+
join(repo, 'processos', 'target', 'debug', 'processos'),
|
|
1483
|
+
];
|
|
1484
|
+
for (const c of candidates) {
|
|
1485
|
+
if (existsSync(c)) return c;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
return null;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
/** The state-home directory that holds an auto-downloaded ProcessOS binary. */
|
|
1492
|
+
function getProcessosBinDir() {
|
|
1493
|
+
return join(getStateHome(), 'bin');
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
function getProcessosCachedBinaryPath() {
|
|
1497
|
+
const name = process.platform === 'win32' ? 'processos.exe' : 'processos';
|
|
1498
|
+
return join(getProcessosBinDir(), name);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/** Sidecar recording the version of the auto-downloaded binary (for update checks). */
|
|
1502
|
+
function getProcessosBinaryMetaPath() {
|
|
1503
|
+
return join(getProcessosBinDir(), PROCESSOS_BINARY_META_FILE);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
function readProcessosBinaryMeta() {
|
|
1507
|
+
try {
|
|
1508
|
+
return JSON.parse(readFileSync(getProcessosBinaryMetaPath(), 'utf8'));
|
|
1509
|
+
} catch {
|
|
1510
|
+
return {};
|
|
1332
1511
|
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* The S3 asset name for the host platform, matching the names the nanobpmn CI
|
|
1516
|
+
* uploads (`processos-<os>-<arch>`, `.exe` on Windows). Null on an unsupported
|
|
1517
|
+
* platform.
|
|
1518
|
+
*/
|
|
1519
|
+
function processosAssetName(platform = process.platform, arch = process.arch) {
|
|
1520
|
+
const map = {
|
|
1521
|
+
'darwin:arm64': 'processos-darwin-arm64',
|
|
1522
|
+
'darwin:x64': 'processos-darwin-x64',
|
|
1523
|
+
'linux:x64': 'processos-linux-x64',
|
|
1524
|
+
'linux:arm64': 'processos-linux-arm64',
|
|
1525
|
+
'win32:x64': 'processos-win32-x64.exe',
|
|
1526
|
+
};
|
|
1527
|
+
return map[`${platform}:${arch}`] || null;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* Join a PROCESSOS_DOWNLOAD_URL base with a leaf (`processos-<arch>` or
|
|
1532
|
+
* `version.json`). The base is normally a directory/prefix (e.g. the S3
|
|
1533
|
+
* `.../processos/latest/` URL); if it already points straight at a binary
|
|
1534
|
+
* asset, we treat its parent directory as the base so siblings resolve too.
|
|
1535
|
+
*/
|
|
1536
|
+
function processosDownloadBase(rawUrl) {
|
|
1537
|
+
const t = String(rawUrl || '').trim();
|
|
1538
|
+
if (!t) return '';
|
|
1539
|
+
if (t.endsWith('/')) return t.slice(0, -1);
|
|
1540
|
+
const lastSeg = t.split('/').pop();
|
|
1541
|
+
// A direct link to a binary asset -> use its parent as the base.
|
|
1542
|
+
if (lastSeg.startsWith('processos-') || lastSeg === 'processos' || lastSeg.endsWith('.exe')) {
|
|
1543
|
+
return t.slice(0, t.length - lastSeg.length - 1);
|
|
1544
|
+
}
|
|
1545
|
+
return t;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
function processosBinaryUrl(rawUrl) {
|
|
1549
|
+
const asset = processosAssetName();
|
|
1550
|
+
if (!asset) {
|
|
1551
|
+
throw new Error(
|
|
1552
|
+
`No prebuilt ProcessOS binary is published for this platform (${process.platform}/${process.arch}).`,
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
return `${processosDownloadBase(rawUrl)}/${asset}`;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function processosVersionMetaUrl(rawUrl) {
|
|
1559
|
+
return `${processosDownloadBase(rawUrl)}/${PROCESSOS_VERSION_META}`;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
/** Fetch and parse the remote version.json (best-effort; null on any failure). */
|
|
1563
|
+
async function fetchProcessosVersionMeta(rawUrl, timeoutMs = 4000) {
|
|
1564
|
+
try {
|
|
1565
|
+
const ctrl = new AbortController();
|
|
1566
|
+
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
1567
|
+
const res = await fetch(processosVersionMetaUrl(rawUrl), { redirect: 'follow', signal: ctrl.signal });
|
|
1568
|
+
clearTimeout(t);
|
|
1569
|
+
if (!res.ok) return null;
|
|
1570
|
+
const j = await res.json();
|
|
1571
|
+
return j && typeof j === 'object' ? j : null;
|
|
1572
|
+
} catch {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/** Download a binary to `dest` (atomic via temp + rename; +x on unix). */
|
|
1578
|
+
async function downloadProcessosBinary(url, dest) {
|
|
1579
|
+
const logger = getLogger();
|
|
1580
|
+
logger.info(`Downloading ProcessOS for ${process.platform}/${process.arch} from ${url} ...`);
|
|
1581
|
+
const res = await fetch(url, { redirect: 'follow' });
|
|
1582
|
+
if (!res.ok) {
|
|
1583
|
+
throw new Error(`ProcessOS download failed: HTTP ${res.status} ${res.statusText} for ${url}`);
|
|
1584
|
+
}
|
|
1585
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
1586
|
+
mkdirSync(getProcessosBinDir(), { recursive: true });
|
|
1587
|
+
const tmp = `${dest}.download`;
|
|
1588
|
+
writeFileSync(tmp, buf);
|
|
1589
|
+
if (process.platform !== 'win32') chmodSync(tmp, 0o755);
|
|
1590
|
+
renameSync(tmp, dest);
|
|
1591
|
+
logger.info(`Saved ProcessOS to ${dest} (${(buf.length / 1_000_000).toFixed(1)} MB).`);
|
|
1592
|
+
return dest;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* Resolve the ProcessOS binary to run, downloading it on demand when the user
|
|
1597
|
+
* has a PROCESSOS_DOWNLOAD_URL but no local copy yet. Resolution:
|
|
1598
|
+
* configured/local binary -> cached download -> fresh download -> error.
|
|
1599
|
+
*/
|
|
1600
|
+
async function resolveProcessosBinary(req) {
|
|
1601
|
+
const configured = findConfiguredProcessosBinary(req); // may throw on a missing configured path
|
|
1602
|
+
if (configured) return configured;
|
|
1603
|
+
|
|
1604
|
+
const dlUrl = getProcessosDownloadUrl();
|
|
1605
|
+
if (dlUrl) {
|
|
1606
|
+
const dest = getProcessosCachedBinaryPath();
|
|
1607
|
+
const meta = await fetchProcessosVersionMeta(dlUrl);
|
|
1608
|
+
await downloadProcessosBinary(processosBinaryUrl(dlUrl), dest);
|
|
1609
|
+
// Record what we fetched so the update notifier can compare later.
|
|
1610
|
+
try {
|
|
1611
|
+
mkdirSync(getProcessosBinDir(), { recursive: true });
|
|
1612
|
+
writeFileSync(
|
|
1613
|
+
getProcessosBinaryMetaPath(),
|
|
1614
|
+
JSON.stringify({
|
|
1615
|
+
version: meta?.version ?? null,
|
|
1616
|
+
commit: meta?.commit ?? null,
|
|
1617
|
+
updated: meta?.updated ?? null,
|
|
1618
|
+
source: processosDownloadBase(dlUrl),
|
|
1619
|
+
downloaded: new Date().toISOString(),
|
|
1620
|
+
}),
|
|
1621
|
+
);
|
|
1622
|
+
} catch {
|
|
1623
|
+
/* sidecar is best-effort */
|
|
1624
|
+
}
|
|
1625
|
+
return dest;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1333
1628
|
throw new Error(
|
|
1334
|
-
`Could not find the ProcessOS binary.\n` +
|
|
1335
|
-
`
|
|
1336
|
-
`
|
|
1337
|
-
`
|
|
1338
|
-
|
|
1629
|
+
`Could not find or download the ProcessOS binary.\n` +
|
|
1630
|
+
`Set the download URL you were given (PROCESSOS_DOWNLOAD_URL), point the plugin at a\n` +
|
|
1631
|
+
`local binary ("c8ctl processos set bin <path>" / --binary / PROCESSOS_BINARY), or build\n` +
|
|
1632
|
+
`from source under the nanobpmn repo.`,
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/**
|
|
1637
|
+
* Whether ProcessOS is enabled for this user. It is a closed alpha, so the
|
|
1638
|
+
* operational commands stay locked until the user either has the binary on
|
|
1639
|
+
* their system (configured path / cached download / local build) or has been
|
|
1640
|
+
* given a PROCESSOS_DOWNLOAD_URL to fetch it from.
|
|
1641
|
+
*/
|
|
1642
|
+
function processosEnabled(req) {
|
|
1643
|
+
if (getProcessosDownloadUrl()) return true;
|
|
1644
|
+
try {
|
|
1645
|
+
if (findConfiguredProcessosBinary(req)) return true;
|
|
1646
|
+
} catch {
|
|
1647
|
+
// A configured-but-missing path still means the user opted in; let the real
|
|
1648
|
+
// not-found error surface from the command rather than the closed-alpha gate.
|
|
1649
|
+
return true;
|
|
1650
|
+
}
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function printProcessosClosedAlpha() {
|
|
1655
|
+
const logger = getLogger();
|
|
1656
|
+
logger.error(
|
|
1657
|
+
'ProcessOS is in closed alpha and is not available yet.\n' +
|
|
1658
|
+
'\n' +
|
|
1659
|
+
'To enable it, set the download URL you were given by the Nano BPM team:\n' +
|
|
1660
|
+
' c8ctl processos set download-url <url> # persists it for this machine\n' +
|
|
1661
|
+
' c8ctl processos start # downloads + runs the matching binary\n' +
|
|
1662
|
+
'\n' +
|
|
1663
|
+
'(or set PROCESSOS_DOWNLOAD_URL in your environment for the same effect)\n' +
|
|
1664
|
+
'\n' +
|
|
1665
|
+
'or, if you already have the binary, point the plugin at it:\n' +
|
|
1666
|
+
' c8ctl processos set bin <path>',
|
|
1339
1667
|
);
|
|
1340
1668
|
}
|
|
1341
1669
|
|
|
1670
|
+
// --- ProcessOS update notifier ---------------------------------------------
|
|
1671
|
+
// Mirrors the nano plugin notifier, but the "latest version" comes from the
|
|
1672
|
+
// version.json the nanobpmn CI publishes next to the S3 binaries rather than
|
|
1673
|
+
// from npm. Throttled to one background fetch + one notice per day.
|
|
1674
|
+
|
|
1675
|
+
function getProcessosUpdateCacheFile() {
|
|
1676
|
+
return join(getStateHome(), PROCESSOS_UPDATE_CACHE_FILE);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function readProcessosUpdateCache() {
|
|
1680
|
+
try {
|
|
1681
|
+
return JSON.parse(readFileSync(getProcessosUpdateCacheFile(), 'utf8'));
|
|
1682
|
+
} catch {
|
|
1683
|
+
return {};
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
function writeProcessosUpdateCache(obj) {
|
|
1688
|
+
try {
|
|
1689
|
+
mkdirSync(getStateHome(), { recursive: true });
|
|
1690
|
+
writeFileSync(getProcessosUpdateCacheFile(), JSON.stringify(obj));
|
|
1691
|
+
} catch {
|
|
1692
|
+
/* best-effort */
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/**
|
|
1697
|
+
* The installed ProcessOS version: the recorded version of an auto-downloaded
|
|
1698
|
+
* binary, else `processos --version` against the resolved binary. Null when no
|
|
1699
|
+
* binary is present or it can't report a version.
|
|
1700
|
+
*/
|
|
1701
|
+
function getInstalledProcessosVersion(req) {
|
|
1702
|
+
const meta = readProcessosBinaryMeta();
|
|
1703
|
+
if (meta.version) return String(meta.version);
|
|
1704
|
+
let binary = null;
|
|
1705
|
+
try {
|
|
1706
|
+
binary = findConfiguredProcessosBinary(req);
|
|
1707
|
+
} catch {
|
|
1708
|
+
binary = null;
|
|
1709
|
+
}
|
|
1710
|
+
if (!binary) return null;
|
|
1711
|
+
try {
|
|
1712
|
+
const res = spawnSync(binary, ['--version'], { encoding: 'utf8', timeout: 3000 });
|
|
1713
|
+
if (res.status === 0) {
|
|
1714
|
+
const m = String(res.stdout || '').match(/(\d+\.\d+\.\d+[^\s]*)/);
|
|
1715
|
+
if (m) return m[1];
|
|
1716
|
+
}
|
|
1717
|
+
} catch {
|
|
1718
|
+
/* ignore */
|
|
1719
|
+
}
|
|
1720
|
+
return null;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* Refresh the cached latest ProcessOS version in a detached background process
|
|
1725
|
+
* (fetches version.json), so the current command never waits on the network.
|
|
1726
|
+
*/
|
|
1727
|
+
function spawnProcessosVersionRefresh(metaUrl, cacheFile) {
|
|
1728
|
+
const script =
|
|
1729
|
+
'const{readFileSync,writeFileSync}=require("fs");' +
|
|
1730
|
+
`let prev={};try{prev=JSON.parse(readFileSync(${JSON.stringify(cacheFile)},"utf8"))}catch{}` +
|
|
1731
|
+
'const out=Object.assign({},prev,{lastCheck:Date.now()});' +
|
|
1732
|
+
'const ac=new AbortController();const t=setTimeout(()=>ac.abort(),5000);' +
|
|
1733
|
+
`fetch(${JSON.stringify(metaUrl)},{redirect:"follow",signal:ac.signal})` +
|
|
1734
|
+
'.then(r=>r.ok?r.json():null).then(j=>{clearTimeout(t);' +
|
|
1735
|
+
'if(j&&j.version){out.latest=String(j.version);out.commit=j.commit||null}' +
|
|
1736
|
+
`try{writeFileSync(${JSON.stringify(cacheFile)},JSON.stringify(out))}catch{}})` +
|
|
1737
|
+
`.catch(()=>{try{writeFileSync(${JSON.stringify(cacheFile)},JSON.stringify(out))}catch{}});`;
|
|
1738
|
+
try {
|
|
1739
|
+
const child = spawn(process.execPath, ['-e', script], { detached: true, stdio: 'ignore' });
|
|
1740
|
+
child.unref();
|
|
1741
|
+
} catch {
|
|
1742
|
+
/* skip this cycle */
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
function printProcessosUpdateNotice(current, latest) {
|
|
1747
|
+
const lines = [
|
|
1748
|
+
'',
|
|
1749
|
+
`╭─ ProcessOS update available: v${current ?? '?'} → v${latest}`,
|
|
1750
|
+
'│ A newer ProcessOS build is published.',
|
|
1751
|
+
'│ Get it: c8ctl processos stop && c8ctl processos start',
|
|
1752
|
+
'│ (a configured binary updates itself; a downloaded one re-fetches)',
|
|
1753
|
+
'╰─ Pin a specific build instead with: c8ctl processos set bin <path>',
|
|
1754
|
+
'',
|
|
1755
|
+
];
|
|
1756
|
+
for (const l of lines) console.error(l);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Best-effort, non-blocking ProcessOS update check. Triggers a background
|
|
1761
|
+
* version.json fetch when the cache is stale and prints a notice (at most once
|
|
1762
|
+
* per day) when the published version is newer than the installed one. Only
|
|
1763
|
+
* meaningful when a download URL is configured (the closed-alpha channel).
|
|
1764
|
+
*/
|
|
1765
|
+
function maybeNotifyProcessosUpdate(req) {
|
|
1766
|
+
try {
|
|
1767
|
+
if (updateNotifierDisabled()) return;
|
|
1768
|
+
const dlUrl = getProcessosDownloadUrl();
|
|
1769
|
+
if (!dlUrl) return; // no published channel to compare against
|
|
1770
|
+
const current = getInstalledProcessosVersion(req);
|
|
1771
|
+
if (!current) return;
|
|
1772
|
+
|
|
1773
|
+
const cacheFile = getProcessosUpdateCacheFile();
|
|
1774
|
+
const cache = readProcessosUpdateCache();
|
|
1775
|
+
const now = Date.now();
|
|
1776
|
+
|
|
1777
|
+
if (!cache.lastCheck || now - cache.lastCheck > UPDATE_CHECK_TTL_MS) {
|
|
1778
|
+
try {
|
|
1779
|
+
mkdirSync(getStateHome(), { recursive: true });
|
|
1780
|
+
} catch {
|
|
1781
|
+
/* ignore */
|
|
1782
|
+
}
|
|
1783
|
+
spawnProcessosVersionRefresh(processosVersionMetaUrl(dlUrl), cacheFile);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
const latest = cache.latest;
|
|
1787
|
+
if (!latest || compareSemver(current, latest) >= 0) return;
|
|
1788
|
+
if (cache.lastNotified && now - cache.lastNotified <= UPDATE_NOTIFY_TTL_MS) return;
|
|
1789
|
+
|
|
1790
|
+
printProcessosUpdateNotice(current, latest);
|
|
1791
|
+
writeProcessosUpdateCache({ ...cache, lastNotified: now });
|
|
1792
|
+
} catch {
|
|
1793
|
+
/* never break a command over the notifier */
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1342
1797
|
/** Probe ProcessOS's GET /health endpoint for reachability. */
|
|
1343
1798
|
async function probeProcessosHealthy(url) {
|
|
1344
1799
|
return probePath(url, '/health');
|
|
@@ -1368,7 +1823,7 @@ async function startProcessos(req) {
|
|
|
1368
1823
|
await stopProcessos({});
|
|
1369
1824
|
}
|
|
1370
1825
|
|
|
1371
|
-
const binary =
|
|
1826
|
+
const binary = await resolveProcessosBinary(req);
|
|
1372
1827
|
const port = getProcessosPort(req);
|
|
1373
1828
|
const url = `http://127.0.0.1:${port}`;
|
|
1374
1829
|
const nanoUrl = req.nanoUrl || getProcessosNanoUrl();
|
|
@@ -1589,6 +2044,8 @@ const PROCESSOS_SET_FIELDS = {
|
|
|
1589
2044
|
port: 'port',
|
|
1590
2045
|
'nano-url': 'nanoUrl',
|
|
1591
2046
|
nanourl: 'nanoUrl',
|
|
2047
|
+
'download-url': 'downloadUrl',
|
|
2048
|
+
downloadurl: 'downloadUrl',
|
|
1592
2049
|
'data-dir': 'dataDir',
|
|
1593
2050
|
datadir: 'dataDir',
|
|
1594
2051
|
env: 'env',
|
|
@@ -1598,6 +2055,7 @@ function printProcessosSetUsage() {
|
|
|
1598
2055
|
const logger = getLogger();
|
|
1599
2056
|
logger.info('Usage: c8ctl processos set <field> <value>');
|
|
1600
2057
|
logger.info(' bin <path> Path to the downloaded ProcessOS binary');
|
|
2058
|
+
logger.info(' download-url <url> Closed-alpha binary download URL (enables ProcessOS)');
|
|
1601
2059
|
logger.info(' port <n> Listen port (default 8090)');
|
|
1602
2060
|
logger.info(' nano-url <url> Target Nano BPM engine URL (default http://localhost:8080)');
|
|
1603
2061
|
logger.info(' data-dir <path> ProcessOS data directory');
|
|
@@ -1670,6 +2128,16 @@ function setProcessosConfig(req) {
|
|
|
1670
2128
|
}
|
|
1671
2129
|
cfg.nanoUrl = val;
|
|
1672
2130
|
logger.info(`Set nano-url = ${val}`);
|
|
2131
|
+
} else if (field === 'downloadUrl') {
|
|
2132
|
+
const val = req.positional[1];
|
|
2133
|
+
if (val === undefined || val === '') {
|
|
2134
|
+
// Allow clearing with an empty value: c8ctl processos set download-url ""
|
|
2135
|
+
delete cfg.downloadUrl;
|
|
2136
|
+
logger.info('Cleared download-url');
|
|
2137
|
+
} else {
|
|
2138
|
+
cfg.downloadUrl = String(val).trim();
|
|
2139
|
+
logger.info(`Set download-url = ${cfg.downloadUrl}`);
|
|
2140
|
+
}
|
|
1673
2141
|
} else if (field === 'dataDir') {
|
|
1674
2142
|
const val = req.positional[1];
|
|
1675
2143
|
if (!val) {
|
|
@@ -1702,10 +2170,25 @@ function showProcessosConfig() {
|
|
|
1702
2170
|
}
|
|
1703
2171
|
}
|
|
1704
2172
|
console.log('');
|
|
2173
|
+
console.log(' closed-alpha channel:');
|
|
2174
|
+
const dlUrl = getProcessosDownloadUrl();
|
|
2175
|
+
const dlSource = process.env.PROCESSOS_DOWNLOAD_URL && String(process.env.PROCESSOS_DOWNLOAD_URL).trim()
|
|
2176
|
+
? ' (from $PROCESSOS_DOWNLOAD_URL)'
|
|
2177
|
+
: cfg.downloadUrl
|
|
2178
|
+
? ' (from "processos set download-url")'
|
|
2179
|
+
: '';
|
|
2180
|
+
console.log(` download url ${dlUrl ? dlUrl + dlSource : '(not set — ProcessOS is a closed alpha; "c8ctl processos set download-url <url>" to enable)'}`);
|
|
2181
|
+
const cached = getProcessosCachedBinaryPath();
|
|
2182
|
+
const meta = readProcessosBinaryMeta();
|
|
2183
|
+
console.log(` cached binary ${existsSync(cached) ? cached : '(none — downloaded on first "processos start")'}`);
|
|
2184
|
+
if (meta.version || meta.commit) {
|
|
2185
|
+
console.log(` version ${meta.version || '?'}${meta.commit ? ` (${String(meta.commit).slice(0, 8)})` : ''}${meta.downloaded ? ` downloaded ${meta.downloaded}` : ''}`);
|
|
2186
|
+
}
|
|
2187
|
+
console.log('');
|
|
1705
2188
|
console.log(` state file ${getProcessosStateFile()}`);
|
|
1706
2189
|
console.log(` log file ${getProcessosLogFile()}`);
|
|
1707
2190
|
console.log('');
|
|
1708
|
-
console.log(' Change with: c8ctl processos set bin <path> | set port <n> | set nano-url <url> | set data-dir <path> | set env KEY=VALUE');
|
|
2191
|
+
console.log(' Change with: c8ctl processos set bin <path> | set download-url <url> | set port <n> | set nano-url <url> | set data-dir <path> | set env KEY=VALUE');
|
|
1709
2192
|
}
|
|
1710
2193
|
|
|
1711
2194
|
function printProcessosUsage() {
|
|
@@ -1717,10 +2200,12 @@ function printProcessosUsage() {
|
|
|
1717
2200
|
console.log(' c8ctl processos stop');
|
|
1718
2201
|
console.log(' c8ctl processos restart [...]');
|
|
1719
2202
|
console.log(' c8ctl processos logs [--follow]');
|
|
1720
|
-
console.log(' c8ctl processos set bin <path> | port <n> | nano-url <url> | data-dir <path> | env KEY=VALUE');
|
|
2203
|
+
console.log(' c8ctl processos set bin <path> | download-url <url> | port <n> | nano-url <url> | data-dir <path> | env KEY=VALUE');
|
|
1721
2204
|
console.log(' c8ctl processos config');
|
|
1722
2205
|
console.log('');
|
|
1723
|
-
console.log('ProcessOS is
|
|
2206
|
+
console.log('ProcessOS is a closed alpha. Enable it with the download URL you were given:');
|
|
2207
|
+
console.log(' c8ctl processos set download-url <url> # plugin downloads + runs the matching binary');
|
|
2208
|
+
console.log('or point the plugin at a binary you already have: "c8ctl processos set bin <path>".');
|
|
1724
2209
|
console.log('By default ProcessOS spawns its own internal pilot Nano engine (the plugin auto-wires the nano');
|
|
1725
2210
|
console.log('binary into PROCESSOS_NANO_BIN). Use --no-spawn-nano to instead use the --nano-url engine for');
|
|
1726
2211
|
console.log('the pilot too. If no nano binary is available, it falls back to --no-spawn-nano automatically.');
|
|
@@ -1784,7 +2269,8 @@ export const metadata = {
|
|
|
1784
2269
|
processos: {
|
|
1785
2270
|
description: 'Manage a local ProcessOS instance — start, status, stop, logs, config',
|
|
1786
2271
|
examples: [
|
|
1787
|
-
{ command: 'c8ctl processos set
|
|
2272
|
+
{ command: 'c8ctl processos set download-url <url>', description: 'Enable the closed alpha + auto-download the matching binary' },
|
|
2273
|
+
{ command: 'c8ctl processos set bin <path>', description: 'Point the plugin at a ProcessOS binary you already have' },
|
|
1788
2274
|
{ command: 'c8ctl processos start', description: 'Start ProcessOS against the local Nano BPM engine' },
|
|
1789
2275
|
{ command: 'c8ctl processos start --nano-url http://localhost:8080', description: 'Start against a specific engine' },
|
|
1790
2276
|
{ command: 'c8ctl processos status', description: 'Show ProcessOS status and health' },
|
|
@@ -1824,6 +2310,7 @@ export const commands = {
|
|
|
1824
2310
|
return;
|
|
1825
2311
|
}
|
|
1826
2312
|
|
|
2313
|
+
let failed = false;
|
|
1827
2314
|
try {
|
|
1828
2315
|
switch (req.subcommand) {
|
|
1829
2316
|
case 'start':
|
|
@@ -1864,8 +2351,10 @@ export const commands = {
|
|
|
1864
2351
|
}
|
|
1865
2352
|
} catch (error) {
|
|
1866
2353
|
logger.error(`nano ${req.subcommand} failed: ${error instanceof Error ? error.message : error}`);
|
|
1867
|
-
|
|
2354
|
+
failed = true;
|
|
1868
2355
|
}
|
|
2356
|
+
maybeNotifyUpdate(req.subcommand);
|
|
2357
|
+
if (failed) process.exit(1);
|
|
1869
2358
|
},
|
|
1870
2359
|
},
|
|
1871
2360
|
processos: {
|
|
@@ -1887,6 +2376,16 @@ export const commands = {
|
|
|
1887
2376
|
return;
|
|
1888
2377
|
}
|
|
1889
2378
|
|
|
2379
|
+
// ProcessOS is a closed alpha: gate the operational commands until the
|
|
2380
|
+
// user has opted in (download URL set or a binary on their system).
|
|
2381
|
+
// `set`/`config` stay open so users can configure/inspect at any time.
|
|
2382
|
+
const ungated = req.subcommand === 'set' || req.subcommand === 'config';
|
|
2383
|
+
if (!ungated && !processosEnabled(req)) {
|
|
2384
|
+
printProcessosClosedAlpha();
|
|
2385
|
+
process.exit(1);
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
let failed = false;
|
|
1890
2389
|
try {
|
|
1891
2390
|
switch (req.subcommand) {
|
|
1892
2391
|
case 'start':
|
|
@@ -1915,8 +2414,11 @@ export const commands = {
|
|
|
1915
2414
|
}
|
|
1916
2415
|
} catch (error) {
|
|
1917
2416
|
logger.error(`processos ${req.subcommand} failed: ${error instanceof Error ? error.message : error}`);
|
|
1918
|
-
|
|
2417
|
+
failed = true;
|
|
1919
2418
|
}
|
|
2419
|
+
maybeNotifyUpdate(req.subcommand);
|
|
2420
|
+
maybeNotifyProcessosUpdate(req);
|
|
2421
|
+
if (failed) process.exit(1);
|
|
1920
2422
|
},
|
|
1921
2423
|
},
|
|
1922
2424
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8ctl-plugin-nano",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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,10 +49,10 @@
|
|
|
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-win32-x64": "1.
|
|
52
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.3.0",
|
|
53
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.3.0",
|
|
54
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.3.0",
|
|
55
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.3.0",
|
|
56
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.3.0"
|
|
57
57
|
}
|
|
58
58
|
}
|