// @bun
// packages/runtime/src/base-daemon.ts
import { spawn, spawnSync as spawnSync2 } from "child_process";
import { existsSync as existsSync5, mkdirSync as mkdirSync4, openSync, writeFileSync as writeFileSync4 } from "fs";
import { homedir as homedir5 } from "os";
import { dirname as dirname4, join as join5, resolve as resolve3 } from "path";
import { fileURLToPath as fileURLToPath2 } from "url";

// packages/runtime/src/broker-process-manager.ts
import { spawnSync } from "child_process";
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
import { homedir as homedir3 } from "os";
import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
import { fileURLToPath } from "url";

// packages/runtime/src/broker-api.ts
import { request as httpRequest } from "http";
var DEFAULT_BROKER_JSON_REQUEST_TIMEOUT_MS = 30000;
function normalizeBaseUrl(baseUrl) {
  const trimmed = baseUrl.trim();
  if (!trimmed) {
    return trimmed;
  }
  try {
    return new URL(trimmed).toString();
  } catch {
    return trimmed;
  }
}
function requestHeaders(options) {
  const headers = {
    accept: "application/json",
    ...options.headers ?? {}
  };
  if (options.body !== undefined && !("content-type" in lowercaseKeys(headers))) {
    headers["content-type"] = "application/json";
  }
  return headers;
}
function lowercaseKeys(value) {
  const result = {};
  for (const key of Object.keys(value)) {
    result[key.toLowerCase()] = value[key] ?? "";
  }
  return result;
}
function requestBody(options) {
  return options.body === undefined ? undefined : JSON.stringify(options.body);
}
async function requestBrokerOverHttp(baseUrl, path, options) {
  const response = await fetch(new URL(path, normalizeBaseUrl(baseUrl)), {
    method: options.method ?? (options.body === undefined ? "GET" : "POST"),
    headers: requestHeaders(options),
    body: requestBody(options),
    signal: options.signal
  });
  return {
    ok: response.ok,
    status: response.status,
    text: await response.text()
  };
}
function shouldFallbackFromUnixSocket(error) {
  const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
  return code === "ENOENT" || code === "ECONNREFUSED" || code === "ENOTSOCK" || code === "EACCES" || code === "FailedToOpenSocket";
}
function errorMessage(error) {
  return error instanceof Error ? error.message : String(error);
}
function requestBrokerOverUnixSocket(socketPath, baseUrl, path, options) {
  return new Promise((resolve, reject) => {
    const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
    const url = new URL(path, normalizedBaseUrl);
    const body = requestBody(options);
    const headers = requestHeaders(options);
    if (body !== undefined) {
      headers["content-length"] = Buffer.byteLength(body).toString();
    }
    const request = httpRequest({
      socketPath,
      path: `${url.pathname}${url.search}`,
      method: options.method ?? (body === undefined ? "GET" : "POST"),
      headers
    }, (response) => {
      let text = "";
      response.setEncoding("utf8");
      response.on("data", (chunk) => {
        text += String(chunk);
      });
      response.on("end", () => {
        options.signal?.removeEventListener("abort", handleAbort);
        resolve({
          ok: Boolean(response.statusCode && response.statusCode >= 200 && response.statusCode < 300),
          status: response.statusCode ?? 0,
          text
        });
      });
    });
    const handleAbort = () => {
      request.destroy(options.signal?.reason instanceof Error ? options.signal.reason : new Error("Scout broker request aborted"));
    };
    request.on("error", (error) => {
      options.signal?.removeEventListener("abort", handleAbort);
      reject(error);
    });
    if (options.signal) {
      if (options.signal.aborted) {
        handleAbort();
        return;
      }
      options.signal.addEventListener("abort", handleAbort, { once: true });
    }
    if (body !== undefined) {
      request.write(body);
    }
    request.end();
  });
}
function defaultBrokerRequestSignal(options) {
  return options.signal ?? AbortSignal.timeout(DEFAULT_BROKER_JSON_REQUEST_TIMEOUT_MS);
}
async function requestBrokerWire(baseUrl, path, options) {
  const requestOptions = {
    ...options,
    signal: defaultBrokerRequestSignal(options)
  };
  const socketPath = options.socketPath?.trim();
  if (socketPath) {
    try {
      return {
        response: await requestBrokerOverUnixSocket(socketPath, baseUrl, path, requestOptions),
        trace: {
          transport: "unix_socket",
          socketPath
        }
      };
    } catch (error) {
      if (!shouldFallbackFromUnixSocket(error)) {
        throw error;
      }
      return {
        response: await requestBrokerOverHttp(baseUrl, path, requestOptions),
        trace: {
          transport: "http",
          socketPath,
          socketFallbackError: `${socketPath}: ${errorMessage(error)}`
        }
      };
    }
  }
  return {
    response: await requestBrokerOverHttp(baseUrl, path, requestOptions),
    trace: {
      transport: "http"
    }
  };
}
async function requestScoutBrokerJsonWithTrace(baseUrl, path, options = {}) {
  const { response, trace } = await requestBrokerWire(baseUrl, path, options);
  let parsed;
  let parsedJson = false;
  if (response.text.length > 0) {
    try {
      parsed = JSON.parse(response.text);
      parsedJson = true;
    } catch {
      parsedJson = false;
    }
  }
  if (!response.ok) {
    if (parsedJson && options.acceptErrorJson?.(parsed)) {
      return { value: parsed, trace };
    }
    throw new Error(`${path} returned ${response.status}: ${response.text}`);
  }
  if (parsedJson) {
    return { value: parsed, trace };
  }
  return { value: undefined, trace };
}

// packages/runtime/src/support-paths.ts
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
import { homedir } from "os";
import { join } from "path";
var OPENSCOUT_RPC_CUTOVER_MARKER = "rpc-runtime-cutover-v1";
function resolveOpenScoutSupportPaths() {
  const home = homedir();
  const supportDirectory = process.env.OPENSCOUT_SUPPORT_DIRECTORY ?? join(home, "Library", "Application Support", "OpenScout");
  const logsDirectory = join(supportDirectory, "logs");
  const runtimeDirectory = join(supportDirectory, "runtime");
  const catalogDirectory = join(supportDirectory, "catalog");
  const controlHome = process.env.OPENSCOUT_CONTROL_HOME ?? join(home, ".openscout", "control-plane");
  const knowledgeDirectory = join(controlHome, "knowledge");
  return {
    supportDirectory,
    logsDirectory,
    appLogsDirectory: join(logsDirectory, "app"),
    brokerLogsDirectory: join(logsDirectory, "broker"),
    runtimeDirectory,
    catalogDirectory,
    relayAgentsDirectory: join(runtimeDirectory, "agents"),
    settingsPath: join(supportDirectory, "settings.json"),
    harnessCatalogPath: join(catalogDirectory, "harness-catalog.json"),
    relayAgentsRegistryPath: join(supportDirectory, "relay-agents.json"),
    managedInstallsPath: join(supportDirectory, "managed-installs.json"),
    relayHubDirectory: process.env.OPENSCOUT_RELAY_HUB ?? join(home, ".openscout", "relay"),
    controlHome,
    knowledgeDirectory,
    knowledgeQmdDirectory: join(knowledgeDirectory, "qmd"),
    knowledgeSqlitePath: join(knowledgeDirectory, "knowledge.sqlite"),
    desktopStatusPath: join(supportDirectory, "agent-status.json"),
    workspaceStatePath: join(supportDirectory, "workspace-state.json"),
    cutoverMarkerPath: join(supportDirectory, OPENSCOUT_RPC_CUTOVER_MARKER)
  };
}
function ensureOpenScoutCleanSlateSync() {
  const paths = resolveOpenScoutSupportPaths();
  if (existsSync(paths.cutoverMarkerPath)) {
    return;
  }
  rmSync(paths.supportDirectory, { recursive: true, force: true });
  rmSync(paths.controlHome, { recursive: true, force: true });
  rmSync(paths.relayHubDirectory, { recursive: true, force: true });
  mkdirSync(paths.supportDirectory, { recursive: true });
  writeFileSync(paths.cutoverMarkerPath, `${Date.now()}
`, "utf8");
}

// packages/runtime/src/tool-resolution.ts
import { accessSync, constants, existsSync as existsSync2 } from "fs";
import { homedir as homedir2 } from "os";
import { basename, dirname, join as join2, resolve } from "path";
var DEFAULT_COMMON_EXECUTABLE_DIRECTORIES = [
  join2(homedir2(), ".bun", "bin"),
  "/opt/homebrew/bin",
  "/usr/local/bin"
];
function expandHomePath(value, env = process.env) {
  const home = env.HOME ?? homedir2();
  if (value === "~") {
    return home;
  }
  if (value.startsWith("~/")) {
    return join2(home, value.slice(2));
  }
  return value;
}
function isExecutablePath(candidate) {
  if (!candidate) {
    return false;
  }
  try {
    accessSync(candidate, constants.X_OK);
    return true;
  } catch {
    return false;
  }
}
function splitPathEntries(env = process.env) {
  const separator = process.platform === "win32" ? ";" : ":";
  return (env.PATH ?? "").split(separator).filter(Boolean);
}
function dedupeDirectories(values, env) {
  const seen = new Set;
  const resolvedEntries = [];
  for (const value of values) {
    const normalized = resolve(expandHomePath(value, env));
    if (seen.has(normalized)) {
      continue;
    }
    seen.add(normalized);
    resolvedEntries.push(normalized);
  }
  return resolvedEntries;
}
function resolveExecutableFromSearch(options) {
  const env = options.env ?? process.env;
  const envKeys = options.envKeys ?? [];
  for (const envKey of envKeys) {
    const explicit = env[envKey]?.trim();
    if (!explicit) {
      continue;
    }
    const expanded = expandHomePath(explicit, env);
    if (isExecutablePath(expanded)) {
      return { path: resolve(expanded), source: "env" };
    }
    const foundOnPath = findExecutableOnSearchPath(explicit, env);
    if (foundOnPath) {
      return { path: foundOnPath.path, source: "env" };
    }
  }
  const commonDirectories = dedupeDirectories([...options.commonDirectories ?? DEFAULT_COMMON_EXECUTABLE_DIRECTORIES], env);
  const searchDirectories = dedupeDirectories([
    ...splitPathEntries(env),
    ...options.extraDirectories ?? [],
    ...commonDirectories
  ], env);
  for (const directory of searchDirectories) {
    for (const name of options.names) {
      const candidate = join2(directory, name);
      if (isExecutablePath(candidate)) {
        return {
          path: candidate,
          source: commonDirectories.includes(directory) ? "common-path" : "path"
        };
      }
    }
  }
  return null;
}
function resolveBunExecutable(env = process.env) {
  return resolveExecutableFromSearch({
    env,
    envKeys: ["OPENSCOUT_BUN_BIN", "SCOUT_BUN_BIN", "BUN_BIN"],
    names: ["bun"]
  });
}
function findExecutableOnSearchPath(name, env) {
  const searchDirectories = dedupeDirectories([
    ...splitPathEntries(env),
    ...DEFAULT_COMMON_EXECUTABLE_DIRECTORIES
  ], env);
  for (const directory of searchDirectories) {
    const candidate = join2(directory, name);
    if (isExecutablePath(candidate)) {
      return { path: candidate, source: "path" };
    }
  }
  return null;
}

// packages/runtime/src/broker-process-manager.ts
function isTmpPath(p) {
  return /^\/(?:private\/)?tmp\//.test(p);
}
var DEFAULT_BROKER_HOST = "127.0.0.1";
var DEFAULT_BROKER_HOST_MESH = "0.0.0.0";
var DEFAULT_BROKER_PORT = 65535;
var DEFAULT_ADVERTISE_SCOPE = "local";
function resolveAdvertiseScope() {
  const raw = (process.env.OPENSCOUT_ADVERTISE_SCOPE ?? "").trim().toLowerCase();
  if (raw === "mesh")
    return "mesh";
  if (raw === "local")
    return "local";
  return DEFAULT_ADVERTISE_SCOPE;
}
function resolveBrokerHost(scope = resolveAdvertiseScope()) {
  const explicit = process.env.OPENSCOUT_BROKER_HOST;
  if (typeof explicit === "string" && explicit.trim().length > 0) {
    return explicit;
  }
  return scope === "mesh" ? DEFAULT_BROKER_HOST_MESH : DEFAULT_BROKER_HOST;
}
var BROKER_SERVICE_POLL_INTERVAL_MS = 100;
var DEFAULT_BROKER_START_TIMEOUT_MS = 15000;
var DEFAULT_BROKER_STOP_TIMEOUT_MS = 20000;
function buildDefaultBrokerUrl(host = DEFAULT_BROKER_HOST, port = DEFAULT_BROKER_PORT) {
  return `http://${host}:${port}`;
}
function buildDefaultBrokerSocketPath(runtimeDirectory) {
  return join3(runtimeDirectory, "broker.sock");
}
var DEFAULT_BROKER_URL = buildDefaultBrokerUrl();
function runtimePackageDir() {
  const explicit = process.env.OPENSCOUT_RUNTIME_PACKAGE_DIR?.trim();
  if (explicit)
    return explicit;
  const fromBundledPackage = findBundledRuntimeDir();
  if (fromBundledPackage)
    return fromBundledPackage;
  const fromCwd = findWorkspaceRuntimeDir(process.cwd());
  if (fromCwd)
    return fromCwd;
  const fromGlobal = findGlobalRuntimeDir();
  if (fromGlobal)
    return fromGlobal;
  const moduleDir = dirname2(fileURLToPath(import.meta.url));
  return resolve2(moduleDir, "..");
}
function isInstalledRuntimePackageDir(candidate) {
  return existsSync3(join3(candidate, "package.json")) && existsSync3(join3(candidate, "bin", "openscout-runtime.mjs"));
}
function findGlobalRuntimeDir() {
  const candidates = [
    join3(homedir3(), ".bun", "node_modules", "@openscout", "runtime"),
    join3(homedir3(), ".bun", "install", "global", "node_modules", "@openscout", "runtime"),
    join3(homedir3(), ".bun", "install", "global", "node_modules", "@openscout", "scout", "node_modules", "@openscout", "runtime")
  ];
  for (const c of candidates) {
    if (isInstalledRuntimePackageDir(c))
      return c;
  }
  try {
    const result = spawnSync("which", ["scout"], { encoding: "utf8", timeout: 3000 });
    const scoutBin = result.stdout?.trim();
    if (scoutBin) {
      const scoutPkg = resolve2(scoutBin, "..", "..");
      if (isInstalledRuntimePackageDir(scoutPkg))
        return scoutPkg;
      const nested = join3(scoutPkg, "node_modules", "@openscout", "runtime");
      if (isInstalledRuntimePackageDir(nested))
        return nested;
      const sibling = resolve2(scoutPkg, "..", "runtime");
      if (isInstalledRuntimePackageDir(sibling))
        return sibling;
    }
  } catch {}
  return null;
}
function findBundledRuntimeDir() {
  const moduleDir = dirname2(fileURLToPath(import.meta.url));
  const candidate = resolve2(moduleDir, "..");
  return isInstalledRuntimePackageDir(candidate) ? candidate : null;
}
function findWorkspaceRuntimeDir(startDir) {
  let current = resolve2(startDir);
  while (true) {
    const candidate = join3(current, "packages", "runtime");
    if (existsSync3(join3(candidate, "package.json")) && existsSync3(join3(candidate, "src"))) {
      return candidate;
    }
    const parent = dirname2(current);
    if (parent === current)
      return null;
    current = parent;
  }
}
function resolveBunExecutable2() {
  const bun = resolveBunExecutable(process.env);
  if (bun) {
    return bun.path;
  }
  throw new Error("Unable to locate Bun for broker service management. Install Bun or set OPENSCOUT_BUN_BIN.");
}
function resolveBrokerServiceMode() {
  const explicit = (process.env.OPENSCOUT_BROKER_SERVICE_MODE ?? "").trim().toLowerCase();
  if (explicit === "prod" || explicit === "production") {
    return "prod";
  }
  if (explicit === "custom") {
    return "custom";
  }
  return "dev";
}
function resolveBrokerStartTimeoutMs() {
  const explicit = Number.parseInt(process.env.OPENSCOUT_BROKER_START_TIMEOUT_MS ?? "", 10);
  if (Number.isFinite(explicit) && explicit > 0) {
    return Math.max(explicit, BROKER_SERVICE_POLL_INTERVAL_MS);
  }
  return DEFAULT_BROKER_START_TIMEOUT_MS;
}
function resolveBrokerStopTimeoutMs() {
  const explicit = Number.parseInt(process.env.OPENSCOUT_BROKER_STOP_TIMEOUT_MS ?? "", 10);
  if (Number.isFinite(explicit) && explicit > 0) {
    return Math.max(explicit, BROKER_SERVICE_POLL_INTERVAL_MS);
  }
  return DEFAULT_BROKER_STOP_TIMEOUT_MS;
}
function resolveBrokerServiceLabel(mode) {
  const explicit = process.env.OPENSCOUT_SERVICE_LABEL?.trim() || process.env.OPENSCOUT_BROKER_SERVICE_LABEL?.trim();
  if (explicit) {
    return explicit;
  }
  switch (mode) {
    case "prod":
      return "com.openscout";
    case "custom":
      return "com.openscout.custom";
    case "dev":
    default:
      return "dev.openscout";
  }
}
function legacyBrokerServiceLabel(mode) {
  switch (mode) {
    case "prod":
      return "com.openscout.broker";
    case "custom":
      return "com.openscout.broker.custom";
    case "dev":
    default:
      return "dev.openscout.broker";
  }
}
function resolveBrokerServiceConfig() {
  const mode = resolveBrokerServiceMode();
  const label = resolveBrokerServiceLabel(mode);
  const uid = typeof process.getuid === "function" ? process.getuid() : Number.parseInt(process.env.UID ?? "0", 10);
  const supportPaths = resolveOpenScoutSupportPaths();
  const defaultSupportDir = join3(homedir3(), "Library", "Application Support", "OpenScout");
  const supportDirectory = isTmpPath(supportPaths.supportDirectory) ? defaultSupportDir : supportPaths.supportDirectory;
  const runtimeDirectory = join3(supportDirectory, "runtime");
  const logsDirectory = join3(supportDirectory, "logs", "broker");
  const controlHome = isTmpPath(supportPaths.controlHome) ? join3(homedir3(), ".openscout", "control-plane") : supportPaths.controlHome;
  const advertiseScope = resolveAdvertiseScope();
  const brokerHost = resolveBrokerHost(advertiseScope);
  const brokerPort = Number.parseInt(process.env.OPENSCOUT_BROKER_PORT ?? String(DEFAULT_BROKER_PORT), 10);
  const brokerUrl = process.env.OPENSCOUT_BROKER_URL ?? buildDefaultBrokerUrl(brokerHost, brokerPort);
  const brokerSocketPath = process.env.OPENSCOUT_BROKER_SOCKET_PATH ?? buildDefaultBrokerSocketPath(runtimeDirectory);
  const launchAgentPath = join3(homedir3(), "Library", "LaunchAgents", `${label}.plist`);
  return {
    label,
    mode,
    uid,
    domainTarget: `gui/${uid}`,
    serviceTarget: `gui/${uid}/${label}`,
    launchAgentPath,
    supportDirectory,
    runtimeDirectory,
    logsDirectory,
    stdoutLogPath: join3(logsDirectory, "stdout.log"),
    stderrLogPath: join3(logsDirectory, "stderr.log"),
    controlHome,
    runtimePackageDir: runtimePackageDir(),
    bunExecutable: resolveBunExecutable2(),
    brokerHost,
    brokerPort,
    brokerUrl,
    brokerSocketPath,
    advertiseScope,
    coreAgents: readCoreAgentsSync()
  };
}
function renderLaunchAgentPlist(config) {
  const launchPath = resolveLaunchAgentPATH();
  const coreAgentsValue = (config.coreAgents ?? []).join(",");
  const envEntries = {
    OPENSCOUT_BROKER_HOST: config.brokerHost,
    OPENSCOUT_BROKER_PORT: String(config.brokerPort),
    OPENSCOUT_BROKER_URL: config.brokerUrl,
    OPENSCOUT_BROKER_SOCKET_PATH: config.brokerSocketPath,
    OPENSCOUT_CONTROL_HOME: config.controlHome,
    OPENSCOUT_BROKER_SERVICE_MODE: config.mode,
    OPENSCOUT_BROKER_SERVICE_LABEL: config.label,
    OPENSCOUT_SERVICE_LABEL: config.label,
    OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope,
    HOME: homedir3(),
    PATH: launchPath,
    ...collectOptionalEnvVars([
      "OPENSCOUT_MESH_ID",
      "OPENSCOUT_MESH_SEEDS",
      "OPENSCOUT_MESH_DISCOVERY_INTERVAL_MS",
      "OPENSCOUT_NODE_NAME",
      "OPENSCOUT_NODE_ID",
      "OPENSCOUT_NODE_QUALIFIER",
      "OPENSCOUT_TAILSCALE_BIN",
      "OPENSCOUT_TAILSCALE_STATUS_JSON",
      "OPENSCOUT_SSE_KEEPALIVE_MS",
      "OPENSCOUT_WEB_EDGE_SCHEME",
      "OPENSCOUT_WEB_PUBLIC_ORIGIN",
      "OPENSCOUT_WEB_PORTAL_HOST",
      "OPENSCOUT_WEB_LOCAL_NAME",
      "OPENSCOUT_WEB_ADVERTISED_HOST",
      "OPENSCOUT_WEB_TRUSTED_HOSTS",
      "OPENSCOUT_WEB_TRUSTED_ORIGINS",
      "OPENSCOUT_WEB_PORT",
      "OPENSCOUT_WEB_FLAG_BUNDLE",
      "OPENSCOUT_WEB_EXPERIENCE",
      "OPENSCOUT_WEB_AB_VARIANT"
    ])
  };
  if (coreAgentsValue) {
    envEntries["OPENSCOUT_CORE_AGENTS"] = coreAgentsValue;
  }
  const envBlock = Object.entries(envEntries).map(([key, value]) => `
    <key>${xmlEscape(key)}</key>
    <string>${xmlEscape(value)}</string>`).join("");
  return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>${xmlEscape(config.label)}</string>
  <key>ProgramArguments</key>
  <array>
    <string>${xmlEscape(config.bunExecutable)}</string>
    <string>${xmlEscape(join3(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
    <string>base</string>
  </array>
  <key>WorkingDirectory</key>
  <string>${xmlEscape(config.runtimePackageDir)}</string>
  <key>RunAtLoad</key>
  <true/>
  <key>KeepAlive</key>
  <dict>
    <key>SuccessfulExit</key>
    <false/>
  </dict>
  <key>StandardOutPath</key>
  <string>${xmlEscape(config.stdoutLogPath)}</string>
  <key>StandardErrorPath</key>
  <string>${xmlEscape(config.stderrLogPath)}</string>
  <key>EnvironmentVariables</key>
  <dict>${envBlock}
  </dict>
</dict>
</plist>
`;
}
function readCoreAgentsSync() {
  try {
    const settingsPath = resolveOpenScoutSupportPaths().settingsPath;
    const raw = readFileSync(settingsPath, "utf8");
    const settings = JSON.parse(raw);
    const raw_agents = settings?.agents?.coreAgents;
    if (Array.isArray(raw_agents)) {
      return raw_agents.filter((v) => typeof v === "string" && v.trim().length > 0);
    }
  } catch {}
  return [];
}
function collectOptionalEnvVars(keys) {
  const entries = {};
  for (const key of keys) {
    const value = process.env[key];
    if (typeof value === "string" && value.trim().length > 0) {
      entries[key] = value;
    }
  }
  return entries;
}
function resolveLaunchAgentPATH() {
  const entries = [
    join3(homedir3(), ".bun", "bin"),
    ...(process.env.PATH ?? "").split(":").filter(Boolean),
    "/opt/homebrew/bin",
    "/usr/local/bin",
    "/usr/bin",
    "/bin",
    "/usr/sbin",
    "/sbin"
  ];
  return Array.from(new Set(entries)).filter((e) => !isTmpPath(e)).join(":");
}
function xmlEscape(value) {
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
}
function ensureParentDirectory(filePath) {
  mkdirSync2(dirname2(filePath), { recursive: true });
}
function ensureServiceDirectories(config) {
  ensureOpenScoutCleanSlateSync();
  mkdirSync2(config.supportDirectory, { recursive: true });
  mkdirSync2(config.runtimeDirectory, { recursive: true, mode: 448 });
  chmodSync(config.runtimeDirectory, 448);
  mkdirSync2(config.logsDirectory, { recursive: true });
  mkdirSync2(config.controlHome, { recursive: true });
  ensureParentDirectory(config.launchAgentPath);
}
function writeLaunchAgentPlist(config) {
  ensureServiceDirectories(config);
  writeFileSync2(config.launchAgentPath, renderLaunchAgentPlist(config), "utf8");
}
function bootoutCommand(config) {
  return `${launchctlPath()} bootout ${config.serviceTarget}`;
}
function legacyLaunchAgentPath(config) {
  return join3(homedir3(), "Library", "LaunchAgents", `${legacyBrokerServiceLabel(config.mode)}.plist`);
}
function legacyServiceTarget(config) {
  return `${config.domainTarget}/${legacyBrokerServiceLabel(config.mode)}`;
}
function bootoutLegacyBrokerService(config) {
  const legacyTarget = legacyServiceTarget(config);
  if (legacyTarget === config.serviceTarget) {
    return;
  }
  runCommand(launchctlPath(), ["bootout", legacyTarget], { allowFailure: true });
}
function runCommand(command, args, options) {
  const result = spawnSync(command, args, {
    env: {
      ...process.env,
      ...options?.env ?? {}
    },
    encoding: "utf8"
  });
  const stdout = (result.stdout ?? "").trim();
  const stderr = (result.stderr ?? "").trim();
  const exitCode = result.status ?? 1;
  if (exitCode !== 0 && !options?.allowFailure) {
    throw new Error(stderr || stdout || `${command} exited with status ${exitCode}`);
  }
  return { exitCode, stdout, stderr };
}
function launchctlPath() {
  return "/bin/launchctl";
}
function readLogLines(path) {
  if (!existsSync3(path)) {
    return [];
  }
  return readFileSync(path, "utf8").split(`
`).map((line) => line.trim()).filter(Boolean);
}
function isPackageScriptBanner(line) {
  return /^\$\s*(bun run|npm run|pnpm\b|yarn\b)/.test(line);
}
function selectLastRelevantLogLine(lines) {
  for (let index = lines.length - 1;index >= 0; index -= 1) {
    const line = lines[index];
    if (!isPackageScriptBanner(line)) {
      return line;
    }
  }
  return lines.at(-1) ?? null;
}
function readLastLogLine(paths) {
  let fallback = null;
  for (const path of paths) {
    const lines = readLogLines(path);
    if (lines.length === 0) {
      continue;
    }
    const relevantLine = selectLastRelevantLogLine(lines);
    if (!relevantLine) {
      continue;
    }
    if (!isPackageScriptBanner(relevantLine)) {
      return relevantLine;
    }
    fallback ??= relevantLine;
  }
  return fallback;
}
function parseLaunchctlPrint(output) {
  const pidMatch = output.match(/\bpid = (\d+)/);
  const stateMatch = output.match(/\bstate = ([^\n]+)/);
  const lastExitMatch = output.match(/\blast exit code = (-?\d+)/i) || output.match(/\blast exit status = (-?\d+)/i);
  return {
    pid: pidMatch ? Number.parseInt(pidMatch[1] ?? "0", 10) : null,
    launchdState: stateMatch?.[1]?.trim() ?? null,
    lastExitStatus: lastExitMatch ? Number.parseInt(lastExitMatch[1] ?? "0", 10) : null
  };
}
function inspectLaunchctl(config) {
  const printResult = runCommand(launchctlPath(), ["print", config.serviceTarget], { allowFailure: true });
  if (printResult.exitCode !== 0) {
    return {
      loaded: false,
      pid: null,
      launchdState: null,
      lastExitStatus: null,
      raw: printResult.stderr || printResult.stdout
    };
  }
  return {
    loaded: true,
    raw: printResult.stdout,
    ...parseLaunchctlPrint(printResult.stdout)
  };
}
async function fetchHealthSnapshot(config) {
  const controller = new AbortController;
  const timeout = setTimeout(() => controller.abort(), 1000);
  const checkedAt = Date.now();
  try {
    const { value: payload, trace } = await requestScoutBrokerJsonWithTrace(config.brokerUrl, "/health", {
      signal: controller.signal,
      socketPath: config.brokerSocketPath
    });
    return {
      reachable: true,
      ok: Boolean(payload.ok),
      checkedAt,
      transport: trace.transport,
      socketPath: trace.socketPath,
      socketFallbackError: trace.socketFallbackError,
      nodeId: payload.nodeId,
      meshId: payload.meshId,
      counts: payload.counts ? {
        nodes: payload.counts.nodes ?? 0,
        actors: payload.counts.actors ?? 0,
        agents: payload.counts.agents ?? 0,
        agentRecords: payload.counts.agentRecords,
        rawAgentRecords: payload.counts.rawAgentRecords,
        configuredAgents: payload.counts.configuredAgents,
        scoutManagedAgents: payload.counts.scoutManagedAgents,
        currentAgentRegistrations: payload.counts.currentAgentRegistrations,
        localAgentRegistrations: payload.counts.localAgentRegistrations,
        remoteAgentRegistrations: payload.counts.remoteAgentRegistrations,
        staleAgentRegistrations: payload.counts.staleAgentRegistrations,
        retiredAgentRegistrations: payload.counts.retiredAgentRegistrations,
        oneTimeAgentCards: payload.counts.oneTimeAgentCards,
        persistentAgentCards: payload.counts.persistentAgentCards,
        conversations: payload.counts.conversations ?? 0,
        messages: payload.counts.messages ?? 0,
        flights: payload.counts.flights ?? 0,
        collaborationRecords: payload.counts.collaborationRecords ?? 0
      } : undefined
    };
  } catch (error) {
    return {
      reachable: false,
      ok: false,
      checkedAt,
      socketPath: config.brokerSocketPath,
      error: error instanceof Error ? error.message : String(error)
    };
  } finally {
    clearTimeout(timeout);
  }
}
async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
  ensureServiceDirectories(config);
  const launchctl = inspectLaunchctl(config);
  const health = await fetchHealthSnapshot(config);
  const installed = existsSync3(config.launchAgentPath);
  const lastLogLine = health.reachable ? readLastLogLine([config.stdoutLogPath, config.stderrLogPath]) : readLastLogLine([config.stderrLogPath, config.stdoutLogPath]);
  return {
    label: config.label,
    mode: config.mode,
    launchAgentPath: config.launchAgentPath,
    bootoutCommand: bootoutCommand(config),
    brokerUrl: config.brokerUrl,
    brokerSocketPath: config.brokerSocketPath,
    supportDirectory: config.supportDirectory,
    runtimeDirectory: config.runtimeDirectory,
    controlHome: config.controlHome,
    stdoutLogPath: config.stdoutLogPath,
    stderrLogPath: config.stderrLogPath,
    installed,
    loaded: launchctl.loaded,
    pid: launchctl.pid,
    launchdState: launchctl.launchdState,
    lastExitStatus: launchctl.lastExitStatus,
    usesLaunchAgent: installed || launchctl.loaded,
    reachable: health.reachable,
    health,
    lastLogLine
  };
}
async function installBrokerService(config = resolveBrokerServiceConfig()) {
  bootoutLegacyBrokerService(config);
  writeLaunchAgentPlist(config);
  return brokerServiceStatus(config);
}
async function startBrokerService(config = resolveBrokerServiceConfig()) {
  bootoutLegacyBrokerService(config);
  writeLaunchAgentPlist(config);
  runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
  await waitForBrokerServiceStopped(config);
  runCommand(launchctlPath(), ["bootstrap", config.domainTarget, config.launchAgentPath], { allowFailure: true });
  runCommand(launchctlPath(), ["kickstart", "-k", config.serviceTarget], { allowFailure: true });
  const attempts = Math.ceil(resolveBrokerStartTimeoutMs() / BROKER_SERVICE_POLL_INTERVAL_MS);
  for (let attempt = 0;attempt < attempts; attempt += 1) {
    const status2 = await brokerServiceStatus(config);
    if (status2.health.reachable) {
      return status2;
    }
    await sleep(BROKER_SERVICE_POLL_INTERVAL_MS);
  }
  const status = await brokerServiceStatus(config);
  throw new Error(status.lastLogLine ?? status.health.error ?? "Broker service did not become healthy.");
}
function sleep(ms) {
  return new Promise((resolve3) => setTimeout(resolve3, ms));
}
async function waitForBrokerServiceStopped(config) {
  let status = await brokerServiceStatus(config);
  const attempts = Math.ceil(resolveBrokerStopTimeoutMs() / BROKER_SERVICE_POLL_INTERVAL_MS);
  for (let attempt = 0;attempt < attempts; attempt += 1) {
    status = await brokerServiceStatus(config);
    if (!status.loaded && !status.health.reachable) {
      return status;
    }
    await sleep(BROKER_SERVICE_POLL_INTERVAL_MS);
  }
  return status;
}
async function stopBrokerService(config = resolveBrokerServiceConfig()) {
  runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
  return waitForBrokerServiceStopped(config);
}
async function restartBrokerService(config = resolveBrokerServiceConfig()) {
  await stopBrokerService(config);
  return startBrokerService(config);
}
async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
  await stopBrokerService(config);
  if (existsSync3(config.launchAgentPath)) {
    rmSync2(config.launchAgentPath, { force: true });
  }
  const legacyPath = legacyLaunchAgentPath(config);
  bootoutLegacyBrokerService(config);
  if (existsSync3(legacyPath)) {
    rmSync2(legacyPath, { force: true });
  }
  return brokerServiceStatus(config);
}
async function main() {
  const command = process.argv[2] ?? "status";
  const json = process.argv.includes("--json");
  const config = resolveBrokerServiceConfig();
  let status;
  switch (command) {
    case "install":
      status = await installBrokerService(config);
      break;
    case "start":
      status = await startBrokerService(config);
      break;
    case "stop":
      status = await stopBrokerService(config);
      break;
    case "restart":
      status = await restartBrokerService(config);
      break;
    case "uninstall":
      status = await uninstallBrokerService(config);
      break;
    case "status":
      status = await brokerServiceStatus(config);
      break;
    default:
      console.error(`Unknown broker service command: ${command}`);
      process.exit(1);
  }
  if (json) {
    console.log(JSON.stringify(status, null, 2));
    return;
  }
  console.log(formatBrokerServiceStatus(status));
}
function formatBrokerServiceStatus(status) {
  const lines = [
    `label: ${status.label}`,
    `mode: ${status.mode}`,
    `launch agent: ${status.installed ? status.launchAgentPath : "not installed"}`,
    `bootout: ${status.bootoutCommand}`,
    `loaded: ${status.loaded ? "yes" : "no"}`,
    `pid: ${status.pid ?? "\u2014"}`,
    `launchd state: ${status.launchdState ?? "\u2014"}`,
    `broker url: ${status.brokerUrl}`,
    `broker socket: ${status.brokerSocketPath}`,
    `reachable: ${status.reachable ? "yes" : "no"}`,
    `health: ${status.health.ok ? "ok" : status.health.error ?? "unreachable"}`,
    `health transport: ${status.health.transport ?? "unknown"}`,
    `logs: ${status.stdoutLogPath}`
  ];
  if (status.health.socketFallbackError) {
    lines.push(`socket fallback: ${status.health.socketFallbackError}`);
  }
  if (status.lastLogLine) {
    lines.push(`last log: ${status.lastLogLine}`);
  }
  return lines.join(`
`);
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1] && !process.argv[1].endsWith("/main.mjs")) {
  main().catch((error) => {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
  });
}

// packages/runtime/src/local-config.ts
import {
  existsSync as existsSync4,
  mkdirSync as mkdirSync3,
  readFileSync as readFileSync2,
  renameSync,
  writeFileSync as writeFileSync3
} from "fs";
import { homedir as homedir4, hostname as osHostname } from "os";
import { dirname as dirname3, join as join4 } from "path";
var LOCAL_CONFIG_VERSION = 1;
var DEFAULT_SCOUT_WEB_PORTAL_HOST = "scout.local";
var DEFAULT_LOCAL_CONFIG = {
  version: LOCAL_CONFIG_VERSION,
  host: "127.0.0.1",
  webLocalName: undefined,
  ports: {
    broker: 65535,
    web: 3200,
    pairing: 7888
  }
};
function normalizeLocalHostnameLabel(value) {
  const firstLabel = value?.trim().replace(/\.local\.?$/i, "").split(".").find((part) => part.trim().length > 0)?.trim();
  const normalized = firstLabel?.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
  return normalized || "localhost";
}
function normalizeLocalHostname(value) {
  const trimmed = value?.trim().replace(/\.$/, "").toLowerCase();
  const labels = trimmed?.split(".").map((label) => label.trim().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-")).filter(Boolean);
  return labels && labels.length > 0 ? labels.join(".") : "localhost";
}
function resolveScoutWebNamedHostname(name) {
  const normalized = normalizeLocalHostname(name);
  return normalized.includes(".") ? normalized : `${normalized}.${DEFAULT_SCOUT_WEB_PORTAL_HOST}`;
}
function resolveConfiguredScoutWebHostname(config = loadLocalConfig(), machineHostname = osHostname()) {
  if (config.webLocalName) {
    return resolveScoutWebNamedHostname(config.webLocalName);
  }
  return `${normalizeLocalHostnameLabel(machineHostname)}.${DEFAULT_SCOUT_WEB_PORTAL_HOST}`;
}
function localConfigHome() {
  return process.env.OPENSCOUT_HOME ?? join4(homedir4(), ".openscout");
}
function localConfigPath() {
  return join4(localConfigHome(), "config.json");
}
function loadLocalConfig() {
  const configPath = localConfigPath();
  if (!existsSync4(configPath))
    return { version: LOCAL_CONFIG_VERSION };
  try {
    return validateLocalConfig(JSON.parse(readFileSync2(configPath, "utf8")));
  } catch {
    return { version: LOCAL_CONFIG_VERSION };
  }
}
function validateLocalConfig(input) {
  if (!input || typeof input !== "object")
    return { version: LOCAL_CONFIG_VERSION };
  const raw = input;
  const out = { version: LOCAL_CONFIG_VERSION };
  if (typeof raw.host === "string" && raw.host.trim().length > 0) {
    out.host = raw.host.trim();
  }
  if (typeof raw.webLocalName === "string" && raw.webLocalName.trim().length > 0) {
    out.webLocalName = raw.webLocalName.trim();
  }
  if (raw.ports && typeof raw.ports === "object") {
    const ports = raw.ports;
    const p = {};
    if (isValidPort(ports.broker))
      p.broker = ports.broker;
    if (isValidPort(ports.web))
      p.web = ports.web;
    if (isValidPort(ports.pairing))
      p.pairing = ports.pairing;
    if (Object.keys(p).length > 0)
      out.ports = p;
  }
  return out;
}
function isValidPort(value) {
  return typeof value === "number" && Number.isInteger(value) && value > 0 && value < 65536;
}
function resolveBrokerPort() {
  return loadLocalConfig().ports?.broker ?? DEFAULT_LOCAL_CONFIG.ports.broker;
}
function resolveWebPort() {
  return loadLocalConfig().ports?.web ?? DEFAULT_LOCAL_CONFIG.ports.web;
}

// packages/runtime/src/local-edge.ts
function uniqRoutes(routes) {
  const seen = new Set;
  const out = [];
  for (const route of routes) {
    const key = `${route.host.toLowerCase()} ${route.upstream}`;
    if (seen.has(key)) {
      continue;
    }
    seen.add(key);
    out.push(route);
  }
  return out;
}
function resolveOpenScoutLocalEdgeConfig(input = {}) {
  const portalHost = resolveScoutWebNamedHostname(input.portalHost ?? DEFAULT_SCOUT_WEB_PORTAL_HOST);
  const nodeHost = resolveScoutWebNamedHostname(input.nodeHost ?? resolveConfiguredScoutWebHostname());
  const wildcardHost = `*.${portalHost}`;
  const scheme = input.scheme ?? "http";
  const upstream = `127.0.0.1:${input.webPort ?? resolveWebPort()}`;
  const brokerUpstream = `127.0.0.1:${input.brokerPort ?? resolveBrokerPort()}`;
  return {
    portalHost,
    nodeHost,
    wildcardHost,
    scheme,
    brokerUpstream,
    routes: uniqRoutes([
      { host: portalHost, upstream },
      { host: wildcardHost, upstream }
    ])
  };
}
function renderOpenScoutStartPage(config) {
  const pageConfig = JSON.stringify({
    startPath: "/__openscout/web/start"
  });
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Start Scout</title>
  <style>
    :root {
      color-scheme: light dark;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      background: #0f1720;
      color: #f5f7fb;
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      min-height: 100vh;
      display: grid;
      place-items: center;
      padding: 32px;
      background:
        radial-gradient(ellipse at 15% 30%, rgba(91, 141, 239, 0.16) 0%, transparent 50%),
        radial-gradient(ellipse at 85% 70%, rgba(74, 222, 128, 0.06) 0%, transparent 40%),
        #111827;
    }
    main {
      width: min(400px, 100%);
      padding: 28px 30px 26px;
      border: 1px solid rgba(255, 255, 255, 0.09);
      border-radius: 10px;
      background: rgba(15, 23, 32, 0.9);
      box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 20px 60px rgba(0, 0, 0, 0.45);
    }
    .badge {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      margin-bottom: 18px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.07em;
      text-transform: uppercase;
      color: rgba(74, 222, 128, 0.8);
    }
    @keyframes blink {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.2; }
    }
    .badge-dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: #4ade80;
      flex-shrink: 0;
      animation: blink 2.4s ease-in-out infinite;
    }
    h1 {
      font-size: 22px;
      font-weight: 700;
      line-height: 1.2;
      margin-bottom: 8px;
      color: #f5f7fb;
    }
    p {
      font-size: 14px;
      color: rgba(245, 247, 251, 0.5);
      line-height: 1.55;
      margin-bottom: 22px;
    }
    button {
      width: 100%;
      min-height: 44px;
      border: 0;
      border-radius: 7px;
      background: #f4d35e;
      color: #17202a;
      font: inherit;
      font-size: 14px;
      font-weight: 700;
      cursor: pointer;
      transition: background 0.1s, opacity 0.1s;
    }
    button:hover:not(:disabled) { background: #f7dc74; }
    button:active:not(:disabled) { background: #e8c44a; }
    button:disabled { cursor: progress; opacity: 0.6; }
    .progress {
      height: 2px;
      margin-top: 14px;
      border-radius: 2px;
      background: rgba(255, 255, 255, 0.06);
      overflow: hidden;
    }
    @keyframes sweep {
      0%   { transform: translateX(-100%); }
      100% { transform: translateX(500%); }
    }
    .progress-bar {
      height: 100%;
      width: 20%;
      border-radius: 2px;
      background: rgba(244, 211, 94, 0.7);
      transform: translateX(-100%);
    }
    .progress-bar.running {
      animation: sweep 1.5s ease-in-out infinite;
    }
    output {
      display: block;
      min-height: 16px;
      margin-top: 10px;
      color: rgba(245, 247, 251, 0.38);
      font-family: ui-monospace, "SF Mono", Menlo, "Cascadia Code", monospace;
      font-size: 12px;
      line-height: 1.4;
    }
  </style>
</head>
<body>
  <main>
    <div class="badge"><span class="badge-dot"></span>Broker online</div>
    <h1>Start Scout</h1>
    <p>The web app is not running yet. Click to start it on this machine.</p>
    <button id="start" type="button">Start Scout</button>
    <div class="progress"><div class="progress-bar" id="bar"></div></div>
    <output id="status" role="status"></output>
  </main>
  <script>
    const config = ${pageConfig};
    const button = document.getElementById('start');
    const bar = document.getElementById('bar');
    const status = document.getElementById('status');
    const targetPath = window.location.pathname + window.location.search + window.location.hash;
    const healthUrl = new URL('/api/health', window.location.origin);
    const startUrl = new URL(config.startPath, window.location.origin);

    function setStatus(message) {
      status.textContent = message;
    }

    function setWaiting(on) {
      bar.classList.toggle('running', on);
    }

    async function waitForWeb() {
      const deadline = Date.now() + 20000;
      while (Date.now() < deadline) {
        try {
          const response = await fetch(healthUrl, { headers: { accept: 'application/json' }, cache: 'no-store' });
          if (response.ok) {
            const body = await response.json();
            if (body && body.ok === true) {
              window.location.replace(targetPath || '/');
              return true;
            }
          }
        } catch {}
        await new Promise((resolve) => setTimeout(resolve, 400));
      }
      return false;
    }

    button.addEventListener('click', async () => {
      button.disabled = true;
      setWaiting(true);
      setStatus('Starting Scout web...');
      try {
        const response = await fetch(startUrl, {
          method: 'POST',
          headers: { accept: 'application/json' },
        });
        const contentType = response.headers.get('content-type') || '';
        if (!contentType.includes('application/json')) {
          throw new Error('Scout broker is not reachable yet.');
        }
        const body = await response.json().catch(() => ({}));
        if (!response.ok || body.error) {
          throw new Error(body.error || 'Scout web did not start.');
        }
        setStatus('Waiting for the web app...');
        const ready = await waitForWeb();
        if (!ready) {
          setWaiting(false);
          setStatus('Scout web did not become ready. Try again in a moment.');
          button.disabled = false;
        }
      } catch (error) {
        setWaiting(false);
        setStatus(error instanceof Error ? error.message : String(error));
        button.disabled = false;
      }
    });
  </script>
</body>
</html>`;
}
function renderOpenScoutCaddyfile(config) {
  const schemes = config.scheme === "both" ? ["http", "https"] : [config.scheme];
  const startPage = renderOpenScoutStartPage(config);
  const blocks = schemes.flatMap((scheme) => config.routes.map((route) => {
    const host = scheme === "http" ? `http://${route.host}` : route.host;
    return `${host} {
` + (scheme === "https" ? `  tls internal
` : "") + `  handle /__openscout/web/start {
` + `    rewrite * /v1/web/start
` + `    reverse_proxy ${config.brokerUpstream}
` + `  }
` + `  handle /__openscout/web/status {
` + `    rewrite * /v1/web/status
` + `    reverse_proxy ${config.brokerUpstream}
` + `  }
` + `  handle {
` + `    reverse_proxy ${route.upstream} {
` + `      lb_try_duration 1s
` + `      lb_try_interval 250ms
` + `    }
` + `  }
` + `  handle_errors {
` + `    header Content-Type "text/html; charset=utf-8"
` + `    respond <<HTML
` + `${startPage}
` + `HTML 200
` + `  }
` + `}`;
  })).join(`

`);
  return `${blocks}
`;
}

// packages/runtime/src/base-daemon.ts
var RESTART_MIN_DELAY_MS = 1000;
var RESTART_MAX_DELAY_MS = 30000;
var BROKER_HEALTH_TIMEOUT_MS = 30000;
var BROKER_HEALTH_POLL_MS = 250;
var CHILD_SHUTDOWN_TIMEOUT_MS = 12000;
var MENU_BUNDLE_ID = "com.openscout.menu";
var MENU_PROCESS_NAME = "OpenScoutMenu";
var PROCESS_NAME = "scout-base";
var BROKER_LAUNCHER_PROCESS_NAME = "scout-broker-run";
var EDGE_PROCESS_NAME = "scout-edge";
var MDNS_PROCESS_NAME = "scout-mdns";
process.title = PROCESS_NAME;
var shuttingDown = false;
var brokerProcess = null;
var caddyProcess = null;
var mdnsProcesses = [];
var brokerRestartDelayMs = RESTART_MIN_DELAY_MS;
var edgeRestartDelayMs = RESTART_MIN_DELAY_MS;
var supervisedWebPid = null;
var supervisorKeepAlive = null;
var config = resolveBrokerServiceConfig();
function log(message, details) {
  if (details === undefined) {
    console.log(`[openscout-base] ${message}`);
    return;
  }
  console.log(`[openscout-base] ${message}`, details);
}
function warn(message, details) {
  if (details === undefined) {
    console.warn(`[openscout-base] ${message}`);
    return;
  }
  console.warn(`[openscout-base] ${message}`, details);
}
function ensureDirectory(path) {
  mkdirSync4(path, { recursive: true });
}
function logFile(name) {
  const dir = join5(config.supportDirectory, "logs", "base");
  ensureDirectory(dir);
  return openSync(join5(dir, name), "a");
}
function runtimeEntrypoint(config2) {
  return join5(config2.runtimePackageDir, "bin", "openscout-runtime.mjs");
}
function spawnBroker() {
  if (shuttingDown || brokerProcess) {
    return;
  }
  const stdout = logFile("broker.stdout.log");
  const stderr = logFile("broker.stderr.log");
  brokerProcess = spawn(config.bunExecutable, [
    "run",
    runtimeEntrypoint(config),
    "broker"
  ], {
    argv0: BROKER_LAUNCHER_PROCESS_NAME,
    cwd: config.runtimePackageDir,
    env: {
      ...process.env,
      OPENSCOUT_PARENT_PID: String(process.pid),
      OPENSCOUT_BROKER_HOST: config.brokerHost,
      OPENSCOUT_BROKER_PORT: String(config.brokerPort),
      OPENSCOUT_BROKER_URL: config.brokerUrl,
      OPENSCOUT_BROKER_SOCKET_PATH: config.brokerSocketPath,
      OPENSCOUT_CONTROL_HOME: config.controlHome,
      OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope
    },
    stdio: ["ignore", stdout, stderr]
  });
  log("broker started", { pid: brokerProcess.pid, url: config.brokerUrl });
  brokerProcess.once("exit", (code, signal) => {
    log("broker exited", { code, signal });
    brokerProcess = null;
    supervisedWebPid = null;
    if (!shuttingDown) {
      scheduleBrokerRestart();
    }
  });
}
function scheduleBrokerRestart() {
  const delay = brokerRestartDelayMs;
  brokerRestartDelayMs = Math.min(brokerRestartDelayMs * 2, RESTART_MAX_DELAY_MS);
  setTimeout(() => {
    if (!shuttingDown) {
      spawnBroker();
      startWebWhenBrokerIsReady();
    }
  }, delay).unref();
}
async function waitForBrokerHealth(timeoutMs = BROKER_HEALTH_TIMEOUT_MS) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    try {
      const response = await fetch(new URL("/health", config.brokerUrl), {
        headers: { accept: "application/json" },
        signal: AbortSignal.timeout(1000)
      });
      if (response.ok) {
        const body = await response.json();
        if (body.ok === true) {
          brokerRestartDelayMs = RESTART_MIN_DELAY_MS;
          return true;
        }
      }
    } catch {}
    await sleep2(BROKER_HEALTH_POLL_MS);
  }
  return false;
}
function resolveEdgeScheme() {
  const value = process.env.OPENSCOUT_WEB_EDGE_SCHEME?.trim().toLowerCase();
  if (value === "http" || value === "https" || value === "both") {
    return value;
  }
  return "http";
}
function forwardedProtoForEdgeScheme(scheme) {
  return scheme === "http" ? "http" : "https";
}
function resolveEdgeConfig() {
  const portalHost = process.env.OPENSCOUT_WEB_PORTAL_HOST?.trim() || DEFAULT_SCOUT_WEB_PORTAL_HOST;
  const nodeHost = process.env.OPENSCOUT_WEB_ADVERTISED_HOST?.trim() || (process.env.OPENSCOUT_WEB_LOCAL_NAME?.trim() ? resolveScoutWebNamedHostname(process.env.OPENSCOUT_WEB_LOCAL_NAME) : resolveConfiguredScoutWebHostname());
  return resolveOpenScoutLocalEdgeConfig({
    portalHost,
    nodeHost,
    scheme: resolveEdgeScheme(),
    brokerPort: config.brokerPort,
    webPort: Number.parseInt(process.env.OPENSCOUT_WEB_PORT ?? "", 10) || resolveWebPort()
  });
}
function resolveLocalEdgeCaddyfilePath() {
  const dir = join5(homedir5(), ".scout", "local-edge");
  ensureDirectory(dir);
  return join5(dir, "Caddyfile");
}
function resolveCaddyExecutable() {
  return process.env.OPENSCOUT_CADDY_BIN?.trim() || "caddy";
}
function spawnMdnsProxy(input) {
  return spawn("/usr/bin/dns-sd", [
    "-P",
    input.name,
    input.scheme === "https" ? "_https._tcp" : "_http._tcp",
    "local",
    String(input.port),
    input.host,
    "127.0.0.1",
    "path=/"
  ], {
    argv0: MDNS_PROCESS_NAME,
    stdio: ["ignore", logFile("mdns.stdout.log"), logFile("mdns.stderr.log")]
  });
}
function stopEdgeProcesses() {
  for (const processRef of mdnsProcesses) {
    if (!processRef.killed) {
      processRef.kill("SIGTERM");
    }
  }
  mdnsProcesses = [];
  if (caddyProcess && !caddyProcess.killed) {
    caddyProcess.kill("SIGTERM");
  }
  caddyProcess = null;
}
function startLocalEdge() {
  if (process.env.OPENSCOUT_BASE_EDGE_ENABLED === "0" || process.platform !== "darwin") {
    return;
  }
  if (shuttingDown || caddyProcess) {
    return;
  }
  const edgeConfig = resolveEdgeConfig();
  const schemes = edgeConfig.scheme === "both" ? ["http", "https"] : [edgeConfig.scheme];
  const caddyfilePath = resolveLocalEdgeCaddyfilePath();
  writeFileSync4(caddyfilePath, renderOpenScoutCaddyfile(edgeConfig), "utf8");
  mdnsProcesses = schemes.flatMap((scheme) => {
    const edgePort = scheme === "https" ? 443 : 80;
    const suffix = scheme.toUpperCase();
    return [
      spawnMdnsProxy({
        name: `Scout Local ${suffix}`,
        host: edgeConfig.portalHost,
        port: edgePort,
        scheme
      }),
      spawnMdnsProxy({
        name: `Scout ${edgeConfig.nodeHost} ${suffix}`,
        host: edgeConfig.nodeHost,
        port: edgePort,
        scheme
      })
    ];
  });
  caddyProcess = spawn(resolveCaddyExecutable(), [
    "run",
    "--config",
    caddyfilePath,
    "--adapter",
    "caddyfile"
  ], {
    argv0: EDGE_PROCESS_NAME,
    env: process.env,
    stdio: ["ignore", logFile("edge.stdout.log"), logFile("edge.stderr.log")]
  });
  log("local edge started", {
    pid: caddyProcess.pid,
    portal: edgeConfig.portalHost,
    node: edgeConfig.nodeHost,
    caddyfile: caddyfilePath
  });
  caddyProcess.once("error", (error) => {
    stopEdgeProcesses();
    warn("local edge failed to start", error.message);
    scheduleEdgeRestart();
  });
  caddyProcess.once("exit", (code, signal) => {
    log("local edge exited", { code, signal });
    stopEdgeProcesses();
    if (!shuttingDown) {
      scheduleEdgeRestart();
    }
  });
}
function scheduleEdgeRestart() {
  const delay = edgeRestartDelayMs;
  edgeRestartDelayMs = Math.min(edgeRestartDelayMs * 2, RESTART_MAX_DELAY_MS);
  setTimeout(() => {
    if (!shuttingDown) {
      startLocalEdge();
    }
  }, delay).unref();
}
async function startWebWhenBrokerIsReady() {
  if (process.env.OPENSCOUT_BASE_START_WEB === "0") {
    return;
  }
  if (!await waitForBrokerHealth()) {
    warn("broker did not become healthy before web startup timeout");
    return;
  }
  try {
    const edgeConfig = resolveEdgeConfig();
    const scheme = forwardedProtoForEdgeScheme(edgeConfig.scheme);
    const response = await fetch(new URL("/v1/web/start", config.brokerUrl), {
      method: "POST",
      headers: {
        accept: "application/json",
        "x-forwarded-host": edgeConfig.portalHost,
        "x-forwarded-proto": scheme
      },
      signal: AbortSignal.timeout(15000)
    });
    const body = await response.json();
    supervisedWebPid = typeof body.pid === "number" ? body.pid : resolvePortListenerPid(Number.parseInt(process.env.OPENSCOUT_WEB_PORT ?? "", 10) || resolveWebPort());
    if (!response.ok || body.ok !== true) {
      warn("web server did not report healthy", body.error ?? response.statusText);
      return;
    }
    log("web server ready", { pid: supervisedWebPid });
  } catch (error) {
    warn("web server startup failed", error instanceof Error ? error.message : String(error));
  }
}
function resolvePortListenerPid(port) {
  const result = spawnSync2("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], {
    encoding: "utf8"
  });
  if ((result.status ?? 1) !== 0) {
    return null;
  }
  const pid = Number.parseInt(result.stdout.trim().split(/\s+/)[0] ?? "", 10);
  return Number.isFinite(pid) && pid > 0 ? pid : null;
}
function findRepoMenuBundle() {
  let current = dirname4(fileURLToPath2(import.meta.url));
  while (true) {
    const candidate = resolve3(current, "apps", "macos", "dist", "OpenScoutMenu.app");
    if (existsSync5(candidate)) {
      return candidate;
    }
    const parent = dirname4(current);
    if (parent === current) {
      return null;
    }
    current = parent;
  }
}
function startMenuBarApp() {
  if (process.env.OPENSCOUT_BASE_MENU_ENABLED === "0" || process.platform !== "darwin") {
    return;
  }
  const explicitBundle = process.env.OPENSCOUT_MENU_BUNDLE_PATH?.trim();
  const repoBundle = explicitBundle && existsSync5(explicitBundle) ? explicitBundle : findRepoMenuBundle();
  const args = repoBundle ? [repoBundle] : ["-b", MENU_BUNDLE_ID];
  const child = spawn("open", args, {
    stdio: ["ignore", logFile("menu.stdout.log"), logFile("menu.stderr.log")]
  });
  child.once("exit", (code) => {
    if (code === 0) {
      log("menu bar app launch requested", { target: repoBundle ?? MENU_BUNDLE_ID });
      return;
    }
    warn("menu bar app launch failed", { target: repoBundle ?? MENU_BUNDLE_ID, code });
  });
}
function stopMenuBarApp() {
  if (process.platform !== "darwin" || process.env.OPENSCOUT_BASE_MENU_ENABLED === "0") {
    return;
  }
  spawn("pkill", ["-x", MENU_PROCESS_NAME], { stdio: "ignore" }).unref();
}
function isChildExited(child) {
  return child.exitCode !== null || child.signalCode !== null;
}
function waitForChildExit(child, timeoutMs) {
  if (isChildExited(child)) {
    return Promise.resolve(true);
  }
  return new Promise((resolve4) => {
    const timeout = setTimeout(() => {
      child.off("exit", onExit);
      resolve4(false);
    }, timeoutMs);
    timeout.unref();
    const onExit = () => {
      clearTimeout(timeout);
      resolve4(true);
    };
    child.once("exit", onExit);
  });
}
async function terminateChildProcess(child, label, timeoutMs = CHILD_SHUTDOWN_TIMEOUT_MS) {
  if (!child || isChildExited(child)) {
    return;
  }
  if (!child.killed) {
    child.kill("SIGTERM");
  }
  if (await waitForChildExit(child, timeoutMs)) {
    return;
  }
  warn(`${label} did not exit after SIGTERM; forcing shutdown`, { pid: child.pid });
  try {
    child.kill("SIGKILL");
  } catch {
    return;
  }
  await waitForChildExit(child, 2000);
}
function stopSupervisedWeb() {
  if (supervisedWebPid && supervisedWebPid > 0) {
    try {
      process.kill(supervisedWebPid, "SIGTERM");
    } catch {}
  }
  supervisedWebPid = null;
}
async function shutdown(exitCode = 0) {
  if (shuttingDown) {
    return;
  }
  shuttingDown = true;
  if (supervisorKeepAlive) {
    clearInterval(supervisorKeepAlive);
    supervisorKeepAlive = null;
  }
  stopSupervisedWeb();
  stopMenuBarApp();
  const activeCaddyProcess = caddyProcess;
  stopEdgeProcesses();
  await terminateChildProcess(brokerProcess, "broker");
  await terminateChildProcess(activeCaddyProcess, "local edge", 2000);
  process.exit(exitCode);
}
function sleep2(ms) {
  return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
}
function keepSupervisorAlive() {
  if (supervisorKeepAlive) {
    return;
  }
  supervisorKeepAlive = setInterval(() => {
    return;
  }, 60000);
}
for (const signal of ["SIGINT", "SIGTERM"]) {
  process.on(signal, () => {
    shutdown(0).catch((error) => {
      warn("shutdown failed", error instanceof Error ? error.message : String(error));
      process.exit(1);
    });
  });
}
log("starting Scout base service", {
  label: config.label,
  brokerUrl: config.brokerUrl,
  bootout: `launchctl bootout ${config.serviceTarget}`
});
keepSupervisorAlive();
spawnBroker();
startLocalEdge();
startMenuBarApp();
startWebWhenBrokerIsReady();
