opencode-gemini-business 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,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI entry point for opencode-gemini-business
5
+ */
6
+
7
+ import('../dist/index.js')
8
+ .then(module => {
9
+ if (module.cli) {
10
+ return module.cli();
11
+ } else {
12
+ throw new Error('CLI function not found in module exports');
13
+ }
14
+ })
15
+ .catch(error => {
16
+ console.error('Failed to load CLI:', error);
17
+ process.exit(1);
18
+ });
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OpenCode Gemini Business Plugin
4
+ * Multi-account Gemini Business pool with automatic rotation
5
+ *
6
+ * Follows OpenCode plugin patterns from:
7
+ * - opencode-gemini-auth
8
+ * - opencode-openai-codex-auth
9
+ * - opencode-antigravity-auth
10
+ */
11
+ import { AccountManager } from './src/account-manager.js';
12
+ import { GeminiBusinessAPI } from './src/gemini-business-api.js';
13
+ /**
14
+ * OpenCode Plugin Entry Point
15
+ *
16
+ * This plugin doesn't use traditional OAuth - it uses cookie-based auth
17
+ * with Gemini Business accounts configured via CLI.
18
+ */
19
+ export declare const GeminiBusinessPlugin: (ctx: any) => Promise<{
20
+ auth: {
21
+ provider: string;
22
+ /**
23
+ * Loader function - called by OpenCode to initialize the provider
24
+ */
25
+ loader(getAuth: any, provider: any): Promise<{
26
+ accountCount?: undefined;
27
+ strategy?: undefined;
28
+ /**
29
+ * Custom fetch implementation for Gemini Business API
30
+ *
31
+ * This intercepts all OpenCode API calls and routes them through
32
+ * the Gemini Business API with account rotation.
33
+ */
34
+ fetch?: undefined;
35
+ } | {
36
+ accountCount: number;
37
+ strategy: "round-robin" | "least-used" | "random";
38
+ /**
39
+ * Custom fetch implementation for Gemini Business API
40
+ *
41
+ * This intercepts all OpenCode API calls and routes them through
42
+ * the Gemini Business API with account rotation.
43
+ */
44
+ fetch(input: any, options: any): Promise<any>;
45
+ }>;
46
+ /**
47
+ * Authentication methods
48
+ *
49
+ * For Gemini Business, we don't support OAuth - accounts are configured
50
+ * via CLI with cookie extraction from browser.
51
+ */
52
+ methods: {
53
+ type: "cookie";
54
+ label: string;
55
+ authorize: () => Promise<{
56
+ type: "instructions";
57
+ instructions: string;
58
+ }>;
59
+ }[];
60
+ };
61
+ }>;
62
+ export default GeminiBusinessPlugin;
63
+ export { AccountManager, GeminiBusinessAPI };
64
+ /**
65
+ * CLI entry point - handles account management commands
66
+ */
67
+ declare function cli(): Promise<void>;
68
+ export { cli };
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAOjE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAAU,KAAK,GAAG;;;QAK7C;;WAEG;wBACmB,GAAG,YAAY,GAAG;;;YA0BpC;;;;;eAKG;;;;;YALH;;;;;eAKG;yBACgB,GAAG,WAAW,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;;QAsDvD;;;;;WAKG;;;;;;;;;;EAyBR,CAAC;AAGF,eAAe,oBAAoB,CAAC;AAGpC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC;AAM7C;;GAEG;AACH,iBAAe,GAAG,kBAkCjB;AAsJD,OAAO,EAAE,GAAG,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * OpenCode Gemini Business Plugin
4
+ * Multi-account Gemini Business pool with automatic rotation
5
+ *
6
+ * Follows OpenCode plugin patterns from:
7
+ * - opencode-gemini-auth
8
+ * - opencode-openai-codex-auth
9
+ * - opencode-antigravity-auth
10
+ */
11
+ import { AccountManager } from './src/account-manager.js';
12
+ import { GeminiBusinessAPI } from './src/gemini-business-api.js';
13
+ // ============================================================================
14
+ // OpenCode Plugin Export
15
+ // ============================================================================
16
+ /**
17
+ * OpenCode Plugin Entry Point
18
+ *
19
+ * This plugin doesn't use traditional OAuth - it uses cookie-based auth
20
+ * with Gemini Business accounts configured via CLI.
21
+ */
22
+ export const GeminiBusinessPlugin = async (ctx) => {
23
+ return {
24
+ auth: {
25
+ provider: 'gemini-business',
26
+ /**
27
+ * Loader function - called by OpenCode to initialize the provider
28
+ */
29
+ async loader(getAuth, provider) {
30
+ const accountManager = new AccountManager();
31
+ await accountManager.loadAccounts();
32
+ const accounts = accountManager.getAccounts();
33
+ if (accounts.length === 0) {
34
+ console.error('\n❌ No Gemini Business accounts configured!\n' +
35
+ '\nTo add an account, run:\n' +
36
+ ' opencode-gemini-business add-account\n' +
37
+ '\nFor help, run:\n' +
38
+ ' opencode-gemini-business help\n');
39
+ return {};
40
+ }
41
+ console.log(`\n✅ Gemini Business: Loaded ${accounts.length} account(s)\n` +
42
+ ` Strategy: ${accountManager.getConfig().rotation_strategy}\n`);
43
+ return {
44
+ // Return empty config - we handle everything in custom fetch
45
+ accountCount: accounts.length,
46
+ strategy: accountManager.getConfig().rotation_strategy,
47
+ /**
48
+ * Custom fetch implementation for Gemini Business API
49
+ *
50
+ * This intercepts all OpenCode API calls and routes them through
51
+ * the Gemini Business API with account rotation.
52
+ */
53
+ async fetch(input, options) {
54
+ try {
55
+ // Get next available account based on rotation strategy
56
+ const account = accountManager.getNextAccount();
57
+ if (!account) {
58
+ throw new Error('No available accounts');
59
+ }
60
+ // Create API client for this account
61
+ const api = new GeminiBusinessAPI(account);
62
+ // Refresh session if needed
63
+ if (api.needsSessionRefresh()) {
64
+ console.log(` ⟳ Refreshing session for: ${account.name}`);
65
+ await api.refreshSession();
66
+ await accountManager.updateSession(account.id, account.session_id, 50 * 60 * 1000 // 50 minutes
67
+ );
68
+ }
69
+ // Parse request body
70
+ const requestBody = options?.body ? JSON.parse(options.body) : {};
71
+ // Make API call
72
+ const response = await api.chatCompletion(requestBody);
73
+ // Success! Reset error count
74
+ accountManager.resetAccountErrors(account.id);
75
+ // Convert to Response object for OpenCode
76
+ return new Response(JSON.stringify(response), {
77
+ status: 200,
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ },
81
+ });
82
+ }
83
+ catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+ // Mark account error
86
+ const account = accountManager.getNextAccount();
87
+ if (account) {
88
+ accountManager.markAccountError(account.id, errorMessage);
89
+ }
90
+ // Rethrow for OpenCode to handle
91
+ throw new Error(`Gemini Business API Error: ${errorMessage}`);
92
+ }
93
+ },
94
+ };
95
+ },
96
+ /**
97
+ * Authentication methods
98
+ *
99
+ * For Gemini Business, we don't support OAuth - accounts are configured
100
+ * via CLI with cookie extraction from browser.
101
+ */
102
+ methods: [
103
+ {
104
+ type: 'cookie',
105
+ label: 'Gemini Business Cookie Auth',
106
+ authorize: async () => ({
107
+ type: 'instructions',
108
+ instructions: '📋 To configure Gemini Business accounts:\n\n' +
109
+ '1. Login to https://business.gemini.google\n' +
110
+ '2. Open DevTools (F12) → Network tab\n' +
111
+ '3. Make any API request (refresh page or send a prompt)\n' +
112
+ '4. Find request headers and copy:\n' +
113
+ ' • team_id (from X-Goog-Team-Id header)\n' +
114
+ ' • __Secure-c_ses cookie\n' +
115
+ ' • __Host-c_oses cookie\n' +
116
+ ' • csesidx value\n\n' +
117
+ '5. Run: opencode-gemini-business add-account\n' +
118
+ ' Or use environment variables (see docs)\n\n' +
119
+ 'For more help: opencode-gemini-business help',
120
+ }),
121
+ },
122
+ ],
123
+ },
124
+ };
125
+ };
126
+ // Export as default for CommonJS compatibility
127
+ export default GeminiBusinessPlugin;
128
+ // Export legacy functions for backward compatibility (if needed)
129
+ export { AccountManager, GeminiBusinessAPI };
130
+ // ============================================================================
131
+ // CLI Commands
132
+ // ============================================================================
133
+ /**
134
+ * CLI entry point - handles account management commands
135
+ */
136
+ async function cli() {
137
+ const args = process.argv.slice(2);
138
+ const command = args[0];
139
+ const accountManager = new AccountManager();
140
+ try {
141
+ await accountManager.loadAccounts();
142
+ }
143
+ catch {
144
+ // Config file might not exist yet - that's ok
145
+ }
146
+ switch (command) {
147
+ case 'add-account':
148
+ await addAccountCommand(accountManager, args.slice(1));
149
+ break;
150
+ case 'list-accounts':
151
+ await listAccountsCommand(accountManager);
152
+ break;
153
+ case 'remove-account':
154
+ await removeAccountCommand(accountManager, args[1]);
155
+ break;
156
+ case 'test-account':
157
+ await testAccountCommand(accountManager, args[1]);
158
+ break;
159
+ case 'help':
160
+ default:
161
+ printHelp();
162
+ break;
163
+ }
164
+ }
165
+ /**
166
+ * Add account command
167
+ */
168
+ async function addAccountCommand(manager, args) {
169
+ console.log('Add Gemini Business Account');
170
+ console.log('----------------------------');
171
+ // Read from command line args or env vars
172
+ const account = {
173
+ name: args[0] || process.env.GEMINI_ACCOUNT_NAME || `account-${Date.now()}`,
174
+ team_id: args[1] || process.env.GEMINI_TEAM_ID || '',
175
+ cookies: {
176
+ secure_c_ses: args[2] || process.env.GEMINI_SECURE_C_SES || '',
177
+ host_c_oses: args[3] || process.env.GEMINI_HOST_C_OSES || '',
178
+ },
179
+ csesidx: args[4] || process.env.GEMINI_CSESIDX || '',
180
+ user_agent: args[5] || process.env.GEMINI_USER_AGENT,
181
+ enabled: true,
182
+ };
183
+ // Validate required fields
184
+ if (!account.team_id || !account.cookies.secure_c_ses || !account.cookies.host_c_oses || !account.csesidx) {
185
+ console.error('Error: Missing required fields!');
186
+ console.error('\nUsage:');
187
+ console.error(' opencode-gemini-business add-account <name> <team_id> <secure_c_ses> <host_c_oses> <csesidx> [user_agent]');
188
+ console.error('\nOr set environment variables:');
189
+ console.error(' GEMINI_ACCOUNT_NAME, GEMINI_TEAM_ID, GEMINI_SECURE_C_SES, GEMINI_HOST_C_OSES, GEMINI_CSESIDX, GEMINI_USER_AGENT');
190
+ process.exit(1);
191
+ }
192
+ const id = await manager.addAccount(account);
193
+ console.log(`✅ Account added successfully! ID: ${id}`);
194
+ console.log(` Name: ${account.name}`);
195
+ console.log(` Team ID: ${account.team_id}`);
196
+ }
197
+ /**
198
+ * List accounts command
199
+ */
200
+ async function listAccountsCommand(manager) {
201
+ const accounts = manager.getAccounts();
202
+ if (accounts.length === 0) {
203
+ console.log('No accounts configured.');
204
+ return;
205
+ }
206
+ console.log(`Found ${accounts.length} account(s):\n`);
207
+ accounts.forEach((account, index) => {
208
+ console.log(`[${index + 1}] ${account.name} (${account.id})`);
209
+ console.log(` Team ID: ${account.team_id}`);
210
+ console.log(` Status: ${account.enabled ? '✅ Enabled' : '❌ Disabled'}`);
211
+ console.log(` Errors: ${account.error_count || 0}`);
212
+ if (account.last_error) {
213
+ console.log(` Last Error: ${account.last_error}`);
214
+ }
215
+ if (account.last_used) {
216
+ const lastUsed = new Date(account.last_used);
217
+ console.log(` Last Used: ${lastUsed.toISOString()}`);
218
+ }
219
+ console.log('');
220
+ });
221
+ }
222
+ /**
223
+ * Remove account command
224
+ */
225
+ async function removeAccountCommand(manager, accountId) {
226
+ if (!accountId) {
227
+ console.error('Error: Account ID required');
228
+ console.error('Usage: opencode-gemini-business remove-account <account_id>');
229
+ process.exit(1);
230
+ }
231
+ const removed = await manager.removeAccount(accountId);
232
+ if (removed) {
233
+ console.log(`✅ Account ${accountId} removed successfully`);
234
+ }
235
+ else {
236
+ console.error(`❌ Account ${accountId} not found`);
237
+ process.exit(1);
238
+ }
239
+ }
240
+ /**
241
+ * Test account command
242
+ */
243
+ async function testAccountCommand(manager, accountId) {
244
+ if (!accountId) {
245
+ console.error('Error: Account ID required');
246
+ console.error('Usage: opencode-gemini-business test-account <account_id>');
247
+ process.exit(1);
248
+ }
249
+ const accounts = manager.getAccounts();
250
+ const account = accounts.find(acc => acc.id === accountId);
251
+ if (!account) {
252
+ console.error(`❌ Account ${accountId} not found`);
253
+ process.exit(1);
254
+ }
255
+ console.log(`Testing account: ${account.name} (${account.id})`);
256
+ console.log('Please wait...\n');
257
+ const api = new GeminiBusinessAPI(account);
258
+ const result = await api.testAccount();
259
+ if (result.success) {
260
+ console.log('✅ Account test successful!');
261
+ console.log(' XSRF Token: OK');
262
+ console.log(' Session: OK');
263
+ console.log(' Authentication: OK');
264
+ }
265
+ else {
266
+ console.log('❌ Account test failed!');
267
+ console.log(` Error: ${result.error}`);
268
+ process.exit(1);
269
+ }
270
+ }
271
+ /**
272
+ * Print help
273
+ */
274
+ function printHelp() {
275
+ console.log('OpenCode Gemini Business Plugin');
276
+ console.log('================================\n');
277
+ console.log('Multi-account Gemini Business pool with automatic rotation\n');
278
+ console.log('Commands:');
279
+ console.log(' add-account <name> <team_id> <secure_c_ses> <host_c_oses> <csesidx> [user_agent]');
280
+ console.log(' Add a new Gemini Business account\n');
281
+ console.log(' list-accounts');
282
+ console.log(' List all configured accounts\n');
283
+ console.log(' remove-account <account_id>');
284
+ console.log(' Remove an account\n');
285
+ console.log(' test-account <account_id>');
286
+ console.log(' Test account credentials\n');
287
+ console.log(' help');
288
+ console.log(' Show this help message\n');
289
+ console.log('Environment Variables:');
290
+ console.log(' GEMINI_ACCOUNT_NAME - Account name');
291
+ console.log(' GEMINI_TEAM_ID - Team ID');
292
+ console.log(' GEMINI_SECURE_C_SES - __Secure-c_ses cookie');
293
+ console.log(' GEMINI_HOST_C_OSES - __Host-c_oses cookie');
294
+ console.log(' GEMINI_CSESIDX - csesidx value');
295
+ console.log(' GEMINI_USER_AGENT - Custom user agent (optional)\n');
296
+ }
297
+ // Export CLI function
298
+ export { cli };
299
+ // Run CLI if executed directly
300
+ if (import.meta.url === `file://${process.argv[1]}`) {
301
+ cli().catch(error => {
302
+ console.error('Error:', error.message);
303
+ process.exit(1);
304
+ });
305
+ }
306
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Account Manager for multi-account rotation
3
+ */
4
+ import { GeminiBusinessAccount, PoolConfig } from './types.js';
5
+ export declare class AccountManager {
6
+ private config;
7
+ private currentIndex;
8
+ constructor(config?: Partial<PoolConfig>);
9
+ /**
10
+ * Load accounts from config file
11
+ */
12
+ loadAccounts(): Promise<void>;
13
+ /**
14
+ * Save accounts to config file
15
+ */
16
+ saveAccounts(): Promise<void>;
17
+ /**
18
+ * Add a new account
19
+ */
20
+ addAccount(account: Omit<GeminiBusinessAccount, 'id'>): Promise<string>;
21
+ /**
22
+ * Remove an account
23
+ */
24
+ removeAccount(id: string): Promise<boolean>;
25
+ /**
26
+ * Get next available account based on rotation strategy
27
+ */
28
+ getNextAccount(): GeminiBusinessAccount | null;
29
+ /**
30
+ * Mark account as failed
31
+ */
32
+ markAccountError(accountId: string, error: string): void;
33
+ /**
34
+ * Reset account errors (e.g., after successful request)
35
+ */
36
+ resetAccountErrors(accountId: string): void;
37
+ /**
38
+ * Check if session needs refresh
39
+ */
40
+ needsSessionRefresh(account: GeminiBusinessAccount): boolean;
41
+ /**
42
+ * Update account session
43
+ */
44
+ updateSession(accountId: string, sessionId: string, expiresIn: number): Promise<void>;
45
+ /**
46
+ * Update XSRF token
47
+ */
48
+ updateXSRFToken(accountId: string, token: string, expiresIn: number): Promise<void>;
49
+ /**
50
+ * Get all accounts
51
+ */
52
+ getAccounts(): GeminiBusinessAccount[];
53
+ /**
54
+ * Get config
55
+ */
56
+ getConfig(): PoolConfig;
57
+ }
58
+ //# sourceMappingURL=account-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-manager.d.ts","sourceRoot":"","sources":["../../src/account-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAS/D,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,YAAY,CAAa;gBAErB,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC;IAYxC;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBnC;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAWnC;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAc7E;;OAEG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjD;;OAEG;IACH,cAAc,IAAI,qBAAqB,GAAG,IAAI;IAqC9C;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAcxD;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ3C;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO;IAS5D;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3F;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASzF;;OAEG;IACH,WAAW,IAAI,qBAAqB,EAAE;IAItC;;OAEG;IACH,SAAS,IAAI,UAAU;CAGxB"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Account Manager for multi-account rotation
3
+ */
4
+ import { readFile, writeFile } from 'fs/promises';
5
+ import { existsSync } from 'fs';
6
+ import { homedir } from 'os';
7
+ import { join } from 'path';
8
+ const CONFIG_DIR = join(homedir(), '.config', 'opencode');
9
+ const ACCOUNTS_FILE = join(CONFIG_DIR, 'gemini-business-accounts.json');
10
+ export class AccountManager {
11
+ config;
12
+ currentIndex = 0;
13
+ constructor(config) {
14
+ this.config = {
15
+ accounts: [],
16
+ rotation_strategy: 'round-robin',
17
+ max_retries: 3,
18
+ retry_delay: 1000,
19
+ session_refresh_threshold: 300, // 5 minutes
20
+ error_threshold: 3,
21
+ ...config,
22
+ };
23
+ }
24
+ /**
25
+ * Load accounts from config file
26
+ */
27
+ async loadAccounts() {
28
+ if (!existsSync(ACCOUNTS_FILE)) {
29
+ console.warn(`No accounts file found at ${ACCOUNTS_FILE}`);
30
+ return;
31
+ }
32
+ try {
33
+ const data = await readFile(ACCOUNTS_FILE, 'utf-8');
34
+ const saved = JSON.parse(data);
35
+ this.config.accounts = saved.accounts || [];
36
+ // Merge other config options if they exist
37
+ if (saved.rotation_strategy)
38
+ this.config.rotation_strategy = saved.rotation_strategy;
39
+ if (saved.max_retries !== undefined)
40
+ this.config.max_retries = saved.max_retries;
41
+ console.log(`Loaded ${this.config.accounts.length} accounts`);
42
+ }
43
+ catch (error) {
44
+ console.error('Failed to load accounts:', error);
45
+ throw new Error(`Failed to load accounts: ${error}`);
46
+ }
47
+ }
48
+ /**
49
+ * Save accounts to config file
50
+ */
51
+ async saveAccounts() {
52
+ try {
53
+ const data = JSON.stringify(this.config, null, 2);
54
+ await writeFile(ACCOUNTS_FILE, data, 'utf-8');
55
+ console.log(`Saved ${this.config.accounts.length} accounts to ${ACCOUNTS_FILE}`);
56
+ }
57
+ catch (error) {
58
+ console.error('Failed to save accounts:', error);
59
+ throw new Error(`Failed to save accounts: ${error}`);
60
+ }
61
+ }
62
+ /**
63
+ * Add a new account
64
+ */
65
+ async addAccount(account) {
66
+ const id = `account-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
67
+ const newAccount = {
68
+ ...account,
69
+ id,
70
+ enabled: true,
71
+ error_count: 0,
72
+ };
73
+ this.config.accounts.push(newAccount);
74
+ await this.saveAccounts();
75
+ return id;
76
+ }
77
+ /**
78
+ * Remove an account
79
+ */
80
+ async removeAccount(id) {
81
+ const index = this.config.accounts.findIndex(acc => acc.id === id);
82
+ if (index === -1)
83
+ return false;
84
+ this.config.accounts.splice(index, 1);
85
+ await this.saveAccounts();
86
+ return true;
87
+ }
88
+ /**
89
+ * Get next available account based on rotation strategy
90
+ */
91
+ getNextAccount() {
92
+ const enabledAccounts = this.config.accounts.filter(acc => acc.enabled);
93
+ if (enabledAccounts.length === 0) {
94
+ console.error('No enabled accounts available');
95
+ return null;
96
+ }
97
+ let account;
98
+ switch (this.config.rotation_strategy) {
99
+ case 'round-robin':
100
+ account = enabledAccounts[this.currentIndex % enabledAccounts.length];
101
+ this.currentIndex++;
102
+ break;
103
+ case 'least-used':
104
+ account = enabledAccounts.reduce((least, current) => {
105
+ const leastUsed = least.last_used || 0;
106
+ const currentUsed = current.last_used || 0;
107
+ return currentUsed < leastUsed ? current : least;
108
+ });
109
+ break;
110
+ case 'random':
111
+ account = enabledAccounts[Math.floor(Math.random() * enabledAccounts.length)];
112
+ break;
113
+ default:
114
+ account = enabledAccounts[0];
115
+ }
116
+ // Update last used timestamp
117
+ account.last_used = Date.now();
118
+ return account;
119
+ }
120
+ /**
121
+ * Mark account as failed
122
+ */
123
+ markAccountError(accountId, error) {
124
+ const account = this.config.accounts.find(acc => acc.id === accountId);
125
+ if (!account)
126
+ return;
127
+ account.error_count = (account.error_count || 0) + 1;
128
+ account.last_error = error;
129
+ // Disable account if error threshold reached
130
+ if (account.error_count >= this.config.error_threshold) {
131
+ account.enabled = false;
132
+ console.warn(`Account ${account.name} (${accountId}) disabled after ${account.error_count} errors`);
133
+ }
134
+ }
135
+ /**
136
+ * Reset account errors (e.g., after successful request)
137
+ */
138
+ resetAccountErrors(accountId) {
139
+ const account = this.config.accounts.find(acc => acc.id === accountId);
140
+ if (!account)
141
+ return;
142
+ account.error_count = 0;
143
+ account.last_error = undefined;
144
+ }
145
+ /**
146
+ * Check if session needs refresh
147
+ */
148
+ needsSessionRefresh(account) {
149
+ if (!account.session_id || !account.session_expires)
150
+ return true;
151
+ const now = Date.now();
152
+ const expiresIn = account.session_expires - now;
153
+ return expiresIn < this.config.session_refresh_threshold * 1000;
154
+ }
155
+ /**
156
+ * Update account session
157
+ */
158
+ async updateSession(accountId, sessionId, expiresIn) {
159
+ const account = this.config.accounts.find(acc => acc.id === accountId);
160
+ if (!account)
161
+ return;
162
+ account.session_id = sessionId;
163
+ account.session_expires = Date.now() + expiresIn;
164
+ await this.saveAccounts();
165
+ }
166
+ /**
167
+ * Update XSRF token
168
+ */
169
+ async updateXSRFToken(accountId, token, expiresIn) {
170
+ const account = this.config.accounts.find(acc => acc.id === accountId);
171
+ if (!account)
172
+ return;
173
+ account.xsrf_token = token;
174
+ account.xsrf_expires = Date.now() + expiresIn;
175
+ await this.saveAccounts();
176
+ }
177
+ /**
178
+ * Get all accounts
179
+ */
180
+ getAccounts() {
181
+ return this.config.accounts;
182
+ }
183
+ /**
184
+ * Get config
185
+ */
186
+ getConfig() {
187
+ return this.config;
188
+ }
189
+ }
190
+ //# sourceMappingURL=account-manager.js.map