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