@awarevue/agent-sdk 1.0.1
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/agent-app.d.ts +25 -0
- package/dist/agent-app.js +143 -0
- package/dist/agent.d.ts +17 -0
- package/dist/agent.js +2 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/package.json +48 -0
- package/dist/transport-logger-decorator.d.ts +11 -0
- package/dist/transport-logger-decorator.js +35 -0
- package/dist/transport.d.ts +10 -0
- package/dist/transport.js +2 -0
- package/dist/ws-agent-transport.d.ts +28 -0
- package/dist/ws-agent-transport.js +100 -0
- package/package.json +48 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Transport } from './transport';
|
|
2
|
+
import { ProviderSpecs, PushEventRq, PushStateUpdateRq } from '@awarevue/api-types';
|
|
3
|
+
import { Agent } from './agent';
|
|
4
|
+
export type DeviceActivity = Omit<PushStateUpdateRq, 'provider'> | Omit<PushEventRq, 'provider'>;
|
|
5
|
+
export type AgentOptions = {
|
|
6
|
+
version: number;
|
|
7
|
+
providers: Record<string, ProviderSpecs>;
|
|
8
|
+
agentId: string;
|
|
9
|
+
replyTimeout?: number;
|
|
10
|
+
transport: Transport;
|
|
11
|
+
};
|
|
12
|
+
export declare class AgentApp {
|
|
13
|
+
private readonly agent;
|
|
14
|
+
private readonly options;
|
|
15
|
+
private static id;
|
|
16
|
+
private static nextId;
|
|
17
|
+
private sub;
|
|
18
|
+
private getReply$;
|
|
19
|
+
private addEnvelope;
|
|
20
|
+
private runProvider$;
|
|
21
|
+
private process$;
|
|
22
|
+
constructor(agent: Agent, options: AgentOptions);
|
|
23
|
+
start(): void;
|
|
24
|
+
stop(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentApp = void 0;
|
|
4
|
+
const rxjs_1 = require("rxjs");
|
|
5
|
+
class AgentApp {
|
|
6
|
+
constructor(agent, options) {
|
|
7
|
+
this.agent = agent;
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.sub = null;
|
|
10
|
+
// getReply$ is a generic function that sends a message to server and waits for a reply.
|
|
11
|
+
this.getReply$ = (responseKind, payload) => {
|
|
12
|
+
const reply$ = (id) => this.options.transport.messages$.pipe((0, rxjs_1.mergeMap)((message) => {
|
|
13
|
+
if (message.kind === 'error-rs' && message.requestId === id) {
|
|
14
|
+
const error = message.error;
|
|
15
|
+
return (0, rxjs_1.throwError)(() => new Error(`Server failed to process message ${message.kind}: ${error}`));
|
|
16
|
+
}
|
|
17
|
+
return (0, rxjs_1.of)(message);
|
|
18
|
+
}), (0, rxjs_1.filter)((message) => message.kind === responseKind &&
|
|
19
|
+
'requestId' in message &&
|
|
20
|
+
message.requestId === id), (0, rxjs_1.take)(1), (0, rxjs_1.timeout)(this.options.replyTimeout || 10000));
|
|
21
|
+
return (0, rxjs_1.of)(this.addEnvelope({ ...payload, id: AgentApp.nextId() })).pipe(
|
|
22
|
+
// send the message to the agent
|
|
23
|
+
(0, rxjs_1.tap)((p) => this.options.transport.send(p)),
|
|
24
|
+
// wait for the agent to reply
|
|
25
|
+
(0, rxjs_1.mergeMap)(({ id }) => reply$(id)));
|
|
26
|
+
};
|
|
27
|
+
this.addEnvelope = (payload) => ({
|
|
28
|
+
...payload,
|
|
29
|
+
id: AgentApp.nextId(),
|
|
30
|
+
from: this.options.agentId,
|
|
31
|
+
version: this.options.version,
|
|
32
|
+
on: Date.now(),
|
|
33
|
+
});
|
|
34
|
+
this.runProvider$ = (context) => {
|
|
35
|
+
return (0, rxjs_1.merge)(
|
|
36
|
+
// run the agent
|
|
37
|
+
this.agent
|
|
38
|
+
.run$(context)
|
|
39
|
+
.pipe((0, rxjs_1.tap)((message) => this.options.transport.send(this.addEnvelope({ ...message, provider: context.provider })))), this.options.transport.messages$.pipe((0, rxjs_1.mergeMap)((message) => {
|
|
40
|
+
switch (message.kind) {
|
|
41
|
+
// handle commands
|
|
42
|
+
case 'command':
|
|
43
|
+
return this.agent.runCommand$(context, message).pipe(
|
|
44
|
+
// success
|
|
45
|
+
(0, rxjs_1.map)(() => ({
|
|
46
|
+
kind: 'command-rs',
|
|
47
|
+
requestId: message.id,
|
|
48
|
+
})),
|
|
49
|
+
// error
|
|
50
|
+
(0, rxjs_1.catchError)((error) => {
|
|
51
|
+
var _a;
|
|
52
|
+
return (0, rxjs_1.of)({
|
|
53
|
+
kind: 'error-rs',
|
|
54
|
+
requestId: message.id,
|
|
55
|
+
error: (_a = error.message) !== null && _a !== void 0 ? _a : 'Unknown error',
|
|
56
|
+
});
|
|
57
|
+
}),
|
|
58
|
+
// send the response
|
|
59
|
+
(0, rxjs_1.tap)((rs) => this.options.transport.send(this.addEnvelope(rs))));
|
|
60
|
+
case 'get-available-devices':
|
|
61
|
+
// get available devices
|
|
62
|
+
return this.agent.getDevicesAndRelations$(context).pipe(
|
|
63
|
+
// success
|
|
64
|
+
(0, rxjs_1.map)((rs) => ({
|
|
65
|
+
kind: 'get-available-devices-rs',
|
|
66
|
+
...rs,
|
|
67
|
+
requestId: message.id,
|
|
68
|
+
})),
|
|
69
|
+
// error
|
|
70
|
+
(0, rxjs_1.catchError)((error) => {
|
|
71
|
+
var _a;
|
|
72
|
+
return (0, rxjs_1.of)({
|
|
73
|
+
kind: 'error-rs',
|
|
74
|
+
requestId: message.id,
|
|
75
|
+
error: (_a = error.message) !== null && _a !== void 0 ? _a : 'Unknown error',
|
|
76
|
+
});
|
|
77
|
+
}), (0, rxjs_1.tap)((rs) => this.options.transport.send(this.addEnvelope(rs))));
|
|
78
|
+
default:
|
|
79
|
+
return rxjs_1.EMPTY;
|
|
80
|
+
}
|
|
81
|
+
})));
|
|
82
|
+
};
|
|
83
|
+
this.process$ = () => {
|
|
84
|
+
const registration$ = this.options.transport.connected$.pipe((0, rxjs_1.switchMap)((connected) => connected
|
|
85
|
+
? this.getReply$('register-rs', {
|
|
86
|
+
kind: 'register',
|
|
87
|
+
providers: this.options.providers,
|
|
88
|
+
}).pipe((0, rxjs_1.retry)({ delay: 3000 }))
|
|
89
|
+
: rxjs_1.EMPTY));
|
|
90
|
+
const startStop$ = this.options.transport.messages$.pipe((0, rxjs_1.filter)((message) => message.kind === 'start' || message.kind === 'stop'), (0, rxjs_1.switchMap)((message) => message.kind === 'start'
|
|
91
|
+
? (0, rxjs_1.merge)((0, rxjs_1.of)({
|
|
92
|
+
provider: message.provider,
|
|
93
|
+
config: message.config,
|
|
94
|
+
lastEventForeignRef: message.lastEventForeignRef,
|
|
95
|
+
lastEventTimestamp: message.lastEventTimestamp,
|
|
96
|
+
}).pipe((0, rxjs_1.mergeMap)((context) => this.runProvider$(context))), (0, rxjs_1.of)({
|
|
97
|
+
kind: 'start-rs',
|
|
98
|
+
requestId: message.id,
|
|
99
|
+
}).pipe((0, rxjs_1.tap)((reply) => this.options.transport.send(this.addEnvelope(reply)))))
|
|
100
|
+
: rxjs_1.EMPTY));
|
|
101
|
+
const validateConfig$ = this.options.transport.messages$.pipe((0, rxjs_1.filter)((message) => message.kind === 'validate-config'), (0, rxjs_1.mergeMap)((message) => {
|
|
102
|
+
const provider = message.provider;
|
|
103
|
+
const config = message.config;
|
|
104
|
+
return this.agent
|
|
105
|
+
.getConfigIssues$({
|
|
106
|
+
provider,
|
|
107
|
+
config,
|
|
108
|
+
})
|
|
109
|
+
.pipe(
|
|
110
|
+
// success
|
|
111
|
+
(0, rxjs_1.map)((issues) => ({
|
|
112
|
+
kind: 'validate-config-rs',
|
|
113
|
+
requestId: message.id,
|
|
114
|
+
issues,
|
|
115
|
+
})),
|
|
116
|
+
// error
|
|
117
|
+
(0, rxjs_1.catchError)((error) => {
|
|
118
|
+
var _a;
|
|
119
|
+
return (0, rxjs_1.of)({
|
|
120
|
+
kind: 'error-rs',
|
|
121
|
+
requestId: message.id,
|
|
122
|
+
error: (_a = error.message) !== null && _a !== void 0 ? _a : 'Unknown error',
|
|
123
|
+
});
|
|
124
|
+
}),
|
|
125
|
+
// send the response
|
|
126
|
+
(0, rxjs_1.tap)((rs) => this.options.transport.send(this.addEnvelope(rs))));
|
|
127
|
+
}));
|
|
128
|
+
return (0, rxjs_1.merge)(registration$, startStop$, validateConfig$);
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
start() {
|
|
132
|
+
this.sub = this.process$().subscribe();
|
|
133
|
+
}
|
|
134
|
+
stop() {
|
|
135
|
+
var _a;
|
|
136
|
+
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
|
|
137
|
+
this.sub = null;
|
|
138
|
+
this.options.transport.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.AgentApp = AgentApp;
|
|
142
|
+
AgentApp.id = 0;
|
|
143
|
+
AgentApp.nextId = () => `${++AgentApp.id}`;
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ValidateProviderConfigRs, DeviceDiscoveryDto, RunCommandRq } from '@awarevue/api-types';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { DeviceActivity } from './agent-app';
|
|
4
|
+
export type Context = {
|
|
5
|
+
provider: string;
|
|
6
|
+
config: Record<string, unknown>;
|
|
7
|
+
};
|
|
8
|
+
export type RunContext = Context & {
|
|
9
|
+
lastEventForeignRef: string | null;
|
|
10
|
+
lastEventTimestamp: number | null;
|
|
11
|
+
};
|
|
12
|
+
export interface Agent {
|
|
13
|
+
getConfigIssues$: (context: Context) => Observable<ValidateProviderConfigRs['issues']>;
|
|
14
|
+
getDevicesAndRelations$: (context: Context) => Observable<DeviceDiscoveryDto>;
|
|
15
|
+
run$: (context: RunContext) => Observable<DeviceActivity>;
|
|
16
|
+
runCommand$: (context: Context, command: RunCommandRq) => Observable<unknown>;
|
|
17
|
+
}
|
package/dist/agent.js
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./agent-app"), exports);
|
|
18
|
+
__exportStar(require("./agent"), exports);
|
|
19
|
+
__exportStar(require("./ws-agent-transport"), exports);
|
|
20
|
+
__exportStar(require("./transport-logger-decorator"), exports);
|
|
21
|
+
__exportStar(require("./transport"), exports);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@awarevue/agent-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "SDK for building Agent implementations that speak the Aware protocol.",
|
|
5
|
+
"author": "Yaser Awajan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json && cp package.json dist/",
|
|
24
|
+
"prepublishOnly": "yarn build",
|
|
25
|
+
"test": "echo \"add unit tests here\"",
|
|
26
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
27
|
+
"lint:fix": "yarn lint --fix"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@awarevue/api-types": "^1.0.0",
|
|
31
|
+
"rxjs": "^7.8.2",
|
|
32
|
+
"ws": "^8",
|
|
33
|
+
"zod": "3.24.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.12.7",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
|
38
|
+
"@typescript-eslint/parser": "^8.31.1",
|
|
39
|
+
"eslint": "^9.25.1",
|
|
40
|
+
"eslint-config-prettier": "^10.1.2",
|
|
41
|
+
"eslint-plugin-import": "^2.31.0",
|
|
42
|
+
"typescript": "^5.8.3"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Message, FromAgent } from '@awarevue/api-types';
|
|
2
|
+
import { Transport } from './transport';
|
|
3
|
+
export declare class TransportWithLogs implements Transport {
|
|
4
|
+
private readonly decoratee;
|
|
5
|
+
constructor(decoratee: Transport);
|
|
6
|
+
get connected$(): import("rxjs").Observable<boolean>;
|
|
7
|
+
get messages$(): import("rxjs").Observable<Message<import("@awarevue/api-types").FromServer>>;
|
|
8
|
+
get errors$(): import("rxjs").Observable<Error>;
|
|
9
|
+
close(): void;
|
|
10
|
+
send(msg: Message<FromAgent>): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransportWithLogs = void 0;
|
|
4
|
+
class TransportWithLogs {
|
|
5
|
+
constructor(decoratee) {
|
|
6
|
+
this.decoratee = decoratee;
|
|
7
|
+
this.decoratee.connected$.subscribe((connected) => {
|
|
8
|
+
console.log(`[${new Date()}] - Transport connected: ${connected}`);
|
|
9
|
+
});
|
|
10
|
+
this.decoratee.messages$.subscribe((msg) => {
|
|
11
|
+
console.log(`[${new Date()}] - Transport message: ${JSON.stringify(msg)}`);
|
|
12
|
+
});
|
|
13
|
+
this.decoratee.errors$.subscribe((err) => {
|
|
14
|
+
console.error(`[${new Date()}] - Transport error: ${err}`);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
get connected$() {
|
|
18
|
+
return this.decoratee.connected$;
|
|
19
|
+
}
|
|
20
|
+
get messages$() {
|
|
21
|
+
return this.decoratee.messages$;
|
|
22
|
+
}
|
|
23
|
+
get errors$() {
|
|
24
|
+
return this.decoratee.errors$;
|
|
25
|
+
}
|
|
26
|
+
close() {
|
|
27
|
+
console.log(`Closing transport`);
|
|
28
|
+
this.decoratee.close();
|
|
29
|
+
}
|
|
30
|
+
send(msg) {
|
|
31
|
+
console.log(`[${new Date()}] - Sending message: ${JSON.stringify(msg)}`);
|
|
32
|
+
this.decoratee.send(msg);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.TransportWithLogs = TransportWithLogs;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FromAgent, FromServer, Message } from '@awarevue/api-types';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
/** What the SDK expects from a transport */
|
|
4
|
+
export interface Transport {
|
|
5
|
+
readonly connected$: Observable<boolean>;
|
|
6
|
+
readonly messages$: Observable<Message<FromServer>>;
|
|
7
|
+
readonly errors$: Observable<Error>;
|
|
8
|
+
send(msg: Message<FromAgent>): void;
|
|
9
|
+
close(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { FromAgent, FromServer, Message } from '@awarevue/api-types';
|
|
3
|
+
import { Transport } from './transport';
|
|
4
|
+
export declare class WsAgentTransport implements Transport {
|
|
5
|
+
private readonly url;
|
|
6
|
+
readonly connected$: Observable<boolean>;
|
|
7
|
+
readonly messages$: Observable<Message<FromServer>>;
|
|
8
|
+
readonly errors$: Observable<Error>;
|
|
9
|
+
private ws;
|
|
10
|
+
private readonly _connected$;
|
|
11
|
+
private readonly _messages$;
|
|
12
|
+
private readonly _errors$;
|
|
13
|
+
private readonly outbound$;
|
|
14
|
+
private readonly destroy$;
|
|
15
|
+
private reconnectDelay;
|
|
16
|
+
private readonly maxDelay;
|
|
17
|
+
private destroyed;
|
|
18
|
+
constructor(url: string);
|
|
19
|
+
send(msg: Message<FromAgent>): void;
|
|
20
|
+
/** Call once when your container/module shuts down. */
|
|
21
|
+
close(): void;
|
|
22
|
+
/** One subscription for all outbound traffic. */
|
|
23
|
+
private setupSender;
|
|
24
|
+
/** Establish or re-establish a WebSocket connection. */
|
|
25
|
+
private connect;
|
|
26
|
+
/** Exponential back-off reconnect, cancelled via destroy$. */
|
|
27
|
+
private scheduleReconnect;
|
|
28
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ws-agent-transport.ts
|
|
3
|
+
//--------------------------------------------------------------
|
|
4
|
+
// A robust, RxJS-friendly WebSocket transport for the Aware SDK
|
|
5
|
+
// • automatic exponential back-off reconnect
|
|
6
|
+
// • single outbound queue, no per-call subscriptions
|
|
7
|
+
// • clean shutdown (no leaks)
|
|
8
|
+
//--------------------------------------------------------------
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.WsAgentTransport = void 0;
|
|
11
|
+
const ws_1 = require("ws");
|
|
12
|
+
const rxjs_1 = require("rxjs");
|
|
13
|
+
class WsAgentTransport {
|
|
14
|
+
constructor(url) {
|
|
15
|
+
this.url = url;
|
|
16
|
+
this._connected$ = new rxjs_1.BehaviorSubject(false);
|
|
17
|
+
this._messages$ = new rxjs_1.Subject();
|
|
18
|
+
this._errors$ = new rxjs_1.Subject();
|
|
19
|
+
this.outbound$ = new rxjs_1.Subject();
|
|
20
|
+
this.destroy$ = new rxjs_1.Subject();
|
|
21
|
+
this.reconnectDelay = 1000; // start at 1 s
|
|
22
|
+
this.maxDelay = 30000; // cap at 30 s
|
|
23
|
+
this.destroyed = false;
|
|
24
|
+
this.connected$ = this._connected$.asObservable();
|
|
25
|
+
this.messages$ = this._messages$.asObservable();
|
|
26
|
+
this.errors$ = this._errors$.asObservable();
|
|
27
|
+
this.connect(); // first connection
|
|
28
|
+
this.setupSender(); // single outbound subscription
|
|
29
|
+
}
|
|
30
|
+
//----------------------------------------
|
|
31
|
+
// Public API
|
|
32
|
+
//----------------------------------------
|
|
33
|
+
send(msg) {
|
|
34
|
+
const { kind, ...data } = msg;
|
|
35
|
+
const payload = JSON.stringify({ event: kind, data });
|
|
36
|
+
this.outbound$.next(payload);
|
|
37
|
+
}
|
|
38
|
+
/** Call once when your container/module shuts down. */
|
|
39
|
+
close() {
|
|
40
|
+
var _a;
|
|
41
|
+
if (this.destroyed)
|
|
42
|
+
return;
|
|
43
|
+
this.destroyed = true;
|
|
44
|
+
this.destroy$.next();
|
|
45
|
+
this.destroy$.complete();
|
|
46
|
+
this._connected$.complete();
|
|
47
|
+
this._messages$.complete();
|
|
48
|
+
this._errors$.complete();
|
|
49
|
+
/** terminate() is immediate; skip the close handshake intentionally */
|
|
50
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.terminate();
|
|
51
|
+
}
|
|
52
|
+
//----------------------------------------
|
|
53
|
+
// Private helpers
|
|
54
|
+
//----------------------------------------
|
|
55
|
+
/** One subscription for all outbound traffic. */
|
|
56
|
+
setupSender() {
|
|
57
|
+
this.outbound$
|
|
58
|
+
.pipe((0, rxjs_1.concatMap)((payload) => this._connected$.pipe((0, rxjs_1.filter)(Boolean), // wait until we’re actually connected
|
|
59
|
+
(0, rxjs_1.take)(1), // just the next open
|
|
60
|
+
(0, rxjs_1.takeUntil)(this.destroy$), (0, rxjs_1.map)(() => payload))), (0, rxjs_1.takeUntil)(this.destroy$))
|
|
61
|
+
.subscribe((payload) => {
|
|
62
|
+
/* readyState **must** be OPEN here by construction */
|
|
63
|
+
this.ws.send(payload);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Establish or re-establish a WebSocket connection. */
|
|
67
|
+
connect() {
|
|
68
|
+
this.ws = new ws_1.WebSocket(this.url, {
|
|
69
|
+
headers: { 'User-Agent': 'Aware Agent SDK' },
|
|
70
|
+
});
|
|
71
|
+
this.ws.on('open', () => {
|
|
72
|
+
this.reconnectDelay = 1000; // reset back-off
|
|
73
|
+
this._connected$.next(true);
|
|
74
|
+
});
|
|
75
|
+
this.ws.on('message', (data) => {
|
|
76
|
+
const raw = JSON.parse(data.toString());
|
|
77
|
+
this._messages$.next({
|
|
78
|
+
kind: raw.event,
|
|
79
|
+
...raw.data,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
this.ws.on('error', (err) => {
|
|
83
|
+
this._errors$.next(err);
|
|
84
|
+
// the 'close' handler will schedule reconnect
|
|
85
|
+
});
|
|
86
|
+
this.ws.on('close', () => {
|
|
87
|
+
this._connected$.next(false);
|
|
88
|
+
if (!this.destroyed)
|
|
89
|
+
this.scheduleReconnect();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Exponential back-off reconnect, cancelled via destroy$. */
|
|
93
|
+
scheduleReconnect() {
|
|
94
|
+
(0, rxjs_1.timer)(this.reconnectDelay)
|
|
95
|
+
.pipe((0, rxjs_1.takeUntil)(this.destroy$))
|
|
96
|
+
.subscribe(() => this.connect());
|
|
97
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxDelay);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.WsAgentTransport = WsAgentTransport;
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@awarevue/agent-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "SDK for building Agent implementations that speak the Aware protocol.",
|
|
5
|
+
"author": "Yaser Awajan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc -p tsconfig.json && cp package.json dist/",
|
|
24
|
+
"prepublishOnly": "yarn build",
|
|
25
|
+
"test": "echo \"add unit tests here\"",
|
|
26
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
27
|
+
"lint:fix": "yarn lint --fix"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@awarevue/api-types": "^1.0.0",
|
|
31
|
+
"rxjs": "^7.8.2",
|
|
32
|
+
"ws": "^8",
|
|
33
|
+
"zod": "3.24.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^20.12.7",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
|
38
|
+
"@typescript-eslint/parser": "^8.31.1",
|
|
39
|
+
"eslint": "^9.25.1",
|
|
40
|
+
"eslint-config-prettier": "^10.1.2",
|
|
41
|
+
"eslint-plugin-import": "^2.31.0",
|
|
42
|
+
"typescript": "^5.8.3"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
}
|
|
48
|
+
}
|