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
- Variables
- func CLIInstalled() bool
- func CreateVerdaCommands() *cobra.Command
- func IsCLINotInstalled(err error) bool
- func ResolveClientID() string
- func ResolveClientSecret() string
- func ResolveDefaultLocation() string
- func ResolveDefaultSSHKeyID() string
- func ResolveProjectID() string
- func ResolveSSHKeyPath() string
- func SetBaseURLForTest(url string) string
- type APIError
- type ActionResult
- type Balance
- type Client
- func (c *Client) ClientID() string
- func (c *Client) GetRelevantContext(ctx context.Context, question string) (string, error)
- func (c *Client) ProjectID() string
- func (c *Client) ResolveInstanceID(ctx context.Context, nameOrID string) (string, error)
- func (c *Client) RunAPI(method, path, body string) (string, error)
- func (c *Client) RunAPIWithContext(ctx context.Context, method, path, body string) (string, error)
- func (c *Client) RunVerdaCLI(args ...string) (string, error)
- func (c *Client) RunVerdaCLIWithContext(ctx context.Context, args ...string) (string, error)
- func (c *Client) WaitClusterRunning(ctx context.Context, id string, opts PollOptions) (*Cluster, error)
- func (c *Client) WaitInstanceRunning(ctx context.Context, id string, opts PollOptions) (*Instance, error)
- func (c *Client) WaitVolumeAvailable(ctx context.Context, id string, opts PollOptions) (*Volume, error)
- type Cluster
- type ClusterSharedVolume
- type ConversationEntry
- type ConversationHistory
- type DeployClusterRequest
- type DeployInstanceRequest
- type ExistingSharedVolumeDto
- type HardwareDescriptor
- type Image
- type Instance
- type InstanceType
- type Location
- type OSVolumeDto
- type PerformInstanceActionRequest
- type PollOptions
- type SSHKey
- type Script
- type SharedVolumeDto
- type TokenResponse
- type Volume
- type VolumeDto
- type WorkerNode
Constants ¶
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).
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).
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).
const ( VolumeTypeHDD = "HDD" VolumeTypeNVMe = "NVMe" VolumeTypeNVMeLocalStorage = "NVMe_Local_Storage" VolumeTypeNVMeOSCluster = "NVMe_OS_Cluster" )
Volume type enum.
const ( ContractLongTerm = "LONG_TERM" ContractPayAsYouGo = "PAY_AS_YOU_GO" ContractSpot = "SPOT" ExtensionAutoRenew = "auto_renew" ExtensionPayAsYouGo = "pay_as_you_go" ExtensionEndContract = "end_contract" )
Contract enum.
const BaseURL = "https://api.verda.com"
BaseURL is the Verda API base; the `/v1` prefix is part of every path we call.
const MaxAnswerLengthInContext = 500
MaxAnswerLengthInContext limits how much of previous answers to include in context.
const MaxHistoryEntries = 20
MaxHistoryEntries limits the conversation history size.
Variables ¶
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 ¶
CreateVerdaCommands builds the `verda` command tree. Registered from cmd/root.go as a sibling of `cf`, `do`, `hetzner`, `vercel`.
func IsCLINotInstalled ¶
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 ¶
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 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 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 ¶
NewClient creates a Verda client. The projectID is optional and used only for conversation-history keying.
func (*Client) GetRelevantContext ¶
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) ResolveInstanceID ¶
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) RunAPIWithContext ¶
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 ¶
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 ¶
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"`
}
Cluster is the GET /v1/clusters/{id} payload.
type ClusterSharedVolume ¶
type ClusterSharedVolume struct {
}
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"`
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 {
}
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 ¶
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 {
}
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).