mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
Merge pull request #21 from AutoMaker-Org/fix/auth-status-display-and-console-cleanup
fix: improve auth status display and remove verbose console logging
This commit is contained in:
62
README.md
62
README.md
@@ -20,54 +20,54 @@ Automaker is an autonomous AI development studio that helps you build software f
|
||||
|
||||
## Getting Started
|
||||
|
||||
**Step 1:** Clone this repository:
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 18+
|
||||
- npm
|
||||
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
|
||||
|
||||
### Quick Start
|
||||
|
||||
```bash
|
||||
# 1. Clone the repo
|
||||
git clone git@github.com:AutoMaker-Org/automaker.git
|
||||
cd automaker
|
||||
```
|
||||
|
||||
**Step 2:** Install dependencies:
|
||||
|
||||
```bash
|
||||
# 2. Install dependencies
|
||||
npm install
|
||||
```
|
||||
|
||||
**Step 3:** Run the Claude Code setup token command:
|
||||
|
||||
```bash
|
||||
# 3. Get your Claude Code OAuth token
|
||||
claude setup-token
|
||||
```
|
||||
# ⚠️ This prints your token - don't share your screen!
|
||||
|
||||
> **⚠️ Warning:** This command will print your token to your terminal. Be careful if you're streaming or sharing your screen, as the token will be visible to anyone watching.
|
||||
|
||||
**Step 4:** Export the Claude Code OAuth token in your shell:
|
||||
|
||||
```bash
|
||||
export CLAUDE_CODE_OAUTH_TOKEN="your-token-here"
|
||||
```
|
||||
|
||||
**Step 5:** Start the development server:
|
||||
|
||||
```bash
|
||||
# 4. Set the token and run
|
||||
export CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat01-..."
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
This will start both the Next.js development server and the Electron application.
|
||||
### Authentication Options
|
||||
|
||||
**Step 6:** MOST IMPORANT: Run the Following after all is setup
|
||||
Automaker supports multiple authentication methods (in order of priority):
|
||||
|
||||
| Method | Environment Variable | Description |
|
||||
|--------|---------------------|-------------|
|
||||
| OAuth Token (env) | `CLAUDE_CODE_OAUTH_TOKEN` | From `claude setup-token` - uses your Claude subscription |
|
||||
| OAuth Token (stored) | — | Stored in app credentials file |
|
||||
| API Key (stored) | — | Anthropic API key stored in app |
|
||||
| API Key (env) | `ANTHROPIC_API_KEY` | Pay-per-use API key |
|
||||
|
||||
**Recommended:** Use `CLAUDE_CODE_OAUTH_TOKEN` if you have a Claude subscription.
|
||||
|
||||
### Persistent Setup (Optional)
|
||||
|
||||
Add to your `~/.bashrc` or `~/.zshrc`:
|
||||
|
||||
```bash
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
echo "W"
|
||||
export CLAUDE_CODE_OAUTH_TOKEN="YOUR_TOKEN_HERE"
|
||||
```
|
||||
|
||||
Then restart your terminal or run `source ~/.bashrc`.
|
||||
|
||||
## Features
|
||||
|
||||
- 📋 **Kanban Board** - Visual drag-and-drop board to manage features through backlog, in progress, waiting approval, and verified stages
|
||||
|
||||
@@ -19,14 +19,12 @@ class AgentService {
|
||||
this.stateDir = path.join(appDataPath, "agent-sessions");
|
||||
this.metadataFile = path.join(appDataPath, "sessions-metadata.json");
|
||||
await fs.mkdir(this.stateDir, { recursive: true });
|
||||
console.log("[AgentService] Initialized with state dir:", this.stateDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or resume a conversation
|
||||
*/
|
||||
async startConversation({ sessionId, workingDirectory }) {
|
||||
console.log("[AgentService] Starting conversation:", sessionId);
|
||||
|
||||
// Initialize session if it doesn't exist
|
||||
if (!this.sessions.has(sessionId)) {
|
||||
@@ -307,7 +305,7 @@ class AgentService {
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AbortError || error?.name === "AbortError") {
|
||||
console.log("[AgentService] Query aborted");
|
||||
// Query aborted
|
||||
session.isRunning = false;
|
||||
session.abortController = null;
|
||||
return { success: false, aborted: true };
|
||||
|
||||
@@ -70,9 +70,6 @@ app.whenReady().then(async () => {
|
||||
addAllowedPath(session.projectPath);
|
||||
}
|
||||
});
|
||||
console.log(
|
||||
`[Security] Pre-loaded ${allowedPaths.size} allowed paths from history`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to load sessions for security whitelist:", error);
|
||||
}
|
||||
@@ -101,7 +98,6 @@ const allowedPaths = new Set();
|
||||
function addAllowedPath(pathToAdd) {
|
||||
if (!pathToAdd) return;
|
||||
allowedPaths.add(path.resolve(pathToAdd));
|
||||
console.log(`[Security] Added allowed path: ${pathToAdd}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +337,6 @@ ipcMain.handle(
|
||||
// Write image to file
|
||||
await fs.writeFile(imageFilePath, base64Data, "base64");
|
||||
|
||||
console.log("[IPC] Saved image to .automaker/images:", imageFilePath);
|
||||
return { success: true, path: imageFilePath };
|
||||
} catch (error) {
|
||||
console.error("[IPC] Failed to save image:", error);
|
||||
@@ -640,10 +635,6 @@ ipcMain.handle(
|
||||
ipcMain.handle(
|
||||
"auto-mode:verify-feature",
|
||||
async (_, { projectPath, featureId }) => {
|
||||
console.log("[IPC] auto-mode:verify-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -669,10 +660,6 @@ ipcMain.handle(
|
||||
ipcMain.handle(
|
||||
"auto-mode:resume-feature",
|
||||
async (_, { projectPath, featureId }) => {
|
||||
console.log("[IPC] auto-mode:resume-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -723,7 +710,6 @@ ipcMain.handle(
|
||||
* and update the app_spec.txt with tech stack and implemented features
|
||||
*/
|
||||
ipcMain.handle("auto-mode:analyze-project", async (_, { projectPath }) => {
|
||||
console.log("[IPC] auto-mode:analyze-project called with:", { projectPath });
|
||||
try {
|
||||
// Add project path to allowed paths
|
||||
addAllowedPath(projectPath);
|
||||
@@ -748,7 +734,6 @@ ipcMain.handle("auto-mode:analyze-project", async (_, { projectPath }) => {
|
||||
* Stop a specific feature
|
||||
*/
|
||||
ipcMain.handle("auto-mode:stop-feature", async (_, { featureId }) => {
|
||||
console.log("[IPC] auto-mode:stop-feature called with:", { featureId });
|
||||
try {
|
||||
return await autoModeService.stopFeature({ featureId });
|
||||
} catch (error) {
|
||||
@@ -763,12 +748,6 @@ ipcMain.handle("auto-mode:stop-feature", async (_, { featureId }) => {
|
||||
ipcMain.handle(
|
||||
"auto-mode:follow-up-feature",
|
||||
async (_, { projectPath, featureId, prompt, imagePaths }) => {
|
||||
console.log("[IPC] auto-mode:follow-up-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
prompt,
|
||||
imagePaths,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -796,10 +775,6 @@ ipcMain.handle(
|
||||
ipcMain.handle(
|
||||
"auto-mode:commit-feature",
|
||||
async (_, { projectPath, featureId }) => {
|
||||
console.log("[IPC] auto-mode:commit-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -957,8 +932,6 @@ let suggestionsExecution = null;
|
||||
* @param {string} suggestionType - Type of suggestions: "features", "refactoring", "security", "performance"
|
||||
*/
|
||||
ipcMain.handle("suggestions:generate", async (_, { projectPath, suggestionType = "features" }) => {
|
||||
console.log("[IPC] suggestions:generate called with:", { projectPath, suggestionType });
|
||||
|
||||
try {
|
||||
// Check if already running
|
||||
if (suggestionsExecution && suggestionsExecution.isActive()) {
|
||||
@@ -1008,7 +981,6 @@ ipcMain.handle("suggestions:generate", async (_, { projectPath, suggestionType =
|
||||
* Stop the current suggestions generation
|
||||
*/
|
||||
ipcMain.handle("suggestions:stop", async () => {
|
||||
console.log("[IPC] suggestions:stop called");
|
||||
try {
|
||||
if (suggestionsExecution && suggestionsExecution.abortController) {
|
||||
suggestionsExecution.abortController.abort();
|
||||
@@ -1081,10 +1053,6 @@ ipcMain.handle("openai:test-connection", async (_, { apiKey }) => {
|
||||
ipcMain.handle(
|
||||
"worktree:revert-feature",
|
||||
async (_, { projectPath, featureId }) => {
|
||||
console.log("[IPC] worktree:revert-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -1117,10 +1085,6 @@ let specRegenerationExecution = null;
|
||||
ipcMain.handle(
|
||||
"spec-regeneration:generate",
|
||||
async (_, { projectPath, projectDefinition }) => {
|
||||
console.log("[IPC] spec-regeneration:generate called with:", {
|
||||
projectPath,
|
||||
});
|
||||
|
||||
try {
|
||||
// Add project path to allowed paths
|
||||
addAllowedPath(projectPath);
|
||||
@@ -1182,7 +1146,6 @@ ipcMain.handle(
|
||||
* Stop the current spec regeneration
|
||||
*/
|
||||
ipcMain.handle("spec-regeneration:stop", async () => {
|
||||
console.log("[IPC] spec-regeneration:stop called");
|
||||
try {
|
||||
if (
|
||||
specRegenerationExecution &&
|
||||
@@ -1216,11 +1179,6 @@ ipcMain.handle("spec-regeneration:status", () => {
|
||||
ipcMain.handle(
|
||||
"spec-regeneration:create",
|
||||
async (_, { projectPath, projectOverview, generateFeatures = true }) => {
|
||||
console.log("[IPC] spec-regeneration:create called with:", {
|
||||
projectPath,
|
||||
generateFeatures,
|
||||
});
|
||||
|
||||
try {
|
||||
// Add project path to allowed paths
|
||||
addAllowedPath(projectPath);
|
||||
@@ -1282,11 +1240,6 @@ ipcMain.handle(
|
||||
ipcMain.handle(
|
||||
"worktree:merge-feature",
|
||||
async (_, { projectPath, featureId, options }) => {
|
||||
console.log("[IPC] worktree:merge-feature called with:", {
|
||||
projectPath,
|
||||
featureId,
|
||||
options,
|
||||
});
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -1412,7 +1365,6 @@ ipcMain.handle("setup:claude-status", async () => {
|
||||
"credentials.json"
|
||||
);
|
||||
const result = claudeCliDetector.getFullStatus(credentialsPath);
|
||||
console.log("[IPC] setup:claude-status result:", result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("[IPC] setup:claude-status error:", error);
|
||||
@@ -1537,7 +1489,6 @@ ipcMain.handle("setup:auth-codex", async (event, { apiKey }) => {
|
||||
*/
|
||||
ipcMain.handle("setup:store-api-key", async (_, { provider, apiKey }) => {
|
||||
try {
|
||||
console.log("[IPC] setup:store-api-key called for provider:", provider);
|
||||
const configPath = path.join(app.getPath("userData"), "credentials.json");
|
||||
let credentials = {};
|
||||
|
||||
@@ -1559,7 +1510,6 @@ ipcMain.handle("setup:store-api-key", async (_, { provider, apiKey }) => {
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
console.log("[IPC] setup:store-api-key stored successfully for:", provider);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("[IPC] setup:store-api-key error:", error);
|
||||
|
||||
@@ -24,7 +24,6 @@ class ClaudeCliDetector {
|
||||
const shell = process.env.SHELL || "/bin/bash";
|
||||
const shellName = path.basename(shell);
|
||||
|
||||
// Common shell config files
|
||||
const configFiles = [];
|
||||
if (shellName.includes("zsh")) {
|
||||
configFiles.push(path.join(homeDir, ".zshrc"));
|
||||
@@ -36,7 +35,6 @@ class ClaudeCliDetector {
|
||||
configFiles.push(path.join(homeDir, ".profile"));
|
||||
}
|
||||
|
||||
// Also check common locations
|
||||
const commonPaths = [
|
||||
path.join(homeDir, ".local", "bin"),
|
||||
path.join(homeDir, ".cargo", "bin"),
|
||||
@@ -45,12 +43,10 @@ class ClaudeCliDetector {
|
||||
path.join(homeDir, "bin"),
|
||||
];
|
||||
|
||||
// Try to extract PATH additions from config files
|
||||
for (const configFile of configFiles) {
|
||||
if (fs.existsSync(configFile)) {
|
||||
try {
|
||||
const content = fs.readFileSync(configFile, "utf-8");
|
||||
// Look for PATH exports that might include claude installation paths
|
||||
const pathMatches = content.match(
|
||||
/export\s+PATH=["']?([^"'\n]+)["']?/g
|
||||
);
|
||||
@@ -71,14 +67,12 @@ class ClaudeCliDetector {
|
||||
}
|
||||
}
|
||||
|
||||
return [...new Set(commonPaths)]; // Remove duplicates
|
||||
return [...new Set(commonPaths)];
|
||||
}
|
||||
|
||||
static detectClaudeInstallation() {
|
||||
console.log("[ClaudeCliDetector] Detecting Claude installation...");
|
||||
|
||||
try {
|
||||
// Method 1: Check if 'claude' command is in PATH (Unix)
|
||||
// Check if 'claude' command is in PATH (Unix)
|
||||
if (process.platform !== "win32") {
|
||||
try {
|
||||
const claudePath = execSync("which claude 2>/dev/null", {
|
||||
@@ -86,12 +80,6 @@ class ClaudeCliDetector {
|
||||
}).trim();
|
||||
if (claudePath) {
|
||||
const version = this.getClaudeVersion(claudePath);
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Found claude at:",
|
||||
claudePath,
|
||||
"version:",
|
||||
version
|
||||
);
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
@@ -100,11 +88,11 @@ class ClaudeCliDetector {
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// CLI not in PATH, continue checking other locations
|
||||
// CLI not in PATH
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Check Windows path
|
||||
// Check Windows path
|
||||
if (process.platform === "win32") {
|
||||
try {
|
||||
const claudePath = execSync("where claude 2>nul", {
|
||||
@@ -114,12 +102,6 @@ class ClaudeCliDetector {
|
||||
.split("\n")[0];
|
||||
if (claudePath) {
|
||||
const version = this.getClaudeVersion(claudePath);
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Found claude at:",
|
||||
claudePath,
|
||||
"version:",
|
||||
version
|
||||
);
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
@@ -132,7 +114,7 @@ class ClaudeCliDetector {
|
||||
}
|
||||
}
|
||||
|
||||
// Method 3: Check for local installation
|
||||
// Check for local installation
|
||||
const localClaudePath = path.join(
|
||||
os.homedir(),
|
||||
".claude",
|
||||
@@ -141,12 +123,6 @@ class ClaudeCliDetector {
|
||||
);
|
||||
if (fs.existsSync(localClaudePath)) {
|
||||
const version = this.getClaudeVersion(localClaudePath);
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Found local claude at:",
|
||||
localClaudePath,
|
||||
"version:",
|
||||
version
|
||||
);
|
||||
return {
|
||||
installed: true,
|
||||
path: localClaudePath,
|
||||
@@ -155,7 +131,7 @@ class ClaudeCliDetector {
|
||||
};
|
||||
}
|
||||
|
||||
// Method 4: Check common installation locations (including those from shell config)
|
||||
// Check common installation locations
|
||||
const commonPaths = this.getUpdatedPathFromShellConfig();
|
||||
const binaryNames = ["claude", "claude-code"];
|
||||
|
||||
@@ -165,12 +141,6 @@ class ClaudeCliDetector {
|
||||
if (fs.existsSync(claudePath)) {
|
||||
try {
|
||||
const version = this.getClaudeVersion(claudePath);
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Found claude at:",
|
||||
claudePath,
|
||||
"version:",
|
||||
version
|
||||
);
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
@@ -178,13 +148,13 @@ class ClaudeCliDetector {
|
||||
method: "cli",
|
||||
};
|
||||
} catch (error) {
|
||||
// File exists but can't get version, might not be executable
|
||||
// File exists but can't get version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 5: Try to source shell config and check PATH again (for Unix)
|
||||
// Try to source shell config and check PATH again (Unix)
|
||||
if (process.platform !== "win32") {
|
||||
try {
|
||||
const shell = process.env.SHELL || "/bin/bash";
|
||||
@@ -205,12 +175,6 @@ class ClaudeCliDetector {
|
||||
}).trim();
|
||||
if (claudePath && claudePath.startsWith("/")) {
|
||||
const version = this.getClaudeVersion(claudePath);
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Found claude via shell config at:",
|
||||
claudePath,
|
||||
"version:",
|
||||
version
|
||||
);
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
@@ -220,11 +184,10 @@ class ClaudeCliDetector {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Failed to source shell config or find claude
|
||||
// Failed to source shell config
|
||||
}
|
||||
}
|
||||
|
||||
console.log("[ClaudeCliDetector] Claude CLI not found");
|
||||
return {
|
||||
installed: false,
|
||||
path: null,
|
||||
@@ -232,10 +195,6 @@ class ClaudeCliDetector {
|
||||
method: "none",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[ClaudeCliDetector] Error detecting Claude installation:",
|
||||
error
|
||||
);
|
||||
return {
|
||||
installed: false,
|
||||
path: null,
|
||||
@@ -274,12 +233,9 @@ class ClaudeCliDetector {
|
||||
* @returns {Object} Authentication status
|
||||
*/
|
||||
static getAuthStatus(appCredentialsPath) {
|
||||
console.log("[ClaudeCliDetector] Checking auth status...");
|
||||
|
||||
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
||||
console.log("[ClaudeCliDetector] Env ANTHROPIC_API_KEY:", !!envApiKey);
|
||||
const envOAuthToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||
|
||||
// Check app's stored credentials
|
||||
let storedOAuthToken = null;
|
||||
let storedApiKey = null;
|
||||
|
||||
@@ -290,51 +246,41 @@ class ClaudeCliDetector {
|
||||
storedOAuthToken = credentials.anthropic_oauth_token || null;
|
||||
storedApiKey =
|
||||
credentials.anthropic || credentials.anthropic_api_key || null;
|
||||
console.log("[ClaudeCliDetector] App credentials:", {
|
||||
hasOAuthToken: !!storedOAuthToken,
|
||||
hasApiKey: !!storedApiKey,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"[ClaudeCliDetector] Error reading app credentials:",
|
||||
error
|
||||
);
|
||||
// Ignore credential read errors
|
||||
}
|
||||
}
|
||||
|
||||
// Determine authentication method
|
||||
// Priority: Stored OAuth Token > Stored API Key > Env API Key
|
||||
// Authentication priority (highest to lowest):
|
||||
// 1. Environment OAuth Token (CLAUDE_CODE_OAUTH_TOKEN)
|
||||
// 2. Stored OAuth Token (from credentials file)
|
||||
// 3. Stored API Key (from credentials file)
|
||||
// 4. Environment API Key (ANTHROPIC_API_KEY)
|
||||
let authenticated = false;
|
||||
let method = "none";
|
||||
|
||||
if (storedOAuthToken) {
|
||||
if (envOAuthToken) {
|
||||
authenticated = true;
|
||||
method = "oauth_token_env";
|
||||
} else if (storedOAuthToken) {
|
||||
authenticated = true;
|
||||
method = "oauth_token";
|
||||
console.log(
|
||||
"[ClaudeCliDetector] Using stored OAuth token (subscription)"
|
||||
);
|
||||
} else if (storedApiKey) {
|
||||
authenticated = true;
|
||||
method = "api_key";
|
||||
console.log("[ClaudeCliDetector] Using stored API key");
|
||||
} else if (envApiKey) {
|
||||
authenticated = true;
|
||||
method = "api_key_env";
|
||||
console.log("[ClaudeCliDetector] Using environment API key");
|
||||
} else {
|
||||
console.log("[ClaudeCliDetector] No authentication found");
|
||||
}
|
||||
|
||||
const result = {
|
||||
return {
|
||||
authenticated,
|
||||
method,
|
||||
hasStoredOAuthToken: !!storedOAuthToken,
|
||||
hasStoredApiKey: !!storedApiKey,
|
||||
hasEnvApiKey: !!envApiKey,
|
||||
hasEnvOAuthToken: !!envOAuthToken,
|
||||
};
|
||||
|
||||
console.log("[ClaudeCliDetector] Auth status result:", result);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Get installation info (installation status only, no auth)
|
||||
|
||||
@@ -32,35 +32,28 @@ class CodexCliDetector {
|
||||
* @returns {Object} Authentication status
|
||||
*/
|
||||
static checkAuth() {
|
||||
console.log('[CodexCliDetector] Checking auth status...');
|
||||
try {
|
||||
const authPath = this.getAuthPath();
|
||||
const envApiKey = process.env.OPENAI_API_KEY;
|
||||
console.log('[CodexCliDetector] Auth path:', authPath);
|
||||
console.log('[CodexCliDetector] Has env API key:', !!envApiKey);
|
||||
|
||||
// First, try to verify authentication using codex CLI command if available
|
||||
// Try to verify authentication using codex CLI command if available
|
||||
try {
|
||||
const detection = this.detectCodexInstallation();
|
||||
if (detection.installed) {
|
||||
try {
|
||||
// Use 'codex login status' to verify authentication
|
||||
const statusOutput = execSync(`"${detection.path || 'codex'}" login status 2>/dev/null`, {
|
||||
const statusOutput = execSync(`"${detection.path || 'codex'}" login status 2>/dev/null`, {
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
// If command succeeds and shows logged in status
|
||||
|
||||
if (statusOutput && (statusOutput.includes('Logged in') || statusOutput.includes('Authenticated'))) {
|
||||
const result = {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'cli_verified',
|
||||
hasAuthFile: fs.existsSync(authPath),
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_verified):', result);
|
||||
return result;
|
||||
}
|
||||
} catch (statusError) {
|
||||
// status command failed, continue with file-based check
|
||||
@@ -72,63 +65,47 @@ class CodexCliDetector {
|
||||
|
||||
// Check if auth file exists
|
||||
if (fs.existsSync(authPath)) {
|
||||
console.log('[CodexCliDetector] Auth file exists, reading content...');
|
||||
let auth = null;
|
||||
try {
|
||||
const content = fs.readFileSync(authPath, 'utf-8');
|
||||
auth = JSON.parse(content);
|
||||
console.log('[CodexCliDetector] Auth file content keys:', Object.keys(auth));
|
||||
console.log('[CodexCliDetector] Auth file has token object:', !!auth.token);
|
||||
if (auth.token) {
|
||||
console.log('[CodexCliDetector] Token object keys:', Object.keys(auth.token));
|
||||
}
|
||||
|
||||
// Check for token object structure (from codex auth login)
|
||||
// Structure: { token: { Id_token, access_token, refresh_token }, last_refresh: ... }
|
||||
// Check for token object structure
|
||||
if (auth.token && typeof auth.token === 'object') {
|
||||
const token = auth.token;
|
||||
if (token.Id_token || token.access_token || token.refresh_token || token.id_token) {
|
||||
const result = {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens', // Distinguish token-based auth from API key auth
|
||||
method: 'cli_tokens',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for tokens at root level (alternative structure)
|
||||
// Check for tokens at root level
|
||||
if (auth.access_token || auth.refresh_token || auth.Id_token || auth.id_token) {
|
||||
const result = {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens', // These are tokens, not API keys
|
||||
method: 'cli_tokens',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens - root level):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for various possible API key fields that codex might use
|
||||
// Note: access_token is NOT an API key, it's a token, so we check for it above
|
||||
// Check for API key fields
|
||||
if (auth.api_key || auth.openai_api_key || auth.apiKey) {
|
||||
const result = {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (auth_file - API key):', result);
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[CodexCliDetector] Error reading/parsing auth file:', error.message);
|
||||
// If we can't parse the file, we can't determine auth status
|
||||
return {
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
@@ -138,10 +115,7 @@ class CodexCliDetector {
|
||||
};
|
||||
}
|
||||
|
||||
// Also check if the file has any meaningful content (non-empty object)
|
||||
// This is a fallback - but we should still try to detect if it's tokens
|
||||
if (!auth) {
|
||||
// File exists but couldn't be parsed
|
||||
return {
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
@@ -150,121 +124,32 @@ class CodexCliDetector {
|
||||
authPath
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const keys = Object.keys(auth);
|
||||
console.log('[CodexCliDetector] File has content, keys:', keys);
|
||||
if (keys.length > 0) {
|
||||
// Check again for tokens in case we missed them (maybe nested differently)
|
||||
const hasTokens = keys.some(key =>
|
||||
key.toLowerCase().includes('token') ||
|
||||
const hasTokens = keys.some(key =>
|
||||
key.toLowerCase().includes('token') ||
|
||||
key.toLowerCase().includes('refresh') ||
|
||||
(auth[key] && typeof auth[key] === 'object' && (
|
||||
auth[key].access_token || auth[key].refresh_token || auth[key].Id_token || auth[key].id_token
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
if (hasTokens) {
|
||||
const result = {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'cli_tokens',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (cli_tokens - fallback detection):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// File exists and has content, likely authenticated
|
||||
// Try to verify by checking if codex command works
|
||||
try {
|
||||
const detection = this.detectCodexInstallation();
|
||||
if (detection.installed) {
|
||||
// Try to verify auth by running a simple command
|
||||
try {
|
||||
execSync(`"${detection.path || 'codex'}" --version 2>/dev/null`, {
|
||||
encoding: 'utf-8',
|
||||
timeout: 3000
|
||||
});
|
||||
// If command succeeds, assume authenticated
|
||||
// But check if it's likely tokens vs API key based on file structure
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (verified via CLI, method:', result.method, '):', result);
|
||||
return result;
|
||||
} catch (cmdError) {
|
||||
// Command failed, but file exists - might still be authenticated
|
||||
// Check if it's likely tokens
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (file exists, method:', result.method, '):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} catch (verifyError) {
|
||||
// Verification failed, but file exists with content
|
||||
// Check if it's likely tokens
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (fallback, method:', result.method, '):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check environment variable
|
||||
if (envApiKey) {
|
||||
const result = {
|
||||
authenticated: true,
|
||||
method: 'env_var',
|
||||
hasAuthFile: false,
|
||||
hasEnvKey: true,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (env_var):', result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// If auth file exists but we didn't find standard keys,
|
||||
// check if codex CLI is installed and try to verify auth
|
||||
if (fs.existsSync(authPath)) {
|
||||
try {
|
||||
const detection = this.detectCodexInstallation();
|
||||
if (detection.installed) {
|
||||
// Auth file exists and CLI is installed - likely authenticated
|
||||
// The file existing is a good indicator that login was successful
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
};
|
||||
}
|
||||
} catch (verifyError) {
|
||||
// Verification attempt failed, but file exists
|
||||
// Assume authenticated if file exists
|
||||
// File exists and has content - check if it's tokens or API key
|
||||
const likelyTokens = keys.some(key => key.toLowerCase().includes('token') || key.toLowerCase().includes('refresh'));
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'auth_file',
|
||||
method: likelyTokens ? 'cli_tokens' : 'auth_file',
|
||||
hasAuthFile: true,
|
||||
hasEnvKey: !!envApiKey,
|
||||
authPath
|
||||
@@ -272,24 +157,30 @@ class CodexCliDetector {
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
// Check environment variable
|
||||
if (envApiKey) {
|
||||
return {
|
||||
authenticated: true,
|
||||
method: 'env_var',
|
||||
hasAuthFile: false,
|
||||
hasEnvKey: true,
|
||||
authPath
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
hasAuthFile: false,
|
||||
hasEnvKey: false,
|
||||
authPath
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (not authenticated):', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[CodexCliDetector] Error checking auth:', error);
|
||||
const result = {
|
||||
return {
|
||||
authenticated: false,
|
||||
method: 'none',
|
||||
error: error.message
|
||||
};
|
||||
console.log('[CodexCliDetector] Auth result (error):', result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -409,7 +300,7 @@ class CodexCliDetector {
|
||||
method: 'none'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[CodexCliDetector] Error detecting Codex installation:', error);
|
||||
// Error detecting Codex installation
|
||||
return {
|
||||
installed: false,
|
||||
path: null,
|
||||
|
||||
@@ -764,7 +764,13 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
||||
}
|
||||
|
||||
for (const feature of detectedFeatures) {
|
||||
await api.features.create(currentProject.path, feature);
|
||||
await api.features.create(currentProject.path, {
|
||||
id: crypto.randomUUID(),
|
||||
category: feature.category,
|
||||
description: feature.description,
|
||||
steps: feature.steps,
|
||||
status: "backlog",
|
||||
});
|
||||
}
|
||||
|
||||
setFeatureListGenerated(true);
|
||||
|
||||
@@ -317,7 +317,7 @@ export function InterviewView() {
|
||||
id: `feature-${Date.now()}-0`,
|
||||
category: "Core",
|
||||
description: "Initial project setup",
|
||||
status: "backlog",
|
||||
status: "backlog" as const,
|
||||
steps: [
|
||||
"Step 1: Review app_spec.txt",
|
||||
"Step 2: Set up development environment",
|
||||
@@ -325,7 +325,9 @@ export function InterviewView() {
|
||||
],
|
||||
skipTests: true,
|
||||
};
|
||||
await api.features.create(fullProjectPath, initialFeature);
|
||||
if (api.features) {
|
||||
await api.features.create(fullProjectPath, initialFeature);
|
||||
}
|
||||
|
||||
const project = {
|
||||
id: `project-${Date.now()}`,
|
||||
|
||||
@@ -53,41 +53,22 @@ export function AuthenticationStatusDisplay({
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
|
||||
<span className="text-muted-foreground">
|
||||
Method:{" "}
|
||||
<span className="font-mono text-foreground">
|
||||
{claudeAuthStatus.method === "oauth"
|
||||
? "OAuth Token"
|
||||
: claudeAuthStatus.method === "api_key"
|
||||
? "API Key"
|
||||
: "Unknown"}
|
||||
</span>
|
||||
<span className="text-green-400 font-medium">Authenticated</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>
|
||||
{claudeAuthStatus.method === "oauth_token_env"
|
||||
? "Using CLAUDE_CODE_OAUTH_TOKEN"
|
||||
: claudeAuthStatus.method === "oauth_token"
|
||||
? "Using stored OAuth token"
|
||||
: claudeAuthStatus.method === "api_key_env"
|
||||
? "Using ANTHROPIC_API_KEY"
|
||||
: claudeAuthStatus.method === "api_key"
|
||||
? "Using stored API key"
|
||||
: "Unknown method"}
|
||||
</span>
|
||||
</div>
|
||||
{claudeAuthStatus.oauthTokenValid && (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>OAuth token configured</span>
|
||||
</div>
|
||||
)}
|
||||
{claudeAuthStatus.apiKeyValid && (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>API key configured</span>
|
||||
</div>
|
||||
)}
|
||||
{apiKeyStatus?.hasAnthropicKey && (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Environment variable detected</span>
|
||||
</div>
|
||||
)}
|
||||
{apiKeys.anthropic && (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Manual API key in settings</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : apiKeyStatus?.hasAnthropicKey ? (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
@@ -100,9 +81,9 @@ export function AuthenticationStatusDisplay({
|
||||
<span>Using manual API key from settings</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
|
||||
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
|
||||
<span className="text-xs">Not Setup</span>
|
||||
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
|
||||
<AlertCircle className="w-3 h-3 shrink-0" />
|
||||
<span className="text-xs">Not configured</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -121,44 +102,21 @@ export function AuthenticationStatusDisplay({
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
|
||||
<span className="text-muted-foreground">
|
||||
Method:{" "}
|
||||
<span className="font-mono text-foreground">
|
||||
{codexAuthStatus.method === "cli_verified" ||
|
||||
codexAuthStatus.method === "cli_tokens"
|
||||
? "CLI Login (OpenAI Account)"
|
||||
: codexAuthStatus.method === "api_key"
|
||||
? "API Key (Auth File)"
|
||||
: codexAuthStatus.method === "env"
|
||||
? "API Key (Environment)"
|
||||
: "Unknown"}
|
||||
</span>
|
||||
<span className="text-green-400 font-medium">Authenticated</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>
|
||||
{codexAuthStatus.method === "cli_verified" ||
|
||||
codexAuthStatus.method === "cli_tokens"
|
||||
? "Using CLI login (OpenAI account)"
|
||||
: codexAuthStatus.method === "api_key"
|
||||
? "Using stored API key"
|
||||
: codexAuthStatus.method === "env"
|
||||
? "Using OPENAI_API_KEY"
|
||||
: "Unknown method"}
|
||||
</span>
|
||||
</div>
|
||||
{codexAuthStatus.method === "cli_verified" ||
|
||||
codexAuthStatus.method === "cli_tokens" ? (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>Account authenticated</span>
|
||||
</div>
|
||||
) : codexAuthStatus.apiKeyValid ? (
|
||||
<div className="flex items-center gap-2 text-green-400">
|
||||
<CheckCircle2 className="w-3 h-3 shrink-0" />
|
||||
<span>API key configured</span>
|
||||
</div>
|
||||
) : null}
|
||||
{apiKeyStatus?.hasOpenAIKey && (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Environment variable detected</span>
|
||||
</div>
|
||||
)}
|
||||
{apiKeys.openai && (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Manual API key in settings</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : apiKeyStatus?.hasOpenAIKey ? (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
@@ -171,9 +129,9 @@ export function AuthenticationStatusDisplay({
|
||||
<span>Using manual API key from settings</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
|
||||
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
|
||||
<span className="text-xs">Not Setup</span>
|
||||
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
|
||||
<AlertCircle className="w-3 h-3 shrink-0" />
|
||||
<span className="text-xs">Not configured</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -189,19 +147,31 @@ export function AuthenticationStatusDisplay({
|
||||
</div>
|
||||
<div className="space-y-1.5 text-xs min-h-12">
|
||||
{apiKeyStatus?.hasGoogleKey ? (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Using environment variable (GOOGLE_API_KEY)</span>
|
||||
</div>
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
|
||||
<span className="text-green-400 font-medium">Authenticated</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Using GOOGLE_API_KEY</span>
|
||||
</div>
|
||||
</>
|
||||
) : apiKeys.google ? (
|
||||
<div className="flex items-center gap-2 text-blue-400">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Using manual API key from settings</span>
|
||||
</div>
|
||||
<>
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
|
||||
<span className="text-green-400 font-medium">Authenticated</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Info className="w-3 h-3 shrink-0" />
|
||||
<span>Using stored API key</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
|
||||
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
|
||||
<span className="text-xs">Not Setup</span>
|
||||
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
|
||||
<AlertCircle className="w-3 h-3 shrink-0" />
|
||||
<span className="text-xs">Not configured</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Project } from "@/store/app-store";
|
||||
import type { Project } from "@/lib/electron";
|
||||
|
||||
interface DeleteProjectDialogProps {
|
||||
open: boolean;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Project } from "@/store/app-store";
|
||||
import type { Project } from "@/lib/electron";
|
||||
import type { NavigationItem } from "../config/navigation";
|
||||
|
||||
interface SettingsNavigationProps {
|
||||
|
||||
@@ -69,17 +69,20 @@ export function useCliStatus() {
|
||||
const result = await api.setup.getClaudeStatus();
|
||||
if (result.success && result.auth) {
|
||||
const auth = result.auth;
|
||||
// Validate method is one of the expected values, default to "none"
|
||||
const validMethods = ["oauth_token_env", "oauth_token", "api_key", "api_key_env", "none"] as const;
|
||||
type AuthMethod = typeof validMethods[number];
|
||||
const method: AuthMethod = validMethods.includes(auth.method as AuthMethod)
|
||||
? (auth.method as AuthMethod)
|
||||
: "none";
|
||||
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),
|
||||
method,
|
||||
hasCredentialsFile: auth.hasCredentialsFile ?? false,
|
||||
oauthTokenValid: auth.hasStoredOAuthToken,
|
||||
oauthTokenValid: auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
|
||||
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
|
||||
hasEnvOAuthToken: auth.hasEnvOAuthToken,
|
||||
hasEnvApiKey: auth.hasEnvApiKey,
|
||||
};
|
||||
setClaudeAuthStatus(authStatus);
|
||||
}
|
||||
|
||||
@@ -233,19 +233,22 @@ function ClaudeSetupStep({
|
||||
setClaudeCliStatus(cliStatus);
|
||||
|
||||
if (result.auth) {
|
||||
// Validate method is one of the expected values, default to "none"
|
||||
const validMethods = ["oauth_token_env", "oauth_token", "api_key", "api_key_env", "none"] as const;
|
||||
type AuthMethod = typeof validMethods[number];
|
||||
const method: AuthMethod = validMethods.includes(result.auth.method as AuthMethod)
|
||||
? (result.auth.method as AuthMethod)
|
||||
: "none";
|
||||
const authStatus = {
|
||||
authenticated: result.auth.authenticated,
|
||||
method: result.auth.method === "oauth_token"
|
||||
? "oauth"
|
||||
: result.auth.method?.includes("api_key")
|
||||
? "api_key"
|
||||
: "none",
|
||||
method,
|
||||
hasCredentialsFile: false,
|
||||
oauthTokenValid: result.auth.hasStoredOAuthToken,
|
||||
oauthTokenValid: result.auth.hasStoredOAuthToken || result.auth.hasEnvOAuthToken,
|
||||
apiKeyValid: result.auth.hasStoredApiKey || result.auth.hasEnvApiKey,
|
||||
hasEnvOAuthToken: result.auth.hasEnvOAuthToken,
|
||||
hasEnvApiKey: result.auth.hasEnvApiKey,
|
||||
};
|
||||
console.log("[Claude Setup] Auth Status:", authStatus);
|
||||
setClaudeAuthStatus(authStatus as any);
|
||||
setClaudeAuthStatus(authStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,7 +358,7 @@ function ClaudeSetupStep({
|
||||
if (result.success) {
|
||||
setClaudeAuthStatus({
|
||||
authenticated: true,
|
||||
method: "oauth",
|
||||
method: "oauth_token",
|
||||
hasCredentialsFile: false,
|
||||
oauthTokenValid: true,
|
||||
});
|
||||
@@ -433,8 +436,8 @@ function ClaudeSetupStep({
|
||||
|
||||
const getAuthMethodLabel = () => {
|
||||
if (!isAuthenticated) return null;
|
||||
if (claudeAuthStatus?.method === "oauth") return "Subscription Token";
|
||||
if (apiKeys.anthropic || claudeAuthStatus?.method === "api_key") return "API Key";
|
||||
if (claudeAuthStatus?.method === "oauth_token_env" || claudeAuthStatus?.method === "oauth_token") return "Subscription Token";
|
||||
if (apiKeys.anthropic || claudeAuthStatus?.method === "api_key" || claudeAuthStatus?.method === "api_key_env") return "API Key";
|
||||
return "Authenticated";
|
||||
};
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ export interface StatResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
// Auto Mode types - Import from electron.d.ts to avoid duplication
|
||||
import type {
|
||||
// Re-export types from electron.d.ts for external use
|
||||
export type {
|
||||
AutoModeEvent,
|
||||
ModelDefinition,
|
||||
ProviderStatus,
|
||||
@@ -55,6 +55,13 @@ import type {
|
||||
FileStatus,
|
||||
} from "@/types/electron";
|
||||
|
||||
// Import types for internal use in this file
|
||||
import type {
|
||||
AutoModeEvent,
|
||||
WorktreeAPI,
|
||||
GitAPI,
|
||||
} from "@/types/electron";
|
||||
|
||||
// Feature type - Import from app-store
|
||||
import type { Feature } from "@/store/app-store";
|
||||
|
||||
@@ -308,17 +315,19 @@ export interface ElectronAPI {
|
||||
getClaudeStatus: () => Promise<{
|
||||
success: boolean;
|
||||
status?: string;
|
||||
installed?: boolean;
|
||||
method?: string;
|
||||
version?: string;
|
||||
path?: string;
|
||||
auth?: {
|
||||
authenticated: boolean;
|
||||
method: string;
|
||||
hasCredentialsFile: boolean;
|
||||
hasToken: boolean;
|
||||
hasCredentialsFile?: boolean;
|
||||
hasToken?: boolean;
|
||||
hasStoredOAuthToken?: boolean;
|
||||
hasStoredApiKey?: boolean;
|
||||
hasEnvApiKey?: boolean;
|
||||
hasEnvOAuthToken?: boolean;
|
||||
};
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
@@ -19,7 +19,10 @@ export interface ProjectInitResult {
|
||||
* Required files and directories in the .automaker directory
|
||||
* Note: app_spec.txt is NOT created automatically - user must set it up via the spec editor
|
||||
*/
|
||||
const REQUIRED_STRUCTURE = {
|
||||
const REQUIRED_STRUCTURE: {
|
||||
directories: string[];
|
||||
files: Record<string, string>;
|
||||
} = {
|
||||
directories: [
|
||||
".automaker",
|
||||
".automaker/context",
|
||||
|
||||
@@ -13,10 +13,12 @@ export interface CliStatus {
|
||||
// Claude Auth Status
|
||||
export interface ClaudeAuthStatus {
|
||||
authenticated: boolean;
|
||||
method: "oauth" | "api_key" | "none";
|
||||
hasCredentialsFile: boolean;
|
||||
method: "oauth_token_env" | "oauth_token" | "api_key" | "api_key_env" | "none";
|
||||
hasCredentialsFile?: boolean;
|
||||
oauthTokenValid?: boolean;
|
||||
apiKeyValid?: boolean;
|
||||
hasEnvOAuthToken?: boolean;
|
||||
hasEnvApiKey?: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
|
||||
1
app/src/types/electron.d.ts
vendored
1
app/src/types/electron.d.ts
vendored
@@ -345,6 +345,7 @@ export interface AutoModeAPI {
|
||||
|
||||
export interface ElectronAPI {
|
||||
ping: () => Promise<string>;
|
||||
openExternalLink: (url: string) => Promise<{ success: boolean; error?: string }>;
|
||||
|
||||
// Dialog APIs
|
||||
openDirectory: () => Promise<{
|
||||
|
||||
Reference in New Issue
Block a user