fix(runners): explicitly resolve bundled Claude Code binary path

SDK 0.2.114's optional-dep resolution tries linux-x64-musl first even on
glibc systems, failing if the musl package is installed as empty shell
(directory exists but binary missing). This caused 'Claude Code native
binary not found' errors on Ubuntu hosts after upgrading SDK.

Add a platform-aware helper (resolveBundledClaudeCodeExecutable) that:
- Respects EJCLAW_CLAUDE_CLI_PATH env override for custom deployments
- Probes platform-specific bundled binary paths in order (glibc before musl)
- Throws with tried-paths list if none exist (vs SDK's silent fallback)

Wire it into agent-runner query() calls (main run + session command).

Also bumps @anthropic-ai/claude-agent-sdk ^0.2.101 -> 0.2.114 so the
bundled CLI supports the 'xhigh' effort level introduced in CLI 2.1.111.

Keeps EJClaw self-contained: no system /home/*/.local/bin/claude dependency.

Verified with CLAUDE_EFFORT=xhigh:
- 37/37 agent-runner tests pass (+ 8 new bundled-cli-path tests)
- reviewer agent-run Exit Code 0 (Duration 183s) after restart
- paired_turn_attempts failed count: 97 -> 0 after fix
- Discord reviewer delivery confirmed
This commit is contained in:
ejclaw
2026-04-20 11:09:50 +09:00
parent 34a51c95b2
commit 5de25ca0aa
5 changed files with 317 additions and 35 deletions

View File

@@ -24,6 +24,7 @@ import {
} from 'ejclaw-runners-shared';
import { fileURLToPath } from 'url';
import { resolveBundledClaudeCodeExecutable } from './bundled-cli-path.js';
import {
prependRoomRoleHeader,
type RoomRoleContext,
@@ -88,6 +89,17 @@ function log(message: string): void {
console.error(`[agent-runner] ${message}`);
}
// 번들 CLI binary를 명시해야 SDK가 musl/glibc 잘못 탐색하는 걸 우회함.
// (SDK 0.2.114의 W7() 헬퍼는 linux-x64-musl를 linux-x64보다 먼저 시도하므로
// glibc 호스트에서 musl 패키지가 빈 껍데기로 설치돼 있으면 실패한다.)
let cachedClaudeCliPath: string | null = null;
function getClaudeCliPath(): string {
if (cachedClaudeCliPath) return cachedClaudeCliPath;
cachedClaudeCliPath = resolveBundledClaudeCodeExecutable();
log(`Resolved bundled Claude Code CLI: ${cachedClaudeCliPath}`);
return cachedClaudeCliPath;
}
// Graceful shutdown: SIGTERM → abort SDK query, allowing cleanup
const agentAbortController = new AbortController();
process.on('SIGTERM', () => {
@@ -267,6 +279,8 @@ async function runQuery(
for await (const message of query({
prompt: stream,
options: {
// 번들 CLI binary를 명시해야 SDK가 musl/glibc 잘못 탐색하는 걸 우회함.
pathToClaudeCodeExecutable: getClaudeCliPath(),
cwd: effectiveCwd,
model,
thinking,
@@ -702,6 +716,8 @@ async function main(): Promise<void> {
for await (const message of query({
prompt: trimmedPrompt,
options: {
// 번들 CLI binary를 명시해야 SDK가 musl/glibc 잘못 탐색하는 걸 우회함.
pathToClaudeCodeExecutable: getClaudeCliPath(),
cwd: mainEffectiveCwd,
resume: sessionId,
systemPrompt: undefined,