Summary
The office-hours skill generates a wireframe HTML file at /tmp/gstack-sketch-*.html, then tries to render it with:
$B goto "file://$SKETCH_FILE"
$B screenshot /tmp/gstack-sketch.png
This silently fails because the browse binary blocks file:// URLs (added in #17 to prevent SSRF/local resource access). The skill hits the fallback path ("skip the render step") and moves on without telling the user what happened.
Root Cause
validateNavigationUrl() in the browse binary rejects any URL with a file: scheme. This is correct for untrusted external content, but the office-hours skill generates the HTML itself — the file is trusted local content.
The skill's fallback message says:
"Visual sketch requires the browse binary. Run the setup script to enable it."
This is misleading — the browse binary IS set up and working. The issue is the URL scheme, not the binary.
Reproduction
- Run
/office-hours on any project with a UI component
- Let it reach the "Visual Sketch" section where it generates wireframe HTML
- Observe:
$B goto "file:///tmp/gstack-sketch-*.html" exits with code 1 and the error Blocked: scheme "file:" is not allowed
- The skill silently skips the render, screenshot, and "present and iterate" steps
Workaround
Serve the file over HTTP before navigating:
python3 -m http.server 18923 --directory /tmp --bind 127.0.0.1 &
$B goto "http://127.0.0.1:18923/gstack-sketch-1234.html"
$B screenshot /tmp/gstack-sketch.png
kill %1
This works perfectly — the browse binary renders and screenshots the wireframe without issues.
Suggested Fix
In the office-hours skill template (office-hours/SKILL.md.tmpl), replace the direct file:// navigation with a temporary HTTP server pattern:
SKETCH_PORT=$(shuf -i 18000-19000 -n 1)
python3 -m http.server "$SKETCH_PORT" --directory "$(dirname "$SKETCH_FILE")" --bind 127.0.0.1 &
_HTTP_PID=$!
sleep 1
$B goto "http://127.0.0.1:$SKETCH_PORT/$(basename "$SKETCH_FILE")"
$B screenshot /tmp/gstack-sketch.png
kill $_HTTP_PID 2>/dev/null
Alternatively, the browse binary could allowlist file:// URLs that are under /tmp/gstack-* since the skill itself generates them and they're trusted.
Related
Environment
- macOS (aarch64-darwin)
- gstack browse binary built and working (verified with
http:// URLs)
- bun, Google Chrome available via Nix
Summary
The office-hours skill generates a wireframe HTML file at
/tmp/gstack-sketch-*.html, then tries to render it with:This silently fails because the browse binary blocks
file://URLs (added in #17 to prevent SSRF/local resource access). The skill hits the fallback path ("skip the render step") and moves on without telling the user what happened.Root Cause
validateNavigationUrl()in the browse binary rejects any URL with afile:scheme. This is correct for untrusted external content, but the office-hours skill generates the HTML itself — the file is trusted local content.The skill's fallback message says:
This is misleading — the browse binary IS set up and working. The issue is the URL scheme, not the binary.
Reproduction
/office-hourson any project with a UI component$B goto "file:///tmp/gstack-sketch-*.html"exits with code 1 and the errorBlocked: scheme "file:" is not allowedWorkaround
Serve the file over HTTP before navigating:
This works perfectly — the browse binary renders and screenshots the wireframe without issues.
Suggested Fix
In the office-hours skill template (
office-hours/SKILL.md.tmpl), replace the directfile://navigation with a temporary HTTP server pattern:Alternatively, the browse binary could allowlist
file://URLs that are under/tmp/gstack-*since the skill itself generates them and they're trusted.Related
file://blockingEnvironment
http://URLs)