import { h as getDefaultNetworkController, p as networkFetch } from "./fetch-virtual-server-BZi6n2Qk.js";
import { ah, ai, Q, aj, a8 } from "./fetch-virtual-server-BZi6n2Qk.js";
import { formatOxcDiagnosticsForTerminal, isSupportedOxcPath, resolveOxcConfigForFile, resolveOxcParserExtension, runOxcOnSource } from "./runtime-Ci2oTJlY.js";
import { g as getFlyAuthorizationHeader } from "./fly-auth-y0soRCV1.js";
const FLY_MACHINES_API_BASE = "https://api.machines.dev/v1";
const FLY_LOGS_API_BASE = "https://api.fly.io";
const INFISICAL_API_BASE = "https://app.infisical.com";
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
const DEFAULT_APP_BUILDING_IMAGE_REF = "ghcr.io/replayio/app-building:latest";
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function runtimeFetch(input, init) {
  const controller = getDefaultNetworkController();
  if (controller) {
    return networkFetch(input, init, controller);
  }
  return fetch(input, init);
}
async function fetchWithTimeout(input, init = {}) {
  const controller = new AbortController();
  const timeoutMs = init.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
  const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const { timeoutMs: _timeoutMs, signal, ...rest } = init;
    if (signal == null ? void 0 : signal.aborted) {
      throw new DOMException("Aborted", "AbortError");
    }
    signal == null ? void 0 : signal.addEventListener("abort", () => controller.abort(), { once: true });
    return await runtimeFetch(input, {
      ...rest,
      signal: controller.signal
    });
  } finally {
    clearTimeout(timeoutHandle);
  }
}
async function fetchJson(input, init, label) {
  const response = await fetchWithTimeout(input, init);
  if (!response.ok) {
    const body = await response.text().catch(() => "");
    throw new Error(`${label} failed (${response.status}): ${body || response.statusText}`);
  }
  return await response.json();
}
async function flyFetchJson(path, token, init = {}) {
  const headers = new Headers(init.headers);
  headers.set("authorization", getFlyAuthorizationHeader(token));
  headers.set("content-type", "application/json");
  return fetchJson(
    `${FLY_MACHINES_API_BASE}${path}`,
    {
      ...init,
      headers
    },
    `Fly API ${init.method ?? "GET"} ${path}`
  );
}
async function flyFetchText(path, token, init = {}) {
  const headers = new Headers(init.headers);
  headers.set("authorization", getFlyAuthorizationHeader(token));
  headers.set("content-type", "application/json");
  const response = await fetchWithTimeout(`${FLY_MACHINES_API_BASE}${path}`, {
    ...init,
    headers
  });
  if (!response.ok) {
    const body = await response.text().catch(() => "");
    throw new Error(`Fly API ${init.method ?? "GET"} ${path} failed (${response.status}): ${body || response.statusText}`);
  }
  return response.text();
}
async function infisicalLogin(clientId, clientSecret) {
  const payload = await fetchJson(
    `${INFISICAL_API_BASE}/api/v1/auth/universal-auth/login`,
    {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({ clientId, clientSecret })
    },
    "Infisical login"
  );
  if (!payload.accessToken) {
    throw new Error("Infisical login did not return an access token.");
  }
  return payload.accessToken;
}
function workerHeaders(instanceId) {
  if (!instanceId) {
    return {};
  }
  return {
    "fly-force-instance-id": instanceId
  };
}
async function createVolume(appName, token, volumeName, region) {
  const payload = await flyFetchJson(
    `/apps/${appName}/volumes`,
    token,
    {
      method: "POST",
      body: JSON.stringify({
        name: volumeName,
        region,
        size_gb: 50,
        encrypted: true,
        require_unique_zone: false
      })
    }
  );
  return payload.id;
}
async function listFlyVolumes(appName, token) {
  return flyFetchJson(
    `/apps/${appName}/volumes`,
    token
  );
}
async function listFlyIpAssignments(appName, token) {
  const payload = await flyFetchJson(
    `/apps/${appName}/ip_assignments`,
    token
  );
  if (Array.isArray(payload)) {
    return payload;
  }
  if (payload && Array.isArray(payload.ips)) {
    return payload.ips;
  }
  return [];
}
async function allocateFlyIpAssignment(appName, token, type) {
  await flyFetchJson(
    `/apps/${appName}/ip_assignments`,
    token,
    {
      method: "POST",
      body: JSON.stringify({ type })
    }
  );
}
function isDuplicateIpAssignmentError(error) {
  const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
  return message.includes("already") || message.includes("exists") || message.includes("assigned");
}
function hasSharedV4Ip(assignments) {
  return assignments.some((assignment) => assignment.type === "shared_v4" || assignment.type === "public (shared)" || assignment.shared === true);
}
function hasV6Ip(assignments) {
  return assignments.some((assignment) => {
    var _a, _b;
    return assignment.type === "v6" || assignment.type === "public" || ((_a = assignment.address) == null ? void 0 : _a.includes(":")) || ((_b = assignment.ip) == null ? void 0 : _b.includes(":"));
  });
}
async function infisicalFetch(path, token, init = {}) {
  const headers = new Headers(init.headers);
  headers.set("authorization", `Bearer ${token}`);
  headers.set("content-type", "application/json");
  return fetchWithTimeout(`${INFISICAL_API_BASE}${path}`, {
    ...init,
    headers
  });
}
async function ensureInfisicalFolder(token, projectId, environment, folderPath) {
  const segments = folderPath.split("/").filter(Boolean);
  let parentPath = "/";
  for (const segment of segments) {
    const response = await infisicalFetch("/api/v2/folders", token, {
      method: "POST",
      body: JSON.stringify({
        projectId,
        environment,
        name: segment,
        path: parentPath
      })
    });
    if (response.ok || response.status === 400) {
      parentPath += `${segment}/`;
      continue;
    }
    const body = await response.text().catch(() => "");
    throw new Error(
      `Infisical create folder ${parentPath}${segment} failed (${response.status}): ${body || response.statusText}`
    );
  }
}
async function upsertInfisicalSecret(options) {
  const {
    token,
    projectId,
    environment,
    secretPath,
    name,
    value
  } = options;
  const pathname = `/api/v4/secrets/${encodeURIComponent(name)}`;
  const body = JSON.stringify({
    projectId,
    environment,
    secretPath,
    secretValue: value,
    type: "shared"
  });
  const createResponse = await infisicalFetch(pathname, token, {
    method: "POST",
    body
  });
  if (createResponse.ok) {
    return;
  }
  if (createResponse.status === 400) {
    const patchResponse = await infisicalFetch(pathname, token, {
      method: "PATCH",
      body
    });
    if (patchResponse.ok) {
      return;
    }
    const patchBody = await patchResponse.text().catch(() => "");
    throw new Error(`Infisical PATCH ${name} failed (${patchResponse.status}): ${patchBody || patchResponse.statusText}`);
  }
  if (createResponse.status === 404) {
    await ensureInfisicalFolder(token, projectId, environment, secretPath);
    const retryResponse = await infisicalFetch(pathname, token, {
      method: "POST",
      body
    });
    if (retryResponse.ok) {
      return;
    }
    if (retryResponse.status === 400) {
      const patchResponse = await infisicalFetch(pathname, token, {
        method: "PATCH",
        body
      });
      if (patchResponse.ok) {
        return;
      }
      const patchBody = await patchResponse.text().catch(() => "");
      throw new Error(`Infisical PATCH ${name} after folder creation failed (${patchResponse.status}): ${patchBody || patchResponse.statusText}`);
    }
    const retryBody = await retryResponse.text().catch(() => "");
    throw new Error(`Infisical POST ${name} after folder creation failed (${retryResponse.status}): ${retryBody || retryResponse.statusText}`);
  }
  const responseBody = await createResponse.text().catch(() => "");
  throw new Error(`Infisical POST ${name} failed (${createResponse.status}): ${responseBody || createResponse.statusText}`);
}
async function setInfisicalGlobalSecret(options) {
  return upsertInfisicalSecret({
    ...options,
    secretPath: "/global/"
  });
}
async function ensureFlyPublicIpAssignments(appName, token) {
  const assignments = await listFlyIpAssignments(appName, token);
  if (!hasSharedV4Ip(assignments)) {
    await allocateFlyIpAssignment(appName, token, "shared_v4").catch((error) => {
      if (!isDuplicateIpAssignmentError(error)) {
        throw error;
      }
    });
  }
  if (!hasV6Ip(assignments)) {
    await allocateFlyIpAssignment(appName, token, "v6").catch((error) => {
      if (!isDuplicateIpAssignmentError(error)) {
        throw error;
      }
    });
  }
}
async function destroyFlyMachine(appName, token, machineId, volumeId) {
  await flyFetchText(`/apps/${appName}/machines/${machineId}?force=true`, token, {
    method: "DELETE"
  }).catch(() => "");
  if (volumeId) {
    await flyFetchText(`/apps/${appName}/volumes/${volumeId}`, token, {
      method: "DELETE"
    }).catch(() => "");
  }
}
async function createAppBuildingMachine(options) {
  const regions = ["dfw", "iad", "ord", "sjc"];
  const volumeName = `app_${options.machineName.replace(/-/g, "_")}`.slice(0, 30);
  let cleanupPromise = null;
  await ensureFlyPublicIpAssignments(options.appName, options.token);
  for (const region of regions) {
    const volumeId = await createVolume(
      options.appName,
      options.token,
      volumeName,
      region
    );
    if (!cleanupPromise) {
      cleanupPromise = listFlyVolumes(options.appName, options.token).then(async (volumes) => {
        await Promise.all(
          volumes.map(async (volume) => {
            if (volume.id === volumeId || volume.attached_machine_id) {
              return;
            }
            await flyFetchText(`/apps/${options.appName}/volumes/${volume.id}`, options.token, {
              method: "DELETE"
            }).catch(() => "");
          })
        );
      });
    }
    try {
      const payload = await flyFetchJson(
        `/apps/${options.appName}/machines`,
        options.token,
        {
          method: "POST",
          body: JSON.stringify({
            name: options.machineName,
            region,
            config: {
              image: options.imageRef,
              env: options.env,
              auto_destroy: true,
              restart: { policy: "on-failure", max_retries: 3 },
              guest: {
                cpu_kind: "performance",
                cpus: 16,
                memory_mb: 32768
              },
              mounts: [{ volume: volumeId, path: "/app" }],
              services: [
                {
                  internal_port: 3e3,
                  protocol: "tcp",
                  autostart: false,
                  autostop: "off",
                  ports: [{ port: 443, handlers: ["tls", "http"] }]
                }
              ]
            }
          })
        }
      );
      await cleanupPromise;
      return {
        machineId: payload.id,
        instanceId: typeof payload.instance_id === "string" && payload.instance_id.trim() ? payload.instance_id.trim() : null,
        volumeId,
        region
      };
    } catch (error) {
      await flyFetchText(`/apps/${options.appName}/volumes/${volumeId}`, options.token, {
        method: "DELETE"
      }).catch(() => "");
      const message = error instanceof Error ? error.message : String(error);
      if (message.includes("412") || message.toLowerCase().includes("insufficient")) {
        continue;
      }
      throw error;
    }
  }
  throw new Error(`Unable to provision a Fly machine for ${options.appName}.`);
}
async function getFlyMachine(appName, token, machineId) {
  return flyFetchJson(
    `/apps/${appName}/machines/${machineId}`,
    token
  );
}
async function waitForFlyMachineStarted(appName, token, machineId, timeoutMs = 18e4) {
  const startedAt = Date.now();
  while (Date.now() - startedAt < timeoutMs) {
    try {
      await flyFetchText(
        `/apps/${appName}/machines/${machineId}/wait?state=started&timeout=60`,
        token
      );
      return;
    } catch {
      await delay(5e3);
    }
  }
  throw new Error(`Machine ${machineId} did not reach the started state.`);
}
async function waitForWorkerReady(baseUrl, instanceId, timeoutMs = 18e4) {
  const startedAt = Date.now();
  while (Date.now() - startedAt < timeoutMs) {
    try {
      return await fetchAppBuildingStatus(baseUrl, instanceId);
    } catch {
      await delay(2e3);
    }
  }
  throw new Error(`Worker at ${baseUrl} did not become ready.`);
}
async function fetchAppBuildingStatus(baseUrl, instanceId) {
  return fetchJson(
    `${baseUrl.replace(/\/+$/, "")}/status`,
    {
      headers: workerHeaders(instanceId)
    },
    "Worker status"
  );
}
async function fetchAppBuildingLogs(baseUrl, instanceId, offset = 0) {
  return fetchJson(
    `${baseUrl.replace(/\/+$/, "")}/logs?offset=${offset}`,
    {
      headers: workerHeaders(instanceId)
    },
    "Worker logs"
  );
}
async function fetchFlyLogsPage(appName, token, options = {}) {
  var _a;
  const url = new URL(
    `${FLY_LOGS_API_BASE}/api/v1/apps/${encodeURIComponent(appName)}/logs`
  );
  url.searchParams.set("next_token", options.nextToken ?? "");
  if (options.startTime) {
    url.searchParams.set("start_time", options.startTime);
  }
  if (options.region) {
    url.searchParams.set("region", options.region);
  }
  if (options.instanceId) {
    url.searchParams.set("instance", options.instanceId);
  }
  const response = await fetchWithTimeout(url.toString(), {
    headers: {
      authorization: getFlyAuthorizationHeader(token)
    },
    signal: options.signal
  });
  if (!response.ok) {
    const body = await response.text().catch(() => "");
    throw new Error(
      `Fly logs GET /api/v1/apps/${appName}/logs failed (${response.status}): ${body || response.statusText}`
    );
  }
  const payload = await response.json();
  return {
    entries: (payload.data || []).map((entry) => entry.attributes).filter((entry) => Boolean(entry)),
    nextToken: ((_a = payload.meta) == null ? void 0 : _a.next_token) || ""
  };
}
async function fetchFlyLogsSince(appName, token, options = {}) {
  return fetchFlyLogsPage(appName, token, {
    nextToken: options.cursor || "",
    startTime: options.cursor ? null : options.startTime || null,
    instanceId: options.machineId || null,
    region: null,
    signal: options.signal
  });
}
async function pollFlyLogs(appName, token, options = {}) {
  const maxAttempts = options.maxAttempts ?? 6;
  const maxPages = options.maxPages ?? 32;
  const initialDelay = options.initialDelayMs ?? 400;
  const maxDelay = options.maxDelayMs ?? 2e3;
  const entries = [];
  let nextToken = "";
  let waitFor = initialDelay;
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    let pagesThisAttempt = 0;
    while (pagesThisAttempt < maxPages) {
      const page = await fetchFlyLogsPage(appName, token, {
        nextToken,
        region: options.region,
        instanceId: options.instanceId
      });
      pagesThisAttempt += 1;
      if (page.nextToken && page.nextToken !== nextToken) {
        nextToken = page.nextToken;
      }
      if (page.entries.length === 0) {
        break;
      }
      entries.push(...page.entries);
      if (!page.nextToken || page.nextToken === nextToken) {
        if (page.entries.length < 50) {
          break;
        }
      }
    }
    if (entries.length > 0) {
      return entries;
    }
    await delay(waitFor);
    waitFor = Math.min(waitFor * 2, maxDelay);
  }
  return entries;
}
function formatFlyLogEntry(entry) {
  const prefix = [
    entry.timestamp || "",
    entry.region ? `[${entry.region}]` : "",
    entry.instance ? `[${entry.instance}]` : "",
    entry.level ? `${entry.level}:` : ""
  ].filter(Boolean).join(" ");
  const message = entry.message || "";
  return prefix ? `${prefix} ${message}`.trimEnd() : message;
}
function parseAddTaskLogMessage(message) {
  const jsonStart = message.indexOf('{"type":"tool"');
  if (jsonStart < 0) return null;
  let payload;
  try {
    payload = JSON.parse(message.slice(jsonStart));
  } catch {
    return null;
  }
  if (payload.type !== "tool" || typeof payload.tool !== "string") return null;
  if (!/\badd-task\.ts\b/.test(payload.tool)) return null;
  const heredocMatch = payload.tool.match(/<<'EOF'\s*\n([\s\S]*?)\nEOF\b/);
  if (!heredocMatch) return null;
  let tasks;
  try {
    tasks = JSON.parse(heredocMatch[1]);
  } catch {
    return null;
  }
  if (!Array.isArray(tasks)) return null;
  const result = [];
  for (const raw of tasks) {
    if (!raw || typeof raw !== "object") continue;
    const task = raw;
    if (typeof task.skill !== "string") continue;
    if (!Array.isArray(task.subtasks)) continue;
    const app = typeof task.app === "string" ? task.app : null;
    for (const sub of task.subtasks) {
      if (typeof sub !== "string") continue;
      const trimmed = sub.trim();
      if (!trimmed) continue;
      const colonIndex = trimmed.indexOf(":");
      const name = colonIndex > 0 ? trimmed.slice(0, colonIndex).trim() : trimmed;
      const description = colonIndex > 0 ? trimmed.slice(colonIndex + 1).trim() : "";
      result.push({ raw: trimmed, name, description, skill: task.skill, app });
    }
  }
  return result.length > 0 ? result : null;
}
const DEFAULT_FLY_LOG_BUFFER_LIMIT = 500;
function mergeFlyLogDelta(existingBuffer, entries, options = {}) {
  const limit = options.bufferLimit ?? DEFAULT_FLY_LOG_BUFFER_LIMIT;
  const seen = new Set(existingBuffer);
  const newFormatted = [];
  let latestTimestamp = options.lastTimestamp ?? null;
  for (const entry of entries) {
    const formatted = formatFlyLogEntry(entry);
    if (seen.has(formatted)) continue;
    seen.add(formatted);
    newFormatted.push(formatted);
    if (entry.timestamp && (!latestTimestamp || entry.timestamp > latestTimestamp)) {
      latestTimestamp = entry.timestamp;
    }
  }
  const mergedBuffer = [...existingBuffer, ...newFormatted].slice(-limit);
  return { newFormatted, mergedBuffer, latestTimestamp };
}
async function fetchAppBuildingEvents(baseUrl, instanceId, offset = 0) {
  return fetchJson(
    `${baseUrl.replace(/\/+$/, "")}/events?offset=${offset}`,
    {
      headers: workerHeaders(instanceId)
    },
    "Worker events"
  );
}
async function postAppBuildingMessage(baseUrl, instanceId, prompt) {
  await fetchJson(
    `${baseUrl.replace(/\/+$/, "")}/message`,
    {
      method: "POST",
      headers: {
        ...workerHeaders(instanceId),
        "content-type": "application/json"
      },
      body: JSON.stringify({ prompt })
    },
    "Worker message"
  );
}
async function postAppBuildingStop(baseUrl, instanceId) {
  await fetchJson(
    `${baseUrl.replace(/\/+$/, "")}/stop`,
    {
      method: "POST",
      headers: {
        ...workerHeaders(instanceId),
        "content-type": "application/json"
      }
    },
    "Worker stop"
  );
}
export {
  DEFAULT_APP_BUILDING_IMAGE_REF,
  ah as DEFAULT_BROWSER_CLAUDE_CODE_PACKAGE,
  DEFAULT_FLY_LOG_BUFFER_LIMIT,
  ai as DETACH_DEV_SERVERS_ENV,
  createAppBuildingMachine,
  Q as createFetchVirtualServer,
  aj as createNodeError,
  destroyFlyMachine,
  fetchAppBuildingEvents,
  fetchAppBuildingLogs,
  fetchAppBuildingStatus,
  fetchFlyLogsPage,
  fetchFlyLogsSince,
  formatFlyLogEntry,
  formatOxcDiagnosticsForTerminal,
  getFlyMachine,
  infisicalLogin,
  isSupportedOxcPath,
  listFlyVolumes,
  mergeFlyLogDelta,
  parseAddTaskLogMessage,
  pollFlyLogs,
  postAppBuildingMessage,
  postAppBuildingStop,
  a8 as registerFetchVirtualServer,
  resolveOxcConfigForFile,
  resolveOxcParserExtension,
  runOxcOnSource,
  setInfisicalGlobalSecret,
  waitForFlyMachineStarted,
  waitForWorkerReady
};
//# sourceMappingURL=internal.mjs.map
