mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
feat: add app status tracking to the backend (#17163)
This does ~95% of the backend work required to integrate the AI work. Most left to integrate from the tasks branch is just frontend, which will be a lot smaller I believe. The real difference between this branch and that one is the abstraction -- this now attaches statuses to apps, and returns the latest status reported as part of a workspace. This change enables us to have a similar UX to in the tasks branch, but for agents other than Claude Code as well. Any app can report status now.
This commit is contained in:
@ -259,6 +259,7 @@ type data struct {
|
||||
workspaceAgentVolumeResourceMonitors []database.WorkspaceAgentVolumeResourceMonitor
|
||||
workspaceAgentDevcontainers []database.WorkspaceAgentDevcontainer
|
||||
workspaceApps []database.WorkspaceApp
|
||||
workspaceAppStatuses []database.WorkspaceAppStatus
|
||||
workspaceAppAuditSessions []database.WorkspaceAppAuditSession
|
||||
workspaceAppStatsLastInsertID int64
|
||||
workspaceAppStats []database.WorkspaceAppStat
|
||||
@ -3697,6 +3698,34 @@ func (q *FakeQuerier) GetLatestCryptoKeyByFeature(_ context.Context, feature dat
|
||||
return latestKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
// Map to track latest status per workspace ID
|
||||
latestByWorkspace := make(map[uuid.UUID]database.WorkspaceAppStatus)
|
||||
|
||||
// Find latest status for each workspace ID
|
||||
for _, appStatus := range q.workspaceAppStatuses {
|
||||
if !slices.Contains(ids, appStatus.WorkspaceID) {
|
||||
continue
|
||||
}
|
||||
|
||||
current, exists := latestByWorkspace[appStatus.WorkspaceID]
|
||||
if !exists || appStatus.CreatedAt.After(current.CreatedAt) {
|
||||
latestByWorkspace[appStatus.WorkspaceID] = appStatus
|
||||
}
|
||||
}
|
||||
|
||||
// Convert map to slice
|
||||
appStatuses := make([]database.WorkspaceAppStatus, 0, len(latestByWorkspace))
|
||||
for _, status := range latestByWorkspace {
|
||||
appStatuses = append(appStatuses, status)
|
||||
}
|
||||
|
||||
return appStatuses, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@ -7488,6 +7517,21 @@ func (q *FakeQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg d
|
||||
return q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAppStatusesByAppIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
statuses := make([]database.WorkspaceAppStatus, 0)
|
||||
for _, status := range q.workspaceAppStatuses {
|
||||
for _, id := range ids {
|
||||
if status.AppID == id {
|
||||
statuses = append(statuses, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
return statuses, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAppsByAgentID(_ context.Context, id uuid.UUID) ([]database.WorkspaceApp, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@ -9584,6 +9628,31 @@ InsertWorkspaceAppStatsLoop:
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAppStatus(_ context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.WorkspaceAppStatus{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
status := database.WorkspaceAppStatus{
|
||||
ID: arg.ID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
WorkspaceID: arg.WorkspaceID,
|
||||
AgentID: arg.AgentID,
|
||||
AppID: arg.AppID,
|
||||
NeedsUserAttention: arg.NeedsUserAttention,
|
||||
State: arg.State,
|
||||
Message: arg.Message,
|
||||
Uri: arg.Uri,
|
||||
Icon: arg.Icon,
|
||||
}
|
||||
q.workspaceAppStatuses = append(q.workspaceAppStatuses, status)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
|
Reference in New Issue
Block a user