@omdp/dsh-key-fallback 3.1.1 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,42 +1,34 @@
1
1
  # @omdp/dsh-key-fallback
2
2
 
3
- Multi-key API key pool with automatic rotation for DeepSeek Harness. Sits between the LLM adapter and the credential store:
4
- the plugin picks a key from the per-provider pool and pre-writes it into the provider's credential reference before each request;
5
- on a configured trigger error it marks the failed key (cooldown) and advances to the next key in the chain, then lets DSH's own
6
- `dsh-llm-retry` decide whether/when to retry (the plugin never re-sends on its own).
7
-
8
- Current version is **v6** (`3.1.0`).
9
-
10
- ## What v6 offers
11
-
12
- **Settings → API Key 回退** — a top-level settings page with a redesigned UI:
13
-
14
- - **Pool cards** with status dots, badges, enable switch, per-pool cooldown display and a "重置冷却" reset button.
15
- - **轮转触发码 (rotation triggers) are now configurable and actually enforced**: click chips to select which error codes
16
- cause rotation — the preset chips cover the full DSH `LlmError` standard code set (`QUOTA` / `AUTH` / `RATE_LIMIT` / `TIMEOUT` /
17
- `TRANSPORT` / `SERVER` / `EMPTY_RESPONSE` / `INVALID_CREDENTIAL`), or add a non-standard/custom code (matched exactly against the
18
- provider's `failure.code`). The default set is the full standard set.
19
- - **真实当前使用 key 显示**: the page shows which key is actually being used right now (derived from the last value written to the
20
- provider's env), instead of a truncated hash.
21
- - **短 ref 命名**: new keys are named `key_fallback_<provider>_key1`, `key_fallback_<provider>_key2`, … so the UI shows clean short
22
- names (`key1`, `key2`…) instead of long ref strings. Existing long refs are migrated once, automatically and idempotently.
23
- - **明文揭示**: every key row has an eye toggle (`👁` / `🙈`) that fetches and shows the real value via `GET /keys/plain` (only for
24
- keys owned by that pool, or the pool's env key).
25
- - **环境密钥可编辑**: the pool's env key (`AGNES_API_KEY` etc.) can be updated right in the page when it's backed by the writable
26
- credential file. If it is supplied by the launching environment (read-only), the UI says so and refuses to edit.
27
- - **Per-key controls**: rename/update value, set the "失败后→" next key (nextRef), lock the pool to a specific key ("设为当前"),
28
- delete a key (the env key is not deletable).
29
- - **Pool-level "锁定"**: lock the pool to a specific key or return to 自动轮换 (cursor-based).
3
+ [简体中文](README.zh-CN.md) | English
4
+
5
+ **Multi-key API key pool with automatic rotation for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)** — sits between the LLM adapter and the credential store. Before each request the plugin picks a key from the per-provider pool and pre-writes it into the provider's credential reference; when a configured trigger error occurs it marks the failed key cooling (fixed `cooldownMs`, no exponential backoff) and advances to the next key. **Re-sending is left entirely to DSH's own `dsh-llm-retry`** — this plugin never re-sends on its own; it only switches the key and lets the retry policy decide.
6
+
7
+ Current version: **v3.1.2** (`v6` UI generation).
8
+
9
+ ## Requirements
10
+
11
+ - DeepSeek Harness with a `web`-profile GUI (`npx @deepseek-ai/dsh web`)
12
+ - Node.js `^22.19` or `>=24`
13
+ - Peer ranges for `@deepseek-ai/dsh-credentials` / `dsh-llm` / `dsh-settings` cover the full `0.1.x` pre-release line — **`0.1.0-rc.6`, `0.1.1-rc.2`, and `0.1.2-alpha.1` all satisfy them**. The plugin only uses the credential-reference half (`resolve`/`describe`/`set`/`unset`/`credentialRef` — stable since `0.1.0-rc.6`) and the `agent/request` + `agent/request-error` waterfall (payload unchanged across these versions); `isCredentialRefName` (added `rc.8`) is implemented locally for compatibility. The peer range deliberately enumerates each `0.1.x` series because npm semver only matches pre-releases within the same `[major,minor,patch]` triple.
14
+
15
+ ## What v3.1.2 offers
16
+
17
+ **Settings → API Key 回退** — a top-level settings page with a redesigned UI (status dots, badges, gradient pool cards, per-key rows):
18
+
19
+ - **Configurable rotation triggers that are actually enforced** (`rotateOn`): click chips to select which error codes cause rotation. The preset chips cover the **full DSH `LlmError` standard code set** — `QUOTA` / `AUTH` / `RATE_LIMIT` / `TIMEOUT` / `TRANSPORT` / `SERVER` / `EMPTY_RESPONSE` / `INVALID_CREDENTIAL` — or add a non-standard/custom code (matched **exactly** against the provider's `failure.code`). What you save is what runs: there is no "magic upgrade" that rewrites three selected codes back into a six-code superset. `ABORTED` (user cancel) never triggers rotation.
20
+ - **真实当前使用 key 显示**: the page shows which key is actually being used right now (derived from the last value written to the provider's env) — not a truncated hash, not a guessed name.
21
+ - **短 ref 命名**: new keys are auto-named `key_fallback_<provider>_key1`, `key_fallback_<provider>_key2`, … so the UI shows clean short names (`key1`, `key2`, … or your custom `label`) instead of long ref strings. Existing legacy long refs are migrated **once, automatically and idempotently** (write new ref → persist config → best-effort unset old ref; any failure aborts and retries safely).
22
+ - **明文揭示**: every key row has an eye toggle (`👁` / `🙈`) that fetches and shows the real value via `GET /keys/plain` (only for keys owned by that pool, or the pool's env key). The env key row also reveals plaintext — it is not swallowed by the read-only note.
23
+ - **环境密钥可编辑**: the pool's env key (`AGNES_API_KEY` etc.) can be updated right in the page when it is backed by the writable credential file. If it is supplied by the launching environment (read-only), the UI says so and refuses to edit (HTTP 400).
24
+ - **Per-key controls**: update value, set the "失败后→" next key (`nextRef`), lock the pool to a specific key ("设为当前"), delete a key (the env key is not deletable).
25
+ - **Pool-level controls**: enable switch, cooldown display, "↺ 重置冷却", pool lock/auto-rotation, per-pool status (`live` / `cooling` / `recovered`).
30
26
 
31
27
  ## Rotation semantics
32
28
 
33
- - **pick**: respects `useKeyRef` lock first (but a cooling locked key is skipped so the pool never deadlocks on a bad key), otherwise
34
- cursor round-robin over live keys.
35
- - **fail** (`agent/request-error`, registered `prepend` so it runs before `dsh-llm-retry`): only when the error matches the pool's
36
- `rotateOn` (code / status mapping / message keyword). Marks the failed key with a fixed `cooldownMs` (no exponential backoff), then
37
- switches to `nextRef` if configured, else the next live key.
38
- - **re-send** is left entirely to DSH's `dsh-llm-retry` with the user's own retry policy; this plugin only switches the key and writes
39
- config/env. `ABORTED` (user cancel) never triggers rotation.
29
+ - **pick** (`agent/request`, pre-write): respects `useKeyRef` lock first (but a cooling locked key is skipped so the pool never deadlocks on a bad key), otherwise cursor round-robin over live keys. The chosen key is written via `credentials.set` **and** `process.env` so the provider actually authenticates with it.
30
+ - **fail** (`agent/request-error`, registered `prepend` so it runs before `dsh-llm-retry`): only when the error matches the pool's `rotateOn` — by `failure.code` (exact), by HTTP status mapping (`429→RATE_LIMIT`, `401/403→AUTH`, `402→QUOTA`, `5xx→SERVER`), or by message keyword. Marks the failed key with a fixed `cooldownMs` (default 30 s), then switches to `nextRef` if configured, else the next live key.
31
+ - **re-send** is left entirely to DSH's `dsh-llm-retry` with the user's own per-provider retry policy. The two are independent and complementary: the retry plugin decides "retry the same key N times" (`retryPolicy.retryableCodes`, default `EMPTY_RESPONSE/RATE_LIMIT/SERVER/TIMEOUT/TRANSPORT` — which notably excludes `AUTH`/`QUOTA`); this plugin decides "switch to the next key". So e.g. an `AUTH` failure (which retry would not re-send anyway) still rotates to the next key — that next request will authenticate with the fresh key.
40
32
 
41
33
  ## Diagnostics
42
34
 
@@ -46,32 +38,38 @@ Current version is **v6** (`3.1.0`).
46
38
  ## Install
47
39
 
48
40
  ```sh
49
- # from a local checkout (no publish required)
41
+ # from npm (recommended)
42
+ cd ~/.dsh/profiles/web
43
+ pnpm add @omdp/dsh-key-fallback
44
+
45
+ # or from a local checkout
50
46
  dsh plugin --profile web add link:D:/WorkSpace/omdp/dsh-key-fallback
51
- # or from npm
52
- dsh plugin --profile web add @omdp/dsh-key-fallback
53
47
 
54
48
  # restart DSH, then open Settings → API Key 回退
55
49
  ```
56
50
 
57
- > Note: this profile currently installs the package from npm (`^3.x`) into
58
- > `~/.dsh/profiles/web/node_modules/@omdp/dsh-key-fallback`; copying updated `lib/index.js` + `lib/client.js` there and restarting DSH
59
- > picks up v6.
51
+ > Note: the profile currently installs the package from npm (`^3.x`) into `~/.dsh/profiles/web/node_modules/@omdp/dsh-key-fallback`; copying updated `lib/index.js` + `lib/client.js` there and restarting DSH picks up a newer version. The package declares a `dsh.bundle.patch`, so it activates automatically — no manual `cordis.patch.yml` editing.
60
52
 
61
53
  ## HTTP API (host)
62
54
 
63
55
  | Method | Path | Purpose |
64
56
  | --- | --- | --- |
65
- | GET | `/dsh-key-fallback/pools` | list pools with live status, activeRef, env writability, per-key status |
66
- | POST | `/dsh-key-fallback/pools` | create/update pool (enabled, cooldownMs, rotateOn, useKeyRef, …) |
57
+ | GET | `/dsh-key-fallback/pools` | list pools with live status, `activeRef`, env writability/source, per-key status |
58
+ | POST | `/dsh-key-fallback/pools` | create/update pool (`enabled`, `cooldownMs`, `rotateOn`, `useKeyRef`, …) |
67
59
  | DELETE | `/dsh-key-fallback/pools?provider=` | delete pool + all its keys |
68
60
  | POST | `/dsh-key-fallback/keys` | add key (auto short ref `key_fallback_<p>_keyN`) |
69
- | PATCH | `/dsh-key-fallback/keys` | update value / label / nextRef / useKeyRef (env key value = edit env) |
61
+ | PATCH | `/dsh-key-fallback/keys` | update value / label / `nextRef` / `useKeyRef` (env key value = edit env) |
70
62
  | DELETE | `/dsh-key-fallback/keys?provider=&ref=` | delete key (env key refused) |
71
63
  | GET | `/dsh-key-fallback/keys/plain?provider=&ref=` | reveal real value (pool-owned keys / env key only) |
72
64
  | POST | `/dsh-key-fallback/reset` | reset cooldown state for a pool |
73
65
  | GET/POST | `/dsh-key-fallback/diag` | diagnostics buffer |
74
66
 
67
+ ## Known limitations
68
+
69
+ - Cooldown is a fixed per-pool `cooldownMs` (no exponential backoff); a cooled key becomes live again when the timer expires.
70
+ - The plugin manages rotation/cooldown state in-memory plus persisted pool config (`keyFallback.providers` in `settings.yaml`); a DSH restart re-reads config and recomputes live status.
71
+ - The env key cannot be deleted through the UI (it is the pool's identity).
72
+
75
73
  ## License
76
74
 
77
- MIT
75
+ MIT
@@ -0,0 +1,75 @@
1
+ # @omdp/dsh-key-fallback
2
+
3
+ English | [简体中文](README.md)
4
+
5
+ **为 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 提供多 key 池 + 自动轮换**——插件位于 LLM 适配器与凭证存储之间:每次请求前从按 provider 分组的 key 池里选一把,预写入该 provider 的凭证引用;遇到配置的触发错误时,把失败 key 标记为冷却(固定 `cooldownMs`,无指数退避)并前进到下一把。**重发完全交给 DSH 自带的 `dsh-llm-retry`**——本插件从不自行重发,只负责换 key,重试策略由 retry policy 决定。
6
+
7
+ 当前版本:**v3.1.2**(v6 UI 代)。
8
+
9
+ ## 环境要求
10
+
11
+ - DeepSeek Harness 带 `web` profile GUI(`npx @deepseek-ai/dsh web`)
12
+ - Node.js `^22.19` 或 `>=24`
13
+ - `@deepseek-ai/dsh-credentials` / `dsh-llm` / `dsh-settings` 的 peer 范围覆盖完整 `0.1.x` 预发布线——**`0.1.0-rc.6`、`0.1.1-rc.2`、`0.1.2-alpha.1` 均满足**。插件只使用 credential-reference 半边(`resolve`/`describe`/`set`/`unset`/`credentialRef`,自 `0.1.0-rc.6` 起稳定)与 `agent/request` + `agent/request-error` waterfall(载荷跨这些版本未变);`isCredentialRefName`(rc.8 新增)本地实现兜底。peer 范围故意逐个枚举 `0.1.x` 系列,因为 npm semver 只匹配同 `[major,minor,patch]` 三元组内的预发布。
14
+
15
+ ## v3.1.2 提供什么
16
+
17
+ **设置 → API Key 回退** —— 顶层设置页,全新 UI(状态点、徽章、渐变池卡片、逐 key 行):
18
+
19
+ - **可配置且真正生效的轮转触发码**(`rotateOn`):点选 chips 决定哪些错误码触发轮换。预设 chips 覆盖 **DSH `LlmError` 标准码全集**——`QUOTA` / `AUTH` / `RATE_LIMIT` / `TIMEOUT` / `TRANSPORT` / `SERVER` / `EMPTY_RESPONSE` / `INVALID_CREDENTIAL`——也可添加非标准/自定义错误码(与 provider 的 `failure.code` **精确匹配**)。你保存什么就执行什么:不存在"把选中的三个码偷偷变回六码超集"的魔法。`ABORTED`(用户取消)永不触发轮换。
20
+ - **真实当前使用 key 显示**:页面显示当前真正在用哪把 key(从最后一次写入 provider env 的值推导)——不是截断的哈希,不是猜的名字。
21
+ - **短 ref 命名**:新 key 自动命名为 `key_fallback_<provider>_key1`、`key_fallback_<provider>_key2`、…,UI 显示干净的短名(`key1`、`key2`、…或你的自定义 `label`)。已有旧长 ref **一次性、幂等地自动迁移**(写新 ref → 持久化配置 → best-effort 删旧 ref;任何一步失败即中止、安全可重试)。
22
+ - **明文揭示**:每行有眼睛开关(`👁` / `🙈`),经 `GET /keys/plain` 显示真实值(仅限本池的 key 或本池 env key)。env key 行也会显示明文——不会被只读说明吞掉。
23
+ - **环境密钥可编辑**:池的 env key(`AGNES_API_KEY` 等)在凭证文件可写时可直接在页面里改。若由启动环境提供(只读),UI 会说明并拒绝编辑(HTTP 400)。
24
+ - **逐 key 控制**:更新值、设置"失败后→"下一把(`nextRef`)、锁定到某把 key("设为当前")、删除 key(env key 不可删)。
25
+ - **池级控制**:启用开关、冷却显示、"↺ 重置冷却"、池锁定/自动轮换、逐池状态(`live` / `cooling` / `recovered`)。
26
+
27
+ ## 轮换语义
28
+
29
+ - **pick**(`agent/request` 预写):先尊重 `useKeyRef` 锁定(但冷却中的锁定 key 会跳过,避免池在某把坏 key 上死锁),否则按游标在 live key 上轮询。选中的 key 同时写 `credentials.set` 和 `process.env`,保证 provider 真的用这把 key 鉴权。
30
+ - **fail**(`agent/request-error`,注册 `prepend` 保证先于 `dsh-llm-retry` 看到错误):仅当错误匹配本池 `rotateOn` 时才处理——按 `failure.code`(精确)、按 HTTP 状态映射(`429→RATE_LIMIT`、`401/403→AUTH`、`402→QUOTA`、`5xx→SERVER`)、或按消息关键字。把失败 key 标记为固定 `cooldownMs`(默认 30 s),然后切到 `nextRef`(若配置)否则下一把 live key。
31
+ - **re-send** 完全交给 DSH 的 `dsh-llm-retry` 与用户自己的 per-provider 重试策略。两者独立互补:重试插件决定"同一把 key 重发几次"(`retryPolicy.retryableCodes` 默认 `EMPTY_RESPONSE/RATE_LIMIT/SERVER/TIMEOUT/TRANSPORT`——注意**不含 AUTH/QUOTA**);本插件决定"换下一把 key"。例如 `AUTH` 失败(重试本来也不会重发,重发也白搭)仍会轮换到下一把——下一次请求就会用新 key 鉴权。
32
+
33
+ ## 诊断
34
+
35
+ - client 会把运行时异常 POST 到 `POST /dsh-key-fallback/diag`。
36
+ - `GET /dsh-key-fallback/diag` 返回内存缓冲(最近 200 条)JSON。
37
+
38
+ ## 安装
39
+
40
+ ```sh
41
+ # 从 npm(推荐)
42
+ cd ~/.dsh/profiles/web
43
+ pnpm add @omdp/dsh-key-fallback
44
+
45
+ # 或从本地 checkout
46
+ dsh plugin --profile web add link:D:/WorkSpace/omdp/dsh-key-fallback
47
+
48
+ # 重启 DSH,然后打开 设置 → API Key 回退
49
+ ```
50
+
51
+ > 注意:该 profile 目前从 npm(`^3.x`)安装到 `~/.dsh/profiles/web/node_modules/@omdp/dsh-key-fallback`;把新版 `lib/index.js` + `lib/client.js` 复制过去并重启 DSH 即可升级。包声明了 `dsh.bundle.patch`,自动激活——无需手动改 `cordis.patch.yml`。
52
+
53
+ ## HTTP API(host)
54
+
55
+ | 方法 | 路径 | 用途 |
56
+ | --- | --- | --- |
57
+ | GET | `/dsh-key-fallback/pools` | 列池:live 状态、`activeRef`、env 可写性/来源、逐 key 状态 |
58
+ | POST | `/dsh-key-fallback/pools` | 创建/更新池(`enabled`、`cooldownMs`、`rotateOn`、`useKeyRef`、…) |
59
+ | DELETE | `/dsh-key-fallback/pools?provider=` | 删除池 + 其全部 key |
60
+ | POST | `/dsh-key-fallback/keys` | 加 key(自动短 ref `key_fallback_<p>_keyN`) |
61
+ | PATCH | `/dsh-key-fallback/keys` | 更新值 / label / `nextRef` / `useKeyRef`(env key 的值=改 env) |
62
+ | DELETE | `/dsh-key-fallback/keys?provider=&ref=` | 删 key(env key 拒绝) |
63
+ | GET | `/dsh-key-fallback/keys/plain?provider=&ref=` | 揭示真实值(仅本池 key / env key) |
64
+ | POST | `/dsh-key-fallback/reset` | 重置某池冷却状态 |
65
+ | GET/POST | `/dsh-key-fallback/diag` | 诊断缓冲 |
66
+
67
+ ## 已知限制
68
+
69
+ - 冷却为每池固定 `cooldownMs`(无指数退避);冷却到期 key 自动恢复 live。
70
+ - 轮换/冷却状态为内存态 + 持久化池配置(`settings.yaml` 的 `keyFallback.providers`);DSH 重启会重读配置并重算 live 状态。
71
+ - env key 不能通过 UI 删除(它是池的身份标识)。
72
+
73
+ ## License
74
+
75
+ MIT
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@omdp/dsh-key-fallback",
3
3
  "description": "API Key 回退轮换插件:多 key 池 + 自动轮换(触发码可配置,含 LlmError 标准码全集)+ 环境密钥编辑 + 明文揭示 + 短 ref 迁移。Settings → API Key 回退 UI. v6.1.",
4
- "version": "3.1.1",
4
+ "version": "3.1.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -39,9 +39,9 @@
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@deepseek-ai/cordis": "^4.0.1",
42
- "@deepseek-ai/dsh-credentials": ">=0.1.0-rc.8 <0.2.0",
43
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
44
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.7 || ^0.1.1-rc.2",
42
+ "@deepseek-ai/dsh-credentials": ">=0.1.0-rc.6 <0.2.0 || >=0.1.1-0 <0.2.0-0 || >=0.1.2-0 <0.2.0-0",
43
+ "@deepseek-ai/dsh-llm": ">=0.1.0-rc.6 <0.2.0 || >=0.1.1-0 <0.2.0-0 || >=0.1.2-0 <0.2.0-0",
44
+ "@deepseek-ai/dsh-settings": ">=0.1.0-rc.6 <0.2.0 || >=0.1.1-0 <0.2.0-0 || >=0.1.2-0 <0.2.0-0",
45
45
  "@deepseek-ai/schemastery": "^3.18.1"
46
46
  }
47
47
  }