/** * Process state management for Godot MCP Enhanced. * * C-04: Async state-mutating operations are serialized through `enqueueAsync`. * Reads are still direct (no queueing) since they're atomic in the Node.js * single-threaded model. This prevents race conditions when MCP clients * introduce parallel tool calls. */ import type { ChildProcess } from 'child_process'; /** Kill process tree without blocking the event loop. Uses async spawn on Windows. */ export declare function forceKillTree(proc: ChildProcess): void; /** * 按 PID 杀进程树(与 forceKillTree 共享双平台语义,IMPORTANT-1)。 * Windows taskkill /F /T 清整树;POSIX pkill -P 杀子进程 + SIGTERM 主进程 * (Godot 可能 spawn 导入/资源子进程,对等 forceKillTree POSIX 分支)。 * 导出仅为测试可测性(@internal)。 */ export declare function killPidTree(pid: number): void; /** Async kill: waits for 'close' event, with 5 s fallback. */ export declare function killProcess(proc: ChildProcess): Promise; export type RunSessionStatus = 'starting' | 'running' | 'stopping' | 'exited' | 'exited_early' | 'errored'; export interface RunSession { proc: ChildProcess | null; status: RunSessionStatus; outputBuffer: string[]; lastFinishedRunOutput: string[]; processStartTime: number; busy: boolean; busyOwner: string; busySince: number; displayPath: string; } /** 记录本会话 spawn 的需要 orphan 兜底的 Godot 进程 PID(可选归属 project key)。 */ export declare function registerSpawnedGodotPid(pid: number, projectKey?: string): void; /** 进程正常退出时移除(主动清理,避免集合累积死 PID)。 */ export declare function unregisterSpawnedGodotPid(pid: number): void; /** 测试用:读取当前 pid 集合(兼容层:返回 pid 数组,归属信息经 orphan ctx 内部消费)。 */ export declare function getSpawnedGodotPids(): number[]; /** 项目 key 归一化(设计 §4.1):resolve + win 下 lowercase;仅供 Map 索引。 */ export declare function normalizeProjectKey(p: string): string; /** 活跃桶 key:活跃指针非空时归一化,否则 ''(惰性兼容桶)。 */ export declare function activeKey(): string; /** 惰性获取/创建活跃桶(硬性实现要求,设计 §4.1——77 个现有用例依赖此语义)。 */ export declare function getOrCreateSession(key: string): RunSession; /** 谓词(设计 §4.1,Supervisor 模式):isAlive=starting/running/stopping(canSignal 同集)。 * 导出供 Web GUI 项目面板等运行时消费方复用(Web GUI spec 2026-09-15 Task 1)。 */ export declare function isAliveStatus(st: RunSessionStatus): boolean; /** 读取指定项目桶(缺省=活跃桶);未创建/已逐出返回 undefined。 */ export declare function getSession(projectPath?: string): RunSession | undefined; /** 会话清单(设计 §4.1:'' 空桶不进列表;pid 无进程时为 null)。 */ export declare function listRunSessions(): Array<{ projectPath: string; displayPath: string; status: RunSessionStatus; pid: number | null; }>; /** 会话详单(Web GUI §3.3.2):白名单字段序列化,proc 不外泄;直接遍历 _sessions * (同 listRunSessions 模式),禁止 getOrCreateSession——读路径不得建桶污染 FIFO 语义。 */ export interface RunSessionDetailed { projectPath: string; displayPath: string; status: RunSessionStatus; pid: number | null; processStartTime: number; busy: boolean; busyOwner: string; busySince: number; /** 两态输出行数:运行中=outputBuffer.length,已结束=lastFinishedRunOutput.length */ outputLines: number; } export declare function listRunSessionsDetailed(): RunSessionDetailed[]; /** 指定桶的进程(守卫身份校验用:getRunSessionProc(key) === proc)。 */ export declare function getRunSessionProc(projectPath: string): ChildProcess | null; /** 面板清理(2026-09-14 批准设计):移除 ended 态桶(alive 拒绝);直接操作 * _sessions + FIFO 摘除。activeKey 桶删除无特殊限制——活跃指针是路径字符串, * 桶删了下次 setProjectDir 重建。 */ export declare function removeRunSession(projectPath: string): { ok: boolean; reason?: 'alive' | 'not_found'; }; /** 按 key 写入 proc(设计 §4.2 C-1):forceKillTree 仅针对**该桶**旧 proc,绝不碰活跃桶 * 或其他桶——run_project 主流程必须用本函数,不得用活跃桶语义的 setRunningProcess。 */ export declare function setRunSessionProc(projectPath: string, proc: ChildProcess | null, skipBusyCheck?: boolean): void; /** 按 key 释放 busy(设计 §4.2 C-1):主流程 busy 释放显式传 key,不落活跃桶。 */ export declare function releaseRunSessionBusy(projectPath: string): void; /** 清 X 桶的 proc/busy/快照挪移(守卫体内动作用;不杀进程——killProcess 由调用方负责, * 活跃指针不转移,设计 §5:被清桶保持可查询)。 */ export declare function clearRunSession(projectPath: string): void; /** killProcess 前设 stopping 态(仅 running/starting 可转;ended 态不动)。 */ export declare function markSessionStopping(projectPath: string): void; /** 直接设置桶状态(spawn 失败终态 'errored' 等);转 ended 态时登记 FIFO; * 转 alive 态时摘除 FIFO 残留(审查 I-1)。 */ export declare function setSessionStatus(projectPath: string, status: RunSessionStatus): void; /** close 判定(设计 §4.1 状态机,权威入口):2s 内退出=exited_early 优先于 code 判定; * 2s 外 code≠0=errored;否则 exited。不创建桶(未知桶 no-op)。 * stopping 态例外(2026-10-01 真机验证 NIT-3):markSessionStopping 已置的会话是 * 用户主动停止(面板 stop / MCP stop_project / run 定时器),被 kill 的退出码非零 * (Windows taskkill /F → 1)是请求的正常终点——统一归 exited,不算 errored。 */ export declare function markSessionExited(projectPath: string, exitCode: number | null): void; /** orphan 清理链排除集合(设计 §4.4):全部桶内活进程——判据为"有管理中的 proc 对象" * (未 killed 且未退出),不做系统级 pid 探测(orphan 第一层自会 isPidAlive)。 */ export declare function getActiveRunPids(): number[]; /** 杀全部桶活进程并清桶(GodotServer.close 用,设计 §5:不留孤儿)。 */ export declare function killAllRunSessions(): Promise; /** 运行中上限检查(设计 §4.1:GODOT_MCP_MAX_SESSIONS 每调用读取,默认 4;同项目覆盖 * 不算新增名额;溢出拒绝并附在跑会话清单,提示先 stop——不自动逐出运行中进程)。 */ export declare function ensureSessionCapacity(projectPath: string): void; /** Serialize an async state-mutating operation. Ensures only one async mutation * is in-flight at a time. Supports returning a value from the serialized function. */ declare function enqueueAsync(fn: () => (Promise | T)): Promise; export declare function isProcessBusy(): boolean; /** * Acquire the long-running process slot through the async serialization queue. * Serialized via enqueueAsync to prevent race conditions when MCP clients * issue parallel tool calls (e.g. run_project + execute_gdscript simultaneously). * Returns true if acquired, false if slot is busy. * Task 2 加可选 projectPath(设计 §4.2):显式传参时锁**目标桶**——run_project 现状顺序是 * acquire 先于 setProjectDir,按活跃桶锁会锁错桶;缺省仍锁活跃桶(兼容层)。 */ export declare function acquireProcessSlot(owner?: string, projectPath?: string): Promise; export declare function setProcessBusy(busy: boolean): void; /** Get info about what is currently holding the long-running lock. */ export declare function getBusyInfo(): { owner: string; startTime: number; projectDir: string; }; /** Build a user-friendly error message when the long-running slot is occupied. * Task 2 加可选 targetKey(设计 §4.2 M-1):多桶同时 busy 时报**目标桶**的持锁信息 * (owner/时长/项目路径取自该桶);缺省报活跃桶(兼容层)。 * 审查 M-2:错误消息构建属读路径,改 getSession 读侧不创建桶(getOrCreateSession 会把 * 未知 key 惰性建成空桶);目标桶不存在时回落活跃桶,仍无 busy 桶 → 空串。 */ export declare function buildBusyErrorMessage(targetKey?: string): string; export declare function acquireShortRunningSlot(): boolean; export declare function releaseShortRunningSlot(): void; export declare function getShortRunningCount(): number; export declare function getRunningProcess(): ChildProcess | null; export declare function setRunningProcess(proc: ChildProcess | null, skipBusyCheck?: boolean): void; export declare function getOutputBuffer(): string[]; export declare function appendOutput(lines: string[], projectKey?: string): void; export declare function clearOutputBuffer(projectKey?: string): void; /** 最近一次已结束运行会话的输出快照(活跃桶;get_debug_output 在当前缓冲为 * 空且无运行进程时回落读取)。 */ export declare function getLastFinishedRunOutput(): string[]; export declare function setOutputBuffer(buf: string[], key?: string): void; export declare function getProcessStartTime(): number; export declare function setProcessStartTime(t: number, key?: string): void; export declare function getProjectDir(): string; export declare function setProjectDir(d: string): void; /** Reset all module-level state — for test isolation. */ export declare function resetState(): void; export { enqueueAsync }; export declare function killOrphanGodotProcesses(projectDir?: string, options?: { fullSystemScan?: boolean; }): Promise;