Simplify finding goosed (#5739)
This commit is contained in:
@@ -12,16 +12,6 @@ import {
|
|||||||
} from './utils';
|
} from './utils';
|
||||||
import type { FixedExtensionEntry } from '../../ConfigContext';
|
import type { FixedExtensionEntry } from '../../ConfigContext';
|
||||||
|
|
||||||
// Mock window.electron
|
|
||||||
const mockElectron = {
|
|
||||||
getBinaryPath: vi.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(window, 'electron', {
|
|
||||||
value: mockElectron,
|
|
||||||
writable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Extension Utils', () => {
|
describe('Extension Utils', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import Electron from 'electron';
|
||||||
|
import fs from 'node:fs';
|
||||||
import { spawn, ChildProcess } from 'child_process';
|
import { spawn, ChildProcess } from 'child_process';
|
||||||
import { createServer } from 'net';
|
import { createServer } from 'net';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { getBinaryPath } from './utils/pathUtils';
|
|
||||||
import log from './utils/logger';
|
import log from './utils/logger';
|
||||||
import { App } from 'electron';
|
import { App } from 'electron';
|
||||||
import { Buffer } from 'node:buffer';
|
import { Buffer } from 'node:buffer';
|
||||||
@@ -94,7 +95,7 @@ export const startGoosed = async (
|
|||||||
return connectToExternalBackend(dir, 3000);
|
return connectToExternalBackend(dir, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
let goosedPath = getBinaryPath(app, 'goosed');
|
let goosedPath = getGoosedBinaryPath(app);
|
||||||
|
|
||||||
const resolvedGoosedPath = path.resolve(goosedPath);
|
const resolvedGoosedPath = path.resolve(goosedPath);
|
||||||
|
|
||||||
@@ -215,3 +216,42 @@ export const startGoosed = async (
|
|||||||
log.info(`Goosed server successfully started on port ${port}`);
|
log.info(`Goosed server successfully started on port ${port}`);
|
||||||
return [port, dir, goosedProcess, stderrLines];
|
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(
|
||||||
|
', '
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import os from 'node:os';
|
|||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { checkServerStatus, startGoosed } from './goosed';
|
import { checkServerStatus, startGoosed } from './goosed';
|
||||||
import { expandTilde, getBinaryPath } from './utils/pathUtils';
|
import { expandTilde } from './utils/pathUtils';
|
||||||
import log from './utils/logger';
|
import log from './utils/logger';
|
||||||
import { ensureWinShims } from './utils/winShims';
|
import { ensureWinShims } from './utils/winShims';
|
||||||
import { addRecentDir, loadRecentDirs } from './utils/recentDirs';
|
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) => {
|
ipcMain.handle('read-file', async (_event, filePath) => {
|
||||||
try {
|
try {
|
||||||
const expandedPath = expandTilde(filePath);
|
const expandedPath = expandTilde(filePath);
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ type ElectronAPI = {
|
|||||||
selectFileOrDirectory: (defaultPath?: string) => Promise<string | null>;
|
selectFileOrDirectory: (defaultPath?: string) => Promise<string | null>;
|
||||||
startPowerSaveBlocker: () => Promise<number>;
|
startPowerSaveBlocker: () => Promise<number>;
|
||||||
stopPowerSaveBlocker: () => Promise<void>;
|
stopPowerSaveBlocker: () => Promise<void>;
|
||||||
getBinaryPath: (binaryName: string) => Promise<string>;
|
|
||||||
readFile: (directory: string) => Promise<FileResponse>;
|
readFile: (directory: string) => Promise<FileResponse>;
|
||||||
writeFile: (directory: string, content: string) => Promise<boolean>;
|
writeFile: (directory: string, content: string) => Promise<boolean>;
|
||||||
ensureDirectory: (dirPath: string) => Promise<boolean>;
|
ensureDirectory: (dirPath: string) => Promise<boolean>;
|
||||||
@@ -169,7 +168,6 @@ const electronAPI: ElectronAPI = {
|
|||||||
ipcRenderer.invoke('select-file-or-directory', defaultPath),
|
ipcRenderer.invoke('select-file-or-directory', defaultPath),
|
||||||
startPowerSaveBlocker: () => ipcRenderer.invoke('start-power-save-blocker'),
|
startPowerSaveBlocker: () => ipcRenderer.invoke('start-power-save-blocker'),
|
||||||
stopPowerSaveBlocker: () => ipcRenderer.invoke('stop-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),
|
readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath),
|
||||||
writeFile: (filePath: string, content: string) =>
|
writeFile: (filePath: string, content: string) =>
|
||||||
ipcRenderer.invoke('write-file', filePath, content),
|
ipcRenderer.invoke('write-file', filePath, content),
|
||||||
|
|||||||
@@ -1,97 +1,5 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'node:fs';
|
|
||||||
import os from 'node:os';
|
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<string, string> = {
|
|
||||||
npx: 'npx.cmd',
|
|
||||||
uvx: 'uvx.exe',
|
|
||||||
};
|
|
||||||
|
|
||||||
// For other binaries, use Windows-specific extensions if available
|
|
||||||
const windowsBinary = windowsBinaryMap[binaryName] || binaryName;
|
|
||||||
return windowsBinary;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For non-Windows platforms, use the original logic
|
|
||||||
const possiblePaths: string[] = [];
|
|
||||||
addPaths(false, possiblePaths, binaryName, 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;
|
|
||||||
} 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 ${binaryName} 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', '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
|
* Expands tilde (~) to the user's home directory
|
||||||
|
|||||||
Reference in New Issue
Block a user