#!/usr/bin/env node
import {
  ensureService,
  swapServeSocket,
  tryConnect,
  waitForMessage
} from "./chunk-55PSMIVK.js";
import "./chunk-3HJM2NFD.js";
import {
  extractOscSignals
} from "./chunk-B3GHP25U.js";
import "./chunk-IHUQNJF6.js";
import {
  createIpcReader,
  encodeBinaryIpcFrame,
  serializeIpc
} from "./chunk-IHIFDLM4.js";
import {
  terminalLogger
} from "./chunk-LWDOXOWY.js";
import {
  SOCK_PATH,
  STOPPED_PATH
} from "./chunk-7RSBFN2G.js";

// src/terminal-worker.ts
import * as pty from "node-pty";
import pkg from "@xterm/headless";
import { SerializeAddon } from "@xterm/addon-serialize";
import { UnicodeGraphemesAddon } from "@xterm/addon-unicode-graphemes";
import { setTimeout as sleep } from "timers/promises";
import { existsSync } from "fs";
var { Terminal: HeadlessTerminal } = pkg;
var DEFAULT_COLS = 80;
var DEFAULT_ROWS = 24;
var RECONNECT_INITIAL_DELAY_MS = 1e3;
var RECONNECT_MAX_DELAY_MS = 5e3;
function normalizeTerminalWorkerEnv(env) {
  const normalized = {};
  for (const [key, value] of Object.entries(env)) {
    if (value !== void 0) normalized[key] = value;
  }
  delete normalized.NO_COLOR;
  if (normalized.CLICOLOR === "0") delete normalized.CLICOLOR;
  normalized.TERM = "xterm-256color";
  normalized.COLORTERM = "truecolor";
  normalized.CLICOLOR = "1";
  return normalized;
}
var ShellTerminalWorker = class {
  constructor(sessionId2, cwd2, name2) {
    this.sessionId = sessionId2;
    this.cwd = cwd2;
    this.name = name2;
    this.terminal.loadAddon(this.serializeAddon);
    this.terminal.loadAddon(new UnicodeGraphemesAddon());
  }
  sessionId;
  cwd;
  name;
  socket = null;
  child = null;
  terminal = new HeadlessTerminal({
    cols: DEFAULT_COLS,
    rows: DEFAULT_ROWS,
    scrollback: 5e3,
    allowProposedApi: true
  });
  serializeAddon = new SerializeAddon();
  outputSeq = 0;
  exiting = false;
  reconnecting = false;
  async run() {
    this.socket = await ensureService();
    this.setupSocketHandlers(this.socket);
    await this.registerWithServe();
    this.startPty();
    process.on("SIGTERM", () => this.shutdown(143));
    process.on("SIGINT", () => this.shutdown(130));
  }
  async registerWithServe() {
    if (!this.socket?.writable) throw new Error("Serve socket is not writable");
    const responsePromise = waitForMessage(this.socket, "session_create_response");
    this.socket.write(
      serializeIpc({
        type: "session_create_request",
        mode: "pty",
        provider: "claude",
        cwd: this.cwd,
        pid: process.pid,
        sessionId: this.sessionId,
        name: this.name,
        kind: "terminal"
      })
    );
    const response = await responsePromise;
    if (response.error) throw new Error(`Failed to register terminal worker: ${response.error}`);
    this.socket.write(
      serializeIpc({ type: "pty_register", sessionId: this.sessionId, pid: process.pid })
    );
    terminalLogger.info({ sessionId: this.sessionId }, "Terminal worker registered with serve");
  }
  startPty() {
    if (this.child) return;
    const shell = process.env.SHELL ?? "/bin/sh";
    const child = pty.spawn(shell, [], {
      name: "xterm-256color",
      cols: this.terminal.cols,
      rows: this.terminal.rows,
      cwd: this.cwd,
      env: normalizeTerminalWorkerEnv(process.env)
    });
    this.child = child;
    child.onData((data) => this.handlePtyData(data));
    child.onExit(({ exitCode, signal }) => {
      const code = signal ? 128 + signal : exitCode;
      terminalLogger.info({ sessionId: this.sessionId, code }, "Terminal worker PTY exited");
      this.exit(code);
    });
    terminalLogger.info(
      { sessionId: this.sessionId, pid: child.pid, shell, cwd: this.cwd },
      "Terminal worker PTY started"
    );
  }
  handlePtyData(data) {
    this.outputSeq += 1;
    this.terminal.write(data);
    const signal = extractOscSignals(data);
    if (signal?.title && this.socket?.writable) {
      this.socket.write(
        serializeIpc({ type: "pty_title_change", sessionId: this.sessionId, title: signal.title })
      );
    }
    if (this.socket?.writable) {
      this.socket.write(
        encodeBinaryIpcFrame(this.sessionId, Buffer.from(data, "utf-8"), this.outputSeq)
      );
    }
  }
  setupSocketHandlers(socket) {
    createIpcReader(
      socket,
      (msg) => this.handleServeMessage(msg),
      void 0,
      (err, line) => {
        terminalLogger.warn({ err: err.message, lineLen: line.length }, "Terminal worker IPC message dropped");
      }
    );
    socket.on("close", () => {
      if (this.exiting) return;
      terminalLogger.info({ sessionId: this.sessionId }, "Serve socket closed; terminal worker will reconnect");
      void this.reconnectToServe();
    });
    socket.on("error", (err) => {
      terminalLogger.warn({ sessionId: this.sessionId, err: err.message }, "Terminal worker socket error");
    });
  }
  handleServeMessage(msg) {
    if ("sessionId" in msg && msg.sessionId !== this.sessionId) return;
    switch (msg.type) {
      case "pty_input":
        this.child?.write(msg.data);
        break;
      case "pty_subscribe":
        if (this.socket?.writable) {
          this.socket.write(
            serializeIpc({
              type: "pty_snapshot",
              sessionId: this.sessionId,
              cols: this.terminal.cols,
              rows: this.terminal.rows,
              data: this.serializeAddon.serialize(),
              outputSeq: this.outputSeq,
              requestId: msg.requestId
            })
          );
        }
        break;
      case "pty_resize_request":
        this.resize(msg.cols, msg.rows);
        break;
      case "pty_terminate":
        this.shutdown(0);
        break;
      case "pty_detach":
        this.socket?.end();
        break;
    }
  }
  resize(cols, rows) {
    this.child?.resize(cols, rows);
    this.terminal.resize(cols, rows);
    if (this.socket?.writable) {
      this.socket.write(serializeIpc({ type: "pty_resize", sessionId: this.sessionId, cols, rows }));
    }
    terminalLogger.info({ sessionId: this.sessionId, cols, rows }, "Terminal worker PTY resized");
  }
  async reconnectToServe() {
    if (this.reconnecting) return;
    this.reconnecting = true;
    try {
      for (let i = 0; !this.exiting; i += 1) {
        await sleep(Math.min(RECONNECT_INITIAL_DELAY_MS * (i + 1), RECONNECT_MAX_DELAY_MS));
        const stopped = existsSync(STOPPED_PATH);
        const newSocket = stopped ? await tryConnect(SOCK_PATH) : await ensureService();
        if (!newSocket) continue;
        this.socket = this.socket ? swapServeSocket(this.socket, newSocket) : newSocket;
        this.setupSocketHandlers(this.socket);
        await this.registerWithServe();
        terminalLogger.info({ sessionId: this.sessionId }, "Terminal worker reconnected to serve");
        this.reconnecting = false;
        return;
      }
    } catch (err) {
      terminalLogger.warn(
        { sessionId: this.sessionId, err: err instanceof Error ? err.message : String(err) },
        "Terminal worker reconnect failed"
      );
      this.reconnecting = false;
      void this.reconnectToServe();
      return;
    }
    this.reconnecting = false;
  }
  shutdown(code) {
    if (this.exiting) return;
    this.exiting = true;
    try {
      this.child?.kill();
    } catch {
    }
    this.exit(code);
  }
  exit(code) {
    if (this.exiting && !this.child) return;
    this.exiting = true;
    this.child = null;
    this.terminal.dispose();
    if (this.socket?.writable) {
      const socket = this.socket;
      const timer = setTimeout(() => process.exit(code), 500);
      socket.end(serializeIpc({ type: "pty_deregister", sessionId: this.sessionId }), () => {
        clearTimeout(timer);
        process.exit(code);
      });
      return;
    }
    process.exit(code);
  }
};
var [sessionId, cwd, name] = process.argv.slice(2);
if (!sessionId || !cwd || !name) {
  console.error("Usage: terminal-worker <sessionId> <cwd> <name>");
  process.exit(1);
}
new ShellTerminalWorker(sessionId, cwd, name).run().catch((err) => {
  terminalLogger.error({ err: err instanceof Error ? err.message : String(err) }, "Terminal worker failed");
  process.exit(1);
});
//# sourceMappingURL=terminal-worker.js.map