From 66f66d1c17213f55aa56d69c0cccc309b16f3362 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 28 Feb 2026 07:27:49 -0800 Subject: Phase 3: IPC layer - Implement src/main/preload.ts with typed API bridge - Projects, sessions, messages CRUD - Chat send/interrupt - Workflow review/advance/permissions - Artifact read/write - Directory picker dialog - Claude message event subscription - Implement src/main/ipc/handlers.ts - All IPC handlers with proper error handling - Message forwarding to renderer - Assistant message storage - Update src/main/index.ts - Initialize database on startup - Register IPC handlers - Clean database close on exit --- src/main/preload.ts | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) (limited to 'src/main/preload.ts') diff --git a/src/main/preload.ts b/src/main/preload.ts index 0b39d91..b3e3f8b 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -1 +1,114 @@ -// Expose nothing for now; keep the door open for future IPC-safe APIs. +import { contextBridge, ipcRenderer, type IpcRendererEvent } from "electron"; +import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk"; +import type { Project } from "./db/projects"; +import type { Session, Message } from "./db/sessions"; +import type { Phase, UserPermissionMode } from "./claude/phases"; + +export interface ClaudeFlowAPI { + // Projects + listProjects: () => Promise; + createProject: (name: string, path: string) => Promise; + deleteProject: (id: string) => Promise; + + // Sessions + listSessions: (projectId: string) => Promise; + createSession: (projectId: string, name: string) => Promise; + deleteSession: (id: string) => Promise; + getSession: (id: string) => Promise; + + // Messages + listMessages: (sessionId: string) => Promise; + + // Chat + sendMessage: (sessionId: string, message: string) => Promise; + interruptSession: (sessionId: string) => Promise; + + // Workflow + triggerReview: (sessionId: string) => Promise; + advancePhase: (sessionId: string) => Promise; + setPermissionMode: ( + sessionId: string, + mode: UserPermissionMode + ) => Promise; + + // Artifacts + readArtifact: ( + projectPath: string, + filename: string + ) => Promise; + writeArtifact: ( + projectPath: string, + filename: string, + content: string + ) => Promise; + + // Events + onClaudeMessage: ( + callback: (sessionId: string, message: SDKMessage) => void + ) => () => void; + + // Dialogs + selectDirectory: () => Promise; +} + +const api: ClaudeFlowAPI = { + // Projects + listProjects: () => ipcRenderer.invoke("projects:list"), + createProject: (name, path) => + ipcRenderer.invoke("projects:create", name, path), + deleteProject: (id) => ipcRenderer.invoke("projects:delete", id), + + // Sessions + listSessions: (projectId) => ipcRenderer.invoke("sessions:list", projectId), + createSession: (projectId, name) => + ipcRenderer.invoke("sessions:create", projectId, name), + deleteSession: (id) => ipcRenderer.invoke("sessions:delete", id), + getSession: (id) => ipcRenderer.invoke("sessions:get", id), + + // Messages + listMessages: (sessionId) => ipcRenderer.invoke("messages:list", sessionId), + + // Chat + sendMessage: (sessionId, message) => + ipcRenderer.invoke("chat:send", sessionId, message), + interruptSession: (sessionId) => + ipcRenderer.invoke("chat:interrupt", sessionId), + + // Workflow + triggerReview: (sessionId) => ipcRenderer.invoke("workflow:review", sessionId), + advancePhase: (sessionId) => ipcRenderer.invoke("workflow:advance", sessionId), + setPermissionMode: (sessionId, mode) => + ipcRenderer.invoke("workflow:setPermissionMode", sessionId, mode), + + // Artifacts + readArtifact: (projectPath, filename) => + ipcRenderer.invoke("artifact:read", projectPath, filename), + writeArtifact: (projectPath, filename, content) => + ipcRenderer.invoke("artifact:write", projectPath, filename, content), + + // Events + onClaudeMessage: (callback) => { + const handler = ( + _: IpcRendererEvent, + sessionId: string, + message: SDKMessage + ) => callback(sessionId, message); + ipcRenderer.on("claude:message", handler); + return () => ipcRenderer.removeListener("claude:message", handler); + }, + + // Dialogs + selectDirectory: async () => { + const result = await ipcRenderer.invoke("dialog:selectDirectory"); + return result; + }, +}; + +contextBridge.exposeInMainWorld("api", api); + +// Type declaration for renderer +declare global { + interface Window { + api: ClaudeFlowAPI; + } +} -- cgit v1.2.3