diff --git a/packages/vscode/src/bridge.ts b/packages/vscode/src/bridge.ts index 82ff93fa..ab665d2a 100644 --- a/packages/vscode/src/bridge.ts +++ b/packages/vscode/src/bridge.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import * as os from 'os'; import * as path from 'path'; -import type { OpenCodeManager } from './opencode'; +import { buildAugmentedPath, type OpenCodeManager } from './opencode'; import { createAgent, createCommand, deleteAgent, deleteCommand, getAgentSources, getCommandSources, updateAgent, updateCommand, type AgentScope, type CommandScope, AGENT_SCOPE, COMMAND_SCOPE, discoverSkills, getSkillSources, createSkill, updateSkill, deleteSkill, readSkillSupportingFile, writeSkillSupportingFile, deleteSkillSupportingFile, type SkillScope, SKILL_SCOPE } from './opencodeConfig'; import { removeProviderAuth } from './opencodeAuth'; import * as gitService from './gitService'; @@ -602,6 +602,11 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo const shell = process.env.SHELL || (process.platform === 'win32' ? 'cmd.exe' : '/bin/sh'); const shellFlag = process.platform === 'win32' ? '/c' : '-c'; + const augmentedEnv = { + ...process.env, + PATH: buildAugmentedPath(), + }; + const results: Array<{ command: string; success: boolean; @@ -620,6 +625,7 @@ export async function handleBridgeMessage(message: BridgeRequest, ctx?: BridgeCo // Use async exec to not block the extension host event loop const { stdout, stderr } = await execAsync(`${shell} ${shellFlag} "${cmd.replace(/"/g, '\\"')}"`, { cwd: resolvedCwd, + env: augmentedEnv, timeout: 300000, // 5 minutes per command }); results.push({ diff --git a/packages/vscode/src/opencode.ts b/packages/vscode/src/opencode.ts index c2bad2ca..c5f6c23a 100644 --- a/packages/vscode/src/opencode.ts +++ b/packages/vscode/src/opencode.ts @@ -126,7 +126,7 @@ function getLoginShellPath(): string | null { return null; } -function buildAugmentedPath(): string { +export function buildAugmentedPath(): string { const augmented = new Set(); const loginPath = getLoginShellPath(); diff --git a/packages/web/server/index.js b/packages/web/server/index.js index de5803e9..12b3370e 100644 --- a/packages/web/server/index.js +++ b/packages/web/server/index.js @@ -989,18 +989,21 @@ function resolveBinaryFromPath(binaryName, searchPath) { } function getOpencodeSpawnConfig() { + const envPath = buildAugmentedPath(); + const resolvedEnv = { ...process.env, PATH: envPath }; + if (OPENCODE_BINARY_ENV) { - const explicit = resolveBinaryFromPath(OPENCODE_BINARY_ENV, process.env.PATH); + const explicit = resolveBinaryFromPath(OPENCODE_BINARY_ENV, envPath); if (explicit) { console.log(`Using OpenCode binary from OPENCODE_BINARY: ${explicit}`); - return { command: explicit, env: undefined }; + return { command: explicit, env: resolvedEnv }; } console.warn( `OPENCODE_BINARY path "${OPENCODE_BINARY_ENV}" not found. Falling back to search.` ); } - return { command: 'opencode', env: undefined }; + return { command: 'opencode', env: resolvedEnv }; } const ENV_CONFIGURED_OPENCODE_PORT = (() => { @@ -4127,7 +4130,12 @@ async function main(options = {}) { // NOTE: This route supports background execution to avoid tying up browser connections. const execJobs = new Map(); const EXEC_JOB_TTL_MS = 30 * 60 * 1000; - const COMMAND_TIMEOUT_MS = 60000; + const COMMAND_TIMEOUT_MS = (() => { + const raw = Number(process.env.OPENCHAMBER_FS_EXEC_TIMEOUT_MS); + if (Number.isFinite(raw) && raw > 0) return raw; + // `bun install` (common worktree setup cmd) often takes >60s. + return 5 * 60 * 1000; + })(); const pruneExecJobs = () => { const now = Date.now(); @@ -4149,9 +4157,12 @@ async function main(options = {}) { let stderr = ''; let timedOut = false; + const envPath = buildAugmentedPath(); + const execEnv = { ...process.env, PATH: envPath }; + const child = spawn(shell, [shellFlag, command], { cwd: resolvedCwd, - env: process.env, + env: execEnv, stdio: ['ignore', 'pipe', 'pipe'], });