11using System . Diagnostics ;
22using System . Management . Automation ;
3- using System . Text ;
43
54namespace AIShell . Integration . Commands ;
65
@@ -12,6 +11,9 @@ public class StartAIShellCommand : PSCmdlet
1211 [ ValidateNotNullOrEmpty ]
1312 public string Path { get ; set ; }
1413
14+ private string _venvPipPath ;
15+ private string _venvPythonPath ;
16+
1517 protected override void BeginProcessing ( )
1618 {
1719 if ( Path is null )
@@ -80,25 +82,21 @@ protected override void BeginProcessing()
8082 targetObject : null ) ) ;
8183 }
8284
83- var python = SessionState . InvokeCommand . GetCommand ( "python3" , CommandTypes . Application ) ;
84- if ( python is null )
85+ try
8586 {
86- ThrowTerminatingError ( new (
87- new NotSupportedException ( "The executable 'python3' (Windows Terminal) cannot be found. It's required to split a pane in iTerm2 programmatically." ) ,
88- "Python3Missing" ,
89- ErrorCategory . NotInstalled ,
90- targetObject : null ) ) ;
87+ InitAndCleanup . CreateVirtualEnvTask . GetAwaiter ( ) . GetResult ( ) ;
9188 }
92-
93- var pip3 = SessionState . InvokeCommand . GetCommand ( "pip3" , CommandTypes . Application ) ;
94- if ( pip3 is null )
89+ catch ( Exception exception )
9590 {
9691 ThrowTerminatingError ( new (
97- new NotSupportedException ( "The executable 'pip3' cannot be found. It's required to split a pane in iTerm2 programmatically." ) ,
98- "Pip3Missing " ,
99- ErrorCategory . NotInstalled ,
92+ exception ,
93+ "FailedToCreateVirtualEnvironment " ,
94+ ErrorCategory . InvalidOperation ,
10095 targetObject : null ) ) ;
10196 }
97+
98+ _venvPipPath = System . IO . Path . Join ( InitAndCleanup . VirtualEnvPath , "bin" , "pip3" ) ;
99+ _venvPythonPath = System . IO . Path . Join ( InitAndCleanup . VirtualEnvPath , "bin" , "python3" ) ;
102100 }
103101 else
104102 {
@@ -112,12 +110,11 @@ protected override void BeginProcessing()
112110
113111 protected override void EndProcessing ( )
114112 {
115- string pipeName = Channel . Singleton . StartChannelSetup ( ) ;
116-
117113 if ( OperatingSystem . IsWindows ( ) )
118114 {
119115 ProcessStartInfo startInfo ;
120116 string wtProfileGuid = Environment . GetEnvironmentVariable ( "WT_PROFILE_ID" ) ;
117+ string pipeName = Channel . Singleton . StartChannelSetup ( ) ;
121118
122119 if ( wtProfileGuid is null )
123120 {
@@ -169,94 +166,40 @@ protected override void EndProcessing()
169166 }
170167 else if ( OperatingSystem . IsMacOS ( ) )
171168 {
172- // Install the Python package 'iterm2'.
173- ProcessStartInfo startInfo = new ( "pip3" )
169+ // Install the Python package 'iterm2' to the venv .
170+ ProcessStartInfo startInfo = new ( _venvPipPath )
174171 {
175- ArgumentList = { "install" , "-q" , "iterm2" } ,
172+ ArgumentList = { "install" , "-q" , "iterm2" , "--disable-pip-version-check" } ,
176173 RedirectStandardError = true ,
177174 RedirectStandardOutput = true
178175 } ;
179176
180- Process proc = new ( ) { StartInfo = startInfo } ;
181- proc . Start ( ) ;
177+ Process proc = Process . Start ( startInfo ) ;
182178 proc . WaitForExit ( ) ;
183179
184180 if ( proc . ExitCode is 1 )
185181 {
182+ string error = "The Python package 'iterm2' cannot be installed. It's required to split a pane in iTerm2 programmatically." ;
183+ string stderr = proc . StandardError . ReadToEnd ( ) ;
184+ if ( ! string . IsNullOrEmpty ( stderr ) )
185+ {
186+ error = $ "{ error } \n Error details:\n { stderr } ";
187+ }
188+
186189 ThrowTerminatingError ( new (
187- new NotSupportedException ( "The Python package 'iterm2' cannot be installed. It's required to split a pane in iTerm2 programmatically." ) ,
190+ new NotSupportedException ( error ) ,
188191 "iterm2Missing" ,
189192 ErrorCategory . NotInstalled ,
190193 targetObject : null ) ) ;
191194 }
192195
193196 proc . Dispose ( ) ;
194197
195- // Write the Python script to a temp file, if not yet.
196- string pythonScript = System . IO . Path . Combine ( System . IO . Path . GetTempPath ( ) , "__aish_split_pane.py" ) ;
197- if ( ! File . Exists ( pythonScript ) )
198- {
199- File . WriteAllText ( pythonScript , SplitPanePythonCode , Encoding . UTF8 ) ;
200- }
201-
202198 // Run the Python script to split the pane and start AIShell.
203- startInfo = new ( "python3" ) { ArgumentList = { pythonScript , Path , pipeName } } ;
204- proc = new ( ) { StartInfo = startInfo } ;
205- proc . Start ( ) ;
199+ string pipeName = Channel . Singleton . StartChannelSetup ( ) ;
200+ startInfo = new ( _venvPythonPath ) { ArgumentList = { InitAndCleanup . PythonScript , Path , pipeName } } ;
201+ proc = Process . Start ( startInfo ) ;
206202 proc . WaitForExit ( ) ;
207203 }
208204 }
209-
210- private const string SplitPanePythonCode = """
211- import iterm2
212- import sys
213-
214- # iTerm needs to be running for this to work
215- async def main(connection):
216- app = await iterm2.async_get_app(connection)
217-
218- # Foreground the app
219- await app.async_activate()
220-
221- window = app.current_terminal_window
222- if window is not None:
223- # Get the current pane so that we can split it.
224- current_tab = window.current_tab
225- current_pane = current_tab.current_session
226-
227- # Get the total width before splitting.
228- width = current_pane.grid_size.width
229-
230- # Split pane vertically
231- split_pane = await current_pane.async_split_pane(vertical=True)
232-
233- # Get the height of the pane after splitting. This value will be
234- # slightly smaller than its height before splitting.
235- height = current_pane.grid_size.height
236-
237- # Calculate the new width for both panes using the ratio 0.4 for the new pane.
238- # Then set the preferred size for both pane sessions.
239- new_current_width = round(width * 0.6);
240- new_split_width = width - new_current_width;
241- current_pane.preferred_size = iterm2.Size(new_current_width, height)
242- split_pane.preferred_size = iterm2.Size(new_split_width, height);
243-
244- # Update the layout, which will change the panes to preferred size.
245- await current_tab.async_update_layout()
246-
247- await split_pane.async_send_text(f'{app_path} --channel {channel}\n')
248- else:
249- # You can view this message in the script console.
250- print("No current iTerm2 window. Make sure you are running in iTerm2.")
251-
252- if len(sys.argv) > 1:
253- app_path = sys.argv[1]
254- channel = sys.argv[2]
255-
256- # Do not specify True for retry. It's possible that the user hasn't enable the Python API for iTerm2,
257- # and in that case, we want it to fail immediately instead of stucking in retries.
258- iterm2.run_until_complete(main)
259- else:
260- print("Please provide the application path as a command line argument.")
261- """ ;
262205}
0 commit comments