* feat(cli): support AO_PUBLIC_URL for reverse-proxied dashboards When AO runs inside a remote dev container or behind a reverse proxy (Caddy/nginx/Traefik), `http://localhost:${port}` was hardcoded across the CLI for console output, `ao open` browser launches, and the session URLs surfaced to the orchestrator agent. None of those URLs were reachable from outside the host. Add an `AO_PUBLIC_URL` env var. When set, the new `dashboardUrl(port)` helper returns it (with trailing slashes stripped) instead of the localhost fallback. The helper replaces every user-facing `http://localhost:${port}` literal in: - `commands/dashboard.ts` — startup banner + browser open - `commands/start.ts` — 12 spots: spinner, "Dashboard:" prints, orchestrator URL fallback, `openUrl()` calls, and the running-state reuse paths - `lib/routes.ts` — `projectSessionUrl()` (used in the orchestrator prompt template, so worker links land on the public hostname) Internal IPC (`lib/daemon.ts` calling its own dashboard's `/api/projects/reload`) is intentionally left on localhost — that traffic never leaves the host, and routing it through a public URL would just add latency and a failure surface. Tests cover the env-var/localhost paths, whitespace trimming, trailing-slash stripping, sub-path preservation, and non-default-port URLs (`__tests__/lib/dashboard-url.test.ts`, 10 cases). Setup guide gets a new "Public dashboard URL" entry under optional env vars. * docs: cover TERMINAL_WS_PATH + path-based mux routing in AO_PUBLIC_URL setup The AO_PUBLIC_URL entry only mentioned terminal ports needing to be reachable, which over-specifies what's required when fronting AO with HTTPS through a reverse proxy. The dashboard's MuxProvider already auto-detects standard ports (`loc.port === ""`/`"443"`/`"80"`) and routes the mux WebSocket through `/ao-terminal-mux` on the same hostname, so a single proxy rule pointing at the dashboard port is sufficient — no extra subdomain or port forwarding for the WS. For non-standard ports or custom paths, document the existing but previously-undiscoverable `TERMINAL_WS_PATH` env var (read by `/api/runtime/terminal/route.ts` and threaded through `MuxProvider` as `proxyWsPath`). Adds a minimal Caddy snippet so users have a working starting point. * feat(web): accept /ao-terminal-mux as alias for /mux on direct-terminal-ws The dashboard's MuxProvider already constructs `wss://hostname/ao-terminal-mux` when accessed on a standard HTTPS port (443), but until now nothing on the server side recognized that path — direct-terminal-ws only matched `/mux`, and the Next.js dashboard doesn't handle WS upgrades at all. Deployments fronted by a path-routing reverse proxy (cloudflared, nginx, Caddy, …) hit the server at `/ao-terminal-mux`, fall through to Next.js, get a 404, and the dashboard's terminal panes hang at "Connecting…" forever. Fix is one line in the upgrade-routing allow-list: accept `/ao-terminal-mux` in addition to `/mux`. The proxy can now route the path-based mux URL straight at DIRECT_TERMINAL_PORT without needing a path-rewrite rule (which most proxies — including cloudflared — don't natively support). Existing `/mux` clients continue to work; the alias is strictly additive. SETUP.md's AO_PUBLIC_URL section is updated to mention the path requirement in one sentence, and a new integration test pins the behavior. * feat(web): opt-in single-port mode (AO_PATH_BASED_MUX) for proxy-only deployments Default behavior unchanged. When AO_PATH_BASED_MUX=1, start-all spawns a small bundled HTTP/WS proxy on PORT that demultiplexes: - HTTP requests forwarded to Next.js (shifted to PORT + 1000; override with NEXT_INTERNAL_PORT) - `wss://hostname/ao-terminal-mux` upgrades tunneled to DIRECT_TERMINAL_PORT/mux Use it when the reverse proxy in front of AO can only forward one hostname:port pair upstream (e.g. Cloudflare Tunnel pointed at a single `service:` URL with no path-based ingress, or a managed-app platform where you don't control the proxy config). One proxy rule then suffices — the WS path is multiplexed onto the same TCP port and demuxed inside the AO process. Tradeoff: one extra Node process and one extra hop per HTTP request, in exchange for proxy-config simplicity. For deployments that *can* do path-based routing the alias added in the previous commit (direct-terminal-ws accepting `/ao-terminal-mux` on its own port) is the lower-overhead path. The new server is pure Node http; no `next` import or other extra dependencies. It's strictly opt-in — the env-var gate keeps the code inert by default, so existing deployments see no behavior change and no extra startup cost. * fix(web): correct single-port proxy header handling, WS hangs, shutdown Addresses review feedback on single-port-server.ts: - Strip hop-by-hop headers (RFC 9110 §7.6.1) before forwarding upstream, including any extras named in the client Connection header. Previously the whole header set was copied verbatim, so a client Connection: close could tear down the keep-alive socket to Next.js. - Add X-Forwarded-For/-Proto/-Host so the upstream sees the real client instead of 127.0.0.1; existing values from an outer proxy are preserved. - Handle non-101 upstream responses on the WS upgrade path. The proxy only listened for 'upgrade', so a 404/502/mid-restart response left the client socket hanging until TCP timeout. A 'response' handler now relays the status and closes the connection. - Call server.closeAllConnections() on shutdown. server.close() alone waits for keep-alive HTTP sockets and piped WS tunnels to drain on their own, which they never do, so shutdown always hit the 5s force-exit timer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(web): cover single-port proxy + fix response-direction headers Follow-up to the previous commit's review fixes, adding regression coverage so future changes can't silently break the proxy. - Refactor single-port-server.ts into an exported createSinglePortServer() factory (mirrors direct-terminal-ws.ts) with a thin isMainModule() entrypoint, so start-all.ts still spawns it as a script while tests can drive it in-process against fake upstreams. - Add single-port-server.integration.test.ts (5 tests, no tmux/Next.js needed — runs on CI/Windows): hop-by-hop strip + X-Forwarded-*, 502 on dead upstream, /ao-terminal-mux WS tunnel, non-101 upgrade relay, and prompt shutdown with a live WS connection. - The shutdown test caught that server.closeAllConnections() does NOT destroy sockets already handed off via the 'upgrade' event — track upgraded sockets explicitly and destroy them in shutdown(). - The header test caught the symmetric response-direction leak: the proxy forwarded the upstream's Connection/Keep-Alive to the client, overriding a client that asked for Connection: close. Strip hop-by-hop from upstream responses too via filterResponseHeaders(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Priyanshu Choudhary <57816400+Priyanchew@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| README.md | ||
| activity-events-webhooks-mux.md | ||
| claude-activity-detection-split.md | ||
| claude-activity-edge-cases.md | ||
| claude-activity-hooks-1941.md | ||
| cli-activity-events.md | ||
| config.json | ||
| dashboard-public-url.md | ||
| fix-done-bar-scroll.md | ||
| issue-1660-recovery-metadata-events.md | ||
| launch-orchestrator-clean.md | ||
| linear-transient-retry.md | ||
| notifier-release-links.md | ||
| quiet-sqlite-rebuild.md | ||
| restore-button-pr-merged.md | ||
README.md
Changesets
Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it in our repository
We have a quick list of common questions to get you started engaging with this project in
our documentation