# 📦 @goodandready/dsh-key-rotation

<div align="center">

<h3>适用于 DeepSeek Harness 的企业级无感 API 密钥轮换、预判限流与跨提供商故障转移引擎</h3>

<p align="center">
  <a href="https://www.npmjs.com/package/@goodandready/dsh-key-rotation"><img src="https://img.shields.io/npm/v/@goodandready/dsh-key-rotation.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/GooDAnDReaDY/dsh-key-rotation.svg?style=for-the-badge&color=10b981&labelColor=064e3b" alt="license"></a>
  <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/DSH-Plugin-8b5cf6.svg?style=for-the-badge&labelColor=2e1065" alt="DSH Plugin"></a>
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-20%2B-f59e0b.svg?style=for-the-badge&labelColor=451a03" alt="Node version"></a>
</p>

<!-- 展厅链接 -->
<p align="center">
  <a href="https://goodandready.app/"><img src="https://img.shields.io/badge/🌐_DSH_Hub-goodandready.app-ff4500.svg?style=for-the-badge&labelColor=1a1a2e" alt="GoodAndReady Showcase"></a>
</p>

<p align="center">
  <a href="README.md"><b>🇬🇧 English</b></a> •
  <a href="README.ru.md"><b>🇷🇺 Русский</b></a> •
  <a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
</p>

<table align="center">
  <tr>
    <td align="center">
      ⭐ <strong>如果您喜欢这个插件，请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用，并鼓励我继续开发和维护它。
      <br><br>
      🐛 <strong>如果您发现 Bug 或希望增加功能</strong>，请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议，并在后续版本中实现有价值的改进。
    </td>
  </tr>
</table>

</div>

---

## ⚡ 概述与核心痛点

### 🚀 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 探测探活冷却密钥，密钥全耗尽时自动级联到备用提供商。

---

## 🏗️ 架构与请求生命周期

```mermaid
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`)。

---

## 🖥️ Web GUI 控制台 (**设置 → 密钥轮换**)

| 功能 | 说明 |
|---|---|
| **顶部状态栏微件** | DSH 顶栏实时健康徽章：🟢 正常 \| 🟡 存在冷却 \| 🔴 密钥池耗尽，点击弹出快速操作面板。 |
| **一键健康矩阵** | 运行全量密钥与模型并行沙箱测试，直观展示 HTTP 状态码与 TTFT 首字延迟。 |
| **一键凭证录入** | 点击添加自动生成规范名称（`<PROVIDER>_API_KEY`, `_2`, `_3`），悬停显示尾号。 |
| **实时状态徽章** | 实时显示：`使用中`、`就绪`、`冷却中`（带倒计时）以及 `凭证未找到`。 |
| **拖拽与顺序调整** | 使用 <kbd>↑</kbd> 和 <kbd>↓</kbd> 按钮调整轮换优先级。 |
| **密钥泄漏探测器** | 实时校验输入格式（`sk-...` 等），防止误贴私钥或无关 Token。 |
| **批量 `.env` 导入** | 支持文件导入解析并自动填充至对应提供商池。 |
| **5 秒撤销栏** | 误删密钥或提供商时提供 5 秒快速撤销操作。 |

---

## 🔒 安全性与凭证存储

* **配置零明文**：插件配置仅保存环境变量引用名（如 `MY_PROVIDER_API_KEY`）。
* **宿主安全存储**：真实密钥持久化保存在 `$DSH_HOME/.credentials.yaml`。
* **前台 5 字符脱敏**：前端仅展示密钥后 5 位字符进行视觉区分。
* **环回安全隔离**：管理接口严格限制来自本地同源请求 (`isTrustedBridgeRequest`)。

---

## 📦 安装指南

```bash
# 通过 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 服务并刷新浏览器页面：
> ```bash
> systemctl --user restart dsh-web
> ```

---

## ⚙️ 配置示例 (`settings.yaml`)

```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
    - provider: secondary-provider
      keys:
        - SECONDARY_API_KEY
        - SECONDARY_API_KEY_2
```

---

## 📄 开源许可

MIT © [GooDAnDReaDY](https://github.com/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` | 半开探测次数 |
