手机随时访问你自己的 DSH —— 流量走你自己的服务端,不依赖任何第三方云。
31.4/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/15 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add xiaohanqing/dsh-relay该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · other
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 20 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-relay-monorepo(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/13 08:09:56
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 79 处,展示前 54 条)
- 写入本地文件packages/plugin/build-host.mjs:41
await mkdir(dirname(outputPath), { recursive: true }); - 写入本地文件packages/plugin/build-host.mjs:42
await writeFile(outputPath, bundled, 'utf8'); - 读取本地文件packages/plugin/lib/index.js:9256
accessSync(p, fsConstants.X_OK); - 读取本地文件packages/plugin/lib/index.js:9257
return existsSync(p); - 读取本地文件packages/plugin/lib/index.js:9267
if (existsSync(p) && isExecutable(p)) return { bin: p }; - 读取本地文件packages/plugin/lib/index.js:9278
if (existsSync(p) && isExecutable(p)) return { bin: p }; - 读取本地文件packages/plugin/lib/index.js:9378
if (r.ok && existsSync2(dest) && statSync(dest).size >= MIN_ARCHIVE_BYTES) return { via: "curl" }; - 读取本地文件packages/plugin/lib/index.js:9378
if (r.ok && existsSync2(dest) && statSync(dest).size >= MIN_ARCHIVE_BYTES) return { via: "curl" }; - 写入本地文件packages/plugin/lib/index.js:9398
mkdirSync(work, { recursive: true }); - 写入本地文件packages/plugin/lib/index.js:9428
mkdirSync(managedBinDir(home), { recursive: true }); - 写入本地文件packages/plugin/lib/index.js:9430
renameSync(found, bin); - 写入本地文件packages/plugin/lib/index.js:9432
copyFileSync(found, bin); - 写入本地文件packages/plugin/lib/index.js:9436
chmodSync(bin, 493); - 删除本地文件递归packages/plugin/lib/index.js:9444
rmSync(work, { recursive: true, force: true }); - 写入本地文件packages/plugin/lib/index.js:9600
mkdirSync2(dir, { recursive: true }); - 删除本地文件packages/plugin/lib/index.js:10139
rmSync2(autoMarkerPath, { force: true }); - 发起网络请求packages/plugin/lib/index.js:10197
res = await fetch(`${httpBase}/__relay/enroll`, { - 发起网络请求packages/plugin/lib/index.js:10229
const res = await fetch(`${httpBase}/__relay/enroll/poll`, { - 删除本地文件packages/plugin/lib/index.js:10860
rmSync3(settingsPath(), { force: true }); - 读取本地文件packages/plugin/src/index.js:41
const v = readFileSync(pinPath(kind), 'utf8').trim(); - 写入本地文件packages/plugin/src/index.js:49
mkdirSync(dirname(pinPath(kind)), { recursive: true }); - 写入本地文件packages/plugin/src/index.js:50
writeFileSync(pinPath(kind), value, { mode: 0o600 }); - 读取本地文件packages/plugin/src/index.js:108
try { return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version; } catch { return ''; } - 写入本地文件packages/plugin/src/service.mjs:111
mkdirSync(dirname(autoMarkerPath), { recursive: true }); - 写入本地文件packages/plugin/src/service.mjs:112
writeFileSync(autoMarkerPath, JSON.stringify({ at: Date.now() }), 'utf8'); - 删除本地文件packages/plugin/src/service.mjs:116
try { rmSync(autoMarkerPath, { force: true }); } catch { } - 发起网络请求packages/plugin/src/service.mjs:177
res = await fetch(`${httpBase}/__relay/enroll`, { - 发起网络请求packages/plugin/src/service.mjs:207
const res = await fetch(`${httpBase}/__relay/enroll/poll`, { - 读取本地文件packages/plugin/src/service.mjs:427
if (!existsSync(r.bin)) { - 读取本地文件packages/plugin/src/service.mjs:447
readFileSync(autoMarkerPath, 'utf8'); - 读取本地文件packages/plugin/src/settings.mjs:16
const raw = JSON.parse(readFileSync(settingsPath(), 'utf8')); - 写入本地文件packages/plugin/src/settings.mjs:23
mkdirSync(dirname(settingsPath()), { recursive: true }); - 写入本地文件packages/plugin/src/settings.mjs:24
writeFileSync(settingsPath(), JSON.stringify(s, null, 2), { mode: 0o600 }); - 删除本地文件packages/plugin/src/settings.mjs:168
try { rmSync(settingsPath(), { force: true }); return true; } catch { return false; } - 写入本地文件packages/plugin/src/tunnel.mjs:178
try { mkdirSync(dir, { recursive: true }); } catch (err) { - 写入本地文件packages/plugin/src/tunnel.mjs:194
try { writeFileSync(f.path, f.content, { mode: 0o600 }); } catch (err) { - 读取本地文件packages/server/src/server.mjs:22
return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version; - 读取密钥环境变量packages/server/src/server.mjs:32
token: (process.env.RELAY_TOKEN ?? '').trim(), - 读取密钥环境变量packages/server/src/server.mjs:33
tokenFile: (process.env.RELAY_TOKEN_FILE ?? '').trim(), - 读取密钥环境变量packages/server/src/server.mjs:34
adminPassword: (process.env.RELAY_ADMIN_PASSWORD ?? '').trim(), - 读取密钥环境变量packages/server/src/server.mjs:35
adminPasswordFile: (process.env.RELAY_ADMIN_PASSWORD_FILE ?? '').trim(), - 读取密钥环境变量packages/server/src/server.mjs:39
portalPassword: (process.env.RELAY_PORTAL_PASSWORD ?? '').trim(), - 读取密钥环境变量packages/server/src/server.mjs:41
tlsKey: (process.env.RELAY_TLS_KEY ?? '').trim(), - 读取本地文件packages/server/src/server.mjs:84
const raw = JSON.parse(readFileSync(cfg.clientsFile, 'utf8')); - 读取本地文件packages/server/src/server.mjs:91
try { return readFileSync(cfg.tokenFile, 'utf8').trim(); } catch { return ''; } - 读取本地文件packages/server/src/server.mjs:188
try { adminPassword = readFileSync(cfg.adminPasswordFile, 'utf8').trim(); } catch { adminPassword = ''; } - 写入本地文件packages/server/src/server.mjs:192
writeFileSync(cfg.adminPasswordFile, `${adminPassword}\n`, { mode: 0o600 }); - 读取本地文件packages/server/src/server.mjs:217
const saved = JSON.parse(readFileSync(cfg.settingsFile, 'utf8')); - 写入本地文件packages/server/src/server.mjs:226
writeFileSync(`${cfg.settingsFile}.tmp`, `${JSON.stringify(settings, null, 2)}\n`, { mode: 0o600 }); - 写入本地文件packages/server/src/server.mjs:227
renameSync(`${cfg.settingsFile}.tmp`, cfg.settingsFile); - 写入本地文件packages/server/src/server.mjs:365
writeFileSync(`${cfg.clientsFile}.tmp`, `${body}\n`, { mode: 0o600 }); - 写入本地文件packages/server/src/server.mjs:366
renameSync(`${cfg.clientsFile}.tmp`, cfg.clientsFile); - 读取本地文件packages/server/src/server.mjs:389
const ADMIN_HTML = readFileSync(new URL('./admin.html', import.meta.url), 'utf8'); - 写入本地文件packages/server/src/server.mjs:618
try { writeFileSync(cfg.adminPasswordFile, `${adminPassword}\n`, { mode: 0o600 }); persisted = true; } catch { }
扫描于 2026/11/3(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 手机访问自建DSH
- 流量走自有服务端
- 不依赖第三方云
README
由 DeepSeek 最新模型翻译生成dsh-relay
在手机上随时随地访问你自己的 DeepSeek Harness(DSH)。跑 DSH 的电脑始终主动外连 一个由你掌控的入口——自建中继服务端,或你已经在用的 frp 服务。
电脑不开任何入站端口;断网、重启、换网络都自动恢复;中间人是你自己,而不是一家隧道云。
工作原理
手机 ──HTTPS──▶ 公网入口 你的电脑(DSH)(frps 或 │ ▲ 隧道,你的中继服务端) │ ▼ 插件主动外连 ────▶ dsh-relay 插件本地反向代理 :3082 └─▶ DSH web(127.0.0.1:3080)
插件侧把 Host/Origin 等请求头改写为回环形态,让 DSH 的回环信任栅栏看到的是一个本机客户端;同时处理浏览器会话握手、跨 NAT 保活 WebSocket,并在所有公网路径上强制访问密码。手机看到的界面与电脑完全一致、实时同步。
展开完整文档(README 剩余 1 节)
dsh-relay 在手机上随时随地访问你自己的 DeepSeek Harness(DSH)。跑 DSH 的电脑始终主动外连 一个由你掌控的入口——自建中继服务端,或你已经在用的 frp 服务。 电脑不开任何入站端口;断网、重启、换网络都自动恢复;中间人是你自己,而不是一家隧道云。 工作原理 手机 ──HTTPS──▶ 公网入口 你的电脑(DSH)(frps 或 │ ▲ 隧道,你的中继服务端) │ ▼ 插件主动外连 ────▶ dsh-relay 插件本地反向代理 :3082 └─▶ DSH web(127.0.0.1:3080) 插件侧把 Host/Origin 等请求头改写为回环形态,让 DSH 的回环信任栅栏看到的是一个本机客户端;同时处理浏览器会话握手、跨 NAT 保活 WebSocket,并在所有公网路径上强制访问密码。手机看到的界面与电脑完全一致、实时同步。 功能 - 两种公网入口,随你选 - frp 隧道——复用你(或朋友)已有的 frp 服务:只填 frps 地址和 token 两个必填项,一键「验证连接」真实登录一次 frps 确认可用后再启动;远程端口可留空(frps 随机分配,插件自动发现并生成二维码) - 自建中继服务端——一个 Docker 容器;多租户(多台电脑、密钥互相隔离)、网页管理台、申请审批接入、密钥自动轮换 - 仅局域网——同一 WiFi 扫码直连,完全不走隧道 - 全链路自愈——有界指数退避重连(最坏路径回归测试盯守 RSS 与重连速率),服务端重启、电脑重启、网络切换后自动恢复 - 纵深安全——见安全 两种接入方式对比 | | 外接隧道 | 自建中继服务端 | | 要部署服务端吗 | 不用 | 要(一个 Docker 容器) | | 中间人是谁 | 隧道入口的运营方:你的 frps(=你),或隧道服务商 | 你自己 | | 一个入口带多台电脑 | 取决于隧道工具 | 支持——多租户,密钥互相隔离 | | 管理台 / 审批 / 密钥轮换 | — | 支持 | | 适合谁 | 一台电脑、零额外部署 | 多台电脑、需要统一管理 | 快速开始 前提:一台跑着 DSH web 的电脑;开发参与需要 Node.js ≥ 22 与 pnpm。 1. 安装插件(在跑 DSH 的电脑上)——一条命令,直接从 GitHub 装: dsh plugin --profile web add 'github:xiaohanqing/dsh-relay#path:packages/plugin' 重启 dsh web,设置页出现「DSH Relay」 以后升级:重跑同一条命令(或 dsh plugin --profile web update dsh-relay)。 2a. 复用已有的 frp 服务(大多数人的路径): 在插件设置里把接入方式切到「frp 隧道」,只填两个必填项: - frps 服务器地址——如 frps.example.com:7000(端口可省略,默认 7000) - 鉴权 token 点「验证连接」会真实连接一次 frps 并测试登录,通过后「启动隧道」即可。 远程端口选填(留空 = frps 随机分配,插件自动发现实际端口并生成二维码)。 手机打开展示的公网地址(或扫码),输入访问密码即可。 2b. 或者自建中继服务端: cd packages/server docker compose up -d --build docker compose logs relay | grep "admin password" # 管理台初始密码 浏览器打开 https://<服务端>:8443/__relay/admin,然后在插件设置里点「一键接入」,回管理台批准即可。详细步骤见 docs/deploy-nas.md。 安全 - 三层门槛:服务端按客户端隔离的密钥串、所有公网路径强制的 8 位访问密码(带限速锁定)、独立的管理台密码 - 密钥自动轮换:中继密钥默认 30 天轮换,10 分钟宽限期;轮换通过已认证通道推送 - 不落盘:中继服务端只做字节搬运,不保存任何业务数据 - 明文链路警示:frp 纯端口映射的手机到 frps 一段是明文,访问密码经由该链路传输。 建议 frps 前加一层 HTTPS 反代;取舍详见 docs/tunnel.md 文档 - 外接隧道指南——支持的每种工具、要填什么、安全提示 - 中继服务端部署——TLS 证书、DDNS、端口转发、nginx 反代 - 协议规格——隧道协议 v1.1(自建服务端模式) - 部署验收清单——部署后逐项自检 - 分包说明:插件 · 服务端 许可证 MIT
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
xiaohanqing/dsh-relay
手机随时访问你自己的 DSH —— 流量走你自己的服务端,不依赖任何第三方云。
相关插件 · 其他
模型破甲清洗HunYiSage/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,993已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,269已汉化DSH
70.8
这是你的仓库?认领条目