From e5cdb0d8af60f066f1a8c99e8f9a1fafbbcde34b Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Fri, 14 Nov 2025 09:59:14 -0500 Subject: [PATCH 1/3] better find goosed --- .../settings/extensions/utils.test.ts | 5 --- ui/desktop/src/goosed.ts | 4 +- ui/desktop/src/main.ts | 7 +--- ui/desktop/src/preload.ts | 2 - ui/desktop/src/utils/pathUtils.ts | 40 +++---------------- 5 files changed, 8 insertions(+), 50 deletions(-) diff --git a/ui/desktop/src/components/settings/extensions/utils.test.ts b/ui/desktop/src/components/settings/extensions/utils.test.ts index 98c90963d427..8f65d936aa91 100644 --- a/ui/desktop/src/components/settings/extensions/utils.test.ts +++ b/ui/desktop/src/components/settings/extensions/utils.test.ts @@ -12,11 +12,6 @@ import { } from './utils'; import type { FixedExtensionEntry } from '../../ConfigContext'; -// Mock window.electron -const mockElectron = { - getBinaryPath: vi.fn(), -}; - Object.defineProperty(window, 'electron', { value: mockElectron, writable: true, diff --git a/ui/desktop/src/goosed.ts b/ui/desktop/src/goosed.ts index e6459cc68aa4..92ce9dfd7d7c 100644 --- a/ui/desktop/src/goosed.ts +++ b/ui/desktop/src/goosed.ts @@ -2,7 +2,7 @@ import { spawn, ChildProcess } from 'child_process'; import { createServer } from 'net'; import os from 'node:os'; import path from 'node:path'; -import { getBinaryPath } from './utils/pathUtils'; +import { getGoosedBinaryPath } from './utils/pathUtils'; import log from './utils/logger'; import { App } from 'electron'; import { Buffer } from 'node:buffer'; @@ -94,7 +94,7 @@ export const startGoosed = async ( return connectToExternalBackend(dir, 3000); } - let goosedPath = getBinaryPath(app, 'goosed'); + let goosedPath = getGoosedBinaryPath(app); const resolvedGoosedPath = path.resolve(goosedPath); diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index bf9456bede8b..54b4e44becd9 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -25,7 +25,7 @@ import os from 'node:os'; import { spawn } from 'child_process'; import 'dotenv/config'; import { checkServerStatus, startGoosed } from './goosed'; -import { expandTilde, getBinaryPath } from './utils/pathUtils'; +import { expandTilde } from './utils/pathUtils'; import log from './utils/logger'; import { ensureWinShims } from './utils/winShims'; import { addRecentDir, loadRecentDirs } from './utils/recentDirs'; @@ -1599,11 +1599,6 @@ ipcMain.handle('check-ollama', async () => { } }); -// Handle binary path requests -ipcMain.handle('get-binary-path', (_event, binaryName) => { - return getBinaryPath(app, binaryName); -}); - ipcMain.handle('read-file', async (_event, filePath) => { try { const expandedPath = expandTilde(filePath); diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts index 070e8a0afeb7..20a0d95b7425 100644 --- a/ui/desktop/src/preload.ts +++ b/ui/desktop/src/preload.ts @@ -66,7 +66,6 @@ type ElectronAPI = { selectFileOrDirectory: (defaultPath?: string) => Promise; startPowerSaveBlocker: () => Promise; stopPowerSaveBlocker: () => Promise; - getBinaryPath: (binaryName: string) => Promise; readFile: (directory: string) => Promise; writeFile: (directory: string, content: string) => Promise; ensureDirectory: (dirPath: string) => Promise; @@ -169,7 +168,6 @@ const electronAPI: ElectronAPI = { ipcRenderer.invoke('select-file-or-directory', defaultPath), startPowerSaveBlocker: () => ipcRenderer.invoke('start-power-save-blocker'), stopPowerSaveBlocker: () => ipcRenderer.invoke('stop-power-save-blocker'), - getBinaryPath: (binaryName: string) => ipcRenderer.invoke('get-binary-path', binaryName), readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath), writeFile: (filePath: string, content: string) => ipcRenderer.invoke('write-file', filePath, content), diff --git a/ui/desktop/src/utils/pathUtils.ts b/ui/desktop/src/utils/pathUtils.ts index f2865227a80f..468b515f88ce 100644 --- a/ui/desktop/src/utils/pathUtils.ts +++ b/ui/desktop/src/utils/pathUtils.ts @@ -4,48 +4,17 @@ import os from 'node:os'; import Electron from 'electron'; import log from './logger'; -export const getBinaryPath = (app: Electron.App, binaryName: string): string => { - // On Windows, rely on PATH we just patched in ensureWinShims for command-line tools - // but use explicit resources/bin path for goosed.exe - if (process.platform === 'win32') { - // For goosed.exe, always use the explicit resources/bin path - if (binaryName === 'goosed') { - return path.join(process.resourcesPath, 'bin', 'goosed.exe'); - } - - // Map binary names to their Windows equivalents - const windowsBinaryMap: Record = { - npx: 'npx.cmd', - uvx: 'uvx.exe', - }; - - // For other binaries, use Windows-specific extensions if available - const windowsBinary = windowsBinaryMap[binaryName] || binaryName; - return windowsBinary; - } +export const getGoosedBinaryPath = (app: Electron.App): string => { + let executableName = process.platform === 'win32' ? 'goosed.exe' : 'goosed'; - // For non-Windows platforms, use the original logic const possiblePaths: string[] = []; - addPaths(false, possiblePaths, binaryName, app); + addPaths(false, possiblePaths, executableName, app); for (const binPath of possiblePaths) { try { - // Security: Resolve the path and validate it's within expected directories const resolvedPath = path.resolve(binPath); - // Ensure the resolved path doesn't contain suspicious sequences - if ( - resolvedPath.includes('..') || - resolvedPath.includes(';') || - resolvedPath.includes('|') || - resolvedPath.includes('&') - ) { - log.error(`Suspicious path detected, skipping: ${resolvedPath}`); - continue; - } - if (fs.existsSync(resolvedPath)) { - // Additional security check: ensure it's a regular file const stats = fs.statSync(resolvedPath); if (stats.isFile()) { return resolvedPath; @@ -59,7 +28,7 @@ export const getBinaryPath = (app: Electron.App, binaryName: string): string => } throw new Error( - `Could not find ${binaryName} binary in any of the expected locations: ${possiblePaths.join( + `Could not find ${executableName} binary in any of the expected locations: ${possiblePaths.join( ', ' )}` ); @@ -75,6 +44,7 @@ const addPaths = ( possiblePaths.push( path.join(process.cwd(), 'src', 'bin', executableName), path.join(process.cwd(), 'bin', executableName), + path.join(process.cwd(), '..', '..', 'target', 'debug', executableName), path.join(process.cwd(), '..', '..', 'target', 'release', executableName) ); } else { From f8594e571c8f71f30f0b78e3f8306e566967f060 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Fri, 14 Nov 2025 13:46:37 -0500 Subject: [PATCH 2/3] not needed --- ui/desktop/src/components/settings/extensions/utils.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/desktop/src/components/settings/extensions/utils.test.ts b/ui/desktop/src/components/settings/extensions/utils.test.ts index 8f65d936aa91..fc5b5e4c4487 100644 --- a/ui/desktop/src/components/settings/extensions/utils.test.ts +++ b/ui/desktop/src/components/settings/extensions/utils.test.ts @@ -12,11 +12,6 @@ import { } from './utils'; import type { FixedExtensionEntry } from '../../ConfigContext'; -Object.defineProperty(window, 'electron', { - value: mockElectron, - writable: true, -}); - describe('Extension Utils', () => { beforeEach(() => { vi.clearAllMocks(); From 2f04a9d509f856e4661ef1361203bb26cb59bfc2 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Fri, 14 Nov 2025 14:11:28 -0500 Subject: [PATCH 3/3] More simple --- ui/desktop/src/goosed.ts | 42 ++++++++++++++++++++- ui/desktop/src/utils/pathUtils.ts | 62 ------------------------------- 2 files changed, 41 insertions(+), 63 deletions(-) diff --git a/ui/desktop/src/goosed.ts b/ui/desktop/src/goosed.ts index 92ce9dfd7d7c..bfd9c142adb8 100644 --- a/ui/desktop/src/goosed.ts +++ b/ui/desktop/src/goosed.ts @@ -1,8 +1,9 @@ +import Electron from 'electron'; +import fs from 'node:fs'; import { spawn, ChildProcess } from 'child_process'; import { createServer } from 'net'; import os from 'node:os'; import path from 'node:path'; -import { getGoosedBinaryPath } from './utils/pathUtils'; import log from './utils/logger'; import { App } from 'electron'; import { Buffer } from 'node:buffer'; @@ -215,3 +216,42 @@ export const startGoosed = async ( log.info(`Goosed server successfully started on port ${port}`); return [port, dir, goosedProcess, stderrLines]; }; + +const getGoosedBinaryPath = (app: Electron.App): string => { + let executableName = process.platform === 'win32' ? 'goosed.exe' : 'goosed'; + + let possiblePaths: string[]; + if (!app.isPackaged) { + possiblePaths = [ + path.join(process.cwd(), 'src', 'bin', executableName), + path.join(process.cwd(), 'bin', executableName), + path.join(process.cwd(), '..', '..', 'target', 'debug', executableName), + path.join(process.cwd(), '..', '..', 'target', 'release', executableName), + ]; + } else { + possiblePaths = [path.join(process.resourcesPath, 'bin', executableName)]; + } + + for (const binPath of possiblePaths) { + try { + const resolvedPath = path.resolve(binPath); + + if (fs.existsSync(resolvedPath)) { + const stats = fs.statSync(resolvedPath); + if (stats.isFile()) { + return resolvedPath; + } else { + log.error(`Path exists but is not a regular file: ${resolvedPath}`); + } + } + } catch (error) { + log.error(`Error checking path ${binPath}:`, error); + } + } + + throw new Error( + `Could not find ${executableName} binary in any of the expected locations: ${possiblePaths.join( + ', ' + )}` + ); +}; diff --git a/ui/desktop/src/utils/pathUtils.ts b/ui/desktop/src/utils/pathUtils.ts index 468b515f88ce..51af4abe4e32 100644 --- a/ui/desktop/src/utils/pathUtils.ts +++ b/ui/desktop/src/utils/pathUtils.ts @@ -1,67 +1,5 @@ import path from 'node:path'; -import fs from 'node:fs'; import os from 'node:os'; -import Electron from 'electron'; -import log from './logger'; - -export const getGoosedBinaryPath = (app: Electron.App): string => { - let executableName = process.platform === 'win32' ? 'goosed.exe' : 'goosed'; - - const possiblePaths: string[] = []; - addPaths(false, possiblePaths, executableName, app); - - for (const binPath of possiblePaths) { - try { - const resolvedPath = path.resolve(binPath); - - if (fs.existsSync(resolvedPath)) { - const stats = fs.statSync(resolvedPath); - if (stats.isFile()) { - return resolvedPath; - } else { - log.error(`Path exists but is not a regular file: ${resolvedPath}`); - } - } - } catch (error) { - log.error(`Error checking path ${binPath}:`, error); - } - } - - throw new Error( - `Could not find ${executableName} binary in any of the expected locations: ${possiblePaths.join( - ', ' - )}` - ); -}; - -const addPaths = ( - isWindows: boolean, - possiblePaths: string[], - executableName: string, - app: Electron.App -): void => { - if (!app.isPackaged) { - possiblePaths.push( - path.join(process.cwd(), 'src', 'bin', executableName), - path.join(process.cwd(), 'bin', executableName), - path.join(process.cwd(), '..', '..', 'target', 'debug', executableName), - path.join(process.cwd(), '..', '..', 'target', 'release', executableName) - ); - } else { - possiblePaths.push( - path.join(process.resourcesPath, 'bin', executableName), - path.join(app.getAppPath(), 'resources', 'bin', executableName) - ); - - if (isWindows) { - possiblePaths.push( - path.join(process.resourcesPath, executableName), - path.join(app.getAppPath(), 'resources', executableName), - path.join(app.getPath('exe'), '..', 'bin', executableName) - ); - } - } -}; /** * Expands tilde (~) to the user's home directory