Integrated into release/v3.7.4 (PR #1742)

This commit is contained in:
diegosouzapw
2026-04-28 20:16:01 -03:00
parent 23ec621865
commit 6a77cf245e
9 changed files with 182 additions and 110 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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<Record<string, unknown>>) {
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 =

View File

@@ -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;
})(),
})()
);
});

View File

@@ -2730,7 +2730,7 @@ export default function ProviderDetailPage() {
{isCompatible && providerNode && (
<Card>
<div className="flex items-center justify-between mb-4">
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div>
<h2 className="text-lg font-semibold">
{isCcCompatible
@@ -2743,7 +2743,7 @@ export default function ProviderDetailPage() {
{getApiLabel()} · {(providerNode.baseUrl || "").replace(/\/$/, "")}/{getApiPath()}
</p>
</div>
<div className="flex items-center gap-2">
<div className="flex flex-wrap items-center gap-2">
<Button size="sm" icon="add" onClick={() => setShowAddApiKeyModal(true)}>
{t("add")}
</Button>
@@ -2788,6 +2788,16 @@ export default function ProviderDetailPage() {
</Button>
</div>
</div>
{isCcCompatible && (
<div className="mb-4 rounded-lg border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-sm text-text-muted">
<div className="flex items-start gap-2">
<span className="material-symbols-outlined mt-0.5 text-[18px] text-amber-500">
warning
</span>
<p>{t("ccCompatibleValidationHint")}</p>
</div>
</div>
)}
</Card>
)}
@@ -5869,6 +5879,16 @@ function AddApiKeyModal({
onClose={onClose}
>
<div className="flex flex-col gap-4">
{isCcCompatible && (
<div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-sm text-text-muted">
<div className="flex items-start gap-2">
<span className="material-symbols-outlined mt-0.5 text-[18px] text-amber-500">
warning
</span>
<p>{t("ccCompatibleValidationHint")}</p>
</div>
</div>
)}
<Input
label={t("nameLabel")}
value={formData.name}
@@ -5929,17 +5949,15 @@ function AddApiKeyModal({
/>
</div>
)}
{isCompatible && (
{isCompatible && !isCcCompatible && (
<p className="text-xs text-text-muted">
{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"),
})}
</p>
)}
<button
@@ -6968,6 +6986,16 @@ function EditCompatibleNodeModal({
onClose={onClose}
>
<div className="flex flex-col gap-4">
{isCcCompatible && (
<div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-sm text-text-muted">
<div className="flex items-start gap-2">
<span className="material-symbols-outlined mt-0.5 text-[18px] text-amber-500">
warning
</span>
<p>{t("ccCompatibleValidationHint")}</p>
</div>
</div>
)}
<Input
label={t("nameLabel")}
value={formData.name}

View File

@@ -1044,7 +1044,6 @@ export default function ProvidersPage() {
<AddCcCompatibleModal
isOpen={showAddCcCompatibleModal}
addLabel={addCcCompatibleLabel}
compatibleLabel={ccCompatibleLabel}
onClose={() => setShowAddCcCompatibleModal(false)}
onCreated={(node) => {
setProviderNodes((prev) => [...prev, node]);
@@ -1752,12 +1751,12 @@ AddAnthropicCompatibleModal.propTypes = {
onCreated: PropTypes.func.isRequired,
};
function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCreated }) {
function AddCcCompatibleModal({ isOpen, addLabel, onClose, onCreated }) {
const t = useTranslations("providers");
const [formData, setFormData] = useState({
name: "",
prefix: "",
baseUrl: "https://api.anthropic.com",
baseUrl: "",
chatPath: CC_COMPATIBLE_DEFAULT_CHAT_PATH,
});
const [submitting, setSubmitting] = useState(false);
@@ -1765,6 +1764,10 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
const [validating, setValidating] = useState(false);
const [validationResult, setValidationResult] = useState<"success" | "failed" | null>(null);
const [showAdvanced, setShowAdvanced] = useState(false);
const hasRequiredFields = Boolean(
formData.name.trim() && formData.prefix.trim() && formData.baseUrl.trim()
);
const canValidate = Boolean(checkKey.trim() && formData.baseUrl.trim());
useEffect(() => {
if (isOpen) {
@@ -1774,7 +1777,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
}, [isOpen]);
const handleSubmit = async () => {
if (!formData.name.trim() || !formData.prefix.trim() || !formData.baseUrl.trim()) return;
if (!hasRequiredFields) return;
setSubmitting(true);
try {
const res = await fetch("/api/provider-nodes", {
@@ -1795,7 +1798,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
setFormData({
name: "",
prefix: "",
baseUrl: "https://api.anthropic.com",
baseUrl: "",
chatPath: CC_COMPATIBLE_DEFAULT_CHAT_PATH,
});
setCheckKey("");
@@ -1835,26 +1838,34 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
return (
<Modal isOpen={isOpen} title={addLabel} onClose={onClose}>
<div className="flex flex-col gap-4">
<div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-3 py-2 text-sm text-text-muted">
<div className="flex items-start gap-2">
<span className="material-symbols-outlined mt-0.5 text-[18px] text-amber-500">
warning
</span>
<p>{t("ccCompatibleValidationHint")}</p>
</div>
</div>
<Input
label={t("nameLabel")}
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder={t("compatibleProdPlaceholder", { type: compatibleLabel })}
hint={t("nameHint")}
placeholder={t("ccCompatibleNamePlaceholder")}
hint={t("ccCompatibleNameHint")}
/>
<Input
label={t("prefixLabel")}
value={formData.prefix}
onChange={(e) => setFormData({ ...formData, prefix: e.target.value })}
placeholder="cc-prod"
hint={t("prefixHint")}
placeholder={t("ccCompatiblePrefixPlaceholder")}
hint={t("ccCompatiblePrefixHint")}
/>
<Input
label={t("baseUrlLabel")}
value={formData.baseUrl}
onChange={(e) => setFormData({ ...formData, baseUrl: e.target.value })}
placeholder="https://api.anthropic.com"
hint={t("compatibleBaseUrlHint", { type: compatibleLabel })}
placeholder={t("ccCompatibleBaseUrlPlaceholder")}
hint={t("ccCompatibleBaseUrlHint")}
/>
<button
type="button"
@@ -1881,7 +1892,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
value={formData.chatPath}
onChange={(e) => setFormData({ ...formData, chatPath: e.target.value })}
placeholder={CC_COMPATIBLE_DEFAULT_CHAT_PATH}
hint={t("chatPathHint")}
hint={t("ccCompatibleChatPathHint")}
/>
</div>
)}
@@ -1896,7 +1907,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
<div className="pt-6">
<Button
onClick={handleValidate}
disabled={!checkKey || validating || !formData.baseUrl.trim()}
disabled={!canValidate || validating}
variant="secondary"
>
{validating ? t("checking") : t("check")}
@@ -1909,16 +1920,7 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
</Badge>
)}
<div className="flex gap-2">
<Button
onClick={handleSubmit}
fullWidth
disabled={
!formData.name.trim() ||
!formData.prefix.trim() ||
!formData.baseUrl.trim() ||
submitting
}
>
<Button onClick={handleSubmit} fullWidth disabled={!hasRequiredFields || submitting}>
{submitting ? t("creating") : t("add")}
</Button>
<Button onClick={onClose} variant="ghost" fullWidth>
@@ -1933,7 +1935,6 @@ function AddCcCompatibleModal({ isOpen, addLabel, compatibleLabel, onClose, onCr
AddCcCompatibleModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
addLabel: PropTypes.string.isRequired,
compatibleLabel: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onCreated: PropTypes.func.isRequired,
};

View File

@@ -270,7 +270,7 @@
"editCombo": "Edit Combo",
"testResults": "Test Results",
"searchQuery": "Search Query",
"addCcCompatible": "Add Cc Compatible",
"addCcCompatible": "Add CC Compatible",
"duplicate": "Duplicate",
"createCombo": "Create Combo",
"searchTypeWeb": "Search Type Web",
@@ -344,7 +344,7 @@
"signatureDefaults": "Signature Defaults",
"errorCreating": "Error Creating",
"timeRangeYear": "Time Range Year",
"compatibleLabel": "Compatible Label",
"compatibleLabel": "Compatible",
"cloudDisabledSuccess": "Cloud Disabled Success",
"deleteConfirm": "Delete Confirm",
"check": "Check",
@@ -511,7 +511,7 @@
"comboUpdated": "Combo Updated",
"weighted": "Weighted",
"providers": "Providers",
"ccCompatibleLabel": "Cc Compatible Label",
"ccCompatibleLabel": "CC Compatible",
"noFallbackChainsDesc": "No Fallback Chains Desc",
"yesImport": "Yes Import",
"lockoutsAutoRefreshHint": "Lockouts Auto Refresh Hint",
@@ -2705,7 +2705,7 @@
"accountIdLabel": "Account Id Label",
"accountIdPlaceholder": "Account Id Placeholder",
"addAnotherApiKey": "Add Another Api Key",
"addCcCompatible": "Add Cc Compatible",
"addCcCompatible": "Add CC Compatible",
"aggregatorsGateways": "Aggregators Gateways",
"apiFormatLabel": "Api Format Label",
"apiKeyOptionalHint": "Api Key Optional Hint",
@@ -2722,27 +2722,27 @@
"bailianBaseUrlHint": "Bailian Base Url Hint",
"blackboxWebCookieHint": "Blackbox Web Cookie Hint",
"blackboxWebCookiePlaceholder": "Blackbox Web Cookie Placeholder",
"blockClaudeExtraUsageDescription": "Block Claude Extra Usage Description",
"blockClaudeExtraUsageLabel": "Block Claude Extra Usage Label",
"ccCompatibleBaseUrlHint": "Cc Compatible Base Url Hint",
"ccCompatibleBaseUrlPlaceholder": "Cc Compatible Base Url Placeholder",
"ccCompatibleChatPathHint": "Cc Compatible Chat Path Hint",
"ccCompatibleContext1mDescription": "Cc Compatible Context1M Description",
"ccCompatibleContext1mLabel": "Cc Compatible Context1M Label",
"ccCompatibleDetailsTitle": "Cc Compatible Details Title",
"ccCompatibleLabel": "Cc Compatible Label",
"ccCompatibleModelsDescription": "Cc Compatible Models Description",
"ccCompatibleNameHint": "Cc Compatible Name Hint",
"ccCompatibleNamePlaceholder": "Cc Compatible Name Placeholder",
"ccCompatiblePrefixHint": "Cc Compatible Prefix Hint",
"ccCompatiblePrefixPlaceholder": "Cc Compatible Prefix Placeholder",
"ccCompatibleValidationHint": "Cc Compatible Validation Hint",
"claudeExtraUsageShort": "Claude Extra Usage Short",
"claudeExtraUsageToggleTitle": "Claude Extra Usage Toggle Title",
"codex5hToggleTitle": "Codex5H Toggle Title",
"blockClaudeExtraUsageDescription": "Hide extra Claude usage rows reported by some providers when they duplicate primary token accounting.",
"blockClaudeExtraUsageLabel": "Block duplicate Claude usage rows",
"ccCompatibleBaseUrlHint": "Base URL for a Claude Code-only relay. Do not include /messages.",
"ccCompatibleBaseUrlPlaceholder": "https://relay.example.com/v1",
"ccCompatibleChatPathHint": "Defaults to Claude Code's strict Messages API path. Change only if your relay documents a different path.",
"ccCompatibleContext1mDescription": "Adds the context-1m beta header when the selected Claude model supports it.",
"ccCompatibleContext1mLabel": "Enable 1M context beta",
"ccCompatibleDetailsTitle": "CC-compatible relay details",
"ccCompatibleLabel": "CC Compatible",
"ccCompatibleModelsDescription": "CC-compatible relays do not expose model listing. Add the Claude model IDs your relay accepts.",
"ccCompatibleNameHint": "Display name for this Claude Code-only relay.",
"ccCompatibleNamePlaceholder": "CC Relay Production",
"ccCompatiblePrefixHint": "Used in model aliases such as prefix/model-id.",
"ccCompatiblePrefixPlaceholder": "cc",
"ccCompatibleValidationHint": "Use this provider only for relays that serve Claude Code clients exclusively. OmniRoute rewrites any incoming request into the Claude Code-compatible wire format so those relays accept it. If you only want to use Claude Code CLI, or you are not sure what this relay type means, use a regular Anthropic-compatible provider instead.",
"claudeExtraUsageShort": "Extra usage",
"claudeExtraUsageToggleTitle": "Block Claude extra usage accounting for this connection",
"codex5hToggleTitle": "Track Codex 5-hour quota for this connection",
"codexFastServiceTierDescription": "Use the priority service tier for Codex requests when available.",
"codexFastServiceTierLabel": "Codex fast service tier",
"codexWeeklyToggleTitle": "Codex Weekly Toggle Title",
"codexWeeklyToggleTitle": "Track Codex weekly quota for this connection",
"compatUpstreamHeaderNamePlaceholder": "Compat Upstream Header Name Placeholder",
"compatUpstreamHeaderValuePlaceholder": "Compat Upstream Header Value Placeholder",
"compatible": "Compatible",
@@ -2750,8 +2750,8 @@
"consoleApiKeyOracleHint": "Console Api Key Oracle Hint",
"consoleApiKeyOracleLabel": "Console Api Key Oracle Label",
"consoleApiKeyOraclePlaceholder": "Console Api Key Oracle Placeholder",
"cpaModeDisabledTitle": "Cpa Mode Disabled Title",
"cpaModeEnabledTitle": "Cpa Mode Enabled Title",
"cpaModeDisabledTitle": "CLIProxyAPI compatibility mode is disabled",
"cpaModeEnabledTitle": "CLIProxyAPI compatibility mode is enabled",
"customUserAgentHint": "Custom User Agent Hint",
"customUserAgentLabel": "Custom User Agent Label",
"databricksBaseUrlHint": "Databricks Base Url Hint",
@@ -2812,18 +2812,18 @@
"sessionCookieLabel": "Session Cookie Label",
"showEmail": "Show Email",
"snowflakeBaseUrlHint": "Snowflake Base Url Hint",
"supportedEndpointAudio": "Supported Endpoint Audio",
"supportedEndpointChat": "Supported Endpoint Chat",
"supportedEndpointEmbeddings": "Supported Endpoint Embeddings",
"supportedEndpointImages": "Supported Endpoint Images",
"supportedEndpointsLabel": "Supported Endpoints Label",
"supportedEndpointAudio": "Audio",
"supportedEndpointChat": "Chat",
"supportedEndpointEmbeddings": "Embeddings",
"supportedEndpointImages": "Images",
"supportedEndpointsLabel": "Supported endpoints",
"tagGroupHint": "Tag Group Hint",
"tagGroupLabel": "Tag Group Label",
"tagGroupPlaceholder": "Tag Group Placeholder",
"testModel": "Test Model",
"testingModel": "Testing Model",
"toggleOffShort": "Toggle Off Short",
"toggleOnShort": "Toggle On Short",
"toggleOffShort": "Off",
"toggleOnShort": "On",
"tokenExpiredBadge": "Token Expired Badge",
"tokenExpiredTitle": "Token Expired Title",
"tokenExpiresSoonTitle": "Token Expires Soon Title",

View File

@@ -270,7 +270,7 @@
"editCombo": "Edit Combo",
"testResults": "Test Results",
"searchQuery": "Search Query",
"addCcCompatible": "Add Cc Compatible",
"addCcCompatible": "添加 CC 兼容",
"duplicate": "Duplicate",
"createCombo": "Create Combo",
"searchTypeWeb": "Search Type Web",
@@ -344,7 +344,7 @@
"signatureDefaults": "Signature Defaults",
"errorCreating": "Error Creating",
"timeRangeYear": "Time Range Year",
"compatibleLabel": "Compatible Label",
"compatibleLabel": "兼容",
"cloudDisabledSuccess": "Cloud Disabled Success",
"deleteConfirm": "Delete Confirm",
"check": "Check",
@@ -511,7 +511,7 @@
"comboUpdated": "Combo Updated",
"weighted": "Weighted",
"providers": "Providers",
"ccCompatibleLabel": "Cc Compatible Label",
"ccCompatibleLabel": "CC 兼容",
"noFallbackChainsDesc": "No Fallback Chains Desc",
"yesImport": "Yes Import",
"lockoutsAutoRefreshHint": "Lockouts Auto Refresh Hint",
@@ -2642,7 +2642,7 @@
"accountIdLabel": "Account Id Label",
"accountIdPlaceholder": "Account Id Placeholder",
"addAnotherApiKey": "Add Another Api Key",
"addCcCompatible": "Add Cc Compatible",
"addCcCompatible": "添加 CC 兼容",
"aggregatorsGateways": "Aggregators Gateways",
"apiFormatLabel": "Api Format Label",
"apiKeyOptionalHint": "Api Key Optional Hint",
@@ -2659,27 +2659,27 @@
"bailianBaseUrlHint": "Bailian Base Url Hint",
"blackboxWebCookieHint": "Blackbox Web Cookie Hint",
"blackboxWebCookiePlaceholder": "Blackbox Web Cookie Placeholder",
"blockClaudeExtraUsageDescription": "Block Claude Extra Usage Description",
"blockClaudeExtraUsageLabel": "Block Claude Extra Usage Label",
"ccCompatibleBaseUrlHint": "Cc Compatible Base Url Hint",
"ccCompatibleBaseUrlPlaceholder": "Cc Compatible Base Url Placeholder",
"ccCompatibleChatPathHint": "Cc Compatible Chat Path Hint",
"ccCompatibleContext1mDescription": "Cc Compatible Context1M Description",
"ccCompatibleContext1mLabel": "Cc Compatible Context1M Label",
"ccCompatibleDetailsTitle": "Cc Compatible Details Title",
"ccCompatibleLabel": "Cc Compatible Label",
"ccCompatibleModelsDescription": "Cc Compatible Models Description",
"ccCompatibleNameHint": "Cc Compatible Name Hint",
"ccCompatibleNamePlaceholder": "Cc Compatible Name Placeholder",
"ccCompatiblePrefixHint": "Cc Compatible Prefix Hint",
"ccCompatiblePrefixPlaceholder": "Cc Compatible Prefix Placeholder",
"ccCompatibleValidationHint": "Cc Compatible Validation Hint",
"claudeExtraUsageShort": "Claude Extra Usage Short",
"claudeExtraUsageToggleTitle": "Claude Extra Usage Toggle Title",
"codex5hToggleTitle": "Codex5H Toggle Title",
"blockClaudeExtraUsageDescription": "隐藏部分 Provider 返回的重复 Claude 额外用量记录,避免和主 token 统计重复。",
"blockClaudeExtraUsageLabel": "屏蔽重复 Claude 用量",
"ccCompatibleBaseUrlHint": "Claude Code 专用中转站的 Base URL不要包含 /messages。",
"ccCompatibleBaseUrlPlaceholder": "https://relay.example.com/v1",
"ccCompatibleChatPathHint": "默认使用 Claude Code 严格的 Messages API 路径。仅在中转站文档要求时修改。",
"ccCompatibleContext1mDescription": "当所选 Claude 模型支持时,添加 context-1m beta header。",
"ccCompatibleContext1mLabel": "启用 1M context beta",
"ccCompatibleDetailsTitle": "CC 兼容中转站详情",
"ccCompatibleLabel": "CC 兼容",
"ccCompatibleModelsDescription": "CC 兼容中转站不提供模型列表。请添加该中转站接受的 Claude 模型 ID。",
"ccCompatibleNameHint": "这个 Claude Code 专用中转站的显示名称。",
"ccCompatibleNamePlaceholder": "CC 中转站生产环境",
"ccCompatiblePrefixHint": "用于 prefix/model-id 这类模型别名。",
"ccCompatiblePrefixPlaceholder": "cc",
"ccCompatibleValidationHint": "这个 Provider 只适用于仅向 Claude Code 客户端提供服务的中转站。OmniRoute 会把任何进入的请求改写为 Claude Code 兼容的传输格式,以通过这些中转站的验证。如果你只是想使用 Claude Code CLI或者不清楚这类中转站是什么请使用普通 Anthropic-compatible Provider。",
"claudeExtraUsageShort": "额外用量",
"claudeExtraUsageToggleTitle": "为此连接屏蔽 Claude 额外用量统计",
"codex5hToggleTitle": "为此连接跟踪 Codex 5 小时配额",
"codexFastServiceTierDescription": "可用时为 Codex 请求使用 priority 服务层。",
"codexFastServiceTierLabel": "Codex 快速服务层",
"codexWeeklyToggleTitle": "Codex Weekly Toggle Title",
"codexWeeklyToggleTitle": "为此连接跟踪 Codex 周配额",
"compatUpstreamHeaderNamePlaceholder": "Compat Upstream Header Name Placeholder",
"compatUpstreamHeaderValuePlaceholder": "Compat Upstream Header Value Placeholder",
"compatible": "Compatible",
@@ -2687,8 +2687,8 @@
"consoleApiKeyOracleHint": "Console Api Key Oracle Hint",
"consoleApiKeyOracleLabel": "Console Api Key Oracle Label",
"consoleApiKeyOraclePlaceholder": "Console Api Key Oracle Placeholder",
"cpaModeDisabledTitle": "Cpa Mode Disabled Title",
"cpaModeEnabledTitle": "Cpa Mode Enabled Title",
"cpaModeDisabledTitle": "CLIProxyAPI 兼容模式已关闭",
"cpaModeEnabledTitle": "CLIProxyAPI 兼容模式已开启",
"customUserAgentHint": "Custom User Agent Hint",
"customUserAgentLabel": "Custom User Agent Label",
"databricksBaseUrlHint": "Databricks Base Url Hint",
@@ -2749,18 +2749,18 @@
"sessionCookieLabel": "Session Cookie Label",
"showEmail": "Show Email",
"snowflakeBaseUrlHint": "Snowflake Base Url Hint",
"supportedEndpointAudio": "Supported Endpoint Audio",
"supportedEndpointChat": "Supported Endpoint Chat",
"supportedEndpointEmbeddings": "Supported Endpoint Embeddings",
"supportedEndpointImages": "Supported Endpoint Images",
"supportedEndpointsLabel": "Supported Endpoints Label",
"supportedEndpointAudio": "音频",
"supportedEndpointChat": "聊天",
"supportedEndpointEmbeddings": "Embeddings",
"supportedEndpointImages": "图像",
"supportedEndpointsLabel": "支持的端点",
"tagGroupHint": "Tag Group Hint",
"tagGroupLabel": "Tag Group Label",
"tagGroupPlaceholder": "Tag Group Placeholder",
"testModel": "Test Model",
"testingModel": "Testing Model",
"toggleOffShort": "Toggle Off Short",
"toggleOnShort": "Toggle On Short",
"toggleOffShort": "",
"toggleOnShort": "",
"tokenExpiredBadge": "Token Expired Badge",
"tokenExpiredTitle": "Token Expired Title",
"tokenExpiresSoonTitle": "Token Expires Soon Title",

View File

@@ -164,6 +164,26 @@ test("buildClaudeCodeCompatibleRequest prefers existing Claude top-level system
]);
});
test("buildClaudeCodeCompatibleRequest does not duplicate an existing default system skeleton", () => {
const payload = buildClaudeCodeCompatibleRequest({
claudeBody: {
system: [
{
type: "text",
text: "You are a Claude agent, built on Anthropic's Claude Agent SDK.",
},
],
messages: [{ role: "user", content: "hello" }],
},
model: "claude-sonnet-4-6",
cwd: "/tmp/claude-code-compatible",
now: new Date("2026-01-02T12:00:00.000Z"),
});
assert.equal(payload.system.length, 1);
assert.match((payload.system[0] as any).text, /Claude Agent SDK/);
});
test("buildClaudeCodeCompatibleRequest covers Claude-native bodies and cache-control stripping", () => {
const stripped = buildClaudeCodeCompatibleRequest({
claudeBody: {