@openparachute/hub 0.6.2 → 0.6.3-rc.2
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 +87 -35
- package/package.json +1 -1
- package/src/__tests__/api-hub-upgrade.test.ts +690 -0
- package/src/__tests__/api-modules-ops.test.ts +359 -3
- package/src/__tests__/api-modules.test.ts +54 -0
- package/src/__tests__/expose-cloudflare.test.ts +163 -72
- package/src/__tests__/expose-off-auto.test.ts +26 -1
- package/src/__tests__/expose.test.ts +260 -240
- package/src/__tests__/hub-control.test.ts +1 -242
- package/src/__tests__/hub-server.test.ts +64 -0
- package/src/__tests__/hub-unit.test.ts +574 -0
- package/src/__tests__/init.test.ts +219 -2
- package/src/__tests__/lifecycle.test.ts +416 -1448
- package/src/__tests__/managed-unit.test.ts +575 -0
- package/src/__tests__/migrate-cutover.test.ts +840 -0
- package/src/__tests__/migrate-offer.test.ts +240 -0
- package/src/__tests__/migrate.test.ts +132 -0
- package/src/__tests__/module-ops-client.test.ts +556 -0
- package/src/__tests__/port-probe.test.ts +23 -0
- package/src/__tests__/setup-wizard.test.ts +130 -0
- package/src/__tests__/status-supervisor.test.ts +504 -0
- package/src/__tests__/status.test.ts +157 -708
- package/src/__tests__/supervisor.test.ts +471 -6
- package/src/__tests__/upgrade.test.ts +351 -5
- package/src/api-hub-upgrade.ts +384 -0
- package/src/api-hub.ts +2 -1
- package/src/api-modules-ops.ts +221 -0
- package/src/api-modules.ts +18 -2
- package/src/cli.ts +97 -12
- package/src/cloudflare/connector-service.ts +117 -322
- package/src/commands/expose-cloudflare.ts +63 -71
- package/src/commands/expose-supervisor.ts +247 -0
- package/src/commands/expose.ts +59 -48
- package/src/commands/init.ts +225 -12
- package/src/commands/lifecycle.ts +455 -816
- package/src/commands/migrate-cutover.ts +837 -0
- package/src/commands/migrate.ts +71 -2
- package/src/commands/serve-boot.ts +71 -25
- package/src/commands/status.ts +535 -235
- package/src/commands/upgrade.ts +100 -2
- package/src/help.ts +128 -68
- package/src/hub-control.ts +23 -162
- package/src/hub-server.ts +39 -0
- package/src/hub-unit.ts +735 -0
- package/src/hub-upgrade-helper.ts +306 -0
- package/src/hub-upgrade-mode.ts +209 -0
- package/src/hub-upgrade-status.ts +150 -0
- package/src/managed-unit.ts +692 -0
- package/src/migrate-offer.ts +186 -0
- package/src/module-ops-client.ts +457 -0
- package/src/port-probe.ts +50 -0
- package/src/process-state.ts +19 -3
- package/src/setup-wizard.ts +80 -1
- package/src/supervisor.ts +389 -38
- package/web/ui/dist/assets/index-D_6AFvZy.js +61 -0
- package/web/ui/dist/assets/{index-BiBlvEaj.css → index-mz8XcVPP.css} +1 -1
- package/web/ui/dist/index.html +2 -2
- package/web/ui/dist/assets/index-CIN3mnmf.js +0 -61
package/src/commands/upgrade.ts
CHANGED
|
@@ -51,6 +51,12 @@ import { homedir } from "node:os";
|
|
|
51
51
|
import { dirname, join } from "node:path";
|
|
52
52
|
import { CONFIG_DIR, SERVICES_MANIFEST_PATH } from "../config.ts";
|
|
53
53
|
import { HUB_PACKAGE, HUB_SVC } from "../hub-control.ts";
|
|
54
|
+
import {
|
|
55
|
+
type HubUnitDeps,
|
|
56
|
+
type HubUnitManagerOpResult,
|
|
57
|
+
defaultHubUnitDeps,
|
|
58
|
+
restartHubUnit as restartHubUnitImpl,
|
|
59
|
+
} from "../hub-unit.ts";
|
|
54
60
|
import { ModuleManifestError } from "../module-manifest.ts";
|
|
55
61
|
import {
|
|
56
62
|
type ServiceSpec,
|
|
@@ -201,6 +207,25 @@ export interface UpgradeOpts {
|
|
|
201
207
|
* flaky probe never blocks a legitimate upgrade.
|
|
202
208
|
*/
|
|
203
209
|
resolveChannelVersion?: (pkg: string, channel: string) => Promise<string | null>;
|
|
210
|
+
/**
|
|
211
|
+
* Supervisor-path seams (design §5) — the ONLY runtime as of Phase 5b.
|
|
212
|
+
* `upgrade hub` rewrites the binary as usual then RESTARTS THE UNIT via the
|
|
213
|
+
* platform manager (`restartHubUnit` — systemctl restart / launchctl kickstart
|
|
214
|
+
* -k): the manager tears down the old hub (children die), starts the new
|
|
215
|
+
* binary, which re-boots every module from services.json. NEVER a PID-signal
|
|
216
|
+
* restart (launchd KeepAlive / systemd Restart=always would fight). Module-
|
|
217
|
+
* target restarts drive the running Supervisor (lifecycle's own dispatch, fed
|
|
218
|
+
* a `supervisor` block here). The detached restart arm was retired in Phase 5b.
|
|
219
|
+
*
|
|
220
|
+
* Production CLI dispatch passes `supervisor: {}`; tests inject the seams they
|
|
221
|
+
* want to assert.
|
|
222
|
+
*/
|
|
223
|
+
supervisor?: {
|
|
224
|
+
/** Deps for the `restartHubUnit` manager op. */
|
|
225
|
+
hubUnitDeps?: HubUnitDeps;
|
|
226
|
+
/** Restart the hub unit via the platform manager (never a PID signal, §5). */
|
|
227
|
+
restartHubUnit?: (deps: HubUnitDeps) => HubUnitManagerOpResult;
|
|
228
|
+
};
|
|
204
229
|
}
|
|
205
230
|
|
|
206
231
|
interface ResolvedTarget {
|
|
@@ -226,6 +251,8 @@ interface Resolved {
|
|
|
226
251
|
channelOverride: "rc" | "latest" | undefined;
|
|
227
252
|
allowDowngrade: boolean;
|
|
228
253
|
resolveChannelVersion: (pkg: string, channel: string) => Promise<string | null>;
|
|
254
|
+
hubUnitDeps: HubUnitDeps;
|
|
255
|
+
restartHubUnit: (deps: HubUnitDeps) => HubUnitManagerOpResult;
|
|
229
256
|
}
|
|
230
257
|
|
|
231
258
|
function bunGlobalPrefixes(): string[] {
|
|
@@ -258,6 +285,20 @@ function resolve(opts: UpgradeOpts): Resolved {
|
|
|
258
285
|
allowDowngrade: opts.allowDowngrade ?? false,
|
|
259
286
|
resolveChannelVersion:
|
|
260
287
|
opts.resolveChannelVersion ?? ((pkg, channel) => npmViewVersion(pkg, channel, runner)),
|
|
288
|
+
// Supervisor seams (the only runtime as of Phase 5b). Production passes
|
|
289
|
+
// `supervisor: {}`; tests inject the seams they want to assert.
|
|
290
|
+
...resolveUpgradeSupervisor(opts.supervisor),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Resolve the supervisor seams for the upgrade path. */
|
|
295
|
+
function resolveUpgradeSupervisor(opts: UpgradeOpts["supervisor"]): {
|
|
296
|
+
hubUnitDeps: HubUnitDeps;
|
|
297
|
+
restartHubUnit: (deps: HubUnitDeps) => HubUnitManagerOpResult;
|
|
298
|
+
} {
|
|
299
|
+
return {
|
|
300
|
+
hubUnitDeps: opts?.hubUnitDeps ?? defaultHubUnitDeps,
|
|
301
|
+
restartHubUnit: opts?.restartHubUnit ?? restartHubUnitImpl,
|
|
261
302
|
};
|
|
262
303
|
}
|
|
263
304
|
|
|
@@ -422,6 +463,13 @@ async function resolveTargets(
|
|
|
422
463
|
// Sweep mode: hub first, then everything in services.json. Hub-first means a
|
|
423
464
|
// dispatcher upgrade can't be undermined mid-sweep by a service upgrade that
|
|
424
465
|
// restarts hub for reasons unrelated to its own code change.
|
|
466
|
+
//
|
|
467
|
+
// Phase 4 note (design §5 item 4): on a unit-managed box, restarting the hub
|
|
468
|
+
// unit re-boots ALL modules from services.json. So the hub-first sweep already
|
|
469
|
+
// boots every module onto current code when the hub binary upgrades; each
|
|
470
|
+
// module target then upgrades its package + `supervisor.restart`s it
|
|
471
|
+
// individually (idempotent — a no-op restart if its code didn't change). The
|
|
472
|
+
// hub-first invariant still holds.
|
|
425
473
|
const targets: ResolvedTarget[] = [hubTarget()];
|
|
426
474
|
for (const entry of manifest.services) {
|
|
427
475
|
const short = shortNameForManifest(entry.name);
|
|
@@ -515,6 +563,56 @@ function readPackageVersion(pkgJsonPath: string): string | null {
|
|
|
515
563
|
}
|
|
516
564
|
}
|
|
517
565
|
|
|
566
|
+
/**
|
|
567
|
+
* Restart an upgraded target after its binary/package was rewritten (design §5).
|
|
568
|
+
* Supervised path only (Phase 5b — the detached restart arm is retired):
|
|
569
|
+
* - HUB target → restart the hub UNIT via the platform manager
|
|
570
|
+
* (`restartHubUnit` — systemctl restart / launchctl kickstart -k). The
|
|
571
|
+
* manager tears down the old hub (children die), starts the new binary,
|
|
572
|
+
* which re-boots every module from services.json. NEVER a PID-signal restart
|
|
573
|
+
* (launchd KeepAlive / systemd Restart=always would fight). The command
|
|
574
|
+
* returns once the restart is dispatched; it does not need to outlive the
|
|
575
|
+
* old hub.
|
|
576
|
+
* - MODULE target → drive the running Supervisor by handing `lifecycle.restart`
|
|
577
|
+
* the SAME opts a bare `parachute restart <svc>` threads: `supervisor: {}`
|
|
578
|
+
* (so the real `isHubUnitInstalled` probe — not a forced override — decides)
|
|
579
|
+
* plus `migrateOffer: { enabled: true }`. Its dispatch then routes to
|
|
580
|
+
* `supervisor.restart` with the 404-fallthrough. The hub unit was already
|
|
581
|
+
* restarted hub-first in the sweep, so it's up to answer.
|
|
582
|
+
*
|
|
583
|
+
* A box with no hub unit takes the actionable migrate path: the hub-target
|
|
584
|
+
* `restartHubUnit` returns `no-unit` (messages surfaced, non-zero), and the
|
|
585
|
+
* module-target `lifecycle.restart` — driven with `supervisor: {}` +
|
|
586
|
+
* `migrateOffer` — runs `requireSupervisedOrOffer`'s real probe, then the §7.5
|
|
587
|
+
* auto-offer / actionable "run `parachute migrate --to-supervised`" error,
|
|
588
|
+
* rather than a bare connection-refused from `driveModuleOp`.
|
|
589
|
+
*/
|
|
590
|
+
async function restartTarget(target: ResolvedTarget, r: Resolved): Promise<number> {
|
|
591
|
+
if (target.short === HUB_SVC) {
|
|
592
|
+
const res = r.restartHubUnit(r.hubUnitDeps);
|
|
593
|
+
for (const m of res.messages) r.log(m);
|
|
594
|
+
if (res.outcome === "ok") {
|
|
595
|
+
r.log(`${target.short}: restarted the hub unit (all modules re-booted).`);
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
return 1;
|
|
599
|
+
}
|
|
600
|
+
// Module target: route through lifecycle's supervisor arm with the SAME opts a
|
|
601
|
+
// bare `parachute restart <svc>` threads — `supervisor: {}` (let the real
|
|
602
|
+
// `isHubUnitInstalled` probe decide; do NOT force `unitInstalled: true` and
|
|
603
|
+
// bypass it) plus `migrateOffer: { enabled: true }`. On a supervised box this
|
|
604
|
+
// drives `supervisor.restart` over the loopback module-ops API; on a no-unit
|
|
605
|
+
// box it gets the §7.5 auto-offer / actionable migrate error instead of a bare
|
|
606
|
+
// connection-refused. `hubUnitDeps` threads through so the real probe + manager
|
|
607
|
+
// ops use the resolved deps (production defaults; tests inject the seams).
|
|
608
|
+
return await r.restartFn(target.short, {
|
|
609
|
+
manifestPath: r.manifestPath,
|
|
610
|
+
configDir: r.configDir,
|
|
611
|
+
supervisor: { hubUnitDeps: r.hubUnitDeps },
|
|
612
|
+
migrateOffer: { enabled: true },
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
518
616
|
async function upgradeLinked(
|
|
519
617
|
target: ResolvedTarget,
|
|
520
618
|
sourceDir: string,
|
|
@@ -573,7 +671,7 @@ async function upgradeLinked(
|
|
|
573
671
|
}
|
|
574
672
|
|
|
575
673
|
r.log(`${target.short}: ${before.sha.slice(0, 7)} → ${after.sha.slice(0, 7)}; restarting…`);
|
|
576
|
-
return await
|
|
674
|
+
return await restartTarget(target, r);
|
|
577
675
|
}
|
|
578
676
|
|
|
579
677
|
/**
|
|
@@ -642,7 +740,7 @@ async function upgradeNpm(target: ResolvedTarget, sourceDir: string, r: Resolved
|
|
|
642
740
|
}
|
|
643
741
|
|
|
644
742
|
r.log(`${target.short}: ${beforeVersion ?? "?"} → ${afterVersion ?? "?"}; restarting…`);
|
|
645
|
-
return await
|
|
743
|
+
return await restartTarget(target, r);
|
|
646
744
|
}
|
|
647
745
|
|
|
648
746
|
async function upgradeOne(target: ResolvedTarget, r: Resolved): Promise<number> {
|
package/src/help.ts
CHANGED
|
@@ -16,15 +16,16 @@ Usage:
|
|
|
16
16
|
parachute setup interactive walk-through: install services + configure
|
|
17
17
|
parachute install <service> install and register a service
|
|
18
18
|
services: ${services}
|
|
19
|
-
parachute status show installed services,
|
|
20
|
-
parachute start [service] start
|
|
21
|
-
parachute stop [service] stop
|
|
22
|
-
parachute restart [service]
|
|
19
|
+
parachute status show installed services, run state, health
|
|
20
|
+
parachute start [service] start a module via the supervisor (or ensure the hub is up)
|
|
21
|
+
parachute stop [service] stop a module via the supervisor (or stop the hub unit)
|
|
22
|
+
parachute restart [service] restart a module via the supervisor (or restart the hub unit)
|
|
23
23
|
parachute upgrade [service] pull / re-install + restart (skips if no changes)
|
|
24
24
|
parachute logs <service> [-f] print service logs; -f to tail
|
|
25
25
|
parachute expose tailnet [off] HTTPS across your tailnet (supported)
|
|
26
26
|
parachute expose public [off] HTTPS on the public internet (exploratory)
|
|
27
|
-
parachute serve run hub
|
|
27
|
+
parachute serve run the hub + supervisor foregrounded (the runtime)
|
|
28
|
+
parachute migrate --to-supervised move a legacy detached install to the managed hub
|
|
28
29
|
parachute migrate [--dry-run] archive legacy files at ecosystem root
|
|
29
30
|
parachute auth <cmd> identity (set password, manage 2FA)
|
|
30
31
|
parachute vault <args...> vault-specific ops (tokens, 2fa, config, init,
|
|
@@ -123,7 +124,9 @@ What it does:
|
|
|
123
124
|
get you to that wizard.
|
|
124
125
|
|
|
125
126
|
Idempotent — every re-run is safe:
|
|
126
|
-
1.
|
|
127
|
+
1. Install + start the hub as a managed unit (launchd on a Mac,
|
|
128
|
+
systemd on a Linux VM) so it survives reboots; no-op if it's
|
|
129
|
+
already up.
|
|
127
130
|
2. If the hub isn't already exposed, in a terminal, offer to set up
|
|
128
131
|
exposure (Tailscale Funnel, Cloudflare Tunnel, or stay loopback).
|
|
129
132
|
The default highlights "no thanks" on laptops and Cloudflare on
|
|
@@ -273,11 +276,17 @@ Usage:
|
|
|
273
276
|
parachute status
|
|
274
277
|
|
|
275
278
|
What it does:
|
|
276
|
-
Reads ~/.parachute/services.json. For each registered
|
|
277
|
-
-
|
|
278
|
-
- probes http://localhost:<port><health> (skipped for known-stopped
|
|
279
|
+
Reads ~/.parachute/services.json. For each registered module:
|
|
280
|
+
- reads its run state from the running hub's supervisor (supervisor.list())
|
|
281
|
+
- probes http://localhost:<port><health> (skipped for known-stopped modules)
|
|
279
282
|
- classifies the install source as bun-linked (local checkout) or npm
|
|
280
283
|
|
|
284
|
+
The hub gets its own row, derived from the platform process manager
|
|
285
|
+
(launchd \`launchctl print\` / systemd \`systemctl is-active\`, or
|
|
286
|
+
"container runtime (managed)" on Render / Fly) — the supervisor runs the
|
|
287
|
+
modules, but the manager runs the hub. The hub row appears even with zero
|
|
288
|
+
modules installed.
|
|
289
|
+
|
|
281
290
|
The STATE column rolls process state + probe result into one of four
|
|
282
291
|
canonical labels (per parachute-patterns/patterns/design-system.md §6):
|
|
283
292
|
active supervised, running, last probe ok
|
|
@@ -293,10 +302,10 @@ What it does:
|
|
|
293
302
|
HEALTH (ok / down / http <code>). Workstream F collapsed them onto the
|
|
294
303
|
single STATE column the SPA + well-known doc also speak.
|
|
295
304
|
|
|
296
|
-
Stopped
|
|
305
|
+
Stopped modules render as STATE=inactive and don't count toward the
|
|
297
306
|
exit code — they're an expected state after fresh install before
|
|
298
|
-
\`parachute start\`.
|
|
299
|
-
|
|
307
|
+
\`parachute start\`. Supervised modules that fail health checks render
|
|
308
|
+
as STATE=failing and exit 1.
|
|
300
309
|
|
|
301
310
|
A "STALE: services.json cached … live package.json …" continuation line
|
|
302
311
|
appears under a row when a bun-linked service has been rebuilt but the
|
|
@@ -409,39 +418,41 @@ Cloudflare tunnel requirements (--cloudflare):
|
|
|
409
418
|
}
|
|
410
419
|
|
|
411
420
|
export function startHelp(): string {
|
|
412
|
-
return `parachute start —
|
|
421
|
+
return `parachute start — start a module via the running hub's supervisor
|
|
413
422
|
|
|
414
423
|
Usage:
|
|
415
|
-
parachute start
|
|
416
|
-
parachute start <service> start just that one
|
|
417
|
-
parachute start hub
|
|
424
|
+
parachute start ensure the hub is up (boots every module)
|
|
425
|
+
parachute start <service> start just that one module
|
|
426
|
+
parachute start hub ensure the hub unit is up
|
|
418
427
|
|
|
419
428
|
What it does:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
\`parachute start
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
\`parachute serve\` is the one runtime — the hub foreground with an
|
|
430
|
+
in-process supervisor that runs each module as an attached child. These
|
|
431
|
+
verbs are clients of that running hub:
|
|
432
|
+
|
|
433
|
+
- \`parachute start <service>\` ensures the hub unit is up, then asks its
|
|
434
|
+
supervisor to start that one module (over the loopback module-ops API,
|
|
435
|
+
authenticated with your operator token). No per-module daemon is
|
|
436
|
+
spawned — the supervisor owns the module process.
|
|
437
|
+
- \`parachute start\` (no service) ensures the hub unit is up. The hub
|
|
438
|
+
boots every installed module on start, so this brings the whole stack
|
|
439
|
+
up. On a box with no hub unit yet, it offers to run
|
|
440
|
+
\`parachute migrate --to-supervised\` (which installs + starts the unit).
|
|
441
|
+
- \`parachute start hub\` is the same "ensure the hub unit is up."
|
|
442
|
+
|
|
443
|
+
Idempotent: starting an already-running module is a no-op.
|
|
431
444
|
|
|
432
445
|
Flags:
|
|
433
|
-
--hub-origin <url> override
|
|
446
|
+
--hub-origin <url> override the hub origin used as the operator token's
|
|
447
|
+
\`iss\` validator on the loopback module-ops call
|
|
434
448
|
(default: current expose-state hub origin, else loopback).
|
|
435
|
-
For \`start hub\`, also doubles as the hub's --issuer.
|
|
436
449
|
|
|
437
450
|
Examples:
|
|
438
451
|
parachute start bring everything up
|
|
439
|
-
parachute start vault just vault
|
|
440
|
-
parachute start hub just the internal hub
|
|
452
|
+
parachute start vault just vault, via the supervisor
|
|
441
453
|
parachute logs vault watch what just started
|
|
442
454
|
|
|
443
|
-
|
|
444
|
-
hub bun <cli>/hub-server.ts --port <picked> ...
|
|
455
|
+
Module start commands (run by the supervisor under \`serve\`):
|
|
445
456
|
vault parachute-vault serve
|
|
446
457
|
scribe parachute-scribe serve
|
|
447
458
|
app parachute-app serve
|
|
@@ -451,39 +462,45 @@ Start commands by service:
|
|
|
451
462
|
}
|
|
452
463
|
|
|
453
464
|
export function stopHelp(): string {
|
|
454
|
-
return `parachute stop — stop
|
|
465
|
+
return `parachute stop — stop a module (or the hub) cleanly
|
|
455
466
|
|
|
456
467
|
Usage:
|
|
457
|
-
parachute stop stop
|
|
458
|
-
parachute stop <service> stop just that one
|
|
459
|
-
parachute stop hub stop the
|
|
468
|
+
parachute stop stop the hub unit (modules stop with it)
|
|
469
|
+
parachute stop <service> stop just that one module
|
|
470
|
+
parachute stop hub stop the hub unit
|
|
460
471
|
|
|
461
472
|
What it does:
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
473
|
+
- \`parachute stop <service>\` asks the running hub's supervisor to stop
|
|
474
|
+
that one module (SIGTERM to the module's process group, then SIGKILL if
|
|
475
|
+
it doesn't exit). No-op if it wasn't running.
|
|
476
|
+
- \`parachute stop\` (no service) and \`parachute stop hub\` stop the hub
|
|
477
|
+
UNIT through the platform process manager (\`launchctl bootout\` /
|
|
478
|
+
\`systemctl stop\`) — never a raw PID signal, which launchd's KeepAlive
|
|
479
|
+
would just undo. Modules are attached children and stop with the hub.
|
|
469
480
|
|
|
470
481
|
Examples:
|
|
471
|
-
parachute stop
|
|
472
|
-
parachute stop
|
|
473
|
-
parachute stop hub just the internal hub
|
|
482
|
+
parachute stop vault just vault, via the supervisor
|
|
483
|
+
parachute stop stop the whole stack (the hub unit)
|
|
474
484
|
`;
|
|
475
485
|
}
|
|
476
486
|
|
|
477
487
|
export function restartHelp(): string {
|
|
478
|
-
return `parachute restart —
|
|
488
|
+
return `parachute restart — restart a module (or the hub)
|
|
479
489
|
|
|
480
490
|
Usage:
|
|
481
|
-
parachute restart restart every
|
|
482
|
-
parachute restart <service> restart just that one
|
|
483
|
-
parachute restart hub restart the
|
|
491
|
+
parachute restart restart the hub unit (re-boots every module)
|
|
492
|
+
parachute restart <service> restart just that one module
|
|
493
|
+
parachute restart hub restart the hub unit
|
|
484
494
|
|
|
485
495
|
What it does:
|
|
486
|
-
|
|
496
|
+
- \`parachute restart <service>\` asks the running hub's supervisor to
|
|
497
|
+
restart that one module. If the module isn't currently supervised
|
|
498
|
+
(e.g. it crashed out of its restart budget), this falls through to a
|
|
499
|
+
fresh \`start\` so the verb is total over module state.
|
|
500
|
+
- \`parachute restart\` (no service) and \`parachute restart hub\` restart
|
|
501
|
+
the hub UNIT via the platform manager (\`launchctl kickstart -k\` /
|
|
502
|
+
\`systemctl restart\`), which re-boots every module — it is NOT a
|
|
503
|
+
fan-out of per-module restarts.
|
|
487
504
|
`;
|
|
488
505
|
}
|
|
489
506
|
|
|
@@ -523,6 +540,13 @@ What it does:
|
|
|
523
540
|
package.json version unchanged after bun add -g), the restart is skipped.
|
|
524
541
|
Re-running on an up-to-date install is a fast no-op.
|
|
525
542
|
|
|
543
|
+
The "restart" step matches the supervised model: upgrading a MODULE
|
|
544
|
+
restarts it via the running hub's supervisor; \`parachute upgrade hub\`
|
|
545
|
+
rewrites the binary on disk, then restarts the hub UNIT through the
|
|
546
|
+
platform manager (\`launchctl kickstart -k\` / \`systemctl restart\`), which
|
|
547
|
+
re-boots every module onto the new code. From the admin SPA (the no-CLI
|
|
548
|
+
Render / Fly path) the same hub-upgrade runs via POST /api/hub/upgrade.
|
|
549
|
+
|
|
526
550
|
Channel detection (hub#332):
|
|
527
551
|
Pre-1.0 governance ships two channels — \`@rc\` (the development chain) and
|
|
528
552
|
\`@latest\` (explicitly-promoted stable). \`parachute upgrade\` reads the
|
|
@@ -556,16 +580,23 @@ If no log file exists yet, prints a hint to \`parachute start <service>\`.
|
|
|
556
580
|
}
|
|
557
581
|
|
|
558
582
|
export function serveHelp(): string {
|
|
559
|
-
return `parachute serve — run the hub
|
|
583
|
+
return `parachute serve — run the hub + supervisor foregrounded (the runtime)
|
|
560
584
|
|
|
561
585
|
Usage:
|
|
562
586
|
parachute serve
|
|
563
587
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
588
|
+
This is the one runtime everywhere. The hub IS the foreground process: it
|
|
589
|
+
runs the HTTP server on port 1939 AND an in-process supervisor that spawns
|
|
590
|
+
every installed module as an attached child, multiplexes their logs into
|
|
591
|
+
its own stdout, and crash-restarts them on a budget. It runs until it gets
|
|
592
|
+
a signal, then SIGTERMs its children and exits.
|
|
593
|
+
|
|
594
|
+
You don't normally invoke \`serve\` by hand — \`parachute init\` (or
|
|
595
|
+
\`parachute migrate --to-supervised\`) installs it as a managed unit so your
|
|
596
|
+
platform's process manager keeps it alive across crashes and reboots:
|
|
597
|
+
launchd on a Mac, systemd on a Linux VM, the container runtime's CMD on
|
|
598
|
+
Render / Fly. Run it directly only for a foregrounded local debug, or on an
|
|
599
|
+
init-less host that can't host a unit.
|
|
569
600
|
|
|
570
601
|
Environment:
|
|
571
602
|
PORT bind port (default 1939). Render injects
|
|
@@ -598,12 +629,14 @@ Examples:
|
|
|
598
629
|
}
|
|
599
630
|
|
|
600
631
|
export function migrateHelp(): string {
|
|
601
|
-
return `parachute migrate — archive
|
|
632
|
+
return `parachute migrate — archive legacy root files, or cut over to the supervised model
|
|
602
633
|
|
|
603
634
|
Usage:
|
|
604
635
|
parachute migrate [--list] [--dry-run] [--yes]
|
|
636
|
+
parachute migrate --to-supervised
|
|
637
|
+
parachute migrate --teardown
|
|
605
638
|
|
|
606
|
-
What it does:
|
|
639
|
+
What it does (the default archive sweep):
|
|
607
640
|
Scans ~/.parachute/ for files and directories that match the
|
|
608
641
|
known-legacy allowlist (daily.db*, server.yaml, channel.log/err,
|
|
609
642
|
channel.start.sh, top-level logs/, tokens.db*, and the legacy lens/
|
|
@@ -619,9 +652,11 @@ What it does:
|
|
|
619
652
|
Dotfiles at the root (.env, .DS_Store, prior .archive-* dirs) are
|
|
620
653
|
never touched.
|
|
621
654
|
|
|
622
|
-
|
|
655
|
+
Archive-sweep safety:
|
|
623
656
|
- Refuses to sweep while any service is running — stop them first
|
|
624
|
-
(\`parachute stop\`) or preview with \`--list\`.
|
|
657
|
+
(\`parachute stop\`) or preview with \`--list\`. A hub running under a
|
|
658
|
+
process-manager unit (the supervised model) is detected as running
|
|
659
|
+
via the platform manager too, not just its pidfile.
|
|
625
660
|
- SQLite-shape files (\`*.db\`, \`*.db-wal\`, \`*.db-shm\`) get a
|
|
626
661
|
\`[live-db]\` label and pull an extra confirmation; wal/shm
|
|
627
662
|
consistency depends on all three moving together.
|
|
@@ -629,14 +664,39 @@ Safety:
|
|
|
629
664
|
\`[unknown — skipping]\`, with skipped items printed last.
|
|
630
665
|
- In a non-TTY shell (CI / piped), refuses without \`--yes\`.
|
|
631
666
|
|
|
667
|
+
--to-supervised (detached → supervised cutover):
|
|
668
|
+
Migrate a legacy detached install (independent \`parachute start\`-spawned
|
|
669
|
+
daemons) to the supervised model: the hub runs as \`parachute serve\`
|
|
670
|
+
under your platform's process manager (launchd on macOS, systemd on
|
|
671
|
+
Linux), survives reboots, and supervises modules as children. The
|
|
672
|
+
cutover is idempotent + re-runnable, and ordered so it never races the
|
|
673
|
+
canonical hub port: it writes the unit file WITHOUT starting it, stops
|
|
674
|
+
the detached hub + modules, sweeps any process still bound to a declared
|
|
675
|
+
port, verifies the ports are free, THEN starts the unit and verifies the
|
|
676
|
+
hub is healthy. If anything fails partway it leaves the box recoverable
|
|
677
|
+
(unit written but not started) and you can simply re-run it. A box with
|
|
678
|
+
no service manager (a container / init-less host) can't host a unit —
|
|
679
|
+
run \`parachute serve\` in the foreground there instead.
|
|
680
|
+
|
|
681
|
+
--teardown (cutover rollback):
|
|
682
|
+
Remove the hub process-manager unit. Idempotent + best-effort. Use it to
|
|
683
|
+
roll back a cutover: the unit is removed and you fall back to running the
|
|
684
|
+
hub with \`parachute serve\` (or re-run \`--to-supervised\` to reinstall it).
|
|
685
|
+
Run this BEFORE \`bun remove -g @openparachute/hub\` so a removed package
|
|
686
|
+
doesn't leave a unit pointing at a deleted binary.
|
|
687
|
+
|
|
632
688
|
Flags:
|
|
633
|
-
--list
|
|
634
|
-
--dry-run
|
|
635
|
-
--yes, -y
|
|
689
|
+
--list print the plan; make no changes (friendly preview)
|
|
690
|
+
--dry-run synonym for --list (kept for back-compat)
|
|
691
|
+
--yes, -y skip the confirmation prompt; required in non-TTY shells
|
|
692
|
+
--to-supervised cut over a detached install to the supervised model
|
|
693
|
+
--teardown remove the hub unit (cutover rollback)
|
|
636
694
|
|
|
637
695
|
Examples:
|
|
638
|
-
parachute migrate --list
|
|
639
|
-
parachute migrate
|
|
640
|
-
parachute migrate --yes
|
|
696
|
+
parachute migrate --list see what would move, without touching anything
|
|
697
|
+
parachute migrate interactive sweep (prompts before acting)
|
|
698
|
+
parachute migrate --yes sweep without prompting
|
|
699
|
+
parachute migrate --to-supervised move to the supervised (serve-under-manager) model
|
|
700
|
+
parachute migrate --teardown remove the hub unit (roll back the cutover)
|
|
641
701
|
`;
|
|
642
702
|
}
|