feat: add bridge dev command that runs devCommand under health supervision - #10
Merged
Merged
Conversation
vercel-eddie
force-pushed
the
eddie/exec-wait-healthy
branch
5 times, most recently
from
May 20, 2026 22:49
aa67689 to
7a125f9
Compare
…rvision Adds a new top-level command that launches the `devCommand` field declared in devcontainer.json (a string or array of strings — e.g. "pnpm dev") and waits for the source deployment's liveness/readiness/startup probes to report the local app as healthy. The command exits when: 1. The dev command process exits (returns that exit code) 2. All configured probes report healthy (success; pid is in the response) 3. --timeout elapses (default 2m, BRIDGE_DEV_TIMEOUT-configurable) Re-running `bridge dev` for the same bridge first kills any previously tracked dev process so the new server replaces the old one cleanly. The PID is persisted in ~/.bridge/sessions/<bridge>.json so we can find it across invocations even if the bridge CLI was interrupted in between. Implementation: - proto/session.proto: track dev_pid / dev_started_at / dev_command on Session - proto/command.proto: add DevCommandResponse + DevCommandReason enum - pkg/devcontainer: Config.DevCommand() reader for the new field; StartDetached / KillProcess / IsProcessAlive / ReadDetachedExitCode primitives on CLIClient. The detached command runs via a `setsid` wrapper through `devcontainer exec`, so the dev process survives our disconnect but still gets the devcontainer's remoteUser, containerEnv, and feature-injected env applied. - pkg/session: add Write(s *Session) so callers can preserve fields across updates. Save still resets dev state — fresh bridges shouldn't inherit stale PIDs. - pkg/commands/dev.go: ties it together. waitForDevHealthy races the three signals in the documented precedence order; exit-before-healthy returns the underlying exit code instead of swallowing it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vercel-eddie
force-pushed
the
eddie/exec-wait-healthy
branch
from
May 20, 2026 22:52
7a125f9 to
7ddd9f8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
bridge dev <bridge>that launches thedevCommandfrom devcontainer.json (e.g."pnpm dev"), streams its output, and waits until the source deployment's probes report the local app healthy. PID is persisted across invocations so re-running cleanly kills the previous server.What's in this PR
bridge devcommand — positional bridge name,--timeout(default 2m,BRIDGE_DEV_TIMEOUT).devCommandfrom devcontainer.json (string or array)devcontainer execwithSetsidso it survives the CLI exitinteract.NewViewportbridge createuses--timeoutProbeRPC on InterceptorService — synchronous, stateless "is the app responding right now?" check. Bypasses the threshold-trackedGetStatusview so a stale HEALTHY from a previous session can't fool the new run. Bridge dev pollsProbe;bridge getkeeps usingGetStatusfor the smoothed steady-state view.bridge getAPPLICATION column — newapplication_statusfield onGetCommandResponseBridge. Queried in parallel per bridge with a 1.5s timeout. Values:healthy/unhealthy/starting/unreachable/stopped. Defaults tohealthywhen the source has no probes.--start-container/-sonbridge create— starts the devcontainer after creating the bridge but doesn't attach.--connectnow implies--start-container. Useful for agent flows that want a container running without holding the terminal.Install path moved to
~/.local/bin/bridge— no sudo, no xattr, no Gatekeeper workaround needed (curl doesn't quarantine its downloads). Override withBRIDGE_INSTALL_DIR=.... Hint aboutPATHis printed only when the directory isn't already on it.linuxBinaryPathno longer dev-only-relative — prefers~/.bridge/bin/bridge-linuxwhen present sobridge createworks from any CWD; falls back to./dist/bridge-linuxonly for the in-repo development workflow.Misc:
dev_pid/dev_started_at/dev_commandsession.Write(*Session)so callers can Load → mutate → Write without losing fields;Savestill resets dev state on fresh createsTests
pkg/probe/*_test.go— Probe interface contract, HTTP/TCP/Exec handlerspkg/devcontainer/devcommand_test.go— string / array / missing / invalid devCommandpkg/session/session_test.go— fresh save, full round-trip with dev fields, Save resets, idempotent Deletepkg/commands/dev_test.go— orchestrator state machine via fakeInterceptor (Probeshape now): immediate healthy, exit-wins-over-health, timeout, half-passing, no-probes-defaults-to-healthy, mid-wait healthy transition, context cancel, mid-wait exit-raceclean🤖 Generated with Claude Code