@rubytech/create-maxy-code 0.1.214 → 0.1.219
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/package.json +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +0 -55
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.js +0 -76
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-eager/src/index.ts +0 -101
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +4 -1
- package/payload/platform/plugins/admin/hooks/__tests__/hook-emit-stale-lock-ttl.test.sh +102 -0
- package/payload/platform/plugins/admin/hooks/__tests__/post-tool-use-agent.test.sh +97 -1
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +55 -74
- package/payload/platform/plugins/admin/hooks/lib/hook-emit.sh +58 -0
- package/payload/platform/plugins/admin/hooks/post-tool-use-agent.sh +100 -29
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +0 -55
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js +130 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/capabilities-here.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +63 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts +14 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.js +50 -0
- package/payload/platform/plugins/admin/mcp/dist/lib/catalogue-census.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +28 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +68 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -0
- package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +31 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +39 -30
- package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/platform.md +28 -25
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
- package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +89 -42
- package/payload/platform/scripts/check-roles-doc-completeness.mjs +96 -0
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +0 -14
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +54 -21
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +20 -9
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts +21 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js +118 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-tail.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +28 -17
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +110 -60
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.js +16 -0
- package/payload/platform/services/claude-session-manager/dist/rc-life.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts +80 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js +306 -0
- package/payload/platform/services/claude-session-manager/dist/rc-script-spawn.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +6 -2
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +12 -46
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +24 -73
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +6 -4
- package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +4 -5
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +4 -5
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +0 -116
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +0 -1
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +0 -125
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +0 -1
- package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +0 -7
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// Task 562 — isolated rc-spawn path via `systemdSpawn` + `script(1)`.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the rc-daemon's spawn mechanism (rc-daemon.ts:312) so the
|
|
4
|
+
// manager holds ZERO file descriptors per spawn: stdio:'ignore' on the
|
|
5
|
+
// outer systemd-run child, real TTY allocated by `script -q -f -c` inside
|
|
6
|
+
// the scope, child's stdout/stderr captured to a retained typescript file
|
|
7
|
+
// at `~/.<brand>/logs/spawn-rc-resume-<unitToken>.log`.
|
|
8
|
+
//
|
|
9
|
+
// Shares no mutable state with the legacy node-pty rc-spawn path:
|
|
10
|
+
// - No `spawnPty`, `registerRcSpawnPty`, or `livePtys` interaction.
|
|
11
|
+
// - No master fd in the manager → no `releasePtyMasterFd` lifecycle.
|
|
12
|
+
// - Own `liveScriptScopeRcSpawns` Map keyed on unitToken for census +
|
|
13
|
+
// liveness observability.
|
|
14
|
+
//
|
|
15
|
+
// Observability follows the cross-arm `[rc-life]` schema (see Task 562
|
|
16
|
+
// brief). Every line carries `source=rc-spawn path=script-scope
|
|
17
|
+
// mode=<new|resume> unitToken=<token>` so a `grep source=rc-spawn`
|
|
18
|
+
// directly compares with `grep source=rc-daemon` lines emitted from
|
|
19
|
+
// rc-daemon.ts under the same schema. The capture file is never
|
|
20
|
+
// unlinked by this module — post-mortem is always possible.
|
|
21
|
+
import { dirname, join } from 'node:path';
|
|
22
|
+
import { existsSync } from 'node:fs';
|
|
23
|
+
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { findExistingJsonlForSessionId } from './jsonl-path.js';
|
|
25
|
+
import { buildRcChildEnv } from './rc-daemon.js';
|
|
26
|
+
const liveScriptScopeRcSpawns = new Map();
|
|
27
|
+
/** For tests + the census sweep. Read-only iteration. */
|
|
28
|
+
export function forEachLiveScriptScopeRcSpawn(cb) {
|
|
29
|
+
for (const e of liveScriptScopeRcSpawns.values())
|
|
30
|
+
cb(e);
|
|
31
|
+
}
|
|
32
|
+
export function liveScriptScopeRcSpawnCount() {
|
|
33
|
+
return liveScriptScopeRcSpawns.size;
|
|
34
|
+
}
|
|
35
|
+
/** Clear the tracker map. Test-only seam — production never calls. */
|
|
36
|
+
export function __resetScriptScopeRcSpawnsForTests() {
|
|
37
|
+
liveScriptScopeRcSpawns.clear();
|
|
38
|
+
}
|
|
39
|
+
/** Schema-tagged line emitter. Every consumer of the cross-arm `[rc-life]`
|
|
40
|
+
* surface must use this so the field order + null-handling stays
|
|
41
|
+
* identical between rc-spawn and rc-daemon. */
|
|
42
|
+
export function emitRcLife(logger, op, source, path, mode, unitToken, fields = {}) {
|
|
43
|
+
const base = `[rc-life] op=${op} source=${source} path=${path} mode=${mode} unitToken=${unitToken}`;
|
|
44
|
+
const extras = Object.entries(fields)
|
|
45
|
+
.map(([k, v]) => `${k}=${v === null ? 'null' : typeof v === 'string' && v.includes(' ') ? JSON.stringify(v) : v}`)
|
|
46
|
+
.join(' ');
|
|
47
|
+
logger(extras ? `${base} ${extras}` : base);
|
|
48
|
+
}
|
|
49
|
+
/** Run the script-scope rc-spawn. Returns once `systemdSpawn` resolves
|
|
50
|
+
* (scope is active, cgroup pid captured). Liveness polling, pid-file
|
|
51
|
+
* binding, and exit observation run in the background — the route
|
|
52
|
+
* responds immediately with `spawnedPid` like the node-pty arm does. */
|
|
53
|
+
export async function handleRcSpawnScriptScope(deps, req) {
|
|
54
|
+
const now = deps.now ?? (() => Date.now());
|
|
55
|
+
const livenessIntervalMs = deps.livenessPollIntervalMs ?? 15_000;
|
|
56
|
+
const start = now();
|
|
57
|
+
const unitToken = `rc-resume-${randomUUID()}`;
|
|
58
|
+
const existingJsonl = req.sessionId
|
|
59
|
+
? findExistingJsonlForSessionId(deps.claudeConfigDir, req.sessionId)
|
|
60
|
+
: null;
|
|
61
|
+
const mode = existingJsonl ? 'resume' : 'new';
|
|
62
|
+
const argv = ['--remote-control'];
|
|
63
|
+
if (req.name)
|
|
64
|
+
argv.push(req.name);
|
|
65
|
+
if (req.sessionId)
|
|
66
|
+
argv.push(mode === 'resume' ? '--resume' : '--session-id', req.sessionId);
|
|
67
|
+
const env = buildRcChildEnv({
|
|
68
|
+
accountId: deps.accountId,
|
|
69
|
+
uiPort: deps.uiPort,
|
|
70
|
+
platformRoot: deps.platformRoot,
|
|
71
|
+
claudeConfigDir: deps.claudeConfigDir,
|
|
72
|
+
brandedNeo4jUri: deps.brandedNeo4jUri,
|
|
73
|
+
brandedNeo4jPassword: deps.brandedNeo4jPassword,
|
|
74
|
+
});
|
|
75
|
+
const cleanEnv = {};
|
|
76
|
+
for (const [k, v] of Object.entries(env)) {
|
|
77
|
+
if (typeof v === 'string')
|
|
78
|
+
cleanEnv[k] = v;
|
|
79
|
+
}
|
|
80
|
+
// Mirror rc-daemon.ts:323 — capture file under <accountDir>/logs/.
|
|
81
|
+
// dirname(claudeConfigDir) is the accountDir (e.g. /home/<user>/.maxy-code/).
|
|
82
|
+
const captureFilePath = join(dirname(deps.claudeConfigDir), 'logs', `spawn-${unitToken}.log`);
|
|
83
|
+
emitRcLife(deps.logger, 'request', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
84
|
+
sessionId: req.sessionId ?? 'new',
|
|
85
|
+
name: req.name ?? 'none',
|
|
86
|
+
existingJsonl: existingJsonl ?? 'none',
|
|
87
|
+
});
|
|
88
|
+
emitRcLife(deps.logger, 'argv', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
89
|
+
cwd: deps.spawnCwd,
|
|
90
|
+
captureFile: captureFilePath,
|
|
91
|
+
argv: JSON.stringify([deps.claudeBin, ...argv]),
|
|
92
|
+
});
|
|
93
|
+
let handle;
|
|
94
|
+
try {
|
|
95
|
+
handle = await deps.systemdSpawn({
|
|
96
|
+
claudeBin: deps.claudeBin,
|
|
97
|
+
claudeArgs: argv,
|
|
98
|
+
env: cleanEnv,
|
|
99
|
+
cwd: deps.spawnCwd,
|
|
100
|
+
unitToken,
|
|
101
|
+
captureFilePath,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
emitRcLife(deps.logger, 'spawn-failed', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
107
|
+
err: JSON.stringify(msg),
|
|
108
|
+
});
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
emitRcLife(deps.logger, 'spawned', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
112
|
+
pid: handle.pid,
|
|
113
|
+
scopeUnit: `claude-session-${unitToken}.scope`,
|
|
114
|
+
captureFile: captureFilePath,
|
|
115
|
+
// Manager holds no fd for this child — this is the isolation invariant.
|
|
116
|
+
// A rising value across script-scope spawns is the leak signature.
|
|
117
|
+
managerOpenFds: 'unchanged',
|
|
118
|
+
});
|
|
119
|
+
const entry = {
|
|
120
|
+
unitToken,
|
|
121
|
+
pid: handle.pid,
|
|
122
|
+
sessionId: req.sessionId,
|
|
123
|
+
mode,
|
|
124
|
+
spawnedAt: start,
|
|
125
|
+
captureFilePath,
|
|
126
|
+
handle,
|
|
127
|
+
pidFileSeen: false,
|
|
128
|
+
remoteBound: false,
|
|
129
|
+
exited: false,
|
|
130
|
+
};
|
|
131
|
+
liveScriptScopeRcSpawns.set(unitToken, entry);
|
|
132
|
+
// Capture-file head emit — first ≤1 KB of the script(1) typescript,
|
|
133
|
+
// settled by either reaching 1 KB or by a short post-spawn delay. The
|
|
134
|
+
// child's own words are the signal whose absence cost 5 occasions
|
|
135
|
+
// (Task 562 brief). Read once and forget; subsequent failures land in
|
|
136
|
+
// op=early-exit's captureTail.
|
|
137
|
+
void readCaptureHead(handle, 500, 1_024).then((head) => {
|
|
138
|
+
emitRcLife(deps.logger, 'child-output', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
139
|
+
pid: handle.pid,
|
|
140
|
+
head: JSON.stringify(head),
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
// pid-file binding — claude writes <sessions>/<pid>.json once Remote
|
|
144
|
+
// Control registration finishes. This is the `ready` signal: pid file
|
|
145
|
+
// present means claude is alive and reachable from claude.ai/code.
|
|
146
|
+
void deps.waitForPid(handle.pid, deps.pidFileTimeoutMs)
|
|
147
|
+
.then((row) => {
|
|
148
|
+
entry.pidFileSeen = true;
|
|
149
|
+
entry.remoteBound = Boolean(row.bridgeSessionId);
|
|
150
|
+
emitRcLife(deps.logger, 'alive', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
151
|
+
pid: handle.pid,
|
|
152
|
+
ranMs: now() - start,
|
|
153
|
+
});
|
|
154
|
+
emitRcLife(deps.logger, 'ready', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
155
|
+
pid: handle.pid,
|
|
156
|
+
pidFile: row.pidFilePath ?? 'unknown',
|
|
157
|
+
bridgeId: row.bridgeSessionId ?? 'unknown',
|
|
158
|
+
remoteSession: row.bridgeSessionId ?? 'unknown',
|
|
159
|
+
});
|
|
160
|
+
})
|
|
161
|
+
.catch((err) => {
|
|
162
|
+
emitRcLife(deps.logger, 'wait-pid-failed', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
163
|
+
pid: handle.pid,
|
|
164
|
+
err: JSON.stringify(err instanceof Error ? err.message : String(err)),
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
// Liveness poll — runs for the session's whole intended life. Catches
|
|
168
|
+
// a session that dies quietly between events without a reproduction.
|
|
169
|
+
// Both arms emit this so endurance is directly comparable.
|
|
170
|
+
const livenessTimer = setInterval(() => {
|
|
171
|
+
if (entry.exited) {
|
|
172
|
+
clearInterval(livenessTimer);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const pidAlive = pidIsAlive(entry.pid);
|
|
176
|
+
emitRcLife(deps.logger, 'liveness', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
177
|
+
ageMs: now() - start,
|
|
178
|
+
pidAlive,
|
|
179
|
+
// scopeActive is captured from handle.waitForExit's terminal — true
|
|
180
|
+
// means we have not yet observed the scope leave the active set.
|
|
181
|
+
// A pidAlive=false here while exited=false is an early death the
|
|
182
|
+
// event flow missed.
|
|
183
|
+
scopeActive: !entry.exited,
|
|
184
|
+
remoteBound: entry.remoteBound,
|
|
185
|
+
});
|
|
186
|
+
}, livenessIntervalMs);
|
|
187
|
+
// Don't block process exit on the liveness timer.
|
|
188
|
+
livenessTimer.unref?.();
|
|
189
|
+
// Exit observation — `handle.waitForExit` polls systemctl. On exit,
|
|
190
|
+
// tag the lifetime, attach captureTail bytes, and remove from the
|
|
191
|
+
// live tracker.
|
|
192
|
+
void handle.waitForExit()
|
|
193
|
+
.then(async (exit) => {
|
|
194
|
+
entry.exited = true;
|
|
195
|
+
clearInterval(livenessTimer);
|
|
196
|
+
const lifetimeMs = now() - start;
|
|
197
|
+
let tail = '';
|
|
198
|
+
try {
|
|
199
|
+
tail = await handle.readCaptureTail(2_048);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
tail = '';
|
|
203
|
+
}
|
|
204
|
+
if (!entry.pidFileSeen && lifetimeMs < 2_000) {
|
|
205
|
+
emitRcLife(deps.logger, 'early-exit', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
206
|
+
pid: handle.pid,
|
|
207
|
+
ranMs: lifetimeMs,
|
|
208
|
+
exitCode: exit.exitCode ?? 'null',
|
|
209
|
+
signal: exit.code ?? 'null',
|
|
210
|
+
captureTail: JSON.stringify(tail),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const reason = inferExitReason(exit.result, lifetimeMs, entry.pidFileSeen);
|
|
214
|
+
emitRcLife(deps.logger, 'exit', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
215
|
+
pid: handle.pid,
|
|
216
|
+
lifetimeMs,
|
|
217
|
+
reason,
|
|
218
|
+
result: exit.result,
|
|
219
|
+
captureTail: JSON.stringify(tail.slice(-512)),
|
|
220
|
+
});
|
|
221
|
+
liveScriptScopeRcSpawns.delete(unitToken);
|
|
222
|
+
})
|
|
223
|
+
.catch((err) => {
|
|
224
|
+
entry.exited = true;
|
|
225
|
+
clearInterval(livenessTimer);
|
|
226
|
+
emitRcLife(deps.logger, 'wait-exit-failed', 'rc-spawn', 'script-scope', mode, unitToken, {
|
|
227
|
+
pid: handle.pid,
|
|
228
|
+
err: JSON.stringify(err instanceof Error ? err.message : String(err)),
|
|
229
|
+
});
|
|
230
|
+
liveScriptScopeRcSpawns.delete(unitToken);
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
spawnedPid: handle.pid,
|
|
234
|
+
sessionId: req.sessionId,
|
|
235
|
+
unitToken,
|
|
236
|
+
mode,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function inferExitReason(result, lifetimeMs, pidFileSeen) {
|
|
240
|
+
if (result === 'success' && pidFileSeen && lifetimeMs >= 2_000)
|
|
241
|
+
return 'archive';
|
|
242
|
+
if (result !== 'success')
|
|
243
|
+
return 'crash';
|
|
244
|
+
if (!pidFileSeen && lifetimeMs < 2_000)
|
|
245
|
+
return 'crash';
|
|
246
|
+
return 'unknown';
|
|
247
|
+
}
|
|
248
|
+
function pidIsAlive(pid) {
|
|
249
|
+
try {
|
|
250
|
+
process.kill(pid, 0);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Reads the head of the script(1) typescript. Returns at most `maxBytes`
|
|
258
|
+
* from the start of the file; waits up to `settleMs` for the file to
|
|
259
|
+
* appear, since systemd-run + script open the typescript asynchronously
|
|
260
|
+
* after the scope is active. */
|
|
261
|
+
async function readCaptureHead(handle, settleMs, maxBytes) {
|
|
262
|
+
const deadline = Date.now() + settleMs;
|
|
263
|
+
while (Date.now() < deadline) {
|
|
264
|
+
if (existsSync(handle.captureFilePath))
|
|
265
|
+
break;
|
|
266
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
267
|
+
}
|
|
268
|
+
// readCaptureTail returns the last `maxBytes` bytes; for a small early
|
|
269
|
+
// file (< maxBytes) tail == head. The boot sequence is well under 1 KB.
|
|
270
|
+
try {
|
|
271
|
+
return await handle.readCaptureTail(maxBytes);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return '';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/** Census sweep — reconciles expected (dashboard-initiated sessions) vs
|
|
278
|
+
* actual (live tracked entries). Detects a leaked entry whose pid is
|
|
279
|
+
* gone but the tracker still holds it, or a tracker whose handle's
|
|
280
|
+
* scope vanished without an exit event. Traffic-independent — runs on
|
|
281
|
+
* a timer in index.ts so a quiet failure surfaces within one interval.
|
|
282
|
+
*
|
|
283
|
+
* The daemon's live handle is supplied via `daemonAlive`; the daemon
|
|
284
|
+
* itself owns the canonical accounting (a single handle reference),
|
|
285
|
+
* but exposing it here keeps the schema unified. */
|
|
286
|
+
export function runRcLifeCensus(logger, daemonAlive) {
|
|
287
|
+
let pidAlive = 0;
|
|
288
|
+
let pidDeadButTracked = 0;
|
|
289
|
+
for (const entry of liveScriptScopeRcSpawns.values()) {
|
|
290
|
+
if (pidIsAlive(entry.pid)) {
|
|
291
|
+
pidAlive += 1;
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
pidDeadButTracked += 1;
|
|
295
|
+
// Independent reconciliation defect — entry exists with a dead pid.
|
|
296
|
+
emitRcLife(logger, 'orphan', 'rc-spawn', 'script-scope', entry.mode, entry.unitToken, {
|
|
297
|
+
pid: entry.pid,
|
|
298
|
+
kind: 'pid-dead-but-tracked',
|
|
299
|
+
ageMs: Date.now() - entry.spawnedAt,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
logger(`[rc-census] source-breakdown=rc-spawn:${liveScriptScopeRcSpawns.size},rc-daemon:${daemonAlive}` +
|
|
304
|
+
` rcSpawnPidAlive=${pidAlive} rcSpawnPidDead=${pidDeadButTracked}`);
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=rc-script-spawn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-script-spawn.js","sourceRoot":"","sources":["../src/rc-script-spawn.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,15 @@ export interface SpecialistDriftResult {
|
|
|
23
23
|
* any other defect class refuses boot and the manager never reaches a
|
|
24
24
|
* spawn that would consult this map. */
|
|
25
25
|
stripList: ReadonlyMap<string, ReadonlySet<string>>;
|
|
26
|
+
/** Per-specialist resolved tool surface — keyed by specialist name (the
|
|
27
|
+
* `.md` basename), value is the sorted set of tool names declared on the
|
|
28
|
+
* specialist's `tools:` line (before any strip-list removal). Populated
|
|
29
|
+
* for every specialist with a `tools:` line, regardless of drift outcome.
|
|
30
|
+
* Consumed by the manager-boot `[specialist-surface]` standing log so a
|
|
31
|
+
* payload-twin or bundle drift that drops a tool from a specialist's
|
|
32
|
+
* resolved surface is greppable from server.log without re-running a
|
|
33
|
+
* failing brief (Task 565 observability item 1). */
|
|
34
|
+
surfaces: ReadonlyMap<string, ReadonlySet<string>>;
|
|
26
35
|
}
|
|
27
36
|
/** Extract the value of the `tools:` line from a YAML frontmatter block.
|
|
28
37
|
* Returns the raw value (whitespace-trimmed) or null when no `tools:`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specialist-drift.d.ts","sourceRoot":"","sources":["../src/specialist-drift.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"specialist-drift.d.ts","sourceRoot":"","sources":["../src/specialist-drift.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAyCpD,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EACF,0BAA0B,GAC1B,wBAAwB,GACxB,gBAAgB,GAChB,uBAAuB,CAAA;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;yCAGqC;IACrC,EAAE,EAAE,OAAO,CAAA;IACX;;wCAEoC;IACpC,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB;;;;6CAIyC;IACzC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD;;;;;;;yDAOqD;IACrD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAA;CACnD;AAED;;;kEAGkE;AAClE,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI3E;AA+CD;;;;;;;;;wDASwD;AACxD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,WAAW,EAAE,WAAW,EACxB,eAAe,GAAE,WAAW,CAAC,MAAM,CAAa,GAC/C,qBAAqB,CAmDvB"}
|
|
@@ -35,6 +35,7 @@ const CC_NATIVE_TOOLS = new Set([
|
|
|
35
35
|
'Read',
|
|
36
36
|
'Write',
|
|
37
37
|
'Edit',
|
|
38
|
+
'MultiEdit',
|
|
38
39
|
'Glob',
|
|
39
40
|
'Grep',
|
|
40
41
|
'Bash',
|
|
@@ -129,6 +130,7 @@ function classifyTool(tool, allSet, excludedPlugins) {
|
|
|
129
130
|
export function assertSpecialistDrift(agentDirs, toolSurface, excludedPlugins = new Set()) {
|
|
130
131
|
const defects = [];
|
|
131
132
|
const stripList = new Map();
|
|
133
|
+
const surfaces = new Map();
|
|
132
134
|
let inspected = 0;
|
|
133
135
|
for (const dir of agentDirs) {
|
|
134
136
|
let entries;
|
|
@@ -168,7 +170,9 @@ export function assertSpecialistDrift(agentDirs, toolSurface, excludedPlugins =
|
|
|
168
170
|
continue;
|
|
169
171
|
inspected += 1;
|
|
170
172
|
const specialistName = basename(entry, '.md');
|
|
171
|
-
|
|
173
|
+
const declaredTools = splitToolList(toolsLine);
|
|
174
|
+
surfaces.set(specialistName, new Set([...declaredTools].sort()));
|
|
175
|
+
for (const tool of declaredTools) {
|
|
172
176
|
const verdict = classifyTool(tool, toolSurface.all, excludedPlugins);
|
|
173
177
|
if (verdict === 'ok')
|
|
174
178
|
continue;
|
|
@@ -189,6 +193,6 @@ export function assertSpecialistDrift(agentDirs, toolSurface, excludedPlugins =
|
|
|
189
193
|
// logs each and exits non-zero, matching the prior strict contract for
|
|
190
194
|
// typos / missing plugins / malformed names.
|
|
191
195
|
const ok = defects.every((d) => d.reason === 'brand-excluded-plugin');
|
|
192
|
-
return { ok, defects, inspected, stripList };
|
|
196
|
+
return { ok, defects, inspected, stripList, surfaces };
|
|
193
197
|
}
|
|
194
198
|
//# sourceMappingURL=specialist-drift.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specialist-drift.js","sourceRoot":"","sources":["../src/specialist-drift.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AACrE,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,uEAAuE;AACvE,wBAAwB;AACxB,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,6EAA6E;AAC7E,aAAa;AACb,EAAE;AACF,6DAA6D;AAC7D,4EAA4E;AAC5E,gCAAgC;AAChC,gEAAgE;AAChE,wCAAwC;AACxC,+EAA+E;AAC/E,gFAAgF;AAChF,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,+EAA+E;AAE/E,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAEtE;;sDAEsD;AACtD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS;IACtC,MAAM;IACN,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,YAAY;IACZ,WAAW;IACX,OAAO;IACP,WAAW;IACX,UAAU;IACV,MAAM;IACN,WAAW;IACX,cAAc;IACd,cAAc;IACd,iBAAiB;CAClB,CAAC,CAAA;AAEF;;;;;;;+BAO+B;AAC/B,MAAM,oBAAoB,GAAG,cAAc,CAAA;AAE3C;;4EAE4E;AAC5E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS,gBAAgB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"specialist-drift.js","sourceRoot":"","sources":["../src/specialist-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,20 +50,12 @@ export declare function listSurvivingScopes(run: SystemctlRunner): {
|
|
|
50
50
|
scopes: SurvivingScope[];
|
|
51
51
|
result: SystemctlResult;
|
|
52
52
|
};
|
|
53
|
-
/** Stop any orphan
|
|
53
|
+
/** Stop any orphan rc scope units left over from prior session-manager
|
|
54
54
|
* boots. Called from the session-manager startup BEFORE the rc-daemon
|
|
55
55
|
* supervisor's first spawn and BEFORE the HTTP server begins accepting
|
|
56
56
|
* /rc-spawn requests, so no in-flight scope can be misidentified as an
|
|
57
|
-
* orphan.
|
|
58
|
-
*
|
|
59
|
-
* Why this exists — Task 554 root cause: pre-554 `systemdSpawn` threw on
|
|
60
|
-
* every spawn attempt via the broken MainPID poll, leaving the scope
|
|
61
|
-
* registered with systemd. Eight `claude-session-rc-daemon-*.scope` units
|
|
62
|
-
* accumulated on the laptop before the fix; the new `systemdSpawn` calls
|
|
63
|
-
* `stopScopeUnit` on every terminal failure, so this reaper is one-shot
|
|
64
|
-
* in steady-state (post-rollout, the list is empty). The function is
|
|
65
|
-
* idempotent and safe to call on every boot — if nothing matches, no
|
|
66
|
-
* stop is issued. */
|
|
57
|
+
* orphan. Idempotent and safe to call on every boot — if nothing
|
|
58
|
+
* matches, no stop is issued. */
|
|
67
59
|
export declare function reapOrphanNonPtyScopes(run: SystemctlRunner, logger: (line: string) => void): Promise<void>;
|
|
68
60
|
/** Outcome surface for a scope-stop attempt.
|
|
69
61
|
*
|
|
@@ -141,48 +133,22 @@ export interface BuildSystemdRunArgvParams {
|
|
|
141
133
|
/** Working directory passed via `--working-directory=`. The manager's
|
|
142
134
|
* prior node-pty spawn passed `cwd`; the scope receives it the same way. */
|
|
143
135
|
cwd: string;
|
|
144
|
-
/** Task 556 — when supplied, wraps the inner command in
|
|
145
|
-
* `script -q -f -c "<cmd>" "<captureFilePath>"`. `script(1)` allocates a
|
|
146
|
-
* real pty (so claude has a TTY) and writes its typescript to the
|
|
147
|
-
* captureFilePath, which survives scope reaping for post-mortem reads.
|
|
148
|
-
* The non-PTY `systemdSpawn` path REQUIRES this (without a TTY,
|
|
149
|
-
* `claude --remote-control --resume` and `claude rc --spawn`
|
|
150
|
-
* early-exit in ~50 ms because `--scope` inherits the launcher's
|
|
151
|
-
* `stdio: 'ignore'` fds). The PTY spawn path (`spawnPtyAdapter`)
|
|
152
|
-
* OMITS it — node-pty already allocates a TTY for the child, and
|
|
153
|
-
* adding script on top would interpose a second pty layer between
|
|
154
|
-
* node-pty's master and the operator's keystrokes. The parent
|
|
155
|
-
* directory of captureFilePath must exist before systemd-run is
|
|
156
|
-
* spawned; the manager creates it. */
|
|
157
|
-
captureFilePath?: string;
|
|
158
136
|
}
|
|
159
|
-
/** POSIX shell single-quote: wrap in `'…'` and escape any embedded `'` as
|
|
160
|
-
* `'\''`. The inner sh command uses `"$1"` to receive the claude
|
|
161
|
-
* invocation as a single shell-evaluated string (so `script -c "$1"`
|
|
162
|
-
* sees the full command line); the joined string is built here and
|
|
163
|
-
* every component is single-quoted so claude args containing spaces,
|
|
164
|
-
* `$`, `*`, backticks, etc. survive the round trip unchanged. */
|
|
165
|
-
export declare function shellSingleQuote(s: string): string;
|
|
166
137
|
/** Build the systemd-run argv that node-pty wraps for one claude spawn.
|
|
167
|
-
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec
|
|
138
|
+
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec "$@"' sh <claudeBin> <claudeArgs...>`
|
|
168
139
|
* wraps the launch so:
|
|
169
140
|
*
|
|
170
141
|
* - **claude ignores SIGHUP** when the manager closes the PTY master fd
|
|
171
142
|
* on restart. Without the trap, master-close → kernel SIGHUP →
|
|
172
143
|
* claude exits.
|
|
173
|
-
* -
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* typescript to `<cap>` (the captureFilePath) so the child's real
|
|
182
|
-
* stdout/stderr survive scope reaping.
|
|
183
|
-
* - **`exec` keeps the pid stable** through the chain (sh → script →
|
|
184
|
-
* claude all reuse the original pid via execve), preserving the
|
|
185
|
-
* `pty.pid == claude.pid` invariant the PTY spawn path depends on. */
|
|
144
|
+
* - **`exec` keeps the pid stable** through the chain (sh → claude
|
|
145
|
+
* reuse the original pid via execve), preserving the
|
|
146
|
+
* `pty.pid == claude.pid` invariant the watcher.waitForPid path
|
|
147
|
+
* depends on.
|
|
148
|
+
*
|
|
149
|
+
* Task 573 — node-pty allocates the TTY for the child. The earlier
|
|
150
|
+
* script(1) wrap (Task 556) plus the non-PTY scope primitive it served
|
|
151
|
+
* are gone. */
|
|
186
152
|
export declare function buildSystemdRunArgv(p: BuildSystemdRunArgvParams): SystemdRunArgv;
|
|
187
153
|
/** Query `systemctl --user show -p MainPID claude-session-<token>.scope`
|
|
188
154
|
* and return the main pid (the claude process). Boot-seed uses this to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"systemd-scope.d.ts","sourceRoot":"","sources":["../src/systemd-scope.ts"],"names":[],"mappings":"AAuBA;;gBAEgB;AAChB,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AAEpD;;;uDAGuD;AACvD,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;gCAGgC;AAChC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;qCAGqC;AACrC,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,eAAe,CAAA;AAE1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CAU/E;AAiBD,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;wEAEwE;AACxE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGlE;AAED;;;kCAGkC;AAClC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;8CAG8C;AAC9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAiBrE;AAED;;qEAEqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG;IACzD,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,MAAM,EAAE,eAAe,CAAA;CACxB,CAaA;AASD
|
|
1
|
+
{"version":3,"file":"systemd-scope.d.ts","sourceRoot":"","sources":["../src/systemd-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -109,26 +109,18 @@ export function listSurvivingScopes(run) {
|
|
|
109
109
|
}
|
|
110
110
|
return { scopes: parseSurvivingScopes(result.stdout), result };
|
|
111
111
|
}
|
|
112
|
-
/** Task 554 —
|
|
113
|
-
* the bare `claude-session-<uuid>.scope` shape and are
|
|
114
|
-
* the boot-seed reconciler claims them as live
|
|
115
|
-
* reaper only sweeps tokens emitted by
|
|
116
|
-
*
|
|
112
|
+
/** Task 554 — rc-spawn / rc-daemon scope unit-token prefixes. PTY operator
|
|
113
|
+
* scopes use the bare `claude-session-<uuid>.scope` shape and are
|
|
114
|
+
* explicitly excluded: the boot-seed reconciler claims them as live
|
|
115
|
+
* operator sessions. The reaper only sweeps tokens emitted by the
|
|
116
|
+
* rc-daemon supervisor and the `/rc-spawn` HTTP route. */
|
|
117
117
|
const NON_PTY_TOKEN_PREFIXES = ['rc-daemon-', 'rc-resume-'];
|
|
118
|
-
/** Stop any orphan
|
|
118
|
+
/** Stop any orphan rc scope units left over from prior session-manager
|
|
119
119
|
* boots. Called from the session-manager startup BEFORE the rc-daemon
|
|
120
120
|
* supervisor's first spawn and BEFORE the HTTP server begins accepting
|
|
121
121
|
* /rc-spawn requests, so no in-flight scope can be misidentified as an
|
|
122
|
-
* orphan.
|
|
123
|
-
*
|
|
124
|
-
* Why this exists — Task 554 root cause: pre-554 `systemdSpawn` threw on
|
|
125
|
-
* every spawn attempt via the broken MainPID poll, leaving the scope
|
|
126
|
-
* registered with systemd. Eight `claude-session-rc-daemon-*.scope` units
|
|
127
|
-
* accumulated on the laptop before the fix; the new `systemdSpawn` calls
|
|
128
|
-
* `stopScopeUnit` on every terminal failure, so this reaper is one-shot
|
|
129
|
-
* in steady-state (post-rollout, the list is empty). The function is
|
|
130
|
-
* idempotent and safe to call on every boot — if nothing matches, no
|
|
131
|
-
* stop is issued. */
|
|
122
|
+
* orphan. Idempotent and safe to call on every boot — if nothing
|
|
123
|
+
* matches, no stop is issued. */
|
|
132
124
|
export async function reapOrphanNonPtyScopes(run, logger) {
|
|
133
125
|
const { scopes, result } = listSurvivingScopes(run);
|
|
134
126
|
if (result.exitCode !== 0) {
|
|
@@ -222,42 +214,23 @@ export async function stopScopeUnit(run, unitToken, opts = {}) {
|
|
|
222
214
|
}
|
|
223
215
|
return { outcome: 'failed-pid-survives', exitCode: 0, stderr: result.stderr };
|
|
224
216
|
}
|
|
225
|
-
/** POSIX shell single-quote: wrap in `'…'` and escape any embedded `'` as
|
|
226
|
-
* `'\''`. The inner sh command uses `"$1"` to receive the claude
|
|
227
|
-
* invocation as a single shell-evaluated string (so `script -c "$1"`
|
|
228
|
-
* sees the full command line); the joined string is built here and
|
|
229
|
-
* every component is single-quoted so claude args containing spaces,
|
|
230
|
-
* `$`, `*`, backticks, etc. survive the round trip unchanged. */
|
|
231
|
-
export function shellSingleQuote(s) {
|
|
232
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
233
|
-
}
|
|
234
217
|
/** Build the systemd-run argv that node-pty wraps for one claude spawn.
|
|
235
|
-
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec
|
|
218
|
+
* Inside the scope, `/bin/sh -c 'trap "" HUP; exec "$@"' sh <claudeBin> <claudeArgs...>`
|
|
236
219
|
* wraps the launch so:
|
|
237
220
|
*
|
|
238
221
|
* - **claude ignores SIGHUP** when the manager closes the PTY master fd
|
|
239
222
|
* on restart. Without the trap, master-close → kernel SIGHUP →
|
|
240
223
|
* claude exits.
|
|
241
|
-
* -
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
* typescript to `<cap>` (the captureFilePath) so the child's real
|
|
250
|
-
* stdout/stderr survive scope reaping.
|
|
251
|
-
* - **`exec` keeps the pid stable** through the chain (sh → script →
|
|
252
|
-
* claude all reuse the original pid via execve), preserving the
|
|
253
|
-
* `pty.pid == claude.pid` invariant the PTY spawn path depends on. */
|
|
224
|
+
* - **`exec` keeps the pid stable** through the chain (sh → claude
|
|
225
|
+
* reuse the original pid via execve), preserving the
|
|
226
|
+
* `pty.pid == claude.pid` invariant the watcher.waitForPid path
|
|
227
|
+
* depends on.
|
|
228
|
+
*
|
|
229
|
+
* Task 573 — node-pty allocates the TTY for the child. The earlier
|
|
230
|
+
* script(1) wrap (Task 556) plus the non-PTY scope primitive it served
|
|
231
|
+
* are gone. */
|
|
254
232
|
export function buildSystemdRunArgv(p) {
|
|
255
233
|
const unit = scopeUnitName(p.unitToken);
|
|
256
|
-
// Task 556 — the `-p Standard{Output,Error}=journal` + SyslogIdentifier
|
|
257
|
-
// properties Task 555 added here are `Exec*=` *service-unit* properties;
|
|
258
|
-
// systemd silently ignores them on `--scope` units, which inherit the
|
|
259
|
-
// launcher's fds. The real capture is `script`'s typescript file
|
|
260
|
-
// (captureFilePath), retrievable after the scope is reaped.
|
|
261
234
|
const args = [
|
|
262
235
|
'--user',
|
|
263
236
|
'--scope',
|
|
@@ -279,35 +252,13 @@ export function buildSystemdRunArgv(p) {
|
|
|
279
252
|
// following positional argv (the sh wrapper) doesn't get swallowed
|
|
280
253
|
// by `--setenv=` etc.
|
|
281
254
|
args.push('--');
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const claudeCmdString = [p.claudeBin, ...p.claudeArgs].map(shellSingleQuote).join(' ');
|
|
290
|
-
// `sh` as $0 leaves $1 = <claude cmd string>, $2 = <captureFilePath>.
|
|
291
|
-
// `exec` replaces the sh process image with script in-place so the
|
|
292
|
-
// pid is preserved; `script` then execs claude on its pty slave
|
|
293
|
-
// (claude is script's child but holds the same controlling TTY).
|
|
294
|
-
// `-q` suppresses script's start/end banner; `-f` flushes the
|
|
295
|
-
// typescript on every write so a fast-exiting child's last bytes
|
|
296
|
-
// hit disk before the scope is reaped.
|
|
297
|
-
args.push('/bin/sh', '-c', 'trap "" HUP; exec script -q -f -c "$1" "$2"', 'sh', claudeCmdString, p.captureFilePath);
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
// PTY path (`spawnPtyAdapter` in index.ts). node-pty already
|
|
301
|
-
// allocates a TTY for the child; the script wrap is omitted so the
|
|
302
|
-
// pty.pid == claude.pid invariant the `watcher.waitForPid` path
|
|
303
|
-
// depends on stays intact. The `exec "$@"` shell wrap keeps the
|
|
304
|
-
// SIGHUP trap and the in-place exec; `$@` expands every argv from
|
|
305
|
-
// $0 onwards inside the shell, so passing 'sh' as $0 lets `"$@"`
|
|
306
|
-
// resolve to `<claudeBin> <claudeArgs...>` after the trap.
|
|
307
|
-
args.push('/bin/sh', '-c', 'trap "" HUP; exec "$@"', 'sh', p.claudeBin);
|
|
308
|
-
for (const a of p.claudeArgs)
|
|
309
|
-
args.push(a);
|
|
310
|
-
}
|
|
255
|
+
// POSIX `sh -c <script> <$0> <$1> <$2>...` — `"$@"` expands to the
|
|
256
|
+
// positionals starting at $1. Passing 'sh' as $0 (the script name)
|
|
257
|
+
// makes claudeBin the first positional and claudeArgs the remainder,
|
|
258
|
+
// so `exec "$@"` runs `<claudeBin> <claudeArgs...>` after the trap.
|
|
259
|
+
args.push('/bin/sh', '-c', 'trap "" HUP; exec "$@"', 'sh', p.claudeBin);
|
|
260
|
+
for (const a of p.claudeArgs)
|
|
261
|
+
args.push(a);
|
|
311
262
|
return { cmd: 'systemd-run', args };
|
|
312
263
|
}
|
|
313
264
|
/** Query `systemctl --user show -p MainPID claude-session-<token>.scope`
|