diff --git a/README.md b/README.md index 36b89412e..b48e16350 100644 --- a/README.md +++ b/README.md @@ -5,39 +5,34 @@ # Codex Proxy RS -**基于 Rust 的多 Provider 透明 AI 网关** +集中管理 OpenAI、xAI 账号,为 Codex 和兼容客户端提供统一的访问地址与密钥。 [![CI](https://github.com/zyycn/codex-proxy-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/zyycn/codex-proxy-rs/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/zyycn/codex-proxy-rs?display_name=tag&sort=semver&style=flat-square)](https://github.com/zyycn/codex-proxy-rs/releases) [![GHCR](https://img.shields.io/badge/GHCR-codex--proxy--rs-2496ED?logo=docker&logoColor=white&style=flat-square)](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs) [![MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/license/mit) -[快速开始](#快速开始) · [客户端接入](#客户端接入) · [运行维护](#运行维护) · [部署](deploy/README.md) · [接口](docs/api.md) · [架构](docs/architecture.md) +[快速开始](#快速开始) · [客户端接入](#客户端接入) · [常见问题](#常见问题) · [部署说明](deploy/README.md) -> [!NOTE] -> 数据面支持 OpenAI Responses、Images 和模型目录协议,不提供 `/v1/chat/completions`。 - ## 功能 -| 领域 | 当前能力 | -| --- | --- | -| 数据面 | Responses JSON、SSE、WebSocket(含 Codex 子代理语义)、Images 生成/编辑与模型目录 | -| Provider | 固定 `openai`、`xai` 两个 Provider,各自拥有 OAuth、credential、额度、目录与 transport | -| 路由 | Client Key 限定账号分组;按模型能力编译 Provider 候选;支持全局精确模型映射、会话亲和和安全 fallback | -| 透明边界 | OpenAI 保留未知请求字段及字段顺序,并原样转发 SSE、WebSocket 与 Images 业务字节;xAI 在 Provider 内转换协议 | -| 账号 | OAuth、OpenAI AT/RT 与账号文件导入,credential/额度刷新、账号恢复及主动额度重置卡 | -| 管理 | 账号与分组、Client Key、运行设置、用量/错误观测、S3/R2 备份、系统更新与回滚 | -| 计量 | Token、图片、费用、延迟、账号与 Provider 归因,并记录上游响应确认的实际 `service_tier` | +- 在网页中添加账号,查看额度、请求记录和费用,刷新或重新授权过期账号。 +- 用账号分组控制不同密钥可使用的账号,设置并发和请求频率。 +- 接入 Codex CLI、桌面端和支持 Responses API 的客户端。 +- 支持流式对话、WebSocket、图片生成与编辑、Codex 独立搜索。 +- 支持 S3/R2 数据库备份、同大版本在线更新和回滚。 -架构边界、状态所有权和请求不变量见 [架构文档](docs/architecture.md)。 +支持 OpenAI、xAI 账号,但图片与独立搜索目前仅通过 OpenAI 提供。模型和功能是否可用取决于上游账号权限。 +**不支持 `/v1/chat/completions`**;仅支持该接口的客户端不能直接接入。 ## 快速开始 -需要 Docker Engine 与 Docker Compose Plugin。 +以下命令适用于 Linux,需要 Git、Docker Engine 和 Docker Compose Plugin。已有部署请先看 +[升级说明](deploy/README.md#镜像升级与源码构建),不要覆盖原配置。 -### 1. 准备配置和目录 +### 1. 下载并配置 ```bash git clone https://github.com/zyycn/codex-proxy-rs.git @@ -48,34 +43,28 @@ install -d -m 0750 .runtime/postgres .runtime/redis cp deploy/config.example.yaml deploy/config.yaml sudo chown "$(id -u):10001" deploy/config.yaml chmod 0640 deploy/config.yaml -``` - -分别生成 PostgreSQL 与 Redis 密码: - -```bash -openssl rand -hex 24 -openssl rand -hex 24 -``` - -把两个结果分别写入 `deploy/config.yaml`,再设置管理员初始密码: - -| 配置 | 约束 | -| --- | --- | -| `store.database.password` | 48 位十六进制 | -| `store.redis.password` | 48 位十六进制 | -| `admin.default_password` | 至少 12 位,不能包含 `$` | - -PostgreSQL、Redis 和应用从同一份 `config.yaml` 读取基础设施密码,不需要额外导出环境变量。 -管理员密码只在首次创建管理员时生效。 - -Linux 需要允许容器组写入运行目录: - -```bash sudo chown -R "$(id -u):10001" .runtime/data .runtime/logs chmod 0770 .runtime/data .runtime/logs ``` -### 2. 启动 +分别生成数据库和 Redis 密码: + +```bash +openssl rand -hex 24 +openssl rand -hex 24 +``` + +编辑 `deploy/config.yaml`,填好以下三项: + +| 配置项 | 填写内容 | +| --- | --- | +| `store.database.password` | 第一个生成的 48 位十六进制密码 | +| `store.redis.password` | 第二个生成的 48 位十六进制密码 | +| `admin.default_password` | 管理员初始密码,至少 12 位,不能包含 `$` | + +不需要创建 `.env`。管理员初始密码只在首次创建账号时使用。 + +### 2. 启动服务 ```bash docker compose -f deploy/compose.yaml config --quiet @@ -84,145 +73,87 @@ docker compose -f deploy/compose.yaml up -d --no-build curl -i http://127.0.0.1:8080/healthz ``` -`204 No Content` 表示应用、PostgreSQL 与 Redis 均可用。管理端地址为 -`http://127.0.0.1:8080`。 +健康检查返回 `204 No Content` 后,打开 `http://127.0.0.1:8080`, +使用 `admin@cpr.local` 和刚设置的管理员密码登录。 -### 3. 初始化网关 +默认地址只能在服务器本机访问。从其他设备使用时,需要配置 +[HTTPS 反向代理](deploy/README.md#公网访问)。 -1. 使用 `admin@cpr.local` 与初始密码登录。 -2. 导入或授权 Provider 账号:OpenAI 支持 OAuth、逐行 AT/RT 与 OAuth JSON; - xAI 支持 OAuth 和账号 JSON。 -3. 创建账号分组并按需配置全局模型映射。未命中的模型名原样交给候选 Provider。 -4. 创建 `sk_...` Client Key,选择可用账号分组,并设置速率和并发限制;空分组范围表示全部账号。 +### 3. 添加账号和密钥 -OpenAI RT 导入会先向官方 token endpoint 换取 AT;仅 AT 的账号不具备自动续期能力, -能否参与调度仍取决于 token 中可验证的身份与后续上游事实。账号导入和首次 OAuth 创建会尽力读取一次额度, -额度上游暂时失败不会回滚已提交的账号。 +1. 在「账号」中添加 OpenAI 或 xAI 账号。OpenAI 支持 OAuth 授权、AT/RT 和账号 JSON;xAI 支持 OAuth 授权和账号 JSON。 +2. 按需创建账号分组,并把账号加入分组。 +3. 创建客户端密钥,选择可用分组。**不选分组表示可使用全部账号**。 +4. 点击该密钥的「使用密钥」,复制客户端配置。 -OpenAI 账号的主动重置卡只在用户打开相应弹窗或手工刷新时查询。消费成功后管理端重新查询卡片与额度; -系统不会通过修改本地 `reset_at` 伪造重置。完整账号合同见 [账号接口](docs/api.md#5-账号)。 - -> [!IMPORTANT] -> xAI 使用 OAuth session,不支持把 xAI API Key 作为上游 credential。 - -完整的权限、密码轮换、备份和升级规则见 [部署文档](deploy/README.md)。 +只导入 AT 的账号不能自动续期。xAI 暂不支持用 API Key 导入上游账号。 ## 客户端接入 +### Codex CLI 和桌面端 + +在「使用密钥」中按系统复制 `config.toml` 和 `auth.json`,也可以通过 CCSwitch 导入。 +已有文件先备份,再合并配置并重启 Codex。客户端使用代理密钥,OpenAI 账号登录状态由服务端管理。 + +新配置已启用原生生图。可以直接提出需求,例如: + +> 帮我做一个咖啡店首页,生成一张咖啡主题配图并用到页面中。 + +是否调用生图由 Codex 根据任务决定,也可以明确要求生成图片。需要支持生图的客户端、模型和 OpenAI 账号; +普通对话可用不代表账号一定有生图额度。 + +完整配置和旧版配置迁移见 [客户端配置](deploy/README.md#客户端配置)。 + +### 其他客户端 + +选择支持 **Responses API** 的客户端,填写: + | 配置 | 值 | | --- | --- | -| Base URL | `http://127.0.0.1:8080/v1` | -| API Key | 管理端创建的 `sk_...` | -| 鉴权 | `Authorization: Bearer ` | +| Base URL | `http://127.0.0.1:8080/v1`,远程使用时换成服务器的 HTTPS 地址 | +| API Key | 管理端创建的 `sk_...` 密钥 | + +可先检查密钥是否能读取模型列表: ```bash curl http://127.0.0.1:8080/v1/models \ -H 'Authorization: Bearer ' - -curl http://127.0.0.1:8080/v1/responses \ - -H 'Authorization: Bearer ' \ - -H 'Content-Type: application/json' \ - -d '{ - "model": "", - "input": "Reply with pong.", - "stream": false - }' ``` -
-公开数据面路由 +接口列表与请求示例见 [接口文档](docs/api.md)。 -| 路由 | 用途 | -| --- | --- | -| `POST /v1/responses` | JSON 响应或 SSE Responses 流 | -| `GET /v1/responses` | Responses WebSocket 升级 | -| `POST /v1/images/generations` | OpenAI Images 生成 JSON 透明转发 | -| `POST /v1/images/edits` | OpenAI Images 编辑 JSON 透明转发 | -| `GET /v1/models` | 当前 Client Key 账号范围内的模型列表 | -| `GET /v1/models/{model_id}` | OpenAI 兼容模型详情 | +## 常见问题 -所有 `/v1/*` 路由都需要 Client Key。Images 是 OpenAI Provider 自有端点,不读取模型字段,也不参与 -文本模型映射。 +### 仍然要求登录,或无法生成图片 -
+重新从管理端复制配置,确认修改的是客户端实际读取的用户配置目录,然后完全退出并重启客户端。 +保留仅含代理密钥的 `auth.json` 不会使新配置失效。详细检查步骤见 +[登录与生图排查](deploy/README.md#登录与生图排查)。 -完整的客户端与管理端路由、鉴权、请求字段和 mutation 语义见 [接口文档](docs/api.md)。 +### 没有可用账号或模型 -### Codex 原生生图 +检查账号是否启用、凭据是否过期、额度是否耗尽,以及密钥绑定的分组是否包含可用账号。 +「恢复账号」只清除本地错误状态,不能恢复上游权限或增加额度。 -在管理端的「使用密钥」中复制配置,或通过 CCSwitch 导入,保存后重启 Codex。 -新配置启用 `features.image_generation = true`,使用以下 Provider 认证方式: +### 如何更新或查看日志 -```toml -[model_providers.OpenAI] -name = "OpenAI" -base_url = "http://127.0.0.1:8080/v1" -wire_api = "responses" -requires_openai_auth = false -# 使用管理端创建的代理密钥,不是 ChatGPT 账号的访问令牌。 -experimental_bearer_token = "" - -[model_providers.OpenAI.http_headers] -# 声明账号认证由服务端托管;此标记本身没有鉴权能力。 -X-OpenAI-Actor-Authorization = "proxy-managed" - -[features] -image_generation = true -``` - -合并到已有配置时,请修改当前 `model_provider` 对应的 Provider 段,不要重复添加同名 TOML 表, -并保留其他个性化设置。配置文件包含代理密钥,请勿公开分享。 -无需更改本地 `auth.json`;真实 ChatGPT 登录状态仍由服务端管理。网关始终校验 Bearer Client Key, -并过滤客户端的 Actor 认证标记,上游身份只来自调度选中的服务端账号。 - -此配置使用官方客户端的服务端托管认证能力,不修改或伪造客户端登录状态。 -需要支持该能力的新版 Codex、支持图片输入及命名空间工具的对话模型,以及具备生图权限的 OpenAI 账号。 -模型会在需要图片时调用原生 `image_gen.imagegen`,图片请求通过本代理的 Images 路由完成。 -原来的 `auth.json` + `requires_openai_auth = true` 配置仍可用于已有请求,但不会因此自动获得原生生图能力。 - -## 运行维护 +同一大版本内拉取发布镜像并重建应用容器: ```bash -# 同一大版本内升级发布镜像 docker compose -f deploy/compose.yaml pull codex-proxy-rs -docker compose -f deploy/compose.yaml up -d --no-build - -# 查看日志 -docker compose -f deploy/compose.yaml logs -f codex-proxy-rs - -# 从当前源码构建 -docker compose -f deploy/compose.yaml build codex-proxy-rs -docker compose -f deploy/compose.yaml up -d +docker compose -f deploy/compose.yaml up -d --no-build codex-proxy-rs +docker compose -f deploy/compose.yaml logs --tail=100 -f codex-proxy-rs ``` -> [!IMPORTANT] -> `.runtime/` 保存 PostgreSQL、Redis、日志、OpenAI 会话锚点和在线更新状态。删除该目录会永久清除 -> 本地运行状态。 - > [!WARNING] -> 不支持跨大版本在线升级。升级到新的大版本时请使用全新的 `.runtime/` 数据目录,并重新导入或授权 -> Provider 账号与 Client Key。 - -Compose 默认只绑定 `127.0.0.1`。公网接入应使用 HTTPS 反向代理,转发 WebSocket upgrade 与真实客户端 -IP;不要暴露 PostgreSQL 或 Redis。当前运行拓扑为单副本,不能通过复制应用容器扩容。 - -## 维护与发布 - -仓库版本只能从干净、已同步上游的发布分支发布: - -```bash -release/publish -``` - -脚本依赖已登录的 GitHub CLI(`gh auth login`),会更新 `release/version.yaml`、创建约定提交和带注释的 -`v` tag,原子推送分支与 tag,再触发 Release 构建。源码提交、仓库发版和运行实例升级是三个 -独立状态;脚本不会登录服务器或改变任何运行实例。 +> 升级前先备份。跨大版本需使用新的数据目录重新部署,不支持直接沿用旧库在线升级。 +> `.runtime/` 包含数据库和运行数据,请勿删除;账号文件、密钥和日志也不要公开分享。 ## 文档 -- [部署与运维](deploy/README.md) -- [客户端与管理端接口](docs/api.md) +- [客户端接入与生图](deploy/README.md#客户端配置) +- [部署、备份与恢复](deploy/README.md) +- [API 参考](docs/api.md) - [系统架构](docs/architecture.md) -- [数据库迁移规则](backend/migrations/README.md) -- [Release](https://github.com/zyycn/codex-proxy-rs/releases) -- [容器镜像](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs) +- [管理端主题](docs/theme.md) +- [数据库迁移](backend/migrations/README.md) diff --git a/backend/migrations/README.md b/backend/migrations/README.md index c55e6ab40..12d7bc4e9 100644 --- a/backend/migrations/README.md +++ b/backend/migrations/README.md @@ -1,8 +1,10 @@ # 数据库迁移 -sqlx 在服务启动时(`serve` 监听之前)按编号顺序执行本目录的迁移,并把每个 -文件的 checksum 记入 `_sqlx_migrations`。此后每次启动都会重新校验:**已应用 -迁移的文件字节与记录不一致时,服务直接拒绝启动**。 +sqlx 在服务启动、监听请求之前按编号执行迁移,并把文件 checksum 记入 `_sqlx_migrations`。 +已应用迁移的字节与记录不一致时,服务会拒绝启动。 + +`0001_initial.sql` 是初始基线,后续编号继续增加字段和索引; +当前完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准,不要只执行 `0001`。 ## 冻结规则 @@ -18,6 +20,16 @@ sqlx 在服务启动时(`serve` 监听之前)按编号顺序执行本目录 - 若同一大版本内确需修正已冻结迁移里的错误,用新迁移做补偿性变更(`alter` / 回填), 不要动原文件。 +从迁移目录检查已登记文件: + +```bash +cd backend/migrations +sha256sum --check --strict .frozen-sha256 +``` + +CI 还会检查是否遗漏新 SQL 文件,并在 PR 中检查清单只增不改。 +遇到 checksum 不一致,先核对运行版本和文件来源;不要修改数据库中的 checksum 来绕过校验。 + ## 本地测试库 `gateway-store` 的 PG/Redis 集成测试需要以下环境变量,未设置时在本地 @@ -28,5 +40,6 @@ export CPR_TEST_DATABASE_URL='postgres://:@127.0.0.1:5432/' export CPR_TEST_REDIS_URL='redis://:@127.0.0.1:6379' ``` -测试自建随机 schema / key 前缀做隔离,可安全指向开发库;凭据按本地部署 -实际值填写(`docker inspect` 对应容器可查)。 +测试自建随机 schema / key 前缀做隔离,但仍应使用专用开发或测试实例,不要连接生产库。 +凭据从自己的部署配置或 CI Secret 中读取,不要粘贴未脱敏的 `docker inspect` 输出。 +环境变量未设置导致的跳过不算数据库测试通过。 diff --git a/deploy/README.md b/deploy/README.md index 21fab2e7e..e294a3e4b 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,11 +1,15 @@ -# 部署 +# 部署与运维 -本目录只有两个配置入口: +首次安装可按 [快速开始](../README.md#快速开始) 操作。 +本文补充客户端配置、权限、备份和升级;命令从仓库根目录执行。 + +需要维护的配置文件: - `config.yaml`:应用行为与真实凭据,由 `config.example.yaml` 复制得到并被 Git 忽略。 - `compose.yaml`:镜像、容器网络、端口、目录映射、健康检查和资源限制。 -项目不使用 `.env` 配置文件。Compose 中的少量环境变量只描述容器内部拓扑与发布链路装配,不是用户配置入口。 +项目不使用 `.env` 配置文件。Compose 环境变量用于容器地址、镜像选择和构建发布; +应用设置与凭据保存在 `config.yaml` 中。已有部署不要重新复制模板覆盖配置。 ## 准备 @@ -23,6 +27,7 @@ chmod 0640 deploy/config.yaml ```bash openssl rand -hex 24 +openssl rand -hex 24 ``` 把两个 48 位十六进制结果分别写入 `deploy/config.yaml` 的: @@ -47,8 +52,8 @@ UID/GID 和 mode,因此配置由当前用户持有,并只向容器组 `10001 模板中的 `openai` / `xai` 只保留请求画像启动基线。OpenAI 的上游地址、WebSocket 池、额度刷新 与 OAuth 设置,以及 xAI 的 OAuth、额度和模型目录策略,均由各自 Provider 使用代码内默认值管理; -模板不重复列出这些默认项。运行后,OpenAI Desktop 与 xAI CLI 的官方发布检查只更新进程内 -版本字段,不回写 `config.yaml`;检查失败时继续使用上一份有效画像。 +模板不重复列出这些默认项。运行后,Provider 检查官方版本并更新运行时请求画像, +不回写 `config.yaml`;检查失败时继续使用上一份有效画像。版本检查不等于重新核验 TLS。 ## 启动 @@ -65,11 +70,120 @@ docker compose -f deploy/compose.yaml ps curl -i http://127.0.0.1:8080/healthz ``` -`204 No Content` 表示应用、PostgreSQL 和 Redis 均可用。 +`204 No Content` 表示应用、PostgreSQL、Redis 和后台任务的健康检查通过,不代表每个上游账号都可用。 不要把未脱敏的 `docker compose config` 或 `docker inspect` 输出上传到工单;它们会包含 PostgreSQL/Redis 启动密码。日常校验使用 `config --quiet`。 +## 公网访问 + +Compose 默认只绑定 `127.0.0.1`。从其他设备访问时,在应用前配置 HTTPS 反向代理, +不要把 PostgreSQL 或 Redis 暴露到公网。 + +反向代理需要保留 `Authorization`,支持 `/v1/responses` 的 WebSocket Upgrade, +并关闭 SSE 响应缓冲。读取超时应覆盖长时间生成任务。 +客户端使用 `https://你的域名/v1`,不要使用前端开发服务的 `5173/dev/v1`。 +当前应用只支持单副本,不能通过复制容器扩容。 + +## 客户端配置 + +在管理端创建客户端密钥,打开「使用密钥」,按操作系统复制 `config.toml` 和 `auth.json`, +或通过 CCSwitch 导入。已有文件先备份,合并后完全退出并重启 Codex。 + +Linux/macOS 默认目录为 `~/.codex/`,Windows 为 `%USERPROFILE%\.codex\`; +设置过 `CODEX_HOME` 时以该目录为准。Provider 设置应写入用户配置,不要只写到项目目录。 +配置层级见 [官方配置参考](https://learn.chatgpt.com/docs/config-file/config-reference)。 + +### config.toml + +以下示例与当前管理端模板的配置项一致。替换地址和密钥,模型可改成该密钥有权限使用的模型: + +```toml +model_provider = "OpenAI" +model = "gpt-5.6-terra" +review_model = "gpt-5.6-terra" +model_reasoning_effort = "max" +service_tier = "default" + +[model_providers.OpenAI] +name = "OpenAI" +base_url = "http://127.0.0.1:8080/v1" +wire_api = "responses" +supports_websockets = false +requires_openai_auth = false +# 填写代理密钥,真实账号由服务端管理。 +experimental_bearer_token = "" + +[model_providers.OpenAI.http_headers] +# 供客户端识别服务端托管认证,本身不是密钥。 +X-OpenAI-Actor-Authorization = "proxy-managed" + +[features] +image_generation = true +goals = true +``` + +`OpenAI` 是自定义 Provider ID,大小写要与 `model_provider` 一致。合并配置时修改已有表, +不要重复添加 `[features]` 或 Provider 表。更换模型时也要检查其支持的推理强度。 +密钥以明文保存,文件仅供本人读取,不要提交到 Git。 + +### auth.json + +```json +{ + "OPENAI_API_KEY": "" +} +``` + +这份文件可以保留,用于兼容已有客户端和 CCSwitch。新配置从 `experimental_bearer_token` +读取代理密钥,不会因为仅含 API Key 的 `auth.json` 存在而关闭生图。 +真实 OpenAI OAuth 账号文件用于管理端账号导入,不要当作代理配置分发给客户端。 + +### 生图和 WebSocket + +新模板已启用原生生图。Codex 可在任务需要图片时调用 `image_gen.imagegen`, +再通过代理的 Images 接口生成或编辑图片;也可以在需求中明确要求生成并使用图片。 +需要支持该能力的客户端、支持图片输入的对话模型,以及有生图权限和额度的 OpenAI 账号。 +代理不会增加上游权限,xAI 账号不能承接这些 Images 请求。 + +本项目已用 Codex CLI 0.153.4 验证过原生生图,这不是最低支持版本声明。 +生图不要求开启 WebSocket。要启用客户端 WebSocket,把当前 Provider 的 +`supports_websockets` 改为 `true`,并检查反向代理是否允许 Upgrade。 + +客户端到代理、代理到上游是两段独立连接。客户端关闭 WebSocket 后, +服务端仍可能用 WebSocket 访问上游;客户端开关不控制服务端连接池和 HTTP 回退策略。 + +### 旧版配置 + +仅含代理密钥的 `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` 仍有效。不要为了接入代理,顺带扩大命令沙箱的联网或文件权限。 + +### 登录与生图排查 + +- 仍提示登录:确认实际读取的用户配置目录、选中的 Provider 和客户端版本,再完全退出重启。 + 新 Provider 使用 `requires_openai_auth = false`,不依赖本地 ChatGPT 登录。 +- 没有生图工具:检查配置是否被覆盖、Actor 标记是否保留、模型是否支持图片输入。 + 官方客户端还会检查缓存登录状态;已核验版本在本地账号为 Free 时会隐藏生图。 + 先备份并区分本地真实账号文件与代理密钥文件,不要直接删除全部登录状态。 +- 已调用生图但失败:查看服务端账号的凭据、权限、额度和请求错误,不能只凭文本对话成功判断。 +- `401`:检查代理密钥是否正确、是否启用;Actor 标记不能代替密钥。 +- `426`:客户端低于管理员设置的最低版本,或版本无法识别,需要更新客户端。 +- 地址包含 `5173/dev/v1`:这是开发代理地址,依赖 Vite 服务。日常使用改成后端或 HTTPS 地址; + 验证 WebSocket 时直接连接后端,当前 Vite 代理未显式开启 WebSocket 转发。 + +其他客户端使用 Responses API、`/v1` Base URL 和代理密钥即可,不需要 Actor 标记。 +路由与请求格式见 [API 参考](../docs/api.md#3-openai-数据面与模型目录)。 + ## 优雅关停 收到停止信号后,应用先停止接收新连接并 drain 存量连接;整个 drain 共享一个从停止信号 @@ -115,7 +229,8 @@ Provider schema 以明文 JSON 保存于 PostgreSQL。Redis 只保存可重建 OpenAI 主动额度重置卡及其消费结果由上游持有,不写入 PostgreSQL/Redis,也不属于本地备份内容; 管理端只在当前浏览器会话中保留最近一次查询结果。 -备份必须包含 `.runtime/postgres`。要保留 OAuth 的 AT/RT 恢复能力,必须保持 +数据库可用管理端的 S3/R2 逻辑备份,或停库后备份 `.runtime/postgres`。 +不要在 PostgreSQL 写入期间直接复制数据目录作为一致性备份。要保留 OAuth 的 AT/RT 恢复记录,需保持 `host.logging.file.enabled: true` 并备份 `.runtime/logs`;恢复记录位于独立文件集,仍按普通日志的 `retention_days` 和 `max_files` 分别约束。若希望保留短期 Redis 状态和会话锚点,也同时备份 `.runtime/redis` 与 `.runtime/data`。 @@ -130,7 +245,8 @@ OpenAI 主动额度重置卡及其消费结果由上游持有,不写入 Postgr 已有 PostgreSQL 数据目录后,直接修改 `database.password` 不会修改数据库用户密码,只会导致 应用无法连接。轮换时必须先在 PostgreSQL 中修改用户密码,再同步更新 `config.yaml`。Redis -密码变更后需要使用新密码重建或重新配置 Redis 数据目录。 +密码变更后需要用新配置重新创建 Redis 和应用容器,不需要删除 Redis 数据目录。 +安排维护窗口,避免应用和 Redis 在过渡期间使用不同密码。 ## 镜像升级与源码构建 @@ -153,10 +269,11 @@ docker compose -f deploy/compose.yaml up -d --no-build 仓库维护者发布新版本时必须从干净且已同步上游的分支运行: ```bash -release/publish +release/publish X.Y.Z ``` -该脚本负责更新 `release/version.yaml`、创建版本提交和带注释 tag,并原子推送分支与 tag。 +将 `X.Y.Z` 替换为待发布版本。脚本需要 Git、Ruby 和已登录的 GitHub CLI, +负责更新 `release/version.yaml`、创建版本提交和带注释 tag,并原子推送分支与 tag。 它不会登录任何服务器,也不会拉取镜像或调用管理端在线更新。 源码提交、仓库发版和运行实例升级是三种独立状态:本地 commit 不等于 Release,Release/tag 和镜像 @@ -199,7 +316,7 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs___.ta ## 备份与恢复 -### 运行时事实 +### 备份内容与限制 - 运行时镜像的 `pg_dump`/`pg_restore` 在构建期从与 compose 服务端同一固定 `postgres:18-bookworm` 镜像(`Dockerfile` 的 `POSTGRES_IMAGE`)COPY 到 @@ -226,15 +343,19 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs___.ta 工具离线恢复: ```bash -pg_restore --no-owner --no-privileges --dbname= .dump +pg_restore --no-owner --no-privileges --password \ + --dbname='postgresql://restore_user@127.0.0.1:5432/restore_db' backup.dump ``` -恢复期间与恢复后的边界见 [架构文档](../docs/architecture.md#11-生命周期安全与恢复)。操作顺序为: +示例中的账号、空目标库和归档路径需按实际情况替换,密码由命令交互输入。 -1. 恢复期间以维护模式启动,不运行备份 Worker(避免把快照中的中间状态当作当前任务继续处理)。 -2. 恢复完成后清理备份控制记录的非终态(`queued/dumping/uploading/deleting`),并重新计算 - `next_run_at`,避免按旧游标立即触发计划。 -3. 重新执行当前 S3 配置的连接探针,再允许计划任务运行。 -4. 不自动删除恢复前已经存在的远端对象。 +恢复期间与恢复后的边界见 [架构文档](../docs/architecture.md#11-生命周期安全与恢复)。 +当前没有在线恢复 API,也没有供部署者直接启用的“维护模式”开关: -首版不提供在线恢复 API;恢复属于人工运维操作。 +1. 先备份现有数据库,并停止应用;在独立的空数据库中恢复归档,检查数据和迁移版本。 +2. 应用保持停止,离线核对备份设置与记录。禁用快照中的旧计划,处理非终态记录 + (`queued/dumping/uploading/deleting`)、旧调度游标和到期清理条件,再决定哪些记录保留。 +3. 确认不会误执行旧任务或删除恢复前的远端对象后,再启动应用;重新测试 S3 连接并设置计划。 + +只关闭计划备份不会停止 Worker 的任务恢复和到期删除。无法确认这些记录的影响时, +不要把恢复后的数据库直接接入运行中的应用。具体处理应根据目标库数据制定,不提供清空生产记录的通用命令。 diff --git a/docs/api.md b/docs/api.md index 5a66aa77a..f90ede831 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,7 +1,8 @@ # Codex Proxy RS 接口 -本文列出 v3 当前公开 HTTP 接口。路由事实以 -`backend/crates/gateway-api/src` 中的 router 为准。 +本文列出 v3 源码中的公开 HTTP 接口,路由以 +`backend/crates/gateway-api/src` 中的 router 为准。配置 Codex 请先看 [客户端配置](../deploy/README.md#客户端配置); +运行实例是否包含这些功能,应结合其版本和 revision 确认。 ## 1. 鉴权与公共约定 @@ -13,6 +14,10 @@ Authorization: Bearer sk_... ``` +Codex 原生生图配置还会携带 `X-OpenAI-Actor-Authorization: proxy-managed`。 +它仅用于客户端识别服务端托管认证,不能代替 Client Key。网关和 OpenAI Provider 都会过滤该请求头, +上游账号身份只由服务端选中的账号提供;不要把真实账号 token 放进该标记。 + Client Key 通过账号分组限定路由范围:未绑定分组时可使用全部账号,绑定一个或多个分组时只能使用 已启用分组成员的并集。分组可以混合 `openai` 与 `xai` 账号;同一请求只会在模型能力明确匹配且满足 重放安全边界时跨 Provider fallback。 @@ -22,7 +27,7 @@ Client Key 通过账号分组限定路由范围:未绑定分组时可使用全 低于对应门槛时,所有 `/v1/*` HTTP 请求和新 WebSocket 握手在访问上游前返回 `426 Upgrade Required`。 未知客户端保持兼容,不应用版本门禁。 -低版本响应使用 OpenAI 风格错误合同: +低版本响应使用 OpenAI 风格错误格式: ```json { @@ -96,7 +101,7 @@ body 不进入这个通用信封。稳定业务码如下: ### 管理写入一致性 -mutation 请求不要求客户端提供全局配置版本。会改变路由快照或安全配置的写入由后端在事务内推进 +管理写入不要求客户端提供全局配置版本。会改变路由快照或安全配置的写入由后端在事务内推进 内部 `config_revision`,并用于快照发布与审计。账号更新和分组查询/写入的部分响应会返回 `configRevision` 作为已提交事实,但它不是客户端 mutation 的前置条件。 @@ -129,6 +134,10 @@ Responses WebSocket 仅接受文本 `response.create`,同一连接串行执行 这对齐 Codex 客户端 `stream_request` 持锁至本轮结束的串行行为,不表示支持额外控制消息类型。 接收队列容量为 32 个事件,超载仍关闭连接;Ping/Pong、客户端关闭和服务关闭不等待队列中的请求执行。 +客户端使用 HTTP/SSE 时,OpenAI Provider 仍可能选择上游 WebSocket。 +客户端配置的 `supports_websockets` 只控制第一段连接,不是服务端传输策略开关。 +上游在响应终态前发送 Close 1000 仍属于失败,不能按“正常关闭”计为成功。 + `GET /v1/models` 默认返回 OpenAI 兼容列表 `{"object": "list", "data": [...]}`;请求携带非空 `client_version` query 参数(Codex 客户端)时改为返回 Codex 专用目录合同 `{"models": [...]}`。 @@ -219,8 +228,10 @@ Responses wire 之间的协议转换层,转换只在 xAI Provider 内完成。 当前 xAI 导入上限为 16 MiB。内部 schema 由目标 Provider 独占解释: - OpenAI 接受单账号 OAuth 文档、`accounts` 数组(最多 200 项)和 CPR 账号 bundle; -- OpenAI OAuth token 字段只识别 `accessToken`、`refreshToken`、`idToken`,可以嵌套在账号 object 内; - 每项至少包含 AT 或 RT。RT-only 会在导入时换取 AT,AT-only 不具备自动续期能力; +- OpenAI OAuth token 字段接受 `accessToken`、`refreshToken`、`idToken`,以及官方 + `auth.json` 中的 `access_token`、`refresh_token`、`id_token`,可以嵌套在 `tokens` 等账号 object 内; + 每项至少包含 AT 或 RT。仅含 `OPENAI_API_KEY` 的客户端代理配置不是 OAuth 账号导入材料; + RT-only 会在导入时换取 AT,AT-only 不具备自动续期能力; - xAI 从单账号 object 或 `accounts` 数组中提取 OAuth token;包装中的代理、并发、优先级等字段不参与认证; - xAI 批量导入逐条独立校验:失败条目跳过并记录日志,不中断其余条目,仅当没有任何条目成功时整个导入才报错; - xAI API Key 不是受支持的账号 credential; @@ -277,8 +288,9 @@ OAuth start 使用: ### OpenAI 身份、额度与状态 -- OAuth 文件导入的 canonical 凭据字段为 `accessToken`、`refreshToken`、`idToken`,不接受含义模糊的 - `token`。仅有 refresh token 时先换取 access token。 +- OAuth 文件导入接受 camelCase 与 snake_case 的三个 token 字段,内部统一保存为 + `accessToken`、`refreshToken`、`idToken`,不接受含义模糊的 `token`。 + 仅有 refresh token 时先换取 access token。 - 身份补全复用官方 `token_data.rs::parse_chatgpt_jwt_claims`:优先解析 `idToken`,缺失字段再由 `accessToken` 补齐;`email` 优先 JWT 顶层值、其次 `https://api.openai.com/profile.email`,用户 ID 优先 `chatgpt_user_id`、其次 `user_id`。该路径不调用 `whoami`,也不信任导入文档顶层的 @@ -380,8 +392,8 @@ PostgreSQL 或 Redis。管理端只在用户打开弹窗或点击刷新时调用 | `POST` | `/api/admin/account-groups/delete` | `{ id }` | 删除未被 Client Key 引用的组 | 列表数据为 `{ items, page, configRevision }`,其中 item 返回 `memberCount`、按 Provider 聚合的 -`providerCounts` 和 `clientKeyCount`。成员响应为 `{ id, items, total, configRevision }`;成员的 -`providerKind` 描述账号自身 Provider,并不是分组属性。 +`providerCounts` 和 `clientKeyCount`。查询分组成员使用账号列表的 `groupId` 筛选, +不提供独立的分组成员路由;账号的 Provider 不代表整个分组的 Provider。 ## 7. Client Key @@ -457,8 +469,8 @@ Windows 离线包接口固定解析 Microsoft Store Product ID `9PLM9XGG6VKS` ``` `source` 为 `microsoft_store` 或 `official_openai`。Store 的四段 package version 只用于下载展示,不参与 -Desktop 三段 SemVer 门禁,也不会自动回写最低版本设置。完整安全与回退设计见 -[客户端最低版本与下载方案](client-min-version-plan.md)。 +Desktop 三段 SemVer 门禁,也不会自动回写最低版本设置。门禁规则见 +[鉴权与公共约定](#1-鉴权与公共约定),解析器职责见 [架构文档](architecture.md#11-生命周期安全与恢复)。 ## 9. 备份 @@ -555,6 +567,10 @@ request/response/upstream ID、outcome 与搜索文本。诊断 `dimension` 可 汇总与洞察中的请求数与 outcome 分布覆盖筛选范围内全部请求;token、缓存、延迟与成本聚合仅统计 已完整交付客户端的成功响应。 +错误记录中的“已自动恢复”表示系统关联到了后续成功请求,不会把原来的失败记录改为成功。 +`upstreamSendState = ambiguous` 表示无法确认该次上游执行结果,不代表后续恢复请求失败; +恢复关联也不等于逐字节验证过两次请求正文。 + Dashboard 的 `accountUsage[]` 由后端提供 `usageWindow`、`metricLabel`、`metricValue`。 `usageWindow` 复用账号额度窗口合同,缺失额度事实时为 `null`;窗口标签、百分比、触顶状态、重置时间 和本地用量由 Provider/Admin 投影。前端不得从套餐缺失推断免费套餐,也不得从显示时舍入的百分比推断 @@ -578,5 +594,5 @@ priority 价格,缺少专用价格时回退到标准价格的 `2.00x`;Flex | `POST` | `/api/admin/system/restart` | 无 | 请求进程重启 | 在线更新仅在当前部署模式、Release 资产和进程重启能力都满足要求时可用,且只在同一 major 版本内 -提供:跨大版本目标会以 `40901` 冲突拒绝,需按发布说明手动迁移。仓库发版流程见 -[部署文档](../deploy/README.md) 与根目录 [README](../README.md)。 +提供:跨大版本目标会以 `40901` 冲突拒绝,需按发布说明重新部署。 +实例升级和仓库发版见 [部署文档](../deploy/README.md#镜像升级与源码构建)。 diff --git a/docs/architecture.md b/docs/architecture.md index f06a39ad9..cb862f0ef 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,6 +1,6 @@ # Codex Proxy RS 架构 -本文回答系统由哪些边界组成、请求如何流动、状态由谁拥有,以及修改时必须保持哪些不变量。 +本文说明模块职责、请求流程、状态存储和必须保持的约束。 具体 HTTP 字段见 [接口文档](api.md),部署参数见 [部署文档](../deploy/README.md);上游 URL、超时、 重试间隔和 UI 布局属于源码或配置,不在架构文档重复维护。 @@ -177,6 +177,21 @@ Core 只理解 `Operation`、能力要求、Provider 候选、稳定错误和 ca - 请求画像以配置为启动基线。OpenAI Desktop 与 xAI CLI 的官方版本检查只更新各自负责的运行时画像, 不回写 `config.yaml`。 +### Codex 原生生图与认证 + +管理端导出的 Codex 配置使用代理 Bearer 密钥,并声明服务端托管账号认证。 +客户端据此开放原生生图工具,图片生成和编辑仍进入现有 Images 路由,不新增独立的登录或图片代理服务。 +这只解决客户端能力识别;模型能力、客户端限制和上游账号的实际生图权限仍分别检查。 + +`X-OpenAI-Actor-Authorization` 是客户端能力标记,不是账号凭据。API 解码和 OpenAI Provider +都过滤该 header,网关继续校验 Client Key,上游认证由服务端账号产生。 +仅含代理密钥的本地 `auth.json` 可以与新配置共存。配置示例维护在 +[部署文档](../deploy/README.md#客户端配置)。 + +客户端到代理与代理到上游的传输选择相互独立。客户端的 `supports_websockets = false` +不禁止 Provider 使用上游 WebSocket。响应终态前的 Close 1000 仍视为失败, +后续恢复请求成功也不改写原失败请求的结果。 + ### 错误与诊断三层边界 错误信息按用途分成三层,不能用同一个 `message` 同时承担协议、界面和诊断职责: @@ -257,7 +272,8 @@ PostgreSQL schema 由迁移目录按编号管理。已应用迁移按字节冻 credential 与 quota 是两组独立事实:credential refresh 不等于 quota refresh,额度接口的 401/403 也不能单独证明 refresh token 永久失效。 -- OpenAI 支持 OAuth、AT/RT 与 OAuth JSON;RT-only 导入先换取 AT,AT-only 导入没有 +- OpenAI 支持 OAuth、AT/RT 与 OAuth JSON,导入识别 camelCase 和官方 `auth.json` 的 snake_case token 字段; + RT-only 导入先换取 AT,AT-only 导入没有 自动续期能力。OAuth 身份只从官方 JWT claims 投影,不信任导入文档顶层身份字段。 - xAI 使用 OAuth session;API Key 不是受支持的账号 credential。 - 新账号导入和首次 OAuth 在 credential 提交后尽力读取一次额度;失败只留下观测,不回滚账号事务。 @@ -303,13 +319,17 @@ HTTP Client 构造失败也不会阻断网关启动。外部解析在已认证 - Provider credential 以 Provider schema 的明文 JSON 保存在 PostgreSQL;数据库和备份必须按敏感数据保护。 - OAuth 恢复日志包含原始 AT/RT,`.runtime/logs` 同样属于敏感数据。 +- `host.logging.request_dump` 默认关闭;开启后独立请求转储包含原始请求头和正文, + 可能包含密钥及用户内容,只能在明确的排障范围内使用,不能作为普通日志公开。 - 真实 secret 不进入普通日志、Debug、fixture 或 audit details;明文只能通过账号导出、Key reveal、 备份设置等明确的敏感 Admin 合同返回。 - OAuth pending flow 使用有期限、带 owner 的一次性 claim;事务成功后才消费,失败释放 claim。 - 在线更新校验 Release host、大小、SHA-256 和归档路径,并只允许同一大版本内更新。 -PostgreSQL 备份恢复属于人工维护操作:恢复时停用备份 Worker,清理快照中的非终态任务,重算计划游标并 -重新验证对象存储;不会自动删除恢复前已经存在的远端对象。 +PostgreSQL 备份恢复属于人工维护操作。当前没有部署级维护模式开关,需要先停止应用, +离线处理快照中的非终态任务、计划游标和到期清理条件,再重新验证对象存储并恢复计划。 +关闭计划开关只阻止新计划任务,不会停止 Worker 的任务恢复和删除流程。 +未经核对就启动旧快照,可能触发远端对象删除;操作步骤见 [部署文档](../deploy/README.md#人工恢复数据库)。 ## 12. 修改与验收 @@ -320,12 +340,16 @@ PostgreSQL 备份恢复属于人工维护操作:恢复时停用备份 Worker ```bash cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check -cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings -cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked +RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings +RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked pnpm --dir frontend format:check pnpm --dir frontend build docker compose -f deploy/compose.yaml config --quiet ``` +线程栈设置与当前 CI 一致。PostgreSQL/Redis 集成测试需按 +[迁移文档](../backend/migrations/README.md#本地测试库) 配置专用测试库;未设置环境变量时,本地相关测试会跳过。 +前端以 lint、类型检查和构建验证,`build` 已包含类型检查,不维护独立前端测试代码。 + 行为、配置或边界变化必须同步其唯一文档 owner:用户入口写入根 README,HTTP 合同写入 `docs/api.md`, 部署操作写入 `deploy/README.md`,架构不变量保留在本文。 diff --git a/docs/theme.md b/docs/theme.md index bdf603138..410b30ac5 100644 --- a/docs/theme.md +++ b/docs/theme.md @@ -5,15 +5,18 @@ # 管理端主题系统 -**基于 Seed、Map、Alias 与 Component Token 的运行时主题架构** +浅色、深色、预置配色与自定义主题的配置和实现说明。 [设计原则](#设计原则) · [Token 模型](#token-模型) · [运行时架构](#运行时架构) · [主题编辑器](#主题编辑器) · [扩展指南](#扩展指南) -主题系统为 Codex Proxy RS 管理端提供浅色、深色、预置配色与自定义配色能力。颜色 Map 复用 Ant Design 的 -Token 分层与十阶色板,再按项目的明暗角色规则派生;中性 Surface 由背景和文字 Seed 统一派生。不引入 `antd`、React、`ConfigProvider` -或 CSS-in-JS,Vue 组件继续通过 CSS Variables 与 Tailwind CSS 4 utilities 消费主题。 +在管理端「主题」中调整颜色、字号、圆角和组件样式,保存后应用到当前浏览器。 +设置保存在浏览器本地,不会同步到服务器或其他设备。只想换配色时,选择预置主题即可。 + +下文说明实现和扩展方式。颜色使用 Ant Design 十阶色板与项目的明暗角色规则, +中性表面从背景和文字 Seed 派生;Vue 组件通过 CSS Variables 与 Tailwind CSS 4 使用这些值。 +项目不依赖 Ant Design 组件库或 CSS-in-JS。 > [!IMPORTANT] > 主题的目标是调整颜色、密度、圆角与层级,不改变现有页面结构和产品语义。默认主题保留既有表面层级,并满足正常文字的可读性约束; @@ -195,8 +198,13 @@ Token 直接覆盖时不会自动重算同组件的其他状态;需要保持 主按钮的颜色在 Component 派生入口统一生成:文字保留 `colorTextLightSolid`,默认背景从 Primary Seed 校正到 4.5:1,Hover/Active 在该背景上分别混入 8% / 16% 黑色,使白字对比度逐级增强;明暗模式共用此规则。 -这组组件状态与全局 `colorPrimaryHover/Active` 分工明确,调整按钮不会反向改写主色 Seed。输入框 Hover 保留柔和 -外圈,Focus 使用已有 `control-outline`,错误外圈使用 Error Border,普通状态继续保持无边设计。 +这组组件状态与全局 `colorPrimaryHover/Active` 分工明确,调整按钮不会反向改写主色 Seed。 +输入框 Hover 与 Focus 使用 Primary Container Hover 的同色、同宽外圈; +错误外圈使用带透明度的 Error 色,具体值由 Input Component Token 提供。 + +结构化浮层头部统一使用 `--cp-popover-header-bg`:浅色从 Secondary Fill 派生, +深色从 Tertiary Fill 派生。健康时间线朝向头部的箭头也使用该值; +不要把浮层头部重新绑定到表格斑马纹或普通悬停背景。该 Token 可在主题编辑器中单独调整。 Theme Editor 只开放真正由对应组件消费的 Component Token。全局 Alias 不放进组件目录,避免一次覆盖同时改变 多个无关组件。 @@ -350,6 +358,7 @@ utility / arbitrary variant;Vue Transition、跨浏览器 Range、动态富文 - 静态填充、Hover、Active 与 Selected 必须使用不同角色,不能复用一个变量制造所有层级。 - Input 的 Hover 与 Active 只改变内部填充;外圈反馈保持同色同宽,错误状态使用独立 Error Token。 - 表格斑马纹使用 `table-row-stripe-bg`,不借用 `row-hover-bg`。 +- 结构化浮层头部使用 `popover-header-bg`,明暗模式分别检查它与表格背景的区分。 - Card 和选项默认不增加装饰性边框;键盘焦点必须保留可见反馈。 - 阴影保持中性,`shadowStrength` 只调节层级强弱,不给阴影染品牌色。 diff --git a/skills/codex-proxy-rs/SKILL.md b/skills/codex-proxy-rs/SKILL.md index 9d4ace7b3..1317cafe1 100644 --- a/skills/codex-proxy-rs/SKILL.md +++ b/skills/codex-proxy-rs/SKILL.md @@ -23,6 +23,8 @@ description: Codex Proxy RS 仓库开发与审计指南。Use for its Rust gatew | HTTP 路由、DTO、敏感导入/导出 | `docs/api.md` | | 系统边界、数据流、状态 owner、不变量 | `docs/architecture.md` | | Compose、密码、备份、更新与恢复 | `deploy/README.md` | +| Codex 配置、auth.json 与原生生图 | `deploy/README.md#客户端配置` | +| 主题配置与 Token | `docs/theme.md` | | 迁移冻结与测试库 | `backend/migrations/README.md` | | 具体目录和常见改动入口 | `references/repo-guide.md` | @@ -39,20 +41,22 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段 - 已应用迁移按字节冻结;schema 变化新增编号迁移,并同步 `.frozen-sha256`。 - 真实 secret 不进入普通日志、Debug、fixture、audit details 或文档示例;明文 Admin 响应只能出现在 账号导出、Key reveal、备份设置等明确敏感合同中。 -- 测试放在各 package 的 `tests/`,统一挂载到单一 `main` 集成测试目标;生产 `src` 不写 test-only 代码。 +- 后端测试放在各 package 的 `tests/`,统一挂载到单一 `main` 集成测试目标;生产 `src` 不写 test-only 代码。 - 不添加兼容 shim、第二套状态机、重复配置或跨层旁路。 ## 当前关键合同 -- 数据面公开 Responses JSON/SSE/WS(含 Codex 子代理语义)、Images generation/edit 和模型目录;不提供 Chat Completions。 -- OpenAI Responses/Images 保持业务 wire 透明;xAI 在 Provider 内完成 Grok/Responses 转换。 +- 数据面公开 Responses JSON/SSE/WS(含 Codex 子代理语义)、Images generation/edit、standalone Search 和模型目录;不提供 Chat Completions。 +- OpenAI Responses/Images/Search 保持业务 wire 透明;xAI 在 Provider 内完成 Grok/Responses 转换。 - Images 固定走 OpenAI Provider 自有端点,不要求模型,也不参与文本模型映射。 +- Codex 生图模板保留代理密钥 `auth.json`,使用服务端托管认证标记;该标记不能代替 Bearer 鉴权, + 并在 API 与 Provider 层被过滤。客户端 WebSocket 开关与上游传输策略相互独立。 - OpenAI 账号支持 OAuth、AT/RT 和 OAuth JSON;xAI 只接受 OAuth session/账号 JSON, 不接受 API Key。 - 主动额度重置卡只由 OpenAI 上游持有。查询由用户显式触发,消费使用 UUIDv4 幂等键;不得写本地卡库存 或直接改 quota reset 时间。 - credential 与 quota 是独立事实;刷新 token 不等于刷新额度,quota 401/403 也不等于 RT 永久失效。 -- 当前业务 schema 由 `0001_initial.sql` 建立。 +- `0001_initial.sql` 建立基线,后续编号迁移继续演进;完整清单以 `.frozen-sha256` 为准。 - 运行拓扑是单副本;worker lease 不是完整多副本 leader election,自更新也只替换当前进程。 ## 前端约束 @@ -61,6 +65,7 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段 - `frontend/src/api` 只保留 wire DTO 与请求函数;页面查询、缓存和交互状态留在对应 view/composable。 - 修改页面前先检查共享组件和相邻调用方;复用既有视觉语言,保持紧凑、低噪声和键盘可访问。 - 上游查询不得由列表渲染或隐藏轮询意外触发。不可逆动作需要明确确认、loading 和不确定结果恢复路径。 +- 当前不维护前端测试代码或独立测试命令,使用 lint、类型检查、构建和必要的界面核验。 ## 验证 @@ -68,8 +73,8 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段 ```bash cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check -cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings -cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked +RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings +RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked pnpm --dir frontend format:check pnpm --dir frontend build docker compose -f deploy/compose.yaml config --quiet diff --git a/skills/codex-proxy-rs/references/repo-guide.md b/skills/codex-proxy-rs/references/repo-guide.md index 5c8f2592d..97582f7a6 100644 --- a/skills/codex-proxy-rs/references/repo-guide.md +++ b/skills/codex-proxy-rs/references/repo-guide.md @@ -43,11 +43,13 @@ - Responses:`POST /v1/responses`、WebSocket `GET /v1/responses`; - Images:`POST /v1/images/generations`、`POST /v1/images/edits`; +- Codex 独立搜索:`POST /v1/alpha/search`; - 模型:`GET /v1/models`、`GET /v1/models/{model_id}`。 Responses adapter 解析参与路由的最少语义,并保留原始请求;review 等子代理仍走 `/v1/responses`, 类型由 `x-openai-subagent` 携带。Images adapter 不解析模型或重建 JSON,直接创建 -`GenerateImage` operation。两者都进入同一 Core execution lifecycle。 +`GenerateImage` operation。Search 同样保留原始正文,不参与模型映射。 +这些请求都进入同一 Core execution lifecycle。 定位请求问题时按以下顺序追踪: @@ -69,8 +71,8 @@ gateway-api adapter - account retry 与跨 Provider 候选推进由 Core 管理; - response ID 和原始 wire 不因观测模型而重写。 -OpenAI 保持 Responses/SSE/WS/Images 业务字节透明;xAI 在自己的 Provider 内完成 Grok/Responses 转换。 -Images 固定调用 OpenAI Provider 自有端点,候选不带 `upstream_model`。 +OpenAI 保持 Responses/SSE/WS/Images/Search 业务字节透明;xAI 在自己的 Provider 内完成 Grok/Responses 转换。 +Images 和 Search 固定调用 OpenAI Provider 自有端点,候选不带 `upstream_model`。 ## 路由与 continuation @@ -85,14 +87,14 @@ OpenAI continuation 按 native/replay-owner/replay-any 推进,xAI 使用客户 ## 账号控制面 -账号路由和 DTO 在 `gateway-api/src/admin/accounts.rs`,业务编排在 +账号路由和 DTO 在 `gateway-api/src/admin/accounts/` 的 handlers、credentials、wire 与 presenter 中,业务编排在 `gateway-admin/src/use_case/accounts.rs`,Provider 解析/验证后由 Store 事务提交。 ### 导入与授权 - 公共导入合同始终是 `{ provider, data }`,其中 `data` 为 Provider-owned JSON object。 - OpenAI 支持 OAuth JSON、`accounts` 数组、AT-only、RT-only 和 AT+RT+可选 ID Token; - 最多 200 项。RT-only 先交换 AT,AT-only 不会凭空获得刷新能力。 + 最多 200 项。token 字段接受 camelCase 和官方 snake_case;RT-only 先交换 AT,AT-only 不具备刷新能力。 - OpenAI OAuth 身份来自 `parse_chatgpt_jwt_claims`,不信任导入 object 顶层的 user/account ID。 - 前端 AT/RT 标签只把“每行一个 token”转换成 `accounts` JSON,没有第二条导入 API。 - xAI 接受 OAuth 账号 object/数组,逐项验证;API Key 不是 credential。 @@ -115,7 +117,7 @@ OpenAI continuation 按 native/replay-owner/replay-any 推进,xAI 使用客户 ## 存储与迁移 -PostgreSQL 业务 schema 由 `0001_initial.sql` 建立。`.frozen-sha256` 覆盖全部迁移, +PostgreSQL 业务 schema 由 `0001_initial.sql` 和后续编号迁移共同组成。`.frozen-sha256` 覆盖全部迁移, 已应用文件不可回改。 主要状态边界: @@ -148,7 +150,9 @@ continuation 等正确性依赖的协调写入不经该 best-effort 队列。 - 页面状态:对应 `views/*/composables/`; - 页面展示转换:对应组件或相邻 presenter/util; - 通用交互:`components/base/`; -- 主题:`styles/tokens.css` 与既有 `cp-*` token。 +- 主题派生:`frontend/src/theme/`;样式桥接在 `styles/index.css`,`styles/tokens.css` 只保存静态基元。 +- Codex 配置:`views/api-keys/utils/codexConfig.ts`,界面与 CCSwitch 共用; + 保留 API Key auth.json,只用 Provider 的 `supports_websockets` 控制客户端 WebSocket。 不要把页面状态塞进 API module,也不要为单一页面创造第二套通用层。修改共享组件前检查所有调用方; 异步上游动作必须区分 loading、明确失败和结果不确定。 @@ -157,8 +161,8 @@ continuation 等正确性依赖的协调写入不经该 best-effort 队列。 ```bash cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check -cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings -cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked +RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings +RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked pnpm --dir frontend format:check pnpm --dir frontend build docker compose -f deploy/compose.yaml config --quiet