Skip to content

Commit 69f98f8

Browse files
hyperpolymathclaude
andcommitted
feat(launcher-standard): cross-platform browser launch + env-overridable wait timing
Two related template improvements that the standard had already partly declared but never specified or implemented in the reference templates. The prose template covered only `xdg-open` / `firefox` / `chromium` — Linux-only despite the standard's stated cross-platform commitment (launcher-standard.adoc §System Integration Modes design principle 2). On macOS the template fell through to "Open manually: $URL"; on Windows-via-Git-Bash and on WSL it did too, even though every modern WSL install has `wslview` and Git Bash has `start`. Fix: - `$BROWSER` env var honoured first (de-facto Unix convention; lets users / operators override without re-minting the launcher). - Platform dispatch via `uname -s`: - macOS: `open` - Linux: WSL detection via `/proc/version contains "microsoft"` → `wslview` preferred (opens in Windows-side default browser; native Linux browsers either land in WSLg or fail). Otherwise the existing `xdg-open` → `firefox` → `chromium` chain. - Windows (MINGW/MSYS/CYGWIN under Git Bash): `start` (cmd builtin) - Fallback unchanged: print "Open manually: $URL" a2ml gains `[browser-launch]` declaring the resolution order declaratively so consumers (`launch-scaffolder`, audit tools) can reason about it without re-parsing the bash. The a2ml had `wait-for-url-timeout-seconds = 15` but the bash template hardcoded `wait_for_server 15` — operators on slow hosts had no way to extend the timeout short of editing the script. Also the template had no per-request timeout: a single hung TCP connection could block the whole polling loop. Fix to `[runtime]`: - `wait-for-url-timeout-env-var = "WAIT_FOR_URL_TIMEOUT_SECONDS"` — formalises the override name - `wait-for-url-poll-interval-seconds = 1` + env var (was implicit in the template's `sleep 1`) - `wait-for-url-per-request-timeout-seconds = 2` — caps each curl probe Fix to template: - `local max_wait="${1:-${WAIT_FOR_URL_TIMEOUT_SECONDS:-15}}"` — chain arg → env → default - `curl -fsS --max-time "$per_request_timeout"` — bounded per-request - Error message now tells operators how to extend the timeout Both files in the same commit per the lock-step requirement and the gate in PR #172. Bash blocks pass `bash -n` syntax check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a03e5a3 commit 69f98f8

2 files changed

Lines changed: 86 additions & 19 deletions

File tree

docs/UX-standards/launcher-standard.adoc

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -75,47 +75,89 @@ stop_server() {
7575
[source,bash]
7676
----
7777
wait_for_server() {
78-
local max_wait=$1
78+
# All three values are env-overridable per [runtime] in launcher-standard.a2ml.
79+
local max_wait="${1:-${WAIT_FOR_URL_TIMEOUT_SECONDS:-15}}"
80+
local poll_interval="${WAIT_FOR_URL_POLL_INTERVAL:-1}"
81+
local per_request_timeout=2 # curl --max-time; caps each probe so a
82+
# hung server can't eat the whole budget
7983
local waited=0
80-
81-
while [ $waited -lt $max_wait ]; do
82-
if curl -fsS "$URL" >/dev/null 2>&1; then
84+
85+
while [ "$waited" -lt "$max_wait" ]; do
86+
if curl -fsS --max-time "$per_request_timeout" "$URL" >/dev/null 2>&1; then
8387
return 0 # Success
8488
fi
85-
sleep 1
86-
waited=$((waited + 1))
89+
sleep "$poll_interval"
90+
waited=$((waited + poll_interval))
8791
done
88-
92+
8993
return 1 # Timeout
9094
}
9195
92-
# Usage
93-
if ! wait_for_server 15; then
94-
err "Server did not start within 15 seconds"
96+
# Usage — pass nothing so the env-var/constant chain applies.
97+
if ! wait_for_server; then
98+
err "Server did not start within ${WAIT_FOR_URL_TIMEOUT_SECONDS:-15}s"
9599
err "Check log: $LOG_FILE"
100+
err "Override with WAIT_FOR_URL_TIMEOUT_SECONDS=<N> if the host needs longer"
96101
return 1
97102
fi
98103
----
99104

100105
=== Browser Launching
101106

107+
Resolution order, per `[browser-launch]` in `launcher-standard.a2ml`:
108+
109+
1. `$BROWSER` env var (de-facto Unix convention; user/operator override)
110+
2. Platform-specific ladder, dispatched via `uname -s`:
111+
- **macOS**: `open`
112+
- **Linux**: if WSL (detected via `/proc/version` containing
113+
"microsoft"), prefer `wslview` so the URL opens in the Windows-side
114+
default browser. Otherwise: `xdg-open` → `firefox` → `chromium`.
115+
- **Windows** (Git Bash / MSYS / Cygwin): `start` (cmd builtin)
116+
3. Fallback: print `"Open manually: $URL"` so the user is never silently
117+
left without a URL.
118+
102119
[source,bash]
103120
----
104121
open_browser() {
105122
if ! is_running; then
106123
err "Server is not running"
107124
return 1
108125
fi
109-
110-
if command -v xdg-open >/dev/null 2>&1; then
111-
xdg-open "$URL" &
112-
elif command -v firefox >/dev/null 2>&1; then
113-
firefox "$URL" &
114-
elif command -v chromium >/dev/null 2>&1; then
115-
chromium "$URL" &
116-
else
117-
log "Open manually: $URL"
126+
127+
# 1. $BROWSER override (canonical Unix convention)
128+
if [[ -n "${BROWSER:-}" ]]; then
129+
"$BROWSER" "$URL" &
130+
return $?
118131
fi
132+
133+
# 2. Platform-specific ladder
134+
case "$(uname -s)" in
135+
Darwin*)
136+
open "$URL" &
137+
;;
138+
Linux*)
139+
# WSL detection: /proc/version mentions "microsoft" under WSL1/2
140+
if grep -qi microsoft /proc/version 2>/dev/null && \
141+
command -v wslview >/dev/null 2>&1; then
142+
wslview "$URL" &
143+
elif command -v xdg-open >/dev/null 2>&1; then
144+
xdg-open "$URL" &
145+
elif command -v firefox >/dev/null 2>&1; then
146+
firefox "$URL" &
147+
elif command -v chromium >/dev/null 2>&1; then
148+
chromium "$URL" &
149+
else
150+
log "Open manually: $URL"
151+
fi
152+
;;
153+
MINGW*|MSYS*|CYGWIN*)
154+
# Git Bash / MSYS / Cygwin all carry `start` from cmd
155+
start "$URL" &
156+
;;
157+
*)
158+
log "Open manually: $URL"
159+
;;
160+
esac
119161
}
120162
----
121163

launcher/launcher-standard.a2ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,37 @@ pid-file-pattern = "${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}}/{app-name}-server.pid"
9292
# Per-user, survives reboot, not world-writable. The {app-name} subdir
9393
# isolates each launcher's logs.
9494
log-file-pattern = "${XDG_STATE_HOME:-$HOME/.local/state}/{app-name}/server.log"
95+
# URL-readiness polling after start. The launcher polls the app's URL
96+
# until either it responds or total-timeout-seconds elapses.
97+
# All three timing values are env-var overridable so operators can tune
98+
# without re-minting the launcher.
9599
wait-for-url-timeout-seconds = 15
100+
wait-for-url-timeout-env-var = "WAIT_FOR_URL_TIMEOUT_SECONDS"
101+
wait-for-url-poll-interval-seconds = 1
102+
wait-for-url-poll-interval-env-var = "WAIT_FOR_URL_POLL_INTERVAL"
103+
# Per-request timeout: cap each individual curl probe so a hung server
104+
# does not eat the entire poll budget on a single request.
105+
wait-for-url-per-request-timeout-seconds = 2
96106
startup-command-search = [
97107
"{repo-dir}/scripts/run.sh",
98108
"{repo-dir}/dev.sh",
99109
]
100110

111+
[browser-launch]
112+
# How open_browser() resolves which command to invoke. Implementations
113+
# MUST honour env-var-override first (de-facto $BROWSER convention),
114+
# then fall back to the platform-specific ladder. Platform detected via
115+
# `uname -s`; on Linux, additionally probe /proc/version for "microsoft"
116+
# to detect WSL — under WSL, wslview is preferred so the URL opens in
117+
# the Windows-side default browser (Linux-side browsers either land in
118+
# WSLg or fail).
119+
env-var-override = "BROWSER"
120+
linux = ["wslview", "xdg-open", "firefox", "chromium"]
121+
linux-wsl-detect = "/proc/version contains 'microsoft' (case-insensitive)"
122+
macos = ["open"]
123+
windows = ["start"] # cmd builtin; works under Git Bash / MSYS / Cygwin
124+
fallback = "log 'Open manually: <URL>'"
125+
101126
[error-visibility]
102127
# When the launcher runs in a GUI context (no tty + DISPLAY/WAYLAND_DISPLAY set),
103128
# errors must be visible to the user via a GUI dialog, not only to stderr.

0 commit comments

Comments
 (0)