From 08d76bbd3d7032a6d5bd255a65ffa35fa2509b3f Mon Sep 17 00:00:00 2001 From: Nam Nguyen Ngoc Date: Mon, 9 Mar 2026 17:33:12 +0700 Subject: [PATCH] fix(ui): support CLI providers without API key in setup wizard (#81) - Recognize claude_cli and chatgpt_oauth as valid providers without API keys - Skip API key validation and show CLI-specific UI when provider is claude_cli - Clear API key state when switching provider type - Update bootstrap status check to handle keyless provider types Co-authored-by: Nam Nguyen Ngoc --- .../pages/setup/hooks/use-bootstrap-status.ts | 4 +- ui/web/src/pages/setup/setup-page.tsx | 3 +- ui/web/src/pages/setup/step-provider.tsx | 66 +++++++++++-------- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/ui/web/src/pages/setup/hooks/use-bootstrap-status.ts b/ui/web/src/pages/setup/hooks/use-bootstrap-status.ts index c1d7d786..b49631f0 100644 --- a/ui/web/src/pages/setup/hooks/use-bootstrap-status.ts +++ b/ui/web/src/pages/setup/hooks/use-bootstrap-status.ts @@ -17,7 +17,9 @@ export function useBootstrapStatus() { if (loading) return { needsSetup: false, currentStep: "complete" as SetupStep }; // A provider is "configured" if enabled + has an API key set (masked as "***") - const hasProvider = providers.some((p) => p.enabled && p.api_key === "***"); + // Claude CLI and ChatGPT OAuth don't require API keys — check type instead + const hasProvider = providers.some((p) => p.enabled && + (p.api_key === "***" || p.provider_type === "claude_cli" || p.provider_type === "chatgpt_oauth")); const hasAgent = agents.length > 0; if (!hasProvider) return { needsSetup: true, currentStep: 1 as SetupStep }; diff --git a/ui/web/src/pages/setup/setup-page.tsx b/ui/web/src/pages/setup/setup-page.tsx index 5e356460..a12d7971 100644 --- a/ui/web/src/pages/setup/setup-page.tsx +++ b/ui/web/src/pages/setup/setup-page.tsx @@ -52,7 +52,8 @@ export function SetupPage() { if (showComplete) { completedSteps.push(1, 2, 3, 4); } // For resuming: find existing provider/agent from server data - const activeProvider = createdProvider ?? providers.find((p) => p.enabled && p.api_key === "***") ?? null; + const activeProvider = createdProvider ?? providers.find((p) => p.enabled && + (p.api_key === "***" || p.provider_type === "claude_cli" || p.provider_type === "chatgpt_oauth")) ?? null; const activeAgent = createdAgent ?? agents[0] ?? null; const handleFinish = () => setShowComplete(true); diff --git a/ui/web/src/pages/setup/step-provider.tsx b/ui/web/src/pages/setup/step-provider.tsx index 42151486..dae45d4d 100644 --- a/ui/web/src/pages/setup/step-provider.tsx +++ b/ui/web/src/pages/setup/step-provider.tsx @@ -14,6 +14,7 @@ import { } from "@/components/ui/select"; import { PROVIDER_TYPES } from "@/constants/providers"; import { useProviders } from "@/pages/providers/hooks/use-providers"; +import { CLISection } from "@/pages/providers/provider-cli-section"; import { slugify } from "@/lib/slug"; import type { ProviderData } from "@/types/provider"; @@ -31,11 +32,14 @@ export function StepProvider({ onComplete }: StepProviderProps) { const [loading, setLoading] = useState(false); const [error, setError] = useState(""); + const isCLI = providerType === "claude_cli"; + const handleTypeChange = (value: string) => { setProviderType(value); const preset = PROVIDER_TYPES.find((t) => t.value === value); setName(slugify(value)); setApiBase(preset?.apiBase || ""); + setApiKey(""); setError(""); }; @@ -47,7 +51,7 @@ export function StepProvider({ onComplete }: StepProviderProps) { ); const handleCreate = async () => { - if (!apiKey.trim()) { setError("API key is required"); return; } + if (!isCLI && !apiKey.trim()) { setError("API key is required"); return; } setLoading(true); setError(""); try { @@ -55,7 +59,7 @@ export function StepProvider({ onComplete }: StepProviderProps) { name: name.trim(), provider_type: providerType, api_base: apiBase.trim() || undefined, - api_key: apiKey.trim(), + api_key: isCLI ? undefined : apiKey.trim(), enabled: true, }) as ProviderData; onComplete(provider); @@ -73,7 +77,9 @@ export function StepProvider({ onComplete }: StepProviderProps) {

Configure LLM Provider

- Connect to an AI provider to power your agents. You'll need an API key. + {isCLI + ? "Connect using your local Claude CLI installation. No API key needed." + : "Connect to an AI provider to power your agents. You'll need an API key."}

@@ -101,35 +107,41 @@ export function StepProvider({ onComplete }: StepProviderProps) { -
- - setApiKey(e.target.value)} - placeholder="sk-..." - /> -
+ {isCLI ? ( + + ) : ( + <> +
+ + setApiKey(e.target.value)} + placeholder="sk-..." + /> +
-
- - setApiBase(e.target.value)} - placeholder={apiBasePlaceholder} - /> -
+
+ + setApiBase(e.target.value)} + placeholder={apiBasePlaceholder} + /> +
+ + )} {error &&

{error}

}
-