/**
 * olam enter <world> — Open terminal into a world's Claude session.
 *
 * Two modes:
 *
 * 1. **Default** (no --start-claude): print the shell command the operator
 *    can run to attach an interactive terminal to the world. Optionally
 *    `--exec` runs that command in-process instead of printing it.
 *
 * 2. **--start-claude** (Phase C/C4): open a tmux session inside the world,
 *    pre-fill the Claude prompt with `--task <text>` if supplied, then
 *    attach. Task text is delivered to tmux's paste buffer via STDIN
 *    (`docker exec -i ... tmux load-buffer -`) and pasted with
 *    `tmux paste-buffer` — LITERAL bytes, no shell evaluation. T17
 *    mitigation. See {@link buildStartClaudeCommands} for the contract.
 */
import { execSync } from 'node:child_process';
import pc from 'picocolors';
import { loadContext } from '../context.js';
import { printError } from '../output.js';
/** Same SAFE_IDENT contract as world/tmux-supervisor.ts and
 *  world/bootstrap-runner.ts — caller-side schema validates upstream;
 *  we re-check defensively at this seam. */
const SAFE_IDENT = /^[a-z0-9][a-z0-9-]{0,63}$/;
/**
 * Build the `olam enter --start-claude` command sequence.
 *
 * SECURITY INVARIANT (T17 mitigation, mirroring C4 spec):
 *
 * When `task` is supplied, the task text is delivered to tmux's paste-buffer
 * via STDIN of `docker exec -i <c> tmux load-buffer -`. It is NEVER passed
 * as a shell argument and NEVER interpolated into any command-line position.
 * `tmux paste-buffer` writes buffer contents literally — without shell
 * evaluation — so a `task` like `"Fix bug; rm -rf /tmp"` arrives in Claude's
 * prompt area as literal text. The user reviews then presses Enter manually
 * (no newline is appended by this builder).
 *
 * Mirrors the same trust class as `BootstrapStep` (bootstrap-runner.ts) and
 * `SupervisedRepo.start` (tmux-supervisor.ts). Do not change without
 * security review — replacing the stdin path with `tmux send-keys
 * "$task"` (or any shell-interpolated form) re-opens the T17 RCE.
 *
 * @throws Error if `containerName` or `sessionName` fail the defensive
 *   regex guard (these become unquoted argv tokens in the returned
 *   commands; metachar injection there bypasses the stdin contract).
 */
export function buildStartClaudeCommands(containerName, sessionName, task) {
    if (!SAFE_IDENT.test(containerName)) {
        throw new Error(`containerName "${containerName}" must match ${SAFE_IDENT} (defensive guard)`);
    }
    if (!SAFE_IDENT.test(sessionName)) {
        throw new Error(`sessionName "${sessionName}" must match ${SAFE_IDENT} (defensive guard)`);
    }
    if (task !== undefined && task.includes('\0')) {
        // CP3 audit ADV-C4-001 closure: tmux paste-buffer's behavior on embedded
        // NUL is implementation-defined (older tmux truncates at first NUL,
        // silently submitting only the prefix). Reject early with a clear error.
        throw new Error('task contains a NUL byte; tmux paste-buffer truncates on NUL — re-encode or remove the byte');
    }
    const steps = [];
    // 1a. Open or attach to the per-world tmux session. With `-A`, if the
    //    session already exists (e.g. C1's supervisor opened it with repo
    //    windows), the -n/-cmd args are IGNORED — so step 1b ensures the
    //    `claude` window exists idempotently regardless of attach-vs-create.
    steps.push({
        description: 'Open or attach to per-world tmux session',
        command: `docker exec ${containerName} tmux new-session -A -d ` +
            `-s ${sessionName} -n claude 'claude'`,
    });
    // 1b. Idempotently ensure the claude window exists. CP3 audit ADV-C4-002
    //    closure: -A attach on an existing session without a `claude` window
    //    would silently no-op step 1's window arg, then step 3's paste-buffer
    //    target would fail mid-flight with the buffer already loaded.
    steps.push({
        description: 'Ensure claude window exists in the session',
        command: `docker exec ${containerName} sh -c ` +
            `"tmux new-window -t ${sessionName} -n claude 'claude' 2>/dev/null || true"`,
    });
    // 2. (Conditional) Deliver task text to the paste buffer via STDIN.
    //    NOTE — this is the load-bearing security primitive: task text is
    //    NEVER part of the argv. tmux load-buffer reads from stdin in
    //    literal-byte mode.
    if (task !== undefined && task.length > 0) {
        steps.push({
            description: 'Load task text into tmux paste buffer via STDIN (literal — no shell eval)',
            command: `docker exec -i ${containerName} tmux load-buffer -`,
            stdin: task,
        });
        steps.push({
            description: 'Paste buffer into the claude window (no Enter — user reviews then submits)',
            command: `docker exec ${containerName} tmux paste-buffer ` +
                `-t ${sessionName}:claude`,
        });
    }
    // 3. Attach an interactive terminal so the operator sees the session.
    steps.push({
        description: 'Attach interactive terminal to the session',
        command: `docker exec -it ${containerName} tmux attach -t ${sessionName}`,
    });
    return steps;
}
export function registerEnter(program) {
    program
        .command('enter')
        .description('Open terminal to a world')
        .argument('<world>', 'World ID')
        .option('--exec', 'Execute the command directly instead of printing it')
        .option('--start-claude', 'Open a Claude session inside the world via tmux (literal-paste task contract)')
        .option('--task <text>', 'Optional task text to pre-fill Claude with (delivered via tmux load-buffer; never shell-evaluated)')
        .action(async (worldId, opts) => {
        const { ctx, error } = await loadContext();
        if (!ctx) {
            printError(error?.message ?? 'Olam is not configured. Run `olam init` first.');
            process.exitCode = 1;
            return;
        }
        // D3: warn-not-block version pin check (once per world per session).
        const worldMeta = ctx.worldManager.getWorld(worldId);
        if (worldMeta) {
            const { checkVersionPin } = await import('@olam/core/src/world/version-pin.js');
            checkVersionPin(worldId, worldMeta.workspacePath);
            // ADR 045 — surface the per-world Claude home so the operator
            // knows which account this world is bound to.
            if (worldMeta.claudeHome) {
                console.log(pc.dim(`# claude-home: ${worldMeta.claudeHome} (per-world Claude Code instance, ADR 045)`));
            }
        }
        const computeWorld = await ctx.computeProvider.getWorld(worldId);
        if (!computeWorld) {
            printError(`World "${worldId}" not found.`);
            process.exitCode = 1;
            return;
        }
        const connectionInfo = await computeWorld.getConnectionInfo();
        // --start-claude path: tmux session with literal-paste task contract.
        if (opts.startClaude) {
            if (computeWorld.provider !== 'docker') {
                printError(`--start-claude is only supported on the docker provider today (got "${computeWorld.provider}")`);
                process.exitCode = 1;
                return;
            }
            const containerName = connectionInfo.containerId;
            const sessionName = `olam-${worldId}`;
            let steps;
            try {
                steps = buildStartClaudeCommands(containerName, sessionName, opts.task);
            }
            catch (err) {
                printError(err instanceof Error ? err.message : String(err));
                process.exitCode = 1;
                return;
            }
            if (opts.exec) {
                for (const step of steps) {
                    try {
                        execSync(step.command, {
                            stdio: step.stdin !== undefined
                                ? ['pipe', 'inherit', 'inherit']
                                : 'inherit',
                            input: step.stdin,
                        });
                    }
                    catch (err) {
                        printError(`Step "${step.description}" failed: ` +
                            (err instanceof Error ? err.message : String(err)));
                        process.exitCode = 1;
                        return;
                    }
                }
                return;
            }
            console.log('Run these commands in order to enter the world:\n');
            // CP3 audit ADV-C4-004 closure: do NOT apply pc.bold to commands in
            // --start-claude print mode — these lines are meant to be copy-pasted
            // into a shell or saved to a script. With FORCE_COLOR=1 set in the
            // operator's env, pc.bold injects ANSI escapes that break the saved
            // script ("command not found: \e[1mdocker"). Plain text only.
            for (const step of steps) {
                console.log(`  ${pc.dim(`# ${step.description}`)}`);
                if (step.stdin !== undefined) {
                    // Print form: shell-quote the task and pipe via printf. Same
                    // security boundary — outer single-quotes neutralize all shell
                    // metacharacters inside the payload.
                    const escaped = step.stdin.replace(/'/g, "'\\''");
                    console.log(`  printf '%s' '${escaped}' | ${step.command}`);
                }
                else {
                    console.log(`  ${step.command}`);
                }
            }
            return;
        }
        // Legacy default path — unchanged.
        const { getEnterCommand } = await import('@olam/core/src/orchestrator/enter.js');
        const result = getEnterCommand(worldId, connectionInfo.containerId, computeWorld.provider, connectionInfo.ssh?.host);
        if (!result.command) {
            printError(result.instructions);
            process.exitCode = 1;
            return;
        }
        if (opts.exec) {
            try {
                execSync(result.command, { stdio: 'inherit' });
            }
            catch (err) {
                printError(err instanceof Error ? err.message : `Command failed: ${result.command}`);
                process.exitCode = 1;
            }
            return;
        }
        console.log('Run this command to enter the world:\n');
        console.log(`  ${pc.bold(result.command)}`);
        const containerName = `olam-${worldId}-devbox`;
        console.log(`\n${pc.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`);
    });
}
//# sourceMappingURL=enter.js.map