CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
index.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| fc1817a | 1 | import { mkdir } from "fs/promises"; |
| 2 | import app from "./app"; | |
| 3 | import { config } from "./lib/config"; | |
| 25b1ff7 | 4 | import { presenceWebsocket } from "./routes/pulls"; |
| eafe8c6 | 5 | import { startWorker } from "./lib/workflow-runner"; |
| 8405c43 | 6 | import { startWebhookDeliveryWorker } from "./lib/webhook-delivery"; |
| 2b821b7 | 7 | import { startAutopilot } from "./lib/autopilot"; |
| 988380a | 8 | import { ensureDemoContent } from "./lib/demo-seed"; |
| 52ad8b1 | 9 | import { ensureDemoActivity } from "./lib/demo-activity-seed"; |
| 2d985e5 | 10 | import { ensureEnvSiteAdmin } from "./lib/admin-bootstrap"; |
| 5ca514a | 11 | import { ensureMarketplaceSeed } from "./lib/agent-marketplace-seed"; |
| 96942a6 | 12 | import { maybeSelfBootstrap } from "./lib/self-bootstrap"; |
| f764c07 | 13 | import { notifySystemdReady } from "./lib/systemd-notify"; |
| 509c376 | 14 | import { loadConfigIntoEnv } from "./lib/system-config"; |
| 60323c5 | 15 | import { startSshServer } from "./lib/ssh-server"; |
| fc1817a | 16 | |
| 17 | // Ensure repos directory exists | |
| 18 | await mkdir(config.gitReposPath, { recursive: true }); | |
| 19 | ||
| 509c376 | 20 | // /admin/integrations boot hook — pull saved integration secrets out of the |
| 21 | // system_config table and into process.env BEFORE anything else reads them. | |
| 22 | // This is the magic that lets the existing synchronous config getters | |
| 23 | // (config.anthropicApiKey, config.resendApiKey, …) transparently pick up | |
| 24 | // values an admin saved through the UI, with no restart needed. If the DB | |
| 25 | // is unreachable at boot, env vars stay as the fallback — never blocks | |
| 26 | // startup. | |
| 27 | try { | |
| 28 | const n = await loadConfigIntoEnv(); | |
| 29 | if (n > 0) console.log(`[system-config] loaded ${n} key(s) from DB into env`); | |
| 30 | } catch (err) { | |
| 31 | console.warn( | |
| 32 | "[system-config] boot load failed (env vars remain authoritative):", | |
| 33 | err instanceof Error ? err.message : err | |
| 34 | ); | |
| 35 | } | |
| 36 | ||
| 96942a6 | 37 | // Self-bootstrap: if Gluecron's own canonical repo (`ccantynz/Gluecron.com.git` |
| 38 | // by default) doesn't exist on disk yet, initialize it from the GitHub mirror | |
| 39 | // and install the post-receive hook. This is the platform's self-healing path | |
| 40 | // — once it runs successfully on a host, future deploys flow through Gluecron | |
| 41 | // itself with no external CI tooling. Fire-and-forget; never blocks startup. | |
| 42 | void maybeSelfBootstrap().catch((err) => { | |
| 43 | console.warn(`[self-bootstrap] swallowed: ${(err as Error).message}`); | |
| 44 | }); | |
| 45 | ||
| 60323c5 | 46 | // SSH git server (Block SSH-1). Listens for git-over-SSH on SSH_PORT |
| 47 | // (default 2222). Authenticates by public key from the `ssh_keys` table. | |
| 48 | // Set SSH_PORT=0 to disable. Set SSH_HOST_KEY to a persistent PEM key | |
| 49 | // in production to avoid "host key changed" warnings on restart. | |
| 50 | startSshServer(); | |
| 51 | ||
| eafe8c6 | 52 | // Start the Actions-equivalent workflow worker (Block C1). Polls |
| 53 | // workflow_runs for queued rows and executes them sequentially. | |
| 54 | startWorker(); | |
| 55 | ||
| 8405c43 | 56 | // Reliable webhook delivery worker (migration 0056). Polls |
| 57 | // webhook_deliveries for pending rows whose next_attempt_at <= now() and | |
| 58 | // retries with exponential backoff before dead-lettering. | |
| 59 | startWebhookDeliveryWorker(); | |
| 60 | ||
| 2b821b7 | 61 | // Autopilot: periodic mirror sync, merge-queue progress, weekly digests, |
| 62 | // advisory rescans. No-op when AUTOPILOT_DISABLED=1. | |
| 63 | startAutopilot(); | |
| 64 | ||
| 2d985e5 | 65 | // Site-admin bootstrap from env (SITE_ADMIN_USERNAME). Idempotent — if the |
| 66 | // user exists, they get a row in site_admins; if not, logged and retried | |
| 67 | // on next boot. Background-fired so a slow DB doesn't block startup. | |
| a28cede | 68 | void ensureEnvSiteAdmin().catch((err) => { |
| 69 | console.warn( | |
| 70 | "[admin-bootstrap] ensureEnvSiteAdmin failed:", | |
| 71 | err instanceof Error ? err.message : err | |
| 72 | ); | |
| 73 | }); | |
| 2d985e5 | 74 | |
| 5ca514a | 75 | // Agent marketplace seed. Idempotent — inserts the 4 canonical example |
| 76 | // listings only if they don't already exist. Background-fired with a small | |
| 77 | // delay so the admin-bootstrap path lands first (the seed lists the | |
| 78 | // publisher as the bootstrap admin). | |
| 79 | void (async () => { | |
| 80 | try { | |
| 81 | await new Promise((r) => setTimeout(r, 1500)); | |
| 82 | await ensureMarketplaceSeed(); | |
| 83 | } catch (err) { | |
| 84 | console.warn( | |
| 85 | "[agent-marketplace-seed] failed:", | |
| 86 | err instanceof Error ? err.message : err | |
| 87 | ); | |
| 88 | } | |
| 89 | })(); | |
| 90 | ||
| 988380a | 91 | // Opt-in demo content seed on boot (DEMO_SEED_ON_BOOT=1). Idempotent, never |
| 52ad8b1 | 92 | // throws — safe to run on every start. Block L3 layers extra activity (more |
| 93 | // issues, an open + merged PR on todo-api, AI-review comment, auto-merge | |
| 94 | // audit row) so the live /demo page has content out of the box. | |
| 988380a | 95 | if (process.env.DEMO_SEED_ON_BOOT === "1") { |
| 52ad8b1 | 96 | void (async () => { |
| 97 | try { | |
| 98 | await ensureDemoContent(); | |
| 99 | await ensureDemoActivity(); | |
| 100 | } catch { | |
| 101 | /* never throw out of boot */ | |
| 102 | } | |
| 103 | })(); | |
| 988380a | 104 | } |
| 105 | ||
| fc1817a | 106 | console.log(` |
| 107 | gluecron v0.1.0 | |
| 108 | ────────────────────── | |
| 109 | http://localhost:${config.port} | |
| 110 | repos: ${config.gitReposPath} | |
| 111 | `); | |
| 112 | ||
| f764c07 | 113 | // BLOCK N2 — tell systemd we're ready. No-op when NOTIFY_SOCKET is unset |
| 114 | // (dev / non-systemd hosts). Fire-and-forget; never throws. | |
| a28cede | 115 | void notifySystemdReady().catch((err) => { |
| 116 | console.warn( | |
| 117 | "[systemd] notifySystemdReady failed:", | |
| 118 | err instanceof Error ? err.message : err | |
| 119 | ); | |
| 120 | }); | |
| f764c07 | 121 | |
| fc1817a | 122 | export default { |
| 123 | port: config.port, | |
| 124 | fetch: app.fetch, | |
| 25b1ff7 | 125 | // WebSocket handler for real-time PR presence (Figma-style diff cursors). |
| 126 | // Bun.serve forwards WS upgrade requests through the same fetch pipeline; | |
| 127 | // the `upgradeWebSocket` middleware in pulls.tsx calls server.upgrade() and | |
| 128 | // returns a null response. This `websocket` object handles the ws lifecycle. | |
| 129 | websocket: presenceWebsocket, | |
| fc1817a | 130 | }; |