踩坑记录(DSH 插件集成)
August 19, 2026 · View on GitHub
开发这个插件时踩过的坑,按「现象 → 原因 → 解法」整理。给后来做 DSH 侧边栏插件的人当路标。
1. 访问服务属性会触发「注入门」报错
现象:宿主端访问 ctx.remote.commands 直接抛注入门(inject-gate)错误。
原因:Cordis 的依赖注入是惰性的,直接访问没声明注入的服务属性会触发门禁。
解法:用全名软探测 ctx.get('remote.commands'),或在 inject 数组里显式声明 ['remote', 'remote.commands']。要判断某服务是否可用的地方(如 ctx.get('llm')),一律用 ctx.get('xxx') 而不是 ctx.xxx。
2. 硬 inject 会卡住整个插件
现象:插件「打不开」——所有槽都不渲染。
原因:inject 里写了某个当前环境没有的服务(比如浏览器端写 sessions、commands),插件会一直等这个服务就绪,整棵树都不落地。
解法:只 inject 一定存在的服务(slots、remote);其余用 ctx.get() 运行时软探测,拿不到就优雅降级。
3. session.events 是私有的,读出来是 undefined
现象:想订阅会话事件、抓 agent 回复,写 session.events 永远是 undefined。
原因:Session 的 events 字段是 private(TypeScript 私有 + 打包后还可能被重命名),不是公开 API。
解法:用公开的 session.subscribe(listener) + session.getSnapshot()。快照里 nodes 是最终消息数组,chat.legacy.partial 是流式中的半成品,running 是运行状态。助理消息节点形如 { kind:'assistant', blocks:[{kind:'text', text}] , seq }。
4. GitHub /search/code 的 CORS 缺陷
现象:浏览器里直接 fetch 代码搜索,带 token 时 Failed to fetch。
原因:/search/code 的认证 200 响应没有 Access-Control-Allow-Origin 头(但 401 响应反而有),所以预检通过、实际响应被 CORS 拦下。
解法:代码搜索挪到宿主端(Node)做,客户端通过命令通道调 /ghcodesearch。
5. README 的 Accept 头返回的是 HTML 不是 JSON
现象:README 一直不显示。
原因:Accept: application/vnd.github.html+json 返回的是渲染好的 HTML,不是 JSON。
解法:用 r.text() 直接取 HTML 文本渲染,不要 r.json()。
6. 第三方 README 的 XSS 风险
现象:dangerouslySetInnerHTML 直接渲染 GitHub README,恶意仓库可注入脚本。
解法:改用 <iframe sandbox="" srcDoc={html}> 隔离渲染。空 sandbox 禁用脚本、表单、同源,图片与样式仍正常。
7. clone 空子目录会拼出盘符根
现象:子目录留空时,目标路径变成 D:\,git 拒绝。
解法:子目录为空时,自动拼上仓库名:base + '\' + repoName。
8. hosts 文件中毒
现象:某段时间所有 GitHub API 请求全挂。
原因:本机 hosts 里 api.github.com(甚至 github.com)被写成了 127.0.0.1。
解法:把 hosts 恢复成真实 IP(20.205.243.x 段)。排查网络问题时先 ping api.github.com 看解析。
9. React 事件把 mouse event 传进参数
现象:onClick={doSearch} 会把点击事件当成第一个参数,导致函数收到错误类型。
解法:写 onClick={() => doSearch()},显式控制参数。
10. 切换搜索类型后结果串号
现象:从「仓库」切到「代码」搜索,旧仓库结果还在。
原因:发起新搜索时没清空旧结果。
解法:doSearch 开头 setResults([]); setTotal(0),并关掉历史面板。
11. 给推理模型设 maxTokens 上限会导致空回复
现象:判断返回「模型没有返回内容」。
原因:推理(reasoning)模型会先消耗大量 token 思考,maxTokens 被思考占满,可见正文为空。
解法:不设 maxTokens 硬截断,靠提示词约束长度;收集时把 reasoning-delta 作为兜底(正文为空时返回思考内容)。
12. 用轮询做流式会刷爆会话日志
现象:宿主流式 + 客户端每 250ms ghjudge-poll,聊天/轨迹里刷出一堆命令节点和巨大 JSON。
原因:命令通道的每次 execute 都会向会话追加 command/run + command/done 生命周期事件(无静默模式),每个轮询都变成一条可见流节点。
解法:改成单次调用返回完整结果,客户端用打字机式分段渲染模拟「流式感」。真·流式需要宿主→客户端的推送通道,而插件能用的 ctx.remote.$on 只转发固定白名单事件,自定义事件推不过去。
13. 定时器句柄在 re-render 时被重置
现象:判断期间每秒更新「已等待 N 秒」,结果到了 clearInterval 却清不掉、定时器越积越多。
原因:定时器句柄声明成组件内的普通 var,每次 re-render 都重置为 null,丢了活跃定时器的引用。
解法:用 React.useRef(null) 保存句柄,跨 render 稳定。
14. 客户端 bundle 无构建,改完重启即可
现象:不确定改 lib/client.js 要不要重新构建。
结论:本插件 exports["./client"] 直接指向 lib/client.js(浏览器端 bundle),用 link: 方式装进 profile 后是 symlink,改完只需重启 dsh web 就生效,无需 Vite 重编。别在没重启前判断「没生效」。
15. 代码搜索/Star/Fork 需要 token,错误体验要友好
现象:没配 token 时点 Star/Fork 直接弹 alert。
解法:统一成卡片顶部浮层提示(toast),并把缺 token 的文案改成「请到『设置』里配置」的引导语,而不是硬报错。GitHub 未认证搜索约 10 次/分钟,认证后 30 次/分钟——把速率限制写进设置说明,403 时带上「剩余 N 次」。
16. 判断要喂 README 摘要,而不是只看 star
现象:只看 star/语言判断,经常误判「是不是该 clone」。
解法:判断前在宿主端拉取该仓库 raw README 前 1500 字(Accept: application/vnd.github.raw,有 token 带上),连同当前工作区一起喂给模型,让它比较「实际用途」而不是元数据。