11import { randomUUID } from "node:crypto" ;
2- import { rmSync } from "node:fs" ;
2+ import { mkdirSync , rmSync , symlinkSync } from "node:fs" ;
3+ import { tmpdir } from "node:os" ;
34import { join } from "node:path" ;
45import { Agent , PermissionMode } from "@posthog/agent" ;
56import {
@@ -128,6 +129,15 @@ export function registerAgentIpc(
128129 ts : Date . now ( ) ,
129130 message : `[Claude stderr] ${ text } ` ,
130131 } ) ;
132+
133+ // Propagate spawn errors specifically to help debugging
134+ if ( text . includes ( "spawn" ) && text . includes ( "ENOENT" ) ) {
135+ emitToRenderer ( {
136+ type : "error" ,
137+ ts : Date . now ( ) ,
138+ message : `Critical Agent Error: ${ text } ` ,
139+ } ) ;
140+ }
131141 } ;
132142
133143 const agent = new Agent ( {
@@ -191,14 +201,35 @@ export function registerAgentIpc(
191201 ( async ( ) => {
192202 const resolvedPermission = resolvePermissionMode ( permissionMode ) ;
193203 try {
204+ // Create a temporary directory to mock 'node' in PATH
205+ // Electron apps don't have 'node' in PATH, and process.execPath points to the App binary
206+ // We symlink 'node' -> process.execPath and add this dir to PATH
207+ const mockNodeDir = join ( tmpdir ( ) , `array-agent-node-${ taskId } ` ) ;
208+ try {
209+ mkdirSync ( mockNodeDir , { recursive : true } ) ;
210+ const nodeSymlinkPath = join ( mockNodeDir , "node" ) ;
211+ // Remove existing symlink if it exists
212+ try {
213+ rmSync ( nodeSymlinkPath , { force : true } ) ;
214+ } catch { }
215+ symlinkSync ( process . execPath , nodeSymlinkPath ) ;
216+ } catch ( err ) {
217+ console . warn ( "[agent] Failed to setup mock node environment" , err ) ;
218+ }
219+
220+ const newPath = `${ mockNodeDir } :${ process . env . PATH || "" } ` ;
221+
194222 const envOverrides = {
195223 ...process . env ,
224+ PATH : newPath ,
196225 POSTHOG_API_KEY : apiKey ,
197226 POSTHOG_API_HOST : apiHost ,
198227 POSTHOG_AUTH_HEADER : `Bearer ${ apiKey } ` ,
199228 ANTHROPIC_API_KEY : apiKey ,
200229 ANTHROPIC_AUTH_TOKEN : apiKey ,
201230 ANTHROPIC_BASE_URL : `${ apiHost } /api/projects/${ projectId } /llm_gateway` ,
231+ // Ensure we can run node in the packaged app
232+ ELECTRON_RUN_AS_NODE : "1" ,
202233 } ;
203234
204235 const mcpOverrides = { } ;
@@ -216,9 +247,16 @@ export function registerAgentIpc(
216247 env : envOverrides ,
217248 mcpServers : mcpOverrides ,
218249 pathToClaudeCodeExecutable : getClaudeCliPath ( ) ,
250+ // Still pass this, but the PATH hack above is the real fix
251+ nodePath : process . execPath ,
219252 } ,
220253 } ) ;
221254
255+ // Clean up mock node dir
256+ try {
257+ rmSync ( mockNodeDir , { recursive : true , force : true } ) ;
258+ } catch { }
259+
222260 emitToRenderer ( { type : "done" , success : true , ts : Date . now ( ) } ) ;
223261 } catch ( err ) {
224262 console . error ( "[agent] task execution failed" , err ) ;
0 commit comments