# 2026-09-24 ## Codex 数据分析新包 · v1.0.0(包骨架 + 入口打通) **交付** - 新建 `ai-electron/frontend/src/codex/` 包(所有文件 codex 前缀): - `styles/codexTheme.less` — `--codex-*` 设计 token + 页面布局基元;色值全部映射到项目既有 token (`@cb-*` 色板 + antd legacy 变量),不引入新色系;布局=顶部状态条 + 会话栏 + 消息区(唯一滚动容器)+ 底部固定输入区 + 产物栏,窄屏(<1200px)收产物栏。 - `codexIndex.ts` — 包出口,只装载全局样式(注意:连入口都不能叫 index.ts,用户要求文件名全部 codex 前缀)。 - `views/codexAnalysis/codexAnalysis.vue` — 三栏骨架 + 空态示例。 - 改 2 处登记(无逻辑改动):`core/router/helper/routeHelper.ts` 加 `explicitDynamicViewMap['/codexAnalysis/codexAnalysis']`; `core/store/modules/menu.json` 在 10263「AI 数据分析」分组下加菜单项(id 10267,标题「Codex 分析」)。 - 计划文档 `codex-analysis-plan.md`(v1.0.0 → v1.9.0,逐版本 review)。 **验证** - `pnpm build` exit=0,产出 `dist/assets/codexAnalysis-*.{js,css}`。 - `pnpm type:check` 共 89 条报错=仓库既有基线数(`graph/trans/otg/call/person/case`),**codex 包 0 报错**。 **环境修复(worktree 首次可用)** - 本 worktree 原本没装依赖,且 `pnpm install` 因 esbuild postinstall `EBUSY`(沙箱拦孙进程 spawn node)失败后 `.bin` 不生成 → 改用 `pnpm install --ignore-scripts` 解决。 - 两个被 gitignore 误伤的目录从主仓库 `E:\workspace\zsjz-ai` 拷入本 worktree(都 ignore,不污染 git status): `ai-electron/frontend/build/`、`ai-electron/frontend/src/case/views/data/`(51 个文件,路由直接引用)。 **下一步**:v1.1.0 起继续(已当场做完,见下)。 ## Codex 数据分析新包 · v1.1.0 → v1.9.0(一次做完) **前端**:`src/codex/` 共 31 个文件(全 `codex` 前缀) - api:`codexApi.ts`(搬家 + 补 `onCodexApproval` / `onCodexApprovalClosed`)、`codexArtifactApi.ts` - store:`codexChatStore.ts`(会话 localStorage 自持、事件按 threadId 缓存、审批队列、产物目录、诊断) - utils:`codexEventReducer` / `codexBlockParser` / `codexMarkdown`(markdown-it + 最小类型 shim)/ `codexEcharts` / `codexFullscreen` / `codexArtifact` / `codexReportTemplate` / `codexReportExport` / `codexFormat` - components:工具栏 / 会话栏 / 消息流 / 消息项 / 输入区 / 过程时间线 / 工具卡 / 审批弹窗 / 块分发器 + markdown·代码·表格·JSON·图表·关系图六个块 / 产物栏 + 预览 / 诊断抽屉 - 视图:`views/codexAnalysis/codexAnalysis.vue`(重写为装配层) **主进程** - 新增 `service/codex/codexArtifactService.ts`:root 白名单(落盘 `codex-data/artifact-roots.json`, 重启后历史会话仍可读)+ 路径穿越校验;只读不写不删;文本/二进制分别限额。 - 新增 `controller/codexArtifactCtl.ts`:`list / readText / readBinary / saveAs / reveal / open / openRoot`。 - `codexCtl.ts` 增量:`threadStart` 返回 `{threadId, cwd}` 并登记 root;`turnRun` 登记可选 cwd;新增 `defaultWorkspace`。 **验证(全绿)** - 前端:`pnpm type:check` 89 条 = 基线、本包 0 报错;`pnpm build` exit=0,产出 `codexAnalysis-*.{js,css}`。 - 主进程:`npx tsc --noEmit` 0 报错;`npm test` 6 文件 / 91 用例全过;`npm run build-electron` exit=0 且 `codexArtifactCtl` 已进 `public/electron/main.js`。 **环境修复(本轮新增)** - `ai-electron` 是 **npm** 项目(只有 `package-lock.json`,无 `.pnpm`),pnpm 装不了(`esbuild@^0.28.0` 在镜像元数据里不存在)。从主仓库拷 `package-lock.json` 后 `npm install --ignore-scripts` 成功。 ## 收尾补漏(同日) 复核计划时发现并修掉两处: 1. **真缺口**:提问只存内存,`eventsRead` 回读不到用户侧文本 → 刷新后只剩回复。 已加 `codex.prompts.v1` 落盘(每会话上限 200 条、单条 2 万字)。 2. **计划有但没做**:产物栏「新产物角标」。已在 store 里按「上一轮快照对比」实现, 手动刷新即清除。 如实记录在 `codex-analysis-plan.md` 的「与计划的偏差」表里(关系图导出是 JSON 不是图、 HTML 报告里图表是折叠 option、未做行级 diff 染色、未真机 GUI 端到端验证)。 ## v1.9.1 + v2.0.0(数据分析增强) **v1.9.1(补齐 v1.x 两处偏差)** - 新增 `utils/codexVisualRegistry.ts`:内容哈希(djb2+长度)→ 图片提供者的注册表, 解决「HTML 报告导出在模块层、拿不到组件里的 ECharts 实例」。 - 关系图导出图片:html2canvas 整块栅格化(节点是 HTML、连线是 SVG,手撸 SVG 只能拿半张图); 导出前 `zoomToFit()`;报告内嵌用的缓存图 `scale=1` 且不改用户当前视图(延迟 600ms 后台截)。 - HTML 报告 `visualBlockToHtml()` 内嵌真实 ``,取不到回退折叠 JSON。 - `downloadCodexDataUrl()` 收敛三处重复的 base64→Blob 逻辑。 - Markdown 报告**仍保留围栏块**(data URL 会让 .md 涨到几十 MB,不适合再加工场景)。 **v2.0.1 分析模板库**:`utils/codexTemplateLibrary.ts`(8 条预置模板,围栏协议写进提示词当教学) + `components/codexTemplatePanel.vue`(搜索/分组/插入/存自定义/删自定义)。 **v2.0.2 工作目录管理**:store `recentCwds`(去重上限 10、Windows 路径大小写不敏感) + `components/codexWorkspacePicker.vue`(最近目录一键开新会话)。 **v2.0.3 多会话并行**:`submitting: boolean` → `runningThreads: string[]`, 只拦同一会话重复提交;侧栏运行中呼吸点、工具栏运行中会话数。 **v2.0.4 会话内检索**:粘顶检索条 + 命中计数 + 上/下一条居中跳转;命中判在消息粒度 (不染色文本,避免与 markdown 的 v-html 打架),命中项加左侧强调条。 **踩坑**:Electron 渲染进程**不支持 `window.prompt`** —— 模板命名改成抽屉里的行内表单。 **验证**:`pnpm type:check` 89 条基线 / 本包 0 报错;`pnpm build` exit=0。 (本轮未改 electron 代码,沿用上一轮 tsc + 91 单测 + build-electron 全绿。) ## v2.0.5 案件工作空间联动(唯一一次动 ai-server) **背景事实(值得记住)** - 案件工作空间 = `/QingJian/workspace/<案件ID>`(`PathConst.WORKSPACE.resolve(id)`), `CaseInfoService.create()` 建案时就已经 `FileUtil.mkdir` 建好,duckdb 也放在里面。 - `CaseInfo` 的 `db` / `db_Path` 都标了 `@JsonIgnore`,所以**工作空间一直没回传给前端**。 - 后端 `/case/open` 与 `/case/current` 都会返回 `CaseInfo`,前端 `useUserStore.openCase()` 里 `{...res}` 展开 → 新字段会自动进案件缓存(`CASE_INFO_KEY`),不用改 case store。 **改动** - 后端(3 处纯增量):`CaseInfo` 加 `@TableField(exist = false) String workspacePath`; `CaseInfoService` 加 `workspacePath(caseId)`(顺带 mkdir 兜底)+ `fillWorkspacePath(caseInfo)`, 在 `listByOwner()` / `current()` 返回前调用;`CaseInfoController.open()` 回传前补一次 (该接口原本又 `getById` 取了一次,正好在返回前补)。 - 前端:`plat/api/case/caseApi.ts` 的 `CaseInfo` 加 `workspacePath?: string`; codex store 增 `caseId` / `caseWorkspace` / `cwdOverrides` / `syncCaseContext()` / `activeCwdSource`; cwd 优先级改为 **会话 cwd → 案件手动覆盖 → 案件工作空间 → 客户端默认**; `setDefaultCwd` 删除,改为 `setWorkspaceDir()`(按案件记忆,键 = 案件ID 或 `no-case`, 旧版全局值会迁移到 `no-case` 下);视图与目录弹窗显示来源徽标。 **验证**:前端 `pnpm type:check` 89 基线 / 本包 0 报错 + `pnpm build` exit=0; 后端 `mvn -o -DskipTests compile` exit=0,并用 `javap` 核验新字段/方法进了 class。 ## v2.0.4 补漏:文本级关键词高亮 对照计划复审时发现 v2.0.4 少做了「关键词高亮」(当时只做了命中计数与上下跳转)。 补 `utils/codexHighlight.ts`: - `splitCodexHighlight(text, keyword)` → 切段,给**插值渲染**的地方用(提问气泡、代码块,加 class); - `highlightCodexHtml(html, keyword)` → 给 **v-html** 的 markdown 用,逐段跳过 `<...>` 只在文本里插 ``; 关键词按「原始 + HTML 转义」两种形式各匹配一次(markdown-it 会把 `&` 输出成 `&`)。 - **踩坑认知**:绝对不能在 `onUpdated` 里手工替换已渲染 DOM 的文本节点来高亮 —— Vue 缓存的 vnode 仍指向被替换掉的旧节点,下次 patch 会把新文本写进已脱离文档的节点, 表现为「内容卡住不更新」。v-html 整块重建 innerHTML,所以字符串替换才安全。 ## v2.1.0 可执行计划(只出计划,未实施) 写进 `codex-analysis-plan.md` 第八节(并顺手把该文档编号从乱的「七插在四前」理顺成一→八): - **v2.1.1 运行参数显式化**(sandbox / approvalPolicy 目前是我硬编码的 `workspace-write`+`on-request`, 属实现缺口,最该先修;危险选项要二次确认且不许作为全局默认静默继承) - **v2.1.5 产物增强**(搜索 / 类型筛选 / 本轮变更 + patch 展开;patch 路径解析失败必须静默降级) - **v2.1.2 块级钉到看板**(复用 `codexTextKey` 按内容哈希恢复;折叠时不渲染,最多 6 个) - **v2.1.3 结论快照与对比**(只存文本与表格、不存图;行级 LCS diff) - **v2.1.4 模板 / 会话列表导入导出**(导入的会话本机没有记录,要明确提示,不装作能恢复) - 8.7 记了**评估后不做**的三项(图表联动筛选做不出可靠交互、SQL 直连预览、消息级删除),附理由 - 8.8 汇总 D1–D5 决策点待确认;8.9 登记 v2.2.0 候选 ## v2.1.0 实施完成(v2.1.1 → v2.1.5,五个子版本一次做完) 新增 5 个文件:`components/codexRunOptionsPanel.vue`、`components/codexBoardPanel.vue`、 `components/codexSnapshotPanel.vue`、`utils/codexArtifactChange.ts`、`utils/codexTextDiff.ts`、`utils/codexSnapshot.ts`。 - **v2.1.1**:`sandbox` / `approvalPolicy` 从硬编码改为按会话 + 全局默认;危险取值二次确认; 顶栏出「只读 / 全权访问 / 免审批 / 每步确认」徽标。类型归位到 `types/codexTypes.ts`(api 只转出)。 - **v2.1.5**:`collectCodexArtifactChanges` 按 `*** Add/Update/Delete File:` 段落头提取路径, **认不出就不猜**;产物栏重构为独立容器(`.codex-aside` 交回页面)+ 搜索 + 类型筛选 + 本轮变更。 - **v2.1.2**:看板只存内容哈希(`codexTextKey`),正文按哈希从当前会话找回;找不到就标失效; 折叠时 `v-if` 不渲染(避免图表重复渲染);最多 6 个。 - **v2.1.3**:快照只存文本与表格(不存图,localStorage 配额撑不住);diff 用**统一视图**而非并排 (并排要两列行号对齐,长报告一折行就错位);LCS 限长 1200 行。 - **v2.1.4**:模板与会话列表都能导出 / 导入;导入的会话标 `external`,打开时如实提示「本机没有执行记录」。 **验证**:`pnpm type:check` 89 条基线 / 本包 0 报错;`pnpm build` exit=0。 ### 顺带发现的既有缺陷(不是本次引入) `vite-plugin-theme-vite3` 生成的暗色主题文件里**自定义属性的 `--` 前缀被抹掉** (`dist/assets/app-antd-dark-theme-style.*.css` 里搜 `--codex-bg` / `--ai-text` 均为 0 处, 只有 `codex-bg` / `ai-text`)→ 暗色模式下 `--ai-*`、`--codex-*`、`--jeesite-*` 都不会被覆盖。 对既有 ai 模块与新 codex 模块**同等影响**;修它要动构建期主题插件,风险收益不匹配, 本次只记录不动手(已写进计划文档 9.4)。 ## 合并到 dev_3 - 在 worktree 提交:`ef86c64 feat(codex): 新增 Codex 数据分析模块(v1.0 → v2.1)` (59 文件 / +11570 −391,无二进制、无 node_modules/dist/target)。 - `git -C E:/workspace/zsjz-ai merge --ff-only workbuddy/dev-3-6ce6fa87` → **快进成功**, `dev_3`:`985e7ff → ef86c64`。 - 在**主仓库**(合并后的真实状态)复验:`pnpm type:check` 89 条基线 / codex 0 报错, `pnpm build` exit=0,产出 `codexAnalysis-*.{js,css}`。 - **远端情况**:`origin` 存在(`http://81.68.143.144:9239/vectorSeek/zsjz-ai.git`), 但远端只有 `dev` / `dev_1` / `master`,**没有 dev_3**,`dev_3` 也没配上游 → 这条提交目前只在本地,要不要推送(`git push -u origin dev_3`)由用户决定。 - **注意**:主仓库工作区里有一个**别人暂存未提交**的 `ai-server/QingJian/uy76tkp8/data.db`(`AM` 状态,二进制 DB)以及未跟踪的 `ai-server/QingJian/conf/` —— 与本次改动无关,**没有碰**;若有人在该仓库直接 `git commit` 会把那个 db 一起提交进去,值得提醒。 - Hook 说明:`.git/hooks/post-commit`、`post-checkout` 是 **Qoder AI tracker**(带 `|| true`, 不会让提交失败);它会尝试起 `reg.exe` / `sc.exe`,被 WorkBuddy 沙箱黑名单拦掉,属正常现象。 ## 推送 dev_3 到远端 - `git -C E:/workspace/zsjz-ai push -u origin dev_3` → `* [new branch] dev_3 -> dev_3`, 已建立上游跟踪 `dev_3...origin/dev_3`;远端 ref = `4bf2ad0`(与本地一致)。 - 该分支相对 `origin/master` **领先 78 个提交**(大多是既有开发历史,不只本次 codex 那两条)。 - 推送前用 `git fetch origin --prune` + `git ls-remote --heads origin dev_3` 确认远端原本没有该分支, 并设 `GIT_TERMINAL_PROMPT=0` 防止凭据提示把命令挂住 —— **这是个可复用的做法**。 - 主仓库那个暂存未提交的 `data.db` 用户明确说「不用管」,推送不受影响(push 只发提交,不发索引)。 - 本条笔记**刻意没有提交、没有推送**:用户授权的是「推送 dev_3」这一次对外动作, 为一条笔记再打提交会给他们共享分支添噪音。需要时再补。 ## 工作目录改为代码决定 + 修「已拒绝访问」 **用户两条明确要求**:① 工作空间路径由代码设置,**不允许在界面上修改**;② 修掉 「该目录不是本客户端的会话工作目录,已拒绝访问」。 **报错根因**:`codexArtifactService.requireAllowedRoot` 只接受 `threadStart` / `turnRun` 登记过的目录,而产物栏在**页面进入时**(还没有会话)就拿 `activeCwd`(= 案件工作空间) 去查询 → 必然被拒。v2.0.5 让案件工作空间成为默认 cwd 后,这个时序问题从偶发变成必现。 **修法** - 主进程:`requireAllowedRoot` 改为「不在白名单但**是绝对路径且真实存在的目录**」→ 补登记并放行 (`logger.warn` 记一条),否则报「工作目录不存在或不可访问」。 理由:工作目录现在由代码决定,可能的取值只有案件工作空间与客户端默认目录两种; 真正防越界的仍是 `resolveInside` 的路径穿越校验。白名单里「只有会话用过才登记」的时序假设不成立。 - 前端:**删掉整个工作目录修改入口** —— 删除 `codexWorkspacePicker.vue`、store 的 `cwdOverrides` / `recentCwds` / `setWorkspaceDir` / `rememberCwd` / `workspaceOverride` / `hasWorkspaceOverride` / 无人读取的 `caseId`,cwd 优先级简化为 **会话 cwd → 案件工作空间 → 客户端默认**(纯代码决定);空态工作目录卡片改为只读展示 + 「目录由当前案件的工作空间自动决定」提示;composer 去掉「目录」按钮与 workspace emit。 - `selectDirectory()` 保留在 `codex/api/codexApi.ts`:`ai/views/aiPlugin/index.vue` 还在用(OS 能力,非 codex 专属)。 **验证**:`pnpm type:check` 89 基线 / 本包 0 报错;`pnpm build` exit=0; 主进程 `npx tsc --noEmit` 0 报错 + `npm test` 全过 + `npm run build-electron` exit=0。 (产物服务没有单测覆盖 requireAllowedRoot —— 它依赖 Electron 的 dialog/shell。) ## 移除「检索」与「快照」 用户要求下线这两个功能:删 `codexMessageList` 检索条、`codexMessageItem` 命中态、 markdown/代码块的关键词高亮透传、工具栏两个按钮,删除 4 个文件 (`codexHighlight.ts` / `codexSnapshot.ts` / `codexTextDiff.ts` / `codexSnapshotPanel.vue`)。 净删约 780 行;包 45 → 41 文件。 保留产物栏文件名筛选、模板库搜索、关系图节点搜索(各自的过滤功能,与会话检索无关)。 `listRef` 仍需要(PNG 长图导出要 `getCaptureElement`)。 提交 `ba68d45`;连同前一条 `0a39a60`(工作目录代码决定 + 修拒绝访问)与用户自己的 `6070d6f`,worktree 分支领先 dev_3/远端 3 个提交,**尚未合并推送**(用户未授权本次推送)。 ## 再次合并到 dev_3(含移除检索/快照) - `git -C E:/workspace/zsjz-ai merge --ff-only workbuddy/dev-3-6ce6fa87` 快进成功, `dev_3`:`4bf2ad0 → 396cba5`(含用户自己的 `6070d6f`、工作目录修复 `0a39a60`、移除检索/快照 `396cba5`)。 - 在主仓库复验:`pnpm type:check` 89 条基线 / codex 0 报错;`pnpm build` exit=0, 产出 `codexAnalysis-*.{js,css}`。 - **未推送**:用户本轮只说「合并」,没说推送;`origin/dev_3` 停在 `4bf2ad0`,落后 4 个提交。 上游跟踪已建好(`dev_3...origin/dev_3`),要推就是一句 `git push`。 ## 聊天页不再展示工作空间路径 用户要求「聊天页面上所有地方都不展示工作空间路径」。清理了 5 处: - 工具栏:整块「工作目录」状态(路径 + 复制按钮)删除,含 `cwd` 计算属性、`onCopyCwd`、`__path/__icon` 样式; - 空态:整个工作目录卡片(来源徽标 + 路径 + 提示)删除,连同 `cwdSource/cwdSourceLabel/caseName`; - 产物栏:面包屑的 `:title="root"` 悬浮提示去掉(悬停会露出绝对路径); - 侧栏:建会话成功提示 `已创建会话(路径)` → `已创建会话`; - 输入区:提示语去掉「未设置工作目录」分支。 保留(非展示):store 的 `activeCwd` 仍作为 threadStart/报告导出元数据使用; composer 的 `cwd` prop 仅用于「切会话清空草稿」的 watch,不渲染。 顺带删了无人使用的 `activeCwdSource` getter。 验证:`pnpm type:check` 89 基线 / 本包 0 报错;`pnpm build` exit=0。 ## 删除会话改为弹框确认 原来侧栏的「移除」是行内二次确认(先点移除 → 出现 删除/取消 两个小按钮),改为 `Modal.confirm` 弹框: 标题「删除会话」、正文带会话名、`okText: '删除'` + `okType: 'danger'`(与 `ai/components/SessionList.vue` 的既有风格一致)。 弹框文案明确「只从本机列表移除,Codex 侧已落盘的历史不受影响」,避免用户误以为会删服务器数据。 顺带删掉行内确认的 `pendingDeleteId` 状态与 `__confirm` 样式。 验证:`pnpm type:check` 89 基线 / 本包 0 报错;`pnpm build` exit=0。 ## 移除会话导入导出 + 新建会话入口调整 用户三条要求:① 移除会话导入导出功能;② 放大新建会话按钮;③ 移除顶栏右上角的「新建会话」。 - **导入导出**:侧栏「导出 / 导入」按钮 + 隐藏 file input + `onExportSessions/onImportClick/onFileChange` 全删;store 的 `exportSessionsJson / importSessionsJson` 一并删除(已无调用方)。 `CodexSessionMeta.external` 标记与侧栏「外部」徽标保留(兼容已导入过的存量数据)。 - **新建会话**:侧栏 head 里的「新建」小按钮移出,改成 head 下方**整行主按钮** (`__create`:34px 高、主色虚线边框、hover 变实心主色、创建中禁用并显示「创建中…」)。 - **顶栏**:右上角「新建会话」按钮删除,emit `'create'` 一并移除;视图里工具栏的 `@create` 解绑。 侧栏按钮成为唯一入口。 验证:`pnpm type:check` 89 基线 / 本包 0 报错;`pnpm build` exit=0。 ## 会话防重:不允许堆空白会话 用户反馈「会话可以一直建,没有聊天记录也能一直建」。优化规则(store 新增 `startNewSession`): 1. 当前会话是空的(`isSessionEmpty` = 无提问无回复)→ 不再新建,提示「已有空白会话,直接输入问题即可」(info); 2. 列表里已有空会话 → 直接切过去,不新建; 3. 都没有才真正 `createSession`。 ——保证任意时刻**至多存在一个空会话**。侧栏「新建会话」改走 `startNewSession`; `send()` 的 `ensureSession` 仍直接 `createSession`(首条消息的懒建是合法路径,不受护栏影响)。 **启动清账**:`bootstrap` 里新增 `pruneEmptySessions()` —— 历史版本允许无限建, 存量的一堆空会话在下次打开页面时自动清理(只保留最新一个,其余从列表移除, 不碰 Codex 侧落盘历史,与 removeSession 同一原则)。 验证:`pnpm type:check` 89 基线 / 本包 0 报错;`pnpm build` exit=0。