opencode-kimi-rotator 1.0.0

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/bin/cli.js ADDED
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { KimiAccountManager } from '../dist/accounts.js';
4
+
5
+ const args = process.argv.slice(2);
6
+ const command = args[0];
7
+
8
+ async function main() {
9
+ const manager = new KimiAccountManager();
10
+ await manager.init();
11
+
12
+ try {
13
+ switch (command) {
14
+ case 'add-key':
15
+ await addKey(manager, args.slice(1));
16
+ break;
17
+ case 'list-keys':
18
+ await listKeys(manager);
19
+ break;
20
+ case 'remove-key':
21
+ await removeKey(manager, args.slice(1));
22
+ break;
23
+ case 'rotate':
24
+ await rotate(manager);
25
+ break;
26
+ case 'use-key':
27
+ await useKey(manager, args.slice(1));
28
+ break;
29
+ case 'set-strategy':
30
+ await setStrategy(manager, args.slice(1));
31
+ break;
32
+ case 'help':
33
+ default:
34
+ showHelp();
35
+ break;
36
+ }
37
+ } catch (error) {
38
+ console.error('Error:', error instanceof Error ? error.message : error);
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ const API_KEY_PATTERN = /^sk-kimi-[a-zA-Z0-9_-]+$/;
44
+ const API_KEY_MIN_LENGTH = 20;
45
+ const API_KEY_MAX_LENGTH = 128;
46
+ const MAX_ACCOUNTS = 100;
47
+ const MAX_ACCOUNT_NAME_LENGTH = 64;
48
+
49
+ function validateApiKey(key) {
50
+ if (!key || typeof key !== 'string') {
51
+ return { valid: false, error: 'API key is required' };
52
+ }
53
+
54
+ if (!key.startsWith('sk-kimi-')) {
55
+ return { valid: false, error: 'API key must start with "sk-kimi-"' };
56
+ }
57
+
58
+ if (key.length < API_KEY_MIN_LENGTH) {
59
+ return { valid: false, error: `API key must be at least ${API_KEY_MIN_LENGTH} characters` };
60
+ }
61
+
62
+ if (key.length > API_KEY_MAX_LENGTH) {
63
+ return { valid: false, error: `API key must not exceed ${API_KEY_MAX_LENGTH} characters` };
64
+ }
65
+
66
+ if (!API_KEY_PATTERN.test(key)) {
67
+ return { valid: false, error: 'API key contains invalid characters' };
68
+ }
69
+
70
+ return { valid: true };
71
+ }
72
+
73
+ function sanitizeAccountName(name) {
74
+ if (!name) return null;
75
+
76
+ const trimmed = name.trim();
77
+
78
+ if (trimmed.length === 0) return null;
79
+
80
+ if (trimmed.length > MAX_ACCOUNT_NAME_LENGTH) {
81
+ return trimmed.substring(0, MAX_ACCOUNT_NAME_LENGTH);
82
+ }
83
+
84
+ return trimmed.replace(/[<>\"']/g, '');
85
+ }
86
+
87
+ async function addKey(manager, args) {
88
+ const key = args[0];
89
+ const rawName = args[1];
90
+
91
+ if (!key) {
92
+ console.error('Usage: opencode-kimi add-key <api-key> [name]');
93
+ process.exit(1);
94
+ }
95
+
96
+ const validation = validateApiKey(key);
97
+ if (!validation.valid) {
98
+ console.error(`Error: ${validation.error}`);
99
+ process.exit(1);
100
+ }
101
+
102
+ const accounts = await manager.listKeys();
103
+ if (accounts.length >= MAX_ACCOUNTS) {
104
+ console.error(`Error: Maximum number of accounts (${MAX_ACCOUNTS}) reached`);
105
+ process.exit(1);
106
+ }
107
+
108
+ const name = sanitizeAccountName(rawName);
109
+
110
+ try {
111
+ const account = await manager.addKey(key, name);
112
+ const updatedAccounts = await manager.listKeys();
113
+ console.log(`✓ Added Kimi API key: ${account.name}`);
114
+ console.log(` Index: ${updatedAccounts.length - 1}`);
115
+ } catch (error) {
116
+ console.error(`Error: ${error.message}`);
117
+ process.exit(1);
118
+ }
119
+ }
120
+
121
+ async function listKeys(manager) {
122
+ const accounts = await manager.listKeys();
123
+
124
+ if (accounts.length === 0) {
125
+ console.log('No Kimi API keys configured.');
126
+ console.log('Run: opencode-kimi add-key <your-api-key>');
127
+ return;
128
+ }
129
+
130
+ const { KimiStorage } = await import('../dist/storage.js');
131
+ const storage = new KimiStorage();
132
+ const fullConfig = await storage.loadConfig();
133
+ const activeIndex = fullConfig.activeIndex;
134
+
135
+ console.log(`\nKimi API Keys (${accounts.length} total, strategy: ${fullConfig.rotationStrategy}):\n`);
136
+
137
+ accounts.forEach((account, index) => {
138
+ const isActive = index === activeIndex;
139
+ const isRateLimited = account.rateLimitResetTime > Date.now();
140
+ const healthBar = getHealthBar(account.healthScore);
141
+
142
+ console.log(`${isActive ? '●' : '○'} [${index}] ${account.name}`);
143
+ console.log(` Key: ${maskKey(account.key)}`);
144
+ console.log(` Health: ${healthBar} ${account.healthScore}%`);
145
+ console.log(` Requests: ${account.successfulRequests}/${account.totalRequests} successful`);
146
+
147
+ if (isRateLimited) {
148
+ const waitTime = Math.ceil((account.rateLimitResetTime - Date.now()) / 1000 / 60);
149
+ console.log(` Status: ⏱️ Rate limited (~${waitTime}m remaining)`);
150
+ } else if (account.consecutiveFailures > 0) {
151
+ console.log(` Status: ⚠️ ${account.consecutiveFailures} consecutive failures`);
152
+ } else {
153
+ console.log(` Status: ✓ Active`);
154
+ }
155
+
156
+ console.log();
157
+ });
158
+ }
159
+
160
+ async function removeKey(manager, args) {
161
+ const index = parseInt(args[0], 10);
162
+
163
+ if (isNaN(index)) {
164
+ console.error('Usage: opencode-kimi remove-key <index>');
165
+ process.exit(1);
166
+ }
167
+
168
+ const accounts = await manager.listKeys();
169
+ if (index < 0 || index >= accounts.length) {
170
+ console.error(`Invalid index. Valid range: 0-${accounts.length - 1}`);
171
+ process.exit(1);
172
+ }
173
+
174
+ await manager.removeKey(index);
175
+ console.log(`✓ Removed Kimi API key at index ${index}`);
176
+ }
177
+
178
+ async function rotate(manager) {
179
+ const result = await manager.rotateToNext();
180
+
181
+ if (!result) {
182
+ console.log('No Kimi API keys configured.');
183
+ return;
184
+ }
185
+
186
+ console.log(`✓ Rotated to next Kimi API key`);
187
+ console.log(` Now using: ${result.account.name} (index ${result.index})`);
188
+ console.log(` Reason: ${result.reason}`);
189
+ }
190
+
191
+ async function useKey(manager, args) {
192
+ const index = parseInt(args[0], 10);
193
+
194
+ if (isNaN(index)) {
195
+ console.error('Usage: opencode-kimi use-key <index>');
196
+ process.exit(1);
197
+ }
198
+
199
+ const accounts = await manager.listKeys();
200
+ if (index < 0 || index >= accounts.length) {
201
+ console.error(`Invalid index. Valid range: 0-${accounts.length - 1}`);
202
+ process.exit(1);
203
+ }
204
+
205
+ await manager.setActiveIndex(index);
206
+ console.log(`✓ Now using: ${accounts[index].name} (index ${index})`);
207
+ }
208
+
209
+ async function setStrategy(manager, args) {
210
+ const strategy = args[0];
211
+
212
+ if (!['round-robin', 'health-based', 'sticky'].includes(strategy)) {
213
+ console.error('Usage: opencode-kimi set-strategy <round-robin|health-based|sticky>');
214
+ console.error('');
215
+ console.error('Strategies:');
216
+ console.error(' round-robin - Cycle through keys sequentially');
217
+ console.error(' health-based - Use health scores + LRU (default)');
218
+ console.error(' sticky - Stay on one key until rate limited');
219
+ process.exit(1);
220
+ }
221
+
222
+ await manager.setRotationStrategy(strategy);
223
+ console.log(`✓ Rotation strategy set to: ${strategy}`);
224
+ }
225
+
226
+ function showHelp() {
227
+ console.log('Kimi API Key Rotator for OpenCode');
228
+ console.log('');
229
+ console.log('Usage: opencode-kimi <command> [options]');
230
+ console.log('');
231
+ console.log('Commands:');
232
+ console.log(' add-key <key> [name] Add a new Kimi API key');
233
+ console.log(' list-keys List all configured keys');
234
+ console.log(' remove-key <index> Remove a key by index');
235
+ console.log(' rotate Manually rotate to next key');
236
+ console.log(' use-key <index> Switch to a specific key');
237
+ console.log(' set-strategy <strategy> Set rotation strategy');
238
+ console.log(' help Show this help message');
239
+ console.log('');
240
+ console.log('Strategies:');
241
+ console.log(' round-robin - Cycle through keys sequentially');
242
+ console.log(' health-based - Use health scores + LRU (default)');
243
+ console.log(' sticky - Stay on one key until rate limited');
244
+ console.log('');
245
+ console.log('Examples:');
246
+ console.log(' opencode-kimi add-key sk-kimi-xxx MyAccount');
247
+ console.log(' opencode-kimi list-keys');
248
+ console.log(' opencode-kimi rotate');
249
+ console.log(' opencode-kimi use-key 1');
250
+ }
251
+
252
+ function maskKey(key) {
253
+ if (key.length <= 12) return '***';
254
+ return key.slice(0, 6) + '...' + key.slice(-6);
255
+ }
256
+
257
+ function getHealthBar(score) {
258
+ const filled = Math.round(score / 10);
259
+ const empty = 10 - filled;
260
+ const bar = '█'.repeat(filled) + '░'.repeat(empty);
261
+ return bar;
262
+ }
263
+
264
+ main();
@@ -0,0 +1,36 @@
1
+ import { KimiAccount } from './types.js';
2
+ type RotationStrategy = 'round-robin' | 'health-based' | 'sticky';
3
+ interface RotationResult {
4
+ account: KimiAccount;
5
+ index: number;
6
+ reason: string;
7
+ }
8
+ export declare class KimiAccountManager {
9
+ private storage;
10
+ private minHealthScore;
11
+ private stickyBonus;
12
+ constructor();
13
+ init(): Promise<void>;
14
+ getNextAccount(forceRotation?: boolean): Promise<RotationResult | null>;
15
+ markAccountUsed(index: number): Promise<void>;
16
+ markAccountSuccess(index: number): Promise<void>;
17
+ markAccountRateLimited(index: number, retryAfterMs: number): Promise<void>;
18
+ markAccountFailure(index: number): Promise<void>;
19
+ getActiveKey(): Promise<string | null>;
20
+ setRotationStrategy(strategy: RotationStrategy): Promise<void>;
21
+ addKey(key: string, name?: string): Promise<KimiAccount>;
22
+ removeKey(index: number): Promise<void>;
23
+ listKeys(): Promise<KimiAccount[]>;
24
+ rotateToNext(): Promise<RotationResult | null>;
25
+ setActiveIndex(index: number): Promise<void>;
26
+ private roundRobinRotation;
27
+ private stickyRotation;
28
+ private healthBasedRotation;
29
+ private calculateAccountScore;
30
+ private getAvailableIndices;
31
+ private getSoonestAvailableIndex;
32
+ private isRateLimited;
33
+ private getAccount;
34
+ }
35
+ export {};
36
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAsB,MAAM,YAAY,CAAC;AAE7D,KAAK,gBAAgB,GAAG,aAAa,GAAG,cAAc,GAAG,QAAQ,CAAC;AAElE,UAAU,cAAc;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,WAAW,CAAM;;IAMnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,cAAc,CAAC,aAAa,UAAQ,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwBrE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7C,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1E,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhD,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKtC,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIxD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,QAAQ,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAIlC,YAAY,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAI9C,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIpC,kBAAkB;YAsBlB,cAAc;YAoCd,mBAAmB;IAgDjC,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,aAAa;YAIP,UAAU;CAOzB"}
@@ -0,0 +1,205 @@
1
+ import { KimiStorage } from './storage.js';
2
+ export class KimiAccountManager {
3
+ storage;
4
+ minHealthScore = 30;
5
+ stickyBonus = 50;
6
+ constructor() {
7
+ this.storage = new KimiStorage();
8
+ }
9
+ async init() {
10
+ await this.storage.init();
11
+ }
12
+ async getNextAccount(forceRotation = false) {
13
+ const config = await this.storage.loadConfig();
14
+ if (config.accounts.length === 0) {
15
+ return null;
16
+ }
17
+ if (config.accounts.length === 1) {
18
+ const account = config.accounts[0];
19
+ await this.markAccountUsed(0);
20
+ return { account, index: 0, reason: 'single-account' };
21
+ }
22
+ switch (config.rotationStrategy) {
23
+ case 'round-robin':
24
+ return this.roundRobinRotation(config);
25
+ case 'sticky':
26
+ return this.stickyRotation(config, forceRotation);
27
+ case 'health-based':
28
+ default:
29
+ return this.healthBasedRotation(config, forceRotation);
30
+ }
31
+ }
32
+ async markAccountUsed(index) {
33
+ await this.storage.updateAccount(index, {
34
+ lastUsed: Date.now(),
35
+ totalRequests: (await this.getAccount(index)).totalRequests + 1,
36
+ });
37
+ }
38
+ async markAccountSuccess(index) {
39
+ const account = await this.getAccount(index);
40
+ const newHealthScore = Math.min(100, account.healthScore + 2);
41
+ await this.storage.updateAccount(index, {
42
+ healthScore: newHealthScore,
43
+ successfulRequests: account.successfulRequests + 1,
44
+ consecutiveFailures: 0,
45
+ });
46
+ }
47
+ async markAccountRateLimited(index, retryAfterMs) {
48
+ const account = await this.getAccount(index);
49
+ const newHealthScore = Math.max(0, account.healthScore - 15);
50
+ await this.storage.updateAccount(index, {
51
+ healthScore: newHealthScore,
52
+ rateLimitResetTime: Date.now() + retryAfterMs,
53
+ consecutiveFailures: account.consecutiveFailures + 1,
54
+ });
55
+ }
56
+ async markAccountFailure(index) {
57
+ const account = await this.getAccount(index);
58
+ const newHealthScore = Math.max(0, account.healthScore - 20);
59
+ await this.storage.updateAccount(index, {
60
+ healthScore: newHealthScore,
61
+ consecutiveFailures: account.consecutiveFailures + 1,
62
+ });
63
+ }
64
+ async getActiveKey() {
65
+ const account = await this.getNextAccount();
66
+ return account?.account.key ?? null;
67
+ }
68
+ async setRotationStrategy(strategy) {
69
+ const config = await this.storage.loadConfig();
70
+ config.rotationStrategy = strategy;
71
+ await this.storage.saveConfig(config);
72
+ }
73
+ async addKey(key, name) {
74
+ return this.storage.addAccount(key, name);
75
+ }
76
+ async removeKey(index) {
77
+ return this.storage.removeAccount(index);
78
+ }
79
+ async listKeys() {
80
+ return this.storage.listAccounts();
81
+ }
82
+ async rotateToNext() {
83
+ return this.getNextAccount(true);
84
+ }
85
+ async setActiveIndex(index) {
86
+ return this.storage.setActiveIndex(index);
87
+ }
88
+ async roundRobinRotation(config) {
89
+ const availableIndices = this.getAvailableIndices(config);
90
+ if (availableIndices.length === 0) {
91
+ const soonestIndex = this.getSoonestAvailableIndex(config);
92
+ const account = config.accounts[soonestIndex];
93
+ return { account, index: soonestIndex, reason: 'all-rate-limited' };
94
+ }
95
+ const currentIndex = config.activeIndex;
96
+ let nextIndex = availableIndices.find(idx => idx > currentIndex) ?? availableIndices[0];
97
+ await this.storage.setActiveIndex(nextIndex);
98
+ await this.markAccountUsed(nextIndex);
99
+ return {
100
+ account: config.accounts[nextIndex],
101
+ index: nextIndex,
102
+ reason: 'round-robin',
103
+ };
104
+ }
105
+ async stickyRotation(config, forceRotation) {
106
+ const currentIndex = config.activeIndex;
107
+ const currentAccount = config.accounts[currentIndex];
108
+ if (!forceRotation && !this.isRateLimited(currentAccount)) {
109
+ await this.markAccountUsed(currentIndex);
110
+ return {
111
+ account: currentAccount,
112
+ index: currentIndex,
113
+ reason: 'sticky',
114
+ };
115
+ }
116
+ const availableIndices = this.getAvailableIndices(config).filter(idx => idx !== currentIndex);
117
+ if (availableIndices.length === 0) {
118
+ const soonestIndex = this.getSoonestAvailableIndex(config);
119
+ const account = config.accounts[soonestIndex];
120
+ await this.storage.setActiveIndex(soonestIndex);
121
+ return { account, index: soonestIndex, reason: 'all-rate-limited' };
122
+ }
123
+ const nextIndex = availableIndices[0];
124
+ await this.storage.setActiveIndex(nextIndex);
125
+ await this.markAccountUsed(nextIndex);
126
+ return {
127
+ account: config.accounts[nextIndex],
128
+ index: nextIndex,
129
+ reason: 'sticky-rotation',
130
+ };
131
+ }
132
+ async healthBasedRotation(config, forceRotation) {
133
+ const currentIndex = config.activeIndex;
134
+ const currentAccount = config.accounts[currentIndex];
135
+ if (!forceRotation && !this.isRateLimited(currentAccount) && currentAccount.healthScore > this.minHealthScore) {
136
+ await this.markAccountUsed(currentIndex);
137
+ return {
138
+ account: currentAccount,
139
+ index: currentIndex,
140
+ reason: 'health-sticky',
141
+ };
142
+ }
143
+ const availableIndices = this.getAvailableIndices(config);
144
+ if (availableIndices.length === 0) {
145
+ const soonestIndex = this.getSoonestAvailableIndex(config);
146
+ const account = config.accounts[soonestIndex];
147
+ await this.storage.setActiveIndex(soonestIndex);
148
+ return { account, index: soonestIndex, reason: 'all-rate-limited' };
149
+ }
150
+ let bestIndex = availableIndices[0];
151
+ let bestScore = this.calculateAccountScore(config.accounts[bestIndex], bestIndex === currentIndex);
152
+ for (const index of availableIndices.slice(1)) {
153
+ const account = config.accounts[index];
154
+ const score = this.calculateAccountScore(account, index === currentIndex);
155
+ if (score > bestScore) {
156
+ bestScore = score;
157
+ bestIndex = index;
158
+ }
159
+ }
160
+ await this.storage.setActiveIndex(bestIndex);
161
+ await this.markAccountUsed(bestIndex);
162
+ return {
163
+ account: config.accounts[bestIndex],
164
+ index: bestIndex,
165
+ reason: 'health-based',
166
+ };
167
+ }
168
+ calculateAccountScore(account, isCurrent) {
169
+ const timeSinceLastUse = Date.now() - account.lastUsed;
170
+ const freshnessBonus = Math.min(20, timeSinceLastUse / (1000 * 60 * 60));
171
+ let score = account.healthScore + freshnessBonus;
172
+ if (isCurrent) {
173
+ score += this.stickyBonus;
174
+ }
175
+ return score;
176
+ }
177
+ getAvailableIndices(config) {
178
+ return config.accounts
179
+ .map((account, index) => ({ account, index }))
180
+ .filter(({ account }) => !this.isRateLimited(account) && account.healthScore >= this.minHealthScore)
181
+ .map(({ index }) => index);
182
+ }
183
+ getSoonestAvailableIndex(config) {
184
+ let soonestIndex = 0;
185
+ let soonestTime = config.accounts[0].rateLimitResetTime;
186
+ for (let i = 1; i < config.accounts.length; i++) {
187
+ if (config.accounts[i].rateLimitResetTime < soonestTime) {
188
+ soonestTime = config.accounts[i].rateLimitResetTime;
189
+ soonestIndex = i;
190
+ }
191
+ }
192
+ return soonestIndex;
193
+ }
194
+ isRateLimited(account) {
195
+ return account.rateLimitResetTime > Date.now();
196
+ }
197
+ async getAccount(index) {
198
+ const accounts = await this.storage.listAccounts();
199
+ if (index < 0 || index >= accounts.length) {
200
+ throw new Error(`Invalid account index: ${index}`);
201
+ }
202
+ return accounts[index];
203
+ }
204
+ }
205
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../src/accounts.ts"],"names":[],"mappings":"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"}