"use strict";
// =============================================================================
// Pure Agent - Code Execution Tool
// Ported from Hermes tools/code_execution_tool.py
// =============================================================================
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 });
const child_process_1 = require("child_process");
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const tool_registry_1 = require("./tool-registry");
const result_types_1 = require("./result-types");
const logger_1 = require("./logger");
const utils_1 = require("./utils");
const constants_1 = require("./constants");
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const DEFAULT_TIMEOUT = 300; // 5 minutes
const MAX_STDOUT_BYTES = 50_000;
const MAX_STDERR_BYTES = 10_000;
const SAFE_ENV_PREFIXES = [
    'PATH', 'HOME', 'USER', 'LANG', 'LC_', 'TERM', 'TMPDIR', 'TMP', 'TEMP',
    'SHELL', 'LOGNAME', 'XDG_', 'PYTHONPATH', 'VIRTUAL_ENV', 'CONDA',
];
const SECRET_SUBSTRINGS = [
    'KEY', 'TOKEN', 'SECRET', 'PASSWORD', 'CREDENTIAL', 'PASSWD', 'AUTH', 'DSN', 'WEBHOOK',
];
const WINDOWS_ESSENTIAL_VARS = new Set([
    'SYSTEMROOT', 'SYSTEMDRIVE', 'WINDIR', 'COMSPEC', 'PATHEXT', 'OS',
    'PROCESSOR_ARCHITECTURE', 'PUBLIC', 'ALLUSERSPROFILE', 'PROGRAMDATA',
    'PROGRAMFILES', 'APPDATA', 'LOCALAPPDATA', 'USERPROFILE', 'USERNAME',
    'HOMEDRIVE', 'HOMEPATH', 'COMPUTERNAME',
]);
// ---------------------------------------------------------------------------
// Environment scrubbing
// ---------------------------------------------------------------------------
function scrubEnv() {
    const source = process.env;
    const result = {};
    for (const [key, value] of Object.entries(source)) {
        if (value === undefined)
            continue;
        const upper = key.toUpperCase();
        // Block secrets
        if (SECRET_SUBSTRINGS.some(s => upper.includes(s)))
            continue;
        // Allow safe prefixes
        if (SAFE_ENV_PREFIXES.some(p => upper.startsWith(p))) {
            result[key] = value;
            continue;
        }
        // Allow Windows essentials
        if ((0, constants_1.isWindows)() && WINDOWS_ESSENTIAL_VARS.has(upper)) {
            result[key] = value;
            continue;
        }
    }
    return result;
}
// ---------------------------------------------------------------------------
// Execute code
// ---------------------------------------------------------------------------
async function executeCode(code, language, timeout, cwd, signal) {
    const startTime = Date.now();
    const safeEnv = scrubEnv();
    let command;
    let args;
    let tmpFile = null;
    switch (language.toLowerCase()) {
        case 'python':
        case 'python3': {
            // Write code to temp file
            tmpFile = path.join(os.tmpdir(), `pa_exec_${Date.now()}.py`);
            fs.writeFileSync(tmpFile, code, 'utf-8');
            command = (0, constants_1.isWindows)() ? 'python' : 'python3';
            args = [tmpFile];
            break;
        }
        case 'javascript':
        case 'js': {
            tmpFile = path.join(os.tmpdir(), `pa_exec_${Date.now()}.js`);
            fs.writeFileSync(tmpFile, code, 'utf-8');
            command = 'node';
            args = [tmpFile];
            break;
        }
        case 'bash':
        case 'sh': {
            command = (0, constants_1.isWindows)() ? 'cmd.exe' : 'bash';
            args = (0, constants_1.isWindows)() ? ['/c', code] : ['-c', code];
            break;
        }
        default: {
            // Default: try as shell command
            command = (0, constants_1.isWindows)() ? 'cmd.exe' : 'bash';
            args = (0, constants_1.isWindows)() ? ['/c', code] : ['-c', code];
        }
    }
    return new Promise((resolve) => {
        const effectiveTimeout = Math.min(timeout * 1000, DEFAULT_TIMEOUT * 1000);
        const proc = (0, child_process_1.spawn)(command, args, {
            cwd,
            env: { ...process.env, ...safeEnv },
            timeout: effectiveTimeout,
            windowsHide: true,
            stdio: ['pipe', 'pipe', 'pipe'],
        });
        let stdout = '';
        let stderr = '';
        proc.stdout?.on('data', (data) => {
            stdout += data.toString('utf-8');
            if (stdout.length > MAX_STDOUT_BYTES) {
                stdout = (0, utils_1.truncate)(stdout, MAX_STDOUT_BYTES).text;
                proc.kill();
            }
        });
        proc.stderr?.on('data', (data) => {
            stderr += data.toString('utf-8');
            if (stderr.length > MAX_STDERR_BYTES) {
                stderr = (0, utils_1.truncate)(stderr, MAX_STDERR_BYTES).text;
            }
        });
        const onAbort = () => proc.kill('SIGTERM');
        signal.addEventListener('abort', onAbort, { once: true });
        proc.on('close', (code) => {
            signal.removeEventListener('abort', onAbort);
            // Cleanup temp file
            if (tmpFile) {
                try {
                    fs.unlinkSync(tmpFile);
                }
                catch { /* ignore */ }
            }
            resolve({
                stdout: stripAnsi(stdout.trim()),
                stderr: stripAnsi(stderr.trim()),
                exitCode: code ?? -1,
                duration: Math.round((Date.now() - startTime) / 1000),
            });
        });
        proc.on('error', (err) => {
            signal.removeEventListener('abort', onAbort);
            if (tmpFile) {
                try {
                    fs.unlinkSync(tmpFile);
                }
                catch { /* ignore */ }
            }
            resolve({
                stdout: '',
                stderr: `Process error: ${err.message}`,
                exitCode: -1,
                duration: Math.round((Date.now() - startTime) / 1000),
            });
        });
    });
}
// ---------------------------------------------------------------------------
// ANSI stripping
// ---------------------------------------------------------------------------
function stripAnsi(text) {
    return text.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
}
// ---------------------------------------------------------------------------
// Tool handler
// ---------------------------------------------------------------------------
async function executeCodeHandler(args, context) {
    const { code, language = 'python', timeout = DEFAULT_TIMEOUT } = args;
    if (!code)
        return (0, result_types_1.toolError)('Missing required parameter: code');
    (0, logger_1.getLogger)().info(`execute_code: language=${language}, ${code.length} chars`);
    try {
        const result = await executeCode(code, language, timeout, context.workingDir, context.signal);
        return (0, result_types_1.toolResult)({
            status: result.exitCode === 0 ? 'success' : 'error',
            stdout: result.stdout,
            stderr: result.stderr,
            exit_code: result.exitCode,
            duration_seconds: result.duration,
        });
    }
    catch (err) {
        return (0, result_types_1.toolError)(`Execution failed: ${err.message}`);
    }
}
// ---------------------------------------------------------------------------
// Registration
// ---------------------------------------------------------------------------
tool_registry_1.registry.register({
    name: 'execute_code',
    toolset: 'code_execution',
    schema: {
        description: 'Execute code in a sandboxed environment. Supports Python, JavaScript, and Bash. The code runs in a child process with a scrubbed environment (secrets removed).',
        parameters: {
            type: 'object',
            properties: {
                code: {
                    type: 'string',
                    description: 'The code to execute',
                },
                language: {
                    type: 'string',
                    enum: ['python', 'python3', 'javascript', 'js', 'bash', 'sh'],
                    description: 'Language/runtime to use. Default: python',
                    default: 'python',
                },
                timeout: {
                    type: 'number',
                    description: 'Timeout in seconds (max 300). Default: 300',
                    default: 300,
                },
            },
            required: ['code'],
        },
    },
    handler: executeCodeHandler,
    isAsync: true,
    description: 'Execute code in sandbox',
    emoji: '🐍',
    maxResultSizeChars: 100000,
});
//# sourceMappingURL=code-execution.js.map