my harness for niri
1import { openBash, closeBash } from "./container/index.js"
2import { createServer } from "./server.js"
3import { initDb } from "./db.js"
4import { shutdown } from "./runner/index.js"
5import { startDiscordGateway } from "./discord/gateway.js"
6
7const PORT = parseInt(process.env.PORT ?? "3000")
8
9async function main() {
10 console.log("[niri] starting up...")
11
12 initDb()
13 await openBash()
14
15 let discordGateway: Awaited<ReturnType<typeof startDiscordGateway>> = null
16 try {
17 discordGateway = await startDiscordGateway()
18 } catch (err) {
19 console.warn("[discord gateway] startup failed:", err)
20 }
21
22 const server = createServer()
23
24 await server.listen({ port: PORT, host: "0.0.0.0" })
25 console.log(`[niri] listening on :${PORT}`)
26
27 let shuttingDown = false
28
29 async function gracefulShutdown(sig: string) {
30 if (shuttingDown) return // ignore duplicate signals
31 shuttingDown = true
32 console.log(`\n[niri] ${sig} received, asking niri to journal and rest...`)
33
34 const timeout = new Promise<void>((resolve) =>
35 setTimeout(() => { console.log("[niri] shutdown timed out, forcing exit"); resolve() }, 60_000)
36 )
37
38 await Promise.race([shutdown(), timeout])
39
40 if (discordGateway) await discordGateway.stop()
41 await server.close()
42 closeBash()
43 process.exit(0)
44 }
45
46 // Second SIGINT = user is insisting
47 process.on("SIGINT", async () => {
48 if (shuttingDown) {
49 console.log("\n[niri] force exit")
50 process.exit(1)
51 }
52 await gracefulShutdown("SIGINT")
53 })
54
55 process.on("SIGTERM", () => gracefulShutdown("SIGTERM"))
56}
57
58main().catch((err) => {
59 console.error("[niri] fatal:", err)
60 process.exit(1)
61})