2617 Commits

Author SHA1 Message Date
erio
fe1c6c958b feat(bedrock): add Claude Code compatibility for AWS Bedrock
- Export ApplyBedrockCCCompat() in GatewayService, called after channel
  model mapping to ensure mapped model ID is used for Opus 4.7+ detection
- Add sanitizeBedrockCCFields(): remove service_tier/interface_geo/
  context_management, inject max_tokens/anthropic_version defaults
- Add sanitizeBedrockCCBetaTokens(): filter anthropic_beta to keep only
  Bedrock-supported tokens, reusing autoInjectBedrockBetaTokens and
  filterBedrockBetaTokens for consistent rules
- Remove unsupported beta tokens (interleaved-thinking, context-management)
  from whitelist based on AWS official docs
- Simplify IsBedrockCCCompatEnabled() to check boolean toggle directly,
  applying CC compat to all accounts regardless of platform
- Add unit tests for IsBedrockCCCompatEnabled (8 cases),
  sanitizeBedrockCCFields (8 cases), sanitizeBedrockCCBetaTokens (7 cases)
- Update bedrock beta policy tests for removed auto-injection
2026-05-21 11:46:24 +08:00
Wesley Liddick
bd3d4d9a24
Merge pull request #2399 from gaoren002/fix/openai-image-upstream-errors
fix(openai): surface image moderation errors
2026-05-21 11:31:22 +08:00
Wesley Liddick
a60a349ecf
Merge pull request #2375 from gaoren002/fix/account-delete-scheduler-cache
fix: clear scheduler cache when deleting accounts
2026-05-21 11:31:05 +08:00
Wesley Liddick
131d4b3050
Merge pull request #2374 from gaoren002/fix/openai-refresh-token-reused
fix: mark reused refresh tokens non-retryable and unschedule errored accounts
2026-05-21 11:30:52 +08:00
Wesley Liddick
eda04c6129
Merge pull request #2615 from wucm667/feat/redeem-code-batch-update
feat(redeem): 兑换码支持批量修改
2026-05-21 10:39:46 +08:00
Wesley Liddick
d3c4e50753
Merge pull request #2645 from lyen1688/fix/trusted-forwarded-ip-acl
PR:为 API Key IP 白/黑名单增加可配置的反代真实 IP 判断
2026-05-21 10:34:28 +08:00
lyen1688
1d2445ff52 修复 API Key ACL 开关的 CI 校验 2026-05-20 23:51:39 +08:00
lyen1688
08c8c67df7 为 API Key ACL 增加反代真实 IP 开关 2026-05-20 22:51:46 +08:00
win
82bc1e199f chore: remove unused real-time log stream / request event bus
删除 fork 独有的实时日志相关功能(上游 Wei-Shaw/sub2api 不存在):

A. OpsLogBroadcaster + SSE 日志流(前端有用但用户不需要):
- backend/internal/service/ops_log_broadcaster{,_test}.go
- backend/internal/handler/ops_log_stream_middleware.go
- backend/internal/handler/admin/ops_log_stream_handler.go
- backend/internal/server/routes/admin.go: GET /admin/ops/logs/{stream,recent}
- backend/internal/server/routes/{gateway,windsurf_gateway}.go: opsLogStream middleware
- backend/internal/service/wire.go: ProvideOpsLogBroadcaster
- frontend/src/views/admin/ops/OpsLogStreamView.vue
- frontend/src/api/admin/ops.ts: subscribeOpsLogStream, getRecentOpsLogs,
  OpsLogEntry/OpsLogFilter/OpsLogRecentResponse 类型
- frontend/src/router/index.ts: AdminOpsLogStream 路由
- frontend/src/components/layout/AppSidebar.vue: 侧边栏入口
- frontend/src/i18n/locales/{en,zh}.ts: nav.opsLogStream + admin.ops.logStream 全部文案

B. RequestEventBus + WS 请求事件流(前端零调用 dead code):
- backend/internal/service/request_event_bus{,_test}.go
- backend/internal/handler/admin/ops_ws_requests_handler.go
- backend/internal/server/routes/admin.go: GET /admin/ops/ws/requests
- backend/internal/handler/gateway_handler.go: RequestEventBus 字段/参数 +
  reqStartTime + reqEventAccountID/reqEventStatus 跟踪 + defer Publish
- backend/internal/service/wire.go: NewRequestEventBus
- backend/internal/handler/admin/ops_handler.go: OpsHandler 中
  requestEventBus + logBroadcaster 字段,简化 NewOpsHandler 签名

保留:
- /admin/ops/ws/qps (前端 QPS 监控仍在用)
- /admin/ops/realtime-traffic (前端在用)
- OpsErrorLoggerMiddleware (与本次无关)

签名变更:
- NewOpsHandler(opsService) — 移除 requestEventBus, logBroadcaster
- NewGatewayHandler(...): 移除 requestEventBus 末位参数
- ProvideRouter / SetupRouter / registerRoutes / RegisterGatewayRoutes /
  RegisterWindsurfGatewayRoutes: 移除 opsLogBroadcaster 参数
- 同步更新 wire_gen.go + 测试调用点

验证:
- 后端 go build/vet 通过
- 前端 pnpm run build 通过 (9.48s)
- 测试: 2 个 baseline 既存失败 (TestProxyImportData...,
  TestWindsurfTierAccessService_Snapshot_HappyPath) 与本次无关
2026-05-20 22:43:20 +08:00
Wesley Liddick
e5d6f1727f
Merge pull request #2641 from Arron196/fix/channel-monitor-responses-reasoning
fix(channel-monitor): 兼容 Responses reasoning 输出
2026-05-20 22:36:46 +08:00
Wesley Liddick
f2d072ffc7
Merge pull request #2643 from Arron196/fix/ops-sla-local-client-errors
fix(ops): 排除本地客户端限制错误的 SLA 计数
2026-05-20 22:36:02 +08:00
win
502d57652f chore: merge upstream Wei-Shaw/sub2api v0.1.130-fix
Upstream 改动 (PR #2636):
- fix(gateway): retry on "thinking block must contain thinking" upstream error
  跨模型切换时,其他模型回过的 assistant 历史里有 type=thinking 但缺
  thinking 文本,送给开启 extended thinking 的 Claude 会被拒,通过
  signature error 重试机制可恢复。

修改文件: backend/internal/service/gateway_service.go (+9),
gateway_request_test.go (+31 测试)
2026-05-20 22:06:26 +08:00
benjamin
69305a6091 fix(ops): 排除本地客户端限制错误的 SLA 计数
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 22:01:33 +08:00
erio
4fd21994c5 feat(bedrock): add Claude Code compatibility transformations for Bedrock accounts
Add channel-level Bedrock CC compatibility toggle (similar to web_search_emulation)
that fixes 4 types of Bedrock 400 errors seen with Claude Code:

1. thinking.type "enabled" → "adaptive" for Opus 4.7+ (only supports adaptive)
2. Add default budget_tokens when missing for older models
3. Replace illegal characters in tool_use IDs to match Bedrock's ^[a-zA-Z0-9_-]+$ pattern
4. anthropic_version / invalid beta flag (already handled elsewhere)

Transformations run in Forward() before any forwarding path, so both native Bedrock
accounts and apikey passthrough accounts pointing to Bedrock relays benefit.

Includes channel-level toggle UI and unit tests.
2026-05-20 21:47:38 +08:00
benjamin
d3d5843b9d fix(channel-monitor): 兼容 Responses reasoning 输出
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 21:19:06 +08:00
name
8211aa7066 fix: retry on "thinking block must contain thinking" upstream error
Some clients reuse assistant history from other models when switching to
claude with extended thinking enabled. If a prior thinking block lacks the
thinking text field, upstream returns:
  messages.X.content.Y.thinking: each thinking block must contain thinking

Add this pattern to isThinkingBlockSignatureError so the existing
FilterThinkingBlocksForRetry retry path triggers and rewrites/drops the
offending blocks.
2026-05-20 18:46:50 +08:00
win
92433656f5 chore: merge upstream Wei-Shaw/sub2api v0.1.128 — keep fork customizations
Upstream 新功能 (34 commits, ~main..origin/main):
- feat(email): 通知邮件模板服务、模板编辑器、订阅/余额提醒邮件
- feat(notification): NotificationEmailService 注入到 Balance/Payment/Setting
- feat(payment): 支付成功通知邮件
- feat(usage): 用户 API Key 用量页支持按日明细
- feat(openai-gateway): Codex OAuth 浏览器 UA 自动改写规避 Cloudflare 质询
- feat(admin): 邮件模板管理接口
- fix(auth): 停用/删除分组后阻断 API Key
- fix(group): 修正分组账号可用计数口径
- fix(openai): /v1/responses respect force chat completions, images n 参数透传
- test(repository): AES Encryptor 单元测试
- chore: VERSION 0.1.128

冲突解决 (backend/cmd/server/wire_gen.go):
- 引入 upstream 新 wire providers: notificationEmailService,
  ProvidePaymentService(10 args), ProvideAdminSettingHandler(8 args)
- 保留 fork 独有依赖: rpmTokenBucketService (RPM 平滑),
  NewOpsHandler 3 参数版本 (requestEventBus, opsLogBroadcaster)
- ProvideBalanceNotifyService 接受 4 参数 (含 notificationEmailService)

修复 session-id helper 设计 (claude_code_session_id.go):
- 发现: TestGatewayService_AnthropicOAuth_InjectsClaudeCodeSessionHeaderFromMetadata
  在 OAuth + mimicClaudeCode=false 场景失败
- 重新审视设计原则: OAuth 凭证本身就是 Claude Code 客户端,可信任 metadata
  派生 session_id;不应受 mimicClaudeCode 标志阻止
- 修复: metadata 派生只看 tokenType=="oauth";UUID 兜底仍需 oauth && mimic
- 更新测试: OAuthNonMimicDerivesFromMetadata 取代原 IgnoresMetadata

所有 fork 独有功能保留:
- Claude Code 2.1.145 mimicry bundle (上个 commit 引入)
- RPM token bucket smoothing (commit 95814974)
- Windsurf/Antigravity/Omniroute 定制
- claudemask/ 校验包 (upstream 已删除,我们仍在 gateway_service 中使用)

不在范围:
- 不修复 baseline 既存的 2 个测试失败 (TestProxyImportData...,
  TestWindsurfTierAccessService_Snapshot_HappyPath) - 与 merge 无关
2026-05-20 17:50:44 +08:00
gaoren002
202aab8e63 fix(accounts): unschedule errored accounts 2026-05-20 09:24:51 +00:00
gaoren002
49b415e333 fix: mark reused refresh tokens non-retryable 2026-05-20 09:24:51 +00:00
gaoren002
60f6602b81 fix: clear scheduler cache when deleting accounts 2026-05-20 09:24:29 +00:00
gaoren002
888cd8092d fix(openai): surface image moderation errors 2026-05-20 09:19:20 +00:00
win
0fefedf9cd feat(claude-mimic): upgrade Claude Code mimicry to 2.1.145 via bundle abstraction
反编译本地 Claude Code 2.1.145 二进制 (Bun 1.3.14 打包,
@anthropic-ai/sdk@0.94.0 嵌入) 提取真实指纹,系统性升级 mimicry。

核心改动:
- 新增 ClaudeCodeBundle struct 作为单一事实源,DefaultBundle 描述当前
  伪装目标的完整快照 (CLIVersion/SDKVersion/RuntimeVersion/OS/Arch)
- DefaultCLIVersion/DefaultStainlessPackageVersion/CLICurrentVersion/
  DefaultHeaders 全部派生自 DefaultBundle,消除三处 (2.1.92, 2.1.104,
  0.70.0, 0.81.0) 版本分裂
- CLI 版本 2.1.92/2.1.104 -> 2.1.145
- SDK 版本 0.70.0/0.81.0 -> 0.94.0
- 新增 12 个 2.1.145 反编译确认的 anthropic-beta token:
  advanced-tool-use, tool-search-tool, mcp-servers, mcp-client,
  mid-conversation-system, afk-mode, cache-diagnosis, context-hint,
  environments, managed-agents, skills, compact
- FullClaudeCodeMimicryBetas() 从 7 个 token 升级到 21 个 ordered list
- 修正 BetaTokenEfficientTools 错日期 (2026-03-28 -> 2025-02-19)
- 从默认 beta header 移除已 GA 的 BetaFineGrainedToolStreaming /
  BetaTokenEfficientTools (常量保留供客户端显式 merge)
- claudemask.RequiredNodeHeaders 加 X-Claude-Code-Session-Id 强制

新增 ensureClaudeCodeSessionID helper (claude_code_session_id.go):
- 真实 CLI 在 SDK 内强制 X-Claude-Code-Session-Id:y_(),缺失被判第三方
- OAuth mimic 路径: metadata.user_id 派生 -> canonical UUID 写入
  -> 兜底 uuid.NewString()
- API key passthrough 路径: 不从 body 派生,保护客户端原始语义
- 所有路径均对客户端传入的非法 UUID 执行删除 (避免恶意值上游透传)
- 所有写入 header 的 session-id 都通过 uuid.Parse 校验

测试:
- 新增 14 个 ensureClaudeCodeSessionID 单元测试,含恶意 UUID 注入拒绝
  + API key 路径隔离 + canonical 形式校验
- 新增 3 个 bundle 派生一致性测试
- mask_test 加 session-id 缺失校验 case
- 老 UA 断言 2.1.104 -> 2.1.145

不在范围:
- TLS 指纹 (utls 已处理)
- Bun.hash vs xxHash64 算法验证 (需 golden vectors,独立项目)

References:
- VERSION:2.1.145 BUILD_TIME:2026-05-19T01:36:35Z
  GIT_SHA:daa4c3755d45ab0cf97bb41db8c03bd2dfd2ff5f
2026-05-20 17:18:47 +08:00
Wesley Liddick
51f72186a5
Merge pull request #2613 from wucm667/feat/api-key-usage-daily-detail
feat(usage): 用户 API Key 用量页支持按日明细
2026-05-20 16:55:42 +08:00
Wesley Liddick
a6db05c824
Merge pull request #2612 from wucm667/fix/group-status-key-auth-block
fix(auth): 停用/删除分组后阻断已发放 API Key 的请求
2026-05-20 16:55:08 +08:00
Wesley Liddick
655e157658
Merge pull request #2611 from wucm667/test/repo-aes-encryptor
test(repository): 补充 AES Encryptor 单元测试
2026-05-20 16:54:33 +08:00
shaw
df2b02e61c fix: 修正分组账号可用计数口径 2026-05-20 16:53:23 +08:00
shaw
dd4d482a70 fix email reminder dedup keys 2026-05-20 16:40:18 +08:00
wucm667
3263ca63c7 feat(redeem): add redeem code batch update 2026-05-20 16:08:41 +08:00
wucm667
22ff1acde3 fix(auth): 停用/删除分组后阻断 API Key 2026-05-20 15:52:00 +08:00
wucm667
90b2b2a757 feat(usage): 用户 API Key 用量页支持按日明细 2026-05-20 15:48:38 +08:00
wucm667
cbdfedab38 test(repository): 补充 AES Encryptor 单元测试
为 AESEncryptor(AES-256-GCM)新增纯单元测试,覆盖:
- NewAESEncryptor:合法 32 字节密钥、错误密钥长度(16/24/其他)、空 key 与非法 hex 三条路径
- 加解密往返:ASCII、中文多字节、空字符串、长字符串(> 1 KB)、特殊字符
- Nonce 随机性:相同明文 30 次加密均产生不同密文
- Decrypt 错误路径:非 base64 输入、长度过短、篡改密文体、篡改 GCM 标签
- 跨实例:相同密钥可互解,不同密钥不可互解

仅新增测试文件,不修改任何业务代码。
带 //go:build unit tag,与 go test -tags=unit 入口一致。
2026-05-20 15:44:00 +08:00
Wesley Liddick
7ec61eb2f5
Merge pull request #2606 from wucm667/fix/openai-responses-respect-force-chat-completions
fix(openai): /v1/responses 入口尊重 force_chat_completions 设置
2026-05-20 15:13:43 +08:00
Wesley Liddick
378a0a6a61
Merge pull request #2599 from Arron196/feature/email-template-editor
feat: 添加邮件模板编辑器与通知邮件模板化
2026-05-20 15:12:57 +08:00
Wesley Liddick
1222d75119
Merge pull request #2595 from wucm667/fix/group-list-available-account-count
test(group): 补充分组列表账号统计回归测试
2026-05-20 15:11:08 +08:00
shaw
825834b5cb test(api-contract): fix admin/settings expected map missing openai_codex_user_agent 2026-05-20 15:07:07 +08:00
Wesley Liddick
fb2098018e
Merge pull request #2593 from wucm667/fix/openai-images-n-param
fix(openai-images): 修复 /v1/images/generations 的 n 参数不生效
2026-05-20 14:35:06 +08:00
shaw
878ad3b569 feat(openai-gateway): Codex OAuth 账号浏览器 UA 自动改写规避 Cloudflare
质询
2026-05-20 14:33:51 +08:00
wucm667
cae93ae137 fix(openai): /v1/responses respect force chat completions 2026-05-20 14:17:26 +08:00
benjamin
e1b53fdeba fix(email): 补全模板 helper 写入错误检查
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:40:32 +08:00
benjamin
12f324688f feat(auth): 在认证服务中保留邮件 locale
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:25:58 +08:00
benjamin
efa1994233 feat(auth): 透传验证码邮件语言偏好
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:25:45 +08:00
benjamin
a6bb6d481b fix(ops): 收紧运维邮件 fallback 和去重
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:25:32 +08:00
benjamin
3fdd5cbaef fix(email): 避免模板投递错误重复 fallback
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:25:18 +08:00
benjamin
7e0b22ceb6 feat(email): 扩展邮件模板错误处理
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 13:25:03 +08:00
win
158785bfc9 chore: merge upstream v0.1.127 — keep omniroute customizations
Upstream highlights:
- v0.1.127 release (150 commits): channel-monitor 协议管理、OpenAI
  Responses 路由配置、模型定价 LiteLLM 默认、payment 强制扫码、
  钉钉 OAuth、用户用量按平台拆分、Ops 错误分类 SLA 调整、
  Anthropic passthrough keepalive、Gemini chat completions 路由 ...
- 91da8159 feat(risk-control): 内容审计新增关键词拦截
- 3d22dd34 feat: gemini-3.5-flash 模型支持

Conflicts resolved:
- Dockerfile: keep pnpm pin to 9.15.9 (upstream pinned generic v9 floating).
- wire_gen.go: combine upstream NewSettingHandler(+userAttributeService)
  with local NewOpsHandler(opsService, requestEventBus, opsLogBroadcaster).
  Verified by re-running wire generate.
- scheduler_cache.go: keep both upstream openai_responses_{mode,supported}
  keys and local model_rate_limits key in filterSchedulerExtra().
- gateway_service.go: keep local context-compression block; drop now-dead
  setOpsUpstreamRequestBody call (upstream removed ops retry replay).
- docker-compose.yml: keep local windsurf-ls service profile and named
  volumes; keep local healthcheck start_period values.

Test mock signatures bumped to match current constructors:
- gateway_models_test.go: add nil for RPMTokenBucketService.
- account_handler_available_models_test.go: add nil for windsurfChatService.
2026-05-20 12:39:40 +08:00
win
27f2b442cb chore: skip AppleDouble migrations + Alpine mirror for CN builds
- migrations_runner: filter ._* / .hidden.sql so macOS metadata files don't
  break the migration scanner; new helper + regression tests cover this.
- Docker images switch Alpine repo to mirrors.aliyun.com (CN build speed)
  and pin frontend pnpm to 9.15.9 to dodge v10 interactive build prompts.
- dockerignore (root + backend): exclude **/.DS_Store and ._* AppleDouble
  artefacts so they don't sneak into image context.
2026-05-20 12:15:25 +08:00
wucm667
5465003d07 test(group): 补充分组列表可用账号数与总账号数统计正确性的集成测试
修复 #2579 报告的可用账号数等于总数问题:
上游已通过 loadAccountCounts / GetAccountCount 两处 SQL 中的
  COUNT(*) FILTER (WHERE status='active' AND schedulable=true)
正确区分可用账号,但缺少覆盖 active < total 场景的测试,
导致回归容易被忽略。

新增三个集成测试:
- TestListWithFilters_ActiveAccountCount_LessThanTotal
    含 active+schedulable、disabled、active+unschedulable 三类账号,
    断言 AccountCount=3、ActiveAccountCount=1,
    并验证 GetAccountCount 返回值与 ListWithFilters 字段一致。
- TestListWithFilters_RateLimitedAccountCount
    验证 rate_limit_reset_at 未过期的账号计入 ActiveAccountCount(仍可调度),
    同时单独出现在 RateLimitedAccountCount 中。
- TestListWithAccountCountSort_AttachesActiveCount
    通过 SortBy=account_count 触发 listWithAccountCountSort 路径,
    验证排序按 total 而非 active,且两个字段均被正确附加。

Fixes #2579
2026-05-20 11:33:29 +08:00
wucm667
2c14efeaa0 fix(openai-images): 修复图片生成 n 参数透传 2026-05-20 11:28:28 +08:00
shaw
91da815993 feat(risk-control): 内容审计新增关键词拦截 2026-05-20 11:13:53 +08:00
benjamin
8cef9a7ab1 chore(wire): 注入通知邮件服务
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-20 11:09:18 +08:00