docs: streamline documentation around current behavior

Remove version migration narratives, consolidate repeated explanations, and organize settings and architecture references.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-10-08 22:26:12 +08:00
co-authored by Codex
parent 66a7e4a695
commit bda6d973a8
12 changed files with 161 additions and 227 deletions
+2 -3
View File
@@ -21,7 +21,7 @@
## 快速预览
无需部署,打开 [快速预览服务](https://codex-proxy-rs.ainz.cc) 即可体验管理端的系统概览、账号分组、代理管理与用量统计
无需部署即可体验管理端的系统概览、账号分组、代理管理与用量统计:
| 登录信息 | 默认值 |
| --- | --- |
@@ -53,8 +53,7 @@ curl -fsSL https://raw.githubusercontent.com/zyycn/codex-proxy-rs/main/deploy/in
[安装脚本](deploy/install.sh) 默认安装到当前目录下的 `codex-proxy-rs/`,下载同一正式 Release 的部署文件,
自动生成密码、设置目录权限并启动服务
完成后会显示访问地址和管理员密码,请保存密码,再按下方步骤
[添加账号与客户端密钥](#添加账号与客户端密钥)
完成后保存显示的访问地址和管理员密码,再[登录管理端](#登录管理端)并[添加账号与客户端密钥](#添加账号与客户端密钥)
可在执行 `bash install.sh` 时传入环境变量:
+2 -5
View File
@@ -28,11 +28,8 @@ cpr-plugin package \
资源映射相对于作者清单所在目录解析。输出包括完整插件 ID、版本和目标 triple 命名的 `.tar.gz` 及 `.sha256`;归档根目录直接包含 `plugin.json`、可执行文件和已声明资源,不包含开发依赖
作者清单可省略普通贡献项的 `id`、`version` 和固定阶段;`middleware` 必须显式声明版本 `4`,
并从 `http`、`websocket`、`service`、`request`、`attempt` 中选择挂载阶段;`upstream_adapter` 必须显式声明版本 `2`。
CLI 与 SDK 的 `Manifest::from_author_slice`、`PluginBuilder::from_json` 共用规范化入口;归档中的
`plugin.json` 写入补全后的贡献项字段,不保留另一份作者声明。
完整规则见 [SDK 清单](../../crates/gateway-plugin/sdk/docs/manifest.md)
作者清单的省略规则、能力版本和挂载阶段见 [SDK 扩展项声明](../../crates/gateway-plugin/sdk/docs/manifest.md#扩展项简写)。
CLI 与 SDK 的 `Manifest::from_author_slice`、`PluginBuilder::from_json` 共用规范化入口,归档只保留补全字段后的 `plugin.json`
支持 `x86_64-unknown-linux-gnu`、`aarch64-unknown-linux-gnu` 和 `aarch64-apple-darwin`。
完整开发示例在独立仓库 `codex-proxy-plugins` 的 `examples/workbench`,可通过[子模块](../../../docs/development.md#源码联调)检出;本工具不负责源码构建、上传或启用插件
+5 -7
View File
@@ -1,7 +1,7 @@
# Gateway Plugin SDK
用于编写 Codex Proxy 插件的 Rust 合同与可选会话辅助。插件只依赖本包,不依赖宿主 Core、Admin、Store 或 Host。
SDK 仍处于实验阶段;能力是否可用取决于宿主支持、清单声明与实际注册,
SDK 处于实验阶段;能力是否可用取决于宿主支持、清单声明与实际注册,
而不只是枚举中存在该名称
| 版本 | 当前值 | 用途 |
@@ -90,14 +90,12 @@ sequenceDiagram
元数据保留 64 KiB 上限;正文总量不由插件传输层额外限制。传输内部以 256 KiB 为单次 I/O 块,
不逐块发起 RPC,也不要求插件配置与宿主同步。读取按进度扩展缓冲区;截断正文必须报错,不能作为完整消息交付
`read_frame` / `write_frame` 使用协议 v2。帧写入队列之前先用 `validate_frame` 校验元数据,
帧写入队列前先用 `validate_frame` 校验元数据,
避免本地输入错误关闭共享传输。协议校验不会自动重试业务调用;部分读写中断后不能把当前位置当作新帧边界。
底层 `read_frame` / `write_frame` 不是取消安全的;自行编排会话时,不能在 `select!` 的其他分支完成后
重建半途中的帧读写。`PluginSession` 会在处理调用完成通知时保留同一个读取 future,直到完整帧到达或会话关闭
会话由宿主发送 `Hello`,插件校验协议后返回携带相同 incarnation 的 `Ready`。
注册结果必须与清单中的能力一致。一次 `Call` 的结果、错误和流帧按 ID 关联;回调必须
携带当前父调用 ID。实例、代次、阶段、账号或资源 ID 用于关联调用和管理资源生命周期
插件须校验 `Hello` 的协议版本。一次 `Call` 的结果、错误和流帧按 ID 关联;实例、代次、阶段、账号或资源 ID 用于关联调用和管理资源生命周期
## 本地验证
@@ -117,6 +115,6 @@ RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml -p
RUST_MIN_STACK=16777216 RUSTDOCFLAGS='-D warnings' cargo +1.97.0 doc --manifest-path backend/Cargo.toml -p gateway-plugin-sdk --all-features --no-deps --locked
```
源码中的类型和字段是当前合同;宿主接入、目录边界与 Runtime 集成验证见
[系统架构](../../../../docs/architecture.md)。Provider 固定为宿主内置的 OpenAI 与 xAI;插件实现只依赖公开 SDK 合同。
源码中的类型和字段是当前合同;宿主接入、目录边界、内置 Provider 与 Runtime 集成验证见
[系统架构](../../../../docs/architecture.md#31-插件扩展)。
单元测试与文档构建不代替目标平台上的安装、配置及实际能力验证
@@ -46,7 +46,7 @@ session.run(plugin).await?;
## 能力与方法
Provider 固定为宿主内置的 OpenAI 与 xAI。当前宿主开放以下 11 类扩展能力;新插件的 `middleware` 使用 v4,`upstream_adapter` 使用 v2,其余使用 v1;旧合同见[弃用窗口](manifest.md#接口弃用):
Provider 固定为宿主内置的 OpenAI 与 xAI。宿主开放以下 11 类扩展能力,版本与声明规则见[扩展项简写](manifest.md#扩展项简写):
| 能力 | 类型化方法 |
| --- | --- |
@@ -227,7 +227,7 @@ SDK 不自动重试。超时或断连不能证明写入未提交;重试上限
### 洋葱中间件
新插件使用 `middleware.version: 4`,所有挂载使用同一个 `ctx + next` 组合器。清单显式选择处理边界,绑定决定顺序;
`middleware.version: 4` 的所有挂载使用同一个 `ctx + next` 组合器。清单显式选择处理边界,绑定决定顺序;
不按 URL、消息类型或每个内部步骤增加处理器。仅处理一个边界可直接声明其调用类型;跨边界插件使用 `MiddlewareCall`
在一个处理器内匹配类型化视图,不感兴趣的调用交给 `call.forward().await`
@@ -353,7 +353,7 @@ Responses 终端重新解码插件正文时读取改写后的正文上限;atte
更早的改写使用上述 HTTP 请求的 `settings`。`request.head.settings_sources` 使用相同的来源结构,`execution.input` 表示进入当前 Key 作用域时的有效值。
原生 Responses WebSocket 每轮请求读取当前宿主快照,重新认证并解析 Key 策略,同时保留握手阶段的显式改写;已开始的执行保持自己的冻结值
middleware v3 / v4 均可读取和改写完整请求、响应 headers、正文与响应帧,不要求
中间件可读取和改写完整请求、响应 headers、正文与响应帧,不要求
`requests` 权限,也不按字段名称过滤认证、Cookie、会话或连接头。安装者承担插件读取与修改这些数据的风险。
类型、HTTP 格式、大小及资源生命周期仍需符合合同
@@ -18,8 +18,7 @@
作者清单不能包含 `package`。使用 `Manifest::from_author_slice()` 校验并规范化;直接反序列化的 `Manifest`
不会执行作者清单的 ID 与固定阶段推导,声明的可选字段仍使用其默认值。打包后,`main` 和所有资源都必须进入 `package.files`;宿主用
`package_for(host, os, architecture)` 检查版本与平台。运行模式仅为 `trustedProcess`:插件拥有与宿主
相同的系统身份,**不是进程沙箱**
`package_for(host, os, architecture)` 检查版本与平台。运行模式为 `trustedProcess`,信任边界见[完整信任](#完整信任)
宿主实际开放的合同由 [plugin-host-compatibility.json](../../runtime/plugin-host-compatibility.json)
声明,SDK 的 `Capability::contract_versions()` 只表示 SDK 能描述的行为版本,不能代替宿主支持检查
@@ -33,28 +32,22 @@
`capabilities` 使用扩展能力标识;Provider、模型和账号 ID 不属于这个集合
当前清单、进程协议和宿主兼容声明的格式版本均为 `2`。清单不接受 `permissions` 字段;使用其他格式版本的包须用匹配的 SDK 和 CLI 重新构建
清单、进程协议和宿主兼容声明的格式版本均为 `2`;清单不接受 `permissions` 字段
安装包清单校验与已安装元数据读取是两个边界:清单拒绝未知字段,并检查必需字段、类型和版本;
数据库元数据允许的字段增减见[持久化规则](../../../../../docs/architecture.md#发布与执行边界),不改变清单或 RPC 合同。
插件依赖新增接口或字段时,应通过 `engines.codex-proxy-rs` 声明所需宿主版本。宿主范围和能力版本不匹配时持续提示风险,仍可尝试启动;包结构、平台与 RPC 封装校验失败仍阻止加载。CLI 的作者校验保持严格,不支持的能力合同须使用匹配的 SDK 构建
数据库元数据的读取规则见[制品与发布](../../../../../docs/architecture.md#制品配置与发布),不改变清单或 RPC 合同。
`engines.codex-proxy-rs` 声明插件所需的宿主范围。范围或能力版本不匹配时持续提示风险,允许尝试启动;包结构、平台与 RPC 封装校验失败阻止加载。CLI 严格校验作者声明是否符合 SDK 支持的合同
## 接口弃用
新合同与旧合同并行提供,管理端和插件加载日志会提示旧合同及迁移方式。新合同首次正式发布后,旧合同至少再兼容 7 个正式版本;主版本、次版本和补丁版本各计一次,alpha、beta、rc、exp 及同版本重试不计
当前支持范围由[宿主兼容声明](../../runtime/plugin-host-compatibility.json)给出,弃用状态、剩余兼容窗口及作者说明以[弃用清单](../../runtime/plugin-api-deprecations.json)为准,管理端和加载日志展示同一提示
剩余窗口由宿主发行物中的 [弃用清单](../../runtime/plugin-api-deprecations.json) 给出,不通过版本号差值推算。窗口内提供旧合同转换;窗口结束后移除转换和支持声明,所有旧合同插件都应升级 SDK、处理器与清单再重新打包。仍允许尝试启动,但不再保证兼容,不按实际读取过哪些字段判断
| 旧合同 | 替代合同 | 迁移内容 |
| --- | --- | --- |
| middleware v3 | middleware v4 | 执行设置及 `settings_sources.execution` 中的 `disable_fast` 改为 `fast_mode` 三态 |
| upstream_adapter v1 | upstream_adapter v2 | 使用新版 `UpstreamAdapterRequest.fast_mode`,同步更新 SDK 和贡献版本 |
旧布尔投影仅在 `disabled` 时为 `true`,`default` 和 `enabled` 均为 `false`。旧中间件原样回传布尔值时保留实际三态,修改其他设置不会丢失 `enabled`;实际从 `true` 改为 `false` 时设为 `default`。旧接口不能表达强制开启,需升级合同使用三态
兼容窗口自替代合同首次正式发布起至少覆盖 7 个后续正式版本;主版本、次版本和补丁版本各计一次,预发行与同标签重试不计。
窗口内提供适配,窗口外不保证兼容;是否使用过特定字段不改变判断,版本号差值也不能代替清单中的计数
## 扩展项简写
作者声明可以省略 `id` 和固定阶段;默认版本为 `1`,新中间件显式选择版本 `4`,新版上游适配器 SDK 使用版本 `2`:
作者声明可以省略 `id` 和固定阶段;普通能力的版本默认为 `1`,`middleware` 显式声明版本 `4`,`upstream_adapter` 显式声明版本 `2`:
```json
{
@@ -71,7 +64,7 @@
}
```
默认扩展项 ID 为 `<publisher>.<name>.<capability-kebab>`。middleware v3 与 upstream_adapter v1 在[弃用窗口](#接口弃用)内仍可加载。除 `middleware` 外,阶段由
默认扩展项 ID 为 `<publisher>.<name>.<capability-kebab>`。除 `middleware` 外,阶段由
capability 固定并由工具生成:
| 阶段 | 能力 |
@@ -83,7 +76,7 @@ capability 固定并由工具生成:
| `management` / `command_line` | `management` / `command_line` |
| `maintenance` | `maintenance` |
`observer` 使用一个处理器接收完成与上游 WebSocket 事件,实例绑定按 `event` 选择订阅类型;具体合同见[观察事件](capabilities.md#路由调度与观察)。宿主仅接受 `observer` 声明;使用 `request_lifecycle`、`usage` 或 `web_socket_observer` 声明的插件须更新清单、处理器与绑定后重新打包
`observer` 使用一个处理器接收完成与上游 WebSocket 事件,实例绑定按 `event` 选择订阅类型;具体合同见[观察事件](capabilities.md#路由调度与观察)
`middleware` 必须从 `http`、`websocket`、`service`、`request`、`attempt` 中显式选择挂载;同一处理器可覆盖多个边界,协议格式等真实业务选择也不能省略。
安装清单若携带不同的固定阶段会被拒绝,而不是在加载时静默改写
@@ -27,7 +27,7 @@
同一请求不能同时命中两个适配器;配置准备时拒绝重叠,执行时再次复核。适配器未命中时仍使用原生上游,
已命中但不可用时拒绝请求。安装、配置、启停和升级使用已有插件管理
upstream_adapter v1 在[弃用窗口](manifest.md#接口弃用)内仍使用旧布尔投影,不能仅修改清单版本而继续使用旧 DTO。能力版本、清单版本、进程 RPC 版本和宿主兼容声明格式版本分别判断,见[清单](manifest.md)。
能力、清单、进程 RPC 和宿主兼容声明分别校验各自版本,见[清单](manifest.md)。
Provider、业务协议和传输名称不能写入宿主兼容清单的 capability / RPC 版本位置
## 执行与受管出站
+4 -14
View File
@@ -3,26 +3,17 @@
sqlx 在服务启动、监听请求之前按编号执行迁移,并把文件 checksum 记入 `_sqlx_migrations`。
已应用迁移的字节与记录不一致时,服务会拒绝启动
初始化基线与后续 schema 变更按递增编号组织。
完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准
迁移按递增编号组织,完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准
## 冻结规则
- 同一大版本内,已合入 main 的迁移文件**永久冻结**,一个字节都不能改——包括注释和
空白。任何改动都会让该大版本的已部署实例在下次重启时因 checksum 失配而无法启动,
且没有自动恢复路径
- 同一大版本内的 schema 变更一律新增编号迁移,永远不回改
旧文件。切换大版本时允许把已完成升级的 schema 折叠成新的初始化基线,因为跨大版本
不支持在线升级,目标版本必须使用全新数据目录部署
- 同一大版本内,已合入 main 的迁移文件**按字节永久冻结**,包括注释和空白。schema 变更及错误修正都须新增编号迁移,使用 `alter` 或回填补偿;修改原文件会导致已部署实例重启时 checksum 失配,且没有自动恢复路径
- 跨大版本不支持在线升级,目标版本必须使用全新数据目录部署;此时可将已完成升级的 schema 折叠为新的初始化基线
- 支持连续升级的预发行线从首次发布起同样冻结迁移。alpha、beta、rc 到对应正式版必须保留
已发布迁移的编号与字节;同一轮 exp 的后续发行也只能追加迁移。发布前以该发行线已发布版本
的数据库验证升级路径,不能只与上一正式版比较。不同 exp 实验线之间、exp 与正式版之间
不保证迁移历史兼容
- `.frozen-sha256` 是冻结清单(`sha256sum` 格式)。CI 会校验:清单内文件
字节未变,且目录里每个 `*.sql` 都已入册。新增迁移时在同一提交里执行
`sha256sum 000N_xxx.sql >> .frozen-sha256` 入册
- 若同一大版本内确需修正已冻结迁移里的错误,用新迁移做补偿性变更(`alter` / 回填),
不要动原文件
- `.frozen-sha256` 使用 `sha256sum` 格式。新增迁移时在同一提交执行 `sha256sum 000N_xxx.sql >> .frozen-sha256` 入册;CI 校验文件字节、SQL 登记完整性及 PR 中清单只增不改
从迁移目录检查已登记文件:
@@ -31,7 +22,6 @@ cd backend/migrations
sha256sum --check --strict .frozen-sha256
```
CI 还会检查是否遗漏新 SQL 文件,并在 PR 中检查清单只增不改。
遇到 checksum 不一致,先核对运行版本和文件来源;不要修改数据库中的 checksum 来绕过校验
## 本地测试库
+27 -60
View File
@@ -66,8 +66,8 @@ services:
GLIBC_TUNABLES: 'glibc.malloc.mmap_threshold=131072:glibc.malloc.trim_threshold=131072'
```
已有部署使用当前 Compose 模板时,需将 `config.example.yaml` 的 `app-runtime` 段合并到现有 `services` 下,保留 PostgreSQL 和 Redis 的桥接配置。
即使不开启优化,也保留该段并将值设为 `''`;Compose 从这里取得参数,无需在 `compose.yaml` 重复填写
Compose 从 `services.app-runtime` 取得参数;不开启优化时保留该段并将值设为 `''`。
PostgreSQL 和 Redis 的凭据桥接配置与该段并列,无需在 `compose.yaml` 重复填写参数
校验并重建应用容器使配置生效;`docker compose restart` 不会更新容器环境变量:
@@ -143,14 +143,15 @@ openssl rand -hex 24
- `store.database.password`
- `store.redis.password`
另行设置 `admin.default_password`。它至少需要 12 个字符,不能是常见弱口令,也不能包含 `$`。
管理员的 `admin.session_ttl_minutes` 控制不活动期限,`admin.session_absolute_ttl_minutes` 控制从登录起计算的最长有效期,
后者省略时为 43200 分钟(30 天)。配置模板分别为 10080 分钟(7 天)和 43200 分钟;已有配置保留自己的不活动期限。
页面仅在接口报告会话失效时尝试续期,已过期或撤销的会话需要重新登录;续期不能超过最长有效期。
修改配置需重启,已有会话的最长有效期不会因此延长。
缺少最长有效期记录的会话保持原有固定期限,重新登录后应用续期策略。
`client.session_ttl_minutes` 控制密钥身份的固定有效期,默认 1440 分钟,不随活动续期。
两种身份共用一个 Cookie,成功登录替换当前浏览器的旧会话;不改变 `/v1/*` 鉴权和限额
另行设置 `admin.default_password`:至少 12 个字符,不能使用常见弱口令或包含 `$`
| 会话配置 | 含义 | 配置模板 / 默认值 |
| --- | --- | --- |
| `admin.session_ttl_minutes` | 管理员不活动期限 | 模板为 10080 分钟(7 天) |
| `admin.session_absolute_ttl_minutes` | 从登录起计算的最长有效期 | 模板及省略时均为 43200 分钟(30 天) |
| `client.session_ttl_minutes` | 密钥身份固定有效期,不随活动续期 | 默认 1440 分钟(1 天) |
修改配置需重启,不能延长已建立会话的最长有效期。浏览器会话与续期规则见[认证 API](../docs/api.md#统一登录与会话),独立于 `/v1/*` 鉴权和限额
PostgreSQL 与 Redis 密码必须是 48 位十六进制字符。Compose 通过 `config.yaml` 的凭据桥接区
引用同一密码;三个值都不需要额外导出为环境变量,数据库和 Redis 密码也不能嵌入连接 URL
@@ -258,7 +259,7 @@ HTTP 传输不加密,公网部署仍建议使用 HTTPS。
并设置 `X-Accel-Buffering: no` 和 `Cache-Control: no-cache, no-transform`。
反向代理仍需允许这些响应头生效;首个事件到达前的等待也需要足够的读取超时
网关默认不限制模型请求的总执行时长。OpenAI 上游流默认有 300 秒空闲超时,持续收到数据不会因总时长超过 600 秒而中断。
网关默认不限制模型请求的总执行时长;OpenAI 上游流默认有 300 秒空闲超时。
`api.request_timeout_seconds` 默认 `null`,只控制 HTTP 路由返回响应前的等待,不是流式正文或 WebSocket 每轮执行的总时限。
排队、插件显式执行期限与客户端断开的边界见 [请求期限](../docs/api.md#请求期限)
@@ -278,7 +279,7 @@ SSE 注释保活用于防止传输链路空闲断开,不会重置 Codex 等待
已有配置先备份,合并后完全退出并重启 Codex。代理密钥由 Provider 配置提供,
不需要另写 `auth.json`,已有官方登录文件可以保留
CC Switch 4.0.4 的一键导入包含连接信息和当前 Key 的日/周额度查询,默认每 30 分钟刷新。
管理端生成的 CC Switch 一键导入包含连接信息和当前 Key 的日/周额度查询,默认每 30 分钟刷新。
查询地址和凭据随导入生成,在 CC Switch 中修改 Provider 的地址或 Key 后,需重新导入以同步用量查询。
导入不包含下方模板的原生生图和 WebSocket 设置。需要原生生图时,使用下方配置直连 CPR,
避免 CC Switch 切换或接管后重写 Provider 配置
@@ -318,7 +319,7 @@ goals = true
`OpenAI` 是自定义 Provider ID,大小写要与 `model_provider` 一致。合并配置时修改已有表,
不要重复添加 `[features]` 或 Provider 表。更换模型时也要检查其支持的推理强度。
密钥以明文保存,文件仅供本人读取,不要提交到 Git
密钥以明文保存,文件仅供本人读取,不要提交到 Git。接入代理不需要扩大命令沙箱的联网或文件权限
需要指定完整模型目录时,在账号的模型列表中导出所选 Codex 模型,并在 `config.toml` 顶层设置
`model_catalog_json = "/absolute/path/to/cpr-model-catalog.json"`。导出文件不含账号凭据;
@@ -339,21 +340,6 @@ goals = true
OpenAI OAuth 账号的上游传输方式默认 WS;固定为 SSE 的账号不承接必须依赖 WS 的预热及连接内续接。
仅使用这类账号时,客户端保持 `supports_websockets = false`,避免先尝试 WS 再回退
### 客户端配置兼容
仅含代理密钥的 `auth.json` 配合 `requires_openai_auth = true` 仍可用于已有请求,
但 API Key 登录本身不会启用原生生图。需要生图时换用上述 Provider 配置
使用 Codex 0.153.4 时,旧配置中的以下字段需删除或替换:
| 字段 | 处理 |
| --- | --- |
| 顶层 `disable_response_storage` | 无有效配置定义,删除 |
| 顶层 `network_access = "enabled"` | 无有效配置定义,删除;不是客户端 API 的联网开关 |
| `features.responses_websockets_v2` | 已标记移除,使用 Provider 的 `supports_websockets` |
`image_generation` 和 `goals` 仍有效。不要为了接入代理,顺带扩大命令沙箱的联网或文件权限
### 时区与客户端环境信息
要覆盖模型请求中的位置和时区,在管理端启用全局自定义位置,或为账号关联的代理设置位置;
@@ -370,8 +356,8 @@ Codex 回答仍显示本机时区时,先核对请求是否使用了预期账
[正文兼容](../docs/api.md#正文兼容),xAI 请求不应用 OpenAI 的请求位置覆盖
Codex 客户端还有独立的出站请求。analytics 使用 `chatgpt_base_url`,可以包含真实系统类型、
系统版本和架构;模型 Provider 的 `base_url` 不改变该地址。已核对的官方 release 配置默认启用
Statsig 指标,目标为 `https://ab.chatgpt.com/otlp/v1/metrics`,不由网关上游画像控制
系统版本和架构;模型 Provider 的 `base_url` 不改变该地址。官方发行配置默认启用 Statsig 指标,目标为
`https://ab.chatgpt.com/otlp/v1/metrics`,不由网关上游画像控制
需要关闭这两类遥测时,将以下字段合并到客户端 `config.toml`,避免重复创建已有表,完全退出并重启 Codex:
@@ -389,10 +375,8 @@ metrics_exporter = "none"
- 仍提示登录:确认实际读取的用户配置目录、选中的 Provider 和客户端版本,再完全退出重启。
新 Provider 使用 `requires_openai_auth = false`,不依赖本地 ChatGPT 登录
- 没有生图工具:检查配置是否被覆盖、Actor 标记是否保留、模型是否支持图片输入。
CC Switch 4.0.4 的一键导入不保留 Actor 标记,配置写入时还可能重算 `requires_openai_auth`,
不能只检查 CPR 导出的模板,需要核对 Codex 实际读取的 Provider 配置。
官方客户端还会检查缓存登录状态;已核验版本在本地账号为 Free 时会隐藏生图。
- 没有生图工具:核对 Codex 实际读取的 Provider 配置,确认 Actor 标记、`requires_openai_auth = false` 和图片输入能力,
不只检查导出模板;CC Switch 导入不包含生图设置。客户端缓存的本地 Free 账号也可能影响工具展示。
先备份并区分本地真实账号文件与代理密钥文件,不要直接删除全部登录状态
- 已调用生图但失败:查看服务端账号的凭据、权限、额度和请求错误,不能只凭文本对话成功判断
- `401`:检查代理密钥是否正确、是否启用;Actor 标记不能代替密钥
@@ -455,11 +439,9 @@ Provider schema 以明文 JSON 保存于 PostgreSQL。Redis 只保存可重建
`application`、`oauth-recovery`、`request-dump`。专用 tracing target 为 `oauth_recovery` 和
`request_dump`;普通日志保留各 Rust 模块的 target,便于按模块过滤。
程序只管理上述规范名称的日志,其他命名的文件由运维手动清理。
普通日志未配置 `retention_days` 时默认使用 7 天,显式配置优先
日志时间戳采用部署时区并携带数字偏移,文件按该时区的日期轮转和整组保留:例如 9 月 8 日配置 1 天,会保留 9 月 7 日全天及 9 月 8 日的所有分片,
到 9 月 9 日才允许清理 9 月 7 日。这会略多保留,保证跨午夜及高流量时不留下半天日志。
配置 7 天同理,保留前 7 个完整自然日及当天,夏令时日期允许为 23 或 25 小时。切换时区不重命名历史文件;
到 9 月 9 日才允许清理 9 月 7 日,避免留下半天日志。夏令时日期允许为 23 或 25 小时。切换时区不重命名历史文件;
按文件名日期与部署时区中的实际修改日期取较近值,旧日期分片近期被写入时整组延后删除。
`max_file_size_mb` 仅决定分片大小(默认 20 MiB),不决定保存时长,单条大记录不会被截断。
关闭的分片压缩为 `.log.gz`;成功压缩、同步并发布归档后才删除原文件,保留原修改时间。
@@ -545,15 +527,6 @@ OAuth 恢复开关为 `host.logging.oauth_recovery`,默认关闭,与普通
升级时先阅读目标版本说明,下载同一 Release 的部署附件,对比模板并合并必要配置,保留已有凭据
和 Compose 自定义项。不要用模板覆盖 `config.yaml`,也不要从 `main` 下载模板搭配旧镜像
按现有配置和接入方式检查以下升级条件:
| 适用条件 | 升级操作 |
| --- | --- |
| `config.yaml` 含 `openai.wire_profile.location` | 该字段会被忽略,可删除;如需继续覆盖请求位置,将值填入管理端全局请求位置并开启开关,数据库初始化不会自动导入 |
| `config.yaml` 含 `host.logging.file.max_files` | 该字段会被忽略,可删除;日志按 `retention_days` 保留,`max_file_size_mb` 只控制分片大小 |
| 使用带 `app-runtime` 继承的 Compose,现有 `config.yaml` 缺少对应段 | 合并模板中的 `services.app-runtime`,保留原凭据桥接;不开启内存优化时将 `GLIBC_TUNABLES` 设为 `''`,见 [小内存优化](#小内存优化) |
| 使用旧管理员认证接口或 Cookie | 改用 `/api/auth/*` 并重新登录;会话合同见 [认证 API](../docs/api.md#4-浏览器认证) |
更新部署文件后,从安装目录拉取目标版本镜像并重建应用容器:
```bash
@@ -587,14 +560,14 @@ docker compose -f deploy/compose.yaml build codex-proxy-rs
| 通道 | 命名示例 | 接收的版本 |
| --- | --- | --- |
| Stable | `3.12.0` | 正式版 |
| RC | `3.12.0-rc.1` | RC、正式版 |
| Beta | `3.12.0-beta.1` | Beta、RC、正式版 |
| Alpha | `3.12.0-alpha.1` | Alpha、Beta、RC、正式版 |
| Exp | `3.10.0-exp.1` | 同一轮实验中编号更高的 exp,仅实验实例可选 |
| Stable | `X.Y.Z` | 正式版 |
| RC | `X.Y.Z-rc.N` | RC、正式版 |
| Beta | `X.Y.Z-beta.N` | Beta、RC、正式版 |
| Alpha | `X.Y.Z-alpha.N` | Alpha、Beta、RC、正式版 |
| Exp | `X.Y.Z-exp.N` | 同一轮实验中编号更高的 exp,仅实验实例可选 |
普通通道在同一大版本内持续接收更高版本,包括后续小版本和补丁版本的预发行。
切回 Stable 不会降级:例如运行 `3.16.0-beta.2` 时,不能安装 `3.15.1`,需要等待 `3.16.0` 或更高正式版。
切换通道不会降级,Stable 候选也必须高于当前运行版本。
下载、安装或待重启期间不能切换通道
同一 `X.Y.Z-exp.N` 系列只用于同一轮实验,不能混用不同实验分支;变更基线即进入另一实验线,需手动迁移。
@@ -649,8 +622,6 @@ Compose 提供以下在线更新运行参数:
- `CPR_ENABLE_SELF_RESTART=true`:更新或回滚完成后允许管理端请求重启;Docker 进程退出后由
Compose 的 `restart: unless-stopped` 拉起新进程
`CPR_UPDATE_CHANNEL` 不参与版本选择,配置中存在该变量时可删除
Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.tar.gz` 与
`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和
归档路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为
@@ -665,7 +636,7 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.ta
### 插件兼容与发行目录
教学示例在 `codex-proxy-plugins` 独立构建和发布,通过[插件管理](../docs/plugins.md)安装。
宿主发行物当前不内置插件,但仍包含用于兼容检查的封口清单
宿主发行物不内置插件,包含用于兼容检查的封口清单
| 文件或目录 | 用途 |
| --- | --- |
@@ -677,11 +648,7 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.ta
- 非空官方清单中的包经身份、平台和摘要校验后幂等导入;导入不确认信任、不创建配置、不启用,也不删除旧包。管理员确认信任后才执行默认配置流程;重复摘要保留首次安装出处
- `sealed` 是构建封口标记,不是密码学签名。普通上传、URL 或 GitHub 安装不能获得 `builtin` 身份,独立教学示例也不例外
已安装制品的元数据读取允许未知字段,可选字段缺失时使用已定义的默认值;无需仅为这些字段差异重新安装。
废弃字段由启动迁移统一清理,制品摘要、包体、接受事实与实例配置继续保留。缺失必需身份、数据类型错误、
清单或协议不兼容不会因此被忽略,仍需按具体错误处理;不要手动删除元数据字段或修改迁移 checksum 来绕过检查
使用不支持 `plugins/official` 目录的更新器时,须完整解压平台发行包或更新正式容器镜像,确保发行文件齐全。
部署须保留完整的二进制、Web 资源与官方插件目录;不要手动修改制品元数据或迁移 checksum 绕过校验。
macOS arm64 提供构建产物;部署前需验证目标平台的插件运行与上游调用,不能只以打包成功判断可用
## 备份与恢复
+47 -57
View File
@@ -395,6 +395,8 @@ Codex 专用目录中的 `context_window` 与 `max_context_window` 分别表示
### 透传与错误恢复
#### OpenAI 透传
OpenAI 路径保留客户端 Responses wire 语义:请求 body 的未知字段和字段顺序保持不变(受控模型
映射除外),HTTP SSE 与 WebSocket 的上游业务事件除下述客户端错误兼容外按原始字节转发,
response ID 按 opaque 值处理而不假设 UUID 或固定长度;除客户端错误兼容与原生续写额度恢复外,
@@ -404,31 +406,29 @@ Images 请求不读取或重建 JSON,也不要求或映射模型字段;
只在原始字节之外完成账号选择、鉴权头替换和端点路由,成功与非容量失败响应正文保持原始字节。
`/v1/alpha/search` 使用相同的 OpenAI Provider 原生端点边界:body(包括 `model`)不解析、不映射,
`x-codex-turn-metadata` 在移除客户端账号身份并按当前 lease 重写 installation ID 后转发;上游账号
Authorization、Cookie、account ID、originator 和 User-Agent 均由代理安全重建。xAI 是 Grok wire 与
Responses wire 之间的协议转换层,转换只在 xAI Provider 内完成。
上游结构化错误的 message/code/type 按上述边界交付客户端,其中内嵌的账号指纹 UUID 已脱敏。模型映射是
全局精确映射,未命中时模型名原样交给候选 Provider;分组只限定账号集合,不参与模型改名
Authorization、Cookie、account ID、originator 和 User-Agent 均由代理安全重建。
模型映射是全局精确映射,未命中时模型名原样交给候选 Provider;分组只限定账号集合,不参与模型改名
xAI 编码器同样保留路由选定的完整模型名,不内置 `grok`、`grok-latest` 等旧型号别名,也不剥离模型前缀;
需要别名时使用显式模型映射。目录读取失败按错误处理,不用硬编码旧型号补全能力
#### xAI 适配
xAI Provider 将 Grok wire 转换为 Responses wire。上游结构化错误的 message/code/type 可透出,内嵌的账号指纹 UUID 脱敏。
模型名完整保留,不剥离前缀;别名使用显式模型映射。目录读取失败返回错误,不用硬编码型号补全能力
xAI 的 `reasoning.effort` 接受 `none / minimal / low / medium / high / xhigh / max`,去除两端
空白并转为小写后保留档位,不按模型名称降档或删除;模型是否支持该选择由上游判断。
未知值或错误类型返回字段错误,其他 reasoning 字段保留。`web_search.filters` 支持
`allowed_domains` 与 `excluded_domains`,两者不能同时为非空;客户端函数与托管搜索工具
同名时使用内部别名,并在返回工具调用时还原客户端名称。搜索工具的旧顶层 `allowed_domains`
不再映射到 filters,直接返回请求错误
同名时使用内部别名,并在返回工具调用时还原客户端名称。搜索工具顶层的 `allowed_domains` 不受支持,返回请求错误
#### 容量拒绝与重试
OpenAI 明确返回 `server_is_overloaded`、`slow_down` 或模型容量不足错误时,代理在允许安全重放且
尚未交付输出的前提下,先做最多 3 次同账号重试,再通过现有调度换号。没有有效服务器建议时,
按 500ms 起步的指数退避等待,单次最多 8 秒;有效 `Retry-After` 优先,接受秒数、HTTP 日期和零延迟,
不受本地退避上限截断。流内失败事件使用相同解析规则,WS 转 HTTP 回退也遵守服务器建议;
等待受请求截止时间和取消约束,重试受请求总尝试次数约束。
`server_is_overloaded`、`slow_down` 等可计分的结构化错误按已发送的失败尝试计入 Smart 账号
健康分。已确认容量拒绝的平滑权重为 0.4,其他可计分失败与成功样本保持 0.2。
失败率使用账号级平滑与时间衰减,影响后续普通选路,已有可用账号的
会话亲和仍优先。容量不足不触发 Provider 全局熔断,也不作为账号额度耗尽;启用账号自动冻结时,
达到容量失败阈值会另外写入临时冷却。
已发送的容量失败计入[智能调度健康分](architecture.md#智能调度),可用账号的会话亲和优先。
容量不足不触发 Provider 全局熔断,也不作为账号额度耗尽;启用账号自动冻结且达到阈值时写入临时冷却。
客户端错误兼容由 API 编码出口统一处理:最终交付的 `server_is_overloaded`、`slow_down` 错误码
投影为 `server_error`,HTTP 错误状态及 WS 包装错误的数字状态投影为 `503`,让客户端执行自己的
有界重试。Provider 已确认容量不足的初始失败,即使没有这两个错误码,也返回 `503`。
@@ -436,10 +436,12 @@ SSE/WS 的 `response.failed` 保留原消息、响应 ID 与其他业务字段
继续按现有规则投影为 `response.failed`。`Retry-After` 等允许下发的响应头保留,
其他错误码不受影响。内部上游状态、错误码、原始事件及计量事实保持不变;已开始输出的请求由
客户端决定如何恢复,代理不因此重放已提交的请求。
明确额度耗尽触发账号隔离与安全换号,
包括 WebSocket 握手返回的 429;不会因其长 `Retry-After` 而转入同账号传输恢复等待。
`flex_unavailable` 是当前请求的终止拒绝,保留原始错误,不自动重试、切换传输或冷却账号
#### 额度耗尽与续写
明确额度耗尽触发账号隔离与安全换号,包括 WebSocket 握手返回的 429;不会因其长 `Retry-After` 而转入同账号传输恢复等待
OpenAI 选号阶段确认本次可选账号全部额度耗尽时,HTTP 返回 `429`,WebSocket 错误帧返回
`status: 429`,两者的 `error.type` 与 `error.code` 均为 `usage_limit_reached`,提示客户端停止
本轮自动重试,等待额度恢复或补充可用账号。空账号池、认证失效和临时容量不足不按额度耗尽处理;
@@ -1309,46 +1311,25 @@ HTTP 返回 `429`,`error.code` 为 `key_daily_budget_exceeded` 或 `key_weekly
并发修改导致版本过期时返回 `409`,不保存当前请求;读取最新设置并确认差异后再提交,不能静默重试覆盖。
成功响应返回新版本,后续请求使用发布后的快照
设置更新字段包括:
设置更新字段按用途分组如下:
```text
configRevision
providerRequestProfiles
openaiClientProfile
xaiClientProfile
requestLocationEnabled
requestLocation
modelMappings
refreshMarginSeconds
refreshConcurrency
maxConcurrentPerAccount
maxWaitingPerKey
maxWaitingPerAccount
openaiGuardianReservedConcurrency
openaiAccountAffinity
openaiSessionAffinityTtlHours
maxAccountRotations
concurrencyWaitTimeoutSeconds
responsesMaxDecompressedBodyBytes
requestIntervalMs
rotationStrategy
smartScheduling
minCodexDesktopVersion
minCodexCliVersion
usageRetentionDays
opsEventRetentionDays
auditRetentionDays
accountAutoFreezeEnabled
accountAutoFreezeThreshold
accountAutoFreezeWindowSeconds
accountAutoFreezeDurationSeconds
accountAutoFreezeProbeEnabled
accountAutoFreezeProbeModel
accountAutoFreezeAdaptiveConcurrency
accountWarmupEnabled
accountWarmupScheduleTime
accountWarmupModel
```
| 用途 | 字段 |
| --- | --- |
| 并发控制版本 | `configRevision` |
| [客户端身份](#provider-客户端身份) | `providerRequestProfiles`、`openaiClientProfile`、`xaiClientProfile` |
| [请求位置](#请求位置) | `requestLocationEnabled`、`requestLocation` |
| 模型映射 | `modelMappings` |
| 凭据刷新 | `refreshMarginSeconds`、`refreshConcurrency` |
| [并发与排队](#并发与排队) | `maxConcurrentPerAccount`、`maxWaitingPerKey`、`maxWaitingPerAccount`、`openaiGuardianReservedConcurrency`、`concurrencyWaitTimeoutSeconds`、`requestIntervalMs` |
| [亲和与换号](#账号亲和与换号) | `openaiAccountAffinity`、`openaiSessionAffinityTtlHours`、`maxAccountRotations` |
| [调度策略](#智能调度) | `rotationStrategy`、`smartScheduling` |
| [请求解压](#请求解压上限) | `responsesMaxDecompressedBodyBytes` |
| [客户端门禁](#1-鉴权与公共约定) | `minCodexDesktopVersion`、`minCodexCliVersion` |
| 历史保留 | `usageRetentionDays`、`opsEventRetentionDays`、`auditRetentionDays` |
| [账号自动冻结](#账号自动冻结) | `accountAutoFreezeEnabled`、`accountAutoFreezeThreshold`、`accountAutoFreezeWindowSeconds`、`accountAutoFreezeDurationSeconds`、`accountAutoFreezeProbeEnabled`、`accountAutoFreezeProbeModel`、`accountAutoFreezeAdaptiveConcurrency` |
| [账号预热](#账号预热) | `accountWarmupEnabled`、`accountWarmupScheduleTime`、`accountWarmupModel` |
### 账号预热
定时账号预热默认关闭。`accountWarmupScheduleTime` 使用部署时区中的 `HH:MM`,
多个时段以逗号分隔,默认 `08:00`;`accountWarmupModel` 默认 `null`,开启前必须显式选择模型。
@@ -1356,6 +1337,8 @@ accountWarmupModel
不存在的本地时刻跳过,重复时刻只执行较早一次;执行进度跨重启保留,时钟回拨不补跑已领取时刻之前的时段。
只有收到响应成功终态才记为预热成功;预热不计入客户端业务用量
### 请求位置
`requestLocationEnabled` 是必填布尔值,默认 `false`:关闭时不覆盖客户端原有位置和时区;开启时使用已保存的
`requestLocation`。关闭不会清空自定义值,代理自定义位置仍优先。
`requestLocation` 是必填的完整对象 `{ country, region, city, timezone }`,不接受 `null`;初始保存值为
@@ -1364,6 +1347,8 @@ accountWarmupModel
关联代理配置了自定义位置时优先使用代理值。保存后通过现有配置发布机制对新请求生效,
已开始请求及其重试保持同一份全局值;普通文本、绝对时间戳和数据驻留要求不受影响
### 并发与排队
`maxConcurrentPerAccount` 是默认账号并发上限,取值 0~4294967295;`0` 表示不限制。
账号的 `concurrencyLimit: null` 继承该默认值,单独设置的正数上限仍优先生效。
无限并发仍统计在途请求,并遵守最小请求间隔、账号可用性与 Client Key 限制
@@ -1384,7 +1369,9 @@ Guardian 以 `subagent_kind` 或 `client_metadata.x-openai-subagent` 值 `guardi
不受普通队列位置或单账号排队上限约束,仍受自身队列总等待容量与等待时限约束。
两类请求仍遵守账号最小请求间隔、可用性和 Client Key 限额。设置更新请求须包含该字段
`openaiAccountAffinity` 控制 OpenAI 账号亲和,默认 `strict`(严格),已有保存的模式保持不变:
### 账号亲和与换号
`openaiAccountAffinity` 控制 OpenAI 账号亲和,默认 `strict`(严格):
- `relaxed`(宽松):会话内请求直接按 `rotationStrategy` 选号,不优先主账号
- `preferred`(优先):会话内所有可关联请求优先主账号,并发已满、请求间隔未到、额度耗尽、停用、限流冷却或不支持当前模型时临时分流,保留会话绑定,后续请求仍优先主账号
@@ -1402,13 +1389,16 @@ Guardian 以 `subagent_kind` 或 `client_metadata.x-openai-subagent` 值 `guardi
提高该值允许请求尝试更多账号,但不放宽安全重放或交付后的重试限制。
设置更新请求须包含账号亲和、亲和时长与最大换号次数,保存后对新请求生效,执行中请求及其重试沿用冻结值
### 请求解压上限
`responsesMaxDecompressedBodyBytes` 是压缩 Responses HTTP 请求的解压输出上限,单位字节,默认
67108864(64 MiB)。必须为正整数,且可表示为进程平台的 `isize`;管理端以整数 MiB 编辑。
保存并发布成功后,新请求使用新值;已鉴权请求沿用原快照,无需重启。调高上限会增加大请求的内存占用,
它不代表整个进程的内存预算
`rotationStrategy` 可取 `smart`、`quota_reset_priority`、`round_robin`、`sticky`。
两个 `minCodex*Version` 字段为 `string | null`,只设置最低版本,不存在最大版本字段
### 智能调度
`rotationStrategy` 可取 `smart`、`quota_reset_priority`、`round_robin`、`sticky`
`smartScheduling` 是必填的完整对象,仅在内置 `smart` 策略下生效,切换其他策略时仍保存其值:
+45 -39
View File
@@ -82,12 +82,10 @@ flowchart TB
Store 按端口命令与实际提交结果选择操作、补齐稳定实体标识和物理差异,在同一事务写入配置 revision 与审计记录;
适配器不另定义动作和实体分类字符串,也不为生成审计而在事务外重复读取旧值
`frontend/` 是独立的 Node 项目,自行管理依赖、pnpm 配置、锁文件和 ESLint;仓库根目录不建立前端 workspace。
管理端与独立示例仓库分别依赖 `@codex-proxy/ui` 的固定 GitHub 标签或提交,通过锁文件固定实际提交并校验源码归档完整性,不要求同级源码目录。安装与构建许可见 [开发环境](development.md#环境与依赖)。
`modules/ui` 与 `modules/plugins` 是可选的 Git 子模块,分别指向两个独立仓库;不加入宿主的 Cargo 或 pnpm workspace,不参与宿主发行构建。源码联调和版本指针维护见 [开发指南](development.md)。
UI 包只公开组件、主题与样式入口;Pinia 持久化、登录、路由和管理请求仍由各自应用持有。
插件页面把所需 UI、Vue 和 Tailwind CSS 4 样式编译进包内静态资源,通过隔离页面与受限消息桥使用自己的管理接口,
不在运行时借用宿主 Vue 实例或内部模块。组件与主题扩展方式见 [管理端主题](theme.md)
`frontend/` 独立管理 Node 依赖、锁文件和 ESLint,根目录不建立前端 workspace。
`modules/ui` 与 `modules/plugins` 是两个独立仓库的可选子模块,不加入宿主 Cargo 或 pnpm workspace,也不参与宿主发行构建。
固定依赖、安装许可与源码联调见[开发指南](development.md);组件、主题和插件页面的消费边界见[主题架构](theme.md#架构概览)。
Pinia 持久化、登录、路由和业务请求由各应用持有
部署时区由 Host 配置拥有,组合根把不可变的 `DeploymentTimeZone` 传给各模块。
Core 的 `time` 只提供时区与日历边界换算;Admin/Provider 拥有查询范围与调度规则,Store 绑定显式 UTC 边界并维护事务。
@@ -128,21 +126,24 @@ flowchart LR
#### 发布与执行边界
本节说明模型扩展的执行约束;配置与发布见[制品、配置与发布](#制品配置与发布),调用链见[请求组合与设置](#请求组合与设置),资源回收见[调用资源与状态](#调用资源与状态)
- **模型目录与路由共用事实**:`model_catalog` 的直接别名保存在发布代次引用中,Core 校验原生模型、静态映射与插件 ID 冲突及目标存在性后发布。
公开列表、详情、原生目录和受管模型查询复用相同别名与账号访问范围,不另维护插件专用目录
- **重试由 Core 裁决**:`retry_policy` 只能停止或继续 Core 已允许的恢复路径;绑定按确定顺序委托,故障回退宿主。
发送、交付、续接、外部副作用、预算和取消限制不进入插件控制面,策略返回后继续复核
- **转换声明不替代正文事实**:middleware v3 / v4 在 request 阶段声明具体承担的功能和额外上游需求,逐层继承尚未承担的需求,实际正文与有效需求共同参与选路。
- **转换声明不替代正文事实**:中间件在 request 阶段声明具体承担的功能和额外上游需求,逐层继承尚未承担的需求,实际正文与有效需求共同参与选路。
attempt 阶段拒绝需求声明。没有改写的正文、header 和响应帧在相同网关边界保留原字节与顺序
#### 制品、配置与发布
- **安装意味着完整信任**:只读校验不持久化;上传、URL 和 GitHub 的确认安装记录对精确摘要的信任,
官方发行导入仍须管理员确认。若插件尚无实例,Admin 以稳定 ID 合并 schema 默认值与适用贡献项 binding:
配置完整则启用,缺少必填项则停用待补;已有实例时不复制配置或切换版本
- **持久化元数据允许字段演进**:Admin 定义已校验制品的读取投影,Store 忽略未知字段;可选字段缺失时沿用清单的缺省语义,身份、摘要和执行所需事实仍为必填,已知字段的类型错误仍报错。
读取和重复安装不重写已有 JSON,废弃字段通过迁移清理;新增必填字段或改变语义时须提供迁移。包清单与 RPC 封装仍按 SDK 校验,能力版本及宿主范围用于兼容性提示,实际调用结果决定对应实例是否可运行
- **兼容转换停留在插件边界**:Runtime 的 `compatibility` 模块集中投影旧字段、接收旧设置并保留来源,Core 与持久化模型只表达当前业务事实。
弃用清单随宿主发布,Admin 只传递提示,不持久化倒计时或根据实际字段使用猜测兼容性。
`release/publish` 在首次正式发布时登记起点,随后每个正式版本递减一次;窗口满后阻止继续发布旧合同,要求移除对应适配及宿主支持声明。预发行不递减,同标签发布恢复不重复计数
- **持久化投影与包校验分离**:Admin 定义已校验制品的读取投影,Store 忽略未知字段,可选字段缺失时使用清单默认值;身份、摘要和执行所需事实必填,已知字段类型错误须报错。
读取和重复安装不重写 JSON,结构与语义变更由数据库迁移管理。包清单和 RPC 封装按 SDK 校验;能力版本及宿主范围用于兼容性提示,实例能否运行由实际调用结果确定
- **兼容转换停留在插件边界**:Runtime 的 `compatibility` 模块集中适配接口字段与设置来源,Core 与持久化模型只表达当前业务事实。
Admin 透传发行物中的弃用提示,不持久化倒计时或根据实际字段使用猜测兼容性;`release/publish` 维护[兼容窗口](../backend/crates/gateway-plugin/sdk/docs/manifest.md#接口弃用),到期时要求移除适配和支持声明
- **一份发布快照**:数据面和管理页面使用同一不可变扩展集合;CLI 复用准备与调用合同,按次读取持久化配置。
新配置准备成功后才提交发布,在途请求持有旧集合直到结束;管理目标和回调另按当前身份与版本复验
- **发布能力显式实现**:Admin 分别定义准备、运行诊断和状态生命周期端口,完整发布实现必须满足全部合同。
@@ -155,6 +156,9 @@ flowchart LR
执行版本校验、事务和审计。Provider 由组合根静态注册为 OpenAI 与 xAI;插件停用不删除账号或历史记录,失败的观察回调不改变响应和账单
- **能力开放不承接插件业务**:宿主向插件提供独立、可组合的原生能力,负责类型校验、资源生命周期、事务与审计。
插件自身的触发条件、跨资源业务关联、策略和流程编排归插件所有;宿主不能因某个插件的使用场景代为实现这些流程
#### 请求组合与设置
- **数据加工走中间件**:Core 的类型化 `compose` 统一维护顺序与单次续体;Runtime 发布时按挂载边界编译有序候选,调用时仅在当前候选中匹配作用域、捕获上下文并拼接续体,协议适配器负责单层 RPC 与资源投影。
接入按 HTTP、WebSocket 消息、逻辑请求、上游尝试和主动服务调用的生命周期边界组织。API 在路由匹配前组合所有公开 HTTP 接口;
`http` 可改写路径、方法、完整 headers、惰性上传与响应流,也可直接返回新路由结果或接管自定义 WebSocket 升级。
@@ -169,7 +173,7 @@ flowchart LR
子调用持有父子关联、冻结计划和取消信号,跳过祖先发起实例;模型 request 与 attempt 延续该集合。内部管理请求使用宿主签发的插件身份,审计记录插件与调用来源,普通客户端不能用 headers 声明此身份。
正文转交延续真实交付寿命,EOF、显式关闭或父级取消释放资源;组合根保活 Dispatcher,Runtime 只保存 Weak,不增加逐接口注册清单
- **主动服务调用在注册表统一组合**:Admin 按类型登记操作,Core 组合器只负责续体,Runtime 搬运跨进程调用。
插件与 CLI 主动调用设置服务的 11 个操作时进入 `service` 中间件一次;原生 HTTP 接口由总入口包裹,业务方法及其内部调用不重复组合。
插件与 CLI 主动调用公开服务时进入 `service` 中间件一次;原生 HTTP 接口由总入口包裹,业务方法及其内部调用不重复组合。
主动子调用继承冻结计划、父子关联和取消信号,无 service 绑定时也保留空计划快照;调用结束回收资源。
各入口复用原用例,运行设置的整体替换仍使用读取版本做事务内比较,冲突不写入。公开范围见 [SDK 服务合同](../backend/crates/gateway-plugin/sdk/docs/capabilities.md#公开服务)
- **上游适配器是洋葱链终端**:`upstream_adapter` 只扩展内置 OpenAI / xAI,沿原 Provider 的选号、租约及 attempt 中间件进入冷流。
@@ -186,6 +190,9 @@ flowchart LR
认证、插件使用的 `ExecutionSettings` 视图和执行终端共用该规则。设置改写经过编译后由终端复用结果,截止时间仍相对请求开始时刻计算,不发布全局配置。
子调用保留显式改写,在目标 Key 作用域重算默认值;Key 限额、Fast 和模型超时的覆盖绑定原 Key,同级调用互不改写。
原生 Responses WebSocket 每轮从当前快照重新认证,保留握手的显式改写;已经开始的执行不随全局发布改变。具体字段与来源合同见 [SDK 中间件](../backend/crates/gateway-plugin/sdk/docs/capabilities.md#http-入口)
#### 调用资源与状态
- **资源跟随调用**:账号、HTTP、模型、状态和日志回调绑定有效父调用,取消随调用传播。受管流建立后不沿用握手的绝对期限,网络操作与模型执行各自计时。
显式 Key 的身份快照随调用保留,每个模型执行拥有独立子调用图与结算;省略 Key 时继承现有模型父请求。页面 Responses 桥复核精确实例目标和 revision
`frontend_authentication` 使用 principal 到 Key 的映射;公开登录回调的一次性票据用于关联流程,不限制插件回调能力。接口合同见 [SDK 回调](../backend/crates/gateway-plugin/sdk/docs/capabilities.md#宿主资源回调)
@@ -206,8 +213,7 @@ flowchart LR
进程、RPC、下载、缓存与回调均有容量和期限限制;Host 提供受管资源,Runtime 负责协议、调用关联与资源生命周期。
下载来源、凭据与代理按目标匹配,网络操作不占用数据库事务,提交前重新校验来源与代理版本
管理页面使用无同源权限的 sandbox iframe,通过固定目标的宿主桥访问已声明路由;
UI、Vue 与样式随插件打包,不读取宿主内部模块。
管理页面使用无同源权限的 sandbox iframe,通过固定目标的宿主桥访问已声明路由。
官方插件身份仅来自受信宿主发行物内的封口清单,普通安装不能指定 `builtin`;封口标记不是密码学签名。
宿主更新与回滚校验发行身份,重启前展示启用插件的兼容风险,确认不改变插件启用配置或制品接受事实
@@ -353,22 +359,8 @@ sequenceDiagram
Client Key 并发占用、账号调度槽位与请求恢复记录使用可续期租约,不把租约 TTL 当作总执行预算。
会话结束或 Drop 后停止续期;Redis 并发租约丢失或无法在有效期内续期时取消请求,避免失去并发约束后继续执行
智能调度配置由 Core 定义和校验,Store 随运行设置及配置 revision 原子保存,快照编译器将其纳入
`AccountSelectionPolicy`。内置选号与诊断复用同一评分函数,近似最优容差随参与立即选号的五项系数总和缩放。
额度重置复用 Provider 的有效重置时间投影,未知或已过期时间不加分。排队系数不影响立即选号的分数和容差,
只在选择账号等待队列时结合其他评分与当前进程内的实时等待人数。评分规则由 Core 账号模块统一拥有,
`ConcurrencyWaitQueue` 在同一锁内读取队长并入队;Provider 只传递已限定续写范围的候选。
排队系数为零时保留最短队列规则;已入队位置、队内 FIFO、取消回收、容量上限和请求共享等待预算保持原合同。
高权重回切仅影响智能调度的软亲和:先验证候选资格,再决定是否由更高权重层覆盖亲和;同层亲和保留。
OpenAI 优先模式沿用可用的会话主账号,高权重回切仅参与需要重新选号的请求。
Provider 限定的原生续写账号范围仍是硬约束。回切在请求选号时发生,不新增后台任务或主动历史重放。
插件显式选号沿用既有候选校验和实际亲和结果,委托内置选号时才应用智能调度配置
Client Key 鉴权完成后,API adapter 从有界请求头识别 Codex Desktop/CLI,Core 使用同一请求冻结的
`RuntimeSnapshot` 比较对应最低版本。Desktop 优先于其 User-Agent 内嵌的 CLI/Core 标记;未知客户端不
应用门禁。API 识别手机远程 UA 后缀;这类 Desktop 请求缺失应用版本时不应用门禁,不以 Core 或远程
客户端版本代替应用版本。其余低版本或已识别但版本不可用时,在进入 Provider 前返回稳定的 `426` 合同,
具体请求头规则见 [API 鉴权与公共约定](api.md#1-鉴权与公共约定)
Client Key 鉴权完成后,API adapter 从有界请求头识别客户端,Core 使用请求冻结的 `RuntimeSnapshot`
检查最低版本,在进入 Provider 前完成门禁。请求头优先级、远程客户端识别与 `426` 错误合同见[鉴权约定](api.md#1-鉴权与公共约定)
核心不变量:
@@ -605,7 +597,24 @@ Client Key 与账号分组形成授权范围:
与凭据在同一事务中提交;Provider 仍独占凭据解析。未附带设置的导入、重新授权和后台刷新保留已有分组、
权重、并发与模型政策。模型政策变更推进配置 revision,不推进 credential revision。管理端导入和账号编辑共用设置表单,凭据输入独立于设置
Continuation 仍受原请求的 Client Key、账号范围、Provider 和发送/交付边界约束:
### 智能调度
Core 定义、校验并统一拥有评分规则,内置选号与诊断复用同一函数;Store 随运行设置和配置 revision 原子保存,
快照编译器将其纳入 `AccountSelectionPolicy`。近似最优容差随立即选号的五项系数总和缩放
- 额度重置使用 Provider 的有效重置时间,未知或已过期时间不加分
- 排队系数只在选择等待队列时结合其他评分和进程内实时等待人数,不影响立即选号分数与容差;系数为零时选最短队列。
`ConcurrencyWaitQueue` 在同一锁内读取队长并入队,保持已有位置、同级 FIFO、取消回收、容量和共享等待预算;Provider 传入候选须已限定续写范围
- 健康分按已发送尝试的结果计算账号级平滑失败率并随时间衰减;明确容量拒绝的平滑权重为 0.4,其他可计分失败和成功为 0.2
- 高权重回切只影响软亲和:先检查候选资格,再允许更高权重层覆盖亲和,同层亲和保留。
OpenAI 优先模式沿用可用主账号;原生续写账号范围是硬约束。回切只发生在重新选号时,不启动后台任务或主动历史重放
- 插件显式选号使用候选校验和实际亲和结果,只有委托内置选号才应用智能调度配置
字段、默认值与可配置范围见[智能调度设置](api.md#智能调度)
### 原生续接与会话亲和
Continuation 受原请求的 Client Key、账号范围、Provider 和发送/交付边界约束:
- native continuation 固定创建它的 Provider 与账号
- OpenAI Provider 的 OAuth 大包传输预检在发送前执行,新链可选择 HTTP;大 connection-local
@@ -777,9 +786,7 @@ PostgreSQL 周期对账才是正确性基础
`normal`、`quota_exhausted`、`rate_limited`、`disabled`、`error`。只有明确上游证据才能恢复或终态化账号,
本地时钟和不确定响应不能伪造事实
容量冻结与普通上游限流共享账号冷却投影,但恢复条件不同:需要探测的冻结在截止时间后仍阻止调度,
普通推理成功不能解除。Admin 恢复任务按冻结代次原子提交探测结果,避免旧结果覆盖手动恢复或新冻结;
自动降低并发通过现有管理事务与快照发布链路,仅按数据库最新设置更新并发上限
容量冻结与上游限流共用冷却投影,探测恢复与持久化并发调整由 Admin 负责,详见[Worker 与账号冻结](#worker-与账号冻结)
PostgreSQL schema 由迁移目录按编号管理。已应用迁移按字节冻结,后续 schema 变化只能新增编号迁移,
详见 [迁移规则](../backend/migrations/README.md)
@@ -909,8 +916,8 @@ Worker 由各 Bundle 贡献、由 Host 统一监督:
账号级 Redis 容量冷却,调度立即跳过该账号;管理端沿用限流状态,通过原因区分容量冻结与上游限流。
冻结与计数由 Redis 保存,不改变 PostgreSQL 账号状态。需要探测的冻结在到期后仍阻止调度,恢复
worker 复用连接测试探针执行真实上游调用,并按冻结代次处理结果:成功清除,失败按配置时长顺延,
过期探测结果不得覆盖更新的冻结或管理员操作。关闭自动冻结或探测后,已有冻结在冷却到期后恢复。
自适应并发下调按观测峰值的 80%(下限 2)原子更新最新有效账号上限,只降不升,不覆盖其他账号
过期探测结果不得覆盖更新的冻结或管理员操作,普通推理成功不能解除需要探测的冻结。关闭自动冻结或探测后,已有冻结在冷却到期后恢复。
自适应并发下调通过管理事务与快照发布,按观测峰值的 80%(下限 2)原子更新最新有效账号上限,只降不升,不覆盖其他账号
设置,审计标注为系统变更;该上限持久保存,恢复调度后不自动调高
周期任务的 Redis lease 只保证单周期互斥,不构成多副本 leader 选举。备份 daemon 依赖单副本部署边界,
@@ -1016,5 +1023,4 @@ RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --
独立插件包的构建、安装与功能验证说明位于 `codex-proxy-plugins` 仓库的 `examples/workbench/README.md`。
功能测试与性能、隔离和平台实测分别记录,不相互替代
改动使现有说明失真或缺少必要信息时,修订所属文档:用户入口写入根 README,HTTP 合同写入 `docs/api.md`,
部署操作写入 `deploy/README.md`,架构不变量保留在本文
文档更新条件、内容归属与行文要求见[文档职责](../CONTRIBUTING.md#文档职责与更新条件)
+10 -15
View File
@@ -14,9 +14,7 @@ flowchart LR
D -->|缺少必填项| F[补充配置后启用]
```
安装包必须满足宿主支持的 [SDK 合同](../backend/crates/gateway-plugin/sdk/README.md)、目标平台和宿主版本范围
合同不匹配时,需由作者使用匹配的 SDK 与 CLI 重新构建打包,不能只修改包内版本字段
安装包须通过结构、目标平台和进程协议校验;宿主版本范围或能力版本不匹配时提示兼容风险,处理方式见[故障处理](#故障处理)
## 安装
@@ -43,9 +41,8 @@ flowchart LR
### 完整信任
插件以网关的系统身份运行,能够访问数据、凭据、配置和网络。安装即信任插件的代码与行为,安全由安装者承担;宿主不提供插件安全沙箱,也不通过权限清单承诺隔离
包格式、摘要和兼容性校验用于确认可加载性,不证明插件安全。只安装可信来源,并在升级前确认目标版本
插件以网关的系统身份访问数据、凭据、配置和网络,宿主不提供安全沙箱或权限清单隔离。
安装即信任全部代码与行为,安全由安装者承担;包格式、摘要和兼容性校验不证明安全。只安装可信来源,升级前确认目标版本
## 配置与启用
@@ -99,16 +96,14 @@ flowchart LR
### 版本与设置
- **检查更新**:GitHub 按保存的策略查询发布,URL 下载并解析当前地址的包;本地上传不支持查询。检查本身不会安装,GitHub 标签也不等于包内版本
- **更新插件**:选择新包并确认安装,再确认切换到目标版本。保留已填写的值,补充新版本缺失的默认值;字段改名、类型改变或删除等不兼容情况由用户调整,不猜测或静默清空参数与密钥
- **更新插件**:安装新包,再按下述切换规则确认目标版本;字段改名、类型改变或删除等不兼容情况由用户调整,不猜测或静默清空参数与密钥
- **切换版本**:在版本列表中选择已安装版本,确认当前版本与目标版本后执行。若该版本已有恢复设置,使用它上次启用时提交的参数、密钥与功能范围;否则从当前设置生成草稿。切换保持启停状态,目标制品必须已接受。停用的历史配置引用旧版本时显示“历史配置”,不表示该版本正在运行
- **回退版本**:只列出同一插件已安装、已接受且保留恢复设置的较早版本,恢复对应设置后重新执行兼容检查
每个精确插件包保留最近一次启用时提交的配置快照,停用期间未完成的草稿不会覆盖恢复点
没有目标版本快照时,草稿保留当前显式值并补充缺失的 schema 默认值。停用草稿不覆盖最近启用时的恢复点;
未保存或已丢失的设置无法通过快照恢复
宿主升级仅能为当前启用配置建立恢复点,无法还原未保存或已丢失的其他版本设置
宿主元数据的可选字段增减不要求重新安装插件;清单、协议、能力或私有状态不兼容仍须处理。
宿主升级前的检查与恢复要求见[插件兼容与发行目录](../deploy/README.md#插件兼容与发行目录)
宿主更新的检查与恢复要求见[插件兼容与发行目录](../deploy/README.md#插件兼容与发行目录)
修改下载来源会先单独保存,取消后续安装也会保留已保存来源。当前浏览器标签页
记住未提交的安装参数和凭据引用,不保存认证明文或上传文件
@@ -116,12 +111,12 @@ flowchart LR
设置恢复不等于私有数据恢复。私有数据迁移仍由插件声明的迁移能力处理,不兼容且无迁移路径时不能切换;迁移失败会尝试
恢复原版本,若恢复会覆盖并发修改则保持停用并提示处理。配置检查或候选准备失败不会覆盖当前设置
旧接口在兼容窗口内显示“接口待升级,当前仍兼容”,可查看剩余正式版本数与迁移说明,不改变插件的启停状态。请更新插件或联系作者升级接口,规则见 [接口弃用](../backend/crates/gateway-plugin/sdk/docs/manifest.md#接口弃用)
接口弃用提示展示兼容窗口与作者说明,不改变插件启停状态;规则见 [接口弃用](../backend/crates/gateway-plugin/sdk/docs/manifest.md#接口弃用)
### 故障处理
宿主版本范围或能力版本不匹配时,管理页持续显示黄色兼容性提醒,仍允许手动启动和随宿主自动启动。
启动成功不代表语义兼容,警告不会自动消失。系统更新和回滚后的重启检查列出风险,确认后保留启用配置并逐个尝试启动
宿主版本范围或能力版本不匹配时,管理页持续显示黄色兼容性提醒,允许手动启动和随宿主自动启动。
启动成功不代表语义兼容,警告不会自动消失。需由作者使用匹配的 [SDK](../backend/crates/gateway-plugin/sdk/README.md) 与 CLI 重新构建,不能只修改包内版本字段;宿主更新和回滚的重启检查见[部署说明](../deploy/README.md#插件兼容与发行目录)
包无法解析、平台不符或 RPC 握手失败时,只有对应插件无法启动。运行中进程崩溃、协议错误或无效响应会停止该实例,
显示红色感叹号及脱敏错误详情,保留启用配置,可在详情中重新启动或切换版本。连续故障受重启次数限制
+5 -6
View File
@@ -94,7 +94,7 @@ flowchart LR
| 状态 | [`stores/modules/theme.ts`](../frontend/src/stores/modules/theme.ts) | 持久化配置、系统明暗偏好、切换动作与动画 |
| 编辑器状态 | [`useThemeEditor.ts`](../frontend/src/views/theme/composables/useThemeEditor.ts) | 草稿、修改计数、恢复与保存 |
独立仓库 `codex-proxy-ui` 是管理端和官方插件页面的共享组件源码;组件不得反向依赖管理端 Store、路由或 API。管理端通过 `@codex-proxy/ui` 公开入口消费组件,插件页面把组件和样式编译进自身静态资源,不在运行时借用宿主模块。
`codex-proxy-ui` 的组件与主题不依赖宿主 Store、路由或 API。应用通过上述公开入口消费;插件页面把 UI、Vue 和 Tailwind CSS 4 样式编译进包内静态资源,不借用宿主运行时模块。
插件页面的标题与副标题由宿主呈现,内容区只渲染业务;主题变化通过宿主桥同步,接入方式见
[SDK 页面与宿主桥](../backend/crates/gateway-plugin/sdk/docs/capabilities.md#页面宿主桥-v2)。
`theme/` 根目录只保留公开入口 `index.ts` 和唯一类型文件 `types.ts`;内部实现按 `core/`、`derive/`、`runtime/` 分层,不增加嵌套 barrel。
@@ -242,8 +242,7 @@ Token 直接覆盖时不会自动重算同组件的其他状态;需要保持
明暗模式分别派生,但共用组件合同。表格背景过渡覆盖普通行、选中行和固定列,尊重减少动态效果偏好
Theme Editor 只开放真正由对应组件消费的 Component Token。全局 Alias 不放进组件目录,避免一次覆盖同时改变
多个无关组件
可编辑字段与覆盖限制见[主题编辑器](#编辑能力)
应用内品牌图标使用 [`AppBrandMark.vue`](../frontend/src/components/AppBrandMark.vue):浅色模式取
`colorBgSpotlight`,深色模式取 `colorBgElevated`,保持中性暗面与白色图形;浏览器 favicon 继续使用固定黑白
@@ -330,7 +329,7 @@ Theme Store 统一读取持久化配置并初始化主题,首个 Vue 组件渲
| 组件 | Action、Form、Surface、Data Display、Navigation、Layout |
| 工作流 | 搜索、单项恢复、撤销草稿、恢复默认、保存并应用 |
Component Token 只开放白名单字段,且不允许在组件目录覆盖全局 Alias。未覆盖项使用全局 Seed 与
Component Token 只开放对应组件实际消费的白名单字段,不允许在组件目录覆盖全局 Alias。未覆盖项使用全局 Seed 与
Alias 算法,避免主题配置逐渐退化成一份无法维护的完整 CSS 快照。
Input、Button Secondary 与 Icon Button Secondary 的三个背景 Token 支持 HEX Alpha 编辑和保存;Seed、容器与表格背景仍只接受实色。
透明填充先合成再参与默认文字对比度计算,用户显式覆盖 Component Token 时仍由用户负责整组状态的搭配
@@ -364,9 +363,9 @@ Store 在其他入口发生变化时更新保存基线;没有未保存修改
- 全局 Token:`--cp-color-bg-container`、`--cp-font-size`
- Component Token:`--cp-table-row-hover-bg`、`--cp-input-active-shadow`
- Preset Color Token:`--cp-color-purple-container-strong`、`--cp-color-cyan-on-container`
- 主题层禁止业务域命名;套餐、模型或页面只能消费通用 Alias、Preset 或 Component Token
- 业务组件按[通用颜色消费](#通用颜色消费)选择角色,不在主题层声明业务域名称
- Map 与 Component 字段由 `theme/core/tokens.ts` 统一生成 CSS Token;禁止在解析器中再写平行的逐项映射表
- 禁止继续引入 `accent`、`soft`、`current`、`subtle` 等与现有角色重叠的平行词汇
- 不使用 `accent`、`soft`、`current`、`subtle` 等与现有角色重叠的平行词汇
### Vue 与 Tailwind CSS 4