dsh-web-ui
zhu1090093659
Plugin and skin collection for DeepSeek Harness (DSH) Web UI - task board, git graph, right-side panel, remote mobile UI, pet, live token stats, and skin center.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:XiaoYuOvO/dsh-foreground-stream
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
DeepSeek Harness Web bundle 插件:前台命令执行(bash / pwsh 工具、非后台模式)期间的实时输出流式展示——对话页输入区上方出现一条"命令输出流条",随命令执行实时滚动显示 stdout/stderr,而不是等命令结束后才一次性看到。
tool/result 内容与执行路径逐字节不变,模型语义零改动。包已按可分发结构打包(dependencies 声明了 host 依赖、files/.gitignore 排除了开发期 junction):
# 在任意位置执行(git 需走本地代理 127.0.0.1:7800):
node "G:\Deepseek Harness Desktop\dependencies\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js" plugin --profile web add github:<owner>/<repo>
# 或带版本:add github:<owner>/<repo>#<tag|commit>
dsh plugin 会自动把声明了 dsh.bundle.patch 的包追加进 dsh.profile.bundles(自动 reconcile)。
安装后必做一步(settings 白名单补丁,幂等脚本):
node "G:\Deepseek Harness Desktop\data\dsh\plugins\dsh-foreground-stream\scripts\patch-allowlist.mjs"
# 或:npm run patch:allowlist
原因:Web 客户端读写 settings 命名空间受 harness
dsh-host-apiproxy的WEB_SETTINGS_NAMESPACES硬编码白名单过滤,插件侧无注册逃生通道;本脚本把dsh-foreground-stream幂等加入该白名单。harness 升级/重装会覆盖补丁,需重跑。
然后重启 DSH web 宿主(Host half 改动不热更新;Client 刷新页面即可)。
# 在插件目录(含 package.json 的目录)下执行:
node "G:\Deepseek Harness Desktop\dependencies\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js" plugin --profile web add .
等价于 pnpm 在 profile 目录里
add file:<插件绝对路径>。Windows 路径含空格时 CLI 可能拆参,可改为:在 profile 目录pnpm add "file:<插件绝对路径>",再手动把包名 追加进dsh.profile.bundles(profiles/web/package.json)。
验证安装:
node "G:\Deepseek Harness Desktop\dependencies\dsh\node_modules\@deepseek-ai\dsh\lib\bin.js" --profile web --dump-config | Select-String -Pattern 'dsh-foreground-stream'
应出现:
- id: dsh-foreground-stream
name: dsh-foreground-stream
1..1000 | ForEach-Object { Write-Output "line $_"; Start-Sleep -Milliseconds 200 }for i in $(seq 1 1000); do echo "line $i"; sleep 0.2; doneping -t 8.8.8.8(需注意默认 timeout)。官方链路(已读源码确认):
dsh-tool-bash/pwsh.execute → ctx.shell.run(spec) → 执行器 runArgv
→ ctx.subprocess.spawn(spec) (stdio 恒为 collect 模式,无增量回调)
→ await handle.done → readFrom(0) 一次性汇总
dsh-subprocess 服务契约明确:collect-mode reader 是 offset-based 非消耗式的
(readFrom(fromByte) 返回 { text, nextOffset, lossy, spillPath },独立读者互不消耗)。
因此插件用自己的游标增量读取 handle.collected.stdout/stderr,官方最终 readFrom(0)
完全不受影响——这就是 task-status "镜像补丁保证零竞争" 哲学的同一个缝。
tool/call({turn, step, callId, name, arguments}),
经 session/event 派发 → 插件记录"当前前台命令"并建立 per-callId 缓冲。tool/call 的 arguments 是模型侧原始 JSON 字符串(agent-loop 直接落
block.arguments,只有 exec.arguments 经过 parseArguments)——插件
先 JSON.parse 再读 command / run_in_background。tool/result 的 message.content 是嵌套形态
[{ type:'tool-result', content:[{type:'text', text}] }]
(createToolResultMessage 包装)——退出码提取需穿透包装层。startCall await dispatch 直到进程结束),
因此"当前前台命令"是单槽;subprocess.spawn 包装器在 spawn 时校验
argv 包含该命令字符串(bash:["bash","-c",cmd];pwsh:["pwsh",...,"-Command",<preamble>cmd];
win32 sandbox 的 runner argv 同样内嵌原 argv)才挂 tap——MCP / skill / worker 等
其他工具的 spawn 因 argv 不匹配而绝不会被误挂。tool/result(message.source.callId)收尾:running=false、尽力恢复退出码/信号、
清 pending。Host 注册只读路由 GET /plugins/dsh-foreground-stream/active?sessionId=...
(流条用,返回当前/最近命令 + 全文 + dock 配置位)与
GET /plugins/dsh-foreground-stream/output?callId=...(历史卡片实时轮询用);
客户端整段替换渲染(tail 效果)。
增量 tap 统一 120ms 心跳 drain(不随输出量放大);缓冲 64KB 保尾截断; 完成后保留 120s 供展示,之后清理;缓冲条目上限 100。
客户端运行中 400ms / 空闲 1s 轮询;输出区 max-height 180px 滚动 + 自动跟随底部 (用户上翻暂停跟随,回底恢复);ANSI 转义防御性清洗。
长命令行展开:命令行超过 64 字符(或含换行)时,流条标题变为可点击
(带 ▾/▴ 指示与 aria-expanded),点击展开完整命令(等宽字体块、
max-height 120px 可滚动),再点收起;调用切换时自动复位。短命令保持单行省略。
历史卡片实时输出:运行中的前台命令,其对话历史卡片自动展开终端面板,
面板输出区按 callId 轮询 /plugins/dsh-foreground-stream/output 实时更新
(400ms/次),无需底部流条即可在记录里直接看到命令输出;命令结束后停止
轮询,改由官方最终结果呈现。
自动展开/收起(config.autoExpand,默认开):运行中的新命令面板自动展开、
运行完成后自动收起;用户手动展开/收起后不再自动干涉(手动状态优先)。
复制命令按钮:历史卡片终端面板内命令行右侧有复制按钮,悬停卡片时显示, 点击一键复制完整命令行(短暂显示 ✓)。
面板展开/收起动画(config.animMs,默认 150ms):终端面板展开播
dshfs-panel-in、收起先播 dshfs-panel-out 再卸载(opacity + 轻微位移);
时长由设置项"面板动画速度"控制(关闭 0 / 快 80 / 标准 150 / 慢 300),
animMs: 0 时无动画、即时展开/收起。
WebUI 设置栏目(命令输出):通用设置 → 外观 → 命令输出,两个开关——
"显示底部命令流条"(showDock)、"命令面板自动展开/收起"(autoExpand)
与动画速度下拉(animMs)。
值存 Host settings 命名空间 dsh-foreground-stream(settings.yaml 持久化),
即时生效(流条/卡片随下一次轮询应用),无需改组合行配置。
注册方式:Host 在 apply 时经 ctx.get('settings') 直连注册,服务未就绪时
500ms 轮询重试(最多 60s)——不依赖嵌套 ctx.inject(真实宿主上该路径
对部分插件不触发)。诊断:GET /plugins/dsh-foreground-stream/settings-state
返回 { state, source, registered }。
⚠️ harness 依赖:Web 客户端能否读写某命名空间由 dsh-host-apiproxy
的 WEB_SETTINGS_NAMESPACES 硬编码白名单决定(代码注释明说:未列入即
settings-not-exposed,即使命名空间已注册)。本插件已把
dsh-foreground-stream 加入该白名单(dependencies/dsh/node_modules/ @deepseek-ai/dsh-host-apiproxy/lib/index.js)。升级/重装 harness 会覆盖
该修改,需重新打补丁,否则设置栏目会退回"设置服务暂不可用"。
组合行配置作为默认层:cordis.patch.yml 的 config.showDock /
config.autoExpand 仍是 base 默认值;UI 里手动开关后以 settings 用户值为准。
- id: dsh-foreground-stream
config:
showDock: false # base 默认层(UI 可再覆盖)
autoExpand: false
| 候选槽 | 结论 |
|---|---|
conversation.input.dock(list)✅ 流条 |
官方既有 additive 座位(task-status 同款),对 bash/pwsh/任何 shell 工具统一生效;负责"执行期间"的实时滚动展示 |
tool.call.toolview(keyed)✅ 历史卡片 |
接管 bash + pwsh 两个 key(priority: -1 最低者胜出,替换官方 bash-toolview-sample 与 pwsh 的通用回退行),原版面板形态:标题行 = 命令执行图标(悬停/已展开变箭头)+ 工具名标题(Bash/PowerShell)· 描述摘要 + 状态;点击行展开终端面板,面板内命令行单行省略、点击命令行本身原位展开为完整多行(无第二面板),下方为输出(等宽、滚动)。数据取自 block 的 callView/resultView(card:'terminal'),缺失回退 parseArgs(argsRaw)/resultText |
conversation.details.tool(single) |
接管 = 渲染所有工具的输出详情面板,太重,未采用 |
settings.general.item(list)✅ 设置栏目 |
通用设置页的"命令输出"行(id command-output,order 15,紧随"外观"行):标题 + 两个开关(showDock / autoExpand),经 settingsScope 绑定 Host 命名空间读写 |
两处都只读展示:官方 execute 行为与模型可见的
tool/result内容零改动。
ctx.shell 的前台 bash/pwsh 工具调用;run_code 内部子调度、终端
(PTY)路径、tool-bash-persistent 不经此链路,无流式旁路(不影响其正常执行)。node --check lib/index.mjs # Host half 语法
node --check lib/client.js # Client half(浏览器 bundle)语法
node scripts/smoke.mjs # Host 逻辑冒烟:补丁/关联/增量/跳过/隔离/dispose
package.json # dsh.bundle.patch + dsh.client(web platform + inject)
cordis.patch.yml # 挂载行:- id: dsh-foreground-stream
lib/index.mjs # Host half:镜像 spawn 补丁 + session/event 关联 + /active 路由
lib/client.js # Client half:dock 流条 + 历史命令卡片(手写 ModuleLoader bundle)
scripts/smoke.mjs # Host 逻辑冒烟:补丁/关联/增量/跳过/隔离/dispose
scripts/smoke-client.mjs # Client 冒烟:bundle 结构/槽注册/展开交互/卡片渲染
README.md
Client half 是手写 plain-JS
window.__ModuleLoader__.load({ id, factory })bundle (与 dsh-message-edit 同款格式),无需 tsdown 构建步骤,提交的产物即源码; 安装时无 build 脚本,pnpm 不会触发 allowBuilds 拦截。
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: terminal、web-ui。