#!/usr/bin/env node
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/server/radar-docker-init-entry.ts
var radar_docker_init_entry_exports = {};
__export(radar_docker_init_entry_exports, {
  maybeProvisionIngress: () => maybeProvisionIngress,
  spawnServiceGroup: () => spawnServiceGroup
});
module.exports = __toCommonJS(radar_docker_init_entry_exports);
var import_node_child_process = require("node:child_process");
var import_node_fs3 = require("node:fs");
var import_node_path3 = require("node:path");

// src/server/radar/mcp.ts
var import_node_https = require("node:https");
var import_node_http = require("node:http");
var ProjectMcpClient = class {
  constructor(opts) {
    this.initialized = false;
    this.callId = 1;
    this.mcpUrl = new URL("/api/mcp/project", opts.serverUrl);
    this.headers = {
      "Content-Type": "application/json",
      "Accept": "application/json, text/event-stream",
      "Authorization": `Bearer ${opts.pat}`
    };
    if (opts.orgSlug) this.headers["X-Org-Slug"] = opts.orgSlug;
    if (opts.projectSlug) this.headers["X-Project-Slug"] = opts.projectSlug;
  }
  async call(toolName, args) {
    await this.ensureInitialized();
    const body = JSON.stringify({
      jsonrpc: "2.0",
      id: this.callId++,
      method: "tools/call",
      params: { name: toolName, arguments: args }
    });
    const resp = await this.send(body);
    const parsed = parseBody(resp.body);
    if (parsed.error) {
      throw new Error(`MCP ${toolName} failed: ${parsed.error.message ?? JSON.stringify(parsed.error)}`);
    }
    const text = parsed.result?.content?.[0]?.text;
    if (!text) return parsed.result;
    if (text.startsWith("\u2500\u2500 Error")) {
      const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
      const reason = lines.find((l) => !l.startsWith("\u2500\u2500")) ?? text;
      throw new Error(`MCP ${toolName} failed: ${reason}`);
    }
    try {
      return JSON.parse(text);
    } catch {
      return text;
    }
  }
  async ensureInitialized() {
    if (this.initialized) return;
    const initBody = JSON.stringify({
      jsonrpc: "2.0",
      id: this.callId++,
      method: "initialize",
      params: {
        protocolVersion: "2025-03-26",
        capabilities: {},
        clientInfo: { name: "launchpod-feedback-agent", version: "0.0.1" }
      }
    });
    const initResp = await this.send(initBody);
    if (initResp.sessionId) this.sessionId = initResp.sessionId;
    const notifBody = JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" });
    await this.send(notifBody);
    this.initialized = true;
  }
  send(body) {
    return new Promise((resolve, reject) => {
      const headers = {
        ...this.headers,
        "Content-Length": String(Buffer.byteLength(body))
      };
      if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
      const requester = this.mcpUrl.protocol === "https:" ? import_node_https.request : import_node_http.request;
      const req = requester(
        {
          host: this.mcpUrl.hostname,
          port: this.mcpUrl.port || (this.mcpUrl.protocol === "https:" ? 443 : 80),
          path: this.mcpUrl.pathname + this.mcpUrl.search,
          method: "POST",
          headers
        },
        (res) => {
          const chunks = [];
          res.on("data", (c) => chunks.push(c));
          res.on("end", () => {
            const text = Buffer.concat(chunks).toString("utf-8");
            if (res.statusCode && res.statusCode >= 400) {
              reject(new Error(`MCP HTTP ${res.statusCode}: ${text}`));
              return;
            }
            const sid = res.headers["mcp-session-id"];
            resolve({ body: text, sessionId: typeof sid === "string" ? sid : void 0 });
          });
        }
      );
      req.on("error", reject);
      req.write(body);
      req.end();
    });
  }
};
function parseBody(text) {
  if (!text) return {};
  if (text.startsWith("event:") || text.includes("\ndata: ")) {
    for (const line of text.split("\n")) {
      if (line.startsWith("data: ")) {
        try {
          return JSON.parse(line.slice(6));
        } catch {
        }
      }
    }
  }
  try {
    return JSON.parse(text);
  } catch {
    return {};
  }
}

// src/server/launch-kit-services.ts
var import_node_fs = require("node:fs");
var import_node_path = require("node:path");
var SHORTHANDS = {
  radar: { port: 3517, bin: "launch-radar", args: [] },
  sequencer: { port: 3517, bin: "launch-sequencer", args: [] },
  chart: { port: 52819, bin: "launch-chart", args: ["serve"] },
  deck: { port: 52829, bin: "launch-deck", args: ["serve"] },
  council: { port: 52839, bin: "launch-council", args: ["serve"] }
};
var DNS_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
function defaultServices() {
  return [expandShorthand("radar")];
}
function expandShorthand(name) {
  const def = SHORTHANDS[name];
  if (!def) {
    throw new Error(
      `[launch-kit-services] unknown shorthand "${name}" \u2014 known: ${Object.keys(SHORTHANDS).join(", ")}. Use an object entry { name, port, bin, args } for custom services.`
    );
  }
  return { name, port: def.port, bin: def.bin, args: [...def.args] };
}
function coerceEntry(raw, index) {
  if (typeof raw === "string") {
    return expandShorthand(raw);
  }
  if (typeof raw !== "object" || raw === null) {
    throw new Error(`[launch-kit-services] entry #${index} must be a string shorthand or an object`);
  }
  const r = raw;
  if (typeof r.name !== "string" || typeof r.port !== "number" || typeof r.bin !== "string") {
    throw new Error(`[launch-kit-services] entry #${index}: { name:string, port:number, bin:string } required`);
  }
  if (r.args !== void 0 && (!Array.isArray(r.args) || r.args.some((a) => typeof a !== "string"))) {
    throw new Error(`[launch-kit-services] entry #${index}: args must be a string[]`);
  }
  return {
    name: r.name,
    port: r.port,
    bin: r.bin,
    args: r.args ?? []
  };
}
function validate(services) {
  if (services.length === 0) {
    throw new Error(`[launch-kit-services] resolved an empty service list`);
  }
  const seenNames = /* @__PURE__ */ new Set();
  const seenPorts = /* @__PURE__ */ new Set();
  for (const s of services) {
    if (!DNS_NAME_RE.test(s.name)) {
      throw new Error(`[launch-kit-services] service name "${s.name}" is not DNS-safe (lowercase letters/digits/hyphens, \u226463 chars, no leading/trailing hyphen)`);
    }
    if (seenNames.has(s.name)) {
      throw new Error(`[launch-kit-services] duplicate service name "${s.name}"`);
    }
    seenNames.add(s.name);
    if (!Number.isInteger(s.port) || s.port < 1 || s.port > 65535) {
      throw new Error(`[launch-kit-services] service "${s.name}" has invalid port ${s.port}`);
    }
    if (seenPorts.has(s.port)) {
      throw new Error(`[launch-kit-services] duplicate port ${s.port} (services must each listen on a unique port)`);
    }
    seenPorts.add(s.port);
  }
  return services;
}
function resolveServices(opts = {}) {
  const env = opts.env ?? process.env;
  const cwd = opts.cwd ?? process.cwd();
  const rawEnv = env.LAUNCHKIT_SERVICES?.trim();
  if (rawEnv) {
    let parsed;
    try {
      parsed = JSON.parse(rawEnv);
    } catch (err) {
      throw new Error(`[launch-kit-services] LAUNCHKIT_SERVICES is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
    }
    if (!Array.isArray(parsed)) {
      throw new Error(`[launch-kit-services] LAUNCHKIT_SERVICES must be a JSON array`);
    }
    return validate(parsed.map(coerceEntry));
  }
  const filePath = (0, import_node_path.join)(cwd, ".launchpod", "services.json");
  if ((0, import_node_fs.existsSync)(filePath)) {
    let parsed;
    try {
      parsed = JSON.parse((0, import_node_fs.readFileSync)(filePath, "utf8"));
    } catch (err) {
      throw new Error(`[launch-kit-services] ${filePath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
    }
    if (!Array.isArray(parsed)) {
      throw new Error(`[launch-kit-services] ${filePath} must be a JSON array`);
    }
    return validate(parsed.map(coerceEntry));
  }
  return validate(defaultServices());
}
var SHORTHAND_NAMES = Object.keys(SHORTHANDS);

// src/server/cf-ingress.ts
var import_node_fs2 = require("node:fs");
var import_node_path2 = require("node:path");
var CF_API_BASE = "https://api.cloudflare.com/client/v4";
var CF_ERR_DNS_RECORD_EXISTS = 81053;
async function cf(opts) {
  const res = await fetch(`${CF_API_BASE}${opts.path}`, {
    method: opts.method,
    headers: {
      Authorization: `Bearer ${opts.apiToken}`,
      "Content-Type": "application/json",
      Accept: "application/json",
      "User-Agent": "launch-kit/cf-ingress"
    },
    body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
    signal: AbortSignal.timeout(15e3)
  });
  const text = await res.text();
  let parsed;
  try {
    parsed = text ? JSON.parse(text) : { success: false };
  } catch {
    throw new Error(`[cf] ${opts.method} ${opts.path} \u2192 ${res.status}, non-JSON body: ${text.slice(0, 200)}`);
  }
  return parsed;
}
function isNotFound(env) {
  return !env.success && (env.errors ?? []).some((e) => e.code === 7003 || e.code === 1001 || e.code === 81044);
}
function loadState(path) {
  if (!(0, import_node_fs2.existsSync)(path)) return null;
  try {
    const parsed = JSON.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
    if (typeof parsed?.tunnelId === "string" && typeof parsed?.accountId === "string") {
      return parsed;
    }
    return null;
  } catch {
    return null;
  }
}
function saveState(path, state) {
  const dir = (0, import_node_path2.dirname)(path);
  if (!(0, import_node_fs2.existsSync)(dir)) (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
  (0, import_node_fs2.writeFileSync)(path, JSON.stringify(state, null, 2));
}
async function ensureTunnel(input, knownTunnelId) {
  if (knownTunnelId) {
    const got = await cf({
      apiToken: input.apiToken,
      method: "GET",
      path: `/accounts/${input.accountId}/cfd_tunnel/${knownTunnelId}`
    });
    if (got.success && got.result && !got.result.deleted_at) {
      return knownTunnelId;
    }
    if (!isNotFound(got) && !got.success) {
      throw new Error(`[cf] tunnel GET failed: ${JSON.stringify(got.errors)}`);
    }
  }
  const created = await cf({
    apiToken: input.apiToken,
    method: "POST",
    path: `/accounts/${input.accountId}/cfd_tunnel`,
    body: { name: input.tunnelName, config_src: "cloudflare" }
  });
  if (!created.success || !created.result) {
    throw new Error(`[cf] tunnel create failed: ${JSON.stringify(created.errors)}`);
  }
  return created.result.id;
}
async function fetchConnectorToken(input, tunnelId) {
  const res = await cf({
    apiToken: input.apiToken,
    method: "GET",
    path: `/accounts/${input.accountId}/cfd_tunnel/${tunnelId}/token`
  });
  if (!res.success || typeof res.result !== "string") {
    throw new Error(`[cf] connector-token fetch failed: ${JSON.stringify(res.errors)}`);
  }
  return res.result;
}
async function setIngressConfig(input, tunnelId) {
  const ingress = input.services.map((s) => ({
    hostname: `${s.name}.${input.zone.name}`,
    service: `http://localhost:${s.port}`
  }));
  ingress.push({ service: "http_status:404" });
  const res = await cf({
    apiToken: input.apiToken,
    method: "PUT",
    path: `/accounts/${input.accountId}/cfd_tunnel/${tunnelId}/configurations`,
    body: { config: { ingress } }
  });
  if (!res.success) {
    throw new Error(`[cf] ingress config PUT failed: ${JSON.stringify(res.errors)}`);
  }
}
async function ensureDnsRecord(input, tunnelId, service) {
  const fqdn = `${service.name}.${input.zone.name}`;
  const target = `${tunnelId}.cfargotunnel.com`;
  const existing = await cf({
    apiToken: input.apiToken,
    method: "GET",
    path: `/zones/${input.zone.id}/dns_records?name=${encodeURIComponent(fqdn)}&type=CNAME`
  });
  if (existing.success && Array.isArray(existing.result) && existing.result.length > 0) {
    const rec = existing.result[0];
    if (rec.content === target) return;
    const upd = await cf({
      apiToken: input.apiToken,
      method: "PUT",
      path: `/zones/${input.zone.id}/dns_records/${rec.id}`,
      body: { type: "CNAME", name: fqdn, content: target, proxied: true, ttl: 1 }
    });
    if (!upd.success) {
      throw new Error(`[cf] DNS record update for ${fqdn} failed: ${JSON.stringify(upd.errors)}`);
    }
    return;
  }
  const created = await cf({
    apiToken: input.apiToken,
    method: "POST",
    path: `/zones/${input.zone.id}/dns_records`,
    body: { type: "CNAME", name: fqdn, content: target, proxied: true, ttl: 1 }
  });
  if (created.success) return;
  if ((created.errors ?? []).some((e) => e.code === CF_ERR_DNS_RECORD_EXISTS)) return;
  throw new Error(`[cf] DNS record create for ${fqdn} failed: ${JSON.stringify(created.errors)}`);
}
async function provisionIngress(input) {
  const prior = loadState(input.stateFile);
  const tunnelId = await ensureTunnel(input, prior?.tunnelId ?? null);
  saveState(input.stateFile, {
    tunnelId,
    accountId: input.accountId,
    tunnelName: input.tunnelName,
    zoneId: input.zone.id
  });
  const connectorToken = await fetchConnectorToken(input, tunnelId);
  await setIngressConfig(input, tunnelId);
  await Promise.all(input.services.map((s) => ensureDnsRecord(input, tunnelId, s)));
  const hostnames = {};
  for (const s of input.services) hostnames[s.name] = `${s.name}.${input.zone.name}`;
  return { tunnelId, connectorToken, hostnames };
}

// src/server/radar-docker-init-entry.ts
var REQUIRED_ENV = [
  "CLAUDE_CREDENTIALS_B64",
  "LS_PAT",
  "LS_ORG_SLUG",
  "LS_PROJECT_SLUG"
];
function fail(message) {
  console.error(message);
  process.exit(1);
}
function requireEnv(name) {
  const v = process.env[name];
  if (!v) fail(`ERROR: ${name} is required but not set`);
  return v;
}
function run(cmd, args, stdio = "inherit") {
  const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio });
  return r.status ?? 1;
}
async function setupFromCloud() {
  const pat = requireEnv("LS_PAT");
  const orgSlug = requireEnv("LS_ORG_SLUG");
  const projectSlug = requireEnv("LS_PROJECT_SLUG");
  const serverUrl = process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app";
  const mcp = new ProjectMcpClient({ serverUrl, pat, orgSlug, projectSlug });
  let bundle;
  try {
    bundle = await mcp.call("radar_bootstrap_get", {});
  } catch (err) {
    fail(`[entrypoint] radar_bootstrap_get failed (${err instanceof Error ? err.message : String(err)}) \u2014 check LS_PAT has mcp:radar:bootstrap scope and LS_ORG_SLUG/LS_PROJECT_SLUG point at a project the user has access to.`);
  }
  if (!process.env.GIT_USER_NAME) process.env.GIT_USER_NAME = bundle.gitName;
  if (!process.env.GIT_USER_EMAIL) process.env.GIT_USER_EMAIL = bundle.gitEmail;
  if (!process.env.GH_TOKEN && bundle.githubToken) process.env.GH_TOKEN = bundle.githubToken;
  if (!process.env.GH_TOKEN) {
    fail(`[entrypoint] no GH_TOKEN available \u2014 user has not connected GitHub (githubTokenStatus=${bundle.githubTokenStatus}). Connect GitHub in LS or pre-set GH_TOKEN in the container env.`);
  }
  const cfNote = bundle.cloudflareToken ? "cloudflare=connected" : "cloudflare=none";
  console.log(`[entrypoint] bundle from cloud: org=${orgSlug} project=${projectSlug} git=${process.env.GIT_USER_NAME} <${process.env.GIT_USER_EMAIL}> github=${bundle.githubTokenStatus.toLowerCase()} ${cfNote}`);
  return bundle;
}
function setupClaudeCredentials() {
  const home = process.env.HOME ?? "/home/launchpod";
  const claudeDir = (0, import_node_path3.join)(home, ".claude");
  (0, import_node_fs3.mkdirSync)(claudeDir, { recursive: true });
  const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
  const credsPath = (0, import_node_path3.join)(claudeDir, ".credentials.json");
  (0, import_node_fs3.writeFileSync)(credsPath, decoded);
  (0, import_node_fs3.chmodSync)(credsPath, 384);
  const configPath = (0, import_node_path3.join)(home, ".claude.json");
  let cfg = {};
  if ((0, import_node_fs3.existsSync)(configPath)) {
    try {
      cfg = JSON.parse((0, import_node_fs3.readFileSync)(configPath, "utf8"));
    } catch {
      cfg = {};
    }
  }
  cfg.hasCompletedOnboarding = true;
  cfg.lastOnboardingVersion = cfg.lastOnboardingVersion ?? "2.1.159";
  cfg.numStartups = (cfg.numStartups ?? 0) + 1;
  cfg.installMethod = cfg.installMethod ?? "global";
  (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(cfg, null, 2));
  (0, import_node_fs3.chmodSync)(configPath, 384);
}
function setupGitAndGh() {
  const name = process.env.GIT_USER_NAME ?? "Radar Bot";
  const email = process.env.GIT_USER_EMAIL ?? "radar@launchpod.local";
  const status = run("launch-kit", ["setup-git", `--identity=${name} <${email}>`]);
  if (status !== 0) fail(`[entrypoint] launch-kit setup-git failed (status ${status})`);
}
function initWorkspaceIfEmpty() {
  process.chdir("/workspace");
  if ((0, import_node_fs3.existsSync)(".git")) {
    console.log("[entrypoint] /workspace already initialized \u2014 skipping init");
    return;
  }
  console.log("[entrypoint] /workspace is empty \u2014 running launch-kit init");
  const status = run("launch-kit", [
    "init",
    `--token=${requireEnv("LS_PAT")}`,
    `--org=${requireEnv("LS_ORG_SLUG")}`,
    `--project=${requireEnv("LS_PROJECT_SLUG")}`,
    `--url=${process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app"}`,
    `--dir=/workspace`
  ]);
  if (status !== 0) fail(`[entrypoint] launch-kit init failed (status ${status})`);
}
async function maybeProvisionIngress(bundle, services, projectSlug) {
  const token = bundle.cloudflareToken ?? null;
  const accountId = bundle.cloudflareAccountId ?? null;
  const zones = bundle.cloudflareZones ?? [];
  if (!token && !accountId && zones.length === 0) return null;
  if (!token || !accountId) {
    fail(`[entrypoint] cloudflare integration is partial \u2014 token=${token ? "set" : "missing"} accountId=${accountId ? "set" : "missing"}. Re-connect the Cloudflare provider in LS.`);
  }
  const baseDomain = process.env.LAUNCHKIT_CF_BASE_DOMAIN?.trim();
  let chosen = null;
  if (baseDomain) {
    chosen = zones.find((z) => z.name === baseDomain) ?? null;
    if (!chosen) {
      fail(`[entrypoint] LAUNCHKIT_CF_BASE_DOMAIN="${baseDomain}" is not among the connected CF token's zones (${zones.map((z) => z.name).join(", ") || "none"}). Either change the env or grant Zone:Read on that zone in the CF token.`);
    }
  } else if (zones.length === 1) {
    chosen = { id: zones[0].id, name: zones[0].name };
  } else {
    fail(`[entrypoint] cloudflare token covers ${zones.length} zones (${zones.map((z) => z.name).join(", ")}) \u2014 set LAUNCHKIT_CF_BASE_DOMAIN to pick one.`);
  }
  const stateFile = "/workspace/.launchpod/launch-kit-tunnel.json";
  console.log(`[entrypoint] provisioning CF named tunnel \u2014 name=launch-kit-${projectSlug} zone=${chosen.name} services=${services.map((s) => s.name).join(",")}`);
  const result = await provisionIngress({
    apiToken: token,
    accountId,
    zone: chosen,
    tunnelName: `launch-kit-${projectSlug}`,
    services: services.map((s) => ({ name: s.name, port: s.port })),
    stateFile
  });
  for (const [name, fqdn] of Object.entries(result.hostnames)) {
    console.log(`[entrypoint]   ${name} \u2192 https://${fqdn}`);
  }
  return result;
}
function spawnServiceGroup(services) {
  const children = [];
  let shuttingDown = false;
  const killAll = (signal = "SIGTERM") => {
    if (shuttingDown) return;
    shuttingDown = true;
    for (const c of children) {
      try {
        c.proc.kill(signal);
      } catch {
      }
    }
  };
  const prefixStream = (name, stream, sink) => {
    let buf = "";
    stream.setEncoding("utf8");
    stream.on("data", (chunk) => {
      buf += chunk;
      const lines = buf.split("\n");
      buf = lines.pop() ?? "";
      for (const line of lines) sink.write(`[${name}] ${line}
`);
    });
    stream.on("end", () => {
      if (buf) sink.write(`[${name}] ${buf}
`);
    });
  };
  const signalHandlers = [];
  const installSignals = () => {
    for (const sig of ["SIGTERM", "SIGINT", "SIGHUP"]) {
      const fn = () => {
        console.log(`[entrypoint] received ${sig} \u2014 forwarding to ${children.length} child process(es)`);
        killAll(sig);
      };
      process.on(sig, fn);
      signalHandlers.push({ sig, fn });
    }
  };
  const removeSignals = () => {
    for (const h of signalHandlers) process.off(h.sig, h.fn);
    signalHandlers.length = 0;
  };
  return new Promise((resolve, reject) => {
    let exitedCount = 0;
    let firstFailure = null;
    for (const spec of services) {
      const args = [...spec.args, "--port", String(spec.port)];
      console.log(`[entrypoint] starting ${spec.name}: ${spec.bin} ${args.join(" ")}`);
      const proc = (0, import_node_child_process.spawn)(spec.bin, args, { stdio: ["ignore", "pipe", "pipe"] });
      children.push({ spec, proc });
      if (proc.stdout) prefixStream(spec.name, proc.stdout, process.stdout);
      if (proc.stderr) prefixStream(spec.name, proc.stderr, process.stderr);
      proc.on("exit", (code, signal) => {
        exitedCount += 1;
        const label = `[${spec.name}] exited code=${code ?? "?"} signal=${signal ?? "-"}`;
        if (!shuttingDown && code !== 0) {
          console.error(`[entrypoint] ${label} \u2014 bringing the group down`);
          if (!firstFailure) firstFailure = { name: spec.name, code, signal };
          killAll();
        } else {
          console.log(`[entrypoint] ${label}`);
        }
        if (exitedCount === children.length) {
          if (firstFailure) reject(new Error(`service "${firstFailure.name}" exited code=${firstFailure.code ?? "?"}`));
          else resolve();
        }
      });
      proc.on("error", (err) => {
        console.error(`[entrypoint] [${spec.name}] spawn error: ${err.message}`);
        if (!firstFailure) firstFailure = { name: spec.name, code: null, signal: null };
        killAll();
      });
    }
    installSignals();
  }).finally(removeSignals);
}
async function main() {
  for (const k of REQUIRED_ENV) requireEnv(k);
  const bundle = await setupFromCloud();
  setupClaudeCredentials();
  setupGitAndGh();
  initWorkspaceIfEmpty();
  let services;
  try {
    services = resolveServices();
  } catch (err) {
    fail(`[entrypoint] ${err instanceof Error ? err.message : String(err)}`);
  }
  console.log(`[entrypoint] services: ${services.map((s) => `${s.name}@${s.port}`).join(", ")}`);
  const ingress = await maybeProvisionIngress(bundle, services, requireEnv("LS_PROJECT_SLUG"));
  if (ingress) {
    process.env.RADAR_CF_TUNNEL_TOKEN = ingress.connectorToken;
    const radarFqdn = ingress.hostnames.radar;
    if (radarFqdn) process.env.RADAR_CF_TUNNEL_HOSTNAME = radarFqdn;
    else if (services.some((s) => s.name === "radar")) {
      fail(`[entrypoint] internal: ingress provisioned but no hostname for radar`);
    }
  } else if (services.length > 1) {
    const first = services[0];
    console.warn(
      `[entrypoint] \u26A0 quick mode \u2014 only the first service "${first.name}" (port ${first.port}) will be exposed via the ephemeral *.trycloudflare.com URL. Other service(s) [${services.slice(1).map((s) => s.name).join(", ")}] will run on localhost inside the container only. Connect a Cloudflare provider in LS and set LAUNCHKIT_CF_BASE_DOMAIN to expose all services with stable subdomains.`
    );
    if (first.name !== "radar") {
      console.warn(`[entrypoint] \u26A0 first service is "${first.name}", not "radar" \u2014 quick tunneling is owned by the radar agent today, so NO external URL will be available.`);
    }
  }
  try {
    await spawnServiceGroup(services);
    process.exit(0);
  } catch (err) {
    console.error(`[entrypoint] ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}
if (!process.env.VITEST) {
  main().catch((err) => {
    console.error(`[entrypoint] fatal: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  maybeProvisionIngress,
  spawnServiceGroup
});
