feat: added terminal error capturing and automated fix prompt

This commit is contained in:
Anirban Kar
2024-12-17 21:19:43 +05:30
parent 42bde1cae4
commit d327cfea29
8 changed files with 474 additions and 213 deletions

View File

@@ -60,7 +60,9 @@ export class BoltShell {
#webcontainer: WebContainer | undefined;
#terminal: ITerminal | undefined;
#process: WebContainerProcess | undefined;
executionState = atom<{ sessionId: string; active: boolean; executionPrms?: Promise<any> } | undefined>();
executionState = atom<
{ sessionId: string; active: boolean; executionPrms?: Promise<any>; abort?: () => void } | undefined
>();
#outputStream: ReadableStreamDefaultReader<string> | undefined;
#shellInputStream: WritableStreamDefaultWriter<string> | undefined;
@@ -93,13 +95,17 @@ export class BoltShell {
return this.#process;
}
async executeCommand(sessionId: string, command: string): Promise<ExecutionResult> {
async executeCommand(sessionId: string, command: string, abort?: () => void): Promise<ExecutionResult> {
if (!this.process || !this.terminal) {
return undefined;
}
const state = this.executionState.get();
if (state?.active && state.abort) {
state.abort();
}
/*
* interrupt the current execution
* this.#shellInputStream?.write('\x03');
@@ -115,11 +121,19 @@ export class BoltShell {
//wait for the execution to finish
const executionPromise = this.getCurrentExecutionResult();
this.executionState.set({ sessionId, active: true, executionPrms: executionPromise });
this.executionState.set({ sessionId, active: true, executionPrms: executionPromise, abort });
const resp = await executionPromise;
this.executionState.set({ sessionId, active: false });
if (resp) {
try {
resp.output = cleanTerminalOutput(resp.output);
} catch (error) {
console.log('failed to format terminal output', error);
}
}
return resp;
}
@@ -215,6 +229,47 @@ export class BoltShell {
}
}
/**
* Cleans and formats terminal output while preserving structure and paths
*/
export function cleanTerminalOutput(input: string): string {
// Step 1: Remove ANSI escape sequences and control characters
const removeAnsi = input.replace(/\u001b\[[0-9;]*[a-zA-Z]/g, '');
// Step 2: Remove terminal control sequences
const removeControl = removeAnsi
.replace(/\[\?[0-9;]*[a-zA-Z]/g, '')
.replace(/\]654;[^\n]*/g, '')
.replace(/\[[0-9]+[GJ]/g, '');
// Step 3: Add newlines at key breakpoints while preserving paths
const formatOutput = removeControl
// Preserve prompt line
.replace(/^([~\/][^\n]+)/m, '$1\n')
// Add newline before command output indicators
.replace(/(?<!^|\n)>/g, '\n>')
// Add newline before error keywords without breaking paths
.replace(/(?<!^|\n|\w)(error|failed|warning|Error|Failed|Warning):/g, '\n$1:')
// Add newline before 'at' in stack traces without breaking paths
.replace(/(?<!^|\n|\/)(at\s+(?!async|sync))/g, '\nat ')
// Ensure 'at async' stays on same line
.replace(/\bat\s+async/g, 'at async');
// Step 4: Clean up whitespace while preserving intentional spacing
const cleanSpaces = formatOutput
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0) // Remove empty lines
.join('\n');
// Step 5: Final cleanup
return cleanSpaces
.replace(/\n{3,}/g, '\n\n') // Replace multiple newlines with double newlines
.replace(/:\s+/g, ': ') // Normalize spacing after colons
.replace(/\s{2,}/g, ' ') // Remove multiple spaces
.trim();
}
export function newBoltShellProcess() {
return new BoltShell();
}