ReviewOS

also looking at this

stacks/uplink

fix(app): a fresh install now either works or says why

#8
Merged glennmichael123 wants to merge fix/setup-blockers into main
16 files +512 -75
app/Commands/Uplink.tsmodified+26-11
Changes to app/Commands/Uplink.ts
@@ -30,7 +30,7 @@ async function openMessages(config: ReturnType<typeof loadConfig>, appDir: strin
3030 catch (error) {
3131 const message = error instanceof MessagesAccessError ? error.message : String(error)
3232 console.error(`[uplink] ${message} Retrying in ${RETRY_OPEN_MS / 1000}s.`)
33 await writeHeartbeat(appDir, { pid: process.pid, startedAt, lastPollAt: null, lastError: message, allowed: config.allowed, own: [], active: [] })
33 await writeHeartbeat(appDir, { pid: process.pid, startedAt, lastPollAt: null, lastError: message, automation: null, lastSendError: null, allowed: config.allowed, own: [], active: [] })
3434 await Bun.sleep(RETRY_OPEN_MS)
3535 }
3636 }
@@ -73,24 +73,39 @@ export default defineCommand((cli) => {
7373 keepMessagesOpen()
7474
7575 const messages = await openMessages(config, appDir, startedAt)
76 const sender = new AppleScriptSender()
7677 const uplink = new Uplink({
7778 config,
7879 messages,
79 sender: new AppleScriptSender(),
80 sender,
8081 engine: selectedEngine(config),
8182 store: new ModelStore(`${appDir}/storage/uplink/cursor`),
8283 })
8384 await uplink.start()
8485
85 const beat = (): Promise<number> => writeHeartbeat(appDir, {
86 pid: process.pid,
87 startedAt,
88 lastPollAt: uplink.lastPollAt,
89 lastError: uplink.lastError,
90 allowed: uplink.allowedHandles,
91 own: uplink.ownHandles,
92 active: uplink.activeRuns,
93 })
86 // The Automation grant is per app, so only this process can prove that
87 // Uplink.app may drive Messages. Ask once at start, and again whenever a
88 // send has failed, so the doctor and the dashboard report the subject
89 // that actually sends rather than whichever terminal ran them.
90 let automation = await sender.canSend()
91 if (!automation.ok)
92 console.error(`[uplink] cannot send through Messages: ${automation.detail}`)
93
94 const beat = async (): Promise<number> => {
95 if (uplink.lastSendError && automation.ok)
96 automation = await sender.canSend()
97 return writeHeartbeat(appDir, {
98 pid: process.pid,
99 startedAt,
100 lastPollAt: uplink.lastPollAt,
101 lastError: uplink.lastError,
102 automation: { ok: automation.ok, detail: automation.detail },
103 lastSendError: uplink.lastSendError,
104 allowed: uplink.allowedHandles,
105 own: uplink.ownHandles,
106 active: uplink.activeRuns,
107 })
108 }
94109 // .env is read once, at start. Editing it (a new token, say) used to do
95110 // nothing until someone remembered uplink:restart, and every text in
96111 // between failed. Under launchd, which restarts us, pick it up on our