Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 77 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,20 @@ QUIET_REDIRECT := >/dev/null 2>&1
endif

# Image tags
FRONTEND_IMAGE ?= vteam-frontend:latest
BACKEND_IMAGE ?= vteam-backend:latest
OPERATOR_IMAGE ?= vteam-operator:latest
RUNNER_IMAGE ?= vteam-claude-runner:latest
FRONTEND_IMAGE ?= vteam_frontend:latest
BACKEND_IMAGE ?= vteam_backend:latest
OPERATOR_IMAGE ?= vteam_operator:latest
RUNNER_IMAGE ?= vteam_claude_runner:latest

# Build metadata (captured at build time)
GIT_COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
GIT_COMMIT_SHORT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
GIT_REPO := $(shell git remote get-url origin 2>/dev/null || echo "local")
GIT_DIRTY := $(shell git diff --quiet 2>/dev/null || echo "-dirty")
GIT_VERSION := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
BUILD_USER := $(shell whoami)@$(shell hostname)

# Colors for output
COLOR_RESET := \033[0m
Expand Down Expand Up @@ -85,22 +95,54 @@ build-all: build-frontend build-backend build-operator build-runner ## Build all

build-frontend: ## Build frontend image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building frontend with $(CONTAINER_ENGINE)..."
@cd components/frontend && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) -t $(FRONTEND_IMAGE) .
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/frontend && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
-t $(FRONTEND_IMAGE) .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Frontend built: $(FRONTEND_IMAGE)"

build-backend: ## Build backend image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building backend with $(CONTAINER_ENGINE)..."
@cd components/backend && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) -t $(BACKEND_IMAGE) .
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/backend && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
-t $(BACKEND_IMAGE) .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Backend built: $(BACKEND_IMAGE)"

build-operator: ## Build operator image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building operator with $(CONTAINER_ENGINE)..."
@cd components/operator && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) -t $(OPERATOR_IMAGE) .
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/operator && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
-t $(OPERATOR_IMAGE) .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Operator built: $(OPERATOR_IMAGE)"

build-runner: ## Build Claude Code runner image
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Building runner with $(CONTAINER_ENGINE)..."
@cd components/runners && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) -t $(RUNNER_IMAGE) -f claude-code-runner/Dockerfile .
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/runners && $(CONTAINER_ENGINE) build $(PLATFORM_FLAG) $(BUILD_FLAGS) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
-t $(RUNNER_IMAGE) -f claude-code-runner/Dockerfile .
@echo "$(COLOR_GREEN)✓$(COLOR_RESET) Runner built: $(RUNNER_IMAGE)"

##@ Git Hooks
Expand Down Expand Up @@ -236,7 +278,15 @@ local-rebuild: ## Rebuild and reload all components

local-reload-backend: ## Rebuild and reload backend only
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Rebuilding backend..."
@cd components/backend && $(CONTAINER_ENGINE) build -t $(BACKEND_IMAGE) . >/dev/null 2>&1
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/backend && $(CONTAINER_ENGINE) build -t $(BACKEND_IMAGE) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
. >/dev/null 2>&1
@$(CONTAINER_ENGINE) tag $(BACKEND_IMAGE) localhost/$(BACKEND_IMAGE) 2>/dev/null || true
@$(CONTAINER_ENGINE) save -o /tmp/backend-reload.tar localhost/$(BACKEND_IMAGE)
@minikube image load /tmp/backend-reload.tar >/dev/null 2>&1
Expand All @@ -259,7 +309,15 @@ local-reload-backend: ## Rebuild and reload backend only

local-reload-frontend: ## Rebuild and reload frontend only
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Rebuilding frontend..."
@cd components/frontend && $(CONTAINER_ENGINE) build -t $(FRONTEND_IMAGE) . >/dev/null 2>&1
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/frontend && $(CONTAINER_ENGINE) build -t $(FRONTEND_IMAGE) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
. >/dev/null 2>&1
@$(CONTAINER_ENGINE) tag $(FRONTEND_IMAGE) localhost/$(FRONTEND_IMAGE) 2>/dev/null || true
@$(CONTAINER_ENGINE) save -o /tmp/frontend-reload.tar localhost/$(FRONTEND_IMAGE)
@minikube image load /tmp/frontend-reload.tar >/dev/null 2>&1
Expand All @@ -283,7 +341,15 @@ local-reload-frontend: ## Rebuild and reload frontend only

local-reload-operator: ## Rebuild and reload operator only
@echo "$(COLOR_BLUE)▶$(COLOR_RESET) Rebuilding operator..."
@cd components/operator && $(CONTAINER_ENGINE) build -t $(OPERATOR_IMAGE) . >/dev/null 2>&1
@echo " Git: $(GIT_BRANCH)@$(GIT_COMMIT_SHORT)$(GIT_DIRTY)"
@cd components/operator && $(CONTAINER_ENGINE) build -t $(OPERATOR_IMAGE) \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
--build-arg GIT_REPO=$(GIT_REPO) \
--build-arg GIT_VERSION=$(GIT_VERSION)$(GIT_DIRTY) \
--build-arg BUILD_DATE=$(BUILD_DATE) \
--build-arg BUILD_USER=$(BUILD_USER) \
. >/dev/null 2>&1
@$(CONTAINER_ENGINE) tag $(OPERATOR_IMAGE) localhost/$(OPERATOR_IMAGE) 2>/dev/null || true
@$(CONTAINER_ENGINE) save -o /tmp/operator-reload.tar localhost/$(OPERATOR_IMAGE)
@minikube image load /tmp/operator-reload.tar >/dev/null 2>&1
Expand Down
45 changes: 42 additions & 3 deletions components/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Build stage
FROM registry.access.redhat.com/ubi9/go-toolset:1.24 AS builder

# Build arguments for metadata
ARG GIT_COMMIT=unknown
ARG GIT_BRANCH=unknown
ARG GIT_REPO=unknown
ARG GIT_VERSION=unknown
ARG BUILD_DATE=unknown
ARG BUILD_USER=unknown

WORKDIR /app

USER 0
Expand All @@ -14,21 +22,52 @@ RUN go mod download
# Copy the source code
COPY . .

# Build the application (with flags to avoid segfault)
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .
# Build the application with embedded version info
# The -X flag injects build-time variables into the binary
# This ensures git metadata is baked into the binary itself, not just ENV vars
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w \
-X main.GitCommit=${GIT_COMMIT} \
-X main.GitBranch=${GIT_BRANCH} \
-X main.GitVersion=${GIT_VERSION} \
-X main.BuildDate=${BUILD_DATE}" \
-o main .

# Final stage
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest

# Build arguments (need to redeclare for final stage)
ARG GIT_COMMIT=unknown
ARG GIT_BRANCH=unknown
ARG GIT_REPO=unknown
ARG GIT_VERSION=unknown
ARG BUILD_DATE=unknown
ARG BUILD_USER=unknown

# Add labels to force cache invalidation and provide metadata
LABEL git.commit="${GIT_COMMIT}"
LABEL git.branch="${GIT_BRANCH}"
LABEL git.version="${GIT_VERSION}"
LABEL build.date="${BUILD_DATE}"
LABEL build.user="${BUILD_USER}"

RUN microdnf install -y git && microdnf clean all
WORKDIR /app

# Copy the binary from builder stage
# Copy the binary from builder stage (binary has metadata embedded via ldflags)
COPY --from=builder /app/main .

# Default agents directory
ENV AGENTS_DIR=/app/agents

# Build metadata as environment variables (fallback, primary source is embedded in binary)
ENV GIT_COMMIT=${GIT_COMMIT}
ENV GIT_BRANCH=${GIT_BRANCH}
ENV GIT_REPO=${GIT_REPO}
ENV GIT_VERSION=${GIT_VERSION}
ENV BUILD_DATE=${BUILD_DATE}
ENV BUILD_USER=${BUILD_USER}

# Set executable permissions and make accessible to any user
RUN chmod +x ./main && chmod 775 /app

Expand Down
1 change: 0 additions & 1 deletion components/backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ require (
github.com/gin-gonic/gin v1.10.1
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674
github.com/joho/godotenv v1.5.1
github.com/onsi/ginkgo/v2 v2.27.3
github.com/onsi/gomega v1.38.3
Expand Down
2 changes: 0 additions & 2 deletions components/backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down
10 changes: 9 additions & 1 deletion components/backend/handlers/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var (
DynamicClient dynamic.Interface
GetGitHubToken func(context.Context, kubernetes.Interface, dynamic.Interface, string, string) (string, error)
DeriveRepoFolderFromURL func(string) string
SendMessageToSession func(string, string, map[string]interface{})
// LEGACY: SendMessageToSession removed - AG-UI server uses HTTP/SSE instead of WebSocket
)

const runnerTokenRefreshedAtAnnotation = "ambient-code.io/token-refreshed-at"
Expand Down Expand Up @@ -2065,6 +2065,10 @@ func StartSession(c *gin.Context) {

if spec, ok := updated.Object["spec"].(map[string]interface{}); ok {
session.Spec = parseSpec(spec)

// NOTE: INITIAL_PROMPT auto-execution handled by runner on startup
// Runner POSTs to /agui/run when ready, events flow through backend
// This works for both UI and headless/API usage
}

if status, ok := updated.Object["status"].(map[string]interface{}); ok {
Expand Down Expand Up @@ -3898,3 +3902,7 @@ func GitListBranchesSession(c *gin.Context) {
}
c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), bodyBytes)
}

// NOTE: autoTriggerInitialPrompt removed - runner handles INITIAL_PROMPT auto-execution
// Runner POSTs to backend's /agui/run when ready, events flow through middleware
// See: components/runners/claude-code-runner/main.py auto_execute_initial_prompt()
3 changes: 0 additions & 3 deletions components/backend/handlers/test_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ func SetupHandlerDependencies(k8sUtils *test_utils.K8sTestUtils) {
}
return "repo"
}
SendMessageToSession = func(sessionID, userID string, message map[string]interface{}) {
// no-op in unit tests
}

logger.Log("Handler dependencies set up with fake clients")
}
Expand Down
34 changes: 33 additions & 1 deletion components/backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,43 @@ import (
"github.com/joho/godotenv"
)

// Build-time metadata (set via -ldflags -X during build)
// These are embedded directly in the binary, so they're always accurate
var (
GitCommit = "unknown"
GitBranch = "unknown"
GitVersion = "unknown"
BuildDate = "unknown"
)

func logBuildInfo() {
log.Println("==============================================")
log.Println("Backend API - Build Information")
log.Println("==============================================")
log.Printf("Version: %s", GitVersion)
log.Printf("Commit: %s", GitCommit)
log.Printf("Branch: %s", GitBranch)
log.Printf("Repository: %s", getEnvOrDefault("GIT_REPO", "unknown"))
log.Printf("Built: %s", BuildDate)
log.Printf("Built by: %s", getEnvOrDefault("BUILD_USER", "unknown"))
log.Println("==============================================")
}

func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}

func main() {
// Load environment from .env in development if present
_ = godotenv.Overload(".env.local")
_ = godotenv.Overload(".env")

// Log build information
logBuildInfo()

// Content service mode - minimal initialization, no K8s access needed
if os.Getenv("CONTENT_SERVICE_MODE") == "true" {
log.Println("Starting in CONTENT_SERVICE_MODE (no K8s client initialization)")
Expand Down Expand Up @@ -94,7 +126,7 @@ func main() {
handlers.DynamicClient = server.DynamicClient
handlers.GetGitHubToken = handlers.WrapGitHubTokenForRepo(git.GetGitHubToken)
handlers.DeriveRepoFolderFromURL = git.DeriveRepoFolderFromURL
handlers.SendMessageToSession = websocket.SendMessageToSession
// LEGACY: SendMessageToSession removed - AG-UI server uses HTTP/SSE instead of WebSocket

// Initialize repo handlers (default implementation already set in client_selection.go)
// GetK8sClientsForRequestRepoFunc uses getK8sClientsForRequestRepoDefault by default
Expand Down
12 changes: 8 additions & 4 deletions components/backend/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,14 @@ func registerRoutes(r *gin.Engine) {
// OAuth integration - requires user auth like all other session endpoints
projectGroup.GET("/agentic-sessions/:sessionName/oauth/:provider/url", handlers.GetOAuthURL)

projectGroup.GET("/sessions/:sessionId/ws", websocket.HandleSessionWebSocket)
projectGroup.GET("/sessions/:sessionId/messages", websocket.GetSessionMessagesWS)
// Removed: /messages/claude-format - Using SDK's built-in resume with persisted ~/.claude state
projectGroup.POST("/sessions/:sessionId/messages", websocket.PostSessionMessageWS)
// AG-UI Protocol endpoints (HttpAgent-compatible)
// See: https://docs.ag-ui.com/quickstart/introduction
// Runner is a FastAPI server - backend proxies requests and streams SSE responses
projectGroup.POST("/agentic-sessions/:sessionName/agui/run", websocket.HandleAGUIRunProxy)
projectGroup.POST("/agentic-sessions/:sessionName/agui/interrupt", websocket.HandleAGUIInterrupt)
projectGroup.GET("/agentic-sessions/:sessionName/agui/events", websocket.HandleAGUIEvents)
projectGroup.GET("/agentic-sessions/:sessionName/agui/history", websocket.HandleAGUIHistory)
projectGroup.GET("/agentic-sessions/:sessionName/agui/runs", websocket.HandleAGUIRuns)

projectGroup.GET("/permissions", handlers.ListProjectPermissions)
projectGroup.POST("/permissions", handlers.AddProjectPermission)
Expand Down
Loading
Loading