mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
refactor(settings): extract CLI status into custom hook
- Create hooks/use-cli-status.ts to manage all CLI status logic - Move Claude and Codex CLI status state management to hook - Move CLI checking useEffect and refresh handlers to hook - Update settings-view.tsx to use new useCliStatus hook - Reduce settings-view.tsx by ~130 lines - Improve testability by isolating CLI status logic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,8 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { KeyboardMap, ShortcutReferencePanel } from "@/components/ui/keyboard-map";
|
||||
// Import custom hooks
|
||||
import { useCliStatus } from "./settings-view/hooks/use-cli-status";
|
||||
// Import extracted sections
|
||||
import { ApiKeysSection } from "./settings-view/api-keys/api-keys-section";
|
||||
import { ClaudeCliStatus } from "./settings-view/cli-status/claude-cli-status";
|
||||
@@ -83,128 +85,21 @@ export function SettingsView() {
|
||||
}
|
||||
};
|
||||
|
||||
const [claudeCliStatus, setClaudeCliStatus] = useState<{
|
||||
success: boolean;
|
||||
status?: string;
|
||||
method?: string;
|
||||
version?: string;
|
||||
path?: string;
|
||||
recommendation?: string;
|
||||
installCommands?: {
|
||||
macos?: string;
|
||||
windows?: string;
|
||||
linux?: string;
|
||||
npm?: string;
|
||||
};
|
||||
error?: string;
|
||||
} | null>(null);
|
||||
|
||||
const [codexCliStatus, setCodexCliStatus] = useState<{
|
||||
success: boolean;
|
||||
status?: string;
|
||||
method?: string;
|
||||
version?: string;
|
||||
path?: string;
|
||||
hasApiKey?: boolean;
|
||||
recommendation?: string;
|
||||
installCommands?: {
|
||||
macos?: string;
|
||||
windows?: string;
|
||||
linux?: string;
|
||||
npm?: string;
|
||||
};
|
||||
error?: string;
|
||||
} | null>(null);
|
||||
// Use CLI status hook
|
||||
const {
|
||||
claudeCliStatus,
|
||||
codexCliStatus,
|
||||
isCheckingClaudeCli,
|
||||
isCheckingCodexCli,
|
||||
handleRefreshClaudeCli,
|
||||
handleRefreshCodexCli,
|
||||
} = useCliStatus();
|
||||
|
||||
const [activeSection, setActiveSection] = useState("api-keys");
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
||||
const [isCheckingClaudeCli, setIsCheckingClaudeCli] = useState(false);
|
||||
const [isCheckingCodexCli, setIsCheckingCodexCli] = useState(false);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const checkCliStatus = async () => {
|
||||
const api = getElectronAPI();
|
||||
if (api?.checkClaudeCli) {
|
||||
try {
|
||||
const status = await api.checkClaudeCli();
|
||||
setClaudeCliStatus(status);
|
||||
} catch (error) {
|
||||
console.error("Failed to check Claude CLI status:", error);
|
||||
}
|
||||
}
|
||||
if (api?.checkCodexCli) {
|
||||
try {
|
||||
const status = await api.checkCodexCli();
|
||||
setCodexCliStatus(status);
|
||||
} catch (error) {
|
||||
console.error("Failed to check Codex CLI status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Claude auth status (re-fetch on mount to ensure persistence)
|
||||
if (api?.setup?.getClaudeStatus) {
|
||||
try {
|
||||
const result = await api.setup.getClaudeStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
const authStatus = {
|
||||
authenticated: auth.authenticated,
|
||||
method:
|
||||
auth.method === "oauth_token"
|
||||
? "oauth" as const
|
||||
: auth.method?.includes("api_key")
|
||||
? "api_key" as const
|
||||
: "none" as const,
|
||||
hasCredentialsFile: auth.hasCredentialsFile ?? false,
|
||||
oauthTokenValid: auth.hasStoredOAuthToken,
|
||||
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
|
||||
};
|
||||
setClaudeAuthStatus(authStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check Claude auth status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Codex auth status (re-fetch on mount to ensure persistence)
|
||||
if (api?.setup?.getCodexStatus) {
|
||||
try {
|
||||
const result = await api.setup.getCodexStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
// Determine method - prioritize cli_verified and cli_tokens over auth_file
|
||||
const method =
|
||||
auth.method === "cli_verified" || auth.method === "cli_tokens"
|
||||
? auth.method === "cli_verified"
|
||||
? "cli_verified" as const
|
||||
: "cli_tokens" as const
|
||||
: auth.method === "auth_file"
|
||||
? "api_key" as const
|
||||
: auth.method === "env_var"
|
||||
? "env" as const
|
||||
: "none" as const;
|
||||
|
||||
const authStatus = {
|
||||
authenticated: auth.authenticated,
|
||||
method,
|
||||
// Only set apiKeyValid for actual API key methods, not CLI login
|
||||
apiKeyValid:
|
||||
method === "cli_verified" || method === "cli_tokens"
|
||||
? undefined
|
||||
: auth.hasAuthFile || auth.hasEnvKey,
|
||||
};
|
||||
setCodexAuthStatus(authStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check Codex auth status:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkCliStatus();
|
||||
}, [setClaudeAuthStatus, setCodexAuthStatus]);
|
||||
|
||||
// Track scroll position to highlight active nav item
|
||||
useEffect(() => {
|
||||
const container = scrollContainerRef.current;
|
||||
@@ -267,36 +162,6 @@ export function SettingsView() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleRefreshClaudeCli = useCallback(async () => {
|
||||
setIsCheckingClaudeCli(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (api?.checkClaudeCli) {
|
||||
const status = await api.checkClaudeCli();
|
||||
setClaudeCliStatus(status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh Claude CLI status:", error);
|
||||
} finally {
|
||||
setIsCheckingClaudeCli(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleRefreshCodexCli = useCallback(async () => {
|
||||
setIsCheckingCodexCli(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (api?.checkCodexCli) {
|
||||
const status = await api.checkCodexCli();
|
||||
setCodexCliStatus(status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh Codex CLI status:", error);
|
||||
} finally {
|
||||
setIsCheckingCodexCli(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex-1 flex flex-col overflow-hidden content-bg"
|
||||
|
||||
169
app/src/components/views/settings-view/hooks/use-cli-status.ts
Normal file
169
app/src/components/views/settings-view/hooks/use-cli-status.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useSetupStore } from "@/store/setup-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
|
||||
interface CliStatusResult {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
method?: string;
|
||||
version?: string;
|
||||
path?: string;
|
||||
recommendation?: string;
|
||||
installCommands?: {
|
||||
macos?: string;
|
||||
windows?: string;
|
||||
linux?: string;
|
||||
npm?: string;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface CodexCliStatusResult extends CliStatusResult {
|
||||
hasApiKey?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for managing Claude and Codex CLI status
|
||||
* Handles checking CLI installation, authentication, and refresh functionality
|
||||
*/
|
||||
export function useCliStatus() {
|
||||
const { setClaudeAuthStatus, setCodexAuthStatus } = useSetupStore();
|
||||
|
||||
const [claudeCliStatus, setClaudeCliStatus] =
|
||||
useState<CliStatusResult | null>(null);
|
||||
|
||||
const [codexCliStatus, setCodexCliStatus] =
|
||||
useState<CodexCliStatusResult | null>(null);
|
||||
|
||||
const [isCheckingClaudeCli, setIsCheckingClaudeCli] = useState(false);
|
||||
const [isCheckingCodexCli, setIsCheckingCodexCli] = useState(false);
|
||||
|
||||
// Check CLI status on mount
|
||||
useEffect(() => {
|
||||
const checkCliStatus = async () => {
|
||||
const api = getElectronAPI();
|
||||
|
||||
// Check Claude CLI
|
||||
if (api?.checkClaudeCli) {
|
||||
try {
|
||||
const status = await api.checkClaudeCli();
|
||||
setClaudeCliStatus(status);
|
||||
} catch (error) {
|
||||
console.error("Failed to check Claude CLI status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Codex CLI
|
||||
if (api?.checkCodexCli) {
|
||||
try {
|
||||
const status = await api.checkCodexCli();
|
||||
setCodexCliStatus(status);
|
||||
} catch (error) {
|
||||
console.error("Failed to check Codex CLI status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Claude auth status (re-fetch on mount to ensure persistence)
|
||||
if (api?.setup?.getClaudeStatus) {
|
||||
try {
|
||||
const result = await api.setup.getClaudeStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
const authStatus = {
|
||||
authenticated: auth.authenticated,
|
||||
method:
|
||||
auth.method === "oauth_token"
|
||||
? ("oauth" as const)
|
||||
: auth.method?.includes("api_key")
|
||||
? ("api_key" as const)
|
||||
: ("none" as const),
|
||||
hasCredentialsFile: auth.hasCredentialsFile ?? false,
|
||||
oauthTokenValid: auth.hasStoredOAuthToken,
|
||||
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
|
||||
};
|
||||
setClaudeAuthStatus(authStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check Claude auth status:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Codex auth status (re-fetch on mount to ensure persistence)
|
||||
if (api?.setup?.getCodexStatus) {
|
||||
try {
|
||||
const result = await api.setup.getCodexStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
// Determine method - prioritize cli_verified and cli_tokens over auth_file
|
||||
const method =
|
||||
auth.method === "cli_verified" || auth.method === "cli_tokens"
|
||||
? auth.method === "cli_verified"
|
||||
? ("cli_verified" as const)
|
||||
: ("cli_tokens" as const)
|
||||
: auth.method === "auth_file"
|
||||
? ("api_key" as const)
|
||||
: auth.method === "env_var"
|
||||
? ("env" as const)
|
||||
: ("none" as const);
|
||||
|
||||
const authStatus = {
|
||||
authenticated: auth.authenticated,
|
||||
method,
|
||||
// Only set apiKeyValid for actual API key methods, not CLI login
|
||||
apiKeyValid:
|
||||
method === "cli_verified" || method === "cli_tokens"
|
||||
? undefined
|
||||
: auth.hasAuthFile || auth.hasEnvKey,
|
||||
};
|
||||
setCodexAuthStatus(authStatus);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check Codex auth status:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkCliStatus();
|
||||
}, [setClaudeAuthStatus, setCodexAuthStatus]);
|
||||
|
||||
// Refresh Claude CLI status
|
||||
const handleRefreshClaudeCli = useCallback(async () => {
|
||||
setIsCheckingClaudeCli(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (api?.checkClaudeCli) {
|
||||
const status = await api.checkClaudeCli();
|
||||
setClaudeCliStatus(status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh Claude CLI status:", error);
|
||||
} finally {
|
||||
setIsCheckingClaudeCli(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Refresh Codex CLI status
|
||||
const handleRefreshCodexCli = useCallback(async () => {
|
||||
setIsCheckingCodexCli(true);
|
||||
try {
|
||||
const api = getElectronAPI();
|
||||
if (api?.checkCodexCli) {
|
||||
const status = await api.checkCodexCli();
|
||||
setCodexCliStatus(status);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to refresh Codex CLI status:", error);
|
||||
} finally {
|
||||
setIsCheckingCodexCli(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
claudeCliStatus,
|
||||
codexCliStatus,
|
||||
isCheckingClaudeCli,
|
||||
isCheckingCodexCli,
|
||||
handleRefreshClaudeCli,
|
||||
handleRefreshCodexCli,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user