@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
package/src/index.test.ts DELETED
@@ -1,454 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as os from 'os';
3
- import * as path from 'path';
4
-
5
- const sendAgentMessageMock = jest.fn().mockResolvedValue({
6
- sessionKey: 'main',
7
- runId: 'run-test',
8
- });
9
-
10
- jest.mock('./gateway-rpc-client', () => ({
11
- OpenClawGatewayRpcClient: jest.fn().mockImplementation(() => ({
12
- sendAgentMessage: sendAgentMessageMock,
13
- })),
14
- }));
15
-
16
- function createLogger() {
17
- return {
18
- info: jest.fn(),
19
- warn: jest.fn(),
20
- error: jest.fn(),
21
- debug: jest.fn(),
22
- };
23
- }
24
-
25
- describe('register unit', () => {
26
- let homeDir: string;
27
- let originalHome: string | undefined;
28
- let workdir: string;
29
-
30
- beforeEach(() => {
31
- jest.resetModules();
32
- jest.clearAllMocks();
33
- originalHome = process.env.HOME;
34
- homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eigenflux-openclaw-home-'));
35
- process.env.HOME = homeDir;
36
- workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'eigenflux-openclaw-workdir-'));
37
- });
38
-
39
- afterEach(() => {
40
- if (originalHome === undefined) {
41
- delete process.env.HOME;
42
- } else {
43
- process.env.HOME = originalHome;
44
- }
45
- fs.rmSync(homeDir, { recursive: true, force: true });
46
- fs.rmSync(workdir, { recursive: true, force: true });
47
- delete (global as { fetch?: typeof fetch }).fetch;
48
- });
49
-
50
- test('sends onboarding prompt through gateway fallback when service starts without token', async () => {
51
- const { default: plugin } = await import('./index');
52
- const services: any[] = [];
53
-
54
- plugin.register({
55
- config: {},
56
- pluginConfig: {
57
- gatewayUrl: 'ws://127.0.0.1:18789',
58
- servers: [
59
- {
60
- name: 'eigenflux',
61
- endpoint: 'http://127.0.0.1:18080',
62
- workdir,
63
- },
64
- ],
65
- },
66
- runtime: {},
67
- logger: createLogger(),
68
- registerService: (service: any) => services.push(service),
69
- registerCommand: jest.fn(),
70
- registerHook: jest.fn(),
71
- on: jest.fn(),
72
- } as any);
73
-
74
- await services[0].start();
75
-
76
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
77
- expect.stringContaining('[EIGENFLUX_AUTH_REQUIRED]')
78
- );
79
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
80
- expect.stringContaining(`credentials_path=${path.join(workdir, 'credentials.json')}`)
81
- );
82
- expect(sendAgentMessageMock).toHaveBeenCalledWith(expect.stringContaining('network=eigenflux'));
83
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
84
- expect.stringContaining(`workdir=${workdir}`)
85
- );
86
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
87
- expect.stringContaining('skill_file=http://127.0.0.1:18080/skill.md')
88
- );
89
-
90
- await services[0].stop();
91
- });
92
-
93
- test('supports /eigenflux auth, profile, and feed commands', async () => {
94
- fs.mkdirSync(workdir, { recursive: true });
95
- const sessionStorePath = path.join(
96
- homeDir,
97
- '.openclaw',
98
- 'agents',
99
- 'main',
100
- 'sessions',
101
- 'sessions.json'
102
- );
103
- fs.writeFileSync(
104
- path.join(workdir, 'credentials.json'),
105
- JSON.stringify({ access_token: 'at_command_token' }),
106
- 'utf-8'
107
- );
108
-
109
- const { default: plugin } = await import('./index');
110
- const commands: any[] = [];
111
- plugin.register({
112
- config: {},
113
- pluginConfig: {
114
- servers: [
115
- {
116
- name: 'eigenflux',
117
- endpoint: 'http://127.0.0.1:18080',
118
- workdir,
119
- sessionStorePath,
120
- },
121
- ],
122
- },
123
- runtime: {},
124
- logger: createLogger(),
125
- registerService: jest.fn(),
126
- registerCommand: (command: any) => commands.push(command),
127
- registerHook: jest.fn(),
128
- on: jest.fn(),
129
- } as any);
130
-
131
- expect(commands).toHaveLength(1);
132
- const command = commands[0];
133
-
134
- const fetchMock = jest
135
- .fn()
136
- .mockResolvedValueOnce(
137
- new Response(
138
- JSON.stringify({
139
- code: 0,
140
- msg: 'success',
141
- data: {
142
- agent: { id: '1', name: 'bot' },
143
- profile: { status: 3, keywords: ['ai'] },
144
- influence: { total_items: 1 },
145
- },
146
- }),
147
- { status: 200 }
148
- )
149
- )
150
- .mockResolvedValueOnce(
151
- new Response(
152
- JSON.stringify({
153
- code: 0,
154
- msg: 'success',
155
- data: {
156
- items: [
157
- {
158
- item_id: '901',
159
- broadcast_type: 'info',
160
- updated_at: 1760000000000,
161
- },
162
- ],
163
- has_more: false,
164
- notifications: [],
165
- },
166
- }),
167
- { status: 200 }
168
- )
169
- );
170
- global.fetch = fetchMock as typeof fetch;
171
-
172
- const authResp = await command.handler({ args: 'auth' });
173
- expect(authResp.text).toContain('EigenFlux auth status (server=eigenflux):');
174
- expect(authResp.text).toContain('status: available');
175
- expect(authResp.text).toContain('at_com');
176
-
177
- const profileResp = await command.handler({ args: 'profile' });
178
- expect(profileResp.text).toContain('EigenFlux profile (server=eigenflux):');
179
- expect(profileResp.text).toContain('"name": "bot"');
180
-
181
- const feedResp = await command.handler({ args: 'feed' });
182
- expect(feedResp.text).toContain('EigenFlux feed result (server=eigenflux):');
183
- expect(feedResp.text).toContain('"item_id": "901"');
184
- expect(fetchMock).toHaveBeenCalledTimes(2);
185
- });
186
-
187
- test('supports /eigenflux here and persists the current conversation route', async () => {
188
- const { default: plugin } = await import('./index');
189
- const commands: any[] = [];
190
- plugin.register({
191
- config: {},
192
- pluginConfig: {
193
- servers: [
194
- {
195
- name: 'eigenflux',
196
- endpoint: 'http://127.0.0.1:18080',
197
- workdir,
198
- sessionKey: 'agent:mengtian:feishu:direct:ou_current',
199
- agentId: 'mengtian',
200
- replyChannel: 'feishu',
201
- replyTo: 'user:ou_current',
202
- replyAccountId: 'default',
203
- },
204
- ],
205
- },
206
- runtime: {},
207
- logger: createLogger(),
208
- registerService: jest.fn(),
209
- registerCommand: (command: any) => commands.push(command),
210
- registerHook: jest.fn(),
211
- on: jest.fn(),
212
- } as any);
213
-
214
- const hereResp = await commands[0].handler({
215
- args: 'here',
216
- channel: 'feishu',
217
- to: 'user:ou_current',
218
- accountId: 'default',
219
- getCurrentConversationBinding: jest.fn().mockResolvedValue({
220
- channel: 'feishu',
221
- accountId: 'default',
222
- conversationId: 'user:ou_current',
223
- }),
224
- });
225
-
226
- expect(hereResp.text).toContain(
227
- 'EigenFlux server eigenflux will deliver to this conversation by default:'
228
- );
229
- expect(hereResp.text).toContain('sessionKey: agent:mengtian:feishu:direct:ou_current');
230
-
231
- const remembered = JSON.parse(
232
- fs.readFileSync(path.join(workdir, 'session.json'), 'utf-8')
233
- ) as Record<string, unknown>;
234
- expect(remembered.sessionKey).toBe('agent:mengtian:feishu:direct:ou_current');
235
- expect(remembered.agentId).toBe('mengtian');
236
- expect(remembered.replyChannel).toBe('feishu');
237
- expect(remembered.replyTo).toBe('user:ou_current');
238
- expect(remembered.replyAccountId).toBe('default');
239
- });
240
-
241
- test('automatically remembers the current conversation when any eigenflux command runs', async () => {
242
- fs.mkdirSync(workdir, { recursive: true });
243
- const sessionStorePath = path.join(
244
- homeDir,
245
- '.openclaw',
246
- 'agents',
247
- 'main',
248
- 'sessions',
249
- 'sessions.json'
250
- );
251
- fs.mkdirSync(path.dirname(sessionStorePath), { recursive: true });
252
- fs.writeFileSync(
253
- sessionStorePath,
254
- JSON.stringify({
255
- 'agent:main:feishu:group:oc_current': {
256
- updatedAt: 300,
257
- deliveryContext: {
258
- channel: 'feishu',
259
- to: 'chat:oc_current',
260
- accountId: 'default',
261
- },
262
- },
263
- }),
264
- 'utf-8'
265
- );
266
-
267
- const { default: plugin } = await import('./index');
268
- const commands: any[] = [];
269
- plugin.register({
270
- config: {},
271
- pluginConfig: {
272
- servers: [
273
- {
274
- name: 'eigenflux',
275
- endpoint: 'http://127.0.0.1:18080',
276
- workdir,
277
- sessionKey: 'agent:main:feishu:group:oc_current',
278
- agentId: 'main',
279
- replyChannel: 'feishu',
280
- replyTo: 'chat:oc_current',
281
- replyAccountId: 'default',
282
- sessionStorePath,
283
- },
284
- ],
285
- },
286
- runtime: {},
287
- logger: createLogger(),
288
- registerService: jest.fn(),
289
- registerCommand: (command: any) => commands.push(command),
290
- registerHook: jest.fn(),
291
- on: jest.fn(),
292
- } as any);
293
-
294
- const authResp = await commands[0].handler({
295
- args: 'auth',
296
- channel: 'feishu',
297
- to: 'chat:oc_current',
298
- accountId: 'default',
299
- getCurrentConversationBinding: jest.fn().mockResolvedValue({
300
- channel: 'feishu',
301
- accountId: 'default',
302
- conversationId: 'chat:oc_current',
303
- }),
304
- });
305
-
306
- expect(authResp.text).toContain('status: missing');
307
-
308
- const remembered = JSON.parse(
309
- fs.readFileSync(path.join(workdir, 'session.json'), 'utf-8')
310
- ) as Record<string, unknown>;
311
- expect(remembered.sessionKey).toBe('agent:main:feishu:group:oc_current');
312
- expect(remembered.replyTo).toBe('chat:oc_current');
313
- });
314
-
315
- test('prefers runtime.subagent delivery when available', async () => {
316
- const { default: plugin } = await import('./index');
317
- const services: any[] = [];
318
- const subagentRun = jest.fn().mockResolvedValue({ runId: 'run-subagent' });
319
-
320
- plugin.register({
321
- config: {},
322
- pluginConfig: {
323
- servers: [
324
- {
325
- name: 'eigenflux',
326
- endpoint: 'http://127.0.0.1:18080',
327
- workdir,
328
- sessionKey: 'agent:main:main',
329
- },
330
- ],
331
- },
332
- runtime: {
333
- subagent: {
334
- run: subagentRun,
335
- },
336
- },
337
- logger: createLogger(),
338
- registerService: (service: any) => services.push(service),
339
- registerCommand: jest.fn(),
340
- } as any);
341
-
342
- await services[0].start();
343
-
344
- expect(subagentRun).toHaveBeenCalledWith({
345
- sessionKey: 'agent:main:main',
346
- message: expect.stringContaining('[EIGENFLUX_AUTH_REQUIRED]'),
347
- deliver: true,
348
- idempotencyKey: expect.any(String),
349
- });
350
- expect(sendAgentMessageMock).not.toHaveBeenCalled();
351
-
352
- await services[0].stop();
353
- });
354
-
355
- test('registers one service per enabled server and injects server-specific skill paths', async () => {
356
- const eigenfluxWorkdir = path.join(workdir, 'eigenflux');
357
- const alphaWorkdir = path.join(workdir, 'alpha');
358
- fs.mkdirSync(eigenfluxWorkdir, { recursive: true });
359
- fs.mkdirSync(alphaWorkdir, { recursive: true });
360
- fs.writeFileSync(path.join(eigenfluxWorkdir, 'skill.md'), '# eigenflux local skill\n', 'utf-8');
361
-
362
- const { default: plugin } = await import('./index');
363
- const services: any[] = [];
364
-
365
- plugin.register({
366
- config: {},
367
- pluginConfig: {
368
- servers: [
369
- {
370
- name: 'eigenflux',
371
- workdir: eigenfluxWorkdir,
372
- },
373
- {
374
- name: 'alpha',
375
- endpoint: 'https://alpha.example.com',
376
- workdir: alphaWorkdir,
377
- },
378
- ],
379
- },
380
- runtime: {},
381
- logger: createLogger(),
382
- registerService: (service: any) => services.push(service),
383
- registerCommand: jest.fn(),
384
- registerHook: jest.fn(),
385
- on: jest.fn(),
386
- } as any);
387
-
388
- expect(services).toHaveLength(2);
389
-
390
- await services[0].start();
391
- await services[1].start();
392
-
393
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
394
- expect.stringContaining('network=eigenflux')
395
- );
396
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
397
- expect.stringContaining(`skill_file=${path.join(eigenfluxWorkdir, 'skill.md')}`)
398
- );
399
- expect(sendAgentMessageMock).toHaveBeenCalledWith(expect.stringContaining('network=alpha'));
400
- expect(sendAgentMessageMock).toHaveBeenCalledWith(
401
- expect.stringContaining('skill_file=https://alpha.example.com/skill.md')
402
- );
403
-
404
- await services[0].stop();
405
- await services[1].stop();
406
- });
407
-
408
- test('supports selecting a non-default server with --server', async () => {
409
- const eigenfluxWorkdir = path.join(workdir, 'eigenflux');
410
- const alphaWorkdir = path.join(workdir, 'alpha');
411
- fs.mkdirSync(eigenfluxWorkdir, { recursive: true });
412
- fs.mkdirSync(alphaWorkdir, { recursive: true });
413
- fs.writeFileSync(
414
- path.join(alphaWorkdir, 'credentials.json'),
415
- JSON.stringify({ access_token: 'at_alpha_token' }),
416
- 'utf-8'
417
- );
418
-
419
- const { default: plugin } = await import('./index');
420
- const commands: any[] = [];
421
- plugin.register({
422
- config: {},
423
- pluginConfig: {
424
- servers: [
425
- {
426
- name: 'eigenflux',
427
- workdir: eigenfluxWorkdir,
428
- },
429
- {
430
- name: 'alpha',
431
- endpoint: 'http://127.0.0.1:18080',
432
- workdir: alphaWorkdir,
433
- },
434
- ],
435
- },
436
- runtime: {},
437
- logger: createLogger(),
438
- registerService: jest.fn(),
439
- registerCommand: (command: any) => commands.push(command),
440
- registerHook: jest.fn(),
441
- on: jest.fn(),
442
- } as any);
443
-
444
- const authResp = await commands[0].handler({ args: '--server alpha auth' });
445
- expect(authResp.text).toContain('EigenFlux auth status (server=alpha):');
446
- expect(authResp.text).toContain(`workdir: ${alphaWorkdir}`);
447
- expect(authResp.text).toContain('status: available');
448
-
449
- const listResp = await commands[0].handler({ args: 'servers' });
450
- expect(listResp.text).toContain('EigenFlux servers:');
451
- expect(listResp.text).toContain('- eigenflux: enabled, default;');
452
- expect(listResp.text).toContain('- alpha: enabled;');
453
- });
454
- });