aicodeman 1.12.2 → 1.14.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.
Files changed (130) hide show
  1. package/README.md +93 -15
  2. package/dist/ai-checker-base.d.ts +2 -0
  3. package/dist/ai-checker-base.d.ts.map +1 -1
  4. package/dist/ai-checker-base.js +31 -3
  5. package/dist/ai-checker-base.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +142 -14
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config/agent-wait.d.ts +73 -0
  10. package/dist/config/agent-wait.d.ts.map +1 -0
  11. package/dist/config/agent-wait.js +99 -0
  12. package/dist/config/agent-wait.js.map +1 -0
  13. package/dist/config/service-names.d.ts +23 -0
  14. package/dist/config/service-names.d.ts.map +1 -0
  15. package/dist/config/service-names.js +29 -0
  16. package/dist/config/service-names.js.map +1 -0
  17. package/dist/daemon-control.d.ts +126 -0
  18. package/dist/daemon-control.d.ts.map +1 -0
  19. package/dist/daemon-control.js +433 -0
  20. package/dist/daemon-control.js.map +1 -0
  21. package/dist/hooks-config.d.ts +28 -6
  22. package/dist/hooks-config.d.ts.map +1 -1
  23. package/dist/hooks-config.js +214 -28
  24. package/dist/hooks-config.js.map +1 -1
  25. package/dist/service-installer.d.ts +96 -0
  26. package/dist/service-installer.d.ts.map +1 -0
  27. package/dist/service-installer.js +333 -0
  28. package/dist/service-installer.js.map +1 -0
  29. package/dist/session-cli-builder.d.ts.map +1 -1
  30. package/dist/session-cli-builder.js +6 -2
  31. package/dist/session-cli-builder.js.map +1 -1
  32. package/dist/tmux-manager.d.ts.map +1 -1
  33. package/dist/tmux-manager.js +5 -1
  34. package/dist/tmux-manager.js.map +1 -1
  35. package/dist/web/public/admin-ui.js.gz +0 -0
  36. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  37. package/dist/web/public/{app.2e9ffd31.js → app.b24f795e.js} +5 -7
  38. package/dist/web/public/app.b24f795e.js.br +0 -0
  39. package/dist/web/public/app.b24f795e.js.gz +0 -0
  40. package/dist/web/public/constants.e7175b95.js.gz +0 -0
  41. package/dist/web/public/cron-ui.js.gz +0 -0
  42. package/dist/web/public/entrance-animations.js.gz +0 -0
  43. package/dist/web/public/i18n.da34cfaf.js +1 -0
  44. package/dist/web/public/i18n.da34cfaf.js.br +0 -0
  45. package/dist/web/public/i18n.da34cfaf.js.gz +0 -0
  46. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  47. package/dist/web/public/index.html +13 -6
  48. package/dist/web/public/index.html.br +0 -0
  49. package/dist/web/public/index.html.gz +0 -0
  50. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  51. package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
  52. package/dist/web/public/mobile-handlers.32fdd57f.js.gz +0 -0
  53. package/dist/web/public/mobile-overview.js.gz +0 -0
  54. package/dist/web/public/mobile.32cacde5.css.gz +0 -0
  55. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  56. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  57. package/dist/web/public/panels-ui.1d4203e0.js.gz +0 -0
  58. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  59. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  60. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  61. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  62. package/dist/web/public/session-ui.1356a4f4.js.gz +0 -0
  63. package/dist/web/public/settings-ui.ea63d5db.js +67 -0
  64. package/dist/web/public/settings-ui.ea63d5db.js.br +0 -0
  65. package/dist/web/public/settings-ui.ea63d5db.js.gz +0 -0
  66. package/dist/web/public/styles.b10e5edd.css +1 -0
  67. package/dist/web/public/styles.b10e5edd.css.br +0 -0
  68. package/dist/web/public/styles.b10e5edd.css.gz +0 -0
  69. package/dist/web/public/subagent-windows.4cc8b005.js.gz +0 -0
  70. package/dist/web/public/sw.js.gz +0 -0
  71. package/dist/web/public/{terminal-ui.78c71f3a.js → terminal-ui.d5ae6dcf.js} +2 -2
  72. package/dist/web/public/terminal-ui.d5ae6dcf.js.br +0 -0
  73. package/dist/web/public/terminal-ui.d5ae6dcf.js.gz +0 -0
  74. package/dist/web/public/ultracode-panel.js.gz +0 -0
  75. package/dist/web/public/ultracode-windows.js.gz +0 -0
  76. package/dist/web/public/upload.html.gz +0 -0
  77. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  78. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  79. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  80. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  81. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  82. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  83. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  84. package/dist/web/public/vendor/xterm.css.gz +0 -0
  85. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  86. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  87. package/dist/web/public/webview-tabs.js +1 -2
  88. package/dist/web/public/webview-tabs.js.br +0 -0
  89. package/dist/web/public/webview-tabs.js.gz +0 -0
  90. package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
  91. package/dist/web/routes/hook-event-routes.js +22 -0
  92. package/dist/web/routes/hook-event-routes.js.map +1 -1
  93. package/dist/web/routes/session-routes.d.ts +4 -0
  94. package/dist/web/routes/session-routes.d.ts.map +1 -1
  95. package/dist/web/routes/session-routes.js +499 -17
  96. package/dist/web/routes/session-routes.js.map +1 -1
  97. package/dist/web/schemas.d.ts +53 -0
  98. package/dist/web/schemas.d.ts.map +1 -1
  99. package/dist/web/schemas.js +59 -0
  100. package/dist/web/schemas.js.map +1 -1
  101. package/dist/web/self-update.d.ts.map +1 -1
  102. package/dist/web/self-update.js +4 -4
  103. package/dist/web/self-update.js.map +1 -1
  104. package/dist/web/server.d.ts.map +1 -1
  105. package/dist/web/server.js +14 -0
  106. package/dist/web/server.js.map +1 -1
  107. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  108. package/dist/web/session-listener-wiring.js +28 -0
  109. package/dist/web/session-listener-wiring.js.map +1 -1
  110. package/dist/web/session-wait-registry.d.ts +407 -0
  111. package/dist/web/session-wait-registry.d.ts.map +1 -0
  112. package/dist/web/session-wait-registry.js +895 -0
  113. package/dist/web/session-wait-registry.js.map +1 -0
  114. package/package.json +2 -1
  115. package/skills/codeman/SKILL.md +274 -0
  116. package/skills/codeman/reference/endpoints.md +214 -0
  117. package/skills/codeman/reference/recipes.md +249 -0
  118. package/dist/web/public/app.2e9ffd31.js.br +0 -0
  119. package/dist/web/public/app.2e9ffd31.js.gz +0 -0
  120. package/dist/web/public/i18n.0fbea500.js +0 -1
  121. package/dist/web/public/i18n.0fbea500.js.br +0 -0
  122. package/dist/web/public/i18n.0fbea500.js.gz +0 -0
  123. package/dist/web/public/settings-ui.01a1ae7d.js +0 -67
  124. package/dist/web/public/settings-ui.01a1ae7d.js.br +0 -0
  125. package/dist/web/public/settings-ui.01a1ae7d.js.gz +0 -0
  126. package/dist/web/public/styles.50cd52bd.css +0 -1
  127. package/dist/web/public/styles.50cd52bd.css.br +0 -0
  128. package/dist/web/public/styles.50cd52bd.css.gz +0 -0
  129. package/dist/web/public/terminal-ui.78c71f3a.js.br +0 -0
  130. package/dist/web/public/terminal-ui.78c71f3a.js.gz +0 -0
@@ -0,0 +1,433 @@
1
+ /**
2
+ * @fileoverview Detached `codeman web` control: start (-d), stop, status.
3
+ *
4
+ * Backs `codeman web -d`, `codeman web --stop` and `codeman web --status`. The
5
+ * server itself is unchanged; this module re-launches the SAME entry script in a
6
+ * new session (`detached: true` calls setsid), so the child has no controlling
7
+ * terminal and no shell job entry. That is what actually makes it outlive the
8
+ * shell: `nohup` does not, because Node re-arms SIGHUP to its default disposition
9
+ * even when it inherits "ignore", and `cli.ts` installs a SIGHUP handler that
10
+ * shuts the server down gracefully (issue #231).
11
+ *
12
+ * Two rules shape the rest of the module:
13
+ *
14
+ * 1. **Never start a second server on one data dir.** `~/.codeman` and the
15
+ * `tmux -L codeman` socket are process-wide (config/instance.ts), so a second
16
+ * instance discovers and attaches PTYs to the first one's live sessions and
17
+ * starts resizing them. A double `-d` therefore has to be a hard error, which
18
+ * means checking both the pidfile AND the port before spawning.
19
+ * 2. **Never report success we have not seen.** The parent polls `/api/status`
20
+ * until the child answers (or dies) before printing a URL. A port clash or a
21
+ * missing dependency otherwise looks exactly like a clean start.
22
+ *
23
+ * Pure helpers (arg building, URL building, pidfile parsing, the process-identity
24
+ * check) are exported separately so they can be unit-tested without spawning.
25
+ *
26
+ * @module daemon-control
27
+ */
28
+ import { spawn, execFileSync } from 'node:child_process';
29
+ import { appendFileSync, closeSync, existsSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
30
+ import http from 'node:http';
31
+ import https from 'node:https';
32
+ import { dataPath } from './config/instance.js';
33
+ import { EXEC_TIMEOUT_MS } from './config/exec-timeout.js';
34
+ /** How long to wait for a freshly spawned server to answer `/api/status`. */
35
+ const START_TIMEOUT_MS = 30_000;
36
+ /** How long to wait for a SIGTERM'd server to actually exit before giving up. */
37
+ const STOP_TIMEOUT_MS = 15_000;
38
+ /** Poll interval while waiting for either of the above. */
39
+ const POLL_INTERVAL_MS = 250;
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ // Pure helpers
42
+ // ─────────────────────────────────────────────────────────────────────────────
43
+ /** Rebuild the `web` argv for the child, dropping the daemon flags themselves. */
44
+ export function buildWebArgs(options) {
45
+ const args = ['web', '--host', options.host, '--port', String(options.port)];
46
+ if (options.https)
47
+ args.push('--https');
48
+ if (options.titleHostname)
49
+ args.push('--title-hostname', options.titleHostname);
50
+ if (options.allowUnauthenticatedNetwork)
51
+ args.push('--allow-unauthenticated-network');
52
+ if (options.multiuser)
53
+ args.push('--multiuser');
54
+ return args;
55
+ }
56
+ /**
57
+ * Connectable address for this bind. A wildcard bind is not itself connectable,
58
+ * so `0.0.0.0` / `::` become loopback; a bare IPv6 literal gets bracketed.
59
+ */
60
+ export function buildBaseUrl(options) {
61
+ const protocol = options.https ? 'https' : 'http';
62
+ let host = options.host.trim();
63
+ if (host === '0.0.0.0' || host === '::' || host === '')
64
+ host = '127.0.0.1';
65
+ if (host.includes(':') && !host.startsWith('['))
66
+ host = `[${host}]`;
67
+ return `${protocol}://${host}:${options.port}`;
68
+ }
69
+ /** The endpoint polled for readiness. */
70
+ export function buildStatusUrl(options) {
71
+ return `${buildBaseUrl(options)}/api/status`;
72
+ }
73
+ /** Parse a pidfile body. Rejects garbage, and pid 1 (init is never ours). */
74
+ export function parsePidFileContents(text) {
75
+ const trimmed = text.trim();
76
+ if (!/^\d+$/.test(trimmed))
77
+ return null;
78
+ const pid = Number.parseInt(trimmed, 10);
79
+ if (!Number.isSafeInteger(pid) || pid <= 1)
80
+ return null;
81
+ return pid;
82
+ }
83
+ /**
84
+ * Does this command line look like a Codeman web server?
85
+ *
86
+ * Pids are recycled, and a stale pidfile pointing at whatever inherited the
87
+ * number is a live footgun: `codeman web --stop` must not SIGTERM an unrelated
88
+ * process. Both the npm bin (`codeman`/`aicodeman`) and the direct entry
89
+ * (`node dist/index.js web`, `tsx src/index.ts web`) have to match.
90
+ */
91
+ export function looksLikeCodemanWeb(command) {
92
+ if (!command)
93
+ return false;
94
+ if (!/(^|\s)web(\s|$)/.test(command))
95
+ return false;
96
+ return /(^|[/\s])(ai)?codeman(\s|$)/.test(command) || /index\.(js|ts)(\s|$)/.test(command);
97
+ }
98
+ // ─────────────────────────────────────────────────────────────────────────────
99
+ // Paths
100
+ // ─────────────────────────────────────────────────────────────────────────────
101
+ /**
102
+ * Resolved at call time, not module load: tests swap `HOME` per file, and the
103
+ * data dir is derived from it (see test/setup.ts).
104
+ */
105
+ export function pidFilePath() {
106
+ return dataPath('web.pid');
107
+ }
108
+ /** Where a detached server's stdout/stderr is appended. */
109
+ export function logFilePath() {
110
+ return dataPath('web.log');
111
+ }
112
+ // ─────────────────────────────────────────────────────────────────────────────
113
+ // Process probing
114
+ // ─────────────────────────────────────────────────────────────────────────────
115
+ /** Signal 0 liveness check. EPERM means the pid exists but is not ours. */
116
+ export function isProcessAlive(pid) {
117
+ try {
118
+ process.kill(pid, 0);
119
+ return true;
120
+ }
121
+ catch (err) {
122
+ return err.code === 'EPERM';
123
+ }
124
+ }
125
+ /** Full command line of a pid, or null. `-o command=` is portable to macOS. */
126
+ export function readProcessCommand(pid) {
127
+ try {
128
+ const out = execFileSync('ps', ['-o', 'command=', '-p', String(pid)], {
129
+ encoding: 'utf-8',
130
+ timeout: EXEC_TIMEOUT_MS,
131
+ stdio: ['ignore', 'pipe', 'ignore'],
132
+ });
133
+ return out.trim() || null;
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ /** Read the pidfile, returning null when it is missing, empty or malformed. */
140
+ export function readPidFile() {
141
+ const file = pidFilePath();
142
+ if (!existsSync(file))
143
+ return null;
144
+ try {
145
+ return parsePidFileContents(readFileSync(file, 'utf-8'));
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ }
151
+ function removePidFile() {
152
+ try {
153
+ unlinkSync(pidFilePath());
154
+ }
155
+ catch {
156
+ /* already gone */
157
+ }
158
+ }
159
+ /** Pid of a live Codeman web server recorded in the pidfile, or null. */
160
+ export function readLivePid() {
161
+ const pid = readPidFile();
162
+ if (pid === null)
163
+ return null;
164
+ if (!isProcessAlive(pid))
165
+ return null;
166
+ // A recycled pid is not ours. `ps` can also legitimately fail (containers with
167
+ // no procps); treat "cannot tell" as ours rather than orphaning the pidfile.
168
+ const command = readProcessCommand(pid);
169
+ if (command !== null && !looksLikeCodemanWeb(command))
170
+ return null;
171
+ return pid;
172
+ }
173
+ /**
174
+ * Probe `/api/status`. Self-signed certs are accepted (`--https` generates one),
175
+ * and 401 counts as up because `CODEMAN_PASSWORD` gates that route. The body is
176
+ * checked so an unrelated service squatting on the port is not read as success.
177
+ */
178
+ export function probeServer(url, timeoutMs = 2000) {
179
+ return new Promise((resolve) => {
180
+ let settled = false;
181
+ const done = (result) => {
182
+ if (settled)
183
+ return;
184
+ settled = true;
185
+ resolve(result);
186
+ };
187
+ let target;
188
+ try {
189
+ target = new URL(url);
190
+ }
191
+ catch {
192
+ done({ up: false });
193
+ return;
194
+ }
195
+ const transport = target.protocol === 'https:' ? https : http;
196
+ const req = transport.request({
197
+ protocol: target.protocol,
198
+ hostname: target.hostname,
199
+ port: target.port,
200
+ path: target.pathname,
201
+ method: 'GET',
202
+ rejectUnauthorized: false,
203
+ timeout: timeoutMs,
204
+ headers: { Accept: 'application/json' },
205
+ }, (res) => {
206
+ if (res.statusCode === 401) {
207
+ res.resume();
208
+ done({ up: true });
209
+ return;
210
+ }
211
+ let body = '';
212
+ res.setEncoding('utf-8');
213
+ res.on('data', (chunk) => {
214
+ if (body.length < 4096)
215
+ body += chunk;
216
+ });
217
+ res.on('end', () => {
218
+ if (!body.includes('"success"')) {
219
+ done({ up: false });
220
+ return;
221
+ }
222
+ let version;
223
+ try {
224
+ version = JSON.parse(body).data?.version;
225
+ }
226
+ catch {
227
+ /* body was truncated at 4KB; up is still true */
228
+ }
229
+ done({ up: true, version });
230
+ });
231
+ res.on('error', () => done({ up: false }));
232
+ });
233
+ req.on('timeout', () => {
234
+ req.destroy();
235
+ done({ up: false });
236
+ });
237
+ req.on('error', () => done({ up: false }));
238
+ req.end();
239
+ });
240
+ }
241
+ function sleep(ms) {
242
+ return new Promise((resolve) => setTimeout(resolve, ms));
243
+ }
244
+ // ─────────────────────────────────────────────────────────────────────────────
245
+ // Start / stop / status
246
+ // ─────────────────────────────────────────────────────────────────────────────
247
+ /**
248
+ * The script to relaunch. `process.execArgv` is carried over with it so a dev
249
+ * run under tsx (whose execArgv holds the tsx loader flags) re-launches through
250
+ * tsx instead of handing a `.ts` file to bare node.
251
+ */
252
+ function entryScript() {
253
+ const script = process.argv[1];
254
+ if (!script)
255
+ throw new Error('cannot determine the codeman entry script to relaunch');
256
+ return script;
257
+ }
258
+ /** Marks one launch in the append-only log so a tail cannot mix two runs. */
259
+ const LOG_SEPARATOR = '=== codeman web start';
260
+ /**
261
+ * Last few lines of the daemon log, for reporting a failed start. The log is
262
+ * append-only across launches, so the tail starts at the last separator when
263
+ * there is one: otherwise a crash report is padded with the previous run's
264
+ * cheerful startup banner.
265
+ */
266
+ export function tailLog(maxLines = 15) {
267
+ try {
268
+ const lines = readFileSync(logFilePath(), 'utf-8').trimEnd().split('\n');
269
+ const start = lines.map((line) => line.startsWith(LOG_SEPARATOR)).lastIndexOf(true);
270
+ const current = start === -1 ? lines : lines.slice(start + 1);
271
+ return current.slice(-maxLines).join('\n');
272
+ }
273
+ catch {
274
+ return '';
275
+ }
276
+ }
277
+ /**
278
+ * Spawn a detached `codeman web` and wait until it answers before returning.
279
+ * Refuses when a server is already up on this data dir (see rule 1 in the module
280
+ * docblock).
281
+ */
282
+ export async function startDaemon(options) {
283
+ const logPath = logFilePath();
284
+ const url = buildBaseUrl(options);
285
+ const statusUrl = buildStatusUrl(options);
286
+ const existingPid = readLivePid();
287
+ if (existingPid !== null) {
288
+ return {
289
+ ok: false,
290
+ reason: 'already-running',
291
+ pid: existingPid,
292
+ logPath,
293
+ message: `a Codeman server is already running (pid ${existingPid}). Stop it with \`codeman web --stop\` first.`,
294
+ };
295
+ }
296
+ const alreadyServing = await probeServer(statusUrl, 1500);
297
+ if (alreadyServing.up) {
298
+ return {
299
+ ok: false,
300
+ reason: 'already-running',
301
+ logPath,
302
+ url,
303
+ message: `something is already serving ${url}. Two servers on one data dir attach to each other's tmux sessions, so refusing to start.`,
304
+ };
305
+ }
306
+ // A pidfile that survived a crash: the process is gone, so it is just litter.
307
+ if (readPidFile() !== null)
308
+ removePidFile();
309
+ const args = buildWebArgs(options);
310
+ try {
311
+ appendFileSync(logPath, `\n${LOG_SEPARATOR} ${new Date().toISOString()} ===\n`, 'utf-8');
312
+ }
313
+ catch {
314
+ /* the spawn below reports a genuinely unwritable log */
315
+ }
316
+ const logFd = openSync(logPath, 'a');
317
+ let child;
318
+ try {
319
+ child = spawn(process.execPath, [...process.execArgv, entryScript(), ...args], {
320
+ detached: true,
321
+ stdio: ['ignore', logFd, logFd],
322
+ env: process.env,
323
+ });
324
+ }
325
+ finally {
326
+ closeSync(logFd);
327
+ }
328
+ let exited = false;
329
+ child.on('exit', () => {
330
+ exited = true;
331
+ });
332
+ child.on('error', () => {
333
+ exited = true;
334
+ });
335
+ const pid = child.pid;
336
+ if (pid === undefined) {
337
+ return { ok: false, reason: 'exited', logPath, message: 'failed to spawn the server process' };
338
+ }
339
+ writeFileSync(pidFilePath(), `${pid}\n`, 'utf-8');
340
+ const deadline = Date.now() + START_TIMEOUT_MS;
341
+ while (Date.now() < deadline) {
342
+ if (exited) {
343
+ removePidFile();
344
+ child.unref();
345
+ return {
346
+ ok: false,
347
+ reason: 'exited',
348
+ logPath,
349
+ message: `the server exited during startup. Last lines of ${logPath}:\n${tailLog()}`,
350
+ };
351
+ }
352
+ const probe = await probeServer(statusUrl, 1000);
353
+ if (probe.up) {
354
+ child.unref();
355
+ return { ok: true, pid, url, logPath };
356
+ }
357
+ await sleep(POLL_INTERVAL_MS);
358
+ }
359
+ child.unref();
360
+ return {
361
+ ok: false,
362
+ reason: 'timeout',
363
+ pid,
364
+ url,
365
+ logPath,
366
+ message: `the server did not answer ${url} within ${START_TIMEOUT_MS / 1000}s. It may still be starting; check ${logPath}.`,
367
+ };
368
+ }
369
+ /** SIGTERM the recorded server and wait for it to actually exit. */
370
+ export async function stopDaemon(options) {
371
+ const pid = readPidFile();
372
+ if (pid === null) {
373
+ const probe = await probeServer(buildStatusUrl(options), 1500);
374
+ if (probe.up) {
375
+ return {
376
+ ok: false,
377
+ reason: 'no-pidfile-but-responding',
378
+ message: 'a server is responding but there is no pidfile, so it was not started with `-d`. If it is a service use `codeman service uninstall` (or stop the unit); otherwise `pkill -f "index.js web"`.',
379
+ };
380
+ }
381
+ return { ok: true, reason: 'not-running', message: 'no daemon is running; nothing to stop' };
382
+ }
383
+ if (!isProcessAlive(pid)) {
384
+ removePidFile();
385
+ return { ok: true, pid, message: `stale pidfile removed (pid ${pid} was not running)` };
386
+ }
387
+ const command = readProcessCommand(pid);
388
+ if (command !== null && !looksLikeCodemanWeb(command)) {
389
+ return {
390
+ ok: false,
391
+ reason: 'foreign-pid',
392
+ pid,
393
+ message: `pid ${pid} is not a Codeman server (${command}). Refusing to signal it; delete ${pidFilePath()} if it is stale.`,
394
+ };
395
+ }
396
+ // SIGTERM, never SIGKILL: cli.ts flushes state on the way out.
397
+ try {
398
+ process.kill(pid, 'SIGTERM');
399
+ }
400
+ catch (err) {
401
+ return { ok: false, reason: 'foreign-pid', pid, message: `could not signal pid ${pid}: ${String(err)}` };
402
+ }
403
+ const deadline = Date.now() + STOP_TIMEOUT_MS;
404
+ while (Date.now() < deadline) {
405
+ if (!isProcessAlive(pid)) {
406
+ removePidFile();
407
+ return { ok: true, pid };
408
+ }
409
+ await sleep(POLL_INTERVAL_MS);
410
+ }
411
+ return {
412
+ ok: false,
413
+ reason: 'timeout',
414
+ pid,
415
+ message: `pid ${pid} did not exit within ${STOP_TIMEOUT_MS / 1000}s. Force it with \`kill -9 ${pid}\` if you are sure.`,
416
+ };
417
+ }
418
+ /** Report on both halves: the recorded process, and whether the port answers. */
419
+ export async function daemonStatus(options) {
420
+ const url = buildBaseUrl(options);
421
+ const pid = readPidFile();
422
+ const probe = await probeServer(buildStatusUrl(options), 2000);
423
+ return {
424
+ pid,
425
+ running: readLivePid() !== null,
426
+ responding: probe.up,
427
+ version: probe.version,
428
+ url,
429
+ pidFile: pidFilePath(),
430
+ logPath: logFilePath(),
431
+ };
432
+ }
433
+ //# sourceMappingURL=daemon-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-control.js","sourceRoot":"","sources":["../src/daemon-control.ts"],"names":[],"mappings":"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"}
@@ -10,13 +10,15 @@
10
10
  * Key exports:
11
11
  * - `generateHooksConfig()` — returns hooks object for settings.local.json
12
12
  * - `writeHooksConfig(casePath)` — writes hooks + env config to disk
13
+ * - `ensureCodemanHooks(casePath)` — safely installs/updates hooks for a managed case
13
14
  * - `updateCaseEnvVars(casePath, envVars)` — merges env vars into settings
14
15
  *
15
16
  * Hook events generated: `idle_prompt`, `permission_prompt`, `elicitation_dialog`,
16
17
  * `stop`, `teammate_idle`, `task_completed`
17
18
  *
18
- * Hook categories: `Notification` (3 matchers), `Stop` (1), `TeammateIdle` (1),
19
- * `TaskCompleted` (1), `PostToolUse` (1 self-contained background Bash rewake)
19
+ * Hook categories: `Notification` (3 matchers), `Stop` (1), `SubagentStop` (1),
20
+ * `TeammateIdle` (1), `TaskCompleted` (1), `PostToolUse` (1 self-contained
21
+ * background Bash rewake)
20
22
  *
21
23
  * @dependencies types (HookEventType), config/auth-config (HOOK_TIMEOUT_SECONDS)
22
24
  * @consumedby web/server (session creation), session-cli-builder (env setup)
@@ -27,8 +29,10 @@
27
29
  * Inline Node helper for Claude Code's `asyncRewake` hook.
28
30
  *
29
31
  * A background Bash tool returns immediately with a task ID, then Claude writes
30
- * its completion as a queue-operation in the transcript. Watching that durable
31
- * record avoids injecting terminal input (which could submit a user's draft).
32
+ * its completion as a queue-operation in the top-level transcript. Subagent hooks
33
+ * receive their own transcript path even though their completion is parent-owned,
34
+ * so the helper watches both paths. Watching durable records avoids injecting
35
+ * terminal input (which could submit a user's draft).
32
36
  * The helper is embedded in settings via `node -e`, so it has no script path
33
37
  * that can go stale after an install or plugin-cache cleanup.
34
38
  *
@@ -38,6 +42,12 @@
38
42
  * forever. The ppid check misses subreaper setups; the deadline is the backstop.
39
43
  */
40
44
  export declare function generateBackgroundWakeScript(): string;
45
+ /**
46
+ * Keep a Claude subagent alive while its Monitor or background Bash work is live.
47
+ * Claude otherwise can publish the worker's last progress sentence as an Agent
48
+ * result when one watcher ends, even if other tracked tasks are still running.
49
+ */
50
+ export declare function generateSubagentStopGuardScript(): string;
41
51
  /**
42
52
  * Generates the hooks section for .claude/settings.local.json
43
53
  *
@@ -72,6 +82,15 @@ export declare function updateCaseModel(casePath: string, model: string | null):
72
82
  * Merges with existing file content, only touching the `hooks` key.
73
83
  */
74
84
  export declare function writeHooksConfig(casePath: string): Promise<void>;
85
+ /**
86
+ * Ensures an explicitly managed case has the current Codeman hooks.
87
+ *
88
+ * Unlike `refreshStaleCodemanHooks`, this may add Codeman handlers to a valid
89
+ * user-owned settings file. It is therefore reserved for case quick-starts,
90
+ * where the user has explicitly asked Codeman to manage that workspace. A
91
+ * malformed existing file is left untouched rather than replaced.
92
+ */
93
+ export declare function ensureCodemanHooks(casePath: string): Promise<void>;
75
94
  /**
76
95
  * Self-heal a case's Codeman-owned hooks block.
77
96
  *
@@ -79,8 +98,11 @@ export declare function writeHooksConfig(casePath: string): Promise<void>;
79
98
  * X-Codeman-Hook-Secret header was added (COD-54, 2026-06-10) keep hook curls in their
80
99
  * settings.local.json that POST to /api/hook-event WITHOUT the secret — which, once the
81
100
  * gate requires it unconditionally (COD-91), silently 401 on a password-protected install.
82
- * Older Codeman blocks also lack the background Bash async-rewake hook. Refresh either
83
- * stale shape on launch so existing cases gain both current behaviors.
101
+ * Older Codeman blocks also lack the current background Bash async-rewake hook or the
102
+ * SubagentStop guard. A further stale shape: hook curls without `-k`, which exit 60 on
103
+ * every --https/tailscale install (the cert is self-signed), swallowed by the hooks'
104
+ * own `|| true` — all six hook events die silently. Refresh any of these stale shapes
105
+ * on launch so existing cases heal.
84
106
  *
85
107
  * Deliberately surgical: regenerates ONLY when settings.local.json already contains
86
108
  * Codeman's own hook curls (they target `/api/hook-event`) and they are stale. No-op
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-config.d.ts","sourceRoot":"","sources":["../src/hooks-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAiCH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAmErD;AAiBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAiE1E;AAsED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BvG;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BxG;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3F;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB9E;AAKD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAalD;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B7F"}
1
+ {"version":3,"file":"hooks-config.d.ts","sourceRoot":"","sources":["../src/hooks-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAmCH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CA4GrD;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,IAAI,MAAM,CA0ExD;AAiBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAiF1E;AA0ED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BvG;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BxG;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB3F;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBtE;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBxE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B9E;AAKD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAalD;AAED;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8B7F"}