Document switch, print-id-token, email, stream, and volume attachment commands

The skill had drifted from the CLI: exc switch, exc print-id-token, the
entire exc email and exc stream command groups, and volume attach/detach
were all missing. The output-formats section also claimed there was no
universal JSON flag, which is no longer true now that --output json is
global on every command.
This commit is contained in:
lolwierd
2026-08-04 12:42:48 +05:30
parent 0d085fe617
commit 5eddc2dd9e

View File

@@ -1,6 +1,6 @@
---
name: excloud-cli
description: Drive Excloud resources (compute, networking, security groups, volumes, snapshots, public IPs, IAM, billing, Kubernetes, object storage buckets, secrets) through the `exc` CLI. Use when a user asks to plan or execute `exc` commands - creating / inspecting / updating / deleting VMs, running commands on them via `exec` / `scp` / `console`, managing security groups and public IPs, pulling Kubernetes kubeconfigs, managing buckets/objects/access keys, or saving / revealing / rotating org secrets - with safety guardrails and auth checks.
description: Drive Excloud resources (compute, networking, security groups, volumes, snapshots, public IPs, IAM, billing, Kubernetes, object storage buckets, secrets, transactional email, stream/Kafka clusters) through the `exc` CLI. Use when a user asks to plan or execute `exc` commands - creating / inspecting / updating / deleting VMs, running commands on them via `exec` / `scp` / `console`, managing security groups and public IPs, pulling Kubernetes kubeconfigs, managing buckets/objects/access keys, saving / revealing / rotating org secrets, sending transactional email, or managing stream clusters - with safety guardrails and auth checks.
---
# Excloud CLI
@@ -23,7 +23,7 @@ Everything below has been observed working at some point; the model should still
- Confirm before anything destructive (see Safety).
- If authentication is missing or expired, tell the user to run `exc login` and stop — do not invent tokens.
- When flag names or behaviours look odd, run `exc <...> --help` rather than guessing. Generated CLIs evolve between releases.
- Read `list` / `get` output shapes carefully before trying to parse them; there is no universal `-o json` flag today (see Output formats).
- Read `list` / `get` output shapes carefully before trying to parse them; pass the global `--output json` flag for machine-readable output (see Output formats).
## Authentication
@@ -37,6 +37,10 @@ If none of those are present or valid, commands that need a token (`exec`, `scp`
`exc me`, `exc org list`, `exc account list`, `exc config list` are useful "where am I?" probes after login.
`exc switch [account] [organization] [--list]` lists or switches between account/org contexts already logged into `~/.exc/config`. With no args on an interactive TTY it opens a `j`/`k`/arrow-key picker; non-interactively (or with `--list`, or `--output json`) it prints the context table instead — use that form when scripting or running non-interactively. `exc switch <account>` switches directly if the account has exactly one org, otherwise pass `[organization]` too. It also silently prunes stale (401/403) contexts from the config file on every run.
`exc print-id-token` prints the current user's raw ID token (from env or `~/.exc/config`) to stdout — useful for piping into other tools that need a bearer token. Treat its output as a credential.
## Safety guardrails
Require explicit user confirmation before running any of these:
@@ -50,6 +54,8 @@ Require explicit user confirmation before running any of these:
- `exc buckets delete`, `exc buckets rm`, `exc buckets objects delete`, recursive bucket/object deletes, and `exc buckets multipart abort`.
- `exc buckets keys delete`; also treat `exc buckets keys create` output as sensitive because the secret is shown once.
- `exc secret delete` (soft-delete, no undo); also `exc secret reveal` — every call writes an audit event, so don't run it for "discovery".
- `exc compute volume detach`, `exc stream terminate`, `exc stream topic delete`, `exc stream user delete`, `exc stream acl delete`.
- `exc email domain delete`, `exc email template delete`, `exc email smtp delete`, `exc email config-set delete`, `exc email suppression delete`; also treat `exc email smtp create` output as sensitive (one-time password) like bucket key creation.
For shell commands delivered through `exc compute exec` or an `exec` script file, refuse or confirm explicitly before running anything like `shutdown`, `reboot`, `rm -rf`, `mkfs`, `dd`, `wipefs`, rewrites of `/etc/fstab`, bootloader edits, or `systemctl stop ssh*` (the last one will make the VM unreachable over SSH — see Interactive access).
@@ -262,6 +268,13 @@ exc compute volume list # the root volume should disappear / move to
- `exc compute volume delete --volume_id <id>` (destructive).
- `exc compute snapshot list` / `exc compute snapshot create --volume_id <id>` / `exc compute snapshot delete --snapshot_id <id>`
### Volume attachment (non-root volumes)
- `exc compute volume attach --vm_id <id> --volume_id <id> [--wait]` — attaches an existing volume to a running VM as a secondary disk.
- `exc compute volume detach --vm_id <id> --volume_id <id> [--wait]`
- `exc compute volume attachments list [--vm_id <id>] [--volume_id <id>]` — cross-VM attachment table; omit both filters to list every attachment in the account.
- `exc compute volumes --vm_id <id>` — shortcut table of every volume (root and attached) on one VM, including `ATTACHMENT_STATE` and `IS_ROOT`. Note this is a sibling of `exc compute volume` (singular), not a subcommand of it.
## SSH key catalog
- `exc compute key list` / `exc compute key get --id <id>`
@@ -277,7 +290,7 @@ exc compute volume list # the root volume should disappear / move to
- The response contains the admin kubeconfig inline. Passing `-o <path>` writes it to disk (mode 0600, creating parent dirs) and strips it from stdout — strongly preferred.
- `exc k8s cluster delete --cluster_id <id>` (destructive).
- `exc k8s cluster worker list --cluster_id <id>`
- `exc k8s cluster worker create --cluster_id <id> --worker_image_id <id> --worker_instance_type <type> --subnet_id <id> --root_volume_size_gib <n> [--allocate_public_ipv4] [--security_group_ids <ids>] [--ssh_pubkey "<pubkey>"]`
- `exc k8s cluster worker create --cluster_id <id> --worker_image_id <id> --worker_instance_type <type> --subnet_id <id> --root_volume_size_gib <n> [--allocate_public_ipv4] [--security_group_ids <ids>] [--ssh_pubkey "<pubkey>"] [--karpenter_managed]`
- `exc k8s cluster worker delete --cluster_id <id> --worker_id <id>` (destructive).
- `exc k8s cluster kubeconfig get --cluster_id <id> [-o <path>]` — fetches the current kubeconfig and prints to stdout (or writes to `-o` with mode 0600). Returns a clear 404 if the cluster id is unknown.
- `exc k8s cluster kubeconfig merge --cluster_id <id> [--kubeconfig <path>] [--backup=true|false]` — merges into `~/.kube/config` (or `--kubeconfig`) using `kubectl config view --merge --flatten --raw`. Requires `kubectl` on PATH. `--backup` defaults to `true` and writes `<path>.bak`, `<path>.bak1`, ... before overwriting.
@@ -406,7 +419,7 @@ Notes:
- `--count`, `--root_volume_gib`, `--baseline_iops`, `--baseline_throughput_mbps`, and `--monthly_egress_gib` are optional numeric inputs. Omitted or zero values let the service apply its defaults where supported; pass explicit values when mirroring a planned `compute create`.
- `--monthly_egress_gib` is a floating-point flag, so decimal values are valid if the installed help shows it.
- `--has_public_ipv4` is boolean; pass `--has_public_ipv4=true` when estimating a public IPv4 allocation.
- The command uses the default zone from CLI config; there is normally no user-facing `--zone_id` flag in the generated command.
- The command uses the default zone from CLI config; there is normally no `--zone_id` flag.
- Output is a human-readable cost table with component rows (`Instance`, optional `Storage`, optional `Network`) and `TOTAL` / hourly summary rows. It is for display and comparison, not JSON scripting.
- If the service has no price data for an instance type, the command prints `No pricing data found for instance type: <type>`.
@@ -434,6 +447,32 @@ Notes:
- `--granularity` accepts `auto`, `hourly`, `daily`, or `monthly`. Hourly is limited to about 7 days, daily to about 31 days, and monthly to about 6 months.
- Table output includes `TOTAL`, cost, share percentage, and a best-effort usage display. Use `--json` for scripts.
## Transactional email
`exc email` sends/manages transactional email through Excloud's email service. Always check `exc email --help` and the relevant subgroup `--help` first — most email subcommands have their own `--json` flag in addition to the global `--output json`.
- `exc email domain create --name <domain> [--mail-from-domain <sub>] [--auto-create-dns] [--wait]` — register a sender domain. `--auto-create-dns` provisions the verification DNS records automatically if the domain lives in an Excloud DNS zone; otherwise use `check-dns` to see what to add manually.
- `exc email domain list` / `exc email domain get --id <id>` / `exc email domain verify --id <id> [--wait]` / `exc email domain check-dns --name <domain>` / `exc email domain delete --id <id> [--yes]` (destructive).
- `exc email send --from <addr> --to <addr> [--to <addr> ...] [--cc ...] [--bcc ...] [--reply-to ...] --subject <s> (--text <body> | --text-file <path>) [--html <body> | --html-file <path>] [--template <name> --data key=value ...] [--config-set <name>] [--tag key=value ...]` — `--from` and at least one `--to` are required; repeat `--to`/`--cc`/`--bcc`/`--reply-to`/`--data`/`--tag` for multiple values.
- `exc email message get --message-id <id>` / `exc email event list [--message-id <id>]` / `exc email metrics [--domain-id <id>]`
- `exc email template create --name <name> --subject <s> (--html <body> | --html-file <path>) [--text <body> | --text-file <path>]` / `exc email template list` / `exc email template delete --id <id> [--yes]`
- `exc email smtp create --name <name> --domain-id <id> [--show-password | --copy-password]` — generates SMTP credentials; the one-time password is only ever shown/copied at creation, capture it immediately. Treat it as sensitive.
- `exc email smtp list` / `exc email smtp delete --id <id> [--yes]`
- `exc email config-set create --name <name> [--webhook-url <url>] [--suppression-reason BOUNCE|COMPLAINT ...] [--list-management] [--track-opens] [--track-clicks]` / `exc email config-set list` / `exc email config-set delete --id <id> [--yes]`
- `exc email suppression put --email <addr> --domain-id <id> [--reason MANUAL|BOUNCE|COMPLAINT|UNSUBSCRIBE]` / `exc email suppression list [--domain-id <id>]` / `exc email suppression delete --email <addr> --domain-id <id> [--yes]`
## Stream (managed Kafka-compatible clusters)
`exc stream` manages stream clusters and their topics/users/ACLs. Always check `exc stream --help` first; cluster create takes compute-style sizing flags similar to `exc compute create` (instance type, subnet, root volume, security groups) plus stream-specific `--allowed_cidrs`.
- `exc stream create --name <name> --instance_type <type> --subnet_id <id> --root_volume_size_gib <n> [--security_group_ids <ids>] [--allowed_cidrs <cidr1,cidr2>] [--ssh_pubkey "<pubkey>"] [--baseline_iops <n>] [--baseline_throughput_mbps <n>] [--wait]` — `--wait` polls for `RUNNING`.
- `exc stream list` / `exc stream get --id <id>` / `exc stream bootstrap --id <id>` (bootstrap-server connection metadata)
- `exc stream restart --id <id> [--wait]` / `exc stream terminate --id <id> [--wait]` (destructive)
- `exc stream topic create --id <cluster_id> --name <name> [--partitions <n>] [--replication_factor <n>] [--retention_ms <n>] [--cleanup_policy <policy>]`
- `exc stream topic list --id <cluster_id>` / `exc stream topic patch --id <cluster_id> --name <name> [--partitions <n>] [--retention_ms <n>] [--cleanup_policy <policy>]` / `exc stream topic delete --id <cluster_id> --name <name>` (destructive)
- `exc stream user create --id <cluster_id> --username <user>` — creates a SCRAM user. `exc stream user list --id <cluster_id>` / `exc stream user rotate --id <cluster_id> --username <user>` (rotates password) / `exc stream user delete --id <cluster_id> --username <user>` (destructive)
- `exc stream acl create --id <cluster_id> --principal <p> --resource_type <t> --resource_name <n> --operation <op> [--permission_type <type>] [--pattern_type <type>] [--host <host>]` / `exc stream acl list --id <cluster_id>` / `exc stream acl delete --id <cluster_id> --acl_id <id>` (destructive)
## Config and misc
- `exc me` / `exc version` / `exc completion <bash|zsh|fish|powershell>`
@@ -442,14 +481,15 @@ Notes:
## Output formats
Every command either prints a column table (or TSV) or prints JSON — no command should print raw Go-struct dumps anymore. Both shapes are machine-parseable; pick your tool accordingly.
Every `exc` command accepts a **global `--output table|json` flag** (default `table`): `exc compute list --output json`, `exc compute create ... --output json`, etc. Prefer this over scraping tables for any scripting.
- **Column tables / TSV** (awk / `cut` / `awk -F\t` friendly): `compute list`, `compute get`, `compute create`, `compute terminate` (TSV `vm_id\tstate`), `compute instancetype list` / `capacity`, `compute image list`, `compute subnet list`, `compute volume list`, `compute volume get`, `compute snapshot list`, `compute publicip list`, `compute key list`, `securitygroup list` / `rule list` / `binding list`, `org list`, `account list`, `apikey list`, `policy list`, `billing estimate`, `billing costs`, `config list`, `compute seriallogs`, `buckets list`, `buckets ls`, `buckets objects list`, `buckets keys list`, `buckets multipart list`.
- **JSON** (pipe through `jq`): `me`, `quota`, `billing get`, `compute health` (`{"raw":"OK"}`), `k8s health`, `compute subnet get`, `compute publicip get`, `compute key get`, `securitygroup get`, `compute metrics`, `compute connect`, `serviceaccount list`, `compute protect`, `compute unprotect`, `compute rename`, `k8s cluster kubeconfig get` (raw kubeconfig YAML, not JSON-wrapped), `buckets get`, `buckets usage`, `buckets keys create`, multipart create/uploadpart/complete/abort, and the inline `kubeconfig` field inside the JSON response from `k8s cluster create` when `-o` is not set.
- With `--output json`, commands print the API response as indented JSON to stdout instead of their normal table/line output. `--wait` progress lines go to stderr in this mode so stdout stays clean JSON.
- Without `--output json`, commands print a column table (or TSV for a couple of terse ones like `compute terminate`).
- `exc email ...` subcommands and most of `exc buckets ...` (`buckets list --json`, `buckets objects list --json`, `billing costs --json`, etc.) also accept their **own** `--json` flag alongside the global `--output json`. For those, check the command's own `--help` rather than assuming `--output json` alone covers it.
- `buckets objects presign` and `buckets objects share` intentionally print only the URL (plus expiry for `share`) regardless of output mode, so they can be piped to tools like `pbcopy`.
- `k8s cluster kubeconfig get` prints raw kubeconfig YAML to stdout (not JSON-wrapped) unless `-o <path>` is set.
Many bucket list/delete commands and `billing costs` have explicit `--json`; use it for scripts instead of scraping tables. `buckets objects presign` intentionally prints only the URL so it can be piped to tools like `pbcopy`.
Before scripting heavy logic against a command, run it once and check the shape. The split between "table" and "JSON" is not always guessable — lists tend to be tables, getters tend to be JSON, but verify.
Before scripting heavy logic against a command, run it once with and without `--output json` and check the shape — coverage of the global flag varies by command family, so verify rather than assume.
## Metrics
@@ -461,7 +501,7 @@ Before scripting heavy logic against a command, run it once and check the shape.
## Error messages to recognise
- `not authenticated; run \`exc login\`` — no valid token in env or `~/.exc/config`.
- `required flag(s) "<name>" not set` — cobra-level enforcement. Read `--help` again.
- `required flag(s) "<name>" not set` — a required flag is missing. Read `--help` again.
- `Could not parse your request!! Are you sure you passed the correct flags?` — generic backend 400. Typically means an unknown ID, a value of the wrong type, or a server-side required field that the CLI accepted as empty. Verify every ID against a `list` before retrying.
- `Oops could not find the <Resource> you specified, maybe try checking if the <resource> exists?` — backend 404-ish. Trust the hint.
- `Oops the IP provided is invalid` — syntactic IP validation on `compute localip`.