#!/usr/bin/env node
import {
  Sandbox,
  Snapshot,
  detectSbx,
  ensureFreshCredentials,
  ensureVercelCredentials,
  ensureVercelEnvLoaded,
  maskKey,
  readVercelCredStatus,
  reloadVercelEnv,
  resolveCredentials,
  secretsPath
} from "./chunk-ECLLV5JH.js";
import {
  createCloudProvider,
  listCloudCheckpoints,
  removeCloudCheckpointDir,
  renderInnerCommand,
  resolveCloudCheckpoint,
  writeCloudCheckpointManifest
} from "./chunk-MXXXKJYS.js";
import {
  stageClaudeCredentialsForUpload,
  stageClaudeStaticForUpload,
  stageCodexCredentialsForUpload,
  stageCodexStaticForUpload,
  stageOpencodeCredentialsForUpload,
  stageOpencodeStaticForUpload
} from "./chunk-ZJXTIH6C.js";
import {
  computeContextSha256,
  preparedStatePathFor,
  readCliStamp,
  readPreparedStateRaw,
  recordBox,
  writePreparedStateRaw
} from "./chunk-SNTHHWKY.js";
import "./chunk-G3H2L3O2.js";

// ../../packages/sandbox-vercel/dist/index.js
import { readFile } from "fs/promises";
import { readFile as readFile2 } from "fs/promises";
import { Writable } from "stream";
import { existsSync } from "fs";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";
var DEFAULT_BACKOFF = [1e3, 2e3, 4e3];
var DEFAULT_ATTEMPT_TIMEOUT_MS = 3e4;
var AttemptTimeoutError = class extends Error {
  constructor(method, ms) {
    super(`vercel ${method}: per-attempt timeout after ${String(ms)}ms`);
    this.name = "AttemptTimeoutError";
  }
};
function statusCodeOf(err) {
  if (!err || typeof err !== "object") return void 0;
  for (const key of ["statusCode", "status", "code"]) {
    const v = err[key];
    if (typeof v === "number") return v;
  }
  const resp = err.response;
  if (resp && typeof resp.status === "number") return resp.status;
  return void 0;
}
function isRetriable(err, allowAmbiguous) {
  if (err instanceof AttemptTimeoutError) return allowAmbiguous;
  const status = statusCodeOf(err);
  if (status !== void 0) {
    if (status === 429) return true;
    if (status >= 500 && status <= 599) return allowAmbiguous;
    return false;
  }
  if (err && typeof err === "object") {
    const candidates = [err, err.cause];
    for (const c of candidates) {
      if (!c || typeof c !== "object") continue;
      const code = c.code;
      if (code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ECONNABORTED" || code === "EAI_AGAIN" || code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "UND_ERR_SOCKET" || code === "UND_ERR_CONNECT_TIMEOUT") {
        return allowAmbiguous;
      }
    }
  }
  return false;
}
async function withVercelRetry(opts, fn) {
  const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;
  const maxAttempts = backoff.length + 1;
  const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;
  const log = opts.onRetry ?? defaultRetryLog;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await raceTimeout(fn(), timeoutMs, opts.method);
    } catch (err) {
      const last = attempt === maxAttempts;
      if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;
      const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4e3;
      log(
        `vercel ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`
      );
      await sleep(delay);
    }
  }
  throw new Error(`withVercelRetry: exhausted attempts for ${opts.method}`);
}
function defaultRetryLog(line) {
  process.stderr.write(`
[vercel-retry] ${line}
`);
}
function sleep(ms) {
  return new Promise((r) => setTimeout(r, ms));
}
async function raceTimeout(p, ms, method) {
  let timer;
  try {
    return await Promise.race([
      p,
      new Promise((_resolve, reject) => {
        timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);
      })
    ]);
  } finally {
    if (timer !== void 0) clearTimeout(timer);
  }
}
function errorSummary(err) {
  if (err instanceof Error) {
    const status = statusCodeOf(err);
    return status !== void 0 ? `${err.name}(${String(status)}): ${truncate(err.message)}` : `${err.name}: ${truncate(err.message)}`;
  }
  return truncate(String(err));
}
function truncate(s, max = 160) {
  return s.length > max ? `${s.slice(0, max)}\u2026` : s;
}
var SCHEMA = 1;
function preparedStatePath() {
  return preparedStatePathFor("vercel");
}
function readPreparedState() {
  const raw = readPreparedStateRaw("vercel");
  if (raw === null || typeof raw !== "object") return { schema: SCHEMA };
  const parsed = raw;
  if (parsed.schema !== SCHEMA) {
    return { schema: SCHEMA };
  }
  return { schema: SCHEMA, base: parsed.base };
}
function writePreparedState(state) {
  writePreparedStateRaw("vercel", state);
}
function updatePreparedState(mutate) {
  const s = readPreparedState();
  mutate(s);
  writePreparedState(s);
}
function ensureVercelBaseSnapshot() {
  const state = readPreparedState();
  if (state.base !== void 0) return;
  throw new Error(
    "no Vercel base snapshot found.\nRun `agentbox prepare --provider vercel` first \u2014 Vercel cannot build images from a Dockerfile, so the base snapshot is a one-time prerequisite for cloud boxes."
  );
}
var DEFAULT_BOX_IMAGE_REF = "agentbox/box:dev";
var BOX_USER = "vscode";
var BOX_OWNER = "vscode:vscode";
var VERCEL_EXPOSED_PORTS = [8080, 6080, 8788];
var VERCEL_MAX_PORTS = 4;
function buildExposedPorts(extra) {
  const ports = [...VERCEL_EXPOSED_PORTS];
  const seen = new Set(ports);
  for (const p of extra ?? []) {
    if (ports.length >= VERCEL_MAX_PORTS) break;
    if (Number.isInteger(p) && p >= 1024 && p < 65536 && !seen.has(p)) {
      ports.push(p);
      seen.add(p);
    }
  }
  return ports;
}
function parseNetworkPolicy(raw) {
  const v = (raw ?? "").trim();
  if (v === "") return void 0;
  if (v === "allow-all" || v === "deny-all") return v;
  const allow = v.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
  return allow.length > 0 ? { allow } : void 0;
}
var DEFAULT_TIMEOUT_MS = 45 * 6e4;
var KEEP_LAST_SNAPSHOTS = { count: 1, expiration: 0, deleteEvicted: false };
function creds() {
  return resolveCredentials();
}
function shq(s) {
  return "'" + s.replace(/'/g, "'\\''") + "'";
}
async function getSandbox(id) {
  return Sandbox.get({ name: id, resume: false, ...creds() });
}
async function maybeGetSandbox(id) {
  try {
    return await getSandbox(id);
  } catch {
    return null;
  }
}
function mapState(s) {
  switch (s) {
    case "running":
      return "running";
    case "pending":
    case "stopping":
    case "snapshotting":
      return "running";
    case "stopped":
      return "paused";
    case "aborted":
    case "failed":
    default:
      return "missing";
  }
}
function buildRunCommand(cmd, opts) {
  const prelude = [];
  if (opts?.cwd) prelude.push(`cd ${shq(opts.cwd)}`);
  for (const [k, v] of Object.entries(opts?.env ?? {})) {
    if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) {
      throw new Error(`vercel exec: invalid env var name ${JSON.stringify(k)}`);
    }
    prelude.push(`export ${k}=${shq(v)}`);
  }
  const inner = [...prelude, cmd].join("\n");
  const user = opts?.user ?? BOX_USER;
  if (user === "root") {
    return { cmd: "bash", args: ["-lc", inner], sudo: true };
  }
  return {
    cmd: "bash",
    args: ["-lc", `sudo -u ${user} -H bash -lc ${shq(inner)}`],
    sudo: true
  };
}
async function pushVercelAgentCredentials(sb, log) {
  const specs = [
    { kind: "claude", stage: stageClaudeCredentialsForUpload, dest: "/home/vscode/.agentbox-creds/claude" },
    { kind: "codex", stage: stageCodexCredentialsForUpload, dest: "/home/vscode/.agentbox-creds/codex" },
    { kind: "opencode", stage: stageOpencodeCredentialsForUpload, dest: "/home/vscode/.agentbox-creds/opencode" }
  ];
  for (const spec of specs) {
    const staged = await spec.stage();
    for (const w of staged.warnings) log(`vercel: [${spec.kind}-creds] ${w}`);
    try {
      if (!staged.tarballPath) {
        log(`vercel: ${spec.kind}: no host credentials to push (skipping)`);
        continue;
      }
      const remote = `/tmp/agentbox-${spec.kind}-creds.tar.gz`;
      await sb.writeFiles([{ path: remote, content: await readFile(staged.tarballPath) }]);
      const extract = `sudo -u vscode mkdir -p ${spec.dest} && sudo -u vscode tar -xzf ${remote} -C ${spec.dest} --no-same-permissions --no-same-owner -m && rm -f ${remote}`;
      const r = await sb.runCommand({ cmd: "bash", args: ["-lc", extract], sudo: true });
      if (r.exitCode !== 0) {
        log(`vercel: WARN \u2014 ${spec.kind} credential extract failed (exit ${String(r.exitCode)})`);
      } else {
        log(`vercel: ${spec.kind}: credentials pushed`);
      }
    } finally {
      await staged.cleanup();
    }
  }
}
var vercelBackend = {
  name: "vercel",
  // Vercel rejects privileged ports (<1024) and can't add a routable port to a
  // running sandbox (update registers a route that 502s — verified). So the
  // in-box WebProxy binds 8080 (exposed at create via VERCEL_EXPOSED_PORTS) and
  // `agentbox url` resolves sandbox.domain(8080) → WebProxy → the in-box service.
  webProxyPort: 8080,
  async provision(req) {
    await ensureFreshCredentials();
    const snapshotId = req.snapshot ?? readPreparedState().base?.snapshotId;
    if (!snapshotId) {
      throw new Error(
        "no Vercel base snapshot found.\nRun `agentbox prepare --provider vercel` first \u2014 Vercel cannot build images from a Dockerfile, so the base snapshot is a one-time prerequisite."
      );
    }
    const networkPolicy = parseNetworkPolicy(req.networkPolicy);
    const log = req.onLog ?? (() => {
    });
    const handle = await withVercelRetry(
      { method: "provision", retryOnAmbiguous: false, attemptTimeoutMs: 9e5, backoffMs: [] },
      async () => {
        const sb = await Sandbox.create({
          name: req.name,
          source: { type: "snapshot", snapshotId },
          resources: { vcpus: req.resources?.cpu ?? 2 },
          ports: buildExposedPorts(req.exposePorts),
          timeout: req.timeoutMs ?? DEFAULT_TIMEOUT_MS,
          env: req.env,
          tags: { agentbox: "true", "agentbox.name": req.name },
          persistent: true,
          // Pin the sandbox-default expiration to never. Evicted snapshots (see
          // KEEP_LAST_SNAPSHOTS) fall back to this, so the shared base/checkpoint
          // a box boots from is never re-stamped with a finite expiry on eviction.
          snapshotExpiration: 0,
          keepLastSnapshots: { ...KEEP_LAST_SNAPSHOTS },
          ...networkPolicy ? { networkPolicy } : {},
          ...creds()
        });
        return { sandboxId: sb.name };
      }
    );
    try {
      const sb = await getSandbox(handle.sandboxId);
      await pushVercelAgentCredentials(sb, log);
    } catch (err) {
      log(
        `vercel: WARN \u2014 agent credential push failed (${err instanceof Error ? err.message : String(err)}); in-box claude/codex/opencode will prompt for interactive login`
      );
    }
    return handle;
  },
  async get(sandboxId) {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "get", retryOnAmbiguous: true }, async () => {
      const sb = await maybeGetSandbox(sandboxId);
      return sb ? { sandboxId: sb.name } : null;
    });
  },
  async list() {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "list", retryOnAmbiguous: true }, async () => {
      const page = await Sandbox.list({ ...creds() });
      const items = await page.toArray();
      return items.filter((sb) => sb.tags?.["agentbox"] === "true").map((sb) => {
        const summary = { sandboxId: sb.name };
        const friendly = sb.tags?.["agentbox.name"] ?? sb.name;
        if (friendly) summary.name = friendly;
        if (typeof sb.createdAt === "number") {
          summary.createdAt = new Date(sb.createdAt).toISOString();
        }
        summary.state = mapState(sb.status);
        return summary;
      });
    });
  },
  async start(h) {
    await ensureFreshCredentials();
    await withVercelRetry(
      { method: "start", retryOnAmbiguous: true, attemptTimeoutMs: 12e4 },
      async () => {
        await Sandbox.get({ name: h.sandboxId, resume: true, ...creds() });
      }
    );
  },
  async stop(h) {
    await ensureFreshCredentials();
    await withVercelRetry(
      { method: "stop", retryOnAmbiguous: true, attemptTimeoutMs: 12e4 },
      async () => {
        const sb = await getSandbox(h.sandboxId);
        await sb.stop();
      }
    );
  },
  // pause == stop on Vercel (the auto-snapshot IS the cold-storage state).
  async pause(h) {
    await this.stop(h);
  },
  async resume(h) {
    await this.start(h);
  },
  async destroy(h) {
    await ensureFreshCredentials();
    await withVercelRetry(
      { method: "destroy", retryOnAmbiguous: true, attemptTimeoutMs: 12e4 },
      async () => {
        const sb = await maybeGetSandbox(h.sandboxId);
        if (!sb) return;
        const snapId = sb.currentSnapshotId;
        const source = sb.sourceSnapshotId;
        const base = readPreparedState().base?.snapshotId;
        const ownSnapshot = snapId !== void 0 && snapId !== source && snapId !== base;
        await sb.delete();
        if (ownSnapshot) {
          try {
            const snap = await Snapshot.get({ snapshotId: snapId, ...creds() });
            await snap.delete();
          } catch {
          }
        }
      }
    );
  },
  async state(h) {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "state", retryOnAmbiguous: true }, async () => {
      const sb = await maybeGetSandbox(h.sandboxId);
      if (!sb) return "missing";
      return mapState(sb.status);
    });
  },
  async exec(h, cmd, opts) {
    await ensureFreshCredentials();
    return withVercelRetry(
      {
        method: "exec",
        retryOnAmbiguous: opts?.noRetry ? false : true,
        attemptTimeoutMs: opts?.attemptTimeoutMs ?? 12e4,
        backoffMs: opts?.noRetry ? [] : void 0
      },
      async () => {
        const sb = await getSandbox(h.sandboxId);
        const r = await sb.runCommand(buildRunCommand(cmd, opts));
        const [stdout, stderr] = await Promise.all([r.stdout(), r.stderr()]);
        return { exitCode: r.exitCode, stdout, stderr };
      }
    );
  },
  async uploadFile(h, localPath, remotePath) {
    await ensureFreshCredentials();
    await withVercelRetry(
      { method: "uploadFile", retryOnAmbiguous: true, attemptTimeoutMs: 3e5 },
      async () => {
        const content = await readFile(localPath);
        const sb = await getSandbox(h.sandboxId);
        await sb.writeFiles([{ path: remotePath, content }]);
        try {
          await sb.runCommand({ cmd: "chown", args: [BOX_OWNER, remotePath], sudo: true });
        } catch {
        }
      }
    );
  },
  async downloadFile(h, remotePath, localPath) {
    await ensureFreshCredentials();
    await withVercelRetry(
      { method: "downloadFile", retryOnAmbiguous: true, attemptTimeoutMs: 3e5 },
      async () => {
        const sb = await getSandbox(h.sandboxId);
        const written = await sb.downloadFile(
          { path: remotePath },
          { path: localPath },
          { mkdirRecursive: true }
        );
        if (written === null) {
          throw new Error(`vercel downloadFile: source not found: ${remotePath}`);
        }
      }
    );
  },
  async listFiles(h, remoteDir) {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "listFiles", retryOnAmbiguous: true }, async () => {
      const sb = await getSandbox(h.sandboxId);
      const entries = await sb.fs.readdir(remoteDir, { withFileTypes: true });
      return entries.map((e) => ({ name: e.name, isDir: e.isDirectory() }));
    });
  },
  async previewUrl(h, port) {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "previewUrl", retryOnAmbiguous: true }, async () => {
      const sb = await getSandbox(h.sandboxId);
      return { url: sb.domain(port), token: void 0 };
    });
  },
  // Fewer params than the interface's (h, port, expiresInSeconds) is fine —
  // Vercel sandbox domains are already public + browser-usable, so the signed
  // URL is just the standard one (the TTL is governed by the sandbox session
  // lifetime, not a per-URL signature, so the expiry arg is irrelevant here).
  async signedPreviewUrl(h, port) {
    return this.previewUrl(h, port);
  },
  async snapshotExists(snapshotName) {
    await ensureFreshCredentials();
    return withVercelRetry({ method: "snapshotExists", retryOnAmbiguous: true }, async () => {
      try {
        const snap = await Snapshot.get({ snapshotId: snapshotName, ...creds() });
        return snap.status === "created";
      } catch {
        return false;
      }
    });
  }
  // NOTE: no `createSnapshot`/`deleteSnapshot` here. Vercel snapshots are
  // addressed by an opaque id (not a caller-chosen name), which doesn't fit the
  // CloudBackend `createSnapshot(handle, name): void` contract — the provider
  // needs the id back to store it in the checkpoint manifest. The Vercel
  // provider therefore overrides the whole `checkpoint` capability in index.ts
  // using `snapshotVercelSandbox` / `deleteVercelSnapshot` below.
};
async function snapshotVercelSandbox(sandboxId) {
  await ensureFreshCredentials();
  return withVercelRetry(
    { method: "createSnapshot", retryOnAmbiguous: false, attemptTimeoutMs: 9e5, backoffMs: [] },
    async () => {
      const sb = await getSandbox(sandboxId);
      const snap = await sb.snapshot({ expiration: 0 });
      return snap.snapshotId;
    }
  );
}
async function deleteVercelSnapshot(snapshotId) {
  await ensureFreshCredentials();
  await withVercelRetry({ method: "deleteSnapshot", retryOnAmbiguous: true }, async () => {
    try {
      const snap = await Snapshot.get({ snapshotId, ...creds() });
      await snap.delete();
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      if (/not.?found|404/i.test(msg)) return;
      throw err;
    }
  });
}
var SELF = dirname(fileURLToPath(import.meta.url));
function findStagedCliRuntimeRoot() {
  const candidates = [
    resolve(SELF, "..", "runtime"),
    resolve(SELF, "..", "..", "runtime")
  ];
  for (const c of candidates) {
    if (existsSync(resolve(c, "vercel", "scripts", "provision.sh"))) return c;
  }
  return void 0;
}
var RUNTIME_ASSETS = [
  { name: "provision.sh", remotePath: "/tmp/agentbox-provision.sh", remoteMode: 493 },
  { name: "agentbox-ctl", remotePath: "/tmp/agentbox-ctl", remoteMode: 493 },
  { name: "agentbox-vnc-start", remotePath: "/tmp/agentbox-vnc-start", remoteMode: 493 },
  { name: "agentbox-checkpoint-cleanup", remotePath: "/tmp/agentbox-checkpoint-cleanup", remoteMode: 493 },
  { name: "agentbox-open", remotePath: "/tmp/agentbox-open", remoteMode: 493 },
  { name: "gh-shim", remotePath: "/tmp/agentbox-gh-shim", remoteMode: 493 },
  { name: "git-shim", remotePath: "/tmp/agentbox-git-shim", remoteMode: 493 },
  { name: "custom-system-CLAUDE.md", remotePath: "/tmp/agentbox-custom-CLAUDE.md", remoteMode: 420 },
  { name: "claude-managed-settings.json", remotePath: "/tmp/agentbox-managed-settings.json", remoteMode: 420 },
  { name: "agentbox-codex-hooks.json", remotePath: "/tmp/agentbox-codex-hooks.json", remoteMode: 420 },
  { name: "agentbox-setup-skill.md", remotePath: "/tmp/agentbox-setup-skill.md", remoteMode: 420 }
];
function candidatesFor(name, opts = {}) {
  const cliRoot = opts.cliRuntimeRoot;
  const monorepo = opts.repoRoot ?? guessRepoRoot();
  const monorepoRelative = {
    "provision.sh": ["packages/sandbox-vercel/scripts/provision.sh"],
    "agentbox-ctl": ["packages/ctl/dist/bin.cjs"],
    "agentbox-vnc-start": ["packages/sandbox-docker/scripts/agentbox-vnc-start"],
    "agentbox-checkpoint-cleanup": ["packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup"],
    "agentbox-open": ["packages/sandbox-docker/scripts/agentbox-open"],
    "gh-shim": ["packages/sandbox-docker/scripts/gh-shim"],
    "git-shim": ["packages/sandbox-docker/scripts/git-shim"],
    "custom-system-CLAUDE.md": ["packages/sandbox-vercel/scripts/custom-system-CLAUDE.md"],
    "claude-managed-settings.json": ["packages/sandbox-docker/scripts/claude-managed-settings.json"],
    "agentbox-codex-hooks.json": ["packages/sandbox-docker/scripts/agentbox-codex-hooks.json"],
    "agentbox-setup-skill.md": ["apps/cli/share/agentbox-setup/SKILL.md"]
  };
  const cliRelative = {
    "provision.sh": ["vercel/scripts/provision.sh"],
    "agentbox-ctl": ["vercel/ctl.cjs"],
    "agentbox-vnc-start": ["vercel/agentbox-vnc-start", "docker/packages/sandbox-docker/scripts/agentbox-vnc-start"],
    "agentbox-checkpoint-cleanup": ["vercel/agentbox-checkpoint-cleanup", "docker/packages/sandbox-docker/scripts/agentbox-checkpoint-cleanup"],
    "agentbox-open": ["vercel/agentbox-open", "docker/packages/sandbox-docker/scripts/agentbox-open"],
    "gh-shim": ["vercel/gh-shim", "docker/packages/sandbox-docker/scripts/gh-shim"],
    "git-shim": ["vercel/git-shim", "docker/packages/sandbox-docker/scripts/git-shim"],
    "custom-system-CLAUDE.md": ["vercel/custom-system-CLAUDE.md"],
    "claude-managed-settings.json": ["vercel/claude-managed-settings.json", "docker/packages/sandbox-docker/scripts/claude-managed-settings.json"],
    "agentbox-codex-hooks.json": ["vercel/agentbox-codex-hooks.json", "docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json"],
    "agentbox-setup-skill.md": ["vercel/agentbox-setup-skill.md", "docker/apps/cli/share/agentbox-setup/SKILL.md"]
  };
  const out = [];
  if (cliRoot) {
    for (const rel of cliRelative[name] ?? []) out.push(resolve(cliRoot, rel));
  }
  for (const rel of monorepoRelative[name] ?? []) out.push(resolve(monorepo, rel));
  return out;
}
function resolveRuntimeAssets(opts = {}) {
  const out = [];
  const missing = [];
  for (const asset of RUNTIME_ASSETS) {
    const cands = candidatesFor(asset.name, opts);
    const hit = cands.find((p) => existsSync(p));
    if (!hit) {
      missing.push({ name: asset.name, tried: cands });
      continue;
    }
    out.push({ ...asset, localPath: hit });
  }
  if (missing.length > 0) {
    const lines = missing.flatMap((m) => [`  - ${m.name}: tried`, ...m.tried.map((p) => `      ${p}`)]);
    throw new Error(
      `vercel: could not resolve runtime assets needed to bake the base snapshot:
` + lines.join("\n") + `

If running from the monorepo, ensure \`pnpm -w build\` has run so packages/ctl/dist/bin.cjs exists.`
    );
  }
  return out;
}
function guessRepoRoot() {
  let cur = SELF;
  for (let i = 0; i < 8; i++) {
    if (existsSync(resolve(cur, "pnpm-workspace.yaml"))) return cur;
    const parent = dirname(cur);
    if (parent === cur) break;
    cur = parent;
  }
  return SELF;
}
var BUILDER_TIMEOUT_MS = 25 * 6e4;
var SHELL = "/bin/bash";
async function prepareVercel(opts = {}) {
  await ensureVercelCredentials();
  await ensureFreshCredentials();
  const creds2 = resolveCredentials();
  const log = opts.onLog ?? (() => {
  });
  const progress = (s) => log(`prepare-vercel: ${s}`);
  const assets = resolveRuntimeAssets({
    cliRuntimeRoot: opts.cliRuntimeRoot ?? findStagedCliRuntimeRoot(),
    repoRoot: opts.repoRoot
  });
  const contextSha = await computeContextSha256(
    assets.map((a) => ({ rel: a.name, abs: a.localPath }))
  );
  const existing = readPreparedState();
  if (!opts.force && existing.base) {
    const stillThere = await snapshotExists(existing.base.snapshotId, creds2);
    if (stillThere && existing.base.contextSha256 === contextSha) {
      progress(
        `base snapshot ${existing.base.snapshotId} already exists (fingerprint ${contextSha.slice(0, 12)} matches); skipping (pass --force to rebuild)`
      );
      return { snapshotName: existing.base.snapshotId };
    }
    if (!stillThere) {
      progress(`recorded base snapshot ${existing.base.snapshotId} is gone on Vercel; rebuilding`);
    } else {
      progress(
        `build context changed (was ${existing.base.contextSha256?.slice(0, 12) ?? "<none>"}, now ${contextSha.slice(0, 12)}); rebuilding`
      );
    }
  }
  progress(`creating builder sandbox (node24, ${String(opts.vcpus ?? 4)} vcpus)`);
  const sb = await Sandbox.create({
    runtime: "node24",
    resources: { vcpus: opts.vcpus ?? 4 },
    timeout: BUILDER_TIMEOUT_MS,
    tags: { agentbox: "true", "agentbox.role": "prepare" },
    persistent: false,
    ...creds2
  });
  progress(`builder sandbox ${sb.name} up`);
  progress(`uploading ${String(assets.length)} runtime asset(s)`);
  await sb.writeFiles(
    await Promise.all(
      assets.map(async (a) => ({
        path: a.remotePath,
        content: await readFile2(a.localPath),
        mode: a.remoteMode
      }))
    )
  );
  progress("running provision.sh (this takes a few minutes)");
  const install = await sb.runCommand({
    cmd: SHELL,
    args: ["-lc", "bash /tmp/agentbox-provision.sh 2>&1"],
    sudo: true,
    stdout: lineSink((l) => log(`[provision] ${l}`)),
    stderr: lineSink((l) => log(`[provision] ${l}`))
  });
  if (install.exitCode !== 0) {
    throw new Error(`provision.sh failed on the builder sandbox (exit ${String(install.exitCode)})`);
  }
  progress("provision.sh complete");
  await stageAgentConfig(sb, opts.hostWorkspace, log);
  progress("creating base snapshot (expiration: never)");
  const snap = await sb.snapshot({ expiration: 0 });
  progress(`snapshot created: ${snap.snapshotId}`);
  const cliStamp = readCliStamp();
  writePreparedState({
    schema: 1,
    base: {
      snapshotId: snap.snapshotId,
      contextSha256: contextSha,
      cliVersion: cliStamp.cliVersion,
      cliCommit: cliStamp.cliCommit,
      createdAt: (/* @__PURE__ */ new Date()).toISOString()
    }
  });
  progress(`wrote ${preparedStatePath()}`);
  progress("deleting builder sandbox");
  try {
    await sb.delete();
    progress("builder sandbox deleted");
  } catch (err) {
    progress(
      `builder delete failed (left for Vercel reaper): ${err instanceof Error ? err.message : String(err)}`
    );
  }
  progress(`prepare complete \u2014 base snapshot ${snap.snapshotId}`);
  return { snapshotName: snap.snapshotId };
}
async function snapshotExists(snapshotId, creds2) {
  try {
    const snap = await Snapshot.get({ snapshotId, ...creds2 });
    return snap.status === "created";
  } catch {
    return false;
  }
}
async function stageAgentConfig(sb, hostWorkspace, log) {
  const progress = (s) => log(`prepare-vercel: ${s}`);
  progress("staging host agent static config");
  const stagings = [];
  try {
    const claudeTar = await stageClaudeStaticForUpload({ hostWorkspace });
    for (const w of claudeTar.warnings) progress(w);
    if (claudeTar.tarballPath) stagings.push({ kind: "claude", tar: claudeTar, dest: "/home/vscode/.claude" });
    else await claudeTar.cleanup();
    const codexTar = await stageCodexStaticForUpload();
    for (const w of codexTar.warnings) progress(w);
    if (codexTar.tarballPath) stagings.push({ kind: "codex", tar: codexTar, dest: "/home/vscode/.codex" });
    else await codexTar.cleanup();
    const opencodeTar = await stageOpencodeStaticForUpload();
    for (const w of opencodeTar.warnings) progress(w);
    if (opencodeTar.tarballPath) stagings.push({ kind: "opencode", tar: opencodeTar, dest: "/home/vscode/.local/share/opencode" });
    else await opencodeTar.cleanup();
    for (const s of stagings) {
      const remote = `/tmp/agentbox-${s.kind}-static.tar.gz`;
      progress(`uploading ${s.kind} static config`);
      await sb.writeFiles([{ path: remote, content: await readFile2(s.tar.tarballPath) }]);
      const extract = `sudo -u vscode mkdir -p ${s.dest} && sudo -u vscode tar -xzf ${remote} -C ${s.dest} --no-same-permissions --no-same-owner -m && rm -f ${remote}`;
      const r = await sb.runCommand({ cmd: SHELL, args: ["-lc", extract], sudo: true });
      if (r.exitCode !== 0) {
        progress(`WARN: ${s.kind} static extract failed (exit ${String(r.exitCode)}) \u2014 continuing`);
      } else {
        progress(`baked ${s.kind} static config into snapshot`);
      }
    }
  } finally {
    for (const s of stagings) await s.tar.cleanup();
  }
}
function lineSink(onLine) {
  let buf = "";
  return new Writable({
    write(chunk, _enc, cb) {
      buf += chunk.toString("utf8");
      let nl;
      while ((nl = buf.indexOf("\n")) !== -1) {
        onLine(buf.slice(0, nl));
        buf = buf.slice(nl + 1);
      }
      cb();
    },
    final(cb) {
      if (buf.length > 0) onLine(buf);
      cb();
    }
  });
}
var prepareVercelProvider = (req) => prepareVercel({
  name: req.name,
  hostWorkspace: req.hostWorkspace ?? process.cwd(),
  force: req.force,
  onLog: req.onLog
});
async function buildVercelAttach(box, kind, opts) {
  const sandboxId = box.cloud?.sandboxId;
  if (!sandboxId) {
    throw new Error(`vercel box ${box.name} has no sandboxId \u2014 record is malformed`);
  }
  const det = await detectSbx();
  if (!det.installed || !det.bin) {
    throw new Error(
      "Vercel interactive attach needs the Vercel `sandbox` CLI \u2014 run `agentbox vercel login` (it installs it) or `npm install -g sandbox`."
    );
  }
  await ensureFreshCredentials();
  const { token, teamId, projectId } = resolveCredentials();
  const interactive = (kind === "shell" || kind === "agent") && !opts?.detached;
  const envPrelude = "export LANG=C.UTF-8 LC_ALL=C.UTF-8 TERM=xterm-256color; ";
  const inner = envPrelude + renderInnerCommand(kind, opts);
  const argv = [
    det.bin,
    "exec",
    "--sudo",
    ...interactive ? ["-i"] : [],
    "--project",
    projectId,
    "--scope",
    teamId,
    sandboxId,
    "--",
    "sudo",
    "-u",
    "vscode",
    "-H",
    "bash",
    "-lc",
    inner
  ];
  return { argv, env: { VERCEL_AUTH_TOKEN: token } };
}
var BACKEND_NAME = "vercel";
var cloudProvider = createCloudProvider(vercelBackend, {
  // Vercel couples RAM to vCPU at 2048 MB/vCPU; disk is a fixed 32 GB NVMe.
  defaultResources: { cpu: 2, memory: 4, disk: 32 },
  launchDockerd: false
});
var vercelCheckpoint = {
  async create(box, name) {
    if (!box.projectRoot) {
      throw new Error(
        "cloud checkpoint requires the box to have a project root (run `agentbox checkpoint` from inside the project)"
      );
    }
    if (!box.cloud?.sandboxId) {
      throw new Error(`vercel box ${box.name} has no sandboxId \u2014 record is malformed`);
    }
    const snapshotId = await snapshotVercelSandbox(box.cloud.sandboxId);
    try {
      await recordBox({ ...box, cloud: { ...box.cloud, lastState: "paused" } });
    } catch {
    }
    const info = await writeCloudCheckpointManifest(box.projectRoot, BACKEND_NAME, name, {
      snapshotName: snapshotId,
      sourceBoxId: box.id,
      sourceBoxName: box.name
    });
    return { ref: info.name };
  },
  async list(projectRoot) {
    const entries = await listCloudCheckpoints(projectRoot, BACKEND_NAME);
    return entries.map((e) => ({ ref: e.name, createdAt: e.manifest.createdAt }));
  },
  async remove(projectRoot, ref) {
    const entry = await resolveCloudCheckpoint(projectRoot, BACKEND_NAME, ref);
    if (!entry) return;
    try {
      await deleteVercelSnapshot(entry.manifest.snapshotName);
    } catch {
    }
    await removeCloudCheckpointDir(projectRoot, BACKEND_NAME, ref);
  }
};
var vercelProvider = {
  ...cloudProvider,
  prepare: prepareVercelProvider,
  buildAttach: buildVercelAttach,
  checkpoint: vercelCheckpoint
};
export {
  DEFAULT_BOX_IMAGE_REF,
  RUNTIME_ASSETS,
  buildVercelAttach,
  candidatesFor,
  ensureVercelBaseSnapshot,
  ensureVercelCredentials,
  ensureVercelEnvLoaded,
  findStagedCliRuntimeRoot,
  maskKey,
  prepareVercel,
  prepareVercelProvider,
  preparedStatePath,
  readPreparedState,
  readVercelCredStatus,
  reloadVercelEnv,
  resolveRuntimeAssets,
  secretsPath,
  updatePreparedState,
  vercelBackend,
  vercelProvider,
  writePreparedState
};
//# sourceMappingURL=dist-RAZP76VX.js.map