Quote the apiKeyHelper path, and say why the helper failed

apiKeyHelper is a shell command line, not a path, so the raw value written
into settings.json was split at the first space. A Windows profile named
"Mohammed Ahmed" produced an attempt to run C:\Users\Mohammed, surfacing as
"your apiKeyHelper script is failing" with nothing to go on. Quote the value
when it contains anything a shell cares about, and leave it bare otherwise so
no existing settings.json churns on the next switch. The POSIX port had the
same bug against a /Users/First Last home; shlex.quote has exactly the wanted
"leave ordinary paths alone" behaviour.

doctor could not see any of this. It quoted the path itself before running it,
so it exercised a command line Claude Code never uses and passed while the
real one failed. It now reads the string out of settings.json, reports it when
it is not what a switch would write, and runs that string through a shell.

The helper itself exited 1 in silence on four distinct faults - no state, no
preset, no key, undecryptable key - collapsing them into one indistinguishable
message. Each now names itself on stderr, which is what /status displays. The
DPAPI case says what it actually means: a key stored by a different Windows
account than the one Claude Code runs as. Success paths stay silent, so stdout
still carries the key and nothing else.

Also make install.ps1 survive a Restricted execution policy: piped through
iex it is not subject to the policy, but invoking the installed script for the
key prompt is, which is where a fresh install died. Set Process scope for the
install, offer to set CurrentUser to RemoteSigned, and clear the
mark-of-the-web that Expand-Archive can leave on the extracted scripts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
smoido
2026-09-04 00:49:50 +03:00
co-authored by Claude Opus 5
parent b32651fe00
commit b60c00450c
8 changed files with 188 additions and 27 deletions
+6 -1
View File
@@ -20,6 +20,7 @@ import glob
import json
import os
import re
import shlex
import sys
# Matches both the qualified gateway id (anthropic/claude-opus-5) and the bare
@@ -156,7 +157,11 @@ def cmd_apply(argv):
if auth.get("mode") == "vault":
if not helper:
raise SystemExit("apply: vault auth needs the key-helper path")
settings["apiKeyHelper"] = helper
# Claude Code runs this value as a shell command line, so a $HOME
# containing a space (common on macOS) has to arrive quoted or the
# shell splits it and tries to execute the first word. shlex.quote
# leaves an ordinary path untouched, so nothing churns.
settings["apiKeyHelper"] = shlex.quote(helper)
else:
block["ANTHROPIC_AUTH_TOKEN"] = str(auth.get("token") or "lmstudio")