/** * 工作空间路径独立持久化 — userData/workspace-config.json * * 为什么独立于数据库:agent.db 存放在工作空间目录内,而"工作空间在哪"必须先于 * 数据库初始化得知(鸡生蛋问题),因此路径单独落一份 JSON。 * * 为什么独立成模块:此前 read/writeWorkspacePathToFile 定义在 main.ts 并由 * ipc/shared.ts 动态 import('../main') 取用 —— main ↔ shared 循环依赖。一旦主 * 入口模块求值中途崩溃(如 v0.8.2 前的顶层 session 访问),bundle 末尾的模块 * 命名空间永不初始化,该动态 import 抛 TDZ "Cannot access 'main' before * initialization",工作空间切换保存随之失败。抽出后为单向依赖,与入口模块 * 生命周期解耦。 * * 注意:文件路径必须在使用时实时计算(app.getPath('userData')),不能在模块 * 顶层缓存 —— E2E 通过 METONA_USER_DATA_DIR 在 main.ts 模块体内重定向 userData, * 而 ESM import 会提升到模块体之前执行,顶层缓存会锁死重定向前的路径。 */ import { app } from 'electron'; import { join } from 'path'; import { existsSync, readFileSync, writeFileSync } from 'fs'; import log from 'electron-log'; function getWorkspaceConfigFile(): string { return join(app.getPath('userData'), 'workspace-config.json'); } export function readWorkspacePathFromFile(): string | null { try { const file = getWorkspaceConfigFile(); if (existsSync(file)) { const data = JSON.parse(readFileSync(file, 'utf-8')); return data.workspacePath ?? null; } } catch { // 忽略读取错误 } return null; } /** * 写入失败向上抛出(调用方决定是否告知用户): * - 启动路径回写(main.ts):非致命,仅记日志; * - 切换工作空间保存(ipc/shared.ts):必须 toast 告知,否则用户以为切换成功, * 下次启动仍回到旧空间(v0.3.10 契约 —— 此前函数内部吞错,错误上报路径为死代码)。 */ export function writeWorkspacePathToFile(workspacePath: string): void { try { writeFileSync(getWorkspaceConfigFile(), JSON.stringify({ workspacePath }, null, 2), 'utf-8'); } catch (err) { log.error('Failed to write workspace config file:', err); throw err; } }