@itssimplereally/opencode-kimicode-auth 0.1.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.
Files changed (115) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +87 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/constants.d.ts +69 -0
  8. package/dist/src/constants.d.ts.map +1 -0
  9. package/dist/src/constants.js +207 -0
  10. package/dist/src/constants.js.map +1 -0
  11. package/dist/src/kimi/oauth.d.ts +64 -0
  12. package/dist/src/kimi/oauth.d.ts.map +1 -0
  13. package/dist/src/kimi/oauth.js +130 -0
  14. package/dist/src/kimi/oauth.js.map +1 -0
  15. package/dist/src/plugin/accounts.d.ts +167 -0
  16. package/dist/src/plugin/accounts.d.ts.map +1 -0
  17. package/dist/src/plugin/accounts.js +843 -0
  18. package/dist/src/plugin/accounts.js.map +1 -0
  19. package/dist/src/plugin/auth.d.ts +13 -0
  20. package/dist/src/plugin/auth.d.ts.map +1 -0
  21. package/dist/src/plugin/auth.js +26 -0
  22. package/dist/src/plugin/auth.js.map +1 -0
  23. package/dist/src/plugin/cache.d.ts +14 -0
  24. package/dist/src/plugin/cache.d.ts.map +1 -0
  25. package/dist/src/plugin/cache.js +56 -0
  26. package/dist/src/plugin/cache.js.map +1 -0
  27. package/dist/src/plugin/cli.d.ts +21 -0
  28. package/dist/src/plugin/cli.d.ts.map +1 -0
  29. package/dist/src/plugin/cli.js +98 -0
  30. package/dist/src/plugin/cli.js.map +1 -0
  31. package/dist/src/plugin/config/index.d.ts +16 -0
  32. package/dist/src/plugin/config/index.d.ts.map +1 -0
  33. package/dist/src/plugin/config/index.js +16 -0
  34. package/dist/src/plugin/config/index.js.map +1 -0
  35. package/dist/src/plugin/config/loader.d.ts +36 -0
  36. package/dist/src/plugin/config/loader.d.ts.map +1 -0
  37. package/dist/src/plugin/config/loader.js +182 -0
  38. package/dist/src/plugin/config/loader.js.map +1 -0
  39. package/dist/src/plugin/config/models.d.ts +18 -0
  40. package/dist/src/plugin/config/models.d.ts.map +1 -0
  41. package/dist/src/plugin/config/models.js +26 -0
  42. package/dist/src/plugin/config/models.js.map +1 -0
  43. package/dist/src/plugin/config/schema.d.ts +107 -0
  44. package/dist/src/plugin/config/schema.d.ts.map +1 -0
  45. package/dist/src/plugin/config/schema.js +282 -0
  46. package/dist/src/plugin/config/schema.js.map +1 -0
  47. package/dist/src/plugin/config/updater.d.ts +55 -0
  48. package/dist/src/plugin/config/updater.d.ts.map +1 -0
  49. package/dist/src/plugin/config/updater.js +154 -0
  50. package/dist/src/plugin/config/updater.js.map +1 -0
  51. package/dist/src/plugin/debug.d.ts +92 -0
  52. package/dist/src/plugin/debug.d.ts.map +1 -0
  53. package/dist/src/plugin/debug.js +406 -0
  54. package/dist/src/plugin/debug.js.map +1 -0
  55. package/dist/src/plugin/errors.d.ts +28 -0
  56. package/dist/src/plugin/errors.d.ts.map +1 -0
  57. package/dist/src/plugin/errors.js +42 -0
  58. package/dist/src/plugin/errors.js.map +1 -0
  59. package/dist/src/plugin/fingerprint.d.ts +41 -0
  60. package/dist/src/plugin/fingerprint.d.ts.map +1 -0
  61. package/dist/src/plugin/fingerprint.js +94 -0
  62. package/dist/src/plugin/fingerprint.js.map +1 -0
  63. package/dist/src/plugin/logger.d.ts +54 -0
  64. package/dist/src/plugin/logger.d.ts.map +1 -0
  65. package/dist/src/plugin/logger.js +120 -0
  66. package/dist/src/plugin/logger.js.map +1 -0
  67. package/dist/src/plugin/recovery/constants.d.ts +26 -0
  68. package/dist/src/plugin/recovery/constants.d.ts.map +1 -0
  69. package/dist/src/plugin/recovery/constants.js +47 -0
  70. package/dist/src/plugin/recovery/constants.js.map +1 -0
  71. package/dist/src/plugin/recovery/index.d.ts +16 -0
  72. package/dist/src/plugin/recovery/index.d.ts.map +1 -0
  73. package/dist/src/plugin/recovery/index.js +16 -0
  74. package/dist/src/plugin/recovery/index.js.map +1 -0
  75. package/dist/src/plugin/recovery/storage.d.ts +24 -0
  76. package/dist/src/plugin/recovery/storage.d.ts.map +1 -0
  77. package/dist/src/plugin/recovery/storage.js +354 -0
  78. package/dist/src/plugin/recovery/storage.js.map +1 -0
  79. package/dist/src/plugin/recovery/types.d.ts +116 -0
  80. package/dist/src/plugin/recovery/types.d.ts.map +1 -0
  81. package/dist/src/plugin/recovery/types.js +6 -0
  82. package/dist/src/plugin/recovery/types.js.map +1 -0
  83. package/dist/src/plugin/recovery.d.ts +63 -0
  84. package/dist/src/plugin/recovery.d.ts.map +1 -0
  85. package/dist/src/plugin/recovery.js +331 -0
  86. package/dist/src/plugin/recovery.js.map +1 -0
  87. package/dist/src/plugin/refresh-queue.d.ts +101 -0
  88. package/dist/src/plugin/refresh-queue.d.ts.map +1 -0
  89. package/dist/src/plugin/refresh-queue.js +248 -0
  90. package/dist/src/plugin/refresh-queue.js.map +1 -0
  91. package/dist/src/plugin/rotation.d.ts +169 -0
  92. package/dist/src/plugin/rotation.d.ts.map +1 -0
  93. package/dist/src/plugin/rotation.js +328 -0
  94. package/dist/src/plugin/rotation.js.map +1 -0
  95. package/dist/src/plugin/storage.d.ts +90 -0
  96. package/dist/src/plugin/storage.d.ts.map +1 -0
  97. package/dist/src/plugin/storage.js +450 -0
  98. package/dist/src/plugin/storage.js.map +1 -0
  99. package/dist/src/plugin/token.d.ts +19 -0
  100. package/dist/src/plugin/token.d.ts.map +1 -0
  101. package/dist/src/plugin/token.js +112 -0
  102. package/dist/src/plugin/token.js.map +1 -0
  103. package/dist/src/plugin/types.d.ts +97 -0
  104. package/dist/src/plugin/types.d.ts.map +1 -0
  105. package/dist/src/plugin/types.js +1 -0
  106. package/dist/src/plugin/types.js.map +1 -0
  107. package/dist/src/plugin/version.d.ts +14 -0
  108. package/dist/src/plugin/version.d.ts.map +1 -0
  109. package/dist/src/plugin/version.js +20 -0
  110. package/dist/src/plugin/version.js.map +1 -0
  111. package/dist/src/plugin.d.ts +5 -0
  112. package/dist/src/plugin.d.ts.map +1 -0
  113. package/dist/src/plugin.js +1077 -0
  114. package/dist/src/plugin.js.map +1 -0
  115. package/package.json +55 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Proactive Token Refresh Queue
3
+ *
4
+ * Ported from LLM-API-Key-Proxy's BackgroundRefresher.
5
+ *
6
+ * This module provides background token refresh to ensure OAuth tokens
7
+ * remain valid without blocking user requests. It periodically checks
8
+ * all accounts and refreshes tokens that are approaching expiry.
9
+ *
10
+ * Features:
11
+ * - Non-blocking background refresh (doesn't block requests)
12
+ * - Configurable refresh buffer (default: 30 minutes before expiry)
13
+ * - Configurable check interval (default: 5 minutes)
14
+ * - Serialized refresh to prevent concurrent refresh storms
15
+ * - Integrates with existing AccountManager and token refresh logic
16
+ * - Silent operation: no console output, uses structured logger
17
+ */
18
+ import { refreshAccessToken } from "./token";
19
+ import { createLogger } from "./logger";
20
+ const log = createLogger("refresh-queue");
21
+ export const DEFAULT_PROACTIVE_REFRESH_CONFIG = {
22
+ enabled: true,
23
+ bufferSeconds: 1800, // 30 minutes
24
+ checkIntervalSeconds: 300, // 5 minutes
25
+ };
26
+ /**
27
+ * Proactive Token Refresh Queue
28
+ *
29
+ * Runs in the background and proactively refreshes tokens before they expire.
30
+ * This ensures that user requests never block on token refresh.
31
+ *
32
+ * All logging is silent by default - uses structured logger that only outputs
33
+ * when OPENCODE_KIMICODE_CONSOLE_LOG=1 is set or TUI logging is available.
34
+ */
35
+ export class ProactiveRefreshQueue {
36
+ config;
37
+ client;
38
+ providerId;
39
+ accountManager = null;
40
+ state = {
41
+ isRunning: false,
42
+ intervalHandle: null,
43
+ isRefreshing: false,
44
+ lastCheckTime: 0,
45
+ lastRefreshTime: 0,
46
+ refreshCount: 0,
47
+ errorCount: 0,
48
+ };
49
+ constructor(client, providerId, config) {
50
+ this.client = client;
51
+ this.providerId = providerId;
52
+ this.config = {
53
+ ...DEFAULT_PROACTIVE_REFRESH_CONFIG,
54
+ ...config,
55
+ };
56
+ }
57
+ /**
58
+ * Set the account manager to use for refresh operations.
59
+ * Must be called before start().
60
+ */
61
+ setAccountManager(manager) {
62
+ this.accountManager = manager;
63
+ }
64
+ /**
65
+ * Check if a token needs proactive refresh.
66
+ * Returns true if the token expires within the buffer period.
67
+ */
68
+ needsRefresh(account) {
69
+ if (!account.expires) {
70
+ // No expiry set - assume it's fine
71
+ return false;
72
+ }
73
+ const now = Date.now();
74
+ const bufferMs = this.config.bufferSeconds * 1000;
75
+ const refreshThreshold = now + bufferMs;
76
+ return account.expires <= refreshThreshold;
77
+ }
78
+ /**
79
+ * Check if a token is already expired.
80
+ */
81
+ isExpired(account) {
82
+ if (!account.expires) {
83
+ return false;
84
+ }
85
+ return account.expires <= Date.now();
86
+ }
87
+ /**
88
+ * Get all accounts that need proactive refresh.
89
+ */
90
+ getAccountsNeedingRefresh() {
91
+ if (!this.accountManager) {
92
+ return [];
93
+ }
94
+ return this.accountManager.getAccounts().filter((account) => {
95
+ // Skip disabled accounts - they shouldn't receive proactive refresh
96
+ if (account.enabled === false) {
97
+ return false;
98
+ }
99
+ // Only refresh if not already expired (let the main flow handle expired tokens)
100
+ if (this.isExpired(account)) {
101
+ return false;
102
+ }
103
+ return this.needsRefresh(account);
104
+ });
105
+ }
106
+ /**
107
+ * Perform a single refresh check iteration.
108
+ * This is called periodically by the background interval.
109
+ */
110
+ async runRefreshCheck() {
111
+ if (this.state.isRefreshing) {
112
+ // Already refreshing - skip this iteration
113
+ return;
114
+ }
115
+ if (!this.accountManager) {
116
+ return;
117
+ }
118
+ this.state.isRefreshing = true;
119
+ this.state.lastCheckTime = Date.now();
120
+ try {
121
+ const accountsToRefresh = this.getAccountsNeedingRefresh();
122
+ if (accountsToRefresh.length === 0) {
123
+ return;
124
+ }
125
+ log.debug("Found accounts needing refresh", { count: accountsToRefresh.length });
126
+ // Refresh accounts serially to avoid concurrent refresh storms
127
+ for (const account of accountsToRefresh) {
128
+ if (!this.state.isRunning) {
129
+ // Queue was stopped - abort
130
+ break;
131
+ }
132
+ try {
133
+ const auth = this.accountManager.toAuthDetails(account);
134
+ const refreshed = await this.refreshToken(auth, account);
135
+ if (refreshed) {
136
+ this.accountManager.updateFromAuth(account, refreshed);
137
+ this.state.refreshCount++;
138
+ this.state.lastRefreshTime = Date.now();
139
+ // Persist the refreshed token
140
+ try {
141
+ await this.accountManager.saveToDisk();
142
+ }
143
+ catch {
144
+ // Non-fatal - token is refreshed in memory
145
+ }
146
+ }
147
+ }
148
+ catch (error) {
149
+ this.state.errorCount++;
150
+ // Log but don't throw - continue with other accounts
151
+ log.warn("Failed to refresh account", {
152
+ accountIndex: account.index,
153
+ error: error instanceof Error ? error.message : String(error),
154
+ });
155
+ }
156
+ }
157
+ }
158
+ finally {
159
+ this.state.isRefreshing = false;
160
+ }
161
+ }
162
+ /**
163
+ * Refresh a single token.
164
+ */
165
+ async refreshToken(auth, account) {
166
+ const minutesUntilExpiry = account.expires
167
+ ? Math.round((account.expires - Date.now()) / 60000)
168
+ : "unknown";
169
+ log.debug("Proactively refreshing token", {
170
+ accountIndex: account.index,
171
+ email: account.email ?? "unknown",
172
+ minutesUntilExpiry,
173
+ });
174
+ return refreshAccessToken(auth, this.client, this.providerId);
175
+ }
176
+ /**
177
+ * Start the background refresh queue.
178
+ */
179
+ start() {
180
+ if (this.state.isRunning) {
181
+ return;
182
+ }
183
+ if (!this.config.enabled) {
184
+ log.debug("Proactive refresh disabled by config");
185
+ return;
186
+ }
187
+ this.state.isRunning = true;
188
+ const intervalMs = this.config.checkIntervalSeconds * 1000;
189
+ log.debug("Started proactive refresh queue", {
190
+ checkIntervalSeconds: this.config.checkIntervalSeconds,
191
+ bufferSeconds: this.config.bufferSeconds,
192
+ });
193
+ // Run initial check after a short delay (let things settle)
194
+ setTimeout(() => {
195
+ if (this.state.isRunning) {
196
+ this.runRefreshCheck().catch((error) => {
197
+ log.error("Initial check failed", {
198
+ error: error instanceof Error ? error.message : String(error),
199
+ });
200
+ });
201
+ }
202
+ }, 5000);
203
+ // Set up periodic checks
204
+ this.state.intervalHandle = setInterval(() => {
205
+ this.runRefreshCheck().catch((error) => {
206
+ log.error("Check failed", {
207
+ error: error instanceof Error ? error.message : String(error),
208
+ });
209
+ });
210
+ }, intervalMs);
211
+ }
212
+ /**
213
+ * Stop the background refresh queue.
214
+ */
215
+ stop() {
216
+ if (!this.state.isRunning) {
217
+ return;
218
+ }
219
+ this.state.isRunning = false;
220
+ if (this.state.intervalHandle) {
221
+ clearInterval(this.state.intervalHandle);
222
+ this.state.intervalHandle = null;
223
+ }
224
+ log.debug("Stopped proactive refresh queue", {
225
+ refreshCount: this.state.refreshCount,
226
+ errorCount: this.state.errorCount,
227
+ });
228
+ }
229
+ /**
230
+ * Get current queue statistics.
231
+ */
232
+ getStats() {
233
+ return { ...this.state };
234
+ }
235
+ /**
236
+ * Check if the queue is currently running.
237
+ */
238
+ isRunning() {
239
+ return this.state.isRunning;
240
+ }
241
+ }
242
+ /**
243
+ * Create a proactive refresh queue instance.
244
+ */
245
+ export function createProactiveRefreshQueue(client, providerId, config) {
246
+ return new ProactiveRefreshQueue(client, providerId, config);
247
+ }
248
+ //# sourceMappingURL=refresh-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-queue.js","sourceRoot":"","sources":["../../../src/plugin/refresh-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Account Rotation System
3
+ *
4
+ * Implements advanced account selection algorithms:
5
+ * - Health Score: Track account wellness based on success/failure
6
+ * - LRU Selection: Prefer accounts with longest rest periods
7
+ * - Jitter: Add random variance to break predictable patterns
8
+ *
9
+ * Used by 'hybrid' strategy for improved ban prevention and load distribution.
10
+ */
11
+ export interface HealthScoreConfig {
12
+ /** Initial score for new accounts (default: 70) */
13
+ initial: number;
14
+ /** Points added on successful request (default: 1) */
15
+ successReward: number;
16
+ /** Points removed on rate limit (default: -10) */
17
+ rateLimitPenalty: number;
18
+ /** Points removed on failure (auth, network, etc.) (default: -20) */
19
+ failurePenalty: number;
20
+ /** Points recovered per hour of rest (default: 2) */
21
+ recoveryRatePerHour: number;
22
+ /** Minimum score to be considered usable (default: 50) */
23
+ minUsable: number;
24
+ /** Maximum score cap (default: 100) */
25
+ maxScore: number;
26
+ }
27
+ export declare const DEFAULT_HEALTH_SCORE_CONFIG: HealthScoreConfig;
28
+ /**
29
+ * Tracks health scores for accounts.
30
+ * Higher score = healthier account = preferred for selection.
31
+ */
32
+ export declare class HealthScoreTracker {
33
+ private readonly scores;
34
+ private readonly config;
35
+ constructor(config?: Partial<HealthScoreConfig>);
36
+ /**
37
+ * Get current health score for an account, applying time-based recovery.
38
+ */
39
+ getScore(accountIndex: number): number;
40
+ /**
41
+ * Record a successful request - improves health score.
42
+ */
43
+ recordSuccess(accountIndex: number): void;
44
+ /**
45
+ * Record a rate limit hit - moderate penalty.
46
+ */
47
+ recordRateLimit(accountIndex: number): void;
48
+ /**
49
+ * Record a failure (auth, network, etc.) - larger penalty.
50
+ */
51
+ recordFailure(accountIndex: number): void;
52
+ /**
53
+ * Check if account is healthy enough to use.
54
+ */
55
+ isUsable(accountIndex: number): boolean;
56
+ /**
57
+ * Get consecutive failure count for an account.
58
+ */
59
+ getConsecutiveFailures(accountIndex: number): number;
60
+ /**
61
+ * Reset health state for an account (e.g., after removal).
62
+ */
63
+ reset(accountIndex: number): void;
64
+ /**
65
+ * Get all scores for debugging/logging.
66
+ */
67
+ getSnapshot(): Map<number, {
68
+ score: number;
69
+ consecutiveFailures: number;
70
+ }>;
71
+ }
72
+ /**
73
+ * Add random jitter to a delay value.
74
+ * Helps break predictable timing patterns.
75
+ *
76
+ * @param baseMs - Base delay in milliseconds
77
+ * @param jitterFactor - Fraction of base to vary (default: 0.3 = ±30%)
78
+ * @returns Jittered delay in milliseconds
79
+ */
80
+ export declare function addJitter(baseMs: number, jitterFactor?: number): number;
81
+ /**
82
+ * Generate a random delay within a range.
83
+ *
84
+ * @param minMs - Minimum delay in milliseconds
85
+ * @param maxMs - Maximum delay in milliseconds
86
+ * @returns Random delay between min and max
87
+ */
88
+ export declare function randomDelay(minMs: number, maxMs: number): number;
89
+ export interface AccountWithMetrics {
90
+ index: number;
91
+ lastUsed: number;
92
+ healthScore: number;
93
+ isRateLimited: boolean;
94
+ isCoolingDown: boolean;
95
+ }
96
+ /**
97
+ * Sort accounts by LRU (least recently used first) with health score tiebreaker.
98
+ *
99
+ * Priority:
100
+ * 1. Filter out rate-limited and cooling-down accounts
101
+ * 2. Filter out unhealthy accounts (score < minUsable)
102
+ * 3. Sort by lastUsed ascending (oldest first = most rested)
103
+ * 4. Tiebreaker: higher health score wins
104
+ */
105
+ export declare function sortByLruWithHealth(accounts: AccountWithMetrics[], minHealthScore?: number): AccountWithMetrics[];
106
+ /**
107
+ * Select account using hybrid strategy with stickiness:
108
+ * 1. Filter available accounts (not rate-limited, not cooling down, healthy, has tokens)
109
+ * 2. Calculate priority score: health (2x) + tokens (5x) + freshness (0.1x)
110
+ * 3. Apply stickiness bonus to current account
111
+ * 4. Only switch if another account beats current by SWITCH_THRESHOLD
112
+ *
113
+ * @param accounts - All accounts with their metrics
114
+ * @param tokenTracker - Token bucket tracker for token balances
115
+ * @param currentAccountIndex - Currently active account index (for stickiness)
116
+ * @param minHealthScore - Minimum health score to be considered
117
+ * @returns Best account index, or null if none available
118
+ */
119
+ export declare function selectHybridAccount(accounts: AccountWithMetrics[], tokenTracker: TokenBucketTracker, currentAccountIndex?: number | null, minHealthScore?: number): number | null;
120
+ export interface TokenBucketConfig {
121
+ /** Maximum tokens per account (default: 50) */
122
+ maxTokens: number;
123
+ /** Tokens regenerated per minute (default: 6) */
124
+ regenerationRatePerMinute: number;
125
+ /** Initial tokens for new accounts (default: 50) */
126
+ initialTokens: number;
127
+ }
128
+ export declare const DEFAULT_TOKEN_BUCKET_CONFIG: TokenBucketConfig;
129
+ /**
130
+ * Client-side rate limiting using Token Bucket algorithm.
131
+ * Helps prevent hitting server 429s by tracking "cost" of requests.
132
+ */
133
+ export declare class TokenBucketTracker {
134
+ private readonly buckets;
135
+ private readonly config;
136
+ constructor(config?: Partial<TokenBucketConfig>);
137
+ /**
138
+ * Get current token balance for an account, applying regeneration.
139
+ */
140
+ getTokens(accountIndex: number): number;
141
+ /**
142
+ * Check if account has enough tokens for a request.
143
+ * @param cost Cost of the request (default: 1)
144
+ */
145
+ hasTokens(accountIndex: number, cost?: number): boolean;
146
+ /**
147
+ * Consume tokens for a request.
148
+ * @returns true if tokens were consumed, false if insufficient
149
+ */
150
+ consume(accountIndex: number, cost?: number): boolean;
151
+ /**
152
+ * Refund tokens (e.g., if request wasn't actually sent).
153
+ */
154
+ refund(accountIndex: number, amount?: number): void;
155
+ getMaxTokens(): number;
156
+ }
157
+ export declare function getTokenTracker(): TokenBucketTracker;
158
+ export declare function initTokenTracker(config: Partial<TokenBucketConfig>): TokenBucketTracker;
159
+ /**
160
+ * Get the global health score tracker instance.
161
+ * Creates one with default config if not initialized.
162
+ */
163
+ export declare function getHealthTracker(): HealthScoreTracker;
164
+ /**
165
+ * Initialize the global health tracker with custom config.
166
+ * Call this at plugin startup if custom config is needed.
167
+ */
168
+ export declare function initHealthTracker(config: Partial<HealthScoreConfig>): HealthScoreTracker;
169
+ //# sourceMappingURL=rotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../../src/plugin/rotation.ts"],"names":[],"mappings":"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"}