@phronesis-io/openclaw-eigenflux 0.0.1 → 0.0.3

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.
Files changed (44) hide show
  1. package/README.md +16 -3
  2. package/dist/config.d.ts +12 -5
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +47 -10
  5. package/dist/config.js.map +1 -1
  6. package/dist/index.d.ts +5 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/notifier.d.ts +1 -17
  11. package/dist/notifier.d.ts.map +1 -1
  12. package/dist/notifier.js +1 -94
  13. package/dist/notifier.js.map +1 -1
  14. package/dist/pm-polling-client.d.ts +1 -0
  15. package/dist/pm-polling-client.d.ts.map +1 -1
  16. package/dist/pm-polling-client.js +64 -57
  17. package/dist/pm-polling-client.js.map +1 -1
  18. package/dist/polling-client.d.ts +1 -0
  19. package/dist/polling-client.d.ts.map +1 -1
  20. package/dist/polling-client.js +65 -58
  21. package/dist/polling-client.js.map +1 -1
  22. package/openclaw.plugin.json +8 -6
  23. package/package.json +2 -2
  24. package/src/agent-prompt-templates.ts +0 -91
  25. package/src/config.test.ts +0 -188
  26. package/src/config.ts +0 -410
  27. package/src/credentials-loader.test.ts +0 -78
  28. package/src/credentials-loader.ts +0 -121
  29. package/src/gateway-rpc-client.test.ts +0 -190
  30. package/src/gateway-rpc-client.ts +0 -373
  31. package/src/index.integration.test.ts +0 -437
  32. package/src/index.test.ts +0 -454
  33. package/src/index.ts +0 -758
  34. package/src/logger.ts +0 -27
  35. package/src/notification-route-resolver.test.ts +0 -136
  36. package/src/notification-route-resolver.ts +0 -430
  37. package/src/notifier.test.ts +0 -374
  38. package/src/notifier.ts +0 -558
  39. package/src/openclaw-plugin-sdk.d.ts +0 -121
  40. package/src/pm-polling-client.test.ts +0 -390
  41. package/src/pm-polling-client.ts +0 -257
  42. package/src/polling-client.test.ts +0 -279
  43. package/src/polling-client.ts +0 -283
  44. package/src/session-route-memory.ts +0 -106
@@ -1,373 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import WebSocket from 'ws';
3
- import { Logger } from './logger';
4
-
5
- const GATEWAY_PROTOCOL_VERSION = 3;
6
- const DEFAULT_CONNECT_TIMEOUT_MS = 8000;
7
- const DEFAULT_REQUEST_TIMEOUT_MS = 10000;
8
- const DEFAULT_SESSION_KEY = 'main';
9
- const DEFAULT_AGENT_ID = 'main';
10
-
11
- type GatewayRequest = {
12
- type: 'req';
13
- id: string;
14
- method: string;
15
- params?: unknown;
16
- };
17
-
18
- type GatewayResponse = {
19
- type: 'res';
20
- id: string;
21
- ok: boolean;
22
- payload?: any;
23
- error?: {
24
- code?: string;
25
- message?: string;
26
- };
27
- };
28
-
29
- type GatewayEvent = {
30
- type: 'event';
31
- event: string;
32
- payload?: any;
33
- };
34
-
35
- type PendingRequest = {
36
- timer: NodeJS.Timeout;
37
- resolve: (payload: any) => void;
38
- reject: (error: Error) => void;
39
- };
40
-
41
- export type OpenClawGatewayRpcClientOptions = {
42
- gatewayUrl: string;
43
- gatewayToken?: string;
44
- sessionKey?: string;
45
- agentId?: string;
46
- replyChannel?: string;
47
- replyTo?: string;
48
- replyAccountId?: string;
49
- logger: Logger;
50
- connectTimeoutMs?: number;
51
- requestTimeoutMs?: number;
52
- };
53
-
54
- export class OpenClawGatewayRpcClient {
55
- private readonly options: OpenClawGatewayRpcClientOptions;
56
-
57
- constructor(options: OpenClawGatewayRpcClientOptions) {
58
- this.options = options;
59
- }
60
-
61
- async sendAgentMessage(message: string): Promise<{ sessionKey: string; runId: string }> {
62
- return this.withConnection(async (conn) => {
63
- const sessionKey = this.options.sessionKey || (await this.resolveSessionKey(conn));
64
- const agentId = this.options.agentId?.trim() || this.resolveAgentIdFromSessionKey(sessionKey);
65
- const idempotencyKey = randomUUID();
66
-
67
- const response = await conn.request('agent', {
68
- sessionKey,
69
- agentId,
70
- message,
71
- deliver: true,
72
- ...(this.options.replyChannel ? { replyChannel: this.options.replyChannel } : {}),
73
- ...(this.options.replyTo ? { replyTo: this.options.replyTo } : {}),
74
- ...(this.options.replyAccountId
75
- ? { replyAccountId: this.options.replyAccountId }
76
- : {}),
77
- idempotencyKey,
78
- });
79
-
80
- const runId = String(response?.runId || idempotencyKey);
81
- return { sessionKey, runId };
82
- });
83
- }
84
-
85
- private resolveAgentIdFromSessionKey(sessionKey: string | undefined): string {
86
- const trimmed = sessionKey?.trim() || '';
87
- const parts = trimmed.split(':').filter((part) => part.length > 0);
88
- if (parts[0]?.toLowerCase() === 'agent' && typeof parts[1] === 'string' && parts[1].trim()) {
89
- return parts[1].trim().toLowerCase();
90
- }
91
- return DEFAULT_AGENT_ID;
92
- }
93
-
94
- private async resolveSessionKey(conn: GatewayConnection): Promise<string> {
95
- try {
96
- const response = await conn.request('sessions.list', {
97
- limit: 20,
98
- includeGlobal: true,
99
- includeUnknown: true,
100
- includeLastMessage: false,
101
- });
102
- const sessions = Array.isArray(response?.sessions)
103
- ? (response.sessions as Array<{ key?: unknown; active?: unknown; kind?: unknown }>)
104
- : [];
105
- const byMainKey = sessions.find((entry) => entry && entry.key === DEFAULT_SESSION_KEY);
106
- if (byMainKey && typeof byMainKey.key === 'string') {
107
- return DEFAULT_SESSION_KEY;
108
- }
109
-
110
- const byMainKind = sessions.find(
111
- (entry) => entry && typeof entry.key === 'string' && String(entry.kind || '').toLowerCase() === 'main'
112
- );
113
- if (byMainKind && typeof byMainKind.key === 'string') {
114
- return byMainKind.key;
115
- }
116
-
117
- const byActive = sessions.find(
118
- (entry) => entry && typeof entry.key === 'string' && entry.active === true
119
- );
120
- if (byActive && typeof byActive.key === 'string') {
121
- return byActive.key;
122
- }
123
-
124
- const first = sessions.find((entry) => entry && typeof entry.key === 'string');
125
- if (first && typeof first.key === 'string') {
126
- return first.key;
127
- }
128
- } catch (error) {
129
- this.options.logger.warn(
130
- `sessions.list failed, fallback to "${DEFAULT_SESSION_KEY}": ${this.formatError(error)}`
131
- );
132
- }
133
- return DEFAULT_SESSION_KEY;
134
- }
135
-
136
- private async withConnection<T>(fn: (conn: GatewayConnection) => Promise<T>): Promise<T> {
137
- const conn = new GatewayConnection({
138
- gatewayUrl: this.options.gatewayUrl,
139
- gatewayToken: this.options.gatewayToken,
140
- logger: this.options.logger,
141
- connectTimeoutMs: this.options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS,
142
- requestTimeoutMs: this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,
143
- });
144
-
145
- try {
146
- await conn.connect();
147
- return await fn(conn);
148
- } finally {
149
- await conn.close();
150
- }
151
- }
152
-
153
- private formatError(error: unknown): string {
154
- if (error instanceof Error) {
155
- return `${error.name}: ${error.message}`;
156
- }
157
- return String(error);
158
- }
159
- }
160
-
161
- type GatewayConnectionOptions = {
162
- gatewayUrl: string;
163
- gatewayToken?: string;
164
- logger: Logger;
165
- connectTimeoutMs: number;
166
- requestTimeoutMs: number;
167
- };
168
-
169
- class GatewayConnection {
170
- private readonly options: GatewayConnectionOptions;
171
- private ws: WebSocket | null = null;
172
- private connectNonce: string | null = null;
173
- private connected = false;
174
- private pending = new Map<string, PendingRequest>();
175
-
176
- constructor(options: GatewayConnectionOptions) {
177
- this.options = options;
178
- }
179
-
180
- async connect(): Promise<void> {
181
- if (this.connected) {
182
- return;
183
- }
184
-
185
- this.ws = new WebSocket(this.options.gatewayUrl, {
186
- maxPayload: 25 * 1024 * 1024,
187
- });
188
-
189
- return new Promise<void>((resolve, reject) => {
190
- let settled = false;
191
- let connectRequested = false;
192
- const connectTimer = setTimeout(() => {
193
- onConnectError(new Error(`Gateway connect timeout after ${this.options.connectTimeoutMs}ms`));
194
- }, this.options.connectTimeoutMs);
195
-
196
- const cleanup = () => {
197
- clearTimeout(connectTimer);
198
- };
199
-
200
- const settle = (fn: () => void) => {
201
- if (settled) {
202
- return;
203
- }
204
- settled = true;
205
- cleanup();
206
- fn();
207
- };
208
-
209
- const onConnectError = (error: unknown) => {
210
- settle(() => {
211
- reject(error instanceof Error ? error : new Error(String(error)));
212
- void this.close();
213
- });
214
- };
215
-
216
- this.ws?.on('error', onConnectError);
217
- this.ws?.on('close', () => {
218
- if (!this.connected) {
219
- onConnectError(new Error('Gateway closed before connect'));
220
- return;
221
- }
222
- this.rejectAllPending(new Error('Gateway connection closed'));
223
- });
224
-
225
- this.ws?.on('message', (data) => {
226
- const raw = data.toString();
227
- let frame: GatewayEvent | GatewayResponse | null = null;
228
- try {
229
- frame = JSON.parse(raw);
230
- } catch {
231
- return;
232
- }
233
- if (!frame || typeof frame !== 'object') {
234
- return;
235
- }
236
- if (frame.type === 'event') {
237
- try {
238
- this.handleEventFrame(frame);
239
- } catch (error) {
240
- onConnectError(error);
241
- return;
242
- }
243
- if (frame.event === 'connect.challenge' && !connectRequested) {
244
- connectRequested = true;
245
- void this.request('connect', this.buildConnectParams())
246
- .then(() => {
247
- this.connected = true;
248
- settle(() => resolve());
249
- })
250
- .catch(onConnectError);
251
- }
252
- return;
253
- }
254
- if (frame.type === 'res') {
255
- try {
256
- this.handleResponseFrame(frame);
257
- } catch (error) {
258
- onConnectError(error);
259
- }
260
- }
261
- });
262
- });
263
- }
264
-
265
- async close(): Promise<void> {
266
- this.rejectAllPending(new Error('Gateway request cancelled: connection closed'));
267
- if (!this.ws) {
268
- return;
269
- }
270
- const ws = this.ws;
271
- this.ws = null;
272
- this.connected = false;
273
- await new Promise<void>((resolve) => {
274
- ws.once('close', () => resolve());
275
- ws.close(1000);
276
- setTimeout(() => resolve(), 1000);
277
- });
278
- }
279
-
280
- async request(method: string, params?: unknown): Promise<any> {
281
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
282
- throw new Error(`Gateway request failed (${method}): websocket is not open`);
283
- }
284
-
285
- const id = randomUUID();
286
- const frame: GatewayRequest = {
287
- type: 'req',
288
- id,
289
- method,
290
- params,
291
- };
292
-
293
- return new Promise<any>((resolve, reject) => {
294
- const timer = setTimeout(() => {
295
- this.pending.delete(id);
296
- reject(new Error(`Gateway request timeout (${method}) after ${this.options.requestTimeoutMs}ms`));
297
- }, this.options.requestTimeoutMs);
298
-
299
- this.pending.set(id, { timer, resolve, reject });
300
- this.ws?.send(JSON.stringify(frame), (error) => {
301
- if (!error) {
302
- return;
303
- }
304
- clearTimeout(timer);
305
- this.pending.delete(id);
306
- reject(error instanceof Error ? error : new Error(String(error)));
307
- });
308
- });
309
- }
310
-
311
- private handleEventFrame(frame: GatewayEvent): void {
312
- if (frame.event !== 'connect.challenge') {
313
- return;
314
- }
315
- const nonce = frame.payload?.nonce;
316
- if (typeof nonce !== 'string' || nonce.trim().length === 0) {
317
- throw new Error('Gateway connect.challenge missing nonce');
318
- }
319
- this.connectNonce = nonce.trim();
320
- }
321
-
322
- private handleResponseFrame(frame: GatewayResponse): void {
323
- const pending = this.pending.get(frame.id);
324
- if (!pending) {
325
- return;
326
- }
327
- clearTimeout(pending.timer);
328
- this.pending.delete(frame.id);
329
- if (frame.ok) {
330
- pending.resolve(frame.payload);
331
- return;
332
- }
333
- const message = frame.error?.message || 'unknown gateway error';
334
- pending.reject(new Error(message));
335
- }
336
-
337
- private buildConnectParams(): Record<string, unknown> {
338
- if (!this.connectNonce) {
339
- throw new Error('Gateway connect failed: missing challenge nonce');
340
- }
341
-
342
- const authToken =
343
- typeof this.options.gatewayToken === 'string' ? this.options.gatewayToken.trim() : '';
344
-
345
- const params: Record<string, unknown> = {
346
- minProtocol: GATEWAY_PROTOCOL_VERSION,
347
- maxProtocol: GATEWAY_PROTOCOL_VERSION,
348
- client: {
349
- id: 'eigenflux-gateway-client',
350
- displayName: 'eigenflux',
351
- version: '1.0.0',
352
- platform: process.platform,
353
- mode: 'backend',
354
- },
355
- role: 'operator',
356
- scopes: ['operator.admin'],
357
- };
358
-
359
- if (authToken) {
360
- params.auth = { token: authToken };
361
- }
362
-
363
- return params;
364
- }
365
-
366
- private rejectAllPending(error: Error): void {
367
- for (const [id, pending] of this.pending.entries()) {
368
- clearTimeout(pending.timer);
369
- this.pending.delete(id);
370
- pending.reject(error);
371
- }
372
- }
373
- }