Codex Auths Validator
Validate and clean Codex auth JSON files in a batch.
Run scope
- Target directories(可配置,适配所有用户):
auths_dir:有效且有额度目录(用户提供或自动探测)auths_no_quota_dir:有效但无额度/限流目录(默认<auths_dir>_no_quota)
- Target files:
*.json - Validation endpoint:
GET https://chatgpt.com/backend-api/wham/usage - Required headers:
Authorization: Bearer <access_token>Chatgpt-Account-Id: <account_id>User-Agent: codex_cli_rs/0.76.0 (Debian 13.0.0; x86_64) WindowsTerminal
自动识别与验证范围(对齐原项目多类型能力)
本 skill 会先自动识别 JSON 所属 provider,再决定验证方式:
- 识别优先级:
type/provider字段 -> 特征字段推断 - 已覆盖类型(与原项目
AuthFileType对齐):qwen/kimi/gemini/gemini-cli/aistudio/claude/codex/antigravity/iflow/vertexunknown(无法明确分类时)
Deduplication rules(去重规则)
在校验之前,先扫描 auths_dir + auths_no_quota_dir 所有 JSON,对比 account_id 字段:
- 扫描顺序:先扫
auths_dir(有额度)再扫auths_no_quota_dir(无额度) - 同一
account_id优先保留有额度的那个;其余重复文件移入auths_invalid_dir,原因记为INVALID_DUPLICATE
此逻辑适用于:
hourly-reconcile.mjs(每小时校验前自动去重)import-archive.mjs(ZIP/7z 导入后、输出报告前自动去重)
Pre-flight expiry check + 自动续期(Token Refresh)
在调用远程 API 之前,按三层优先级判断 token 是否过期:
优先级(高→低):
1. JWT id_token 里的 exp 字段(Base64 decode,最权威)
2. json.expired 字段
3. json.last_refresh + 7天(兜底推算,假设 codex token 7天有效期)
无法判断 → 默认未过期,继续走 API
过期后不直接丢弃,而是先尝试用 refresh_token 续期:
POST https://auth0.openai.com/oauth/token
{
grant_type: "refresh_token",
client_id: "pdlLIX2Y72MIl2rhLhTE9VV9bN905kBh",
refresh_token: <json.refresh_token>
}
续期成功 → 写回文件(更新 access_token / expired / last_refresh / id_token),继续 API 校验
refresh_token 失效(null / invalid_grant) → ⚠️ 不直接判 INVALID,继续用原 access_token 走 API 校验
→ API 返回 401 才判定 INVALID_EXPIRED(access_token 可能比 expired 字段标注时间更长存活)
网络/5xx → TRANSIENT_KEEP,原位保留,下次重试
此逻辑适用于三个脚本:hourly-reconcile.mjs、import-archive.mjs、validate-auths.mjs。
Report 文件自动清理(reports 目录)
hourly-reconcile.mjs 启动时自动清理旧 report 文件:
- 默认保留最近 72 个(对应约 3 天),可通过
--max-report-files <n>覆盖 - 按 mtime 升序排列,删除超出数量的最老文件
- 防止 reports 目录无限膨胀
Invalid 目录积累警告
当 auths_invalid 累积超过 500 个文件时,hourly-reconcile 会在报告尾部打印:
⚠️ auths_invalid 已积累 N 个文件,建议运行清理命令:rm -rf /path/to/auths_invalid/*
Decision rules
A) codex 类型(可做远程额度验证)
- 过期检测(三层)→ 尝试 refresh_token 续期 → 续期成功则用新 token;续期失败则用原 token 继续走 API → API 返回 401 才判
INVALID_EXPIRED 200且有额度 -> 放在auths_dir200但无额度(limit_reached=true或 windowused_percent>=100)-> 放在auths_no_quota_dir429(限流/额度耗尽)-> 放在auths_no_quota_dir401/403-> 判定无效,移入auths_invalid_dir5xx/timeout/network-> 临时错误,原位保留(不迁移)
B) 非 codex 类型(先做结构有效性校验)
- 必要字段满足 -> 结构有效,保留原位(可选后续接入 provider 专用远程验证)
- 必要字段缺失 / 坏 JSON /
._*.json-> 判定无效,移入auths_invalid_dir
统一状态输出(用于给用户解释"为什么无效")
VALID_QUOTA:有效且有额度VALID_NO_QUOTA:有效但无额度/被限流INVALID_AUTH:认证失败(401/403)INVALID_EXPIRED:token 过期、refresh_token 失效、且 API 也返回 401(三重确认才丢弃)INVALID_JSON:JSON 格式损坏INVALID_MISSING_FIELDS:缺少必要字段INVALID_APPLEDOUBLE:._*.json垃圾文件SCHEMA_VALID_PROVIDER:非 codex,结构有效(保留)INVALID_DUPLICATE:account_id 重复,优先保留有额度的,其余移除TRANSIENT_KEEP:临时错误(网络/5xx/续期失败),原位保留下次重试- reason=
refreshed:token 已过期但通过 refresh_token 成功续期并继续校验
Move invalid files into auths_invalid_dir (default: <auths_dir>_invalid). Do not hard-delete immediately.
Invalid directory location pattern:
/home/docker/CLIProxyAPI/auths_invalid/
Write report file:
_validation_report.json
Bulk cleanup command (after user confirmation):
rm -rf /home/docker/CLIProxyAPI/auths_invalid/*
入口原则(项目主标语)
OpenClaw 的 Codex 认证 JSON 自动化验证技能:每小时清理、ZIP/7z 导入校验、每日 GitHub 学习巡检。
只要告诉我 JSON 文件存放的目录,我就能自己工作。
如果用户不提供目录:
- 默认视为用户可能安装了
Cli-Proxy-API-Management-Center。 - 按源项目配置与 Docker 挂载线索自动探测目录(
auth-dir优先)。
首次安装引导(降低新用户操作)
如果用户第一次使用本 skill,先走"自动发现 + 最少提问"流程:
- 先自动探测认证目录(不要一上来就问用户):
node skills/codex-auths-validator/scripts/discover-auth-dir.mjs
- 优先采用探测结果里的
recommended目录。 - 若探测不到可靠目录,才询问用户认证目录路径(用户说一个路径就直接支持,不要求固定目录)。
- 若用户安装了
Cli-Proxy-API-Management-Center,优先检查其auth-dir配置与 Docker 挂载路径。 - 自动创建无额度目录:
<auth_dir>_no_quota。 - 首次引导时用中文给用户明确说明:
- 你识别到的认证目录
- 双目录规则(有额度/无额度)
- 接下来会自动创建的每小时与每日任务
目标:尽量少让新用户手动配置,能自动发现就自动发现。
Scripts mapping(两个脚本分别做什么)
1) scripts/validate-auths.mjs(一次性人工清理/导入前筛选)
用途:
- 手动全量校验、一次性清理、导入前预检。
- 支持无效文件
delete或invalid两种模式(invalid表示移动到auths_invalid_dir)。
典型命令:
node skills/codex-auths-validator/scripts/validate-auths.mjs \
--dir-quota /home/docker/CLIProxyAPI/auths \
--dir-no-quota /home/docker/CLIProxyAPI/auths_no_quota \
--invalid-action invalid \
--concurrency 40 \
--timeout-ms 12000
--invalid-action:
delete:无效文件直接删除invalid:无效文件移入auths_invalid_dir(默认<auths_dir>_invalid)
3) scripts/hourly-run-and-notify.sh(系统 crontab 专用,直发 TG)
用途:
- 供系统 crontab 每小时调用,完全不依赖 OpenClaw cron delivery 机制。
- 直接运行
hourly-reconcile.mjs,默认发精简摘要(从reports/hourly-reconcile-*.json读取关键统计,避免解析文本误判);若auths与auths_no_quota两个目录都为空(以目录内*.json文件数判定,避免解析输出误判),则只发极简通知。 - 详细日志默认不发送(用户反馈不需要);仅当脚本异常(exit!=0)才自动附详细日志文件。若确实需要在无效/临时错误时也发,可设置
SEND_DETAIL=1。 - 这是最稳定的通知方式:不需要 LLM session,不受 auth-profiles.json 影响。
系统 crontab 条目(只配置一个目录即可运行):
0 * * * * AUTH_DIR=/home/docker/CLIProxyAPI/auths CODEX_AUTH_VALIDATOR_SECRETS_FILE=/root/.openclaw/secrets/codex-auths-validator.env bash /root/.openclaw/workspace/skills/codex-auths-validator/scripts/hourly-run-and-notify.sh >> /tmp/codex-auths-cron.log 2>&1
不写 AUTH_DIR 时默认使用:/home/docker/CLIProxyAPI/auths。
安装命令:
(crontab -l 2>/dev/null | grep -v hourly-run-and-notify; echo "0 * * * * CODEX_AUTH_VALIDATOR_SECRETS_FILE=/root/.openclaw/secrets/codex-auths-validator.env bash /root/.openclaw/workspace/skills/codex-auths-validator/scripts/hourly-run-and-notify.sh >> /tmp/codex-auths-cron.log 2>&1") | crontab -
5) scripts/hourly-reconcile.mjs(每小时定时任务专用,稳定版)
用途:
- 供 cron 每小时自动任务调用。
- 内置并发锁(
/tmp/codex-auths-hourly.lock)防止重叠执行。 - 临时错误(timeout/network/5xx)保留原位,避免统计抖动。
- 无效 JSON 不直接删除,移动到无效目录(默认
<auths_dir>_invalid)并输出原因,提示用户是否删除。
典型命令:
node skills/codex-auths-validator/scripts/hourly-reconcile.mjs \
--dir-quota /home/docker/CLIProxyAPI/auths \
--dir-no-quota /home/docker/CLIProxyAPI/auths_no_quota \
--dir-invalid /home/docker/CLIProxyAPI/auths_invalid \
--concurrency 40 \
--timeout-ms 12000
Output contract
Return a JSON summary including:
- total files
- moved files
- moved AppleDouble count
- kept count
- moved-by-validation count
- reason histogram
- invalid directory path
- moved samples
Archive import workflow(zip/7z 自动接管)
When user provides .zip or .7z package:
- Auto-extract archive to temp workspace.
- Auto-scan all files; only
*.jsonenters validation pipeline. - Non-JSON files (code, scripts, docs, binaries, etc.) are ignored and never imported.
- Validate JSON files with this skill rules.
- Classify to target folders:
- valid + has quota ->
auths_dir - valid but no quota / 429 ->
auths_no_quota_dir - invalid ->
auths_invalid_dir
- valid + has quota ->
- Return summary:
- total files in archive
- json files processed
- non-json files ignored
- imported to
auths - imported to
auths_no_quota - moved to
auths_invalid - status/reason histogram
Recommended command:
node skills/codex-auths-validator/scripts/import-archive.mjs \
--archive <package.zip|package.7z> \
--dir-quota <auths_dir> \
--dir-no-quota <auths_no_quota_dir> \
--dir-invalid <auths_invalid_dir>
Suggested command sequence:
mkdir -p /tmp/codex-auths-import
unzip <package.zip> -d /tmp/codex-auths-import
# validate extracted files (same endpoint/rules), then copy passed ones:
cp <passed-json-files> /home/docker/CLIProxyAPI/auths/
Hard delete (only after confirmation)
After user confirms, delete invalid directory and import temp folders:
rm -rf /home/docker/CLIProxyAPI/auths_invalid/*
rm -rf /tmp/codex-auths-import-<timestamp>
Bulk cleanup command (remove all historical import temp folders):
for d in /tmp/codex-auths-import-*; do
[ -e "$d" ] && rm -rf "$d"
done
Hourly scheduled validation & delete workflow
When user asks for hourly auto-clean:
- Create a cron job (Asia/Shanghai) with
expr: 0 * * * *. - Each run validates all
*.jsonin/home/docker/CLIProxyAPI/auths. - Move all unqualified files into
auths_invalid_dir(no hard delete) based on rules:- unqualified: 401/403, malformed JSON, missing token/account_id,
._*.json - qualified: 200/429
- unqualified: 401/403, malformed JSON, missing token/account_id,
- Send user summary after each run in Chinese:
- 总共:<总数> 个
- 合格的:<合格数> 个
- 不合格并删除的:<删除数> 个
- 删除原因统计:<按原因计数>
- If failed, report error and processed progress.
Recommended cron payload style(结论):
- 不要用 OpenClaw cron 来做"每小时跑脚本+发TG"(详见
reports/lock-incident.md)。 - 该场景必须用系统 crontab +
scripts/hourly-run-and-notify.sh。 - OpenClaw cron 仅保留用于"AI 学习巡检/总结"等纯 agentTurn 任务。
对话总结(阶段成果,需持续更新)
以下为本技能从0到1的关键对话沉淀(用于新维护者快速理解):
- 基础能力落地:先实现 codex JSON 批量验证、失效清理、ZIP 导入。
- 双目录分层:将"有效有额度/有效无额度"拆分为
auths_dir与auths_no_quota_dir。 - 稳定性修复:新增每小时巡检并发锁,避免重叠执行导致统计波动。
- 无效文件策略升级:无效文件不直接删,统一入
auths_invalid_dir并附原因,询问用户是否删除。 - 多 provider 识别:对齐 Cli-Proxy-API-Management-Center 类型体系,先识别 provider 再选择验证方式。
- 归档接管能力:支持 ZIP/7z,自动只处理 JSON,忽略代码和其他非 JSON 文件;新增专用脚本
import-archive.mjs。 - 新手零配置体验:用户只给一个目录即可自动接管:
--auth-dir <auths_dir>(只给“有额度目录 auths”)- 自动推导
<auths_dir>_no_quota/<auths_dir>_invalid/reports(与 auths 同级) - 若未提供则按 CPA 线索自动探测
auth-dir。
- 自动化运维闭环:固定 3 个定时任务(小时清理/每日学习/每日同步)。
- 文档与仓库同步纪律:任何改动必须同步 SKILL + WORKFLOW + README,并立即中文 commit + push。
- 三层JWT过期检测:优先级 JWT id_token exp > expired 字段 > last_refresh+7天,无法判断默认未过期继续 API 校验。
- refresh_token 自动续期:过期先尝试续期并写回文件救回可用账号;invalid_grant 才 INVALID_EXPIRED;网络错误 TRANSIENT_KEEP。
- account_id 去重:扫描顺序先有额度后无额度,优先保留有额度账号,重复移入 invalid(INVALID_DUPLICATE)。
- reports 目录自动清理:hourly-reconcile 启动时自动清理旧报告,默认保留最近 72 个,--max-report-files 可配置。
- invalid 目录积累警告:超过 500 个时打印警告,提示
rm -rf .../auths_invalid/*清理命令。 - validate-auths.mjs 功能对齐:加入三层过期检测 + refresh_token 续期 + account_id 去重,与 hourly/import 行为一致。
- 续期失败不直接 INVALID(关键修复):refresh_token 失效时不直接丢弃,继续用原 access_token 走 API 校验——因为 OpenAI access_token 实际存活时间可能长于
expired字段标注值。只有 API 返回 401 才最终判定失效。 - cron agentId 必须用 main(运维经验):isolated agent(如 fast-pool)需要独立 auth-profiles.json;若未配置则所有 isolated cron job 会静默 auth 失败,无任何运行输出,用户无感知。创建/修复 cron 任务时统一使用
agentId: "main",sessionKey 对应agent:main:telegram:direct:<chatId>。 - 定时任务通知必须用系统 crontab + shell + curl(架构决策):OpenClaw cron 的两种模式(isolated agentTurn / main systemEvent)均不适合"无人值守执行 shell 脚本+发 TG"场景。isolated 模式需独立 auth key;main systemEvent 只入队文字不保证执行。正确做法:系统 crontab 直接跑 shell 脚本,curl 调 Telegram Bot API 发通知,完全不依赖 LLM session。新增
scripts/hourly-run-and-notify.sh封装此逻辑。 - 每日学习巡检规则升级(2026-03-14):从泛泛
git pull + grep升级为精准接口追踪,明确只追 6 个维度(认证API / Token续期 / Account字段 / provider枚举 / JSON schema / 状态码语义);时间从 00:00 调整为凌晨 01:00(上海);输出格式固定化,每项必须明确说有/无变化;只有真实变化才更新 skill 文件,禁止无中生有。
Learning rationale and evolution notes (must maintain)
Keep this section updated when environment, upstream API behavior, or source project logic changes.
Why these rules were chosen
200means credential is usable now, so keep.429usually means rate/usage exhaustion, not token death, so keep.401/403indicates invalid/unauthorized token/account context, so remove as useless.- malformed JSON / missing
access_token/ missingaccount_idcannot pass API auth, so remove. ._*.jsonare AppleDouble artifacts, not real auth files, so remove.
What was learned from project analysis (CLI Proxy API Management Center)
From router-for-me/Cli-Proxy-API-Management-Center, codex quota UI and logic map to:
- Auth-files page quota refresh action uses quota loader flow.
- Codex quota backend endpoint:
https://chatgpt.com/backend-api/wham/usage. - Required Codex usage request header:
Chatgpt-Account-Id(resolved from auth fileid_tokenpayload fieldchatgpt_account_id/chatgptAccountId). - Usage payload schema (frontend expectation):
plan_typerate_limit/code_review_rate_limitwithprimary_window/secondary_windowandused_percent, pluslimit_reached.additional_rate_limits[](for extra metered features).
- Auth-files status toggle behavior reverted to a dedicated backend endpoint:
PATCH /auth-files/statuswith body{ name, disabled }.- Response contains
{ status, disabled }and UI uses returneddisabledto confirm final state.
- Auth-files UI also treats
status_messageorstatusMessageas the status message source. - 403/404 are rendered as credential/update hints in UI, but operational cleanup here treats 401/403 as invalid credentials and 429 as pass.
Mutable inputs (must re-verify when changed)
Always re-check these before running bulk cleanup in a new environment:
- Auth directory path (default now:
/home/docker/CLIProxyAPI/auths). - Validation endpoint and required headers.
- JSON field schema (
type,access_token,account_id). - User policy on invalid archive vs direct delete.
- User policy on timeout/network handling.
Learning update protocol
When new evidence appears (new GitHub version, API change, user policy change):
- Record what changed and why in this section.
- Update decision rules and execution commands.
- Re-test with a small sample before full batch.
- Keep success snapshots (counts + key reasons) for regression comparison.
- If schedule behavior changes, update cron workflow text too.
Claude 套餐检测接口更新(2026-03-13 巡检)
来源: router-for-me/Cli-Proxy-API-Management-Center commit ccf90f8(2026-03-07)
Claude 配额检测新增了 Profile API 接口来判断套餐类型:
- 新增端点:
GET https://api.anthropic.com/api/oauth/profile- 与
GET https://api.anthropic.com/api/oauth/usage并发请求(Promise.allSettled) - 请求头:
CLAUDE_REQUEST_HEADERS(与 usage 接口相同的 Bearer token)
- 与
- 关键响应字段:
organization.rate_limit_tier(字符串) - 套餐映射表(
CLAUDE_PLAN_TYPE_MAP):default_claude_max_5x→plan_max5(Max 5x 套餐)default_claude_max_20x→plan_max20(Max 20x 套餐)default_claude_pro→plan_pro(Pro 套餐)default_claude_ai→plan_free(免费版)- 未知 tier 值 →
plan_unknown(保底) rate_limit_tier为空 / profile 请求失败 →planType为null(不影响 usage 判断)
- 注意: profile 请求失败不影响 usage 数据解析,两者独立返回。
对 skill 规则影响: Claude auth 文件的额度展示现在可以额外展示套餐类型,但核心 VALID/INVALID 判断逻辑不变(仍依赖 usage 接口的 quota 窗口数据)。
Lock management enhancement (2026-03-09)
hourly-reconcile.mjs lock file format was upgraded from pid\n to pid\ntimestamp\n.
New behavior:
LOCK_MAX_AGE_MS(default 900000 = 15 min): stale thresholdcleanStaleLock(): auto-removes lock if owning PID is dead OR lock age ≥ 15 min- Prevents permanent cron stall from zombie lock files
--lock-max-age-msCLI flag allows override
Key: this means a lock surviving more than 15 minutes will be auto-cleared on next startup - no more manual rm -f /tmp/codex-auths-hourly.lock needed.
Success snapshots (historical)
- Snapshot A: local auth dir full validation
- total: 5125
- kept: 3124
- removed: 2001 (2000 AppleDouble + 1 auth_403)
- Snapshot B: zip import #1
- total in zip: 50
- imported: 50
- failed: 0
- Snapshot C: zip import #2
- total in zip: 1000
- imported: 999
- failed: 1 (auth_403)
- Snapshot D: 7z import
- total in 7z: 1145
- imported to auths: 1145
- imported to auths_no_quota: 0
- moved to auths_invalid: 0
- status: VALID_QUOTA x1145
- Snapshot E: ZIP import #1 (auths_all---03fa8448...zip)
- total files: 6302
- json files: 6301
- imported to auths (quota): 34
- imported to auths_no_quota: 99
- moved to auths_invalid: 6168 (INVALID_AUTH 401)
- post-action: manually deleted 6168 INVALID_AUTH files
- Snapshot F: ZIP import #2 (auths_all---412a6374...zip)
- total files: 6302
- json files: 6301
- INVALID_EXPIRED: 6300
- INVALID_MISSING_FIELDS: 1
- imported to auths: 0
- imported to auths_no_quota: 0
- post-action: manually cleaned auths_invalid (6434 files)
Maintain snapshots so future changes can be compared quickly.
Required sync policy (codex-auths-validator)
For any change related to codex-auths-validator (rules, user path handling, API endpoint, headers, cron behavior, workflow, docs, scripts):
- Update skill files immediately (
SKILL.md/WORKFLOW.md/scripts/*as needed). - Update GitHub-facing documentation immediately (
README.md) to keep repo usage guide consistent. - Commit immediately with a Chinese commit message.
- Push immediately to GitHub repository
LSH160981/skills-codex-auths-validator. - Reply with commit id(s) after push.
Do not delay bundling changes for later when they affect behavior or operation.
Daily 01:00 GitHub learning workflow (Asia/Shanghai)
使命:本 skill 所有逻辑来自上游项目,必须追踪上游接口变化并及时同步。
当需要创建/更新学习巡检任务时:
- Schedule:
0 1 * * *(Asia/Shanghai)(凌晨 01:00,避开整点高峰) sessionTarget: isolated,payload.kind: agentTurn,agentId: "main"
只追这 6 个维度(其余不管)
| 维度 | 具体关注点 |
|---|---|
| 认证/校验 API | endpoint、请求头字段、响应结构、状态码语义 |
| Token 生命周期 | refresh_token 流程、expired 字段、id_token 结构、续期接口参数 |
| Account 体系 | account_id 字段名变更、chatgpt_account_id、多账户字段 |
| 新 provider/type | AuthFileType 枚举新增/废弃 |
| JSON schema | auth 文件新增/废弃字段 |
| 状态码语义 | 429/401/403 有没有新含义 |
执行流程(每次必须全部执行)
Step 1:拉取本地镜像更新
cd /root/.openclaw/workspace/tmp-cli-proxy && git fetch && git log HEAD..origin/main --oneline 2>/dev/null | head -20 || git log --oneline -5
cd /root/.openclaw/workspace/tmp-cpa && git fetch && git log HEAD..origin/main --oneline 2>/dev/null | head -20 || git log --oneline -5
# 有新 commit 则 git pull --rebase
Step 2:精准 grep
# 接口相关
grep -rn 'wham/usage\|backend-api/wham\|Chatgpt-Account-Id\|chatgpt_account_id\|chatgptAccountId' \
/root/.openclaw/workspace/tmp-cli-proxy /root/.openclaw/workspace/tmp-cpa \
--include='*.ts' --include='*.js' --include='*.vue' -l 2>/dev/null
# Token 续期
grep -rn 'refresh_token\|grant_type.*refresh\|oauth/token\|auth0.openai' \
/root/.openclaw/workspace/tmp-cli-proxy /root/.openclaw/workspace/tmp-cpa \
--include='*.ts' --include='*.js' -l 2>/dev/null
# Provider/type 枚举
grep -rn 'AuthFileType\|type.*codex\|type.*claude\|type.*gemini\|type.*qwen\|type.*kimi\|type.*vertex\|type.*iflow\|type.*antigravity' \
/root/.openclaw/workspace/tmp-cli-proxy /root/.openclaw/workspace/tmp-cpa \
--include='*.ts' --include='*.js' 2>/dev/null | head -40
# 状态码处理
grep -rn '401\|403\|429\|limit_reached\|used_percent\|rate_limit' \
/root/.openclaw/workspace/tmp-cli-proxy /root/.openclaw/workspace/tmp-cpa \
--include='*.ts' --include='*.js' 2>/dev/null | grep -v 'node_modules\|\.git' | head -40
Step 3:GitHub Commits 检查
用 web_fetch 或 browser 访问:
https://api.github.com/repos/router-for-me/Cli-Proxy-API-Management-Center/commits?per_page=10https://github.com/router-for-me/Cli-Proxy-API-Management-Center/commits/main
重点看近 7 天 commits,提取标题含 auth/token/codex/quota/provider/api 的变更。
Step 4:对比当前 skill 规则
读取本 SKILL.md 的 "Decision rules"、"Pre-flight expiry check"、"设计盲区" 三节,判断:
- 有无新接口参数 → 更新 Decision rules
- 有无新 provider type → 更新
lib/provider.mjs - 有无 JSON 字段变化 → 更新
lib/provider.mjs#schemaValid - 状态码语义有无调整 → 更新 Decision rules
- refresh_token 接口有无变化(endpoint/client_id/grant_type)→ 更新
lib/codex.mjs#tryRefreshToken
Step 5:按需同步(只有真实变化才更新)
1. 更新 SKILL.md(对应章节 + Learning rationale 段落)
2. 如影响脚本逻辑:更新 lib/provider.mjs 或 lib/codex.mjs
3. 更新根目录 README.md(版本演进部分)
4. git add ... && git commit -m '学习更新: <一句话说什么变了>'
5. git push skills-origin master
输出格式(必须严格按此)
巡检时间(上海):
检查了:
接口相关发现:
- wham/usage 端点:{无变化 / 有变化:xxx}
- 请求头:{无变化 / 有变化:xxx}
- Token 续期接口:{无变化 / 有变化:xxx}
- Provider/type 枚举:{无变化 / 新增:xxx}
- JSON schema:{无变化 / 有变化:xxx}
- 状态码语义:{无变化 / 有变化:xxx}
skill 更新:{未更新 / 已更新,commit: xxxx}
需要人工确认:{否 / 是,原因:xxx}
Mandatory auto-provision on new machine (3 cron jobs)
When this skill is installed/used on a new machine, ALWAYS ensure these three cron jobs exist automatically (create if missing, update if exists by name):
Job A: Hourly auth validation cleanup(系统 crontab)
结论:该任务必须用系统 crontab(OpenClaw cron 不可靠,详见
reports/lock-incident.md)。
- Name(crontab):
Codex auths hourly-run-and-notify - Schedule:
0 * * * *(Asia/Shanghai 在系统层面按服务器时区;如需严格上海时区请把服务器 TZ 设为 Asia/Shanghai) - Command:
0 * * * * bash /root/.openclaw/workspace/skills/codex-auths-validator/scripts/hourly-run-and-notify.sh >> /tmp/codex-auths-cron.log 2>&1
Behavior(双目录流转 + 稳定通知):
- 调用
scripts/hourly-reconcile.mjs扫描并迁移:/home/docker/CLIProxyAPI/auths/home/docker/CLIProxyAPI/auths_no_quota/home/docker/CLIProxyAPI/auths_invalid
- 输出通过 Telegram Bot API 发送给用户(超长自动改发文件)
- 失败重试 3 次(TG sendMessage/sendDocument)
- 本地日志:
/tmp/codex-auths-cron.log+/tmp/codex-auths/hourly-reconcile-*.log
- 调用
依赖:
- TG token/chatId 禁止硬编码在仓库文件;必须按用户单独配置:使用环境变量或本机 secrets 文件(默认
/root/.openclaw/secrets/codex-auths-validator.env),换用户必须单独询问并单独存放。 - 不依赖 OpenClaw agent session / auth-profiles.json
- TG token/chatId 禁止硬编码在仓库文件;必须按用户单独配置:使用环境变量或本机 secrets 文件(默认
OpenClaw cron 里的同名 job(如果存在)应禁用,避免重复跑。
Job B: Daily 01:00 GitHub learning check(凌晨接口巡检)
- Name:
Codex auths 每日01:00 GitHub学习巡检(上海) - Schedule:
0 1 * * *(Asia/Shanghai) - 追踪维度:认证API / Token续期接口 / Account字段 / provider枚举 / JSON schema / 状态码语义
- 详见上方 "Daily 01:00 GitHub learning workflow" 章节
Job C: Daily 00:00 skill self-sync (Asia/Shanghai)
- Name:
Codex auths 每日00:00 技能同步(上海) - Schedule:
0 0 * * *(Asia/Shanghai) - Behavior:
- pull latest updates from
https://github.com/LSH160981/skills-codex-auths-validator.git - sync
skills/codex-auths-validator/*to local workspace - if changed, apply immediately and send update summary to user
- pull latest updates from
Idempotent enforcement rule
Every time this skill runs in a new environment:
cron.list(includeDisabled=true)- find jobs by exact name
- create missing jobs via
cron.add - patch existing jobs via
cron.updateto keep schedule/payload consistent - report ensured job IDs to user
This guarantees all required cron jobs auto-appear after skill deployment on any machine.
Multi-user path policy (important)
This skill must work for any user environment, not only /home/docker/CLIProxyAPI/auths.
通用一条指令能力(新增)
只要用户告诉一个 JSON 存放目录,skill 就必须自动接管并完成工作:
- 把该目录直接作为
auths_dir。 - 自动派生并创建:
auths_no_quota_dir = <auths_dir>_no_quotaauths_invalid_dir = <auths_dir>_invalid
- 自动执行校验、分层、无效归档、结果汇总。
- 自动创建/修复定时任务(无需用户额外配置细节)。
When user provides a JSON folder path, the skill should:
- Accept the path directly as
auths_dir. - Derive
auths_no_quota_diras<auths_dir>_no_quotaunless user specifies another path. - Derive
auths_invalid_diras<auths_dir>_invalidunless user specifies another path. - Create missing target directories automatically.
- Run the same validation/migration/archive rules without asking extra setup questions.
If no path is provided, run discovery first; only ask user when discovery has low confidence.
设计盲区与边界条件(你可能没想到的地方)
以下问题均已在代码中修复或给出处理策略,记录在此供后续维护参考。
1. 跨文件系统移动(EXDEV)
问题:renameSync 在 /tmp → /home/docker 等不同挂载点之间会抛 EXDEV
影响:所有 safeMove 调用(hourly-reconcile / validate-auths / import-archive)均受影响
伪代码:
try { fs.renameSync(src, dst) }
catch (err) {
if (err.code === 'EXDEV') {
fs.copyFileSync(src, dst) // 先复制
fs.unlinkSync(src) // 再删源
} else throw err
}
修复:已在 lib/codex.mjs#safeMove 统一实现,三脚本共用
2. listJson 把目录/符号链接当文件
问题:readdirSync().filter(.endsWith('.json')) 不检查是否为普通文件
风险:名为 foo.json 的目录或 symlink 进入流程 → JSON.parse 失败 → 误入 invalid
恶意 symlink 指向 /etc/passwd → readFileSync 读取敏感文件
伪代码:
files = readdirSync(dir)
.filter(f => f.endsWith('.json'))
.filter(f => lstatSync(join(dir, f)).isFile()) // ← 加这一行
修复:已在 lib/codex.mjs#listJsonFiles 统一实现
补充修复:listJsonFiles 对目录不可读/不存在加 try/catch,直接返回 [],避免 cron 整体崩溃
3. 非 codex 文件在 hourly-reconcile 被误判为 invalid
问题:旧代码 if type !== 'codex' → to_invalid('non_codex')
影响:用户 auths 目录里若混有 claude/gemini JSON,每次巡检都被移入 invalid
修复:改为 detectProvider() + validateSchemaWithReason()
schema 有效 → keep(SCHEMA_VALID_PROVIDER,不动位置)
schema 无效 → to_invalid(才算真的无效)
现在与 validate-auths.mjs 的逻辑一致
4. dedupRemoved 没写入 report JSON(统计盲区)
问题:dedup 删除数只 console.log 出来,report JSON 里没有此字段
影响:hourly-run-and-notify.sh 解析 report 时 DEDUP 永远是 0,TG 摘要数字不准
修复:report JSON 新增 dedupRemoved 字段;shell 脚本解析时同步读取
5. validate-auths.mjs 的 import 语句位于函数定义之后
问题:ESM 静态 import 应在文件顶部;混在 const/function 之间虽然语义上 Node.js
能处理(import 被提升),但工具链(linter/bundler)可能报错或行为异常
修复:重写 validate-auths.mjs,所有 import 统一移到文件顶部
6. Telegram 4096 字符上限
问题:sendMessage 单条最多 4096 字节,超长静默截断(或 API 400 错误)
场景:无效原因统计很多时(如 6000 个文件结果),摘要超长
伪代码:
if len(text) > 4000:
write text to tmp file
sendDocument(tmp_file, caption="消息超长,以文件发送")
else:
sendMessage(text)
修复:已在 hourly-run-and-notify.sh#tg_send_text 实现自动分片
7. refreshedCount / dedupRemoved 在 TG 摘要中缺失
问题:shell 脚本只解析了 checkedTotal/finalQuota 等,没有读取 refreshedCount / dedupRemoved
影响:用户看不到"本次续期了几个 token",无法感知续期效果
修复:hourly-run-and-notify.sh 中新增 REFRESHED / DEDUP 字段解析,摘要中展示
8. 原子写(writeJsonAtomic)防止写半段崩溃
问题:续期成功后 writeFileSync 直接写入原文件;若进程中途被 kill,文件变为空或半写
影响:下次读取该文件 → JSON.parse 失败 → 误判 INVALID_JSON → 有效账号丢失
伪代码:
tmp = file + '.tmp-' + pid + '-' + Date.now()
writeFileSync(tmp, data) // 先写临时文件
renameSync(tmp, file) // 原子替换(同设备内 rename 是原子的)
修复:已在 lib/codex.mjs#writeJsonAtomic 实现,三脚本共用
9. 同一 token 被两个并发 worker 同时 refresh
问题:同一文件若同时出现在 DIR_QUOTA 和 DIR_NO_QUOTA(去重之前的窗口),
两个 worker 同时读取 → 同时 tryRefreshToken → 第一个写回文件
第二个写回时覆盖第一个的结果(可能是更旧的 token)
伪代码(最简防护):
processing = new Set()
worker():
if fullPath in processing: ops.push({action:'keep', reason:'concurrent_skip'})
else:
processing.add(fullPath)
... 正常处理 ...
processing.delete(fullPath)
当前状态:去重发生在 dedup 阶段(校验之前),理论上不会有同一文件路径被两个 worker 抢。
但 DIR_QUOTA 和 DIR_NO_QUOTA 里可能有相同 account_id 的不同文件,各自被处理 → 两个 refresh
请求 → 两次写回不同文件(实际上互不干扰,因为是两个不同的 fullPath)。
结论:当前实现安全,不需要加锁。
10. import-archive.mjs 子目录同名文件 basename 冲突
问题:archive 里 a/auth.json 和 b/auth.json 的 basename 都是 auth.json
旧实现用 jsonFiles.find(f => path.basename(f) === name) 回查 src
→ 会找错文件,造成“验证 A 文件但复制了 B 文件”的数据正确性风险
修复:results 里直接记录 fullPath,不再用 basename 反查;导入时直接用 fullPath
建议(可选增强):同时记录 originalRelPath(archive 内相对路径),report 里展示映射更易溯源
11. reports 目录排序不稳定(mtime 精度问题)
问题:同一秒内生成的多个 report 文件,sort by mtime 可能顺序不定
影响:pruneReportDir 删除“最老”文件时可能误删同批
修复:按文件名字典序排序(文件名含时间戳,字典序=时间序),不依赖 mtime
12. writeJsonAtomic 遗留 .tmp 垃圾文件
问题:writeJsonAtomic 在 kill -9 时可能遗留 .<base>.tmp-PID-TS 文件
影响:目录污染(不影响校验,但长期堆积)
修复:
1) lib/codex.mjs 新增 cleanTmpFiles(dir)
2) hourly-reconcile.mjs 启动时清理 DIR_QUOTA / DIR_NO_QUOTA
3) hourly-run-and-notify.sh 启动时 find -delete 清理
事故 / Bug / 事故复盘(统一归档)
历史事故与 bug 记录统一归档到:skills/codex-auths-validator/reports/lock-incident.md(仓库唯一真相)。
- 请不要在 SKILL.md 中继续追加长篇事故日志
- SKILL.md 只保留结论与指向该报告的链接