You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
config.py sat at exactly the 500-line gate with no headroom. Split the OS
keyring access — the one external secret dependency — into a new
core/keyring_store.py: KEYRING_SERVICE plus set_secret/get_secret/
restore_secret/delete_secret/usable, the single importer of `keyring`.
config.py (500 -> 442) now reads and writes secrets through that wrapper and
no longer imports keyring directly, so the documented "secrets live in the
keyring, not the dotfile" boundary is structural. config.keyring_usable stays
as a thin delegator so the auth-state facade (doctor/login and their tests) is
unchanged. The two clear_* paths collapse onto the shared delete_secret.
Tests that reached into config's keyring internals repointed to keyring_store
(config.keyring -> keyring_store.keyring, config.KEYRING_SERVICE ->
keyring_store.KEYRING_SERVICE). No behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Bkx51XDcYBdnot1EF8h9U
`choices`. Contract 4 also forbids `rich` here, so "no Rich below the UI
38
38
layer" is structural.
39
39
@@ -139,7 +139,7 @@ heavily-reworked commands with long bodies; small commands keep the inline
139
139
### Cross-cutting state (resolution order matters)
140
140
141
141
-**`app/context.py`** — `AppState` (profile, env) is attached to the Typer context in the root `@app.callback()`. `run_command` is the standard command wrapper.
142
-
-**`core/config.py`** — profiles persisted in `config.toml` (via `platformdirs`); the **API key lives only in the OS keyring** (`KEYRING_SERVICE = "assemblyai-cli"`), never in a dotfile. Key resolution order: `--api-key` flag (validation paths only) → `ASSEMBLYAI_API_KEY` env → keyring. **Run commands deliberately expose no `--api-key` flag** so keys can't leak into `ps`/shell history.
142
+
-**`core/config.py`** — profiles persisted in `config.toml` (via `platformdirs`); the **API key lives only in the OS keyring**, never in a dotfile. The keyring access itself is factored into **`core/keyring_store.py`** (the single importer of `keyring`, holding `KEYRING_SERVICE = "assemblyai-cli"` + `set_secret`/`get_secret`/`restore_secret`/`delete_secret`/`usable`), so the "secrets never touch the dotfile" split is structural; `config` reads/writes secrets through it and only `config.keyring_usable` re-surfaces the probe on the auth facade. Key resolution order: `--api-key` flag (validation paths only) → `ASSEMBLYAI_API_KEY` env → keyring. **Run commands deliberately expose no `--api-key` flag** so keys can't leak into `ps`/shell history.
143
143
-**`core/environments.py`** — a frozen `Environment` (api_base, streaming_host, llm_gateway_base, ams_base, stytch_*). `DEFAULT_ENV` is **`production`**; use `--sandbox` (or `--env sandbox000` / `AAI_ENV`) to target the sandbox. The active environment is a process-global set once at startup; precedence: `--env` → `AAI_ENV` → profile's stored env → default. A credential is only valid against the environment that minted it.
144
144
-**`core/client.py`** — thin wrappers over the `assemblyai` SDK (`transcribe`, `list_transcripts`, `stream_audio`, etc.). It normalizes SDK exceptions: auth failures become a single clean `auth_failure()``CLIError`; everything else becomes `APIError`. New SDK calls should follow this try/except shape.
145
145
-**`core/errors.py`** — the `CLIError` hierarchy (each with `error_type` + `exit_code`). `ui/output.py` emits errors to **stderr**; stdout stays clean for pipelines. `--json` switches to machine-readable output; it is never auto-enabled — `output.resolve_json()` deliberately keeps human text the default even when piped or agent-run.
0 commit comments