Skip to content

Commit 4c2d084

Browse files
hi-leiMilosz Szewczak
andauthored
Feat/cli improvements (#5)
* feat: add structured output, shell completion, ssh, describe, and wait Add five CLI improvements to close the gap with modern cloud CLIs: 1. `--output json|yaml|table` global flag (-o) for structured output across all list and describe commands, enabling scripting and automation pipelines (e.g. `verda vm list -o json | jq`). 2. `verda completion` command for bash, zsh, fish, and powershell shell completions using Cobra's built-in generation. 3. `verda ssh <instance>` command to SSH into a running VM by hostname or ID. Resolves IP from the API and exec's into ssh. Supports --user, --key, and pass-through args via --. 4. `verda vm describe` and `verda volume describe` commands to show detailed info about a single resource (aliased as get/show), with full --output flag support. 5. `--wait` and `--wait-timeout` flags on vm create, vm action, volume create, and volume action. Generic polling utility with animated spinner for interactive use and silent mode for structured output. Includes 33 new unit tests covering all features. * docs: update README with new commands and global flags Add documentation for ssh, describe, completion commands, --output flag, structured output examples, and --wait flag. * feat: add images, availability, and cost commands Add three new command groups: - `verda images` — browse OS images with --type and --category filters. Shows name, category, and details (e.g., CUDA version). - `verda availability` — check instance type availability per location. Supports --location, --type, and --spot flags. Single-type check returns a boolean; full matrix shows all available types per datacenter. - `verda cost` — cost estimation, pricing, and billing: - `estimate` — calculate hourly/daily/monthly costs for an instance type + optional volumes, with spot pricing support - `price-history` — show historical fixed and dynamic pricing - `balance` — display current account balance All commands support --output json/yaml for scripting. Includes tests for cost math, formatting, and filtering logic. * fix(cost): use list-all endpoint instead of single-type lookup The /instance-types/{type} endpoint returns 404. Fetch all instance types and filter client-side instead. * fix(cost): use lowercase currency to match API expectations The Verda API requires lowercase 'usd', not 'USD'. * fix(cost): remove price-history subcommand The /instance-types/price-history endpoint has been removed by the API (410 Gone). Dynamic pricing is no longer supported. * feat: add verda locations command List available datacenter locations with code, name, and country. Supports --output json/yaml. * test+docs: add locations tests and update README * feat(cost): add running subcommand and live test script - `verda cost running` shows burn rate of all running instances with per-instance breakdown including attached volume costs - Add scripts/live-test.sh for automated integration testing * test+docs: add running cost tests and update README * fix: suppress spinners for structured output When --output is json or yaml, return nil from Factory.Status() so spinners don't write escape codes to stdout and break piping. * feat: add instance-types command and non-interactive vm list - `verda instance-types` lists all types with specs and pricing, with --gpu, --cpu, and --spot filters. Groups output by GPU vs CPU. - `verda vm list` now prints a static table when stdout is piped or redirected, instead of launching the interactive picker. Interactive mode still works in a terminal. * fix(instance-types): clean GPU descriptions and improve formatting - Strip duplicate count prefix from API GPU descriptions (e.g., "1x 1x H100 SXM5 80GB" → "1x H100 SXM5") - Strip trailing VRAM from description (shown in separate column) - Format large RAM values as TB (e.g., 1440GB → 1.4TB) - Widen GPU column for longer names * fix(vm list): dynamic hostname column width in table mode Compute max hostname length so columns stay aligned with long names. * docs: add instance-types to README * refactor: move renderVolumeSummary to dedicated view.go * refactor: extract UniqueVolumeIDs to shared util * feat: code review fixes, interactive pickers, and test infrastructure - Separate "Flags" from "Global Flags" in help output - Register --output flag completion values (json/yaml/table) - Remove dead JSON bool field from vm list options - Fix Poll() timeout to return error instead of nil - Consolidate duplicate polling code from status_view.go into shared wait.go - Centralize status messages and terminal statuses in status_messages.go - Add interactive picker for verda ssh (no-arg shows running instances) - Add interactive picker for vm describe and volume describe (no-arg shows list) - Add reusable TestFactory in cmd/util/testing.go for command-level tests - Add arg-validation and status message tests - Fix pre-existing lint issues (stuttering type names, hugeParam, goimports) - Ignore .ai/notes/ and docs/plans/ in .gitignore * fix: version output and instance card display issues - Version: show human-readable text by default, JSON only with -o json - Image: show image slug instead of volume name, append OS name if different - Compute: strip duplicate "1x" prefix from GPU description - Pricing: remove redundant field (only fixed pricing exists) - Storage Status: show actual status (attached/detached) with "(OS)" label * fix(version): show only version and platform by default Full build details still available via verda version -o json. * feat(vm): add availability command showing instance specs and pricing Joins instance type specs, pricing, and live availability data into a single view — sorted by price, filterable by location, type, kind, and spot. Addresses user request for a native alternative to the Python availability script. Usage: verda vm availability verda vm availability --location FIN-01 --kind gpu --spot verda vm availability -o json * fix: resolve gosec G204 and G602 lint warnings - ssh.go: suppress G204 for intentional syscall.Exec into ssh - images_test.go, instancetypes_test.go: use empty slice instead of nil to avoid G602 false positive on nil range --------- Co-authored-by: Milosz Szewczak <milosz@datacrunch.io>
1 parent c0b869a commit 4c2d084

56 files changed

Lines changed: 4242 additions & 191 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Build artifacts
22
bin/
33
_output/
4+
verda
45

56
# Local smoke-test output (see test/run.sh)
67
tmp/
@@ -360,3 +361,15 @@ tmp-test-*/
360361

361362
# VHS demo recordings
362363
vhs/
364+
365+
# Live test script (contains local testing logic)
366+
scripts/live-test.sh
367+
368+
# Claude Code local config (absolute paths, machine-specific)
369+
.claude/settings.json
370+
.claude/commands/
371+
.claude/topic-switch.json
372+
373+
# AI working notes and implementation plans (not shipped)
374+
.ai/notes/
375+
docs/plans/

README.md

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,22 @@ Auth Commands:
110110
111111
VM Commands:
112112
vm Manage VM instances
113+
ssh SSH into a running VM instance
113114
114115
Resource Commands:
116+
availability Check instance type availability
117+
images List available OS images
118+
instance-types List instance types with specs and pricing
119+
locations List datacenter locations
115120
ssh-key Manage SSH keys
116121
startup-script Manage startup scripts
117122
volume Manage volumes
118123
124+
Info Commands:
125+
cost Cost estimation, pricing, and billing
126+
119127
Other Commands:
128+
completion Generate shell completion scripts
120129
settings Manage CLI settings
121130
update Update Verda CLI to latest or specific version
122131
version Print version information
@@ -125,23 +134,76 @@ Other Commands:
125134
### VM
126135

127136

128-
| Command | Description |
129-
| ----------------- | ------------------------------------------ |
130-
| `verda vm create` | Create a VM (interactive wizard or flags) |
131-
| `verda vm list` | List and inspect VM instances |
132-
| `verda vm action` | Start, shutdown, hibernate, or delete a VM |
137+
| Command | Description |
138+
| -------------------- | ------------------------------------------ |
139+
| `verda vm create` | Create a VM (interactive wizard or flags) |
140+
| `verda vm list` | List and inspect VM instances |
141+
| `verda vm describe` | Show detailed info about a single VM |
142+
| `verda vm action` | Start, shutdown, hibernate, or delete a VM |
143+
144+
145+
### SSH
146+
147+
```bash
148+
# Connect by hostname
149+
verda ssh gpu-runner
133150

151+
# Connect with options
152+
verda ssh gpu-runner --user ubuntu --key ~/.ssh/id_ed25519
153+
154+
# Port forwarding and other ssh args
155+
verda ssh gpu-runner -- -L 8080:localhost:8080
156+
```
134157

135158
### Volume
136159

137160

138-
| Command | Description |
139-
| --------------------- | -------------------------------------------- |
140-
| `verda volume create` | Create a block storage volume |
141-
| `verda volume list` | List volumes |
142-
| `verda volume action` | Detach, rename, resize, clone, or delete |
143-
| `verda volume trash` | List deleted volumes (restorable within 96h) |
161+
| Command | Description |
162+
| ----------------------- | -------------------------------------------- |
163+
| `verda volume create` | Create a block storage volume |
164+
| `verda volume list` | List volumes |
165+
| `verda volume describe` | Show detailed info about a single volume |
166+
| `verda volume action` | Detach, rename, resize, clone, or delete |
167+
| `verda volume trash` | List deleted volumes (restorable within 96h) |
168+
169+
170+
### Instance Types, Images, Locations & Availability
144171

172+
```bash
173+
# Browse instance types with specs and pricing
174+
verda instance-types
175+
verda instance-types --gpu # GPU only
176+
verda instance-types --cpu # CPU only
177+
verda instance-types --spot # spot pricing
178+
179+
# List all OS images
180+
verda images
181+
verda images --type 1V100.6V # compatible with instance type
182+
verda images --category ubuntu # filter by category
183+
184+
# List datacenter locations
185+
verda locations
186+
187+
# Check capacity
188+
verda availability # full matrix
189+
verda availability --location FIN-01 # specific location
190+
verda availability --type 1V100.6V # specific type
191+
verda availability --spot # spot only
192+
```
193+
194+
### Cost & Billing
195+
196+
```bash
197+
# Estimate costs before creating
198+
verda cost estimate --type 1V100.6V --os-volume 100 --storage 500
199+
verda cost estimate --type 1V100.6V --spot
200+
201+
# See what your running instances are costing you
202+
verda cost running
203+
204+
# Account balance
205+
verda cost balance
206+
```
145207

146208
### SSH Keys & Startup Scripts
147209

@@ -183,15 +245,54 @@ Available themes: `default`, `dracula`, `catppuccin`, `catppuccin-latte`, `nord`
183245
| `verda auth use PROFILE` | Switch active auth profile |
184246

185247

248+
### Shell Completion
249+
250+
```bash
251+
# Bash
252+
source <(verda completion bash)
253+
254+
# Zsh (add to ~/.zshrc or run once)
255+
verda completion zsh > "${fpath[1]}/_verda"
256+
257+
# Fish
258+
verda completion fish | source
259+
```
260+
186261
## Global Flags
187262

188263

189-
| Flag | Description |
190-
| ------------ | ----------------------------------------------------- |
191-
| `--debug` | Enable debug output (API request/response details) |
192-
| `--timeout` | HTTP request timeout (default: 30s) |
193-
| `--base-url` | Override API base URL |
194-
| `--config` | Path to config file (default: `~/.verda/config.yaml`) |
264+
| Flag | Description |
265+
| ----------------- | ----------------------------------------------------- |
266+
| `--output, -o` | Output format: `table`, `json`, `yaml` (default: table) |
267+
| `--debug` | Enable debug output (API request/response details) |
268+
| `--timeout` | HTTP request timeout (default: 30s) |
269+
| `--base-url` | Override API base URL |
270+
| `--config` | Path to config file (default: `~/.verda/config.yaml`) |
271+
272+
### Structured Output
273+
274+
All list and describe commands support `--output json` and `--output yaml` for scripting:
275+
276+
```bash
277+
# Pipe to jq
278+
verda vm list -o json | jq '.[].hostname'
279+
280+
# YAML output
281+
verda volume describe vol-123 -o yaml
282+
283+
# Use in CI/CD scripts
284+
INSTANCE_ID=$(verda vm list -o json | jq -r '.[0].id')
285+
```
286+
287+
### Wait for Operations
288+
289+
Async commands support `--wait` to poll until completion:
290+
291+
```bash
292+
verda vm create --hostname gpu-runner --wait --wait-timeout 10m
293+
verda vm action --id abc-123 --wait
294+
verda volume create --name data --size 500 --wait
295+
```
195296

196297

197298
## Configuration

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ require (
1313
go.yaml.in/yaml/v3 v3.0.4
1414
)
1515

16-
require gopkg.in/ini.v1 v1.67.1
16+
require (
17+
github.com/charmbracelet/x/term v0.2.2
18+
gopkg.in/ini.v1 v1.67.1
19+
)
1720

1821
require (
1922
charm.land/bubbles/v2 v2.1.0 // indirect
@@ -23,7 +26,6 @@ require (
2326
github.com/charmbracelet/harmonica v0.2.0 // indirect
2427
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
2528
github.com/charmbracelet/x/ansi v0.11.6 // indirect
26-
github.com/charmbracelet/x/term v0.2.2 // indirect
2729
github.com/charmbracelet/x/termios v0.1.1 // indirect
2830
github.com/charmbracelet/x/windows v0.2.2 // indirect
2931
github.com/clipperhouse/displaywidth v0.11.0 // indirect

0 commit comments

Comments
 (0)