[Bug] 运行时 manifest 获取失败时,已安装运行时仍无法离线启动
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 401
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
## 问题描述
macOS arm64 正式版 Cindy 在启动阶段获取运行时 manifest 失败时,即使本机已经安装并通过校验 Claude Code、Codex、Pi 和内置 ripgrep,仍会停留在 Splash 页面,只显示“环境初始化失败”和“重试”,无法进入应用。
这会把“启动时暂时无法访问 CDN / 代理隧道失败”和“本地运行时确实损坏”混成同一个阻断错误。
## 环境
- Cindy 版本:0.1.38(本地正式打包版本)
- 平台:macOS 26.5.1,Apple Silicon(arm64)
- 当前线上 manifest 的运行时版本:
- Claude Code 2.1.219
- Codex 0.145.0
- Pi 0.83.0
- 安装方式:macOS 正式打包版本
## 复现步骤
1. 保留已经安装好的运行时二进制,确保对应目录中存在 `.verified` 标记。
2. 让 Cindy 启动时无法通过 Electron 网络层获取 `manifest-darwin-arm64.json`,例如代理隧道失败或临时断网。
3. 启动 Cindy。
4. 观察 Splash 页面。
## 实际结果
页面停留在:
> 环境初始化失败
只有“重试”按钮,没有失败组件、错误码、manifest 状态或离线启动选项。重试会重复进入相同流程。
本次日志中的关键记录:
```
[clientEndpoints] fetch failed (ERR_TUNNEL_CONNECTION_FAILED | net::ERR_TUNNEL_CONNECTION_FAILED) for https://hotfix.cindy.app/cindy/endpoint.json
[clientEndpoints] starting with cached endpoint manifest automatically after network failure
[updateService] Manifest fetch failed, falling back to local patch
```
本机检查确认以下运行时文件存在、具有可执行权限,并且可以直接执行版本检查:
- Claude Code 2.1.219
- Codex 0.145.0
- Pi 0.83.0
- 内置 ripgrep 15.1.0
因此这次不是“运行时文件不存在”或“二进制无法执行”,而是启动链把在线 manifest 获取失败升级成了环境初始化失败。
## 期望结果
1. 如果本地已有通过 `.verified` 校验的运行时,启动时应允许离线进入应用,manifest 更新放到后台重试。
2. 如果确实必须在线获取 manifest,应明确显示:
- 是 manifest 获取失败还是二进制准备失败;
- 失败的 agent / 组件;
- 错误码和网络原因;
- 可执行的恢复方式。
3. Pi 是可选运行时,Pi 准备失败不应阻断 Claude/Codex 主流程。
## 初步源码定位
当前实现存在两个问题:
1. `apps/desktop/src/main/agent-binaries/factory.ts` 的 `prepare()` 在本地没有内存 manifest 时直接调用 `fetchManifest()`;获取失败就返回 `manifest_failed`,不会先根据本地已验证版本启动。
2. `apps/desktop/src/renderer/contexts/EnvCheckContext.tsx` / `hooks/useSplash.ts` 将 `allPassed === false` 统一映射为 `splash_failed`,而 `SplashScreen.tsx` 对该状态只渲染通用标题和“重试”按钮,没有展示 `EnvCheckResult` 中已有的错误详情。
相关代码:
- https://github.com/makecindy/cindy/blob/main/apps/desktop/src/main/agent-binaries/factory.ts
- https://github.com/makecindy/cindy/blob/main/apps/desktop/src/main/bootstrap-electron.ts
- https://github.com/makecindy/cindy/blob/main/apps/desktop/src/renderer/contexts/EnvCheckContext.tsx
- https://github.com/makecindy/cindy/blob/main/apps/desktop/src/renderer/hooks/useSplash.ts
- https://github.com/makecindy/cindy/blob/main/apps/desktop/src/renderer/components/splash/SplashScreen.tsx
## 建议验收标准
- [ ] 已验证的本地 Claude/Codex 运行时在 manifest 暂时不可用时仍可启动。
- [ ] manifest 不可用时不覆盖或删除现有可用运行时。
- [ ] 环境检查失败页显示具体组件、错误码和恢复建议。
- [ ] 网络失败、manifest 缺字段、下载失败、二进制损坏分别显示不同状态。
- [ ] Pi 失败继续保持非阻断降级。
- [ ] 增加断网 / 代理隧道失败场景的启动测试。
Contributor guide
Research direction
Start with prepare() in apps/desktop/src/main/agent-binaries/factory.ts, then trace the result through bootstrap-electron.ts, EnvCheckContext.tsx, useSplash.ts, and SplashScreen.tsx. Check how manifest failures and allPassed are represented before running or adding startup tests for offline and tunnel-failure cases. Done means verified local runtimes can start offline, failure details are shown distinctly, existing runtimes remain intact, and Pi remains non-blocking.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, typescript
- Domain
- desktop, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100