wicked-crew 0.5.0 → 0.7.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/dist/api/audit.d.ts +70 -0
- package/dist/api/audit.d.ts.map +1 -0
- package/dist/api/audit.js +131 -0
- package/dist/api/audit.js.map +1 -0
- package/dist/api/auth.d.ts +192 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +515 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/gate-cache.d.ts +8 -0
- package/dist/api/gate-cache.d.ts.map +1 -1
- package/dist/api/gate-cache.js +10 -0
- package/dist/api/gate-cache.js.map +1 -1
- package/dist/api/guidance-index.d.ts +39 -0
- package/dist/api/guidance-index.d.ts.map +1 -0
- package/dist/api/guidance-index.js +67 -0
- package/dist/api/guidance-index.js.map +1 -0
- package/dist/api/open-path.d.ts +34 -0
- package/dist/api/open-path.d.ts.map +1 -0
- package/dist/api/open-path.js +101 -0
- package/dist/api/open-path.js.map +1 -0
- package/dist/api/retry-index.d.ts +30 -0
- package/dist/api/retry-index.d.ts.map +1 -0
- package/dist/api/retry-index.js +45 -0
- package/dist/api/retry-index.js.map +1 -0
- package/dist/api/routes.d.ts +164 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +769 -21
- package/dist/api/routes.js.map +1 -1
- package/dist/api/run-files.d.ts +63 -0
- package/dist/api/run-files.d.ts.map +1 -0
- package/dist/api/run-files.js +271 -0
- package/dist/api/run-files.js.map +1 -0
- package/dist/api/seat-health.d.ts +55 -0
- package/dist/api/seat-health.d.ts.map +1 -0
- package/dist/api/seat-health.js +273 -0
- package/dist/api/seat-health.js.map +1 -0
- package/dist/api/seat-signin.d.ts +27 -0
- package/dist/api/seat-signin.d.ts.map +1 -0
- package/dist/api/seat-signin.js +143 -0
- package/dist/api/seat-signin.js.map +1 -0
- package/dist/api/server.d.ts +190 -3
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +365 -11
- package/dist/api/server.js.map +1 -1
- package/dist/api/stall-watchdog.d.ts +62 -0
- package/dist/api/stall-watchdog.d.ts.map +1 -0
- package/dist/api/stall-watchdog.js +138 -0
- package/dist/api/stall-watchdog.js.map +1 -0
- package/dist/cli/index.js +171 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp.d.ts +14 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +119 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/core/adapter.d.ts +87 -10
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +347 -28
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/bridge-reaper.d.ts +134 -0
- package/dist/core/bridge-reaper.d.ts.map +1 -0
- package/dist/core/bridge-reaper.js +286 -0
- package/dist/core/bridge-reaper.js.map +1 -0
- package/dist/core/deliver.d.ts +118 -0
- package/dist/core/deliver.d.ts.map +1 -0
- package/dist/core/deliver.js +241 -0
- package/dist/core/deliver.js.map +1 -0
- package/dist/core/deliverable-floor.d.ts +103 -0
- package/dist/core/deliverable-floor.d.ts.map +1 -0
- package/dist/core/deliverable-floor.js +173 -0
- package/dist/core/deliverable-floor.js.map +1 -0
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/types.d.ts +92 -352
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +14 -4
- package/dist/core/types.js.map +1 -1
- package/dist/interactive/bridge-pool.d.ts +99 -0
- package/dist/interactive/bridge-pool.d.ts.map +1 -0
- package/dist/interactive/bridge-pool.js +244 -0
- package/dist/interactive/bridge-pool.js.map +1 -0
- package/dist/interactive/bridge-root.d.ts +36 -0
- package/dist/interactive/bridge-root.d.ts.map +1 -0
- package/dist/interactive/bridge-root.js +48 -0
- package/dist/interactive/bridge-root.js.map +1 -0
- package/dist/interactive/chat-events.d.ts +207 -0
- package/dist/interactive/chat-events.d.ts.map +1 -0
- package/dist/interactive/chat-events.js +769 -0
- package/dist/interactive/chat-events.js.map +1 -0
- package/dist/interactive/demo-events.d.ts +283 -0
- package/dist/interactive/demo-events.d.ts.map +1 -0
- package/dist/interactive/demo-events.js +889 -0
- package/dist/interactive/demo-events.js.map +1 -0
- package/dist/interactive/draft-events.d.ts +224 -0
- package/dist/interactive/draft-events.d.ts.map +1 -0
- package/dist/interactive/draft-events.js +793 -0
- package/dist/interactive/draft-events.js.map +1 -0
- package/dist/interactive/edit-events.d.ts +194 -0
- package/dist/interactive/edit-events.d.ts.map +1 -0
- package/dist/interactive/edit-events.js +601 -0
- package/dist/interactive/edit-events.js.map +1 -0
- package/dist/interactive/ledger.d.ts +39 -0
- package/dist/interactive/ledger.d.ts.map +1 -0
- package/dist/interactive/ledger.js +93 -0
- package/dist/interactive/ledger.js.map +1 -0
- package/dist/interactive/proxy-routes.d.ts +39 -0
- package/dist/interactive/proxy-routes.d.ts.map +1 -0
- package/dist/interactive/proxy-routes.js +189 -0
- package/dist/interactive/proxy-routes.js.map +1 -0
- package/dist/interactive/repo-snapshot.d.ts +100 -0
- package/dist/interactive/repo-snapshot.d.ts.map +1 -0
- package/dist/interactive/repo-snapshot.js +289 -0
- package/dist/interactive/repo-snapshot.js.map +1 -0
- package/dist/interactive/ws-relay.d.ts +85 -0
- package/dist/interactive/ws-relay.d.ts.map +1 -0
- package/dist/interactive/ws-relay.js +191 -0
- package/dist/interactive/ws-relay.js.map +1 -0
- package/dist/projects/activity.d.ts +29 -0
- package/dist/projects/activity.d.ts.map +1 -0
- package/dist/projects/activity.js +172 -0
- package/dist/projects/activity.js.map +1 -0
- package/dist/projects/charter.d.ts +28 -0
- package/dist/projects/charter.d.ts.map +1 -0
- package/dist/projects/charter.js +53 -0
- package/dist/projects/charter.js.map +1 -0
- package/dist/projects/events.d.ts +55 -0
- package/dist/projects/events.d.ts.map +1 -0
- package/dist/projects/events.js +141 -0
- package/dist/projects/events.js.map +1 -0
- package/dist/projects/graph-paths.d.ts +92 -0
- package/dist/projects/graph-paths.d.ts.map +1 -0
- package/dist/projects/graph-paths.js +130 -0
- package/dist/projects/graph-paths.js.map +1 -0
- package/dist/projects/graph.d.ts +179 -0
- package/dist/projects/graph.d.ts.map +1 -0
- package/dist/projects/graph.js +775 -0
- package/dist/projects/graph.js.map +1 -0
- package/dist/projects/membership-index.d.ts +25 -0
- package/dist/projects/membership-index.d.ts.map +1 -0
- package/dist/projects/membership-index.js +46 -0
- package/dist/projects/membership-index.js.map +1 -0
- package/dist/projects/routes.d.ts +97 -0
- package/dist/projects/routes.d.ts.map +1 -0
- package/dist/projects/routes.js +510 -0
- package/dist/projects/routes.js.map +1 -0
- package/dist/projects/settings.d.ts +32 -0
- package/dist/projects/settings.d.ts.map +1 -0
- package/dist/projects/settings.js +64 -0
- package/dist/projects/settings.js.map +1 -0
- package/dist/qe/acceptance.d.ts +137 -0
- package/dist/qe/acceptance.d.ts.map +1 -0
- package/dist/qe/acceptance.js +249 -0
- package/dist/qe/acceptance.js.map +1 -0
- package/dist/qe/gate-events.d.ts +111 -0
- package/dist/qe/gate-events.d.ts.map +1 -0
- package/dist/qe/gate-events.js +168 -0
- package/dist/qe/gate-events.js.map +1 -0
- package/dist/qe/ledger.d.ts +100 -0
- package/dist/qe/ledger.d.ts.map +1 -0
- package/dist/qe/ledger.js +154 -0
- package/dist/qe/ledger.js.map +1 -0
- package/dist/studio/assets/index-8p8uwCxG.js +530 -0
- package/dist/studio/assets/index-D6S9zUtO.css +32 -0
- package/dist/studio/index.html +5 -3
- package/package.json +10 -4
- package/dist/studio/assets/index-DaaUU8Ep.css +0 -32
- package/dist/studio/assets/index-Fu5DRC00.js +0 -423
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// GET /runs/:id/files + GET /runs/:id/diff machinery (DES-FEEDBACK-002 CREW-1).
|
|
2
|
+
//
|
|
3
|
+
// The studio's in-app file & diff viewer cannot read the daemon host's filesystem, so the daemon
|
|
4
|
+
// serves the bytes — which is exactly why every path is contained FIRST against the same root set
|
|
5
|
+
// `POST /open` validates (`allowedRootsFor` + `isInsideRoot`, open-path.ts), every payload is
|
|
6
|
+
// capped, and git runs through `execCapped` with argv arrays only (never a shell string) and
|
|
7
|
+
// `--no-ext-diff` (a configured external diff driver must never execute here). Both routes are
|
|
8
|
+
// read-only by construction: `fs` reads and `git diff`/`git status` — zero write capability, a
|
|
9
|
+
// strictly smaller threat surface than `/open` handing the path to an OS opener.
|
|
10
|
+
import { promises as fsp } from 'node:fs';
|
|
11
|
+
import { execCapped, ExecOutputTooLarge } from '../core/exec.js';
|
|
12
|
+
/** File-content cap (DES-FEEDBACK-002 §3.3): past this, `content` holds the first 512 KB and
|
|
13
|
+
* `truncated: true` — the studio renders a labeled truncation banner, never a silent amputation. */
|
|
14
|
+
export const FILE_CONTENT_CAP_BYTES = 512 * 1024;
|
|
15
|
+
/** Binary sniff window: a NUL byte in the first 8 KB ⇒ `binary: true`, `content: ""` — the studio
|
|
16
|
+
* falls back to the external-open affordance rather than rendering mojibake. */
|
|
17
|
+
export const BINARY_SNIFF_BYTES = 8 * 1024;
|
|
18
|
+
/** Diff output cap (§3.3): past this the diff is cut at 1 MB and `truncated: true`. */
|
|
19
|
+
export const DIFF_OUTPUT_CAP_BYTES = 1024 * 1024;
|
|
20
|
+
/** The target exists but is not a regular file (directory, socket, …) — the route's 400, distinct
|
|
21
|
+
* from ENOENT's 404. Serving directory listings is explicitly out of scope (§3.3). */
|
|
22
|
+
export class NotARegularFileError extends Error {
|
|
23
|
+
path;
|
|
24
|
+
constructor(path) {
|
|
25
|
+
super(`not a regular file: ${path}`);
|
|
26
|
+
this.path = path;
|
|
27
|
+
this.name = 'NotARegularFileError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Read at most `FILE_CONTENT_CAP_BYTES` of `target`. The open-then-fstat order (not stat-then-open)
|
|
32
|
+
* closes the classic swap race: the size and the bytes come from the SAME open file description.
|
|
33
|
+
* Callers must have contained `target` already — this function does filesystem work only.
|
|
34
|
+
*/
|
|
35
|
+
export async function readFileCapped(target) {
|
|
36
|
+
const fh = await fsp.open(target, 'r');
|
|
37
|
+
try {
|
|
38
|
+
const st = await fh.stat();
|
|
39
|
+
if (!st.isFile())
|
|
40
|
+
throw new NotARegularFileError(target);
|
|
41
|
+
const size = st.size;
|
|
42
|
+
const toRead = Math.min(size, FILE_CONTENT_CAP_BYTES);
|
|
43
|
+
const buf = Buffer.alloc(toRead);
|
|
44
|
+
let off = 0;
|
|
45
|
+
while (off < toRead) {
|
|
46
|
+
const { bytesRead } = await fh.read(buf, off, toRead - off, off);
|
|
47
|
+
if (bytesRead === 0)
|
|
48
|
+
break; // file shrank underneath us — serve what exists
|
|
49
|
+
off += bytesRead;
|
|
50
|
+
}
|
|
51
|
+
const got = buf.subarray(0, off);
|
|
52
|
+
if (got.subarray(0, Math.min(off, BINARY_SNIFF_BYTES)).includes(0)) {
|
|
53
|
+
return { content: '', size, truncated: false, binary: true };
|
|
54
|
+
}
|
|
55
|
+
return { content: got.toString('utf8'), size, truncated: size > FILE_CONTENT_CAP_BYTES, binary: false };
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
await fh.close();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const GIT_TIMEOUT_MS = 10_000;
|
|
62
|
+
// ── Diff base (DES-UX-001 §8.1, CREW-UX-1) ─────────────────────────────────────────────────────
|
|
63
|
+
/** `?base=merge-base` — the one non-ref literal: diff against `git merge-base <default-branch>
|
|
64
|
+
* HEAD`, i.e. the run branch's fork point, so committed run work is visible (§8.1). */
|
|
65
|
+
export const MERGE_BASE_LITERAL = 'merge-base';
|
|
66
|
+
/** `base` is not a plain ref — a flag, a path, a revision range, a separator. Rejected BEFORE any
|
|
67
|
+
* git process sees it: the query parameter is a diff baseline, never a command surface (§8.1). */
|
|
68
|
+
export class InvalidDiffBaseError extends Error {
|
|
69
|
+
base;
|
|
70
|
+
constructor(base) {
|
|
71
|
+
super('`base` must be a plain git ref (branch, tag, or commit id) — ' +
|
|
72
|
+
'flags, paths, revision ranges, and separators are rejected');
|
|
73
|
+
this.base = base;
|
|
74
|
+
this.name = 'InvalidDiffBaseError';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** `base` is well-formed but does not resolve to a commit inside the run's repo — containment:
|
|
78
|
+
* a ref from another repo (or thin air) is a 400, never a git error surfaced as a 500. */
|
|
79
|
+
export class UnresolvableDiffBaseError extends Error {
|
|
80
|
+
base;
|
|
81
|
+
constructor(base, detail) {
|
|
82
|
+
super(`unresolvable \`base\`: ${detail}`);
|
|
83
|
+
this.base = base;
|
|
84
|
+
this.name = 'UnresolvableDiffBaseError';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** A plain ref: git's own charset for the safe subset — alphanumeric start (excludes `-flag`,
|
|
88
|
+
* `.hidden`, `/abs/path`), then word/dot/slash/dash. `..` (ranges AND traversal), `~`/`^`/`@{}`
|
|
89
|
+
* (rev operators), `:`, whitespace, and `;` are all outside the charset or explicitly banned. */
|
|
90
|
+
const PLAIN_REF_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,255}$/;
|
|
91
|
+
export function isPlainRef(base) {
|
|
92
|
+
return PLAIN_REF_RE.test(base) && !base.includes('..') && !base.endsWith('/');
|
|
93
|
+
}
|
|
94
|
+
/** One contained git read: argv array (never a shell string), capped, cwd-scoped to the run's
|
|
95
|
+
* workdir. Returns trimmed stdout, or `null` ONLY when git ran to completion and answered "no"
|
|
96
|
+
* (unresolvable ref, no merge base, …) — a plain non-zero exit. Operational failures are never
|
|
97
|
+
* `null`: ENOENT (git missing), "not a git repository", a timed-out/killed process (`killed`/
|
|
98
|
+
* `signal` set), and output-cap overflow all rethrow, so they surface as the route's 500 rather
|
|
99
|
+
* than masquerading as a 400 "unresolvable base" (Copilot, #307). */
|
|
100
|
+
async function gitQuery(workdir, args) {
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execCapped('git', args, { timeout: GIT_TIMEOUT_MS, cwd: workdir });
|
|
103
|
+
return stdout.trim();
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (err.code === 'ENOENT')
|
|
107
|
+
throw err;
|
|
108
|
+
if (err instanceof ExecOutputTooLarge)
|
|
109
|
+
throw err;
|
|
110
|
+
const proc = err;
|
|
111
|
+
if (proc.killed === true || (proc.signal !== undefined && proc.signal !== null))
|
|
112
|
+
throw err;
|
|
113
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
114
|
+
if (msg.toLowerCase().includes('not a git repository'))
|
|
115
|
+
throw err;
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** SHA shapes rev-parse may emit (SHA-1 today, SHA-256 repos exist). Anything else is treated as
|
|
120
|
+
* "did not resolve" — the resolved value is what reaches `git diff`, never the caller's text. */
|
|
121
|
+
const COMMIT_SHA_RE = /^[0-9a-f]{40,64}$/;
|
|
122
|
+
/**
|
|
123
|
+
* Resolve `?base=` to a commit SHA inside the run's repo, or throw the route's named 400s.
|
|
124
|
+
* Two accepted spellings (§8.1): the literal `merge-base` → `git merge-base <default-branch>
|
|
125
|
+
* HEAD`; or a plain ref that `git rev-parse --verify` resolves IN THIS repo. Everything git
|
|
126
|
+
* ultimately diffs against is the resolved SHA — the raw parameter never reaches `git diff`,
|
|
127
|
+
* and `--end-of-options` keeps even a validated ref from ever parsing as a flag.
|
|
128
|
+
*/
|
|
129
|
+
async function resolveDiffBase(workdir, base) {
|
|
130
|
+
if (base === MERGE_BASE_LITERAL) {
|
|
131
|
+
// Default branch: origin's HEAD when the repo has one, else local main/master.
|
|
132
|
+
let branch = null;
|
|
133
|
+
const symref = await gitQuery(workdir, ['symbolic-ref', '--quiet', 'refs/remotes/origin/HEAD']);
|
|
134
|
+
if (symref !== null && symref.startsWith('refs/remotes/')) {
|
|
135
|
+
branch = symref.slice('refs/remotes/'.length);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
for (const cand of ['main', 'master']) {
|
|
139
|
+
if ((await gitQuery(workdir, ['rev-parse', '--verify', `refs/heads/${cand}`])) !== null) {
|
|
140
|
+
branch = cand;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (branch === null) {
|
|
146
|
+
throw new UnresolvableDiffBaseError(base, 'no default branch found in this repo to merge-base against');
|
|
147
|
+
}
|
|
148
|
+
const sha = await gitQuery(workdir, ['merge-base', '--end-of-options', branch, 'HEAD']);
|
|
149
|
+
if (sha === null || !COMMIT_SHA_RE.test(sha)) {
|
|
150
|
+
throw new UnresolvableDiffBaseError(base, `no merge base between ${branch} and HEAD`);
|
|
151
|
+
}
|
|
152
|
+
return sha;
|
|
153
|
+
}
|
|
154
|
+
if (!isPlainRef(base))
|
|
155
|
+
throw new InvalidDiffBaseError(base);
|
|
156
|
+
const sha = await gitQuery(workdir, [
|
|
157
|
+
'rev-parse',
|
|
158
|
+
'--verify',
|
|
159
|
+
'--end-of-options',
|
|
160
|
+
`${base}^{commit}`,
|
|
161
|
+
]);
|
|
162
|
+
if (sha === null || !COMMIT_SHA_RE.test(sha)) {
|
|
163
|
+
throw new UnresolvableDiffBaseError(base, `ref does not resolve inside the run's repo: ${base}`);
|
|
164
|
+
}
|
|
165
|
+
return sha;
|
|
166
|
+
}
|
|
167
|
+
/** Git's ways of saying "there is nothing here to diff" — a real answer (`diff: ""`), not an
|
|
168
|
+
* error. Mirrors the git-history route's tolerance (routes.ts): a workdir that is not a repo, or
|
|
169
|
+
* a repo with no commits yet (`HEAD` unresolvable — every file is untracked and the untracked
|
|
170
|
+
* pass still runs for the commit-less case). */
|
|
171
|
+
function isEmptyDiffAnswer(msg) {
|
|
172
|
+
// Case-insensitive on the repo check: `git log` says "fatal: not a git repository" but
|
|
173
|
+
// `git diff` says "warning: Not a git repository. Use --no-index …" — measured, not assumed.
|
|
174
|
+
const lower = msg.toLowerCase();
|
|
175
|
+
return (lower.includes('not a git repository') ||
|
|
176
|
+
msg.includes('does not have any commits') ||
|
|
177
|
+
msg.includes("ambiguous argument 'HEAD'") ||
|
|
178
|
+
msg.includes('unknown revision'));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The run's worktree diff against `base` (default HEAD) — staged + unstaged in one answer — with
|
|
182
|
+
* untracked files appended as `git diff --no-index /dev/null <file>` all-addition hunks (§3.3: a
|
|
183
|
+
* run's *created* files must diff as additions rather than vanish). `relPath`, when given, narrows
|
|
184
|
+
* both passes to that one file; it is always a single argv element after `--` (the open-path rule
|
|
185
|
+
* — no shell string is ever composed). The literal `/dev/null` is git's own cross-platform
|
|
186
|
+
* null-device spelling (git special-cases it, including on Windows).
|
|
187
|
+
*
|
|
188
|
+
* `base` (CREW-UX-1, DES-UX-001 §8.1): the literal `merge-base` diffs from the run branch's fork
|
|
189
|
+
* point off the default branch — committed run work becomes visible; a plain ref diffs from that
|
|
190
|
+
* ref, resolved inside this repo first (`resolveDiffBase` — the raw parameter never reaches
|
|
191
|
+
* `git diff`). Throws `InvalidDiffBaseError` / `UnresolvableDiffBaseError` for the route's 400s.
|
|
192
|
+
*/
|
|
193
|
+
export async function worktreeDiff(workdir, relPath, base) {
|
|
194
|
+
const limit = relPath === undefined ? [] : ['--', relPath];
|
|
195
|
+
let out = '';
|
|
196
|
+
// Resolve the baseline BEFORE any diff pass: a bad `base` must 400 without partial output.
|
|
197
|
+
let baseRev = 'HEAD';
|
|
198
|
+
if (base !== undefined && base.length > 0) {
|
|
199
|
+
try {
|
|
200
|
+
baseRev = await resolveDiffBase(workdir, base);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
if (err.code === 'ENOENT')
|
|
204
|
+
throw err; // git missing — 500
|
|
205
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
206
|
+
// The route's standing tolerance: a non-repo workdir answers an empty diff for any
|
|
207
|
+
// WELL-FORMED base — a malformed base has already thrown InvalidDiffBaseError above,
|
|
208
|
+
// and that 400 wins over the tolerance (Copilot, #307).
|
|
209
|
+
if (msg.toLowerCase().includes('not a git repository'))
|
|
210
|
+
return { diff: '', truncated: false };
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Tracked changes (staged + unstaged, plus committed-since-base when a base was resolved):
|
|
215
|
+
// diff the worktree against the baseline.
|
|
216
|
+
try {
|
|
217
|
+
const { stdout } = await execCapped('git', ['diff', '--no-color', '--no-ext-diff', baseRev, ...limit], { timeout: GIT_TIMEOUT_MS, cwd: workdir });
|
|
218
|
+
out = stdout;
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
if (err.code === 'ENOENT')
|
|
222
|
+
throw err; // git missing — the route's 500
|
|
223
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
224
|
+
if (msg.toLowerCase().includes('not a git repository'))
|
|
225
|
+
return { diff: '', truncated: false };
|
|
226
|
+
if (!isEmptyDiffAnswer(msg))
|
|
227
|
+
throw err;
|
|
228
|
+
// No commits yet: tracked diff is vacuously empty; fall through to the untracked pass.
|
|
229
|
+
}
|
|
230
|
+
// Untracked files → all-addition hunks. `-z` (NUL separators, no quoting) + `-uall` (individual
|
|
231
|
+
// files, never a collapsed `?? dir/` entry that --no-index could not diff).
|
|
232
|
+
const { stdout: statusOut } = await execCapped('git', ['status', '--porcelain', '-z', '-uall', ...limit], { timeout: GIT_TIMEOUT_MS, cwd: workdir });
|
|
233
|
+
const untracked = statusOut
|
|
234
|
+
.split('\0')
|
|
235
|
+
.filter((entry) => entry.startsWith('?? '))
|
|
236
|
+
.map((entry) => entry.slice(3));
|
|
237
|
+
for (const file of untracked) {
|
|
238
|
+
// Cap is in BYTES; `out.length` counts UTF-16 code units. `>=`: exactly-at-cap is
|
|
239
|
+
// already done — spawning one more git only to discard its output is waste (Copilot, #305).
|
|
240
|
+
if (Buffer.byteLength(out, 'utf8') >= DIFF_OUTPUT_CAP_BYTES)
|
|
241
|
+
break;
|
|
242
|
+
try {
|
|
243
|
+
const { stdout } = await execCapped('git', ['diff', '--no-color', '--no-ext-diff', '--no-index', '--', '/dev/null', file], { timeout: GIT_TIMEOUT_MS, cwd: workdir });
|
|
244
|
+
out += stdout;
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
// `--no-index` exits 1 WHEN THE FILES DIFFER — which is every hit here. The diff is still
|
|
248
|
+
// on stdout; only a stdout-less failure is a real error.
|
|
249
|
+
const stdout = err.stdout;
|
|
250
|
+
if (typeof stdout === 'string' && stdout.length > 0)
|
|
251
|
+
out += stdout;
|
|
252
|
+
else if (err.code === 'ENOENT')
|
|
253
|
+
throw err;
|
|
254
|
+
else
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Byte-accurate cap (Copilot, #305): `slice` on the string counts UTF-16 code units, which
|
|
259
|
+
// under-enforces for non-ASCII diffs. Cut on the UTF-8 byte buffer, then back off any
|
|
260
|
+
// incomplete trailing multibyte sequence — decoding a torn tail yields U+FFFD, which
|
|
261
|
+
// re-encodes LARGER and would breach the cap by a byte.
|
|
262
|
+
const outBytes = Buffer.from(out, 'utf8');
|
|
263
|
+
if (outBytes.byteLength > DIFF_OUTPUT_CAP_BYTES) {
|
|
264
|
+
let end = DIFF_OUTPUT_CAP_BYTES;
|
|
265
|
+
while (end > 0 && (outBytes[end] & 0xc0) === 0x80)
|
|
266
|
+
end--; // land on a sequence boundary
|
|
267
|
+
return { diff: outBytes.subarray(0, end).toString('utf8'), truncated: true };
|
|
268
|
+
}
|
|
269
|
+
return { diff: out, truncated: false };
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=run-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-files.js","sourceRoot":"","sources":["../../src/api/run-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { CoreEvent, SeatHealth } from '../core/types.js';
|
|
2
|
+
export type { SeatHealth };
|
|
3
|
+
/** Rolling window for the repeated-acpFallback rule. */
|
|
4
|
+
export declare const FALLBACK_WINDOW_MS: number;
|
|
5
|
+
/** Fallbacks within the window that flip a seat inactive. */
|
|
6
|
+
export declare const FALLBACK_THRESHOLD = 3;
|
|
7
|
+
/**
|
|
8
|
+
* In-memory per-seat health, folded from live CoreEvents. In-memory ON PURPOSE: health is a
|
|
9
|
+
* statement about the running platform's ability to reach a CLI right now — a daemon restart
|
|
10
|
+
* genuinely does not know, and "assume active, let the next failure/probe speak" is the honest
|
|
11
|
+
* default (and the one the declarative-roster doctrine implies).
|
|
12
|
+
*/
|
|
13
|
+
export declare class SeatHealthTracker {
|
|
14
|
+
private readonly entries;
|
|
15
|
+
/** `${session}:${ord}` → cli key. `unitOutputCaptured` carries no seat; this is the correlation. */
|
|
16
|
+
private readonly assignments;
|
|
17
|
+
/** cli key → recent failure-fallback timestamps (epoch ms), pruned to the rolling window. */
|
|
18
|
+
private readonly fallbacks;
|
|
19
|
+
/** Default `since` for seats that have never changed state. */
|
|
20
|
+
private readonly startedAt;
|
|
21
|
+
/** Fold one CoreEvent into the map. Safe on every event type; unknown types are ignored. */
|
|
22
|
+
ingest(event: CoreEvent): void;
|
|
23
|
+
/** Flip a seat ACTIVE: message cleared, `since` updated on a genuine transition. */
|
|
24
|
+
markActive(key: string, atMs?: number): void;
|
|
25
|
+
/** Flip a seat INACTIVE with the error excerpt; `since` survives while already inactive. */
|
|
26
|
+
markInactive(key: string, message: string, atMs?: number): void;
|
|
27
|
+
/** The seat's health — a seat never seen in an event is ACTIVE with no message (the default). */
|
|
28
|
+
healthFor(key: string): SeatHealth;
|
|
29
|
+
}
|
|
30
|
+
/** The roster slice the probe needs (a `RosterSeat` satisfies it structurally). */
|
|
31
|
+
export interface ProbeSeat {
|
|
32
|
+
key: string;
|
|
33
|
+
/** The seat's cheap liveness command, e.g. `["claude", "--version"]`. */
|
|
34
|
+
version_probe?: unknown;
|
|
35
|
+
[k: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
export interface SeatProbeOptions {
|
|
38
|
+
/** Probe cadence. Default 10 minutes — a recovery path, not a monitor. */
|
|
39
|
+
intervalMs?: number;
|
|
40
|
+
/** Per-probe timeout. Default 5s; a probe that hangs counts as still-down. */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
/** Injectable prober (tests). Resolve `true` ⇔ the command exited 0. */
|
|
43
|
+
runProbe?: (argv: string[], timeoutMs: number) => Promise<boolean>;
|
|
44
|
+
log?: (m: string) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The low-frequency recovery probe (crew#274 §3): every `intervalMs`, for INACTIVE seats only,
|
|
48
|
+
* run the seat's `version_probe`; exit 0 flips the seat active with the message cleared. Quota
|
|
49
|
+
* resets and re-logins are thus detected without operator action. Callers own the guard against
|
|
50
|
+
* probing in tests (the daemon arms this from `createServer`, env-gated).
|
|
51
|
+
*/
|
|
52
|
+
export declare function startSeatHealthProbe(tracker: SeatHealthTracker, roster: () => ProbeSeat[], opts?: SeatProbeOptions): {
|
|
53
|
+
stop(): void;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=seat-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seat-health.d.ts","sourceRoot":"","sources":["../../src/api/seat-health.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9D,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AACjD,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAiCpC;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,oGAAoG;IACpG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,6FAA6F;IAC7F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IACzD,+DAA+D;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IAEtD,4FAA4F;IAC5F,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAoG9B,oFAAoF;IACpF,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,IAAI;IAUhD,4FAA4F;IAC5F,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,IAAI;IAWnE,iGAAiG;IACjG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU;CAGnC;AAED,mFAAmF;AACnF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B;AAcD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,MAAM,SAAS,EAAE,EACzB,IAAI,GAAE,gBAAqB,GAC1B;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,CAmDlB"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Seat health (crew#274): runtime health per council seat, folded from the live CoreEvent stream.
|
|
2
|
+
//
|
|
3
|
+
// The operator doctrine this implements (recorded in ~/.config/wicked-council/clis.toml): every
|
|
4
|
+
// CLI seat stays LISTED and ENABLED in config — quota/auth/runtime errors are RUNTIME state the
|
|
5
|
+
// platform detects and DISPLAYS (`inactive` + the error excerpt), never a hand-edited disable.
|
|
6
|
+
// The old pattern left stale disables behind: quotas that had long reset, and an "out of credits"
|
|
7
|
+
// comment masking what had become a `401 Unauthorized` needing a re-login.
|
|
8
|
+
//
|
|
9
|
+
// The fold (one rule per event, shared by every caller — the gate-cache posture):
|
|
10
|
+
//
|
|
11
|
+
// - `stepFailed` marks the seat INACTIVE when the failure is seat-level: `failureKind:
|
|
12
|
+
// "workerError"` (the CLI process itself failed — crew#277 asks exactly this stamp), or a
|
|
13
|
+
// `detail` naming the wrapped runner's "(cli `x` exited N)" message, an ACP "timeout waiting",
|
|
14
|
+
// or an auth/quota string (401/unauthorized/quota/rate-limit/credits). The seat is read from
|
|
15
|
+
// the detail when it names one, else from the unit's `unitDistributed` assignment.
|
|
16
|
+
// - `acpFallback` is NOT alone inactive — `session_died` falls back to single-shot and the unit
|
|
17
|
+
// can still succeed — but REPEATED fallback (3+ in 10 minutes) is a seat that cannot hold a
|
|
18
|
+
// session, and that is. `governance_requires_wrapped` is deliberate routing, not a failure,
|
|
19
|
+
// and never counts.
|
|
20
|
+
// - `unitOutputCaptured` with `stepStatus: "ok"` marks the assigned seat ACTIVE again and clears
|
|
21
|
+
// the message (the event carries no seat, so `unitDistributed`/`unitReassigned` are folded
|
|
22
|
+
// into a per-unit assignment map for the correlation).
|
|
23
|
+
//
|
|
24
|
+
// Recovery without an ok output comes from `startSeatHealthProbe`: every `intervalMs` (default
|
|
25
|
+
// 10 minutes), INACTIVE seats only, run the seat's `version_probe` command with a short timeout;
|
|
26
|
+
// exit 0 flips the seat active. Active seats are never probed — this is a recovery path, not a
|
|
27
|
+
// monitor.
|
|
28
|
+
import { execCapped } from '../core/exec.js';
|
|
29
|
+
/** Rolling window for the repeated-acpFallback rule. */
|
|
30
|
+
export const FALLBACK_WINDOW_MS = 10 * 60 * 1000;
|
|
31
|
+
/** Fallbacks within the window that flip a seat inactive. */
|
|
32
|
+
export const FALLBACK_THRESHOLD = 3;
|
|
33
|
+
/** Health messages are operator-facing chips, not transcripts — bound them hard. */
|
|
34
|
+
const EXCERPT_MAX = 240;
|
|
35
|
+
function excerpt(s) {
|
|
36
|
+
const flat = s.replace(/\s+/g, ' ').trim();
|
|
37
|
+
return flat.length <= EXCERPT_MAX ? flat : `${flat.slice(0, EXCERPT_MAX - 1)}…`;
|
|
38
|
+
}
|
|
39
|
+
/** The wrapped runner's seat-naming failure message: "(cli `x` exited N) …" (execute_wrapped.rs). */
|
|
40
|
+
const CLI_IN_DETAIL = /\(cli `([^`]+)` exited /;
|
|
41
|
+
/**
|
|
42
|
+
* Seat-level failure signatures in a `stepFailed` detail. Deliberately narrow: a unit that failed
|
|
43
|
+
* its WORK (missing deliverable, gate veto) says nothing about the seat's health, so only strings
|
|
44
|
+
* that name the CLI process, its transport, or its account state qualify.
|
|
45
|
+
*/
|
|
46
|
+
const SEAT_FAILURE_PATTERNS = [
|
|
47
|
+
CLI_IN_DETAIL, // non-zero exit, seat named by the runner itself
|
|
48
|
+
/timeout waiting/i, // ACP "timeout waiting for response id=…" (acp_runner.rs)
|
|
49
|
+
/\b401\b|\bunauthorized\b/i, // auth: needs a re-login
|
|
50
|
+
/\bquota\b|rate.?limit|too many requests|\b429\b/i, // quota/rate ceiling
|
|
51
|
+
/\bout of credits\b|\binsufficient credits\b/i, // account balance
|
|
52
|
+
];
|
|
53
|
+
/** `acpFallback` kinds that are deliberate routing rather than a failure — never counted. */
|
|
54
|
+
const BENIGN_FALLBACK_KINDS = new Set(['governance_requires_wrapped']);
|
|
55
|
+
function str(v) {
|
|
56
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* In-memory per-seat health, folded from live CoreEvents. In-memory ON PURPOSE: health is a
|
|
60
|
+
* statement about the running platform's ability to reach a CLI right now — a daemon restart
|
|
61
|
+
* genuinely does not know, and "assume active, let the next failure/probe speak" is the honest
|
|
62
|
+
* default (and the one the declarative-roster doctrine implies).
|
|
63
|
+
*/
|
|
64
|
+
export class SeatHealthTracker {
|
|
65
|
+
entries = new Map();
|
|
66
|
+
/** `${session}:${ord}` → cli key. `unitOutputCaptured` carries no seat; this is the correlation. */
|
|
67
|
+
assignments = new Map();
|
|
68
|
+
/** cli key → recent failure-fallback timestamps (epoch ms), pruned to the rolling window. */
|
|
69
|
+
fallbacks = new Map();
|
|
70
|
+
/** Default `since` for seats that have never changed state. */
|
|
71
|
+
startedAt = new Date().toISOString();
|
|
72
|
+
/** Fold one CoreEvent into the map. Safe on every event type; unknown types are ignored. */
|
|
73
|
+
ingest(event) {
|
|
74
|
+
// Replayed log entries carry the engine's capture-time `ts`; live frames don't, and for
|
|
75
|
+
// those "now" IS the observation time (the gate-cache's observedAt rule).
|
|
76
|
+
const at = typeof event.ts === 'number' ? event.ts : Date.now();
|
|
77
|
+
const session = str(event.session);
|
|
78
|
+
const ord = typeof event.ord === 'number' ? event.ord : undefined;
|
|
79
|
+
switch (event.type) {
|
|
80
|
+
case 'unitDistributed': {
|
|
81
|
+
const cli = str(event.cli);
|
|
82
|
+
if (session !== undefined && ord !== undefined && cli !== undefined) {
|
|
83
|
+
this.assignments.set(`${session}:${ord}`, cli);
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
case 'unitReassigned': {
|
|
88
|
+
// `newCli: null` means the council re-convenes — the follow-up unitDistributed will set
|
|
89
|
+
// the new seat. DROP the stale assignment meanwhile: leaving it in place would attribute
|
|
90
|
+
// the interregnum's events to the seat that was just taken off the unit (Copilot, #279).
|
|
91
|
+
const newCli = str(event.newCli);
|
|
92
|
+
if (session !== undefined && ord !== undefined && newCli === undefined) {
|
|
93
|
+
this.assignments.delete(`${session}:${ord}`);
|
|
94
|
+
}
|
|
95
|
+
if (session !== undefined && ord !== undefined && newCli !== undefined) {
|
|
96
|
+
this.assignments.set(`${session}:${ord}`, newCli);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
case 'unitOutputCaptured': {
|
|
101
|
+
if (event.stepStatus !== 'ok' || session === undefined || ord === undefined)
|
|
102
|
+
return;
|
|
103
|
+
const seat = this.assignments.get(`${session}:${ord}`);
|
|
104
|
+
if (seat !== undefined)
|
|
105
|
+
this.markActive(seat, at);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
case 'stepFailed': {
|
|
109
|
+
const detail = typeof event.detail === 'string' ? event.detail : '';
|
|
110
|
+
const failureKind = str(event.failureKind);
|
|
111
|
+
// The detail names the seat when the wrapped runner produced it; otherwise fall back to
|
|
112
|
+
// the unit's assignment (a workerError detail is the CLI's own output and rarely does).
|
|
113
|
+
const named = CLI_IN_DETAIL.exec(detail)?.[1];
|
|
114
|
+
const assigned = session !== undefined && ord !== undefined
|
|
115
|
+
? this.assignments.get(`${session}:${ord}`)
|
|
116
|
+
: undefined;
|
|
117
|
+
const seat = named ?? assigned;
|
|
118
|
+
if (seat === undefined)
|
|
119
|
+
return;
|
|
120
|
+
const seatLevel = failureKind === 'workerError' || SEAT_FAILURE_PATTERNS.some((re) => re.test(detail));
|
|
121
|
+
if (seatLevel) {
|
|
122
|
+
const msg = excerpt(detail) || `seat failure (${failureKind ?? 'unreported'})`;
|
|
123
|
+
this.markInactive(seat, msg, at);
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
case 'acpFallback': {
|
|
128
|
+
const cliKey = str(event.cliKey);
|
|
129
|
+
const fallbackKind = str(event.fallbackKind);
|
|
130
|
+
if (cliKey === undefined)
|
|
131
|
+
return;
|
|
132
|
+
if (fallbackKind !== undefined && BENIGN_FALLBACK_KINDS.has(fallbackKind))
|
|
133
|
+
return;
|
|
134
|
+
const fresh = (this.fallbacks.get(cliKey) ?? []).filter((t) => at - t < FALLBACK_WINDOW_MS);
|
|
135
|
+
fresh.push(at);
|
|
136
|
+
this.fallbacks.set(cliKey, fresh);
|
|
137
|
+
if (fresh.length >= FALLBACK_THRESHOLD) {
|
|
138
|
+
const reason = str(event.reason) ?? fallbackKind ?? 'unknown';
|
|
139
|
+
this.markInactive(cliKey, `repeated ACP fallback (${fresh.length} in 10 min): ${reason}`, at);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// One fallback is not inactive (session death falls back and the unit can still work),
|
|
143
|
+
// but it IS an observed error — stamp lastErrorAt without flipping the status.
|
|
144
|
+
const prev = this.entries.get(cliKey);
|
|
145
|
+
this.entries.set(cliKey, {
|
|
146
|
+
status: prev?.status ?? 'active',
|
|
147
|
+
...(prev?.message !== undefined ? { message: prev.message } : {}),
|
|
148
|
+
since: prev?.since ?? this.startedAt,
|
|
149
|
+
lastErrorAt: new Date(at).toISOString(),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// A finished run's assignments can never activate/deactivate anyone again — drop them.
|
|
155
|
+
case 'sessionCompleted':
|
|
156
|
+
case 'sessionFailed':
|
|
157
|
+
case 'runCancelled': {
|
|
158
|
+
if (session === undefined)
|
|
159
|
+
return;
|
|
160
|
+
const prefix = `${session}:`;
|
|
161
|
+
for (const key of [...this.assignments.keys()]) {
|
|
162
|
+
if (key.startsWith(prefix))
|
|
163
|
+
this.assignments.delete(key);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** Flip a seat ACTIVE: message cleared, `since` updated on a genuine transition. */
|
|
172
|
+
markActive(key, atMs = Date.now()) {
|
|
173
|
+
const prev = this.entries.get(key);
|
|
174
|
+
this.entries.set(key, {
|
|
175
|
+
status: 'active',
|
|
176
|
+
since: prev?.status === 'active' ? prev.since : new Date(atMs).toISOString(),
|
|
177
|
+
...(prev?.lastErrorAt !== undefined ? { lastErrorAt: prev.lastErrorAt } : {}),
|
|
178
|
+
});
|
|
179
|
+
this.fallbacks.delete(key); // an ok output resets the repeated-fallback window too
|
|
180
|
+
}
|
|
181
|
+
/** Flip a seat INACTIVE with the error excerpt; `since` survives while already inactive. */
|
|
182
|
+
markInactive(key, message, atMs = Date.now()) {
|
|
183
|
+
const prev = this.entries.get(key);
|
|
184
|
+
const iso = new Date(atMs).toISOString();
|
|
185
|
+
this.entries.set(key, {
|
|
186
|
+
status: 'inactive',
|
|
187
|
+
message: excerpt(message),
|
|
188
|
+
since: prev?.status === 'inactive' ? prev.since : iso,
|
|
189
|
+
lastErrorAt: iso,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/** The seat's health — a seat never seen in an event is ACTIVE with no message (the default). */
|
|
193
|
+
healthFor(key) {
|
|
194
|
+
return this.entries.get(key) ?? { status: 'active', since: this.startedAt };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Default prober: run the version command; exit 0 → true, anything else (incl. timeout) → false. */
|
|
198
|
+
async function defaultRunProbe(argv, timeoutMs) {
|
|
199
|
+
const [file, ...args] = argv;
|
|
200
|
+
if (file === undefined)
|
|
201
|
+
return false;
|
|
202
|
+
try {
|
|
203
|
+
await execCapped(file, args, { timeout: timeoutMs, maxBuffer: 1024 * 1024 });
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The low-frequency recovery probe (crew#274 §3): every `intervalMs`, for INACTIVE seats only,
|
|
212
|
+
* run the seat's `version_probe`; exit 0 flips the seat active with the message cleared. Quota
|
|
213
|
+
* resets and re-logins are thus detected without operator action. Callers own the guard against
|
|
214
|
+
* probing in tests (the daemon arms this from `createServer`, env-gated).
|
|
215
|
+
*/
|
|
216
|
+
export function startSeatHealthProbe(tracker, roster, opts = {}) {
|
|
217
|
+
const intervalMs = opts.intervalMs ?? FALLBACK_WINDOW_MS; // 10 min
|
|
218
|
+
const timeoutMs = opts.timeoutMs ?? 5_000;
|
|
219
|
+
const runProbe = opts.runProbe ?? defaultRunProbe;
|
|
220
|
+
const log = opts.log ?? (() => undefined);
|
|
221
|
+
let inFlight = false;
|
|
222
|
+
const tick = async () => {
|
|
223
|
+
if (inFlight)
|
|
224
|
+
return; // a slow round must never stack onto the next interval
|
|
225
|
+
inFlight = true;
|
|
226
|
+
try {
|
|
227
|
+
let seats;
|
|
228
|
+
try {
|
|
229
|
+
seats = roster();
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
log(`[seat-health] roster read failed, skipping probe round: ${String(err)}`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
for (const seat of seats) {
|
|
236
|
+
const key = str(seat.key);
|
|
237
|
+
if (key === undefined)
|
|
238
|
+
continue;
|
|
239
|
+
if (tracker.healthFor(key).status !== 'inactive')
|
|
240
|
+
continue; // recovery only
|
|
241
|
+
const probe = seat.version_probe;
|
|
242
|
+
const argv = Array.isArray(probe)
|
|
243
|
+
? probe.filter((s) => typeof s === 'string' && s.length > 0)
|
|
244
|
+
: [];
|
|
245
|
+
if (argv.length === 0)
|
|
246
|
+
continue; // unprobeable seat: only an ok output can recover it
|
|
247
|
+
try {
|
|
248
|
+
if (await runProbe(argv, timeoutMs)) {
|
|
249
|
+
tracker.markActive(key);
|
|
250
|
+
log(`[seat-health] '${key}' recovered (version probe exited 0)`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
/* a throwing prober reads as still-down */
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
inFlight = false;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const handle = setInterval(() => {
|
|
263
|
+
void tick();
|
|
264
|
+
}, intervalMs);
|
|
265
|
+
// Never hold the process open for a probe — the daemon's server lifecycle owns shutdown.
|
|
266
|
+
handle.unref?.();
|
|
267
|
+
return {
|
|
268
|
+
stop() {
|
|
269
|
+
clearInterval(handle);
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=seat-health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seat-health.js","sourceRoot":"","sources":["../../src/api/seat-health.ts"],"names":[],"mappings":"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"}
|