agent-ecosystem/src/main/utils/cliEnv.ts
iliya 60d80cde70 fix(auth): avoid setting CLAUDE_CONFIG_DIR when it matches default path
On macOS, the Claude CLI uses a Keychain namespace derived from the
presence of CLAUDE_CONFIG_DIR env var. Setting it to the default
~/.claude creates a different Keychain key than when the var is absent,
causing "not logged in" errors even after successful `claude auth login`.

Only set CLAUDE_CONFIG_DIR when the user has configured a custom path
that differs from the auto-detected default.

Fixes #27
2026-03-25 12:23:57 +02:00

53 lines
1.9 KiB
TypeScript

/**
* Builds an enriched environment for Claude CLI child processes.
*
* Packaged Electron apps on macOS receive a minimal PATH (often just /usr/bin:/bin)
* and may lack USER (needed for macOS Keychain credential lookup).
* This helper merges the user's interactive-shell env (cached during startup) with
* common install locations so that `claude` and its subprocesses (node, npx, etc.)
* can find the tools they need and authenticate properly.
*/
import { buildMergedCliPath } from '@main/utils/cliPathMerge';
import { getAutoDetectedClaudeBasePath, getClaudeBasePath } from '@main/utils/pathDecoder';
import { getCachedShellEnv, getShellPreferredHome } from '@main/utils/shellEnv';
import { userInfo } from 'os';
export function buildEnrichedEnv(binaryPath?: string | null): NodeJS.ProcessEnv {
const shellEnv = getCachedShellEnv();
const home = getShellPreferredHome();
let osUsername = '';
try {
osUsername = userInfo().username;
} catch {
// userInfo() can throw in restricted environments (Docker, no passwd entry)
}
const user =
shellEnv?.USER?.trim() ||
process.env.USER?.trim() ||
process.env.USERNAME?.trim() ||
osUsername ||
'';
// Only set CLAUDE_CONFIG_DIR when the user has configured a custom path.
// Setting it to the default ~/.claude changes the macOS Keychain namespace
// that the CLI uses for OAuth credential lookup, causing "not logged in"
// even though `claude auth login` succeeded without the env var.
const configDir = getClaudeBasePath();
const isCustomConfigDir = configDir !== getAutoDetectedClaudeBasePath();
return {
...process.env,
...(shellEnv ?? {}),
HOME: home,
USERPROFILE: home,
PATH: buildMergedCliPath(binaryPath),
...(isCustomConfigDir ? { CLAUDE_CONFIG_DIR: configDir } : {}),
...(user
? {
USER: user,
LOGNAME: shellEnv?.LOGNAME?.trim() || process.env.LOGNAME?.trim() || user,
}
: {}),
};
}