chore(desktop): retire Cursor subscription - #2037
Conversation
Astro-Han
left a comment
There was a problem hiding this comment.
Approve — non-blocking suggestions only
Three independent reviews (deletion completeness / retirement logic & tests / first-principles & refactoring) all passed. The removal is clean and complete (no residual imports or IPC channels; typecheck plus the 1535 desktop tests are green), and every safety-boundary claim in the description checks out against the code: deleteSecret is a no-op for missing keys (idempotent), and the slug/legacy file name match the retired service exactly. The current shape is already the optimal solution under the existing constraints — no refactor needed. The following are optional P3-level suggestions:
- Document two decisions in
cursor-subscription-retirement.ts(zero-cost, leaves a trail for a future chore):- This is a bounded-lifetime sweeper:
deleteSecretis read-modify-write, so every launch rewritescredentials.json, and a fresh install materializes an empty credentials file on first launch (also under thefirst-rune2e fixture). Once the legacy window closes, the whole 44-line module can be removed. - Unconditional deletion is deliberate (avoids the "read-then-delete" semantics and TOCTOU of existence checks; no one-off upgrade marker needed).
- This is a bounded-lifetime sweeper:
- Two cheap test additions: the both-fail branch (both sides reject,
AggregateErroraggregates both reasons) is unasserted, and test 1's rerun doesn't re-assertoauth_tokenis still null. - PR body wording nit: "never reads/parses" isn't literally true —
deleteSecretnecessarily reads and parses the whole store file. Suggest "never consumes, imports, or refreshes" (no committed docs are affected). - Drive-by fix (pre-existing from #1851, not introduced here): SECURITY.md:167/:218 and preload.ts:618 reference the deleted
claude-subscription-ipc-boundary.test.ts.
简体中文
三份独立 review(删除完整性 / 清理逻辑与测试 / 第一性原理与重构)结论一致:PASS。删除彻底无残留(无 import/IPC 通道残留,typecheck 与 1535 个 desktop 测试全绿),四条安全边界声明逐条核验为真(deleteSecret 对缺失 key 为 no-op,幂等成立;slug 与遗留文件名与退役 service 完全一致)。当前实现已是既有约束下的最优解,无需重构。以下均为非阻塞建议:
- 建议在
cursor-subscription-retirement.ts注释中记录两个决策:该逻辑是有界存活期 sweeper(deleteSecret为 read-modify-write,每次启动都会改写credentials.json,全新安装会在首次启动物化出空凭据文件,e2efirst-runfixture 下同样触发),遗留窗口关闭后可整体移除这 44 行;无条件删除是刻意选择(避免存在性检查的读取语义与 TOCTOU,也无需一次性升级标记)。 - 可补 2 个测试点:双失败分支(两侧同时 reject 时
AggregateError聚合两个 reason)未断言;test 1 的 rerun 未重断言oauth_token仍为 null。 - PR body 措辞微调(不影响已合入文档):"never reads/parses" 字面不成立——
deleteSecret内部必然读+解析整个文件;建议改为 "never consumes, imports, or refreshes"。 - 顺带修正(#1851 遗留,非本 PR 引入):SECURITY.md:167/:218 与 preload.ts:618 引用了已删除的
claude-subscription-ipc-boundary.test.ts。
* refactor(desktop): remove consumer-less IPC bridge surface Deletes three renderer-dead bridge surfaces, all verified zero-consumer across renderer/overlay/e2e: - appWindow.subscribeOpenSettings + the window:openSettings wiring in app-shell-effects: the only sender (app-menu item) was removed in #390; Cmd/Ctrl+, keeps working through the renderer keydown handler. - dailyReview.list/get/delete aliases duplicating the live *Archive variants, plus the daily-review:delete channel end-to-end - retention cleanup calls store.deleteArchive directly and no renderer spelling of delete has a consumer. - The speculative maka.usage preload namespace, per the decision recorded on #1982: the main-side usage:* authority handlers (#1596) stay for the #2010 M4 client adapter, so the IPC surface contract test now carries a self-checking MAIN_ONLY_CHANNELS allowlist documenting that state. The third #1978 item (ungated cursor-subscription:logout) became moot when #2037 retired the whole Cursor subscription surface. Closes #1978. Ref #1982. Verification: desktop typecheck clean; main suite 1585/1585 including the IPC surface contract and app-shell effect stability contracts. * test(e2e): assert both Skill chips land before leaving the session The leave-and-return journey only asserted the first chip before navigating away; leaving while the second token is still committing races the draft snapshot and loses the chip on return, which is exactly how this spec failed on CI. Mirror the projectChip assertion for workspaceChip so the draft is known-complete before the navigation.
English
Summary
Safety boundary
The cleanup never reads, imports, parses, or refreshes either credential source. It independently attempts both deletions so a partial failure is logged and retried on the next launch.
Only
cursor-subscription:oauth_tokenis removed from the shared store. A same-slug API key can belong to a user-created connection and remains untouched, as do credentials for every other provider. Re-running the cleanup after success is safe.Validation
npm run build:testnpm --workspace @maka/desktop run test:checksnpm run lintnpm run format:checkgit diff --check origin/main...HEADPart of #1982.
简体中文
概要
安全边界
清理过程不会读取、导入、解析或刷新任何一处历史凭据。两个删除会被独立尝试,因此单侧失败会被记录,并在下次启动时自然重试。
共享存储中只删除
cursor-subscription:oauth_token。同 slug 的 API key 可能属于用户创建的连接,因此会被保留;其他 provider 的凭据也完全不受影响。清理成功后重复执行仍然安全。验证
npm run build:testnpm --workspace @maka/desktop run test:checksnpm run lintnpm run format:checkgit diff --check origin/main...HEAD属于 #1982 的一部分。