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

视频工坊 tkliuxing/dsh-video-forge

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

在对话里描述镜头,自动生成视频并记账防重复扣费

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · model
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成

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

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

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 63 处,展示前 61 条)
  • 发起网络请求
    src/client/panel-api.ts:117
    const response = await fetch(apiUrl(action), {
  • 读取本地文件
    src/compose.ts:145
    const real = await realpath(candidate).catch(() => candidate)
  • 读取本地文件
    src/compose.ts:170
    if (!(await stat(artifact.lastFrame.path).then(info => info.isFile(), () => false))) {
  • 读取本地文件
    src/destination.ts:89
    if (!(await access(existing.path, constants.W_OK).then(() => true, () => false))) {
  • 写入本地文件
    src/destination.ts:198
    await copyFile(frame.path, target, constants.COPYFILE_EXCL)
  • 写入本地文件
    src/destination.ts:200
    await chmod(target, 0o666 & ~process.umask()).catch(() => undefined)
  • 删除本地文件
    src/destination.ts:207
    await unlink(downloaded.path).catch(() => undefined)
  • 删除本地文件
    src/destination.ts:209
    await unlink(downloaded.lastFrame.path).catch(() => undefined)
  • 删除本地文件
    src/destination.ts:211
    await rmdir(dirname(downloaded.path)).catch(() => undefined)
  • 读取本地文件
    src/destination.ts:281
    real = await realpath(cwd)
  • 读取本地文件
    src/destination.ts:307
    const info = await stat(probe).catch(absent)
  • 读取本地文件
    src/destination.ts:308
    if (info !== null) return { path: probe, real: await realpath(probe), directory: info.isDirectory(), dangling: false }
  • 读取本地文件
    src/destination.ts:310
    if ((await lstat(probe).catch(absent))?.isSymbolicLink() === true) return { path: probe, real: probe, directory: false, dangling: true }
  • 写入本地文件
    src/destination.ts:346
    await mkdir(wanted, { recursive: true })
  • 读取本地文件
    src/destination.ts:348
    const directory = await realpath(wanted)
  • 写入本地文件
    src/destination.ts:365
    await copyFile(artifact.path, staging, constants.COPYFILE_EXCL)
  • 写入本地文件
    src/destination.ts:368
    await chmod(staging, 0o666 & ~process.umask())
  • 删除本地文件
    src/destination.ts:380
    await rm(staging, { force: true })
  • 写入本地文件
    src/destination.ts:408
    await rename(staging, final)
  • 删除本地文件
    src/destination.ts:411
    await unlink(final).catch(() => undefined)
  • 发起网络请求
    src/download.ts:76
    const response = await options.fetch(options.url, {
  • 写入本地文件
    src/download.ts:97
    await mkdir(options.directory, { recursive: true, mode: 0o700 })
  • 写入本地文件
    src/download.ts:101
    await writeFile(path, body, { mode: 0o600 })
  • 发起网络请求
    src/download.ts:131
    const response = await options.fetch(url, { method: 'GET', headers, signal })
  • 写入本地文件
    src/download.ts:142
    await writeFile(path, body, { mode: 0o600 })
  • 读取本地文件
    src/ledger.ts:127
    text = await readFile(this.#options.path, { encoding: 'utf8' })
  • 写入本地文件
    src/ledger.ts:140
    await rename(this.#options.path, quarantine).catch(() => undefined)
  • 写入本地文件
    src/ledger.ts:288
    await mkdir(directory, { recursive: true, mode: 0o700 })
  • 写入本地文件
    src/ledger.ts:291
    await writeFile(temporary, body, { encoding: 'utf8', mode: 0o600 })
  • 写入本地文件
    src/ledger.ts:293
    await rename(temporary, this.#options.path)
  • 删除本地文件
    src/ledger.ts:295
    await rm(temporary, { force: true })
  • 读取本地文件
    src/media-route.ts:242
    const real = await realpath(path)
  • 读取本地文件
    src/media-route.ts:243
    const named = await stat(real)
  • 发起网络请求
    src/operations.ts:335
    return fetch(input, init)
  • 读取本地文件
    src/panel-api.ts:286
    return (await stat(artifact.path)).isFile() ? 'present' : 'missing'
  • 读取本地文件
    src/panel-api.ts:288
    const info = await lstat(artifact.path)
  • 读取本地文件
    src/panel-api.ts:290
    return (await realpath(artifact.path)) === artifact.path ? 'present' : 'missing'
  • 读取本地文件
    src/price-source.ts:269
    raw = readFileSync(this.#options.path, 'utf8')
  • 写入本地文件
    src/price-source.ts:521
    await mkdir(dirname(path), { recursive: true })
  • 写入本地文件
    src/price-source.ts:523
    await writeFile(temporary, payload, { mode: 0o600 })
  • 写入本地文件
    src/price-source.ts:524
    await rename(temporary, path)
  • 发起网络请求
    src/providers/adapter.ts:186
    const response = await context.fetch(url, {
  • 发起网络请求
    src/providers/adapter.ts:227
    const response = await context.fetch(url, {
  • 读取本地文件
    src/selection.ts:168
    const reading = this.#readFile().then(
  • 读取本地文件
    src/selection.ts:226
    async #readFile(): Promise<void> {
  • 读取本地文件
    src/selection.ts:229
    text = await readFile(this.#options.path, 'utf8')
  • 写入本地文件
    src/selection.ts:294
    await mkdir(directory, { recursive: true, mode: 0o700 })
  • 写入本地文件
    src/selection.ts:297
    await writeFile(temporary, `${JSON.stringify(document, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 })
  • 写入本地文件
    src/selection.ts:299
    await rename(temporary, this.#options.path)
  • 删除本地文件
    src/selection.ts:301
    await rm(temporary, { force: true })
  • 读取本地文件
    src/skills.ts:88
    const body = await readFile(join(ASSETS_DIR, file), 'utf8')
  • 读取本地文件
    src/workspace-fs.ts:29
    stat(target: WorkspaceTarget, signal?: AbortSignal): Promise<{ type: 'file' | 'directory' | 'other'; size?: number } | undefined>
  • 读取本地文件
    src/workspace-fs.ts:89
    async stat(target) {
  • 读取本地文件
    src/workspace-fs.ts:91
    const info = await stat(String(target.targetKey))
  • 读取本地文件
    src/workspace-fs.ts:98
    const info = await stat(String(target.targetKey))
  • 读取本地文件
    src/workspace-fs.ts:106
    const entries = await readdir(String(target.targetKey), { withFileTypes: true })
  • 写入本地文件
    src/workspace-fs.ts:119
    await mkdir(dirname(path), { recursive: true })
  • 写入本地文件
    src/workspace-fs.ts:122
    await writeFile(path, content, { flag: 'wx' })
  • 写入本地文件
    src/workspace-fs.ts:132
    await writeFile(temporary, content)
  • 写入本地文件
    src/workspace-fs.ts:133
    await rename(temporary, path)
  • 读取本地文件
    src/workspace-fs.ts:140
    return await realpath(path)
扫描于 2026/10/31(0 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/cordis-plugin-loader@deepseek-ai/dsh-app-boot@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-resources@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-layout@deepseek-ai/dsh-client-ui-plugin-manager@deepseek-ai/dsh-client-ui-primitives
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

DeepSeek Harness 的在线模型视频生成:三个模型工具、一份重启后仍保留的服务商任务台账、实时聊天卡片和 Videos 面板,以及根据公开价格源估算的费用。

README

由 DeepSeek 最新模型翻译生成
dsh-video-forge 在 DeepSeek Harness(dsh)的工作区对话里用在线模型生成视频。说一句要什么镜头,成片就会落在工作区的 renders/<shot>/vNNN.mp4;每次生成都记在一份持久化台账里,所以超时或重启 不会让你付两次钱。 按设计没有本地推理路径:这个插件只是在线模型的客户端。 English: README.md 环境要求 - DSH 0.2.x(含 rc 版本)。包里声明了 engines.dsh (^0.2.0-rc.1 || ^0.2.1-rc.2),宿主会拒绝安装声明区间与运行版本不符的插件。 它不会悄悄加载一个按宿主没有的 API 编译出来的插件。 - Node ^22.19.0 || >=24.0.0。 - 至少一家受支持供应商的账号与 API key(火山方舟 ARK Seedance、MiniMax,或任意 OpenAI-Videos 兼容网关)。没配置之前不会生成,也不会产生费用。 - 不需要 ffmpeg、模型权重或 GPU:渲染在供应商侧,本插件只搬字节。 安装 dsh plugin --profile web add dsh-video-forge web 是你启动的 profile(dsh web);换成你实际在用的 desktop、tui 等即可。 装完重启该 profile。
展开完整文档(README 剩余 1 节)
dsh-video-forge

在 DeepSeek Harness(dsh)的工作区对话里用在线模型生成视频。说一句要什么镜头,成片就会落在工作区的 renders/<shot>/vNNN.mp4;每次生成都记在一份持久化台账里,所以超时或重启 不会让你付两次钱。

按设计没有本地推理路径:这个插件只是在线模型的客户端。

English: README.md

环境要求

- DSH 0.2.x(含 rc 版本)。包里声明了 engines.dsh
(^0.2.0-rc.1 || ^0.2.1-rc.2),宿主会拒绝安装声明区间与运行版本不符的插件。
它不会悄悄加载一个按宿主没有的 API 编译出来的插件。
- Node ^22.19.0 || >=24.0.0。
- 至少一家受支持供应商的账号与 API key(火山方舟 ARK Seedance、MiniMax,或任意 OpenAI-Videos 兼容网关)。没配置之前不会生成,也不会产生费用。
- 不需要 ffmpeg、模型权重或 GPU:渲染在供应商侧,本插件只搬字节。

安装

dsh plugin --profile web add dsh-video-forge

web 是你启动的 profile(dsh web);换成你实际在用的 desktop、tui 等即可。
装完重启该 profile。

配置供应商

常规做法是在 GUI 里:侧栏 → 插件 → Video Forge。它会以可选列表给出本版本支持的协议:火山方舟 ARK(Seedance)、MiniMax(H3)、OpenAI(Sora),或自定义的 OpenAI-Videos 兼容网关,并自动填好 Base URL、凭据引用与厂商文档里的模型,允许添加多家供应商。API key 经宿主的凭据服务保存,界面只显示掩码后的尾号。

同样的配置也可以手写进 profile 的 cordis.patch.yml:

- id: video-forge
name: dsh-video-forge
config:
providers:
- id: ark
protocol: ark-seedance
baseUrl: https://ark.cn-beijing.volces.com/api/v3
authRef: ARK_API_KEY
models:
- id: doubao-seedance-2-0-fast-260128
modes: [t2v, i2v, flf2v, s2v]
resolutions: ['480p', '720p']
aspectRatios: ['16:9', '9:16', '1:1']
defaultProvider: ark
defaultModel: doubao-seedance-2-0-fast-260128

模型按 providerId::modelId 引用。凭据先走宿主凭据服务,再走环境变量
(<PROVIDER>_API_KEY);回环网关可以不带 key。

在对话里怎么用

- "做一个黄昏灯塔的 5 秒镜头" → 调用 video_generate;聊天卡片跟着任务实时更新,出片后可以直接播放。
- "让 materials/cat.png 里的猫转头看镜头" → 用工作区图片做首帧(i2v)生成。
拖进对话的附件也行:模型把它看到的路径传进来,插件按字节认出这是图片。
- "接着上一个镜头" → from 取上一版 take 的尾帧(ARK)作为这一版的首帧,落在同一个镜头目录里。
- "Alice 走进来" → characters: ['alice'] 会带上 Alice 注册在案的描述与参考图,让她在每个镜头里长得一样。
- 超时或重启之后问"好了吗?" → video_status。插件在重启后接管的任务,完成时还会主动在对话里说一声。

工具

| 工具 | 作用 |
| video_generate | 提交一次生成(t2v / i2v / flf2v / s2v),立刻返回任务 id 与后台 job id。shot 决定 renders/ 下的目录名;from 接着上一版拍;characters 应用 video-project.yaml 里注册的角色。拒绝(只读对话、远端工作区、清单写坏、重复请求)都发生在花钱之前 |
| video_status | 恢复入口:最近的任务、进度、每部片在哪、失败原因、内容审核拒绝、供应商上报的用量。不给任务 id 时还会报告当前设置:有哪些供应商、key 是否配好、模型与模式,以及本工作区的清单、权限模式与角色。超时之后先查这里,不要重新提交 |
| video_cancel | 请供应商停下(与 job_kill 不同:后者只是停止盯守,供应商照旧干活照旧计费) |

费用,以及什么离开了你的机器

video_status 也回答钱的问题:cost: true 把已结算用量乘价目表求和(按供应商/模型/镜头分组,附价目表日期,以及还有哪些任务的用量未知);estimate 在提交前先给一次请求估价。每个数字都标注为估算,真正算数的是供应商的账单,而且厂商确实有过不打招呼就调价的历史。

价格来自公开数据源(LiteLLM 的价格库,按 7.0 CNY/USD 折算并按分辨率档拆分),而不是手写在源码里;手写的那份写下的当天就已经错了。这份数据在后台抓取,绝不发生在工具调用里;整体校验后缓存在 $DSH_HOME/dsh-video-forge/price-cache.json,取值顺序是 你自己的行 > 新抓到的 > 缓存 > 内置快照。每笔金额都带 price_source (remote / cache / bundled / none,以及时效与最近一次失败),价目表里查不到的模型报"未定价"而不报 0。raw GitHub 到不了时,把 priceSourceUrl 指向镜像(jsDelivr、npmmirror);留空则关闭该数据源。

会离开你机器的是提示词、你点名的参考图、video-project.yaml 里的角色描述,还有 from 用到的上一个镜头的尾帧。这些都会用你配置的 key 上传给你配置的供应商,受那家厂商的条款约束。没有任何东西会发给插件作者;本插件不带遥测。供应商的内容审核可能直接拒绝任务:拒绝会被记录下来,完全相同的请求不会被重提,改过的请求照常放行。

随包附带的 skill

插件带 5 个 skill(由模型用 skill 工具加载,其中 3 个也注册为 /名字 命令)。
它们负责引导工作流;工具本身是自洽的,从不依赖某个 skill 是否被加载过。

| Skill | 覆盖什么 |
| video-project(/video-project) | 工作区布局与 video-project.yaml:路径、默认值、风格、角色、版本号 |
| video-prompting | 怎么写/改提示词(主体、动作、镜头、风格),时长与画幅,供应商差异 |
| video-storyboard(/video-storyboard) | 把剧本拆成 storyboard/shots.yaml,一次一个镜头地生成 |
| video-iterate | 重拍还是续拍(from)、角色一致性,以及交付前的尾帧自检 |
| video-guide(/video-guide) | 端到端用法:配置、能做什么不能做什么、费用、成片落在哪 |

工作区就是项目

<workspace>/
video-project.yaml # 可选:默认值、风格、角色、目录映射 materials/ # 参考图(任意布局;路径按工作区相对写) renders/<shot>/v001.mp4 # 每一版 take,永不覆盖 renders/<shot>/v001.json # 这一版由什么产生:提示词、模型、素材、用量 renders/<shot>/v001.last.jpg # 它的尾帧(ARK),供 from 使用

一个工作区里第一条视频还会顺手写出带注释的 video-project.yaml,以及内容为 的 renders/.gitignore(想提交成片就删掉它,比如配合 LFS)。清单大致是这样:

version: 1
defaults: { provider: ark, model: doubao-seedance-2-0-fast-260128, resolution: 720p, aspect_ratio: "16:9" }
style: warm film grain, soft daylight
characters:
alice: { refs: [materials/alice/.png], description: "red scarf, short black hair" }
paths: { renders: renders }

清单解析不了,或某一节里键名拼错,都会在提交之前被拒。插件只写 renders/(以及首次产出的那两个附带文件);它尊重对话的权限模式:只读对话会被拒绝,workspace-write 下一个其实是指向工作区外部的 renders/ 也会被拒绝。万一工作区最终放不下这条已经付过钱的视频,它留在 $DSH_HOME 里,video_status 会说明原因。

协议

| 协议 | 覆盖 |
| ark-seedance | 火山方舟 Seedance(POST/GET/DELETE {base}/contents/generations/tasks),带免费的尾帧(return_last_frame) |
| minimax-video | MiniMax 视频生成 V2(POST /v2/video_generation、GET /v2/query/video_generation/{id});不能取消,没有尾帧 |
| generic-openai-videos | OpenAI-Videos 形状的网关(/v1/videos),每条路径与状态词都可配 |

界面入口

| 位置 | 是什么 |
| 输入框工具行 | Video model chip:这个对话用哪个模型生成,以及时长 / 分辨率 / 画幅。它的优先级高于 video-project.yaml 和插件设置;你(或模型)在调用里显式传的参数仍然最优先。"自动"把决定权还回去。按会话隔离:新对话从"自动"开始 |
| 聊天 | 每次 video_generate 一张卡片,跟着任务实时更新:状态与进度、出片后是真正的 播放器、文件在哪(或已被移走/删除)、供应商还可能停下时的取消按钮,以及两条出口:在文件查看器中打开该视频,或去面板看 全部视频 |
| 右侧栏 → 视频 | 本工作区里的每一部片子,新镜头在前:一版 take 一行,带状态、大小与时长,最新的一条默认展开并可播放。进行中与失败的排在最下面;renders/ 里台账不认识的片子标为"仅文件"。插件交付到工作区之外的片子根本不列出:这个面板是本项目的视频,那条片子落在哪由聊天卡片负责说明。面板只读:从不提交,也不删东西 |
| 右侧栏 → 文件查看器 | 打开 .mp4(从面板、文件树,或对话里的路径)会在与文本、图片同一个 tab 里播放,上方仍是宿主自己的工具栏(刷新、用你的播放器打开) |
| 引导页(右侧栏的"+") | 可以选到"视频"面板 |
| 右下角角标 | 在飞的任务数(没有时隐藏) |
| 侧栏 → 插件 → Video Forge | 供应商与模型以表单呈现;API key 经宿主凭据服务保存,界面只显示掩码后的尾号 |

没有单独的工作台页面,视频面板也不是:浏览器侧任何东西都无法提交一次付费生成或写文件。
浏览器侧只访问 /video-forge/api/{overview,cancel},除已配对的设备外仅限回环。
宿主不为插件路由做门禁,所以由本插件自己做。

东西都在哪

- 视频:工作区的 renders/(见上)。不在任何工作区对话里发起的调用,成片留在 $DSH_HOME/dsh-video-forge/outputs/。
- 台账:$DSH_HOME/dsh-video-forge/tasks.json。什么都不自动删,retain 只报告插件自己目录的压力。
- 媒体:GET|HEAD /video-forge/media/<taskId>/<token>/<name>。路径只从台账解析,绝不来自请求;一个 128-bit token 授权访问,且工作区里的片子只在它仍是当初写入的那个文件时才提供。
- 流式播放:GET|HEAD /api/video-forge/stream?path=…[&sessionId=…] 以 HTTP Range 提供 mp4,长片可以跳到中间而不必整段读完。它挂在宿主自己的已认证 /api 通道上(仅 owner、同源),读的是同一个用户本来就能通过 /api/file 打开的文件,加上 Range 并没有多给任何可达性。上面那条插件自己的信任门路由做不到这点:它只认识台账里记过的文件。

升级与卸载

dsh plugin --profile web add dsh-video-forge@latest # 升到最新版
dsh plugin --profile web remove dsh-video-forge # 卸载

两步之后都重启 profile。卸载不会删除 $DSH_HOME/dsh-video-forge/(台账、产物、价格缓存)或工作区的 renders/。那些是你的数据,要清自己清。

开发

pnpm install
pnpm check # tsc --noEmit、tsdown 构建、vitest

fixtures/provider.ts 是一个离线替身,跑在真 socket 上,模拟 ARK / MiniMax / OpenAI 兼容协议,所以整条"提交 → 轮询 → 下载 → 交付"链路不需要 key 也不需要网络就能跑完。
fixtures/litellm-video-sample.json 是上游价格数据的一份冻结切片,内置快照被断言等于这份冻结样本的归一化结果,两者因此不会漂移。

针对真实供应商的实测是分开的,永不进入 pnpm check。free 只发不可能产生计费任务的请求;paid 还会为每家供应商真跑一次最便宜的生成:

VIDEO_FORGE_LIVE=free ARK_API_KEY=… MINIMAX_API_KEY=… pnpm test:live

设计说明(中文,一个里程碑一节,坑也写在里面)在 docs/DESIGN.md;发版流程在 docs/RELEASING.md。

许可

MIT,见 LICENSE。

内置价格快照派生自 LiteLLM 的价格库(MIT)。
厂商名称与模型 id 仅作描述性使用;本项目与这些厂商没有隶属关系。
预览分享卡片长什么样 ↓tkliuxing/dsh-video-forge
分享 · DeepSeek Harness Hub
tkliuxing/dsh-video-forge

在对话里描述镜头,自动生成视频并记账防重复扣费

回到顶部