Metadata-Version: 2.4
Name: peaq-os-cli
Version: 0.0.1
Summary: Python CLI for peaqOS, the operating system for the machine economy — on-chain identity, credit rating, and omnichain infrastructure for robots and machines.
Author-email: peaqOS <info@peaq.xyz>
License: Apache-2.0
Project-URL: Homepage, https://www.peaq.xyz
Project-URL: Documentation, https://docs.peaq.xyz
Project-URL: Repository, https://github.com/peaqnetwork/peaq-os-cli-py
Project-URL: Issues, https://github.com/peaqnetwork/peaq-os-cli-py/issues
Keywords: peaq,peaqos,peaq-os,machine-economy,depin,robotics,ai-agents,blockchain,omnichain,peaqid,did,machine-nft,machine-tokenization,machine-credit-rating,mcr,cli,python
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: peaq_os_sdk>=0.0.1
Requires-Dist: click>=8.1
Requires-Dist: python-dotenv>=1.0
Requires-Dist: Pillow>=10.0
Requires-Dist: svglib>=0.9
Requires-Dist: reportlab>=4.0
Requires-Dist: eth-account<1.0,>=0.10
Provides-Extra: ows
Requires-Dist: open-wallet-standard>=1.3.2; extra == "ows"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: flake8; extra == "dev"

# peaq-os-cli

Python CLI for peaqOS. Describes install and the console entry point.

## Install

```bash
python3 -m venv .peaq-os-cli
source .peaq-os-cli/bin/activate
pip install -e ".[dev]"
```

## Run Tests
```
pytest
```

## Quality Gates
```
ruff check src tests
black --check src tests
mypy src
pytest -q --cov=src/peaq_os_cli --cov-fail-under=90
flake8 src tests
```

## Console usage

The `peaqos` script invokes the Click root group in `peaq_os_cli.main`.

```bash
peaqos --help
peaqos --version
```

Authoritative option text for any subcommand is always available via built-in help:

```bash
peaqos <command> -h
peaqos qualify event -h
peaqos qualify mcr -h
```

## Commands

### `peaqos init`

Interactive wizard that writes a `.env` file in the current working directory.

```bash
peaqos init                    # interactive (network, key, URLs, contract addresses)
peaqos init --non-interactive  # read all values from environment variables
peaqos init --force            # overwrite existing .env without confirmation
```

**Example (interactive):**

```
Network (mainnet, testnet) [mainnet]: mainnet
Private key source (paste, generate, wallet): generate
  Address:  0xAbCd...1234
  Key:      0xdeadbeef...
IMPORTANT: Save this private key securely. It will not be shown again.
RPC URL [https://peaq.api.onfinality.io/public]:
MCR API URL [https://mcr.peaq.xyz]:
Gas Station URL [https://depinstation.peaq.xyz]:
Event Registry address: 0xEe6f...78aB
  Config: .env written to /home/operator/project/.env

  Running whoami to verify...
  Address :  0xAbCd...1234
  Network :  mainnet
  RPC URL :  https://peaq.api.onfinality.io/public
  Chain ID:  3338
  MCR API :  https://mcr.peaq.xyz

  Contracts:
    IdentityRegistry:  0x9075...0B6A
    IdentityStaking :  0x7d39...9B8E
    EventRegistry   :  0xEe6f...78aB
    MachineNFT      :  0xaF13...Bd61
    DID Registry    :  0x0000...0800
    Batch Precompile:  0x0000...0805
```

### `peaqos whoami`

Show the active wallet address, network, chain ID, and all contract addresses.

```bash
peaqos whoami
```

**Example:**

```
  Address :  0xAbCd...1234
  Network :  mainnet
  RPC URL :  https://peaq.api.onfinality.io/public
  Chain ID:  3338
  MCR API :  https://mcr.peaq.xyz

  Contracts:
    IdentityRegistry:  0x9075...0B6A
    IdentityStaking :  0x7d39...9B8E
    EventRegistry   :  0xEe6f...78aB
    MachineNFT      :  0xaF13...Bd61
    DID Registry    :  0x0000...0800
    Batch Precompile:  0x0000...0805
```

### `peaqos wallet`

Manage OWS (Open Wallet Standard) wallets in the local encrypted vault (`~/.ows/`).
Requires the optional OWS dependency: `pip install peaq-os-cli[ows]`.

```
peaqos wallet create <name> [--words 12|24] [--json]
peaqos wallet import <name> (--mnemonic | --private-key-file <path>) [--index <n>] [--json]
peaqos wallet list [--json]
peaqos wallet show <name-or-id> [--json]
peaqos wallet export <name-or-id>
peaqos wallet delete <name-or-id>
peaqos wallet use <name-or-id>
```

**`wallet create`** — Generate a new mnemonic-backed wallet. Displays addresses for all supported chains (peaq, Base, Ethereum, Solana, Bitcoin, Cosmos, etc.). The mnemonic is never shown; use `wallet export` to retrieve it.

```bash
peaqos wallet create my-machine
peaqos wallet create my-machine --words 24   # 24-word mnemonic
peaqos wallet create my-machine --json       # JSON output
```

**`wallet import`** — Import an existing wallet from a BIP-39 mnemonic (hidden prompt) or a private key file.

```bash
peaqos wallet import recovered --mnemonic
peaqos wallet import from-file --private-key-file ./operator.key
```

**`wallet list`** — List all wallets in the vault. Shows Name, ID, peaq Address, Key Type, and Created date.

```bash
peaqos wallet list
peaqos wallet list --json   # full WalletInfo array
```

**`wallet show`** — Display full wallet details with addresses for every supported chain family.

```bash
peaqos wallet show operator
peaqos wallet show operator --json
```

**`wallet export`** — Export the recovery phrase or private key (requires confirmation).

```bash
peaqos wallet export my-machine
```

**`wallet delete`** — Securely delete a wallet from the vault (requires confirmation).

```bash
peaqos wallet delete my-machine
```

**`wallet use`** — Set a wallet as the active default by writing `PEAQOS_OWS_WALLET` to `.env`. Subsequent commands use this wallet via `load_client()`.

```bash
peaqos wallet use operator
```

**Migration from raw keys:**

```bash
peaqos wallet import my-operator --private-key-file ./operator.key
peaqos wallet use my-operator
peaqos whoami   # address matches the original key
```

### `peaqos activate`

Run the full machine onboarding flow end-to-end. Six steps: balance check →
2FA enrollment → gas-station funding → register on `IdentityRegistry` → mint
machine NFT → write DID attributes.

**Two modes:**

* **Self-managed** — no proxy flags. The caller's own key signs every step
  and holds the resulting NFT.
* **Proxy-managed** — `--for <machine-address> --machine-key <path>`
  together. The operator funds and submits register / mint on behalf of the
  machine, then the machine key signs its own DID attributes (the peaq DID
  precompile enforces `msg.sender == didAccount`). The machine EOA holds the
  resulting NFT. Both flags are required together; supplying one alone is
  rejected with exit `1`.

```bash
peaqos activate                                          # self mode
peaqos activate --for 0xMachine --machine-key ./m.key    # proxy mode
```

**Flags:**

| Flag               | Purpose                                                         |
| ------------------ | --------------------------------------------------------------- |
| `--for`            | Machine EOA address. Presence switches to proxy mode.           |
| `--machine-key`    | Path to a file with the machine's 0x-prefixed hex private key.  |
| `--doc-url`        | Documentation URL written to the machine DID.                   |
| `--data-api`       | Raw data API URL written to the machine DID.                    |
| `--visibility`     | `public` (default) / `private` / `onchain`.                     |
| `--skip-funding`   | Skip balance check, 2FA, and gas-station funding (steps 1–3).   |

Private keys **must** be supplied via file path (`--machine-key`). Inline
key flags are intentionally unsupported — reading from a file keeps the key
out of shell history and `ps` output.

**Environment:**

Connection / caller identity:

| Variable                   | Purpose                                            |
| -------------------------- | -------------------------------------------------- |
| `PEAQOS_PRIVATE_KEY`       | Operator private key (self or proxy mode caller). |
| `PEAQOS_NETWORK`           | `mainnet` or `testnet`.                            |
| `PEAQOS_RPC_URL`           | Override the default RPC endpoint for the network. |
| `PEAQOS_GAS_STATION_URL`   | Gas-station base URL for steps 2–3.                |

Contract addresses (all required — missing any yields exit `3`):

| Variable                     | Contract                                           |
| ---------------------------- | -------------------------------------------------- |
| `IDENTITY_REGISTRY_ADDRESS`  | `IdentityRegistry` (step 4 register).              |
| `IDENTITY_STAKING_ADDRESS`   | Identity staking contract.                         |
| `EVENT_REGISTRY_ADDRESS`     | Event registry contract.                           |
| `MACHINE_NFT_ADDRESS`        | Machine NFT (step 5 mint).                         |
| `DID_REGISTRY_ADDRESS`       | DID registry contract.                             |
| `BATCH_PRECOMPILE_ADDRESS`   | peaq batch precompile (step 6 batched DID writes). |

**Idempotent rerun.** Every mutating step does a read-before-write precheck:
registration consults `machineIdOfOwner`, mint consults `token_id_of` +
NFT `ownerOf`, DID writes consult `readAttribute`. Re-running `activate`
against on-chain state that is already complete submits no transactions
and exits `0`. A TOCTOU revert (`AlreadyRegistered` / `AlreadyExists`)
is also recovered as skip rather than surfaced as a network error.

**Proxy preconditions.** Proxy mode requires the operator to already be
registered on `IdentityRegistry` (i.e. have called `activate` in self mode
first). If not, the command fails with exit `2` (network/chain error)
before spending any gas.

**Output streams.** The final summary (Machine ID, Token ID, DIDs; plus
`Machine Address` and `Operator DID` in proxy mode) is written to `stdout`
so it can be piped / captured. Progress lines (`[1/6]`, `[2/6]`, …) and
per-step info/warning messages go to `stderr`. Integration tests assert on
`result.stdout`.

**Idempotency log.** Every step appends a JSONL entry to `./peaqos.log`
in the working directory with fields like `step`, `status`
(`pending` / `skipped` / `failed` / `confirmed`), `mode`, `machine_id`,
`tx_hash`, and (for DID writes) `machine_did_tx_count=6` /
`proxy_did_tx_count=2`. This file is both a resume marker for partial
failures and the audit trail.

**Example — first run (self mode):**

```bash
$ peaqos activate > out.txt 2> err.txt ; echo "exit: $?"
exit: 0

$ cat out.txt
Machine activated successfully.
  Machine ID:   42
  Token ID:     11
  Machine DID:  did:peaq:0xDC5b20847F43d67928F49Cd4f85D696b5A7617B5

$ cat err.txt
[1/6] Balance check
  Operator 0xDC5b...17B5: 1.0000 PEAQ (sufficient)
[2/6] 2FA enrollment - skipped (all wallets funded)
[3/6] Fund from Gas Station - skipped (all wallets funded)
[4/6] Register machine
  Registered. machine_id=42
[5/6] Mint NFT
  Minted NFT for machine_id=42 -> token_id=11 (tx 0xminttx...)
[6/6] Write DID attributes
  Wrote 6 machine DID attributes (tx 0xdidtx...)
```

**Example — rerun is a no-op (exit 0, no tx submitted):**

```bash
$ peaqos activate 2>&1 1>/dev/null   # stderr only, shows skip path
[1/6] Balance check
  Operator 0xDC5b...17B5: 1.0000 PEAQ (sufficient)
[2/6] 2FA enrollment - skipped (all wallets funded)
[3/6] Fund from Gas Station - skipped (all wallets funded)
[4/6] Register machine
  Already registered (machine_id=42). Skipping.
[5/6] Mint NFT
  Already minted (token_id=11). Skipping.
[6/6] Write DID attributes
  Machine DID attributes already on chain. Skipping.
```

The stdout summary stays the same on rerun; only the stderr skip
messages and the `peaqos.log` entries flip from `confirmed` to `skipped`.

**Example — `peaqos.log` (JSONL, one line per step):**

```jsonl
{"step":"register","status":"confirmed","mode":"self","address":"0xDC5b...","machine_id":42,"ts":"2026-04-23T15:00:18Z"}
{"step":"mint_nft","status":"confirmed","machine_id":42,"recipient":"0xDC5b...","token_id":11,"tx_hash":"0xminttx...","ts":"..."}
{"step":"machine_did","status":"confirmed","mode":"self","machine_id":42,"token_id":11,"operator_did":"","machine_did_tx_count":6,"tx_hash":"0xdidtx...","ts":"..."}
```

On a rerun, the same three rows appear with `"status":"skipped"` and, for
the DID row, `"recovered_from":"AttributeAlreadyOnChain"`.


### `peaqos show machine`

Display the full on-chain profile for a single machine DID — identity,
DID attributes, MCR snapshot, and recent event summary.

```bash
peaqos show machine did:peaq:0x<40-hex>
peaqos show machine did:peaq:0x<40-hex> --json   # raw JSON to stdout
```

Example output:

```
  Machine: did:peaq:0x9a5F1E244c15e491Ae571c5bF77fDD836ddc37C5

    Machine ID:  45
    Operator  :  did:peaq:0x9Eea...641C

    DID Attributes:
      documentation_url:  https://example.com/docs
      data_visibility  :  public

    MCR Snapshot:
      Rating     :  B
      Score      :  31 / 100
      Bond Status:  bonded

    Recent Events:
      Total            :  10
      Last Event       :  2026-04-20T14:30:00Z
      Last Origin Value:  123
      Last Currency    :  HKD
      Last Subunit     :  100
      Last Status      :  ok
      Last USD Value   :  0.13
```

When the most recent event is a revenue event, the block surfaces the
PRO-336 / PRO-334 FX fields. `Last USD Value` is rendered as USD
dollars (Decimal-quantised to 2 places — `usd_value=13` → `0.13`,
`usd_value=150` → `1.50`). When `amount_status` is
`"unsupported_currency"` or `"fx_unavailable"`, the `Last USD Value`
row shows `—` (em-dash) so the CLI never displays a misleading USD
number for a row whose FX state the server flagged as unreliable:

```
    Recent Events:
      Total            :  10
      Last Event       :  2026-04-20T14:30:00Z
      Last Origin Value:  100
      Last Currency    :  XYZ
      Last Status      :  unsupported_currency
      Last USD Value   :  —
```

Activity events (eventType=1) omit all five FX lines.

### `peaqos show operator machines`

List every machine managed by a given operator DID in a tabular summary
(`peaqID`, `Machine ID`, `MCR`, `Rating`).

```bash
peaqos show operator machines did:peaq:0x<40-hex>
peaqos show operator machines did:peaq:0x<40-hex> --json   # raw JSON to stdout
```

Example output:

```
  Operator: did:peaq:0x9Eeab1aCcb1A701aEfAB00F3b8a275a39646641C
  Machines: 3

peaqID                                              Machine ID   MCR   Rating
────────────────────────────────────────────────────────────────────────────
did:peaq:0x9a5F...37C5                              45           31    B
did:peaq:0xAb3D...12F0                              46           75    A
did:peaq:0xC12E...99B1                              47           55    BB
```

### `peaqos qualify event`

Submit one machine event on-chain via the Event Registry (`submitEvent`).

#### Prerequisites

Configure `.env` in the CLI directory (or export the same variables): private key, RPC URL, and contract addresses. See `peaq_os_cli.config.load_client`.

#### Required flags

| Flag | Meaning |
|------|---------|
| `--machine-id` | Positive integer machine identity. |
| `--type` | `revenue` or `activity`. |
| `--value` | Non-negative integer **as ISO 4217 subunit** per PRO-334. **BREAKING**: pre-PRO-334 callers passed whole-currency amounts; the wire is now subunit. Example: `HK$1.23 → --value=123 --currency HKD`; `¥100 → --value=100 --currency JPY` (JPY has no minor unit). Partner is responsible for the conversion. |
| `--ts` | Event time: Unix seconds (digits only) or ISO 8601 with timezone (`Z` or `+hh:mm`). |

Use a timestamp **on or before** the chain's block time. If `--ts` is ahead of the network clock, the contract can revert with `FutureTimestamp`.

#### Common options

| Flag | Meaning |
|------|---------|
| `--trust` | `self` (default), `onchain`, or `hardware`. |
| `--source-chain` | `same`, `peaq`, or `base` (maps to a chain id for the SDK). |
| `--source-tx` | 32-byte tx hash (hex, `0x` optional). **Required** when `--trust` is `onchain`. |
| `--raw-data` | File path; file bytes are hashed and stored as the event data hash. |
| `--metadata` | File path; bytes are sent as on-chain metadata. |
| `--currency` | Per PRO-336 §6 — currency code for revenue events (e.g. `USD`, `HKD`, 3-10 uppercase alphanumeric chars). Activity events take `""`. **Omit** to use the SDK's smart default (`"USD"` for revenue, `""` for activity). |

#### Examples

```bash
# Revenue event (self-reported trust, default source chain)
peaqos qualify event --machine-id 42 --type revenue --value 100 --ts 1735000000
```

```
Event submitted.
  Machine ID:  42
  Type:        revenue
  Value:       100
  Trust:       self-reported
  Tx:          0x3f4a8c1e2d9b7f05a6c3e8d1f4b2a7c9e0d5f3b1a8e2c6d9f7b4a1e3c5d8f2b4
  Data Hash:   0xa1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890
```

```bash
# Activity with ISO timestamp
peaqos qualify event --machine-id 42 --type activity --value 0 --ts "2026-04-22T12:00:00Z"

# On-chain-verified event with a source tx hash
peaqos qualify event --machine-id 42 --type revenue --value 200 --ts 1735000000 \
  --trust onchain \
  --source-tx 0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab

# Attach a payload file (raw data is hashed on submit)
peaqos qualify event --machine-id 42 --type activity --value 0 --ts 1735000000 \
  --raw-data ./sensor.bin
```

### `peaqos qualify mcr`

Look up a machine's **Machine Credit Rating (MCR)** from the MCR HTTP API.

#### Usage

```text
peaqos qualify mcr <DID> [--json]
```

- **`DID`** — Must match `did:peaq:0x` plus exactly 40 hex characters (checksum casing is allowed).
- **`--json`** — Print **only** JSON to stdout (pretty-printed, indent 2). No banner or prose. Useful for scripts.

In `--json` mode the object includes the SDK field **`mcr`** (rating label, e.g. `Provisioned`, `BBB`) and a duplicate key **`mcr_rating`** with the same string for tools that expect a `*_rating` field.

#### Examples

```bash
# Human-readable block (rating, score, bond, events, trend, last updated)
peaqos qualify mcr did:peaq:0x9a5F1E244c15e491Ae571c5bF77fDD836ddc37C5
```

```
MCR for did:peaq:0x9a5F1E244c15e491Ae571c5bF77fDD836ddc37C5

  Rating:          A
  Score:           82 / 100
  Bond Status:     bonded

  Events:
    Total:         150
    Revenue:       120
    Activity:      30

  30-Day Revenue:  +12.5%
  Last Updated:    2026-04-20T14:30:00Z
  FX Degraded:     no
```

`FX Degraded: yes` (PRO-336 §S6 / PRO-331) means at least one event in the
scoring set used a degraded FX source (`stale_latest` /
`default_usd_fx_outage`). Use it to distinguish a conservative score caused
by FX outage from an empty-data machine when gating UI / alerts on data
quality.

```bash
# Machine-readable JSON for jq / scripts
peaqos qualify mcr did:peaq:0x9a5F1E244c15e491Ae571c5bF77fDD836ddc37C5 --json
```

```json
{
  "did": "did:peaq:0x9a5F1E244c15e491Ae571c5bF77fDD836ddc37C5",
  "mcr": "A",
  "mcr_rating": "A",
  "mcr_score": 82,
  "bond_status": "bonded",
  "event_count": 150,
  "revenue_event_count": 120,
  "activity_event_count": 30,
  "revenue_trend": "+12.5%",
  "last_updated": 1745152200
}
```

#### Typical failures

| Situation | Exit | What you see |
|-----------|------|----------------|
| Bad or empty DID | `1` | Validation message |
| No MCR row for that DID (HTTP 404) | `2` | `Machine not found` |
| API unavailable (HTTP 503) | `2` | `MCR API unavailable` |
| Other HTTP / RPC issues | `2` | Wrapped SDK or network message |

## Errors

The CLI maps SDK and network exceptions to stable exit codes:

| Exit code | Meaning                                                      |
| --------- | ------------------------------------------------------------ |
| `0`       | Success                                                      |
| `1`       | User / validation error (bad input, cap, rate limit)         |
| `2`       | Network, RPC, or on-chain error (connection, HTTP, revert)   |
| `3`       | Configuration error (missing env vars, invalid private key)  |

Subcommands funnel exceptions through `peaq_os_cli.errors.handle_sdk_error`,
which raises `click.ClickException` with the mapped exit code and a
user-friendly message. Known on-chain revert reasons and Faucet API error
codes are translated by `map_revert_reason` and `map_faucet_error`.

## Output formatting

Subcommands render results through `peaq_os_cli.formatting` to keep human
output consistent across the CLI:

* `truncate_address` — shortens a hex address to `0x{first4}...{last4}`.
* `format_timestamp` — renders a Unix timestamp as ISO 8601 UTC, or `—` for
  `None`.
* `format_key_value` — aligns `key: value` pairs so the colons line up.
* `format_table` — left-aligned columnar table with a header separator.
* `print_json` — writes `json.dumps(data, indent=2)` to stdout for
  pipe-friendly output.
* `print_step` — writes `[{step}/{total}] {label}` progress lines to
  stderr, suppressed when the active Click context is quiet.

## Utilities

Input parsing and validation helpers in `peaq_os_cli.utils`:

* `parse_timestamp` — accepts pure-digit Unix seconds or ISO 8601 strings
  with an explicit UTC offset (`...Z` or `...+HH:MM`). Raises
  `click.BadParameter` on unrecognised input.
* `validate_did_format` — requires `did:peaq:0x` followed by 40 hex
  characters.
* `validate_address_format` — requires a `0x`-prefixed 40-hex-character
  address.
* `read_key_file` — reads, strips, and validates a `0x`-prefixed 64-hex
  private key file. Raises `click.ClickException` with exit code `1` on
  missing files or invalid content.
