2026-03-20 20:06:21 +02:00
|
|
|
import { app, ipcMain, shell } from 'electron';
|
|
|
|
|
import { IPC_CHANNELS } from './channels';
|
2026-04-02 13:26:32 +02:00
|
|
|
import {
|
|
|
|
|
getPermissionsStatus,
|
|
|
|
|
requestAccessibility,
|
2026-04-03 19:57:48 +02:00
|
|
|
requestScreenRecording,
|
2026-04-02 13:26:32 +02:00
|
|
|
restartApp,
|
|
|
|
|
} from '../modules/permissions';
|
2026-04-02 11:17:49 +05:30
|
|
|
import {
|
|
|
|
|
selectFolder,
|
|
|
|
|
addWatchedFolder,
|
|
|
|
|
removeWatchedFolder,
|
|
|
|
|
getWatchedFolders,
|
|
|
|
|
getWatcherStatus,
|
2026-04-03 00:40:49 +05:30
|
|
|
getPendingFileEvents,
|
|
|
|
|
acknowledgeFileEvents,
|
2026-04-02 11:17:49 +05:30
|
|
|
pauseWatcher,
|
|
|
|
|
resumeWatcher,
|
2026-04-02 22:20:11 +05:30
|
|
|
markRendererReady,
|
2026-04-03 02:56:24 +05:30
|
|
|
browseFiles,
|
2026-04-03 00:28:24 +05:30
|
|
|
readLocalFiles,
|
2026-04-08 15:46:52 +05:30
|
|
|
listFolderFiles,
|
2026-04-08 16:07:25 +05:30
|
|
|
seedFolderMtimes,
|
2026-04-08 15:46:52 +05:30
|
|
|
type WatchedFolderConfig,
|
2026-04-02 11:17:49 +05:30
|
|
|
} from '../modules/folder-watcher';
|
2026-04-07 00:43:40 -07:00
|
|
|
import { getShortcuts, setShortcuts, type ShortcutConfig } from '../modules/shortcuts';
|
2026-04-20 12:42:06 -07:00
|
|
|
import { getAutoLaunchState, setAutoLaunch } from '../modules/auto-launch';
|
2026-04-07 04:45:48 -07:00
|
|
|
import { getActiveSearchSpaceId, setActiveSearchSpaceId } from '../modules/active-search-space';
|
2026-04-07 00:43:40 -07:00
|
|
|
import { reregisterQuickAsk } from '../modules/quick-ask';
|
|
|
|
|
import { reregisterAutocomplete } from '../modules/autocomplete';
|
2026-04-07 03:42:46 -07:00
|
|
|
import { reregisterGeneralAssist } from '../modules/tray';
|
2026-04-18 14:35:14 -07:00
|
|
|
import {
|
|
|
|
|
getDistinctId,
|
|
|
|
|
getMachineId,
|
|
|
|
|
identifyUser as analyticsIdentify,
|
|
|
|
|
resetUser as analyticsReset,
|
|
|
|
|
trackEvent,
|
|
|
|
|
} from '../modules/analytics';
|
2026-04-23 15:45:59 +05:30
|
|
|
import {
|
2026-04-23 17:23:38 +05:30
|
|
|
readAgentLocalFileText,
|
|
|
|
|
writeAgentLocalFileText,
|
2026-04-24 05:03:23 +05:30
|
|
|
getAgentFilesystemMounts,
|
2026-04-23 15:45:59 +05:30
|
|
|
getAgentFilesystemSettings,
|
|
|
|
|
pickAgentFilesystemRoot,
|
|
|
|
|
setAgentFilesystemSettings,
|
|
|
|
|
} from '../modules/agent-filesystem';
|
2026-03-20 20:06:21 +02:00
|
|
|
|
2026-04-06 23:02:25 -07:00
|
|
|
let authTokens: { bearer: string; refresh: string } | null = null;
|
|
|
|
|
|
2026-03-20 20:06:21 +02:00
|
|
|
export function registerIpcHandlers(): void {
|
|
|
|
|
ipcMain.on(IPC_CHANNELS.OPEN_EXTERNAL, (_event, url: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const parsed = new URL(url);
|
|
|
|
|
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
|
|
|
|
shell.openExternal(url);
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// invalid URL — ignore
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_APP_VERSION, () => {
|
|
|
|
|
return app.getVersion();
|
|
|
|
|
});
|
2026-04-02 13:26:32 +02:00
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_PERMISSIONS_STATUS, () => {
|
|
|
|
|
return getPermissionsStatus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.REQUEST_ACCESSIBILITY, () => {
|
|
|
|
|
requestAccessibility();
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-03 19:57:48 +02:00
|
|
|
ipcMain.handle(IPC_CHANNELS.REQUEST_SCREEN_RECORDING, () => {
|
|
|
|
|
requestScreenRecording();
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-02 13:26:32 +02:00
|
|
|
ipcMain.handle(IPC_CHANNELS.RESTART_APP, () => {
|
|
|
|
|
restartApp();
|
|
|
|
|
});
|
2026-04-04 09:15:13 +02:00
|
|
|
|
2026-04-02 11:17:49 +05:30
|
|
|
// Folder sync handlers
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_SELECT_FOLDER, () => selectFolder());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_ADD_FOLDER, (_event, config) =>
|
|
|
|
|
addWatchedFolder(config)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_REMOVE_FOLDER, (_event, folderPath: string) =>
|
|
|
|
|
removeWatchedFolder(folderPath)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_GET_FOLDERS, () => getWatchedFolders());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_GET_STATUS, () => getWatcherStatus());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_PAUSE, () => pauseWatcher());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_RESUME, () => resumeWatcher());
|
2026-04-02 22:20:11 +05:30
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_RENDERER_READY, () => {
|
|
|
|
|
markRendererReady();
|
|
|
|
|
});
|
2026-04-03 00:28:24 +05:30
|
|
|
|
2026-04-03 00:40:49 +05:30
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_GET_PENDING_EVENTS, () =>
|
|
|
|
|
getPendingFileEvents()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_ACK_EVENTS, (_event, eventIds: string[]) =>
|
|
|
|
|
acknowledgeFileEvents(eventIds)
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-08 15:46:52 +05:30
|
|
|
ipcMain.handle(IPC_CHANNELS.FOLDER_SYNC_LIST_FILES, (_event, config: WatchedFolderConfig) =>
|
|
|
|
|
listFolderFiles(config)
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-08 16:07:25 +05:30
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.FOLDER_SYNC_SEED_MTIMES,
|
|
|
|
|
(_event, folderPath: string, mtimes: Record<string, number>) =>
|
|
|
|
|
seedFolderMtimes(folderPath, mtimes),
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-03 02:56:24 +05:30
|
|
|
ipcMain.handle(IPC_CHANNELS.BROWSE_FILES, () => browseFiles());
|
2026-04-03 00:28:24 +05:30
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.READ_LOCAL_FILES, (_event, paths: string[]) =>
|
|
|
|
|
readLocalFiles(paths)
|
|
|
|
|
);
|
2026-04-06 23:02:25 -07:00
|
|
|
|
2026-04-23 17:23:38 +05:30
|
|
|
ipcMain.handle(IPC_CHANNELS.READ_AGENT_LOCAL_FILE_TEXT, async (_event, virtualPath: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await readAgentLocalFileText(virtualPath);
|
|
|
|
|
return { ok: true, path: result.path, content: result.content };
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to read local file';
|
|
|
|
|
return { ok: false, path: virtualPath, error: message };
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.WRITE_AGENT_LOCAL_FILE_TEXT,
|
|
|
|
|
async (_event, virtualPath: string, content: string) => {
|
|
|
|
|
try {
|
|
|
|
|
const result = await writeAgentLocalFileText(virtualPath, content);
|
|
|
|
|
return { ok: true, path: result.path };
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const message = error instanceof Error ? error.message : 'Failed to write local file';
|
|
|
|
|
return { ok: false, path: virtualPath, error: message };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-06 23:02:25 -07:00
|
|
|
ipcMain.handle(IPC_CHANNELS.SET_AUTH_TOKENS, (_event, tokens: { bearer: string; refresh: string }) => {
|
|
|
|
|
authTokens = tokens;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_AUTH_TOKENS, () => {
|
|
|
|
|
return authTokens;
|
|
|
|
|
});
|
2026-04-07 00:43:40 -07:00
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_SHORTCUTS, () => getShortcuts());
|
|
|
|
|
|
2026-04-20 12:42:06 -07:00
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_AUTO_LAUNCH, () => getAutoLaunchState());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.SET_AUTO_LAUNCH,
|
|
|
|
|
async (_event, payload: { enabled: boolean; openAsHidden?: boolean }) => {
|
|
|
|
|
const next = await setAutoLaunch(payload.enabled, payload.openAsHidden);
|
|
|
|
|
trackEvent('desktop_auto_launch_toggled', {
|
|
|
|
|
enabled: next.enabled,
|
|
|
|
|
open_as_hidden: next.openAsHidden,
|
|
|
|
|
supported: next.supported,
|
|
|
|
|
});
|
|
|
|
|
return next;
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-07 04:45:48 -07:00
|
|
|
ipcMain.handle(IPC_CHANNELS.GET_ACTIVE_SEARCH_SPACE, () => getActiveSearchSpaceId());
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.SET_ACTIVE_SEARCH_SPACE, (_event, id: string) =>
|
|
|
|
|
setActiveSearchSpaceId(id)
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-07 00:43:40 -07:00
|
|
|
ipcMain.handle(IPC_CHANNELS.SET_SHORTCUTS, async (_event, config: Partial<ShortcutConfig>) => {
|
|
|
|
|
const updated = await setShortcuts(config);
|
2026-04-07 03:42:46 -07:00
|
|
|
if (config.generalAssist) await reregisterGeneralAssist();
|
2026-04-07 00:43:40 -07:00
|
|
|
if (config.quickAsk) await reregisterQuickAsk();
|
|
|
|
|
if (config.autocomplete) await reregisterAutocomplete();
|
2026-04-18 14:35:14 -07:00
|
|
|
trackEvent('desktop_shortcut_updated', {
|
|
|
|
|
keys: Object.keys(config),
|
|
|
|
|
});
|
2026-04-07 00:43:40 -07:00
|
|
|
return updated;
|
|
|
|
|
});
|
2026-04-18 14:35:14 -07:00
|
|
|
|
|
|
|
|
// Analytics bridge — the renderer (web UI) hands the logged-in user down
|
|
|
|
|
// to the main process so desktop-only events are attributed to the same
|
|
|
|
|
// PostHog person, not just an anonymous machine ID.
|
|
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.ANALYTICS_IDENTIFY,
|
|
|
|
|
(_event, payload: { userId: string; properties?: Record<string, unknown> }) => {
|
|
|
|
|
if (!payload?.userId) return;
|
|
|
|
|
analyticsIdentify(String(payload.userId), payload.properties);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.ANALYTICS_RESET, () => {
|
|
|
|
|
analyticsReset();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.ANALYTICS_CAPTURE,
|
|
|
|
|
(_event, payload: { event: string; properties?: Record<string, unknown> }) => {
|
|
|
|
|
if (!payload?.event) return;
|
|
|
|
|
trackEvent(payload.event, payload.properties);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.ANALYTICS_GET_CONTEXT, () => {
|
|
|
|
|
return {
|
|
|
|
|
distinctId: getDistinctId(),
|
|
|
|
|
machineId: getMachineId(),
|
|
|
|
|
appVersion: app.getVersion(),
|
|
|
|
|
platform: process.platform,
|
|
|
|
|
};
|
|
|
|
|
});
|
2026-04-23 15:45:59 +05:30
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.AGENT_FILESYSTEM_GET_SETTINGS, () =>
|
|
|
|
|
getAgentFilesystemSettings()
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-24 05:03:23 +05:30
|
|
|
ipcMain.handle(IPC_CHANNELS.AGENT_FILESYSTEM_GET_MOUNTS, () =>
|
|
|
|
|
getAgentFilesystemMounts()
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-23 15:45:59 +05:30
|
|
|
ipcMain.handle(
|
|
|
|
|
IPC_CHANNELS.AGENT_FILESYSTEM_SET_SETTINGS,
|
2026-04-24 01:45:13 +05:30
|
|
|
(_event, settings: { mode?: 'cloud' | 'desktop_local_folder'; localRootPaths?: string[] | null }) =>
|
2026-04-23 15:45:59 +05:30
|
|
|
setAgentFilesystemSettings(settings)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
ipcMain.handle(IPC_CHANNELS.AGENT_FILESYSTEM_PICK_ROOT, () =>
|
|
|
|
|
pickAgentFilesystemRoot()
|
|
|
|
|
);
|
2026-03-20 20:06:21 +02:00
|
|
|
}
|