Compare commits

...
12 Commits
Author SHA1 Message Date
semantic-release-bot 7a6587cb8b chore(release): 0.15.3
## [0.15.3](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.2...v0.15.3) (2026-05-02)

### 👷 Build System

* allow fs-xattr and appdmg to build in pnpm v10 ([1c26a9e](https://github.com/evandrodevbr/GeminiNexus/commit/1c26a9e542f3fa5a05b370b8a332ffe5a727ea92))
2026-05-02 05:13:21 +00:00
Gemini Nexus Bot e1c9a9e34a Merge branch 'main' of https://github.com/evandrodevbr/GeminiNexus 2026-05-02 02:12:25 -03:00
Gemini Nexus Bot 1c26a9e542 build: allow fs-xattr and appdmg to build in pnpm v10 2026-05-02 02:09:53 -03:00
semantic-release-bot 5026be12d2 chore(release): 0.15.2
## [0.15.2](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.1...v0.15.2) (2026-05-02)

### 👷 Build System

* allow macos-alias and other native modules to build in pnpm v10 ([2e3a65b](https://github.com/evandrodevbr/GeminiNexus/commit/2e3a65b76b91012638c422f6223b79399775c635))
2026-05-02 04:57:50 +00:00
Gemini Nexus Bot c05daba98b Merge branch 'main' of https://github.com/evandrodevbr/GeminiNexus 2026-05-02 01:57:07 -03:00
Gemini Nexus Bot 2e3a65b76b build: allow macos-alias and other native modules to build in pnpm v10
- Fixes MakerDMG failure (Cannot find module '../build/Release/volume.node')
- Add macos-alias, better-sqlite3, keytar, and electron to trustedDependencies and pnpm.onlyBuiltDependencies
- Also updated pnpm-workspace.yaml allowBuilds directive
2026-05-02 01:48:15 -03:00
semantic-release-bot 62634b5511 chore(release): 0.15.1
## [0.15.1](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.0...v0.15.1) (2026-05-02)

### ✅ Tests

* add universal electron mock to fix headless CI test runs ([1d35801](https://github.com/evandrodevbr/GeminiNexus/commit/1d35801980e3e54cec1d3e52ed75850eb7eec350))
* fix test environments failing due to missing IS_DEV_ENVIRONMENT mock export ([269ad18](https://github.com/evandrodevbr/GeminiNexus/commit/269ad18ed6c09585e881d0d916558b1bcc7ad940))
* update proxy-advanced-handler tests to reflect optional service registration ([f4daaa8](https://github.com/evandrodevbr/GeminiNexus/commit/f4daaa8ca13bc75c7e5090905176c06b61ecfedc))

### 👷 Build System

* replace MakerSquirrel with MakerZIP for Windows ([27223f5](https://github.com/evandrodevbr/GeminiNexus/commit/27223f587d7facf25beef77aa6984cd248f2703d))
2026-05-02 04:46:37 +00:00
Gemini Nexus Bot 27223f587d build: replace MakerSquirrel with MakerZIP for Windows
- Remove @electron-forge/maker-squirrel dependency
- Squirrel.Windows fails on modern Windows GitHub Actions runners with Win32Exception during zip creation
- We already use MakerWix which produces a much better .msi installer for Windows users
- Add MakerZIP support for win32 to provide a portable alternative alongside the MSI
2026-05-02 01:42:46 -03:00
Gemini Nexus Bot 1d35801980 test: add universal electron mock to fix headless CI test runs
Vitest workers throw 'Electron failed to install correctly' when backend unit tests indirectly import electron via path/util modules. This mock provides stubs for ipc, app, and BrowserWindow interfaces so happy-dom environments don't crash expecting native binaries.
2026-05-02 01:27:57 -03:00
Gemini Nexus Bot 269ad18ed6 test: fix test environments failing due to missing IS_DEV_ENVIRONMENT mock export 2026-05-02 01:18:12 -03:00
Gemini Nexus Bot 9b9b977ae6 Merge branch 'main' of https://github.com/evandrodevbr/GeminiNexus 2026-05-02 01:09:55 -03:00
Gemini Nexus Bot f4daaa8ca1 test: update proxy-advanced-handler tests to reflect optional service registration
- Mock service-registry correctly exposing getServiceOptional
- Update tests to expect successful fallback resolutions when services are not yet registered
- Remove obsolete error test that is now correctly handled via ConfigManager fallback
2026-05-02 01:05:06 -03:00
17 changed files with 3163 additions and 8711 deletions

No files matched your search

+24
View File
@@ -2,6 +2,30 @@
# Changelog
## [0.15.3](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.2...v0.15.3) (2026-05-02)
### 👷 Build System
* allow fs-xattr and appdmg to build in pnpm v10 ([1c26a9e](https://github.com/evandrodevbr/GeminiNexus/commit/1c26a9e542f3fa5a05b370b8a332ffe5a727ea92))
## [0.15.2](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.1...v0.15.2) (2026-05-02)
### 👷 Build System
* allow macos-alias and other native modules to build in pnpm v10 ([2e3a65b](https://github.com/evandrodevbr/GeminiNexus/commit/2e3a65b76b91012638c422f6223b79399775c635))
## [0.15.1](https://github.com/evandrodevbr/GeminiNexus/compare/v0.15.0...v0.15.1) (2026-05-02)
### ✅ Tests
* add universal electron mock to fix headless CI test runs ([1d35801](https://github.com/evandrodevbr/GeminiNexus/commit/1d35801980e3e54cec1d3e52ed75850eb7eec350))
* fix test environments failing due to missing IS_DEV_ENVIRONMENT mock export ([269ad18](https://github.com/evandrodevbr/GeminiNexus/commit/269ad18ed6c09585e881d0d916558b1bcc7ad940))
* update proxy-advanced-handler tests to reflect optional service registration ([f4daaa8](https://github.com/evandrodevbr/GeminiNexus/commit/f4daaa8ca13bc75c7e5090905176c06b61ecfedc))
### 👷 Build System
* replace MakerSquirrel with MakerZIP for Windows ([27223f5](https://github.com/evandrodevbr/GeminiNexus/commit/27223f587d7facf25beef77aa6984cd248f2703d))
## [0.15.0](https://github.com/evandrodevbr/GeminiNexus/compare/v0.14.0...v0.15.0) (2026-05-02)
### ✨ Features
+1 -6
View File
@@ -10,7 +10,6 @@ import type {
import { MakerDeb } from '@electron-forge/maker-deb';
import { MakerDMG } from '@electron-forge/maker-dmg';
import { MakerRpm } from '@electron-forge/maker-rpm';
import { MakerSquirrel } from '@electron-forge/maker-squirrel';
import { MakerWix } from '@electron-forge/maker-wix';
import { MakerZIP } from '@electron-forge/maker-zip';
import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives';
@@ -441,10 +440,6 @@ const config: ForgeConfig = {
},
},
makers: [
new MakerSquirrel({
setupIcon: 'images/icon.ico',
iconUrl: 'https://raw.githubusercontent.com/evandrodevbr/GeminiNexus/main/images/icon.ico',
}),
...(process.platform === 'win32' && process.arch === 'x64' && isWixAvailable()
? [
new MakerWix({
@@ -463,7 +458,7 @@ const config: ForgeConfig = {
},
['darwin'],
),
new MakerZIP({}, ['darwin']),
new MakerZIP({}, ['darwin', 'win32']),
appImageMaker,
new MakerRpm({}),
new MakerDeb({}),
+25 -3
View File
@@ -1,7 +1,7 @@
{
"name": "gemini-nexus",
"productName": "Gemini Nexus",
"version": "0.15.0",
"version": "0.15.3",
"description": "Gemini Nexus - Electron App",
"main": ".vite/build/main.js",
"private": true,
@@ -32,7 +32,6 @@
"@electron-forge/maker-deb": "^7.11.1",
"@electron-forge/maker-dmg": "^7.11.1",
"@electron-forge/maker-rpm": "^7.11.1",
"@electron-forge/maker-squirrel": "^7.11.1",
"@electron-forge/maker-wix": "^7.9.0",
"@electron-forge/maker-zip": "^7.11.1",
"@electron-forge/plugin-auto-unpack-natives": "^7.11.1",
@@ -171,9 +170,32 @@
"@nestjs/core",
"@sentry/cli",
"@swc/core",
"protobufjs"
"protobufjs",
"macos-alias",
"better-sqlite3",
"keytar",
"electron",
"electron-winstaller",
"esbuild",
"fs-xattr",
"appdmg"
],
"pnpm": {
"onlyBuiltDependencies": [
"@bitdisaster/exe-icon-extractor",
"@nestjs/core",
"@sentry/cli",
"@swc/core",
"protobufjs",
"macos-alias",
"better-sqlite3",
"keytar",
"electron",
"electron-winstaller",
"esbuild",
"fs-xattr",
"appdmg"
],
"overrides": {
"node-abi": "^4.28.0",
"fastify": "^5.8.5",
+2986 -8662
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -8,5 +8,8 @@ allowBuilds:
electron-winstaller: true
esbuild: true
keytar: true
macos-alias: true
protobufjs: true
sharp: true
fs-xattr: true
appdmg: true
+2 -1
View File
@@ -16,7 +16,8 @@ import { getSwitchGuardSnapshot } from '../../ipc/switchGuard';
vi.mock('../../utils/paths', async () => {
const path = await import('path');
const agentDir = path.join(process.cwd(), 'temp_test_agent');
return {
return {
IS_DEV_ENVIRONMENT: false,
getAgentDir: vi.fn(() => agentDir),
getAccountsFilePath: vi.fn(() => path.join(agentDir, 'accounts.json')),
getBackupsDir: vi.fn(() => path.join(agentDir, 'backups')),
+2 -1
View File
@@ -57,7 +57,8 @@ vi.mock('../../ipc/database/dbConnection', () => ({
}),
}));
vi.mock('../../utils/paths', () => ({
vi.mock('../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getGeminiNexusDbPaths: () => ['mock-db'],
getCloudAccountsDbPath: () => 'mock-cloud-db',
getAgentDir: vi.fn(() => '/mock/agent/dir'),
+2 -1
View File
@@ -21,7 +21,8 @@ vi.mock('../../ipc/database/dbConnection', () => ({
}),
}));
vi.mock('../../utils/paths', () => ({
vi.mock('../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getGeminiNexusDbPaths: () => ['mock-db'],
}));
+2 -1
View File
@@ -47,7 +47,8 @@ vi.mock('../../utils/logger', () => ({
},
}));
vi.mock('../../utils/paths', () => ({
vi.mock('../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getGeminiNexusDbPaths: vi.fn().mockReturnValue(['/tmp/test.vscdb']),
}));
+2 -1
View File
@@ -64,7 +64,8 @@ vi.mock('../../../ipc/database/dbConnection', () => ({
configureDatabase: vi.fn(),
}));
vi.mock('../../../utils/paths', () => ({
vi.mock('../../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getCloudAccountsDbPath: vi.fn(() => '/mock/db/path.sqlite'),
}));
+2 -1
View File
@@ -48,7 +48,8 @@ vi.mock('../../../utils/logger', () => ({
},
}));
vi.mock('../../../utils/paths', () => ({
vi.mock('../../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getGeminiNexusDbPaths: vi.fn().mockReturnValue(['/tmp/test.vscdb']),
}));
+2 -1
View File
@@ -52,7 +52,8 @@ vi.mock('../../../utils/logger', () => ({
},
}));
vi.mock('../../../utils/paths', () => ({
vi.mock('../../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getGeminiNexusStoragePaths: vi.fn().mockReturnValue(['/tmp/storage.json']),
getGeminiNexusDbPaths: vi.fn().mockReturnValue(['/tmp/state.vscdb']),
getAgentDir: vi.fn().mockReturnValue('/tmp/agent'),
@@ -48,14 +48,19 @@ vi.mock('../../../ipc/proxy-advanced/service-registry', () => {
function getServiceOrThrow(key: string) {
const service = (proxyAdvancedRegistry as Record<string, unknown>)[key];
if (!service) {
throw new Error(`${key} is not registered`);
throw new Error(`ProxyAdvanced service '${key}' is not registered`);
}
return service;
}
function getServiceOptional(key: string) {
return (proxyAdvancedRegistry as Record<string, unknown>)[key];
}
return {
proxyAdvancedRegistry,
getServiceOrThrow,
getServiceOptional,
};
});
@@ -176,10 +181,10 @@ describe('Proxy Advanced Handler', () => {
});
describe('getParityCounters', () => {
it('should return error when tokenManager is null', async () => {
it('should return empty counters when tokenManager is null', async () => {
const result = await getParityCounters();
expect(result.success).toBe(false);
expect(result.error).toBe('Proxy advanced service not initialized');
expect(result.success).toBe(true);
expect(result.data?.totalRequests).toBe(0);
});
it('should return counters from tokenManager', async () => {
@@ -200,10 +205,10 @@ describe('Proxy Advanced Handler', () => {
});
describe('getCircuitBreakerStatus', () => {
it('should return error when tokenManager is null', async () => {
it('should return empty status when tokenManager is null', async () => {
const result = await getCircuitBreakerStatus();
expect(result.success).toBe(false);
expect(result.error).toBe('Proxy advanced service not initialized');
expect(result.success).toBe(true);
expect(result.data?.states).toEqual({});
});
it('should return mapped status from tokenManager', async () => {
@@ -222,10 +227,10 @@ describe('Proxy Advanced Handler', () => {
});
describe('getProxyMetrics', () => {
it('should return error when proxyMetrics is null', async () => {
it('should return zero metrics when proxyMetrics is null', async () => {
const result = await getProxyMetrics();
expect(result.success).toBe(false);
expect(result.error).toBe('Proxy advanced service not initialized');
expect(result.success).toBe(true);
expect(result.data?.totalRequests).toBe(0);
});
it('should return metrics from proxyMetrics service', async () => {
@@ -249,10 +254,10 @@ describe('Proxy Advanced Handler', () => {
});
describe('getRecentRequests', () => {
it('should return error when proxyReplay is null', async () => {
it('should return empty array when proxyReplay is null', async () => {
const result = await getRecentRequests();
expect(result.success).toBe(false);
expect(result.error).toBe('Proxy advanced service not initialized');
expect(result.success).toBe(true);
expect(result.data).toEqual([]);
});
it('should return recent requests from proxyReplay', async () => {
@@ -410,10 +415,10 @@ describe('Proxy Advanced Handler', () => {
});
describe('generateIdeConfig', () => {
it('should return error when proxyIdeConfig is null', async () => {
it('should generate fallback config when proxyIdeConfig is null', async () => {
const result = await generateIdeConfig('vscode');
expect(result.success).toBe(false);
expect(result.error).toBe('Proxy advanced service not initialized');
expect(result.success).toBe(true);
expect(result.data?.name).toBe('VS Code');
});
it('should return error for unsupported IDE', async () => {
@@ -428,7 +433,8 @@ describe('Proxy Advanced Handler', () => {
vi.mocked(proxyAdvancedRegistry).proxyIdeConfig = {
generateConfigForCurrentServer: vi.fn().mockReturnValue({
ide: 'vscode',
content: null,
format: 'json',
content: { 'openai.url': 'http://localhost:8045/v1', 'openai.apiKey': 'key' },
}),
} as any;
@@ -443,7 +449,8 @@ describe('Proxy Advanced Handler', () => {
vi.mocked(proxyAdvancedRegistry).proxyIdeConfig = {
generateConfigForCurrentServer: vi.fn().mockReturnValue({
ide: 'cursor',
content: null,
format: 'json',
content: { OPENAI_BASE_URL: 'http://localhost:8045/v1', OPENAI_API_KEY: 'key' },
}),
} as any;
@@ -453,16 +460,5 @@ describe('Proxy Advanced Handler', () => {
expect(result.data?.proxySetting).toBe('OPENAI_BASE_URL');
vi.mocked(proxyAdvancedRegistry).proxyIdeConfig = null;
});
it('should handle null result from config generator', async () => {
vi.mocked(proxyAdvancedRegistry).proxyIdeConfig = {
generateConfigForCurrentServer: vi.fn().mockReturnValue(null),
} as any;
const result = await generateIdeConfig('vscode');
expect(result.success).toBe(false);
expect(result.error).toBe('Failed to generate IDE config: server not configured');
vi.mocked(proxyAdvancedRegistry).proxyIdeConfig = null;
});
});
});
});
@@ -24,7 +24,8 @@ vi.mock('../../../ipc/database/cloudHandler', () => ({
ensureDatabaseInitialized: vi.fn(),
}));
vi.mock('../../../utils/paths', () => ({
vi.mock('../../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getCloudAccountsDbPath: vi.fn(() => '/mock/db/path.sqlite'),
}));
+2 -1
View File
@@ -4,7 +4,8 @@ import path from 'path';
vi.mock('../../utils/paths', async () => {
const path = await import('path');
return {
return {
IS_DEV_ENVIRONMENT: false,
getAgentDir: vi.fn(() => path.join(process.cwd(), 'temp_test_logs')),
};
});
+78
View File
@@ -59,3 +59,81 @@ class MockIntersectionObserver {
disconnect() {}
}
(globalThis as any).IntersectionObserver = MockIntersectionObserver;
// Universal Electron mock for testing environments
vi.mock('electron', () => {
return {
app: {
getPath: vi.fn((name) => `/mock/path/${name}`),
getName: vi.fn(() => 'Gemini Nexus Test'),
getVersion: vi.fn(() => '1.0.0'),
isPackaged: false,
getLocale: vi.fn(() => 'en-US'),
getPreferredSystemLanguages: vi.fn(() => ['en-US']),
requestSingleInstanceLock: vi.fn(() => true),
quit: vi.fn(),
disableHardwareAcceleration: vi.fn(),
setName: vi.fn(),
commandLine: {
appendSwitch: vi.fn(),
},
},
ipcMain: {
on: vi.fn(),
handle: vi.fn(),
removeHandler: vi.fn(),
removeAllListeners: vi.fn(),
},
ipcRenderer: {
on: vi.fn(),
off: vi.fn(),
once: vi.fn(),
invoke: vi.fn(),
send: vi.fn(),
postMessage: vi.fn(),
},
BrowserWindow: vi.fn().mockImplementation(() => ({
loadURL: vi.fn(),
loadFile: vi.fn(),
show: vi.fn(),
close: vi.fn(),
destroy: vi.fn(),
webContents: {
send: vi.fn(),
on: vi.fn(),
session: {
webRequest: {
onBeforeSendHeaders: vi.fn(),
}
}
},
on: vi.fn(),
})),
dialog: {
showMessageBox: vi.fn().mockResolvedValue({ response: 0 }),
showOpenDialog: vi.fn().mockResolvedValue({ canceled: true, filePaths: [] }),
showSaveDialog: vi.fn().mockResolvedValue({ canceled: true, filePath: '' }),
},
shell: {
openPath: vi.fn(),
openExternal: vi.fn(),
},
contextBridge: {
exposeInMainWorld: vi.fn(),
},
Tray: vi.fn().mockImplementation(() => ({
setToolTip: vi.fn(),
setContextMenu: vi.fn(),
on: vi.fn(),
destroy: vi.fn(),
})),
Menu: {
buildFromTemplate: vi.fn(),
},
nativeTheme: {
themeSource: 'system',
shouldUseDarkColors: true,
on: vi.fn(),
},
};
});
+2 -1
View File
@@ -25,7 +25,8 @@ vi.mock('../../ipc/database/cloudHandler', () => ({
ensureDatabaseInitialized: vi.fn(),
}));
vi.mock('../../utils/paths', () => ({
vi.mock('../../utils/paths', () => ({
IS_DEV_ENVIRONMENT: false,
getCloudAccountsDbPath: vi.fn(() => '/mock/db/path.sqlite'),
}));