kraube

package module
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: MIT Imports: 26 Imported by: 0

README

Kraube API

Lightweight Go gateway for Anthropic Messages API via OAuth subscription

Access Claude (Opus, Sonnet, Haiku) through your Pro/Max/Team subscription. No API key needed.

CI Release Go Report Card

Documentation · Quick Start · TokenProvider · Releases


What it does

  • OAuth gateway to Anthropic Messages API through claude.ai subscription (Pro/Max/Team)
  • Stateless — token comes from any source via TokenProvider interface
  • Replicates Claude Code CLI protocol: billing header, metadata, beta headers, Chrome TLS
  • Streaming and non-streaming requests, tool use, extended thinking, vision, documents
  • Lightweight — minimal wrapper over HTTP, not a framework

Quick Start

Install

go get github.com/scott-walker/kraube-api

Authenticate

go build -o kraube ./cmd/kraube/
kraube login

Opens browser for OAuth, saves credentials to ~/.config/kraube/credentials.json. Override with --out PATH or KRAUBE_CREDENTIALS_PATH.

Use

ctx := context.Background()

client, err := kraube.NewClient(ctx, kraube.WithTokenFile(""))
if err != nil {
    log.Fatal(err)
}

resp, err := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    Messages:  []kraube.Message{kraube.UserMessage("Hello!")},
})
fmt.Println(resp.Text())

TokenProvider

Single interface for authentication. Token can come from anywhere:

type TokenProvider interface {
    Token(ctx context.Context) (string, error)
}

Built-in options:

Option Description Auto-refresh Multi-process safe
WithTokenFile(path) JSON credentials file + OS-level file lock Yes Yes
WithToken(token) Refresh token held in memory Yes No (in-memory only)
WithEnvToken(envVar) Refresh token from env var (in-memory) Yes No
WithTokenProvider(p) Any custom implementation Up to you Up to you
// From file (after kraube login). Safe across parallel processes on the
// same machine — refresh is serialized via flock(2) / LockFileEx.
client, _ := kraube.NewClient(ctx, kraube.WithTokenFile(""))

// Refresh token directly. In-memory only — rotations are not persisted.
client, _ := kraube.NewClient(ctx, kraube.WithToken(os.Getenv("MY_TOKEN")))

// Custom provider (Vault, Redis, DB...)
client, _ := kraube.NewClient(ctx, kraube.WithTokenProvider(myVaultProvider))

Usage Guide

Streaming

stream, err := client.Messages.Stream(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    Messages:  []kraube.Message{kraube.UserMessage("Tell me a story")},
})
defer stream.Close()

for stream.Next() {
    switch evt := stream.Event().(type) {
    case *kraube.ContentBlockStartEvent:
        if evt.ContentBlock.Type == "tool_use" {
            fmt.Printf("Tool: %s\n", evt.ContentBlock.Name)
        }
    case *kraube.ContentBlockDeltaEvent:
        if evt.Delta.Type == "text_delta" {
            fmt.Print(evt.Delta.Text) // real-time text
        }
    }
}
fmt.Println()

System Prompt

resp, _ := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:    kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    System:   kraube.SystemText("You are a helpful assistant."),
    Messages: []kraube.Message{kraube.UserMessage("Hi")},
})

Tool Use

tool := kraube.Tool{
    Name:        "get_weather",
    Description: "Get weather for a city",
    InputSchema: &kraube.Schema{
        Type: "object",
        Properties: map[string]*kraube.Schema{
            "city": {Type: "string", Desc: "City name"},
        },
        Required: []string{"city"},
    },
}

resp, _ := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    Tools:     []kraube.Tool{tool},
    Messages:  []kraube.Message{kraube.UserMessage("Weather in Tokyo?")},
})

if resp.HasToolUse() {
    for _, tu := range resp.ToolUses() {
        fmt.Printf("Tool: %s, Input: %s\n", tu.Name, string(tu.Input))
    }
}

Built-in tools:

kraube.WebSearchTool()      // web search
kraube.CodeExecutionTool()  // code execution
kraube.TextEditorTool()     // text editor (Claude Code style)
kraube.BashTool()           // bash execution

Extended Thinking

resp, _ := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelOpus4_6,
    MaxTokens: 8192,
    Thinking:  kraube.ThinkingEnabled(4096),
    Messages:  []kraube.Message{kraube.UserMessage("Solve this...")},
})

for _, b := range resp.ThinkingBlocks() {
    fmt.Println("Thinking:", b.Thinking)
}
fmt.Println("Answer:", resp.Text())

Vision

resp, _ := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    Messages: []kraube.Message{
        kraube.UserBlocks(
            kraube.TextBlock("What's in this image?"),
            kraube.ImageURLBlock("https://example.com/photo.jpg"),
        ),
    },
})

Error Handling

resp, err := client.Messages.Create(ctx, req)
if err != nil {
    var apiErr *kraube.APIError
    if errors.As(err, &apiErr) {
        switch {
        case apiErr.IsRateLimit():
            // wait and retry
        case apiErr.IsOverloaded():
            // server overloaded
        case apiErr.IsAuthentication():
            // invalid token
        }
    }
}

APIError.Error() renders as HTTP <status> <type>: <message> so the numeric status is always visible, even without a type assertion.

With --debug (or kraube.EnableDevLog()), every failing request emits a full api: error response log line containing the method, URL, HTTP status, local and remote socket addresses, proxy URL (redacted), request headers (with Authorization / Cookie redacted), request body, response headers, and response body — bounded to 8 KB per field so huge payloads don't flood stderr. Successful requests also log local_addr / remote_addr / proxy, which makes egress-IP problems (multi-homed hosts, split proxies) easy to diagnose.

Proxy

Kraube routes all traffic of a single Client instance — /v1/messages, the initial profile fetch, and OAuth token refresh — through a single transport. A single WithProxy(...) is enough; the library propagates the per-Client HTTPClient into the token manager, so refresh reuses the same tunnel. The Chrome TLS fingerprint is preserved end-to-end: the proxy only sees a plain TCP hop, and the uTLS handshake runs over the tunnel directly against api.anthropic.com and platform.claude.com.

Programmatic:

// Explicit proxy URL. Credentials in the URL become Basic proxy auth.
client, _ := kraube.NewClient(ctx,
    kraube.WithTokenFile(""),
    kraube.WithProxy("http://user:pass@proxy.example.com:8080"),
)

// SOCKS5 (username/password optional)
client, _ := kraube.NewClient(ctx,
    kraube.WithTokenFile(""),
    kraube.WithProxy("socks5://127.0.0.1:1080"),
)

// No explicit option — HTTPS_PROXY / ALL_PROXY are picked up from the
// environment automatically.
client, _ := kraube.NewClient(ctx, kraube.WithTokenFile(""))

// Force a direct connection, ignoring env variables.
client, _ := kraube.NewClient(ctx,
    kraube.WithTokenFile(""),
    kraube.WithProxy(""),
)

Supported schemes: http, https, socks5, socks5h. Any other scheme is a hard error rather than a silent fallback. A bare host:port is tolerated and assumed to be http://.

Standalone auth calls without a Client instance (Login, LoginManual, top-level FetchProfile) go through a package-level HTTP client. Point it through a proxy with SetAuthHTTPClient:

hc, _ := kraube.NewProxiedHTTPClient("http://proxy:8080")
kraube.SetAuthHTTPClient(hc)

creds, _ := kraube.LoginManual(ctx, readCode)

NewProxiedHTTPClient("") returns a client that follows HTTPS_PROXY / ALL_PROXY from the environment. Passing nil to SetAuthHTTPClient restores http.DefaultClient. For the normal kraube.NewClient(..., WithProxy(...)) path this helper is not required — refresh is already covered by WithProxy.

CLI:

kraube --proxy http://user:pass@proxy.example.com:8080 "hi"
kraube --proxy socks5://127.0.0.1:1080 stream "tell me a joke"

# Or via environment — no flag needed:
HTTPS_PROXY=http://proxy:8080 kraube "hi"

The --proxy flag applies to every subcommand (login, usage, query, stream) and is installed for OAuth calls as well, so kraube login --proxy ... also goes through the proxy.

Architecture

Your code ──▶ kraube.Client
                  │
          ┌───────┴──��────┐
          ▼               ▼
    TokenProvider     HTTP Transport
    (any source)      (Chrome TLS)
          │               │
          ▼               ▼
    OAuth Token    api.anthropic.com
                   + billing header
                   + metadata.user_id
                   + beta headers

CLI

go build -o kraube ./cmd/kraube/

kraube login                          # OAuth via browser
kraube "What is Go?"                  # send a message
kraube stream "Tell me..."            # stream response
kraube usage                          # subscription limits
kraube --debug "prompt"               # verbose logging
kraube --proxy http://user:pass@host:8080 "prompt"   # route via proxy

Available flags: --debug, --proxy URL, --out PATH (login only). The client also honors HTTPS_PROXY / ALL_PROXY and KRAUBE_CREDENTIALS_PATH from the environment.

Documentation

License

MIT

Documentation

Overview

Package kraube — легковесный Go-шлюз для доступа к Anthropic Messages API через OAuth подписку (Claude Pro/Max/Team).

Kraube API реплицирует HTTP-протокол Claude Code CLI — billing header, metadata.user_id, model-specific beta headers, Chrome TLS fingerprint — предоставляя полный доступ к API через подписку без API key.

Библиотека stateless по дизайну: токен может прийти откуда угодно через интерфейс TokenProvider.

Quick start:

// Из файла токена:
client, err := kraube.NewClient(ctx, kraube.WithTokenFile(""))

// Из токена напрямую:
client, err := kraube.NewClient(ctx, kraube.WithToken(token))

// Из env variable:
client, err := kraube.NewClient(ctx, kraube.WithEnvToken("KRAUBE_TOKEN"))

// Свой провайдер (Vault, Redis, DB, что угодно):
client, err := kraube.NewClient(ctx, kraube.WithTokenProvider(myProvider))

resp, err := client.Messages.Create(ctx, &kraube.MessageRequest{
    Model:     kraube.ModelSonnet4_6,
    MaxTokens: 1024,
    Messages:  []kraube.Message{kraube.UserMessage("Hello!")},
})

Index

Constants

View Source
const (
	DefaultBaseURL    = "https://api.anthropic.com"
	DefaultAPIVersion = "2023-06-01"
)
View Source
const CredentialsPathEnv = "KRAUBE_CREDENTIALS_PATH"

CredentialsPathEnv is the environment variable used to override the default credentials file location. Read by DefaultCredentialsPath and honored by both the CLI and WithTokenFile("").

Variables

View Source
var (
	ToolChoiceAuto = &ToolChoice{Type: "auto"}
	ToolChoiceAny  = &ToolChoice{Type: "any"}
	ToolChoiceNone = &ToolChoice{Type: "none"}
)

Functions

func DefaultCredentialsPath

func DefaultCredentialsPath() string

DefaultCredentialsPath returns the path where credentials are stored. Resolution order: $KRAUBE_CREDENTIALS_PATH → $XDG_CONFIG_HOME/kraube/credentials.json → ~/.config/kraube/credentials.json.

func DefaultRateLimitPath

func DefaultRateLimitPath() string

DefaultRateLimitPath returns ~/.config/kraube/ratelimit.json.

func EnableDevLog

func EnableDevLog()

EnableDevLog enables verbose stderr logging for development. Call once at startup: kraube.EnableDevLog()

func Float64

func Float64(v float64) *float64

Ptr helpers for optional fields.

func Int

func Int(v int) *int

func NewProxiedHTTPClient added in v0.4.0

func NewProxiedHTTPClient(proxyURL string) (*http.Client, error)

NewProxiedHTTPClient returns an *http.Client that uses kraube's Chrome- fingerprinted transport routed through the given proxy URL. Useful for installing a custom auth HTTP client via SetAuthHTTPClient, or as a starting point for WithHTTPClient.

Passing an empty proxyURL yields a client that reads HTTPS_PROXY / ALL_PROXY from the environment (same rules as NewClient without WithProxy). Pass a literal URL to force a specific proxy.

func SaveCredentials

func SaveCredentials(path string, creds *Credentials) error

SaveCredentials writes credentials to disk as JSON with restricted permissions (0600). The parent directory is created with 0700 if missing.

func SaveRateLimit

func SaveRateLimit(path string, rl *RateLimitInfo) error

SaveRateLimit writes rate limit info to disk.

func SetAuthHTTPClient added in v0.4.0

func SetAuthHTTPClient(c *http.Client)

SetAuthHTTPClient installs a package-level HTTP client used for OAuth token flows (login / refresh / exchange) and the standalone FetchProfile helper. Pass nil to restore http.DefaultClient.

func SetLogger

func SetLogger(l *slog.Logger)

SetLogger sets the package-level logger. Pass nil to disable logging.

Types

type APIError

type APIError struct {
	Type   string         `json:"type"` // "error"
	Detail APIErrorDetail `json:"error"`
	Status int            `json:"-"`
}

APIError represents an error response from the Anthropic API.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) IsAuthentication

func (e *APIError) IsAuthentication() bool

func (*APIError) IsInvalidRequest

func (e *APIError) IsInvalidRequest() bool

func (*APIError) IsNotFound

func (e *APIError) IsNotFound() bool

func (*APIError) IsOverloaded

func (e *APIError) IsOverloaded() bool

func (*APIError) IsPermission

func (e *APIError) IsPermission() bool

func (*APIError) IsRateLimit

func (e *APIError) IsRateLimit() bool

type APIErrorDetail

type APIErrorDetail struct {
	Type    string `json:"type"` // "invalid_request_error", "authentication_error", etc.
	Message string `json:"message"`
}

type CacheControl

type CacheControl struct {
	Type string `json:"type"`          // "ephemeral"
	TTL  string `json:"ttl,omitempty"` // "5m" or "1h"
}

type CallbackTokenProvider

type CallbackTokenProvider struct {
	// contains filtered or unexported fields
}

CallbackTokenProvider wraps a user-supplied function as a TokenProvider. The function should return a valid access token string.

func NewCallbackTokenProvider

func NewCallbackTokenProvider(fn func(ctx context.Context) (string, error)) *CallbackTokenProvider

NewCallbackTokenProvider creates a provider from a callback function. The function is called on each Token() invocation — the caller is responsible for caching/refreshing as needed.

func (*CallbackTokenProvider) Token

type Citation

type Citation struct {
	Type              string `json:"type"`
	CitedText         string `json:"cited_text,omitempty"`
	DocumentIndex     *int   `json:"document_index,omitempty"`
	DocumentTitle     string `json:"document_title,omitempty"`
	StartCharIndex    *int   `json:"start_char_index,omitempty"`
	EndCharIndex      *int   `json:"end_char_index,omitempty"`
	StartPageNumber   *int   `json:"start_page_number,omitempty"`
	EndPageNumber     *int   `json:"end_page_number,omitempty"`
	StartBlockIndex   *int   `json:"start_block_index,omitempty"`
	EndBlockIndex     *int   `json:"end_block_index,omitempty"`
	EncryptedIndex    string `json:"encrypted_index,omitempty"`
	Title             string `json:"title,omitempty"`
	URL               string `json:"url,omitempty"`
	Source            string `json:"source,omitempty"`
	SearchResultIndex *int   `json:"search_result_index,omitempty"`
}

type Client

type Client struct {
	BaseURL    string
	APIVersion string
	HTTPClient *http.Client

	// Auth (OAuth only — subscription-based access via claude.ai).
	Provider TokenProvider // token source

	// OAuth profile (fetched on init for metadata.user_id).
	Profile   *Profile
	SessionID string // unique per client instance
	DeviceID  string // stable device identifier

	// Rate limits — updated automatically from response headers after each API call.
	RateLimit     *RateLimitInfo
	RateLimitPath string // path to cache file (for persistence between CLI runs)

	Messages *MessagesService
	// contains filtered or unexported fields
}

Client is the Anthropic API client.

func NewClient

func NewClient(ctx context.Context, opts ...Option) (*Client, error)

NewClient creates a client with the given options.

client, err := kraube.NewClient(ctx, kraube.WithToken("..."))
client, err := kraube.NewClient(ctx, kraube.WithTokenFile(""))
client, err := kraube.NewClient(ctx, kraube.WithTokenProvider(myProvider))

type Content

type Content struct {
	Text   string
	Blocks []ContentBlock
}

Content represents message content: either a plain string or an array of ContentBlock.

func BlocksContent

func BlocksContent(blocks []ContentBlock) Content

BlocksContent creates a Content from content blocks.

func TextContent

func TextContent(s string) Content

TextContent creates a Content from a plain string.

func (Content) MarshalJSON

func (c Content) MarshalJSON() ([]byte, error)

func (*Content) UnmarshalJSON

func (c *Content) UnmarshalJSON(data []byte) error

type ContentBlock

type ContentBlock struct {
	Type string `json:"type"`

	// text
	Text      string     `json:"text,omitempty"`
	Citations []Citation `json:"citations,omitempty"`

	// image
	Source *ImageSource `json:"source,omitempty"`

	// document
	Title   string `json:"title,omitempty"`
	Context string `json:"context,omitempty"`

	// tool_use
	ID    string          `json:"id,omitempty"`
	Name  string          `json:"name,omitempty"`
	Input json.RawMessage `json:"input,omitempty"`

	// tool_result
	ToolUseID string   `json:"tool_use_id,omitempty"`
	IsError   bool     `json:"is_error,omitempty"`
	Content   *Content `json:"content,omitempty"`

	// thinking
	Thinking  string `json:"thinking,omitempty"`
	Signature string `json:"signature,omitempty"`

	// redacted_thinking
	Data string `json:"data,omitempty"`

	// cache_control (can be on any block)
	CacheControl *CacheControl `json:"cache_control,omitempty"`
}

ContentBlock is a polymorphic content element. Check Type to determine which fields are populated.

func ImageBase64Block

func ImageBase64Block(mediaType, data string) ContentBlock

func ImageURLBlock

func ImageURLBlock(url string) ContentBlock

func TextBlock

func TextBlock(text string) ContentBlock

func ThinkingBlock

func ThinkingBlock(thinking, signature string) ContentBlock

func ToolResultBlock

func ToolResultBlock(toolUseID string, content Content, isError bool) ContentBlock

func ToolUseBlock

func ToolUseBlock(id, name string, input json.RawMessage) ContentBlock

type ContentBlockDeltaEvent

type ContentBlockDeltaEvent struct {
	Type  string `json:"type"` // "content_block_delta"
	Index int    `json:"index"`
	Delta Delta  `json:"delta"`
}

func (*ContentBlockDeltaEvent) EventType added in v0.2.0

func (*ContentBlockDeltaEvent) EventType() string

type ContentBlockStartEvent

type ContentBlockStartEvent struct {
	Type         string       `json:"type"` // "content_block_start"
	Index        int          `json:"index"`
	ContentBlock ContentBlock `json:"content_block"`
}

func (*ContentBlockStartEvent) EventType added in v0.2.0

func (*ContentBlockStartEvent) EventType() string

type ContentBlockStopEvent

type ContentBlockStopEvent struct {
	Type  string `json:"type"` // "content_block_stop"
	Index int    `json:"index"`
}

func (*ContentBlockStopEvent) EventType added in v0.2.0

func (*ContentBlockStopEvent) EventType() string

type CountTokensRequest

type CountTokensRequest struct {
	Model    Model           `json:"model"`
	Messages []Message       `json:"messages"`
	System   *SystemPrompt   `json:"system,omitempty"`
	Tools    []Tool          `json:"tools,omitempty"`
	Thinking *ThinkingConfig `json:"thinking,omitempty"`
}

CountTokensRequest is the request body for POST /v1/messages/count_tokens.

type CountTokensResponse

type CountTokensResponse struct {
	InputTokens int `json:"input_tokens"`
}

CountTokensResponse is the response from POST /v1/messages/count_tokens.

type Credentials

type Credentials struct {
	RefreshToken string `json:"refreshToken"`
	AccessToken  string `json:"accessToken"`
	ExpiresAt    int64  `json:"expiresAt"` // unix ms
}

Credentials is the on-disk representation of an OAuth session. All three fields are persisted together so any process on the machine can reuse a live access token without performing an unnecessary refresh.

func LoadCredentials

func LoadCredentials(path string) (*Credentials, error)

LoadCredentials reads credentials from disk. Returns an error if the file is missing, empty, or malformed.

func Login

func Login(ctx context.Context, openBrowser func(url string) error) (*Credentials, error)

Login performs the full OAuth authorization_code flow with PKCE. It opens the browser and waits for the callback. Returns the full credentials (refresh + access + expiresAt) for storage on success.

func LoginManual

func LoginManual(ctx context.Context, readCode func(authURL string) (string, error)) (*Credentials, error)

LoginManual performs OAuth login without a local callback server. It prints the authorization URL and waits for the user to paste the code. Ideal for headless/SSH environments.

The readCode function should prompt the user and return the pasted code.

func (*Credentials) IsAccessLive added in v0.3.0

func (c *Credentials) IsAccessLive() bool

IsAccessLive reports whether the stored access token is still valid with a 60-second safety margin.

type Delta

type Delta struct {
	Type        string `json:"type"` // "text_delta", "input_json_delta", "thinking_delta", "signature_delta"
	Text        string `json:"text,omitempty"`
	PartialJSON string `json:"partial_json,omitempty"`
	Thinking    string `json:"thinking,omitempty"`
	Signature   string `json:"signature,omitempty"`
}

Delta is the incremental content in a content_block_delta.

type DeltaFinal

type DeltaFinal struct {
	StopReason   StopReason `json:"stop_reason"`
	StopSequence *string    `json:"stop_sequence,omitempty"`
}

DeltaFinal is the delta in a message_delta event.

type ErrorEvent

type ErrorEvent struct {
	Type  string         `json:"type"` // "error"
	Error APIErrorDetail `json:"error"`
}

ErrorEvent is an error during streaming.

func (*ErrorEvent) EventType added in v0.2.0

func (*ErrorEvent) EventType() string

type ImageSource

type ImageSource struct {
	Type      string `json:"type"`
	MediaType string `json:"media_type,omitempty"`
	Data      string `json:"data,omitempty"`
	URL       string `json:"url,omitempty"`
}

type Message

type Message struct {
	Role    Role    `json:"role"`
	Content Content `json:"content"`
}

Message is a conversation turn (user or assistant).

func AssistantBlocks

func AssistantBlocks(blocks ...ContentBlock) Message

AssistantBlocks creates an assistant message from content blocks.

func AssistantMessage

func AssistantMessage(text string) Message

AssistantMessage creates an assistant message from a string.

func UserBlocks

func UserBlocks(blocks ...ContentBlock) Message

UserBlocks creates a user message from content blocks.

func UserMessage

func UserMessage(text string) Message

UserMessage creates a user message from a string.

type MessageDeltaEvent

type MessageDeltaEvent struct {
	Type  string     `json:"type"` // "message_delta"
	Delta DeltaFinal `json:"delta"`
	Usage Usage      `json:"usage"`
}

func (*MessageDeltaEvent) EventType added in v0.2.0

func (*MessageDeltaEvent) EventType() string

type MessageRequest

type MessageRequest struct {
	Model         Model           `json:"model"`
	MaxTokens     int             `json:"max_tokens"`
	Messages      []Message       `json:"messages"`
	System        *SystemPrompt   `json:"system,omitempty"`
	Temperature   *float64        `json:"temperature,omitempty"`
	TopP          *float64        `json:"top_p,omitempty"`
	TopK          *int            `json:"top_k,omitempty"`
	StopSequences []string        `json:"stop_sequences,omitempty"`
	Tools         []Tool          `json:"tools,omitempty"`
	ToolChoice    *ToolChoice     `json:"tool_choice,omitempty"`
	Stream        bool            `json:"stream,omitempty"`
	Thinking      *ThinkingConfig `json:"thinking,omitempty"`
	OutputConfig  *OutputConfig   `json:"output_config,omitempty"`
	Metadata      *Metadata       `json:"metadata,omitempty"`
	ServiceTier   string          `json:"service_tier,omitempty"`
	CacheControl  *CacheControl   `json:"cache_control,omitempty"`
}

MessageRequest is the request body for POST /v1/messages.

type MessageResponse

type MessageResponse struct {
	ID           string         `json:"id"`
	Type         string         `json:"type"` // "message"
	Role         Role           `json:"role"`
	Content      []ContentBlock `json:"content"`
	Model        string         `json:"model"`
	StopReason   StopReason     `json:"stop_reason"`
	StopSequence *string        `json:"stop_sequence,omitempty"`
	Usage        Usage          `json:"usage"`
}

MessageResponse is the response from POST /v1/messages (non-streaming).

func (*MessageResponse) HasToolUse

func (r *MessageResponse) HasToolUse() bool

HasToolUse returns true if the model wants to call a tool.

func (*MessageResponse) Text

func (r *MessageResponse) Text() string

Text returns concatenated text from all text content blocks.

func (*MessageResponse) ThinkingBlocks

func (r *MessageResponse) ThinkingBlocks() []ContentBlock

ThinkingBlocks returns all thinking blocks from the response.

func (*MessageResponse) ToolUses

func (r *MessageResponse) ToolUses() []ContentBlock

ToolUses returns all tool_use blocks from the response.

type MessageStartEvent

type MessageStartEvent struct {
	Type    string          `json:"type"` // "message_start"
	Message MessageResponse `json:"message"`
}

func (*MessageStartEvent) EventType added in v0.2.0

func (*MessageStartEvent) EventType() string

type MessageStopEvent

type MessageStopEvent struct {
	Type string `json:"type"` // "message_stop"
}

func (*MessageStopEvent) EventType added in v0.2.0

func (*MessageStopEvent) EventType() string

type MessagesService

type MessagesService struct {
	// contains filtered or unexported fields
}

MessagesService handles the /v1/messages endpoints.

func (*MessagesService) CountTokens

CountTokens counts tokens for a message request without sending it.

func (*MessagesService) Create

Create sends a non-streaming message request.

func (*MessagesService) Stream

Stream sends a streaming message request and returns a StreamReader. The caller must call StreamReader.Close() when done.

type Metadata

type Metadata struct {
	UserID string `json:"user_id,omitempty"`
}

type Model

type Model = string

Model identifiers for Anthropic models.

const (
	ModelOpus4_6   Model = "claude-opus-4-6"
	ModelSonnet4_6 Model = "claude-sonnet-4-6"
	ModelHaiku4_5  Model = "claude-haiku-4-5"
	ModelOpus4_5   Model = "claude-opus-4-5"
	ModelSonnet4_5 Model = "claude-sonnet-4-5"
	ModelOpus4_1   Model = "claude-opus-4-1"
	ModelOpus4_0   Model = "claude-opus-4-0"
	ModelSonnet4_0 Model = "claude-sonnet-4-0"
	ModelHaiku3    Model = "claude-3-haiku-20240307"
)

type Option

type Option func(*clientConfig)

Option configures a Client during construction.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL overrides the API base URL (default: https://api.anthropic.com).

func WithEnvToken

func WithEnvToken(envVar string) Option

WithEnvToken reads the refresh token from the given environment variable. Rotation is held in memory only.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets a custom HTTP client. By default, the client uses a Chrome TLS fingerprinted transport.

func WithProxy added in v0.4.0

func WithProxy(proxyURL string) Option

WithProxy routes all outbound API traffic through the given proxy URL. Supported schemes: http, https, socks5, socks5h. The URL may embed credentials (http://user:pass@host:port) for Basic proxy auth.

When this option is omitted, the client automatically honors the HTTPS_PROXY / ALL_PROXY environment variables (in that order). Passing an empty string here explicitly disables both env lookup and proxying, forcing a direct connection even if the environment is set.

client, _ := kraube.NewClient(ctx,
    kraube.WithTokenFile(""),
    kraube.WithProxy("http://user:pass@proxy.example.com:8080"),
)

func WithToken added in v0.2.0

func WithToken(refreshToken string) Option

WithToken creates a client with the given refresh token held in memory.

Rotated refresh tokens are kept in memory only — when the process exits, they are lost. For a persistent setup that survives restarts and works across parallel processes, use WithTokenFile instead.

func WithTokenFile added in v0.2.0

func WithTokenFile(path string) Option

WithTokenFile loads credentials from a JSON file on disk. Pass "" to use the default path (honors $KRAUBE_CREDENTIALS_PATH, otherwise ~/.config/kraube/credentials.json).

This mode uses an OS-level file lock to serialize refresh operations across processes, so multiple instances on the same machine share a single rotated refresh token safely.

func WithTokenProvider

func WithTokenProvider(p TokenProvider) Option

WithTokenProvider sets a custom TokenProvider. This is the most flexible option — implement the TokenProvider interface to supply access tokens from any source.

func WithoutProfile

func WithoutProfile() Option

WithoutProfile skips fetching the OAuth profile on init. Useful for performance-sensitive scenarios where metadata.user_id is not needed.

type OutputConfig

type OutputConfig struct {
	Format *OutputFormat `json:"format,omitempty"`
	Effort string        `json:"effort,omitempty"` // "low", "medium", "high", "max"
}

type OutputFormat

type OutputFormat struct {
	Type   string          `json:"type"` // "json_schema"
	Schema json.RawMessage `json:"schema"`
}

type PingEvent

type PingEvent struct {
	Type string `json:"type"` // "ping"
}

PingEvent is a keepalive ping.

func (*PingEvent) EventType added in v0.2.0

func (*PingEvent) EventType() string

type Profile

type Profile struct {
	AccountUUID      string `json:"account_uuid"`
	OrganizationUUID string `json:"organization_uuid"`
	DisplayName      string `json:"display_name"`
	Email            string `json:"email"`
}

Profile holds account info from the OAuth profile endpoint.

func FetchProfile

func FetchProfile(ctx context.Context, accessToken string) (*Profile, error)

FetchProfile retrieves the user's profile using an OAuth access token. Uses the package-level authHTTPClient (see SetAuthHTTPClient).

type RateLimitInfo

type RateLimitInfo struct {
	// Per-window utilization (nil if not present in response).
	FiveHour *RateLimitWindow // 5-hour session window
	SevenDay *RateLimitWindow // 7-day weekly window

	// Unified status from headers.
	Status   string // "allowed", "rejected", "allowed_warning"
	ResetsAt time.Time
	Claim    string // "five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage"
	Fallback bool   // fallback mode available

	// Extra usage / overage.
	OverageStatus         string // overage status
	OverageResetsAt       time.Time
	OverageDisabledReason string

	// Timestamp when this info was last updated.
	UpdatedAt time.Time
}

RateLimitInfo holds the latest rate limit state from API response headers. Updated automatically after each API call.

func LoadRateLimit

func LoadRateLimit(path string) (*RateLimitInfo, error)

LoadRateLimit reads cached rate limit info from disk.

func (*RateLimitInfo) HasData

func (r *RateLimitInfo) HasData() bool

HasData returns true if any rate limit data has been received.

type RateLimitWindow

type RateLimitWindow struct {
	Utilization float64   // 0.0–1.0 (fraction of limit used)
	ResetsAt    time.Time // when this window resets
}

RateLimitWindow holds utilization data for a single rate limit window.

func (*RateLimitWindow) TimeUntilReset

func (w *RateLimitWindow) TimeUntilReset() time.Duration

TimeUntilReset returns duration until the window resets.

func (*RateLimitWindow) UsedPercent

func (w *RateLimitWindow) UsedPercent() float64

UsedPercent returns utilization as a percentage (0–100).

type Role

type Role = string
const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
)

type Schema

type Schema struct {
	Type       string             `json:"type"`
	Properties map[string]*Schema `json:"properties,omitempty"`
	Required   []string           `json:"required,omitempty"`
	Items      *Schema            `json:"items,omitempty"`
	Enum       []string           `json:"enum,omitempty"`
	Desc       string             `json:"description,omitempty"`

	// Additional JSON Schema fields
	MinItems *int      `json:"minItems,omitempty"`
	MaxItems *int      `json:"maxItems,omitempty"`
	Minimum  *float64  `json:"minimum,omitempty"`
	Maximum  *float64  `json:"maximum,omitempty"`
	Pattern  string    `json:"pattern,omitempty"`
	Default  any       `json:"default,omitempty"`
	AnyOf    []*Schema `json:"anyOf,omitempty"`
	OneOf    []*Schema `json:"oneOf,omitempty"`
}

Schema is a JSON Schema object for tool inputs.

type StopReason

type StopReason = string
const (
	StopReasonEndTurn      StopReason = "end_turn"
	StopReasonMaxTokens    StopReason = "max_tokens"
	StopReasonStopSequence StopReason = "stop_sequence"
	StopReasonToolUse      StopReason = "tool_use"
)

type StreamEvent

type StreamEvent interface {
	EventType() string
}

StreamEvent is the interface satisfied by all typed SSE events. Use a type switch to handle specific event types in the streaming loop.

type StreamReader

type StreamReader struct {
	// contains filtered or unexported fields
}

StreamReader reads SSE events from a streaming response. After each Next() call, Event() returns the current typed event and CurrentBlock() returns the content block being built (if applicable).

func (*StreamReader) Close

func (r *StreamReader) Close() error

Close releases the underlying response body.

func (*StreamReader) CurrentBlock added in v0.2.0

func (r *StreamReader) CurrentBlock() *ContentBlock

CurrentBlock returns the content block associated with the current event, reflecting accumulated state so far (e.g. tool_use with partial input). Returns nil for events without a block (message_start, message_delta, ping).

func (*StreamReader) Err

func (r *StreamReader) Err() error

Err returns any error encountered during streaming.

func (*StreamReader) Event added in v0.2.0

func (r *StreamReader) Event() StreamEvent

Event returns the current SSE event from the last Next() call. Use a type switch to handle specific event types:

for stream.Next() {
    switch evt := stream.Event().(type) {
    case *kraube.ContentBlockDeltaEvent:
        fmt.Print(evt.Delta.Text)
    case *kraube.ContentBlockStartEvent:
        fmt.Println("Block:", evt.ContentBlock.Type)
    }
}

Returns nil before the first Next() call or after the stream ends.

func (*StreamReader) EventType added in v0.2.0

func (r *StreamReader) EventType() string

EventType returns the type string of the current event (e.g. "content_block_delta", "message_start"). Returns "" if no current event.

func (*StreamReader) Message

func (r *StreamReader) Message() *MessageResponse

Message returns the accumulated message after streaming completes.

func (*StreamReader) Next

func (r *StreamReader) Next() bool

Next advances to the next SSE event. Returns false when done or on error. After Next returns true, call Event() to access the current event.

type SystemBlock

type SystemBlock struct {
	Type         string        `json:"type"` // "text"
	Text         string        `json:"text"`
	CacheControl *CacheControl `json:"cache_control,omitempty"`
}

type SystemPrompt

type SystemPrompt struct {
	Text   string
	Blocks []SystemBlock
}

SystemPrompt can be a plain string or an array of text blocks (for caching).

func SystemBlocks

func SystemBlocks(blocks ...SystemBlock) *SystemPrompt

func SystemText

func SystemText(s string) *SystemPrompt

func (SystemPrompt) MarshalJSON

func (s SystemPrompt) MarshalJSON() ([]byte, error)

func (*SystemPrompt) UnmarshalJSON

func (s *SystemPrompt) UnmarshalJSON(data []byte) error

type ThinkingConfig

type ThinkingConfig struct {
	Type         string `json:"type"` // "enabled", "disabled", "adaptive"
	BudgetTokens int    `json:"budget_tokens,omitempty"`
	Display      string `json:"display,omitempty"` // "summarized", "omitted"
}

func ThinkingAdaptive

func ThinkingAdaptive() *ThinkingConfig

func ThinkingDisabled

func ThinkingDisabled() *ThinkingConfig

func ThinkingEnabled

func ThinkingEnabled(budgetTokens int) *ThinkingConfig

type TokenProvider

type TokenProvider interface {
	// Token returns a valid access token ready for use in API requests.
	// Implementations should handle refreshing internally if needed.
	Token(ctx context.Context) (string, error)
}

TokenProvider abstracts how access tokens are obtained. The client calls Token() before each request to get a valid access token string.

type Tool

type Tool struct {
	Type         string        `json:"type,omitempty"` // "custom" or built-in type
	Name         string        `json:"name"`
	Description  string        `json:"description,omitempty"`
	InputSchema  *Schema       `json:"input_schema,omitempty"`
	CacheControl *CacheControl `json:"cache_control,omitempty"`
}

Tool defines a tool the model can use.

func BashTool

func BashTool() Tool

func CodeExecutionTool

func CodeExecutionTool() Tool

func TextEditorTool

func TextEditorTool() Tool

func WebSearchTool

func WebSearchTool(opts ...func(*Tool)) Tool

type ToolChoice

type ToolChoice struct {
	Type                   string `json:"type"` // "auto", "any", "tool", "none"
	Name                   string `json:"name,omitempty"`
	DisableParallelToolUse bool   `json:"disable_parallel_tool_use,omitempty"`
}

func ToolChoiceTool

func ToolChoiceTool(name string) *ToolChoice

type Usage

type Usage struct {
	InputTokens              int  `json:"input_tokens"`
	OutputTokens             int  `json:"output_tokens"`
	CacheCreationInputTokens *int `json:"cache_creation_input_tokens,omitempty"`
	CacheReadInputTokens     *int `json:"cache_read_input_tokens,omitempty"`
}

Directories

Path Synopsis
cmd
kraube command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL