fix(dashboard): respect HERMES_BASE_PATH in WebSocket URLs (#25547)

When the dashboard is reverse-proxied under a path prefix
(`X-Forwarded-Prefix: /dashboard`), the SPA already routes its
`/api/...` REST traffic through `HERMES_BASE_PATH` via
`web/src/lib/api.ts`. Three WebSocket URLs constructed elsewhere
were still hardcoded to root `/api/...` and so opened
`wss://host/api/...` instead of `wss://host/dashboard/api/...`,
forcing operators to forward selected root API/WS paths through the
reverse proxy as a workaround (see issue #25547).

Add `HERMES_BASE_PATH` between `host` and `/api/...` in the
three constructed WebSocket URLs:

- `web/src/pages/ChatPage.tsx` — PTY WebSocket
- `web/src/components/ChatSidebar.tsx` — events subscriber
- `web/src/lib/gatewayClient.ts` — JSON-RPC gateway WebSocket

When the dashboard is served at root, `HERMES_BASE_PATH === """
and the URLs are bit-for-bit identical to before. Under a prefix,
the WebSocket connections now go through the same proxy path the
REST calls already use.

Note: bundled dashboard plugins (kanban, hermes-achievements) embed
`"/api/plugins/..."` in their compiled `dist/index.js` and
remain out of scope here — those need source-side fixes per plugin.

Fixes #25547.
This commit is contained in:
wesleysimplicio 2026-05-17 11:36:29 -07:00 committed by Teknium
parent 714b3b2bd8
commit 74031e1e2a
3 changed files with 7 additions and 3 deletions

View file

@ -13,6 +13,8 @@
* await gw.request("prompt.submit", { session_id, text: "hi" })
*/
import { HERMES_BASE_PATH } from "@/lib/api";
export type GatewayEventName =
| "gateway.ready"
| "session.info"
@ -117,7 +119,7 @@ export class GatewayClient {
const scheme = location.protocol === "https:" ? "wss:" : "ws:";
const ws = new WebSocket(
`${scheme}//${location.host}/api/ws?token=${encodeURIComponent(resolved)}`,
`${scheme}//${location.host}${HERMES_BASE_PATH}/api/ws?token=${encodeURIComponent(resolved)}`,
);
this.ws = ws;