// Force strict mode and setup for ESM
"use strict";
import {
  CHANNEL_DAEMON_WORKER_SENTINEL,
  QWEN_DAEMON_TOKEN_ENV,
  QWEN_DAEMON_URL_ENV,
  QWEN_DAEMON_WORKSPACE_ENV,
  QWEN_SERVER_TOKEN_ENV
} from "./chunk-EDAGQADF.js";
import {
  channelSelectionNames
} from "./chunk-MX2YXRER.js";
import {
  sanitizeLogText
} from "./chunk-HI55Z6PR.js";
import "./chunk-UFC57OYT.js";
import "./chunk-K5PGHDBN.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/cli/src/serve/channel-worker-supervisor.ts
init_esbuild_shims();
import { fork } from "node:child_process";
import { randomUUID } from "node:crypto";
var DEFAULT_CHANNEL_WORKER_STARTUP_TIMEOUT_MS = 3e4;
function selectionChannelArgs(selection) {
  return channelSelectionNames(selection).flatMap((name) => [
    "--channel",
    name
  ]);
}
__name(selectionChannelArgs, "selectionChannelArgs");
function defaultSpawnWorker(execPath, argv, options) {
  const child = fork(argv[0], argv.slice(1), {
    execPath,
    cwd: options.cwd,
    env: options.env,
    stdio: options.stdio
  });
  return child;
}
__name(defaultSpawnWorker, "defaultSpawnWorker");
function isReadyMessage(message) {
  return typeof message === "object" && message !== null && message.type === "ready";
}
__name(isReadyMessage, "isReadyMessage");
function requestedChannelNames(selection) {
  return selection.mode === "names" ? [...selection.names] : void 0;
}
__name(requestedChannelNames, "requestedChannelNames");
function sanitizeWorkerError(error) {
  return Array.from(sanitizeLogText(error, 512)).slice(0, 512).join("");
}
__name(sanitizeWorkerError, "sanitizeWorkerError");
function notifyExit(onExit, snapshot) {
  try {
    onExit?.(snapshot);
  } catch {
  }
}
__name(notifyExit, "notifyExit");
function waitForExit(child, timeoutMs) {
  return new Promise((resolve) => {
    let settled = false;
    const done = /* @__PURE__ */ __name((exited) => {
      if (settled) return;
      settled = true;
      if (timer) clearTimeout(timer);
      resolve(exited);
    }, "done");
    const timer = setTimeout(() => done(false), timeoutMs);
    timer.unref();
    child.once("exit", () => done(true));
  });
}
__name(waitForExit, "waitForExit");
function hasObservedExit(snapshot) {
  return snapshot.exitCode !== void 0 || snapshot.signal !== void 0;
}
__name(hasObservedExit, "hasObservedExit");
function createWorkerEnv(opts) {
  const env = { ...process.env };
  env["QWEN_CODE_NO_RELAUNCH"] = "true";
  env[CHANNEL_DAEMON_WORKER_SENTINEL] = randomUUID();
  env[QWEN_DAEMON_URL_ENV] = opts.daemonUrl;
  env[QWEN_DAEMON_WORKSPACE_ENV] = opts.workspace;
  delete env[QWEN_SERVER_TOKEN_ENV];
  delete env[QWEN_DAEMON_TOKEN_ENV];
  if (opts.daemonToken) {
    env[QWEN_DAEMON_TOKEN_ENV] = opts.daemonToken;
  }
  return env;
}
__name(createWorkerEnv, "createWorkerEnv");
function createChannelWorkerSupervisor(opts) {
  const spawnWorker = opts.spawnWorker ?? defaultSpawnWorker;
  let child;
  let snapshot = {
    enabled: true,
    state: "disabled",
    channels: channelSelectionNames(opts.selection)
  };
  let ready = false;
  let stopping = false;
  let exitNotified = false;
  const snapshotCopy = /* @__PURE__ */ __name(() => ({
    ...snapshot,
    channels: [...snapshot.channels],
    ...snapshot.requestedChannels ? { requestedChannels: [...snapshot.requestedChannels] } : {}
  }), "snapshotCopy");
  const setExited = /* @__PURE__ */ __name((state, code, signal, error) => {
    const next = {
      ...snapshot,
      state,
      exitCode: code,
      signal
    };
    if (error) {
      next.error = error;
    } else {
      delete next.error;
    }
    snapshot = {
      ...next
    };
  }, "setExited");
  return {
    async start() {
      if (child) return;
      ready = false;
      stopping = false;
      exitNotified = false;
      const argv = [
        opts.cliEntryPath,
        "channel",
        "daemon-worker",
        ...selectionChannelArgs(opts.selection)
      ];
      const env = createWorkerEnv({
        daemonUrl: opts.daemonUrl,
        workspace: opts.workspace,
        ...opts.daemonToken ? { daemonToken: opts.daemonToken } : {}
      });
      const requestedChannels = requestedChannelNames(opts.selection);
      snapshot = {
        enabled: true,
        state: "starting",
        channels: channelSelectionNames(opts.selection),
        ...requestedChannels ? { requestedChannels } : {},
        startedAt: (/* @__PURE__ */ new Date()).toISOString()
      };
      child = spawnWorker(process.execPath, argv, {
        cwd: opts.workspace,
        env,
        stdio: ["ignore", "inherit", "inherit", "ipc"]
      });
      if (child.pid !== void 0) {
        snapshot = { ...snapshot, pid: child.pid };
      }
      const startedChild = child;
      await new Promise((resolve, reject) => {
        let settled = false;
        let exitObserved = false;
        let startupTimer;
        function cleanupReadyWait() {
          if (startupTimer) {
            clearTimeout(startupTimer);
            startupTimer = void 0;
          }
          startedChild.removeListener("message", settleReady);
        }
        __name(cleanupReadyWait, "cleanupReadyWait");
        function failBeforeReady(err) {
          if (settled) return;
          settled = true;
          cleanupReadyWait();
          reject(err);
        }
        __name(failBeforeReady, "failBeforeReady");
        function settleReady(message) {
          if (settled || !isReadyMessage(message)) return;
          if (child !== startedChild) return;
          settled = true;
          ready = true;
          cleanupReadyWait();
          const next = {
            ...snapshot,
            state: "running",
            pid: message.pid ?? startedChild.pid,
            channels: message.channels && message.channels.length > 0 ? [...message.channels] : [...snapshot.channels]
          };
          if (message.requestedChannels?.length) {
            next.requestedChannels = [...message.requestedChannels];
          }
          snapshot = next;
          resolve();
        }
        __name(settleReady, "settleReady");
        function settleExit(code, signal) {
          if (child !== startedChild) return;
          exitObserved = true;
          const state = ready ? "exited" : "failed";
          const message = `Channel worker exited before ready (code=${code ?? "null"}, signal=${signal ?? "null"}).`;
          setExited(
            state,
            code,
            signal,
            snapshot.error ?? (ready ? void 0 : sanitizeWorkerError(message))
          );
          if (ready && !stopping && !exitNotified) {
            exitNotified = true;
            notifyExit(opts.onExit, snapshotCopy());
          }
          if (!settled) {
            failBeforeReady(new Error(snapshot.error ?? message));
          }
          child = void 0;
        }
        __name(settleExit, "settleExit");
        function settleError(err) {
          if (child !== startedChild || exitObserved) return;
          if (settled && ready) return;
          snapshot = {
            ...snapshot,
            state: "failed",
            error: sanitizeWorkerError(err.message)
          };
          if (!settled) {
            failBeforeReady(new Error(snapshot.error));
          }
        }
        __name(settleError, "settleError");
        startupTimer = setTimeout(() => {
          const timeoutMs = opts.startupTimeoutMs ?? DEFAULT_CHANNEL_WORKER_STARTUP_TIMEOUT_MS;
          const error = `Channel worker did not become ready within ${timeoutMs}ms.`;
          snapshot = {
            ...snapshot,
            state: "failed",
            error: sanitizeWorkerError(error)
          };
          failBeforeReady(new Error(error));
          if (child === startedChild) {
            child.kill("SIGTERM");
          }
        }, opts.startupTimeoutMs ?? DEFAULT_CHANNEL_WORKER_STARTUP_TIMEOUT_MS);
        startupTimer.unref();
        startedChild.on("message", settleReady);
        startedChild.once("exit", settleExit);
        startedChild.once("error", settleError);
      });
    },
    async stop() {
      if (!child || snapshot.state === "exited" || snapshot.state === "failed" && hasObservedExit(snapshot) || snapshot.state === "stopped") {
        child = void 0;
        snapshot = { ...snapshot, state: "stopped" };
        return;
      }
      const exited = waitForExit(child, 5e3);
      stopping = true;
      child.kill("SIGTERM");
      if (!await exited) {
        const killed = waitForExit(child, 2e3);
        child.kill("SIGKILL");
        if (!await killed) {
          child = void 0;
          stopping = false;
          snapshot = {
            ...snapshot,
            state: "failed",
            signal: "SIGKILL",
            error: "Channel worker did not exit after SIGKILL."
          };
          return;
        }
      }
      child = void 0;
      stopping = false;
      snapshot = { ...snapshot, state: "stopped" };
    },
    killAllSync() {
      if (!child || snapshot.state === "exited" || snapshot.state === "failed" && hasObservedExit(snapshot) || snapshot.state === "stopped") {
        return;
      }
      const preserveFailure = snapshot.state === "failed" && !hasObservedExit(snapshot);
      stopping = true;
      child.kill("SIGKILL");
      child = void 0;
      if (!preserveFailure) {
        snapshot = {
          ...snapshot,
          state: "stopped",
          signal: "SIGKILL"
        };
      }
    },
    snapshot() {
      return snapshotCopy();
    }
  };
}
__name(createChannelWorkerSupervisor, "createChannelWorkerSupervisor");
export {
  createChannelWorkerSupervisor
};
