"use strict";
// =============================================================================
// Pure Agent - File Safety Enhancements
// Ported from Hermes agent/file_safety.py, tools/path_security.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 });
exports.isBinaryExtension = isBinaryExtension;
exports.isBinaryContent = isBinaryContent;
exports.isWriteDenied = isWriteDenied;
exports.isCredentialFile = isCredentialFile;
exports.hasPathTraversal = hasPathTraversal;
exports.checkFileDedup = checkFileDedup;
exports.clearFileDedup = clearFileDedup;
exports.lintFileContent = lintFileContent;
exports.detectLineEnding = detectLineEnding;
exports.normalizeLineEndings = normalizeLineEndings;
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
// ---------------------------------------------------------------------------
// Write-denied paths (port from Hermes)
// ---------------------------------------------------------------------------
const WRITE_DENIED_PREFIXES = [
    '/etc/', '/boot/', '/usr/lib/systemd/', '/proc/', '/sys/',
    '/dev/', '/run/',
];
const WRITE_DENIED_PATHS = new Set([
    '/etc/passwd', '/etc/shadow', '/etc/sudoers', '/etc/hosts',
]);
const CREDENTIAL_FILE_PATTERNS = [
    /\.env$/i,
    /\.env\.\w+$/i,
    /credentials?\.json$/i,
    /\.ssh\//i,
    /\.gnupg\//i,
    /\.aws\//i,
    /\.kube\//i,
    /token/i,
    /secret/i,
    /\.pem$/i,
    /\.key$/i,
    /\.p12$/i,
    /\.pfx$/i,
];
// ---------------------------------------------------------------------------
// Binary detection
// ---------------------------------------------------------------------------
const BINARY_EXTENSIONS = new Set([
    '.exe', '.dll', '.so', '.dylib', '.bin', '.dat', '.db', '.sqlite', '.sqlite3',
    '.pyc', '.pyo', '.class', '.o', '.obj', '.a', '.lib',
    '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
    '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
    '.mp3', '.mp4', '.wav', '.avi', '.mkv', '.mov', '.flv', '.wmv',
    '.ttf', '.otf', '.woff', '.woff2', '.eot',
    '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.ico', '.tiff',
    '.psd', '.ai', '.sketch',
]);
function isBinaryExtension(filepath) {
    const ext = path.extname(filepath).toLowerCase();
    return BINARY_EXTENSIONS.has(ext);
}
function isBinaryContent(buffer, sampleSize = 8192) {
    const sample = buffer.subarray(0, Math.min(sampleSize, buffer.length));
    let nonTextBytes = 0;
    for (let i = 0; i < sample.length; i++) {
        const byte = sample[i];
        // Null byte or control characters (except \n \r \t \b)
        if (byte === 0 || (byte < 32 && byte !== 10 && byte !== 13 && byte !== 9 && byte !== 8)) {
            nonTextBytes++;
        }
    }
    // If >10% non-text bytes, consider binary
    return nonTextBytes > sample.length * 0.1;
}
// ---------------------------------------------------------------------------
// Path security
// ---------------------------------------------------------------------------
function isWriteDenied(filepath) {
    const normalized = path.resolve(filepath);
    if (WRITE_DENIED_PATHS.has(normalized))
        return true;
    for (const prefix of WRITE_DENIED_PREFIXES) {
        if (normalized.startsWith(prefix))
            return true;
    }
    if (normalized.includes('docker.sock'))
        return true;
    return false;
}
function isCredentialFile(filepath) {
    const normalized = filepath.replace(/\\/g, '/');
    return CREDENTIAL_FILE_PATTERNS.some(pattern => pattern.test(normalized));
}
function hasPathTraversal(filepath) {
    const parts = filepath.split(/[/\\]/);
    return parts.includes('..');
}
const _readHistory = new Map();
const MAX_HISTORY = 500;
const STALE_WARNING_THRESHOLD = 2;
const STALE_BLOCK_THRESHOLD = 4;
function checkFileDedup(filepath, offset, limit) {
    const normalized = path.resolve(filepath);
    const key = `${normalized}:${offset}:${limit}`;
    try {
        const stat = fs.statSync(normalized);
        const mtime = stat.mtimeMs;
        const existing = _readHistory.get(key);
        if (existing && existing.mtime === mtime) {
            existing.count++;
            existing.lastReadAt = Date.now();
            if (existing.count >= STALE_BLOCK_THRESHOLD) {
                return {
                    allowed: false,
                    blocked: true,
                    warning: `File "${path.basename(filepath)}" at offset ${offset} has been read ${existing.count} times without changes. Use search_files or read a different section.`,
                };
            }
            if (existing.count >= STALE_WARNING_THRESHOLD) {
                return {
                    allowed: true,
                    warning: `File "${path.basename(filepath)}" at offset ${offset} has been read ${existing.count} times without changes. Consider using search_files instead.`,
                };
            }
        }
        else {
            _readHistory.set(key, { path: normalized, mtime, count: 1, lastReadAt: Date.now() });
        }
        // Prune old entries
        if (_readHistory.size > MAX_HISTORY) {
            const entries = Array.from(_readHistory.entries());
            entries.sort((a, b) => a[1].lastReadAt - b[1].lastReadAt);
            for (let i = 0; i < entries.length - MAX_HISTORY / 2; i++) {
                _readHistory.delete(entries[i][0]);
            }
        }
        return { allowed: true };
    }
    catch {
        return { allowed: true };
    }
}
function clearFileDedup() {
    _readHistory.clear();
}
function lintFileContent(filepath, content) {
    const ext = path.extname(filepath).toLowerCase();
    const errors = [];
    switch (ext) {
        case '.json': {
            try {
                JSON.parse(content);
            }
            catch (e) {
                errors.push(`JSON: ${e.message}`);
            }
            break;
        }
        case '.yaml':
        case '.yml': {
            // Basic YAML validation (check for obvious issues)
            const lines = content.split('\n');
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i];
                // Tab indentation is invalid in YAML
                if (line.startsWith('\t')) {
                    errors.push(`YAML: Line ${i + 1}: Tab indentation is not allowed`);
                }
            }
            break;
        }
        case '.js':
        case '.mjs':
        case '.cjs':
        case '.ts':
        case '.tsx':
        case '.jsx': {
            // Basic bracket matching
            let braces = 0, parens = 0, brackets = 0;
            let inString = false;
            let stringChar = '';
            let inLineComment = false;
            let inBlockComment = false;
            for (let i = 0; i < content.length; i++) {
                const c = content[i];
                const next = content[i + 1];
                if (inLineComment) {
                    if (c === '\n')
                        inLineComment = false;
                    continue;
                }
                if (inBlockComment) {
                    if (c === '*' && next === '/') {
                        inBlockComment = false;
                        i++;
                    }
                    continue;
                }
                if (inString) {
                    if (c === '\\') {
                        i++;
                        continue;
                    }
                    if (c === stringChar)
                        inString = false;
                    continue;
                }
                if (c === '/' && next === '/') {
                    inLineComment = true;
                    i++;
                    continue;
                }
                if (c === '/' && next === '*') {
                    inBlockComment = true;
                    i++;
                    continue;
                }
                if (c === '"' || c === "'" || c === '`') {
                    inString = true;
                    stringChar = c;
                    continue;
                }
                if (c === '{')
                    braces++;
                else if (c === '}')
                    braces--;
                else if (c === '(')
                    parens++;
                else if (c === ')')
                    parens--;
                else if (c === '[')
                    brackets++;
                else if (c === ']')
                    brackets--;
            }
            if (braces !== 0)
                errors.push(`JS/TS: Unmatched braces (diff: ${braces})`);
            if (parens !== 0)
                errors.push(`JS/TS: Unmatched parentheses (diff: ${parens})`);
            if (brackets !== 0)
                errors.push(`JS/TS: Unmatched brackets (diff: ${brackets})`);
            break;
        }
    }
    return { success: errors.length === 0, errors };
}
// ---------------------------------------------------------------------------
// Line ending detection
// ---------------------------------------------------------------------------
function detectLineEnding(content) {
    const crlfCount = (content.match(/\r\n/g) || []).length;
    const lfOnlyCount = (content.match(/(?<!\r)\n/g) || []).length;
    if (crlfCount > 0 && crlfCount >= lfOnlyCount)
        return '\r\n';
    if (lfOnlyCount > 0)
        return '\n';
    return null;
}
function normalizeLineEndings(content, target) {
    if (target === '\r\n') {
        return content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
    }
    return content.replace(/\r\n/g, '\n');
}
//# sourceMappingURL=file-safety.js.map