@@ -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"
@@ -768,15 +767,7 @@ func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) {
768767// workspace and validate the sidebar app health.
769768func (api * API )taskSend (rw http.ResponseWriter ,r * http.Request ) {
770769ctx := r .Context ()
771-
772- idStr := chi .URLParam (r ,"id" )
773- taskID ,err := uuid .Parse (idStr )
774- if err != nil {
775- httpapi .Write (ctx ,rw ,http .StatusBadRequest , codersdk.Response {
776- Message :fmt .Sprintf ("Invalid UUID %q for task ID." ,idStr ),
777- })
778- return
779- }
770+ task := httpmw .TaskParam (r )
780771
781772var req codersdk.TaskSendRequest
782773if ! httpapi .Read (ctx ,rw ,r ,& req ) {
@@ -789,7 +780,7 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
789780return
790781}
791782
792- if err = api .authAndDoWithTaskSidebarAppClient (r ,taskID ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
783+ if err : =api .authAndDoWithTaskAppClient (r ,task ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
793784agentAPIClient ,err := aiagentapi .NewClient (appURL .String (),aiagentapi .WithHTTPClient (client ))
794785if err != nil {
795786return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
@@ -848,18 +839,10 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
848839// We enforce ApplicationConnect RBAC on the workspace and validate the sidebar app health.
849840func (api * API )taskLogs (rw http.ResponseWriter ,r * http.Request ) {
850841ctx := r .Context ()
851-
852- idStr := chi .URLParam (r ,"id" )
853- taskID ,err := uuid .Parse (idStr )
854- if err != nil {
855- httpapi .Write (ctx ,rw ,http .StatusBadRequest , codersdk.Response {
856- Message :fmt .Sprintf ("Invalid UUID %q for task ID." ,idStr ),
857- })
858- return
859- }
842+ task := httpmw .TaskParam (r )
860843
861844var out codersdk.TaskLogsResponse
862- if err := api .authAndDoWithTaskSidebarAppClient (r ,taskID ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
845+ if err := api .authAndDoWithTaskAppClient (r ,task ,func (ctx context.Context ,client * http.Client ,appURL * url.URL )error {
863846agentAPIClient ,err := aiagentapi .NewClient (appURL .String (),aiagentapi .WithHTTPClient (client ))
864847if err != nil {
865848return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
@@ -907,7 +890,7 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
907890httpapi .Write (ctx ,rw ,http .StatusOK ,out )
908891}
909892
910- //authAndDoWithTaskSidebarAppClient centralizes the shared logic to:
893+ //authAndDoWithTaskAppClient centralizes the shared logic to:
911894//
912895// - Fetch the task workspace
913896// - Authorize ApplicationConnect on the workspace
@@ -916,15 +899,31 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
916899//
917900// The provided callback receives the context, an HTTP client that dials via the
918901// agent, and the base app URL (as a value URL) to perform any request.
919- func (api * API )authAndDoWithTaskSidebarAppClient (
902+ func (api * API )authAndDoWithTaskAppClient (
920903r * http.Request ,
921- taskID uuid. UUID ,
904+ task database. Task ,
922905do func (ctx context.Context ,client * http.Client ,appURL * url.URL )error ,
923906)error {
924907ctx := r .Context ()
925908
926- workspaceID := taskID
927- workspace ,err := api .Database .GetWorkspaceByID (ctx ,workspaceID )
909+ if task .Status != database .TaskStatusActive {
910+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
911+ Message :"Task status must be active." ,
912+ Detail :fmt .Sprintf ("Task status is %q, it must be %q to interact with the task." ,task .Status ,codersdk .TaskStatusActive ),
913+ })
914+ }
915+ if ! task .WorkspaceID .Valid {
916+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
917+ Message :"Task does not have a workspace." ,
918+ })
919+ }
920+ if ! task .WorkspaceAppID .Valid {
921+ return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
922+ Message :"Task does not have a workspace app." ,
923+ })
924+ }
925+
926+ workspace ,err := api .Database .GetWorkspaceByID (ctx ,task .WorkspaceID .UUID )
928927if err != nil {
929928if httpapi .Is404Error (err ) {
930929return httperror .ErrResourceNotFound
@@ -940,65 +939,30 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
940939return httperror .ErrResourceNotFound
941940}
942941
943- data ,err := api .workspaceData (ctx ,[]database. Workspace { workspace } )
942+ apps ,err := api .Database . GetWorkspaceAppsByAgentID (ctx ,task . WorkspaceAgentID . UUID )
944943if err != nil {
945944return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
946945Message :"Internal error fetching workspace resources." ,
947946Detail :err .Error (),
948947})
949948}
950- if len (data .builds )== 0 || len (data .templates )== 0 {
951- return httperror .ErrResourceNotFound
952- }
953- build := data .builds [0 ]
954- if build .HasAITask == nil || ! * build .HasAITask || build .AITaskSidebarAppID == nil || * build .AITaskSidebarAppID == uuid .Nil {
955- return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
956- Message :"Task is not configured with a sidebar app." ,
957- })
958- }
959949
960- // Find the sidebar app details to get the URL and validate app health.
961- sidebarAppID := * build .AITaskSidebarAppID
962- agentID ,sidebarApp ,ok := func () (uuid.UUID , codersdk.WorkspaceApp ,bool ) {
963- for _ ,res := range build .Resources {
964- for _ ,agent := range res .Agents {
965- for _ ,app := range agent .Apps {
966- if app .ID == sidebarAppID {
967- return agent .ID ,app ,true
968- }
969- }
970- }
950+ var app * database.WorkspaceApp
951+ for _ ,a := range apps {
952+ if a .ID == task .WorkspaceAppID .UUID {
953+ app = & a
954+ break
971955}
972- return uuid .Nil , codersdk.WorkspaceApp {},false
973- }()
974- if ! ok {
975- return httperror .NewResponseError (http .StatusBadRequest , codersdk.Response {
976- Message :"Task sidebar app not found in latest build." ,
977- })
978- }
979-
980- // Return an informative error if the app isn't healthy rather than trying
981- // and failing.
982- switch sidebarApp .Health {
983- case codersdk .WorkspaceAppHealthDisabled :
984- // No health check, pass through.
985- case codersdk .WorkspaceAppHealthInitializing :
986- return httperror .NewResponseError (http .StatusServiceUnavailable , codersdk.Response {
987- Message :"Task sidebar app is initializing. Try again shortly." ,
988- })
989- case codersdk .WorkspaceAppHealthUnhealthy :
990- return httperror .NewResponseError (http .StatusServiceUnavailable , codersdk.Response {
991- Message :"Task sidebar app is unhealthy." ,
992- })
993956}
994957
995958// Build the direct app URL and dial the agent.
996- if sidebarApp .URL == "" {
959+ appURL := app .Url .String
960+ if appURL == "" {
997961return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
998962Message :"Task sidebar app URL is not configured." ,
999963})
1000964}
1001- parsedURL ,err := url .Parse (sidebarApp . URL )
965+ parsedURL ,err := url .Parse (appURL )
1002966if err != nil {
1003967return httperror .NewResponseError (http .StatusInternalServerError , codersdk.Response {
1004968Message :"Internal error parsing task app URL." ,
@@ -1013,7 +977,7 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
1013977
1014978dialCtx ,dialCancel := context .WithTimeout (ctx ,time .Second * 30 )
1015979defer dialCancel ()
1016- agentConn ,release ,err := api .agentProvider .AgentConn (dialCtx ,agentID )
980+ agentConn ,release ,err := api .agentProvider .AgentConn (dialCtx ,task . WorkspaceAgentID . UUID )
1017981if err != nil {
1018982return httperror .NewResponseError (http .StatusBadGateway , codersdk.Response {
1019983Message :"Failed to reach task app endpoint." ,