feat: switched to native text sanitization, removed Bun.stripANSI

- Added `sanitizeText` function to pi-natives that strips ANSI escape sequences, removes control characters and lone surrogates, and normalizes line endings.
- Moved `sanitizeText` function from `@oh-my-pi/pi-utils` to `@oh-my-pi/pi-natives` for better code organization and native performance.
- Added line length clamping (4000 characters) to bash and Python execution output to prevent excessively long lines.
- Replaced internal `#normalizeOutput` methods with `sanitizeText` utility function in bash and Python execution components.
- Fixed bash interactive tool to gracefully handle malformed output chunks by normalizing them with `sanitizeText`.
- Simplified documentation by removing WASM terminology from package descriptions and comments.
This commit is contained in:
can1357
2026-02-14 03:25:13 +01:00
parent 2aa156060b
commit 2e33e436b4
19 changed files with 290 additions and 135 deletions
@@ -1,6 +1,8 @@
/**
* Component for displaying bash command execution with streaming output.
*/
import { sanitizeText } from "@oh-my-pi/pi-natives";
import { Container, Loader, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
import { getSymbolTheme, theme } from "../../modes/theme/theme";
import type { TruncationMeta } from "../../tools/output-meta";
@@ -10,6 +12,7 @@ import { truncateToVisualLines } from "./visual-truncate";
// Preview line limit when not expanded (matches tool execution behavior)
const PREVIEW_LINES = 20;
const MAX_DISPLAY_LINE_CHARS = 4000;
export class BashExecutionComponent extends Container {
#outputLines: string[] = [];
@@ -73,13 +76,15 @@ export class BashExecutionComponent extends Container {
}
appendOutput(chunk: string): void {
const clean = this.#normalizeOutput(chunk);
const clean = sanitizeText(chunk);
// Append to output lines
const newLines = clean.split("\n");
const newLines = clean.split("\n").map(line => this.#clampDisplayLine(line));
if (this.#outputLines.length > 0 && newLines.length > 0) {
// Append first chunk to last line (incomplete line continuation)
this.#outputLines[this.#outputLines.length - 1] += newLines[0];
this.#outputLines[this.#outputLines.length - 1] = this.#clampDisplayLine(
`${this.#outputLines[this.#outputLines.length - 1]}${newLines[0]}`,
);
this.#outputLines.push(...newLines.slice(1));
} else {
this.#outputLines.push(...newLines);
@@ -184,15 +189,17 @@ export class BashExecutionComponent extends Container {
}
}
#normalizeOutput(text: string): string {
// Strip ANSI codes and normalize line endings
// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand
return Bun.stripANSI(text).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
#clampDisplayLine(line: string): string {
if (line.length <= MAX_DISPLAY_LINE_CHARS) {
return line;
}
const omitted = line.length - MAX_DISPLAY_LINE_CHARS;
return `${line.slice(0, MAX_DISPLAY_LINE_CHARS)}… [${omitted} chars omitted]`;
}
#setOutput(output: string): void {
const clean = this.#normalizeOutput(output);
this.#outputLines = clean ? clean.split("\n") : [];
const clean = sanitizeText(output);
this.#outputLines = clean ? clean.split("\n").map(line => this.#clampDisplayLine(line)) : [];
}
/**