{"_id":"@argszero/cordis-plugin-credential-rotate","_rev":"2-ac03caf0fe6749d9b488078b6eba5891","name":"@argszero/cordis-plugin-credential-rotate","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@argszero/cordis-plugin-credential-rotate","version":"0.1.0","keywords":["api-key","cordis","credential","deepseek-harness","dsh","failover","llm","plugin","quota","rate-limit","rotate"],"license":"MIT","_id":"@argszero/cordis-plugin-credential-rotate@0.1.0","maintainers":[{"name":"argszero","email":"argszero.reg@gmail.com"}],"dsh":{"bundle":{"patch":"./cordis.patch.yml"}},"dist":{"shasum":"c10e31a9cddf233fbc471c8981c3345028d4869f","tarball":"https://registry.npmjs.org/@argszero/cordis-plugin-credential-rotate/-/cordis-plugin-credential-rotate-0.1.0.tgz","fileCount":6,"integrity":"sha512-lfzcEx2dbs+0bF2NEQmc2s1QgN4e1U82lsxRDrgQNoOVCmktKVd55k7Mip7hWfR09q1hnMLyP6usiKVF2it+OQ==","signatures":[{"sig":"MEYCIQD6L0Jarb31H2l31nWkd92E8hg3IQAfpNMOuKm2lLTWdgIhAMKcvNcz7XD2K7EDIrFOOyndoObdcgaZA13nmzEimN6T","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35707},"main":"lib/index.js","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./package.json":"./package.json"},"gitHead":"ba9a7799b6618963b21b4ef8ef50154ca0e0cc87","scripts":{"test":"tsc && node --test \"test/*.test.js\"","build":"tsc","prepublishOnly":"tsc"},"_npmUser":{"name":"argszero","email":"argszero.reg@gmail.com"},"_npmVersion":"11.17.0","description":"Per-route API-key rotation + retry for dsh. When a provider request ends with a retryable failure (default: QUOTA, AUTH — the codes where waiting cannot help), this plugin (1) repoints the provider's credential reference (e.g. llm-deepseek's apiKeyEnv) to","directories":{},"_nodeVersion":"26.5.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.0","@types/node":"^26.5.0","@deepseek-ai/cordis":"^4.0.2","@deepseek-ai/dsh-llm":"0.1.5-rc.2"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.2","@deepseek-ai/dsh-llm":">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cordis-plugin-credential-rotate_0.1.0_1789143032769_0.956662002892168","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"_id":"@argszero/cordis-plugin-credential-rotate@0.1.1","dsh":{"bundle":{"patch":"./cordis.patch.yml"}},"dist":{"shasum":"2846b134afe41bbb453e731612bfaa90e3f8970f","tarball":"https://registry.npmjs.org/@argszero/cordis-plugin-credential-rotate/-/cordis-plugin-credential-rotate-0.1.1.tgz","fileCount":6,"integrity":"sha512-A2tgN+uhycH+s1x7/8M/pSGvKrmPv2+w/ZOk116h81LNYW3VbRa8D5M+PH8f+/XPgCGjgKN2Iz9OFDzBTthpEw==","signatures":[{"sig":"MEUCIFFkN95e95xgpLWAOjCl+NwZKzaTImiyrk3QZgyZF09SAiEA//3VvWDxdhk8UPNXQQQ0mMm7QlnrokyJ30fNo1YKGfk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFtgDaKcAJsmAqvHEHT1Xi8xLEfcJN8Y5+TRPctPDZ23AiBcTS9gLpEITCpXkaTEFdtXxRtRFHYim6kiCI3wNFgKcA=="}],"unpackedSize":35983},"main":"lib/index.js","name":"@argszero/cordis-plugin-credential-rotate","type":"module","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./src/*":"./src/*","./package.json":"./package.json"},"gitHead":"ba9a7799b6618963b21b4ef8ef50154ca0e0cc87","license":"MIT","scripts":{"test":"tsc && node --test \"test/*.test.js\"","build":"tsc","prepublishOnly":"tsc"},"version":"0.1.1","_npmUser":{"name":"argszero","email":"argszero.reg@gmail.com"},"keywords":["api-key","cordis","credential","deepseek-harness","dsh","failover","llm","plugin","quota","rate-limit","rotate"],"_npmVersion":"11.17.0","description":"Per-route API-key rotation + retry for dsh. When a provider request ends with a retryable failure (default: QUOTA, AUTH — the codes where waiting cannot help), this plugin (1) repoints the provider's credential reference (e.g. llm-deepseek's apiKeyEnv) to","directories":{},"maintainers":[{"name":"argszero","email":"argszero.reg@gmail.com"}],"_nodeVersion":"26.5.0","dependencies":{"@deepseek-ai/schemastery":"^3.18.1"},"_hasShrinkwrap":false,"devDependencies":{"semver":"^7.8.5","typescript":"^5.5.0","@types/node":"^26.5.0","@deepseek-ai/cordis":"^4.0.2","@deepseek-ai/dsh-llm":"0.1.6-alpha.2"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.2","@deepseek-ai/dsh-llm":">=0.1.2-rc.1 <0.1.3 || >=0.1.3-alpha.2 <0.1.4 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-alpha.1 <0.2.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cordis-plugin-credential-rotate_0.1.1_1789709093343_0.9074732627526108"}}},"time":{"created":"2026-09-11T16:10:32.601Z","modified":"2026-09-18T05:24:53.563Z","0.1.0":"2026-09-11T16:10:32.898Z","0.1.1":"2026-09-18T05:24:53.426Z"},"license":"MIT","keywords":["api-key","cordis","credential","deepseek-harness","dsh","failover","llm","plugin","quota","rate-limit","rotate"],"description":"Per-route API-key rotation + retry for dsh. When a provider request ends with a retryable failure (default: QUOTA, AUTH — the codes where waiting cannot help), this plugin (1) repoints the provider's credential reference (e.g. llm-deepseek's apiKeyEnv) to","maintainers":[{"name":"argszero","email":"argszero.reg@gmail.com"}],"readme":"# @argszero/cordis-plugin-credential-rotate\n\nPer-route **API-key rotation + retry** for [dsh](https://github.com/deepseek-ai/deepseek-harness) — when one\ncredential is exhausted, the next one takes the same request. No proxy, no `baseURL` surgery.\n\nGrounded in [discussion #6344](https://github.com/deepseek-ai/deepseek-harness/discussions/6344)\n(“one provider route, several credentials, rotate automatically”).\n\n## What it does\n\nA route carries one credential *reference* (`apiKeyEnv` for `llm-deepseek`,\n`profile.apiKeyEnv` for `llm-pi-ai`). When that key hits its quota or is revoked,\nthe whole route goes dark even though the operator holds several keys.\n\nThis plugin keeps the route alive:\n\n1. a request fails with a **rotation-eligible** code (`QUOTA`, `AUTH` by default),\n2. **and nothing visible has been rendered yet**,\n3. so it repoints the credential reference to the next name in your pool — via\n   `settings.update(namespace, patch)` — and re-runs **the same request** through\n   the `llm/stream` waterfall. The adapter re-resolves its credential once per\n   stream call, so the retry simply carries the next key.\n\nWhen the pool is spent, the request fails visibly with code\n`CREDENTIAL_ROTATE_EXHAUSTED` (the message names every reference tried and\nrepeats the provider's own failure text).\n\n## Install\n\n```sh\ndsh plugin --profile web add @argszero/cordis-plugin-credential-rotate\n```\n\nThen put the pool in the environment (values, not references — the plugin never\nsees them) and point the plugin at the pod that holds the reference:\n\n```sh\nDEEPSEEK_KEY_A=sk-... DEEPSEEK_KEY_B=sk-... dsh web\n```\n\n```yaml\n# profile layer\n- set:\n    - id: credential-rotate\n      config:\n        pods: ['llm-deepseek:apiKeyEnv']\n        refs: [DEEPSEEK_KEY_A, DEEPSEEK_KEY_B]\n```\n\n`refs` are environment-variable **names**. A reference that resolves to nothing\nis skipped with a warning — it is never installed as an empty key.\n\n| option | default | meaning |\n|---|---|---|\n| `pods` | `[]` | `<settingsNamespace>:<field>` addresses that hold the credential reference |\n| `refs` | *(required)* | the pool, tried in order |\n| `rotateCodes` | `[QUOTA, AUTH]` | failure codes that justify a rotation |\n| `maxRotationsPerRequest` | `3` | rotations allowed inside one request |\n| `maxPodWritesPerRotation` | `200` | write bound for one rotation move |\n\n## Why not rotate on `RATE_LIMIT` too?\n\n`RATE_LIMIT` is **transient** — the same key usually works again after the\nprovider's own `Retry-After`, which the in-tree `llm-retry` policy already\nhonours. `QUOTA` and `AUTH` are the codes where waiting cannot help. Rotating on\n`RATE_LIMIT` would burn every key on one throttled minute; if you disagree, add\nit to `rotateCodes` explicitly.\n\n## What it deliberately does not do\n\n- **No response buffering.** Chunks are forwarded the instant they arrive. The\n  only withheld data is the *head* of an attempt (chunks that arrived before\n  anything visible), which must not be replayed if that attempt is discarded.\n  That buffer closes permanently at the first visible delta.\n- **No rotation after visible output.** A mid-stream failure is passed through\n  untouched: re-running a partially-rendered answer would duplicate it, and that\n  policy belongs to the harness (`assistant/attempt`), not to a plugin.\n- **No unbounded retry.** Rotations are bounded by `maxRotationsPerRequest` and\n  the pool length; the failure always surfaces.\n- **No durability.** The pool cursor is per-request, so a restart re-starts the\n  pool from the top while the *installed* reference survives in settings.\n  Rotation is a latency optimization, not a quota accountant.\n\n## Compatibility\n\ndsh `0.1.2-rc.1`, `0.1.3-alpha.2`, `0.1.5`, and `0.1.6`\n(`>=0.1.2-rc.1 <0.1.3 || >=0.1.3-alpha.2 <0.1.4 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-alpha.1 <0.2.0`).\nThe wrapped seam (`llm/stream`) and the per-stream-call credential resolution\nboth predate this range; every line above has had the full suite run against it.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}