Documentation
¶
Overview ¶
Package serve provides port file lifecycle management and web session bootstrap for the td serve HTTP server.
Package serve provides the HTTP API layer for td serve, including response envelopes, DTOs with explicit JSON serialization, and request validation helpers.
Index ¶
- Constants
- func BumpSessionActivity(database *db.DB, sessionID string) error
- func DeletePortFile(baseDir string) error
- func GenerateInstanceID() (string, error)
- func GetOrCreateWebSession(database *db.DB) (*db.SessionRow, error)
- func IsPortFileStale(info *PortInfo) bool
- func IsServerHealthy(port int) bool
- func StartSessionHeartbeat(ctx context.Context, database *db.DB, sessionID string)
- func WriteError(w http.ResponseWriter, code, message string, status int)
- func WritePortFile(baseDir string, info *PortInfo) error
- func WriteSuccess(w http.ResponseWriter, data interface{}, status int)
- func WriteValidation(w http.ResponseWriter, fields []FieldError)
- type ActivityItemDTO
- type BoardCreateBody
- type BoardDTO
- type BoardPositionBody
- type BoardUpdateBody
- type CommentCreateBody
- type CommentDTO
- type DependencyCreateBody
- type DependencyDTO
- type Envelope
- type ErrorPayload
- type FieldError
- type FocusBody
- type HandoffDTO
- type IssueCreateBody
- type IssueDTO
- type IssueUpdateBody
- type LogDTO
- type MonitorDTO
- type PaginatedResponse
- type PaginationDTO
- type PortInfo
- type RecentHandoffDTO
- type SSEEvent
- type SSEHub
- type ServeConfig
- type Server
- type SessionDTO
- type StatsDTO
- type TaskListDTO
- type ValidationDetails
Constants ¶
const ( ErrValidation = "validation_error" // 400 ErrNotFound = "not_found" // 404 ErrConflict = "conflict" // 409 ErrForbidden = "forbidden" // 403 ErrInternal = "internal" // 500 )
Standard error codes mapped to HTTP status codes.
Variables ¶
This section is empty.
Functions ¶
func BumpSessionActivity ¶
BumpSessionActivity updates the last_activity timestamp for a session.
func DeletePortFile ¶
DeletePortFile removes the port file. Called on server shutdown. Errors are returned but callers may choose to ignore them during cleanup.
func GenerateInstanceID ¶
GenerateInstanceID creates a new random instance ID with the srv_ prefix and 6 random hex characters (e.g. "srv_8f3b2c").
func GetOrCreateWebSession ¶
func GetOrCreateWebSession(database *db.DB) (*db.SessionRow, error)
GetOrCreateWebSession finds or creates the shared web session used by the td serve HTTP server. The session is identified by:
- agent_type = "web"
- agent_pid = 0
- branch = "default"
If a matching session exists, its activity timestamp is bumped. If none exists, a new session named "td-serve-web" is created.
func IsPortFileStale ¶
IsPortFileStale checks whether the port file describes a server that is no longer running. A port file is stale if:
- The PID is not alive, OR
- The PID is alive but the health endpoint doesn't respond
func IsServerHealthy ¶
IsServerHealthy checks if a server at the given port is alive by sending an HTTP GET to localhost:{port}/health. Returns true only if a 200 response is received within the health timeout.
func StartSessionHeartbeat ¶
StartSessionHeartbeat launches a goroutine that periodically bumps the session's last_activity timestamp. The goroutine stops when the provided context is cancelled. Errors during bumps are silently ignored since heartbeats are best-effort.
func WriteError ¶
func WriteError(w http.ResponseWriter, code, message string, status int)
WriteError writes a JSON error envelope.
func WritePortFile ¶
WritePortFile writes the port file to baseDir/.todos/serve-port after acquiring an exclusive lock on the lock file. The lock is released after writing. This ensures only one server instance can register itself at a time.
func WriteSuccess ¶
func WriteSuccess(w http.ResponseWriter, data interface{}, status int)
WriteSuccess writes a JSON success envelope with the given data and status.
func WriteValidation ¶
func WriteValidation(w http.ResponseWriter, fields []FieldError)
WriteValidation writes a 400 validation_error response with field-level details.
Types ¶
type ActivityItemDTO ¶
type ActivityItemDTO struct {
Timestamp string `json:"timestamp"`
SessionID string `json:"session_id"`
Type string `json:"type"`
IssueID string `json:"issue_id"`
IssueTitle string `json:"issue_title"`
Message string `json:"message"`
LogType string `json:"log_type"`
Action string `json:"action"`
EntityID string `json:"entity_id"`
EntityType string `json:"entity_type"`
PreviousData string `json:"previous_data"`
NewData string `json:"new_data"`
}
ActivityItemDTO is the API representation of a unified activity feed item.
func ActivityItemToDTO ¶
func ActivityItemToDTO(item *monitor.ActivityItem) ActivityItemDTO
ActivityItemToDTO converts a monitor.ActivityItem to an ActivityItemDTO.
func ActivityItemsToDTOs ¶
func ActivityItemsToDTOs(items []monitor.ActivityItem) []ActivityItemDTO
ActivityItemsToDTOs converts a slice of activity items to DTOs.
type BoardCreateBody ¶
BoardCreateBody represents the expected JSON body for creating a board.
type BoardDTO ¶
type BoardDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Query string `json:"query"`
IsBuiltin bool `json:"is_builtin"`
ViewMode string `json:"view_mode"`
LastViewedAt *string `json:"last_viewed_at"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
BoardDTO is the API representation of a board.
func BoardToDTO ¶
BoardToDTO converts a models.Board to a BoardDTO.
func BoardsToDTOs ¶
BoardsToDTOs converts a slice of boards to DTOs.
type BoardPositionBody ¶
BoardPositionBody represents the expected JSON body for setting an issue position on a board.
type BoardUpdateBody ¶
BoardUpdateBody represents the expected JSON body for updating a board. All fields are optional; only present fields are applied.
type CommentCreateBody ¶
type CommentCreateBody struct {
Text string `json:"text"`
}
CommentCreateBody represents the expected JSON body for adding a comment.
type CommentDTO ¶
type CommentDTO struct {
ID string `json:"id"`
IssueID string `json:"issue_id"`
SessionID string `json:"session_id"`
Text string `json:"text"`
CreatedAt string `json:"created_at"`
}
CommentDTO is the API representation of a comment.
func CommentToDTO ¶
func CommentToDTO(comment *models.Comment) CommentDTO
CommentToDTO converts a models.Comment to a CommentDTO.
func CommentsToDTOs ¶
func CommentsToDTOs(comments []models.Comment) []CommentDTO
CommentsToDTOs converts a slice of comments to DTOs.
type DependencyCreateBody ¶
type DependencyCreateBody struct {
DependsOn string `json:"depends_on"`
}
DependencyCreateBody represents the expected JSON body for adding a dependency.
type DependencyDTO ¶
type DependencyDTO struct {
DepID string `json:"dep_id"`
IssueID string `json:"issue_id"`
DependsOnID string `json:"depends_on_id"`
RelationType string `json:"relation_type"`
}
DependencyDTO is the API representation of an issue dependency.
func DependenciesToDTOs ¶
func DependenciesToDTOs(deps []models.IssueDependency) []DependencyDTO
DependenciesToDTOs converts a slice of dependencies to DTOs.
func DependencyToDTO ¶
func DependencyToDTO(dep *models.IssueDependency) DependencyDTO
DependencyToDTO converts a models.IssueDependency to a DependencyDTO.
type Envelope ¶
type Envelope struct {
OK bool `json:"ok"`
Data interface{} `json:"data,omitempty"`
Error *ErrorPayload `json:"error,omitempty"`
}
Envelope is the standard response wrapper for all API responses. Success: {"ok": true, "data": {...}} Error: {"ok": false, "error": {"code": "...", "message": "...", "details": ...}}
type ErrorPayload ¶
type ErrorPayload struct {
Code string `json:"code"`
Message string `json:"message"`
Details interface{} `json:"details,omitempty"`
}
ErrorPayload holds structured error information.
type FieldError ¶
type FieldError struct {
Field string `json:"field"`
Rule string `json:"rule"`
Value interface{} `json:"value,omitempty"`
Expected interface{} `json:"expected,omitempty"`
Message string `json:"message"`
}
FieldError describes a single validation failure on a request field.
func ValidateIssueCreate ¶
func ValidateIssueCreate(body *IssueCreateBody, titleMin, titleMax int) []FieldError
ValidateIssueCreate validates an IssueCreateBody and returns any field errors. The titleMin and titleMax parameters allow callers to pass configured limits.
func ValidateIssueUpdate ¶
func ValidateIssueUpdate(body *IssueUpdateBody, titleMin, titleMax int) []FieldError
ValidateIssueUpdate validates an IssueUpdateBody and returns any field errors. The titleMin and titleMax parameters allow callers to pass configured limits.
func ValidatePagination ¶
func ValidatePagination(limit, offset int) []FieldError
ValidatePagination validates limit and offset query parameters.
type FocusBody ¶
type FocusBody struct {
IssueID *string `json:"issue_id"`
}
FocusBody represents the expected JSON body for setting focus. IssueID is a pointer so that null/absent can be distinguished from empty string.
type HandoffDTO ¶
type HandoffDTO struct {
ID string `json:"id"`
IssueID string `json:"issue_id"`
SessionID string `json:"session_id"`
Done []string `json:"done"`
Remaining []string `json:"remaining"`
Decisions []string `json:"decisions"`
Uncertain []string `json:"uncertain"`
Timestamp string `json:"timestamp"`
}
HandoffDTO is the API representation of a handoff.
func HandoffToDTO ¶
func HandoffToDTO(handoff *models.Handoff) HandoffDTO
HandoffToDTO converts a models.Handoff to a HandoffDTO.
type IssueCreateBody ¶
type IssueCreateBody struct {
Title string `json:"title"`
Description string `json:"description"`
Type string `json:"type"`
Priority string `json:"priority"`
Points int `json:"points"`
Labels []string `json:"labels"`
ParentID string `json:"parent_id"`
Acceptance string `json:"acceptance"`
Sprint string `json:"sprint"`
Minor bool `json:"minor"`
DeferUntil string `json:"defer_until"`
DueDate string `json:"due_date"`
}
IssueCreateBody represents the expected JSON body for creating an issue.
type IssueDTO ¶
type IssueDTO struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
Type string `json:"type"`
Priority string `json:"priority"`
Points int `json:"points"`
Labels []string `json:"labels"`
ParentID *string `json:"parent_id"`
Acceptance string `json:"acceptance"`
Sprint string `json:"sprint"`
ImplementerSession *string `json:"implementer_session"`
CreatorSession *string `json:"creator_session"`
ReviewerSession *string `json:"reviewer_session"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ClosedAt *string `json:"closed_at"`
DeletedAt *string `json:"deleted_at"`
Minor bool `json:"minor"`
CreatedBranch *string `json:"created_branch"`
DeferUntil *string `json:"defer_until"`
DueDate *string `json:"due_date"`
DeferCount int `json:"defer_count"`
}
IssueDTO is the API representation of an issue. All documented fields are always present (no omitempty for documented fields). Nullable fields use *string so they serialize as JSON null when nil. Collections serialize as [] when empty, never null.
func IssueToDTO ¶
IssueToDTO converts a models.Issue to an IssueDTO with proper null/empty handling for the API layer.
func IssuesToDTOs ¶
IssuesToDTOs converts a slice of issues to DTOs.
type IssueUpdateBody ¶
type IssueUpdateBody struct {
Title *string `json:"title"`
Description *string `json:"description"`
Type *string `json:"type"`
Priority *string `json:"priority"`
Points *int `json:"points"`
Labels []string `json:"labels"`
ParentID *string `json:"parent_id"`
Acceptance *string `json:"acceptance"`
Sprint *string `json:"sprint"`
Minor *bool `json:"minor"`
DeferUntil *string `json:"defer_until"`
DueDate *string `json:"due_date"`
}
IssueUpdateBody represents the expected JSON body for updating an issue. All fields are optional; only present fields are applied.
type LogDTO ¶
type LogDTO struct {
ID string `json:"id"`
IssueID string `json:"issue_id"`
SessionID string `json:"session_id"`
WorkSessionID string `json:"work_session_id"`
Message string `json:"message"`
Type string `json:"type"`
Timestamp string `json:"timestamp"`
}
LogDTO is the API representation of a session log entry.
func LogsToDTOs ¶
LogsToDTOs converts a slice of logs to DTOs.
type MonitorDTO ¶
type MonitorDTO struct {
FocusedIssue *IssueDTO `json:"focused_issue"`
InProgress []IssueDTO `json:"in_progress"`
Activity []ActivityItemDTO `json:"activity"`
TaskList TaskListDTO `json:"task_list"`
RecentHandoffs []RecentHandoffDTO `json:"recent_handoffs"`
ActiveSessions []string `json:"active_sessions"`
Timestamp string `json:"timestamp"`
}
MonitorDTO is the API representation of the full monitor state.
func MonitorDataToDTO ¶
func MonitorDataToDTO(msg *monitor.RefreshDataMsg) MonitorDTO
MonitorDataToDTO converts a RefreshDataMsg to a MonitorDTO.
type PaginatedResponse ¶
type PaginatedResponse struct {
Items interface{} `json:"items"`
Pagination PaginationDTO `json:"pagination"`
}
PaginatedResponse wraps a list result with pagination metadata.
type PaginationDTO ¶
type PaginationDTO struct {
Limit int `json:"limit"`
Offset int `json:"offset"`
Total int `json:"total"`
}
PaginationDTO describes the pagination state returned alongside list results.
type PortInfo ¶
type PortInfo struct {
Port int `json:"port"`
PID int `json:"pid"`
StartedAt time.Time `json:"started_at"`
InstanceID string `json:"instance_id"`
}
PortInfo contains the metadata written to the port file when the server starts.
func ReadPortFile ¶
ReadPortFile reads and parses the port file from baseDir/.todos/serve-port. Returns an error if the file doesn't exist or is missing required fields.
type RecentHandoffDTO ¶
type RecentHandoffDTO struct {
IssueID string `json:"issue_id"`
SessionID string `json:"session_id"`
Timestamp string `json:"timestamp"`
}
RecentHandoffDTO is the API representation of a recent handoff summary.
type SSEEvent ¶
type SSEEvent struct {
ID string // change_token used as event ID
Event string // "refresh" or "ping"
Data string // JSON payload
}
SSEEvent represents a single Server-Sent Event.
type SSEHub ¶
type SSEHub struct {
// contains filtered or unexported fields
}
SSEHub manages connected SSE clients and broadcasts events.
func (*SSEHub) Broadcast ¶
Broadcast sends a refresh event to all connected clients with the given change token.
type ServeConfig ¶
type ServeConfig struct {
Port int
Addr string
Token string
CORSOrigin string
PollInterval time.Duration
}
ServeConfig holds the configuration for the HTTP server.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the td serve HTTP server.
func NewServer ¶
func NewServer(database *db.DB, baseDir, sessionID string, config ServeConfig) *Server
NewServer creates a new Server, registers all routes, and sets up the middleware chain. Handlers are placeholder 501s until subsequent tasks implement them.
func (*Server) ListenAndServe ¶
ListenAndServe starts the HTTP server on the configured address and port, and handles graceful shutdown when the context is cancelled.
func (*Server) NotifyChange ¶
func (s *Server) NotifyChange()
NotifyChange is called after successful write operations. It: 1. Gets the current change_token 2. Broadcasts a refresh event to all SSE clients 3. Triggers a debounced autosync
func (*Server) Shutdown ¶
Shutdown gracefully stops the HTTP server. If the server has not been started, this is a no-op.
func (*Server) StartBackground ¶
StartBackground starts long-lived background processes (SSE polling loop).
func (*Server) StopBackground ¶
func (s *Server) StopBackground()
StopBackground stops long-lived background processes.
type SessionDTO ¶
type SessionDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Branch string `json:"branch"`
AgentType string `json:"agent_type"`
AgentPID int `json:"agent_pid"`
ContextID string `json:"context_id"`
PreviousSessionID *string `json:"previous_session_id"`
StartedAt string `json:"started_at"`
LastActivity string `json:"last_activity"`
}
SessionDTO is the API representation of a session.
func SessionToDTO ¶
func SessionToDTO(sess *session.Session) SessionDTO
SessionToDTO converts a session.Session to a SessionDTO.
func SessionsToDTOs ¶
func SessionsToDTOs(sessions []session.Session) []SessionDTO
SessionsToDTOs converts a slice of sessions to DTOs.
type StatsDTO ¶
type StatsDTO struct {
Total int `json:"total"`
ByStatus map[string]int `json:"by_status"`
ByType map[string]int `json:"by_type"`
ByPriority map[string]int `json:"by_priority"`
OldestOpen *IssueDTO `json:"oldest_open"`
NewestTask *IssueDTO `json:"newest_task"`
LastClosed *IssueDTO `json:"last_closed"`
CreatedToday int `json:"created_today"`
CreatedThisWeek int `json:"created_this_week"`
TotalPoints int `json:"total_points"`
AvgPointsPerTask float64 `json:"avg_points_per_task"`
CompletionRate float64 `json:"completion_rate"`
TotalLogs int `json:"total_logs"`
TotalHandoffs int `json:"total_handoffs"`
MostActiveSession string `json:"most_active_session"`
}
StatsDTO is the API representation of extended project statistics.
func StatsToDTO ¶
func StatsToDTO(stats *models.ExtendedStats) StatsDTO
StatsToDTO converts a models.ExtendedStats to a StatsDTO.
type TaskListDTO ¶
type TaskListDTO struct {
Reviewable []IssueDTO `json:"reviewable"`
NeedsRework []IssueDTO `json:"needs_rework"`
InProgress []IssueDTO `json:"in_progress"`
Ready []IssueDTO `json:"ready"`
PendingReview []IssueDTO `json:"pending_review"`
Blocked []IssueDTO `json:"blocked"`
Closed []IssueDTO `json:"closed"`
}
TaskListDTO is the API representation of categorized task lists.
type ValidationDetails ¶
type ValidationDetails struct {
Fields []FieldError `json:"fields"`
}
ValidationDetails wraps field-level validation errors in error.details.