/**
 * Bash command execution with streaming support and cancellation.
 *
 * This module provides a unified bash execution implementation used by:
 * - AgentSession.executeBash() for interactive and RPC modes
 * - Direct calls from modes that need bash execution
 */
import { randomBytes } from "node:crypto";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import stripAnsi from "strip-ansi";
import { sanitizeBinaryOutput } from "../utils/shell.js";
import { OutputAccumulator } from "./tools/output-accumulator.js";
const USER_BASH_ARTIFACT_LINE_THRESHOLD = 300;
const USER_BASH_ARTIFACT_PREVIEW_LINES = 300;
function createBashArtifactPaths() {
    const day = new Date().toISOString().slice(0, 10);
    const id = `art_bash_${day.replaceAll("-", "")}_${randomBytes(4).toString("hex")}`;
    const relativeBase = `.threadwell/artifacts/${day}/${id}`;
    return {
        id,
        outputPath: `${relativeBase}.txt`,
        metadataPath: `${relativeBase}.json`,
    };
}
async function writeBashArtifactMetadata(metadata) {
    await writeFile(join(metadata.cwd, metadata.metadataPath), `${JSON.stringify(metadata, null, "\t")}\n`, "utf-8");
}
// ============================================================================
// Implementation
// ============================================================================
/**
 * Execute a bash command using custom BashOperations.
 * Used for remote execution (SSH, containers, etc.).
 */
export async function executeBashWithOperations(command, cwd, operations, options) {
    const artifactPaths = options?.artifactLargeOutputs ? createBashArtifactPaths() : undefined;
    const output = new OutputAccumulator({
        tempFilePrefix: "threadwell-bash",
        maxLines: artifactPaths ? USER_BASH_ARTIFACT_PREVIEW_LINES : undefined,
        outputFilePath: artifactPaths ? join(cwd, artifactPaths.outputPath) : undefined,
        outputDisplayPath: artifactPaths?.outputPath,
        persistLines: artifactPaths ? USER_BASH_ARTIFACT_LINE_THRESHOLD : undefined,
    });
    const decoder = new TextDecoder();
    const finalizeOutput = async (cancelled) => {
        output.finish();
        const snapshot = output.snapshot({ persistIfTruncated: true });
        await output.closeTempFile();
        if (artifactPaths && snapshot.truncation.truncated && snapshot.fullOutputPath) {
            await writeBashArtifactMetadata({
                id: artifactPaths.id,
                kind: "tool_output",
                tool: "bash",
                cwd,
                command: options?.artifactCommand ?? command,
                createdAt: new Date().toISOString(),
                outputPath: artifactPaths.outputPath,
                metadataPath: artifactPaths.metadataPath,
                bytes: snapshot.truncation.totalBytes,
                lines: snapshot.truncation.totalLines,
                previewPolicy: "tail",
                previewLines: snapshot.truncation.outputLines,
            });
        }
        return {
            output: snapshot.content,
            exitCode: cancelled ? undefined : 0,
            cancelled,
            truncated: snapshot.truncation.truncated,
            fullOutputPath: snapshot.fullOutputPath,
        };
    };
    const onData = (data) => {
        const text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\r/g, "");
        output.append(Buffer.from(text, "utf-8"));
        options?.onChunk?.(text);
    };
    try {
        const result = await operations.exec(command, cwd, {
            onData,
            signal: options?.signal,
        });
        const cancelled = options?.signal?.aborted ?? false;
        const bashResult = await finalizeOutput(cancelled);
        return { ...bashResult, exitCode: cancelled ? undefined : (result.exitCode ?? undefined) };
    }
    catch (err) {
        if (options?.signal?.aborted) {
            return finalizeOutput(true);
        }
        await output.closeTempFile();
        throw err;
    }
}
//# sourceMappingURL=bash-executor.js.map