Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
410 lines
22 KiB
TypeScript
410 lines
22 KiB
TypeScript
import { app, dialog, ipcMain, shell, type BrowserWindow } from "electron";
|
|
import type { DatabaseConnection } from "../database/connection";
|
|
import { IPC_CHANNELS } from "../../shared/ipc";
|
|
import { fail, ok, type DesktopResult } from "../../shared/result";
|
|
import type { AppSnapshot } from "../../shared/domain";
|
|
import { SnapshotService } from "../services/snapshot-service";
|
|
import { OrganizationService } from "../services/organization-service";
|
|
import { ApprovalService } from "../services/approval-service";
|
|
import { ChatService } from "../services/chat-service";
|
|
import { ProviderService } from "../services/provider-service";
|
|
import { ProviderOAuthService } from "../services/provider-oauth-service";
|
|
import { SkillService } from "../services/skill-service";
|
|
import { SocialService } from "../services/social-service";
|
|
import { FileService } from "../services/file-service";
|
|
import { MigrationService } from "../services/migration-service";
|
|
import { OnboardingService } from "../services/onboarding-service";
|
|
import { ComplianceService } from "../services/compliance-service";
|
|
import { SearchService } from "../services/search-service";
|
|
import { IdentityAccessService } from "../services/identity-access-service";
|
|
import { OperationsService } from "../services/operations-service";
|
|
import { SdlcService } from "../services/sdlc-service";
|
|
import { AuditService } from "../services/audit-service";
|
|
import { RollbackService } from "../services/rollback-service";
|
|
import { TaskExecutionService } from "../services/task-execution-service";
|
|
import { DocumentService } from "../services/document-service";
|
|
import { KnowledgeService } from "../services/knowledge-service";
|
|
import { AutomationService } from "../services/automation-service";
|
|
import { MeetingService } from "../services/meeting-service";
|
|
import { SprintService } from "../services/sprint-service";
|
|
import { ComposioConnectionService } from "../services/composio/composio-connection-service";
|
|
import { IntegrationKeyStore } from "../services/integration-key-store";
|
|
import { importStagedSkills, stageBuiltinSkillsLibrary } from "../services/builtin-skills";
|
|
import { coalesce } from "./coalesce";
|
|
import { normalizeExternalBrowserUrl } from "./external-url";
|
|
|
|
export interface IpcDeps {
|
|
connection: DatabaseConnection;
|
|
mainWindow: BrowserWindow;
|
|
artifactRoot: string;
|
|
/** Bundled built-in skills source (repo folder in dev, resources when packaged). */
|
|
builtinSkillsSource: string;
|
|
/** Normalized on-disk skill library under the profile directory. */
|
|
builtinSkillsLibrary: string;
|
|
}
|
|
|
|
export function registerIpcHandlers(deps: IpcDeps): void {
|
|
const snapshot = new SnapshotService(deps.connection.db);
|
|
const organization = new OrganizationService(deps.connection.db);
|
|
const approvals = new ApprovalService(deps.connection.db);
|
|
const chat = new ChatService(deps.connection.db);
|
|
const providers = new ProviderService(deps.connection.db);
|
|
const integrationKeys = new IntegrationKeyStore(deps.connection.db);
|
|
const providerOAuth = new ProviderOAuthService(integrationKeys);
|
|
const skills = new SkillService(deps.connection.db, deps.artifactRoot);
|
|
const notifyDataChanged = coalesce(() => {
|
|
try {
|
|
if (!deps.mainWindow.isDestroyed() && !deps.mainWindow.webContents.isDestroyed()) {
|
|
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
|
}
|
|
} catch { /* renderer may have been disposed during agent run */ }
|
|
}, 200);
|
|
const taskExecution = new TaskExecutionService(deps.connection.db, providers, skills, notifyDataChanged);
|
|
const social = new SocialService(deps.connection.db);
|
|
const files = new FileService(deps.connection.db, deps.artifactRoot);
|
|
const migrations = new MigrationService(deps.connection.db);
|
|
const compliance = new ComplianceService(deps.connection.db);
|
|
const search = new SearchService(deps.connection.db);
|
|
const identityAccess = new IdentityAccessService(deps.connection.db);
|
|
const operations = new OperationsService(deps.connection.db);
|
|
const sdlc = new SdlcService(deps.connection.db);
|
|
const audit = new AuditService(deps.connection.db);
|
|
const rollback = new RollbackService(deps.connection.db);
|
|
const documents = new DocumentService(deps.connection.db);
|
|
const knowledge = new KnowledgeService(deps.connection.db);
|
|
const automation = new AutomationService(deps.connection.db);
|
|
const meetingService = new MeetingService(deps.connection.db);
|
|
const sprintService = new SprintService(deps.connection.db);
|
|
const onboarding = new OnboardingService({
|
|
db: deps.connection.db,
|
|
saveProviderCredential: (input) => providers.saveCredential(input),
|
|
});
|
|
|
|
const mutate = <T>(operation: () => T): DesktopResult<AppSnapshot> => {
|
|
try {
|
|
deps.connection.transaction(operation);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("operation_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
};
|
|
|
|
const query = <T>(code: string, read: () => T): DesktopResult<T> => {
|
|
try {
|
|
return ok(read());
|
|
} catch (error) {
|
|
return fail(code, error instanceof Error ? error.message : String(error));
|
|
}
|
|
};
|
|
|
|
ipcMain.handle(IPC_CHANNELS.loadSnapshot, () => {
|
|
try {
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("snapshot_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.bootstrapOnboarding, (_event, input) => {
|
|
try {
|
|
let summary: { orgId: string } | undefined;
|
|
// Staging happens before the transaction (file mirroring), the import
|
|
// inside it: the CEO's kickoff run can be claimed within seconds of
|
|
// commit and must already find the forced humanizer skill.
|
|
const staged = stageBuiltinSkillsLibrary(deps.builtinSkillsSource, deps.builtinSkillsLibrary);
|
|
deps.connection.transaction(() => {
|
|
summary = onboarding.bootstrap(input);
|
|
const report = importStagedSkills(skills, summary.orgId, staged);
|
|
if (report.failures.length > 0) {
|
|
console.warn(`[skills] onboarding import failures (${report.failures.length}): ${report.failures.slice(0, 3).join("; ")}`);
|
|
}
|
|
});
|
|
notifyDataChanged();
|
|
return ok(summary);
|
|
} catch (error) {
|
|
return fail("onboarding_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.createOrganization, (_event, input) => mutate(() => organization.createOrganization(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setActiveOrganization, (_event, input) => mutate(() => organization.setActiveOrganization(input.orgId)));
|
|
ipcMain.handle(IPC_CHANNELS.setUiPreferences, (_event, input) => {
|
|
const before = snapshot.load().uiPreferences.language;
|
|
const result = mutate(() => organization.updateUiPreferences(input));
|
|
if (result.ok && result.data.uiPreferences.language !== before) {
|
|
// Chromium's locale is fixed at launch; a language change needs a
|
|
// fresh process to take effect.
|
|
setTimeout(() => {
|
|
app.relaunch();
|
|
app.quit();
|
|
}, 150);
|
|
}
|
|
return result;
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.deleteOrganization, (_event, input) => mutate(() => organization.deleteOrganization(input.orgId)));
|
|
ipcMain.handle(IPC_CHANNELS.createAgent, (_event, input) => mutate(() => organization.createAgent(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateAgent, (_event, input) => mutate(() => organization.updateAgent(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateEmployeeProfile, (_event, input) => mutate(() => organization.updateEmployeeProfile(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setAutonomousPaused, (_event, input) => mutate(() => organization.setAutonomousPaused(Boolean(input.paused))));
|
|
ipcMain.handle(IPC_CHANNELS.setIntegrationKey, (_event, input) => mutate(() => integrationKeys.write(input.name, input.value)));
|
|
ipcMain.handle(IPC_CHANNELS.clearIntegrationKey, (_event, input) => mutate(() => integrationKeys.clear(input.name)));
|
|
ipcMain.handle(IPC_CHANNELS.createTask, (_event, input) => mutate(() => organization.createTask(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateTask, (_event, input) => mutate(() => organization.updateTask(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteTask, (_event, input) => mutate(() => organization.deleteTask(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateTaskStatus, (_event, input) => mutate(() => organization.updateTaskStatus(input.orgId, input.taskId, input.status)));
|
|
ipcMain.handle(IPC_CHANNELS.runTask, async (_event, input) => {
|
|
try {
|
|
await taskExecution.runTask(input);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
notifyDataChanged();
|
|
return fail("task_run_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.createGoal, (_event, input) => mutate(() => organization.createGoal(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createProject, (_event, input) => mutate(() => organization.createProject(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createWorkspace, (_event, input) => mutate(() => organization.createWorkspace(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createDocument, (_event, input) => mutate(() => documents.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateDocument, (_event, input) => mutate(() => documents.update(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteDocument, (_event, input) => mutate(() => documents.delete(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createKnowledgeEntry, (_event, input) => mutate(() => knowledge.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteKnowledgeEntry, (_event, input) => mutate(() => knowledge.delete(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateKnowledgeEntry, (_event, input) => mutate(() => knowledge.update(input)));
|
|
ipcMain.handle(IPC_CHANNELS.listDocumentVersions, (_event, input) => query("document_versions_failed", () =>
|
|
documents.versions(input.orgId, input.documentId).map((v) => ({
|
|
version: v.version, title: v.title, content: v.content, status: v.status,
|
|
changedByType: v.changed_by_type, changedById: v.changed_by_id, reason: v.reason, createdAt: v.created_at,
|
|
}))));
|
|
ipcMain.handle(IPC_CHANNELS.listKnowledgeRevisions, (_event, input) => query("knowledge_revisions_failed", () =>
|
|
knowledge.revisions(input.orgId, input.entryId).map((r) => ({
|
|
revision: r.revision, topic: r.topic, category: r.category, content: r.content, importance: r.importance,
|
|
changedByType: r.changed_by_type, changedById: r.changed_by_id, reason: r.reason, createdAt: r.created_at,
|
|
}))));
|
|
ipcMain.handle(IPC_CHANNELS.createAutomationRule, (_event, input) => mutate(() => automation.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateAutomationRule, (_event, input) => mutate(() => automation.update(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteAutomationRule, (_event, input) => mutate(() => automation.delete(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createMeeting, (_event, input) => mutate(() => meetingService.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteMeeting, (_event, input) => mutate(() => meetingService.delete(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createSprint, (_event, input) => mutate(() => sprintService.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteSprint, (_event, input) => mutate(() => sprintService.delete(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createApproval, (_event, input) => mutate(() => approvals.create(input)));
|
|
ipcMain.handle(IPC_CHANNELS.decideApproval, (_event, input) => mutate(() => approvals.decide(input)));
|
|
ipcMain.handle(IPC_CHANNELS.syncEmployeeIdentity, (_event, input) => mutate(() => identityAccess.syncEmployeeIdentity(input)));
|
|
ipcMain.handle(IPC_CHANNELS.grantRole, (_event, input) => mutate(() => identityAccess.grantRole(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createChatSpace, (_event, input) => mutate(() => chat.createSpace(input)));
|
|
ipcMain.handle(IPC_CHANNELS.sendMessage, (_event, input) => mutate(() => chat.sendMessage(input)));
|
|
ipcMain.handle(IPC_CHANNELS.editMessage, (_event, input) => mutate(() => chat.editMessage(input)));
|
|
ipcMain.handle(IPC_CHANNELS.deleteMessage, (_event, input) => mutate(() => chat.deleteMessage(input)));
|
|
ipcMain.handle(IPC_CHANNELS.markChatSpaceRead, (_event, input) => mutate(() => chat.markSpaceRead(input)));
|
|
ipcMain.handle(IPC_CHANNELS.saveProviderCredential, (_event, input) => mutate(() => providers.saveCredential(input)));
|
|
ipcMain.handle(IPC_CHANNELS.revokeProviderCredential, (_event, input) => mutate(() => providers.revokeCredential(input)));
|
|
ipcMain.handle(IPC_CHANNELS.updateProviderPolicy, (_event, input) => mutate(() => providers.updatePolicy(input)));
|
|
ipcMain.handle(IPC_CHANNELS.checkProviderHealth, async (_event, input) => {
|
|
try {
|
|
await providers.checkHealth(input.orgId, input.providerId);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("provider_health_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.listProviderModels, async (_event, input) => {
|
|
try {
|
|
return ok(await providers.listModels(input));
|
|
} catch (error) {
|
|
return fail("provider_models_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.launchProviderSignIn, (_event, input) => {
|
|
try {
|
|
providers.launchSignInWindow(input);
|
|
return ok(null);
|
|
} catch (error) {
|
|
return fail("provider_sign_in_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.startProviderOAuth, async (_event, input) => {
|
|
const emit = (event: Record<string, unknown>) =>
|
|
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "provider-oauth", providerKey: input.providerKey, ...event });
|
|
try {
|
|
const secret = await providerOAuth.run(
|
|
{ providerKey: input.providerKey, enterpriseDomain: input.enterpriseDomain ?? null },
|
|
{
|
|
onAuth: ({ url, instructions }) => {
|
|
const browserUrl = normalizeExternalBrowserUrl(url);
|
|
void shell.openExternal(browserUrl);
|
|
emit({ phase: "authorize", url: browserUrl, instructions });
|
|
},
|
|
onProgress: (message) => emit({ phase: "progress", message }),
|
|
},
|
|
);
|
|
if (input.orgId && input.providerId && input.employeeId) {
|
|
providers.saveCredential({
|
|
orgId: input.orgId,
|
|
providerId: input.providerId,
|
|
secret,
|
|
model: input.model ?? null,
|
|
employeeId: input.employeeId,
|
|
});
|
|
emit({ phase: "active" });
|
|
notifyDataChanged();
|
|
return ok({ secret: null });
|
|
}
|
|
emit({ phase: "active" });
|
|
return ok({ secret });
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
emit({ phase: message === "Login cancelled" ? "cancelled" : "error", message });
|
|
return fail("provider_oauth_failed", message);
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.cancelProviderOAuth, (_event, input) => {
|
|
providerOAuth.cancel(input.providerKey);
|
|
return ok(null);
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.openExternalUrl, async (_event, url: string) => {
|
|
try {
|
|
await shell.openExternal(normalizeExternalBrowserUrl(url));
|
|
return ok(null);
|
|
} catch (error) {
|
|
return fail("external_url_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.createProviderResponse, async (_event, input) => {
|
|
try {
|
|
await providers.createResponse(input);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("provider_response_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.streamProviderResponse, async (_event, input) => {
|
|
try {
|
|
await providers.streamResponse(input, (streamEvent) => {
|
|
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "provider-response-stream", event: streamEvent });
|
|
});
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("provider_stream_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.cancelProviderResponse, async (_event, input) => {
|
|
try {
|
|
await providers.cancelResponse(input);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("provider_cancel_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.createSocialAccount, (_event, input) => mutate(() => social.createAccount(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createScheduledPost, (_event, input) => mutate(() => social.createScheduledPost(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setScheduledPostState, (_event, input) => mutate(() => social.setScheduledPostState(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setFilePinned, (_event, input) => mutate(() => files.setPinned(input.orgId, input.fileId, input.pinned)));
|
|
|
|
ipcMain.handle(IPC_CHANNELS.launchSocialLogin, (_event, input) => {
|
|
try {
|
|
social.launchLoginWindow(input);
|
|
return ok(null);
|
|
} catch (error) {
|
|
return fail("social_login_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.importFile, async (_event, input) => {
|
|
try {
|
|
const fileId = await files.importFile(input);
|
|
if (fileId) notifyDataChanged();
|
|
return ok(fileId);
|
|
} catch (error) {
|
|
return fail("file_import_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.openFile, async (_event, input) => {
|
|
try {
|
|
await files.openFile(input);
|
|
return ok(null);
|
|
} catch (error) {
|
|
return fail("file_open_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.pickDirectory, async () => {
|
|
try {
|
|
const result = await dialog.showOpenDialog(deps.mainWindow, { properties: ["openDirectory", "createDirectory"] });
|
|
if (result.canceled || result.filePaths.length === 0) return ok(null);
|
|
return ok(result.filePaths[0]);
|
|
} catch (error) {
|
|
return fail("pick_directory_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
ipcMain.handle(IPC_CHANNELS.createMigrationReport, (_event, input) => mutate(() => migrations.createReport(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createRollbackSnapshot, (_event, input) => mutate(() => rollback.createSnapshot(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setRollbackSnapshotActive, (_event, input) => mutate(() => rollback.setActive(input)));
|
|
ipcMain.handle(IPC_CHANNELS.resolveMigrationReconciliation, (_event, input) => mutate(() => rollback.resolveReconciliationItem(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setFeatureFlag, (_event, input) => mutate(() => operations.setFeatureFlag(input)));
|
|
ipcMain.handle(IPC_CHANNELS.runChaosDrill, (_event, input) => mutate(() => operations.runChaosDrill(input)));
|
|
ipcMain.handle(IPC_CHANNELS.runSdlcReview, (_event, input) => mutate(() => sdlc.runReview(input)));
|
|
ipcMain.handle(IPC_CHANNELS.runEvalSuite, (_event, input) => mutate(() => sdlc.runEvalSuite(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setPrBotGate, (_event, input) => mutate(() => sdlc.setPrBotGate(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createComplianceExport, (_event, input) => mutate(() => compliance.createExport(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createAuditReplay, (_event, input) => mutate(() => audit.createThreadReplay(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createLegalHold, (_event, input) => mutate(() => compliance.createLegalHold(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setLegalHoldState, (_event, input) => mutate(() => compliance.setLegalHoldState(input)));
|
|
ipcMain.handle(IPC_CHANNELS.createRetentionFreeze, (_event, input) => mutate(() => compliance.createRetentionFreeze(input)));
|
|
ipcMain.handle(IPC_CHANNELS.setRetentionFreezeState, (_event, input) => mutate(() => compliance.setRetentionFreezeState(input)));
|
|
ipcMain.handle(IPC_CHANNELS.search, (_event, input) => {
|
|
try {
|
|
return ok(search.search(input));
|
|
} catch (error) {
|
|
return fail("search_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
|
|
const composioConnections = new ComposioConnectionService(deps.connection.db, integrationKeys);
|
|
|
|
ipcMain.handle(IPC_CHANNELS.listConnectorCatalog, async () => {
|
|
try {
|
|
return ok(await composioConnections.listCatalog());
|
|
} catch (error) {
|
|
return fail("composio_catalog_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.connectComposioToolkit, async (_event, input) => {
|
|
try {
|
|
const result = await composioConnections.connectToolkit(input.orgId, input.toolkitSlug);
|
|
notifyDataChanged();
|
|
return ok(result);
|
|
} catch (error) {
|
|
return fail("composio_connect_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.disconnectComposioToolkit, async (_event, input) => {
|
|
try {
|
|
await composioConnections.disconnectToolkit(input.orgId, input.toolkitSlug);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("composio_disconnect_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.refreshComposioAccounts, async (_event, input) => {
|
|
try {
|
|
await composioConnections.refreshAccounts(input.orgId);
|
|
notifyDataChanged();
|
|
return ok(snapshot.load());
|
|
} catch (error) {
|
|
return fail("composio_refresh_failed", error instanceof Error ? error.message : String(error));
|
|
}
|
|
});
|
|
ipcMain.handle(IPC_CHANNELS.windowMinimize, () => deps.mainWindow.minimize());
|
|
ipcMain.handle(IPC_CHANNELS.windowMaximize, () => deps.mainWindow.isMaximized() ? deps.mainWindow.unmaximize() : deps.mainWindow.maximize());
|
|
ipcMain.handle(IPC_CHANNELS.windowClose, () => deps.mainWindow.close());
|
|
}
|