resolve the claude cli instead of hardcoding /usr/local/bin/claude
the path was pinned to /usr/local/bin/claude for the bwrap-sandboxed architecture: the jail ro-bound /usr and saw nothing else, so the installer's real target (~/.local/bin/claude) had to be symlinked somewhere the sandbox could reach. that sandbox is gone, and the constant outlived it — the sidecar could not run anywhere the symlink was absent. a stock macos host has no /usr/local/bin at all. resolve at module load instead: CLAUDE_BIN pins it explicitly, else whatever is on PATH, else the locations the installer writes to. same shape as OPENCODE_BIN in the opencode sidecar. the resolved path is logged at startup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { existsSync } from 'node:fs';
|
||||
import { homedir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { query, type Query } from '@anthropic-ai/claude-agent-sdk';
|
||||
import type { ChatEvent } from '../../api/chat/types';
|
||||
@@ -7,9 +9,27 @@ import { createParseState, processMessage } from './stream-parser';
|
||||
|
||||
const SEND_TIMEOUT_MS = 30 * 60 * 1000;
|
||||
|
||||
// Use /usr/local/bin/claude so it's visible inside bwrap sandbox (which ro-binds /usr).
|
||||
// The actual binary lives at ~/.local/bin/claude, symlinked from /usr/local/bin/claude.
|
||||
const CLAUDE_BIN = '/usr/local/bin/claude';
|
||||
// Where the Claude Code CLI lives. This was hardcoded to /usr/local/bin/claude, which dated from the
|
||||
// bwrap-sandboxed architecture: the jail ro-bound /usr and saw nothing else, so the installer's real
|
||||
// target (~/.local/bin/claude) had to be symlinked into a path the sandbox could reach. That sandbox
|
||||
// is gone, and the hardcoded path made the sidecar unrunnable anywhere it does not exist — a stock
|
||||
// macOS host has no /usr/local/bin at all.
|
||||
//
|
||||
// Resolution order mirrors OPENCODE_BIN in the opencode sidecar: an explicit pin, then PATH, then the
|
||||
// locations Anthropic's installer actually writes to.
|
||||
function resolveClaudeBin(): string {
|
||||
const pinned = process.env.CLAUDE_BIN;
|
||||
if (pinned) return pinned;
|
||||
|
||||
const onPath = Bun.which('claude');
|
||||
if (onPath) return onPath;
|
||||
|
||||
const candidates = [join(homedir(), '.local', 'bin', 'claude'), '/usr/local/bin/claude', '/opt/homebrew/bin/claude'];
|
||||
return candidates.find((candidate) => existsSync(candidate)) ?? 'claude';
|
||||
}
|
||||
|
||||
const CLAUDE_BIN = resolveClaudeBin();
|
||||
console.log(`[claude] CLI resolved to ${CLAUDE_BIN}`);
|
||||
|
||||
// Capture original HOME before user-instance overrides it
|
||||
const HOST_HOME = process.env.HOME!;
|
||||
|
||||
Reference in New Issue
Block a user