FNOS-008-07 Codex Auth Desktop 登录修复本地验证记录(2026-09-28)
本记录是 本地与源码级证据,不是 Desktop 目标环境验收。真实 DSH Desktop 运行时验收仍待执行,原因见「未完成部分」。
环境与前置
- 仓库:
fn-packages/fnos-dsh,插件@tnnevol/dsh-codex-auth版本0.1.7-rc.2。 - 上游契约依据:DSH 源码检出
~/workspace/fork-pj/deepseek-harness,提交477b4f4205(release(dsh): 0.1.7-rc.2)。 - 本地实例:
DSH_HOME=<仓库根>/.dsh,dsh web --no-open --port 8124,profileweb已链接本插件。
缺陷与根因
CodexAuthSection.signIn() 原实现:
ts
const popup = window.open(CODEX_AUTH_VERIFICATION_URI, '_blank')
if (popup === null) { setStatus({ status: 'error', message: t('popupBlocked') }); return }
await jsonRequest(CODEX_AUTH_LOGIN_PATH, 'POST') // ← Desktop 下永不执行DSH Desktop 的 Electron 壳在主窗口安装 setWindowOpenHandler(apps/desktop/src/main.ts:235-238):对 http/https 调 shell.openExternal(url) 后一律返回 { action: 'deny' }。因此 Desktop 下 window.open 恒为 null:
- 授权页确实已在系统浏览器打开;
- 但函数在
null分支return,设备码请求永不发出; - 用户看到「浏览器阻止了登录窗口」,且无论如何重试都不会成功——Desktop 上 Codex Auth 完全无法登录。
第二处开窗(「打开授权页面」按钮)同样在 null 时 return,仅丢失窗口句柄,表现为「取消时关不掉授权窗口」,不阻断流程。
本次实现
- 新增
src/client/window-opener.ts的openAuthorizationWindow(url):返回窗口句柄、吞掉开窗异常;刻意不带noopener(noopener会让返回值恒为null,也会在跨域导航后让close()静默失效)。 signIn():null不再判为失败;改为if (popup !== null) authWindowsRef.current.add(popup),登录继续请求设备码并进入「等待授权」。取消作废路径改用popup?.close()。- 「打开授权页面」按钮:
null时静默跳过登记,不再提前return。 - 保留既有语义:保留 opener、
signIn内不自动复制授权码、取消走独立cancel端点而非logout、关窗不等于放弃;未改动/plugins/.../auth/*路由、trustedRequest判定与dsh.client.platform: web。 - 测试:新增
tests/client/desktop-window.spec.ts(10 条);同步更新authorization-window.spec.ts与authorization-code-copy.spec.ts中绑定旧写法window.open(...)的断言(断言语义不变:仍要求「请求设备码前先打开固定授权页」)。
已核实无缺口的路径
| 路径 | 依据 | 结论 |
|---|---|---|
/plugins/dsh-codex-auth-plugin/auth/* 路由 | apps/desktop/src/main.ts:629 → forwardWebRequest() | 由壳转发并注入 Cookie,可用 |
| 落点校验 | apps/desktop/src/web-document.ts(删除 origin、重设 cookie) | 无 origin |
| Host 信任判定 | src/host/auth-routes.ts trustedRequest():无 origin 时回落 localPeer(req) | Desktop 转发来自 loopback,放行 |
| 剪贴板(授权码复制) | apps/desktop/src/microphone-permissions.ts:18:非 media 权限返回 true | Semi copyable 可用 |
| 客户端载体 | packages/client/modules/src/index.ts:841 只接受 platform === 'web' | 不得改动,Desktop 复用同一载体 |
实际操作与结果
- 插件检查:
pnpm --filter @tnnevol/dsh-codex-auth run check→ 18 个测试文件、88 条测试全部通过;typecheck通过。 - 构建:
pnpm run build通过;lib/client.js2,254,253 字节、lib/index.js34,457 字节;Host 产物不含客户端开窗实现(分层未被打破)。 - 组合级端到端:本地
dsh web下GET /plugins/dsh-codex-auth-plugin/auth/status→200,返回{"status":"signed-in","expiresAt":"..."},证明 Desktop 转发路径上的该路由可用。 - 客户端产物:从真实 boot graph 取回含本插件的组合包(
200),产物内确认if (popup !== null) authWindowsRef.current.add(popup),且组件内已无popupBlocked中断路径。 - 静态审计:
window.open仅剩src/client/window-opener.ts一处;两处句柄登记均有判空守卫;客户端无node:内建导入与绝对路径。
未完成部分(状态 blocked 的原因)
- 未执行真实 DSH Desktop 运行时验收:本机未安装 Electron、
apps/desktop未构建(无.desktop-build),无法启动 Desktop 客户端。 - 因此
FNOS-008-07-AC-01、AC-05、AC-07目前只有源码级与本地组合证据;AC-02/AC-03/AC-04的核心判据(null开窗后登录继续)已由单元测试锁定,但尚未在 Desktop 实测。 - 待补:在目标 DSH Desktop 版本安装同一 bundle,实测登录、取消、复制、模型目录同步与重启恢复。
回滚
删除 src/client/window-opener.ts,把两处开窗恢复为原先的 window.open(...) 写法即可;不涉及凭据、账号或持久化数据,回退无数据风险。
验收人
- 执行:CodeBuddy(DSH 会话 Agent)
- 结论:本地实现与组合级验证通过;Desktop 目标环境验收待补,故状态为
blocked。