# 复制为 .env 后填入真实值:cp .env.example .env # .env 已被 .gitignore 排除,不会进仓库。 # 也可以直接用环境变量传入 —— 环境变量优先级高于 .env。 # ════════════════════════════════════════════════════════════════ # 必填两项:邮箱 Worker + 出口 IP 映射 # ════════════════════════════════════════════════════════════════ # ── CF Worker 临时邮箱 ─────────────────────────────────────────── # Worker 的 Admin Token。缺失时 run.py 会在启动阶段直接报错。 FO_WORKER_ADMIN_TOKEN= # Worker 基址与收信域名。这两项**不再有写死的默认值**(仓库可能公开), # 缺失时在入口报错,而不是带着空 base 去发一堆注定失败的请求。 FO_WORKER_BASE=https://..workers.dev FO_WORKER_DOMAIN= # ── 槽位代理池(**本项目必需,不是可选优化**)──────────────────── # 🔴 为什么必需:站点按 **IP** 限每日注册数,实测单 IP 上限 **5 个/天**, # 触顶后返回「该 IP 今日注册数量已达上限,请明日再试或联系管理员」。 # 同一时刻换出口 IP 立刻恢复(实测 4/4 成功)。 # ⇒ "有几个不同出口 IP"直接决定一天能注册几个。 # # 起池三步(都在本机跑,不需要额外服务): # # 1) 写槽位清单(一行一个;也接受逗号分隔): # FO_PROXY_SLOTS_FILE=.workbuddy-ai/proxypool/slots.txt # 文件内容形如: # http://127.0.0.1:7901 # http://127.0.0.1:7902 # # 2) 起独立 mihomo 实例(**不要动 Clash Verge** —— 改它的配置会影响你上网): # "<你的 mihomo 可执行文件>" -d .workbuddy-ai/proxypool \ # -f .workbuddy-ai/proxypool/config.yaml # 内核路径从这里读(别写进源码 —— 绝对路径会暴露本机目录结构): # FO_MIHOMO_EXE=<你的 mihomo 可执行文件绝对路径> # ⚠ 内核的 `listeners` 段里每个 listener 指定一个 `proxy`(策略组), # 这样"一个本地端口 = 一个固定出口 IP"。 # # 3) 量出真实出口 IP 并写进下面这张表(**必做**): # python tools/probes/probe_slots.py # FO_PROXY_SLOTS_FILE=.workbuddy-ai/proxypool/slots.txt # 槽位清单的**内联**写法(一行/逗号分隔),与 `FO_PROXY_SLOTS_FILE` 二选一。 # 🔴 优先级:`FO_PROXY_SLOTS_FILE` 非空时**只用文件**,这个被忽略 —— # 两个都配不会合并,而是静默取其一。批量跑之前用 `python run.py probe` # 确认池子大小(它会打印"6 槽 / 4 个不同出口"这类摘要)。 # 用途:临时试一个新出口、不想改文件时。 # 例:FO_PROXY_SLOTS=http://127.0.0.1:7901,http://127.0.0.1:7902 # FO_PROXY_SLOTS= # 🔀 记账维度:`egress`(默认)| `session`。 # · egress —— 每个槽位一个**固定出口 IP**(本地 mihomo),靠下面的 # FO_SLOT_EGRESS_IPS 映射。 # · session —— **动态住宅代理**:多个会话共用 host:port,靠用户名/密码里的 # `sid-XXXX` 区分,每个会话是独立出口;此时**不需要** FO_SLOT_EGRESS_IPS, # 记账维度退化成 host:port:sid-XXXX。 # 🔴 住宅代理**必须**用 session:10 个会话都是 `:2000`,按端口记会把它们 # 合成一个桶 —— 10 路并发被本地闸门串成 1 路,且一个会话触顶会被误判成 # 全部触顶(池子把 10 个一起冷却)。 FO_PROXY_SCOPE_MODE=egress # scheme-less 代理串(`host:port:user:pass`)默认用哪个协议: # http(默认)| https | socks5 | socks5h # ⚠ `socks5`/`socks5h` 需要 PySocks:`pip install "requests[socks]"`, # 否则 requests 会报 `InvalidSchema: Missing dependencies for SOCKS support`。 FO_PROXY_SCHEME=http # 🔴 槽位端口 → 出口 IP 的映射(**仅 `egress` 模式**;`session` 模式可留空)。 # 配额必须按出口 IP 记,绝不能按槽位号 —— `slots.txt` 的顺序随时会变, # 用位置号当 scope 时清单一改动既有记录就整体错配到别的 IP 头上, # 而且**不报错**。 # ⚠ 这张表**不进仓库**(出口 IP 属于基础设施标识,公开可被搜索)。 # ⚠ 换订阅 / 换节点 / 换机房之后这张表就过期了,必须重测。 FO_SLOT_EGRESS_IPS= # 一个槽位被判"这个出口今日配额已满"后冷却多久才重新启用(秒)。 # 默认 120。⚠ 这是**基础**冷却:同一槽位反复被判死时按 2 的幂退避。 FO_PROXY_COOLDOWN=120 # 退避封顶(秒),默认 21600(6 小时)。 # 🔴 为什么需要退避:基础冷却 120s,而配额窗口是**一天** —— 差 720 倍。 # 不递增的话,一个已满的出口每 2 分钟被重新租出去打一枪,一天约 720 次, # 每次都在加深风控。退避把"一天 720 次"压到"一天 6 次左右"。 FO_PROXY_COOLDOWN_MAX=21600 # 全池都在冷却时,一个任务最多等多久(秒),超时按失败记账。 # 🔴 别设 0(全冷却瞬间变一片假失败),也别设无穷(批量会永远挂着看不出卡死)。 FO_PROXY_SLOT_TIMEOUT=240 # 起飞前是否做槽位端口连通检查(1 开 / 0 关,默认开)。 # 🔴 默认开的理由:槽位实例是**独立前台进程**,很容易"配置还留着、进程已经没了"。 # 那种状态下每条记录都会以**代理连接错误**收场,而在这个项目里 # "注册全失败"最容易被误读成"IP 又被封了" —— 结论完全错。 FO_PROXY_PREFLIGHT=1 # ── 单条全局代理(不用池子时的退路)────────────────────────────── # FO_PROXY=host:port:user:pass # FO_PROXY=http://user:pass@host:port # FO_PROXY=socks5://user:pass@host:port (socks5 需先装 PySocks) # 只作用于**被封的那一侧**(站点 API)。邮箱 Worker 默认直连 —— # 收信轮询是整链瓶颈,绕道代理只会更慢,而 Cloudflare 不关心我们的出口 IP。 # 确实要让邮箱也走代理时:FO_PROXY_MAIL=1 # FO_PROXY= # FO_PROXY_MAIL= # ════════════════════════════════════════════════════════════════ # 下面这些有实测默认值,通常不用改 # ════════════════════════════════════════════════════════════════ # ── 站点 ───────────────────────────────────────────────────────── # FO_SITE_BASE=https://opc.fiime.cn # ── 单 IP 每日注册上限(**本地保护用**)────────────────────────── # 实测默认 5(2026-09-26)。这是**本地保守计数**,不是权威计量 —— # 换机器 / 删 state 文件都会重置。 # 🔴 撞墙的代价很高(那个出口**当天**就废了),所以宁可少跑几个。 # 被误拦时:调大这个值,或加 --ignore-quota。 FO_IP_QUOTA_MAX=5 FO_IP_QUOTA_WINDOW_H=24 # 计数文件路径覆盖(默认 .workbuddy-ai/state/opc_quota.jsonl) # FO_QUOTA_STATE= # ── 速率闸门 ───────────────────────────────────────────────────── # 两次注册之间的最小间隔(秒)。站点要求 **60 秒**(实测:连打 5 次, # 第 2~5 次立刻返回「注册过于频繁,请一分钟后再试」),这里取 62 留余量。 # ⚠ 这是**每个出口**各自的间隔,不是全局 —— 池化模式下 4 个出口可以 # 各自 62s 一次。 # 🔴 别调小:撞到 429 后虽然会退避重试,但会白等一个 62s 窗口。 FO_REG_MIN_INTERVAL=62 # 撞到速率限流后的退避重试次数。 FO_REG_RATE_RETRY=2 # 速率限流后的退避秒数(每次重试都等这么久,不是指数增长 —— 服务端窗口就是 60s)。 FO_REG_RATE_BACKOFF=65 # ── HTTP ───────────────────────────────────────────────────────── # 单个请求的超时(秒)。注册/激活/建 key 实测都在 3s 内; # 唯一慢的是收信等待,而那是**轮询**(FO_MAIL_POLL_TIMEOUT),不走这个超时。 FO_REQUEST_TIMEOUT=30 # 请求的 User-Agent。 # ⚠ 默认值必须与**当前主流 Chrome 大版本**接近 —— 站点是 SPA,UA 太旧时 # 前端可能降级渲染,而后端接口照常,症状会很难查(页面能开、接口报错)。 # 🔴 换 UA **不能**绕过 IP 日配额(已实测)—— 别把它当反封手段。 FO_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36 # ── 收信 ───────────────────────────────────────────────────────── # 邮箱源。**当前只认 `worker`**(CF Worker 实现,见 src/mailbox.py 的工厂)。 # ⚠ 加新源要同时改 `config.MAILBOX_KINDS` 与 `make_source()` —— 两处必须一起改, # 否则"校验认得的取值"和"工厂认得的取值"会漂移。 # ⚠ 刻意**不做别名**(不认 `cf` / `temp`):别名会让 `=cf` 看起来像换了个源, # 其实只是换了个写法。 FO_MAILBOX_KIND=worker # 邮件实测在注册后数秒内到达;轮询间隔 2s 足够。 FO_MAIL_POLL_INTERVAL=2 FO_MAIL_POLL_TIMEOUT=120 # 激活邮件的发件人域名片段。实测发件人是 `rockssr@163.com`。 # ⚠ 这是**排序偏好,不是过滤器** —— 匹配不到会退回"任意发件人", # 避免发信域名换了之后整链空等 120s。 FO_MAIL_SENDER_HINT=rockssr # ── 账号生成 ───────────────────────────────────────────────────── # 用户名前缀(默认 lz + 6 位数字)。站点规则约 `^[a-zA-Z0-9_]{3,20}$`。 # FO_USERNAME_PREFIX=lz # 密码长度(默认 12)。🔴 站点要求**必须含字母**(纯数字会被 400 拒)。 FO_PASSWORD_LEN=12 # 账号类型:personal | enterprise | community | alliance FO_ACCOUNT_TYPE=personal # ── 注册邀请码(推荐关系)──────────────────────────────────────── # 留空 = 不带码注册(账号不挂在任何邀请人名下)。填上 = **统一注入**: # 之后每一批注册都带上这个码,不需要每次记着传命令行参数。 # 形态:8 位大写字母数字(实测 `GET /api/invite` 返回形如 `R5AZGYNU`)。 # 🔴🔴 服务端**不校验码是否存在**(实测:乱填 8 位 `ZZZZZZZZ` → HTTP 200、 # 账号照建、零报错)。所以码拼错了**不会**以注册失败的形式暴露 —— # 成功率、退出码、错误分类全是绿的。 # 唯一判据是响应里的 `user.invitedAt`(0 = 推荐关系未成立), # 本工具把它落进台账(`invite_code` / `invited_at`)并在报告末尾的 # 「邀请码(推荐关系)」一节单独报出来。 # ⚠ 带码时**注册阶段返回 `registrationTokenReward: 0`**,奖励延后到激活 # 才发(激活返回 100000000)。"注册时 reward=0" 是正常现象,不是错误信号。 # ⚠ 不建议填自己的码给自己注册 —— 大概率被判自邀,`invitedAt` 仍为 0。 # 真实值只写进 `.env`(已在 .gitignore 中),别写进这个模板。 FO_INVITE_CODE= # ── 交付产物(报告文案用)──────────────────────────────────────── # 报告里"怎么调"给的示例模型。🔴 必须是 **text 类** —— 服务端 `models` 列表 # 顺序是 opc-txt-v1 / opc-image-v1 / opc-video-v1 / ...,把 image/video 塞进 # chat/completions 的示例里,读者照着抄就报错。 # ⚠ 权威值以服务端 `service.models` 为准(`ServiceInfo.default_model()` 会优先 # 在服务端列表里找这个 id,找不到就取第一个 text 模型)。 FO_DEFAULT_MODEL=opc-txt-v1 # 每账号最多能建几把 key。⚠ **只是报告文案的兜底值**,权威值是服务端上报的 # `maxApiKeysPerUser`(实测 5)。站点改了这里不会自动跟。 FO_MAX_KEYS_PER_USER=5