import { existsSync, statSync } from 'node:fs'; import path from 'node:path'; import { getErrorMessage } from '@shared/utils/errorHandling'; import { applyOpenCodeRuntimeBinaryEnv, OPENCODE_LEGACY_BINARY_PATH_ENV, OPENCODE_RUNTIME_BINARY_PATH_ENV, } from './openCodeRuntimeBinaryEnv'; export interface EnsureOpenCodeBridgeRuntimeBinaryEnvOptions { targetEnv: NodeJS.ProcessEnv; bridgeEnv?: NodeJS.ProcessEnv; resolveVerifiedOpenCodeRuntimeBinaryPath: () => Promise; onWarning?: (message: string) => void; } function resolveExistingFilePath(filePath: string): string | null { const resolvedPath = path.resolve(filePath.trim()); if (!existsSync(resolvedPath)) { return null; } try { return statSync(resolvedPath).isFile() ? resolvedPath : null; } catch { return null; } } function getOpenCodeRuntimeBinaryEnvValues(env: NodeJS.ProcessEnv): string[] { return [ env[OPENCODE_RUNTIME_BINARY_PATH_ENV]?.trim(), env[OPENCODE_LEGACY_BINARY_PATH_ENV]?.trim(), ].filter((value): value is string => Boolean(value)); } function resolveExistingOpenCodeRuntimeBinaryEnvPath(env: NodeJS.ProcessEnv): string | null { for (const value of getOpenCodeRuntimeBinaryEnvValues(env)) { const resolvedPath = resolveExistingFilePath(value); if (resolvedPath) { return resolvedPath; } } return null; } function clearOpenCodeRuntimeBinaryEnvValues( env: NodeJS.ProcessEnv, invalidValues: Set ): void { for (const key of [OPENCODE_RUNTIME_BINARY_PATH_ENV, OPENCODE_LEGACY_BINARY_PATH_ENV]) { const value = env[key]?.trim(); if (value && invalidValues.has(value)) { delete env[key]; } } } export async function ensureOpenCodeBridgeRuntimeBinaryEnv({ targetEnv, bridgeEnv = targetEnv, resolveVerifiedOpenCodeRuntimeBinaryPath, onWarning, }: EnsureOpenCodeBridgeRuntimeBinaryEnvOptions): Promise { if ( targetEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV]?.trim() || targetEnv[OPENCODE_LEGACY_BINARY_PATH_ENV]?.trim() ) { const existingBinaryPath = resolveExistingOpenCodeRuntimeBinaryEnvPath(targetEnv); if (!existingBinaryPath) { const invalidValues = new Set(getOpenCodeRuntimeBinaryEnvValues(targetEnv)); clearOpenCodeRuntimeBinaryEnvValues(targetEnv, invalidValues); if (targetEnv !== bridgeEnv) { clearOpenCodeRuntimeBinaryEnvValues(bridgeEnv, invalidValues); } } else { targetEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV] = existingBinaryPath; targetEnv[OPENCODE_LEGACY_BINARY_PATH_ENV] = existingBinaryPath; applyOpenCodeRuntimeBinaryEnv(targetEnv, existingBinaryPath); return; } } if ( targetEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV]?.trim() || targetEnv[OPENCODE_LEGACY_BINARY_PATH_ENV]?.trim() ) { applyOpenCodeRuntimeBinaryEnv(targetEnv, null); return; } try { const openCodeBinary = await resolveVerifiedOpenCodeRuntimeBinaryPath(); applyOpenCodeRuntimeBinaryEnv(targetEnv, openCodeBinary); if ( targetEnv !== bridgeEnv && targetEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV] && !bridgeEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV] ) { applyOpenCodeRuntimeBinaryEnv(bridgeEnv, targetEnv[OPENCODE_RUNTIME_BINARY_PATH_ENV]); } } catch (error) { onWarning?.(`[OpenCode] Runtime adapter OpenCode binary unresolved: ${getErrorMessage(error)}`); } }