DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

XiaoYuOvO /

dsh-foreground-stream

Verified

DSH plugin: 前台命令执行实时输出流式展示(对话历史命令卡片实时输出 + 底部流条 + WebUI 设置)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@cf51338c

dsh-foreground-stream

DeepSeek Harness Web bundle 插件:前台命令执行(bash / pwsh 工具、非后台模式)期间的实时输出流式展示——对话页输入区上方出现一条"命令输出流条",随命令执行实时滚动显示 stdout/stderr,而不是等命令结束后才一次性看到。

  • 不修改任何官方工具包:tool/result 内容与执行路径逐字节不变,模型语义零改动。
  • 只旁路展示:命令完成后官方工具卡片照常呈现最终结果;本条只负责"执行期间"的增量画面。
  • 纯内存缓冲,进程重启即清,不做任何跨会话持久化。

安装

从 GitHub 一键添加(推上仓库后)

包已按可分发结构打包(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. 重启 DSH web(新组合在启动时加载)。
  2. 让 agent 跑一条长时间前台命令,例如:
    • pwsh(Windows):1..1000 | ForEach-Object { Write-Output "line $_"; Start-Sleep -Milliseconds 200 }
    • bash(POSIX):for i in $(seq 1 1000); do echo "line $i"; sleep 0.2; done
    • 或 ping -t 8.8.8.8(需注意默认 timeout)。
  3. 观察对话页输入框上方:出现"pwsh · <命令>"条,命令输出随执行逐行滚动; 命令结束显示"命令完成 · 退出码 N"约 4 秒后自动收起,官方工具卡片照常呈现最终结果。

设计说明

为什么是"镜像旁路"而不是改官方包

官方链路(已读源码确认):

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 "镜像补丁保证零竞争" 哲学的同一个缝。

callId 关联(把输出挂到具体工具调用)

  • agent-loop 在 execute 之前同步 append 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 包装)——退出码提取需穿透包装层。
  • 前台 shell 调用在 agent-loop 中严格串行(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 不经此链路,无流式旁路(不影响其正常执行)。
  • 输出缓冲是"插件视角的镜像",与官方最终 tool/result 在超长输出截断边界上可能有 细微差异(各用各的 64KB 上限),但展示用途完全够用。

开发与自检

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 拦截。

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout
—/ 5

No ratings yet

Verified DSH bundle

Commit cf51338cf8a7

Community comments

No comments yet. Be the first to write one.