set-key takes the ref it was given, and a key inline
`claude-mode set-key openrouter sk-or-...` did the wrong thing on the POSIX port: the dispatcher loop kept the *last* positional, so the key became the ref name and the prompt asked for "the key for ref 'sk-or-...'". Storing to that ref succeeds, so nothing looks wrong until the switch later reports no key for 'openrouter' - which is exactly what happened on macOS. Both ports now take `set-key [ref] [key]`: the first positional is the ref unless it is key-shaped (sk-* or longer than a ref name would plausibly be), in which case it is the key for the default ref, and a second positional is always the key. An inline key warns that it is now in the shell history, since the hidden prompt leaves no trace. Extra arguments are refused instead of ignored. The Windows dispatch already read the first positional correctly, so it only gains the inline form. The shipped default preset moves to the models asked for: DeepSeek 4.1 Flash on opus, GLM Flash on sonnet. Existing installs keep their own presets - the installer never overwrites one without --force - so an upgrade needs `claude-mode preset set default opus ...` and the same for sonnet, or a --force install. Verified in the bash 3.2 container: ref+key stores under the ref, a bare key stores under openrouter, an empty prompt aborts, a custom ref still works, the stored value is byte-identical, and four positionals are refused. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -186,7 +186,7 @@ claude-mode preset set <name> <tier> <model-id>
|
||||
claude-mode preset all <name> <model-id> point every tier at one model
|
||||
claude-mode preset rm <name>
|
||||
|
||||
claude-mode set-key [ref] store a key (hidden prompt, DPAPI)
|
||||
claude-mode set-key [ref] [key] store a key (hidden prompt; [key] for scripts)
|
||||
claude-mode models [filter] models available from the active provider
|
||||
claude-mode doctor verify auth, endpoint, model ids, stray env vars
|
||||
|
||||
@@ -209,7 +209,7 @@ menu to read before the thing you asked for happens. Build more with
|
||||
|
||||
| preset | provider | opus | sonnet | haiku | fable |
|
||||
|---|---|---|---|---|---|
|
||||
| `default` | openrouter | `z-ai/glm-5.3-flash` | `deepseek/deepseek-v4-flash-0731` | `openrouter/free` | `z-ai/glm-5.3` |
|
||||
| `default` | openrouter | `deepseek/deepseek-v4.1-flash` | `z-ai/glm-5.3-flash` | `openrouter/free` | `z-ai/glm-5.3` |
|
||||
| `zai` | zai | `glm-5.3` | `glm-5.3` | `glm-4.7` | `glm-5.3` |
|
||||
| `lmstudio` | lmstudio | whatever setup finds on your server (all tiers) | | | |
|
||||
|
||||
|
||||
+22
-4
@@ -181,7 +181,7 @@ claude-mode - switch Claude Code between Anthropic, OpenRouter, Z.AI, LM Studio
|
||||
point every tier at one model
|
||||
claude-mode preset rm <name>
|
||||
|
||||
claude-mode set-key [ref] store an API key (hidden prompt, DPAPI-encrypted)
|
||||
claude-mode set-key [ref] [key] store an API key (hidden prompt, DPAPI-encrypted)
|
||||
claude-mode models [filter] models available from the active provider
|
||||
claude-mode doctor verify auth, endpoint, model ids, stray env vars
|
||||
claude-mode repair [--all] strip [1m] tags from cached model ids
|
||||
@@ -375,11 +375,16 @@ function ConvertFrom-SecureStringPlain {
|
||||
}
|
||||
|
||||
function Set-VaultKey {
|
||||
param([string] $Ref)
|
||||
param([string] $Ref, [string] $Key = '')
|
||||
Initialize-Root
|
||||
if ($Key) {
|
||||
Write-Warn2 'the key was given on the command line, so it is in this shell history - the hidden prompt leaves no trace'
|
||||
$plain = $Key
|
||||
} else {
|
||||
Write-Host "Paste the API key for ref '$Ref' (input hidden):"
|
||||
$secure = Read-Host -AsSecureString
|
||||
$plain = ConvertFrom-SecureStringPlain $secure
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($plain)) { throw 'empty key, aborted' }
|
||||
|
||||
# A hidden prompt will happily swallow a mis-paste. Guard the two shapes that
|
||||
@@ -392,7 +397,8 @@ function Set-VaultKey {
|
||||
throw "that value is a claude-mode command, not an API key. Nothing was stored."
|
||||
}
|
||||
if ($plain.Length -lt 16) {
|
||||
Write-Warn2 "that key is only $($plain.Length) characters - unusually short. Storing anyway."
|
||||
$unit = if ($plain.Length -eq 1) { 'character' } else { 'characters' }
|
||||
Write-Warn2 "that key is only $($plain.Length) $unit - unusually short. Storing anyway."
|
||||
}
|
||||
if ($Ref -eq 'openrouter' -and $plain -notlike 'sk-or-*') {
|
||||
Write-Warn2 "key does not start with 'sk-or-' - storing anyway"
|
||||
@@ -2198,7 +2204,19 @@ try {
|
||||
}
|
||||
'presets' { Invoke-Presets }
|
||||
'preset' { Invoke-PresetCmd -Argv $Rest }
|
||||
'set-key' { Set-VaultKey -Ref $(if ($Rest -and $Rest.Count -ge 1) { $Rest[0] } else { 'openrouter' }) }
|
||||
'set-key' {
|
||||
# `set-key [ref] [key]`. The key is normally typed at the hidden
|
||||
# prompt; anything key-shaped in the ref position is taken as the
|
||||
# key for the default ref, which is the easy mistake to make.
|
||||
$kRef = ''; $kKey = ''
|
||||
foreach ($a in $Rest) {
|
||||
if (-not $kRef -and -not ($a -like 'sk-*' -or $a.Length -gt 24)) { $kRef = $a; continue }
|
||||
if (-not $kKey) { $kKey = $a; continue }
|
||||
throw "unexpected extra argument '$a'"
|
||||
}
|
||||
if (-not $kRef) { $kRef = 'openrouter' }
|
||||
Set-VaultKey -Ref $kRef -Key $kKey
|
||||
}
|
||||
'models' { Invoke-Models -Filter $(if ($Rest -and $Rest.Count -ge 1) { $Rest[0] } else { $null }) }
|
||||
'doctor' { Invoke-Doctor }
|
||||
'repair' { [void](Invoke-Repair -All:([bool]($Rest -and ($Rest -contains '--all')))) }
|
||||
|
||||
+31
-6
@@ -269,7 +269,7 @@ claude-mode - switch Claude Code between Anthropic, OpenRouter, Z.AI, LM Studio
|
||||
claude-mode preset auth <name> none|key [ref] whether that server needs a key
|
||||
claude-mode preset rm <name>
|
||||
|
||||
claude-mode set-key [ref] [--terminal] store an API key (hidden prompt)
|
||||
claude-mode set-key [ref] [key] store an API key (hidden prompt; key for scripts)
|
||||
claude-mode models [filter] models available from the active provider
|
||||
claude-mode doctor verify auth, endpoint, model ids, env
|
||||
claude-mode repair strip [1m] tags from cached model ids
|
||||
@@ -1242,15 +1242,20 @@ cmd_models() {
|
||||
}
|
||||
|
||||
cmd_set_key() {
|
||||
local ref="${1:-openrouter}" secret
|
||||
local ref="${1:-openrouter}" inline="${2:-}" secret unit
|
||||
init_root
|
||||
printf ' storage backend: %s\n' "$(cm_vault_backend_label)"
|
||||
if [ "$(cm_vault_backend)" = "file" ]; then
|
||||
warn 'no keyring available - the key will be stored in a 0600 file, NOT encrypted.'
|
||||
warn 'install libsecret-tools (secret-tool) or pass for encrypted storage.'
|
||||
fi
|
||||
if [ -n "$inline" ]; then
|
||||
secret="$inline"
|
||||
warn 'the key was given on the command line, so it is in this shell history - the hidden prompt leaves no trace'
|
||||
else
|
||||
printf ' paste the API key for ref '\''%s'\'' (input hidden): ' "$ref"
|
||||
IFS= read -rs secret; printf '\n'
|
||||
fi
|
||||
[ -n "$secret" ] || { err 'empty key, aborted'; return 1; }
|
||||
|
||||
# A hidden prompt will happily swallow a mis-paste. Guard the two shapes that
|
||||
@@ -1260,7 +1265,10 @@ cmd_set_key() {
|
||||
*[[:space:]]*) err 'that value contains whitespace, so it is not an API key (a pasted command line?). Nothing was stored.'; return 1 ;;
|
||||
claude-mode*) err 'that value is a claude-mode command, not an API key. Nothing was stored.'; return 1 ;;
|
||||
esac
|
||||
[ "${#secret}" -lt 16 ] && warn "that key is only ${#secret} characters - unusually short. Storing anyway."
|
||||
if [ "${#secret}" -lt 16 ]; then
|
||||
unit=characters; [ "${#secret}" -eq 1 ] && unit=character
|
||||
warn "that key is only ${#secret} $unit - unusually short. Storing anyway."
|
||||
fi
|
||||
if [ "$ref" = "openrouter" ] && [ "${secret#sk-or-}" = "$secret" ]; then
|
||||
warn "key does not start with 'sk-or-' - storing anyway"
|
||||
fi
|
||||
@@ -2432,13 +2440,30 @@ case "$cmd" in
|
||||
# --terminal is for callers with no stdin to offer: the bar
|
||||
# widget cannot host a hidden password prompt, so it asks the
|
||||
# terminal to host one instead.
|
||||
_ref=openrouter; _term=0
|
||||
# `set-key [ref] [key]`. The key is normally typed at the
|
||||
# hidden prompt; a second word is taken as the key itself, for
|
||||
# scripting. Anything key-shaped in the *ref* position is also
|
||||
# taken as the key for the default ref - it used to become the
|
||||
# ref name instead, which stored the wrong thing under a ref
|
||||
# nobody would ever look up, and only failed later and quietly.
|
||||
_ref=''; _term=0; _secret=''
|
||||
for _a in "$@"; do
|
||||
case "$_a" in
|
||||
--terminal) _term=1 ;;
|
||||
*) _ref="$_a" ;;
|
||||
*)
|
||||
if [ -z "$_ref" ]; then
|
||||
case "$_a" in
|
||||
sk-*|sk_*) _secret="$_a" ;;
|
||||
*) if [ "${#_a}" -gt 24 ]; then _secret="$_a"; else _ref="$_a"; fi ;;
|
||||
esac
|
||||
elif [ -z "$_secret" ]; then
|
||||
_secret="$_a"
|
||||
else
|
||||
err "unexpected extra argument '$_a'"; exit 1
|
||||
fi ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$_ref" ] || _ref=openrouter
|
||||
if [ "$_term" -eq 1 ]; then
|
||||
_t="$(cm_terminal_cmd)"
|
||||
setsid nohup "$_t" -e bash -lc \
|
||||
@@ -2446,7 +2471,7 @@ case "$cmd" in
|
||||
>/dev/null 2>&1 &
|
||||
ok "opened $_t to store the '$_ref' key"
|
||||
else
|
||||
cmd_set_key "$_ref"
|
||||
cmd_set_key "$_ref" "$_secret"
|
||||
fi ;;
|
||||
models) cmd_models "${1:-}" ;;
|
||||
doctor) cmd_doctor ;;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"provider": "openrouter",
|
||||
"description": "Daily driver. Flash-class models on the hot tiers, GLM on fable.",
|
||||
"description": "Daily driver. DeepSeek 4.1 Flash on opus, GLM Flash on sonnet, GLM on fable.",
|
||||
"baseUrl": "https://openrouter.ai/api",
|
||||
"auth": {
|
||||
"mode": "vault",
|
||||
"keyRef": "openrouter"
|
||||
},
|
||||
"models": {
|
||||
"opus": "z-ai/glm-5.3-flash",
|
||||
"sonnet": "deepseek/deepseek-v4-flash-0731",
|
||||
"opus": "deepseek/deepseek-v4.1-flash",
|
||||
"sonnet": "z-ai/glm-5.3-flash",
|
||||
"haiku": "openrouter/free",
|
||||
"fable": "z-ai/glm-5.3"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user