mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
feat: enhance PTY handling for Windows in ClaudeUsageService and TerminalService
- Added detection for Electron environment to improve compatibility with Windows PTY processes. - Implemented winpty fallback for ConPTY failures, ensuring robust terminal session creation in Electron and other contexts. - Updated error handling to provide clearer messages for authentication and terminal access issues. - Refined usage data detection logic to avoid false positives, improving the accuracy of usage reporting. These changes aim to enhance the reliability and user experience of terminal interactions on Windows, particularly in Electron applications.
This commit is contained in:
@@ -71,6 +71,12 @@ export class TerminalService extends EventEmitter {
|
||||
private dataCallbacks: Set<DataCallback> = new Set();
|
||||
private exitCallbacks: Set<ExitCallback> = new Set();
|
||||
private isWindows = os.platform() === 'win32';
|
||||
// On Windows, ConPTY requires AttachConsole which fails in Electron/service mode
|
||||
// Detect Electron by checking for electron-specific env vars or process properties
|
||||
private isElectron =
|
||||
!!(process.versions && (process.versions as Record<string, string>).electron) ||
|
||||
!!process.env.ELECTRON_RUN_AS_NODE;
|
||||
private useConptyFallback = false; // Track if we need to use winpty fallback on Windows
|
||||
|
||||
/**
|
||||
* Kill a PTY process with platform-specific handling.
|
||||
@@ -339,13 +345,60 @@ export class TerminalService extends EventEmitter {
|
||||
|
||||
logger.info(`Creating session ${id} with shell: ${shell} in ${cwd}`);
|
||||
|
||||
const ptyProcess = pty.spawn(shell, shellArgs, {
|
||||
// Build PTY spawn options
|
||||
const ptyOptions: pty.IPtyForkOptions = {
|
||||
name: 'xterm-256color',
|
||||
cols: options.cols || 80,
|
||||
rows: options.rows || 24,
|
||||
cwd,
|
||||
env,
|
||||
});
|
||||
};
|
||||
|
||||
// On Windows, always use winpty instead of ConPTY
|
||||
// ConPTY requires AttachConsole which fails in many contexts:
|
||||
// - Electron apps without a console
|
||||
// - VS Code integrated terminal
|
||||
// - Spawned from other applications
|
||||
// The error happens in a subprocess so we can't catch it - must proactively disable
|
||||
if (this.isWindows) {
|
||||
(ptyOptions as pty.IWindowsPtyForkOptions).useConpty = false;
|
||||
logger.info(
|
||||
`[createSession] Using winpty for session ${id} (ConPTY disabled for compatibility)`
|
||||
);
|
||||
}
|
||||
|
||||
let ptyProcess: pty.IPty;
|
||||
try {
|
||||
ptyProcess = pty.spawn(shell, shellArgs, ptyOptions);
|
||||
} catch (spawnError) {
|
||||
const errorMessage = spawnError instanceof Error ? spawnError.message : String(spawnError);
|
||||
|
||||
// Check for Windows ConPTY-specific errors
|
||||
if (this.isWindows && errorMessage.includes('AttachConsole failed')) {
|
||||
// ConPTY failed - try winpty fallback
|
||||
if (!this.useConptyFallback) {
|
||||
logger.warn(`[createSession] ConPTY AttachConsole failed, retrying with winpty fallback`);
|
||||
this.useConptyFallback = true;
|
||||
|
||||
try {
|
||||
(ptyOptions as pty.IWindowsPtyForkOptions).useConpty = false;
|
||||
ptyProcess = pty.spawn(shell, shellArgs, ptyOptions);
|
||||
logger.info(`[createSession] Successfully spawned session ${id} with winpty fallback`);
|
||||
} catch (fallbackError) {
|
||||
const fallbackMessage =
|
||||
fallbackError instanceof Error ? fallbackError.message : String(fallbackError);
|
||||
logger.error(`[createSession] Winpty fallback also failed:`, fallbackMessage);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
logger.error(`[createSession] PTY spawn failed (winpty):`, errorMessage);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
logger.error(`[createSession] PTY spawn failed:`, errorMessage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const session: TerminalSession = {
|
||||
id,
|
||||
@@ -409,7 +462,11 @@ export class TerminalService extends EventEmitter {
|
||||
|
||||
// Handle exit
|
||||
ptyProcess.onExit(({ exitCode }) => {
|
||||
logger.info(`Session ${id} exited with code ${exitCode}`);
|
||||
const exitMessage =
|
||||
exitCode === undefined || exitCode === null
|
||||
? 'Session terminated'
|
||||
: `Session exited with code ${exitCode}`;
|
||||
logger.info(`${exitMessage} (${id})`);
|
||||
this.sessions.delete(id);
|
||||
this.exitCallbacks.forEach((cb) => cb(id, exitCode));
|
||||
this.emit('exit', id, exitCode);
|
||||
|
||||
Reference in New Issue
Block a user