From 6a77cf245e8f58c794512d85c3ab8d058a712f1e Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 28 Apr 2026 20:16:01 -0300 Subject: [PATCH] Integrated into release/v3.7.4 (PR #1742) --- .env.example | 4 ++ docs/ENVIRONMENT.md | 7 +- open-sse/services/claudeCodeCompatible.ts | 20 ++++-- public/sw.js | 12 ++-- .../dashboard/providers/[id]/page.tsx | 52 +++++++++++---- .../(dashboard)/dashboard/providers/page.tsx | 49 +++++++------- src/i18n/messages/en.json | 64 +++++++++---------- src/i18n/messages/zh-CN.json | 64 +++++++++---------- .../claude-code-compatible-request.test.ts | 20 ++++++ 9 files changed, 182 insertions(+), 110 deletions(-) diff --git a/.env.example b/.env.example index eb3dc31fc4..e9992bfb91 100644 --- a/.env.example +++ b/.env.example @@ -705,6 +705,10 @@ APP_LOG_TO_FILE=true # ── CC-compatible provider (experimental) ── # Enable the Claude Code compatible provider endpoint. +# This is only for third-party relays that accept Claude Code clients exclusively. +# OmniRoute rewrites requests to pass those relays' Claude Code client validation. +# If you only want to use Claude Code CLI, or you are not sure what these relays are, +# keep this disabled and add a regular Anthropic-compatible provider instead. # Used by: src/shared/utils/featureFlags.ts # ENABLE_CC_COMPATIBLE_PROVIDER=false diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index fddc751655..02e7c2843b 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -541,12 +541,17 @@ Automatic model pricing data synchronization from external sources. | `CLOUDFLARED_BIN` | auto-detect | `src/lib/cloudflaredTunnel.ts` | Custom path to `cloudflared` binary. | | `SEARCH_CACHE_TTL_MS` | `300000` (5 min) | `open-sse/services/searchCache.ts` | TTL for search API (Perplexity, Brave, etc.) response caching. | | `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE` | `false` | `src/app/api/providers/route.ts` | Allow multiple simultaneous connections per OpenAI-compatible provider. | -| `ENABLE_CC_COMPATIBLE_PROVIDER` | `false` | `src/shared/utils/featureFlags.ts` | Enable experimental Claude Code compatible provider endpoint. | +| `ENABLE_CC_COMPATIBLE_PROVIDER` | `false` | `src/shared/utils/featureFlags.ts` | Reveal the experimental CC-compatible provider UI for Claude Code-only relays. | | `CLIPROXYAPI_HOST` | `127.0.0.1` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge host (legacy integration). | | `CLIPROXYAPI_PORT` | `5544` | `open-sse/executors/cliproxyapi.ts` | CLIProxyAPI bridge port. | | `CLIPROXYAPI_CONFIG_DIR` | `~/.cli-proxy-api` | `src/lib/versionManager/processManager.ts` | CLIProxyAPI config directory. | | `LOCAL_HOSTNAMES` | _(empty)_ | `open-sse/config/providerRegistry.ts` | Comma-separated additional hostnames treated as "local" (Docker service names, etc.). | +`ENABLE_CC_COMPATIBLE_PROVIDER` is only for third-party relays that accept Claude Code clients +exclusively. OmniRoute rewrites requests so those relays accept them. If you only want to use +Claude Code CLI, or you are not sure what these relays are, keep this disabled and add a regular +Anthropic-compatible provider instead. + --- ## 21. Proxy Health diff --git a/open-sse/services/claudeCodeCompatible.ts b/open-sse/services/claudeCodeCompatible.ts index e747f3b544..c79a20fc5c 100644 --- a/open-sse/services/claudeCodeCompatible.ts +++ b/open-sse/services/claudeCodeCompatible.ts @@ -567,10 +567,7 @@ function buildClaudeCodeCompatibleSystemBlocks({ return preparedBlock; }); - const hasDefaultSystemBlock = preparedCustomSystemBlocks.some( - (block) => - block.type === "text" && block.text === CLAUDE_CODE_COMPATIBLE_DEFAULT_SYSTEM_BLOCKS[0].text - ); + const hasDefaultSystemBlock = containsDefaultSystemSkeleton(preparedCustomSystemBlocks); if (hasDefaultSystemBlock) return preparedCustomSystemBlocks; @@ -580,6 +577,21 @@ function buildClaudeCodeCompatibleSystemBlocks({ ]; } +function containsDefaultSystemSkeleton(blocks: Array>) { + const skeleton = CLAUDE_CODE_COMPATIBLE_DEFAULT_SYSTEM_BLOCKS; + if (skeleton.length === 0) return true; + if (blocks.length < skeleton.length) return false; + + return blocks.some((_, startIndex) => + skeleton.every((defaultBlock, offset) => { + const candidateBlock = blocks[startIndex + offset]; + if (!candidateBlock) return false; + + return Object.entries(defaultBlock).every(([key, value]) => candidateBlock[key] === value); + }) + ); +} + function convertClaudeCodeCompatibleMessage(message: MessageLike | null | undefined) { const rawRole = String(message?.role || "").toLowerCase(); const role = diff --git a/public/sw.js b/public/sw.js index d667507629..eaa1d86398 100644 --- a/public/sw.js +++ b/public/sw.js @@ -13,7 +13,7 @@ self.addEventListener("install", (event) => { caches .open(CACHE_NAME) .then((cache) => cache.addAll(APP_SHELL)) - .then(() => self.skipWaiting()), + .then(() => self.skipWaiting()) ); }); @@ -21,8 +21,10 @@ self.addEventListener("activate", (event) => { event.waitUntil( caches .keys() - .then((keys) => Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))) - .then(() => self.clients.claim()), + .then((keys) => + Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))) + ) + .then(() => self.clients.claim()) ); }); @@ -34,7 +36,7 @@ self.addEventListener("fetch", (event) => { const requestUrl = new URL(event.request.url); const isSameOrigin = requestUrl.origin === self.location.origin; const isExcludedPath = EXCLUDED_PATH_PREFIXES.some((prefix) => - requestUrl.pathname.startsWith(prefix), + requestUrl.pathname.startsWith(prefix) ); const destination = event.request.destination; const isStaticAsset = ["style", "script", "image", "font"].includes(destination); @@ -70,6 +72,6 @@ self.addEventListener("fetch", (event) => { void caches.open(CACHE_NAME).then((cache) => cache.put(event.request, responseClone)); } return networkResponse; - })(), + })() ); }); diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 75a73e591c..25ad3c7f7b 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -2730,7 +2730,7 @@ export default function ProviderDetailPage() { {isCompatible && providerNode && ( -
+

{isCcCompatible @@ -2743,7 +2743,7 @@ export default function ProviderDetailPage() { {getApiLabel()} · {(providerNode.baseUrl || "").replace(/\/$/, "")}/{getApiPath()}

-
+
@@ -2788,6 +2788,16 @@ export default function ProviderDetailPage() {
+ {isCcCompatible && ( +
+
+ + warning + +

{t("ccCompatibleValidationHint")}

+
+
+ )} )} @@ -5869,6 +5879,16 @@ function AddApiKeyModal({ onClose={onClose} >
+ {isCcCompatible && ( +
+
+ + warning + +

{t("ccCompatibleValidationHint")}

+
+
+ )}
)} - {isCompatible && ( + {isCompatible && !isCcCompatible && (

- {isCcCompatible - ? t("ccCompatibleValidationHint") - : isAnthropic - ? t("validationChecksAnthropicCompatible", { - provider: providerName || t("anthropicCompatibleName"), - }) - : t("validationChecksOpenAiCompatible", { - provider: providerName || t("openaiCompatibleName"), - })} + {isAnthropic + ? t("validationChecksAnthropicCompatible", { + provider: providerName || t("anthropicCompatibleName"), + }) + : t("validationChecksOpenAiCompatible", { + provider: providerName || t("openaiCompatibleName"), + })}

)}
)} @@ -1896,7 +1907,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr