@code-yeongyu/senpi 2026.9.18 → 2026.9.19
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 +103 -0
- package/dist/bundle/chunks/{anthropic-messages-ZEJ3DPCP.js → anthropic-messages-WZCFP6BT.js} +1 -1
- package/dist/bundle/chunks/{app-server-command-ELSORC4D.js → app-server-command-QK7Q6XK5.js} +1 -1
- package/dist/bundle/chunks/bun-extension-importer-JQZRPNJH.js +10 -0
- package/dist/bundle/chunks/{chunk-Y6HDGVS5.js → chunk-34GCWLNV.js} +1 -1
- package/dist/bundle/chunks/{chunk-XJUVWITG.js → chunk-3FRTRCR6.js} +1 -1
- package/dist/bundle/chunks/{chunk-VRAYXSST.js → chunk-FDLVINNY.js} +2 -2
- package/dist/bundle/chunks/{chunk-QNZ24YOQ.js → chunk-HGAX6JZ7.js} +3 -1
- package/dist/bundle/chunks/{chunk-QGBRSL64.js → chunk-HSH674C6.js} +1 -1
- package/dist/bundle/chunks/chunk-IUZUN32Z.js +13 -0
- package/dist/bundle/chunks/chunk-IXAUDPOK.js +2 -0
- package/dist/bundle/chunks/{chunk-AH72FRZ2.js → chunk-KKDSBABY.js} +11 -11
- package/dist/bundle/chunks/{chunk-NLOZDJQX.js → chunk-LF3XTYYF.js} +3 -3
- package/dist/bundle/chunks/{chunk-ANTGWIGI.js → chunk-MUC3MZTV.js} +1 -1
- package/dist/bundle/chunks/chunk-NARL7XFO.js +8 -0
- package/dist/bundle/chunks/chunk-NQAVM5XL.js +2 -0
- package/dist/bundle/chunks/chunk-P7PWCGLJ.js +5 -0
- package/dist/bundle/chunks/{chunk-4C7LNOHI.js → chunk-PLZK7ABD.js} +1 -1
- package/dist/bundle/chunks/{chunk-CKNRCW35.js → chunk-RLRLQRI2.js} +1 -1
- package/dist/bundle/chunks/{chunk-7FXIHFZU.js → chunk-UAYBYTFN.js} +1 -1
- package/dist/bundle/chunks/{chunk-7A47FVQC.js → chunk-W4SRGDQ4.js} +3 -6
- package/dist/bundle/chunks/{chunk-QROWINIH.js → chunk-XK24AFYU.js} +1 -1
- package/dist/bundle/chunks/cli-main-7SG3K4K3.js +3 -0
- package/dist/bundle/chunks/cursor-agent.js +38 -0
- package/dist/bundle/chunks/cursor.js +2 -0
- package/dist/bundle/chunks/devin-agent.js +5 -0
- package/dist/bundle/chunks/devin.js +75 -0
- package/dist/bundle/chunks/{help-fast-path-WQCATMI4.js → help-fast-path-RQDCLDQT.js} +1 -1
- package/dist/bundle/chunks/host-command-3LS772D5.js +2 -0
- package/dist/bundle/chunks/{host-lifecycle-EAXSS3F2.js → host-lifecycle-TA5XGUW5.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +20 -0
- package/dist/bundle/chunks/interactive-host-runtime-AITXUDCT.js +4 -0
- package/dist/bundle/chunks/interactive-mode-7MN6GGCG.js +2 -0
- package/dist/bundle/chunks/{multi-session-host-OK6WMHB4.js → multi-session-host-WKX34SOP.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-2RS3VSPV.js → openai-completions-TOWYKX2U.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-C2K4YRGI.js → package-manager-cli-CB2X57TD.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-536AQTIS.js → rotation-stream-KDLA7K6X.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-CI6V2SBW.js +2 -0
- package/dist/bundle/chunks/{session-picker-BUHK3FCS.js → session-picker-DXCITGLS.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +359 -346
- package/dist/bundle/chunks/{state-store-OYDXEIXU.js → state-store-HBBOD7I5.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/deferred-commands.d.ts +7 -0
- package/dist/cli/deferred-commands.d.ts.map +1 -1
- package/dist/cli/deferred-commands.js +12 -0
- package/dist/cli/deferred-commands.js.map +1 -1
- package/dist/cli/host-command.d.ts +21 -0
- package/dist/cli/host-command.d.ts.map +1 -0
- package/dist/cli/host-command.js +148 -0
- package/dist/cli/host-command.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js +2 -17
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-7.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.js +5 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-8.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.js +28 -0
- package/dist/core/extensions/builtin/prompt-preset/kimi-k2-code.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 +21 -1
- 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/bun-extension-importer.d.ts.map +1 -1
- package/dist/core/extensions/bun-extension-importer.js +31 -5
- package/dist/core/extensions/bun-extension-importer.js.map +1 -1
- package/dist/core/extensions/bun-extension-registry.d.ts +9 -0
- package/dist/core/extensions/bun-extension-registry.d.ts.map +1 -1
- package/dist/core/extensions/bun-extension-registry.js +72 -16
- package/dist/core/extensions/bun-extension-registry.js.map +1 -1
- package/dist/core/extensions/extension-runtime-module.d.ts +5 -0
- package/dist/core/extensions/extension-runtime-module.d.ts.map +1 -0
- package/dist/core/extensions/extension-runtime-module.js +6 -0
- package/dist/core/extensions/extension-runtime-module.js.map +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/index.d.ts +3 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +5 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/rpc/host-daemon-env.d.ts +18 -0
- package/dist/modes/rpc/host-daemon-env.d.ts.map +1 -0
- package/dist/modes/rpc/host-daemon-env.js +95 -0
- package/dist/modes/rpc/host-daemon-env.js.map +1 -0
- package/dist/modes/rpc/host-decision.d.ts +1 -1
- package/dist/modes/rpc/host-decision.d.ts.map +1 -1
- package/dist/modes/rpc/host-decision.js +2 -0
- package/dist/modes/rpc/host-decision.js.map +1 -1
- package/dist/modes/rpc/host-ensure.js +8 -1
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-launch-spec.d.ts +43 -0
- package/dist/modes/rpc/host-launch-spec.d.ts.map +1 -0
- package/dist/modes/rpc/host-launch-spec.js +183 -0
- package/dist/modes/rpc/host-launch-spec.js.map +1 -0
- package/dist/modes/rpc/host-launch.d.ts +3 -1
- package/dist/modes/rpc/host-launch.d.ts.map +1 -1
- package/dist/modes/rpc/host-launch.js +24 -5
- package/dist/modes/rpc/host-launch.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +36 -2
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-probe.d.ts +7 -0
- package/dist/modes/rpc/host-probe.d.ts.map +1 -1
- package/dist/modes/rpc/host-probe.js +15 -4
- package/dist/modes/rpc/host-probe.js.map +1 -1
- package/dist/modes/rpc/host-process-metrics.d.ts +10 -0
- package/dist/modes/rpc/host-process-metrics.d.ts.map +1 -0
- package/dist/modes/rpc/host-process-metrics.js +83 -0
- package/dist/modes/rpc/host-process-metrics.js.map +1 -0
- package/dist/modes/rpc/host-runner.d.ts +38 -0
- package/dist/modes/rpc/host-runner.d.ts.map +1 -0
- package/dist/modes/rpc/host-runner.js +195 -0
- package/dist/modes/rpc/host-runner.js.map +1 -0
- package/dist/modes/rpc/host-status.d.ts +58 -0
- package/dist/modes/rpc/host-status.d.ts.map +1 -0
- package/dist/modes/rpc/host-status.js +117 -0
- package/dist/modes/rpc/host-status.js.map +1 -0
- package/docs/extensions.md +35 -0
- package/docs/providers.md +30 -0
- package/docs/rpc.md +177 -15
- package/docs/settings.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +72 -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/README.md +9 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts +150 -7
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +17 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai-stream.d.ts +13 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai-stream.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai-stream.js +55 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai-stream.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.d.ts +11 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.js +94 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.models.d.ts +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.models.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.models.js +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/bai.models.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/bai.json +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/mistral.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.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/venice.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/dist/providers/kimi-coding.models.d.ts +142 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js +156 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +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
- package/dist/bundle/chunks/bun-extension-importer-SW7XGNSG.js +0 -10
- package/dist/bundle/chunks/chunk-AGXZF7RF.js +0 -2
- package/dist/bundle/chunks/chunk-F5JZNLDE.js +0 -8
- package/dist/bundle/chunks/cli-main-4TWJXJKM.js +0 -3
- package/dist/bundle/chunks/interactive-host-runtime-EDXG6A5L.js +0 -4
- package/dist/bundle/chunks/interactive-mode-PD7S7KFZ.js +0 -2
- package/dist/bundle/chunks/rpc-mode-TIJFUWXS.js +0 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHICH environment variables a daemon this machine starts is allowed to see.
|
|
3
|
+
*
|
|
4
|
+
* A shared daemon outlives the shell that started it and serves every client on the machine, so
|
|
5
|
+
* "whatever the ensuring process happened to have" is the wrong scope for its environment: a CI
|
|
6
|
+
* token, a database URL or a one-off secret exported in one terminal would be inherited by a
|
|
7
|
+
* process that then answers other clients for hours. The daemon therefore receives an ALLOWLIST -
|
|
8
|
+
* the names a coding agent genuinely needs (the shell's own wiring, the locale, the brand's own
|
|
9
|
+
* `SENPI_`/`OMO_`/`PI_` lane, proxy settings and provider credentials) - plus whatever the launch
|
|
10
|
+
* spec states explicitly. Everything else is dropped, by NAME, without ever reading its value.
|
|
11
|
+
*
|
|
12
|
+
* The allowlist is matched case-sensitively on POSIX, where `PATH` and `Path` are two different
|
|
13
|
+
* variables, and case-insensitively on win32, where they are one - and where a daemon stripped of
|
|
14
|
+
* `SystemRoot`, `ComSpec` or `PATHEXT` cannot spawn a child at all.
|
|
15
|
+
*/
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { parseJson, readFileOrUndefined, writeStateFile } from "./host-daemon-state.js";
|
|
18
|
+
/** Names a daemon may inherit from the process that ensures it. */
|
|
19
|
+
const ALLOWED_ENV_NAMES = [
|
|
20
|
+
/^(PATH|HOME|USER|LOGNAME|SHELL|TMPDIR|TERM|LANG)$/u,
|
|
21
|
+
/^(LC|XDG)_/u,
|
|
22
|
+
/^(SENPI|OMO|PI)_/u,
|
|
23
|
+
/^(HTTPS?_PROXY|NO_PROXY)$/u,
|
|
24
|
+
/^[A-Z0-9_]+_API_KEY$/u,
|
|
25
|
+
/^(ANTHROPIC|OPENAI|GOOGLE|GEMINI|AZURE|AWS|OPENROUTER|XAI|MISTRAL|DEEPSEEK|GROQ|CEREBRAS|MINIMAX)_/u,
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* win32 wiring no process can run without. Dropping `SystemRoot` alone makes every subsequent
|
|
29
|
+
* `spawn` fail, so these are allowed in addition to the list above on that platform only.
|
|
30
|
+
*/
|
|
31
|
+
const WINDOWS_SYSTEM_ENV_NAMES = new Set([
|
|
32
|
+
"SYSTEMROOT",
|
|
33
|
+
"SYSTEMDRIVE",
|
|
34
|
+
"WINDIR",
|
|
35
|
+
"COMSPEC",
|
|
36
|
+
"PATHEXT",
|
|
37
|
+
"TEMP",
|
|
38
|
+
"TMP",
|
|
39
|
+
"USERPROFILE",
|
|
40
|
+
"APPDATA",
|
|
41
|
+
"LOCALAPPDATA",
|
|
42
|
+
"PROGRAMFILES",
|
|
43
|
+
"PROGRAMFILES(X86)",
|
|
44
|
+
"PROGRAMDATA",
|
|
45
|
+
"HOMEDRIVE",
|
|
46
|
+
"HOMEPATH",
|
|
47
|
+
"NUMBER_OF_PROCESSORS",
|
|
48
|
+
"PROCESSOR_ARCHITECTURE",
|
|
49
|
+
"OS",
|
|
50
|
+
]);
|
|
51
|
+
export function daemonEnvIsAllowed(name, platform = process.platform) {
|
|
52
|
+
if (platform !== "win32")
|
|
53
|
+
return ALLOWED_ENV_NAMES.some((pattern) => pattern.test(name));
|
|
54
|
+
const upper = name.toUpperCase();
|
|
55
|
+
return WINDOWS_SYSTEM_ENV_NAMES.has(upper) || ALLOWED_ENV_NAMES.some((pattern) => pattern.test(upper));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The `env` an ensure hands the daemon: every name it may NOT inherit mapped to `null` (which
|
|
59
|
+
* removes it), then the launch spec's own entries. Values are never inspected - a variable is kept
|
|
60
|
+
* or dropped by its name alone - and the spec's entries win, because they were stated on purpose.
|
|
61
|
+
*/
|
|
62
|
+
export function daemonEnvOverrides(processEnv, specEnv = {}, platform = process.platform) {
|
|
63
|
+
const overrides = {};
|
|
64
|
+
for (const name of Object.keys(processEnv)) {
|
|
65
|
+
if (!daemonEnvIsAllowed(name, platform))
|
|
66
|
+
overrides[name] = null;
|
|
67
|
+
}
|
|
68
|
+
for (const [name, value] of Object.entries(specEnv))
|
|
69
|
+
overrides[name] = value;
|
|
70
|
+
return overrides;
|
|
71
|
+
}
|
|
72
|
+
/** The names the daemon ends up with, sorted. Names only: a value never leaves this process. */
|
|
73
|
+
export function daemonEnvKeys(processEnv, specEnv = {}, platform = process.platform) {
|
|
74
|
+
const kept = Object.keys(processEnv).filter((name) => daemonEnvIsAllowed(name, platform));
|
|
75
|
+
return [...new Set([...kept, ...Object.keys(specEnv)])].sort();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Records the granted NAMES beside the daemon's other state, so `senpi host status` can report the
|
|
79
|
+
* scope a running daemon was started with. Written by whichever client spawned the generation;
|
|
80
|
+
* absent for a daemon started by a client that predates this file, which reads as "unknown scope".
|
|
81
|
+
*/
|
|
82
|
+
export async function writeDaemonEnvKeys(paths, keys) {
|
|
83
|
+
await writeStateFile(daemonEnvKeysFile(paths), { env_keys: keys });
|
|
84
|
+
}
|
|
85
|
+
export async function readDaemonEnvKeys(paths) {
|
|
86
|
+
const parsed = parseJson(await readFileOrUndefined(daemonEnvKeysFile(paths)));
|
|
87
|
+
const keys = parsed?.env_keys;
|
|
88
|
+
if (!Array.isArray(keys))
|
|
89
|
+
return [];
|
|
90
|
+
return keys.filter((key) => typeof key === "string");
|
|
91
|
+
}
|
|
92
|
+
function daemonEnvKeysFile(paths) {
|
|
93
|
+
return join(paths.dir, "env-keys.json");
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=host-daemon-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-daemon-env.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-daemon-env.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WHICH environment variables a daemon this machine starts is allowed to see.\n *\n * A shared daemon outlives the shell that started it and serves every client on the machine, so\n * \"whatever the ensuring process happened to have\" is the wrong scope for its environment: a CI\n * token, a database URL or a one-off secret exported in one terminal would be inherited by a\n * process that then answers other clients for hours. The daemon therefore receives an ALLOWLIST -\n * the names a coding agent genuinely needs (the shell's own wiring, the locale, the brand's own\n * `SENPI_`/`OMO_`/`PI_` lane, proxy settings and provider credentials) - plus whatever the launch\n * spec states explicitly. Everything else is dropped, by NAME, without ever reading its value.\n *\n * The allowlist is matched case-sensitively on POSIX, where `PATH` and `Path` are two different\n * variables, and case-insensitively on win32, where they are one - and where a daemon stripped of\n * `SystemRoot`, `ComSpec` or `PATHEXT` cannot spawn a child at all.\n */\nimport { join } from \"node:path\";\nimport type { HostDaemonPaths } from \"./host-daemon-paths.ts\";\nimport { parseJson, readFileOrUndefined, writeStateFile } from \"./host-daemon-state.ts\";\n\n/** Names a daemon may inherit from the process that ensures it. */\nconst ALLOWED_ENV_NAMES = [\n\t/^(PATH|HOME|USER|LOGNAME|SHELL|TMPDIR|TERM|LANG)$/u,\n\t/^(LC|XDG)_/u,\n\t/^(SENPI|OMO|PI)_/u,\n\t/^(HTTPS?_PROXY|NO_PROXY)$/u,\n\t/^[A-Z0-9_]+_API_KEY$/u,\n\t/^(ANTHROPIC|OPENAI|GOOGLE|GEMINI|AZURE|AWS|OPENROUTER|XAI|MISTRAL|DEEPSEEK|GROQ|CEREBRAS|MINIMAX)_/u,\n] as const;\n\n/**\n * win32 wiring no process can run without. Dropping `SystemRoot` alone makes every subsequent\n * `spawn` fail, so these are allowed in addition to the list above on that platform only.\n */\nconst WINDOWS_SYSTEM_ENV_NAMES = new Set([\n\t\"SYSTEMROOT\",\n\t\"SYSTEMDRIVE\",\n\t\"WINDIR\",\n\t\"COMSPEC\",\n\t\"PATHEXT\",\n\t\"TEMP\",\n\t\"TMP\",\n\t\"USERPROFILE\",\n\t\"APPDATA\",\n\t\"LOCALAPPDATA\",\n\t\"PROGRAMFILES\",\n\t\"PROGRAMFILES(X86)\",\n\t\"PROGRAMDATA\",\n\t\"HOMEDRIVE\",\n\t\"HOMEPATH\",\n\t\"NUMBER_OF_PROCESSORS\",\n\t\"PROCESSOR_ARCHITECTURE\",\n\t\"OS\",\n]);\n\nexport function daemonEnvIsAllowed(name: string, platform: NodeJS.Platform = process.platform): boolean {\n\tif (platform !== \"win32\") return ALLOWED_ENV_NAMES.some((pattern) => pattern.test(name));\n\tconst upper = name.toUpperCase();\n\treturn WINDOWS_SYSTEM_ENV_NAMES.has(upper) || ALLOWED_ENV_NAMES.some((pattern) => pattern.test(upper));\n}\n\n/**\n * The `env` an ensure hands the daemon: every name it may NOT inherit mapped to `null` (which\n * removes it), then the launch spec's own entries. Values are never inspected - a variable is kept\n * or dropped by its name alone - and the spec's entries win, because they were stated on purpose.\n */\nexport function daemonEnvOverrides(\n\tprocessEnv: Readonly<Record<string, string | undefined>>,\n\tspecEnv: Readonly<Record<string, string>> = {},\n\tplatform: NodeJS.Platform = process.platform,\n): Record<string, string | null> {\n\tconst overrides: Record<string, string | null> = {};\n\tfor (const name of Object.keys(processEnv)) {\n\t\tif (!daemonEnvIsAllowed(name, platform)) overrides[name] = null;\n\t}\n\tfor (const [name, value] of Object.entries(specEnv)) overrides[name] = value;\n\treturn overrides;\n}\n\n/** The names the daemon ends up with, sorted. Names only: a value never leaves this process. */\nexport function daemonEnvKeys(\n\tprocessEnv: Readonly<Record<string, string | undefined>>,\n\tspecEnv: Readonly<Record<string, string>> = {},\n\tplatform: NodeJS.Platform = process.platform,\n): string[] {\n\tconst kept = Object.keys(processEnv).filter((name) => daemonEnvIsAllowed(name, platform));\n\treturn [...new Set([...kept, ...Object.keys(specEnv)])].sort();\n}\n\n/**\n * Records the granted NAMES beside the daemon's other state, so `senpi host status` can report the\n * scope a running daemon was started with. Written by whichever client spawned the generation;\n * absent for a daemon started by a client that predates this file, which reads as \"unknown scope\".\n */\nexport async function writeDaemonEnvKeys(paths: HostDaemonPaths, keys: readonly string[]): Promise<void> {\n\tawait writeStateFile(daemonEnvKeysFile(paths), { env_keys: keys });\n}\n\nexport async function readDaemonEnvKeys(paths: HostDaemonPaths): Promise<string[]> {\n\tconst parsed = parseJson(await readFileOrUndefined(daemonEnvKeysFile(paths)));\n\tconst keys = parsed?.env_keys;\n\tif (!Array.isArray(keys)) return [];\n\treturn keys.filter((key): key is string => typeof key === \"string\");\n}\n\nfunction daemonEnvKeysFile(paths: HostDaemonPaths): string {\n\treturn join(paths.dir, \"env-keys.json\");\n}\n"]}
|
|
@@ -85,7 +85,7 @@ export type AttachOnlyDecision = Extract<HostDecision, {
|
|
|
85
85
|
action: "start" | "reuse" | "refuse";
|
|
86
86
|
}>;
|
|
87
87
|
/** Why an ensure refused. The decision's own refusals plus the one only a pidfile can produce. */
|
|
88
|
-
export type HostRefusalReason = "protocol" | "capability" | "foreign_writer" | "legacy_host";
|
|
88
|
+
export type HostRefusalReason = "protocol" | "capability" | "foreign_writer" | "legacy_host" | "host_busy";
|
|
89
89
|
/**
|
|
90
90
|
* An ensure that found an unusable host and refused to act on it. A refusal is FINAL by design:
|
|
91
91
|
* this process may not signal a host it did not start (I1), and starting a second host on an
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-decision.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAEN,KAAK,mBAAmB,EAExB,MAAM,qCAAqC,CAAC;AAM7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,KAAK,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEvF,4GAA4G;AAC5G,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAElE,kFAAkF;AAClF,eAAO,MAAM,0BAA0B,YACtC,eAAe,wDAIN,CAAC;AAEX,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,CAAC;IAC1C,iGAAiG;IACjG,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;CACnC;AAED,uGAAuG;AACvG,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;AAEhD,sEAAsE;AACtE,MAAM,MAAM,mBAAmB;AAC9B,gGAAgG;AAC9F,2BAA2B;AAC7B,+EAA+E;GAC7E,2BAA2B,CAAC;AAE/B,wGAAwG;AACxG,MAAM,MAAM,YAAY,GACrB;IACA,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;CAC3B,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAA;CAAE,GACtG;IACA,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB,CAAC;IAClD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC7B,GACD;IACA,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,+IAA+I;IAC/I,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,qBAAqB,GAAG,mBAAmB,CAAC;IAC5E,oGAAoG;IACpG,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CACtC,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAA;CAAE,CAAC;AAE3G,0GAA0G;AAC1G,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAA;CAAE,CAAC,CAAC;AAEjG,kGAAkG;AAClG,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,YAAY,GAAG,gBAAgB,GAAG,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"host-decision.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"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"}
|
|
@@ -60,6 +60,8 @@ function refusalDetail(reason, host) {
|
|
|
60
60
|
return "its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead";
|
|
61
61
|
case "legacy_host":
|
|
62
62
|
return "a host from before the per-socket daemon directory is still running on this endpoint; it is never signalled, and no second host is started beside it";
|
|
63
|
+
case "host_busy":
|
|
64
|
+
return "its socket accepts connections but did not answer inside the probe budget: a live host under load, which is never ended to make room for a replacement";
|
|
63
65
|
default:
|
|
64
66
|
return assertNever(reason);
|
|
65
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-decision.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACN,oBAAoB,GAGpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACN,2BAA2B,EAC3B,0BAA0B,EAC1B,uBAAuB,GACvB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAyB,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEvF,4GAA4G;AAC5G,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AAEvC;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAElE,kFAAkF;AAClF,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACzC,eAAe;IACf,2BAA2B;IAC3B,0BAA0B;IAC1B,uBAAuB;CACd,CAAC;AA2DX;;;;GAIG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAIhD,YAAY,MAAc,EAAE,MAAyB,EAAE,IAAkC;QACxF,KAAK,CAAC,cAAc,MAAM,aAAa,MAAM,MAAM,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AAED,SAAS,aAAa,CAAC,MAAyB,EAAE,IAAkC;IACnF,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,UAAU;YACd,OAAO,4CAA4C,IAAI,EAAE,eAAe,IAAI,SAAS,uBAAuB,qBAAqB,EAAE,CAAC;QACrI,KAAK,YAAY,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;YAC5G,OAAO,+BAA+B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,oDAAoD,CAAC;QACnH,CAAC;QACD,KAAK,gBAAgB;YACpB,OAAO,8GAA8G,CAAC;QACvH,KAAK,aAAa;YACjB,OAAO,sJAAsJ,CAAC;QAC/J;YACC,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;AACF,CAAC;AAYD,MAAM,UAAU,gBAAgB,CAC/B,MAA0B,EAC1B,IAAkC,EAClC,MAA0B;IAE1B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IAC7G,CAAC;IACD,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,CAAC,eAAe,EAAE,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAChG,0FAA0F;QAC1F,4FAA4F;QAC5F,IAAI,MAAM,KAAK,UAAU;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QACnG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IACvE,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;IAC7G,IAAI,MAAM,KAAK,UAAU,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1E,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACvE,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC1G,CAAC;IACD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5G,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC,cAAc,IAAI,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;QACrG,OAAO;YACN,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc;YAC7F,WAAW,EAAE,IAAI;SACjB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED,4GAA4G;AAC5G,SAAS,UAAU,CAAC,MAA0B,EAAE,IAAsB;IACrE,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,YAAY,GAAwB;QACzC,IAAI,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE;QAC9B,OAAO,EAAE,IAAI,CAAC,aAAa;QAC3B,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO;KACrD,CAAC;IACF,IAAI,oBAAoB,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3E,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,cAAc,CAAC,MAA0B,EAAE,IAAsB;IACzE,IAAI,MAAM,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,2BAA2B,CAAC;IAChH,IAAI,MAAM,CAAC,aAAa,CAAC,UAAU,KAAK,IAAI,CAAC,cAAc,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IACzF,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,2BAA2B,CAAC;AACtH,CAAC;AAED,SAAS,MAAM,CAAC,SAAuC,EAAE,OAAqC;IAC7F,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACnE,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,8FAA8F;AAC9F,SAAS,WAAW,CAAC,SAAwB,EAAE,OAAkC;IAChF,OAAO,OAAO,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACvG,CAAC;AAED,SAAS,WAAW,CAAC,KAAY;IAChC,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC","sourcesContent":["/**\n * What a client does when it finds - or does not find - a host on the shared RPC socket.\n *\n * One machine-wide host serves several client surfaces, so the process on the socket is\n * usually NOT this process's child. Two invariants follow, and this module is where they\n * are decided once instead of in every client:\n *\n * I1 - never terminate, signal or replace a host you did not start. The only sanctioned\n * replacement is the drain-based generation handoff, which ends no running work.\n * I2 - compatibility is `protocolVersion` + capabilities, NEVER a version-string compare,\n * and \"is my build newer?\" is `compareEngineOrdinal`, never `serverVersion`. An\n * ordinal that cannot be compared (a build without git metadata, a host that reports\n * none) is EQUAL, and since a handoff needs STRICTLY greater, such a pair attaches.\n *\n * The profile rule is the third guard: a handoff additionally requires the candidate's\n * extension set to be a SUPERSET of the running host's. Handing off to a narrower generation\n * would silently drop whatever the running host loaded (an editor plugin, a member bundle)\n * from every session that reopens in the new generation - so a narrower candidate attaches\n * and says so instead.\n */\nimport {\n\tcompareEngineOrdinal,\n\ttype EngineBuildIdentity,\n\ttype EngineOrdinal,\n} from \"../../core/engine-build-identity.ts\";\nimport {\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n} from \"./custom-capability.ts\";\nimport type { HostProtocolInfo } from \"./host-protocol-info.ts\";\nimport type { RpcLaunchProfile } from \"./rpc-types.ts\";\n\nexport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-protocol-info.ts\";\n\n/** The wire protocol this build speaks. A host answering with any other number is refused, not replaced. */\nexport const HOST_PROTOCOL_VERSION = 1;\n\n/**\n * HOST capability: this host drains into a successor generation on SIGUSR1 instead of dying with\n * its sessions. A host without it is attach-only - it is never renamed, signalled or upgraded.\n */\nexport const GENERATION_HANDOFF_CAPABILITY = \"generation_handoff\";\n\n/** What a daemon session needs from a host before any client may attach to it. */\nexport const REQUIRED_HOST_CAPABILITIES = [\n\t\"multi_session\",\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n] as const;\n\nexport interface HostDecisionClient {\n\treadonly protocolVersion: number;\n\treadonly requiredCapabilities: readonly string[];\n\treadonly identity: EngineBuildIdentity;\n\t/**\n\t * What THIS client would launch a host with. Absent = no launch spec (a bare CLI attaching to\n\t * whatever is running): such a client can prove nothing about extensions, so it never hands off.\n\t */\n\treadonly launchProfile?: RpcLaunchProfile;\n\t/** True when the running host's pidfile records this process as its writer (the I1 question). */\n\treadonly startedByUs: boolean;\n\t/** The platform both sides run on - one daemon is machine-local, so the host shares it. */\n\treadonly platform: NodeJS.Platform;\n}\n\n/** `upgrade` may hand off, `fallback` prefers no host over a mismatched one, `never` only attaches. */\nexport type HostDecisionPolicy = \"upgrade\" | \"fallback\" | \"never\";\n\nexport type HostAction = HostDecision[\"action\"];\n\n/** Attached anyway, with something the caller should say out loud. */\nexport type HostDecisionWarning =\n\t/** This client's extension set is missing something the host loads; upgrading would drop it. */\n\t| \"profile_narrower_attached\"\n\t/** The profiles differ (or cannot be compared) and no upgrade was possible. */\n\t| \"profile_mismatch_attached\";\n\n/** Every action carries only the reasons that can produce it, so an impossible pair cannot be built. */\nexport type HostDecision =\n\t| {\n\t\t\treadonly action: \"start\";\n\t\t\t/** `no_host`: nothing answered. `restart_own_host`: nothing answered and the pidfile is ours. */\n\t\t\treadonly reason: \"no_host\" | \"restart_own_host\";\n\t\t\treadonly upgradeable: false;\n\t }\n\t| { readonly action: \"refuse\"; readonly reason: \"protocol\" | \"capability\"; readonly upgradeable: false }\n\t| {\n\t\t\treadonly action: \"fallback\";\n\t\t\treadonly reason: \"capability\" | \"engine_mismatch\";\n\t\t\treadonly upgradeable: boolean;\n\t }\n\t| {\n\t\t\treadonly action: \"reuse\";\n\t\t\t/** `handoff_unsupported`: the host predates the drain handoff. `win32_attach_only`: named pipes cannot be renamed and win32 has no SIGUSR1. */\n\t\t\treadonly reason: \"compatible\" | \"handoff_unsupported\" | \"win32_attach_only\";\n\t\t\t/** Whether a generation handoff against this host is possible at all, independent of the policy. */\n\t\t\treadonly upgradeable: boolean;\n\t\t\treadonly warning?: HostDecisionWarning;\n\t }\n\t| { readonly action: \"handoff\"; readonly reason: \"newer_engine\" | \"profile\"; readonly upgradeable: true };\n\n/** What a `never` policy can answer: it attaches, starts or refuses, and never touches a running host. */\nexport type AttachOnlyDecision = Extract<HostDecision, { action: \"start\" | \"reuse\" | \"refuse\" }>;\n\n/** Why an ensure refused. The decision's own refusals plus the one only a pidfile can produce. */\nexport type HostRefusalReason = \"protocol\" | \"capability\" | \"foreign_writer\" | \"legacy_host\";\n\n/**\n * An ensure that found an unusable host and refused to act on it. A refusal is FINAL by design:\n * this process may not signal a host it did not start (I1), and starting a second host on an\n * endpoint another process already owns is exactly the failure the refusal exists to prevent.\n */\nexport class HostEnsureRefusedError extends Error {\n\treadonly socket: string;\n\treadonly reason: HostRefusalReason;\n\n\tconstructor(socket: string, reason: HostRefusalReason, host: HostProtocolInfo | undefined) {\n\t\tsuper(`RPC socket ${socket} refused: ${reason} - ${refusalDetail(reason, host)}`);\n\t\tthis.name = \"HostEnsureRefusedError\";\n\t\tthis.socket = socket;\n\t\tthis.reason = reason;\n\t}\n}\n\nfunction refusalDetail(reason: HostRefusalReason, host: HostProtocolInfo | undefined): string {\n\tswitch (reason) {\n\t\tcase \"protocol\":\n\t\t\treturn `the running host speaks protocol version ${host?.protocolVersion ?? \"unknown\"}, this build speaks ${HOST_PROTOCOL_VERSION}`;\n\t\tcase \"capability\": {\n\t\t\tconst missing = REQUIRED_HOST_CAPABILITIES.filter((capability) => !host?.capabilities.includes(capability));\n\t\t\treturn `the running host is missing ${JSON.stringify(missing)}; it owns the socket, so no second host is started`;\n\t\t}\n\t\tcase \"foreign_writer\":\n\t\t\treturn \"its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead\";\n\t\tcase \"legacy_host\":\n\t\t\treturn \"a host from before the per-socket daemon directory is still running on this endpoint; it is never signalled, and no second host is started beside it\";\n\t\tdefault:\n\t\t\treturn assertNever(reason);\n\t}\n}\n\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: \"never\",\n): AttachOnlyDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision {\n\tif (host === undefined) {\n\t\treturn { action: \"start\", reason: client.startedByUs ? \"restart_own_host\" : \"no_host\", upgradeable: false };\n\t}\n\tif (host.protocolVersion !== client.protocolVersion) {\n\t\treturn { action: \"refuse\", reason: \"protocol\", upgradeable: false };\n\t}\n\tif (!client.requiredCapabilities.every((capability) => host.capabilities.includes(capability))) {\n\t\t// A host that cannot serve this client still OWNS the socket: starting a second one would\n\t\t// bind over an endpoint another process is serving. Refuse, or fall back to no host at all.\n\t\tif (policy === \"fallback\") return { action: \"fallback\", reason: \"capability\", upgradeable: false };\n\t\treturn { action: \"refuse\", reason: \"capability\", upgradeable: false };\n\t}\n\tconst upgradeable = client.platform !== \"win32\" && host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY);\n\tif (policy === \"fallback\" && host.engineVersion !== client.identity.text) {\n\t\treturn { action: \"fallback\", reason: \"engine_mismatch\", upgradeable };\n\t}\n\tconst warning = profileWarning(client, host);\n\tif (client.platform === \"win32\") {\n\t\treturn { action: \"reuse\", reason: \"win32_attach_only\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (!upgradeable) {\n\t\treturn { action: \"reuse\", reason: \"handoff_unsupported\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (policy === \"upgrade\" && client.launchProfile && host.launch_profile && supersedes(client, host)) {\n\t\treturn {\n\t\t\taction: \"handoff\",\n\t\t\treason: sameRelease(client.identity.ordinal, host.engineOrdinal) ? \"profile\" : \"newer_engine\",\n\t\t\tupgradeable: true,\n\t\t};\n\t}\n\treturn { action: \"reuse\", reason: \"compatible\", upgradeable, ...(warning && { warning }) };\n}\n\n/** A strictly newer build whose extensions cover everything the host loads - the only handoff candidate. */\nfunction supersedes(client: HostDecisionClient, host: HostProtocolInfo): boolean {\n\tif (host.engineOrdinal === undefined) return false;\n\tconst hostIdentity: EngineBuildIdentity = {\n\t\ttext: host.engineVersion ?? \"\",\n\t\tordinal: host.engineOrdinal,\n\t\tscheme: host.engineOrdinal[4] > 0 ? \"epoch\" : \"nodef\",\n\t};\n\tif (compareEngineOrdinal(client.identity, hostIdentity) <= 0) return false;\n\treturn covers(client.launchProfile, host.launch_profile);\n}\n\nfunction profileWarning(client: HostDecisionClient, host: HostProtocolInfo): HostDecisionWarning | undefined {\n\tif (client.launchProfile === undefined || host.launch_profile === undefined) return \"profile_mismatch_attached\";\n\tif (client.launchProfile.profile_id === host.launch_profile.profile_id) return undefined;\n\treturn covers(client.launchProfile, host.launch_profile) ? \"profile_mismatch_attached\" : \"profile_narrower_attached\";\n}\n\nfunction covers(candidate: RpcLaunchProfile | undefined, running: RpcLaunchProfile | undefined): boolean {\n\tif (candidate === undefined || running === undefined) return false;\n\treturn running.core.extensions.every((extension) => candidate.core.extensions.includes(extension));\n}\n\n/** True when both builds are the same released version and only their build epoch differs. */\nfunction sameRelease(candidate: EngineOrdinal, running: EngineOrdinal | undefined): boolean {\n\treturn running !== undefined && candidate.slice(0, 4).every((part, index) => part === running[index]);\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"host-decision.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-decision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What a client does when it finds - or does not find - a host on the shared RPC socket.\n *\n * One machine-wide host serves several client surfaces, so the process on the socket is\n * usually NOT this process's child. Two invariants follow, and this module is where they\n * are decided once instead of in every client:\n *\n * I1 - never terminate, signal or replace a host you did not start. The only sanctioned\n * replacement is the drain-based generation handoff, which ends no running work.\n * I2 - compatibility is `protocolVersion` + capabilities, NEVER a version-string compare,\n * and \"is my build newer?\" is `compareEngineOrdinal`, never `serverVersion`. An\n * ordinal that cannot be compared (a build without git metadata, a host that reports\n * none) is EQUAL, and since a handoff needs STRICTLY greater, such a pair attaches.\n *\n * The profile rule is the third guard: a handoff additionally requires the candidate's\n * extension set to be a SUPERSET of the running host's. Handing off to a narrower generation\n * would silently drop whatever the running host loaded (an editor plugin, a member bundle)\n * from every session that reopens in the new generation - so a narrower candidate attaches\n * and says so instead.\n */\nimport {\n\tcompareEngineOrdinal,\n\ttype EngineBuildIdentity,\n\ttype EngineOrdinal,\n} from \"../../core/engine-build-identity.ts\";\nimport {\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n} from \"./custom-capability.ts\";\nimport type { HostProtocolInfo } from \"./host-protocol-info.ts\";\nimport type { RpcLaunchProfile } from \"./rpc-types.ts\";\n\nexport { type HostProtocolInfo, parseHostProtocolInfo } from \"./host-protocol-info.ts\";\n\n/** The wire protocol this build speaks. A host answering with any other number is refused, not replaced. */\nexport const HOST_PROTOCOL_VERSION = 1;\n\n/**\n * HOST capability: this host drains into a successor generation on SIGUSR1 instead of dying with\n * its sessions. A host without it is attach-only - it is never renamed, signalled or upgraded.\n */\nexport const GENERATION_HANDOFF_CAPABILITY = \"generation_handoff\";\n\n/** What a daemon session needs from a host before any client may attach to it. */\nexport const REQUIRED_HOST_CAPABILITIES = [\n\t\"multi_session\",\n\tEXTENSION_EVENTS_CAPABILITY,\n\tSESSION_CONTEXT_CAPABILITY,\n\tSESSION_KIND_CAPABILITY,\n] as const;\n\nexport interface HostDecisionClient {\n\treadonly protocolVersion: number;\n\treadonly requiredCapabilities: readonly string[];\n\treadonly identity: EngineBuildIdentity;\n\t/**\n\t * What THIS client would launch a host with. Absent = no launch spec (a bare CLI attaching to\n\t * whatever is running): such a client can prove nothing about extensions, so it never hands off.\n\t */\n\treadonly launchProfile?: RpcLaunchProfile;\n\t/** True when the running host's pidfile records this process as its writer (the I1 question). */\n\treadonly startedByUs: boolean;\n\t/** The platform both sides run on - one daemon is machine-local, so the host shares it. */\n\treadonly platform: NodeJS.Platform;\n}\n\n/** `upgrade` may hand off, `fallback` prefers no host over a mismatched one, `never` only attaches. */\nexport type HostDecisionPolicy = \"upgrade\" | \"fallback\" | \"never\";\n\nexport type HostAction = HostDecision[\"action\"];\n\n/** Attached anyway, with something the caller should say out loud. */\nexport type HostDecisionWarning =\n\t/** This client's extension set is missing something the host loads; upgrading would drop it. */\n\t| \"profile_narrower_attached\"\n\t/** The profiles differ (or cannot be compared) and no upgrade was possible. */\n\t| \"profile_mismatch_attached\";\n\n/** Every action carries only the reasons that can produce it, so an impossible pair cannot be built. */\nexport type HostDecision =\n\t| {\n\t\t\treadonly action: \"start\";\n\t\t\t/** `no_host`: nothing answered. `restart_own_host`: nothing answered and the pidfile is ours. */\n\t\t\treadonly reason: \"no_host\" | \"restart_own_host\";\n\t\t\treadonly upgradeable: false;\n\t }\n\t| { readonly action: \"refuse\"; readonly reason: \"protocol\" | \"capability\"; readonly upgradeable: false }\n\t| {\n\t\t\treadonly action: \"fallback\";\n\t\t\treadonly reason: \"capability\" | \"engine_mismatch\";\n\t\t\treadonly upgradeable: boolean;\n\t }\n\t| {\n\t\t\treadonly action: \"reuse\";\n\t\t\t/** `handoff_unsupported`: the host predates the drain handoff. `win32_attach_only`: named pipes cannot be renamed and win32 has no SIGUSR1. */\n\t\t\treadonly reason: \"compatible\" | \"handoff_unsupported\" | \"win32_attach_only\";\n\t\t\t/** Whether a generation handoff against this host is possible at all, independent of the policy. */\n\t\t\treadonly upgradeable: boolean;\n\t\t\treadonly warning?: HostDecisionWarning;\n\t }\n\t| { readonly action: \"handoff\"; readonly reason: \"newer_engine\" | \"profile\"; readonly upgradeable: true };\n\n/** What a `never` policy can answer: it attaches, starts or refuses, and never touches a running host. */\nexport type AttachOnlyDecision = Extract<HostDecision, { action: \"start\" | \"reuse\" | \"refuse\" }>;\n\n/** Why an ensure refused. The decision's own refusals plus the one only a pidfile can produce. */\nexport type HostRefusalReason = \"protocol\" | \"capability\" | \"foreign_writer\" | \"legacy_host\" | \"host_busy\";\n\n/**\n * An ensure that found an unusable host and refused to act on it. A refusal is FINAL by design:\n * this process may not signal a host it did not start (I1), and starting a second host on an\n * endpoint another process already owns is exactly the failure the refusal exists to prevent.\n */\nexport class HostEnsureRefusedError extends Error {\n\treadonly socket: string;\n\treadonly reason: HostRefusalReason;\n\n\tconstructor(socket: string, reason: HostRefusalReason, host: HostProtocolInfo | undefined) {\n\t\tsuper(`RPC socket ${socket} refused: ${reason} - ${refusalDetail(reason, host)}`);\n\t\tthis.name = \"HostEnsureRefusedError\";\n\t\tthis.socket = socket;\n\t\tthis.reason = reason;\n\t}\n}\n\nfunction refusalDetail(reason: HostRefusalReason, host: HostProtocolInfo | undefined): string {\n\tswitch (reason) {\n\t\tcase \"protocol\":\n\t\t\treturn `the running host speaks protocol version ${host?.protocolVersion ?? \"unknown\"}, this build speaks ${HOST_PROTOCOL_VERSION}`;\n\t\tcase \"capability\": {\n\t\t\tconst missing = REQUIRED_HOST_CAPABILITIES.filter((capability) => !host?.capabilities.includes(capability));\n\t\t\treturn `the running host is missing ${JSON.stringify(missing)}; it owns the socket, so no second host is started`;\n\t\t}\n\t\tcase \"foreign_writer\":\n\t\t\treturn \"its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead\";\n\t\tcase \"legacy_host\":\n\t\t\treturn \"a host from before the per-socket daemon directory is still running on this endpoint; it is never signalled, and no second host is started beside it\";\n\t\tcase \"host_busy\":\n\t\t\treturn \"its socket accepts connections but did not answer inside the probe budget: a live host under load, which is never ended to make room for a replacement\";\n\t\tdefault:\n\t\t\treturn assertNever(reason);\n\t}\n}\n\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: \"never\",\n): AttachOnlyDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision;\nexport function decideHostAction(\n\tclient: HostDecisionClient,\n\thost: HostProtocolInfo | undefined,\n\tpolicy: HostDecisionPolicy,\n): HostDecision {\n\tif (host === undefined) {\n\t\treturn { action: \"start\", reason: client.startedByUs ? \"restart_own_host\" : \"no_host\", upgradeable: false };\n\t}\n\tif (host.protocolVersion !== client.protocolVersion) {\n\t\treturn { action: \"refuse\", reason: \"protocol\", upgradeable: false };\n\t}\n\tif (!client.requiredCapabilities.every((capability) => host.capabilities.includes(capability))) {\n\t\t// A host that cannot serve this client still OWNS the socket: starting a second one would\n\t\t// bind over an endpoint another process is serving. Refuse, or fall back to no host at all.\n\t\tif (policy === \"fallback\") return { action: \"fallback\", reason: \"capability\", upgradeable: false };\n\t\treturn { action: \"refuse\", reason: \"capability\", upgradeable: false };\n\t}\n\tconst upgradeable = client.platform !== \"win32\" && host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY);\n\tif (policy === \"fallback\" && host.engineVersion !== client.identity.text) {\n\t\treturn { action: \"fallback\", reason: \"engine_mismatch\", upgradeable };\n\t}\n\tconst warning = profileWarning(client, host);\n\tif (client.platform === \"win32\") {\n\t\treturn { action: \"reuse\", reason: \"win32_attach_only\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (!upgradeable) {\n\t\treturn { action: \"reuse\", reason: \"handoff_unsupported\", upgradeable: false, ...(warning && { warning }) };\n\t}\n\tif (policy === \"upgrade\" && client.launchProfile && host.launch_profile && supersedes(client, host)) {\n\t\treturn {\n\t\t\taction: \"handoff\",\n\t\t\treason: sameRelease(client.identity.ordinal, host.engineOrdinal) ? \"profile\" : \"newer_engine\",\n\t\t\tupgradeable: true,\n\t\t};\n\t}\n\treturn { action: \"reuse\", reason: \"compatible\", upgradeable, ...(warning && { warning }) };\n}\n\n/** A strictly newer build whose extensions cover everything the host loads - the only handoff candidate. */\nfunction supersedes(client: HostDecisionClient, host: HostProtocolInfo): boolean {\n\tif (host.engineOrdinal === undefined) return false;\n\tconst hostIdentity: EngineBuildIdentity = {\n\t\ttext: host.engineVersion ?? \"\",\n\t\tordinal: host.engineOrdinal,\n\t\tscheme: host.engineOrdinal[4] > 0 ? \"epoch\" : \"nodef\",\n\t};\n\tif (compareEngineOrdinal(client.identity, hostIdentity) <= 0) return false;\n\treturn covers(client.launchProfile, host.launch_profile);\n}\n\nfunction profileWarning(client: HostDecisionClient, host: HostProtocolInfo): HostDecisionWarning | undefined {\n\tif (client.launchProfile === undefined || host.launch_profile === undefined) return \"profile_mismatch_attached\";\n\tif (client.launchProfile.profile_id === host.launch_profile.profile_id) return undefined;\n\treturn covers(client.launchProfile, host.launch_profile) ? \"profile_mismatch_attached\" : \"profile_narrower_attached\";\n}\n\nfunction covers(candidate: RpcLaunchProfile | undefined, running: RpcLaunchProfile | undefined): boolean {\n\tif (candidate === undefined || running === undefined) return false;\n\treturn running.core.extensions.every((extension) => candidate.core.extensions.includes(extension));\n}\n\n/** True when both builds are the same released version and only their build epoch differs. */\nfunction sameRelease(candidate: EngineOrdinal, running: EngineOrdinal | undefined): boolean {\n\treturn running !== undefined && candidate.slice(0, 4).every((part, index) => part === running[index]);\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
@@ -14,7 +14,7 @@ import { decideHostAction, HOST_PROTOCOL_VERSION, HostEnsureRefusedError, REQUIR
|
|
|
14
14
|
import { handoffHost } from "./host-handoff.js";
|
|
15
15
|
import { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from "./host-launch.js";
|
|
16
16
|
import { DEFAULT_HOST_IDLE_EXIT_MS } from "./host-lifecycle.js";
|
|
17
|
-
import { probeProtocolInfo } from "./host-probe.js";
|
|
17
|
+
import { probeProtocolInfo, probeSocketReachable } from "./host-probe.js";
|
|
18
18
|
import { acquireOwnershipSafeLock } from "./ownership-safe-lock.js";
|
|
19
19
|
import { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from "./protocol-identity.js";
|
|
20
20
|
import { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from "./socket-transport.js";
|
|
@@ -96,6 +96,13 @@ async function ensureHostLocked(paths, socket, options) {
|
|
|
96
96
|
// this record may end it - anyone else refuses rather than signalling somebody else's host.
|
|
97
97
|
if (!startedByUs)
|
|
98
98
|
throw new HostEnsureRefusedError(socket, "foreign_writer", protocol);
|
|
99
|
+
// ... and silent is not the same as gone. A host serving many sessions can miss a probe
|
|
100
|
+
// budget while its event loop is busy; its socket still ACCEPTS the connection. Ending it
|
|
101
|
+
// then would destroy every live session to replace a host that was never broken, so a
|
|
102
|
+
// reachable socket is refused instead of signalled - the caller retries or falls back.
|
|
103
|
+
if (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {
|
|
104
|
+
throw new HostEnsureRefusedError(socket, "host_busy", protocol);
|
|
105
|
+
}
|
|
99
106
|
await stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);
|
|
100
107
|
}
|
|
101
108
|
// A host from before this layout registered itself in the FLAT directory. Its files are another
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\tif (registered && pidMatches) {\n\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\tif (!startedByUs) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\nasync function startHost(paths: HostDaemonPaths, socket: string, options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration: 0,\n\t\tinstanceId,\n\t});\n\tconst stderr = await open(paths.stderrLog, \"w\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration: 0,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = \"0\";\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAEN,8BAA8B,EAC9B,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,GAEnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,qBAAqB,EACrB,gBAAgB,EAEhB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACN,gBAAgB,EAChB,qBAAqB,EAGrB,sBAAsB,EAEtB,0BAA0B,GAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,yBAAyB,EAAqD,MAAM,qBAAqB,CAAC;AACnH,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE5G,OAAO,EACN,qBAAqB,EACrB,mBAAmB,EAEnB,oBAAoB,GAEpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAyB,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAiDnE,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAC7C,MAAM,8BAA8B,GAAG,MAAM,CAAC;AAC9C,MAAM,4BAA4B,GAAG,MAAM,CAAC;AAC5C,MAAM,uBAAuB,GAAG,MAAM,CAAC;AACvC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B;;;;;;;;GAQG;AACH,MAAM,mBAAmB,GACxB,8BAA8B,GAAG,uBAAuB,GAAG,gBAAgB,GAAG,4BAA4B,GAAG,MAAM,CAAC;AACrH,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,MAAM,WAAW,GAAG;IACnB,OAAO,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,iBAAiB,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE;CACnG,CAAC;AACX,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA0B;IAC1D,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7G,MAAM,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACrC,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5D,iFAAiF;IACjF,oFAAoF;IACpF,qFAAqF;IACrF,iFAAiF;IACjF,mFAAmF;IACnF,MAAM,4BAA4B,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,GAAG,UAAU,OAAO,EAAE,WAAW,CAAC,CAAC;IAClF,IAAI,CAAC;QACJ,MAAM,OAAO,CAAC,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC;QAC3C,OAAO,MAAM,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,EAAE,CAAC;IACjB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC9B,KAAsB,EACtB,MAAc,EACd,OAA0B;IAE1B,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;IAClC,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACrD,6FAA6F;IAC7F,0FAA0F;IAC1F,wFAAwF;IACxF,MAAM,cAAc,GAAG,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;IACjF,MAAM,WAAW,GAAG,cAAc,IAAI,CAAC,MAAM,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IACvF,MAAM,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;IACxD,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACzB,KAAK,OAAO;YACX,qEAAqE;YACrE,2EAA2E;YAC3E,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACnD,KAAK,QAAQ;YACZ,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACrE,KAAK,SAAS;YACb,OAAO,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;QAC/D,KAAK,OAAO;YACX,MAAM;QACP;YACC,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,EAAE,oBAAoB,IAAI,oBAAoB,CAAC;IACxE,MAAM,UAAU,GAAG,cAAc,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,uBAAuB,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAClH,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;QAC9B,4FAA4F;QAC5F,4FAA4F;QAC5F,IAAI,CAAC,WAAW;YAAE,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACvF,wFAAwF;QACxF,0FAA0F;QAC1F,sFAAsF;QACtF,uFAAuF;QACvF,IAAI,MAAM,oBAAoB,CAAC,MAAM,EAAE,8BAA8B,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,eAAe,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,aAAa,IAAI,uBAAuB,EAAE,KAAK,CAAC,CAAC;IACxG,CAAC;IACD,gGAAgG;IAChG,+FAA+F;IAC/F,2EAA2E;IAC3E,IAAI,MAAM,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,MAAM,IAAI,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC5G,IAAI,cAAc;QAAE,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACvD,OAAO,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,sGAAsG;AACtG,SAAS,MAAM,CACd,OAA0B,EAC1B,WAAoB,EACpB,QAAsC;IAEtC,IAAI,OAAO,CAAC,OAAO,KAAK,mBAAmB,EAAE,CAAC;QAC7C,OAAO,gBAAgB,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC3F,OAAO,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,iBAAiB,CAC/B,KAAsB,EACtB,MAAc,EACd,OAA0B,EAC1B,WAAmB;IAEnB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;QAChC,MAAM;QACN,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,WAAW,EAAE;QAC3C,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;QAChC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,KAAK,EAAE;YACN,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtG;KACD,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACnF,MAAM,YAAY,CACjB,KAAK,EACL,+BAA+B,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3F,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,0GAA0G;AAC1G,SAAS,YAAY,CAAC,OAA0B,EAAE,WAAoB;IACrE,OAAO;QACN,eAAe,EAAE,qBAAqB;QACtC,oBAAoB,EAAE,0BAA0B;QAChD,QAAQ,EAAE,mBAAmB,EAAE;QAC/B,2FAA2F;QAC3F,2FAA2F;QAC3F,gFAAgF;QAChF,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,mBAAmB;YAC1C,CAAC,CAAC,EAAE,aAAa,EAAE,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE;YAC5F,CAAC,CAAC,EAAE,CAAC;QACN,WAAW;QACX,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC1B,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,SAAS,aAAa,CAAC,QAA2B;IACjD,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED,mGAAmG;AACnG,SAAS,eAAe,CAAC,UAAsC,EAAE,MAAc;IAC9E,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC3C,OAAO,UAAU,CAAC,MAAM,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,CAAC;AACxE,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,KAAsB,EAAE,MAAc,EAAE,OAA0B;IAC1F,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC;IAClC,+FAA+F;IAC/F,4EAA4E;IAC5E,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC;IAChC,6EAA6E;IAC7E,qEAAqE;IACrE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,MAAM,kBAAkB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,MAAM,iBAAiB,CAAC,KAAK,EAAE;QAC9B,MAAM;QACN,YAAY,EAAE,+BAA+B;QAC7C,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,WAAW;QACnD,UAAU,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,IAAI,yBAAyB;QACnE,UAAU,EAAE,CAAC;QACb,UAAU;KACV,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACvD,IAAI,OAAkC,CAAC;IACvC,IAAI,KAA2C,CAAC;IAChD,IAAI,WAAkC,CAAC;IACvC,IAAI,SAAyC,CAAC;IAC9C,IAAI,CAAC;QACJ,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,WAAW,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC,cAAc,CAAC,IAAI,iBAAiB,CAAC,cAAc,CAAC,CAAC;QAChH,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE;YAC/C,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,IAAI;YACjB,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;YAC5C,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;SACtC,CAAC,CAAC;QACH,SAAS,GAAG,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;YACvC,KAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;gBACpC,WAAW,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC/B,WAAW,CAAC,WAAW,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,WAAW,EAAE,oBAAoB,IAAI,oBAAoB,CAAC;QACxE,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC5C,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;YAC1C,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE;gBACnB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;YAC/E,CAAC,CAAC;SACF,CAAC,CAAC;QACH,4FAA4F;QAC5F,4FAA4F;QAC5F,kCAAkC;QAClC,MAAM,cAAc,GAAG,WAAW,EAAE,oBAAoB;YACvD,CAAC,CAAC,WAAW,CAAC,oBAAoB;YAClC,CAAC,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxE,MAAM,gBAAgB,GAAG,iBAAiB,IAAI,CAAC,MAAM,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACvG,+FAA+F;QAC/F,0FAA0F;QAC1F,8FAA8F;QAC9F,+FAA+F;QAC/F,OAAO,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,IAAI,IAAI,EAAE,CAAC;QACzE,MAAM,WAAW,EAAE,kBAAkB,EAAE,EAAE,CAAC;QAC1C,MAAM,qBAAqB,CAAC,KAAK,EAAE;YAClC,MAAM,EAAE,OAAO;YACf,MAAM;YACN,UAAU;YACV,UAAU,EAAE,CAAC;YACb,eAAe,EAAE,iBAAiB,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU;SACnG,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,8EAA8E;QAC9E,4EAA4E;QAC5E,6EAA6E;QAC7E,yDAAyD;QACzD,MAAM,mBAAmB,GAAG,WAAW,CAAC;QACxC,6EAA6E;QAC7E,0EAA0E;QAC1E,4EAA4E;QAC5E,IAAI,CAAC,mBAAmB,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC3F,IAAI,CAAC;gBACJ,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,IAAI,SAAS;gBAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7D,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBAC1D,IAAI,CAAC;oBACJ,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACvB,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACX,CAAC;QACF,CAAC;QACD,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC1B,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,KAAK,CAAC;QACb,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,YAAY,CACpC,KAAK,EACL,oCAAoC,mBAAmB,CAAC,IAAI,IAAI,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,qCAAqC,CAClL,CAAC;QACF,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;YAAS,CAAC;QACV,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IACD,MAAM,kBAAkB,GAAG,WAAW,EAAE,kBAAkB,IAAI,4BAA4B,CAAC;IAC3F,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,EAAE,SAAS,CAAC,CAAC;IAC7E,IAAI,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACtF,+EAA+E;IAC/E,gFAAgF;IAChF,+EAA+E;IAC/E,mEAAmE;IACnE,mFAAmF;IACnF,iFAAiF;IACjF,2DAA2D;IAC3D,MAAM,WAAW,GAAG,MAAM,gBAAgB,CACzC,KAAK,EACL,SAAS,EACT,WAAW,EAAE,aAAa,IAAI,uBAAuB,CACrD,CAAC,IAAI,CACL,GAAG,EAAE,CAAC,SAAS,EACf,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC5E,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ;QAC9B,CAAC,CAAC,mEAAmE,MAAM,CAAC,QAAQ,CAAC,eAAe,mBAAmB,MAAM,CAAC,QAAQ,CAAC,aAAa,qBAAqB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,+CAA+C,qBAAqB,8BAA8B,IAAI,CAAC,SAAS,CAAC,0BAA0B,CAAC,EAAE;QACnW,CAAC,CAAC,MAAM,CAAC,MAAM;YACd,CAAC,CAAC,oCAAoC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,qCAAqC;YAClK,CAAC,CAAC,mEAAmE,kBAAkB,IAAI,CAAC;IAC9F,MAAM,UAAU,GAAG,MAAM,YAAY,CACpC,KAAK,EACL,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,6BAA6B,WAAW,GAAG,CAC3F,CAAC;IACF,MAAM,qBAAqB,CAAC,KAAK,CAAC,CAAC;IACnC,2EAA2E;IAC3E,0EAA0E;IAC1E,iCAAiC;IACjC,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,uBAAuB,CACrC,OAAsB,EACtB,KAAmD;IAEnD,IAAI,CAAC;QACJ,OAAO,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,KAAK,YAAY,8BAA8B;YAAE,OAAO,KAAK,CAAC;QAClE,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC9B,KAAmB,EACnB,SAA6B,EAC7B,aAAqB;IAErB,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAC1E,IAAI,MAAM,EAAE;QAAE,OAAO;IACrB,MAAM,MAAM,GAAG,CAAC,IAAoB,EAAE,EAAE;QACvC,IAAI,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACzB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC;gBAAE,MAAM,KAAK,CAAC;QACnD,CAAC;IACF,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3G,MAAM,CAAC,SAAS,CAAC,CAAC;IAClB,IAAI,MAAM,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO;IACzC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClB,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,uBAAuB,KAAK,CAAC,GAAG,IAAI,GAAG,+BAA+B,CAAC,CAAC;IACzF,CAAC;AACF,CAAC;AAED,KAAK,UAAU,eAAe,CAC7B,OAAsB,EACtB,aAAqB,EACrB,aAAa,GAAiD,oBAAoB;IAElF,MAAM,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IACzD,IAAI,MAAM,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC;QAAE,OAAO;IACrE,MAAM,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IACzD,IAAI,CAAC,CAAC,MAAM,WAAW,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,uBAAuB,OAAO,CAAC,GAAG,+BAA+B,CAAC,CAAC;IACpF,CAAC;AACF,CAAC;AAID,gFAAgF;AAChF,mFAAmF;AACnF,qFAAqF;AACrF,uFAAuF;AACvF,qEAAqE;AACrE,KAAK,UAAU,uBAAuB,CACrC,OAAsB,EACtB,aAA2D;IAE3D,IAAI,CAAC;QACJ,OAAO,CAAC,MAAM,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAChH,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,KAAK,YAAY,8BAA8B;YAAE,OAAO,SAAS,CAAC;QACtE,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,KAAK,UAAU,eAAe,CAC7B,OAAsB,EACtB,MAAsB,EACtB,aAAa,GAAiD,oBAAoB;IAElF,IAAI,CAAC,MAAM,uBAAuB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,KAAK,MAAM;QAAE,OAAO;IAC/E,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC;YAAE,MAAM,KAAK,CAAC;IACnD,CAAC;AACF,CAAC;AAED,KAAK,UAAU,WAAW,CACzB,OAAsB,EACtB,SAAiB,EACjB,aAAa,GAAiD,oBAAoB;IAElF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,uBAAuB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QACpF,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,MAAM,uBAAuB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,KAAK,MAAM,CAAC;AAC3E,CAAC;AAMD,KAAK,UAAU,gBAAgB,CAC9B,MAAc,EACd,SAAiB,EACjB,SAA8B;IAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,IAAI,YAA0C,CAAC;IAC/C,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,iBAAiB,CAC9B,MAAM,EACN,IAAI,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAC3E,CAAC;QACF,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC;QAC/E,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,sEAAsE;YACtE,sEAAsE;YACtE,wEAAwE;YACxE,qEAAqE;YACrE,uEAAuE;YACvE,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC;YACzB,IAAI,IAAI,EAAE,CAAC;gBACV,YAAY,GAAG,IAAI,CAAC;gBACpB,IAAI,YAAY,CAAC,IAAI,CAAC;oBAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACP,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;YAClD,CAAC;QACF,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YAClB,YAAY,GAAG,KAAK,CAAC;YACrB,IAAI,YAAY,CAAC,KAAK,CAAC;gBAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACrD,CAAC;QACD,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,WAAW,CAAC,KAA+C;IACnE,OAAO,CAAC,CAAC,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,QAAsC;IAC3D,OAAO,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC;AACpG,CAAC;AAED,8GAA8G;AAC9G,SAAS,OAAO,CACf,OAA0B,EAC1B,UAA4E;IAE5E,MAAM,GAAG,GAAsB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,GAAG,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,QAAQ,IAAI,WAAW,EAAE,CAAC;IACvD,GAAG,CAAC,2BAA2B,CAAC,GAAG,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7E,+FAA+F;IAC/F,8FAA8F;IAC9F,0FAA0F;IAC1F,GAAG,CAAC,oBAAoB,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC;IAClD,GAAG,CAAC,mBAAmB,CAAC,GAAG,GAAG,CAAC;IAC/B,GAAG,CAAC,mBAAmB,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;IAChD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,KAAK,UAAU,4BAA4B;IAC1C,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,MAAM,OAAO,CAAC,GAAG,CAChB,OAAO;aACL,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC;aAC3F,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACpB,IAAI,CAAC;gBACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAIpF,CAAC;gBACF,IACC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;oBAC7B,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ;oBAC1C,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;oBACnC,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC;oBACjC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM;oBACrC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC;oBACxD,CAAC,CAAC,MAAM,qBAAqB,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAChG,CAAC,KAAc,EAAE,EAAE;wBAClB,6EAA6E;wBAC7E,IAAI,KAAK,YAAY,8BAA8B;4BAAE,OAAO,IAAI,CAAC;wBACjE,MAAM,KAAK,CAAC;oBACb,CAAC,CACD,CAAC;oBAEF,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACX,CAAC,CAAC,CACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACX,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,KAAsB,EAAE,OAAe;IAClE,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACnD,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,IAAI,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO,OAAO,CAAC;QACrD,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAc;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC;SACzB,MAAM,CAAC,6BAA6B,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;SACvE,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACzC,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,eAAe,CAAC,KAAc,EAAE,IAAY;IACpD,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACzE,CAAC;AAED,SAAS,WAAW,CAAC,KAAY;IAChC,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACxE,CAAC","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo, probeSocketReachable } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\tif (registered && pidMatches) {\n\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\tif (!startedByUs) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\t// ... and silent is not the same as gone. A host serving many sessions can miss a probe\n\t\t// budget while its event loop is busy; its socket still ACCEPTS the connection. Ending it\n\t\t// then would destroy every live session to replace a host that was never broken, so a\n\t\t// reachable socket is refused instead of signalled - the caller retries or falls back.\n\t\tif (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {\n\t\t\tthrow new HostEnsureRefusedError(socket, \"host_busy\", protocol);\n\t\t}\n\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\nasync function startHost(paths: HostDaemonPaths, socket: string, options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration: 0,\n\t\tinstanceId,\n\t});\n\tconst stderr = await open(paths.stderrLog, \"w\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration: 0,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = \"0\";\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type SessionRuntimeKind } from "../../cli/args.ts";
|
|
2
|
+
import type { HostColdStart, HostLifecyclePolicyInput } from "./host-lifecycle.ts";
|
|
3
|
+
export interface HostLaunchSpecCore {
|
|
4
|
+
readonly session_runtime: SessionRuntimeKind;
|
|
5
|
+
/** A daemon serves many clients at once; a spec that says otherwise is refused, not honoured. */
|
|
6
|
+
readonly multi_session: true;
|
|
7
|
+
/** Extension paths, relative to the directory holding the spec file. */
|
|
8
|
+
readonly extensions: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
/** The document itself, as a client writes it. */
|
|
11
|
+
export interface HostLaunchSpec {
|
|
12
|
+
readonly spec_version: 1;
|
|
13
|
+
readonly core: HostLaunchSpecCore;
|
|
14
|
+
readonly tunables?: {
|
|
15
|
+
readonly idleExitMs?: number;
|
|
16
|
+
readonly coldStart?: HostColdStart;
|
|
17
|
+
};
|
|
18
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
19
|
+
}
|
|
20
|
+
export type HostLaunchSpecRefusal = "launch_spec_unreadable" | "launch_spec_invalid" | "launch_spec_insecure" | "launch_spec_path_escape" | "launch_spec_env_denied" | "launch_spec_missing_extension";
|
|
21
|
+
/** A spec that may not be acted on, naming the property that failed and the path that failed it. */
|
|
22
|
+
export declare class HostLaunchSpecError extends Error {
|
|
23
|
+
readonly reason: HostLaunchSpecRefusal;
|
|
24
|
+
readonly detail: string;
|
|
25
|
+
constructor(reason: HostLaunchSpecRefusal, detail: string);
|
|
26
|
+
}
|
|
27
|
+
/** A trusted spec, in the shape the ensure/handoff entry points take. */
|
|
28
|
+
export interface ResolvedHostLaunchSpec {
|
|
29
|
+
/** Host CLI arguments: the session runtime and one absolute `--extension` per listed path. */
|
|
30
|
+
readonly hostArgs: readonly string[];
|
|
31
|
+
readonly policy: HostLifecyclePolicyInput;
|
|
32
|
+
readonly env: Readonly<Record<string, string>>;
|
|
33
|
+
}
|
|
34
|
+
/** What an ensure without a spec launches: the daemon defaults, no extensions, no extra env. */
|
|
35
|
+
export declare const DEFAULT_HOST_LAUNCH_SPEC: ResolvedHostLaunchSpec;
|
|
36
|
+
/**
|
|
37
|
+
* Reads, trusts and resolves one launch spec. Every failure is a `HostLaunchSpecError`; nothing is
|
|
38
|
+
* started, and no daemon state is written, until this has returned.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadHostLaunchSpec(specPath: string, platform?: NodeJS.Platform): Promise<ResolvedHostLaunchSpec>;
|
|
41
|
+
/** Parses the document. A boundary: everything past it is typed and nothing re-validates it. */
|
|
42
|
+
export declare function parseHostLaunchSpec(text: string): HostLaunchSpec;
|
|
43
|
+
//# sourceMappingURL=host-launch-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-launch-spec.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-launch-spec.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAKnF,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,eAAe,EAAE,kBAAkB,CAAC;IAC7C,iGAAiG;IACjG,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IACzF,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,qBAAqB,GAC9B,wBAAwB,GACxB,qBAAqB,GACrB,sBAAsB,GACtB,yBAAyB,GACzB,wBAAwB,GACxB,+BAA+B,CAAC;AAEnC,oGAAoG;AACpG,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,YAAY,MAAM,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAKxD;CACD;AAED,yEAAyE;AACzE,MAAM,WAAW,sBAAsB;IACtC,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;IAC1C,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C;AAED,gGAAgG;AAChG,eAAO,MAAM,wBAAwB,EAAE,sBAItC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,OAAO,CAAC,sBAAsB,CAAC,CAkBjC;AAED,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CA+BhE"}
|