Documentation
¶
Overview ¶
Package stromboli provides a Go SDK for the Stromboli API.
Stromboli is a container orchestration service for Claude Code agents, enabling isolated execution of Claude prompts in Podman containers. This SDK provides a clean, idiomatic Go interface to interact with the Stromboli API.
Installation ¶
To install the SDK, use go get:
go get github.com/tomblancdev/stromboli-go
Quick Start ¶
Create a client and execute a prompt:
package main
import (
"context"
"fmt"
"log"
"github.com/tomblancdev/stromboli-go"
)
func main() {
// Create a new client
client, err := stromboli.NewClient("http://localhost:8585")
if err != nil {
log.Fatal(err)
}
// Check API health
health, err := client.Health(context.Background())
if err != nil {
log.Fatal(err)
}
fmt.Printf("API Status: %s (v%s)\n", health.Status, health.Version)
}
Client Configuration ¶
The client can be configured using functional options:
client, err := stromboli.NewClient("http://localhost:8585",
stromboli.WithTimeout(5*time.Minute),
stromboli.WithHTTPClient(customHTTPClient),
)
if err != nil {
log.Fatal(err)
}
Streaming ¶
For real-time output, use the Stream method:
stream, err := client.Stream(ctx, &stromboli.StreamRequest{
Prompt: "Write a story",
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for stream.Next() {
fmt.Print(stream.Event().Data)
}
Authentication ¶
For authenticated endpoints, obtain and set a token:
tokens, _ := client.GetToken(ctx, "client-id") client.SetToken(tokens.AccessToken) // Now authenticated methods work validation, _ := client.ValidateToken(ctx)
Error Handling ¶
The SDK provides typed errors for common failure cases:
result, err := client.Run(ctx, req)
if err != nil {
var apiErr *stromboli.Error
if errors.As(err, &apiErr) {
switch apiErr.Code {
case "NOT_FOUND":
// Handle not found
case "TIMEOUT":
// Handle timeout
}
}
}
Architecture ¶
The SDK is built in two layers:
- Wrapper Layer: Clean, idiomatic Go API with context support and typed error handling (this package)
- Generated Layer: Auto-generated HTTP client from OpenAPI spec (github.com/tomblancdev/stromboli-go/generated)
Users should only interact with the wrapper layer. The generated layer is an implementation detail and may change between versions.
Thread Safety ¶
The Client is safe for concurrent use by multiple goroutines. Each method call is independent and does not share state.
API Version Compatibility ¶
This SDK version targets Stromboli API v0.4.0-alpha. Compatibility with other API versions is not guaranteed. Use Client.Health to check the server version at runtime.
Index ¶
- Constants
- Variables
- func IsCompatible(serverVersion string) bool
- func MustBeCompatible(serverVersion string)
- func SetLogger(l Logger)
- type AsyncRunResponse
- type ClaudeOptions
- type ClaudeStatus
- type Client
- func (c *Client) CancelJob(ctx context.Context, jobID string) error
- func (c *Client) ClaudeStatus(ctx context.Context) (*ClaudeStatus, error)
- func (c *Client) ClearToken()
- func (c *Client) CreateSecret(ctx context.Context, req *CreateSecretRequest) error
- func (c *Client) DeleteSecret(ctx context.Context, name string) error
- func (c *Client) DestroySession(ctx context.Context, sessionID string) error
- func (c *Client) GetImage(ctx context.Context, name string) (*Image, error)
- func (c *Client) GetJob(ctx context.Context, jobID string) (*Job, error)
- func (c *Client) GetMessage(ctx context.Context, sessionID, messageID string) (*Message, error)
- func (c *Client) GetMessages(ctx context.Context, sessionID string, opts *GetMessagesOptions) (*MessagesResponse, error)
- func (c *Client) GetSecret(ctx context.Context, name string) (*Secret, error)
- func (c *Client) GetToken(ctx context.Context, clientID string) (*TokenResponse, error)
- func (c *Client) Health(ctx context.Context) (*HealthResponse, error)
- func (c *Client) ListImages(ctx context.Context) ([]*Image, error)
- func (c *Client) ListJobs(ctx context.Context) ([]*Job, error)
- func (c *Client) ListSecrets(ctx context.Context) ([]*Secret, error)
- func (c *Client) ListSessions(ctx context.Context) ([]string, error)
- func (c *Client) Logout(ctx context.Context) (*LogoutResponse, error)
- func (c *Client) PullImage(ctx context.Context, req *PullImageRequest) (*PullImageResponse, error)
- func (c *Client) RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)
- func (c *Client) Run(ctx context.Context, req *RunRequest) (*RunResponse, error)
- func (c *Client) RunAsync(ctx context.Context, req *RunRequest) (*AsyncRunResponse, error)
- func (c *Client) SearchImages(ctx context.Context, opts *SearchImagesOptions) ([]*ImageSearchResult, error)
- func (c *Client) SetToken(token string)
- func (c *Client) Stream(ctx context.Context, req *StreamRequest) (*Stream, error)
- func (c *Client) ValidateToken(ctx context.Context) (*TokenValidation, error)
- type CompatibilityResult
- type CompatibilityStatus
- type ComponentHealth
- type CrashInfo
- type CreateSecretRequest
- type EnvironmentConfig
- type Error
- type GetMessagesOptions
- type HealthResponse
- type Image
- type ImageSearchResult
- type Job
- type LifecycleHooks
- type Logger
- type LogoutResponse
- type Message
- type MessagesResponse
- type Model
- type Option
- func WithHTTPClient(httpClient *http.Client) Option
- func WithRequestHook(hook RequestHook) Option
- func WithResponseHook(hook ResponseHook) Option
- func WithRetries(n int) Optiondeprecated
- func WithStreamTimeout(d time.Duration) Option
- func WithTimeout(d time.Duration) Option
- func WithToken(token string) Option
- func WithUserAgent(userAgent string) Option
- type PodmanOptions
- type PullImageRequest
- type PullImageResponse
- type RequestHook
- type ResponseHook
- type RunRequest
- type RunResponse
- type SearchImagesOptions
- type Secret
- type Stream
- type StreamEvent
- type StreamRequest
- type TokenResponse
- type TokenValidation
Constants ¶
const ( // RunStatusCompleted indicates successful execution. RunStatusCompleted = "completed" // RunStatusError indicates execution failed. RunStatusError = "error" )
RunStatus constants for execution results.
const ( // StatusOK indicates the service or component is healthy. StatusOK = "ok" // StatusError indicates the service or component has an error. StatusError = "error" )
HealthStatus constants for convenience.
const ( // JobStatusPending indicates the job is queued but not yet started. JobStatusPending = "pending" // JobStatusRunning indicates the job is currently executing. JobStatusRunning = "running" // JobStatusCompleted indicates the job completed successfully. JobStatusCompleted = "completed" // JobStatusFailed indicates the job failed with an error. JobStatusFailed = "failed" // JobStatusCancelled indicates the job was cancelled. JobStatusCancelled = "cancelled" )
JobStatus constants for async job states.
Use these with [Job.Status]:
if job.Status == stromboli.JobStatusCompleted {
fmt.Println(job.Output)
}
const APIVersion = "0.4.0-alpha"
APIVersion is the target Stromboli API version this SDK was built for.
The SDK is tested against this API version and may not work correctly with significantly different API versions. Use Client.Health to check the actual server version at runtime.
const APIVersionRange = ">=0.4.0-alpha <0.5.0"
APIVersionRange defines the range of Stromboli API versions this SDK is compatible with, using semver constraint syntax.
Examples of constraint syntax:
- ">=1.0.0 <2.0.0" — versions 1.x.x
- "^1.2.3" — versions >=1.2.3 and <2.0.0
- "~1.2.3" — versions >=1.2.3 and <1.3.0
Use IsCompatible or CheckCompatibility to verify a server version.
const Version = "0.3.1-alpha"
Version is the current SDK version.
This version follows semantic versioning (https://semver.org/). The version is incremented according to the following rules:
- MAJOR: Breaking changes to the public API
- MINOR: New features, backwards compatible
- PATCH: Bug fixes, backwards compatible
Variables ¶
var ( // ErrNotFound indicates the requested resource does not exist. // Used for jobs, sessions, and other resources without specific not-found errors. // HTTP status: 404. ErrNotFound = &Error{ Code: "NOT_FOUND", Message: "resource not found", Status: 404, } // ErrTimeout indicates the request timed out. // This can occur for long-running operations or network issues. // HTTP status: 408. ErrTimeout = &Error{ Code: "TIMEOUT", Message: "request timed out", Status: 408, } // HTTP status: 401. ErrUnauthorized = &Error{ Code: "UNAUTHORIZED", Message: "invalid credentials", Status: 401, } // ErrBadRequest indicates invalid request parameters. // Check the error message for details about what was invalid. // HTTP status: 400. ErrBadRequest = &Error{ Code: "BAD_REQUEST", Message: "invalid request", Status: 400, } // ErrInternal indicates an internal server error. // This usually indicates a bug in the Stromboli server. // HTTP status: 500. ErrInternal = &Error{ Code: "INTERNAL", Message: "internal server error", Status: 500, } // Retry the request after a short delay. // HTTP status: 503. ErrUnavailable = &Error{ Code: "UNAVAILABLE", Message: "service temporarily unavailable", Status: 503, } // ErrSecretExists indicates a secret with this name already exists. // HTTP status: 409. ErrSecretExists = &Error{ Code: "SECRET_EXISTS", Message: "secret already exists", Status: 409, } // ErrInvalidSecretName indicates the secret name is invalid. // HTTP status: 400. ErrInvalidSecretName = &Error{ Code: "INVALID_SECRET_NAME", Message: "invalid secret name", Status: 400, } // ErrImageNotFound indicates the requested image was not found locally. // This is distinct from [ErrNotFound] to differentiate between local image // lookup failures and other resource not-found errors. // Use [Client.PullImage] to fetch the image from a registry. // HTTP status: 404. ErrImageNotFound = &Error{ Code: "IMAGE_NOT_FOUND", Message: "image not found", Status: 404, } // ErrImagePullFailed indicates the image pull operation failed. // HTTP status: 500. ErrImagePullFailed = &Error{ Code: "IMAGE_PULL_FAILED", Message: "failed to pull image", Status: 500, } // ErrRateLimited indicates too many requests were made. // HTTP status: 429. // // NOTE: The RetryAfter field is not automatically populated because // the go-swagger client doesn't expose response headers in error responses. // To capture the Retry-After header, use [WithResponseHook] to inspect // the response before the error is returned: // // var retryAfter time.Duration // client, _ := stromboli.NewClient(url, // stromboli.WithResponseHook(func(resp *http.Response) { // if resp.StatusCode == 429 { // if s := resp.Header.Get("Retry-After"); s != "" { // if seconds, err := strconv.Atoi(s); err == nil { // retryAfter = time.Duration(seconds) * time.Second // } // } // } // }), // ) ErrRateLimited = &Error{ Code: "RATE_LIMITED", Message: "too many requests", Status: 429, } )
Sentinel errors for common error conditions.
Use errors.Is to check for these errors:
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Resource not found")
}
Error Design
Generic errors (ErrNotFound, ErrTimeout, etc.) are used for most resources. Resource-specific errors exist where the failure mode is domain-specific:
- Images: ErrImageNotFound, ErrImagePullFailed - container image operations have distinct failure modes (local lookup vs registry pull)
- Secrets: ErrSecretExists, ErrInvalidSecretName - secret operations have specific validation and conflict rules
When checking for "not found" errors, use the specific error if available (e.g., ErrImageNotFound for images), or ErrNotFound for other resources.
Functions ¶
func IsCompatible ¶
IsCompatible checks if a server version is compatible with this SDK.
This is a convenience function that returns true if the version falls within APIVersionRange. Use CheckCompatibility for detailed results.
Example:
health, _ := client.Health(ctx)
if !stromboli.IsCompatible(health.Version) {
log.Printf("Warning: API %s may not be compatible", health.Version)
}
Returns false if the version string cannot be parsed.
func MustBeCompatible ¶
func MustBeCompatible(serverVersion string)
MustBeCompatible panics if the server version is not compatible.
Use this in initialization code where incompatibility should be fatal:
func main() {
client := stromboli.NewClient(url)
health, err := client.Health(ctx)
if err != nil {
log.Fatal(err)
}
stromboli.MustBeCompatible(health.Version)
// Continue with compatible server...
}
func SetLogger ¶
func SetLogger(l Logger)
SetLogger sets the logger used by the SDK for warnings and debug output. Pass nil to restore the default logger (standard log package). This function is safe for concurrent use.
Example:
// Use a custom logger stromboli.SetLogger(myLogger) // Restore default stromboli.SetLogger(nil)
Types ¶
type AsyncRunResponse ¶
type AsyncRunResponse struct {
// JobID is the unique identifier for the async job.
// Use this with [Client.GetJob] to check status.
// Example: "job-abc123def456"
JobID string `json:"job_id"`
}
AsyncRunResponse represents the result of starting an async execution.
Use the JobID to poll for completion with Client.GetJob:
async, err := client.RunAsync(ctx, req)
if err != nil {
log.Fatal(err)
}
// Poll for completion
for {
job, _ := client.GetJob(ctx, async.JobID)
if job.Status == "completed" {
fmt.Println(job.Output)
break
}
time.Sleep(time.Second)
}
type ClaudeOptions ¶
type ClaudeOptions struct {
// Model specifies the Claude model to use.
// Use the Model* constants: ModelHaiku, ModelSonnet, ModelOpus.
// Default: server-configured default (usually sonnet).
Model Model `json:"model,omitempty"`
// SessionID enables conversation continuation.
// Pass a previous response's SessionID to continue the conversation.
// Example: "550e8400-e29b-41d4-a716-446655440000"
SessionID string `json:"session_id,omitempty"`
// Resume continues an existing session (requires SessionID).
Resume bool `json:"resume,omitempty"`
// MaxBudgetUSD limits the API cost for this execution.
// Example: 5.0 means max $5 USD.
MaxBudgetUSD float64 `json:"max_budget_usd,omitempty"`
// SystemPrompt replaces the default system prompt entirely.
// Use AppendSystemPrompt to add to it instead.
SystemPrompt string `json:"system_prompt,omitempty"`
// AppendSystemPrompt adds to the default system prompt.
// Example: "Focus on security best practices"
AppendSystemPrompt string `json:"append_system_prompt,omitempty"`
// AllowedTools lists tools Claude can use.
// Supports patterns like "Bash(git:*)" for git commands only.
// Example: []string{"Read", "Bash(git:*)", "Edit"}
AllowedTools []string `json:"allowed_tools,omitempty"`
// DisallowedTools lists tools Claude cannot use.
// Example: []string{"Write", "Bash"}
DisallowedTools []string `json:"disallowed_tools,omitempty"`
// DangerouslySkipPermissions bypasses all permission checks.
// Only use in fully sandboxed environments.
// WARNING: This allows Claude to run any command without confirmation.
DangerouslySkipPermissions bool `json:"dangerously_skip_permissions,omitempty"`
// PermissionMode controls how permissions are handled.
// Values: "default", "acceptEdits", "bypassPermissions", "plan", "dontAsk"
PermissionMode string `json:"permission_mode,omitempty"`
// OutputFormat controls the response format.
// Values: "text", "json", "stream-json"
OutputFormat string `json:"output_format,omitempty"`
// JSONSchema specifies a JSON Schema for structured output validation.
// When set, Claude's output MUST conform to this schema.
// Requires OutputFormat to be "json" for best results.
//
// If the output does not match the schema, the API may return an error
// or Claude may retry to produce conforming output (behavior depends on
// the Stromboli server configuration).
//
// Example:
//
// &stromboli.ClaudeOptions{
// OutputFormat: "json",
// JSONSchema: `{
// "type": "object",
// "required": ["summary", "score"],
// "properties": {
// "summary": {"type": "string"},
// "score": {"type": "integer", "minimum": 0, "maximum": 100}
// }
// }`,
// }
//
// See: https://json-schema.org/specification
JSONSchema string `json:"json_schema,omitempty"`
// Verbose enables detailed logging.
Verbose bool `json:"verbose,omitempty"`
// Debug enables debug mode with optional category filter.
// Example: "api,hooks"
Debug string `json:"debug,omitempty"`
// Continue resumes the most recent conversation in workspace.
// Ignores SessionID if set.
Continue bool `json:"continue,omitempty"`
// Agent specifies a predefined agent configuration.
// Example: "reviewer"
Agent string `json:"agent,omitempty"`
// FallbackModel is used when the primary model is overloaded.
// Example: "haiku"
FallbackModel string `json:"fallback_model,omitempty"`
// AddDirs specifies additional directories for tool access.
// Example: []string{"/home/user/shared", "/data"}
AddDirs []string `json:"add_dirs,omitempty"`
// Agents specifies custom agents definition (JSON object).
// Example: map[string]interface{}{"reviewer": ...}
Agents map[string]interface{} `json:"agents,omitempty"`
// AllowDangerouslySkipPermissions enables bypass as an option without enabling by default.
AllowDangerouslySkipPermissions bool `json:"allow_dangerously_skip_permissions,omitempty"`
// Betas specifies beta headers for API requests.
// Example: []string{"interleaved-thinking-2025-05-14"}
Betas []string `json:"betas,omitempty"`
// DisableSlashCommands disables all slash commands/skills.
DisableSlashCommands bool `json:"disable_slash_commands,omitempty"`
// Files specifies file resources in format: file_id:path.
// Example: []string{"abc123:/workspace/file.txt"}
Files []string `json:"files,omitempty"`
// ForkSession creates a new session ID when resuming.
ForkSession bool `json:"fork_session,omitempty"`
// IncludePartialMessages includes partial message chunks (stream-json only).
IncludePartialMessages bool `json:"include_partial_messages,omitempty"`
// InputFormat specifies the input format: text, stream-json.
// Example: "text"
InputFormat string `json:"input_format,omitempty"`
// McpConfigs specifies MCP server config files or JSON strings.
// Example: []string{"/path/to/mcp.json"}
McpConfigs []string `json:"mcp_configs,omitempty"`
// NoPersistence prevents saving session to disk.
NoPersistence bool `json:"no_persistence,omitempty"`
// PluginDirs specifies plugin directories.
// Example: []string{"/home/user/.claude/plugins"}
PluginDirs []string `json:"plugin_dirs,omitempty"`
// ReplayUserMessages re-emits user messages on stdout.
ReplayUserMessages bool `json:"replay_user_messages,omitempty"`
// SettingSources specifies setting sources to load: user, project, local.
// Example: []string{"user", "project"}
SettingSources []string `json:"setting_sources,omitempty"`
// Settings specifies path to settings JSON file or JSON string.
// Example: "/path/to/settings.json"
Settings string `json:"settings,omitempty"`
// StrictMcpConfig only uses MCP servers from mcp_configs.
StrictMcpConfig bool `json:"strict_mcp_config,omitempty"`
// Tools specifies built-in tools ("", "default", or specific names).
// Example: []string{"Bash", "Read", "Edit"}
Tools []string `json:"tools,omitempty"`
}
ClaudeOptions configures Claude's behavior during execution.
All fields are optional. Use these to customize the model, set permissions, configure tools, and more.
Example:
&stromboli.ClaudeOptions{
Model: stromboli.ModelSonnet,
MaxBudgetUSD: 5.0,
AllowedTools: []string{"Read", "Bash(git:*)"},
DangerouslySkipPermissions: true,
}
type ClaudeStatus ¶
type ClaudeStatus struct {
// Configured indicates whether Claude credentials are set up.
// When false, execution requests will fail with an authentication error.
Configured bool `json:"configured"`
// Message provides additional context about the configuration status.
// When Configured is true: "Claude is configured"
// When Configured is false: explains what is missing
Message string `json:"message"`
}
ClaudeStatus represents the Claude configuration status.
Use Client.ClaudeStatus to check if Claude credentials are configured:
status, err := client.ClaudeStatus(ctx)
if err != nil {
log.Fatal(err)
}
if status.Configured {
fmt.Println("Claude is ready!")
} else {
fmt.Printf("Claude not configured: %s\n", status.Message)
}
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the Stromboli API client.
Client provides a clean, idiomatic Go interface to the Stromboli API. It wraps the auto-generated client with additional features:
- Context support for cancellation and timeouts
- Typed errors for common failure cases
- Simplified request/response types
Create a new client using NewClient:
client, err := stromboli.NewClient("http://localhost:8585")
if err != nil {
log.Fatal(err)
}
The client is safe for concurrent use by multiple goroutines.
Methods ¶
System:
- Client.Health: Check API health status
- Client.ClaudeStatus: Check Claude configuration status
Execution:
- Client.Run: Execute Claude synchronously
- Client.RunAsync: Execute Claude asynchronously (returns job ID)
Auth:
- Client.GetToken: Obtain JWT tokens
- Client.RefreshToken: Refresh access token
- Client.ValidateToken: Validate current token
- Client.Logout: Invalidate current token
Secrets:
- Client.ListSecrets: List available Podman secrets
func NewClient ¶
NewClient creates a new Stromboli API client.
The baseURL should be the full URL to the Stromboli API, including the protocol and port. Examples:
Returns an error if the URL is invalid or malformed.
Use functional options to customize the client:
client, err := stromboli.NewClient("http://localhost:8585",
stromboli.WithTimeout(5*time.Minute),
stromboli.WithHTTPClient(customHTTPClient),
)
if err != nil {
log.Fatal(err)
}
The returned client is safe for concurrent use.
func (*Client) CancelJob ¶
CancelJob cancels a pending or running job.
Use this method to stop a job that is no longer needed. Only pending and running jobs can be cancelled. Completed, failed, or already cancelled jobs cannot be cancelled (returns 409 Conflict error).
Example:
err := client.CancelJob(ctx, "job-abc123")
if err != nil {
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Job not found")
} else {
log.Fatal(err)
}
}
fmt.Println("Job cancelled successfully")
Cancel a job immediately after starting:
job, _ := client.RunAsync(ctx, req)
// Changed our mind, cancel it
err := client.CancelJob(ctx, job.JobID)
if err != nil {
log.Fatal(err)
}
func (*Client) ClaudeStatus ¶
func (c *Client) ClaudeStatus(ctx context.Context) (*ClaudeStatus, error)
ClaudeStatus returns the Claude configuration status.
Use this method to check if the Stromboli server has valid Claude credentials configured. If not configured, execution requests will fail.
Example:
status, err := client.ClaudeStatus(ctx)
if err != nil {
log.Fatalf("Failed to check Claude status: %v", err)
}
if !status.Configured {
log.Fatalf("Claude is not configured: %s", status.Message)
}
fmt.Println("Claude is ready for execution")
The context can be used to set a timeout or cancel the request:
ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() status, err := client.ClaudeStatus(ctx)
func (*Client) ClearToken ¶
func (c *Client) ClearToken()
ClearToken removes the Bearer token from the client.
This is equivalent to calling SetToken("") but more explicit. Use this after Client.Logout to clear local state.
Example:
client.Logout(ctx) client.ClearToken()
func (*Client) CreateSecret ¶
func (c *Client) CreateSecret(ctx context.Context, req *CreateSecretRequest) error
CreateSecret creates a new Podman secret.
Secrets can be used to securely pass sensitive data (API keys, tokens, etc.) to containers without exposing them in environment variables or command lines.
Example:
err := client.CreateSecret(ctx, &stromboli.CreateSecretRequest{
Name: "github-token",
Value: "ghp_xxxx...",
})
if err != nil {
log.Fatal(err)
}
Returns ErrSecretExists if a secret with this name already exists:
err := client.CreateSecret(ctx, req)
if errors.Is(err, stromboli.ErrSecretExists) {
fmt.Println("Secret already exists")
}
func (*Client) DeleteSecret ¶
DeleteSecret permanently deletes a Podman secret.
WARNING: This action cannot be undone. Secrets currently in use by running containers may cause those containers to fail.
Example:
err := client.DeleteSecret(ctx, "github-token")
if err != nil {
log.Fatal(err)
}
fmt.Println("Secret deleted")
Returns ErrNotFound if the secret doesn't exist:
err := client.DeleteSecret(ctx, "unknown-secret")
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Secret not found")
}
func (*Client) DestroySession ¶
DestroySession removes a session and all its stored data.
Use this method to clean up old sessions that are no longer needed. This operation is permanent and cannot be undone.
Example:
err := client.DestroySession(ctx, "sess-abc123")
if err != nil {
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Session not found")
} else {
log.Fatal(err)
}
}
fmt.Println("Session destroyed")
Bulk cleanup:
sessions, _ := client.ListSessions(ctx)
for _, id := range sessions {
if err := client.DestroySession(ctx, id); err != nil {
log.Printf("Failed to destroy %s: %v\n", id, err)
}
}
func (*Client) GetImage ¶
GetImage returns detailed information about a specific container image.
This includes all labels, compatibility information, and available tools.
Example:
image, err := client.GetImage(ctx, "python:3.12-slim")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Image: %s\n", image.ID)
fmt.Printf("Compatible: %v\n", image.Compatible)
fmt.Printf("Tools: %v\n", image.Tools)
Returns ErrImageNotFound if the image doesn't exist locally:
image, err := client.GetImage(ctx, "nonexistent:latest")
if errors.Is(err, stromboli.ErrImageNotFound) {
fmt.Println("Image not found")
}
func (*Client) GetJob ¶
GetJob returns the status and result of an async job.
Use this method to poll for job completion or check the status of a previously started async execution.
Basic polling example:
job, _ := client.RunAsync(ctx, req)
for {
status, err := client.GetJob(ctx, job.JobID)
if err != nil {
log.Fatal(err)
}
switch {
case status.IsCompleted():
fmt.Println(status.Output)
return
case status.IsFailed():
log.Fatalf("Job failed: %s", status.Error)
case status.IsRunning():
fmt.Println("Still running...")
time.Sleep(2 * time.Second)
}
}
Returns ErrNotFound if the job doesn't exist:
status, err := client.GetJob(ctx, "invalid-id")
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Job not found")
}
func (*Client) GetMessage ¶
GetMessage returns a specific message from session history by UUID.
Use this method to retrieve full details about a specific message, including its content, tool calls, and results.
Example:
msg, err := client.GetMessage(ctx, "sess-abc123", "msg-uuid-456")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Role: %s\n", msg.Type)
fmt.Printf("Content: %v\n", msg.Content)
func (*Client) GetMessages ¶
func (c *Client) GetMessages(ctx context.Context, sessionID string, opts *GetMessagesOptions) (*MessagesResponse, error)
GetMessages returns paginated conversation history for a session.
Use this method to retrieve past messages from a session, including user prompts, assistant responses, tool calls, and results.
Basic usage:
messages, err := client.GetMessages(ctx, "sess-abc123", nil)
if err != nil {
log.Fatal(err)
}
for _, msg := range messages.Messages {
fmt.Printf("[%s] %s\n", msg.Type, msg.UUID)
}
With pagination:
messages, _ := client.GetMessages(ctx, "sess-abc123", &stromboli.GetMessagesOptions{
Limit: 50,
Offset: 100,
})
if messages.HasMore {
// Fetch next page
nextPage, _ := client.GetMessages(ctx, "sess-abc123", &stromboli.GetMessagesOptions{
Limit: 50,
Offset: messages.Offset + messages.Limit,
})
}
func (*Client) GetSecret ¶
GetSecret retrieves metadata for a specific secret.
For security, the actual secret value is never returned - only the ID, name, and creation time.
Example:
secret, err := client.GetSecret(ctx, "github-token")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Secret %s created at %s\n", secret.Name, secret.CreatedAt)
Returns ErrNotFound if the secret doesn't exist:
secret, err := client.GetSecret(ctx, "unknown-secret")
if errors.Is(err, stromboli.ErrNotFound) {
fmt.Println("Secret not found")
}
func (*Client) GetToken ¶
GetToken obtains JWT tokens using a client ID.
Use this method to authenticate with the Stromboli API. The returned tokens can be used for subsequent authenticated requests.
Example:
tokens, err := client.GetToken(ctx, "my-client-id")
if err != nil {
log.Fatal(err)
}
// Set the token for future requests
client.SetToken(tokens.AccessToken)
// Token expires in tokens.ExpiresIn seconds
fmt.Printf("Token expires in %d seconds\n", tokens.ExpiresIn)
func (*Client) Health ¶
func (c *Client) Health(ctx context.Context) (*HealthResponse, error)
Health returns the health status of the Stromboli API.
Use this method to:
- Check if the API is reachable and healthy
- Verify the server version
- Check the status of individual components (e.g., Podman)
Example:
health, err := client.Health(ctx)
if err != nil {
log.Fatalf("API is unreachable: %v", err)
}
if !health.IsHealthy() {
for _, c := range health.Components {
if !c.IsHealthy() {
log.Printf("Component %s is unhealthy: %s", c.Name, c.Error)
}
}
}
fmt.Printf("API v%s is healthy\n", health.Version)
The context can be used to set a timeout or cancel the request:
ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() health, err := client.Health(ctx)
func (*Client) ListImages ¶
ListImages returns all local container images sorted by compatibility rank.
Images are ranked by their compatibility with Stromboli:
- Rank 1-2: Verified compatible (have required tools)
- Rank 3: Standard glibc (compatible)
- Rank 4: Incompatible (Alpine/musl)
Example:
images, err := client.ListImages(ctx)
if err != nil {
log.Fatal(err)
}
for _, img := range images {
fmt.Printf("%s:%s (rank %d, compatible: %v)\n",
img.Repository, img.Tag, img.CompatibilityRank, img.Compatible)
}
func (*Client) ListJobs ¶
ListJobs returns all async jobs.
Use this method to get an overview of all jobs, their status, and when they were created. The list includes pending, running, completed, failed, and cancelled jobs.
Example:
jobs, err := client.ListJobs(ctx)
if err != nil {
log.Fatal(err)
}
for _, job := range jobs {
fmt.Printf("%s: %s (created: %s)\n", job.ID, job.Status, job.CreatedAt)
}
Filter by status:
jobs, _ := client.ListJobs(ctx)
for _, job := range jobs {
if job.IsRunning() {
fmt.Printf("Job %s is still running\n", job.ID)
}
}
func (*Client) ListSecrets ¶
ListSecrets returns all available Podman secrets.
These secrets can be injected into container execution environments using [PodmanOptions.SecretsEnv].
Example:
secrets, err := client.ListSecrets(ctx)
if err != nil {
log.Fatal(err)
}
for _, s := range secrets {
fmt.Printf("Secret: %s (created: %s)\n", s.Name, s.CreatedAt)
}
Using secrets in execution:
secrets, _ := client.ListSecrets(ctx)
result, _ := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Use my GitHub token to list repos",
Podman: &stromboli.PodmanOptions{
SecretsEnv: map[string]string{
"GITHUB_TOKEN": secrets[0].Name, // Use first available secret
},
},
})
func (*Client) ListSessions ¶
ListSessions returns all existing session IDs.
Sessions are created automatically when running Claude with a new conversation. Use this method to list all available sessions for resumption or cleanup.
Example:
sessionIDs, err := client.ListSessions(ctx)
if err != nil {
log.Fatal(err)
}
for _, id := range sessionIDs {
fmt.Printf("Session: %s\n", id)
}
To continue a specific session, use the session ID with [ClaudeOptions.SessionID]:
result, _ := client.Run(ctx, &stromboli.RunRequest{
Prompt: "What did we discuss earlier?",
Claude: &stromboli.ClaudeOptions{
SessionID: sessionIDs[0],
Resume: true,
},
})
func (*Client) Logout ¶
func (c *Client) Logout(ctx context.Context) (*LogoutResponse, error)
Logout invalidates the current access token.
After calling this method, the token will no longer be accepted by the API. This method requires a valid token to be set using Client.SetToken.
Example:
client.SetToken(accessToken)
result, err := client.Logout(ctx)
if err != nil {
log.Fatal(err)
}
if result.Success {
fmt.Println("Successfully logged out")
client.SetToken("") // Clear the token
}
func (*Client) PullImage ¶
func (c *Client) PullImage(ctx context.Context, req *PullImageRequest) (*PullImageResponse, error)
PullImage pulls a container image from a registry.
This operation may take some time for large images.
Example:
result, err := client.PullImage(ctx, &stromboli.PullImageRequest{
Image: "python:3.12-slim",
Platform: "linux/amd64",
})
if err != nil {
log.Fatal(err)
}
if result.Success {
fmt.Printf("Pulled image %s (ID: %s)\n", result.Image, result.ImageID)
}
func (*Client) RefreshToken ¶
RefreshToken obtains a new access token using a refresh token.
Use this method when your access token has expired. The refresh token has a longer lifetime and can be used to obtain new access tokens.
Example:
// When access token expires, use refresh token
newTokens, err := client.RefreshToken(ctx, tokens.RefreshToken)
if err != nil {
// Refresh token may also be expired, need to re-authenticate
log.Fatal(err)
}
client.SetToken(newTokens.AccessToken)
func (*Client) Run ¶
func (c *Client) Run(ctx context.Context, req *RunRequest) (*RunResponse, error)
Run executes Claude synchronously and waits for the result.
This method blocks until Claude completes execution or an error occurs. For long-running tasks, consider using Client.RunAsync instead.
Basic usage:
result, err := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Hello, Claude!",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Output)
With configuration:
result, err := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Review this code for security issues",
Workdir: "/workspace",
Claude: &stromboli.ClaudeOptions{
Model: stromboli.ModelSonnet,
MaxBudgetUSD: 5.0,
AllowedTools: []string{"Read", "Glob", "Grep"},
},
Podman: &stromboli.PodmanOptions{
Memory: "2g",
Timeout: "10m",
Volumes: []string{"/home/user/project:/workspace:ro"},
},
})
Continuing a conversation:
// First request
result1, _ := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Remember: my favorite color is blue",
})
// Continue the conversation
result2, _ := client.Run(ctx, &stromboli.RunRequest{
Prompt: "What's my favorite color?",
Claude: &stromboli.ClaudeOptions{
SessionID: result1.SessionID,
Resume: true,
},
})
Timeout Behavior ¶
The effective request timeout is determined by the shorter of:
- The client's configured timeout (via WithTimeout)
- The context's deadline (if set via context.WithTimeout)
For long-running tasks, either increase the client timeout or use Client.RunAsync instead.
The context can be used for cancellation:
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() result, err := client.Run(ctx, req)
func (*Client) RunAsync ¶
func (c *Client) RunAsync(ctx context.Context, req *RunRequest) (*AsyncRunResponse, error)
RunAsync starts Claude execution asynchronously and returns a job ID.
Use this method for long-running tasks. Poll the job status with Client.GetJob or configure a webhook to be notified on completion.
Basic usage:
job, err := client.RunAsync(ctx, &stromboli.RunRequest{
Prompt: "Analyze this large codebase",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Job started: %s\n", job.JobID)
With webhook notification:
job, err := client.RunAsync(ctx, &stromboli.RunRequest{
Prompt: "Review all files in the project",
WebhookURL: "https://example.com/webhook",
})
Polling for completion:
job, _ := client.RunAsync(ctx, req)
for {
status, err := client.GetJob(ctx, job.JobID)
if err != nil {
log.Fatal(err)
}
switch status.Status {
case "completed":
fmt.Println(status.Output)
return
case "failed":
log.Fatalf("Job failed: %s", status.Error)
case "running":
fmt.Println("Still running...")
time.Sleep(2 * time.Second)
}
}
func (*Client) SearchImages ¶
func (c *Client) SearchImages(ctx context.Context, opts *SearchImagesOptions) ([]*ImageSearchResult, error)
SearchImages searches container registries for images matching the query.
Returns results from Docker Hub and other configured registries.
Example:
results, err := client.SearchImages(ctx, &stromboli.SearchImagesOptions{
Query: "python",
Limit: 10,
})
if err != nil {
log.Fatal(err)
}
for _, r := range results {
fmt.Printf("%s: %s (stars: %d, official: %v)\n",
r.Name, r.Description, r.Stars, r.Official)
}
func (*Client) SetToken ¶
SetToken sets the Bearer token for authenticated requests.
This token is used for endpoints that require authentication, such as Client.ValidateToken and Client.Logout. SetToken is safe for concurrent use.
Token Validation ¶
Tokens are validated to prevent HTTP header injection attacks. If a token contains control characters (CR, LF, or other characters < 0x20), the token is rejected and a warning is logged. The previous token remains unchanged. This is a security measure - valid JWT tokens never contain these characters.
To check if a token was accepted, call [Client.getToken] after setting, or use Client.ValidateToken to verify with the server.
Empty Token ¶
Passing an empty string clears the token. Use Client.ClearToken for a more explicit way to remove the token.
Example:
tokens, _ := client.GetToken(ctx, "my-client-id") client.SetToken(tokens.AccessToken) // Now authenticated endpoints will work validation, _ := client.ValidateToken(ctx)
func (*Client) Stream ¶
Stream executes Claude and streams output in real-time.
This method connects to the SSE (Server-Sent Events) endpoint and returns a Stream that yields events as they arrive.
Timeout Behavior ¶
WARNING: The client timeout (WithTimeout) does NOT apply to streaming requests. If no context deadline is set and the server stops responding, this method may block indefinitely. Always use context.WithTimeout:
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() stream, err := client.Stream(ctx, req)
The timeout behavior differs from regular requests because streams are designed for long-running connections where data arrives incrementally.
Basic Usage ¶
stream, err := client.Stream(ctx, &stromboli.StreamRequest{
Prompt: "Write a haiku about Go programming",
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for stream.Next() {
fmt.Print(stream.Event().Data)
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
Channel Iteration ¶
stream, _ := client.Stream(ctx, req)
defer stream.Close()
for event := range stream.Events() {
fmt.Print(event.Data)
}
Continuing a Conversation ¶
// First interaction
stream1, _ := client.Stream(ctx, &stromboli.StreamRequest{
Prompt: "My name is Alice",
})
// ... consume stream1 ...
sessionID := "..." // Get from previous response
// Continue conversation
stream2, _ := client.Stream(ctx, &stromboli.StreamRequest{
Prompt: "What's my name?",
SessionID: sessionID,
})
func (*Client) ValidateToken ¶
func (c *Client) ValidateToken(ctx context.Context) (*TokenValidation, error)
ValidateToken validates the current access token and returns its claims.
This method requires a valid token to be set using Client.SetToken.
Example:
client.SetToken(accessToken)
validation, err := client.ValidateToken(ctx)
if err != nil {
log.Fatal(err)
}
if validation.Valid {
fmt.Printf("Token valid for subject: %s\n", validation.Subject)
fmt.Printf("Expires at: %d\n", validation.ExpiresAt)
}
type CompatibilityResult ¶
type CompatibilityResult struct {
// Status is the compatibility status.
Status CompatibilityStatus
// ServerVersion is the version reported by the server.
ServerVersion string
// SDKVersion is this SDK's version.
SDKVersion string
// TargetAPIVersion is the API version this SDK was built for.
TargetAPIVersion string
// SupportedRange is the range of API versions this SDK supports.
SupportedRange string
// Message is a human-readable description of the result.
Message string
}
CompatibilityResult contains detailed information about API compatibility.
func CheckCompatibility ¶
func CheckCompatibility(serverVersion string) *CompatibilityResult
CheckCompatibility performs a detailed compatibility check between the server version and this SDK.
Example:
health, _ := client.Health(ctx)
result := stromboli.CheckCompatibility(health.Version)
switch result.Status {
case stromboli.Compatible:
fmt.Println("Server is compatible")
case stromboli.Incompatible:
fmt.Printf("Warning: %s\n", result.Message)
case stromboli.Unknown:
fmt.Printf("Could not determine compatibility: %s\n", result.Message)
}
func (*CompatibilityResult) IsCompatible ¶
func (r *CompatibilityResult) IsCompatible() bool
IsCompatible returns true if the status indicates compatibility.
type CompatibilityStatus ¶
type CompatibilityStatus int
CompatibilityStatus represents the result of a version compatibility check.
const ( // Compatible means the API version is within the supported range. Compatible CompatibilityStatus = iota // Incompatible means the API version is outside the supported range. Incompatible // Unknown means the version could not be parsed. Unknown )
func (CompatibilityStatus) String ¶
func (s CompatibilityStatus) String() string
String returns a human-readable representation of the status.
type ComponentHealth ¶
type ComponentHealth struct {
// Name is the component identifier.
// Example: "podman".
Name string `json:"name"`
// Status indicates the component health.
// Values: "ok" (healthy) or "error" (unhealthy).
Status string `json:"status"`
// Error contains the error message when Status is "error".
// Empty when Status is "ok".
Error string `json:"error,omitempty"`
}
ComponentHealth represents the health status of an individual component.
Stromboli checks the following components:
- "podman": Container runtime availability
- Additional components may be added in future versions
func (*ComponentHealth) IsHealthy ¶
func (c *ComponentHealth) IsHealthy() bool
IsHealthy returns true if the component status is "ok".
type CrashInfo ¶
type CrashInfo struct {
// Reason is a human-readable description of why the job crashed.
// Example: "Container OOM killed", "Timeout exceeded"
Reason string `json:"reason,omitempty"`
// ExitCode is the container exit code (if available).
// Common values: 137 (OOM), 143 (SIGTERM), 1 (general error)
ExitCode int64 `json:"exit_code,omitempty"`
// PartialOutput contains any output captured before the crash.
// This can help debug what the job was doing when it crashed.
PartialOutput string `json:"partial_output,omitempty"`
// Signal is the signal that killed the process (if applicable).
// Examples: "SIGSEGV", "SIGKILL", "SIGTERM"
Signal string `json:"signal,omitempty"`
// TaskCompleted indicates whether the task appeared to complete before crashing.
TaskCompleted bool `json:"task_completed,omitempty"`
}
CrashInfo contains details about a job crash.
This is populated when a job terminates unexpectedly due to container issues, OOM errors, or other infrastructure problems.
type CreateSecretRequest ¶
type CreateSecretRequest struct {
// Name is the secret name (required).
// Must be unique among existing secrets.
// Example: "github-token"
Name string `json:"name"`
// Value is the secret data (required).
// This value is stored securely and never returned by the API.
// Example: "ghp_xxxx..."
Value string `json:"value"`
}
CreateSecretRequest represents a request to create a new Podman secret.
Use with Client.CreateSecret:
err := client.CreateSecret(ctx, &stromboli.CreateSecretRequest{
Name: "github-token",
Value: "ghp_xxxx...",
})
type EnvironmentConfig ¶
type EnvironmentConfig struct {
// Type of environment: "" (default single container) or "compose".
// Example: "compose"
Type string `json:"type,omitempty"`
// Path to compose file (required when Type="compose").
// Must be an absolute path ending in .yml or .yaml.
// Example: "/home/user/project/docker-compose.yml"
Path string `json:"path,omitempty"`
// Service name where Claude will run (required when Type="compose").
// Example: "dev"
Service string `json:"service,omitempty"`
// BuildTimeout is the optional build timeout override for compose.
// If not specified, uses server default (10m).
// Examples: "15m", "30m"
BuildTimeout string `json:"build_timeout,omitempty"`
}
EnvironmentConfig specifies a compose-based multi-service environment.
When set, the agent runs inside the specified service of a Docker Compose stack instead of a standalone container. This allows running Claude in complex multi-container environments.
Example:
&stromboli.EnvironmentConfig{
Type: "compose",
Path: "/home/user/project/docker-compose.yml",
Service: "dev",
}
type Error ¶
type Error struct {
// Code is a machine-readable error code.
// Common values: NOT_FOUND, TIMEOUT, UNAUTHORIZED, BAD_REQUEST, INTERNAL.
Code string
// Message is a human-readable error description.
Message string
// Status is the HTTP status code returned by the API.
// Zero if the error occurred before receiving a response.
Status int
// Cause is the underlying error, if any.
// Use errors.Unwrap or errors.Is to inspect the cause chain.
Cause error
// RetryAfter indicates how long to wait before retrying (for 429 responses).
// Zero if no Retry-After header was provided or not applicable.
RetryAfter time.Duration
}
Error represents an error returned by the Stromboli API.
Error implements the standard error interface and supports error wrapping via the Unwrap method. Use errors.As to check for specific error types:
result, err := client.Run(ctx, req)
if err != nil {
var apiErr *stromboli.Error
if errors.As(err, &apiErr) {
fmt.Printf("API error %s: %s\n", apiErr.Code, apiErr.Message)
}
}
Common error codes include:
- NOT_FOUND: The requested resource does not exist
- TIMEOUT: The request timed out
- UNAUTHORIZED: Invalid or missing authentication
- BAD_REQUEST: Invalid request parameters
- INTERNAL: Internal server error
func (*Error) Error ¶
Error returns a string representation of the error.
The format is "stromboli: CODE: message" or "stromboli: CODE: message: cause" if there is an underlying error.
func (*Error) Is ¶
Is reports whether the target error matches this error.
Two errors match if they have the same Code. The Status field is NOT compared, so errors.Is(err, ErrNotFound) matches any NOT_FOUND error regardless of the HTTP status code. This allows sentinel errors to match all instances of that error type.
Example:
if errors.Is(err, stromboli.ErrNotFound) {
// Handles any NOT_FOUND error (404, or otherwise)
}
type GetMessagesOptions ¶
type GetMessagesOptions struct {
// Limit is the maximum number of messages to return (default: 50, max: 200).
Limit int64 `json:"limit,omitempty"`
// Offset is the number of messages to skip (for pagination).
Offset int64 `json:"offset,omitempty"`
}
GetMessagesOptions configures the pagination for Client.GetMessages.
Example:
messages, _ := client.GetMessages(ctx, "sess-abc123", &stromboli.GetMessagesOptions{
Limit: 50,
Offset: 100,
})
type HealthResponse ¶
type HealthResponse struct {
// Name is the service name, typically "stromboli".
Name string `json:"name"`
// Status indicates the overall health status.
// Values: "ok" (healthy) or "error" (unhealthy).
Status string `json:"status"`
// Version is the Stromboli server version.
// Example: "0.3.0-alpha".
Version string `json:"version"`
// Components lists the health status of individual components.
// Check this to identify which component is failing when Status is "error".
Components []ComponentHealth `json:"components"`
}
HealthResponse represents the health status of the Stromboli API.
Use Client.Health to retrieve the current health status:
health, err := client.Health(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s, Version: %s\n", health.Status, health.Version)
func (*HealthResponse) IsHealthy ¶
func (h *HealthResponse) IsHealthy() bool
IsHealthy returns true if the overall status is "ok".
Example:
health, _ := client.Health(ctx)
if !health.IsHealthy() {
log.Println("API is unhealthy!")
}
type Image ¶
type Image struct {
// ID is the image ID (usually sha256:...).
// Example: "sha256:abc123def456"
ID string `json:"id,omitempty"`
// Repository is the image repository name.
// Example: "python"
Repository string `json:"repository,omitempty"`
// Tag is the image tag.
// Example: "3.12-slim"
Tag string `json:"tag,omitempty"`
// Size is the image size in bytes.
// Example: 125000000
Size int64 `json:"size,omitempty"`
// Created is when the image was created (RFC3339 format).
// Example: "2024-01-15T10:30:00Z"
Created string `json:"created,omitempty"`
// Description is a human-readable description of the image.
// Example: "Python development image"
Description string `json:"description,omitempty"`
// Compatible indicates if the image is compatible with Stromboli.
// Images with glibc are compatible; Alpine/musl images are not.
Compatible bool `json:"compatible,omitempty"`
// CompatibilityRank indicates the image's compatibility level.
// 1-2: Verified compatible, 3: Standard glibc, 4: Incompatible (Alpine/musl)
CompatibilityRank int64 `json:"compatibility_rank,omitempty"`
// HasClaudeCLI indicates if the image has Claude CLI pre-installed.
HasClaudeCLI bool `json:"has_claude_cli,omitempty"`
// Tools lists tools available in the image.
// Example: []string{"python", "pip", "git"}
Tools []string `json:"tools,omitempty"`
}
Image represents a local container image with compatibility information.
Use Client.ListImages to list all available images:
images, err := client.ListImages(ctx)
for _, img := range images {
fmt.Printf("%s:%s (rank %d)\n", img.Repository, img.Tag, img.CompatibilityRank)
}
func (*Image) CreatedTime ¶
CreatedTime parses Created as time.Time. Returns zero time if Created is empty or parsing fails.
NOTE: Parsing errors are silently ignored. If you need to validate the timestamp format, use time.Parse(time.RFC3339, i.Created) directly.
type ImageSearchResult ¶
type ImageSearchResult struct {
// Name is the image name.
// Example: "python"
Name string `json:"name,omitempty"`
// Description is the image description from the registry.
// Example: "Python is an interpreted programming language"
Description string `json:"description,omitempty"`
// Stars is the number of stars on the registry.
// Example: 8500
Stars int64 `json:"stars,omitempty"`
// Official indicates if this is an official image.
Official bool `json:"official,omitempty"`
// Automated indicates if this image is automatically built.
Automated bool `json:"automated,omitempty"`
// Index is the registry index (e.g., "docker.io").
// Example: "docker.io"
Index string `json:"index,omitempty"`
}
ImageSearchResult represents a search result from a container registry.
Use Client.SearchImages to search registries:
results, err := client.SearchImages(ctx, &stromboli.SearchImagesOptions{
Query: "python",
Limit: 10,
})
for _, r := range results {
fmt.Printf("%s: %s (stars: %d)\n", r.Name, r.Description, r.Stars)
}
type Job ¶
type Job struct {
// ID is the unique job identifier.
// Example: "job-abc123def456"
ID string `json:"id"`
// Status indicates the current job state.
// Values: "pending", "running", "completed", "failed", "cancelled"
Status string `json:"status"`
// Output contains Claude's response when Status is "completed".
Output string `json:"output,omitempty"`
// Error contains the error message when Status is "failed".
Error string `json:"error,omitempty"`
// SessionID can be used to continue this conversation.
// Pass this to RunRequest.Claude.SessionID for follow-up requests.
SessionID string `json:"session_id,omitempty"`
// CreatedAt is when the job was created (RFC3339 format).
// Example: "2024-01-15T10:30:00Z"
CreatedAt string `json:"created_at,omitempty"`
// UpdatedAt is when the job was last updated (RFC3339 format).
// Example: "2024-01-15T10:31:00Z"
UpdatedAt string `json:"updated_at,omitempty"`
// CrashInfo contains crash details if the job crashed.
CrashInfo *CrashInfo `json:"crash_info,omitempty"`
}
Job represents the status and result of an async job.
Use Client.GetJob to retrieve job status, or Client.ListJobs to list all jobs:
job, err := client.GetJob(ctx, "job-abc123")
if err != nil {
log.Fatal(err)
}
switch job.Status {
case stromboli.JobStatusCompleted:
fmt.Println(job.Output)
case stromboli.JobStatusRunning:
fmt.Println("Still running...")
case stromboli.JobStatusFailed:
fmt.Printf("Failed: %s\n", job.Error)
}
func (*Job) CreatedAtTime ¶
CreatedAtTime parses CreatedAt as time.Time. Returns zero time if CreatedAt is empty or parsing fails.
NOTE: Parsing errors are silently ignored. If you need to validate the timestamp format, use time.Parse(time.RFC3339, j.CreatedAt) directly.
func (*Job) IsCancelled ¶
IsCancelled returns true if the job was cancelled.
func (*Job) IsCompleted ¶
IsCompleted returns true if the job completed successfully.
func (*Job) UpdatedAtTime ¶
UpdatedAtTime parses UpdatedAt as time.Time. Returns zero time if UpdatedAt is empty or parsing fails.
NOTE: Parsing errors are silently ignored. If you need to validate the timestamp format, use time.Parse(time.RFC3339, j.UpdatedAt) directly.
type LifecycleHooks ¶
type LifecycleHooks struct {
// OnCreateCommand runs after container creation, before Claude starts (first run only).
// Commands are executed sequentially via "podman exec".
// Example: []string{"pip install -r requirements.txt"}
OnCreateCommand []string `json:"on_create_command,omitempty"`
// PostCreate runs after OnCreateCommand completes (first run only).
// Commands are executed sequentially via "podman exec".
// Example: []string{"npm run setup"}
PostCreate []string `json:"post_create,omitempty"`
// PostStart runs after container starts (every run, including continues).
// Commands are executed sequentially via "podman exec".
// Example: []string{"redis-server --daemonize yes"}
PostStart []string `json:"post_start,omitempty"`
// HooksTimeout is the maximum duration for all hooks combined.
// If not specified, hooks run with the container's timeout.
// Examples: "5m", "30s"
HooksTimeout string `json:"hooks_timeout,omitempty"`
}
LifecycleHooks configures commands to run at specific container lifecycle stages.
Use these hooks to set up the container environment before Claude starts, such as installing dependencies, starting background services, etc.
Example:
&stromboli.LifecycleHooks{
OnCreateCommand: []string{"pip install -r requirements.txt"},
PostStart: []string{"redis-server --daemonize yes"},
HooksTimeout: "5m",
}
type Logger ¶
type Logger interface {
Printf(format string, v ...interface{})
}
Logger is the interface used for SDK logging. Implement this interface to customize log output.
type LogoutResponse ¶
type LogoutResponse struct {
// Success indicates whether the logout was successful.
Success bool `json:"success"`
// Message provides additional context about the logout.
Message string `json:"message"`
}
LogoutResponse represents the result of invalidating a token.
Use Client.Logout to invalidate the current token:
result, err := client.Logout(ctx)
if err != nil {
log.Fatal(err)
}
if result.Success {
fmt.Println("Logged out successfully")
}
type Message ¶
type Message struct {
// UUID is the unique message identifier.
// Example: "92242819-b7d1-48d4-b023-6134c3e9f63a"
UUID string `json:"uuid,omitempty"`
// Type indicates the message type.
// Values: "user", "assistant", "queue-operation"
Type string `json:"type,omitempty"`
// ParentUUID is the parent message UUID for threading.
ParentUUID string `json:"parent_uuid,omitempty"`
// SessionID is the session this message belongs to.
SessionID string `json:"session_id,omitempty"`
// Cwd is the working directory at time of message.
// Example: "/workspace"
Cwd string `json:"cwd,omitempty"`
// GitBranch is the git branch at time of message.
// Example: "main"
GitBranch string `json:"git_branch,omitempty"`
// PermissionMode is the permission mode active for this message.
// Example: "bypassPermissions"
PermissionMode string `json:"permission_mode,omitempty"`
// Timestamp is when the message was created (RFC3339 format).
Timestamp string `json:"timestamp,omitempty"`
// Version is the Claude Code version that created this message.
Version string `json:"version,omitempty"`
// Content contains the message content (text, tool calls, etc.).
// The structure varies by message type:
// - For "user" messages: string or []ContentBlock
// - For "assistant" messages: []ContentBlock with text and tool_use
//
// Use type assertions or json.Marshal/Unmarshal to work with this field.
//
// Example:
//
// // Check if content is a simple string
// if text, ok := msg.Content.(string); ok {
// fmt.Println(text)
// }
//
// // For complex content, marshal and unmarshal
// data, _ := json.Marshal(msg.Content)
// var blocks []map[string]interface{}
// json.Unmarshal(data, &blocks)
Content interface{} `json:"content,omitempty"`
// ToolResult contains tool use results (for tool_result messages).
// The structure is typically:
// - ToolUseID: string - The ID of the tool use this result responds to
// - Content: string or []ContentBlock - The result data
// - IsError: bool - Whether this result represents an error
//
// Use type assertions or json.Marshal/Unmarshal to work with this field.
ToolResult interface{} `json:"tool_result,omitempty"`
}
Message represents a single message from session history.
Messages can be user prompts, assistant responses, or tool interactions. Use Client.GetMessages to list messages or Client.GetMessage to get a specific message by UUID.
func (*Message) ContentAsBlocks ¶
ContentAsBlocks returns the content as a slice of maps if it contains content blocks. Returns nil and false if content is not in block format.
WARNING: Non-map entries in the content array are skipped. The returned int indicates how many entries were skipped, allowing callers to detect data loss. If skipped > 0, some content was not map-typed and was omitted from results.
The ok return value indicates whether Content was in array format ([]interface{}), NOT whether any blocks were found. An empty content array returns ok=true with an empty blocks slice. Use ok=false to detect non-array content formats.
For more precise typing, use json.Marshal/Unmarshal:
data, _ := json.Marshal(msg.Content) var blocks []YourBlockType json.Unmarshal(data, &blocks)
func (*Message) ContentAsString ¶
ContentAsString returns the content as a string if it is a simple string message. Returns empty string and false if content is not a string.
Example:
if text, ok := msg.ContentAsString(); ok {
fmt.Println(text)
}
func (*Message) TimestampTime ¶
TimestampTime parses Timestamp as time.Time. Returns zero time if Timestamp is empty or parsing fails.
NOTE: Parsing errors are silently ignored. If you need to validate the timestamp format, use time.Parse(time.RFC3339, m.Timestamp) directly.
type MessagesResponse ¶
type MessagesResponse struct {
// Messages is the list of messages in this page.
Messages []*Message `json:"messages"`
// Total is the total number of messages in the session.
Total int64 `json:"total"`
// Limit is the maximum messages per page (requested or default).
Limit int64 `json:"limit"`
// Offset is the number of messages skipped.
Offset int64 `json:"offset"`
// HasMore indicates if there are more messages to fetch.
HasMore bool `json:"has_more"`
}
MessagesResponse represents a paginated list of session messages.
Use Client.GetMessages to retrieve messages from a session:
resp, _ := client.GetMessages(ctx, "sess-abc123", nil)
for _, msg := range resp.Messages {
fmt.Printf("[%s] %s\n", msg.Type, msg.UUID)
}
if resp.HasMore {
// Fetch more messages...
}
type Model ¶
type Model string
Model represents a Claude model identifier.
The SDK provides constants for common models (ModelHaiku, ModelSonnet, ModelOpus). For newer models not yet added to the SDK, you can cast any string to Model:
customModel := stromboli.Model("claude-3-5-sonnet-20241022")
Model values are passed directly to the API, so you can use any model identifier supported by the Stromboli server.
const ( // ModelHaiku is the fastest and most cost-effective model. // Best for simple tasks, quick responses, and high-volume use cases. ModelHaiku Model = "haiku" // ModelSonnet is the balanced model for most use cases. // Good balance of speed, capability, and cost. ModelSonnet Model = "sonnet" // ModelOpus is the most capable model. // Best for complex reasoning, nuanced tasks, and highest quality output. ModelOpus Model = "opus" )
Model constants for Claude model selection.
Use these with [ClaudeOptions.Model]:
&stromboli.ClaudeOptions{
Model: stromboli.ModelHaiku,
}
type Option ¶
type Option func(*Client)
Option configures a Client.
Options are passed to NewClient to customize the client behavior. Multiple options can be combined:
client, err := stromboli.NewClient("http://localhost:8585",
stromboli.WithTimeout(5*time.Minute),
stromboli.WithHTTPClient(customHTTPClient),
)
Options are applied in order, so later options override earlier ones.
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client for making requests.
Use this option to customize transport settings like:
- TLS configuration
- Proxy settings
- Connection pooling
- Custom transports (e.g., for testing)
The provided client's Timeout field is ignored in favor of WithTimeout. Use WithTimeout to control request timeouts.
Passing nil logs a warning and is ignored (the default client is retained). This is typically a programmer error; check for nil before calling.
Default: A new http.Client with cloned http.DefaultTransport.
Example:
httpClient := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
}
client := stromboli.NewClient(url,
stromboli.WithHTTPClient(httpClient),
)
func WithRequestHook ¶
func WithRequestHook(hook RequestHook) Option
WithRequestHook sets a hook that is called before each HTTP request.
Use this for observability (logging, metrics) or to modify requests before they are sent. Pass nil to clear a previously set hook.
IMPORTANT: Hooks are captured at client creation time. Setting this option AFTER calling NewClient will NOT affect API calls that use the internal generated client. To use different hooks, create a new client.
Example:
client, err := stromboli.NewClient(url,
stromboli.WithRequestHook(func(req *http.Request) {
log.Printf("Request: %s %s", req.Method, req.URL)
}),
)
func WithResponseHook ¶
func WithResponseHook(hook ResponseHook) Option
WithResponseHook sets a hook that is called after each HTTP response.
Use this for observability (logging, metrics) or to inspect response headers and status codes. See ResponseHook for important caveats about body availability. Pass nil to clear a previously set hook.
IMPORTANT: Hooks are captured at client creation time. Setting this option AFTER calling NewClient will NOT affect API calls that use the internal generated client. To use different hooks, create a new client.
Example:
client, err := stromboli.NewClient(url,
stromboli.WithResponseHook(func(resp *http.Response) {
log.Printf("Response: %d %s", resp.StatusCode, resp.Status)
}),
)
func WithRetries
deprecated
WithRetries sets the maximum number of retry attempts for failed requests.
Deprecated: Retry logic is not implemented. This option logs a warning and does nothing. Consider using:
- github.com/hashicorp/go-retryablehttp for automatic retries
- github.com/cenkalti/backoff for custom retry logic
- github.com/avast/retry-go for simple retry patterns
This option will be removed in v1.0.
Note: The deprecation warning is logged when NewClient is called. If you use SetLogger to configure a custom logger, call it before creating clients to see this warning in your logger.
Default: 0 (no retries).
func WithStreamTimeout ¶
WithStreamTimeout sets the default timeout for streaming requests.
Unlike regular requests, streams are long-running connections where data arrives incrementally. This timeout applies only if no context deadline is set when calling Client.Stream, or if the existing deadline is further away than this timeout.
IMPORTANT: This is a TOTAL DURATION timeout, not an idle/inactivity timeout. The stream will be cancelled after this duration regardless of whether data is still being received. If you need idle detection (timeout when no data arrives for a period), use Stream.EventsWithContext with periodic checks or implement a custom wrapper with read deadlines.
If not set, streaming requests have no timeout by default. This can be dangerous as a stalled server may cause the client to hang indefinitely. It's recommended to either set this option or use context.WithTimeout.
A timeout of zero or negative disables the stream timeout (not recommended).
Example:
client, err := stromboli.NewClient(url,
stromboli.WithStreamTimeout(5*time.Minute),
)
// Now Stream will automatically timeout after 5 minutes if no context deadline is set
stream, err := client.Stream(ctx, req)
func WithTimeout ¶
WithTimeout sets the default timeout for all requests.
The timeout applies to the entire request lifecycle, including connection establishment, request sending, and response reading. A timeout of zero means no timeout. Negative values are treated as zero.
Default: 30 seconds.
Example:
client, err := stromboli.NewClient(url,
stromboli.WithTimeout(5*time.Minute), // Long timeout for slow operations
)
For per-request timeouts, use context.WithTimeout instead:
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() result, err := client.Health(ctx)
func WithToken ¶
WithToken sets the Bearer token for authenticated requests.
Use this option when you already have a valid access token and want to create an authenticated client from the start.
Pass an empty string to clear any previously set token.
Alternatively, use Client.SetToken to set the token after client creation, or Client.GetToken to obtain a new token.
Example:
client := stromboli.NewClient(url,
stromboli.WithToken("my-access-token"),
)
// Authenticated endpoints now work
validation, err := client.ValidateToken(ctx)
func WithUserAgent ¶
WithUserAgent sets a custom User-Agent header for all requests.
The User-Agent is sent with every request and can be used for server-side analytics or debugging.
Default: "stromboli-go/{version}".
Example:
client := stromboli.NewClient(url,
stromboli.WithUserAgent("my-app/1.0.0"),
)
type PodmanOptions ¶
type PodmanOptions struct {
// Memory limits container memory usage.
// Examples: "512m", "1g", "2g"
Memory string `json:"memory,omitempty"`
// Timeout sets the maximum execution time.
// Examples: "30s", "5m", "1h"
Timeout string `json:"timeout,omitempty"`
// Cpus limits CPU usage.
// Examples: "0.5" (half a CPU), "2" (two CPUs)
Cpus string `json:"cpus,omitempty"`
// Lower values = lower priority.
CPUShares int64 `json:"cpu_shares,omitempty"`
// Volumes mounts host paths into the container.
// Format: "host_path:container_path" or "host_path:container_path:options"
// Options: "ro" (read-only), "rw" (read-write, default)
// Example: []string{"/data:/data:ro", "/workspace:/workspace"}
Volumes []string `json:"volumes,omitempty"`
// Image overrides the container image.
// Must match server-configured allowed patterns.
// Example: "python:3.12"
Image string `json:"image,omitempty"`
// SecretsEnv injects Podman secrets as environment variables.
// Key: environment variable name, Value: Podman secret name.
// The secret must exist (created via `podman secret create`).
// Example: map[string]string{"GH_TOKEN": "github-token"}
SecretsEnv map[string]string `json:"secrets_env,omitempty"`
// Lifecycle configures commands to run at specific container lifecycle stages.
// See [LifecycleHooks] for available hooks.
Lifecycle *LifecycleHooks `json:"lifecycle,omitempty"`
// Environment specifies a compose-based multi-service environment.
// When set, the agent runs inside the specified service of the compose stack.
// See [EnvironmentConfig] for configuration options.
Environment *EnvironmentConfig `json:"environment,omitempty"`
}
PodmanOptions configures the container execution environment.
Use these options to control resource limits, mount volumes, and configure container behavior.
Example:
&stromboli.PodmanOptions{
Memory: "2g",
Timeout: "10m",
Volumes: []string{"/home/user/project:/workspace:ro"},
}
type PullImageRequest ¶
type PullImageRequest struct {
// Image is the image reference to pull (required).
// Example: "python:3.12-slim"
Image string `json:"image"`
// Platform specifies the platform for multi-arch images.
// Example: "linux/amd64", "linux/arm64"
Platform string `json:"platform,omitempty"`
// Quiet suppresses pull progress output.
Quiet bool `json:"quiet,omitempty"`
}
PullImageRequest represents a request to pull a container image.
Use with Client.PullImage:
result, err := client.PullImage(ctx, &stromboli.PullImageRequest{
Image: "python:3.12-slim",
Platform: "linux/amd64",
})
type PullImageResponse ¶
type PullImageResponse struct {
// Success indicates if the pull was successful.
Success bool `json:"success,omitempty"`
// Image is the pulled image reference.
// Example: "python:3.12-slim"
Image string `json:"image,omitempty"`
// ImageID is the pulled image's ID.
// Example: "sha256:abc123def456"
ImageID string `json:"image_id,omitempty"`
}
PullImageResponse represents the result of an image pull operation.
type RequestHook ¶
RequestHook is called before each HTTP request is sent. Use this for logging, metrics, or modifying requests.
type ResponseHook ¶
ResponseHook is called after each HTTP response is received.
WARNING: For most API methods (Run, Health, etc.), the response body will be consumed by the generated client before your hook runs. The hook is primarily useful for inspecting headers and status codes, not body content. For the Stream method, the body is available as it hasn't been consumed yet.
Use this for logging, metrics, or inspecting response metadata.
type RunRequest ¶
type RunRequest struct {
// Prompt is the message to send to Claude. Required.
Prompt string `json:"prompt"`
// Workdir is the working directory inside the container.
// Use Podman.Volumes to mount host paths into the container.
// Example: "/workspace"
Workdir string `json:"workdir,omitempty"`
// WebhookURL is called when an async job completes.
// Only used with [Client.RunAsync].
// Example: "https://example.com/webhook"
WebhookURL string `json:"webhook_url,omitempty"`
// Claude contains Claude-specific configuration options.
// See [ClaudeOptions] for available settings.
Claude *ClaudeOptions `json:"claude,omitempty"`
// Podman contains container configuration options.
// See [PodmanOptions] for available settings.
Podman *PodmanOptions `json:"podman,omitempty"`
}
RunRequest represents a request to execute Claude in an isolated container.
At minimum, you must provide a Prompt. All other fields are optional and provide fine-grained control over Claude's execution environment.
Basic usage:
result, err := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Hello, Claude!",
})
With options:
result, err := client.Run(ctx, &stromboli.RunRequest{
Prompt: "Review this code",
Workdir: "/workspace",
Claude: &stromboli.ClaudeOptions{
Model: stromboli.ModelHaiku,
MaxBudgetUSD: 1.0,
},
Podman: &stromboli.PodmanOptions{
Memory: "1g",
Timeout: "5m",
},
})
type RunResponse ¶
type RunResponse struct {
// ID is the unique execution identifier.
// Example: "run-abc123def456"
ID string `json:"id"`
// Status indicates execution result.
// Values: "completed" (success) or "error" (failure).
Status string `json:"status"`
// Output contains Claude's response when Status is "completed".
Output string `json:"output,omitempty"`
// Error contains the error message when Status is "error".
Error string `json:"error,omitempty"`
// SessionID can be used to continue this conversation.
// Pass this to RunRequest.Claude.SessionID for follow-up requests.
SessionID string `json:"session_id,omitempty"`
}
RunResponse represents the result of a synchronous Claude execution.
Important: A nil error from Client.Run means the API call succeeded, not necessarily that Claude execution succeeded. Always check Status and Error fields to determine if the execution completed successfully.
Check Status to determine if execution succeeded:
result, err := client.Run(ctx, req)
if err != nil {
log.Fatal(err) // API call failed
}
if result.IsSuccess() {
fmt.Println(result.Output)
} else {
// Execution failed - check result.Error for details
fmt.Printf("Execution failed: %s\n", result.Error)
}
func (*RunResponse) IsSuccess ¶
func (r *RunResponse) IsSuccess() bool
IsSuccess returns true if the execution completed successfully.
type SearchImagesOptions ¶
type SearchImagesOptions struct {
// Query is the search term (required).
// Example: "python"
Query string
// Limit is the maximum number of results to return.
// Default varies by registry.
Limit int64
// NoTrunc disables truncation of output.
NoTrunc bool
}
SearchImagesOptions configures an image search request.
Example:
results, err := client.SearchImages(ctx, &stromboli.SearchImagesOptions{
Query: "python",
Limit: 25,
NoTrunc: true,
})
type Secret ¶
type Secret struct {
// ID is the unique identifier of the secret.
// Example: "abc123def456"
ID string `json:"id,omitempty"`
// Name is the secret name used to reference it.
// Example: "github-token"
Name string `json:"name"`
// CreatedAt is when the secret was created (RFC3339 format).
// Example: "2024-01-15T10:30:00Z"
CreatedAt string `json:"created_at,omitempty"`
}
Secret represents a Podman secret's metadata.
Secrets are used to securely pass sensitive data (API keys, tokens, etc.) to containers without exposing them in environment variables or command lines.
Use Client.CreateSecret to create a new secret:
err := client.CreateSecret(ctx, &stromboli.CreateSecretRequest{
Name: "github-token",
Value: "ghp_xxxx...",
})
func (*Secret) CreatedAtTime ¶
CreatedAtTime parses CreatedAt as time.Time. Returns zero time if CreatedAt is empty or parsing fails.
NOTE: Parsing errors are silently ignored. If you need to validate the timestamp format, use time.Parse(time.RFC3339, s.CreatedAt) directly.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream represents an active SSE stream from Claude.
Use Client.Stream to create a stream, then iterate over events:
stream, err := client.Stream(ctx, &stromboli.StreamRequest{
Prompt: "Count from 1 to 10",
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for stream.Next() {
event := stream.Event()
fmt.Print(event.Data)
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
func (*Stream) Close ¶
Close closes the stream and releases resources.
Always call Close when done with the stream, preferably with defer. This is required even if Stream.Next returns false due to an error, as the underlying HTTP response body must be closed to release resources.
Close is safe to call multiple times and is thread-safe.
Example:
stream, err := client.Stream(ctx, req)
if err != nil {
log.Fatal(err)
}
defer stream.Close() // Always close, even on errors
func (*Stream) Err ¶
Err returns any error that occurred during streaming.
Returns nil if:
- The stream completed successfully (normal EOF)
- The stream is still active
- No error has occurred yet
To distinguish between "completed successfully" and "still active", check if Stream.Next returned false. After Next returns false, Err() == nil means normal completion; Err() != nil means an error.
func (*Stream) Event ¶
func (s *Stream) Event() *StreamEvent
Event returns the current event.
Call this after Stream.Next returns true. If called before the first successful Stream.Next call, returns an empty event (not nil) to prevent nil pointer dereferences.
This method is thread-safe and can be called concurrently with Stream.Next.
func (*Stream) Events
deprecated
func (s *Stream) Events() <-chan *StreamEvent
Events returns a channel that yields events from the stream.
The channel is closed when the stream ends or an error occurs. Check Stream.Err after the channel closes to see if an error occurred.
Deprecated: Use Stream.EventsWithContext to avoid goroutine leaks if you stop reading before the stream ends.
Example:
for event := range stream.Events() {
fmt.Print(event.Data)
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
func (*Stream) EventsWithContext ¶
func (s *Stream) EventsWithContext(ctx context.Context) <-chan *StreamEvent
EventsWithContext returns a channel that yields events from the stream.
The channel is closed when the stream ends, an error occurs, or the context is cancelled. This is the preferred method to avoid goroutine leaks if you stop reading before the stream ends.
Example:
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
for event := range stream.EventsWithContext(ctx) {
fmt.Print(event.Data)
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
func (*Stream) Next ¶
Next advances to the next event in the stream.
Returns true if an event is available, false if the stream is exhausted or an error occurred. Call Stream.Err to check for errors.
Example:
for stream.Next() {
event := stream.Event()
fmt.Print(event.Data)
}
type StreamEvent ¶
type StreamEvent struct {
// Type is the event type (from "event:" line).
// Common types: "", "message", "error", "done"
Type string
// Data is the event payload (from "data:" line).
Data string
// ID is the event ID (from "id:" line), if provided.
ID string
}
StreamEvent represents a single event from the SSE stream.
SSE events have an optional event type and data payload. Most events will have Type empty and Data containing the output.
type StreamRequest ¶
type StreamRequest struct {
// Prompt is the message to send to Claude. Required.
Prompt string
// Workdir is the working directory inside the container.
Workdir string
// SessionID enables conversation continuation.
SessionID string
}
StreamRequest represents a request for streaming Claude output.
This is a simplified version of RunRequest for the streaming endpoint, which only supports a subset of options via query parameters.
type TokenResponse ¶
type TokenResponse struct {
// AccessToken is the JWT access token for API authentication.
// Use with [WithToken] option or pass to authenticated endpoints.
AccessToken string `json:"access_token"`
// RefreshToken is used to obtain new access tokens.
// Use with [Client.RefreshToken] when the access token expires.
RefreshToken string `json:"refresh_token"`
// ExpiresIn is the access token lifetime in seconds.
// Example: 3600 (1 hour)
ExpiresIn int64 `json:"expires_in"`
// TokenType is the token type, typically "Bearer".
TokenType string `json:"token_type"`
}
TokenResponse represents JWT tokens returned by authentication endpoints.
Use Client.GetToken to obtain tokens:
tokens, err := client.GetToken(ctx, "my-client-id")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Access token: %s\n", tokens.AccessToken)
type TokenValidation ¶
type TokenValidation struct {
// Valid indicates whether the token is valid.
Valid bool `json:"valid"`
// Subject is the token subject (typically client ID).
Subject string `json:"subject"`
// ExpiresAt is the token expiration time as Unix timestamp.
ExpiresAt int64 `json:"expires_at"`
}
TokenValidation represents the result of validating a JWT token.
Use Client.ValidateToken to validate the current token:
validation, err := client.ValidateToken(ctx)
if err != nil {
log.Fatal(err)
}
if validation.Valid {
fmt.Printf("Token valid until: %d\n", validation.ExpiresAt)
}