@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,328 @@
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 const DEFAULT_HEALTH_SCORE_CONFIG = {
12
+ initial: 70,
13
+ successReward: 1,
14
+ rateLimitPenalty: -10,
15
+ failurePenalty: -20,
16
+ recoveryRatePerHour: 2,
17
+ minUsable: 50,
18
+ maxScore: 100,
19
+ };
20
+ /**
21
+ * Tracks health scores for accounts.
22
+ * Higher score = healthier account = preferred for selection.
23
+ */
24
+ export class HealthScoreTracker {
25
+ scores = new Map();
26
+ config;
27
+ constructor(config = {}) {
28
+ this.config = { ...DEFAULT_HEALTH_SCORE_CONFIG, ...config };
29
+ }
30
+ /**
31
+ * Get current health score for an account, applying time-based recovery.
32
+ */
33
+ getScore(accountIndex) {
34
+ const state = this.scores.get(accountIndex);
35
+ if (!state) {
36
+ return this.config.initial;
37
+ }
38
+ // Apply passive recovery based on time since last update
39
+ const now = Date.now();
40
+ const hoursSinceUpdate = (now - state.lastUpdated) / (1000 * 60 * 60);
41
+ const recoveredPoints = Math.floor(hoursSinceUpdate * this.config.recoveryRatePerHour);
42
+ return Math.min(this.config.maxScore, state.score + recoveredPoints);
43
+ }
44
+ /**
45
+ * Record a successful request - improves health score.
46
+ */
47
+ recordSuccess(accountIndex) {
48
+ const now = Date.now();
49
+ const current = this.getScore(accountIndex);
50
+ this.scores.set(accountIndex, {
51
+ score: Math.min(this.config.maxScore, current + this.config.successReward),
52
+ lastUpdated: now,
53
+ lastSuccess: now,
54
+ consecutiveFailures: 0,
55
+ });
56
+ }
57
+ /**
58
+ * Record a rate limit hit - moderate penalty.
59
+ */
60
+ recordRateLimit(accountIndex) {
61
+ const now = Date.now();
62
+ const state = this.scores.get(accountIndex);
63
+ const current = this.getScore(accountIndex);
64
+ this.scores.set(accountIndex, {
65
+ score: Math.max(0, current + this.config.rateLimitPenalty),
66
+ lastUpdated: now,
67
+ lastSuccess: state?.lastSuccess ?? 0,
68
+ consecutiveFailures: (state?.consecutiveFailures ?? 0) + 1,
69
+ });
70
+ }
71
+ /**
72
+ * Record a failure (auth, network, etc.) - larger penalty.
73
+ */
74
+ recordFailure(accountIndex) {
75
+ const now = Date.now();
76
+ const state = this.scores.get(accountIndex);
77
+ const current = this.getScore(accountIndex);
78
+ this.scores.set(accountIndex, {
79
+ score: Math.max(0, current + this.config.failurePenalty),
80
+ lastUpdated: now,
81
+ lastSuccess: state?.lastSuccess ?? 0,
82
+ consecutiveFailures: (state?.consecutiveFailures ?? 0) + 1,
83
+ });
84
+ }
85
+ /**
86
+ * Check if account is healthy enough to use.
87
+ */
88
+ isUsable(accountIndex) {
89
+ return this.getScore(accountIndex) >= this.config.minUsable;
90
+ }
91
+ /**
92
+ * Get consecutive failure count for an account.
93
+ */
94
+ getConsecutiveFailures(accountIndex) {
95
+ return this.scores.get(accountIndex)?.consecutiveFailures ?? 0;
96
+ }
97
+ /**
98
+ * Reset health state for an account (e.g., after removal).
99
+ */
100
+ reset(accountIndex) {
101
+ this.scores.delete(accountIndex);
102
+ }
103
+ /**
104
+ * Get all scores for debugging/logging.
105
+ */
106
+ getSnapshot() {
107
+ const result = new Map();
108
+ for (const [index] of this.scores) {
109
+ result.set(index, {
110
+ score: this.getScore(index),
111
+ consecutiveFailures: this.getConsecutiveFailures(index),
112
+ });
113
+ }
114
+ return result;
115
+ }
116
+ }
117
+ // ============================================================================
118
+ // JITTER UTILITIES
119
+ // ============================================================================
120
+ /**
121
+ * Add random jitter to a delay value.
122
+ * Helps break predictable timing patterns.
123
+ *
124
+ * @param baseMs - Base delay in milliseconds
125
+ * @param jitterFactor - Fraction of base to vary (default: 0.3 = ±30%)
126
+ * @returns Jittered delay in milliseconds
127
+ */
128
+ export function addJitter(baseMs, jitterFactor = 0.3) {
129
+ const jitterRange = baseMs * jitterFactor;
130
+ const jitter = (Math.random() * 2 - 1) * jitterRange; // -jitterRange to +jitterRange
131
+ return Math.max(0, Math.round(baseMs + jitter));
132
+ }
133
+ /**
134
+ * Generate a random delay within a range.
135
+ *
136
+ * @param minMs - Minimum delay in milliseconds
137
+ * @param maxMs - Maximum delay in milliseconds
138
+ * @returns Random delay between min and max
139
+ */
140
+ export function randomDelay(minMs, maxMs) {
141
+ return Math.round(minMs + Math.random() * (maxMs - minMs));
142
+ }
143
+ /**
144
+ * Sort accounts by LRU (least recently used first) with health score tiebreaker.
145
+ *
146
+ * Priority:
147
+ * 1. Filter out rate-limited and cooling-down accounts
148
+ * 2. Filter out unhealthy accounts (score < minUsable)
149
+ * 3. Sort by lastUsed ascending (oldest first = most rested)
150
+ * 4. Tiebreaker: higher health score wins
151
+ */
152
+ export function sortByLruWithHealth(accounts, minHealthScore = 50) {
153
+ return accounts
154
+ .filter(acc => !acc.isRateLimited && !acc.isCoolingDown && acc.healthScore >= minHealthScore)
155
+ .sort((a, b) => {
156
+ // Primary: LRU (oldest lastUsed first)
157
+ const lruDiff = a.lastUsed - b.lastUsed;
158
+ if (lruDiff !== 0)
159
+ return lruDiff;
160
+ // Tiebreaker: higher health score wins
161
+ return b.healthScore - a.healthScore;
162
+ });
163
+ }
164
+ /** Stickiness bonus added to current account's score to prevent unnecessary switching */
165
+ const STICKINESS_BONUS = 150;
166
+ /** Minimum score advantage required to switch away from current account */
167
+ const SWITCH_THRESHOLD = 100;
168
+ /**
169
+ * Select account using hybrid strategy with stickiness:
170
+ * 1. Filter available accounts (not rate-limited, not cooling down, healthy, has tokens)
171
+ * 2. Calculate priority score: health (2x) + tokens (5x) + freshness (0.1x)
172
+ * 3. Apply stickiness bonus to current account
173
+ * 4. Only switch if another account beats current by SWITCH_THRESHOLD
174
+ *
175
+ * @param accounts - All accounts with their metrics
176
+ * @param tokenTracker - Token bucket tracker for token balances
177
+ * @param currentAccountIndex - Currently active account index (for stickiness)
178
+ * @param minHealthScore - Minimum health score to be considered
179
+ * @returns Best account index, or null if none available
180
+ */
181
+ export function selectHybridAccount(accounts, tokenTracker, currentAccountIndex = null, minHealthScore = 50) {
182
+ const candidates = accounts
183
+ .filter(acc => !acc.isRateLimited &&
184
+ !acc.isCoolingDown &&
185
+ acc.healthScore >= minHealthScore &&
186
+ tokenTracker.hasTokens(acc.index))
187
+ .map(acc => ({
188
+ ...acc,
189
+ tokens: tokenTracker.getTokens(acc.index)
190
+ }));
191
+ if (candidates.length === 0) {
192
+ return null;
193
+ }
194
+ const maxTokens = tokenTracker.getMaxTokens();
195
+ const scored = candidates
196
+ .map(acc => {
197
+ const baseScore = calculateHybridScore(acc, maxTokens);
198
+ // Apply stickiness bonus to current account
199
+ const stickinessBonus = acc.index === currentAccountIndex ? STICKINESS_BONUS : 0;
200
+ return {
201
+ index: acc.index,
202
+ baseScore,
203
+ score: baseScore + stickinessBonus,
204
+ isCurrent: acc.index === currentAccountIndex
205
+ };
206
+ })
207
+ .sort((a, b) => b.score - a.score);
208
+ const best = scored[0];
209
+ if (!best) {
210
+ return null;
211
+ }
212
+ // If current account is still a candidate, check if switch is warranted
213
+ const currentCandidate = scored.find(s => s.isCurrent);
214
+ if (currentCandidate && !best.isCurrent) {
215
+ // Only switch if best beats current's BASE score by threshold
216
+ // (compare base scores to avoid circular stickiness bonus comparison)
217
+ const advantage = best.baseScore - currentCandidate.baseScore;
218
+ if (advantage < SWITCH_THRESHOLD) {
219
+ return currentCandidate.index;
220
+ }
221
+ }
222
+ return best.index;
223
+ }
224
+ function calculateHybridScore(account, maxTokens) {
225
+ const healthComponent = account.healthScore * 2; // 0-200
226
+ const tokenComponent = (account.tokens / maxTokens) * 100 * 5; // 0-500
227
+ const secondsSinceUsed = (Date.now() - account.lastUsed) / 1000;
228
+ const freshnessComponent = Math.min(secondsSinceUsed, 3600) * 0.1; // 0-360
229
+ return Math.max(0, healthComponent + tokenComponent + freshnessComponent);
230
+ }
231
+ export const DEFAULT_TOKEN_BUCKET_CONFIG = {
232
+ maxTokens: 50,
233
+ regenerationRatePerMinute: 6,
234
+ initialTokens: 50,
235
+ };
236
+ /**
237
+ * Client-side rate limiting using Token Bucket algorithm.
238
+ * Helps prevent hitting server 429s by tracking "cost" of requests.
239
+ */
240
+ export class TokenBucketTracker {
241
+ buckets = new Map();
242
+ config;
243
+ constructor(config = {}) {
244
+ this.config = { ...DEFAULT_TOKEN_BUCKET_CONFIG, ...config };
245
+ }
246
+ /**
247
+ * Get current token balance for an account, applying regeneration.
248
+ */
249
+ getTokens(accountIndex) {
250
+ const state = this.buckets.get(accountIndex);
251
+ if (!state) {
252
+ return this.config.initialTokens;
253
+ }
254
+ const now = Date.now();
255
+ const minutesSinceUpdate = (now - state.lastUpdated) / (1000 * 60);
256
+ const recoveredTokens = minutesSinceUpdate * this.config.regenerationRatePerMinute;
257
+ return Math.min(this.config.maxTokens, state.tokens + recoveredTokens);
258
+ }
259
+ /**
260
+ * Check if account has enough tokens for a request.
261
+ * @param cost Cost of the request (default: 1)
262
+ */
263
+ hasTokens(accountIndex, cost = 1) {
264
+ return this.getTokens(accountIndex) >= cost;
265
+ }
266
+ /**
267
+ * Consume tokens for a request.
268
+ * @returns true if tokens were consumed, false if insufficient
269
+ */
270
+ consume(accountIndex, cost = 1) {
271
+ const current = this.getTokens(accountIndex);
272
+ if (current < cost) {
273
+ return false;
274
+ }
275
+ this.buckets.set(accountIndex, {
276
+ tokens: current - cost,
277
+ lastUpdated: Date.now(),
278
+ });
279
+ return true;
280
+ }
281
+ /**
282
+ * Refund tokens (e.g., if request wasn't actually sent).
283
+ */
284
+ refund(accountIndex, amount = 1) {
285
+ const current = this.getTokens(accountIndex);
286
+ this.buckets.set(accountIndex, {
287
+ tokens: Math.min(this.config.maxTokens, current + amount),
288
+ lastUpdated: Date.now(),
289
+ });
290
+ }
291
+ getMaxTokens() {
292
+ return this.config.maxTokens;
293
+ }
294
+ }
295
+ // ============================================================================
296
+ // SINGLETON TRACKERS
297
+ // ============================================================================
298
+ let globalTokenTracker = null;
299
+ export function getTokenTracker() {
300
+ if (!globalTokenTracker) {
301
+ globalTokenTracker = new TokenBucketTracker();
302
+ }
303
+ return globalTokenTracker;
304
+ }
305
+ export function initTokenTracker(config) {
306
+ globalTokenTracker = new TokenBucketTracker(config);
307
+ return globalTokenTracker;
308
+ }
309
+ let globalHealthTracker = null;
310
+ /**
311
+ * Get the global health score tracker instance.
312
+ * Creates one with default config if not initialized.
313
+ */
314
+ export function getHealthTracker() {
315
+ if (!globalHealthTracker) {
316
+ globalHealthTracker = new HealthScoreTracker();
317
+ }
318
+ return globalHealthTracker;
319
+ }
320
+ /**
321
+ * Initialize the global health tracker with custom config.
322
+ * Call this at plugin startup if custom config is needed.
323
+ */
324
+ export function initHealthTracker(config) {
325
+ globalHealthTracker = new HealthScoreTracker(config);
326
+ return globalHealthTracker;
327
+ }
328
+ //# sourceMappingURL=rotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation.js","sourceRoot":"","sources":["../../../src/plugin/rotation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Files/directories that should be gitignored in the config directory.
3
+ * These contain sensitive data or machine-specific state.
4
+ */
5
+ export declare const GITIGNORE_ENTRIES: string[];
6
+ /**
7
+ * Ensures a .gitignore file exists in the config directory with entries
8
+ * for sensitive files. Creates the file if missing, or appends missing
9
+ * entries if it already exists.
10
+ */
11
+ export declare function ensureGitignore(configDir: string): Promise<void>;
12
+ /**
13
+ * Synchronous version of ensureGitignore for use in sync code paths.
14
+ */
15
+ export declare function ensureGitignoreSync(configDir: string): void;
16
+ export type ModelFamily = "kimi";
17
+ /**
18
+ * Header style for API requests.
19
+ * Kimi uses a single header style matching kimi-cli.
20
+ */
21
+ export type HeaderStyle = "kimi-cli";
22
+ export interface RateLimitState {
23
+ kimi?: number;
24
+ [key: string]: number | undefined;
25
+ }
26
+ export type CooldownReason = "auth-failure" | "network-error" | "project-error" | "validation-required";
27
+ export interface AccountMetadata {
28
+ email?: string;
29
+ refreshToken: string;
30
+ addedAt: number;
31
+ lastUsed: number;
32
+ enabled?: boolean;
33
+ lastSwitchReason?: "rate-limit" | "initial" | "rotation";
34
+ rateLimitResetTimes?: RateLimitState;
35
+ coolingDownUntil?: number;
36
+ cooldownReason?: CooldownReason;
37
+ /** Per-account device fingerprint for rate limit mitigation */
38
+ fingerprint?: import("./fingerprint").Fingerprint;
39
+ fingerprintHistory?: import("./fingerprint").FingerprintVersion[];
40
+ /** Cached soft quota data */
41
+ cachedQuota?: Record<string, {
42
+ remainingFraction?: number;
43
+ resetTime?: string;
44
+ modelCount: number;
45
+ }>;
46
+ cachedQuotaUpdatedAt?: number;
47
+ }
48
+ export interface AccountStorage {
49
+ version: 1;
50
+ accounts: AccountMetadata[];
51
+ activeIndex: number;
52
+ activeIndexByFamily?: {
53
+ kimi?: number;
54
+ };
55
+ }
56
+ /**
57
+ * @deprecated Aliases retained so existing imports compile during migration.
58
+ * New code should use AccountMetadata / AccountStorage / RateLimitState directly.
59
+ */
60
+ export type AccountMetadataV3 = AccountMetadata;
61
+ export type AccountStorageV4 = AccountStorage;
62
+ export type RateLimitStateV3 = RateLimitState;
63
+ /**
64
+ * Gets the config directory path, with the following precedence:
65
+ * 1. OPENCODE_CONFIG_DIR env var (if set)
66
+ * 2. ~/.config/opencode (all platforms, including Windows)
67
+ *
68
+ * On Windows, also checks for legacy %APPDATA%\opencode path for migration.
69
+ */
70
+ declare function getConfigDir(): string;
71
+ export declare function getStoragePath(): string;
72
+ /**
73
+ * Gets the config directory path. Exported for use by other modules.
74
+ */
75
+ export { getConfigDir };
76
+ export declare function deduplicateAccountsByEmail<T extends {
77
+ email?: string;
78
+ lastUsed?: number;
79
+ addedAt?: number;
80
+ }>(accounts: T[]): T[];
81
+ export declare function loadAccounts(): Promise<AccountStorage | null>;
82
+ export declare function saveAccounts(storage: AccountStorage): Promise<void>;
83
+ /**
84
+ * Save accounts storage by replacing the entire file (no merge).
85
+ * Use this for destructive operations like delete where we need to
86
+ * remove accounts that would otherwise be merged back from existing storage.
87
+ */
88
+ export declare function saveAccountsReplace(storage: AccountStorage): Promise<void>;
89
+ export declare function clearAccounts(): Promise<void>;
90
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/plugin/storage.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,eAAO,MAAM,iBAAiB,UAK7B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8CtE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAuC3D;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC;AAErC,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,eAAe,GAAG,eAAe,GAAG,qBAAqB,CAAC;AAExG,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;IACzD,mBAAmB,CAAC,EAAE,cAAc,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,+DAA+D;IAC/D,WAAW,CAAC,EAAE,OAAO,eAAe,EAAE,WAAW,CAAC;IAClD,kBAAkB,CAAC,EAAE,OAAO,eAAe,EAAE,kBAAkB,EAAE,CAAC;IAClE,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrG,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAChD,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAC9C,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAa9C;;;;;;GAMG;AACH,iBAAS,YAAY,IAAI,MAAM,CAS9B;AAiFD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,CAAC;AA6FxB,wBAAgB,0BAA0B,CACxC,CAAC,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,QAAQ,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAsDpB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA0DnE;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBzE;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBhF;AA2BD,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAUnD"}