Bring the gateway up at the end of Inkbox setup - #77
Conversation
Nothing the setup wizard writes reaches the agent until the gateway reloads .env, and the wizard only ever printed `hermes gateway run` as a parting instruction. Finish setup by detecting the gateway and offering to act on it. Detection reads the CLI's runtime snapshot (systemd/launchd/s6 service slots plus a bare `gateway run` process), falling back to the gateway's own PID file, and returns liveness plus whether a service slot exists. Three branches follow, each defaulting to yes: restart a running gateway, start an installed-but-down one, or install and launch when there is no service slot at all. Foreground `run` is only ever printed — executing it would take over the wizard's terminal. The restart is skipped when setup is running inside the gateway process, which Hermes refuses anyway to avoid a KeepAlive kill loop, and the profile carries into the subprocess through the inherited HERMES_HOME. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found running this on macOS: launchd refused the bootstrap (exit 5), `hermes gateway install` fell back to "Started gateway as a background process instead" and exited 0, and the immediate re-check still reported nothing running. The step then printed "Gateway service installed. Start it with: hermes gateway start" -- wrong twice over. launchd had not installed anything, and a second start on top of the background process would have duplicated the gateway. The closing next-steps list repeated the mistake with `hermes gateway run`. get_running_pid() only reports a gateway once it has taken its runtime lock and written its PID record, which happens partway through startup, so a probe fired the instant install returns races it. Poll for up to 15s instead. When the window still closes unconfirmed, say so and point at `hermes gateway status` rather than handing out a start command, since install reported success and may well have brought one up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed
Both of those last two lines were wrong. launchd installed nothing, and a second start on top of the running background process would have duplicated the gateway. Cause: Fix is two-part:
4 tests updated/added, including one asserting the unconfirmed path emits neither |
Found debugging why a freshly configured agent never answered an SMS. `hermes gateway stop` no-ops on macOS whenever the launchd plist exists but the job was never bootstrapped -- it boots out nothing, reports success, and leaves the detached process alive. Every subsequent run of this step then started another one on top. Only one gateway can hold the Inkbox platform lock and the adapter's listen port, so the extras come up with Inkbox dead, and the survivor may predate the config just written. This step still printed "Gateway started with the new Inkbox config" over all of it. Count the gateway PIDs the runtime snapshot already carries. Above one, say so and point at `hermes gateway stop --all` instead of claiming the new config is live. The start branch also confirms liveness before claiming it, the way install already did. The stop no-op and the launchd bootstrap failure are host bugs and are not fixable from here; this only stops the wizard from reporting a success that is not real. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed What happened: Reported success, killed nothing. Every later Only one can hold the Inkbox platform lock and the adapter's listen port ( Fix: the runtime snapshot already carries
The 4 new tests. 290 pass. |
The wizard ended on a "Next steps" list even after it had just started or restarted the gateway, which reads as unfinished work when there is none left to do. When the gateway is live, close on a banner naming the identity the agent now runs as and the one command worth knowing: ╭────────────────────────────────────────────────────╮ │ Your Hermes agent is set up and running on Inkbox. │ │ │ │ Inkbox identity: dimas-clanker │ │ Check its health: hermes inkbox doctor │ ╰────────────────────────────────────────────────────╯ The next-steps list stays for the case it was written for: nothing is listening, so `hermes gateway run` is genuinely the next thing to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.2.6 shipped with the gateway-startup work in #77, so the fleet-shared number moves on for this feature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.2.6 shipped with the gateway-startup work in #77, so the fleet-shared number moves on for this feature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add dedicated-outbound iMessage group chats * Bump to 0.2.7 for the group-chat release 0.2.6 shipped with the gateway-startup work in #77, so the fleet-shared number moves on for this feature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
hermes inkbox setupwrote credentials, channels, realtime config and a signing key to~/.hermes/.env, then printedhermes gateway runand exited. None of it reached the agent until someone re-read that line and acted on it — and on a rerun, where a gateway is already up on the old config,runwas the wrong command entirely.Setup now ends with a real step: detect the gateway, then offer to act on it. Every prompt defaults to yes.
Restart the gateway now?hermes gateway restartLaunch the gateway now?hermes gateway startInstall and launch the gateway service now?hermes gateway installand closes on a banner rather than a to-do list with nothing left to do:
The next-steps list survives only for the case it was written for: nothing listening, so
hermes gateway runreally is the next thing.Detection
_gateway_runtime_state()returns(running, service_installed):hermes_cli.gateway.get_gateway_runtime_snapshot()— the CLI's unified view, covering systemd/launchd/s6 service slots and a barehermes gateway runprocess.gateway.status.is_gateway_running()(the PID/lock file) when that import fails.(None, False)when neither is importable — the wizard prints both commands rather than guessing.Two fixes that came out of running this on macOS
Exit codes are not evidence.
hermes gateway installhit a launchd bootstrap failure, fell back to starting a plain background gateway, and exited 0:Both of those last lines were wrong — launchd installed nothing, and a second start on top of the running process would have duplicated the gateway.
get_running_pid()only reports a gateway once it has taken its runtime lock and written its PID record, so a probe fired the instantinstallreturns races the process it just spawned. Now_wait_for_gateway_running()polls for up to 15s, and an unconfirmed start or install reportsCould not confirm the gateway came upand points athermes gateway statusrather than handing out a start command.A stale sibling can own Inkbox.
hermes gateway stopno-ops whenever the launchd plist exists but the job was never bootstrapped — it takes the launchd path onget_launchd_plist_path().exists(), boots out a job that was never loaded, and never reaches the code that kills the detached process:Reported success, killed nothing. Every later
Launch the gateway now?stacked another one on top. Only one can hold the Inkbox platform lock and the adapter's listen port (adapter.py:1788, and thePort %d already in usecheck below it), so the extras come up with Inkbox dead — and the survivor may predate the config just written. Inbound messages then have no receiver while this step reports success.The runtime snapshot already carries a deduplicated
gateway_pidstuple and this step was collapsing it to a bool. It now reports when more than one is alive, points athermes gateway stop --all, and suppresses the success line — in the restart, start and install branches.Both of those are host bugs and not fixable from the plugin; this only stops the wizard reporting a success that is not real.
Decisions worth a look
runis never executed, only printed — it would take over the wizard's terminal. That is why the no-slot branch offersinstall(which registers a supervised slot and asks its own start-now question) rather thanrun.installgets no subprocess timeout — it prompts on this same terminal, so a clock would kill it mid-answer.restart/startare bounded at 180s; the CLI's own service-restart timeout is 90s and a restart drains in-flight agent runs first._HERMES_GATEWAY=1. Hermes refuses that itself to avoid a KeepAlive kill loop.HERMES_HOME, which the CLI exports before argparse, sohermes -p foo inkbox setuptargets foo's gateway.Also
Start the gateway with: hermes gateway runin the closing agent summary, and the run/restart commands printed mid-flow in the iMessage walkthrough. Both defer to the new step.Verified live on macOS across the restart and launch branches, including a launchd fallback to a background process. 22 tests cover every branch, the decline paths, the poll behaviour and its timeout, duplicate detection, the banner's content and box geometry, and the
hermes-not-on-PATH fallback (python -m hermes_cli.main). 290 pass; 3 pre-existingtests/test_external_reply.pyfailures come from a missingaiohttpin this environment and are unrelated — confirmed against a clean checkout.Siblings: inkbox-ai/openclaw-plugin#46, inkbox-ai/claude-code-plugin#47, inkbox-ai/codex-plugin#29, inkbox-ai/opencode-plugin#11.