fix super admin claude: host cwd, host mcp config paths

Capture HOST_HOME before user-instance overrides process.env.HOME so
Super Admin spawns claude in /home/pastilhas. Generate separate MCP
configs for sandbox (sandbox paths) and host (real filesystem paths),
pick based on role at spawn time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 01:29:14 +00:00
co-authored by Claude Opus 4.6
parent 29229c8cb7
commit 8264e7b995
3 changed files with 50 additions and 25 deletions
+17 -11
View File
@@ -12,6 +12,8 @@ const SEND_TIMEOUT_MS = 5 * 60 * 1000;
// The actual binary lives at ~/.local/bin/claude, symlinked from /usr/local/bin/claude.
const CLAUDE_BIN = '/usr/local/bin/claude';
// Capture original HOME before user-instance overrides it
const HOST_HOME = process.env.HOME!;
const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data');
// Build full bwrap sandbox args for Claude (prefix + Anthropic env + runuser suffix)
@@ -28,11 +30,13 @@ function buildSandboxArgs(email: string): string[] {
// Active streaming processes
const activeProcs = new Map<string, Subprocess>();
// MCP config path, set by user-instance at startup
let mcpConfigPath: string | undefined;
// MCP config paths, set by user-instance at startup
let mcpSandboxPath: string | undefined; // path inside bwrap sandbox (/data/...)
let mcpHostPath: string | undefined; // path on the host filesystem
export function setMcpConfigPath(path: string): void {
mcpConfigPath = path;
export function setMcpConfigPath(sandboxPath: string, hostPath: string): void {
mcpSandboxPath = sandboxPath;
mcpHostPath = hostPath;
}
// ── Blocking send ──
@@ -60,7 +64,9 @@ export async function spawnClaude(params: ClaudeSpawnParams): Promise<ClaudeCode
'json',
];
if (mcpConfigPath) claudeArgs.push('--mcp-config', mcpConfigPath);
const isSuperAdmin = params.role === 'Super Admin';
const mcpConfig = isSuperAdmin ? mcpHostPath : mcpSandboxPath;
if (mcpConfig) claudeArgs.push('--mcp-config', mcpConfig);
const subModel = params.model?.split('/')[1];
if (subModel) claudeArgs.push('--model', subModel);
@@ -69,9 +75,8 @@ export async function spawnClaude(params: ClaudeSpawnParams): Promise<ClaudeCode
claudeArgs.push('--resume', existingSession);
}
const isSuperAdmin = params.role === 'Super Admin';
const spawnCmd = isSuperAdmin ? claudeArgs : [...buildSandboxArgs(email), ...claudeArgs];
const spawnCwd = isSuperAdmin ? (process.env.HOME ?? join(DATA_PATH, email, 'home')) : undefined;
const spawnCwd = isSuperAdmin ? HOST_HOME : undefined;
const proc = Bun.spawn(spawnCmd, {
stdin: 'pipe',
@@ -155,7 +160,10 @@ export async function spawnClaudeStreaming(
'--include-partial-messages',
];
if (mcpConfigPath) claudeArgs.push('--mcp-config', mcpConfigPath);
const { CLAUDECODE: _, ...cleanEnv } = process.env;
const isSuperAdmin = params.role === 'Super Admin';
const mcpConfig = isSuperAdmin ? mcpHostPath : mcpSandboxPath;
if (mcpConfig) claudeArgs.push('--mcp-config', mcpConfig);
const subModel = params.model?.split('/')[1];
if (subModel) claudeArgs.push('--model', subModel);
@@ -164,10 +172,8 @@ export async function spawnClaudeStreaming(
claudeArgs.push('--resume', existingSession);
}
const { CLAUDECODE: _, ...cleanEnv } = process.env;
const isSuperAdmin = params.role === 'Super Admin';
const spawnCmd = isSuperAdmin ? claudeArgs : [...buildSandboxArgs(email), ...claudeArgs];
const spawnCwd = isSuperAdmin ? (process.env.HOME ?? join(DATA_PATH, email, 'home')) : undefined;
const spawnCwd = isSuperAdmin ? HOST_HOME : undefined;
const proc = Bun.spawn(spawnCmd, {
stdin: 'ignore',