| import app from "./app"; |
| import { logger } from "./lib/logger"; |
| import { ensureLoaded } from "./lib/research-data"; |
| import { startJobPoller } from "./lib/job-tracker"; |
| import { startLatencyAlertMonitor } from "./llm/latency-alert-monitor"; |
| import { |
| loadPersistedLatencySamples, |
| startLatencySamplePersistence, |
| } from "./llm/sample-persistence"; |
| import { config } from "./lib/config"; |
| import { db } from "@workspace/db"; |
| import { inviteCodes } from "@workspace/db"; |
| import { sql } from "drizzle-orm"; |
| import { seedToolGraphOnce } from "./lib/tool-graph-seed"; |
| import { seedDrugClipOnce } from "./lib/drugclip/seed"; |
| import { |
| toolGraphEnabled, |
| startDeprecationScheduler, |
| } from "./lib/tool-graph"; |
| import { startEvolutionScheduler } from "./lib/evolution/scheduler"; |
| import { applyCapabilityMigrations } from "./lib/capability/migrations/apply.js"; |
| import { loadRegisteredCapabilities } from "./lib/capability/loader.js"; |
|
|
| |
| |
| |
| |
| |
| if (process.env["NODE_ENV"] === "production" && config.inviteCodes.length === 0) { |
| void (async () => { |
| try { |
| const rows = await db |
| .select({ n: sql<number>`count(*)::int` }) |
| .from(inviteCodes); |
| const count = rows[0]?.n ?? 0; |
| if (count === 0) { |
| logger.error( |
| "AUTH BOOTSTRAP: no invite codes available (INVITE_CODES env is empty AND invite_codes table is empty). " + |
| "All registrations will return 403. Run `pnpm --filter @workspace/api-server seed:prod` " + |
| "with INITIAL_INVITE_CODES set, or populate the INVITE_CODES env var, then redeploy.", |
| ); |
| } |
| } catch (err) { |
| logger.warn( |
| { err }, |
| "AUTH BOOTSTRAP: could not query invite_codes table to verify auth bootstrap state.", |
| ); |
| } |
| })(); |
| } |
|
|
| |
| void ensureLoaded().catch((err) => |
| logger.error({ err }, "research data warm-load failed"), |
| ); |
|
|
| |
| |
| |
| if (toolGraphEnabled() && process.env["NODE_ENV"] !== "test") { |
| void seedToolGraphOnce().catch((err) => |
| logger.warn({ err }, "tool-graph seed failed (continuing without graph)"), |
| ); |
| } |
|
|
| |
| |
| if (process.env["NODE_ENV"] !== "test") { |
| void seedDrugClipOnce().catch((err) => |
| logger.warn({ err }, "drugclip seed failed (continuing without network)"), |
| ); |
| } |
|
|
| |
| |
| startJobPoller(); |
|
|
| |
| |
| |
| if (process.env["NODE_ENV"] !== "test") { |
| startLatencyAlertMonitor(); |
| |
| |
| |
| |
| void loadPersistedLatencySamples() |
| .catch((err) => |
| logger.warn({ err }, "loadPersistedLatencySamples failed"), |
| ) |
| .finally(() => { |
| startLatencySamplePersistence(); |
| }); |
| } |
|
|
| const rawPort = process.env["PORT"]; |
|
|
| if (!rawPort) { |
| throw new Error( |
| "PORT environment variable is required but was not provided.", |
| ); |
| } |
|
|
| const port = Number(rawPort); |
|
|
| if (Number.isNaN(port) || port <= 0) { |
| throw new Error(`Invalid PORT value: "${rawPort}"`); |
| } |
|
|
| const server = app.listen(port, () => { |
| logger.info({ port }, "Server listening"); |
| |
| void import("./lib/capability/external-knowledge/index.ts").then((m) => |
| m.logAdapterStatusBanner({ info: (msg) => logger.info(msg) }), |
| ); |
| |
| |
| |
| |
| void applyCapabilityMigrations() |
| .then(({ applied, skipped }) => |
| logger.info( |
| { applied, skipped }, |
| "capability schema migrations applied", |
| ), |
| ) |
| .then(() => loadRegisteredCapabilities()) |
| .then(({ loaded, errors }) => { |
| if (loaded.length > 0 || errors.length > 0) { |
| logger.info( |
| { loaded, errors }, |
| "capability registry boot scan complete", |
| ); |
| } else { |
| logger.info( |
| "capability registry boot scan complete (0 manifests in _registered/)", |
| ); |
| } |
| }) |
| .catch((err) => |
| logger.error( |
| { err }, |
| "capability boot pipeline failed (server still up; capability layer degraded)", |
| ), |
| ); |
| }); |
|
|
| |
| |
| if (toolGraphEnabled()) { |
| startDeprecationScheduler(); |
| } |
|
|
| |
| |
| |
| |
| if (process.env["NODE_ENV"] !== "test") { |
| startEvolutionScheduler(); |
| } |
|
|
| server.on("error", (err: NodeJS.ErrnoException) => { |
| if (err.code === "EADDRINUSE") { |
| logger.fatal( |
| { err, port }, |
| `Port ${port} is already in use. Another process is bound to it; refusing to start.`, |
| ); |
| } else if (err.code === "EACCES") { |
| logger.fatal( |
| { err, port }, |
| `Permission denied binding to port ${port}.`, |
| ); |
| } else { |
| logger.fatal({ err, port }, "Fatal error while starting HTTP server"); |
| } |
| process.exit(1); |
| }); |
|
|