@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,374 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as os from 'os';
3
- import * as path from 'path';
4
- import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
5
- import { Logger } from './logger';
6
- import { EigenFluxNotifier } from './notifier';
7
-
8
- function createLogger(): Logger {
9
- return new Logger({
10
- info: jest.fn(),
11
- warn: jest.fn(),
12
- error: jest.fn(),
13
- debug: jest.fn(),
14
- });
15
- }
16
-
17
- function createApi(overrides: Partial<OpenClawPluginApi> = {}): OpenClawPluginApi {
18
- return {
19
- id: 'eigenflux',
20
- name: 'EigenFlux',
21
- source: '/tmp/eigenflux',
22
- config: {},
23
- pluginConfig: {},
24
- runtime: {} as OpenClawPluginApi['runtime'],
25
- logger: {
26
- info: jest.fn(),
27
- warn: jest.fn(),
28
- error: jest.fn(),
29
- debug: jest.fn(),
30
- },
31
- registerService: jest.fn(),
32
- ...overrides,
33
- } as unknown as OpenClawPluginApi;
34
- }
35
-
36
- function createConfig() {
37
- return {
38
- gatewayUrl: 'ws://127.0.0.1:18789',
39
- sessionKey: 'agent:main:feishu:direct:ou_123',
40
- agentId: 'main',
41
- replyChannel: 'feishu',
42
- replyTo: 'ou_123',
43
- openclawCliBin: 'openclaw',
44
- };
45
- }
46
-
47
- describe('EigenFluxNotifier', () => {
48
- test('prefers runtime.subagent delivery when available', async () => {
49
- const run = jest.fn().mockResolvedValue({ runId: 'run-subagent' });
50
- const sendAgentMessage = jest.fn();
51
-
52
- const notifier = new EigenFluxNotifier(
53
- createApi({
54
- runtime: {
55
- subagent: {
56
- run,
57
- },
58
- } as OpenClawPluginApi['runtime'],
59
- }),
60
- createLogger(),
61
- createConfig(),
62
- () => ({
63
- sendAgentMessage,
64
- }),
65
- jest.fn()
66
- );
67
-
68
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
69
- expect(run).toHaveBeenCalledWith({
70
- sessionKey: 'agent:main:feishu:direct:ou_123',
71
- message: '[EIGENFLUX_TEST] payload',
72
- deliver: true,
73
- idempotencyKey: expect.any(String),
74
- });
75
- expect(sendAgentMessage).not.toHaveBeenCalled();
76
- });
77
-
78
- test('falls back to gateway rpc agent when runtime.subagent is unavailable', async () => {
79
- const sendAgentMessage = jest.fn().mockResolvedValue({
80
- sessionKey: 'agent:main:feishu:direct:ou_123',
81
- runId: 'run-gateway',
82
- });
83
-
84
- const notifier = new EigenFluxNotifier(
85
- createApi({
86
- runtime: {} as OpenClawPluginApi['runtime'],
87
- }),
88
- createLogger(),
89
- createConfig(),
90
- () => ({
91
- sendAgentMessage,
92
- }),
93
- jest.fn()
94
- );
95
-
96
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
97
- expect(sendAgentMessage).toHaveBeenCalledWith('[EIGENFLUX_TEST] payload');
98
- });
99
-
100
- test('falls back to runtime command agent when gateway rpc fails', async () => {
101
- const runCommandWithTimeout = jest.fn().mockResolvedValue({
102
- code: 0,
103
- stdout: 'ok',
104
- stderr: '',
105
- });
106
-
107
- const notifier = new EigenFluxNotifier(
108
- createApi({
109
- runtime: {
110
- system: {
111
- runCommandWithTimeout,
112
- },
113
- } as OpenClawPluginApi['runtime'],
114
- }),
115
- createLogger(),
116
- createConfig(),
117
- () => ({
118
- sendAgentMessage: jest.fn().mockRejectedValue(new Error('gateway failed')),
119
- }),
120
- jest.fn()
121
- );
122
-
123
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
124
- expect(runCommandWithTimeout).toHaveBeenCalledWith(
125
- [
126
- 'openclaw',
127
- 'agent',
128
- '--message',
129
- '[EIGENFLUX_TEST] payload',
130
- '--agent',
131
- 'main',
132
- '--deliver',
133
- '--reply-channel',
134
- 'feishu',
135
- '--reply-to',
136
- 'ou_123',
137
- ],
138
- { timeoutMs: 15000 }
139
- );
140
- });
141
-
142
- test('falls back to runtime heartbeat when command path is unavailable', async () => {
143
- const enqueueSystemEvent = jest.fn().mockReturnValue(true);
144
- const requestHeartbeatNow = jest.fn();
145
- const spawnRunner = jest.fn().mockResolvedValue({
146
- ok: false as const,
147
- mode: 'spawn',
148
- error: 'spawn disabled in test',
149
- });
150
-
151
- const notifier = new EigenFluxNotifier(
152
- createApi({
153
- runtime: {
154
- system: {
155
- enqueueSystemEvent,
156
- requestHeartbeatNow,
157
- },
158
- } as OpenClawPluginApi['runtime'],
159
- }),
160
- createLogger(),
161
- createConfig(),
162
- () => ({
163
- sendAgentMessage: jest.fn().mockRejectedValue(new Error('gateway failed')),
164
- }),
165
- spawnRunner
166
- );
167
-
168
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
169
- expect(enqueueSystemEvent).toHaveBeenCalledWith('[EIGENFLUX_TEST] payload', {
170
- sessionKey: 'agent:main:feishu:direct:ou_123',
171
- deliveryContext: {
172
- channel: 'feishu',
173
- to: 'ou_123',
174
- },
175
- });
176
- expect(requestHeartbeatNow).toHaveBeenCalledWith({
177
- reason: 'plugin:eigenflux',
178
- coalesceMs: 0,
179
- agentId: 'main',
180
- sessionKey: 'agent:main:feishu:direct:ou_123',
181
- });
182
- });
183
-
184
- test('deliverWithSubagent only uses runtime.subagent', async () => {
185
- const run = jest.fn().mockResolvedValue({ runId: 'run-subagent-only' });
186
- const notifier = new EigenFluxNotifier(
187
- createApi({
188
- runtime: {
189
- subagent: {
190
- run,
191
- },
192
- } as OpenClawPluginApi['runtime'],
193
- }),
194
- createLogger(),
195
- createConfig(),
196
- () => ({
197
- sendAgentMessage: jest.fn(),
198
- }),
199
- jest.fn()
200
- );
201
-
202
- await expect(notifier.deliverWithSubagent('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
203
- expect(run).toHaveBeenCalledTimes(1);
204
- });
205
-
206
- test('resolves the freshest external session route for runtime.subagent', async () => {
207
- const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eigenflux-session-store-'));
208
- const sessionStorePath = path.join(stateDir, 'sessions.json');
209
- fs.writeFileSync(
210
- sessionStorePath,
211
- JSON.stringify({
212
- 'agent:main:main': {
213
- updatedAt: 100,
214
- deliveryContext: { channel: 'webchat' },
215
- },
216
- 'agent:main:feishu:direct:ou_older': {
217
- updatedAt: 200,
218
- deliveryContext: {
219
- channel: 'feishu',
220
- to: 'user:ou_older',
221
- accountId: 'default',
222
- },
223
- },
224
- 'agent:main:feishu:group:oc_latest': {
225
- updatedAt: 300,
226
- deliveryContext: {
227
- channel: 'feishu',
228
- to: 'chat:oc_latest',
229
- accountId: 'default',
230
- },
231
- },
232
- }),
233
- 'utf-8'
234
- );
235
-
236
- const run = jest.fn().mockResolvedValue({ runId: 'run-external-session' });
237
- const notifier = new EigenFluxNotifier(
238
- createApi({
239
- runtime: {
240
- subagent: {
241
- run,
242
- },
243
- } as OpenClawPluginApi['runtime'],
244
- }),
245
- createLogger(),
246
- {
247
- ...createConfig(),
248
- sessionKey: 'main',
249
- replyChannel: undefined,
250
- replyTo: undefined,
251
- replyAccountId: undefined,
252
- sessionStorePath,
253
- },
254
- () => ({
255
- sendAgentMessage: jest.fn(),
256
- }),
257
- jest.fn()
258
- );
259
-
260
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
261
- expect(run).toHaveBeenCalledWith({
262
- sessionKey: 'agent:main:feishu:group:oc_latest',
263
- message: '[EIGENFLUX_TEST] payload',
264
- deliver: true,
265
- idempotencyKey: expect.any(String),
266
- });
267
-
268
- fs.rmSync(stateDir, { recursive: true, force: true });
269
- });
270
-
271
- test('normalizes explicit reply targets from session store before CLI fallback', async () => {
272
- const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eigenflux-session-store-'));
273
- const sessionStorePath = path.join(stateDir, 'sessions.json');
274
- fs.writeFileSync(
275
- sessionStorePath,
276
- JSON.stringify({
277
- 'agent:main:feishu:direct:ou_123': {
278
- updatedAt: 300,
279
- deliveryContext: {
280
- channel: 'feishu',
281
- to: 'user:ou_123',
282
- accountId: 'default',
283
- },
284
- },
285
- }),
286
- 'utf-8'
287
- );
288
-
289
- const runCommandWithTimeout = jest.fn().mockResolvedValue({
290
- code: 0,
291
- stdout: 'ok',
292
- stderr: '',
293
- });
294
-
295
- const notifier = new EigenFluxNotifier(
296
- createApi({
297
- runtime: {
298
- system: {
299
- runCommandWithTimeout,
300
- },
301
- } as OpenClawPluginApi['runtime'],
302
- }),
303
- createLogger(),
304
- {
305
- ...createConfig(),
306
- sessionKey: 'main',
307
- replyChannel: 'feishu',
308
- replyTo: 'ou_123',
309
- sessionStorePath,
310
- },
311
- () => ({
312
- sendAgentMessage: jest.fn().mockRejectedValue(new Error('gateway failed')),
313
- }),
314
- jest.fn()
315
- );
316
-
317
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
318
- expect(runCommandWithTimeout).toHaveBeenCalledWith(
319
- [
320
- 'openclaw',
321
- 'agent',
322
- '--message',
323
- '[EIGENFLUX_TEST] payload',
324
- '--agent',
325
- 'main',
326
- '--deliver',
327
- '--reply-channel',
328
- 'feishu',
329
- '--reply-to',
330
- 'user:ou_123',
331
- '--reply-account',
332
- 'default',
333
- ],
334
- { timeoutMs: 15000 }
335
- );
336
-
337
- fs.rmSync(stateDir, { recursive: true, force: true });
338
- });
339
-
340
- test('remembers the resolved route after a successful delivery', async () => {
341
- const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'eigenflux-notifier-memory-'));
342
- const run = jest.fn().mockResolvedValue({ runId: 'run-subagent-memory' });
343
- const notifier = new EigenFluxNotifier(
344
- createApi({
345
- runtime: {
346
- subagent: {
347
- run,
348
- },
349
- } as OpenClawPluginApi['runtime'],
350
- }),
351
- createLogger(),
352
- {
353
- ...createConfig(),
354
- workdir,
355
- },
356
- () => ({
357
- sendAgentMessage: jest.fn(),
358
- }),
359
- jest.fn()
360
- );
361
-
362
- await expect(notifier.deliver('[EIGENFLUX_TEST] payload')).resolves.toBe(true);
363
-
364
- const remembered = JSON.parse(
365
- fs.readFileSync(path.join(workdir, 'session.json'), 'utf-8')
366
- ) as Record<string, unknown>;
367
- expect(remembered.sessionKey).toBe('agent:main:feishu:direct:ou_123');
368
- expect(remembered.agentId).toBe('main');
369
- expect(remembered.replyChannel).toBe('feishu');
370
- expect(remembered.replyTo).toBe('ou_123');
371
-
372
- fs.rmSync(workdir, { recursive: true, force: true });
373
- });
374
- });