🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

录制回放 azure5100/huahua-dsh-record-replay

DeepSeek Harness
azure510076 天前有提交已提供中文文档扫描:含敏感能力

回放会话时间线,录屏自动生成可安装技能

28.6/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add azure5100/huahua-dsh-record-replay
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · chat
装得上吗
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 76 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 91 处,展示前 73 条)
  • 发起网络请求
    lib/client.js:37
    return (await readJson(await fetch("/api/dsh-record-replay/recordings", {
  • 发起网络请求
    lib/client.js:44
    await readJson(await fetch("/api/dsh-record-replay/recording" + query({
  • 发起网络请求
    lib/client.js:53
    return (await readJson(await fetch("/api/dsh-record-replay/recording" + query({
  • 发起网络请求
    lib/client.js:62
    return (await readJson(await fetch("/api/dsh-record-replay/recordings"))).recordings;
  • 发起网络请求
    lib/client.js:65
    return (await readJson(await fetch("/api/dsh-record-replay/recording" + query({ id })))).recording;
  • 发起网络请求
    lib/client.js:68
    await readJson(await fetch("/api/dsh-record-replay/recording" + query({ id }), { method: "DELETE" }));
  • 写入剪贴板
    lib/client.js:8763
    await navigator.clipboard.writeText(text);
  • 读取本地文件
    lib/routes.js:106
    const stream = createReadStream(file, { start, end });
  • 读取本地文件
    lib/routes.js:118
    const stream = createReadStream(file);
  • 读取本地文件
    lib/routes.js:440
    const stream = createReadStream(file);
  • 读取本地文件
    lib/recording-store.js:28
    const text = await readFile(join(this.dir(id), 'metadata.json'), 'utf8');
  • 写入本地文件
    lib/recording-store.js:37
    await writeFile(join(this.dir(meta.id), 'metadata.json'), JSON.stringify(meta, null, 2), 'utf8');
  • 写入本地文件
    lib/recording-store.js:41
    await mkdir(this.root, { recursive: true });
  • 写入本地文件
    lib/recording-store.js:43
    await mkdir(this.dir(id), { recursive: true });
  • 写入本地文件
    lib/recording-store.js:44
    await mkdir(join(this.dir(id), 'frames'), { recursive: true });
  • 写入本地文件
    lib/recording-store.js:54
    await writeFile(join(this.dir(id), 'video.webm'), body);
  • 写入本地文件
    lib/recording-store.js:64
    await mkdir(join(this.dir(id), 'frames'), { recursive: true });
  • 读取本地文件
    lib/recording-store.js:80
    entries = await readdir(this.root);
  • 读取本地文件
    lib/recording-store.js:104
    const info = await stat(this.videoPath(id));
  • 删除本地文件递归
    lib/recording-store.js:122
    await rm(this.dir(id), { recursive: true, force: true });
  • 写入本地文件
    lib/skill-installer.js:68
    await mkdir(dir, { recursive: true });
  • 写入本地文件
    lib/skill-installer.js:69
    await writeFile(join(dir, 'SKILL.md'), parsed.text, 'utf8');
  • 读取本地文件
    lib/skill-installer.js:76
    entries = await readdir(this.root);
  • 读取本地文件
    lib/skill-installer.js:86
    const text = await readFile(join(this.root, entry, 'SKILL.md'), 'utf8');
  • 读取本地文件
    lib/session-store.js:46
    raw = new Uint8Array(await readFile(file));
  • 读取本地文件
    lib/session-store.js:61
    raw = new Uint8Array(await readFile(file));
  • 读取本地文件
    lib/session-store.js:107
    projects = await readdir(this.root);
  • 读取本地文件
    lib/session-store.js:118
    sessions = await readdir(projectDir);
  • 读取本地文件
    lib/session-store.js:130
    const info = await stat(read.file).catch(() => undefined);
  • 读取本地文件
    lib/session-store.js:145
    const raw = new Uint8Array(await readFile(summary.path));
  • 写入本地文件
    lib/pack-store.js:17
    await mkdir(this.root, { recursive: true });
  • 写入本地文件
    lib/pack-store.js:24
    await writeFile(file, JSON.stringify(pack, null, 2), 'utf8');
  • 读取本地文件
    lib/pack-store.js:25
    const info = await stat(file);
  • 读取本地文件
    lib/pack-store.js:39
    files = await readdir(this.root);
  • 读取本地文件
    lib/pack-store.js:52
    const info = await stat(join(this.root, file));
  • 读取本地文件
    lib/pack-store.js:75
    const text = await readFile(join(this.root, `${id}.json`), 'utf8');
  • 删除本地文件
    lib/pack-store.js:87
    await rm(join(this.root, `${id}.json`));
  • 发起网络请求
    src/client/api.ts:49
    const response = await fetch(API_BASE + '/recordings', {
  • 发起网络请求
    src/client/api.ts:59
    const response = await fetch(API_BASE + '/recording' + query({ id, part: 'video' }), {
  • 发起网络请求
    src/client/api.ts:67
    const response = await fetch(API_BASE + '/recording' + query({ id, name }), {
  • 发起网络请求
    src/client/api.ts:76
    const response = await fetch(API_BASE + '/recordings')
  • 发起网络请求
    src/client/api.ts:82
    const response = await fetch(API_BASE + '/recording' + query({ id }))
  • 发起网络请求
    src/client/api.ts:88
    const response = await fetch(API_BASE + '/recording' + query({ id }), { method: 'DELETE' })
  • 写入本地文件
    src/pack-store.ts:17
    await mkdir(this.root, { recursive: true })
  • 写入本地文件
    src/pack-store.ts:25
    await writeFile(file, JSON.stringify(pack, null, 2), 'utf8')
  • 读取本地文件
    src/pack-store.ts:26
    const info = await stat(file)
  • 读取本地文件
    src/pack-store.ts:40
    try { files = await readdir(this.root) } catch { return [] }
  • 读取本地文件
    src/pack-store.ts:47
    const info = await stat(join(this.root, file))
  • 读取本地文件
    src/pack-store.ts:70
    const text = await readFile(join(this.root, `${id}.json`), 'utf8')
  • 删除本地文件
    src/pack-store.ts:78
    try { await rm(join(this.root, `${id}.json`)); return true } catch { return false }
  • 读取本地文件
    src/recording-store.ts:31
    const text = await readFile(join(this.dir(id), 'metadata.json'), 'utf8')
  • 写入本地文件
    src/recording-store.ts:39
    await writeFile(join(this.dir(meta.id), 'metadata.json'), JSON.stringify(meta, null, 2), 'utf8')
  • 写入本地文件
    src/recording-store.ts:44
    await mkdir(this.root, { recursive: true })
  • 写入本地文件
    src/recording-store.ts:46
    await mkdir(this.dir(id), { recursive: true })
  • 写入本地文件
    src/recording-store.ts:47
    await mkdir(join(this.dir(id), 'frames'), { recursive: true })
  • 写入本地文件
    src/recording-store.ts:57
    await writeFile(join(this.dir(id), 'video.webm'), body)
  • 写入本地文件
    src/recording-store.ts:66
    await mkdir(join(this.dir(id), 'frames'), { recursive: true })
  • 读取本地文件
    src/recording-store.ts:82
    try { entries = await readdir(this.root) } catch { return [] }
  • 读取本地文件
    src/recording-store.ts:102
    const info = await stat(this.videoPath(id))
  • 删除本地文件递归
    src/recording-store.ts:116
    try { await rm(this.dir(id), { recursive: true, force: true }); return true } catch { return false }
  • 读取本地文件
    src/routes.ts:98
    const stream = createReadStream(file, { start, end })
  • 读取本地文件
    src/routes.ts:110
    const stream = createReadStream(file)
  • 读取本地文件
    src/routes.ts:382
    const stream = createReadStream(file)
  • 读取本地文件
    src/session-store.ts:53
    try { raw = new Uint8Array(await readFile(file)) } catch { continue }
  • 读取本地文件
    src/session-store.ts:64
    try { raw = new Uint8Array(await readFile(file)) } catch { continue }
  • 读取本地文件
    src/session-store.ts:105
    try { projects = await readdir(this.root) } catch { return out }
  • 读取本地文件
    src/session-store.ts:110
    try { sessions = await readdir(projectDir) } catch { continue }
  • 读取本地文件
    src/session-store.ts:116
    const info = await stat(read.file).catch(() => undefined)
  • 读取本地文件
    src/session-store.ts:130
    const raw = new Uint8Array(await readFile(summary.path))
  • 写入本地文件
    src/skill-installer.ts:72
    await mkdir(dir, { recursive: true })
  • 写入本地文件
    src/skill-installer.ts:73
    await writeFile(join(dir, 'SKILL.md'), parsed.text, 'utf8')
  • 读取本地文件
    src/skill-installer.ts:80
    try { entries = await readdir(this.root) } catch { return [] }
  • 读取本地文件
    src/skill-installer.ts:85
    const text = await readFile(join(this.root, entry, 'SKILL.md'), 'utf8')
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-settings@deepseek-ai/dsh-system-prompt
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 会话时间线回放
  • 回放包导出导入
  • 录屏生成技能

DSH 录制回放插件:会话时间线回放、回放包导出导入、重跑录制的对话、录屏生成技能

README

由 DeepSeek 最新模型翻译生成
🐻 huahua 出品 · DSH Record & Replay DSH 录制回放插件 · 由 huahua 打造并持续维护 | 品牌 | 项目 | | 🐻 huahua | 本插件(录制回放) | | 🐻 huahua | huahua-dsh-plugin-orchestra · 插件管理系统 | huahua 系列:为 DeepSeek Harness 打造实用、完整、可维护的插件。 DSH Web GUI 的「录制回放」插件,两套能力: 1. 会话回放:DSH 自动录制每一场会话(~/.dsh/sessions/session.jsonl.zstd),本插件把它变成可用资产——时间线回放、导出/导入可分享的回放包、一键复刻到全新会话重新执行。 2. 录屏 → 生成技能(Codex Record & Replay 同款):在浏览器里录下你的屏幕操作(computer use,getDisplayMedia → webm 视频 + 采样帧),回放查看,并让 agent 逐帧分析(describe_image)自动生成 SKILL.md,安装进 ~/.dsh/skills 立即可用。 类似 Codex 的 record & replay:录制是自动的,本插件补齐「回放 / 分享 / 复刻」这一半。
展开完整文档(README 剩余 1 节)
🐻 huahua 出品 · DSH Record & Replay

DSH 录制回放插件 · 由 huahua 打造并持续维护

| 品牌 | 项目 |
| 🐻 huahua | 本插件(录制回放) |
| 🐻 huahua | huahua-dsh-plugin-orchestra · 插件管理系统 |

huahua 系列:为 DeepSeek Harness 打造实用、完整、可维护的插件。

DSH Web GUI 的「录制回放」插件,两套能力:

1. 会话回放:DSH 自动录制每一场会话(~/.dsh/sessions/session.jsonl.zstd),本插件把它变成可用资产——时间线回放、导出/导入可分享的回放包、一键复刻到全新会话重新执行。
2. 录屏 → 生成技能(Codex Record & Replay 同款):在浏览器里录下你的屏幕操作(computer use,getDisplayMedia → webm 视频 + 采样帧),回放查看,并让 agent 逐帧分析(describe_image)自动生成 SKILL.md,安装进 ~/.dsh/skills 立即可用。

类似 Codex 的 record & replay:录制是自动的,本插件补齐「回放 / 分享 / 复刻」这一半。

衍生声明(Derivative notice):本仓库是 kangshifu1/dsh-record-replay(Apache-2.0)的适配衍生版。原项目作者:kangshifu1。本项目在此基础上做了 Windows / DSH rc.5 适配修复(见下方「Windows 适配」一节),其余功能与架构源自原项目。按 Apache-2.0 §4 注明来源与本项目改动。

Windows 适配(Windows adaptation)

本项目在 Windows + DeepSeek Harness 0.1.0-rc.5 上实测通过,并为此做了 4 处客户端源码适配(原版按更新一代 DSH 外壳的 DOM 约定编写,在 rc.5 上不可用):

| 文件 | 适配内容 |
| src/client/mount.tsx | 面板挂载选择器双匹配:[data-pane="conversation"], [class="centerCol"]——适配 rc.5 外壳(无 data-pane,中央列为 CSS Module 类名 centerCol) |
| src/client/styles.ts | 定位上下文同步双选择器——修复「进入面板后左侧主界面被整个盖住」的问题(rc.5 无 data-pane 定位上下文,绝对定位会锚定到外层 frame) |
| src/client/locales.ts | 新增「关闭面板」文案(中英双语) |
| src/client/panel/ReplayPanel.tsx | 面板头部加「关闭面板」按钮——原版无关闭入口,界面被盖住时无法返回对话 |

适配目标 DSH 版本:DeepSeek Harness 0.1.0-rc.5(Windows 桌面版)。在带 data-pane/taskboard/ssh 面板的新版外壳上,双选择器保持向后兼容(老规则对不存在的元素是空操作)。

完整适配过程与踩坑记录见:docs/适配修复报告.md(Windows 环境构建:NTFS 目录联接、pnpm 11、Node ≥ 20)。

能力

| 功能 | 说明 |
| 会话库 | 侧边栏「录制回放」入口,列出本机全部已录制会话(标题 / 项目 / 时间 / 消息数) |
| 时间线回放 | 以可读时间线查看任意会话:用户消息、助手回复(推理过程可折叠)、工具调用与参数、工具结果(长文可展开),支持搜索过滤 |
| 导出回放包 | 把任意会话导出为单个 dsh-replay-pack JSON(.replay.json),git 友好、体积小,可直接放到 GitHub 仓库分享 |
| 导入回放包 | 导入队友 / GitHub 上分享的 .replay.json,存到 ~/.dsh/replay-packs,可查看、删除、复刻 |
| 复刻执行 | 把录制会话的用户消息按原顺序逐条发送到全新会话,让 agent 真实重新执行一遍(可用于复现、回归、教学、模板化流程) |
| 录屏 | 浏览器 getDisplayMedia 录制屏幕/窗口(webm + 每 2s 采样帧,存 ~/.dsh/recordings),支持回放与删除 |
| 生成技能 | 一键让 agent 会话逐帧分析录屏(describe_image),输出 SKILL.md 并安装到 ~/.dsh/skills——新技能被 skill 目录热监听发现,直接进入每个会话的技能目录 |

安装

本地路径安装(开发 / 自用,推荐)
dsh plugin --profile web add link:/path/to/dsh-record-replay

或从 npm 安装(发布后)
dsh plugin --profile web add dsh-record-replay

安装后重启 dsh web GUI(插件集会变更需要重启生效)。侧边栏出现「录制回放」入口即成功。

原理:dsh plugin 是 pnpm 转发器——把包装进 profile 的 node_modules,检测到 dsh.bundle 声明后把 cordis.patch.yml 并入 bundle 层,插件行进入 roster;浏览器半区按 dsh.client 声明以 /plugins/dsh-record-replay/client.js 加载。

使用

1. 点侧边栏「录制回放」打开面板。
2. 会话 tab:任一会话可「回放」(时间线)、「导出」(下载回放包)、「复刻」(开新会话重新执行)。
3. 回放包 tab:导入 .replay.json,管理已导入的包。
4. 录屏 tab:点「开始录屏」选择屏幕/窗口录制操作;停止后自动上传视频与采样帧。对任意录屏可「回放」,或点「生成技能」——插件开一个 agent 会话,让它逐帧调用 describe_image 提炼工作流并产出 SKILL.md,写入 ~/.dsh/skills//SKILL.md 后即成为可用技能。

回放包格式

{
"format": "dsh-replay-pack",
"version": 1,
"meta": { "title": "...", "cwd": "...", "createdAt": 0, "agentPreset": "code", "exportedAt": 0, "sourceSessionId": "session-..." },
"items": [
{ "kind": "user", "turn": 1, "step": 1, "text": "...", "time": 0 },
{ "kind": "assistant", "turn": 1, "step": 1, "text": "...", "reasoning": "...", "time": 0 },
{ "kind": "tool", "turn": 1, "step": 1, "name": "run_code", "callId": "...", "argsText": "{...}", "time": 0 },
{ "kind": "result", "turn": 1, "step": 1, "callId": "...", "text": "...", "time": 0 }
]
}

数据与隐私

- 读取:~/.dsh/sessions/*/session.jsonl(.zstd)(DSH 自动录制,本插件只读)。
- 写入:~/.dsh/replay-packs/(导入的回放包)、~/.dsh/recordings/(录屏)、~/.dsh/skills/(生成的技能)。
- 录屏隐私:录屏会捕获屏幕上的一切(含密码输入等),生成技能前请留意;录制全程仅在浏览器本地,上传存于本机 ~/.dsh/recordings。
- 注意:回放包含完整对话与工具输出,可能含密钥 / 敏感信息——分享前请自行脱敏。API 路由仅限 loopback(本机浏览器同源),LAN 暴露的 dsh web 部署不会提供服务。

开发

pnpm install
pnpm build # tsc 编译宿主半区到 lib/,tsdown 打包浏览器半区到 lib/client.js
pnpm smoke # 对本机真实会话跑一遍 扫描→解析→建包→解析→存库 冒烟测试

架构

双面插件(dsh-web-ui 家族约定):

- 宿主半区(src/index.ts,node):SessionStore 扫描解码会话、timeline.ts 蒸馏时间线、replay-pack.ts / pack-store.ts 回放包、recording-store.ts 录屏存储(webm + 帧,视频带 Range 支持)、skill-installer.ts 技能安装(校验 frontmatter 与 skill 命名、写入 ~/.dsh/skills)、routes.ts 提供 /api/dsh-record-replay/,并向 agent 宣告插件存在(system-prompt section)。zstd 解码使用 vendored fzstd(MIT,支持多帧流),无运行时第三方依赖。
- 浏览器半区(src/client/):侧边栏入口(DOM 级注入、自愈)+ 中央列面板(React,单占用者接管,与 task-board / ssh 面板互斥)。会话/回放包/录屏三个 tab;复刻执行与技能生成都复用官方 sessions / workspaces / connection 服务——技能生成会话用 describe_image 逐帧看录屏帧(宿主以 HTTP URL 提供),最后把 agent 输出的 SKILL.md 交给宿主安装。

License

Apache-2.0。Vendored fzstd © 101arrowz, MIT(见 THIRD_PARTY_NOTICES.md)。

🐻 huahua 出品

huahua(花花的团队/个人)致力于为 DeepSeek Harness 打造高质量插件:

- huahua-dsh-plugin-orchestra — DSH 插件管理系统:清单 / 更新检测 / 一键升级 / 备份回滚
- 本插件(record-replay) — DSH 录制回放:会话时间线 / 回放包 / 复刻 / 录屏生成技能

功能完整、Windows 实测可用、持续维护。有问题欢迎提 Issue / PR。
预览分享卡片长什么样 ↓azure5100/huahua-dsh-record-replay
分享 · DeepSeek Harness Hub
azure5100/huahua-dsh-record-replay

回放会话时间线,录屏自动生成可安装技能

这是你的仓库?认领条目

回到顶部