ReviewOS

also looking at this

stacks/uplink

fix(app): report a crash instead of vanishing, and cap the log

#55
Merged glennmichael123 wants to merge fix/log-a-crash-and-cap-the-log into main
4 files +252 -5
app/Desktop/launcher.tsmodified+25-5
Changes to app/Desktop/launcher.ts
@@ -11,14 +11,14 @@
1111 * Run from source for development with `bun app/Desktop/launcher.ts`; Craft
1212 * then comes from CRAFT_BIN or PATH (pantry installs it).
1313 */
14import { appendFileSync, existsSync, mkdirSync } from 'node:fs'
14import { existsSync } from 'node:fs'
1515import { dirname, join } from 'node:path'
1616import process from 'node:process'
1717import { format } from 'node:util'
1818import pkg from '../../package.json'
1919import { agentPath } from '../Uplink/agent-path'
2020import { acquireSingleInstance, startDesktopAgent } from '../Uplink/desktop-agent'
21import { LOG_PATH } from '../Uplink/settings'
21import { installCrashHandlers, writeLog } from '../Uplink/log'
2222
2323// Launched from Finder or from its own LaunchAgent, this process gets the bare
2424// launchd PATH, and everything below it inherits that: the engines, their
@@ -28,14 +28,31 @@ import { LOG_PATH } from '../Uplink/settings'
2828process.env.PATH = agentPath()
2929
3030// Launched from Finder or at login there is no terminal, so keep a log where
31// Console.app looks for one.
31// Console.app looks for one. From a terminal the developer keeps stderr.
3232if (!process.stdout.isTTY) {
33 mkdirSync(dirname(LOG_PATH), { recursive: true })
3433 for (const level of ['log', 'warn', 'error'] as const) {
35 console[level] = (...args: unknown[]) => appendFileSync(LOG_PATH, `${new Date().toISOString()} ${format(...args)}\n`)
34 console[level] = (...args: unknown[]) => writeLog(format(...args))
3635 }
3736}
3837
38/**
39 * Whether the things `shutdown` closes over exist yet. A crash during startup
40 * is both the one most worth reporting and the one where calling `shutdown`
41 * would itself throw, so that case exits directly. The single instance lock is
42 * reaped by the next start.
43 */
44let running = false
45
46installCrashHandlers({
47 shutdown: (code) => {
48 if (!running)
49 process.exit(code)
50 // A hung agent.stop() must not keep a half dead process alive.
51 setTimeout(() => process.exit(code), 3000).unref()
52 void shutdown(code)
53 },
54})
55
3956if (!acquireSingleInstance()) {
4057 console.log('[uplink] already running')
4158 process.exit(0)
@@ -84,6 +101,9 @@ void new Response(craft.stderr).text().then((stderr) => {
84101
85102// The point is answering while you are away, so hold off idle sleep.
86103const keepAwake = Bun.spawn(['/usr/bin/caffeinate', '-i', '-w', String(process.pid)], { stdio: ['ignore', 'ignore', 'ignore'] })
104// Everything `shutdown` touches now exists, so a crash from here on can go
105// through it rather than exiting on the spot.
106running = true
87107
88108let stopping = false
89109async function shutdown(code: number): Promise<never> {