mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 12:14:19 +00:00
fix(cliproxy): keep provider sections consistent for variants
This commit is contained in:
1 parent
f6c86b79fc
commit
254abd504f
10 files changed
+149
-51
No files matched your search
@@ -206,7 +206,7 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
>
|
>
|
||||||
<option value="">{t('cliproxyDialog.selectProvider')}</option>
|
<option value="">{t('cliproxyDialog.selectProvider')}</option>
|
||||||
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
||||||
<optgroup key={section.id} label={section.label}>
|
<optgroup key={section.id} label={t(section.labelKey)}>
|
||||||
{providerOptions
|
{providerOptions
|
||||||
.filter((opt) => section.providers.includes(opt.value))
|
.filter((opt) => section.providers.includes(opt.value))
|
||||||
.map((opt) => (
|
.map((opt) => (
|
||||||
@@ -224,9 +224,9 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
)}
|
)}
|
||||||
{selectedProviderSection && (
|
{selectedProviderSection && (
|
||||||
<p className="mt-2 text-xs text-muted-foreground">
|
<p className="mt-2 text-xs text-muted-foreground">
|
||||||
{selectedProviderSection.hint}
|
{t(selectedProviderSection.hintKey)}
|
||||||
{isPlusExtraProvider(selectedProvider)
|
{isPlusExtraProvider(selectedProvider)
|
||||||
? ' Requires the optional Plus backend while that track remains community-maintained.'
|
? ` ${t('providerConfig.plusTrackNote')}`
|
||||||
: ''}
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -320,7 +320,7 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
>
|
>
|
||||||
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
||||||
<optgroup key={section.id} label={section.label}>
|
<optgroup key={section.id} label={t(section.labelKey)}>
|
||||||
{providerOptions
|
{providerOptions
|
||||||
.filter((opt) => section.providers.includes(opt.value))
|
.filter((opt) => section.providers.includes(opt.value))
|
||||||
.map((opt) => (
|
.map((opt) => (
|
||||||
@@ -333,9 +333,9 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
</select>
|
</select>
|
||||||
{compositeTiers?.[tier]?.provider && (
|
{compositeTiers?.[tier]?.provider && (
|
||||||
<p className="mt-2 text-xs text-muted-foreground">
|
<p className="mt-2 text-xs text-muted-foreground">
|
||||||
{getProviderSection(compositeTiers[tier].provider)?.hint}
|
{t(getProviderSection(compositeTiers[tier].provider)?.hintKey || '')}
|
||||||
{isPlusExtraProvider(compositeTiers[tier].provider)
|
{isPlusExtraProvider(compositeTiers[tier].provider)
|
||||||
? ' Requires the optional Plus backend while that track remains community-maintained.'
|
? ` ${t('providerConfig.plusTrackNote')}`
|
||||||
: ''}
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
>
|
>
|
||||||
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
||||||
<optgroup key={section.id} label={section.label}>
|
<optgroup key={section.id} label={t(section.labelKey)}>
|
||||||
{providerOptions
|
{providerOptions
|
||||||
.filter((opt) => section.providers.includes(opt.value))
|
.filter((opt) => section.providers.includes(opt.value))
|
||||||
.map((opt) => (
|
.map((opt) => (
|
||||||
@@ -340,9 +340,9 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
</select>
|
</select>
|
||||||
{compositeTiers?.[tier]?.provider && (
|
{compositeTiers?.[tier]?.provider && (
|
||||||
<p className="mt-2 text-xs text-muted-foreground">
|
<p className="mt-2 text-xs text-muted-foreground">
|
||||||
{getProviderSection(compositeTiers[tier].provider)?.hint}
|
{t(getProviderSection(compositeTiers[tier].provider)?.hintKey || '')}
|
||||||
{isPlusExtraProvider(compositeTiers[tier].provider)
|
{isPlusExtraProvider(compositeTiers[tier].provider)
|
||||||
? ' Requires the optional Plus backend while that track remains community-maintained.'
|
? ` ${t('providerConfig.plusTrackNote')}`
|
||||||
: ''}
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -422,7 +422,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||||
>
|
>
|
||||||
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
{CLIPROXY_PROVIDER_SECTIONS.map((section) => (
|
||||||
<optgroup key={section.id} label={section.label}>
|
<optgroup key={section.id} label={t(section.labelKey)}>
|
||||||
{providerOptions
|
{providerOptions
|
||||||
.filter((opt) => section.providers.includes(opt.value))
|
.filter((opt) => section.providers.includes(opt.value))
|
||||||
.map((opt) => (
|
.map((opt) => (
|
||||||
@@ -435,9 +435,9 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
</select>
|
</select>
|
||||||
{selectedProvider && (
|
{selectedProvider && (
|
||||||
<p className="mt-2 text-xs text-muted-foreground">
|
<p className="mt-2 text-xs text-muted-foreground">
|
||||||
{getProviderSection(selectedProvider)?.hint}
|
{t(getProviderSection(selectedProvider)?.hintKey || '')}
|
||||||
{isPlusExtraProvider(selectedProvider)
|
{isPlusExtraProvider(selectedProvider)
|
||||||
? ' Requires the optional Plus backend while that track remains community-maintained.'
|
? ` ${t('providerConfig.plusTrackNote')}`
|
||||||
: ''}
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -304,6 +304,7 @@ export function ProviderEditor({
|
|||||||
<ProviderInfoTab
|
<ProviderInfoTab
|
||||||
provider={provider}
|
provider={provider}
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
|
baseProvider={baseProvider}
|
||||||
defaultTarget={defaultTarget}
|
defaultTarget={defaultTarget}
|
||||||
data={data}
|
data={data}
|
||||||
authStatus={authStatus}
|
authStatus={authStatus}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
interface ProviderInfoTabProps {
|
interface ProviderInfoTabProps {
|
||||||
provider: string;
|
provider: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
|
baseProvider?: string;
|
||||||
defaultTarget?: CliTarget;
|
defaultTarget?: CliTarget;
|
||||||
data?: SettingsResponse;
|
data?: SettingsResponse;
|
||||||
authStatus: AuthStatus;
|
authStatus: AuthStatus;
|
||||||
@@ -25,6 +26,7 @@ interface ProviderInfoTabProps {
|
|||||||
export function ProviderInfoTab({
|
export function ProviderInfoTab({
|
||||||
provider,
|
provider,
|
||||||
displayName,
|
displayName,
|
||||||
|
baseProvider,
|
||||||
defaultTarget,
|
defaultTarget,
|
||||||
data,
|
data,
|
||||||
authStatus,
|
authStatus,
|
||||||
@@ -34,7 +36,8 @@ export function ProviderInfoTab({
|
|||||||
const resolvedTarget = defaultTarget || 'claude';
|
const resolvedTarget = defaultTarget || 'claude';
|
||||||
const isDroidTarget = resolvedTarget === 'droid';
|
const isDroidTarget = resolvedTarget === 'droid';
|
||||||
const isCodexProvider = provider === 'codex';
|
const isCodexProvider = provider === 'codex';
|
||||||
const providerSection = getProviderSection(provider);
|
const sectionProvider = baseProvider || authStatus.provider || provider;
|
||||||
|
const providerSection = getProviderSection(sectionProvider);
|
||||||
const managementPrefix =
|
const managementPrefix =
|
||||||
resolvedTarget === 'claude' ? `ccs ${provider}` : `ccs ${provider} --target claude`;
|
resolvedTarget === 'claude' ? `ccs ${provider}` : `ccs ${provider} --target claude`;
|
||||||
const changeModelCommand = `${managementPrefix} --config`;
|
const changeModelCommand = `${managementPrefix} --config`;
|
||||||
@@ -105,13 +108,15 @@ export function ProviderInfoTab({
|
|||||||
</div>
|
</div>
|
||||||
{providerSection && (
|
{providerSection && (
|
||||||
<div className="grid grid-cols-[100px_1fr] gap-2 text-sm items-start">
|
<div className="grid grid-cols-[100px_1fr] gap-2 text-sm items-start">
|
||||||
<span className="font-medium text-muted-foreground">Track</span>
|
<span className="font-medium text-muted-foreground">
|
||||||
|
{t('providerConfig.trackLabel')}
|
||||||
|
</span>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span className="font-mono">{providerSection.label}</span>
|
<span className="font-mono">{t(providerSection.labelKey)}</span>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{providerSection.hint}
|
{t(providerSection.hintKey)}
|
||||||
{isPlusExtraProvider(provider)
|
{isPlusExtraProvider(sectionProvider)
|
||||||
? ' Requires the optional Plus backend while that track remains community-maintained.'
|
? ` ${t('providerConfig.plusTrackNote')}`
|
||||||
: ''}
|
: ''}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+50
-12
@@ -977,10 +977,13 @@ const resources = {
|
|||||||
backendBinary: 'Backend Binary',
|
backendBinary: 'Backend Binary',
|
||||||
stopProxyToSwitch: 'Stop the running proxy in Instance Status to switch backend.',
|
stopProxyToSwitch: 'Stop the running proxy in Instance Status to switch backend.',
|
||||||
default: 'Default',
|
default: 'Default',
|
||||||
plusDesc: 'Full provider support including Kiro and GitHub Copilot',
|
plusDesc:
|
||||||
originalDesc: 'Original binary (Gemini, Codex, Antigravity only)',
|
'Optional track for extra providers. Still supported, but currently community-maintained instead of upstream-maintained.',
|
||||||
|
originalDesc: 'Default, always-available backend for the core provider track.',
|
||||||
|
plusFallbackNotice:
|
||||||
|
'The Plus provider track is not deprecated, but local CLIProxy still falls back to the original backend while the maintained fork path is being brought back.',
|
||||||
variantsIncompatible:
|
variantsIncompatible:
|
||||||
'Existing Kiro/Copilot variants will not work with CLIProxyAPI. Switch to CLIProxyAPIPlus or remove those variants.',
|
'Existing plus-extra variants ({{providers}}) will not run on the original backend. Keep them visible for reference, but switch to Plus before using them.',
|
||||||
safety: 'Safety',
|
safety: 'Safety',
|
||||||
agyModeTitle: 'Antigravity + Gemini Power User Mode',
|
agyModeTitle: 'Antigravity + Gemini Power User Mode',
|
||||||
agyModeDesc:
|
agyModeDesc:
|
||||||
@@ -2234,6 +2237,13 @@ const resources = {
|
|||||||
},
|
},
|
||||||
providerConfig: {
|
providerConfig: {
|
||||||
defaultDeviceCodeInstruction: 'Complete the authorization in your browser.',
|
defaultDeviceCodeInstruction: 'Complete the authorization in your browser.',
|
||||||
|
trackLabel: 'Track',
|
||||||
|
sectionCoreLabel: 'Core / original backend',
|
||||||
|
sectionCoreHint: 'Default, always-available provider track',
|
||||||
|
sectionPlusLabel: 'Plus extras / community-maintained',
|
||||||
|
sectionPlusHint: 'Still supported, but separated from the default backend for now',
|
||||||
|
plusTrackNote:
|
||||||
|
'Requires the optional Plus backend while that track remains community-maintained.',
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -3443,10 +3453,12 @@ const resources = {
|
|||||||
backendBinary: '后端二进制',
|
backendBinary: '后端二进制',
|
||||||
stopProxyToSwitch: '请先在实例状态中停止正在运行的代理,再切换后端。',
|
stopProxyToSwitch: '请先在实例状态中停止正在运行的代理,再切换后端。',
|
||||||
default: '默认',
|
default: '默认',
|
||||||
plusDesc: '完整支持包括 Kiro 和 GitHub Copilot 在内的提供商',
|
plusDesc: '额外提供商的可选线路。仍受支持,但目前由社区维护而非上游维护。',
|
||||||
originalDesc: '原版二进制(仅 Gemini、Codex、Antigravity)',
|
originalDesc: '核心提供商线路的默认、始终可用后端。',
|
||||||
|
plusFallbackNotice:
|
||||||
|
'Plus 提供商线路并未弃用,但在受维护的 fork 恢复之前,本地 CLIProxy 仍会回退到原始后端。',
|
||||||
variantsIncompatible:
|
variantsIncompatible:
|
||||||
'现有 Kiro/Copilot 变体与 CLIProxyAPI 不兼容。请切换到 CLIProxyAPIPlus 或移除这些变体。',
|
'现有 plus 扩展变体({{providers}})无法在原始后端上运行。可以保留作参考,但使用前请切换到 Plus。',
|
||||||
safety: '安全',
|
safety: '安全',
|
||||||
agyModeTitle: 'Antigravity + Gemini 高级模式',
|
agyModeTitle: 'Antigravity + Gemini 高级模式',
|
||||||
agyModeDesc: '跳过 AGY 责任确认清单,以及 Gemini Dashboard 中输入风险短语的步骤。',
|
agyModeDesc: '跳过 AGY 责任确认清单,以及 Gemini Dashboard 中输入风险短语的步骤。',
|
||||||
@@ -4656,6 +4668,12 @@ const resources = {
|
|||||||
},
|
},
|
||||||
providerConfig: {
|
providerConfig: {
|
||||||
defaultDeviceCodeInstruction: '请在浏览器中完成授权。',
|
defaultDeviceCodeInstruction: '请在浏览器中完成授权。',
|
||||||
|
trackLabel: '分组',
|
||||||
|
sectionCoreLabel: '核心 / 原始后端',
|
||||||
|
sectionCoreHint: '默认且始终可用的提供商线路',
|
||||||
|
sectionPlusLabel: 'Plus 扩展 / 社区维护',
|
||||||
|
sectionPlusHint: '仍然受支持,但目前与默认后端分开显示',
|
||||||
|
plusTrackNote: '需要可选的 Plus 后端;当前这条线路由社区维护。',
|
||||||
},
|
},
|
||||||
profileEditorSections: {
|
profileEditorSections: {
|
||||||
imageAnalysis: '图片分析',
|
imageAnalysis: '图片分析',
|
||||||
@@ -5934,10 +5952,13 @@ const resources = {
|
|||||||
stopProxyToSwitch:
|
stopProxyToSwitch:
|
||||||
'Dừng proxy đang chạy trong Trạng thái phiên bản trước khi chuyển backend.',
|
'Dừng proxy đang chạy trong Trạng thái phiên bản trước khi chuyển backend.',
|
||||||
default: 'Mặc định',
|
default: 'Mặc định',
|
||||||
plusDesc: 'Hỗ trợ đầy đủ nhà cung cấp, bao gồm Kiro và GitHub Copilot',
|
plusDesc:
|
||||||
originalDesc: 'Binary gốc (chỉ Gemini, Codex, Antigravity)',
|
'Nhánh tùy chọn cho các nhà cung cấp bổ sung. Vẫn được hỗ trợ nhưng hiện do cộng đồng duy trì thay vì upstream.',
|
||||||
|
originalDesc: 'Backend mặc định, luôn sẵn sàng cho nhóm nhà cung cấp cốt lõi.',
|
||||||
|
plusFallbackNotice:
|
||||||
|
'Nhánh nhà cung cấp Plus chưa bị khai tử, nhưng CLIProxy cục bộ vẫn quay về backend gốc cho tới khi đường dẫn fork được duy trì được bật lại.',
|
||||||
variantsIncompatible:
|
variantsIncompatible:
|
||||||
'Các biến thể Kiro/Copilot hiện tại sẽ không hoạt động với CLIProxyAPI. Chuyển sang CLIProxyAPIPlus hoặc xóa các biến thể đó.',
|
'Các biến thể plus-extra hiện có ({{providers}}) sẽ không chạy trên backend gốc. Có thể giữ lại để tham chiếu, nhưng hãy chuyển sang Plus trước khi dùng.',
|
||||||
safety: 'An toàn',
|
safety: 'An toàn',
|
||||||
agyModeTitle: 'Chế độ power user Antigravity + Gemini',
|
agyModeTitle: 'Chế độ power user Antigravity + Gemini',
|
||||||
agyModeDesc:
|
agyModeDesc:
|
||||||
@@ -7170,6 +7191,12 @@ const resources = {
|
|||||||
},
|
},
|
||||||
providerConfig: {
|
providerConfig: {
|
||||||
defaultDeviceCodeInstruction: 'Hoàn tất việc cấp quyền trong trình duyệt của bạn.',
|
defaultDeviceCodeInstruction: 'Hoàn tất việc cấp quyền trong trình duyệt của bạn.',
|
||||||
|
trackLabel: 'Nhóm',
|
||||||
|
sectionCoreLabel: 'Core / backend gốc',
|
||||||
|
sectionCoreHint: 'Nhóm nhà cung cấp mặc định, luôn sẵn sàng',
|
||||||
|
sectionPlusLabel: 'Plus extras / cộng đồng duy trì',
|
||||||
|
sectionPlusHint: 'Vẫn được hỗ trợ nhưng hiện được tách khỏi backend mặc định',
|
||||||
|
plusTrackNote: 'Cần backend Plus tùy chọn; hiện tại nhánh này do cộng đồng duy trì.',
|
||||||
},
|
},
|
||||||
profileEditorSections: {
|
profileEditorSections: {
|
||||||
imageAnalysis: 'Phân tích hình ảnh',
|
imageAnalysis: 'Phân tích hình ảnh',
|
||||||
@@ -8461,10 +8488,13 @@ const resources = {
|
|||||||
stopProxyToSwitch:
|
stopProxyToSwitch:
|
||||||
'バックエンドを切り替える前に、インスタンス状態から実行中のプロキシを停止してください。',
|
'バックエンドを切り替える前に、インスタンス状態から実行中のプロキシを停止してください。',
|
||||||
default: 'デフォルト',
|
default: 'デフォルト',
|
||||||
plusDesc: 'Kiro と GitHub Copilot を含むすべてのプロバイダーをサポート',
|
plusDesc:
|
||||||
originalDesc: '元のバイナリ(Gemini、Codex、Antigravity のみ)',
|
'追加プロバイダー向けのオプショントラックです。引き続きサポートされていますが、現在は upstream ではなくコミュニティ保守です。',
|
||||||
|
originalDesc: 'コアプロバイダートラック向けの既定かつ常時利用可能な backend。',
|
||||||
|
plusFallbackNotice:
|
||||||
|
'Plus プロバイダートラックは廃止ではありませんが、保守中の fork が戻るまではローカル CLIProxy は引き続きオリジナル backend にフォールバックします。',
|
||||||
variantsIncompatible:
|
variantsIncompatible:
|
||||||
'既存の Kiro/Copilot バリアントは CLIProxyAPI では動作しません。CLIProxyAPIPlus に切り替えるか、それらのバリアントを削除してください。',
|
'既存の plus-extra バリアント({{providers}})はオリジナル backend では動作しません。参照用に残すことはできますが、使用前に Plus へ切り替えてください。',
|
||||||
safety: '安全設定',
|
safety: '安全設定',
|
||||||
agyModeTitle: 'Antigravity + Gemini パワーユーザーモード',
|
agyModeTitle: 'Antigravity + Gemini パワーユーザーモード',
|
||||||
agyModeDesc:
|
agyModeDesc:
|
||||||
@@ -9984,6 +10014,14 @@ const resources = {
|
|||||||
},
|
},
|
||||||
providerConfig: {
|
providerConfig: {
|
||||||
defaultDeviceCodeInstruction: 'ブラウザーで認証を完了してください。',
|
defaultDeviceCodeInstruction: 'ブラウザーで認証を完了してください。',
|
||||||
|
trackLabel: 'トラック',
|
||||||
|
sectionCoreLabel: 'コア / オリジナル backend',
|
||||||
|
sectionCoreHint: '既定で常に利用できるプロバイダートラック',
|
||||||
|
sectionPlusLabel: 'Plus 拡張 / コミュニティ保守',
|
||||||
|
sectionPlusHint:
|
||||||
|
'引き続きサポートされていますが、当面は既定の backend から分離されています',
|
||||||
|
plusTrackNote:
|
||||||
|
'このトラックはオプションの Plus backend が必要で、現在はコミュニティ保守です。',
|
||||||
},
|
},
|
||||||
updatesSpotlight: {
|
updatesSpotlight: {
|
||||||
openUpdatesCenter: '更新センターを開く',
|
openUpdatesCenter: '更新センターを開く',
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export type CLIProxyProviderSectionId = 'core' | 'plus-extra';
|
|||||||
|
|
||||||
export interface CLIProxyProviderSection {
|
export interface CLIProxyProviderSection {
|
||||||
id: CLIProxyProviderSectionId;
|
id: CLIProxyProviderSectionId;
|
||||||
label: string;
|
labelKey: string;
|
||||||
hint: string;
|
hintKey: string;
|
||||||
providers: readonly CLIProxyProvider[];
|
providers: readonly CLIProxyProvider[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,14 +42,14 @@ export const PLUS_EXTRA_CLIPROXY_PROVIDERS: readonly CLIProxyProvider[] = Object
|
|||||||
export const CLIPROXY_PROVIDER_SECTIONS: readonly CLIProxyProviderSection[] = Object.freeze([
|
export const CLIPROXY_PROVIDER_SECTIONS: readonly CLIProxyProviderSection[] = Object.freeze([
|
||||||
{
|
{
|
||||||
id: 'core',
|
id: 'core',
|
||||||
label: 'Core / original backend',
|
labelKey: 'providerConfig.sectionCoreLabel',
|
||||||
hint: 'Default, always-available provider track',
|
hintKey: 'providerConfig.sectionCoreHint',
|
||||||
providers: CORE_CLIPROXY_PROVIDERS,
|
providers: CORE_CLIPROXY_PROVIDERS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'plus-extra',
|
id: 'plus-extra',
|
||||||
label: 'Plus extras / community-maintained',
|
labelKey: 'providerConfig.sectionPlusLabel',
|
||||||
hint: 'Still supported, but separated from the default backend for now',
|
hintKey: 'providerConfig.sectionPlusHint',
|
||||||
providers: PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
providers: PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -309,6 +309,23 @@ export function getProviderSection(provider: unknown): CLIProxyProviderSection |
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VariantLike {
|
||||||
|
provider?: unknown;
|
||||||
|
tiers?: Record<string, { provider?: unknown } | undefined> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function variantUsesPlusExtraProvider(variant: VariantLike | null | undefined): boolean {
|
||||||
|
if (!variant) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant.tiers) {
|
||||||
|
return Object.values(variant.tiers).some((tier) => isPlusExtraProvider(tier?.provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPlusExtraProvider(variant.provider);
|
||||||
|
}
|
||||||
|
|
||||||
export function groupProvidersBySection<T>(
|
export function groupProvidersBySection<T>(
|
||||||
items: readonly T[],
|
items: readonly T[],
|
||||||
getProvider: (item: T) => unknown
|
getProvider: (item: T) => unknown
|
||||||
|
|||||||
@@ -406,10 +406,10 @@ export function CliproxyPage() {
|
|||||||
<div key={section.id} className="space-y-1">
|
<div key={section.id} className="space-y-1">
|
||||||
<div className="px-3">
|
<div className="px-3">
|
||||||
<div className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
<div className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
||||||
{section.label}
|
{t(section.labelKey)}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-[11px] leading-relaxed text-muted-foreground">
|
<p className="mt-1 text-[11px] leading-relaxed text-muted-foreground">
|
||||||
{section.hint}
|
{t(section.hintKey)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
CORE_CLIPROXY_PROVIDERS,
|
CORE_CLIPROXY_PROVIDERS,
|
||||||
PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
||||||
getProviderDisplayName,
|
getProviderDisplayName,
|
||||||
isPlusExtraProvider,
|
variantUsesPlusExtraProvider,
|
||||||
} from '@/lib/provider-config';
|
} from '@/lib/provider-config';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -216,7 +216,9 @@ export default function ProxySection() {
|
|||||||
const checkPlusOnlyVariants = useCallback(async () => {
|
const checkPlusOnlyVariants = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const result = await api.cliproxy.list();
|
const result = await api.cliproxy.list();
|
||||||
const hasIncompatible = result.variants.some((v) => isPlusExtraProvider(v.provider));
|
const hasIncompatible = result.variants.some((variant) =>
|
||||||
|
variantUsesPlusExtraProvider(variant)
|
||||||
|
);
|
||||||
setHasKiroGhcpVariants(hasIncompatible);
|
setHasKiroGhcpVariants(hasIncompatible);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[Proxy] Failed to check variants:', err);
|
console.error('[Proxy] Failed to check variants:', err);
|
||||||
@@ -496,10 +498,7 @@ export default function ProxySection() {
|
|||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<span className="font-medium">{t('settingsProxy.backendPlusApi')}</span>
|
<span className="font-medium">{t('settingsProxy.backendPlusApi')}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">{t('settingsProxy.plusDesc')}</p>
|
||||||
Optional track for extra providers. Still supported, but currently
|
|
||||||
community-maintained instead of upstream-maintained.
|
|
||||||
</p>
|
|
||||||
<p className="mt-2 text-[11px] leading-relaxed text-muted-foreground">
|
<p className="mt-2 text-[11px] leading-relaxed text-muted-foreground">
|
||||||
{plusProviderNames}
|
{plusProviderNames}
|
||||||
</p>
|
</p>
|
||||||
@@ -521,9 +520,7 @@ export default function ProxySection() {
|
|||||||
{t('settingsProxy.default')}
|
{t('settingsProxy.default')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">{t('settingsProxy.originalDesc')}</p>
|
||||||
Default, always-available backend for the core provider track.
|
|
||||||
</p>
|
|
||||||
<p className="mt-2 text-[11px] leading-relaxed text-muted-foreground">
|
<p className="mt-2 text-[11px] leading-relaxed text-muted-foreground">
|
||||||
{coreProviderNames}
|
{coreProviderNames}
|
||||||
</p>
|
</p>
|
||||||
@@ -533,8 +530,7 @@ export default function ProxySection() {
|
|||||||
<Alert className="py-2 border-amber-200 bg-amber-50 dark:border-amber-900/50 dark:bg-amber-900/20 [&>svg]:top-2.5">
|
<Alert className="py-2 border-amber-200 bg-amber-50 dark:border-amber-900/50 dark:bg-amber-900/20 [&>svg]:top-2.5">
|
||||||
<AlertTriangle className="h-4 w-4 text-amber-600" />
|
<AlertTriangle className="h-4 w-4 text-amber-600" />
|
||||||
<AlertDescription className="text-amber-700 dark:text-amber-400">
|
<AlertDescription className="text-amber-700 dark:text-amber-400">
|
||||||
The Plus provider track is not deprecated, but local CLIProxy still falls back to
|
{t('settingsProxy.plusFallbackNotice')}
|
||||||
the original backend while the maintained fork path is being brought back.
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
@@ -543,8 +539,7 @@ export default function ProxySection() {
|
|||||||
<Alert variant="destructive" className="py-2">
|
<Alert variant="destructive" className="py-2">
|
||||||
<AlertTriangle className="h-4 w-4" />
|
<AlertTriangle className="h-4 w-4" />
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
Existing plus-extra variants ({plusProviderNames}) will not run on the original
|
{t('settingsProxy.variantsIncompatible', { providers: plusProviderNames })}
|
||||||
backend. Keep them visible for reference, but switch to Plus before using them.
|
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -75,4 +75,24 @@ describe('ProviderInfoTab', () => {
|
|||||||
)
|
)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses the base provider when rendering variant track metadata', () => {
|
||||||
|
render(
|
||||||
|
<ProviderInfoTab
|
||||||
|
provider="my-cursor"
|
||||||
|
baseProvider="cursor"
|
||||||
|
displayName="My Cursor Variant"
|
||||||
|
defaultTarget="claude"
|
||||||
|
authStatus={{
|
||||||
|
...authenticatedStatus,
|
||||||
|
provider: 'cursor',
|
||||||
|
displayName: 'Cursor',
|
||||||
|
}}
|
||||||
|
supportsModelConfig
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByText('Track')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Plus extras / community-maintained')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
parseRequestedUpstreamModelRules,
|
parseRequestedUpstreamModelRules,
|
||||||
PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
PLUS_EXTRA_CLIPROXY_PROVIDERS,
|
||||||
PROVIDER_COLORS,
|
PROVIDER_COLORS,
|
||||||
|
variantUsesPlusExtraProvider,
|
||||||
} from '@/lib/provider-config';
|
} from '@/lib/provider-config';
|
||||||
|
|
||||||
describe('provider model mapping helpers', () => {
|
describe('provider model mapping helpers', () => {
|
||||||
@@ -89,6 +90,27 @@ describe('provider presentation metadata', () => {
|
|||||||
expect(grouped[1]?.items.map((entry) => entry.provider)).toEqual(['cursor']);
|
expect(grouped[1]?.items.map((entry) => entry.provider)).toEqual(['cursor']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('detects plus-extra providers inside composite variants', () => {
|
||||||
|
expect(
|
||||||
|
variantUsesPlusExtraProvider({
|
||||||
|
provider: 'gemini',
|
||||||
|
tiers: {
|
||||||
|
opus: { provider: 'gemini' },
|
||||||
|
sonnet: { provider: 'cursor' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
variantUsesPlusExtraProvider({
|
||||||
|
provider: 'gemini',
|
||||||
|
tiers: {
|
||||||
|
opus: { provider: 'gemini' },
|
||||||
|
sonnet: { provider: 'kimi' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
['cursor', 'Cursor', 'Cursor browser-authenticated provider', '/assets/sidebar/cursor.svg'],
|
['cursor', 'Cursor', 'Cursor browser-authenticated provider', '/assets/sidebar/cursor.svg'],
|
||||||
['gitlab', 'GitLab Duo', 'GitLab Duo with OAuth or PAT auth', '/assets/providers/gitlab.svg'],
|
['gitlab', 'GitLab Duo', 'GitLab Duo with OAuth or PAT auth', '/assets/providers/gitlab.svg'],
|
||||||
|
|||||||
Reference in new issue
Block a user