Ctrl K
ComunidadGuías…

instrumentation.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Next 16 framework error hook. Routes uncaught render/RSC/route errors into
// the audit module so they land in the same forensic trail as exec/fs/auth
// events — no extra log channel, no extra ops surface. Best-effort: this
// handler must NEVER throw (would mask the real error).
import type { Instrumentation } from "next";

export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    // The only place that knows how Next exposes cookies. `lib/auth` stays framework-free
    // and asks this registered reader, which keeps Phase 2's boundary honest. A failure
    // here is NOT swallowed: without it every session read fails closed, and a silent
    // catch would turn that into a confusing "not registered" error per request.
    await import("next/headers").then(({ cookies }) =>
      import("./lib/auth/require-session").then(({ registerSessionCookieReader, SESSION_COOKIE }) =>
        registerSessionCookieReader(async () => (await cookies()).getAll(SESSION_COOKIE)),
      ),
    );
    // Warm durable agent-session maintenance before accepting requests. Backfilling
    // the conversation index once at boot keeps MCP lookup O(1) even on hosts with
    // thousands of old CLI sessions; archive retention remains best-effort.
    await Promise.all([
      import("./lib/agent/session-files")
        .then(({ backfillConversationIndex }) => backfillConversationIndex())
        .catch(() => undefined),
      import("./lib/agent/session-archive")
        .then(({ pruneAgentSessionArchives }) => pruneAgentSessionArchives())
        .catch(() => undefined),
      Promise.all([import("./lib/workflow/graph-triggers"), import("./lib/mcp/tools"), import("./lib/mcp/capability-runtime")])
        .then(([{ startWorkflowScheduler }, { TOOLS_BY_NAME }, { msoCapabilityRuntime }]) => startWorkflowScheduler((name) => TOOLS_BY_NAME.get(name), msoCapabilityRuntime))
        .catch(() => undefined),
      import("./lib/mcp/federation/batonly-worker")
        .then(({ startBatonlyFederationWorker }) => startBatonlyFederationWorker())
        .catch(() => undefined),
    ]);
    // systemd liveness: when the unit sets WatchdogSec, systemd exposes
    // NOTIFY_SOCKET + WATCHDOG_USEC. Node has no native AF_UNIX SOCK_DGRAM
    // (dgram is UDP-only), so we ping via the `systemd-notify` binary — which
    // requires NotifyAccess=all in the unit, since the ping comes from a child
    // PID, not main. A missed ping past WatchdogSec → systemd restarts us; this
    // catches a wedged event loop that Restart=always (crash-only) can't.
    // No-op in dev / demo / CI where NOTIFY_SOCKET is unset.
    if (!process.env.NOTIFY_SOCKET) return;
    const { execFile } = await import("node:child_process");
    const usec = parseInt(process.env.WATCHDOG_USEC ?? "0", 10);
    const intervalMs = usec > 0 ? Math.floor(usec / 2000) : 10_000; // half the deadline, in ms
    const ping = () => execFile("systemd-notify", ["WATCHDOG=1"], () => {});
    ping();
    setInterval(ping, intervalMs).unref();
  }
}

export const onRequestError: Instrumentation.onRequestError = async (
  err,
  request,
  context,
) => {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    try {
      const { audit } = await import("./lib/host/audit");
      const e = err instanceof Error ? err : new Error(String(err));
      // Route shape goes into structured `meta` (since the audit field exists)
      // instead of being packed into `target` — easier to filter with `jq`,
      // greppable as `routerKind`/`routeType` keys, and leaves `target` clean
      // for the actual path the request hit.
      await audit({
        action: "framework.error",
        ok: false,
        actor: null,
        target: request.path,
        detail: `${e.name}: ${e.message}`,
        meta: {
          routerKind: context.routerKind,
          routeType: context.routeType,
        },
      });
    } catch {
      // never crash here — best-effort logging only
    }
  }
};