mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-09-08 20:20:04 +00:00
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 <namnn.0911@gmail.com>
This commit is contained in:
co-authored by
Nam Nguyen Ngoc
parent
acf3db7bc6
commit
08d76bbd3d
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-lg font-semibold">Configure LLM Provider</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
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."}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -101,35 +107,41 @@ export function StepProvider({ onComplete }: StepProviderProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="inline-flex items-center gap-1.5">
|
||||
API Key *
|
||||
<InfoTip text="Your provider's secret key. Encrypted server-side and never exposed in API responses." />
|
||||
</Label>
|
||||
<Input
|
||||
type="password"
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</div>
|
||||
{isCLI ? (
|
||||
<CLISection open={true} />
|
||||
) : (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label className="inline-flex items-center gap-1.5">
|
||||
API Key *
|
||||
<InfoTip text="Your provider's secret key. Encrypted server-side and never exposed in API responses." />
|
||||
</Label>
|
||||
<Input
|
||||
type="password"
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
placeholder="sk-..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="inline-flex items-center gap-1.5">
|
||||
API Base URL
|
||||
<InfoTip text="The endpoint URL for API requests. Auto-filled based on provider type. Override only if using a custom proxy." />
|
||||
</Label>
|
||||
<Input
|
||||
value={apiBase}
|
||||
onChange={(e) => setApiBase(e.target.value)}
|
||||
placeholder={apiBasePlaceholder}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label className="inline-flex items-center gap-1.5">
|
||||
API Base URL
|
||||
<InfoTip text="The endpoint URL for API requests. Auto-filled based on provider type. Override only if using a custom proxy." />
|
||||
</Label>
|
||||
<Input
|
||||
value={apiBase}
|
||||
onChange={(e) => setApiBase(e.target.value)}
|
||||
placeholder={apiBasePlaceholder}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{error && <p className="text-sm text-destructive">{error}</p>}
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={handleCreate} disabled={loading || !apiKey.trim()}>
|
||||
<Button onClick={handleCreate} disabled={loading || (!isCLI && !apiKey.trim())}>
|
||||
{loading ? "Creating..." : "Create Provider"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user