import {
  storeFile
} from "../chunk-STPT3SWU.js";
import {
  createFile,
  findFileByContainerPath
} from "../chunk-BGBPOR4J.js";
import {
  listEvents
} from "../chunk-LH3NEWMT.js";
import {
  init_ids,
  newId
} from "../chunk-F4WUVOLE.js";
import "../chunk-HFDLUBWN.js";
import "../chunk-RII4M3J2.js";
import "../chunk-DDJOPRB7.js";
import "../chunk-TLEKKGH7.js";
import "../chunk-KBM3MISV.js";
import "../chunk-2ESYSVXG.js";

// src/sync/container-file-sync.ts
import { createHash } from "crypto";
import path from "path";
init_ids();
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
  ".o",
  ".pyc",
  ".so",
  ".dylib",
  ".bin",
  ".wasm",
  ".mp3",
  ".mp4"
]);
var BLOCKED_PREFIXES = [
  "/proc/",
  "/sys/",
  "/dev/",
  "/etc/",
  "/bin/",
  "/sbin/",
  "/usr/bin/",
  "/usr/sbin/",
  "/usr/lib/",
  "/var/run/",
  "/var/log/"
];
var BLOCKED_BASENAME_RE = /^(tmp\.[A-Za-z0-9]+|\.claude-.+|policy-limits\.json)$/;
var MAX_FILE_SIZE = 50 * 1024 * 1024;
var MAX_FILES_PER_SYNC = 20;
function extractFilePaths(sessionId) {
  const events = listEvents(sessionId, { limit: 500, order: "desc" });
  const seen = /* @__PURE__ */ new Set();
  const paths = [];
  let sawFileTools = false;
  let sawBash = false;
  for (const evt of events) {
    if (evt.type !== "agent.tool_use") continue;
    let payload;
    try {
      payload = JSON.parse(evt.payload_json);
    } catch {
      continue;
    }
    let filePath;
    const toolName = payload.name;
    const FILE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "write_file", "edit_file", "file_edit", "write", "edit"]);
    if (FILE_TOOLS.has(toolName ?? "") && payload.input) {
      sawFileTools = true;
      filePath = payload.input.file_path ?? payload.input.filePath ?? payload.input.path;
    } else if (toolName === "Bash" || toolName === "bash") {
      sawBash = true;
    } else if (toolName === "apply_patch" && payload.input) {
      sawFileTools = true;
      const patch = payload.input.patchText;
      const match = patch?.match(/\*\*\* (?:Add|Update) File: (.+)/);
      if (match) filePath = match[1].trim();
    }
    if (!filePath || typeof filePath !== "string" || filePath === "") continue;
    const resolved = filePath.startsWith("/") ? filePath : `/${filePath}`;
    if (seen.has(resolved)) continue;
    seen.add(resolved);
    paths.push(resolved);
  }
  return { paths, sawFileTools, sawBash };
}
function isPathSafe(p) {
  if (!p.startsWith("/")) return false;
  if (p.includes("..")) return false;
  if (BLOCKED_PREFIXES.some((prefix) => p.startsWith(prefix))) return false;
  if (BLOCKED_BASENAME_RE.test(path.basename(p))) return false;
  return true;
}
function isBinaryExtension(p) {
  const ext = path.extname(p).toLowerCase();
  return BINARY_EXTENSIONS.has(ext);
}
async function discoverChangedFiles(sandboxName, provider, secrets) {
  try {
    const result = await provider.exec(
      sandboxName,
      ["sh", "-c", [
        "find /home /root /workspace /mnt /tmp",
        "-maxdepth 4 -type f -mmin -60 -size +0c",
        "! -path '*/.git/*' ! -path '*/node_modules/*' ! -path '*/.npm/*'",
        "! -path '*/.config/*' ! -path '*/.local/*' ! -path '*/.cache/*'",
        "! -path '*/cache/*' ! -path '*-debug-*.log'",
        "! -path '*/tool-bridge/*' ! -path '*/permission-bridge/*' ! -path '*/v8-cache/*'",
        "! -name '.*' ! -name '*.sqlite' ! -name '*.sqlite-*'",
        "! -name 'installation_id' ! -name 'plugins.sha'",
        "2>/dev/null | head -30"
      ].join(" ")],
      { secrets, timeoutMs: 1e4 }
    );
    if (result.exit_code !== 0 || !result.stdout.trim()) return [];
    const clean = result.stdout.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
    return clean.trim().split("\n").map((l) => l.trim()).filter(Boolean);
  } catch {
    return [];
  }
}
async function syncContainerFiles(opts) {
  const { sessionId, sandboxName, provider, secrets } = opts;
  const extracted = extractFilePaths(sessionId);
  let allPaths = extracted.paths;
  console.log(`[container-file-sync] ${sessionId}: extracted ${allPaths.length} paths, sawFileTools=${extracted.sawFileTools}, sawBash=${extracted.sawBash}`);
  const autoExecResults = [];
  if (extracted.sawFileTools && !extracted.sawBash) {
    const scriptPaths = allPaths.filter((p) => /\.(js|mjs)$/.test(p));
    for (const scriptPath of scriptPaths) {
      try {
        const peek = await provider.exec(sandboxName, ["head", "-20", "--", scriptPath], { secrets, timeoutMs: 5e3 });
        const head = (peek.stdout ?? "").replace(/[\x00-\x1f]/g, "");
        const LIB_PATTERNS = [
          { re: /require\(["']docx["']\)|from\s+["']docx["']/, pkg: "docx" },
          { re: /require\(["']pdfkit["']\)|from\s+["']pdfkit["']/, pkg: "pdfkit" },
          { re: /require\(["']exceljs["']\)|from\s+["']exceljs["']/, pkg: "exceljs" },
          { re: /require\(["']officegen["']\)|from\s+["']officegen["']/, pkg: "officegen" }
        ];
        const matched = LIB_PATTERNS.find((p) => p.re.test(head));
        if (!matched) continue;
        console.log(`[container-file-sync] ${sessionId}: auto-executing ${matched.pkg} script: ${scriptPath}`);
        const dir = path.dirname(scriptPath) || "/tmp";
        const run = await provider.exec(
          sandboxName,
          ["sh", "-c", `cd -- "${dir}" && (npm list "${matched.pkg}" 2>/dev/null || npm install "${matched.pkg}" 2>/dev/null) && node -- "${scriptPath}"`],
          { secrets, timeoutMs: 3e4 }
        );
        if (run.exit_code === 0) {
          console.log(`[container-file-sync] ${sessionId}: script executed successfully`);
          extracted.sawBash = true;
          autoExecResults.push({ scriptPath, success: true });
        } else {
          const stderr = (run.stderr ?? "").replace(/[\x00-\x1f]/g, "").slice(0, 200);
          console.warn(`[container-file-sync] ${sessionId}: script failed (exit ${run.exit_code}): ${stderr}`);
          autoExecResults.push({ scriptPath, success: false });
        }
      } catch (err) {
        console.warn(`[container-file-sync] ${sessionId}: auto-execute check failed for ${scriptPath}:`, err);
      }
    }
  }
  if (allPaths.length > 0) {
    console.log(`[container-file-sync] ${sessionId}: tracked paths: ${allPaths.join(", ")}`);
  }
  try {
    const outputResult = await provider.exec(
      sandboxName,
      ["sh", "-c", "find /mnt/session/outputs -type f 2>/dev/null || true"],
      { secrets, timeoutMs: 5e3 }
    );
    if (outputResult.exit_code === 0 && outputResult.stdout.trim()) {
      const clean = outputResult.stdout.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
      for (const filePath of clean.trim().split("\n").filter(Boolean)) {
        const p = filePath.trim();
        if (p && !allPaths.includes(p)) {
          allPaths.push(p);
        }
      }
      console.log(`[container-file-sync] ${sessionId}: found ${clean.trim().split("\n").filter(Boolean).length} file(s) in /mnt/session/outputs/`);
    }
  } catch {
  }
  {
    const discovered = await discoverChangedFiles(sandboxName, provider, secrets);
    console.log(`[container-file-sync] ${sessionId}: discovered ${discovered.length} files on container: ${discovered.join(", ")}`);
    for (const p of discovered) {
      if (!allPaths.includes(p)) allPaths.push(p);
    }
  }
  if (allPaths.length === 0) {
    console.log(`[container-file-sync] ${sessionId}: no paths to sync`);
    return { synced: 0, skipped: 0, files: [], autoExecuted: [] };
  }
  const validPaths = [];
  let skipped = 0;
  for (const p of allPaths) {
    if (!isPathSafe(p)) {
      console.log(`[container-file-sync] ${sessionId}: skip unsafe: ${p}`);
      skipped++;
      continue;
    }
    if (isBinaryExtension(p)) {
      console.log(`[container-file-sync] ${sessionId}: skip binary ext: ${p}`);
      skipped++;
      continue;
    }
    validPaths.push(p);
  }
  console.log(`[container-file-sync] ${sessionId}: ${validPaths.length} valid paths, ${skipped} skipped`);
  if (validPaths.length === 0) return { synced: 0, skipped, files: [], autoExecuted: autoExecResults };
  if (validPaths.length > MAX_FILES_PER_SYNC) {
    console.warn(
      `[container-file-sync] ${sessionId}: ${validPaths.length} files to sync, capping at ${MAX_FILES_PER_SYNC}`
    );
    skipped += validPaths.length - MAX_FILES_PER_SYNC;
    validPaths.length = MAX_FILES_PER_SYNC;
  }
  let synced = 0;
  const syncedFiles = [];
  const BINARY_READ_EXTS = /* @__PURE__ */ new Set([
    ".docx",
    ".xlsx",
    ".pptx",
    ".pdf",
    ".zip",
    ".tar",
    ".gz",
    ".png",
    ".jpg",
    ".jpeg",
    ".gif",
    ".ico",
    ".webp",
    ".woff",
    ".woff2",
    ".ttf",
    ".otf",
    ".eot"
  ]);
  for (const filePath of validPaths) {
    try {
      const ext = path.extname(filePath).toLowerCase();
      const isBinary = BINARY_READ_EXTS.has(ext);
      const result = isBinary ? await provider.exec(sandboxName, ["sh", "-c", `base64 < "${filePath}" | tr -d '\\n'`], { secrets, timeoutMs: 15e3 }) : await provider.exec(sandboxName, ["cat", "--", filePath], { secrets, timeoutMs: 15e3 });
      if (result.exit_code !== 0 || !result.stdout) {
        skipped++;
        continue;
      }
      const raw = result.stdout.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
      const data = isBinary ? Buffer.from(raw.trim(), "base64") : Buffer.from(raw, "utf8");
      if (data.length > MAX_FILE_SIZE) {
        skipped++;
        continue;
      }
      const hash = createHash("sha256").update(data).digest("hex");
      const existing = findFileByContainerPath(sessionId, filePath, hash);
      if (existing) {
        skipped++;
        continue;
      }
      const fileId = newId("file");
      const filename = path.basename(filePath);
      const storagePath = storeFile(fileId, filename, data);
      const fileExt = path.extname(filename).toLowerCase();
      const contentType = MIME_MAP[fileExt] ?? "text/plain";
      createFile({
        filename,
        size: data.length,
        content_type: contentType,
        storage_path: storagePath,
        scope: { type: "session", id: sessionId },
        container_path: filePath,
        content_hash: hash
      });
      syncedFiles.push({ id: fileId, filename, size_bytes: data.length, mime_type: contentType, container_path: filePath });
      synced++;
    } catch (err) {
      console.warn(`[container-file-sync] failed to sync ${filePath}:`, err);
      skipped++;
    }
  }
  return { synced, skipped, files: syncedFiles, autoExecuted: autoExecResults };
}
var MIME_MAP = {
  ".ts": "text/typescript",
  ".tsx": "text/typescript",
  ".js": "text/javascript",
  ".jsx": "text/javascript",
  ".json": "application/json",
  ".html": "text/html",
  ".css": "text/css",
  ".md": "text/markdown",
  ".py": "text/x-python",
  ".rs": "text/x-rust",
  ".go": "text/x-go",
  ".java": "text/x-java",
  ".rb": "text/x-ruby",
  ".sh": "text/x-shellscript",
  ".yaml": "text/yaml",
  ".yml": "text/yaml",
  ".toml": "text/toml",
  ".xml": "text/xml",
  ".sql": "text/x-sql",
  ".txt": "text/plain",
  ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  ".pdf": "application/pdf",
  ".zip": "application/zip",
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".gif": "image/gif",
  ".svg": "image/svg+xml",
  ".csv": "text/csv",
  ".c": "text/x-c",
  ".cpp": "text/x-c++",
  ".h": "text/x-c",
  ".hpp": "text/x-c++"
};
export {
  syncContainerFiles
};
