Skip to content

feat: 新增 extraTools 配置接管其他工具卡片 - #5

Merged
better-er merged 2 commits into
mainfrom
feat/extra-tools-config
Sep 13, 2026
Merged

better-er merged 2 commits into
mainfrom
feat/extra-tools-config

Conversation

@better-er

Copy link
Copy Markdown
Owner

本 PR 给 dsh-edit-diff 加上 extraTools 配置,让它在 editwrite 之外也能接管其他工具的卡片,并默认接管 dsh-remote-file-system 的 edit_remotewrite_remote;顺带按 CICD.md 整理 CI。

背景

dsh-edit-diff 此前只注册 tool.call.toolvieweditwrite 两个 key,参数名与判断逻辑都写死在代码里。远程文件工具的参数名与原生完全一致,但 key 没注册,卡片只能落回通用形式。

浏览器半身拿不到 cordis 配置:__DSH_BOOT__ 的 entry 结构里没有 config 字段,客户端内核创建插件 entry 时只传包名,所以 cordis.patch.yml 里写给插件的配置只有 host 半身能看见。双半插件传配置的官方通道是 settings 命名空间。

具体改动

host 端 src/index.ts

  • 由无操作占位改为真实插件:导出 Config schema 与 ExtraTool 类型,注册 settings 命名空间 dsh-edit-diff
  • ctx.settings.installSection() 把 entry 配置注册为 base 层,用户分节覆盖其上;没有挂载 settings provider 时退回 entry 值。
  • 新增依赖 @deepseek-ai/schemastery,与 @deepseek-ai/cordis@deepseek-ai/dsh-settings 一起进 peerDependencies。

client 端 src/client.ts

  • 新增参数名表 KeySpecextraSpecs,把原来写死的 name === 'edit' || name === 'write' 改成按工具名取参数名。
  • editwrite 使用默认参数名 file_pathold_stringnew_stringcontentextraTools 条目可逐项覆盖。
  • 判断规则:参数里同时有旧文本与新文本按 edit 型渲染,否则有整文件内容按 write 型渲染。
  • ctx.inject(['settingsScope'], ...) 绑定命名空间,配置变化时整批重建这批 key 的注册;settingsScope 缺失时内置两张卡片照常工作。

配置

  • 插件自带 cordis.patch.yml 默认接管 edit_remotewrite_remote,参数名与原生一致只写名字。
  • 要改这份默认,在 profile 的 cordis.patch.yml 里按 id 覆盖 extraTools

文档与 CI

  • README 新增「配置」节,功能列表补「接管其他工具」一条并链接 dsh-remote-file-system。
  • .github/workflows/ci.yml 去掉 master 触发与 node 22/26 矩阵,改为单一 Node 26.3.1,产物校验按双半身加 dts 的形态写。
  • .github/workflows/release.yml 补上已发布版本守卫、environment: releasenpm publish --provenance,与 CICD.md 的模板对齐。
  • package.jsonengines: >=26.3.1,版本提到 0.4.0

验证

  • pnpm typecheck 通过,pnpm build 通过。
  • host 产物单独导入 Node 能加载,Config schema 能把 extraTools 解析出来。
  • 客户端 bundle 里能查到 settingsScope 绑定与 extraTools 接线。
  • 三个 YAML 都过 pyyaml 校验。
  • 实机验证:profile 配上 extraTools 后,edit_remotewrite_remote 的卡片显示为去重 diff。

影响面

  • 默认行为变化:装了这个插件的人都会多注册 edit_remotewrite_remote 两个 key;没装 dsh-remote-file-system 时这两个 key 无人认领,不渲染,无影响。
  • editwrite 的渲染路径不变,settingsScope 缺失时照常工作。
  • host 半身新增运行时依赖 @deepseek-ai/schemastery,另两个是类型依赖。
  • 版本号提到 0.4.0;发版时 release 工作流按 tag 比对,同号则不产生回写提交。

@better-er
better-er requested a lite review from Copilot September 13, 2026 20:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@better-er better-er left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #5 审阅:feat: 新增 extraTools 配置接管其他工具卡片

审阅对象 #5,base main,head 3dcb057,8 文件 +460/−110。工作区已重新 fetch,本地 HEAD 与 PR head 一致。

事实核对

  1. 构建与类型pnpm typecheck 通过,pnpm build 通过,产出 lib/index.jslib/index.d.tslib/client.jslib/client.js.mappnpm install --frozen-lockfile 通过,锁文件与 package.json 一致。线上 CI 同名检查 typecheck · build 为 pass。仓库无单测。
  2. 远端工具参数名:dsh-remote-file-system 的 edit_remotefile_path/old_string/new_stringwrite_remotefile_path/content,与 DEFAULT_SPEC 完全一致,所以 cordis.patch.yml 默认只写 name 是成立的。
  3. settings 通道:宿主 ctx.settings.installSection(owner, ns, schema, entry, hooks) 与 dsh-settings 的签名一致,setSource/onChange 为必填,传空实现能通过类型检查。客户端 settingsScope.bind({ namespace }).getSnapshot().valuesubscribe 与 dsh-client-ui-settings 的 SettingsScope 契约一致,当前 DSH 0.1.5-rc.1 里服务名确实叫 settingsScope,没有 webUiSettings 别名。
  4. CI 与发布:单一 Node 26.3.1、四项产物校验、release 的前置已发布守卫与 npm publish --provenance 都与 CICD.md 的模板对齐。README 的 profile 覆盖示例符合 patch 按 id 替换整段 config 的语义,省略 name 与 dsh-web-app 的真实覆盖写法一致。
  5. 其余改动与描述相符,未发现别的失实。

修改要求

1. 必修:配置变化时的「整批重建」没有真正注销旧注册,二次 sync 会抛错

现实现拿 ctx.effect 包住 slots.inject,再用 disposeExtra 重跑,但这条链路上没有任何 disposer 被真正收集:

  • slots.register 的契约是返回注销函数,内部实现为 ctx.effect(() => this._register(...)),靠回调返回的 disposer 注销。客户端把 slots.register(...) 放在块体箭头里且不 return,回调返回 undefined。
  • slots.inject 同样返回注销函数,内部用 ctx.effect(callback) 包住用户回调;用户回调不返回 disposer,这个 effect 就收集不到任何东西。
  • cordis 的 Fiber.effect 只收集 effect body 返回的 disposer,嵌套的 ctx.effect 各自挂在 fiber 的 _disposables 上,父 effect 的 disposer 不会连带注销它们。
  • SlotCore.register 对 keyed 槽位遇到同 key 同 priority 会直接抛 keyed slot "..." already has an entry for key "...",而这里 priority 固定为 -1。

于是 settings 第二次变化时 sync 会重新注册同一批 key 并抛错,README 里「settings 变化会让浏览器端重建这批 key 的注册」不成立。首次加载通常只发生一次有效 sync,实机验证很容易漏掉;但本插件把命名空间注册进了 Host,插件设置页可以改 extraTools,或 settings 镜像重建时就会命中。

修正方向,每条工具单独 inject 并让回调表达式返回 disposer,同时接住 slots.inject 自己返回的注销函数:

const disposers: Array<() => void> = []
for (const name of names) {
  disposers.push(slots.inject('tool.call.toolview', () =>
    slots.register({ name: 'tool.call.toolview', key: name, priority: -1 }, DiffCard)))
}
disposeExtra = () => { for (const dispose of disposers.reverse()) dispose() }

2. 建议:SlotsService 子集接口的返回类型写错

inject(name, register): voidregister(options, component): void 实际都返回 () => void。当前写法把返回值抹掉,既掩盖了问题 1,也让正确修法过不了类型检查,应改成 () => void

3. 建议:extraTools 里的同名条目会当场抛错

names 未去重,同一份配置里出现两个同名条目时,同一轮 sync 就会对同 key 同 priority 注册两次并抛错。建议在收集 names 时按名字去重。

4. 小问题:订阅退订与 effect 时机

scope.subscribe(sync) 返回的退订函数没有保存,sync 里用 ctx.effectslots.inject 也不能连带注销 inject 控制器。按问题 1 的修法直接收集 slots.inject 的 disposer 即可,subscribe 的退订也建议一并保存,在 settingsScope 卸载时调用。

结论

CI 与文档核对全部通过,主功能方向正确,但配置热更新的注销逻辑存在实打实的缺陷,建议按第 1 条修完再合。

@better-er
better-er merged commit 7363919 into main Sep 13, 2026
1 check passed
@better-er
better-er deleted the feat/extra-tools-config branch September 13, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants