# Tianshu-mcp 日志台(GUI) 英文版:[gui-log-viewer.en.md](gui-log-viewer.en.md) Tianshu-mcp 日志台(英文名 **Tianshu-mcp Logs**)是一个**本地只读**的桌面应用,把天枢 MCP 落盘的四类日志与任务产物统一到一个界面里查看。它**不依赖 MCP server 在运行**,直接读文件系统。 - 代码位置:`mcp-gui/`(独立工程,独立版本号,独立 CI) - 技术栈:Tauri 2.x(Rust 后端)+ Vue 3 + Vite + TypeScript - 与 MCP 主包的关系:**只读消费方**,不替代 `query_task` / `get_task_report`(那两个面向机器与天枢),也不改动任何业务数据 --- ## 一、安装 产物来自 CI(GitHub Actions 的 `GUI` workflow): - **正式使用**:从 GitHub 或 Gitee 的 **Release**(tag 形如 `gui-v0.1.0`)下载安装包; - **预览版**:tag 含 `-beta.` / `-rc.` 的预发布同样发布在两个仓库,但**带 Pre-release 标记**; - **验证构建**:从对应提交的 Actions 运行页下载 `gui-*` workflow artifact。 | 平台 | 安装包 | 自动更新载体 | |---|---|---| | Windows | `.exe`(NSIS 安装器) | 同一 `.exe` 直接被 updater 复用(配套 `.exe.sig` 签名;**Tauri updater 不支持 MSI**,故未提供 MSI) | | macOS | `.dmg` | `.app.tar.gz`(**按架构区分为 `*_darwin-aarch64.app.tar.gz` / `*_darwin-x86_64.app.tar.gz`**) | > 自动更新载体的命名取决于 `bundle.createUpdaterArtifacts`:本项目用 v2 原生模式(`true`), > Windows 侧不额外打包 `.nsis.zip`,而是直接复用 NSIS 安装器(`*-setup.exe` + `*-setup.exe.sig`); > macOS 侧为 `.app.tar.gz` + `.sig`。 > 注意 Tauri 产出的 macOS 载体名**不含架构**,两个架构会同名相互覆盖,故发布前由 CI **按平台重命名**以保证唯一。 > > 产物基名取自 `bundle.productName`,本项目刻意使用 **无空格** 的 `Tianshu-mcp-Logs`(界面显示名仍是「Tianshu-mcp 日志台 / Tianshu-mcp Logs」): > GitHub 会**重命名**含空格等非字母数字字符的发行资产名(空格→点),而 Gitee 原样保留,两端不一致会让更新清单里的下载地址失效。 > CI 另有一道断言,产物名含非 `[A-Za-z0-9._-]` 字符即直接失败。 > > **`bundle.productName` 一经发布即冻结,不要再改**:NSIS 的「设置 → 应用和功能」卸载项注册表键由它拼成 > (`…\CurrentVersion\Uninstall\`),一旦改名,新安装器就不再认得旧安装 —— 旧版本既不被覆盖也不被卸载, > 于是列表里留下两条记录、旧目录与旧快捷方式留在磁盘上(`0.1.0-beta.1 → 0.1.0-beta.3` 之间的真实事故)。 > 为此 `mcp-gui/src-tauri/windows/installer-hooks.nsh`(经 `bundle.windows.nsis.installerHooks` 挂载)在安装前执行 > `NSIS_HOOK_PREINSTALL`:检测到历史遗留名称的卸载项时,静默运行它自己的卸载器,再兜底清除残留的注册表键与快捷方式。 > 该钩子只在检测到旧名称时动作,**全新安装与当前名称的版本更新完全不受影响**,也**不会删除用户数据** > (NSIS 卸载器的「Delete app data」复选框只在交互模式下才置位,静默卸载不触发)。 > **macOS 未做 Apple 代码签名与公证**:首次打开可能需要在「系统设置 → 隐私与安全性」中手动允许。 > 这不影响功能,也不影响自动更新——更新包的完整性由 minisign 签名校验保证。 --- ## 二、数据目录 应用启动时按与 MCP server **完全相同的规则**解析默认数据目录: 1. 环境变量 `TIANSHU_MCP_HOME`(非空即用); 2. 否则 `~/.tianshu-mcp`。 侧栏底部的数据目录区可: - 在多个数据目录之间**切换**; - **追加**目录(校验规则:目录下必须存在 `logs/` 或 `tasks/`,否则明确拒绝并给出原因); - **移除**已追加的目录。 追加的目录列表持久化在**系统应用配置目录**(如 Windows 的 `%APPDATA%`、macOS 的 `Application Support`),**不写入项目目录**。 --- ## 三、四类日志与整页视图(洞察 / MCP 能力) | 数据源 | 路径(相对数据目录) | 界面位置 | |---|---|---| | 全局运行日志 | `logs/server.log` | 工作区 · 运行日志(侧栏「运行日志」进入) | | 任务事件流 | `tasks//task.jsonl` | 工作区 · 事件流 | | 原始执行日志 | `tasks//agent-<轮次>.log`、`verify-<轮次>.log` | 工作区 · Agent 日志 / 验收日志 | | 验收报告 | `tasks//report-<轮次>.{md,json,html}`、`dry-run-report-<轮次>.{md,json}` | 工作区 · 验收报告 | ### 3.1 任务概览 - **界面是一条常驻左侧栏 + 四个整页**:**任务概览页**、**洞察页**、**MCP 能力页**与**全屏工作区**;侧栏自上而下为「品牌 → 全局搜索 → 主导航(任务列表 / **洞察** / 运行日志 / **MCP 能力** / **设置** / **命令面板**)→ **数据目录**(该行内含 **刷新** 与「添加目录 / 移除」三个图标按钮)→ 任务分区导航」, 概览页自上而下为「五格指标仪(任务总数 / 进行中 / 已结束 / 已成功 / 已失败)→ 状态圆片行 → 任务卡网格」;**设置入口与数据目录同属主导航区**(数据目录紧接「设置」下方,上沿一条分组线),**侧栏底部不常驻任何控件**;品牌行只有应用名,无装饰色块; - 任务以**卡片**呈现:左侧竖条为状态色(按 `statusTone` 取语义色),标题前有 `›` 前缀,卡片内含状态标签、`agent · taskId`、`更新时间 · 轮次 [· 报告轮次]` 与干跑标记; - 顶部**状态圆片**(全部 / 执行中 / 已成功 / 已失败 / 需注意 / 待人工)是**页内分组浏览**,不会改写筛选条件; - `[筛选]` 浮层内提供完整条件:关键字、Agent、状态、项目、起止时间、**错误类型 / 干跑 / 返修 / 视觉验收**(0.1.1-beta.2 起),只看进行中,以及排序(更新时间 / 创建时间 / 任务 ID + 升降序)与「清空筛选」;其中错误类型选项来自当前任务集合的**分面**(不硬编码枚举),三项布尔条件均为「全部 / 是 / 否」三态,口径与任务快照字段一一对应(返修 = `roundsUsed > 1`,视觉验收 = 存在 `report-<轮次>.html`); - 覆盖 `tsk_*`(派单任务)与 `vfy_*`(独立路径验收记录);点卡片进入该任务的**全屏工作区**。 ### 3.2 事件流 - 解析 `task.jsonl` 为时间线,**区分状态跃迁事件与细粒度 Agent 事件**(`task_dispatched` / `confirmation_dialog_detected` / `awaiting_user_authorization` / `file_modification_started` / `rework_triggered`); - `note` 事件仍是**进度 / 审计通道**,界面单独标注,不与语义事件混为一谈; - `file_modification_started` 是**启发式推断**(适配器并不直接观测文件系统),文案如实保留「可能开始改动文件」; - 无法解析的坏行**跳过但计数**并在顶部明确提示,不静默丢弃。 ### 3.3 原始日志 - 首屏只读**尾部窗口**(64 KiB),向前按块加载,并显示「已加载 N / 共 M」; - 级别过滤(`DEBUG/INFO/WARN/ERROR`)、关键字高亮、行号与自动换行开关; - **实时跟随**:文件被追加时自动增量刷新;**手动向上翻阅会自动暂停跟随**(不会被强行拉回底部),右下角显示状态,可一键「跳到最新」。 ### 3.4 验收报告 - `report-<轮次>.md`:Markdown 渲染; - `report-<轮次>.json`:结构化卡片(检查项通过 / 失败 / 跳过、耗时、退出码、输出尾部、`changedFiles`、`diffstat`、`analysis.signals`、阻塞问题); - `report-<轮次>.html`(视觉验收):在 **sandbox iframe** 中渲染——**禁用脚本、注入 CSP 阻断一切外部资源、不联网**; - `dry-run-report-*` 与 `report-*` **分开展示**(两者结论口径不同:静态分析 vs 真实命令验收); - 多轮报告可并排**对比**。 ### 3.5 MCP 能力视图 侧栏「**MCP 能力**」进入一个整页,只读展示 `tianshu-mcp` 的**工具面**(当前 13 个工具),按能力三族分组: - **read**(读 / 查询,无副作用,免审批)、**write**(有副作用,全部需审批)、**execute**(执行项目侧命令但不改源码,免审批); - 每个工具给出名称、审批标记(需审批 / 免审批)与一行用途说明;issue #28 的等待原语 `wait_task` / `wait_any` 即在此列出。 数据源是 GUI **内嵌的镜像清单**(`mcp-gui/src/core/capabilities.ts`),**不读文件系统、不连 MCP server**——打开即有内容,与当前数据目录无关。该镜像与真源 `src/mcp/tools.ts` 的 `TOOL_DEFS` 由 `mcp-gui/scripts/check-schema-parity.mjs` 在 CI 中**逐项比对**(名称 + capability + requireApproval),任一漂移即失败。 ### 3.6 洞察页(效能看板 / 失败归因 / 时间趋势) 侧栏「**洞察**」进入一个整页(0.1.1 起),只读统计 `tasks/` 下的历史任务与**最新一轮**验收报告: | 分区 | 内容 | |---|---| | 效能看板 | 按 Agent 与按项目两张表:任务数 / 成功率 / 平均轮次 / 一次通过率 / 平均验收耗时 / 报告缺失数 | | 失败归因 | 四类归因 TOP 列表(`errorType` / `failedCheck` / `blockingIssue` / `signal`),带占比条 | | 时间趋势 | 按天 / 按周切换;任务量柱状 + 成功率与返修率折线(纯内联 SVG,不引图表库) | | 任务对比 | 勾选 2–4 个任务并排看指标(0.1.1-beta.2 起,见 §3.7) | | 磁盘占用 | 产物占盘合计 / `logs/` 占比 / 任务体积 TOP 20 / 「可清理」提示(0.1.1-beta.3 起,见下) | **磁盘占用(0.1.1-beta.3 起)的口径**: - 只统计 `logs/` 与 `tasks/` 下**所有**子目录的**实测体积**,**不按 `tsk_` / `vfy_` 前缀过滤**——磁盘占用要如实反映实际占盘, 过滤前缀会把真实占用算漏;这与「任务列表」的扫描口径**有意不同**,界面上已标注; - **只 `stat` 文件、不读内容、不删除任何文件**;`top_tasks` 只放任务目录前 20,`logs/` 单列; - **「可清理」提示的判据全部是相对口径**(具名常量在 `mcp-gui/src/core/insights.ts` 顶部): 单个文件占该任务 **> 50%**,或该任务体积 **≥ 全部任务体积中位数的 2 倍**(中位数只按任务算,不含 `logs/`); - **只有提示,没有删除入口**:本页与后端命令都不提供任何清理 / 删除能力。 **口径(看数前必读)**: - 日期按 **UTC** 切分(取 `createdAt` 前 10 字符);**按周以周一为一周之始**(ISO-8601),跨月 / 跨年的同一周归并; - **「一次通过」= 成功且仅用 1 轮**;**返修率 = `roundsUsed > 1` 的任务数 / 该桶任务总数**; - **只统计每个任务的最新一轮报告**(快照 `reportRound`,否则取最大 `report-<轮次>.json`)——返修后才成功的任务,其更早一轮的失败**不进归因**; - **平均验收耗时**只统计起止时间**都可解析**的轮次(解析失败不进分母,不编造); - **分母为 0 显示 `—`**,不显示 0%; - **只读**:不写任何业务数据,**不提供删除 / 清理**;解析失败的报告如实计为「解析失败 N 份」。 取数:Tauri 命令 `get_insights`(`mcp-gui/src-tauri/src/insights.rs` + `timestamps.rs`)一次扫描并只做**计数与求和**; 比率 / TopN / 趋势补齐 / 周历聚合在前端 `mcp-gui/src/core/insights.ts` 的纯函数里完成(可单测)。 进入页面加载一次,页内有「刷新」,切换数据目录自动重载,**不做实时监听**。 ### 3.7 任务对比(0.1.1-beta.2 起) 洞察页第 4 个子分区「**任务对比**」:左侧勾选任务(复用任务列表数据 + 关键字过滤,上限 **4 个**,超出时明确提示、 **不顶替**已勾选项),右侧是**并排指标矩阵**(列为任务,行为指标):状态 / Agent / 项目 / 已用轮次 / 验收耗时 / 改动行数(+ / -)/ 改动文件数 / 最新报告判定 / 失败检查项 / 错误类型 / 结论说明。轮次与耗时两行会标注**横向最优**。 - **报告按需读取**:勾选某任务时才读它最新一轮 `report-<轮次>.json`(优先快照 `reportRound`),结果按 `taskId` 缓存; 切换数据目录会清空勾选与缓存。 - **缺失一律 `—`**:无报告时判定与改动统计留空,耗时回退任务快照的 `createdAt → finishedAt`,两者都不可解析则留空 ——**不编造时长、不编造最优**。 - **只读**:只读 `task.json` 与 `report-*.json`,不写任何业务数据。 ### 3.8 命令面板与快捷键(0.1.1-beta.2 起) | 快捷键 | 行为 | |---|---| | `Ctrl/Cmd + K` | 打开 / 关闭命令面板(侧栏也有常驻入口) | | `Esc` | 关闭命令面板 | | `Ctrl/Cmd + R` | 刷新任务列表(已拦截,不会触发 webview 重载) | 面板输入即做**子序列模糊匹配**(不要求连续,但字符顺序一致),`↑` `↓` 移动、`Enter` 执行;面板打开时**不触发**其他全局快捷键, 带 `Alt` / `Shift` 的组合一律不拦截。命令全部复用既有数据与动作:跳任务列表 / 洞察 / 运行日志 / MCP 能力、跨任务全局搜索、 打开设置、切换数据目录(当前目录带标注)、直接打开某个任务。**命令面板不新增任何写操作。** **任务命令按输入实时检索、没有条数上限**(`buildTaskCommands` 每次输入时遍历全部任务;空输入只列导航与数据目录, 不铺任务列表);结果列表的 `PALETTE_RESULT_LIMIT = 20` **只限制展示条数,不限制检索范围**,输入再具体一些即可收敛到目标任务。 > 版本提示:`0.1.1-beta.2` 为「预生成前 50 条」的旧行为;**实时检索自 `0.1.1-beta.3` 起生效**。 ### 3.9 基线分区(0.1.1-beta.3 起) 工作区任务分区第 5 项「**基线**」,只读展示该任务派单时保存的 `baseline.json`: - 是否版本库 / HEAD(短哈希)/ 动工前是否已脏 / 动工前已有改动与未跟踪文件数 / 采集时间 / 基线说明; - **与最新报告对照**:报告里本次实际改动(文件数、`+N / -M`)与基线里的「已有改动」并排,便于判断改动是否越界; - 报告**按需读取**(优先快照 `reportRound`,否则取最大轮次的 `report-.json`),结果按任务缓存; - **没有基线就如实说**:缺失或损坏一律提示「该任务没有保存的动工前基线」,**不编造零值**。 ### 3.10 事件流阶段视图(0.1.1-beta.3 起) 事件流工具栏新增 **列表 / 阶段** 分段切换: | 视图 | 数据来源 | 说明 | |---|---|---| | 列表 | **尾部窗口**(默认 64 KiB,可向前加载) | 行为与之前完全一致(大文件友好) | | 阶段 | **全量** `task.jsonl`(`full = true`,按需读一次) | 状态跃迁甘特,条宽按占阶段总时长比例(纯 div) | - 阶段 = **相邻两次状态跃迁之间**(`created → started → verify_start → fix_start → … → succeeded/failed`); - 最后一段没有结束时间时标 **「进行中」并用斜纹区分**,**不编造时长**; - 全量读取后若开头仍不是 `created`(文件被截断),**如实提示**阶段视图可能不完整; - 有坏行时按视图各自计数并在顶部提示(列表 / 阶段互不混淆)。 ### 3.11 深链 `tianshu://task/<任务ID>`(0.1.1-beta.3 起) - **冷启动与热启动都支持**:URL 会进入 Rust 侧队列并发出 `gui://deeplink` 信号,前端**取走队列**后解析路由 (冷启动时事件早于前端监听,故**队列才是事实来源**,不会丢链接); - **单实例**:第二次启动不新开窗口,而是**唤出已有窗口**并把 URL 转给首实例; - **协议注册**在启动时进行,**失败不阻塞启动**(macOS 返回 `UnsupportedPlatform` 属预期); - **只认 `tianshu://task/`**,任务 ID 走字符白名单 `[A-Za-z0-9_-]`(深链是外部输入,ID 会拼进文件路径); 含 `..` / `%2e` 的写法**直接拒绝**(URL 归一化会把 `..` 消掉,归一化后就再也看不出原样);**畸形百分号编码** (如 `%zz`、`%80`)同样按「无法识别」处理并如实提示——解码失败只返回「无法识别」,**不抛错**(抛出会中断 后续深链订阅,见 issue #33);无法识别的链接在界面上**如实提示**,不静默丢弃; - **平台差异**:Windows / Linux 走系统协议注册(冷启动可用);macOS 的系统级冷启动注册由插件返回「不支持」, **以热启动路径为准**——差异如实记录,不做「看起来都支持」的表述。 --- ## 四、跨任务搜索 / 导出 / 复制 ### 4.1 全局搜索 - **在侧栏**:搜索框聚焦即回到概览页并进入**搜索模式**(主体从任务卡网格换成结果列表);`[任务列表]` 按钮回到卡片网格; - 范围可选:事件流 / Agent 日志 / 验收日志 / 验收报告 / 运行日志(另有区分大小写开关); - 结果按「任务 → 文件 → 行」分组,显示命中片段,点击即跳转到对应视图与位置(并进入该任务的**工作区**); - **按需扫描,不建本地全文索引**;带进度反馈且**可取消**。 ### 4.2 导出 - **单文件导出**:导出当前查看的日志 / 报告原文(工作区面包屑右侧 `⇩ 导出`); - **任务整包导出**:把整个 `/` 目录打成 zip,可选**排除体积大的原始日志**(`agent-*.log` / `verify-*.log`),被排除的文件数如实回报;该开关位于**任务摘要带**展开区。 ### 4.3 复制 任务 ID(面包屑右侧)、任务目录绝对路径(摘要带展开区)、当前日志全文(日志工具条),均可一键复制。 --- ## 五、界面语言与主题 - **中英双语**可切换,默认中文; - 主题三选一:**跟随系统 / 浅色 / 深色**,默认跟随系统(**深色是设计基准**)。 ### 5.1 设计系统与界面版式 - **自研设计系统「黑曜石终端」,不依赖任何 UI 组件库 / CSS 框架 / 动画库**;色值 / 字体 / 间距 / 圆角 / 动效 / 布局常量的**唯一真源**是 `mcp-gui/src/styles.css`(`src/theme/index.ts` 只负责把主题模式写成 ``)。 - **布局范式是「常驻左侧栏 + 两态内容区」**:左侧栏(品牌 · 全局搜索 · 主导航(任务列表 / 运行日志 / 设置)· **数据目录(该行含刷新 / 添加目录 / 移除三个图标按钮)** · 任务分区导航)恒在,右侧内容区在**任务概览页**(指标仪 · 状态圆片 · 任务卡网格 · 搜索模式)↔ **全屏工作区**(面包屑 · 任务摘要带 · 内容)之间切换;`server.log` 是工作区的第二形态。**任务分区导航与主导航同处一条侧栏——页面里不存在第二层左栏;没有常驻任务栏、没有常驻详情栏、没有横排标签页**。**设置与数据目录都在主导航区**(数据目录紧接「设置」下方),**侧栏底部不常驻任何控件**;「刷新」是**全局动作**,放在数据目录行内,两态内容区都能用。 - **设置面板是居中弹窗**(不是右侧抽屉):自窗口中央浮出、四周留白并带 4px 圆角,超高时面板内部滚动;点遮罩空白处关闭。 - **配色**:深色为默认(锂黑 `#0B0D0C` 画布 + 荧绿 `#3DFFA0` 点缀),浅色为同语言的「米白终端」;**强调色只用于选中 / 主操作 / 焦点 / 面包屑返回项**,状态位只用语义色(成功 / 失败 / 需注意 / 待人工 / 已终止),两者靠位置与形状区分。 - **字体**:**仅系统内置字体**,等宽主导(Windows `Cascadia Mono`、macOS `SF Mono`,中文回退 `PingFang SC` / `Microsoft YaHei UI`)——**不下载、不外链任何字体**,完全离线可用。 - **图标**:全部为内联 SVG(**不使用 emoji**),随文字颜色自动着色。 - **版式记忆点**:侧栏左沿与面包屑栏上沿(概览页为指标仪上沿)各一条 1px 荧绿细线;侧栏导航项左侧 3px 强调脊标出当前位置;任务卡左侧 3px 状态脊 + 标题 `›` 前缀;状态用**方括号标签**(`[OK] 已成功`);面板圆角 4px、标签与输入 3px;深色底极淡点阵、浅色底极淡网格。 - **读数一致性**:计数、时间、行号、字节、轮次、退出码全部采用等宽数字(`tabular-nums`),数值跳动时不会左右抖动。 - **可访问性**:键盘 `Tab` 可达全部可交互元素并显示焦点环(任务卡与导航项是原生按钮,可 `Enter` / `Space` 触发);图标按钮均带 `aria-label`;遵循系统「减少动态效果」设置。 ### 5.2 系统托盘与关闭行为 - **常驻系统托盘**(Windows 通知区域 / macOS 菜单栏),图标**复用应用图标**,不额外引入资源。 - **右键托盘图标**弹出两项菜单:**显示日志台** / **退出日志台**;菜单文案**随界面语言切换**(在设置面板切换中英后立即生效,**无需重启**)。**左键单击**托盘图标 = 显示并聚焦窗口(不弹菜单)。 - **默认关闭窗口 = 缩小到托盘**:点窗口 X 只是把窗口收进托盘,**应用继续在后台运行**(日志跟随等能力不受影响);可由托盘菜单重新唤出或退出。 - 设置面板的**「关闭窗口」**二选一(默认「缩小到托盘」): - `缩小到托盘` —— 点 X 隐藏窗口到托盘,应用继续运行; - `关闭应用` —— 点 X 直接彻底退出,托盘图标一并消失。 - 托盘菜单的 `退出日志台` 与设置里的 `关闭应用` **都是彻底退出**(不驻留后台)。 - macOS 额外处理:窗口收进托盘后,点 **Dock 图标**会重新显示窗口。 ### 5.3 更新日志面板 - **启动静默检查**:应用启动后静默检查一次更新(不阻塞首屏);命中新版本且**该版本未被忽略**时, 自动弹出更新窗口「更新日志」。检查在界面挂载之后异步进行,失败**不打扰**日志阅读主流程; - **窗口尺寸**:按维护者批注(默认窗口 1440×900 下 ≈1070×750)实现为 `min(1070px, 92vw)` × `min(750px, 88vh)` 的居中浮层; - **三个动作**: - `下载并安装` —— 走本次选定的源直接下载安装(Windows 会启动安装器;macOS 需重启应用); - `忽略此版本` —— **只压自动提示**:该版本不再自动弹窗,但手动点「检查更新」**仍会展示**; 记录持久化在应用偏好中,**出现更高版本时会重新提示**; - `稍后` —— 只关窗,下次启动仍会提示; - **更新源如实展示**:每次检查都并发实测 Gitee / GitHub 并按可达性与延迟择优,窗口内写明 「本次将通过哪个源更新」及其探测结果(可达毫秒数 / 不可达);两端都不可达时给出降级提示; - **release 正文**:窗口正文即该版本的发行说明(由 `docs/release-gui-v<版本>.md` + `.en.md` 合成的双语正文, 经 Markdown 渲染,禁用内联 HTML),因此**更新前就能读到改了什么**; - **手动入口保留**:设置面板的「检查更新」也会打开该窗口——手动结果**一律展示**,不受「忽略」影响。 --- ## 六、双源自动更新(Gitee / GitHub) ### 6.1 更新源如何选择 **主动实测,不依赖系统区域 / 时区**(VPN 场景下区域不可信): 1. 检查更新时并发探测两个更新清单端点,按「可达性 + 延迟」择优; 2. 探测结果按 TTL 缓存,避免频繁探测拖慢启动; 3. 两端都不可达 → 回退**上次成功使用的源**;无历史则回退 GitHub,并明确提示降级; 4. 设置面板提供三态开关:**自动 / 强制 Gitee / 强制 GitHub**(默认自动),便于网络异常时自救。 | 源 | 清单端点 | |---|---| | GitHub | `https://raw.githubusercontent.com/lanlan0811/tianshu-mcp/master/update/gui/latest.json` | | Gitee | `https://gitee.com/lan0811/tianshu-mcp/raw/master/update/gui/latest-gitee.json` | 典型现象:中国大陆网络命中 **Gitee**;境外(含中国香港、中国台湾)命中 **GitHub**。 ### 6.2 签名与失败处理 - 更新包使用 **minisign 签名**,应用内置公钥校验,**验签不通过一律拒绝安装**(这是 Gitee 侧免遭中间篡改的底线); - 检查 / 下载 / 安装任一步失败都**不影响日志查看主流程**,设置面板同时给出「手动下载」入口; - 「手动下载」**跟随本次实际使用的更新源**:命中 Gitee 就给 Gitee 发行页,命中 GitHub 就给 GitHub 发行页(未知源回退 GitHub)——与面板上显示的「更新源」一致,**不会把大陆网络下的用户又送去 GitHub**; - 该入口用**系统默认浏览器**打开(并非在应用窗口内跳转;Tauri 的 webview 会拦截新建窗口请求,故应用内置了受白名单约束的外部打开能力,**只允许 `github.com` 与 `gitee.com` 两个域名**);打开失败只在界面上报错,不影响日志查看。 - 更新通道:正式版与预览版**共用同一份清单**(`update/gui/latest.json` 与 `latest-gitee.json`), 自动更新按语义版本比大小(`0.1.1-beta.1 > 0.1.0`),因此两条线互通、升级路径不分叉; 发行页是否带 Pre-release 标记由 tag 形态决定(含 `-beta.` / `-rc.` 即为预览版)。 ### 6.3 未配置更新公钥时 若 CI 未配置 `UPDATER_PUBKEY`,安装包里保留占位公钥,应用会在设置面板明确提示「自动更新暂不可用」。此时安装包本身**仍然可用**,只是不能自动更新——手动下载覆盖安装即可。 --- ## 七、本地开发与自行构建 ### 7.1 只做前端预览(推荐) 前端可完全离线调试,**不需要 Rust 工具链**: ```bash cd mcp-gui npm install npm run dev # Vite 开发服务器(端口 1420) ``` > **端口 1420 被占用时**:`dev` 前会跑一次 `predev` 守卫(`scripts/dev-port-guard.mjs`),直接打印占用者 > 与结束命令(Windows `netstat -ano | findstr :1420` + `taskkill /PID /T /F`;macOS/Linux > `lsof -ti :1420 | xargs kill`),而不是抛一段 EADDRINUSE 堆栈。端口是**故意固定**的——Tauri 的 > `devUrl` 写死 `http://localhost:1420`,静默换端口会让宿主连不上;只跑前端预览时可临时换端口: > `npm run dev -- --port 1421`。 不在桌面运行时时,`src/api/` 会自动切换到 **mock 数据出口**,数据来自 `mcp-gui/fixtures/`(真实日志样本,已脱敏),因此筛选、搜索、报告渲染、语言与主题等交互都能在没有后端的情况下完整验证。 前端质量门禁: ```bash cd mcp-gui npm run typecheck # vue-tsc --noEmit npm run lint # eslint . --max-warnings 0 npm run test # vitest run npm run check:schema # TS 真源 ↔ 前端镜像 ↔ Rust 镜像 词表一致性 ``` ### 7.2 Rust / Tauri 侧一律在 CI 构建 按 issue #25 的硬约束:**开发机不执行 Rust 侧构建与检查**(`cargo fmt` / `clippy` / `tauri build` 全在 `GUI` workflow 完成)。这样做的原因是避免「本地环境绿、CI 红」的假信号。 CI 会依次执行: 1. `mcp-gui` 前端 `typecheck` / `lint` / `test`; 2. 用 `tauri icon` 从 `assets/tianshu-mcp-icon.svg` **生成图标**(仓库只保留 SVG 源); 3. 从 `TIANSHU_UPDATER_PUBKEY` 注入更新公钥(可选); 4. `cargo fmt --check` / `cargo clippy -- -D warnings` / `cargo test`; 5. `tauri build`(Windows 出 NSIS,macOS 出 dmg + `.app.tar.gz`)。 因此: - `mcp-gui/src-tauri/icons/` 与 `Cargo.lock` **不入库**(由 CI 生成); - 想自行打包,请直接复用 CI 产物,或自行准备 Rust 工具链后在本机执行 `npx tauri build`(本项目不以此为验收依据)。 **实测状态(2026-09-27)**:`GUI` workflow 已跑通——`schema-parity` ✅,三平台 `cargo fmt --check` / `clippy -- -D warnings` / `cargo test` 全绿, 且**三平台(`windows-x86_64` / `darwin-x86_64` / `darwin-aarch64`)全部 success**,均完成 `tauri build` 打包并上传产物。 打包时若未配置签名密钥,workflow 会自动降级为 `--config '{"bundle":{"createUpdaterArtifacts":false}}'`:**安装包照常产出,自动更新不可用**(设置面板会明确提示)。 **手动触发(Actions 页的 `Run workflow`)语义**:**无条件构建**三平台矩阵(不看你最近提交改了什么), 适合「本地什么都不想改、但想跑一次构建 / 验证 Secrets」。push 与 PR 才会做变更过滤(仅当 `mcp-gui/**`、两个词表真源或 `gui.yml` 自身有改动时才构建), 以免纯文档提交也占满三平台 runner。 ### 7.3 目录结构 ```text mcp-gui/ ├── src/ Vue 3 前端(views / components / stores / i18n / theme) │ ├── api/ 唯一数据出口(Tauri invoke 封装 + 可切换 mock) │ └── core/ 纯逻辑(日志行解析 / 事件解析 / 字节窗口 / 筛选 / 报告摘要 / 沙箱 / 洞察 / 阶段切分 / 深链解析) ├── fixtures/ 真实日志样本(脱敏),供 mock 与单测使用 ├── scripts/ 一致性检查、图标公钥注入、更新清单生成 └── src-tauri/ Rust 后端(data_home / scanner / event_stream / tail / baseline / diskscan / watcher / search / export / updater) ``` --- ## 八、安全边界 - 对业务数据**全程只读**;唯一的写入是应用自身偏好(系统应用配置目录)与用户显式选择的导出 / 更新临时文件; - 所有「相对数据目录」的路径都做**越界防护**(拒绝绝对路径与 `..`); - 视觉验收 HTML 在 sandbox iframe 内渲染,且额外注入 CSP、剥离 `