#!/usr/bin/env node
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// src/server/launch-kit-paths.ts
var LAUNCHSECURE_DIR;
var init_launch_kit_paths = __esm({
  "src/server/launch-kit-paths.ts"() {
    "use strict";
    LAUNCHSECURE_DIR = ".launchsecure";
  }
});

// src/server/orbit/registry.ts
function emptyRegistry() {
  return { version: 1, worktrees: {} };
}
function readRegistry() {
  if (!(0, import_node_fs2.existsSync)(REGISTRY_PATH)) return emptyRegistry();
  try {
    const parsed = JSON.parse((0, import_node_fs2.readFileSync)(REGISTRY_PATH, "utf-8"));
    if (parsed?.version === 1 && parsed.worktrees && typeof parsed.worktrees === "object") {
      return parsed;
    }
  } catch {
  }
  return emptyRegistry();
}
function listWorktrees() {
  return Object.values(readRegistry().worktrees);
}
var import_node_fs2, import_node_os, import_node_path2, REGISTRY_DIR, REGISTRY_PATH, LOCK_PATH;
var init_registry = __esm({
  "src/server/orbit/registry.ts"() {
    "use strict";
    import_node_fs2 = require("node:fs");
    import_node_os = require("node:os");
    import_node_path2 = require("node:path");
    init_launch_kit_paths();
    REGISTRY_DIR = (0, import_node_path2.join)((0, import_node_os.homedir)(), LAUNCHSECURE_DIR, "orbit");
    REGISTRY_PATH = (0, import_node_path2.join)(REGISTRY_DIR, "state.json");
    LOCK_PATH = (0, import_node_path2.join)(REGISTRY_DIR, "state.json.lock");
  }
});

// src/server/lib/worktree.ts
function resolveWorktreeRoot(slug, monorepoRoot) {
  const local = listWorktrees().filter((w) => w.projectRoot === monorepoRoot);
  const match = local.find((w) => w.slug === slug);
  if (match) return match.path;
  if (local.length === 0) {
    throw new Error(
      `worktree="${slug}" requested but no worktrees are registered for this project. Run \`launch-orbit create <branch>\` first, or use \`project_root\` to point at an arbitrary path.`
    );
  }
  const available = local.map((w) => `"${w.slug}" (${w.branch})`).join(", ");
  throw new Error(`Unknown worktree "${slug}". Available: ${available}.`);
}
function resolveWorktreeOrProjectRoot(args, monorepoRoot) {
  const projectRoot = typeof args.project_root === "string" ? args.project_root.trim() : "";
  if (projectRoot) {
    return (0, import_node_path3.isAbsolute)(projectRoot) ? projectRoot : (0, import_node_path3.resolve)(monorepoRoot, projectRoot);
  }
  const worktree = typeof args.worktree === "string" ? args.worktree.trim() : "";
  if (worktree) {
    return resolveWorktreeRoot(worktree, monorepoRoot);
  }
  return null;
}
var import_node_path3, WORKTREE_PARAM_DESCRIPTION, PROJECT_ROOT_PARAM_DESCRIPTION;
var init_worktree = __esm({
  "src/server/lib/worktree.ts"() {
    "use strict";
    import_node_path3 = require("node:path");
    init_registry();
    WORKTREE_PARAM_DESCRIPTION = "Optional orbit worktree slug (from `launch-orbit create`). Resolves to the worktree's path via the orbit registry (~/.launchsecure/orbit/state.json). Lets you query a worktree's state from a Claude Code session pinned to the main repo. Superseded by `project_root`.";
    PROJECT_ROOT_PARAM_DESCRIPTION = "Optional explicit project root. Accepts an absolute path or a path relative to the monorepo root. Escape hatch when `worktree` doesn't fit. Takes precedence over all other root args.";
  }
});

// src/server/beacon-mcp.ts
var beacon_mcp_exports = {};
__export(beacon_mcp_exports, {
  __testing: () => __testing,
  startBeaconMcpServer: () => startBeaconMcpServer
});
function ndjsonDir(args = {}) {
  const rootFromArgs = resolveWorktreeOrProjectRoot(args, process.cwd());
  if (rootFromArgs) return (0, import_node_path4.resolve)(rootFromArgs, ".launchsecure");
  const env = process.env.BEACON_NDJSON_DIR;
  return env ? (0, import_node_path4.resolve)(env) : (0, import_node_path4.resolve)(process.cwd(), ".launchsecure");
}
function listSessionFiles(args = {}) {
  const dir = ndjsonDir(args);
  try {
    const entries = (0, import_node_fs3.readdirSync)(dir);
    const sessions = [];
    for (const name of entries) {
      const m = name.match(/^beacon-([A-Za-z0-9_-]+)\.ndjson$/);
      if (!m) continue;
      const path = (0, import_node_path4.join)(dir, name);
      const st = (0, import_node_fs3.statSync)(path);
      sessions.push({
        token: m[1],
        path,
        sizeBytes: st.size,
        modifiedAt: st.mtime.toISOString()
      });
    }
    sessions.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
    return sessions;
  } catch {
    return [];
  }
}
function resolveSession(token, args = {}) {
  const all = listSessionFiles(args);
  if (all.length === 0) return null;
  if (!token) return all[0];
  return all.find((s) => s.token === token) ?? null;
}
function parseEvents(filePath) {
  const raw = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
  const out = [];
  for (const line of raw.split("\n")) {
    const t = line.trim();
    if (!t) continue;
    try {
      const ev = JSON.parse(t);
      if (typeof ev.kind === "string" && typeof ev.seq === "number") out.push(ev);
    } catch {
    }
  }
  return out;
}
function shortPath(url) {
  try {
    const u = new URL(url);
    return (u.pathname || "/") + (u.search || "");
  } catch {
    return url;
  }
}
function summarize(e) {
  switch (e.kind) {
    case "route": {
      const r = e;
      return `route ${shortPath(r.from)} \u2192 ${shortPath(r.to)} (${r.via})`;
    }
    case "click":
    case "mousedown": {
      const t = e.target;
      const lbl = t.ariaLabel || t.text || t.label;
      const tail = t.selector?.split(" > ").pop()?.slice(0, 40) ?? "";
      const display = lbl ? `"${lbl}"` : tail;
      const role = t.role ? ` role=${t.role}` : "";
      const intercepted = e.hitMatchesTarget === false ? " [INTERCEPTED]" : "";
      return `${e.kind} <${t.tag}${role}> ${display}${intercepted}`;
    }
    case "dialog": {
      const d = e;
      return `dialog ${d.action} ${d.selector.slice(0, 60)}`;
    }
    case "submit": {
      const f = e.form;
      const fields = f.fields.map((fd) => fd.name + (fd.hasValue ? "\u2713" : "\u2205")).join(",");
      return `submit ${f.method} ${f.action || "-"} fields=[${fields}]`;
    }
    case "fetch":
    case "xhr": {
      const n = e;
      const fl = n.failed ? "FAIL" : "ok";
      return `${e.kind} ${n.method} ${n.status} ${n.durationMs}ms ${fl} ${shortPath(n.url)}`;
    }
    case "error":
    case "unhandledrejection": {
      return `${e.kind} ${e.message?.slice(0, 100) ?? ""}`;
    }
    case "probe": {
      const p = e;
      return `probe "${p.label}"`;
    }
    default:
      return e.kind;
  }
}
function compact(e) {
  return {
    seq: e.seq,
    ts: e.ts,
    kind: e.kind,
    sessionId: e.sessionId,
    summary: summarize(e)
  };
}
function isFailure(e) {
  if (e.kind === "error" || e.kind === "unhandledrejection") return true;
  if ((e.kind === "fetch" || e.kind === "xhr") && e.failed) return true;
  if (e.kind === "click" && e.hitMatchesTarget === false) return true;
  return false;
}
function sessionsTool(args = {}) {
  const files = listSessionFiles(args);
  const out = files.map((f) => {
    let events = [];
    try {
      events = parseEvents(f.path);
    } catch {
    }
    const kindCounts = {};
    for (const e of events) kindCounts[e.kind] = (kindCounts[e.kind] || 0) + 1;
    const lastEv = events[events.length - 1];
    return {
      token: f.token,
      path: f.path,
      sizeBytes: f.sizeBytes,
      modifiedAt: f.modifiedAt,
      eventCount: events.length,
      kindCounts,
      lastEventAt: lastEv ? new Date(lastEv.ts).toISOString() : null,
      lastSeq: lastEv?.seq ?? null
    };
  });
  return { sessions: out };
}
function recentTool(args) {
  const s = resolveSession(args.session, args);
  if (!s) return { error: "no session found", sessions: [] };
  const events = parseEvents(s.path);
  const limit = Math.min(Math.max(1, args.limit ?? 50), 500);
  const filtered = events.filter((e) => {
    if (args.kind && e.kind !== args.kind) return false;
    if (args.since !== void 0 && e.ts < args.since) return false;
    return true;
  });
  const recent = filtered.slice(-limit).reverse();
  return {
    session: s.token,
    matched: filtered.length,
    returned: recent.length,
    events: recent.map(compact)
  };
}
function failuresTool(args) {
  const s = resolveSession(args.session, args);
  if (!s) return { error: "no session found" };
  const events = parseEvents(s.path);
  const limit = Math.min(Math.max(1, args.limit ?? 20), 100);
  const failures = events.filter((e) => {
    if (!isFailure(e)) return false;
    if (args.since !== void 0 && e.ts < args.since) return false;
    return true;
  });
  const recent = failures.slice(-limit).reverse();
  return {
    session: s.token,
    matched: failures.length,
    returned: recent.length,
    // Full event objects for failures — agent needs the response body etc.
    events: recent
  };
}
function correlateTool(args) {
  const s = resolveSession(args.session, args);
  if (!s) return { error: "no session found" };
  const events = parseEvents(s.path);
  const idx = events.findIndex((e) => e.seq === args.seq);
  if (idx < 0) return { error: `event seq=${args.seq} not found in session ${s.token}` };
  const before = Math.min(Math.max(0, args.before ?? 5), 50);
  const after = Math.min(Math.max(0, args.after ?? 5), 50);
  const start = Math.max(0, idx - before);
  const end = Math.min(events.length, idx + after + 1);
  const slice = events.slice(start, end);
  return {
    session: s.token,
    target: events[idx],
    // full event
    context: slice.map(compact)
  };
}
function searchTool(args) {
  const s = resolveSession(args.session, args);
  if (!s) return { error: "no session found" };
  const events = parseEvents(s.path);
  const limit = Math.min(Math.max(1, args.limit ?? 20), 100);
  let re;
  const reMatch = args.pattern.match(/^\/(.+)\/([gimsuy]*)$/);
  try {
    re = reMatch ? new RegExp(reMatch[1], reMatch[2]) : new RegExp(args.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
  } catch (err) {
    return { error: `invalid pattern: ${err.message}` };
  }
  const matches = [];
  for (const e of events) {
    const serialized = JSON.stringify(e);
    const m = serialized.match(re);
    if (m) {
      const start = Math.max(0, (m.index ?? 0) - 30);
      const end = Math.min(serialized.length, (m.index ?? 0) + m[0].length + 30);
      matches.push({
        seq: e.seq,
        ts: e.ts,
        kind: e.kind,
        summary: summarize(e),
        matchLine: serialized.slice(start, end)
      });
      if (matches.length >= limit) break;
    }
  }
  return { session: s.token, matched: matches.length, events: matches };
}
function eventTool(args) {
  const s = resolveSession(args.session, args);
  if (!s) return { error: "no session found" };
  const events = parseEvents(s.path);
  const ev = events.find((e) => e.seq === args.seq);
  if (!ev) return { error: `event seq=${args.seq} not found in session ${s.token}` };
  return { session: s.token, event: ev };
}
async function handleTool(name, args) {
  switch (name) {
    case "beacon.sessions":
      return sessionsTool(args);
    case "beacon.recent":
      return recentTool(args);
    case "beacon.failures":
      return failuresTool(args);
    case "beacon.correlate":
      return correlateTool(args);
    case "beacon.search":
      return searchTool(args);
    case "beacon.event":
      return eventTool(args);
    default:
      throw new Error(`unknown tool: ${name}`);
  }
}
function send(msg) {
  process.stdout.write(JSON.stringify(msg) + "\n");
}
function sendResponse(id, result) {
  send({ jsonrpc: "2.0", id, result });
}
function sendError(id, code, message) {
  send({ jsonrpc: "2.0", id, error: { code, message } });
}
async function handleMessage(parsed) {
  const id = parsed.id;
  const method = parsed.method;
  const params = parsed.params ?? {};
  switch (method) {
    case "initialize":
      sendResponse(id ?? null, {
        protocolVersion: "2024-11-05",
        capabilities: { tools: {} },
        serverInfo: SERVER_INFO
      });
      break;
    case "notifications/initialized":
      break;
    case "tools/list":
      sendResponse(id ?? null, { tools: TOOLS });
      break;
    case "tools/call": {
      const toolName = params.name;
      const toolArgs = params.arguments ?? {};
      try {
        const result = await handleTool(toolName, toolArgs);
        sendResponse(id ?? null, {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
        });
      } catch (err) {
        sendError(id ?? null, -32603, `Tool error: ${err.message}`);
      }
      break;
    }
    case "ping":
      sendResponse(id ?? null, {});
      break;
    default:
      if (id !== void 0) sendError(id ?? null, -32601, `Method not found: ${method}`);
  }
}
function startBeaconMcpServer() {
  process.stderr.write("[launch-beacon mcp] starting on stdio\n");
  const teardown = () => {
    process.exit(0);
  };
  process.on("SIGTERM", teardown);
  process.on("SIGINT", teardown);
  process.stdin.setEncoding("utf-8");
  let buffer = "";
  process.stdin.on("data", (chunk) => {
    buffer += chunk;
    const lines = buffer.split("\n");
    buffer = lines.pop() || "";
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed) continue;
      try {
        handleMessage(JSON.parse(trimmed)).catch((err) => {
          process.stderr.write(`[launch-beacon mcp] message error: ${err}
`);
        });
      } catch (err) {
        process.stderr.write(`[launch-beacon mcp] parse error: ${err}
`);
      }
    }
  });
  process.stdin.on("end", () => {
    process.stderr.write("[launch-beacon mcp] stdin closed, exiting\n");
    teardown();
  });
}
var import_node_fs3, import_node_path4, SERVER_INFO, ROOT_PARAM_SCHEMA, TOOLS, __testing;
var init_beacon_mcp = __esm({
  "src/server/beacon-mcp.ts"() {
    "use strict";
    import_node_fs3 = require("node:fs");
    import_node_path4 = require("node:path");
    init_worktree();
    SERVER_INFO = { name: "launch-beacon", version: "0.0.1" };
    ROOT_PARAM_SCHEMA = {
      worktree: { type: "string", description: WORKTREE_PARAM_DESCRIPTION },
      project_root: { type: "string", description: PROJECT_ROOT_PARAM_DESCRIPTION }
    };
    TOOLS = [
      {
        name: "beacon.sessions",
        description: "List all launch-beacon monitor NDJSON files under .launchsecure/ with per-session summary (token, event count, kind breakdown, first/last seq, last event timestamp). Use to discover which sessions are available before querying with the other tools.",
        inputSchema: {
          type: "object",
          properties: { ...ROOT_PARAM_SCHEMA }
        }
      },
      {
        name: "beacon.recent",
        description: "Recent events from a session, optionally filtered by kind. Returns a compact summary line per event (seq, ts, kind, derived label). Use to get a quick sense of what just happened.",
        inputSchema: {
          type: "object",
          properties: {
            session: { type: "string", description: "Session token (filename stem). Defaults to most recent NDJSON." },
            kind: { type: "string", description: "Filter to one kind: click, route, dialog, fetch, xhr, error, unhandledrejection, submit, probe, mousedown." },
            since: { type: "number", description: "Epoch ms \u2014 only return events at or after this timestamp." },
            limit: { type: "number", description: "Max events to return. Default 50, hard cap 500." },
            ...ROOT_PARAM_SCHEMA
          }
        }
      },
      {
        name: "beacon.failures",
        description: "All failure-shaped events: window errors, unhandled rejections, fetch/xhr with status >= 400 or that threw, and clicks where hitMatchesTarget=false (intercepted by overlay). Returns full event objects (including request/response bodies for failed network calls) so the agent has enough to diagnose. Use this as the FIRST tool when asked 'what's broken'.",
        inputSchema: {
          type: "object",
          properties: {
            session: { type: "string", description: "Session token. Defaults to most recent NDJSON." },
            since: { type: "number", description: "Epoch ms \u2014 only return failures at or after this timestamp." },
            limit: { type: "number", description: "Max failures to return. Default 20, hard cap 100." },
            ...ROOT_PARAM_SCHEMA
          }
        }
      },
      {
        name: "beacon.correlate",
        description: "Given a target event seq, return the N events before and after it. Use to answer 'what was the user doing when this error fired' \u2014 pass the error's seq and you get the clicks/routes/fetches that preceded and followed it. Returned events are summaries; call beacon.event for full detail.",
        inputSchema: {
          type: "object",
          properties: {
            session: { type: "string", description: "Session token. Defaults to most recent NDJSON." },
            seq: { type: "number", description: "Target event sequence number." },
            before: { type: "number", description: "Events before to include. Default 5, max 50." },
            after: { type: "number", description: "Events after to include. Default 5, max 50." },
            ...ROOT_PARAM_SCHEMA
          },
          required: ["seq"]
        }
      },
      {
        name: "beacon.search",
        description: "Substring/regex search across all events in a session. Searches the serialized JSON of each event, so URLs, selectors, response bodies, error messages, and click labels are all searchable. Useful for 'did we ever POST to /api/X?' or 'find the click that contained the word Submit'.",
        inputSchema: {
          type: "object",
          properties: {
            pattern: { type: "string", description: "Substring or /regex/flags pattern to search for." },
            session: { type: "string", description: "Session token. Defaults to most recent NDJSON." },
            limit: { type: "number", description: "Max matches. Default 20, hard cap 100." },
            ...ROOT_PARAM_SCHEMA
          },
          required: ["pattern"]
        }
      },
      {
        name: "beacon.event",
        description: "Fetch the full JSON of a single event by sequence number. Use this AFTER beacon.correlate or beacon.recent shows a summary that you want to inspect deeply (full response body, full selector chain, etc.).",
        inputSchema: {
          type: "object",
          properties: {
            session: { type: "string", description: "Session token. Defaults to most recent NDJSON." },
            seq: { type: "number", description: "Event sequence number to fetch." },
            ...ROOT_PARAM_SCHEMA
          },
          required: ["seq"]
        }
      }
    ];
    __testing = {
      parseEvents,
      summarize,
      isFailure,
      listSessionFiles,
      resolveSession,
      sessionsTool,
      recentTool,
      failuresTool,
      correlateTool,
      searchTool,
      eventTool
    };
  }
});

// src/server/beacon-monitor-entry.ts
var import_node_crypto = require("node:crypto");
var import_node_path5 = require("node:path");

// src/server/beacon/ndjson-writer.ts
var import_node_fs = require("node:fs");
var import_node_path = require("node:path");
function openNdjsonWriter(opts) {
  (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(opts.path), { recursive: true });
  const stream = (0, import_node_fs.createWriteStream)(opts.path, { flags: "a" });
  let total = 0;
  let closed = false;
  stream.on("error", (err) => {
    opts.onError?.(err);
  });
  return {
    write(event) {
      if (closed) return;
      let line;
      try {
        line = JSON.stringify(event);
      } catch (err) {
        opts.onError?.(err instanceof Error ? err : new Error(String(err)));
        return;
      }
      stream.write(line + "\n");
      total += 1;
    },
    count() {
      return total;
    },
    path() {
      return opts.path;
    },
    close() {
      return new Promise((resolveClose) => {
        if (closed) {
          resolveClose();
          return;
        }
        closed = true;
        stream.end(() => resolveClose());
      });
    }
  };
}

// src/server/beacon/server.ts
var import_node_http = require("node:http");
var MAX_BODY_BYTES = 1048576;
var TOKEN_PATH_PREFIX = "/m/";
function writeCors(res, origin) {
  res.setHeader("Access-Control-Allow-Origin", origin && origin !== "null" ? origin : "*");
  res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "content-type");
  res.setHeader("Access-Control-Max-Age", "600");
  res.setHeader("Vary", "Origin");
}
function readBody(req) {
  return new Promise((resolve4, reject) => {
    const chunks = [];
    let total = 0;
    req.on("data", (chunk) => {
      total += chunk.length;
      if (total > MAX_BODY_BYTES) {
        reject(new Error("payload too large"));
        req.destroy();
        return;
      }
      chunks.push(chunk);
    });
    req.on("end", () => resolve4(Buffer.concat(chunks).toString("utf8")));
    req.on("error", (err) => reject(err));
  });
}
function isMonitorBatchEnvelope(value) {
  if (!value || typeof value !== "object") return false;
  const v = value;
  if (typeof v.sessionId !== "string") return false;
  if (!Array.isArray(v.events)) return false;
  if (!v.meta || typeof v.meta !== "object") return false;
  const meta = v.meta;
  if (typeof meta.url !== "string" || typeof meta.userAgent !== "string") return false;
  return true;
}
function startBeaconServer(opts) {
  return new Promise((resolveStart, rejectStart) => {
    const server = (0, import_node_http.createServer)(async (req, res) => {
      writeCors(res, req.headers.origin);
      if (req.method === "OPTIONS") {
        res.writeHead(204);
        res.end();
        return;
      }
      if (req.method !== "POST") {
        res.writeHead(405, { "content-type": "text/plain" });
        res.end("method not allowed");
        return;
      }
      const url = req.url ?? "/";
      const pathOnly = url.split("?")[0] ?? "/";
      if (!pathOnly.startsWith(TOKEN_PATH_PREFIX)) {
        res.writeHead(404, { "content-type": "text/plain" });
        res.end("not found");
        return;
      }
      const provided = pathOnly.slice(TOKEN_PATH_PREFIX.length).replace(/\/+$/, "");
      if (provided !== opts.token) {
        res.writeHead(403, { "content-type": "text/plain" });
        res.end("bad token");
        return;
      }
      let bodyText;
      try {
        bodyText = await readBody(req);
      } catch (err) {
        opts.onBadBatch?.(`body read failed: ${err.message}`, req);
        res.writeHead(413, { "content-type": "text/plain" });
        res.end("payload error");
        return;
      }
      let parsed;
      try {
        parsed = JSON.parse(bodyText);
      } catch {
        opts.onBadBatch?.("invalid JSON", req);
        res.writeHead(400, { "content-type": "text/plain" });
        res.end("invalid json");
        return;
      }
      if (!isMonitorBatchEnvelope(parsed)) {
        opts.onBadBatch?.("schema mismatch", req);
        res.writeHead(400, { "content-type": "text/plain" });
        res.end("bad batch shape");
        return;
      }
      try {
        opts.onBatch(parsed, req);
      } catch (err) {
        opts.onServerError?.(err instanceof Error ? err : new Error(String(err)));
      }
      res.writeHead(204);
      res.end();
    });
    server.on("error", (err) => {
      opts.onServerError?.(err);
      rejectStart(err);
    });
    server.listen(opts.port, opts.host, () => {
      const addr = server.address();
      const port = addr ? addr.port : opts.port;
      const url = `http://${opts.host}:${port}/m/${opts.token}`;
      resolveStart({
        url,
        port,
        close() {
          return new Promise((resolveClose) => {
            server.close(() => resolveClose());
          });
        }
      });
    });
  });
}

// src/server/beacon-monitor-entry.ts
function parseMonitorFlags(argv) {
  const flags = { port: 9876, host: "127.0.0.1", quiet: false };
  for (let i = 0; i < argv.length; i++) {
    const a = argv[i];
    if (a === "--port") {
      const n = Number(argv[++i]);
      if (!Number.isInteger(n) || n < 0 || n > 65535) {
        die(`--port must be an integer in 0..65535 (got "${argv[i]}")`);
      }
      flags.port = n;
    } else if (a === "--host") {
      const v = argv[++i];
      if (!v) die("--host requires a value");
      flags.host = v;
    } else if (a === "--cwd") {
      const v = argv[++i];
      if (!v) die("--cwd requires a path");
      flags.cwd = v;
    } else if (a === "--out") {
      const v = argv[++i];
      if (!v) die("--out requires a path");
      flags.out = v;
    } else if (a === "--token") {
      const v = argv[++i];
      if (!v) die("--token requires a value");
      if (!/^[A-Za-z0-9_-]+$/.test(v)) die("--token must be alphanumeric (plus _ or -)");
      flags.token = v;
    } else if (a === "--quiet") {
      flags.quiet = true;
    } else if (a === "--help" || a === "-h") {
      printHelp();
      process.exit(0);
    } else if (a.startsWith("--")) {
      die(`unknown flag: ${a}`);
    } else {
      die(`unexpected argument: ${a}`);
    }
  }
  return flags;
}
function mintToken() {
  return (0, import_node_crypto.randomBytes)(4).toString("hex");
}
async function runMonitor(argv) {
  const flags = parseMonitorFlags(argv);
  const token = flags.token ?? mintToken();
  const projectRoot = flags.cwd ? (0, import_node_path5.resolve)(flags.cwd) : process.cwd();
  const ndjsonDirEnv = process.env.BEACON_NDJSON_DIR;
  const outPath = flags.out ? (0, import_node_path5.resolve)(projectRoot, flags.out) : ndjsonDirEnv ? (0, import_node_path5.resolve)(ndjsonDirEnv, `beacon-${token}.ndjson`) : (0, import_node_path5.resolve)(projectRoot, ".launchsecure", `beacon-${token}.ndjson`);
  const writer = openNdjsonWriter({
    path: outPath,
    onError: (err) => {
      process.stderr.write(`
[launch-beacon] ndjson write error: ${err.message}
`);
    }
  });
  const stats = {
    events: 0,
    badBatches: 0,
    lastKind: "",
    lastAt: 0
  };
  let server;
  try {
    server = await startBeaconServer({
      port: flags.port,
      host: flags.host,
      token,
      onBatch: (batch) => {
        for (const raw of batch.events) {
          writer.write(raw);
          stats.events += 1;
          const ev = raw;
          if (typeof ev.kind === "string") stats.lastKind = ev.kind;
          if (typeof ev.ts === "number") stats.lastAt = ev.ts;
        }
        draw();
      },
      onBadBatch: (reason) => {
        stats.badBatches += 1;
        process.stderr.write(`
[launch-beacon] bad batch: ${reason}
`);
      },
      onServerError: (err) => {
        process.stderr.write(`
[launch-beacon] server error: ${err.message}
`);
      }
    });
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (msg.includes("EADDRINUSE")) {
      die(`port ${flags.port} already in use \u2014 pass --port <n> to choose another`);
    }
    die(`failed to start: ${msg}`);
    return;
  }
  process.stdout.write(`
launch-beacon monitor \u2014 listening
`);
  process.stdout.write(`  paste URL    ${server.url}
`);
  process.stdout.write(`  writing      ${outPath}
`);
  process.stdout.write(`  session      ${token}

`);
  let heartbeatTimer = null;
  const draw = () => {
    if (flags.quiet) return;
    const since = stats.lastAt ? Math.max(0, Math.round((Date.now() - stats.lastAt) / 1e3)) : null;
    const sinceStr = since === null ? "\u2014" : since < 5 ? "just now" : `${since}s ago`;
    const lastStr = stats.lastKind ? `${stats.lastKind} (${sinceStr})` : "\u2014";
    const badStr = stats.badBatches > 0 ? ` | bad: ${stats.badBatches}` : "";
    const line = `  events: ${stats.events}${badStr} | last: ${lastStr}`;
    process.stdout.write(`\r\x1B[K${line}`);
  };
  if (!flags.quiet) {
    heartbeatTimer = setInterval(draw, 1e3);
    draw();
  }
  const shutdown = async (signal) => {
    if (heartbeatTimer) clearInterval(heartbeatTimer);
    process.stdout.write(`

[launch-beacon] caught ${signal}, shutting down\u2026
`);
    try {
      await server.close();
    } catch (err) {
      process.stderr.write(`[launch-beacon] server close error: ${err.message}
`);
    }
    try {
      await writer.close();
    } catch (err) {
      process.stderr.write(`[launch-beacon] writer close error: ${err.message}
`);
    }
    process.stdout.write(`  events written: ${writer.count()}
`);
    process.stdout.write(`  file:           ${outPath}
`);
    process.exit(0);
  };
  process.on("SIGINT", () => void shutdown("SIGINT"));
  process.on("SIGTERM", () => void shutdown("SIGTERM"));
}
function printHelp() {
  process.stdout.write(
    `launch-beacon

Usage:
  launch-beacon monitor [flags]    Start the HTTP receiver for beacon monitor batches

Flags for "monitor":
  --port <n>       port to listen on (default: 9876)
  --host <host>    host to bind to (default: 127.0.0.1)
  --cwd <path>     project root for the output file (default: process.cwd())
  --out <path>     output NDJSON file (default: <cwd>/.launchsecure/beacon-<token>.ndjson)
  --token <str>    use this token instead of a random 8-char hex
  --quiet          suppress the live heartbeat line

Env:
  BEACON_NDJSON_DIR  override the default output directory; same var the MCP
                     server reads so monitor + MCP land on the same files.

The printed URL is what you paste into the beacon debug panel. Beacon POSTs
monitor batches there; this process writes each event as a JSON line to the
output file. Ctrl+C shuts down cleanly.
`
  );
}
async function main() {
  const argv = process.argv.slice(2);
  const subcommand = argv[0];
  if (!subcommand || subcommand === "monitor") {
    await runMonitor(subcommand === "monitor" ? argv.slice(1) : argv);
    return;
  }
  if (subcommand === "mcp") {
    const { startBeaconMcpServer: startBeaconMcpServer2 } = await Promise.resolve().then(() => (init_beacon_mcp(), beacon_mcp_exports));
    startBeaconMcpServer2();
    return;
  }
  if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
    printHelp();
    return;
  }
  die(`unknown subcommand: ${subcommand}`);
}
function die(msg) {
  process.stderr.write(`[launch-beacon] ${msg}
`);
  process.exit(2);
}
void main();
