// Force strict mode and setup for ESM
"use strict";
import {
  ndJsonStream
} from "./chunk-3VJ7MT3N.js";
import {
  MissingCliEntryError
} from "./chunk-7MFZZRJS.js";
import {
  redactLogCredentials
} from "./chunk-HLKG6CZN.js";
import {
  init_esbuild_shims
} from "./chunk-A4BMJM77.js";
import {
  __name
} from "./chunk-J2S4EL5Y.js";

// packages/acp-bridge/src/spawnChannel.ts
init_esbuild_shims();
import { spawn } from "node:child_process";
import * as os from "node:os";
import { Readable, Writable } from "node:stream";
import { getHeapStatistics } from "node:v8";
var cachedMemoryArgs;
function getAcpMemoryArgs() {
  if (cachedMemoryArgs) return cachedMemoryArgs;
  const constrainedMemory = process.constrainedMemory;
  const constrained = typeof constrainedMemory === "function" ? constrainedMemory() : 0;
  const totalBytes = constrained && constrained > 0 ? constrained : os.totalmem();
  const totalMB = Math.floor(totalBytes / (1024 * 1024));
  const targetMB = Math.min(Math.floor(totalMB * 0.5), 16384);
  const currentLimitMB = Math.floor(
    getHeapStatistics().heap_size_limit / (1024 * 1024)
  );
  cachedMemoryArgs = [
    ...targetMB > currentLimitMB ? [`--max-old-space-size=${targetMB}`] : [],
    "--expose-gc"
  ];
  return cachedMemoryArgs;
}
__name(getAcpMemoryArgs, "getAcpMemoryArgs");
function createStderrForwarder(opts) {
  const { prefix, onDiagnosticLine } = opts;
  const STDERR_LINE_CAP_CHARS = 64 * 1024;
  let buf = "";
  const flush = /* @__PURE__ */ __name((line) => {
    if (line.length > 0) {
      const safe = redactLogCredentials(line);
      process.stderr.write(prefix + safe + "\n");
      if (onDiagnosticLine) onDiagnosticLine(prefix + safe, "warn");
    }
  }, "flush");
  return {
    onData(chunk) {
      buf += chunk;
      let nl = buf.indexOf("\n");
      while (nl !== -1) {
        flush(buf.slice(0, nl));
        buf = buf.slice(nl + 1);
        nl = buf.indexOf("\n");
      }
      while (buf.length > STDERR_LINE_CAP_CHARS) {
        const truncated = redactLogCredentials(buf.slice(0, STDERR_LINE_CAP_CHARS)) + " [truncated]";
        process.stderr.write(prefix + truncated + "\n");
        if (onDiagnosticLine) onDiagnosticLine(prefix + truncated, "warn");
        buf = buf.slice(STDERR_LINE_CAP_CHARS);
      }
    },
    onEnd() {
      if (buf.length > 0) flush(buf);
    }
  };
}
__name(createStderrForwarder, "createStderrForwarder");
function createSpawnChannelFactory(options = {}) {
  return async (workspaceCwd, childEnvOverrides) => {
    const cliEntry = process.env["QWEN_CLI_ENTRY"] || process.argv[1];
    if (!cliEntry) {
      throw new MissingCliEntryError();
    }
    const childEnv = scrubChildEnv(
      process.env,
      SCRUBBED_CHILD_ENV_KEYS,
      childEnvOverrides
    );
    childEnv["QWEN_CODE_NO_RELAUNCH"] = "true";
    const memoryArgs = getAcpMemoryArgs();
    const execArgs = process.execArgv.filter(
      (a) => !/^--inspect(-brk)?($|=)/.test(a)
    );
    const child = spawn(
      process.execPath,
      [
        ...execArgs,
        ...memoryArgs,
        cliEntry,
        "--acp",
        ...options.extraArgs ?? []
      ],
      {
        cwd: workspaceCwd,
        stdio: ["pipe", "pipe", "pipe"],
        env: childEnv
      }
    );
    if (child.stderr) {
      const prefix = `[serve pid=${child.pid} cwd=${workspaceCwd}] `;
      const forwarder = createStderrForwarder({
        prefix,
        onDiagnosticLine: options.onDiagnosticLine
      });
      child.stderr.setEncoding("utf8");
      child.stderr.on("data", forwarder.onData);
      child.stderr.on("end", forwarder.onEnd);
      child.stderr.on("error", () => {
      });
    }
    const exited = new Promise((resolve) => {
      let resolved = false;
      const finish = /* @__PURE__ */ __name((info) => {
        if (resolved) return;
        resolved = true;
        resolve(info);
      }, "finish");
      child.once(
        "exit",
        (code, signal) => finish({ exitCode: code, signalCode: signal })
      );
      child.once("error", () => finish(void 0));
    });
    if (!child.stdin || !child.stdout) {
      child.kill("SIGKILL");
      throw new Error(
        "Spawned ACP child has no stdin/stdout \u2014 cannot establish NDJSON channel."
      );
    }
    const writable = Writable.toWeb(child.stdin);
    const readable = Readable.toWeb(child.stdout);
    const stream = ndJsonStream(writable, readable, options.pipeHooks);
    return {
      stream,
      kill: /* @__PURE__ */ __name(() => killChild(child), "kill"),
      killSync: /* @__PURE__ */ __name(() => {
        if (child.exitCode === null && child.signalCode === null) {
          try {
            child.kill("SIGKILL");
          } catch {
          }
        }
      }, "killSync"),
      exited
    };
  };
}
__name(createSpawnChannelFactory, "createSpawnChannelFactory");
var defaultSpawnChannelFactory = createSpawnChannelFactory();
var KILL_HARD_DEADLINE_MS = 1e4;
var SCRUBBED_CHILD_ENV_KEYS = /* @__PURE__ */ new Set([
  "QWEN_SERVER_TOKEN",
  "QWEN_CODE_SIMPLE"
]);
function scrubChildEnv(source, scrubbed, overrides) {
  const childEnv = { ...source };
  for (const key of scrubbed) {
    delete childEnv[key];
  }
  if (overrides) {
    for (const [key, value] of Object.entries(overrides)) {
      if (scrubbed.has(key)) continue;
      if (value === void 0) {
        delete childEnv[key];
      } else {
        childEnv[key] = value;
      }
    }
  }
  return childEnv;
}
__name(scrubChildEnv, "scrubChildEnv");
function killChild(child) {
  return new Promise((resolve) => {
    if (child.exitCode !== null || child.signalCode !== null) {
      resolve();
      return;
    }
    let resolved = false;
    const finish = /* @__PURE__ */ __name(() => {
      if (resolved) return;
      resolved = true;
      child.removeListener("exit", finish);
      resolve();
    }, "finish");
    child.once("exit", finish);
    try {
      child.kill("SIGTERM");
    } catch {
      finish();
      return;
    }
    setTimeout(() => {
      if (!resolved && child.exitCode === null && child.signalCode === null) {
        try {
          child.kill("SIGKILL");
        } catch {
        }
      }
    }, 5e3).unref();
    setTimeout(() => {
      if (!resolved) {
        process.stderr.write(
          `qwen serve: killChild hard deadline (${KILL_HARD_DEADLINE_MS}ms) reached; child pid=${child.pid} still alive (uninterruptible sleep?) \u2014 abandoning. Operator should check for zombie qwen --acp processes holding workspace resources.
`
        );
        finish();
      }
    }, KILL_HARD_DEADLINE_MS).unref();
  });
}
__name(killChild, "killChild");

export {
  getAcpMemoryArgs,
  createStderrForwarder,
  createSpawnChannelFactory,
  defaultSpawnChannelFactory,
  scrubChildEnv
};
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
