Metadata-Version: 2.4
Name: impreza-cli
Version: 0.7.0
Summary: Official command-line interface for the Impreza Host public REST API
Author-email: Impreza Host <support@imprezahost.com>
License: MIT
Project-URL: Homepage, https://imprezahost.com
Project-URL: Documentation, https://docs.imprezahost.com
Project-URL: Repository, https://github.com/imprezahost/impreza-devkit
Project-URL: Changelog, https://github.com/imprezahost/impreza-devkit/blob/master/CHANGELOG.md
Keywords: impreza,hosting,cli,offshore,crypto
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT 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 :: Internet :: WWW/HTTP
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: impreza-sdk<0.8,>=0.7.0
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13.7
Requires-Dist: click>=8.0
Requires-Dist: tomli>=2.0; python_version < "3.11"
Requires-Dist: tomli-w>=1.0
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Requires-Dist: pytest-cov>=4.1; extra == "test"
Requires-Dist: pyyaml>=6.0; extra == "test"
Requires-Dist: types-PyYAML>=6.0; extra == "test"
Provides-Extra: dev
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Provides-Extra: yaml
Requires-Dist: pyyaml>=6.0; extra == "yaml"

# `impreza-cli` — Official CLI for Impreza Host

Command-line interface for the Impreza Host public REST API.
Built on top of [`impreza-sdk`](../sdk-python/README.md) — same
auth model, same Tor support, same retry behaviour, plus
multi-context configuration and Rich-rendered tables for human-
friendly output.

```bash
pip install impreza-cli
```

Requires Python 3.10+. See [`../CHANGELOG.md`](../CHANGELOG.md) for
release history.

## Quickstart

```bash
# 1. Add a context with your API credentials. Generate keys in
#    Impreza Account → API Keys; whitelist the calling
#    machine's IP at the same screen.
$ impreza context create personal --key imp_... --secret ...
Context 'personal' created and set as default.

# 2. Confirm everything works. impreza doctor runs five sequenced
#    health checks (config, API reachable, key status, IP
#    whitelist, account profile) and exits 0 only if all pass.
$ impreza doctor

impreza doctor
----------------------------------------
[OK]    active-context: Default context
[OK]    api-reachable: GET /account/api-keys/self OK (142ms)
        key prefix='imp_a1b2c3d4', label='devkit'
[OK]    key-status: status='active'
[OK]    ip-whitelist: request_ip 198.51.100.23 matches entry ('home')
[OK]    account-profile: Jane Doe <jane@example.com>, balance 5.00 USD
                         registered 2024-01-15
----------------------------------------
All checks passed. 5/5.

# 3. Read commands span every resource group:
$ impreza account info             # profile + balance
$ impreza vps list                 # across both backends
$ impreza domain check example.com mydomain.io
$ impreza catalog products --group "VPS"

# 4. Pipe into jq for scripting (every read verb supports --output
#    json | yaml):
$ impreza invoice list --output json \
    | jq '[.[] | select(.status == "Unpaid")] | length'

# 5. Write verbs are gated by confirm_or_exit so you don't lose
#    data accidentally; pass --yes / -y to skip prompts in scripts:
$ impreza vps reboot 17988
$ impreza vps proxmox snapshots create 17988 pre-update
$ impreza domain dns add example.com --type A --name www --value 203.0.113.24

# 6. Crypto top-up. --browser opens the BTCPay invoice URL
#    automatically; --wait polls until the gateway confirms
#    (default 2h timeout matches server-side invoice expiry).
$ impreza account topup --amount 50 --method xmr --browser --wait
```

## Authentication

Two ways to authenticate. The CLI tries them in order:

1. **Context** (recommended) — `impreza context create <name>` stores
   credentials in a config file; commands read them automatically.
   Per-invocation override via `impreza --context other <command>`.

2. **Environment variables** — `IMPREZA_API_KEY` + `IMPREZA_API_SECRET`.
   Useful in CI, but contexts are preferred for local work.

The config file lives at:

| OS | Path |
|---|---|
| Linux | `$XDG_CONFIG_HOME/impreza/config.toml` (default `~/.config/impreza/config.toml`) |
| macOS | `~/Library/Application Support/impreza/config.toml` |
| Windows | `%APPDATA%\impreza\config.toml` |

Override with `IMPREZA_CONFIG=/path/to/config.toml` for testing or
non-standard layouts.

On POSIX, the config file is `chmod 0o600` after every write so only
the owner can read the credentials. Windows ACLs are left to the OS
default.

## Commands

The CLI groups commands by resource. Run `impreza <group> --help`
to see the full subcommand list, or `impreza <group> <command>
--help` for option-level detail.

| Group | Verbs | Notes |
|---|---|---|
| `context` | `create / use / list / current / delete` | Local credential management — never hits the network |
| `doctor` | (single command) | Health check — config + API reachable + key status + IP whitelist + account profile |
| `account` | `info / balance / services / topup / topup-status` | Profile + balance + services + crypto top-up |
| `catalog` | `products / product / product-groups / tlds` | Pre-purchase discovery |
| `domain` | `show / check / pricing / register / transfer / set-nameservers / lock / unlock / id-protection / raa-verify / gdpr-auth / transfer-approval` + `domain dns list / add / update / delete / activate` | Domain registrations + full DNS CRUD |
| `vps` | `list / show / status / start / stop / reboot / shutdown / set-hostname / set-password / reinstall / migrate / cancel` + `vps proxmox snapshots / backups / backup-schedules / network` + `vps cloud images / rescue / iso / ssh-keys / vnc / vnc-password / resize / boot-order / ipv6` | Cross-backend (Proxmox + Cloud) VPS with smart dispatch |
| `order` | `list / show / create / upgrade` | Submit / browse product orders |
| `service` | `cancel` | Submit cancellation request (any service) |
| `webhook` | `list / show / create / update / delete / rotate-secret / deliveries / event-types` | Webhook subscription management + delivery history. Event-payload contract documented in [`../openapi/asyncapi.yaml`](../openapi/asyncapi.yaml). |
| `invoice` | `list / show` | Invoices with line items + transactions |
| `key` | `whoami` | Active API key identity + IP whitelist |

**Conventions:**

- Destructive verbs prompt for confirmation; pass `--yes` / `-y`
  to skip the prompt in scripts.
- Operation-returning verbs (`vps reinstall`, `vps migrate`,
  `vps proxmox snapshots rollback`, `vps proxmox backups
  create/restore`) accept `--wait` to block on the Proxmox queue,
  with `--timeout` (default 600 s for fast ops, 1800 s for the
  slower restores).
- Cost-incurring verbs (`domain register/transfer/id-protection`,
  `order create/upgrade`, `account topup`) call out the
  balance impact in the confirmation prompt; an
  `InsufficientCredit` 402 surfaces with a hint pointing at
  `impreza account topup`.
- Verbs that mutate a resource emit a green success line on
  stdout; queued / reboot-required state changes emit a cyan
  info line. Errors are red on stderr.

**Service termination policy:** `service cancel` / `vps cancel`
submit an `AddCancelRequest` — staff approves the actual
termination. There is no direct customer path to terminate a
service or remove a service suspension (suspension is
billing-state and is removed automatically when the overdue
invoice is paid, or manually by staff after an abuse hold is
resolved).

## Output formats

Every command supports `--output table|json|yaml` (short form `-o`).

| Format | Default | Best for |
|---|---|---|
| `table` | yes | human reading at the terminal |
| `json` | | piping into `jq`, automation, scripting |
| `yaml` | | human-editable config snapshots, CI/CD pipelines |

YAML output requires the optional `pyyaml` dependency:

```bash
pip install impreza-cli[yaml]
```

The CLI raises a clear `RuntimeError` pointing at the install hint
if you select `--output yaml` without it.

The flag works at both the global level and per-command:

```bash
# Global default for the invocation
impreza --output json account info

# Per-command override (wins over global)
impreza --output yaml account info --output table
```

## Tab completion

Typer ships completion for `bash`, `zsh`, `fish`, and PowerShell
out of the box:

```bash
# Install for the current shell (auto-detected)
impreza --install-completion

# Or explicitly
impreza --install-completion bash    # / zsh / fish / powershell

# Inspect the script before installing
impreza --show-completion bash
```

After installing, restart the shell (or `source ~/.bashrc` /
equivalent) and `impreza <TAB>` should suggest resource groups,
`impreza account <TAB>` should suggest verbs, and so on.

## Tor

Four knobs, from the most persistent to the most one-off:

```bash
# Persisted at context creation (writes [settings] use_tor=true)
impreza context create offshore --key imp_... --secret ... --via-tor

# Explicit SOCKS5 proxy instead (wins over use_tor; works for any
# SOCKS5, not just Tor)
impreza context create offshore --key imp_... --secret ...   --proxy socks5://127.0.0.1:9050

# One-shot global flag on any command
impreza --via-tor account info

# Env var, picked up by the SDK transparently
IMPREZA_USE_TOR=1 impreza account info
```

All four route through the local Tor daemon's SOCKS5 port with remote
DNS (no local lookup of the API hostname). Because exit nodes rotate,
set the API key's IP factor to `tofu` or `keyonly` in the client area —
`whitelist` cannot work without a stable egress IP. When the account
uses the API's `.onion` mirror, the IP factor sees a stable loopback
instead.

The SDK's `auto_tor=True` path (probe Tor, fall back to clearnet)
isn't surfaced through the CLI yet — it is a fail-open convenience,
which is the wrong default here.

## Error handling

The CLI maps SDK exceptions to friendly stderr messages and a
non-zero exit code, matching the format `ImprezaError.__str__`
produces:

```
Error: Invalid API credentials. (code=UNAUTHORIZED) [request_id=req_abc]
```

Tracebacks never leak from expected failures (auth errors, missing
contexts, 404s, 429s, etc.). Bugs in the CLI itself still raise so
the traceback isn't swallowed — that's intentional.

## Reviewed application configuration

Version 0.6.0 adds configuration export, review and apply. Documents are limited
to 64 KiB of UTF-8 and carry secret references instead of values. Applications
retain their current configuration until a reviewed plan is explicitly applied.

See [the configuration guide](https://docs.imprezahost.com/customer-workflows.html#config-cli)
for SDK and CLI examples, permissions, review expiry and replay behavior.

## Development

```bash
git clone https://github.com/imprezahost/impreza-devkit.git
cd impreza-devkit/cli-python

python -m venv .venv
# Linux/macOS:        source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1

# Install editable + test/dev/yaml extras + the SDK as a path dep
pip install -e ../sdk-python -e ".[test,dev,yaml]"

pytest                              # unit + Typer-runner E2E
ruff check
mypy --strict impreza_cli
```

To run the live integration smokes (skipped silently without creds):

```bash
export IMPREZA_API_KEY="imp_..."
export IMPREZA_API_SECRET="..."
# Optional, for `impreza domain show / dns list`:
export IMPREZA_TEST_DOMAIN="<a domain on your account>"

pytest -v -s tests/
```

The smokes exercise the same surface as the unit tests against the
real API, so they catch contract drift between the CLI and the
server.

## License

MIT. See [`../LICENSE`](../LICENSE) at the repository root.
