Metadata-Version: 2.4
Name: gcube-cli
Version: 0.4.0
Summary: Official command-line tool for the gcube AI GPU cloud platform
Author-email: Data Alliance <support@data-alliance.com>
License: Apache-2.0
Project-URL: Homepage, https://gcube.ai
Project-URL: Documentation, https://gcube.ai/docs
Project-URL: Bug Tracker, https://github.com/data-alliance/gcube-cli/issues
Keywords: gcube,gpu,cloud,ai,cli,mlops
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: click<8.3,>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: rich>=13.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: websockets>=13.0
Requires-Dist: paramiko>=3.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.30; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: types-PyYAML; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"

# gcube CLI

Official command-line tool for the [gcube AI GPU cloud platform](https://gcube.ai).
Manage GPU workloads, monitor resources, and stream container logs — all from your terminal.

---

## Overview

```
gcube <service> <operation> [options]
```

| Feature | Description |
|---|---|
| Easy install | `pip install gcube-cli`, Python 3.10+ |
| AWS CLI style | `gcube <service> <operation>` with `--output table\|json\|yaml` |
| Browser auth | `gcube auth login` opens browser for one-click authentication (PKCE) |
| Shell completion | Tab completion for bash, zsh, fish (`gcube completion install`) |
| Command aliases | Short aliases: `wl` (workload), `cred` (credential) |
| AI agent skill | `gcube skill install` for Claude Code, Cursor integration |
| Key dependencies | click · httpx · rich · pyyaml · websockets · paramiko |

---

## Installation

```bash
pip install gcube-cli
```

---

## Quick Start

```bash
# 1. Log in (opens browser)
gcube auth login

# 2. Check available GPUs and note the CODE
gcube gpu list

# 3. (Optional) Check personal storages and note the MOUNT KEY if mounting storage
gcube storage list

# 4. Generate a workload template, edit it, then register
gcube workload register --skeleton > workload.yaml
# edit workload.yaml
gcube workload register -f workload.yaml

# 5. Check status — SER is the workload identifier shown in the list
gcube workload list

# 6. Start the workload
gcube workload start <ser>

# 7. Stream logs
gcube workload logs <ser>
```

---

## Configuration

### Authentication

```bash
gcube auth login                         # Log in via browser (recommended)
gcube auth logout                        # Log out and revoke token
gcube auth status                        # Show login email, token expiry
```

Alternatively, set a token manually:

```bash
gcube config set --token "eyJ..."        # Copy from gcube web console > API Token menu
```

### Config File

Generated automatically at `~/.gcube/config.yaml`:

```yaml
platform_url: https://api.gcube.ai
ws_url: wss://console.gcube.ai:61443
auth:
  access_token: "eyJ..."
output: table          # table | json | yaml
```

### Config Commands

```
gcube config                              # Interactive setup
gcube config set --token <token>          # Set API token
gcube config set --platform-url <url>     # Set platform URL
gcube config set --ws-url <url>           # Set WebSocket URL (for log streaming)
gcube config set --output <format>        # Set default output format
gcube config get <key>                    # Get a config value (token, platform-url, ws-url, email, output)
gcube config status                       # Show current configuration (with value source)
```

> The previous `gcube configure` name still works as a deprecated alias.
>
> `config status` shows each setting's source: `env` (environment variable), `config` (config file), or `default`.

### Environment Variables

Environment variables take precedence over the config file.

| Variable | Description |
|---|---|
| `GCUBE_PLATFORM_URL` | Platform base URL |
| `GCUBE_ACCESS_TOKEN` | Bearer token (useful for CI/CD) |
| `GCUBE_OUTPUT` | Default output format (`table`\|`json`\|`yaml`) |
| `GCUBE_SERVICE_CODE` | Service code filter (`gcube`\|`edu`\|`katech`) |

---

## Global Options

| Flag | Description | Default |
|---|---|---|
| `-o, --output <format>` | Output format: `table`\|`json`\|`yaml` | `table` |
| `-V, --version` | Show version | — |
| `--help` | Show help | — |

> `-o` must come **before** the subcommand: `gcube -o json workload list`, not `gcube workload list -o json`.

> **Command aliases:** `wl` = `workload`, `cred` = `credential`. Example: `gcube wl list`.

---

## Command Reference

### Workload

Manage the full lifecycle of GPU workloads. Each workload is identified by a **SER** (serial number) shown in `gcube workload list`.

| Command | Description |
|---|---|
| `gcube workload register --skeleton` | Print a blank YAML template to stdout |
| `gcube workload register -f <yaml>` | Register a workload from a YAML file |
| `gcube workload register --image <img> ...` | Register a workload with inline flags |
| `gcube workload update <ser> --skeleton` | Export current workload config as editable YAML |
| `gcube workload update <ser> -f <yaml>` | Update a stopped workload |
| `gcube workload list` | List workloads |
| `gcube workload describe <ser>` | Show workload details |
| `gcube workload start <ser>` | Start a workload and monitor deployment status in real time |
| `gcube workload start <ser> --no-watch` | Start a workload without monitoring |
| `gcube workload start <ser> --timeout <sec>` | Stop monitoring after N seconds if the deploy hasn't finished (exit code 124) |
| `gcube workload watch <ser>` | Re-attach to a deploying workload and monitor its status |
| `gcube workload watch <ser> --plain` | Force plain line-per-event output instead of the live dashboard |
| `gcube workload stop <ser>` | Stop a workload |
| `gcube workload delete <ser>` | Delete a workload |
| `gcube workload logs <ser>` | Stream container logs in real time |
| `gcube workload logs <ser> --pod <idx> --container <idx>` | Stream a specific container's logs |
| `gcube workload ssh <ser>` | Connect to a running workload via SSH |
| `gcube workload ssh <ser> --info` | Show SSH connection info without connecting |
| `gcube workload ssh <ser> --delete` | Delete SSH connection for a workload |
| `gcube workload ssh <ser> -- <command>` | Run a remote command via SSH |
| `gcube workload pods <ser>` | List pods |
| `gcube workload schedule <ser>` | Show current schedules |
| `gcube workload schedule <ser> -f <yaml>` | Set schedules from YAML (full replace) |
| `gcube workload schedule <ser> --days ... --start/--stop` | Append inline schedule entries |
| `gcube workload schedule <ser> --logs` | Show schedule execution history |
| `gcube workload unschedule <ser>` | Remove all schedules |

**Key Flags**

| Flag | Description |
|---|---|
| `-f, --file <path>` | Workload YAML file path |
| `--description <text>` | Workload description (2-80 chars) |
| `--image <image>` | Container image |
| `--gpu <code>` | GPU code (from `gcube gpu list` CODE column) |
| `--cuda <version>` | CUDA version code |
| `--no-watch` | Start without monitoring deployment status (start only) |
| `--owner <email>` | Filter by owner (list only) |
| `--pod <idx>` | Pod index for log streaming (0-based) |
| `--container <idx>` | Container index for log streaming (0-based) |
| `--ip <addr>` | Public IP for SSH (auto-detected if omitted) |
| `--info` | Show SSH connection info without connecting (ssh only) |
| `--show-password` | Reveal password in `--info` output (ssh only) |
| `-y, --yes` | Skip confirmation (stop/delete/ssh --delete); register/update despite failed image verification |
| `--days <spec>` | Schedule days: `Mon-Fri`, `daily`, `weekdays`, `weekend`, or bitmask (schedule only) |
| `--start <HH:mm>` | Schedule START time (schedule only) |
| `--stop <HH:mm>` | Schedule STOP time (schedule only) |
| `--timezone <tz>` | IANA timezone for schedule (default: `Asia/Seoul`) |
| `--logs` | Show schedule execution history (schedule only) |
| `--limit <n>` | Number of log entries to show (default: 20, with `--logs`) |

**CUDA Version Codes**

Formula: `MAJOR * 1000 + MINOR * 10 + PATCH` (e.g. CUDA 12.1 → `12010`, 12.6 → `12060`). Patch is usually 0.

**Workload YAML Format** (for `register -f` and `update -f`)

```yaml
# workload.yaml
description: "My ML training job"  # required, 2-80 chars
cuda: "12020"                      # optional CUDA version code
sharedMemory: 1                    # GB

containers:
  - containerImage: "pytorch/pytorch:2.0"  # required
    repo: docker.io
    port: 0                        # 0 = auto-detect (falls back to 8000 if undetectable)
    maxConnection: 4               # concurrent requests (default 4)
    containerCommand: "python train.py"
    isCredential: false            # set true to use a saved registry credential
    containerEnvs:                 # each entry is a single key-value object
      - EPOCHS: "100"
      - BATCH_SIZE: "32"
    userStorages: []               # see Storage section for MOUNT KEY format

gpuSpecs:
  - gpuCode: "029"                 # required — CODE from 'gcube gpu list'
  # add more entries for multiple replicas:
  # - gpuCode: "029"
```

> Use `gcube workload update <ser> --skeleton > workload.yaml` to export the current config before editing.

> Image verification is advisory — if it fails, you'll be asked whether to register anyway (`-y` skips the prompt). If the service port can't be determined, it defaults to **8000**.

**Optional Istio fields** (omit to use defaults):

- `isIstioProxy` (default: `true`) — injects the Istio service mesh sidecar into each pod. Disable only if your container has compatibility issues with the sidecar.
- `isIstioL7Hash` (default: `false`) — enables L7 request hashing so the same client is consistently routed to the same pod. Useful for stateful inference. Requires 2 or more replicas (multiple `gpuSpecs` entries).

**Workload States**

The CLI derives a phase from the latest operation record, not the raw API `state` field.

| Phase | Meaning |
|-------|---------|
| `idle` | Registered but never started, or stopped cleanly |
| `starting` | Deployment in progress |
| `running` | Running and serving (service URL available) |
| `stopping` | Shutting down |
| `finished` | Stopped after successful run |
| `failed` | Deployment or runtime failure |

> With `-o json`, the API returns raw state values (`open`, `deploy`, `finish`). These are **not** the same as the derived phases above — the CLI resolves the phase from `operations[0]` first, falling back to raw state only when no operations exist. Fallback mapping: `open` → `idle`, `deploy` → `running`, `finish` → `finished`.

**Deployment Monitoring**

`gcube workload start <ser>` monitors deployment in real time by default, showing four stages until the workload is running:

```
1. VM Provisioning   2. Node Ready   3. Image Pulling   4. Container Start
```

- Press `Ctrl+C` to stop monitoring; the deployment continues in the background.
- `gcube workload watch <ser>` re-attaches to a workload that is already deploying. If the workload is already running, stopped, or not yet deployed, it reports the status and exits without waiting.
- Use `--no-watch` to start without monitoring (e.g. in scripts).
- For deployments with more than 5 replicas, progress is shown as an aggregate counter (e.g. `Deploying...  60/100`) instead of per-stage detail.

**Non-interactive / scripting**

When stdout is not a terminal (piped/redirected) or with `--plain`, output switches to a plain line-per-event stream. With `-o json`/`-o yaml`, a single result object is printed (`{ser, status, startedAt, completedAt, durationSec, failReason}`). See [Scripting examples](#scripting--batch-deployment) for usage patterns.

**Schedule**

Automate workload start/stop with recurring or one-time schedules. Scheduled workloads show a `(scheduled)` badge in `workload list` and a schedule section in `describe`.

```bash
# Show current schedules
gcube workload schedule 2212

# Set schedules inline (appends to existing)
gcube workload schedule 2212 --days Mon-Fri --start 09:00 --stop 18:00

# Set from YAML file (replaces all schedules)
gcube workload schedule 2212 -f schedule.yaml

# View execution history
gcube workload schedule 2212 --logs
gcube workload schedule 2212 --logs --limit 50

# Remove all schedules
gcube workload unschedule 2212
```

Schedule YAML format (`-f`):

```yaml
timezone: Asia/Seoul
schedules:
  # Sugar form — expands to separate START + STOP entries
  - days: Mon-Fri
    start: "09:00"
    stop: "18:00"

  # Full form — explicit action per entry
  - action: START
    time: "09:00"
    days: [Mon, Wed, Fri]
  - action: STOP
    time: "18:00"
    days: [Mon, Wed, Fri]

  # One-time schedule
  - action: START
    time: "14:00"
    date: "2026-08-01"
```

Day spec accepts: `Mon`-`Sun` names, ranges (`Mon-Fri`), comma-separated (`Mon,Wed,Fri`), aliases (`daily`, `weekdays`, `weekend`), or a bitmask integer (Mon=1, Tue=2, Wed=4, ..., Sun=64; e.g. `31` = Mon-Fri).

> `-f` YAML uses **full replacement** semantics — all entries (including disabled ones) must be included. Inline `--days/--start/--stop` **appends** to existing schedules.
>
> Times are sent as-is in `HH:mm` and interpreted in the given timezone (default `Asia/Seoul`). The `--timezone` flag overrides this, or it inherits from the existing schedule.

---

### GPU

List available GPUs with specs and pricing.

| Command | Description |
|---|---|
| `gcube gpu list` | Available GPUs (CODE, specs, hourly price range) |
| `gcube gpu list --all` | All GPUs including unavailable ones |

> The CODE column is a zero-padded sequence number (001, 002, ...) used when registering workloads.

**GPU Tiers**

| Tier | Provider | Deploy speed | Stability | Interruption risk |
|------|----------|-------------|-----------|-------------------|
| tier1 | Cloud (CSP) | Slow (image pull 30-60+ min) | High | Very low |
| tier2 | Dedicated servers | Moderate (10-20 min) | High | Low |
| tier3 | PC / personal | Fast (often pre-cached) | Variable | Higher — GPU may be reclaimed by the provider, causing the workload to fail |

> Prices in `gpu list` are hourly rates in KRW. All point amounts (1P = 1 KRW).

---

### Storage

List personal storages available for mounting into workloads.

| Command | Description |
|---|---|
| `gcube storage list` | List bound personal storages |

The **MOUNT KEY** column is the identifier used in workload YAML to mount a storage:
a numeric SER for general (S3/drive) storages, or the literal `gcube` for gcube storage.

```yaml
containers:
  - containerImage: "pytorch/pytorch:2.0"
    userStorages:
      - "95": "/mnt/data"           # MOUNT KEY: mountPath (numeric SER, quoted)
      - gcube: "/mnt/gcube-data"    # gcube storage uses the literal key 'gcube'
```

Only storages in `bound` state are listed.

> ⚠️ `ReadWriteOnce` storages can only be mounted to a single node — avoid them in multi-replica workloads.

---

### Resource

Monitor CPU, GPU, and memory usage of a workload.

| Command | Description |
|---|---|
| `gcube resource workload <ser>` | Workload resource usage (time average) |

---

### Point

Check point balance and spending history.

| Command | Description |
|---|---|
| `gcube point status` | Balance, total charged/spent, low-balance warning |
| `gcube point spending` | Daily spending for the current month |
| `gcube point spending --month 2026-03` | Daily spending for a specific month |
| `gcube point spending --workload <ser>` | Filter by workload |

---

### Credential

Manage registry credentials.

| Command | Description |
|---|---|
| `gcube credential list` | List saved credentials |
| `gcube credential create --repo <type> --username <name> --token <token>` | Create a credential |
| `gcube credential delete --repo <type>` | Delete a credential |

Supported `--repo` types: `docker`, `github`, `harbor`, `aws`, `huggingface`, `quay`

To pull from a private registry, save a credential with `gcube credential create` and set `isCredential: true` in the corresponding container's workload YAML.

---

### Completion

Generate and install shell completion scripts for tab-completion of commands, options, and arguments.

| Command | Description |
|---|---|
| `gcube completion bash` | Print bash completion script |
| `gcube completion zsh` | Print zsh completion script |
| `gcube completion fish` | Print fish completion script |
| `gcube completion install` | Auto-detect shell and install completion |
| `gcube completion install --shell <shell>` | Install for a specific shell |

> On Windows (Git Bash / WSL), completion scripts are automatically patched to strip `\r` from output.

---

### Skill

Manage AI agent skill files for integration with AI coding assistants (Claude Code, Cursor).

| Command | Description |
|---|---|
| `gcube skill show` | Print the bundled skill file to stdout |
| `gcube skill install` | Auto-detect AI tools and install the skill file |
| `gcube skill install --target claude` | Install to `.claude/skills/gcube/SKILL.md` |
| `gcube skill install --target cursor` | Install to `.cursor/rules/gcube.mdc` |
| `gcube skill install --path <dir>` | Install to a custom directory |

The skill file contains comprehensive gcube CLI usage instructions for AI agents, including GPU selection, YAML manifest format, workload lifecycle, and common pitfalls.

| Flag | Description |
|---|---|
| `--target, -t` | Target tool: `claude` or `cursor` (auto-detected if omitted) |
| `--path, -p` | Custom directory (mutually exclusive with `--target`) |
| `-y, --yes` | Overwrite without confirmation |

> After upgrading the CLI (`pip install --upgrade gcube-cli`), re-run `gcube skill install -y` to update the installed skill file.

---

## Exit Codes

| Code | Meaning |
|---|---|
| `0` | Success |
| `1` | Invalid argument or state error |
| `2` | gcube API error |
| `3` | Authentication failure or token expired |
| `4` | Network error |
| `124` | `workload watch`/`start` timed out (`--timeout`) before the deploy finished |

---

## Examples

### Register and Manage Workloads

```bash
# Check available GPUs (note the CODE column)
gcube gpu list

# Generate a template, edit it, then register
gcube workload register --skeleton > workload.yaml
# edit workload.yaml
gcube workload register -f workload.yaml

# Register with inline flags
gcube workload register \
  --description "inference service" \
  --image ollama/ollama:latest \
  --gpu 029

# List and inspect
gcube workload list
gcube workload describe 2212
gcube -o json workload describe 2212

# Start and monitor deployment (default), or start without monitoring
gcube workload start 2212
gcube workload start 2212 --no-watch

# Re-attach to a deploying workload
gcube workload watch 2212

# Stop / delete
gcube workload stop 2212
gcube workload delete 2212
```

### Scripting / Batch Deployment

```bash
# Plain log to a file (auto-detected; no live dashboard). Capture stderr too so
# failure/timeout messages land in the log. Branch on the exit code.
gcube workload watch 2212 --timeout 1800 > 2212.log 2>&1
echo "exit=$?"   # 0 success · 1 failed · 124 timeout

# Machine-readable result for aggregating many deploys
gcube -o json workload watch 2212 --timeout 1800 > 2212.json
# {"ser":2212,"status":"success","startedAt":...,"completedAt":...,"durationSec":...,"failReason":null}

# Per-student burst with distinct tokens (one process each)
for ser in "${SERS[@]}"; do
  GCUBE_ACCESS_TOKEN="${TOKENS[$ser]}" \
    gcube -o json workload watch "$ser" --timeout 1800 > "$ser.json" &
done
wait
```

### Update a Workload

```bash
# Export current config, edit, and re-apply
gcube workload stop 2212
gcube workload update 2212 --skeleton > workload.yaml
# edit workload.yaml
gcube workload update 2212 -f workload.yaml
gcube workload start 2212
```

### Schedule a Workload

```bash
# Auto start at 09:00 and stop at 18:00 on weekdays
gcube workload schedule 2212 --days Mon-Fri --start 09:00 --stop 18:00

# Check what's configured
gcube workload schedule 2212

# Check if schedules actually ran
gcube workload schedule 2212 --logs

# Remove all schedules
gcube workload unschedule 2212
```

### Stream Logs

```bash
# Single container — streams immediately
gcube workload logs 2212

# Multi-container — shows selection list first
gcube workload logs 2226
gcube workload logs 2226 --pod 0 --container 1
```

### SSH into a Workload

```bash
# Interactive SSH session
gcube workload ssh 2212

# Multi-container — select pod/container
gcube workload ssh 2212 --pod 0 --container 1

# Run a remote command
gcube workload ssh 2212 -- nvidia-smi

# View connection info
gcube workload ssh 2212 --info
gcube workload ssh 2212 --info --show-password
```

> SSH requires the workload to be in `running` state. The `paramiko` package is required (`pip install paramiko`).

### Resource Monitoring

```bash
gcube resource workload 2212
gcube -o json resource workload 2212
```

---

## License

Apache License 2.0
