#!/usr/bin/env node
import {
  DEFAULT_RELAY_PORT,
  readBoxStatus
} from "./chunk-ZJXTIH6C.js";

// src/commands/_cloud-attach.ts
import { spawn as spawn3 } from "child_process";
import { appendFileSync } from "fs";
import { homedir } from "os";
import { join as join2 } from "path";
import { spinner } from "@clack/prompts";

// src/provider/registry.ts
var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
function isKnownProvider(name) {
  return KNOWN.includes(name);
}
async function getProvider(name) {
  switch (name) {
    case "docker": {
      const mod = await import("./dist-ASLPRUQR.js");
      return mod.dockerProvider;
    }
    case "daytona": {
      const mod = await import("./dist-PTJ6CEQY.js");
      await mod.ensureDaytonaCredentials();
      return mod.daytonaProvider;
    }
    case "hetzner": {
      const mod = await import("./dist-WMQDMTWS.js");
      await mod.ensureHetznerCredentials();
      return mod.hetznerProvider;
    }
    case "vercel": {
      const mod = await import("./dist-RAZP76VX.js");
      await mod.ensureVercelCredentials();
      return mod.vercelProvider;
    }
    default:
      throw new Error(`unknown sandbox provider: ${String(name)}`);
  }
}
async function providerForBox(box) {
  return getProvider(box.provider ?? "docker");
}
async function providerForCreate(choice) {
  const flag = choice.flag?.trim();
  const name = flag && flag.length > 0 ? flag : choice.config.box.provider;
  if (typeof name !== "string" || name.length === 0 || !isKnownProvider(name)) {
    throw new Error(
      `unknown sandbox provider "${String(name)}" (known: ${KNOWN.join(", ")})`
    );
  }
  return getProvider(name);
}

// src/wrapped-pty/run.ts
import { spawn as spawn2, spawnSync } from "child_process";

// src/pty/pty-backend.ts
async function loadPtyBackend() {
  try {
    const ptyMod = await import("@homebridge/node-pty-prebuilt-multiarch");
    const xtermMod = await import("@xterm/headless");
    const spawn4 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
    const Terminal = xtermMod["Terminal"] ?? xtermMod["default"]?.["Terminal"];
    if (typeof spawn4 !== "function" || typeof Terminal !== "function") {
      return null;
    }
    return {
      ptySpawn: spawn4,
      termCtor: Terminal
    };
  } catch {
    return null;
  }
}

// src/terminal/host.ts
import { spawn } from "child_process";
function detectHostTerminal(env = process.env) {
  const tmux = env["TMUX"];
  if (tmux && tmux.length > 0) return "tmux";
  const termProgram = env["TERM_PROGRAM"];
  if (termProgram === "iTerm.app") return "iterm2";
  return "unknown";
}
function shellQuote(s) {
  if (s.length === 0) return "''";
  return "'" + s.replace(/'/g, "'\\''") + "'";
}
function appleScriptEscape(s) {
  return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
}
function shellJoin(argv) {
  return argv.map(shellQuote).join(" ");
}
async function spawnInNewTerminal(args) {
  if (args.host === "tmux") return spawnInTmux(args);
  return spawnInITerm2(args);
}
async function spawnInTmux(args) {
  const cmdStr = shellJoin(args.argv);
  let tmuxArgv;
  let noteKind;
  if (args.mode === "split") {
    tmuxArgv = ["split-window", "-h", "-c", args.cwd, "--", cmdStr];
    noteKind = "tmux split";
  } else {
    tmuxArgv = ["new-window", "-n", args.title, "-c", args.cwd, "--", cmdStr];
    noteKind = "tmux window";
  }
  const r = await runQuiet("tmux", tmuxArgv);
  if (r.code !== 0) {
    return {
      launched: false,
      note: "",
      error: `tmux ${tmuxArgv.join(" ")} exited ${String(r.code)}: ${r.stderr.trim()}`
    };
  }
  return {
    launched: true,
    note: `Attached in new ${noteKind}.`
  };
}
async function spawnInITerm2(args) {
  const inner = shellJoin(args.argv);
  const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${inner}`;
  const cmdLit = `"${appleScriptEscape(cmdLine)}"`;
  let lines;
  let noteKind;
  switch (args.mode) {
    case "split":
      lines = [
        'tell application "iTerm"',
        "  tell current session of current window to set _s to (split vertically with default profile)",
        `  tell _s to write text ${cmdLit}`,
        "end tell"
      ];
      noteKind = "iTerm2 split";
      break;
    case "tab":
      lines = [
        'tell application "iTerm"',
        "  tell current window to set _t to (create tab with default profile)",
        `  tell current session of _t to write text ${cmdLit}`,
        "end tell"
      ];
      noteKind = "iTerm2 tab";
      break;
    case "window":
      lines = [
        'tell application "iTerm"',
        "  set _w to (create window with default profile)",
        `  tell current session of _w to write text ${cmdLit}`,
        "end tell"
      ];
      noteKind = "iTerm2 window";
      break;
  }
  const r = await runQuiet("osascript", ["-e", lines.join("\n")]);
  if (r.code !== 0) {
    return {
      launched: false,
      note: "",
      error: `osascript exited ${String(r.code)}: ${r.stderr.trim()}`
    };
  }
  return {
    launched: true,
    note: `Attached in new ${noteKind}.`
  };
}
function runQuiet(cmd, argv) {
  return new Promise((resolve) => {
    const child = spawn(cmd, argv, { stdio: ["ignore", "ignore", "pipe"] });
    let stderr = "";
    child.stderr?.on("data", (chunk) => {
      stderr += chunk.toString("utf8");
    });
    child.on("error", (err) => {
      resolve({ code: 127, stderr: err.message });
    });
    child.on("exit", (code) => {
      resolve({ code: typeof code === "number" ? code : 1, stderr });
    });
  });
}

// src/terminal/title.ts
var ESC = "\x1B";
var BEL = "\x07";
function sanitize(title) {
  return title.replace(/[\x00-\x1f\x7f]/g, " ").trim();
}
function setTerminalTitle(title, stream = process.stdout) {
  if (!stream.isTTY) return;
  stream.write(`${ESC}]0;${sanitize(title)}${BEL}`);
}
function pushTerminalTitle(stream = process.stdout) {
  if (!stream.isTTY) return;
  stream.write(`${ESC}[22;2t`);
}
function popTerminalTitle(stream = process.stdout) {
  if (!stream.isTTY) return;
  stream.write(`${ESC}[23;2t`);
}

// src/wrapped-pty/input-router.ts
var KEY_ENTER = 13;
var KEY_LF = 10;
var KEY_ESC = 27;
var KEY_CTRL_C = 3;
var KEY_Y_LOW = 121;
var KEY_Y_UP = 89;
var KEY_N_LOW = 110;
var KEY_N_UP = 78;
var KEY_LEADER = 1;
var KEY_CTRL_V = 22;
var KEY_A_LOW = 97;
var KEY_V_LOW = 118;
function parseCsiKey(buf, i) {
  if (buf[i] !== KEY_ESC || buf[i + 1] !== 91) return null;
  const params = [];
  let val = -1;
  for (let j = i + 2; j < buf.length; j++) {
    const b = buf[j];
    if (b !== void 0 && b >= 48 && b <= 57) {
      val = (val < 0 ? 0 : val) * 10 + (b - 48);
      continue;
    }
    if (b === 59) {
      params.push(val);
      val = -1;
      continue;
    }
    if (b === 58) {
      params.push(val);
      val = -1;
      while (j + 1 < buf.length && buf[j + 1] !== 59 && buf[j + 1] !== 117 && buf[j + 1] !== 126) {
        j++;
      }
      continue;
    }
    if (b === 117 || b === 126) {
      if (val >= 0) params.push(val);
      const len = j - i + 1;
      const modsToCtrl = (m) => (m - 1 & 4) !== 0;
      if (b === 117) {
        const code2 = params[0];
        if (code2 === void 0 || code2 < 0) return null;
        return { len, code: code2, ctrl: modsToCtrl(params[1] ?? 1) };
      }
      if (params[0] !== 27) return null;
      const code = params[2];
      if (code === void 0 || code < 0) return null;
      return { len, code, ctrl: modsToCtrl(params[1] ?? 1) };
    }
    return null;
  }
  return null;
}
var DEFAULT_LEADER_TIMEOUT_MS = 2e3;
function createInputRouter(opts) {
  let active = null;
  let disposed = false;
  const leaderChords = opts.leaderChords ?? {};
  const leaderEnabled = Object.keys(leaderChords).length > 0;
  const onPasteImage = opts.onPasteImage;
  const pasteEnabled = typeof onPasteImage === "function";
  let pasteInFlight = false;
  const leaderTimeoutMs = opts.leaderTimeoutMs ?? DEFAULT_LEADER_TIMEOUT_MS;
  const setTimer = opts.setTimer ?? ((ms, fn) => setTimeout(fn, ms));
  const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
  let leader = false;
  let leaderTimer = null;
  const disarmLeader = () => {
    if (leaderTimer != null) {
      clearTimer(leaderTimer);
      leaderTimer = null;
    }
  };
  const exitLeader = () => {
    if (!leader) return;
    leader = false;
    disarmLeader();
    opts.onLeaderChange?.(false);
  };
  const enterLeader = () => {
    leader = true;
    disarmLeader();
    leaderTimer = setTimer(leaderTimeoutMs, () => {
      leaderTimer = null;
      exitLeader();
    });
    opts.onLeaderChange?.(true);
  };
  const resolveLeaderByte = (b) => {
    if (b === KEY_LEADER) {
      exitLeader();
      opts.onForward(Buffer.from([KEY_LEADER]));
      return;
    }
    if (b === KEY_ESC) {
      exitLeader();
      return;
    }
    const action = leaderChords[String.fromCharCode(b).toLowerCase()];
    if (action) {
      exitLeader();
      opts.onAction?.(action);
      return;
    }
    exitLeader();
    opts.onForward(Buffer.from([b]));
  };
  const settle = (answer, cancelled) => {
    if (!active) return;
    const body = {
      id: active.ev.id,
      answer,
      ...cancelled ? { cancelled: true } : {}
    };
    const p = active;
    active = null;
    p.resolve(body);
    opts.onAnswer(body);
  };
  const handleCapturedByte = (b) => {
    if (!active) return;
    if (b === KEY_Y_LOW || b === KEY_Y_UP) {
      settle("y");
      return;
    }
    if (b === KEY_N_LOW || b === KEY_N_UP) {
      settle("n");
      return;
    }
    if (b === KEY_ESC || b === KEY_CTRL_C) {
      settle("n", true);
      return;
    }
    if (b === KEY_ENTER || b === KEY_LF) {
      const def = active.ev.defaultAnswer ?? "n";
      settle(def);
      return;
    }
  };
  const triggerPaste = (reemit) => {
    if (pasteInFlight) return;
    pasteInFlight = true;
    const done = () => {
      pasteInFlight = false;
      if (!disposed) opts.onForward(reemit);
    };
    void Promise.resolve().then(() => onPasteImage?.()).then(done, done);
  };
  const feedSteady = (buf) => {
    let chunkStart = 0;
    const flushChunk = (end) => {
      if (end > chunkStart) opts.onForward(buf.subarray(chunkStart, end));
      chunkStart = end;
    };
    let i = 0;
    while (i < buf.length) {
      const byte = buf[i];
      if (byte === void 0) {
        i++;
        continue;
      }
      if (leader) {
        const k = parseCsiKey(buf, i);
        if (k) {
          if (k.ctrl && k.code === KEY_A_LOW) {
            exitLeader();
            opts.onForward(Buffer.from([KEY_LEADER]));
          } else {
            const action = leaderChords[String.fromCharCode(k.code).toLowerCase()];
            exitLeader();
            if (action) opts.onAction?.(action);
            else opts.onForward(buf.subarray(i, i + k.len));
          }
          i += k.len;
          chunkStart = i;
          continue;
        }
        resolveLeaderByte(byte);
        i += 1;
        chunkStart = i;
        continue;
      }
      if (leaderEnabled && byte === KEY_LEADER) {
        flushChunk(i);
        enterLeader();
        i += 1;
        chunkStart = i;
        continue;
      }
      if (leaderEnabled && byte === KEY_ESC) {
        const k = parseCsiKey(buf, i);
        if (k && k.ctrl && k.code === KEY_A_LOW) {
          flushChunk(i);
          enterLeader();
          i += k.len;
          chunkStart = i;
          continue;
        }
      }
      if (pasteEnabled && byte === KEY_ESC) {
        const k = parseCsiKey(buf, i);
        if (k && k.ctrl && k.code === KEY_V_LOW) {
          flushChunk(i);
          const seq = Buffer.from(buf.subarray(i, i + k.len));
          i += k.len;
          chunkStart = i;
          triggerPaste(seq);
          continue;
        }
      }
      if (pasteEnabled && byte === KEY_CTRL_V) {
        flushChunk(i);
        i += 1;
        chunkStart = i;
        triggerPaste(Buffer.from([KEY_CTRL_V]));
        continue;
      }
      i += 1;
    }
    flushChunk(buf.length);
  };
  return {
    get capturing() {
      return active !== null;
    },
    feed(buf) {
      if (disposed) return;
      if (active) {
        if (buf.length > 1 && buf[0] === KEY_ESC) return;
        for (let i = 0; i < buf.length; i++) {
          const byte = buf[i];
          if (byte === void 0) continue;
          if (active) {
            handleCapturedByte(byte);
          } else {
            opts.onForward(buf.subarray(i));
            return;
          }
        }
        return;
      }
      if (!leaderEnabled && !pasteEnabled) {
        opts.onForward(buf);
        return;
      }
      feedSteady(buf);
    },
    capture(ev) {
      return new Promise((resolve, reject) => {
        if (leader) exitLeader();
        if (active) {
          settle("n", true);
        }
        active = { ev, resolve, reject };
      });
    },
    abort(reason) {
      if (!active) return;
      const p = active;
      active = null;
      const msg = reason === "pty-exit" ? "pty exited" : "resolved by sibling wrapper";
      p.reject(new Error(msg));
    },
    dispose() {
      if (disposed) return;
      disposed = true;
      disarmLeader();
      if (active) {
        const p = active;
        active = null;
        p.reject(new Error("input router disposed"));
      }
    }
  };
}

// src/dashboard/sidebar.ts
function ellipsize(s, max) {
  if (max <= 0) return "";
  if (s.length <= max) return s;
  if (max === 1) return "\u2026";
  return s.slice(0, max - 1) + "\u2026";
}
function ellipsizeHead(s, max) {
  if (max <= 0) return "";
  if (s.length <= max) return s;
  if (max === 1) return "\u2026";
  return "\u2026" + s.slice(s.length - (max - 1));
}
function activityCell(b) {
  if (b.pendingPrompt) return "\u25B2 prompt";
  if (b.checkpointing) return "\u25C6 checkpoint";
  if (b.state !== "running") return `[${b.state}]`;
  switch (b.activity) {
    case "working":
      return "\u25CF working";
    case "idle":
      return "\u25CB idle";
    case "waiting":
      return "\u25D0 waiting";
    default:
      return "? unknown";
  }
}
var NEW_BOX_ID = "__agentbox_new__";
var NEW_BOX_LABEL = "+ New box";
var SIDEBAR_HEADER = "AgentBox";
function topBorder(label, w) {
  const lead = `\u2500\u2500\u2500\u2500 ${label} `;
  if (lead.length >= w) return lead.slice(0, w);
  return lead + "\u2500".repeat(w - lead.length);
}
function fit(s, w) {
  if (s.length === w) return s;
  if (s.length > w) return s.slice(0, w);
  return s + " ".repeat(w - s.length);
}
function center(s, w) {
  if (s.length >= w) return s.slice(0, w);
  const pad = w - s.length;
  const leftPad = Math.floor(pad / 2);
  return " ".repeat(leftPad) + s + " ".repeat(pad - leftPad);
}
function projectLabel(project) {
  if (!project) return "(no project)";
  const parts = project.split("/").filter(Boolean);
  return parts[parts.length - 1] ?? project;
}
function stripTitleGlyph(s) {
  const t = s.replace(/^[\s\p{S}*·]+/u, "");
  return t.length > 0 ? t : s.trim();
}
function boxRow(b, marker, w) {
  const numStr = b.index != null ? `${b.index} ` : "";
  const status = activityCell(b);
  const left = `${marker}${numStr}`;
  const room = w - left.length - status.length - 2;
  if (room <= 0) return fit(`${left}${status}`, w);
  const middle = b.state === "running" && b.sessionTitle ? ellipsize(stripTitleGlyph(b.sessionTitle), room) : ellipsizeHead(b.name, room);
  return fit(`${left}${middle}`, w - status.length - 1) + status + " ";
}
function sidebarLines(boxes, selectedId, w, h) {
  const lines = [topBorder(SIDEBAR_HEADER, w), fit("", w)];
  const rowOwner = [null, null];
  const headerRows = [true, false];
  const push = (line, owner, header) => {
    lines.push(fit(line, w));
    rowOwner.push(owner);
    headerRows.push(header);
  };
  let prevProject;
  let seenGroup = false;
  for (const b of boxes) {
    const marker = b.id === selectedId ? "\u25B8" : " ";
    if (b.id === NEW_BOX_ID) {
      push(`${marker}${NEW_BOX_LABEL}`, b.id, false);
      continue;
    }
    if (!seenGroup || b.project !== prevProject) {
      push(center(` \u2500\u2500 ${projectLabel(b.project)} \u2500\u2500 `, w), null, true);
      prevProject = b.project;
      seenGroup = true;
    }
    push(boxRow(b, marker, w), b.id, false);
  }
  if (boxes.length === 0) push(" (no boxes)", null, false);
  while (lines.length < h) push("", null, false);
  return {
    lines: lines.slice(0, h),
    rowOwner: rowOwner.slice(0, h),
    headerRows: headerRows.slice(0, h)
  };
}
function menuLines(boxName, w, h) {
  const body = [
    "",
    `  No agent session in ${boxName}.`,
    "",
    "   [c]  Start Claude",
    "   [x]  Start Codex",
    "   [o]  Start OpenCode",
    "   [s]  Open a shell",
    "",
    "  Ctrl+Option+\u2191/\u2193 switch \xB7 Ctrl-a then c/s/u/q (code/screen/url/quit)"
  ];
  const top = Math.max(0, Math.floor((h - body.length) / 2));
  const out = [];
  for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? "", w));
  return out;
}
function lifecycleMenuLines(boxName, state, confirmDestroy, w, h) {
  const body = confirmDestroy ? [
    "",
    `  Destroy ${boxName}?`,
    "  This removes the container and its volumes.",
    "",
    "   [y]  Yes, destroy",
    "   [any other key]  Cancel"
  ] : [
    "",
    `  Box ${boxName} is ${state}.`,
    "",
    state === "paused" ? "   [u]  Unpause" : "   [s]  Start",
    "   [d]  Destroy",
    "",
    "  Ctrl+Option+\u2191/\u2193 switch \xB7 Ctrl-a then q quit"
  ];
  const top = Math.max(0, Math.floor((h - body.length) / 2));
  const out = [];
  for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? "", w));
  return out;
}
function createMenuLines(where, w, h) {
  const body = [
    "",
    "  Create a new box",
    "",
    "   [c]  Create + launch Claude",
    "   [x]  Create + launch Codex",
    "   [o]  Create + launch OpenCode",
    "   [n]  Create only",
    "",
    `  in ${where}`,
    "",
    "  Ctrl+Option+\u2191/\u2193 switch \xB7 Ctrl-a then q quit"
  ];
  const top = Math.max(0, Math.floor((h - body.length) / 2));
  const out = [];
  for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? "", w));
  return out;
}
var BAR_BG = "\x1B[48;2;48;48;48m";
var BAR_BASE = BAR_BG + "\x1B[38;5;250m";
var BAR_BRAND = "\x1B[48;5;39m\x1B[38;5;16m";
var BRAND_BOLD = "\x1B[1m";
var BRAND_NOBOLD = "\x1B[22m";
var HINT_KEY = "\x1B[38;5;255m";
var HINT_TXT = "\x1B[38;5;245m";
var BAR_RESET = "\x1B[0m";
var SWITCH_HINT = ["Control+Option+\u2191/\u2193", "switch"];
var HINT_GROUPS = [
  SWITCH_HINT,
  ["Control+a c", "code"],
  ["Control+a s", "screen"],
  ["Control+a u", "url"],
  ["Control+a q", "quit"]
];
var COLLAPSED_HINT_GROUPS = [
  SWITCH_HINT,
  ["Control+a", "more"]
];
var ADVANCED_HINT_GROUPS = [
  ["c", "code"],
  ["s", "screen"],
  ["u", "url"],
  ["t", "stop"],
  ["p", "pause"],
  ["d", "destroy"],
  ["q", "quit"]
];
function statusLine(box, w, stateLabel, groups = HINT_GROUPS, fallbackGroups) {
  const state = stateLabel ?? (box ? box.state === "running" ? box.activity ?? "unknown" : box.state : "");
  const brandPrefix = box ? " agentbox \u25B8 " : " agentbox ";
  const base = box ? `${box.name} (${state})` : "";
  const coreMain = box ? `${base} ` : "";
  const corePlain = brandPrefix + coreMain;
  const SEP = "   \u2502   ";
  const renderHints = (g) => ({
    plain: g.map(([k, l]) => `${k}: ${l}`).join(SEP) + " ",
    styled: g.map(([k, l]) => `${HINT_KEY}${k}${HINT_TXT}: ${l}`).join(`${HINT_TXT}${SEP}`) + " "
  });
  let hints = null;
  for (const g of [groups, fallbackGroups ?? COLLAPSED_HINT_GROUPS]) {
    const h = renderHints(g);
    if (corePlain.length + h.plain.length + 1 <= w) {
      hints = h;
      break;
    }
  }
  if (!hints) {
    return BAR_BASE + BAR_BRAND + fit(corePlain, w) + BAR_RESET;
  }
  const room = w - corePlain.length - hints.plain.length - 1;
  let titleSeg = "";
  if (box?.sessionTitle && room >= 7) {
    titleSeg = ` \u2014 ${ellipsize(box.sessionTitle, Math.min(40, room - 3))}`;
  }
  const leftPlain = brandPrefix + base + titleSeg + (box ? " " : "");
  const leftStyled = BAR_BRAND + brandPrefix + BRAND_BOLD + base + titleSeg + (box ? " " : "") + BRAND_NOBOLD;
  const gap = w - leftPlain.length - hints.plain.length;
  return BAR_BASE + leftStyled + BAR_BASE + " ".repeat(gap) + hints.styled + BAR_RESET;
}

// src/wrapped-pty/footer.ts
var SPINNER_FRAMES = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
var URGENT = "\x1B[38;5;220m\x1B[1m";
var TITLE = "\x1B[1m\x1B[38;5;253m";
var TXT = "\x1B[38;5;250m";
var SUBTLE = "\x1B[38;5;245m";
var RESET = "\x1B[0m";
var UNDERLINE = "\x1B[4m";
var NO_UNDERLINE = "\x1B[24m";
var QUESTION_ACCENT = "\x1B[38;5;51m\x1B[1m";
var NOTICE_BG = "\x1B[48;5;220m";
var NOTICE_FG = "\x1B[38;5;16m\x1B[1m";
var FLASH_FG = "\x1B[38;5;150m\x1B[1m";
var COLLAPSED_HINTS_PLAIN = [
  ["Control+a", "Actions"]
];
var COLLAPSED_HINTS_DETACHABLE = [
  ["Control+a", "Actions"],
  ["Control+a d", "detach"]
];
var DETACH_PIN_HINTS = [
  ["Control+a d", "detach"]
];
var DETACHABLE_LEADER_HINTS = [
  ["c", "code"],
  ["s", "screen"],
  ["u", "url"],
  ["d", "detach"]
];
var PLAIN_LEADER_HINTS = [
  ["c", "code"],
  ["s", "screen"],
  ["u", "url"]
];
function padTo(visible, width) {
  if (visible.length === width) return visible;
  if (visible.length > width) {
    if (width <= 1) return visible.slice(0, width);
    return visible.slice(0, width - 1) + "\u2026";
  }
  return visible + " ".repeat(width - visible.length);
}
function answerChip(defaultAnswer) {
  const yesKey = "y Yes";
  const noKey = "n No";
  const sep = " \xB7 ";
  const yesIsDefault = defaultAnswer === "y";
  const yes = yesIsDefault ? `${UNDERLINE}${yesKey}${NO_UNDERLINE}` : yesKey;
  const no = yesIsDefault ? noKey : `${UNDERLINE}${noKey}${NO_UNDERLINE}`;
  const ansi = `${NOTICE_BG}${NOTICE_FG} ${yes}${sep}${no} ${RESET}`;
  const width = ` ${yesKey}${sep}${noKey} `.length;
  return { ansi, width };
}
function renderFooter(state, cols) {
  if (cols <= 0) return "";
  if (state.kind === "idle") {
    const sidebarBox = {
      id: "",
      // unused by statusLine
      name: state.boxName,
      state: "running",
      // we're attached, so the container is up
      activity: state.claudeActivity,
      sessionTitle: state.sessionTitle
    };
    const isClaude = state.mode === "claude";
    const detachable = state.detachable ?? isClaude;
    const stateLabel = isClaude ? void 0 : state.mode === "shell" ? "shell" : state.mode;
    if (state.leaderActive) {
      const leaderHints = detachable ? DETACHABLE_LEADER_HINTS : PLAIN_LEADER_HINTS;
      return statusLine(sidebarBox, cols, stateLabel, leaderHints);
    }
    const collapsed = detachable ? COLLAPSED_HINTS_DETACHABLE : COLLAPSED_HINTS_PLAIN;
    const fallback = detachable ? DETACH_PIN_HINTS : void 0;
    return statusLine(sidebarBox, cols, stateLabel, collapsed, fallback);
  }
  if (state.kind === "flash") {
    const prefix = " \u25B8 ";
    const inner2 = Math.max(0, cols - prefix.length);
    const message2 = padTo(state.message, inner2);
    return `${BAR_BG}${FLASH_FG}${prefix}${TXT}${message2}${RESET}`;
  }
  if (state.kind === "notice") {
    const spinner2 = SPINNER_FRAMES[state.frame % SPINNER_FRAMES.length];
    const prefix = ` ${spinner2} `;
    const inner2 = Math.max(0, cols - prefix.length);
    const message2 = padTo(state.message, inner2);
    return `${NOTICE_BG}${NOTICE_FG}${prefix}${message2}${RESET}`;
  }
  const chip = answerChip(state.prompt.defaultAnswer);
  const tag = " [!] ";
  const sep = "  ";
  const inner = Math.max(0, cols - tag.length - chip.width);
  const detailRaw = state.prompt.detail ?? "";
  let message = state.prompt.message;
  let detail = detailRaw;
  const messageBudget = Math.max(8, inner - (detail.length > 0 ? sep.length + 8 : 0));
  if (message.length > messageBudget) {
    message = message.slice(0, Math.max(0, messageBudget - 1)) + "\u2026";
  }
  const usedByMessage = message.length;
  const detailBudget = Math.max(0, inner - usedByMessage - sep.length);
  if (detail.length > detailBudget) {
    detail = detailBudget <= 1 ? "" : detail.slice(0, detailBudget - 1) + "\u2026";
  }
  const middlePlain = detail.length > 0 ? `${message}${sep}${detail}` : message;
  const padded = padTo(middlePlain, inner);
  return `${BAR_BG}${URGENT}${tag}${TXT}${padded}${RESET}${chip.ansi}`;
}
function cursorMoveTo(row, col) {
  return `\x1B[${String(row)};${String(col)}H`;
}
var CURSOR_SAVE = "\x1B7";
var CURSOR_RESTORE = "\x1B8";
var SYNC_BEGIN = "\x1B[?2026h";
var SYNC_END = "\x1B[?2026l";
var ALERT_BAND_ROWS = 3;
function blankBar(cols, bg) {
  return `${bg}${" ".repeat(Math.max(0, cols))}${RESET}`;
}
function renderPromptBand(prompt, cols, rows) {
  const tag = " [!] ";
  const indent = " ".repeat(tag.length);
  const contW = Math.max(0, cols - indent.length);
  const chip = answerChip(prompt.defaultAnswer);
  const title = (prompt.context?.command ?? "confirm").toUpperCase();
  const titleW = Math.max(0, cols - tag.length - chip.width);
  const titlePadded = padTo(title, titleW);
  const line1 = `${BAR_BG}${URGENT}${tag}${TITLE}${titlePadded}${RESET}${chip.ansi}`;
  const line2 = `${BAR_BG}${TXT}${indent}${padTo(prompt.message, contW)}${RESET}`;
  const line3 = `${BAR_BG}${SUBTLE}${indent}${padTo(prompt.detail ?? "", contW)}${RESET}`;
  return [line1, line2, line3].slice(0, rows);
}
function renderNoticeBand(message, frame, cols, rows) {
  const spinner2 = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
  const prefix = ` ${spinner2} `;
  const indent = " ".repeat(prefix.length);
  const firstW = Math.max(0, cols - prefix.length);
  const contW = Math.max(0, cols - indent.length);
  const out = [];
  let i = 0;
  for (let r = 0; r < rows; r++) {
    const isLast = r === rows - 1;
    const w = r === 0 ? firstW : contW;
    let cell;
    if (i >= message.length) {
      cell = " ".repeat(w);
    } else if (isLast) {
      cell = padTo(message.slice(i), w);
      i = message.length;
    } else {
      cell = message.slice(i, i + w).padEnd(w);
      i += w;
    }
    const lead = r === 0 ? prefix : indent;
    out.push(`${NOTICE_BG}${NOTICE_FG}${lead}${cell}${RESET}`);
  }
  return out;
}
function renderQuestionBand(payload, cols, rows) {
  const q = payload.questions[0];
  if (!q) return Array.from({ length: rows }, () => blankBar(cols, BAR_BG));
  const tag = " [?] ";
  const indent = " ".repeat(tag.length);
  const innerW = Math.max(0, cols - tag.length);
  const contW = Math.max(0, cols - indent.length);
  const header = q.header && q.header.trim().length > 0 ? q.header : "Question";
  const headerPadded = padTo(header, innerW);
  const line1 = `${BAR_BG}${QUESTION_ACCENT}${tag}${TXT}${headerPadded}${RESET}`;
  const questionText = padTo(q.question, contW);
  const line2 = `${BAR_BG}${TXT}${indent}${questionText}${RESET}`;
  const optLabels = q.options.map((o) => o.label).join(" \xB7 ");
  const optsLine = optLabels.length > 0 ? `options: ${optLabels}` : "";
  const optsPadded = padTo(optsLine, contW);
  const line3 = `${BAR_BG}${SUBTLE}${indent}${optsPadded}${RESET}`;
  return [line1, line2, line3].slice(0, rows);
}
function renderAlertBand(state, cols, rows = ALERT_BAND_ROWS) {
  if (cols <= 0 || rows <= 0) return Array.from({ length: Math.max(0, rows) }, () => "");
  if (state.kind === "prompt") return renderPromptBand(state.prompt, cols, rows);
  if (state.kind === "notice") return renderNoticeBand(state.message, state.frame, cols, rows);
  return renderQuestionBand(state.question, cols, rows);
}

// src/wrapped-pty/prompt-client.ts
import { request as httpRequest } from "http";
import { request as httpsRequest } from "https";
var INITIAL_BACKOFF_MS = 200;
var MAX_BACKOFF_MS = 5e3;
function subscribePrompts(opts) {
  let closed = false;
  let req = null;
  let res = null;
  let reconnectTimer = null;
  let backoffMs = INITIAL_BACKOFF_MS;
  let url;
  try {
    url = new URL(opts.relayBaseUrl);
  } catch (err) {
    if (opts.onError) opts.onError(err instanceof Error ? err : new Error(String(err)));
    return { close: () => {
    } };
  }
  const isHttps = url.protocol === "https:";
  const transport = isHttps ? httpsRequest : httpRequest;
  const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;
  function scheduleReconnect() {
    if (closed) return;
    const delay = backoffMs;
    backoffMs = Math.min(MAX_BACKOFF_MS, backoffMs * 2);
    reconnectTimer = setTimeout(() => {
      reconnectTimer = null;
      connect();
    }, delay);
    if (typeof reconnectTimer.unref === "function") reconnectTimer.unref();
  }
  let buffer = "";
  function consumeMessages() {
    let idx = buffer.indexOf("\n\n");
    while (idx !== -1) {
      const raw = buffer.slice(0, idx);
      buffer = buffer.slice(idx + 2);
      idx = buffer.indexOf("\n\n");
      if (raw.startsWith(":")) continue;
      let event = "";
      let dataLine = "";
      for (const line of raw.split("\n")) {
        if (line.startsWith("event:")) event = line.slice("event:".length).trim();
        else if (line.startsWith("data:")) dataLine = line.slice("data:".length).trim();
      }
      if (event === "prompt-ask" && dataLine.length > 0) {
        try {
          const ev = JSON.parse(dataLine);
          if (ev && typeof ev.id === "string") opts.onPrompt(ev);
        } catch {
        }
      } else if (event === "prompt-resolved" && dataLine.length > 0) {
        try {
          const payload = JSON.parse(dataLine);
          if (payload && typeof payload.id === "string") opts.onResolved(payload.id);
        } catch {
        }
      } else if (event === "notice-set" && dataLine.length > 0) {
        try {
          const ev = JSON.parse(dataLine);
          if (ev && typeof ev.id === "string") opts.onNotice?.(ev);
        } catch {
        }
      } else if (event === "notice-clear" && dataLine.length > 0) {
        try {
          const payload = JSON.parse(dataLine);
          if (payload && typeof payload.id === "string") opts.onNoticeCleared?.(payload.id);
        } catch {
        }
      }
    }
  }
  function connect() {
    if (closed) return;
    req = transport({
      host: url.hostname,
      port,
      method: "GET",
      path: `${url.pathname.replace(/\/$/, "")}/admin/prompts/stream?boxId=${encodeURIComponent(opts.boxId)}`,
      headers: { Accept: "text/event-stream" }
    });
    req.on("response", (r) => {
      res = r;
      if (r.statusCode !== 200) {
        if (opts.onError) opts.onError(new Error(`SSE stream returned ${String(r.statusCode)}`));
        r.resume();
        close();
        return;
      }
      backoffMs = INITIAL_BACKOFF_MS;
      r.setEncoding("utf8");
      r.on("data", (chunk) => {
        buffer += chunk;
        consumeMessages();
      });
      r.on("end", () => {
        if (!closed) scheduleReconnect();
      });
      r.on("error", () => {
        if (!closed) scheduleReconnect();
      });
    });
    req.on("error", () => {
      if (!closed) scheduleReconnect();
    });
    req.end();
  }
  function close() {
    if (closed) return;
    closed = true;
    if (reconnectTimer) clearTimeout(reconnectTimer);
    try {
      res?.destroy();
    } catch {
    }
    try {
      req?.destroy();
    } catch {
    }
  }
  connect();
  return { close };
}
function postAnswer(opts) {
  return new Promise((resolve) => {
    let url;
    try {
      url = new URL(opts.relayBaseUrl);
    } catch {
      resolve({ ok: false, status: 0 });
      return;
    }
    const isHttps = url.protocol === "https:";
    const transport = isHttps ? httpsRequest : httpRequest;
    const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;
    const json = JSON.stringify(opts.body);
    const req = transport(
      {
        host: url.hostname,
        port,
        method: "POST",
        path: `${url.pathname.replace(/\/$/, "")}/admin/prompts/answer`,
        headers: {
          "Content-Type": "application/json",
          "Content-Length": Buffer.byteLength(json).toString()
        },
        timeout: 3e3
      },
      (res) => {
        res.resume();
        const status = res.statusCode ?? 0;
        resolve({ ok: status === 204 || status === 404, status });
      }
    );
    req.on("error", () => resolve({ ok: false, status: 0 }));
    req.on("timeout", () => {
      req.destroy();
      resolve({ ok: false, status: 0 });
    });
    req.write(json);
    req.end();
  });
}

// src/wrapped-pty/run.ts
var FOOTER_ROWS = 1;
var MIN_INNER_ROWS = 5;
var STATUS_POLL_INTERVAL_MS = 3e3;
var SPINNER_INTERVAL_MS = 120;
var FLASH_DURATION_MS = 2e3;
var RAPID_RECONNECT_MS = 8e3;
var MAX_RAPID_RECONNECTS = 3;
var CHECKPOINT_DROP_GRACE_MS = 4e3;
var ACTION_FLASH = {
  screen: "Opening noVNC viewer\u2026",
  code: "Launching VS Code / Cursor\u2026",
  url: "Opening box URL\u2026"
};
var ACTION_CMD = {
  screen: { sub: "screen", flags: [] },
  // --no-wait: don't block on `wait-ready` — the box is already running.
  code: { sub: "code", flags: ["--no-wait"] },
  url: { sub: "url", flags: [] }
};
function buildAgentboxAttachArgv(mode, boxName) {
  if (mode !== "claude" && mode !== "codex" && mode !== "opencode") return null;
  return [mode, "attach", boxName, "--attach-in", "same"];
}
async function runWrappedAttach(opts) {
  const command = opts.command ?? "docker";
  const logErr = (msg) => {
    opts.onError?.(msg);
  };
  const openIn = opts.openIn ?? "same";
  if (openIn !== "same") {
    const subArgv = buildAgentboxAttachArgv(opts.mode, opts.boxName);
    const host = subArgv ? detectHostTerminal() : "unknown";
    if (subArgv && host !== "unknown" && process.argv[1]) {
      const r = await spawnInNewTerminal({
        host,
        mode: openIn,
        argv: [process.execPath, process.argv[1], ...subArgv],
        cwd: process.cwd(),
        title: opts.boxName
      });
      if (r.launched) {
        process.stdout.write(r.note + "\n");
        return 0;
      }
      if (r.error) logErr(r.error);
    }
  }
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
    return runFallback(command, opts.dockerArgv, opts.env);
  }
  const backend = await loadPtyBackend();
  if (!backend) {
    process.stderr.write(
      "agentbox: permission prompts disabled (node-pty backend unavailable)\n"
    );
    return runFallback(command, opts.dockerArgv, opts.env);
  }
  const cols = process.stdout.columns ?? 80;
  const rows = process.stdout.rows ?? 24;
  const innerRows = Math.max(1, rows - FOOTER_ROWS);
  let pty = backend.ptySpawn(command, opts.dockerArgv, {
    name: "xterm-256color",
    cols,
    rows: innerRows,
    env: opts.env ? { ...process.env, ...opts.env } : process.env
  });
  let lastSpawnAt = Date.now();
  const resizePty = (c, r) => {
    try {
      pty.resize(c, r);
    } catch {
    }
  };
  pushTerminalTitle();
  let lastEmittedTitle = opts.boxName;
  setTerminalTitle(lastEmittedTitle);
  const detachable = opts.detachable ?? opts.mode === "claude";
  let leaderActive = false;
  const buildIdle = (sessionTitle, claudeActivity) => ({
    kind: "idle",
    boxName: opts.boxName,
    sessionTitle,
    claudeActivity,
    mode: opts.mode,
    detachable,
    leaderActive
  });
  let footerState = buildIdle();
  let lastSessionTitle;
  let lastActivity;
  let capturingPrompt = null;
  let activeNotice = null;
  let reconnectBanner = null;
  let noticeFrame = 0;
  let questionPayload = null;
  let bandState = null;
  let bandReservedRows = 0;
  let spinnerTimer = null;
  let flashMessage = null;
  let flashTimer = null;
  let reconnecting = false;
  let reconnectAbort = null;
  let userDetached = false;
  let checkpointNoticeAt = 0;
  let checkpointNoticeClearedAt = 0;
  const reservedRows = () => FOOTER_ROWS + bandReservedRows;
  const bandFits = () => {
    const rs = process.stdout.rows ?? rows;
    return rs - FOOTER_ROWS - ALERT_BAND_ROWS >= MIN_INNER_ROWS;
  };
  const redrawChrome = () => {
    const cs = process.stdout.columns ?? cols;
    const rs = process.stdout.rows ?? rows;
    const footerLine = renderFooter(footerState, cs);
    let payload = SYNC_BEGIN + CURSOR_SAVE;
    if (bandReservedRows > 0 && bandState) {
      const bandLines = renderAlertBand(bandState, cs, bandReservedRows);
      for (let i = 0; i < bandLines.length; i++) {
        const row = rs - FOOTER_ROWS - (bandLines.length - i);
        payload += cursorMoveTo(row + 1, 1) + bandLines[i];
      }
    }
    payload += cursorMoveTo(rs, 1) + footerLine + CURSOR_RESTORE + SYNC_END;
    process.stdout.write(payload);
  };
  const recomputeFooter = () => {
    const collapsed = bandState !== null && bandReservedRows === 0;
    if (collapsed && reconnectBanner) {
      footerState = { kind: "notice", message: reconnectBanner, frame: noticeFrame };
    } else if (collapsed && capturingPrompt) {
      footerState = { kind: "prompt", prompt: capturingPrompt };
    } else if (collapsed && activeNotice) {
      footerState = { kind: "notice", message: activeNotice.message, frame: noticeFrame };
    } else if (flashMessage) {
      footerState = { kind: "flash", message: flashMessage };
    } else {
      footerState = buildIdle(lastSessionTitle, lastActivity);
    }
  };
  const recomputeBand = () => {
    if (reconnectBanner) {
      bandState = { kind: "notice", message: reconnectBanner, frame: noticeFrame };
    } else if (capturingPrompt) {
      bandState = { kind: "prompt", prompt: capturingPrompt };
    } else if (activeNotice) {
      bandState = { kind: "notice", message: activeNotice.message, frame: noticeFrame };
    } else if (questionPayload) {
      bandState = { kind: "question", question: questionPayload };
    } else {
      bandState = null;
    }
  };
  const relayoutForBand = () => {
    const cs = process.stdout.columns ?? cols;
    const rs = process.stdout.rows ?? rows;
    const inner = Math.max(1, rs - reservedRows());
    resizePty(cs, inner);
    process.stdout.write(`\x1B[1;${String(inner)}r`);
    let clear = SYNC_BEGIN + CURSOR_SAVE;
    for (let r = inner + 1; r <= rs; r++) clear += cursorMoveTo(r, 1) + "\x1B[2K";
    clear += CURSOR_RESTORE + SYNC_END;
    process.stdout.write(clear);
  };
  const applyBandChange = () => {
    recomputeBand();
    const wantRows = bandState && bandFits() ? ALERT_BAND_ROWS : 0;
    if (wantRows !== bandReservedRows) {
      bandReservedRows = wantRows;
      relayoutForBand();
    }
    recomputeFooter();
    redrawChrome();
  };
  const startSpinner = () => {
    if (spinnerTimer) return;
    spinnerTimer = setInterval(() => {
      noticeFrame++;
      if (bandState?.kind === "notice") {
        bandState = { kind: "notice", message: bandState.message, frame: noticeFrame };
        if (bandReservedRows === 0) recomputeFooter();
        redrawChrome();
      }
    }, SPINNER_INTERVAL_MS);
    if (typeof spinnerTimer.unref === "function") spinnerTimer.unref();
  };
  const stopSpinner = () => {
    if (spinnerTimer) {
      clearInterval(spinnerTimer);
      spinnerTimer = null;
    }
  };
  const wireOutput = () => {
    pty.onData((d) => {
      process.stdout.write(d);
      redrawChrome();
    });
  };
  wireOutput();
  const leaderChords = detachable ? { c: "code", s: "screen", u: "url", d: "detach" } : { c: "code", s: "screen", u: "url" };
  const runAction = (name) => {
    if (name === "detach") {
      if (!reconnecting) {
        userDetached = true;
        pty.write("d");
      }
      return;
    }
    const cliEntry = process.argv[1];
    if (typeof cliEntry === "string" && cliEntry.length > 0) {
      const cmd = ACTION_CMD[name];
      try {
        spawn2(
          process.execPath,
          [cliEntry, cmd.sub, opts.boxId, ...cmd.flags],
          { detached: true, stdio: "ignore" }
        ).unref();
      } catch (e) {
        logErr(`leader-action spawn (${name}) failed: ${e.message}`);
      }
    }
    flashMessage = ACTION_FLASH[name];
    if (flashTimer) clearTimeout(flashTimer);
    flashTimer = setTimeout(() => {
      flashTimer = null;
      flashMessage = null;
      recomputeFooter();
      redrawChrome();
    }, FLASH_DURATION_MS);
    if (typeof flashTimer.unref === "function") flashTimer.unref();
    recomputeFooter();
    redrawChrome();
  };
  const handlePasteImage = async () => {
    if (!opts.onPasteImage) return;
    if (flashTimer) {
      clearTimeout(flashTimer);
      flashTimer = null;
    }
    flashMessage = "Pasting image\u2026";
    recomputeFooter();
    redrawChrome();
    let result = "error";
    try {
      result = await opts.onPasteImage();
    } catch (e) {
      logErr(`paste-image failed: ${e.message}`);
    }
    flashMessage = result === "pasted" ? "Image pasted" : result === "no-image" ? "No image in clipboard" : "Image paste failed";
    flashTimer = setTimeout(() => {
      flashTimer = null;
      flashMessage = null;
      recomputeFooter();
      redrawChrome();
    }, FLASH_DURATION_MS);
    if (typeof flashTimer.unref === "function") flashTimer.unref();
    recomputeFooter();
    redrawChrome();
  };
  const router = createInputRouter({
    onForward: (b) => {
      if (reconnecting) {
        if (b.length === 1 && b[0] === 3) reconnectAbort?.abort();
        return;
      }
      pty.write(b.toString("utf8"));
    },
    onAnswer: (body) => {
      void postAnswer({ relayBaseUrl: opts.relayBaseUrl, body });
      capturingPrompt = null;
      applyBandChange();
    },
    leaderChords,
    onLeaderChange: (open) => {
      leaderActive = open;
      recomputeFooter();
      redrawChrome();
    },
    onAction: (name) => {
      runAction(name);
    },
    onPasteImage: opts.onPasteImage ? handlePasteImage : void 0
  });
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
  process.stdin.resume();
  const onStdinData = (chunk) => {
    router.feed(chunk);
  };
  process.stdin.on("data", onStdinData);
  const onResize = () => {
    const cs = process.stdout.columns ?? cols;
    const rs = process.stdout.rows ?? rows;
    bandReservedRows = bandState && bandFits() ? ALERT_BAND_ROWS : 0;
    const inner = Math.max(1, rs - reservedRows());
    resizePty(cs, inner);
    process.stdout.write(`\x1B[1;${String(inner)}r`);
    recomputeFooter();
    redrawChrome();
  };
  process.stdout.on("resize", onResize);
  const stream = subscribePrompts({
    relayBaseUrl: opts.relayBaseUrl,
    boxId: opts.boxId,
    onPrompt: (ev) => {
      capturingPrompt = ev;
      applyBandChange();
      router.capture(ev).catch((e) => {
        const msg = e instanceof Error ? e.message : String(e);
        if (msg !== "resolved-elsewhere") {
          logErr(`prompt capture rejected: ${msg}`);
        }
      });
    },
    onResolved: (id) => {
      if (capturingPrompt && capturingPrompt.id === id) {
        capturingPrompt = null;
        router.abort("resolved-elsewhere");
        applyBandChange();
      }
    },
    onNotice: (ev) => {
      if (ev.kind === "checkpoint") checkpointNoticeAt = Date.now();
      activeNotice = ev;
      startSpinner();
      applyBandChange();
    },
    onNoticeCleared: (id) => {
      if (activeNotice && activeNotice.id === id) {
        if (activeNotice.kind === "checkpoint") checkpointNoticeClearedAt = Date.now();
        activeNotice = null;
        stopSpinner();
        applyBandChange();
      }
    }
  });
  const pollStatus = async () => {
    try {
      const status = await readBoxStatus({
        id: opts.boxId,
        name: opts.boxName,
        projectIndex: opts.projectIndex
      });
      const body = opts.mode === "codex" ? status?.codex : opts.mode === "opencode" ? status?.opencode : opts.mode === "shell" ? void 0 : status?.claude;
      const nextTitle = body?.sessionTitle?.trim() || void 0;
      const nextActivity = body?.state || void 0;
      const desiredTitle = nextTitle ?? opts.boxName;
      if (desiredTitle !== lastEmittedTitle) {
        lastEmittedTitle = desiredTitle;
        setTerminalTitle(desiredTitle);
      }
      const nextQuestion = opts.mode === "claude" && status?.claude.state === "question" ? status.claude.question ?? null : null;
      const questionChanged = (nextQuestion?.capturedAt ?? null) !== (questionPayload?.capturedAt ?? null);
      if (questionChanged) {
        questionPayload = nextQuestion;
        applyBandChange();
      }
      if (nextTitle === lastSessionTitle && nextActivity === lastActivity) return;
      lastSessionTitle = nextTitle;
      lastActivity = nextActivity;
      if (footerState.kind === "idle") {
        recomputeFooter();
        redrawChrome();
      }
    } catch (e) {
      logErr(`status poll failed: ${e.message}`);
    }
  };
  void pollStatus();
  const statusTimer = setInterval(() => {
    void pollStatus();
  }, STATUS_POLL_INTERVAL_MS);
  if (typeof statusTimer.unref === "function") statusTimer.unref();
  process.stdout.write(`\x1B[1;${String(innerRows)}r`);
  if (opts.mode === "shell" && !detachable) {
    process.stdout.write("\x1B[H\x1B[2J");
  }
  redrawChrome();
  const reconnectFlow = async (code) => {
    const controller = new AbortController();
    reconnecting = true;
    reconnectAbort = controller;
    reconnectBanner = "box rebooting \u2014 reconnecting\u2026";
    startSpinner();
    applyBandChange();
    let spec = null;
    try {
      spec = await opts.reconnect?.(controller.signal, code) ?? null;
    } catch (e) {
      logErr(`reconnect failed: ${e.message}`);
    } finally {
      reconnecting = false;
      reconnectAbort = null;
      reconnectBanner = null;
      if (!activeNotice) stopSpinner();
      applyBandChange();
    }
    if (spec) {
      flashMessage = "reconnected";
      if (flashTimer) clearTimeout(flashTimer);
      flashTimer = setTimeout(() => {
        flashTimer = null;
        flashMessage = null;
        recomputeFooter();
        redrawChrome();
      }, FLASH_DURATION_MS);
      if (typeof flashTimer.unref === "function") flashTimer.unref();
      recomputeFooter();
      redrawChrome();
    }
    return spec;
  };
  let exitCode = 0;
  let rapidFails = 0;
  for (; ; ) {
    const code = await new Promise((resolve) => {
      pty.onExit(({ exitCode: exitCode2 }) => resolve(exitCode2));
    });
    if (userDetached || !opts.reconnect) {
      exitCode = code;
      break;
    }
    const checkpointing = checkpointNoticeAt > checkpointNoticeClearedAt || Date.now() - checkpointNoticeClearedAt < CHECKPOINT_DROP_GRACE_MS;
    if (!checkpointing && code === 0) {
      exitCode = code;
      break;
    }
    rapidFails = Date.now() - lastSpawnAt < RAPID_RECONNECT_MS ? rapidFails + 1 : 0;
    if (rapidFails >= MAX_RAPID_RECONNECTS) {
      logErr("giving up reconnect after repeated rapid failures");
      exitCode = code;
      break;
    }
    const next = await reconnectFlow(code);
    if (!next) {
      exitCode = code;
      break;
    }
    const rsNow = process.stdout.rows ?? rows;
    const innerNow = Math.max(1, rsNow - reservedRows());
    pty = backend.ptySpawn(next.command, next.argv, {
      name: "xterm-256color",
      cols: process.stdout.columns ?? cols,
      rows: innerNow,
      env: next.env ? { ...process.env, ...next.env } : process.env
    });
    wireOutput();
    lastSpawnAt = Date.now();
    checkpointNoticeAt = 0;
    checkpointNoticeClearedAt = 0;
    process.stdout.write(`\x1B[1;${String(innerNow)}r`);
    redrawChrome();
  }
  process.stdin.off("data", onStdinData);
  process.stdout.off("resize", onResize);
  clearInterval(statusTimer);
  stopSpinner();
  if (flashTimer) clearTimeout(flashTimer);
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
  process.stdin.pause();
  stream.close();
  router.dispose();
  const rsFinal = process.stdout.rows ?? rows;
  const csFinal = process.stdout.columns ?? cols;
  let teardownPaint = "\x1B[r";
  for (let r = rsFinal - bandReservedRows; r <= rsFinal; r++) {
    if (r >= 1) teardownPaint += cursorMoveTo(r, 1) + "\x1B[2K";
  }
  teardownPaint += cursorMoveTo(rsFinal, csFinal);
  process.stdout.write(teardownPaint);
  popTerminalTitle();
  if (exitCode === 0 && opts.detachNotice) {
    process.stdout.write("\x1B[1A\x1B[2K\r" + opts.detachNotice + "\n");
  }
  return exitCode;
}
function runFallback(command, argv, env) {
  const child = spawnSync(command, argv, {
    stdio: "inherit",
    env: env ? { ...process.env, ...env } : process.env
  });
  return child.status ?? 0;
}

// src/lib/paste-image.ts
import { rm as rm2 } from "fs/promises";
import { dirname } from "path";

// src/lib/host-clipboard.ts
import { mkdtemp, rm, stat, writeFile } from "fs/promises";
import { tmpdir } from "os";
import { join } from "path";
import { execa } from "execa";
async function captureClipboardImage() {
  if (process.platform !== "darwin" && process.platform !== "linux") return null;
  const dir = await mkdtemp(join(tmpdir(), "agentbox-clip-"));
  const pngPath = join(dir, "clip.png");
  const ok = process.platform === "darwin" ? await captureDarwin(dir, pngPath) : await captureLinux(pngPath);
  if (ok) return pngPath;
  await rm(dir, { recursive: true, force: true }).catch(() => {
  });
  return null;
}
async function clipboardCaptureAvailable() {
  if (process.platform === "darwin") return true;
  if (process.platform === "linux") return await linuxClipboardTool() !== null;
  return false;
}
function captureScriptArgs(pngPath, tiffPath) {
  return [
    "try",
    "  set theData to (the clipboard as \xABclass PNGf\xBB)",
    `  set fh to open for access (POSIX file ${JSON.stringify(pngPath)}) with write permission`,
    "  set eof fh to 0",
    "  write theData to fh",
    "  close access fh",
    '  return "PNG"',
    "on error",
    "  try",
    "    set theData to (the clipboard as \xABclass TIFF\xBB)",
    `    set fh to open for access (POSIX file ${JSON.stringify(tiffPath)}) with write permission`,
    "    set eof fh to 0",
    "    write theData to fh",
    "    close access fh",
    '    return "TIFF"',
    "  on error",
    '    return "NONE"',
    "  end try",
    "end try"
  ].map((line) => ["-e", line]).flat();
}
async function captureDarwin(dir, pngPath) {
  const tiffPath = join(dir, "clip.tiff");
  const res = await execa("osascript", captureScriptArgs(pngPath, tiffPath), {
    reject: false
  });
  const kind = res.stdout.trim();
  if (kind === "PNG") return fileHasBytes(pngPath);
  if (kind === "TIFF" && await fileHasBytes(tiffPath)) {
    const conv = await execa(
      "sips",
      ["-s", "format", "png", tiffPath, "--out", pngPath],
      { reject: false }
    );
    if (conv.exitCode === 0) return fileHasBytes(pngPath);
  }
  return false;
}
async function linuxClipboardTool() {
  if (process.env["WAYLAND_DISPLAY"] && await hasCmd("wl-paste")) return "wayland";
  if (process.env["DISPLAY"] && await hasCmd("xclip")) return "x11";
  return null;
}
async function captureLinux(pngPath) {
  const tool = await linuxClipboardTool();
  if (!tool) return false;
  let buf = null;
  if (tool === "wayland") {
    const types = await execa("wl-paste", ["--list-types"], { reject: false });
    if (types.exitCode !== 0 || !/image\/png/i.test(types.stdout)) return false;
    const r = await execa("wl-paste", ["--type", "image/png"], {
      encoding: "buffer",
      reject: false
    });
    if (r.exitCode === 0) buf = asBuffer(r.stdout);
  } else {
    const sel = ["-selection", "clipboard"];
    const targets = await execa("xclip", [...sel, "-t", "TARGETS", "-o"], {
      reject: false
    });
    if (targets.exitCode !== 0 || !/image\/png/i.test(targets.stdout)) return false;
    const r = await execa("xclip", [...sel, "-t", "image/png", "-o"], {
      encoding: "buffer",
      reject: false
    });
    if (r.exitCode === 0) buf = asBuffer(r.stdout);
  }
  if (!buf || !isPng(buf)) return false;
  await writeFile(pngPath, buf);
  return true;
}
async function hasCmd(cmd) {
  const r = await execa("sh", ["-c", `command -v ${cmd}`], { reject: false });
  return r.exitCode === 0;
}
function asBuffer(out) {
  if (Buffer.isBuffer(out)) return out;
  if (out instanceof Uint8Array) return Buffer.from(out);
  return null;
}
function isPng(buf) {
  return buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71;
}
async function fileHasBytes(path) {
  try {
    const s = await stat(path);
    return s.isFile() && s.size > 0;
  } catch {
    return false;
  }
}

// src/lib/paste-image.ts
function loadClipboardScript(boxPngPath) {
  return [
    "pgrep -x Xvnc >/dev/null 2>&1 || /usr/local/bin/agentbox-vnc-start >/dev/null 2>&1 || true",
    "for _ in $(seq 1 30); do [ -S /tmp/.X11-unix/X1 ] && break; sleep 0.2; done",
    `setsid sh -c 'DISPLAY=:1 xclip -selection clipboard -t image/png -i ${boxPngPath}' </dev/null >/dev/null 2>&1 &`
  ].join("; ");
}
async function pasteHostClipboardImage(provider, box) {
  if (typeof provider.uploadPath !== "function") return "error";
  const hostPng = await captureClipboardImage();
  if (!hostPng) return "no-image";
  const boxPng = `/tmp/agentbox-clip-${String(Date.now())}.png`;
  try {
    await provider.uploadPath(box, hostPng, boxPng);
    await provider.exec(box, ["sh", "-lc", loadClipboardScript(boxPng)], {
      user: "vscode"
    });
    return "pasted";
  } catch {
    return "error";
  } finally {
    await rm2(dirname(hostPng), { recursive: true, force: true }).catch(() => {
    });
  }
}

// src/commands/_cloud-attach.ts
var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
var RECONNECT_TIMEOUT_MS = 5 * 6e4;
function abortableSleep(ms, signal) {
  return new Promise((resolve) => {
    if (signal.aborted) {
      resolve();
      return;
    }
    const t = setTimeout(resolve, ms);
    signal.addEventListener(
      "abort",
      () => {
        clearTimeout(t);
        resolve();
      },
      { once: true }
    );
  });
}
function buildCloudAttachInnerCommand(binary, extraArgs) {
  if (!extraArgs || extraArgs.length === 0) {
    return `bash -lc exec\\ ${binary}`;
  }
  const blob = Buffer.from(extraArgs.join("\n"), "utf8").toString("base64");
  return `bash -lc 'mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
}
async function cloudAgentAttach(args) {
  const provider = await providerForBox(args.box);
  if (!provider.buildAttach) {
    throw new Error(`provider '${provider.name}' does not support interactive attach`);
  }
  const buildAttach = provider.buildAttach.bind(provider);
  let box = args.box;
  const state = await provider.probeState(box);
  if (state === "missing") {
    throw new Error(`cloud sandbox for ${box.name} is missing; was it destroyed?`);
  }
  if (state !== "running") {
    const s = spinner();
    s.start(state === "paused" ? "resuming box" : "starting box");
    box = await provider.start(box);
    s.stop("box running");
  }
  const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs);
  const safeOpenIn = box.provider === "daytona" ? "same" : args.openIn;
  if (safeOpenIn && safeOpenIn !== "same" && args.extraArgs && args.extraArgs.length > 0) {
    await startDetachedSession(provider, box, args.sessionName, command);
  }
  let spec = await provider.buildAttach(box, "agent", {
    sessionName: args.sessionName,
    command
  });
  const canPaste = args.mode === "claude" && await clipboardCaptureAvailable();
  const reconnect = async (signal) => {
    const deadline = Date.now() + RECONNECT_TIMEOUT_MS;
    let backoff = 500;
    for (; ; ) {
      if (signal.aborted || Date.now() > deadline) return null;
      try {
        box = await provider.start(box);
        break;
      } catch {
        await abortableSleep(backoff, signal);
        backoff = Math.min(backoff * 2, 5e3);
      }
    }
    if (signal.aborted) return null;
    const prev = spec;
    spec = await buildAttach(box, "agent", { sessionName: args.sessionName, command });
    if (prev.cleanup) {
      try {
        await prev.cleanup();
      } catch {
      }
    }
    return { command: spec.argv[0], argv: spec.argv.slice(1), env: spec.env };
  };
  try {
    const code = await runWrappedAttach({
      container: box.name,
      command: spec.argv[0],
      dockerArgv: spec.argv.slice(1),
      env: spec.env,
      relayBaseUrl: RELAY_HOST_URL,
      boxId: box.id,
      boxName: box.name,
      projectIndex: box.projectIndex,
      mode: args.mode,
      detachable: true,
      openIn: safeOpenIn,
      reconnect,
      onError: (msg) => {
        try {
          appendFileSync(
            join2(homedir(), ".agentbox", "logs", "attach.log"),
            `${(/* @__PURE__ */ new Date()).toISOString()} [${box.name}] ${msg}
`
          );
        } catch {
        }
      },
      onPasteImage: canPaste ? () => pasteHostClipboardImage(provider, box) : void 0
    });
    process.exit(code);
  } finally {
    if (spec.cleanup) await spec.cleanup();
  }
}
async function startDetachedSession(provider, box, sessionName, command) {
  if (!provider.buildAttach) {
    throw new Error(`provider '${provider.name}' does not support detached sessions`);
  }
  const spec = await provider.buildAttach(box, "agent", {
    sessionName,
    command,
    detached: true
  });
  try {
    await runDetached(spec.argv, spec.env);
  } finally {
    if (spec.cleanup) await spec.cleanup();
  }
}
async function cloudAgentStartDetached(args) {
  const provider = await providerForBox(args.box);
  let box = args.box;
  const state = await provider.probeState(box);
  if (state === "missing") {
    throw new Error(`cloud sandbox for ${box.name} is missing; was it destroyed?`);
  }
  if (state !== "running") {
    box = await provider.start(box);
  }
  const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs);
  await startDetachedSession(provider, box, args.sessionName, command);
}
function runDetached(argv, env) {
  return new Promise((resolve) => {
    const child = spawn3(argv[0], argv.slice(1), {
      stdio: "ignore",
      env: env ? { ...process.env, ...env } : process.env
    });
    child.on("error", () => resolve());
    child.on("exit", () => resolve());
  });
}

export {
  isKnownProvider,
  getProvider,
  providerForBox,
  providerForCreate,
  loadPtyBackend,
  detectHostTerminal,
  setTerminalTitle,
  pushTerminalTitle,
  popTerminalTitle,
  NEW_BOX_ID,
  NEW_BOX_LABEL,
  stripTitleGlyph,
  sidebarLines,
  menuLines,
  lifecycleMenuLines,
  createMenuLines,
  ADVANCED_HINT_GROUPS,
  statusLine,
  renderFooter,
  ALERT_BAND_ROWS,
  renderAlertBand,
  subscribePrompts,
  postAnswer,
  runWrappedAttach,
  clipboardCaptureAvailable,
  pasteHostClipboardImage,
  buildCloudAttachInnerCommand,
  cloudAgentAttach,
  cloudAgentStartDetached
};
//# sourceMappingURL=chunk-GYJ62GFL.js.map