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

产物预览 lucagiftzek/dsh-artifacts

DeepSeek Harnessv0.1.1
lucagiftzek37 天前有提交已提供中文文档扫描:含敏感能力

为 DSHDeepSeek HarnessWeb GUI 侧边栏提供的 Artifacts产物标签页。

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

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

自动兼容检查有告警:npm 同名包「dsh-artifacts」归属 zoahdev/dsh-artifacts,装到的可能不是本插件。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add lucagiftzek/dsh-artifacts
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

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

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意

npm 同名包「dsh-artifacts」归属 zoahdev/dsh-artifacts,装到的可能不是本插件

展开查看检查明细 ↓

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

✓npm 包dsh-artifacts @ 0.1.1
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/14 07:37:47

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 64 处,展示前 51 条)
  • 发起网络请求
    lib/client.js:75
    const res = await fetch(API + route, opts);
  • 发起网络请求
    lib/client.js:287
    const res = await fetch(
  • 写入剪贴板
    lib/client.js:661
    await navigator.clipboard.writeText(abs(r.url));
  • 写入剪贴板
    lib/client.js:695
    await navigator.clipboard.writeText(abs(item.url));
  • 读取本地文件
    lib/engine/cli.js:34
    return JSON.parse(readFileSync(0, "utf8"));
  • 读取本地文件
    lib/engine/save.js:239
    } else if (existsSync(finalDir)) updating = true;
  • 写入本地文件
    lib/engine/save.js:243
    mkdirSync(target.path, { recursive: true });
  • 读取本地文件
    lib/engine/save.js:247
    if (updating && existsSync(finalDir)) cpSync(finalDir, stage, { recursive: true });
  • 删除本地文件
    lib/engine/save.js:252
    unlinkSync(dst);
  • 写入本地文件
    lib/engine/save.js:258
    mkdirSync(dirname(dst), { recursive: true });
  • 写入本地文件
    lib/engine/save.js:259
    writeFileSync(dst, bytesOf(c));
  • 写入本地文件
    lib/engine/save.js:269
    writeFileSync(join(stage, "template.json"), JSON.stringify(tj, null, 2) + "\n");
  • 写入本地文件
    lib/engine/save.js:270
    writeFileSync(join(stage, "README.md"), readme.endsWith("\n") ? readme : readme + "\n");
  • 读取本地文件
    lib/engine/save.js:273
    const sizeTotal = all.reduce((a, f) => a + statSync(join(stage, f)).size, 0);
  • 读取本地文件
    lib/engine/save.js:297
    if (existsSync(finalDir)) {
  • 写入本地文件
    lib/engine/save.js:299
    renameSync(finalDir, old);
  • 删除本地文件递归
    lib/engine/save.js:307
    if (old) rmSync(old, { recursive: true, force: true });
  • 删除本地文件递归
    lib/engine/save.js:320
    if (existsSync(stage)) rmSync(stage, { recursive: true, force: true });
  • 读取本地文件
    lib/engine/save.js:320
    if (existsSync(stage)) rmSync(stage, { recursive: true, force: true });
  • 删除本地文件递归
    lib/engine/save.js:321
    if (trial) rmSync(trial, { recursive: true, force: true });
  • 读取本地文件
    lib/engine/templates.js:52
    return statSync(p).isDirectory();
  • 读取本地文件
    lib/engine/templates.js:59
    return statSync(p).isFile();
  • 读取本地文件
    lib/engine/templates.js:66
    statSync(p);
  • 读取本地文件
    lib/engine/templates.js:87
    accessSync(d, FS.W_OK);
  • 读取本地文件
    lib/engine/templates.js:119
    return JSON.parse(readFileSync(path, "utf8"));
  • 读取本地文件
    lib/engine/templates.js:125
    raw = readFileSync(p, "utf8");
  • 读取本地文件
    lib/listing-cli.js:17
    const text = readFileSync(0, "utf8");
  • 读取本地文件
    lib/listing.js:174
    text = readFileSync(listingFile(env), "utf8");
  • 写入本地文件
    lib/listing.js:190
    mkdirSync(storeDir(env), { recursive: true });
  • 写入本地文件
    lib/listing.js:191
    appendFileSync(listingFile(env), JSON.stringify(e) + "\n");
  • 读取本地文件
    lib/listing.js:242
    return statSync(path, { bigint: true }).mtimeNs.toString(16);
  • 读取本地文件
    lib/listing.js:256
    public: rel.includes("/") ? false : existsSync(join(dir, rel) + ".public")
  • 读取本地文件
    lib/listing.js:274
    reg = JSON.parse(readFileSync(join(store, "registry.json"), "utf8"));
  • 读取本地文件
    lib/listing.js:282
    text = readFileSync(join(store, "claims.jsonl"), "utf8");
  • 读取本地文件
    lib/listing.js:317
    entries = readdirSync(dir);
  • 写入本地文件
    lib/listing.js:536
    mkdirSync(storeDir(env), { recursive: true });
  • 写入本地文件
    lib/listing.js:538
    appendFileSync(join(storeDir(env), "claims.jsonl"), rels.map((name) => JSON.stringify({ name, session, time: now, how: "tool" }) + "\n").join(""));
  • 写入本地文件
    lib/listing.js:606
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    lib/listing.js:611
    renameSync(tmp, target);
  • 删除本地文件
    lib/listing.js:614
    unlinkSync(tmp);
  • 读取本地文件
    lib/plugin-skills.js:51
    if (!existsSync(root)) return []
  • 读取本地文件
    lib/plugin-skills.js:53
    for (const entry of readdirSync(root).sort()) {
  • 读取本地文件
    lib/plugin-skills.js:57
    if (!statSync(d).isDirectory() || !existsSync(p)) continue
  • 读取本地文件
    lib/plugin-skills.js:57
    if (!statSync(d).isDirectory() || !existsSync(p)) continue
  • 读取本地文件
    lib/plugin-skills.js:58
    const { meta } = parseSkillFile(readFileSync(p, 'utf8'), p)
  • 读取本地文件
    lib/plugin-skills.js:85
    const raw = await readFile(locator, { encoding: 'utf8', signal: options.signal })
  • 写入本地文件
    lib/tools.js:85
    mkdirSync(store, { recursive: true });
  • 写入本地文件
    lib/tools.js:87
    appendFileSync(join(store, "claims.jsonl"), rels.map((name) => JSON.stringify({ name, session, time: now, how: "tool" }) + "\n").join(""));
  • 发起网络请求
    server/bridge.js:38
    return fetch(API + route, opts).then(function (r) {
  • 写入剪贴板
    server/bridge.js:157
    try { navigator.clipboard.writeText(String(text)); toast('Copied - paste it into the chat'); } catch (e) {}
  • 发起网络请求
    server/bridge.js:330
    fetch(probe, { cache: 'no-store', credentials: 'same-origin' }).then(function (r) { return r.text(); }).then(function (t) {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 列出智能体生成文件
  • 就地预览文件
  • 支持实时重载

README

由 DeepSeek 最新模型翻译生成
dsh-artifacts 为 DSH(DeepSeek Harness)Web GUI 侧边栏提供的 Artifacts(产物)标签页。 智能体会生成文件——一份 HTML 报告、一个仪表盘、一张图表、一个 CSV。此标签页会在侧边栏内按最新优先的顺序列出它们,并就地预览其中一个,这样正在观看会话的人就能看到它产出了什么,而无需手动把路径复制到浏览器中。 它是侧边栏自带标签页(资源管理器、编辑器、终端、浏览器)的同级,而非覆盖层:它通过它们所使用的同一个公共扩展点进行注册。 它的功能 - 通过 ctx.betterSidebar.registerTab 注册一个标签页,dsh-artifacts:library。 - 按最新优先的顺序列出产物目录,并带有类型徽章、大小和存在时长。 - 点击某一行即可在标签页内的 iframe 中预览它,使用索引提供的相对 URL,因此它与 GUI 同源,并会随浏览器现有的会话 cookie 一起加载。 - Open ↗ 会在真实的浏览器标签页中打开它,Copy link 会复制可分享的绝对 URL,Reload 会重新获取预览,Refresh 会重新读取索引。 - 每 5 秒轮询一次,仅在标签页处于活动状态且面板打开时,使用侧边栏正是为此提供的 visible prop。
展开完整文档(README 剩余 1 节)
dsh-artifacts

为 DSH(DeepSeek Harness)Web GUI 侧边栏提供的 Artifacts(产物)标签页。

智能体会生成文件——一份 HTML 报告、一个仪表盘、一张图表、一个 CSV。此标签页会在侧边栏内按最新优先的顺序列出它们,并就地预览其中一个,这样正在观看会话的人就能看到它产出了什么,而无需手动把路径复制到浏览器中。

它是侧边栏自带标签页(资源管理器、编辑器、终端、浏览器)的同级,而非覆盖层:它通过它们所使用的同一个公共扩展点进行注册。

它的功能

- 通过 ctx.betterSidebar.registerTab 注册一个标签页,dsh-artifacts:library。
- 按最新优先的顺序列出产物目录,并带有类型徽章、大小和存在时长。
- 点击某一行即可在标签页内的 iframe 中预览它,使用索引提供的相对 URL,因此它与 GUI 同源,并会随浏览器现有的会话 cookie 一起加载。
- Open ↗ 会在真实的浏览器标签页中打开它,Copy link 会复制可分享的绝对 URL,Reload 会重新获取预览,Refresh 会重新读取索引。
- 每 5 秒轮询一次,仅在标签页处于活动状态且面板打开时,使用侧边栏正是为此提供的 visible prop。
- 工具栏和行的几何尺寸有意采用固定盒子:任何标签或度量都不会随状态改变宽度,因此轮询重新渲染不会让工具栏抖动。

如果你的服务器向所提供的 HTML 中注入实时重载片段(参考实现见 docs/artifact-index-endpoint.md),那么当文件被重新生成时,预览会自行刷新。本插件不实现该功能,也不需要了解它。

要求

- 已挂载 dsh-better-sidebar 的 DSH Web GUI(入口 id 为 better-sidebar)。
如果注册表缺失,客户端部分会记录一个错误并且不注册任何内容——它从不抛出异常,因此缺失的依赖会降级为“没有标签页”,而不是损坏的侧边栏。
- 某个同源 HTTP 端点,响应 docs/artifact-index-endpoint.md 中的小型 JSON 索引契约。DSH 本身不定义产物目录或索引路由,因此这是你需要接上的唯一一块。一个独立的参考服务器和一个发布脚本位于 contrib/。

安装

from GitHub
dsh plugin --profile web add github:lucagiftzek/dsh-artifacts

or from a checkout
git clone https://github.com/lucagiftzek/dsh-artifacts
dsh plugin --profile web add link:./dsh-artifacts

dsh plugin 是 pnpm 的透传,因此 add 也会把该包追加到
dsh.profile.bundles。移除命令是 dsh plugin --profile web remove dsh-artifacts。

激活需要重启 dsh-web,这会生成新的启动令牌并终止任何进行中的会话。
此后对 lib/client.js 的重新构建会被实时拾取——所提供的 rev 会变化,刷新浏览器即可。

将它指向你的产物

默认索引 URL 是 /report/?list=1。无需重新构建即可按浏览器覆盖它:

localStorage['dsh-artifacts:indexUrl'] = '/artifacts/index.json'
端点必须返回:

{ "count": 2, "items": [
{ "name": "report.html", "url": "/report/report.html", "ext": "html",
"size": 11160, "mtime": 1788934528 }
] }

url 可以是相对路径或绝对路径。插件在没有其他字段(badge、缺失的 size)的情况下也能渲染的任何内容,都会静默降级。完整契约,以及如何保持仅元数据并限制在一个目录内,见 docs/artifact-index-endpoint.md。

将列表限定到当前聊天

一个目录通常存放每个会话的输出,因此该标签页可以缩小列表范围。当侧边栏提供会话 id 时,请求会带上 &session=;能够归属工件的端点会为每个条目返回一个布尔值 mine,标签页会显示 This chat / All 切换开关,当它有内容时默认选中 This chat。

归属刻意是端点的职责,而不是插件的——只有宿主知道会话是什么。完全支持忽略该参数: 省略 mine,切换开关就不会渲染,这正是 contrib/ 中的参考服务器所做的。唯一要避免的错误是为你不了解的工件输出 mine: false;标签页会把布尔值解读为“归属已运行”,并会提供一个永久为空的 This chat 视图。除非它是真的,否则什么都不要说。docs/artifact-index-endpoint.md 描述了一条可行的转录扫描规则,包括为什么要缓存它并验证 id。

架构:为什么 node 那一半是空的

lib/index.js 不导入任何内容,也不注册任何内容。这是刻意的,两个原因都是值得在设计时防范的失败:

1. 它仍然必须存在。 客户端模块宿主扫描会从挂载在宿主 Loader 中的包读取 dsh.client 声明。没有挂载,就没有提供出去的 bundle。
2. 错误的宿主导入是致命的,而不是局部的。 本插件作者运行的一个 DSH profile 正是因此被搞垮:两个插件从 @deepseek-ai/dsh-settings 导入了已安装版本并未导出的符号。该 ESM 导入在加载时变成了硬性 SyntaxError,loader 条目抛出异常,dsh 在启动完成前就以 1 退出——数百次重启,站点完全宕机。一个零导入的宿主半部分不会以那种方式失败。
3. 未满足的 inject 会挂起启动。 宿主半部分上的 inject: [] 避免了由没有任何东西提供的 inject 所导致的“1 entry did not activate”失败。

所有数据都通过 HTTP 到达浏览器,因此该插件不需要宿主服务、不需要文件系统访问,也不需要设置命名空间。

构建

pnpm install # esbuild is a devDependency
node scripts/build.mjs # writes lib/client.js (+ map)

设置 ESBUILD_MAIN=/path/to/esbuild/lib/main.js 以针对位于其他位置的打包器进行构建,这可以保持构建离线。

客户端 bundle 是包装在 web 启动工厂中的 CJS
(window.__ModuleLoader__.load({ id, factory }))。react 和
react/jsx-runtime 保持外部化,并解析到 shell 自身的实例 ——所提供的 URL 是 combo 形式 /plugins/??dsh-artifacts/client.js&rev=…,而不是普通的按插件路径。构建后运行两道关卡:

- purity —— 任何 @deepseek-ai/ 都不得进入 bundle;值导入会内联一个 shell 包的重复运行时实例。
- seed words —— 每个裸 require 都必须是已声明的外部依赖,否则模块系统会在物化时大声抛出异常。

Test

node test/run.mjs # offline: no browser, no DSH_HOME, no network

test/render.mjs 以浏览器模块系统的方式物化 bundle,驱动 registerTab,并结构化地渲染六种状态 —— 空库、带选中项的有内容库、隐藏标签页、获取错误、消失的选中项,以及 busy 下的工具栏几何。它注入真实的 react/jsx-runtime,使元素是真正的元素,并为 react 提供一个可编程的 stub,这样 hooks 无需 dispatcher。

test/fetch.mjs 从轮询间隔中捕获 load(),并驱动每一种 fetch 结果,包括代理返回 HTML 而非 JSON 的 auth-redirect 情况。

这两个测试套件已经证明了它们的价值。render 套件捕获了一个消失的选中项在 effect 中被同步的问题,这会导致绘制一帧时 iframe 指向一个刚刚 404 的路径 —— 现在它是在渲染期间派生的。它后来还捕获了一个工具栏在每次轮询时改变宽度的问题,因为按钮标签随 busy 切换。

For agents

skills/publish-artifact/SKILL.md 是一个即插即用的技能,教 agent 这个标签页所读取的约定:在哪里写入 artifacts、如何命名它们,以及永远不要在其中放入什么。将它复制到 $DSH_HOME/skills/、~/.agents/skills/,或项目的 .dsh/skills/。

License

MIT —— 见 LICENSE。Copyright (c) 2026 Loukas "Luca" Tzekos.

随你怎么用。唯一的要求是署名:保留版权行,如果你公开 fork 它,在 README 中注明 "based on dsh-artifacts by Luca Tzekos" 会不胜感激。见 NOTICE。
预览分享卡片长什么样 ↓lucagiftzek/dsh-artifacts
分享 · DeepSeek Harness Hub
lucagiftzek/dsh-artifacts

为 DSHDeepSeek HarnessWeb GUI 侧边栏提供的 Artifacts产物标签页。

这是你的仓库?认领条目

回到顶部