rowboat/apps/x/packages/shared/src/ipc.ts

613 lines
15 KiB
TypeScript
Raw Normal View History

2025-12-29 15:30:57 +05:30
import { z } from 'zod';
import { RelPath, Encoding, Stat, DirEntry, ReaddirOptions, ReadFileResult, WorkspaceChangeEvent, WriteFileOptions, WriteFileResult, RemoveOptions } from './workspace.js';
import { ListToolsResponse } from './mcp.js';
2026-01-16 09:48:11 +05:30
import { AskHumanResponsePayload, CreateRunOptions, Run, ListRunsResponse, ToolPermissionAuthorizePayload } from './runs.js';
import { LlmModelConfig } from './models.js';
import { AgentScheduleConfig, AgentScheduleEntry } from './agent-schedule.js';
import { AgentScheduleState } from './agent-schedule-state.js';
import { ServiceEvent } from './service-events.js';
import { UserMessageContent } from './message.js';
2025-12-29 15:30:57 +05:30
// ============================================================================
// Runtime Validation Schemas (Single Source of Truth)
// ============================================================================
const ipcSchemas = {
'app:getVersions': {
req: z.null(),
res: z.object({
chrome: z.string(),
node: z.string(),
electron: z.string(),
}),
},
'workspace:getRoot': {
req: z.null(),
res: z.object({
root: z.string(),
}),
},
'workspace:exists': {
req: z.object({
path: RelPath,
}),
res: z.object({
exists: z.boolean(),
}),
},
'workspace:stat': {
req: z.object({
path: RelPath,
}),
res: Stat,
},
'workspace:readdir': {
req: z.object({
path: z.string(), // Empty string allowed for root directory
opts: ReaddirOptions.optional(),
}),
res: z.array(DirEntry),
},
'workspace:readFile': {
req: z.object({
path: RelPath,
encoding: Encoding.optional(),
}),
res: ReadFileResult,
},
'workspace:writeFile': {
req: z.object({
path: RelPath,
data: z.string(),
opts: WriteFileOptions.optional(),
}),
res: WriteFileResult,
},
'workspace:mkdir': {
req: z.object({
path: RelPath,
recursive: z.boolean().optional(),
}),
res: z.object({
ok: z.literal(true),
}),
},
'workspace:rename': {
req: z.object({
from: RelPath,
to: RelPath,
overwrite: z.boolean().optional(),
}),
res: z.object({
ok: z.literal(true),
}),
},
'workspace:copy': {
req: z.object({
from: RelPath,
to: RelPath,
overwrite: z.boolean().optional(),
}),
res: z.object({
ok: z.literal(true),
}),
},
'workspace:remove': {
req: z.object({
path: RelPath,
opts: RemoveOptions.optional(),
}),
res: z.object({
ok: z.literal(true),
}),
},
'workspace:didChange': {
req: WorkspaceChangeEvent,
res: z.null(),
},
'mcp:listTools': {
req: z.object({
serverName: z.string(),
cursor: z.string().optional(),
}),
res: ListToolsResponse,
},
'mcp:executeTool': {
req: z.object({
serverName: z.string(),
toolName: z.string(),
input: z.record(z.string(), z.unknown()),
}),
res: z.object({
result: z.unknown(),
}),
},
'runs:create': {
req: CreateRunOptions,
res: Run,
},
'runs:createMessage': {
req: z.object({
runId: z.string(),
message: UserMessageContent,
2026-03-13 10:26:08 +05:30
voiceInput: z.boolean().optional(),
voiceOutput: z.enum(['summary', 'full']).optional(),
2026-03-13 13:58:30 +05:30
searchEnabled: z.boolean().optional(),
2025-12-29 15:30:57 +05:30
}),
res: z.object({
messageId: z.string(),
}),
},
'runs:authorizePermission': {
req: z.object({
runId: z.string(),
authorization: ToolPermissionAuthorizePayload,
}),
res: z.object({
success: z.literal(true),
}),
},
'runs:provideHumanInput': {
req: z.object({
runId: z.string(),
reply: AskHumanResponsePayload,
}),
res: z.object({
success: z.literal(true),
}),
},
'runs:stop': {
req: z.object({
runId: z.string(),
force: z.boolean().optional().default(false),
2025-12-29 15:30:57 +05:30
}),
res: z.object({
success: z.literal(true),
}),
},
2026-01-16 09:48:11 +05:30
'runs:fetch': {
req: z.object({
runId: z.string(),
}),
res: Run,
},
'runs:list': {
req: z.object({
cursor: z.string().optional(),
}),
res: ListRunsResponse,
},
2026-02-16 15:28:20 +05:30
'runs:delete': {
req: z.object({
runId: z.string(),
}),
res: z.object({ success: z.boolean() }),
},
2025-12-29 15:30:57 +05:30
'runs:events': {
req: z.null(),
res: z.null(),
2026-01-06 06:56:42 +05:30
},
'services:events': {
req: ServiceEvent,
res: z.null(),
},
'models:list': {
req: z.null(),
res: z.object({
providers: z.array(z.object({
id: z.string(),
name: z.string(),
models: z.array(z.object({
id: z.string(),
name: z.string().optional(),
release_date: z.string().optional(),
})),
})),
lastUpdated: z.string().optional(),
}),
},
'models:test': {
req: LlmModelConfig,
res: z.object({
success: z.boolean(),
error: z.string().optional(),
}),
},
'models:saveConfig': {
req: LlmModelConfig,
res: z.object({
success: z.literal(true),
}),
},
2026-01-06 06:56:42 +05:30
'oauth:connect': {
req: z.object({
provider: z.string(),
clientId: z.string().optional(),
2026-01-06 06:56:42 +05:30
}),
res: z.object({
success: z.boolean(),
error: z.string().optional(),
}),
},
'oauth:disconnect': {
req: z.object({
provider: z.string(),
}),
res: z.object({
success: z.boolean(),
}),
},
'oauth:list-providers': {
req: z.null(),
res: z.object({
providers: z.array(z.string()),
}),
},
'oauth:getState': {
2026-01-06 06:56:42 +05:30
req: z.null(),
res: z.object({
config: z.record(z.string(), z.object({
connected: z.boolean(),
2026-02-21 22:58:38 +05:30
error: z.string().nullable().optional(),
})),
2026-01-06 06:56:42 +05:30
}),
},
2026-01-20 15:18:47 +05:30
'oauth:didConnect': {
req: z.object({
provider: z.string(),
success: z.boolean(),
error: z.string().optional(),
}),
res: z.null(),
},
2026-01-08 11:19:43 +05:30
'granola:getConfig': {
req: z.null(),
res: z.object({
enabled: z.boolean(),
}),
},
'granola:setConfig': {
req: z.object({
enabled: z.boolean(),
}),
res: z.object({
success: z.literal(true),
}),
},
'slack:getConfig': {
req: z.null(),
res: z.object({
enabled: z.boolean(),
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
}),
},
'slack:setConfig': {
req: z.object({
enabled: z.boolean(),
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
}),
res: z.object({
success: z.literal(true),
}),
},
'slack:listWorkspaces': {
req: z.null(),
res: z.object({
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
error: z.string().optional(),
}),
},
2026-01-21 20:29:24 +05:30
'onboarding:getStatus': {
req: z.null(),
res: z.object({
showOnboarding: z.boolean(),
}),
},
'onboarding:markComplete': {
req: z.null(),
res: z.object({
success: z.literal(true),
}),
},
// Composio integration channels
'composio:is-configured': {
req: z.null(),
res: z.object({
configured: z.boolean(),
}),
},
'composio:set-api-key': {
req: z.object({
apiKey: z.string(),
}),
res: z.object({
success: z.boolean(),
error: z.string().optional(),
}),
},
'composio:initiate-connection': {
req: z.object({
toolkitSlug: z.string(),
}),
res: z.object({
success: z.boolean(),
redirectUrl: z.string().optional(),
connectedAccountId: z.string().optional(),
error: z.string().optional(),
}),
},
'composio:get-connection-status': {
req: z.object({
toolkitSlug: z.string(),
}),
res: z.object({
isConnected: z.boolean(),
status: z.string().optional(),
}),
},
'composio:sync-connection': {
req: z.object({
toolkitSlug: z.string(),
connectedAccountId: z.string(),
}),
res: z.object({
status: z.string(),
}),
},
'composio:disconnect': {
req: z.object({
toolkitSlug: z.string(),
}),
res: z.object({
success: z.boolean(),
}),
},
'composio:list-connected': {
req: z.null(),
res: z.object({
toolkits: z.array(z.string()),
}),
},
'composio:execute-action': {
req: z.object({
actionSlug: z.string(),
toolkitSlug: z.string(),
input: z.record(z.string(), z.unknown()),
}),
res: z.object({
data: z.unknown(),
2026-03-15 23:39:48 +05:30
successful: z.boolean(),
error: z.string().nullable(),
}),
},
2026-03-02 17:52:23 +05:30
'composio:use-composio-for-google': {
req: z.null(),
res: z.object({
enabled: z.boolean(),
}),
},
Onboarding rebased (#426) * Enhance onboarding modal to support multiple paths (Rowboat and BYOK). v1 * new onboarding flow * Resolve stash merge conflicts: keep both inline-task and billing features Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor billing information structure and API integration * onboarding ui refactor * Update import path for getAccessToken in billing.ts to reflect new directory structure * Implement Gmail integration with Composio, enhancing onboarding flow to support Gmail connection status and API key management. Update ConnectorsPopover and SettingsDialog components to reflect new functionality, including dynamic tab visibility based on Rowboat connection status. * use composio for calendar * Enhance onboarding flow to support Google Calendar integration with Composio. Add state management for Google Calendar connection status, loading states, and connection handling. Update UI components to reflect Google Calendar connectivity in onboarding steps. * Integrate Google Calendar sync functionality with Composio, enhancing the connection handling in composio-handler and oauth-handler. Update onboarding modal and connectors-popover to manage connection states and provide user feedback during the sync process. Implement Composio-based event syncing in sync_calendar.ts. * Maximize window on ready-to-show event in main.ts to improve user experience by preventing blank screen on launch. * Enhance WelcomeStep component in onboarding flow with new feature highlights and animations. Introduce icons for memory, connectivity, and privacy features. Update logo display with ambient glow effect and improve user feedback during connection states. * Refactor voice availability check in App component to trigger on OAuth state changes. Update SidebarContentPanel to enhance billing display with improved styling and clearer upgrade call-to-action. * Enhance OAuth event handling by notifying the renderer on provider disconnection. Update ConnectorsPopover to listen for OAuth state changes, and refine WelcomeStep component by removing feature highlights and adjusting layout for improved user experience. * Implement Rowboat model settings in the settings dialog, including loading and saving model configurations based on Rowboat connection status. Enhance chat input component to manage Rowboat connection state and update model loading logic accordingly. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com>
2026-03-17 12:04:57 +05:30
'composio:use-composio-for-google-calendar': {
req: z.null(),
res: z.object({
enabled: z.boolean(),
}),
},
'composio:didConnect': {
req: z.object({
toolkitSlug: z.string(),
success: z.boolean(),
error: z.string().optional(),
}),
res: z.null(),
},
// Agent schedule channels
'agent-schedule:getConfig': {
req: z.null(),
res: AgentScheduleConfig,
},
'agent-schedule:getState': {
req: z.null(),
res: AgentScheduleState,
},
'agent-schedule:updateAgent': {
req: z.object({
agentName: z.string(),
entry: AgentScheduleEntry,
}),
res: z.object({
success: z.literal(true),
}),
},
'agent-schedule:deleteAgent': {
req: z.object({
agentName: z.string(),
}),
res: z.object({
success: z.literal(true),
}),
},
// Shell integration channels
'shell:openPath': {
req: z.object({ path: z.string() }),
res: z.object({ error: z.string().optional() }),
},
'shell:readFileBase64': {
req: z.object({ path: z.string() }),
res: z.object({ data: z.string(), mimeType: z.string(), size: z.number() }),
},
2026-02-27 20:22:54 +05:30
// Knowledge version history channels
'knowledge:history': {
req: z.object({ path: RelPath }),
res: z.object({
commits: z.array(z.object({
oid: z.string(),
message: z.string(),
timestamp: z.number(),
author: z.string(),
})),
}),
},
'knowledge:fileAtCommit': {
req: z.object({ path: RelPath, oid: z.string() }),
res: z.object({ content: z.string() }),
},
'knowledge:restore': {
req: z.object({ path: RelPath, oid: z.string() }),
res: z.object({ ok: z.literal(true) }),
},
'knowledge:didCommit': {
req: z.object({}),
res: z.null(),
},
// Search channels
'search:query': {
req: z.object({
query: z.string(),
limit: z.number().optional(),
types: z.array(z.enum(['knowledge', 'chat'])).optional(),
}),
res: z.object({
results: z.array(z.object({
type: z.enum(['knowledge', 'chat']),
title: z.string(),
preview: z.string(),
path: z.string(),
})),
}),
},
2026-03-13 10:26:08 +05:30
// Voice mode channels
'voice:getConfig': {
req: z.null(),
res: z.object({
deepgram: z.object({ apiKey: z.string() }).nullable(),
elevenlabs: z.object({ apiKey: z.string(), voiceId: z.string().optional() }).nullable(),
}),
},
'voice:synthesize': {
req: z.object({
text: z.string(),
}),
res: z.object({
audioBase64: z.string(),
mimeType: z.string(),
}),
},
2026-03-13 22:26:17 +05:30
'voice:getDeepgramToken': {
req: z.null(),
res: z.object({
token: z.string(),
}).nullable(),
},
'meeting:summarize': {
req: z.object({
transcript: z.string(),
meetingStartTime: z.string().optional(),
}),
res: z.object({
notes: z.string(),
}),
},
2026-03-04 22:15:15 +05:30
// Inline task schedule classification
'export:note': {
req: z.object({
markdown: z.string(),
format: z.enum(['md', 'pdf', 'docx']),
title: z.string(),
}),
res: z.object({
success: z.boolean(),
error: z.string().optional(),
}),
},
2026-03-04 22:15:15 +05:30
'inline-task:classifySchedule': {
req: z.object({
instruction: z.string(),
}),
res: z.object({
schedule: z.union([
z.object({ type: z.literal('cron'), expression: z.string(), startDate: z.string(), endDate: z.string(), label: z.string() }),
z.object({ type: z.literal('window'), cron: z.string(), startTime: z.string(), endTime: z.string(), startDate: z.string(), endDate: z.string(), label: z.string() }),
z.object({ type: z.literal('once'), runAt: z.string(), label: z.string() }),
]).nullable(),
}),
},
'inline-task:process': {
req: z.object({
instruction: z.string(),
noteContent: z.string(),
notePath: z.string(),
}),
res: z.object({
instruction: z.string(),
schedule: z.union([
z.object({ type: z.literal('cron'), expression: z.string(), startDate: z.string(), endDate: z.string() }),
z.object({ type: z.literal('window'), cron: z.string(), startTime: z.string(), endTime: z.string(), startDate: z.string(), endDate: z.string() }),
z.object({ type: z.literal('once'), runAt: z.string() }),
]).nullable(),
scheduleLabel: z.string().nullable(),
response: z.string().nullable(),
}),
},
Onboarding rebased (#426) * Enhance onboarding modal to support multiple paths (Rowboat and BYOK). v1 * new onboarding flow * Resolve stash merge conflicts: keep both inline-task and billing features Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor billing information structure and API integration * onboarding ui refactor * Update import path for getAccessToken in billing.ts to reflect new directory structure * Implement Gmail integration with Composio, enhancing onboarding flow to support Gmail connection status and API key management. Update ConnectorsPopover and SettingsDialog components to reflect new functionality, including dynamic tab visibility based on Rowboat connection status. * use composio for calendar * Enhance onboarding flow to support Google Calendar integration with Composio. Add state management for Google Calendar connection status, loading states, and connection handling. Update UI components to reflect Google Calendar connectivity in onboarding steps. * Integrate Google Calendar sync functionality with Composio, enhancing the connection handling in composio-handler and oauth-handler. Update onboarding modal and connectors-popover to manage connection states and provide user feedback during the sync process. Implement Composio-based event syncing in sync_calendar.ts. * Maximize window on ready-to-show event in main.ts to improve user experience by preventing blank screen on launch. * Enhance WelcomeStep component in onboarding flow with new feature highlights and animations. Introduce icons for memory, connectivity, and privacy features. Update logo display with ambient glow effect and improve user feedback during connection states. * Refactor voice availability check in App component to trigger on OAuth state changes. Update SidebarContentPanel to enhance billing display with improved styling and clearer upgrade call-to-action. * Enhance OAuth event handling by notifying the renderer on provider disconnection. Update ConnectorsPopover to listen for OAuth state changes, and refine WelcomeStep component by removing feature highlights and adjusting layout for improved user experience. * Implement Rowboat model settings in the settings dialog, including loading and saving model configurations based on Rowboat connection status. Enhance chat input component to manage Rowboat connection state and update model loading logic accordingly. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com>
2026-03-17 12:04:57 +05:30
// Billing channels
'billing:getInfo': {
req: z.null(),
res: z.object({
userEmail: z.string().nullable(),
userId: z.string().nullable(),
Onboarding rebased (#426) * Enhance onboarding modal to support multiple paths (Rowboat and BYOK). v1 * new onboarding flow * Resolve stash merge conflicts: keep both inline-task and billing features Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refactor billing information structure and API integration * onboarding ui refactor * Update import path for getAccessToken in billing.ts to reflect new directory structure * Implement Gmail integration with Composio, enhancing onboarding flow to support Gmail connection status and API key management. Update ConnectorsPopover and SettingsDialog components to reflect new functionality, including dynamic tab visibility based on Rowboat connection status. * use composio for calendar * Enhance onboarding flow to support Google Calendar integration with Composio. Add state management for Google Calendar connection status, loading states, and connection handling. Update UI components to reflect Google Calendar connectivity in onboarding steps. * Integrate Google Calendar sync functionality with Composio, enhancing the connection handling in composio-handler and oauth-handler. Update onboarding modal and connectors-popover to manage connection states and provide user feedback during the sync process. Implement Composio-based event syncing in sync_calendar.ts. * Maximize window on ready-to-show event in main.ts to improve user experience by preventing blank screen on launch. * Enhance WelcomeStep component in onboarding flow with new feature highlights and animations. Introduce icons for memory, connectivity, and privacy features. Update logo display with ambient glow effect and improve user feedback during connection states. * Refactor voice availability check in App component to trigger on OAuth state changes. Update SidebarContentPanel to enhance billing display with improved styling and clearer upgrade call-to-action. * Enhance OAuth event handling by notifying the renderer on provider disconnection. Update ConnectorsPopover to listen for OAuth state changes, and refine WelcomeStep component by removing feature highlights and adjusting layout for improved user experience. * Implement Rowboat model settings in the settings dialog, including loading and saving model configurations based on Rowboat connection status. Enhance chat input component to manage Rowboat connection state and update model loading logic accordingly. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com>
2026-03-17 12:04:57 +05:30
subscriptionPlan: z.string().nullable(),
subscriptionStatus: z.string().nullable(),
sanctionedCredits: z.number(),
availableCredits: z.number(),
}),
},
2025-12-29 15:30:57 +05:30
} as const;
// ============================================================================
// Type Helpers
// ============================================================================
export type IPCChannels = {
[K in keyof typeof ipcSchemas]: {
req: z.infer<typeof ipcSchemas[K]['req']>;
res: z.infer<typeof ipcSchemas[K]['res']>;
};
};
/**
* Channels that use invoke/handle (request/response pattern)
* These are channels with non-null responses
*/
export type InvokeChannels = {
[K in keyof IPCChannels]:
IPCChannels[K]['res'] extends null ? never : K
}[keyof IPCChannels];
/**
* Channels that use send/on (fire-and-forget pattern)
* These are channels with null responses (no response expected)
*/
export type SendChannels = {
[K in keyof IPCChannels]:
IPCChannels[K]['res'] extends null ? K : never
}[keyof IPCChannels];
// ============================================================================
// Type Guards
// ============================================================================
export function validateRequest<K extends keyof IPCChannels>(
channel: K,
data: unknown
): IPCChannels[K]['req'] {
const schema = ipcSchemas[channel].req;
return schema.parse(data) as IPCChannels[K]['req'];
}
export function validateResponse<K extends keyof IPCChannels>(
channel: K,
data: unknown
): IPCChannels[K]['res'] {
const schema = ipcSchemas[channel].res;
return schema.parse(data) as IPCChannels[K]['res'];
}