@robota-sdk/agent-transport-ws 3.0.0-beta.61 → 3.0.0-beta.63
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/node/index.cjs +99 -1
- package/dist/node/index.d.cts +50 -2
- package/dist/node/index.d.ts +50 -2
- package/dist/node/index.js +98 -1
- package/package.json +6 -2
package/dist/node/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
WsTransport: () => WsTransport,
|
|
23
24
|
createWsHandler: () => createWsHandler,
|
|
24
25
|
createWsTransport: () => createWsTransport
|
|
25
26
|
});
|
|
@@ -148,6 +149,7 @@ function createWsHandler(options) {
|
|
|
148
149
|
return { onMessage, cleanup };
|
|
149
150
|
}
|
|
150
151
|
function subscribeSessionEvents(session, send) {
|
|
152
|
+
const onUserMessage = (content) => send({ type: "user_message", content });
|
|
151
153
|
const onTextDelta = (delta) => send({ type: "text_delta", delta });
|
|
152
154
|
const onToolStart = (state) => send({ type: "tool_start", state });
|
|
153
155
|
const onToolEnd = (state) => send({ type: "tool_end", state });
|
|
@@ -157,6 +159,8 @@ function subscribeSessionEvents(session, send) {
|
|
|
157
159
|
const onError = (error) => send({ type: "error", message: error.message });
|
|
158
160
|
const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
|
|
159
161
|
const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
|
|
162
|
+
const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
|
|
163
|
+
session.on("user_message", onUserMessage);
|
|
160
164
|
session.on("text_delta", onTextDelta);
|
|
161
165
|
session.on("tool_start", onToolStart);
|
|
162
166
|
session.on("tool_end", onToolEnd);
|
|
@@ -166,7 +170,9 @@ function subscribeSessionEvents(session, send) {
|
|
|
166
170
|
session.on("error", onError);
|
|
167
171
|
session.on("background_task_event", onBackgroundTaskEvent);
|
|
168
172
|
session.on("background_job_group_event", onBackgroundJobGroupEvent);
|
|
173
|
+
session.on("execution_workspace_event", onExecutionWorkspace);
|
|
169
174
|
return () => {
|
|
175
|
+
session.off("user_message", onUserMessage);
|
|
170
176
|
session.off("text_delta", onTextDelta);
|
|
171
177
|
session.off("tool_start", onToolStart);
|
|
172
178
|
session.off("tool_end", onToolEnd);
|
|
@@ -176,6 +182,7 @@ function subscribeSessionEvents(session, send) {
|
|
|
176
182
|
session.off("error", onError);
|
|
177
183
|
session.off("background_task_event", onBackgroundTaskEvent);
|
|
178
184
|
session.off("background_job_group_event", onBackgroundJobGroupEvent);
|
|
185
|
+
session.off("execution_workspace_event", onExecutionWorkspace);
|
|
179
186
|
};
|
|
180
187
|
}
|
|
181
188
|
function createWsMessageHandler(session, send) {
|
|
@@ -219,7 +226,7 @@ function isSessionControlMessage(msg) {
|
|
|
219
226
|
return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
|
|
220
227
|
}
|
|
221
228
|
function isSessionQueryMessage(msg) {
|
|
222
|
-
return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
|
|
229
|
+
return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending" || msg.type === "get-execution-workspace";
|
|
223
230
|
}
|
|
224
231
|
function isBackgroundQueryMessage(msg) {
|
|
225
232
|
return msg.type === "get-background-tasks" || msg.type === "get-background-task" || msg.type === "read-background-task-log" || msg.type === "get-background-job-groups" || msg.type === "get-background-job-group" || msg.type === "wait-background-job-group";
|
|
@@ -261,6 +268,11 @@ function handleSessionQueryMessage(session, send, msg) {
|
|
|
261
268
|
send({ type: "context", state: session.getContextState() });
|
|
262
269
|
} else if (msg.type === "get-executing") {
|
|
263
270
|
send({ type: "executing", executing: session.isExecuting() });
|
|
271
|
+
} else if (msg.type === "get-execution-workspace") {
|
|
272
|
+
send({
|
|
273
|
+
type: "execution_workspace_event",
|
|
274
|
+
snapshot: session.getExecutionWorkspaceSnapshot()
|
|
275
|
+
});
|
|
264
276
|
} else {
|
|
265
277
|
send({ type: "pending", pending: session.getPendingPrompt() });
|
|
266
278
|
}
|
|
@@ -289,8 +301,94 @@ function createWsTransport(options) {
|
|
|
289
301
|
}
|
|
290
302
|
};
|
|
291
303
|
}
|
|
304
|
+
|
|
305
|
+
// src/ws-transport-configurable.ts
|
|
306
|
+
var import_node_http = require("http");
|
|
307
|
+
var import_ws = require("ws");
|
|
308
|
+
var DEFAULT_PORT = 7070;
|
|
309
|
+
var DEFAULT_MAX_RETRIES = 20;
|
|
310
|
+
var WsTransport = class {
|
|
311
|
+
name = "ws";
|
|
312
|
+
defaultEnabled = true;
|
|
313
|
+
optionsSchema = {
|
|
314
|
+
port: { type: "number", description: "WebSocket server port", default: DEFAULT_PORT },
|
|
315
|
+
maxRetries: {
|
|
316
|
+
type: "number",
|
|
317
|
+
description: "Port retry attempts when port is occupied",
|
|
318
|
+
default: DEFAULT_MAX_RETRIES
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
session = null;
|
|
322
|
+
stopFn = null;
|
|
323
|
+
port;
|
|
324
|
+
maxRetries;
|
|
325
|
+
constructor(config = {}) {
|
|
326
|
+
this.port = config.port ?? DEFAULT_PORT;
|
|
327
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
328
|
+
}
|
|
329
|
+
attach(session) {
|
|
330
|
+
this.session = session;
|
|
331
|
+
}
|
|
332
|
+
async start() {
|
|
333
|
+
if (!this.session) throw new Error("WsTransport: attach() must be called before start()");
|
|
334
|
+
const handle = await this.bindWithRetry(this.session, this.port, this.maxRetries);
|
|
335
|
+
this.stopFn = handle.stop;
|
|
336
|
+
}
|
|
337
|
+
async stop() {
|
|
338
|
+
await this.stopFn?.();
|
|
339
|
+
this.stopFn = null;
|
|
340
|
+
}
|
|
341
|
+
validateOptions(options) {
|
|
342
|
+
const { port, maxRetries } = options;
|
|
343
|
+
if (port !== void 0 && (typeof port !== "number" || port < 1 || port > 65535)) return false;
|
|
344
|
+
if (maxRetries !== void 0 && (typeof maxRetries !== "number" || maxRetries < 0))
|
|
345
|
+
return false;
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
bindWithRetry(session, port, retriesLeft) {
|
|
349
|
+
return this.tryBind(session, port).catch((err) => {
|
|
350
|
+
if (err.code === "EADDRINUSE" && retriesLeft > 0)
|
|
351
|
+
return this.bindWithRetry(session, port + 1, retriesLeft - 1);
|
|
352
|
+
throw err;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
tryBind(session, port) {
|
|
356
|
+
return new Promise((resolve, reject) => {
|
|
357
|
+
const httpServer = (0, import_node_http.createServer)((_, res) => {
|
|
358
|
+
res.writeHead(400).end("WebSocket endpoint");
|
|
359
|
+
});
|
|
360
|
+
httpServer.on("error", (err) => {
|
|
361
|
+
httpServer.close();
|
|
362
|
+
reject(err);
|
|
363
|
+
});
|
|
364
|
+
httpServer.listen(port, "127.0.0.1", () => {
|
|
365
|
+
const wss = new import_ws.WebSocketServer({ server: httpServer });
|
|
366
|
+
wss.on("connection", (ws) => {
|
|
367
|
+
const send = (message) => {
|
|
368
|
+
if (ws.readyState === import_ws.WebSocket.OPEN) ws.send(JSON.stringify(message));
|
|
369
|
+
};
|
|
370
|
+
const { onMessage, cleanup } = createWsHandler({ session, send });
|
|
371
|
+
ws.on("message", (data) => onMessage(String(data)));
|
|
372
|
+
ws.on("close", cleanup);
|
|
373
|
+
ws.on("error", cleanup);
|
|
374
|
+
send({ type: "messages", messages: session.getMessages() });
|
|
375
|
+
send({
|
|
376
|
+
type: "execution_workspace_event",
|
|
377
|
+
snapshot: session.getExecutionWorkspaceSnapshot()
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
resolve({
|
|
381
|
+
stop: () => new Promise((res) => {
|
|
382
|
+
wss.close(() => httpServer.close(() => res()));
|
|
383
|
+
})
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
};
|
|
292
389
|
// Annotate the CommonJS export names for ESM import in node:
|
|
293
390
|
0 && (module.exports = {
|
|
391
|
+
WsTransport,
|
|
294
392
|
createWsHandler,
|
|
295
393
|
createWsTransport
|
|
296
394
|
});
|
package/dist/node/index.d.cts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor
|
|
1
|
+
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, IExecutionWorkspaceSnapshot, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor } from '@robota-sdk/agent-sdk';
|
|
2
|
+
export { IExecutionWorkspaceEntry, IExecutionWorkspaceSnapshot, TExecutionAttention, TExecutionWorkspaceStatus } from '@robota-sdk/agent-sdk';
|
|
3
|
+
import { ITransportAdapter, IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
|
|
4
|
+
import { ISession, TUniversalValue } from '@robota-sdk/agent-core';
|
|
2
5
|
|
|
3
6
|
type TBackgroundControlAction = 'cancel' | 'close' | 'send';
|
|
4
7
|
/** Inbound message from client to server. */
|
|
@@ -21,6 +24,8 @@ type TClientMessage = {
|
|
|
21
24
|
type: 'get-executing';
|
|
22
25
|
} | {
|
|
23
26
|
type: 'get-pending';
|
|
27
|
+
} | {
|
|
28
|
+
type: 'get-execution-workspace';
|
|
24
29
|
} | {
|
|
25
30
|
type: 'get-background-tasks';
|
|
26
31
|
filter?: IBackgroundTaskListFilter;
|
|
@@ -55,6 +60,9 @@ type TClientMessage = {
|
|
|
55
60
|
type TServerMessage = {
|
|
56
61
|
type: 'text_delta';
|
|
57
62
|
delta: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'user_message';
|
|
65
|
+
content: string;
|
|
58
66
|
} | {
|
|
59
67
|
type: 'tool_start';
|
|
60
68
|
state: IToolState;
|
|
@@ -91,6 +99,9 @@ type TServerMessage = {
|
|
|
91
99
|
} | {
|
|
92
100
|
type: 'pending';
|
|
93
101
|
pending: string | null;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'execution_workspace_event';
|
|
104
|
+
snapshot: IExecutionWorkspaceSnapshot;
|
|
94
105
|
} | {
|
|
95
106
|
type: 'background_task_event';
|
|
96
107
|
event: TBackgroundTaskEvent;
|
|
@@ -180,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
|
|
|
180
191
|
onMessage: ((data: string) => void) | null;
|
|
181
192
|
};
|
|
182
193
|
|
|
183
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Self-contained WS transport implementing IConfigurableTransport.
|
|
196
|
+
* Owns the WebSocket server lifecycle (ws package), started/stopped via the transport registry.
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
interface IWsTransportConfig {
|
|
200
|
+
port?: number;
|
|
201
|
+
maxRetries?: number;
|
|
202
|
+
}
|
|
203
|
+
declare class WsTransport implements IConfigurableTransport {
|
|
204
|
+
readonly name = "ws";
|
|
205
|
+
readonly defaultEnabled = true;
|
|
206
|
+
readonly optionsSchema: {
|
|
207
|
+
port: {
|
|
208
|
+
type: string;
|
|
209
|
+
description: string;
|
|
210
|
+
default: number;
|
|
211
|
+
};
|
|
212
|
+
maxRetries: {
|
|
213
|
+
type: string;
|
|
214
|
+
description: string;
|
|
215
|
+
default: number;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
private session;
|
|
219
|
+
private stopFn;
|
|
220
|
+
private readonly port;
|
|
221
|
+
private readonly maxRetries;
|
|
222
|
+
constructor(config?: IWsTransportConfig);
|
|
223
|
+
attach(session: ISession): void;
|
|
224
|
+
start(): Promise<void>;
|
|
225
|
+
stop(): Promise<void>;
|
|
226
|
+
validateOptions(options: Record<string, TUniversalValue>): boolean;
|
|
227
|
+
private bindWithRetry;
|
|
228
|
+
private tryBind;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { type IWsHandlerOptions, type IWsTransportConfig, type IWsTransportOptions, type TClientMessage, type TServerMessage, WsTransport, createWsHandler, createWsTransport };
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor
|
|
1
|
+
import { IToolState, IExecutionResult, ICommandResult, InteractiveSession, IExecutionWorkspaceSnapshot, TBackgroundTaskEvent, TBackgroundJobGroupEvent, IBackgroundTaskState, IBackgroundJobGroupState, IBackgroundTaskLogPage, IBackgroundTaskListFilter, IBackgroundTaskInput, IBackgroundTaskLogCursor } from '@robota-sdk/agent-sdk';
|
|
2
|
+
export { IExecutionWorkspaceEntry, IExecutionWorkspaceSnapshot, TExecutionAttention, TExecutionWorkspaceStatus } from '@robota-sdk/agent-sdk';
|
|
3
|
+
import { ITransportAdapter, IConfigurableTransport } from '@robota-sdk/agent-interface-transport';
|
|
4
|
+
import { ISession, TUniversalValue } from '@robota-sdk/agent-core';
|
|
2
5
|
|
|
3
6
|
type TBackgroundControlAction = 'cancel' | 'close' | 'send';
|
|
4
7
|
/** Inbound message from client to server. */
|
|
@@ -21,6 +24,8 @@ type TClientMessage = {
|
|
|
21
24
|
type: 'get-executing';
|
|
22
25
|
} | {
|
|
23
26
|
type: 'get-pending';
|
|
27
|
+
} | {
|
|
28
|
+
type: 'get-execution-workspace';
|
|
24
29
|
} | {
|
|
25
30
|
type: 'get-background-tasks';
|
|
26
31
|
filter?: IBackgroundTaskListFilter;
|
|
@@ -55,6 +60,9 @@ type TClientMessage = {
|
|
|
55
60
|
type TServerMessage = {
|
|
56
61
|
type: 'text_delta';
|
|
57
62
|
delta: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'user_message';
|
|
65
|
+
content: string;
|
|
58
66
|
} | {
|
|
59
67
|
type: 'tool_start';
|
|
60
68
|
state: IToolState;
|
|
@@ -91,6 +99,9 @@ type TServerMessage = {
|
|
|
91
99
|
} | {
|
|
92
100
|
type: 'pending';
|
|
93
101
|
pending: string | null;
|
|
102
|
+
} | {
|
|
103
|
+
type: 'execution_workspace_event';
|
|
104
|
+
snapshot: IExecutionWorkspaceSnapshot;
|
|
94
105
|
} | {
|
|
95
106
|
type: 'background_task_event';
|
|
96
107
|
event: TBackgroundTaskEvent;
|
|
@@ -180,4 +191,41 @@ declare function createWsTransport(options: IWsTransportOptions): ITransportAdap
|
|
|
180
191
|
onMessage: ((data: string) => void) | null;
|
|
181
192
|
};
|
|
182
193
|
|
|
183
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Self-contained WS transport implementing IConfigurableTransport.
|
|
196
|
+
* Owns the WebSocket server lifecycle (ws package), started/stopped via the transport registry.
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
interface IWsTransportConfig {
|
|
200
|
+
port?: number;
|
|
201
|
+
maxRetries?: number;
|
|
202
|
+
}
|
|
203
|
+
declare class WsTransport implements IConfigurableTransport {
|
|
204
|
+
readonly name = "ws";
|
|
205
|
+
readonly defaultEnabled = true;
|
|
206
|
+
readonly optionsSchema: {
|
|
207
|
+
port: {
|
|
208
|
+
type: string;
|
|
209
|
+
description: string;
|
|
210
|
+
default: number;
|
|
211
|
+
};
|
|
212
|
+
maxRetries: {
|
|
213
|
+
type: string;
|
|
214
|
+
description: string;
|
|
215
|
+
default: number;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
private session;
|
|
219
|
+
private stopFn;
|
|
220
|
+
private readonly port;
|
|
221
|
+
private readonly maxRetries;
|
|
222
|
+
constructor(config?: IWsTransportConfig);
|
|
223
|
+
attach(session: ISession): void;
|
|
224
|
+
start(): Promise<void>;
|
|
225
|
+
stop(): Promise<void>;
|
|
226
|
+
validateOptions(options: Record<string, TUniversalValue>): boolean;
|
|
227
|
+
private bindWithRetry;
|
|
228
|
+
private tryBind;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { type IWsHandlerOptions, type IWsTransportConfig, type IWsTransportOptions, type TClientMessage, type TServerMessage, WsTransport, createWsHandler, createWsTransport };
|
package/dist/node/index.js
CHANGED
|
@@ -121,6 +121,7 @@ function createWsHandler(options) {
|
|
|
121
121
|
return { onMessage, cleanup };
|
|
122
122
|
}
|
|
123
123
|
function subscribeSessionEvents(session, send) {
|
|
124
|
+
const onUserMessage = (content) => send({ type: "user_message", content });
|
|
124
125
|
const onTextDelta = (delta) => send({ type: "text_delta", delta });
|
|
125
126
|
const onToolStart = (state) => send({ type: "tool_start", state });
|
|
126
127
|
const onToolEnd = (state) => send({ type: "tool_end", state });
|
|
@@ -130,6 +131,8 @@ function subscribeSessionEvents(session, send) {
|
|
|
130
131
|
const onError = (error) => send({ type: "error", message: error.message });
|
|
131
132
|
const onBackgroundTaskEvent = (event) => send({ type: "background_task_event", event });
|
|
132
133
|
const onBackgroundJobGroupEvent = (event) => send({ type: "background_job_group_event", event });
|
|
134
|
+
const onExecutionWorkspace = (event) => send({ type: "execution_workspace_event", snapshot: event.snapshot });
|
|
135
|
+
session.on("user_message", onUserMessage);
|
|
133
136
|
session.on("text_delta", onTextDelta);
|
|
134
137
|
session.on("tool_start", onToolStart);
|
|
135
138
|
session.on("tool_end", onToolEnd);
|
|
@@ -139,7 +142,9 @@ function subscribeSessionEvents(session, send) {
|
|
|
139
142
|
session.on("error", onError);
|
|
140
143
|
session.on("background_task_event", onBackgroundTaskEvent);
|
|
141
144
|
session.on("background_job_group_event", onBackgroundJobGroupEvent);
|
|
145
|
+
session.on("execution_workspace_event", onExecutionWorkspace);
|
|
142
146
|
return () => {
|
|
147
|
+
session.off("user_message", onUserMessage);
|
|
143
148
|
session.off("text_delta", onTextDelta);
|
|
144
149
|
session.off("tool_start", onToolStart);
|
|
145
150
|
session.off("tool_end", onToolEnd);
|
|
@@ -149,6 +154,7 @@ function subscribeSessionEvents(session, send) {
|
|
|
149
154
|
session.off("error", onError);
|
|
150
155
|
session.off("background_task_event", onBackgroundTaskEvent);
|
|
151
156
|
session.off("background_job_group_event", onBackgroundJobGroupEvent);
|
|
157
|
+
session.off("execution_workspace_event", onExecutionWorkspace);
|
|
152
158
|
};
|
|
153
159
|
}
|
|
154
160
|
function createWsMessageHandler(session, send) {
|
|
@@ -192,7 +198,7 @@ function isSessionControlMessage(msg) {
|
|
|
192
198
|
return msg.type === "submit" || msg.type === "command" || msg.type === "abort" || msg.type === "cancel-queue";
|
|
193
199
|
}
|
|
194
200
|
function isSessionQueryMessage(msg) {
|
|
195
|
-
return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending";
|
|
201
|
+
return msg.type === "get-messages" || msg.type === "get-context" || msg.type === "get-executing" || msg.type === "get-pending" || msg.type === "get-execution-workspace";
|
|
196
202
|
}
|
|
197
203
|
function isBackgroundQueryMessage(msg) {
|
|
198
204
|
return msg.type === "get-background-tasks" || msg.type === "get-background-task" || msg.type === "read-background-task-log" || msg.type === "get-background-job-groups" || msg.type === "get-background-job-group" || msg.type === "wait-background-job-group";
|
|
@@ -234,6 +240,11 @@ function handleSessionQueryMessage(session, send, msg) {
|
|
|
234
240
|
send({ type: "context", state: session.getContextState() });
|
|
235
241
|
} else if (msg.type === "get-executing") {
|
|
236
242
|
send({ type: "executing", executing: session.isExecuting() });
|
|
243
|
+
} else if (msg.type === "get-execution-workspace") {
|
|
244
|
+
send({
|
|
245
|
+
type: "execution_workspace_event",
|
|
246
|
+
snapshot: session.getExecutionWorkspaceSnapshot()
|
|
247
|
+
});
|
|
237
248
|
} else {
|
|
238
249
|
send({ type: "pending", pending: session.getPendingPrompt() });
|
|
239
250
|
}
|
|
@@ -262,7 +273,93 @@ function createWsTransport(options) {
|
|
|
262
273
|
}
|
|
263
274
|
};
|
|
264
275
|
}
|
|
276
|
+
|
|
277
|
+
// src/ws-transport-configurable.ts
|
|
278
|
+
import { createServer } from "http";
|
|
279
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
280
|
+
var DEFAULT_PORT = 7070;
|
|
281
|
+
var DEFAULT_MAX_RETRIES = 20;
|
|
282
|
+
var WsTransport = class {
|
|
283
|
+
name = "ws";
|
|
284
|
+
defaultEnabled = true;
|
|
285
|
+
optionsSchema = {
|
|
286
|
+
port: { type: "number", description: "WebSocket server port", default: DEFAULT_PORT },
|
|
287
|
+
maxRetries: {
|
|
288
|
+
type: "number",
|
|
289
|
+
description: "Port retry attempts when port is occupied",
|
|
290
|
+
default: DEFAULT_MAX_RETRIES
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
session = null;
|
|
294
|
+
stopFn = null;
|
|
295
|
+
port;
|
|
296
|
+
maxRetries;
|
|
297
|
+
constructor(config = {}) {
|
|
298
|
+
this.port = config.port ?? DEFAULT_PORT;
|
|
299
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
300
|
+
}
|
|
301
|
+
attach(session) {
|
|
302
|
+
this.session = session;
|
|
303
|
+
}
|
|
304
|
+
async start() {
|
|
305
|
+
if (!this.session) throw new Error("WsTransport: attach() must be called before start()");
|
|
306
|
+
const handle = await this.bindWithRetry(this.session, this.port, this.maxRetries);
|
|
307
|
+
this.stopFn = handle.stop;
|
|
308
|
+
}
|
|
309
|
+
async stop() {
|
|
310
|
+
await this.stopFn?.();
|
|
311
|
+
this.stopFn = null;
|
|
312
|
+
}
|
|
313
|
+
validateOptions(options) {
|
|
314
|
+
const { port, maxRetries } = options;
|
|
315
|
+
if (port !== void 0 && (typeof port !== "number" || port < 1 || port > 65535)) return false;
|
|
316
|
+
if (maxRetries !== void 0 && (typeof maxRetries !== "number" || maxRetries < 0))
|
|
317
|
+
return false;
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
bindWithRetry(session, port, retriesLeft) {
|
|
321
|
+
return this.tryBind(session, port).catch((err) => {
|
|
322
|
+
if (err.code === "EADDRINUSE" && retriesLeft > 0)
|
|
323
|
+
return this.bindWithRetry(session, port + 1, retriesLeft - 1);
|
|
324
|
+
throw err;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
tryBind(session, port) {
|
|
328
|
+
return new Promise((resolve, reject) => {
|
|
329
|
+
const httpServer = createServer((_, res) => {
|
|
330
|
+
res.writeHead(400).end("WebSocket endpoint");
|
|
331
|
+
});
|
|
332
|
+
httpServer.on("error", (err) => {
|
|
333
|
+
httpServer.close();
|
|
334
|
+
reject(err);
|
|
335
|
+
});
|
|
336
|
+
httpServer.listen(port, "127.0.0.1", () => {
|
|
337
|
+
const wss = new WebSocketServer({ server: httpServer });
|
|
338
|
+
wss.on("connection", (ws) => {
|
|
339
|
+
const send = (message) => {
|
|
340
|
+
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(message));
|
|
341
|
+
};
|
|
342
|
+
const { onMessage, cleanup } = createWsHandler({ session, send });
|
|
343
|
+
ws.on("message", (data) => onMessage(String(data)));
|
|
344
|
+
ws.on("close", cleanup);
|
|
345
|
+
ws.on("error", cleanup);
|
|
346
|
+
send({ type: "messages", messages: session.getMessages() });
|
|
347
|
+
send({
|
|
348
|
+
type: "execution_workspace_event",
|
|
349
|
+
snapshot: session.getExecutionWorkspaceSnapshot()
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
resolve({
|
|
353
|
+
stop: () => new Promise((res) => {
|
|
354
|
+
wss.close(() => httpServer.close(() => res()));
|
|
355
|
+
})
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
};
|
|
265
361
|
export {
|
|
362
|
+
WsTransport,
|
|
266
363
|
createWsHandler,
|
|
267
364
|
createWsTransport
|
|
268
365
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-transport-ws",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.63",
|
|
4
4
|
"description": "WebSocket transport adapter for exposing InteractiveSession over real-time connections",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
@@ -30,9 +30,13 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
33
|
+
"ws": "^8.18.3",
|
|
34
|
+
"@robota-sdk/agent-core": "3.0.0-beta.63",
|
|
35
|
+
"@robota-sdk/agent-interface-transport": "3.0.0-beta.63",
|
|
36
|
+
"@robota-sdk/agent-sdk": "3.0.0-beta.63"
|
|
34
37
|
},
|
|
35
38
|
"devDependencies": {
|
|
39
|
+
"@types/ws": "^8.5.10",
|
|
36
40
|
"rimraf": "^5.0.5",
|
|
37
41
|
"tsup": "^8.0.1",
|
|
38
42
|
"typescript": "^5.3.3",
|