@awarevue/agent-sdk 2.0.74 → 2.0.76
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/__tests__/agent-server.test.d.ts +1 -0
- package/dist/__tests__/agent-server.test.js +257 -0
- package/dist/__tests__/in-memory-transport.d.ts +10 -0
- package/dist/__tests__/in-memory-transport.js +42 -0
- package/dist/agent-app-with-defaults.d.ts +16 -0
- package/dist/agent-app-with-defaults.js +23 -0
- package/dist/agent-protocol.d.ts +1 -1
- package/dist/agent-protocol.js +13 -6
- package/dist/agent-server.d.ts +50 -0
- package/dist/agent-server.js +142 -0
- package/dist/default-validator.d.ts +1 -1
- package/dist/hubs/in-memory.js +2 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/package.json +5 -4
- package/dist/transports/index.d.ts +1 -0
- package/dist/transports/index.js +1 -0
- package/dist/transports/logging.js +1 -1
- package/dist/transports/ws-json-encoder.d.ts +12 -0
- package/dist/transports/ws-json-encoder.js +28 -0
- package/dist/transports/ws.d.ts +3 -17
- package/dist/transports/ws.js +3 -19
- package/package.json +5 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
const agent_server_1 = require("../agent-server");
|
|
6
|
+
const in_memory_1 = require("../hubs/in-memory");
|
|
7
|
+
const constants_1 = require("../constants");
|
|
8
|
+
const in_memory_transport_1 = require("./in-memory-transport");
|
|
9
|
+
let msgSeq = 0;
|
|
10
|
+
const envelope = (from, payload) => ({
|
|
11
|
+
...payload,
|
|
12
|
+
id: `msg-${++msgSeq}`,
|
|
13
|
+
from,
|
|
14
|
+
version: constants_1.AGENT_PROTOCOL_VERSION,
|
|
15
|
+
on: Date.now(),
|
|
16
|
+
});
|
|
17
|
+
const registerPayload = (agentId) => envelope(agentId, {
|
|
18
|
+
kind: 'register',
|
|
19
|
+
providers: {
|
|
20
|
+
'test-provider': {
|
|
21
|
+
title: 'Test Provider',
|
|
22
|
+
configSchema: {},
|
|
23
|
+
configDefault: {},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const startRsPayload = (agentId, requestId) => envelope(agentId, { kind: 'start-rs', requestId });
|
|
28
|
+
const stopRsPayload = (agentId, requestId) => envelope(agentId, { kind: 'stop-rs', requestId });
|
|
29
|
+
/**
|
|
30
|
+
* Connects a fake agent to the hub and returns the agent's side of the transport.
|
|
31
|
+
*/
|
|
32
|
+
function connectAgent(hub, peerId) {
|
|
33
|
+
const [agentSide, serverSide] = (0, in_memory_transport_1.createTransportPair)();
|
|
34
|
+
hub.addPeer(peerId, serverSide);
|
|
35
|
+
return agentSide;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Subscribe BEFORE sending — in-memory transports deliver synchronously,
|
|
39
|
+
* so the reply arrives during send() and is missed if subscribed after.
|
|
40
|
+
*/
|
|
41
|
+
function sendAndAwait(agent, msg, replyKind) {
|
|
42
|
+
const p = (0, rxjs_1.firstValueFrom)(agent.messages$.pipe((0, rxjs_1.filter)((m) => m.kind === replyKind), (0, rxjs_1.timeout)(1000)));
|
|
43
|
+
agent.send(msg);
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
/* ------------------------------------------------------------------ */
|
|
47
|
+
/* Tests */
|
|
48
|
+
/* ------------------------------------------------------------------ */
|
|
49
|
+
(0, vitest_1.describe)('AgentServer', () => {
|
|
50
|
+
let hub;
|
|
51
|
+
let server;
|
|
52
|
+
(0, vitest_1.beforeEach)(() => {
|
|
53
|
+
hub = new in_memory_1.InMemoryHub();
|
|
54
|
+
msgSeq = 0;
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.afterEach)(() => {
|
|
57
|
+
server === null || server === void 0 ? void 0 : server.finalize();
|
|
58
|
+
hub === null || hub === void 0 ? void 0 : hub.close();
|
|
59
|
+
});
|
|
60
|
+
// ── Registration ──────────────────────────────────────────────────
|
|
61
|
+
(0, vitest_1.describe)('registration', () => {
|
|
62
|
+
(0, vitest_1.it)('should resolve agentId from msg.from on register', async () => {
|
|
63
|
+
const registered = [];
|
|
64
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
65
|
+
onRegister: ({ agentId, accept }) => {
|
|
66
|
+
registered.push(agentId);
|
|
67
|
+
accept();
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
server.init();
|
|
71
|
+
const agent = connectAgent(hub, 1);
|
|
72
|
+
const reply = await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
73
|
+
(0, vitest_1.expect)(reply.kind).toBe('register-rs');
|
|
74
|
+
(0, vitest_1.expect)(registered).toEqual(['my-agent']);
|
|
75
|
+
});
|
|
76
|
+
(0, vitest_1.it)('should allow rejecting registration', async () => {
|
|
77
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
78
|
+
onRegister: ({ reject }) => {
|
|
79
|
+
reject('not allowed');
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
server.init();
|
|
83
|
+
const agent = connectAgent(hub, 1);
|
|
84
|
+
const reply = await sendAndAwait(agent, registerPayload('bad-agent'), 'error-rs');
|
|
85
|
+
(0, vitest_1.expect)(reply.kind).toBe('error-rs');
|
|
86
|
+
(0, vitest_1.expect)(reply.error).toBe('not allowed');
|
|
87
|
+
});
|
|
88
|
+
(0, vitest_1.it)('should handle multiple agents with different peer IDs', async () => {
|
|
89
|
+
const registered = [];
|
|
90
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
91
|
+
onRegister: ({ agentId, accept }) => {
|
|
92
|
+
registered.push(agentId);
|
|
93
|
+
accept();
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
server.init();
|
|
97
|
+
const agentA = connectAgent(hub, 1);
|
|
98
|
+
const agentB = connectAgent(hub, 2);
|
|
99
|
+
await sendAndAwait(agentA, registerPayload('agent-alpha'), 'register-rs');
|
|
100
|
+
await sendAndAwait(agentB, registerPayload('agent-beta'), 'register-rs');
|
|
101
|
+
(0, vitest_1.expect)(registered).toContain('agent-alpha');
|
|
102
|
+
(0, vitest_1.expect)(registered).toContain('agent-beta');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
// ── Start / Stop ──────────────────────────────────────────────────
|
|
106
|
+
(0, vitest_1.describe)('start and stop', () => {
|
|
107
|
+
(0, vitest_1.it)('should route startAgent by agentId to correct peer', async () => {
|
|
108
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
109
|
+
onRegister: ({ accept }) => accept(),
|
|
110
|
+
});
|
|
111
|
+
server.init();
|
|
112
|
+
const agentA = connectAgent(hub, 1);
|
|
113
|
+
const agentB = connectAgent(hub, 2);
|
|
114
|
+
await sendAndAwait(agentA, registerPayload('agent-alpha'), 'register-rs');
|
|
115
|
+
await sendAndAwait(agentB, registerPayload('agent-beta'), 'register-rs');
|
|
116
|
+
// subscribe before sending
|
|
117
|
+
const startPromise = (0, rxjs_1.firstValueFrom)(agentB.messages$.pipe((0, rxjs_1.filter)((m) => m.kind === 'start'), (0, rxjs_1.timeout)(1000)));
|
|
118
|
+
server.startAgent({
|
|
119
|
+
agentId: 'agent-beta',
|
|
120
|
+
provider: 'test-provider',
|
|
121
|
+
config: { url: 'http://test' },
|
|
122
|
+
lastEventForeignRef: null,
|
|
123
|
+
lastEventTimestamp: null,
|
|
124
|
+
});
|
|
125
|
+
const startMsg = await startPromise;
|
|
126
|
+
(0, vitest_1.expect)(startMsg.kind).toBe('start');
|
|
127
|
+
(0, vitest_1.expect)(startMsg.provider).toBe('test-provider');
|
|
128
|
+
});
|
|
129
|
+
(0, vitest_1.it)('should fire onStarted when agent replies with start-rs', async () => {
|
|
130
|
+
const started = [];
|
|
131
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
132
|
+
onRegister: ({ accept }) => accept(),
|
|
133
|
+
onStarted: ({ agentId }) => started.push(agentId),
|
|
134
|
+
});
|
|
135
|
+
server.init();
|
|
136
|
+
const agent = connectAgent(hub, 1);
|
|
137
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
138
|
+
agent.send(startRsPayload('my-agent', 'req-1'));
|
|
139
|
+
(0, vitest_1.expect)(started).toEqual(['my-agent']);
|
|
140
|
+
});
|
|
141
|
+
(0, vitest_1.it)('should fire onStopped when agent replies with stop-rs', async () => {
|
|
142
|
+
const stopped = [];
|
|
143
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
144
|
+
onRegister: ({ accept }) => accept(),
|
|
145
|
+
onStopped: ({ agentId }) => stopped.push(agentId),
|
|
146
|
+
});
|
|
147
|
+
server.init();
|
|
148
|
+
const agent = connectAgent(hub, 1);
|
|
149
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
150
|
+
agent.send(stopRsPayload('my-agent', 'req-2'));
|
|
151
|
+
(0, vitest_1.expect)(stopped).toEqual(['my-agent']);
|
|
152
|
+
});
|
|
153
|
+
(0, vitest_1.it)('should send stop to the correct agent', async () => {
|
|
154
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
155
|
+
onRegister: ({ accept }) => accept(),
|
|
156
|
+
});
|
|
157
|
+
server.init();
|
|
158
|
+
const agent = connectAgent(hub, 1);
|
|
159
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
160
|
+
const stopPromise = (0, rxjs_1.firstValueFrom)(agent.messages$.pipe((0, rxjs_1.filter)((m) => m.kind === 'stop'), (0, rxjs_1.timeout)(1000)));
|
|
161
|
+
server.stopAgent('my-agent', 'test-provider');
|
|
162
|
+
const stopMsg = await stopPromise;
|
|
163
|
+
(0, vitest_1.expect)(stopMsg.kind).toBe('stop');
|
|
164
|
+
(0, vitest_1.expect)(stopMsg.provider).toBe('test-provider');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
// ── Disconnect ────────────────────────────────────────────────────
|
|
168
|
+
(0, vitest_1.describe)('disconnect', () => {
|
|
169
|
+
(0, vitest_1.it)('should fire onUnregistered with agentId when peer disconnects after registration', async () => {
|
|
170
|
+
const unregistered = [];
|
|
171
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
172
|
+
onRegister: ({ accept }) => accept(),
|
|
173
|
+
onUnregistered: ({ agentId }) => unregistered.push(agentId),
|
|
174
|
+
});
|
|
175
|
+
server.init();
|
|
176
|
+
const agent = connectAgent(hub, 1);
|
|
177
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
178
|
+
agent.close();
|
|
179
|
+
(0, vitest_1.expect)(unregistered).toEqual(['my-agent']);
|
|
180
|
+
});
|
|
181
|
+
(0, vitest_1.it)('should not fire onUnregistered for peers that never registered', async () => {
|
|
182
|
+
const unregistered = [];
|
|
183
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
184
|
+
onUnregistered: ({ agentId }) => unregistered.push(agentId),
|
|
185
|
+
});
|
|
186
|
+
server.init();
|
|
187
|
+
// connect and immediately disconnect without registering
|
|
188
|
+
const agent = connectAgent(hub, 1);
|
|
189
|
+
agent.close();
|
|
190
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
191
|
+
(0, vitest_1.expect)(unregistered).toEqual([]);
|
|
192
|
+
});
|
|
193
|
+
(0, vitest_1.it)('should clean up maps after disconnect', async () => {
|
|
194
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
195
|
+
onRegister: ({ accept }) => accept(),
|
|
196
|
+
});
|
|
197
|
+
server.init();
|
|
198
|
+
const agent = connectAgent(hub, 1);
|
|
199
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
200
|
+
(0, vitest_1.expect)(server.getAgentSender('my-agent')).not.toBeNull();
|
|
201
|
+
agent.close();
|
|
202
|
+
(0, vitest_1.expect)(server.getAgentSender('my-agent')).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
// ── Protocol validation ───────────────────────────────────────────
|
|
206
|
+
(0, vitest_1.describe)('protocol validation', () => {
|
|
207
|
+
(0, vitest_1.it)('should reject messages with wrong protocol version', async () => {
|
|
208
|
+
server = new agent_server_1.AgentServer(hub);
|
|
209
|
+
server.init();
|
|
210
|
+
const notifications = [];
|
|
211
|
+
server.notifications$.subscribe((n) => notifications.push(n));
|
|
212
|
+
const agent = connectAgent(hub, 1);
|
|
213
|
+
const badMsg = {
|
|
214
|
+
kind: 'register',
|
|
215
|
+
providers: {},
|
|
216
|
+
id: 'bad-1',
|
|
217
|
+
from: 'bad-agent',
|
|
218
|
+
version: 999,
|
|
219
|
+
on: Date.now(),
|
|
220
|
+
};
|
|
221
|
+
const reply = await sendAndAwait(agent, badMsg, 'error-rs');
|
|
222
|
+
(0, vitest_1.expect)(reply.kind).toBe('error-rs');
|
|
223
|
+
(0, vitest_1.expect)(reply.error).toContain('Incompatible protocol version');
|
|
224
|
+
(0, vitest_1.expect)(notifications.some((n) => n.includes('incompatible'))).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
// ── messages$ stream ──────────────────────────────────────────────
|
|
228
|
+
(0, vitest_1.describe)('messages$ stream', () => {
|
|
229
|
+
(0, vitest_1.it)('should include agentId alongside peer in emitted messages', async () => {
|
|
230
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
231
|
+
onRegister: ({ accept }) => accept(),
|
|
232
|
+
});
|
|
233
|
+
server.init();
|
|
234
|
+
const emittedPromise = (0, rxjs_1.firstValueFrom)(server.messages$.pipe((0, rxjs_1.timeout)(1000)));
|
|
235
|
+
const agent = connectAgent(hub, 42);
|
|
236
|
+
agent.send(registerPayload('stream-agent'));
|
|
237
|
+
const emitted = await emittedPromise;
|
|
238
|
+
(0, vitest_1.expect)(emitted.peer).toBe(42);
|
|
239
|
+
(0, vitest_1.expect)(emitted.agentId).toBe('stream-agent');
|
|
240
|
+
(0, vitest_1.expect)(emitted.msg.kind).toBe('register');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
// ── finalize ──────────────────────────────────────────────────────
|
|
244
|
+
(0, vitest_1.describe)('finalize', () => {
|
|
245
|
+
(0, vitest_1.it)('should clear internal maps on finalize', async () => {
|
|
246
|
+
server = new agent_server_1.AgentServer(hub, {
|
|
247
|
+
onRegister: ({ accept }) => accept(),
|
|
248
|
+
});
|
|
249
|
+
server.init();
|
|
250
|
+
const agent = connectAgent(hub, 1);
|
|
251
|
+
await sendAndAwait(agent, registerPayload('my-agent'), 'register-rs');
|
|
252
|
+
(0, vitest_1.expect)(server.getAgentSender('my-agent')).not.toBeNull();
|
|
253
|
+
server.finalize();
|
|
254
|
+
(0, vitest_1.expect)(server.getAgentSender('my-agent')).toBeNull();
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DuplexTransport } from '../transport_types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a linked pair of in-process DuplexTransports.
|
|
4
|
+
* Writing to one side is immediately readable on the other.
|
|
5
|
+
* No serialization, no network — pure in-memory for testing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createTransportPair<TA, TB>(): [
|
|
8
|
+
DuplexTransport<TA, TB>,
|
|
9
|
+
DuplexTransport<TB, TA>
|
|
10
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTransportPair = createTransportPair;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a linked pair of in-process DuplexTransports.
|
|
7
|
+
* Writing to one side is immediately readable on the other.
|
|
8
|
+
* No serialization, no network — pure in-memory for testing.
|
|
9
|
+
*/
|
|
10
|
+
function createTransportPair() {
|
|
11
|
+
const aToB = new rxjs_1.Subject();
|
|
12
|
+
const bToA = new rxjs_1.Subject();
|
|
13
|
+
const aConnected = new rxjs_1.BehaviorSubject(true);
|
|
14
|
+
const bConnected = new rxjs_1.BehaviorSubject(true);
|
|
15
|
+
const close = () => {
|
|
16
|
+
aConnected.next(false);
|
|
17
|
+
bConnected.next(false);
|
|
18
|
+
aConnected.complete();
|
|
19
|
+
bConnected.complete();
|
|
20
|
+
aToB.complete();
|
|
21
|
+
bToA.complete();
|
|
22
|
+
};
|
|
23
|
+
const sideA = {
|
|
24
|
+
connected$: aConnected.asObservable(),
|
|
25
|
+
messages$: bToA.asObservable(),
|
|
26
|
+
send: (msg) => {
|
|
27
|
+
if (aConnected.value)
|
|
28
|
+
aToB.next(msg);
|
|
29
|
+
},
|
|
30
|
+
close,
|
|
31
|
+
};
|
|
32
|
+
const sideB = {
|
|
33
|
+
connected$: bConnected.asObservable(),
|
|
34
|
+
messages$: aToB.asObservable(),
|
|
35
|
+
send: (msg) => {
|
|
36
|
+
if (bConnected.value)
|
|
37
|
+
bToA.next(msg);
|
|
38
|
+
},
|
|
39
|
+
close,
|
|
40
|
+
};
|
|
41
|
+
return [sideA, sideB];
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FromAgent, FromServer, Message } from '@awarevue/api-types';
|
|
2
|
+
import { Agent } from './agent';
|
|
3
|
+
import { AgentApp, AgentOptions } from './agent-app';
|
|
4
|
+
import { DuplexTransport } from './transport_types';
|
|
5
|
+
export type AgentAppWithDefaultsOptions = Omit<AgentOptions, 'transport'> & {
|
|
6
|
+
url: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
transport?: DuplexTransport<Message<FromServer>, Message<FromAgent>>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Creates an AgentApp instance with default transport settings (WS transport wrapped in a logging decorator) if no custom transport is provided.
|
|
12
|
+
* @param agent The agent instance to use.
|
|
13
|
+
* @param options Configuration options for the AgentApp.
|
|
14
|
+
* @returns A configured AgentApp instance.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createAgentApp(agent: Agent, options: AgentAppWithDefaultsOptions): AgentApp;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAgentApp = createAgentApp;
|
|
4
|
+
const agent_app_1 = require("./agent-app");
|
|
5
|
+
const logging_1 = require("./transports/logging");
|
|
6
|
+
const ws_1 = require("./transports/ws");
|
|
7
|
+
const ws_json_encoder_1 = require("./transports/ws-json-encoder");
|
|
8
|
+
/**
|
|
9
|
+
* Creates an AgentApp instance with default transport settings (WS transport wrapped in a logging decorator) if no custom transport is provided.
|
|
10
|
+
* @param agent The agent instance to use.
|
|
11
|
+
* @param options Configuration options for the AgentApp.
|
|
12
|
+
* @returns A configured AgentApp instance.
|
|
13
|
+
*/
|
|
14
|
+
function createAgentApp(agent, options) {
|
|
15
|
+
const { url, apiKey, transport, ...rest } = options;
|
|
16
|
+
const finalTransport = transport !== null && transport !== void 0 ? transport : new logging_1.LoggingDuplexTransport(new ws_json_encoder_1.WsJsonEncoder(new ws_1.WsDuplexTransport({
|
|
17
|
+
url,
|
|
18
|
+
headers: {
|
|
19
|
+
Authorization: `APIKey ${apiKey}`,
|
|
20
|
+
},
|
|
21
|
+
})));
|
|
22
|
+
return new agent_app_1.AgentApp(agent, { ...rest, transport: finalTransport });
|
|
23
|
+
}
|
package/dist/agent-protocol.d.ts
CHANGED
|
@@ -27,6 +27,6 @@ export declare class AgentProtocol<D extends Direction> {
|
|
|
27
27
|
constructor(transport: DuplexTransport<Message<Inbound<D>>, Message<Outbound<D>>>, options: AgentProtocolOptions);
|
|
28
28
|
getReply$: <K extends RequestKind>(payload: Extract<Outbound<D>, {
|
|
29
29
|
kind: K;
|
|
30
|
-
}
|
|
30
|
+
}>, timeoutMs?: number) => Observable<Message<PayloadByKind[ResponseKind<K>]>>;
|
|
31
31
|
send: (payload: Outbound<D>) => void;
|
|
32
32
|
}
|
package/dist/agent-protocol.js
CHANGED
|
@@ -15,17 +15,24 @@ class AgentProtocol {
|
|
|
15
15
|
version: constants_1.AGENT_PROTOCOL_VERSION,
|
|
16
16
|
on: Date.now(),
|
|
17
17
|
});
|
|
18
|
-
this.getReply$ = (payload) => {
|
|
18
|
+
this.getReply$ = (payload, timeoutMs) => {
|
|
19
19
|
const responseKind = api_types_1.ReplyKindByRequestKind[payload.kind];
|
|
20
|
-
const reply$ = (id) => this.transport.messages$.pipe(
|
|
21
|
-
|
|
20
|
+
const reply$ = (id) => this.transport.messages$.pipe(
|
|
21
|
+
// pass through only messages with matching requestId (it could be response, error or progress update)
|
|
22
|
+
(0, rxjs_1.filter)((message) => 'requestId' in message && message.requestId === id), (0, rxjs_1.mergeMap)((message) => {
|
|
23
|
+
// if the agent responded with an error message related to our request, throw an error to fail the stream
|
|
24
|
+
if (message.kind === 'error-rs') {
|
|
22
25
|
const error = message.error;
|
|
23
26
|
return (0, rxjs_1.throwError)(() => new Error(`Server failed to process message ${payload.kind}: ${error}`));
|
|
24
27
|
}
|
|
25
28
|
return (0, rxjs_1.of)(message);
|
|
26
|
-
}),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
}),
|
|
30
|
+
// enforce timeout (progress updates will reset the timer, but if no messages arrive for `timeoutMs` duration, the request is considered failed)
|
|
31
|
+
(0, rxjs_1.timeout)(timeoutMs || this.options.replyTimeout || 10000),
|
|
32
|
+
// pass through only the final response message (filter out progress updates)
|
|
33
|
+
(0, rxjs_1.filter)((message) => message.kind === responseKind),
|
|
34
|
+
// we only expect one response message, so complete the stream after the response arrives
|
|
35
|
+
(0, rxjs_1.take)(1));
|
|
29
36
|
return (0, rxjs_1.of)(this.addEnvelope({ ...payload, id: AgentProtocol.nextId() })).pipe(
|
|
30
37
|
// send the message to the agent
|
|
31
38
|
(0, rxjs_1.tap)((p) => this.transport.send(p)),
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FromAgent, FromServer, Message, RegisterRq, StartServiceRq } from '@awarevue/api-types';
|
|
2
|
+
import { HubTransport, PeerId } from './transport_types';
|
|
3
|
+
import { AgentProtocol } from './agent-protocol';
|
|
4
|
+
import { Subject } from 'rxjs';
|
|
5
|
+
export interface OnUnregisteredEventArgs {
|
|
6
|
+
agentId: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
export type OnRegisterEventArgs = {
|
|
10
|
+
agentId: string;
|
|
11
|
+
accept: () => void;
|
|
12
|
+
reject: (reason: string) => void;
|
|
13
|
+
} & Omit<RegisterRq, 'kind'>;
|
|
14
|
+
export type OnStoppedEventArgs = {
|
|
15
|
+
agentId: string;
|
|
16
|
+
};
|
|
17
|
+
export type OnStartedEventArgs = {
|
|
18
|
+
agentId: string;
|
|
19
|
+
};
|
|
20
|
+
export type StartAgentArgs = {
|
|
21
|
+
agentId: string;
|
|
22
|
+
} & Omit<StartServiceRq, 'kind'>;
|
|
23
|
+
export type AgentServerOptions = {
|
|
24
|
+
/** Timeout for awaiting replies to sent messages (default 10s) */
|
|
25
|
+
replyTimeout?: number;
|
|
26
|
+
onRegister?: (args: OnRegisterEventArgs) => void;
|
|
27
|
+
onUnregistered?: (args: OnUnregisteredEventArgs) => void;
|
|
28
|
+
onStarted?: (args: OnStartedEventArgs) => void;
|
|
29
|
+
onStopped?: (args: OnStoppedEventArgs) => void;
|
|
30
|
+
};
|
|
31
|
+
export declare class AgentServer<TPeer extends PeerId = string> {
|
|
32
|
+
private readonly hub;
|
|
33
|
+
private readonly options;
|
|
34
|
+
readonly notifications$: Subject<string>;
|
|
35
|
+
readonly messages$: Subject<{
|
|
36
|
+
msg: Message<FromAgent>;
|
|
37
|
+
peer: TPeer;
|
|
38
|
+
agentId: string;
|
|
39
|
+
}>;
|
|
40
|
+
private sub;
|
|
41
|
+
private readonly _agentToPeer;
|
|
42
|
+
private readonly _peerToAgent;
|
|
43
|
+
constructor(hub: HubTransport<Message<FromAgent>, Message<FromServer>, TPeer>, options?: AgentServerOptions);
|
|
44
|
+
init(): void;
|
|
45
|
+
startAgent({ agentId, ...rest }: StartAgentArgs): void;
|
|
46
|
+
stopAgent(agentId: string, provider: string): void;
|
|
47
|
+
finalize(): void;
|
|
48
|
+
getAgentSender(agentId: string): AgentProtocol<'server'> | null;
|
|
49
|
+
private getPeerSender;
|
|
50
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentServer = void 0;
|
|
4
|
+
const api_types_1 = require("@awarevue/api-types");
|
|
5
|
+
const agent_protocol_1 = require("./agent-protocol");
|
|
6
|
+
const rxjs_1 = require("rxjs");
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
class AgentServer {
|
|
9
|
+
constructor(hub, options = {}) {
|
|
10
|
+
this.hub = hub;
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.notifications$ = new rxjs_1.Subject();
|
|
13
|
+
this.messages$ = new rxjs_1.Subject();
|
|
14
|
+
this.sub = null;
|
|
15
|
+
this._agentToPeer = new Map();
|
|
16
|
+
this._peerToAgent = new Map();
|
|
17
|
+
}
|
|
18
|
+
init() {
|
|
19
|
+
if (this.sub) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const processMessages$ = this.hub.messages$.pipe(
|
|
23
|
+
// validate
|
|
24
|
+
(0, rxjs_1.map)(({ msg, peer }) => ({ msg, peer, isValid: (0, api_types_1.isMessageFromAgent)(msg) })), (0, rxjs_1.tap)(({ msg, peer, isValid }) => {
|
|
25
|
+
var _a;
|
|
26
|
+
const id = (_a = msg.id) !== null && _a !== void 0 ? _a : 'unknown';
|
|
27
|
+
if (msg.version !== constants_1.AGENT_PROTOCOL_VERSION) {
|
|
28
|
+
this.notifications$.next(`Agent ${msg.from} has incompatible protocol version: ${msg.version}`);
|
|
29
|
+
const protocol = this.getPeerSender(peer);
|
|
30
|
+
protocol === null || protocol === void 0 ? void 0 : protocol.send({
|
|
31
|
+
kind: 'error-rs',
|
|
32
|
+
requestId: id,
|
|
33
|
+
error: `Incompatible protocol version. Expected ${constants_1.AGENT_PROTOCOL_VERSION} but got ${msg.version}`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (!isValid) {
|
|
37
|
+
// log
|
|
38
|
+
const issues = (0, api_types_1.getAgentMessageIssues)(msg).join(', ');
|
|
39
|
+
this.notifications$.next(`Received invalid message from agent ${peer}: ${issues}`);
|
|
40
|
+
const sender = this.getPeerSender(peer);
|
|
41
|
+
sender === null || sender === void 0 ? void 0 : sender.send({
|
|
42
|
+
kind: 'error-rs',
|
|
43
|
+
requestId: id,
|
|
44
|
+
error: issues,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}),
|
|
49
|
+
// dismiss invalid protocol messages
|
|
50
|
+
(0, rxjs_1.filter)(({ msg, isValid }) => msg.version === constants_1.AGENT_PROTOCOL_VERSION && isValid),
|
|
51
|
+
// forward valid messages to the messages$ stream
|
|
52
|
+
(0, rxjs_1.tap)((m) => {
|
|
53
|
+
var _a, _b, _c, _d, _e, _f;
|
|
54
|
+
const agentId = m.msg.from;
|
|
55
|
+
// if this is a registration message, establish the agentId ↔ peer mapping
|
|
56
|
+
if (m.msg.kind === 'register') {
|
|
57
|
+
this._agentToPeer.set(agentId, m.peer);
|
|
58
|
+
this._peerToAgent.set(m.peer, agentId);
|
|
59
|
+
this.messages$.next({ ...m, agentId });
|
|
60
|
+
(_b = (_a = this.options).onRegister) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
61
|
+
...m.msg,
|
|
62
|
+
agentId,
|
|
63
|
+
reject: (reason) => {
|
|
64
|
+
const protocol = this.getPeerSender(m.peer);
|
|
65
|
+
protocol === null || protocol === void 0 ? void 0 : protocol.send({
|
|
66
|
+
kind: 'error-rs',
|
|
67
|
+
requestId: m.msg.id,
|
|
68
|
+
error: reason,
|
|
69
|
+
});
|
|
70
|
+
this._agentToPeer.delete(agentId);
|
|
71
|
+
this._peerToAgent.delete(m.peer);
|
|
72
|
+
},
|
|
73
|
+
accept: () => {
|
|
74
|
+
const protocol = this.getPeerSender(m.peer);
|
|
75
|
+
protocol === null || protocol === void 0 ? void 0 : protocol.send({
|
|
76
|
+
kind: 'register-rs',
|
|
77
|
+
requestId: m.msg.id,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
this.messages$.next({ ...m, agentId });
|
|
84
|
+
if (m.msg.kind === 'start-rs') {
|
|
85
|
+
(_d = (_c = this.options).onStarted) === null || _d === void 0 ? void 0 : _d.call(_c, { agentId });
|
|
86
|
+
}
|
|
87
|
+
else if (m.msg.kind === 'stop-rs') {
|
|
88
|
+
(_f = (_e = this.options).onStopped) === null || _f === void 0 ? void 0 : _f.call(_e, { agentId });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
const processConnections$ = this.hub.peerEvents$.pipe((0, rxjs_1.filter)((event) => event.type === 'leave'), (0, rxjs_1.tap)(({ peer, reason }) => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
const agentId = this._peerToAgent.get(peer);
|
|
95
|
+
this.notifications$.next(`Peer ${peer}${agentId ? ` (agent: ${agentId})` : ''} left: ${reason !== null && reason !== void 0 ? reason : 'unknown reason'}`);
|
|
96
|
+
// clean up maps
|
|
97
|
+
if (agentId) {
|
|
98
|
+
this._peerToAgent.delete(peer);
|
|
99
|
+
this._agentToPeer.delete(agentId);
|
|
100
|
+
(_b = (_a = this.options).onUnregistered) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
101
|
+
agentId,
|
|
102
|
+
reason: reason,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
this.sub = (0, rxjs_1.merge)(processMessages$, processConnections$).subscribe();
|
|
107
|
+
}
|
|
108
|
+
startAgent({ agentId, ...rest }) {
|
|
109
|
+
const protocol = this.getAgentSender(agentId);
|
|
110
|
+
protocol === null || protocol === void 0 ? void 0 : protocol.send({ kind: 'start', ...rest });
|
|
111
|
+
}
|
|
112
|
+
stopAgent(agentId, provider) {
|
|
113
|
+
const protocol = this.getAgentSender(agentId);
|
|
114
|
+
protocol === null || protocol === void 0 ? void 0 : protocol.send({ kind: 'stop', provider });
|
|
115
|
+
}
|
|
116
|
+
finalize() {
|
|
117
|
+
var _a;
|
|
118
|
+
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
119
|
+
this.sub = null;
|
|
120
|
+
this._agentToPeer.clear();
|
|
121
|
+
this._peerToAgent.clear();
|
|
122
|
+
}
|
|
123
|
+
getAgentSender(agentId) {
|
|
124
|
+
const peer = this._agentToPeer.get(agentId);
|
|
125
|
+
if (peer === undefined)
|
|
126
|
+
return null;
|
|
127
|
+
return this.getPeerSender(peer);
|
|
128
|
+
}
|
|
129
|
+
getPeerSender(peer) {
|
|
130
|
+
var _a;
|
|
131
|
+
const connection = this.hub.connection(peer);
|
|
132
|
+
if (!connection) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const agentId = (_a = this._peerToAgent.get(peer)) !== null && _a !== void 0 ? _a : `${peer}`;
|
|
136
|
+
return new agent_protocol_1.AgentProtocol(connection, {
|
|
137
|
+
replyTimeout: this.options.replyTimeout,
|
|
138
|
+
id: agentId,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.AgentServer = AgentServer;
|
|
@@ -7,4 +7,4 @@ export declare const createValidator: <T extends Agent>(agent: T) => (context: C
|
|
|
7
7
|
objectKind?: "person" | "zone" | "schedule" | "device" | "accessRule" | undefined;
|
|
8
8
|
objectId?: string | undefined;
|
|
9
9
|
index?: number | undefined;
|
|
10
|
-
}[], Record<"accessRule" | "schedule" | "person" | "
|
|
10
|
+
}[], Record<"accessRule" | "schedule" | "person" | "zone" | "device", Record<string, Record<string, unknown>>>]>;
|
package/dist/hubs/in-memory.js
CHANGED
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InMemoryHub = void 0;
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
|
-
const operators_1 = require("rxjs/operators");
|
|
6
5
|
class InMemoryHub {
|
|
7
6
|
constructor() {
|
|
8
7
|
this.peerEventsSubject = new rxjs_1.Subject();
|
|
9
8
|
this.messagesSubject = new rxjs_1.Subject();
|
|
10
9
|
this.peers = new Map();
|
|
11
10
|
this.peerSubs = new Map();
|
|
12
|
-
this.peerEvents$ = this.peerEventsSubject.asObservable()
|
|
13
|
-
this.messages$ = this.messagesSubject.asObservable()
|
|
11
|
+
this.peerEvents$ = this.peerEventsSubject.asObservable();
|
|
12
|
+
this.messages$ = this.messagesSubject.asObservable();
|
|
14
13
|
}
|
|
15
14
|
addPeer(peerId, conn) {
|
|
16
15
|
// defensive: if peer already exists, clean it first
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./agent-app"), exports);
|
|
18
|
+
__exportStar(require("./agent-app-with-defaults"), exports);
|
|
19
|
+
__exportStar(require("./agent-server"), exports);
|
|
18
20
|
__exportStar(require("./agent"), exports);
|
|
19
21
|
__exportStar(require("./constants"), exports);
|
|
20
22
|
__exportStar(require("./agent-protocol"), exports);
|
package/dist/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "git+https://github.com/Linc-Security-Systems/aware-essentials.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "2.0.
|
|
7
|
+
"version": "2.0.76",
|
|
8
8
|
"description": "SDK for building Agent implementations that speak the agent protocol.",
|
|
9
9
|
"author": "Yaser Awajan",
|
|
10
10
|
"license": "MIT",
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc -p tsconfig.json && cp package.json dist/",
|
|
28
28
|
"prepublishOnly": "yarn build",
|
|
29
|
-
"test": "
|
|
29
|
+
"test": "vitest run",
|
|
30
30
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
31
31
|
"lint:fix": "yarn lint --fix"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@awarevue/api-types": "2.0.
|
|
34
|
+
"@awarevue/api-types": "2.0.76",
|
|
35
35
|
"rxjs": "^7.8.2",
|
|
36
36
|
"ws": "^8"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@awarevue/api-types": "2.0.
|
|
39
|
+
"@awarevue/api-types": "2.0.76",
|
|
40
40
|
"@types/node": "^20.12.7",
|
|
41
41
|
"@types/ws": "^8.18.1",
|
|
42
42
|
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"eslint-plugin-import": "^2.31.0",
|
|
47
47
|
"rxjs": "7.8.2",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
|
+
"vitest": "^4.1.4",
|
|
49
50
|
"ws": "^8",
|
|
50
51
|
"zod": "3.25.76"
|
|
51
52
|
},
|
package/dist/transports/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./ws"), exports);
|
|
18
|
+
__exportStar(require("./ws-json-encoder"), exports);
|
|
18
19
|
__exportStar(require("./logging"), exports);
|
|
@@ -16,7 +16,7 @@ class LoggingDuplexTransport {
|
|
|
16
16
|
this.inner = inner;
|
|
17
17
|
this.label = (_a = opts.label) !== null && _a !== void 0 ? _a : 'transport';
|
|
18
18
|
this.sink =
|
|
19
|
-
(_b = opts.logger) !== null && _b !== void 0 ? _b : ((direction, label, msg) => console.log(`[${label}] ${direction}`, JSON.stringify(msg
|
|
19
|
+
(_b = opts.logger) !== null && _b !== void 0 ? _b : ((direction, label, msg) => console.log(`[${label}] ${direction}`, JSON.stringify(msg)));
|
|
20
20
|
this.connected$ = inner.connected$;
|
|
21
21
|
// Tap into the incoming stream before exposing it to consumers.
|
|
22
22
|
this.messages$ = inner.messages$.pipe((0, operators_1.tap)((msg) => this.sink('IN', this.label, msg)));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { DuplexTransport } from '../transport_types';
|
|
3
|
+
import { FromAgent, FromServer, Message } from '@awarevue/api-types';
|
|
4
|
+
export declare class WsJsonEncoder implements DuplexTransport<Message<FromServer | FromAgent>, Message<FromAgent | FromServer>> {
|
|
5
|
+
private defaultDeserializer;
|
|
6
|
+
private defaultSerializer;
|
|
7
|
+
readonly connected$: Observable<boolean>;
|
|
8
|
+
readonly messages$: Observable<Message<FromServer | FromAgent>>;
|
|
9
|
+
readonly send: (msg: Message<FromAgent | FromServer>) => void;
|
|
10
|
+
readonly close: () => void;
|
|
11
|
+
constructor(decoratee: DuplexTransport<string, string>);
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WsJsonEncoder = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
class WsJsonEncoder {
|
|
6
|
+
constructor(decoratee) {
|
|
7
|
+
this.defaultDeserializer = (raw) => {
|
|
8
|
+
const wsShape = JSON.parse(raw);
|
|
9
|
+
return {
|
|
10
|
+
kind: wsShape.event,
|
|
11
|
+
...wsShape.data,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
this.defaultSerializer = (msg) => {
|
|
15
|
+
const { kind, ...data } = msg;
|
|
16
|
+
const wsShape = {
|
|
17
|
+
event: kind,
|
|
18
|
+
data,
|
|
19
|
+
};
|
|
20
|
+
return JSON.stringify(wsShape);
|
|
21
|
+
};
|
|
22
|
+
this.connected$ = decoratee.connected$;
|
|
23
|
+
this.messages$ = decoratee.messages$.pipe((0, rxjs_1.map)((raw) => this.defaultDeserializer(raw)), (0, rxjs_1.catchError)(() => rxjs_1.EMPTY));
|
|
24
|
+
this.send = (msg) => decoratee.send(this.defaultSerializer(msg));
|
|
25
|
+
this.close = () => decoratee.close();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.WsJsonEncoder = WsJsonEncoder;
|
package/dist/transports/ws.d.ts
CHANGED
|
@@ -8,16 +8,6 @@ export interface WsDuplexTransportOptions {
|
|
|
8
8
|
* (e.g. Authorization, User-Agent).
|
|
9
9
|
*/
|
|
10
10
|
headers?: Record<string, string>;
|
|
11
|
-
/**
|
|
12
|
-
* Serialise an outbound message to a string or Buffer.
|
|
13
|
-
* Defaults to `JSON.stringify`.
|
|
14
|
-
*/
|
|
15
|
-
serialise?: (msg: unknown) => string;
|
|
16
|
-
/**
|
|
17
|
-
* Deserialise an inbound raw payload to a message object.
|
|
18
|
-
* Defaults to `JSON.parse`.
|
|
19
|
-
*/
|
|
20
|
-
deserialise?: (raw: string) => unknown;
|
|
21
11
|
/** Initial reconnect delay in ms (default 1 000). */
|
|
22
12
|
reconnectDelay?: number;
|
|
23
13
|
/** Maximum reconnect delay in ms (default 30 000). */
|
|
@@ -25,10 +15,10 @@ export interface WsDuplexTransportOptions {
|
|
|
25
15
|
/** When false, no automatic reconnect is attempted (default true). */
|
|
26
16
|
autoReconnect?: boolean;
|
|
27
17
|
}
|
|
28
|
-
export declare class WsDuplexTransport
|
|
18
|
+
export declare class WsDuplexTransport implements DuplexTransport<string, string> {
|
|
29
19
|
private readonly opts;
|
|
30
20
|
readonly connected$: Observable<boolean>;
|
|
31
|
-
readonly messages$: Observable<
|
|
21
|
+
readonly messages$: Observable<string>;
|
|
32
22
|
readonly errors$: Observable<Error>;
|
|
33
23
|
private readonly _connected$;
|
|
34
24
|
private readonly _messages$;
|
|
@@ -40,12 +30,8 @@ export declare class WsDuplexTransport<TIn, TOut> implements DuplexTransport<TIn
|
|
|
40
30
|
private readonly maxDelay;
|
|
41
31
|
private readonly autoReconnect;
|
|
42
32
|
private destroyed;
|
|
43
|
-
private readonly serialise;
|
|
44
|
-
private readonly deserialise;
|
|
45
|
-
private defaultDeserializer;
|
|
46
|
-
private defaultSerializer;
|
|
47
33
|
constructor(opts: WsDuplexTransportOptions);
|
|
48
|
-
send(msg:
|
|
34
|
+
send(msg: string): void;
|
|
49
35
|
close(): void;
|
|
50
36
|
/** Single subscription that drains the outbound queue when connected. */
|
|
51
37
|
private setupSender;
|
package/dist/transports/ws.js
CHANGED
|
@@ -14,7 +14,7 @@ const rxjs_1 = require("rxjs");
|
|
|
14
14
|
/* ---------------------------------------------------------------- */
|
|
15
15
|
class WsDuplexTransport {
|
|
16
16
|
constructor(opts) {
|
|
17
|
-
var _a, _b, _c
|
|
17
|
+
var _a, _b, _c;
|
|
18
18
|
this.opts = opts;
|
|
19
19
|
// ---- internal subjects ----
|
|
20
20
|
this._connected$ = new rxjs_1.BehaviorSubject(false);
|
|
@@ -23,28 +23,12 @@ class WsDuplexTransport {
|
|
|
23
23
|
this.outbound$ = new rxjs_1.Subject();
|
|
24
24
|
this.destroy$ = new rxjs_1.Subject();
|
|
25
25
|
this.destroyed = false;
|
|
26
|
-
this.defaultDeserializer = (raw) => {
|
|
27
|
-
const env = JSON.parse(raw);
|
|
28
|
-
return {
|
|
29
|
-
kind: env.event,
|
|
30
|
-
...env.data,
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
this.defaultSerializer = (msg) => {
|
|
34
|
-
if (typeof msg === 'object' && 'kind' in (msg || {})) {
|
|
35
|
-
const { kind, ...data } = msg;
|
|
36
|
-
return JSON.stringify({ event: kind, data });
|
|
37
|
-
}
|
|
38
|
-
return JSON.stringify(msg);
|
|
39
|
-
};
|
|
40
26
|
this.connected$ = this._connected$.asObservable();
|
|
41
27
|
this.messages$ = this._messages$.asObservable();
|
|
42
28
|
this.errors$ = this._errors$.asObservable();
|
|
43
29
|
this.reconnectDelay = (_a = opts.reconnectDelay) !== null && _a !== void 0 ? _a : 1000;
|
|
44
30
|
this.maxDelay = (_b = opts.maxReconnectDelay) !== null && _b !== void 0 ? _b : 30000;
|
|
45
31
|
this.autoReconnect = (_c = opts.autoReconnect) !== null && _c !== void 0 ? _c : true;
|
|
46
|
-
this.serialise = ((_d = opts.serialise) !== null && _d !== void 0 ? _d : this.defaultSerializer);
|
|
47
|
-
this.deserialise = ((_e = opts.deserialise) !== null && _e !== void 0 ? _e : this.defaultDeserializer);
|
|
48
32
|
this.connect();
|
|
49
33
|
this.setupSender();
|
|
50
34
|
}
|
|
@@ -52,7 +36,7 @@ class WsDuplexTransport {
|
|
|
52
36
|
/* Public API */
|
|
53
37
|
/* -------------------------------------------------------------- */
|
|
54
38
|
send(msg) {
|
|
55
|
-
this.outbound$.next(
|
|
39
|
+
this.outbound$.next(msg);
|
|
56
40
|
}
|
|
57
41
|
close() {
|
|
58
42
|
var _a;
|
|
@@ -89,7 +73,7 @@ class WsDuplexTransport {
|
|
|
89
73
|
});
|
|
90
74
|
this.ws.on('message', (data) => {
|
|
91
75
|
try {
|
|
92
|
-
const msg =
|
|
76
|
+
const msg = data.toString();
|
|
93
77
|
this._messages$.next(msg);
|
|
94
78
|
}
|
|
95
79
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "git+https://github.com/Linc-Security-Systems/aware-essentials.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "2.0.
|
|
7
|
+
"version": "2.0.76",
|
|
8
8
|
"description": "SDK for building Agent implementations that speak the agent protocol.",
|
|
9
9
|
"author": "Yaser Awajan",
|
|
10
10
|
"license": "MIT",
|
|
@@ -26,17 +26,17 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc -p tsconfig.json && cp package.json dist/",
|
|
28
28
|
"prepublishOnly": "yarn build",
|
|
29
|
-
"test": "
|
|
29
|
+
"test": "vitest run",
|
|
30
30
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
31
31
|
"lint:fix": "yarn lint --fix"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@awarevue/api-types": "2.0.
|
|
34
|
+
"@awarevue/api-types": "2.0.76",
|
|
35
35
|
"rxjs": "^7.8.2",
|
|
36
36
|
"ws": "^8"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@awarevue/api-types": "2.0.
|
|
39
|
+
"@awarevue/api-types": "2.0.76",
|
|
40
40
|
"@types/node": "^20.12.7",
|
|
41
41
|
"@types/ws": "^8.18.1",
|
|
42
42
|
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"eslint-plugin-import": "^2.31.0",
|
|
47
47
|
"rxjs": "7.8.2",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
|
+
"vitest": "^4.1.4",
|
|
49
50
|
"ws": "^8",
|
|
50
51
|
"zod": "3.25.76"
|
|
51
52
|
},
|