aicodeman 1.13.0 → 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 (75) hide show
  1. package/README.md +26 -2
  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/service-names.d.ts +23 -0
  10. package/dist/config/service-names.d.ts.map +1 -0
  11. package/dist/config/service-names.js +29 -0
  12. package/dist/config/service-names.js.map +1 -0
  13. package/dist/daemon-control.d.ts +126 -0
  14. package/dist/daemon-control.d.ts.map +1 -0
  15. package/dist/daemon-control.js +433 -0
  16. package/dist/daemon-control.js.map +1 -0
  17. package/dist/hooks-config.d.ts +28 -8
  18. package/dist/hooks-config.d.ts.map +1 -1
  19. package/dist/hooks-config.js +205 -29
  20. package/dist/hooks-config.js.map +1 -1
  21. package/dist/service-installer.d.ts +96 -0
  22. package/dist/service-installer.d.ts.map +1 -0
  23. package/dist/service-installer.js +333 -0
  24. package/dist/service-installer.js.map +1 -0
  25. package/dist/web/public/admin-ui.js.gz +0 -0
  26. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  27. package/dist/web/public/app.b24f795e.js.gz +0 -0
  28. package/dist/web/public/constants.e7175b95.js.gz +0 -0
  29. package/dist/web/public/cron-ui.js.gz +0 -0
  30. package/dist/web/public/entrance-animations.js.gz +0 -0
  31. package/dist/web/public/i18n.da34cfaf.js.gz +0 -0
  32. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  33. package/dist/web/public/index.html +1 -1
  34. package/dist/web/public/index.html.br +0 -0
  35. package/dist/web/public/index.html.gz +0 -0
  36. package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
  37. package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
  38. package/dist/web/public/mobile-handlers.32fdd57f.js.gz +0 -0
  39. package/dist/web/public/mobile-overview.js.gz +0 -0
  40. package/dist/web/public/mobile.32cacde5.css.gz +0 -0
  41. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  42. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  43. package/dist/web/public/panels-ui.1d4203e0.js.gz +0 -0
  44. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  45. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  46. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  47. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  48. package/dist/web/public/session-ui.1356a4f4.js.gz +0 -0
  49. package/dist/web/public/settings-ui.ea63d5db.js.gz +0 -0
  50. package/dist/web/public/styles.b10e5edd.css.gz +0 -0
  51. package/dist/web/public/subagent-windows.4cc8b005.js.gz +0 -0
  52. package/dist/web/public/sw.js.gz +0 -0
  53. package/dist/web/public/{terminal-ui.78c71f3a.js → terminal-ui.d5ae6dcf.js} +2 -2
  54. package/dist/web/public/terminal-ui.d5ae6dcf.js.br +0 -0
  55. package/dist/web/public/terminal-ui.d5ae6dcf.js.gz +0 -0
  56. package/dist/web/public/ultracode-panel.js.gz +0 -0
  57. package/dist/web/public/ultracode-windows.js.gz +0 -0
  58. package/dist/web/public/upload.html.gz +0 -0
  59. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  60. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  61. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  62. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  63. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  64. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  65. package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
  66. package/dist/web/public/vendor/xterm.css.gz +0 -0
  67. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  68. package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
  69. package/dist/web/public/webview-tabs.js.gz +0 -0
  70. package/dist/web/self-update.d.ts.map +1 -1
  71. package/dist/web/self-update.js +4 -4
  72. package/dist/web/self-update.js.map +1 -1
  73. package/package.json +1 -1
  74. package/dist/web/public/terminal-ui.78c71f3a.js.br +0 -0
  75. package/dist/web/public/terminal-ui.78c71f3a.js.gz +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-names.d.ts","sourceRoot":"","sources":["../../src/config/service-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAUH,0FAA0F;AAC1F,eAAO,MAAM,YAAY,QAAmE,CAAC;AAE7F,kFAAkF;AAClF,eAAO,MAAM,aAAa,QAAyE,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @fileoverview Supervisor identity (systemd unit name / launchd job label).
3
+ *
4
+ * Three things now write or look for the same supervisor job: `install.sh`, the
5
+ * in-app self-updater (`web/self-update.ts` detects it to decide how to restart),
6
+ * and `codeman service install`. The names live here so they cannot drift apart,
7
+ * because a mismatch is silent in the worst way: `service install` would happily
8
+ * create a SECOND job alongside the installer's, and two servers sharing one data
9
+ * dir and one tmux socket attach PTYs to each other's live sessions
10
+ * (see config/instance.ts).
11
+ *
12
+ * The names are instance-scoped for exactly that reason: a `CODEMAN_INSTANCE=beta`
13
+ * build writing `com.codeman.web` would overwrite the production LaunchAgent. The
14
+ * DEFAULT instance keeps the historical names byte-identical, so existing installs
15
+ * and every unit install.sh has already written are unaffected.
16
+ *
17
+ * @module config/service-names
18
+ */
19
+ import { CODEMAN_INSTANCE } from './instance.js';
20
+ /**
21
+ * Instance name reduced to characters that are safe in a filename and in a
22
+ * launchd label. `CODEMAN_INSTANCE` is arbitrary operator input.
23
+ */
24
+ const SAFE_INSTANCE = CODEMAN_INSTANCE.replace(/[^A-Za-z0-9_-]/g, '').slice(0, 32);
25
+ /** systemd user unit: `codeman-web.service`, or `codeman-web-beta.service` for a beta. */
26
+ export const SYSTEMD_UNIT = `codeman-web${SAFE_INSTANCE ? `-${SAFE_INSTANCE}` : ''}.service`;
27
+ /** launchd job label: `com.codeman.web`, or `com.codeman.beta.web` for a beta. */
28
+ export const LAUNCHD_LABEL = SAFE_INSTANCE ? `com.codeman.${SAFE_INSTANCE}.web` : 'com.codeman.web';
29
+ //# sourceMappingURL=service-names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-names.js","sourceRoot":"","sources":["../../src/config/service-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD;;;GAGG;AACH,MAAM,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEnF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;AAE7F,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC,CAAC,CAAC,eAAe,aAAa,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC"}
@@ -0,0 +1,126 @@
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
+ /** The `web` command's options, as far as a detached relaunch cares about them. */
29
+ export interface WebLaunchOptions {
30
+ host: string;
31
+ port: number;
32
+ https: boolean;
33
+ titleHostname?: string;
34
+ allowUnauthenticatedNetwork?: boolean;
35
+ multiuser?: boolean;
36
+ }
37
+ export interface StartResult {
38
+ ok: boolean;
39
+ pid?: number;
40
+ url?: string;
41
+ /** Machine-readable failure cause; `undefined` on success. */
42
+ reason?: 'already-running' | 'exited' | 'timeout';
43
+ message?: string;
44
+ logPath: string;
45
+ }
46
+ export interface StopResult {
47
+ ok: boolean;
48
+ pid?: number;
49
+ reason?: 'not-running' | 'foreign-pid' | 'timeout' | 'no-pidfile-but-responding';
50
+ message?: string;
51
+ }
52
+ export interface DaemonStatus {
53
+ pid: number | null;
54
+ /** The pid in the pidfile is alive AND still looks like a Codeman web process. */
55
+ running: boolean;
56
+ /** Something answered `/api/status` at the expected address. */
57
+ responding: boolean;
58
+ version?: string;
59
+ url: string;
60
+ pidFile: string;
61
+ logPath: string;
62
+ }
63
+ /** Rebuild the `web` argv for the child, dropping the daemon flags themselves. */
64
+ export declare function buildWebArgs(options: WebLaunchOptions): string[];
65
+ /**
66
+ * Connectable address for this bind. A wildcard bind is not itself connectable,
67
+ * so `0.0.0.0` / `::` become loopback; a bare IPv6 literal gets bracketed.
68
+ */
69
+ export declare function buildBaseUrl(options: WebLaunchOptions): string;
70
+ /** The endpoint polled for readiness. */
71
+ export declare function buildStatusUrl(options: WebLaunchOptions): string;
72
+ /** Parse a pidfile body. Rejects garbage, and pid 1 (init is never ours). */
73
+ export declare function parsePidFileContents(text: string): number | null;
74
+ /**
75
+ * Does this command line look like a Codeman web server?
76
+ *
77
+ * Pids are recycled, and a stale pidfile pointing at whatever inherited the
78
+ * number is a live footgun: `codeman web --stop` must not SIGTERM an unrelated
79
+ * process. Both the npm bin (`codeman`/`aicodeman`) and the direct entry
80
+ * (`node dist/index.js web`, `tsx src/index.ts web`) have to match.
81
+ */
82
+ export declare function looksLikeCodemanWeb(command: string | null | undefined): boolean;
83
+ /**
84
+ * Resolved at call time, not module load: tests swap `HOME` per file, and the
85
+ * data dir is derived from it (see test/setup.ts).
86
+ */
87
+ export declare function pidFilePath(): string;
88
+ /** Where a detached server's stdout/stderr is appended. */
89
+ export declare function logFilePath(): string;
90
+ /** Signal 0 liveness check. EPERM means the pid exists but is not ours. */
91
+ export declare function isProcessAlive(pid: number): boolean;
92
+ /** Full command line of a pid, or null. `-o command=` is portable to macOS. */
93
+ export declare function readProcessCommand(pid: number): string | null;
94
+ /** Read the pidfile, returning null when it is missing, empty or malformed. */
95
+ export declare function readPidFile(): number | null;
96
+ /** Pid of a live Codeman web server recorded in the pidfile, or null. */
97
+ export declare function readLivePid(): number | null;
98
+ export interface ProbeResult {
99
+ /** A Codeman server answered. A 401 counts: auth is active, the server is up. */
100
+ up: boolean;
101
+ version?: string;
102
+ }
103
+ /**
104
+ * Probe `/api/status`. Self-signed certs are accepted (`--https` generates one),
105
+ * and 401 counts as up because `CODEMAN_PASSWORD` gates that route. The body is
106
+ * checked so an unrelated service squatting on the port is not read as success.
107
+ */
108
+ export declare function probeServer(url: string, timeoutMs?: number): Promise<ProbeResult>;
109
+ /**
110
+ * Last few lines of the daemon log, for reporting a failed start. The log is
111
+ * append-only across launches, so the tail starts at the last separator when
112
+ * there is one: otherwise a crash report is padded with the previous run's
113
+ * cheerful startup banner.
114
+ */
115
+ export declare function tailLog(maxLines?: number): string;
116
+ /**
117
+ * Spawn a detached `codeman web` and wait until it answers before returning.
118
+ * Refuses when a server is already up on this data dir (see rule 1 in the module
119
+ * docblock).
120
+ */
121
+ export declare function startDaemon(options: WebLaunchOptions): Promise<StartResult>;
122
+ /** SIGTERM the recorded server and wait for it to actually exit. */
123
+ export declare function stopDaemon(options: WebLaunchOptions): Promise<StopResult>;
124
+ /** Report on both halves: the recorded process, and whether the port answers. */
125
+ export declare function daemonStatus(options: WebLaunchOptions): Promise<DaemonStatus>;
126
+ //# sourceMappingURL=daemon-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-control.d.ts","sourceRoot":"","sources":["../src/daemon-control.ts"],"names":[],"mappings":"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"}
@@ -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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"}