mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
- Introduced a restricted file system wrapper to ensure all file operations are confined to the script's directory, enhancing security. - Updated various modules to utilize the new secure file system methods, replacing direct fs calls with validated operations. - Enhanced path validation in the server routes and context loaders to prevent unauthorized access to the file system. - Adjusted environment variable handling to use centralized methods for reading and writing API keys, ensuring consistent security practices. This change improves the overall security posture of the application by enforcing strict file access controls and validating paths before any operations are performed.
144 lines
4.2 KiB
TypeScript
144 lines
4.2 KiB
TypeScript
/**
|
|
* Business logic for getting Claude CLI status
|
|
*/
|
|
|
|
import { exec } from 'child_process';
|
|
import { promisify } from 'util';
|
|
import { getClaudeCliPaths, getClaudeAuthIndicators, systemPathAccess } from '@automaker/platform';
|
|
import { getApiKey } from './common.js';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
export async function getClaudeStatus() {
|
|
let installed = false;
|
|
let version = '';
|
|
let cliPath = '';
|
|
let method = 'none';
|
|
|
|
const isWindows = process.platform === 'win32';
|
|
|
|
// Try to find Claude CLI using platform-specific command
|
|
try {
|
|
// Use 'where' on Windows, 'which' on Unix-like systems
|
|
const findCommand = isWindows ? 'where claude' : 'which claude';
|
|
const { stdout } = await execAsync(findCommand);
|
|
// 'where' on Windows can return multiple paths - take the first one
|
|
cliPath = stdout.trim().split(/\r?\n/)[0];
|
|
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 from centralized system paths
|
|
const commonPaths = getClaudeCliPaths();
|
|
|
|
for (const p of commonPaths) {
|
|
try {
|
|
if (await systemPathAccess(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
|
|
const 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,
|
|
// Additional fields for detailed status
|
|
oauthTokenValid: false,
|
|
apiKeyValid: false,
|
|
hasCliAuth: false,
|
|
hasRecentActivity: false,
|
|
};
|
|
|
|
// Use centralized system paths to check Claude authentication indicators
|
|
const indicators = await getClaudeAuthIndicators();
|
|
|
|
// Check for recent activity (indicates working authentication)
|
|
if (indicators.hasStatsCacheWithActivity) {
|
|
auth.hasRecentActivity = true;
|
|
auth.hasCliAuth = true;
|
|
auth.authenticated = true;
|
|
auth.method = 'cli_authenticated';
|
|
}
|
|
|
|
// Check for settings + sessions (indicates CLI is set up)
|
|
if (!auth.hasCliAuth && indicators.hasSettingsFile && indicators.hasProjectsSessions) {
|
|
auth.hasCliAuth = true;
|
|
auth.authenticated = true;
|
|
auth.method = 'cli_authenticated';
|
|
}
|
|
|
|
// Check credentials file
|
|
if (indicators.hasCredentialsFile && indicators.credentials) {
|
|
auth.hasCredentialsFile = true;
|
|
if (indicators.credentials.hasOAuthToken) {
|
|
auth.hasStoredOAuthToken = true;
|
|
auth.oauthTokenValid = true;
|
|
auth.authenticated = true;
|
|
auth.method = 'oauth_token';
|
|
} else if (indicators.credentials.hasApiKey) {
|
|
auth.apiKeyValid = true;
|
|
auth.authenticated = true;
|
|
auth.method = 'api_key';
|
|
}
|
|
}
|
|
|
|
// Environment variables override stored credentials (higher priority)
|
|
if (auth.hasEnvApiKey) {
|
|
auth.authenticated = true;
|
|
auth.apiKeyValid = true;
|
|
auth.method = 'api_key_env';
|
|
}
|
|
|
|
// 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';
|
|
}
|
|
|
|
// 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';
|
|
}
|
|
|
|
return {
|
|
status: installed ? 'installed' : 'not_installed',
|
|
installed,
|
|
method,
|
|
version,
|
|
path: cliPath,
|
|
auth,
|
|
};
|
|
}
|