@naisys/hub 3.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/naisys-hub +2 -0
- package/dist/handlers/hubAccessKeyService.js +28 -0
- package/dist/handlers/hubAgentService.js +238 -0
- package/dist/handlers/hubAttachmentService.js +227 -0
- package/dist/handlers/hubConfigService.js +85 -0
- package/dist/handlers/hubCostService.js +279 -0
- package/dist/handlers/hubHeartbeatService.js +132 -0
- package/dist/handlers/hubHostService.js +35 -0
- package/dist/handlers/hubLogService.js +121 -0
- package/dist/handlers/hubMailService.js +397 -0
- package/dist/handlers/hubModelsService.js +106 -0
- package/dist/handlers/hubRunService.js +96 -0
- package/dist/handlers/hubSendMailService.js +126 -0
- package/dist/handlers/hubUserService.js +62 -0
- package/dist/naisysHub.js +126 -0
- package/dist/services/accessKeyService.js +32 -0
- package/dist/services/agentRegistrar.js +53 -0
- package/dist/services/hostRegistrar.js +82 -0
- package/dist/services/hubServerLog.js +47 -0
- package/dist/services/naisysConnection.js +45 -0
- package/dist/services/naisysServer.js +164 -0
- package/package.json +57 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { createNaisysConnection } from "./naisysConnection.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates the NAISYS namespace server that accepts NAISYS instance connections.
|
|
4
|
+
*/
|
|
5
|
+
export function createNaisysServer(nsp, initialHubAccessKey, logService, hostRegistrar) {
|
|
6
|
+
let hubAccessKey = initialHubAccessKey;
|
|
7
|
+
// Track connected NAISYS instances (keyed by hostId)
|
|
8
|
+
const naisysConnections = new Map();
|
|
9
|
+
// Track connected supervisor instances (multiple allowed, all share one hostId)
|
|
10
|
+
const supervisorConnections = [];
|
|
11
|
+
// Generic event handlers registry - maps event name to set of registered handlers
|
|
12
|
+
const eventHandlers = new Map();
|
|
13
|
+
function registerEvent(event, handler, schema) {
|
|
14
|
+
if (!eventHandlers.has(event)) {
|
|
15
|
+
eventHandlers.set(event, new Set());
|
|
16
|
+
}
|
|
17
|
+
eventHandlers.get(event).add({ handler, schema });
|
|
18
|
+
}
|
|
19
|
+
// Unregister an event handler
|
|
20
|
+
function unregisterEvent(event, handler) {
|
|
21
|
+
const handlers = eventHandlers.get(event);
|
|
22
|
+
if (handlers) {
|
|
23
|
+
for (const registered of handlers) {
|
|
24
|
+
if (registered.handler === handler) {
|
|
25
|
+
handlers.delete(registered);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Emit an event to all registered handlers, validating data if schema provided
|
|
32
|
+
function raiseEvent(event, hostId, ...args) {
|
|
33
|
+
const handlers = eventHandlers.get(event);
|
|
34
|
+
if (handlers) {
|
|
35
|
+
for (const { handler, schema } of handlers) {
|
|
36
|
+
// If schema is provided and we have data, validate it
|
|
37
|
+
if (schema && args.length > 0) {
|
|
38
|
+
const result = schema.safeParse(args[0]);
|
|
39
|
+
if (!result.success) {
|
|
40
|
+
logService.error(`[Hub] Schema validation failed for event '${event}' from ${hostId}: ${JSON.stringify(result.error.issues)}`);
|
|
41
|
+
continue; // Skip this handler if validation fails
|
|
42
|
+
}
|
|
43
|
+
// Call handler with validated data
|
|
44
|
+
handler(hostId, result.data, ...args.slice(1));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
handler(hostId, ...args);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function sendMessage(hostId, event, payload, ack) {
|
|
53
|
+
const connection = naisysConnections.get(hostId);
|
|
54
|
+
if (!connection) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
connection.sendMessage(event, payload, ack);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
// Authentication middleware
|
|
61
|
+
nsp.use(async (socket, next) => {
|
|
62
|
+
const { hubAccessKey: clientAccessKey, hostName, hostType: rawHostType, } = socket.handshake.auth;
|
|
63
|
+
if (!clientAccessKey || clientAccessKey !== hubAccessKey) {
|
|
64
|
+
logService.log(`[Hub] Connection rejected: invalid access key from ${socket.handshake.address}`);
|
|
65
|
+
return next(new Error("Invalid access key"));
|
|
66
|
+
}
|
|
67
|
+
if (!hostName) {
|
|
68
|
+
logService.log(`[Hub] Connection rejected: missing hostName`);
|
|
69
|
+
return next(new Error("Missing hostName"));
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const hostType = (typeof rawHostType === "string" ? rawHostType : "naisys");
|
|
73
|
+
const hostId = await hostRegistrar.registerHost(hostName, hostType, socket.handshake.address);
|
|
74
|
+
// Reject duplicate naisys connections (supervisors may have multiple)
|
|
75
|
+
if (hostType === "naisys" && naisysConnections.has(hostId)) {
|
|
76
|
+
logService.log(`[Hub] Connection rejected: host '${hostName}' is already connected`);
|
|
77
|
+
return next(new Error(`Host '${hostName}' already has an active connection`));
|
|
78
|
+
}
|
|
79
|
+
socket.data.hostId = hostId;
|
|
80
|
+
socket.data.hostName = hostName;
|
|
81
|
+
socket.data.hostType = hostType;
|
|
82
|
+
next();
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logService.error(`[Hub] Connection rejected: failed to register host ${hostName}: ${err}`);
|
|
86
|
+
return next(new Error("NAISYS instance registration failed"));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Handle new connections
|
|
90
|
+
nsp.on("connection", (socket) => {
|
|
91
|
+
const { hostId, hostName, hostType } = socket.data;
|
|
92
|
+
// Create connection handler for this socket, passing our emit function
|
|
93
|
+
const connection = createNaisysConnection(socket, {
|
|
94
|
+
hostId,
|
|
95
|
+
hostName,
|
|
96
|
+
connectedAt: new Date(),
|
|
97
|
+
hostType,
|
|
98
|
+
}, raiseEvent, logService);
|
|
99
|
+
if (hostType === "supervisor") {
|
|
100
|
+
supervisorConnections.push(connection);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
naisysConnections.set(hostId, connection);
|
|
104
|
+
}
|
|
105
|
+
raiseEvent("client_connected", hostId, connection);
|
|
106
|
+
logService.log(`[Hub] Active connections: naisys=${naisysConnections.size}, supervisors=${supervisorConnections.length}`);
|
|
107
|
+
// Clean up on disconnect
|
|
108
|
+
socket.on("disconnect", () => {
|
|
109
|
+
if (hostType === "supervisor") {
|
|
110
|
+
const idx = supervisorConnections.indexOf(connection);
|
|
111
|
+
if (idx !== -1)
|
|
112
|
+
supervisorConnections.splice(idx, 1);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
naisysConnections.delete(hostId);
|
|
116
|
+
}
|
|
117
|
+
raiseEvent("client_disconnected", hostId);
|
|
118
|
+
logService.log(`[Hub] Active connections: naisys=${naisysConnections.size}, supervisors=${supervisorConnections.length}`);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
/** Update the hub access key used for authenticating new connections */
|
|
122
|
+
function updateHubAccessKey(newKey) {
|
|
123
|
+
hubAccessKey = newKey;
|
|
124
|
+
}
|
|
125
|
+
/** Disconnect all connected clients */
|
|
126
|
+
function disconnectAllClients() {
|
|
127
|
+
for (const connection of naisysConnections.values()) {
|
|
128
|
+
connection.disconnect();
|
|
129
|
+
}
|
|
130
|
+
for (const connection of supervisorConnections) {
|
|
131
|
+
connection.disconnect();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Broadcast an event to all supervisor connections */
|
|
135
|
+
function broadcastToSupervisors(event, payload) {
|
|
136
|
+
for (const conn of supervisorConnections) {
|
|
137
|
+
conn.sendMessage(event, payload);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Broadcast an event to all connections (naisys + supervisors) */
|
|
141
|
+
function broadcastToAll(event, payload) {
|
|
142
|
+
for (const conn of naisysConnections.values()) {
|
|
143
|
+
conn.sendMessage(event, payload);
|
|
144
|
+
}
|
|
145
|
+
for (const conn of supervisorConnections) {
|
|
146
|
+
conn.sendMessage(event, payload);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Return control interface
|
|
150
|
+
return {
|
|
151
|
+
registerEvent,
|
|
152
|
+
unregisterEvent,
|
|
153
|
+
sendMessage,
|
|
154
|
+
broadcastToSupervisors,
|
|
155
|
+
broadcastToAll,
|
|
156
|
+
getConnectedClients: () => Array.from(naisysConnections.values()),
|
|
157
|
+
getConnectionByHostId: (hostId) => naisysConnections.get(hostId),
|
|
158
|
+
getConnectionCount: () => naisysConnections.size,
|
|
159
|
+
getSupervisorConnectionCount: () => supervisorConnections.length,
|
|
160
|
+
updateHubAccessKey,
|
|
161
|
+
disconnectAllClients,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=naisysServer.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@naisys/hub",
|
|
3
|
+
"version": "3.0.0-beta.10",
|
|
4
|
+
"description": "NAISYS Hub - Adds persistence and multi-instance coordination to NAISYS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/naisysHub.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"naisys-hub": "bin/naisys-hub"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/naisysHub.js",
|
|
12
|
+
"./services/hubServerLog": "./dist/services/hubServerLog.js",
|
|
13
|
+
"./services/runnerServer": "./dist/services/runnerServer.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"dev": "tsx watch src/naisysHub.ts",
|
|
18
|
+
"start": "node dist/naisysHub.js",
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"type-check": "tsc --noEmit",
|
|
21
|
+
"npm:publish:dryrun": "npm publish --dry-run",
|
|
22
|
+
"npm:publish": "npm publish --access public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"bin",
|
|
27
|
+
"!dist/**/*.map",
|
|
28
|
+
"!dist/**/*.d.ts",
|
|
29
|
+
"!dist/**/*.d.ts.map"
|
|
30
|
+
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@naisys/supervisor": "3.0.0-beta.10"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"@naisys/supervisor": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@naisys/common": "3.0.0-beta.10",
|
|
41
|
+
"@naisys/common-node": "3.0.0-beta.10",
|
|
42
|
+
"@naisys/hub-database": "3.0.0-beta.10",
|
|
43
|
+
"@naisys/hub-protocol": "3.0.0-beta.10",
|
|
44
|
+
"commander": "^14.0.3",
|
|
45
|
+
"dotenv": "^17.3.1",
|
|
46
|
+
"pino": "^10.3.1",
|
|
47
|
+
"socket.io": "^4.8.3"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^25.5.0",
|
|
51
|
+
"tsx": "^4.21.0",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=22.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|