opencode-nim-rotator 0.0.1
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/LICENSE +21 -0
- package/README.md +201 -0
- package/bin/opencode-nim-rotator.ts +10 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/storage.d.ts +18 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +129 -0
- package/dist/storage.js.map +1 -0
- package/dist/themes.d.ts +64 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +422 -0
- package/dist/themes.js.map +1 -0
- package/dist/tui/actions.d.ts +3 -0
- package/dist/tui/actions.d.ts.map +1 -0
- package/dist/tui/actions.js +68 -0
- package/dist/tui/actions.js.map +1 -0
- package/dist/tui/app.d.ts +2 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +141 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/screens.d.ts +10 -0
- package/dist/tui/screens.d.ts.map +1 -0
- package/dist/tui/screens.js +316 -0
- package/dist/tui/screens.js.map +1 -0
- package/dist/tui/state.d.ts +29 -0
- package/dist/tui/state.d.ts.map +1 -0
- package/dist/tui/state.js +47 -0
- package/dist/tui/state.js.map +1 -0
- package/dist/tui/types.d.ts +12 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +2 -0
- package/dist/tui/types.js.map +1 -0
- package/dist/tui/ui.d.ts +17 -0
- package/dist/tui/ui.d.ts.map +1 -0
- package/dist/tui/ui.js +77 -0
- package/dist/tui/ui.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
- package/scripts/postinstall.js +69 -0
- package/scripts/uninstall.js +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# opencode-nim-rotator
|
|
2
|
+
|
|
3
|
+
An [OpenCode](https://opencode.ai) plugin for managing and rotating multiple NVIDIA NIM API keys.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
The plugin uses OpenCode's `auth` hook with a custom `fetch` function to intercept every NVIDIA NIM API call and inject a rotated API key into the `Authorization` header. This is the same pattern used by the codex and github-copilot plugins in production.
|
|
8
|
+
|
|
9
|
+
**Key rotation happens per-request**: each LLM call uses the next key in the rotation. If a key returns 401/403, it automatically increments the failure count and retries with the next key.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g opencode-nim-rotator
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The postinstall script automatically adds the plugin to your `~/.config/opencode/opencode.json`.
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
### 1. Add API keys
|
|
22
|
+
|
|
23
|
+
Run the TUI manager:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun opencode-nim-rotator
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or manually — add at least one key via OpenCode's auth system:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
opencode /connect nvidia
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Select "Enter NVIDIA NIM API Key" and paste your key.
|
|
36
|
+
|
|
37
|
+
### 2. Add more keys via the TUI
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bun opencode-nim-rotator
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The TUI lets you:
|
|
44
|
+
|
|
45
|
+
- **Add** keys with a friendly name
|
|
46
|
+
- **Rename** keys
|
|
47
|
+
- **Delete** keys
|
|
48
|
+
- **Toggle** keys on/off
|
|
49
|
+
- **Reset** failure counts
|
|
50
|
+
- **Switch** rotation strategy (round-robin or least-failures)
|
|
51
|
+
|
|
52
|
+
### 3. Restart OpenCode
|
|
53
|
+
|
|
54
|
+
After adding keys, restart opencode. The plugin's `auth` loader will fire on startup and provide the custom `fetch` that rotates keys.
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
### Environment Variables
|
|
59
|
+
|
|
60
|
+
| Variable | Description | Default |
|
|
61
|
+
| -------------------------- | ----------------------------------- | ------------------------------------------ |
|
|
62
|
+
| `NIM_ROTATOR_STORE_PATH` | Path to key store JSON file | `~/.config/opencode/nim-rotator-keys.json` |
|
|
63
|
+
| `NIM_ROTATOR_MAX_FAILURES` | Max failures before disabling a key | `5` |
|
|
64
|
+
|
|
65
|
+
### opencode.json Options
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"plugin": [
|
|
70
|
+
[
|
|
71
|
+
"opencode-nim-rotator",
|
|
72
|
+
{
|
|
73
|
+
"rotationStrategy": "round-robin",
|
|
74
|
+
"storePath": "/custom/path/to/keys.json"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Rotation Strategies
|
|
82
|
+
|
|
83
|
+
- **`round-robin`** (default): Cycles through keys in order
|
|
84
|
+
- **`least-failures`**: Always uses the key with the fewest failures
|
|
85
|
+
|
|
86
|
+
## How Key Rotation Works
|
|
87
|
+
|
|
88
|
+
1. On startup, the `auth` hook `loader` registers a custom `fetch` for the `nvidia` provider
|
|
89
|
+
2. Every NVIDIA API call goes through this custom fetch
|
|
90
|
+
3. The fetch selects the next key based on the rotation strategy
|
|
91
|
+
4. The `Authorization: Bearer <key>` header is replaced with the rotated key
|
|
92
|
+
5. If the request returns 401/403, the failure count is incremented and the next key is tried
|
|
93
|
+
6. Keys that exceed `NIM_ROTATOR_MAX_FAILURES` are automatically skipped
|
|
94
|
+
7. Successful requests reset the key's failure count to 0
|
|
95
|
+
|
|
96
|
+
## Key Store Format
|
|
97
|
+
|
|
98
|
+
Keys are stored in `~/.config/opencode/nim-rotator-keys.json` with file mode `0600`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"keys": [
|
|
103
|
+
{
|
|
104
|
+
"id": "uuid",
|
|
105
|
+
"name": "work-key",
|
|
106
|
+
"key": "nvapi-...",
|
|
107
|
+
"createdAt": 1700000000000,
|
|
108
|
+
"lastUsedAt": 1700000100000,
|
|
109
|
+
"failureCount": 0,
|
|
110
|
+
"enabled": true
|
|
111
|
+
}
|
|
112
|
+
],
|
|
113
|
+
"currentIndex": 0,
|
|
114
|
+
"rotationStrategy": "round-robin",
|
|
115
|
+
"updatedAt": 1700000000000
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Themes
|
|
120
|
+
|
|
121
|
+
The TUI supports multiple color themes that match OpenCode's built-in themes. By default, the rotator **syncs with your `opencode.json` theme setting** — when you change your theme in OpenCode, the rotator picks it up automatically.
|
|
122
|
+
|
|
123
|
+
### Available Themes
|
|
124
|
+
|
|
125
|
+
| ID | Name |
|
|
126
|
+
| ------------ | ------------------ |
|
|
127
|
+
| `opencode` | OpenCode (default) |
|
|
128
|
+
| `catppuccin` | Catppuccin Mocha |
|
|
129
|
+
| `dracula` | Dracula |
|
|
130
|
+
| `gruvbox` | Gruvbox |
|
|
131
|
+
| `kanagawa` | Kanagawa |
|
|
132
|
+
| `nord` | Nord |
|
|
133
|
+
| `one-dark` | One Dark |
|
|
134
|
+
| `rosepine` | Rose Pine |
|
|
135
|
+
| `solarized` | Solarized |
|
|
136
|
+
| `tokyonight` | Tokyonight |
|
|
137
|
+
|
|
138
|
+
### Setting a Theme
|
|
139
|
+
|
|
140
|
+
From the TUI main menu, select **Theme** to pick a theme or switch back to syncing with `opencode.json`.
|
|
141
|
+
|
|
142
|
+
To override via `opencode.json`:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"theme": "dracula"
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
To override independently (saved in the key store):
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"theme": "kanagawa",
|
|
155
|
+
"keys": [...]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Set the store `theme` field to `""` or remove it to revert to syncing with `opencode.json`.
|
|
160
|
+
|
|
161
|
+
## TUI
|
|
162
|
+
|
|
163
|
+
The TUI is built with [OpenTUI](https://opentui.com) and provides a menu-driven interface:
|
|
164
|
+
|
|
165
|
+
- Main menu with Add, Manage, Strategy, and Theme options
|
|
166
|
+
- Key selector showing name, masked key, failure count, last used
|
|
167
|
+
- Key actions: toggle, rename, delete
|
|
168
|
+
- Add key flow: enter name, then enter key
|
|
169
|
+
- Theme selector with sync-to-opencode option
|
|
170
|
+
|
|
171
|
+
## Development
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Install dependencies
|
|
175
|
+
bun install
|
|
176
|
+
|
|
177
|
+
# Run TUI locally
|
|
178
|
+
bun run tui
|
|
179
|
+
|
|
180
|
+
# Build TypeScript
|
|
181
|
+
bun run build
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Uninstall
|
|
185
|
+
|
|
186
|
+
To remove the plugin and clean up all associated data:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
npm uninstall -g opencode-nim-rotator
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The uninstaller will automatically:
|
|
193
|
+
|
|
194
|
+
1. Remove `opencode-nim-rotator` from your `~/.config/opencode/opencode.json` plugin list
|
|
195
|
+
2. Delete your key store file at `~/.config/opencode/nim-rotator-keys.json`
|
|
196
|
+
|
|
197
|
+
**Note:** This will permanently delete all stored API keys, so back them up first if needed. After uninstalling, restart opencode to apply the changes.
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { createCliRenderer } from "@opentui/core";
|
|
4
|
+
import { state } from "../src/tui/state.js";
|
|
5
|
+
import { initApp } from "../src/tui/app.js";
|
|
6
|
+
|
|
7
|
+
const renderer = await createCliRenderer({ exitOnCtrlC: false });
|
|
8
|
+
|
|
9
|
+
state.renderer = renderer;
|
|
10
|
+
initApp();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAsB,MAAM,qBAAqB,CAAC;AAkCtE,eAAO,MAAM,mBAAmB,EAAE,MAyFjC,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { loadStore, saveStore, addKey, getNextKey, recordFailure, getActiveKeys, } from "./storage.js";
|
|
2
|
+
const PROVIDER_ID = "nvidia";
|
|
3
|
+
const NIM_BASE_URL = "https://integrate.api.nvidia.com";
|
|
4
|
+
const VALID_STRATEGIES = ["round-robin", "least-failures"];
|
|
5
|
+
function isValidStrategy(val) {
|
|
6
|
+
return typeof val === "string" && VALID_STRATEGIES.includes(val);
|
|
7
|
+
}
|
|
8
|
+
function isAuthError(obj) {
|
|
9
|
+
if (typeof obj === "object" && obj !== null) {
|
|
10
|
+
const code = obj.code;
|
|
11
|
+
const status = obj.status;
|
|
12
|
+
if (code === 401 || code === 403 || status === 401 || status === 403)
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const msg = String(obj).toLowerCase();
|
|
16
|
+
return (msg.includes("401") || msg.includes("403") || msg.includes("unauthorized"));
|
|
17
|
+
}
|
|
18
|
+
export const NvidiaNimKeyRotator = async (input, options) => {
|
|
19
|
+
const config = {
|
|
20
|
+
storePath: options?.storePath,
|
|
21
|
+
rotationStrategy: isValidStrategy(options?.rotationStrategy)
|
|
22
|
+
? options.rotationStrategy
|
|
23
|
+
: "round-robin",
|
|
24
|
+
};
|
|
25
|
+
const store = loadStore(config);
|
|
26
|
+
const activeKeys = getActiveKeys(store, config);
|
|
27
|
+
// Seed an env key if the store is empty
|
|
28
|
+
if (activeKeys.length === 0) {
|
|
29
|
+
const envKey = process.env.NVIDIA_API_KEY;
|
|
30
|
+
if (envKey) {
|
|
31
|
+
const existing = store.keys.find((k) => k.name === "env-default");
|
|
32
|
+
if (!existing) {
|
|
33
|
+
addKey(store, "env-default", envKey);
|
|
34
|
+
saveStore(store, config);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const hooks = {
|
|
39
|
+
auth: {
|
|
40
|
+
provider: PROVIDER_ID,
|
|
41
|
+
// We deliberately omit auth.loader. The fetch we used
|
|
42
|
+
// to return was never called by opencode; it only uses
|
|
43
|
+
// the apiKey string from the authorize step. Rotation is
|
|
44
|
+
// handled in chat.headers instead.
|
|
45
|
+
methods: [
|
|
46
|
+
{
|
|
47
|
+
type: "api",
|
|
48
|
+
label: "Enter NVIDIA NIM API Key",
|
|
49
|
+
async authorize(inputs) {
|
|
50
|
+
const key = inputs?.["apiKey"];
|
|
51
|
+
if (!key)
|
|
52
|
+
return { type: "failed" };
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch(`${NIM_BASE_URL}/v1/models`, {
|
|
55
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
56
|
+
});
|
|
57
|
+
if (!res.ok)
|
|
58
|
+
return { type: "failed" };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { type: "failed" };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
type: "success",
|
|
65
|
+
key,
|
|
66
|
+
provider: PROVIDER_ID,
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
// Rotate the API key on every outgoing request by mutating the
|
|
73
|
+
// Authorization header. This is the hook that actually runs
|
|
74
|
+
// before each LLM call, unlike auth.loader which only runs once.
|
|
75
|
+
"chat.headers": async (_input, _output) => {
|
|
76
|
+
const next = getNextKey(store, config);
|
|
77
|
+
if (next) {
|
|
78
|
+
_output.headers["Authorization"] = `Bearer ${next.key.key}`;
|
|
79
|
+
saveStore(store, config);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"shell.env": async (_input, output) => {
|
|
83
|
+
if (output.env.NVIDIA_API_KEY !== undefined) {
|
|
84
|
+
const next = getNextKey(store, config);
|
|
85
|
+
if (next) {
|
|
86
|
+
output.env.NVIDIA_API_KEY = next.key.key;
|
|
87
|
+
saveStore(store, config);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
event: async ({ event }) => {
|
|
92
|
+
if (event.type === "session.error" && isAuthError(event.error)) {
|
|
93
|
+
if (store.lastUsedKeyId) {
|
|
94
|
+
recordFailure(store, store.lastUsedKeyId);
|
|
95
|
+
saveStore(store, config);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
return hooks;
|
|
101
|
+
};
|
|
102
|
+
export default NvidiaNimKeyRotator;
|
|
103
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ApiKeyEntry, KeyStore, KeyStoreConfig } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_MAX_FAILURES = 5;
|
|
3
|
+
export declare function resolveStorePath(config?: KeyStoreConfig): string;
|
|
4
|
+
export declare function loadStore(config?: KeyStoreConfig): KeyStore;
|
|
5
|
+
export declare function saveStore(store: KeyStore, config?: KeyStoreConfig): void;
|
|
6
|
+
export declare function addKey(store: KeyStore, name: string, key: string): void;
|
|
7
|
+
export declare function removeKey(store: KeyStore, id: string): void;
|
|
8
|
+
export declare function renameKey(store: KeyStore, id: string, newName: string): void;
|
|
9
|
+
export declare function toggleKey(store: KeyStore, id: string, enabled?: boolean): void;
|
|
10
|
+
export declare function getMaxFailures(config?: KeyStoreConfig): number;
|
|
11
|
+
export declare function getActiveKeys(store: KeyStore, config?: KeyStoreConfig): ApiKeyEntry[];
|
|
12
|
+
export declare function getNextKey(store: KeyStore, config?: KeyStoreConfig): {
|
|
13
|
+
key: ApiKeyEntry;
|
|
14
|
+
index: number;
|
|
15
|
+
} | null;
|
|
16
|
+
export declare function recordFailure(store: KeyStore, keyId: string): void;
|
|
17
|
+
export declare function resetFailures(store: KeyStore, keyId?: string): void;
|
|
18
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAQxE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAWtC,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAMhE;AAED,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,QAAQ,CAwB3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAUxE;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAUvE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAO3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,QAAQ,EACf,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,GAChB,IAAI,CAGN;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAK9D;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,MAAM,CAAC,EAAE,cAAc,GACtB,WAAW,EAAE,CAGf;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,QAAQ,EACf,MAAM,CAAC,EAAE,cAAc,GACtB;IAAE,GAAG,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAyB5C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAGlE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAOnE"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
const DEFAULT_STORE_PATH = join(homedir(), ".config", "opencode", "nim-rotator-keys.json");
|
|
5
|
+
export const DEFAULT_MAX_FAILURES = 5;
|
|
6
|
+
function getDefaultStore() {
|
|
7
|
+
return {
|
|
8
|
+
keys: [],
|
|
9
|
+
currentIndex: 0,
|
|
10
|
+
rotationStrategy: "round-robin",
|
|
11
|
+
updatedAt: Date.now(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function resolveStorePath(config) {
|
|
15
|
+
return (config?.storePath ??
|
|
16
|
+
process.env.NIM_ROTATOR_STORE_PATH ??
|
|
17
|
+
DEFAULT_STORE_PATH);
|
|
18
|
+
}
|
|
19
|
+
export function loadStore(config) {
|
|
20
|
+
const storePath = resolveStorePath(config);
|
|
21
|
+
try {
|
|
22
|
+
if (existsSync(storePath)) {
|
|
23
|
+
const raw = readFileSync(storePath, "utf-8");
|
|
24
|
+
const data = JSON.parse(raw);
|
|
25
|
+
if (!data.keys || !Array.isArray(data.keys)) {
|
|
26
|
+
console.warn(`[nim-rotator] Store at "${storePath}" has invalid keys format, using defaults`);
|
|
27
|
+
return getDefaultStore();
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
...getDefaultStore(),
|
|
31
|
+
...data,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error(`[nim-rotator] Failed to load store at "${storePath}":`, err);
|
|
37
|
+
console.warn("[nim-rotator] Starting with a fresh store. Your existing keys are preserved on disk.");
|
|
38
|
+
}
|
|
39
|
+
return getDefaultStore();
|
|
40
|
+
}
|
|
41
|
+
export function saveStore(store, config) {
|
|
42
|
+
const storePath = resolveStorePath(config);
|
|
43
|
+
const dir = dirname(storePath);
|
|
44
|
+
if (!existsSync(dir)) {
|
|
45
|
+
mkdirSync(dir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
store.updatedAt = Date.now();
|
|
48
|
+
writeFileSync(storePath, JSON.stringify(store, null, 2) + "\n", {
|
|
49
|
+
mode: 0o600,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export function addKey(store, name, key) {
|
|
53
|
+
const entry = {
|
|
54
|
+
id: crypto.randomUUID(),
|
|
55
|
+
name,
|
|
56
|
+
key,
|
|
57
|
+
createdAt: Date.now(),
|
|
58
|
+
failureCount: 0,
|
|
59
|
+
enabled: true,
|
|
60
|
+
};
|
|
61
|
+
store.keys.push(entry);
|
|
62
|
+
}
|
|
63
|
+
export function removeKey(store, id) {
|
|
64
|
+
const index = store.keys.findIndex((k) => k.id === id);
|
|
65
|
+
if (index === -1)
|
|
66
|
+
return;
|
|
67
|
+
store.keys.splice(index, 1);
|
|
68
|
+
if (store.currentIndex >= store.keys.length) {
|
|
69
|
+
store.currentIndex = 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function renameKey(store, id, newName) {
|
|
73
|
+
const entry = store.keys.find((k) => k.id === id);
|
|
74
|
+
if (entry)
|
|
75
|
+
entry.name = newName;
|
|
76
|
+
}
|
|
77
|
+
export function toggleKey(store, id, enabled) {
|
|
78
|
+
const entry = store.keys.find((k) => k.id === id);
|
|
79
|
+
if (entry)
|
|
80
|
+
entry.enabled = enabled ?? !entry.enabled;
|
|
81
|
+
}
|
|
82
|
+
export function getMaxFailures(config) {
|
|
83
|
+
return (config?.maxFailuresBeforeDisable ??
|
|
84
|
+
(Number(process.env.NIM_ROTATOR_MAX_FAILURES) || DEFAULT_MAX_FAILURES));
|
|
85
|
+
}
|
|
86
|
+
export function getActiveKeys(store, config) {
|
|
87
|
+
const maxFailures = getMaxFailures(config);
|
|
88
|
+
return store.keys.filter((k) => k.enabled && k.failureCount < maxFailures);
|
|
89
|
+
}
|
|
90
|
+
export function getNextKey(store, config) {
|
|
91
|
+
const active = getActiveKeys(store, config);
|
|
92
|
+
if (active.length === 0)
|
|
93
|
+
return null;
|
|
94
|
+
const strategy = config?.rotationStrategy ?? store.rotationStrategy ?? "round-robin";
|
|
95
|
+
if (strategy === "least-failures") {
|
|
96
|
+
const sorted = [...active].sort((a, b) => a.failureCount - b.failureCount);
|
|
97
|
+
const best = sorted[0];
|
|
98
|
+
const idx = store.keys.indexOf(best);
|
|
99
|
+
store.currentIndex = idx;
|
|
100
|
+
store.lastUsedKeyId = best.id;
|
|
101
|
+
best.lastUsedAt = Date.now();
|
|
102
|
+
return { key: best, index: idx };
|
|
103
|
+
}
|
|
104
|
+
// round-robin
|
|
105
|
+
let idx = store.currentIndex % active.length;
|
|
106
|
+
const selected = active[idx];
|
|
107
|
+
const realIdx = store.keys.indexOf(selected);
|
|
108
|
+
store.currentIndex = (idx + 1) % active.length;
|
|
109
|
+
store.lastUsedKeyId = selected.id;
|
|
110
|
+
selected.lastUsedAt = Date.now();
|
|
111
|
+
return { key: selected, index: realIdx };
|
|
112
|
+
}
|
|
113
|
+
export function recordFailure(store, keyId) {
|
|
114
|
+
const entry = store.keys.find((k) => k.id === keyId);
|
|
115
|
+
if (entry)
|
|
116
|
+
entry.failureCount++;
|
|
117
|
+
}
|
|
118
|
+
export function resetFailures(store, keyId) {
|
|
119
|
+
if (keyId) {
|
|
120
|
+
const entry = store.keys.find((k) => k.id === keyId);
|
|
121
|
+
if (entry)
|
|
122
|
+
entry.failureCount = 0;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
for (const k of store.keys)
|
|
126
|
+
k.failureCount = 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
package/dist/themes.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface SelectThemeColors {
|
|
2
|
+
backgroundColor: string;
|
|
3
|
+
focusedBackgroundColor: string;
|
|
4
|
+
focusedTextColor: string;
|
|
5
|
+
selectedBackgroundColor: string;
|
|
6
|
+
selectedTextColor: string;
|
|
7
|
+
textColor: string;
|
|
8
|
+
descriptionColor: string;
|
|
9
|
+
selectedDescriptionColor: string;
|
|
10
|
+
}
|
|
11
|
+
export interface InputThemeColors {
|
|
12
|
+
backgroundColor: string;
|
|
13
|
+
focusedBackgroundColor: string;
|
|
14
|
+
textColor: string;
|
|
15
|
+
cursorColor: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function selectColors(theme: RotatorTheme): SelectThemeColors;
|
|
18
|
+
export declare function inputColors(theme: RotatorTheme): InputThemeColors;
|
|
19
|
+
export declare function dangerSelectColors(theme: RotatorTheme): SelectThemeColors;
|
|
20
|
+
export declare function applySelectColors(target: {
|
|
21
|
+
backgroundColor: string;
|
|
22
|
+
focusedBackgroundColor: string;
|
|
23
|
+
focusedTextColor: string;
|
|
24
|
+
selectedBackgroundColor: string;
|
|
25
|
+
selectedTextColor: string;
|
|
26
|
+
textColor: string;
|
|
27
|
+
descriptionColor: string;
|
|
28
|
+
selectedDescriptionColor: string;
|
|
29
|
+
}, colors: SelectThemeColors): void;
|
|
30
|
+
export interface RotatorTheme {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
background: string;
|
|
34
|
+
backgroundPanel: string;
|
|
35
|
+
backgroundElement: string;
|
|
36
|
+
text: string;
|
|
37
|
+
textMuted: string;
|
|
38
|
+
primary: string;
|
|
39
|
+
primaryMuted: string;
|
|
40
|
+
accent: string;
|
|
41
|
+
error: string;
|
|
42
|
+
errorBg: string;
|
|
43
|
+
success: string;
|
|
44
|
+
warning: string;
|
|
45
|
+
selectedBg: string;
|
|
46
|
+
selectedText: string;
|
|
47
|
+
border: string;
|
|
48
|
+
borderActive: string;
|
|
49
|
+
inputBg: string;
|
|
50
|
+
inputFocusedBg: string;
|
|
51
|
+
cursor: string;
|
|
52
|
+
description: string;
|
|
53
|
+
selectedDescription: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function getTheme(id: string): RotatorTheme;
|
|
56
|
+
export declare function listThemes(): RotatorTheme[];
|
|
57
|
+
export declare function getThemeIdFromOpenCodeConfig(): string | null;
|
|
58
|
+
export declare function getResolvedTheme(): RotatorTheme;
|
|
59
|
+
export declare function getThemeOverride(): string | null;
|
|
60
|
+
export declare function saveThemeOverride(themeId: string): void;
|
|
61
|
+
export declare function setPreviewTheme(themeId: string | null): void;
|
|
62
|
+
export declare function getPreviewTheme(): string | null;
|
|
63
|
+
export declare function getActiveTheme(): RotatorTheme;
|
|
64
|
+
//# sourceMappingURL=themes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../src/themes.ts"],"names":[],"mappings":"AA6EA,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAWnE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,gBAAgB,CAOjE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAWzE;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE;IACN,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wBAAwB,EAAE,MAAM,CAAC;CAClC,EACD,MAAM,EAAE,iBAAiB,GACxB,IAAI,CASN;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AA+PD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAEjD;AAED,wBAAgB,UAAU,IAAI,YAAY,EAAE,CAE3C;AAED,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CAG5D;AAED,wBAAgB,gBAAgB,IAAI,YAAY,CAM/C;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAShD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAmBvD;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE5D;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,cAAc,IAAI,YAAY,CAS7C"}
|