Skip to content

Repository files navigation

CompactGate

CompactGate 是一个给 Codex CLI 和 Claude Code 用的本地路由与协议适配代理。

它的作用很简单:

  • 普通请求继续发到你的主上游
  • 只有 compact 请求单独分流
  • 必要时自动改写 compact 模型名
  • Codex Responses 与 Claude Messages 可以接入不同格式的兼容上游
  • 你还能在本地页面里看配置、健康状态和最近日志

如果你现在还不清楚 “compact 请求” 是什么,也没关系。你只要记住:

CompactGate 让 Codex 在“正常对话”和“压缩上下文”这两类请求上,可以走不同的上游。

这个项目解决什么问题

很多时候你会遇到下面几种情况:

  1. 你的主上游能跑正常对话,但不支持 compact 模型。
  2. 你想把 compact 请求单独走另一家兼容 OpenAI API 的服务。
  3. 你想保留原来的 Codex 使用方式,只在中间加一层本地代理。
  4. 你想在本地看最近请求都走到了哪条路由,并保留完整请求 / 响应正文方便排查。
  5. 你的客户端和上游分别使用 Responses、Messages 或 Chat Completions,需要在代理层转换。

CompactGate 就是为这个场景做的。

30 秒理解工作方式

接入后,链路会变成这样:

Codex -> CompactGate -> 你的上游服务

CompactGate 会按规则转发:

普通 /v1/* 请求                                      -> primary 主上游
/v1/responses/compact                               -> compact 策略
/v1/responses + request_kind: "compaction"          -> compact 策略
/v1/responses + input[].type: "compaction_trigger"  -> primary 上游策略(逻辑记录为 remote_v2)

新版 Codex 的上下文压缩有三种线上的请求形态:专用 /responses/compact 的 Remote V1、带 compaction_trigger 且仍走普通 /responses 的 Remote V2,以及由 x-codex-turn-metadata 标记为 request_kind: "compaction" 的本地摘要压缩。CompactGate 会识别三种形态;Remote V2 复用 primary 模型、凭据和协议,不要求上游提供 compact 模型。Responses 上游原生转发,Anthropic Messages 转成原生 compaction,Chat 上游转成普通摘要请求并返回 CompactGate 签名状态。没有这些精确信号的普通请求仍走 primary。

Remote V1 与 Remote V2 不是同一路径换了名字:

项目 Remote V1 Remote V2
请求入口 /v1/responses/compact /v1/responses
真实触发信号 专用 compact 路径 input[].type: "compaction_trigger"responses_compaction_v2 元数据
实际上游 split 时走独立 compact.base_url 始终复用 Primary 上游与协议
模型策略 可改写为 compact 模型 保留 Primary 模型和 reasoning 策略
状态处理 支持响应归一化、桥接和短期去重 Responses 保留 provider state;Messages/Chat 使用签名 cg1_ 可移植摘要
日志 route=compactcompaction_mode=remote_v1 route=compactcompaction_mode=remote_v2

OpenAI Codex 官方 0.140.0 发布说明把 Remote Compaction V2 改为默认启用,因此 CompactGate 把 0.140.0 记作“V2 默认起点”,而不是“V1 被删除”的硬切换点。账号灰度、配置、桌面内置 CLI 和二开版本仍可能改变实际行为,所以 Studio 按“实际请求 > 历史观测 > 本机版本基线”的顺序展示协议;所有兼容路径始终保留。

服务启动时会执行一次有超时保护的 codex --version,之后每 6 小时刷新本机版本。真实请求里的 User-Agent 更权威,例如 codex-tui/0.144.1-cometix 会拆成原始版本 0.144.1-cometix、官方数字基线 0.144.1 和二开变体 cometix;如果它实际发送 V1,Studio 仍显示 V1,不会因为数字基线较新而强制标成 V2。

你只需要把 Codex 的 base_url 改成 CompactGate,本地代理就会替你处理剩下的事情。

最短上手

1. 安装依赖

npm install

2. 复制配置文件

cp compactgate.example.json compactgate.json

3. 修改 compactgate.json

先至少改这几个值:

  • primary.base_url:你的主上游地址
  • compact.base_url:你的 compact 上游地址
  • primary.api_key_env:主上游密钥环境变量名
  • compact.api_key_env:compact 上游密钥环境变量名

如果你暂时只想走一套上游,也可以把:

"upstream_mode": "primary"

这样 compact 请求也会走主上游。

4. 设置环境变量

export PRIMARY_API_KEY="你的主上游密钥"
export COMPACT_API_KEY="你的 compact 上游密钥"

如果你不想用环境变量,也可以先启动服务,再去 Studio 页面里直接保存 API Key。

5. 启动

npm run build
npm start

启动后打开:

http://127.0.0.1:7865/

这里就是 CompactGate Studio。

Codex 怎么接入

构建后可以直接用一次性启动器,不修改 ~/.codex/config.toml

node dist/server/cli.js agent codex -- --model gpt-5.5
node dist/server/cli.js agent codex --profile PROFILE_ID -- --model gpt-5.5

通过 npm link 或包管理器安装 bin 后,可将 node dist/server/cli.js 换成 compactgate--url 可覆盖默认的 http://127.0.0.1:7865-- 后的参数 原样传给 Codex。

把 Codex 的 OpenAI 兼容 base_url 指向 CompactGate:

model_provider = "compactgate"
model = "gpt-5.5"

[model_providers.compactgate]
name = "OpenAI"
wire_api = "responses"
requires_openai_auth = true
base_url = "http://127.0.0.1:7865/v1"

这里有一个非常重要的点:

name = "OpenAI"
wire_api = "responses"

这两项不要随便改,尤其是 name 必须保持 "OpenAI",否则 Codex 可能不会启用预期的 Responses 和远程压缩能力。不同 Codex 版本不一定都调用 /v1/responses/compact,也可能使用上面另外两种压缩请求形态。

Claude Code 怎么接入

同一个启动器通过进程内 --settings 指向 CompactGate,不写 ~/.claude/settings.json

node dist/server/cli.js agent claude
node dist/server/cli.js agent claude --profile PROFILE_ID -- --model sonnet

启动器把 ANTHROPIC_BASE_URL 设为 http://127.0.0.1:7865/anthropic;指定 profile 时,再通过 ANTHROPIC_CUSTOM_HEADERS 注入请求级 x-compactgate-profile--url-- 的语义和 Codex 启动器一致。

配置文件怎么理解

下面是一个最常见的配置例子:

{
  "listen": "127.0.0.1:7865",
  "primary": {
    "base_url": "https://primary.example/v1",
    "api_key_env": "PRIMARY_API_KEY",
    "upstream_protocol": "openai_responses",
    "model_override": "",
    "reasoning_effort": "",
    "state_domain_id": ""
  },
  "compact": {
    "base_url": "https://compact.example/v1",
    "api_key_env": "COMPACT_API_KEY",
    "upstream_protocol": "openai_responses",
    "upstream_mode": "split",
    "model_mode": "linked",
    "model_template": "{model}-openai-compact",
    "model_override": ""
  },
  "timeouts": {
    "primary_ms": 120000,
    "compact_ms": 900000
  },
  "logging": {
    "redact_body": true,
    "persist_body": false,
    "keep_recent": 200,
    "capture_dir": null,
    "capture_body_max_bytes": 1048576,
    "capture_dir_max_bytes": 21474836480,
    "max_database_bytes": 1073741824
  },
  "primary_failover": {
    "auto_schedule": true,
    "state_portability": "recover_on_error"
  }
}

Codex 的 primary.base_urlcompact.base_url 表示完整上游 API 根,例如 https://host/v1https://host/api/paas/v4。CompactGate 会精确移除客户端请求开头的 /v1,再把剩余端点拼到该 API 根,因此不会因拼接额外生成 /v1/v1/responses,也不会破坏 /v4 这类供应商版本路径。

Claude 的 claude.primary.base_url 表示上游主机或供应商前缀,例如 https://api.anthropic.comhttps://host/anthropic/v1/messages 会追加到该前缀;如果 base URL 已以完整 /v1 段结尾,则拼接边界只保留一个 /v1。Claude Messages POST 返回 404 时,CompactGate 不会猜测并重试其他路径。

*.upstream_protocol

主路由与压缩路由都保存明确的上游协议。可选值为:

  • openai_responses:OpenAI Responses API
  • anthropic_messages:Anthropic Messages API
  • openai_chat:OpenAI Chat Completions API

旧配置不需要手工迁移:Codex 的 primary / compact 默认保持 openai_responses,Claude 的 claude.primary / claude.compact 默认保持 anthropic_messages。同协议请求继续直通,只有协议不同时才转换请求和响应。

客户端入口 Responses 上游 Messages 上游 Chat 上游
Codex /v1/responses 直通 转换 转换
Codex /v1/responses/compact 直通 原生或可移植压缩转换 明确拒绝
Claude /anthropic/v1/messages 转换 直通 转换
Claude /anthropic/v1/messages/count_tokens 转换为 /v1/responses/input_tokens 直通 返回 501

协议转换覆盖文本、图片、自定义 function tools、tool result、JSON/SSE、usage 和错误响应。Responses 与 Messages 之间还会保留可移植的 reasoning/compaction 状态;无法安全移植的 provider-private 状态会明确失败。Chat 路径只承诺无状态的 文本、图片和 function tool 交互;reasoning、compaction、structured output、 provider tools 和多 choice 不会被静默丢弃,而是在请求发送前或流解析时返回明确错误。

Claude Code 的手工摘要提示仍按现有规则走 claude.primary,CompactGate 不根据 提示词猜测压缩流量;claude.compact 继续作为独立配置、凭据和 profile 元数据保存。

logging.keep_recent 控制 Studio 首屏和 /api/logs/recent 默认返回多少条日志,范围是 1 到 2000,不是 SQLite 日志保留上限。SQLite 请求日志默认最多占用 1 GiB;超过后先清空历史正文并保留元数据,仍超限时才按时间顺序删除最早的元数据行。

推荐使用分离存储:保持 logging.persist_body = false,再设置 logging.capture_dir。每个代理请求会写入一份独立 JSON,单段正文默认最多 1 MiB,受管抓包文件合计默认最多 20 GiB。目录超限时只删除最旧抓包,SQLite 元数据继续保留并把 capture_status 标为 purged。把 capture_dir 热更新为 null 可停止新抓包;COMPACTGATE_CAPTURE_DIRCOMPACTGATE_CAPTURE_BODY_MAX_BYTES 仍优先于配置文件。

logging.redact_body 是为旧配置保留的兼容字段,当前不会改变 SQLite 正文或抓包正文;鉴权头、Cookie 头和常见凭据查询参数始终单独脱敏。Studio 不提供这个无效开关,保存其他日志设置时也不会重写它。

最重要的字段只有这些:

primary

普通请求走的主上游。

primary.model_override 非空时覆盖请求模型;primary.reasoning_effort 可设为 lowmediumhighxhighmax,仅对 Responses API 写入 reasoning.effort。两者留空都表示保留客户端请求。旧配置中的 none 仍会按原值转发,但不会出现在 Studio 下拉选项中;需要取消覆盖时应改为“跟随请求”。

primary.state_domain_id 用来声明该 profile 的 provider 状态域。保存的 profile 留空时按 profile ID 隔离;没有 profile 的直连配置才回退到上游 URL origin。只有两个 profile 明确配置相同值时,CompactGate 才把它们视为可直接共享 encrypted reasoning/compaction 状态。

旧对话切换 profile 后失败的根因不是路由仍指向旧 host,而是请求正文携带了旧上游签发的 provider-private state。welfare 抓包样本包含 59 个 reasoning item(其中 15 个 encrypted_content 为 null 或格式无效)、1 个 compaction item 和 51 组工具调用;原实现切换 host 后仍把这些状态逐字节发给新上游,而新对话没有这批外域状态,所以新对话可以成功。CPA/CLIProxyAPI 只验证 GPT reasoning 密文的外层格式,不能证明新上游可解密;sub2api 还会从不可变正文重建每次尝试、跨 passthrough 边界删除整个 reasoning,并在明确的 400 invalid_encrypted_content 后定向重试。CompactGate 组合这些边界,并额外处理 compaction、工具配对、状态域绑定和流式响应提交点。

primary_failover.state_portability

  • off:不做旧会话迁移。
  • recover_on_error:默认值。第一次始终把当前请求原样发给选中的 profile;只有明确的可修复 400,或具有目标健康和状态域证据的兼容性失败,才从原始请求体派生恢复请求。旧配置值 compatibility_firstdomain_aware 在加载时归一化为此模式。

明确 400 invalid_encrypted_contentprevious_response_not_found 直接执行一次错误专用修复。修复后仍返回同一明确 400 时,若跨域证据成立仍可继续 strict。welfare 一类泛化失败必须同时满足:请求含 provider-owned state、目标 profile 最近 15 分钟有同模型/端点的无状态成功,以及已知状态域不匹配;来源未知时还要求 10 分钟内出现两次相同失败。恢复链为原始请求、CPA 低损清理、严格跨域清理;错误专用修复按错误位置插入。所有请求固定在当前 profile/host,最多发送 4 个不同 body,且每次都从同一份不可变请求体派生。只有完整成功响应才更新持久绑定。

通过 POST /api/config/profiles/apply 手动应用 Codex profile 是一次权威切换:CompactGate 会清除旧的进程内会话 stickiness,并强制下一次 Primary 选择新 profile,即使 auto_schedule=true。自动调度内部同步 active profile 不触发该强制逻辑。

compact

compact 请求走的上游。

CompactGate 只在以下精确信号出现时标记为压缩流量:路径是 /v1/responses/compactremote_v1);/v1/responses 的 Codex turn metadata 中 request_kindcompactionlocal,实现为 responses_compaction_v2 时为 remote_v2);或者 input 中存在 type: "compaction_trigger"remote_v2)。body 中的 client_metadata 优先于兼容请求头。压缩成功后的普通 /v1/responses 后续 turn 即使携带 type: "compaction" 和 V2 beta 标记,也保持普通 primary 日志;Responses provider state 原样保留,CompactGate 签名状态可还原为摘要消息。它不会根据提示词、模型名或 token 数猜测请求用途。Remote V2 的真实压缩动作仍使用 Primary 上游。

compact.upstream_mode

可选值:

  • split:local/Remote V1 compact 请求走 compact.base_url
  • primary:local/Remote V1 compact 请求也走 primary.base_url

Remote V2 始终走 primary 上游,不受此项切换,也不会改写为 compact 模型。

compact.model_mode

可选值:

  • linked:按模板改写模型名
  • custom:无论原模型是什么,都改成固定模型

compact.model_template

model_mode = "linked" 时使用。

比如:

{model}-openai-compact

如果原模型是:

gpt-5.5

那么 compact 请求会被改写成:

gpt-5.5-openai-compact

compact.model_override

model_mode = "custom" 时使用。

例如你可以固定改成:

my-compact-model

上游扩展头与显式代理

primarycompactclaude.primaryclaude.compact 都支持:

{
  "extra_headers": {
    "x-provider-feature": "enabled"
  },
  "proxy_url": "http://user:pass@127.0.0.1:8080"
}

extra_headers 会随 profile 保存,但禁止认证、Cookie、长度和 hop-by-hop 头; 值不会从 GET /api/config 返回,抓包也按配置头名动态脱敏。proxy_url 仅接受 HTTP CONNECT 代理,显式配置优先于环境代理且非法值直接失败,不会静默直连。

Claude 场景路由

claude.scene_map 可为 long_contextbackgroundweb_searchthinkingimagedefault 指定 profile_id 与可选 modellong_context_bytes 使用 UTF-8 文本字节数判断,图片 base64 不计入。优先级固定为:显式请求 profile、 长上下文、后台任务、Web 搜索、思考、图片、默认。

Codex 与 Claude 请求都可携带 x-compactgate-profile: PROFILE_ID 做单次精确选择; 仅 loopback 客户端可用,转发前会删除,并且不会修改 active profile、健康度或粘性。

Studio 页面能做什么

打开 http://127.0.0.1:7865/ 后,你可以直接:

  • 修改主上游和 compact 上游地址
  • 为 Codex/Claude 的主路由和压缩路由选择上游协议
  • 切换 split / primary 模式
  • 切换 linked / custom 模型改写方式
  • 从已保存的 Primary/Claude 上游按需拉取模型并选择思考强度,或保留自定义兼容模型
  • 直接保存 API Key
  • 预览某条请求会走哪条路由
  • 查看健康状态
  • 实时查看最近日志

配置页六个 Tab 分别使用 /config/profiles/config/routes/config/model/config/logging/config/preview/config/portable,可通过浏览器前进、后退回顾切换历史。

模型目录不会在页面加载时自动联网。CompactGate 会根据已保存的 base URL 生成 OpenAI-compatible /v1/models/models 候选;仅当端点返回 404/405 时尝试下一条路径。Primary 使用 Bearer 凭据,Claude 保留 Anthropic 兼容鉴权头。修改 URL 或凭据草稿后需先保存,再拉取对应上游目录。

日志是实时刷新的,使用的是 SSE。

默认情况下,日志会记录这些信息:

  • 路由类型
  • 状态码
  • 模型映射
  • 实际发送给 Primary Responses 上游的 reasoning.effort
  • 上游主机
  • 耗时
  • request id
  • 可选的客户端请求体
  • 可选的实际上游请求体
  • 可选的上游响应体

元数据始终持久化到本地 SQLite。只有 logging.persist_body = true 时正文才进入 SQLite;推荐保持关闭并通过有界抓包目录按需诊断。Studio 日志列表不返回正文或本机抓包路径,展开详情后也只有点击“查看抓包”才会加载原始内容。抓包查看器会对“客户端请求 → 上游请求”和“上游响应 → 客户端响应”做最多 200 项的结构化 JSON 对比;正文截断或不是 JSON 时明确标为不可比较。压缩日志还保存实现名、请求 compaction/trigger 数和响应 compaction 数,便于定位协议漂移。

日志和本地数据库

请求元数据会持久化到 SQLite,不按页面展示数量自动删除。数据库文件、WAL 和 SHM 侧写文件合计默认上限为 1 GiB。超过上限时,CompactGate 先清空四段历史正文并把 body_status 标为 purged;回收后仍超限才删除最早的元数据行。维护任务执行 SQLite checkpoint/vacuum 回收磁盘空间。

默认文件位置是:

compactgate-logs.sqlite

日志库路径固定由配置文件路径派生。例如 compactgate.json 对应同目录的 compactgate-logs.sqlite

调试抓包

如果你需要把单次请求写成独立 JSON 文件,包含脱敏后的请求头和正文,可以临时打开调试捕获:

COMPACTGATE_CAPTURE_DIR=/path/to/captures npm start

也可以在 Studio 的“配置 → 日志存储”中选择“分离存储”并设置目录。默认不开启抓包。

管理 API

如果你想自己对接页面或脚本,可以用这些接口:

GET /api/health

查看服务和上下游状态。

GET /api/config

查看当前运行配置。

注意:这个接口不会返回明文 API Key。

GET /api/config/export

导出完整配置。

这是本地可移植备份,会保留配置中直填的 API Key;GET /api/config 才是用于页面展示的脱敏配置。

每次配置持久化前还会在同目录创建 0600 版本备份并保留最近 10 份:

  • GET /api/config/backups:列出备份元数据
  • POST /api/config/backups/restore:传 {"backup_id":"...","confirm":true} 恢复
  • DELETE /api/config/backups:传相同确认结构删除

恢复前会先校验 JSON 和完整配置合同;失败不会改变内存或当前配置文件。

PATCH /api/config

热更新配置并写回磁盘,不需要重启。

POST /api/test-route

预览一条请求最终会怎么路由、怎么改模型。

POST /api/compact/capability-probe

{"model":"MODEL"} 对当前 compact 路径执行一次真实、30 秒上限、512 KiB 响应上限的原生压缩探测。探测不会写日志、bridge、去重、profile 健康或故障转移 状态;Chat 上游直接返回不支持,网络/上游错误通过 supported=false 返回。

GET /api/logs/recent

分页读取日志。默认返回 logging.keep_recent 条;历史记录可以用 limitoffset 继续读取。

可以加筛选:

?route=primary
?route=compact
?host=api.example.com
?limit=200&offset=200

GET /api/logs/:request_id

按响应头 x-compactgate-request-id 查询单条元数据日志、正文状态与抓包生命周期状态。本机抓包路径和正文内容不会返回。不存在返回 404;旧数据库中若有重复请求 ID,返回 409 且不会删除历史日志。

GET /api/logs/:request_id/capture

按需读取受管抓包。写入中返回 202,从未保存返回 404,已清理或文件丢失返回 410,重复请求 ID 返回 409。

GET /api/logs/:request_id/capture/download

下载同一条受管抓包的 JSON 文件,不暴露本机路径。

POST /api/logs/maintenance/purge-bodies

请求体必须包含 { "confirm": true }。清空 SQLite 中四段历史正文、保留元数据行,并返回清理条数和清理前后数据库大小。

GET /api/events

SSE 实时事件流。

它会推送两类事件:

  • snapshot:当前配置、健康状态、当前日志页
  • log:一条新完成的代理日志

开发

启动后端开发模式:

npm run dev

如果你还想单独跑前端开发服务器,再开一个终端:

npx vite --host 127.0.0.1 --port 5173

运行检查:

npm test
npm run build

常见问题

1. Codex 没有调用 /v1/responses/compact,压缩还能分流吗

可以。新版 Codex 还可能把压缩请求发到普通 /v1/responses:本地压缩由 x-codex-turn-metadata 中的 request_kind: "compaction" 标识,Remote V2 由 input[].type: "compaction_trigger" 标识。CompactGate 会把它们记录为 compact 逻辑流量,但 Remote V2 实际复用 primary 上游与协议,不走 /responses/compact,也不需要 compact 模型。

如果日志仍显示为 primary,再检查 Codex 配置里是不是:

name = "OpenAI"
wire_api = "responses"

2. compact 请求走错了上游

去 Studio 页面里看:

  • compact.upstream_mode 是不是你想要的值
  • compact.base_url 配得对不对

也可以用 POST /api/test-route 预览。

3. 主上游已经支持 compact 模型,还需要单独 compact 上游吗

不需要。

这只影响 local/Remote V1。直接把:

"upstream_mode": "primary"

这样 compact 请求也走主上游。

4. 日志会保存哪些正文

默认 logging.persist_body = false,SQLite 只保存可检索元数据,不保存客户端请求体、实际上游请求体、上游响应体或客户端响应体。兼容模式可打开 persist_body,但数据库达到容量上限时会优先清理这些正文。

需要原始请求和响应时,推荐配置有大小上限的独立抓包目录:

COMPACTGATE_CAPTURE_DIR=/path/to/captures

5. split 模式下 compact 之后下一次普通请求报错怎么办

请确认你运行的是新版 CompactGate。

这类报错通常不是超时。常见现象是 Codex 提示:

Stream disconnected before completion: stream closed before response.completed

如果 primary 上游收到原始 type: "compaction",但不能验证其中的 encrypted_content,上游可能会返回 invalid_encrypted_content,并且响应流里没有 Codex 需要的 response.completed 事件,最终就会显示上面的断流错误。

如果日志同时显示 status=502Client disconnected before upstream response completed.,需要继续查看 upstream_statusstream_terminal_eventclient_disconnect_phasestream_outcome。Remote V2 或 Remote V1 在已经收到 response.completed/[DONE] 后,Codex CLI 可能先关闭连接而上游 HTTP 流尚未发出 end;新版会保留真实上游状态和已缓冲响应,并记录 stream_outcome=successclient_disconnect_phase=after_terminal,不再把它当作压缩失败。终止事件之前关闭仍记录为客户端取消或未完成流;真实上游 5xx 仍保留为上游错误。

日志中的 response_model 只表示上游响应正文明确返回的模型。Remote V2 的 response.compaction 可能不携带 model,此时 response_model 保持为空,effective_response_model 则在成功时使用 target_model,并由 response_model_source=target_fallback 标记为“目标模型推断”。Studio 主展示“有效响应模型”,同时单独显示“上游声明模型”,不会把推断值伪装成上游字段;只有明确读到上游模型时来源才是 upstream,失败、取消或未完成流则为 unavailable。超出流观察器 payload 上限的完整 response.completed 仍会按事件名识别,并记录 stream_oversized_event_count 供诊断。

当前版本在 compact.upstream_mode = "split" 时,会把成功 compact 响应里的可读 summary 状态记录下来。下一次包含同一段 encrypted_content 的普通 /v1/responses 请求会继续走 primary,但 CompactGate 会先把可读 compact 状态转换成 assistant summary message,再转发给 primary。

这个修复支持英文和中文等 Unicode 可读摘要,可以避免把可读摘要误当成不可解密的 compact 加密状态发给 primary。如果 compact 上游只返回不可读的加密状态,CompactGate 不能解密它,primary 仍可能无法恢复这段压缩上下文。

如果你刚更新代码,请重新执行构建并重启 CompactGate,让运行中的 dist/server/main.js 加载新版本。

About

允许codex的常用接口和compact走不同的api

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages