Documentation
¶
Index ¶
- Constants
- func AuthMiddleware(secret string, next http.Handler) http.Handler
- func BuildRDPFile(host string, port int, username string, appProgram string) string
- func DebugLoggingMiddleware(next http.Handler) http.Handler
- func DefaultCredentialsPath() string
- func EncodeFeed(cells []Cell, baseHost string, now time.Time) ([]byte, error)
- func GenerateAPIKey() string
- func GenerateIcon(title string, size int) ([]byte, error)
- func GeneratePreview(title string, width, height int) ([]byte, error)
- func GenerateSelfSignedCert(extraHosts ...string) (tls.Certificate, error)
- func LoggingMiddleware(next http.Handler) http.Handler
- func NewChatHandler(exec Executor, logPrompts bool, systemPrompt string) http.Handler
- func NewModelsHandler(lookPath LookPathFunc, ac AnthropicClient) http.Handler
- func NewResponseCancelHandler(store *JobStore) http.Handler
- func NewResponseGetHandler(store *JobStore) http.Handler
- func NewResponsesHandler(exec Executor, store *JobStore, logPrompts bool, systemPrompt string) http.Handler
- func NewSOAPReconnectStub() http.Handler
- func ReadClaudeCredentials(path string) string
- func WorkspaceRoutes(enum CellEnumerator, publicHost string, opts ...WorkspaceOpt) *http.ServeMux
- type APIError
- type APIErrorBody
- type AnthropicClient
- type Cell
- type CellEnumerator
- type ChatChoice
- type ChatMessage
- type ChatRequest
- type ChatResponse
- type ChatStreamChoice
- type ChatStreamChunk
- type ChatStreamDelta
- type ChatStreamOptions
- type ChatUsage
- type CompositeEnumerator
- type DockerAPIClient
- type DockerEnumerator
- type ExecOpts
- type ExecResult
- type Executor
- type Folder
- type Folders
- type HealthResponse
- type HostingTerminalServer
- type HostingTerminalServers
- type IconElement
- type Icons
- type Job
- type JobStore
- func (s *JobStore) Cancel(id string) (Job, bool)
- func (s *JobStore) Complete(id string, status string, result *ResponsesObject)
- func (s *JobStore) Create(id string, cancel context.CancelFunc) *Job
- func (s *JobStore) Get(id string) (Job, bool)
- func (s *JobStore) Sweep(now time.Time, ttl time.Duration) int
- type LookPathFunc
- type MockEnumerator
- type ModelInfo
- type ModelsResponse
- type Publisher
- type RealAnthropicClient
- type Resource
- type ResourceCollection
- type ResourceFile
- type Resources
- type ResponsesError
- type ResponsesIncompleteDetails
- type ResponsesInputTokensDetails
- type ResponsesObject
- type ResponsesOutputContentPart
- type ResponsesOutputItem
- type ResponsesOutputTokensDetails
- type ResponsesReasoningConfig
- type ResponsesRequest
- type ResponsesUsage
- type Server
- func (s *Server) APIKey() string
- func (s *Server) HTTPAddr() string
- func (s *Server) SetAPIKey(key string)
- func (s *Server) SetAnthropicClient(ac AnthropicClient)
- func (s *Server) SetDebug(v bool)
- func (s *Server) SetLogPrompts(v bool)
- func (s *Server) SetLookPath(fn LookPathFunc)
- func (s *Server) SetSystemPrompt(p string)
- func (s *Server) SetTLS(v bool)
- func (s *Server) SetWorkspace(enabled, mock bool, host string)
- func (s *Server) Start(ctx context.Context) (addr string, errCh chan error)
- type ShellExecutor
- type StreamEvent
- type StreamEventKind
- type TerminalServer
- type TerminalServerRef
- type TerminalServers
- type Usage
- type WorkspaceOpt
Constants ¶
const (
// AnthropicAPIURL is the default Anthropic models endpoint.
AnthropicAPIURL = "https://api.anthropic.com/v1/models"
)
const DefaultPort = 8484
DefaultPort is the default listen port for devcell serve.
const WorkspaceFeedContentType = "application/x-msts-radc+xml; charset=utf-8"
Variables ¶
This section is empty.
Functions ¶
func AuthMiddleware ¶
AuthMiddleware returns a handler that checks the Authorization: Bearer <secret> header. If secret is empty, all requests are allowed (no auth).
func BuildRDPFile ¶ added in v0.8.0
func DebugLoggingMiddleware ¶ added in v0.8.0
DebugLoggingMiddleware logs full request headers and response bodies to stderr.
func DefaultCredentialsPath ¶
func DefaultCredentialsPath() string
DefaultCredentialsPath returns the default path to Claude's credentials.
func EncodeFeed ¶ added in v0.8.0
func GenerateAPIKey ¶
func GenerateAPIKey() string
GenerateAPIKey creates a random API key for use when none is configured.
func GeneratePreview ¶ added in v0.8.0
func GenerateSelfSignedCert ¶ added in v0.8.0
func GenerateSelfSignedCert(extraHosts ...string) (tls.Certificate, error)
func LoggingMiddleware ¶ added in v0.6.0
LoggingMiddleware logs every HTTP request with method, path, status, and duration.
func NewChatHandler ¶
NewChatHandler returns an http.Handler for POST /v1/chat/completions.
@Summary Send a chat completion request @Description Accepts an OpenAI-compatible chat completion request and routes it to the appropriate @Description LLM agent binary (Claude Code or OpenCode) running inside the DevCell container. @Description @Description The `model` field determines which agent handles the request: @Description - `"claude"` or `"anthropic"` — routes to Claude Code CLI @Description - `"opencode"` — routes to OpenCode CLI @Description - `"claude/claude-sonnet-4-5"` — routes to Claude Code with a specific sub-model @Description @Description Only the **last user message** in the `messages` array is sent as the prompt to the agent. @Description The response is a single-choice completion with finish_reason "stop" on success or "error" on failure. @Description @Description **Honored fields:** @Description - `reasoning_effort` (`low` / `medium` / `high`) → maps to the `claude --effort` CLI flag @Description to control thinking budget. Other values (including Claude's `xhigh`/`max`) are silently dropped. @Description @Description **Example request:** @Description ```json @Description {"model": "claude", "messages": [{"role": "user", "content": "explain this repo"}]} @Description ``` @Tags chat @Accept json @Produce json @Param request body ChatRequest true "Chat completion request" @Success 200 {object} ChatResponse "Successful completion" @Failure 400 {string} string "Invalid JSON, missing model, empty messages, or unknown model prefix" @Failure 401 {string} string "Missing or invalid Bearer token" @Failure 405 {string} string "Only POST is allowed" @Security BearerAuth @Router /v1/chat/completions [post]
func NewModelsHandler ¶
func NewModelsHandler(lookPath LookPathFunc, ac AnthropicClient) http.Handler
NewModelsHandler returns an http.Handler for GET /v1/models.
@Summary List available models @Description Returns all models that can be used in chat completion requests. @Description @Description Models are discovered dynamically at request time: @Description 1. If the `claude` binary is found, the server tries the Anthropic API to list real model IDs @Description (e.g. `anthropic/claude-sonnet-4-5-20250514`). If the API is unreachable, it falls back to @Description aliases: `anthropic/opus`, `anthropic/sonnet`, `anthropic/haiku`. @Description 2. If the `opencode` binary is found, `opencode` is added as an available model. @Description @Description Use any returned `id` value as the `model` field in `/v1/chat/completions`. @Tags models @Produce json @Success 200 {object} ModelsResponse "List of available models" @Failure 401 {string} string "Missing or invalid Bearer token" @Failure 405 {string} string "Only GET is allowed" @Security BearerAuth @Router /v1/models [get]
func NewResponseCancelHandler ¶ added in v0.7.0
NewResponseCancelHandler returns an http.Handler for POST /v1/responses/{id}/cancel.
Cancels an in-progress background job. The job's context is cancelled and status flips to "cancelled" immediately; the underlying goroutine may continue running but its result is discarded (Complete is a no-op once status == "cancelled").
@Summary Cancel an in-progress background Response @Description Cancels a background `/v1/responses` job. Idempotent — calling @Description cancel on a job that has already completed or been cancelled @Description returns 200 with the current state. @Tags responses @Produce json @Param id path string true "Response id (resp_...)" @Success 200 {object} ResponsesObject "Job state after cancel" @Failure 404 {object} APIError "Response id not found" @Security BearerAuth @Router /v1/responses/{id}/cancel [post]
func NewResponseGetHandler ¶ added in v0.7.0
NewResponseGetHandler returns an http.Handler for GET /v1/responses/{id}.
Returns the current state of a background job as a ResponsesObject. While the job is in-progress, Output / OutputText are empty. After the job terminates (completed / failed / cancelled), the response is fully populated and identical to what a synchronous POST would return.
@Summary Retrieve a Response by id @Description Polls the state of a background `/v1/responses` job. Submit a @Description response with `"background": true` to get back a 202 + `id`, then @Description GET `/v1/responses/{id}` until `status` is terminal @Description (`completed`, `failed`, `cancelled`). @Tags responses @Produce json @Param id path string true "Response id (resp_...)" @Success 200 {object} ResponsesObject "Current state of the response" @Failure 404 {object} APIError "Response id not found (never existed or evicted)" @Security BearerAuth @Router /v1/responses/{id} [get]
func NewResponsesHandler ¶ added in v0.6.0
func NewResponsesHandler(exec Executor, store *JobStore, logPrompts bool, systemPrompt string) http.Handler
NewResponsesHandler returns an http.Handler for POST /v1/responses.
@Summary Create a model response (Responses API) @Description OpenAI-compatible Responses API endpoint. Accepts a request shaped like @Description `client.responses.create` from the official SDKs and returns a Response object. @Description @Description The `model` field selects the agent (same routing as `/v1/chat/completions`): @Description - `"anthropic/sonnet"`, `"claude/<id>"` — routes to the Claude Code CLI @Description - `"opencode"` — routes to the OpenCode CLI @Description @Description The `input` field is either a string or an array of input items @Description (`{"role": "user|assistant|system", "content": "..."}` or with typed content parts). @Description The `instructions` field is prepended as a system message. @Description @Description **Statelessness:** devcell does not persist responses. `previous_response_id` @Description is accepted for compatibility but ignored — clients must send full conversation @Description history every request. @Description @Description **Streaming** is supported for the claude agent: set `"stream": true` and the @Description handler returns Server-Sent Events with token-level deltas. Opencode falls back to @Description the buffered path even when stream is set. @Description @Description **Background mode** is supported: set `"background": true` to receive `202 Accepted` @Description with a stub response containing only `id` and `status: "in_progress"`. Poll @Description `GET /v1/responses/{id}` until `status` is terminal (`completed`, `failed`, or @Description `cancelled`). Use `POST /v1/responses/{id}/cancel` to abort in-progress jobs. @Description Combining `stream: true` with `background: true` returns 400 — pick one mode. @Description @Description **Honored fields beyond core:** @Description - `reasoning.effort` (`low` / `medium` / `high`) → maps to the `claude --effort` CLI flag @Description to control thinking budget. Other values (including Claude's `xhigh`/`max`) are silently dropped. @Description @Description **Unsupported fields** (`tools`, `response_format`, `temperature`, `top_p`, @Description `max_output_tokens`, `metadata`, `store`, `service_tier`, etc.) are accepted to keep @Description SDK clients happy but have no effect — devcell shells out to a CLI agent and @Description cannot honor them. @Description @Description **Example request:** @Description ```json @Description {"model": "anthropic/sonnet", "input": "What is 2+2?"} @Description ``` @Description @Description **Reading the response:** most clients use the top-level `output_text` field. @Description SDK clients use the `output[].content[].text` structure. @Tags responses @Accept json @Produce json @Param request body ResponsesRequest true "Responses API request" @Success 200 {object} ResponsesObject "Successful response (synchronous)" @Success 202 {object} ResponsesObject "Background job accepted; poll GET /v1/responses/{id}" @Failure 400 {object} APIError "Invalid JSON, missing model/input, unknown model, or unsupported stream+background combination" @Failure 401 {string} string "Missing or invalid Bearer token" @Failure 405 {object} APIError "Only POST is allowed" @Security BearerAuth @Router /v1/responses [post]
func NewSOAPReconnectStub ¶ added in v0.8.0
func ReadClaudeCredentials ¶
ReadClaudeCredentials reads the OAuth access token from Claude's credentials file.
func WorkspaceRoutes ¶ added in v0.8.0
func WorkspaceRoutes(enum CellEnumerator, publicHost string, opts ...WorkspaceOpt) *http.ServeMux
Types ¶
type APIError ¶ added in v0.6.0
type APIError struct {
Error APIErrorBody `json:"error"`
}
APIError is the OpenAI-shaped error envelope returned by /v1/responses for HTTP-level errors (4xx / 5xx).
type APIErrorBody ¶ added in v0.6.0
type APIErrorBody struct {
Message string `json:"message" example:"streaming is not supported"`
Type string `json:"type" example:"invalid_request_error"`
Code string `json:"code,omitempty" example:"streaming_unsupported"`
}
APIErrorBody is the inner error object.
type AnthropicClient ¶
AnthropicClient abstracts Anthropic API calls for testability.
type Cell ¶ added in v0.8.0
type Cell struct {
ID string
Title string
Host string
Port int
Type string // "Desktop" or "RemoteApp"
AppProgram string // RemoteApp program alias (e.g. "||xterm")
}
func MockResources ¶ added in v0.8.0
func MockResources() []Cell
type CellEnumerator ¶ added in v0.8.0
type CellEnumerator interface {
ListCells() []Cell
}
type ChatChoice ¶
type ChatChoice struct {
// Index of this choice (always 0 — single-choice responses).
Index int `json:"index" example:"0"`
// The assistant's response message.
Message ChatMessage `json:"message"`
// Finish reason: "stop" on success, "error" if the agent exited non-zero.
FinishReason string `json:"finish_reason" example:"stop"`
}
ChatChoice is a single choice in the response.
type ChatMessage ¶
type ChatMessage struct {
// Role of the message author: "system", "user", or "assistant".
Role string `json:"role" example:"user"`
// The message content (prompt text for user, response text for assistant).
Content string `json:"content" example:"Explain the main function in this repo"`
}
ChatMessage is an OpenAI-compatible message.
type ChatRequest ¶
type ChatRequest struct {
// Model selects the agent. Use "claude", "anthropic", or "opencode" as a prefix.
// Append a sub-model with a slash: "claude/claude-sonnet-4-5".
Model string `json:"model" example:"claude"`
// Messages is the conversation history. The last user message is used as the prompt.
Messages []ChatMessage `json:"messages"`
// ReasoningEffort, when set, controls Claude's thinking budget for this request.
// Valid values: "low", "medium", "high". Other values are silently dropped.
// Maps to the `claude --effort` CLI flag. Has no effect on the opencode agent.
ReasoningEffort string `json:"reasoning_effort,omitempty" example:"high"`
// Stream, when true, emits Server-Sent Events with token-level deltas.
// Supported only for the claude agent (opencode falls back to buffered).
Stream bool `json:"stream,omitempty" example:"false"`
// StreamOptions configures streaming behavior. Honored only when Stream is true.
StreamOptions *ChatStreamOptions `json:"stream_options,omitempty"`
}
ChatRequest is the OpenAI-compatible chat completions request.
type ChatResponse ¶
type ChatResponse struct {
// Unique completion ID (format: chatcmpl-<hex>).
ID string `json:"id" example:"chatcmpl-a1b2c3d4e5f6"`
// Object type (always "chat.completion").
Object string `json:"object" example:"chat.completion"`
// Unix timestamp of when the response was created.
Created int64 `json:"created" example:"1714000000"`
// The model that was requested.
Model string `json:"model" example:"claude"`
// Response choices (always a single element).
Choices []ChatChoice `json:"choices"`
// Token usage (stubbed, reserved for future use).
Usage ChatUsage `json:"usage"`
}
ChatResponse is the OpenAI-compatible chat completions response.
type ChatStreamChoice ¶ added in v0.6.0
type ChatStreamChoice struct {
Index int `json:"index"`
Delta ChatStreamDelta `json:"delta"`
FinishReason *string `json:"finish_reason"`
}
ChatStreamChoice is one element of a chat.completion.chunk's choices array. The `delta` carries the incremental content; `finish_reason` is null on every chunk except the last.
type ChatStreamChunk ¶ added in v0.6.0
type ChatStreamChunk struct {
ID string `json:"id"`
Object string `json:"object"` // always "chat.completion.chunk"
Created int64 `json:"created"`
Model string `json:"model"`
Choices []ChatStreamChoice `json:"choices"`
Usage *ChatUsage `json:"usage,omitempty"`
}
ChatStreamChunk is the JSON payload of one SSE `data:` frame.
type ChatStreamDelta ¶ added in v0.6.0
type ChatStreamDelta struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
}
ChatStreamDelta is the OpenAI per-chunk delta. Role is sent on the first chunk; content on each text-delta chunk; both empty on the terminal chunk.
type ChatStreamOptions ¶ added in v0.6.0
type ChatStreamOptions struct {
// IncludeUsage, when true, embeds the token-usage object in the
// final SSE chunk. Off by default to match OpenAI's contract.
IncludeUsage bool `json:"include_usage,omitempty" example:"true"`
}
ChatStreamOptions mirrors OpenAI's stream_options object.
type ChatUsage ¶
type ChatUsage struct {
PromptTokens int `json:"prompt_tokens" example:"42"`
CompletionTokens int `json:"completion_tokens" example:"7"`
TotalTokens int `json:"total_tokens" example:"49"`
}
ChatUsage tracks token usage. Populated from claude --output-format=json (input + cache_creation + cache_read merged into prompt_tokens to match OpenAI semantics). Zero-valued for opencode and for claude paths where JSON parsing fell back to raw stdout.
type CompositeEnumerator ¶ added in v0.8.0
type CompositeEnumerator struct {
// contains filtered or unexported fields
}
func NewCompositeEnumerator ¶ added in v0.8.0
func NewCompositeEnumerator(sources ...CellEnumerator) *CompositeEnumerator
func (*CompositeEnumerator) ListCells ¶ added in v0.8.0
func (c *CompositeEnumerator) ListCells() []Cell
type DockerAPIClient ¶ added in v0.8.0
type DockerEnumerator ¶ added in v0.8.0
type DockerEnumerator struct {
// contains filtered or unexported fields
}
func NewDockerEnumerator ¶ added in v0.8.0
func NewDockerEnumerator() (*DockerEnumerator, error)
func (*DockerEnumerator) ListCells ¶ added in v0.8.0
func (d *DockerEnumerator) ListCells() []Cell
type ExecOpts ¶ added in v0.6.0
type ExecOpts struct {
// Agent is the binary name ("claude" or "opencode").
Agent string
// Prompt is the assembled prompt string passed via -p / positional arg.
Prompt string
// Model is the optional sub-model (e.g. "sonnet" or "claude-sonnet-4-5"). Empty = agent default.
Model string
// Effort, when set, is passed as --effort to the claude CLI.
// Valid values: "low", "medium", "high". Empty = CLI default.
Effort string
// SystemPrompt, when set, is passed as --append-system-prompt to claude.
// Operator-level baseline (set on `cell serve` startup), composes with
// any per-request `instructions` / `system` role from the OpenAI body —
// it does NOT override them. Ignored for opencode (no equivalent flag).
SystemPrompt string
}
ExecOpts is the bundle of arguments passed to Executor.Run.
Adding a new CLI-flag mapping (e.g. --max-budget-usd) means adding a field here rather than widening Run's signature.
type ExecResult ¶
type ExecResult struct {
Stdout string
Stderr string
ExitCode int
// Usage carries token-and-cost telemetry parsed from the agent's
// machine-readable output (claude --output-format=json today).
// Zero-valued when the agent doesn't emit usage data (opencode) or
// when JSON parsing falls back to raw stdout.
Usage Usage
}
ExecResult holds the output of an agent execution.
type Executor ¶
type Executor interface {
Run(opts ExecOpts) ExecResult
}
Executor runs an agent command and returns the result.
type HealthResponse ¶ added in v0.6.0
type HealthResponse struct {
// Server status — "ok" when the server is running and ready to accept requests.
Status string `json:"status" example:"ok"`
// Composite version string matching `cell --version` output.
// Format: `<version>-<build_date>-<commit>`.
Version string `json:"version" example:"v0.1.0-2026-04-26-abc1234"`
// Semantic version tag from the build (e.g. "v0.1.0").
VersionTag string `json:"version_tag" example:"v0.1.0"`
// Git commit hash this binary was built from.
Commit string `json:"commit" example:"abc1234"`
// Build date (UTC).
BuildDate string `json:"build_date" example:"2026-04-26"`
}
HealthResponse is the health check response body.
Version fields are injected at build time via -ldflags by the `task cell:build` / `task swag:generate` flow. An unbuilt-via-task binary will report defaults: version=v0.0.0, commit=none, build_date=unknown.
type HostingTerminalServer ¶ added in v0.8.0
type HostingTerminalServer struct {
ResourceFile ResourceFile `xml:"ResourceFile"`
TerminalServerRef TerminalServerRef `xml:"TerminalServerRef"`
}
type HostingTerminalServers ¶ added in v0.8.0
type HostingTerminalServers struct {
HTS []HostingTerminalServer `xml:"HostingTerminalServer"`
}
type IconElement ¶ added in v0.8.0
type Icons ¶ added in v0.8.0
type Icons struct {
IconRaw *IconElement `xml:"IconRaw,omitempty"`
Icon32 *IconElement `xml:"Icon32,omitempty"`
Icon256 *IconElement `xml:"Icon256,omitempty"`
}
type Job ¶ added in v0.7.0
type Job struct {
ID string
Status string // "in_progress" | "completed" | "failed" | "cancelled"
Result *ResponsesObject
Cancel context.CancelFunc
CreatedAt time.Time
FinishedAt time.Time
}
Job tracks the state of a single background /v1/responses request.
Jobs are created with status "in_progress" when a client submits a request with "background": true. A goroutine runs the agent, populates Result, and transitions Status to "completed" / "failed". Clients poll GET /v1/responses/{id} to retrieve the final ResponsesObject.
type JobStore ¶ added in v0.7.0
type JobStore struct {
// contains filtered or unexported fields
}
JobStore is the in-memory job registry for background responses.
Single-process cell serve, so a mutex-protected map is sufficient. Jobs survive only while the server runs — clients should not assume cross-restart durability.
func NewJobStore ¶ added in v0.7.0
func NewJobStore() *JobStore
NewJobStore returns an empty JobStore.
func (*JobStore) Cancel ¶ added in v0.7.0
Cancel marks the job as cancelled and triggers its context cancel func. Returns (snapshot, true) if the job was found, (Job{}, false) otherwise. Idempotent: cancelling an already-cancelled job is a no-op success.
func (*JobStore) Complete ¶ added in v0.7.0
func (s *JobStore) Complete(id string, status string, result *ResponsesObject)
Complete transitions the job to a terminal state with the given result. status must be "completed" or "failed".
func (*JobStore) Create ¶ added in v0.7.0
func (s *JobStore) Create(id string, cancel context.CancelFunc) *Job
Create inserts a new job in "in_progress" state and returns it. cancel is the context.CancelFunc bound to the goroutine running the agent.
func (*JobStore) Get ¶ added in v0.7.0
Get returns a snapshot of the job by id, or (Job{}, false) if not found. The snapshot is a value copy taken under the read lock; callers can read its fields without further synchronization. Mutations to the snapshot do not affect the stored job.
func (*JobStore) Sweep ¶ added in v0.7.0
Sweep evicts terminal jobs whose FinishedAt is older than now-ttl. In-progress jobs are never evicted regardless of age. Returns the count of evicted entries.
Intended for periodic invocation from a goroutine bound to the server lifetime; the caller chooses cadence and ttl.
type LookPathFunc ¶
LookPathFunc matches exec.LookPath signature.
type MockEnumerator ¶ added in v0.8.0
type MockEnumerator struct{}
func NewMockEnumerator ¶ added in v0.8.0
func NewMockEnumerator() *MockEnumerator
func (*MockEnumerator) ListCells ¶ added in v0.8.0
func (m *MockEnumerator) ListCells() []Cell
type ModelInfo ¶
type ModelInfo struct {
// Model identifier — use this value in the chat completions "model" field.
ID string `json:"id" example:"anthropic/claude-sonnet-4-5-20250514"`
// Object type (always "model").
Object string `json:"object" example:"model"`
// Unix timestamp when the model was discovered.
Created int64 `json:"created" example:"1714000000"`
// Owner of the model: "anthropic" for API-discovered models, "devcell" for local agents.
OwnedBy string `json:"owned_by" example:"anthropic"`
}
ModelInfo represents a single model in the OpenAI /v1/models response.
func DiscoverModels ¶
func DiscoverModels(lookPath LookPathFunc, ac AnthropicClient) []ModelInfo
DiscoverModels probes for installed agent binaries and returns available models. When claude is found, tries the Anthropic API first (via credentials), falls back to hardcoded aliases.
func FetchAnthropicModels ¶
FetchAnthropicModels hits the Anthropic API to get available models. Returns nil, nil if token is empty (no-op).
type ModelsResponse ¶
type ModelsResponse struct {
// Object type (always "list").
Object string `json:"object" example:"list"`
// Available models discovered from installed agents and the Anthropic API.
Data []ModelInfo `json:"data"`
}
ModelsResponse is the OpenAI-compatible /v1/models response.
type RealAnthropicClient ¶
type RealAnthropicClient struct {
CredentialsPath string // path to .credentials.json
APIURL string // override for testing; defaults to AnthropicAPIURL
}
RealAnthropicClient reads credentials and hits the Anthropic API.
func (*RealAnthropicClient) FetchModels ¶
func (c *RealAnthropicClient) FetchModels() ([]ModelInfo, error)
FetchModels reads the Claude OAuth token and fetches models from the Anthropic API.
type Resource ¶ added in v0.8.0
type Resource struct {
ID string `xml:"ID,attr"`
Alias string `xml:"Alias,attr"`
Title string `xml:"Title,attr"`
LastUpdated string `xml:"LastUpdated,attr"`
Type string `xml:"Type,attr"`
ShowByDefault string `xml:"ShowByDefault,attr"`
Icons Icons `xml:"Icons"`
FileExtensions string `xml:"FileExtensions"`
Folders Folders `xml:"Folders"`
HostingTerminalServers HostingTerminalServers `xml:"HostingTerminalServers"`
}
type ResourceCollection ¶ added in v0.8.0
type ResourceCollection struct {
XMLName xml.Name `xml:"http://schemas.microsoft.com/ts/2007/05/tswf ResourceCollection"`
PubDate string `xml:"PubDate,attr"`
SchemaVersion string `xml:"SchemaVersion,attr"`
SupportsReconnect string `xml:"SupportsReconnect,attr,omitempty"`
Publisher Publisher `xml:"Publisher"`
}
type ResourceFile ¶ added in v0.8.0
type ResponsesError ¶ added in v0.6.0
type ResponsesError struct {
// Short error code, e.g. "server_error".
Code string `json:"code,omitempty" example:"server_error"`
// Human-readable message — typically the agent's stderr.
Message string `json:"message" example:"agent failed"`
}
ResponsesError describes a model-side failure (exit != 0 from the agent CLI).
Note: HTTP-level errors (400, 401, 405) use a different envelope at the top of the response — see APIError.
type ResponsesIncompleteDetails ¶ added in v0.6.0
type ResponsesIncompleteDetails struct {
Reason string `json:"reason,omitempty"`
}
ResponsesIncompleteDetails is reserved — always null in devcell.
type ResponsesInputTokensDetails ¶ added in v0.6.0
type ResponsesInputTokensDetails struct {
CachedTokens int `json:"cached_tokens" example:"0"`
}
ResponsesInputTokensDetails carries the cached-input breakdown.
type ResponsesObject ¶ added in v0.6.0
type ResponsesObject struct {
// Unique response ID (format: resp_<hex>).
ID string `json:"id" example:"resp_a1b2c3d4e5f6"`
// Object type — always "response".
Object string `json:"object" example:"response"`
// Unix timestamp (seconds) when the response was created.
CreatedAt int64 `json:"created_at" example:"1714000000"`
// Status: "completed" on success, "failed" if the agent exited non-zero.
Status string `json:"status" example:"completed"`
// Model echo of the requested model string.
Model string `json:"model" example:"anthropic/sonnet"`
// Output items generated by the model.
Output []ResponsesOutputItem `json:"output"`
// Convenience field: concatenation of all output_text parts in Output.
// Most clients (n8n, simple scripts) read this directly.
OutputText string `json:"output_text" example:"Hello, world!"`
// Token usage (stubbed at zero — reserved for future use).
Usage ResponsesUsage `json:"usage"`
// Error populated when Status == "failed", null otherwise.
Error *ResponsesError `json:"error"`
// IncompleteDetails is reserved — always null.
IncompleteDetails *ResponsesIncompleteDetails `json:"incomplete_details"`
// Echo of the input instructions, or null if not set.
Instructions *string `json:"instructions"`
// Echo of input metadata, or null.
Metadata map[string]string `json:"metadata"`
// ParallelToolCalls — echo of input or default true.
ParallelToolCalls bool `json:"parallel_tool_calls" example:"true"`
// PreviousResponseID — always null (stateless).
PreviousResponseID *string `json:"previous_response_id"`
// Reasoning config — echoes the input reasoning object (with normalized
// effort if it was applied), or null if no reasoning was sent.
Reasoning *ResponsesReasoningConfig `json:"reasoning"`
// Store flag — echo of input or default true.
Store bool `json:"store" example:"true"`
// Sampling temperature — echo of input or default 1.0 (devcell ignores it).
Temperature float64 `json:"temperature" example:"1.0"`
// ToolChoice — always "auto".
ToolChoice string `json:"tool_choice" example:"auto"`
// Tools — always empty array (no tools bridged).
Tools []any `json:"tools"`
// Top-p — echo of input or default 1.0 (devcell ignores it).
TopP float64 `json:"top_p" example:"1.0"`
// Truncation — "disabled" by default.
Truncation string `json:"truncation" example:"disabled"`
// User — echo of input or empty.
User string `json:"user,omitempty"`
}
ResponsesObject is the OpenAI Responses-API response body.
type ResponsesOutputContentPart ¶ added in v0.6.0
type ResponsesOutputContentPart struct {
// Always "output_text" for text responses.
Type string `json:"type" example:"output_text"`
// The actual text.
Text string `json:"text" example:"Hello, world!"`
// Annotations on the text (always empty — reserved for future use).
Annotations []any `json:"annotations"`
}
ResponsesOutputContentPart is one part of an output message's content array.
type ResponsesOutputItem ¶ added in v0.6.0
type ResponsesOutputItem struct {
// Item type — always "message".
Type string `json:"type" example:"message"`
// Unique item ID (format: msg_<hex>).
ID string `json:"id" example:"msg_a1b2c3d4e5f6"`
// Status of this item — "completed" on success.
Status string `json:"status" example:"completed"`
// Role of the message author — always "assistant".
Role string `json:"role" example:"assistant"`
// Content parts of the message.
Content []ResponsesOutputContentPart `json:"content"`
}
ResponsesOutputItem is a single item in the output array.
devcell only emits "message" items (assistant messages). Reasoning, tool_call, and other variants are not produced.
type ResponsesOutputTokensDetails ¶ added in v0.6.0
type ResponsesOutputTokensDetails struct {
ReasoningTokens int `json:"reasoning_tokens" example:"0"`
}
ResponsesOutputTokensDetails is reserved for reasoning-model splits (reasoning_tokens vs visible output). Always zero today; included for schema completeness.
type ResponsesReasoningConfig ¶ added in v0.6.0
type ResponsesReasoningConfig struct {
// Effort: "low", "medium", or "high". Other values are silently dropped.
Effort string `json:"effort,omitempty" example:"high"`
// Summary controls reasoning summary verbosity. Accepted, ignored.
Summary string `json:"summary,omitempty"`
// GenerateSummary is a deprecated alias of Summary. Accepted, ignored.
GenerateSummary string `json:"generate_summary,omitempty"`
}
ResponsesReasoningConfig is the OpenAI-spec reasoning object.
The Responses API allows clients to control thinking budget on reasoning models. devcell honors `effort` (mapping it to `claude --effort`) and ignores other fields.
type ResponsesRequest ¶ added in v0.6.0
type ResponsesRequest struct {
// Model selects the agent. Use "claude", "anthropic", or "opencode" as a prefix.
// Append a sub-model with a slash: "anthropic/sonnet" or "anthropic/claude-sonnet-4-5".
Model string `json:"model" example:"anthropic/sonnet"`
// Input is either a string OR an array of input items.
// String form: a single user message.
// Array form: a multi-turn conversation; each item has role + content.
Input json.RawMessage `json:"input" swaggertype:"string" example:"hello"`
// Instructions is an optional system prompt prepended to the conversation.
Instructions string `json:"instructions,omitempty" example:"be brief"`
// Stream, if true, returns 400 — streaming is not supported.
Stream bool `json:"stream,omitempty"`
// Reasoning carries reasoning-model controls. Only `reasoning.effort`
// (low|medium|high) is honored — it maps to `claude --effort`. Other
// fields (summary, generate_summary) are accepted and ignored.
Reasoning *ResponsesReasoningConfig `json:"reasoning,omitempty"`
// Accepted, ignored — kept for client compatibility.
PreviousResponseID string `json:"previous_response_id,omitempty"`
Tools json.RawMessage `json:"tools,omitempty" swaggerignore:"true"`
ToolChoice json.RawMessage `json:"tool_choice,omitempty" swaggerignore:"true"`
ResponseFormat json.RawMessage `json:"response_format,omitempty" swaggerignore:"true"`
Text json.RawMessage `json:"text,omitempty" swaggerignore:"true"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
MaxOutputTokens *int `json:"max_output_tokens,omitempty"`
Metadata json.RawMessage `json:"metadata,omitempty" swaggerignore:"true"`
Store *bool `json:"store,omitempty"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
Truncation string `json:"truncation,omitempty"`
User string `json:"user,omitempty"`
Background *bool `json:"background,omitempty"`
Include []string `json:"include,omitempty"`
}
ResponsesRequest is the OpenAI Responses-API request body.
All fields are accepted, but only Model, Input, Instructions, and Stream affect behavior. Tools, ResponseFormat, Reasoning, sampling params and other fields are decoded for compatibility and silently ignored — devcell shells out to a CLI agent and cannot honor them.
type ResponsesUsage ¶ added in v0.6.0
type ResponsesUsage struct {
InputTokens int `json:"input_tokens" example:"42"`
InputTokensDetails *ResponsesInputTokensDetails `json:"input_tokens_details,omitempty"`
OutputTokens int `json:"output_tokens" example:"7"`
OutputTokensDetails *ResponsesOutputTokensDetails `json:"output_tokens_details,omitempty"`
TotalTokens int `json:"total_tokens" example:"49"`
}
ResponsesUsage tracks token usage. Populated from claude --output-format=json. The Responses API has its own naming (input_tokens / output_tokens) and exposes cached-token detail under input_tokens_details.cached_tokens — that's claude's cache_read_input_tokens and is real money saved, so we surface it.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the devcell HTTP API server.
func NewServer ¶
NewServer creates a Server. Use port=0 to let the OS pick a free port. Uses exec.LookPath for model discovery and RealAnthropicClient by default.
func (*Server) SetAnthropicClient ¶
func (s *Server) SetAnthropicClient(ac AnthropicClient)
SetAnthropicClient overrides the Anthropic API client (for testing).
func (*Server) SetLogPrompts ¶ added in v0.6.0
SetLogPrompts enables or disables full prompt + response body logging.
When true, /v1/chat/completions and /v1/responses handlers log the assembled prompt and the model's reply at INFO level. Off by default — prompts often contain secrets, PII, or large pasted content from upstream tools (n8n flows, agents, etc.), so this is opt-in.
func (*Server) SetLookPath ¶
func (s *Server) SetLookPath(fn LookPathFunc)
SetLookPath overrides the binary discovery function (for testing).
func (*Server) SetSystemPrompt ¶ added in v0.6.0
SetSystemPrompt sets the operator-level system prompt passed to claude as --append-system-prompt on every /v1/chat/completions and /v1/responses request. Empty disables the flag (default). Composes with — does not override — any per-request `instructions` / `system` role from the body.
func (*Server) SetWorkspace ¶ added in v0.8.0
type ShellExecutor ¶
type ShellExecutor struct{}
ShellExecutor runs agent binaries as subprocesses.
func (*ShellExecutor) Run ¶
func (e *ShellExecutor) Run(opts ExecOpts) ExecResult
Run executes the agent binary with the given options.
type StreamEvent ¶ added in v0.6.0
type StreamEvent struct {
Kind StreamEventKind
// MessageID populated on MessageStart (the upstream message id).
MessageID string
// Model populated on MessageStart.
Model string
// Delta populated on TextDelta — incremental text since the previous
// delta, not cumulative.
Delta string
// StopReason populated on MessageStop — "end_turn" / "max_tokens" / …
StopReason string
// Final populated on Result — reuses claude_json.go's typed envelope
// for the terminal usage + cost payload.
Final *claudeJSONResult
// Err populated on Error — terminal; the caller should stop reading.
Err error
}
StreamEvent is the canonical, OpenAI-agnostic event the SSE formatters consume. One source (claude scanner), two sinks (Chat Completions and Responses).
type StreamEventKind ¶ added in v0.6.0
type StreamEventKind int
StreamEventKind discriminates the canonical event sent on the channel.
const ( StreamEventMessageStart StreamEventKind StreamEventTextDelta StreamEventMessageStop StreamEventResult StreamEventError )
type TerminalServer ¶ added in v0.8.0
type TerminalServerRef ¶ added in v0.8.0
type TerminalServerRef struct {
Ref string `xml:"Ref,attr"`
}
type TerminalServers ¶ added in v0.8.0
type TerminalServers struct {
TS []TerminalServer `xml:"TerminalServer"`
}
type Usage ¶ added in v0.6.0
type Usage struct {
InputTokens int
OutputTokens int
CacheCreationInputTokens int
CacheReadInputTokens int
// TotalCostUSD is what claude reports; opencode doesn't surface cost.
TotalCostUSD float64
}
Usage is the agent-side token/cost view for one request. Mapped into OpenAI's per-API shape (ChatUsage / ResponsesUsage) at handler time.
Field naming follows Anthropic's wire format so the parser is a 1:1 JSON decode of claude's `usage` object — the OpenAI mapping (prompt_tokens = input + cache_creation + cache_read; completion = output) is done in the handlers, not here, so other agents can plug in with their own native shape.
type WorkspaceOpt ¶ added in v0.8.0
type WorkspaceOpt func(*workspaceCfg)
func WithCertDER ¶ added in v0.8.0
func WithCertDER(der []byte) WorkspaceOpt
Source Files
¶
- anthropic.go
- auth.go
- claude_json.go
- claude_stream.go
- exec.go
- exec_stream.go
- handler.go
- jobs.go
- logging.go
- models.go
- responses.go
- responses_get.go
- server.go
- sse_chat.go
- sse_responses.go
- tls.go
- workspace_discovery.go
- workspace_docker.go
- workspace_enumerator.go
- workspace_handler.go
- workspace_icon.go
- workspace_rdp.go
- workspace_soap.go
- workspace_xml.go