import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"; import { dirname, join } from "node:path"; import type { HelpState, LegacyHelpState, PersistResult } from "../types.ts"; import { emptyState, migrateState } from "./migrate.ts"; export interface StatePaths { stateDir: string; stateFile: string; legacyStateFile: string; } export function createStatePaths(agentDir: string): StatePaths { const stateDir = join(agentDir, "state", "pi-dynamic-help"); return { stateDir, stateFile: join(stateDir, "state.json"), legacyStateFile: join(agentDir, "state", "dynamic-help.json"), }; } function parseJsonFile(path: string): { ok: true; value: T } | { ok: false; error: unknown } | { ok: undefined } { if (!existsSync(path)) return { ok: undefined }; try { return { ok: true, value: JSON.parse(readFileSync(path, "utf-8")) as T }; } catch (error) { return { ok: false, error }; } } function timestamp(): string { return new Date().toISOString().replace(/[-:]/g, "").replace(/\..*$/, "").replace("T", "-"); } export function backupCorruptState(path: string): string | undefined { if (!existsSync(path)) return undefined; const backup = join(dirname(path), `state.corrupt-${timestamp()}.json`); renameSync(path, backup); return backup; } export function loadState(paths: StatePaths): { state: HelpState; warnings: string[] } { const warnings: string[] = []; const current = parseJsonFile(paths.stateFile); if (current.ok === true) return { state: migrateState(current.value), warnings }; if (current.ok === false) { try { const backup = backupCorruptState(paths.stateFile); warnings.push(`状态文件损坏,已备份为 ${backup}`); } catch (error) { warnings.push(`状态文件损坏,且备份失败:${String(error)}`); } } const legacy = parseJsonFile(paths.legacyStateFile); if (legacy.ok === true) return { state: migrateState(legacy.value), warnings }; if (legacy.ok === false) warnings.push("旧状态文件无法解析,已忽略"); return { state: emptyState(), warnings }; } export function saveState(paths: StatePaths, state: HelpState): PersistResult { try { mkdirSync(paths.stateDir, { recursive: true }); const tmp = `${paths.stateFile}.tmp-${process.pid}-${Date.now()}`; writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`); renameSync(tmp, paths.stateFile); return { ok: true }; } catch (error) { return { ok: false, warning: `帮助状态保存失败:${String(error)}` }; } }