fix(profiles): clone auth.json so OAuth credentials carry to cloned profiles (#51719)

Selective --clone / --clone-from / --clone-config copied .env but not
auth.json, silently dropping the credential pool — including OAuth tokens
(Anthropic `claude /login`, Codex, xAI) that never land in .env. A profile
cloned from an OAuth-authenticated default therefore resolved a different
provider (or none) than the source under provider: auto. --clone-all already
carried auth.json via the full copytree; only the selective path missed it.

Add auth.json to _CLONE_CONFIG_FILES and tighten it to 0o600 after copy,
matching .env semantics.
This commit is contained in:
Teknium 2026-06-23 23:44:34 -07:00 committed by GitHub
parent 050bd01b7b
commit f504aecffe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 5 deletions

View file

@ -53,9 +53,21 @@ _PROFILE_DIRS = [
]
# Files copied during --clone (if they exist in the source)
#
# auth.json carries the per-profile credential pool — including OAuth tokens
# (Anthropic `claude /login`, Codex, xAI) that never land in .env. Cloning
# .env but not auth.json silently dropped those credentials from the clone,
# so a profile cloned from an OAuth-authenticated default resolved a
# different provider (or none) than the source. The global-root fallback in
# read_credential_pool only masks this while the clone's HERMES_HOME still
# resolves to the same default root and the user hasn't run `hermes auth add`
# locally. Copying it here makes selective --clone match `.env` semantics and
# the full --clone-all copytree (which already carries auth.json). Both are
# credential files and get tightened to owner-only (0o600) after copy.
_CLONE_CONFIG_FILES = [
"config.yaml",
".env",
"auth.json",
"SOUL.md",
]
@ -938,11 +950,12 @@ def create_profile(
if src.exists():
dst = profile_dir / filename
shutil.copy2(src, dst)
# Tighten .env to owner-only after copy. shutil.copy2
# preserves source mode bits, but if the source's .env
# was loose (host umask 0o022 leaving 0o644), tighten
# explicitly so the clone doesn't inherit weak perms.
if filename == ".env":
# Tighten credential files to owner-only after copy.
# shutil.copy2 preserves source mode bits, but if the
# source's .env / auth.json was loose (host umask 0o022
# leaving 0o644), tighten explicitly so the clone doesn't
# inherit weak perms.
if filename in {".env", "auth.json"}:
try:
os.chmod(str(dst), 0o600)
except OSError: