import { io, type Socket } from "socket.io-client"; import type { ChatEvent, ChatHistoryPayload, LiveRuntimeInfo, ModelListPayload, ModelRef, PersistedSessionInfo, RuntimeCreatedPayload, ThinkingLevel, } from "./types"; export type SocketCallbacks = { onConnect?: () => void; onDisconnect?: () => void; onError?: (message: string) => void; onSessionsList?: (payload: { persisted: PersistedSessionInfo[]; live: LiveRuntimeInfo[]; }) => void; onRuntimeCreated?: (payload: RuntimeCreatedPayload) => void; onRuntimeOpened?: (payload: RuntimeCreatedPayload) => void; onChatEvent?: (payload: ChatEvent) => void; onChatHistory?: (payload: ChatHistoryPayload) => void; }; export function createSocket( baseUrl: string, callbacks: SocketCallbacks, token?: string, ): Socket { const socket = io(baseUrl, { transports: ["websocket"], autoConnect: true, auth: token ? { token } : undefined, }); socket.on("connect", () => callbacks.onConnect?.()); socket.on("disconnect", () => callbacks.onDisconnect?.()); socket.on("connect_error", (error) => { callbacks.onError?.(error.message || "Connection failed"); }); socket.on("error", (payload: { message?: string }) => { callbacks.onError?.(payload.message ?? "Socket error"); }); socket.on("sessions:list:result", (payload) => callbacks.onSessionsList?.(payload)); socket.on("runtime:created", (payload) => callbacks.onRuntimeCreated?.(payload)); socket.on("runtime:opened", (payload) => callbacks.onRuntimeOpened?.(payload)); socket.on("chat:event", (payload) => callbacks.onChatEvent?.(payload)); socket.on("chat:history", (payload) => callbacks.onChatHistory?.(payload)); return socket; } export function listModels( socket: Socket, payload: { runtimeId?: string; cwd?: string }, timeoutMs = 10000, ): Promise { return new Promise((resolve, reject) => { socket.timeout(timeoutMs).emit( "models:list", payload, (error: Error | null, response: ModelListPayload | { error: string }) => { if (error) { reject(new Error("Timed out loading models")); } else if ("error" in response) { reject(new Error(response.error)); } else { resolve(response); } }, ); }); } export function setRuntimeModel( socket: Socket, runtimeId: string, model: ModelRef, timeoutMs = 10000, ): Promise { return new Promise((resolve, reject) => { socket.timeout(timeoutMs).emit( "runtime:model:set", { runtimeId, model }, ( error: Error | null, response: { runtime: LiveRuntimeInfo } | { error: string }, ) => { if (error) { reject(new Error("Timed out setting model")); } else if ("error" in response) { reject(new Error(response.error)); } else { resolve(response.runtime); } }, ); }); } export function setRuntimeThinkingLevel( socket: Socket, runtimeId: string, level: ThinkingLevel, timeoutMs = 10000, ): Promise { return new Promise((resolve, reject) => { socket.timeout(timeoutMs).emit( "runtime:thinking:set", { runtimeId, level }, (error: Error | null, response: { runtime: LiveRuntimeInfo } | { error: string }) => { if (error) { reject(new Error("Timed out setting reasoning effort")); } else if ("error" in response) { reject(new Error(response.error)); } else { resolve(response.runtime); } }, ); }); }