import {
  shellEscape
} from "../chunk-H26LNIXA.js";
import {
  getConfig,
  readEnvOrSetting
} from "../chunk-2MAQ6JFL.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/providers/fly.ts
var BASE_URL = "https://api.machines.dev";
function getToken(secrets) {
  const token = secrets?.FLY_API_TOKEN ?? readEnvOrSetting("FLY_API_TOKEN");
  if (!token) throw new Error("FLY_API_TOKEN required \u2014 add to vault, .env, or gateway settings");
  return token;
}
function getAppName(secrets) {
  const app = secrets?.FLY_APP_NAME ?? readEnvOrSetting("FLY_APP_NAME");
  if (!app) throw new Error("FLY_APP_NAME required \u2014 add to vault, .env, or gateway settings");
  return app;
}
function headers(secrets) {
  return {
    Authorization: `Bearer ${getToken(secrets)}`,
    "Content-Type": "application/json"
  };
}
var g = globalThis;
if (!g.__caFlyMachines) g.__caFlyMachines = /* @__PURE__ */ new Map();
var machines = g.__caFlyMachines;
function getImage(secrets) {
  return secrets?.FLY_IMAGE ?? process.env.FLY_IMAGE ?? "node:22";
}
function buildExecBody(argv, opts) {
  const cmd = argv.map((a) => shellEscape(a)).join(" ");
  let fullCmd;
  if (opts?.stdin) {
    const b64 = Buffer.from(opts.stdin).toString("base64");
    fullCmd = `echo '${b64}' | base64 -d | ${cmd}`;
  } else {
    fullCmd = cmd;
  }
  return {
    // `command` (string[]) is the current field; `cmd` (string) is deprecated.
    command: ["bash", "-c", fullCmd],
    timeout: opts?.timeoutMs ? Math.ceil(opts.timeoutMs / 1e3) : 60
  };
}
async function refreshMachineMap(prefix, secrets) {
  const app = getAppName(secrets);
  try {
    const res = await fetch(`${BASE_URL}/v1/apps/${app}/machines`, {
      headers: headers(secrets)
    });
    if (!res.ok) return [];
    const data = await res.json();
    const pfx = prefix ?? "ca-sess-";
    for (const m of data) {
      if (m.name) machines.set(m.name, m.id);
    }
    return data.filter((m) => m.name?.startsWith(pfx)).map((m) => ({ name: m.name }));
  } catch {
    return [];
  }
}
var flyProvider = {
  name: "fly",
  stripControlChars: false,
  async checkAvailability(secrets) {
    if (!(secrets?.FLY_API_TOKEN ?? readEnvOrSetting("FLY_API_TOKEN"))) {
      return { available: false, message: "FLY_API_TOKEN required \u2014 add to vault, .env, or gateway settings" };
    }
    if (!(secrets?.FLY_APP_NAME ?? readEnvOrSetting("FLY_APP_NAME"))) {
      return { available: false, message: "FLY_APP_NAME required \u2014 add to vault, .env, or gateway settings" };
    }
    return { available: true };
  },
  async create({ name, secrets }) {
    const app = getAppName(secrets);
    const res = await fetch(`${BASE_URL}/v1/apps/${app}/machines`, {
      method: "POST",
      headers: headers(secrets),
      body: JSON.stringify({
        name,
        config: {
          image: getImage(secrets),
          auto_destroy: true,
          guest: {
            cpu_kind: "shared",
            cpus: 2,
            memory_mb: 1024
          },
          init: {
            cmd: ["sleep", "infinity"]
          }
        }
      })
    });
    if (!res.ok) {
      const body = await res.text().catch(() => "");
      throw new Error(`Fly create failed (${res.status}): ${body}`);
    }
    const data = await res.json();
    machines.set(name, data.id);
    const machineId = data.id;
    const startRes = await fetch(
      `${BASE_URL}/v1/apps/${app}/machines/${machineId}/wait?state=started&timeout=60`,
      { headers: headers(secrets) }
    );
    if (!startRes.ok) {
      const body = await startRes.text().catch(() => "");
      console.warn(`Fly machine wait-for-start warning (${startRes.status}): ${body}`);
    }
  },
  async delete(name, secrets) {
    const machineId = machines.get(name);
    if (!machineId) return;
    const app = getAppName(secrets);
    try {
      await fetch(`${BASE_URL}/v1/apps/${app}/machines/${machineId}/stop`, {
        method: "POST",
        headers: headers(secrets)
      }).catch(() => {
      });
      const res = await fetch(
        `${BASE_URL}/v1/apps/${app}/machines/${machineId}?force=true`,
        {
          method: "DELETE",
          headers: headers(secrets)
        }
      );
      if (!res.ok && res.status !== 404) {
        const body = await res.text().catch(() => "");
        console.warn(`Fly delete failed (${res.status}): ${body}`);
      }
    } catch {
    }
    machines.delete(name);
  },
  async list(opts) {
    return refreshMachineMap(opts?.prefix);
  },
  async exec(name, argv, opts) {
    let machineId = machines.get(name);
    const secrets = opts?.secrets;
    if (!machineId) {
      await refreshMachineMap("ca-sess-", secrets);
      machineId = machines.get(name);
      if (!machineId) throw new Error(`Fly machine not found for name: ${name}`);
    }
    const app = getAppName(secrets);
    const execBody = buildExecBody(argv, opts);
    const timeoutSec = execBody.timeout ?? 60;
    const res = await fetch(
      `${BASE_URL}/v1/apps/${app}/machines/${machineId}/exec`,
      {
        method: "POST",
        headers: headers(secrets),
        body: JSON.stringify(execBody),
        signal: AbortSignal.timeout((timeoutSec + 10) * 1e3)
      }
    );
    if (!res.ok) {
      const text = await res.text().catch(() => "");
      throw new Error(`Fly exec failed (${res.status}): ${text}`);
    }
    const result = await res.json();
    return {
      stdout: result.stdout ?? "",
      stderr: result.stderr ?? "",
      exit_code: result.exit_code ?? 0
    };
  },
  async startExec(name, opts) {
    let machineId = machines.get(name);
    const secrets = opts.secrets;
    if (!machineId) {
      await refreshMachineMap("ca-sess-", secrets);
      machineId = machines.get(name);
      if (!machineId) throw new Error(`Fly machine not found for name: ${name}`);
    }
    const app = getAppName(secrets);
    const execBody = buildExecBody(opts.argv, {
      stdin: opts.stdin,
      timeoutMs: opts.timeoutMs ?? getConfig().agentTimeoutMs
    });
    const controller = new AbortController();
    if (opts.signal) {
      if (opts.signal.aborted) {
        controller.abort();
      } else {
        opts.signal.addEventListener("abort", () => controller.abort());
      }
    }
    const timeoutSec = execBody.timeout ?? 300;
    const fetchTimer = setTimeout(() => controller.abort(), (timeoutSec + 10) * 1e3);
    let res;
    try {
      res = await fetch(
        `${BASE_URL}/v1/apps/${app}/machines/${machineId}/exec`,
        {
          method: "POST",
          headers: headers(secrets),
          body: JSON.stringify(execBody),
          signal: controller.signal
        }
      );
    } finally {
      clearTimeout(fetchTimer);
    }
    if (!res.ok) {
      const text = await res.text().catch(() => "");
      throw new Error(`Fly exec failed (${res.status}): ${text}`);
    }
    const result = await res.json();
    const encoder = new TextEncoder();
    const stdout = new ReadableStream({
      start(c) {
        if (result.stdout) c.enqueue(encoder.encode(result.stdout));
        c.close();
      }
    });
    return {
      stdout,
      exit: Promise.resolve({ code: result.exit_code ?? 0 }),
      async kill() {
        controller.abort();
      }
    };
  }
};
export {
  flyProvider
};
