踩坑记录(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 里写了某个当前环境没有的服务(比如浏览器端写 sessionscommands),插件会一直等这个服务就绪,整棵树都不落地。

解法:只 inject 一定存在的服务(slotsremote);其余用 ctx.get() 运行时软探测,拿不到就优雅降级。

3. session.events 是私有的,读出来是 undefined

现象:想订阅会话事件、抓 agent 回复,写 session.events 永远是 undefined

原因Sessionevents 字段是 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 带上),连同当前工作区一起喂给模型,让它比较「实际用途」而不是元数据。