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
cli-tools config: store API keys instead of requiring an exported one (#14)
generate-names needed OPENAI_API_KEY or ANTHROPIC_API_KEY in the environment,
which in practice means writing the key into a shell profile — a plaintext copy
in a file that is backed up, synced, and read by every process you start, to
configure one command that runs occasionally.
So there is now a store: ~/.config/cli-tools/credentials.json, written 0600
inside a 0700 directory, with a command to manage it.
cli-tools config what is set, and where each key came from
cli-tools config set openai prompts with echo off
cli-tools config unset openai
Nothing prints a whole key. `config` shows a masked preview and a length —
enough to tell two keys apart, not enough to use one — and --json carries the
same previews rather than the values.
The environment still wins over the file, because a one-off `KEY=… command` and
a CI-injected key both have to keep working. That precedence is invisible at the
moment it bites: you store a key, the old one keeps being used, and nothing says
why. So `config` reports the *source* of each key rather than only whether one
exists, and states plainly when a stored value is being shadowed. It is the same
lesson as the `list` fix in #12 — presence is not the useful question, provenance
is.
A value may be piped for scripts, or passed inline; inline warns, because it
lands in shell history and in `ps`.
Deliberately not a .env: nothing loads it into an environment wholesale, nothing
syncs it, and it is not how a key travels between machines. A secret a deployed
service needs still belongs on that service.
resolveCredentials returns an environment-shaped record, so resolveProvider
consumes it unchanged — the wiring is one line, and a test covers it.
167 tests pass (was 164 plus 3 new wiring cases), typecheck clean. Verified end
to end in a sandbox: set/unset/re-set, 0600 on a file that already existed with
a looser mode, masked output, and no key in --json.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments