@cat-factory/cli 0.8.6 → 0.9.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 +65 -0
- package/dist/args.d.ts +21 -3
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +117 -0
- package/dist/args.js.map +1 -1
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -1
- package/dist/host-shell.d.ts +6 -0
- package/dist/host-shell.d.ts.map +1 -1
- package/dist/host-shell.js +1 -1
- package/dist/host-shell.js.map +1 -1
- package/dist/supervise-k3s.d.ts +50 -0
- package/dist/supervise-k3s.d.ts.map +1 -0
- package/dist/supervise-k3s.js +130 -0
- package/dist/supervise-k3s.js.map +1 -0
- package/dist/supervise-runtime.d.ts +169 -0
- package/dist/supervise-runtime.d.ts.map +1 -0
- package/dist/supervise-runtime.js +400 -0
- package/dist/supervise-runtime.js.map +1 -0
- package/dist/supervise.d.ts +147 -0
- package/dist/supervise.d.ts.map +1 -0
- package/dist/supervise.js +130 -0
- package/dist/supervise.js.map +1 -0
- package/dist/superviseCommand.d.ts +25 -0
- package/dist/superviseCommand.d.ts.map +1 -0
- package/dist/superviseCommand.js +136 -0
- package/dist/superviseCommand.js.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision core for `cat-factory supervise` — the self-healing local-dev supervisor.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. Every local deployment runs its server under `node --watch`, and
|
|
5
|
+
* `node --watch` PARKS on crash: it restarts the entry only on a FILE CHANGE, never on a process
|
|
6
|
+
* exit. A sleeping laptop is the common trigger — on resume the Postgres/Docker connection is
|
|
7
|
+
* gone, the server dies in `migrate`, and the watcher settles at "Waiting for file changes before
|
|
8
|
+
* restarting". The result is the worst kind of failure: the wrapper PID is still alive and the
|
|
9
|
+
* ready banner has already scrolled past, so the stack LOOKS running while nothing is bound to
|
|
10
|
+
* the port, and the SPA reports only a generic "can't reach backend". It never self-heals, and it
|
|
11
|
+
* stays that way until someone notices and restarts by hand.
|
|
12
|
+
*
|
|
13
|
+
* This module is the JUDGEMENT half of the fix, kept pure — no sockets, no processes, no ambient
|
|
14
|
+
* clock — so every transition is unit-testable from a table of observations (`supervise.test.ts`).
|
|
15
|
+
* `supervise-runtime.ts` owns the effects and feeds observations in. That split is the same one
|
|
16
|
+
* `scripts/silent-catch.mjs` documents for itself: a guard whose judgement nothing tests is a
|
|
17
|
+
* guard that is trusted without evidence.
|
|
18
|
+
*/
|
|
19
|
+
/** Tuning for the supervisor loop. Resolve partial input with {@link resolveSuperviseConfig}. */
|
|
20
|
+
export interface SuperviseConfig {
|
|
21
|
+
/** How often the health probe runs. */
|
|
22
|
+
pollMs: number;
|
|
23
|
+
/**
|
|
24
|
+
* Grace window after a (re)start during which a failed probe does NOT count against the child.
|
|
25
|
+
* A cold boot builds the workspace dependency and runs migrations first, so the port legitimately
|
|
26
|
+
* stays unbound for a while.
|
|
27
|
+
*/
|
|
28
|
+
bootGraceMs: number;
|
|
29
|
+
/** Grace window after a detected resume, so a still-waking Docker/Postgres isn't blamed. */
|
|
30
|
+
resumeGraceMs: number;
|
|
31
|
+
/**
|
|
32
|
+
* A tick arriving this much later than `pollMs` means time jumped — the host slept (or stalled
|
|
33
|
+
* hard). Timers do not fire while suspended, so lateness is the signal.
|
|
34
|
+
*
|
|
35
|
+
* The measurement is deliberately taken tick-START to tick-START (see {@link step}): sampling it
|
|
36
|
+
* after the probe would fold the probe's own duration into the drift, and a probe that times out
|
|
37
|
+
* on a filtered port takes seconds — enough to read as a suspend on a short `--poll` and so to
|
|
38
|
+
* bypass `failureThreshold` entirely.
|
|
39
|
+
*/
|
|
40
|
+
clockJumpMs: number;
|
|
41
|
+
/** Consecutive failed probes required before a repair (outside any grace window). */
|
|
42
|
+
failureThreshold: number;
|
|
43
|
+
/**
|
|
44
|
+
* How many restarts in a row may fail to produce a SERVING stack before the supervisor reports
|
|
45
|
+
* and gives up. A command that is simply broken (a syntax error, a missing binary, a port already
|
|
46
|
+
* owned by something else) can never be fixed by restarting it, and looping forever on it is the
|
|
47
|
+
* exact pathology this supervisor exists to end — the motivating incident was a container that
|
|
48
|
+
* restarted 518 times, exiting 0 each time, while `docker ps` showed healthy motion.
|
|
49
|
+
*/
|
|
50
|
+
maxFailedStarts: number;
|
|
51
|
+
}
|
|
52
|
+
/** Defaults chosen for a laptop-dev loop: notice within ~30s, never fight a cold boot. */
|
|
53
|
+
export declare const SUPERVISE_DEFAULTS: {
|
|
54
|
+
readonly pollMs: 10000;
|
|
55
|
+
readonly bootGraceMs: 60000;
|
|
56
|
+
readonly resumeGraceMs: 25000;
|
|
57
|
+
readonly failureThreshold: 3;
|
|
58
|
+
readonly maxFailedStarts: 5;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Fill in defaults and derive `clockJumpMs` from the poll interval. A tick 3 intervals late is
|
|
62
|
+
* well outside normal scheduler jitter but still catches a short suspend.
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveSuperviseConfig(partial?: Partial<SuperviseConfig>): SuperviseConfig;
|
|
65
|
+
/** Loop state carried between ticks. Treated as immutable: {@link step} returns the next one. */
|
|
66
|
+
export interface SuperviseState {
|
|
67
|
+
/** Consecutive failed probes so far, reset by any success or repair. */
|
|
68
|
+
failures: number;
|
|
69
|
+
/** No failed probe counts against the child until this timestamp (boot/resume grace). */
|
|
70
|
+
quietUntil: number;
|
|
71
|
+
/** When the previous tick ran — the basis for clock-jump (sleep) detection. */
|
|
72
|
+
lastTickAt: number;
|
|
73
|
+
}
|
|
74
|
+
/** What the runtime should do about this tick. Every branch of {@link step} names one. */
|
|
75
|
+
export type SuperviseAction =
|
|
76
|
+
/** Serving, and it was serving before too — nothing to say. */
|
|
77
|
+
{
|
|
78
|
+
kind: 'serving';
|
|
79
|
+
}
|
|
80
|
+
/** Serving again after one or more failed probes, without needing a repair. */
|
|
81
|
+
| {
|
|
82
|
+
kind: 'recovered';
|
|
83
|
+
afterFailures: number;
|
|
84
|
+
}
|
|
85
|
+
/** Not serving, but inside a boot/resume grace window — wait it out. */
|
|
86
|
+
| {
|
|
87
|
+
kind: 'grace';
|
|
88
|
+
msLeft: number;
|
|
89
|
+
}
|
|
90
|
+
/** Not serving; failure counted but still below the threshold. */
|
|
91
|
+
| {
|
|
92
|
+
kind: 'counting';
|
|
93
|
+
failures: number;
|
|
94
|
+
threshold: number;
|
|
95
|
+
}
|
|
96
|
+
/** Run the recovery ladder: re-check dependencies, then restart the child. */
|
|
97
|
+
| {
|
|
98
|
+
kind: 'repair';
|
|
99
|
+
reason: string;
|
|
100
|
+
}
|
|
101
|
+
/** The host resumed from sleep and the stack is still serving. */
|
|
102
|
+
| {
|
|
103
|
+
kind: 'resumed';
|
|
104
|
+
driftMs: number;
|
|
105
|
+
};
|
|
106
|
+
/** State for a freshly started child: clean counters and a full boot grace window. */
|
|
107
|
+
export declare function initialState(now: number, config: SuperviseConfig): SuperviseState;
|
|
108
|
+
/**
|
|
109
|
+
* State to adopt right after (re)spawning a child mid-run — a fresh boot grace, counters clear.
|
|
110
|
+
*
|
|
111
|
+
* `lastTickAt` is re-based on `now` (the moment the new child started), NOT carried over from the
|
|
112
|
+
* previous tick, because a repair is not instantaneous: it runs the whole dependency ladder first,
|
|
113
|
+
* and those budgets are 90s (compose readiness) and 120s (apiserver readiness) against a default
|
|
114
|
+
* `clockJumpMs` of 30s. Carrying the old timestamp forward makes the very next tick measure the
|
|
115
|
+
* repair's own duration as drift, read a slow-but-successful recovery as a host suspend, and — since
|
|
116
|
+
* resume detection deliberately outranks the boot-grace window — immediately kill the child it just
|
|
117
|
+
* started. Re-basing means the clock-jump signal only ever measures time we were genuinely idle.
|
|
118
|
+
*/
|
|
119
|
+
export declare function stateAfterStart(now: number, config: SuperviseConfig): SuperviseState;
|
|
120
|
+
/**
|
|
121
|
+
* One tick of the supervisor: current state + what we just observed -> next state + the action to
|
|
122
|
+
* take. Pure; the caller supplies `now` and the probe result.
|
|
123
|
+
*
|
|
124
|
+
* `now` must be sampled at the START of the tick, before the probe runs — see `clockJumpMs`.
|
|
125
|
+
*
|
|
126
|
+
* Order matters:
|
|
127
|
+
* 1. The clock-jump check runs FIRST and outranks the grace windows, because a resume is precisely
|
|
128
|
+
* when the stack is most likely already dead — deferring it to the normal threshold path would
|
|
129
|
+
* idle for another `failureThreshold * pollMs` before repairing something we can already tell
|
|
130
|
+
* is broken.
|
|
131
|
+
* 2. A confirmed-serving stack short-circuits everything below it.
|
|
132
|
+
* 3. A child that has EXITED then repairs immediately, ahead of the grace window and the failure
|
|
133
|
+
* counter, because neither can tell us anything a dead process handle hasn't already: counting
|
|
134
|
+
* three more probes against a process that does not exist just adds `failureThreshold * pollMs`
|
|
135
|
+
* of downtime. This is checked only once the stack is known not to be serving, so a wrapper that
|
|
136
|
+
* exits while its grandchild keeps serving (a shell that `exec`s away, say) is left alone
|
|
137
|
+
* rather than having a healthy server restarted out from under it.
|
|
138
|
+
*/
|
|
139
|
+
export declare function step(state: SuperviseState, observation: {
|
|
140
|
+
now: number;
|
|
141
|
+
serving: boolean;
|
|
142
|
+
childExited?: boolean;
|
|
143
|
+
}, config: SuperviseConfig): {
|
|
144
|
+
state: SuperviseState;
|
|
145
|
+
action: SuperviseAction;
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=supervise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervise.d.ts","sourceRoot":"","sources":["../src/supervise.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,iGAAiG;AACjG,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,4FAA4F;IAC5F,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;;;;OAQG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB;aAC7B,MAAM,EAAE,KAAM;aACd,WAAW,EAAE,KAAM;aACnB,aAAa,EAAE,KAAM;aACrB,gBAAgB,EAAE,CAAC;aACnB,eAAe,EAAE,CAAC;CACV,CAAA;AAEV;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,OAAO,CAAC,eAAe,CAAM,GAAG,eAAe,CAU9F;AAED,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAA;IAChB,yFAAyF;IACzF,UAAU,EAAE,MAAM,CAAA;IAClB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,0FAA0F;AAC1F,MAAM,MAAM,eAAe;AACzB,+DAA+D;AAC7D;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE;AACrB,+EAA+E;GAC7E;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;AAC9C,wEAAwE;GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACnC,kEAAkE;GAChE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAC3D,8EAA8E;GAC5E;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACpC,kEAAkE;GAChE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAExC,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,cAAc,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,cAAc,CAEpF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,EACrE,MAAM,EAAE,eAAe,GACtB;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,CAwDpD"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision core for `cat-factory supervise` — the self-healing local-dev supervisor.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. Every local deployment runs its server under `node --watch`, and
|
|
5
|
+
* `node --watch` PARKS on crash: it restarts the entry only on a FILE CHANGE, never on a process
|
|
6
|
+
* exit. A sleeping laptop is the common trigger — on resume the Postgres/Docker connection is
|
|
7
|
+
* gone, the server dies in `migrate`, and the watcher settles at "Waiting for file changes before
|
|
8
|
+
* restarting". The result is the worst kind of failure: the wrapper PID is still alive and the
|
|
9
|
+
* ready banner has already scrolled past, so the stack LOOKS running while nothing is bound to
|
|
10
|
+
* the port, and the SPA reports only a generic "can't reach backend". It never self-heals, and it
|
|
11
|
+
* stays that way until someone notices and restarts by hand.
|
|
12
|
+
*
|
|
13
|
+
* This module is the JUDGEMENT half of the fix, kept pure — no sockets, no processes, no ambient
|
|
14
|
+
* clock — so every transition is unit-testable from a table of observations (`supervise.test.ts`).
|
|
15
|
+
* `supervise-runtime.ts` owns the effects and feeds observations in. That split is the same one
|
|
16
|
+
* `scripts/silent-catch.mjs` documents for itself: a guard whose judgement nothing tests is a
|
|
17
|
+
* guard that is trusted without evidence.
|
|
18
|
+
*/
|
|
19
|
+
/** Defaults chosen for a laptop-dev loop: notice within ~30s, never fight a cold boot. */
|
|
20
|
+
export const SUPERVISE_DEFAULTS = {
|
|
21
|
+
pollMs: 10_000,
|
|
22
|
+
bootGraceMs: 60_000,
|
|
23
|
+
resumeGraceMs: 25_000,
|
|
24
|
+
failureThreshold: 3,
|
|
25
|
+
maxFailedStarts: 5,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Fill in defaults and derive `clockJumpMs` from the poll interval. A tick 3 intervals late is
|
|
29
|
+
* well outside normal scheduler jitter but still catches a short suspend.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveSuperviseConfig(partial = {}) {
|
|
32
|
+
const pollMs = partial.pollMs ?? SUPERVISE_DEFAULTS.pollMs;
|
|
33
|
+
return {
|
|
34
|
+
pollMs,
|
|
35
|
+
bootGraceMs: partial.bootGraceMs ?? SUPERVISE_DEFAULTS.bootGraceMs,
|
|
36
|
+
resumeGraceMs: partial.resumeGraceMs ?? SUPERVISE_DEFAULTS.resumeGraceMs,
|
|
37
|
+
clockJumpMs: partial.clockJumpMs ?? pollMs * 3,
|
|
38
|
+
failureThreshold: partial.failureThreshold ?? SUPERVISE_DEFAULTS.failureThreshold,
|
|
39
|
+
maxFailedStarts: partial.maxFailedStarts ?? SUPERVISE_DEFAULTS.maxFailedStarts,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** State for a freshly started child: clean counters and a full boot grace window. */
|
|
43
|
+
export function initialState(now, config) {
|
|
44
|
+
return { failures: 0, quietUntil: now + config.bootGraceMs, lastTickAt: now };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* State to adopt right after (re)spawning a child mid-run — a fresh boot grace, counters clear.
|
|
48
|
+
*
|
|
49
|
+
* `lastTickAt` is re-based on `now` (the moment the new child started), NOT carried over from the
|
|
50
|
+
* previous tick, because a repair is not instantaneous: it runs the whole dependency ladder first,
|
|
51
|
+
* and those budgets are 90s (compose readiness) and 120s (apiserver readiness) against a default
|
|
52
|
+
* `clockJumpMs` of 30s. Carrying the old timestamp forward makes the very next tick measure the
|
|
53
|
+
* repair's own duration as drift, read a slow-but-successful recovery as a host suspend, and — since
|
|
54
|
+
* resume detection deliberately outranks the boot-grace window — immediately kill the child it just
|
|
55
|
+
* started. Re-basing means the clock-jump signal only ever measures time we were genuinely idle.
|
|
56
|
+
*/
|
|
57
|
+
export function stateAfterStart(now, config) {
|
|
58
|
+
return { failures: 0, quietUntil: now + config.bootGraceMs, lastTickAt: now };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* One tick of the supervisor: current state + what we just observed -> next state + the action to
|
|
62
|
+
* take. Pure; the caller supplies `now` and the probe result.
|
|
63
|
+
*
|
|
64
|
+
* `now` must be sampled at the START of the tick, before the probe runs — see `clockJumpMs`.
|
|
65
|
+
*
|
|
66
|
+
* Order matters:
|
|
67
|
+
* 1. The clock-jump check runs FIRST and outranks the grace windows, because a resume is precisely
|
|
68
|
+
* when the stack is most likely already dead — deferring it to the normal threshold path would
|
|
69
|
+
* idle for another `failureThreshold * pollMs` before repairing something we can already tell
|
|
70
|
+
* is broken.
|
|
71
|
+
* 2. A confirmed-serving stack short-circuits everything below it.
|
|
72
|
+
* 3. A child that has EXITED then repairs immediately, ahead of the grace window and the failure
|
|
73
|
+
* counter, because neither can tell us anything a dead process handle hasn't already: counting
|
|
74
|
+
* three more probes against a process that does not exist just adds `failureThreshold * pollMs`
|
|
75
|
+
* of downtime. This is checked only once the stack is known not to be serving, so a wrapper that
|
|
76
|
+
* exits while its grandchild keeps serving (a shell that `exec`s away, say) is left alone
|
|
77
|
+
* rather than having a healthy server restarted out from under it.
|
|
78
|
+
*/
|
|
79
|
+
export function step(state, observation, config) {
|
|
80
|
+
const { now, serving } = observation;
|
|
81
|
+
const driftMs = now - state.lastTickAt - config.pollMs;
|
|
82
|
+
if (driftMs > config.clockJumpMs) {
|
|
83
|
+
// Timers stalled, so wall-clock time passed without us running: the host suspended. Extend the
|
|
84
|
+
// quiet window either way — a resume needs a moment for Docker's VM and the DB to come back.
|
|
85
|
+
const quietUntil = Math.max(state.quietUntil, now + config.resumeGraceMs);
|
|
86
|
+
if (serving) {
|
|
87
|
+
return {
|
|
88
|
+
state: { failures: 0, quietUntil, lastTickAt: now },
|
|
89
|
+
action: { kind: 'resumed', driftMs },
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
state: { failures: 0, quietUntil, lastTickAt: now },
|
|
94
|
+
action: {
|
|
95
|
+
kind: 'repair',
|
|
96
|
+
reason: `not serving after a ${Math.round(driftMs / 1000)}s stall (host slept?)`,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (serving) {
|
|
101
|
+
const action = state.failures > 0
|
|
102
|
+
? { kind: 'recovered', afterFailures: state.failures }
|
|
103
|
+
: { kind: 'serving' };
|
|
104
|
+
return { state: { failures: 0, quietUntil: state.quietUntil, lastTickAt: now }, action };
|
|
105
|
+
}
|
|
106
|
+
if (observation.childExited === true) {
|
|
107
|
+
return {
|
|
108
|
+
state: { failures: 0, quietUntil: state.quietUntil, lastTickAt: now },
|
|
109
|
+
action: { kind: 'repair', reason: 'the supervised command exited' },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (now < state.quietUntil) {
|
|
113
|
+
return {
|
|
114
|
+
state: { ...state, lastTickAt: now },
|
|
115
|
+
action: { kind: 'grace', msLeft: state.quietUntil - now },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const failures = state.failures + 1;
|
|
119
|
+
if (failures >= config.failureThreshold) {
|
|
120
|
+
return {
|
|
121
|
+
state: { failures: 0, quietUntil: state.quietUntil, lastTickAt: now },
|
|
122
|
+
action: { kind: 'repair', reason: `${failures} consecutive failed health probes` },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
state: { failures, quietUntil: state.quietUntil, lastTickAt: now },
|
|
127
|
+
action: { kind: 'counting', failures, threshold: config.failureThreshold },
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=supervise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervise.js","sourceRoot":"","sources":["../src/supervise.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cat-factory supervise` — wire the real effects into the supervision loop.
|
|
3
|
+
*
|
|
4
|
+
* Thin on purpose: the judgement lives in `supervise.ts`, the effects in `supervise-runtime.ts`.
|
|
5
|
+
* This resolves flags into a config, builds the process/socket/shell-backed seams, prints what it
|
|
6
|
+
* is about to watch, and hands over to `runSupervisor`.
|
|
7
|
+
*/
|
|
8
|
+
import { type CliOptions } from './args.js';
|
|
9
|
+
/**
|
|
10
|
+
* Re-quote a token that contains whitespace. The child is launched as one shell string (see
|
|
11
|
+
* `createChildLauncher`), so a path with a space in it would otherwise split into two arguments —
|
|
12
|
+
* `C:\Program Files\nodejs\node.exe` being the case that matters on Windows.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately NOT `JSON.stringify`: that escapes backslashes (`C:\\Program Files\\…`), which no
|
|
15
|
+
* shell unescapes, so the quoted path becomes a path that does not exist. Only the surrounding
|
|
16
|
+
* quotes and any embedded quote need handling.
|
|
17
|
+
*
|
|
18
|
+
* The escape for an embedded quote is platform-specific, because `shell: true` means a genuinely
|
|
19
|
+
* different shell on each side: `cmd.exe` does not honour a backslash escape at all (it would pass
|
|
20
|
+
* the backslash through and treat the quote as closing the argument), and doubles the quote instead.
|
|
21
|
+
* `platform` is injectable so both dialects are testable from either host.
|
|
22
|
+
*/
|
|
23
|
+
export declare function quoteToken(token: string, platform?: string): string;
|
|
24
|
+
export declare function supervise(options: CliOptions): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=superviseCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superviseCommand.d.ts","sourceRoot":"","sources":["../src/superviseCommand.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAY,KAAK,UAAU,EAAoC,MAAM,WAAW,CAAA;AAcvF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAMrF;AAkBD,wBAAsB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGlE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cat-factory supervise` — wire the real effects into the supervision loop.
|
|
3
|
+
*
|
|
4
|
+
* Thin on purpose: the judgement lives in `supervise.ts`, the effects in `supervise-runtime.ts`.
|
|
5
|
+
* This resolves flags into a config, builds the process/socket/shell-backed seams, prints what it
|
|
6
|
+
* is about to watch, and hands over to `runSupervisor`.
|
|
7
|
+
*/
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { ArgError, OPTION_DEFAULTS } from './args.js';
|
|
10
|
+
import { createNodeShell } from './host-shell.js';
|
|
11
|
+
import { createK3sClusterDependency } from './supervise-k3s.js';
|
|
12
|
+
import { createChildLauncher, createComposeDependency, createHealthProbe, createPortReaper, OperatorActionRequiredError, runSupervisor, } from './supervise-runtime.js';
|
|
13
|
+
import { resolveSuperviseConfig } from './supervise.js';
|
|
14
|
+
/**
|
|
15
|
+
* Re-quote a token that contains whitespace. The child is launched as one shell string (see
|
|
16
|
+
* `createChildLauncher`), so a path with a space in it would otherwise split into two arguments —
|
|
17
|
+
* `C:\Program Files\nodejs\node.exe` being the case that matters on Windows.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT `JSON.stringify`: that escapes backslashes (`C:\\Program Files\\…`), which no
|
|
20
|
+
* shell unescapes, so the quoted path becomes a path that does not exist. Only the surrounding
|
|
21
|
+
* quotes and any embedded quote need handling.
|
|
22
|
+
*
|
|
23
|
+
* The escape for an embedded quote is platform-specific, because `shell: true` means a genuinely
|
|
24
|
+
* different shell on each side: `cmd.exe` does not honour a backslash escape at all (it would pass
|
|
25
|
+
* the backslash through and treat the quote as closing the argument), and doubles the quote instead.
|
|
26
|
+
* `platform` is injectable so both dialects are testable from either host.
|
|
27
|
+
*/
|
|
28
|
+
export function quoteToken(token, platform = process.platform) {
|
|
29
|
+
if (token === '')
|
|
30
|
+
return '""';
|
|
31
|
+
if (!/[\s"]/.test(token))
|
|
32
|
+
return token;
|
|
33
|
+
const escaped = platform === 'win32' ? token.replace(/"/g, '""') : token.replace(/"/g, String.raw `\"`);
|
|
34
|
+
return `"${escaped}"`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Narrow the shared `--runtime` picklist to what a supervisor can actually start, REFUSING the
|
|
38
|
+
* third member rather than quietly treating it as k3d. `k3s` proper is a host service (systemd), not
|
|
39
|
+
* a set of containers this command owns, so it has no `cluster start` to call. Degrading silently
|
|
40
|
+
* would leave `k3d cluster list` never listing the cluster, so the dependency would report "not
|
|
41
|
+
* ready — will retry next cycle" on every cycle forever, with nothing naming the real reason.
|
|
42
|
+
*/
|
|
43
|
+
function supervisedRuntime(runtime) {
|
|
44
|
+
if (runtime === undefined)
|
|
45
|
+
return 'k3d';
|
|
46
|
+
if (runtime === 'k3d' || runtime === 'kind')
|
|
47
|
+
return runtime;
|
|
48
|
+
throw new ArgError(`--runtime ${runtime} cannot be supervised: a k3s host service has no cluster for this command ` +
|
|
49
|
+
'to start. Use --runtime k3d or --runtime kind, or drop --k3s-cluster.');
|
|
50
|
+
}
|
|
51
|
+
export async function supervise(options) {
|
|
52
|
+
const argv = options.superviseCommand ?? [];
|
|
53
|
+
if (argv.length === 0) {
|
|
54
|
+
throw new ArgError('supervise needs a command to run, after `--`\n' +
|
|
55
|
+
' e.g. cat-factory supervise --port 8787 -- pnpm dev');
|
|
56
|
+
}
|
|
57
|
+
// Not `argv.map(quoteToken)`: `map` would pass the index as the platform argument.
|
|
58
|
+
const command = argv.map((token) => quoteToken(token)).join(' ');
|
|
59
|
+
const cwd = options.dir ? resolve(options.dir) : process.cwd();
|
|
60
|
+
const port = options.port ?? OPTION_DEFAULTS.port;
|
|
61
|
+
const healthPath = options.healthPath ?? OPTION_DEFAULTS.healthPath;
|
|
62
|
+
const config = resolveSuperviseConfig({
|
|
63
|
+
pollMs: options.pollSeconds !== undefined ? options.pollSeconds * 1_000 : undefined,
|
|
64
|
+
bootGraceMs: options.bootGraceSeconds !== undefined ? options.bootGraceSeconds * 1_000 : undefined,
|
|
65
|
+
failureThreshold: options.failures,
|
|
66
|
+
});
|
|
67
|
+
const shell = createNodeShell();
|
|
68
|
+
// Only wire the dependencies that were named — the supervisor is useful with none at all. Order
|
|
69
|
+
// matters: the database comes first because the server dies in `migrate` without it, while a dead
|
|
70
|
+
// cluster only breaks environment provisioning.
|
|
71
|
+
const dependencies = [];
|
|
72
|
+
if (options.composeService) {
|
|
73
|
+
dependencies.push(createComposeDependency(shell, {
|
|
74
|
+
dir: options.composeDir ? resolve(options.composeDir) : cwd,
|
|
75
|
+
service: options.composeService,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
if (options.k3sCluster) {
|
|
79
|
+
dependencies.push(createK3sClusterDependency(shell, {
|
|
80
|
+
cluster: options.k3sCluster,
|
|
81
|
+
runtime: supervisedRuntime(options.k3sRuntime),
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
const launcher = createChildLauncher({ command, cwd });
|
|
85
|
+
const probe = createHealthProbe({ port, healthPath });
|
|
86
|
+
const log = (message) => {
|
|
87
|
+
process.stdout.write(`[supervise] ${message}\n`);
|
|
88
|
+
};
|
|
89
|
+
const reaper = createPortReaper(shell, port, { log });
|
|
90
|
+
log(`watching :${port}${healthPath} every ${config.pollMs / 1_000}s — ` +
|
|
91
|
+
`repairs after ${config.failureThreshold} failed probes or a detected resume`);
|
|
92
|
+
for (const dependency of dependencies)
|
|
93
|
+
log(`dependency: ${dependency.label}`);
|
|
94
|
+
log(`command: ${command}`);
|
|
95
|
+
// Bring the dependencies up BEFORE the first start, so a cold `pnpm dev:safe` on a machine whose
|
|
96
|
+
// engine was restarted doesn't spend its first boot crashing against a stopped database.
|
|
97
|
+
for (const dependency of dependencies) {
|
|
98
|
+
try {
|
|
99
|
+
const ready = await dependency.ensure();
|
|
100
|
+
log(ready
|
|
101
|
+
? `✔ ${dependency.label} is ready`
|
|
102
|
+
: `✖ ${dependency.label} is not ready — starting anyway`);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (!(err instanceof OperatorActionRequiredError))
|
|
106
|
+
throw err;
|
|
107
|
+
log(`✖ ${dependency.label} NEEDS YOU: ${err.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Shutdown is delegated to the loop, which owns the child handle: aborting makes it break out of
|
|
111
|
+
// its sleep, kill the child TREE, and reap the port. Reaping from here instead would kill the
|
|
112
|
+
// inner listener while leaving the package-manager wrapper and its parked `node --watch` alive.
|
|
113
|
+
const stopper = new AbortController();
|
|
114
|
+
const stop = () => {
|
|
115
|
+
if (stopper.signal.aborted)
|
|
116
|
+
return;
|
|
117
|
+
log('shutting down');
|
|
118
|
+
stopper.abort();
|
|
119
|
+
};
|
|
120
|
+
process.on('SIGINT', stop);
|
|
121
|
+
process.on('SIGTERM', stop);
|
|
122
|
+
const outcome = await runSupervisor({
|
|
123
|
+
config,
|
|
124
|
+
probe,
|
|
125
|
+
launcher,
|
|
126
|
+
dependencies,
|
|
127
|
+
reaper,
|
|
128
|
+
log,
|
|
129
|
+
stopSignal: stopper.signal,
|
|
130
|
+
});
|
|
131
|
+
// A supervisor that stopped because the command is broken must not report success — a wrapper
|
|
132
|
+
// exiting 0 on a dead stack is the failure shape this whole command exists to make impossible.
|
|
133
|
+
if (outcome.gaveUp !== undefined)
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=superviseCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superviseCommand.js","sourceRoot":"","sources":["../src/superviseCommand.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Bootstrap CLI for the Agent Architecture Board: scaffold a local-mode deployment (Node/local backend + frontend SPA) on your own machine — generates the crypto secrets, populates and gitignores the .env files, and mints a GitHub/GitLab personal access token by opening the browser at the right pre-scoped URL.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bootstrap",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"typescript": "7.0.2",
|
|
42
42
|
"valibot": "^1.4.2",
|
|
43
43
|
"vitest": "^4.1.10",
|
|
44
|
-
"@cat-factory/contracts": "0.
|
|
45
|
-
"@cat-factory/kernel": "0.
|
|
44
|
+
"@cat-factory/contracts": "0.197.0",
|
|
45
|
+
"@cat-factory/kernel": "0.194.0"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsc -b tsconfig.build.json",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
51
51
|
"test": "vitest",
|
|
52
52
|
"test:run": "vitest run",
|
|
53
|
-
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
53
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
54
|
+
"test:supervise": "vitest run --config vitest.supervise.config.ts"
|
|
54
55
|
}
|
|
55
56
|
}
|