66 "fmt"
77 "log/slog"
88 "net/http"
9+ "net/url"
10+ "strings"
911 "sync"
1012 "time"
1113
@@ -33,6 +35,7 @@ type Server struct {
3335 agentio * termexec.Process
3436 agentType mf.AgentType
3537 emitter * EventEmitter
38+ chatBasePath string
3639}
3740
3841func (s * Server ) GetOpenAPI () string {
@@ -95,14 +98,20 @@ func NewServer(ctx context.Context, agentType mf.AgentType, process *termexec.Pr
9598 agentio : process ,
9699 agentType : agentType ,
97100 emitter : emitter ,
101+ chatBasePath : strings .TrimSuffix (chatBasePath , "/" ),
98102 }
99103
100104 // Register API routes
101- s .registerRoutes (chatBasePath )
105+ s .registerRoutes ()
102106
103107 return s
104108}
105109
110+ // Handler returns the underlying chi.Router for testing purposes.
111+ func (s * Server ) Handler () http.Handler {
112+ return s .router
113+ }
114+
106115func (s * Server ) StartSnapshotLoop (ctx context.Context ) {
107116 s .conversation .StartSnapshotLoop (ctx )
108117 go func () {
@@ -116,7 +125,7 @@ func (s *Server) StartSnapshotLoop(ctx context.Context) {
116125}
117126
118127// registerRoutes sets up all API endpoints
119- func (s * Server ) registerRoutes (chatBasePath string ) {
128+ func (s * Server ) registerRoutes () {
120129 // GET /status endpoint
121130 huma .Get (s .api , "/status" , s .getStatus , func (o * huma.Operation ) {
122131 o .Description = "Returns the current status of the agent."
@@ -158,7 +167,7 @@ func (s *Server) registerRoutes(chatBasePath string) {
158167 s .router .Handle ("/" , http .HandlerFunc (s .redirectToChat ))
159168
160169 // Serve static files for the chat interface under /chat
161- s .registerStaticFileRoutes (chatBasePath )
170+ s .registerStaticFileRoutes ()
162171}
163172
164173// getStatus handles GET /status
@@ -305,14 +314,20 @@ func (s *Server) Stop(ctx context.Context) error {
305314}
306315
307316// registerStaticFileRoutes sets up routes for serving static files
308- func (s * Server ) registerStaticFileRoutes (chatBasePath string ) {
309- chatHandler := FileServerWithIndexFallback (chatBasePath )
317+ func (s * Server ) registerStaticFileRoutes () {
318+ chatHandler := FileServerWithIndexFallback (s . chatBasePath )
310319
311320 // Mount the file server at /chat
312321 s .router .Handle ("/chat" , http .StripPrefix ("/chat" , chatHandler ))
313322 s .router .Handle ("/chat/*" , http .StripPrefix ("/chat" , chatHandler ))
314323}
315324
316325func (s * Server ) redirectToChat (w http.ResponseWriter , r * http.Request ) {
317- http .Redirect (w , r , "/chat/embed" , http .StatusTemporaryRedirect )
326+ rdir , err := url .JoinPath (s .chatBasePath , "embed" )
327+ if err != nil {
328+ s .logger .Error ("Failed to construct redirect URL" , "error" , err )
329+ http .Error (w , "Failed to redirect" , http .StatusInternalServerError )
330+ return
331+ }
332+ http .Redirect (w , r , rdir , http .StatusTemporaryRedirect )
318333}
0 commit comments