DeepSeek Harness Hub
← 返回列表

高级侧边栏工具集navid-kianfar/dsh-advanced-sidebar

DeepSeek Harnessspec-screeneduicode在 GitHub 查看 ↗
⚠ 装前注意

在侧边栏管理 Git 更改、终端、文件与后台任务

基本兼容但装前注意:未发布到 npm registry,仅可从源码安装 · 最近上游提交 2026/8/26 · 已提供中文文档

DeepSeek Harness Web 客户端的高级侧边栏操作:git 更改、终端、文件浏览器、开发服务器预览、后台任务、打开方式、归档和删除。

综合分
29.9
GitHub 分
29.9
用户评分
★ Stars
0
周下载量
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add navid-kianfar/dsh-advanced-sidebar
未发布到 npm registry,仅可从源码安装,改用 GitHub 源安装
数据截至 2026/8/27(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

npm 包@achasoft/dsh-advanced-sidebar(未发布到 npm,仅可源码安装)
Node 引擎要求 ^22.19 || >=24 · 基线 Node 22.19 满足
dsh CLI 依赖未声明 dsh 版本约束
入口文件main/exports/bin 已声明

未发布到 npm registry,仅可从源码安装

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/8/30 10:54:56

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-api-remotes@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-layout@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-settings-plugins@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

@achasoft/dsh-advanced-sidebar

DeepSeek Harness Web 客户端的高级侧边栏操作:更改、终端、文件、后台任务、打开方式、归档和删除,可从侧边栏底部的同一个菜单以及打开会话标题栏中的同一菜单访问。

浏览器已经能做的所有事情都通过 Web 客户端自身的能力完成。所有它在结构上无法做到的事情——运行 git、分配伪终端、启动编辑器、停止后台任务、删除会话日志——都通过此插件自己的 Typert Remote 命名空间 ctx.remote.advancedSidebar 完成。

这里没有任何面向模型的内容:没有工具、没有提示词部分、没有会话事件。

安装

dsh plugin --profile web add /abs/path/to/dsh-advanced-sidebar
dsh web

如果从 harness 源代码检出安装:

pnpm dsh plugin --profile web add ../dsh-plugins/dsh-advanced-sidebar
pnpm dsh web

请先构建插件(pnpm install && pnpm run build);当组合的插件没有构建好的 lib/client.js 时,Web 客户端会拒绝启动。

菜单

| 条目 | 打开的内容 | 在 Host 上需要什么 |
|---|---|---|
| 更改 | 会话工作目录中未提交的更改、每个文件的补丁,以及暂存 + 提交 | PATH 中的 git、ctx.subprocess、ctx.fs |
| 终端 | 在该目录中你自己的交互式 shell | ctx.subprocess、ctx.fs |
| 文件 | 该目录,一次一层,带文本预览 | ctx.fs |
| 预览 | 工作区的开发服务器,启动并显示在框架中,带其日志 | ctx.subprocess、ctx.fs |
| 后台任务 | 此会话的 ctx.jobs 记录,带停止功能以及已结束任务的输出 | ctx.jobs |
| 打开方式 ▸ | 第二个浏览器窗口、配置的编辑器,或操作系统的文件管理器 | 编辑器需要 ctx.subprocess |
| 归档 | 隐藏会话;其日志和记账槽位保留 | ctx.workspaceRegistry |
| 删除 | 归档,并且在 purge 模式下——删除持久会话日志 | ctx.workspaceRegistry、ctx.sessionPersistence |

缺少所需能力的条目会被禁用并在旁边显示原因,而不是被隐藏,因此拼错的 command 或未安装的 git 是可见的,而不是悄无声息。在设置中关闭的条目则完全不存在。

每个条目的目标是会话自己的 cwd,回退到其 Workspace 路径。因此,在子目录中启动的会话会在那里工作,而不是在模型未使用的仓库根目录工作。

每个面板做什么,以及它刻意不做什么

更改读取仓库并写入其索引。读取使用 git status --porcelain=v2 --branch -z --untracked-files=all,在 src/host/porcelain.ts 中解析;-z 正是让包含空格、引号或换行符的路径在状态读取和随后的 diff 请求之间保持一致的原因。
暂存和取消暂存作用于单个文件或整个组,而提交记录已暂存的内容,并可选进行修补。每次写入都会返回随后的读取结果,因此列表永远不会比它们所描述的索引滞后一个往返,而打开的补丁会随它们所描述的索引一起被丢弃。

丢弃被刻意省略。暂存、取消暂存和提交都是可恢复的——前两者不会触及工作树,而提交会留在 reflog 中——而 git restore 会销毁未提交的工作,且没有任何东西可以从中恢复。侧边栏不适合放置这组操作中唯一不可逆的动词,而它在旁边的终端面板中只需按一个键。

面板做了四件事,而不是留给 git 自己的错误文本来处理:

- 每条路径在 git 看到它之前都被证明位于仓库内部,并在 -- 之后作为字面路径传递——git add 接受的是路径规范,因此未经检查的 :(exclude) 会暂存没人挑选的内容。
- 消息作为单个参数传递给 -m,因此没有 shell 会看到它,其中的任何内容都不会变成选项。一条 --amend --author=someone 的消息提交的就是该文本。
- 作者通过 git var GIT_AUTHOR_IDENT 读取——这是 git 自己对“这次提交会由谁署名”的回答——并显示在输入框下方,因此缺失的 user.email 会在按下按钮之前就可见。
- 提交有自己的超时(gitCommitTimeoutMs,默认 2 分钟),因为它会运行仓库的 pre-commit 钩子,而该钩子的运行时间可能远远超过任何读取;在运行中途终止它会留下一个陈旧的 index.lock。钩子会运行,并且钩子的 stderr 会原样返回,而不是被概括。

两种写入都受设置(allowGitStaging、allowGitCommit)门控,这些设置由宿主强制执行,而不仅仅是在菜单中:关闭它们会移除该动词,而不是隐藏它。

终端通过 ctx.subprocess.spawnTerminal 分配自己的 shell——刻意不使用 ctx.terminals。该注册表的会话以 Agent 为所有者边界,是模型的工作终端;加入它们会让人类的按键落入模型认为由它控制的会话中。

屏幕是真正的终端模拟器(@xterm/xterm),这就是浏览器包体积大的原因。这不是偏好问题:交互式 shell 在每次按键时都会用光标寻址重绘其提示符,而手工编写的屏幕模型会把登录 shell 的提示符渲染成被覆盖的碎片——经过验证,然后被替换。颜色、行编辑、历史召回和全屏程序都随模拟器而来。Ctrl+C 被拦截,并作为发送到前台进程组的信号而不是字节传递,这就是中断正在运行的命令与什么都不做之间的区别。

输出是轮询的,而不是推送的:树外插件没有从宿主到客户端的推送通道,因此面板持有它已经写入模拟器的整个流偏移量,并请求其后到来的任何内容。这个偏移量也正是重新打开的面板能够重放保留的回滚内容的原因。
这里没有调整大小:子进程接缝不暴露任何调整大小的能力。模拟器跟随抽屉,使渲染出的行保持可读,但外壳保持其被分配时的大小;Restart 会按新的大小分配一个。

src/client/terminal-screen.ts 作为 Preview 和 Background 任务面板的日志渲染器保留下来,这些面板显示的是纯输出,而不是交互式屏幕。

Files 通过这个插件自己的端点列出,而不是 Web Client 的 listDirectory,因为 Host 的浏览能力只返回目录——它唯一随附的调用方是工作区选择器。每个路径都通过 ctx.fs 解析,并在任何读取发生之前证明其位于工作区内。

Preview 运行你正在构建的内容,并将其显示在对话旁边。

启动配置从工作区自己的 .claude/launch.json——Claude Code 的文件,未作改动——以及 previews 设置行中读取,按此顺序;在两者中都声明的名称取自仓库的文件,因为仓库是自身如何运行的权威。带有 runtimeExecutable 的行会启动一个进程;只有 url 的行仅用于附加,只是将框架指向已经在运行的某个东西。

{
"version": "0.0.1",
"configurations": [
{ "name": "web", "runtimeExecutable": "npm", "runtimeArgs": ["run", "dev"], "port": 3000 },
{ "name": "docs", "runtimeExecutable": "pnpm", "runtimeArgs": ["docs:dev"], "port": 5173, "cwd": "website" }
]
}

就绪状态是对配置端口的 TCP 连接,重试直到它接受连接或 previewReadyTimeoutMs 过去。HTTP 探测需要路径、方法,以及对哪些状态码算数的判断;而一个正在监听的套接字是每个开发服务器都认同的唯一事实。PORT 会导出给子进程,并且设置 NO_COLOR/FORCE_COLOR,以便日志视图显示文本而不是转义序列。

框架是一个普通的 ,带有视口预设(桌面、平板、移动),按抽屉缩放,并有一个可编辑的地址栏,以便你可以导航到某个路由,而不仅仅是根路径。有两个控件是常驻的,而不是错误状态:

- Logs —— stdout 和 stderr 按到达顺序交错,由 Terminal 面板使用的同一个调用方拥有的偏移量读取。启动失败的服务器没有任何东西可以放入框架,而它的 stderr 是原因存在的唯一地方,因此启动失败会自行打开日志视图。
- Open in a new window —— 页面可以拒绝被框架嵌入(X-Frame-Options、frame-ancestors),而跨源框架嵌入使面板无法检测到这一点:无论哪种情况加载事件都会触发。因此这个逃生出口始终存在,而不是在发生无法观察到的失败后才出现。
停止是在 previewGraceMs 之后对整个进程树先发送 SIGTERM,再发送 SIGKILL,因此开发服务器自身的子进程也会一并终止。启动一个已在运行的配置会替换它,而不是争抢端口,并且插件卸载时每个服务器都会停止。

后台任务 不会获取自己的列表。Host 已经推送 session/jobs 帧,Web Client 会将其合并到 jobsBySession 中,因此该面板读取的是与会话标题栏中的任务标签相同的实时数据。只有停止和输出会经过此插件的端点:

- 停止 会将注册表记录标记为 reported,这会抑制其生产者原本会传递给模型的完成通知。对于按下停止的人来说,这是正确的取舍——工作是在其授权下被取消的——这也是为什么该动词是一个设置项(allowTaskKill),而不是始终开启。
- 输出 仅在任务已结束 且 其完成已被报告后才提供。ctx.jobs.read() 消费的是模型读取的同一游标,因此排空一个活动任务的流会静默删除模型即将接收的输出。已排空的文本会保留在 Host 侧,因此重新打开面板会再次显示它,而不是第二次读取为空。

打开方式 区分目录和文件:目录会被打开,文件会在其文件夹中被 选中(open -R、explorer /select,)。harness 自带的 host.openPath 会将路径交给其默认应用程序,这对第一种情况是正确的动词,对第二种情况则是错误的。

删除 是拼装而成的,因为没有任何 harness 能力可以删除会话——持久化是仅追加的,并且不暴露删除动词,而工作区注册表只能归档。因此,删除会如实说明它管理的是哪一半:

- archive 隐藏会话并保留其日志。
- purge 还会移除持久化后端的每会话产物。这无法撤销,它需要 confirmDelete,并且它 绝不触碰活动会话——正在运行的回合会继续追加到一个已不存在的文件,因此归档会提交,原因也会随之返回。不保留每会话产物的后端(SQLite)会从 describe() 报告 archive,因此确认提示永远不会承诺一个不会发生的移除。

活动会话是通过 agent registry 检测的,而不是会话存储:agent 才是运行回合的东西,而存储仅仅保留的冷会话并没有被写入。

设置

advanced-sidebar 部分由 Host 半部分注册,并作为卡片呈现在设置的 Plugins 选项卡上。每个控件都直接写入绑定的设置作用域,该作用域负责修订隔离;没有保存或丢弃。
describe() 也携带已解析的 section,并且每个界面都读取 bound scope value ?? describe().settings。这并非冗余:ctx.settingsScope 仅在回环连接上解析为真实文档,而在每个远程 Web Client 上都会以无值的方式回答 unavailable。如果没有 Host 的副本,整个界面会把“无设置”读作“已关闭”,并在远程访问时消失;有了它,远程客户端就能以只读方式看到部署的真实配置。

每个因部署而异的选项都是 cordis.patch.yml 中 advanced-sidebar 行上的 config 字段——位置、存在哪些条目、抽屉宽度、删除模式、任务权限、git 边界、shell、终端数量、文件预览边界,以及 Open in 目标。卡片会编辑除目标列表之外的所有这些内容,目标列表保留在 cordis.yml 中,因为在那里可以正确编写命令及其参数;卡片会显示每个目标在此 Host 上的可用性,这是文件无法声明的。

组合

两行,始终都会组合:

- insert:
- id: advanced-sidebar
name: '@achasoft/dsh-advanced-sidebar/host'
config: { ... }
- id: advanced-sidebar-ui
name: '@achasoft/dsh-advanced-sidebar'

第二行刻意使用裸包名:Web Client 通过解析 /package.json 来发现浏览器端的一半,因此使用子路径的行会让每个席位都悄无声息地得不到服务。

四个插槽注册:sidebar.footer.action、conversation.session.header.utilities、shell.overlay(抽屉和 Delete 确认)以及 settings.plugin.item。它们没有共同的 React 祖先,因此用户打开的内容存放在本包拥有的一个控制器中,并通过其注入面交给每个注册项。

生成的 Typert 产物

generated/ 携带浏览器端一半所挂载的 RPC 契约。测试框架的 Typert 生成器读取一个以测试框架自身的 tsconfig.host.json 为种子的 TypeScript 程序,因此它无法针对该检出之外的包运行;因此,该产物由 scripts/emit-typert.mjs 按照该生成器的格式编写,其中保存着端点列表和每个线上 schema。

编辑 src/host/types.ts 或 @Remote 表面意味着也要编辑该规范:

pnpm run regen:typert   # re-emits generated/ and records its fingerprint

pnpm test 会拒绝不匹配:scripts/check-typert.mjs 将声明的端点和生成的端点作为集合进行比较,并重新哈希输入,而 tests/typert-contract.spec.ts 会通过每个描述符的 schema 解析一个代表性值。

开发

pnpm install
pnpm run build       # tsc emit -> tsdown two-half bundle
pnpm run typecheck
pnpm test            # typert drift check + vitest

要进行实时循环,请在本包中运行 npx tsdown --watch:测试框架的 HMR 一半会对所服务的 bundle 进行 stat 轮询,因此任何写入 lib/client.js 的操作都会触发重新加载。

已知限制
- 预览框架无法报告自己的控制台或网络。 这些需要同源访问被框住的页面,而另一个端口上的开发服务器无法提供这种访问。面板显示的是服务器自身的日志;浏览器的开发者工具只需点击“在新窗口中打开”即可访问。
- 没有推送通道。 树外插件无法添加有线框架,因此终端输出、预览日志和 git 读取都是轮询的。后台任务是例外——它们搭乘 Host 现有的 session/jobs 推送。
- 没有终端调整大小。 子进程接缝没有调整大小的动词。长行会换行而不是滚动,并且 Restart 会重新测量框的大小。
- 停止任务会抑制其模型通知。 参见上文的后台任务;allowTaskKill: false 会移除该动词。
- Purge 移除一个产物。 正是持久化后端为该会话报告的路径——后端拥有的 sidecar 由后端负责移除,而这里的递归删除可能会删除一个目录。
- 浏览器包约为 1 MB(gzip 后 205 KB)。 模拟器占了大部分。客户端模块加载器为每个插件提供一个文件,没有代码分割,因此无法推迟到 Terminal 面板打开时再加载。
- 此处未使用 --dsw-alias-label-error。 ui-theme 没有声明这样的 token,尽管有三个 harness 样式表引用了它;此包使用 --dsw-alias-state-error-primary,并且 tests/styles.spec.ts 会在任何 ui-theme 未声明的 token 上失败。

验证

在发布前,每个面板都针对运行中的 dsh web 进行了测试:git 读取、逐文件补丁、暂存一个文件、取消暂存它,以及针对真实仓库的真实提交——用 git log 验证,然后撤销;一个运行命令并渲染其输出的 /bin/zsh shell;文件列表和文本预览;一个启动并显示在框架中的 .claude/launch.json 开发服务器,以及一个故意失败的服务器,其 stderr 打开了日志视图;空任务列表;以及 Delete 确认。tests/ 覆盖了针对真实 git status -z 输出的 porcelain 解析器、启动文件解析器和合并、屏幕模型的控制词汇、路径包含防护、生成的 wire 契约端到端,以及样式表命名的每个设计 token。

对完成包进行的对抗性审计发现了 35 个候选缺陷;已确认的缺陷已在此修复,包括未跟踪 diff 路径中的路径遍历漏洞(git diff --no-index 本身不应用任何仓库包含)、重复的任务输出缓冲区、在服务器失败时永不停止的预览轮询循环,以及上文描述的设置回退。

许可证

MIT

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群