Commit Graph
4929 Commits
Author SHA1 Message Date
Wesley Liddick 6f43986c37 Merge pull request #3811 from jianjianai/hotfix/admin-scheduler-score-opt-in
fix(admin): 管理员账号列表默认关闭调度权值计算以降低负载
2026-07-08 10:22:10 +08:00
Wesley Liddick f407d6a044 Merge pull request #3804 from fengshao1227/fix/compact-body-signal-routing
fix(compact): 识别 /v1/responses body 中的 compaction_trigger 信号
2026-07-08 10:12:58 +08:00
shaw a56eb5b4dc fix(compact): body-signal 提升上移到 handler 层并对齐 path-based 链路
合并 main 解决拆分冲突后,将原先 Forward 内的 body-signal 提升重构到
handler 的 compact 归一化入口之前,修复原方案的四个问题:

- reqStream 未重推导:body-signal 原始请求带 stream:true,Forward 级提升
  后 compact 上游返回 JSON(Accept: application/json)却被流式 handler
  解析,"stream ended before a terminal event" 会触发最多
  max_account_switches 次换号 failover,且每次都白烧一次上游 compact 配额;
  handler 级提升让白名单归一化先删除 stream,reqStream 自然为 false。
- requireCompact 调度过滤失效:原方案 path 改写发生在 requireCompact 判定
  之后,调度器不会过滤不支持 compact 的账号;现在改写先于该判定。
- passthrough / Grok / chat-completions 桥接分支位于 Forward 检测点之前,
  passthrough 账号完全无法命中;handler 级改写对所有分支生效。
- body 归一化口径不一致:body-signal 现在与 path-based 一样走白名单归一化
  (prompt_cache_key 等一并删除),而非仅依赖 OAuth 黑名单转换。

检测函数导出为 HasCompactionTriggerInInput 供 handler 使用,保留原 PR 的
7 个单测;新增 6 个 handler 级回归测试(提升、codex 别名路由、尾斜杠、
子路径不误伤、path-based 无双重后缀、普通请求不受影响)。

Refs #3777
2026-07-08 10:04:14 +08:00
shaw a855317624 Merge remote-tracking branch 'origin/main' into fix/compact-body-signal-routing
# Conflicts:
#	backend/internal/service/openai_gateway_service.go
2026-07-08 09:57:31 +08:00
Wesley Liddick f4e8bec54c Merge pull request #3803 from fengshao1227/fix/sidebar-scroll-position-persist
fix(frontend): 路由切换后保持侧边栏滚动位置
2026-07-08 09:53:05 +08:00
Wesley Liddick 6631cbad67 Merge pull request #3812 from Wei-Shaw/refactor/split-bloated-files-r2
refactor: 纯移动拆分 8 个后端大文件与 i18n 语言包
2026-07-08 09:03:44 +08:00
shaw d0bcaa8b09 test(i18n): 新增语言包 spread 组装键冲突守卫测试 2026-07-08 08:49:23 +08:00
shaw bb5d2e84a1 refactor(handler): 纯移动拆分 setting_handler.go(3957→468行) 2026-07-08 08:49:22 +08:00
shaw f013bc1141 refactor(service): 纯移动拆分 admin_service.go(4409→642行) 2026-07-08 08:49:22 +08:00
shaw 2a4c28e8f5 refactor(service): 纯移动拆分 antigravity_gateway_service.go(4664→639行) 2026-07-08 08:49:22 +08:00
shaw d0f669338b refactor(service): 纯移动拆分 openai_ws_forwarder.go(4675→399行) 2026-07-08 08:49:21 +08:00
shaw db3bd9971e refactor(repository): 纯移动拆分 usage_log_repo.go(4701→212行) 2026-07-08 08:49:21 +08:00
shaw 4d23ad4bac refactor(service): 纯移动拆分 openai_gateway_service.go(4872→1095行) 2026-07-08 08:49:21 +08:00
shaw 50043b1176 refactor(service): 纯移动拆分 setting_service.go(5471→263行) 2026-07-08 08:49:20 +08:00
shaw 084d26cbd2 refactor(service): 纯移动拆分 gateway_service.go(7294→1289行) 2026-07-08 08:49:20 +08:00
shaw d9e514f982 refactor(i18n): 拆分 zh/en 语言包为域模块(深比较等价校验通过) 2026-07-08 08:49:19 +08:00
Wesley Liddick 317339e716 Merge pull request #3813 from Wei-Shaw/fix/bump-aws-sdk-eventstream-vuln
fix(deps): 升级 AWS SDK 修复 GO-2026-5764(backend-security CI 失败)
2026-07-08 08:48:20 +08:00
shaw a4f942d8a9 fix(deps): 升级 AWS SDK 修复 govulncheck 报告的 GO-2026-5764
eventstream v1.7.5→v1.7.8、service/s3 v1.96.2→v1.97.3(含关联 internal 模块),
修复 EventStream 解码器 DoS 漏洞,恢复 backend-security CI。
2026-07-08 08:37:29 +08:00
jjaw 6ae5fc31b3 fix(admin): gate scheduler score calculation 2026-07-08 06:58:35 +08:00
zhengshanandClaude Opus 4.8 3866da508f fix(ratelimit): Anthropic 无 reset 头的 429 也进入兜底冷却
此前 Anthropic 429 若响应头无窗口重置时间(如 Extra usage required),
被视为"非真实限流"直接跳过标记。后果:账号永不冷却,调度器让每个
请求反复撞同一批持续 429 的账号——failover 预算烧尽后客户端稳定
收到 429(生产实测:一次请求 1.3s 内连换 4 账号全 429,而 DB 中
这些账号的限流状态毫无更新)。

改为与其他平台一致走 apply429FallbackRateLimit 秒级兜底回避:
- 默认 5s,管理端 RateLimit429CooldownSettings 可调 1~7200s
- 管理端关闭该设置即恢复旧行为(不标记)
- 日志 reason 标记为 anthropic_no_reset_time 便于运营区分

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 23:19:23 +08:00
Wesley Liddick e8e234252a Merge pull request #3807 from Wei-Shaw/refactor/gateway-cc-pipeline-and-file-split
refactor(gateway): 抽取 CC forwarder 公共管线并拆分两大 service 文件
2026-07-07 23:08:55 +08:00
shaw d754be0d8e refactor(gateway): 抽取 CC forwarder 公共管线并拆分两大 service 文件
PR #3802 遗留项:三个 CC forwarder(raw 直转 / responses 回退 / messages
回退)间约 85% 重复的 HTTP 管线与 SSE 循环骨架收敛到新文件
openai_gateway_cc_pipeline.go,messages / chat_completions 两条主路径中
逐字相同的错误处理块一并接入。各路径有意保留的行为差异(GLM effort
归一化、fast policy 适用范围、ClientDisconnect 语义、Grok 分支等)留在
调用方,未强行统一。

同时对两个最臃肿的网关文件做纯移动拆分(零语义变化,逐字节校验):

- openai_gateway_service.go 7821→4872 行:
  调度 → openai_gateway_scheduling.go
  passthrough → openai_gateway_passthrough.go
  用量/计费/codex 快照 → openai_gateway_usage.go
- gateway_service.go 10912→7294 行:
  调度 → gateway_scheduling.go
  Anthropic APIKey 直通 → gateway_anthropic_passthrough.go
  Bedrock → gateway_bedrock.go

回归保障:全部搬迁块与 HEAD 逐字节 diff 一致;留存文件经"HEAD 减去搬迁
范围"重构比对,差异仅为 goimports 移除的孤儿 import;定向单测
(fallback/raw/cyber/grok/transport/调度/用量/广域 Forward-Handle 扫描)
全绿;另经独立对抗审计确认零行为差异。
2026-07-07 22:14:46 +08:00
li 2dd2be9922 fix(compact): 识别 /v1/responses body 中的 compaction_trigger 信号
Codex remote compact v2 可以把 compact 触发器作为 input item
(type=compaction_trigger)嵌入普通 POST /v1/responses 请求体,
而非直接调用 /v1/responses/compact。此前 isCompactRequest 仅检查
URL path 后缀,导致 body-signal 形式的 compact 请求被当作普通
Responses 处理——上游路径、模型映射、body 归一化全部错误,Codex
收到非 compact 响应后报 "expected exactly one compaction output
item, got 0",长会话无法继续。

新增 hasCompactionTriggerInInput 检测 input 中的 compaction_trigger
item,命中后提升为 compact 请求并改写 URL path,使后续所有
isCompactRequest 分支(模型映射、body 归一化、上游 URL 构建)
自动生效。

Fixes #3777 (part 1: body-signal routing)
2026-07-07 21:23:02 +08:00
li a86c534cb0 test: 补充侧边栏滚动位置持久化的前端测试
- AppSidebar.spec.ts:验证 template ref 绑定、sidebarNavRef 声明、
  onBeforeUnmount 保存 scrollTop、onMounted 恢复
- app.spec.ts:验证 sidebarScrollTop 默认值为 0 且可读写
2026-07-07 21:11:40 +08:00
li c7e44a83ad fix(frontend): 路由切换后保持侧边栏滚动位置
AppLayout 在每个页面组件内部包裹,路由切换时整个组件树(含
AppSidebar)被销毁重建,侧边栏滚动位置丢失。管理员菜单较长
或屏幕较矮时,每次点击靠下的菜单项后都要重新滚动。

在 appStore 增加 sidebarScrollTop 状态,AppSidebar 在
onBeforeUnmount 保存、onMounted 恢复滚动位置。

Fixes #3796
2026-07-07 21:08:03 +08:00
Wesley Liddick f68f3b86c6 Merge pull request #3802 from Wei-Shaw/fix/messages-chat-fallback-hardening
fix(messages): 修复 /v1/messages CC 回退的错误处理旁路并补齐流式测试
2026-07-07 21:07:29 +08:00
shaw dad92488e5 fix(messages): 修复 /v1/messages CC 回退的错误处理旁路并补齐流式测试
针对 #3795 合并后审计发现的问题:

- 非 failover 上游错误分支改走共享 handleAnthropicErrorResponse:恢复
  ops 上游错误记录、错误透传规则、cyber_policy 检测与按状态码映射的
  错误 type(400→invalid_request_error 等);删除随之失效的
  mapUpstreamStatusToAnthropicStatus
- 流式读错误对齐 forwardResponsesViaRawChatCompletions:scanner 出错时
  不再 finalize(不再补发 message_stop 把截断伪装成正常完成),返回
  stream usage incomplete 错误
- sawDone 由死变量改为与兄弟一致的无 [DONE] 哨兵 debug 日志
- 补充 fast policy 在本路径有意省略的说明注释
- 新增 8 个单测:流式收尾闭块、tool_call 分片聚合、length→max_tokens、
  空流补帧、非 failover 400 走共享处理器、读错误不伪造收尾、
  Responses 账号门控回归、非流式转换
2026-07-07 20:59:41 +08:00
Wesley Liddick 4bcb13f77f Merge pull request #3786 from creamtea47/codex/openai-fast-force-priority
add force priority fast policy action
2026-07-07 20:45:45 +08:00
Wesley Liddick 9643382bd3 Merge pull request #3795 from fengshao1227/fix/messages-inbound-chat-completions-fallback
fix(messages): /v1/messages 入站支持不兼容 Responses API 的 OpenAI 上游
2026-07-07 20:45:14 +08:00
Wesley Liddick af4b75a77c Merge pull request #3794 from fengshao1227/fix/image-gen-namespace-permission-bypass
fix(image): 识别 Codex namespace image_gen 工具声明以拦截生图请求
2026-07-07 20:42:43 +08:00
Wesley Liddick c7249a54ac Merge pull request #3793 from heathermhuang/codex/grok-video-text-fallback
fix: route text-only Grok video 1.5 requests
2026-07-07 20:22:44 +08:00
Wesley Liddick 65ff800362 Merge pull request #3799 from Wei-Shaw/fix/batch-image-audit-hardening
fix(batch-image): 修复审计发现的计费死锁、状态机与队列原子性缺陷
2026-07-07 20:20:49 +08:00
shaw b5604afd3d test(batch-image): 集成测试适配 ReplaceBatchImageItemsForJob 的 indexing 状态守卫
守卫要求 job 处于 indexing 状态才允许重建 item 表;原测试在 created
状态下直接调用被 409 拒绝。改为先断言非 indexing 状态的冲突行为,
再经 created→submitted→indexing 合法链推进后执行原有断言。
2026-07-07 20:13:28 +08:00
CHOS1N 2fd3539527 Merge remote-tracking branch 'upstream/main' into codex/add-response-format-compat 2026-07-07 20:05:50 +08:00
limingeandClaude Fable 5 13e773ef5e feat: Codex 客户端模型清单(manifest)透传接口
背景:Codex CLI / Codex App 会从 provider 的 GET {base_url}/models?client_version=...
(自定义 provider 模式)或 GET /backend-api/codex/models(chatgpt_base_url 模式)
刷新模型选单,期望 ChatGPT Codex manifest 格式({"models":[{slug,...}]})。
sub2api 此前只提供 OpenAI 兼容格式的 /v1/models,Codex 客户端解析失败后静默
回落到本地缓存,导致指向 sub2api 的 Codex 客户端模型选单永久冻结在切换
provider 当天的状态,新模型(如 gpt-5.6 系列)永远不会出现。

方案:新增 manifest 透传——用组内可调度 OAuth 账号的凭据向
chatgpt.com/backend-api/codex/models 实时转发请求,响应体与 ETag 原样透传。
不在网关侧解析或维护模型清单:manifest schema 随 Codex 客户端版本演进,
透传保证网关无需跟进 schema 变化,且返回的始终是账号真实的模型权限
(区别于静态 DefaultModels 的"理论列表")。

路由:
- GET /backend-api/codex/models(新增)
- GET /v1/models 带 client_version 查询参数且组平台为 openai 时分发到
  manifest 透传(client_version 是 Codex 客户端的天然指纹,普通 OpenAI
  客户端不携带);其余请求保持原有 OpenAI 格式行为不变。

上游失败时按 fast-fail 返回错误,不伪造列表;Codex 客户端自身会回落缓存。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 19:49:40 +08:00
xlplbo 27e29f0562 feat(apicompat): 添加 tool_search 支持,增强工具调用处理逻辑 2026-07-07 19:19:35 +08:00
shaw 80a229bce5 fix(batch-image): 修复审计发现的计费死锁、状态机与队列原子性缺陷
修复 PR #3768 批量图像 MVP 合并后审计报告中的全部问题:

结算与计费(高危):
- 所有 SETTLEMENT_* 失败(超冻结/计数非法/manifest 冲突/定价缺失/扣费失败)
  统一计入 retry_count 并在耗尽时释放冻结转 failed,消灭 settling 无限
  requeue 导致的冻结余额永久锁死
- 耗尽出口的释放指纹统一为 RequestHash,与 processor/Cancel/recovery 一致;
  release 遇同 request id 指纹冲突视为幂等成功,治愈历史毒消息
- 管理端校验 hold_multiplier >= discount_multiplier,定价快照对存量脏数据钳制
- 释放前校验 per-job hold claim(dedup+归档表),杜绝幻影释放

索引对账(高危):
- provider 输出与提交 custom_id 集对账:未知条目丢弃并记事件,
  漏项补 PROVIDER_RESULT_MISSING 失败行,保证 success+fail == item_count

提交与恢复(高危):
- 提交前转 uploading 并在 provider.Submit 期间心跳刷新 updated_at;
  恢复扫描改为原子复核(FailStaleUnsubmittedBatchImageJob),
  消灭慢提交被误杀退款而上游任务照常计费的孤儿场景
- 上游任务创建成功但本地状态推进失败时,尽力取消上游并清理输入
- recovery 释放失败时入队交由 worker releaseTerminalHold 兜底重试

队列与并发(中危):
- Enqueue(SetNX+LPush)与 Reserve(BRPop+ZAdd)均改为 Lua 原子脚本,
  消灭崩溃窗口导致 job 脱离队列、被 7 天 inflight 键锁死
- 锁冲突按 LockConflictDelay 重新入队(原直接丢弃需等 10 分钟 stale 恢复)
- 处理期间心跳:active zset 续期(ZAddXX 防幽灵成员)+ 锁 TTL 续期
- ReplaceBatchImageItemsForJob 增加 indexing 状态守卫,防掉队 worker 重写账目

存量回归(中危):
- image-only 定价条目(仅图片价无 token 价)恢复 token 计费 fail-closed,
  不再按 $0 计费;图片计费路径不受影响
- 鉴权余额门槛恢复 balance <= 0 语义,MinimumBalanceReserve 不再作硬 403

加固:
- ZIP max_items 钳制到管理员上限;Submit 补齐 Platform==Gemini 校验;
  gemini downloadUri 跟随前做 host 白名单校验
- 批量客户端改用共享 httpclient(拨号/TLS/响应头超时有界)
- 审计点名的忽略错误(MarkDownloaded/SettlementFailed/AppendEvent 等)改为记日志
2026-07-07 18:53:56 +08:00
li 69d02d2036 fix: 补齐 failover 路径的 appendOpsUpstreamError 日志
审查发现 error 路径缺少 appendOpsUpstreamError 和 upstreamDetail
记录,与 forwardResponsesViaRawChatCompletions 不对称。
2026-07-07 17:35:09 +08:00
li 5c0e580fbe fix(messages): /v1/messages 入站支持不兼容 Responses API 的 OpenAI 上游
ForwardAsAnthropic 此前无条件把 Anthropic Messages 转为 Responses
格式发往 /v1/responses,导致只支持 /v1/chat/completions 的第三方
OpenAI 兼容上游(京东云、DeepSeek、Kimi、GLM 等)全部 400。

在入口增加与 ForwardAsChatCompletions 对称的 ShouldUseResponsesAPI
分流:APIKey 账号 + 上游不支持 Responses 时,改走新建的
forwardAnthropicViaRawChatCompletions,链式复用全部已有转换桥
(Anthropic→Responses→CC 请求,CC→Responses→Anthropic 响应),
不新建转换函数。

Fixes #3735
2026-07-07 17:23:18 +08:00
li a02700c16f fix(image): 识别 Codex namespace image_gen 工具声明以拦截生图请求
分组 allow_image_generation=false 时,Codex /image 技能通过
namespace 工具声明(type=namespace, name=image_gen)和 Responses
Lite 的 input additional_tools 绕过了现有生图意图检测——现有逻辑
只识别 tools[].type == "image_generation" 这一种形式。

在 gjson 版(openAIJSONToolsContainImageGeneration)和 map 版
(hasOpenAIImageGenerationTool)检测中增加 namespace image_gen
识别;同时扫描 input[].type == "additional_tools" 中嵌套的工具
声明,覆盖 Responses Lite 格式。

Fixes #3608
2026-07-07 16:58:48 +08:00
Heatherm Huang 3b2099350e fix: route text-only grok video requests 2026-07-07 16:44:20 +08:00
Wesley Liddick 44ab690a01 Merge pull request #3768 from Turtle-Li/feature/batch-image-foundation
feat: add batch image generation MVP
2026-07-07 16:32:45 +08:00
xlplbo 75fb3c41c2 fix(apicompat): responses→chat 桥支持 custom 工具,修复 Codex exec 丢失
Codex 0.14x 的核心执行工具 exec 是 type=custom(freeform)工具,
responsesToolsToChatTools 丢弃所有非 function 工具,导致模型工具列表中
没有 exec、无法执行任何命令;SSE 序列化层 responsesItemWire 也缺少
custom_tool_call 分支,即使产出该类型 item,call_id/name/input 也会被
白名单剥离。

- custom 工具降级为单一 input:string 参数的 function 工具
- 回程(非流式+流式)按名字还原 custom_tool_call 项/事件
- 历史 custom_tool_call / custom_tool_call_output 项转换(此前被跳过)
- responsesItemWire 补 custom_tool_call 分支
- tools 全部被丢弃时不再转发 tool_choice(修复上游 400
  "'tool_choice' is only allowed when 'tools' are specified")

feat(apicompat): chat 桥补齐 tool_search/namespace/字符串工具声明

对齐参考实现(cc-switch transform_codex_chat.rs)的工具类型覆盖:
- tool_search → 固定 function 代理工具(query 必填/limit 可选),
  历史项 tool_search_call/tool_search_output 一并转换
- namespace → 子 function 工具摊平为 <namespace>__<name>,
  超 64 字符截断加 sha256 短哈希;历史项带 namespace 字段时同规则摊平
- tools: ["name"] 字符串简写按 custom 工具解析

fix(apicompat): 工具调用宣告延迟到类型可判定,兼容 name 迟到的上游

output_item.added 的 custom/function 判定依赖工具名。名字晚于首个分片
到达时,此前会按 function_call 宣告造成 added/done 项类型不一致。
改为存在 custom 工具且名字未到时延迟宣告,宣告时补发已累积的参数增量;
请求无 custom 工具时行为不变。

PR 标题与正文(可直接粘贴)

标题:fix(apicompat): responses→chat 桥丢弃 custom 工具导致 Codex 无法执行命令

正文:

## 问题

OpenAI APIKey 账号接入仅支持 /v1/chat/completions 的第三方上游
(DeepSeek/GLM/Kimi 等)时,Codex 0.14x 客户端文本对话正常,但无法执行
任何命令:exec 调用全部 "aborted",spawn_agent 派出的子智能体永远卡在
running,多轮后模型自述"环境无法执行文件系统命令"放弃。

## 根因

Codex 0.14x 的核心执行工具 exec 是 Responses API 的 custom(freeform)
工具,而非普通 function:

    {"type": "custom", "name": "exec", "format": {"type": "grammar", ...}}

两处缺陷叠加:

1. `responsesToolsToChatTools` 丢弃所有非 function 工具
   (chatcompletions_responses_bridge.go),模型工具列表中从未出现 exec;
2. SSE 序列化 `responsesItemWire` 按 item 类型白名单重组字段,无
   custom_tool_call 分支,call_id/name/input 会被剥离
   (responses_stream_event_wire.go)。

次生缺陷:工具全部被丢弃时 tool_choice 仍被转发,部分上游报 400
"'tool_choice' is only allowed when 'tools' are specified"。

## 改动

(三个提交的要点清单,见各提交说明)

设计与 cc-switch 的 Codex chat 转换层(transform_codex_chat.rs /
streaming_codex_chat.rs)对齐:custom 工具降级为 {"input": string}
function 工具;custom 调用的参数增量不可流式还原为自由文本,缓冲至
收尾一次性下发 custom_tool_call_input.delta/done。

## 测试

- 新增 17 个单元测试:请求工具转换、tool_choice 兜底、历史项转换、
  非流式/流式回程还原、wire 序列化字段完整性、name 迟到宣告、命名摊平
- `go test ./internal/pkg/apicompat/...` 与 service 相关测试全绿
- 真机验证:Codex CLI 0.142.5 → 本桥 → GLM/DeepSeek 系 chat-only 上游,
  exec 实际执行命令并多轮续作正常

## 影响面

仅影响 "APIKey 账号 + 判定不支持 Responses" 的 chat 直转路径:

- OAuth 账号与透传路径不经过本桥(透传为字节级原文转发)
- CC→Responses 反方向仅构造 ResponsesRequest,新增字段带 omitempty
  且不赋值,序列化输出不变
- 探测逻辑未动,路由判定不变
- 共享类型改动均为增量容错(新字段 omitempty;UnmarshalJSON 只新增
  字符串工具的解析成功路径)
2026-07-07 16:23:44 +08:00
Wesley Liddick 17d991786b Merge pull request #3788 from fengshao1227/fix/codex-function-call-item-id
fix(codex): 剥离续链 function_call item 的非法 item_* id
2026-07-07 16:22:01 +08:00
Turtle_Li c10ae9f9d2 chore: remove batch image draft reports 2026-07-07 16:21:25 +08:00
li fd64d07e61 fix(codex): 剥离续链 function_call item 的非法 item_* id
OpenAI OAuth 转发续链请求时,function_call 等 call-input 类 item 的
id 被客户端以 item_* 形式回放,但上游要求以 fc 开头,返回 400
"Expected an ID that begins with 'fc'",导致工具续链反复失败。

filterCodexInputWithOptions 在 PreserveReferences=true 路径下对
call-input 类 item(function_call/tool_call/local_shell_call 等)
增加 id 前缀检查:非 fc 开头即删除。output 类(function_call_output
等)的 id 无此约束,不动。

Fixes #3785
2026-07-07 15:54:55 +08:00
shaw 17b6481f8b chore: update sponsors 2026-07-07 15:44:28 +08:00
NellPoi e979990bf9 add force priority fast policy action 2026-07-07 15:27:23 +08:00
shaw 156e17335f chore: update sponsors 2026-07-07 15:15:52 +08:00
Turtle_Li a6023840f7 Merge remote-tracking branch 'origin/main' into feature/batch-image-foundation
# Conflicts:
#	deploy/Dockerfile
2026-07-07 15:14:37 +08:00