Metadata-Version: 2.5
Name: riley-cli
Version: 0.1.0
Summary: CLI for Riley data ingest and agentic BI, over the Riley REST API
Project-URL: Homepage, https://app.askriley.io
Project-URL: Source, https://github.com/AskRiley/riley-cli
Project-URL: Issues, https://github.com/AskRiley/riley-cli/issues
Author-email: Riley <support@askriley.io>
License: Proprietary
License-File: LICENSE
Keywords: analytics,cli,ingest,riley
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: rich>=13.7
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Riley CLI

Riley from the terminal, covering the two halves of the product:

1. **Getting data in** — push a SQL/ETL result file into a Riley dataset.
2. **Asking questions** — run agentic BI analyses and pull the tables behind the answers.

It calls the same first-party REST API the web app uses. Human login is browser
PKCE; the CLI stores a Riley-owned `riley_cli_` credential (not a Firebase
refresh token). It is not an MCP client and it does not orchestrate managed sync.

## Install

The PyPI package is `riley-cli`. The command it installs is `riley`.
You need Python 3.11 or newer.

```bash
# Humans: isolated tool, `riley` on PATH
pipx install riley-cli
pipx upgrade riley-cli

# CI, Airflow, Docker: install into that job's venv or image
python -m pip install riley-cli
```

Do not `pip install --user riley-cli` onto the system interpreter. If
`riley --version` is not this CLI, another package may own the `riley` name
on PATH (`which riley`).

From a clone, for development:

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

## Sign in

`riley login` opens [app.askriley.io](https://app.askriley.io) so Google, Okta, MFA,
and password all work the same way as the web app. A CLI login credential is
cached in `~/.config/riley/credentials.json` (directory mode 700, file mode 600)
for 12 hours, or until you `riley logout` or revoke it in **Integrations → Command
line access**. That file is a bearer token. Treat it like a password: do not
commit it, paste it into tickets, or copy it onto a shared machine.

```bash
riley login
riley whoami
riley logout
riley token list
```

`--no-browser` prints the URL instead of opening it (the callback still has to
reach this machine). Headless or CI hosts should not use `riley login`. Create
an automation token in Integrations → Command line access and export it:

```bash
export RILEY_CLI_TOKEN=riley_cli_…
```

Automation tokens inherit **your** Riley role. They are not org/service-account
identities. Signing out of the website does not revoke CLI tokens. `riley logout`
revokes a file credential on this machine; it does **not** revoke a token provided
via `RILEY_CLI_TOKEN` (revoke those in Integrations → Command line access).

A CLI credential cannot create another CLI credential.

Accounts with an unverified email are rejected by the API on everything except
`whoami`.

## Pipeline upload

This is a non-interactive pipeline step: a query result on one side, Riley on
the other. The first named upload creates the dataset from the file schema.
Later uploads of the same `--dataset-name` apply `--mode`.
The command stays attached until Riley is ready and streams each first-upload
stage: schema validation, dataset/table creation, staging, row loading, column
classification, and final query readiness.

```bash
psql ... -o /tmp/orders.csv
riley upload --dataset-name orders --source /tmp/orders.csv --mode replace

# entity snapshots
riley upload --dataset-name accounts --source ./accounts.csv \
  --mode upsert --merge-key account_id

# one-off (dataset name taken from the file stem)
riley upload --source ./scratch.csv
```

`--mode` is `replace` (default), `append`, or `upsert`. Watch the load with
`riley datasets pipeline` and `riley datasets history orders`.

## Checking what's queryable

```bash
riley datasets list
riley datasets pipeline
riley datasets schema orders
riley datasets history orders
riley datasets export orders --out orders.csv
riley datasets export orders --limit 5000 --out orders.csv
```

Default export is 1,000 rows, with a hard max of 50,000. `--json` without `--out`
returns a row envelope; `--out` writes CSV or jsonl.

## Asking questions

```bash
riley ask "What are the top 5 accounts at risk of churning?"
riley ask "And how did that change last quarter?" -c <conversation_id>
riley analysis citation 456 1 --out accounts.csv
riley analysis feedback 456 "Missed the enterprise segment"
```

`riley ask` mirrors the web app: it queues the analysis, reports each tool call as
the agent works, then streams the summary token by token over SSE. Add `--no-wait`
to just queue it and get an `analysis_result_id` back.

## Scripting

Every command takes `--json`, which prints a machine-readable payload on stdout.
Progress chatter and prompts go to stderr, so pipes stay clean:

```bash
riley --json datasets list | jq -r '.tables[].name'
```

## Configuration

| Variable | Purpose |
|---|---|
| `RILEY_API_URL` | Backend host. Also `--api-url`. Defaults to `https://api.askriley.io` |
| `RILEY_APP_URL` | Website host for `riley login`. Defaults to `https://app.askriley.io` |
| `RILEY_CLI_TOKEN` | Automation token from Integrations → Command line access (overrides the credentials file) |
| `RILEY_CONFIG_DIR` | Where credentials are cached |

A file login is bound to the API host it was issued for. `riley login` against
prod will not send that token to `--api-url` / `RILEY_API_URL` for a different
host (for example `http://localhost:8000`). Log in against that host, or use
`RILEY_CLI_TOKEN`. `localhost` and `127.0.0.1` are different hosts.

`RILEY_CLI_TOKEN` is meant for CI: it is sent to whatever API URL the command
uses. Prefer the environment (or your orchestrator's secret store) over putting
the token in a file on a runner.

`RILEY_APP_URL` only changes where `riley login` opens the browser. Point it
at a host you trust.

The CLI does not follow HTTP redirects, so a mis-typed `http://` API URL is
an error rather than a silent hop that could replay the bearer.

## Layout

| Module | Role |
|---|---|
| `browser_auth.py` | Loopback PKCE for `riley login` |
| `client.py` | REST client: envelope unwrapping, multipart, SSE |
| `commands/uploads.py` | Pipeline file ingest |
| `commands/datasets.py` | Catalog, schema, pipeline health |
| `commands/analyze.py` | Agentic BI |
| `commands/tokens.py` | `riley token list` |

## Tests

```bash
pytest
```

`tests/fake_riley.py` is an in-process stand-in for the Riley REST API, including
the SSE stream, so the suite exercises real HTTP without touching a live backend.
