Metadata-Version: 2.4
Name: peaq-os-cli
Version: 0.0.3
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 |

### `peaqos scale agent pair`

Pair an AI agent to an activated machine via the challenge-sign flow.
Requests a pairing challenge, collects the agent's EIP-191 signature, and
creates the pairing with the signed proof. The one-time `pairing_token` is
printed on success. Subsequent commands (such as `peaqos scale search`)
use that token via `--pairing-token-file`.

Requires an activated machine and a platform API key
(`PEAQOS_ORCH_API_KEY`). The token is rendered to stdout exactly once
and is never written to `peaqos.log` or `--verbose` output.

```bash
# Interactive: prompts for the EIP-191 signature
peaqos scale agent pair \
  --machine-id mach_1 \
  --agent-address 0xAgent...0101 \
  --agent-provider teneo \
  --agent-role machine-market-buyer

# Non-interactive: reads signature from file, skips confirmation
peaqos scale agent pair \
  --machine-id mach_1 \
  --agent-address 0xAgent...0101 \
  --agent-provider teneo \
  --agent-role machine-market-buyer \
  --agent-signature-file sig.txt \
  --per-tx-limit 5 --daily-limit 20 --currency USD \
  --allowed-skills pyth-price-feed,walrus-store \
  --yes

# JSON output (requires --agent-signature-file)
peaqos scale agent pair \
  --machine-id mach_1 \
  --agent-address 0xAgent...0101 \
  --agent-provider teneo \
  --agent-role machine-market-buyer \
  --agent-signature-file sig.txt \
  --json
```
### `peaqos scale machine onboard`

Onboard a machine into the Machine Market. Four-step wizard: request an
identity challenge, sign it (via key file, OWS wallet, or manual paste),
register the machine with identity proof, and activate it.

The CLI verifies that the signer address matches one of the DID
controller addresses returned by the challenge endpoint before
proceeding to registration.

**Signing modes** (checked in order):

1. `--identity-signature-file` — read a pre-signed EIP-191 signature from file.
2. `--identity-key-file` — read a private key from file and sign the challenge.
3. OWS wallet (`PEAQOS_OWS_WALLET` set) — sign via the active wallet.
   The key is exported from the vault for the duration of signing only.
4. None of the above — display the challenge and prompt for a pasted signature.

A raw `PEAQOS_PRIVATE_KEY` client does **not** auto-sign; it always falls
through to mode 4 (manual prompt). Only `--identity-signature-file`,
`--identity-key-file`, or an active OWS wallet avoid the prompt.

```bash
# Sign with a DID controller key file (non-interactive)
peaqos scale machine onboard \
  --identity-ref peaqos:machine:my-bot \
  --display-name "My Edge Node" \
  --owner-id operator-42 \
  --machine-type edge-node \
  --runtime-profile linux-docker \
  --identity-key-file ./controller.key \
  --capabilities price-feed,qvac \
  --skill-keys pyth-price-feed \
  --labels env=production,region=eu \
  --yes

# Auto-sign via OWS wallet (PEAQOS_OWS_WALLET must be set)
peaqos scale machine onboard \
  --identity-ref did:peaq:0xAbCd...1234 \
  --display-name "My Edge Node" \
  --owner-id operator-42 \
  --machine-type edge-node \
  --runtime-profile linux-docker \
  --yes

# Pre-signed signature from file
peaqos scale machine onboard \
  --identity-ref peaqos:machine:my-bot \
  --display-name "My Edge Node" \
  --owner-id operator-42 \
  --machine-type edge-node \
  --runtime-profile linux-docker \
  --identity-signature-file sig.txt \
  --yes

# Interactive: prompts for a pasted EIP-191 signature
peaqos scale machine onboard \
  --identity-ref did:peaq:0xAbCd...1234 \
  --display-name "My Edge Node" \
  --owner-id operator-42 \
  --machine-type edge-node \
  --runtime-profile linux-docker

# JSON output (requires a non-interactive signing method)
peaqos scale machine onboard \
  --identity-ref peaqos:machine:my-bot \
  --display-name "My Edge Node" \
  --owner-id operator-42 \
  --machine-type edge-node \
  --runtime-profile linux-docker \
  --identity-key-file ./controller.key \
  --json
```

#### Flags

| Flag | Required | Description |
|------|----------|-------------|
| `--identity-ref` | Yes | Machine identity reference (`did:peaq:0x...` or `peaqos:machine:<id>`). |
| `--display-name` | Yes | Human-readable machine name. |
| `--owner-id` | Yes | Operator/owner identifier. |
| `--machine-type` | Yes | Machine type (e.g. `edge-node`, `robot`). |
| `--runtime-profile` | Yes | Runtime profile (e.g. `linux-docker`). |
| `--capabilities` | No | Machine capabilities (comma-separated). |
| `--skill-keys` | No | Skill keys the machine supports (comma-separated). |
| `--labels` | No | Machine labels (`key=value`, comma-separated). |
| `--identity-signature-file` | No | Path to file containing a pre-signed EIP-191 signature. |
| `--identity-key-file` | No | Path to file containing the DID controller private key for signing. Mutually exclusive with `--identity-signature-file`. |
| `--skip-activate` | No | Register in `draft` status without activating. |
| `-y`, `--yes` | No | Skip confirmation prompt. |
| `--json` | No | Output raw JSON. Requires a non-interactive signing method (`--identity-signature-file`, `--identity-key-file`, or OWS wallet). |

#### Example output

```
[1/4] Requesting identity challenge...
[2/4] Signing identity challenge...
      Signed with key from ./controller.key
      Signer: 0x1111...1111
[3/4] Registering machine...
[4/4] Activating machine...

Machine onboarded successfully (active).

  Machine ID:  mach_abc123
  Identity:    did:peaq:0xAbCd...1234
  Status:      active
  Name:        My Edge Node

  Next: peaqos scale agent pair --machine-id mach_abc123
```

### `peaqos scale machine status`

Display details of a single machine.

```bash
peaqos scale machine status mach_abc123
peaqos scale machine status mach_abc123 --json
```

#### Example output

```
Machine mach_abc123

  Display name:  My Edge Node
  Status:        active
  Identity:      peaqos:machine:my-bot
  Owner:         operator-42
  Type:          edge-node
  Runtime:       linux-docker
  Capabilities:  price-feed, qvac
  Skills:        pyth-price-feed
  Created:       2026-05-16T12:00:00Z
```

### `peaqos scale machine list`

List all machines registered on the orchestration service.

```bash
peaqos scale machine list
peaqos scale machine list --json
```

#### Example output

```
Machines

  ID                     Name                      Status   Identity               Type
  ─────────────────────────────────────────────────────────────────────────────────────────
  mach_abc123            My Edge Node              active   peaqos:machine:my-bot  edge-node
  mach_def456            Test Machine              draft    did:peaq:0xAbCd...1e8f robot

2 machines found.
```

### `peaqos scale search`

Search for services matching a task in the Machine Market. The agent
describes what it needs (service type, capabilities, budget) and gets
back a ranked list of matching quotes.

Requires an active machine and a valid agent pairing token
(`--pairing-token-file`).

```bash
peaqos scale search \
  --machine-id mach_1 \
  --service-type oracle.price-feed \
  --pairing-token-file ./token.txt

peaqos scale search \
  --machine-id mach_1 \
  --service-type oracle.price-feed \
  --pairing-token-file ./token.txt \
  --json   # raw JSON to stdout
```

#### Flags

| Flag | Required | Description |
| `--machine-id` | Yes | Machine performing the search. |
| `--service-type` | Yes | Service type (e.g. `oracle.price-feed`). |
| `--pairing-token-file` | Yes | Path to file containing the agent pairing token. |
| `--agent-pairing-id` | No | Agent pairing ID. |
| `--operation` | No | Desired operation (e.g. `get-latest-price`). |
| `--capabilities` | No | Required capabilities (comma-separated). |
| `--region` | No | Preferred region. |
| `--max-results` | No | Max quotes to return. |
| `--budget-amount` | No | Budget amount. |
| `--budget-max` | No | Maximum budget amount. |
| `--budget-currency` | No | Budget currency (e.g. `USD`). |
| `--native-only` | No | Require native execution (no external handoff). |
| `--allow-handoff` | No | Allow external handoff. |
| `--provider-credentials` | No | Path to JSON file with provider credentials. |
| `--json` | No | Output raw JSON to stdout. |

#### Example output

```
Searching for "oracle.price-feed" services...

Found 2 matching services.

#  Skill Key         Provider   Score  Execution    Integration
────────────────────────────────────────────────────────────────
1  pyth-price-feed   pyth       0.95   native       native
2  dia-price-feed    dia        0.72   ext-handoff  partner-required

Search ID: msearch_449650d9b2be

Use: peaqos scale order <service-id> to place an order (when available).
```

When no quotes are returned:

```
Searching for "compute.marketplace" services...

No matching services found.

Search ID: msearch_abc123

Try broadening your search: remove --native-only, increase --budget-amount, or try a different --service-type.
```

#### Environment

Orchestration-specific variables (in `.env` or environment):

| Variable | Purpose |
|----------|---------|
| `PEAQOS_ORCHESTRATION_URL` | Base URL of the Machine Markets API. |
| `PEAQOS_ORCH_API_KEY` | Platform API key (deployment-level auth). |

Both can also be passed as CLI flags (`--orchestration-url`,
`--orch-api-key`).

### `peaqos scale order <service-uuid>`

Place a market order end-to-end: create the order, handle payment when
required, then execute. The service UUID is the first argument (from a
prior `peaqos scale search` quote). Registered subcommands
(`status`, `list`, `received`, `dispute`) take precedence over service IDs.

Requires an active machine, agent pairing ID, and pairing token
(`--pairing-token-file`). Progress steps are written to `stderr`; the
final summary or `--json` payload goes to `stdout`.

**Payment flows** (chosen from the service quote):

| Pattern | Steps | When |
| ------- | ----- | ---- |
| No payment | `[1/2]` create → execute | `paymentStatus` is `not-required` (e.g. native Pyth) |
| Wallet payment | `[1/5]` create → payment intent → send payment → proof → execute | EVM USDC, Solana USDT, etc. |
| Escrow | Same as wallet, but step 4 calls escrow lock instead of payment proof | External handoff with on-chain escrow |

**Payment transfer modes** (step 3 when wallet payment is required):

| Mode | How |
| ---- | --- |
| **OWS** | `PEAQOS_OWS_WALLET` set — CLI signs and sends the ERC-20 transfer via the OWS vault; tx hash captured automatically. Passphrase from `OWS_PASSPHRASE` or an interactive prompt. |
| **Manual** | No OWS wallet — CLI prints amount, chain, token, and payee; operator completes the transfer and pastes the tx hash at the `>` prompt. |
| **Pre-supplied** | `--payment-tx-hash` (+ `--payment-chain`, `--payment-token`) — skips step 3; proof submitted with the given hash/signature. |

Use `--skip-payment` only when payment was already completed externally;
combine with `--payment-tx-hash` to record proof without re-sending funds.

#### Flags

| Flag / argument | Required | Description |
| --------------- | -------- | ----------- |
| `<service-uuid>` | Yes | Service ID from search results. |
| `--machine-id` | Yes | Machine placing the order. |
| `--agent-pairing-id` | Yes | Agent pairing ID (from `peaqos scale agent pair`). |
| `--pairing-token-file` | Yes | Path to agent pairing token file. |
| `--search-id` | No | Search ID from a prior `peaqos scale search`. |
| `--quote-id` | No | Quote ID from search results. |
| `--operation` | No | Requested operation (e.g. `get-latest-price`). |
| `--budget-amount` | No | Budget amount. |
| `--budget-currency` | No | Budget currency (e.g. `USD`). |
| `--input` | No | JSON file with operation input. |
| `--provider-credentials` | No | JSON file with provider credentials (never logged). |
| `--payment-tx-hash` | No | Pre-completed payment tx hash (EVM) or signature (Solana). |
| `--payment-chain` | No | Chain for proof (e.g. `base`, `solana`, `peaq`). Required with `--payment-tx-hash`. |
| `--payment-token` | No | Token for proof (e.g. `USDC`, `USDT`). Required with `--payment-tx-hash`. |
| `--skip-payment` | No | Skip the on-chain payment step (use with `--payment-tx-hash` when proof is supplied separately). |
| `-y`, `--yes` | No | Skip confirmation prompts. |
| `--json` | No | Output JSON to stdout (implies `--yes`). Contains `order`, `execution`, and `payment` when a payment record exists. |

#### Examples

**No-payment service** (native execution, two steps):

```bash
peaqos scale order svc_pyth_btc_usd \
  --machine-id mach_1 \
  --agent-pairing-id pair_67c50f8fdd3c \
  --pairing-token-file ./token.txt \
  --search-id msearch_449650d9b2be \
  --quote-id quote_abc123 \
  --operation get-latest-price \
  --yes
```

```
[1/2] Creating order...
[2/2] Executing order...

Order executed.

  Order ID:      ord_e89c70259bdb
  Service:       BTC/USD (pyth-price-feed)
  Status:        delivered
  Payment:       not_required

  Confirm delivery: peaqos scale order received ord_e89c70259bdb
```

**Manual wallet payment** (operator sends USDC, pastes hash):

```bash
peaqos scale order svc_dia_eth_usd \
  --machine-id mach_1 \
  --agent-pairing-id pair_67c50f8fdd3c \
  --pairing-token-file ./token.txt \
  --yes
# Step 3 prompts: paste 0x... tx hash after transferring off-wallet
```

**OWS auto-payment** (`PEAQOS_OWS_WALLET` in `.env`):

```bash
export PEAQOS_OWS_WALLET=agent
export OWS_PASSPHRASE='...'   # optional; otherwise prompted once
peaqos scale order svc_dia_eth_usd \
  --machine-id mach_1 \
  --agent-pairing-id pair_67c50f8fdd3c \
  --pairing-token-file ./token.txt \
  --yes
```

```
[3/5] Sending payment...
      Signing transfer via OWS (wallet=agent, chain=base)
      0x3333...3333 → 0x4444...4444  1.00 USDC
      Tx: 0x602d...5584
```

**Pre-supplied payment** (skip transfer step):

```bash
peaqos scale order svc_dia_eth_usd \
  --machine-id mach_1 \
  --agent-pairing-id pair_67c50f8fdd3c \
  --pairing-token-file ./token.txt \
  --payment-tx-hash 0x602d5584... \
  --payment-chain base \
  --payment-token USDC \
  --yes
```

**External handoff** (escrow + handoff URL):

```
Order placed (external handoff).

  Order ID:      ord_63ab7e934300
  Service:       Aethir GPU Cloud
  Status:        handoff
  Handoff:       Open Aethir Cloud → https://app.aethir.com/

  After using the external service:
    peaqos scale order received ord_63ab7e934300
```

**JSON output** (scripting):

```bash
peaqos scale order svc_pyth_btc_usd \
  --machine-id mach_1 \
  --agent-pairing-id pair_67c50f8fdd3c \
  --pairing-token-file ./token.txt \
  --yes --json
```

```json
{
  "order": { "id": "ord_e89c70259bdb", "status": "delivered", "...": "..." },
  "execution": {
    "status_code": 200,
    "run": { "...": "..." },
    "outcome": { "...": "..." }
  }
}
```

When the order requires payment, the response also includes `"payment": { "status": "held", ... }`.

On partial failure after create, the error includes the order ID, current
payment status, and a hint to run `peaqos scale order status <id>`.

### `peaqos scale order status <order-id>`

Check order and payment state.

```bash
peaqos scale order status ord_e89c70259bdb
peaqos scale order status ord_e89c70259bdb --json
```

#### Example output

```
Order ord_e89c70259bdb

  Service:        BTC/USD (pyth-price-feed)
  Status:         delivered
  Execution:      native
  Created:        2026-05-16T12:00:00Z

  Payment:
    Status:       not_required
    Rail:         —
    Amount:       —

  Next: peaqos scale order received ord_e89c70259bdb
```

`--json` returns `{ "order": { ... }, "payment": { ... } }`.

### `peaqos scale order list`

List market orders for a machine (`GET /market/orders`). Supports
page-by-page listing with `--limit` and `--cursor`, or fetching every
order in one shot when using `--json` without `--limit`.

**Flags:**

| Flag | Description |
|------|-------------|
| `--machine-id` | **Required.** Machine whose orders to list. |
| `--limit` | Page size (1–500). Omitted → server default (100). |
| `--cursor` | Opaque cursor from a previous response (next page). |
| `--json` | Machine-readable output (see below). |

**Human output** (default) returns one page. When the API includes a
`next_cursor`, a copy-paste hint is printed after the table. Cursors are
shown verbatim in the hint and are never written to logs (even with
`--verbose`).

```bash
peaqos scale order list --machine-id mach_prod_smoke_mcr_1
peaqos scale order list --machine-id mach_prod_smoke_mcr_1 --limit 10
peaqos scale order list --machine-id mach_prod_smoke_mcr_1 \
  --limit 10 --cursor eyJ2IjoxLCJvZmZzZXQiOjJ9
```

#### Example output (human)

```
Orders for mach_prod_smoke_mcr_1

  ID                     Service                   Status      Execution   Created
  ─────────────────────────────────────────────────────────────────────────────────
  ord_e89c70259bdb       BTC/USD (pyth-price-feed) confirmed   native      2026-05-16T12:25:00Z
  ord_63ab7e934300       Aethir GPU Cloud          disputed    ext-handoff 2026-05-16T13:00:00Z

2 orders shown.

Next page: peaqos scale order list --machine-id mach_prod_smoke_mcr_1 --cursor eyJ2IjoxLCJvZmZzZXQiOjJ9
```

When there is no next page, only the count line is shown (no `Next page:` line).

**JSON output:**

| Invocation | Shape |
|------------|--------|
| `--json` (no `--limit`) | Root **array** of all orders across pages (auto-paginated). |
| `--json --limit N` | Single page: `{ "items": [...], "next_cursor": "..." \| null }`. |

```bash
# All orders (pipe-friendly for jq)
peaqos scale order list --machine-id mach_1 --json | jq '.[] | .id'

# Manual paging
peaqos scale order list --machine-id mach_1 --json --limit 5
```

```json
[
  { "id": "ord_e89c70259bdb", "status": "delivered", "...": "..." },
  { "id": "ord_63ab7e934300", "status": "handoff", "...": "..." }
]
```

```json
{
  "items": [{ "id": "ord_e89c70259bdb", "...": "..." }],
  "next_cursor": "eyJ2IjoxLCJvZmZzZXQiOjJ9"
}
```

Invalid `--limit` (0, negative, or above 500) exits with code `1` and:
`Limit must be between 1 and 500.`

### `peaqos scale order received <order-id>`

Confirm delivery and release escrowed funds. The order must be in
`delivered` status (native execution or after external handoff).

```bash
peaqos scale order received ord_e89c70259bdb \
  --pairing-token-file ./token.txt

peaqos scale order received ord_e89c70259bdb \
  --pairing-token-file ./token.txt \
  --json
```

#### Example output

```
Delivery confirmed.

  Order ID:  ord_e89c70259bdb
  Status:    confirmed
  Payment:   release_pending
```

### `peaqos scale order dispute <order-id>`

Raise a dispute and freeze escrowed payment. Requires `--reason` and the
pairing token. Prompts for confirmation unless `--yes` or `--json`.

```bash
peaqos scale order dispute ord_63ab7e934300 \
  --reason "Service unavailable after payment" \
  --pairing-token-file ./token.txt \
  --yes

# Optional evidence JSON file
peaqos scale order dispute ord_63ab7e934300 \
  --reason "Incorrect deliverable" \
  --evidence ./evidence.json \
  --pairing-token-file ./token.txt \
  --json
```

#### Example output

```
Dispute raised.

  Order ID:  ord_63ab7e934300
  Status:    disputed
  Payment:   frozen
  Reason:    Service unavailable after payment
```

#### Integration tests

Order lifecycle integration tests live at
`tests/integration/commands/scale/test_order_integration.py`. Enable with
`PEAQOS_CLI_ORCH_INTEGRATION=1` and the `PEAQOS_INTEGRATION_*` variables
documented in that file.

## 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.
