11import type { AgentEvent } from "@agent-kanban/shared" ;
22import { useCallback , useEffect , useRef , useState } from "react" ;
3- import { getAuthToken } from "../lib/auth-client" ;
3+ import { getAuthToken , refreshAuthToken } from "../lib/auth-client" ;
44
55export type { AgentEvent } ;
66
@@ -30,8 +30,6 @@ export function useSessionRelay({ sessionId, enabled = true }: UseSessionRelayOp
3030
3131 useEffect ( ( ) => {
3232 if ( ! enabled || ! sessionId ) return ;
33- const token = getAuthToken ( ) ;
34- if ( ! token ) return ;
3533
3634 let closed = false ;
3735 historyLoaded . current = false ;
@@ -51,77 +49,92 @@ export function useSessionRelay({ sessionId, enabled = true }: UseSessionRelayOp
5149 } , 5000 ) ;
5250 }
5351
54- const wsUrl = `${ location . origin . replace ( / ^ h t t p / , "ws" ) } /api/tunnel/ws?role=browser&sessionId=${ sessionId } &token=${ encodeURIComponent ( token ) } ` ;
55- const ws = new WebSocket ( wsUrl ) ;
56- wsRef . current = ws ;
52+ let ws : WebSocket | null = null ;
5753
58- ws . onopen = ( ) => {
54+ function connect ( token : string ) {
5955 if ( closed ) return ;
60- setWsConnected ( true ) ;
61- requestHistory ( ws ) ;
62- } ;
56+ const wsUrl = `${ location . origin . replace ( / ^ h t t p / , "ws" ) } /api/tunnel/ws?role=browser&sessionId=${ sessionId } &token=${ encodeURIComponent ( token ) } ` ;
57+ ws = new WebSocket ( wsUrl ) ;
58+ wsRef . current = ws ;
59+
60+ ws . onopen = ( ) => {
61+ if ( closed || ! ws ) return ;
62+ setWsConnected ( true ) ;
63+ requestHistory ( ws ) ;
64+ } ;
65+
66+ ws . onmessage = ( rawEvent ) => {
67+ if ( closed ) return ;
68+ let msg : Record < string , unknown > ;
69+ try {
70+ msg = JSON . parse ( rawEvent . data ) ;
71+ } catch {
72+ return ;
73+ }
6374
64- ws . onmessage = ( rawEvent ) => {
65- if ( closed ) return ;
66- let msg : Record < string , unknown > ;
67- try {
68- msg = JSON . parse ( rawEvent . data ) ;
69- } catch {
70- return ;
71- }
72-
73- switch ( msg . type ) {
74- case "session:history" : {
75- historyLoaded . current = true ;
76- clearTimeout ( historyRetryTimer . current ) ;
77- const history = msg . events as RelayEvent [ ] | undefined ;
78- if ( Array . isArray ( history ) ) {
79- setEvents ( ( prev ) => {
80- const liveEvents = prev . filter ( ( e ) => e . id . startsWith ( "live-" ) ) ;
81- return [ ...history , ...liveEvents ] ;
82- } ) ;
75+ switch ( msg . type ) {
76+ case "session:history" : {
77+ historyLoaded . current = true ;
78+ clearTimeout ( historyRetryTimer . current ) ;
79+ const history = msg . events as RelayEvent [ ] | undefined ;
80+ if ( Array . isArray ( history ) ) {
81+ setEvents ( ( prev ) => {
82+ const liveEvents = prev . filter ( ( e ) => e . id . startsWith ( "live-" ) ) ;
83+ return [ ...history , ...liveEvents ] ;
84+ } ) ;
85+ }
86+ break ;
8387 }
84- break ;
85- }
86- case "agent:event" : {
87- const id = `live-${ ++ idCounter . current } ` ;
88- setEvents ( ( prev ) => [ ...prev , { id, event : msg . event as AgentEvent , timestamp : new Date ( ) . toISOString ( ) } ] ) ;
89- break ;
90- }
91- case "agent:status" : {
92- const status = msg . status as string ;
93- if ( status === "working" || status === "done" || status === "rate_limited" ) {
94- setAgentStatus ( status ) ;
88+ case "agent:event" : {
89+ const id = `live-${ ++ idCounter . current } ` ;
90+ setEvents ( ( prev ) => [ ...prev , { id, event : msg . event as AgentEvent , timestamp : new Date ( ) . toISOString ( ) } ] ) ;
91+ break ;
9592 }
96- break ;
97- }
98- case "daemon:connected" :
99- setDaemonConnected ( true ) ;
100- setAgentStatus ( "idle" ) ;
101- // Re-request history if it was never loaded (e.g. the initial
102- // request was forwarded to a stale daemon socket that never replied).
103- if ( ! historyLoaded . current && ws . readyState === WebSocket . OPEN ) {
104- historyRetries . current = 0 ;
105- requestHistory ( ws ) ;
93+ case "agent:status" : {
94+ const status = msg . status as string ;
95+ if ( status === "working" || status === "done" || status === "rate_limited" ) {
96+ setAgentStatus ( status ) ;
97+ }
98+ break ;
10699 }
107- break ;
108- case "daemon:disconnected" :
109- setDaemonConnected ( false ) ;
110- setAgentStatus ( "idle" ) ;
111- break ;
112- }
113- } ;
100+ case "daemon:connected" :
101+ setDaemonConnected ( true ) ;
102+ setAgentStatus ( "idle" ) ;
103+ // Re-request history if it was never loaded (e.g. the initial
104+ // request was forwarded to a stale daemon socket that never replied).
105+ if ( ! historyLoaded . current && ws ?. readyState === WebSocket . OPEN ) {
106+ historyRetries . current = 0 ;
107+ requestHistory ( ws ) ;
108+ }
109+ break ;
110+ case "daemon:disconnected" :
111+ setDaemonConnected ( false ) ;
112+ setAgentStatus ( "idle" ) ;
113+ break ;
114+ }
115+ } ;
114116
115- ws . onclose = ( ) => {
116- if ( closed ) return ;
117- setWsConnected ( false ) ;
118- setDaemonConnected ( false ) ;
119- } ;
117+ ws . onclose = ( ) => {
118+ if ( closed ) return ;
119+ setWsConnected ( false ) ;
120+ setDaemonConnected ( false ) ;
121+ } ;
122+ }
123+
124+ const token = getAuthToken ( ) ;
125+ if ( token ) {
126+ connect ( token ) ;
127+ void refreshAuthToken ( ) ;
128+ } else {
129+ void refreshAuthToken ( ) . then ( ( freshToken ) => {
130+ if ( freshToken ) connect ( freshToken ) ;
131+ } ) ;
132+ }
120133
121134 return ( ) => {
122135 closed = true ;
123136 clearTimeout ( historyRetryTimer . current ) ;
124- ws . close ( ) ;
137+ ws ? .close ( ) ;
125138 if ( wsRef . current === ws ) wsRef . current = null ;
126139 } ;
127140 } , [ sessionId , enabled ] ) ;
0 commit comments