Metadata-Version: 2.4
Name: streamformation-cli
Version: 0.1.0
Summary: CLI for StreamFormation
Project-URL: Homepage, https://streamformation.com
Project-URL: Repository, https://git.nedos.co/streamformation/sf-mono
Requires-Python: >=3.11
Requires-Dist: rich>=14.2.0
Requires-Dist: streamformation-sdk>=0.1.0
Requires-Dist: typer>=0.20.0
Requires-Dist: websockets>=15.0.1
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.3.0; extra == 'dev'
Description-Content-Type: text/markdown

# StreamFormation CLI

`cli/` contains the `sf` command. It is a thin operator/developer CLI over the
Python SDK, so CLI behavior should mirror SDK behavior instead of rebuilding URL
or payload logic.

## Install

From the repository root:

```bash
python3 -m pip install -e ./sdk -e ./cli
sf --help
```

The command reads config from flags, environment variables, then
`$HOME/.sf/config`, then the nearest project `.sf/config`.

```bash
sf configure --api-url https://api.dev.streamformation.com/api --api-key "$TOKEN"
sf configure --dev --admin-api-key "$STREAMFORMATION_ADMIN_API_KEY"
sf configure --dev --api-key "$TOKEN" --alert alert-1
sf configure --dev --api-key "$TOKEN" --alert alert-1 --reference-audio .sf/reference.wav
sf configure --local --dev --api-key "$TOKEN" --alert alert-1
```

Common environment variables:

| Variable | Purpose |
| --- | --- |
| `STREAMFORMATION_API_URL` | API base URL, including `/api`. |
| `STREAMFORMATION_API_KEY` | User bearer token or user key. |
| `STREAMFORMATION_ADMIN_API_KEY` | Admin key for bootstrap/e2e commands. |
| `STREAMFORMATION_ALERT` / `SF_ALERT` | Default alert for shorthand sends. |
| `STREAMFORMATION_REFERENCE_AUDIO` / `SF_REFERENCE_AUDIO` | Default reference audio path for shorthand voice-clone sends. |
| `STREAMFORMATION_AUDIO_DURATION_PADDING_MS` / `SF_AUDIO_DURATION_PADDING_MS` | Padding added to probed audio length for shorthand alert duration. Defaults to `1000`. |
| `STREAMFORMATION_MEDIA_URL` | Optional media URL override for e2e assertions. |
| `NOTIFY_URL` / `NOTIFY_API_KEY` | Optional notify-service log assertions in e2e. |

`--dev` uses `https://api.dev.streamformation.com/api`.

## Command Structure

| Command | Purpose |
| --- | --- |
| `sf configure` | Save local API URL and keys. |
| `sf admin ...` | Admin bootstrap/login operations. |
| `sf alerts ...` | Alert CRUD, submit-key notification sends, and read-key WebSocket URLs. |
| `sf media ...` | Upload, list, download, and delete user media. |
| `sf voice ...` | Generate TTS, cloned, or designed voice audio and optionally send it to an alert. |
| `sf e2e alerts` | Full SDK/CLI smoke flow for alerts, media, WebSockets, and notify logs. |

Alert notification submission intentionally lives under `sf alerts send`. The
backend `notify/` service is a separate internal delivery service and should not
be confused with alert overlay notifications.

## Agent Shorthand

For agents and local scripts, put default credentials in `$HOME/.sf/config`.
Project-local `.sf/config` is only used when a value is missing from home config:

```text
STREAMFORMATION_API_URL=https://api.dev.streamformation.com/api
STREAMFORMATION_API_KEY=usr_... or user JWT
STREAMFORMATION_ALERT=alert-1
STREAMFORMATION_REFERENCE_AUDIO=/absolute/path/to/reference.wav
```

`key_...` is the write-side submit key and can send directly without user auth.
`alert_...` is the read-side render/WebSocket key. Alert names, ids, and short
URLs require `STREAMFORMATION_API_KEY`/`--api-key` or an inline user key such as
`user_.../alert-1` so the CLI can resolve the friendly ref to `key_...`.

Then send with the top-level shorthand:

```bash
sf "Thanks for the donation"
sf "Thanks with cloned voice" --reference-audio .sf/reference.wav
sf --alert alert-1 "Thanks with media" --media-url https://media-dev.streamformation.com/usr_.../muted-alert.mp4
sf --alert key_... "Thanks with cloned voice" --reference-audio .sf/reference.wav
sf --alert user_.../alert-1 "Thanks with inline user key" --reference-audio .sf/reference.wav
```

When the shorthand includes `--reference-audio`, the CLI sends the reference
audio to the submit-key backend. The backend clones audio, stores it as
`render/{notification_id}/voice-clone.mp3`, injects `audio_url`, and sets
`settings.duration` to generated audio length plus 1000 ms unless duration was
explicitly provided. Programmatic callers can submit with `wait=false` and poll
`/api/notifications/{notification_id}` for the in-memory status.

When the shorthand includes an existing `--audio-url`, the CLI probes the audio
with `ffprobe` when available and sets `settings.duration` to audio length plus
1000 ms. Change the padding with:

```bash
STREAMFORMATION_AUDIO_DURATION_PADDING_MS=1500 sf "Thanks" --reference-audio .sf/reference.wav
sf "Thanks" --audio-url https://media-dev.streamformation.com/usr_.../voice/clip.mp3 --audio-duration-padding-ms 500
```

Explicit settings win:

```bash
sf "Thanks" --audio-url https://media-dev.streamformation.com/usr_.../voice/clip.mp3 --settings '{"duration":3000}'
```

To clone from an existing alert video, extract compact reference audio first:

```bash
ffmpeg -y -i ./alert-video-with-audio.mp4 -vn -ac 1 -ar 24000 -t 30 .sf/reference.wav
sf "Thanks in the cloned voice" \
  --reference-audio .sf/reference.wav \
  --media-url https://media-dev.streamformation.com/usr_.../muted-alert.mp4
```

If `media_url` is a video with its own audio track, the browser may play both
the video audio and the dynamic `audio_url`. Use muted video media when cloned
audio should be the only sound.

## Admin

```bash
sf --dev --admin-api-key "$ADMIN_KEY" admin users list --output table
sf --dev --admin-api-key "$ADMIN_KEY" admin users ensure dev-user \
  --handle dev-user \
  --email dev-user@example.com \
  --name "Dev User" \
  --output json
sf --dev --admin-api-key "$ADMIN_KEY" admin login usr_...
```

`admin login` prints a user JWT that can be passed back as
`STREAMFORMATION_API_KEY` or `--api-key`.

## Alerts

Alert management uses authenticated `/api` routes. Alert notification delivery
uses root routes derived from the same base URL:

| Key | Direction | Shape |
| --- | --- | --- |
| `key_...` | Write/submit | `POST /{key}` or `GET /{key}` trigger. |
| `alert_...` | Read/render | `GET /{alert_key}` and `GET /ws?alert_key=...`. |

Create and inspect alerts:

```bash
sf --dev --api-key "$TOKEN" alerts create \
  --name "Donation" \
  --short-url donation \
  --config '{"type":"donation","layout":"bottom"}' \
  --output json

sf --dev --api-key "$TOKEN" alerts list
sf --dev --api-key "$TOKEN" alerts delete al_...
```

Send a plain alert notification:

```bash
sf --dev alerts send key_... \
  --message "Thanks for the donation" \
  --data '{"amount":42,"user":"Ada"}' \
  --settings '{"duration":2500,"animationIn":"fadeInUp"}' \
  --output json
```

Send caller-formatted payload data:

```bash
sf --dev alerts send key_... \
  --formatted '{"title":"New member","body":"Ada joined","accent":"#19c37d"}' \
  --output json
```

Send dynamic media/audio URLs:

```bash
sf --dev alerts send key_... \
  --message "Voice alert" \
  --audio-url https://media-dev.streamformation.com/usr_.../voice/clip.mp3 \
  --media-url https://media-dev.streamformation.com/usr_.../alert.gif \
  --settings '{"soundVolume":0.7}' \
  --output json
```

Get the WebSocket URL for an overlay:

```bash
sf --dev alerts ws-url alert_...
```

## Voice

Voice commands call backend `/api/voice/*`, which calls Hyper voice endpoints,
stores the generated audio in StreamFormation media, and returns a reusable
media URL.

TTS:

```bash
sf --dev voice tts "Thanks for the donation" \
  --voice Chelsie \
  --format mp3 \
  --output json
```

Clone from reference audio:

```bash
sf --dev voice clone "Thanks for the donation" \
  --ref tests/fixtures/voice/reference.wav \
  --format mp3 \
  --output json
```

Design a voice from a description:

```bash
sf --dev voice design "Welcome in" \
  --desc "warm narrator, upbeat, clean studio voice" \
  --format mp3 \
  --output json
```

Generate audio and immediately send it to an alert:

```bash
sf --dev voice clone "Thanks!" \
  --ref tests/fixtures/voice/reference.wav \
  --send key_... \
  --output json
```

The REST response includes:

```json
{
  "operation": "clone",
  "bucket": "media",
  "key": "usr_.../voice/....mp3",
  "url": "https://media-dev.streamformation.com/usr_.../voice/....mp3",
  "content_type": "audio/mp3",
  "size": 45716,
  "response_format": "mp3",
  "source": {}
}
```

## Media

```bash
sf --dev media upload ./overlay.png --output json
sf --dev media ls
sf --dev media download 'usr_.../file.png' ./file.png
sf --dev media rm 'usr_.../file.png'
```

Uploaded media responses include `key` and public `url`. Use those URLs in
`sf alerts send --media-url` or SDK notification payloads.

## E2E

```bash
STREAMFORMATION_ADMIN_API_KEY=... sf --dev e2e alerts
```

The e2e flow bootstraps a user, uploads media, creates alerts, sends alert
notifications, verifies WebSocket delivery, checks metrics, and cleans up. When
`NOTIFY_URL` and `NOTIFY_API_KEY` are set, it also verifies notify-service logs.

Use this in CI with:

```bash
SF_E2E_NOTIFY_REQUIRED=1 \
NOTIFY_URL=https://api.dev.streamformation.com/notify \
NOTIFY_API_KEY=... \
STREAMFORMATION_ADMIN_API_KEY=... \
sf --dev e2e alerts
```
