Terminal API
The Terminal API provides comprehensive functionality for terminal integration and management within the development environment.
Overview
The Terminal API enables you to:
- Create and manage terminal instances
- Execute commands programmatically
- Monitor terminal output and events
- Customize terminal appearance and behavior
- Integrate with shell environments
- Handle terminal input and output
- Support multiple terminal types
- Provide terminal-based user interfaces
Basic Usage
Terminal Management
typescript
import { TraeAPI } from '@trae/api';
// Terminal manager
class TerminalManager {
private terminals: Map<string, TerminalInstance> = new Map();
private activeTerminal: TerminalInstance | null = null;
private eventEmitter = new TraeAPI.EventEmitter<TerminalEvent>();
private terminalCounter = 0;
constructor() {
this.setupEventListeners();
}
// Create terminal
async createTerminal(options: TerminalOptions = {}): Promise<TerminalInstance> {
const terminalId = `terminal-${++this.terminalCounter}`;
const terminal = new TerminalInstance(terminalId, options);
this.terminals.set(terminalId, terminal);
// Set as active if it's the first terminal
if (!this.activeTerminal) {
this.activeTerminal = terminal;
}
console.log(`Created terminal: ${terminalId}`);
this.eventEmitter.fire({ type: 'terminalCreated', terminal });
return terminal;
}
// Get terminal by ID
getTerminal(terminalId: string): TerminalInstance | null {
return this.terminals.get(terminalId) || null;
}
// Get all terminals
getAllTerminals(): TerminalInstance[] {
return Array.from(this.terminals.values());
}
// Get active terminal
getActiveTerminal(): TerminalInstance | null {
return this.activeTerminal;
}
// Set active terminal
setActiveTerminal(terminalId: string): boolean {
const terminal = this.terminals.get(terminalId);
if (terminal) {
this.activeTerminal = terminal;
this.eventEmitter.fire({ type: 'terminalActivated', terminal });
return true;
}
return false;
}
// Close terminal
async closeTerminal(terminalId: string): Promise<boolean> {
const terminal = this.terminals.get(terminalId);
if (!terminal) {
return false;
}
await terminal.dispose();
this.terminals.delete(terminalId);
// Update active terminal if needed
if (this.activeTerminal === terminal) {
const remaining = Array.from(this.terminals.values());
this.activeTerminal = remaining.length > 0 ? remaining[0] : null;
}
console.log(`Closed terminal: ${terminalId}`);
this.eventEmitter.fire({ type: 'terminalClosed', terminalId });
return true;
}
// Execute command in terminal
async executeCommand(command: string, options: ExecuteOptions = {}): Promise<ExecutionResult> {
let terminal = options.terminalId ? this.getTerminal(options.terminalId) : this.activeTerminal;
if (!terminal) {
// Create new terminal if none exists
terminal = await this.createTerminal({
name: 'Command Terminal',
cwd: options.cwd
});
}
return terminal.executeCommand(command, options);
}
// Execute command and return output
async executeCommandWithOutput(command: string, options: ExecuteOptions = {}): Promise<string> {
const result = await this.executeCommand(command, options);
return new Promise((resolve, reject) => {
let output = '';
const outputListener = result.onOutput(data => {
output += data;
});
const exitListener = result.onExit(code => {
outputListener.dispose();
exitListener.dispose();
if (code === 0) {
resolve(output);
} else {
reject(new Error(`Command failed with exit code ${code}: ${output}`));
}
});
});
}
// Setup event listeners
private setupEventListeners(): void {
// Listen for workspace changes
TraeAPI.workspace.onDidChangeWorkspaceFolders(() => {
// Update terminal working directories if needed
this.updateTerminalWorkingDirectories();
});
}
private updateTerminalWorkingDirectories(): void {
const workspaceFolders = TraeAPI.workspace.workspaceFolders;
if (!workspaceFolders || workspaceFolders.length === 0) {
return;
}
const primaryWorkspace = workspaceFolders[0].uri.fsPath;
for (const terminal of this.terminals.values()) {
if (terminal.options.followWorkspace) {
terminal.changeWorkingDirectory(primaryWorkspace);
}
}
}
// Event handling
onDidChangeTerminal(listener: (event: TerminalEvent) => void): TraeAPI.Disposable {
return this.eventEmitter.event(listener);
}
dispose(): void {
for (const terminal of this.terminals.values()) {
terminal.dispose();
}
this.terminals.clear();
this.activeTerminal = null;
this.eventEmitter.dispose();
}
}
// Terminal instance
class TerminalInstance {
private process: any = null;
private isRunning = false;
private outputBuffer: string = '';
private eventEmitter = new TraeAPI.EventEmitter<TerminalInstanceEvent>();
private executionCounter = 0;
private currentExecution: ExecutionResult | null = null;
constructor(
public readonly id: string,
public readonly options: TerminalOptions
) {
this.initialize();
}
private async initialize(): Promise<void> {
try {
await this.createProcess();
console.log(`Terminal ${this.id} initialized`);
this.eventEmitter.fire({ type: 'initialized' });
} catch (error) {
console.error(`Failed to initialize terminal ${this.id}:`, error);
this.eventEmitter.fire({ type: 'error', error: error as Error });
}
}
private async createProcess(): Promise<void> {
const { spawn } = require('child_process');
const shell = this.getShell();
const args = this.getShellArgs();
const env = this.getEnvironment();
this.process = spawn(shell, args, {
cwd: this.options.cwd || process.cwd(),
env,
stdio: ['pipe', 'pipe', 'pipe']
});
this.isRunning = true;
// Setup process event handlers
this.process.stdout.on('data', (data: Buffer) => {
const text = data.toString();
this.outputBuffer += text;
this.eventEmitter.fire({ type: 'output', data: text, stream: 'stdout' });
});
this.process.stderr.on('data', (data: Buffer) => {
const text = data.toString();
this.outputBuffer += text;
this.eventEmitter.fire({ type: 'output', data: text, stream: 'stderr' });
});
this.process.on('exit', (code: number, signal: string) => {
this.isRunning = false;
console.log(`Terminal ${this.id} exited with code ${code}`);
this.eventEmitter.fire({ type: 'exit', code, signal });
});
this.process.on('error', (error: Error) => {
console.error(`Terminal ${this.id} error:`, error);
this.eventEmitter.fire({ type: 'error', error });
});
}
private getShell(): string {
if (this.options.shell) {
return this.options.shell;
}
// Platform-specific default shells
switch (process.platform) {
case 'win32':
return process.env.COMSPEC || 'cmd.exe';
case 'darwin':
case 'linux':
default:
return process.env.SHELL || '/bin/bash';
}
}
private getShellArgs(): string[] {
if (this.options.shellArgs) {
return this.options.shellArgs;
}
// Platform-specific default args
switch (process.platform) {
case 'win32':
return ['/K']; // Keep window open
case 'darwin':
case 'linux':
default:
return ['-i']; // Interactive mode
}
}
private getEnvironment(): NodeJS.ProcessEnv {
const env = { ...process.env };
// Add custom environment variables
if (this.options.env) {
Object.assign(env, this.options.env);
}
// Set terminal-specific variables
env.TERM = this.options.termType || 'xterm-256color';
env.TRAE_TERMINAL_ID = this.id;
return env;
}
// Execute command
async executeCommand(command: string, options: ExecuteOptions = {}): Promise<ExecutionResult> {
if (!this.isRunning) {
throw new Error(`Terminal ${this.id} is not running`);
}
const executionId = `exec-${++this.executionCounter}`;
const execution = new ExecutionResult(executionId, command, options);
this.currentExecution = execution;
// Send command to terminal
this.sendInput(command + '\n');
console.log(`Executing command in terminal ${this.id}: ${command}`);
this.eventEmitter.fire({ type: 'commandExecuted', command, executionId });
return execution;
}
// Send input to terminal
sendInput(input: string): void {
if (this.process && this.isRunning) {
this.process.stdin.write(input);
this.eventEmitter.fire({ type: 'input', data: input });
}
}
// Send key sequence
sendKey(key: string): void {
const keySequences: { [key: string]: string } = {
'enter': '\n',
'tab': '\t',
'escape': '\x1b',
'backspace': '\x08',
'delete': '\x7f',
'up': '\x1b[A',
'down': '\x1b[B',
'right': '\x1b[C',
'left': '\x1b[D',
'home': '\x1b[H',
'end': '\x1b[F',
'pageup': '\x1b[5~',
'pagedown': '\x1b[6~',
'ctrl+c': '\x03',
'ctrl+d': '\x04',
'ctrl+z': '\x1a'
};
const sequence = keySequences[key.toLowerCase()] || key;
this.sendInput(sequence);
}
// Change working directory
async changeWorkingDirectory(path: string): Promise<boolean> {
try {
const command = process.platform === 'win32' ? `cd /d "${path}"` : `cd "${path}"`;
await this.executeCommand(command);
console.log(`Changed working directory to: ${path}`);
this.eventEmitter.fire({ type: 'workingDirectoryChanged', path });
return true;
} catch (error) {
console.error(`Failed to change working directory:`, error);
return false;
}
}
// Clear terminal
clear(): void {
const clearCommand = process.platform === 'win32' ? 'cls' : 'clear';
this.sendInput(clearCommand + '\n');
this.outputBuffer = '';
this.eventEmitter.fire({ type: 'cleared' });
}
// Get terminal output
getOutput(): string {
return this.outputBuffer;
}
// Get terminal info
getInfo(): TerminalInfo {
return {
id: this.id,
name: this.options.name || `Terminal ${this.id}`,
isRunning: this.isRunning,
shell: this.getShell(),
cwd: this.options.cwd || process.cwd(),
pid: this.process?.pid,
createdAt: this.options.createdAt || Date.now()
};
}
// Event handling
onDidChangeState(listener: (event: TerminalInstanceEvent) => void): TraeAPI.Disposable {
return this.eventEmitter.event(listener);
}
// Dispose terminal
async dispose(): Promise<void> {
if (this.process && this.isRunning) {
this.process.kill();
// Wait for process to exit
await new Promise<void>((resolve) => {
if (!this.isRunning) {
resolve();
return;
}
const exitListener = this.eventEmitter.event(event => {
if (event.type === 'exit') {
exitListener.dispose();
resolve();
}
});
// Force kill after timeout
setTimeout(() => {
if (this.isRunning && this.process) {
this.process.kill('SIGKILL');
}
exitListener.dispose();
resolve();
}, 5000);
});
}
this.eventEmitter.dispose();
console.log(`Terminal ${this.id} disposed`);
}
}
// Execution result
class ExecutionResult {
private eventEmitter = new TraeAPI.EventEmitter<ExecutionEvent>();
private isCompleted = false;
private exitCode: number | null = null;
private output = '';
private startTime = Date.now();
constructor(
public readonly id: string,
public readonly command: string,
public readonly options: ExecuteOptions
) {}
// Mark execution as completed
complete(exitCode: number): void {
if (this.isCompleted) return;
this.isCompleted = true;
this.exitCode = exitCode;
const duration = Date.now() - this.startTime;
console.log(`Command execution completed: ${this.command} (exit code: ${exitCode}, duration: ${duration}ms)`);
this.eventEmitter.fire({ type: 'completed', exitCode, duration });
}
// Add output
addOutput(data: string): void {
this.output += data;
this.eventEmitter.fire({ type: 'output', data });
}
// Get execution info
getInfo(): ExecutionInfo {
return {
id: this.id,
command: this.command,
isCompleted: this.isCompleted,
exitCode: this.exitCode,
output: this.output,
startTime: this.startTime,
duration: this.isCompleted ? Date.now() - this.startTime : null
};
}
// Event handlers
onOutput(listener: (data: string) => void): TraeAPI.Disposable {
return this.eventEmitter.event(event => {
if (event.type === 'output') {
listener(event.data);
}
});
}
onExit(listener: (exitCode: number) => void): TraeAPI.Disposable {
return this.eventEmitter.event(event => {
if (event.type === 'completed') {
listener(event.exitCode);
}
});
}
onCompleted(listener: (info: ExecutionInfo) => void): TraeAPI.Disposable {
return this.eventEmitter.event(event => {
if (event.type === 'completed') {
listener(this.getInfo());
}
});
}
dispose(): void {
this.eventEmitter.dispose();
}
}
// Initialize terminal manager
const terminalManager = new TerminalManager();Advanced Features
Terminal UI Integration
typescript
// Terminal UI provider
class TerminalUIProvider {
private terminalPanel: TraeAPI.WebviewPanel | null = null;
private terminals: Map<string, TerminalView> = new Map();
constructor(private terminalManager: TerminalManager) {
this.setupEventListeners();
}
// Show terminal panel
async showTerminalPanel(): Promise<void> {
if (this.terminalPanel) {
this.terminalPanel.reveal();
return;
}
this.terminalPanel = TraeAPI.window.createWebviewPanel(
'terminal',
'Terminal',
TraeAPI.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true
}
);
this.terminalPanel.webview.html = this.getTerminalHTML();
this.setupWebviewMessageHandling();
this.terminalPanel.onDidDispose(() => {
this.terminalPanel = null;
});
}
private getTerminalHTML(): string {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Terminal</title>
<style>
body {
margin: 0;
padding: 0;
background: #1e1e1e;
color: #d4d4d4;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
overflow: hidden;
}
.terminal-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.terminal-tabs {
display: flex;
background: #2d2d30;
border-bottom: 1px solid #3e3e42;
padding: 0;
margin: 0;
}
.terminal-tab {
padding: 8px 16px;
background: #2d2d30;
border: none;
color: #cccccc;
cursor: pointer;
border-right: 1px solid #3e3e42;
position: relative;
}
.terminal-tab.active {
background: #1e1e1e;
color: #ffffff;
}
.terminal-tab .close-btn {
margin-left: 8px;
color: #cccccc;
cursor: pointer;
}
.terminal-tab .close-btn:hover {
color: #ffffff;
}
.terminal-content {
flex: 1;
position: relative;
}
.terminal-view {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: none;
}
.terminal-view.active {
display: block;
}
.terminal-output {
height: calc(100% - 30px);
overflow-y: auto;
padding: 10px;
white-space: pre-wrap;
word-wrap: break-word;
}
.terminal-input {
height: 30px;
background: #1e1e1e;
border: none;
border-top: 1px solid #3e3e42;
color: #d4d4d4;
padding: 0 10px;
font-family: inherit;
font-size: inherit;
outline: none;
width: 100%;
box-sizing: border-box;
}
.new-terminal-btn {
padding: 8px 16px;
background: #0e639c;
border: none;
color: #ffffff;
cursor: pointer;
margin-left: auto;
}
.new-terminal-btn:hover {
background: #1177bb;
}
.output-line {
margin: 0;
padding: 0;
}
.output-error {
color: #f48771;
}
.output-warning {
color: #dcdcaa;
}
.output-success {
color: #4ec9b0;
}
</style>
</head>
<body>
<div class="terminal-container">
<div class="terminal-tabs">
<button class="new-terminal-btn" onclick="createNewTerminal()">+ New Terminal</button>
</div>
<div class="terminal-content" id="terminalContent">
<!-- Terminal views will be added here -->
</div>
</div>
<script>
const vscode = acquireVsCodeApi();
let activeTerminalId = null;
let terminals = new Map();
// Create new terminal
function createNewTerminal() {
vscode.postMessage({ type: 'createTerminal' });
}
// Switch to terminal
function switchTerminal(terminalId) {
if (activeTerminalId) {
document.getElementById('tab-' + activeTerminalId).classList.remove('active');
document.getElementById('view-' + activeTerminalId).classList.remove('active');
}
activeTerminalId = terminalId;
document.getElementById('tab-' + terminalId).classList.add('active');
document.getElementById('view-' + terminalId).classList.add('active');
// Focus input
const input = document.getElementById('input-' + terminalId);
if (input) {
input.focus();
}
}
// Close terminal
function closeTerminal(terminalId, event) {
event.stopPropagation();
vscode.postMessage({ type: 'closeTerminal', terminalId });
}
// Send command
function sendCommand(terminalId, command) {
vscode.postMessage({ type: 'sendCommand', terminalId, command });
}
// Handle input
function handleInput(terminalId, event) {
if (event.key === 'Enter') {
const input = event.target;
const command = input.value;
input.value = '';
// Add command to output
addOutput(terminalId, '$ ' + command + '\n', 'command');
// Send command
sendCommand(terminalId, command);
}
}
// Add output to terminal
function addOutput(terminalId, text, type = 'output') {
const output = document.getElementById('output-' + terminalId);
if (output) {
const line = document.createElement('div');
line.className = 'output-line';
if (type === 'error') line.className += ' output-error';
if (type === 'warning') line.className += ' output-warning';
if (type === 'success') line.className += ' output-success';
line.textContent = text;
output.appendChild(line);
output.scrollTop = output.scrollHeight;
}
}
// Handle messages from extension
window.addEventListener('message', event => {
const message = event.data;
switch (message.type) {
case 'terminalCreated':
createTerminalView(message.terminal);
break;
case 'terminalClosed':
removeTerminalView(message.terminalId);
break;
case 'terminalOutput':
addOutput(message.terminalId, message.data, message.outputType);
break;
case 'terminalList':
message.terminals.forEach(terminal => createTerminalView(terminal));
break;
}
});
// Create terminal view
function createTerminalView(terminal) {
const tabsContainer = document.querySelector('.terminal-tabs');
const contentContainer = document.getElementById('terminalContent');
// Create tab
const tab = document.createElement('button');
tab.id = 'tab-' + terminal.id;
tab.className = 'terminal-tab';
tab.onclick = () => switchTerminal(terminal.id);
tab.innerHTML = `
${terminal.name}
<span class="close-btn" onclick="closeTerminal('${terminal.id}', event)">×</span>
`;
// Insert before new terminal button
const newBtn = document.querySelector('.new-terminal-btn');
tabsContainer.insertBefore(tab, newBtn);
// Create view
const view = document.createElement('div');
view.id = 'view-' + terminal.id;
view.className = 'terminal-view';
view.innerHTML = `
<div id="output-${terminal.id}" class="terminal-output"></div>
<input id="input-${terminal.id}" class="terminal-input"
placeholder="Type command and press Enter..."
onkeydown="handleInput('${terminal.id}', event)">
`;
contentContainer.appendChild(view);
// Switch to new terminal if it's the first one
if (!activeTerminalId) {
switchTerminal(terminal.id);
}
terminals.set(terminal.id, terminal);
}
// Remove terminal view
function removeTerminalView(terminalId) {
const tab = document.getElementById('tab-' + terminalId);
const view = document.getElementById('view-' + terminalId);
if (tab) tab.remove();
if (view) view.remove();
terminals.delete(terminalId);
// Switch to another terminal if this was active
if (activeTerminalId === terminalId) {
activeTerminalId = null;
const remainingTerminals = Array.from(terminals.keys());
if (remainingTerminals.length > 0) {
switchTerminal(remainingTerminals[0]);
}
}
}
// Request initial terminal list
vscode.postMessage({ type: 'getTerminals' });
</script>
</body>
</html>
`;
}
private setupWebviewMessageHandling(): void {
if (!this.terminalPanel) return;
this.terminalPanel.webview.onDidReceiveMessage(async message => {
switch (message.type) {
case 'createTerminal':
const terminal = await this.terminalManager.createTerminal({
name: `Terminal ${this.terminals.size + 1}`
});
this.sendMessage({
type: 'terminalCreated',
terminal: terminal.getInfo()
});
break;
case 'closeTerminal':
await this.terminalManager.closeTerminal(message.terminalId);
this.sendMessage({
type: 'terminalClosed',
terminalId: message.terminalId
});
break;
case 'sendCommand':
const terminalInstance = this.terminalManager.getTerminal(message.terminalId);
if (terminalInstance) {
await terminalInstance.executeCommand(message.command);
}
break;
case 'getTerminals':
const terminals = this.terminalManager.getAllTerminals();
this.sendMessage({
type: 'terminalList',
terminals: terminals.map(t => t.getInfo())
});
break;
}
});
}
private sendMessage(message: any): void {
if (this.terminalPanel) {
this.terminalPanel.webview.postMessage(message);
}
}
private setupEventListeners(): void {
this.terminalManager.onDidChangeTerminal(event => {
if (!this.terminalPanel) return;
switch (event.type) {
case 'terminalCreated':
this.sendMessage({
type: 'terminalCreated',
terminal: event.terminal.getInfo()
});
break;
case 'terminalClosed':
this.sendMessage({
type: 'terminalClosed',
terminalId: event.terminalId
});
break;
}
});
}
}
// Terminal view for individual terminals
class TerminalView {
constructor(
public readonly terminal: TerminalInstance,
private uiProvider: TerminalUIProvider
) {
this.setupEventListeners();
}
private setupEventListeners(): void {
this.terminal.onDidChangeState(event => {
switch (event.type) {
case 'output':
this.uiProvider.sendMessage({
type: 'terminalOutput',
terminalId: this.terminal.id,
data: event.data,
outputType: event.stream === 'stderr' ? 'error' : 'output'
});
break;
}
});
}
}
// Initialize terminal UI
const terminalUIProvider = new TerminalUIProvider(terminalManager);Shell Integration
typescript
// Shell integration utilities
class ShellIntegration {
// Detect available shells
static async getAvailableShells(): Promise<ShellInfo[]> {
const shells: ShellInfo[] = [];
if (process.platform === 'win32') {
// Windows shells
const windowsShells = [
{ name: 'Command Prompt', path: 'cmd.exe', type: 'cmd' },
{ name: 'PowerShell', path: 'powershell.exe', type: 'powershell' },
{ name: 'PowerShell Core', path: 'pwsh.exe', type: 'pwsh' },
{ name: 'Git Bash', path: 'C:\\Program Files\\Git\\bin\\bash.exe', type: 'bash' }
];
for (const shell of windowsShells) {
if (await this.isShellAvailable(shell.path)) {
shells.push(shell);
}
}
} else {
// Unix-like shells
const unixShells = [
{ name: 'Bash', path: '/bin/bash', type: 'bash' },
{ name: 'Zsh', path: '/bin/zsh', type: 'zsh' },
{ name: 'Fish', path: '/usr/local/bin/fish', type: 'fish' },
{ name: 'Dash', path: '/bin/dash', type: 'dash' }
];
for (const shell of unixShells) {
if (await this.isShellAvailable(shell.path)) {
shells.push(shell);
}
}
}
return shells;
}
private static async isShellAvailable(path: string): Promise<boolean> {
try {
const { access } = require('fs').promises;
await access(path);
return true;
} catch {
return false;
}
}
// Get shell-specific command formatting
static formatCommand(command: string, shellType: string): string {
switch (shellType) {
case 'powershell':
case 'pwsh':
// PowerShell specific formatting
return command;
case 'cmd':
// Command Prompt specific formatting
return command;
case 'bash':
case 'zsh':
case 'fish':
default:
// Unix shell formatting
return command;
}
}
// Get shell-specific environment setup
static getShellEnvironment(shellType: string): { [key: string]: string } {
const env: { [key: string]: string } = {};
switch (shellType) {
case 'powershell':
case 'pwsh':
env.PSModulePath = process.env.PSModulePath || '';
break;
case 'bash':
case 'zsh':
env.HISTSIZE = '10000';
env.HISTFILESIZE = '20000';
break;
}
return env;
}
}Interfaces
typescript
interface TerminalOptions {
name?: string;
shell?: string;
shellArgs?: string[];
cwd?: string;
env?: { [key: string]: string };
termType?: string;
followWorkspace?: boolean;
createdAt?: number;
}
interface ExecuteOptions {
terminalId?: string;
cwd?: string;
env?: { [key: string]: string };
timeout?: number;
shell?: string;
}
interface TerminalInfo {
id: string;
name: string;
isRunning: boolean;
shell: string;
cwd: string;
pid?: number;
createdAt: number;
}
interface ExecutionInfo {
id: string;
command: string;
isCompleted: boolean;
exitCode: number | null;
output: string;
startTime: number;
duration: number | null;
}
interface ShellInfo {
name: string;
path: string;
type: string;
}
type TerminalEvent = {
type: 'terminalCreated';
terminal: TerminalInstance;
} | {
type: 'terminalClosed';
terminalId: string;
} | {
type: 'terminalActivated';
terminal: TerminalInstance;
};
type TerminalInstanceEvent = {
type: 'initialized';
} | {
type: 'output';
data: string;
stream: 'stdout' | 'stderr';
} | {
type: 'input';
data: string;
} | {
type: 'exit';
code: number;
signal: string;
} | {
type: 'error';
error: Error;
} | {
type: 'commandExecuted';
command: string;
executionId: string;
} | {
type: 'workingDirectoryChanged';
path: string;
} | {
type: 'cleared';
};
type ExecutionEvent = {
type: 'output';
data: string;
} | {
type: 'completed';
exitCode: number;
duration: number;
};API Reference
Core Interfaces
typescript
interface TerminalAPI {
// Terminal management
createTerminal(options?: TerminalOptions): Promise<TerminalInstance>;
getTerminal(terminalId: string): TerminalInstance | null;
getAllTerminals(): TerminalInstance[];
getActiveTerminal(): TerminalInstance | null;
setActiveTerminal(terminalId: string): boolean;
closeTerminal(terminalId: string): Promise<boolean>;
// Command execution
executeCommand(command: string, options?: ExecuteOptions): Promise<ExecutionResult>;
executeCommandWithOutput(command: string, options?: ExecuteOptions): Promise<string>;
// Events
onDidChangeTerminal(listener: (event: TerminalEvent) => void): TraeAPI.Disposable;
}
interface TerminalInstance {
readonly id: string;
readonly options: TerminalOptions;
// Command execution
executeCommand(command: string, options?: ExecuteOptions): Promise<ExecutionResult>;
sendInput(input: string): void;
sendKey(key: string): void;
// Terminal control
changeWorkingDirectory(path: string): Promise<boolean>;
clear(): void;
// Information
getOutput(): string;
getInfo(): TerminalInfo;
// Events
onDidChangeState(listener: (event: TerminalInstanceEvent) => void): TraeAPI.Disposable;
// Lifecycle
dispose(): Promise<void>;
}Best Practices
- Resource Management: Properly dispose of terminals and execution results
- Error Handling: Handle terminal process errors gracefully
- Performance: Use output buffering and efficient event handling
- Security: Validate commands and sanitize input
- User Experience: Provide clear feedback for terminal operations
- Cross-platform: Support different shells and platforms
- Integration: Integrate with workspace and project settings
- Accessibility: Ensure terminal UI is accessible
Related APIs
- Commands API - For terminal command registration
- Workspace API - For workspace integration
- UI API - For terminal UI components
- Settings API - For terminal configuration