feat: added terminal error capturing and automated fix prompt
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user