bitwarden / bitwarden/agent-access
Feature request: --no-tui flag / AAC_HEADLESS env for headless daemon use (ratatui panic under macOS LaunchAgent Background)
- Dominant language
- Rust
- Stars
- 145
- Forks
- 11
- Avg merge
- 20h 57m
- Merged PRs (30d)
- 1
Description
## Summary
`aac listen` panics at startup when invoked without a controlling terminal (e.g., under macOS LaunchAgent `ProcessType: Background`, systemd headless daemon, container with no PTY, `nohup` invocation, etc.). Would like a `--no-tui` flag (or `AAC_HEADLESS=1` env var) that skips the ratatui TUI init entirely and runs the listener as a pure background daemon with plain-text logging to stdout/stderr.
## Version
```
$ aac --version
aac 0.11.0 (3b000d15af71be5ddcf8893b099f3a66d386be9d)
```
## Use case
Running `aac listen` as an always-on background service on macOS via LaunchAgent, so daemons that need Bitwarden-injected credentials (via `aac run --domain X --env VAR=field -- `) can fetch reliably across reboots + user-session transitions without depending on a foreground Terminal window.
Current workaround: wrap `aac listen` in `/usr/bin/script -q /dev/null ` — `script(1)` allocates a full PTY that satisfies ratatui's `Terminal::init()`. Works but adds a wrapper layer + prevents attach-for-approval (approval prompts happen inside the script-owned PTY which isn't reattachable). A `--no-tui` flag would eliminate the wrapper + let approval prompts either (a) route to an out-of-band notification channel per #142 OR (b) skip approval when `--reusable-psk` cache hit exists.
## Reproduction
Minimal LaunchAgent plist (`~/Library/LaunchAgents/com.example.aac_listen.plist`):
```xml
Label
com.example.aac_listen
Program
/opt/homebrew/bin/aac
ProgramArguments
/opt/homebrew/bin/aac
listen
--reusable-psk
--provider
bitwarden
-v
RunAtLoad
StandardOutPath/tmp/aac.log
StandardErrorPath/tmp/aac.err
EnvironmentVariables
PATH/opt/homebrew/bin:/usr/bin:/bin
BW_SESSION
ProcessTypeBackground
```
`launchctl load ~/Library/LaunchAgents/com.example.aac_listen.plist` → immediate panic + KeepAlive respawn loop.
## Panic output (`/tmp/aac.err`)
```
The application panicked (crashed).
Message: failed to initialize terminal: Os { code: 6, kind: Uncategorized, message: "Device not configured" }
Location: /Users/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ratatui-0.29.0/src/terminal/init.rs:52
Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
```
`Os code: 6` = macOS `ENXIO` returned when `open("/dev/tty", ...)` fails because the process has no controlling terminal. ratatui `Terminal::init()` opens `/dev/tty` unconditionally regardless of flag combination — `--reusable-psk` (which pre-caches PSK to disk) doesn't bypass the TUI init because init happens before flag processing that would enable headless mode.
## Requested change
Add one of:
1. `--no-tui` flag → skip ratatui `Terminal::init()`; log approval state + errors as structured stderr text; approval requests emit to stderr with an out-of-band callback URL/channel (composes cleanly with #142 out-of-band notification feature request)
2. `AAC_HEADLESS=1` env var equivalent
3. Auto-detect: if `isatty(0)` and `isatty(1)` and `isatty(2)` are all false → skip TUI init automatically (Unix convention)
Bonus: with `--no-tui`, `--reusable-psk` cache hits could auto-approve without any user interaction (since the whole point of PSK cache is "this remote client was previously approved for this domain"). This would enable fully-headless daemon lifecycle.
## Empirical validation of workaround (for anyone hitting this)
```bash
# Works — script(1) allocates PTY that satisfies ratatui:
exec /usr/bin/script -q /dev/null /opt/homebrew/bin/aac listen --reusable-psk --provider bitwarden -v
# Fails — screen -dmS silent-dies under launchd Background context:
/usr/bin/screen -dmS aactest '/opt/homebrew/bin/aac listen ...'
# Fails — bare invocation under LaunchAgent Background = ratatui panic:
Program = /opt/homebrew/bin/aac; ProgramArguments = [aac, listen, ...]
```
## Related
- #139 approval prompts desync (would matter less with headless mode + out-of-band approval channel)
- #142 out-of-band notification for pending approvals (composes with `--no-tui`)
Contributor guide
Research direction
Start at the `aac listen` startup path and the ratatui `Terminal::init()` call described in the panic. Compare the requested `--no-tui`, `AAC_HEADLESS=1`, and automatic detection options, then define approval and logging behavior for headless operation. Done means a no-PTY invocation avoids the panic and reports errors and approval state through the documented non-TUI channel.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100