var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined") return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});

// src/buffer.ts
var SessionRingBuffer = class {
  maxBytes;
  chunks = [];
  nextSeq = 1;
  totalBytes = 0;
  constructor(options = {}) {
    this.maxBytes = options.maxBytes ?? 512 * 1024;
  }
  append(data) {
    const normalized = typeof data === "string" ? data : String(data ?? "");
    const bytes = Buffer.byteLength(normalized, "utf8");
    const seq = this.nextSeq++;
    this.chunks.push({ seq, data: normalized, bytes });
    this.totalBytes += bytes;
    this.trim();
    return seq;
  }
  snapshot(sinceSeq) {
    const relevant = typeof sinceSeq === "number" ? this.chunks.filter((chunk) => chunk.seq > sinceSeq) : this.chunks;
    const text = relevant.map((chunk) => chunk.data).join("");
    const truncated = !!this.chunks[0] && typeof sinceSeq === "number" && sinceSeq < this.chunks[0].seq - 1;
    return {
      seq: this.nextSeq - 1,
      text,
      truncated
    };
  }
  getState() {
    return {
      scrollbackBytes: this.totalBytes,
      snapshotSeq: this.nextSeq - 1
    };
  }
  clear() {
    this.chunks = [];
    this.totalBytes = 0;
    this.nextSeq = 1;
  }
  restore(snapshot) {
    this.clear();
    const text = String(snapshot.text || "");
    if (!text) {
      this.nextSeq = Math.max(1, Number(snapshot.seq || 0) + 1);
      return;
    }
    const bytes = Buffer.byteLength(text, "utf8");
    const seq = Math.max(1, Number(snapshot.seq || 1));
    this.chunks = [{ seq, data: text, bytes }];
    this.totalBytes = bytes;
    this.nextSeq = seq + 1;
    this.trim();
  }
  trim() {
    while (this.totalBytes > this.maxBytes && this.chunks.length > 1) {
      const removed = this.chunks.shift();
      if (!removed) break;
      this.totalBytes -= removed.bytes;
    }
  }
};

// src/registry.ts
import { randomUUID } from "crypto";

// src/runtime-labels.ts
import * as path from "path";
function normalizeSlug(input) {
  return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
}
function normalizeValue(input) {
  return input.trim().toLowerCase();
}
function getWorkspaceLabel(workspace) {
  const trimmed = workspace.trim();
  if (!trimmed) return "workspace";
  const normalized = trimmed.replace(/[\\/]+$/, "");
  const base = path.basename(normalized);
  return base || normalized;
}
function buildRuntimeDisplayName(payload) {
  const explicit = payload.displayName?.trim();
  if (explicit) return explicit;
  const workspaceLabel = getWorkspaceLabel(payload.workspace);
  const providerLabel = payload.providerType.trim() || "runtime";
  return `${providerLabel} @ ${workspaceLabel}`;
}
function buildRuntimeKey(payload, existingKeys) {
  const requested = payload.runtimeKey?.trim();
  const existing = new Set(Array.from(existingKeys, (key) => key.toLowerCase()));
  const displayName = buildRuntimeDisplayName(payload);
  const baseKey = normalizeSlug(requested || displayName || getWorkspaceLabel(payload.workspace) || payload.providerType || "runtime") || "runtime";
  if (!existing.has(baseKey)) return baseKey;
  let suffix = 2;
  let candidate = `${baseKey}-${suffix}`;
  while (existing.has(candidate)) {
    suffix += 1;
    candidate = `${baseKey}-${suffix}`;
  }
  return candidate;
}
var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
function isSessionHostLiveRuntime(record) {
  if (!record) return false;
  if (record.surfaceKind === "live_runtime") return true;
  if (record.surfaceKind === "recovery_snapshot" || record.surfaceKind === "inactive_record") return false;
  const lifecycle = String(record.lifecycle || "").trim();
  return LIVE_LIFECYCLES.has(lifecycle);
}
function getSessionHostRecoveryLabel(meta) {
  const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
  if (!recoveryState) return null;
  if (recoveryState === "auto_resumed") return "restored after restart";
  if (recoveryState === "resume_failed") return "restore failed";
  if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
  if (recoveryState === "orphan_snapshot") return "snapshot recovered";
  return recoveryState.replace(/_/g, " ");
}
function isSessionHostRecoverySnapshot(record) {
  if (!record) return false;
  if (record.surfaceKind === "recovery_snapshot") return true;
  if (record.surfaceKind === "live_runtime" || record.surfaceKind === "inactive_record") return false;
  if (isSessionHostLiveRuntime(record)) return false;
  const lifecycle = String(record.lifecycle || "").trim();
  if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
    return false;
  }
  const meta = record.meta || void 0;
  if (meta?.restoredFromStorage === true) return true;
  return getSessionHostRecoveryLabel(meta) !== null;
}
function getSessionHostSurfaceKind(record) {
  if (record?.surfaceKind === "live_runtime" || record?.surfaceKind === "recovery_snapshot" || record?.surfaceKind === "inactive_record") {
    return record.surfaceKind;
  }
  if (isSessionHostLiveRuntime(record)) return "live_runtime";
  if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
  return "inactive_record";
}
function resolveAttachableRuntimeRecord(records, identifier) {
  const record = resolveRuntimeRecord(records, identifier);
  const surfaceKind = getSessionHostSurfaceKind(record);
  if (surfaceKind === "live_runtime") {
    return record;
  }
  if (surfaceKind === "recovery_snapshot") {
    throw new Error(`Runtime ${record.runtimeKey} is a recovery snapshot, not a live attach target. Resume or recover it first.`);
  }
  throw new Error(`Runtime ${record.runtimeKey} is ${record.lifecycle}, not a live attach target.`);
}
function uniqueMatch(records, predicate) {
  const matches = records.filter(predicate);
  if (matches.length === 1) return matches[0] || null;
  if (matches.length === 0) return null;
  const labels = matches.map((record) => `${record.runtimeKey} (${record.sessionId})`).join(", ");
  throw new Error(`Ambiguous runtime target. Matches: ${labels}`);
}
function resolveRuntimeRecord(records, identifier) {
  const target = identifier.trim();
  if (!target) {
    throw new Error("Runtime target is required");
  }
  const exact = uniqueMatch(
    records,
    (record) => record.sessionId === target || normalizeValue(record.runtimeKey) === normalizeValue(target) || normalizeValue(record.displayName) === normalizeValue(target)
  );
  if (exact) return exact;
  const prefix = uniqueMatch(
    records,
    (record) => record.sessionId.startsWith(target) || normalizeValue(record.runtimeKey).startsWith(normalizeValue(target))
  );
  if (prefix) return prefix;
  throw new Error(`Unknown runtime target: ${target}`);
}
function formatRuntimeOwner(record) {
  if (!record.writeOwner) return "none";
  return `${record.writeOwner.ownerType}:${record.writeOwner.clientId}`;
}

// src/registry.ts
var SessionHostRegistry = class {
  sessions = /* @__PURE__ */ new Map();
  createSession(payload) {
    const sessionId = payload.sessionId || randomUUID();
    if (this.sessions.has(sessionId)) {
      throw new Error(`Session already exists: ${sessionId}`);
    }
    const now = Date.now();
    const initialClient = payload.clientId ? [{
      clientId: payload.clientId,
      type: payload.clientType || "daemon",
      readOnly: false,
      attachedAt: now,
      lastSeenAt: now
    }] : [];
    const record = {
      sessionId,
      runtimeKey: buildRuntimeKey(
        payload,
        Array.from(this.sessions.values(), (state) => state.record.runtimeKey)
      ),
      displayName: buildRuntimeDisplayName(payload),
      workspaceLabel: getWorkspaceLabel(payload.workspace),
      transport: "pty",
      providerType: payload.providerType,
      category: payload.category,
      workspace: payload.workspace,
      launchCommand: payload.launchCommand,
      createdAt: now,
      lastActivityAt: now,
      lifecycle: "starting",
      writeOwner: null,
      attachedClients: initialClient,
      buffer: {
        scrollbackBytes: 0,
        snapshotSeq: 0
      },
      meta: payload.meta || {}
    };
    record.meta = {
      sessionHostCols: payload.cols || 80,
      sessionHostRows: payload.rows || 24,
      ...record.meta
    };
    this.sessions.set(sessionId, {
      record,
      buffer: new SessionRingBuffer()
    });
    return this.cloneRecord(record);
  }
  restoreSession(record, snapshot) {
    const cloned = this.cloneRecord(record);
    this.sessions.set(cloned.sessionId, {
      record: cloned,
      buffer: (() => {
        const buffer = new SessionRingBuffer();
        if (snapshot) buffer.restore(snapshot);
        return buffer;
      })()
    });
    return this.cloneRecord(cloned);
  }
  listSessions() {
    return Array.from(this.sessions.values()).map((state) => this.cloneRecord(state.record)).sort((a, b) => b.lastActivityAt - a.lastActivityAt);
  }
  getSession(sessionId) {
    const state = this.sessions.get(sessionId);
    return state ? this.cloneRecord(state.record) : null;
  }
  attachClient(payload) {
    const state = this.requireSession(payload.sessionId);
    const now = Date.now();
    let removedDaemonOwner = false;
    if (payload.clientType === "daemon") {
      const staleDaemonClientIds = state.record.attachedClients.filter((client) => client.type === "daemon" && client.clientId !== payload.clientId).map((client) => client.clientId);
      if (staleDaemonClientIds.length > 0) {
        state.record.attachedClients = state.record.attachedClients.filter(
          (client) => !(client.type === "daemon" && client.clientId !== payload.clientId)
        );
        if (state.record.writeOwner && staleDaemonClientIds.includes(state.record.writeOwner.clientId)) {
          removedDaemonOwner = true;
        }
      }
    }
    const existing = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
    if (existing) {
      existing.type = payload.clientType;
      existing.readOnly = !!payload.readOnly;
      existing.lastSeenAt = now;
    } else {
      state.record.attachedClients.push({
        clientId: payload.clientId,
        type: payload.clientType,
        readOnly: !!payload.readOnly,
        attachedAt: now,
        lastSeenAt: now
      });
    }
    if (removedDaemonOwner) {
      state.record.writeOwner = null;
    }
    state.record.lastActivityAt = now;
    return this.cloneRecord(state.record);
  }
  detachClient(payload) {
    const state = this.requireSession(payload.sessionId);
    state.record.attachedClients = state.record.attachedClients.filter((client) => client.clientId !== payload.clientId);
    if (state.record.writeOwner?.clientId === payload.clientId) {
      state.record.writeOwner = null;
    }
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  acquireWrite(payload) {
    const state = this.requireSession(payload.sessionId);
    if (state.record.writeOwner && state.record.writeOwner.clientId !== payload.clientId && !payload.force) {
      throw new Error(`Write owned by ${state.record.writeOwner.clientId}`);
    }
    const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
    if (attachedClient) {
      attachedClient.readOnly = false;
      attachedClient.lastSeenAt = Date.now();
    }
    state.record.writeOwner = {
      clientId: payload.clientId,
      ownerType: payload.ownerType,
      acquiredAt: Date.now()
    };
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  releaseWrite(payload) {
    const state = this.requireSession(payload.sessionId);
    const attachedClient = state.record.attachedClients.find((client) => client.clientId === payload.clientId);
    if (attachedClient) {
      attachedClient.readOnly = false;
      attachedClient.lastSeenAt = Date.now();
    }
    if (state.record.writeOwner?.clientId === payload.clientId) {
      state.record.writeOwner = null;
    }
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  appendOutput(sessionId, data) {
    const state = this.requireSession(sessionId);
    const seq = state.buffer.append(data);
    state.record.buffer = state.buffer.getState();
    state.record.lastActivityAt = Date.now();
    return { record: this.cloneRecord(state.record), seq };
  }
  getSnapshot(sessionId, sinceSeq) {
    const state = this.requireSession(sessionId);
    state.record.buffer = state.buffer.getState();
    return state.buffer.snapshot(sinceSeq);
  }
  clearBuffer(sessionId) {
    const state = this.requireSession(sessionId);
    state.buffer.clear();
    state.record.buffer = state.buffer.getState();
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  updateSessionMeta(sessionId, meta, replace = false) {
    const state = this.requireSession(sessionId);
    state.record.meta = replace ? { ...meta } : {
      ...state.record.meta || {},
      ...meta
    };
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  markStarted(sessionId, pid) {
    const state = this.requireSession(sessionId);
    state.record.lifecycle = "running";
    state.record.startedAt = state.record.startedAt || Date.now();
    if (typeof pid === "number") state.record.osPid = pid;
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  markStopped(sessionId, lifecycle = "stopped") {
    const state = this.requireSession(sessionId);
    state.record.lifecycle = lifecycle;
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  setLifecycle(sessionId, lifecycle) {
    const state = this.requireSession(sessionId);
    state.record.lifecycle = lifecycle;
    state.record.lastActivityAt = Date.now();
    return this.cloneRecord(state.record);
  }
  deleteSession(sessionId) {
    return this.sessions.delete(sessionId);
  }
  requireSession(sessionId) {
    const state = this.sessions.get(sessionId);
    if (!state) throw new Error(`Unknown session: ${sessionId}`);
    return state;
  }
  cloneRecord(record) {
    return {
      ...record,
      launchCommand: {
        ...record.launchCommand,
        args: [...record.launchCommand.args],
        env: record.launchCommand.env ? { ...record.launchCommand.env } : void 0
      },
      writeOwner: record.writeOwner ? { ...record.writeOwner } : null,
      attachedClients: record.attachedClients.map((client) => ({ ...client })),
      buffer: { ...record.buffer },
      meta: { ...record.meta }
    };
  }
};

// src/ipc.ts
import * as os from "os";
import * as path2 from "path";
import * as net from "net";
import { randomUUID as randomUUID2 } from "crypto";
function getDefaultSessionHostEndpoint(appName = "adhdev") {
  if (process.platform === "win32") {
    return {
      kind: "pipe",
      path: `\\\\.\\pipe\\${appName}-session-host`
    };
  }
  return {
    kind: "unix",
    path: path2.join(os.tmpdir(), `${appName}-session-host.sock`)
  };
}
function serializeEnvelope(envelope) {
  return `${JSON.stringify(envelope)}
`;
}
function createLineParser(onEnvelope) {
  let buffer = "";
  return (chunk) => {
    buffer += chunk.toString();
    let newlineIndex = buffer.indexOf("\n");
    while (newlineIndex >= 0) {
      const rawLine = buffer.slice(0, newlineIndex).trim();
      buffer = buffer.slice(newlineIndex + 1);
      if (rawLine) {
        onEnvelope(JSON.parse(rawLine));
      }
      newlineIndex = buffer.indexOf("\n");
    }
  };
}
var SessionHostClient = class {
  endpoint;
  socket = null;
  requestWaiters = /* @__PURE__ */ new Map();
  eventListeners = /* @__PURE__ */ new Set();
  constructor(options = {}) {
    this.endpoint = options.endpoint || getDefaultSessionHostEndpoint(options.appName || "adhdev");
  }
  async connect() {
    if (this.socket && !this.socket.destroyed) return;
    if (this.socket) {
      try {
        this.socket.destroy();
      } catch {
      }
      this.socket = null;
    }
    const socket = net.createConnection(this.endpoint.path);
    this.socket = socket;
    socket.on("data", createLineParser((envelope) => {
      if (envelope.kind === "response") {
        const waiter = this.requestWaiters.get(envelope.requestId);
        if (waiter) {
          this.requestWaiters.delete(envelope.requestId);
          waiter.resolve(envelope.response);
        }
        return;
      }
      if (envelope.kind === "event") {
        for (const listener of this.eventListeners) listener(envelope.event);
      }
    }));
    socket.on("error", (error) => {
      for (const waiter of this.requestWaiters.values()) {
        waiter.reject(error);
      }
      this.requestWaiters.clear();
      if (this.socket === socket) {
        this.socket = null;
      }
      try {
        socket.destroy();
      } catch {
      }
    });
    await new Promise((resolve2, reject) => {
      socket.once("connect", () => resolve2());
      socket.once("error", reject);
    });
  }
  onEvent(listener) {
    this.eventListeners.add(listener);
    return () => {
      this.eventListeners.delete(listener);
    };
  }
  async request(request) {
    await this.connect();
    if (!this.socket) throw new Error("Session host socket unavailable");
    const requestId = randomUUID2();
    const envelope = {
      kind: "request",
      requestId,
      request
    };
    const response = await new Promise((resolve2, reject) => {
      const timeout = setTimeout(() => {
        this.requestWaiters.delete(requestId);
        reject(new Error(`Session host request timed out after 30s (${request.type})`));
      }, 3e4);
      this.requestWaiters.set(requestId, {
        resolve: (value) => {
          clearTimeout(timeout);
          resolve2(value);
        },
        reject: (error) => {
          clearTimeout(timeout);
          reject(error);
        }
      });
      this.socket?.write(serializeEnvelope(envelope));
    });
    return response;
  }
  async close() {
    if (!this.socket) return;
    const socket = this.socket;
    this.socket = null;
    for (const waiter of this.requestWaiters.values()) {
      waiter.reject(new Error("Session host client closed"));
    }
    this.requestWaiters.clear();
    await new Promise((resolve2) => {
      let settled = false;
      const done = () => {
        if (settled) return;
        settled = true;
        resolve2();
      };
      socket.once("close", done);
      socket.end();
      socket.destroy();
      setTimeout(done, 50);
    });
  }
};
function createResponseEnvelope(requestId, response) {
  return {
    kind: "response",
    requestId,
    response
  };
}
function writeEnvelope(socket, envelope) {
  socket.write(serializeEnvelope(envelope));
}

// src/spawn-env.ts
import * as os2 from "os";
import * as path3 from "path";
function sanitizeSpawnEnv(baseEnv, overrides) {
  const env = {};
  const source = { ...baseEnv, ...overrides || {} };
  for (const [key, value] of Object.entries(source)) {
    if (typeof value !== "string") continue;
    env[key] = value;
  }
  for (const key of Object.keys(env)) {
    if (key === "INIT_CWD" || key === "npm_command" || key === "npm_execpath" || key === "npm_node_execpath" || key.startsWith("npm_") || key.startsWith("npm_config_") || key.startsWith("npm_package_") || key.startsWith("npm_lifecycle_") || key.startsWith("PNPM_") || key.startsWith("YARN_") || key.startsWith("BUN_") || key.startsWith("VSCODE_") || key.startsWith("ELECTRON_")) {
      delete env[key];
    }
  }
  delete env.CODEX_THREAD_ID;
  delete env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE;
  applyTerminalColorEnv(env);
  return env;
}
function applyTerminalColorEnv(env) {
  if (env.NO_COLOR) return;
  if (!env.TERM || env.TERM === "xterm-color") {
    env.TERM = "xterm-256color";
  }
  if (!env.COLORTERM) env.COLORTERM = "truecolor";
  if (process.platform === "win32") {
    if (!env.FORCE_COLOR) env.FORCE_COLOR = "1";
    if (!env.CLICOLOR) env.CLICOLOR = "1";
  }
}
function ensureNodePtySpawnHelperPermissions(logFn) {
  if (os2.platform() === "win32") return;
  try {
    const fs = __require("fs");
    const ptyDir = path3.resolve(path3.dirname(__require.resolve("node-pty")), "..");
    const platformArch = `${os2.platform()}-${os2.arch()}`;
    const helper = path3.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
    if (fs.existsSync(helper)) {
      const stat = fs.statSync(helper);
      if (!(stat.mode & 73)) {
        fs.chmodSync(helper, stat.mode | 493);
        logFn?.(`Fixed spawn-helper permissions: ${helper}`);
      }
    }
  } catch {
  }
}
export {
  SessionHostClient,
  SessionHostRegistry,
  SessionRingBuffer,
  applyTerminalColorEnv,
  buildRuntimeDisplayName,
  buildRuntimeKey,
  createLineParser,
  createResponseEnvelope,
  ensureNodePtySpawnHelperPermissions,
  formatRuntimeOwner,
  getDefaultSessionHostEndpoint,
  getSessionHostRecoveryLabel,
  getSessionHostSurfaceKind,
  getWorkspaceLabel,
  isSessionHostLiveRuntime,
  isSessionHostRecoverySnapshot,
  resolveAttachableRuntimeRecord,
  resolveRuntimeRecord,
  sanitizeSpawnEnv,
  writeEnvelope
};
//# sourceMappingURL=index.mjs.map