Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions apps/frontend/src/main/ipc-handlers/terminal-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,17 @@ export function registerTerminalHandlers(
ipcMain.handle(
IPC_CHANNELS.CLAUDE_PROFILE_INITIALIZE,
async (_, profileId: string): Promise<IPCResult> => {
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Handler called for profileId:', profileId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

A large number of console.log and console.error statements have been added throughout this file for debugging (e.g., here and on lines 317, 319, 322, 325, 341, 350, 352, 363, 365, 369, 372, 396, 397, 401, 403, 407, 417, 426). These should be removed before merging to keep production logs clean. If some logging is desired, please use the existing debugLog and debugError utilities.

try {
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Getting profile manager...');
const profileManager = getClaudeProfileManager();
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Getting profile...');
const profile = profileManager.getProfile(profileId);
if (!profile) {
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Profile not found!');
return { success: false, error: 'Profile not found' };
}
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Profile found:', profile.name);

// Ensure the config directory exists for non-default profiles
if (!profile.isDefault && profile.configDir) {
Expand All @@ -333,6 +338,7 @@ export function registerTerminalHandlers(
const terminalId = `claude-login-${profileId}-${Date.now()}`;
const homeDir = process.env.HOME || process.env.USERPROFILE || '/tmp';

console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Creating terminal:', terminalId);
debugLog('[IPC] Initializing Claude profile:', {
profileId,
profileName: profile.name,
Expand All @@ -341,7 +347,9 @@ export function registerTerminalHandlers(
});

// Create a new terminal for the login process
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Calling terminalManager.create...');
const createResult = await terminalManager.create({ id: terminalId, cwd: homeDir });
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Terminal created:', createResult.success);

// If terminal creation failed, return the error
if (!createResult.success) {
Expand All @@ -352,17 +360,18 @@ export function registerTerminalHandlers(
}

// Wait a moment for the terminal to initialize
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Waiting 500ms for terminal init...');
await new Promise(resolve => setTimeout(resolve, 500));
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Wait complete');

// Build the login command with the profile's config dir
// Use platform-specific syntax and escaping for environment variables
// Use full path to claude CLI - no need to modify PATH since we have the absolute path
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Getting Claude CLI invocation...');
let loginCommand: string;
const { command: claudeCmd, env: claudeEnv } = await getClaudeCliInvocationAsync();
const pathPrefix = claudeEnv.PATH
? (process.platform === 'win32'
? `set "PATH=${escapeShellArgWindows(claudeEnv.PATH)}" && `
: `export PATH=${escapeShellArg(claudeEnv.PATH)} && `)
: '';
const { command: claudeCmd } = await getClaudeCliInvocationAsync();
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Got Claude CLI:', claudeCmd);

// Use the full path directly - escaping only needed for paths with spaces
const shellClaudeCmd = process.platform === 'win32'
? `"${escapeShellArgWindows(claudeCmd)}"`
: escapeShellArg(claudeCmd);
Expand All @@ -372,24 +381,30 @@ export function registerTerminalHandlers(
// SECURITY: Use Windows-specific escaping for cmd.exe
const escapedConfigDir = escapeShellArgWindows(profile.configDir);
// Windows cmd.exe syntax: set "VAR=value" with %VAR% for expansion
loginCommand = `${pathPrefix}set "CLAUDE_CONFIG_DIR=${escapedConfigDir}" && echo Config dir: %CLAUDE_CONFIG_DIR% && ${shellClaudeCmd} setup-token`;
loginCommand = `set "CLAUDE_CONFIG_DIR=${escapedConfigDir}" && echo Config dir: %CLAUDE_CONFIG_DIR% && ${shellClaudeCmd} setup-token`;
} else {
// SECURITY: Use POSIX escaping for bash/zsh
const escapedConfigDir = escapeShellArg(profile.configDir);
// Unix/Mac bash/zsh syntax: export VAR=value with $VAR for expansion
loginCommand = `${pathPrefix}export CLAUDE_CONFIG_DIR=${escapedConfigDir} && echo "Config dir: $CLAUDE_CONFIG_DIR" && ${shellClaudeCmd} setup-token`;
loginCommand = `export CLAUDE_CONFIG_DIR=${escapedConfigDir} && echo "Config dir: $CLAUDE_CONFIG_DIR" && ${shellClaudeCmd} setup-token`;
}
} else {
loginCommand = `${pathPrefix}${shellClaudeCmd} setup-token`;
// Simple command for default profile - just run setup-token
loginCommand = `${shellClaudeCmd} setup-token`;
}

console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Built login command, length:', loginCommand.length);
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Login command:', loginCommand);
debugLog('[IPC] Sending login command to terminal:', loginCommand);

// Write the login command to the terminal
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Writing command to terminal...');
terminalManager.write(terminalId, `${loginCommand}\r`);
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Command written successfully');

// Notify the renderer that an auth terminal was created
// This allows the UI to display the terminal so users can see the OAuth flow
console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Notifying renderer of auth terminal...');
const mainWindow = getMainWindow();
if (mainWindow) {
mainWindow.webContents.send(IPC_CHANNELS.TERMINAL_AUTH_CREATED, {
Expand All @@ -399,6 +414,7 @@ export function registerTerminalHandlers(
});
}

console.log('[IPC:CLAUDE_PROFILE_INITIALIZE] Returning success!');
return {
success: true,
data: {
Expand All @@ -407,6 +423,7 @@ export function registerTerminalHandlers(
}
};
} catch (error) {
console.error('[IPC:CLAUDE_PROFILE_INITIALIZE] EXCEPTION:', error);
debugError('[IPC] Failed to initialize Claude profile:', error);
return {
success: false,
Expand Down
44 changes: 43 additions & 1 deletion apps/frontend/src/main/terminal/pty-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,51 @@ export function setupPtyHandlers(

/**
* Write data to a PTY process
* Uses setImmediate to prevent blocking the event loop on large writes
*/
export function writeToPty(terminal: TerminalProcess, data: string): void {
terminal.pty.write(data);
console.log('[PtyManager:writeToPty] About to write to pty, data length:', data.length);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This function contains many console.log and console.error statements for debugging. Please remove them all before merging to avoid polluting production logs. If logging is necessary, consider using a dedicated, level-based logger like debugLog and debugError which are used elsewhere in the codebase.


// For large commands, write in chunks to prevent blocking
if (data.length > 1000) {
console.log('[PtyManager:writeToPty] Large write detected, using chunked write');
const chunkSize = 100; // Smaller chunks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The values 1000 and 100 are magic numbers. Please extract them into named constants at the top of the file for improved readability and maintainability, for example:

const LARGE_WRITE_THRESHOLD = 1000;
const WRITE_CHUNK_SIZE = 100;

Then use these constants in the code.

let offset = 0;
let chunkNum = 0;

const writeChunk = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeeBuilds @AndyMik90 I think we are good on this for a while, but we may want to think ahead to see what something like Tauri would look like for a Cloud version. Much better multi-threading with Rust that would allow us to avoid some of these blocking scenarios.

if (offset >= data.length) {
console.log('[PtyManager:writeToPty] Chunked write completed, total chunks:', chunkNum);
return;
}

const chunk = data.slice(offset, offset + chunkSize);
chunkNum++;
console.log('[PtyManager:writeToPty] Writing chunk', chunkNum, 'offset:', offset, 'size:', chunk.length);
try {
terminal.pty.write(chunk);
console.log('[PtyManager:writeToPty] Chunk', chunkNum, 'written');
offset += chunkSize;
// Use setImmediate to yield to the event loop between chunks
setImmediate(writeChunk);
} catch (error) {
console.error('[PtyManager:writeToPty] Chunked write FAILED at chunk', chunkNum, 'offset', offset, ':', error);
}
};

// Start the chunked write after yielding
console.log('[PtyManager:writeToPty] Scheduling first chunk...');
setImmediate(writeChunk);
console.log('[PtyManager:writeToPty] First chunk scheduled, returning');
} else {
try {
terminal.pty.write(data);
console.log('[PtyManager:writeToPty] Write completed successfully');
} catch (error) {
console.error('[PtyManager:writeToPty] Write FAILED:', error);
throw error;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The error handling here is inconsistent with the chunked write logic. While the chunked path catches and logs errors, this path re-throws the error. Since the caller (TerminalManager.write) doesn't handle exceptions, this could lead to an uncaught exception. For consistency and stability, you should log the error here using debugError instead of re-throwing it. This will require importing debugError from ../../shared/utils/debug-logger.

} catch (error) {
      console.error('[PtyManager:writeToPty] Write FAILED:', error);
    }

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function contains a lot of console.log and console.error statements for debugging the chunked writing logic. Please remove them before merging.

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Asynchronous chunked write with no completion tracking.

The function returns immediately for large writes while chunks continue writing asynchronously via setImmediate. This creates several issues:

  1. No completion notification – Callers cannot determine when the write finishes.
  2. Error handling gap – Errors in later chunks (line 184) are logged but not propagated to the caller since the function has already returned.
  3. Race condition risk – The terminal PTY could be killed or the TerminalProcess object destroyed while chunks are still queued.
  4. Very small chunks – 100-byte chunks for data >1000 bytes could queue hundreds of callbacks for large payloads.
Recommended fixes

Option 1: Return a Promise so callers can await completion:

-export function writeToPty(terminal: TerminalProcess, data: string): void {
+export function writeToPty(terminal: TerminalProcess, data: string): Promise<void> {
   console.log('[PtyManager:writeToPty] About to write to pty, data length:', data.length);
 
   // For large commands, write in chunks to prevent blocking
   if (data.length > 1000) {
     console.log('[PtyManager:writeToPty] Large write detected, using chunked write');
-    const chunkSize = 100; // Smaller chunks
+    const chunkSize = 1000; // Larger chunks to reduce overhead
     let offset = 0;
     let chunkNum = 0;
 
-    const writeChunk = () => {
+    return new Promise<void>((resolve, reject) => {
+      const writeChunk = () => {
-      if (offset >= data.length) {
-        console.log('[PtyManager:writeToPty] Chunked write completed, total chunks:', chunkNum);
-        return;
-      }
+        if (offset >= data.length) {
+          console.log('[PtyManager:writeToPty] Chunked write completed, total chunks:', chunkNum);
+          resolve();
+          return;
+        }
 
-      const chunk = data.slice(offset, offset + chunkSize);
-      chunkNum++;
-      console.log('[PtyManager:writeToPty] Writing chunk', chunkNum, 'offset:', offset, 'size:', chunk.length);
-      try {
-        terminal.pty.write(chunk);
-        console.log('[PtyManager:writeToPty] Chunk', chunkNum, 'written');
-        offset += chunkSize;
-        // Use setImmediate to yield to the event loop between chunks
-        setImmediate(writeChunk);
-      } catch (error) {
-        console.error('[PtyManager:writeToPty] Chunked write FAILED at chunk', chunkNum, 'offset', offset, ':', error);
-      }
-    };
+        const chunk = data.slice(offset, offset + chunkSize);
+        chunkNum++;
+        console.log('[PtyManager:writeToPty] Writing chunk', chunkNum, 'offset:', offset, 'size:', chunk.length);
+        try {
+          terminal.pty.write(chunk);
+          console.log('[PtyManager:writeToPty] Chunk', chunkNum, 'written');
+          offset += chunkSize;
+          setImmediate(writeChunk);
+        } catch (error) {
+          console.error('[PtyManager:writeToPty] Chunked write FAILED at chunk', chunkNum, 'offset', offset, ':', error);
+          reject(error);
+        }
+      };
 
-    // Start the chunked write after yielding
-    console.log('[PtyManager:writeToPty] Scheduling first chunk...');
-    setImmediate(writeChunk);
-    console.log('[PtyManager:writeToPty] First chunk scheduled, returning');
+      console.log('[PtyManager:writeToPty] Scheduling first chunk...');
+      setImmediate(writeChunk);
+    });
   } else {
     try {
       terminal.pty.write(data);
       console.log('[PtyManager:writeToPty] Write completed successfully');
+      return Promise.resolve();
     } catch (error) {
       console.error('[PtyManager:writeToPty] Write FAILED:', error);
-      throw error;
+      return Promise.reject(error);
     }
   }
 }

Then update the caller in terminal-manager.ts:

-  write(id: string, data: string): void {
+  async write(id: string, data: string): Promise<void> {
     console.log('[TerminalManager:write] Writing to terminal:', id, 'data length:', data.length);
     const terminal = this.terminals.get(id);
     if (terminal) {
       console.log('[TerminalManager:write] Terminal found, calling writeToPty...');
-      PtyManager.writeToPty(terminal, data);
+      await PtyManager.writeToPty(terminal, data);
       console.log('[TerminalManager:write] writeToPty completed');
     } else {
       console.error('[TerminalManager:write] Terminal NOT found:', id);
     }
   }

Option 2: If you cannot change the signature, at least track pending writes and handle terminal destruction:

+// Track pending writes per terminal
+const pendingWrites = new Map<string, number>();
+
 export function writeToPty(terminal: TerminalProcess, data: string): void {
   console.log('[PtyManager:writeToPty] About to write to pty, data length:', data.length);
 
   if (data.length > 1000) {
+    const terminalId = terminal.id;
+    pendingWrites.set(terminalId, (pendingWrites.get(terminalId) || 0) + 1);
+
     const writeChunk = () => {
+      // Check if terminal still exists
+      if (!terminal.pty) {
+        console.warn('[PtyManager:writeToPty] Terminal destroyed, aborting chunked write');
+        pendingWrites.set(terminalId, Math.max(0, (pendingWrites.get(terminalId) || 1) - 1));
+        return;
+      }
+
       if (offset >= data.length) {
         console.log('[PtyManager:writeToPty] Chunked write completed');
+        pendingWrites.set(terminalId, Math.max(0, (pendingWrites.get(terminalId) || 1) - 1));
         return;
       }
       // ... rest of chunk logic
     };
   }
 }

Committable suggestion skipped: line range outside the PR's diff.


/**
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/src/main/terminal/terminal-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,14 @@ export class TerminalManager {
* Send input to a terminal
*/
write(id: string, data: string): void {
console.log('[TerminalManager:write] Writing to terminal:', id, 'data length:', data.length);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

These console.log and console.error statements (lines 123, 126, 128, 130) appear to be for debugging. Please remove them before merging to keep production logs clean. If logging is needed, consider using the debugLog and debugError utilities available in the project.

const terminal = this.terminals.get(id);
if (terminal) {
console.log('[TerminalManager:write] Terminal found, calling writeToPty...');
PtyManager.writeToPty(terminal, data);
console.log('[TerminalManager:write] writeToPty completed');
} else {
console.error('[TerminalManager:write] Terminal NOT found:', id);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These console.log and console.error statements seem to be for debugging and should be removed before this is merged.

}

Expand Down
28 changes: 17 additions & 11 deletions apps/frontend/src/renderer/components/RateLimitModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ import { Label } from './ui/label';
import { Input } from './ui/input';
import { useRateLimitStore } from '../stores/rate-limit-store';
import { useClaudeProfileStore, loadClaudeProfiles, switchTerminalToProfile } from '../stores/claude-profile-store';
import { useToast } from '../hooks/use-toast';

const CLAUDE_UPGRADE_URL = 'https://claude.ai/upgrade';

export function RateLimitModal() {
const { t } = useTranslation('common');
const { isModalOpen, rateLimitInfo, hideRateLimitModal, clearPendingRateLimit } = useRateLimitStore();
const { profiles, activeProfileId, isSwitching } = useClaudeProfileStore();
const { toast } = useToast();
const [selectedProfileId, setSelectedProfileId] = useState<string | null>(null);
const [autoSwitchEnabled, setAutoSwitchEnabled] = useState(false);
const [isLoadingSettings, setIsLoadingSettings] = useState(false);
Expand Down Expand Up @@ -116,22 +118,26 @@ export function RateLimitModal() {
// Close the modal so user can see the terminal
hideRateLimitModal();

// Alert the user about the terminal
alert(
`A terminal has been opened to authenticate "${profileName}".\n\n` +
`Steps to complete:\n` +
`1. Check the "Agent Terminals" section in the sidebar\n` +
`2. Complete the OAuth login in your browser\n` +
`3. The token will be saved automatically\n\n` +
`Once done, return here and the account will be available.`
);
// Notify the user about the terminal (non-blocking)
toast({
title: `Authenticating "${profileName}"`,
description: 'Check the Agent Terminals section in the sidebar to complete OAuth login.',
});
} else {
alert(`Failed to start authentication: ${initResult.error || 'Please try again.'}`);
toast({
variant: 'destructive',
title: 'Failed to start authentication',
description: initResult.error || 'Please try again.',
});
}
}
} catch (err) {
console.error('Failed to add profile:', err);
alert('Failed to add profile. Please try again.');
toast({
variant: 'destructive',
title: 'Failed to add profile',
description: 'Please try again.',
});
} finally {
setIsAddingProfile(false);
}
Expand Down
28 changes: 17 additions & 11 deletions apps/frontend/src/renderer/components/SDKRateLimitModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Label } from './ui/label';
import { Input } from './ui/input';
import { useRateLimitStore } from '../stores/rate-limit-store';
import { useClaudeProfileStore, loadClaudeProfiles } from '../stores/claude-profile-store';
import { useToast } from '../hooks/use-toast';
import type { SDKRateLimitInfo } from '../../shared/types';

const CLAUDE_UPGRADE_URL = 'https://claude.ai/upgrade';
Expand Down Expand Up @@ -55,6 +56,7 @@ function getSourceIcon(source: SDKRateLimitInfo['source']) {
export function SDKRateLimitModal() {
const { isSDKModalOpen, sdkRateLimitInfo, hideSDKRateLimitModal, clearPendingRateLimit } = useRateLimitStore();
const { profiles, isSwitching, setSwitching } = useClaudeProfileStore();
const { toast } = useToast();
const [selectedProfileId, setSelectedProfileId] = useState<string | null>(null);
const [autoSwitchEnabled, setAutoSwitchEnabled] = useState(false);
const [isLoadingSettings, setIsLoadingSettings] = useState(false);
Expand Down Expand Up @@ -160,22 +162,26 @@ export function SDKRateLimitModal() {
// Close the modal so user can see the terminal
hideSDKRateLimitModal();

// Alert the user about the terminal
alert(
`A terminal has been opened to authenticate "${profileName}".\n\n` +
`Steps to complete:\n` +
`1. Check the "Agent Terminals" section in the sidebar\n` +
`2. Complete the OAuth login in your browser\n` +
`3. The token will be saved automatically\n\n` +
`Once done, return here and the account will be available.`
);
// Notify the user about the terminal (non-blocking)
toast({
title: `Authenticating "${profileName}"`,
description: 'Check the Agent Terminals section in the sidebar to complete OAuth login.',
});
} else {
alert(`Failed to start authentication: ${initResult.error || 'Please try again.'}`);
toast({
variant: 'destructive',
title: 'Failed to start authentication',
description: initResult.error || 'Please try again.',
});
}
}
} catch (err) {
console.error('Failed to add profile:', err);
alert('Failed to add profile. Please try again.');
toast({
variant: 'destructive',
title: 'Failed to add profile',
description: 'Please try again.',
});
} finally {
setIsAddingProfile(false);
}
Expand Down
51 changes: 42 additions & 9 deletions apps/frontend/src/renderer/components/onboarding/OAuthStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Card, CardContent } from '../ui/card';
import { cn } from '../../lib/utils';
import { loadClaudeProfiles as loadGlobalClaudeProfiles } from '../../stores/claude-profile-store';
import { useClaudeLoginTerminal } from '../../hooks/useClaudeLoginTerminal';
import { useToast } from '../../hooks/use-toast';
import type { ClaudeProfile } from '../../../shared/types';

interface OAuthStepProps {
Expand All @@ -42,6 +43,7 @@ interface OAuthStepProps {
*/
export function OAuthStep({ onNext, onBack, onSkip }: OAuthStepProps) {
const { t } = useTranslation('onboarding');
const { toast } = useToast();

// Claude Profiles state
const [claudeProfiles, setClaudeProfiles] = useState<ClaudeProfile[]>([]);
Expand Down Expand Up @@ -102,13 +104,16 @@ export function OAuthStep({ onNext, onBack, onSkip }: OAuthStepProps) {
if (info.success && info.profileId) {
// Reload profiles to show updated state
await loadClaudeProfiles();
// Show simple success notification
alert(`✅ Profile authenticated successfully!\n\n${info.email ? `Account: ${info.email}` : 'Authentication complete.'}\n\nYou can now use this profile.`);
// Show simple success notification (non-blocking)
toast({
title: 'Profile authenticated successfully',
description: info.email ? `Account: ${info.email}` : 'Authentication complete. You can now use this profile.',
});
}
});

return unsubscribe;
}, []);
}, [toast]);

// Profile management handlers - following patterns from IntegrationSettings.tsx
const handleAddProfile = async () => {
Expand Down Expand Up @@ -152,12 +157,20 @@ export function OAuthStep({ onNext, onBack, onSkip }: OAuthStepProps) {
// Users can see the 'claude setup-token' output directly
} else {
await loadClaudeProfiles();
alert(`Failed to start authentication: ${initResult.error || 'Please try again.'}`);
toast({
variant: 'destructive',
title: 'Failed to start authentication',
description: initResult.error || 'Please try again.',
});
}
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to add profile');
alert('Failed to add profile. Please try again.');
toast({
variant: 'destructive',
title: 'Failed to add profile',
description: 'Please try again.',
});
} finally {
setIsAddingProfile(false);
}
Expand Down Expand Up @@ -224,13 +237,21 @@ export function OAuthStep({ onNext, onBack, onSkip }: OAuthStepProps) {
try {
const initResult = await window.electronAPI.initializeClaudeProfile(profileId);
if (!initResult.success) {
alert(`Failed to start authentication: ${initResult.error || 'Please try again.'}`);
toast({
variant: 'destructive',
title: 'Failed to start authentication',
description: initResult.error || 'Please try again.',
});
}
// Note: If successful, the terminal is now visible in the UI via the onTerminalAuthCreated event
// Users can see the 'claude setup-token' output and complete OAuth flow directly
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to authenticate profile');
alert('Failed to start authentication. Please try again.');
toast({
variant: 'destructive',
title: 'Failed to start authentication',
description: 'Please try again.',
});
} finally {
setAuthenticatingProfileId(null);
}
Expand Down Expand Up @@ -267,12 +288,24 @@ export function OAuthStep({ onNext, onBack, onSkip }: OAuthStepProps) {
setManualToken('');
setManualTokenEmail('');
setShowManualToken(false);
toast({
title: 'Token saved',
description: 'Your token has been saved successfully.',
});
} else {
alert(`Failed to save token: ${result.error || 'Please try again.'}`);
toast({
variant: 'destructive',
title: 'Failed to save token',
description: result.error || 'Please try again.',
});
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save token');
alert('Failed to save token. Please try again.');
toast({
variant: 'destructive',
title: 'Failed to save token',
description: 'Please try again.',
});
} finally {
setSavingTokenProfileId(null);
}
Expand Down
Loading
Loading