@memo-code/memo 0.8.5 → 0.8.6
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/dist/web/server/main.cjs +145360 -0
- package/package.json +2 -1
- package/dist/web/server/app.controller.d.ts +0 -6
- package/dist/web/server/app.controller.js +0 -33
- package/dist/web/server/app.module.d.ts +0 -4
- package/dist/web/server/app.module.js +0 -61
- package/dist/web/server/auth/access-token.guard.d.ts +0 -9
- package/dist/web/server/auth/access-token.guard.js +0 -53
- package/dist/web/server/auth/auth.controller.d.ts +0 -18
- package/dist/web/server/auth/auth.controller.js +0 -75
- package/dist/web/server/auth/auth.module.d.ts +0 -2
- package/dist/web/server/auth/auth.module.js +0 -24
- package/dist/web/server/auth/auth.service.d.ts +0 -15
- package/dist/web/server/auth/auth.service.js +0 -146
- package/dist/web/server/auth/auth.types.d.ts +0 -26
- package/dist/web/server/auth/auth.types.js +0 -2
- package/dist/web/server/auth/public.decorator.d.ts +0 -2
- package/dist/web/server/auth/public.decorator.js +0 -7
- package/dist/web/server/chat/chat.controller.d.ts +0 -30
- package/dist/web/server/chat/chat.controller.js +0 -150
- package/dist/web/server/chat/chat.module.d.ts +0 -2
- package/dist/web/server/chat/chat.module.js +0 -26
- package/dist/web/server/chat/chat.service.d.ts +0 -61
- package/dist/web/server/chat/chat.service.js +0 -847
- package/dist/web/server/chat/chat.types.d.ts +0 -38
- package/dist/web/server/chat/chat.types.js +0 -2
- package/dist/web/server/common/constants.d.ts +0 -1
- package/dist/web/server/common/constants.js +0 -4
- package/dist/web/server/common/filters/api-error.filter.d.ts +0 -7
- package/dist/web/server/common/filters/api-error.filter.js +0 -95
- package/dist/web/server/common/interceptors/api-response.interceptor.d.ts +0 -15
- package/dist/web/server/common/interceptors/api-response.interceptor.js +0 -51
- package/dist/web/server/common/middleware/request-logging.middleware.d.ts +0 -7
- package/dist/web/server/common/middleware/request-logging.middleware.js +0 -42
- package/dist/web/server/config/memo-config.service.d.ts +0 -7
- package/dist/web/server/config/memo-config.service.js +0 -106
- package/dist/web/server/config/memo-config.types.d.ts +0 -6
- package/dist/web/server/config/memo-config.types.js +0 -2
- package/dist/web/server/config/server-config.module.d.ts +0 -2
- package/dist/web/server/config/server-config.module.js +0 -22
- package/dist/web/server/config/server-config.service.d.ts +0 -14
- package/dist/web/server/config/server-config.service.js +0 -326
- package/dist/web/server/config/server-config.service.test.d.ts +0 -1
- package/dist/web/server/config/server-config.service.test.js +0 -193
- package/dist/web/server/config/server-config.types.d.ts +0 -27
- package/dist/web/server/config/server-config.types.js +0 -2
- package/dist/web/server/main.d.ts +0 -1
- package/dist/web/server/main.js +0 -19
- package/dist/web/server/mcp/mcp.controller.d.ts +0 -38
- package/dist/web/server/mcp/mcp.controller.js +0 -126
- package/dist/web/server/mcp/mcp.module.d.ts +0 -2
- package/dist/web/server/mcp/mcp.module.js +0 -22
- package/dist/web/server/mcp/mcp.service.d.ts +0 -25
- package/dist/web/server/mcp/mcp.service.js +0 -56
- package/dist/web/server/server.d.ts +0 -18
- package/dist/web/server/server.js +0 -142
- package/dist/web/server/sessions/sessions.controller.d.ts +0 -8
- package/dist/web/server/sessions/sessions.controller.js +0 -59
- package/dist/web/server/sessions/sessions.module.d.ts +0 -2
- package/dist/web/server/sessions/sessions.module.js +0 -24
- package/dist/web/server/sessions/sessions.service.d.ts +0 -22
- package/dist/web/server/sessions/sessions.service.js +0 -217
- package/dist/web/server/sessions/sessions.types.d.ts +0 -18
- package/dist/web/server/sessions/sessions.types.js +0 -2
- package/dist/web/server/skills/skills.controller.d.ts +0 -31
- package/dist/web/server/skills/skills.controller.js +0 -86
- package/dist/web/server/skills/skills.module.d.ts +0 -2
- package/dist/web/server/skills/skills.module.js +0 -24
- package/dist/web/server/skills/skills.service.d.ts +0 -38
- package/dist/web/server/skills/skills.service.js +0 -97
- package/dist/web/server/stream/stream.module.d.ts +0 -2
- package/dist/web/server/stream/stream.module.js +0 -20
- package/dist/web/server/stream/stream.service.d.ts +0 -26
- package/dist/web/server/stream/stream.service.js +0 -166
- package/dist/web/server/tsconfig.build.tsbuildinfo +0 -1
- package/dist/web/server/workspaces/workspaces.module.d.ts +0 -2
- package/dist/web/server/workspaces/workspaces.module.js +0 -20
- package/dist/web/server/workspaces/workspaces.service.d.ts +0 -38
- package/dist/web/server/workspaces/workspaces.service.js +0 -378
- package/dist/web/server/workspaces/workspaces.types.d.ts +0 -1
- package/dist/web/server/workspaces/workspaces.types.js +0 -2
- package/dist/web/server/workspaces/workspaces.utils.d.ts +0 -1
- package/dist/web/server/workspaces/workspaces.utils.js +0 -9
- package/dist/web/server/ws/rpc-router.service.d.ts +0 -20
- package/dist/web/server/ws/rpc-router.service.js +0 -275
- package/dist/web/server/ws/session-runtime-registry.service.d.ts +0 -37
- package/dist/web/server/ws/session-runtime-registry.service.js +0 -118
- package/dist/web/server/ws/ws-event-bus.service.d.ts +0 -5
- package/dist/web/server/ws/ws-event-bus.service.js +0 -27
- package/dist/web/server/ws/ws-gateway.module.d.ts +0 -2
- package/dist/web/server/ws/ws-gateway.module.js +0 -42
- package/dist/web/server/ws/ws-gateway.service.d.ts +0 -42
- package/dist/web/server/ws/ws-gateway.service.js +0 -473
- package/dist/web/server/ws/ws.errors.d.ts +0 -8
- package/dist/web/server/ws/ws.errors.js +0 -16
- package/dist/web/server/ws/ws.types.d.ts +0 -34
- package/dist/web/server/ws/ws.types.js +0 -2
|
@@ -1,473 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
var WsGatewayService_1;
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.WsGatewayService = void 0;
|
|
14
|
-
const node_crypto_1 = require("node:crypto");
|
|
15
|
-
const common_1 = require("@nestjs/common");
|
|
16
|
-
const ws_1 = require("ws");
|
|
17
|
-
const stream_service_1 = require("../stream/stream.service");
|
|
18
|
-
const rpc_router_service_1 = require("./rpc-router.service");
|
|
19
|
-
const session_runtime_registry_service_1 = require("./session-runtime-registry.service");
|
|
20
|
-
const ws_errors_1 = require("./ws.errors");
|
|
21
|
-
const ws_event_bus_service_1 = require("./ws-event-bus.service");
|
|
22
|
-
const WS_PATH = '/api/ws';
|
|
23
|
-
const MAX_REQUEST_BYTES = 256 * 1024;
|
|
24
|
-
const MAX_REQUESTS_PER_MINUTE = 120;
|
|
25
|
-
const REQUEST_TIMEOUT_MS = 20_000;
|
|
26
|
-
function readBearerTokenFromHeader(authorization) {
|
|
27
|
-
if (!authorization)
|
|
28
|
-
return null;
|
|
29
|
-
const raw = Array.isArray(authorization) ? authorization[0] : authorization;
|
|
30
|
-
if (!raw)
|
|
31
|
-
return null;
|
|
32
|
-
const [scheme, token] = raw.split(' ');
|
|
33
|
-
if (scheme !== 'Bearer' || !token)
|
|
34
|
-
return null;
|
|
35
|
-
const trimmed = token.trim();
|
|
36
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
37
|
-
}
|
|
38
|
-
function asRpcRequest(input) {
|
|
39
|
-
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
40
|
-
throw new ws_errors_1.WsRpcError('BAD_FRAME', 'Invalid frame payload');
|
|
41
|
-
}
|
|
42
|
-
const frame = input;
|
|
43
|
-
if (frame.type !== 'rpc.request') {
|
|
44
|
-
throw new ws_errors_1.WsRpcError('BAD_FRAME', 'Frame type must be rpc.request');
|
|
45
|
-
}
|
|
46
|
-
if (typeof frame.id !== 'string' || !frame.id.trim()) {
|
|
47
|
-
throw new ws_errors_1.WsRpcError('BAD_FRAME', 'Frame id is required');
|
|
48
|
-
}
|
|
49
|
-
if (typeof frame.method !== 'string' || !frame.method.trim()) {
|
|
50
|
-
throw new ws_errors_1.WsRpcError('BAD_FRAME', 'Frame method is required');
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
id: frame.id,
|
|
54
|
-
type: 'rpc.request',
|
|
55
|
-
method: frame.method,
|
|
56
|
-
params: frame.params,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function rawByteLength(raw) {
|
|
60
|
-
if (typeof raw === 'string') {
|
|
61
|
-
return Buffer.byteLength(raw);
|
|
62
|
-
}
|
|
63
|
-
if (raw instanceof ArrayBuffer) {
|
|
64
|
-
return raw.byteLength;
|
|
65
|
-
}
|
|
66
|
-
if (Array.isArray(raw)) {
|
|
67
|
-
return raw.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
68
|
-
}
|
|
69
|
-
return raw.byteLength;
|
|
70
|
-
}
|
|
71
|
-
function rawToUtf8(raw) {
|
|
72
|
-
if (typeof raw === 'string') {
|
|
73
|
-
return raw;
|
|
74
|
-
}
|
|
75
|
-
if (raw instanceof ArrayBuffer) {
|
|
76
|
-
return Buffer.from(raw).toString('utf8');
|
|
77
|
-
}
|
|
78
|
-
if (Array.isArray(raw)) {
|
|
79
|
-
return Buffer.concat(raw.map((chunk) => Buffer.from(chunk))).toString('utf8');
|
|
80
|
-
}
|
|
81
|
-
return raw.toString('utf8');
|
|
82
|
-
}
|
|
83
|
-
let WsGatewayService = WsGatewayService_1 = class WsGatewayService {
|
|
84
|
-
rpcRouter;
|
|
85
|
-
streamService;
|
|
86
|
-
sessionRegistry;
|
|
87
|
-
eventBus;
|
|
88
|
-
logger = new common_1.Logger(WsGatewayService_1.name);
|
|
89
|
-
connections = new Map();
|
|
90
|
-
wsServer = null;
|
|
91
|
-
attached = false;
|
|
92
|
-
globalStreamUnsubscribe = null;
|
|
93
|
-
constructor(rpcRouter, streamService, sessionRegistry, eventBus) {
|
|
94
|
-
this.rpcRouter = rpcRouter;
|
|
95
|
-
this.streamService = streamService;
|
|
96
|
-
this.sessionRegistry = sessionRegistry;
|
|
97
|
-
this.eventBus = eventBus;
|
|
98
|
-
}
|
|
99
|
-
attach(options) {
|
|
100
|
-
if (this.attached)
|
|
101
|
-
return;
|
|
102
|
-
this.attached = true;
|
|
103
|
-
this.wsServer = new ws_1.WebSocketServer({ noServer: true });
|
|
104
|
-
this.globalStreamUnsubscribe = this.streamService.subscribeAll((sessionId, payload) => {
|
|
105
|
-
const data = this.mapRuntimeStatusPayload(sessionId, payload);
|
|
106
|
-
if (!data)
|
|
107
|
-
return;
|
|
108
|
-
this.sessionRegistry.setWorkspaceId(sessionId, data.workspaceId);
|
|
109
|
-
this.sessionRegistry.setStatus(sessionId, data.status);
|
|
110
|
-
this.broadcastEventToAll('chat.runtime.status', data);
|
|
111
|
-
});
|
|
112
|
-
options.httpServer.on('upgrade', (request, socket, head) => {
|
|
113
|
-
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
114
|
-
if (requestUrl.pathname !== WS_PATH || !this.wsServer)
|
|
115
|
-
return;
|
|
116
|
-
this.wsServer.handleUpgrade(request, socket, head, (ws) => {
|
|
117
|
-
void this.handleConnection(ws, request, options.verifyAccessToken);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
async onModuleDestroy() {
|
|
122
|
-
if (!this.wsServer)
|
|
123
|
-
return;
|
|
124
|
-
if (this.globalStreamUnsubscribe) {
|
|
125
|
-
this.globalStreamUnsubscribe();
|
|
126
|
-
this.globalStreamUnsubscribe = null;
|
|
127
|
-
}
|
|
128
|
-
for (const connection of this.connections.values()) {
|
|
129
|
-
for (const unsubscribe of connection.sessionSubscriptions.values()) {
|
|
130
|
-
unsubscribe();
|
|
131
|
-
}
|
|
132
|
-
connection.sessionSubscriptions.clear();
|
|
133
|
-
connection.socket.close(1001, 'server shutdown');
|
|
134
|
-
}
|
|
135
|
-
this.connections.clear();
|
|
136
|
-
await new Promise((resolve) => {
|
|
137
|
-
this.wsServer?.close(() => resolve());
|
|
138
|
-
});
|
|
139
|
-
this.wsServer = null;
|
|
140
|
-
}
|
|
141
|
-
async handleConnection(socket, request, verifyAccessToken) {
|
|
142
|
-
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
143
|
-
const queryToken = requestUrl.searchParams.get('accessToken')?.trim() ?? null;
|
|
144
|
-
const headerToken = readBearerTokenFromHeader(request.headers.authorization);
|
|
145
|
-
const accessToken = headerToken ?? queryToken;
|
|
146
|
-
if (!accessToken) {
|
|
147
|
-
socket.close(ws_errors_1.WS_CLOSE_UNAUTHORIZED, 'UNAUTHORIZED');
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
let payload;
|
|
151
|
-
try {
|
|
152
|
-
payload = await verifyAccessToken(accessToken);
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
155
|
-
socket.close(ws_errors_1.WS_CLOSE_UNAUTHORIZED, 'UNAUTHORIZED');
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const connection = {
|
|
159
|
-
id: (0, node_crypto_1.randomUUID)(),
|
|
160
|
-
socket,
|
|
161
|
-
username: payload.sub,
|
|
162
|
-
requestWindow: [],
|
|
163
|
-
sessionSubscriptions: new Map(),
|
|
164
|
-
};
|
|
165
|
-
this.connections.set(connection.id, connection);
|
|
166
|
-
socket.on('message', (raw) => {
|
|
167
|
-
void this.handleMessage(connection, raw);
|
|
168
|
-
});
|
|
169
|
-
socket.on('close', () => {
|
|
170
|
-
this.cleanupConnection(connection.id);
|
|
171
|
-
});
|
|
172
|
-
socket.on('error', (error) => {
|
|
173
|
-
this.logger.warn(`ws connection error: ${error.message}`);
|
|
174
|
-
this.cleanupConnection(connection.id);
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
async handleMessage(connection, raw) {
|
|
178
|
-
if (rawByteLength(raw) > MAX_REQUEST_BYTES) {
|
|
179
|
-
connection.socket.close(1009, 'FRAME_TOO_LARGE');
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
const now = Date.now();
|
|
183
|
-
let removeCount = 0;
|
|
184
|
-
while (removeCount < connection.requestWindow.length &&
|
|
185
|
-
now - connection.requestWindow[removeCount] >= 60_000) {
|
|
186
|
-
removeCount += 1;
|
|
187
|
-
}
|
|
188
|
-
if (removeCount > 0) {
|
|
189
|
-
connection.requestWindow.splice(0, removeCount);
|
|
190
|
-
}
|
|
191
|
-
connection.requestWindow.push(now);
|
|
192
|
-
if (connection.requestWindow.length > MAX_REQUESTS_PER_MINUTE) {
|
|
193
|
-
this.sendRpcError(connection, null, new ws_errors_1.WsRpcError('RATE_LIMITED', 'Too many requests on this websocket connection.'));
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
let parsed;
|
|
197
|
-
try {
|
|
198
|
-
const payload = JSON.parse(rawToUtf8(raw));
|
|
199
|
-
parsed = asRpcRequest(payload);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
const wsError = error instanceof ws_errors_1.WsRpcError
|
|
203
|
-
? error
|
|
204
|
-
: new ws_errors_1.WsRpcError('BAD_FRAME', error instanceof Error ? error.message : 'Invalid frame');
|
|
205
|
-
this.sendRpcError(connection, null, wsError);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const timer = setTimeout(() => {
|
|
209
|
-
settled = true;
|
|
210
|
-
this.sendRpcError(connection, parsed.id, new ws_errors_1.WsRpcError('TIMEOUT', `RPC timeout after ${REQUEST_TIMEOUT_MS}ms`));
|
|
211
|
-
}, REQUEST_TIMEOUT_MS);
|
|
212
|
-
let settled = false;
|
|
213
|
-
try {
|
|
214
|
-
const result = await this.rpcRouter.dispatch({
|
|
215
|
-
connectionId: connection.id,
|
|
216
|
-
username: connection.username,
|
|
217
|
-
}, parsed.method, parsed.params);
|
|
218
|
-
if (parsed.method === 'chat.session.attach') {
|
|
219
|
-
const params = parsed.params &&
|
|
220
|
-
typeof parsed.params === 'object' &&
|
|
221
|
-
!Array.isArray(parsed.params)
|
|
222
|
-
? parsed.params
|
|
223
|
-
: {};
|
|
224
|
-
const sessionId = typeof params.sessionId === 'string' ? params.sessionId : '';
|
|
225
|
-
if (sessionId) {
|
|
226
|
-
this.bindSession(connection, sessionId);
|
|
227
|
-
}
|
|
228
|
-
const snapshot = result;
|
|
229
|
-
if (snapshot?.state?.workspaceId) {
|
|
230
|
-
this.sessionRegistry.setWorkspaceId(sessionId, snapshot.state.workspaceId);
|
|
231
|
-
}
|
|
232
|
-
this.sendEvent(connection, 'chat.session.snapshot', {
|
|
233
|
-
sessionId,
|
|
234
|
-
state: snapshot.state,
|
|
235
|
-
turns: snapshot.turns,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
if (parsed.method === 'chat.session.close' ||
|
|
239
|
-
parsed.method === 'sessions.remove') {
|
|
240
|
-
const params = parsed.params &&
|
|
241
|
-
typeof parsed.params === 'object' &&
|
|
242
|
-
!Array.isArray(parsed.params)
|
|
243
|
-
? parsed.params
|
|
244
|
-
: {};
|
|
245
|
-
const sessionId = typeof params.sessionId === 'string' ? params.sessionId : '';
|
|
246
|
-
if (sessionId) {
|
|
247
|
-
this.unbindSession(connection, sessionId);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (parsed.method === 'workspace.add' ||
|
|
251
|
-
parsed.method === 'workspace.update' ||
|
|
252
|
-
parsed.method === 'workspace.remove') {
|
|
253
|
-
this.broadcastEventToAll('workspace.changed', {
|
|
254
|
-
action: parsed.method,
|
|
255
|
-
payload: result,
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (settled)
|
|
259
|
-
return;
|
|
260
|
-
settled = true;
|
|
261
|
-
this.sendRpcSuccess(connection, parsed.id, result);
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
if (settled)
|
|
265
|
-
return;
|
|
266
|
-
settled = true;
|
|
267
|
-
const wsError = this.toWsRpcError(error);
|
|
268
|
-
this.sendRpcError(connection, parsed.id, wsError);
|
|
269
|
-
if (parsed.method === 'chat.session.attach' &&
|
|
270
|
-
wsError.code === 'SESSION_OCCUPIED') {
|
|
271
|
-
connection.socket.close(ws_errors_1.WS_CLOSE_SESSION_OCCUPIED, 'SESSION_OCCUPIED');
|
|
272
|
-
}
|
|
273
|
-
if (parsed.method === 'chat.session.attach' &&
|
|
274
|
-
wsError.code === 'SESSION_NOT_FOUND') {
|
|
275
|
-
connection.socket.close(ws_errors_1.WS_CLOSE_NOT_FOUND, 'SESSION_NOT_FOUND');
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
finally {
|
|
279
|
-
clearTimeout(timer);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
bindSession(connection, sessionId) {
|
|
283
|
-
if (connection.sessionSubscriptions.has(sessionId)) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const unsubscribe = this.streamService.subscribe(sessionId, (payload) => {
|
|
287
|
-
const mapped = this.mapStreamPayload(sessionId, payload);
|
|
288
|
-
if (!mapped)
|
|
289
|
-
return;
|
|
290
|
-
this.sendEvent(connection, mapped.topic, mapped.data);
|
|
291
|
-
});
|
|
292
|
-
connection.sessionSubscriptions.set(sessionId, unsubscribe);
|
|
293
|
-
}
|
|
294
|
-
unbindSession(connection, sessionId) {
|
|
295
|
-
const unsubscribe = connection.sessionSubscriptions.get(sessionId);
|
|
296
|
-
if (unsubscribe) {
|
|
297
|
-
unsubscribe();
|
|
298
|
-
connection.sessionSubscriptions.delete(sessionId);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
cleanupConnection(connectionId) {
|
|
302
|
-
const connection = this.connections.get(connectionId);
|
|
303
|
-
if (!connection)
|
|
304
|
-
return;
|
|
305
|
-
const released = this.sessionRegistry.releaseAll(connection.id);
|
|
306
|
-
for (const sessionId of released) {
|
|
307
|
-
this.unbindSession(connection, sessionId);
|
|
308
|
-
}
|
|
309
|
-
for (const sessionId of Array.from(connection.sessionSubscriptions.keys())) {
|
|
310
|
-
this.unbindSession(connection, sessionId);
|
|
311
|
-
}
|
|
312
|
-
this.connections.delete(connectionId);
|
|
313
|
-
}
|
|
314
|
-
mapStreamPayload(sessionId, payload) {
|
|
315
|
-
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
const frame = payload;
|
|
319
|
-
if (frame.type === 'turn.start') {
|
|
320
|
-
return {
|
|
321
|
-
topic: 'chat.turn.start',
|
|
322
|
-
data: { sessionId, ...frame.payload },
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
if (frame.type === 'assistant.chunk') {
|
|
326
|
-
return {
|
|
327
|
-
topic: 'chat.turn.chunk',
|
|
328
|
-
data: { sessionId, ...frame.payload },
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
if (frame.type === 'turn.final') {
|
|
332
|
-
return {
|
|
333
|
-
topic: 'chat.turn.final',
|
|
334
|
-
data: { sessionId, ...frame.payload },
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
if (frame.type === 'session.status') {
|
|
338
|
-
return {
|
|
339
|
-
topic: 'chat.session.status',
|
|
340
|
-
data: { sessionId, ...frame.payload },
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
if (frame.type === 'session.snapshot') {
|
|
344
|
-
return {
|
|
345
|
-
topic: 'chat.session.state',
|
|
346
|
-
data: { sessionId, state: frame.payload },
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
if (frame.type === 'system.message') {
|
|
350
|
-
return {
|
|
351
|
-
topic: 'chat.system.message',
|
|
352
|
-
data: { sessionId, ...frame.payload },
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
if (frame.type === 'approval.request') {
|
|
356
|
-
return {
|
|
357
|
-
topic: 'chat.approval.request',
|
|
358
|
-
data: { sessionId, ...frame.payload },
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
if (frame.type === 'tool.action') {
|
|
362
|
-
return {
|
|
363
|
-
topic: 'chat.tool.action',
|
|
364
|
-
data: { sessionId, ...frame.payload },
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
if (frame.type === 'tool.observation') {
|
|
368
|
-
return {
|
|
369
|
-
topic: 'chat.tool.observation',
|
|
370
|
-
data: { sessionId, ...frame.payload },
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
if (frame.type === 'error') {
|
|
374
|
-
return {
|
|
375
|
-
topic: 'chat.error',
|
|
376
|
-
data: { sessionId, ...frame.payload },
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
mapRuntimeStatusPayload(sessionId, payload) {
|
|
382
|
-
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
const frame = payload;
|
|
386
|
-
if (frame.type !== 'session.status')
|
|
387
|
-
return null;
|
|
388
|
-
const workspaceId = frame.payload.workspaceId;
|
|
389
|
-
const updatedAt = frame.payload.updatedAt;
|
|
390
|
-
if (typeof workspaceId !== 'string' || !workspaceId.trim())
|
|
391
|
-
return null;
|
|
392
|
-
if (typeof updatedAt !== 'string' || !updatedAt.trim())
|
|
393
|
-
return null;
|
|
394
|
-
return {
|
|
395
|
-
sessionId,
|
|
396
|
-
status: frame.payload.status,
|
|
397
|
-
workspaceId,
|
|
398
|
-
updatedAt,
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
sendRpcSuccess(connection, id, data) {
|
|
402
|
-
const response = {
|
|
403
|
-
id,
|
|
404
|
-
type: 'rpc.response',
|
|
405
|
-
ok: true,
|
|
406
|
-
data,
|
|
407
|
-
};
|
|
408
|
-
this.sendSerialized(connection, JSON.stringify(response));
|
|
409
|
-
}
|
|
410
|
-
sendRpcError(connection, id, error) {
|
|
411
|
-
const response = {
|
|
412
|
-
id: id ?? (0, node_crypto_1.randomUUID)(),
|
|
413
|
-
type: 'rpc.response',
|
|
414
|
-
ok: false,
|
|
415
|
-
error: {
|
|
416
|
-
code: error.code,
|
|
417
|
-
message: error.message,
|
|
418
|
-
...(error.details === undefined ? {} : { details: error.details }),
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
this.sendSerialized(connection, JSON.stringify(response));
|
|
422
|
-
}
|
|
423
|
-
sendEvent(connection, topic, data) {
|
|
424
|
-
const eventFrame = this.eventBus.create(topic, data);
|
|
425
|
-
this.sendSerialized(connection, JSON.stringify(eventFrame));
|
|
426
|
-
}
|
|
427
|
-
broadcastEventToAll(topic, data) {
|
|
428
|
-
if (this.connections.size === 0)
|
|
429
|
-
return;
|
|
430
|
-
const eventFrame = this.eventBus.create(topic, data);
|
|
431
|
-
const encoded = JSON.stringify(eventFrame);
|
|
432
|
-
for (const connection of this.connections.values()) {
|
|
433
|
-
this.sendSerialized(connection, encoded);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
sendSerialized(connection, encoded) {
|
|
437
|
-
if (connection.socket.readyState !== connection.socket.OPEN) {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
try {
|
|
441
|
-
connection.socket.send(encoded);
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
445
|
-
this.logger.warn(`ws send failed connection=${connection.id}: ${message}`);
|
|
446
|
-
this.cleanupConnection(connection.id);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
toWsRpcError(error) {
|
|
450
|
-
if (error instanceof ws_errors_1.WsRpcError) {
|
|
451
|
-
return error;
|
|
452
|
-
}
|
|
453
|
-
if (error && typeof error === 'object' && 'message' in error) {
|
|
454
|
-
const message = typeof error.message === 'string'
|
|
455
|
-
? (error.message ?? 'Request failed')
|
|
456
|
-
: 'Request failed';
|
|
457
|
-
if (message.toLowerCase().includes('not found') ||
|
|
458
|
-
message.toLowerCase().includes('session not found')) {
|
|
459
|
-
return new ws_errors_1.WsRpcError('SESSION_NOT_FOUND', message);
|
|
460
|
-
}
|
|
461
|
-
return new ws_errors_1.WsRpcError('RPC_ERROR', message);
|
|
462
|
-
}
|
|
463
|
-
return new ws_errors_1.WsRpcError('RPC_ERROR', 'Request failed');
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
exports.WsGatewayService = WsGatewayService;
|
|
467
|
-
exports.WsGatewayService = WsGatewayService = WsGatewayService_1 = __decorate([
|
|
468
|
-
(0, common_1.Injectable)(),
|
|
469
|
-
__metadata("design:paramtypes", [rpc_router_service_1.RpcRouterService,
|
|
470
|
-
stream_service_1.StreamService,
|
|
471
|
-
session_runtime_registry_service_1.SessionRuntimeRegistry,
|
|
472
|
-
ws_event_bus_service_1.WsEventBus])
|
|
473
|
-
], WsGatewayService);
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare class WsRpcError extends Error {
|
|
2
|
-
readonly code: string;
|
|
3
|
-
readonly details?: unknown | undefined;
|
|
4
|
-
constructor(code: string, message: string, details?: unknown | undefined);
|
|
5
|
-
}
|
|
6
|
-
export declare const WS_CLOSE_UNAUTHORIZED = 4401;
|
|
7
|
-
export declare const WS_CLOSE_NOT_FOUND = 4404;
|
|
8
|
-
export declare const WS_CLOSE_SESSION_OCCUPIED = 4409;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WS_CLOSE_SESSION_OCCUPIED = exports.WS_CLOSE_NOT_FOUND = exports.WS_CLOSE_UNAUTHORIZED = exports.WsRpcError = void 0;
|
|
4
|
-
class WsRpcError extends Error {
|
|
5
|
-
code;
|
|
6
|
-
details;
|
|
7
|
-
constructor(code, message, details) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.details = details;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
exports.WsRpcError = WsRpcError;
|
|
14
|
-
exports.WS_CLOSE_UNAUTHORIZED = 4401;
|
|
15
|
-
exports.WS_CLOSE_NOT_FOUND = 4404;
|
|
16
|
-
exports.WS_CLOSE_SESSION_OCCUPIED = 4409;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { WebSocket } from 'ws';
|
|
2
|
-
export type RpcRequestFrame = {
|
|
3
|
-
id: string;
|
|
4
|
-
type: 'rpc.request';
|
|
5
|
-
method: string;
|
|
6
|
-
params?: unknown;
|
|
7
|
-
};
|
|
8
|
-
export type RpcResponseFrame = {
|
|
9
|
-
id: string;
|
|
10
|
-
type: 'rpc.response';
|
|
11
|
-
ok: true;
|
|
12
|
-
data: unknown;
|
|
13
|
-
} | {
|
|
14
|
-
id: string;
|
|
15
|
-
type: 'rpc.response';
|
|
16
|
-
ok: false;
|
|
17
|
-
error: {
|
|
18
|
-
code: string;
|
|
19
|
-
message: string;
|
|
20
|
-
details?: unknown;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
export type RpcEventFrame = {
|
|
24
|
-
type: 'event';
|
|
25
|
-
topic: string;
|
|
26
|
-
data: unknown;
|
|
27
|
-
seq: number;
|
|
28
|
-
ts: string;
|
|
29
|
-
};
|
|
30
|
-
export type WsConnectionContext = {
|
|
31
|
-
id: string;
|
|
32
|
-
socket: WebSocket;
|
|
33
|
-
username: string;
|
|
34
|
-
};
|