[BUG] ��Դ������ electron:dev ʱ�����Ȼ�������ط�������ʧ�� / Failed to fetch������CORS Ԥ�챻 H5 access �Ž��ܾ�
提交前确认
- 我已经阅读 README 常见问题
- 我已经升级到最新版本后复现过这个问题(
main@0676c194) - 我已经搜索过现有 issues,确认没有重复问题
- 我已经隐藏截图和日志中的 API Key、Token、Cookie 等敏感信息
问题描述
按 #783 的建议从源码启动桌面端(bun run --cwd desktop electron:dev)时,界面必然显示启动错误页「本地服务启动失败」+「启动错误 / Failed to fetch」,点「重试」无效。
sidecar 本身完全健康:进程在跑、端口在监听、/health 返回 200。问题出在 renderer 到 sidecar 的跨源请求上。
运行环境
- 使用方式: 桌面端(源码本地构建)
- 操作系统: Windows 11 专业版 build 26200 (x64)
- 桌面端版本: 0.6.3(仓库
main@0676c194) - 安装来源: 源码本地构建
- Bun 版本: 1.3.14
- Node 版本: v24.21.0
Provider / 模型配置
与本次问题无关——在未配置任何 provider 的干净环境下同样 100% 复现。
复现步骤
git clone https://github.com/NanmiCoder/cc-haha.git && cd cc-hahabun installcd desktop && bun installbun run build:sidecars(生成desktop/src-tauri/binaries/claude-sidecar-<triple>[.exe])- 回到仓库根目录:
bun run --cwd desktop electron:dev - 窗口打开后即显示「本地服务启动失败 / Failed to fetch」
是否稳定复现: 是(每次必现)
根因
开发模式下 renderer 由 Vite 提供,origin 是 http://localhost:1420;sidecar 监听在 127.0.0.1:<动态端口>。两者跨源,因此 renderer 的每个 API 调用都会先触发 Chromium 的 CORS 预检。
src/server/api/client.ts 给请求带上 Content-Type: application/json,所以预检是必然发生的。而按规范,预检请求不携带 Authorization——协商该头正是预检的目的。
服务端 src/server/h5AccessPolicy.ts 的判定却是:
function isLocalDesktopOrNavigationOrigin(request, origin, context) {
if (!origin) return !isCrossSiteSubresource(request.headers)
if (LOCAL_DESKTOP_ORIGINS.has(origin)) return true // 只有 'file://'
if (context.localAccessTokenConfigured) return false // ← dev 渲染进程命中这里
return isLoopbackBrowserOrigin(origin)
}桌面端启动 sidecar 时会注入 CC_HAHA_LOCAL_ACCESS_TOKEN(serverRuntime.ts 的 withLocalAccessToken),于是 localAccessTokenConfigured === true。带 Origin 的预检既不是 file://、又拿不到 token,被判定为 h5-browser,进而命中 shouldBlockDisabledH5Access 返回:
HTTP 403
{"error":"Forbidden","message":"H5 access is disabled. Enable H5 access from the local desktop app first."}该 403 响应不含 Access-Control-Allow-Origin,浏览器随即拦截真实请求,renderer 只看到 TypeError: Failed to fetch。
为什么打包版不受影响:打包后 renderer 从 file:// 加载,而 file:// 在 LOCAL_DESKTOP_ORIGINS 白名单里,直接走 local-trusted 分支。所以这是 dev-only 缺陷,正式用户碰不到。
实测证据
对运行中的 dev 应用直接发请求(Origin: http://localhost:1420):
| 请求 | 修复前 |
|---|---|
OPTIONS /api/settings(预检) |
403,无 ACAO |
GET /api/settings(带 token 的真实请求) |
200 |
GET /api/settings(Origin: file://,打包版形态) |
200 |
GET /health |
200 |
即:真实请求本身能通过,但预检把它挡在门外。
错误信息与日志
界面文案:
本地服务启动失败
启动错误
Failed to fetch服务端响应体(关键证据):
{"error":"Forbidden","message":"H5 access is disabled. Enable H5 access from the local desktop app first."}期望行为
bun run electron:dev 能正常使用,与打包版一致。
建议修复方向
我已在本地验证过一个最小改动,供参考(未提 PR,等你确认这个问题的定性):
desktop/scripts/electron-dev.ts:dev launcher 通过新变量CC_HAHA_TRUSTED_RENDERER_ORIGIN把已被resolveRendererEntry校验过的渲染 URL 下发给 sidecar。src/server/h5AccessPolicy.ts:新增resolveTrustedRendererOrigin()(重新校验协议必须是 http(s) 且 host 必须是 loopback),并在 origin 判定中只豁免这一个确切 origin。src/server/index.ts:启动时读取该变量注入请求上下文。
安全性上刻意收紧:
- 用 dev launcher 专属变量而非
ELECTRON_RENDERER_URL,打包版从构造上不可能继承该豁免; - 不放开 loopback——实测
Origin: http://localhost:5173、http://127.0.0.1:1420、http://localhost:1421、http://[::1]:1420仍全部 403; - H5 控制面
/api/h5-access、/api/h5-access/enable与破坏性端点/api/settings/session-cleanup仍返回 403。
补充说明
- 本文所述
electron:dev路径由 #783 的回复推荐("如果想启动完整的 Electron 桌面窗口,可以用bun run electron:dev"),且release-notes/v0.5.0.md记录过对electron:dev启动器的修复(#1091),推测此路径仍被维护。 - CI 中没有任何工作流会启动 dev 渲染进程,因此该问题不会被 CI 拦住。
Source: NanmiCoder/cc-haha