A Go CLI for the ModelsLab platform. It covers account management, model discovery, AI generation (image/video/audio/3D/chat), billing, wallet, subscriptions, teams, usage analytics, and MCP server mode. ~10k lines of Go across 26 files, 105 subcommands in 14 groups.
# Build
go build -o modelslab ./cmd/modelslab/
# Run unit tests
go test ./internal/... -v
# Run integration tests (requires local server)
cd ~/Documents/GitHub/modelslab-frontend-v2 && php artisan serve --port=8888 &
MODELSLAB_TEST_TOKEN="<token>" MODELSLAB_TEST_API_KEY="<key>" go test ./tests/ -v -base-url http://127.0.0.1:8888
# Snapshot release build (no publish)
goreleaser release --snapshot --clean
# Lint
go vet ./...cmd/modelslab/main.go # Entry point, version injection via ldflags
internal/
api/client.go # HTTP client (retry, rate limiting, dual auth)
api/client_test.go # 14 unit tests with httptest
auth/keyring.go # Credential storage (OS keychain + file fallback)
cmd/
root.go # Root command, global flags, getClient(), outputResult()
helpers.go # extractItems(), extractData(), firstNonNil()
auth.go # 12 auth commands (login, signup, logout, tokens, etc.)
profile.go # 6 profile commands
keys.go # 5 API key commands
models.go # 8 model discovery commands
generate.go # 20 generation commands + polling + download
billing.go # 13 billing commands + Stripe tokenization + payment links + setup intents
wallet.go # 10 wallet commands
subscriptions.go # 10 subscription commands
teams.go # 7 team commands
usage.go # 3 usage analytics commands
config.go # 6 config/profile commands
docs.go # 2 docs commands
completion.go # Shell completions
mcp.go # MCP serve + tools list commands
config/config.go # Viper config management
config/config_test.go # 7 unit tests
mcp/server.go # MCP server (~30 tools, stdio/SSE)
output/formatter.go # JSON, table, jq, key-value formatting
output/formatter_test.go # 8 unit tests
tests/integration_test.go # 30 integration tests against local server
The CLI uses two credential types stored in the OS keychain (go-keyring) with file fallback at ~/.config/modelslab/profiles/{profile}.json:
- Bearer token — for control plane endpoints (
/api/agents/v1/*) — profile, keys, billing, wallet, subscriptions, teams, usage - API key — for generation endpoints (
/api/*) — image, video, audio, 3D, chat
internal/api/client.go exposes:
DoControlPlane(method, path, body)— uses Bearer tokenDoGeneration(method, path, body)— uses API key in JSON bodyDoControlPlaneIdempotent(...)— addsIdempotency-KeyUUID header for billing mutations
The backend returns two patterns. The extractItems() helper in helpers.go handles both:
Paginated: { "data": { "items": [...], "pagination": {...} }, "error": null }
Direct: { "data": [...], "error": null }
Single: { "data": { "field": "value" }, "error": null }
Use extractItems() for lists, extractData() for single objects.
API responses use inconsistent field names. The firstNonNil(map, keys...) helper tries multiple field names:
name := firstNonNil(item, "model_name", "name", "title")Generation commands use async polling (pollAndDownload in generate.go):
- Submit request → get
request_id - Poll
GET /api/v6/images/fetch/{id}with exponential backoff (1s → 2s → 4s → 8s → 10s cap) - Timeout after 5 minutes
- Auto-download output files to
./generated/directory
--no-wait flag skips polling and returns immediately.
Every command supports --output json and --jq '<expression>' via outputResult() in root.go. Human mode prints colored tables. The internal/output/formatter.go handles all formatting.
CLI flags → env vars (MODELSLAB_*) → project config → user config (~/.config/modelslab/config.toml) → defaults.
internal/mcp/server.go registers ~37 tools covering both control plane and generation endpoints. Supports stdio (default) and sse transports. Used by Claude Desktop/Code:
{ "mcpServers": { "modelslab": { "command": "modelslab", "args": ["mcp", "serve"] } } }- Command registration: Each
internal/cmd/*.gofile creates aninit()function that adds commands to the root or parent group viarootCmd.AddCommand(). - Error handling:
api.Clientreturns semantic exit codes (3=auth, 4=rate-limit, 5=not-found, 6=payment, 7=timeout, 10=network). Commands propagate these viaos.Exit(). - Retry logic: The HTTP client auto-retries 429 and 5xx responses with exponential backoff (max 3 retries). Rate limit headers
X-RateLimit-RemainingandX-RateLimit-Resetare respected. - Secrets masking:
output.MaskSecret()shows only last 4 chars. Used when displaying tokens/keys. - Stripe tokenization:
billing.goembeds the Stripe publishable key and tokenizes card data client-side viaDoStripe()before sending the token to the backend.
go test ./internal/api/ -v # HTTP client tests with httptest mock server
go test ./internal/config/ -v # Config management tests
go test ./internal/output/ -v # Output formatting tests# Start backend
cd ~/Documents/GitHub/modelslab-frontend-v2 && php artisan serve --port=8888
# Run with credentials
MODELSLAB_TEST_TOKEN="<token>" MODELSLAB_TEST_API_KEY="<key>" go test ./tests/ -v -base-url http://127.0.0.1:8888Test user: test@example.com / password (from DatabaseSeeder.php).
Integration tests use runCLI() helper that executes the compiled binary as a subprocess. Authenticated tests are gated behind env vars — they skip if credentials are not set.
- GoReleaser v2 —
.goreleaser.ymlbuilds for darwin/linux/windows × amd64/arm64 - CI —
.github/workflows/ci.ymlruns tests on push/PR to main - Release —
.github/workflows/release.ymltriggered byv*tags, runs GoReleaser - Install script —
install.shdetects platform and downloads from GitHub Releases - Package managers — Homebrew tap (
modelslab/tap), Scoop bucket (ModelsLab/scoop-bucket), deb/rpm via nfpm
- Create or edit the appropriate file in
internal/cmd/ - Register the command in
init()by adding it to its parent command - Use
getClient()to get the API client - Use
DoControlPlane()orDoGeneration()for the HTTP call - Use
outputResult()to handle JSON/human output - Use
extractItems()orextractData()to parse the response
- Edit
internal/mcp/server.go - Add a new
addTool()call with the tool name, description, JSON schema, and handler function
Check internal/cmd/helpers.go for the shared response parsing helpers. If the API returns a new field name variant, add it to the firstNonNil() call for that command.
The full design spec is in cli.md. It contains the complete API endpoint mapping, all 102 commands, UX flows, and architectural decisions. Consult it for the authoritative specification.