一个随机域名 + 一个随机口令,把本机的服务按需对外开放。
35.6/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/25 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
相关生态插件(非 dsh 原生,按其对应运行时安装)
git clone https://github.com/dingminhua/dsh-service-portal.git状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
生态应用
桌面端 / Web 外壳,不以 dsh plugin add 安装
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 10 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装engines.node 要求 >=24,不满足 Node 22.19.0
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-service-portal @ 1.0.9
✗Node 引擎要求 >=24 · 基线 Node 22.19 不满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 16:36:02
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 38 处,展示前 23 条)
- 读取本地文件lib/index.js:64
return JSON.parse(readFileSync(manifest, "utf8")).version || "0.0.0"; - 读取本地文件lib/index.js:86
const info = statSync(self); - 读取本地文件lib/index.js:176
if (configured && existsSync(configured)) return configured; - 读取本地文件lib/index.js:179
if (existsSync(venv)) return venv; - 读取本地文件lib/index.js:182
if (existsSync(runtime)) return runtime; - 读取本地文件lib/index.js:203
if (configured && existsSync(configured)) candidates.push(configured); - 写入本地文件lib/index.js:212
mkdirSync(dir, { recursive: true }); - 执行外部命令来自配置/变量lib/index.js:218
const result = spawnSync(candidate, ["-X", "utf8", "-c", - 删除本地文件lib/index.js:225
try { rmSync(probeFile, { force: true }); } catch { } - 发起网络请求lib/index.js:287
req = http.request({ - 执行外部命令来自配置/变量lib/index.js:732
child = spawn(argv[0], argv.slice(1), { - 写入本地文件lib/index.js:782
mkdirSync(dir, { recursive: true }); - 写入本地文件lib/index.js:784
writeFileSync(tempFile, password + "\n", { encoding: "utf8", mode: 0o600 }); - 发起网络请求lib/index.js:806
const res = await fetch(endpoint, { - 发起网络请求lib/index.js:831
const res = await fetch(`http://127.0.0.1:${port}/_api/tunnel/stop`, - 发起网络请求lib/index.js:853
const res = await fetch(endpoint, { - 发起网络请求lib/index.js:882
const res = await fetch(endpoint, { - 删除本地文件lib/index.js:1021
try { rmSync(pass.tempFile, { force: true }); } catch { } - 写入本地文件lib/index.js:1036
mkdirSync(dirname(logFile), { recursive: true }); - 执行外部命令来自配置/变量lib/index.js:1038
child = spawn(argv[0], argv.slice(1), { - 发起网络请求lib/index.js:1116
req = http.request({ - 写入剪贴板lib/client.js:703
navigator.clipboard.writeText(text).then(done, () => fallbackCopy(text) && done()); - 发起网络请求lib/client.js:787
const res = await fetch(API + path, {
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
6 张 · 点击查看大图把任意本地服务暴露到随机 cloudflared 域名,只需一个密码——局域网用二维码,公网用二维码加密码,按节点路由在 DSH 设置面板里管理。
README
由 DeepSeek 最新模型翻译生成dsh-service-portal
一个随机域名 + 一个随机口令,把本机的服务按需对外开放。
English ·安装 ·工作原理 ·节点管理 ·更新日志 ·问题反馈
dsh-service-portal(对外服务门户) 是一个 DeepSeek Harness (DSH) 插件。
它把任意本机服务按需暴露到外网:cloudflared 建一个随机域名,一道口令登录 (只需口令,没有账号)守住入口,设置页一键开关并显示连接与口令。
它最初长在 ld-rl-poker 里(为了把训练配置台/模型查看台放到外网),但门户本身与德州扑克无关—— 任何本地服务都能用同一套「一个域名 + 一个口令 + 按需挂载」。
功能特性
- 装好即用,不用先点开关 —— 插件加载后门户自动起来(只开内网,不会自动把本机服务挂到公网)。打开设置页就能看到内网地址和二维码,扫一下就进。
外网暴露必须由你显式点「开启公网访问」。
- 记得你上次的选择 —— 点过「停止门户」,下次打开 DSH 就不再自动启动它;点过「启动」,下次打开照旧自动启动(仍然只开内网,公网永远是手动开)。
也就是"默认开着,之后跟着你走":它写回插件配置的 autoStart,可以在配置里手改,与点按钮等效。设置页会明说当前是哪一种,不让你猜。
展开完整文档(README 剩余 1 节)
dsh-service-portal
一个随机域名 + 一个随机口令,把本机的服务按需对外开放。
English ·安装 ·工作原理 ·节点管理 ·更新日志 ·问题反馈
dsh-service-portal(对外服务门户) 是一个 DeepSeek Harness (DSH) 插件。
它把任意本机服务按需暴露到外网:cloudflared 建一个随机域名,一道口令登录 (只需口令,没有账号)守住入口,设置页一键开关并显示连接与口令。
它最初长在 ld-rl-poker 里(为了把训练配置台/模型查看台放到外网),但门户本身与德州扑克无关—— 任何本地服务都能用同一套「一个域名 + 一个口令 + 按需挂载」。
功能特性
- 装好即用,不用先点开关 —— 插件加载后门户自动起来(只开内网,不会自动把本机服务挂到公网)。打开设置页就能看到内网地址和二维码,扫一下就进。
外网暴露必须由你显式点「开启公网访问」。
- 记得你上次的选择 —— 点过「停止门户」,下次打开 DSH 就不再自动启动它;点过「启动」,下次打开照旧自动启动(仍然只开内网,公网永远是手动开)。
也就是"默认开着,之后跟着你走":它写回插件配置的 autoStart,可以在配置里手改,与点按钮等效。设置页会明说当前是哪一种,不让你猜。
- 设置页就是 dsh-pocket 那个形态 —— 局域网、公网两个区块,各自带开关 + 二维码 + 地址 + 访问密码行 + 高级(手动选地址)。
- 节点管理就在插件设置里(插件页 → 该插件 → 配置)—— 添加节点(名称 / 对外路径 / 本机端口 / 备注)、 开放 / 屏蔽(路由闸门)、编辑、删除,以及每个节点的两个访问入口:「内网访问」(走局域网地址)与「公网访问」(走隧道地址)。
两者各自按自己的开关判断能否点击 —— 关掉局域网后内网按钮变灰、公网照常可用;没开公网时公网按钮变灰,并写明"先点「开启公网访问」"。
不用改配置文件、不用重启。
- 只管路由,不管启停 —— 「屏蔽」只表示门户不再把这条路由转过去, 不会停止上游程序;上游由你自己的方式启动(插件不猜它的启动命令)。
- 自带门户网页(只读) —— 扫码打开的是测试页 + 引导页:列出已发布的节点,每个节点有「访问」(打开它)和「测试」(就地探一次通不通并把结论写在卡片里,不用跳走):
- 「通了:HTTP 200」= 门户转发成功且上游有响应;
- 「不通:HTTP 502 … 请用它自己的方式启动那个程序」= 上游没起;
- 「上游有响应,但它自己返回了 HTTP 502」= 上游在线、是它内部出错。
最后这条必须和上一条分开说 —— 状态码一样,把上游自己的错报成「没启动」会让人去启动一个本来就在跑的程序。
没有任何设置功能 —— 要改配置请到 DSH 插件页里该插件自己的配置里。
- 内网 + 外网两种访问 —— 同一张页面,两条入口:
- 内网(http://192.168.x.x:8899)默认免密,同一 WiFi 下手机/平板直接打开。
可开「局域网访问密码」,也可以直接关掉整个局域网访问;关掉后内网请求直接 403(一张说明页,不是登录页 —— 手机那边不是决策者,看到的是「请到电脑上开启」),公网与本机不受影响;
- 外网(https://<随机>.trycloudflare.com)必须口令,人在外面也能用。
每次开启要先用勾选式免责声明确认一次(服务端也校验,防绕过前端直接打接口)。
- 两个开关改完立即生效,不用重启门户(重启会换掉隧道域名,代价太大)。
- 本机访问永远免密 —— 否则你在电脑上连自己的门户都进不去。
- 手机扫码即用 —— 设置页与门户页都显示二维码(内联 SVG,不依赖外部请求),手机扫一下就到;页面按窄屏自适应。
- 只需口令,不要账号 —— 自定义登录页(只有口令输入框),不弹浏览器原生「账号 + 密码」框;口令可每次随机或你自己设固定值。
- 登录限速 —— 单 IP 连续失败 5 次锁 60 秒,另有全局阈值防分布式撞码。
- 服务状态一目了然 —— 每个服务显示 已启用/未启用/存活探测失败,未启用的不可点;健康检查是真探测(TCP 连上游端口),不是标记位。
- 控制检验(本仓库硬纪律)—— 26 个 Python 控制检验脚本 + 8 个端到端脚本 + 83 项 Node 检验;依赖缺失必须「跳过并返回 2」,不得静默通过。
界面
两张图都是真页面渲染出来的截图(无头 Chrome 实拍,不是设计稿),且全部用演示数据: 192.168.71.10 是占位内网地址、warm-dawn-1234.trycloudflare.com 是占位域名、口令是示例串。README 里不放任何真实地址或口令。
设置页:内网 / 公网两条入口并排,各自判断开关;扫码即开,节点增删在此
门户页是扫码打开的那个页面——只读的测试页与入口页:点「访问」直接打开节点,点「测试」就地探一次通不通。它刻意做得极简:没有设置功能,设置只在上面那个页面里。
门户页:节点列表 —「访问」打开,「测试」就地探通不通
窄屏与深色
门户页是给手机扫进来的,所以窄屏与深色是主场景,不是附属:
| 窄屏(浅色) | 窄屏(深色) | 宽屏 |
工作原理
┌─────────── 门户进程(一个端口,默认 8899)───────────┐ 内网 http://192.168.x.x:8899 ──┤ │ ├──▶ 按 Host 分类:loopback / lan / public │ 外网 https://<随机>.trycloudflare.com ─┘ │ │ ├─ / → 门户页(只读:测试 + 引导) ├─ /_auth/login → 口令登录(外网) ├─ /_api/* → 门户 API(受 DSH 插件设置调用) └─ /<路由路径>/… → 转发到对应服务
认证强度按来源区分(照搬 dsh-pocket 的模型,见致谢):
| Host 分类 | 判定 | 门户页 / 服务 |
| loopback | localhost、127.x、::1 | 免密 |
| lan | RFC1918 私网、CGNAT(100.64/10)、.local、单标签名 | 免密 |
| public | 其余一切(含你自建域名) | 必须口令 |
兜底方向是安全的一侧:认不出的 Host 一律当公网。反着写(只把 .trycloudflare.com 当外网、其余当内网)会让人挂个自己的域名就绕过认证——dsh-pocket 踩过这个坑(它的 issue #66),本项目直接按 fail-closed 实现。
安装
方式一:npm(发布后)
$dsh = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"
& $dsh plugin --profile desktop add dsh-service-portal
方式二:link 本地插件(开发中)
$dsh = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"
& $dsh plugin --profile desktop add "link:D:/DshProject/dsh-service-portal"
装完需要重启 DSH(插件在宿主进程里加载)。
若旧 profile 仍装有已删除的子插件 dsh-poker-viewer,先移除再装根插件: & $dsh plugin --profile desktop remove dsh-poker-viewer。
⚠ 装完还要「启用」这一步(最常见的"装完没反应")
dsh plugin add 实质是 pnpm 的包装:它只把包装进 profile 的 dependencies。
真正让宿主加载一个 bundle 的,是 profile 清单里的 dsh.profile.bundles 名单 (~/.dsh/profiles/<profile>/package.json)。只在 dependencies 里、不在 bundles 里 = 宿主根本不加载这个插件:设置页没有这一项、/service-portal/api/ 全部 404,而且不报任何错。
所以装完还要在宿主里把这条 bundle 打开,任选一种:
- 应用内:「插件」面板里找到 dsh-service-portal,把开关打开(由应用内的插件管理器写入 dsh.profile.bundles,并立刻生效,无需重启);
- 命令行:dsh plugin 只转发 pnpm,不能改 bundles 名单,所以命令行没有等价操作;要改就编辑 ~/.dsh/profiles/<profile>/package.json 的 dsh.profile.bundles (加上 "dsh-service-portal")后重启宿主。
宿主升级后请先查这一条:新版本宿主重建 profile 清单时有可能把这条从 bundles 里漏掉,表现就是"升级前好好的,升级后插件页空白、接口 404"。症状与处理见已知限制 里的「宿主升级后插件被静默跳过」。
兼容的 DSH 版本
从 1.0.1 起,本插件在 package.json 里声明了新版 DSH 的版本门禁字段:
| 字段 | 值 | 作用 |
| engines.node | >=24 | 对齐 DSH 0.2.1-rc.1 自带运行时(Node 24.21.0) |
| engines.dsh | >=0.2.1-rc.1 <0.3.0-0 | DSH 版本门禁:声明了它,宿主就只按它判定,忽略 @deepseek-ai/dsh 的 peer 区间 |
| dsh.manifestVersion | 1 | 插件清单格式标识(前瞻声明,当前宿主不强制) |
为什么必须写 engines.dsh:旧写法只声明 @deepseek-ai/cordis / schemastery 的 peer 区间, 对 DSH 自身版本没有任何约束 —— 装到不兼容的宿主上会在启动期失败,而发版时毫无提示。
新版宿主(0.2.1-rc.1 起)的判定顺序是:有 engines.dsh 就只看它;没有才逐个检查 @deepseek-ai/dsh / @deepseek-ai/dsh- 的 peer。两条都不写 = 任何版本都放行。
升级 DSH 大版本时,先跑 npm run test:manifest。它会调用宿主自己的
evaluatePluginCompatibility() 判定本插件,同时用反例确认判定逻辑确实生效;
找不到宿主运行时则明确 skip(DSH_APP_BOOT=<dsh 根目录> 可显式指定),不会假装通过。
已验证的宿主版本:DSH 0.2.1-rc.3(正式版 DeepSeek Harness.app)。
0.2.1-rc.2 → rc.3 的改动集中在导航/面板入口自定义(新增 ui-entry-customization 与三个 .actions 槽位)、埋点事件、以及 DeepSeek API Key 的 /models 列表路由, 没有触碰本插件用到的任何接口:plugins.bundle.config / plugins.row.config
两个槽位的注册契约、webServer.register({kind:'prefix'})、Config(schemastery)
投影、dsh.client 清单字段,全部未变。
用宿主自己的运行时跑门禁检验(rc.3 实测 10/10 通过)。
注意:宿主装在 app.asar 里,普通 node 读不到 —— 用宿主自带的 Electron 当 node 用。
APP="/Applications/DeepSeek Harness.app/Contents/Resources"
DSH_APP_BOOT="$APP/app.asar/dsh/node_modules/@deepseek-ai/dsh-app-boot" \
ELECTRON_RUN_AS_NODE=1 "$APP/../MacOS/DeepSeek Harness" --test test/manifest.test.mjs
配置(DSH 设置页)
| 字段 | 默认 | 说明 |
| infraPath | 本仓库根 | 基础设施仓库:含 tools/ 与 runs/(隧道/代理/凭据) |
| repoPath | 空 | 宿主仓库根:被开放的服务本体在这里。必须自己填 |
| pythonPath | 空 | 拉服务用的解释器;留空则自动选 .venv313 → .runtime → PATH |
| cloudflaredPath | 空 | cloudflared 可执行文件路径;留空则自动探测(~/.dsh/dsh-pocket/bin → ~/.dsh/bin → ~/.local/bin → /usr/local/bin → /opt/homebrew/bin → PATH)|
| passwordMode | random | 口令形态:random 每次开启自动生成 / fixed 用固定口令 |
| fixedPassword | 空 | passwordMode = fixed 时使用,至少 12 字符(保存时即校验) |
| portalPort | 8899 | 门户端口。只接受 1–65535 的整数,非法值回落默认 |
| autoStart | true | 打开 DSH 时自动启动门户(只开内网,不建隧道)。跟随你的启停动作:点「停止门户」会被写成 false,点「启动」写回 true |
| lanAuthEnabled | false | 内网访问是否要口令(false = 免密) |
| webEnabled | true | 是否挂载 /service-portal/api 路由 |
所有字段都是 .volatile():改设置即时生效,不需要重挂插件。
「局域网访问密码」「局域网访问」两个开关走门户的 /_api/settings, 改完立即生效且立即落盘,不受「运行中不可改」限制。
口令形态是安全相关设置:口令模式与固定口令在门户运行中不可改(先「关闭」再改),避免出现「界面显示的口令」与「实际生效的口令」不一致。
为什么 autoStart 默认开、但只开内网
对齐 dsh-pocket 的「装好即用」:装了插件打开设置页就该看到内网地址和二维码,而不是一片空白等着你点按钮。但外网暴露是另一回事 —— 那是把本机服务放到公网上,必须由你显式决定(点「开启公网访问」),不能因为装了插件就自动发生。
用户停过之后不该再自己起来。 所以 autoStart 不是"厂家决定的一次性设置",而是 跟随你的启停动作:点「停止门户」→ 写 false(下次打开 DSH 不自动起);点「启动」→ 写回 true。两条纪律:
- 只在动作真的成功时写:停不掉(门户僵死、端口还被占着)时不改偏好 —— 否则会出现 "界面说已停止、下次开机它又自己起来"这种最难查的错位;启动失败同理,记成"要自动起" 只会让下次开机再白试一遍。
- 只认启停动作:自动启动本身、开/关公网、崩溃退出都不碰它。否则自动启动会把用户 "停掉"的决定又改回来,形成"永远关不掉"的循环。
平台重启后才生效 —— 它是"下次打开 DSH 时要不要自动起"的偏好,不影响当前这次运行。
口令的两种形态
| | random(默认) | fixed |
| 谁生成 | 门户每次开启自动生成 16 字符随机口令 | 你自己设定(≥12 字符) |
| 何时变 | 每次点「开启」都换 | 只有你改它才变 |
| 泄漏后果 | 只在本次开启期间有效,重启即作废 | 长期有效,直到你改口令 |
| 适用 | 分享给临时协作者、不想记口令 | 常用设备,省去每次复制口令 |
| 安全代价 | 需重新分发口令 | 口令一旦外泄就是长期敞口 |
固定口令不会进入进程命令行(那会让本机任意进程通过 Win32_Process 读到它):插件把它写进 runs/ 下的临时文件并以 --password-file 传路径,启动器读完即删。
HTTP 接口
挂在宿主 webServer 上(prefix 路由,路径 /service-portal/api):
| 方法 | 路径 | 作用 |
| GET | /service-portal/api/status | 当前状态:是否运行、模式、地址、口令、口令形态、端点列表 |
| GET | /service-portal/api | 等同 /status |
| GET | /service-portal/api/settings | 口令形态设置(模式 + 固定口令 + 最小长度) |
| POST | /service-portal/api/settings | 保存口令形态(body:{"passwordMode":"fixed","fixedPassword":"..."}) |
| POST | /service-portal/api/start | 启动(body 可选 {"singleDomain": true},默认单域名+路径) |
| POST | /service-portal/api/stop | 停止 |
未知子路径返回 404(不拿 status 兜底——拼错的路径不许「看起来能用」)。
在插件设置里管理节点
在 DSH 里打开 设置 → 插件 → 找到 dsh-service-portal → 配置,就在那里管理要对外发布的路由(注册在插件页的 plugins.bundle.config / plugins.row.config 槽位,与 workbuddy 同一形态;不会占用宿主设置的左侧导航)。
每个节点一条卡片:
| 操作 | 说明 |
| 添加节点 | 填 名称 / 对外路径(如 /ollama)/ 本机端口(如 127.0.0.1:11434)/ 备注 |
| 访问 | 点了直接在新标签打开该路由(/<对外路径>/)。屏蔽时置灰不可点 |
| 开放 · 屏蔽 | 路由闸门:开放 = 门户把请求转过去;屏蔽 = 不转(该路由立即 404)。不启停上游程序 |
| 编辑 / 删除 | 改对外路径、上游、备注;删除只从注册表移除 |
| 状态 | 绿=开放中,灰=已屏蔽;上游没响应时另给一行提示(不影响路由开放与否) |
「屏蔽」不是"停服务" —— 上游程序照跑不误。本插件不负责启动/停止上游(那需要知道它的启动命令、工作目录、就绪判定,是一整套配置,且容易做出 "以为在管、其实没管住"的假象)。上游请用它自己的方式启动。
改动立即落盘(runs/portal_registry.json)并生效,不用重启门户、不用重建隧道。
门户页面是只读的(扫码后的落地页):列出已发布的节点,每个节点两个动作 ——「访问」(打开它)与「测试」(就地探一次,把通/不通与原因写在卡片里)。
那是测试页 + 引导页,没有任何设置功能。
页首左侧是插件 logo(与设置页同一张图),右侧是「鼓励一下 ★」(去 GitHub 点星);窄屏自动换行,按钮拉成全宽。
页面上不再列访问地址:进门户本来就是扫码/点链接来的,地址就在浏览器地址栏里;要复制地址或看二维码,到插件设置页。
「测试」的三种结论(真机用 Chrome 点过,三种都对且各不相同):
| 上游状态 | 页面结论 |
| 活着 | 通了:HTTP 200 —— 门户转发成功且上游有响应 |
| 没启动 | 不通:HTTP 502 —— 门户转发过去了但上游没响应,请用它自己的方式启动 |
| 在线但自己 502 | 上游有响应,但它自己返回了 HTTP 502 —— 上游在线,请看它自己的日志 |
最后两条状态码都是 502,只能靠正文区分(门户自己发的那条固定含 Bad Gateway 与 上游)。分开说是必须的:把上游自己的错报成「没启动」,用户会去启动一个本来就在跑的程序。契约由 tools/check_portal_probe.py 钉住。
节点字段:
| 字段 | 必填 | 说明 |
| key | ✓ | 唯一标识,[a-zA-Z0-9_-]{1,32},不能与保留字冲突。从对外路径自动派生(/rltrain → rltrain),界面不必填 |
| name | ✓ | 显示名(≤64 字符,页面会 HTML 转义) |
| mount | ✓ | 对外路径,以 / 开头;不能与其他开放项重复、不能占用 /_auth /_api |
| target | ✓ | 本机上游 host:port |
| enabled | | 路由是否开放(开放 / 屏蔽) |
| script / cwd / autoStart | | 已废弃:老数据仍能读,但插件不再拿它们去启动任何进程 |
命令行也能直接改(等同设置页操作): POST /_api/services、POST /_api/services/<key>/open|block|delete|update (enable / disable 作为老别名仍可用)。
工具(也可脱离插件直接用)
| 文件 | 作用 |
| tools/portal_run.py | 门户启动器:起门户页 + 内网/外网双入口(不管上游进程) |
| tools/portal_proxy.py | 按 Host 分类认证的反向代理(内网免密 / 外网口令) |
| tools/portal_console.py | 门户页(只读:测试 + 引导)+ 二维码(纯 Python,无依赖) |
| tools/portal_registry.py | 服务注册表(运行时增删改查 + 原子落盘) |
| tools/portal_login.py | 口令登录 + 签名会话 cookie |
| tools/start_public_gui.py | 旧版启动器(静态清单 + 单/多域名两种形态,仍可用) |
| tools/tunnel_auth.py | 旧版认证代理(口令登录 + 多挂载点路径分流) |
python -X utf8 tools/portal_run.py # 起门户(内网 + 外网隧道)
python -X utf8 tools/portal_run.py --lan-only # 只开内网
python -X utf8 tools/portal_run.py --port 8899 # 指定端口
python -X utf8 tools/portal_run.py --status / --stop
控制检验
| 检查 | 项数 | 拦的 bug |
| tools/check_portal_parity.py | 169 | 设置页三处对标:缺右上角求 Star 区块、局域网口令与公网共用、「自动选地址」只是显示不生效;未启动状态逻辑;排版(字号刻度、只引用真实主题令牌、动作与说明分层、警示块去重);公网密码自定义与警告提示;节点开关必须是"路由闸门"而不是启停上游、门户页只读无管理入口;节点卡片的内网/公网两个访问入口各自判断开关 |
| tools/check_portal_stop_scope.py | 13 | --stop 顺手杀掉同端口上别人的进程(用户正在跑的门户被检验脚本干掉);停止后口令/候选被整份删掉 |
| tools/check_portal_shutdown.py | 6 | 门户自退接口:局域网/公网来源必须被拒(不能从手机把门户关掉)、GET 不生效、端口松了进程没退 |
| tools/check_plugin_stop.py | 6 | 设置页「停止门户」真能停:宿主派生进程杀不掉门户却报成功(沙箱/作业隔离),改走"请门户自己退" |
| tools/check_portal_settings_forward.py | 7 | 设置页局域网三项(总开关 / 口令开关 / 手动选地址)被宿主静默忽略:返回 ok:true、toast 说改了,门户其实一点没变 |
| tools/check_public_pin.py | 18 | 公网密码自定义:新密码真能登录、旧密码真的作废(拿会话 cookie 当判据)、重启后仍在;安全负例:不能用局域网口令当公网密码、太短/空白被拒 |
| tools/check_portal_service_api.py | 13 | 节点接口:门户侧增删、开放/屏蔽(open/block)、enable/disable 老别名仍可用、restart 被明确拒绝;反例——门户没跑时转发必须返回 ok:false(上一轮的"静默忽略却弹成功"就是栽在这) |
| tools/check_portal_add_route.py | 12 | 添加节点走宿主 HTTP 路由的端到端:真 createHandler + 真门户 + 真磁盘;开放/屏蔽、反例(restart 必须被拒、target 非法必须 400 + 明确 error) |
| tools/check_gate_does_not_stop_upstream.py | 17 | 「屏蔽」不得停止上游进程(用户最关心的一条):起真上游进程,验屏蔽后 PID 仍活、绕过门户直连仍通、门户不再转发(404);删除节点也不动上游 |
| tools/check_portal_status_running_services.py | 6 | 添加节点后设置页看不到:宿主原来从不去问活门户要服务清单(判据漏了 services),修后能读回;反例——门户没跑时必须空数组,不许编造 |
| tools/check_shutdown_self_exit.py | 8 | 第二次点「停止」失败:serve_forever 先起、关闭钩子晚装,那个窗口里 /_api/shutdown 回 503「门户还没就绪」,宿主退到 --stop 兜底而兜底在沙箱里杀不掉(stoppedBy=stop-command)。含源码顺序断言 + 实测「端口一在听就能自退」 |
| tools/check_portal_lan_pin.py | 23 | 局域网口令刷新/自定义;公网拿局域网口令能进来(安全边界)、候选列表没落盘 |
| tools/check_portal_form.py | 8 | 控制台表单提交被当成坏 JSON(页面上点「添加服务」永远失败)、表单没有 key 字段导致必然失败 |
| tools/check_portal_tunnel.py | 11 | 门户已在跑时「开启公网访问」什么都不做(幂等检查把建隧道也跳过了)、「关闭公网」把整个门户停掉 |
| tools/check_portal_bind.py | 7 | 只绑 127.0.0.1(手机永远连不上)、Windows SO_REUSEADDR 重复绑定(流量随机分流)、端口被占不换端口 |
| tools/check_portal_websocket.py | 8 | 升级头被过滤(握手失败)、101 响应没原样回传、握手后没有双向管道(实时数据不刷新) |
| tools/check_portal_integration.py | — | 畸形 Host 被判成内网(garbage value 竟免密)、控制台链接双斜杠、局域网总开关只改界面不改认证 |
| tools/check_portal_registry.py | 88 | 路由重复/非法、key 冲突、改完不落盘(重启就丢)、坏文件崩溃 |
| tools/check_portal_console.py | 41 | 未启用服务可点、服务名里塞 (存储型 XSS)、二维码不确定;测试页(按钮/结果容器/屏蔽节点禁用/不用 opaque-redirect)、页脚「鼓励一下」、美化令牌(语义变量、深浅色、44px 触控、焦点环、reduced-motion) |
| tools/check_portal_probe.py | 21 | 门户「测试」按钮的服务端契约:上游活->200、上游死->门户 502 且正文含 Bad Gateway+上游(界面靠它区分是谁发的 502)、上游自己的 502 原样透传、302 透传。含真上游 + 真门户的端到端 |
| tools/check_probe_button_browser.py | 4 | 真机端到端:CDP 驱动真 Chrome 点门户页的「测试」按钮,逐节点核对结论(活的/死的/自崩的三种文案必须各不相同 —— 把上游自己的 502 报成「没启动」,用户会去启动一个本来就在跑的程序) |
| tools/check_portal_proxy.py | — | 自建域名被误判成内网、认证未前置、限速失效、cf-connecting-ip 被伪造 |
| tools/check_portal_auth.py | 35 | 未登录可访问、会话被伪造/篡改/过期、口令进 cookie |
| tools/check_tunnel_auth.py | 35 | 认证绕过、前缀没剥、上游错、未知前缀 200 兜底 |
| tools/check_tunnel_domain.py | 10 | 复用上一次的域名(追加日志里读到旧行) |
| tools/check_plugin.py | 41 | 插件导出契约、服务已死却报「运行中」、口令形态非法 |
端到端(真起门户进程,不是单元级)
| 脚本 | 项数 | 覆盖 |
| test/acceptance_portal.py | 30 | 页面增删改开关服务、路由改动生效、内网免密/外网口令、二维码/手机端/限速/健康检查真实性、三分类 fail-closed、依赖缺失返回 2 |
| test/test_autostart.py | 4 | 插件加载后门户自动跑起来(「装好即用」)、只开内网、内网地址可用 |
| test/test_lan_address.py | 16 | 网卡评分选地址(物理网卡优先、198.18.x 降权)、手动选地址立即生效、非法地址被拒、覆盖值落盘 |
| test/test_lan_toggle.py | 10 | 内网口令开关立即生效(不重启门户)、本机永远免密、公网不受影响 |
| test/test_lan_switch.py | 6 | 局域网访问总开关:关掉后内网直接 403(不是要口令)、本机不受影响、重开恢复 |
| test/test_public_link.py | 12 | 真实公网链路:域名可达、公网首页是登录页、未登录拿不到 API 数据、内网免密、畸形 Host fail-closed |
| test/test_settings_data.py | 10 | 设置页数据链:状态字段、二维码生成、二维码只给本门户地址(防图床) |
| test/test_host_buttons.py | 10 | 走插件宿主 API(设置页按钮真正调用的路径):开启→状态→重复开启→停止;停止走的是"门户自退";收尾恢复用户原本在跑的门户 |
依赖缺失必须「跳过并返回 2」,不得静默通过 —— 这是本仓库的硬纪律。
检验不许动用户正在跑的门户:脚本一律用临时端口 + 临时 PORTAL_RUNS_DIR,不碰用户真实的 runs/(那里面存着局域网口令和你注册的节点)。
这条是踩出来的:一句裸的 portal_run.py --stop 曾把用户的门户干掉,用户看到的是「二维码和访问地址一起消失」。
$node = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\primary-runtime\dependencies\node\bin\node.exe"
& $node --test test/host.test.mjs # 55 项宿主端控制检验 & $node --test test/client.test.mjs # 28 项客户端分发契约检验
Python 控制检验
python -X utf8 tools\check_portal_bind.py
python -X utf8 tools\check_portal_websocket.py
python -X utf8 tools\check_portal_integration.py
python -X utf8 tools\check_portal_registry.py
python -X utf8 tools\check_portal_console.py
python -X utf8 tools\check_portal_proxy.py
python -X utf8 tools\check_portal_auth.py
python -X utf8 tools\check_portal_stop_scope.py
python -X utf8 tools\check_portal_shutdown.pypowershell
python -X utf8 tools\check_plugin_stop.py
python -X utf8 tools\check_tunnel_auth.py
python -X utf8 tools\check_tunnel_domain.py
python -X utf8 tools\check_plugin.py
端到端(会真起门户进程,几十秒)
python -X utf8 test\acceptance_portal.py
python -X utf8 test\test_autostart.py
python -X utf8 test\test_lan_address.py
python -X utf8 test\test_lan_toggle.py
python -X utf8 test\test_lan_switch.py
python -X utf8 test\test_settings_data.py
python -X utf8 test\test_host_buttons.py # 需要 DSH 在跑;宿主代码过期会跳过并返回 2
改样式时怎么看效果(人工看图,不是自动检验)
tools/render_panel_preview.mjs 把设置面板真渲染出来(直接 require lib/client.js,最小 React 替身 + 假 fetch),用无头 Chrome 截图肉眼核对: powershell
node tools\render_panel_preview.mjs # 打印地址
然后访问 /?s=running|public|off[&dark=1] 截图
它是人工工具,不进控制检验(要人看图,没法自动判过),而且有已知局限:替身 React 的文本排版不真实(截图里标题与副标题会"看起来压在一起"),只适合看结构顺序、颜色、胶囊形状、层级差别。局限写在文件头。
它现在能反映真实布局了(0.8.4 修的):替身工厂原来把 className 当成字面量属性设上去(className="..."),而外观在 0.8.4 起改由注入的 CSS 类承担,于是预览会画出一个和真实渲染完全不同的页面(两栏不并排、hover 没有)。
这种"预览骗人"比没有预览更糟,已改成映射 className → class。
看门户页(访问页)的效果
tools/shoot_portal.py 在临时端口用当前代码起一个真门户(真 HTTP、真状态文件、真注册表),再用无头 Chrome 截三张图(窄屏 / 窄屏深色 / 宽屏): powershell
python -X utf8 tools\shoot_portal.py # 输出到 runs/shots/
为什么不直接截你线上那个 8899:那个进程里是改动前的 portal_console 模块(Python 一 import 就固定),要看新页必须重启你的门户 —— 而重启会换掉 cloudflared 隧道域名(你发出去的地址就废了)。所以另起一个临时的来看。
外观:一份注入的 CSS,而不是内联 style
面板的外观(底色、描边、圆角、尺寸、hover、焦点环、窄屏媒体查询)都在 client/index.jsx 顶部的 CARD_CSS 里,模块加载时注入一个(照 workbuddy 的做法,按 data-plugin-css 查重做幂等)。
为什么不用内联 style:内联的优先级高于类选择器,连 :hover 也压不过它 ——内联一旦写了底色,类里的 hover 就成了死代码。而"可点击元素要有按压反馈""键盘焦点要可见""窄屏要塌单栏"这三条都只能靠 :hover / :focus-visible / @media 实现。所以按钮原语的 style 对象是空的,颜色与尺寸一律走 .dsp- 类(类名统一 dsp- 前缀,避免与宿主或其他插件撞车)。
安全边界(必须如实理解)
- 口令是明文存在 runs/public_gui_credentials.json 并明文显示在插件设置页。
- 拿到口令 == 能改对外发布的路由。持有效会话者可以调用 /api/services 增删节点、开放或屏蔽路由。但它不能启停你的上游程序 —— 本插件已不做启停(所以拿口令的人无法借这里去停 5090 上的训练)。对 ld-rl-poker 而言,风险面从"能启停训练"缩小到"能改发布出去的路由"。
- 局域网免密时,同 WiFi 上的人也能读改节点(/_api/services 不需要口令)。
这是"内网免密"体验的代价;不想承担就开「局域网访问密码」或关掉局域网访问。
节点管理搬到插件设置后,日常操作走宿主转发(127.0.0.1),不必再开着控制台页面。
- 有些本地服务自身没有认证(两个 GUI、Ollama 等),挂出去就等于公开——门户这层口令是它们唯一的关卡。
- 本插件不会碰 dsh-pocket 自己的隧道(目标 3081);--stop 只停自己记录的进程。
- 登录后的会话是签名 cookie(HttpOnly,默认 12 小时到期):不必每次请求都带口令,但这也意味着在这段时间内,那个浏览器就等于已授权。会话密钥随门户进程随机生成,关掉门户即全体登出。
- 选固定口令就要接受它的代价:门户重启后口令不变,一次外泄就是长期敞口。
只想临时分享就保持默认的 random。
- 门户跑在本机 127.0.0.1 上,口令校验也发生在这一层;不要把入口端口直接暴露到公网,否则认证层会被绕过(cloudflared 隧道才是唯一的对外通道)。
测试 powershell $node = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\primary-runtime\dependencies\node\bin\node.exe" & $node --test test/host.test.mjs # 55 项宿主端控制检验 & $node --test test/client.test.mjs # 28 项客户端分发契约检验
端到端验收:真起门户进程,逐条验证「内网免密/外网口令/页面管理服务/限速/健康检查」
python -X utf8 test\acceptance_portal.py # 30 项
认证层与路由层的控制检验(纯标准库)
bash
python -X utf8 tools\check_portal_auth.py # 35 项:登录页/会话/认证前置
python -X utf8 tools\check_tunnel_auth.py # 35 项:路由/前缀剥离/多挂载/502
与同族插件的关系
dsh-service-portal 是 LaoDing 插件家族的一员(家族品牌图标与 dsh-connect-workbuddy / dsh-connect-trae 等一致)。家族成员共享:
- 设置卡片外观与 row. 文案约定;
- 手写 CommonJS 客户端分发格式(window.__ModuleLoader__.load),零构建依赖;
- 「启动器,不是第二份实现」的复用纪律:隧道/认证等已完成并验收的实现不在插件里重写。
历史上门户功能曾是专用子插件 dsh-poker-viewer(ld-rl-poker 外网开关);2026-10-15 已将其功能吸收进根插件成为通用门户能力(服务清单驱动,不绑定任何具体服务),子插件目录已删除。
市场收录与展示
插件已包含可安装的 dsh.bundle manifest,并发布到 npm。社区市场通常从 Awesome DSH Plugin 注册表同步条目;仓库内的提交草稿位于 awesome-dsh-plugin-submission/dingminhua__dsh-service-portal.yml。
上面这个链接用 GitHub 绝对地址,不是相对路径 —— awesome-dsh-plugin-submission/ 与 screenshots.json 不在 package.json 的 files 白名单里(它们只服务于仓库与收录流程,不该进 npm 包)。用相对路径的话,npm 页面上的链接会 404。
收录目录规范(依据 Awesome DSH Plugin 的 contributing.md):
- 每个插件一个 YAML:data/plugins/<owner>__<repo>.yml,字段为 url(与仓库地址完全一致)、 name(owner/repo)、category(有效值之一,本项目为 remote)、 description.en(必填,以句号结尾)/ description.zh(可选);描述中含 : 必须加引号。
- 仓库必须声明 dsh.bundle manifest(本项目 dsh.bundle.patch: ./cordis.patch.yml),且被 dsh-plugin topic 标记;提交数 ≥ 10、仓库满 1 天由 CI 自动检查。
- 一个 PR 最多收录 3 条;只改自己的条目,不动其他插件。
市场卡片中的截图与 GitHub README 徽章是两套机制:
- GitHub 徽章由本 README 顶部的 Shields 图片链接生成。
- 市场截图按当前约定由插件自己仓库根目录的 screenshots.json 声明,注册表 data/screenshots.json 是旧约定下的回退,不再添加新键。
- 市场图标/占位图由具体市场的展示规则决定,并不是 npm package.json 的通用字段,也不是 README 徽章。
已知限制
- 随机域名,不固定:每次启动都是一次性的 .trycloudflare.com,关闭或重启后失效,且可能被他人复用为陌生网站。需要固定自有域名得用 named tunnel + ingress,本插件不支持。
界面因此反复提醒「请勿收藏,每次从本页扫当前二维码」。
- cloudflared 要自己装:本项目复用本机已有的那份,不会自动下载(同族的 dsh-pocket 会多镜像源下载)。装好插件后在设置页就会看到缺什么、怎么装: GET /status 带出 prereqs 检测结果,缺 Python 3 时两个启动按钮直接禁用(内网也起不来),缺 cloudflared 时只有「启动并开公网」禁用、「启动(只内网)」照常可点。
- 存活探测只在刷新时发生:门户页与设置页打开时探一次上游端口,没有后台定时器,所以一个服务刚挂掉时,页面可能要刷新一次才会变「上游未响应」。
- 公网可达性受 quick tunnel 与 Cloudflare 边缘影响:DNS 传播可能需要几秒到几十秒, check_portal_tunnel.py 会等传播完成再判定;失败时先看 cloudflared 日志再怀疑插件。
- 内网免密是默认值,不是安全建议:同一 WiFi 下任何设备无需口令即可打开门户。在不可信的网络上请打开「局域网访问密码」。这是刻意的取舍(扫码即用的便利),界面已如实说明。
- Node ≥ 24 与宿主 API 版本:插件依赖 DSH 提供的 webServer / settings 能力;宿主缺这些能力时插件会 fail-closed(不挂路由),而不是半残运行。
- 宿主升级后插件被静默跳过:宿主只会加载 profile 清单 dsh.profile.bundles 里的 bundle。
新版宿主重建 profile 清单时若漏掉这一条,本插件会整体消失且不报错 —— 设置页没有这一项、 /service-portal/api/status 返回 404。判据:curl http://127.0.0.1:<端口>/service-portal/api/status 连不上就是没加载(不是"没启动门户")。处理:在「插件」面板里把 dsh-service-portal 重新打开,或把 "dsh-service-portal" 写回 ~/.dsh/profiles/<profile>/package.json 的 dsh.profile.bundles 后重启宿主。
- Windows / macOS / Linux 均已验证:cloudflared 的探测路径按平台与常见安装位置推导,若你的安装位置不在候选内,在设置页填 cloudflaredPath 即可显式指定(填了不存在的路径时,设置页会明确说是「填错了」而不是「没装」,两者要分别处理)。
免责声明
- 本项目仅供个人学习和研究使用,仅用于把你自己拥有或有权访问的本机服务对外提供,请勿用于商业用途或超出个人合理使用的场景。
- 公网暴露是你自己的决定,风险自担。开启公网访问后,任何拿到该地址与口令的人都能访问你发布的节点(能增删、开放或屏蔽节点,但动不了上游程序)。请勿在公网长期暴露管理口令,用完后及时「关闭公网」。
- 使用者需遵守 Cloudflare 的服务条款;因使用本项目产生的任何后果(包括但不限于域名被回收、隧道被限制、服务中断、数据暴露),由使用者自行承担。
- 本项目作者不对任何因使用或滥用本项目产生的直接或间接损失负责。
- 本项目与 Cloudflare、DeepSeek 均无关联,未获其授权或认可;文中出现的名称仅用于描述兼容关系,其商标权利归各自所有。
未做
- 固定自有域名:需要 named tunnel + ingress。现在每次启动都是随机 trycloudflare 域名。
- cloudflared 自动下载:本项目复用本机已有的那份(dsh-pocket 会多镜像源下载)。
- 服务健康检查定时刷新:现在是页面刷新时探测存活,没有后台定时器。
致谢
本项目的实现建立在他人已公开的工作之上。以下内容如实标注来源与许可证,我们对此保持充分尊重:
连接内核的参照
本项目的门户形态(门户网页、内网+外网双入口、按 Host 分类的认证强度)明确参照了 shaobeichen/dsh-pocket:
「把 DeepSeek Harness 装进你的口袋:一个包、一个设置页,手机扫码即同步访问电脑上的 DSH(局域网 + 公网,实时同屏)。」 —— GPL-2.0
我们从它学到的设计(逐条对应到本项目的实现):
| 它的做法 | 我们的实现 |
| classifyHost() 把 Host 分成 loopback / lan / public,按类别决定认证强度 | tools/portal_proxy.py::classify_host(),同一套三分类 |
| fail-closed:认不出的 Host 一律当公网(它曾因反着写被绕过,issue #66) | 同样 fail-closed,并有 20 项分类控制检验(含「自建域名必须判为公网」) |
| 公网始终要密码;局域网可开关;loopback 免密 | 内网免密、外网必须口令(用户选定) |
| 登录限速:单 IP 5 次失败锁 60s + 全局阈值 | RateLimiter,同样的阈值策略 |
| cf-connecting-ip 只在源地址是 loopback 时信任 | client_ip(),同一条判定(防伪造绕过限速) |
| 自定义登录页(不弹浏览器原生认证框) | tools/portal_login.py |
| 手机扫码即用(二维码 + 移动端适配) | tools/portal_console.py(纯 Python 生成 SVG 二维码,不引入依赖) |
同时说明清楚:本项目没有复制 dsh-pocket 的任何代码。
- 它是 JavaScript(lib/proxy.mjs / lib/tunnel.mjs / client/),本项目的门户是独立的 Python 实现(tools/portal_.py),两侧语言、结构与命名均不同;
- 双方暴露的 HTTP 接口、口令存储格式、配置文件格式都不兼容;
- 我们借用的是设计判断(尤其是 fail-closed 的兜底方向、限速阈值、IP 信任边界这类「用血换来的结论」),实现是从零写的。
因此本项目的 MIT 许可不受其 GPL-2.0 传染:GPL 约束的是衍生作品的代码分发,而「参考设计思路、独立实现」不构成衍生作品。我们仍在此明确致谢并标注其许可,一是尊重原作者,二是让读者能自己判断。
如果将来要在本项目里直接搬运它的代码,那就必须整体转为 GPL-2.0 ——请勿在未确认的情况下混入其源码片段。
插件呈现与结构的基线
- dingminhua/dsh-connect-workbuddy
(MIT,Copyright (c) 2026 LaoDing)— 插件的呈现方式与其保持一致:设置卡片的结构与交互、
只读的 host↔client 路由形态、dsh.bundle manifest 与 npm 发布工程。品牌图标(client/icon.ts 内联的 64px data URI)与其逐字节相同,为的是同族插件外观一致。
- dingminhua/dsh-connect-trae(MIT,
Copyright (c) 2026 LaoDing)、dingminhua/dsh-subagent-default-model
(MIT,Copyright (c) 2026 LaoDing)— dsm- 卡片风格体系与 row. 双语文案键约定的来源,
经 dsh-connect-workbuddy 转引。
其他
- cloudflared(Apache-2.0)—— 公网隧道。
- mexiaosqwq/dsh-web-mobile(MIT)—— dsh-pocket 的移动端适配移植自它,本项目未直接使用,但移动端设计的源头在此。
说明
以上项目的版权归各自作者所有。本项目采用借鉴设计思路 + 独立实现的方式,未整体复制任何参考项目的源码;关键模块均为独立编写,并在源文件头部注释中标注了所参考的具体项目与模式。
若你发现本项目标注有遗漏或不当之处,请提交 issue,我们会立即更正。
第三方开源依赖
本项目参考的开源项目、其许可证与合规说明,完整记录见 THIRD_PARTY_NOTICES.md。引入新的外部依赖或复用其他项目代码时,请同步更新该文件并遵守对应许可证要求。
许可证
MIT,Copyright (c) 2026 LaoDing。第三方致谢见 THIRD_PARTY_NOTICES.md。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
dingminhua/dsh-service-portal
一个随机域名 + 一个随机口令,把本机的服务按需对外开放。
相关插件 · 市场 / 管理
插件精选列表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