Metadata-Version: 2.5
Name: wohnungssuche-cli
Version: 0.6.0
Summary: CLI for the Wohnungssuche apartment-hunt cockpit — an ordinary signed-in identity
License-Expression: MIT
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.15
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# wohnungssuche-cli

Die Kommandozeile des Wohnungssuche-Cockpits.

## Das Prinzip

Die CLI ist **eine gewöhnliche angemeldete Identität**. Sie besitzt ein
undurchsichtiges Geheimnis ohne jede Aussage über Rechte, tauscht es gegen ein
kurzlebiges JWT, dessen `sub` dieselbe Nutzer-Id ist wie in einer
Browsersitzung — und danach sieht kein Autorisierungstor im Backend einen
Unterschied.

```
wau_<40 hex>  ──POST {site}/cli/token, Authorization: Bearer──▶  { token, expiresAt }
                 401 mit Grund (unknown | revoked | expired | orphaned)
                 503, wenn die Deployment nicht signieren kann
JWT (RS256, 15 min)
  ──▶ POST {cloud}/api/query | /api/mutation | /api/action
      { "path": "tenants:listMine", "args": {}, "format": "json" }
```

Es gibt keine zweite Rechtelogik, keine Fachfunktion weiß von der CLI, und ein
Token kann nie mehr als sein Besitzer im Browser.

## Einrichten

```bash
uv tool install wohnungssuche-cli

# Entweder ein Token aus der App (Einstellungen → CLI-Token):
wohnungssuche auth login --convex-url <cloud> --site-url <site> --token wau_…

# Oder über die gewöhnliche Anmeldung; die CLI legt sich das Token selbst an:
wohnungssuche auth login --convex-url <cloud> --site-url <site> --email du@example.de

wohnungssuche doctor
```

Zwei Adressen, zwei verschiedene Hosts:

| Feld | was dort liegt |
|---|---|
| `convex_url` | `/api/query`, `/api/mutation`, `/api/action` |
| `site_url` | `/cli/token`, `/cli-jwks.json` |

Datei: `~/.config/wohnen/config.toml`, Modus 0600. Feldweise überschreibbar mit
`WOHNEN_CONVEX_URL`, `WOHNEN_SITE_URL`, `WOHNEN_TOKEN`; eine andere Datei mit
`WOHNEN_CONFIG`.

### Umstieg von der alten Konfiguration

Die alte Datei hielt `deployment_url` + `api_key`. Der `api_key` ging an eine
eigene REST-Fläche unter `/api/*`, hing an keiner Identität und ist ersatzlos
abgeschaltet. **Er lässt sich nicht in ein Token umrechnen** — ein neues Token
prägt ein angemeldeter Mensch. Außerdem braucht die Datei jetzt beide Adressen:
`deployment_url` war die Site-Adresse, die Fachaufrufe gehen an die
Cloud-Adresse.

`auth status` und `doctor` erkennen die alte Gestalt und sagen das, statt sie
stillschweigend zu übergehen.

## Kommandos

```
auth login|status|logout      doctor      whoami
tenants   list [--all] | create <slug> <name>
users     list [--tenant|--all] | create <email> --password …
          | set-password <konto> | set-language <email> <de|en>
          | add-to-tenant <email> --tenant … | remove <konto> [--from-tenant …]
projects  list | create <name> --region <ort> | update <p> … | archive <p>
syncs     list | detail <syncRunId> | errors | trigger <plattform> | stale
listings  list | detail <id> | save <id> [--off] | hide <id> | restore <id>
schedule  list | set <plattform> --interval <min> [--enabled|--disabled]
tokens    create <name> | list | revoke <tokenId>
platform  admins list | admins grant <email> | admins revoke <email>
dashboard show
```

Jedes Kommando kann `--json`.

**Für Aufgaben statt Befehle: [`playbooks/`](./playbooks/README.md).** Dort
steht kopierfertig, wie man einen Haushalt in Betrieb nimmt, ein Konto anlegt,
ein Passwort neu setzt und einen Plattform-Betreiber ernennt — samt Nachweis
und den Fallen. Eine neue Fähigkeit gilt erst als fertig, wenn ihr Playbook
existiert.

Alles, was einen Suchauftrag braucht, nimmt `--project <slug|id>`. Ohne Angabe
gilt der aktive Suchauftrag, und die CLI sagt auf stderr, welchen sie genommen
hat. Eine **Kennung**, die nicht in der eigenen Liste steht, wird absichtlich
ans Backend durchgereicht — es wirft dann mit Grund, statt dass die CLI eine
Antwort erfindet.

**Kennungen werden in Tabellen nie gekürzt.** `syncs list` druckt genau das,
was `syncs detail` erwartet.

## Veröffentlichen

Die Gitea-Registry nimmt **jede Version genau einmal** an. Wer `cli/**` ändert,
ohne die Version anzuheben, bekommt einen roten Lauf mit `409 Conflict:
package file already exists` — eine Meldung, die nach einem Problem der
Registry aussieht und keines ist. Deshalb prüft der Workflow das vorher und
sagt es im Klartext.

Anheben heißt: **beide** Stellen, `cli/pyproject.toml` und
`cli/src/wohnen_cli/__init__.py`.

## Entwickeln

```bash
uv venv .venv && uv pip install -e ".[dev]"
.venv/bin/ruff check .
.venv/bin/python -m pytest -q
```

Aufbau: `src/wohnen_cli/core/` ist markenneutral (Konfiguration, Token-Tausch,
JWT-Speicher mit `0600`, Transport mit genau EINEM Erneuerungsversuch bei einem
Anmeldefehler, Ausgabe, Fehler); `src/wohnen_cli/{branding,client,context}.py`
und `commands/` sind die dünne Schicht darüber.
