"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const fs = require("node:fs");
const path = require("node:path");
const promises = require("node:stream/promises");
const node_stream = require("node:stream");
const node_child_process = require("node:child_process");
const node_crypto = require("node:crypto");
const index = require("./index-BKifir_y.js");
require("zod");
function _interopNamespaceDefault(e) {
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
  if (e) {
    for (const k in e) {
      if (k !== "default") {
        const d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: () => e[k]
        });
      }
    }
  }
  n.default = e;
  return Object.freeze(n);
}
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
function getPlatformInfo() {
  return {
    platform: process.platform,
    arch: process.arch
  };
}
function buildBinaryPath(dataDir, name, platform) {
  const ext = (platform ?? process.platform) === "win32" ? ".exe" : "";
  return path.join(dataDir, "deps", `${name}${ext}`);
}
function findInPath(name) {
  try {
    node_child_process.execFileSync(name, ["--version"], { stdio: "pipe", timeout: 5e3 });
    return name;
  } catch {
    return null;
  }
}
async function downloadBinary(opts) {
  const { name, url, targetDir, targetName, logger, isArchive, archiveFormat, archiveInnerPath } = opts;
  const targetPath = path.join(targetDir, targetName);
  if (fs.existsSync(targetPath)) {
    logger.debug("Binary already exists", { meta: { name, targetPath } });
    return targetPath;
  }
  fs.mkdirSync(targetDir, { recursive: true });
  logger.info("Downloading binary", { meta: { name, url } });
  const response = await fetch(url, { redirect: "follow" });
  if (!response.ok || !response.body) {
    throw new Error(`Failed to download ${name}: ${response.status} ${response.statusText}`);
  }
  if (isArchive) {
    const ext = archiveFormat ?? "tar.gz";
    const tmpArchive = path.join(targetDir, `${name}-download.${ext}`);
    const nodeStream = node_stream.Readable.fromWeb(response.body);
    await promises.pipeline(nodeStream, fs.createWriteStream(tmpArchive));
    const tmpExtractDir = path.join(targetDir, `${name}-extract`);
    fs.mkdirSync(tmpExtractDir, { recursive: true });
    if (ext === "zip") {
      try {
        node_child_process.execFileSync("unzip", ["-o", "-q", tmpArchive, "-d", tmpExtractDir], { stdio: "pipe" });
      } catch {
        node_child_process.execFileSync("tar", ["-xf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
      }
    } else if (ext === "tar.xz") {
      node_child_process.execFileSync("tar", ["-xJf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
    } else {
      node_child_process.execFileSync("tar", ["-xzf", tmpArchive, "-C", tmpExtractDir], { stdio: "pipe" });
    }
    if (archiveInnerPath) {
      const { copyFileSync } = await import("node:fs");
      const sourcePath = path.join(tmpExtractDir, archiveInnerPath);
      if (!fs.existsSync(sourcePath)) {
        throw new Error(`Binary not found in archive at ${archiveInnerPath}`);
      }
      copyFileSync(sourcePath, targetPath);
    }
    fs.unlinkSync(tmpArchive);
    const { rmSync } = await import("node:fs");
    rmSync(tmpExtractDir, { recursive: true, force: true });
  } else {
    const nodeStream = node_stream.Readable.fromWeb(response.body);
    await promises.pipeline(nodeStream, fs.createWriteStream(targetPath));
  }
  fs.chmodSync(targetPath, 493);
  logger.info("Binary downloaded", { meta: { name, targetPath } });
  return targetPath;
}
async function ensureBinary(opts) {
  const ext = process.platform === "win32" ? ".exe" : "";
  const targetName = `${opts.name}${ext}`;
  const targetPath = path.join(opts.targetDir, targetName);
  if (fs.existsSync(targetPath)) {
    opts.logger.debug("Binary found at target path", { meta: { name: opts.name, targetPath } });
    return targetPath;
  }
  const inPath = findInPath(opts.name);
  if (inPath) {
    opts.logger.info("Binary found in system PATH", { meta: { name: opts.name } });
    return inPath;
  }
  return downloadBinary({
    name: opts.name,
    url: opts.downloadUrl,
    targetDir: opts.targetDir,
    targetName,
    logger: opts.logger,
    isArchive: opts.isArchive,
    archiveFormat: opts.archiveFormat,
    archiveInnerPath: opts.archiveInnerPath
  });
}
const FFMPEG_VERSION = "7.1";
function getFfmpegDownloadUrl(platform, arch) {
  switch (platform) {
    case "linux": {
      const archMap = { x64: "amd64", arm64: "arm64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported Linux architecture: ${arch}`);
      return `https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${a}-static.tar.xz`;
    }
    case "darwin": {
      const archMap = { arm64: "arm64", x64: "amd64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported macOS architecture: ${arch}`);
      return `https://www.osxexperts.net/ffmpeg${FFMPEG_VERSION.replace(".", "")}arm.zip`;
    }
    default:
      throw new Error(`Unsupported platform: ${platform}`);
  }
}
function getFfmpegArchiveInfo(platform) {
  switch (platform) {
    case "linux":
      return {
        isArchive: true,
        archiveFormat: "tar.xz",
        archiveInnerPath: `ffmpeg-${FFMPEG_VERSION}-amd64-static/ffmpeg`
      };
    case "darwin":
      return {
        isArchive: true,
        archiveFormat: "zip",
        archiveInnerPath: "ffmpeg"
      };
    default:
      throw new Error(`Unsupported platform: ${platform}`);
  }
}
async function ensureFfmpeg(dataDir, logger) {
  const depsDir = path.join(dataDir, "deps");
  const platform = process.platform;
  const arch = process.arch;
  const archiveInfo = getFfmpegArchiveInfo(platform);
  return ensureBinary({
    name: "ffmpeg",
    targetDir: depsDir,
    downloadUrl: getFfmpegDownloadUrl(platform, arch),
    logger,
    ...archiveInfo
  });
}
const PYTHON_VERSION = "3.12.12";
const PP_BASE = `https://github.com/bjia56/portable-python/releases/download/cpython-v${PYTHON_VERSION}-build.0`;
function getPythonDownloadUrl(platform, arch) {
  switch (platform) {
    case "linux": {
      const archMap = { x64: "x86_64", arm64: "aarch64" };
      const a = archMap[arch];
      if (!a) throw new Error(`Unsupported Linux architecture for Python: ${arch}`);
      return `${PP_BASE}/python-headless-${PYTHON_VERSION}-linux-${a}.zip`;
    }
    case "darwin": {
      return `${PP_BASE}/python-headless-${PYTHON_VERSION}-darwin-universal2.zip`;
    }
    default:
      throw new Error(`Unsupported platform for portable Python: ${platform}`);
  }
}
async function ensurePython(dataDir, logger) {
  const pythonDir = path.join(dataDir, "deps", "python");
  const pythonBin = path.join(pythonDir, "bin", "python3");
  if (fs.existsSync(pythonBin)) {
    logger.debug("Portable Python found", { meta: { pythonBin } });
    return pythonBin;
  }
  logger.info("Downloading portable Python (headless)", { meta: { version: PYTHON_VERSION } });
  const depsDir = path.join(dataDir, "deps");
  fs.mkdirSync(depsDir, { recursive: true });
  const url = getPythonDownloadUrl(process.platform, process.arch);
  const tmpArchive = path.join(depsDir, "python-download.zip");
  logger.info("Python download source", { meta: { url } });
  const response = await fetch(url, { redirect: "follow" });
  if (!response.ok || !response.body) {
    logger.warn("Failed to download Python — ML detection will not be available", { meta: { status: response.status } });
    return null;
  }
  const nodeStream = node_stream.Readable.fromWeb(response.body);
  await promises.pipeline(nodeStream, fs.createWriteStream(tmpArchive));
  try {
    node_child_process.execFileSync("unzip", ["-o", "-q", tmpArchive, "-d", depsDir], { stdio: "pipe" });
  } catch {
    node_child_process.execFileSync("python3", ["-m", "zipfile", "-e", tmpArchive, depsDir], { stdio: "pipe" });
  }
  const { unlinkSync } = await import("node:fs");
  unlinkSync(tmpArchive);
  if (!fs.existsSync(pythonBin)) {
    const candidates = fs.readdirSync(depsDir).filter(
      (name) => name.startsWith("python-") && fs.existsSync(path.join(depsDir, name, "bin", "python3"))
    );
    if (candidates.length === 0) {
      logger.warn("Python extraction succeeded but no python3 binary found in any subdir", {
        meta: { depsDir }
      });
      return null;
    }
    const extractedRoot = path.join(depsDir, candidates[0]);
    if (fs.existsSync(pythonDir)) {
      fs.rmSync(pythonDir, { recursive: true, force: true });
    }
    fs.renameSync(extractedRoot, pythonDir);
  }
  if (!fs.existsSync(pythonBin)) {
    logger.warn("Python extraction succeeded but binary not found at expected path", {
      meta: { pythonBin }
    });
    return null;
  }
  fs.chmodSync(pythonBin, 493);
  if (process.platform === "darwin") {
    const realBin = path.join(pythonDir, "bin", "python3.real");
    if (!fs.existsSync(realBin)) {
      fs.renameSync(pythonBin, realBin);
      const archFlag = process.arch === "arm64" ? "-arm64" : "-x86_64";
      fs.writeFileSync(
        pythonBin,
        `#!/bin/sh
exec /usr/bin/arch ${archFlag} "${realBin}" "$@"
`
      );
      fs.chmodSync(pythonBin, 493);
      const pyAlt = path.join(pythonDir, "bin", "python");
      if (fs.existsSync(pyAlt)) {
        try {
          fs.rmSync(pyAlt, { force: true });
        } catch {
        }
        fs.writeFileSync(
          pyAlt,
          `#!/bin/sh
exec /usr/bin/arch ${archFlag} "${realBin}" "$@"
`
        );
        fs.chmodSync(pyAlt, 493);
      }
      logger.info("Locked python3 to host arch via shell stub", {
        meta: { archFlag, realBin }
      });
    }
  }
  logger.info("Portable Python installed", { meta: { version: PYTHON_VERSION, pythonBin } });
  return pythonBin;
}
async function installPythonPackages(pythonPath, packages, logger) {
  if (packages.length === 0) return;
  logger.info("Installing Python packages", { meta: { packages } });
  try {
    node_child_process.execFileSync(pythonPath, ["-m", "pip", "install", "--quiet", ...packages], {
      stdio: "pipe",
      timeout: 3e5
      // 5 minutes for large packages like torch
    });
    logger.info("Python packages installed successfully");
  } catch (err) {
    logger.error("Failed to install Python packages", { meta: { error: index.errMsg(err) } });
    throw err;
  }
}
async function installPythonRequirements(pythonPath, requirementsFile, logger) {
  if (!fs.existsSync(requirementsFile)) {
    throw new Error(`Requirements file not found: ${requirementsFile}`);
  }
  const contents = fs.readFileSync(requirementsFile);
  const hash = node_crypto.createHash("sha256").update(contents).digest("hex").slice(0, 16);
  const fileBase = path.basename(requirementsFile).replace(/\.txt$/i, "");
  const pythonRoot = path.join(pythonPath, "..", "..");
  const markerDir = path.join(pythonRoot, ".requirements-installed");
  const markerPath = path.join(markerDir, `${fileBase}-${hash}.marker`);
  if (fs.existsSync(markerPath)) {
    logger.debug("Python requirements already installed", { meta: { requirementsFile, hash } });
    return;
  }
  logger.info("Installing Python requirements", { meta: { requirementsFile, hash } });
  try {
    node_child_process.execFileSync(
      pythonPath,
      ["-m", "pip", "install", "--quiet", "--disable-pip-version-check", "-r", requirementsFile],
      {
        stdio: "pipe",
        timeout: 6e5
        // 10 minutes — coremltools + tensorflow can be slow
      }
    );
  } catch (err) {
    logger.error("Failed to install Python requirements", {
      meta: { requirementsFile, error: index.errMsg(err) }
    });
    throw err;
  }
  fs.mkdirSync(markerDir, { recursive: true });
  fs.writeFileSync(markerPath, `${requirementsFile}
${(/* @__PURE__ */ new Date()).toISOString()}
`);
  logger.info("Python requirements installed", { meta: { requirementsFile, hash } });
}
const STORAGE_LOCATION_TYPES = [
  "data",
  "media",
  "recordings",
  "recordings-high",
  "recordings-low",
  "recordings-clips",
  "event-images",
  "models",
  "addons-data",
  "cache",
  "logs",
  "backups"
];
const DEFAULT_LOCATION_SUBDIRS = {
  "data": "db",
  "media": "media",
  "recordings": "recordings",
  "recordings-high": "recordings-high",
  "recordings-low": "recordings-low",
  "recordings-clips": "recordings-clips",
  "event-images": "event-images",
  "models": "models",
  "addons-data": "addons-data",
  "cache": "/tmp/camstack-cache",
  "logs": "logs",
  "backups": "backups"
};
class FilesystemStorageProvider {
  id = "local";
  name = "Local Filesystem";
  supportedLocations = [...STORAGE_LOCATION_TYPES];
  rootPath;
  locationPaths;
  constructor(rootPath, overrides) {
    this.rootPath = path__namespace.resolve(rootPath);
    this.locationPaths = /* @__PURE__ */ new Map();
    for (const loc of STORAGE_LOCATION_TYPES) {
      const override = overrides?.[loc];
      if (override) {
        this.locationPaths.set(loc, path__namespace.resolve(override));
      } else {
        const subdir = DEFAULT_LOCATION_SUBDIRS[loc];
        this.locationPaths.set(
          loc,
          path__namespace.isAbsolute(subdir) ? subdir : path__namespace.join(this.rootPath, subdir)
        );
      }
    }
  }
  resolve({ location, relativePath }) {
    const base = this.locationPaths.get(location);
    if (!base) throw new Error(`Unknown storage location: ${location}`);
    return path__namespace.join(base, relativePath);
  }
  async write({ location, relativePath, data }) {
    const filePath = this.resolve({ location, relativePath });
    await fs__namespace.promises.mkdir(path__namespace.dirname(filePath), { recursive: true });
    if (Buffer.isBuffer(data)) {
      await fs__namespace.promises.writeFile(filePath, data);
    } else {
      const writeStream = fs__namespace.createWriteStream(filePath);
      await new Promise((resolve, reject) => {
        data.pipe(writeStream);
        writeStream.on("finish", resolve);
        writeStream.on("error", reject);
      });
    }
  }
  async read({ location, relativePath }) {
    return fs__namespace.promises.readFile(this.resolve({ location, relativePath }));
  }
  async exists({ location, relativePath }) {
    try {
      await fs__namespace.promises.access(this.resolve({ location, relativePath }));
      return true;
    } catch {
      return false;
    }
  }
  async list({ location, prefix }) {
    const base = this.locationPaths.get(location);
    if (!base) return [];
    const dir = prefix ? path__namespace.join(base, prefix) : base;
    try {
      const entries = await fs__namespace.promises.readdir(dir, { withFileTypes: true });
      return entries.map((e) => prefix ? `${prefix}/${e.name}` : e.name);
    } catch {
      return [];
    }
  }
  async delete({ location, relativePath }) {
    const filePath = this.resolve({ location, relativePath });
    await fs__namespace.promises.rm(filePath, { force: true });
  }
  async getAvailableSpace({ location }) {
    const base = this.locationPaths.get(location);
    if (!base) return null;
    try {
      let target = base;
      while (!fs__namespace.existsSync(target)) {
        const parent = path__namespace.dirname(target);
        if (!parent || parent === target) return null;
        target = parent;
      }
      const stats = await fs__namespace.promises.statfs(target);
      return stats.bavail * stats.bsize;
    } catch {
      return null;
    }
  }
  /** Get the resolved path for a location type */
  getLocationPath(location) {
    const p = this.locationPaths.get(location);
    if (!p) throw new Error(`Unknown storage location: ${location}`);
    return p;
  }
  /** Get the root path */
  getRootPath() {
    return this.rootPath;
  }
}
exports.FilesystemStorageProvider = FilesystemStorageProvider;
exports.PYTHON_VERSION = PYTHON_VERSION;
exports.buildBinaryPath = buildBinaryPath;
exports.downloadBinary = downloadBinary;
exports.ensureBinary = ensureBinary;
exports.ensureFfmpeg = ensureFfmpeg;
exports.ensurePython = ensurePython;
exports.findInPath = findInPath;
exports.getFfmpegDownloadUrl = getFfmpegDownloadUrl;
exports.getPlatformInfo = getPlatformInfo;
exports.getPythonDownloadUrl = getPythonDownloadUrl;
exports.installPythonPackages = installPythonPackages;
exports.installPythonRequirements = installPythonRequirements;
//# sourceMappingURL=node.js.map
