把查询结果沉淀为可复用卡片并追溯每个数字来源
31.2/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/22 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add zshunxinz/data-canvas-dsh该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · tool
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 15 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件写入剪贴板(不计风险)读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 78 处,展示前 72 条)
- 发起网络请求client.js:511
const response = await fetch(`${API}${path}`, { - 写入剪贴板client.js:3442
await globalThis.navigator.clipboard.writeText(method) - 写入剪贴板client.js:3677
try { await navigator.clipboard.writeText(request.ref.prompt); setChosen('已复制') } - 写入剪贴板client.js:3781
try { await navigator.clipboard.writeText(usage.storage_path); setCopied(true) } catch { setCopied(false) } - 写入剪贴板client.js:4061
await navigator.clipboard.writeText(prompt) - 写入剪贴板client.js:4756
await navigator.clipboard.writeText(text) - 读取本地文件lib/store.js:89
if (!existsSync(path)) return fallback - 读取本地文件lib/store.js:91
return JSON.parse(readFileSync(path, 'utf8')) - 写入本地文件lib/store.js:99
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件lib/store.js:101
writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf8') - 写入本地文件lib/store.js:103
renameSync(tmp, path) - 删除本地文件lib/store.js:105
try { unlinkSync(tmp) } catch { } - 读取本地文件lib/store.js:360
const history = existsSync(path) ? readJson(path, { undo: [], redo: [] }) : { undo: [], redo: [] } - 读取本地文件lib/store.js:376
if (!existsSync(path)) return { undo: [], redo: [] } - 读取本地文件lib/store.js:487
if (existsSync(markerPath)) return null - 读取本地文件lib/store.js:537
if (!existsSync(path)) { - 删除本地文件lib/store.js:574
try { unlinkSync(path) } catch { } - 删除本地文件lib/store.js:810
unlinkSync(join(this.templateDir, `${id}.json`)) - 删除本地文件lib/store.js:978
unlinkSync(path) - 读取本地文件lib/routes.js:38
const PLUGIN_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version - 读取本地文件lib/routes.js:44
try { text = readFileSync(new URL('../CHANGELOG.md', import.meta.url), 'utf8') } catch { return [] } - 读取本地文件lib/trace-service.js:27
function readRef (path) { try { return JSON.parse(readFileSync(path, 'utf8')) } catch (error) { if (error.code === 'ENOENT') return null; throw new Ca - 写入本地文件lib/trace-service.js:30
try { writeFileSync(tmp, JSON.stringify(value), { flag: 'wx' }); renameSync(tmp, path) } - 写入本地文件lib/trace-service.js:30
try { writeFileSync(tmp, JSON.stringify(value), { flag: 'wx' }); renameSync(tmp, path) } - 删除本地文件lib/trace-service.js:31
finally { try { unlinkSync(tmp) } catch (error) { if (error.code !== 'ENOENT') throw error } } - 写入本地文件lib/trace-service.js:180
mkdirSync(this.followups, { recursive: true }); writeRef(path, record) - 读取本地文件lib/reference-service.js:86
if (!existsSync(this.usages)) return [] - 读取本地文件lib/reference-service.js:88
for (const name of readdirSync(this.usages)) { - 读取本地文件lib/reference-service.js:90
for (const line of readFileSync(join(this.usages, name), 'utf8').split('\n')) { - 读取本地文件lib/execution-evidence.js:16
if (existsSync(path)) return - 写入本地文件lib/execution-evidence.js:21
mkdirSync(dir, { recursive: true }) - 写入本地文件lib/execution-evidence.js:23
writeFileSync(tmp, JSON.stringify(record), { flag: 'wx' }); renameSync(tmp, path) - 写入本地文件lib/execution-evidence.js:23
writeFileSync(tmp, JSON.stringify(record), { flag: 'wx' }); renameSync(tmp, path) - 读取本地文件lib/execution-evidence.js:28
if (!existsSync(path)) return null - 读取本地文件lib/execution-evidence.js:29
return JSON.parse(readFileSync(path, 'utf8')) - 写入本地文件lib/example-data.js:19
mkdirSync(root, { recursive: true }) - 读取本地文件lib/example-data.js:21
if (existsSync(marker)) return false - 读取本地文件lib/example-data.js:22
if (!existsSync(EXAMPLE_FILE)) { - 写入本地文件lib/example-data.js:24
writeFileSync(marker, new Date().toISOString(), 'utf8') - 读取本地文件lib/example-data.js:30
const bytes = readFileSync(EXAMPLE_FILE) - 写入本地文件lib/example-data.js:39
writeFileSync(marker, new Date().toISOString(), 'utf8') - 写入本地文件lib/data-store.js:37
try { writeFileSync(tmp, text, { encoding: 'utf8', flag: 'wx' }); renameSync(tmp, path) } - 写入本地文件lib/data-store.js:37
try { writeFileSync(tmp, text, { encoding: 'utf8', flag: 'wx' }); renameSync(tmp, path) } - 删除本地文件lib/data-store.js:38
finally { try { unlinkSync(tmp) } catch (e) { if (e.code !== 'ENOENT') throw e } } - 读取本地文件lib/data-store.js:41
try { return JSON.parse(readFileSync(path, 'utf8')) } - 写入本地文件lib/data-store.js:49
mkdirSync(this.inputs, { recursive: true }) - 读取本地文件lib/data-store.js:51
if (!existsSync(path)) write(path, input) - 读取本地文件lib/data-store.js:62
try { files = readdirSync(this.dir) } catch (error) { if (error.code === 'ENOENT') return []; throw error } - 写入本地文件lib/data-store.js:81
mkdirSync(this.dir, { recursive: true }); write(join(this.dir, `${data.id}.json`), data) - 删除本地文件lib/data-store.js:98
remove (id) { const data = this.get(id); unlinkSync(join(this.dir, `${validId(id)}.json`)); if (data.secret_ref) deleteSecret(this.root, data.secret_r - 写入本地文件lib/data-store.js:153
mkdirSync(this.results, { recursive: true }); write(join(this.results, `${result.id}.json`), result) - 写入本地文件lib/data-store.js:183
mkdirSync(this.dir, { recursive: true }) - 读取本地文件lib/data-store.js:254
try { files = readdirSync(this.results) } catch (error) { if (error.code === 'ENOENT') return []; throw error } - 读取本地文件lib/data-store.js:287
try { files = readdirSync(this.results) } catch (error) { if (error.code === 'ENOENT') return []; throw error } - 读取密钥环境变量lib/secrets.js:20
const raw = process.env.XCHARTS_SECRET_KEY - 读取本地文件lib/secrets.js:30
if (existsSync(path)) { - 读取本地文件lib/secrets.js:31
const key = readFileSync(path) - 写入本地文件lib/secrets.js:35
mkdirSync(root, { recursive: true }) - 写入本地文件lib/secrets.js:37
writeFileSync(path, key, { mode: 0o600 }) - 写入本地文件lib/secrets.js:39
try { chmodSync(path, 0o600) } catch { } - 写入本地文件lib/secrets.js:59
mkdirSync(secretsDir(root), { recursive: true }) - 写入本地文件lib/secrets.js:60
writeFileSync(recordPath(root, ref), JSON.stringify(record), { mode: 0o600 }) - 读取本地文件lib/secrets.js:67
try { record = JSON.parse(readFileSync(recordPath(root, ref), 'utf8')) } catch { throw new CanvasError('SECRET_NOT_FOUND', '凭据不存在或已被删除') } - 删除本地文件lib/secrets.js:83
try { unlinkSync(recordPath(root, ref)); return true } catch { return false } - 读取本地文件lib/storage-maintenance.js:19
const entries = readdirSync(path) - 读取本地文件lib/storage-maintenance.js:22
try { const info = statSync(join(path, entry)); if (info.isFile()) { bytes += info.size; files++ } } catch { } - 读取本地文件lib/storage-maintenance.js:29
try { return readdirSync(root).reduce((sum, entry) => { try { return sum + (statSync(join(root, entry)).isFile() ? 1 : 0) } catch { return sum } }, 0) - 读取本地文件lib/storage-maintenance.js:29
try { return readdirSync(root).reduce((sum, entry) => { try { return sum + (statSync(join(root, entry)).isFile() ? 1 : 0) } catch { return sum } }, 0) - 读取本地文件lib/storage-maintenance.js:64
if (!existsSync(path)) return { target, label: meta.label, deleted_files: 0, freed_bytes: 0 } - 删除本地文件递归lib/storage-maintenance.js:66
rmSync(path, { recursive: true, force: true }) - 写入本地文件lib/usage-log.js:11
mkdirSync(dir, { recursive: true }) - 写入本地文件lib/usage-log.js:12
appendFileSync(join(dir, `${record.at.slice(0, 7)}.jsonl`), `${JSON.stringify(record)}\n`, 'utf8')
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
数据画布,把dsh分析数据的结果变成可复用的分析卡片:支持导入文件或连接数据库(只读),查询、看结果、落卡、在对话里引用、追溯每个数字的来源。
README
由 DeepSeek 最新模型翻译生成data-canvas-dsh
把数据变成可复用的分析卡片:导入文件或连接数据库(只读),查询、看结果、落卡、在对话里引用、追溯每个数字的来源。
这是 DeepSeek Harness 的一个插件。它不要求你写代码,也不接管你的数据:文件与查询结果都保存在本机,数据库只读访问,密码不会离开这台电脑。
它适合谁
- 想在一个地方反复查看同一批分析结果,而不是每次重新问 AI;
- 需要把「某个数字怎么来的」讲清楚(查询、数据源、卡片版本都能追溯);
- 用聊天推进分析,但希望结果能沉淀成卡片、随时回来改。
装上之后,先看示例
安装并重启 DSH 后,打开侧边栏的「数据源」,列表里已经有一份:
零售订单(示例) — 1,200 行、19 列
不需要你准备任何数据。它包含 2025 年全年的订单:门店、渠道、商品类别、数量、单价、折扣、销售额、成本、利润、评分等,还带少量缺失值,用来展示解析提示。你可以直接查询它,也可以随时删除。
想用自己的数据:点「新增数据源」拖入 CSV/TSV/XLSX/Parquet,或连接 MySQL/PostgreSQL。
数据源与分析:默认示例数据
五分钟上手
1. 看一份数据
左侧选中「零售订单(示例)」,右侧就是数据预览;表结构栏会列出字段,点表名可展开字段,右键可看完整表结构或 CREATE TABLE 语法。
展开完整文档(README 剩余 1 节)
data-canvas-dsh
把数据变成可复用的分析卡片:导入文件或连接数据库(只读),查询、看结果、落卡、在对话里引用、追溯每个数字的来源。
这是 DeepSeek Harness 的一个插件。它不要求你写代码,也不接管你的数据:文件与查询结果都保存在本机,数据库只读访问,密码不会离开这台电脑。
它适合谁
- 想在一个地方反复查看同一批分析结果,而不是每次重新问 AI;
- 需要把「某个数字怎么来的」讲清楚(查询、数据源、卡片版本都能追溯);
- 用聊天推进分析,但希望结果能沉淀成卡片、随时回来改。
装上之后,先看示例
安装并重启 DSH 后,打开侧边栏的「数据源」,列表里已经有一份:
零售订单(示例) — 1,200 行、19 列
不需要你准备任何数据。它包含 2025 年全年的订单:门店、渠道、商品类别、数量、单价、折扣、销售额、成本、利润、评分等,还带少量缺失值,用来展示解析提示。你可以直接查询它,也可以随时删除。
想用自己的数据:点「新增数据源」拖入 CSV/TSV/XLSX/Parquet,或连接 MySQL/PostgreSQL。
数据源与分析:默认示例数据
五分钟上手
1. 看一份数据
左侧选中「零售订单(示例)」,右侧就是数据预览;表结构栏会列出字段,点表名可展开字段,右键可看完整表结构或 CREATE TABLE 语法。
2. 查一个问题
- 文件数据源(包括示例):点「配置查询」,选择「门店」作为分组、指标「销售额 · 求和」,执行;
- 数据库:在 SQL 框里写查询,Ctrl/Cmd + Enter 执行:
SELECT store, SUM(amount) AS amount
FROM orders
GROUP BY store
ORDER BY amount DESC
3. 把结果留下来
- 点「添加到画布」→ 结果变成一张卡片,在「分析画布」里拖拽布局;
- 点「在对话中使用」→ 把结果的引用发给 AI,它回答时会先读取这份结果;
- 卡片菜单里可以查看溯源:这张卡片来自哪次查询、哪个数据源、哪个版本。
4. 回到对话继续
在聊天里引用数据源或结果,AI 会基于真实数据回答;追问卡片时,它会沿着卡片版本继续,而不是重新猜。
分析画布
对话中的卡片与引用
功能一览
数据源
- 文件:CSV / TSV / XLSX / Parquet,导入时给出编码、分隔符、缺失值等解析提示,失败可调整设置重试;
- 数据库:MySQL / PostgreSQL,只读连接,支持测试连接、表结构浏览、按字段名找表;
- 查询:只读 SQL(单条、自动 LIMIT、15 秒超时、可取消)、常用 SQL、最近执行、格式化。
结果
- 表格:排序、列显隐、列宽拖拽、冻结首列、全屏、表内搜索、复制、导出 CSV、字段画像;
- NULL 与空字符串区分显示(NULL 是淡灰「—」);
- 结果可添加为画布卡片,或在对话里引用。
分析画布
- 网格布局、拖拽/缩放、对齐、撤销重做;
- 卡片数据版本与溯源、复算(结果口径变化会提示)。
数据管理
- 引用协议 xcharts:ds/...、xcharts:res/...、xcharts:card/...,AI 先解析引用再回答;
- 使用情况(哪些卡片/会话用过这份数据)、备份/恢复、存储占用与清理、插件内更新记录。
安装
前置:DeepSeek Harness(桌面或 dsh web),Node.js 22+。
方式一:从 GitHub 安装(当前推荐)
在 profile 的 package.json 里声明 git 依赖(固定到发布 tag,避免分支变动):
{
"dependencies": {
"data-canvas-dsh": "github:zshunxinz/data-canvas-dsh#v0.28.1"
}
}
并在 dsh.profile.bundles 中加入 data-canvas-dsh,然后重启 DSH。
仓库已提交构建产物(client.js、client.echarts.js),git 安装无需再跑构建;依赖(pg/mysql2/hyparquet)会随安装自动装上。
方式二:本地源码(开发/调试)
{
"dependencies": { "data-canvas-dsh": "link:<path-to-data-canvas-dsh>" }
}
并在 dsh.profile.bundles 中加入 data-canvas-dsh,然后重启 DSH。
npm 包暂未发布;发布后会补 npm install data-canvas-dsh 的方式。
宿主侧的路由与工具在启动时加载,因此安装或升级后需要重启 DSH;浏览器页面刷新即可。
安全与隐私
- 只读:数据库连接使用只读事务;SQL 守卫只放行单条查询,拒绝写操作、DDL、多语句;插件不提供数据编辑。
- 密码不出本机:凭据用 AES-256-GCM 加密保存在本地 secrets/;数据源列表、Agent 工具输出、日志、备份、导出文件中都没有密码。
- 主机打码:工具与溯源输出中显示 10.0.. 这样的形式。
- 数据不离开本机:插件不向外部服务上传数据;AI 只能通过你主动引用的内容看到数据。
建议连接数据库时使用只读账号——代码守卫是纵深防御,不能替代数据库权限。
兼容性
- DSH:开发与验证于 0.2.0-rc.2 系列;
- Node.js:22+;
- 数据库:MySQL 5.7+ / PostgreSQL 12+;
- 文件:CSV / TSV / XLSX / Parquet,单文件最多 50,000 行;
- 数据源结果最多保存 5,000 行。
常见问题
示例数据从哪来?可以删吗? 来自仓库的 examples/retail-orders.csv,首次运行时自动导入。可以随时删除,删掉后不会被重新加回来。想找回:删除 ~/.dsh/storages/xcharts-canvas/.example-source-imported 后重启,或手动导入该文件。
数据存在哪里? 默认路径 ~/.dsh/storages/xcharts-canvas/(画布、数据源、结果、凭据)。备份/导出在「设置」里操作,备份不包含密码。
为什么连不上数据库? 先用「测试连接」确认主机、端口、库名与账号;只读账号需要相应表的查询权限。错误提示会给出可操作建议。
能修改数据库里的数据吗? 不能,这是刻意的设计。插件只用于分析,写操作请用专门的数据库工具。
SQL 有没有限制? 单条语句、只读、自动加 LIMIT、15 秒超时;SELECT / WITH / EXPLAIN / SHOW / DESCRIBE 等可执行。
更多文档
- 使用说明 — 界面、功能与常见操作
- 架构 — 宿主/客户端、数据存储、API、引用协议
- 安全 — 只读、凭据、脱敏、威胁模型与限制
- 开发 — 构建、测试、代码组织与发布
开发
pnpm install
pnpm build:client # 拼接 client-src 为 client.js
pnpm test # 构建 + 全量测试
pnpm pack # 生成可安装的 tgz
示例数据可用 python scripts/make-example-data.py 重新生成(固定随机种子)。
许可与贡献
MIT。欢迎 issue 与 PR;提交前请跑 pnpm test。
依赖许可:pg、mysql2、hyparquet、csv-parse、exceljs 为 MIT,echarts 为 Apache-2.0。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
zshunxinz/data-canvas-dsh
把查询结果沉淀为可复用卡片并追溯每个数字来源
相关插件 · 工具 / 效率
破甲管控台kingcharleslzy-ai/dsh-purge
dsh-purge Community · DeepSeek Harness 社区维护版:宿主兼容、状态诊断、提示词管理、更新与本机备份回滚。基于小杨 / YuJunZhiXue 的 dsh-purge。
★ 4,330已汉化DeepSeek 客户端
74.5
网文长卷lingfengQAQ/webnovel-writer
基于 Claude Code 的长篇网文辅助创作系统,解决 AI 写作中的「遗忘」和「幻觉」问题,支持 200…
★ 7,636已汉化DSH
72.2
多智能体协作HKUDS/AutoSwarm
AutoSwarm: Unify Your Agents. Unify Your Context (Codex · Claude Code · DeepSeek Harness · Muse · Grok · WorkBuddy· nanobot · and more)
★ 2,349已汉化DSH
71.6
Vox 拼贴视频生成Alisa0808/vox-director
🎬 Vox Director拼贴动效导演
★ 2,129已汉化DSH
71.4
Agent 演化引擎Q00/ouroboros
智能体 OS:智能体自己变聪明,我们只守住底线:面试准入、分阶段评估、带预算的进化循环。MCP 服务器,14 种运行时:Claude Code、Codex CLI、Gemini CLI、OpenCode、Copilot、Kiro 等。
★ 6,311已汉化DSH
71.4
智能体运行框架路由HarnessRouter/harnessrouter
智能体 harness 的统一接口。
★ 3,984已汉化DSH
71.3
这是你的仓库?认领条目