Bring the gateway up at the end of Inkbox setup - #46
Conversation
The wizard writes channels.inkbox to the OpenClaw config with a plain temp-file rename, which skips the host's config-write path and the restart it schedules afterwards. A gateway that was already running kept serving the old settings, and setup ended on "run `openclaw inkbox doctor`" without ever mentioning it. Setup now closes by asking the host for `gateway status --json`, then offering the action that fits: restart a running gateway, launch an installed-but-stopped one, or install and launch a service when no slot exists. Each defaults to yes. Foreground `gateway run` is only ever printed, since running it would take over the wizard's terminal. The step runs only on the persisted path — when the snippet is printed for manual paste there is nothing on disk yet to pick up. Detection and the lifecycle calls live in a new gateway-service module with an injectable runner, so tests never spawn the host CLI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found running the Hermes twin of this step on macOS: launchd refused the bootstrap (exit 5), the host 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: 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. A gateway is not visible to `gateway status` until it finishes claiming its runtime state, so poll for up to 15s instead of asking once. When the window still closes unconfirmed, say so and point at `gateway status` rather than handing out a start command, since install reported success and may well have brought one up. Both the poll window and its delay are injectable so tests neither wait nor spin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed There, This module had the identical shape, so it gets the identical fix:
Both the poll window and its delay are injectable — the first cut of the test span 15 real seconds against the hardcoded window. 19 tests in |
Setup ended on "run doctor / start the gateway" even after it had just started or restarted one, which reads as unfinished work when there is none left. When the gateway is live, close on a banner naming the identity the agent now runs as and the one command worth knowing. The instruction lines stay for the case they were written for: nothing is listening, so starting one really is the next thing to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed Setup signed off with The instruction lines stay for the case they were written for — nothing listening, so starting one really is the next step. Box width comes off the longest row, so long handles do not break it. 379 tests, |
The install branch already polled for liveness before claiming success; the start branch right above it still trusted `gateway start` exiting 0. That exit only says the command ran -- a gateway that fails to bind is gone a second or two later, and the operator got "started with the new Inkbox config" over it. Same treatment as install: poll, and when the window closes unconfirmed say so and point at `gateway status` rather than claiming a start that may not have happened. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`openclaw plugins install git:inkbox-ai/openclaw-plugin` fails on main: the host clones, runs npm install, then refuses to load the plugin because package.json points main at ./dist/index.js and dist/ is gitignored. The message reads as a publisher packaging issue, which is exactly what it is. npm runs `prepare` after installing dependencies in a git checkout, which is the hook for this. One line, and the clone compiles itself. Every existing job hid the problem: they all `npm run build` first, then install from a linked local path, where the host accepts TypeScript source outright. So the one install path a user actually takes was the only one never exercised. The new git-install job runs it with no pre-build, cloning this commit into a bare repo and installing through `git:file://` -- the identical code path in the host as `git:owner/repo`, without needing the branch to exist publicly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed The host clones, runs Fix: Why CI never caught itEvery job builds first, then installs from a linked local path — where the host accepts TypeScript source outright: npm ci
npm run build # ← builds dist/ itself
openclaw plugins install -l "$GITHUB_WORKSPACE" # ← -l, local pathSame shape in all four live workflows and canary. Both conditions that would surface the bug are bypassed. The one install path a user actually takes was the only one never exercised. So Unrelated papercut in the host
if (atIndex > lastSlashIndex && atIndex > 0) { ... }
380 tests, |
The `prepare` hook I added cannot run. The host installs plugin dependencies with --ignore-scripts hardcoded unconditionally (src/infra/safe-package-install.ts:55) on top of --omit=dev, so no lifecycle script executes and typescript is not even present. Verified by installing this branch through `git:file://` on a real machine: the identical "requires compiled runtime output" failure. The git-install CI job goes with it -- it asserts a behaviour we do not actually support, so it would fail on every run. Nothing is regressed by this: `git:owner/repo` was never the documented install. README says clone, npm install, npm run build, then `openclaw plugins install -l ./`, which is also what CI exercises and what works today. Making `git:` installs work needs either a committed dist/ or an npm publish, which is a distribution decision, not a fix to slip into this PR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Correcting myself: The host installs plugin dependencies with // src/infra/safe-package-install.ts:48
export function createSafeNpmInstallArgs(options = {}): string[] {
return [
"install",
...(options.omitDev ? ["--omit=dev"] : []),
...
"--ignore-scripts", // ← alwaysand Confirmed on a real machine: installing this branch through The CI job goes with it — it asserted something we do not support, so it would have failed every run. Nothing is regressed. git clone https://github.com/inkbox-ai/openclaw-plugin.git
cd openclaw-plugin && npm install && npm run build
openclaw plugins install -l ./which is what CI exercises and what works. My earlier claim that "CI never tests the path a user takes" was wrong on that point — CI tests exactly the documented path. Making This PR is back to what it says on the tin: the gateway (re)start step, the poll-don't-probe confirmations, and the closing banner. |
Summary
The wizard wrote
channels.inkboxto disk and ended onRun \openclaw inkbox doctor``. A gateway that was already running kept serving the old settings, and nothing said so — a rotated signing key or a new identity silently did not take effect.Root cause:
persistOpenClawConfigFile(src/setup-wizard.ts:262) does a plain temp-file +renameontoopenclaw.json, bypassing the host'swriteConfigpath and the restart it schedules viaafterWrite: { mode: "restart" }— the same hook the host's own plugin CLI uses for exactly this.Setup now closes by asking the host for
gateway status --json --no-probeand offering the action that fits. Every prompt defaults to yes.Restart the gateway now?openclaw gateway restartLaunch the gateway now?openclaw gateway startInstall and launch the gateway service now?openclaw gateway installThen a closing banner instead of a to-do list with nothing left to do:
Exit codes are not evidence
Both the
startandinstallbranches confirm liveness by polling, not by trusting the command's exit status.gateway startexiting 0 only says the command ran; a gateway that fails to bind is gone a second or two later. When the window closes unconfirmed the step says so and points atgateway status— it never prints a success line, and never suggests starting a gateway that install may already have brought up.This came out of testing the Hermes twin on macOS, where
launchctl bootstrapfails and the host silently degrades to a plain background process. The first cut probed once and reportedGateway service installed. Start it with: gateway start— wrong twice: launchd had installed nothing, and a second start would have duplicated a live process.Decisions worth a look
persistConfigis absent the wizard prints the snippet for manual paste; nothing is on disk yet, so there is nothing to pick up and the step is skipped.gateway runis never executed, only printed — it would take over the wizard's terminal. That is why the no-slot branch offersinstall(which registers a launchd/systemd/schtasks slot and brings it up) rather thanrun.openclawis an optional peer dependency, so importing its status modules is not safe. It re-invokes the entry script running the wizard ([process.execPath, process.argv[1]]), the way the host's updater shells back into its owngatewaycommands, falling back toopenclawon PATH.Reverted mid-PR
8631fc8added apreparescript claiming to fixopenclaw plugins install git:<repo>. It cannot work — the host installs plugin dependencies with--ignore-scriptshardcoded unconditionally (src/infra/safe-package-install.ts:55) plus--omit=dev, so no lifecycle script runs andtypescriptis not present.a02ab09reverts it and the CI job that went with it. Nothing regressed:git:owner/repowas never the documented install — the README says clone, build,openclaw plugins install -l ./, which is what CI exercises and what works.Verified by hand
Ran the real install against the built branch in an isolated state dir:
Re-running the install over an existing one relinks cleanly. All four detection branches, the banner and both poll confirmations are present in the compiled output.
Also
19 tests in
tests/gateway-service.test.tscover status parsing (JSON preceded by warnings, pid-only runtime, unusable output), every offer branch, the decline paths, unconfirmed start and install, lifecycle failure, and the entry-script fallback. 380 tests pass,tsc --noEmitclean.Siblings: inkbox-ai/hermes-agent-plugin#77, inkbox-ai/claude-code-plugin#47, inkbox-ai/codex-plugin#29, inkbox-ai/opencode-plugin#11.