Commit Graph
137 Commits
Author SHA1 Message Date
Wesley Liddick 4d91f39474 Merge pull request #4431 from linyqh/codex/fix-codex-image-handoff
fix(openai): 修复 Codex 生图网关侧交接问题
2026-07-16 19:32:33 +08:00
shaw 7f5d067af2 feat(grok): 支持上游端点手动切换与快捷端点,修复 SSO 建号自定义地址被覆盖
官方端点(api.x.ai / cli-chat-proxy.grok.com)偶发不可用,运营方需要在
端点间手动切换。旧语义把 OAuth 账号存储的官方 host 一律视同"未定制"并
回落默认 CLI 网关:填了官方地址保存成功却不生效、重新编辑开关回到关闭、
再次保存直接删值,形成"修改不生效"的静默循环。

后端:
- GetGrokBaseURL OAuth 分支改为"存了什么用什么":官方 API / 区域 API /
  第三方转发地址一律按填写值转发与探测,仅空值或无法解析的脏数据回落
  默认 CLI 网关;删除官方变体运行时迁移逻辑
- *.api.x.ai 区域端点(us-east-1/us-west-2/eu-west-1 等)纳入可信 host,
  OAuth 使用时不受运营方 URL 白名单限制,官方 host 仍强制 /v1 path
- 修复 SSO 批量建号 MergeCredentials 方向缺陷:BuildAccountCredentials
  恒写官方 base_url,会覆盖导入请求指定的自定义转发地址;抽出
  grokSSOImportCredentials 显式保留请求值(与 RefreshAccountToken 对齐)

前端:
- isCustomGrokBaseUrl 仅默认 CLI 网关 host 视同未定制:api.x.ai 与区域
  端点保存后正常回显(开关开启 + 显示地址),不再被静默吞掉
- 新增 GrokBaseUrlPresets 快捷端点组件(Grok Build CLI / 官方 API /
  us-east-1 / us-west-2 / eu-west-1),接入编辑(OAuth 自定义区 + apikey
  Base URL)、新增(同前)与批量编辑(所选平台全为 grok 时显示,点击
  自动勾选 base_url);仅快速填充,输入框仍可自由填写任意第三方地址
2026-07-16 19:10:21 +08:00
viccyandOmX 9d1064badd 澄清 Codex 图片桥接与本地执行器边界
配置界面现在区分非 Responses Lite 的 hosted 桥接与客户端本地 image_gen,并准确描述账号 strip 策略。API Key Mode 同时提示完全重启 Codex 后新建 task,以重建客户端工具注册表。

Constraint: 兼容模式继续保持默认,Responses Lite 的本地工具由客户端与账号策略决定
Rejected: 写入 features.image_generation | 当前 Codex 已默认启用且仓库既有配置刻意省略该项
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: 图片桥接文案必须区分 hosted 注入、客户端声明和 image-only 路由
Tested: 前端 lint、typecheck、关键套件 93 项、定向套件 43 项
Not-tested: Codex Desktop 真实工具注册表重建

Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-07-16 17:11:17 +08:00
Tian Lee 0765d10c1d feat: 增加上游 Sub2API 计费倍率探测与账号展示 2026-07-15 23:58:46 +08:00
shaw eb2b8632de fix(frontend): 修复 Grok OAuth 建号缺上游配置入口与 JSON 导入渲染崩溃
- CreateAccountModal 为 Grok OAuth 新增自定义上游地址与请求头覆写区块,
  经统一校验注入授权码兑换/RT 批量/SSO 批量三条创建路径(授权码路径
  在兑换前校验,避免烧掉一次性 code)
- JSON 导入示例文案内嵌花括号被 vue-i18n 消息编译器当占位符解析,
  渲染时抛 Invalid token in placeholder 炸掉面板:示例移出 i18n 硬编码;
  同类修复 settings 的死键 claudeOAuthSystemPromptBlocksPlaceholder(删)
  与 Hint 中被吞掉的 {billing_header}(字面量转义)
- 新增 localesMessageCompile 守卫测试:预编译中英全部文案,拦截非法占位符
- 面板打开后 scrollIntoView + 聚焦,避免在弹窗滚动容器视野外展开
- 删除「填入模板」按钮及三平台模板常量(产品决策:无意义)
- 抽取 HeaderOverrideEditor 共享组件,消除 Create/Edit/BulkEdit 三处重复
2026-07-15 22:05:59 +08:00
shaw 221581400b feat(grok): 支持账号级自定义上游地址与请求头覆写
将账号级请求头覆写从 anthropic/openai 的 api_key 账号扩展到 Grok 的
api_key 与 oauth 账号,并放开 Grok OAuth 账号的自定义上游地址(仅作用于
转发端点,授权与 token 刷新链路不变)。

后端:
- IsHeaderOverrideEligible 扩展到 Grok(api_key+oauth),禁止名单新增
  x-grok-conv-id(逐请求会话路由头)。
- 所有 Grok 上游请求路径接线 ApplyHeaderOverrides(Responses/Chat 桥/
  媒体/配额探测/billing 探测/连通性测试),统一置于内置默认头之后。
- GetGrokBaseURL/GetGrokMediaBaseURL 放开 OAuth 自定义地址:官方地址
  视同未定制回落官方网关,仅显式第三方 host 改发转发流量。
- 非官方 host 允许任意 path 前缀,官方 host 仍强制 /v1。
- OAuth base_url 校验按 host 判定官方/自定义,自定义 host 恒受运营方
  URL 策略约束,不受 XAI_ALLOW_UNSAFE_URL_OVERRIDES 调试开关放宽。
- 给 GrokQuotaService 注入 config,使配额/billing 探测与转发共用同一
  URL 策略。

前端:
- Edit 模态为 Grok OAuth 账号新增「自定义上游地址」开关。
- 请求头表单新增 JSON 快速导入与按 JSON 一键复制(复用 useClipboard,
  兼容非安全上下文 HTTP 页面)。
- 门控改为平台×类型判定,Bulk 批量 base_url 增加格式校验,zh/en 文案同步。
2026-07-15 20:30:23 +08:00
benjamin e9fb5983cd fix(billing): 默认关闭 OpenAI 长上下文计费 2026-07-13 23:32:16 +08:00
benjamin 3e4d48e010 Merge remote-tracking branch 'upstream/main' into fix/api-double-billing
# Conflicts:
#	frontend/src/components/account/EditAccountModal.vue
2026-07-13 18:06:44 +08:00
benjamin a0ac5e0240 fix(billing): 默认开启 OpenAI 长上下文计费 2026-07-13 17:55:01 +08:00
Wesley Liddick 664b7be30f Merge pull request #4055 from iMouseWu/feat/account-plan-type-edit
feat(account): 账号编辑弹窗支持手动覆盖 OpenAI 订阅档位 plan_type(仅 OAuth)
2026-07-13 15:32:57 +08:00
benjamin 0d9c140bc2 Merge upstream/main into fix/api-double-billing 2026-07-13 11:10:33 +08:00
Heatherm Huang d9e466ad3a feat(grok): support xAI API key accounts
Allow Grok API-key accounts in Responses forwarding and connection tests, expose creation and edit defaults in the dashboard, and document the supported setup.
2026-07-13 10:11:33 +08:00
benjamin 92dcfb5ebc fix(billing): 按账号控制 OpenAI 长上下文计费 2026-07-13 09:47:19 +08:00
iMouseWuandClaude Opus 4.8 c56a64fabd feat(account): 账号编辑弹窗支持手动覆盖 OpenAI 订阅档位 plan_type(仅 OAuth)
此前 plan_type(ChatGPT Plus/Pro/Free)只由系统自动写入(OAuth 换码/刷新、
429 同步、导入),UI 无编辑入口。现于 EditAccountModal 为 OpenAI OAuth 非影子
账号新增一个订阅档位下拉,可手动纠正(走既有 PUT /admin/accounts/:id 合并,
plan_type 非敏感字段无需改后端)。

- 仅 OAuth 非影子账号显示/提交:setup-token 无订阅调度语义
  (IsOpenAIChatGPTSubscription 要求 oauth),影子账号凭据由母账号管理
- 下拉:清空 + Plus/Pro/Free;别名(chatgptpro→Pro)与自定义值(team 等)
  友好显示并保留 canonical 值,避免编辑丢失或写脏
- readPlanType 仅接受字符串,挡住脏数据;清空则删除该键(恢复自动识别)
- 抽 buildPlanTypeOptions/applyPlanType/readPlanType/planTypeDisplayLabel 为
  纯函数入 credentialsBuilder.ts,并补 12 个单测(别名去重、保留其余凭据键、
  set/delete 语义)
- 文案(zh/en)注明:令牌临期刷新或 429 会用真实档位自动覆盖手改值

经三方评审会(Claude + Codex + 作者)一轮交叉评审达成共识后落地。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:31:45 +08:00
shaw ec7b20649e feat: apikey 账号支持请求头覆写(Anthropic/OpenAI)
- 账号 credentials 新增 header_override_enabled / header_overrides,
  仅对 anthropic/openai 平台的 api_key 账号生效
- 转发前对同名请求头做大小写不敏感覆盖(EqualFold 全量删除后按
  wire casing 写入单值),值为空的条目视为占位不参与覆盖
- 覆盖全部出站路径:/v1/messages(标准+透传)、count_tokens、
  /v1/responses、chat completions、embeddings、images、全部 WS 模式、
  账号测试/探测、上游模型列表
- 创建/编辑/批量更新统一校验:RFC 7230 名称格式、去重、长度/条目上限、
  24 个禁止覆写头(认证/连接控制/accept-encoding/sec-websocket-*/
  会话隔离头),应用时二次防御过滤
- 前端三个账号弹窗新增开关+键值行编辑器+模板按钮(Claude Code CLI /
  Codex CLI 标准头,值为空),本地校验与后端规则对齐,i18n 中英文
2026-07-06 19:11:08 +08:00
shaw be297b90ce refactor(frontend): merge Codex image bridge and tool policy into one four-state control
Replace the two adjacent account-level controls (3-state injection bridge
+ 2-state explicit tool policy) in EditAccountModal with a single
four-state selector: follow channel / force inject / no injection /
block all. The four states map onto the existing extra keys
(codex_image_generation_bridge, codex_image_generation_explicit_tool_policy)
with no backend change; strip already disables the bridge upstream, so
the effective decision space is exactly these four states.

- load: strip takes precedence and reads as "block all"; legacy
  bridge_enabled key still recognized and cleaned up on save
- save: block writes policy=strip and drops the bridge override;
  inherit clears both keys
- semantic accent colors per state (sky/emerald/amber/rose) with dark
  mode support; state badge follows the same palette
- replace account-scope i18n keys in zh/en (channel-scope keys untouched)
- rewrite specs to cover all four states (25/25 passing)
2026-07-04 09:49:37 +08:00
Wesley Liddick dfdb838ce4 Merge pull request #3690 from AndersonBY/feature/codex-image-tool-policy
feat: add Codex image tool strip policy
2026-07-04 09:27:37 +08:00
Anderson f385cdceb7 feat: add Codex image tool strip policy 2026-07-03 22:36:39 +08:00
Heatherm Huang b2687b2258 fix(frontend): edit grok oauth model mapping 2026-07-03 19:43:54 +08:00
Wesley Liddick 3020652fa1 Merge pull request #3565 from zy6p/zy6p/pr-openai-ws-http-bridge
feat(openai-ws): 支持 http_bridge ingress 模式
2026-07-02 17:40:32 +08:00
alfadb 7869b7fe38 fix(anthropic): 支持 API Key Bearer 认证方式 2026-07-02 10:43:08 +08:00
DaydreamCodingandClaude Sonnet 5 bdf7ead157 feat(spark-shadow): OpenAI Spark 链接型影子账号
背景:gpt-5.3-codex-spark 使用独立于 codex 全局(5h/7d)的配额窗口(数据源是
/wham/usage 响应体的 codex_bengalfox,而非 codex 全局用的 x-codex-* 响应头),且
只能挂在已完成 OAuth 授权的 OpenAI 账号下复用其登录态,不能作为独立账号单独接入。
为此新增“链接型影子账号”(spark shadow account):影子账号本身不持有任何凭据,
通过 parent_account_id 指向母账号,凭据/token/代理透传自母账号并共享母账号的刷新
周期,仅在配额维度(quota_dimension=spark)和用量窗口上与母账号完全独立调度、互不
连坐。

实现:
- 数据模型:migration 154(+154a)给 accounts 表加 parent_account_id /
  quota_dimension 列 + 4 条约束(维度合法 / parent⟺非 global 维度一致 / 禁自指 /
  FK)+ 2 个 CONCURRENTLY 索引(母账号索引 + 每母账号至多一个影子的唯一索引)。
- 创建:POST /api/v1/admin/accounts/:id/shadow(CreateShadow)—— 一母一影(唯一
  索引兜底并发竞态),继承母账号 proxy/分组/并发/优先级(显式传参可覆盖),默认
  model_mapping 恒等映射到 spark(拒绝非 spark 模型),母账号必须是真实的 OpenAI
  OAuth 账号(非影子)。
- 凭据透传:resolveCredentialAccount 把影子解析回母账号,GetAccessToken / 请求头
  / WS 三条路径统一走此函数;影子自身 Credentials 恒为空(仅允许写 model_mapping),
  凭据写入的汇聚点 persistAccountCredentials 对影子早返 no-op,防止误写。
- 调度:parentHealthyForShadow 只看母账号是否仍是 OpenAI OAuth + 凭据/传输是否
  可用(active、token 未过期、未处于 401/刷新失败/传输故障导致的临时不可调度冷却),
  刻意不看母账号的 global 限流窗口——两条 429 道互不连坐。
- 用量:影子的 codex_5h/7d 走 OpenAIQuotaService.QueryUsage(/wham/usage 的
  codex_bengalfox),与母账号走的 WSv2 探测(/responses 头)完全独立的数据源、
  刷新节流与 staleness 判定。
- 备份:ExportData 显式排除影子账号(影子不持凭据,通用凭据型导入强制
  credentials 非空、无法表达父子链接),按 skipped_shadows 计数提示前端。
- 前端:账号操作菜单新增“创建 Spark 影子”入口,影子行展示回填的母账号信息
  (邮箱 / plan / 隐私模式 / 订阅到期 / chatgpt_account_id),批量操作自动跳过
  影子账号。

说明:migrations 目录用完整文件名(而非纯数字前缀)标识迁移,故本次新增的
154_account_spark_shadow.sql / 154a_..._notx.sql 与已有的
154_add_ops_system_logs_api_key_id.sql 按序号共存,与目录里 145/151 已有的
先例一致。

测试:新增约 20 个测试文件,覆盖 handler(CreateShadow 校验 / 母账号信息回填)、
repository(影子 round-trip / 一母一影唯一索引 / 迁移 schema)、service(凭据
透传三路径 / 调度母健康门 / 用量窗口来源与刷新节流 / CRS 母账号不变量 / 各类
早返与 fail-closed 场景)及前端组件(账号列表 / 操作菜单 / 用量重置)。

验证(镜像 CI;golangci-lint 首次全量分析耗时过长被跳过,其余全部实测):
- gofmt -l:干净
- go build ./... / go vet ./...:通过
- go test ./... -count=1:全绿(全部包 ok,含 internal/service、
  internal/repository、migrations)
- go test -tags integration ./internal/repository/... ./internal/service/...
  (真实 Postgres,testcontainers):全绿,含迁移幂等性
  (TestMigrationsRunner_IsIdempotent_AndSchemaIsUpToDate)与影子相关全部用例
- pnpm lint:check / pnpm typecheck / pnpm build(真实 vite 构建)/
  pnpm vitest run:全绿(124 文件 760 用例)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 12:21:45 +08:00
zy6p 5654321380 fix(frontend): enable ws mode editing for openai setup-token accounts
(cherry picked from commit 73031d1ba8079b2f369a11b3de9542f27bd61405)
(cherry picked from commit acdbb9ce0471e5ab1009dc25e171a6497222347c)
2026-06-30 10:41:41 +08:00
zy6p 901958ba1b feat(openai-ws): add http_bridge ingress mode and account ws selector
(cherry picked from commit 58647ff63d7ff994c7c14c84c4913a8d3ed6be05)
(cherry picked from commit 9f18fb7c24e187fb20e90d1d9e89fcec91c56937)
2026-06-30 10:40:05 +08:00
DaydreamCodingandClaude Opus 4.8 819fda34d9 feat(codex-detect): codex_cli_only 检测加固 + 引擎指纹统一信号列表 + 账号级 app-server
将 codex_cli_only 客户端识别从「单一 strict 开关 + 固定 OR 头集合」重构为
可逐项管理的引擎指纹信号列表,加固整条判定链,并补齐账号级 app-server 控制、
对齐前端设置文案。

判定链(每步可短路):
- 账号未开 codex_cli_only → 不限制;gateway.force_codex_cli → 旁路放行
- 全局黑名单命中(OR 宽 deny)→ 立即拒
- 身份候选:官方 UA(strict,仅前缀)/ 官方 originator(OR)/ 全局白名单(双因子 AND)
  / 全局 app-server 开关 OR 账号 app-server 开关;均不命中 → 拒
- 版本门(仅官方候选):UA 须可解析引擎版本,再校验 [min,max] 区间
- 引擎指纹 AND 硬门:按信号列表逐条勾选 AND、每条行内变体 OR;无 Required 信号 → 放行

引擎指纹信号列表(唯一真源)
- 新增 openai.EngineFingerprintSignal 类型 + EvaluateEngineFingerprint 求值器
  (勾选 AND / 行内变体 OR / 无勾选 → 放行)
- CodexRestrictionPolicy 增 EngineFingerprintSignals;信号列表单一决定是否启用指纹门,
  不再保留独立「要求引擎指纹」总开关(与「信号全不选」语义重复)
- 新设置键 codex_cli_only_engine_fingerprint_signals(默认只勾 x-codex- 前缀);
  旧 body 指纹开关幂等迁移并入信号列表;wire 接线
- 黑/白名单自由条目、命名预设、版本区间 全局设置管线
- gateway 缺 settingService(仅测试/误配可达)时指纹门回退默认种子信号、失败关闭,
  不再因零值 policy(nil 信号)失败开放

账号级 Codex app-server(替换已失效的 ClaudeCode 放行机制)
- account.IsCodexCLIOnlyAppServerAllowed() 读 extra.codex_cli_only_allow_app_server,
  仅在 codex_cli_only 开启时生效;候选身份门「全局 OR 账号」,与旧系统双层控制对齐
- 移除已无入口的 claude_code 预设机制(allowedClientRegistry / MatchAllowedClients /
  账号 GetCodexCLIOnlyAllowedClients / reason);白名单 AllowedClientEntry / IsAllowedClientMatch 保留

门加固(反伪 + 写入校验)
- 官方 UA 访问门改 strict:IsCodexOfficialClientRequestStrict 仅前缀匹配,收窄「浏览器前缀 +
  中段 codex token」伪造面(strict 仍保留 Codex 家族前缀与 UA 尾部兜底,故对「任意前缀 +
  官方尾部 (name;ver)」仍放行——与 UA 可伪造、真正反伪靠引擎指纹门的设计一致)
- 官方客户端识别扩展:新增 codex-tui/、codex_vscode_copilot/ 前缀 + UA 尾部 (name;ver) 兜底
  (恢复 CODEX_INTERNAL_ORIGINATOR_OVERRIDE 的真实 client,如 cccc→codex-tui),originator 改
  精确集。该识别经 IsCodexOfficialClientByHeaders 被 passthrough 复用,故透传的官方判定一并
  修正(codex-tui 等不再被误改写 UA)——非「行为不变」,属有意修正
- 白名单写入校验 ValidateCodexWhitelistEntriesJSON + AllowedClientEntry.IsWhitelistable:
  双因子 AND 条目须可命中(非空 originator + 非空 ua_contains),拒绝写入会静默失效的死规则;
  黑名单(OR 宽 deny,允许 originator-only)不受约束

管理端 / 前端
- handler / DTO / settings_view / 契约测试;gateway 接入判定链
- 信号列表编辑器(替换 body 开关)、api 类型、SettingsView;无勾选给常驻警告
- Create/Edit/Bulk 三弹窗「Codex Only」下新增 app-server 开关(OR 合并全局)
- 文案:UA/Originator → User-Agent/Originator;黑/白名单重命名为 User-Agent/Originator 黑/白名单;
  「允许 App Server 第三方客户端」→「Codex app-server」+ 简介示例;i18n zh/en 同步
- 移除死代码 HasCodex*Fingerprint helper

测试:引擎指纹求值器 / 账号 app-server(OR 语义)/ detector(含 N1 strict、失败关闭)/
白名单写入校验 / BulkEdit spec 等;后端 build + service/openai/admin 单测全绿,前端 vue-tsc + vitest 全绿。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:19:41 +08:00
wucm667 650c50e34b fix(antigravity): add project fallback for standard tier 2026-06-25 16:29:33 +08:00
wucm667andClaude Opus 4.7 c9caadb378 fix(account): address second-round review on quota auto-pause
- TopK initial filter now drops quota-paused accounts: fold the quota check
  into isAccountRequestCompatible so session-hash, TopK pool, and per-candidate
  rechecks all skip paused accounts. Previously the candidate pool was built
  without the quota check, so paused accounts could fill TopK and leave the
  scheduler returning "no available accounts" even with healthy ones available.
- Add per-account explicit disable flags auto_pause_5h_disabled /
  auto_pause_7d_disabled with toggles in EditAccountModal. Without these,
  leaving the account threshold blank silently falls back to the global default,
  so admins could not exempt a single account once a global default existed.
  Disable is per-window: an account can opt out of 5h auto-pause while still
  honoring 7d. Schedule snapshot whitelist includes the new fields, i18n EN/ZH
  updated, threshold-hint text revised to explain "blank = global default".
- Move quota auto-pause settings off the request hot path: replace the per-repo
  TTL+singleflight sync DB read with a per-SettingService stale-while-revalidate
  in-memory snapshot. Get is non-blocking (atomic.Pointer load + async refresh
  on staleness); writes via UpdateOpsAdvancedSettings push directly into the
  cache through an injected sink; wire warms the cache at startup. Adds Warm
  (sync) for tests/init and SetOpenAIQuotaAutoPauseSettings (sink target).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 14:32:45 +08:00
wucm667 ead471d64b feat(account): 支持按 5h/7d 用量阈值自动暂停账号调度 2026-05-29 10:47:47 +08:00
Wesley Liddick 433f8dcd13 Merge pull request #2834 from DaydreamCoding/pr/openai-codex-cli-allow-claude-code
feat(openai): codex_cli_only 新增放行 Claude Code Codex 插件的机制
2026-05-29 10:30:33 +08:00
shaw 37044b83eb fix(openai): clarify endpoint capability UI 2026-05-29 09:23:06 +08:00
shaw ed1b57c597 fix(openai): gate routing by endpoint capability 2026-05-29 08:58:10 +08:00
DaydreamCodingandClaude Opus 4.7 56908d3c4c feat(openai): codex_cli_only 新增放行 Claude Code Codex 插件的机制
适用场景:在 Claude Code 中使用 https://github.com/openai/codex-plugin-cc
插件时,插件经官方 codex app-server 以 clientInfo.name="Claude Code" 完成
initialize 握手,请求头被设为 originator=Claude Code、User-Agent 含
"Claude Code/",不在官方客户端白名单内,原本会被 codex_cli_only 拦截 403。

在官方客户端白名单未命中时评估两层独立放行(OR 语义):

- 按账号:account.Extra.codex_cli_only_allowed_clients 引用命名预设
  (目前仅 claude_code),detector reason=allowed_client_matched
- 全局开关:/admin/settings 网关服务 OpenAI 区块新增
  openai_allow_claude_code_codex_plugin(默认 false),开启后对所有
  codex_cli_only 账号统一放行,detector reason=global_allowed_client_matched

签名仍要求 originator=Claude Code 精确等值 + UA 含 "Claude Code/"。
上游转发保持透传不变。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 23:55:34 +08:00
StarryKiraandClaude Opus 4.7 21033dceb9 feat(account): configurable pool-mode same-account retry status codes
Pool mode currently retries the same account for a fixed set of
upstream HTTP statuses: 401, 403, 429. Some upstream pool deployments
also need same-account retry for transient provider/proxy statuses
such as 502, 503, 520, 529, but hard-coding more statuses changes
behavior for everyone.

Add a per-account credentials option `pool_mode_retry_status_codes`
that lets admins choose which upstream HTTP status codes trigger
same-account retry in pool mode:

- Unset (default): preserve the current 401/403/429 default
- Explicit list: override the defaults with the configured codes
- Codes normalized to the 100-599 range, deduplicated, sorted

The standalone `isPoolModeRetryableStatus` helper is kept as the
default-only fallback. All 15 gateway call sites switch to the new
`Account.IsPoolModeRetryableStatus` method so behavior is preserved
for accounts that do not configure the new field.

Frontend admin UI gains a "Retry Status Codes" comma-separated input
under the pool-mode section in both Create/Edit account modals
(en + zh i18n).

Fixes #2731

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 11:24:25 -07:00
shaw 0430899748 feat(admin): add compact proxy IP resource link
Add a low-visibility proxy IP resource link near proxy-related controls.

- Show the link beside account proxy selectors

- Show the link in the create proxy dialog tab row

- Keep the entry inline to avoid interrupting form workflows
2026-05-23 14:18:19 +08:00
Wesley Liddick a929e285ce Merge pull request #2271 from StarryKira/fix/redact-account-credentials
fix(security): 屏蔽 admin 账号接口返回的敏感凭证字段
2026-05-19 16:15:36 +08:00
Wesley Liddick f05670dd0f Merge pull request #2454 from wucm667/codex/issue-2426-model-mapping
fix(account): 保留模型白名单和模型映射组合配置
2026-05-19 14:50:03 +08:00
Wesley Liddick e365aae450 Merge pull request #2450 from wucm667/codex/issue-2431-responses-api-support
feat: 支持后台配置 OpenAI Responses API 路由
2026-05-19 14:47:10 +08:00
benjaminandSisyphus 5713820813 feat: add upstream model sync controls
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-18 19:01:55 +08:00
harukaandClaude Opus 4.7 3ca232ad06 fix(frontend): 编辑弹窗回退旧 credentials 结构以兼容旧后端
新代码仅依赖 credentials_status 会导致两种灰度场景被误判为未配置:
- 新前端 + 旧后端:旧后端未返回 credentials_status,前端读不到已脱敏的
  api_key / service_account_json,阻止保存。
- 旧前端 + 新后端:旧前端也读不到已脱敏字段(旧前端不在本 PR 范围)。

修复:
- API key 判断改为 credentials_status?.has_api_key ?? Boolean(currentCredentials.api_key)
- Vertex SA 判断:有 credentials_status 用 status,否则回退读
  credentials.service_account_json / service_account

补充测试覆盖:
- apikey/Vertex SA 各自的新后端脱敏响应、旧后端未脱敏响应、
  两者皆缺时阻止保存。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 03:02:08 +08:00
wucm667 827764d7bd fix(account): preserve combined model restrictions 2026-05-14 15:00:28 +08:00
wucm667 862819042c feat(openai): 支持后台配置 Responses API 路由 2026-05-14 11:46:24 +08:00
harukaandClaude Opus 4.7 0f8e2d0934 fix(security): 屏蔽 admin 账号接口返回的敏感凭证字段
Account.Credentials 是 JSONB map,混合存放可编辑的非敏感配置(base_url、
model_mapping、project_id 等)与敏感秘钥(OAuth access/refresh/id token、
API key、AWS secret、Vertex private key 等)。当前所有 admin 账号接口直接
透传该 map,token 经由浏览器 DevTools、抓包、日志等途径泄漏。

- service 包新增 SensitiveCredentialKeys 清单与 MergePreservingSensitiveCreds
  作为单一权威定义。
- dto 层 RedactCredentials 在响应里剥离敏感子键,输出 credentials_status
  (has_<key> 布尔标识)告知前端存在性,不暴露原值。
- AccountFromServiceShallow 接入脱敏,覆盖 list、get、create、update、
  refresh、batch、bulk-update、OAuth 创建等 9 个 handler。
- service.UpdateAccount 改为合并语义:incoming 没传敏感键则保留 existing,
  让前端"全对象 PUT"流程在脱敏后无感工作;显式提供新 token 仍会覆盖。
- 前端 EditAccountModal 修复脱敏后会崩的两处兜底:apikey 必填检查和
  Vertex SA JSON 存在性校验改读 credentials_status.has_*。
- 导出端点 /admin/accounts/data 走独立的 DataAccount 结构,按设计保留
  完整 credentials 作为管理员备份路径。

测试:RedactCredentials 单元测试、mapper 端到端 JSON 断言(确认序列化
后无 token 子串)、UpdateAccount 合并语义三种场景(保留 / 覆盖 / 空 map 跳过)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 03:44:04 +08:00
shaw 7a9c1d7edd feat(frontend): add account Codex image bridge control 2026-05-07 11:07:33 +08:00
shaw 93d91e20b9 fix(vertex): audit fixes for Vertex Service Account feature (#1977)
- Security: force token_uri to Google default, preventing SSRF via crafted service account JSON
- Dedup: extract shared getVertexServiceAccountAccessToken() to eliminate ~35 lines of duplication between ClaudeTokenProvider and GeminiTokenProvider
- Fix: apply model mapping + Vertex model ID normalization in forward_as_responses and forward_as_chat_completions paths
- Fix: exclude service_account from AI Studio endpoint selection (Vertex cannot serve generativelanguage.googleapis.com)
- Feature: add model restriction/mapping UI for service_account in EditAccountModal
- Dedup: extract VERTEX_LOCATION_OPTIONS to shared constants
- i18n: replace all hardcoded Chinese strings in Vertex UI with translation keys
2026-04-29 16:53:09 +08:00
Oliver 6d11f9ed77 Add Vertex service account support 2026-04-25 20:39:58 -04:00
shaw 095f457c57 feat(openai): port /responses/compact account support flow (PR #1555)
将 vansour/sub2api#1555 的 OpenAI compact 能力建模手工移植到当前 main:账号
级 compact 状态/auto-force_on-force_off 模式、compact-only 模型映射、调度器
tier 分层(已支持 > 未知 > 已知不支持)、管理后台 compact 主动探测,以及对应
i18n/状态徽章。普通 /responses 流量行为不变,无数据库迁移。
2026-04-25 14:52:58 +08:00
erio 948d8e6d02 fix(admin): prevent browser password manager from autofilling account API key
Chrome's password manager matched the apikey-type account's Base URL + API Key
inputs as a login form and autofilled the last saved password by domain, so
editing a Gemini account could overwrite its apikey with a Claude key that
shared the same Base URL. Add autocomplete="new-password" plus data-*-ignore
attributes for 1Password / LastPass / Bitwarden to opt the field out of every
major password manager's autofill.
2026-04-17 22:06:32 +08:00
KnowSky404 836092a666 fix: restore ctx pool ws mode option in account ui 2026-04-16 02:13:04 +00:00
erio 1b7c295199 refactor: M5 useQuotaNotifyState composable + H14 Vue file splits
M5: New composable frontend/src/composables/useQuotaNotifyState.ts
  - Replaces 9 individual refs in both Create/Edit modals with reactive state
  - Provides loadFromExtra/writeToExtra/reset helpers
  - Eliminates ~120 lines of duplicated code across the two modals

H14: Vue file length violations fixed
  - AdminPaymentPlansView.vue: 325 → 183 lines (extracted PlanEditDialog.vue)
  - QuotaLimitCard.vue: 327 → 268 lines (extracted QuotaDimensionRow.vue)
  - PlanEditDialog.vue: 181 lines (new, plan create/edit form)
  - QuotaDimensionRow.vue: 108 lines (new, single quota dimension row)
2026-04-14 09:33:39 +08:00
erio a43da62254 fix(accounts): unify modal width, add notify props to create, fix quota layout
- EditAccountModal width changed from "normal" to "wide" (match CreateAccountModal)
- CreateAccountModal now passes all quota notify props to QuotaLimitCard
- QuotaLimitCard: when global notify disabled, hide title row, input takes full width
- Quota alert email: show remaining quota + threshold (fixed/$, percentage/%) instead of usage trigger point
2026-04-14 09:31:32 +08:00