mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 08:03:12 +00:00
Route Claude panes through a per-account CLAUDE_CONFIG_DIR
Mirrors the CODEX_HOME spawn-time mechanism: ORCA_CLAUDE_CONFIG_DIR joins the positive overlay allowlist and each shell wrapper re-exports CLAUDE_CONFIG_DIR after user rc files, so a hand-typed claude inherits the selected account. WIP recovered from an interrupted worker; per-account home creation and the shared-store migration are not implemented yet.
This commit is contained in:
@@ -24,7 +24,7 @@ export function applyClaudeEnvPatch(
|
||||
}
|
||||
}
|
||||
|
||||
if (patch.CLAUDE_CONFIG_DIR) {
|
||||
if (patch.CLAUDE_CONFIG_DIR && !baseEnv.CLAUDE_CONFIG_DIR) {
|
||||
baseEnv.CLAUDE_CONFIG_DIR = patch.CLAUDE_CONFIG_DIR
|
||||
}
|
||||
if (patch.ANTHROPIC_CUSTOM_HEADERS !== undefined) {
|
||||
|
||||
@@ -19,7 +19,15 @@ export class ClaudeRuntimeAuthService extends ClaudeRuntimeAuthSync {
|
||||
target?: ClaudeAccountSelectionTarget
|
||||
): Promise<ClaudeRuntimeAuthPreparation> {
|
||||
const effectiveTarget = target ?? this.getDefaultAccountSelectionTarget()
|
||||
await this.syncForCurrentSelection(effectiveTarget)
|
||||
const settings = this.store.getSettings()
|
||||
const selectedId = getSelectedClaudeAccountIdForTarget(settings, effectiveTarget)
|
||||
const selected = selectedId
|
||||
? settings.claudeManagedAccounts.find((account) => account.id === selectedId)
|
||||
: null
|
||||
// Isolated accounts are already Claude's runtime store; never copy them into ~/.claude.
|
||||
if (!selected || selected.managedAuthRuntime === 'wsl') {
|
||||
await this.syncForCurrentSelection(effectiveTarget)
|
||||
}
|
||||
return this.getPreparation(effectiveTarget)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../runtime-selection'
|
||||
import { ClaudeRuntimeAuthSnapshotRestore } from './runtime-auth-snapshot-restore'
|
||||
import type { ClaudeRuntimeAuthPreparation } from './runtime-auth-types'
|
||||
import { resolveOwnedClaudeManagedAuthPath } from '../managed-auth-path'
|
||||
|
||||
export class ClaudeRuntimeAuthPreparationService extends ClaudeRuntimeAuthSnapshotRestore {
|
||||
protected getPreparation(target?: ClaudeAccountSelectionTarget): ClaudeRuntimeAuthPreparation {
|
||||
@@ -63,6 +64,24 @@ export class ClaudeRuntimeAuthPreparationService extends ClaudeRuntimeAuthSnapsh
|
||||
provenance: `wsl:${normalizeClaudeAccountSelectionTarget(normalizedTarget).wslDistro ?? '__default__'}:system`
|
||||
}
|
||||
}
|
||||
if (activeAccount?.managedAuthRuntime !== 'wsl' && activeAccount) {
|
||||
const managedPath = resolveOwnedClaudeManagedAuthPath(
|
||||
activeAccount.id,
|
||||
activeAccount.managedAuthPath,
|
||||
{ adoptLegacyMarker: true }
|
||||
)
|
||||
if (managedPath) {
|
||||
return {
|
||||
configDir: managedPath,
|
||||
runtime: 'host',
|
||||
wslDistro: null,
|
||||
wslLinuxConfigDir: null,
|
||||
envPatch: { CLAUDE_CONFIG_DIR: managedPath },
|
||||
stripAuthEnv: true,
|
||||
provenance: `managed:${activeAccount.id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
configDir: paths.configDir,
|
||||
runtime: 'host',
|
||||
|
||||
@@ -23,6 +23,13 @@ export class ClaudeRuntimeAuthSync extends ClaudeRuntimeAuthPreparationService {
|
||||
this.lastSyncedAccountId
|
||||
)
|
||||
this.managedRefreshDeferredByLivePtyAccountId = null
|
||||
// Per-account Claude config dirs are self-contained; syncing them into the
|
||||
// shared ~/.claude would recreate the stale-sibling race this isolation removes.
|
||||
if (activeAccount?.managedAuthRuntime === 'host') {
|
||||
this.clearLastWrittenRuntimeState()
|
||||
this.lastSyncedAccountId = activeAccount.id
|
||||
return
|
||||
}
|
||||
const previousManagedCredentialsJson = previousAccount
|
||||
? await this.readManagedCredentials(previousAccount)
|
||||
: null
|
||||
|
||||
@@ -14,6 +14,7 @@ export function getDaemonZshWrapperSpec(): ZshStartupHookSpec {
|
||||
agentTeamsPath: true,
|
||||
remoteCliBinDir: false,
|
||||
codexHome: true,
|
||||
claudeConfigDir: true,
|
||||
codexLaunchPreflight: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ const POWERSHELL_OSC133_BOOTSTRAP = `# Orca OSC 133 shell integration for PowerS
|
||||
if ($env:ORCA_OPENCODE_CONFIG_DIR) { $env:OPENCODE_CONFIG_DIR = $env:ORCA_OPENCODE_CONFIG_DIR }
|
||||
if ($env:ORCA_MIMOCODE_HOME) { $env:MIMOCODE_HOME = $env:ORCA_MIMOCODE_HOME }
|
||||
if ($env:ORCA_CODEX_HOME) { $env:CODEX_HOME = $env:ORCA_CODEX_HOME }
|
||||
if ($env:ORCA_CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR = $env:ORCA_CLAUDE_CONFIG_DIR }
|
||||
|
||||
if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage" -and
|
||||
((-not (Test-Path variable:global:__OrcaOsc133State)) -or
|
||||
|
||||
@@ -52,6 +52,8 @@ __orca_restore_agent_teams_path
|
||||
${getPosixOmpShellWrapper()}
|
||||
# Why: Codex must keep using Orca's runtime CODEX_HOME after profile scripts.
|
||||
[[ -n "\${ORCA_CODEX_HOME:-}" ]] && export CODEX_HOME="\${ORCA_CODEX_HOME}"
|
||||
# Why: Claude must keep using Orca's runtime config directory after profile scripts.
|
||||
[[ -n "\${ORCA_CLAUDE_CONFIG_DIR:-}" ]] && export CLAUDE_CONFIG_DIR="\${ORCA_CLAUDE_CONFIG_DIR}"
|
||||
${getPosixCodexShellLaunchPreflight()}
|
||||
# Why: emit OSC 133 C/D so terminal-command-lifecycle can drop stale agent
|
||||
# status when the foreground command (e.g. an interrupted Claude/Codex CLI)
|
||||
|
||||
@@ -27,6 +27,7 @@ export function getLocalZshWrapperSpec(): ZshStartupHookSpec {
|
||||
agentTeamsPath: true,
|
||||
remoteCliBinDir: false,
|
||||
codexHome: true,
|
||||
claudeConfigDir: true,
|
||||
codexLaunchPreflight: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,20 @@ export function finalizeWindowsLocalPtySpawnEnvironment(args: {
|
||||
}
|
||||
if (env.CLAUDE_CONFIG_DIR) {
|
||||
// Why: managed WSL Claude passes a Linux CLAUDE_CONFIG_DIR through wsl.exe; non-default vars need WSLENV import.
|
||||
addWslEnvKeys(env, ['CLAUDE_CONFIG_DIR'])
|
||||
const claudeConfigWslInfo = parseWslPath(env.CLAUDE_CONFIG_DIR)
|
||||
if (claudeConfigWslInfo) {
|
||||
if (plan.launchWslDistro && plan.launchWslDistro !== claudeConfigWslInfo.distro) {
|
||||
delete env.CLAUDE_CONFIG_DIR
|
||||
delete env.ORCA_CLAUDE_CONFIG_DIR
|
||||
} else {
|
||||
env.CLAUDE_CONFIG_DIR = claudeConfigWslInfo.linuxPath
|
||||
env.ORCA_CLAUDE_CONFIG_DIR = claudeConfigWslInfo.linuxPath
|
||||
addWslEnvKeys(env, ['CLAUDE_CONFIG_DIR', 'ORCA_CLAUDE_CONFIG_DIR'])
|
||||
}
|
||||
} else {
|
||||
env.ORCA_CLAUDE_CONFIG_DIR ??= env.CLAUDE_CONFIG_DIR
|
||||
addWslEnvKeys(env, ['CLAUDE_CONFIG_DIR', 'ORCA_CLAUDE_CONFIG_DIR'])
|
||||
}
|
||||
}
|
||||
if (env[ORCA_HERMES_STARTUP_QUERY_ENV] !== undefined) {
|
||||
// Why: wsl.exe drops custom Windows env vars; the startup wrapper needs this imported inside WSL.
|
||||
@@ -65,6 +78,10 @@ export function finalizeWindowsLocalPtySpawnEnvironment(args: {
|
||||
delete env.ORCA_CODEX_HOME
|
||||
}
|
||||
|
||||
if (pathWin32.basename(plan.shellPath).toLowerCase() !== 'wsl.exe' && env.CLAUDE_CONFIG_DIR) {
|
||||
env.ORCA_CLAUDE_CONFIG_DIR ??= env.CLAUDE_CONFIG_DIR
|
||||
}
|
||||
|
||||
const shellBasename = pathWin32.basename(plan.shellPath).toLowerCase()
|
||||
const codexLaunchPreflightCommand = env.ORCA_CODEX_LAUNCH_PREFLIGHT
|
||||
if (
|
||||
|
||||
@@ -30,6 +30,7 @@ const OVERLAY_ENV_KEYS = [
|
||||
'ORCA_MIMOCODE_HOME',
|
||||
'ORCA_OMP_STATUS_EXTENSION',
|
||||
'ORCA_CODEX_HOME',
|
||||
'ORCA_CLAUDE_CONFIG_DIR',
|
||||
'ORCA_AGENT_TEAMS_SHIM_DIR',
|
||||
'ORCA_REMOTE_CLI_BIN_DIR'
|
||||
] as const
|
||||
|
||||
@@ -46,6 +46,8 @@ export type ZshWrapperRestoreSpec = {
|
||||
codexHome: boolean
|
||||
/** The `codex()` wrapper that runs Orca's launch preflight. */
|
||||
codexLaunchPreflight: boolean
|
||||
/** Orca's runtime Claude config directory. */
|
||||
claudeConfigDir: boolean
|
||||
}
|
||||
|
||||
export type ZshStartupHookSpec = {
|
||||
@@ -75,6 +77,8 @@ const MIMOCODE_HOME_RESTORE = `[[ -n "\${ORCA_MIMOCODE_HOME:-}" ]] && export MIM
|
||||
const REMOTE_CLI_BIN_DIR_RESTORE = `[[ -n "\${ORCA_REMOTE_CLI_BIN_DIR:-}" ]] && case ":$PATH:" in *:"\${ORCA_REMOTE_CLI_BIN_DIR}":*) ;; *) export PATH="\${ORCA_REMOTE_CLI_BIN_DIR}:$PATH" ;; esac`
|
||||
const CODEX_HOME_RESTORE = `# Why: Codex must keep using Orca's runtime CODEX_HOME after rc files.
|
||||
[[ -n "\${ORCA_CODEX_HOME:-}" ]] && export CODEX_HOME="\${ORCA_CODEX_HOME}"`
|
||||
const CLAUDE_CONFIG_DIR_RESTORE = `# Why: Claude must keep using Orca's runtime config directory after rc files.
|
||||
[[ -n "\${ORCA_CLAUDE_CONFIG_DIR:-}" ]] && export CLAUDE_CONFIG_DIR="\${ORCA_CLAUDE_CONFIG_DIR}"`
|
||||
|
||||
/**
|
||||
* The OSC 133 hooks, defined at top level so their bodies are parsed before the
|
||||
@@ -125,6 +129,7 @@ function getOverlayRestoreBlocks(spec: ZshStartupHookSpec): (string | null)[] {
|
||||
spec.restores.remoteCliBinDir ? REMOTE_CLI_BIN_DIR_RESTORE : null,
|
||||
getPosixOmpShellWrapper(),
|
||||
spec.restores.codexHome ? CODEX_HOME_RESTORE : null,
|
||||
spec.restores.claudeConfigDir ? CLAUDE_CONFIG_DIR_RESTORE : null,
|
||||
spec.restores.codexLaunchPreflight ? getPosixCodexShellLaunchPreflight() : null
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user