import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);

// src/cli/ms-365-write-pretool.ts
import { readFileSync } from "node:fs";
import { createConnection } from "node:net";
import { homedir } from "node:os";
import { join } from "node:path";
import { randomBytes } from "node:crypto";
var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
var KERNEL_POLL_INTERVAL_MS = 2000;
var IPC_CONNECT_TIMEOUT_MS = 3000;
var IPC_REPLY_TIMEOUT_MS = 1e4;
var KERNEL_RPC_TIMEOUT_MS = 3000;
var GATEWAY_SOCKET = process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR !== undefined ? join(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join(homedir(), ".claude", "channels", "telegram", "gateway.sock"));
var KERNEL_SOCKET = process.env.SWITCHROOM_KERNEL_SOCKET ?? "/run/switchroom/kernel/sock";
var TOOL_PREFIX = "mcp__ms-365__";
var GATED_MS365_WRITE_TOOLS = new Set([
  "upload-file-content",
  "create-upload-session",
  "create-event",
  "update-event",
  "delete-event",
  "update-message",
  "delete-message"
]);
function isGatedMs365Tool(toolName) {
  if (!toolName.startsWith(TOOL_PREFIX))
    return false;
  return GATED_MS365_WRITE_TOOLS.has(toolName.slice(TOOL_PREFIX.length));
}
function readStdin() {
  try {
    return readFileSync(0, "utf8");
  } catch {
    return "";
  }
}
function parseHookInput() {
  const raw = readStdin();
  if (!raw)
    return null;
  try {
    const obj = JSON.parse(raw);
    return obj && typeof obj === "object" ? obj : null;
  } catch {
    return null;
  }
}
function extractMs365Preview(toolName, toolInput) {
  const o = toolInput && typeof toolInput === "object" ? toolInput : {};
  let itemId = "(new)";
  for (const k of ["itemId", "item_id", "id", "driveItemId", "eventId", "messageId", "message_id"]) {
    if (typeof o[k] === "string" && o[k].length > 0) {
      itemId = o[k];
      break;
    }
  }
  let itemDisplayName = "(unknown)";
  for (const k of ["name", "fileName", "file_name", "displayName", "subject", "title"]) {
    if (typeof o[k] === "string" && o[k].length > 0) {
      itemDisplayName = o[k];
      break;
    }
  }
  let deepLink;
  for (const k of ["webUrl", "web_url", "url", "link"]) {
    if (typeof o[k] === "string" && o[k].startsWith("http")) {
      deepLink = o[k];
      break;
    }
  }
  let sizeBytesAfter;
  for (const k of ["contentSize", "content_size", "fileSize", "size"]) {
    if (typeof o[k] === "number" && Number.isFinite(o[k])) {
      sizeBytesAfter = o[k];
      break;
    }
  }
  if (sizeBytesAfter === undefined && typeof o.content === "string") {
    const len = o.content.length;
    sizeBytesAfter = Math.floor(len * 3 / 4);
  }
  return { itemId, itemDisplayName, deepLink, sizeBytesAfter };
}
async function sendGatewayRequest(socket, payload, matchType, correlationId) {
  return new Promise((resolve) => {
    const conn = createConnection(socket);
    let buf = "";
    const cleanup = () => {
      try {
        conn.destroy();
      } catch {}
    };
    const replyTimer = setTimeout(() => {
      cleanup();
      resolve({ ok: false, reason: "gateway reply timeout" });
    }, IPC_REPLY_TIMEOUT_MS);
    const connectTimer = setTimeout(() => {
      cleanup();
      resolve({ ok: false, reason: "gateway connect timeout" });
    }, IPC_CONNECT_TIMEOUT_MS);
    conn.once("error", (err) => {
      clearTimeout(replyTimer);
      clearTimeout(connectTimer);
      resolve({ ok: false, reason: `gateway: ${err.message}` });
    });
    conn.once("connect", () => {
      clearTimeout(connectTimer);
      conn.write(JSON.stringify(payload) + `
`);
    });
    conn.on("data", (chunk) => {
      buf += chunk.toString("utf8");
      while (true) {
        const lineEnd = buf.indexOf(`
`);
        if (lineEnd === -1)
          break;
        const line = buf.slice(0, lineEnd);
        buf = buf.slice(lineEnd + 1);
        try {
          const parsed = JSON.parse(line);
          if (parsed?.type === matchType && parsed?.correlationId === correlationId) {
            clearTimeout(replyTimer);
            cleanup();
            resolve({ ok: true, value: parsed });
            return;
          }
        } catch {}
      }
    });
  });
}
async function rpcKernel(payload) {
  return new Promise((resolve) => {
    const conn = createConnection(KERNEL_SOCKET);
    let buf = "";
    const cleanup = () => {
      try {
        conn.destroy();
      } catch {}
    };
    const timer = setTimeout(() => {
      cleanup();
      resolve({ ok: false, reason: "kernel rpc timeout" });
    }, KERNEL_RPC_TIMEOUT_MS);
    conn.once("error", (err) => {
      clearTimeout(timer);
      resolve({ ok: false, reason: `kernel: ${err.message}` });
    });
    conn.once("connect", () => {
      conn.write(JSON.stringify(payload) + `
`);
    });
    conn.on("data", (chunk) => {
      buf += chunk.toString("utf8");
      const lineEnd = buf.indexOf(`
`);
      if (lineEnd === -1)
        return;
      const line = buf.slice(0, lineEnd);
      clearTimeout(timer);
      cleanup();
      try {
        resolve({ ok: true, value: JSON.parse(line) });
      } catch (err) {
        resolve({ ok: false, reason: `kernel parse: ${String(err)}` });
      }
    });
  });
}
async function approvalLookup(agentUnit, scope, approverSet) {
  const res = await rpcKernel({
    v: 1,
    op: "approval_lookup",
    agent_unit: agentUnit,
    scope,
    action: "write",
    current_approver_set: approverSet
  });
  if (!res.ok)
    return null;
  return res.value;
}
function fail(reason) {
  process.stdout.write(JSON.stringify({
    decision: "block",
    reason: `ms-365 write blocked: ${reason}`
  }));
  process.exit(0);
}
function allow() {
  process.exit(0);
}
async function main() {
  const input = parseHookInput();
  if (!input)
    fail("malformed stdin");
  const toolName = input.tool_name;
  if (typeof toolName !== "string" || !isGatedMs365Tool(toolName)) {
    allow();
  }
  const agentName = process.env.SWITCHROOM_AGENT_NAME;
  if (!agentName) {
    allow();
  }
  const accountEmail = process.env.SWITCHROOM_MICROSOFT_ACCOUNT ?? "(unknown)";
  const extract = extractMs365Preview(toolName, input.tool_input);
  const preview = {
    agentName,
    toolName,
    itemId: extract.itemId,
    itemDisplayName: extract.itemDisplayName,
    accountEmail,
    deepLink: extract.deepLink,
    sizeBytesAfter: extract.sizeBytesAfter
  };
  const correlationId = randomBytes(16).toString("hex");
  const requestResult = await sendGatewayRequest(GATEWAY_SOCKET, {
    type: "request_ms365_approval",
    correlationId,
    agentName,
    preview,
    ttlMs: HOOK_TIMEOUT_MS
  }, "ms365_approval_posted", correlationId);
  if (!requestResult.ok)
    fail(requestResult.reason);
  const response = requestResult.value;
  if (!response.ok || !response.requestId) {
    fail(response.reason ?? "gateway returned ok=false");
  }
  const deadline = response.expiresAtMs ?? Date.now() + HOOK_TIMEOUT_MS;
  const scope = `ms-365:write:${preview.itemId}`;
  while (Date.now() < deadline) {
    await new Promise((r) => setTimeout(r, KERNEL_POLL_INTERVAL_MS));
    const lookup = await approvalLookup(agentName, scope, []);
    if (!lookup)
      continue;
    const state = lookup.state;
    if (state === "granted")
      allow();
    if (state === "denied" || state === "drift_revoked" || state === "expired") {
      fail(`operator ${state}`);
    }
  }
  fail("approval timed out");
}
if (__require.main == __require.module) {
  main().catch((err) => {
    const m = err instanceof Error ? err.message : String(err);
    fail(`hook error: ${m}`);
  });
}
export {
  isGatedMs365Tool,
  extractMs365Preview,
  GATED_MS365_WRITE_TOOLS
};
