@code-yeongyu/senpi 2026.8.31 → 2026.9.2
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/CHANGELOG.md +32 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +6 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +11 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +4 -4
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +108 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js +11 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/extensions/builtin/recommended-models/index.d.ts +1 -1
- package/dist/core/extensions/builtin/recommended-models/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/recommended-models/index.js +1 -1
- package/dist/core/extensions/builtin/recommended-models/index.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +1 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/main.d.ts +4 -4
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +10 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/app-server/daemon/process.d.ts +13 -2
- package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/process.js +58 -12
- package/dist/modes/app-server/daemon/process.js.map +1 -1
- package/dist/modes/app-server/daemon.js +62 -4
- package/dist/modes/app-server/daemon.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +11 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +35 -6
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/ethos-tips.js +1 -1
- package/dist/modes/interactive/tips/catalog/ethos-tips.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +8 -3
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/custom-capability.d.ts +1 -0
- package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
- package/dist/modes/rpc/custom-capability.js +1 -0
- package/dist/modes/rpc/custom-capability.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +93 -17
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +15 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +180 -55
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +2 -2
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +89 -21
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +94 -56
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +8 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +7 -2
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/socket-transport.d.ts +13 -0
- package/dist/modes/rpc/socket-transport.d.ts.map +1 -0
- package/dist/modes/rpc/socket-transport.js +92 -0
- package/dist/modes/rpc/socket-transport.js.map +1 -0
- package/docs/extensions.md +1 -1
- package/docs/rpc.md +5 -1
- package/docs/settings.md +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/groq.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -40,15 +40,17 @@
|
|
|
40
40
|
import { spawn } from "node:child_process";
|
|
41
41
|
import { randomUUID } from "node:crypto";
|
|
42
42
|
import { realpathSync, writeSync } from "node:fs";
|
|
43
|
-
import { access, chmod, mkdir, readFile, rm, unlink } from "node:fs/promises";
|
|
43
|
+
import { access, chmod, mkdir, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
44
44
|
import { createConnection, createServer } from "node:net";
|
|
45
45
|
import { tmpdir } from "node:os";
|
|
46
|
-
import { dirname, join, resolve } from "node:path";
|
|
46
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
47
47
|
import { fileURLToPath } from "node:url";
|
|
48
48
|
import { getAgentDir, isBunBinary } from "../../config.js";
|
|
49
|
+
import { readProcessStartTime } from "../app-server/daemon/process.js";
|
|
49
50
|
import { createHostDaemonPaths } from "./host-ensure.js";
|
|
50
51
|
import { HOST_CLEANUP_PATHS_ENV, HOST_SCRATCH_DIR_ENV, HOST_WATCH_FD_ENV, HOST_WATCH_PPID_ENV, } from "./host-watchdog.js";
|
|
51
52
|
import { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from "./jsonl.js";
|
|
53
|
+
import { authenticateSocket, createSocketSecret, readSocketSecret, resolveSocketTransportAddress, SOCKET_SECRET_FILE_ENV, sendSocketHandshake, socketSecretPath, } from "./socket-transport.js";
|
|
52
54
|
/** Environment override for the cold-start policy: `transient` or `persistent`. */
|
|
53
55
|
export const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
|
|
54
56
|
/** Environment override for the idle-exit window in milliseconds. */
|
|
@@ -56,6 +58,8 @@ export const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
|
|
|
56
58
|
/** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
|
|
57
59
|
export const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;
|
|
58
60
|
const CHILD_STOP_TIMEOUT_MS = 5_000;
|
|
61
|
+
/** Win32 named-pipe shutdown can leave supervisor handles live after close starts. */
|
|
62
|
+
const WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS = 2_000;
|
|
59
63
|
/**
|
|
60
64
|
* Child stdio slot carrying the supervisor-lifetime pipe. The supervisor holds
|
|
61
65
|
* the write end open and never writes; the kernel closes it when the supervisor
|
|
@@ -68,10 +72,24 @@ const CHILD_WATCH_FD = 3;
|
|
|
68
72
|
* regardless of where the public socket lives, and private against other local
|
|
69
73
|
* users, so it gets its own 0700 directory under the OS temp directory.
|
|
70
74
|
*/
|
|
71
|
-
async function createInternalSocketPath() {
|
|
75
|
+
async function createInternalSocketPath(baseDir = tmpdir()) {
|
|
76
|
+
if (process.platform === "win32") {
|
|
77
|
+
const dir = join(baseDir, `internal-${randomUUID()}`);
|
|
78
|
+
await mkdir(dir, { recursive: false, mode: 0o700 });
|
|
79
|
+
return {
|
|
80
|
+
socket: `\\\\.\\pipe\\senpi-rpc-internal-${randomUUID()}`,
|
|
81
|
+
dir,
|
|
82
|
+
secretPath: join(dir, "secret"),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
72
85
|
const dir = join(tmpdir(), `senpi-rpc-host-internal-${randomUUID().slice(0, 8)}`);
|
|
73
86
|
await mkdir(dir, { recursive: false, mode: 0o700 });
|
|
74
|
-
|
|
87
|
+
await writeFile(join(dir, ".owner"), JSON.stringify({
|
|
88
|
+
pid: process.pid,
|
|
89
|
+
processStartTime: await readProcessStartTime(process.pid),
|
|
90
|
+
createdAt: Date.now(),
|
|
91
|
+
}), { mode: 0o600 });
|
|
92
|
+
return { socket: join(dir, "host.sock"), dir, secretPath: join(dir, ".secret") };
|
|
75
93
|
}
|
|
76
94
|
export function parseColdStart(value) {
|
|
77
95
|
return value === "transient" || value === "persistent" ? value : undefined;
|
|
@@ -236,31 +254,72 @@ export function resolveHostChildLaunch(launch, internalSocket, compiled = isBunB
|
|
|
236
254
|
],
|
|
237
255
|
};
|
|
238
256
|
}
|
|
257
|
+
/** Mirrors cross-spawn: survives cmd.exe parsing and `CommandLineToArgvW`. */
|
|
258
|
+
function quoteWindowsShellArg(value) {
|
|
259
|
+
const escaped = value
|
|
260
|
+
.replace(/(\\*)"/g, '$1$1\\"')
|
|
261
|
+
.replace(/(\\*)$/, "$1$1")
|
|
262
|
+
.replace(/([()%!^"<>&|;,])/g, "^$1");
|
|
263
|
+
return `"${escaped}"`;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Windows refuses to spawn a `.cmd`/`.bat` without a shell, and Node's
|
|
267
|
+
* `shell: true` concatenates argv without escaping it. Escape each original
|
|
268
|
+
* value before adding the surrounding quotes so `.cmd`/`.bat` launchers survive
|
|
269
|
+
* cmd.exe parsing without double-escaping.
|
|
270
|
+
* Exported for tests.
|
|
271
|
+
*/
|
|
272
|
+
export function spawnableChildLaunch(launch, platform = process.platform) {
|
|
273
|
+
const extension = extname(launch.command).toLowerCase();
|
|
274
|
+
if (platform !== "win32" || (extension !== ".cmd" && extension !== ".bat")) {
|
|
275
|
+
return { ...launch, shell: false };
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
command: quoteWindowsShellArg(launch.command),
|
|
279
|
+
args: launch.args.map(quoteWindowsShellArg),
|
|
280
|
+
shell: true,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
239
283
|
export async function runHostSupervisor(launch) {
|
|
240
284
|
const paths = createHostDaemonPaths(launch.agentDir ?? getAgentDir());
|
|
241
285
|
const policy = resolveHostPolicy(await readSettingsFile(paths.settingsFile), process.env);
|
|
242
286
|
const publicSocket = launch.socket;
|
|
243
|
-
const internal = await createInternalSocketPath();
|
|
287
|
+
const internal = await createInternalSocketPath(paths.dir);
|
|
244
288
|
const internalSocket = internal.socket;
|
|
289
|
+
const internalSecretPath = internal.secretPath ?? socketSecretPath(internalSocket);
|
|
290
|
+
const internalSecret = process.platform === "win32" ? await createSocketSecret(internalSecretPath) : undefined;
|
|
291
|
+
const publicSecret = process.platform === "win32" ? await readSocketSecret(socketSecretPath(publicSocket)) : undefined;
|
|
245
292
|
const clientSockets = new Set();
|
|
246
293
|
const busySessions = new Map();
|
|
247
294
|
let observerHealthy = false;
|
|
248
295
|
let observerReconnectTimer;
|
|
296
|
+
let childExitWatchTimer;
|
|
249
297
|
let shuttingDown = false;
|
|
250
298
|
let shutdownPromise;
|
|
251
|
-
const childLaunch = resolveHostChildLaunch(launch, internalSocket);
|
|
299
|
+
const childLaunch = spawnableChildLaunch(resolveHostChildLaunch(launch, internalSocket));
|
|
252
300
|
const child = spawn(childLaunch.command, childLaunch.args, {
|
|
253
301
|
env: {
|
|
254
302
|
...process.env,
|
|
255
303
|
...(launch.agentDir ? { SENPI_CODING_AGENT_DIR: launch.agentDir } : {}),
|
|
256
304
|
[HOST_WATCH_FD_ENV]: String(CHILD_WATCH_FD),
|
|
257
305
|
[HOST_WATCH_PPID_ENV]: String(process.pid),
|
|
258
|
-
[HOST_SCRATCH_DIR_ENV]: internal.dir,
|
|
259
|
-
[
|
|
306
|
+
...(internal.dir ? { [HOST_SCRATCH_DIR_ENV]: internal.dir } : {}),
|
|
307
|
+
...(internalSecret ? { [SOCKET_SECRET_FILE_ENV]: internalSecretPath } : {}),
|
|
308
|
+
[HOST_CLEANUP_PATHS_ENV]: [
|
|
309
|
+
paths.pidFile,
|
|
310
|
+
paths.settingsFile,
|
|
311
|
+
...(process.platform === "win32" ? [] : [publicSocket]),
|
|
312
|
+
].join("\n"),
|
|
260
313
|
},
|
|
261
314
|
// Slot 3 is the lifetime pipe: "pipe" gives the child a read end it can
|
|
262
315
|
// wait on and keeps the write end owned by this process alone.
|
|
316
|
+
shell: childLaunch.shell,
|
|
263
317
|
stdio: ["ignore", "ignore", "inherit", "pipe"],
|
|
318
|
+
// The supervisor is spawned detached, so on win32 it owns no console. A
|
|
319
|
+
// console-subsystem child started from it would allocate a fresh one,
|
|
320
|
+
// which Windows Terminal renders as an empty window that takes focus.
|
|
321
|
+
// CREATE_NO_WINDOW gives the child a console with no window instead.
|
|
322
|
+
windowsHide: true,
|
|
264
323
|
});
|
|
265
324
|
// Nothing is ever written; the pipe exists purely so its EOF is a reliable
|
|
266
325
|
// death notification. Errors on it must not crash the supervisor.
|
|
@@ -272,23 +331,31 @@ export async function runHostSupervisor(launch) {
|
|
|
272
331
|
void shutdown(reason, exitCode);
|
|
273
332
|
});
|
|
274
333
|
const server = createServer((client) => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
334
|
+
const accept = () => {
|
|
335
|
+
if (shuttingDown) {
|
|
336
|
+
client.destroy();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const internal = createConnection(resolveSocketTransportAddress(internalSocket, process.platform, internalSecret));
|
|
340
|
+
if (internalSecret)
|
|
341
|
+
sendSocketHandshake(internal, internalSecret);
|
|
342
|
+
clientSockets.add(client);
|
|
343
|
+
const detach = () => {
|
|
344
|
+
clientSockets.delete(client);
|
|
345
|
+
internal.destroy();
|
|
346
|
+
client.destroy();
|
|
347
|
+
};
|
|
348
|
+
client.pipe(internal);
|
|
349
|
+
internal.pipe(client);
|
|
350
|
+
client.once("close", detach);
|
|
351
|
+
client.once("error", detach);
|
|
352
|
+
internal.once("close", detach);
|
|
353
|
+
internal.once("error", detach);
|
|
285
354
|
};
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
internal.once("close", detach);
|
|
291
|
-
internal.once("error", detach);
|
|
355
|
+
if (publicSecret)
|
|
356
|
+
authenticateSocket(client, publicSecret, accept);
|
|
357
|
+
else
|
|
358
|
+
accept();
|
|
292
359
|
});
|
|
293
360
|
server.once("error", (cause) => {
|
|
294
361
|
if (!shuttingDown)
|
|
@@ -343,25 +410,40 @@ export async function runHostSupervisor(launch) {
|
|
|
343
410
|
process.exit(exitCode);
|
|
344
411
|
shuttingDown = true;
|
|
345
412
|
clearInterval(ticker);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
413
|
+
if (childExitWatchTimer)
|
|
414
|
+
clearInterval(childExitWatchTimer);
|
|
415
|
+
const hardExit = process.platform === "win32"
|
|
416
|
+
? setTimeout(() => process.exit(exitCode), WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS)
|
|
417
|
+
: undefined;
|
|
418
|
+
try {
|
|
419
|
+
writeStderrLine(`senpi rpc host supervisor: ${reason} shutdown`);
|
|
420
|
+
for (const client of clientSockets)
|
|
421
|
+
client.destroy();
|
|
422
|
+
await closeServer(server);
|
|
423
|
+
// Unlink the private directory BEFORE the child stop, which can take seconds:
|
|
424
|
+
// an external SIGKILL landing during that wait (ensureHost escalates while
|
|
425
|
+
// replacing a host) would otherwise leave the directory behind. The child
|
|
426
|
+
// keeps serving through its already-open socket fd until it exits, and its
|
|
427
|
+
// own watchdog cleanup makes the removal idempotent.
|
|
428
|
+
if (internal.dir)
|
|
429
|
+
await rm(internal.dir, { recursive: true, force: true });
|
|
430
|
+
await stopChild(child);
|
|
431
|
+
observer?.destroy();
|
|
432
|
+
if (publicSocketOwned && process.platform !== "win32")
|
|
433
|
+
await rm(publicSocket, { force: true });
|
|
434
|
+
// Mirror ensureHost's cleanupState: the pidfile and settings describe a
|
|
435
|
+
// live host only; the stderr log stays for diagnostics.
|
|
436
|
+
await rm(paths.pidFile, { force: true });
|
|
437
|
+
await rm(paths.settingsFile, { force: true });
|
|
438
|
+
}
|
|
439
|
+
finally {
|
|
440
|
+
if (hardExit)
|
|
441
|
+
clearTimeout(hardExit);
|
|
442
|
+
// Explicitly terminate after every supervisor shutdown trigger. Windows
|
|
443
|
+
// named-pipe handles can outlive their JavaScript wrappers, so cleanup
|
|
444
|
+
// failure must never leave the supervisor resident or the host orphaned.
|
|
445
|
+
process.exit(exitCode);
|
|
446
|
+
}
|
|
365
447
|
}
|
|
366
448
|
let observer;
|
|
367
449
|
let publicSocketOwned = false;
|
|
@@ -371,17 +453,20 @@ export async function runHostSupervisor(launch) {
|
|
|
371
453
|
// would leave that directory behind.
|
|
372
454
|
registerSupervisorSignals(shutdown);
|
|
373
455
|
try {
|
|
374
|
-
await waitForListener(internalSocket, 30_000);
|
|
456
|
+
await waitForListener(internalSocket, 30_000, internalSecret);
|
|
375
457
|
await connectObserver();
|
|
376
458
|
await prepareSocketPath(publicSocket);
|
|
377
|
-
await listen(server, publicSocket);
|
|
459
|
+
await listen(server, publicSocket, publicSecret);
|
|
378
460
|
publicSocketOwned = true;
|
|
379
461
|
}
|
|
380
462
|
catch (cause) {
|
|
381
463
|
await shutdown(`startup failed: ${errorMessage(cause)}`, 1);
|
|
382
464
|
}
|
|
383
465
|
async function connectObserver() {
|
|
384
|
-
const
|
|
466
|
+
const secret = internalSecret;
|
|
467
|
+
const next = createConnection(resolveSocketTransportAddress(internalSocket, process.platform, secret));
|
|
468
|
+
if (secret)
|
|
469
|
+
sendSocketHandshake(next, secret);
|
|
385
470
|
await waitForConnect(next, 5_000);
|
|
386
471
|
observer = next;
|
|
387
472
|
observerHealthy = true;
|
|
@@ -402,6 +487,33 @@ export async function runHostSupervisor(launch) {
|
|
|
402
487
|
next.once("close", lost);
|
|
403
488
|
next.once("error", lost);
|
|
404
489
|
}
|
|
490
|
+
if (process.platform === "win32" && child.pid !== undefined) {
|
|
491
|
+
const childStartTime = await readProcessStartTime(child.pid, process.platform, 1_000);
|
|
492
|
+
let missingIdentityChecks = 0;
|
|
493
|
+
let checkingChildIdentity = false;
|
|
494
|
+
const checkChildIdentity = () => {
|
|
495
|
+
if (shuttingDown || checkingChildIdentity || child.exitCode !== null || child.signalCode !== null)
|
|
496
|
+
return;
|
|
497
|
+
checkingChildIdentity = true;
|
|
498
|
+
void readProcessStartTime(child.pid, process.platform, 1_000)
|
|
499
|
+
.then((currentStartTime) => {
|
|
500
|
+
if (currentStartTime === undefined)
|
|
501
|
+
missingIdentityChecks++;
|
|
502
|
+
else
|
|
503
|
+
missingIdentityChecks = 0;
|
|
504
|
+
const identityChanged = childStartTime !== undefined && currentStartTime !== undefined && currentStartTime !== childStartTime;
|
|
505
|
+
if (identityChanged || missingIdentityChecks >= 2) {
|
|
506
|
+
void shutdown("rpc host child exit observed by identity watchdog", 0);
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
.catch(() => { })
|
|
510
|
+
.finally(() => {
|
|
511
|
+
checkingChildIdentity = false;
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
childExitWatchTimer = setInterval(checkChildIdentity, 500);
|
|
515
|
+
childExitWatchTimer.unref?.();
|
|
516
|
+
}
|
|
405
517
|
writeStderrLine(`senpi rpc host ready on unix://${publicSocket} (coldStart=${policy.coldStart}, idleExitMs=${policy.coldStart === "persistent" ? "never" : String(policy.idleExitMs)})`);
|
|
406
518
|
await new Promise(() => { });
|
|
407
519
|
}
|
|
@@ -455,18 +567,20 @@ function waitForChildExit(child, timeoutMs) {
|
|
|
455
567
|
child.once("exit", onExit);
|
|
456
568
|
});
|
|
457
569
|
}
|
|
458
|
-
async function waitForListener(socketPath, timeoutMs) {
|
|
570
|
+
async function waitForListener(socketPath, timeoutMs, secret) {
|
|
459
571
|
const deadline = Date.now() + timeoutMs;
|
|
460
572
|
while (Date.now() <= deadline) {
|
|
461
|
-
if (await canConnect(socketPath))
|
|
573
|
+
if (await canConnect(socketPath, secret))
|
|
462
574
|
return;
|
|
463
575
|
await delay(50);
|
|
464
576
|
}
|
|
465
577
|
throw new Error(`${socketPath}: host did not start listening within ${timeoutMs}ms`);
|
|
466
578
|
}
|
|
467
|
-
function canConnect(socketPath) {
|
|
579
|
+
function canConnect(socketPath, secret) {
|
|
468
580
|
return new Promise((resolve) => {
|
|
469
|
-
const socket = createConnection(socketPath);
|
|
581
|
+
const socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));
|
|
582
|
+
if (secret)
|
|
583
|
+
sendSocketHandshake(socket, secret);
|
|
470
584
|
const settle = (value) => {
|
|
471
585
|
socket.destroy();
|
|
472
586
|
resolve(value);
|
|
@@ -499,6 +613,8 @@ function waitForConnect(socket, timeoutMs) {
|
|
|
499
613
|
});
|
|
500
614
|
}
|
|
501
615
|
async function prepareSocketPath(socketPath) {
|
|
616
|
+
if (process.platform === "win32")
|
|
617
|
+
return;
|
|
502
618
|
await mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });
|
|
503
619
|
try {
|
|
504
620
|
await access(socketPath);
|
|
@@ -512,14 +628,23 @@ async function prepareSocketPath(socketPath) {
|
|
|
512
628
|
throw new Error(`${socketPath}: address already in use by a live server.`);
|
|
513
629
|
await unlink(socketPath);
|
|
514
630
|
}
|
|
515
|
-
function listen(server, socketPath) {
|
|
631
|
+
function listen(server, socketPath, secret) {
|
|
516
632
|
return new Promise((resolve, reject) => {
|
|
517
633
|
server.once("error", reject);
|
|
518
|
-
server.listen(
|
|
634
|
+
server.listen({
|
|
635
|
+
path: resolveSocketTransportAddress(socketPath, process.platform, secret),
|
|
636
|
+
readableAll: false,
|
|
637
|
+
writableAll: false,
|
|
638
|
+
}, async () => {
|
|
519
639
|
server.off("error", reject);
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
640
|
+
try {
|
|
641
|
+
if (process.platform !== "win32" && !socketPath.startsWith("\0"))
|
|
642
|
+
await chmod(socketPath, 0o600);
|
|
643
|
+
resolve();
|
|
644
|
+
}
|
|
645
|
+
catch (cause) {
|
|
646
|
+
reject(cause);
|
|
647
|
+
}
|
|
523
648
|
});
|
|
524
649
|
});
|
|
525
650
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-lifecycle.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Lifecycle supervisor for the shared RPC socket host started by ensureHost().\n *\n * Process tree:\n *\n * ensureHost() ──detached──▶ host-lifecycle.ts (this supervisor, owns the pidfile)\n * │ byte-proxies the public socket\n * ▼\n * cli-main --mode rpc --listen unix://<public>.internal\n *\n * The supervisor exists to enforce the host lifecycle policy without touching the\n * RPC host itself:\n *\n * - cold start: `transient` (default) means the host lives for the current login\n * session and idle-exits; `persistent` never idle-exits.\n * - idle exit: after a continuous window with zero attached client connections\n * and zero active agent turns, the supervisor tears the host down cleanly\n * (child SIGTERM first so the host flushes pending output and removes its own\n * socket, then pidfile/settings removal mirroring ensureHost's cleanupState).\n *\n * Observability without host changes: proxying the public socket yields the\n * exact connection count, and the supervisor keeps one always-on observer\n * connection to the internal socket. The multi-session host broadcasts every\n * session lifecycle/agent event to every connection, so the observer sees\n * `agent_start`/`agent_settled` for all sessions even when no client is\n * attached. If the observer connection is ever unhealthy, activity is reported\n * as unknown (non-idle), so a broken observer can only keep the host alive,\n * never kill it mid-turn.\n *\n * Lifetime binding: the host is spawned with an extra inherited pipe on fd 3\n * whose write end this supervisor holds and never writes to. The kernel closes\n * that end whenever the supervisor dies - including SIGKILL, an OOM kill, or a\n * crash, where no JS handler runs at all - so the host reads EOF and shuts down\n * cleanly, removing the private internal directory. `stopChild()` remains the\n * fast path for orderly shutdowns; the pipe is what makes an orphaned host\n * impossible. `SENPI_RPC_HOST_WATCH_PPID` is passed alongside as a belt-and-\n * braces fallback for platforms where the extra fd is not inherited.\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { realpathSync, writeSync } from \"node:fs\";\nimport { access, chmod, mkdir, readFile, rm, unlink } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server, type Socket } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir, isBunBinary } from \"../../config.ts\";\nimport { createHostDaemonPaths } from \"./host-ensure.ts\";\nimport {\n\tHOST_CLEANUP_PATHS_ENV,\n\tHOST_SCRATCH_DIR_ENV,\n\tHOST_WATCH_FD_ENV,\n\tHOST_WATCH_PPID_ENV,\n} from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\n\nexport type HostColdStart = \"transient\" | \"persistent\";\n\n/** Environment override for the cold-start policy: `transient` or `persistent`. */\nexport const HOST_COLD_START_ENV = \"SENPI_RPC_HOST_COLD_START\";\n/** Environment override for the idle-exit window in milliseconds. */\nexport const HOST_IDLE_EXIT_MS_ENV = \"SENPI_RPC_HOST_IDLE_EXIT_MS\";\n/** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */\nexport const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;\n\n/** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */\nexport interface HostLifecyclePolicyInput {\n\treadonly coldStart?: HostColdStart;\n\treadonly idleExitMs?: number;\n}\n\nexport interface HostLifecyclePolicy {\n\treadonly coldStart: HostColdStart;\n\treadonly idleExitMs: number;\n}\n\nconst CHILD_STOP_TIMEOUT_MS = 5_000;\n\n/**\n * Child stdio slot carrying the supervisor-lifetime pipe. The supervisor holds\n * the write end open and never writes; the kernel closes it when the supervisor\n * dies for ANY reason (SIGKILL, OOM kill, crash), so the host sees EOF on this\n * fd and shuts itself down. Catchable-signal cleanup alone cannot do this.\n */\nconst CHILD_WATCH_FD = 3;\n\n/**\n * The internal hop must stay short enough for sun_path (104 bytes on macOS)\n * regardless of where the public socket lives, and private against other local\n * users, so it gets its own 0700 directory under the OS temp directory.\n */\nasync function createInternalSocketPath(): Promise<{ socket: string; dir: string }> {\n\tconst dir = join(tmpdir(), `senpi-rpc-host-internal-${randomUUID().slice(0, 8)}`);\n\tawait mkdir(dir, { recursive: false, mode: 0o700 });\n\treturn { socket: join(dir, \"host.sock\"), dir };\n}\n\nexport function parseColdStart(value: string | undefined): HostColdStart | undefined {\n\treturn value === \"transient\" || value === \"persistent\" ? value : undefined;\n}\n\nexport function parseIdleExitMs(value: string | undefined): number | undefined {\n\tif (value === undefined || !/^\\d+$/.test(value.trim())) return undefined;\n\tconst parsed = Number(value.trim());\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/**\n * Resolves the effective host policy. Precedence: environment overrides beat\n * settings.json, which beats the documented defaults (transient, 15 minutes).\n * Invalid values at either source fall through to the next source.\n */\nexport function resolveHostPolicy(\n\tsettings: unknown,\n\tenv: Readonly<Record<string, string | undefined>>,\n): HostLifecyclePolicy {\n\tconst record = isRecord(settings) ? settings : {};\n\tconst coldStart =\n\t\tparseColdStart(env[HOST_COLD_START_ENV]) ?? parseColdStart(asOptionalString(record.coldStart)) ?? \"transient\";\n\tconst idleExitMs =\n\t\tparseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV]) ??\n\t\tparseIdleExitMs(asOptionalString(record.idleExitMs)) ??\n\t\tDEFAULT_HOST_IDLE_EXIT_MS;\n\treturn { coldStart, idleExitMs };\n}\n\nexport interface HostActivity {\n\treadonly connections: number;\n\treadonly activeTurns: number;\n}\n\n/**\n * How the supervisor reads its child's exit. The RPC host exits 0 only through\n * its own clean shutdown path - including its idle/empty-host policy - so that\n * is an intentional stop, not a crash: the supervisor mirrors its own idle exit\n * instead of reporting failure. Any non-zero code or signal stays a crash.\n */\nexport function classifyChildExit(\n\tcode: number | null,\n\tsignal: NodeJS.Signals | null,\n): { reason: string; exitCode: number } {\n\tif (code === 0 && signal === null) return { reason: \"rpc host exited on its own idle policy\", exitCode: 0 };\n\treturn { reason: `rpc host process exited unexpectedly (${code ?? signal})`, exitCode: 1 };\n}\n\nexport type IdleExitDecision = \"active\" | \"idle\" | \"exit\";\n\n/**\n * Pure idle-window decision core. `update()` must be called with the CURRENT\n * activity state; the window only counts continuously idle time and any\n * activity resets it, so a busy host can never cross the threshold.\n */\nexport class IdleExitDecider {\n\tprivate idleSince: number | undefined;\n\tprivate readonly now: () => number;\n\treadonly idleExitMs: number;\n\n\tconstructor(idleExitMs: number, now: () => number = Date.now) {\n\t\tthis.idleExitMs = idleExitMs;\n\t\tthis.now = now;\n\t}\n\n\tupdate(activity: HostActivity): IdleExitDecision {\n\t\t// Any attachment or active turn both holds the host open and resets the\n\t\t// window, so only CONTINUOUS idle can ever cross the threshold.\n\t\tif (activity.connections > 0 || activity.activeTurns > 0) {\n\t\t\tthis.idleSince = undefined;\n\t\t\treturn \"active\";\n\t\t}\n\t\tif (this.idleExitMs === Number.POSITIVE_INFINITY) return \"idle\";\n\t\tif (this.idleSince === undefined) {\n\t\t\tthis.idleSince = this.now();\n\t\t\treturn \"idle\";\n\t\t}\n\t\treturn this.now() - this.idleSince >= this.idleExitMs ? \"exit\" : \"idle\";\n\t}\n}\n\nexport interface SupervisorLaunch {\n\treadonly socket: string;\n\treadonly hostArgs: readonly string[];\n\t/** Optional runtime command used by rebranded/bundled callers. */\n\treadonly childCommand?: string;\n\treadonly childArgs?: readonly string[];\n\t/** Explicit ownership directory for callers whose environment is not yet branded. */\n\treadonly agentDir?: string;\n}\n\n/** Hidden internal launch route: wire-invisible, never advertised by the public CLI surface. */\nexport const INTERNAL_SUPERVISOR_FLAG = \"--internal-rpc-host-supervisor\";\n\n/**\n * Engine-global flags a rebranded wrapper may legitimately prepend when it\n * re-dispatches this binary. `packages/omo-native` injects `--extension <dir>`\n * for every non-early command, which pushed the sentinel off argv[0].\n */\nconst INJECTABLE_PREFIX_FLAGS = new Set([\"--extension\"]);\n\n/**\n * Returns the internal supervisor payload when argv selects that route.\n *\n * The route dispatches when the sentinel is argv[0] OR is preceded only by\n * known injectable prefix flags and their values - the one perturbation\n * wrappers legitimately perform. Everything else disqualifies it: a positional\n * operand, `--`, or an unknown flag before the sentinel all return undefined,\n * so a user-supplied value that happens to equal the sentinel can never reach\n * the supervisor.\n *\n * The skipped prefix is deliberately NOT forwarded to the host: a wrapper\n * re-injects its own prefix on every re-entry, so the host child receives it\n * from the wrapper rather than twice from here.\n */\nexport function findInternalSupervisorArgs(argv: readonly string[]): readonly string[] | undefined {\n\tfor (let index = 0; index < argv.length; index++) {\n\t\tconst arg = argv[index];\n\t\tif (arg === INTERNAL_SUPERVISOR_FLAG) return argv.slice(index + 1);\n\t\t// A prefix flag only counts when its value is actually present.\n\t\tif (!INJECTABLE_PREFIX_FLAGS.has(arg) || index + 1 >= argv.length) return undefined;\n\t\tindex++;\n\t}\n\treturn undefined;\n}\n\n/** `--socket <path>` selects the public socket; every other argument is forwarded to the host CLI. */\nexport function parseSupervisorArgs(argv: readonly string[]): SupervisorLaunch | undefined {\n\tconst hostArgs: string[] = [];\n\tlet socket: string | undefined;\n\tlet childCommand: string | undefined;\n\tlet childArgs: readonly string[] | undefined;\n\tlet agentDir: string | undefined;\n\tfor (let index = 0; index < argv.length; index++) {\n\t\tconst arg = argv[index];\n\t\tif (arg === \"--socket\" && index + 1 < argv.length) {\n\t\t\tsocket = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--child-command\" && index + 1 < argv.length) {\n\t\t\tchildCommand = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--child-args\" && index + 1 < argv.length) {\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(argv[++index]);\n\t\t\t\tif (Array.isArray(parsed) && parsed.every((value) => typeof value === \"string\")) childArgs = parsed;\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--agent-dir\" && index + 1 < argv.length) {\n\t\t\tagentDir = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\thostArgs.push(arg);\n\t}\n\treturn socket === undefined ? undefined : { socket, hostArgs, childCommand, childArgs, agentDir };\n}\n\n/** Resolves the committed CLI entry this supervisor wraps (source tree or built dist). */\nexport function resolveCliMainPath(): string {\n\tconst modulePath = fileURLToPath(import.meta.url);\n\tconst extension = modulePath.endsWith(\".ts\") ? \".ts\" : \".js\";\n\treturn resolve(dirname(modulePath), \"..\", \"..\", `cli-main${extension}`);\n}\n\n/**\n * Resolves the host child spawn. Explicit child commands (desktop launchers)\n * are forwarded untouched. The default re-enters the committed CLI entry\n * through the runtime, except in compiled standalone binaries, which always\n * boot their embedded entrypoint and would parse a script path as CLI\n * arguments - there the executable itself is the CLI, so the mode flags are\n * passed directly. Exported for tests.\n */\nexport function resolveHostChildLaunch(\n\tlaunch: SupervisorLaunch,\n\tinternalSocket: string,\n\tcompiled: boolean = isBunBinary,\n): { command: string; args: string[] } {\n\tif (launch.childCommand) {\n\t\treturn {\n\t\t\tcommand: launch.childCommand,\n\t\t\targs: [...(launch.childArgs ?? []), \"--listen\", `unix://${internalSocket}`],\n\t\t};\n\t}\n\treturn {\n\t\tcommand: process.execPath,\n\t\targs: [\n\t\t\t...(compiled ? [] : [...process.execArgv, resolveCliMainPath()]),\n\t\t\t\"--mode\",\n\t\t\t\"rpc\",\n\t\t\t\"--multi-session\",\n\t\t\t\"--listen\",\n\t\t\t`unix://${internalSocket}`,\n\t\t\t...launch.hostArgs,\n\t\t],\n\t};\n}\n\nexport async function runHostSupervisor(launch: SupervisorLaunch): Promise<void> {\n\tconst paths = createHostDaemonPaths(launch.agentDir ?? getAgentDir());\n\tconst policy = resolveHostPolicy(await readSettingsFile(paths.settingsFile), process.env);\n\tconst publicSocket = launch.socket;\n\tconst internal = await createInternalSocketPath();\n\tconst internalSocket = internal.socket;\n\tconst clientSockets = new Set<Socket>();\n\tconst busySessions = new Map<string, number>();\n\tlet observerHealthy = false;\n\tlet observerReconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\tlet shuttingDown = false;\n\tlet shutdownPromise: Promise<never> | undefined;\n\n\tconst childLaunch = resolveHostChildLaunch(launch, internalSocket);\n\tconst child = spawn(childLaunch.command, childLaunch.args, {\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...(launch.agentDir ? { SENPI_CODING_AGENT_DIR: launch.agentDir } : {}),\n\t\t\t[HOST_WATCH_FD_ENV]: String(CHILD_WATCH_FD),\n\t\t\t[HOST_WATCH_PPID_ENV]: String(process.pid),\n\t\t\t[HOST_SCRATCH_DIR_ENV]: internal.dir,\n\t\t\t[HOST_CLEANUP_PATHS_ENV]: [publicSocket, paths.pidFile, paths.settingsFile].join(\"\\n\"),\n\t\t},\n\t\t// Slot 3 is the lifetime pipe: \"pipe\" gives the child a read end it can\n\t\t// wait on and keeps the write end owned by this process alone.\n\t\tstdio: [\"ignore\", \"ignore\", \"inherit\", \"pipe\"],\n\t});\n\t// Nothing is ever written; the pipe exists purely so its EOF is a reliable\n\t// death notification. Errors on it must not crash the supervisor.\n\tchild.stdio[CHILD_WATCH_FD]?.on(\"error\", () => {});\n\tchild.once(\"exit\", (code, signal) => {\n\t\tif (shuttingDown) return;\n\t\tconst { reason, exitCode } = classifyChildExit(code, signal);\n\t\tvoid shutdown(reason, exitCode);\n\t});\n\n\tconst server = createServer((client) => {\n\t\tif (shuttingDown) {\n\t\t\tclient.destroy();\n\t\t\treturn;\n\t\t}\n\t\tconst internal = createConnection(internalSocket);\n\t\tclientSockets.add(client);\n\t\tconst detach = (): void => {\n\t\t\tclientSockets.delete(client);\n\t\t\tinternal.destroy();\n\t\t\tclient.destroy();\n\t\t};\n\t\tclient.pipe(internal);\n\t\tinternal.pipe(client);\n\t\tclient.once(\"close\", detach);\n\t\tclient.once(\"error\", detach);\n\t\tinternal.once(\"close\", detach);\n\t\tinternal.once(\"error\", detach);\n\t});\n\tserver.once(\"error\", (cause) => {\n\t\tif (!shuttingDown) void shutdown(`public socket listener failed: ${errorMessage(cause)}`, 1);\n\t});\n\n\tconst decider = new IdleExitDecider(\n\t\tpolicy.coldStart === \"persistent\" ? Number.POSITIVE_INFINITY : policy.idleExitMs,\n\t);\n\tconst tickIntervalMs = Math.max(20, Math.min(1_000, policy.idleExitMs / 4));\n\tconst ticker = setInterval(() => {\n\t\tif (decider.update(currentActivity()) === \"exit\") void shutdown(\"idle\", 0);\n\t}, tickIntervalMs);\n\n\tfunction currentActivity(): HostActivity {\n\t\treturn {\n\t\t\tconnections: clientSockets.size,\n\t\t\tactiveTurns: observerHealthy ? countBusySessions() : 1,\n\t\t};\n\t}\n\n\tfunction countBusySessions(): number {\n\t\tlet busy = 0;\n\t\tfor (const count of busySessions.values()) if (count > 0) busy++;\n\t\treturn busy;\n\t}\n\n\tfunction observeHostEvent(line: string): void {\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (typeof event !== \"object\" || event === null) return;\n\t\tconst { type, sessionId } = event as { type?: unknown; sessionId?: unknown };\n\t\tif (typeof sessionId !== \"string\") return;\n\t\tif (type === \"agent_start\") busySessions.set(sessionId, (busySessions.get(sessionId) ?? 0) + 1);\n\t\telse if (type === \"agent_settled\")\n\t\t\tbusySessions.set(sessionId, Math.max(0, (busySessions.get(sessionId) ?? 1) - 1));\n\t}\n\n\tasync function shutdown(reason: string, exitCode: number): Promise<never> {\n\t\t// Single-flight: concurrent triggers (listener error, child exit, signals)\n\t\t// must not process.exit mid-cleanup. Late callers park on this promise while\n\t\t// the first shutdown finishes tearing down and exits.\n\t\tshutdownPromise ??= performShutdown(reason, exitCode);\n\t\treturn shutdownPromise;\n\t}\n\n\tasync function performShutdown(reason: string, exitCode: number): Promise<never> {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tclearInterval(ticker);\n\t\twriteStderrLine(`senpi rpc host supervisor: ${reason} shutdown`);\n\t\tfor (const client of clientSockets) client.destroy();\n\t\tawait closeServer(server);\n\t\t// Unlink the private directory BEFORE the child stop, which can take seconds:\n\t\t// an external SIGKILL landing during that wait (ensureHost escalates while\n\t\t// replacing a host) would otherwise leave the directory behind. The child\n\t\t// keeps serving through its already-open socket fd until it exits, and its\n\t\t// own watchdog cleanup makes the removal idempotent.\n\t\tawait rm(internal.dir, { recursive: true, force: true });\n\t\tawait stopChild(child);\n\t\tobserver?.destroy();\n\t\tif (publicSocketOwned) await rm(publicSocket, { force: true });\n\t\t// Mirror ensureHost's cleanupState: the pidfile and settings describe a\n\t\t// live host only; the stderr log stays for diagnostics.\n\t\tawait rm(paths.pidFile, { force: true });\n\t\tawait rm(paths.settingsFile, { force: true });\n\t\tprocess.exit(exitCode);\n\t}\n\n\tlet observer: Socket | undefined;\n\tlet publicSocketOwned = false;\n\t// Registered before the startup handshake, not after it: the private internal\n\t// directory already exists at this point, so a SIGTERM arriving during host\n\t// startup must run the same cleanup instead of Node's default kill, which\n\t// would leave that directory behind.\n\tregisterSupervisorSignals(shutdown);\n\ttry {\n\t\tawait waitForListener(internalSocket, 30_000);\n\t\tawait connectObserver();\n\t\tawait prepareSocketPath(publicSocket);\n\t\tawait listen(server, publicSocket);\n\t\tpublicSocketOwned = true;\n\t} catch (cause) {\n\t\tawait shutdown(`startup failed: ${errorMessage(cause)}`, 1);\n\t}\n\tasync function connectObserver(): Promise<void> {\n\t\tconst next = createConnection(internalSocket);\n\t\tawait waitForConnect(next, 5_000);\n\t\tobserver = next;\n\t\tobserverHealthy = true;\n\t\tattachJsonlLineReader(next, observeHostEvent, { maxLineLength: MAX_RPC_LINE_CHARACTERS });\n\t\tconst lost = (): void => {\n\t\t\tif (observer !== next || shuttingDown) return;\n\t\t\tobserverHealthy = false;\n\t\t\tobserver = undefined;\n\t\t\tif (observerReconnectTimer === undefined) {\n\t\t\t\tobserverReconnectTimer = setTimeout(() => {\n\t\t\t\t\tobserverReconnectTimer = undefined;\n\t\t\t\t\tvoid connectObserver().catch(() => lost());\n\t\t\t\t}, 250);\n\t\t\t\tobserverReconnectTimer.unref?.();\n\t\t\t}\n\t\t};\n\t\tnext.once(\"close\", lost);\n\t\tnext.once(\"error\", lost);\n\t}\n\n\twriteStderrLine(\n\t\t`senpi rpc host ready on unix://${publicSocket} (coldStart=${policy.coldStart}, idleExitMs=${\n\t\t\tpolicy.coldStart === \"persistent\" ? \"never\" : String(policy.idleExitMs)\n\t\t})`,\n\t);\n\tawait new Promise<never>(() => {});\n}\n\n/** External stop (ensureHost replacement, tests, QA) must clean up like idle exit. */\nfunction registerSupervisorSignals(shutdown: (reason: string, exitCode: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tvoid shutdown(`signal:${signal}`, signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n\nasync function readSettingsFile(settingsFile: string): Promise<unknown> {\n\ttry {\n\t\treturn JSON.parse(await readFile(settingsFile, \"utf8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function stopChild(child: ChildProcess): Promise<void> {\n\tif (child.exitCode !== null || child.signalCode !== null) return;\n\ttry {\n\t\tchild.kill(\"SIGTERM\");\n\t} catch {\n\t\treturn;\n\t}\n\tif (await waitForChildExit(child, CHILD_STOP_TIMEOUT_MS)) return;\n\ttry {\n\t\tchild.kill(\"SIGKILL\");\n\t} catch {\n\t\treturn;\n\t}\n\tawait waitForChildExit(child, 2_000);\n}\n\nfunction waitForChildExit(child: ChildProcess, timeoutMs: number): Promise<boolean> {\n\tif (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);\n\treturn new Promise((resolve) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.off(\"exit\", onExit);\n\t\t\tresolve(false);\n\t\t}, timeoutMs);\n\t\tconst onExit = (): void => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(true);\n\t\t};\n\t\tchild.once(\"exit\", onExit);\n\t});\n}\n\nasync function waitForListener(socketPath: string, timeoutMs: number): Promise<void> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif (await canConnect(socketPath)) return;\n\t\tawait delay(50);\n\t}\n\tthrow new Error(`${socketPath}: host did not start listening within ${timeoutMs}ms`);\n}\n\nfunction canConnect(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(socketPath);\n\t\tconst settle = (value: boolean): void => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(value);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t});\n}\n\nfunction waitForConnect(socket: Socket, timeoutMs: number): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\treject(new Error(`observer connection to internal host timed out after ${timeoutMs}ms`));\n\t\t}, timeoutMs);\n\t\tconst onConnect = (): void => {\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t};\n\t\tconst onError = (cause: Error): void => {\n\t\t\tcleanup();\n\t\t\treject(cause);\n\t\t};\n\t\tconst cleanup = (): void => {\n\t\t\tclearTimeout(timer);\n\t\t\tsocket.off(\"connect\", onConnect);\n\t\t\tsocket.off(\"error\", onError);\n\t\t};\n\t\tsocket.once(\"connect\", onConnect);\n\t\tsocket.once(\"error\", onError);\n\t});\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await canConnect(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction listen(server: Server, socketPath: string): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(socketPath, async () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\tif (!socketPath.startsWith(\"\\0\")) await chmod(socketPath, 0o600);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tserver.close(() => resolve());\n\t});\n}\n\nfunction writeStderrLine(text: string): void {\n\t// A detached daemon exiting right after an async stderr.write to a file can\n\t// lose the output entirely; write synchronously so diagnostics always land.\n\ttry {\n\t\twriteSync(2, `${text}\\n`);\n\t} catch {\n\t\t/* fd 2 unavailable: nothing more we can do. */\n\t}\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asOptionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : typeof value === \"number\" ? String(value) : undefined;\n}\n\nfunction isEntryScript(): boolean {\n\tconst entry = process.argv[1];\n\tif (!entry) return false;\n\ttry {\n\t\treturn fileURLToPath(import.meta.url) === realpathSync(entry);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nif (isEntryScript()) {\n\tconst launch = parseSupervisorArgs(process.argv.slice(2));\n\tif (!launch) {\n\t\twriteStderrLine(\"usage: host-lifecycle.ts --socket <path> [host cli args...]\");\n\t\tprocess.exit(2);\n\t}\n\tvoid runHostSupervisor(launch);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"host-lifecycle.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * Lifecycle supervisor for the shared RPC socket host started by ensureHost().\n *\n * Process tree:\n *\n * ensureHost() ──detached──▶ host-lifecycle.ts (this supervisor, owns the pidfile)\n * │ byte-proxies the public socket\n * ▼\n * cli-main --mode rpc --listen unix://<public>.internal\n *\n * The supervisor exists to enforce the host lifecycle policy without touching the\n * RPC host itself:\n *\n * - cold start: `transient` (default) means the host lives for the current login\n * session and idle-exits; `persistent` never idle-exits.\n * - idle exit: after a continuous window with zero attached client connections\n * and zero active agent turns, the supervisor tears the host down cleanly\n * (child SIGTERM first so the host flushes pending output and removes its own\n * socket, then pidfile/settings removal mirroring ensureHost's cleanupState).\n *\n * Observability without host changes: proxying the public socket yields the\n * exact connection count, and the supervisor keeps one always-on observer\n * connection to the internal socket. The multi-session host broadcasts every\n * session lifecycle/agent event to every connection, so the observer sees\n * `agent_start`/`agent_settled` for all sessions even when no client is\n * attached. If the observer connection is ever unhealthy, activity is reported\n * as unknown (non-idle), so a broken observer can only keep the host alive,\n * never kill it mid-turn.\n *\n * Lifetime binding: the host is spawned with an extra inherited pipe on fd 3\n * whose write end this supervisor holds and never writes to. The kernel closes\n * that end whenever the supervisor dies - including SIGKILL, an OOM kill, or a\n * crash, where no JS handler runs at all - so the host reads EOF and shuts down\n * cleanly, removing the private internal directory. `stopChild()` remains the\n * fast path for orderly shutdowns; the pipe is what makes an orphaned host\n * impossible. `SENPI_RPC_HOST_WATCH_PPID` is passed alongside as a belt-and-\n * braces fallback for platforms where the extra fd is not inherited.\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { realpathSync, writeSync } from \"node:fs\";\nimport { access, chmod, mkdir, readFile, rm, unlink, writeFile } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server, type Socket } from \"node:net\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, extname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir, isBunBinary } from \"../../config.ts\";\nimport { readProcessStartTime } from \"../app-server/daemon/process.ts\";\nimport { createHostDaemonPaths } from \"./host-ensure.ts\";\nimport {\n\tHOST_CLEANUP_PATHS_ENV,\n\tHOST_SCRATCH_DIR_ENV,\n\tHOST_WATCH_FD_ENV,\n\tHOST_WATCH_PPID_ENV,\n} from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\nimport {\n\tauthenticateSocket,\n\tcreateSocketSecret,\n\treadSocketSecret,\n\tresolveSocketTransportAddress,\n\tSOCKET_SECRET_FILE_ENV,\n\tsendSocketHandshake,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\n\nexport type HostColdStart = \"transient\" | \"persistent\";\n\n/** Environment override for the cold-start policy: `transient` or `persistent`. */\nexport const HOST_COLD_START_ENV = \"SENPI_RPC_HOST_COLD_START\";\n/** Environment override for the idle-exit window in milliseconds. */\nexport const HOST_IDLE_EXIT_MS_ENV = \"SENPI_RPC_HOST_IDLE_EXIT_MS\";\n/** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */\nexport const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;\n\n/** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */\nexport interface HostLifecyclePolicyInput {\n\treadonly coldStart?: HostColdStart;\n\treadonly idleExitMs?: number;\n}\n\nexport interface HostLifecyclePolicy {\n\treadonly coldStart: HostColdStart;\n\treadonly idleExitMs: number;\n}\n\nconst CHILD_STOP_TIMEOUT_MS = 5_000;\n/** Win32 named-pipe shutdown can leave supervisor handles live after close starts. */\nconst WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS = 2_000;\n\n/**\n * Child stdio slot carrying the supervisor-lifetime pipe. The supervisor holds\n * the write end open and never writes; the kernel closes it when the supervisor\n * dies for ANY reason (SIGKILL, OOM kill, crash), so the host sees EOF on this\n * fd and shuts itself down. Catchable-signal cleanup alone cannot do this.\n */\nconst CHILD_WATCH_FD = 3;\n\n/**\n * The internal hop must stay short enough for sun_path (104 bytes on macOS)\n * regardless of where the public socket lives, and private against other local\n * users, so it gets its own 0700 directory under the OS temp directory.\n */\nasync function createInternalSocketPath(\n\tbaseDir = tmpdir(),\n): Promise<{ socket: string; dir?: string; secretPath?: string }> {\n\tif (process.platform === \"win32\") {\n\t\tconst dir = join(baseDir, `internal-${randomUUID()}`);\n\t\tawait mkdir(dir, { recursive: false, mode: 0o700 });\n\t\treturn {\n\t\t\tsocket: `\\\\\\\\.\\\\pipe\\\\senpi-rpc-internal-${randomUUID()}`,\n\t\t\tdir,\n\t\t\tsecretPath: join(dir, \"secret\"),\n\t\t};\n\t}\n\tconst dir = join(tmpdir(), `senpi-rpc-host-internal-${randomUUID().slice(0, 8)}`);\n\tawait mkdir(dir, { recursive: false, mode: 0o700 });\n\tawait writeFile(\n\t\tjoin(dir, \".owner\"),\n\t\tJSON.stringify({\n\t\t\tpid: process.pid,\n\t\t\tprocessStartTime: await readProcessStartTime(process.pid),\n\t\t\tcreatedAt: Date.now(),\n\t\t}),\n\t\t{ mode: 0o600 },\n\t);\n\treturn { socket: join(dir, \"host.sock\"), dir, secretPath: join(dir, \".secret\") };\n}\n\nexport function parseColdStart(value: string | undefined): HostColdStart | undefined {\n\treturn value === \"transient\" || value === \"persistent\" ? value : undefined;\n}\n\nexport function parseIdleExitMs(value: string | undefined): number | undefined {\n\tif (value === undefined || !/^\\d+$/.test(value.trim())) return undefined;\n\tconst parsed = Number(value.trim());\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/**\n * Resolves the effective host policy. Precedence: environment overrides beat\n * settings.json, which beats the documented defaults (transient, 15 minutes).\n * Invalid values at either source fall through to the next source.\n */\nexport function resolveHostPolicy(\n\tsettings: unknown,\n\tenv: Readonly<Record<string, string | undefined>>,\n): HostLifecyclePolicy {\n\tconst record = isRecord(settings) ? settings : {};\n\tconst coldStart =\n\t\tparseColdStart(env[HOST_COLD_START_ENV]) ?? parseColdStart(asOptionalString(record.coldStart)) ?? \"transient\";\n\tconst idleExitMs =\n\t\tparseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV]) ??\n\t\tparseIdleExitMs(asOptionalString(record.idleExitMs)) ??\n\t\tDEFAULT_HOST_IDLE_EXIT_MS;\n\treturn { coldStart, idleExitMs };\n}\n\nexport interface HostActivity {\n\treadonly connections: number;\n\treadonly activeTurns: number;\n}\n\n/**\n * How the supervisor reads its child's exit. The RPC host exits 0 only through\n * its own clean shutdown path - including its idle/empty-host policy - so that\n * is an intentional stop, not a crash: the supervisor mirrors its own idle exit\n * instead of reporting failure. Any non-zero code or signal stays a crash.\n */\nexport function classifyChildExit(\n\tcode: number | null,\n\tsignal: NodeJS.Signals | null,\n): { reason: string; exitCode: number } {\n\tif (code === 0 && signal === null) return { reason: \"rpc host exited on its own idle policy\", exitCode: 0 };\n\treturn { reason: `rpc host process exited unexpectedly (${code ?? signal})`, exitCode: 1 };\n}\n\nexport type IdleExitDecision = \"active\" | \"idle\" | \"exit\";\n\n/**\n * Pure idle-window decision core. `update()` must be called with the CURRENT\n * activity state; the window only counts continuously idle time and any\n * activity resets it, so a busy host can never cross the threshold.\n */\nexport class IdleExitDecider {\n\tprivate idleSince: number | undefined;\n\tprivate readonly now: () => number;\n\treadonly idleExitMs: number;\n\n\tconstructor(idleExitMs: number, now: () => number = Date.now) {\n\t\tthis.idleExitMs = idleExitMs;\n\t\tthis.now = now;\n\t}\n\n\tupdate(activity: HostActivity): IdleExitDecision {\n\t\t// Any attachment or active turn both holds the host open and resets the\n\t\t// window, so only CONTINUOUS idle can ever cross the threshold.\n\t\tif (activity.connections > 0 || activity.activeTurns > 0) {\n\t\t\tthis.idleSince = undefined;\n\t\t\treturn \"active\";\n\t\t}\n\t\tif (this.idleExitMs === Number.POSITIVE_INFINITY) return \"idle\";\n\t\tif (this.idleSince === undefined) {\n\t\t\tthis.idleSince = this.now();\n\t\t\treturn \"idle\";\n\t\t}\n\t\treturn this.now() - this.idleSince >= this.idleExitMs ? \"exit\" : \"idle\";\n\t}\n}\n\nexport interface SupervisorLaunch {\n\treadonly socket: string;\n\treadonly hostArgs: readonly string[];\n\t/** Optional runtime command used by rebranded/bundled callers. */\n\treadonly childCommand?: string;\n\treadonly childArgs?: readonly string[];\n\t/** Explicit ownership directory for callers whose environment is not yet branded. */\n\treadonly agentDir?: string;\n}\n\n/** Hidden internal launch route: wire-invisible, never advertised by the public CLI surface. */\nexport const INTERNAL_SUPERVISOR_FLAG = \"--internal-rpc-host-supervisor\";\n\n/**\n * Engine-global flags a rebranded wrapper may legitimately prepend when it\n * re-dispatches this binary. `packages/omo-native` injects `--extension <dir>`\n * for every non-early command, which pushed the sentinel off argv[0].\n */\nconst INJECTABLE_PREFIX_FLAGS = new Set([\"--extension\"]);\n\n/**\n * Returns the internal supervisor payload when argv selects that route.\n *\n * The route dispatches when the sentinel is argv[0] OR is preceded only by\n * known injectable prefix flags and their values - the one perturbation\n * wrappers legitimately perform. Everything else disqualifies it: a positional\n * operand, `--`, or an unknown flag before the sentinel all return undefined,\n * so a user-supplied value that happens to equal the sentinel can never reach\n * the supervisor.\n *\n * The skipped prefix is deliberately NOT forwarded to the host: a wrapper\n * re-injects its own prefix on every re-entry, so the host child receives it\n * from the wrapper rather than twice from here.\n */\nexport function findInternalSupervisorArgs(argv: readonly string[]): readonly string[] | undefined {\n\tfor (let index = 0; index < argv.length; index++) {\n\t\tconst arg = argv[index];\n\t\tif (arg === INTERNAL_SUPERVISOR_FLAG) return argv.slice(index + 1);\n\t\t// A prefix flag only counts when its value is actually present.\n\t\tif (!INJECTABLE_PREFIX_FLAGS.has(arg) || index + 1 >= argv.length) return undefined;\n\t\tindex++;\n\t}\n\treturn undefined;\n}\n\n/** `--socket <path>` selects the public socket; every other argument is forwarded to the host CLI. */\nexport function parseSupervisorArgs(argv: readonly string[]): SupervisorLaunch | undefined {\n\tconst hostArgs: string[] = [];\n\tlet socket: string | undefined;\n\tlet childCommand: string | undefined;\n\tlet childArgs: readonly string[] | undefined;\n\tlet agentDir: string | undefined;\n\tfor (let index = 0; index < argv.length; index++) {\n\t\tconst arg = argv[index];\n\t\tif (arg === \"--socket\" && index + 1 < argv.length) {\n\t\t\tsocket = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--child-command\" && index + 1 < argv.length) {\n\t\t\tchildCommand = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--child-args\" && index + 1 < argv.length) {\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(argv[++index]);\n\t\t\t\tif (Array.isArray(parsed) && parsed.every((value) => typeof value === \"string\")) childArgs = parsed;\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg === \"--agent-dir\" && index + 1 < argv.length) {\n\t\t\tagentDir = argv[++index];\n\t\t\tcontinue;\n\t\t}\n\t\thostArgs.push(arg);\n\t}\n\treturn socket === undefined ? undefined : { socket, hostArgs, childCommand, childArgs, agentDir };\n}\n\n/** Resolves the committed CLI entry this supervisor wraps (source tree or built dist). */\nexport function resolveCliMainPath(): string {\n\tconst modulePath = fileURLToPath(import.meta.url);\n\tconst extension = modulePath.endsWith(\".ts\") ? \".ts\" : \".js\";\n\treturn resolve(dirname(modulePath), \"..\", \"..\", `cli-main${extension}`);\n}\n\n/**\n * Resolves the host child spawn. Explicit child commands (desktop launchers)\n * are forwarded untouched. The default re-enters the committed CLI entry\n * through the runtime, except in compiled standalone binaries, which always\n * boot their embedded entrypoint and would parse a script path as CLI\n * arguments - there the executable itself is the CLI, so the mode flags are\n * passed directly. Exported for tests.\n */\nexport function resolveHostChildLaunch(\n\tlaunch: SupervisorLaunch,\n\tinternalSocket: string,\n\tcompiled: boolean = isBunBinary,\n): { command: string; args: string[] } {\n\tif (launch.childCommand) {\n\t\treturn {\n\t\t\tcommand: launch.childCommand,\n\t\t\targs: [...(launch.childArgs ?? []), \"--listen\", `unix://${internalSocket}`],\n\t\t};\n\t}\n\treturn {\n\t\tcommand: process.execPath,\n\t\targs: [\n\t\t\t...(compiled ? [] : [...process.execArgv, resolveCliMainPath()]),\n\t\t\t\"--mode\",\n\t\t\t\"rpc\",\n\t\t\t\"--multi-session\",\n\t\t\t\"--listen\",\n\t\t\t`unix://${internalSocket}`,\n\t\t\t...launch.hostArgs,\n\t\t],\n\t};\n}\n\n/** Mirrors cross-spawn: survives cmd.exe parsing and `CommandLineToArgvW`. */\nfunction quoteWindowsShellArg(value: string): string {\n\tconst escaped = value\n\t\t.replace(/(\\\\*)\"/g, '$1$1\\\\\"')\n\t\t.replace(/(\\\\*)$/, \"$1$1\")\n\t\t.replace(/([()%!^\"<>&|;,])/g, \"^$1\");\n\treturn `\"${escaped}\"`;\n}\n\n/**\n * Windows refuses to spawn a `.cmd`/`.bat` without a shell, and Node's\n * `shell: true` concatenates argv without escaping it. Escape each original\n * value before adding the surrounding quotes so `.cmd`/`.bat` launchers survive\n * cmd.exe parsing without double-escaping.\n * Exported for tests.\n */\nexport function spawnableChildLaunch(\n\tlaunch: { command: string; args: string[] },\n\tplatform: NodeJS.Platform = process.platform,\n): { command: string; args: string[]; shell: boolean } {\n\tconst extension = extname(launch.command).toLowerCase();\n\tif (platform !== \"win32\" || (extension !== \".cmd\" && extension !== \".bat\")) {\n\t\treturn { ...launch, shell: false };\n\t}\n\treturn {\n\t\tcommand: quoteWindowsShellArg(launch.command),\n\t\targs: launch.args.map(quoteWindowsShellArg),\n\t\tshell: true,\n\t};\n}\n\nexport async function runHostSupervisor(launch: SupervisorLaunch): Promise<void> {\n\tconst paths = createHostDaemonPaths(launch.agentDir ?? getAgentDir());\n\tconst policy = resolveHostPolicy(await readSettingsFile(paths.settingsFile), process.env);\n\tconst publicSocket = launch.socket;\n\tconst internal = await createInternalSocketPath(paths.dir);\n\tconst internalSocket = internal.socket;\n\tconst internalSecretPath = internal.secretPath ?? socketSecretPath(internalSocket);\n\tconst internalSecret = process.platform === \"win32\" ? await createSocketSecret(internalSecretPath) : undefined;\n\tconst publicSecret =\n\t\tprocess.platform === \"win32\" ? await readSocketSecret(socketSecretPath(publicSocket)) : undefined;\n\tconst clientSockets = new Set<Socket>();\n\tconst busySessions = new Map<string, number>();\n\tlet observerHealthy = false;\n\tlet observerReconnectTimer: ReturnType<typeof setTimeout> | undefined;\n\tlet childExitWatchTimer: ReturnType<typeof setInterval> | undefined;\n\tlet shuttingDown = false;\n\tlet shutdownPromise: Promise<never> | undefined;\n\n\tconst childLaunch = spawnableChildLaunch(resolveHostChildLaunch(launch, internalSocket));\n\tconst child = spawn(childLaunch.command, childLaunch.args, {\n\t\tenv: {\n\t\t\t...process.env,\n\t\t\t...(launch.agentDir ? { SENPI_CODING_AGENT_DIR: launch.agentDir } : {}),\n\t\t\t[HOST_WATCH_FD_ENV]: String(CHILD_WATCH_FD),\n\t\t\t[HOST_WATCH_PPID_ENV]: String(process.pid),\n\t\t\t...(internal.dir ? { [HOST_SCRATCH_DIR_ENV]: internal.dir } : {}),\n\t\t\t...(internalSecret ? { [SOCKET_SECRET_FILE_ENV]: internalSecretPath } : {}),\n\t\t\t[HOST_CLEANUP_PATHS_ENV]: [\n\t\t\t\tpaths.pidFile,\n\t\t\t\tpaths.settingsFile,\n\t\t\t\t...(process.platform === \"win32\" ? [] : [publicSocket]),\n\t\t\t].join(\"\\n\"),\n\t\t},\n\t\t// Slot 3 is the lifetime pipe: \"pipe\" gives the child a read end it can\n\t\t// wait on and keeps the write end owned by this process alone.\n\t\tshell: childLaunch.shell,\n\t\tstdio: [\"ignore\", \"ignore\", \"inherit\", \"pipe\"],\n\t\t// The supervisor is spawned detached, so on win32 it owns no console. A\n\t\t// console-subsystem child started from it would allocate a fresh one,\n\t\t// which Windows Terminal renders as an empty window that takes focus.\n\t\t// CREATE_NO_WINDOW gives the child a console with no window instead.\n\t\twindowsHide: true,\n\t});\n\t// Nothing is ever written; the pipe exists purely so its EOF is a reliable\n\t// death notification. Errors on it must not crash the supervisor.\n\tchild.stdio[CHILD_WATCH_FD]?.on(\"error\", () => {});\n\tchild.once(\"exit\", (code, signal) => {\n\t\tif (shuttingDown) return;\n\t\tconst { reason, exitCode } = classifyChildExit(code, signal);\n\t\tvoid shutdown(reason, exitCode);\n\t});\n\n\tconst server = createServer((client) => {\n\t\tconst accept = (): void => {\n\t\t\tif (shuttingDown) {\n\t\t\t\tclient.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst internal = createConnection(\n\t\t\t\tresolveSocketTransportAddress(internalSocket, process.platform, internalSecret),\n\t\t\t);\n\t\t\tif (internalSecret) sendSocketHandshake(internal, internalSecret);\n\t\t\tclientSockets.add(client);\n\t\t\tconst detach = (): void => {\n\t\t\t\tclientSockets.delete(client);\n\t\t\t\tinternal.destroy();\n\t\t\t\tclient.destroy();\n\t\t\t};\n\t\t\tclient.pipe(internal);\n\t\t\tinternal.pipe(client);\n\t\t\tclient.once(\"close\", detach);\n\t\t\tclient.once(\"error\", detach);\n\t\t\tinternal.once(\"close\", detach);\n\t\t\tinternal.once(\"error\", detach);\n\t\t};\n\t\tif (publicSecret) authenticateSocket(client, publicSecret, accept);\n\t\telse accept();\n\t});\n\tserver.once(\"error\", (cause) => {\n\t\tif (!shuttingDown) void shutdown(`public socket listener failed: ${errorMessage(cause)}`, 1);\n\t});\n\n\tconst decider = new IdleExitDecider(\n\t\tpolicy.coldStart === \"persistent\" ? Number.POSITIVE_INFINITY : policy.idleExitMs,\n\t);\n\tconst tickIntervalMs = Math.max(20, Math.min(1_000, policy.idleExitMs / 4));\n\tconst ticker = setInterval(() => {\n\t\tif (decider.update(currentActivity()) === \"exit\") void shutdown(\"idle\", 0);\n\t}, tickIntervalMs);\n\n\tfunction currentActivity(): HostActivity {\n\t\treturn {\n\t\t\tconnections: clientSockets.size,\n\t\t\tactiveTurns: observerHealthy ? countBusySessions() : 1,\n\t\t};\n\t}\n\n\tfunction countBusySessions(): number {\n\t\tlet busy = 0;\n\t\tfor (const count of busySessions.values()) if (count > 0) busy++;\n\t\treturn busy;\n\t}\n\n\tfunction observeHostEvent(line: string): void {\n\t\tlet event: unknown;\n\t\ttry {\n\t\t\tevent = JSON.parse(line);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tif (typeof event !== \"object\" || event === null) return;\n\t\tconst { type, sessionId } = event as { type?: unknown; sessionId?: unknown };\n\t\tif (typeof sessionId !== \"string\") return;\n\t\tif (type === \"agent_start\") busySessions.set(sessionId, (busySessions.get(sessionId) ?? 0) + 1);\n\t\telse if (type === \"agent_settled\")\n\t\t\tbusySessions.set(sessionId, Math.max(0, (busySessions.get(sessionId) ?? 1) - 1));\n\t}\n\n\tasync function shutdown(reason: string, exitCode: number): Promise<never> {\n\t\t// Single-flight: concurrent triggers (listener error, child exit, signals)\n\t\t// must not process.exit mid-cleanup. Late callers park on this promise while\n\t\t// the first shutdown finishes tearing down and exits.\n\t\tshutdownPromise ??= performShutdown(reason, exitCode);\n\t\treturn shutdownPromise;\n\t}\n\n\tasync function performShutdown(reason: string, exitCode: number): Promise<never> {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tclearInterval(ticker);\n\t\tif (childExitWatchTimer) clearInterval(childExitWatchTimer);\n\t\tconst hardExit =\n\t\t\tprocess.platform === \"win32\"\n\t\t\t\t? setTimeout(() => process.exit(exitCode), WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS)\n\t\t\t\t: undefined;\n\t\ttry {\n\t\t\twriteStderrLine(`senpi rpc host supervisor: ${reason} shutdown`);\n\t\t\tfor (const client of clientSockets) client.destroy();\n\t\t\tawait closeServer(server);\n\t\t\t// Unlink the private directory BEFORE the child stop, which can take seconds:\n\t\t\t// an external SIGKILL landing during that wait (ensureHost escalates while\n\t\t\t// replacing a host) would otherwise leave the directory behind. The child\n\t\t\t// keeps serving through its already-open socket fd until it exits, and its\n\t\t\t// own watchdog cleanup makes the removal idempotent.\n\t\t\tif (internal.dir) await rm(internal.dir, { recursive: true, force: true });\n\t\t\tawait stopChild(child);\n\t\t\tobserver?.destroy();\n\t\t\tif (publicSocketOwned && process.platform !== \"win32\") await rm(publicSocket, { force: true });\n\t\t\t// Mirror ensureHost's cleanupState: the pidfile and settings describe a\n\t\t\t// live host only; the stderr log stays for diagnostics.\n\t\t\tawait rm(paths.pidFile, { force: true });\n\t\t\tawait rm(paths.settingsFile, { force: true });\n\t\t} finally {\n\t\t\tif (hardExit) clearTimeout(hardExit);\n\t\t\t// Explicitly terminate after every supervisor shutdown trigger. Windows\n\t\t\t// named-pipe handles can outlive their JavaScript wrappers, so cleanup\n\t\t\t// failure must never leave the supervisor resident or the host orphaned.\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t}\n\n\tlet observer: Socket | undefined;\n\tlet publicSocketOwned = false;\n\t// Registered before the startup handshake, not after it: the private internal\n\t// directory already exists at this point, so a SIGTERM arriving during host\n\t// startup must run the same cleanup instead of Node's default kill, which\n\t// would leave that directory behind.\n\tregisterSupervisorSignals(shutdown);\n\ttry {\n\t\tawait waitForListener(internalSocket, 30_000, internalSecret);\n\t\tawait connectObserver();\n\t\tawait prepareSocketPath(publicSocket);\n\t\tawait listen(server, publicSocket, publicSecret);\n\t\tpublicSocketOwned = true;\n\t} catch (cause) {\n\t\tawait shutdown(`startup failed: ${errorMessage(cause)}`, 1);\n\t}\n\tasync function connectObserver(): Promise<void> {\n\t\tconst secret = internalSecret;\n\t\tconst next = createConnection(resolveSocketTransportAddress(internalSocket, process.platform, secret));\n\t\tif (secret) sendSocketHandshake(next, secret);\n\t\tawait waitForConnect(next, 5_000);\n\t\tobserver = next;\n\t\tobserverHealthy = true;\n\t\tattachJsonlLineReader(next, observeHostEvent, { maxLineLength: MAX_RPC_LINE_CHARACTERS });\n\t\tconst lost = (): void => {\n\t\t\tif (observer !== next || shuttingDown) return;\n\t\t\tobserverHealthy = false;\n\t\t\tobserver = undefined;\n\t\t\tif (observerReconnectTimer === undefined) {\n\t\t\t\tobserverReconnectTimer = setTimeout(() => {\n\t\t\t\t\tobserverReconnectTimer = undefined;\n\t\t\t\t\tvoid connectObserver().catch(() => lost());\n\t\t\t\t}, 250);\n\t\t\t\tobserverReconnectTimer.unref?.();\n\t\t\t}\n\t\t};\n\t\tnext.once(\"close\", lost);\n\t\tnext.once(\"error\", lost);\n\t}\n\n\tif (process.platform === \"win32\" && child.pid !== undefined) {\n\t\tconst childStartTime = await readProcessStartTime(child.pid, process.platform, 1_000);\n\t\tlet missingIdentityChecks = 0;\n\t\tlet checkingChildIdentity = false;\n\t\tconst checkChildIdentity = (): void => {\n\t\t\tif (shuttingDown || checkingChildIdentity || child.exitCode !== null || child.signalCode !== null) return;\n\t\t\tcheckingChildIdentity = true;\n\t\t\tvoid readProcessStartTime(child.pid!, process.platform, 1_000)\n\t\t\t\t.then((currentStartTime) => {\n\t\t\t\t\tif (currentStartTime === undefined) missingIdentityChecks++;\n\t\t\t\t\telse missingIdentityChecks = 0;\n\t\t\t\t\tconst identityChanged =\n\t\t\t\t\t\tchildStartTime !== undefined && currentStartTime !== undefined && currentStartTime !== childStartTime;\n\t\t\t\t\tif (identityChanged || missingIdentityChecks >= 2) {\n\t\t\t\t\t\tvoid shutdown(\"rpc host child exit observed by identity watchdog\", 0);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tcheckingChildIdentity = false;\n\t\t\t\t});\n\t\t};\n\t\tchildExitWatchTimer = setInterval(checkChildIdentity, 500);\n\t\tchildExitWatchTimer.unref?.();\n\t}\n\twriteStderrLine(\n\t\t`senpi rpc host ready on unix://${publicSocket} (coldStart=${policy.coldStart}, idleExitMs=${\n\t\t\tpolicy.coldStart === \"persistent\" ? \"never\" : String(policy.idleExitMs)\n\t\t})`,\n\t);\n\tawait new Promise<never>(() => {});\n}\n\n/** External stop (ensureHost replacement, tests, QA) must clean up like idle exit. */\nfunction registerSupervisorSignals(shutdown: (reason: string, exitCode: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tvoid shutdown(`signal:${signal}`, signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n\nasync function readSettingsFile(settingsFile: string): Promise<unknown> {\n\ttry {\n\t\treturn JSON.parse(await readFile(settingsFile, \"utf8\"));\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function stopChild(child: ChildProcess): Promise<void> {\n\tif (child.exitCode !== null || child.signalCode !== null) return;\n\ttry {\n\t\tchild.kill(\"SIGTERM\");\n\t} catch {\n\t\treturn;\n\t}\n\tif (await waitForChildExit(child, CHILD_STOP_TIMEOUT_MS)) return;\n\ttry {\n\t\tchild.kill(\"SIGKILL\");\n\t} catch {\n\t\treturn;\n\t}\n\tawait waitForChildExit(child, 2_000);\n}\n\nfunction waitForChildExit(child: ChildProcess, timeoutMs: number): Promise<boolean> {\n\tif (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true);\n\treturn new Promise((resolve) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tchild.off(\"exit\", onExit);\n\t\t\tresolve(false);\n\t\t}, timeoutMs);\n\t\tconst onExit = (): void => {\n\t\t\tclearTimeout(timer);\n\t\t\tresolve(true);\n\t\t};\n\t\tchild.once(\"exit\", onExit);\n\t});\n}\n\nasync function waitForListener(socketPath: string, timeoutMs: number, secret?: Uint8Array): Promise<void> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif (await canConnect(socketPath, secret)) return;\n\t\tawait delay(50);\n\t}\n\tthrow new Error(`${socketPath}: host did not start listening within ${timeoutMs}ms`);\n}\n\nfunction canConnect(socketPath: string, secret?: Uint8Array): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform, secret));\n\t\tif (secret) sendSocketHandshake(socket, secret);\n\t\tconst settle = (value: boolean): void => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(value);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t});\n}\n\nfunction waitForConnect(socket: Socket, timeoutMs: number): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\treject(new Error(`observer connection to internal host timed out after ${timeoutMs}ms`));\n\t\t}, timeoutMs);\n\t\tconst onConnect = (): void => {\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t};\n\t\tconst onError = (cause: Error): void => {\n\t\t\tcleanup();\n\t\t\treject(cause);\n\t\t};\n\t\tconst cleanup = (): void => {\n\t\t\tclearTimeout(timer);\n\t\t\tsocket.off(\"connect\", onConnect);\n\t\t\tsocket.off(\"error\", onError);\n\t\t};\n\t\tsocket.once(\"connect\", onConnect);\n\t\tsocket.once(\"error\", onError);\n\t});\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tif (process.platform === \"win32\") return;\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await canConnect(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction listen(server: Server, socketPath: string, secret?: Uint8Array): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(\n\t\t\t{\n\t\t\t\tpath: resolveSocketTransportAddress(socketPath, process.platform, secret),\n\t\t\t\treadableAll: false,\n\t\t\t\twritableAll: false,\n\t\t\t},\n\t\t\tasync () => {\n\t\t\t\tserver.off(\"error\", reject);\n\t\t\t\ttry {\n\t\t\t\t\tif (process.platform !== \"win32\" && !socketPath.startsWith(\"\\0\")) await chmod(socketPath, 0o600);\n\t\t\t\t\tresolve();\n\t\t\t\t} catch (cause) {\n\t\t\t\t\treject(cause);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tserver.close(() => resolve());\n\t});\n}\n\nfunction writeStderrLine(text: string): void {\n\t// A detached daemon exiting right after an async stderr.write to a file can\n\t// lose the output entirely; write synchronously so diagnostics always land.\n\ttry {\n\t\twriteSync(2, `${text}\\n`);\n\t} catch {\n\t\t/* fd 2 unavailable: nothing more we can do. */\n\t}\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asOptionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : typeof value === \"number\" ? String(value) : undefined;\n}\n\nfunction isEntryScript(): boolean {\n\tconst entry = process.argv[1];\n\tif (!entry) return false;\n\ttry {\n\t\treturn fileURLToPath(import.meta.url) === realpathSync(entry);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nif (isEntryScript()) {\n\tconst launch = parseSupervisorArgs(process.argv.slice(2));\n\tif (!launch) {\n\t\twriteStderrLine(\"usage: host-lifecycle.ts --socket <path> [host cli args...]\");\n\t\tprocess.exit(2);\n\t}\n\tvoid runHostSupervisor(launch);\n}\n"]}
|
|
@@ -5,7 +5,7 @@ export declare const HOST_SCRATCH_DIR_ENV = "SENPI_RPC_HOST_SCRATCH_DIR";
|
|
|
5
5
|
/** Fallback binding when no inherited fd is available: poll this pid. */
|
|
6
6
|
export declare const HOST_WATCH_PPID_ENV = "SENPI_RPC_HOST_WATCH_PPID";
|
|
7
7
|
/** Poll cadence for the ppid fallback. */
|
|
8
|
-
export declare const HOST_WATCH_PPID_INTERVAL_MS =
|
|
8
|
+
export declare const HOST_WATCH_PPID_INTERVAL_MS = 250;
|
|
9
9
|
export declare const HOST_CLEANUP_PATHS_ENV = "SENPI_RPC_HOST_CLEANUP_PATHS";
|
|
10
10
|
export interface HostWatchdogConfig {
|
|
11
11
|
readonly fd?: number;
|
|
@@ -31,5 +31,5 @@ export declare function readHostWatchdogConfigFromBrandEnv(): HostWatchdogConfig
|
|
|
31
31
|
* must not close it themselves. Returns a disarm function; a no-op when no
|
|
32
32
|
* binding is configured.
|
|
33
33
|
*/
|
|
34
|
-
export declare function armHostWatchdog(config: HostWatchdogConfig | undefined, onSupervisorGone: (reason: string) => void): () => void;
|
|
34
|
+
export declare function armHostWatchdog(config: HostWatchdogConfig | undefined, onSupervisorGone: (reason: string, cleanup?: Promise<void>) => void): () => void;
|
|
35
35
|
//# sourceMappingURL=host-watchdog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-watchdog.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-watchdog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"host-watchdog.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-watchdog.ts"],"names":[],"mappings":"AAqBA,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAC3D,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAC/D,0CAA0C;AAC1C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAG/C,eAAO,MAAM,sBAAsB,iCAAiC,CAAC;AAErE,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAQD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACrC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC7D,kBAAkB,GAAG,SAAS,CAYhC;AAED,yEAAyE;AACzE,wBAAgB,kCAAkC,IAAI,kBAAkB,GAAG,SAAS,CAWnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC9B,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,GACjE,MAAM,IAAI,CAqBZ"}
|