From 0ffde4f3a2688ea1864fab3c94d3d020d4b33a93 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Sun, 29 Mar 2026 19:44:02 -0400 Subject: [PATCH] feat(codex-ui): surface cliproxy setup guidance --- .../services/codex-dashboard-service.ts | 16 ++++ .../codex-dashboard-service.test.ts | 16 ++++ .../provider-editor/provider-info-tab.tsx | 12 +-- .../compatible-cli/codex-docs-tab.tsx | 37 ++++++++ .../codex-model-providers-card.tsx | 33 +++++++ .../compatible-cli/codex-overview-tab.tsx | 86 +++++++++++++++---- ui/src/lib/support-updates-catalog.ts | 26 ++++-- 7 files changed, 196 insertions(+), 30 deletions(-) diff --git a/src/web-server/services/codex-dashboard-service.ts b/src/web-server/services/codex-dashboard-service.ts index 505c84b1..3285f29b 100644 --- a/src/web-server/services/codex-dashboard-service.ts +++ b/src/web-server/services/codex-dashboard-service.ts @@ -702,6 +702,7 @@ export async function getCodexDashboardDiagnostics(): Promise left.localeCompare(right)) : []; const activeProfile = asString(config?.profile); + const activeModelProvider = asString(config?.model_provider); const profileNames = Object.keys(asObject(config?.profiles) ?? {}).sort((left, right) => left.localeCompare(right) ); @@ -726,6 +727,21 @@ export async function getCodexDashboardDiagnostics(): Promise provider.name === activeModelProvider); + if (!activeProvider) { + warnings.push( + `Active model provider "${activeModelProvider}" is selected but missing from [model_providers].` + ); + } else { + if (!activeProvider.baseUrl) { + warnings.push(`Active model provider "${activeProvider.name}" is missing base_url.`); + } + if (!activeProvider.envKey) { + warnings.push(`Active model provider "${activeProvider.name}" is missing env_key.`); + } + } + } if (modelProviders.some((provider) => provider.usesExperimentalBearerToken)) { warnings.push( 'One or more model_providers entries use experimental_bearer_token; prefer env_key-backed auth.' diff --git a/tests/unit/web-server/codex-dashboard-service.test.ts b/tests/unit/web-server/codex-dashboard-service.test.ts index f5950d91..08855f0e 100644 --- a/tests/unit/web-server/codex-dashboard-service.test.ts +++ b/tests/unit/web-server/codex-dashboard-service.test.ts @@ -218,6 +218,22 @@ model = "gpt-5.4" expect(diagnostics.supportMatrix.some((entry) => entry.id === 'default')).toBe(true); }); + it('warns when the active model provider is selected without base_url or env_key', async () => { + fs.writeFileSync( + path.join(codexHome, 'config.toml'), + `model_provider = "cliproxy" + +[model_providers.cliproxy] +wire_api = "responses" +` + ); + + const diagnostics = await getCodexDashboardDiagnostics(); + + expect(diagnostics.warnings.some((warning) => warning.includes('missing base_url'))).toBe(true); + expect(diagnostics.warnings.some((warning) => warning.includes('missing env_key'))).toBe(true); + }); + it('summarizes granular approval policies without flattening them to null', async () => { fs.writeFileSync( path.join(codexHome, 'config.toml'), diff --git a/ui/src/components/cliproxy/provider-editor/provider-info-tab.tsx b/ui/src/components/cliproxy/provider-editor/provider-info-tab.tsx index 8cde0f8c..4d2c30f2 100644 --- a/ui/src/components/cliproxy/provider-editor/provider-info-tab.tsx +++ b/ui/src/components/cliproxy/provider-editor/provider-info-tab.tsx @@ -91,18 +91,14 @@ export function ProviderInfoTab({ {isCodexProvider && ( <> + - - )}
+ + + + + CCS bridge recipe + + + +

+ Use ccsxp if you want the built-in CCS Codex provider shortcut on native + Codex. Use the saved recipe below if you want plain codex or a personal + alias like cxp to default to CLIProxy. +

+
+              {CLIPROXY_NATIVE_CODEX_RECIPE}
+            
+
+

+ 1. Save the cliproxy provider in your user config. +

+

+ 2. Set top-level model_provider to cliproxy. +

+

+ 3. Export CLIPROXY_API_KEY in your shell before launching native Codex. +

+
+
+
+ diff --git a/ui/src/components/compatible-cli/codex-model-providers-card.tsx b/ui/src/components/compatible-cli/codex-model-providers-card.tsx index 7d3b9814..e1449789 100644 --- a/ui/src/components/compatible-cli/codex-model-providers-card.tsx +++ b/ui/src/components/compatible-cli/codex-model-providers-card.tsx @@ -33,6 +33,16 @@ const EMPTY_MODEL_PROVIDER_DRAFT: CodexModelProviderEntry = { supportsWebsockets: false, }; +const CLIPROXY_CODEX_PROVIDER_PRESET: CodexModelProviderEntry = { + name: 'cliproxy', + displayName: 'CLIProxy Codex', + baseUrl: 'http://127.0.0.1:8317/api/provider/codex', + envKey: 'CLIPROXY_API_KEY', + wireApi: 'responses', + requiresOpenaiAuth: false, + supportsWebsockets: false, +}; + interface ModelProviderEditorProps { initialDraft: CodexModelProviderEntry; isNew: boolean; @@ -56,6 +66,23 @@ function ModelProviderEditor({ return ( <> + {isNew && ( +
+

+ Quick start: apply the CLIProxy Codex preset here, then set{' '} + Default provider to cliproxy in Top-level settings. +

+ +
+ )} +
+ +

+ If you want plain native codex to default to CLIProxy, save a provider named{' '} + cliproxy with CLIPROXY_API_KEY here, then pick{' '} + cliproxy in the Default provider control above. +

); } diff --git a/ui/src/components/compatible-cli/codex-overview-tab.tsx b/ui/src/components/compatible-cli/codex-overview-tab.tsx index 9a2f34ee..accf30fb 100644 --- a/ui/src/components/compatible-cli/codex-overview-tab.tsx +++ b/ui/src/components/compatible-cli/codex-overview-tab.tsx @@ -24,6 +24,13 @@ import { import type { CodexDashboardDiagnostics } from '@/hooks/use-codex-types'; import { cn } from '@/lib/utils'; +const CLIPROXY_NATIVE_CODEX_RECIPE = `model_provider = "cliproxy" + +[model_providers.cliproxy] +base_url = "http://127.0.0.1:8317/api/provider/codex" +env_key = "CLIPROXY_API_KEY" +wire_api = "responses"`; + function formatTimestamp(value: number | null | undefined): string { if (!value || !Number.isFinite(value)) return 'N/A'; return new Date(value).toLocaleString(); @@ -70,6 +77,17 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) {

Codex is a first-class runtime target in CCS, but it stays runtime-only in v1.

+

+ ccs-codex and ccsx launch native Codex against your saved + native config, while ccsxp is the opinionated shortcut for{' '} + ccs codex --target codex. +

+

+ Plain codex or a personal alias like cxp needs{' '} + model_provider = "cliproxy" plus a matching{' '} + [model_providers.cliproxy] entry if you want CLIProxy as the saved native + default. +

Saved default targets for API profiles and variants still remain on Claude or Droid.

@@ -103,7 +121,8 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) { mono /> - + +
--config override support @@ -113,6 +132,40 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) { + + + + + CLIProxy-backed native Codex + + + +

+ There are two supported paths. Use ccsxp if you want the built-in CCS + Codex provider shortcut. Use the saved recipe below if you want plain{' '} + codex or a personal alias like cxp to default to CLIProxy. +

+
+

Saved native Codex recipe

+
+                {CLIPROXY_NATIVE_CODEX_RECIPE}
+              
+
+
+

+ 1. Save a provider named cliproxy with the base URL and env key above. +

+

+ 2. In Top-level settings, set Default provider to{' '} + cliproxy. +

+

+ 3. Export CLIPROXY_API_KEY in your shell before launching native Codex. +

+
+
+
+ @@ -213,19 +266,19 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) {

Native Codex runtime

- Use ccs-codex, ccsx, or --target codex. CCS - launches the local Codex CLI and depends on native Codex capabilities such as{' '} - --config overrides. + Use ccs-codex, ccsx, or --target codex when + you want the local Codex CLI to honor your saved native user config.

-

Codex provider / bridge

+

CCS Codex provider / bridge

- CCS can route provider credentials transiently through CLIProxy. That is not the - same as editing local config.toml, and some routed values may never - persist here. + Use ccsxp or ccs codex --target codex when you want the + built-in CCS Codex provider on native Codex. That path uses transient CCS-managed + overrides and is separate from the saved cliproxy recipe above.

diff --git a/ui/src/lib/support-updates-catalog.ts b/ui/src/lib/support-updates-catalog.ts index 67a82731..c67dadf4 100644 --- a/ui/src/lib/support-updates-catalog.ts +++ b/ui/src/lib/support-updates-catalog.ts @@ -59,7 +59,7 @@ export const SUPPORT_NOTICES: SupportNotice[] = [ id: 'codex-target-runtime-support', title: 'Native Codex runtime support is live', summary: - 'Codex now participates as a first-class runtime target through ccs-codex, ccsx, or --target codex.', + 'Codex now participates as a first-class runtime target through ccs-codex, ccsx, ccsxp, or --target codex.', primaryAction: 'Use Codex as a runtime target for native Codex sessions and Codex-routed CLIProxy flows.', publishedAt: '2026-03-28', @@ -68,6 +68,7 @@ export const SUPPORT_NOTICES: SupportNotice[] = [ entryIds: ['codex-target', 'codex-cliproxy'], highlights: [ 'Use ccs-codex or ccsx for native Codex runs.', + 'Use ccsxp for the built-in CCS Codex provider shortcut on native Codex.', 'Built-in Codex and Codex bridge profiles can run on native Codex with --target codex.', 'Saved default targets for API profiles and variants remain claude or droid.', ], @@ -82,7 +83,14 @@ export const SUPPORT_NOTICES: SupportNotice[] = [ { id: 'copy-codex-provider-command', label: 'Run built-in Codex on Codex', - description: 'Use the built-in Codex provider with native Codex runtime.', + description: 'Use the built-in Codex provider shortcut on native Codex.', + type: 'command', + command: 'ccsxp "your prompt"', + }, + { + id: 'copy-codex-provider-command-explicit', + label: 'Run built-in Codex on Codex (explicit)', + description: 'Use the explicit built-in Codex provider route on native Codex.', type: 'command', command: 'ccs codex --target codex "your prompt"', }, @@ -95,7 +103,12 @@ export const SUPPORT_NOTICES: SupportNotice[] = [ }, ], routes: [{ label: 'Codex CLI', path: '/codex' }], - commands: ['ccs-codex', 'ccsx', 'ccs codex --target codex "your prompt"'], + commands: [ + 'ccs-codex', + 'ccsx', + 'ccsxp "your prompt"', + 'ccs codex --target codex "your prompt"', + ], }, { id: 'droid-target-support', @@ -250,7 +263,7 @@ export const CLI_SUPPORT_ENTRIES: CliSupportEntry[] = [ name: 'Codex via CLIProxy', scope: 'cliproxy', status: 'stable', - summary: 'OAuth-backed provider with configurable variant model and target.', + summary: 'OAuth-backed provider with configurable variant model and a native Codex shortcut.', pillars: { baseUrl: 'Managed by CLIProxy backend', auth: 'OAuth account via CLIProxy auth flow', @@ -261,11 +274,14 @@ export const CLI_SUPPORT_ENTRIES: CliSupportEntry[] = [ { label: 'Control Panel', path: '/cliproxy/control-panel' }, ], commands: [ - 'ccs codex', + 'ccsxp "your prompt"', 'ccs codex --target codex', + 'ccs codex', 'ccs cliproxy create mycodex --provider codex', 'ccs api create codex-api --cliproxy-provider codex', ], + notes: + 'Use ccsxp when you want the built-in Codex provider on native Codex without retyping --target codex.', }, { id: 'gemini-cliproxy',