在 Chrome 侧边栏里读页面、填表单、翻译并汇总多标签内容
29.7/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/9/16 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add jerryxugit-2026/dsh-web-companion该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · browser
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 51 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-web-companion(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/9/21 23:55:54
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 136 处,展示前 110 条)
- 读取本地文件bootstrap/doctor.mjs:55
if (!existsSync(patchFile)) return null - 读取本地文件bootstrap/doctor.mjs:56
return readCompanionEntry(readFileSync(patchFile, 'utf8'), PLUGIN_ID).entryPath ?? null - 执行外部命令来自配置/变量bootstrap/install.mjs:177
const res = spawnSync(cmd, args, { stdio: 'inherit', env: { ...process.env, DSH_HOME: dshHome }, ...opts }) - 执行外部命令固定命令bootstrap/install.mjs:266
try { return execFileSync('npm', ['prefix', '-g'], { encoding: 'utf8' }).trim() } catch { return null } - 读取本地文件bootstrap/install.mjs:276
const namedDshMissing = namedDsh !== null && !existsSync(namedDsh.path) - 读取本地文件bootstrap/install.mjs:277
const chosenDsh = dshCandidates.find((c) => existsSync(c.path)) ?? null - 读取本地文件bootstrap/install.mjs:289
const esbuildReady = [esbuildTarget, join(ROOT, 'extension', 'node_modules', 'esbuild')].find((x) => existsSync(x)) ?? null - 读取本地文件bootstrap/install.mjs:298
return JSON.parse(readFileSync(file, 'utf8')).devDependencies?.esbuild ?? null - 读取本地文件bootstrap/install.mjs:319
if (!existsSync(layout.pairingFile)) return null - 读取本地文件bootstrap/install.mjs:321
const origins = JSON.parse(readFileSync(layout.pairingFile, 'utf8'))?.extensionOrigins - 写入本地文件bootstrap/install.mjs:484
mkdirSync(layout.installDir, { recursive: true }) - 删除本地文件递归bootstrap/install.mjs:510
rmSync(dest, { recursive: true, force: true }) - 写入本地文件bootstrap/install.mjs:511
mkdirSync(dirname(dest), { recursive: true }) - 删除本地文件递归bootstrap/install.mjs:560
rmSync(esbuildTarget, { recursive: true, force: true }) - 写入本地文件bootstrap/install.mjs:561
mkdirSync(esbuildTarget, { recursive: true }) - 写入本地文件bootstrap/install.mjs:583
copyFileSync(layout.pairingFile, `${layout.pairingFile}.bak-before-install`) - 执行外部命令来自配置/变量bootstrap/install.mjs:624
const probeImport = spawnSync(process.execPath, ['--input-type=module', '-e', - 写入本地文件bootstrap/install.mjs:690
if (existsSync(layout.profilePatch) && !existsSync(`${layout.profilePatch}.bak-before-companion`)) copyFileSync(layout.profilePatch, `${layout.profile - 写入本地文件bootstrap/install.mjs:691
mkdirSync(dirname(layout.profilePatch), { recursive: true }) - 删除本地文件递归bootstrap/lib/dsh-root.mjs:130
const rm = io.rm ?? ((p) => rmSync(p, { recursive: true, force: true })) - 写入本地文件bootstrap/lib/dsh-root.mjs:131
const mkdir = io.mkdir ?? ((p) => mkdirSync(p, { recursive: true })) - 执行外部命令来自配置/变量bootstrap/lib/health.mjs:237
const spawnFn = o.spawn ?? ((cmd, args, opts) => spawnSync(cmd, args, opts)) - 读取本地文件bootstrap/lib/health.mjs:249
if (existsSync(patchFile)) mountEntry = readCompanionEntry(readFileSync(patchFile, 'utf8'), PLUGIN_ID).entryPath ?? null - 读取本地文件bootstrap/lib/health.mjs:249
if (existsSync(patchFile)) mountEntry = readCompanionEntry(readFileSync(patchFile, 'utf8'), PLUGIN_ID).entryPath ?? null - 读取本地文件bootstrap/lib/health.mjs:254
if (existsSync(checkDistScript)) { - 写入本地文件bootstrap/lib/native-host-install.mjs:97
mkdirSync(dirname(plan.runnerPath), { recursive: true }) - 写入本地文件bootstrap/lib/native-host-install.mjs:98
writeFileSync(plan.runnerPath, plan.runnerBody) - 写入本地文件bootstrap/lib/native-host-install.mjs:99
chmodSync(plan.runnerPath, 0o755) - 写入本地文件bootstrap/lib/native-host-install.mjs:101
mkdirSync(dirname(plan.manifestPath), { recursive: true }) - 写入本地文件bootstrap/lib/native-host-install.mjs:108
writeFileSync(manifestTmp, `${JSON.stringify(plan.manifest, null, 2)}\n`) - 写入本地文件bootstrap/lib/native-host-install.mjs:109
renameSync(manifestTmp, plan.manifestPath) - 删除本地文件bootstrap/lib/native-host-install.mjs:117
rmSync(plan.manifestPath, { force: true }) - 执行外部命令固定命令bootstrap/lib/probe.mjs:19
const out = execFileSync('/bin/sh', ['-c', 'command -v "$1"', 'sh', cmd], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() - 执行外部命令来自配置/变量bootstrap/lib/probe.mjs:74
return execFileSync(dshPath, ['-V'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim().replace(/^v/u, '') || null - 执行外部命令固定命令bootstrap/lib/probe.mjs:83
const out = execFileSync('lsof', ['-nP', `-iTCP:${String(port)}`, '-sTCP:LISTEN'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }) - 发起网络请求bootstrap/lib/probe.mjs:103
const res = await fetch(url, { signal: AbortSignal.timeout(2000) }) - 读取本地文件bootstrap/lib/probe.mjs:133
if (!existsSync(path)) { - 读取本地文件bootstrap/lib/probe.mjs:137
if (existsSync(at)) { - 读取本地文件bootstrap/lib/probe.mjs:138
try { accessSync(at, constants.W_OK); return 'missing' } catch { return 'unwritable' } - 读取本地文件bootstrap/lib/probe.mjs:147
try { accessSync(at, constants.W_OK); return 'missing' } catch { return 'unwritable' } - 读取本地文件bootstrap/lib/probe.mjs:150
if (!statSync(path).isDirectory()) return 'unwritable' - 读取本地文件bootstrap/lib/probe.mjs:151
accessSync(path, constants.W_OK) - 读取本地文件bootstrap/uninstall.mjs:55
if (!existsSync(patchFile)) return null - 读取本地文件bootstrap/uninstall.mjs:56
return readCompanionEntry(readFileSync(patchFile, 'utf8'), PLUGIN_ID).entryPath ?? null - 读取本地文件bootstrap/uninstall.mjs:77
const patchText = existsSync(layout.profilePatch) ? readFileSync(layout.profilePatch, 'utf8') : '' - 读取本地文件bootstrap/uninstall.mjs:77
const patchText = existsSync(layout.profilePatch) ? readFileSync(layout.profilePatch, 'utf8') : '' - 写入本地文件bootstrap/uninstall.mjs:84
copyFileSync(layout.profilePatch, `${layout.profilePatch}.bak-before-uninstall`) - 写入本地文件bootstrap/uninstall.mjs:85
writeFileSync(layout.profilePatch, removal.text) - 读取本地文件bootstrap/uninstall.mjs:92
if (plan.manifestPath !== null && existsSync(plan.manifestPath) && await w.confirm('删掉这个清单?(拉起器随安装目录一起删)')) { - 删除本地文件bootstrap/uninstall.mjs:93
rmSync(plan.manifestPath, { force: true }) - 读取本地文件bootstrap/uninstall.mjs:101
} else if (existsSync(installDir) && await w.confirm('删掉整个安装目录?')) { - 删除本地文件递归bootstrap/uninstall.mjs:102
rmSync(installDir, { recursive: true, force: true }) - 删除本地文件bootstrap/uninstall.mjs:110
rmSync(layout.pairingFile, { force: true }) - 读取本地文件extension/build.mjs:34
for (const entry of readdirSync(dir)) { - 读取本地文件extension/build.mjs:36
const info = statSync(full) - 删除本地文件递归extension/build.mjs:44
rmSync(DIST, { recursive: true, force: true }) - 写入本地文件extension/build.mjs:45
mkdirSync(DIST, { recursive: true }) - 读取本地文件extension/build.mjs:46
const entryPoints = Object.keys(ENTRIES).filter((rel) => existsSync(join(HERE, rel))) - 读取本地文件extension/build.mjs:64
if (!existsSync(produced)) continue - 写入本地文件extension/build.mjs:66
mkdirSync(dirname(target), { recursive: true }) - 删除本地文件extension/build.mjs:68
if (produced !== target) rmSync(produced, { force: true }) - 写入本地文件extension/build.mjs:72
mkdirSync(dirname(target), { recursive: true }) - 读取本地文件extension/build.mjs:80
if (!existsSync(DIST)) { - 写入本地文件extension/build.mjs:104
mkdirSync(dirname(outPath), { recursive: true }) - 写入本地文件extension/build.mjs:105
writeFileSync(outPath, `${JSON.stringify(report, null, 2)}\n`) - 读取本地文件extension/i18n/codegen.mjs:45
const source = JSON.parse(readFileSync(SOURCE, 'utf8')) - 读取本地文件extension/i18n/codegen.mjs:114
if (!existsSync(t.path)) { - 读取本地文件extension/i18n/codegen.mjs:119
if (readFileSync(t.path, 'utf8') !== t.body) { - 写入本地文件extension/i18n/codegen.mjs:133
mkdirSync(dirname(t.path), { recursive: true }) - 写入本地文件extension/i18n/codegen.mjs:134
writeFileSync(t.path, t.body) - 发起网络请求extension/src/lib/result.js:32
const response = await fetch(url, { ...init, signal: controller.signal }) - 写入本地文件native-host/host.mjs:26
mkdirSync(dirname(logFile), { recursive: true }) - 写入本地文件native-host/host.mjs:27
appendFileSync(logFile, `${new Date().toISOString()} ${line}\n`) - 读取本地文件native-host/install.mjs:30
const manifest = JSON.parse(readFileSync(join(ROOT, 'extension', 'manifest.json'), 'utf8')) - 删除本地文件native-host/install.mjs:37
rmSync(MANIFEST_PATH, { force: true }) - 写入本地文件native-host/install.mjs:63
mkdirSync(MANIFEST_DIR, { recursive: true }) - 写入本地文件native-host/install.mjs:64
writeFileSync(RUNNER, runnerBody) - 写入本地文件native-host/install.mjs:65
chmodSync(RUNNER, 0o755) - 写入本地文件native-host/install.mjs:66
writeFileSync(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}\n`) - 读取本地文件native-host/install.mjs:71
console.log(` host exists: ${existsSync(join(HERE, 'host.mjs'))}`) - 写入本地文件native-host/launcher.mjs:105
try { await writeFile(stateFile(dshHome), `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 }) } catch { } - 读取本地文件native-host/launcher.mjs:111
try { return JSON.parse(await readFile(stateFile(dshHome), 'utf8')) } catch { return null } - 读取本地文件native-host/rotate.mjs:29
const bytes = statSync(file).size - 读取本地文件native-host/rotate.mjs:31
const lines = readFileSync(file, 'utf8').split('\n').filter((line) => line !== '') - 写入本地文件native-host/rotate.mjs:33
writeFileSync(file, kept.length === 0 ? '' : `${kept.join('\n')}\n`, 'utf8') - 读取本地文件protocol/codegen.mjs:30
const schemaText = readFileSync(SCHEMA_PATH, 'utf8') - 读取本地文件protocol/codegen.mjs:200
const existing = existsSync(artifactSpec.path) ? readFileSync(artifactSpec.path, 'utf8') : '' - 读取本地文件protocol/codegen.mjs:200
const existing = existsSync(artifactSpec.path) ? readFileSync(artifactSpec.path, 'utf8') : '' - 写入本地文件protocol/codegen.mjs:207
mkdirSync(dirname(artifactSpec.path), { recursive: true }) - 写入本地文件protocol/codegen.mjs:208
writeFileSync(artifactSpec.path, content) - 写入本地文件spike/cdp-embed.mjs:28
mkdirSync(OUT, { recursive: true }) - 执行外部命令来自配置/变量spike/cdp-embed.mjs:31
const cookie = execFileSync(process.execPath, [join(HERE, 'mint-cookie.mjs'), '127.0.0.1:3080'], { encoding: 'utf8' }).trim() - 发起网络请求spike/cdp-embed.mjs:75
const browser = await Cdp.connect((await (await fetch(`http://127.0.0.1:${PORT}/json/version`)).json()).webSocketDebuggerUrl) - 发起网络请求spike/cdp-embed.mjs:103
record.http.set(key, (record.http.get(key) ?? 0) + 1) - 写入本地文件spike/cdp-embed.mjs:238
writeFileSync(path, Buffer.from(data, 'base64')) - 写入本地文件spike/cdp-embed.mjs:242
writeFileSync(join(OUT, 'embed-report.json'), `${JSON.stringify(report, null, 2)}\n`) - 写入本地文件spike/cdp-panel.mjs:26
mkdirSync(OUT, { recursive: true }) - 执行外部命令来自配置/变量spike/cdp-panel.mjs:29
const cookie = execFileSync(process.execPath, [join(HERE, 'mint-cookie.mjs'), '127.0.0.1:3080'], { encoding: 'utf8' }).trim() - 发起网络请求spike/cdp-panel.mjs:73
const browser = await Cdp.connect((await (await fetch(`http://127.0.0.1:${PORT}/json/version`)).json()).webSocketDebuggerUrl) - 发起网络请求spike/cdp-panel.mjs:103
record.http.set(key, (record.http.get(key) ?? 0) + 1) - 写入本地文件spike/cdp-panel.mjs:219
writeFileSync(path, Buffer.from(data, 'base64')) - 写入本地文件spike/cdp-panel.mjs:233
writeFileSync(join(OUT, 'panel-report.json'), `${JSON.stringify(report, null, 2)}\n`) - 写入本地文件spike/cdp-spike.mjs:30
mkdirSync(OUT, { recursive: true }) - 执行外部命令来自配置/变量spike/cdp-spike.mjs:32
const cookie = execFileSync(process.execPath, [join(HERE, 'mint-cookie.mjs'), DSH_AUTHORITY], { encoding: 'utf8' }).trim() - 发起网络请求spike/cdp-spike.mjs:80
const browser = await Cdp.connect((await (await fetch(`http://127.0.0.1:${PORT}/json/version`)).json()).webSocketDebuggerUrl) - 发起网络请求spike/cdp-spike.mjs:97
record.http.set(key, (record.http.get(key) ?? 0) + 1) - 写入本地文件spike/cdp-spike.mjs:141
writeFileSync(path, Buffer.from(data, 'base64')) - 写入本地文件spike/cdp-spike.mjs:203
writeFileSync(join(OUT, 'report.json'), `${JSON.stringify(report, null, 2)}\n`) - 发起网络请求spike/ext/panel.js:14
const response = await fetch(`${DSH_ORIGIN}/api/session/list`, { - 读取本地文件spike/mint-cookie.mjs:27
const text = readFileSync(join(home, '.credentials.yaml'), 'utf8')
扫描于 2026/11/4(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 侧边栏读取页面
- 自动填写表单
- 翻译并汇总多标签
Chrome 侧边栏,把 DeepSeek Harness(DSH)智能体放在你正在读的页面旁边——自己读,或让智能体来操作。基于 DeepSeek Harness 构建;与 DeepSeek 无隶属或背书关系。
README
由 DeepSeek 最新模型翻译生成DSH 浏览器助手
侧边栏:DSH 已连接,四个按钮(附加页面 / 附加选中内容 / 浏览器控制 / 写入操作),以及一个在聊天中以 @文件 引用的已捕获页面
English(本节) · 中文 · 安装指南 · 安装部署
一个 AI 智能体,就坐在你正在浏览的网页旁边,位于 Chrome 侧边栏中。
它为你阅读页面,而且——当你开启该功能时——它还会操作页面:填写表单、翻译、点击、把多个标签页中的内容收集到一处。
它运行在你自己的电脑上(通过 DeepSeek Harness)。不会向第三方浏览器服务发送任何内容。
你实际要做的事——只有三件,别无其他:
1. 下载并解压——见下方第 1 步。
2. 在终端中运行两条命令——向导在每次更改前都会询问你,并且只询问一次你的 DeepSeek API 密钥。
3. 在 Chrome 中加载扩展——向导会明确告诉你该选择哪个文件夹。
其他所有事情——包括构建扩展——都由向导为你完成。无需编译器、无需 Xcode、无需开发者工具:只需要 Node 22 或更高版本。
背景:我为什么构建这个
我是 Jerry。我发现浏览器侧边栏加上 AI 智能体能解决一大类日常的 “在网页上做事”的问题——那些过去意味着来回复制粘贴的事情:
- 填写表单——申请、注册、税务和登记表格。
展开完整文档(README 剩余 1 节)
DSH 浏览器助手 侧边栏:DSH 已连接,四个按钮(附加页面 / 附加选中内容 / 浏览器控制 / 写入操作),以及一个在聊天中以 @文件 引用的已捕获页面 English(本节) · 中文 · 安装指南 · 安装部署 一个 AI 智能体,就坐在你正在浏览的网页旁边,位于 Chrome 侧边栏中。 它为你阅读页面,而且——当你开启该功能时——它还会操作页面:填写表单、翻译、点击、把多个标签页中的内容收集到一处。 它运行在你自己的电脑上(通过 DeepSeek Harness)。不会向第三方浏览器服务发送任何内容。 你实际要做的事——只有三件,别无其他: 1. 下载并解压——见下方第 1 步。 2. 在终端中运行两条命令——向导在每次更改前都会询问你,并且只询问一次你的 DeepSeek API 密钥。 3. 在 Chrome 中加载扩展——向导会明确告诉你该选择哪个文件夹。 其他所有事情——包括构建扩展——都由向导为你完成。无需编译器、无需 Xcode、无需开发者工具:只需要 Node 22 或更高版本。 背景:我为什么构建这个 我是 Jerry。我发现浏览器侧边栏加上 AI 智能体能解决一大类日常的 “在网页上做事”的问题——那些过去意味着来回复制粘贴的事情: - 填写表单——申请、注册、税务和登记表格。 - 翻译页面——保持布局和术语一致,而不只是逐字翻译。 - 总结长页面——一份 50 页的政策、一个长帖子、一个文档页面。 - 从多个标签页收集内容——在三个商店比较同一件商品,从五个页面收集地址。 起初我使用 Chrome + ChatGPT 扩展,它确实很棒。但我注意到中国很多人无法使用 ChatGPT,而 DeepSeek 4.1 Flash 已经非常好,并且在中国可以使用。然而 DeepSeek Harness 没有这样的扩展。所以我构建了这个。 这就是全部原因:把我喜欢的体验,变成我身边的人也能用的东西,并在本地运行。 你可以用它做什么 你打开侧边栏,就拥有了一个能看到你所在页面的智能体。 - 询问你正在阅读的页面。 “这份合同实际上让我承诺了什么?”“用五点总结这个。” 无需复制,无需粘贴——页面已经摆在它面前。 - 填写表单。 给它一个表单页面,告诉它你想说什么;它会读取每个字段,理解什么该填在哪里,然后填写。在更改任何内容之前,它会先询问你。 - 翻译。 整个页面,或只是你选中的部分——术语保持一致。 - 总结长内容。 长政策页面、论坛帖子、文档:获取重要的部分,然后针对它们提出后续问题。 - 从多个标签页收集。“在三个商店比较这个产品。”“从这五个页面中分别获取地址。”它会将它们汇总到一个答案或一张表中。 - 以三种方式把它正在看的内容交给它。可以是页面、你选中的文本,或截图(用于图表、画布、PDF 查看器——那些不是文本的东西)。还有一个右键菜单,如果你在聊天中输入「看左边」(或“look left”),它会自动抓取另一个标签页中的页面。 - 让它真正点击和输入。打开写操作开关后,它可以填写、点击、选择、滚动和导航——这些是完成表单或走完向导所需的操作。除非你刻意关闭询问,否则每一个此类操作都会先由你确认(在这种情况下,它们会被拒绝,而不是被悄悄允许)。 它捕获的所有内容都会作为普通 Markdown 文件保存在你的工作区中,并在聊天中以 @网页捕获/….md 的形式引用——因此你可以像任何其他文件一样阅读、编辑、保留或删除它。 工作原理 ┌──────────────────────────── Chrome ────────────────────────────┐ │ the page you are on side panel │ │ ┌──────────────┐ ┌─────────────────────────┐ │ │ │ web page │ │ Companion panel │ │ │ │ │ │ page / selection / shot │ │ │ └──────┬───────┘ │ control / write switch │ │ │ │ reads the page └───────┬─────────────────┘ │ │ ▼ │ shows the DSH UI │ │ ┌──────────────────────┐ ┌────────▼─────────────────┐ │ │ │ extension background │ │ DSH chat UI (in a frame) │ │ │ └────────┬─────────────┘ └────────┬─────────────────┘ │ └───────────┼────────────────────────────────┼───────────────────┘ │ local connection │ local connection ▼ ▼ ┌──────────────────────────────────────────────────────────┐ │ DeepSeek Harness — running on your computer │ │ └ the Companion plugin (inside DSH) │ │ · talks to the browser │ │ · gives the AI its browser abilities │ │ · asks you before any change is made │ │ · saves the captures and keeps a local log │ └──────────────────────────────────────────────────────────┘ ▲ lets Chrome start DSH when it is not running ┌─────┴──────┐ │ small │ │ launcher │ └────────────┘ 三个小部件,全部在你的机器上: 1. Chrome 扩展——唯一能够接触网页的部分。它在你安装时于你的计算机上构建,并内置你自己的设置。 2. DeepSeek Harness 内的插件——它与你的本地 DSH 运行在同一进程中,为 AI 提供浏览器能力、对更改进行把关,并保留日志。 3. 一个小型启动器——这样点击扩展时,如果 DeepSeek Harness 还没有运行,就可以为你启动它。 为什么要这样构建 最简单的版本可能是:把 DSH 放在侧边栏里,输入“向左看”,抓取页面文本。 我没有止步于此,因为那个版本只能读取: - 仅仅读取是不够的。 填写表单和翻译页面是你要做的事情。只读版本无处安放“点击这里”、“输入这个”、“等待页面加载”——也无处先征求你的同意。 - 一次性大转储并不总是你想要的。 有时是你选中的文本,有时是一张图表的图片,有时是一个由控件而非文本组成的页面。所以有三种独立的方式把它交出去,而不是一种隐藏的行为。 - 只有扩展才能访问页面。 显示在框架内的页面无法读取其他标签页或其他网站。所以扩展是手,DeepSeek Harness 是大脑。 - 更改需要许可和记录。 任何修改页面的操作都要经过确认,并且每次捕获都会写入本地日志。“AI 悄悄点击了什么东西”是不可接受的。 它安全吗? 简短版:它完全在你的电脑上运行,未经你的同意什么都不会发生。 - 没有第三方浏览器服务。 你的扩展读取页面并将其交给你自己的本地 DSH 进程。中间没有公司。 - 没有任何内容暴露到网络。 插件只在你自己的机器上监听,不与互联网通信。 - 只有你的浏览器能与它通信。 连接需要一个在你的机器上生成的密钥配对密钥,并且它会检查调用方确实是你的扩展。一个随机的网页无法访问它。 - 你的扩展身份是被固定的,所以另一个扩展无法冒充它。 - 写操作默认拒绝。 如果无法询问——或者会话被设置为从不询问——该操作会被拒绝,并且它会告诉你,说“不”的是设置,而不是某个人。 - 每一次更改都会先征求同意,并被记录在一个你可以阅读的本地日志中。 - 此仓库中没有机密:没有密钥,没有配对文件,没有构建好的扩展。构建过程会检查这一点。 获取并安装它 第 1 步——下载它(逐步点击,无需 GitHub 经验) 你不需要绿色的 Code 按钮,也不需要 git。GitHub 有一个专门为此设计的页面:Releases。以下是你要点击的内容。 1. 打开此页面—— (或者打开项目页面,点击右栏中的 Releases。) 2. 你会在顶部看到最新的条目(目前是 v3.47.3)。点击它的标题。 3. 在该页面上,滚动到最底部——最后一个部分叫做 Assets。 在它下面,点击写着 Source code (zip) 的那一项。一个 .zip 文件就会开始下载。 (除非你确定需要,否则忽略 Source code (tar.gz);忽略 Assets 上方的所有内容。) 4. 找到下载的文件——通常在下载文件夹中——然后双击它来解压。 你会得到一个名称以 dsh-web-companion 开头的文件夹。 把它移到你以后还能找到的地方(放在你的主文件夹里就行)。 (熟悉 git?git clone https://github.com/jerryxugit-2026/dsh-web-companion.git 能得到同样的东西。) 你下载的是什么(以及不是什么):只有项目自身的源代码。里面没有 node_modules,没有预构建的扩展,也没有密钥。这就是下载体积很小的原因。 我需要编译任何东西吗?——不需要 对你来说没有构建步骤,也不会有任何东西在背后偷偷下载。 你自己用复制粘贴的命令安装两个前置依赖(见下文);然后向导会检查它们,如果有任何缺失就准确告诉你缺什么,并为你构建 Chrome 扩展(只需几秒钟)。你从不需要打开编译器,也不需要 Xcode 或任何开发者工具。 安装程序刻意不下载或安装任何东西。 它过去会自行决定并拉取东西;这在实践中被证明是错误的——在一台已经从源码安装了 DeepSeek Harness 的机器上,检查只看了 PATH,没有看到它,于是就会安装第二份副本。所以现在它只报告:缺什么、装到哪里、以及要运行的确切命令。有一个例外——它为依赖项唯一会写入的东西是一个指向你已经拥有的 esbuild 的符号链接,而不是下载一个。 而且如果缺少某个依赖,它会在写入任何一个文件之前就停下。 为什么扩展没有已经构建好放在下载包里? 因为构建过程会把你的 DSH 端口和你的配对密钥烘焙进去。预构建的副本在你的机器上根本无法工作——所以向导改为在本地构建它。 第 2 步——运行安装程序 你需要 macOS、Node 22 或更新版本以及 Chrome。 有两样东西需要你先自行安装。 向导会检查它们,如果缺少某一个就告诉你确切的命令——但它不会替你安装它们: 1. DeepSeek Harness —— 插件所依附的宿主程序。 固定版本:npm 上其子包的 latest 是一个损坏的占位包。 npm install -g @deepseek-ai/dsh@0.1.5-rc.2 2. esbuild —— 只使用一次,用来构建 Chrome 扩展(约 11 MB)。 如果缺少它,向导会为你的文件夹打印出确切的命令。 npm install --prefix "/extension" esbuild 已经通过源码检出(一个没有 node_modules 的 git clone)拥有 DeepSeek Harness?那是源码,不是可用的安装——先构建它,或者别动它,按上面的方法安装已发布的包。如果你从自定义位置运行它,请把向导指向它的启动器:--dsh 。 让终端进入正确的文件夹(每个人都会卡住的地方): 1. 按 Cmd + Space,输入 Terminal,按回车——会打开一个黑色窗口。 2. 输入 cd ——字母 c、d 和一个空格——然后从 Finder 把解压后的文件夹拖进那个窗口(路径会自己打出来),按回车。 3. 现在运行: node bootstrap/install.mjs # 试运行:它只打印它将会改动什么 node bootstrap/install.mjs --apply # 真正的操作:每一步之前都会先问你 几乎每个人都会犯的那个拼写错误:文件是 bootstrap/install.mjs——中间是斜杠,因为 install.mjs 位于 bootstrap 文件夹里面。bootstrap.install.mjs(一个点)不是文件,Node 会回答 Cannot find module。让它自己打出来吧:输入 node boot,按 Tab(它会补全为 bootstrap/),输入 in,再按一次 Tab,然后回车。 绝对路径在任何目录下也有效: node /full/path/to/dsh-web-companion-3.49.1/bootstrap/install.mjs 向导在做任何事之前会问你两件事——程序安装到哪里,以及你的 DSH 数据放在哪里。两者都有合理的默认值,所以直接按两次回车: ? 本程序安装到哪个目录? [/Users/you/Downloads/dsh-web-companion-3.49.1] ← 按回车 ? DSH 数据目录(配对钥匙/凭据放这里)? [/Users/you/.dsh] ← 按回车 (这两个提示是中文的——安装程序是为这个项目写的终端程序。 它会安装到你解压的那个文件夹里——这是默认值,而且很安全。向导会把源代码留在原处,并在它们旁边添加它需要的东西(node_modules、构建好的扩展、一个启动器)。这样程序就待在一个你看得见、删得掉的文件夹里,而不是把自己散落到某个你还得去找的地方。如果你更想把它放到别处,可以传 --install-dir 或者在那个提示处输入一个路径。) 然后,只需一次: 1. 在 Chrome 中打开 chrome://extensions,开启开发者模式,点击加载已解压的扩展程序,然后选择安装程序刚刚为你构建好的 extension/dist 文件夹。 2. 点击扩展图标打开侧边栏——那就是你的智能体的窗口。 3. 重启 DeepSeek Harness(dsh web)以便它加载插件,然后检查一切: node bootstrap/doctor.mjs # 验证 DSH、配对、构建好的扩展、连通性 安装程序会问你安装到哪里,以及你的 DSH 数据放在哪里。它会检查 Node、DSH、Chrome 和端口;如果缺少 DSH 就安装它;在你的机器上构建扩展;向你的 DSH 设置中添加一行(先备份);然后验证结果。要再次移除它: node bootstrap/uninstall.mjs。 (不支持 Windows。安装程序会说明这一点并停止,而不是假装支持。) 面向工程师 设计文档、变更日志和账本位于 docs/。面向模型的工具是 browser_read、browser_tabs、browser_wait、browser_screenshot、browser_ax,以及受门控的写入操作;两个本地通道是 /ag/agent(扩展)和 /ag/client(DSH 页面)。 npm run check 运行整套门禁测试;npm run probe:all 运行真实 Chrome 探测。 DSH Browser Companion(中文) English(见上) · 中文(本节) · 安装说明 · 安装部署 一个 AI agent,就坐在你正在看的网页旁边 —— 在 Chrome 侧边栏里。它替你读页面,在你同意时也替你操作页面:填表、翻译、点击、把好几个标签页里的东西收拢到一处。 它跑在你自己的电脑上(通过 DeepSeek Harness),网页不需要交给任何第三方的浏览器服务。 你实际要做的 —— 只有 3 件事: 1. 下载并解压 —— 见下面「第一步」。 2. 在终端运行两条命令 —— 向导每改一处都会先问你,并会问你一次 DeepSeek API key。 3. 在 Chrome 里加载扩展 —— 向导会告诉你具体该选哪个文件夹。 其余全部(包括构建扩展)都由向导自己做。不需要编译器、不需要 Xcode、不需要开发者工具,只需要装好 Node 22 或更高版本。 开发背景:为什么做这个 我是 Jerry。我发现浏览器侧边栏 + AI agent 能解决一大类"上网办事"的问题 —— 那些以前只能来回复制粘贴的事: - 填表 —— 申请、注册、报税与登记类表单。 - 翻译网页 —— 保持版式与术语一致,而不是逐词硬翻。 - 长页面总结 —— 几十页的条款、很长的帖子、一篇文档。 - 跨标签页收集 —— 在三家店比一个商品、从五个页面里收地址。 我最开始用的是 Chrome + ChatGPT 的插件,确实非常好用。但我发现很多人在国内无法使用 ChatGPT,而 DeepSeek 4.1 Flash 已经非常好用、国内可以直接用。可是 DeepSeek Harness 没有这样的扩展程序。 于是我自己做了这个。 理由就这么简单:把我喜欢的那种体验,做成身边的人能用、而且跑在本地的东西。 你能让它做什么 点开侧边栏,你就有了一个看得见当前页面的 agent。 - 就着当前页面问它。 "这份合同到底让我承担了什么?""用五句话总结这一页。" 不用复制粘贴,页面已经在它眼前。 - 替你填表。 给它一个表单页,告诉它你想写什么;它读懂每个字段要什么,然后填好。动手之前会先问你。 - 翻译。 整页翻,或只翻你划中的那段 —— 术语前后一致。 - 总结长内容。 长条款、长帖、文档:只给要紧的部分,然后可以就着它追问。 - 跨标签页收集。 "把这个商品在那三家店比一下。""从这五个页面里把地址取出来。" 它会汇总成一段答案或一张表。 - 把你在看的东西交给它,三种方式。 整页、你划中的文字、或截图(图表、canvas、PDF 阅读器这类不是文字的东西)。 也有右键菜单;在对话里打「看左边」(或 "look left"),它会自己把另一个标签页抓过来。 - 让它真的动手。 打开写操作开关,它就能填、点、选、滚、跳 —— 那些能真正填完一张表、走完一个向导的动作。 这些动作每一个都先经过你确认;除非你特意关掉询问(那样它们会被当场拒绝,而不是偷偷放行)。 它抓到的内容会作为普通 Markdown 文件存在你的工作目录里,并在对话中以 @网页捕获/….md 引用 ——你可以像对待任何文件一样读它、改它、留着或删掉。 它是怎么工作的 ┌──────────────────────────── Chrome ────────────────────────────┐ │ 你正在看的网页 侧边栏 │ │ ┌──────────────┐ ┌─────────────────────────┐ │ │ │ 网页 │ │ Companion 面板 │ │ │ │ │ │ 页面 / 选区 / 截图 │ │ │ └──────┬───────┘ │ 浏览器控制 / 写操作开关 │ │ │ │ 读取页面 └───────┬─────────────────┘ │ │ ▼ │ 显示 DSH 界面 │ │ ┌──────────────────────┐ ┌────────▼─────────────────┐ │ │ │ 扩展的后台 │ │ DSH 对话界面(在框里) │ │ │ └────────┬─────────────┘ └────────┬─────────────────┘ │ └───────────┼────────────────────────────────┼───────────────────┘ │ 本机连接 │ 本机连接 ▼ ▼ ┌──────────────────────────────────────────────────────────┐ │ DeepSeek Harness —— 跑在你的电脑上 │ │ └ Companion 插件(在 DSH 进程内) │ │ · 与浏览器通信 │ │ · 给 AI 浏览器能力 │ │ · 任何改动之前先问你 │ │ · 保存抓取内容并留一份本机日志 │ └──────────────────────────────────────────────────────────┘ ▲ 让 Chrome 在 DSH 没跑时把它拉起来 ┌─────┴──────┐ │ 小启动器 │ └────────────┘ 三块小东西,全在你机器上: 1. Chrome 扩展 —— 唯一能碰网页的部分。安装时在你的电脑上现场构建,把你的设置烤进去。 2. DeepSeek Harness 里的插件 —— 和你的本机 DSH 同一个进程:给 AI 浏览器能力、给改动把关、留日志。 3. 一个小启动器 —— DSH 没在跑时,点扩展就能把它拉起来。 为什么做成这个结构 最省事的版本是:把 DSH 放进侧边栏,打一句「看左边」,把页面文字抓过来。我没有停在那里,因为那个版本只会读: - 光读不够。 填表和翻译都是你要做的事。只读的版本没有地方安放“点这里”“输入这个”“等页面加载”,也没有地方先问你一句。 - 整页一把抓不总是你要的。 有时要的是你划中的文字,有时要一张图表的截图,有时页面是由控件而不是文字拼成的。 所以是三种各自独立的交付方式,而不是一个藏在背后的行为。 - 只有扩展能碰到页面。 框里显示的页面读不到别的标签页、读不到别的网站。所以扩展当“手”,DeepSeek Harness 当“脑”。 - 改动需要许可与留痕。 任何会修改页面的动作都走一次确认,每次抓取都写进本机日志。 “AI 悄悄点了什么”是不可接受的。 它安全吗 一句话:全部跑在你的电脑上,没有你的同意就什么都不做。 - 不经过第三方浏览器服务。 扩展读到页面后交给你本机的 DSH 进程,中间没有厂商。 - 不对外暴露网络。 插件只监听你自己的机器,不与互联网通信。 - 只有你的浏览器能跟它说话。 连接需要一把在你机器上生成的配对钥匙,并校验对方确实是你那个扩展。 随便一个网页连不上它。 - 扩展身份是钉死的,别的扩展冒充不了。 - 写操作 fail-closed。 问不成、或会话被设成“从不询问”时,动作会被拒绝,并告诉你是设置拒绝的。 - 每次改动都先问、且留痕 —— 本机日志可查。 - 仓库里没有任何机密:没有钥匙、没有配对文件、没有构建产物。构建门禁会扫这些。 怎么下载、怎么安装 第一步:下载(一步步点,不需要用过 GitHub) 不要点绿色的 Code 按钮,也不需要 git。GitHub 有一个专门给这件事的页面叫 Releases。 你要点的就是下面这几下: 1. 打开这个页面 —— (或者打开项目主页,在右侧栏点 Releases。) 2. 你会看到最上面那一条(当前是 v3.47.3),点它的标题。 3. 进去后拉到页面最下面,最后一块区域叫 Assets。在它下面点 Source code (zip) 那一项,浏览器就开始下载一个 .zip。 (除非你明确知道自己要,否则不要选 Source code (tar.gz);Assets 上面那些内容也都不用管。) 4. 找到刚下载的文件(通常在下载文件夹),双击解压。你会得到一个名字以 dsh-web-companion 开头的文件夹。把它挪到你找得到的地方(比如你的主目录)。 (熟悉 git 的话,git clone https://github.com/jerryxugit-2026/dsh-web-companion.git 拿到的是同一份东西。) 你下载到的(以及没有下载到的):只有项目自己的源码。里面没有 node_modules、没有预先构建好的扩展、没有任何钥匙。所以下载包很小。 需要我自己「编译」吗?—— 不需要 没有需要你做的编译步骤,也不会有东西在你不知情时被下载。 两个前置依赖由你自己用一条可复制的命令装好(见下),向导只负责检查它们、缺什么就明确告诉你、然后替你构建 Chrome 扩展(只要几秒)。 你不用打开任何编译器,也不需要 Xcode 或任何开发者工具。 安装器是故意不下载、不安装任何东西的。 它原来会自己判断、自己去下 —— 实测下来这是错的:有台机器上 DeepSeek Harness 是用源码装的,而检查只看 PATH,没看见它,于是差点又装一份全局的。 所以现在它只报告:缺什么、装到哪、跑哪条命令。唯一的例外是:一旦发现关键依赖缺失,它会在写任何一个文件之前就停下来。依赖层唯一的例外是:本机已经有一份 esbuild 时,它建一个符号链接过去复用,而不是下载。 为什么下载包里不直接放构建好的扩展? 因为构建会把你的 DSH 端口和你的配对钥匙烤进去;预构建的副本在你机器上根本跑不起来,所以由向导在本机替你构建。 第二步:运行安装器 需要 macOS、Node 22 或更高、Chrome。 有两个东西要你自己先装好。 向导会检查它们、缺了就告诉你确切的命令 —— 但它不会替你装: bash 1. DeepSeek Harness —— 插件寄宿的那个宿主程序。 版本要钉死:它在 npm 上的 latest 子包是个跑不起来的 stub。 npm install -g @deepseek-ai/dsh@0.1.5-rc.2 2. esbuild —— 只在构建 Chrome 扩展时用一次(约 11 MB)。 缺了的话,向导会连你的路径一起把确切命令打出来。 npm install --prefix "/extension" esbuild 已经有一份源码形式的 DeepSeek Harness(git clone 下来、没有 node_modules)?那是源码,不是能跑的安装 —— 先把它装起来,或者干脆不管它、按上面装官方发布的包。如果你的 DSH 跑在自定位置,用 --dsh 点给向导。 怎么让终端进到那个文件夹(大多数人卡在这一步): 1. 按 Cmd + 空格,输入 Terminal(终端),回车 —— 会开一个黑窗口。 2. 先输入 cd 再加一个空格,然后把刚才解压出来的文件夹从访达(Finder)拖进这个窗口(路径会自己填上),按回车。 3. 然后运行: bash node bootstrap/install.mjs # dry run:只打印“打算”改哪些文件 node bootstrap/install.mjs --apply # 真装:每一步都会先问你 几乎所有人都会打错的一个地方:是 bootstrap/install.mjs —— 中间是斜杠,因为 install.mjs 在 bootstrap 文件夹里面。写成 bootstrap.install.mjs(点)就不是文件了,node 会回你 Cannot find module。省事的办法是让终端替你补全:输入 node boot,按 Tab(自动补成 bootstrap/),再输入 in,再按 Tab,回车。 用绝对路径也行(在任何目录下都能跑): bash node /你的完整路径/dsh-web-companion-3.49.1/bootstrap/install.mjs 向导在动手之前会问你两件事 —— 程序装到哪、DSH 数据目录在哪。两个都有合适的默认值,直接按两次回车就行: ? 本程序安装到哪个目录? [/Users/你/Downloads/dsh-web-companion-3.49.1] ← 回车 ? DSH 数据目录(配对钥匙/凭据放这里)? [/Users/你/.dsh] ← 回车 (它就装在你解压出来的那个文件夹里 —— 这是缺省值,而且是安全的。 向导把源码留在原地,只把需要的东西加到旁边(node_modules、构建出的扩展、拉起器)。这样程序就在一个你看得见、删得掉的文件夹里,而不是散落到某个你还得去找的地方。想装到别处,就加 --install-dir ,或者在那个提示处直接输入路径。) 然后一次性的事: 1. Chrome 里打开 chrome://extensions,开启开发者模式,点加载已解压的扩展程序,选安装器刚为你构建出来的 extension/dist 文件夹。 2. 点扩展图标打开侧边栏 —— 那就是 agent 的窗口。 3. 重启 DeepSeek Harness(dsh web)让它加载插件,然后自检: bash node bootstrap/doctor.mjs # 检查 DSH、配对、构建产物、连通性 安装器会问你装到哪、DSH 数据目录在哪;检查 Node、DSH、Chrome 与端口;DSH 缺了就替你装;在你的机器上现场构建扩展;往你的 DSH 设置里加一行(改前先备份);最后自证一遍。 想卸掉:node bootstrap/uninstall.mjs。 (不支持 Windows。安装器会明确说明并当场停下,而不是假装成功。) 给工程师 设计文档、变更记录与台账在 docs/。面向模型的工具是 browser_read、browser_tabs、 browser_wait、browser_screenshot、browser_ax,以及受闸门保护的写操作;两条本机通道是 /ag/agent(扩展侧)与 /ag/client(DSH 页面侧)。npm run check 跑全部门禁,npm run probe:all 跑真 Chrome 探针。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
jerryxugit-2026/dsh-web-companion
在 Chrome 侧边栏里读页面、填表单、翻译并汇总多标签内容
相关插件 · 浏览器
智能体插件框架deepseek-ai/deepseek-harness
DeepSeek Harnessdsh是由 DeepSeek AI 开发的开源 agent harness智能体框架。
★ 270,422已汉化DSH
71.9
多设备 Agent 看板YaoApp/yao
✨ 所有 Agent 和工作区集中管理,覆盖你的每一台设备。任务看板追踪进度,桌面端、移动端、浏览器、API…
★ 8,147已汉化DSH
71.2
可扩展侧边栏工作台omdsh-dev/DSH-better-sidebar
DSH 的双工作台:原生右侧栏 + 自绘底部面板,并把 ctx.betterSidebar 服务开放给所有插件
★ 4,761已汉化DSH
71.1
浏览器自动化桥Tencent/BrowserSkill
你的 Agent。你的浏览器。连接,就能协作。
★ 9,967已汉化DSH
71
AI 小说创作工作台EthanYoQ/AI-Novel-Writer
AI 小说作家 / AI Novel Writer
★ 1,591已汉化DSH
70.7
智能体浏览器引擎platonai/Browser4
DeepSeek Harness 集成
★ 1,175已汉化DSH
70.4
这是你的仓库?认领条目