dsh-git-branch
English | 中文
在 DeepSeek Harness(DSH)的对话框 (composer)工具行中,紧跟在权限选择框之后,显示当前项目的 git 分支。
刻意做成只读:只显示,不提供任何选择。目录不是 git 仓库时什么都不渲染——没有占位、 没有空框、没有报错。
挂在哪里
对话框工具行的固定布局:
[+ 命令] [权限 ▾] [plan] ← 分支标记渲染在这里
插件注册到 conversation.input.left,即 shell 紧跟在常驻模式控件之后渲染的追加式槽位。
该槽位对宿主 UI 是追加式的(replaceRisk: none),因此既不会替换也不会遮挡任何自带控件。
样式刻意对齐相邻的权限选择框:同样 28px 高、13px/500 字号、--dsw-alias-label-secondary
文字色、无自带背景、14px 的 currentColor 图标——浅色与深色主题下都落在同一基线上。分支名
过长时省略号截断,不会把右侧控件挤走。
行为
| 目录状态 | 渲染内容 | 悬浮提示 |
|---|---|---|
| 在某个分支上 | 分支图标 + main |
Git 分支: main |
| 游离 HEAD | 分支图标 + a1b2c3d |
游离 HEAD: a1b2c3d |
| 仓库还没有第一次提交 | 分支图标 + 默认分支名 | Git 分支: main |
| 不在仓库里 | 什么都不渲染 | — |
重新读取的时机:会话工作目录变化、每轮对话结束、标签页重新可见。从不轮询。
环境要求
- DSH
0.2.0-rc.2或更新。 - Host 进程的
PATH上有git。 - 别无其它:没有构建步骤、没有运行期依赖、没有配置项。
git 不存在或探测超过 5s 超时时,该目录会被判为“非仓库”,标记保持隐藏,不会向用户报错。
安装
本包是一个 DSH bundle:自带 cordis.patch.yml,所以安装它的同时就会加上它的插件行。
推荐——走插件管理器。 在 Web 界面 Settings → Plugins → 添加插件,目标填
https://github.com/helloHupc/dsh-git-branch 或本地 checkout 目录。Agent 侧等价的做法是用
plugin_manager 工具的 install_bundle,target 同样是该 URL 或目录。
本地 checkout 安装。 clone 之后,用上面任一方式安装该目录。
安装是持久改动:对该 profile 下的所有会话生效,重启后依然存在。
目前端到端验证过的是从本地目录安装;从仓库 URL 安装走的是插件管理器有文档的 git 支持。
卸载
在同一个 Plugins 页面移除该 bundle,或用 plugin_manager 工具的 remove_bundle。依赖与
bundle 选择会一起移除;插件的插件行和它的路由也随之消失。
实现方式
Host 一半(index.js) 在 Web 载体上注册一条只读路由:
GET /dsh-git-branch?sessionId=<id>&cwd=<绝对路径>
- 工作目录取 Host 会话存储里该 Session 的权威 cwd;只有当 Host 尚未物化该会话时,才使用
客户端传来的绝对
cwd。绝不把 Host 进程自己的工作目录当作答案。 - 每次探测只用系统
git跑一次(execFile;无 shell、无第三方库、无缓存),形式为git -C <cwd> …,并设置GIT_OPTIONAL_LOCKS=0、GIT_TERMINAL_PROMPT=0、5s 超时,同时 删除 git 自己的仓库覆盖变量(GIT_DIR、GIT_WORK_TREE等)——即便 Host 是在另一个 git 上下文里启动的,也始终由-C决定是哪个仓库。 - 仓库判据依次为
rev-parse --show-toplevel、symbolic-ref --short HEAD,游离 HEAD 时再用rev-parse --short HEAD。symbolic-ref在“还没有任何提交”的 unborn HEAD 上也能回答,而rev-parse HEAD此时已经失败。 - 围栏与
/api网关完全一致:Host必须是回环地址,或部署声明的trustedHosts之一;拒绝Sec-Fetch-Site: cross-site;带Origin时主机名必须一致。这是 DNS rebinding 与跨站读取 的防线,不是身份认证。
一切格式正确的请求都返回 200 + { ok: true, … },包括 { "isRepo": false } —— 隐藏标记
是客户端的决定,不是错误分支。只有围栏(403)、非 GET 方法(405)和 URL 畸形(400)才返回其它
状态。
浏览器一半(client.js) 是浏览器产物:一个 classic script,只注册一个惰性 factory——这
正是 DSH 模块加载器要求的形态。它只用自己写的 React 与主题 token,不 import 任何
@deepseek-ai/dsh-client-* 包:那些包说变就变,而一个抛异常的组件会把这个槽位条目整个变空。
样式、词典、槽位注册全部登记为 ctx.effect,所以插件卸载与 HMR 重新激活都能干净地移除并重装。
显示名称
插件列表里的标题与描述来自 locale/<语言>.json 的 meta.title / meta.description,
不是 package.json 的 name:
locale/en.json是必需的回退锚点;同目录下的其它文件(如zh.json)会一起被读取。- 标题与图标都经过包的
exports解析,所以"./locale/*.json"这条导出必须保留。 package.json的name是标识符:它决定 Loader 行name与浏览器模块 id。
开发
node test/host.test.mjs # Host 一半的检查:围栏、方法、工作目录、git 探测
test/host.test.mjs 会创建真实的临时仓库并直接驱动注册的路由处理器——不需要 profile、不需要
安装 DSH、不联网,只要求有 git。
改两半的生效范围不同:
| 文件 | 生效时机 |
|---|---|
client.js、locale/*.json |
下次页面加载 |
index.js |
DSH 重启之后——Host 代码不会在进程内热替换 |
安全说明
- 这条路由会通过本地 HTTP 载体暴露某个仓库的分支名。围栏阻止的是敌意页面的跨站读取;把服务绑到 非回环地址时,请按对待任何本地服务的标准对待它。
- 客户端的
cwd只接受绝对路径,且仅在会话存储没有答案时使用。路由从不回退到 Host 进程的工作 目录,因此无法被引导去报告一个无关的仓库。 - 只读:每次调用都只是
rev-parse/symbolic-ref探测。不写文件、不加锁、不用凭据、不弹提示。
已知边界
- 每个请求一次
git子进程;git缺失或探测超时都会被判为“非仓库”。 - 这条路由不做身份认证,这一点与本地 Web 载体的其余部分同一设计。
- 已针对 DSH
0.2.0-rc.2验证。浏览器一半只使用该槽位有文档的标准 props,但未来的 DSH 版本仍 有可能改动那个槽位。
许可
MIT © helloHupc
No comments yet. Be the first to write one.