- Added @github/copilot-sdk and related dependencies for GitHub Copilot integration. - Implemented ChatCopilot adapter for LangChain compatibility. - Created LLM factory to return provider-specific models (OpenAI, Anthropic, Copilot). - Developed Orchestrator agent using LangGraph for intent routing and context assembly. - Enhanced IPC communication for streaming AI responses to the renderer. - Updated progress documentation with implementation details and learnings.
96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
/**
|
|
* Custom electron IPC ↔ tRPC v11 bridge.
|
|
*
|
|
* Replaces the incompatible electron-trpc package (v0.7.x bundles tRPC v10's
|
|
* callProcedure which checks `procedure._def[type]`, while tRPC v11 uses
|
|
* `procedure._def.type`).
|
|
*/
|
|
import { ipcMain, type BrowserWindow } from 'electron';
|
|
import {
|
|
callTRPCProcedure,
|
|
getErrorShape,
|
|
getTRPCErrorFromUnknown,
|
|
transformTRPCResponse,
|
|
type AnyRouter,
|
|
} from '@trpc/server';
|
|
|
|
export const IPC_CHANNEL = 'trpc';
|
|
|
|
/** Context passed to every tRPC procedure via the IPC bridge. */
|
|
export type TRPCContext = {
|
|
/** The IPC sender — available for streaming chunks back to the renderer. */
|
|
sender?: Electron.WebContents;
|
|
};
|
|
|
|
interface IPCRequest {
|
|
method: 'request';
|
|
operation: {
|
|
id: number;
|
|
type: 'query' | 'mutation' | 'subscription';
|
|
path: string;
|
|
input?: unknown;
|
|
};
|
|
}
|
|
|
|
export function createIPCHandler<TRouter extends AnyRouter>({
|
|
router,
|
|
windows = [],
|
|
}: {
|
|
router: TRouter;
|
|
windows?: BrowserWindow[];
|
|
}) {
|
|
const config = router._def._config;
|
|
|
|
ipcMain.on(IPC_CHANNEL, async (event, message: IPCRequest) => {
|
|
if (message.method !== 'request') return;
|
|
|
|
const { id, type, path, input } = message.operation;
|
|
|
|
// Deserialize input through transformer (identity by default)
|
|
const rawInput = input !== undefined
|
|
? config.transformer.input.deserialize(input)
|
|
: undefined;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const respond = (response: any) => {
|
|
if (event.sender.isDestroyed()) return;
|
|
const serialised = transformTRPCResponse(config, response);
|
|
event.reply(IPC_CHANNEL, serialised);
|
|
};
|
|
|
|
try {
|
|
const result = await callTRPCProcedure({
|
|
router,
|
|
path,
|
|
getRawInput: async () => rawInput,
|
|
ctx: { sender: event.sender } satisfies TRPCContext,
|
|
type,
|
|
signal: undefined as unknown as AbortSignal,
|
|
batchIndex: 0,
|
|
});
|
|
|
|
respond({ id, result: { type: 'data', data: result } });
|
|
} catch (cause) {
|
|
const error = getTRPCErrorFromUnknown(cause);
|
|
respond({
|
|
id,
|
|
error: getErrorShape({
|
|
config,
|
|
error,
|
|
type,
|
|
path,
|
|
input: rawInput,
|
|
ctx: {},
|
|
}),
|
|
});
|
|
}
|
|
});
|
|
|
|
// Attach additional windows later if needed
|
|
return {
|
|
attachWindow(_win: BrowserWindow) {
|
|
// No per-window setup needed — ipcMain.on is global
|
|
},
|
|
};
|
|
}
|