@code-yeongyu/senpi 2026.9.18 → 2026.9.19-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +117 -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-QBJ4GPN7.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-5E5TLB76.js +8 -0
- package/dist/bundle/chunks/{chunk-AH72FRZ2.js → chunk-EHSDTQ6I.js} +13 -13
- 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-NLOZDJQX.js → chunk-LF3XTYYF.js} +3 -3
- package/dist/bundle/chunks/{chunk-ANTGWIGI.js → chunk-MUC3MZTV.js} +1 -1
- 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-MLJ2H637.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-7C75IXNF.js +2 -0
- package/dist/bundle/chunks/{multi-session-host-OK6WMHB4.js → multi-session-host-2YCJKQOL.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-4ANSELYT.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-536AQTIS.js → rotation-stream-KDLA7K6X.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-OPFQIWFP.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/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +57 -33
- package/dist/core/agent-session.js.map +1 -1
- 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 +84 -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,13 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
import{HOST_PROTOCOL_VERSION,HostEnsureRefusedError,REQUIRED_HOST_CAPABILITIES,decideHostAction,ensureHost,handoffHost,probeProtocolInfo,requestOnSocket,stopHost}from"./chunk-P7PWCGLJ.js";import{createHostDaemonPaths,generationPaths,parseJson,processIsLive,readFileOrUndefined,writeStateFile}from"./chunk-LF3XTYYF.js";import{engineBuildIdentity}from"./chunk-HSH674C6.js";import{isSessionRuntimeKind}from"./chunk-HGAX6JZ7.js";import{APP_NAME,envValue,getAgentDir}from"./chunk-H5RK2RYZ.js";import{writeSync}from"node:fs";import{resolve as resolve2}from"node:path";import{readFile,realpath,stat}from"node:fs/promises";import{dirname,isAbsolute,relative,resolve}from"node:path";var SPEC_ENV_NAME=/^(SENPI|OMO|PI)_[A-Z0-9_]+$/u,HostLaunchSpecError=class extends Error{constructor(reason,detail){super(`${reason}: ${detail}`),this.name="HostLaunchSpecError",this.reason=reason,this.detail=detail}},DEFAULT_HOST_LAUNCH_SPEC={hostArgs:[],policy:{},env:{}};async function loadHostLaunchSpec(specPath,platform=process.platform){let absolute=resolve(specPath);await assertTrustedFile(absolute,platform);let spec=parseHostLaunchSpec(await readSpecText(absolute)),env=trustedEnv(spec.env),extensions=await resolveExtensions(spec.core.extensions,dirname(absolute));return{hostArgs:["--session-runtime",spec.core.session_runtime,...extensions.flatMap(path=>["--extension",path])],policy:{...spec.tunables?.idleExitMs!==void 0&&{idleExitMs:spec.tunables.idleExitMs},...spec.tunables?.coldStart!==void 0&&{coldStart:spec.tunables.coldStart}},env}}function parseHostLaunchSpec(text){let parsed;try{parsed=JSON.parse(text)}catch(error){throw new HostLaunchSpecError("launch_spec_invalid",error instanceof Error?error.message:"not JSON")}if(!isRecord(parsed))throw new HostLaunchSpecError("launch_spec_invalid","the spec is not a JSON object");if(parsed.spec_version!==1)throw new HostLaunchSpecError("launch_spec_invalid",`unsupported spec_version ${String(parsed.spec_version)}`);let core=parsed.core;if(!isRecord(core))throw new HostLaunchSpecError("launch_spec_invalid","core must be an object");if(typeof core.session_runtime!="string"||!isSessionRuntimeKind(core.session_runtime))throw new HostLaunchSpecError("launch_spec_invalid","core.session_runtime must be in-process or worker");if(core.multi_session!==!0)throw new HostLaunchSpecError("launch_spec_invalid","core.multi_session must be true");if(!Array.isArray(core.extensions)||!core.extensions.every(entry=>typeof entry=="string"))throw new HostLaunchSpecError("launch_spec_invalid","core.extensions must be an array of strings");let tunables=parsedTunables(parsed.tunables);return{spec_version:1,core:{session_runtime:core.session_runtime,multi_session:!0,extensions:core.extensions},...tunables&&{tunables},...isRecord(parsed.env)&&{env:parsedEnv(parsed.env)}}}async function readSpecText(absolute){try{return await readFile(absolute,"utf8")}catch(error){throw new HostLaunchSpecError("launch_spec_unreadable",error instanceof Error?error.message:absolute)}}async function assertTrustedFile(absolute,platform){if(platform==="win32")return;let stats=await stat(absolute).catch(error=>{throw new HostLaunchSpecError("launch_spec_unreadable",error instanceof Error?error.message:absolute)}),uid=process.getuid?.();if(uid!==void 0&&stats.uid!==uid)throw new HostLaunchSpecError("launch_spec_insecure",`${absolute} is owned by uid ${stats.uid}, not ${uid}`);if((stats.mode&18)!==0)throw new HostLaunchSpecError("launch_spec_insecure",`${absolute} is group/world writable (mode ${(stats.mode&511).toString(8)})`)}function trustedEnv(env){for(let name of Object.keys(env??{}))if(!SPEC_ENV_NAME.test(name))throw new HostLaunchSpecError("launch_spec_env_denied",`${name} is outside ${SPEC_ENV_NAME.source}`);return env??{}}async function resolveExtensions(extensions,specDir){let rootReal=await realpath(specDir).catch(()=>specDir),resolved=[];for(let entry of extensions){let target=resolve(specDir,entry);if(escapes(specDir,target))throw new HostLaunchSpecError("launch_spec_path_escape",`${entry} resolves outside ${specDir}`);let real=await realpath(target).catch(()=>{});if(real===void 0)throw new HostLaunchSpecError("launch_spec_missing_extension",`${target} does not exist`);if(escapes(rootReal,real))throw new HostLaunchSpecError("launch_spec_path_escape",`${entry} links outside ${specDir}`);resolved.push(real)}return resolved}function escapes(root,target){let step=relative(root,target);return step.startsWith("..")||isAbsolute(step)}function parsedTunables(value){if(!isRecord(value))return;let idleExitMs=value.idleExitMs,coldStart=value.coldStart;return{...typeof idleExitMs=="number"&&Number.isFinite(idleExitMs)&&idleExitMs>0&&{idleExitMs},...(coldStart==="transient"||coldStart==="persistent")&&{coldStart}}}function parsedEnv(value){let env={};for(let[name,entry]of Object.entries(value)){if(typeof entry!="string")throw new HostLaunchSpecError("launch_spec_invalid",`env.${name} must be a string`);env[name]=entry}return env}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{join}from"node:path";var ALLOWED_ENV_NAMES=[/^(PATH|HOME|USER|LOGNAME|SHELL|TMPDIR|TERM|LANG)$/u,/^(LC|XDG)_/u,/^(SENPI|OMO|PI)_/u,/^(HTTPS?_PROXY|NO_PROXY)$/u,/^[A-Z0-9_]+_API_KEY$/u,/^(ANTHROPIC|OPENAI|GOOGLE|GEMINI|AZURE|AWS|OPENROUTER|XAI|MISTRAL|DEEPSEEK|GROQ|CEREBRAS|MINIMAX)_/u],WINDOWS_SYSTEM_ENV_NAMES=new Set(["SYSTEMROOT","SYSTEMDRIVE","WINDIR","COMSPEC","PATHEXT","TEMP","TMP","USERPROFILE","APPDATA","LOCALAPPDATA","PROGRAMFILES","PROGRAMFILES(X86)","PROGRAMDATA","HOMEDRIVE","HOMEPATH","NUMBER_OF_PROCESSORS","PROCESSOR_ARCHITECTURE","OS"]);function daemonEnvIsAllowed(name,platform=process.platform){if(platform!=="win32")return ALLOWED_ENV_NAMES.some(pattern=>pattern.test(name));let upper=name.toUpperCase();return WINDOWS_SYSTEM_ENV_NAMES.has(upper)||ALLOWED_ENV_NAMES.some(pattern=>pattern.test(upper))}function daemonEnvOverrides(processEnv,specEnv={},platform=process.platform){let overrides={};for(let name of Object.keys(processEnv))daemonEnvIsAllowed(name,platform)||(overrides[name]=null);for(let[name,value]of Object.entries(specEnv))overrides[name]=value;return overrides}function daemonEnvKeys(processEnv,specEnv={},platform=process.platform){let kept=Object.keys(processEnv).filter(name=>daemonEnvIsAllowed(name,platform));return[...new Set([...kept,...Object.keys(specEnv)])].sort()}async function writeDaemonEnvKeys(paths,keys){await writeStateFile(daemonEnvKeysFile(paths),{env_keys:keys})}async function readDaemonEnvKeys(paths){let keys=parseJson(await readFileOrUndefined(daemonEnvKeysFile(paths)))?.env_keys;return Array.isArray(keys)?keys.filter(key=>typeof key=="string"):[]}function daemonEnvKeysFile(paths){return join(paths.dir,"env-keys.json")}import{readdir as readdir2}from"node:fs/promises";import{execFile}from"node:child_process";import{readdir}from"node:fs/promises";import{promisify}from"node:util";var run=promisify(execFile),PS_TIMEOUT_MS=5e3,KILOBYTES_PER_MEGABYTE=1024,UNOBSERVED={rss_mb:null,open_fds:null,zombies:null};async function readHostProcessMetrics(pid,platform=process.platform){if(platform==="win32")return UNOBSERVED;let rows=await processTable();if(rows===void 0)return UNOBSERVED;let tree=descendants(rows,pid);return tree.length===0?UNOBSERVED:{rss_mb:Math.round(tree.reduce((total,row)=>total+row.rssKb,0)/KILOBYTES_PER_MEGABYTE),open_fds:platform==="linux"?await openDescriptors(tree):null,zombies:tree.filter(row=>row.state.startsWith("Z")).length}}async function processTable(){try{let{stdout}=await run("ps",["-A","-o","pid=,ppid=,stat=,rss="],{timeout:PS_TIMEOUT_MS});return stdout.split(`
|
|
3
|
+
`).flatMap(line=>{let[pid,ppid,state,rss]=line.trim().split(/\s+/u);if(pid===void 0||ppid===void 0||state===void 0||rss===void 0)return[];let row={pid:Number(pid),ppid:Number(ppid),state,rssKb:Number(rss)};return Number.isInteger(row.pid)&&Number.isInteger(row.ppid)&&Number.isFinite(row.rssKb)?[row]:[]})}catch{return}}function descendants(rows,root){let tree=rows.filter(row=>row.pid===root);if(tree.length===0)return tree;for(let index=0;index<tree.length;index++){let parent=tree[index].pid;for(let row of rows)row.ppid===parent&&!tree.includes(row)&&tree.push(row)}return tree}async function openDescriptors(tree){let total=0,counted=!1;for(let row of tree){let entries=await readdir(`/proc/${row.pid}/fd`).catch(()=>{});entries!==void 0&&(counted=!0,total+=entries.length)}return counted?total:null}var STATUS_PROBE_TIMEOUT_MS=1e4;async function readHostStatus(options){let paths=createHostDaemonPaths({socket:options.socket,...options.agentDir?{agentDir:options.agentDir}:{}}),host=await probeProtocolInfo(options.socket,STATUS_PROBE_TIMEOUT_MS),generations=await readGenerations(paths),current=generations.find(row=>row.current),metrics=current?await readHostProcessMetrics(current.pid):UNOBSERVED_METRICS;return{reachable:host!==void 0,socket:options.socket,pid:current?.pid??null,instanceId:host?.instanceId??current?.instanceId??null,generation:host?.generation??current?.generation??null,engineVersion:host?.engineVersion??current?.engineVersion??null,capabilities:host?.capabilities??[],launchProfile:host?.launch_profile??null,sessions:await readSessionCounts(options.socket,options.includeWorkers===!0),zombies:metrics.zombies,rss_mb:metrics.rss_mb,open_fds:metrics.open_fds,env_keys:await readDaemonEnvKeys(paths),generations}}async function readSessionCounts(socket,includeWorkers){let reply=await requestOnSocket(socket,{type:"list_sessions",...includeWorkers?{include_workers:!0}:{}},STATUS_PROBE_TIMEOUT_MS),rows=sessionRows(reply),attached=rows.filter(row=>row.attachments>0).length,retained=rows.length-attached;return{total:rows.length,interactive:rows.filter(row=>row.kind!=="worker").length,worker:rows.filter(row=>row.kind==="worker").length,retained,foreign_attached:attached,foreign_retained:retained}}function hostSummary(host){return host===void 0?null:{protocolVersion:host.protocolVersion,serverVersion:host.serverVersion,capabilities:host.capabilities,instanceId:host.instanceId??null,generation:host.generation??null,engineVersion:host.engineVersion??null,launchProfileId:host.launch_profile?.profile_id??null}}var UNOBSERVED_METRICS={rss_mb:null,open_fds:null,zombies:null};function sessionRows(reply){return!isRecord2(reply)||!Array.isArray(reply.sessions)?[]:reply.sessions.flatMap(entry=>isRecord2(entry)?[{kind:typeof entry.kind=="string"?entry.kind:"interactive",attachments:typeof entry.attachments=="number"?entry.attachments:0}]:[])}async function readGenerations(paths){let pointer=parseJson(await readFileOrUndefined(paths.pointerFile)),currentId=typeof pointer?.instance_id=="string"?pointer.instance_id:void 0,entries=await readdir2(paths.generationsDir).catch(()=>[]),rows=[];for(let instanceId of entries){let record=parseJson(await readFileOrUndefined(generationPaths(paths,instanceId).pidFile));typeof record?.pid=="number"&&rows.push({instanceId,generation:typeof record.generation=="number"?record.generation:0,pid:record.pid,engineVersion:typeof record.engineVersion=="string"?record.engineVersion:null,current:instanceId===currentId,alive:processIsLive(record.pid)})}return rows.sort((left,right)=>left.generation-right.generation)}function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var HOST_EXIT_OK=0,HOST_EXIT_ERROR=1,HOST_EXIT_USAGE=2,HOST_EXIT_REFUSED=3,HOST_EXIT_FALLBACK=4,PROBE_TIMEOUT_MS=1e4;async function runHostRequest(request){switch(request.action){case"ensure":return ensureOutcome(request.target,request.spec,request.policy);case"status":return statusOutcome(request.target,request.includeWorkers);case"stop":return stopOutcome(request.target,request.drain,request.force);case"handoff":return handoffOutcome(request.target,request.spec);default:return assertNever(request)}}async function ensureOutcome(target,spec,policy){let before=await probeProtocolInfo(target.socket,PROBE_TIMEOUT_MS);if(policy==="fallback"){let decision=decideHostAction(decisionClient(),before,"fallback");if(decision.action==="fallback")return refusal("fallback",before,{reason:decision.reason,socket:target.socket})}let ensured;try{ensured=await ensureHost({socket:target.socket,agentDir:target.agentDir,hostArgs:spec.hostArgs,env:daemonEnvOverrides(process.env,spec.env),policy:spec.policy,upgrade:policy==="upgrade"?"if-engine-differs":"never"})}catch(error){if(!(error instanceof HostEnsureRefusedError))throw error;return refusal("refuse",before,{reason:error.reason,socket:target.socket})}let host=await probeProtocolInfo(target.socket,PROBE_TIMEOUT_MS);return ensured.reused||await recordEnvScope(target,spec),{exitCode:HOST_EXIT_OK,payload:{action:ensureAction(ensured.reused,before,host),...identityPayload(target.socket,ensured.pid,host),reused:ensured.reused}}}function ensureAction(reused,before,after){return reused?"reuse":before!==void 0&&before.instanceId!==after?.instanceId?"handoff":"start"}async function statusOutcome(target,includeWorkers){let status=await readHostStatus({socket:target.socket,agentDir:target.agentDir,includeWorkers});return{exitCode:status.reachable?HOST_EXIT_OK:HOST_EXIT_REFUSED,payload:{...status}}}async function stopOutcome(target,drain,force){let host=await probeProtocolInfo(target.socket,PROBE_TIMEOUT_MS),sessions=await readSessionCounts(target.socket,!0),occupied=sessions.foreign_attached+sessions.foreign_retained>0;if(!drain&&!force&&occupied)return refusal("refuse",host,{reason:"sessions_live",socket:target.socket,sessions});let result=await stopHost({socket:target.socket,agentDir:target.agentDir,drain,force});return result.action==="refuse"?refusal("refuse",host,{reason:result.reason,socket:target.socket,sessions}):{exitCode:HOST_EXIT_OK,payload:{action:result.action,socket:target.socket,pid:result.pid,sessions}}}async function handoffOutcome(target,spec){let before=await probeProtocolInfo(target.socket,PROBE_TIMEOUT_MS),result=await handoffHost({socket:target.socket,agentDir:target.agentDir,hostArgs:spec.hostArgs,env:daemonEnvOverrides(process.env,spec.env),policy:spec.policy});if(result.action==="refuse")return refusal("refuse",before,{reason:upgradeRefusal(result.reason),socket:target.socket,detail:result.reason,upgradeable:result.upgradeable});await recordEnvScope(target,spec);let host=await probeProtocolInfo(target.socket,PROBE_TIMEOUT_MS);return{exitCode:HOST_EXIT_OK,payload:{action:"handoff",...identityPayload(target.socket,result.pid,host),reused:!1}}}function upgradeRefusal(reason){return reason==="handoff_unsupported"?"upgrade_unsupported":reason}function identityPayload(socket,pid,host){return{socket,pid,instanceId:host?.instanceId??null,generation:host?.generation??null,engineVersion:host?.engineVersion??null,engineOrdinal:host?.engineOrdinal??null,capabilities:host?.capabilities??[],launchProfileId:host?.launch_profile?.profile_id??null,upgradeable:decideHostAction(decisionClient(),host,"upgrade").upgradeable}}function refusal(kind,host,body){return{exitCode:kind==="refuse"?HOST_EXIT_REFUSED:HOST_EXIT_FALLBACK,payload:{action:kind,...body,host:hostSummary(host)}}}async function recordEnvScope(target,spec){let paths=createHostDaemonPaths({socket:target.socket,agentDir:target.agentDir});await writeDaemonEnvKeys(paths,daemonEnvKeys(process.env,spec.env))}function decisionClient(){return{protocolVersion:HOST_PROTOCOL_VERSION,requiredCapabilities:REQUIRED_HOST_CAPABILITIES,identity:engineBuildIdentity(),startedByUs:!1,platform:process.platform}}function assertNever(value){throw new Error(`unreachable host request: ${JSON.stringify(value)}`)}var SUBCOMMANDS=["ensure","status","stop","handoff"],POLICIES=["upgrade","fallback","never"],USAGE=`usage: ${APP_NAME} host <ensure|status|stop|handoff> [options]
|
|
4
|
+
|
|
5
|
+
ensure [--launch-spec <file>] [--policy upgrade|fallback|never] [--socket <path>]
|
|
6
|
+
status [--include-workers] [--socket <path>]
|
|
7
|
+
stop [--drain] [--force] [--socket <path>]
|
|
8
|
+
handoff [--launch-spec <file>] [--socket <path>]
|
|
9
|
+
|
|
10
|
+
--json accepted for symmetry; the answer is always one JSON line on stdout`;async function runHostCommand(args){let parsed=parseHostArgs(args);if(typeof parsed=="string")return process.stderr.write(`${parsed}
|
|
11
|
+
${USAGE}
|
|
12
|
+
`),HOST_EXIT_USAGE;try{let outcome=await runHostRequest(await hostRequest(parsed));return emit(outcome.payload),outcome.exitCode}catch(error){return error instanceof HostLaunchSpecError?(emit({action:"error",reason:error.reason,detail:error.detail}),HOST_EXIT_USAGE):(emit({action:"error",reason:"host_error",detail:error instanceof Error?error.message:String(error)}),HOST_EXIT_ERROR)}}async function hostRequest(parsed){let agentDir=getAgentDir(),target={socket:resolveHostSocket(parsed.socket,agentDir),agentDir};switch(parsed.subcommand){case"ensure":return{action:"ensure",target,spec:await launchSpec(parsed.specPath),policy:parsed.policy};case"status":return{action:"status",target,includeWorkers:parsed.includeWorkers};case"stop":return{action:"stop",target,drain:parsed.drain,force:parsed.force};case"handoff":return{action:"handoff",target,spec:await launchSpec(parsed.specPath)};default:return assertNever2(parsed.subcommand)}}function launchSpec(specPath){return specPath===void 0?DEFAULT_HOST_LAUNCH_SPEC:loadHostLaunchSpec(specPath)}function resolveHostSocket(explicit,agentDir){return explicit??envValue("RPC_SOCKET")??resolve2(agentDir,"rpc","rpc.sock")}function parseHostArgs(args){let[subcommand,...rest]=args;if(subcommand===void 0||!isSubcommand(subcommand))return`Error: unknown host command "${subcommand??""}".`;let specPath,policy="upgrade",socket,includeWorkers=!1,drain=!1,force=!1;for(let index=0;index<rest.length;index++){let flag=rest[index],value=rest[index+1];if(flag!=="--json")if(flag==="--include-workers")includeWorkers=!0;else if(flag==="--drain")drain=!0;else if(flag==="--force")force=!0;else if(flag==="--launch-spec"&&value!==void 0)specPath=value,index++;else if(flag==="--socket"&&value!==void 0)socket=value,index++;else if(flag==="--policy"&&value!==void 0&&isPolicy(value))policy=value,index++;else return`Error: unknown option "${flag}" for "${APP_NAME} host ${subcommand}".`}return{subcommand,...specPath!==void 0&&{specPath},policy,...socket!==void 0&&{socket},includeWorkers,drain,force}}function emit(payload){writeSync(1,`${JSON.stringify(payload)}
|
|
13
|
+
`)}function isSubcommand(value){return SUBCOMMANDS.includes(value)}function isPolicy(value){return POLICIES.includes(value)}function assertNever2(value){throw new Error(`unreachable host subcommand: ${JSON.stringify(value)}`)}export{HostLaunchSpecError,DEFAULT_HOST_LAUNCH_SPEC,loadHostLaunchSpec,parseHostLaunchSpec,readHostStatus,HOST_EXIT_OK,HOST_EXIT_ERROR,HOST_EXIT_USAGE,HOST_EXIT_REFUSED,HOST_EXIT_FALLBACK,runHostRequest,runHostCommand,parseHostArgs};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
var COMBINER_KEYS=["anyOf","oneOf","allOf"],SCHEMA_MAP_KEYS=["properties","patternProperties","$defs","definitions"],SCHEMA_SINGLE_KEYS=["items","additionalProperties","contains","propertyNames","if","then","else","not"];function isJsonObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function isScalarType(type){return type==="string"||type==="number"||type==="integer"||type==="boolean"}function moveTypeIntoCombinerBranches(node){if(!("type"in node))return;let parentType=node.type;for(let combiner of COMBINER_KEYS){let branches=node[combiner];if(Array.isArray(branches))for(let branch of branches)isJsonObject(branch)&&!("type"in branch)&&(branch.type=parentType)}delete node.type}function collapseConstUnion(node){let branches=node.anyOf;if(!Array.isArray(branches)||branches.length<2||"type"in node)return;let values=[],sharedType;for(let branch of branches){if(!isJsonObject(branch)||!Object.hasOwn(branch,"const"))return;let keys=Object.keys(branch);if(keys.length!==2||!keys.includes("type"))return;let branchType=branch.type;if(!isScalarType(branchType))return;if(sharedType===void 0)sharedType=branchType;else if(sharedType!==branchType)return;values.push(branch.const)}sharedType===void 0||values.length===0||(delete node.anyOf,node.type=sharedType,node.enum=values)}function mergeRootObjectUnion(schema){let branches=Array.isArray(schema.anyOf)?schema.anyOf:Array.isArray(schema.oneOf)?schema.oneOf:void 0;if(branches===void 0||branches.length===0||schema.properties!==void 0&&!isJsonObject(schema.properties)||schema.required!==void 0&&!Array.isArray(schema.required))return;let objectBranches=[];for(let branch of branches){if(!isJsonObject(branch)||branch.type!=="object"&&branch.type!==void 0||branch.properties!==void 0&&!isJsonObject(branch.properties)||branch.required!==void 0&&!Array.isArray(branch.required))return;objectBranches.push(branch)}let properties=isJsonObject(schema.properties)?{...schema.properties}:{};for(let branch of objectBranches)if(isJsonObject(branch.properties))for(let[name,propertySchema]of Object.entries(branch.properties)){let existing=properties[name];properties[name]=existing===void 0||JSON.stringify(existing)===JSON.stringify(propertySchema)?propertySchema:{anyOf:[existing,propertySchema]}}let rootRequired=Array.isArray(schema.required)?schema.required.filter(name=>typeof name=="string"):[],branchRequiredSets=objectBranches.map(branch=>new Set(Array.isArray(branch.required)?branch.required.filter(name=>typeof name=="string"):[])),firstBranchRequired=branchRequiredSets[0],commonBranchRequired=firstBranchRequired?[...firstBranchRequired].filter(name=>branchRequiredSets.every(names=>names.has(name))):[],required=[...new Set([...rootRequired,...commonBranchRequired])],{anyOf:_anyOf,oneOf:_oneOf,...rest}=schema;return{...rest,type:"object",properties,...required.length>0?{required}:{}}}function normalizeNode(node,isRoot=!1){if(Array.isArray(node))return node.map(child=>normalizeNode(child));if(!isJsonObject(node))return node;delete node.optional,COMBINER_KEYS.some(key=>Array.isArray(node[key]))&&!isRoot&&moveTypeIntoCombinerBranches(node);for(let combiner of COMBINER_KEYS){let branches=node[combiner];Array.isArray(branches)&&(node[combiner]=branches.map(branch=>normalizeNode(branch)))}Array.isArray(node.anyOf)&&collapseConstUnion(node);for(let key of SCHEMA_SINGLE_KEYS)Object.hasOwn(node,key)&&(node[key]=normalizeNode(node[key]));for(let key of SCHEMA_MAP_KEYS){let map=node[key];if(isJsonObject(map))for(let name of Object.keys(map))map[name]=normalizeNode(map[name])}return node}function normalizeToolParametersForOpenAICompat(schema){let normalized=normalizeNode(structuredClone(schema),!0);return ensureRootObjectSchema(normalized)}function ensureRootObjectSchema(schema){let merged=mergeRootObjectUnion(schema);return merged||(schema.type!==void 0||COMBINER_KEYS.some(key=>Array.isArray(schema[key]))?schema:{...schema,type:"object"})}function normalizeToolParametersForMoonshot(schema){return stripMoonshotAnnotations(normalizeToolParametersForOpenAICompat(schema))}function resolveRootObjectSchema(schema){return mergeRootObjectUnion(structuredClone(schema))??schema}function stripMoonshotAnnotations(node){if(Array.isArray(node))return node.map(child=>stripMoonshotAnnotations(child));if(!isJsonObject(node))return node;delete node.format,delete node.examples,delete node.readOnly,delete node.writeOnly,delete node.deprecated,delete node.$schema,delete node.$id;for(let combiner of COMBINER_KEYS){let branches=node[combiner];Array.isArray(branches)&&(node[combiner]=branches.map(branch=>stripMoonshotAnnotations(branch)))}for(let key of SCHEMA_SINGLE_KEYS)Object.hasOwn(node,key)&&(node[key]=stripMoonshotAnnotations(node[key]));for(let key of SCHEMA_MAP_KEYS){let map=node[key];if(isJsonObject(map))for(let name of Object.keys(map))map[name]=stripMoonshotAnnotations(map[name])}return node}export{normalizeToolParametersForOpenAICompat,normalizeToolParametersForMoonshot,resolveRootObjectSchema};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{HOST_INSTANCE_ID_ENV,MAX_RPC_LINE_CHARACTERS,attachJsonlLineReader,engineBuildIdentity}from"./chunk-
|
|
2
|
+
import{HOST_INSTANCE_ID_ENV,MAX_RPC_LINE_CHARACTERS,attachJsonlLineReader,engineBuildIdentity}from"./chunk-HSH674C6.js";import{envValue,getAgentDir,isBunBinary,isBundledNode}from"./chunk-H5RK2RYZ.js";import{spawn}from"node:child_process";import{randomUUID}from"node:crypto";import{existsSync,readFileSync,realpathSync,writeSync}from"node:fs";import{access,chmod as chmod3,mkdir as mkdir4,readFile as readFile4,rename as rename3,rm as rm3,unlink as unlink2,writeFile as writeFile5}from"node:fs/promises";import{createConnection,createServer}from"node:net";import{tmpdir}from"node:os";import{dirname as dirname3,extname,join as join2,resolve}from"node:path";import{fileURLToPath}from"node:url";import{execFile}from"node:child_process";function parseDaemonPidFile(text){let parsed;try{parsed=JSON.parse(text)}catch(error){if(error instanceof SyntaxError)return;throw error}let unguarded=parsed!==null&&isRecord(parsed)&&parsed.processStartTime===null;if(!(!isRecord(parsed)||typeof parsed.pid!="number"||!unguarded&&typeof parsed.processStartTime!="string")&&!(!Number.isInteger(parsed.pid)||parsed.pid<=0)){if(unguarded)return{pid:parsed.pid,processStartTime:null};if(!(typeof parsed.processStartTime!="string"||parsed.processStartTime.trim()===""))return{pid:parsed.pid,processStartTime:parsed.processStartTime}}}var ProcessIdentityUnreadableError=class extends Error{constructor(pid,attempts,cause){super(`process identity for live pid ${pid} stayed unreadable after ${attempts} probe attempt(s): ${cause instanceof Error?cause.message:String(cause)}`),this.name="ProcessIdentityUnreadableError",this.pid=pid,this.attempts=attempts,this.cause=cause}};async function processMatchesPidFile(pidFile,readStartTime=readProcessStartTime,isLive=processIsLive,retry={}){if(pidFile.processStartTime===null){if(!isLive(pidFile.pid))return!1;throw new ProcessIdentityUnreadableError(pidFile.pid,0,new Error("pidfile carries no process identity guard"))}let attempts=Math.max(1,retry.attempts??5),delayMs=retry.delayMs??200,lastError;for(let attempt=1;attempt<=attempts;attempt++)try{let current=await readStartTime(pidFile.pid);if(current!==void 0)return current===pidFile.processStartTime;if(!isLive(pidFile.pid))return!1;lastError=new Error("process identity probe returned no identity for a live process"),attempt<attempts&&await new Promise(resolve2=>setTimeout(resolve2,delayMs))}catch(error){if(!isLive(pidFile.pid))return!1;lastError=error,attempt<attempts&&await new Promise(resolve2=>setTimeout(resolve2,delayMs))}throw new ProcessIdentityUnreadableError(pidFile.pid,attempts,lastError)}async function readProcessIdentity(pid,platform=process.platform,timeoutMs,isLive=processIsLive){let command=platform==="win32"?{executable:"powershell.exe",args:["-NoProfile","-NonInteractive","-Command",`$process = Get-CimInstance Win32_Process -Filter "ProcessId=${String(pid)}" -ErrorAction Stop; if ($null -eq $process) { Write-Output '__SENPI_ABSENT__'; exit 0 }; $process.CreationDate.ToFileTimeUtc().ToString("D", [Globalization.CultureInfo]::InvariantCulture)`]}:{executable:"ps",args:["-o","lstart=","-p",String(pid)]};return new Promise(resolve2=>{let effectiveTimeoutMs=timeoutMs??(platform==="win32"?1e3:void 0);execFile(command.executable,command.args,{windowsHide:!0,...effectiveTimeoutMs===void 0?{}:{timeout:effectiveTimeoutMs}},(error,stdout)=>{if(error){let code="code"in error?error.code:void 0;if(platform!=="win32"&&code===1||!isLive(pid)){resolve2({kind:"absent"});return}resolve2({kind:"error",error});return}let output=stdout.trim();if(output==="__SENPI_ABSENT__")return resolve2({kind:"absent"});if(!output||platform==="win32"&&!/^\d+$/.test(output))return resolve2({kind:"error",error:new Error("invalid process identity output")});resolve2({kind:"present",identity:output})}).once("error",error=>resolve2({kind:"error",error}))})}async function stopValidatedPid(pidFile,signal){if(await processMatchesPidFile(pidFile)){try{process.kill(pidFile.pid,signal)}catch(error){if(!isNodeErrorCode(error,"ESRCH"))throw error}signal==="SIGTERM"&&await waitForGone(pidFile,1e4),signal==="SIGKILL"&&await waitForGone(pidFile,2e3)}}async function waitForGone(pidFile,timeoutMs){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(!await processMatchesPidFile(pidFile))return!0;await delay(100)}return!await processMatchesPidFile(pidFile)}async function readProcessStartTime(pid,platform=process.platform,timeoutMs){return readProcessIdentity(pid,platform,timeoutMs).then(result=>{if(result.kind==="error")throw result.error;return result.kind==="present"?result.identity:void 0})}function processIsLive(pid){try{return process.kill(pid,0),!0}catch(error){if(isNodeErrorCode(error,"ESRCH"))return!1;if(isNodeErrorCode(error,"EPERM"))return!0;throw error}}async function waitForStartTime(pid,timeoutMs,readStartTime=readProcessStartTime,isLive=processIsLive){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){let startTime;try{startTime=await readStartTime(pid)}catch{}if(startTime)return startTime;await delay(20)}if(!isLive(pid))throw new Error(`spawned daemon pid ${pid} had no process start time`)}function delay(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}import{createHash}from"node:crypto";import{chmod,mkdir,writeFile}from"node:fs/promises";import{basename,join,win32}from"node:path";var HOST_DAEMON_LAYOUT=2,HOST_DAEMON_DIR_ENV="SENPI_RPC_HOST_DAEMON_DIR",DIRECTORY_MODE=448,HOST_STATE_FILE_MODE=384;function daemonDirectoryName(socket,platform=process.platform){let canonical=platform==="win32"?win32.normalize(socket).toLowerCase():socket;return createHash("sha256").update(canonical,"utf8").digest("hex").slice(0,16)}function createHostDaemonPaths(target){let flatDir=join(target.agentDir??getAgentDir(),"rpc-host-daemon"),dir=join(flatDir,daemonDirectoryName(target.socket));return{flatDir,layoutMarker:join(flatDir,"layout.json"),legacyPidFile:join(flatDir,"host.pid"),dir,pointerFile:join(dir,"host.pid"),lockFile:join(dir,"daemon.lock"),settingsFile:join(dir,"settings.json"),stderrLog:join(dir,"stderr.log"),generationsDir:join(dir,"generations"),reservationsDir:join(dir,"reservations")}}function generationPaths(paths,instanceId){let dir=join(paths.generationsDir,instanceId);return{dir,relativeDir:`generations/${instanceId}`,pidFile:join(dir,"host.pid"),settingsFile:join(dir,"settings.json"),scratchDir:join(dir,"scratch")}}var HostDaemonStateError=class extends Error{constructor(path,cause){super(`RPC daemon state directory ${path} is not usable: ${cause instanceof Error?cause.message:cause}`,{cause}),this.name="HostDaemonStateError",this.path=path}};async function createDaemonDirectories(paths){try{await mkdir(paths.flatDir,{recursive:!0,mode:DIRECTORY_MODE});for(let directory of[paths.dir,paths.generationsDir,paths.reservationsDir])await mkdir(directory,{recursive:!0,mode:DIRECTORY_MODE}),await chmod(directory,DIRECTORY_MODE);await writeFile(paths.layoutMarker,`${JSON.stringify({layout:HOST_DAEMON_LAYOUT,dir:basename(paths.dir)})}
|
|
3
3
|
`,{mode:HOST_STATE_FILE_MODE})}catch(cause){throw new HostDaemonStateError(paths.dir,cause)}}async function createGenerationDirectory(generation){try{await mkdir(generation.scratchDir,{recursive:!0,mode:DIRECTORY_MODE}),await chmod(generation.dir,DIRECTORY_MODE)}catch(cause){throw new HostDaemonStateError(generation.dir,cause)}}import{rename,rm}from"node:fs/promises";import{readFile,writeFile as writeFile2}from"node:fs/promises";async function writeHostSettings(paths,settings){let generation=generationPaths(paths,settings.instanceId);await createGenerationDirectory(generation),await writeStateFile(paths.settingsFile,settings),await writeStateFile(generation.settingsFile,settings)}async function readHostSettings(paths){let parsed=parseJson(await readFileOrUndefined(paths.settingsFile));if(parsed)return{...typeof parsed.coldStart=="string"&&{coldStart:parsed.coldStart},...typeof parsed.idleExitMs=="number"&&{idleExitMs:parsed.idleExitMs}}}async function writeStateFile(path,content){try{await writeFile2(path,`${JSON.stringify(content)}
|
|
4
4
|
`,{mode:HOST_STATE_FILE_MODE})}catch(cause){throw new HostDaemonStateError(path,cause)}}async function readFileOrUndefined(path){try{return await readFile(path,"utf8")}catch(error){if(isNodeErrorCode2(error,"ENOENT")||isNodeErrorCode2(error,"ENOTDIR"))return;throw error}}function parseJson(text){if(text!==void 0)try{let parsed=JSON.parse(text);return isRecord2(parsed)?parsed:void 0}catch{return}}function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function isNodeErrorCode2(error,code){return error instanceof Error&&"code"in error&&error.code===code}async function readHostRegistration(paths){let pointer=parseJson(await readFileOrUndefined(paths.pointerFile));if(!pointer||typeof pointer.instance_id!="string")return;let text=await readFileOrUndefined(generationPaths(paths,pointer.instance_id).pidFile),record=text===void 0?void 0:parseDaemonPidFile(text);if(record===void 0)return;let parsed=parseJson(text)??{},writer=parseWriter(parsed);return{record,...writer&&{writer},...typeof parsed.socket=="string"&&{socket:parsed.socket},instanceId:pointer.instance_id,generation:typeof parsed.generation=="number"?parsed.generation:0}}async function writeHostRegistration(paths,registration){let generation=generationPaths(paths,registration.instanceId),writer={pid:process.pid,startTime:await thisProcessStartTime()},build=engineBuildIdentity();await createGenerationDirectory(generation),await writeStateFile(generation.pidFile,{...registration.record,instance_id:registration.instanceId,generation:registration.generation,engineVersion:build.text,engineOrdinal:build.ordinal,launchProfileId:registration.launchProfileId,socket:registration.socket,writer}),await writeStateFile(`${paths.pointerFile}.${process.pid}.tmp`,{layout:HOST_DAEMON_LAYOUT,instance_id:registration.instanceId,generation_dir:generation.relativeDir,writer}),await rename(`${paths.pointerFile}.${process.pid}.tmp`,paths.pointerFile)}async function clearHostRegistration(paths){let pointer=parseJson(await readFileOrUndefined(paths.pointerFile));typeof pointer?.instance_id=="string"&&await rm(generationPaths(paths,pointer.instance_id).dir,{recursive:!0,force:!0}),await rm(paths.pointerFile,{force:!0}),await rm(paths.settingsFile,{force:!0})}async function releaseGeneration(paths,owner){let generation=generationPaths(paths,owner.instanceId),record=parseDaemonPidFile(await readFileOrUndefined(generation.pidFile)??"");if(record!==void 0&&record.pid!==owner.pid)return;parseJson(await readFileOrUndefined(paths.pointerFile))?.instance_id===owner.instanceId&&(await rm(paths.pointerFile,{force:!0}),await rm(paths.settingsFile,{force:!0})),await rm(generation.dir,{recursive:!0,force:!0})}async function readLegacyHostRecord(paths){let text=await readFileOrUndefined(paths.legacyPidFile);return text===void 0?void 0:parseDaemonPidFile(text)}async function provenOwner(registered,socket){let record=registered?.record;if(!record||record.processStartTime===null||registered?.socket!==void 0&®istered.socket!==socket)return;let identity={pid:record.pid,processStartTime:record.processStartTime};return await processMatchesPidFile(identity,readProcessStartTime).catch(()=>!1)?{...identity,instanceId:registered.instanceId}:void 0}async function legacyHostIsLive(paths,probe){let record=await readLegacyHostRecord(paths);if(record===void 0)return!1;try{return await processMatchesPidFile(record,probe)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!0;throw error}}async function writtenByThisProcess(writer){return writer===void 0||writer.pid!==process.pid||writer.startTime===null?!1:writer.startTime===await thisProcessStartTime()}var selfStartTime;function thisProcessStartTime(){return selfStartTime??=readProcessStartTime(process.pid).then(value=>value??null,()=>null),selfStartTime}function parseWriter(parsed){if(!isRecord2(parsed.writer)||typeof parsed.writer.pid!="number")return;let{pid,startTime}=parsed.writer;return{pid,startTime:typeof startTime=="string"?startTime:null}}import{rmSync}from"node:fs";import{rm as rm2}from"node:fs/promises";import{Socket}from"node:net";var HOST_WATCH_FD_ENV="SENPI_RPC_HOST_WATCH_FD",HOST_SCRATCH_DIR_ENV="SENPI_RPC_HOST_SCRATCH_DIR",HOST_PUBLIC_SOCKET_ENV="SENPI_RPC_HOST_PUBLIC_SOCKET",HOST_WATCH_PPID_ENV="SENPI_RPC_HOST_WATCH_PPID",HOST_WATCH_PPID_INTERVAL_MS=250,HOST_CLEANUP_PATHS_ENV="SENPI_RPC_HOST_CLEANUP_PATHS";function parsePositiveInteger(value){if(value===void 0||!/^\d+$/.test(value.trim()))return;let parsed=Number(value.trim());return Number.isSafeInteger(parsed)&&parsed>0?parsed:void 0}function readHostWatchdogConfig(env=process.env){let fd=parsePositiveInteger(env[HOST_WATCH_FD_ENV]),ppid=parsePositiveInteger(env[HOST_WATCH_PPID_ENV]);if(fd===void 0&&ppid===void 0)return;let scratchDir=env[HOST_SCRATCH_DIR_ENV],cleanupPaths=env[HOST_CLEANUP_PATHS_ENV]?.split(`
|
|
5
5
|
`).filter(Boolean),publicSocket=env[HOST_PUBLIC_SOCKET_ENV];return{fd,ppid,scratchDir:scratchDir===void 0||scratchDir===""?void 0:scratchDir,cleanupPaths,publicSocket:publicSocket===""?void 0:publicSocket}}function readHostWatchdogConfigFromBrandEnv(){return readHostWatchdogConfig({[HOST_WATCH_FD_ENV]:process.env[HOST_WATCH_FD_ENV]??envValue("RPC_HOST_WATCH_FD"),[HOST_WATCH_PPID_ENV]:process.env[HOST_WATCH_PPID_ENV]??envValue("RPC_HOST_WATCH_PPID"),[HOST_SCRATCH_DIR_ENV]:process.env[HOST_SCRATCH_DIR_ENV]??envValue("RPC_HOST_SCRATCH_DIR"),[HOST_PUBLIC_SOCKET_ENV]:process.env[HOST_PUBLIC_SOCKET_ENV]??envValue("RPC_HOST_PUBLIC_SOCKET"),[HOST_CLEANUP_PATHS_ENV]:process.env[HOST_CLEANUP_PATHS_ENV]??envValue("RPC_HOST_CLEANUP_PATHS")})}function armHostWatchdog(config,onSupervisorGone){if(!config)return()=>{};let fire=reason=>{disarm();let captured=captureBeforeCleanup(config);if(process.platform==="win32"){let cleanup=captured.then(()=>cleanupWatchdogPaths(config));onSupervisorGone(reason,cleanup)}else captured.then(()=>cleanupWatchdogPaths(config)).finally(()=>onSupervisorGone(reason))},disarmers=[],disarm=()=>{for(let stop of disarmers.splice(0))stop()};return config.fd!==void 0&&disarmers.push(watchFdForEof(config.fd,fire)),config.ppid!==void 0&&disarmers.push(watchPpid(config.ppid,fire)),disarm}function watchFdForEof(fd,fire){let stream,streamFailed=!1,fired=!1,fireOnce=reason=>{fired||(fired=!0,fire(reason))};try{stream=new Socket({fd,readable:!0,writable:!1})}catch{return()=>{}}stream.resume();let onEnd=()=>fireOnce(`supervisor pipe fd ${fd} closed`),onClose=()=>{streamFailed||onEnd()},onError=()=>{streamFailed=!0};return stream.once("end",onEnd),process.platform==="win32"&&stream.once("close",onClose),stream.once("error",onError),()=>{stream.off("end",onEnd),stream.off("close",onClose),stream.off("error",onError),stream.destroy()}}function watchPpid(supervisorPid,fire){if(supervisorPid===process.pid)return()=>{};let timer=setInterval(()=>{if(!processAlive(supervisorPid)){fire(`supervisor pid ${supervisorPid} is gone (ppid=${process.ppid})`);return}process.ppid!==supervisorPid&&fire(`supervisor pid ${supervisorPid} is gone (ppid=${process.ppid})`)},HOST_WATCH_PPID_INTERVAL_MS);return timer.unref?.(),()=>clearInterval(timer)}function processAlive(pid){try{return process.kill(pid,0),!0}catch(cause){return cause instanceof Error&&"code"in cause&&cause.code!=="ESRCH"}}function captureBeforeCleanup(config){return config.beforeCleanup===void 0?Promise.resolve():Promise.resolve().then(config.beforeCleanup).catch(()=>{})}async function cleanupWatchdogPaths(config){let paths=[...config.cleanupPaths??[],...config.scratchDir?[config.scratchDir]:[]];if(process.platform==="win32"){for(let path of paths)try{rmSync(path,{recursive:!0,force:!0,maxRetries:10,retryDelay:100})}catch{}return}await Promise.all(paths.map(async path=>{try{await rm2(path,{recursive:!0,force:!0})}catch{}}))}import{mkdir as mkdir2,readFile as readFile2,rename as rename2,stat,unlink,writeFile as writeFile3}from"node:fs/promises";import{dirname}from"node:path";var PUBLIC_SOCKET_IDENTITY_FILE="public-socket.owner",SOCKET_IDENTITY_WAIT_MS=3e4,MAX_SOCKET_PATH_BYTES=103;function generationBindPath(publicSocket,generation){return`${publicSocket}.next-${generation}`}function sameSocketIdentity(a,b){return a.dev===b.dev&&a.ino===b.ino}async function statSocketIdentity(socketPath){try{let{dev,ino}=await stat(socketPath);return{dev,ino}}catch(cause){if(isNodeErrorCode3(cause,"ENOENT"))return;throw cause}}async function writeSocketIdentityFile(path,identity){await mkdir2(dirname(path),{recursive:!0,mode:448});let tmpPath=`${path}.${process.pid}.tmp`;await writeFile3(tmpPath,`${JSON.stringify(identity)}
|
|
6
|
-
`,{mode:384}),await rename2(tmpPath,path)}async function readSocketIdentityFile(path){let raw;try{raw=await readFile2(path,"utf8")}catch(cause){if(isNodeErrorCode3(cause,"ENOENT"))return;throw cause}let parsed;try{parsed=JSON.parse(raw)}catch{return}if(!(typeof parsed!="object"||parsed===null||typeof parsed.dev!="number"||typeof parsed.ino!="number"))return{dev:parsed.dev,ino:parsed.ino}}async function waitForSocketIdentityFile(path,timeoutMs=SOCKET_IDENTITY_WAIT_MS,intervalMs=25){let deadline=Date.now()+timeoutMs;for(;;){let identity=await readSocketIdentityFile(path);if(identity!==void 0)return identity;if(Date.now()>=deadline)return;await delayUnrefed(intervalMs)}}async function unlinkOwnedSocket(socketPath,identity,log){if(process.platform==="win32"||socketPath.startsWith("\0"))return;let current;try{current=await statSocketIdentity(socketPath)}catch(cause){log(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);return}if(current!==void 0){if(identity===void 0){log(`socket path ${socketPath} ownership unknown; leaving it`);return}if(!sameSocketIdentity(current,identity)){log(`socket path ${socketPath} now owned by another host; leaving it`);return}try{await unlink(socketPath)}catch(cause){isNodeErrorCode3(cause,"ENOENT")||log(`socket ${socketPath} removal failed (${errorMessage(cause)})`)}}}async function shieldSocketDuringClose(socketPath,close){if(process.platform==="win32"||socketPath.startsWith("\0"))return close();let shieldPath=`${socketPath}.shield-${process.pid}`,shielded=!1;try{await statSocketIdentity(socketPath)!==void 0&&(await rename2(socketPath,shieldPath),shielded=!0)}catch{}try{await close()}finally{if(shielded)try{await rename2(shieldPath,socketPath)}catch{}}}function delayUnrefed(ms){return new Promise(resolve2=>{setTimeout(resolve2,ms).unref?.()})}function errorMessage(cause){return cause instanceof Error?cause.message:String(cause)}function isNodeErrorCode3(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}import{createHash as createHash2,randomBytes,timingSafeEqual}from"node:crypto";import{chmod as chmod2,mkdir as mkdir3,readFile as readFile3,writeFile as writeFile4}from"node:fs/promises";import{dirname as dirname2,win32 as win322}from"node:path";var WINDOWS_PIPE_PREFIX="\\\\.\\pipe\\senpi-rpc-",SOCKET_SECRET_BYTES=32,SOCKET_SECRET_SUFFIX=".secret",SOCKET_SECRET_FILE_ENV="SENPI_RPC_SOCKET_SECRET_FILE",SOCKET_HANDSHAKE_TIMEOUT_MS=2e3;function socketSecretPath(logicalPath){if(logicalPath.toLowerCase().startsWith("\\\\.\\pipe\\"))throw new Error(`Windows RPC named-pipe addresses need a logical filesystem path for their secret: ${logicalPath}`);return`${logicalPath}${SOCKET_SECRET_SUFFIX}`}async function ensureSocketSecret(path){try{let existing=await readFile3(path);if(existing.length===SOCKET_SECRET_BYTES)return existing}catch(error){if(!isNodeErrorCode4(error,"ENOENT"))throw error}return createSocketSecret(path)}async function createSocketSecret(path){await mkdir3(dirname2(path),{recursive:!0});let secret=randomBytes(SOCKET_SECRET_BYTES);return await writeFile4(path,secret,{mode:384}),await chmod2(path,384),secret}async function readSocketSecret(path){let secret=await readFile3(path);if(secret.length!==SOCKET_SECRET_BYTES)throw new Error(`invalid RPC socket secret: ${path}`);return secret}function resolveSocketTransportAddress(socketPath,platform,secret){if(platform!=="win32"||socketPath.toLowerCase().startsWith("\\\\.\\pipe\\"))return socketPath;if(!/^[a-zA-Z]:[\\/]/.test(socketPath)&&!/^\\\\[^/]/.test(socketPath))throw new Error(`Windows RPC socket path must be drive-qualified or UNC: ${socketPath}`);let canonical=win322.normalize(socketPath).toLowerCase(),identity=Buffer.concat([Buffer.from(canonical,"utf8"),Buffer.from(secret??[])]),name=createHash2("sha256").update(identity).digest("hex").slice(0,32);return`${WINDOWS_PIPE_PREFIX}${name}`}function authenticateSocket(socket,secret,onAuthenticated){let received=Buffer.alloc(0),finish=()=>{clearTimeout(timer),socket.off("data",onData),socket.off("error",onError)},onError=()=>{finish(),socket.destroy()},timer=setTimeout(()=>{finish(),socket.destroy()},SOCKET_HANDSHAKE_TIMEOUT_MS),onData=chunk=>{if(received=Buffer.concat([received,chunk]),received.length<secret.length)return;if(finish(),!timingSafeEqual(received.subarray(0,secret.length),Buffer.from(secret))){socket.destroy();return}let remainder=received.subarray(secret.length);remainder.length>0&&socket.unshift(remainder),onAuthenticated()};socket.on("data",onData),socket.once("error",onError),socket.once("close",finish)}function sendSocketHandshake(socket,secret){socket.write(Buffer.from(secret))}function isNodeErrorCode4(error,code){return error instanceof Error&&"code"in error&&error.code===code}var HOST_COLD_START_ENV="SENPI_RPC_HOST_COLD_START",HOST_IDLE_EXIT_MS_ENV="SENPI_RPC_HOST_IDLE_EXIT_MS",DEFAULT_HOST_IDLE_EXIT_MS=15*6e4,CHILD_STOP_TIMEOUT_MS=5e3,WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS=2e3,CHILD_WATCH_FD=3;async function createInternalSocketPath(baseDir=tmpdir(),platform=process.platform){if(platform==="win32"){let dir2=join2(baseDir,`internal-${randomUUID()}`);return await mkdir4(dir2,{recursive:!0,mode:448}),{socket:`\\\\.\\pipe\\senpi-rpc-internal-${randomUUID()}`,dir:dir2,secretPath:join2(dir2,"secret")}}let dir=join2(tmpdir(),`senpi-rpc-host-internal-${randomUUID().slice(0,8)}`);return await mkdir4(dir,{recursive:!1,mode:448}),await writeFile5(join2(dir,".owner"),JSON.stringify({pid:process.pid,processStartTime:await readProcessStartTime(process.pid),createdAt:Date.now()}),{mode:384}),{socket:join2(dir,"host.sock"),dir,secretPath:join2(dir,".secret")}}function parseColdStart(value){return value==="transient"||value==="persistent"?value:void 0}function parseIdleExitMs(value){if(value===void 0||!/^\d+$/.test(value.trim()))return;let parsed=Number(value.trim());return Number.isFinite(parsed)&&parsed>0?parsed:void 0}function resolveHostPolicy(settings,env){let record=isRecord3(settings)?settings:{},coldStart=parseColdStart(env[HOST_COLD_START_ENV])??parseColdStart(asOptionalString(record.coldStart))??"transient",idleExitMs=parseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV])??parseIdleExitMs(asOptionalString(record.idleExitMs))??DEFAULT_HOST_IDLE_EXIT_MS;return{coldStart,idleExitMs}}function classifyChildExit(code,signal){return code===0&&signal===null?{reason:"rpc host exited on its own idle policy",exitCode:0}:{reason:`rpc host process exited unexpectedly (${code??signal})`,exitCode:1}}var IdleExitDecider=class{constructor(idleExitMs,now=Date.now){this.idleExitMs=idleExitMs,this.now=now}update(activity){return activity.connections>0||activity.activeTurns>0?(this.idleSince=void 0,"active"):this.idleExitMs===Number.POSITIVE_INFINITY?"idle":this.idleSince===void 0?(this.idleSince=this.now(),"idle"):this.now()-this.idleSince>=this.idleExitMs?"exit":"idle"}},INTERNAL_SUPERVISOR_FLAG="--internal-rpc-host-supervisor",INJECTABLE_PREFIX_FLAGS=new Set(["--extension"]);function findInternalSupervisorArgs(argv){for(let index=0;index<argv.length;index++){let arg=argv[index];if(arg===INTERNAL_SUPERVISOR_FLAG)return argv.slice(index+1);if(!INJECTABLE_PREFIX_FLAGS.has(arg)||index+1>=argv.length)return;index++}}function parseSupervisorArgs(argv){let hostArgs=[],socket,childCommand,childArgs,agentDir,bindSocket,replaceIdentity;for(let index=0;index<argv.length;index++){let arg=argv[index];if(arg==="--socket"&&index+1<argv.length){socket=argv[++index];continue}if(arg==="--child-command"&&index+1<argv.length){childCommand=argv[++index];continue}if(arg==="--child-args"&&index+1<argv.length){try{let parsed=JSON.parse(argv[++index]);Array.isArray(parsed)&&parsed.every(value=>typeof value=="string")&&(childArgs=parsed)}catch{return}continue}if(arg==="--agent-dir"&&index+1<argv.length){agentDir=argv[++index];continue}if(arg==="--bind"&&index+1<argv.length){bindSocket=argv[++index];continue}if(arg==="--replace"&&index+1<argv.length){replaceIdentity=parseSocketIdentity(argv[++index]);continue}hostArgs.push(arg)}return socket===void 0?void 0:{socket,hostArgs,childCommand,childArgs,agentDir,bindSocket,replaceIdentity}}function parseSocketIdentity(value){let match=/^(\d+):(\d+)$/.exec(value);return match?{dev:Number(match[1]),ino:Number(match[2])}:void 0}function resolveCliMainPath(){let modulePath=fileURLToPath(import.meta.url),extension=modulePath.endsWith(".ts")?".ts":".js";return resolve(dirname3(modulePath),"..","..",`cli-main${extension}`)}function resolveHostChildLaunch(launch,internalSocket,compiled=isBunBinary){return launch.childCommand?{command:launch.childCommand,args:[...launch.childArgs??[],"--listen",`unix://${internalSocket}`]}:{command:process.execPath,args:[...compiled?[]:[...process.execArgv,resolveCliMainPath()],"--mode","rpc","--multi-session","--listen",`unix://${internalSocket}`,...launch.hostArgs]}}function quoteWindowsShellArg(value){return`"${value.replace(/(\\*)"/g,'$1$1\\"').replace(/(\\*)$/,"$1$1").replace(/([()%!^"<>&|;,])/g,"^$1")}"`}function spawnableChildLaunch(launch,platform=process.platform){let extension=extname(launch.command).toLowerCase();return platform!=="win32"||extension!==".cmd"&&extension!==".bat"?{...launch,shell:!1}:{command:quoteWindowsShellArg(launch.command),args:launch.args.map(quoteWindowsShellArg),shell:!0}}async function runHostSupervisor(launch){let paths=createHostDaemonPaths({socket:launch.socket,agentDir:launch.agentDir??getAgentDir()}),told=process.env[HOST_INSTANCE_ID_ENV],instanceId=told!==void 0&&told.trim()!==""?told:randomUUID(),generation=generationPaths(paths,instanceId),policy=resolveHostPolicy(await readSettingsFile(paths.settingsFile),process.env),publicSocket=launch.socket,bindSocket=launch.bindSocket??publicSocket,successor=launch.bindSocket!==void 0,publicSecret=process.platform==="win32"?await ensurePublicSocketSecret(publicSocket):void 0,internal=await createInternalSocketPath(paths.dir),internalSocket=internal.socket,internalSecretPath=internal.secretPath??socketSecretPath(internalSocket),internalSecret=process.platform==="win32"?await createSocketSecret(internalSecretPath):void 0,clientSockets=new Set,busySessions=new Map,observerHealthy=!1,observerReconnectTimer,childExitWatchTimer,shuttingDown=!1,draining=!1,shutdownPromise,childLaunch=spawnableChildLaunch(resolveHostChildLaunch(launch,internalSocket)),child=spawn(childLaunch.command,childLaunch.args,{env:{...process.env,...launch.agentDir?{SENPI_CODING_AGENT_DIR:launch.agentDir}:{},[HOST_DAEMON_DIR_ENV]:paths.dir,[HOST_INSTANCE_ID_ENV]:instanceId,[HOST_WATCH_FD_ENV]:String(CHILD_WATCH_FD),[HOST_WATCH_PPID_ENV]:String(process.pid),...internal.dir?{[HOST_SCRATCH_DIR_ENV]:internal.dir}:{},...internalSecret?{[SOCKET_SECRET_FILE_ENV]:internalSecretPath}:{},[HOST_CLEANUP_PATHS_ENV]:[...successor?[]:[paths.pointerFile,generation.pidFile,paths.settingsFile],...process.platform==="win32"?[publicSocket]:[]].join(`
|
|
6
|
+
`,{mode:384}),await rename2(tmpPath,path)}async function readSocketIdentityFile(path){let raw;try{raw=await readFile2(path,"utf8")}catch(cause){if(isNodeErrorCode3(cause,"ENOENT"))return;throw cause}let parsed;try{parsed=JSON.parse(raw)}catch{return}if(!(typeof parsed!="object"||parsed===null||typeof parsed.dev!="number"||typeof parsed.ino!="number"))return{dev:parsed.dev,ino:parsed.ino}}async function waitForSocketIdentityFile(path,timeoutMs=SOCKET_IDENTITY_WAIT_MS,intervalMs=25){let deadline=Date.now()+timeoutMs;for(;;){let identity=await readSocketIdentityFile(path);if(identity!==void 0)return identity;if(Date.now()>=deadline)return;await delayUnrefed(intervalMs)}}async function unlinkOwnedSocket(socketPath,identity,log){if(process.platform==="win32"||socketPath.startsWith("\0"))return;let current;try{current=await statSocketIdentity(socketPath)}catch(cause){log(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);return}if(current!==void 0){if(identity===void 0){log(`socket path ${socketPath} ownership unknown; leaving it`);return}if(!sameSocketIdentity(current,identity)){log(`socket path ${socketPath} now owned by another host; leaving it`);return}try{await unlink(socketPath)}catch(cause){isNodeErrorCode3(cause,"ENOENT")||log(`socket ${socketPath} removal failed (${errorMessage(cause)})`)}}}async function shieldSocketDuringClose(socketPath,close){if(process.platform==="win32"||socketPath.startsWith("\0"))return close();let shieldPath=`${socketPath}.shield-${process.pid}`,shielded=!1;try{await statSocketIdentity(socketPath)!==void 0&&(await rename2(socketPath,shieldPath),shielded=!0)}catch{}try{await close()}finally{if(shielded)try{await rename2(shieldPath,socketPath)}catch{}}}function delayUnrefed(ms){return new Promise(resolve2=>{setTimeout(resolve2,ms).unref?.()})}function errorMessage(cause){return cause instanceof Error?cause.message:String(cause)}function isNodeErrorCode3(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}import{createHash as createHash2,randomBytes,timingSafeEqual}from"node:crypto";import{chmod as chmod2,mkdir as mkdir3,readFile as readFile3,writeFile as writeFile4}from"node:fs/promises";import{dirname as dirname2,win32 as win322}from"node:path";var WINDOWS_PIPE_PREFIX="\\\\.\\pipe\\senpi-rpc-",SOCKET_SECRET_BYTES=32,SOCKET_SECRET_SUFFIX=".secret",SOCKET_SECRET_FILE_ENV="SENPI_RPC_SOCKET_SECRET_FILE",SOCKET_HANDSHAKE_TIMEOUT_MS=2e3;function socketSecretPath(logicalPath){if(logicalPath.toLowerCase().startsWith("\\\\.\\pipe\\"))throw new Error(`Windows RPC named-pipe addresses need a logical filesystem path for their secret: ${logicalPath}`);return`${logicalPath}${SOCKET_SECRET_SUFFIX}`}async function ensureSocketSecret(path){try{let existing=await readFile3(path);if(existing.length===SOCKET_SECRET_BYTES)return existing}catch(error){if(!isNodeErrorCode4(error,"ENOENT"))throw error}return createSocketSecret(path)}async function createSocketSecret(path){await mkdir3(dirname2(path),{recursive:!0});let secret=randomBytes(SOCKET_SECRET_BYTES);return await writeFile4(path,secret,{mode:384}),await chmod2(path,384),secret}async function readSocketSecret(path){let secret=await readFile3(path);if(secret.length!==SOCKET_SECRET_BYTES)throw new Error(`invalid RPC socket secret: ${path}`);return secret}function resolveSocketTransportAddress(socketPath,platform,secret){if(platform!=="win32"||socketPath.toLowerCase().startsWith("\\\\.\\pipe\\"))return socketPath;if(!/^[a-zA-Z]:[\\/]/.test(socketPath)&&!/^\\\\[^/]/.test(socketPath))throw new Error(`Windows RPC socket path must be drive-qualified or UNC: ${socketPath}`);let canonical=win322.normalize(socketPath).toLowerCase(),identity=Buffer.concat([Buffer.from(canonical,"utf8"),Buffer.from(secret??[])]),name=createHash2("sha256").update(identity).digest("hex").slice(0,32);return`${WINDOWS_PIPE_PREFIX}${name}`}function authenticateSocket(socket,secret,onAuthenticated){let received=Buffer.alloc(0),finish=()=>{clearTimeout(timer),socket.off("data",onData),socket.off("error",onError)},onError=()=>{finish(),socket.destroy()},timer=setTimeout(()=>{finish(),socket.destroy()},SOCKET_HANDSHAKE_TIMEOUT_MS),onData=chunk=>{if(received=Buffer.concat([received,chunk]),received.length<secret.length)return;if(finish(),!timingSafeEqual(received.subarray(0,secret.length),Buffer.from(secret))){socket.destroy();return}let remainder=received.subarray(secret.length);remainder.length>0&&socket.unshift(remainder),onAuthenticated()};socket.on("data",onData),socket.once("error",onError),socket.once("close",finish)}function sendSocketHandshake(socket,secret){socket.write(Buffer.from(secret))}function isNodeErrorCode4(error,code){return error instanceof Error&&"code"in error&&error.code===code}var HOST_COLD_START_ENV="SENPI_RPC_HOST_COLD_START",HOST_IDLE_EXIT_MS_ENV="SENPI_RPC_HOST_IDLE_EXIT_MS",DEFAULT_HOST_IDLE_EXIT_MS=15*6e4,CHILD_STOP_TIMEOUT_MS=5e3,WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS=2e3,CHILD_WATCH_FD=3;async function createInternalSocketPath(baseDir=tmpdir(),platform=process.platform){if(platform==="win32"){let dir2=join2(baseDir,`internal-${randomUUID()}`);return await mkdir4(dir2,{recursive:!0,mode:448}),{socket:`\\\\.\\pipe\\senpi-rpc-internal-${randomUUID()}`,dir:dir2,secretPath:join2(dir2,"secret")}}let dir=join2(tmpdir(),`senpi-rpc-host-internal-${randomUUID().slice(0,8)}`);return await mkdir4(dir,{recursive:!1,mode:448}),await writeFile5(join2(dir,".owner"),JSON.stringify({pid:process.pid,processStartTime:await readProcessStartTime(process.pid),createdAt:Date.now()}),{mode:384}),{socket:join2(dir,"host.sock"),dir,secretPath:join2(dir,".secret")}}function parseColdStart(value){return value==="transient"||value==="persistent"?value:void 0}function parseIdleExitMs(value){if(value===void 0||!/^\d+$/.test(value.trim()))return;let parsed=Number(value.trim());return Number.isFinite(parsed)&&parsed>0?parsed:void 0}function resolveHostPolicy(settings,env){let record=isRecord3(settings)?settings:{},coldStart=parseColdStart(env[HOST_COLD_START_ENV])??parseColdStart(asOptionalString(record.coldStart))??"transient",idleExitMs=parseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV])??parseIdleExitMs(asOptionalString(record.idleExitMs))??DEFAULT_HOST_IDLE_EXIT_MS;return{coldStart,idleExitMs}}function classifyChildExit(code,signal){return code===0&&signal===null?{reason:"rpc host exited on its own idle policy",exitCode:0}:{reason:`rpc host process exited unexpectedly (${code??signal})`,exitCode:1}}var IdleExitDecider=class{constructor(idleExitMs,now=Date.now){this.idleExitMs=idleExitMs,this.now=now}update(activity){return activity.connections>0||activity.activeTurns>0?(this.idleSince=void 0,"active"):this.idleExitMs===Number.POSITIVE_INFINITY?"idle":this.idleSince===void 0?(this.idleSince=this.now(),"idle"):this.now()-this.idleSince>=this.idleExitMs?"exit":"idle"}},INTERNAL_SUPERVISOR_FLAG="--internal-rpc-host-supervisor",INJECTABLE_PREFIX_FLAGS=new Set(["--extension"]);function findInternalSupervisorArgs(argv){for(let index=0;index<argv.length;index++){let arg=argv[index];if(arg===INTERNAL_SUPERVISOR_FLAG)return argv.slice(index+1);if(!INJECTABLE_PREFIX_FLAGS.has(arg)||index+1>=argv.length)return;index++}}function parseSupervisorArgs(argv){let hostArgs=[],socket,childCommand,childArgs,agentDir,bindSocket,replaceIdentity;for(let index=0;index<argv.length;index++){let arg=argv[index];if(arg==="--socket"&&index+1<argv.length){socket=argv[++index];continue}if(arg==="--child-command"&&index+1<argv.length){childCommand=argv[++index];continue}if(arg==="--child-args"&&index+1<argv.length){try{let parsed=JSON.parse(argv[++index]);Array.isArray(parsed)&&parsed.every(value=>typeof value=="string")&&(childArgs=parsed)}catch{return}continue}if(arg==="--agent-dir"&&index+1<argv.length){agentDir=argv[++index];continue}if(arg==="--bind"&&index+1<argv.length){bindSocket=argv[++index];continue}if(arg==="--replace"&&index+1<argv.length){replaceIdentity=parseSocketIdentity(argv[++index]);continue}hostArgs.push(arg)}return socket===void 0?void 0:{socket,hostArgs,childCommand,childArgs,agentDir,bindSocket,replaceIdentity}}function parseSocketIdentity(value){let match=/^(\d+):(\d+)$/.exec(value);return match?{dev:Number(match[1]),ino:Number(match[2])}:void 0}function resolveCliMainPath(){let modulePath=fileURLToPath(import.meta.url),extension=modulePath.endsWith(".ts")?".ts":".js",unbundled=resolve(dirname3(modulePath),"..","..",`cli-main${extension}`);return existsSync(unbundled)?unbundled:resolveDeclaredCliEntry(modulePath)??unbundled}function resolveDeclaredCliEntry(modulePath){let dir=dirname3(modulePath);for(let depth=0;depth<8;depth+=1){let manifestPath=resolve(dir,"package.json");if(existsSync(manifestPath)){try{let declared=JSON.parse(readFileSync(manifestPath,"utf8")).bin,candidates=typeof declared=="string"?[declared]:Object.values(declared??{});for(let candidate of candidates){let entry=resolve(dir,candidate);if(existsSync(entry))return entry}}catch{}return}let parent=dirname3(dir);if(parent===dir)return;dir=parent}}function resolveHostChildLaunch(launch,internalSocket,compiled=isBunBinary){return launch.childCommand?{command:launch.childCommand,args:[...launch.childArgs??[],"--listen",`unix://${internalSocket}`]}:{command:process.execPath,args:[...compiled?[]:[...process.execArgv,resolveCliMainPath()],"--mode","rpc","--multi-session","--listen",`unix://${internalSocket}`,...launch.hostArgs]}}function quoteWindowsShellArg(value){return`"${value.replace(/(\\*)"/g,'$1$1\\"').replace(/(\\*)$/,"$1$1").replace(/([()%!^"<>&|;,])/g,"^$1")}"`}function spawnableChildLaunch(launch,platform=process.platform){let extension=extname(launch.command).toLowerCase();return platform!=="win32"||extension!==".cmd"&&extension!==".bat"?{...launch,shell:!1}:{command:quoteWindowsShellArg(launch.command),args:launch.args.map(quoteWindowsShellArg),shell:!0}}async function runHostSupervisor(launch){let paths=createHostDaemonPaths({socket:launch.socket,agentDir:launch.agentDir??getAgentDir()}),told=process.env[HOST_INSTANCE_ID_ENV],instanceId=told!==void 0&&told.trim()!==""?told:randomUUID(),generation=generationPaths(paths,instanceId),policy=resolveHostPolicy(await readSettingsFile(paths.settingsFile),process.env),publicSocket=launch.socket,bindSocket=launch.bindSocket??publicSocket,successor=launch.bindSocket!==void 0,publicSecret=process.platform==="win32"?await ensurePublicSocketSecret(publicSocket):void 0,internal=await createInternalSocketPath(paths.dir),internalSocket=internal.socket,internalSecretPath=internal.secretPath??socketSecretPath(internalSocket),internalSecret=process.platform==="win32"?await createSocketSecret(internalSecretPath):void 0,clientSockets=new Set,busySessions=new Map,observerHealthy=!1,observerReconnectTimer,childExitWatchTimer,shuttingDown=!1,draining=!1,shutdownPromise,childLaunch=spawnableChildLaunch(resolveHostChildLaunch(launch,internalSocket)),child=spawn(childLaunch.command,childLaunch.args,{env:{...process.env,...launch.agentDir?{SENPI_CODING_AGENT_DIR:launch.agentDir}:{},[HOST_DAEMON_DIR_ENV]:paths.dir,[HOST_INSTANCE_ID_ENV]:instanceId,[HOST_WATCH_FD_ENV]:String(CHILD_WATCH_FD),[HOST_WATCH_PPID_ENV]:String(process.pid),...internal.dir?{[HOST_SCRATCH_DIR_ENV]:internal.dir}:{},...internalSecret?{[SOCKET_SECRET_FILE_ENV]:internalSecretPath}:{},[HOST_CLEANUP_PATHS_ENV]:[...successor?[]:[paths.pointerFile,generation.pidFile,paths.settingsFile],...process.platform==="win32"?[publicSocket]:[]].join(`
|
|
7
7
|
`),...process.platform==="win32"?{}:{[HOST_PUBLIC_SOCKET_ENV]:publicSocket}},shell:childLaunch.shell,stdio:["ignore","ignore","inherit","pipe"],windowsHide:!0});child.stdio[CHILD_WATCH_FD]?.on("error",()=>{}),child.once("exit",(code,signal)=>{if(shuttingDown)return;let{reason,exitCode}=classifyChildExit(code,signal);shutdown(reason,exitCode)});let server=createServer(client=>{let accept=()=>{if(shuttingDown||draining){client.destroy();return}let internal2=createConnection(resolveSocketTransportAddress(internalSocket,process.platform,internalSecret));internalSecret&&sendSocketHandshake(internal2,internalSecret),clientSockets.add(client),decider.update(currentActivity());let detach=()=>{clientSockets.delete(client),decider.update(currentActivity()),internal2.destroy(),client.destroy()};client.pipe(internal2),internal2.pipe(client),client.once("close",detach),client.once("error",detach),internal2.once("close",detach),internal2.once("error",detach)};publicSecret?authenticateSocket(client,publicSecret,accept):accept()});server.once("error",cause=>{shuttingDown||shutdown(`public socket listener failed: ${errorMessage2(cause)}`,1)});let decider=new IdleExitDecider(policy.coldStart==="persistent"?Number.POSITIVE_INFINITY:policy.idleExitMs),tickIntervalMs=Math.max(20,Math.min(1e3,policy.idleExitMs/4)),ticker=setInterval(()=>{decider.update(currentActivity())==="exit"&&shutdown("idle",0)},tickIntervalMs);function currentActivity(){return{connections:clientSockets.size,activeTurns:observerHealthy?countBusySessions():1}}function countBusySessions(){let busy=0;for(let count of busySessions.values())count>0&&busy++;return busy}function observeHostEvent(line){let event;try{event=JSON.parse(line)}catch{return}if(typeof event!="object"||event===null)return;let{type,sessionId}=event;if(typeof sessionId=="string"){if(type==="agent_start")busySessions.set(sessionId,(busySessions.get(sessionId)??0)+1);else if(type==="agent_settled")busySessions.set(sessionId,Math.max(0,(busySessions.get(sessionId)??1)-1));else return;decider.update(currentActivity())}}async function shutdown(reason,exitCode){return shutdownPromise??=performShutdown(reason,exitCode),shutdownPromise}async function performShutdown(reason,exitCode){shuttingDown&&process.exit(exitCode),shuttingDown=!0,clearInterval(ticker),childExitWatchTimer&&clearInterval(childExitWatchTimer);let hardExit=process.platform==="win32"?setTimeout(()=>process.exit(exitCode),WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS):void 0;try{writeStderrLine(`senpi rpc host supervisor: ${reason} shutdown`);for(let client of clientSockets)client.destroy();draining||await shieldSocketDuringClose(publicSocket,()=>closeServer(server)),internal.dir&&await rm3(internal.dir,{recursive:!0,force:!0}),await stopChild(child),observer?.destroy(),publicSocketOwned&&process.platform!=="win32"&&await unlinkOwnedSocket(publicSocket,publicSocketIdentity,supervisorLog),await releaseGeneration(paths,{instanceId,pid:process.pid})}finally{hardExit&&clearTimeout(hardExit),process.exit(exitCode)}}let observer,publicSocketOwned=!1,publicSocketIdentity;function supervisorLog(message){writeStderrLine(`senpi rpc host supervisor: ${message}`)}function drainForHandoff(){if(!(draining||shuttingDown)&&(draining=!0,supervisorLog("draining into the next generation"),child.pid!==void 0&&child.exitCode===null&&child.signalCode===null))try{process.kill(child.pid,"SIGUSR1")}catch(cause){supervisorLog(`could not ask the host to drain: ${errorMessage2(cause)}`)}}registerSupervisorSignals(shutdown,drainForHandoff);try{await waitForListener(internalSocket,3e4,internalSecret),await connectObserver(),await prepareSocketPath(bindSocket),await listen(server,bindSocket,publicSecret),publicSocketOwned=!0,successor&&await adoptPublicSocket(bindSocket,publicSocket,launch.replaceIdentity),publicSocketIdentity=await statSocketIdentity(publicSocket),publicSocketIdentity&&internal.dir&&await writeSocketIdentityFile(join2(internal.dir,PUBLIC_SOCKET_IDENTITY_FILE),publicSocketIdentity)}catch(cause){await shutdown(`startup failed: ${errorMessage2(cause)}`,1)}async function connectObserver(){let secret=internalSecret,next=createConnection(resolveSocketTransportAddress(internalSocket,process.platform,secret));secret&&sendSocketHandshake(next,secret),await waitForConnect(next,5e3),observer=next,observerHealthy=!0,attachJsonlLineReader(next,observeHostEvent,{maxLineLength:MAX_RPC_LINE_CHARACTERS});let lost=()=>{observer!==next||shuttingDown||(observerHealthy=!1,observer=void 0,observerReconnectTimer===void 0&&(observerReconnectTimer=setTimeout(()=>{observerReconnectTimer=void 0,connectObserver().catch(()=>lost())},250),observerReconnectTimer.unref?.()))};next.once("close",lost),next.once("error",lost)}if(process.platform==="win32"&&child.pid!==void 0){let childStartTime=await readProcessStartTime(child.pid,process.platform,1e3).catch(()=>{}),missingIdentityChecks=0,checkingChildIdentity=!1;childExitWatchTimer=setInterval(()=>{shuttingDown||checkingChildIdentity||child.exitCode!==null||child.signalCode!==null||(checkingChildIdentity=!0,readProcessStartTime(child.pid,process.platform,1e3).then(currentStartTime=>{currentStartTime===void 0&&!processIsLive(child.pid)?missingIdentityChecks++:currentStartTime!==void 0&&(missingIdentityChecks=0),(childStartTime!==void 0&¤tStartTime!==void 0&¤tStartTime!==childStartTime||missingIdentityChecks>=2)&&shutdown("rpc host child exit observed by identity watchdog",0)}).catch(()=>{}).finally(()=>{checkingChildIdentity=!1}))},500),childExitWatchTimer.unref?.()}writeStderrLine(`senpi rpc host ready on unix://${publicSocket} (coldStart=${policy.coldStart}, idleExitMs=${policy.coldStart==="persistent"?"never":String(policy.idleExitMs)})`),await new Promise(()=>{})}function registerSupervisorSignals(shutdown,drain){for(let signal of process.platform==="win32"?["SIGTERM"]:["SIGTERM","SIGHUP"])process.on(signal,()=>{shutdown(`signal:${signal}`,signal==="SIGHUP"?129:143)});process.platform!=="win32"&&process.on("SIGUSR1",drain)}async function adoptPublicSocket(bindSocket,publicSocket,expected){if(expected===void 0)throw new Error(`${publicSocket}: a generation launch must name the entry it replaces`);let current=await statSocketIdentity(publicSocket);if(current===void 0||current.dev!==expected.dev||current.ino!==expected.ino)throw new Error(`${publicSocket}: owned by another socket entry now; refusing to replace it`);await rename3(bindSocket,publicSocket)}async function ensurePublicSocketSecret(publicSocket){let secretPath=socketSecretPath(publicSocket);try{return await ensureSocketSecret(secretPath)}catch(cause){throw new Error(`senpi rpc host supervisor: cannot provision public socket secret ${secretPath}`,{cause})}}async function readSettingsFile(settingsFile){try{return JSON.parse(await readFile4(settingsFile,"utf8"))}catch{return}}async function stopChild(child){if(!(child.exitCode!==null||child.signalCode!==null)){try{child.kill("SIGTERM")}catch{return}if(!await waitForChildExit(child,CHILD_STOP_TIMEOUT_MS)){try{child.kill("SIGKILL")}catch{return}await waitForChildExit(child,2e3)}}}function waitForChildExit(child,timeoutMs){return child.exitCode!==null||child.signalCode!==null?Promise.resolve(!0):new Promise(resolve2=>{let timer=setTimeout(()=>{child.off("exit",onExit),resolve2(!1)},timeoutMs),onExit=()=>{clearTimeout(timer),resolve2(!0)};child.once("exit",onExit)})}async function waitForListener(socketPath,timeoutMs,secret){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(await canConnect(socketPath,secret))return;await delay2(50)}throw new Error(`${socketPath}: host did not start listening within ${timeoutMs}ms`)}function canConnect(socketPath,secret){return new Promise(resolve2=>{let socket=createConnection(resolveSocketTransportAddress(socketPath,process.platform,secret));secret&&sendSocketHandshake(socket,secret);let settle=value=>{socket.destroy(),resolve2(value)};socket.once("connect",()=>settle(!0)),socket.once("error",()=>settle(!1))})}function waitForConnect(socket,timeoutMs){return new Promise((resolve2,reject)=>{let timer=setTimeout(()=>{cleanup(),reject(new Error(`observer connection to internal host timed out after ${timeoutMs}ms`))},timeoutMs),onConnect=()=>{cleanup(),resolve2()},onError=cause=>{cleanup(),reject(cause)},cleanup=()=>{clearTimeout(timer),socket.off("connect",onConnect),socket.off("error",onError)};socket.once("connect",onConnect),socket.once("error",onError)})}async function prepareSocketPath(socketPath){if(process.platform!=="win32"){if(Buffer.byteLength(socketPath)>MAX_SOCKET_PATH_BYTES)throw new Error(`${socketPath}: socket path is ${Buffer.byteLength(socketPath)} bytes, over the ${MAX_SOCKET_PATH_BYTES}-byte limit.`);await mkdir4(dirname3(socketPath),{recursive:!0,mode:448});try{await access(socketPath)}catch(cause){if(isNodeErrorCode5(cause,"ENOENT"))return;throw cause}if(await canConnect(socketPath))throw new Error(`${socketPath}: address already in use by a live server.`);await unlink2(socketPath)}}function listen(server,socketPath,secret){return new Promise((resolve2,reject)=>{server.once("error",reject),server.listen({path:resolveSocketTransportAddress(socketPath,process.platform,secret),readableAll:!1,writableAll:!1},async()=>{server.off("error",reject);try{process.platform!=="win32"&&!socketPath.startsWith("\0")&&await chmod3(socketPath,384),resolve2()}catch(cause){reject(cause)}})})}function closeServer(server){return new Promise(resolve2=>{server.close(()=>resolve2())})}function writeStderrLine(text){try{writeSync(2,`${text}
|
|
8
|
-
`)}catch{}}function delay2(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function errorMessage2(cause){return cause instanceof Error?cause.message:String(cause)}function isNodeErrorCode5(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}function isRecord3(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function asOptionalString(value){return typeof value=="string"?value:typeof value=="number"?String(value):void 0}function isEntryScript(){let entry=process.argv[1];if(!entry)return!1;try{return fileURLToPath(import.meta.url)===realpathSync(entry)}catch{return!1}}if(!isBundledNode&&isEntryScript()){let launch=parseSupervisorArgs(process.argv.slice(2));launch||(writeStderrLine("usage: host-lifecycle.ts --socket <path> [host cli args...]"),process.exit(2)),runHostSupervisor(launch)}export{parseDaemonPidFile,ProcessIdentityUnreadableError,processMatchesPidFile,stopValidatedPid,waitForGone,readProcessStartTime,processIsLive,waitForStartTime,
|
|
8
|
+
`)}catch{}}function delay2(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function errorMessage2(cause){return cause instanceof Error?cause.message:String(cause)}function isNodeErrorCode5(cause,code){return cause instanceof Error&&"code"in cause&&cause.code===code}function isRecord3(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function asOptionalString(value){return typeof value=="string"?value:typeof value=="number"?String(value):void 0}function isEntryScript(){let entry=process.argv[1];if(!entry)return!1;try{return fileURLToPath(import.meta.url)===realpathSync(entry)}catch{return!1}}if(!isBundledNode&&isEntryScript()){let launch=parseSupervisorArgs(process.argv.slice(2));launch||(writeStderrLine("usage: host-lifecycle.ts --socket <path> [host cli args...]"),process.exit(2)),runHostSupervisor(launch)}export{HOST_DAEMON_DIR_ENV,createHostDaemonPaths,generationPaths,createDaemonDirectories,writeHostSettings,readHostSettings,writeStateFile,readFileOrUndefined,parseJson,parseDaemonPidFile,ProcessIdentityUnreadableError,processMatchesPidFile,stopValidatedPid,waitForGone,readProcessStartTime,processIsLive,waitForStartTime,readHostRegistration,writeHostRegistration,clearHostRegistration,releaseGeneration,provenOwner,legacyHostIsLive,writtenByThisProcess,SOCKET_SECRET_FILE_ENV,socketSecretPath,ensureSocketSecret,createSocketSecret,readSocketSecret,resolveSocketTransportAddress,authenticateSocket,sendSocketHandshake,readHostWatchdogConfigFromBrandEnv,armHostWatchdog,PUBLIC_SOCKET_IDENTITY_FILE,MAX_SOCKET_PATH_BYTES,generationBindPath,statSocketIdentity,readSocketIdentityFile,waitForSocketIdentityFile,unlinkOwnedSocket,shieldSocketDuringClose,HOST_COLD_START_ENV,HOST_IDLE_EXIT_MS_ENV,DEFAULT_HOST_IDLE_EXIT_MS,createInternalSocketPath,parseColdStart,parseIdleExitMs,resolveHostPolicy,classifyChildExit,IdleExitDecider,INTERNAL_SUPERVISOR_FLAG,findInternalSupervisorArgs,parseSupervisorArgs,resolveCliMainPath,resolveHostChildLaunch,spawnableChildLaunch,runHostSupervisor};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
-
import{getApiKeyEnvVars}from"./chunk-
|
|
2
|
+
import{getApiKeyEnvVars}from"./chunk-FDLVINNY.js";var MAX_ENV_SLOT_INDEX=16;function primaryEnvVar(providerId){if(providerId==="claude-sdk-oauth")return"CLAUDE_CODE_OAUTH_TOKEN";let envVars=getApiKeyEnvVars(providerId);if(!(!envVars||envVars.length===0))return envVars.find(name=>name.endsWith("_API_KEY"))??envVars[0]}function discoverEnvSlots(providerId,env){let primary=primaryEnvVar(providerId);if(!primary)return[];let slots=[],base=env(primary);base&&slots.push({name:"env",envVarName:primary,key:base,source:"env"});for(let index=2;index<=MAX_ENV_SLOT_INDEX;index++){let envVarName=`${primary}_${index}`,value=env(envVarName);value&&slots.push({name:`env-${index}`,envVarName,key:value,source:"env"})}return slots}export{discoverEnvSlots};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
function isRecord(value){return typeof value=="object"&&value!==null}function extractHttpStatus(error){if(!isRecord(error))return;let status=error.status;if(typeof status=="number")return status;let response=error.response;if(isRecord(response)&&typeof response.status=="number")return response.status}function errorMessage(error){return error instanceof Error?error.message:typeof error=="string"?error:String(error)}function isForcedToolChoiceUnsupportedError(error,sentForcedToolChoice){if(!sentForcedToolChoice||extractHttpStatus(error)!==400)return!1;let message=errorMessage(error);return/tool[_\s-]?choices?\b.*?(not\s+compatible|incompatible|not\s+supported|unsupported)/is.test(message)||/forces?\s+tool\s+use.*?(not\s+compatible|incompatible|not\s+supported|unsupported)/is.test(message)||/does\s+not\s+support\s+forced\s+tool[_\s-]?choices?/is.test(message)}function omitToolChoiceParam(params){let nextParams={...params};return delete nextParams.tool_choice,nextParams}export{isForcedToolChoiceUnsupportedError,omitToolChoiceParam};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
|
|
2
|
+
import{CUSTOM_UNSUPPORTED_CAPABILITY,EXTENSION_EVENTS_CAPABILITY,RPC_CLIENT_CAPABILITIES_ENV,SESSION_CONTEXT_CAPABILITY,SESSION_KIND_CAPABILITY}from"./chunk-U6Z6OBX4.js";import{DEFAULT_HOST_IDLE_EXIT_MS,HOST_DAEMON_DIR_ENV,INTERNAL_SUPERVISOR_FLAG,MAX_SOCKET_PATH_BYTES,ProcessIdentityUnreadableError,clearHostRegistration,createDaemonDirectories,createHostDaemonPaths,createSocketSecret,generationBindPath,legacyHostIsLive,processIsLive,processMatchesPidFile,provenOwner,readHostRegistration,readHostSettings,readProcessStartTime,readSocketSecret,releaseGeneration,resolveCliMainPath,resolveSocketTransportAddress,sendSocketHandshake,socketSecretPath,statSocketIdentity,waitForStartTime,writeHostRegistration,writeHostSettings,writtenByThisProcess}from"./chunk-LF3XTYYF.js";import{HOST_GENERATION_ENV,HOST_INSTANCE_ID_ENV,compareEngineOrdinal,engineBuildIdentity,hostLaunchProfile}from"./chunk-HSH674C6.js";import{ENV_AGENT_DIR,getAgentDir,isBunBinary}from"./chunk-H5RK2RYZ.js";function parseHostProtocolInfo(data){if(!isRecord(data)||typeof data.serverVersion!="string"||!Array.isArray(data.capabilities)||!data.capabilities.every(entry=>typeof entry=="string"))return;let ordinal=parseOrdinal(data.engineOrdinal),launchProfile=parseLaunchProfile(data.launch_profile);return{protocolVersion:typeof data.protocolVersion=="number"?data.protocolVersion:0,serverVersion:data.serverVersion,capabilities:data.capabilities,...typeof data.instanceId=="string"&&{instanceId:data.instanceId},...typeof data.generation=="number"&&Number.isSafeInteger(data.generation)&&{generation:data.generation},...typeof data.engineVersion=="string"&&{engineVersion:data.engineVersion},...ordinal&&{engineOrdinal:ordinal},...launchProfile&&{launch_profile:launchProfile}}}function parseOrdinal(value){if(!(!Array.isArray(value)||value.length!==5||!value.every(part=>typeof part=="number")))return[value[0],value[1],value[2],value[3],value[4]]}function parseLaunchProfile(value){if(!isRecord(value)||typeof value.profile_id!="string"||!isRecord(value.core))return;let{extensions,multi_session,session_runtime}=value.core;if(!(!Array.isArray(extensions)||!extensions.every(entry=>typeof entry=="string"))&&typeof multi_session=="boolean"&&!(session_runtime!=="in-process"&&session_runtime!=="worker"))return{profile_id:value.profile_id,core:{extensions,multi_session,session_runtime}}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}var HOST_PROTOCOL_VERSION=1,GENERATION_HANDOFF_CAPABILITY="generation_handoff",REQUIRED_HOST_CAPABILITIES=["multi_session",EXTENSION_EVENTS_CAPABILITY,SESSION_CONTEXT_CAPABILITY,SESSION_KIND_CAPABILITY],HostEnsureRefusedError=class extends Error{constructor(socket,reason,host){super(`RPC socket ${socket} refused: ${reason} - ${refusalDetail(reason,host)}`),this.name="HostEnsureRefusedError",this.socket=socket,this.reason=reason}};function refusalDetail(reason,host){switch(reason){case"protocol":return`the running host speaks protocol version ${host?.protocolVersion??"unknown"}, this build speaks ${HOST_PROTOCOL_VERSION}`;case"capability":{let missing=REQUIRED_HOST_CAPABILITIES.filter(capability=>!host?.capabilities.includes(capability));return`the running host is missing ${JSON.stringify(missing)}; it owns the socket, so no second host is started`}case"foreign_writer":return"its pidfile was written by another process, so this one may not signal it; stop that host explicitly instead";case"legacy_host":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";case"host_busy":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";default:return assertNever(reason)}}function decideHostAction(client,host,policy){if(host===void 0)return{action:"start",reason:client.startedByUs?"restart_own_host":"no_host",upgradeable:!1};if(host.protocolVersion!==client.protocolVersion)return{action:"refuse",reason:"protocol",upgradeable:!1};if(!client.requiredCapabilities.every(capability=>host.capabilities.includes(capability)))return policy==="fallback"?{action:"fallback",reason:"capability",upgradeable:!1}:{action:"refuse",reason:"capability",upgradeable:!1};let upgradeable=client.platform!=="win32"&&host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY);if(policy==="fallback"&&host.engineVersion!==client.identity.text)return{action:"fallback",reason:"engine_mismatch",upgradeable};let warning=profileWarning(client,host);return client.platform==="win32"?{action:"reuse",reason:"win32_attach_only",upgradeable:!1,...warning&&{warning}}:upgradeable?policy==="upgrade"&&client.launchProfile&&host.launch_profile&&supersedes(client,host)?{action:"handoff",reason:sameRelease(client.identity.ordinal,host.engineOrdinal)?"profile":"newer_engine",upgradeable:!0}:{action:"reuse",reason:"compatible",upgradeable,...warning&&{warning}}:{action:"reuse",reason:"handoff_unsupported",upgradeable:!1,...warning&&{warning}}}function supersedes(client,host){if(host.engineOrdinal===void 0)return!1;let hostIdentity={text:host.engineVersion??"",ordinal:host.engineOrdinal,scheme:host.engineOrdinal[4]>0?"epoch":"nodef"};return compareEngineOrdinal(client.identity,hostIdentity)<=0?!1:covers(client.launchProfile,host.launch_profile)}function profileWarning(client,host){if(client.launchProfile===void 0||host.launch_profile===void 0)return"profile_mismatch_attached";if(client.launchProfile.profile_id!==host.launch_profile.profile_id)return covers(client.launchProfile,host.launch_profile)?"profile_mismatch_attached":"profile_narrower_attached"}function covers(candidate,running){return candidate===void 0||running===void 0?!1:running.core.extensions.every(extension=>candidate.core.extensions.includes(extension))}function sameRelease(candidate,running){return running!==void 0&&candidate.slice(0,4).every((part,index)=>part===running[index])}function assertNever(value){throw new Error(`unreachable host decision: ${JSON.stringify(value)}`)}import{createConnection}from"node:net";var DEFAULT_PROBE_TIMEOUT_MS=1e4,PROBE_REQUEST_ID="ensure-host-probe";function probeHost(options){return probeProtocolInfo(options.socket,options.timeoutMs??DEFAULT_PROBE_TIMEOUT_MS)}async function probeProtocolInfo(socketPath,timeoutMs){let reply=await requestOnSocket(socketPath,{type:"get_protocol_info"},timeoutMs);return reply===void 0?void 0:parseHostProtocolInfo(reply)}async function probeSocketReachable(socketPath,timeoutMs){return(await connectAndAsk(socketPath,{id:PROBE_REQUEST_ID,type:"get_protocol_info"},timeoutMs)).connected}async function probeSessionCount(socketPath,timeoutMs){let reply=await requestOnSocket(socketPath,{type:"list_sessions",include_workers:!0},timeoutMs);if(!(!isRecord2(reply)||!Array.isArray(reply.sessions)))return reply.sessions.length}async function requestOnSocket(socketPath,command,timeoutMs){return(await connectAndAsk(socketPath,{id:PROBE_REQUEST_ID,...command},timeoutMs)).answer}async function connectAndAsk(socketPath,request,timeoutMs){let secret;if(process.platform==="win32")try{secret=await readSocketSecret(socketSecretPath(socketPath))}catch{return{connected:!1,answer:void 0}}return new Promise(resolveProbe=>{let socket=createConnection(resolveSocketTransportAddress(socketPath,process.platform,secret)),buffer="",settled=!1,connected=!1,finish=value=>{settled||(settled=!0,clearTimeout(timeout),socket.destroy(),resolveProbe({connected,answer:value}))},timeout=setTimeout(()=>finish(),timeoutMs);socket.once("connect",()=>{connected=!0,socket.write(`${JSON.stringify(request)}
|
|
3
|
+
`)}),socket.on("data",chunk=>{for(buffer+=chunk.toString("utf8");;){let newline=buffer.indexOf(`
|
|
4
|
+
`);if(newline===-1)return;let line=buffer.slice(0,newline);buffer=buffer.slice(newline+1);let answer=readAnswer(line);if(answer!==void 0)return finish(answer)}}),socket.once("error",()=>finish()),socket.once("close",()=>finish()),secret&&sendSocketHandshake(socket,secret)})}function readAnswer(text){let parsed;try{parsed=JSON.parse(text)}catch{return}if(!(!isRecord2(parsed)||parsed.id!==PROBE_REQUEST_ID||parsed.success!==!0))return parsed.data??{}}function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}import{existsSync}from"node:fs";import{basename,dirname,resolve}from"node:path";import{fileURLToPath}from"node:url";var PINNED_HOST_CLIENT_CAPABILITIES=[EXTENSION_EVENTS_CAPABILITY,CUSTOM_UNSUPPORTED_CAPABILITY];function defaultHostLaunch(supervisorArgs,compiled=isBunBinary,sibling=resolveHostLifecycleEntryPath()??null){return compiled?{command:process.execPath,args:[INTERNAL_SUPERVISOR_FLAG,...supervisorArgs]}:sibling!==null?{command:process.execPath,args:[...process.execArgv,sibling,...supervisorArgs]}:{command:process.execPath,args:[...process.execArgv,resolveCliMainPath(),INTERNAL_SUPERVISOR_FLAG,...supervisorArgs]}}function resolveHostLifecycleEntryPath(){let modulePath=fileURLToPath(import.meta.url);if(basename(dirname(modulePath))==="chunks")return;let extension=modulePath.endsWith(".ts")?".ts":".js",sibling=resolve(dirname(modulePath),`host-lifecycle${extension}`);return existsSync(sibling)?sibling:void 0}async function stopHost(options){let paths=createHostDaemonPaths({socket:options.socket,...options.agentDir?{agentDir:options.agentDir}:{}}),registered=await readHostRegistration(paths),owner=await provenOwner(registered,options.socket);if(!owner)return{action:"refuse",reason:"unknown_owner"};let host=await probeProtocolInfo(options.socket,options.timeoutMs??1e4);if(options.drain===!0)return!host?.capabilities.includes(GENERATION_HANDOFF_CAPABILITY)||process.platform==="win32"?{action:"refuse",reason:"drain_unsupported"}:(signalGeneration(owner.pid,"SIGUSR1"),{action:"drained",pid:owner.pid});let sessions=await probeSessionCount(options.socket,options.timeoutMs??1e4);return options.force!==!0&&host!==void 0&&sessions!==void 0&&sessions>0?{action:"refuse",reason:"sessions_live"}:(signalGeneration(owner.pid,"SIGTERM"),await releaseGeneration(paths,{instanceId:owner.instanceId,pid:owner.pid}),{action:"stopped",pid:owner.pid})}function signalGeneration(pid,signal){try{return process.kill(pid,signal),!0}catch(error){if(error instanceof Error&&"code"in error&&error.code==="ESRCH")return!1;throw error}}import{spawn}from"node:child_process";import{randomUUID}from"node:crypto";import{open}from"node:fs/promises";var DEFAULT_HANDOFF_READINESS_MS=3e4;async function startSuccessor(context){let{options,paths,host,owner}=context,generation=(host.generation??0)+1,instanceId=randomUUID(),bindSocket=generationBindPath(options.socket,generation);if(Buffer.byteLength(bindSocket)>MAX_SOCKET_PATH_BYTES)return{action:"refuse",reason:"socket_path_too_long",upgradeable:!0,detail:bindSocket};let replaced=await statSocketIdentity(options.socket);if(!replaced)return{action:"refuse",reason:"socket_replaced",upgradeable:!0};let running=await readHostSettings(paths);await writeHostSettings(paths,{socket:options.socket,capabilities:PINNED_HOST_CLIENT_CAPABILITIES,coldStart:options.policy?.coldStart??running?.coldStart??"transient",idleExitMs:options.policy?.idleExitMs??running?.idleExitMs??DEFAULT_HOST_IDLE_EXIT_MS,generation,instanceId}),await options._test?.beforeSpawn?.();let argv=["--socket",options.socket,"--bind",bindSocket,"--replace",`${replaced.dev}:${replaced.ino}`,...options.hostArgs??[]],launch=options._test?.launch?.(argv)??defaultHostLaunch(argv),stderr=await open(paths.stderrLog,"a",384),child=spawn(launch.command,[...launch.args],{detached:!0,windowsHide:!0,env:successorEnv(options,{paths,generation,instanceId}),stdio:["ignore","ignore",stderr.fd]});await stderr.close();let exited=new Promise(resolve2=>child.once("exit",()=>resolve2()));try{if(child.pid===void 0)throw new Error("failed to spawn the successor generation");let answer=await awaitSuccessor(options,host,exited);if(!answer)return child.exitCode===null&&child.signalCode===null&&child.kill("SIGKILL"),{action:"refuse",reason:await abortReason(options.socket,replaced),upgradeable:!0};let processStartTime=await waitForStartTime(child.pid,1e4).catch(()=>{})??null;return await writeHostRegistration(paths,{record:{pid:child.pid,processStartTime},socket:options.socket,instanceId,generation,launchProfileId:hostLaunchProfile(["--mode","rpc","--multi-session",...options.hostArgs??[]],process.cwd()).profile_id}),child.unref(),signalGeneration(owner.pid,"SIGUSR1"),{action:"handoff",pid:child.pid,socket:options.socket,generation:answer.generation??generation,instanceId:answer.instanceId??""}}catch(cause){return child.exitCode===null&&child.signalCode===null&&child.kill("SIGKILL"),{action:"refuse",reason:"successor_unavailable",upgradeable:!0,detail:cause instanceof Error?cause.message:String(cause)}}}async function awaitSuccessor(options,previous,exited){let deadline=Date.now()+(options._test?.readinessTimeoutMs??DEFAULT_HANDOFF_READINESS_MS),childGone=!1;for(exited.then(()=>{childGone=!0});Date.now()<=deadline;){let answer=await probeProtocolInfo(options.socket,2e3);if(answer&&answer.instanceId!==void 0&&answer.instanceId!==previous.instanceId)return answer;if(childGone)return;await delay(50)}}async function abortReason(socket,replaced){let current=await statSocketIdentity(socket).catch(()=>{});return current===void 0||current.dev!==replaced.dev||current.ino!==replaced.ino?"socket_replaced":"successor_unavailable"}function successorEnv(options,successor){let env={...process.env,[HOST_GENERATION_ENV]:String(successor.generation),[HOST_INSTANCE_ID_ENV]:successor.instanceId,[HOST_DAEMON_DIR_ENV]:successor.paths.dir,[RPC_CLIENT_CAPABILITIES_ENV]:PINNED_HOST_CLIENT_CAPABILITIES.join(","),...options.agentDir?{[ENV_AGENT_DIR]:options.agentDir}:{}};for(let[key,value]of Object.entries(options.env??{}))value===null?delete env[key]:env[key]=value;return env}function delay(ms){return new Promise(resolve2=>setTimeout(resolve2,ms))}async function handoffHost(options){if((options._test?.platform??process.platform)==="win32")return{action:"refuse",reason:"upgrade_unsupported",upgradeable:!1};let paths=createHostDaemonPaths({socket:options.socket,...options.agentDir?{agentDir:options.agentDir}:{}});await createDaemonDirectories(paths);let host=await probeProtocolInfo(options.socket,1e4);if(!host)return{action:"refuse",reason:"no_host",upgradeable:!1};if(!host.capabilities.includes(GENERATION_HANDOFF_CAPABILITY))return{action:"refuse",reason:"handoff_unsupported",upgradeable:!1};let registered=await readHostRegistration(paths),owner=await provenOwner(registered,options.socket);return owner?startSuccessor({options,paths,host,owner}):{action:"refuse",reason:"unknown_owner",upgradeable:!0}}import{spawn as spawn2}from"node:child_process";import{createHash,randomUUID as randomUUID2}from"node:crypto";import{mkdir,open as open2,readdir,readFile,rm,writeFile}from"node:fs/promises";import{tmpdir}from"node:os";import{dirname as dirname2,join}from"node:path";import{stat}from"node:fs/promises";var LegacyLockArtifactError=class extends Error{constructor(lockPath){super(`Legacy lock directory is present at ${lockPath}`),this.code="ELEGACY_LOCK_ARTIFACT",this.name="LegacyLockArtifactError",this.lockPath=lockPath}},DEFAULT_RETRIES={retries:100,minTimeout:20,maxTimeout:100};async function openLockDatabase(lockPath){if(typeof globalThis.Bun<"u"){let{Database}=await import("bun:sqlite"),database2=new Database(lockPath,{create:!0});return{exec:sql=>database2.exec(sql),close:()=>database2.close(!1)}}let{DatabaseSync}=await import("node:sqlite"),database=new DatabaseSync(lockPath);return{exec:sql=>database.exec(sql),close:()=>database.close()}}async function acquireOwnershipSafeLock(lockPath,options={}){let retries=options.retries??DEFAULT_RETRIES,deadline=Date.now()+retries.retries*retries.maxTimeout;for(;;){await rejectLegacyDirectory(lockPath);let busyMs=Math.max(1,Math.min(retries.maxTimeout,deadline-Date.now())),database;try{database=await openLockDatabase(lockPath),database.exec(`PRAGMA busy_timeout = ${busyMs}; BEGIN EXCLUSIVE;`);let released=!1,held=database;return async()=>{if(!released){released=!0;try{held.exec("COMMIT;")}finally{held.close()}}}}catch(error){try{database?.close()}catch{}if(await rejectLegacyDirectory(lockPath),isBusy(error)&&Date.now()<deadline){await new Promise(resolve2=>setTimeout(resolve2,Math.min(retries.minTimeout,Math.max(1,deadline-Date.now()))));continue}throw error}}}async function rejectLegacyDirectory(lockPath){try{if((await stat(lockPath)).isDirectory())throw new LegacyLockArtifactError(lockPath)}catch(error){if(error instanceof LegacyLockArtifactError||error.code!=="ENOENT")throw error}}function isBusy(error){let message=error instanceof Error?error.message:String(error);return/busy|locked/i.test(message)||error?.code==="SQLITE_BUSY"}var SPAWNED_HOST_PROBE_TIMEOUT_MS=1e4,EXISTING_HOST_PROBE_TIMEOUT_MS=1e4,DEFAULT_READINESS_TIMEOUT_MS=1e4,DEFAULT_STOP_TIMEOUT_MS=1e4,SIGKILL_GRACE_MS=2e3,ENSURE_LOCK_WAIT_MS=EXISTING_HOST_PROBE_TIMEOUT_MS+DEFAULT_STOP_TIMEOUT_MS+SIGKILL_GRACE_MS+DEFAULT_READINESS_TIMEOUT_MS+1e4,LOCK_BUSY_WAIT_MS=100,lockOptions={retries:{retries:ENSURE_LOCK_WAIT_MS/LOCK_BUSY_WAIT_MS,minTimeout:20,maxTimeout:LOCK_BUSY_WAIT_MS}};async function ensureHost(options){let socket=normalizeSocketPath(options.socket),paths=createHostDaemonPaths({socket,...options.agentDir?{agentDir:options.agentDir}:{}});await createDaemonDirectories(paths);let lockTarget=join(tmpdir(),"senpi-rpc-host-locks",createSocketLockName(socket));await mkdir(dirname2(lockTarget),{recursive:!0}),await writeFile(lockTarget,"",{flag:"a",mode:384}),await reapOrphanedInternalHostDirs();let release=await acquireOwnershipSafeLock(`${lockTarget}.lock`,lockOptions);try{return await options._test?.afterLockAcquired?.(),await ensureHostLocked(paths,socket,options)}finally{await release()}}async function ensureHostLocked(paths,socket,options){let testOptions=options._test,registered=await readHostRegistration(paths),registeredHere=registersSocket(registered,socket),protocol=await probeProtocolInfo(socket,EXISTING_HOST_PROBE_TIMEOUT_MS),startedByUs=registeredHere&&await writtenByThisProcess(registered?.writer),attachedPid=registeredHere?registered?.record.pid??0:0,decision=decide(options,startedByUs,protocol);switch(decision.action){case"reuse":return{pid:attachedPid,socket,reused:!0};case"refuse":throw new HostEnsureRefusedError(socket,decision.reason,protocol);case"handoff":return upgradeGeneration(paths,socket,options,attachedPid);case"start":break;default:return assertNever2(decision)}let probe=testOptions?.readProcessStartTime??readProcessStartTime,pidMatches=registeredHere&®istered?await matchesPidFileOrUnknown(registered.record,probe):!1;if(registered&&pidMatches){if(!startedByUs)throw new HostEnsureRefusedError(socket,"foreign_writer",protocol);if(await probeSocketReachable(socket,EXISTING_HOST_PROBE_TIMEOUT_MS))throw new HostEnsureRefusedError(socket,"host_busy",protocol);await stopManagedHost(registered.record,testOptions?.stopTimeoutMs??DEFAULT_STOP_TIMEOUT_MS,probe)}if(await legacyHostIsLive(paths,probe))throw new HostEnsureRefusedError(socket,"legacy_host",protocol);return registeredHere&&await clearHostRegistration(paths),startHost(paths,socket,options)}function decide(options,startedByUs,protocol){if(options.upgrade!=="if-engine-differs")return decideHostAction(ensureClient(options,startedByUs),protocol,"never");let decision=decideHostAction(ensureClient(options,startedByUs),protocol,"upgrade");return decision.action==="fallback"?{action:"reuse",reason:"compatible",upgradeable:!1}:decision}async function upgradeGeneration(paths,socket,options,attachedPid){let result=await handoffHost({socket,agentDir:options.agentDir??getAgentDir(),hostArgs:options.hostArgs??[],...options.env?{env:options.env}:{},...options.policy?{policy:options.policy}:{},_test:{...options._test?.launch?{launch:options._test.launch}:{},...options._test?.readinessTimeoutMs?{readinessTimeoutMs:options._test.readinessTimeoutMs}:{}}});return result.action==="handoff"?{pid:result.pid,socket,reused:!1}:(await appendStderr(paths,`generation handoff refused: ${result.reason}${result.detail?` (${result.detail})`:""}`),{pid:attachedPid,socket,reused:!0})}function ensureClient(options,startedByUs){return{protocolVersion:HOST_PROTOCOL_VERSION,requiredCapabilities:REQUIRED_HOST_CAPABILITIES,identity:engineBuildIdentity(),...options.upgrade==="if-engine-differs"?{launchProfile:hostLaunchProfile(hostChildArgv(options.hostArgs??[]),process.cwd())}:{},startedByUs,platform:process.platform}}function hostChildArgv(hostArgs){return["--mode","rpc","--multi-session",...hostArgs]}function registersSocket(registered,socket){return registered===void 0?!1:registered.socket===void 0||registered.socket===socket}async function startHost(paths,socket,options){let testOptions=options._test,instanceId=randomUUID2();process.platform==="win32"&&await createSocketSecret(socketSecretPath(socket)),await writeHostSettings(paths,{socket,capabilities:PINNED_HOST_CLIENT_CAPABILITIES,coldStart:options.policy?.coldStart??"transient",idleExitMs:options.policy?.idleExitMs??DEFAULT_HOST_IDLE_EXIT_MS,generation:0,instanceId});let stderr=await open2(paths.stderrLog,"w",384),pidFile,child,exitedEarly,childExit;try{let supervisorArgs=["--socket",socket,...options.hostArgs??[]],launch=testOptions?.spawn??testOptions?.launch?.(supervisorArgs)??defaultHostLaunch(supervisorArgs);if(child=spawn2(launch.command,[...launch.args],{detached:!0,windowsHide:!0,env:hostEnv(options,{paths,instanceId}),stdio:["ignore","ignore",stderr.fd]}),childExit=new Promise(resolveExit=>{child.once("exit",(code,signal)=>{exitedEarly={code,signal},resolveExit(exitedEarly)})}),child.pid===void 0)throw new Error("failed to spawn RPC socket host");let probe=testOptions?.readProcessStartTime??readProcessStartTime,observedStartTime=await Promise.race([waitForStartTime(child.pid,1e4,probe),childExit.then(()=>{throw new Error("RPC socket host exited before its start time could be read")})]),unhurriedProbe=testOptions?.readProcessStartTime?testOptions.readProcessStartTime:pid=>readProcessStartTime(pid,process.platform,15e3),processStartTime=observedStartTime??await unhurriedProbe(child.pid).catch(()=>{});pidFile={pid:child.pid,processStartTime:processStartTime??null},await testOptions?.beforePidFileWrite?.(),await writeHostRegistration(paths,{record:pidFile,socket,instanceId,generation:0,launchProfileId:hostLaunchProfile(hostChildArgv(options.hostArgs??[]),process.cwd()).profile_id}),child.unref()}catch(error){let exitedBeforeCleanup=exitedEarly;if(!exitedBeforeCleanup&&child&&child.exitCode===null&&child.signalCode===null){try{child.kill("SIGTERM")}catch{}if(childExit&&await Promise.race([childExit,delay2(2e3)]),child.exitCode===null&&child.signalCode===null)try{child.kill("SIGKILL")}catch{}}if(!exitedBeforeCleanup)throw await clearHostRegistration(paths),error;let diagnostic2=await appendStderr(paths,`RPC socket host exited with code ${exitedBeforeCleanup.code??"null"}${exitedBeforeCleanup.signal?` (${exitedBeforeCleanup.signal})`:""} before answering get_protocol_info`);throw await clearHostRegistration(paths),new Error(diagnostic2)}finally{await stderr.close()}let readinessTimeoutMs=testOptions?.readinessTimeoutMs??DEFAULT_READINESS_TIMEOUT_MS,result=await pollProtocolInfo(socket,readinessTimeoutMs,childExit);if(isCompatible(result.protocol))return{pid:pidFile.pid,socket,reused:!1};let stopFailure=await stopSpawnedChild(child,childExit,testOptions?.stopTimeoutMs??DEFAULT_STOP_TIMEOUT_MS).then(()=>{},error=>error instanceof Error?error.message:String(error)),message=result.protocol?`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)}`:result.exited?`RPC socket host exited with code ${result.exited.code??"null"}${result.exited.signal?` (${result.exited.signal})`:""} before answering get_protocol_info`:`spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`,diagnostic=await appendStderr(paths,stopFailure===void 0?message:`${message} (teardown also reported: ${stopFailure})`);throw await clearHostRegistration(paths),new Error(diagnostic)}async function matchesPidFileOrUnknown(pidFile,probe){try{return await processMatchesPidFile(pidFile,probe)}catch(error){if(error instanceof ProcessIdentityUnreadableError)return!1;throw error}}async function stopSpawnedChild(child,childExit,termTimeoutMs){let exited=()=>child.exitCode!==null||child.signalCode!==null;if(exited())return;let signal=name=>{try{child.kill(name)}catch(error){if(!isNodeErrorCode(error,"ESRCH"))throw error}},waitFor=ms=>Promise.race([childExit.then(()=>!0),delay2(ms).then(()=>exited())]);if(signal("SIGTERM"),!await waitFor(termTimeoutMs)&&(signal("SIGKILL"),!await waitFor(SIGKILL_GRACE_MS)))throw new Error(`RPC socket host pid ${child.pid??"?"} remained alive after SIGKILL`)}async function stopManagedHost(pidFile,termTimeoutMs,readStartTime=readProcessStartTime){if(await signalValidated(pidFile,"SIGTERM",readStartTime),!await waitForGone(pidFile,termTimeoutMs,readStartTime)&&(await signalValidated(pidFile,"SIGKILL",readStartTime),!await waitForGone(pidFile,SIGKILL_GRACE_MS,readStartTime)))throw new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`)}async function resolvePidFileOwnership(pidFile,readStartTime){try{return await processMatchesPidFile(pidFile,readStartTime,processIsLive,{attempts:1})?"owns":"gone"}catch(error){if(error instanceof ProcessIdentityUnreadableError)return"unknown";throw error}}async function signalValidated(pidFile,signal,readStartTime=readProcessStartTime){if(await resolvePidFileOwnership(pidFile,readStartTime)==="owns")try{process.kill(pidFile.pid,signal)}catch(error){if(!isNodeErrorCode(error,"ESRCH"))throw error}}async function waitForGone(pidFile,timeoutMs,readStartTime=readProcessStartTime){let deadline=Date.now()+timeoutMs;for(;Date.now()<=deadline;){if(await resolvePidFileOwnership(pidFile,readStartTime)==="gone")return!0;await delay2(50)}return await resolvePidFileOwnership(pidFile,readStartTime)==="gone"}async function pollProtocolInfo(socket,timeoutMs,childExit){let deadline=Date.now()+timeoutMs,lastProtocol;for(;Date.now()<=deadline;){let probe=probeProtocolInfo(socket,Math.min(SPAWNED_HOST_PROBE_TIMEOUT_MS,Math.max(1,deadline-Date.now()))),raced=childExit?await Promise.race([probe,childExit]):await probe;if(isChildExit(raced)){let info=await probe;if(info){if(lastProtocol=info,isCompatible(info))return{protocol:info}}else return{protocol:lastProtocol,exited:raced}}else if(raced&&(lastProtocol=raced,isCompatible(raced)))return{protocol:raced};await delay2(50)}return{protocol:lastProtocol}}function isChildExit(value){return!!value&&"code"in value&&"signal"in value}function isCompatible(protocol){return decideHostAction(ensureClient({socket:""},!1),protocol,"never").action==="reuse"}function hostEnv(options,generation){let env={...process.env};for(let[key,value]of Object.entries(options.env??{}))value===null?delete env[key]:env[key]=value;return env[ENV_AGENT_DIR]=options.agentDir??getAgentDir(),env[RPC_CLIENT_CAPABILITIES_ENV]=PINNED_HOST_CLIENT_CAPABILITIES.join(","),env[HOST_INSTANCE_ID_ENV]=generation.instanceId,env[HOST_GENERATION_ENV]="0",env[HOST_DAEMON_DIR_ENV]=generation.paths.dir,env}async function reapOrphanedInternalHostDirs(){try{let entries=await readdir(tmpdir(),{withFileTypes:!0});await Promise.all(entries.filter(entry=>entry.isDirectory()&&entry.name.startsWith("senpi-rpc-host-internal-")).map(async entry=>{try{let owner=JSON.parse(await readFile(join(tmpdir(),entry.name,".owner"),"utf8"));typeof owner.pid=="number"&&typeof owner.processStartTime=="string"&&typeof owner.createdAt=="number"&&owner.processStartTime.length>0&&owner.createdAt<Date.now()-6e4&&(await readdir(join(tmpdir(),entry.name))).length===1&&!await processMatchesPidFile({pid:owner.pid,processStartTime:owner.processStartTime}).catch(error=>{if(error instanceof ProcessIdentityUnreadableError)return!0;throw error})&&await rm(join(tmpdir(),entry.name),{recursive:!0,force:!0})}catch{}}))}catch{}}async function appendStderr(paths,message){try{let stderr=(await readFile(paths.stderrLog,"utf8")).trim();return stderr?`${message}
|
|
5
|
+
${stderr}`:message}catch(error){if(isNodeErrorCode(error,"ENOENT"))return message;throw error}}function createSocketLockName(socket){return createHash("sha256").update(resolveSocketTransportAddress(socket,process.platform),"utf8").digest("hex").slice(0,32)}function normalizeSocketPath(value){return value.startsWith("unix://")?value.slice(7):value}function delay2(ms){return new Promise(resolveDelay=>setTimeout(resolveDelay,ms))}function isNodeErrorCode(error,code){return error instanceof Error&&"code"in error&&error.code===code}function assertNever2(value){throw new Error(`unreachable host decision: ${JSON.stringify(value)}`)}export{parseHostProtocolInfo,HOST_PROTOCOL_VERSION,GENERATION_HANDOFF_CAPABILITY,REQUIRED_HOST_CAPABILITIES,HostEnsureRefusedError,decideHostAction,probeHost,probeProtocolInfo,requestOnSocket,PINNED_HOST_CLIENT_CAPABILITIES,stopHost,handoffHost,acquireOwnershipSafeLock,ensureHost};
|