Change:
Shell integration in the terminal could enable APIs like being able to listen to commands that run and get their output/exit code. Rough example:
I came here to request this, and here it is already. Being in Backlog means it'll get on the work plan eventually? Cheers, looking forward to this, it's the missing link for my 'debug last command line' extension!
backlog means it's a valid item and it may be worked on in the future. I'm pretty sure this one will happen within the next year
Proposal: and how does this look to you, as you'll be adopting this for the extension?
I've forked this off to as I realized this issue is more about general listening to commands
Voting for this issue. The proposed interface would exactly solve a problem I have developing an extension.
+1 for this, can we get it on a roadmap plz?
Hi and - I've noticed this issue has been on an "iteration plan" in Nov and Oct of 2022. I'm wondering if you think it will be picked up any time soon? Thanks, really appreciate all the hard work!
I'll actually likely be looking into a proposed API for this in July. Maybe not an executeCommand API but at least the ability to extract command+output after the fact
Here's an early proposal: It doesn't look like this will make it in the July release though.
Hi this looks great! It would definitely solve my needs (I only need exit code, not the command output). Would love to use this in August
Wow I hate to be person to ask this, but what are some scenarios for this, and will all commands executed result in the event being fired? I'm trying to reason about how this would fit with the PowerShell Extension (where we have our own Extension Terminal, that does support shell integration by virtue of copying the same setup script into shell for the ASCII control codes). First thing that comes to mind is that maybe this would replace our use of (which is what we call for , i.e. select some PowerShell code and run it in the active terminal). Would the command still show up in the active terminal as if it were pasted, or would it run "in the background?"
provided shell integration is enabled, it would be a more reliable way of running a command as we have logic that will trigger ^C if VS Code is unsure if there's anything in the prompt (always on Windows but improvements are planned). The biggest thing this gives that people have been after is exit code and output reporting, both of which are impossible currently.
Needs an early discussion in the API sync before we start impl.
Feedback: Should it be ? Should there be 2 events? When shell integration isn't available Throwing synchronously? Rejecting the promise? Make executeCommand undefined until it is safe? This would force to consider at compile-time
Feedback: Should we have a type that is only active when shell integration is active?
Latest:
Related on Stack Overflow: (should be closed as a duplicate) (should probably be closed as a duplicate) Also
Does this (should we) also account for cases where we can stream through as output gets printed?
seems reasonable to me to expose this either as a stream or as an async generator. I can see some use cases such as real time output parsing for test adapters or log parsers.
Gathering interest for Shell Script auto-indent:
please do not link to random issues to try get more attention on it. It's just spam that causes notifications and wastes time.
the API is essentially the streaming API you're talking about and it has some major performance issues as all data needs to be passed to the extension host (due to API implementation guidelines). We can't really do streaming without sending over escape sequences too which I was explicitly trying to avoid with this one in order to make it more generally useful. I think for tests specifically we have special handling of that via test extensions that integrate with the testing view.
If you have better (less intrusive) ways to get interest on a given ticket, could you please share how you would proceed?
social media or friends? Just you shouldn't post off topic stuff to our bug tracker as it's distracting.
I do not use any social media, nor do I have friends interested in such topics X) So I assumed people interested in similar VSCode issues would have the most chances to also be interested in the one I posted... Yet it does not sem to be the case here, so I am sorry for the inconvenience.
Hi there, Wanted to +1 this as it unlocks our ability to work on colorized build output which is the number 2 actively and top-voted issue for the CMake Tools Extension by giving the extension more knowledge of the terminal: Is there any ability for the team to work this to unblock our users high requests for greater visibility of their build?
can you elaborate on how this API would help you? Skimming the issue it seems like you would be after either allowing colors to be controlled in an output channel more finely (just textmate grammars atm?) or the existing extension API? This particular issue tracks the ability to be able to listen to when commands execute and get their output, it's nothing to do with adding colors to output?
Secondary to getting ANSI color support in the terminal, our back up request would be that we want to atleast have more of the terminal exposed so that we can potentially build this support ourselves for the extension as a workaround.
I'm still a little confused, the terminal supports ANSI escape sequences for color and always has, the output view does not as it's an editor behind the scenes and colors via textmate grammars. This issue is specifically about an API for the terminal. If you do indeed run a process in a terminal, ANSI escape sequences will be passed through and work, you can write manually via a and you can intercept input/output via this same API if you need to for some reason.
Can confirm that we do this with extension for our runs, I think is conflating shell integration knowledge with ANSI which are just output characters.
To fill in some context for questions. We have two separate asks that would separately help us solve some problems for ensuring that users who run build can get colorized output, either from the output channel or from the terminal. ANSI and colorization in the output channel. This, based on your response, seems like it is not supporting any further than textmate grammers. Thanks for this context. We could likely either suggest users to install extensions that already do this, or provide our own. the ask from this issue, the ability to execute a command and get its result. This will help us be able to get result information, both for whether the build succeeded or failed, as well as simply knowing when the build completed. More context on this can also be found here: We currently can't directly integrate with sending our build to the terminal, we only support it through users creating a entry, because we'd need this API in order to get result information from the command. Does this provide more understanding and context? Thanks!
I'm not too familiar with the tasks extension API but could you contribute a shell task to get the colors working there? If you want to know when users run a command in any terminal then this request makes sense, bringing colors into the discussion is the main thing that was confusing me
Yes, I believe that has been our strategy in the past, we have a setting that users can enable that will use build tasks so that they can get colorization. However, it looks like at some point this broke for us (we can investigate this), and so we're hoping that this API can be enabled so that we can, instead of relying on keeping a entry up-to-date and correct, we can directly invoke a build (or any command) with the extension and automatically use the terminal so that users can get colorization with no effort of their own.
This is a little surprising as all a task does is run a command in the terminal, if the type is shell it will run it within a shell.
Ah, it seems like it's because we actually attempt to provide a task type, and when users use this, colorization seems not to be there. However, if they switch to a task type, colorization is there. This could be possibly a bug within our own task type, that we should investigate, or, could it be a bug with VS Code where technically we should still get colorization with this?
I'm guessing for a task type you would need to wrap the call in a shell yourself something like ? I'm not sure if you're able to do this via the API so we handle the shell stuff (eg. using the default shell, cmd on Windows, etc.). might have more info?
Is this task to run CMake configuration? or to run build? At least for build, check and see if that makes any difference. Do you have a link to an issue ticket about this I can look at?
what broke for you?
The task is to run CMake build: It may not be that it broke, but that the way we are doing tasks right now removes the colors, I think this may be because we are doing our own task type. Here is the relevant issue comment: Thanks.
I have an extension using the onDidWriteTerminalData proposed API. However it is retrieving the output from the terminal of my extension window. I want to retrieve the output of application's terminal (the extension host) that is running my extension. Is this possible? The purpose of this is so that my extension can help debug the extension host's code by viewing the statements.
accesses the data send to vscode terminals, are not put in a vscode terminal but the devtools console. You'd need to use a different approach.
I'm planning on iterating more on this in March. Also considering the idea of stabilizing again, instead of worrying about extensions using and disposing of the listeners for performance reasons, perhaps I could flip it around and prevent misuse by rate limiting the event for very busy terminals.
Could this event be per-terminal rather than per-window?
that's the easy fix, but unfortunately (fortunately?) a hardline approach is taken to extension API guidelines and since objects are public objects, the event must be global
That is a little cursed but hard to argue with. A solution could be to batch the events so that no more than x events per terminal per second are emitted.
Current state of these proposed APIs:
This would be perfect for my use case, running a command and getting the result!
you can test that first one by , you just can't publish it. The second one likely won't become stable, however I think we need to add some way of streaming progress from the first.
This would not work for my use case at all, where I would like to detect a string (an URL with a port number) written by a launched command and connect to that port while the command is active. This is something I need for a custom (non-DAP) debugger for a non-software use case.
an event/stream to get the data of a command while it's happening would work though right?
Yep, would work perfectly. Either line-buffered or just chunks of data as they arrive. I would also be OK with specifying a search pattern (a regex perhaps) so that I get an event every time it triggers, which may be a more performant option in some conditions.
Is there a way with this proposed API to know what command you are referring to? I would imagine that we'd need to be able to invoke the command, and then get the result. Or, is it expected to invoke the command, then listen for event and do your own validation for what task it was?
Here's another shot at the API which I think satisfies all the requirements: Listen to command execution finished without escape sequences Listen to all data from start to end, with and without escape sequences Allow enhanced and more reliable command execution over This round was made close to the tasks API as they share a lot of similarities. I'll bring it to the API sync on Tuesday, any feedback welcome of course!
I think the above gives you what you're after.
If it gets truncated, will it be impossible to reliably detect a substring in the output?
I think I'll have to limit it somehow, aggressive batching as needed and probably will truncate at some point where it's unreasonable (10s, 100s of mbs?). So yes it would be impossible to detect a substring in the truncated output in that case (unless you used something like the also proposed quick fix provider).
Repeatedly searching a substring in a 100s of MBs string is going to introduce a lot of CPU load. Probably enough to compete with the UI thread on lower-end hardware, in my experience doing similar things in the browser. Maintaining it might also be expensive.
oh I mean it's possible an extension will not get some of the data if the command is doing too much. Thinking more about this though, the event itself would probably give the addition to rawData, not rawData itself. We may also want to consider rawData being to be nicer on memory and not keep creating giant strings.
made some tweaks to better support your problem
Thank you. It would be extremely useful to know when was truncated. I think this can be achieved by a single integer field indicating how many characters were emitted before the first string in the array. This would start at 0 and increased every time it's truncated, making it possible for an extension to unambiguously determine whether it's misisng any output or not. Are events guaranteed to fire on each character written to the terminal or not? If the answer is no, then the event probably should use a similar mechanism as the previous paragraph describes, for the same reason. I believe that will suffer the same performance issues as , except that making a guarantee that it's updated on each line makes it a lot more severe as now you cannot batch more than a single line.
good point, : Added:
Here's the cleaned up proposal with some fixes:
Feedback from standup was that people don't want to do escaping themselves, so maybe we have another signature like this:
How would the extension know what shell is currently active in the terminal if they wanted to manual quoting? Also, why is a single string and not an array of strings? How is automatic quoting going to work if is a single string of (presumably) multiple arguments?
Yes agreed, I too missed that one.
Anyone still tracks this issue? Something unexpected happens when I use proposed API . I followed to use terminalExecuteCommandEvent but onDidExecuteTerminalCommand is not triggered. However, it is fine with terminalDataWriteEvent. That is wierd. Any help is appreciated, guys. ! !
I know why it happened. It turned out that shell integration had to be manually installed , and changed powershell's execution policy to Unrestricted( with powershell run as administrator, but I don't think it's a good way). ! ! If powershell's execution policy is restricted, script below to $Profile causes an execution policy error, and onDidExecuteTerminalCommand will not be triggered. ! So I will be seeking a way to use onDidExecuteTerminalCommand without changing execution policy.
Update:
Lots of feedback from API sync: - Let's move and all shell integration-related APIs into a or in order to separate the behavior that may not exist from the behavior that will always exist. () Instead of this we could have . It's possible this could activate twice in the future, but it should never deactivate. So onDidActivate with the object is preferable to - Rename to ? - The event still breaks the public event API rule. Turns out is being used for the first time in the proposed . This is exactly what we want, resolves the event problems and we could get rid of the events in favor of this: Given the above , we could omit completely which avoids the possible memory problems. If we did this then it could be something like in order to indicate that it's streaming from that point onwards, ie. you must start listening to the stream immediately after the command executes, and we would start streaming in the (js) task after the command has been executed. Whatever we do here is needs to be "fair", to allow an extension to listen to the complete stream if it wants to. For truncated data, we could move to an object inside the iterable. A concern here was always about creating objects on streamed data, but since we are controlling batching this would be as little as 1 object per data batch. - For , this could fundamentally change if we revisited exposing the buffer on . It would likely turn into a buffer range then and not include the data. Turns out this would also be very useful for Copilot for referencing commands in the terminal. The extension host has a hard cap on the size of text files that it synchronizes to ~30-50mb. We could have a similar limit to terminal buffer tracking, such as only allowing x lines of scrollback to be tracked, up to a cap. The pty host is on the remote machine and already has a mirrored version of If we could set up communication between the pty host and the extension host then this would be a great way to synchronize the buffer as it would avoid going over a remote connection twice. On remote the pty host needs to go via the server process anyway:
Update after feedback:
Rough draft of what a terminal buffer API could look like:
what happened to the discussion about automatic shell quoting? Does the string signature of mean no automatic shell quoting, and the object signature mean yes automatic shell quoting?
yep, I don't think we need shell quoting. You can pass the command line directly, or let VS Code generate the command line (with quoting) based on the executable type (eg. pwsh arg quoting is a little different to other shells).
Update (from ): ts // features: { // cwdReporting: boolean; // explicitCommandLineReporting: boolean; // }; //
Feedback from API sync: Overall good direction Add 2 overloads for and don't call the args options as they are not optional. This also allows overload-specific examples Switch to to future proof us (don't include as we're unsure if it's useful) is no longer part of this, instead moved into Will start implementing it!
I was thinking that this may also be useful now if is toggled. I recall there has been a few cases when we're interested in cwd of a user, so it could potentially be a useful feature to include. I can look for such instances in the Python extension to back this.
doesn't necessarily mean shell integration will work or not, just whether VS Code tried to inject the script. Good point, I'll think about adding a cwd property to
Update: 2 signatures Removed the and possibility of truncated data, so it's now . It looks like we'll likely implement the buffer API which will mean that truncation no longer needs to happen. will be the way to guarantee getting all data in the terminal since output may be trimmed. -- Added , when this value changes will fire
This sounds terminal specific: Curious, why doesn't these live under the scope? As a consumer, I only care about a specific terminal, and once that is done, I can dispose the event rather than listening for changes to all terminals. As per API guidelines, I would expect a producer object to be: !
because of ; global objects must have global events. Done!
TODOs in
Latest: ts /
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See in the project root for license information. *
-------------/ declare module 'vscode' { // // TODO: Add missing docs // TODO: Review and polish up all docs export interface TerminalShellExecution { / The full command line that was executed, including both the command and arguments. The { TerminalShellExecutionCommandLineConfidence confidence} of this value depends on the specific shell's shell integration implementation. This value may become more accurate after { onDidEndTerminalShellExecution} is fired. / readonly commandLine: TerminalShellExecutionCommandLine; / The working directory that was reported by the shell when this command executed. This will be a { Uri} if the path reported by the shell can reliably be mapped to the connected machine. This requires the shell integration to support working directory reporting. / readonly cwd: Uri | undefined; / Creates a stream of raw data (including escape sequences) that is written to the terminal. This will only include data that was written after was called for the first time, ie. you must call immediately after the command is executed via { executeCommand} or { onDidStartTerminalShellExecution} to not miss any data. // Log all data written to the terminal for a command const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); const stream = command.readData(); for await (const data of stream) { (data); } / // TODO: read? "data" typically means Uint8Array. What's the encoding of the string? Usage here will typically be checking for substrings // TODO: dispose function? readData(): AsyncIterable<string; // read(): AsyncIterable<string; // createReader(): { stream: AsyncIterable<string; dispose: () =void } // createReadStream(): { stream: AsyncIterable<string; dispose: () =void }; // // interface DisposableAsyncIterable<Textends Disposable { // read(): AsyncIterable<T; // } } / A command line that was executed in a terminal. / export interface TerminalShellExecutionCommandLine { / The full command line that was executed, including both the command and its arguments. / value: string; / Whether the command line value came from a trusted source and is therefore safe to execute without user additional confirmation, such as a notification that asks "Do you want to execute (command)?". This is false when the command line was reported explicitly by the shell integration script (ie. { TerminalShellExecutionCommandLineConfidence.High high confidence}), but did not include a nonce for verification. / isTrusted: boolean; / The confidence of the command line value which is determined by how the value was obtained. This depends upon the implementation of the shell integration script. / confidence: TerminalShellExecutionCommandLineConfidence; } / The confidence of a { TerminalShellExecutionCommandLine} value. / enum TerminalShellExecutionCommandLineConfidence { / The command line value confidence is low. This means that the value was read from the terminal buffer using markers reported by the shell integration script. Additionally one of the following conditions will be met: The command started on the very left-most column which is unusual, or The command is multi-line which is more difficult to accurately detect due to line continuation characters and right prompts. Command line markers were not reported by the shell integration script. / Low = 0, / The command line value confidence is medium. This means that the value was read from the terminal buffer using markers reported by the shell integration script. The command is single-line and does not start on the very left-most column (which is unusual). / Medium = 1, / The command line value confidence is high. This means that the value was explicitly send from the shell integration script. / High = 2 } export interface Terminal { / An object that contains -powered features for the terminal. This will always be undefined immediately after the terminal is created. Listen to { window.onDidActivateTerminalShellIntegration} to be notified when shell integration is activated for a terminal. Note that this object may remain undefined if shell integation never activates. For example Command Prompt does not support shell integration and a user's shell setup could conflict with the automatic shell integration activation. / readonly shellIntegration: TerminalShellIntegration | undefined; } export interface TerminalShellIntegration { / The current working directory of the terminal. This will be a { Uri} if the path reported by the shell can reliably be mapped to the connected machine. / readonly cwd: Uri | undefined; / Execute a command, sending ^C as necessary to interrupt any running command if needed. commandLine The command line to execute, this is the exact text that will be sent to the terminal. // Execute a command in a terminal immediately after being created const myTerm = window.createTerminal(); window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) ={ if (terminal === myTerm) { const command = shellIntegration.executeCommand('echo "Hello world"'); const code = await command.exitCode; (); } })); // Fallback to sendText if there is no shell integration within 3 seconds of launching setTimeout(() ={ if (!myTerm.shellIntegration) { myTerm.sendText('echo "Hello world"'); // Without shell integration, we can't know when the command has finished or what the // exit code was. } }, 3000); // Send command to terminal that has been alive for a while const commandLine = 'echo "Hello world"'; if (term.shellIntegration) { const command = term.shellIntegration.executeCommand({ commandLine }); const code = await command.exitCode; (); } else { term.sendText(commandLine); // Without shell integration, we can't know when the command has finished or what the // exit code was. } / executeCommand(commandLine: string): TerminalShellExecution; / Execute a command, sending ^C as necessary to interrupt any running command if needed. Note This is not guaranteed to work as must be activated. Check whether { TerminalShellExecution.exitCode} is rejected to verify whether it was successful. command A command to run. args Arguments to launch the executable with which will be automatically escaped based on the executable type. // Execute a command in a terminal immediately after being created const myTerm = window.createTerminal(); window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) ={ if (terminal === myTerm) { const command = shellIntegration.executeCommand({ command: 'echo', args: ['Hello world'] }); const code = await command.exitCode; (); } })); // Fallback to sendText if there is no shell integration within 3 seconds of launching setTimeout(() ={ if (!myTerm.shellIntegration) { myTerm.sendText('echo "Hello world"'); // Without shell integration, we can't know when the command has finished or what the // exit code was. } }, 3000); // Send command to terminal that has been alive for a while const commandLine = 'echo "Hello world"'; if (term.shellIntegration) { const command = term.shellIntegration.executeCommand({ command: 'echo', args: ['Hello world'] }); const code = await command.exitCode; (); } else { term.sendText(commandLine); // Without shell integration, we can't know when the command has finished or what the // exit code was. } / executeCommand(executable: string, args: string[]): TerminalShellExecution; } export interface TerminalShellIntegrationChangeEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; } export interface TerminalShellExecutionStartEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; / The terminal shell execution that has ended. / readonly execution: TerminalShellExecution; } export interface TerminalShellExecutionEndEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; / The terminal shell execution that has ended. / readonly execution: TerminalShellExecution; / The exit code reported by the shell. means the shell did not report an exit code or the shell reported a command started before the command finished. / readonly exitCode: number | undefined; } export namespace window { / Fires when shell integration activates or one of its properties changes in a terminal. / export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent; / This will be fired when a terminal command is started. This event will fire only when is activated for the terminal. / export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent; / This will be fired when a terminal command is ended. This event will fire only when is activated for the terminal. / export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent; } }
Very close to the final API! We will wait for some feedback from users before finalizing. ts /
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See in the project root for license information. *
-------------/ declare module 'vscode' { // / A command that was executed in a terminal. / export interface TerminalShellExecution { / The command line that was executed. The { TerminalShellExecutionCommandLineConfidence confidence} of this value depends on the specific shell's shell integration implementation. This value may become more accurate after { window.onDidEndTerminalShellExecution} is fired. // Log the details of the command line on start and end window.onDidStartTerminalShellExecution(event ={ const commandLine = event.execution.commandLine; (); }); window.onDidEndTerminalShellExecution(event ={ const commandLine = event.execution.commandLine; (); }); function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { return [ , , ` Trusted: ${command.ommandLine.isTrusted} ].join('n'); } / readonly commandLine: TerminalShellExecutionCommandLine; / The working directory that was reported by the shell when this command executed. This { Uri} may represent a file on another machine (eg. ssh into another machine). This requires the shell integration to support working directory reporting. / readonly cwd: Uri | undefined; / Creates a stream of raw data (including escape sequences) that is written to the terminal. This will only include data that was written after was called for the first time, ie. you must call immediately after the command is executed via { TerminalShellIntegration.executeCommand} or { window.onDidStartTerminalShellExecution} to not miss any data. // Log all data written to the terminal for a command const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); const stream = (); for await (const data of stream) { (data); } / read(): AsyncIterable<string; } / A command line that was executed in a terminal. / export interface TerminalShellExecutionCommandLine { / The full command line that was executed, including both the command and its arguments. / readonly value: string; / Whether the command line value came from a trusted source and is therefore safe to execute without user additional confirmation, such as a notification that asks "Do you want to execute (command)?". This verification is likely only needed if you are going to execute the command again. This is only when the command line was reported explicitly by the shell integration script (ie. { TerminalShellExecutionCommandLineConfidence.High high confidence}) and it used a nonce for verification. / readonly isTrusted: boolean; / The confidence of the command line value which is determined by how the value was obtained. This depends upon the implementation of the shell integration script. / readonly confidence: TerminalShellExecutionCommandLineConfidence; } / The confidence of a { TerminalShellExecutionCommandLine} value. / enum TerminalShellExecutionCommandLineConfidence { / The command line value confidence is low. This means that the value was read from the terminal buffer using markers reported by the shell integration script. Additionally one of the following conditions will be met: The command started on the very left-most column which is unusual, or The command is multi-line which is more difficult to accurately detect due to line continuation characters and right prompts. Command line markers were not reported by the shell integration script. / Low = 0, / The command line value confidence is medium. This means that the value was read from the terminal buffer using markers reported by the shell integration script. The command is single-line and does not start on the very left-most column (which is unusual). / Medium = 1, / The command line value confidence is high. This means that the value was explicitly sent from the shell integration script or the command was executed via the { TerminalShellIntegration.executeCommand} API. / High = 2 } export interface Terminal { / An object that contains -powered features for the terminal. This will always be immediately after the terminal is created. Listen to { window.onDidActivateTerminalShellIntegration} to be notified when shell integration is activated for a terminal. Note that this object may remain undefined if shell integation never activates. For example Command Prompt does not support shell integration and a user's shell setup could conflict with the automatic shell integration activation. / readonly shellIntegration: TerminalShellIntegration | undefined; } / -powered capabilities owned by a terminal. / export interface TerminalShellIntegration { / The current working directory of the terminal. This { Uri} may represent a file on another machine (eg. ssh into another machine). This requires the shell integration to support working directory reporting. / readonly cwd: Uri | undefined; / Execute a command, sending ^C as necessary to interrupt any running command if needed. commandLine The command line to execute, this is the exact text that will be sent to the terminal. // Execute a command in a terminal immediately after being created const myTerm = window.createTerminal(); window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) ={ if (terminal === myTerm) { const command = shellIntegration.executeCommand('echo "Hello world"'); const code = await command.exitCode; (); } })); // Fallback to sendText if there is no shell integration within 3 seconds of launching setTimeout(() ={ if (!myTerm.shellIntegration) { myTerm.sendText('echo "Hello world"'); // Without shell integration, we can't know when the command has finished or what the // exit code was. } }, 3000); // Send command to terminal that has been alive for a while const commandLine = 'echo "Hello world"'; if (term.shellIntegration) { const command = term.shellIntegration.executeCommand({ commandLine }); const code = await command.exitCode; (); } else { term.sendText(commandLine); // Without shell integration, we can't know when the command has finished or what the // exit code was. } / executeCommand(commandLine: string): TerminalShellExecution; / Execute a command, sending ^C as necessary to interrupt any running command if needed. Note This is not guaranteed to work as must be activated. Check whether { TerminalShellExecution.exitCode} is rejected to verify whether it was successful. command A command to run. args Arguments to launch the executable with which will be automatically escaped based on the executable type. // Execute a command in a terminal immediately after being created const myTerm = window.createTerminal(); window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) ={ if (terminal === myTerm) { const command = shellIntegration.executeCommand({ command: 'echo', args: ['Hello world'] }); const code = await command.exitCode; (); } })); // Fallback to sendText if there is no shell integration within 3 seconds of launching setTimeout(() ={ if (!myTerm.shellIntegration) { myTerm.sendText('echo "Hello world"'); // Without shell integration, we can't know when the command has finished or what the // exit code was. } }, 3000); // Send command to terminal that has been alive for a while const commandLine = 'echo "Hello world"'; if (term.shellIntegration) { const command = term.shellIntegration.executeCommand({ command: 'echo', args: ['Hello world'] }); const code = await command.exitCode; (); } else { term.sendText(commandLine); // Without shell integration, we can't know when the command has finished or what the // exit code was. } / executeCommand(executable: string, args: string[]): TerminalShellExecution; } export interface TerminalShellIntegrationChangeEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; } export interface TerminalShellExecutionStartEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; / The terminal shell execution that has ended. / readonly execution: TerminalShellExecution; } export interface TerminalShellExecutionEndEvent { / The terminal that shell integration has been activated in. / readonly terminal: Terminal; / The shell integration object. / readonly shellIntegration: TerminalShellIntegration; / The terminal shell execution that has ended. / readonly execution: TerminalShellExecution; / The exit code reported by the shell. means the shell did not report an exit code or the shell reported a command started before the command finished. / readonly exitCode: number | undefined; } export namespace window { / Fires when shell integration activates or one of its properties changes in a terminal. / export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent; / This will be fired when a terminal command is started. This event will fire only when is activated for the terminal. / export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent; / This will be fired when a terminal command is ended. This event will fire only when is activated for the terminal. */ export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent; } }
I've reviewed the API and it appears entirely sufficient for my goals (scanning the terminal output for a URI that an extension spawning the terminal will then connect to).
I hope to test this out soon to see if it meets our needs! Our needs are simplistic, we want to be able to run a command in the terminal and get the return code of that command. Reading the API, I think this can be done, but I plan to test it in the coming days.
yep should be very easy for that by listening to :+1:
This would require invoking the command and then statically waiting for this handler, right? Is there / was there consideration of an async method where we can directly invoke a command in the terminal and wait for it to complete?
yes you can use to execute a command and track its progress. For just listening you would use
I'm curious on your suggested way, with this API, to create your own terminal and then use the shellIntegration to run the command, since this is event driven rather than all async and await? Specifically for our use case, it'd be best if we could, all in one async method, create a terminal, use that terminal to invoke a command, and then get the result of the command.
Also, I notice that there is no way to guarantee that this works because the shell integration could possibly be turned off by the user. Is there a way around this? Another way to integrate with the shell? Etc.
there's an example in the jsdoc for executing a command immediately after creating a terminal with a fallback: You would also want to dispose of the listener when you're done with it.
Understood. That makes sense. My general feedback is that this is somewhat complex for someone who wants to invoke a command and simply get the exit code. It definitely makes it possible, by creating a variable outside of the scope and then updating it in the handlers, but in a perfect world we would be able to do this process in a simple async await pattern.
you can wrap it on your end, there are too many things that can go wrong for us to expose some reliable method to always do this. For example you could assume shell integration is active, and if it doesn't work then just point the user at troubleshooting docs. If you do that (which I'd recommend) then it would be quite simple.
Got it, that makes sense. Is there a specific documentation on the VS Code site that has documentation on when shellIntegration won't be on, and what troubleshooting there is? Is it just this page?
yes just that page. There's a bit of information about it at the bottom. It's hard to give general advice because they're are so many different setups
A simple usage sample is available in the samples repo: (it won't work properly until tomorrow due to a bug fix to in core)
Hi can you please confirm, if as part of this feature, allowing access to the active terminal selected text is going to be available / planned to work in the short term? (related issue ). One of the use cases could be for extensions using a command as part of terminal/context to access the selected text from the terminal to perform things like AI Suggestions, Output sharing, etc. Thank you
selection access will be done as a part of the buffer access api, not this one. That's been but on hold for a while as I have some competing priorities and want to think about the API shape in the background a bit. We definitely do want to finalize an api for selection eventually as GH copilot uses the current proposal, unfortunately to make it future proof we need to tie it in with the much more complex buffer proposal.
Moving to July for Python to adopt first and give feedback before finalizing.
Is there any way to receive a stream of raw data from a terminal that does not have shell integration? For example if I create a terminal with any command of my choice by setting to and to . Since there's only one command that could plausibly be run in that terminal it's not necessary to try and guess which command the output comes from. The current API doesn't seem to offer anything to cover this use case, and I think it would be too limiting for my purpose.
no, this is only for when shell integration is enabled. There is the related proposal for buffer access which allows reading the buffer which would probably cover your use case.
Thank you, this really helps!
I have a quick question. What is the difference between this API and My current use case is grabbing the output from an executed command, and I am using to do so. I suppose we can also use read() from to read the output stream but feel free to correct me. I wonder which one would you think have a higher chance for public release. Before these two APIs I remembered having to work around with the old , so I truly appreciate the current more stable approaches you have made available. Thanks!
is old and will be removed, is unlikely to get finalized and may be replaced eventually with improvements upon . should be finalized very soon, I just need a fix a few bugs with it.
Plan: Fix the 2 bugs that have been found, these are impl-related, not API shape related , Get final sign off from Finalize for August

Discussion:
"scmValidation", "tabInputMultiDiff", "tabInputTextMerge", <del> "terminalShellIntegration", </del> "timeline" ], "categories": [

"../../src/vscode-dts/vscode.proposed.scmTextDocument.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputMultiDiff.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts", <del> "../../src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts", </del> "../../src/vscode-dts/vscode.proposed.timeline.d.ts", "../../src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts", "../types/lib.textEncoder.d.ts"

"telemetry", "terminalDataWriteEvent", "terminalDimensions", <del> "terminalShellIntegration", </del> "testObserver", "textSearchProvider", "timeline",

terminalSelection: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalSelection.d.ts', }, <del> terminalShellIntegration: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts', }, </del> testMessageStackTrace: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts', },

return _asExtensionEvent(extHostTerminalService.onDidExecuteTerminalCommand)(listener, thisArg, disposables); }, onDidChangeTerminalShellIntegration(listener, thisArg?, disposables?) { <del> checkProposedApiEnabled(extension, 'terminalShellIntegration'); </del> return _asExtensionEvent(extHostTerminalShellIntegration.onDidChangeTerminalShellIntegration)(listener, thisArg, disposables); }, onDidStartTerminalShellExecution(listener, thisArg?, disposables?) { <del> checkProposedApiEnabled(extension, 'terminalShellIntegration'); </del> return _asExtensionEvent(extHostTerminalShellIntegration.onDidStartTerminalShellExecution)(listener, thisArg, disposables); }, onDidEndTerminalShellExecution(listener, thisArg?, disposables?) { <del> checkProposedApiEnabled(extension, 'terminalShellIntegration'); </del> return _asExtensionEvent(extHostTerminalShellIntegration.onDidEndTerminalShellExecution)(listener, thisArg, disposables); }, get state() {

readonly state: TerminalState; /** <ins> * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered * features for the terminal. This will always be `undefined` immediately after the terminal * is created. Listen to {@link window.onDidChangeTerminalShellIntegration} to be notified * when shell integration is activated for a terminal. * * Note that this object may remain undefined if shell integation never activates. For * example Command Prompt does not support shell integration and a user's shell setup could * conflict with the automatic shell integration activation. */ readonly shellIntegration: TerminalShellIntegration | undefined; /** </ins> * Send text to the terminal. The text is written to the stdin of the underlying pty process * (shell) of the terminal. *

} /** <ins> * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. */ export interface TerminalShellIntegration { /** * The current working directory of the terminal. This {@link Uri} may represent a file on * another machine (eg. ssh into another machine). This requires the shell integration to * support working directory reporting. */ readonly cwd: Uri | undefined; /** * Execute a command, sending ^C as necessary to interrupt any running command if needed. * * @param commandLine The command line to execute, this is the exact text that will be sent * to the terminal. * * @example * // Execute a command in a terminal immediately after being created * const myTerm = window.createTerminal(); * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { *   if (terminal === myTerm) { *     const execution = shellIntegration.executeCommand('echo "Hello world"'); *     window.onDidEndTerminalShellExecution(event => { *     if (event.execution === execution) { *       console.log(`Command exited with code ${event.exitCode}`); *     } *   } * })); * // Fallback to sendText if there is no shell integration within 3 seconds of launching * setTimeout(() => { *   if (!myTerm.shellIntegration) { *     myTerm.sendText('echo "Hello world"'); *     // Without shell integration, we can't know when the command has finished or what the *     // exit code was. *   } * }, 3000); * * @example * // Send command to terminal that has been alive for a while * const commandLine = 'echo "Hello world"'; * if (term.shellIntegration) { *   const execution = shellIntegration.executeCommand({ commandLine }); *   window.onDidEndTerminalShellExecution(event => { *   if (event.execution === execution) { *     console.log(`Command exited with code ${event.exitCode}`); *   } * } else { *   term.sendText(commandLine); *   // Without shell integration, we can't know when the command has finished or what the *   // exit code was. * } */ executeCommand(commandLine: string): TerminalShellExecution; /** * Execute a command, sending ^C as necessary to interrupt any running command if needed. * * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to * verify whether it was successful. * * @param command A command to run. * @param args Arguments to launch the executable with which will be automatically escaped * based on the executable type. * * @example * // Execute a command in a terminal immediately after being created * const myTerm = window.createTerminal(); * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { *   if (terminal === myTerm) { *     const command = shellIntegration.executeCommand({ *       command: 'echo', *       args: ['Hello world'] *     }); *     const code = await command.exitCode; *     console.log(`Command exited with code ${code}`); *   } * })); * // Fallback to sendText if there is no shell integration within 3 seconds of launching * setTimeout(() => { *   if (!myTerm.shellIntegration) { *     myTerm.sendText('echo "Hello world"'); *     // Without shell integration, we can't know when the command has finished or what the *     // exit code was. *   } * }, 3000); * * @example * // Send command to terminal that has been alive for a while * const commandLine = 'echo "Hello world"'; * if (term.shellIntegration) { *   const command = term.shellIntegration.executeCommand({ *     command: 'echo', *     args: ['Hello world'] *   }); *   const code = await command.exitCode; *   console.log(`Command exited with code ${code}`); * } else { *   term.sendText(commandLine); *   // Without shell integration, we can't know when the command has finished or what the *   // exit code was. * } */ executeCommand(executable: string, args: string[]): TerminalShellExecution; } /** * A command that was executed in a terminal. */ export interface TerminalShellExecution { /** * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} * of this value depends on the specific shell's shell integration implementation. This * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is * fired. * * @example * // Log the details of the command line on start and end * window.onDidStartTerminalShellExecution(event => { *   const commandLine = event.execution.commandLine; *   console.log(`Command startedn${summarizeCommandLine(commandLine)}`); * }); * window.onDidEndTerminalShellExecution(event => { *   const commandLine = event.execution.commandLine; *   console.log(`Command endedn${summarizeCommandLine(commandLine)}`); * }); * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { *   return [ *     `  Command line: ${command.commandLine.value}`, *     `  Confidence: ${command.commandLine.confidence}`, *     `  Trusted: ${command.commandLine.isTrusted} *   ].join('n'); * } */ readonly commandLine: TerminalShellExecutionCommandLine; /** * The working directory that was reported by the shell when this command executed. This * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This * requires the shell integration to support working directory reporting. */ readonly cwd: Uri | undefined; /** * Creates a stream of raw data (including escape sequences) that is written to the * terminal. This will only include data that was written after `read` was called for * the first time, ie. you must call `read` immediately after the command is executed via * {@link TerminalShellIntegration.executeCommand} or * {@link window.onDidStartTerminalShellExecution} to not miss any data. * * @example * // Log all data written to the terminal for a command * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); * const stream = command.read(); * for await (const data of stream) { *   console.log(data); * } */ read(): AsyncIterable<string>; } /** * A command line that was executed in a terminal. */ export interface TerminalShellExecutionCommandLine { /** * The full command line that was executed, including both the command and its arguments. */ readonly value: string; /** * Whether the command line value came from a trusted source and is therefore safe to * execute without user additional confirmation, such as a notification that asks "Do you * want to execute (command)?". This verification is likely only needed if you are going to * execute the command again. * * This is `true` only when the command line was reported explicitly by the shell * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) * and it used a nonce for verification. */ readonly isTrusted: boolean; /** * The confidence of the command line value which is determined by how the value was * obtained. This depends upon the implementation of the shell integration script. */ readonly confidence: TerminalShellExecutionCommandLineConfidence; } /** * The confidence of a {@link TerminalShellExecutionCommandLine} value. */ enum TerminalShellExecutionCommandLineConfidence { /** * The command line value confidence is low. This means that the value was read from the * terminal buffer using markers reported by the shell integration script. Additionally one * of the following conditions will be met: * * - The command started on the very left-most column which is unusual, or * - The command is multi-line which is more difficult to accurately detect due to line *   continuation characters and right prompts. * - Command line markers were not reported by the shell integration script. */ Low = 0, /** * The command line value confidence is medium. This means that the value was read from the * terminal buffer using markers reported by the shell integration script. The command is * single-line and does not start on the very left-most column (which is unusual). */ Medium = 1, /** * The command line value confidence is high. This means that the value was explicitly sent * from the shell integration script or the command was executed via the * {@link TerminalShellIntegration.executeCommand} API. */ High = 2 } /** * An event signalling that a terminal's shell integration has changed. */ export interface TerminalShellIntegrationChangeEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; } /** * An event signalling that an execution has started in a terminal. */ export interface TerminalShellExecutionStartEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; /** * The terminal shell execution that has ended. */ readonly execution: TerminalShellExecution; } /** * An event signalling that an execution has ended in a terminal. */ export interface TerminalShellExecutionEndEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; /** * The terminal shell execution that has ended. */ readonly execution: TerminalShellExecution; /** * The exit code reported by the shell. * * Note that `undefined` means the shell either did not report an exit  code (ie. the shell * integration script is misbehaving) or the shell reported a command started before the command * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use * case, it may be best to treat this as a failure. * * @example * const execution = shellIntegration.executeCommand({ *   command: 'echo', *   args: ['Hello world'] * }); * window.onDidEndTerminalShellExecution(event => { *   if (event.execution === execution) { *     if (event.exitCode === undefined) { * 	     console.log('Command finished but exit code is unknown'); *     } else if (event.exitCode === 0) { * 	     console.log('Command succeeded'); *     } else { * 	     console.log('Command failed'); *     } *   } * }); */ readonly exitCode: number | undefined; } /** </ins> * Provides information on a line in a terminal in order to provide links for it. */ export interface TerminalLinkContext {

export const onDidChangeTerminalState: Event<Terminal>; /** <ins> * Fires when shell integration activates or one of its properties changes in a terminal. */ export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>; /** * This will be fired when a terminal command is started. This event will fire only when * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is * activated for the terminal. */ export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent>; /** * This will be fired when a terminal command is ended. This event will fire only when * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is * activated for the terminal. */ export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent>; /** </ins> * Represents the current window's state. */ export const state: WindowState;

<del> /*--------------------------------------------------------------------------------------------- *  Copyright (c) Microsoft Corporation. All rights reserved. *  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/145234 /** * A command that was executed in a terminal. */ export interface TerminalShellExecution { /** * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} * of this value depends on the specific shell's shell integration implementation. This * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is * fired. * * @example * // Log the details of the command line on start and end * window.onDidStartTerminalShellExecution(event => { *   const commandLine = event.execution.commandLine; *   console.log(`Command startedn${summarizeCommandLine(commandLine)}`); * }); * window.onDidEndTerminalShellExecution(event => { *   const commandLine = event.execution.commandLine; *   console.log(`Command endedn${summarizeCommandLine(commandLine)}`); * }); * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { *   return [ *     `  Command line: ${command.commandLine.value}`, *     `  Confidence: ${command.commandLine.confidence}`, *     `  Trusted: ${command.commandLine.isTrusted} *   ].join('n'); * } */ readonly commandLine: TerminalShellExecutionCommandLine; /** * The working directory that was reported by the shell when this command executed. This * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This * requires the shell integration to support working directory reporting. */ readonly cwd: Uri | undefined; /** * Creates a stream of raw data (including escape sequences) that is written to the * terminal. This will only include data that was written after `read` was called for * the first time, ie. you must call `read` immediately after the command is executed via * {@link TerminalShellIntegration.executeCommand} or * {@link window.onDidStartTerminalShellExecution} to not miss any data. * * @example * // Log all data written to the terminal for a command * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); * const stream = command.read(); * for await (const data of stream) { *   console.log(data); * } */ read(): AsyncIterable<string>; } /** * A command line that was executed in a terminal. */ export interface TerminalShellExecutionCommandLine { /** * The full command line that was executed, including both the command and its arguments. */ readonly value: string; /** * Whether the command line value came from a trusted source and is therefore safe to * execute without user additional confirmation, such as a notification that asks "Do you * want to execute (command)?". This verification is likely only needed if you are going to * execute the command again. * * This is `true` only when the command line was reported explicitly by the shell * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) * and it used a nonce for verification. */ readonly isTrusted: boolean; /** * The confidence of the command line value which is determined by how the value was * obtained. This depends upon the implementation of the shell integration script. */ readonly confidence: TerminalShellExecutionCommandLineConfidence; } /** * The confidence of a {@link TerminalShellExecutionCommandLine} value. */ enum TerminalShellExecutionCommandLineConfidence { /** * The command line value confidence is low. This means that the value was read from the * terminal buffer using markers reported by the shell integration script. Additionally one * of the following conditions will be met: * * - The command started on the very left-most column which is unusual, or * - The command is multi-line which is more difficult to accurately detect due to line *   continuation characters and right prompts. * - Command line markers were not reported by the shell integration script. */ Low = 0, /** * The command line value confidence is medium. This means that the value was read from the * terminal buffer using markers reported by the shell integration script. The command is * single-line and does not start on the very left-most column (which is unusual). */ Medium = 1, /** * The command line value confidence is high. This means that the value was explicitly sent * from the shell integration script or the command was executed via the * {@link TerminalShellIntegration.executeCommand} API. */ High = 2 } export interface Terminal { /** * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered * features for the terminal. This will always be `undefined` immediately after the terminal * is created. Listen to {@link window.onDidChangeTerminalShellIntegration} to be notified * when shell integration is activated for a terminal. * * Note that this object may remain undefined if shell integation never activates. For * example Command Prompt does not support shell integration and a user's shell setup could * conflict with the automatic shell integration activation. */ readonly shellIntegration: TerminalShellIntegration | undefined; } /** * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. */ export interface TerminalShellIntegration { /** * The current working directory of the terminal. This {@link Uri} may represent a file on * another machine (eg. ssh into another machine). This requires the shell integration to * support working directory reporting. */ readonly cwd: Uri | undefined; /** * Execute a command, sending ^C as necessary to interrupt any running command if needed. * * @param commandLine The command line to execute, this is the exact text that will be sent * to the terminal. * * @example * // Execute a command in a terminal immediately after being created * const myTerm = window.createTerminal(); * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { *   if (terminal === myTerm) { *     const execution = shellIntegration.executeCommand('echo "Hello world"'); *     window.onDidEndTerminalShellExecution(event => { *     if (event.execution === execution) { *       console.log(`Command exited with code ${event.exitCode}`); *     } *   } * })); * // Fallback to sendText if there is no shell integration within 3 seconds of launching * setTimeout(() => { *   if (!myTerm.shellIntegration) { *     myTerm.sendText('echo "Hello world"'); *     // Without shell integration, we can't know when the command has finished or what the *     // exit code was. *   } * }, 3000); * * @example * // Send command to terminal that has been alive for a while * const commandLine = 'echo "Hello world"'; * if (term.shellIntegration) { *   const execution = shellIntegration.executeCommand({ commandLine }); *   window.onDidEndTerminalShellExecution(event => { *   if (event.execution === execution) { *     console.log(`Command exited with code ${event.exitCode}`); *   } * } else { *   term.sendText(commandLine); *   // Without shell integration, we can't know when the command has finished or what the *   // exit code was. * } */ executeCommand(commandLine: string): TerminalShellExecution; /** * Execute a command, sending ^C as necessary to interrupt any running command if needed. * * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to * verify whether it was successful. * * @param command A command to run. * @param args Arguments to launch the executable with which will be automatically escaped * based on the executable type. * * @example * // Execute a command in a terminal immediately after being created * const myTerm = window.createTerminal(); * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { *   if (terminal === myTerm) { *     const command = shellIntegration.executeCommand({ *       command: 'echo', *       args: ['Hello world'] *     }); *     const code = await command.exitCode; *     console.log(`Command exited with code ${code}`); *   } * })); * // Fallback to sendText if there is no shell integration within 3 seconds of launching * setTimeout(() => { *   if (!myTerm.shellIntegration) { *     myTerm.sendText('echo "Hello world"'); *     // Without shell integration, we can't know when the command has finished or what the *     // exit code was. *   } * }, 3000); * * @example * // Send command to terminal that has been alive for a while * const commandLine = 'echo "Hello world"'; * if (term.shellIntegration) { *   const command = term.shellIntegration.executeCommand({ *     command: 'echo', *     args: ['Hello world'] *   }); *   const code = await command.exitCode; *   console.log(`Command exited with code ${code}`); * } else { *   term.sendText(commandLine); *   // Without shell integration, we can't know when the command has finished or what the *   // exit code was. * } */ executeCommand(executable: string, args: string[]): TerminalShellExecution; } export interface TerminalShellIntegrationChangeEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; } export interface TerminalShellExecutionStartEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; /** * The terminal shell execution that has ended. */ readonly execution: TerminalShellExecution; } export interface TerminalShellExecutionEndEvent { /** * The terminal that shell integration has been activated in. */ readonly terminal: Terminal; /** * The shell integration object. */ readonly shellIntegration: TerminalShellIntegration; /** * The terminal shell execution that has ended. */ readonly execution: TerminalShellExecution; /** * The exit code reported by the shell. * * Note that `undefined` means the shell either did not report an exit  code (ie. the shell * integration script is misbehaving) or the shell reported a command started before the command * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use * case, it may be best to treat this as a failure. * * @example * const execution = shellIntegration.executeCommand({ *   command: 'echo', *   args: ['Hello world'] * }); * window.onDidEndTerminalShellExecution(event => { *   if (event.execution === execution) { *     if (event.exitCode === undefined) { * 	     console.log('Command finished but exit code is unknown'); *     } else if (event.exitCode === 0) { * 	     console.log('Command succeeded'); *     } else { * 	     console.log('Command failed'); *     } *   } * }); */ readonly exitCode: number | undefined; } export namespace window { /** * Fires when shell integration activates or one of its properties changes in a terminal. */ export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>; /** * This will be fired when a terminal command is started. This event will fire only when * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is * activated for the terminal. */ export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent>; /** * This will be fired when a terminal command is ended. This event will fire only when * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is * activated for the terminal. */ export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent>; } } </del>