DSBot · 让 DeepSeek Harness 在手机上变成真 PWA
38/100
综合分
3
GitHub 星标
137
npm 周下载
—/5
用户评分
数据截至 2026/10/26 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add zylzyqzz/dsbot该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · market
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意未发布到 npm registry,仅可从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包@zylzyqzz/dsbot(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=20.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 16:33:53
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 94 处,展示前 79 条)
- 执行外部命令来自配置/变量scripts/setup-browser.mjs:276
const result = spawnSync(command, args, { stdio: options.quiet ? 'pipe' : 'inherit', encoding: 'utf8', ...options }) - 发起网络请求scripts/setup-browser.mjs:287
const response = await fetch(url, { redirect: 'follow' }) - 写入本地文件scripts/setup-browser.mjs:290
writeFileSync(destination, buffer) - 写入本地文件scripts/setup-browser.mjs:295
mkdirSync(destination, { recursive: true }) - 读取本地文件scripts/setup-browser.mjs:315
if (!existsSync(file)) continue - 写入本地文件scripts/setup-browser.mjs:316
try { chmodSync(file, 0o755) } catch { } - 发起网络请求scripts/setup-browser.mjs:321
const response = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(20000) }) - 读取本地文件scripts/setup-browser.mjs:357
if (existsSync(binary)) { - 读取本地文件scripts/setup-browser.mjs:397
const from = existsSync(inner) ? inner : staging - 写入本地文件scripts/setup-browser.mjs:398
mkdirSync(prefix, { recursive: true }) - 删除本地文件递归scripts/setup-browser.mjs:399
rmSync(target, { recursive: true, force: true }) - 写入本地文件scripts/setup-browser.mjs:401
renameSync(from, target) - 删除本地文件递归scripts/setup-browser.mjs:404
rmSync(from, { recursive: true, force: true }) - 删除本地文件递归scripts/setup-browser.mjs:406
rmSync(staging, { recursive: true, force: true }) - 读取本地文件scripts/setup-browser.mjs:409
if (!existsSync(binary)) throw new Error(`Chrome binary missing after extract: ${binary}`) - 读取本地文件scripts/setup-browser.mjs:419
try { os = parseOsRelease(readFileSync('/etc/os-release', 'utf8')) } catch { } - 读取本地文件scripts/setup-browser.mjs:445
if (existsSync(core)) { log(` playwright-core already present: ${core}`); return dir } - 写入本地文件scripts/setup-browser.mjs:450
mkdirSync(dir, { recursive: true }) - 执行外部命令来自配置/变量scripts/setup-browser.mjs:489
const result = spawnSync(process.execPath, args, { encoding: 'utf8' }) - 执行外部命令来自配置/变量scripts/setup-browser.mjs:599
const patched = spawnSync(process.execPath, [patchScript], { encoding: 'utf8' }) - 读取本地文件scripts/verify-browser.mjs:65
if (!chrome || !existsSync(chrome)) { - 读取本地文件scripts/verify-browser.mjs:70
if (!existsSync(shim)) { - 发起网络请求scripts/xray-split-route.mjs:168
const response = await fetch(url, { redirect: 'follow' }) - 写入本地文件scripts/xray-split-route.mjs:170
writeFileSync(destination, Buffer.from(await response.arrayBuffer())) - 写入本地文件scripts/xray-split-route.mjs:174
mkdirSync(geoDir, { recursive: true }) - 读取本地文件scripts/xray-split-route.mjs:177
if (existsSync(file)) { log(` ${name} present (${(readFileSync(file).length / 1024).toFixed(0)} KB)`); continue } - 读取本地文件scripts/xray-split-route.mjs:177
if (existsSync(file)) { log(` ${name} present (${(readFileSync(file).length / 1024).toFixed(0)} KB)`); continue } - 读取本地文件scripts/xray-split-route.mjs:181
log(` ${name} -> ${file} (${(readFileSync(file).length / 1024).toFixed(0)} KB)`) - 执行外部命令固定命令scripts/xray-split-route.mjs:186
const probe = spawnSync('xray', ['-test', '-config', configFile], { encoding: 'utf8' }) - 读取本地文件scripts/xray-split-route.mjs:202
if (!existsSync(configFile)) throw new Error(`config not found: ${configFile}`) - 读取本地文件scripts/xray-split-route.mjs:203
const original = readFileSync(configFile, 'utf8') - 写入本地文件scripts/xray-split-route.mjs:220
copyFileSync(configFile, backup) - 写入本地文件scripts/xray-split-route.mjs:222
writeFileSync(configFile, JSON.stringify(config, null, 2) + '\n') - 写入本地文件scripts/xray-split-route.mjs:230
copyFileSync(backup, configFile) - 读取本地文件lib/lan-gate-auth.cjs:49
const existing = fs.readFileSync(target, 'utf8').trim() - 写入本地文件lib/lan-gate-auth.cjs:55
fs.mkdirSync(path.dirname(target), { recursive: true }) - 写入本地文件lib/lan-gate-auth.cjs:58
fs.writeFileSync(target, password + '\n', { mode: 0o600 }) - 写入本地文件lib/lan-gate-auth.cjs:59
try { fs.chmodSync(target, 0o600) } catch (err) { } - 读取本地文件lib/lan-gate-server.cjs:54
function loadDecisions() { try { var raw = JSON.parse(fs.readFileSync(stateFile(), 'utf8')); if (raw && typeof raw === 'object' && raw.decisions && ty - 写入本地文件lib/lan-gate-server.cjs:55
function saveDecisions(decisions) { try { fs.mkdirSync(dshHome(), { recursive: true }); var tmp = stateFile() + '.tmp'; fs.writeFileSync(tmp, JSON.str - 读取密钥环境变量lib/lan-gate-server.cjs:88
var VAPID = { publicKey: process.env.LAN_GATE_VAPID_PUBLICKEY || '', privateKey: process.env.LAN_GATE_VAPID_PRIVATEKEY || '' } - 读取密钥环境变量lib/lan-gate-server.cjs:120
var PASSWORD = process.env.LAN_GATE_PASSWORD || '' - 读取密钥环境变量lib/lan-gate-server.cjs:121
var PASSWORD_FILE = process.env.LAN_GATE_PASSWORD_FILE || '' - 读取本地文件lib/lan-gate-server.cjs:126
? { password: fs.readFileSync(PASSWORD_FILE, 'utf8').trim(), created: false, file: PASSWORD_FILE } - 读取本地文件lib/lan-gate-server.cjs:243
try { body = fs.readFileSync(abs) } catch (e) { return false } - 发起网络请求lib/lan-gate-server.cjs:265
var request = http.request({ - 读取本地文件lib/lan-gate-server.cjs:346
const stat = fs.statSync(file) - 读取本地文件lib/lan-gate-server.cjs:349
const text = fs.readFileSync(file, 'utf8') - 发起网络请求lib/lan-gate-server.cjs:431
var req = http.request(sub.endpoint, { method: 'POST', headers: headers }, function (res) { - 读取密钥环境变量lib/lan-gate-server.cjs:470
var DSH_TOKEN = process.env.LAN_GATE_DSH_TOKEN || '' - 发起网络请求lib/lan-gate-server.cjs:507
var upstream = http.request({ host: TARGET_HOST, port: TARGET_PORT, method: 'GET', path: '/?token=' + encodeURIComponent(DSH_TOKEN), headers: headers - 发起网络请求lib/lan-gate-server.cjs:662
var upstream = http.request({ host: TARGET_HOST, port: TARGET_PORT, method: req.method, path: req.url, headers: headers }, function (upRes) { - 读取本地文件lib/web-push.cjs:94
const raw = JSON.parse(fs.readFileSync(file, 'utf8')) - 写入本地文件lib/web-push.cjs:102
fs.mkdirSync(path.dirname(file), { recursive: true }) - 写入本地文件lib/web-push.cjs:104
fs.writeFileSync(tmp, JSON.stringify(keys, null, 2), { mode: 0o600 }) - 写入本地文件lib/web-push.cjs:105
fs.renameSync(tmp, file) - 发起网络请求pwa/inject.js:151
return fetch('/pwa/push/subscribe', { - 发起网络请求pwa/inject.js:171
return fetch('/pwa/push/subscribe', { - 发起网络请求pwa/inject.js:643
fetch('/pwa/push/send', { - 发起网络请求pwa/sw.js:91
const fetchPromise = fetch(req) - 发起网络请求pwa/sw.js:107
return fetch(req) - 发起网络请求pwa/sw.js:114
return await fetch(req) - 发起网络请求pwa/sw.js:147
return fetch('/pwa/push/subscribe', { - 发起网络请求scripts/check-upstream-contract.mjs:82
const req = http.request({ - 读取本地文件scripts/check-upstream-contract.mjs:120
const css = readFileSync(join(ROOT, 'pwa', 'app.css'), 'utf8') - 读取本地文件scripts/check-upstream-contract.mjs:210
} else if (!existsSync(SLOT_CATALOG)) { - 读取本地文件scripts/check-upstream-contract.mjs:213
const catalog = readFileSync(SLOT_CATALOG, 'utf8') - 读取本地文件scripts/check-upstream-contract.mjs:258
const text = readFileSync(join(ROOT, file), 'utf8') - 读取本地文件scripts/patch-ds-bot-image.mjs:195
if (!existsSync(file)) { - 读取本地文件scripts/patch-ds-bot-image.mjs:200
if (!existsSync(backup)) { console.error(`no backup to restore: ${backup}`); return 2 } - 写入本地文件scripts/patch-ds-bot-image.mjs:201
copyFileSync(backup, file) - 读取本地文件scripts/patch-ds-bot-image.mjs:206
const result = patcher(readFileSync(file, 'utf8')) - 写入本地文件scripts/patch-ds-bot-image.mjs:217
if (!existsSync(backup)) copyFileSync(file, backup) - 读取本地文件scripts/patch-ds-bot-image.mjs:217
if (!existsSync(backup)) copyFileSync(file, backup) - 写入本地文件scripts/patch-ds-bot-image.mjs:218
writeFileSync(file, result.source) - 执行外部命令来自配置/变量scripts/patch-ds-bot-image.mjs:220
const check = spawnSync(process.execPath, ['--check', file], { encoding: 'utf8' }) - 写入本地文件scripts/patch-ds-bot-image.mjs:222
copyFileSync(backup, file) - 读取本地文件scripts/pw-proxy-shim.mjs:57
const parsed = JSON.parse(readFileSync(file, 'utf8')) - 读取本地文件scripts/pw-proxy-shim.mjs:127
if (!existsSync(candidate)) continue
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 安全远程访问网关
- 安装到手机主屏
- 离线可用与任务推送
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
DSBot · 让 DeepSeek Harness 在手机上变成真 PWA 把 DeepSeek Harness(DSH)变成手机上的完整 PWA:安全远程访问你的自建 DSH + 一键「添加到主屏」成独立 App + 离线可用 + 触屏手势 + 任务完成通知。 基于 MIT 的 dsh-mobile-gate(安全网关基座)做差异化增强。 当前版本 0.3.0 —— 对齐 DSH 官方 0.2.x(engines.dsh: ">=0.2.0 <0.3.0")。 📍 生态定位:社区现有移动端方案都停留在「窄屏 CSS 微调」,本项目做的是移动端 PWA 一站式完整方案。 ✨ 特性 | 模块 | 说明 | | 📡 安全远程访问 | 独立子进程网关,首次访问本机审批(审批绑 IP、令牌绑浏览器,同一出口 IP 下的手机和电脑可同时在线)+ 可选访问密码 + 每 IP 限流。DSH 主服务仍只监听本机回环,/api 信任栅栏不受影响 | | 📱 真 PWA | manifest.json + service worker:手机浏览器「添加到主屏」后全屏独立窗口运行,带图标/启动屏/主题色 | | 🧩 可安装 | 主屏图标、standalone 显示、apple-touch-icon、maskable 图标 | | 🌐 离线可用 | service worker:静态壳缓存优先、API 网络优先、导航离线回退页 | | 👆 触屏手势 | 下拉刷新、边缘右滑返回、捏合缩放字体(可重置) | | 🔔 任务完成通知 | 真正的 Web Push(RFC 8291 aes128gcm 加密 + RFC 8292 VAPID 鉴权),切到别的 App 也能收到 | | 📐 触屏布局 | 44px 触摸目标、safe-area 适配、全屏弹窗、紧凑排版、代码横向滚动——桌面零影响 | | 🌐 Bot 浏览器 | 一条命令给 Bot 装上真实 Chrome(Chrome for Testing + playwright-core + 代理 shim + profile patch + 自检),截图直接出现在对话里,遇到登录/支付/验证码可人机接力。详见 docs/browser.md | | 🔒 桌面不受影响 | 所有规则都以 html[data-lan-device="phone"] 或以排除 data-lan-device="desktop" 的 @media 为根 | | 🎯 0.2 版 DOM 对齐 | 移动端 CSS 全部改用 DSH 0.2 的 slot 词表(main.conversation / conversation.view / conversation.chat.flow / conversation.input.model / conversation.composer.dock …),0.1 时代已失效的选择器全部移除 | 🏗️ 架构 手机 ──> 网关 (独立 Node 子进程 · 0.0.0.0:3088) ├─ 未批准 -> 「等待本机批准」页面(轮询 /lan-gate/admin) ├─ 已批准+令牌Cookie -> 反向代理到 DSH Web UI (本机地址:端口)(每个浏览器各领一枚令牌) │ └─ HTML 注入:manifest link + PWA bootstrap + 触屏CSS + randomUUID polyfill ├─ /pwa/* -> 直接提供 manifest / sw.js / app.css / 图标 / offline.html ├─ /pwa/push/* -> 订阅 / 发送(真实加密推送) └─ 超限 -> 429 限流页电脑 127.0.0.1:3088/lan-gate/admin -> 批准/拒绝/撤销设备、选择访问方式(手机/电脑/自动) - 网关是独立子进程,与 DSH 主进程隔离:挂掉不影响主服务,插件停止时自动终止。 - DSH 0.2 起官方直接拒绝 --host 0.0.0.0(通配地址一律报错),所以 Web UI 永远只监听一个具体的本机地址。远程访问只能靠本网关:它独占对外端口并做审批,DSH 侧的 /api 信任栅栏完好。 - 网关的目标地址不是写死的:插件从 webStartup 服务读出本次启动真实的 host/port(dsh web --port 0 / --host <addr> 都照样工作),再显式传给子进程。 🚀 快速开始(在你自己的 DSH 服务器上) 方式零:npm 一键安装(推荐,已发布) npm add @zylzyqzz/dsbot@0.3.0 dsh plugin --profile web add @zylzyqzz/dsbot 已声明 dsh.bundle manifest,安装后自动激活配置层,无需手写 patch。 包:https://www.npmjs.com/package/@zylzyqzz/dsbot 方式一:从 GitHub 本地安装 git clone https://github.com/zylzyqzz/dsbot.git cd dsbot dsh plugin --profile web add ./dsbot 源码安装需要给 pnpm 放行构建脚本(见官方 publish 文档的 allowBuilds 说明)。本包是纯 JS、无构建步骤,npm 预构建包不需要任何授权。 方式二:静态挂载 参考 cordis.patch.yml.example,把绝对路径替换进你的 profile patch。 配置(环境变量) | 变量 | 默认 | 说明 | | LAN_GATE_PORT | 3088 | 网关监听端口 | | LAN_GATE_HOST | 0.0.0.0 | 网关监听地址 | | LAN_GATE_TARGET_HOST | 127.0.0.1 | 本机 DSH Web UI 地址(插件会自动填 webStartup.host,此变量优先) | | LAN_GATE_TARGET_PORT | 3080 | 本机 DSH Web UI 端口(插件会自动填 webStartup.port,此变量优先) | | LAN_GATE_RATE_LIMIT | 120 | 每 IP 每分钟请求上限 | | LAN_GATE_PASSWORD | 空 | 访问密码。设了之后打开网关先要输密码,密码只存在服务器上,浏览器拿到的是 HMAC 派生的会话 cookie | | LAN_GATE_TRUSTED_HOSTS | 空 | 对外服务地址白名单,逗号分隔(如 203.0.113.10,app.example)。DSH 0.2 的 Host/Origin 栅栏按浏览器看到的 Host 校验,网关原样透传 Host,所以这里必须声明对外地址;不带端口的条目匹配任意端口 | | LAN_GATE_REWRITE_HOST | 空 | 设为 1 时把 Host 改写成回环地址(仅老版本 DSH 需要) | | LAN_GATE_DSH_TOKEN | 自动 | DSH 进程 launch token,插件从 ctx.connection 自动读取并交给网关 | | LAN_GATE_PUBLIC_URL | 空 | 仅用于生成 token 化 URL 时的基准地址 | | LAN_GATE_VAPID_PUBLICKEY | 自动生成 | Web Push VAPID 公钥(base64url) | | LAN_GATE_VAPID_PRIVATEKEY | 自动生成 | Web Push VAPID 私钥(base64url PKCS#8) | | LAN_GATE_VAPID_SUBJECT | mailto:dsbot@localhost | VAPID 联系方式 | VAPID 密钥对未提供时会自动生成一次并保存到 ~/.dsh/lan-gate-vapid.json(权限 600),重启不失效。 DSH 0.2 的浏览器鉴权是怎么打通的 DSH 0.2 起,浏览器要拿一个签名 session cookie 才能用 Web UI,而这个 cookie 的首次签发需要一个进程 launch token(每次重启都会变)。本插件的做法: 1. 插件在 DSH 进程内从 ctx.connection.authenticatedUrl() 读出本次启动的 launch token,交给网关子进程; 2. 网关原样透传浏览器 Host(DSH 的 Host/Origin 栅栏与 cookie 的 authority 都基于它),并在浏览器还没有 session cookie 时,用这个 token 去上游换一张 cookie,直接转交给浏览器; 3. 于是用户打开的就是一个不带 token 的普通 URL,不需要复制启动日志里的 tokenized 链接。 前提是 DSH 侧要信任对外地址:把 LAN_GATE_TRUSTED_HOSTS 设成对外的 host 或 host:port(bundle 的 connection 行会自动把它并进 --trusted-host 列表),或直接在启动参数里加 --trusted-host <对外地址>。 使用流程 1. 服务端:启动 DSH + 本插件,看到 [dsbot] listening on 0.0.0.0:3088 -> <host>:<port> (pwa=on)。 2. 手机:浏览器打开 http://<你的IP>:3088 → 出现「等待本机批准」。 3. 电脑:打开 http://127.0.0.1:3088/lan-gate/admin → 批准该设备并选「手机」。 4. 手机:下拉刷新 → 进入 DSH Web UI(已注入 PWA)。 5. 添加到主屏:浏览器菜单 → 「添加到主屏」→ 变成独立 App。 6. (可选)任务完成通知:页面左下角会出现「开启」提示,允许后即可收到。 🌐 Bot 浏览器(真实 Chrome,一条命令) ds-bot 有 12 个浏览器工具,但不自带浏览器,启动参数还是写死的、没有代理配置项。这条命令把缺的部分一次补齐: bash 在你的 DSH 服务器上 node scripts/setup-browser.mjs --proxy http://127.0.0.1:7890 装完重启 sudo systemctl restart dsh-web 它会:下载 Chrome for Testing → 装系统库(Ubuntu 24.04 的 libasound2t64 改名已处理)→ 装 playwright-core → 装代理 shim → 真开一个网页验证 → 把 - id: bot 写进 profile patch(先备份)。 装完 Bot 就多出 browser_open / browser_snapshot / browser_click / browser_screenshot / browser_handoff 等 12 个工具,其中 browser_screenshot 的截图直接进对话,不是一行工具日志。 bash node scripts/setup-browser.mjs --verify-only # 随时自检 node scripts/setup-browser.mjs --print-config # 只打印 YAML sudo node scripts/xray-split-route.mjs # 代理分流:国内直连,其余走代理 为什么需要 shim、代理怎么选、排错表 → docs/browser.md 🔔 任务完成通知是怎么判定的 DSH 0.2 在智能体工作期间会渲染 [data-chat-running]。注入脚本用 MutationObserver 盯着它:一旦从「有」变成「没有」,说明这一轮跑完了 —— 此时 1. 立刻弹一个本机 Notification(页面在前台时最直接); 2. 同时把消息发给网关的 /pwa/push/send,网关按订阅加密后推给其他设备。 0.2 版移除了旧的宿主侧 dsh-push.mjs 插件:它依赖 DSH 0.1 的 conversation/http 服务接口,在 0.2 已不存在。判定逻辑改到浏览器侧,反而更准(DSH 自己的 DOM 就是事实来源),也不再占用宿主进程。 ⚙️ 本机安装测试 bash npm test # 起 mock 上游,验证 /pwa 资产、HTML 注入、状态上报、Web Push 加解密回环 🗂️ 项目结构 | 路径 | 作用 | | lan-gate.mjs | Cordis 插件入口:解析目标地址 + spawn 网关子进程 + 生命周期管理 | | lib/lan-gate-server.cjs | 网关本体:零依赖单文件(HTTP 代理 + 审批 + 令牌 + 限流 + PWA 注入) | | lib/web-push.cjs | 真 Web Push:VAPID 密钥对 + ES256 JWT + ECDH/HKDF 派生 + AES-128-GCM 加密 | | pwa/manifest.json | PWA 安装清单 | | pwa/sw.js | service worker(离线缓存 + 推送通知 + 订阅轮换) | | pwa/inject.js | 注入页引导:注册 SW + 加载手势 + 通知订阅 + 任务完成判定 | | pwa/touch-gestures.js | 下拉刷新 / 边缘返弹 / 捏合缩放 | | pwa/app.css | 移动触屏布局(data-lan-device 前缀,桌面零影响) | | pwa/offline.html | 离线回退页 | | pwa/icons/ | SVG 源 + 192/512 PNG + maskable 图标 | | cordis.patch.yml / .example | 插件 bundle 挂载层 | | scripts/setup-browser.mjs | Bot 浏览器一键安装:Chrome + 系统库 + playwright-core + shim + 自检 + profile patch | | scripts/verify-browser.mjs | 浏览器自检(真开一个网页,输出 JSON 报告) | | scripts/pw-proxy-shim.mjs | Playwright 代理 shim:注入 --proxy-server 与去自动化标志,不改 ds-bot 源码 | | scripts/xray-split-route.mjs | xray 分流修复:国内直连 / 其余走代理,带 xray -test 与自动回滚 | | docs/browser.md | Bot 浏览器原理、配置、排错 | | test/gateway.test.cjs | 网关冒烟测试 | | test/browser-setup.test.cjs | 安装器 / shim / 分流规则的纯函数测试 | 🧑💻 开发贴士 - 隔离:网关是子进程,lan-gate.mjs 只负责 spawn + 生命周期,永不 import 它的服务代码进 DSH 进程。 - 环境变量要显式传:DSH 0.2 的 ctx.subprocess 会先洗掉子进程环境里的凭据和 DSH_ 事实,再叠加调用方给的 env。所以 LAN_GATE_、DSH_HOME、VAPID 密钥必须在 spawn 请求里显式给出。 - 移动 CSS 前缀:新规则一律挂 html[data-lan-device="phone"] 或排除 desktop 的 @media(max-width:820px),桌面必须永不受影响。 - 稳定选择器:用 [data-slot=...] / [data-*] / ARIA 而非 hash 类名,避免前端构建后失效。DSH 改 slot 名时移动端样式会静默失效 —— 升级 DSH 后记得回上游 packages/client 重新核一遍 slot 词表。 - 注入页的单引号坑:lib/lan-gate-server.cjs 里的注入脚本字符串,历史上有「双引号套双引号」bug,注意字面量转义。 - 推送必须加密:推送服务拒收明文 body,必须走 lib/web-push.cjs 的 encryptPayload + buildVapidAuthorization。 🙏 致谢 - dsh-mobile-gate(MIT)——安全网关基座。 - awesome-dsh-plugin —— 社区插件精选列表。 ⚠️ 安全须知 安装插件 = 在你的机器上运行第三方代码,权限与你本人相同。收录/发布不等于安全审查。请:只在你自己的服务器上跑、别放密钥到不熟悉的环境、定期审计 lib/lan-gate-server.cjs 的变更。远程访问务必走审批 + 令牌,别把网关端口裸奔到公网。 License MIT。网关 lib/lan-gate-server.cjs 基于 dsh-mobile-gate 扩展,保留原 MIT 版权与许可,详见 LICENSE。
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
zylzyqzz/dsbot
DSBot · 让 DeepSeek Harness 在手机上变成真 PWA
相关插件 · 市场 / 管理
插件精选列表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
这是你的仓库?认领条目