package main import "github.com/orchard9/rdev/pkg/api" func registerAgentPaths(spec *api.OpenAPISpec) { spec.AddPath("/agents", "get", withAuth( "List code agents", `Returns all registered code agent providers and their status. Shows which agents are available, their supported models, and the current default.`, "Code Agents", "projects:read", `{ "agents": [ { "provider": "claudecode", "name": "Claude Code", "available": true, "default": true, "supported_models": ["claude-sonnet-4-20250514"], "default_model": "claude-sonnet-4-20250514" }, { "provider": "opencode", "name": "OpenCode", "available": false, "default": false, "supported_models": ["gpt-4o", "claude-sonnet-4-20250514"], "default_model": "claude-sonnet-4-20250514" } ], "default_agent": "claudecode", "total_agents": 2, "available_count": 1 }`, )) spec.AddPath("/agents/health", "get", withAuth( "Get agent health status", `Returns the health status of all registered code agents. Checks connectivity to each agent backend and reports availability.`, "Code Agents", "projects:read", `{ "agents": [ { "provider": "claudecode", "name": "Claude Code", "healthy": true, "message": "available", "latency": "125ms", "checked_at": "2026-01-27T12:00:00Z" }, { "provider": "opencode", "name": "OpenCode", "healthy": false, "message": "unavailable", "latency": "5.002s", "checked_at": "2026-01-27T12:00:00Z" } ], "healthy_count": 1, "total_count": 2, "default_agent": "claudecode", "default_healthy": true }`, )) spec.AddPath("/agents/{provider}", "get", withAuthAndParams( "Get agent capabilities", `Returns detailed capabilities for a specific code agent provider. Includes supported features, models, and configuration options.`, "Code Agents", "projects:read", []param{{Name: "provider", In: "path", Description: "Agent provider ID (e.g., 'claudecode', 'opencode')", Required: true}}, )) spec.AddPath("/agents/default", "post", withAuthAndBody( "Set default agent", `Changes the default code agent used for command execution. The specified provider must be registered and ideally available.`, "Code Agents", "admin", `{"provider": "opencode"}`, `{ "default_agent": "opencode", "message": "default agent updated" }`, )) } // param represents an OpenAPI parameter. type param struct { Name string In string Description string Required bool } // withAuth creates an operation that requires authentication. func withAuth(summary, description, tag, scope, example string) map[string]any { return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "responses": map[string]any{ "200": map[string]any{ "description": "Success", "content": map[string]any{ "application/json": map[string]any{ "example": example, }, }, }, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } // withAuthAndBody creates an operation with auth and request body. func withAuthAndBody(summary, description, tag, scope, requestExample, responseExample string) map[string]any { return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "requestBody": map[string]any{ "required": true, "content": map[string]any{ "application/json": map[string]any{ "example": requestExample, }, }, }, "responses": map[string]any{ "201": map[string]any{ "description": "Created", "content": map[string]any{ "application/json": map[string]any{ "example": responseExample, }, }, }, "400": map[string]any{"description": "Bad Request - Invalid input"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } // withAuthAndParams creates an operation with auth and path parameters. func withAuthAndParams(summary, description, tag, scope string, params []param) map[string]any { parameters := make([]map[string]any, len(params)) for i, p := range params { parameters[i] = map[string]any{ "name": p.Name, "in": p.In, "description": p.Description, "required": p.Required, "schema": map[string]any{"type": "string"}, } } return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "parameters": parameters, "responses": map[string]any{ "200": map[string]any{"description": "Success"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, "404": map[string]any{"description": "Not Found"}, }, } } // withAuthBodyAndParams creates an operation with auth, body, and params. func withAuthBodyAndParams(summary, description, tag, scope string, params []param, requestExample, responseExample string) map[string]any { parameters := make([]map[string]any, len(params)) for i, p := range params { parameters[i] = map[string]any{ "name": p.Name, "in": p.In, "description": p.Description, "required": p.Required, "schema": map[string]any{"type": "string"}, } } return map[string]any{ "summary": summary, "description": description + "\n\n**Required scope**: `" + scope + "`", "tags": []string{tag}, "security": []map[string]any{ {"ApiKeyAuth": []string{}}, }, "parameters": parameters, "requestBody": map[string]any{ "required": true, "content": map[string]any{ "application/json": map[string]any{ "example": requestExample, }, }, }, "responses": map[string]any{ "201": map[string]any{ "description": "Created", "content": map[string]any{ "application/json": map[string]any{ "example": responseExample, }, }, }, "400": map[string]any{"description": "Bad Request - Invalid input"}, "401": map[string]any{"description": "Unauthorized - Missing or invalid API key"}, "403": map[string]any{"description": "Forbidden - Insufficient permissions"}, }, } } func registerWorkerPaths(spec *api.OpenAPISpec) { spec.AddPath("/workers", "get", withAuth( "List workers", "Returns all registered workers in the pool with status summary.", "Workers", "admin", `{ "workers": [ { "id": "rdev-worker-0", "hostname": "rdev-worker-0.rdev.svc", "status": "idle", "capabilities": ["build", "test", "deploy"], "registered_at": "2026-01-27T12:00:00Z", "last_heartbeat": "2026-01-27T12:05:00Z", "version": "1.0.0" } ], "total": 1, "summary": {"idle": 1, "busy": 0, "draining": 0, "offline": 0} }`, )) spec.AddPath("/workers/{workerId}", "get", withAuthAndParams( "Get worker", "Returns details for a specific worker.", "Workers", "admin", []param{{Name: "workerId", In: "path", Description: "Worker ID", Required: true}}, )) spec.AddPath("/workers/{workerId}/drain", "post", withAuthAndParams( "Drain worker", "Sets a worker to draining status. It will finish its current task but stop accepting new work.", "Workers", "admin", []param{{Name: "workerId", In: "path", Description: "Worker ID", Required: true}}, )) } func registerBuildPaths(spec *api.OpenAPISpec) { spec.AddPath("/projects/{id}/builds", "post", withAuthBodyAndParams( "Start build", "Enqueues a build task for a project. The build will be picked up by an available worker.", "Builds", "projects:execute", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, `{ "prompt": "Build a landing page with Next.js and Tailwind CSS", "template": "nextjs-landing", "auto_commit": true, "auto_push": true, "callback_url": "https://example.com/webhook" }`, `{ "task_id": "task-abc123", "project_id": "my-project", "status": "pending", "status_url": "/builds/task-abc123" }`, )) spec.AddPath("/projects/{id}/builds", "get", withAuthAndParams( "List builds", "Returns build history for a project.", "Builds", "projects:read", []param{{Name: "id", In: "path", Description: "Project ID", Required: true}}, )) spec.AddPath("/builds/{taskId}", "get", withAuthAndParams( "Get build status", "Returns the status and result of a specific build.", "Builds", "projects:read", []param{{Name: "taskId", In: "path", Description: "Build task ID", Required: true}}, )) spec.AddPath("/project/create-and-build", "post", withAuthAndBody( "Create project and build", `Creates a new project and immediately enqueues a build task. Combines project creation (git repo, DNS, CI activation) with build submission in a single call.`, "Builds", "admin", `{ "name": "my-landing-page", "description": "Landing page for product launch", "template": "nextjs-landing", "prompt": "Build a modern landing page with hero, features, and CTA sections", "auto_commit": true, "auto_push": true }`, `{ "project_id": "my-landing-page", "name": "my-landing-page", "domain": "my-landing-page.threesix.ai", "url": "https://my-landing-page.threesix.ai", "git": { "owner": "jordan", "name": "my-landing-page", "clone_http": "https://git.threesix.ai/jordan/my-landing-page.git" }, "task_id": "task-abc123", "status": "pending", "status_url": "/builds/task-abc123" }`, )) }