Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
NODE_ENV: production
GH_TOKEN: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}
PUBLISH_RELEASE: true
NOTARIZE: true
run: pnpm --filter vibe build:mac

- name: List artifacts
Expand Down
3 changes: 3 additions & 0 deletions apps/electron-app/app-update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider: github
owner: co-browser
repo: vibe
3 changes: 0 additions & 3 deletions apps/electron-app/dev-app-update.yml

This file was deleted.

49 changes: 34 additions & 15 deletions apps/electron-app/electron-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@ module.exports = {
"!**/.vscode/*",
"!src/*",
"!electron.vite.config.{js,ts,mjs,cjs}",
"!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}",
"!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,CHANGELOG.md,README.md}",
"!{.env,.env.*,.npmrc,pnpm-lock.yaml}",
"!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}",
"out/**/*",
"app-update.yml",
],
afterSign: "scripts/notarize.js",
afterAllArtifactBuild: "scripts/notarizedmg.js",
afterSign:
process.env.NOTARIZE === "true" ? "scripts/notarize.js" : undefined,
afterAllArtifactBuild:
process.env.NOTARIZE === "true" ? "scripts/notarizedmg.js" : undefined,
asarUnpack: [
"dist/mac-arm64/vibe.app/Contents/Resources/app.asar.unpacked/node_modules/sqlite3/build/Release/node_sqlite3.node",
"**/out/main/processes/mcp-manager-process.js",
"**/out/main/processes/agent-process.js"
"**/out/main/processes/agent-process.js",
],
extraResources: [
{
from: "app-update.yml",
to: "app-update.yml",
},
{
from: "../../packages/mcp-gmail",
to: "mcp-servers/mcp-gmail",
Expand Down Expand Up @@ -56,6 +63,7 @@ module.exports = {
},
category: "public.app-category.developer-tools",
entitlements: "resources/entitlements.mac.plist",
entitlementsInherit: "resources/entitlements.mac.plist",
darkModeSupport: true,
electronLanguages: ["en"],
hardenedRuntime: true,
Expand All @@ -74,7 +82,6 @@ module.exports = {
},
dmg: {
icon: "resources/icon.icns",
background: "resources/DMG_Background.tiff",
sign: true,
format: "ULFO",
internetEnabled: true,
Expand Down Expand Up @@ -108,20 +115,32 @@ module.exports = {
},
// Ensure NODE_ENV is set for packaged app
asar: {
smartUnpack: true
smartUnpack: true,
},
npmRebuild: false,
// Only include publish config when explicitly publishing (e.g., in CI)
...(process.env.PUBLISH_RELEASE === "true" ? {
publish: {
provider: "github",
owner: "co-browser",
repo: "vibe",
releaseType: "draft",
publishAutoUpdate: true
}
} : {}),
...(process.env.PUBLISH_RELEASE === "true"
? {
publish: {
provider: "github",
owner: "co-browser",
repo: "vibe",
releaseType: "draft",
publishAutoUpdate: true,
},
}
: {}),
electronDownload: {
mirror: "https://npmmirror.com/mirrors/electron/",
},
electronFuses: {
runAsNode: false,
enableCookieEncryption: true,
enableNodeOptionsEnvironmentVariable: false,
enableNodeCliInspectArguments: false,
enableEmbeddedAsarIntegrityValidation: true,
onlyLoadAppFromAsar: true,
loadBrowserProcessSpecificV8Snapshot: false,
grantFileProtocolExtraPrivileges: false,
},
};
11 changes: 7 additions & 4 deletions apps/electron-app/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
browserWindowSessionIntegration,
childProcessIntegration,
} from "@sentry/electron/main";
import AppUpdater from "./services/update-service";
import { getAppUpdater } from "./services";
import log from "electron-log";

// Configure electron-log to write to file
Expand Down Expand Up @@ -758,9 +758,12 @@ app.whenReady().then(async () => {
mainWindow.webContents &&
!mainWindow.webContents.isDestroyed()
) {
//TODO: move to ipc service
const appUpdater = new AppUpdater(mainWindow);
appUpdater.checkForUpdates();
// Initialize and check for updates
const appUpdater = getAppUpdater();
if (appUpdater) {
appUpdater.initialize();
appUpdater.checkForUpdates();
}
mainWindow.webContents
.executeJavaScript(
`
Expand Down
95 changes: 95 additions & 0 deletions apps/electron-app/src/main/ipc/app/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { ipcMain, app, BrowserWindow } from "electron";
import { getAppUpdater } from "../../services";
import { createLogger } from "@vibe/shared-types";

const logger = createLogger("IPC:Update");

/**
* Register update-related IPC handlers
*/
export function registerUpdateHandlers(): void {
// Check for updates
ipcMain.handle("app:check-for-update", async () => {
try {
logger.info("Checking for updates");
const updater = getAppUpdater();
if (!updater) {
logger.warn("AppUpdater not initialized");
return {
currentVersion: app.getVersion(),
updateInfo: null,
};
}
return await updater.checkForUpdates();
} catch (error) {
logger.error("Failed to check for updates:", error);
throw error;
}
});

// Show update dialog
ipcMain.handle("app:show-update-dialog", async _event => {
try {
const updater = getAppUpdater();
if (!updater) {
logger.warn("AppUpdater not initialized");
return;
}

// Get the focused window
const win = BrowserWindow.getFocusedWindow();
if (!win) {
logger.warn("No window available for update dialog");
return;
}

return await updater.showUpdateDialog(win);
} catch (error) {
logger.error("Failed to show update dialog:", error);
throw error;
}
});
}

/**
* Forward update events to all windows
*/
/**
* Forward update events from auto-updater to all renderer processes
* This ensures all windows receive update notifications
*/
export function setupUpdateEventForwarding(): void {
const updater = getAppUpdater();
if (!updater) {
logger.warn("AppUpdater not initialized for event forwarding");
return;
}

// Forward all update events to renderer processes
const events = [
"checking-for-update",
"update-available",
"update-not-available",
"download-progress",
"update-downloaded",
"error",
];

events.forEach(eventName => {
updater.autoUpdater.on(eventName as any, (...args: any[]) => {
logger.debug(`Update event: ${eventName}`, args);

// Send to all windows
const windows = BrowserWindow.getAllWindows();
windows.forEach(window => {
if (!window.isDestroyed()) {
try {
window.webContents.send(`update-${eventName}`, ...args);
} catch (error) {
logger.error(`Failed to send update event to window: ${error}`);
}
}
});
});
});
}
8 changes: 8 additions & 0 deletions apps/electron-app/src/main/ipc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import "@/ipc/app/notifications";
import "@/ipc/app/actions";
import "@/ipc/app/gmail";
import "@/ipc/app/onboarding";
import {
registerUpdateHandlers,
setupUpdateEventForwarding,
} from "@/ipc/app/update";

// Chat APIs - direct imports (register themselves)
import "@/ipc/chat/chat-messaging";
Expand Down Expand Up @@ -50,6 +54,10 @@ export function registerAllIpcHandlers(browser: Browser): () => void {
// Setup browser event forwarding (needs browser instance)
setupBrowserEventForwarding();

// Register update handlers
registerUpdateHandlers();
setupUpdateEventForwarding();

// Setup session state sync (broadcasts to all windows)
let sessionUnsubscribe: (() => void) | null = null;
try {
Expand Down
20 changes: 20 additions & 0 deletions apps/electron-app/src/main/menu/items/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import type { MenuItemConstructorOptions } from "electron";
import { dialog, BrowserWindow } from "electron";
import { getAppUpdater } from "@/services";

export function createHelpMenu(): MenuItemConstructorOptions {
const isMac = process.platform === "darwin";
Expand Down Expand Up @@ -45,6 +46,25 @@ F1: Show this help dialog`,
accelerator: "F1",
click: showKeyboardShortcutsHelp,
},
{ type: "separator" },
{
label: "Check for Updates...",
click: async () => {
const updater = getAppUpdater();
const focusedWindow = BrowserWindow.getFocusedWindow();

if (focusedWindow && updater) {
// Send event to renderer to show checking state
focusedWindow.webContents.send("update-checking-for-update");

try {
await updater.checkForUpdates();
} catch (error) {
console.error("Error checking for updates:", error);
}
}
},
},
] as MenuItemConstructorOptions[],
};
}
16 changes: 16 additions & 0 deletions apps/electron-app/src/main/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,19 @@ export type {
BrowsingHistoryEntry,
SavedPassword,
} from "./profile-service";

// Export the AppUpdater service
import AppUpdater from "./update-service";
export { AppUpdater };

/**
* Get the singleton AppUpdater instance
*/
export function getAppUpdater(): AppUpdater | null {
try {
return AppUpdater.getInstance();
} catch (error) {
console.error("Failed to get AppUpdater instance:", error);
return null;
}
}
Loading