UNPKG

naisys

Version:

NAISYS - Autonomous AI agent runner with built-in context management and cost tracking

112 lines 4.43 kB
import { resolveHostAccessKey } from "@naisys/common-node"; import { HubConnectErrorDataSchema } from "@naisys/hub-protocol"; import { io } from "socket.io-client"; export function createHubConnection(hubClientConfig, hubClientLog, raiseEvent, onConnected, onDisconnected, onConnectError) { const hubUrl = hubClientConfig.hubUrl; // Extract origin and base path from hub URL (e.g. "http://localhost:3300/hub" → origin + "/hub") // Socket.IO needs origin for connection and base path for its transport path const hubUrlParsed = new URL(hubUrl); const hubOrigin = hubUrlParsed.origin; const hubBasePath = hubUrlParsed.pathname.replace(/\/$/, ""); let socket = null; let connected = false; function connect() { hubClientLog.log(`[NAISYS:HubClient] Connecting to ${hubUrl}...`); const accessKey = resolveHostAccessKey(); if (!accessKey) { onConnectError({ message: "HOST_ACCESS_KEY is not set" }); return; } socket = io(hubOrigin, { path: hubBasePath + "/socket.io", extraHeaders: { "ngrok-skip-browser-warning": "true", }, auth: (cb) => { // Re-read access key on each connection attempt so rotated keys are picked up cb({ accessKey: resolveHostAccessKey(), instanceId: hubClientConfig.instanceId, startedAt: hubClientConfig.processStartedAt, clientVersion: hubClientConfig.clientVersion, environment: hubClientConfig.environment, }); }, reconnection: true, reconnectionDelay: 1000, reconnectionDelayMax: 30000, }); socket.on("connect", () => { connected = true; hubClientLog.log(`[NAISYS:HubClient] Connected to ${hubUrl}`); onConnected(); }); socket.on("disconnect", (reason) => { connected = false; hubClientLog.log(`[NAISYS:HubClient] Disconnected from ${hubUrl}: ${reason}`); onDisconnected(); // Server-initiated disconnects don't auto-reconnect in Socket.IO. // Always retry; the server's connect_error (fatal vs non-fatal) decides // whether to give up — covers key rotation, supersede, same-instance replace. if (reason === "io server disconnect") { socket?.connect(); } }); socket.on("connect_error", (error) => { const parsedData = HubConnectErrorDataSchema.safeParse(error.data); const connectError = { message: error.message, data: parsedData.success ? parsedData.data : undefined, }; hubClientLog.log(`[NAISYS:HubClient] Connection error to ${hubUrl}: ${connectError.message}${connectError.data ? ` (${connectError.data.code})` : ""}`); onConnectError(connectError); }); // Forward all socket events to hubClient's event handlers socket.onAny((eventName, ...args) => { hubClientLog.log(`[NAISYS:HubClient] Received ${eventName} from ${hubUrl}`); raiseEvent(eventName, ...args); }); } function disconnect() { const currentSocket = socket; socket = null; connected = false; if (currentSocket) { currentSocket.disconnect(); hubClientLog.log(`[NAISYS:HubClient] Disconnected from ${hubUrl}`); } } function isConnected() { return connected; } function getUrl() { return hubUrl; } /** * Send a message to the hub with optional acknowledgement callback. * @param event - Event name * @param payload - Message payload * @param ack - Optional callback for acknowledgement * @returns true if message was sent, false if not connected */ function sendMessage(event, payload, ack) { if (!socket || !connected) { return false; } if (ack) { socket.emit(event, payload, ack); } else { socket.emit(event, payload); } return true; } return { connect, disconnect, isConnected, getUrl, sendMessage, }; } //# sourceMappingURL=hubConnection.js.map