Skip to content

Validate input before credentials; fix help rendering and closed pipes#55

Merged
alexkroman merged 3 commits into
mainfrom
claude/magical-goldberg-8bpfuw
Jun 10, 2026
Merged

Validate input before credentials; fix help rendering and closed pipes#55
alexkroman merged 3 commits into
mainfrom
claude/magical-goldberg-8bpfuw

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Summary

This PR hardens input validation to run before credential resolution, fixes help text rendering on narrow terminals, and ensures the CLI properly handles closed downstream pipes (e.g., aai … | head).

Key Changes

Input validation before credentials:

  • resolve_audio_source() now checks that local file paths exist before any credential resolution, so typos read as "file not found" (exit 2) rather than auth failures or API errors
  • Added validate_transcript_id() to reject path traversal attempts (e.g., ../../etc/passwd) before they're interpolated into API request paths
  • get_transcript() validates the transcript ID before making the request
  • transcribe, stream, and agent commands now fail cleanly on missing local files before prompting for login
  • --show-code paths bypass the existence check (generating code for a file you don't have yet is legitimate)

Help rendering fixes:

  • Introduced _NoClipTable to pin the no_wrap flag on all columns except the last two, preventing flag names like --end-of-turn-confidence from being clipped to --end-of-turn-c… on 80-column terminals
  • Patched Typer's rich rendering to use the new table class
  • Fixed Click's unknown-flag suggestion rendering: instead of showing "('(Possible options: --json)',)", now displays clean (Possible options: --json)

Closed-pipe handling:

  • PipeSafeConsole in theme.py re-raises BrokenPipeError instead of converting it to SystemExit(1), so aai … | head exits 0 (success) rather than 1
  • main.run() catches the SystemExit(1) that Typer's vendored Click raises when it detects a closed pipe (via PacifyFlushWrapper) and rewrites it to exit 0
  • Added comprehensive tests for the closed-pipe contract

Keyring robustness:

  • config.py now treats keyring.errors.NoKeyringError (headless machines with no keyring backend) as "no key stored" rather than crashing
  • _keyring_get() helper wraps all keyring reads to suppress KeyringError exceptions
  • clear_api_key() and clear_session() now suppress the broader KeyringError (not just PasswordDeleteError) to handle headless environments

Error handling improvements:

  • run_command() in context.py now catches unexpected exceptions and emits them as clean one-line errors (with JSON shape under --json) rather than tracebacks
  • Added check_source_exists() in transcribe_exec.py to validate audio sources early in the transcribe flow

Documentation:

  • Updated root help text to document authentication: ASSEMBLYAI_API_KEY env var, --env/AAI_ENV for backend selection
  • Updated AGENTS.md to clarify that dev dependencies are a PEP 735 [dependency-groups] group, not a [project] extra

Testing

  • New test_source_validation.py covers file existence checks, transcript ID validation, and the validation-before-credentials contract
  • New test_help_rendering.py pins flag rendering and unknown-flag suggestions across all commands
  • Updated test_context.py, test_config.py, test_main_module.py, and test_theme.py with tests for the new error handling and closed-pipe behavior
  • Snapshot tests updated to reflect help text changes (flag names no longer clipped, help text rewording)
  • Existing transcribe/stream tests now use an autouse fixture to create real audio files (required since path validation now runs before mocking)

https://claude.ai/code/session_012SruYtH5KNGh7pcwMqYrxc

claude added 3 commits June 10, 2026 03:46
…elp legibility

Six issues surfaced by CLI usability testing:

- Treat a missing/broken OS keyring backend as "no stored key": every
  credentialed command (and aai doctor/init) crashed with a NoKeyringError
  traceback on headless machines. Reads degrade to None, deletes are
  suppressed, and run_command gains a last-resort handler so a bug prints
  one clean line (JSON shape under --json), never a traceback.
- Document authentication in the root help (login / ASSEMBLYAI_API_KEY /
  --env / AAI_ENV) and name the env var in the not-signed-in suggestion.
- Validate inputs before credentials: local audio paths are existence-
  checked before key resolution (transcribe/stream/agent), and transcript
  ids must be url-safe tokens before they reach the API path — a crafted
  id could previously steer an authenticated GET to an arbitrary route.
  whoami now honors ASSEMBLYAI_API_KEY as a preflight check for CI.
- Honor the closed-pipe-is-success contract end to end: Rich's console
  and vendored Click both converted EPIPE to exit 1 before main.run()'s
  BrokenPipeError handler could see it.
- Render flag names un-clipped at 80 columns (no more "--end-of-turn-c…"):
  help-table name columns no longer shrink below their content, and the
  one unfittable flag is renamed to --end-of-turn-confidence.
- Fold vendored Click's flag suggestion into the message instead of the
  stringified tuple it printed ("('(Possible options: --json)',)").

https://claude.ai/code/session_012SruYtH5KNGh7pcwMqYrxc
…il gates

Three paper cuts hit while working in this repo:

- AGENTS.md's setup command (uv sync --extra dev) errors: dev tooling is a
  PEP 735 dependency group installed by default, not an extra. Say so.
- A bare pytest ran the e2e/install/install_script suites, which need
  network/credentials and fail confusingly in sandboxes; addopts now carries
  the same -m exclusion check.sh uses (an explicit -m still overrides it).
- The diff-cover and mutation gates are the slowest failures to discover via
  the full script; document how to re-run just those two stages.

https://claude.ai/code/session_012SruYtH5KNGh7pcwMqYrxc
Rich force-enables color under GITHUB_ACTIONS, so in CI the option
highlighter interleaves style codes inside the error message ("No such
option: --zzqq") and the plain substring asserts fail. Compare on the
color-free render, same as the snapshot tests' _normalize.

https://claude.ai/code/session_012SruYtH5KNGh7pcwMqYrxc
@alexkroman alexkroman merged commit 2380ba2 into main Jun 10, 2026
11 checks passed
@alexkroman alexkroman deleted the claude/magical-goldberg-8bpfuw branch June 10, 2026 04:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants