// Force strict mode and setup for ESM
"use strict";
import {
  MissingCliEntryError
} from "./chunk-ZQ4ICJPB.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/process-registry.ts
init_esbuild_shims();
var TERM_GRACE_MS = 5e3;
var EXIT_DEADLINE_MS = 1e4;
var ProcessRegistry = class {
  static {
    __name(this, "ProcessRegistry");
  }
  reservations = /* @__PURE__ */ new Set();
  children = /* @__PURE__ */ new Set();
  draining = false;
  shutdownPromise;
  reserve() {
    if (this.draining) {
      throw new Error("ACP process registry is draining");
    }
    const token = Symbol("acp-child");
    this.reservations.add(token);
    let settled = false;
    return {
      attach: /* @__PURE__ */ __name((child) => {
        if (settled || !this.reservations.delete(token)) {
          throw new Error("ACP process reservation is no longer active");
        }
        settled = true;
        const tracked = new TrackedChild(child, () => {
          this.children.delete(tracked);
        });
        this.children.add(tracked);
        if (this.draining) void tracked.terminate().catch(() => {
        });
        return tracked;
      }, "attach"),
      cancel: /* @__PURE__ */ __name(() => {
        if (settled) return;
        settled = true;
        this.reservations.delete(token);
      }, "cancel")
    };
  }
  shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    this.draining = true;
    this.shutdownPromise = Promise.allSettled(
      [...this.children].map((child) => child.terminate())
    ).then((results) => {
      const failures = results.flatMap(
        (result) => result.status === "rejected" ? [result.reason] : []
      );
      if (failures.length > 0) {
        throw new AggregateError(failures, "ACP child process shutdown failed");
      }
    });
    return this.shutdownPromise;
  }
  killAllSync() {
    this.draining = true;
    for (const child of this.children) child.killSync();
  }
  get activeProcessCount() {
    return this.children.size;
  }
};
var TrackedChild = class {
  constructor(child, onExit) {
    this.child = child;
    this.onExit = onExit;
    this.exited = new Promise((resolve) => {
      const finish = /* @__PURE__ */ __name((info) => {
        if (this.exitedSettled) return;
        this.exitedSettled = true;
        this.onExit();
        resolve(info);
      }, "finish");
      child.once("exit", (exitCode, signalCode) => {
        finish({ exitCode, signalCode });
      });
      child.once("spawn", () => {
        this.spawnConfirmed = true;
      });
      child.once("error", () => {
        if (!this.spawnConfirmed) finish(void 0);
      });
    });
  }
  static {
    __name(this, "TrackedChild");
  }
  exited;
  exitedSettled = false;
  spawnConfirmed = false;
  terminatePromise;
  terminate() {
    this.terminatePromise ??= this.terminateOnce();
    return this.terminatePromise;
  }
  killSync() {
    if (this.exitedSettled) return;
    try {
      this.child.kill("SIGKILL");
    } catch {
    }
  }
  async terminateOnce() {
    if (this.exitedSettled) return;
    try {
      this.child.kill("SIGTERM");
    } catch {
      if (this.exitedSettled) return;
    }
    let hardKillTimer;
    let deadlineTimer;
    const deadline = new Promise((_, reject) => {
      hardKillTimer = setTimeout(() => this.killSync(), TERM_GRACE_MS);
      hardKillTimer.unref();
      deadlineTimer = setTimeout(() => {
        reject(
          new Error(
            `ACP child pid=${this.child.pid ?? "unknown"} did not exit within ${EXIT_DEADLINE_MS}ms`
          )
        );
      }, EXIT_DEADLINE_MS);
      deadlineTimer.unref();
    });
    try {
      const exitInfo = await Promise.race([this.exited, deadline]);
      if (exitInfo && (exitInfo.exitCode !== 0 || exitInfo.signalCode !== null)) {
        throw new Error(
          `ACP child pid=${this.child.pid ?? "unknown"} exited uncleanly during shutdown (code=${exitInfo.exitCode ?? "none"}, signal=${exitInfo.signalCode ?? "none"})`
        );
      }
    } finally {
      if (hardKillTimer) clearTimeout(hardKillTimer);
      if (deadlineTimer) clearTimeout(deadlineTimer);
    }
  }
};

// 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 = {}) {
  const processRegistry = options.processRegistry ?? new ProcessRegistry();
  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 reservation = processRegistry.reserve();
    let child;
    try {
      child = spawn(
        process.execPath,
        [
          ...execArgs,
          ...memoryArgs,
          cliEntry,
          "--acp",
          ...options.extraArgs ?? []
        ],
        {
          cwd: workspaceCwd,
          stdio: ["pipe", "pipe", "pipe"],
          env: childEnv
        }
      );
    } catch (error) {
      reservation.cancel();
      throw error;
    }
    const trackedChild = reservation.attach(child);
    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", () => {
      });
    }
    if (!child.stdin || !child.stdout) {
      trackedChild.killSync();
      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(() => trackedChild.terminate(), "kill"),
      killSync: /* @__PURE__ */ __name(() => trackedChild.killSync(), "killSync"),
      exited: trackedChild.exited
    };
  };
}
__name(createSpawnChannelFactory, "createSpawnChannelFactory");
var defaultSpawnChannelFactory = createSpawnChannelFactory();
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");

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
 */
