@inductiv/node-red-openai-api 1.103.0 → 6.27.0
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/README.md +213 -86
- package/examples/realtime/client-secrets.json +182 -0
- package/examples/responses/computer-use.json +142 -0
- package/examples/responses/mcp.json +1 -1
- package/examples/responses/phase.json +102 -0
- package/examples/responses/tool-search.json +107 -0
- package/examples/responses/websocket.json +172 -0
- package/internals/openai-api-features-v6.23.0-v6.27.0.md +96 -0
- package/lib.js +12696 -15003
- package/locales/en-US/node.json +50 -1
- package/node.html +1723 -1012
- package/node.js +204 -54
- package/package.json +9 -7
- package/src/assistants/help.html +1 -77
- package/src/audio/help.html +1 -37
- package/src/batch/help.html +3 -17
- package/src/chat/help.html +11 -89
- package/src/container-files/help.html +1 -27
- package/src/containers/help.html +8 -18
- package/src/conversations/help.html +135 -0
- package/src/conversations/methods.js +73 -0
- package/src/conversations/template.html +10 -0
- package/src/embeddings/help.html +1 -11
- package/src/evals/help.html +249 -0
- package/src/evals/methods.js +114 -0
- package/src/evals/template.html +14 -0
- package/src/files/help.html +4 -17
- package/src/fine-tuning/help.html +1 -35
- package/src/images/help.html +1 -45
- package/src/lib.js +53 -1
- package/src/messages/help.html +19 -39
- package/src/messages/methods.js +13 -0
- package/src/messages/template.html +7 -18
- package/src/models/help.html +1 -5
- package/src/moderations/help.html +1 -5
- package/src/node.html +126 -37
- package/src/realtime/help.html +209 -0
- package/src/realtime/methods.js +45 -0
- package/src/realtime/template.html +7 -0
- package/src/responses/help.html +286 -63
- package/src/responses/methods.js +234 -16
- package/src/responses/template.html +21 -1
- package/src/responses/websocket.js +150 -0
- package/src/runs/help.html +1 -123
- package/src/skills/help.html +183 -0
- package/src/skills/methods.js +99 -0
- package/src/skills/template.html +13 -0
- package/src/threads/help.html +1 -15
- package/src/uploads/help.html +1 -21
- package/src/vector-store-file-batches/help.html +1 -27
- package/src/vector-store-file-batches/methods.js +5 -5
- package/src/vector-store-files/help.html +1 -25
- package/src/vector-store-files/methods.js +4 -7
- package/src/vector-stores/help.html +2 -31
- package/src/vector-stores/methods.js +5 -11
- package/src/vector-stores/template.html +7 -22
- package/src/videos/help.html +113 -0
- package/src/videos/methods.js +50 -0
- package/src/videos/template.html +8 -0
- package/src/webhooks/help.html +61 -0
- package/src/webhooks/methods.js +40 -0
- package/src/webhooks/template.html +4 -0
- package/test/openai-methods-mapping.test.js +1559 -0
- package/test/openai-node-auth-routing.test.js +206 -0
- package/test/openai-responses-websocket.test.js +472 -0
- package/test/service-host-editor-template.test.js +56 -0
- package/test/service-host-node.test.js +185 -0
- package/test/services.test.js +150 -0
- package/test/utils.test.js +78 -0
package/src/responses/methods.js
CHANGED
|
@@ -1,23 +1,187 @@
|
|
|
1
1
|
const OpenAI = require("openai").OpenAI;
|
|
2
|
+
const { ResponsesWebSocket } = require("./websocket.js");
|
|
3
|
+
|
|
4
|
+
function getResponsesWebSocketConnections(node) {
|
|
5
|
+
if (!node._responsesWebSocketConnections) {
|
|
6
|
+
node._responsesWebSocketConnections = new Map();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return node._responsesWebSocketConnections;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function ensureResponsesWebSocketCleanup(node) {
|
|
13
|
+
if (node._responsesWebSocketCleanupRegistered) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof node.registerCleanupHandler !== "function") {
|
|
18
|
+
throw new Error("OpenAI API node does not support cleanup registration");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
node._responsesWebSocketCleanupRegistered = true;
|
|
22
|
+
node.registerCleanupHandler(async () => {
|
|
23
|
+
const connections = getResponsesWebSocketConnections(node);
|
|
24
|
+
const closeOperations = [];
|
|
25
|
+
|
|
26
|
+
for (const connection of connections.values()) {
|
|
27
|
+
closeOperations.push(
|
|
28
|
+
connection.close({
|
|
29
|
+
code: 1000,
|
|
30
|
+
reason: "Node-RED node closed",
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await Promise.all(closeOperations);
|
|
36
|
+
connections.clear();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requireConnectionId(payload) {
|
|
41
|
+
if (typeof payload.connection_id !== "string" || payload.connection_id.trim() === "") {
|
|
42
|
+
throw new Error("msg.payload.connection_id must be a non-empty string");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return payload.connection_id;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createResponsesWebSocketEventMessage(connectionId, event) {
|
|
49
|
+
return {
|
|
50
|
+
payload: event,
|
|
51
|
+
openai: {
|
|
52
|
+
transport: "responses.websocket",
|
|
53
|
+
direction: "server",
|
|
54
|
+
connection_id: connectionId,
|
|
55
|
+
event_type: event.type,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function attachResponsesWebSocketListeners(node, connectionId, connection) {
|
|
61
|
+
connection.on("event", (event) => {
|
|
62
|
+
node.send(createResponsesWebSocketEventMessage(connectionId, event));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
connection.on("error", (error) => {
|
|
66
|
+
node.error(error);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
connection.on("close", () => {
|
|
70
|
+
const connections = getResponsesWebSocketConnections(node);
|
|
71
|
+
connections.delete(connectionId);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function connectResponsesWebSocket(parameters) {
|
|
76
|
+
const node = parameters._node;
|
|
77
|
+
const payload = parameters.payload || {};
|
|
78
|
+
const connectionId = requireConnectionId(payload);
|
|
79
|
+
const connections = getResponsesWebSocketConnections(node);
|
|
80
|
+
|
|
81
|
+
if (connections.has(connectionId)) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Responses websocket connection '${connectionId}' is already open on this node`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ensureResponsesWebSocketCleanup(node);
|
|
88
|
+
|
|
89
|
+
const connection = new ResponsesWebSocket(this.clientParams);
|
|
90
|
+
const connectionDetails = await connection.open();
|
|
91
|
+
|
|
92
|
+
attachResponsesWebSocketListeners(node, connectionId, connection);
|
|
93
|
+
connections.set(connectionId, connection);
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
object: "response.websocket.connection",
|
|
97
|
+
action: "connect",
|
|
98
|
+
connection_id: connectionId,
|
|
99
|
+
url: connectionDetails.url,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function sendResponsesWebSocketEvent(parameters) {
|
|
104
|
+
const node = parameters._node;
|
|
105
|
+
const payload = parameters.payload || {};
|
|
106
|
+
const connectionId = requireConnectionId(payload);
|
|
107
|
+
const connections = getResponsesWebSocketConnections(node);
|
|
108
|
+
const connection = connections.get(connectionId);
|
|
109
|
+
|
|
110
|
+
if (!connection) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Responses websocket connection '${connectionId}' is not open on this node`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!payload.event || typeof payload.event !== "object" || Array.isArray(payload.event)) {
|
|
117
|
+
throw new Error("msg.payload.event must be an object");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (payload.event.type !== "response.create") {
|
|
121
|
+
throw new Error("msg.payload.event.type must be 'response.create'");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
connection.send(payload.event);
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
object: "response.websocket.client_event",
|
|
128
|
+
action: "send",
|
|
129
|
+
connection_id: connectionId,
|
|
130
|
+
event_type: payload.event.type,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function closeResponsesWebSocket(parameters) {
|
|
135
|
+
const node = parameters._node;
|
|
136
|
+
const payload = parameters.payload || {};
|
|
137
|
+
const connectionId = requireConnectionId(payload);
|
|
138
|
+
const connections = getResponsesWebSocketConnections(node);
|
|
139
|
+
const connection = connections.get(connectionId);
|
|
140
|
+
|
|
141
|
+
if (!connection) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Responses websocket connection '${connectionId}' is not open on this node`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const closeDetails = await connection.close({
|
|
148
|
+
code: payload.code,
|
|
149
|
+
reason: payload.reason,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
connections.delete(connectionId);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
object: "response.websocket.connection",
|
|
156
|
+
action: "close",
|
|
157
|
+
connection_id: connectionId,
|
|
158
|
+
code: closeDetails.code,
|
|
159
|
+
reason: closeDetails.reason,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function streamResponse(parameters, response) {
|
|
164
|
+
const { _node, msg } = parameters;
|
|
165
|
+
_node.status({
|
|
166
|
+
fill: "green",
|
|
167
|
+
shape: "dot",
|
|
168
|
+
text: "OpenaiApi.status.streaming",
|
|
169
|
+
});
|
|
170
|
+
for await (const chunk of response) {
|
|
171
|
+
if (typeof chunk === "object") {
|
|
172
|
+
const newMsg = { ...msg, payload: chunk };
|
|
173
|
+
_node.send(newMsg);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
_node.status({});
|
|
177
|
+
}
|
|
2
178
|
|
|
3
179
|
async function createModelResponse(parameters) {
|
|
4
|
-
const { _node, ...params } = parameters;
|
|
5
180
|
const openai = new OpenAI(this.clientParams);
|
|
6
181
|
const response = await openai.responses.create(parameters.payload);
|
|
7
182
|
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
fill: "green",
|
|
11
|
-
shape: "dot",
|
|
12
|
-
text: "OpenaiApi.status.streaming",
|
|
13
|
-
});
|
|
14
|
-
for await (const chunk of response) {
|
|
15
|
-
if (typeof chunk === "object") {
|
|
16
|
-
const newMsg = { ...parameters.msg, payload: chunk };
|
|
17
|
-
_node.send(newMsg);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
_node.status({});
|
|
183
|
+
if (parameters.payload.stream) {
|
|
184
|
+
await streamResponse(parameters, response);
|
|
21
185
|
} else {
|
|
22
186
|
return response;
|
|
23
187
|
}
|
|
@@ -28,13 +192,32 @@ async function getModelResponse(parameters) {
|
|
|
28
192
|
const { response_id, ...params } = parameters.payload;
|
|
29
193
|
const response = await openai.responses.retrieve(response_id, params);
|
|
30
194
|
|
|
31
|
-
|
|
195
|
+
if (params.stream) {
|
|
196
|
+
await streamResponse(parameters, response);
|
|
197
|
+
} else {
|
|
198
|
+
return response;
|
|
199
|
+
}
|
|
32
200
|
}
|
|
33
201
|
|
|
34
202
|
async function deleteModelResponse(parameters) {
|
|
35
203
|
const openai = new OpenAI(this.clientParams);
|
|
36
204
|
const { response_id, ...params } = parameters.payload;
|
|
37
|
-
const response = await openai.responses.
|
|
205
|
+
const response = await openai.responses.delete(response_id, params);
|
|
206
|
+
|
|
207
|
+
return response;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function cancelModelResponse(parameters) {
|
|
211
|
+
const openai = new OpenAI(this.clientParams);
|
|
212
|
+
const { response_id, ...params } = parameters.payload;
|
|
213
|
+
const response = await openai.responses.cancel(response_id, params);
|
|
214
|
+
|
|
215
|
+
return response;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function compactModelResponse(parameters) {
|
|
219
|
+
const openai = new OpenAI(this.clientParams);
|
|
220
|
+
const response = await openai.responses.compact(parameters.payload);
|
|
38
221
|
|
|
39
222
|
return response;
|
|
40
223
|
}
|
|
@@ -47,9 +230,44 @@ async function listInputItems(parameters) {
|
|
|
47
230
|
return [...list.data];
|
|
48
231
|
}
|
|
49
232
|
|
|
233
|
+
async function countInputTokens(parameters) {
|
|
234
|
+
const openai = new OpenAI(this.clientParams);
|
|
235
|
+
const response = await openai.responses.inputTokens.count(parameters.payload);
|
|
236
|
+
|
|
237
|
+
return response;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function manageModelResponseWebSocket(parameters) {
|
|
241
|
+
const payload = parameters.payload || {};
|
|
242
|
+
|
|
243
|
+
if (typeof payload.action !== "string" || payload.action.trim() === "") {
|
|
244
|
+
throw new Error(
|
|
245
|
+
"msg.payload.action must be one of 'connect', 'send', or 'close'"
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (payload.action === "connect") {
|
|
250
|
+
return connectResponsesWebSocket.call(this, parameters);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (payload.action === "send") {
|
|
254
|
+
return sendResponsesWebSocketEvent.call(this, parameters);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (payload.action === "close") {
|
|
258
|
+
return closeResponsesWebSocket.call(this, parameters);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
throw new Error("msg.payload.action must be one of 'connect', 'send', or 'close'");
|
|
262
|
+
}
|
|
263
|
+
|
|
50
264
|
module.exports = {
|
|
51
265
|
createModelResponse,
|
|
52
266
|
getModelResponse,
|
|
53
267
|
deleteModelResponse,
|
|
268
|
+
cancelModelResponse,
|
|
269
|
+
compactModelResponse,
|
|
54
270
|
listInputItems,
|
|
271
|
+
countInputTokens,
|
|
272
|
+
manageModelResponseWebSocket,
|
|
55
273
|
};
|
|
@@ -14,9 +14,29 @@
|
|
|
14
14
|
data-i18n="OpenaiApi.parameters.deleteModelResponse"
|
|
15
15
|
></option>
|
|
16
16
|
|
|
17
|
+
<option
|
|
18
|
+
value="cancelModelResponse"
|
|
19
|
+
data-i18n="OpenaiApi.parameters.cancelModelResponse"
|
|
20
|
+
></option>
|
|
21
|
+
|
|
22
|
+
<option
|
|
23
|
+
value="compactModelResponse"
|
|
24
|
+
data-i18n="OpenaiApi.parameters.compactModelResponse"
|
|
25
|
+
></option>
|
|
26
|
+
|
|
17
27
|
<option
|
|
18
28
|
value="listInputItems"
|
|
19
29
|
data-i18n="OpenaiApi.parameters.listInputItems"
|
|
20
30
|
></option>
|
|
21
31
|
|
|
22
|
-
|
|
32
|
+
<option
|
|
33
|
+
value="countInputTokens"
|
|
34
|
+
data-i18n="OpenaiApi.parameters.countInputTokens"
|
|
35
|
+
></option>
|
|
36
|
+
|
|
37
|
+
<option
|
|
38
|
+
value="manageModelResponseWebSocket"
|
|
39
|
+
data-i18n="OpenaiApi.parameters.manageModelResponseWebSocket"
|
|
40
|
+
></option>
|
|
41
|
+
|
|
42
|
+
</optgroup>
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require("node:events");
|
|
4
|
+
const { WebSocket } = require("ws");
|
|
5
|
+
|
|
6
|
+
function buildResponsesWebSocketURL(clientParams) {
|
|
7
|
+
const baseURL = clientParams.baseURL || "https://api.openai.com/v1";
|
|
8
|
+
const path = "/responses";
|
|
9
|
+
const url = new URL(baseURL + (baseURL.endsWith("/") ? path.slice(1) : path));
|
|
10
|
+
const defaultQuery = clientParams.defaultQuery || {};
|
|
11
|
+
|
|
12
|
+
for (const [key, value] of Object.entries(defaultQuery)) {
|
|
13
|
+
if (value !== undefined && value !== null) {
|
|
14
|
+
url.searchParams.set(key, value);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (url.protocol === "https:") {
|
|
19
|
+
url.protocol = "wss:";
|
|
20
|
+
} else if (url.protocol === "http:") {
|
|
21
|
+
url.protocol = "ws:";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildResponsesWebSocketHeaders(clientParams) {
|
|
28
|
+
const headers = {};
|
|
29
|
+
const defaultHeaders = clientParams.defaultHeaders || {};
|
|
30
|
+
const hasAuthorizationOverride = Object.prototype.hasOwnProperty.call(
|
|
31
|
+
defaultHeaders,
|
|
32
|
+
"Authorization"
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
for (const [headerName, headerValue] of Object.entries(defaultHeaders)) {
|
|
36
|
+
if (headerValue !== undefined && headerValue !== null) {
|
|
37
|
+
headers[headerName] = headerValue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!clientParams.defaultQuery && !hasAuthorizationOverride) {
|
|
42
|
+
headers.Authorization = `Bearer ${clientParams.apiKey}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (clientParams.organization) {
|
|
46
|
+
headers["OpenAI-Organization"] = clientParams.organization;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return headers;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createEventParseError(error) {
|
|
53
|
+
const parseError = new Error("Could not parse Responses websocket event");
|
|
54
|
+
parseError.cause = error;
|
|
55
|
+
return parseError;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class ResponsesWebSocket extends EventEmitter {
|
|
59
|
+
constructor(clientParams) {
|
|
60
|
+
super();
|
|
61
|
+
this.clientParams = clientParams;
|
|
62
|
+
this.url = buildResponsesWebSocketURL(clientParams);
|
|
63
|
+
this.headers = buildResponsesWebSocketHeaders(clientParams);
|
|
64
|
+
this.socket = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async open() {
|
|
68
|
+
if (this.socket) {
|
|
69
|
+
throw new Error("Responses websocket connection is already open");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const socket = new WebSocket(this.url, { headers: this.headers });
|
|
73
|
+
|
|
74
|
+
await new Promise((resolve, reject) => {
|
|
75
|
+
socket.once("open", () => {
|
|
76
|
+
this.socket = socket;
|
|
77
|
+
this.attachSocketListeners(socket);
|
|
78
|
+
resolve();
|
|
79
|
+
});
|
|
80
|
+
socket.once("error", reject);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
url: this.url.toString(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
attachSocketListeners(socket) {
|
|
89
|
+
socket.on("message", (data) => {
|
|
90
|
+
let event;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
event = JSON.parse(data.toString());
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.emit("error", createEventParseError(error));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.emit("event", event);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
socket.on("error", (error) => {
|
|
103
|
+
this.emit("error", error);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
socket.on("close", (code, reason) => {
|
|
107
|
+
if (this.socket === socket) {
|
|
108
|
+
this.socket = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.emit("close", {
|
|
112
|
+
code,
|
|
113
|
+
reason: reason.toString(),
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
send(event) {
|
|
119
|
+
if (!this.socket) {
|
|
120
|
+
throw new Error("Responses websocket connection is not open");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.socket.send(JSON.stringify(event));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async close(props = {}) {
|
|
127
|
+
if (!this.socket) {
|
|
128
|
+
throw new Error("Responses websocket connection is not open");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const socket = this.socket;
|
|
132
|
+
const closeCode = props.code ?? 1000;
|
|
133
|
+
const closeReason = props.reason ?? "OK";
|
|
134
|
+
|
|
135
|
+
await new Promise((resolve, reject) => {
|
|
136
|
+
socket.once("close", resolve);
|
|
137
|
+
socket.once("error", reject);
|
|
138
|
+
socket.close(closeCode, closeReason);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
code: closeCode,
|
|
143
|
+
reason: closeReason,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
ResponsesWebSocket,
|
|
150
|
+
};
|