// Force strict mode and setup for ESM
"use strict";
import {
  MissingCliEntryError
} from "./chunk-LX3DYYB3.js";
import {
  redactLogCredentials
} from "./chunk-M5GB774H.js";
import {
  init_esbuild_shims
} from "./chunk-5O2XNYP6.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";

// packages/acp-bridge/src/ndJsonStream.ts
init_esbuild_shims();
function ndJsonStream(output, input, hooks) {
  const textEncoder = new TextEncoder();
  const textDecoder = new TextDecoder();
  const readable = new ReadableStream({
    async start(controller) {
      const pending = [];
      const reader = input.getReader();
      try {
        while (true) {
          const { value, done } = await reader.read();
          if (done) break;
          if (!value) continue;
          readChunk(value, pending, controller, textDecoder, hooks);
        }
      } finally {
        reader.releaseLock();
        controller.close();
      }
    }
  });
  const writable = new WritableStream({
    async write(message) {
      const content = JSON.stringify(message);
      const payload = textEncoder.encode(content);
      const frame = new Uint8Array(payload.byteLength + 1);
      frame.set(payload);
      frame[payload.byteLength] = 10;
      const writer = output.getWriter();
      try {
        await writer.write(frame);
        callHook(hooks?.onMessageSent, payload.byteLength);
        callHook(hooks?.onMessageObserved, {
          direction: "sent",
          bytes: payload.byteLength,
          message
        });
      } finally {
        writer.releaseLock();
      }
    }
  });
  return { readable, writable };
}
__name(ndJsonStream, "ndJsonStream");
function readChunk(chunk, pending, controller, textDecoder, hooks) {
  let start = 0;
  let newline = chunk.indexOf(10, start);
  while (newline !== -1) {
    const lineBytes = takeLineBytes(pending, chunk.subarray(start, newline));
    handleLine(lineBytes, controller, textDecoder, hooks);
    start = newline + 1;
    newline = chunk.indexOf(10, start);
  }
  if (start < chunk.length) {
    pending.push(chunk.subarray(start));
  }
}
__name(readChunk, "readChunk");
function takeLineBytes(pending, current) {
  if (pending.length === 0) return current;
  const totalLength = pending.reduce((sum, part) => sum + part.byteLength, 0) + current.byteLength;
  const line = new Uint8Array(totalLength);
  let offset = 0;
  for (const part of pending) {
    line.set(part, offset);
    offset += part.byteLength;
  }
  line.set(current, offset);
  pending.length = 0;
  return line;
}
__name(takeLineBytes, "takeLineBytes");
function handleLine(lineBytes, controller, textDecoder, hooks) {
  const line = textDecoder.decode(lineBytes);
  const trimmedLine = line.trim();
  if (!trimmedLine) return;
  try {
    const message = JSON.parse(trimmedLine);
    controller.enqueue(message);
    const bytes = jsonPayloadByteLength(lineBytes);
    callHook(hooks?.onMessageReceived, bytes);
    callHook(hooks?.onMessageObserved, {
      direction: "received",
      bytes,
      message
    });
  } catch (err) {
    console.error("Failed to parse JSON message:", trimmedLine, err);
  }
}
__name(handleLine, "handleLine");
function jsonPayloadByteLength(lineBytes) {
  return lineBytes[lineBytes.byteLength - 1] === 13 ? lineBytes.byteLength - 1 : lineBytes.byteLength;
}
__name(jsonPayloadByteLength, "jsonPayloadByteLength");
function callHook(hook, value) {
  try {
    hook?.(value);
  } catch {
  }
}
__name(callHook, "callHook");

// packages/acp-bridge/src/spawnChannel.ts
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 sourceEnv = options.sourceEnv ?? process.env;
    const cliEntry = sourceEnv["QWEN_CLI_ENTRY"] || process.argv[1];
    if (!cliEntry) {
      throw new MissingCliEntryError();
    }
    const childEnv = scrubChildEnv(
      sourceEnv,
      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 {
  ndJsonStream,
  getAcpMemoryArgs,
  createStderrForwarder,
  createSpawnChannelFactory,
  defaultSpawnChannelFactory,
  scrubChildEnv
};
/**
 * @license
 * Copyright 2026 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * @license
 * Copyright 2025 Qwen Team
 * SPDX-License-Identifier: Apache-2.0
 */
