Zhi Yang commited on
Commit
fcb2d84
·
1 Parent(s): 9af49ff

feat: support Playwright proxy configuration

Browse files
.env.example CHANGED
@@ -11,6 +11,12 @@ CPA_KEY=your_key
11
  # API 鉴权(不设置则不启用,任何人都可访问)
12
  API_KEY=
13
 
 
 
 
 
 
 
14
  # 轮询配置
15
  EMAIL_POLL_INTERVAL=3
16
  EMAIL_POLL_TIMEOUT=300
 
11
  # API 鉴权(不设置则不启用,任何人都可访问)
12
  API_KEY=
13
 
14
+ # Playwright 浏览器代理(可选)
15
+ # 示例:socks5://user:pass@host.docker.internal:1080
16
+ PLAYWRIGHT_PROXY_URL=
17
+ # 示例:localhost,127.0.0.1
18
+ PLAYWRIGHT_PROXY_BYPASS=
19
+
20
  # 轮询配置
21
  EMAIL_POLL_INTERVAL=3
22
  EMAIL_POLL_TIMEOUT=300
docker-compose.yml CHANGED
@@ -1,6 +1,8 @@
1
  services:
2
  autoteam:
3
  build: .
 
 
4
  ports:
5
  - "8787:8787"
6
  volumes:
 
1
  services:
2
  autoteam:
3
  build: .
4
+ extra_hosts:
5
+ - "host.docker.internal:host-gateway"
6
  ports:
7
  - "8787:8787"
8
  volumes:
docs/configuration.md CHANGED
@@ -17,10 +17,34 @@ cp .env.example .env
17
  | `CPA_URL` | CLIProxyAPI 地址 | 否(默认 `http://127.0.0.1:8317`) |
18
  | `CPA_KEY` | CPA 管理密钥 | 是 |
19
  | `API_KEY` | Web 面板 / API 鉴权密钥 | 否(首次启动自动生成) |
 
 
20
  | `AUTO_CHECK_THRESHOLD` | 额度低于此百分比触发轮转 | 否(默认 `10`) |
21
  | `AUTO_CHECK_INTERVAL` | 巡检间隔(秒) | 否(默认 `300`) |
22
  | `AUTO_CHECK_MIN_LOW` | 至少几个账号低于阈值才触发 | 否(默认 `2`) |
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  ### 内联注释
25
 
26
  `.env` 支持尾部内联注释,例如:
 
17
  | `CPA_URL` | CLIProxyAPI 地址 | 否(默认 `http://127.0.0.1:8317`) |
18
  | `CPA_KEY` | CPA 管理密钥 | 是 |
19
  | `API_KEY` | Web 面板 / API 鉴权密钥 | 否(首次启动自动生成) |
20
+ | `PLAYWRIGHT_PROXY_URL` | Playwright 浏览器代理 URL,如 `socks5://user:pass@host:port` | 否 |
21
+ | `PLAYWRIGHT_PROXY_BYPASS` | Playwright 代理绕过列表,如 `localhost,127.0.0.1` | 否 |
22
  | `AUTO_CHECK_THRESHOLD` | 额度低于此百分比触发轮转 | 否(默认 `10`) |
23
  | `AUTO_CHECK_INTERVAL` | 巡检间隔(秒) | 否(默认 `300`) |
24
  | `AUTO_CHECK_MIN_LOW` | 至少几个账号低于阈值才触发 | 否(默认 `2`) |
25
 
26
+ ## Playwright 代理
27
+
28
+ AutoTeam 的浏览器流量(ChatGPT 登录、邀请接受、Codex OAuth 等)现在支持单独配置代理。
29
+
30
+ 推荐优先使用一个环境变量:
31
+
32
+ ```dotenv
33
+ PLAYWRIGHT_PROXY_URL=socks5://host.docker.internal:1080
34
+ PLAYWRIGHT_PROXY_BYPASS=localhost,127.0.0.1
35
+ ```
36
+
37
+ 如果代理需要认证,也可以直接写进 URL:
38
+
39
+ ```dotenv
40
+ PLAYWRIGHT_PROXY_URL=socks5://username:password@host.docker.internal:1080
41
+ ```
42
+
43
+ 说明:
44
+
45
+ - `PLAYWRIGHT_PROXY_URL` 会被解析为 Playwright 所需的 `server` / `username` / `password` 字段
46
+ - `PLAYWRIGHT_PROXY_BYPASS` 建议至少包含 `localhost,127.0.0.1`,避免本地回调或容器内本地服务误走代理
47
+
48
  ### 内联注释
49
 
50
  `.env` 支持尾部内联注释,例如:
docs/getting-started.md CHANGED
@@ -118,6 +118,26 @@ docker compose restart
118
 
119
  直接打开 `http://your-server:8787`,会显示配置向导页面,在浏览器中填写。
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  ## 第三步:管理员登录
122
 
123
  配置完成后,需要先用 ChatGPT Team 管理员账号登录。
 
118
 
119
  直接打开 `http://your-server:8787`,会显示配置向导页面,在浏览器中填写。
120
 
121
+ 如果你需要让浏览器流量走宿主机 SOCKS5 代理,在 Linux Docker 下还需要给容器增加:
122
+
123
+ ```yaml
124
+ extra_hosts:
125
+ - "host.docker.internal:host-gateway"
126
+ ```
127
+
128
+ 然后在 `data/.env` 中加入:
129
+
130
+ ```dotenv
131
+ PLAYWRIGHT_PROXY_URL=socks5://host.docker.internal:1080
132
+ PLAYWRIGHT_PROXY_BYPASS=localhost,127.0.0.1
133
+ ```
134
+
135
+ 如果代理需要认证,可以写成:
136
+
137
+ ```dotenv
138
+ PLAYWRIGHT_PROXY_URL=socks5://username:password@host.docker.internal:1080
139
+ ```
140
+
141
  ## 第三步:管理员登录
142
 
143
  配置完成后,需要先用 ChatGPT Team 管理员账号登录。
docs/troubleshooting.md CHANGED
@@ -40,10 +40,11 @@ AUTO_CHECK_INTERVAL=300 # 5 分钟
40
  ### Codex OAuth 登录失败:未获取到 authorization code
41
 
42
  常见原因:
43
- 1. **IP 被标记** — VPS 的 IP 被 OpenAI / Cloudflare 拦截,建议换住宅代理
44
  2. **Cloudflare 验证** — 浏览器环境被检测,需更新 Chromium 或切换网络
45
  3. **workspace 选择失败** — 页面结构变化,查看 `screenshots/codex_04_*.png`
46
  4. **自动回调不可达** — 如果浏览器和 AutoTeam 不在同一台机器,`localhost:1455` 回调可能不会到达 AutoTeam,此时请改用手动粘贴回调 URL
 
47
 
48
  ### 登录后 plan 显示 free 而不是 team
49
 
@@ -167,6 +168,28 @@ volumes:
167
  - ./data:/app/data
168
  ```
169
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  ## Web 面板相关
171
 
172
  ### 页面显示 `JSON parse error`
 
40
  ### Codex OAuth 登录失败:未获取到 authorization code
41
 
42
  常见原因:
43
+ 1. **IP 被标记** — VPS 的 IP 被 OpenAI/Cloudflare 拦截,建议换住宅代理
44
  2. **Cloudflare 验证** — 浏览器环境被检测,需更新 Chromium 或切换网络
45
  3. **workspace 选择失败** — 页面结构变化,查看 `screenshots/codex_04_*.png`
46
  4. **自动回调不可达** — 如果浏览器和 AutoTeam 不在同一台机器,`localhost:1455` 回调可能不会到达 AutoTeam,此时请改用手动粘贴回调 URL
47
+ 5. **本地回调被代理拦截** — 如果启用了 `PLAYWRIGHT_PROXY_URL`,建议同时设置 `PLAYWRIGHT_PROXY_BYPASS=localhost,127.0.0.1`
48
 
49
  ### 登录后 plan 显示 free 而不是 team
50
 
 
168
  - ./data:/app/data
169
  ```
170
 
171
+ ### 容器里访问不到宿主机 SOCKS5 代理
172
+
173
+ 如果代理在宿主机上,比如 `host.docker.internal:1080`,Linux Docker 需要给容器补一条 host-gateway 映射:
174
+
175
+ ```yaml
176
+ extra_hosts:
177
+ - "host.docker.internal:host-gateway"
178
+ ```
179
+
180
+ 然后在 `data/.env` 中配置:
181
+
182
+ ```dotenv
183
+ PLAYWRIGHT_PROXY_URL=socks5://host.docker.internal:1080
184
+ PLAYWRIGHT_PROXY_BYPASS=localhost,127.0.0.1
185
+ ```
186
+
187
+ 如果代理需要认证,可以直接写成:
188
+
189
+ ```dotenv
190
+ PLAYWRIGHT_PROXY_URL=socks5://username:password@host.docker.internal:1080
191
+ ```
192
+
193
  ## Web 面板相关
194
 
195
  ### 页面显示 `JSON parse error`
src/autoteam/api.py CHANGED
@@ -74,6 +74,8 @@ class SetupConfig(BaseModel):
74
  CLOUDMAIL_DOMAIN: str = ""
75
  CPA_URL: str = "http://127.0.0.1:8317"
76
  CPA_KEY: str = ""
 
 
77
  API_KEY: str = ""
78
 
79
 
@@ -105,8 +107,9 @@ def post_setup_save(config: SetupConfig):
105
  if not data.get("API_KEY"):
106
  data["API_KEY"] = _secrets.token_urlsafe(24)
107
 
 
108
  for key, value in data.items():
109
- if value:
110
  _write_env(key, value)
111
  os.environ[key] = value
112
 
 
74
  CLOUDMAIL_DOMAIN: str = ""
75
  CPA_URL: str = "http://127.0.0.1:8317"
76
  CPA_KEY: str = ""
77
+ PLAYWRIGHT_PROXY_URL: str = ""
78
+ PLAYWRIGHT_PROXY_BYPASS: str = ""
79
  API_KEY: str = ""
80
 
81
 
 
107
  if not data.get("API_KEY"):
108
  data["API_KEY"] = _secrets.token_urlsafe(24)
109
 
110
+ clearable_fields = {"PLAYWRIGHT_PROXY_URL", "PLAYWRIGHT_PROXY_BYPASS"}
111
  for key, value in data.items():
112
+ if value or key in clearable_fields:
113
  _write_env(key, value)
114
  os.environ[key] = value
115
 
src/autoteam/chatgpt_api.py CHANGED
@@ -17,6 +17,7 @@ from autoteam.admin_state import (
17
  get_chatgpt_workspace_name,
18
  update_admin_state,
19
  )
 
20
  from autoteam.textio import read_text
21
 
22
  logger = logging.getLogger(__name__)
@@ -114,10 +115,7 @@ class ChatGPTTeamAPI:
114
  def _launch_browser(self):
115
  SCREENSHOT_DIR.mkdir(exist_ok=True)
116
  self.playwright = sync_playwright().start()
117
- self.browser = self.playwright.chromium.launch(
118
- headless=False,
119
- args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
120
- )
121
  self.context = self.browser.new_context(
122
  viewport={"width": 1280, "height": 800},
123
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
 
17
  get_chatgpt_workspace_name,
18
  update_admin_state,
19
  )
20
+ from autoteam.config import get_playwright_launch_options
21
  from autoteam.textio import read_text
22
 
23
  logger = logging.getLogger(__name__)
 
115
  def _launch_browser(self):
116
  SCREENSHOT_DIR.mkdir(exist_ok=True)
117
  self.playwright = sync_playwright().start()
118
+ self.browser = self.playwright.chromium.launch(**get_playwright_launch_options())
 
 
 
119
  self.context = self.browser.new_context(
120
  viewport={"width": 1280, "height": 800},
121
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
src/autoteam/codex_auth.py CHANGED
@@ -20,6 +20,7 @@ from autoteam.admin_state import (
20
  get_chatgpt_workspace_name,
21
  )
22
  from autoteam.auth_storage import AUTH_DIR, ensure_auth_dir, ensure_auth_file_permissions
 
23
  from autoteam.textio import write_text
24
 
25
  logger = logging.getLogger(__name__)
@@ -265,10 +266,7 @@ def login_codex_via_browser(email, password, mail_client=None):
265
  auth_code = None
266
 
267
  with sync_playwright() as p:
268
- browser = p.chromium.launch(
269
- headless=False,
270
- args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
271
- )
272
  context = browser.new_context(
273
  viewport={"width": 1280, "height": 800},
274
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
 
20
  get_chatgpt_workspace_name,
21
  )
22
  from autoteam.auth_storage import AUTH_DIR, ensure_auth_dir, ensure_auth_file_permissions
23
+ from autoteam.config import get_playwright_launch_options
24
  from autoteam.textio import write_text
25
 
26
  logger = logging.getLogger(__name__)
 
266
  auth_code = None
267
 
268
  with sync_playwright() as p:
269
+ browser = p.chromium.launch(**get_playwright_launch_options())
 
 
 
270
  context = browser.new_context(
271
  viewport={"width": 1280, "height": 800},
272
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
src/autoteam/config.py CHANGED
@@ -2,6 +2,7 @@
2
 
3
  import os
4
  from pathlib import Path
 
5
 
6
  from autoteam.textio import parse_env_line, parse_env_value, read_text
7
 
@@ -46,3 +47,62 @@ API_KEY = os.environ.get("API_KEY", "")
46
  AUTO_CHECK_INTERVAL = _get_int_env("AUTO_CHECK_INTERVAL", 300) # 巡检间隔(秒),默认 5 分钟
47
  AUTO_CHECK_THRESHOLD = _get_int_env("AUTO_CHECK_THRESHOLD", 10) # 额度低于此百分比触发轮转,默认 10%
48
  AUTO_CHECK_MIN_LOW = _get_int_env("AUTO_CHECK_MIN_LOW", 2) # 至少几个账号低于阈值才触发,默认 2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  import os
4
  from pathlib import Path
5
+ from urllib.parse import unquote, urlsplit
6
 
7
  from autoteam.textio import parse_env_line, parse_env_value, read_text
8
 
 
47
  AUTO_CHECK_INTERVAL = _get_int_env("AUTO_CHECK_INTERVAL", 300) # 巡检间隔(秒),默认 5 分钟
48
  AUTO_CHECK_THRESHOLD = _get_int_env("AUTO_CHECK_THRESHOLD", 10) # 额度低于此百分比触发轮转,默认 10%
49
  AUTO_CHECK_MIN_LOW = _get_int_env("AUTO_CHECK_MIN_LOW", 2) # 至少几个账号低于阈值才触发,默认 2
50
+
51
+ # Playwright 代理配置
52
+ PLAYWRIGHT_PROXY_URL = os.environ.get("PLAYWRIGHT_PROXY_URL", "").strip()
53
+ PLAYWRIGHT_PROXY_SERVER = os.environ.get("PLAYWRIGHT_PROXY_SERVER", "").strip()
54
+ PLAYWRIGHT_PROXY_USERNAME = os.environ.get("PLAYWRIGHT_PROXY_USERNAME", "").strip()
55
+ PLAYWRIGHT_PROXY_PASSWORD = os.environ.get("PLAYWRIGHT_PROXY_PASSWORD", "").strip()
56
+ PLAYWRIGHT_PROXY_BYPASS = os.environ.get("PLAYWRIGHT_PROXY_BYPASS", "").strip()
57
+
58
+
59
+ def _format_proxy_host(hostname: str) -> str:
60
+ if ":" in hostname and not hostname.startswith("["):
61
+ return f"[{hostname}]"
62
+ return hostname
63
+
64
+
65
+ def _parse_proxy_url(proxy_url: str):
66
+ if "://" not in proxy_url:
67
+ return {"server": proxy_url}
68
+
69
+ parsed = urlsplit(proxy_url)
70
+ if not parsed.scheme or not parsed.hostname:
71
+ return {"server": proxy_url}
72
+
73
+ host = _format_proxy_host(parsed.hostname)
74
+ server = f"{parsed.scheme}://{host}"
75
+ if parsed.port:
76
+ server = f"{server}:{parsed.port}"
77
+
78
+ proxy = {"server": server}
79
+ if parsed.username:
80
+ proxy["username"] = unquote(parsed.username)
81
+ if parsed.password:
82
+ proxy["password"] = unquote(parsed.password)
83
+ return proxy
84
+
85
+
86
+ def get_playwright_launch_options():
87
+ """统一的 Playwright Chromium 启动参数。"""
88
+ options = {
89
+ "headless": False,
90
+ "args": ["--disable-blink-features=AutomationControlled", "--no-sandbox"],
91
+ }
92
+
93
+ proxy = None
94
+ if PLAYWRIGHT_PROXY_URL:
95
+ proxy = _parse_proxy_url(PLAYWRIGHT_PROXY_URL)
96
+ elif PLAYWRIGHT_PROXY_SERVER:
97
+ proxy = {"server": PLAYWRIGHT_PROXY_SERVER}
98
+ if PLAYWRIGHT_PROXY_USERNAME:
99
+ proxy["username"] = PLAYWRIGHT_PROXY_USERNAME
100
+ if PLAYWRIGHT_PROXY_PASSWORD:
101
+ proxy["password"] = PLAYWRIGHT_PROXY_PASSWORD
102
+
103
+ if proxy:
104
+ if PLAYWRIGHT_PROXY_BYPASS:
105
+ proxy["bypass"] = PLAYWRIGHT_PROXY_BYPASS
106
+ options["proxy"] = proxy
107
+
108
+ return options
src/autoteam/invite.py CHANGED
@@ -26,6 +26,7 @@ from playwright.sync_api import sync_playwright
26
 
27
  from autoteam.chatgpt_api import ChatGPTTeamAPI
28
  from autoteam.cloudmail import CloudMailClient
 
29
 
30
  logger = logging.getLogger(__name__)
31
 
@@ -403,10 +404,7 @@ def run():
403
  logger.info("[邀请] 开始注册 ChatGPT 账号")
404
 
405
  with sync_playwright() as p:
406
- browser = p.chromium.launch(
407
- headless=False,
408
- args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
409
- )
410
  context = browser.new_context(
411
  viewport={"width": 1280, "height": 800},
412
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
 
26
 
27
  from autoteam.chatgpt_api import ChatGPTTeamAPI
28
  from autoteam.cloudmail import CloudMailClient
29
+ from autoteam.config import get_playwright_launch_options
30
 
31
  logger = logging.getLogger(__name__)
32
 
 
404
  logger.info("[邀请] 开始注册 ChatGPT 账号")
405
 
406
  with sync_playwright() as p:
407
+ browser = p.chromium.launch(**get_playwright_launch_options())
 
 
 
408
  context = browser.new_context(
409
  viewport={"width": 1280, "height": 800},
410
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
src/autoteam/manager.py CHANGED
@@ -57,6 +57,7 @@ from autoteam.codex_auth import (
57
  refresh_main_auth_file,
58
  save_auth_file,
59
  )
 
60
  from autoteam.cpa_sync import sync_from_cpa, sync_main_codex_to_cpa, sync_to_cpa
61
  from autoteam.textio import read_text, write_text
62
 
@@ -665,10 +666,7 @@ def _complete_registration(email, password, invite_link, mail_client):
665
 
666
  logger.info("[注册] 开始注册 %s...", email)
667
  with sync_playwright() as p:
668
- browser = p.chromium.launch(
669
- headless=False,
670
- args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
671
- )
672
  context = browser.new_context(
673
  viewport={"width": 1280, "height": 800},
674
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
@@ -1140,13 +1138,9 @@ def _register_direct_once(mail_client, email, password, cloudmail_account_id=Non
1140
  signup_url = "https://chatgpt.com/auth/login"
1141
 
1142
  with sync_playwright() as p:
1143
- launch_kwargs = {
1144
- "headless": False,
1145
- "args": ["--disable-blink-features=AutomationControlled", "--no-sandbox"],
1146
- }
1147
  if sys.platform.startswith("win"):
1148
  launch_kwargs["slow_mo"] = 100
1149
-
1150
  browser = p.chromium.launch(**launch_kwargs)
1151
  context = browser.new_context(
1152
  viewport={"width": 1280, "height": 800},
@@ -1503,10 +1497,7 @@ def reinvite_account(chatgpt_api, mail_client, acc):
1503
  chatgpt_api.stop()
1504
 
1505
  with sync_playwright() as p:
1506
- browser = p.chromium.launch(
1507
- headless=False,
1508
- args=["--disable-blink-features=AutomationControlled", "--no-sandbox"],
1509
- )
1510
  context = browser.new_context(
1511
  viewport={"width": 1280, "height": 800},
1512
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
 
57
  refresh_main_auth_file,
58
  save_auth_file,
59
  )
60
+ from autoteam.config import get_playwright_launch_options
61
  from autoteam.cpa_sync import sync_from_cpa, sync_main_codex_to_cpa, sync_to_cpa
62
  from autoteam.textio import read_text, write_text
63
 
 
666
 
667
  logger.info("[注册] 开始注册 %s...", email)
668
  with sync_playwright() as p:
669
+ browser = p.chromium.launch(**get_playwright_launch_options())
 
 
 
670
  context = browser.new_context(
671
  viewport={"width": 1280, "height": 800},
672
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
 
1138
  signup_url = "https://chatgpt.com/auth/login"
1139
 
1140
  with sync_playwright() as p:
1141
+ launch_kwargs = get_playwright_launch_options()
 
 
 
1142
  if sys.platform.startswith("win"):
1143
  launch_kwargs["slow_mo"] = 100
 
1144
  browser = p.chromium.launch(**launch_kwargs)
1145
  context = browser.new_context(
1146
  viewport={"width": 1280, "height": 800},
 
1497
  chatgpt_api.stop()
1498
 
1499
  with sync_playwright() as p:
1500
+ browser = p.chromium.launch(**get_playwright_launch_options())
 
 
 
1501
  context = browser.new_context(
1502
  viewport={"width": 1280, "height": 800},
1503
  user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36",
src/autoteam/setup_wizard.py CHANGED
@@ -22,6 +22,8 @@ REQUIRED_CONFIGS = [
22
  ("CLOUDMAIL_DOMAIN", "CloudMail 邮箱域名(如 @example.com)", "", False),
23
  ("CPA_URL", "CPA (CLIProxyAPI) 地址", "http://127.0.0.1:8317", True),
24
  ("CPA_KEY", "CPA 管理密钥", "", False),
 
 
25
  ("API_KEY", "API 鉴权密钥(回车自动生成)", "", True),
26
  ]
27
 
@@ -79,7 +81,7 @@ def check_and_setup(interactive: bool = True) -> bool:
79
 
80
  for key, prompt, default, optional in REQUIRED_CONFIGS:
81
  val = env.get(key, "") or os.environ.get(key, "")
82
- if not val:
83
  missing.append((key, prompt, default, optional))
84
 
85
  if not missing:
 
22
  ("CLOUDMAIL_DOMAIN", "CloudMail 邮箱域名(如 @example.com)", "", False),
23
  ("CPA_URL", "CPA (CLIProxyAPI) 地址", "http://127.0.0.1:8317", True),
24
  ("CPA_KEY", "CPA 管理密钥", "", False),
25
+ ("PLAYWRIGHT_PROXY_URL", "Playwright 浏览器代理 URL(可选,如 socks5://host:port)", "", True),
26
+ ("PLAYWRIGHT_PROXY_BYPASS", "Playwright 代理绕过列表(可选,如 localhost,127.0.0.1)", "", True),
27
  ("API_KEY", "API 鉴权密钥(回车自动生成)", "", True),
28
  ]
29
 
 
81
 
82
  for key, prompt, default, optional in REQUIRED_CONFIGS:
83
  val = env.get(key, "") or os.environ.get(key, "")
84
+ if not val and not optional:
85
  missing.append((key, prompt, default, optional))
86
 
87
  if not missing: