cnitlrt Claude Opus 4.6 (1M context) commited on
Commit
a80afa0
·
1 Parent(s): a60ba37

fix: get_team_member_count 失败兜底 + account_id UUID 校验 + rotate 超员自动清理

Browse files

- get_team_member_count 返回 -1 时用本地 active 数兜底,加详细错误日志
- get_chatgpt_account_id 校验 UUID 格式,state.json 存了 user ID 时 fallback 到 .env
- rotate 发现超员时自动清理多余的本地管理账号,优先移除额度最低的

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

src/autoteam/admin_state.py CHANGED
@@ -109,9 +109,20 @@ def get_admin_session_token():
109
  return load_admin_state().get("session_token", "")
110
 
111
 
 
 
 
 
 
 
 
112
  def get_chatgpt_account_id():
113
  state = load_admin_state()
114
- return state.get("account_id", "") or os.environ.get("CHATGPT_ACCOUNT_ID", "")
 
 
 
 
115
 
116
 
117
  def get_admin_password():
 
109
  return load_admin_state().get("session_token", "")
110
 
111
 
112
+ def _is_valid_uuid(value: str) -> bool:
113
+ """检查是否为有效的 UUID 格式"""
114
+ import re
115
+
116
+ return bool(re.match(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", value, re.I))
117
+
118
+
119
  def get_chatgpt_account_id():
120
  state = load_admin_state()
121
+ state_id = state.get("account_id", "")
122
+ # state.json 里的值必须是 UUID 格式才有效(user-xxx 是 user ID 不是 account ID)
123
+ if state_id and _is_valid_uuid(state_id):
124
+ return state_id
125
+ return os.environ.get("CHATGPT_ACCOUNT_ID", "")
126
 
127
 
128
  def get_admin_password():
src/autoteam/manager.py CHANGED
@@ -1172,12 +1172,39 @@ def cmd_rotate(target_seats=5):
1172
  if not chatgpt or not chatgpt.browser:
1173
  ensure_chatgpt()
1174
  api_count = get_team_member_count(chatgpt)
1175
- # API 有缓存延迟,移出后可能返回据,手动修正
1176
- current_count = max(0, api_count - removed_count) if api_count >= 0 else 0
 
 
 
 
 
 
 
1177
  vacancies = TARGET - current_count
1178
 
1179
  if vacancies <= 0:
1180
- logger.info("[4/5] Team 已满 (%d/%d)", current_count, TARGET)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1181
  return
1182
 
1183
  logger.info("[4/5] 填补 %d 个空缺 (当前 %d/%d)...", vacancies, current_count, TARGET)
@@ -1436,9 +1463,13 @@ def cmd_main_codex_sync():
1436
  def get_team_member_count(chatgpt_api):
1437
  """获取当前 Team 成员数"""
1438
  account_id = get_chatgpt_account_id()
 
 
 
1439
  path = f"/backend-api/accounts/{account_id}/users"
1440
  result = chatgpt_api._api_fetch("GET", path)
1441
  if result["status"] != 200:
 
1442
  return -1
1443
  data = json.loads(result["body"])
1444
  members = data.get("items", data.get("users", data.get("members", [])))
 
1172
  if not chatgpt or not chatgpt.browser:
1173
  ensure_chatgpt()
1174
  api_count = get_team_member_count(chatgpt)
1175
+ logger.info("[4/5] API 返回成员: %d(本轮移出: %d)", api_count, removed_count)
1176
+ if api_count <= 0:
1177
+ # API 返回异常,用本地 active 账号数兜底
1178
+ local_active = sum(1 for a in load_accounts() if a["status"] == STATUS_ACTIVE)
1179
+ logger.warning("[4/5] API 成员数异常 (%d),使用本地 active 数: %d", api_count, local_active)
1180
+ current_count = local_active
1181
+ else:
1182
+ # API 有缓存延迟,移出后可能返回旧数据,手动修正
1183
+ current_count = max(0, api_count - removed_count)
1184
  vacancies = TARGET - current_count
1185
 
1186
  if vacancies <= 0:
1187
+ excess = current_count - TARGET
1188
+ if excess > 0:
1189
+ logger.info("[4/5] Team 超员 (%d/%d),清理 %d 个多余成员...", current_count, TARGET, excess)
1190
+ # 只移除本地管理的账号,优先移除额度最低的
1191
+ all_accs = load_accounts()
1192
+ local_active = [a for a in all_accs if a["status"] == STATUS_ACTIVE]
1193
+ # 按额度排序,额度低的优先移除
1194
+ local_active.sort(key=lambda a: 100 - (a.get("last_quota") or {}).get("primary_pct", 0))
1195
+ removed = 0
1196
+ for acc in local_active:
1197
+ if removed >= excess:
1198
+ break
1199
+ email = acc["email"]
1200
+ if remove_from_team(chatgpt, email):
1201
+ update_account(email, status=STATUS_STANDBY)
1202
+ logger.info("[4/5] 超员清理: %s → standby", email)
1203
+ removed += 1
1204
+ if removed:
1205
+ logger.info("[4/5] 已清理 %d 个多余成员", removed)
1206
+ else:
1207
+ logger.info("[4/5] Team 已满 (%d/%d)", current_count, TARGET)
1208
  return
1209
 
1210
  logger.info("[4/5] 填补 %d 个空缺 (当前 %d/%d)...", vacancies, current_count, TARGET)
 
1463
  def get_team_member_count(chatgpt_api):
1464
  """获取当前 Team 成员数"""
1465
  account_id = get_chatgpt_account_id()
1466
+ if not account_id:
1467
+ logger.error("[Team] account_id 为空,无法查询成员数")
1468
+ return -1
1469
  path = f"/backend-api/accounts/{account_id}/users"
1470
  result = chatgpt_api._api_fetch("GET", path)
1471
  if result["status"] != 200:
1472
+ logger.error("[Team] 获取成员列表失败: %d %s", result["status"], result["body"][:200])
1473
  return -1
1474
  data = json.loads(result["body"])
1475
  members = data.get("items", data.get("users", data.get("members", [])))