From e80c1f6625edff83684c53f05fd8c8b7f1cda7f7 Mon Sep 17 00:00:00 2001 From: Menghuan1918 Date: Sat, 8 Aug 2026 15:23:01 +0800 Subject: [PATCH] terminal: build the ws URL like the app's own downlinks; show the attempted URL on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The served bundle's wsUrl is provably correct, yet one report showed ws://host/ws//host/sidebar/ws/terminal (a /ws/ prefix no version ever emitted). Mirror the app's own downlink construction (new URL(path, location.origin) + protocol swap — the construction the chat's working event streams use), and surface the exact attempted URL in the failure banner plus a console.error, so a recurrence identifies the source (stale page vs environment rewrite) immediately. --- src/client/TerminalView.tsx | 16 +++++++++++++--- src/client/sidebar.module.css | 9 +++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/client/TerminalView.tsx b/src/client/TerminalView.tsx index 9dae45c..ea24f03 100644 --- a/src/client/TerminalView.tsx +++ b/src/client/TerminalView.tsx @@ -24,6 +24,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) { const hostRef = useRef(null) const [connected, setConnected] = useState(false) const [fatal, setFatal] = useState(null) + const [lastUrl, setLastUrl] = useState(null) const connectRef = useRef<(() => void) | null>(null) useEffect(() => { @@ -48,10 +49,15 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) { let failures = 0 const wsUrl = (): string => { - const proto = location.protocol === 'https:' ? 'wss' : 'ws' const params = new URLSearchParams({ sessionId: scope.sessionId, tab: tabId }) if (scope.cwd !== undefined && scope.cwd !== '') params.set('cwd', scope.cwd) - return `${proto}//${location.host}/sidebar/ws/terminal?${params.toString()}` + // Same construction the app's own downlink WebSockets use (new URL + // over location.origin + protocol swap): whatever the environment + // does to the app's websockets applies identically here. + const url = new URL('/sidebar/ws/terminal', location.origin) + url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:' + url.search = params.toString() + return url.toString() } const sendResize = (): void => { @@ -62,7 +68,9 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) { const connect = (): void => { if (closed) return - socket = new WebSocket(wsUrl()) + const url = wsUrl() + setLastUrl(url) + socket = new WebSocket(url) socket.onopen = () => { failures = 0 setConnected(true) @@ -86,6 +94,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) { failures += 1 if (failures >= FAILURE_LIMIT) { const detail = event.reason !== '' ? ` (${event.code}: ${event.reason})` : ` (${event.code})` + console.error('[dsh-better-sidebar] terminal connection failed:', event.code, event.reason, url) setFatal(`${t('terminalConnectFailed')}${detail}`) return } @@ -133,6 +142,7 @@ export function TerminalView(props: { scope: SessionScope; tabId: string }) { {fatal !== null && (
{t('terminalError')}: {fatal} + {lastUrl !== null &&
{lastUrl}
}