Skip to content

Commit f962d22

Browse files
authored
Fixes for terminal commands (#271)
* v0.2.20 * Get rid of shell wrapping
1 parent 64255b8 commit f962d22

File tree

1 file changed

+103
-19
lines changed

1 file changed

+103
-19
lines changed

src/terminal-manager.ts

Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { spawn } from 'child_process';
2+
import path from 'path';
23
import { TerminalSession, CommandExecutionResult, ActiveSession, TimingInfo, OutputEvent } from './types.js';
34
import { DEFAULT_COMMAND_TIMEOUT } from './config.js';
45
import { configManager } from './config-manager.js';
@@ -13,6 +14,76 @@ interface CompletedSession {
1314
endTime: Date;
1415
}
1516

17+
/**
18+
* Configuration for spawning a shell with appropriate flags
19+
*/
20+
interface ShellSpawnConfig {
21+
executable: string;
22+
args: string[];
23+
useShellOption: string | boolean;
24+
}
25+
26+
/**
27+
* Get the appropriate spawn configuration for a given shell
28+
* This handles login shell flags for different shell types
29+
*/
30+
function getShellSpawnArgs(shellPath: string, command: string): ShellSpawnConfig {
31+
const shellName = path.basename(shellPath).toLowerCase();
32+
33+
// Unix shells with login flag support
34+
if (shellName.includes('bash') || shellName.includes('zsh')) {
35+
return {
36+
executable: shellPath,
37+
args: ['-l', '-c', command],
38+
useShellOption: false
39+
};
40+
}
41+
42+
// PowerShell Core (cross-platform, supports -Login)
43+
if (shellName === 'pwsh' || shellName === 'pwsh.exe') {
44+
return {
45+
executable: shellPath,
46+
args: ['-Login', '-Command', command],
47+
useShellOption: false
48+
};
49+
}
50+
51+
// Windows PowerShell 5.1 (no login flag support)
52+
if (shellName === 'powershell' || shellName === 'powershell.exe') {
53+
return {
54+
executable: shellPath,
55+
args: ['-Command', command],
56+
useShellOption: false
57+
};
58+
}
59+
60+
// CMD
61+
if (shellName === 'cmd' || shellName === 'cmd.exe') {
62+
return {
63+
executable: shellPath,
64+
args: ['/c', command],
65+
useShellOption: false
66+
};
67+
}
68+
69+
// Fish shell (uses -l for login, -c for command)
70+
if (shellName.includes('fish')) {
71+
return {
72+
executable: shellPath,
73+
args: ['-l', '-c', command],
74+
useShellOption: false
75+
};
76+
}
77+
78+
// Unknown/other shells - use shell option for safety
79+
// This provides a fallback for shells we don't explicitly handle
80+
return {
81+
executable: command,
82+
args: [],
83+
useShellOption: shellPath
84+
};
85+
}
86+
1687
export class TerminalManager {
1788
private sessions: Map<number, TerminalSession> = new Map();
1889
private completedSessions: Map<number, CompletedSession> = new Map();
@@ -66,29 +137,42 @@ export class TerminalManager {
66137
console.log(`Enhanced SSH command: ${enhancedCommand}`);
67138
}
68139

69-
// Wrap command to run in login shell if using bash/zsh to get full PATH
70-
let finalCommand = enhancedCommand;
71-
let finalShell: string | boolean = shellToUse;
140+
// Get the appropriate spawn configuration for the shell
141+
let spawnConfig: ShellSpawnConfig;
142+
let spawnOptions: any;
72143

73-
if (typeof shellToUse === 'string' && shellToUse !== 'powershell.exe') {
74-
// For bash/zsh, wrap the command to run as login shell
75-
// This ensures PATH is loaded from profile files (like .zprofile, .bash_profile)
76-
if (shellToUse.includes('bash') || shellToUse.includes('zsh')) {
77-
finalCommand = `${shellToUse} -l -c ${JSON.stringify(enhancedCommand)}`;
78-
finalShell = true; // Use system shell to execute the wrapped command
144+
if (typeof shellToUse === 'string') {
145+
// Use shell-specific configuration with login flags where appropriate
146+
spawnConfig = getShellSpawnArgs(shellToUse, enhancedCommand);
147+
spawnOptions = {
148+
env: {
149+
...process.env,
150+
TERM: 'xterm-256color' // Better terminal compatibility
151+
}
152+
};
153+
154+
// Add shell option if needed (for unknown shells)
155+
if (spawnConfig.useShellOption) {
156+
spawnOptions.shell = spawnConfig.useShellOption;
79157
}
158+
} else {
159+
// Boolean or undefined shell - use default shell option behavior
160+
spawnConfig = {
161+
executable: enhancedCommand,
162+
args: [],
163+
useShellOption: shellToUse
164+
};
165+
spawnOptions = {
166+
shell: shellToUse,
167+
env: {
168+
...process.env,
169+
TERM: 'xterm-256color'
170+
}
171+
};
80172
}
81173

82-
const spawnOptions: any = {
83-
shell: finalShell,
84-
env: {
85-
...process.env,
86-
TERM: 'xterm-256color' // Better terminal compatibility
87-
}
88-
};
89-
90-
// Spawn the process with an empty array of arguments and our options
91-
const childProcess = spawn(finalCommand, [], spawnOptions);
174+
// Spawn the process with appropriate arguments
175+
const childProcess = spawn(spawnConfig.executable, spawnConfig.args, spawnOptions);
92176
let output = '';
93177

94178
// Ensure childProcess.pid is defined before proceeding

0 commit comments

Comments
 (0)