Skip to the content

Capsid menu

app/lib/inbox-report.server.ts

51 lines and 3 functions in DrDustinEdwards/carrel, at commit d5d5e82 on 2026-10-24. This file on GitHub.

Functions

The functions of inbox-report.server.ts
FunctionLinesComplexityCoveredCRAPMutation
capsidOrigin15 to 182100%OkCRAP 2No data: mutation testing has not run on this repo yet
inboxReport24 to 324100%OkCRAP 4No data: mutation testing has not run on this repo yet
reportInbox37 to 587100%WatchCRAP 7No data: mutation testing has not run on this repo yet

Imports

It imports app/db/schema.ts, app/lib/mcp/server.ts, app/lib/timed-fetch.server.ts. Imported by workers/app.ts.

Source

// What Carrel reports about itself to Capsid's inbox (POST /ops/inbox/report), for the app badge in
// the shared AdminShell: the AI drafts waiting for Dustin's review. Each report replaces the last
// whole and Capsid expires it after 6 hours, so the cron sends it every run (15 minutes), and sends
// no items when nothing waits so a cleared queue clears the badge. The bearer is Carrel's own Capsid
// agent key, the secret CAPSID_AGENT_KEY. Never on a page's path: a failure is logged, not thrown.

import { count, min } from "drizzle-orm";
import { drizzle } from "drizzle-orm/d1";

import { aiDrafts } from "~/db/schema";
import { carrelOrigin } from "~/lib/mcp/server";
import { timedFetch } from "~/lib/timed-fetch.server";

/** Capsid's origin, from the var CAPSID_ORIGIN; read loosely, as carrelOrigin does, for an older wrangler.jsonc without it. */
function capsidOrigin(env: Env): string {
  const configured = (env as { CAPSID_ORIGIN?: string }).CAPSID_ORIGIN?.trim();
  return (configured || "https://mcp.dustinedwards.info").replace(/\/+$/, "");
}

type InboxItem = { title: string; link?: string; since?: string };
type InboxReport = { namespace: "carrel"; items: InboxItem[] };

/** The report body for `waiting` AI drafts, the oldest made at `since`. None waiting: no items. */
function inboxReport(waiting: number, since: string | null, origin: string): InboxReport {
  if (waiting <= 0) return { namespace: "carrel", items: [] };
  const item: InboxItem = {
    title: waiting === 1 ? "1 AI draft waits for review" : `${waiting} AI drafts wait for review`,
    link: `${origin}/`,
  };
  if (since) item.since = since;
  return { namespace: "carrel", items: [item] };
}

export type ReportResult = { state: "skipped" | "sent" | "failed"; detail: string };

/** Counts the waiting AI drafts and reports them. Never throws; a failure is logged and returned. */
export async function reportInbox(env: Env, fetcher: typeof fetch = timedFetch): Promise<ReportResult> {
  const key = env.CAPSID_AGENT_KEY?.trim();
  if (!key) return { state: "skipped", detail: "CAPSID_AGENT_KEY is not set." };
  try {
    const row = await drizzle(env.DB)
      .select({ waiting: count(), since: min(aiDrafts.createdAt) })
      .from(aiDrafts)
      .get();
    const body = inboxReport(row?.waiting ?? 0, row?.since ?? null, carrelOrigin(env));
    const response = await fetcher(`${capsidOrigin(env)}/ops/inbox/report`, {
      method: "POST",
      headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
      body: JSON.stringify(body),
    });
    if (!response.ok) throw new Error(`Capsid answered ${response.status}`);
    return { state: "sent", detail: `${body.items.length} item(s) reported.` };
  } catch (error) {
    const detail = error instanceof Error ? error.message : String(error);
    console.error(JSON.stringify({ inbox: "report-failed", error: detail }));
    return { state: "failed", detail };
  }
}