import { B as BackgroundTaskState, a as BackgroundTaskRegistryEntry } from './background-task-D_UDhcs2.js'; /** * 后台任务进程原语(pid 判活 / 进程树处置 / start time 身份判据)——跨端单一实现。 * * 契约两端(此前各持一份逐字同构实现,对齐手段是注释,已归一到本模块): * - extension 侧:@zhushanwen/pi-base-tool-enhance(bash_kill 前校验 / 后台 timeout * 到点校验 / 对账判死),原实现在其 kill-tree.ts * - runtime 侧:xyz-agent background-task-reaper(孤儿收殓三分支判定),原实现在 * background-task-reaper.ts 原语区 * * 分支语义(与 pi 实装对齐,勿单侧擅改):Windows `taskkill /F /T`;POSIX 杀进程组 * `kill -- -`(后台任务 detached spawn 自成进程组,pgid = pid);进程组杀不到时 * 回退单 pid + `pgrep -P` 递归杀残留子进程。 * * 日志通道:本模块零日志依赖(protocol 零运行时依赖纪律)——回退路径的诊断经可选 * 回调 `onFallback(step, err)` 上报,extension 侧注入 logger.debug 适配、runtime 侧 * 注入 console.debug 适配。step 为稳定短标识符: * - 'process-group-kill-missed' POSIX 进程组 kill 失败(组长已死),走回退 * - 'single-pid-kill-missed' 单 pid kill 失败(目标已死,幂等语义) * - 'taskkill-failed' Windows taskkill 失败 * - 'descendant-enumeration-failed' pgrep 不可用/失败,放弃子孙枚举 * - 'descendant-kill-missed' 子孙 kill 失败(已死,幂等语义) */ /** killProcessTree 回退路径诊断回调(落盘/console 通道由调用方适配注入)。 */ type ProcessFallbackLogger = (step: string, err: unknown) => void; /** * pid 判活:kill(pid, 0) 不发信号只做存在性/权限校验。 * ESRCH = 已死(含被 reap 后);EPERM = 进程存在但属其他用户,仍视为活。 */ declare function isPidAlive(pid: number): boolean; /** * 杀整棵进程树(同步;收殓路径在 process.on("exit") 里跑,必须同步)。 * POSIX 杀进程组(pgid = pid)一次性覆盖全部子孙;进程组杀不到(组长已死)回退 * 单 pid + pgrep -P 递归清理残留子进程(先杀孙辈再杀子辈,防孙辈在父死后被 * reparent 逃逸枚举)。Windows taskkill /F /T。幂等:目标已死时静默成功 * (回退路径的诊断经 onFallback 上报,无回调则静默)。 */ declare function killProcessTree(pid: number, onFallback?: ProcessFallbackLogger): void; /** * 取进程 start time(epoch 秒)。ps -o lstart= 跨 macOS/Linux(= 号去表头;Linux * /proc//stat 精度更高但 macOS 无 /proc,统一 ps 保跨平台一致)。 * 返回 undefined:进程不存在 / ps 不可用(Windows)/ 输出不可解析——调用方一律按 * 「无法校验 → 保守跳过」处理。 */ declare function getProcessStartTimeSec(pid: number): number | undefined; /** * pid 身份判据(「宁不杀勿误杀」的唯一判定点)。 * true = 当前占用该 pid 的进程 start time 与登记值匹配,可安全 kill。 * - 有登记 start time(spawn 时 ps 读取成功)→ 精确比较(同单位 epoch 秒) * - 缺登记 start time(旧条目 / ps 不可用平台登记)→ startedAtMs 秒级降级: * 登记发生在 spawn 之后(进程先启动、条目后登记),原进程 start time 必然 * ≤ floor(startedAtMs/1000)(floor 单调性,零误跳) */ declare function pidStartMatchesRegistered(actualStartSec: number, registeredStartSec: number | undefined, startedAtMs: number): boolean; /** * registry.json 文件行为原语(解析防御 / corrupt 隔离 / 原子写 / 终态 LRU 裁剪 / * 序列化)——跨端单一实现。 * * 契约两端(此前各持一份同构实现,对齐手段是注释): * - extension 侧:@zhushanwen/pi-base-tool-enhance background/registry.ts(bte 的 * readRegistry 返回 Map、锁壳用 pi-file-lock) * - runtime 侧:xyz-agent background-task-reaper / services/background-task/ * registry-write.ts(数组形态、锁壳用统一锁) * * 本模块只提供无锁原语与纯函数;锁壳(withFileLockSync RMW 编排)留在各侧—— * 两侧锁实现不同(pi-file-lock vs runtime 统一锁)是产品决策,不下沉。 * * 日志通道:本模块零日志依赖——warn 级诊断(registry 读失败 / corrupt 隔离 / * tmp 清理失败)经可选回调 `onLog('warn', event, detail)` 上报,extension 侧注入 * extension-logger 适配、runtime 侧注入 console 适配。corrupt 隔离 warn 是排障 * 生命线,两侧适配必接。锁壳层的「registry write failed」warn 属各侧锁壳职责 * (写失败时条目停留 running 的降级决策在锁壳),不在本模块。 */ /** registry 文件原语日志回调(落盘/console 通道由调用方适配注入)。 */ type RegistryFileLogFn = (level: 'warn' | 'debug', event: string, detail?: unknown) => void; /** 终态裁剪入参的最小结构约束(泛型透传,保留调用方条目类型)。 */ interface TrimTerminalEntryLike { taskId: string; startedAt: number; endedAt?: number; state: BackgroundTaskState; } /** * 校验并归一化 registry 文件内容;形状非法返回 undefined(走 corrupt 隔离路径)。 * version 不匹配 / entries 非数组 → 整体非法;单条脏数据丢弃、不报废全表 * (契约 guard isBackgroundTaskRegistryEntry 复用)。 */ declare function parseRegistryContent(raw: string): BackgroundTaskRegistryEntry[] | undefined; /** .corrupt 落点:固定名优先;已存在则带时间戳,不覆盖前一份现场。 */ declare function corruptPathFor(registryPath: string): string; /** * 读取 registry 全量条目 + 损坏标记(runtime 现形态为超集;extension 侧 Map 形态 * 由各侧薄壳投影)。UI 读侧需要区分「真的空表」与「损坏被隔离的空表」。 * * 文件不存在 / 读失败 → 空表 + corrupted:false(常态/降级,非损坏);解析失败 → * 重命名 .corrupt 保留现场 + onLog warn + 空表 + corrupted:true(「空表重建」由 * 后续写入自然完成,不立即写空文件)。 */ declare function readRegistry(registryPath: string, onLog?: RegistryFileLogFn): { entries: BackgroundTaskRegistryEntry[]; corrupted: boolean; }; /** * registry 文件序列化(字节契约单点:version 锁定 + JSON indent 2 + 尾部换行)。 * 两侧写路径共用本函数——文件字节形态归一是 V4/P2 验收的前提。 */ declare function serializeRegistryFile(entries: BackgroundTaskRegistryEntry[]): string; /** * 原子写:tmp(pid+随机段唯一化防并发碰撞)+ rename(POSIX/Windows 均原子); * 失败清理 tmp 后向上抛原错误(锁壳层决定降级语义)。tmp 清理失败不掩盖原错误, * 仅经 onLog 上报诊断。 */ declare function atomicWriteRegistry(registryPath: string, content: string, onLog?: RegistryFileLogFn): void; /** * 终态条目 LRU 裁剪(纯函数):终态(exited/orphaned)按 `endedAt ?? startedAt` * 升序、超上限淘汰最老;活跃条目(running/killing)永不淘汰;未超上限原样保留。 * 入参数组不被修改(sort 作用于 filter 产生的新数组),返回保留集新数组。 * extension task-store 与 registry 写侧、runtime registry-write 共用(max 由各侧 * 传参,上限值不变)。 */ declare function trimTerminalEntries(entries: T[], max: number): T[]; /** * 输出文件 tail 读取原语(末尾字节窗口 + 行/字节双上限)——跨端单一实现。 * * 此前两侧各持一份逐行同构实现且签名已实际漂移(bte `(file, maxLines, maxBytes)` * vs runtime `(file, maxBytes, maxLines)`;返回字段 `output` vs `text`)——本模块 * 定型单一签名:`(file, opts: { maxBytes, maxLines })` → `{ text, truncated }` * (字段名取 runtime 的 `text`,比 `output` 中性且不与「输出文件」混淆)。 * * 默认上限(bte 50KB / runtime 32KB)是各自产品决策,**留在各调用方**传参, * 本模块不设默认值。 * * 日志通道:零日志依赖——close 失败(已读完内容,不影响结果)的 debug 诊断经可选 * 回调 `onLog('debug', event, detail)` 上报,调用方不适配则静默。 */ /** output-tail 日志回调(落盘/console 通道由调用方适配注入;close debug 可选)。 */ type OutputTailLogFn = (level: 'warn' | 'debug', event: string, detail?: unknown) => void; /** tail 读取上限(字节/行双上限,先到为准;由调用方按产品口径传参)。 */ interface OutputTailOptions { maxBytes: number; maxLines: number; } interface OutputTailResult { text: string; /** 读取窗口被截断(内容超任一上限)时 true。 */ truncated: boolean; } /** * 读文件尾部(行/字节双上限)。文件不存在/不可读返回 undefined——调用方按 * 「输出丢失」降级,不崩溃。实现从文件末尾按字节窗口读(不整读大文件, * O(maxBytes) 而非 O(fileSize))。 */ declare function readOutputTail(outputFile: string, opts: OutputTailOptions, onLog?: OutputTailLogFn): OutputTailResult | undefined; export { type OutputTailLogFn, type OutputTailOptions, type OutputTailResult, type ProcessFallbackLogger, type RegistryFileLogFn, type TrimTerminalEntryLike, atomicWriteRegistry, corruptPathFor, getProcessStartTimeSec, isPidAlive, killProcessTree, parseRegistryContent, pidStartMatchesRegistered, readOutputTail, readRegistry, serializeRegistryFile, trimTerminalEntries };