Tracked deferral from up --auto-forward (spec 015-auto-forward-ports, task T059; shipped in #188). v1 honors onAutoForward values ignore/silent/notify; openBrowser/openPreview are accepted but treated as notify.
Goal / acceptance
portsAttributes.onAutoForward: openBrowser (and openPreview) trigger opening the forwarded http://127.0.0.1:<host_port> URL on the host when the port becomes active.
Background
to_resolved() in crates/core/src/port_forward/daemon.rs currently collapses OpenBrowser/OpenPreview → Notify. The forward still happens and is reported; nothing is auto-opened.
Hidden issues / gotchas
- The forwarder is a detached, headless background process (stdio redirected to a log;
setsid). It has no display/session context, so naively shelling xdg-open/open from the daemon may target the wrong session, fail silently, or do nothing on a headless server / SSH host. This is the core reason it's awkward.
- "openPreview" has no meaning outside an IDE. It's a VS Code in-editor webview concept. A CLI can at best treat it like
openBrowser; true preview belongs to an editor integration, not deacon.
- Cross-platform divergence:
xdg-open (Linux), open (macOS), start/rundll32 (Windows) — and none of them are right on a remote/headless host.
- Risk in CI/automation: must be a no-op (not an error) when there's no usable browser/display (
$DISPLAY unset, $SSH_CONNECTION set, $BROWSER empty, CI), and must never block or fail up/the daemon. Hard to assert meaningfully in hermetic tests beyond "doesn't crash / no-ops headlessly."
- Should likely respect the
$BROWSER env var and be suppressible.
Recommended path forward
- Implement
openBrowser (map openPreview → same, with a doc note that true preview is editor-only): on first activation of such a port, best-effort launch the host browser via $BROWSER else platform default (xdg-open/open/start).
- Guard hard: skip silently when headless/remote/CI (
!$DISPLAY && !$WAYLAND_DISPLAY on Linux, $SSH_CONNECTION set, CI set) or no opener found — log at debug, never error.
- Add a kill-switch (e.g.
--no-open / honor $BROWSER="").
- Tests: unit-test the decision logic (which value → open vs notify, headless → no-op); integration just asserts the forward still works and nothing blocks.
Notes
Low effort, low risk if the headless guard is strict. Lowest-value of the set on a server/SSH workflow (where you'd open the SSH-tunneled URL on your laptop yourself).
Tracked deferral from
up --auto-forward(spec015-auto-forward-ports, task T059; shipped in #188). v1 honorsonAutoForwardvaluesignore/silent/notify;openBrowser/openPrevieware accepted but treated asnotify.Goal / acceptance
portsAttributes.onAutoForward: openBrowser(andopenPreview) trigger opening the forwardedhttp://127.0.0.1:<host_port>URL on the host when the port becomes active.Background
to_resolved()incrates/core/src/port_forward/daemon.rscurrently collapsesOpenBrowser/OpenPreview→Notify. The forward still happens and is reported; nothing is auto-opened.Hidden issues / gotchas
setsid). It has no display/session context, so naively shellingxdg-open/openfrom the daemon may target the wrong session, fail silently, or do nothing on a headless server / SSH host. This is the core reason it's awkward.openBrowser; true preview belongs to an editor integration, not deacon.xdg-open(Linux),open(macOS),start/rundll32(Windows) — and none of them are right on a remote/headless host.$DISPLAYunset,$SSH_CONNECTIONset,$BROWSERempty, CI), and must never block or failup/the daemon. Hard to assert meaningfully in hermetic tests beyond "doesn't crash / no-ops headlessly."$BROWSERenv var and be suppressible.Recommended path forward
openBrowser(mapopenPreview→ same, with a doc note that true preview is editor-only): on first activation of such a port, best-effort launch the host browser via$BROWSERelse platform default (xdg-open/open/start).!$DISPLAY && !$WAYLAND_DISPLAYon Linux,$SSH_CONNECTIONset,CIset) or no opener found — log at debug, never error.--no-open/ honor$BROWSER="").Notes
Low effort, low risk if the headless guard is strict. Lowest-value of the set on a server/SSH workflow (where you'd open the SSH-tunneled URL on your laptop yourself).