const XOPC_EXT_PORT = 19820;
const XOPC_EXT_HOST = "127.0.0.1";
const XOPC_EXT_WS_URL = `ws://${XOPC_EXT_HOST}:${XOPC_EXT_PORT}/browser-ext`;
const WS_RECONNECT_BASE_DELAY = 2e3;
const WS_RECONNECT_MAX_DELAY = 5e3;

const LOG_COLORS = {
  debug: "color: #888",
  info: "color: #3b82f6",
  warn: "color: #f59e0b",
  error: "color: #ef4444"
};
function createLogger(prefix) {
  function log(level, message, data) {
    const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
    const formatted = `[${timestamp}] [${prefix}] ${message}`;
    if (data && Object.keys(data).length > 0) {
      console[level](`%c${formatted}`, LOG_COLORS[level], data);
    } else {
      console[level](`%c${formatted}`, LOG_COLORS[level]);
    }
  }
  return {
    debug: (msg, data) => log("debug", msg, data),
    info: (msg, data) => log("info", msg, data),
    warn: (msg, data) => log("warn", msg, data),
    error: (msg, data) => log("error", msg, data)
  };
}

async function sendContentMessage(tabId, message) {
  try {
    await chrome.tabs.sendMessage(tabId, message);
  } catch {
  }
}
async function showOverlayOnTab(tabId, status) {
  await sendContentMessage(tabId, { type: "content/show-overlay", status });
}
async function hideOverlayOnTab(tabId) {
  await sendContentMessage(tabId, { type: "content/hide-overlay" });
}
async function showClickRippleOnTab(tabId, x, y) {
  await sendContentMessage(tabId, { type: "content/show-click-ripple", x, y });
}
async function showHoverHighlightOnTab(tabId, selector) {
  await sendContentMessage(tabId, { type: "content/show-hover-highlight", selector });
}
async function showInputFlashOnTab(tabId, selector) {
  await sendContentMessage(tabId, { type: "content/show-input-flash", selector });
}
async function showScrollIndicatorOnTab(tabId, direction) {
  await sendContentMessage(tabId, { type: "content/show-scroll-indicator", direction });
}

const log$2 = createLogger("SessionManager");
const XOPC_TAB_GROUP_TITLE = "xopc";
const automationSessions = /* @__PURE__ */ new Map();
const WINDOW_IDLE_TIMEOUT = 12e4;
const DEFAULT_WORKSPACE = "default";
function resetWindowIdleTimer(workspace = DEFAULT_WORKSPACE) {
  const session = automationSessions.get(workspace);
  if (!session) return;
  if (session.idleTimer) clearTimeout(session.idleTimer);
  session.idleDeadlineAt = Date.now() + WINDOW_IDLE_TIMEOUT;
  session.idleTimer = setTimeout(async () => {
    const current = automationSessions.get(workspace);
    if (!current) return;
    try {
      const tabs = await chrome.tabs.query({ groupId: current.groupId });
      for (const t of tabs) {
        if (t.id != null) await chrome.tabs.remove(t.id).catch(() => {
        });
      }
    } catch {
    }
    automationSessions.delete(workspace);
    log$2.info("Automation tab group cleared due to idle timeout", { workspace });
  }, WINDOW_IDLE_TIMEOUT);
}
async function waitForTabLoad(tabId, timeoutMs = 3e4) {
  return new Promise((resolve) => {
    const timeout = setTimeout(resolve, timeoutMs);
    const listener = (id, info) => {
      if (id === tabId && info.status === "complete") {
        clearTimeout(timeout);
        chrome.tabs.onUpdated.removeListener(listener);
        resolve();
      }
    };
    chrome.tabs.onUpdated.addListener(listener);
    chrome.tabs.get(tabId).then((tab) => {
      if (tab.status === "complete") {
        clearTimeout(timeout);
        chrome.tabs.onUpdated.removeListener(listener);
        resolve();
      }
    }).catch(() => {
      clearTimeout(timeout);
      chrome.tabs.onUpdated.removeListener(listener);
      resolve();
    });
  });
}
async function resolveTargetWindowId() {
  const w = await chrome.windows.getLastFocused({ windowTypes: ["normal"] });
  if (w?.id != null) return w.id;
  const all = await chrome.windows.getAll({ windowTypes: ["normal"] });
  const first = all.find((x) => x.id != null);
  if (first?.id != null) return first.id;
  throw new Error("No normal browser window — open a Chrome window, then run automation again.");
}
async function getOrCreateAutomationWindow(workspace = DEFAULT_WORKSPACE) {
  const existing = automationSessions.get(workspace);
  if (existing) {
    try {
      await chrome.tabGroups.get(existing.groupId);
      const tabsInGroup = await chrome.tabs.query({ groupId: existing.groupId });
      if (tabsInGroup.length === 0) throw new Error("empty group");
      existing.tabIds = tabsInGroup.map((t) => t.id).filter(Boolean);
      const active = tabsInGroup.find((t) => t.active);
      existing.activeTabId = active?.id ?? tabsInGroup[0]?.id ?? null;
      resetWindowIdleTimer(workspace);
      return existing;
    } catch {
      automationSessions.delete(workspace);
    }
  }
  const windowId = await resolveTargetWindowId();
  const tab = await chrome.tabs.create({
    windowId,
    url: "about:blank",
    active: false
  });
  if (tab.id == null) throw new Error("Failed to create automation tab");
  const groupId = await chrome.tabs.group({
    tabIds: [tab.id],
    createProperties: { windowId }
  });
  await chrome.tabGroups.update(groupId, {
    title: XOPC_TAB_GROUP_TITLE,
    collapsed: false,
    color: "blue"
  });
  const session = {
    windowId,
    groupId,
    tabIds: [tab.id],
    activeTabId: tab.id,
    idleTimer: null,
    idleDeadlineAt: 0,
    createdAt: Date.now()
  };
  automationSessions.set(workspace, session);
  resetWindowIdleTimer(workspace);
  log$2.info("Created automation tab group", { workspace, windowId, groupId, tabId: tab.id });
  return session;
}
async function getActiveTabId(workspace = DEFAULT_WORKSPACE) {
  const session = await getOrCreateAutomationWindow(workspace);
  if (session.activeTabId != null) {
    const ok = await chrome.tabs.get(session.activeTabId).catch(() => null);
    if (ok) return session.activeTabId;
  }
  const tabs = await chrome.tabs.query({ groupId: session.groupId });
  const tabId = tabs.find((t) => t.active)?.id ?? tabs[0]?.id;
  if (tabId == null) throw new Error("No tab in xopc automation group");
  session.activeTabId = tabId;
  return tabId;
}
async function addTabToAutomationGroup(url, workspace = DEFAULT_WORKSPACE) {
  const session = await getOrCreateAutomationWindow(workspace);
  const tab = await chrome.tabs.create({
    windowId: session.windowId,
    url,
    active: false
  });
  if (tab.id == null) throw new Error("Failed to create tab");
  const inGroup = await chrome.tabs.query({ groupId: session.groupId });
  const mergedIds = [...inGroup.map((t) => t.id).filter(Boolean), tab.id];
  await chrome.tabs.group({ tabIds: mergedIds, groupId: session.groupId });
  session.activeTabId = tab.id;
  session.tabIds = mergedIds;
  resetWindowIdleTimer(workspace);
  return { tabId: tab.id, session };
}
async function closeSession(workspace = DEFAULT_WORKSPACE) {
  const session = automationSessions.get(workspace);
  if (!session) return;
  if (session.idleTimer) clearTimeout(session.idleTimer);
  const tabs = await chrome.tabs.query({ groupId: session.groupId }).catch(() => []);
  for (const t of tabs) {
    if (t.id != null) await hideOverlayOnTab(t.id);
  }
  for (const t of tabs) {
    if (t.id != null) await chrome.tabs.remove(t.id).catch(() => {
    });
  }
  automationSessions.delete(workspace);
  log$2.info("Closed automation session", { workspace });
}
async function closeAllSessions() {
  for (const workspace of [...automationSessions.keys()]) {
    await closeSession(workspace);
  }
}

async function handleOpen(cmd) {
  const url = cmd.args?.url;
  if (!url) return { id: cmd.id, ok: false, error: "Missing required arg: url" };
  const tabId = cmd.tabId ?? await getActiveTabId();
  await showOverlayOnTab(tabId, "operating");
  try {
    await chrome.tabs.update(tabId, { url });
    await waitForTabLoad(tabId, cmd.timeout ?? 3e4);
    resetWindowIdleTimer();
    const tab = await chrome.tabs.get(tabId);
    await hideOverlayOnTab(tabId);
    if (tab.groupId != null) {
      await chrome.tabGroups.update(tab.groupId, { collapsed: false }).catch(() => {
      });
    }
    await chrome.tabs.update(tabId, { active: true }).catch(() => {
    });
    return {
      id: cmd.id,
      ok: true,
      data: { url: tab.url, title: tab.title, tabId }
    };
  } catch (e) {
    await hideOverlayOnTab(tabId);
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleNavigate(cmd) {
  return handleOpen(cmd);
}
async function handleReload(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  await showOverlayOnTab(tabId, "operating");
  try {
    await chrome.tabs.reload(tabId);
    await waitForTabLoad(tabId, cmd.timeout ?? 3e4);
    resetWindowIdleTimer();
    const tab = await chrome.tabs.get(tabId);
    await hideOverlayOnTab(tabId);
    return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
  } catch (e) {
    await hideOverlayOnTab(tabId);
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleBack(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    await chrome.tabs.goBack(tabId);
    await waitForTabLoad(tabId, cmd.timeout ?? 1e4);
    const tab = await chrome.tabs.get(tabId);
    return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleForward(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    await chrome.tabs.goForward(tabId);
    await waitForTabLoad(tabId, cmd.timeout ?? 1e4);
    const tab = await chrome.tabs.get(tabId);
    return { id: cmd.id, ok: true, data: { url: tab.url, title: tab.title } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleGetUrl(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    const tab = await chrome.tabs.get(tabId);
    return { id: cmd.id, ok: true, data: { url: tab.url } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleGetTitle(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    const tab = await chrome.tabs.get(tabId);
    return { id: cmd.id, ok: true, data: { title: tab.title } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}

const log$1 = createLogger("CDP");
const attached = /* @__PURE__ */ new Set();
function isDebuggableUrl(url) {
  if (!url) return true;
  return url.startsWith("http://") || url.startsWith("https://") || url === "about:blank" || url.startsWith("data:");
}
async function ensureAttached(tabId) {
  try {
    const tab = await chrome.tabs.get(tabId);
    if (!isDebuggableUrl(tab.url)) {
      attached.delete(tabId);
      throw new Error(`Cannot debug tab ${tabId}: URL is ${tab.url ?? "unknown"}`);
    }
  } catch (e) {
    if (e instanceof Error && e.message.startsWith("Cannot debug tab")) throw e;
    attached.delete(tabId);
    throw new Error(`Tab ${tabId} no longer exists`);
  }
  if (attached.has(tabId)) {
    try {
      await chrome.debugger.sendCommand({ tabId }, "Runtime.evaluate", {
        expression: "1",
        returnByValue: true
      });
      return;
    } catch {
      attached.delete(tabId);
    }
  }
  const MAX_RETRIES = 3;
  const RETRY_DELAY = 500;
  let lastError = "";
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
    try {
      try {
        await chrome.debugger.detach({ tabId });
      } catch {
      }
      await chrome.debugger.attach({ tabId }, "1.3");
      attached.add(tabId);
      await chrome.debugger.sendCommand({ tabId }, "Runtime.enable", {});
      await chrome.debugger.sendCommand({ tabId }, "Page.enable", {});
      log$1.debug("Attached to tab", { tabId, attempt });
      return;
    } catch (e) {
      lastError = e instanceof Error ? e.message : String(e);
      if (attempt < MAX_RETRIES) {
        await new Promise((r) => setTimeout(r, RETRY_DELAY));
      }
    }
  }
  throw new Error(`Failed to attach debugger to tab ${tabId}: ${lastError}`);
}
async function sendCommand(tabId, method, params) {
  await ensureAttached(tabId);
  return chrome.debugger.sendCommand({ tabId }, method, params ?? {});
}
async function evaluate(tabId, expression) {
  const result = await sendCommand(tabId, "Runtime.evaluate", {
    expression,
    returnByValue: true,
    awaitPromise: true,
    userGesture: true
  });
  if (result.exceptionDetails) {
    throw new Error(`JS evaluation failed: ${result.exceptionDetails.text ?? "unknown error"}`);
  }
  return result.result?.value;
}
async function captureScreenshot(tabId, options) {
  if (options?.fullPage) {
    const metrics = await sendCommand(tabId, "Page.getLayoutMetrics", {});
    if (metrics.contentSize) {
      await sendCommand(tabId, "Emulation.setDeviceMetricsOverride", {
        width: Math.ceil(metrics.contentSize.width),
        height: Math.ceil(metrics.contentSize.height),
        deviceScaleFactor: 1,
        mobile: false
      });
    }
  }
  const result = await sendCommand(tabId, "Page.captureScreenshot", {
    format: options?.format,
    quality: options?.quality
  });
  if (options?.fullPage) {
    await sendCommand(tabId, "Emulation.clearDeviceMetricsOverride", {});
  }
  return result.data;
}
async function dispatchInput(tabId, type, params) {
  await sendCommand(tabId, `Input.${type}`, params);
}
chrome.debugger.onDetach.addListener((source) => {
  if (source.tabId) attached.delete(source.tabId);
});

async function handleMouseMove(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const x = cmd.args?.x ?? 0;
  const y = cmd.args?.y ?? 0;
  try {
    await dispatchInput(tabId, "dispatchMouseEvent", {
      type: "mouseMoved",
      x,
      y
    });
    return { id: cmd.id, ok: true, data: { x, y } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleMouseClick(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const x = cmd.args?.x ?? 0;
  const y = cmd.args?.y ?? 0;
  const button = cmd.args?.button ?? "left";
  const clickCount = cmd.args?.clickCount ?? 1;
  try {
    await dispatchInput(tabId, "dispatchMouseEvent", {
      type: "mousePressed",
      x,
      y,
      button,
      clickCount
    });
    await dispatchInput(tabId, "dispatchMouseEvent", {
      type: "mouseReleased",
      x,
      y,
      button,
      clickCount
    });
    await showClickRippleOnTab(tabId, x, y);
    return { id: cmd.id, ok: true, data: { x, y, button } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleMouseWheel(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const x = cmd.args?.x ?? 0;
  const y = cmd.args?.y ?? 0;
  const deltaX = cmd.args?.deltaX ?? 0;
  const deltaY = cmd.args?.deltaY ?? 0;
  try {
    await dispatchInput(tabId, "dispatchMouseEvent", {
      type: "mouseWheel",
      x,
      y,
      deltaX,
      deltaY
    });
    return { id: cmd.id, ok: true, data: { deltaX, deltaY } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleKeyboardPress(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const key = cmd.args?.key;
  if (!key) return { id: cmd.id, ok: false, error: "Missing required arg: key" };
  try {
    await dispatchInput(tabId, "dispatchKeyEvent", {
      type: "keyDown",
      key,
      windowsVirtualKeyCode: key.length === 1 ? key.charCodeAt(0) : 0
    });
    await dispatchInput(tabId, "dispatchKeyEvent", {
      type: "keyUp",
      key,
      windowsVirtualKeyCode: key.length === 1 ? key.charCodeAt(0) : 0
    });
    return { id: cmd.id, ok: true, data: { key } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleKeyboardType(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const text = cmd.args?.text;
  if (!text) return { id: cmd.id, ok: false, error: "Missing required arg: text" };
  try {
    for (const char of text) {
      await dispatchInput(tabId, "dispatchKeyEvent", {
        type: "char",
        text: char
      });
    }
    return { id: cmd.id, ok: true, data: { text } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleType(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const selector = cmd.args?.selector;
  const text = cmd.args?.text;
  if (!text) return { id: cmd.id, ok: false, error: "Missing required arg: text" };
  try {
    if (selector) {
      await evaluate(tabId, `document.querySelector('${selector}')?.focus()`);
      await showInputFlashOnTab(tabId, selector);
    }
    await sendCommand(tabId, "Input.insertText", { text });
    if (cmd.args?.pressEnter) {
      await dispatchInput(tabId, "dispatchKeyEvent", {
        type: "keyDown",
        key: "Enter",
        code: "Enter",
        windowsVirtualKeyCode: 13
      });
      await dispatchInput(tabId, "dispatchKeyEvent", {
        type: "keyUp",
        key: "Enter",
        code: "Enter",
        windowsVirtualKeyCode: 13
      });
    }
    return { id: cmd.id, ok: true, data: { text, selector } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}

async function handleClick(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const selector = cmd.args?.selector;
  const text = cmd.args?.text;
  try {
    let x, y;
    if (selector) {
      await showHoverHighlightOnTab(tabId, selector);
      const box = await evaluate(tabId, `(() => {
        const el = document.querySelector('${selector}');
        if (!el) return null;
        const r = el.getBoundingClientRect();
        return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
      })()`);
      if (!box || typeof box !== "object") {
        return { id: cmd.id, ok: false, error: `Element not found: ${selector}` };
      }
      const coords = box;
      x = coords.x;
      y = coords.y;
    } else if (text) {
      const box = await evaluate(tabId, `(() => {
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
        while (walker.nextNode()) {
          if (walker.currentNode.textContent?.trim().includes('${text.replace(/'/g, "\\'")}')) {
            const el = walker.currentNode.parentElement;
            if (el) { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }
          }
        }
        return null;
      })()`);
      if (!box || typeof box !== "object") {
        return { id: cmd.id, ok: false, error: `Element with text not found: ${text}` };
      }
      const coords = box;
      x = coords.x;
      y = coords.y;
    } else if (cmd.args?.x !== void 0 && cmd.args?.y !== void 0) {
      x = cmd.args.x;
      y = cmd.args.y;
    } else {
      return { id: cmd.id, ok: false, error: "Click requires selector, text, or x/y coordinates" };
    }
    await dispatchInput(tabId, "dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
    await dispatchInput(tabId, "dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
    await showClickRippleOnTab(tabId, x, y);
    return { id: cmd.id, ok: true, data: { x, y, selector, text } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleScroll(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const deltaY = cmd.args?.deltaY ?? (cmd.args?.direction === "up" ? -300 : 300);
  try {
    await dispatchInput(tabId, "dispatchMouseEvent", {
      type: "mouseWheel",
      x: 640,
      y: 360,
      deltaX: 0,
      deltaY
    });
    await showScrollIndicatorOnTab(tabId, deltaY > 0 ? "down" : "up");
    return { id: cmd.id, ok: true, data: { deltaY } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleEvaluate(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const expression = cmd.args?.expression;
  if (!expression) return { id: cmd.id, ok: false, error: "Missing required arg: expression" };
  try {
    const result = await evaluate(tabId, expression);
    return { id: cmd.id, ok: true, data: { result } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleWait(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const selector = cmd.args?.selector;
  const timeout = cmd.timeout ?? 1e4;
  if (!selector) {
    const ms = cmd.args?.ms ?? 1e3;
    await new Promise((r) => setTimeout(r, ms));
    return { id: cmd.id, ok: true, data: { waited: ms } };
  }
  try {
    const start = Date.now();
    while (Date.now() - start < timeout) {
      const found = await evaluate(tabId, `!!document.querySelector('${selector}')`);
      if (found) return { id: cmd.id, ok: true, data: { selector, elapsed: Date.now() - start } };
      await new Promise((r) => setTimeout(r, 200));
    }
    return { id: cmd.id, ok: false, error: `Timeout waiting for selector: ${selector}` };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleQuerySelector(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const selector = cmd.args?.selector;
  if (!selector) return { id: cmd.id, ok: false, error: "Missing required arg: selector" };
  try {
    const result = await evaluate(tabId, `(() => {
      const el = document.querySelector('${selector}');
      if (!el) return null;
      const r = el.getBoundingClientRect();
      return { tag: el.tagName.toLowerCase(), text: el.textContent?.slice(0, 200), rect: { x: r.x, y: r.y, width: r.width, height: r.height } };
    })()`);
    return { id: cmd.id, ok: true, data: result };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}

async function handleState(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    const tab = await chrome.tabs.get(tabId);
    return {
      id: cmd.id,
      ok: true,
      data: { url: tab.url, title: tab.title, tabId, status: tab.status }
    };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleSnapshot(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    const accessibility = await sendCommand(tabId, "Accessibility.getFullAXTree", {});
    const nodes = accessibility.nodes?.slice(0, 200).map((n) => ({
      role: n.role?.value,
      name: n.name?.value
    })).filter((n) => n.role && n.name) ?? [];
    const tab = await chrome.tabs.get(tabId);
    return {
      id: cmd.id,
      ok: true,
      data: { url: tab.url, title: tab.title, nodes, nodeCount: nodes.length }
    };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleScreenshot(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const fullPage = cmd.args?.fullPage ?? cmd.args?.full_page ?? false;
  try {
    const base64 = await captureScreenshot(tabId, {
      format: "png",
      fullPage
    });
    return { id: cmd.id, ok: true, data: { base64, format: "png", fullPage } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleGetViewportSize(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  try {
    const size = await evaluate(tabId, `({ width: window.innerWidth, height: window.innerHeight })`);
    return { id: cmd.id, ok: true, data: size };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleSetViewportSize(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const width = cmd.args?.width ?? 1280;
  const height = cmd.args?.height ?? 720;
  try {
    await sendCommand(tabId, "Emulation.setDeviceMetricsOverride", {
      width,
      height,
      deviceScaleFactor: 1,
      mobile: false
    });
    return { id: cmd.id, ok: true, data: { width, height } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleClose(cmd) {
  try {
    await closeSession();
    return { id: cmd.id, ok: true, data: { closed: true } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleNewTab(cmd) {
  try {
    const url = cmd.args?.url ?? "about:blank";
    const { tabId } = await addTabToAutomationGroup(url);
    return { id: cmd.id, ok: true, data: { tabId, url } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleListTabs(cmd) {
  try {
    const sess = automationSessions.get(DEFAULT_WORKSPACE);
    if (!sess) {
      return { id: cmd.id, ok: true, data: { tabs: [] } };
    }
    const tabs = await chrome.tabs.query({ groupId: sess.groupId });
    return {
      id: cmd.id,
      ok: true,
      data: {
        tabs: tabs.map((t) => ({ id: t.id, url: t.url, title: t.title, active: t.active }))
      }
    };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleGetCookies(cmd) {
  const url = cmd.args?.url;
  try {
    const cookies = await chrome.cookies.getAll(url ? { url } : {});
    return { id: cmd.id, ok: true, data: { cookies: cookies.slice(0, 100) } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
async function handleDialog(cmd) {
  const tabId = cmd.tabId ?? await getActiveTabId();
  const accept = cmd.args?.accept ?? true;
  const promptText = cmd.args?.text;
  try {
    await sendCommand(tabId, "Page.handleJavaScriptDialog", {
      accept,
      promptText
    });
    return { id: cmd.id, ok: true, data: { accept, promptText } };
  } catch (e) {
    return { id: cmd.id, ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}

const log = createLogger("Background");
let ws = null;
let reconnectTimer = null;
let reconnectDelay = WS_RECONNECT_BASE_DELAY;
let intentionalClose = false;
async function dispatchCommand(cmd) {
  const start = Date.now();
  try {
    let result;
    switch (cmd.action) {
      // Navigation
      case "open":
        result = await handleOpen(cmd);
        break;
      case "navigate":
        result = await handleNavigate(cmd);
        break;
      case "reload":
        result = await handleReload(cmd);
        break;
      case "back":
        result = await handleBack(cmd);
        break;
      case "forward":
        result = await handleForward(cmd);
        break;
      case "get_url":
        result = await handleGetUrl(cmd);
        break;
      case "get_title":
        result = await handleGetTitle(cmd);
        break;
      // Page
      case "state":
        result = await handleState(cmd);
        break;
      case "snapshot":
        result = await handleSnapshot(cmd);
        break;
      case "screenshot":
        result = await handleScreenshot(cmd);
        break;
      case "get_viewport_size":
        result = await handleGetViewportSize(cmd);
        break;
      case "set_viewport_size":
        result = await handleSetViewportSize(cmd);
        break;
      case "close":
        result = await handleClose(cmd);
        break;
      case "new_tab":
        result = await handleNewTab(cmd);
        break;
      case "list_tabs":
        result = await handleListTabs(cmd);
        break;
      case "get_cookies":
        result = await handleGetCookies(cmd);
        break;
      case "dialog":
        result = await handleDialog(cmd);
        break;
      // Elements
      case "click":
        result = await handleClick(cmd);
        break;
      case "scroll":
        result = await handleScroll(cmd);
        break;
      case "evaluate":
        result = await handleEvaluate(cmd);
        break;
      case "wait":
        result = await handleWait(cmd);
        break;
      case "query_selector":
        result = await handleQuerySelector(cmd);
        break;
      // Input
      case "mouse_move":
        result = await handleMouseMove(cmd);
        break;
      case "mouse_click":
        result = await handleMouseClick(cmd);
        break;
      case "mouse_wheel":
        result = await handleMouseWheel(cmd);
        break;
      case "keys":
        result = await handleKeyboardPress(cmd);
        break;
      case "keyboard_type":
        result = await handleKeyboardType(cmd);
        break;
      case "type":
        result = await handleType(cmd);
        break;
      // Ping
      case "ping":
        result = {
          id: cmd.id,
          ok: true,
          data: {
            pong: true,
            extensionVersion: chrome.runtime.getManifest().version,
            timestamp: Date.now()
          }
        };
        break;
      default:
        result = { id: cmd.id, ok: false, error: `Unknown action: ${cmd.action}` };
    }
    result.durationMs = Date.now() - start;
    return result;
  } catch (e) {
    return {
      id: cmd.id,
      ok: false,
      error: e instanceof Error ? e.message : String(e),
      durationMs: Date.now() - start
    };
  }
}
function connect() {
  if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
    return;
  }
  intentionalClose = false;
  log.info("Connecting to xopc daemon...", { url: XOPC_EXT_WS_URL });
  ws = new WebSocket(XOPC_EXT_WS_URL);
  ws.onopen = () => {
    log.info("Connected to xopc daemon");
    reconnectDelay = WS_RECONNECT_BASE_DELAY;
    sendStatus();
  };
  ws.onmessage = async (event) => {
    try {
      const cmd = JSON.parse(event.data);
      log.debug("Received command", { action: cmd.action, id: cmd.id });
      const result = await dispatchCommand(cmd);
      ws?.send(JSON.stringify(result));
    } catch (e) {
      log.error("Failed to process message", {
        error: e instanceof Error ? e.message : String(e)
      });
    }
  };
  ws.onclose = () => {
    ws = null;
    if (!intentionalClose) {
      log.warn("Disconnected, reconnecting...", { delay: reconnectDelay });
      scheduleReconnect();
    }
  };
  ws.onerror = () => {
  };
}
function scheduleReconnect() {
  if (reconnectTimer) clearTimeout(reconnectTimer);
  reconnectTimer = setTimeout(() => {
    reconnectTimer = null;
    reconnectDelay = Math.min(reconnectDelay * 1.5, WS_RECONNECT_MAX_DELAY);
    connect();
  }, reconnectDelay);
}
function disconnect() {
  intentionalClose = true;
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }
  if (ws) {
    ws.close();
    ws = null;
  }
}
async function sendStatus() {
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
  try {
    const sess = automationSessions.get(DEFAULT_WORKSPACE);
    let activeTabId;
    let activeUrl;
    if (sess?.activeTabId != null) {
      const tab = await chrome.tabs.get(sess.activeTabId).catch(() => null);
      if (tab) {
        activeTabId = tab.id;
        activeUrl = tab.url;
      }
    }
    const status = {
      type: "status",
      connected: true,
      tabCount: sess ? sess.tabIds.length : 0,
      activeTabId,
      activeUrl
    };
    ws.send(JSON.stringify(status));
  } catch {
  }
}
chrome.runtime.onMessage.addListener(
  (message, _sender, sendResponse) => {
    switch (message.type) {
      case "popup/get-status":
        sendResponse({
          connected: ws?.readyState === WebSocket.OPEN,
          url: XOPC_EXT_WS_URL
        });
        break;
      case "popup/connect":
        connect();
        sendResponse({ ok: true });
        break;
      case "popup/disconnect":
        disconnect();
        sendResponse({ ok: true });
        break;
      case "content/heartbeat":
        sendResponse({ ok: true });
        break;
      default:
        sendResponse({ ok: false, error: `Unknown message: ${message.type}` });
    }
    return true;
  }
);
chrome.runtime.onInstalled.addListener(() => {
  log.info("Extension installed/updated");
  connect();
});
chrome.runtime.onStartup.addListener(() => {
  log.info("Browser started");
  connect();
});
connect();
self.addEventListener("beforeunload", () => {
  disconnect();
  void closeAllSessions();
});
log.info("xopc Browser Bridge service worker loaded");
