@volter/supercode-health 0.1.13 → 0.1.14
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/lib/features.mjs +1 -0
- package/lib/recorded.mjs +136 -0
- package/lib/sample.mjs +9 -0
- package/lib/thresholds.mjs +2 -0
- package/package.json +3 -2
package/lib/features.mjs
CHANGED
|
@@ -13,6 +13,7 @@ export const FEATURES = [
|
|
|
13
13
|
{ id: 'disk-io', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'device throughput; top writers per process' },
|
|
14
14
|
{ id: 'tcp', barrier: 'none', platforms: ['darwin', 'linux', 'win32'], reads: 'sockets per state, ephemeral ports in use, TIME_WAIT by listener and its pid' },
|
|
15
15
|
{ id: 'files', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'open files against the system limit' },
|
|
16
|
+
{ id: 'recorded', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'every command supercode recorded outside itself (harness configs, hooks baked into live sessions), one of each program and verb run as its consumer runs it' },
|
|
16
17
|
{ id: 'drift', barrier: 'none', platforms: ['darwin', 'linux'], reads: 'running supercode processes whose install was replaced after they started (one ps)' },
|
|
17
18
|
{ id: 'services', barrier: 'none', platforms: ['darwin', 'linux'], reads: "configured launchd/systemd user jobs: running, last exit, log silence" },
|
|
18
19
|
{ id: 'supercode', barrier: 'integration:machine-daemon', platforms: ['darwin', 'linux', 'win32'], reads: "this machine's supercode connector: answering, server link, stuck launches" },
|
package/lib/recorded.mjs
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Recorded commands (t_4e879316, ADR 0022): every command this machine holds for supercode outside supercode itself
|
|
2
|
+
// still runs. Supercode writes commands into things it does not own: a harness's config (Codex's hooks.json and
|
|
3
|
+
// config.toml, Claude's settings and .claude.json) and the hooks baked into a session's launch flags, which the session
|
|
4
|
+
// holds until it ends. A release that moved a verb out of the native left those answering `unrecognized subcommand`
|
|
5
|
+
// (2026-10-06: Codex failed every tool call, Claude's AskUserQuestion was blocked). This reads them all, groups them by
|
|
6
|
+
// what they run (program and verb), and runs one of each as its consumer runs it: a hook with {} on stdin, an MCP server
|
|
7
|
+
// sent initialize. A hook may refuse by its own logic; what is said is a command that cannot run at all.
|
|
8
|
+
import { mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
import { homedir, tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { run } from './run.mjs';
|
|
13
|
+
|
|
14
|
+
const json = (path) => { try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; } };
|
|
15
|
+
const text = (path) => { try { return readFileSync(path, 'utf8'); } catch { return ''; } };
|
|
16
|
+
const mentions = (command) => /supercode/.test(command);
|
|
17
|
+
const unquote = (value) => { try { return JSON.parse(`"${value}"`); } catch { return value.replace(/\\"/g, '"'); } };
|
|
18
|
+
/** The `command` values in a JSON or TOML text: a hook's, an MCP server's. */
|
|
19
|
+
const commandsIn = (source) => [...source.matchAll(/"command"\s*[:=]\s*"((?:\\.|[^"\\])*)"|\bcommand\s*=\s*'([^']*)'/g)].map((m) => (m[1] !== undefined ? unquote(m[1]) : m[2]));
|
|
20
|
+
|
|
21
|
+
/** What a command runs, for grouping: its program (the first path in it) and the supercode verb after it. */
|
|
22
|
+
function shape(command) {
|
|
23
|
+
const words = command.match(/'[^']*'|"[^"]*"|\S+/g)?.map((word) => word.replace(/^['"]|['"]$/g, '')) ?? [];
|
|
24
|
+
const at = words.findIndex((word) => /supercode/.test(word) && !word.includes('='));
|
|
25
|
+
if (at < 0) return null;
|
|
26
|
+
return `${words[at]} ${words.slice(at + 1).filter((word) => !word.startsWith('/') && !word.startsWith("'")).slice(0, 2).join(' ')}`.trim();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Every recorded command naming supercode: `{ command, kind: 'hook' | 'mcp', where }`. */
|
|
30
|
+
export async function recordedCommands({ home = homedir(), processes = true } = {}) {
|
|
31
|
+
const found = [];
|
|
32
|
+
const hooks = (config, where) => {
|
|
33
|
+
for (const [event, groups] of Object.entries(config?.hooks ?? {})) for (const group of groups ?? []) for (const hook of group.hooks ?? []) {
|
|
34
|
+
if (typeof hook.command === 'string' && mentions(hook.command)) found.push({ command: hook.command, kind: 'hook', where: `${where} ${event}` });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
hooks(json(join(home, '.codex', 'hooks.json')), 'codex hooks.json');
|
|
38
|
+
for (const file of ['settings.json', 'settings.local.json']) hooks(json(join(home, '.claude', file)), `claude ${file}`);
|
|
39
|
+
for (const [name, server] of Object.entries(json(join(home, '.claude.json'))?.mcpServers ?? {})) {
|
|
40
|
+
const command = [server.command, ...(server.args ?? [])].map((word) => `'${String(word).replace(/'/g, `'\\''`)}'`).join(' ');
|
|
41
|
+
if (mentions(command)) found.push({ command, kind: 'mcp', where: `claude mcp ${name}` });
|
|
42
|
+
}
|
|
43
|
+
// a section runs to the next header line (`[name]` opening a line), not to the next `[` (an `args = [...]` array)
|
|
44
|
+
for (const m of text(join(home, '.codex', 'config.toml')).matchAll(/^\[mcp_servers\.([^\]\n]+)\]\s*\n((?:(?!\[)[^\n]*\n?)*)/gm)) {
|
|
45
|
+
const program = /command\s*=\s*"([^"]+)"/.exec(m[2])?.[1];
|
|
46
|
+
const args = [...(/args\s*=\s*\[([^\]]*)\]/.exec(m[2])?.[1] ?? '').matchAll(/"([^"]*)"/g)].map((a) => a[1]);
|
|
47
|
+
const command = [program, ...args].map((word) => `'${String(word).replace(/'/g, `'\\''`)}'`).join(' ');
|
|
48
|
+
if (program && mentions(command)) found.push({ command, kind: 'mcp', where: `codex mcp ${m[1]}` });
|
|
49
|
+
}
|
|
50
|
+
// the hooks baked into running sessions: Claude's --settings (a file, or inline JSON) and Codex's -c values
|
|
51
|
+
if (processes) {
|
|
52
|
+
const table = String((await run('ps', ['-A', '-ww', '-o', 'pid=,command='], { timeoutMs: 10_000 })).stdout ?? '');
|
|
53
|
+
for (const line of table.split('\n')) {
|
|
54
|
+
const m = /^\s*(\d+)\s+\S*?(?:^|\/)(claude|codex)(\s.*)$/.exec(line);
|
|
55
|
+
if (!m) continue;
|
|
56
|
+
const sources = [m[3], ...[...m[3].matchAll(/--settings\s+(\/\S+\.json)/g)].map((f) => text(f[1]))];
|
|
57
|
+
for (const source of sources) for (const command of commandsIn(source)) if (mentions(command)) found.push({ command, kind: 'hook', where: `${m[2]} session ${m[1]}` });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return found;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The command as it can be run with no effect on this machine's live state (#1255 review P1: run as recorded, a relay's
|
|
65
|
+
* gate settled its queued send and relay-inbound overwrote its turn): its program and verb exactly as recorded, every
|
|
66
|
+
* data path after them (a folder, a binding; never a script an interpreter runs) a path that does not exist inside
|
|
67
|
+
* `root`, and no leading `env NAME=…` (which would name the live
|
|
68
|
+
* home again). Run under `root`'s own HOME and supercode homes, it reaches no live daemon, relay, binding or mailbox:
|
|
69
|
+
* what it shows is whether its program runs that verb at all.
|
|
70
|
+
*/
|
|
71
|
+
export function isolated(command, root) {
|
|
72
|
+
const words = command.match(/'(?:[^']|'\\'')*'|"(?:\\.|[^"\\])*"|\S+/g) ?? [];
|
|
73
|
+
const bare = (word) => word.replace(/^'(.*)'$/s, '$1').replace(/^"(.*)"$/s, '$1');
|
|
74
|
+
let at = 0;
|
|
75
|
+
if (bare(words[0] ?? '') === 'env') { at = 1; while (at < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(bare(words[at]))) at += 1; }
|
|
76
|
+
const [program, ...rest] = words.slice(at);
|
|
77
|
+
if (!program) return null;
|
|
78
|
+
const quote = (word) => `'${word.replace(/'/g, `'\\''`)}'`;
|
|
79
|
+
// a script an interpreter runs (`node …/guard.mjs`) is what runs, kept; a data path (a relay's folder, a binding) is not
|
|
80
|
+
const data = (word) => word.startsWith('/') && !/\.(?:m?js|cjs)$/.test(word);
|
|
81
|
+
return [quote(bare(program)), ...rest.map((word, index) => (data(bare(word)) ? quote(join(root, 'absent', String(index))) : quote(bare(word))))].join(' ');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The environment a probe runs in: `root`'s own homes, nothing of this machine's. */
|
|
85
|
+
function isolatedEnv(root) {
|
|
86
|
+
for (const dir of ['home', 'supercode', 'xdg', 'tmp']) mkdirSync(join(root, dir), { recursive: true });
|
|
87
|
+
return { PATH: process.env.PATH ?? '/usr/bin:/bin', HOME: join(root, 'home'), SUPERCODE_HOME: join(root, 'supercode'), XDG_CONFIG_HOME: join(root, 'xdg'), TMPDIR: join(root, 'tmp'), CODEX_HOME: join(root, 'home', '.codex'), CLAUDE_CONFIG_DIR: join(root, 'home', '.claude') };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Run `command` as its consumer would, isolated (above); `{ code, stdout, stderr }`. */
|
|
91
|
+
function attempt(command, { mcp, root }) {
|
|
92
|
+
return new Promise((done) => {
|
|
93
|
+
const safe = isolated(command, root);
|
|
94
|
+
if (!safe) { done({ code: 127, stdout: '', stderr: 'no program in the command' }); return; }
|
|
95
|
+
const child = spawn('/bin/sh', ['-c', safe], { stdio: ['pipe', 'pipe', 'pipe'], env: isolatedEnv(root), cwd: root });
|
|
96
|
+
let stdout = '', stderr = '', settled = false;
|
|
97
|
+
const finish = (answer) => { if (settled) return; settled = true; clearTimeout(timer); try { child.kill('SIGKILL'); } catch { /* gone */ } done(answer); };
|
|
98
|
+
const timer = setTimeout(() => finish({ code: null, stdout, stderr: `${stderr}\n(no answer within 15 s)` }), 15_000);
|
|
99
|
+
child.on('error', (error) => finish({ code: 127, stdout, stderr: error.message }));
|
|
100
|
+
child.stdout.on('data', (data) => { stdout += data; if (mcp && /"result"/.test(stdout)) finish({ code: 0, stdout, stderr }); });
|
|
101
|
+
child.stderr.on('data', (data) => { stderr += data; });
|
|
102
|
+
child.on('close', (code) => finish({ code, stdout, stderr }));
|
|
103
|
+
child.stdin.end(mcp ? `${JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'supercode-health', version: '1' } } })}\n` : '{}');
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const cannotRun = ({ code, stderr }) => code === null || code === 126 || code === 127
|
|
108
|
+
|| /unrecognized subcommand|unexpected argument|command not found|No such file|Cannot find module|Exec format/i.test(stderr);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The recorded commands that cannot run, one per shape (program and verb):
|
|
112
|
+
* `[{ shape, command, kind, where: [..], count, code, error }]`. `commands` (for a reading without the machine's) are
|
|
113
|
+
* `recordedCommands()`'s.
|
|
114
|
+
*/
|
|
115
|
+
export async function brokenRecorded({ commands = null } = {}) {
|
|
116
|
+
const all = commands ?? (await recordedCommands());
|
|
117
|
+
const groups = new Map();
|
|
118
|
+
for (const entry of all) {
|
|
119
|
+
const key = `${entry.kind} ${shape(entry.command) ?? entry.command}`;
|
|
120
|
+
const group = groups.get(key) ?? { key, ...entry, where: [], count: 0 };
|
|
121
|
+
group.where.push(entry.where);
|
|
122
|
+
group.count += 1;
|
|
123
|
+
groups.set(key, group);
|
|
124
|
+
}
|
|
125
|
+
const broken = [];
|
|
126
|
+
const root = mkdtempSync(join(tmpdir(), 'supercode-recorded-'));
|
|
127
|
+
try {
|
|
128
|
+
await Promise.all([...groups.values()].map(async (group) => {
|
|
129
|
+
const answer = await attempt(group.command, { mcp: group.kind === 'mcp', root });
|
|
130
|
+
const failed = group.kind === 'mcp' ? !/"result"/.test(answer.stdout) : cannotRun(answer);
|
|
131
|
+
if (failed) broken.push({ shape: group.key, command: group.command.slice(0, 300), kind: group.kind, where: [...new Set(group.where)].slice(0, 10), count: group.count, code: answer.code, error: answer.stderr.trim().split('\n')[0]?.slice(0, 200) ?? '' });
|
|
132
|
+
}));
|
|
133
|
+
} finally { rmSync(root, { recursive: true, force: true }); }
|
|
134
|
+
return broken.sort((a, b) => b.count - a.count);
|
|
135
|
+
}
|
|
136
|
+
|
package/lib/sample.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { rendererOrigins, originOf, localPort } from './chrome.mjs';
|
|
|
8
8
|
import { expiredFailures, failureSummary, keyFor, Ledger, runningRelease, sayOnChanges, slowFailures } from './failures.mjs';
|
|
9
9
|
import { healthDir } from './paths.mjs';
|
|
10
10
|
import { pinnedHomes, staleSupercode } from './drift.mjs';
|
|
11
|
+
import { brokenRecorded } from './recorded.mjs';
|
|
11
12
|
import { join } from 'node:path';
|
|
12
13
|
|
|
13
14
|
const MB = 1024 * 1024;
|
|
@@ -399,6 +400,14 @@ export class Sampler {
|
|
|
399
400
|
slow.readings.pinned = pinned;
|
|
400
401
|
for (const p of pinned) slow.observations.push({ key: `release.pinned:${p.home}`, family: 'pinned', def: alarm('release.pinned'), value: 1, met: { warn: true, crit: false, clears: false }, summary: `board home ${p.home} is ${p.why} (orchestrator ${p.version ?? 'unknown'})`, detail: p });
|
|
401
402
|
});
|
|
403
|
+
if (on.has('recorded')) await attempt('recorded', async () => {
|
|
404
|
+
const broken = await brokenRecorded();
|
|
405
|
+
slow.families.add('recorded');
|
|
406
|
+
// every broken shape this pass found; one that runs again is cleared
|
|
407
|
+
slow.observations.push({ enumerated: 'recorded', family: 'recorded', by: 'suffix', entities: new Set(broken.map((b) => b.shape)) });
|
|
408
|
+
slow.readings.recorded = broken;
|
|
409
|
+
for (const b of broken) slow.observations.push({ key: `recorded.broken:${b.shape}`, family: 'recorded', def: alarm('recorded.broken'), value: b.count, met: { warn: true, crit: true, clears: false }, summary: `${b.count} recorded command(s) cannot run (${b.where.slice(0, 3).join(', ')}): ${b.error}; ${b.command.slice(0, 120)}`, detail: b });
|
|
410
|
+
});
|
|
402
411
|
if (on.has('services')) await attempt('services', async () => {
|
|
403
412
|
const list = cfg.services ?? [];
|
|
404
413
|
const jobs = await this.reader.services(list.map((s) => s.label));
|
package/lib/thresholds.mjs
CHANGED
|
@@ -36,6 +36,8 @@ export const DEFAULTS = {
|
|
|
36
36
|
'release.drift': { forSec: 300 },
|
|
37
37
|
// a board home its record holds to another install's orchestrator: said once it has stayed so 5 min
|
|
38
38
|
'release.pinned': { forSec: 300 },
|
|
39
|
+
// a recorded command that cannot run fails its harness on every use (a hook on every tool call): said at once
|
|
40
|
+
'recorded.broken': { forSec: 0 },
|
|
39
41
|
'supercode.connector': { forSec: 60 },
|
|
40
42
|
'supercode.launch': { stuckMin: 5, failedWithinMin: 30 },
|
|
41
43
|
// one failure that keeps happening (the same command or door with the same outcome, the same mail expiry) in the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-health",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Optional machine pack: a cheap, deterministic probe of a machine's vitals that mails the machine's maintainer when a threshold is crossed. Each feature sits behind its own permission barrier.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"./bin": "./bin/health.mjs",
|
|
10
10
|
"./format": "./lib/format.mjs",
|
|
11
11
|
"./paths": "./lib/paths.mjs",
|
|
12
|
-
"./drift": "./lib/drift.mjs"
|
|
12
|
+
"./drift": "./lib/drift.mjs",
|
|
13
|
+
"./recorded": "./lib/recorded.mjs"
|
|
13
14
|
},
|
|
14
15
|
"bin": {
|
|
15
16
|
"supercode-health": "./bin/health.mjs"
|