import {
  ContainerGone
} from "./chunk-CTVBTCTF.js";
import {
  getConfig
} from "./chunk-2MAQ6JFL.js";
import {
  ApiError
} from "./chunk-EZYKRG4W.js";

// src/containers/exec.ts
function api() {
  return getConfig().spriteApi.replace(/\/+$/, "");
}
function authHeaders(tokenOverride) {
  const token = tokenOverride ?? getConfig().spriteToken;
  if (!token) {
    throw new ApiError(500, "server_error", "SPRITE_TOKEN required \u2014 add to vault or .env");
  }
  return { Authorization: `Bearer ${token}` };
}
async function startExec(sandboxName, opts) {
  const params = new URLSearchParams();
  for (const c of opts.argv) params.append("cmd", c);
  if (opts.stdin != null) params.set("stdin", "true");
  const localAbort = new AbortController();
  const timeoutMs = opts.timeoutMs ?? getConfig().agentTimeoutMs;
  const timeoutId = setTimeout(
    () => localAbort.abort(new DOMException("exec timeout", "AbortError")),
    timeoutMs
  );
  if (opts.signal) {
    if (opts.signal.aborted) localAbort.abort(opts.signal.reason);
    else opts.signal.addEventListener("abort", () => localAbort.abort(opts.signal.reason));
  }
  const res = await fetch(
    `${api()}/v1/sprites/${encodeURIComponent(sandboxName)}/exec?${params.toString()}`,
    {
      method: "POST",
      headers: authHeaders(opts.secrets?.SPRITE_TOKEN),
      body: opts.stdin ?? void 0,
      signal: localAbort.signal
    }
  );
  if (!res.ok) {
    clearTimeout(timeoutId);
    const text = await res.text().catch(() => "");
    if (res.status === 404) {
      throw new ContainerGone(sandboxName, `sandbox exec failed (404): ${text.slice(0, 300)}`);
    }
    throw new ApiError(
      502,
      "server_error",
      `sandbox exec failed (${res.status}): ${text.slice(0, 300)}`
    );
  }
  if (!res.body) {
    clearTimeout(timeoutId);
    throw new ApiError(502, "server_error", "sandbox exec returned no body");
  }
  let exitResolve = () => {
  };
  let exitReject = () => {
  };
  const exit = new Promise((resolve, reject) => {
    exitResolve = resolve;
    exitReject = reject;
  });
  const [consumerStream, watchStream] = res.body.tee();
  (async () => {
    const reader = watchStream.getReader();
    try {
      for (; ; ) {
        const { done } = await reader.read();
        if (done) break;
      }
      exitResolve({ code: 0 });
    } catch (err) {
      if (localAbort.signal.aborted) {
        exitReject(new DOMException("aborted", "AbortError"));
      } else {
        exitReject(err);
      }
    } finally {
      clearTimeout(timeoutId);
      try {
        reader.releaseLock();
      } catch {
      }
    }
  })();
  return {
    stdout: consumerStream,
    exit,
    async kill() {
      localAbort.abort(new DOMException("killed", "AbortError"));
    }
  };
}

export {
  startExec
};
