verda

package
v0.0.6 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package verda provides a client for the Verda Cloud REST API and the `verda` CLI. Verda (ex-DataCrunch) is a European GPU/AI cloud. The package mirrors the shape of internal/vercel so wiring into cmd/, routing, ask-mode, and the desktop backend stays uniform.

Index

Constants

View Source
const (
	StatusRunning            = "running"
	StatusProvisioning       = "provisioning"
	StatusOffline            = "offline"
	StatusDiscontinued       = "discontinued"
	StatusUnknown            = "unknown"
	StatusOrdered            = "ordered"
	StatusNotFound           = "notfound"
	StatusNew                = "new"
	StatusError              = "error"
	StatusDeleting           = "deleting"
	StatusValidating         = "validating"
	StatusNoCapacity         = "no_capacity"
	StatusInstallationFailed = "installation_failed"
)

Resource status enum (shared by instances and clusters).

View Source
const (
	InstanceActionBoot          = "boot"
	InstanceActionStart         = "start"
	InstanceActionShutdown      = "shutdown"
	InstanceActionForceShutdown = "force_shutdown"
	InstanceActionDelete        = "delete"
	InstanceActionDiscontinue   = "discontinue"
	InstanceActionHibernate     = "hibernate"
	InstanceActionConfigureSpot = "configure_spot"
	InstanceActionDeleteStuck   = "delete_stuck"
	InstanceActionDeploy        = "deploy"
	InstanceActionTransfer      = "transfer"
)

Instance action enum (body of PUT /v1/instances).

View Source
const (
	VolumeActionAttach  = "attach"
	VolumeActionDetach  = "detach"
	VolumeActionDelete  = "delete"
	VolumeActionRename  = "rename"
	VolumeActionResize  = "resize"
	VolumeActionRestore = "restore"
	VolumeActionClone   = "clone"
	VolumeActionCancel  = "cancel"
	VolumeActionCreate  = "create"
	VolumeActionExport  = "export"
)

Volume action enum (body of PUT /v1/volumes).

View Source
const (
	VolumeTypeHDD               = "HDD"
	VolumeTypeNVMe              = "NVMe"
	VolumeTypeHDDShared         = "HDD_Shared"
	VolumeTypeNVMeShared        = "NVMe_Shared"
	VolumeTypeNVMeLocalStorage  = "NVMe_Local_Storage"
	VolumeTypeNVMeSharedCluster = "NVMe_Shared_Cluster"
	VolumeTypeNVMeOSCluster     = "NVMe_OS_Cluster"
)

Volume type enum.

View Source
const (
	ContractLongTerm     = "LONG_TERM"
	ContractPayAsYouGo   = "PAY_AS_YOU_GO"
	ContractSpot         = "SPOT"
	ExtensionAutoRenew   = "auto_renew"
	ExtensionPayAsYouGo  = "pay_as_you_go"
	ExtensionEndContract = "end_contract"
)

Contract enum.

View Source
const BaseURL = "https://api.verda.com"

BaseURL is the Verda API base; the `/v1` prefix is part of every path we call.

View Source
const MaxAnswerLengthInContext = 500

MaxAnswerLengthInContext limits how much of previous answers to include in context.

View Source
const MaxHistoryEntries = 20

MaxHistoryEntries limits the conversation history size.

Variables

View Source
var ErrCLINotInstalled = errors.New("verda CLI not installed")

ErrCLINotInstalled is returned by RunVerdaCLI* when the `verda` binary is missing from PATH. Typed so callers can surface a clear, one-shot error instead of the generic exec failure and can branch with errors.Is.

Functions

func CLIInstalled

func CLIInstalled() bool

CLIInstalled reports whether the `verda` binary is reachable on PATH. Fast (just a lookpath) so callers can branch UX eagerly — e.g., to hide a "login with verda CLI" hint when the binary isn't present but the REST credentials are configured and usable.

func CreateVerdaCommands

func CreateVerdaCommands() *cobra.Command

CreateVerdaCommands builds the `verda` command tree. Registered from cmd/root.go as a sibling of `cf`, `do`, `hetzner`, `vercel`.

func IsCLINotInstalled

func IsCLINotInstalled(err error) bool

IsCLINotInstalled reports whether err indicates the verda binary is missing. Shorthand for `errors.Is(err, ErrCLINotInstalled)` so call sites don't need to import errors explicitly.

func ResolveClientID

func ResolveClientID() string

ResolveClientID returns the Verda client ID. Resolution order: `verda.client_id` viper key → VERDA_CLIENT_ID env → parsed ~/.verda/credentials.

func ResolveClientSecret

func ResolveClientSecret() string

ResolveClientSecret returns the Verda client secret. Resolution order mirrors ResolveClientID.

func ResolveDefaultLocation

func ResolveDefaultLocation() string

ResolveDefaultLocation returns the configured default Verda location code (e.g. "FIN-01"). Used by create-flow helpers when the user doesn't pass --location.

func ResolveDefaultSSHKeyID

func ResolveDefaultSSHKeyID() string

ResolveDefaultSSHKeyID returns the configured default Verda SSH key UUID. Used by `clanker verda deploy` and the verda-instant k8s cluster provider.

func ResolveProjectID

func ResolveProjectID() string

ResolveProjectID returns the Verda project ID (used as conversation scope). Resolution order: `verda.default_project_id` → VERDA_PROJECT_ID → empty.

func ResolveSSHKeyPath

func ResolveSSHKeyPath() string

ResolveSSHKeyPath returns the local private-key path used when pulling kubeconfig off a Verda Instant Cluster's head node. Defaults to ~/.ssh/id_ed25519 if not configured.

func SetBaseURLForTest

func SetBaseURLForTest(url string) string

SetBaseURLForTest exposes the test-only base URL override to external test packages (e.g. internal/maker/exec_verda_test.go that need to run the full executor against an httptest server). Returns the previous value so tests can restore on cleanup. Never call from production code.

Types

type APIError

type APIError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

APIError is the body of any non-2xx Verda response.

func (*APIError) Error

func (e *APIError) Error() string

type ActionResult

type ActionResult struct {
	InstanceID string `json:"instanceId"`
	Action     string `json:"action"`
	Status     string `json:"status"`
	Error      string `json:"error,omitempty"`
	StatusCode int    `json:"statusCode,omitempty"`
}

ActionResult is an entry in a 207 Multi-Status response from PUT /v1/instances (and similar bulk-action endpoints). Verda returns one of these per target ID.

func DecodeActionResults

func DecodeActionResults(body string) ([]ActionResult, error)

DecodeActionResults decodes a 207 Multi-Status body from PUT /v1/instances. When the Verda API succeeds uniformly it returns 202 with a plain JSON body; a partial failure comes back as an array of ActionResult entries.

type Balance

type Balance struct {
	Amount   float64 `json:"amount"`
	Currency string  `json:"currency,omitempty"`
}

Balance is the GET /v1/balance payload.

type Client

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

Client wraps the Verda REST API (OAuth2 Client Credentials) and the official `verda` CLI. A single client is safe to share across goroutines — the token cache is mutex-protected.

func NewClient

func NewClient(clientID, clientSecret, projectID string, debug bool) (*Client, error)

NewClient creates a Verda client. The projectID is optional and used only for conversation-history keying.

func (*Client) ClientID

func (c *Client) ClientID() string

ClientID exposes the client ID (used for conversation-history keying).

func (*Client) GetRelevantContext

func (c *Client) GetRelevantContext(ctx context.Context, question string) (string, error)

GetRelevantContext gathers Verda context for LLM queries. Keyword-gated so we don't fetch every resource type on every question. Sections the user's question doesn't reference are skipped unless they're marked default.

func (*Client) ProjectID

func (c *Client) ProjectID() string

ProjectID exposes the project ID.

func (*Client) ResolveInstanceID

func (c *Client) ResolveInstanceID(ctx context.Context, nameOrID string) (string, error)

ResolveInstanceID returns the Verda instance UUID for the given input. If `nameOrID` already looks like a UUID it's returned verbatim (lowercased). Otherwise we list instances and match by hostname. Used by CLI commands so users can pass a friendly hostname where an ID is expected.

func (*Client) RunAPI

func (c *Client) RunAPI(method, path, body string) (string, error)

RunAPI executes a Verda REST call with retry/backoff.

func (*Client) RunAPIWithContext

func (c *Client) RunAPIWithContext(ctx context.Context, method, path, body string) (string, error)

RunAPIWithContext executes a Verda REST call with a caller-controlled context. Path should start with `/v1/...`. Bodies are passed as a JSON string (empty for GETs). Retries on 429 (honoring Retry-After) and 5xx with capped exponential backoff.

func (*Client) RunVerdaCLI

func (c *Client) RunVerdaCLI(args ...string) (string, error)

RunVerdaCLI shells out to the `verda` binary with `--agent` enforced so we get structured JSON on stdout. Useful for commands the CLI covers but we don't want to re-plumb through REST (e.g. `verda auth show`).

func (*Client) RunVerdaCLIWithContext

func (c *Client) RunVerdaCLIWithContext(ctx context.Context, args ...string) (string, error)

RunVerdaCLIWithContext runs the Verda CLI with a caller-controlled context. Credentials flow through env vars so the child process picks them up without needing a logged-in profile.

func (*Client) WaitClusterRunning

func (c *Client) WaitClusterRunning(ctx context.Context, id string, opts PollOptions) (*Cluster, error)

WaitClusterRunning polls GET /v1/clusters/{id} until the cluster reports a terminal status (running, error, discontinued, etc.) or the deadline hits.

func (*Client) WaitInstanceRunning

func (c *Client) WaitInstanceRunning(ctx context.Context, id string, opts PollOptions) (*Instance, error)

WaitInstanceRunning polls GET /v1/instances/{id} until the instance reports `running` or a terminal status, or the bounded deadline is reached.

func (*Client) WaitVolumeAvailable

func (c *Client) WaitVolumeAvailable(ctx context.Context, id string, opts PollOptions) (*Volume, error)

WaitVolumeAvailable polls GET /v1/volumes/{id} until the volume reports a stable status (`attached`, `created`, `detached`, `deleted`) or times out.

type Cluster

type Cluster struct {
	ID                string                `json:"id"`
	IP                string                `json:"ip,omitempty"` // jump host
	Status            string                `json:"status"`
	CreatedAt         string                `json:"created_at,omitempty"`
	CPU               HardwareDescriptor    `json:"cpu"`
	GPU               HardwareDescriptor    `json:"gpu"`
	GPUMemory         HardwareDescriptor    `json:"gpu_memory"`
	Memory            HardwareDescriptor    `json:"memory"`
	Hostname          string                `json:"hostname,omitempty"`
	Description       string                `json:"description,omitempty"`
	Location          string                `json:"location,omitempty"`
	PricePerHour      float64               `json:"price_per_hour,omitempty"`
	ClusterType       string                `json:"cluster_type,omitempty"`
	Image             string                `json:"image,omitempty"`
	OSName            string                `json:"os_name,omitempty"`
	StartupScriptID   string                `json:"startup_script_id,omitempty"`
	SSHKeyIDs         []string              `json:"ssh_key_ids,omitempty"`
	Contract          string                `json:"contract,omitempty"`
	ExtensionSettings string                `json:"extension_settings,omitempty"`
	LongTermPeriod    string                `json:"long_term_period,omitempty"`
	WorkerNodes       []WorkerNode          `json:"worker_nodes,omitempty"`
	SharedVolumes     []ClusterSharedVolume `json:"shared_volumes,omitempty"`
}

Cluster is the GET /v1/clusters/{id} payload.

type ClusterSharedVolume

type ClusterSharedVolume struct {
	ID              string `json:"id"`
	Name            string `json:"name"`
	MountPoint      string `json:"mount_point,omitempty"`
	SizeInGigabytes int    `json:"size_in_gigabytes,omitempty"`
}

ClusterSharedVolume describes an SFS mounted on a cluster.

type ConversationEntry

type ConversationEntry struct {
	Timestamp time.Time `json:"timestamp"`
	Question  string    `json:"question"`
	Answer    string    `json:"answer"`
}

ConversationEntry is a single Q&A exchange.

type ConversationHistory

type ConversationHistory struct {
	Entries []ConversationEntry `json:"entries"`
	ScopeID string              `json:"scope_id"`
	// contains filtered or unexported fields
}

ConversationHistory maintains conversation state for Verda ask mode.

func NewConversationHistory

func NewConversationHistory(scopeID string) *ConversationHistory

NewConversationHistory creates a new conversation history keyed by scope (project_id for team accounts, "personal" otherwise).

func (*ConversationHistory) AddEntry

func (h *ConversationHistory) AddEntry(question, answer string)

AddEntry records a new question/answer pair and prunes the log.

func (*ConversationHistory) GetRecentContext

func (h *ConversationHistory) GetRecentContext(maxEntries int) string

GetRecentContext returns a compact string of recent exchanges for the LLM prompt.

func (*ConversationHistory) Load

func (h *ConversationHistory) Load() error

Load restores history from disk. Missing file is not an error.

func (*ConversationHistory) Save

func (h *ConversationHistory) Save() error

Save persists the conversation to ~/.clanker/conversations/verda_<scope>.json. Write is atomic: marshal → write temp file → rename over the destination. A crash mid-write leaves either the old or new file intact but never a half-written blob that would poison subsequent loads. A per-scope file lock serializes concurrent Save calls for the same project — without it, two parallel `ask --verda` calls could race on the rename and drop one history.

type DeployClusterRequest

type DeployClusterRequest struct {
	ClusterType       string                    `json:"cluster_type"`
	Image             string                    `json:"image"`
	SSHKeyIDs         []string                  `json:"ssh_key_ids,omitempty"`
	StartupScriptID   string                    `json:"startup_script_id,omitempty"`
	Hostname          string                    `json:"hostname"`
	Description       string                    `json:"description"`
	LocationCode      string                    `json:"location_code"`
	Contract          string                    `json:"contract,omitempty"`
	ExtensionSettings string                    `json:"extension_settings,omitempty"`
	SharedVolume      *SharedVolumeDto          `json:"shared_volume,omitempty"`
	ExistingVolumes   []ExistingSharedVolumeDto `json:"existing_volumes,omitempty"`
	Coupon            string                    `json:"coupon,omitempty"`
}

DeployClusterRequest is the body of POST /v1/clusters.

type DeployInstanceRequest

type DeployInstanceRequest struct {
	InstanceType    string       `json:"instance_type"`
	Image           string       `json:"image"`
	Hostname        string       `json:"hostname"`
	Description     string       `json:"description"`
	LocationCode    string       `json:"location_code"`
	SSHKeyIDs       []string     `json:"ssh_key_ids,omitempty"`
	StartupScriptID string       `json:"startup_script_id,omitempty"`
	IsSpot          bool         `json:"is_spot,omitempty"`
	Coupon          string       `json:"coupon,omitempty"`
	Contract        string       `json:"contract,omitempty"`
	OSVolume        *OSVolumeDto `json:"os_volume,omitempty"`
	Volumes         []VolumeDto  `json:"volumes,omitempty"`
	ExistingVolumes []string     `json:"existing_volumes,omitempty"`
}

DeployInstanceRequest is the body of POST /v1/instances.

type ExistingSharedVolumeDto

type ExistingSharedVolumeDto struct {
	ID string `json:"id"`
}

ExistingSharedVolumeDto attaches a previously created SFS on cluster creation.

type HardwareDescriptor

type HardwareDescriptor struct {
	Description     string `json:"description,omitempty"`
	NumberOfCores   int    `json:"number_of_cores,omitempty"`
	NumberOfGpus    int    `json:"number_of_gpus,omitempty"`
	SizeInGigabytes int    `json:"size_in_gigabytes,omitempty"`
}

HardwareDescriptor is the uniform shape Verda uses for cpu/gpu/memory/storage blocks across instance, cluster, and instance-type responses.

type Image

type Image struct {
	ID        string   `json:"id,omitempty"`
	ImageType string   `json:"image_type,omitempty"`
	Name      string   `json:"name,omitempty"`
	Category  string   `json:"category,omitempty"`
	IsDefault bool     `json:"is_default,omitempty"`
	IsCluster bool     `json:"is_cluster,omitempty"`
	Details   []string `json:"details,omitempty"`
}

Image is an entry in GET /v1/images or /v1/images/cluster.

type Instance

type Instance struct {
	ID              string             `json:"id"`
	IP              string             `json:"ip,omitempty"`
	Status          string             `json:"status"`
	CreatedAt       string             `json:"created_at,omitempty"`
	CPU             HardwareDescriptor `json:"cpu"`
	GPU             HardwareDescriptor `json:"gpu"`
	GPUMemory       HardwareDescriptor `json:"gpu_memory"`
	Memory          HardwareDescriptor `json:"memory"`
	Storage         HardwareDescriptor `json:"storage"`
	Hostname        string             `json:"hostname,omitempty"`
	Description     string             `json:"description,omitempty"`
	Location        string             `json:"location,omitempty"`
	PricePerHour    float64            `json:"price_per_hour,omitempty"`
	IsSpot          bool               `json:"is_spot,omitempty"`
	InstanceType    string             `json:"instance_type,omitempty"`
	Image           string             `json:"image,omitempty"`
	OSName          string             `json:"os_name,omitempty"`
	StartupScriptID string             `json:"startup_script_id,omitempty"`
	SSHKeyIDs       []string           `json:"ssh_key_ids,omitempty"`
	OSVolumeID      string             `json:"os_volume_id,omitempty"`
	JupyterToken    string             `json:"jupyter_token,omitempty"`
	Contract        string             `json:"contract,omitempty"`
	Pricing         string             `json:"pricing,omitempty"`
	VolumeIDs       []string           `json:"volume_ids,omitempty"`
}

Instance is the GET /v1/instances/{id} payload.

type InstanceType

type InstanceType struct {
	ID                  string             `json:"id"`
	InstanceType        string             `json:"instance_type"`
	Name                string             `json:"name,omitempty"`
	DisplayName         string             `json:"display_name,omitempty"`
	Model               string             `json:"model,omitempty"`
	Description         string             `json:"description,omitempty"`
	Manufacturer        string             `json:"manufacturer,omitempty"`
	CPU                 HardwareDescriptor `json:"cpu"`
	GPU                 HardwareDescriptor `json:"gpu"`
	GPUMemory           HardwareDescriptor `json:"gpu_memory"`
	Memory              HardwareDescriptor `json:"memory"`
	Storage             HardwareDescriptor `json:"storage"`
	P2P                 string             `json:"p2p,omitempty"`
	PricePerHour        string             `json:"price_per_hour,omitempty"`
	SpotPrice           string             `json:"spot_price,omitempty"`
	ServerlessPrice     string             `json:"serverless_price,omitempty"`
	ServerlessSpotPrice string             `json:"serverless_spot_price,omitempty"`
	Currency            string             `json:"currency,omitempty"`
	BestFor             []string           `json:"best_for,omitempty"`
	DeployWarning       string             `json:"deploy_warning,omitempty"`
	SupportedOS         []string           `json:"supported_os,omitempty"`
}

InstanceType is an entry in GET /v1/instance-types.

type Location

type Location struct {
	Code        string `json:"code"`
	Name        string `json:"name"`
	CountryCode string `json:"country_code,omitempty"`
}

Location is an entry in GET /v1/locations.

type OSVolumeDto

type OSVolumeDto struct {
	Name              string `json:"name"`
	Size              int    `json:"size"`
	OnSpotDiscontinue string `json:"on_spot_discontinue,omitempty"`
}

OSVolumeDto is the nested OS-volume spec on DeployInstanceRequest.

type PerformInstanceActionRequest

type PerformInstanceActionRequest struct {
	Action            string      `json:"action"`
	ID                interface{} `json:"id"` // string or []string
	VolumeIDs         []string    `json:"volume_ids,omitempty"`
	DeletePermanently bool        `json:"delete_permanently,omitempty"`
}

PerformInstanceActionRequest is the body of PUT /v1/instances.

type PollOptions

type PollOptions struct {
	Interval time.Duration
	Max      time.Duration
}

PollOptions tune the bounded polling helpers.

type SSHKey

type SSHKey struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Key       string `json:"key,omitempty"`
	CreatedAt string `json:"created_at,omitempty"`
}

SSHKey is the GET /v1/ssh-keys/{id} payload.

type Script

type Script struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Script    string `json:"script,omitempty"`
	CreatedAt string `json:"created_at,omitempty"`
}

Script is the GET /v1/scripts/{id} payload.

type SharedVolumeDto

type SharedVolumeDto struct {
	Name string `json:"name"`
	Size int    `json:"size"`
}

SharedVolumeDto is the shared_volume block on cluster creation.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	Scope        string `json:"scope"`
}

TokenResponse is the body of POST /v1/oauth2/token.

type Volume

type Volume struct {
	ID           string   `json:"id"`
	InstanceID   string   `json:"instance_id,omitempty"`
	Name         string   `json:"name"`
	CreatedAt    string   `json:"created_at,omitempty"`
	Status       string   `json:"status"`
	Size         int      `json:"size"`
	IsOSVolume   bool     `json:"is_os_volume,omitempty"`
	Target       string   `json:"target,omitempty"`
	Type         string   `json:"type"`
	Location     string   `json:"location,omitempty"`
	SSHKeyIDs    []string `json:"ssh_key_ids,omitempty"`
	PseudoPath   string   `json:"pseudo_path,omitempty"`
	Contract     string   `json:"contract,omitempty"`
	BaseHourly   float64  `json:"base_hourly_cost,omitempty"`
	MonthlyPrice float64  `json:"monthly_price,omitempty"`
	Currency     string   `json:"currency,omitempty"`
}

Volume is the GET /v1/volumes/{id} payload (active; trash has additional fields).

type VolumeDto

type VolumeDto struct {
	Name              string `json:"name"`
	Size              int    `json:"size"`
	Type              string `json:"type"`
	OnSpotDiscontinue string `json:"on_spot_discontinue,omitempty"`
}

VolumeDto is the nested non-OS-volume spec on DeployInstanceRequest.

type WorkerNode

type WorkerNode struct {
	ID        string `json:"id"`
	Hostname  string `json:"hostname,omitempty"`
	PublicIP  string `json:"public_ip,omitempty"`
	PrivateIP string `json:"private_ip,omitempty"`
	Status    string `json:"status,omitempty"`
}

WorkerNode is an entry in Cluster.WorkerNodes.

Jump to

Keyboard shortcuts

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