仅浏览器版 DeepSeek Harness
44.2/100
综合分
30
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/9/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:缺少 main/exports/bin 入口声明;要求 dsh 0.1.1-rc.2,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add futrime/webdsh该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · market
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 66 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(download_interrupted · 2026/9/27) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;要求 dsh 0.1.1-rc.2,而最新版为 0.2.1-rc.5;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包deepseek-web-harness(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✗dsh CLI 依赖要求 0.1.1-rc.2 · 最新 0.2.1-rc.5 不兼容
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 17:41:17
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 65 处,展示前 60 条)
- 发起网络请求public/sw.js:103
fetch(request, request.mode === 'no-cors' ? { mode: 'no-cors' } : undefined) - 发起网络请求public/sw.js:105
.catch(() => fetch(request)), - 发起网络请求public/sw.js:122
const response = await fetch(request) - 发起网络请求src/browser/engine.ts:975
async fetch(url: string, init: { method?: string, headers?: Record<string, string>, body?: string } = {}): Promise<{ - 发起网络请求src/browser/net.ts:154
const response = await fetch(url.href, { - 发起网络请求src/browser/net.ts:228
const pending = this.#fetch(url) - 发起网络请求src/browser/net.ts:238
async #fetch(url: string): Promise<string | undefined> { - 发起网络请求src/browser/task.ts:1230
const response = await machine.fetch(String(params.url), { - 发起网络请求src/browser/task.ts:1244
const response = await machine.fetch(String(params.url)) - 发起网络请求src/host/bridges.ts:349
browserMachine().fetch(url, init ?? {}), - 发起网络请求src/host/model-modalities.ts:182
const response = await fetch(url, { - 发起网络请求src/host/module-system.ts:225
const response = await fetch(href) - 发起网络请求src/net/cors-proxy.ts:373
const response = await fetch(target, { method: 'GET', credentials: 'omit', signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) }) - 发起网络请求src/net/download-links.ts:106
const response = await fetch(url.href) - 发起网络请求src/net/machine-network.ts:507
const response = await fetch(attempt, { - 发起网络请求src/pkg/registry.ts:53
const response = await fetch(`${registry}/${name.replace('/', '%2f')}`) - 发起网络请求src/pkg/registry.ts:113
const response = await fetch(url) - 发起网络请求src/plugins/manager.ts:78
const response = await fetch(`${registry}/${name.replace('/', '%2f')}`) - 发起网络请求src/plugins/manager.ts:245
const response = await fetch(resolved.tarball) - 请求外部主机api.github.comsrc/plugins/sources.ts:147
const listing = await fetch(`https://api.github.com/repos/${repository}/git/trees/${encodePath(ref)}?recursive=1`, - 发起网络请求src/plugins/sources.ts:147
const listing = await fetch(`https://api.github.com/repos/${repository}/git/trees/${encodePath(ref)}?recursive=1`, - 请求外部主机raw.githubusercontent.comsrc/plugins/sources.ts:169
const response = await fetch(`https://raw.githubusercontent.com/${repository}/${encodePath(ref)}/${encodePath(entry.path)}`, - 发起网络请求src/plugins/sources.ts:169
const response = await fetch(`https://raw.githubusercontent.com/${repository}/${encodePath(ref)}/${encodePath(entry.path)}`, - 读取本地文件src/python/container-python.ts:132
if (existsSync(marker)) return - 写入本地文件src/python/container-python.ts:134
mkdirSync(home, { recursive: true }) - 发起网络请求src/python/container-python.ts:142
response = await fetch(`${base}${name}`) - 写入本地文件src/python/container-python.ts:151
writeFileSync(staged, Buffer.from(await response.arrayBuffer())) - 写入本地文件src/python/container-python.ts:152
renameSync(staged, target) - 写入本地文件src/python/container-python.ts:154
writeFileSync(marker, `${new Date().toISOString()}\n`) - 读取本地文件src/python/container-python.ts:169
const stale = readdirSync(home).filter(name => name.startsWith('.installed-') && name !== `.installed-${PYODIDE_VERSION}`) - 删除本地文件递归src/python/container-python.ts:172
for (const name of [...stale, 'site-packages', 'wheels']) rmSync(join(home, name), { recursive: true, force: true }) - 写入本地文件src/python/container-python.ts:212
mkdirSync(sitePackages, { recursive: true }) - 写入本地文件src/python/container-python.ts:213
mkdirSync(wheels, { recursive: true }) - 读取本地文件src/python/container-python.ts:515
if (!existsSync(path)) { - 读取本地文件src/python/container-python.ts:737
if (!existsSync(file)) return { error: `Could not open requirements file: ${file}`, status: 1 } - 读取本地文件src/python/container-python.ts:738
for (const line of readFileSync(file, 'utf8').split('\n')) { - 发起网络请求src/runtime/guests.ts:139
const answer = await fetch(url, { headers: { range: 'bytes=0-99' } }) - 发起网络请求src/runtime/v86.ts:1884
const response = await fetch(image.src) - 发起网络请求src/shell/busybox.ts:426
const response = await fetch(url, { - 执行外部命令来自配置/变量src/shell/container-cli.ts:87
const result = spawnSync(name, args, { - 读取本地文件src/shell/container-cli.ts:152
return readFileSync(0, 'utf8') - 读取本地文件src/shell/container-cli.ts:251
script: readFileSync(file, 'utf8'), - 发起网络请求src/shell/git.ts:374
if (subcommand === 'fetch') await git.fetch(options as never) - 读取本地文件src/shell/node-volume.ts:89
const stats = followFinal ? fs.statSync(path) : fs.lstatSync(path) - 读取本地文件src/shell/node-volume.ts:98
return fs.existsSync(path) - 读取本地文件src/shell/node-volume.ts:104
const stats = followFinal ? fs.statSync(path) : fs.lstatSync(path) - 读取本地文件src/shell/node-volume.ts:113
return new Uint8Array(fs.readFileSync(path)) - 写入本地文件src/shell/node-volume.ts:118
fs.writeFileSync(path, data, { mode }) - 写入本地文件src/shell/node-volume.ts:123
fs.appendFileSync(path, data, { mode }) - 写入本地文件src/shell/node-volume.ts:138
fs.truncateSync(path, size) - 删除本地文件src/shell/node-volume.ts:143
fs.unlinkSync(path) - 写入本地文件src/shell/node-volume.ts:148
fs.mkdirSync(path, { mode }) - 写入本地文件src/shell/node-volume.ts:156
return fs.mkdirSync(path, { recursive: true, mode }) - 读取本地文件src/shell/node-volume.ts:161
return fs.readdirSync(path) - 读取本地文件src/shell/node-volume.ts:167
for (const entry of fs.readdirSync(path, { withFileTypes: true })) { - 删除本地文件src/shell/node-volume.ts:177
fs.rmdirSync(path) - 删除本地文件src/shell/node-volume.ts:182
fs.rmSync(path, { recursive: options.recursive ?? false, force: options.force ?? false }) - 写入本地文件src/shell/node-volume.ts:202
fs.renameSync(from, to) - 发起网络请求src/shell/tools.ts:161
const response = await fetch(url, { - 发起网络请求src/shell/tools.ts:203
const response = await fetch(url, { signal: context.signal ?? null })
扫描于 2026/11/3(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
涉及外部主机:api.github.com、raw.githubusercontent.com
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cordis-plugin-group@deepseek-ai/cordis-plugin-hmr@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/cordis-plugin-timer@deepseek-ai/cosmokit@deepseek-ai/dsh@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-default-model@deepseek-ai/dsh-agent-instructions@deepseek-ai/dsh-agent-loop给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成webdsh
webdsh — DeepSeek Harness in a browser tab, with no server and no install
DeepSeek Harness 就在浏览器标签页里 —— 真智能体,真 Node,不用跑服务器。
DeepSeek Harness(dsh)是一套智能体框架,里面一切都是插件。dsh web 会跑一个 Node 宿主,再给浏览器客户端提供服务。webdsh 就是它,只不过变成了静态文件 —— 宿主跑在页面里,智能体的命令跑在 WebContainers 里:Node 本身,就在标签页中。
- ⚡ 什么都不用跑。 不用服务器,不用安装,不用本地 Node —— 框架直接在页面里启动。
- 🌍 容器也在线。 页面自己的 CORS 策略会预加载进它启动的每个 Node 进程,所以容器里的 fetch 遇到被拒的主机会自己走代理重试 —— http://example.com 现在在那儿能通了,以前不行。
- 🖥️ 真 Node,真 Python。 npm install 和 pip install 都能用,终端和智能体共用一个容器。
- 💾 或者一整台 PC。
展开完整文档(README 剩余 1 节)
webdsh
webdsh — DeepSeek Harness in a browser tab, with no server and no install
DeepSeek Harness 就在浏览器标签页里 —— 真智能体,真 Node,不用跑服务器。
DeepSeek Harness(dsh)是一套智能体框架,里面一切都是插件。dsh web 会跑一个 Node 宿主,再给浏览器客户端提供服务。webdsh 就是它,只不过变成了静态文件 —— 宿主跑在页面里,智能体的命令跑在 WebContainers 里:Node 本身,就在标签页中。
- ⚡ 什么都不用跑。 不用服务器,不用安装,不用本地 Node —— 框架直接在页面里启动。
- 🌍 容器也在线。 页面自己的 CORS 策略会预加载进它启动的每个 Node 进程,所以容器里的 fetch 遇到被拒的主机会自己走代理重试 —— http://example.com 现在在那儿能通了,以前不行。
- 🖥️ 真 Node,真 Python。 npm install 和 pip install 都能用,终端和智能体共用一个容器。
- 💾 或者一整台 PC。 设置 → Machine 会把容器换成 v86,并提供 128 台机器 —— v86 的全部目录,从 512 字节的引导扇区游戏到 Windows 2000,其中 127 台开箱即用,什么都不用配 —— 模拟 x86,有自己的屏幕,还带着那台机器实际拥有的工具集。
- 🧭 或者一个浏览器。 第三台机器是真实网页的真实标签页,助手用三种方式驱动它们:页面结构(一棵带标签的树,每个可点击的东西都有句柄)、像素,或者页面里的 JavaScript。支持多标签页,有自己的 cookie 和按站点存储,并且会持久化 —— 每个标签页都沙箱化进不透明源,所以页面碰不到这套框架、它的存储,也碰不到你的密钥。这是浏览器自己的规则,不是这个构建做出的承诺:每条逃逸路径都会返回 SecurityError,npm run test:browser 会验证这一点。
- 🤖 而且它可以被编程。 一张有二十行的表格,每回合一个动作是错的形状,所以浏览器机器也接受程序:browser_task 运行 getByRole('button', {name: 'Save'}).click(),在具名任务空间里重试 expect、frame、弹窗、对话框、下载和上传,并在多次调用之间保留页面、变量和登录状态——还带凭据,所以一次中途被打断的表单填写,可以问它发生了什么,而不是重来一遍。模型自己的代码运行在它自己的不透明源里,不持有这个页面的任何东西:挡住被浏览站点的同一条边界,也挡住模型写的脚本。
- 🌐 PC 在线。 默认走 WISP 中继,所以客户机拿到真正的 TCP——https://、包管理器、ssh——没有中继时,页面自己就是路由器:它应答客户机的 DHCP、DNS 和 ping,并以浏览器 fetch 承载 HTTP,走的是应用其余部分用的同一套 CORS 策略。wget http://example.com 在模拟的 Buildroot 上两种方式都能用;同一个 URL 从容器里发出则返回 fetch failed。
- 👁️ 它能看。 附上一张图片,模型就能读:由浏览器自己的解码器按路由的预算做方向校正、尺寸上限和重新编码,途中剥掉源文件的 EXIF 和色彩配置档。你自己添加的模型会被问它接受什么,所以你自己网关上的视觉模型是睁着眼睛到来的,而不是被注册成纯文本。
- 🧩 真正的插件。 从 npm、tarball、GitHub 或路径安装——就在浏览器里。
- 📦 真正的 dsh。 已发布的 @deepseek-ai/ 包,未经修改:135 行里有 120 行与 dsh web 的组合方式完全一致。
- 🔒 属于你。 文件、会话和密钥都存在你浏览器的存储里。不上传任何东西。
目录
- 背景
- 安装
- 用法
- 维护者
- 贡献
- 许可证
背景
这里没有任何东西是 dsh 的分支。智能体循环、工具注册表、模型适配器和整个 Web 客户端都在安装时来自 npm;唯一的修改是一层 cordis.patch.yml——dsh 正是为这种情况记录的机制。
这个仓库添加的是底下的平台:一个同步 POSIX 文件系统,镜像到 IndexedDB(src/vfs);在它之上实现的 node:(src/node);会话可以运行的运行时——WebContainers 和一个模拟的 x86 PC(src/runtime);以及一个用沙箱 frame 搭出来的浏览器(src/browser)——一个页面内的虚拟服务器,服务 /api;每个出站请求都要经过的 CORS 策略,以及给模拟机器的那张网(src/net);还有这次构建附带的插件(packages/)。
有六行组合被替换,每一行都是因为随附的那一行命名了页面不可能拥有的东西——或者,就 shell 而言,不可能诚实描述的东西。还有四行是重新配置而非替换,其中包括决定是否这次部署能否打开一条通路。npx tsx scripts/alignment.ts 会打印出全部差异。
安装
无需安装——打开页面即可。想自己跑:
npm ci
npm run build # → dist/
node scripts/serve.mjs 4173
需要 Node 22 或更高版本。dist/ 是纯静态文件,URL 都是相对路径,所以放在域名根目录、项目子路径或本地目录都能用。
使用
打开页面,选一个工作区,开始对话。六条路由上共 42 个模型已预先注册,所以它先回答,再向你提问。
侧边栏里有三样东西:
- Files —— 工作区,智能体和终端看到的就是它。把文件拖进来;把文件、目录或勾选的内容拿出去。点击助手提到的路径,就能在这里打开它。
- Machine —— Ctrl+ 。这个会话跑在什么上:Node 容器的终端、模拟 PC 的实时屏幕、浏览器的标签栏和地址栏。点击屏幕,机器就接管你的键盘和鼠标;按 Escape 交还。浏览器的标签页就是助手正在操作的那些标签页,不是它们的第二份副本——它点什么,你就看到什么。
- Settings —— 用哪台机器、哪些模型、哪个 CORS 代理、哪些插件。
窗口宽时,两个面板停在对话旁边;窗口窄时,停在底部。它们从对话里取宽度,而不是盖住对话。
机器。 Settings → Machine 提供三种:Node 容器、浏览器,以及 128 台模拟 PC——v86 目录的全部,其中 127 台开箱即用,无需任何设置。选择在下次加载时生效,因为它决定助手拿到哪些工具:容器里是 jsh、Node 和 Python;浏览器上是 browser_navigate、browser_snapshot、browser_click、browser_screenshot、browser_eval 等;客户机上是 sh 或 dos,外加 vm_screenshot、vm_key、vm_type、vm_mouse 等,客户机的磁盘与你的工作区不共享任何东西。客户机只会拿到当前能用的工具,拿不到那些会返回空结果的工具——没有文字的桌面上不给 vm_screen,从未启用鼠标的提示符下不给 vm_mouse。唯一剩下的 Arch,需要一台主机托管它的 9p 树——从你自己的电脑上打开一台,它就留在你的浏览器里;或者把设置指向一台提供这些内容的主机。npm run v86:catalog 会打印与上游的差异;npm run v86:boot -- --bundled --as-shipped 会重新启动全部 127 台。磁盘来自 v86 自己的 copy/images 以及 src/runtime/v86-mirror.json 里列出的主机,主要是 AndyZijianZhang/webdsh-images,它的 NOTICE.json 记录了每个镜像的来源和许可。
目录装不下的,是 v86 无法执行的操作系统。OpenHarmony 就是大家常问的那个:它的每个目标平台都是 ARM、RISC-V、 Xtensa、C-SKY 或 x86-64,而 v86 只支持 32 位,所以没有镜像可提供,也就没有对应的行。docs/openharmony-on-v86.md 里有证据,npm run openharmony:check 会从上游重新读取,这样答案不会悄悄过时。
浏览器机器。 标签页,处在一个本页面无法触达的框架里。每个标签页都被沙箱隔离,不带 allow-same-origin,因此获得不透明源——浏览器本身就拒绝它访问本页面的 DOM、localStorage、IndexedDB 以及其中的密钥。这个选择牺牲了其他一切:不透明源文档不受 service worker 控制,无法跨源获取任何内容,并且从 localStorage、sessionStorage、document.cookie 和 indexedDB 都会得到 SecurityError。所有这些都在写下任何一行之前测量过。
所以页面就是浏览器的网络与存储进程,框架只是它的渲染器。页面通过这里其他一切都在用的同一套 CORS 策略获取内容,重写文档使其自包含,并通过消息通道把 cookie 和按站点存储交给 shim。三项测量塑造了它:service worker 从不控制不透明源,所以常见的同源代理技巧行不通;data: URL 不会污染 canvas,而 blob: URL 会,所以每个子资源都内联为 data:,否则 browser_screenshot 在任何带图片的页面上都会失效;window.location 是 defineProperty 唯一拒绝的全局对象,所以脚本文本用 acorn 解析,命名 location、top 和 parent 的表达式被重写为读取虚拟对象——按自身 URL 路由的站点能正常工作,而每个大型站点都附带的 frame-buster 则悄无声息地什么都不做。
两个限制,这里明确说明,模型读取的工具描述里也说明。请求上不携带任何 cookie——Cookie 是浏览器禁止页面设置的请求头,而 set-cookie 不跨源暴露——所以 cookie 在页面内部工作并在访问之间持久化,登录后的任何内容都不可达。大多数主机需要 CORS 代理,因为大多数网站不允许跨源读取;没有代理,只有发送 access-control-allow-origin 的主机才能浏览。npm run test:browser 针对本地 fixture 站点驱动这一切,包括隔离。
截图。 模型驱动一台它能看见的机器,会拍照来看它,两个屏幕工具——模拟 PC 上的 vm_screenshot、标签页上的 browser_screenshot——都像 read_image 交回文件那样直接把图片交回:一次调用,无需打开任何东西。图片不会同时写入工作区。观察启动是每隔几秒一张截图,检查页面是每次编辑再来一张,而 Files 面板被它们填满不是任何人想要的。仍然会写入文件的情况是:助手为一个值得保留的视图指定路径,或者模型完全无法被展示图片(在那里文件就是整个回答),以及 Settings → Machine → Screenshots,后者会为任何想要留档的人保留每一张截图。
你添加的模型。 Settings → Models 接受任何 OpenAI 兼容的路由:一个 base URL、一个 key,再加 Fetch models。该列表对模态的说明会连同其余内容一起读取,所以端点描述为接受图像的模型,注册后就会接受图像——网关上的 GLM-5.3-Flash 就是实测的案例。这里不做任何猜测:没有描述模态的列表,其模型就留在路由默认的位置,也就是文本;你自己声明的条目永远不会被改写。如果你的路由什么都没说,而它的模型确实能处理图片,那么在设置文档里给该模型条目写上 input: [text, image],就是这里为你补全的全部内容。页面自带的名单也以同样方式描述,来自各服务商自己的目录——OVHcloud 除外,它根本不发布模态信息,所以它的视觉模型是被问了一个关于图片的问题,然后答了出来。(那条路由随后会拒绝每个智能体回合都携带的工具定义,这是它自己的一堵墙,该由服务商来挪;这里改变的是,页面不再拒绝发送它本可以发送的图片。)
机器的网络。 模拟 PC 会得到一块以太网卡,以及网卡另一端的这个页面:它自己应答 guest 的 ARP、DHCP、DNS 和 ping,并把 guest TCP 里的 HTTP 请求转成 fetch 调用——这些调用会走上面的 CORS 策略,先直连,只有主机拒绝浏览器时才走代理。所以模拟的 Buildroot 可以 wget http://example.com;同一个 URL 从容器里发出却会答 fetch failed,因为它的请求从 StackBlitz 的 worker 发出,这个页面根本看不到,也无法自动重试。80 以外的端口也能用,而原版 v86 会重置它们。
标签页承载不了的是 TLS:从 guest 内部发出的 https:// 必须在这里终止,所以这些连接会被拒绝,而不是挂在那里;以 HTTPS 提供时,只要主机需要,这个页面就在线上把普通 http:// 当作 HTTPS 发送。它只是出站 HTTP,别无其他——没有通往 guest 内运行服务器的入站路由,没有超出页面伪造的 DHCP 和 DNS 之外的 UDP,也没有 cookie 或 CORS 隐藏的响应头,所以任何需要登录的东西都用不了。ping 和 DNS 由页面应答,而不是由所指名的主机应答。guest 会被拒绝访问这个页面自身的 origin,因为 fetch 由标签页发出,而同源请求根本不需要 CORS——那会是一条通往 harness 自己 /api 的线路。你电脑的网络没有被封锁:localhost、局域网和 v86 的 <port>.external 名称都能用。
这就是底线。默认配置了一个 WISP 中继,有了它,guest 得到的是真正的 TCP,而不是 HTTP 形状的 TCP:https:// 端到端可用,包管理器和 ssh 成为可能,DNS 也是真的。代价是,一个第三方会经手机器发出的每一个字节,包括 TLS 会话内部的字节。它只做转发——Settings → Network 里写得很清楚,点一下就能清除,清除后页面内的桥接仍然可用。如果机器启动时中继没有响应,就会为这次桥接丢弃它,而不是留下一个悄无声息、什么都不做的网络。某台 guest 是否有这张卡的驱动,是它磁盘上的事实:按机器逐一测量——目前测了三台——没有测过的机器就如实说明,而不是许诺一个网络。
容器的网络。 WebContainer 的请求从 StackBlitz 自己的 worker 发出,页面的 fetch 补丁和 public/sw.js 都看不到它们——所以应用其余部分使用的 CORS 重试永远到不了这里,shell 工具的建议是手动加代理前缀。现在改为内置:src/container/net-shim.ts 在启动时写在 shell 旁边,并通过 NODE_OPTIONS 预加载进每个 Node 进程,因此被拒绝的请求会自动经配置的代理重试一次,无论它来自哪里。实测:没有它时 fetch('http://example.com') 返回 fetch failed,有它时返回页面。
配置中继后,globalThis.dshConnect(host, port) 还能给容器一个真正的 TCP socket——模拟机器的中继,用一个设置同时服务两个运行时。它是提供出来,而不是覆盖 net.connect 装上,因为 Node 自己的 fetch 就建立在这个函数上,接管它会让进程里每个请求都挂起。在这个 socket 之上跑 TLS 在这里行不通:这个运行时的 tls 会去取普通流没有的内部结构,所以 https 仍然交给 fetch。
插件。 在输入框里用 /plugin add <package>,或者 Settings → Plugins。接受 npm 名称、tarball URL、owner/repo#ref 或路径。组合在启动时固定,所以改动会在下次重新加载时生效。
持久化。 工作区、会话和记录在重新加载后保留。window.dsh.exportFs() 下载一个 zip;window.dsh.reset() 清空全部。
值得知道:容器的 shell 是 jsh,不是 bash,也不带 git;python3 是编译成 WebAssembly 的 CPython 3.14,首次使用时获取,所以它有 pip 但没有编译器、没有子进程、没有 socket——而且要写 python3,因为 jsh 把 python 做了别名,途中会丢掉引号。
Maintainers
@futrime
Contributing
欢迎在 futrime/webdsh 提 issue 和 PR。提交遵循 Conventional Commits;先让 npx tsc --noEmit 和 npm test 通过;npm run test:vision、npm run test:v86 和 npm run test:workload 覆盖镜像流水线、模拟机器,以及智能体在每台机器上完成真实任务,npm run test:machine-network 则在不启动任何东西的情况下测试 guest HTTP 桥接的解析器和它的拒绝行为。
License
Apache-2.0 © Zijian Zhang。它组合的 @deepseek-ai/* 包由 DeepSeek AI 按自己的条款发布,它拉取的磁盘镜像仍归各自作者所有。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
futrime/webdsh
仅浏览器版 DeepSeek Harness
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 22,025未汉化DSH
73.6
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 2,049已汉化MCP
72.8
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,358已汉化DSH
71.9
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13938 个插件含 PR 收录与…
★ 350已汉化DSH
71.8
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7