149 lines
4.4 KiB
TypeScript
149 lines
4.4 KiB
TypeScript
import { BrowserWindow } from 'electron';
|
|
import { windowManager } from '@electron/service/window-service';
|
|
import logManager from '@electron/service/logger';
|
|
import { updateTrayStatus } from '@electron/main/tray';
|
|
import type { GatewayEvent, RuntimeRefreshTopic } from './types';
|
|
import * as chatHandlers from './handlers/chat';
|
|
import * as providerHandlers from './handlers/provider';
|
|
import * as skillHandlers from './handlers/skills';
|
|
|
|
type RuntimeChangeBroadcast = {
|
|
topics: RuntimeRefreshTopic[];
|
|
reason?: string;
|
|
warnings?: string[];
|
|
channelType?: string;
|
|
accountId?: string;
|
|
};
|
|
|
|
class GatewayManager {
|
|
private initialized = false;
|
|
private status: 'connected' | 'disconnected' | 'reconnecting' = 'disconnected';
|
|
|
|
private setStatus(status: 'connected' | 'disconnected' | 'reconnecting'): void {
|
|
this.status = status;
|
|
updateTrayStatus(status);
|
|
this.broadcast({ type: 'gateway:status', status });
|
|
}
|
|
|
|
async init(): Promise<void> {
|
|
if (this.initialized) return;
|
|
this.initialized = true;
|
|
logManager.info('GatewayManager initialized');
|
|
this.setStatus('connected');
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
await this.init();
|
|
}
|
|
|
|
async stop(): Promise<void> {
|
|
this.initialized = false;
|
|
this.setStatus('disconnected');
|
|
}
|
|
|
|
async restart(options?: RuntimeChangeBroadcast): Promise<void> {
|
|
this.initialized = false;
|
|
this.setStatus('reconnecting');
|
|
await this.init();
|
|
if (options) {
|
|
this.notifyRuntimeChanged(options);
|
|
}
|
|
}
|
|
|
|
getStatus(): {
|
|
status: 'connected' | 'disconnected' | 'reconnecting';
|
|
initialized: boolean;
|
|
mode: 'in-process';
|
|
} {
|
|
return {
|
|
status: this.status,
|
|
initialized: this.initialized,
|
|
mode: 'in-process',
|
|
};
|
|
}
|
|
|
|
async checkHealth(): Promise<{
|
|
ok: boolean;
|
|
status: 'connected' | 'disconnected' | 'reconnecting';
|
|
initialized: boolean;
|
|
mode: 'in-process';
|
|
}> {
|
|
return {
|
|
ok: this.initialized && this.status === 'connected',
|
|
...this.getStatus(),
|
|
};
|
|
}
|
|
|
|
async rpc(method: string, params: any): Promise<any> {
|
|
if (!this.initialized) {
|
|
await this.init();
|
|
}
|
|
|
|
logManager.info(`Gateway RPC: ${method}`, params);
|
|
|
|
switch (method) {
|
|
case 'chat.send':
|
|
return chatHandlers.handleChatSend(params, (event) => this.broadcast(event));
|
|
case 'chat.history':
|
|
return chatHandlers.handleChatHistory(params);
|
|
case 'chat.abort':
|
|
return chatHandlers.handleChatAbort(params, (event) => this.broadcast(event));
|
|
case 'session.list':
|
|
return chatHandlers.handleSessionList();
|
|
case 'session.delete':
|
|
return chatHandlers.handleSessionDelete(params);
|
|
case 'provider.list':
|
|
return providerHandlers.handleProviderList();
|
|
case 'provider.getDefault':
|
|
return providerHandlers.handleProviderGetDefault();
|
|
case 'skills.status':
|
|
return skillHandlers.handleSkillsStatus();
|
|
case 'skills.update':
|
|
return skillHandlers.handleSkillsUpdate(params);
|
|
default:
|
|
throw new Error(`Unknown gateway RPC method: ${method}`);
|
|
}
|
|
}
|
|
|
|
broadcast(event: GatewayEvent): void {
|
|
const mainWindow = BrowserWindow.getAllWindows().find(
|
|
(win) => windowManager.getName(win) === 'main'
|
|
);
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
mainWindow.webContents.send('gateway:event', event);
|
|
}
|
|
}
|
|
|
|
notifyRuntimeChanged(options: RuntimeChangeBroadcast): void {
|
|
const topics = Array.from(new Set(options.topics.filter(Boolean)));
|
|
if (topics.length === 0) {
|
|
return;
|
|
}
|
|
|
|
this.broadcast({
|
|
type: 'runtime:changed',
|
|
topics,
|
|
reason: options.reason,
|
|
warnings: options.warnings && options.warnings.length > 0 ? options.warnings : undefined,
|
|
channelType: options.channelType,
|
|
accountId: options.accountId,
|
|
syncedAt: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
reloadProviders(options?: RuntimeChangeBroadcast): void {
|
|
logManager.info('GatewayManager reloading providers');
|
|
// For now, providers are resolved on each chat.send call,
|
|
// so no in-memory cache to invalidate. Future: notify active sessions.
|
|
this.notifyRuntimeChanged({
|
|
topics: options?.topics ?? ['providers', 'models'],
|
|
reason: options?.reason ?? 'providers:reload',
|
|
warnings: options?.warnings,
|
|
channelType: options?.channelType,
|
|
accountId: options?.accountId,
|
|
});
|
|
}
|
|
}
|
|
|
|
export const gatewayManager = new GatewayManager();
|