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

浏览器侧边助手 jerryxugit-2026/dsh-web-companion

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

在 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
装得上吗
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 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
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 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 探针。
预览分享卡片长什么样 ↓jerryxugit-2026/dsh-web-companion
分享 · DeepSeek Harness Hub
jerryxugit-2026/dsh-web-companion

在 Chrome 侧边栏里读页面、填表单、翻译并汇总多标签内容

这是你的仓库?认领条目

回到顶部