Repository navigation
fix(tui): resolve the /speak OpenAI key through the secret chain - #4709
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Initialization overrides injected transcribers, and secret lookup does not preserve finish and cancellation behavior.
2 open issues
What changed in this PR
Routes /speak authentication through the existing secret-provider chain so voice input can use the same credential sources as chat, without resolving secrets at TUI startup.
Changes:
- Resolve
OPENAI_API_KEYlazily when transcription starts. - Pass the run command’s environment provider into the TUI, with a default for other callers.
- Update transcriber tests for provider injection and verify lazy lookup.
| File | Description |
|---|---|
pkg/tui/tui.go |
Adds environment-provider configuration and initializes the transcriber after options. |
pkg/audio/transcribe/transcribe.go |
Resolves the OpenAI key through a provider on each run. |
pkg/audio/transcribe/transcribe_test.go |
Updates key injection and tests lazy resolution. |
pkg/audio/transcribe/finish_test.go |
Adapts finish tests to provider-based construction. |
cmd/root/run.go |
Supplies the runtime environment provider to the TUI. |
Review effort: Balanced
| apiKey, _ := t.env.Get(ctx, "OPENAI_API_KEY") | ||
| if apiKey == "" { | ||
| return errors.New("/speak sends audio directly to OpenAI; set a separate OPENAI_API_KEY environment variable even when chat uses Docker's models gateway, which does not support /speak") |
There was a problem hiding this comment.
Right, fixed in 8859213. The lookup now runs after the finish watcher and deferred cleanup are set up, on captureCtx, so finishing during an op unlock prompt stops it (and starts the finish timeout), and an interrupted lookup returns nil instead of the missing-key error. Tests: TestRun_FinishInterruptsKeyLookup and TestRun_CancelDuringKeyLookupIsNotAnError, both with a provider that blocks like op read.
| if m.envProvider == nil { | ||
| m.envProvider = environment.NewDefaultProvider() | ||
| } | ||
| m.transcriber = transcribe.New(m.envProvider) |
There was a problem hiding this comment.
Good catch, a regression from moving construction after the options. Fixed in 8859213: the default transcriber is only built when none was injected. Test: TestNew_KeepsInjectedTranscriber.
572bd0a to
16f136b
Compare
/speak read OPENAI_API_KEY with os.Getenv, so it ignored op:// references, --env-from-file, the docker-agent env file and every other source of the chain. The transcriber now takes an environment.Provider and reads the key when /speak starts, never at TUI construction, so launching the TUI cannot trigger an op unlock prompt. run passes its runtime env provider; other callers default to environment.NewDefaultProvider(). Signed-off-by: Arnaud Héritier <aheritier@apache.org>
Signed-off-by: Arnaud Héritier <aheritier@apache.org>
16f136b to
c7e920c
Compare
… lookup - New built the default transcriber after the options, replacing one passed with WithTranscriber; it now only fills in a missing one. - The key lookup ran before the finish watcher and on the parent context, so finishing during an op unlock prompt hung, and cancelling reported a missing key. It now runs on the capture context after the watcher is set up, and an interrupted lookup ends the run quietly. Signed-off-by: Arnaud Héritier <aheritier@apache.org>

Summary
/speakreadOPENAI_API_KEYwithos.Getenv(pkg/tui/tui.go), so it ignored every other secret source:op://1Password references,--env-from-file, the docker-agent env file (~/.config/cagent/.env, wheredocker agent setupstores keys), run secrets and Docker Desktop. Chat worked with a key that voice input could not see.Changes
transcribe.New(env environment.Provider)replacesNew(apiKey string).RunreadsOPENAI_API_KEYfromenvwhen/speakstarts.opunlock prompt or a Docker Desktop query when voice input is not used.tui.WithEnvProvideroption;docker agent runpasses its runtime env provider (so--env-from-fileapplies). Other callers default toenvironment.NewDefaultProvider().Stacked on #4708, so
/speakalso honoursDOCKER_AGENT_ENV_OPENAI_API_KEY, and the secrets guide caveat that #4708 adds ("voice transcription still readsOPENAI_API_KEYdirectly") is removed here, landing together with the fix.Tests
TestRun_ResolvesKeyLazily:Newmakes no lookup;Runmakes exactly one.MapEnvProvider.References
pkg/environment/default.go--env-from-file)pkg/config/runtime.godocs/guides/secrets/index.mdThis is part 2 of 2 in a stack made with GitButler: