Skip to content

Commit 1634d0f

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. ## 1. Cross-platform open_browser() 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. ## 2. wait_for_server() reads the env-var override 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 431adbb commit 1634d0f

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
@@ -70,47 +70,89 @@ stop_server() {
7070
[source,bash]
7171
----
7272
wait_for_server() {
73-
local max_wait=$1
73+
# All three values are env-overridable per [runtime] in launcher-standard.a2ml.
74+
local max_wait="${1:-${WAIT_FOR_URL_TIMEOUT_SECONDS:-15}}"
75+
local poll_interval="${WAIT_FOR_URL_POLL_INTERVAL:-1}"
76+
local per_request_timeout=2 # curl --max-time; caps each probe so a
77+
# hung server can't eat the whole budget
7478
local waited=0
75-
76-
while [ $waited -lt $max_wait ]; do
77-
if curl -fsS "$URL" >/dev/null 2>&1; then
79+
80+
while [ "$waited" -lt "$max_wait" ]; do
81+
if curl -fsS --max-time "$per_request_timeout" "$URL" >/dev/null 2>&1; then
7882
return 0 # Success
7983
fi
80-
sleep 1
81-
waited=$((waited + 1))
84+
sleep "$poll_interval"
85+
waited=$((waited + poll_interval))
8286
done
83-
87+
8488
return 1 # Timeout
8589
}
8690
87-
# Usage
88-
if ! wait_for_server 15; then
89-
err "Server did not start within 15 seconds"
91+
# Usage — pass nothing so the env-var/constant chain applies.
92+
if ! wait_for_server; then
93+
err "Server did not start within ${WAIT_FOR_URL_TIMEOUT_SECONDS:-15}s"
9094
err "Check log: $LOG_FILE"
95+
err "Override with WAIT_FOR_URL_TIMEOUT_SECONDS=<N> if the host needs longer"
9196
return 1
9297
fi
9398
----
9499

95100
=== Browser Launching
96101

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

launcher/launcher-standard.a2ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,37 @@ mode = "--auto"
6161
background = "nohup"
6262
pid-file-pattern = "/tmp/{app-name}-server.pid"
6363
log-file-pattern = "/tmp/{app-name}-server.log"
64+
# URL-readiness polling after start. The launcher polls the app's URL
65+
# until either it responds or total-timeout-seconds elapses.
66+
# All three timing values are env-var overridable so operators can tune
67+
# without re-minting the launcher.
6468
wait-for-url-timeout-seconds = 15
69+
wait-for-url-timeout-env-var = "WAIT_FOR_URL_TIMEOUT_SECONDS"
70+
wait-for-url-poll-interval-seconds = 1
71+
wait-for-url-poll-interval-env-var = "WAIT_FOR_URL_POLL_INTERVAL"
72+
# Per-request timeout: cap each individual curl probe so a hung server
73+
# does not eat the entire poll budget on a single request.
74+
wait-for-url-per-request-timeout-seconds = 2
6575
startup-command-search = [
6676
"{repo-dir}/scripts/run.sh",
6777
"{repo-dir}/dev.sh",
6878
]
6979

80+
[browser-launch]
81+
# How open_browser() resolves which command to invoke. Implementations
82+
# MUST honour env-var-override first (de-facto $BROWSER convention),
83+
# then fall back to the platform-specific ladder. Platform detected via
84+
# `uname -s`; on Linux, additionally probe /proc/version for "microsoft"
85+
# to detect WSL — under WSL, wslview is preferred so the URL opens in
86+
# the Windows-side default browser (Linux-side browsers either land in
87+
# WSLg or fail).
88+
env-var-override = "BROWSER"
89+
linux = ["wslview", "xdg-open", "firefox", "chromium"]
90+
linux-wsl-detect = "/proc/version contains 'microsoft' (case-insensitive)"
91+
macos = ["open"]
92+
windows = ["start"] # cmd builtin; works under Git Bash / MSYS / Cygwin
93+
fallback = "log 'Open manually: <URL>'"
94+
7095
[error-visibility]
7196
# When the launcher runs in a GUI context (no tty + DISPLAY/WAYLAND_DISPLAY set),
7297
# errors must be visible to the user via a GUI dialog, not only to stderr.

0 commit comments

Comments
 (0)