@@ -11,7 +11,6 @@ import (
1111"strings"
1212"time"
1313
14- "github.com/go-chi/chi/v5"
1514"github.com/google/uuid"
1615
1716"cdr.dev/slog"
@@ -770,15 +769,7 @@ func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) {
770769// workspace and validate the sidebar app health.
771770func (api * API )taskSend (rw http.ResponseWriter ,r * http.Request ) {
772771ctx := r .Context ()
773-
774- idStr := chi .URLParam (r ,"id" )
775- taskID ,err := uuid .Parse (idStr )
776- if err != nil {
777- httpapi .Write (ctx ,rw ,http .StatusBadRequest , codersdk.Response {
778- Message :fmt .Sprintf ("Invalid UUID %q for task ID." ,idStr ),
779- })
780- return
781- }
772+ task := httpmw .TaskParam (r )
782773
783774var req codersdk.TaskSendRequest
784775if ! httpapi .Read (ctx ,rw ,r ,& req ) {
@@ -791,7 +782,7 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
791782return
792783}
793784
794- if err = api .authAndDoWithTaskSidebarAppClient (r ,taskID ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
785+ if err : =api .authAndDoWithTaskAppClient (r ,task ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
795786agentAPIClient ,err := aiagentapi .NewClient (appURL .String (),aiagentapi .WithHTTPClient (client ))
796787if err != nil {
797788return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
@@ -850,18 +841,10 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
850841// We enforce ApplicationConnect RBAC on the workspace and validate the sidebar app health.
851842func (api * API )taskLogs (rw http.ResponseWriter ,r * http.Request ) {
852843ctx := r .Context ()
853-
854- idStr := chi .URLParam (r ,"id" )
855- taskID ,err := uuid .Parse (idStr )
856- if err != nil {
857- httpapi .Write (ctx ,rw ,http .StatusBadRequest , codersdk.Response {
858- Message :fmt .Sprintf ("Invalid UUID %q for task ID." ,idStr ),
859- })
860- return
861- }
844+ task := httpmw .TaskParam (r )
862845
863846var out codersdk.TaskLogsResponse
864- if err := api .authAndDoWithTaskSidebarAppClient (r ,taskID ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
847+ if err := api .authAndDoWithTaskAppClient (r ,task ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
865848agentAPIClient ,err := aiagentapi .NewClient (appURL .String (),aiagentapi .WithHTTPClient (client ))
866849if err != nil {
867850return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
@@ -909,7 +892,7 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
909892httpapi .Write (ctx ,rw ,http .StatusOK ,out )
910893}
911894
912- //authAndDoWithTaskSidebarAppClient centralizes the shared logic to:
895+ //authAndDoWithTaskAppClient centralizes the shared logic to:
913896//
914897// - Fetch the task workspace
915898// - Authorize ApplicationConnect on the workspace
@@ -918,15 +901,31 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
918901//
919902// The provided callback receives the context, an HTTP client that dials via the
920903// agent, and the base app URL (as a value URL) to perform any request.
921- func (api * API )authAndDoWithTaskSidebarAppClient (
904+ func (api * API )authAndDoWithTaskAppClient (
922905r * http.Request ,
923- taskID uuid. UUID ,
906+ task database. Task ,
924907do func (ctx context.Context ,client * http.Client ,appURL * url.URL )error ,
925908)error {
926909ctx := r .Context ()
927910
928- workspaceID := taskID
929- workspace ,err := api .Database .GetWorkspaceByID (ctx ,workspaceID )
911+ if task .Status != database .TaskStatusActive {
912+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
913+ Message :"Task status must be active." ,
914+ Detail :fmt .Sprintf ("Task status is %q, it must be %q to interact with the task." ,task .Status ,codersdk .TaskStatusActive ),
915+ })
916+ }
917+ if ! task .WorkspaceID .Valid {
918+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
919+ Message :"Task does not have a workspace." ,
920+ })
921+ }
922+ if ! task .WorkspaceAppID .Valid {
923+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
924+ Message :"Task does not have a workspace app." ,
925+ })
926+ }
927+
928+ workspace ,err := api .Database .GetWorkspaceByID (ctx ,task .WorkspaceID .UUID )
930929if err != nil {
931930if httpapi .Is404Error (err ) {
932931return httperror .ErrResourceNotFound
@@ -942,65 +941,30 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
942941return httperror .ErrResourceNotFound
943942}
944943
945- data ,err := api .workspaceData (ctx ,[]database. Workspace { workspace } )
944+ apps ,err := api .Database . GetWorkspaceAppsByAgentID (ctx ,task . WorkspaceAgentID . UUID )
946945if err != nil {
947946return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
948947Message :"Internal error fetching workspace resources." ,
949948Detail :err .Error (),
950949})
951950}
952- if len (data .builds )== 0 || len (data .templates )== 0 {
953- return httperror .ErrResourceNotFound
954- }
955- build := data .builds [0 ]
956- if build .HasAITask == nil || ! * build .HasAITask || build .AITaskSidebarAppID == nil || * build .AITaskSidebarAppID == uuid .Nil {
957- return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
958- Message :"Task is not configured with a sidebar app." ,
959- })
960- }
961951
962- // Find the sidebar app details to get the URL and validate app health.
963- sidebarAppID := * build .AITaskSidebarAppID
964- agentID ,sidebarApp ,ok := func () (uuid.UUID , codersdk.WorkspaceApp ,bool ) {
965- for _ ,res := range build .Resources {
966- for _ ,agent := range res .Agents {
967- for _ ,app := range agent .Apps {
968- if app .ID == sidebarAppID {
969- return agent .ID ,app ,true
970- }
971- }
972- }
952+ var app * database.WorkspaceApp
953+ for _ ,a := range apps {
954+ if a .ID == task .WorkspaceAppID .UUID {
955+ app = & a
956+ break
973957}
974- return uuid .Nil , codersdk.WorkspaceApp {},false
975- }()
976- if ! ok {
977- return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
978- Message :"Task sidebar app not found in latest build." ,
979- })
980- }
981-
982- // Return an informative error if the app isn't healthy rather than trying
983- // and failing.
984- switch sidebarApp .Health {
985- case codersdk .WorkspaceAppHealthDisabled :
986- // No health check, pass through.
987- case codersdk .WorkspaceAppHealthInitializing :
988- return httperror .NewResponseError (http .StatusServiceUnavailable , codersdk.Response {
989- Message :"Task sidebar app is initializing. Try again shortly." ,
990- })
991- case codersdk .WorkspaceAppHealthUnhealthy :
992- return httperror .NewResponseError (http .StatusServiceUnavailable , codersdk.Response {
993- Message :"Task sidebar app is unhealthy." ,
994- })
995958}
996959
997960// Build the direct app URL and dial the agent.
998- if sidebarApp .URL == "" {
961+ appURL := app .Url .String
962+ if appURL == "" {
999963return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
1000964Message :"Task sidebar app URL is not configured." ,
1001965})
1002966}
1003- parsedURL ,err := url .Parse (sidebarApp . URL )
967+ parsedURL ,err := url .Parse (appURL )
1004968if err != nil {
1005969return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
1006970Message :"Internal error parsing task app URL." ,
@@ -1015,7 +979,7 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
1015979
1016980dialCtx ,dialCancel := context .WithTimeout (ctx ,time .Second * 30 )
1017981defer dialCancel ()
1018- agentConn ,release ,err := api .agentProvider .AgentConn (dialCtx ,agentID )
982+ agentConn ,release ,err := api .agentProvider .AgentConn (dialCtx ,task . WorkspaceAgentID . UUID )
1019983if err != nil {
1020984return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
1021985Message :"Failed to reach task app endpoint." ,