"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SHELL_COMMAND_ALLOWLIST_PRESETS = void 0;
exports.buildAutomationPrompt = buildAutomationPrompt;
exports.runAutomationScript = runAutomationScript;
exports.prepareAutomationRun = prepareAutomationRun;
exports.listShellCommandAllowlistPresets = listShellCommandAllowlistPresets;
exports.matchShellCommandPreset = matchShellCommandPreset;
const child_process_1 = require("child_process");
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const capabilities_1 = require("../permissions/capabilities");
const capabilityAudit_1 = require("../permissions/capabilityAudit");
const MAX_SCRIPT_OUTPUT = 50_000;
const AUTOMATION_SCRIPT_CAPABILITIES = ['arbitrary-shell', 'background-autonomous-jobs'];
exports.SHELL_COMMAND_ALLOWLIST_PRESETS = [
    { id: 'git-read-status', label: 'Git read-only status and diffs', pattern: /^git\s+(status(?:\s+--short)?|diff\s+(--stat|--name-only)|log\s+--oneline(?:\s+-\d+)?)$/i, examples: ['git status --short', 'git diff --stat', 'git diff --name-only', 'git log --oneline -5'] },
    { id: 'file-discovery', label: 'Read-only file discovery', pattern: /^(rg\s+--files(?:\s+(?!.*\.\.)[-\w./*]+)?|dir(?:\s+(?!.*\.\.)[-\w./*]+)?|Get-ChildItem(?:\s+(?!.*\.\.)[-\w./*]+)?)$/i, examples: ['rg --files', 'dir', 'Get-ChildItem'] },
    { id: 'tool-version', label: 'Tool version checks', pattern: /^(node|npm|git)\s+(--version|-v)$/i, examples: ['node --version', 'npm --version', 'git --version'] },
    { id: 'project-validation', label: 'Project validation scripts', pattern: /^npm\s+run\s+(typecheck|build|smoke:ui)$/i, examples: ['npm run typecheck', 'npm run build', 'npm run smoke:ui'] },
    { id: 'dependency-audit', label: 'Read-only dependency audit', pattern: /^npm\s+audit(?:\s+--audit-level=(low|moderate|high|critical))?(?:\s+--json)?$/i, examples: ['npm audit', 'npm audit --audit-level=moderate', 'npm audit --audit-level=high --json'] },
];
async function buildAutomationPrompt(job, scriptOutput = '') {
    if (!scriptOutput.trim())
        return job.prompt;
    return [
        job.prompt,
        '',
        'Script context:',
        scriptOutput.trim(),
    ].join('\n');
}
async function runAutomationScript(command, cwd, timeoutMs = 30_000) {
    return new Promise((resolve) => {
        (0, child_process_1.exec)(command, { cwd, timeout: timeoutMs, maxBuffer: MAX_SCRIPT_OUTPUT * 2 }, (error, stdout, stderr) => {
            const output = [stdout, stderr].filter(Boolean).join('\n').slice(0, MAX_SCRIPT_OUTPUT);
            if (error && !output)
                resolve(`Script failed: ${error.message}`);
            else
                resolve(output || '(script produced no output)');
        });
    });
}
async function prepareAutomationRun(projectDir, job, now = new Date(), policy = {}) {
    const scriptOutput = job.scriptCommand ? await runAutomationScriptWithPolicy(job.scriptCommand, projectDir, job.id, { ...policy, now }) : '';
    const prompt = await buildAutomationPrompt(job, scriptOutput);
    const outputPath = path.join(projectDir, '.harness', 'automations', 'output', job.id, `${safeTimestamp(now)}.md`);
    await fs.mkdir(path.dirname(outputPath), { recursive: true });
    await fs.writeFile(outputPath, `# ${job.name}\n\n${prompt}\n`, 'utf-8');
    return { jobId: job.id, prompt, scriptOutput, outputPath };
}
async function runAutomationScriptWithPolicy(command, cwd, jobId, policy) {
    const grants = policy.grants ?? [];
    const denied = AUTOMATION_SCRIPT_CAPABILITIES
        .map((capabilityId) => (0, capabilities_1.evaluateCapabilityGrant)(capabilityId, grants, { now: policy.now, killSwitchActive: policy.killSwitchActive }))
        .filter((evaluation) => evaluation.decision !== 'allow');
    if (denied.length > 0) {
        const reason = denied.map((evaluation) => `${evaluation.capabilityId}: ${evaluation.reason}`).join('; ');
        await (0, capabilityAudit_1.appendCapabilityAuditEvent)(cwd, { type: 'automation_script.denied', jobId, command, reason }, policy.now);
        return denied.map((evaluation) => `Script blocked by ${evaluation.capabilityId}: ${evaluation.reason}`).join('\n');
    }
    // Two-stage allowlist: built-in preset OR an operator-approved entry on
    // an active arbitrary-shell grant. Both paths are equally audited so a
    // forensics pass can tell which gate admitted the command.
    const preset = matchShellCommandPreset(command);
    if (preset) {
        await (0, capabilityAudit_1.appendCapabilityAuditEvent)(cwd, { type: 'automation_script.allowed', jobId, command, presetId: preset.id }, policy.now);
        return runAutomationScript(command, cwd);
    }
    const grantMatch = (0, capabilities_1.commandMatchesGrantAllowlist)('arbitrary-shell', grants, command, policy.now);
    if (grantMatch.matched) {
        await (0, capabilityAudit_1.appendCapabilityAuditEvent)(cwd, { type: 'automation_script.allowed', jobId, command, presetId: `grant:${grantMatch.grantId ?? 'unknown'}:${grantMatch.pattern ?? ''}` }, policy.now);
        return runAutomationScript(command, cwd);
    }
    const reason = 'Command does not match an allowlist preset or any grant-defined pattern.';
    await (0, capabilityAudit_1.appendCapabilityAuditEvent)(cwd, { type: 'automation_script.denied', jobId, command, reason }, policy.now);
    return `Script blocked by command allowlist: ${reason}`;
}
function listShellCommandAllowlistPresets() {
    return exports.SHELL_COMMAND_ALLOWLIST_PRESETS.map(({ id, label, examples }) => ({ id, label, examples: [...examples] }));
}
function matchShellCommandPreset(command) {
    const normalized = command.trim().replace(/\s+/g, ' ');
    return exports.SHELL_COMMAND_ALLOWLIST_PRESETS.find((preset) => preset.pattern.test(normalized)) ?? null;
}
function safeTimestamp(value) {
    return value.toISOString().replace(/[:.]/g, '-');
}
//# sourceMappingURL=runner.js.map