dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
解决中转站多分组多倍率不方便管理的问题:在提供 /v1/token/group 的站点上就地切换分组并同步刷新模型列表与计费倍率;缺少该接口的站点自动降级为只读,模型仍可正常拉取与调用。
DSH(DeepSeek Harness)插件 dsh-Aimy:解决中转站多分组多倍率不方便管理的问题——把中转站作为模型来源接入 DSH,支持就地切换中转站分组,同步刷新模型列表与计费倍率。
配合的中转站接口:GET/PUT /v1/token/group(见 ../docs/dsh-aimy.md)。
入口 · 插槽 · 说明 首页左侧栏图标 + 主区面板「Aimy-艾米」 · sidebar.panellist(order 30)+ main(key dsh-aimy) · 整页面板,撑满主区、自带滚动
main 的 key、包名一致,都是 dsh-aimy;sidebar 点图标时把 activePanelId 设成该 id,main 据此命中。src/client/app.ts 的 apply() 里,inject = ['slots']。react.createElement(可变 children);react/jsx-runtime 的 jsx(type, props, key) 第三参是 key,会把 h(type, props, ...children) 的子节点全部丢掉。src/shared/brand.ts 里的品牌路径。为什么不放设置页:宿主dsh-client-ui-settings-general的navIcon(id)按分区 id
硬编码图标,只认account/models/agent-presets/plugins/archived-sessions,
其余一律回落到齿轮;settings.section插槽也只消费id/order/label,从不读icon,
插件侧无法覆盖。借用已知 id 同样不可行——它们已被内置分区占用,重复 id 会让
renderSlot(only: active) 同时渲染两个分区。所以只保留侧栏入口,避免设置页出现一行齿轮。市场(dsh-market)详情页会展示插件截图。在你自己的仓库里声明,改图无需再提 PR:
// screenshots.json(与 package.json 同级)
["assets/screenshots/01-panel.png", "assets/screenshots/02-group.png"]assets/screenshots/(见该目录下的 README)/ 开头、不得含 ..),或 GitHub 托管的 https 绝对 URL(raw.githubusercontent.com / user-images.githubusercontent.com / camo.githubusercontent.com / github.com)。第三方图床会被拒——截图 URL 会带上访问者 IP
自检:npm run check:screenshots(校验格式、数量、主机白名单、文件是否存在)
当前未声明 screenshots.json:保留「从 README 自动抽取」的回退。想显示真实面板截图时,把图放进 assets/screenshots/ 再声明即可。@deepseek-ai/dsh-llm-pi-ai 挂载模型路由,切组后重挂载刷新模型目录dsh plugin --profile desktop add github:Sheep1x/dsh-aimy--profile 不能省:DSH 的 plugin 子命令要求显式指定 profile(省略会报 --profile is required)。dsh-aimy 加进 dsh.profile.bundles(见下)。lib/,安装即得可加载的包,不需要 allowBuilds 构建授权。cd ~/.dsh/profiles/desktop
pnpm add github:Sheep1x/dsh-aimy # 或 npm i github:Sheep1x/dsh-aimy然后在 profile 的 package.json 里把 dsh-aimy 加进 dsh.profile.bundles。
前置条件:本机要有git(pnpm 的github:依赖靠它取包),且能访问 GitHub。
仓库提交了构建产物 `lib/`,所以源码安装(dsh plugin add/pnpm add github:…/file:)拿到的就是可直接加载的包,
不依赖prepare在装机时构建(file:与dsh plugin add都不会跑prepare)。
改完源码请 node build.mjs 并一起提交 `lib/`,否则用户装到的是旧代码。先把源码构建出 lib/,再装:
pnpm add file:D:/openwork/dsh-aimygit clone https://github.com/Sheep1x/dsh-aimy.git
cd dsh-aimy
npm install # 装 esbuild 等构建依赖
npm run build # 产出 lib/index.js 与 lib/client.js
npm test # 31 项测试(host 27 + client 4)然后在 DSH profile 里装本地目录,并把 dsh-aimy 加进 dsh.profile.bundles(~/.dsh/profiles//package.json)。
cd ~/.dsh/profiles/desktop
pnpm add link:D:/openwork/dsh-aimy # 软链:build 完重启即生效
# 或 pnpm add file:D:/openwork/dsh-aimy # 快照:改完要 pnpm install --forcelink:只在node_modules/dsh-aimy建一条软链;file:会按files字段打快照,源码变动 pnpm 未必自动刷新。
两种方式下 peer(@deepseek-ai/*)都由 DSH 的 profile 解析器按包名路由,不依赖本目录的node_modules。
cd dsh-aimy
"C:/Program Files/nodejs/node.exe" build.mjs # 构建 lib/index.js + lib/client.js然后在 DSH profile(~/.dsh/profiles//package.json)里加依赖并加入 bundles:
cd ~/.dsh/profiles/desktop
pnpm add file:D:/openwork/one-hub/dsh-aimy{
"dsh": { "profile": { "bundles": [ "...", "dsh-aimy" ] } }
}重启 DSH 后,首页左侧栏出现「Aimy-艾米」图标,点开是整页面板。
Tab · 地址 · 密钥引用 · 路由名 · 显示名 · 能力 Aimy-艾米(默认) · 内置 https://aimy.sheep1x.top,界面只读展示、不可编辑 · ONEHUB_API_KEY · onehub · Aimy-艾米 · 读模型 + 真实切分组 兼容模式 · 用户自填(写入 $DSH_HOME/dsh-aimy/profiles.json) · ONEHUB_COMPAT_API_KEY · onehub-compat · 兼容模式 · 读模型 + 调用;分组接口缺失时自动只读
anthropic-messages 协议。/v1/token/group 返回 404 时只把 supportsSwitch 置 false,模型照常拉取与调用。插件配置在 profile 的 cordis.patch.yml(bundle 自带默认值,按需覆盖):
- id: dsh-aimy
name: dsh-aimy
config:
baseURL: https://aimy.sheep1x.top # 中转站根地址(不要带 /v1)
apiKeyRef: ONEHUB_API_KEY # 凭据引用名
group: '' # 期望分组(空 = 跟随令牌)密钥不写进配置文件:优先级是
ctx.credentials.set(apiKeyRef, …),write-only)保存后插件会立刻用它探测一次中转站,成功即挂载模型路由。点「清除」会 unset 该引用, 环境变量兜底重新生效。
站点能力 · 支持情况 GET /v1/models(标准 OpenAI 格式) · ✅ 必需。绝大多数中转站都有,new-api / sub2api / one-hub 均满足 调用模型 · ✅ 三种协议任选(Responses / Chat Completions / Anthropic) GET/PUT /v1/token/group(分组切换) · ⚠️ 可选能力。有则支持就地切组;没有则自动降级为只读
分组接口是可选扩展,不是所有站点都有。 new-api / sub2api 属于通用 OpenAI 兼容网关, 该接口是各站点自行加的,多数部署并没有实现。插件对此的处理是降级而非报错:
200 但 data 里既无 group 也无 groups、或返回 success:false→ 一律视为「该站点不支持切换分组」,supportsSwitch 置 false;
因此:在任何 OpenAI 兼容中转站上都能拉到模型并用;只有提供该接口的站点才能就地切组。 本站(one-hub 系)实测 GET /v1/token/group 正常返回 group / groups / ratio。
以下三条只在你想用分组切换时才需要;只拉模型用则无需满足:
/v1/token/group 的版本。切组是令牌级状态:同一把 key 上其它客户端也会跟着换组,所以建议专用令牌。
host 半侧在 /dsh-aimy-relay 前缀下暴露(仅供本插件的浏览器半侧调用):
方法 · 路径 · 说明 GET · /state · 当前分组、倍率、可选分组、模型目录(命中缓存时不打网络) POST · /refresh · 强制重新拉取分组与模型目录 POST · /switch · { "group": "vip" } 真实切组,随后刷新目录 POST · /credentials · { "apiKey": "sk-…" } 写入凭据库并立即探测/挂载 DELETE · /credentials · 清除凭据库中的密钥
"C:/Program Files/nodejs/node.exe" --test test/*.test.mjstest/host.test.mjs:host 路由与挂载(假中转站 + 假 cordis ctx)test/client.test.mjs:client 半侧(走 window.__ModuleLoader__ 契约,假 hooks 渲染真 React 元素树)main 的 keysize/activehost 覆盖(27 项):正常读取 / 真实切组 / 模型路由挂载 / 切组后重挂载 / 同分组不重挂载 / 中转站报错透传 / 空分组拦截 / 旧版站点降级 / 未配置密钥 / 保存密钥后自动挂载 / 空密钥拒绝 / 清除密钥 / 模型失效判定(在目录内 / 切走后失效 / 切回恢复 / 指向别的路由不误报)。
src/
shared/types.ts 两侧共用类型
host/relay.ts 中转站 HTTP 客户端(错误归一)
host/state.ts 模型目录缓存($DSH_HOME/dsh-aimy/state.json,仅非密钥)
host/routes.ts /dsh-aimy-relay/* 的纯函数编排
host/index.ts Cordis 插件入口(name / inject / apply)
client/index.ts __ModuleLoader__ 外壳
client/app.ts 装配层:注册 sidebar.panellist + main
client/section.ts 「中转站」设置分区 UIopenai-responses(默认)· openai-completions · anthropic-messages
协议在界面上是下拉框,两个 Tab 都能改:面板状态卡片里的「协议」一行,选中即保存 (POST /config?profile=aimy|compat,只传 api),改完服务端会按新协议重新挂载模型路由。 预设档案只开放协议这一项,地址仍固化在配置里、只读展示;兼容模式则地址与协议都可改, 只改协议时会保留已填地址。
协议变化必须触发重挂载:同一个模型集合在 completions / responses 下是两套线路, sameModels() 只看模型会误判成「没变化」,所以 forceRemount() 会先丢掉旧路由 再重建(ProviderMount.api 记录当前线路用于比较)。