also looking at this
fix(app): a fresh install now either works or says why
#8
16 files
+512
-75
| @@ -30,7 +30,7 @@ async function openMessages(config: ReturnType<typeof loadConfig>, appDir: strin | ||
| 30 | 30 | catch (error) { |
| 31 | 31 | const message = error instanceof MessagesAccessError ? error.message : String(error) |
| 32 | 32 | 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: [] }) | |
| 34 | 34 | await Bun.sleep(RETRY_OPEN_MS) |
| 35 | 35 | } |
| 36 | 36 | } |
| @@ -73,24 +73,39 @@ export default defineCommand((cli) => { | ||
| 73 | 73 | keepMessagesOpen() |
| 74 | 74 | |
| 75 | 75 | const messages = await openMessages(config, appDir, startedAt) |
| 76 | const sender = new AppleScriptSender() | |
| 76 | 77 | const uplink = new Uplink({ |
| 77 | 78 | config, |
| 78 | 79 | messages, |
| 79 | sender: new AppleScriptSender(), | |
| 80 | sender, | |
| 80 | 81 | engine: selectedEngine(config), |
| 81 | 82 | store: new ModelStore(`${appDir}/storage/uplink/cursor`), |
| 82 | 83 | }) |
| 83 | 84 | await uplink.start() |
| 84 | 85 | |
| 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 | } | |
| 94 | 109 | // .env is read once, at start. Editing it (a new token, say) used to do |
| 95 | 110 | // nothing until someone remembered uplink:restart, and every text in |
| 96 | 111 | // between failed. Under launchd, which restarts us, pick it up on our |