From 90cf4f0a91ab389ce813438001d62705cc74c82a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 18 Mar 2026 14:04:42 -0400 Subject: [PATCH] refactor: consolidate chat streaming endpoints under /stream (#23248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves per-chat streaming/watch endpoints under a `/stream` sub-route for better API consistency: | Before | After | |--------|-------| | `GET /{chat}/stream` | `GET /{chat}/stream/` | | `GET /{chat}/desktop` | `GET /{chat}/stream/desktop` | | `GET /{chat}/git/watch` | `GET /{chat}/stream/git` | ### Changes - **`coderd/coderd.go`** — Route definitions: replaced flat routes with `r.Route("/stream", ...)` sub-router - **`site/src/api/api.ts`** — Updated WebSocket URLs for `watchChatGit` and `watchChatDesktop` - **`coderd/chats_test.go`** — Updated desktop test URL - **`coderd/workspaceagents_internal_test.go`** — Updated git watcher test URLs (route mounts + dial URLs) - **`site/src/pages/AgentsPage/AgentDetail.stories.tsx`** — Updated storybook WebSocket mock paths --- coderd/chats_test.go | 2 +- coderd/coderd.go | 8 +++++--- coderd/workspaceagents_internal_test.go | 20 +++++++++---------- site/src/api/api.ts | 6 ++++-- .../pages/AgentsPage/AgentDetail.stories.tsx | 4 ++-- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/coderd/chats_test.go b/coderd/chats_test.go index 7148c43682..f056d5fbf4 100644 --- a/coderd/chats_test.go +++ b/coderd/chats_test.go @@ -4538,7 +4538,7 @@ func TestWatchChatDesktop(t *testing.T) { res, err := client.Request( ctx, http.MethodGet, - fmt.Sprintf("/api/experimental/chats/%s/desktop", createdChat.ID), + fmt.Sprintf("/api/experimental/chats/%s/stream/desktop", createdChat.ID), nil, ) require.NoError(t, err) diff --git a/coderd/coderd.go b/coderd/coderd.go index 9fd3e3850c..15984d7170 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1209,13 +1209,15 @@ func New(options *Options) *API { r.Route("/{chat}", func(r chi.Router) { r.Use(httpmw.ExtractChatParam(options.Database)) r.Get("/", api.getChat) - r.Get("/git/watch", api.watchChatGit) - r.Get("/desktop", api.watchChatDesktop) r.Patch("/", api.patchChat) r.Get("/messages", api.getChatMessages) r.Post("/messages", api.postChatMessages) r.Patch("/messages/{message}", api.patchChatMessage) - r.Get("/stream", api.streamChat) + r.Route("/stream", func(r chi.Router) { + r.Get("/", api.streamChat) + r.Get("/desktop", api.watchChatDesktop) + r.Get("/git", api.watchChatGit) + }) r.Post("/interrupt", api.interruptChat) r.Get("/diff", api.getChatDiffContents) r.Route("/queue/{queuedMessage}", func(r chi.Router) { diff --git a/coderd/workspaceagents_internal_test.go b/coderd/workspaceagents_internal_test.go index 0267f15283..a2203cdf6d 100644 --- a/coderd/workspaceagents_internal_test.go +++ b/coderd/workspaceagents_internal_test.go @@ -109,7 +109,7 @@ func TestWatchChatGit(t *testing.T) { // And: We mount the HTTP handler. r.With(httpmw.ExtractChatParam(mDB)). - Get("/chats/{chat}/git/watch", api.watchChatGit) + Get("/chats/{chat}/stream/git", api.watchChatGit) // Given: We create the HTTP server. srv := httptest.NewServer(r) @@ -117,7 +117,7 @@ func TestWatchChatGit(t *testing.T) { // When: We make a request. req, err := http.NewRequestWithContext(ctx, http.MethodGet, - fmt.Sprintf("%s/chats/%s/git/watch", srv.URL, chatID), nil) + fmt.Sprintf("%s/chats/%s/stream/git", srv.URL, chatID), nil) require.NoError(t, err) resp, err := http.DefaultClient.Do(req) @@ -165,7 +165,7 @@ func TestWatchChatGit(t *testing.T) { // And: We mount the HTTP handler. r.With(httpmw.ExtractChatParam(mDB)). - Get("/chats/{chat}/git/watch", api.watchChatGit) + Get("/chats/{chat}/stream/git", api.watchChatGit) // Given: We create the HTTP server. srv := httptest.NewServer(r) @@ -173,7 +173,7 @@ func TestWatchChatGit(t *testing.T) { // When: We make a request. req, err := http.NewRequestWithContext(ctx, http.MethodGet, - fmt.Sprintf("%s/chats/%s/git/watch", srv.URL, chatID), nil) + fmt.Sprintf("%s/chats/%s/stream/git", srv.URL, chatID), nil) require.NoError(t, err) resp, err := http.DefaultClient.Do(req) @@ -242,7 +242,7 @@ func TestWatchChatGit(t *testing.T) { // And: We mount the HTTP handler. r.With(httpmw.ExtractChatParam(mDB)). - Get("/chats/{chat}/git/watch", api.watchChatGit) + Get("/chats/{chat}/stream/git", api.watchChatGit) // Given: We create the HTTP server. srv := httptest.NewServer(r) @@ -250,7 +250,7 @@ func TestWatchChatGit(t *testing.T) { // When: We make a request. req, err := http.NewRequestWithContext(ctx, http.MethodGet, - fmt.Sprintf("%s/chats/%s/git/watch", srv.URL, chatID), nil) + fmt.Sprintf("%s/chats/%s/stream/git", srv.URL, chatID), nil) require.NoError(t, err) resp, err := http.DefaultClient.Do(req) @@ -376,7 +376,7 @@ func TestWatchChatGit(t *testing.T) { }) // And: We mount the HTTP handler. r.With(httpmw.ExtractChatParam(mDB)). - Get("/chats/{chat}/git/watch", api.watchChatGit) + Get("/chats/{chat}/stream/git", api.watchChatGit) // Given: We create the HTTP server. coderdSrv := httptest.NewServer(r) @@ -384,7 +384,7 @@ func TestWatchChatGit(t *testing.T) { // And: Dial the WebSocket as a client. wsURL := strings.Replace(coderdSrv.URL, "http://", "ws://", 1) - clientConn, resp, err := websocket.Dial(ctx, fmt.Sprintf("%s/chats/%s/git/watch", wsURL, chatID), nil) + clientConn, resp, err := websocket.Dial(ctx, fmt.Sprintf("%s/chats/%s/stream/git", wsURL, chatID), nil) require.NoError(t, err) if resp.Body != nil { defer resp.Body.Close() @@ -538,7 +538,7 @@ func TestWatchChatGit(t *testing.T) { }) // And: We mount the HTTP handler. r.With(httpmw.ExtractChatParam(mDB)). - Get("/chats/{chat}/git/watch", api.watchChatGit) + Get("/chats/{chat}/stream/git", api.watchChatGit) // Given: We create the HTTP server. coderdSrv := httptest.NewServer(r) @@ -546,7 +546,7 @@ func TestWatchChatGit(t *testing.T) { // And: Dial the WebSocket as a client. wsURL := strings.Replace(coderdSrv.URL, "http://", "ws://", 1) - clientConn, resp, err := websocket.Dial(ctx, fmt.Sprintf("%s/chats/%s/git/watch", wsURL, chatID), nil) + clientConn, resp, err := websocket.Dial(ctx, fmt.Sprintf("%s/chats/%s/stream/git", wsURL, chatID), nil) require.NoError(t, err) if resp.Body != nil { defer resp.Body.Close() diff --git a/site/src/api/api.ts b/site/src/api/api.ts index e933416f5d..feba6c2224 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -160,11 +160,13 @@ export const watchChats = (): OneWayWebSocket => { }; export const watchChatGit = (chatId: string): WebSocket => { - return createWebSocket(`/api/experimental/chats/${chatId}/git/watch`); + return createWebSocket(`/api/experimental/chats/${chatId}/stream/git`); }; export const watchChatDesktop = (chatId: string): WebSocket => { - const socket = createWebSocket(`/api/experimental/chats/${chatId}/desktop`); + const socket = createWebSocket( + `/api/experimental/chats/${chatId}/stream/desktop`, + ); // RFB is a binary protocol — noVNC expects arraybuffer, not blob. socket.binaryType = "arraybuffer"; return socket; diff --git a/site/src/pages/AgentsPage/AgentDetail.stories.tsx b/site/src/pages/AgentsPage/AgentDetail.stories.tsx index 1147c9bfb6..2a556eb0b1 100644 --- a/site/src/pages/AgentsPage/AgentDetail.stories.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.stories.tsx @@ -764,7 +764,7 @@ export const SidebarWithPRAndRepos: Story = { { diffUrl: "https://github.com/coder/coder/pull/456" }, ), webSocket: { - "/git/watch": [ + "/stream/git": [ { event: "message", data: JSON.stringify({ @@ -945,7 +945,7 @@ export const SidebarWithSingleRepo: Story = { { diffUrl: undefined }, ), webSocket: { - "/git/watch": [ + "/stream/git": [ { event: "message", data: JSON.stringify({