Metadata-Version: 2.4
Name: amd-oneclick-sdk
Version: 1.0.0
Summary: Scoped command-line SDK for Radeon Cloud accounts and instances
License-Expression: MIT
Keywords: amd,gpu,rocm,jupyter,radeon-cloud
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# Radeon Cloud CLI (RCC)

RCC is the command-line interface and Python SDK for connecting a local
workstation to an existing Radeon Cloud account and managing that account's
Jupyter instance. Browser authorization uses the existing GitHub, email, or SSO
login flow and returns a scoped, expiring `rcc_...` resource token to the CLI.

> **Integration status:** this package is version `1.0.0` and implements the
> first Radeon-Cloud-Internal integration. The server feature is disabled by
> default with `RCC_CLI_ENABLED=false`; operators must apply the RCC database
> migration and verify the public routes before enabling it. Use
> `rcc capabilities` to inspect the contract exposed by a deployment.

For the Chinese integration handoff, including interface ownership, token
scopes, implemented safeguards, current limitations, and the joint test
checklist, see [主项目接口对接与联调清单](../docs/ops/rcc-sdk-main-project-handoff-cn.md).

The built-in Manager endpoint is `https://radeon-global.anruicloud.com`.
Operators can select another deployment with a named profile, `--base-url`, or
the matched `RCC_URL` / `RCC_TOKEN` environment variables described below.

An RCC token is deliberately separate from the existing `rc-...` model
forwarding / Token Factory credential:

- `rcc_...` authorizes scoped account, Catalog, and Jupyter-instance actions.
- `rc-...` remains a model-forwarding credential and is rejected by this SDK
  before a resource request is sent.

## Current capabilities

- **Browser-assisted account authorization:** approve one workstation through
  the existing Radeon Cloud website without copying a web session or password
  into the CLI.
- **Per-device credentials:** every login receives its own named, scoped,
  expiring token; logout revokes only the selected token.
- **Multiple local accounts:** save named account profiles and select an
  explicit identity for each command.
- **Read-only account and Credits status:** inspect the signed-in identity and
  GPU-hour balance without exposing the legacy Token Factory credential.
- **Administrator Catalog discovery:** list and search approved environments,
  resource pools, and resource templates.
- **Jupyter lifecycle:** create, inspect, watch, read logs from, open, and delete
  the current account-owned instance.
- **Safe browser handoff:** open a ready Jupyter instance through a short-lived,
  single-use URL.
- **Managed SSH:** reuse a matching local identity, append its public key only
  when needed, and enter a ready instance with `rcc instance ssh`.
- **Automation output:** use versioned `human`, `json`, or `ndjson` output and
  generate shell completion locally.

The Radeon Cloud service never receives a kubeconfig, Kubernetes
ServiceAccount token, SSH private key, internal Launch Job identifier,
workload API key, or OpenCode password.

## Installation

### Requirements

- Python 3.9 or newer.
- Linux, macOS, or Windows 10/11.
- A browser that can reach the configured Radeon Cloud login page. A browser is
  not required on the CLI host when `--no-browser` is used.
- OpenSSH (`ssh` and `ssh-keygen`) when managed SSH is used.

The package has no third-party Python runtime dependencies.

### Install from the repository

From the Radeon-Cloud-Internal repository root:

```bash
python3 -m pip install ./sdk
rcc --version
```

For SDK development:

```bash
python3 -m pip install -e ./sdk
```

### Install a release wheel

```bash
pipx install ./amd_oneclick_sdk-1.0.0-py3-none-any.whl
rcc --version
```

### Packaged Linux and Windows bundles

A release bundle places exactly one RCC wheel beside its platform installer.
After extracting the bundle, run:

```bash
# Linux, from the extracted linux bundle
bash install-rcc.sh
```

```text
Windows: double-click install-rcc.cmd or run it from Command Prompt.
```

Both installers create an isolated per-user Python environment and finish by
printing the installed RCC version. Administrator privileges are not required.

Both `rcc` and the compatibility executable `amd-oneclick` invoke the same CLI.

## Quick start

### 1. Sign in

```bash
rcc auth login --account work
rcc auth status
rcc doctor
```

RCC opens a one-time verification page on the configured Radeon Cloud site.
Sign in through the normal web flow, review the device name, and approve the
request. On a headless or remote workstation, print the link instead:

```bash
rcc auth login --account work --no-browser
```

Open that link in a trusted browser before the transaction expires. The CLI
polls only the one-time authorization transaction; it never reads browser
cookies.

### 2. Inspect approved resources

```bash
rcc credits status
rcc catalog list
rcc catalog list --kind environment
rcc catalog list --kind resource_pool
rcc catalog list --kind resource_template
rcc capabilities
```

Catalog `available` means the account is policy-eligible at read time. It does
not reserve a GPU or guarantee that capacity is currently schedulable.

### 3. Create a Jupyter instance

Configure the account's SSH key once before its first SSH-enabled launch:

```bash
rcc instance add-key
```

This validates an existing local identity or offers to generate Ed25519, then
appends only its public half. Instance creation enables SSH by default; use
`--no-ssh` only when SSH is not wanted.
Direct `--token` or `RCC_TOKEN` credentials must also pass `--identity-file`;
RCC never borrows the active named account's private key for another token.

Run the command in an interactive terminal to select Catalog values, or pass
all selectors explicitly for automation:

```bash
rcc instance create \
  --env REVIEWED_ENVIRONMENT \
  --pod-type RESOURCE_POOL \
  --resource-template RESOURCE_TEMPLATE
```

By default, the command waits for readiness by polling the current-instance
endpoint directly. It does not read or expose an internal Launch Job. To return
as soon as creation is accepted:

```bash
rcc instance create \
  --env REVIEWED_ENVIRONMENT \
  --pod-type RESOURCE_POOL \
  --resource-template RESOURCE_TEMPLATE \
  --no-wait

rcc instance status --watch
rcc instance logs
```

Use `--idempotency-key KEY` when retrying the same logical create operation
after a lost response. The same key must not be reused for a different launch.

### 4. Open the instance

```bash
rcc instance open
```

For a headless machine, print the short-lived URL and open it on another trusted
device:

```bash
rcc instance open --print
```

The URL is single-use and sensitive even though it contains neither the RCC
token nor an internal workload credential. Do not place it in logs or chat.

### 5. Release the GPU

```bash
rcc instance delete --yes
rcc instance status
```

Deletion is explicit. Logging out or removing a local profile does not stop an
instance or GPU billing. A successful response that says the instance is
shutting down does not mean cleanup has completed; continue checking status
until no active instance is reported.

## Supported command surface

```text
rcc status
rcc auth login|status|logout
rcc account add|list|current|use|remove
rcc credits status
rcc catalog list|show|search
rcc doctor
rcc capabilities
rcc instance create|add-key|list|status|logs|open|ssh|delete
rcc completion bash|zsh|fish|powershell
```

The following table is the first-release command-to-API contract. Commands
marked **local** require no server endpoint.

| Command | RCC BFF request(s) | Required scope / behavior |
|---|---|---|
| `rcc auth login` | `POST /api/cli/auth/start`, browser `GET/POST /auth/cli/verify/{code}`, then `POST /api/cli/auth/poll` | Login start and poll are one-time, unauthenticated transactions; browser approval requires the existing web session. |
| `rcc auth status` | `GET /api/cli/auth/status` | `account:read`; `--all` checks `GET /api/cli/account` for each saved profile. |
| `rcc auth logout` | `POST /api/cli/auth/logout` | `account:read`; revokes the effective device token; removes a profile only when it supplied that token. |
| `rcc account add` | Same device-authorization flow as `auth login` | Saves a new named local profile after browser approval. |
| `rcc account list/current/use` | **Local** | Reads or updates `~/.radeon-cloud/config.json`; token values are never displayed. |
| `rcc account remove` | **Local** by default; `POST /api/cli/auth/logout` with `--revoke` | Removes only the named local profile unless `--revoke` is explicitly supplied. |
| `rcc status` | `GET /api/cli/account` and `GET /api/cli/instances/current` | `account:read`, `instance:read`; combines account, Credits, and current-instance state. |
| `rcc credits status` | `GET /api/cli/account` | `account:read`; Credits are read-only in RCC. |
| `rcc catalog list/show/search` | `GET /api/cli/catalog` | `catalog:read`; `show` and `search` filter the returned approved Catalog locally. |
| `rcc capabilities` | `GET /api/cli/capabilities` | `account:read`; reports enabled and deferred features. |
| `rcc doctor` | `GET /ready`, `GET /api/cli/auth/status`, and `GET /api/cli/capabilities` | Checks local configuration, transport, token validity, and server contract. |
| `rcc instance add-key` | `POST /api/cli/ssh-key` | `instance:create`; validates locally and appends only the public key. |
| `rcc instance create` | `POST /api/cli/instances`, then `GET /api/cli/instances/current` while waiting | `instance:create`, then `instance:read`; SSH is enabled unless `--no-ssh` is passed. |
| `rcc instance list/status` | `GET /api/cli/instances/current` | `instance:read`; `--all` performs the same account-isolated request for each local profile. |
| `rcc instance logs` | `GET /api/cli/instances/current/logs` | `logs:read`. |
| `rcc instance open` | `POST /api/cli/instances/current/open` | `instance:open`; returns a one-time browser handoff. |
| `rcc instance ssh` | `GET /api/cli/instances/current`, then **local** OpenSSH | `instance:read`; executes `ssh` with the selected account's local private-key path. |
| `rcc instance delete` | `GET /api/cli/account`, `GET /api/cli/instances/current`, then `DELETE /api/cli/instances/current` | `account:read`, `instance:read`, `instance:delete`; identity and current-instance context are checked before deletion. |
| `rcc completion SHELL` | **Local** | Generates completion from the same registry used by the parser. |

The default `rcc_...` token scopes are:

```text
account:read
catalog:read
instance:read
instance:create
instance:delete
instance:open
logs:read
```

The BFF enforces the required scope again for every request. Possessing a token
does not grant commands outside its recorded scopes.

## Accounts, authentication, and Credits

`account` manages local profiles; `auth` manages remote per-device tokens.

```bash
rcc auth login --account work
rcc account add personal
rcc account list
rcc account current
rcc account use work
rcc -A personal status
rcc -A work auth status
rcc -A work auth logout
```

`-A/--account` is an identity boundary. When a named profile is explicitly
selected, ambient `RCC_URL` and `RCC_TOKEN` credentials for another account are
ignored. Destructive commands should use `-A NAME` when multiple profiles are
configured.

Environment-based automation may provide a matched pair:

```bash
export RCC_URL=https://radeon.example.com
export RCC_TOKEN=rcc_REDACTED
rcc --output json status
```

`RCC_TOKEN` requires a matching `RCC_URL`; RCC refuses to guess which Manager
should receive an ambient token. Command-line `--base-url` and `--token`
overrides are also available, but passing secrets in command arguments may
expose them through process inspection or shell history.

`rcc auth logout` resolves the same URL/token pair as other commands. If the
token came from a saved profile, logout revokes it and removes that profile;
if it came from explicit arguments or the environment, local profiles are kept.
Environment-only automation can log out without a local profile. A concurrent
replacement login is preserved when an older token is revoked.

Logout does not stop resources, release a GPU, end web sessions, or revoke
tokens on other devices. `rcc auth logout --all` processes all saved profiles
and ignores ambient credentials. It cannot be combined with `--account`,
`--base-url`, or `--token`.

Credits are intentionally read-only:

```bash
rcc credits status
```

Credit requests and changes remain in the Radeon Cloud web or administrator
workflow.

## Instance options and behavior

Only Catalog-approved Jupyter instances are supported. Creation accepts:

- `--env`: approved environment ID, name, or image selector.
- `--pod-type`: exact resource-pool ID from the Catalog.
- `--resource-template`: exact resource-template ID from the Catalog.
- `--disk-size-gb`: optional requested workspace size.
- `--resource-profile`: optional reviewed resource profile; default `auto`.
- `--use-pvc` or `--no-pvc`: optional persistence preference.
- `instance add-key --identity-file`: existing local private key or matching `.pub` path.
- `--no-ssh`: create without managed SSH.
- `--idempotency-key`: stable retry key for one logical create operation.
- `--no-wait`: return after the request is accepted.
- `--timeout`: local readiness wait limit; default 1800 seconds.

The Manager reports a terminal launch failure through current-instance status,
including while failed gateway resources are being cleaned up. A waiting create
exits with code `1` and the failure classification. It also stops if the accepted
instance disappears, is being deleted, or a different instance becomes current.
Internal Launch Job IDs remain private.

A local timeout or Ctrl+C does not cancel server-side work. Recover with:

```bash
rcc instance status --watch
rcc instance logs
```

Use `--interval` and `--timeout` with `instance status --watch` to control local
polling. Status and logs are always resolved from the authenticated account;
the SDK cannot supply an arbitrary server-side user name.

## Python SDK

Python callers can use the same browser authorization and RCC BFF endpoints:

```python
from amd_oneclick_sdk import RadeonCloudClient, authenticate

manager_url = "https://radeon.example.com"
login = authenticate(base_url=manager_url, device_name="build-workstation")
client = RadeonCloudClient(base_url=manager_url, token=login.access_token)

print(client.account())
print(client.capabilities())
print(client.catalog())

created = client.create_instance(
    idempotency_key="provision-notebook-001",
    environment="REVIEWED_ENVIRONMENT",
    pod_type="RESOURCE_POOL",
    resource_template="RESOURCE_TEMPLATE",
)
print(created)
print(client.instance_current())
```

`OneClickClient` remains available as a compatibility alias.

The supported lower-level client methods are:

```text
start_cli_auth       poll_cli_auth
auth_status          auth_logout
account              capabilities          catalog
add_ssh_key          create_instance        instance_current
instance_logs
open_instance        delete_instance
```

Do not print, commit, or embed `AuthResult.access_token` in source code.

The Python client uses explicit credentials or a matched `RCC_URL` / `RCC_TOKEN`
pair (legacy `AMD_ONECLICK_*` aliases also work). It does not load workstation
profiles automatically. An explicit URL cannot be paired with a token inherited
from a different environment URL. Passing `token="", require_token=False`
creates an anonymous client and never inherits an ambient token; browser login
uses this mode. Explicit named CLI profiles ignore ambient credentials.

## Machine-readable output

Put global output options before the command:

```bash
rcc --output json catalog list
rcc --output ndjson instance status --watch
rcc --quiet --output json instance status
rcc -vv doctor
```

- `json` writes one final result document to stdout; progress is written to
  stderr.
- `ndjson` writes event, result, or error documents one per line.
- Structured documents use `schema_version: rcc.cli/v1` and expose stable
  command fields under `data`.
- Sensitive fields are redacted. `instance open` is an explicit handoff action,
  so its one-time URL must still be handled as a secret.
- `completion` is a raw-artifact exception and writes a shell script instead of
  an RCC result envelope.

Exit codes:

| Code | Meaning |
|---|---|
| `0` | The local command completed successfully. |
| `1` | Manager, network, TLS, protocol, or unexpected local failure. |
| `2` | Usage, local configuration, or authentication failure. |
| `5` | Local wait timeout; server-side work may still continue. |
| `130` | Interrupted by the user; remote work is left unchanged. |

A status command can return `0` while reporting a remote lifecycle such as
`failed` or `none`. Automation must inspect the documented fields under `data`
as well as the process exit code.

`auth status --all` exposes `data.active_account` and `data.accounts` in both
JSON and NDJSON. `instance status` includes `data.error_code` and `data.detail`
when the server reports a failure.

`doctor` returns `0` with `data.ok=true` only after service health,
authentication, configuration permissions, and the RCC API v1 contract all
pass. It requires output schema version 1 and all first-release capabilities.
Missing tokens, skipped checks, capability errors, and old-server fallbacks
return `1`; inspect `data.capability_state`, `data.capability_detail`,
`data.diagnostics`, and `data.remediation`. A successful doctor result must
still be followed by the actual instance lifecycle validation for that deployment.

## Authentication and security model

```text
RCC / Python SDK
  -> HTTPS + scoped rcc_ bearer token
  -> Radeon-Cloud-Internal frontend BFF (/api/cli/*)
  -> browser account lookup + derived tenant identity
  -> internal Manager Service API
  -> Kubernetes account-owned Jupyter instance
```

- The browser continues to use the existing Radeon Cloud login page. The only
  added browser page is the isolated device-authorization confirmation page;
  the existing business SPA, navigation, and pages are unchanged.
- Raw RCC tokens, polling secrets, and browser codes are not stored by the
  server. Their SHA-256 hashes are stored with token prefix, device name,
  scopes, expiry, and operational metadata.
- Each token is individually revocable. Account `token_version` changes can
  invalidate all previously issued RCC tokens after a security event.
- The BFF derives the internal Manager user identity from the authenticated web
  account. A CLI-supplied `user_name` is neither accepted nor trusted.
- RCC instance responses are allowlisted and exclude internal Launch Job and
  status URLs, SSH public-key material, workload API keys, OpenCode passwords,
  and internal browser-handoff fields. SSH host, port, username, and readiness
  metadata are allowlisted.
- Authentication, token, and browser-handoff responses use
  `Cache-Control: no-store` where credentials or one-time material may be
  present.
- TLS verification cannot be disabled from the CLI. Plain HTTP is limited to
  loopback development and the explicitly retained legacy validation endpoint;
  never send a production token over that endpoint.

## Local state

| Path | Purpose |
|---|---|
| `~/.radeon-cloud/config.json` | Named account profiles, scoped tokens, endpoint, expiry metadata, and the last opaque instance lifecycle fence. |
| `~/.radeon-cloud/config.json.lock` | Cross-process lock used for atomic profile updates. |
| `~/.radeon-cloud/keys/NAME/id_ed25519` | Per-account local SSH private key; never uploaded. |

On POSIX systems, RCC creates the directory with mode `0700` and the config and
lock files with mode `0600`. On Windows, store the file in a user-restricted
directory and verify its ACL separately. Do not commit this configuration or
copy its token into logs.

## Deferred capabilities

The first Radeon-Cloud-Internal integration intentionally does **not** expose:

- Registration or registration status.
- Credit requests.
- Instance resource sampling over SSH.
- Launch File, Launch Plan, or public Launch Job commands.
- Recipe discovery or execution.
- Serve, ComfyUI, or workload process management.
- Custom images supplied by the SDK caller.
- Distill discovery, planning, submission, Runs, logs, artifacts, or cancel.

These commands are absent from `rcc --help`, the command registry, and shell
completion. Their implementation modules and Client HTTP methods are not part
of this release. The
Manager may use an internal Launch Job while creating a notebook, but its ID and
API are not exposed to RCC. `rcc capabilities` reports every deferred feature
as disabled.

## Server rollout

RCC is dark by default. Operators should follow the migration guide at
[`ops/migrations/rcc-cli/README.md`](../ops/migrations/rcc-cli/README.md):

1. Back up PostgreSQL.
2. Apply `001_expand.sql` as the schema owner.
3. Apply `002_grant_runtime.sql` as the owner for the frontend role, including
   `SELECT` on `amd_oneclick_schema_versions` as well as the RCC table permissions.
4. Run `001_preflight.sql` **as the frontend database role** and require
   `ready=true` with no missing privileges.
5. Deploy the frontend BFF and SDK with `RCC_CLI_ENABLED=false`. The Manager
   needs no new configuration: RCC adds no Service API route, alters no frozen
   Service API function or model, and sends no new protocol parameter.
6. Verify the edge routes `/api/cli/*` and `/auth/cli/*`.
7. Set `RCC_CLI_ENABLED=true` only after those checks succeed.

Merging the PR does not apply these manual migrations or enable the feature.
Then run `rcc doctor` and validate login → Credits/catalog → create/readiness →
logs → one-time browser opening → deletion/resource release → logout in the
test deployment before production enablement.

Rollback is feature-flag only: set `RCC_CLI_ENABLED=false` and retain the RCC
tables so issued tokens remain revocable and auditable.

## Troubleshooting

### The browser does not open

```bash
rcc auth login --no-browser
```

Open the printed short-lived URL in a trusted browser signed in to the intended
Radeon Cloud account.

### RCC routes return 404

Confirm that the migration is complete, the edge forwards `/api/cli/*` and
`/auth/cli/*`, and the frontend process has `RCC_CLI_ENABLED=true`.

### An `rc-...` token is rejected

This is expected. Run `rcc auth login` to obtain a scoped `rcc_...` resource
token. Keep the `rc-...` credential only for model forwarding / Token Factory.

### A token expired or was revoked

```bash
rcc auth login --account NAME
```

Browser authorization replaces the selected workstation profile with a newly
issued token.

### Instance creation needs selectors

Run `rcc catalog list` first. In a non-interactive shell, pass `--env`,
`--pod-type`, and `--resource-template` explicitly.

### A local launch wait timed out

```bash
rcc instance status --watch
rcc instance logs
```

The timeout does not cancel the server-side instance creation.

### A ready instance does not open automatically

```bash
rcc instance open --print
```

Open the returned URL promptly; it is short-lived and single-use.

## Local regression checks

From the repository root, with the repository's test dependencies installed:

```bash
python3 -m pytest -q tests/test_rcc_frontend.py \
  tests/test_rcc_cli_schema.py tests/test_rcc_migrations.py \
  tests/test_frontend_api_surface.py tests/test_service_api_baseline.py
```

These cover device authorization and token lifecycle, the RCC schema contract,
the applied migration under PostgreSQL, the frontend route inventory, and the
frozen Service API contract. The PostgreSQL check starts and stops an isolated
socket-only database and never uses an external database URL; it runs when
PostgreSQL server binaries and `psycopg2` are present and the test user is not
root, and otherwise reports a skip. The rest use temporary SQLite databases and
synthetic accounts. None of them allocate GPUs or contact real login providers.

## Project structure

```text
sdk/
  amd_oneclick_sdk/
    __init__.py            # Supported public Python exports
    auth.py                # Browser-assisted device authorization
    client.py              # RCC BFF HTTP client
    rcc_config.py          # Secure local multi-account state
    keys.py                # Local SSH key validation and generation
    security.py            # URL, TLS, and handoff validation
    cli.py                 # Public CLI facade
    _cli/                  # Command parser, registry, handlers, and output
  linux/                   # Linux installer scripts
  windows/                 # Windows installer scripts
  pyproject.toml           # Package metadata
  README.md                # This guide

frontend/rcc_routes.py     # RCC authentication and resource BFF
app/cli_auth/              # RCC schema contract and credential persistence
templates/cli_auth.html    # Standalone device-approval page
ops/migrations/rcc-cli/    # Additive database migration and rollout guide
```

## License

The SDK package is distributed under the MIT license declared in
[`pyproject.toml`](pyproject.toml).
