Repository navigation
Releases: wjf1/commandcode-proxy
Releases · wjf1/commandcode-proxy
Release list
v4.22.4: 直连 IPv4 优先与代理探针双模自愈
新增与修复
- 直连 IPv4 优先与代理连通性自愈(Auto-fallback),彻底解决换设备或未开代理时的连接稳定:
解决在未开启代理软件、或切换到无代理终端时,直连海外 Cloudflare 上游依然稳定高可用。- IPv4 优先解析策略:全局注入
dns.setDefaultResultOrder('ipv4first')(可通过COMMANDCODE_DNS_ORDER自定义),彻底避开国内运营商恶劣的 IPv6 握手黑洞与 10 秒超时(UND_ERR_CONNECT_TIMEOUT); - 代理探针与双模自愈:
proxy-agent启动时自动执行 TCP 快速探针;若本地代理未开启或切换至无代理环境,自动平滑回退为 IPv4 优先直连模式,绝不阻塞任何模型调用; - 测试覆盖:新增代理探测与 Auto-fallback 降级单元测试,全量 48 个套件、626 个测试用例 100% 通过。
- IPv4 优先解析策略:全局注入
v4.22.3: 出站 HTTP/HTTPS 代理集成与网络超时修复
新增与修复
- 出站网络代理(HTTP/HTTPS Proxy)集成,解决海外上游连接超时与 fetch failed:
直连海外上游https://api.commandcode.ai时,因国内网络对 Cloudflare CDN 节点的丢包、高延迟及 IPv6 首选握手失败,导致后台额度同步、健康探活及部分模型调用频繁抛出fetch failed(UND_ERR_CONNECT_TIMEOUT)。- 网络层治理:新增
src/utils/proxy-agent.ts,基于undici的ProxyAgent与setGlobalDispatcher,将全局出站 fetch 流量路由至本机科学上网客户端(默认http://127.0.0.1:7897); - 配置与安全隔离:
- 支持
config.json(upstream.proxy)与环境变量(HTTPS_PROXY/HTTP_PROXY/ALL_PROXY); - 强制加固
NO_PROXY,确保本地回环地址(localhost,127.0.0.1,::1)直连,保证本地测试与 Fastify 服务不受代理影响; - 代理配置仅作为传输层跳板,现有针对业务目标 URL 的 SSRF 与域名白名单安全校验保持严格生效;
- 支持
- 部署与启动链适配:在
start.cmd与.env中固化出站代理支持; - 测试与验证:新增
tests/proxy-agent.test.ts单元测试,全量 48 个套件、621 个测试用例 100% 通过。
- 网络层治理:新增
v4.22.2: 兼容 fake-ip DNS 解析,防止上游主机被误拦截 (BLOCKED_HOST)
修复
- fake-ip 环境下上游被误拦(BLOCKED_HOST):本机 clash 系代理开启 fake-ip 模式(TUN/DNS 劫持)时,
api.commandcode.ai会被解析为 198.18.x.x 假 IP,DNS rebinding guard 将其判为保留地址直接拒绝,
请求毫秒级失败(2026-09-30 23:40~23:47 批量复现)。修复分两层:- 部署侧:
start.cmd注入COMMANDCODE_UPSTREAM_ALLOWED_HOSTS=api.commandcode.ai,
将该域名加入 DNS 校验白名单(仅豁免此域,SSRF/rebinding 防护对其它域名不变); - 网关侧(本机 Clash Verge 配置,不入库):
dns.fake-ip-filter加入+.commandcode.ai,
令该域名走真实 IP 解析。
影响面:API 行为零变化;watchdog.ps1同步注入环境变量(被 .gitignore 忽略,需本机部署手动同步)。
已知环境遗留:本机项目根的历史~/.env(2026-09-04 凭据文件)使admin-key-mask一项隔离断言失败,与本次变更无关。
- 部署侧:
v4.22.1: 管理面 Origin Scheme 校验收紧与安全加固 (ORIGIN_SCHEME_CHECK)
安全
- Wave 4:管理面 Origin scheme 校验收紧(ORIGIN_SCHEME_CHECK)——原安全策略的最后一项。
管理面写操作此前只比对 Origin 的 host,scheme 不受限:同 host 的混合内容场景
(如 https 页面驱动 http 管理接口)可以通过校验。现在默认要求 Origin scheme 与
请求协议一致(dashboard 与 prompts 两处管理面 hook 均已接入)。
影响面:绑定 127.0.0.1 直连的本地用户(http→http)完全不受影响;反代 TLS 终止
(https 页面 → http 后端)的部署会收到 403,设ORIGIN_SCHEME_CHECK=off显式回退。
不传请求协议的旧调用形态保持旧语义(零破坏)。新增 4 项测试(含端到端 403)。
说明
- 本版收口后,原开发计划全部项目均处于「已落地」或「有记录的取消/条件触发」终态。
v4.22.0: 上游 Pipeline 编排化与仪表盘运营可视化
优化提升计划收尾:架构 D(upstream Pipeline)完成 + 仪表盘运营能力可视化。
零配置升级承诺:API 行为与错误语义零变化(导出面 14 符号逐一比对一致);
前端为纯增量两张只读卡片,默认渲染不变。
架构
- D. upstream Pipeline 编排化:sendToCC 325 行深嵌套 → 181 行编排层 +
pipeline/5 个阶段模块(timeouts 超时装配 / request 受控请求含 SSRF 重定向防护 /
response-error 分类与退避 / stream 流包装与首事件探测 / errors 共享基础件),
upstream.ts 730 → 393 行。阶段间显式参数传递、无共享可变闭包;SSRF 注释逐行随迁;
日志顺序与文案逐字保留。导出面 14 符号一致,调用方零改动。
明确保留在编排层:并发槽位管理、onRetry 换号回调、重试循环控制流(强行提取会引入风险)。 - 既有测试矩阵(挂钟上限/首事件探测/重试换号/SSRF 重定向/流错误)拆分前后零回归。
仪表盘
- 新增
GET /api/features只读端点:健康检查/Webhook/Prompt 版本/限流/模型访问/审计
六类能力的运行时开关状态与参数(WEBHOOK_URL 不回显,专测锁死)。 - 概览页新增「通道健康」与「运行开关」两张只读卡片:探活结果/延迟/连续失败/可用率、
各开关徽标与参数;随概览页既有 30s 轮询刷新,不新增定时器;全 esc() 转义。
明确不做(计划终态)
- E. 路由去重(chat.ts/messages.ts 60% 重复 → RouteMiddleware):按架构师建议
取消——风险最高、成本翻倍,且 4.21.0 的限流/审计插桩刚在两路由落地,大改会把
插桩与既有覆盖搅动,回归风险陡增;重复的维护代价由 607 项测试矩阵兜底可控。 - Prompt A/B、语义缓存、MCP Gateway、Docker/K8s、最低成本路由:与本地单 exe 定位
冲突,砍掉(4.20.0 分析结论维持)。
测试
- 全量 606/607 通过(+features 5 项);唯一失败为既有环境性用例(本机生产 .env)。
tsc --noEmit、eslint零错误。
v4.21.1: 仪表盘版本徽标显示真实版本号 (v4 → v4.21.1)
修复
- 仪表盘头部版本徽标显示真实版本号:此前硬编码为
v4,现从/api/status的
version字段驱动(fetchStatus既有轮询顺带更新,无新增请求);接口不可达时
保持占位v4不谎报。<title>同步去掉过时的v4字样。
v4.21.0: 仪表盘全新 UI、账户生命周期管理与凭据安全加固
优化提升计划第 0 期(界面)+ 第 1 期 + 第 2 期,6 条任务线并行开发后合并。
零配置升级承诺:仪表盘默认渲染与 4.20.0 完全一致;除审计日志(minimal,仅元数据)与
健康检查(纯旁路日志)默认开启外,其余新功能全部默认关闭,API 行为不变。
可视化界面(Phase 0–2,21 人日计划的全量前置三期)
- Phase 0(零感知):语义色板 CSS 变量收口(暗色默认视觉逐一对齐不变)、统一空状态与骨架屏、
窄屏宽表横向滚动降级。 - Phase 1(受益):概览页补「今日请求/成本」「活跃账号」卡片;账号卡片登录方式与 5h 额度徽标;
概览页快捷刷新。 - Phase 2(增强):用量表默认折叠的筛选栏 + 列头排序 + 行下钻详情;模型家族徽章;
日志 level/模型/错误码结构化高亮。全部动态内容走 esc() 转义,新增 20 项 spa 测试(65 项前端测试全绿)。
架构(Phase 1 + Phase 2 存储/adapter 部分)
- config.ts 859 → 547 行:OAuth/浏览器登录拆出
auth-browser.ts,SSRF 守卫拆出
security-guard.ts(均 re-export 保持调用方零改动);types 按域拆 5 文件(index.ts 聚合 re-export)。 - usage-store 后端接口化(
UsageStorageBackend,默认 jsonl,文件格式逐字节不变,为 SQLite 预留)。 - adapter.ts 964 行单类 → 组装式 5 模块(reasoning / request-translate / stream-encode /
anthropic-response / usage-extract),92 个 SSE 快照逐字节钉住行为零变化;
快照注入固定模型缓存保证跨环境可复现。 - 明确不做(本轮):upstream Pipeline 编排(D)与路由去重(E)——按架构师建议后置,
需配合绞杀者灰度单独一轮。
新功能(默认关闭 / 仅旁路)
- 通道健康检查(默认 5 分钟探活,仅日志告警,绝不拦截请求):
HEALTH_CHECK_INTERVAL_MS(0 关闭)。 - Webhook 告警(默认关):
WEBHOOK_URL+WEBHOOK_COST_USD/WEBHOOK_ERROR_RATE阈值,
每日每事件一次去重,fire-and-forget 不重试。 - Prompt 版本管理(默认关):
PROMPT_VERSIONS=on启用/api/prompts/*——保存自动快照、
按时间戳回滚(回滚前当前内容同样先快照,历史不丢);目录穿越白名单防护。 - 速率限制(默认关):
RATE_LIMIT_RPM/RATE_LIMIT_TPM,60s 滑动窗口,超限 429 +
Retry-After;错误码复用既有RATE_LIMIT(429),零追加公共契约。 - 模型访问控制(默认关):
MODEL_ALLOWLIST/MODEL_BLOCKLIST,拒绝 403。 - 审计日志(默认 on,minimal):只记 ts/route/model/tokens/status/duration 元数据,
绝不记录消息正文;AUDIT_LOG=off关闭。
修复
- 删除 adapter 拆分暴露的死函数
anthropicImageToWire(拆分前即无调用的 private 方法)。
测试
- 全量 601/602 通过(+188:spa 20、adapter 快照 92、健康检查 12、webhook 18、prompt 11、
限速 13、模型控制 13、审计 10 等新测试);唯一失败为既有环境性用例(本机存在生产 .env)。 tsc --noEmit、eslint零错误;6 分支合并零冲突(按文件域并行隔离)。
v4.20.0: DNS 重绑定防御、安全审计日志与模型访问控制
优化提升计划第 0 期(安全止血 Wave 2–3)。三个新开关全部安全默认:默认 on/conservative,
不改配置的用户升级后 API 行为不变(零配置升级承诺),每个开关都可显式回退。
安全
- 日志密钥脱敏(Wave 2):
LOG_REDACTION=on(默认)对进入环形缓冲 / proxy.log / 仪表盘
日志页的每条日志按密钥形态打码——Authorization: Bearer/Basic、api-key/x-api-key
键值(含 JSON 引号形态)、裸sk-令牌、query 中的token/key参数统一替换为
[REDACTED];off显式回退。脱敏在控制字符清洗之后执行,用�拆分关键字的
日志注入无法绕过。新增tests/log-redaction.test.ts(10 项)。 - SSRF 重定向阻断(Wave 3):修复真实绕过点——此前唯一的数据面 fetch 未设
redirect,Node 默认 follow,被攻击者控制的上游可用 302 把带凭据的请求引向
169.254.169.254等内网/元数据地址,完全绕过对初始 URL 的白名单校验(回归测试先行
证实:mock 上游 302 → 元数据地址,旧代码真被跟过去直到空闲超时)。现在所有上游请求
一律redirect: 'manual'(数据面 POST + 3 个 GET 出口),默认conservative不跟随;
UPSTREAM_REDIRECT=follow显式放行后逐跳复检:私网/回环/保留地址永不跟随且不受
allowlist 影响,同 host 跳转保留 POST 与请求体,跨 host 跳转剥离Authorization,
链条最多 5 跳。新增tests/upstream-redirect.test.ts(6 项,真实 mock 上游 + fetch mock)。 - DNS rebinding 防护(Wave 3):
DNS_REBINDING_GUARD=on(默认)在每个上游请求出口
做"请求前解析 + 校验"——URL 字面校验只能看 hostname,攻击者域名可先解析公网通过校验、
实际建连时再解析进内网;现在解析结果含私网/保留地址即拒绝(fail-closed),off显式
回退。IP 字面量 / localhost / allowlist 命中主机跳过解析校验(无 rebinding 可能或已
显式信任)。已知残余:lookup 与建连间存在 TOCTOU 窗口,彻底封闭需固定解析结果建连
(自定义 undici Agent),按计划做"请求前校验"档位。新增tests/dns-rebinding.test.ts(8 项)。 - 说明:计划 Wave 1(
safeEqual侧信道修复、usage-history 原子写)在 4.18.x–4.19.x
已先行落地,本版无对应改动。
兼容性
- 三个开关默认值即安全默认:
LOG_REDACTION=on只影响日志显示不影响 API;重定向默认
不跟随——依赖上游 302 跳转的自建镜像用户需显式设UPSTREAM_REDIRECT=follow;
DNS_REBINDING_GUARD=on对 allowlist 命中的自建网关域名不生效(运维显式信任即放行)。
测试
- 新增 24 项测试(10 + 6 + 8),全量 413 项通过(Windows 本机 412 + 1 项既有环境性失败:
admin-key-mask的 .env 隔离用例在本机存在生产.env时必然触发,CI 干净环境全绿);
tsc --noEmit、eslint无错误。
v4.19.1: 修复 exe 加载期崩溃、Node 18 支持结束与换用 @yao-pkg/pkg
修复
engines从>=18.17抬到>=20,Node 18 支持结束。4.19.0 新增的smoke-node18作业首跑就红了:TypeError: diagnostics.tracingChannel is not a function——fastify 5 用到的diagnostics_channel.tracingChannel是 Node 20 才有的 API。
这不是新引入的缺陷,而是"声明的支持面下限从来没人执行过":主矩阵跑在 20/22 上,engines只是声明、npm 默认不拦,所以自 dependabot 把 fastify 从 4.29.1 升到 5.12.3 起,Node 18 就已经起不来了,直到这个作业出现才被发现。该作业本身是对的,不要为了让它变绿而放宽它。- 打包后的 exe 此前在加载期就崩,根本起不来(
src/utils/version.ts)。CJS 打包下import.meta.url是空字符串,new URL('../../package.json', '')抛ERR_INVALID_URL——而抛出点在"为坏候选兜底"的那个try之外(它在数组字面量求值时发生,不是读取文件时),于是模块加载失败、进程在起监听之前就死了。改为单独包住这一句、让它降级到下面的兜底路径。
实测:修复前 exe 退出码 1、无监听;修复后 exe 正常启动(banner 报 4.19.1、/health200、netstat确认端口归属commandcode-proxy-v4.exe)。
构建
- 打包器
pkg→@yao-pkg/pkg(6.x),目标node18-win-x64→node22-win-x64。原来那条链是被 vercel/pkg 的天花板钉住的:pkg 已停止维护、最高只支持 node18,engines >=18.17与--target node18-win-x64都是它的产物,不是独立的产品决策。换到维护中的 fork 后 exe 内嵌 node22,"零依赖 exe"这项能力不变(静态资源另有问题,见文末「已知问题」)。
注意:打包需要 Node ≥22 来运行@yao-pkg/pkg自身(其预编译二进制由@yao-pkg/pkg-fetch提供,覆盖 node22/24/26)。
CI
smoke-node18→smoke-node20,改为验证engines的下限本身。选下限而不是 22:能起在 20 上就蕴含能起在 22 上,一个作业同时覆盖"声明的下限是真的"和"pkg 内嵌的 node22 跑得起来"。
安全
npm audit清零(此前 2 高危 + 1 中危)。曾评估"把 fastify 回退到 4.x 以保住 Node 18"这条路线:技术上可行(^4.29.1下 typecheck/lint/build 0 错误、389 项全绿、零代码改动),但 4.29.1 是 4.x 终点、不会再有安全补丁,npm audit会常驻 fastify 与 find-my-way 两个 high(唯一修复是升到fastify@5.12.5这个 semver-major),故不采用——不值得为一个已 EOL 的运行时长期背一组修不掉的公告。顺带把 vercel/pkg 那条no fix的 moderate 也一并去掉了。
测试
- 全量 389 项通过,
tsc --noEmit、eslint .无错误。 - 交付运行时冒烟在真实 Node 20.20.2 上实测通过(跑的是
node dist/index.js,即 CI 里那个作业的形态):/health返回{"status":"ok","version":"4.19.1"},/返回 99,986 字节、含<title>的仪表盘 HTML。
已知问题
- 打包后的 exe 仍不能提供仪表盘:exe 起得来、
/health与/v1/*正常,但GET /返回 500Dashboard assets missing: public/index.html not found.。
根因是静态资源的落点与代码读取的路径不一致,且本次没能定位到正确落点,故不写成"已修复"。实测数据:exe 内__dirname=C:\snapshot\dist,而C:\snapshot\public\index.html不存在(构建日志里连Cannot stat警告都没有——它既不报缺,也没落在预期位置)。
临时办法:把仓库的public/目录放在 exe 同级再运行(那是代码的第一候选路径)。定位 pkg 的资源落点需要单独一轮,届时再决定是修正 assets 路径还是改成构建期内嵌。这条与本次改动无关,是既存缺陷:修复前 exe 连启动都到不了,所以从未暴露过。
v4.19.0: 架构审查批次 B — 修复工具调用吞丢与凭据掩码收口
架构审查(批次 B)修复。取次版本号而非修订号:含四处行为变更(下方标 saveConfigFile 的返回值),不是单纯补丁。
修复
- 流式
finish_reason不再压过已经流出的工具调用(adapter.ts)。上游明确回stop时,此前会把finish_reason定成stop并盖掉tool-calls,而客户端按它判断回合是否结束——整批tool_calls被丢弃、agent 静默卡住。现在只有在没有任何工具调用流出时才回stop。 - Anthropic 路由的
tool_use分片合并(messages.ts)。此前每个 tool-call-delta 各发一轮 start/delta/stop,N 个分片变成 N 个同 id 的块;现在合并为一块。 - OpenAI 非流式工具调用按 id 合并(
chat.ts)。此前用set覆盖,同一调用的后续分片直接丢失;固定回落的call_1改为唯一 id。 - 流交还后的空闲超时按真实分类落库(
upstream.ts)。此前一律记PROVIDER_PROTOCOL_ERROR,README 承诺的STREAM_IDLE_TIMEOUT一直没有兑现。 /api/auth/manual-login不再明文回传apiKey(dashboard.ts)。同文件的 accounts / browser-login / aggregate 三个出口早已掩码,这是唯一漏网的一个;四处重复的掩码表达式收口为toSafeAccount()/maskApiKey(),新增端点不会再漏。- 删掉最后一个账号时同步清除
.env凭据与进程内兜底 Key(config.ts)。getActiveApiKey()在账号表为空时会回落到环境变量,此前被删除的账号仍会继续烧额度。.env也不再固化apiBase/ccVersion的公网默认值。 - 配置落盘失败不再假成功(
config.ts+dashboard.ts)。⚠️ saveConfigFile改为返回是否落盘,setActiveAccount/setRotationMode/logoutAccount传播失败,loginNewAccount落盘失败时抛错,三个仪表盘端点改回 500——配置目录不可写从"假成功"变成显式失败,前端已同步适配。 - 流式路径上游不给 usage 时用编码器估算兜底
outputTokens(chat.ts)。实测 205 条真实请求中 0 条带输出量,成本此前被系统性低估。 input_tokens估算不再把 config 元数据与图片 base64 当提示词(messages.ts+upstream.ts);count_tokens计入数组形态tool_result、图片与toolsschema——此前 1200 字符的工具输出只报 1。project归属不再恒为空(request-context.ts)。systemTextOf此前不认 OpenAI 的messages[role=system|developer],导致/v1/chat/completions全部记录project: null。- 崩溃预算退出前 flush 排队的用量写入(
index.ts)。此前直接process.exit,把已经排队的用量丢掉。 - 面板的删除/开关失败不再静默(
public/index.html)。18 处裸fetch收敛为一个apiJson()封装,此前只有 2 处检查res.ok,后端改返回 500 时 UI 会TypeError,表现为"点了删除没反应";fetchStatus不再整体吞异常,代理挂掉时显式显示"无法连接代理"而非Port :undefined。 - 额度卡片渲染前统一重置(
public/index.html),此前切换账号会留着上一个账号的数字;日志页只在原本贴底时跟随滚动,不再每 5s 强制跳底。
新增
DETERMINISTIC_REQUEST_SHAPE否决表(upstream.ts)。请求形态类错误不再重试——实测此前会打满 3 次、多花约 1.5s 退避;瞬时故障的重试保护保持不变,未做策略反转。/api/usage/history支持?limit=,导出 CSV 取全量并在截断时如实说明(此前恒 200 条却提示"已导出 N 条")。auto-quota轮换与额度采样的装配条件移入 tick 并读新鲜配置(index.ts),面板上开启后无需重启代理即生效。⚠️ 引擎开关只认严格布尔(dashboard.ts),"false"/0此前会被当成"继续运行"。- 无障碍基线(
public/index.html):48 个图标补aria-hidden、24 个th补scope、tablist/tab/tabpanel配aria-selected与方向键、三个弹窗补role="dialog"/aria-modal/ 焦点圈定与归还、live region、label for、纯图标按钮补aria-label。
性能
dayKey按timeZone缓存Intl.DateTimeFormat(usage-store.ts):3 万条带时区记录 1776ms → 140ms。此前逐条构造 formatter,会阻塞事件循环约 2 秒。logger每行时间戳不再用toLocaleTimeString,同属逐次构造 formatter 的写法。- 原子写的临时文件名加
pid(config.ts+usage-store.ts),避免多实例互踩。
构建
- CI 主矩阵 Node 20 → 22:
vitest@5要求 ≥22.12,此前是把测试 Runner 跑在它自己声明的支持面之下。 - 新增
smoke-node18作业:只装生产依赖 + 下载 dist 产物,用 Node 18.17 起真实进程断言/health与仪表盘 HTML,覆盖 pkg 交付的运行时。 - 覆盖率步骤加
--coverage.thresholds.statements=65。已验证该开关真会拦:阈值 99 时 exit=1,65 时 exit=0。 eslint.config.js把仓库根三个.mjs工具纳入 lint(此前被根级*.mjs忽略,与配置自己写明的原则矛盾),立刻抓出bench-models.mjs一个"取了不用"的变量;该工具的起始额度用量改为真的打印出来(它会实际烧额度)。
测试
- 新增 10 个回归防线文件:
tool-call-fragments、config-account-lifecycle、usage-surfacing、dashboard-browser-login-mask、token-estimation、tool-image-and-params、spa-api-json、spa-a11y、spa-search-functions、spa-html-integrity,并扩充attribution、upstream-probe。 - 全量 389 项通过(原 316 + 73),
tsc --noEmit、eslint .无错误。所有"修复"类改动都先在未改动的 HEAD 上跑出红、改后变绿才提交结论。
已知未做
cache_control透传、tool_result内图片的 wire 形状、stop/response_format下发:需要真实 Key 且套餐覆盖 Anthropic 系模型才能验证效果。上游对未知字段是静默忽略(已实测),但"不报错"不等于"生效"——不要当已支持特性宣传。- 流式背压(
write()返回值被忽略、不 pause 上游)与getUsageStats整体记忆化:建议配压测单独一轮。 - 配置落在
process.cwd()(换目录启动会像"账号全丢"):需迁移策略。