你们使用cliiproxy中转站的时候,很多账号有问题,api请求会有很多失败怎么办?
- 内容介绍
- 文章标签
- 相关推荐
我加了一些chatgpt账号到中转站,感觉有很多失效的号或者没额度的,我看了下记录,api请求会很多失败,我觉得这样会影响工作效率,有没有哪个中转站可以自动识别失效的账号,跳过这些没额度的,只请求有效账号的额度?
sub2api可以吗 ?求大佬解答一下
--【壹】--:
有个之前站里发的油猴脚本可以批量检测清理
// ==UserScript==
// @name CPA AuthFiles Universal Cleaner
// @namespace openai_registor
// @version 2.2.0
// @description 一键优化:删除失效,启用健康,禁用无额度
// @author local
// @match *://*/management.html*
// @grant none
// ==/UserScript==
(function () {
"use strict";
const BASE_ORIGIN = window.location.origin;
const AUTH_FILES_URL = `${BASE_ORIGIN}/v0/management/auth-files`;
const AUTH_FILES_STATUS_URL = `${BASE_ORIGIN}/v0/management/auth-files/status`;
const API_CALL_URL = `${BASE_ORIGIN}/v0/management/api-call`;
const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
const DEFAULT_UA = "codex_cli_rs/universal (Windows)";
const QUERY_CONCURRENCY = 4;
const PAGE_SIZE = 20;
const TOKEN_CACHE_KEY = "tm_auth_token";
const CAPTURED_TOKEN_KEY = "tm_last_bearer_token_v1";
const CLI_PROXY_AUTH_KEY = "cli-proxy-auth";
const ENC_PREFIX = "enc::v1::";
const ENC_SEED = "cli-proxy-api-webui::secure-storage";
let runtimeToken = "";
let manualPromptBlockedUntil = 0;
let pendingFiles = [];
let inventorySnapshot = {
loaded: false,
total: 0,
};
function isActiveStatus(status) {
return String(status || "").trim().toLowerCase() === "active";
}
function buildInventorySnapshot(files) {
return {
loaded: true,
total: files.length,
};
}
function applyDeletedItems(items) {
if (!inventorySnapshot.loaded || !Array.isArray(items) || !items.length) return;
inventorySnapshot.total = Math.max(0, inventorySnapshot.total - items.length);
}
function getSystemStatusText(item) {
return isActiveStatus(item?.status) ? "健康" : "失效";
}
function getBackendStatusLabel(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
if (rawStatus === "active") return "正常";
if (rawStatus === "disabled") return "已禁用";
if (rawStatus === "error") return "出错";
if (rawStatus === "pending") return "等待完成";
if (rawStatus === "refreshing") return "刷新中";
if (rawStatus === "unknown") return "状态未知";
return item?.status || "未知状态";
}
function getBackendStatusTone(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
if (rawStatus === "active") return "ok";
if (rawStatus === "pending" || rawStatus === "refreshing" || rawStatus === "unknown") return "warn";
if (rawStatus === "disabled" || rawStatus === "error") return "fail";
return "idle";
}
function getBackendReasonText(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
const rawMessage = String(item?.statusMessage || "").trim().toLowerCase();
if (rawStatus === "active") return "后台认为它目前正常。";
if (rawStatus === "disabled") {
if (rawMessage === "disabled via management api") return "后台原因:这个号被手动禁用了。";
if (rawMessage === "removed via management api") return "后台原因:这个号被管理后台移除过。";
return "后台原因:这个号目前被标记为禁用。";
}
if (rawStatus === "pending") return "后台原因:这个号还在等待外部操作,比如登录确认或验证。";
if (rawStatus === "refreshing") return "后台原因:这个号正在刷新中,暂时还没恢复到正常状态。";
if (rawStatus === "unknown") return "后台原因:后台暂时判断不出它是不是正常。";
if (rawMessage === "unauthorized") return "后台原因:登录失效,或者凭证已经失效。";
if (rawMessage === "payment_required") return "后台原因:权限、套餐或付费状态有问题。";
if (rawMessage === "not_found") return "后台原因:后台没找到它需要访问的资源。";
if (rawMessage === "quota exhausted") return "后台原因:额度已经用完了。";
if (rawMessage === "context canceled") return "后台原因:请求被中断了,通常是超时、切换或上游主动取消。";
if (rawMessage === "transient upstream error") return "后台原因:上游服务临时出错。";
if (rawMessage === "request failed") return "后台原因:请求失败,但后台没有给更细的分类。";
if (rawStatus === "error") {
if (rawMessage) return `后台原因:${item.statusMessage}`;
return "后台原因:这个号请求时报错了。";
}
if (rawMessage) return `后台原因:${item.statusMessage}`;
if (item?.disabled) return "后台原因:这个号被标记为禁用。";
if (item?.unavailable) return "后台原因:这个号暂时不可用。";
return "后台原因:不是 active,但后台没有返回更明确的说明。";
}
function getBackendReasonTone(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
const rawMessage = String(item?.statusMessage || "").trim().toLowerCase();
if (rawStatus === "active") return "ok";
if (["pending", "refreshing", "unknown"].includes(rawStatus)) return "warn";
if (["disabled", "error"].includes(rawStatus)) return "fail";
if (["unauthorized", "payment_required", "not_found", "quota exhausted", "context canceled", "transient upstream error", "request failed"].includes(rawMessage)) return "fail";
return "idle";
}
function isCodexChannel(item) {
return normalizeChannelKey(item?.channel) === "codex";
}
function supportsActiveCheck(item) {
return isCodexChannel(item);
}
function isQuotaState(item) {
return item?.queryState === "quota";
}
function isHealthyState(item) {
return item?.queryState === "ok";
}
function isFailedState(item) {
return item?.queryState === "failed";
}
function getEnabledStateText(item) {
return item?.disabled ? "已禁用" : "已启用";
}
function getEnabledStateTone(item) {
return item?.disabled ? "fail" : "ok";
}
function getActualResultText(item) {
if (!supportsActiveCheck(item)) return "仅展示";
if (isHealthyState(item)) return "健康";
if (isQuotaState(item)) return "无额度";
if (isFailedState(item)) {
if (String(item?.lastStatusCode || "") === "NO_AUTH") return "失效";
return "失效";
}
return "待检查";
}
function getActualResultTone(item) {
if (!supportsActiveCheck(item)) return "idle";
if (isHealthyState(item)) return "ok";
if (isQuotaState(item)) return "warn";
if (isFailedState(item)) {
if (String(item?.lastStatusCode || "") === "NO_AUTH") return "warn";
return "fail";
}
return "idle";
}
function getDeleteAdviceText(item) {
if (isHealthyState(item)) return "建议保留,它目前是健康且有额度的";
if (isQuotaState(item)) return "建议禁用,它目前可用但没有额度";
if (isFailedState(item)) return "可考虑删除,它目前已判定为失效";
return "先检查再决定,现在不建议删除";
}
function getUsageText(item) {
if (!supportsActiveCheck(item)) return "该渠道仅展示,不参与主动检测或自动删除";
if (item?.queryState === "unqueried") return "等待 Codex 检测";
if (isQuotaState(item)) return `额度已用尽 · 重置 ${item?.resetText || "-"}`;
if (isFailedState(item)) return `检测失败 · 返回 ${item?.lastStatusCode == null ? "--" : String(item.lastStatusCode)}`;
const used = item?.usedPercent === null ? "未知" : `${item.usedPercent}%`;
return `Codex 已用 ${used} · 重置 ${item?.resetText || "-"}`;
}
function getBadgeText(item) {
if (isHealthyState(item)) return "实测结果:健康";
if (isQuotaState(item)) return "实测结果:无额度";
if (isFailedState(item)) return "实测结果:失效";
return "实测结果:待检查";
}
function getTechDetailText(item) {
const authIndex = item?.authIndex || "-";
const statusCode = item?.lastStatusCode == null ? "--" : String(item.lastStatusCode);
return `authIndex=${authIndex} | 返回码=${statusCode}`;
}
function normalizeChannelKey(value) {
return String(value || "").trim().toLowerCase();
}
function inferChannelFromName(name) {
const raw = String(name || "").trim().toLowerCase();
if (!raw) return "unknown";
if (raw.includes("qwen")) return "qwen";
if (raw.includes("codex")) return "codex";
if (raw.includes("kiro")) return "kiro";
if (raw.includes("claude")) return "claude";
if (raw.includes("gemini")) return "gemini";
if (raw.includes("openai")) return "openai";
return "unknown";
}
function extractChannel(item) {
const directKeys = ["channel", "provider", "type", "model_provider", "modelProvider"];
for (const key of directKeys) {
const value = normalizeChannelKey(item?.[key]);
if (value) return value;
}
const nested = [
item?.id_token?.provider,
item?.idToken?.provider,
item?.id_token?.channel,
item?.idToken?.channel,
];
for (const value of nested) {
const normalized = normalizeChannelKey(value);
if (normalized) return normalized;
}
return inferChannelFromName(extractFileName(item));
}
function formatChannelLabel(channel) {
const key = normalizeChannelKey(channel);
if (!key || key === "all") return "全部渠道";
if (key === "unknown") return "未分类";
const pretty = {
qwen: "Qwen",
codex: "Codex",
kiro: "Kiro",
claude: "Claude",
gemini: "Gemini",
openai: "OpenAI",
};
return pretty[key] || key;
}
function getChannelCounts(items) {
const counts = new Map();
for (const item of items) {
const key = normalizeChannelKey(item?.channel) || "unknown";
counts.set(key, (counts.get(key) || 0) + 1);
}
return counts;
}
function persistToken(token) {
const t = String(token || "").trim();
if (!t) return "";
runtimeToken = t;
try { localStorage.setItem(TOKEN_CACHE_KEY, t); } catch (_) {}
try { sessionStorage.setItem(TOKEN_CACHE_KEY, t); } catch (_) {}
try { localStorage.setItem(CAPTURED_TOKEN_KEY, t); } catch (_) {}
return t;
}
function decodeCliProxyAuth(raw) {
if (!raw) return "";
if (!raw.startsWith(ENC_PREFIX)) return raw;
try {
const keyText = `${ENC_SEED}|${window.location.host}|${navigator.userAgent}`;
const key = new TextEncoder().encode(keyText);
const bin = atob(raw.slice(ENC_PREFIX.length));
const data = Uint8Array.from(bin, (ch) => ch.charCodeAt(0));
const out = new Uint8Array(data.length);
for (let i = 0; i < data.length; i += 1) out[i] = data[i] ^ key[i % key.length];
return new TextDecoder().decode(out);
} catch (_) {
return "";
}
}
function tryReadTokensFromCliProxyAuth() {
try {
const raw = localStorage.getItem(CLI_PROXY_AUTH_KEY);
const decoded = decodeCliProxyAuth(raw);
if (!decoded) return [];
const obj = JSON.parse(decoded);
const cands = [
obj?.state?.managementKey,
obj?.state?.adminKey,
obj?.state?.authToken,
obj?.state?.token,
obj?.state?.password,
obj?.managementKey,
obj?.adminKey,
obj?.authToken,
obj?.token,
];
return cands
.filter((x) => typeof x === "string" && x.trim())
.map((x) => String(x).trim());
} catch (_) {
return [];
}
}
function tryReadTokensFromStorage() {
const keys = [
TOKEN_CACHE_KEY,
CAPTURED_TOKEN_KEY,
"tm_token",
"auth_token",
"management_token",
"management_key",
"admin_key",
];
const tokens = [];
for (const k of keys) {
try {
const v = localStorage.getItem(k) || sessionStorage.getItem(k);
if (v && String(v).trim()) tokens.push(String(v).trim());
} catch (_) {}
}
return tokens;
}
function installTokenSniffer() {
if (window.__tmUniversalSnifferInstalled) return;
window.__tmUniversalSnifferInstalled = true;
const origFetch = window.fetch.bind(window);
window.fetch = async function (input, init) {
try {
let headers = null;
if (init && init.headers) headers = new Headers(init.headers);
else if (input && typeof input === "object" && "headers" in input) headers = new Headers(input.headers);
if (headers) {
const auth = headers.get("authorization") || headers.get("Authorization");
if (auth && /^Bearer\s+/i.test(auth)) persistToken(auth.replace(/^Bearer\s+/i, ""));
}
} catch (_) {}
return origFetch(input, init);
};
const origSetHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
try {
if (/^authorization$/i.test(String(name || "")) && /^Bearer\s+/i.test(String(value || ""))) {
persistToken(String(value).replace(/^Bearer\s+/i, ""));
}
} catch (_) {}
return origSetHeader.call(this, name, value);
};
}
function collectTokenCandidates() {
const all = [
runtimeToken,
...tryReadTokensFromCliProxyAuth(),
...tryReadTokensFromStorage(),
].map((x) => String(x || "").trim()).filter(Boolean);
const uniq = [];
const seen = new Set();
for (const t of all) {
if (seen.has(t)) continue;
seen.add(t);
uniq.push(t);
}
return uniq;
}
function clearTokenCacheValue(token) {
const t = String(token || "").trim();
if (!t) return;
if (runtimeToken === t) runtimeToken = "";
const keys = [
TOKEN_CACHE_KEY,
CAPTURED_TOKEN_KEY,
"tm_token",
"auth_token",
"management_token",
"management_key",
"admin_key",
];
for (const k of keys) {
try {
if (localStorage.getItem(k) === t) localStorage.removeItem(k);
} catch (_) {}
try {
if (sessionStorage.getItem(k) === t) sessionStorage.removeItem(k);
} catch (_) {}
}
}
function isUnauthorizedError(err) {
const status = Number(err?.status);
if (status === 401) return true;
const msg = String(err?.message || "");
return msg.includes("HTTP 401");
}
function buildHttpError(message, status, rawText) {
const e = new Error(message);
e.status = status;
e.rawText = rawText || "";
return e;
}
async function callWithAuthRetry(opName, fn) {
try {
return await fn("");
} catch (err) {
if (!isUnauthorizedError(err)) throw err;
}
const tried = new Set();
let lastErr = null;
const candidates = collectTokenCandidates();
for (const token of candidates) {
if (!token || tried.has(token)) continue;
tried.add(token);
try {
const result = await fn(token);
persistToken(token);
return result;
} catch (err) {
lastErr = err;
if (isUnauthorizedError(err)) {
clearTokenCacheValue(token);
continue;
}
throw err;
}
}
const now = Date.now();
if (now < manualPromptBlockedUntil) {
if (lastErr) throw lastErr;
throw new Error(`${opName}失败:没有可用密钥`);
}
const manual = window.prompt(
`当前登录已失效。\n请输入“管理后台登录密钥”(就是你打开 management 页面时输入的那串密码)后重试:\n场景:${opName}`,
runtimeToken || ""
);
if (manual && String(manual).trim()) {
const token = persistToken(String(manual).trim());
try {
return await fn(token);
} catch (err) {
if (isUnauthorizedError(err)) clearTokenCacheValue(token);
throw err;
}
}
manualPromptBlockedUntil = Date.now() + 10000;
if (lastErr) throw lastErr;
throw new Error(`${opName}失败:没有可用密钥`);
}
function safeJson(text) {
try { return JSON.parse(text); } catch (_) { return null; }
}
function extractFileName(item) {
for (const key of ["name", "id", "filename", "file_name"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function extractAuthIndex(item) {
for (const key of ["authIndex", "auth_index", "authindex"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function extractChatgptAccountId(item) {
const nested = item?.id_token?.chatgpt_account_id || item?.idToken?.chatgpt_account_id;
if (nested) return String(nested);
for (const key of ["chatgpt_account_id", "chatgptAccountId", "account_id", "accountId"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function getHeaders(token) {
const headers = {
accept: "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
referer: `${BASE_ORIGIN}/management.html`,
};
if (token && String(token).trim()) {
headers.authorization = `Bearer ${String(token).trim()}`;
}
return headers;
}
function normalizeFilesPayload(data) {
const files = Array.isArray(data?.files)
? data.files.filter((x) => x && typeof x === "object")
: Array.isArray(data)
? data.filter((x) => x && typeof x === "object")
: [];
return files.map((item) => ({
name: extractFileName(item) || "(no-name)",
authIndex: extractAuthIndex(item) || "",
status: String(item?.status ?? ""),
statusMessage: String(item?.status_message ?? item?.statusMessage ?? ""),
disabled: Boolean(item?.disabled),
unavailable: Boolean(item?.unavailable),
channel: extractChannel(item),
chatgptAccountId: extractChatgptAccountId(item) || "",
usedPercent: null,
resetText: "-",
lastStatusCode: null,
queryState: "unqueried",
hasQuota: null,
deleteEligible: false,
}));
}
async function fetchAllFiles(token) {
const resp = await fetch(AUTH_FILES_URL, {
method: "GET",
headers: getHeaders(token),
credentials: "include",
});
if (!resp.ok) {
const raw = await resp.text();
throw buildHttpError(`获取失败: HTTP ${resp.status}`, resp.status, raw);
}
const data = await resp.json();
return normalizeFilesPayload(data);
}
async function fetchNonActive(token) {
const all = await fetchAllFiles(token);
return all.filter((x) => String(x.status || "").toLowerCase() !== "active");
}
async function deleteByName(token, name) {
const url = `${AUTH_FILES_URL}?name=${encodeURIComponent(name)}`;
const resp = await fetch(url, {
method: "DELETE",
headers: getHeaders(token),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) throw buildHttpError(`删除失败: HTTP 401`, 401, text);
const data = safeJson(text);
const ok = resp.status === 200 && (!data || data.status === "ok");
return { ok, status: resp.status, raw: text };
}
async function patchAuthFileDisabledAt(token, url, name, disabled) {
const resp = await fetch(url, {
method: "PATCH",
headers: { ...getHeaders(token), "content-type": "application/json" },
body: JSON.stringify({ name, disabled: Boolean(disabled) }),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) throw buildHttpError(`更新启用状态失败: HTTP 401`, 401, text);
if (!resp.ok) throw buildHttpError(`更新启用状态失败: HTTP ${resp.status}`, resp.status, text);
const data = safeJson(text);
if (data && data.status && data.status !== "ok") {
throw buildHttpError(`更新启用状态失败: ${data.status}`, resp.status, text);
}
return { ok: true, status: resp.status, raw: text, data };
}
async function patchAuthFileDisabled(token, name, disabled) {
try {
return await patchAuthFileDisabledAt(token, AUTH_FILES_URL, name, disabled);
} catch (err) {
if (Number(err?.status) !== 404) throw err;
}
return patchAuthFileDisabledAt(token, AUTH_FILES_STATUS_URL, name, disabled);
}
async function queryUsageByAuthIndex(token, fileItem) {
if (!fileItem?.authIndex) {
return { ok: false, statusCode: "NO_AUTH", bodyObj: null, bodyText: "missing authIndex" };
}
const header = {
Authorization: "Bearer $TOKEN$",
"Content-Type": "application/json",
"User-Agent": DEFAULT_UA,
};
if (fileItem.chatgptAccountId) header["Chatgpt-Account-Id"] = fileItem.chatgptAccountId;
const body = {
authIndex: fileItem.authIndex,
method: "GET",
url: USAGE_URL,
header,
};
const resp = await fetch(API_CALL_URL, {
method: "POST",
headers: { ...getHeaders(token), "content-type": "application/json" },
body: JSON.stringify(body),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) {
throw buildHttpError(`余额查询失败: HTTP 401`, 401, text);
}
const data = safeJson(text);
const statusCode = data?.status_code ?? data?.statusCode ?? null;
let bodyObj = null;
let bodyText = "";
if (typeof data?.body === "string") {
bodyText = data.body;
const parsed = safeJson(data.body);
if (parsed && typeof parsed === "object") bodyObj = parsed;
} else if (data?.body && typeof data.body === "object") {
bodyObj = data.body;
bodyText = JSON.stringify(data.body, null, 2);
} else {
bodyText = text;
}
const result = {
ok: resp.ok && Number(statusCode) === 200,
statusCode,
bodyObj,
bodyText,
};
return result;
}
function normalizeUsedPercent(value) {
const num = Number(value);
return Number.isFinite(num) ? Math.max(0, Math.min(100, num)) : null;
}
function formatUsageResetText(windowInfo) {
if (windowInfo?.reset_at !== undefined && windowInfo?.reset_at !== null) {
const ts = Number(windowInfo.reset_at);
if (Number.isFinite(ts)) return new Date(ts * 1000).toLocaleString();
}
if (windowInfo?.reset_after_seconds !== undefined && windowInfo?.reset_after_seconds !== null) {
const sec = Number(windowInfo.reset_after_seconds);
if (Number.isFinite(sec)) return `${sec}s`;
}
return "-";
}
function parseUsageSnapshot(bodyObj) {
const rateLimit = bodyObj?.rate_limit;
const windows = [
{ label: "5h", data: rateLimit?.primary_window },
{ label: "7d", data: rateLimit?.secondary_window },
].filter((entry) => entry.data && typeof entry.data === "object");
let displayWindow = windows[0] || null;
let maxUsedPercent = null;
for (const entry of windows) {
const usedPercent = normalizeUsedPercent(entry?.data?.used_percent);
if (usedPercent === null) continue;
if (maxUsedPercent === null || usedPercent > maxUsedPercent) {
maxUsedPercent = usedPercent;
displayWindow = entry;
}
}
const limitReached = Boolean(rateLimit?.limit_reached) || rateLimit?.allowed === false || windows.some((entry) => {
const usedPercent = normalizeUsedPercent(entry?.data?.used_percent);
return usedPercent !== null && usedPercent >= 100;
});
const hasQuota = limitReached ? false : true;
return {
usedPercent: maxUsedPercent,
resetText: formatUsageResetText(displayWindow?.data),
hasQuota,
limitReached,
displayWindowLabel: displayWindow?.label || "",
};
}
function isQuotaResult(statusCode, bodyObj, bodyText) {
const code = Number(statusCode);
const text = `${JSON.stringify(bodyObj || {})} ${String(bodyText || "")}`.toLowerCase();
if (bodyObj?.rate_limit?.limit_reached === true) return true;
if (bodyObj?.rate_limit?.allowed === false) return true;
if (text.includes("quota exhausted")) return true;
if (text.includes("limit reached")) return true;
if (text.includes("payment_required")) return true;
return code === 402;
}
function markQueryResult(item, result, usageSnapshot) {
item.lastStatusCode = result?.statusCode;
item.usedPercent = usageSnapshot?.usedPercent ?? null;
item.resetText = usageSnapshot?.resetText || "-";
item.hasQuota = usageSnapshot?.hasQuota ?? null;
if (Number(result?.statusCode) === 200) {
item.queryState = usageSnapshot?.hasQuota === false ? "quota" : "ok";
item.deleteEligible = false;
return;
}
if (isQuotaResult(result?.statusCode, result?.bodyObj, result?.bodyText)) {
item.queryState = "quota";
item.deleteEligible = false;
item.hasQuota = false;
return;
}
item.queryState = "failed";
item.deleteEligible = true;
item.hasQuota = null;
}
function markQueryFailed(item, statusCode) {
item.lastStatusCode = statusCode ?? "ERR";
item.usedPercent = null;
item.resetText = "-";
item.hasQuota = null;
item.queryState = "failed";
item.deleteEligible = true;
}
function collectStats(items) {
const stats = { total: items.length, unqueried: 0, healthy: 0, quota: 0, failed: 0, deletable: 0 };
for (const item of items) {
if (!supportsActiveCheck(item)) continue;
if (isHealthyState(item)) stats.healthy += 1;
else if (isQuotaState(item)) stats.quota += 1;
else if (isFailedState(item)) stats.failed += 1;
else stats.unqueried += 1;
if (item.deleteEligible) stats.deletable += 1;
}
return stats;
}
async function runWithConcurrency(items, limit, worker) {
if (!items.length) return;
let cursor = 0;
const n = Math.max(1, Math.min(limit, items.length));
const runners = Array.from({ length: n }, async () => {
while (true) {
const idx = cursor;
cursor += 1;
if (idx >= items.length) break;
await worker(items[idx], idx);
}
});
await Promise.all(runners);
}
function createSidebar() {
if (document.getElementById("__tm_universal_panel")) return;
const DESKTOP_PANEL_WIDTH = 920;
const panel = document.createElement("div");
panel.id = "__tm_universal_panel";
panel.style.display = "flex";
panel.style.flexDirection = "column";
panel.style.position = "fixed";
panel.style.zIndex = "99999";
panel.style.backdropFilter = "blur(18px) saturate(120%)";
panel.style.borderRadius = "20px";
panel.style.padding = "12px";
panel.style.overflow = "hidden";
panel.style.willChange = "transform";
panel.style.transition = "transform 0.26s ease, opacity 0.22s ease";
panel.style.fontFamily = "'MiSans','PingFang SC','HarmonyOS Sans SC','Microsoft YaHei UI','Noto Sans SC',sans-serif";
panel.style.gap = "8px";
panel.style.transformOrigin = "right top";
panel.style.pointerEvents = "none";
panel.style.visibility = "hidden";
panel.style.opacity = "0";
function createSegmentWrap() {
const wrap = document.createElement("div");
wrap.style.display = "inline-flex";
wrap.style.alignItems = "center";
wrap.style.gap = "4px";
wrap.style.padding = "4px";
wrap.style.borderRadius = "14px";
wrap.style.boxSizing = "border-box";
wrap.style.minWidth = "0";
return wrap;
}
function createActionBtn(text, role) {
const btn = document.createElement("button");
btn.type = "button";
btn.dataset.role = role;
btn.dataset.text = text;
btn.textContent = text;
btn.style.padding = "10px 14px";
btn.style.borderRadius = "12px";
btn.style.cursor = "pointer";
btn.style.fontSize = "12px";
btn.style.fontWeight = "700";
btn.style.letterSpacing = "0.02em";
btn.style.transition = "transform 0.2s ease, opacity 0.2s ease, box-shadow 0.2s ease";
btn.addEventListener("mouseenter", () => {
if (!btn.disabled) btn.style.transform = "translateY(-1px)";
});
btn.addEventListener("mouseleave", () => {
btn.style.transform = "translateY(0)";
});
return btn;
}
function createSegmentBtn(text) {
const btn = document.createElement("button");
btn.type = "button";
btn.textContent = text;
btn.style.flex = "0 0 auto";
btn.style.whiteSpace = "nowrap";
btn.style.padding = "7px 12px";
btn.style.borderRadius = "10px";
btn.style.fontSize = "11px";
btn.style.fontWeight = "700";
btn.style.lineHeight = "1";
btn.style.cursor = "pointer";
btn.style.transition = "background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease";
btn.addEventListener("mouseenter", () => {
if (!btn.disabled) btn.style.transform = "translateY(-1px)";
});
btn.addEventListener("mouseleave", () => {
btn.style.transform = "translateY(0)";
});
return btn;
}
function createStat(label) {
const box = document.createElement("div");
box.style.display = "inline-flex";
box.style.alignItems = "baseline";
box.style.gap = "6px";
box.style.padding = "7px 10px";
box.style.borderRadius = "999px";
box.style.whiteSpace = "nowrap";
const name = document.createElement("span");
name.textContent = label;
name.style.fontSize = "11px";
name.style.fontWeight = "600";
const value = document.createElement("span");
value.textContent = "0";
value.style.fontSize = "14px";
value.style.fontWeight = "800";
box.appendChild(name);
box.appendChild(value);
return { box, name, value };
}
const topBar = document.createElement("div");
topBar.style.display = "flex";
topBar.style.flexDirection = "column";
topBar.style.gap = "8px";
panel.appendChild(topBar);
const actionRow = document.createElement("div");
actionRow.style.display = "flex";
actionRow.style.alignItems = "center";
actionRow.style.justifyContent = "space-between";
actionRow.style.gap = "8px";
actionRow.style.flexWrap = "wrap";
topBar.appendChild(actionRow);
const actionGroup = document.createElement("div");
actionGroup.style.display = "flex";
actionGroup.style.alignItems = "center";
actionGroup.style.gap = "8px";
actionGroup.style.flexWrap = "wrap";
actionRow.appendChild(actionGroup);
const btnBatchQuery = createActionBtn("全量检查", "query");
const btnQuickCheck = createActionBtn("快速检查", "query");
const btnBatchDelete = createActionBtn("一键优化", "delete");
btnBatchQuery.title = "读取全部 Codex 凭证并执行完整检查";
btnQuickCheck.title = "只读取后台已标成异常的凭证并执行检查";
btnBatchDelete.title = "基于当前检查结果执行删除失效、启用健康、禁用无额度";
actionGroup.appendChild(btnBatchQuery);
actionGroup.appendChild(btnQuickCheck);
actionGroup.appendChild(btnBatchDelete);
const actionHint = document.createElement("div");
actionHint.textContent = "说明:全量检查会检查全部,快速检查只看后台异常,一键优化会删除失效、启用健康、禁用无额度";
actionHint.style.fontSize = "11px";
actionHint.style.fontWeight = "600";
actionHint.style.lineHeight = "1.5";
actionHint.style.marginLeft = "2px";
actionGroup.appendChild(actionHint);
const statsRow = document.createElement("div");
statsRow.style.display = "flex";
statsRow.style.alignItems = "center";
statsRow.style.justifyContent = "flex-end";
statsRow.style.gap = "6px";
statsRow.style.flexWrap = "wrap";
actionRow.appendChild(statsRow);
const metricTotal = createStat("总数");
const metricHealthy = createStat("健康");
const metricNoQuota = createStat("无额度");
const metricFailed = createStat("失效");
statsRow.appendChild(metricTotal.box);
statsRow.appendChild(metricHealthy.box);
statsRow.appendChild(metricNoQuota.box);
statsRow.appendChild(metricFailed.box);
const progressText = document.createElement("div");
progressText.style.fontSize = "11px";
progressText.style.fontWeight = "600";
progressText.style.whiteSpace = "nowrap";
progressText.style.display = "none";
statsRow.appendChild(progressText);
const toolRow = document.createElement("div");
toolRow.style.display = "flex";
toolRow.style.alignItems = "center";
toolRow.style.justifyContent = "space-between";
toolRow.style.gap = "8px";
toolRow.style.flexWrap = "wrap";
topBar.appendChild(toolRow);
const leftTabs = document.createElement("div");
leftTabs.style.display = "flex";
leftTabs.style.alignItems = "center";
leftTabs.style.gap = "8px";
leftTabs.style.flexWrap = "wrap";
leftTabs.style.minWidth = "0";
leftTabs.style.flex = "1";
toolRow.appendChild(leftTabs);
const channelTabs = createSegmentWrap();
channelTabs.style.overflowX = "auto";
channelTabs.style.overflowY = "hidden";
channelTabs.style.maxWidth = "100%";
channelTabs.style.scrollbarWidth = "thin";
leftTabs.appendChild(channelTabs);
channelTabs.addEventListener("wheel", (event) => {
if (!event || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
channelTabs.scrollLeft += event.deltaY;
event.preventDefault();
}, { passive: false });
const healthTabs = createSegmentWrap();
leftTabs.appendChild(healthTabs);
const pagerWrap = createSegmentWrap();
toolRow.appendChild(pagerWrap);
const prevBtn = createSegmentBtn("上一页");
const pageText = document.createElement("div");
pageText.textContent = "1 / 1";
pageText.style.fontSize = "11px";
pageText.style.fontWeight = "700";
pageText.style.padding = "0 8px";
pageText.style.whiteSpace = "nowrap";
const nextBtn = createSegmentBtn("下一页");
pagerWrap.appendChild(prevBtn);
pagerWrap.appendChild(pageText);
pagerWrap.appendChild(nextBtn);
const list = document.createElement("div");
list.style.flex = "1";
list.style.minHeight = "0";
list.style.overflow = "auto";
list.style.padding = "6px";
list.style.borderRadius = "16px";
list.style.backdropFilter = "blur(8px)";
panel.appendChild(list);
const toggleBtn = document.createElement("button");
toggleBtn.style.position = "fixed";
toggleBtn.style.zIndex = "100000";
toggleBtn.style.borderRadius = "999px";
toggleBtn.style.padding = "9px 13px";
toggleBtn.style.cursor = "pointer";
toggleBtn.style.fontSize = "12px";
toggleBtn.style.fontWeight = "700";
toggleBtn.style.backdropFilter = "blur(10px)";
toggleBtn.style.transition = "right 0.26s ease, bottom 0.26s ease, transform 0.2s ease";
toggleBtn.addEventListener("mouseenter", () => {
toggleBtn.style.transform = "translateY(-1px)";
});
toggleBtn.addEventListener("mouseleave", () => {
toggleBtn.style.transform = "translateY(0)";
});
let isOpen = false;
const mediaQuery = window.matchMedia("(max-width: 768px)");
let isMobile = mediaQuery.matches;
const colorQuery = window.matchMedia("(prefers-color-scheme: dark)");
const THEME_STORAGE_KEY = "cli-proxy-theme";
let lastThemeStoreRaw = "";
const THEME_TOKENS = {
dark: {
panelText: "#f4f0e9",
panelBg: "linear-gradient(152deg, rgba(64, 62, 58, 0.76), rgba(32, 33, 38, 0.82))",
panelBorder: "1px solid rgba(228, 217, 204, 0.22)",
panelShadow: "0 16px 42px rgba(10, 10, 14, 0.35)",
softBg: "rgba(37, 39, 44, 0.62)",
softBorder: "1px solid rgba(223,214,204,0.18)",
metricBg: "rgba(65, 67, 74, 0.62)",
metricBorder: "1px solid rgba(231,222,211,0.12)",
metricLabelText: "#d9d0c5",
metricValueText: "#fff8ef",
statusText: "#f2ece3",
summaryText: "#d6d0c9",
tipText: "#dcc5a9",
listBg: "rgba(23, 24, 28, 0.42)",
listBorder: "1px solid rgba(223,214,204,0.18)",
toggleBg: "linear-gradient(145deg, rgba(86, 88, 99, 0.9), rgba(60, 62, 72, 0.92))",
toggleText: "#f4efe8",
toggleBorder: "1px solid rgba(224,214,203,0.24)",
toggleShadow: "0 10px 24px rgba(11, 13, 18, 0.34)",
emptyText: "#d2c9be",
cardBg: "linear-gradient(154deg, rgba(73, 71, 67, 0.36), rgba(44, 45, 50, 0.44))",
cardBorder: "1px solid rgba(228,218,206,0.2)",
titleText: "#f7f2ea",
metaText: "#d6cec3",
usageText: "#e8dfd4",
badgeOkBg: "rgba(78, 123, 112, 0.56)",
badgeOkText: "#d7f0e5",
badgeWarnBg: "rgba(146, 118, 67, 0.6)",
badgeWarnText: "#fff0c9",
badgeFailBg: "rgba(145, 88, 84, 0.62)",
badgeFailText: "#ffe2de",
badgeIdleBg: "rgba(117, 114, 112, 0.58)",
badgeIdleText: "#f0e8de",
rowQueryBg: "linear-gradient(135deg, rgba(83, 129, 120, 0.9), rgba(70, 107, 118, 0.9))",
rowDeleteBg: "linear-gradient(135deg, rgba(152, 87, 92, 0.9), rgba(124, 72, 82, 0.9))",
rowBtnBorder: "1px solid rgba(231,222,211,0.16)",
rowBtnText: "#fffdf9",
topBtnBorder: "1px solid rgba(225,216,205,0.2)",
topBtnText: "#fff9f1",
topBtnBg: {
query: "linear-gradient(135deg, rgba(83, 105, 139, 0.92), rgba(71, 90, 120, 0.94))",
delete: "linear-gradient(135deg, rgba(148, 86, 92, 0.94), rgba(124, 72, 84, 0.94))",
},
},
light: {
panelText: "#2d2a26",
panelBg: "linear-gradient(156deg, rgba(255, 252, 246, 0.9), rgba(244, 241, 236, 0.92))",
panelBorder: "1px solid rgba(165, 156, 144, 0.42)",
panelShadow: "0 14px 30px rgba(126, 119, 108, 0.18)",
softBg: "rgba(255, 251, 245, 0.86)",
softBorder: "1px solid rgba(171, 163, 152, 0.28)",
metricBg: "rgba(255, 255, 252, 0.92)",
metricBorder: "1px solid rgba(177, 168, 157, 0.28)",
metricLabelText: "#666058",
metricValueText: "#25221e",
statusText: "#312d28",
summaryText: "#4f5f79",
tipText: "#8a5d3a",
listBg: "rgba(255, 254, 250, 0.74)",
listBorder: "1px solid rgba(171, 163, 152, 0.35)",
toggleBg: "linear-gradient(145deg, rgba(251, 250, 247, 0.96), rgba(239, 236, 230, 0.94))",
toggleText: "#5d6273",
toggleBorder: "1px solid rgba(164, 156, 145, 0.48)",
toggleShadow: "0 8px 18px rgba(136, 129, 118, 0.2)",
emptyText: "#5f5a53",
cardBg: "linear-gradient(155deg, rgba(255, 255, 252, 0.88), rgba(247, 244, 238, 0.84))",
cardBorder: "1px solid rgba(177, 168, 157, 0.3)",
titleText: "#25221e",
metaText: "#5c564f",
usageText: "#3d3832",
badgeOkBg: "rgba(212, 237, 227, 0.92)",
badgeOkText: "#24564c",
badgeWarnBg: "rgba(250, 233, 187, 0.96)",
badgeWarnText: "#7a5a16",
badgeFailBg: "rgba(247, 216, 210, 0.92)",
badgeFailText: "#7b3537",
badgeIdleBg: "rgba(233, 227, 218, 0.96)",
badgeIdleText: "#554f48",
rowQueryBg: "linear-gradient(135deg, rgba(95, 150, 140, 0.9), rgba(76, 124, 131, 0.92))",
rowDeleteBg: "linear-gradient(135deg, rgba(196, 112, 119, 0.9), rgba(170, 90, 102, 0.9))",
rowBtnBorder: "1px solid rgba(126, 117, 107, 0.24)",
rowBtnText: "#fffdfa",
topBtnBorder: "1px solid rgba(127, 119, 109, 0.24)",
topBtnText: "#fffdf9",
topBtnBg: {
query: "linear-gradient(135deg, rgba(102, 134, 176, 0.92), rgba(84, 113, 155, 0.92))",
delete: "linear-gradient(135deg, rgba(198, 111, 121, 0.92), rgba(171, 91, 103, 0.92))",
},
},
};
function readThemeFromProjectStore() {
try {
const raw = localStorage.getItem(THEME_STORAGE_KEY);
if (!raw) return null;
lastThemeStoreRaw = raw;
const parsed = JSON.parse(raw);
const resolved = parsed?.state?.resolvedTheme || parsed?.resolvedTheme;
if (resolved === "dark" || resolved === "light") return resolved;
const theme = parsed?.state?.theme || parsed?.theme;
if (theme === "dark" || theme === "light") return theme;
} catch (_) {}
return null;
}
function readThemeFromDom() {
try {
const dt = (document.documentElement.getAttribute("data-theme") || "").toLowerCase();
if (dt === "dark") return "dark";
if (dt === "light") return "light";
} catch (_) {}
return null;
}
function resolveTheme() {
const domTheme = readThemeFromDom();
if (domTheme) return domTheme;
const storeTheme = readThemeFromProjectStore();
if (storeTheme) return storeTheme;
return colorQuery.matches ? "dark" : "light";
}
let activeTheme = resolveTheme();
let panelBusy = false;
let currentChannelFilter = "all";
let currentPage = 1;
let currentHealthFilter = "all";
function token() {
return THEME_TOKENS[activeTheme] || THEME_TOKENS.dark;
}
function setProgress(text) {
progressText.textContent = text || "";
progressText.style.display = text ? "block" : "none";
}
function styleSegmentButton(btn, active, tone) {
const t = token();
const toneName = tone || "neutral";
const activeBg = toneName === "delete" ? t.topBtnBg.delete : toneName === "ok" ? t.rowQueryBg : t.topBtnBg.query;
const activeBorder = toneName === "delete" ? t.rowBtnBorder : t.topBtnBorder;
btn.style.border = active ? activeBorder : "1px solid transparent";
btn.style.background = active ? activeBg : "transparent";
btn.style.color = active ? t.rowBtnText : t.panelText;
btn.style.boxShadow = active ? "0 8px 18px rgba(0,0,0,0.12)" : "none";
btn.style.opacity = btn.disabled ? "0.45" : active ? "1" : "0.92";
}
function applyTheme(themeName) {
if (!THEME_TOKENS[themeName]) return;
activeTheme = themeName;
const t = token();
panel.style.color = t.panelText;
panel.style.background = t.panelBg;
panel.style.border = t.panelBorder;
panel.style.boxShadow = t.panelShadow;
[metricTotal, metricHealthy, metricNoQuota, metricFailed].forEach((metric) => {
metric.box.style.background = t.metricBg;
metric.box.style.border = t.metricBorder;
metric.box.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.04)";
metric.name.style.color = t.metricLabelText;
metric.value.style.color = t.metricValueText;
});
progressText.style.color = t.summaryText;
actionHint.style.color = t.summaryText;
[channelTabs, healthTabs, pagerWrap].forEach((wrap) => {
wrap.style.background = t.softBg;
wrap.style.border = t.softBorder;
wrap.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.04)";
});
pageText.style.color = t.summaryText;
list.style.background = t.listBg;
list.style.border = t.listBorder;
toggleBtn.style.background = t.toggleBg;
toggleBtn.style.color = t.toggleText;
toggleBtn.style.border = t.toggleBorder;
toggleBtn.style.boxShadow = t.toggleShadow;
[btnBatchQuery, btnQuickCheck, btnBatchDelete].forEach((btn) => {
const role = btn.dataset.role || "query";
btn.style.border = t.topBtnBorder;
btn.style.color = t.topBtnText;
btn.style.background = t.topBtnBg[role] || t.topBtnBg.query;
btn.style.boxShadow = "0 12px 24px rgba(0,0,0,0.14)";
});
}
function syncTheme() {
applyTheme(resolveTheme());
scheduleRender();
}
function setBusy(busy, role) {
panelBusy = Boolean(busy);
[btnBatchQuery, btnQuickCheck, btnBatchDelete].forEach((btn) => {
const isBusyBtn = panelBusy && (btn.dataset.role === (role || "query"));
btn.disabled = panelBusy;
btn.style.opacity = panelBusy ? "0.74" : "1";
btn.style.cursor = panelBusy ? "not-allowed" : "pointer";
btn.textContent = isBusyBtn ? `${btn.dataset.text}...` : btn.dataset.text;
});
}
function getFilteredItems(items) {
const key = normalizeChannelKey(currentChannelFilter);
let result = !key || key === "all" ? items.slice() : items.filter((item) => normalizeChannelKey(item?.channel) === key);
if (currentHealthFilter === "failed") result = result.filter((item) => isFailedState(item));
if (currentHealthFilter === "healthy") result = result.filter((item) => isHealthyState(item));
if (currentHealthFilter === "quota") result = result.filter((item) => isQuotaState(item));
return result;
}
function getTotalPages(items) {
return Math.max(1, Math.ceil(items.length / PAGE_SIZE));
}
function getPageItems(items) {
const totalPages = getTotalPages(items);
currentPage = Math.min(Math.max(1, currentPage), totalPages);
const start = (currentPage - 1) * PAGE_SIZE;
return items.slice(start, start + PAGE_SIZE);
}
function updateSummary() {
const s = collectStats(pendingFiles);
const totalValue = inventorySnapshot.loaded ? inventorySnapshot.total : pendingFiles.length;
metricTotal.value.textContent = String(totalValue || 0);
metricHealthy.value.textContent = String(s.healthy);
metricNoQuota.value.textContent = String(s.quota);
metricFailed.value.textContent = String(s.failed);
}
function renderChannelTabs() {
currentChannelFilter = "all";
channelTabs.innerHTML = "";
channelTabs.style.display = "none";
}
function renderHealthTabs() {
const options = [
{ value: "all", label: "全部", tone: "query" },
{ value: "healthy", label: "健康", tone: "ok" },
{ value: "quota", label: "无额度", tone: "query" },
{ value: "failed", label: "失效", tone: "delete" },
];
healthTabs.innerHTML = "";
options.forEach((option) => {
const btn = createSegmentBtn(option.label);
const isActive = option.value === currentHealthFilter;
styleSegmentButton(btn, isActive, option.tone);
btn.addEventListener("click", () => {
if (currentHealthFilter === option.value) return;
currentHealthFilter = option.value;
currentPage = 1;
scheduleRender();
});
healthTabs.appendChild(btn);
});
}
function renderPager(totalPages) {
pageText.textContent = `${currentPage} / ${totalPages}`;
prevBtn.disabled = currentPage <= 1 || panelBusy;
nextBtn.disabled = currentPage >= totalPages || panelBusy;
styleSegmentButton(prevBtn, true, "query");
styleSegmentButton(nextBtn, true, "query");
prevBtn.style.opacity = prevBtn.disabled ? "0.45" : "1";
nextBtn.style.opacity = nextBtn.disabled ? "0.45" : "1";
prevBtn.style.cursor = prevBtn.disabled ? "not-allowed" : "pointer";
nextBtn.style.cursor = nextBtn.disabled ? "not-allowed" : "pointer";
}
let renderQueued = false;
function scheduleRender() {
if (renderQueued) return;
renderQueued = true;
requestAnimationFrame(() => {
renderQueued = false;
renderList();
});
}
function renderList() {
const t = token();
function applyToneBadge(el, tone) {
if (tone === "ok") {
el.style.background = t.badgeOkBg;
el.style.color = t.badgeOkText;
return;
}
if (tone === "warn") {
el.style.background = t.badgeWarnBg;
el.style.color = t.badgeWarnText;
return;
}
if (tone === "fail") {
el.style.background = t.badgeFailBg;
el.style.color = t.badgeFailText;
return;
}
el.style.background = t.badgeIdleBg;
el.style.color = t.badgeIdleText;
}
updateSummary();
renderChannelTabs(pendingFiles);
renderHealthTabs();
list.innerHTML = "";
const filteredItems = getFilteredItems(pendingFiles);
const totalPages = getTotalPages(filteredItems);
const pageItems = getPageItems(filteredItems);
renderPager(totalPages);
if (!filteredItems.length) {
list.style.display = "block";
const empty = document.createElement("div");
empty.textContent = inventorySnapshot.loaded ? "当前没有 Codex 凭证" : "还没有 Codex 数据";
empty.style.fontSize = "12px";
empty.style.lineHeight = "1.6";
empty.style.color = t.emptyText;
empty.style.padding = "14px";
empty.style.textAlign = "center";
list.appendChild(empty);
return;
}
list.style.display = "grid";
list.style.gridTemplateColumns = isMobile ? "minmax(0, 1fr)" : "repeat(2, minmax(0, 1fr))";
list.style.gap = "8px";
list.style.alignContent = "start";
pageItems.forEach((item, idx) => {
const card = document.createElement("div");
card.style.border = t.cardBorder;
card.style.borderRadius = "14px";
card.style.padding = "9px 10px";
card.style.background = t.cardBg;
card.style.backdropFilter = "blur(6px)";
card.style.display = "flex";
card.style.flexDirection = "column";
card.style.gap = "6px";
card.style.minWidth = "0";
card.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.05)";
card.style.transition = "transform 0.18s ease, box-shadow 0.18s ease";
card.addEventListener("mouseenter", () => {
card.style.transform = "translateY(-1px)";
card.style.boxShadow = "0 10px 20px rgba(0,0,0,0.10)";
});
card.addEventListener("mouseleave", () => {
card.style.transform = "translateY(0)";
card.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.05)";
});
const headRow = document.createElement("div");
headRow.style.display = "flex";
headRow.style.alignItems = "flex-start";
headRow.style.justifyContent = "space-between";
headRow.style.gap = "8px";
const nameWrap = document.createElement("div");
nameWrap.style.minWidth = "0";
nameWrap.style.flex = "1";
const absoluteIndex = (currentPage - 1) * PAGE_SIZE + idx + 1;
const name = document.createElement("div");
name.textContent = `${absoluteIndex}. ${item.name}`;
name.style.fontSize = "12px";
name.style.fontWeight = "700";
name.style.lineHeight = "1.45";
name.style.wordBreak = "break-all";
name.style.color = t.titleText;
nameWrap.appendChild(name);
const meta = document.createElement("div");
meta.textContent = getTechDetailText(item);
meta.style.fontSize = "10px";
meta.style.color = t.metaText;
meta.style.wordBreak = "break-all";
meta.style.lineHeight = "1.5";
nameWrap.appendChild(meta);
headRow.appendChild(nameWrap);
const badges = document.createElement("div");
badges.style.display = "flex";
badges.style.alignItems = "center";
badges.style.gap = "6px";
badges.style.flexWrap = "wrap";
badges.style.justifyContent = "flex-end";
const channelBadge = document.createElement("span");
channelBadge.textContent = formatChannelLabel(item.channel);
channelBadge.style.display = "inline-flex";
channelBadge.style.alignItems = "center";
channelBadge.style.padding = "3px 8px";
channelBadge.style.borderRadius = "999px";
channelBadge.style.fontSize = "10px";
channelBadge.style.fontWeight = "700";
channelBadge.style.background = t.badgeIdleBg;
channelBadge.style.color = t.badgeIdleText;
badges.appendChild(channelBadge);
const enabledBadge = document.createElement("span");
enabledBadge.textContent = getEnabledStateText(item);
enabledBadge.style.display = "inline-flex";
enabledBadge.style.alignItems = "center";
enabledBadge.style.padding = "3px 8px";
enabledBadge.style.borderRadius = "999px";
enabledBadge.style.fontSize = "10px";
enabledBadge.style.fontWeight = "700";
applyToneBadge(enabledBadge, getEnabledStateTone(item));
badges.appendChild(enabledBadge);
const actualBadge = document.createElement("span");
actualBadge.textContent = getActualResultText(item);
actualBadge.style.display = "inline-flex";
actualBadge.style.alignItems = "center";
actualBadge.style.padding = "3px 8px";
actualBadge.style.borderRadius = "999px";
actualBadge.style.fontSize = "10px";
actualBadge.style.fontWeight = "800";
applyToneBadge(actualBadge, getActualResultTone(item));
badges.appendChild(actualBadge);
headRow.appendChild(badges);
card.appendChild(headRow);
const usageRow = document.createElement("div");
usageRow.textContent = item.queryState === "unqueried" ? "尚未检查" : getUsageText(item);
usageRow.style.fontSize = "11px";
usageRow.style.lineHeight = "1.5";
usageRow.style.color = t.usageText;
card.appendChild(usageRow);
list.appendChild(card);
});
}
function applyPanelState() {
if (isMobile) {
panel.style.left = "10px";
panel.style.right = "10px";
panel.style.top = "auto";
panel.style.bottom = "64px";
panel.style.width = "auto";
panel.style.maxHeight = "74vh";
panel.style.transform = isOpen ? "translateY(0) scale(1)" : "translateY(calc(100% + 32px)) scale(0.985)";
list.style.minHeight = "min(48vh, 420px)";
toggleBtn.style.left = "auto";
toggleBtn.style.right = "16px";
toggleBtn.style.top = "auto";
toggleBtn.style.bottom = "16px";
toggleBtn.textContent = isOpen ? "收起" : "展开";
} else {
panel.style.left = "auto";
panel.style.right = "16px";
panel.style.top = "70px";
panel.style.bottom = "auto";
panel.style.width = "min(920px, calc(100vw - 24px))";
panel.style.maxHeight = "80vh";
panel.style.transform = isOpen ? "translateX(0) scale(1)" : "translateX(calc(100% + 24px)) scale(0.985)";
list.style.minHeight = "0";
toggleBtn.style.left = "auto";
toggleBtn.style.right = "16px";
toggleBtn.style.top = "auto";
toggleBtn.style.bottom = "16px";
toggleBtn.textContent = isOpen ? "收起" : "展开";
}
panel.style.opacity = isOpen ? "1" : "0";
panel.style.pointerEvents = isOpen ? "auto" : "none";
panel.style.visibility = isOpen ? "visible" : "hidden";
}
function setOpen(open) {
isOpen = Boolean(open);
applyPanelState();
}
toggleBtn.addEventListener("click", () => {
setOpen(!isOpen);
});
const mediaHandler = () => {
isMobile = mediaQuery.matches;
applyPanelState();
scheduleRender();
};
if (typeof mediaQuery.addEventListener === "function") mediaQuery.addEventListener("change", mediaHandler);
else if (typeof mediaQuery.addListener === "function") mediaQuery.addListener(mediaHandler);
const colorHandler = () => {
syncTheme();
};
if (typeof colorQuery.addEventListener === "function") colorQuery.addEventListener("change", colorHandler);
else if (typeof colorQuery.addListener === "function") colorQuery.addListener(colorHandler);
window.addEventListener("storage", (ev) => {
if (!ev || ev.key === null || ev.key === THEME_STORAGE_KEY) syncTheme();
});
const rootObserver = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === "attributes" && m.attributeName === "data-theme") {
syncTheme();
break;
}
}
});
rootObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
setInterval(() => {
try {
const raw = localStorage.getItem(THEME_STORAGE_KEY) || "";
if (raw !== lastThemeStoreRaw) {
lastThemeStoreRaw = raw;
syncTheme();
}
} catch (_) {}
}, 1200);
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && isOpen) setOpen(false);
});
prevBtn.addEventListener("click", () => {
if (currentPage <= 1 || panelBusy) return;
currentPage -= 1;
scheduleRender();
});
nextBtn.addEventListener("click", () => {
const totalPages = getTotalPages(getFilteredItems(pendingFiles));
if (currentPage >= totalPages || panelBusy) return;
currentPage += 1;
scheduleRender();
});
async function batchQueryItems(items, alreadyBusy) {
const codexItems = items.filter((item) => supportsActiveCheck(item));
if (!codexItems.length) {
setProgress("Codex 0");
return;
}
if (!alreadyBusy) setBusy(true, "query");
let ok = 0;
let fail = 0;
let processed = 0;
try {
await runWithConcurrency(codexItems, QUERY_CONCURRENCY, async (item) => {
try {
const res = await callWithAuthRetry(`检测 Codex ${item.name}`, (token) =>
queryUsageByAuthIndex(token, item)
);
const snap = parseUsageSnapshot(res.bodyObj);
markQueryResult(item, res, snap);
if (isHealthyState(item)) ok += 1;
else if (isFailedState(item)) fail += 1;
} catch (_) {
markQueryFailed(item, "ERR");
fail += 1;
}
processed += 1;
setProgress(`Codex ${processed}/${codexItems.length}`);
scheduleRender();
});
const done = collectStats(pendingFiles);
setProgress(`健康 ${done.healthy} 无额度 ${done.quota} 失效 ${done.failed}`);
} finally {
scheduleRender();
if (!alreadyBusy) setBusy(false);
}
}
async function fetchAndQueryAll() {
setBusy(true, "query");
setProgress("读取中");
try {
const allFiles = await callWithAuthRetry("读取全部 Codex 凭证", (token) => fetchAllFiles(token));
const codexFiles = allFiles.filter((item) => supportsActiveCheck(item));
inventorySnapshot = buildInventorySnapshot(codexFiles);
currentPage = 1;
pendingFiles = codexFiles.slice();
renderList();
if (!pendingFiles.length) {
setProgress("总数 0");
return;
}
await batchQueryItems(pendingFiles, true);
} catch (e) {
setProgress(e?.status === 401 ? "读取失败" : "检查失败");
} finally {
setBusy(false);
scheduleRender();
}
}
async function fetchAndQueryNonActive() {
setBusy(true, "query");
setProgress("读取异常中");
try {
const abnormalFiles = await callWithAuthRetry("读取异常 Codex 凭证", (token) => fetchNonActive(token));
const codexFiles = abnormalFiles.filter((item) => supportsActiveCheck(item));
inventorySnapshot = buildInventorySnapshot(codexFiles);
currentPage = 1;
pendingFiles = codexFiles.slice();
renderList();
if (!pendingFiles.length) {
setProgress("异常 0");
return;
}
await batchQueryItems(pendingFiles, true);
} catch (e) {
setProgress(e?.status === 401 ? "读取失败" : "快速检查失败");
} finally {
setBusy(false);
scheduleRender();
}
}
function isToggleUnsupportedError(err) {
const status = Number(err?.status);
return status === 404 || status === 405 || status === 501;
}
function syncLocalDisabledState(item, disabled) {
item.disabled = Boolean(disabled);
if (disabled) {
item.status = "disabled";
item.statusMessage = "disabled via management API";
return;
}
item.status = "active";
item.statusMessage = "";
}
async function updateItemsDisabledState(items, disabled, phaseLabel, progressPrefix) {
if (!items.length) {
return { success: 0, failed: 0, unsupported: false };
}
let success = 0;
let failed = 0;
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
setProgress(`${progressPrefix} ${i + 1}/${items.length}`);
try {
await callWithAuthRetry(`${phaseLabel} ${item.name}`, (token) =>
patchAuthFileDisabled(token, item.name, disabled)
);
syncLocalDisabledState(item, disabled);
success += 1;
} catch (err) {
if (isToggleUnsupportedError(err)) {
return { success, failed, unsupported: true };
}
failed += 1;
}
scheduleRender();
}
return { success, failed, unsupported: false };
}
async function deleteFailedItems() {
if (!pendingFiles.length) {
setProgress("总数 0");
return;
}
const deletable = pendingFiles.filter((x) => supportsActiveCheck(x) && x.deleteEligible && x.name && x.name !== "(no-name)");
const enableTargets = pendingFiles.filter((x) =>
supportsActiveCheck(x) && isHealthyState(x) && x.disabled && x.name && x.name !== "(no-name)"
);
const disableTargets = pendingFiles.filter((x) =>
supportsActiveCheck(x) && isQuotaState(x) && !x.disabled && x.name && x.name !== "(no-name)"
);
if (!deletable.length && !enableTargets.length && !disableTargets.length) {
setProgress("无需优化");
return;
}
setBusy(true, "delete");
let deleteSuccess = 0;
const deletedSet = new Set();
let enableSummary = { success: 0, failed: 0, unsupported: false };
let disableSummary = { success: 0, failed: 0, unsupported: false };
try {
for (let i = 0; i < deletable.length; i += 1) {
const item = deletable[i];
setProgress(`删除失效 ${i + 1}/${deletable.length}`);
try {
const r = await callWithAuthRetry(`删除 ${item.name}`, (token) =>
deleteByName(token, item.name)
);
if (r.ok) {
deleteSuccess += 1;
deletedSet.add(item);
}
} catch (_) {
// keep failed item in list
}
scheduleRender();
}
pendingFiles = pendingFiles.filter((x) => !deletedSet.has(x));
applyDeletedItems(Array.from(deletedSet));
enableSummary = await updateItemsDisabledState(enableTargets, false, "启用凭证", "启用健康");
disableSummary = await updateItemsDisabledState(disableTargets, true, "禁用凭证", "禁用无额度");
const deleteFailed = deletable.length - deleteSuccess;
const parts = [
`已删 ${deleteSuccess}`,
deleteFailed ? `未删 ${deleteFailed}` : "",
`已启用 ${enableSummary.success}`,
enableSummary.failed ? `启用失败 ${enableSummary.failed}` : "",
`已禁用 ${disableSummary.success}`,
disableSummary.failed ? `禁用失败 ${disableSummary.failed}` : "",
].filter(Boolean);
if (enableSummary.unsupported || disableSummary.unsupported) {
parts.push("启用/禁用接口不可用");
}
setProgress(parts.join(" "));
} finally {
setBusy(false, "delete");
scheduleRender();
}
}
btnBatchQuery.addEventListener("click", async () => {
if (panelBusy) return;
await fetchAndQueryAll();
});
btnQuickCheck.addEventListener("click", async () => {
if (panelBusy) return;
await fetchAndQueryNonActive();
});
btnBatchDelete.addEventListener("click", async () => {
if (panelBusy) return;
await deleteFailedItems();
});
syncTheme();
renderList();
applyPanelState();
document.body.appendChild(panel);
document.body.appendChild(toggleBtn);
}
function init() {
installTokenSniffer();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", createSidebar, { once: true });
} else {
createSidebar();
}
}
init();
})();
--【贰】--:
好的 佬 谢谢 我去试试看看,不然太影响效率了
我加了一些chatgpt账号到中转站,感觉有很多失效的号或者没额度的,我看了下记录,api请求会很多失败,我觉得这样会影响工作效率,有没有哪个中转站可以自动识别失效的账号,跳过这些没额度的,只请求有效账号的额度?
sub2api可以吗 ?求大佬解答一下
--【壹】--:
有个之前站里发的油猴脚本可以批量检测清理
// ==UserScript==
// @name CPA AuthFiles Universal Cleaner
// @namespace openai_registor
// @version 2.2.0
// @description 一键优化:删除失效,启用健康,禁用无额度
// @author local
// @match *://*/management.html*
// @grant none
// ==/UserScript==
(function () {
"use strict";
const BASE_ORIGIN = window.location.origin;
const AUTH_FILES_URL = `${BASE_ORIGIN}/v0/management/auth-files`;
const AUTH_FILES_STATUS_URL = `${BASE_ORIGIN}/v0/management/auth-files/status`;
const API_CALL_URL = `${BASE_ORIGIN}/v0/management/api-call`;
const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
const DEFAULT_UA = "codex_cli_rs/universal (Windows)";
const QUERY_CONCURRENCY = 4;
const PAGE_SIZE = 20;
const TOKEN_CACHE_KEY = "tm_auth_token";
const CAPTURED_TOKEN_KEY = "tm_last_bearer_token_v1";
const CLI_PROXY_AUTH_KEY = "cli-proxy-auth";
const ENC_PREFIX = "enc::v1::";
const ENC_SEED = "cli-proxy-api-webui::secure-storage";
let runtimeToken = "";
let manualPromptBlockedUntil = 0;
let pendingFiles = [];
let inventorySnapshot = {
loaded: false,
total: 0,
};
function isActiveStatus(status) {
return String(status || "").trim().toLowerCase() === "active";
}
function buildInventorySnapshot(files) {
return {
loaded: true,
total: files.length,
};
}
function applyDeletedItems(items) {
if (!inventorySnapshot.loaded || !Array.isArray(items) || !items.length) return;
inventorySnapshot.total = Math.max(0, inventorySnapshot.total - items.length);
}
function getSystemStatusText(item) {
return isActiveStatus(item?.status) ? "健康" : "失效";
}
function getBackendStatusLabel(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
if (rawStatus === "active") return "正常";
if (rawStatus === "disabled") return "已禁用";
if (rawStatus === "error") return "出错";
if (rawStatus === "pending") return "等待完成";
if (rawStatus === "refreshing") return "刷新中";
if (rawStatus === "unknown") return "状态未知";
return item?.status || "未知状态";
}
function getBackendStatusTone(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
if (rawStatus === "active") return "ok";
if (rawStatus === "pending" || rawStatus === "refreshing" || rawStatus === "unknown") return "warn";
if (rawStatus === "disabled" || rawStatus === "error") return "fail";
return "idle";
}
function getBackendReasonText(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
const rawMessage = String(item?.statusMessage || "").trim().toLowerCase();
if (rawStatus === "active") return "后台认为它目前正常。";
if (rawStatus === "disabled") {
if (rawMessage === "disabled via management api") return "后台原因:这个号被手动禁用了。";
if (rawMessage === "removed via management api") return "后台原因:这个号被管理后台移除过。";
return "后台原因:这个号目前被标记为禁用。";
}
if (rawStatus === "pending") return "后台原因:这个号还在等待外部操作,比如登录确认或验证。";
if (rawStatus === "refreshing") return "后台原因:这个号正在刷新中,暂时还没恢复到正常状态。";
if (rawStatus === "unknown") return "后台原因:后台暂时判断不出它是不是正常。";
if (rawMessage === "unauthorized") return "后台原因:登录失效,或者凭证已经失效。";
if (rawMessage === "payment_required") return "后台原因:权限、套餐或付费状态有问题。";
if (rawMessage === "not_found") return "后台原因:后台没找到它需要访问的资源。";
if (rawMessage === "quota exhausted") return "后台原因:额度已经用完了。";
if (rawMessage === "context canceled") return "后台原因:请求被中断了,通常是超时、切换或上游主动取消。";
if (rawMessage === "transient upstream error") return "后台原因:上游服务临时出错。";
if (rawMessage === "request failed") return "后台原因:请求失败,但后台没有给更细的分类。";
if (rawStatus === "error") {
if (rawMessage) return `后台原因:${item.statusMessage}`;
return "后台原因:这个号请求时报错了。";
}
if (rawMessage) return `后台原因:${item.statusMessage}`;
if (item?.disabled) return "后台原因:这个号被标记为禁用。";
if (item?.unavailable) return "后台原因:这个号暂时不可用。";
return "后台原因:不是 active,但后台没有返回更明确的说明。";
}
function getBackendReasonTone(item) {
const rawStatus = String(item?.status || "").trim().toLowerCase();
const rawMessage = String(item?.statusMessage || "").trim().toLowerCase();
if (rawStatus === "active") return "ok";
if (["pending", "refreshing", "unknown"].includes(rawStatus)) return "warn";
if (["disabled", "error"].includes(rawStatus)) return "fail";
if (["unauthorized", "payment_required", "not_found", "quota exhausted", "context canceled", "transient upstream error", "request failed"].includes(rawMessage)) return "fail";
return "idle";
}
function isCodexChannel(item) {
return normalizeChannelKey(item?.channel) === "codex";
}
function supportsActiveCheck(item) {
return isCodexChannel(item);
}
function isQuotaState(item) {
return item?.queryState === "quota";
}
function isHealthyState(item) {
return item?.queryState === "ok";
}
function isFailedState(item) {
return item?.queryState === "failed";
}
function getEnabledStateText(item) {
return item?.disabled ? "已禁用" : "已启用";
}
function getEnabledStateTone(item) {
return item?.disabled ? "fail" : "ok";
}
function getActualResultText(item) {
if (!supportsActiveCheck(item)) return "仅展示";
if (isHealthyState(item)) return "健康";
if (isQuotaState(item)) return "无额度";
if (isFailedState(item)) {
if (String(item?.lastStatusCode || "") === "NO_AUTH") return "失效";
return "失效";
}
return "待检查";
}
function getActualResultTone(item) {
if (!supportsActiveCheck(item)) return "idle";
if (isHealthyState(item)) return "ok";
if (isQuotaState(item)) return "warn";
if (isFailedState(item)) {
if (String(item?.lastStatusCode || "") === "NO_AUTH") return "warn";
return "fail";
}
return "idle";
}
function getDeleteAdviceText(item) {
if (isHealthyState(item)) return "建议保留,它目前是健康且有额度的";
if (isQuotaState(item)) return "建议禁用,它目前可用但没有额度";
if (isFailedState(item)) return "可考虑删除,它目前已判定为失效";
return "先检查再决定,现在不建议删除";
}
function getUsageText(item) {
if (!supportsActiveCheck(item)) return "该渠道仅展示,不参与主动检测或自动删除";
if (item?.queryState === "unqueried") return "等待 Codex 检测";
if (isQuotaState(item)) return `额度已用尽 · 重置 ${item?.resetText || "-"}`;
if (isFailedState(item)) return `检测失败 · 返回 ${item?.lastStatusCode == null ? "--" : String(item.lastStatusCode)}`;
const used = item?.usedPercent === null ? "未知" : `${item.usedPercent}%`;
return `Codex 已用 ${used} · 重置 ${item?.resetText || "-"}`;
}
function getBadgeText(item) {
if (isHealthyState(item)) return "实测结果:健康";
if (isQuotaState(item)) return "实测结果:无额度";
if (isFailedState(item)) return "实测结果:失效";
return "实测结果:待检查";
}
function getTechDetailText(item) {
const authIndex = item?.authIndex || "-";
const statusCode = item?.lastStatusCode == null ? "--" : String(item.lastStatusCode);
return `authIndex=${authIndex} | 返回码=${statusCode}`;
}
function normalizeChannelKey(value) {
return String(value || "").trim().toLowerCase();
}
function inferChannelFromName(name) {
const raw = String(name || "").trim().toLowerCase();
if (!raw) return "unknown";
if (raw.includes("qwen")) return "qwen";
if (raw.includes("codex")) return "codex";
if (raw.includes("kiro")) return "kiro";
if (raw.includes("claude")) return "claude";
if (raw.includes("gemini")) return "gemini";
if (raw.includes("openai")) return "openai";
return "unknown";
}
function extractChannel(item) {
const directKeys = ["channel", "provider", "type", "model_provider", "modelProvider"];
for (const key of directKeys) {
const value = normalizeChannelKey(item?.[key]);
if (value) return value;
}
const nested = [
item?.id_token?.provider,
item?.idToken?.provider,
item?.id_token?.channel,
item?.idToken?.channel,
];
for (const value of nested) {
const normalized = normalizeChannelKey(value);
if (normalized) return normalized;
}
return inferChannelFromName(extractFileName(item));
}
function formatChannelLabel(channel) {
const key = normalizeChannelKey(channel);
if (!key || key === "all") return "全部渠道";
if (key === "unknown") return "未分类";
const pretty = {
qwen: "Qwen",
codex: "Codex",
kiro: "Kiro",
claude: "Claude",
gemini: "Gemini",
openai: "OpenAI",
};
return pretty[key] || key;
}
function getChannelCounts(items) {
const counts = new Map();
for (const item of items) {
const key = normalizeChannelKey(item?.channel) || "unknown";
counts.set(key, (counts.get(key) || 0) + 1);
}
return counts;
}
function persistToken(token) {
const t = String(token || "").trim();
if (!t) return "";
runtimeToken = t;
try { localStorage.setItem(TOKEN_CACHE_KEY, t); } catch (_) {}
try { sessionStorage.setItem(TOKEN_CACHE_KEY, t); } catch (_) {}
try { localStorage.setItem(CAPTURED_TOKEN_KEY, t); } catch (_) {}
return t;
}
function decodeCliProxyAuth(raw) {
if (!raw) return "";
if (!raw.startsWith(ENC_PREFIX)) return raw;
try {
const keyText = `${ENC_SEED}|${window.location.host}|${navigator.userAgent}`;
const key = new TextEncoder().encode(keyText);
const bin = atob(raw.slice(ENC_PREFIX.length));
const data = Uint8Array.from(bin, (ch) => ch.charCodeAt(0));
const out = new Uint8Array(data.length);
for (let i = 0; i < data.length; i += 1) out[i] = data[i] ^ key[i % key.length];
return new TextDecoder().decode(out);
} catch (_) {
return "";
}
}
function tryReadTokensFromCliProxyAuth() {
try {
const raw = localStorage.getItem(CLI_PROXY_AUTH_KEY);
const decoded = decodeCliProxyAuth(raw);
if (!decoded) return [];
const obj = JSON.parse(decoded);
const cands = [
obj?.state?.managementKey,
obj?.state?.adminKey,
obj?.state?.authToken,
obj?.state?.token,
obj?.state?.password,
obj?.managementKey,
obj?.adminKey,
obj?.authToken,
obj?.token,
];
return cands
.filter((x) => typeof x === "string" && x.trim())
.map((x) => String(x).trim());
} catch (_) {
return [];
}
}
function tryReadTokensFromStorage() {
const keys = [
TOKEN_CACHE_KEY,
CAPTURED_TOKEN_KEY,
"tm_token",
"auth_token",
"management_token",
"management_key",
"admin_key",
];
const tokens = [];
for (const k of keys) {
try {
const v = localStorage.getItem(k) || sessionStorage.getItem(k);
if (v && String(v).trim()) tokens.push(String(v).trim());
} catch (_) {}
}
return tokens;
}
function installTokenSniffer() {
if (window.__tmUniversalSnifferInstalled) return;
window.__tmUniversalSnifferInstalled = true;
const origFetch = window.fetch.bind(window);
window.fetch = async function (input, init) {
try {
let headers = null;
if (init && init.headers) headers = new Headers(init.headers);
else if (input && typeof input === "object" && "headers" in input) headers = new Headers(input.headers);
if (headers) {
const auth = headers.get("authorization") || headers.get("Authorization");
if (auth && /^Bearer\s+/i.test(auth)) persistToken(auth.replace(/^Bearer\s+/i, ""));
}
} catch (_) {}
return origFetch(input, init);
};
const origSetHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
try {
if (/^authorization$/i.test(String(name || "")) && /^Bearer\s+/i.test(String(value || ""))) {
persistToken(String(value).replace(/^Bearer\s+/i, ""));
}
} catch (_) {}
return origSetHeader.call(this, name, value);
};
}
function collectTokenCandidates() {
const all = [
runtimeToken,
...tryReadTokensFromCliProxyAuth(),
...tryReadTokensFromStorage(),
].map((x) => String(x || "").trim()).filter(Boolean);
const uniq = [];
const seen = new Set();
for (const t of all) {
if (seen.has(t)) continue;
seen.add(t);
uniq.push(t);
}
return uniq;
}
function clearTokenCacheValue(token) {
const t = String(token || "").trim();
if (!t) return;
if (runtimeToken === t) runtimeToken = "";
const keys = [
TOKEN_CACHE_KEY,
CAPTURED_TOKEN_KEY,
"tm_token",
"auth_token",
"management_token",
"management_key",
"admin_key",
];
for (const k of keys) {
try {
if (localStorage.getItem(k) === t) localStorage.removeItem(k);
} catch (_) {}
try {
if (sessionStorage.getItem(k) === t) sessionStorage.removeItem(k);
} catch (_) {}
}
}
function isUnauthorizedError(err) {
const status = Number(err?.status);
if (status === 401) return true;
const msg = String(err?.message || "");
return msg.includes("HTTP 401");
}
function buildHttpError(message, status, rawText) {
const e = new Error(message);
e.status = status;
e.rawText = rawText || "";
return e;
}
async function callWithAuthRetry(opName, fn) {
try {
return await fn("");
} catch (err) {
if (!isUnauthorizedError(err)) throw err;
}
const tried = new Set();
let lastErr = null;
const candidates = collectTokenCandidates();
for (const token of candidates) {
if (!token || tried.has(token)) continue;
tried.add(token);
try {
const result = await fn(token);
persistToken(token);
return result;
} catch (err) {
lastErr = err;
if (isUnauthorizedError(err)) {
clearTokenCacheValue(token);
continue;
}
throw err;
}
}
const now = Date.now();
if (now < manualPromptBlockedUntil) {
if (lastErr) throw lastErr;
throw new Error(`${opName}失败:没有可用密钥`);
}
const manual = window.prompt(
`当前登录已失效。\n请输入“管理后台登录密钥”(就是你打开 management 页面时输入的那串密码)后重试:\n场景:${opName}`,
runtimeToken || ""
);
if (manual && String(manual).trim()) {
const token = persistToken(String(manual).trim());
try {
return await fn(token);
} catch (err) {
if (isUnauthorizedError(err)) clearTokenCacheValue(token);
throw err;
}
}
manualPromptBlockedUntil = Date.now() + 10000;
if (lastErr) throw lastErr;
throw new Error(`${opName}失败:没有可用密钥`);
}
function safeJson(text) {
try { return JSON.parse(text); } catch (_) { return null; }
}
function extractFileName(item) {
for (const key of ["name", "id", "filename", "file_name"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function extractAuthIndex(item) {
for (const key of ["authIndex", "auth_index", "authindex"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function extractChatgptAccountId(item) {
const nested = item?.id_token?.chatgpt_account_id || item?.idToken?.chatgpt_account_id;
if (nested) return String(nested);
for (const key of ["chatgpt_account_id", "chatgptAccountId", "account_id", "accountId"]) {
const v = item?.[key];
if (v) return String(v);
}
return null;
}
function getHeaders(token) {
const headers = {
accept: "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
referer: `${BASE_ORIGIN}/management.html`,
};
if (token && String(token).trim()) {
headers.authorization = `Bearer ${String(token).trim()}`;
}
return headers;
}
function normalizeFilesPayload(data) {
const files = Array.isArray(data?.files)
? data.files.filter((x) => x && typeof x === "object")
: Array.isArray(data)
? data.filter((x) => x && typeof x === "object")
: [];
return files.map((item) => ({
name: extractFileName(item) || "(no-name)",
authIndex: extractAuthIndex(item) || "",
status: String(item?.status ?? ""),
statusMessage: String(item?.status_message ?? item?.statusMessage ?? ""),
disabled: Boolean(item?.disabled),
unavailable: Boolean(item?.unavailable),
channel: extractChannel(item),
chatgptAccountId: extractChatgptAccountId(item) || "",
usedPercent: null,
resetText: "-",
lastStatusCode: null,
queryState: "unqueried",
hasQuota: null,
deleteEligible: false,
}));
}
async function fetchAllFiles(token) {
const resp = await fetch(AUTH_FILES_URL, {
method: "GET",
headers: getHeaders(token),
credentials: "include",
});
if (!resp.ok) {
const raw = await resp.text();
throw buildHttpError(`获取失败: HTTP ${resp.status}`, resp.status, raw);
}
const data = await resp.json();
return normalizeFilesPayload(data);
}
async function fetchNonActive(token) {
const all = await fetchAllFiles(token);
return all.filter((x) => String(x.status || "").toLowerCase() !== "active");
}
async function deleteByName(token, name) {
const url = `${AUTH_FILES_URL}?name=${encodeURIComponent(name)}`;
const resp = await fetch(url, {
method: "DELETE",
headers: getHeaders(token),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) throw buildHttpError(`删除失败: HTTP 401`, 401, text);
const data = safeJson(text);
const ok = resp.status === 200 && (!data || data.status === "ok");
return { ok, status: resp.status, raw: text };
}
async function patchAuthFileDisabledAt(token, url, name, disabled) {
const resp = await fetch(url, {
method: "PATCH",
headers: { ...getHeaders(token), "content-type": "application/json" },
body: JSON.stringify({ name, disabled: Boolean(disabled) }),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) throw buildHttpError(`更新启用状态失败: HTTP 401`, 401, text);
if (!resp.ok) throw buildHttpError(`更新启用状态失败: HTTP ${resp.status}`, resp.status, text);
const data = safeJson(text);
if (data && data.status && data.status !== "ok") {
throw buildHttpError(`更新启用状态失败: ${data.status}`, resp.status, text);
}
return { ok: true, status: resp.status, raw: text, data };
}
async function patchAuthFileDisabled(token, name, disabled) {
try {
return await patchAuthFileDisabledAt(token, AUTH_FILES_URL, name, disabled);
} catch (err) {
if (Number(err?.status) !== 404) throw err;
}
return patchAuthFileDisabledAt(token, AUTH_FILES_STATUS_URL, name, disabled);
}
async function queryUsageByAuthIndex(token, fileItem) {
if (!fileItem?.authIndex) {
return { ok: false, statusCode: "NO_AUTH", bodyObj: null, bodyText: "missing authIndex" };
}
const header = {
Authorization: "Bearer $TOKEN$",
"Content-Type": "application/json",
"User-Agent": DEFAULT_UA,
};
if (fileItem.chatgptAccountId) header["Chatgpt-Account-Id"] = fileItem.chatgptAccountId;
const body = {
authIndex: fileItem.authIndex,
method: "GET",
url: USAGE_URL,
header,
};
const resp = await fetch(API_CALL_URL, {
method: "POST",
headers: { ...getHeaders(token), "content-type": "application/json" },
body: JSON.stringify(body),
credentials: "include",
});
const text = await resp.text();
if (resp.status === 401) {
throw buildHttpError(`余额查询失败: HTTP 401`, 401, text);
}
const data = safeJson(text);
const statusCode = data?.status_code ?? data?.statusCode ?? null;
let bodyObj = null;
let bodyText = "";
if (typeof data?.body === "string") {
bodyText = data.body;
const parsed = safeJson(data.body);
if (parsed && typeof parsed === "object") bodyObj = parsed;
} else if (data?.body && typeof data.body === "object") {
bodyObj = data.body;
bodyText = JSON.stringify(data.body, null, 2);
} else {
bodyText = text;
}
const result = {
ok: resp.ok && Number(statusCode) === 200,
statusCode,
bodyObj,
bodyText,
};
return result;
}
function normalizeUsedPercent(value) {
const num = Number(value);
return Number.isFinite(num) ? Math.max(0, Math.min(100, num)) : null;
}
function formatUsageResetText(windowInfo) {
if (windowInfo?.reset_at !== undefined && windowInfo?.reset_at !== null) {
const ts = Number(windowInfo.reset_at);
if (Number.isFinite(ts)) return new Date(ts * 1000).toLocaleString();
}
if (windowInfo?.reset_after_seconds !== undefined && windowInfo?.reset_after_seconds !== null) {
const sec = Number(windowInfo.reset_after_seconds);
if (Number.isFinite(sec)) return `${sec}s`;
}
return "-";
}
function parseUsageSnapshot(bodyObj) {
const rateLimit = bodyObj?.rate_limit;
const windows = [
{ label: "5h", data: rateLimit?.primary_window },
{ label: "7d", data: rateLimit?.secondary_window },
].filter((entry) => entry.data && typeof entry.data === "object");
let displayWindow = windows[0] || null;
let maxUsedPercent = null;
for (const entry of windows) {
const usedPercent = normalizeUsedPercent(entry?.data?.used_percent);
if (usedPercent === null) continue;
if (maxUsedPercent === null || usedPercent > maxUsedPercent) {
maxUsedPercent = usedPercent;
displayWindow = entry;
}
}
const limitReached = Boolean(rateLimit?.limit_reached) || rateLimit?.allowed === false || windows.some((entry) => {
const usedPercent = normalizeUsedPercent(entry?.data?.used_percent);
return usedPercent !== null && usedPercent >= 100;
});
const hasQuota = limitReached ? false : true;
return {
usedPercent: maxUsedPercent,
resetText: formatUsageResetText(displayWindow?.data),
hasQuota,
limitReached,
displayWindowLabel: displayWindow?.label || "",
};
}
function isQuotaResult(statusCode, bodyObj, bodyText) {
const code = Number(statusCode);
const text = `${JSON.stringify(bodyObj || {})} ${String(bodyText || "")}`.toLowerCase();
if (bodyObj?.rate_limit?.limit_reached === true) return true;
if (bodyObj?.rate_limit?.allowed === false) return true;
if (text.includes("quota exhausted")) return true;
if (text.includes("limit reached")) return true;
if (text.includes("payment_required")) return true;
return code === 402;
}
function markQueryResult(item, result, usageSnapshot) {
item.lastStatusCode = result?.statusCode;
item.usedPercent = usageSnapshot?.usedPercent ?? null;
item.resetText = usageSnapshot?.resetText || "-";
item.hasQuota = usageSnapshot?.hasQuota ?? null;
if (Number(result?.statusCode) === 200) {
item.queryState = usageSnapshot?.hasQuota === false ? "quota" : "ok";
item.deleteEligible = false;
return;
}
if (isQuotaResult(result?.statusCode, result?.bodyObj, result?.bodyText)) {
item.queryState = "quota";
item.deleteEligible = false;
item.hasQuota = false;
return;
}
item.queryState = "failed";
item.deleteEligible = true;
item.hasQuota = null;
}
function markQueryFailed(item, statusCode) {
item.lastStatusCode = statusCode ?? "ERR";
item.usedPercent = null;
item.resetText = "-";
item.hasQuota = null;
item.queryState = "failed";
item.deleteEligible = true;
}
function collectStats(items) {
const stats = { total: items.length, unqueried: 0, healthy: 0, quota: 0, failed: 0, deletable: 0 };
for (const item of items) {
if (!supportsActiveCheck(item)) continue;
if (isHealthyState(item)) stats.healthy += 1;
else if (isQuotaState(item)) stats.quota += 1;
else if (isFailedState(item)) stats.failed += 1;
else stats.unqueried += 1;
if (item.deleteEligible) stats.deletable += 1;
}
return stats;
}
async function runWithConcurrency(items, limit, worker) {
if (!items.length) return;
let cursor = 0;
const n = Math.max(1, Math.min(limit, items.length));
const runners = Array.from({ length: n }, async () => {
while (true) {
const idx = cursor;
cursor += 1;
if (idx >= items.length) break;
await worker(items[idx], idx);
}
});
await Promise.all(runners);
}
function createSidebar() {
if (document.getElementById("__tm_universal_panel")) return;
const DESKTOP_PANEL_WIDTH = 920;
const panel = document.createElement("div");
panel.id = "__tm_universal_panel";
panel.style.display = "flex";
panel.style.flexDirection = "column";
panel.style.position = "fixed";
panel.style.zIndex = "99999";
panel.style.backdropFilter = "blur(18px) saturate(120%)";
panel.style.borderRadius = "20px";
panel.style.padding = "12px";
panel.style.overflow = "hidden";
panel.style.willChange = "transform";
panel.style.transition = "transform 0.26s ease, opacity 0.22s ease";
panel.style.fontFamily = "'MiSans','PingFang SC','HarmonyOS Sans SC','Microsoft YaHei UI','Noto Sans SC',sans-serif";
panel.style.gap = "8px";
panel.style.transformOrigin = "right top";
panel.style.pointerEvents = "none";
panel.style.visibility = "hidden";
panel.style.opacity = "0";
function createSegmentWrap() {
const wrap = document.createElement("div");
wrap.style.display = "inline-flex";
wrap.style.alignItems = "center";
wrap.style.gap = "4px";
wrap.style.padding = "4px";
wrap.style.borderRadius = "14px";
wrap.style.boxSizing = "border-box";
wrap.style.minWidth = "0";
return wrap;
}
function createActionBtn(text, role) {
const btn = document.createElement("button");
btn.type = "button";
btn.dataset.role = role;
btn.dataset.text = text;
btn.textContent = text;
btn.style.padding = "10px 14px";
btn.style.borderRadius = "12px";
btn.style.cursor = "pointer";
btn.style.fontSize = "12px";
btn.style.fontWeight = "700";
btn.style.letterSpacing = "0.02em";
btn.style.transition = "transform 0.2s ease, opacity 0.2s ease, box-shadow 0.2s ease";
btn.addEventListener("mouseenter", () => {
if (!btn.disabled) btn.style.transform = "translateY(-1px)";
});
btn.addEventListener("mouseleave", () => {
btn.style.transform = "translateY(0)";
});
return btn;
}
function createSegmentBtn(text) {
const btn = document.createElement("button");
btn.type = "button";
btn.textContent = text;
btn.style.flex = "0 0 auto";
btn.style.whiteSpace = "nowrap";
btn.style.padding = "7px 12px";
btn.style.borderRadius = "10px";
btn.style.fontSize = "11px";
btn.style.fontWeight = "700";
btn.style.lineHeight = "1";
btn.style.cursor = "pointer";
btn.style.transition = "background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease";
btn.addEventListener("mouseenter", () => {
if (!btn.disabled) btn.style.transform = "translateY(-1px)";
});
btn.addEventListener("mouseleave", () => {
btn.style.transform = "translateY(0)";
});
return btn;
}
function createStat(label) {
const box = document.createElement("div");
box.style.display = "inline-flex";
box.style.alignItems = "baseline";
box.style.gap = "6px";
box.style.padding = "7px 10px";
box.style.borderRadius = "999px";
box.style.whiteSpace = "nowrap";
const name = document.createElement("span");
name.textContent = label;
name.style.fontSize = "11px";
name.style.fontWeight = "600";
const value = document.createElement("span");
value.textContent = "0";
value.style.fontSize = "14px";
value.style.fontWeight = "800";
box.appendChild(name);
box.appendChild(value);
return { box, name, value };
}
const topBar = document.createElement("div");
topBar.style.display = "flex";
topBar.style.flexDirection = "column";
topBar.style.gap = "8px";
panel.appendChild(topBar);
const actionRow = document.createElement("div");
actionRow.style.display = "flex";
actionRow.style.alignItems = "center";
actionRow.style.justifyContent = "space-between";
actionRow.style.gap = "8px";
actionRow.style.flexWrap = "wrap";
topBar.appendChild(actionRow);
const actionGroup = document.createElement("div");
actionGroup.style.display = "flex";
actionGroup.style.alignItems = "center";
actionGroup.style.gap = "8px";
actionGroup.style.flexWrap = "wrap";
actionRow.appendChild(actionGroup);
const btnBatchQuery = createActionBtn("全量检查", "query");
const btnQuickCheck = createActionBtn("快速检查", "query");
const btnBatchDelete = createActionBtn("一键优化", "delete");
btnBatchQuery.title = "读取全部 Codex 凭证并执行完整检查";
btnQuickCheck.title = "只读取后台已标成异常的凭证并执行检查";
btnBatchDelete.title = "基于当前检查结果执行删除失效、启用健康、禁用无额度";
actionGroup.appendChild(btnBatchQuery);
actionGroup.appendChild(btnQuickCheck);
actionGroup.appendChild(btnBatchDelete);
const actionHint = document.createElement("div");
actionHint.textContent = "说明:全量检查会检查全部,快速检查只看后台异常,一键优化会删除失效、启用健康、禁用无额度";
actionHint.style.fontSize = "11px";
actionHint.style.fontWeight = "600";
actionHint.style.lineHeight = "1.5";
actionHint.style.marginLeft = "2px";
actionGroup.appendChild(actionHint);
const statsRow = document.createElement("div");
statsRow.style.display = "flex";
statsRow.style.alignItems = "center";
statsRow.style.justifyContent = "flex-end";
statsRow.style.gap = "6px";
statsRow.style.flexWrap = "wrap";
actionRow.appendChild(statsRow);
const metricTotal = createStat("总数");
const metricHealthy = createStat("健康");
const metricNoQuota = createStat("无额度");
const metricFailed = createStat("失效");
statsRow.appendChild(metricTotal.box);
statsRow.appendChild(metricHealthy.box);
statsRow.appendChild(metricNoQuota.box);
statsRow.appendChild(metricFailed.box);
const progressText = document.createElement("div");
progressText.style.fontSize = "11px";
progressText.style.fontWeight = "600";
progressText.style.whiteSpace = "nowrap";
progressText.style.display = "none";
statsRow.appendChild(progressText);
const toolRow = document.createElement("div");
toolRow.style.display = "flex";
toolRow.style.alignItems = "center";
toolRow.style.justifyContent = "space-between";
toolRow.style.gap = "8px";
toolRow.style.flexWrap = "wrap";
topBar.appendChild(toolRow);
const leftTabs = document.createElement("div");
leftTabs.style.display = "flex";
leftTabs.style.alignItems = "center";
leftTabs.style.gap = "8px";
leftTabs.style.flexWrap = "wrap";
leftTabs.style.minWidth = "0";
leftTabs.style.flex = "1";
toolRow.appendChild(leftTabs);
const channelTabs = createSegmentWrap();
channelTabs.style.overflowX = "auto";
channelTabs.style.overflowY = "hidden";
channelTabs.style.maxWidth = "100%";
channelTabs.style.scrollbarWidth = "thin";
leftTabs.appendChild(channelTabs);
channelTabs.addEventListener("wheel", (event) => {
if (!event || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) return;
channelTabs.scrollLeft += event.deltaY;
event.preventDefault();
}, { passive: false });
const healthTabs = createSegmentWrap();
leftTabs.appendChild(healthTabs);
const pagerWrap = createSegmentWrap();
toolRow.appendChild(pagerWrap);
const prevBtn = createSegmentBtn("上一页");
const pageText = document.createElement("div");
pageText.textContent = "1 / 1";
pageText.style.fontSize = "11px";
pageText.style.fontWeight = "700";
pageText.style.padding = "0 8px";
pageText.style.whiteSpace = "nowrap";
const nextBtn = createSegmentBtn("下一页");
pagerWrap.appendChild(prevBtn);
pagerWrap.appendChild(pageText);
pagerWrap.appendChild(nextBtn);
const list = document.createElement("div");
list.style.flex = "1";
list.style.minHeight = "0";
list.style.overflow = "auto";
list.style.padding = "6px";
list.style.borderRadius = "16px";
list.style.backdropFilter = "blur(8px)";
panel.appendChild(list);
const toggleBtn = document.createElement("button");
toggleBtn.style.position = "fixed";
toggleBtn.style.zIndex = "100000";
toggleBtn.style.borderRadius = "999px";
toggleBtn.style.padding = "9px 13px";
toggleBtn.style.cursor = "pointer";
toggleBtn.style.fontSize = "12px";
toggleBtn.style.fontWeight = "700";
toggleBtn.style.backdropFilter = "blur(10px)";
toggleBtn.style.transition = "right 0.26s ease, bottom 0.26s ease, transform 0.2s ease";
toggleBtn.addEventListener("mouseenter", () => {
toggleBtn.style.transform = "translateY(-1px)";
});
toggleBtn.addEventListener("mouseleave", () => {
toggleBtn.style.transform = "translateY(0)";
});
let isOpen = false;
const mediaQuery = window.matchMedia("(max-width: 768px)");
let isMobile = mediaQuery.matches;
const colorQuery = window.matchMedia("(prefers-color-scheme: dark)");
const THEME_STORAGE_KEY = "cli-proxy-theme";
let lastThemeStoreRaw = "";
const THEME_TOKENS = {
dark: {
panelText: "#f4f0e9",
panelBg: "linear-gradient(152deg, rgba(64, 62, 58, 0.76), rgba(32, 33, 38, 0.82))",
panelBorder: "1px solid rgba(228, 217, 204, 0.22)",
panelShadow: "0 16px 42px rgba(10, 10, 14, 0.35)",
softBg: "rgba(37, 39, 44, 0.62)",
softBorder: "1px solid rgba(223,214,204,0.18)",
metricBg: "rgba(65, 67, 74, 0.62)",
metricBorder: "1px solid rgba(231,222,211,0.12)",
metricLabelText: "#d9d0c5",
metricValueText: "#fff8ef",
statusText: "#f2ece3",
summaryText: "#d6d0c9",
tipText: "#dcc5a9",
listBg: "rgba(23, 24, 28, 0.42)",
listBorder: "1px solid rgba(223,214,204,0.18)",
toggleBg: "linear-gradient(145deg, rgba(86, 88, 99, 0.9), rgba(60, 62, 72, 0.92))",
toggleText: "#f4efe8",
toggleBorder: "1px solid rgba(224,214,203,0.24)",
toggleShadow: "0 10px 24px rgba(11, 13, 18, 0.34)",
emptyText: "#d2c9be",
cardBg: "linear-gradient(154deg, rgba(73, 71, 67, 0.36), rgba(44, 45, 50, 0.44))",
cardBorder: "1px solid rgba(228,218,206,0.2)",
titleText: "#f7f2ea",
metaText: "#d6cec3",
usageText: "#e8dfd4",
badgeOkBg: "rgba(78, 123, 112, 0.56)",
badgeOkText: "#d7f0e5",
badgeWarnBg: "rgba(146, 118, 67, 0.6)",
badgeWarnText: "#fff0c9",
badgeFailBg: "rgba(145, 88, 84, 0.62)",
badgeFailText: "#ffe2de",
badgeIdleBg: "rgba(117, 114, 112, 0.58)",
badgeIdleText: "#f0e8de",
rowQueryBg: "linear-gradient(135deg, rgba(83, 129, 120, 0.9), rgba(70, 107, 118, 0.9))",
rowDeleteBg: "linear-gradient(135deg, rgba(152, 87, 92, 0.9), rgba(124, 72, 82, 0.9))",
rowBtnBorder: "1px solid rgba(231,222,211,0.16)",
rowBtnText: "#fffdf9",
topBtnBorder: "1px solid rgba(225,216,205,0.2)",
topBtnText: "#fff9f1",
topBtnBg: {
query: "linear-gradient(135deg, rgba(83, 105, 139, 0.92), rgba(71, 90, 120, 0.94))",
delete: "linear-gradient(135deg, rgba(148, 86, 92, 0.94), rgba(124, 72, 84, 0.94))",
},
},
light: {
panelText: "#2d2a26",
panelBg: "linear-gradient(156deg, rgba(255, 252, 246, 0.9), rgba(244, 241, 236, 0.92))",
panelBorder: "1px solid rgba(165, 156, 144, 0.42)",
panelShadow: "0 14px 30px rgba(126, 119, 108, 0.18)",
softBg: "rgba(255, 251, 245, 0.86)",
softBorder: "1px solid rgba(171, 163, 152, 0.28)",
metricBg: "rgba(255, 255, 252, 0.92)",
metricBorder: "1px solid rgba(177, 168, 157, 0.28)",
metricLabelText: "#666058",
metricValueText: "#25221e",
statusText: "#312d28",
summaryText: "#4f5f79",
tipText: "#8a5d3a",
listBg: "rgba(255, 254, 250, 0.74)",
listBorder: "1px solid rgba(171, 163, 152, 0.35)",
toggleBg: "linear-gradient(145deg, rgba(251, 250, 247, 0.96), rgba(239, 236, 230, 0.94))",
toggleText: "#5d6273",
toggleBorder: "1px solid rgba(164, 156, 145, 0.48)",
toggleShadow: "0 8px 18px rgba(136, 129, 118, 0.2)",
emptyText: "#5f5a53",
cardBg: "linear-gradient(155deg, rgba(255, 255, 252, 0.88), rgba(247, 244, 238, 0.84))",
cardBorder: "1px solid rgba(177, 168, 157, 0.3)",
titleText: "#25221e",
metaText: "#5c564f",
usageText: "#3d3832",
badgeOkBg: "rgba(212, 237, 227, 0.92)",
badgeOkText: "#24564c",
badgeWarnBg: "rgba(250, 233, 187, 0.96)",
badgeWarnText: "#7a5a16",
badgeFailBg: "rgba(247, 216, 210, 0.92)",
badgeFailText: "#7b3537",
badgeIdleBg: "rgba(233, 227, 218, 0.96)",
badgeIdleText: "#554f48",
rowQueryBg: "linear-gradient(135deg, rgba(95, 150, 140, 0.9), rgba(76, 124, 131, 0.92))",
rowDeleteBg: "linear-gradient(135deg, rgba(196, 112, 119, 0.9), rgba(170, 90, 102, 0.9))",
rowBtnBorder: "1px solid rgba(126, 117, 107, 0.24)",
rowBtnText: "#fffdfa",
topBtnBorder: "1px solid rgba(127, 119, 109, 0.24)",
topBtnText: "#fffdf9",
topBtnBg: {
query: "linear-gradient(135deg, rgba(102, 134, 176, 0.92), rgba(84, 113, 155, 0.92))",
delete: "linear-gradient(135deg, rgba(198, 111, 121, 0.92), rgba(171, 91, 103, 0.92))",
},
},
};
function readThemeFromProjectStore() {
try {
const raw = localStorage.getItem(THEME_STORAGE_KEY);
if (!raw) return null;
lastThemeStoreRaw = raw;
const parsed = JSON.parse(raw);
const resolved = parsed?.state?.resolvedTheme || parsed?.resolvedTheme;
if (resolved === "dark" || resolved === "light") return resolved;
const theme = parsed?.state?.theme || parsed?.theme;
if (theme === "dark" || theme === "light") return theme;
} catch (_) {}
return null;
}
function readThemeFromDom() {
try {
const dt = (document.documentElement.getAttribute("data-theme") || "").toLowerCase();
if (dt === "dark") return "dark";
if (dt === "light") return "light";
} catch (_) {}
return null;
}
function resolveTheme() {
const domTheme = readThemeFromDom();
if (domTheme) return domTheme;
const storeTheme = readThemeFromProjectStore();
if (storeTheme) return storeTheme;
return colorQuery.matches ? "dark" : "light";
}
let activeTheme = resolveTheme();
let panelBusy = false;
let currentChannelFilter = "all";
let currentPage = 1;
let currentHealthFilter = "all";
function token() {
return THEME_TOKENS[activeTheme] || THEME_TOKENS.dark;
}
function setProgress(text) {
progressText.textContent = text || "";
progressText.style.display = text ? "block" : "none";
}
function styleSegmentButton(btn, active, tone) {
const t = token();
const toneName = tone || "neutral";
const activeBg = toneName === "delete" ? t.topBtnBg.delete : toneName === "ok" ? t.rowQueryBg : t.topBtnBg.query;
const activeBorder = toneName === "delete" ? t.rowBtnBorder : t.topBtnBorder;
btn.style.border = active ? activeBorder : "1px solid transparent";
btn.style.background = active ? activeBg : "transparent";
btn.style.color = active ? t.rowBtnText : t.panelText;
btn.style.boxShadow = active ? "0 8px 18px rgba(0,0,0,0.12)" : "none";
btn.style.opacity = btn.disabled ? "0.45" : active ? "1" : "0.92";
}
function applyTheme(themeName) {
if (!THEME_TOKENS[themeName]) return;
activeTheme = themeName;
const t = token();
panel.style.color = t.panelText;
panel.style.background = t.panelBg;
panel.style.border = t.panelBorder;
panel.style.boxShadow = t.panelShadow;
[metricTotal, metricHealthy, metricNoQuota, metricFailed].forEach((metric) => {
metric.box.style.background = t.metricBg;
metric.box.style.border = t.metricBorder;
metric.box.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.04)";
metric.name.style.color = t.metricLabelText;
metric.value.style.color = t.metricValueText;
});
progressText.style.color = t.summaryText;
actionHint.style.color = t.summaryText;
[channelTabs, healthTabs, pagerWrap].forEach((wrap) => {
wrap.style.background = t.softBg;
wrap.style.border = t.softBorder;
wrap.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.04)";
});
pageText.style.color = t.summaryText;
list.style.background = t.listBg;
list.style.border = t.listBorder;
toggleBtn.style.background = t.toggleBg;
toggleBtn.style.color = t.toggleText;
toggleBtn.style.border = t.toggleBorder;
toggleBtn.style.boxShadow = t.toggleShadow;
[btnBatchQuery, btnQuickCheck, btnBatchDelete].forEach((btn) => {
const role = btn.dataset.role || "query";
btn.style.border = t.topBtnBorder;
btn.style.color = t.topBtnText;
btn.style.background = t.topBtnBg[role] || t.topBtnBg.query;
btn.style.boxShadow = "0 12px 24px rgba(0,0,0,0.14)";
});
}
function syncTheme() {
applyTheme(resolveTheme());
scheduleRender();
}
function setBusy(busy, role) {
panelBusy = Boolean(busy);
[btnBatchQuery, btnQuickCheck, btnBatchDelete].forEach((btn) => {
const isBusyBtn = panelBusy && (btn.dataset.role === (role || "query"));
btn.disabled = panelBusy;
btn.style.opacity = panelBusy ? "0.74" : "1";
btn.style.cursor = panelBusy ? "not-allowed" : "pointer";
btn.textContent = isBusyBtn ? `${btn.dataset.text}...` : btn.dataset.text;
});
}
function getFilteredItems(items) {
const key = normalizeChannelKey(currentChannelFilter);
let result = !key || key === "all" ? items.slice() : items.filter((item) => normalizeChannelKey(item?.channel) === key);
if (currentHealthFilter === "failed") result = result.filter((item) => isFailedState(item));
if (currentHealthFilter === "healthy") result = result.filter((item) => isHealthyState(item));
if (currentHealthFilter === "quota") result = result.filter((item) => isQuotaState(item));
return result;
}
function getTotalPages(items) {
return Math.max(1, Math.ceil(items.length / PAGE_SIZE));
}
function getPageItems(items) {
const totalPages = getTotalPages(items);
currentPage = Math.min(Math.max(1, currentPage), totalPages);
const start = (currentPage - 1) * PAGE_SIZE;
return items.slice(start, start + PAGE_SIZE);
}
function updateSummary() {
const s = collectStats(pendingFiles);
const totalValue = inventorySnapshot.loaded ? inventorySnapshot.total : pendingFiles.length;
metricTotal.value.textContent = String(totalValue || 0);
metricHealthy.value.textContent = String(s.healthy);
metricNoQuota.value.textContent = String(s.quota);
metricFailed.value.textContent = String(s.failed);
}
function renderChannelTabs() {
currentChannelFilter = "all";
channelTabs.innerHTML = "";
channelTabs.style.display = "none";
}
function renderHealthTabs() {
const options = [
{ value: "all", label: "全部", tone: "query" },
{ value: "healthy", label: "健康", tone: "ok" },
{ value: "quota", label: "无额度", tone: "query" },
{ value: "failed", label: "失效", tone: "delete" },
];
healthTabs.innerHTML = "";
options.forEach((option) => {
const btn = createSegmentBtn(option.label);
const isActive = option.value === currentHealthFilter;
styleSegmentButton(btn, isActive, option.tone);
btn.addEventListener("click", () => {
if (currentHealthFilter === option.value) return;
currentHealthFilter = option.value;
currentPage = 1;
scheduleRender();
});
healthTabs.appendChild(btn);
});
}
function renderPager(totalPages) {
pageText.textContent = `${currentPage} / ${totalPages}`;
prevBtn.disabled = currentPage <= 1 || panelBusy;
nextBtn.disabled = currentPage >= totalPages || panelBusy;
styleSegmentButton(prevBtn, true, "query");
styleSegmentButton(nextBtn, true, "query");
prevBtn.style.opacity = prevBtn.disabled ? "0.45" : "1";
nextBtn.style.opacity = nextBtn.disabled ? "0.45" : "1";
prevBtn.style.cursor = prevBtn.disabled ? "not-allowed" : "pointer";
nextBtn.style.cursor = nextBtn.disabled ? "not-allowed" : "pointer";
}
let renderQueued = false;
function scheduleRender() {
if (renderQueued) return;
renderQueued = true;
requestAnimationFrame(() => {
renderQueued = false;
renderList();
});
}
function renderList() {
const t = token();
function applyToneBadge(el, tone) {
if (tone === "ok") {
el.style.background = t.badgeOkBg;
el.style.color = t.badgeOkText;
return;
}
if (tone === "warn") {
el.style.background = t.badgeWarnBg;
el.style.color = t.badgeWarnText;
return;
}
if (tone === "fail") {
el.style.background = t.badgeFailBg;
el.style.color = t.badgeFailText;
return;
}
el.style.background = t.badgeIdleBg;
el.style.color = t.badgeIdleText;
}
updateSummary();
renderChannelTabs(pendingFiles);
renderHealthTabs();
list.innerHTML = "";
const filteredItems = getFilteredItems(pendingFiles);
const totalPages = getTotalPages(filteredItems);
const pageItems = getPageItems(filteredItems);
renderPager(totalPages);
if (!filteredItems.length) {
list.style.display = "block";
const empty = document.createElement("div");
empty.textContent = inventorySnapshot.loaded ? "当前没有 Codex 凭证" : "还没有 Codex 数据";
empty.style.fontSize = "12px";
empty.style.lineHeight = "1.6";
empty.style.color = t.emptyText;
empty.style.padding = "14px";
empty.style.textAlign = "center";
list.appendChild(empty);
return;
}
list.style.display = "grid";
list.style.gridTemplateColumns = isMobile ? "minmax(0, 1fr)" : "repeat(2, minmax(0, 1fr))";
list.style.gap = "8px";
list.style.alignContent = "start";
pageItems.forEach((item, idx) => {
const card = document.createElement("div");
card.style.border = t.cardBorder;
card.style.borderRadius = "14px";
card.style.padding = "9px 10px";
card.style.background = t.cardBg;
card.style.backdropFilter = "blur(6px)";
card.style.display = "flex";
card.style.flexDirection = "column";
card.style.gap = "6px";
card.style.minWidth = "0";
card.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.05)";
card.style.transition = "transform 0.18s ease, box-shadow 0.18s ease";
card.addEventListener("mouseenter", () => {
card.style.transform = "translateY(-1px)";
card.style.boxShadow = "0 10px 20px rgba(0,0,0,0.10)";
});
card.addEventListener("mouseleave", () => {
card.style.transform = "translateY(0)";
card.style.boxShadow = "inset 0 1px 0 rgba(255,255,255,0.05)";
});
const headRow = document.createElement("div");
headRow.style.display = "flex";
headRow.style.alignItems = "flex-start";
headRow.style.justifyContent = "space-between";
headRow.style.gap = "8px";
const nameWrap = document.createElement("div");
nameWrap.style.minWidth = "0";
nameWrap.style.flex = "1";
const absoluteIndex = (currentPage - 1) * PAGE_SIZE + idx + 1;
const name = document.createElement("div");
name.textContent = `${absoluteIndex}. ${item.name}`;
name.style.fontSize = "12px";
name.style.fontWeight = "700";
name.style.lineHeight = "1.45";
name.style.wordBreak = "break-all";
name.style.color = t.titleText;
nameWrap.appendChild(name);
const meta = document.createElement("div");
meta.textContent = getTechDetailText(item);
meta.style.fontSize = "10px";
meta.style.color = t.metaText;
meta.style.wordBreak = "break-all";
meta.style.lineHeight = "1.5";
nameWrap.appendChild(meta);
headRow.appendChild(nameWrap);
const badges = document.createElement("div");
badges.style.display = "flex";
badges.style.alignItems = "center";
badges.style.gap = "6px";
badges.style.flexWrap = "wrap";
badges.style.justifyContent = "flex-end";
const channelBadge = document.createElement("span");
channelBadge.textContent = formatChannelLabel(item.channel);
channelBadge.style.display = "inline-flex";
channelBadge.style.alignItems = "center";
channelBadge.style.padding = "3px 8px";
channelBadge.style.borderRadius = "999px";
channelBadge.style.fontSize = "10px";
channelBadge.style.fontWeight = "700";
channelBadge.style.background = t.badgeIdleBg;
channelBadge.style.color = t.badgeIdleText;
badges.appendChild(channelBadge);
const enabledBadge = document.createElement("span");
enabledBadge.textContent = getEnabledStateText(item);
enabledBadge.style.display = "inline-flex";
enabledBadge.style.alignItems = "center";
enabledBadge.style.padding = "3px 8px";
enabledBadge.style.borderRadius = "999px";
enabledBadge.style.fontSize = "10px";
enabledBadge.style.fontWeight = "700";
applyToneBadge(enabledBadge, getEnabledStateTone(item));
badges.appendChild(enabledBadge);
const actualBadge = document.createElement("span");
actualBadge.textContent = getActualResultText(item);
actualBadge.style.display = "inline-flex";
actualBadge.style.alignItems = "center";
actualBadge.style.padding = "3px 8px";
actualBadge.style.borderRadius = "999px";
actualBadge.style.fontSize = "10px";
actualBadge.style.fontWeight = "800";
applyToneBadge(actualBadge, getActualResultTone(item));
badges.appendChild(actualBadge);
headRow.appendChild(badges);
card.appendChild(headRow);
const usageRow = document.createElement("div");
usageRow.textContent = item.queryState === "unqueried" ? "尚未检查" : getUsageText(item);
usageRow.style.fontSize = "11px";
usageRow.style.lineHeight = "1.5";
usageRow.style.color = t.usageText;
card.appendChild(usageRow);
list.appendChild(card);
});
}
function applyPanelState() {
if (isMobile) {
panel.style.left = "10px";
panel.style.right = "10px";
panel.style.top = "auto";
panel.style.bottom = "64px";
panel.style.width = "auto";
panel.style.maxHeight = "74vh";
panel.style.transform = isOpen ? "translateY(0) scale(1)" : "translateY(calc(100% + 32px)) scale(0.985)";
list.style.minHeight = "min(48vh, 420px)";
toggleBtn.style.left = "auto";
toggleBtn.style.right = "16px";
toggleBtn.style.top = "auto";
toggleBtn.style.bottom = "16px";
toggleBtn.textContent = isOpen ? "收起" : "展开";
} else {
panel.style.left = "auto";
panel.style.right = "16px";
panel.style.top = "70px";
panel.style.bottom = "auto";
panel.style.width = "min(920px, calc(100vw - 24px))";
panel.style.maxHeight = "80vh";
panel.style.transform = isOpen ? "translateX(0) scale(1)" : "translateX(calc(100% + 24px)) scale(0.985)";
list.style.minHeight = "0";
toggleBtn.style.left = "auto";
toggleBtn.style.right = "16px";
toggleBtn.style.top = "auto";
toggleBtn.style.bottom = "16px";
toggleBtn.textContent = isOpen ? "收起" : "展开";
}
panel.style.opacity = isOpen ? "1" : "0";
panel.style.pointerEvents = isOpen ? "auto" : "none";
panel.style.visibility = isOpen ? "visible" : "hidden";
}
function setOpen(open) {
isOpen = Boolean(open);
applyPanelState();
}
toggleBtn.addEventListener("click", () => {
setOpen(!isOpen);
});
const mediaHandler = () => {
isMobile = mediaQuery.matches;
applyPanelState();
scheduleRender();
};
if (typeof mediaQuery.addEventListener === "function") mediaQuery.addEventListener("change", mediaHandler);
else if (typeof mediaQuery.addListener === "function") mediaQuery.addListener(mediaHandler);
const colorHandler = () => {
syncTheme();
};
if (typeof colorQuery.addEventListener === "function") colorQuery.addEventListener("change", colorHandler);
else if (typeof colorQuery.addListener === "function") colorQuery.addListener(colorHandler);
window.addEventListener("storage", (ev) => {
if (!ev || ev.key === null || ev.key === THEME_STORAGE_KEY) syncTheme();
});
const rootObserver = new MutationObserver((mutations) => {
for (const m of mutations) {
if (m.type === "attributes" && m.attributeName === "data-theme") {
syncTheme();
break;
}
}
});
rootObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
setInterval(() => {
try {
const raw = localStorage.getItem(THEME_STORAGE_KEY) || "";
if (raw !== lastThemeStoreRaw) {
lastThemeStoreRaw = raw;
syncTheme();
}
} catch (_) {}
}, 1200);
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && isOpen) setOpen(false);
});
prevBtn.addEventListener("click", () => {
if (currentPage <= 1 || panelBusy) return;
currentPage -= 1;
scheduleRender();
});
nextBtn.addEventListener("click", () => {
const totalPages = getTotalPages(getFilteredItems(pendingFiles));
if (currentPage >= totalPages || panelBusy) return;
currentPage += 1;
scheduleRender();
});
async function batchQueryItems(items, alreadyBusy) {
const codexItems = items.filter((item) => supportsActiveCheck(item));
if (!codexItems.length) {
setProgress("Codex 0");
return;
}
if (!alreadyBusy) setBusy(true, "query");
let ok = 0;
let fail = 0;
let processed = 0;
try {
await runWithConcurrency(codexItems, QUERY_CONCURRENCY, async (item) => {
try {
const res = await callWithAuthRetry(`检测 Codex ${item.name}`, (token) =>
queryUsageByAuthIndex(token, item)
);
const snap = parseUsageSnapshot(res.bodyObj);
markQueryResult(item, res, snap);
if (isHealthyState(item)) ok += 1;
else if (isFailedState(item)) fail += 1;
} catch (_) {
markQueryFailed(item, "ERR");
fail += 1;
}
processed += 1;
setProgress(`Codex ${processed}/${codexItems.length}`);
scheduleRender();
});
const done = collectStats(pendingFiles);
setProgress(`健康 ${done.healthy} 无额度 ${done.quota} 失效 ${done.failed}`);
} finally {
scheduleRender();
if (!alreadyBusy) setBusy(false);
}
}
async function fetchAndQueryAll() {
setBusy(true, "query");
setProgress("读取中");
try {
const allFiles = await callWithAuthRetry("读取全部 Codex 凭证", (token) => fetchAllFiles(token));
const codexFiles = allFiles.filter((item) => supportsActiveCheck(item));
inventorySnapshot = buildInventorySnapshot(codexFiles);
currentPage = 1;
pendingFiles = codexFiles.slice();
renderList();
if (!pendingFiles.length) {
setProgress("总数 0");
return;
}
await batchQueryItems(pendingFiles, true);
} catch (e) {
setProgress(e?.status === 401 ? "读取失败" : "检查失败");
} finally {
setBusy(false);
scheduleRender();
}
}
async function fetchAndQueryNonActive() {
setBusy(true, "query");
setProgress("读取异常中");
try {
const abnormalFiles = await callWithAuthRetry("读取异常 Codex 凭证", (token) => fetchNonActive(token));
const codexFiles = abnormalFiles.filter((item) => supportsActiveCheck(item));
inventorySnapshot = buildInventorySnapshot(codexFiles);
currentPage = 1;
pendingFiles = codexFiles.slice();
renderList();
if (!pendingFiles.length) {
setProgress("异常 0");
return;
}
await batchQueryItems(pendingFiles, true);
} catch (e) {
setProgress(e?.status === 401 ? "读取失败" : "快速检查失败");
} finally {
setBusy(false);
scheduleRender();
}
}
function isToggleUnsupportedError(err) {
const status = Number(err?.status);
return status === 404 || status === 405 || status === 501;
}
function syncLocalDisabledState(item, disabled) {
item.disabled = Boolean(disabled);
if (disabled) {
item.status = "disabled";
item.statusMessage = "disabled via management API";
return;
}
item.status = "active";
item.statusMessage = "";
}
async function updateItemsDisabledState(items, disabled, phaseLabel, progressPrefix) {
if (!items.length) {
return { success: 0, failed: 0, unsupported: false };
}
let success = 0;
let failed = 0;
for (let i = 0; i < items.length; i += 1) {
const item = items[i];
setProgress(`${progressPrefix} ${i + 1}/${items.length}`);
try {
await callWithAuthRetry(`${phaseLabel} ${item.name}`, (token) =>
patchAuthFileDisabled(token, item.name, disabled)
);
syncLocalDisabledState(item, disabled);
success += 1;
} catch (err) {
if (isToggleUnsupportedError(err)) {
return { success, failed, unsupported: true };
}
failed += 1;
}
scheduleRender();
}
return { success, failed, unsupported: false };
}
async function deleteFailedItems() {
if (!pendingFiles.length) {
setProgress("总数 0");
return;
}
const deletable = pendingFiles.filter((x) => supportsActiveCheck(x) && x.deleteEligible && x.name && x.name !== "(no-name)");
const enableTargets = pendingFiles.filter((x) =>
supportsActiveCheck(x) && isHealthyState(x) && x.disabled && x.name && x.name !== "(no-name)"
);
const disableTargets = pendingFiles.filter((x) =>
supportsActiveCheck(x) && isQuotaState(x) && !x.disabled && x.name && x.name !== "(no-name)"
);
if (!deletable.length && !enableTargets.length && !disableTargets.length) {
setProgress("无需优化");
return;
}
setBusy(true, "delete");
let deleteSuccess = 0;
const deletedSet = new Set();
let enableSummary = { success: 0, failed: 0, unsupported: false };
let disableSummary = { success: 0, failed: 0, unsupported: false };
try {
for (let i = 0; i < deletable.length; i += 1) {
const item = deletable[i];
setProgress(`删除失效 ${i + 1}/${deletable.length}`);
try {
const r = await callWithAuthRetry(`删除 ${item.name}`, (token) =>
deleteByName(token, item.name)
);
if (r.ok) {
deleteSuccess += 1;
deletedSet.add(item);
}
} catch (_) {
// keep failed item in list
}
scheduleRender();
}
pendingFiles = pendingFiles.filter((x) => !deletedSet.has(x));
applyDeletedItems(Array.from(deletedSet));
enableSummary = await updateItemsDisabledState(enableTargets, false, "启用凭证", "启用健康");
disableSummary = await updateItemsDisabledState(disableTargets, true, "禁用凭证", "禁用无额度");
const deleteFailed = deletable.length - deleteSuccess;
const parts = [
`已删 ${deleteSuccess}`,
deleteFailed ? `未删 ${deleteFailed}` : "",
`已启用 ${enableSummary.success}`,
enableSummary.failed ? `启用失败 ${enableSummary.failed}` : "",
`已禁用 ${disableSummary.success}`,
disableSummary.failed ? `禁用失败 ${disableSummary.failed}` : "",
].filter(Boolean);
if (enableSummary.unsupported || disableSummary.unsupported) {
parts.push("启用/禁用接口不可用");
}
setProgress(parts.join(" "));
} finally {
setBusy(false, "delete");
scheduleRender();
}
}
btnBatchQuery.addEventListener("click", async () => {
if (panelBusy) return;
await fetchAndQueryAll();
});
btnQuickCheck.addEventListener("click", async () => {
if (panelBusy) return;
await fetchAndQueryNonActive();
});
btnBatchDelete.addEventListener("click", async () => {
if (panelBusy) return;
await deleteFailedItems();
});
syncTheme();
renderList();
applyPanelState();
document.body.appendChild(panel);
document.body.appendChild(toggleBtn);
}
function init() {
installTokenSniffer();
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", createSidebar, { once: true });
} else {
createSidebar();
}
}
init();
})();
--【贰】--:
好的 佬 谢谢 我去试试看看,不然太影响效率了

