DeepSeek Harness 按服务商轮换 API key:密钥池、429 限流自动切换、冷却探测和设置界面
42/100
综合分
11
GitHub 星标
1,760
npm 周下载
—/5
用户评分
数据截至 2026/10/31 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @goodandready/dsh-key-rotationnpm 源 · @goodandready/dsh-key-rotation 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · ui
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 4 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包@goodandready/dsh-key-rotation @ 0.8.84
✓Node 引擎要求 >=18 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/1 07:02:32
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 88 处,展示前 48 条)
- 发起网络请求lib/client.js:613
fetch("/dsh-key-rotation/status", { headers: { accept: "application/json" }, signal: typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout - 发起网络请求lib/client.js:641
fetch("/dsh-key-rotation/sandbox-cache", { headers: { accept: "application/json" }, signal: typeof AbortSignal !== "undefined" && typeof AbortSignal.t - 发起网络请求lib/client.js:895
const res = await fetch("/api/dsh-key-rotation/update", { headers: { "x-dsh-plugin-update": "1" } }); - 发起网络请求lib/client.js:914
const res = await fetch("/api/dsh-key-rotation/update", { - 发起网络请求lib/client.js:977
return fetch(CONFIG_PATH, { headers: { accept: "application/json" }, signal: controller.signal }).then(async (response) => { - 发起网络请求lib/client.js:1263
res = await fetch("/dsh-key-rotation/export", { - 写入剪贴板lib/client.js:2355
if (navigator.clipboard) navigator.clipboard.writeText(key).then(() => setSecretDraft((cur) => ({ ...cur, ["copied:" + key]: true }))).catch(() => { - 读取密钥环境变量lib/crypto-storage.js:14
export function encryptSecret(plaintext, secretKey = process.env.DSH_KEY_SECRET) { - 读取密钥环境变量lib/crypto-storage.js:30
export function decryptSecret(ciphertext, secretKey = process.env.DSH_KEY_SECRET) { - 读取密钥环境变量lib/crypto-storage.js:55
export function decryptSecretFailClosed(ciphertext, secretKey = process.env.DSH_KEY_SECRET) { - 读取本地文件lib/plugin-updater.js:75
if (!existsSync(entry)) return undefined - 读取本地文件lib/plugin-updater.js:78
if (existsSync(manifestPath)) { - 读取本地文件lib/plugin-updater.js:80
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) - 发起网络请求lib/plugin-updater.js:152
const response = await fetch(`${registry.replace(/ $/, '')}/${encodeURIComponent(packageName)}/latest`, { - 读取本地文件lib/plugin-updater.js:166
const value = JSON.parse(await readFile(manifestUrl, 'utf8')) - 读取本地文件lib/plugin-updater.js:193
if (!existsSync(lockPath)) return undefined - 读取本地文件lib/plugin-updater.js:194
const stat = statSync(lockPath) - 删除本地文件递归lib/plugin-updater.js:228
rmSync(lockPath, { recursive: true, force: true }) - 删除本地文件递归lib/plugin-updater.js:238
rmSync(lockPath, { recursive: true, force: true }) - 删除本地文件递归lib/plugin-updater.js:247
rmSync(lockPath, { recursive: true, force: true }) - 执行外部命令来自配置/变量lib/plugin-updater.js:270
const child = spawn(process.execPath, [ - 请求外部主机api.telegram.orglib/routes/webhook.js:76
const hookRes = await fetch(`https://api.telegram.org/bot${botToken}/setWebhook`, { - 发起网络请求lib/routes/webhook.js:76
const hookRes = await fetch(`https://api.telegram.org/bot${botToken}/setWebhook`, { - 请求外部主机api.telegram.orglib/routes/webhook.js:105
await bestEffort('telegram-answerCallbackQuery', () => fetch(`https://api.telegram.org/bot${botToken}/answerCallbackQuery`, { - 发起网络请求lib/routes/webhook.js:105
await bestEffort('telegram-answerCallbackQuery', () => fetch(`https://api.telegram.org/bot${botToken}/answerCallbackQuery`, { - 读取本地文件lib/routes/config.js:8
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')); - 发起网络请求lib/safe-fetch.js:298
const req = transport.request(urlObj, reqOpts, (res) => { - 发起网络请求lib/client-src/20-hooks.js:8
fetch('/dsh-key-rotation/status', { headers: { accept: 'application/json' }, signal: (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout - 发起网络请求lib/client-src/20-hooks.js:38
fetch('/dsh-key-rotation/sandbox-cache', { headers: { accept: 'application/json' }, signal: (typeof AbortSignal !== 'undefined' && typeof AbortSignal. - 发起网络请求lib/client-src/40-components.js:81
const res = await fetch('/api/dsh-key-rotation/update', { headers: { 'x-dsh-plugin-update': '1' } }); - 发起网络请求lib/client-src/40-components.js:98
const res = await fetch('/api/dsh-key-rotation/update', { - 发起网络请求lib/client-src/40-components.js:154
return fetch(CONFIG_PATH, { headers: { accept: 'application/json' }, signal: controller.signal }) - 发起网络请求lib/client-src/50-card-main.js:119
res = await fetch('/dsh-key-rotation/export', { - 发起网络请求lib/client-src/50-card-main.js:128
res = await fetch(`/dsh-key-rotation/export?${q.toString()}`, { - 发起网络请求lib/client-src/50-card-main.js:156
const res = await fetch('/dsh-key-rotation/config-import', { - 发起网络请求lib/client-src/50-card-main.js:189
const res = await fetch('/dsh-key-rotation/config-import', { - 发起网络请求lib/client-src/50-card-main.js:232
fetch('/dsh-key-rotation/discover', { - 发起网络请求lib/client-src/50-card-main.js:249
const res = await fetch('/dsh-key-rotation/test-matrix', { - 发起网络请求lib/client-src/51-card-pool-mutators.js:16
fetch('/dsh-key-rotation/test', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ ref, probe: 'models' }), sig - 发起网络请求lib/client-src/51-card-pool-mutators.js:20
return fetch('/dsh-key-rotation/reset', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ ref }), signal: (typ - 发起网络请求lib/client-src/51-card-pool-mutators.js:39
const r = await fetch('/dsh-key-rotation/balance', { - 发起网络请求lib/client-src/51-card-pool-mutators.js:57
const r = await fetch('/dsh-key-rotation/preset-limits?provider=' + encodeURIComponent(provName)); - 发起网络请求lib/client-src/51-card-pool-mutators.js:82
return fetch('/dsh-key-rotation/test', { - 发起网络请求lib/client-src/51-card-pool-mutators.js:116
await fetch('/dsh-key-rotation/key', { - 写入剪贴板lib/client-src/52-card-pool-rows.js:56
if (navigator.clipboard) navigator.clipboard.writeText(key).then(() => setSecretDraft((cur) => ({ ...cur, ['copied:' + key]: true }))).catch(() => {}) - 发起网络请求lib/client-src/53-card-dialogs-and-render.js:367
fetch('/dsh-key-rotation/snapshot', { headers: { accept: 'application/json' }, signal: (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeo - 发起网络请求lib/client-src/60-header-chip.js:13
fetch('/dsh-key-rotation/health', { - 发起网络请求lib/client-src/60-header-chip.js:52
fetch('/dsh-key-rotation/quick-action', {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
涉及外部主机:api.telegram.org
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-llm@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 密钥池管理
- 429限流自动切换
- 冷却探测
最近更新
在 GitHub 查看全部 →v0.8.722026-10-23
· 发布 v0.8.72 [DSH v0.2.1-rc.2]
安全版本,解决 issue #516。
· 为什么发这个版本
修复一个安全漏洞:局域网/Tailscale 客户端经 dsh-lanmode 代理到回环地址、且不带 Origin 头时,能绕过写操作端点的来源授权;同时让 /auto-import 在请求体无效时直接拒绝。
· 改了什么
· 写操作来源校验改为默认拒绝(#516):不带 Origin 头的写操作 HTTP 方法(POST、PUT、PATCH、DELETE)一律返回 403 Forbidden,除非能明确识别为 Desktop 外壳(User-Agent 含 @deepseek-ai/dsh-desktop/ 且 Cookie 含 dsh-auth-),或 Referer 为同源且匹配。
· /auto-import 改为默认拒绝(#516):无效 JSON 返回 400 Bad Request,不再默认导入所有发现的 key。自动导入需要显式传 refs 数组或 all: true。设置卡片的客户端会显式发送 { all: true }。
· Desktop 外壳识别:在 lib/pool.js 中抽出 isDesktopShellRequest(req),与官方 Desktop 渲染进程的 forwardWebRequest 行为一致。
· 测试套件:补齐完整回归覆盖(test/issue-516-write-origin-guard.test.mjs,958/958 通过)。
· 验证
· npm test:958/958 通过,0 失败
· npm run check:205 个 JS 文件语法无误
· preflight.sh:FAIL=0
· 包体积:打包后 232 kB(上限 262 kB)
· 提交对应关系
· Gitea 提交:5dcfbc0
· GitHub 镜像提交:2e8cfe6cb867ff5040caee38f9767bcc2b6ae15d
v0.8.672026-10-19
· 功能
· 完整池配置导出与导入(#504):
- 脱敏导出:导出拓扑和池,凭据打码(sk-...cdef),webhook 密钥隐藏,可安全用于缺陷报告和分享。
- 完整备份导出,采用 AES-256-GCM 加密:可选口令用 PBKDF2(100,000 次迭代)加密配置,基于标准 node:crypto。
- 配置导入路由 POST /dsh-key-rotation/config-import:支持文件上传、原始 JSON,或带口令解密的加密载荷。
- 差异预览模式:dryRun: true 在应用前返回新建、更新、删除的服务商及密钥数量摘要。
- 设置卡片里的交互界面:导出(脱敏或完整,可选口令)与导入(文件上传、合并或替换模式、差异预览、应用)的模态抽屉。
· 聊天头部浮层里的快捷密钥操作(#499):
- 扩展 KeyRotationHeaderChip 浮层:显示每个已配置池的活跃服务商和密钥尾部(...abcd)。
- 内联交互操作按钮:
- ⏭️ 跳过:把活跃密钥游标推进到池中下一个健康候选,无需离开聊天。
- 🔄 重置:立即重置活跃服务商的失败计数、冷却和熔断器。
- ⏸️ 暂停:切换当前活跃密钥的暂停状态,即时隔离故障。
- 端点 POST /dsh-key-rotation/quick-action:经回环校验的安全操作路由,执行跳过、重置、暂停命令并上报审计遥测事件。
· 提交与验证
· Gitea 提交:1f85ba151805607a79bef510eff7b2b6d8c698e8
· GitHub 镜像:e8a92cf40ead95030cf7d7e16764f471c37a8040
· 测试:934 项通过,预检:FAIL=0 WARN=3。
v0.8.662026-10-19
· 功能
· 基于限流响应头的动态重置调度器(#498):
- 精确冷却对齐:限流失败时解析上游 retry-after、x-ratelimit-reset-*、anthropic-ratelimit-*-reset 响应头,把冷却时间精确设为 failedUntil = now + dynamicCooldownMs(加 250ms 安全抖动,受 maxCooldownMs 限制),resetReason = 'header'。
- 即时 unref 恢复定时器:上游窗口一滚动,就为受罚的 key 安排精确的 unref setTimeout 唤醒。
- 在 /dsh-key-rotation/status 和 WebUI 倒计时徽标中暴露 resetReason 和 headerResetMs。
· 上游余额检查器与额度追踪(#495):
- 多服务商余额检查器(lib/balance-inspector.js):查询 DeepSeek、SiliconFlow、OpenRouter 和 OpenAI 的实时账户余额与剩余额度。
- 余额运维路由 GET/POST /dsh-key-rotation/balance(lib/ops-balance.js):查询缓存的余额,或立即触发跨服务商 key 的实时余额检查。
- 空余额预保护:确认余额为零的 key 进入软暂停(EMPTY_BALANCE_PAUSED),刷新后余额为正时自动恢复。
- 余额状态持久化:缓存在 pool.state.balances,通过 lib/persistence.js 持久化。
- WebUI 集成:服务商页脚有可交互的 Check Balances 操作按钮,key 卡片上有实时余额徽标。
· 提交与验证
· Gitea 提交:933589eee5d59d794b770fbeea72d5b26833bbde
· GitHub 镜像:94259fcf4ce58391790ec2edf78409296e42d07c
· 测试:927 项通过,预检:FAIL=0 WARN=3。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成📦 @goodandready/dsh-key-rotation
适用于 DeepSeek Harness 的企业级无感 API 密钥轮换、预判限流与跨提供商故障转移引擎
🇬🇧 English •
🇷🇺 Русский •
🇨🇳 中文说明
⭐ 如果您喜欢这个插件,请在 GitHub 上为它点亮 Star — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
🐛 如果您发现 Bug 或希望增加功能,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
⚡ 概述与核心痛点
🚀 v0.8.67 新特性
- 📥 密钥池完整配置导入与导出 (#504): 支持脱敏导出安全分享、AES-256-GCM 密码加密完整备份、导入差异预览 (Diff Preview) 以及合并/全量替换模式。
- ⚡ 聊天头部快捷键操作 (#499): 直接在对话工具栏气泡中管理密钥 — 快速跳过当前密钥 (Skip)、一键重置冷却与断路器 (Reset)、暂停故障密钥 (Pause)。
展开完整文档(README 剩余 1 节)
📦 @goodandready/dsh-key-rotation
适用于 DeepSeek Harness 的企业级无感 API 密钥轮换、预判限流与跨提供商故障转移引擎
🇬🇧 English •
🇷🇺 Русский •
🇨🇳 中文说明
⭐ 如果您喜欢这个插件,请在 GitHub 上为它点亮 Star — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
🐛 如果您发现 Bug 或希望增加功能,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
⚡ 概述与核心痛点
🚀 v0.8.67 新特性
- 📥 密钥池完整配置导入与导出 (#504): 支持脱敏导出安全分享、AES-256-GCM 密码加密完整备份、导入差异预览 (Diff Preview) 以及合并/全量替换模式。
- ⚡ 聊天头部快捷键操作 (#499): 直接在对话工具栏气泡中管理密钥 — 快速跳过当前密钥 (Skip)、一键重置冷却与断路器 (Reset)、暂停故障密钥 (Pause)。
🚀 v0.8.66 新特性
- ⏳ 基于限流响应头的动态重置调度器 (#498): 解析上游 retry-after 与 x-ratelimit-reset- 响应头,设定精确冷却时间(+250ms抖动保护),并通过独立 unref 定时器在窗口重置瞬间立刻唤醒可用密钥,无需等待30秒周期性轮询。
- 💰 上游余额与信用额度巡检 (#495): 支持 DeepSeek、SiliconFlow、OpenRouter、OpenAI 实时余额查询 (/dsh-key-rotation/balance),零余额前置软暂停 (EMPTY_BALANCE_PAUSED),持久化余额状态以及 WebUI 密钥卡片实时余额标签 (💰 15.50 CNY)。
🚀 v0.8.65 新特性
- ⚡ 流量平滑微队列 (Traffic Smoothing Micro-Queue) (#496): 漏桶微队列平滑多智能体调用中的毫秒级突发并发,避免触发不必要的429限流与冷却流转。队列等待超限时自动轮转至池中下一个候选密钥。
- 📊 动态上游限制检测与预设规则 (#503): 自动提取上游响应头中的限流数据 (x-ratelimit-, anthropic-ratelimit-, retry-after),并在前端展示实时配额状态;支持一键应用 DeepSeek、Groq、Anthropic、OpenAI、Gemini 等官方规范预设限制。
🚀 v0.8.11 新特性(一键更新与质量门禁)
- 设置卡片中的插件更新器:查看当前/最新版本并一键安装(#307)。
- bestEffort 替代空 catch:非关键副作用在 debug 级别记录,不再被静默吞掉(#315).
- 客户端颜色仅使用主题变量,并清理 production-path 测试(#311、#314)。
- 发布集不再包含代理专用文件(#308)。
🚀 v0.8.10 版本新特性(流并发加固与状态自动修剪)
- 杜绝并发计数泄漏:在 try ... finally 中强制释放流并发占用,防止在正常完成或客户端中断时密钥被永久锁定。
- 探测抗网络抖动重试:在 probeModels 中遇到套接字网络临时错误时自动重试一次,避免密钥被误判损坏。
- 内存与过期状态自动清理:在定期清理周期中自动清除已删除密钥的内部映射记录。
🚀 v0.8.9 版本新特性(智能路由与界面升级)
- 前瞻性限流防护 (Proactive Rate-Limit Guard):根据 x-ratelimit-remaining- 和 Retry-After 响应头在触发 429 错误前自动预冷密钥。
- 自愈恢复 (Self-Healing / Auto-Unbreak):通过免费的 /models 接口进行定期后台探测,自动恢复处于 broken 状态的密钥,无需消耗聊天代币。
- 延迟感知路由 (Latency-Aware Routing):可选路由策略:round-robin(轮询)、least-loaded(并发负载最低)与 lowest-latency(p95 延迟最低)。
- dsh-clinebot 风格界面升级:支持带进度提示的批量“测试所有密钥”(Test All Keys)、实时事件流折叠抽屉(Live Event Stream)及配额重置倒计时徽标。
- 原生中英文双语支持:内置英文(en)与中文(zh)用户界面词典。
🛠️ v0.8.0 版本新特性(稳定性)
- 🔌 熔断器:连续失败后快速失败(CIRCUIT_OPEN),半开探测自动恢复。
- 🕒 单调时钟:冷却/熔断使用进程单调时间,NTP 校时不会颠倒剩余时间。
- 📮 非阻塞 Webhook:有界队列 + backoff,轮换不等待 HTTP。
- 🧱 原子写文件:损坏 JSON 不会覆盖既有状态。
- 🧹 克隆路由 GC:清理孤儿自动路由。
- 🧭 错误分类:408/425/429/5xx、套接字与 gRPC 的 switch/surface/soft。
- 📡 Status:提供商 circuit + meta。
- 🧪 Smoke:429 → 切换密钥 → 成功。
🛠️ v0.7.33 版本新特性 (稳定性与问题修复)
- 🔍 修复密钥探测 BaseURL 解析:resolveBaseUrl 现已支持从密钥 ref 反查归属提供商池,恢复在线模型连通性探测。
- 🛡️ 防御级联无限递归:在跨提供商故障转移中增加递归深度防护,彻底杜绝循环级联导致的堆栈溢出。
- 🕒 纠正 PST 太平洋时间配额重置:修复 UTC-8 时区偏移符号,确保日配额在太平洋时间午夜准时重置。
- 🧹 定时器生命周期自动回收:将金丝雀探测与自愈定时器纳入 Cordis 效应生命周期,消除热重载遗留孤儿定时器。
- ⚡ 负载均衡超时锁自动释放:pickLeastLoaded 算法现已检测过期连接锁,确保最小连接调度不发生偏移。
- 🌐 完整中文界面本地化:为 React 设置面板补充全部 zh 语言包,实现标准的三语(英/俄/中)无缝对齐。
🚀 v0.7.31 版本新特性
- ⚡ O(1) 令牌桶累加器:将速率限制计算升级为 O(1) 时间复杂度与零内存分配,并支持响应头自适应同步。
- 🛡️ 软/硬故障分级退避:区分临时网络抖动(502/503/超时获得 10 秒平缓冷却)与硬性配额超限(指数退避倍增)。
- ⏳ 惩罚衰减(Penalty Decay):持续稳定运行的密钥每小时自动平减一次失败惩罚系数。
- 🎲 冷却抖动(Jitter):为解锁时间添加 ±12.5% 随机离散度,彻底消除上游惊群效应。
- 🎯 定向金丝雀探测:支持针对具体目标模型进行轻量级单 Token 连通性探测。
- 📊 TTFT 百分位数(p50 / p95 / p99):在高精健康度指标中计算首字延迟百分位数。
- 🔔 Webhook 警报聚合摘要:在 5 秒窗口内将突发告警合并为单一结构化事件摘要,支持 Telegram/Discord/Slack。
- 🧹 30 天用量压缩:自动清理超过 30 天的历史统计数据,保障长期运行内存上限。
- ✨ 乐观 UI 与快速筛选标签:一键重置即时生效,密钥列表新增 全部、就绪、冷却中、故障 状态筛选胶囊。
在高吞吐量自主智能体运行、多子智能体并行执行与多轮工具调用场景下,API 极易触发上游服务商的速率限制(HTTP 429 Too Many Requests、RPM/TPM 耗尽、每日配额限制或网络抖动)。在原生的 DeepSeek Harness 中,单个密钥耗尽会导致整个智能体执行链路崩溃,破坏会话的 Replay 状态并要求人工干预。
dsh-key-rotation 基于 Cordis 微内核架构构建,提供了无缝透明的 API 密钥池轮换、客户端预判限流(Token Bucket)与跨提供商故障转移(Failover Cascade) 解决方案。
与修改模型提供商 ID 的传统网关代理不同,dsh-key-rotation 通过运行时拦截 ctx.credentials.resolve 与 llm/stream 钩子工作:
* 保持提供商身份一致:仅切换底层解析的 API 密钥,维持 pi-ai 多轮会话与工具状态 100% 一致。
* 令牌桶预判限流:在发起网络请求前预先跳过已饱和的密钥,彻底消除重试网络延迟。
* 最小连接数并发控制:动态均衡各密钥的 In-Flight 并发流,防止并发突发拥塞。
* 金丝雀自愈与级联:通过轻量 Sandbox 探测探活冷却密钥,密钥全耗尽时自动级联到备用提供商。
🏗️ 架构与请求生命周期
graph LR
subgraph ClientLayer ["客户端与智能体层"]
UserMsg["用户 / 智能体消息"] --> Adapter["pi-ai 模型适配器"]
end
subgraph RotationEngine ["dsh-key-rotation 核心引擎"]
Adapter --> StreamHook["llm/stream 拦截器"]
StreamHook --> BucketCheck{"Token Bucket\nRPM / TPM 校验"}
BucketCheck -->|未超限| ConcurrencyCheck{"并发跟踪器\n最小连接数"}
BucketCheck -->|已超限| NextKey1["选取下一可用密钥"]
ConcurrencyCheck -->|有空闲槽位| KeyResolver["ctx.credentials.resolve"]
ConcurrencyCheck -->|槽位已满| NextKey1
KeyResolver --> ActiveKey["活跃密钥 (执行中)"]
ActiveKey -.->|HTTP 429 / Quota / 错误| Failover["即时故障转移"]
Failover --> BackoffCalc["指数退避与隔离"]
Failover --> NextKey2["重试下一密钥 (零 Token 丢失)"]
Failover -.->|所有密钥均在冷却中| CascadeEngine["跨提供商级联"]
BackoffCalc --> QuotaWindow["日历重置 / 午夜对齐窗口"]
BackoffCalc --> CanaryProbe["金丝雀探针 (Sandbox Ping)"]
CanaryProbe -->|探活成功| PoolReady["恢复至就绪池"]
end
subgraph UpstreamLayer ["上游服务商端点"]
ActiveKey --> UpstreamAPI["主要提供商 API"]
CascadeEngine --> FallbackAPI["备用提供商 API"]
end
style ClientLayer fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
style RotationEngine fill:#181825,stroke:#cba6f7,stroke-width:2px,color:#cdd6f4
style UpstreamLayer fill:#11111b,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4
✨ 核心功能详解
🔄 1. 透明轮换与即时故障转移
* 维持提供商标识一致:轮换仅替换底层解析的凭证引用,不改变 Provider ID,彻底避免 INVALID_REPLAY_STATE 异常。
* 零 Token 丢失重试:在首个内容块发出前发生错误时,无感重试并切换至池中下一个健康密钥。
* 全状态码支持:支持 QUOTA、RATE_LIMIT、SERVER、TIMEOUT、TRANSPORT、EMPTY_RESPONSE、UNKNOWN_MODEL、AUTH 等。
* 正则消息模式分类:内置 SWITCHABLE_MESSAGE_PATTERN 正则引擎,自动识别 SDK 抛出的非结构化配额与限流异常。
* 非流式安全防护:通过 agent/request-error 生命周期钩子保护 Embeddings 及 Batch 调用。
⏱️ 2. 预判限流与并发控制
* Token Bucket 令牌桶 (lib/bucket.js):滑动窗口跟踪每分钟请求数 (rpmLimit) 与 Token 数 (tpmLimit),预先拦截超限密钥。
* 最小连接负载均衡 (lib/concurrency.js):实时追踪每把密钥的活跃流数量 (inFlight),执行 maxConcurrency 限制。
* 死锁自动释放:针对网络异常中断连接,超时 5 分钟自动清理占用计数。
🛡️ 3. 自动愈合与跨提供商级联
* 跨提供商故障转移级联 (lib/cascade.js):主提供商密钥全部冷却时,自动级联路由到备用提供商池。
* 沙箱密钥探测 (lib/sandbox.js):按需对密钥执行 /models 探测后再回到轮换;空闲冷却由 self-heal sweep 解除。
* 配额日历重置对齐 (lib/quota-window.js):支持 midnight_utc、midnight_pst 与 rolling_24h 配额刷新窗口。
* 自适应指数退避 (lib/pool.js):连续失败使冷却时间呈指数递增(基准 → ×2 → ×4 → 上限 ×8)。
📊 4. 统计分析与多平台交互式 Webhook
* 交互式 Webhook (lib/webhook.js):向 Telegram、Discord、Slack 推送带交互按钮的富文本警报,可在移动聊天中一键重置冷却或暂停提供商。
* 使用量与成本报表 (lib/usage-report.js):按日统计各密钥请求数与预估成本,支持一键导出 CSV/JSON (GET /dsh-key-rotation/usage-report)。
* 延迟 SLO 监控 (lib/histogram.js):记录首字延迟(TTFT)与健康度评分 (0..100)。
🎯 5. 模型级路由与按模型 Token 额度
* 模型子池(lib/pool.js):为特定模型层级(如重型推理模型 vs 轻量工具模型)配置专用密钥池。
* 按「模型 × 密钥」的 Token 额度(lib/model-quota.js):可为某个凭据在某个模型上单独设置本地 Token 额度。额度耗尽的密钥只在该模型上被跳过——claude-sonnet 额度用尽绝不会导致同一把密钥的 claude-opus 被禁用。
按模型、按密钥的 Token 额度
tokenLimit 作用于某个模型池中的某一个凭据。同一凭据可以在它所属的每个模型池中拥有各自独立的额度:
dsh-key-rotation:
quotaResetWindow:
type: midnight_utc
hour: 0
providers:
- provider: anthropic
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
models:
claude-sonnet:
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
quotas:
CLAUDE_KEY_A:
tokenLimit: 1000000
CLAUDE_KEY_B:
tokenLimit: 1000000
claude-opus:
keys:
- CLAUDE_KEY_A
- CLAUDE_KEY_B
quotas:
CLAUDE_KEY_A:
tokenLimit: 200000
CLAUDE_KEY_B:
tokenLimit: 200000
行为如下:
claude-sonnet 请求 → CLAUDE_KEY_A 的 Sonnet 额度仍有剩余 → 使用 CLAUDE_KEY_A → 按实际 usage 扣除 Token → CLAUDE_KEY_A / Sonnet 达到上限 → 后续 Sonnet 请求跳过 CLAUDE_KEY_A,改用 CLAUDE_KEY_B → claude-opus 请求仍可继续使用 CLAUDE_KEY_A
需要注意的规则与限制:
* tokenLimit 表示单个凭据在单个模型池中、于当前额度周期内可消耗的 Token 数。缺失、null、非数字或非正数一律表示不做本地 Token 限制——完全不写 quotas 时行为与此前版本完全一致。
* quotaResetWindow 控制这些额度的重置时机,复用已有的 midnight_utc / midnight_pst / rolling_24h 配置。重置是惰性的:周期结束后首次读取即归零,不会为每把密钥创建定时器。
* 额度依据成功响应返回的 usage 记账。 只有正常完成且带有可用 usage 的请求才会被扣减;失败、中断、被本地额度拒绝和重试路径都不会计费。
* 不上报 usage 的提供商无法精确统计。 这类请求不会被猜测、也不会被扣减,因此模型额度只可能被真正上报了 Token 用量的响应耗尽。
* 允许最后一个放行的请求略微超出配置上限;并发在途请求同样可能产生有限超出。这是当前版本的预期行为——本版本没有 Token 预留机制。
* 本地模型额度失败即关闭(fail closed):当所有凭据都没有额度时,请求不会发往上游,也不会回退到原始凭据绕过限制,而是进入既有的密钥池耗尽 / 级联流程。
* 额度耗尽属于预算状态,不是凭据故障:它不会写入冷却、失败计数或损坏标记;上游返回的 QUOTA 错误也仍然只在真正处理该请求的模型池内生效。
* 修改额度立即生效:把上限调到已用量之下会立刻变为已耗尽;调高则自动重新计算剩余量且不清空已用量;删除 quotas.<REF> 立即恢复为不限额。
* 状态文件中只保存凭据 ref 与计数器,额度上限本身仍来自设置配置;磁盘中不会写入任何真实 API Key,状态接口也不会返回密钥值。
🖥️ Web GUI 控制台 (设置 → 密钥轮换)
| 功能 | 说明 |
| 顶部状态栏微件 | DSH 顶栏实时健康徽章:🟢 正常 \| 🟡 存在冷却 \| 🔴 密钥池耗尽,点击弹出快速操作面板。 |
| 一键健康矩阵 | 运行全量密钥与模型并行沙箱测试,直观展示 HTTP 状态码与 TTFT 首字延迟。 |
| 一键凭证录入 | 点击添加自动生成规范名称(<PROVIDER>_API_KEY, _2, _3),悬停显示尾号。 |
| 实时状态徽章 | 实时显示:使用中、就绪、冷却中(带倒计时)以及 凭证未找到。 |
| 拖拽与顺序调整 | 使用 ↑ 和 ↓ 按钮调整轮换优先级。 |
| 密钥泄漏探测器 | 实时校验输入格式(sk-... 等),防止误贴私钥或无关 Token。 |
| 批量 .env 导入 | 支持文件导入解析并自动填充至对应提供商池。 |
| 5 秒撤销栏 | 误删密钥或提供商时提供 5 秒快速撤销操作。 |
| 模型子池与 Token 额度编辑 | 在每个提供商下按模型维护凭据列表,并逐个设置 Token 额度;实时显示 已用 / 上限、百分比、剩余量与重置倒计时,未配置时显示 不限额。 |
🔒 安全性与凭证存储
* 配置零明文:插件配置仅保存环境变量引用名(如 MY_PROVIDER_API_KEY)。
* 宿主安全存储:真实密钥持久化保存在 $DSH_HOME/.credentials.yaml。
* 前台 5 字符脱敏:前端仅展示密钥后 5 位字符进行视觉区分。对于长度 <= 5 的短密钥,返回统一脱敏占位符 (),防止凭证完整泄露。
Fail-Closed 环回同源安全隔离及写操作 Origin 守卫:管理接口严格通过 isTrustedBridgeRequest 校验:套接字对端与 Host 头必须均为环回地址(127.0.0.1、::1、localhost),并始终拒绝 sec-fetch-site: cross-site。若请求携带 Origin,则必须是 http(s) 环回源且与 Host 完全一致。变更方法(POST/PUT/PATCH/DELETE)无 Origin 时,仅允许通过明确 Desktop 外壳身份识别(User-Agent 含 @deepseek-ai/dsh-desktop/ 且 Cookie 含 dsh-auth-)或同源 Referer 的请求。GET/HEAD/OPTIONS 保持不限制,因为浏览器同源读取时不发送 Origin。
凭证池耗尽 Fail-Closed 熔断:当受管凭证池中所有密钥均被耗尽、暂停、过期或触发 RPM/TPM 限制时,解析器立即返回 LOCAL_POOL_EXHAUSTED 错误熔断,杜绝静默回退泄漏未受控原始密钥。
SSRF 与 DNS 重绑定防护:远程配置导入严格仅支持 HTTPS,全面校验解析 IP 并拦截 IPv4-mapped/IPv4-compatible 等各类 IPv6 环回变体(如 ::ffff:127.0.0.1、::ffff:7f00:1、64:ff9b::/96),并通过 undici 调度器执行连接时(connect-time)DNS 校验,从根本上防御 TOCTOU DNS 重绑定。
* 跨池吊销与状态继承:模型子池自动继承基础提供商的暂停、吊销与过期时间;运行时 401 永久认证失败将即时在所有关联共享池中统一标记吊销。
📦 安装指南
通过 DSH 插件管理器安装 (Web Profile):
dsh plugin --profile web add @goodandready/dsh-key-rotation
或直接从 GitHub 安装:
dsh plugin --profile web add github:GooDAnDReaDY/dsh-key-rotation
[!IMPORTANT]
安装后请重启 DSH Web 服务并刷新浏览器页面:
systemctl --user restart dsh-web
⚙️ 配置示例 (settings.yaml)
dsh-key-rotation:
switchCodes:
- QUOTA
- RATE_LIMIT
- SERVER
- TIMEOUT
- TRANSPORT
- EMPTY_RESPONSE
- UNKNOWN_MODEL
- AUTH
cooldownMs: 60000
circuitBreakerEnabled: true
circuitBreakerThreshold: 5
circuitBreakerOpenMs: 30000
circuitBreakerHalfOpenProbes: 1
concurrencyLimit: 5
quotaResetWindow:
type: midnight_utc
hour: 0
cascade:
- provider: backup-provider-id
model: your-backup-model-id
webhookUrl: "https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>"
providers:
- provider: your-primary-provider
rpmLimit: 60
tpmLimit: 100000
keys:
- PRIMARY_API_KEY
- PRIMARY_API_KEY_2
- PRIMARY_API_KEY_BACKUP
models:
reasoning-model-id:
keys:
- PRIMARY_API_KEY
- PRIMARY_API_KEY_2
quotas:
PRIMARY_API_KEY:
tokenLimit: 500000
PRIMARY_API_KEY_2:
tokenLimit: 1000000
- provider: secondary-provider
keys:
- SECONDARY_API_KEY
- SECONDARY_API_KEY_2
📄 开源许可
MIT © GooDAnDReaDY
v0.7.39
- 自动恢复与 lastUsedAt 修复:修复了 healIdleCooldowns 中对密钥调用时间戳的读取逻辑,直接从 lastUsedAt 映射读取。在 credentials.resolve 中补充记录每次密钥调用的时间戳,使状态面板的最近使用时间生效并正确支持空闲密钥恢复。
- 运行期快照缓存优化:消除了定时维护清理、/status 路由及密钥池耗尽处理中重复调用 buildRuntime() 的开销。
- CI 测试稳定性提升:对维护定时器与通知防抖定时器执行 unref,确保 Node.js 事件循环在测试完成后干净退出,彻底解决 CI 运行器假死问题。
v0.7.38
- 热路径流处理优化:在 rotate() 结束块处理中消除 4 次多余的 buildRuntime() 重复调用,直接复用请求作用域内的 runtime0 快照。
- 零额外字符串分配的限流头解析:重构 extractRateLimit(),采用单次遍历结合键长度检查,彻底消除对每个响应头执行 .toLowerCase() / .toUpperCase() 的内存碎片分配。
- 日期 ISO 字符串记忆化:在统计 costDays 与 usageDays 时将 todayIso 计算收敛为单次,杜绝重复创建 Date 实例。
- 状态统计零数组分配:在 /dsh-key-rotation/status 中将 totalUsage 的计算由 [...values()].reduce() 改为直接迭代累加,避免频繁轮询引发的垃圾回收波动。
- 孤立通知记录自动清理:在配置移除提供商模型池时,自动清理关联的通知限频缓存。
v0.7.37
- 通过 AsyncLocalStorage 隔离请求上下文:使用 Node.js 的 node:async_hooks 将解析后的密钥 (pickedRef)、启动时间和重试严格限定在单个请求上下文内,彻底消除并发请求间的竞态条件与误罚。
- 流异常自动故障转移:修复流在首个 token 返回前抛出传输异常(如 HTTP 429)直接终止的问题。若未发送内容块,现在会自动触发 isSwitchableError 并顺畅切换到备用密钥。
- 避免在 rotate() 中直接修改共享状态:遍历候选列表改用纯净的局部切片,不再直接覆盖修改 pool.weightedRefs。
- 测试成功后自动解除隔离:在设置界面通过沙箱成功验证密钥有效性后,自动清除 failedUntil 和 brokenUntil 惩罚标记。
- 定期内存压缩清理:将 compactUsage(pool, 30, now) 接入 30 秒后台巡检定时器,杜绝超长运行环境下的内存增长。
- 并发环境下的精确延迟统计:为每个请求独立计时,避免全局变量被并发请求覆盖导致 p50/p95 延迟失真。
v0.7.36
- 架构精简与稳定性加固:移除 6 个过度设计的模块(shadow、incident、agent-budget、region、canary、maintenance)与废弃端点。
- buildRuntime 高性能记忆化:消除每个流式 token/chunk 上的深拷贝与模式重解析开销。
- 原子轮询指针推进:并发请求在选定候选密钥时立即推进指针,消除并发工具调用中的竞争条件。
- 增强的可切换错误检测:直接解析 HTTP 状态码(429, 401, 403, 5xx)与 gRPC 状态码(RESOURCE_EXHAUSTED, UNAVAILABLE)。
- 用户友好的耗尽提示:密钥池耗尽时返回带恢复倒计时的清晰通知。
- 智能轮询(Smart Polling):标签页不活动时暂停客户端后台轮询。
v0.7.35
- 生命周期清理: 将 credentials.resolve 猴子补丁和 ctx.on 事件监听器 (llm/stream, agent/request-error) 封装在 ctx.effect 作用域内,确保卸载时自动注销并恢复原始方法 (#238, #239)。
- 配置密钥角色: 在 Config Schema 中为 incidentGitHubToken 和 webhookActionToken 增加 .role('secret'),避免明文泄露并在 UI 中掩码显示 (#237)。
- 设置架构与状态: 在设置卡片中增加原生 settingsScope 绑定支持,保留 HTTP 桥接安全回退机制 (#235)。
- 原生设计系统 (Changed in v0.8.3): 与 dsh-clinebot 基准对齐:模块化分区卡片、实时密钥池指标块、胶囊状态徽章与全局主题语义 token (#281)。
- 本地化与文案 (Changed in v0.8.2): 插件仅注册英文源字符串 en;俄文/中文由 DSH 核心 locale 与 translation 插件通过 props.t 提供。活动语言回退:snapshot → 首个 navigator.languages → en。已移除 settings.section 回退与内置 ru/zh 表 (#236, #275, #277)。
- 死代码清理: 移除 header-chip 迁移后残留的废弃 mountDashboard 函数 (#240)。
熔断器参数(v0.8.0)
| 参数 | 类型 | 默认 | 说明 |
| circuitBreakerEnabled | boolean | true | 启用熔断 |
| circuitBreakerThreshold | number | 5 | 连续失败阈值 |
| circuitBreakerOpenMs | number | 30000 | 打开时长 ms |
| circuitBreakerHalfOpenProbes | number | 1 | 半开探测次数 |
| showChatHeaderChip | boolean | true | 在会话界面顶部工具栏显示密钥池状态按钮 (#25) |预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
GooDAnDReaDY/dsh-key-rotation
DeepSeek Harness 按服务商轮换 API key:密钥池、429 限流自动切换、冷却探测和设置界面
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,350已汉化DSH
71.5
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,506已汉化DSH
71.1
AI 矢量设计工具ZSeven-W/openpencil
全球首个 AI 原生开源矢量设计工具。
★ 7,027已汉化DSH
71.1
这是你的仓库?认领条目