본문으로 건너뛰기
B

dsh-keys-manager

btspoony/dsh-keys-manager

A DSH plugin that turns one provider API key into a managed pool + more built-in LLM Providers

설치

dsh plugin --profile web add github:btspoony/dsh-keys-manager

README

DSH Keys Manager

npm downloads License: MIT Node DSH

English | 简体中文

A DeepSeek Harness plugin that turns one provider API key into a managed pool. It extends the Models page rather than replacing it: providers, models, and the model picker stay where they are; only the API-key source moves behind the manager.

What it does

Pool and routing

  • Several keys per provider, each with an alias and a health state.
  • One key pinned to a session, round-robin for new sessions; a failing request is retried on its key, then moved to another key.
  • Exponential cooldown recovery, capped at seven days.

Accounts and sign-in

  • Seven pi-ai OAuth providers can join the pool as an account instead of a pasted key.
  • Two more sign in through the plugin's own bundled clients: Kilo and Z.AI Coding Plan.
  • Seven companion transport routes for providers the vendored pi-ai cannot speak: Cursor, Devin, the two Google routes, Factory Droid, and the two GitLab Duo routes.

Interface

  • A search-driven Keys Manager page on the plugin's own detail page.
  • A "managed by Keys Manager" badge inside the existing Models provider cards.
  • Extra built-in providers installed as ordinary llm-pi-ai routes — 35 of them, listed under Provider coverage.

Quick start

Install

In the Desktop or Web app, open Plugins → Add plugin, enter dsh-keys-manager, click Install, then Enable now. This installs the same published package as the CLI path, already carrying its built bundle.

dsh plugin --profile web add dsh-keys-manager

One install yields both bundles, dsh-keys-manager and llm-omp-transports; enable both — the manager owns credentials and routing, the transport the seven routes' wire protocols. Refresh the Web page afterwards for the browser half.

Install from a local checkout
git clone https://github.com/btspoony/dsh-keys-manager
cd dsh-keys-manager
pnpm i
dsh plugin --profile web link .

add installs the published tarball with its built lib/ and client bundle; link . builds them via prepare during pnpm i, as does prepack when packing.

Use it

Take over a provider and add keys

  1. Search for a provider. Open Plugins → Keys Manager → View (Plugins → 密钥管理 → 查看 in the Chinese locale); the manager renders on the plugin's detail page. Existing DSH providers appear first; the plugin also ships the curated extra catalog described under Provider coverage.
  2. Adopt a provider. For an existing provider the manager imports the currently resolved credential, writes a broker reference into the provider profile, and leaves the original key in the credential store so release can restore it.
  3. Add more keys with an alias. Every key is stored in the Harness credential store under its own reference; the manager state document never contains a key literal.
  4. Manage a key from the three-dot menu: rename, pause/resume, delete. A key row shows status, last use, and the recovery time while cooling.
  5. Tune retry count (1–3), base cooldown, and recovery grace in the settings card.

Accounts and sign-in

Providers that ship a native OAuth sign-in offer two ways to join the pool: Add key stores a static API key, while Add account runs the provider's OAuth sign-in flow and stores the result as an account row in the same pool. Nine routes join as an account: seven through pi-ai's own flows, kilo and zai through the plugin's bundled clients (device code, pasted zcode:// redirect), which mint a static credential renewed only by Sign in again.

Route groupHow it joinsRefresh / renew
9 OAuth accountsAdd account — seven pi-ai flows, two plugin clientsprovider-managed for the seven; Sign in again for the two static credentials
7 transport routesTransport accounts — one grant per routesee Transport routes

API keys form the selection ring — sticky sessions, round-robin, retry failover and cooldowns operate across the provider's keys — while one projected OAuth grant serves its provider, and an account row's counters reflect only that account's own successes and failures. Per-account ranking is future work. When the projected grant fails, the account cools down and the pool falls back to its keys, or stops serving on an OAuth-only route until a manual re-login.

muse-code needs no transport or account row of its own: the vendored pi-ai meta route (openai-responses on https://api.meta.ai/v1) serves it, the agreement check was static, and the first live call is a manual smoke check per Known limits.

Transport routes

Providers the vendored pi-ai cannot speak are served by the companion dsh-llm-omp-transports component:

  • One grant per route, at llm-omp-transports/<route> — no key pool and no per-account rotation. Sign-in happens in the Transport accounts section of the Keys Manager page.
  • Models are generated from the pinned @oh-my-pi/pi-catalog@18.12.3 table when the transport is built, and the clients are unofficial: read the transports package docs and its known limits before relying on them.
RouteSign-inRefreshModel rows
cursorbrowser page + pollon the credential seam123
devinpaste the landed callback URLnone — sign in again2
google-gemini-cliloopback 8085 + paste fallbackon the credential seam7
google-antigravityloopback 51121 + paste fallbackon the credential seam22
factory-droidWorkOS device code (URL + user code)one refresh in a 60 s window, keeps orgId/region62
gitlab-duoPKCE loopback localhost:8080/callback, no port fallback, paste fallbackone refresh in a 60 s window16
gitlab-duo-agentpaste the vscode:// URLone refresh, echoes the vscode redirect1
Per-route notes
  • Cursor — streams text and reasoning and hands tool calls to DSH's tool loop; images are refused with a typed error. The client presents Cursor's vendor-internal OAuth identity.
  • Devin — no refresh: a static bearer whose expiry is derived from the token (JWT exp minus five minutes, else one year), and without a readable expiry the row shows active with a blank countdown column.
  • Google routes — both share one generated lib/omp/google.mjs artifact yet keep separate grants, account rows and catalogs. The grant is {access, refresh, expires, projectId, email?}; sign-in resolves the Cloud project the route bills, and a legacy-tier or paid account whose project cannot be discovered fails AUTH_FAILED until GOOGLE_CLOUD_PROJECT (or GOOGLE_CLOUD_PROJECT_ID) is set.
  • Factory Droid — four wires behind one proxy, chosen by model id inside the artifact; rows carry no base URL and the account's residency (orgId/region) survives refresh.
  • GitLab Duo pair — gitlab-duo's direct-access token cache is keyed on the GitLab token, so a rotated token misses it; gitlab-duo-agent re-seeds a workflow per request and replays the transcript rather than resuming a socket.

Selection and failure policy

MechanismBehaviour
Sticky sessionThe first request of a session selects a key and binds it for that session/provider pair. Later requests reuse it.
Round robinA new session advances the provider cursor and takes the next healthy key.
RetryA failing request is retried on the same key retryCount times. Request-shaped failures such as CONTEXT_WINDOW_EXCEEDED and cancellation never cool a key.
SwitchAfter the retry budget is spent, the failing key is marked cooling, and the session is rebound to another healthy key. When no alternate key exists, the manager delegates to the composition's normal retry/fallback policy.
CooldownThe first failure cools a key for baseCooldownMs. A failure while cooling, or within recoveryGraceMs after recovery, doubles the next cooldown; a long healthy gap resets the escalation. The hard cap is seven days.
RecoveryThe key is automatically selectable again when its cooldown expires; no manual reset is required.

Configuration & data

Manager metadata lives at:

$DSH_HOME/keys-manager/state.json

with owner-only permissions. It contains aliases, credential references, health counters, session bindings, and provider adoption facts. API key literals live only in the Harness credential store; an account row follows the same rule behind its grantRef, with only non-sensitive metadata (email, account id, expiry) in the state. The file is written atomically through a temporary file and a rename.

Each managed provider profile points its apiKeyEnv at a broker reference such as KEYSMGR_ZAI_CODING_CN_3FA2C1. The Host credential layer answers that reference per request with the session's selected key. The Models page sees the profile as configured; writing directly to the broker reference is refused with a pointer to the manager.

OAuth2 extension point

The state shape already distinguishes kind: "api-key" from kind: "oauth" and keeps a nullable oauth object beside the credential reference. A future account flow can add records under keys-manager/<provider>/<key> without rewriting existing rows. Providers whose only authentication is OAuth are reported as unsupported in this version rather than being treated as API-key providers.

Provider coverage

Four groups, one pool:

  • DSH built-in — routes from the shipped DSH pi-ai catalog. The set follows your installed DSH version, so the Models page is the authoritative list; four of them are outside single-key pooling (below).
  • Plugin extra built-in — 35 routes this plugin adds as ordinary llm-pi-ai routes.
  • OAuth accounts — 9 routes that join as an account instead of a pasted key: seven use pi-ai's own flows, kilo and zai are signed in by the plugin's bundled clients.
  • Transport routes — 7 routes the vendored pi-ai cannot speak, served by dsh-llm-omp-transports.
Supported — every plugin extra, OAuth account and transport route

Plugin extra built-in — 35 — from oh-my-pi's provider catalog (MIT):

ProviderRoute idAPI key env
DeepInfradeepinfraDEEPINFRA_API_KEY
NovitanovitaNOVITA_API_KEY
StepFunstepfunSTEPFUN_API_KEY
SambaNovasambanovaSAMBANOVA_API_KEY
QianfanqianfanQIANFAN_API_KEY
GMI Cloudgmi-cloudGMI_API_KEY
SyntheticsyntheticSYNTHETIC_API_KEY
ZenMuxzenmuxZENMUX_API_KEY
Kilo GatewaykiloKILO_API_KEY
NanoGPTnanogptNANO_GPT_API_KEY
Venice AIveniceVENICE_API_KEY
AIML APIaimlapiAIMLAPI_API_KEY
AI&aiandAIAND_API_KEY
HelmcodehelmcodeHELMCODE_API_KEY
Alibaba Coding Planalibaba-coding-planALIBABA_CODING_PLAN_API_KEY
MiniMax Coding (Global)minimax-codeMINIMAX_CODE_API_KEY
MiniMax Coding (CN)minimax-code-cnMINIMAX_CODE_CN_API_KEY
Umans AI Coding PlanumansUMANS_AI_CODING_PLAN_API_KEY
Sakana AIsakanaSAKANA_API_KEY
CoralBrickscoralbricksCORAL_API_KEY
Wafer Serverlesswafer-serverlessWAFER_SERVERLESS_API_KEY
Alibaba Token Planalibaba-token-planALIBABA_TOKEN_PLAN_API_KEY
Alibaba Token Plan (CN)alibaba-token-plan-cnBAILIAN_TOKEN_PLAN_API_KEY
Charm Hypercharm-hyperCHARM_HYPER_API_KEY, HYPER_API_KEY
LiteLLM ProxylitellmLITELLM_API_KEY
SiliconFlowsiliconflowSILICONFLOW_API_KEY
SiliconFlow (CN)siliconflow-cnSILICONFLOW_CN_API_KEY
Zhipu Coding Planzhipu-coding-planZHIPU_API_KEY
AbliterationabliterationABLITERATION_API_KEY, ABLIT_KEY
ClinePasscline-passCLINE_API_KEY
Command CodecommandcodeCOMMAND_CODE_API_KEY, COMMANDCODE_API_KEY
CoreWeave Serverless InferencecoreweaveCOREWEAVE_API_KEY
Fire PassfirepassFIREPASS_API_KEY
Ollama Cloudollama-cloudOLLAMA_CLOUD_API_KEY
Yolo Autoyolo-autoYOLO_AUTO_API_KEY

Where two variables are listed, either is accepted; the first is the primary. CoreWeave Serverless Inference also needs COREWEAVE_PROJECT=<team>/<project> in the DSH process environment before it is added.

OAuth accounts — 9

ProviderRouteSign-in client
Anthropicanthropicpi-ai
GitHub Copilotgithub-copilotpi-ai
Kimi Codingkimi-codingpi-ai
OpenAI Codexopenai-codexpi-ai
OpenRouteropenrouterpi-ai
Radiusradiuspi-ai
xAIxaipi-ai
Kilokiloplugin bundled client — device code + poll
Z.AI Coding Planzaiplugin bundled client — pasted zcode:// redirect

The two plugin-signed credentials are static, tagged signin:kilo / signin:zai-coding-plan, and nothing is written to llm-pi-ai/<provider>; the seven pi-ai rows are the provider's own grants. kilo is listed twice on purpose: the same route takes an API key as an extra built-in and an account through its bundled client.

Transport routes — 7 — cursor, devin, google-gemini-cli, google-antigravity, factory-droid, gitlab-duo, gitlab-duo-agent; sign-in, refresh and model detail live in Transport routes.

Not pooled — 4 DSH built-ins
ProviderWhy
amazon-bedrockAWS credential chain (profile, IAM keys, ECS/IRSA/EC2), not one API key
cloudflare-ai-gatewayAPI key plus a Cloudflare account ID and gateway ID
cloudflare-workers-aiAPI key plus a Cloudflare account ID
openai-codexChatGPT OAuth sign-in only; no API-key credential — usable as an OAuth account instead of a key

Every other DSH catalog route exposes an API-key credential; a route you declare yourself in the profile (自定义 / Custom) is pooled like any built-in.

Documentation

DocContent
Developmentbuild and dev workflow, repository layout
ReleaseCI checks and the npm release flow
Known limitscurrent design boundaries, including the seven transport routes
Transports packageTransport routes: support status, risks, build/test, release

Chinese: README.zh-CN.md · 开发 · 发布 · 已知限制.

License

Released under the MIT License.

관련 플러그인