Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,15 @@ sudo docker compose down

#### 🌐 代理配置

| 变量名 | 描述 | 默认值 |
| :------------------------------ | :--------------------------------------------------- | :-------- |
| `INITIAL_AUTH_INDEX` | 启动时使用的初始身份验证索引。 | `0` |
| `MAX_RETRIES` | 请求失败后的最大重试次数(仅对假流式和非流式生效)。 | `3` |
| `RETRY_DELAY` | 两次重试之间的间隔(毫秒)。 | `2000` |
| `SWITCH_ON_USES` | 自动切换帐户前允许的请求次数(设为 `0` 禁用)。 | `40` |
| `FAILURE_THRESHOLD` | 切换帐户前允许的连续失败次数(设为 `0` 禁用)。 | `3` |
| `IMMEDIATE_SWITCH_STATUS_CODES` | 触发立即切换帐户的 HTTP 状态码(逗号分隔)。 | `429,503` |
| 变量名 | 描述 | 默认值 |
| :------------------------------ | :---------------------------------------------------------------------------------------------------------- | :-------- |
| `INITIAL_AUTH_INDEX` | 启动时使用的初始身份验证索引。 | `0` |
| `ENABLE_AUTH_UPDATE` | 是否启用自动保存凭证更新。设为 `true` 启用后,将在每次登录/切换账号成功时以及每 24 小时自动更新 auth 文件。 | `false` |
| `MAX_RETRIES` | 请求失败后的最大重试次数(仅对假流式和非流式生效)。 | `3` |
| `RETRY_DELAY` | 两次重试之间的间隔(毫秒)。 | `2000` |
| `SWITCH_ON_USES` | 自动切换帐户前允许的请求次数(设为 `0` 禁用)。 | `40` |
| `FAILURE_THRESHOLD` | 切换帐户前允许的连续失败次数(设为 `0` 禁用)。 | `3` |
| `IMMEDIATE_SWITCH_STATUS_CODES` | 触发立即切换帐户的 HTTP 状态码(逗号分隔)。 | `429,503` |

#### 🗒️ 其他配置

Expand Down
17 changes: 9 additions & 8 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,15 @@ This endpoint is forwarded to the official Gemini API format endpoint.

#### 🌐 Proxy Configuration

| Variable | Description | Default |
| :------------------------------ | :--------------------------------------------------------------------------------------------------- | :-------- |
| `INITIAL_AUTH_INDEX` | Initial authentication index to use on startup. | `0` |
| `MAX_RETRIES` | Maximum number of retries for failed requests (only effective for fake streaming and non-streaming). | `3` |
| `RETRY_DELAY` | Delay between retries in milliseconds. | `2000` |
| `SWITCH_ON_USES` | Number of requests before automatically switching accounts (`0` to disable). | `40` |
| `FAILURE_THRESHOLD` | Number of consecutive failures before switching accounts (`0` to disable). | `3` |
| `IMMEDIATE_SWITCH_STATUS_CODES` | HTTP status codes that trigger immediate account switching (comma-separated). | `429,503` |
| Variable | Description | Default |
| :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------- |
| `INITIAL_AUTH_INDEX` | Initial authentication index to use on startup. | `0` |
| `ENABLE_AUTH_UPDATE` | Whether to enable automatic auth credential updates. If set to `true`, the auth file will be automatically updated upon successful login/account switch and every 24 hours. | `false` |
| `MAX_RETRIES` | Maximum number of retries for failed requests (only effective for fake streaming and non-streaming). | `3` |
| `RETRY_DELAY` | Delay between retries in milliseconds. | `2000` |
| `SWITCH_ON_USES` | Number of requests before automatically switching accounts (`0` to disable). | `40` |
| `FAILURE_THRESHOLD` | Number of consecutive failures before switching accounts (`0` to disable). | `3` |
| `IMMEDIATE_SWITCH_STATUS_CODES` | HTTP status codes that trigger immediate account switching (comma-separated). | `429,503` |

#### 🗒️ Other Configuration

Expand Down
2 changes: 1 addition & 1 deletion src/auth/AuthSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class AuthSource {
validIndices.push(index);
this.accountNameMap.set(index, authData.accountName || null);
} catch (e) {
invalidSourceDescriptions.push(`auth-${index}`);
invalidSourceDescriptions.push(`auth-${index} (parse error)`);
}
} else {
invalidSourceDescriptions.push(`auth-${index} (unreadable)`);
Expand Down
63 changes: 62 additions & 1 deletion src/core/BrowserManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,51 @@ class BrowserManager {
this._currentAuthIndex = value;
}

/**
* Feature: Update authentication file
* Writes the current storageState back to the auth file, effectively extending session validity.
* @param {number} authIndex - The auth index to update
*/
async _updateAuthFile(authIndex) {
if (!this.context) return;

// Check availability of auto-update feature from config
if (!this.config.enableAuthUpdate) {
return;
}

try {
const configDir = path.join(process.cwd(), "configs", "auth");
const authFilePath = path.join(configDir, `auth-${authIndex}.json`);

// Read original file content to preserve all fields (e.g. accountName, custom fields)
// Relies on AuthSource validation (checks valid index AND file existence)
const authData = this.authSource.getAuth(authIndex);
if (!authData) {
this.logger.warn(
`[Auth Update] Auth source #${authIndex} returned no data (invalid index or file missing), skipping update.`
);
return;
}

const storageState = await this.context.storageState();

// Merge new credentials into existing data
authData.cookies = storageState.cookies;
authData.origins = storageState.origins;

// Note: We do NOT force-set accountName. If it was there, it stays; if not, it remains missing.
// This preserves the "missing state" as requested.

// Overwrite the file with merged data
await fs.promises.writeFile(authFilePath, JSON.stringify(authData, null, 2));

this.logger.info(`[Auth Update] 💾 Successfully updated auth credentials for account #${authIndex}`);
} catch (error) {
this.logger.error(`[Auth Update] ❌ Failed to update auth file: ${error.message}`);
}
}

/**
* Interface: Notify user activity
* Used to force wake up the Launch detection when a request comes in
Expand Down Expand Up @@ -305,7 +350,19 @@ class BrowserManager {
}
}

// 2. Popup & Overlay Cleanup
// 3. Auto-Save Auth: Every ~24 hours (21600 ticks * 4s = 86400s)
if (tickCount % 21600 === 0) {
if (this._currentAuthIndex !== -1) {
try {
this.logger.info("[HealthMonitor] 💾 Triggering daily periodic auth file update...");
await this._updateAuthFile(this._currentAuthIndex);
} catch (e) {
this.logger.warn(`[HealthMonitor] Auth update failed: ${e.message}`);
}
}
}

// 4. Popup & Overlay Cleanup
await page.evaluate(() => {
const blockers = [
"div.cdk-overlay-backdrop",
Expand Down Expand Up @@ -887,6 +944,10 @@ class BrowserManager {
this._startHealthMonitor();
this._startBackgroundWakeup();
this._currentAuthIndex = authIndex;

// [Auth Update] Save the refreshed cookies to the auth file immediately
await this._updateAuthFile(authIndex);

this.logger.info("==================================================");
this.logger.info(`✅ [Browser] Account ${authIndex} context initialized successfully!`);
this.logger.info("✅ [Browser] Browser client is ready.");
Expand Down
4 changes: 4 additions & 0 deletions src/utils/ConfigLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ConfigLoader {
apiKeys: [],
apiKeySource: "Not set",
browserExecutablePath: null,
enableAuthUpdate: false,
failureThreshold: 3,
forceThinking: false,
forceUrlContext: false,
Expand Down Expand Up @@ -59,6 +60,8 @@ class ConfigLoader {
if (process.env.FORCE_WEB_SEARCH) config.forceWebSearch = process.env.FORCE_WEB_SEARCH.toLowerCase() === "true";
if (process.env.FORCE_URL_CONTEXT)
config.forceUrlContext = process.env.FORCE_URL_CONTEXT.toLowerCase() === "true";
if (process.env.ENABLE_AUTH_UPDATE)
config.enableAuthUpdate = process.env.ENABLE_AUTH_UPDATE.toLowerCase() === "true";

let rawCodes = process.env.IMMEDIATE_SWITCH_STATUS_CODES;
let codesSource = "environment variable";
Expand Down Expand Up @@ -132,6 +135,7 @@ class ConfigLoader {
this.logger.info(` Force Thinking: ${config.forceThinking}`);
this.logger.info(` Force Web Search: ${config.forceWebSearch}`);
this.logger.info(` Force URL Context: ${config.forceUrlContext}`);
this.logger.info(` Auto Update Auth: ${config.enableAuthUpdate}`);
this.logger.info(
` Usage-based Switch Threshold: ${
config.switchOnUses > 0 ? `Switch after every ${config.switchOnUses} requests` : "Disabled"
Expand Down