mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
- Made the generation status variables private and introduced getter functions for both spec and suggestions generation states. - Updated relevant route handlers to utilize the new getter functions, improving encapsulation and reducing direct access to shared state. - Enhanced code maintainability by centralizing state management logic.
184 lines
5.7 KiB
TypeScript
184 lines
5.7 KiB
TypeScript
/**
|
|
* Business logic for getting Claude CLI status
|
|
*/
|
|
|
|
import { exec } from "child_process";
|
|
import { promisify } from "util";
|
|
import os from "os";
|
|
import path from "path";
|
|
import fs from "fs/promises";
|
|
import { getApiKey } from "./common.js";
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
export async function getClaudeStatus() {
|
|
let installed = false;
|
|
let version = "";
|
|
let cliPath = "";
|
|
let method = "none";
|
|
|
|
// Try to find Claude CLI
|
|
try {
|
|
const { stdout } = await execAsync(
|
|
"which claude || where claude 2>/dev/null"
|
|
);
|
|
cliPath = stdout.trim();
|
|
installed = true;
|
|
method = "path";
|
|
|
|
// Get version
|
|
try {
|
|
const { stdout: versionOut } = await execAsync("claude --version");
|
|
version = versionOut.trim();
|
|
} catch {
|
|
// Version command might not be available
|
|
}
|
|
} catch {
|
|
// Not in PATH, try common locations
|
|
const commonPaths = [
|
|
path.join(os.homedir(), ".local", "bin", "claude"),
|
|
path.join(os.homedir(), ".claude", "local", "claude"),
|
|
"/usr/local/bin/claude",
|
|
path.join(os.homedir(), ".npm-global", "bin", "claude"),
|
|
];
|
|
|
|
for (const p of commonPaths) {
|
|
try {
|
|
await fs.access(p);
|
|
cliPath = p;
|
|
installed = true;
|
|
method = "local";
|
|
|
|
// Get version from this path
|
|
try {
|
|
const { stdout: versionOut } = await execAsync(`"${p}" --version`);
|
|
version = versionOut.trim();
|
|
} catch {
|
|
// Version command might not be available
|
|
}
|
|
break;
|
|
} catch {
|
|
// Not found at this path
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check authentication - detect all possible auth methods
|
|
// Note: apiKeys.anthropic_oauth_token stores OAuth tokens from subscription auth
|
|
// apiKeys.anthropic stores direct API keys for pay-per-use
|
|
let auth = {
|
|
authenticated: false,
|
|
method: "none" as string,
|
|
hasCredentialsFile: false,
|
|
hasToken: false,
|
|
hasStoredOAuthToken: !!getApiKey("anthropic_oauth_token"),
|
|
hasStoredApiKey: !!getApiKey("anthropic"),
|
|
hasEnvApiKey: !!process.env.ANTHROPIC_API_KEY,
|
|
hasEnvOAuthToken: !!process.env.CLAUDE_CODE_OAUTH_TOKEN,
|
|
// Additional fields for detailed status
|
|
oauthTokenValid: false,
|
|
apiKeyValid: false,
|
|
hasCliAuth: false,
|
|
hasRecentActivity: false,
|
|
};
|
|
|
|
const claudeDir = path.join(os.homedir(), ".claude");
|
|
|
|
// Check for recent Claude CLI activity - indicates working authentication
|
|
// The stats-cache.json file is only populated when the CLI is working properly
|
|
const statsCachePath = path.join(claudeDir, "stats-cache.json");
|
|
try {
|
|
const statsContent = await fs.readFile(statsCachePath, "utf-8");
|
|
const stats = JSON.parse(statsContent);
|
|
|
|
// Check if there's any activity (which means the CLI is authenticated and working)
|
|
if (stats.dailyActivity && stats.dailyActivity.length > 0) {
|
|
auth.hasRecentActivity = true;
|
|
auth.hasCliAuth = true;
|
|
auth.authenticated = true;
|
|
auth.method = "cli_authenticated";
|
|
}
|
|
} catch {
|
|
// Stats file doesn't exist or is invalid
|
|
}
|
|
|
|
// Check for settings.json - indicates CLI has been set up
|
|
const settingsPath = path.join(claudeDir, "settings.json");
|
|
try {
|
|
await fs.access(settingsPath);
|
|
// If settings exist but no activity, CLI might be set up but not authenticated
|
|
if (!auth.hasCliAuth) {
|
|
// Try to check for other indicators of auth
|
|
const sessionsDir = path.join(claudeDir, "projects");
|
|
try {
|
|
const sessions = await fs.readdir(sessionsDir);
|
|
if (sessions.length > 0) {
|
|
auth.hasCliAuth = true;
|
|
auth.authenticated = true;
|
|
auth.method = "cli_authenticated";
|
|
}
|
|
} catch {
|
|
// Sessions directory doesn't exist
|
|
}
|
|
}
|
|
} catch {
|
|
// Settings file doesn't exist
|
|
}
|
|
|
|
// Check for credentials file (OAuth tokens from claude login) - legacy/alternative auth
|
|
const credentialsPath = path.join(claudeDir, "credentials.json");
|
|
try {
|
|
const credentialsContent = await fs.readFile(credentialsPath, "utf-8");
|
|
const credentials = JSON.parse(credentialsContent);
|
|
auth.hasCredentialsFile = true;
|
|
|
|
// Check what type of token is in credentials
|
|
if (credentials.oauth_token || credentials.access_token) {
|
|
auth.hasStoredOAuthToken = true;
|
|
auth.oauthTokenValid = true;
|
|
auth.authenticated = true;
|
|
auth.method = "oauth_token"; // Stored OAuth token from credentials file
|
|
} else if (credentials.api_key) {
|
|
auth.apiKeyValid = true;
|
|
auth.authenticated = true;
|
|
auth.method = "api_key"; // Stored API key in credentials file
|
|
}
|
|
} catch {
|
|
// No credentials file or invalid format
|
|
}
|
|
|
|
// Environment variables override stored credentials (higher priority)
|
|
if (auth.hasEnvOAuthToken) {
|
|
auth.authenticated = true;
|
|
auth.oauthTokenValid = true;
|
|
auth.method = "oauth_token_env"; // OAuth token from CLAUDE_CODE_OAUTH_TOKEN env var
|
|
} else if (auth.hasEnvApiKey) {
|
|
auth.authenticated = true;
|
|
auth.apiKeyValid = true;
|
|
auth.method = "api_key_env"; // API key from ANTHROPIC_API_KEY env var
|
|
}
|
|
|
|
// In-memory stored OAuth token (from setup wizard - subscription auth)
|
|
if (!auth.authenticated && getApiKey("anthropic_oauth_token")) {
|
|
auth.authenticated = true;
|
|
auth.oauthTokenValid = true;
|
|
auth.method = "oauth_token"; // Stored OAuth token from setup wizard
|
|
}
|
|
|
|
// In-memory stored API key (from settings UI - pay-per-use)
|
|
if (!auth.authenticated && getApiKey("anthropic")) {
|
|
auth.authenticated = true;
|
|
auth.apiKeyValid = true;
|
|
auth.method = "api_key"; // Manually stored API key
|
|
}
|
|
|
|
return {
|
|
status: installed ? "installed" : "not_installed",
|
|
installed,
|
|
method,
|
|
version,
|
|
path: cliPath,
|
|
auth,
|
|
};
|
|
}
|