add desktop codex login and analyst copilot

This commit is contained in:
dirtydishes 2026-05-20 10:41:13 -04:00
parent fb25b5ac97
commit a8d183f38e
24 changed files with 4127 additions and 97 deletions

View file

@ -1,5 +1,6 @@
import { app, BrowserWindow, shell } from "electron";
import type { Event as ElectronEvent } from "electron";
import { app, BrowserWindow, ipcMain, shell } from "electron";
import type { Event as ElectronEvent, IpcMainInvokeEvent } from "electron";
import { fileURLToPath } from "node:url";
import {
DESKTOP_PRODUCTION_URL,
@ -7,11 +8,25 @@ import {
isTrustedAppUrl,
resolveDesktopStartUrl
} from "./security.js";
import { IslandflowDesktopAiService } from "./desktop-ai.js";
import {
DESKTOP_AI_CANCEL_LOGIN,
DESKTOP_AI_GET_STATE,
DESKTOP_AI_LOGIN_BROWSER,
DESKTOP_AI_LOGIN_DEVICE,
DESKTOP_AI_LOGOUT,
DESKTOP_AI_RUN_TASK,
DESKTOP_AI_STATE_CHANNEL,
DESKTOP_AI_UPDATE_PREFERENCES
} from "./desktop-ai-ipc.js";
const WINDOW_BACKGROUND_COLOR = "#06080b";
const WINDOW_TITLE = "Islandflow";
let mainWindow: BrowserWindow | null = null;
let desktopAiService: IslandflowDesktopAiService | null = null;
const PRELOAD_PATH = fileURLToPath(new URL("./preload.js", import.meta.url));
const canOpenExternalUrl = (sourceUrl: string, targetUrl: string): boolean => {
return isTrustedAppUrl(sourceUrl) && isSafeExternalUrl(targetUrl);
@ -61,6 +76,7 @@ const createMainWindow = (): BrowserWindow => {
title: WINDOW_TITLE,
backgroundColor: WINDOW_BACKGROUND_COLOR,
webPreferences: {
preload: PRELOAD_PATH,
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
@ -92,6 +108,68 @@ const createMainWindow = (): BrowserWindow => {
return window;
};
const broadcastDesktopAiState = (): void => {
if (!desktopAiService) {
return;
}
const state = desktopAiService.getState();
for (const window of BrowserWindow.getAllWindows()) {
window.webContents.send(DESKTOP_AI_STATE_CHANNEL, state);
}
};
const getTrustedSenderUrl = (event: IpcMainInvokeEvent): string => {
const senderUrl = event.senderFrame?.url || event.sender.getURL();
if (!isTrustedAppUrl(senderUrl)) {
throw new Error(`Rejected desktop AI IPC from untrusted origin: ${senderUrl || "unknown"}`);
}
return senderUrl;
};
const registerDesktopAiIpc = (service: IslandflowDesktopAiService): void => {
const guard = (event: IpcMainInvokeEvent): void => {
getTrustedSenderUrl(event);
};
ipcMain.handle(DESKTOP_AI_GET_STATE, async (event) => {
guard(event);
await service.start();
return service.getState();
});
ipcMain.handle(DESKTOP_AI_LOGIN_BROWSER, async (event) => {
guard(event);
await service.loginWithBrowser();
});
ipcMain.handle(DESKTOP_AI_LOGIN_DEVICE, async (event) => {
guard(event);
await service.loginWithDeviceCode();
});
ipcMain.handle(DESKTOP_AI_CANCEL_LOGIN, async (event) => {
guard(event);
await service.cancelLogin();
});
ipcMain.handle(DESKTOP_AI_LOGOUT, async (event) => {
guard(event);
await service.logout();
});
ipcMain.handle(DESKTOP_AI_UPDATE_PREFERENCES, async (event, next) => {
guard(event);
await service.updatePreferences(next);
});
ipcMain.handle(DESKTOP_AI_RUN_TASK, async (event, request) => {
guard(event);
return service.runTask(request);
});
};
const ensureMainWindow = (): void => {
if (mainWindow) {
return;
@ -101,6 +179,20 @@ const ensureMainWindow = (): void => {
};
app.whenReady().then(() => {
desktopAiService = new IslandflowDesktopAiService(
app.getPath("userData"),
async (url) => {
await shell.openExternal(url);
},
() => {
broadcastDesktopAiState();
}
);
registerDesktopAiIpc(desktopAiService);
void desktopAiService.start().catch((error) => {
console.error("[desktop-ai] Failed to start Codex bridge:", error);
broadcastDesktopAiState();
});
ensureMainWindow();
app.on("activate", () => {