feat(host): the Mac follows its commands over Neon Realtime and says it listens on a slow timer
Merged
CH
10d ago+525+525+012345678901234567890123456789-241-241-012345678901234567890123456789
Stack 4 · Mac commands, on #3274. compose-mac-commands.ts follows the account's unclaimed commands through the live layer, in place of one held read after another.
- Follow: uses
liveRowswith reseal and poll. Realtime is off (noLUKE_REALTIME), so this is the poll the service holds until the rows move (#4272). Every read namesaccountId(LIVE_USER_QUERY) and the Mac's kinds (RUNS, shell). When the service answers signed out, the follow ends. - Claim: each new command is claimed by id, earliest queued first, and run or refused by its kind, as before #4273. Only what the service grants runs, so two Macs never run one command.
- Presence, the one timer that remains:
macCommands.listening(RUNS)is sent at once and then every 15 s, inside the service's 45 sLISTENER_FRESH. No row change can say the process is alive. Each beat reads the gate first. When it shuts, the follow and the beats end, and running commands are stopped. - No fallback: an unanswered read is asked again after 5 s. The client's held read
waiting()is removed, because the desktop no longer asks for it. The service still answers it.
compose-mac-commands.test.ts runs against a scripted service with Realtime off: answered polled, and held until the rows move. It covers:
- the gate;
- claim order;
- a browser command refused while the shell one runs;
- a command queued later heard within about a second;
- every read naming the account and shell;
- the 15 s beat, and stopping when the setting is switched off;
- a signed-out read following nothing and being asked again.
Checked piece by piece: tsc for features and host; vitest over all of host and the client; knip; oxlint; discarded-effect.