@omnixal/openclaw-nats-plugin 0.2.16 → 0.2.17

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.
@@ -0,0 +1,514 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "0bd82560-d525-4af4-8c7d-930289e0499c",
5
+ "prevId": "e071c2ec-330f-4444-bc0d-1a840e0566a8",
6
+ "tables": {
7
+ "cron_jobs": {
8
+ "name": "cron_jobs",
9
+ "columns": {
10
+ "id": {
11
+ "name": "id",
12
+ "type": "text",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": false
16
+ },
17
+ "name": {
18
+ "name": "name",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ },
24
+ "expr": {
25
+ "name": "expr",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false
30
+ },
31
+ "subject": {
32
+ "name": "subject",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": true,
36
+ "autoincrement": false
37
+ },
38
+ "payload": {
39
+ "name": "payload",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": false,
43
+ "autoincrement": false
44
+ },
45
+ "timezone": {
46
+ "name": "timezone",
47
+ "type": "text",
48
+ "primaryKey": false,
49
+ "notNull": true,
50
+ "autoincrement": false,
51
+ "default": "'UTC'"
52
+ },
53
+ "enabled": {
54
+ "name": "enabled",
55
+ "type": "integer",
56
+ "primaryKey": false,
57
+ "notNull": true,
58
+ "autoincrement": false,
59
+ "default": true
60
+ },
61
+ "last_run_at": {
62
+ "name": "last_run_at",
63
+ "type": "integer",
64
+ "primaryKey": false,
65
+ "notNull": false,
66
+ "autoincrement": false
67
+ },
68
+ "created_at": {
69
+ "name": "created_at",
70
+ "type": "integer",
71
+ "primaryKey": false,
72
+ "notNull": true,
73
+ "autoincrement": false
74
+ }
75
+ },
76
+ "indexes": {
77
+ "cron_jobs_name_unique": {
78
+ "name": "cron_jobs_name_unique",
79
+ "columns": [
80
+ "name"
81
+ ],
82
+ "isUnique": true
83
+ },
84
+ "cron_jobs_name_idx": {
85
+ "name": "cron_jobs_name_idx",
86
+ "columns": [
87
+ "name"
88
+ ],
89
+ "isUnique": false
90
+ }
91
+ },
92
+ "foreignKeys": {},
93
+ "compositePrimaryKeys": {},
94
+ "uniqueConstraints": {},
95
+ "checkConstraints": {}
96
+ },
97
+ "dedup_events": {
98
+ "name": "dedup_events",
99
+ "columns": {
100
+ "event_id": {
101
+ "name": "event_id",
102
+ "type": "text",
103
+ "primaryKey": true,
104
+ "notNull": true,
105
+ "autoincrement": false
106
+ },
107
+ "subject": {
108
+ "name": "subject",
109
+ "type": "text",
110
+ "primaryKey": false,
111
+ "notNull": true,
112
+ "autoincrement": false
113
+ },
114
+ "seen_at": {
115
+ "name": "seen_at",
116
+ "type": "integer",
117
+ "primaryKey": false,
118
+ "notNull": true,
119
+ "autoincrement": false
120
+ }
121
+ },
122
+ "indexes": {
123
+ "dedup_events_seen_at_idx": {
124
+ "name": "dedup_events_seen_at_idx",
125
+ "columns": [
126
+ "seen_at"
127
+ ],
128
+ "isUnique": false
129
+ }
130
+ },
131
+ "foreignKeys": {},
132
+ "compositePrimaryKeys": {},
133
+ "uniqueConstraints": {},
134
+ "checkConstraints": {}
135
+ },
136
+ "event_routes": {
137
+ "name": "event_routes",
138
+ "columns": {
139
+ "id": {
140
+ "name": "id",
141
+ "type": "text",
142
+ "primaryKey": true,
143
+ "notNull": true,
144
+ "autoincrement": false
145
+ },
146
+ "name": {
147
+ "name": "name",
148
+ "type": "text",
149
+ "primaryKey": false,
150
+ "notNull": true,
151
+ "autoincrement": false
152
+ },
153
+ "pattern": {
154
+ "name": "pattern",
155
+ "type": "text",
156
+ "primaryKey": false,
157
+ "notNull": true,
158
+ "autoincrement": false
159
+ },
160
+ "target": {
161
+ "name": "target",
162
+ "type": "text",
163
+ "primaryKey": false,
164
+ "notNull": true,
165
+ "autoincrement": false,
166
+ "default": "'main'"
167
+ },
168
+ "enabled": {
169
+ "name": "enabled",
170
+ "type": "integer",
171
+ "primaryKey": false,
172
+ "notNull": true,
173
+ "autoincrement": false,
174
+ "default": true
175
+ },
176
+ "priority": {
177
+ "name": "priority",
178
+ "type": "integer",
179
+ "primaryKey": false,
180
+ "notNull": true,
181
+ "autoincrement": false,
182
+ "default": 5
183
+ },
184
+ "filter": {
185
+ "name": "filter",
186
+ "type": "text",
187
+ "primaryKey": false,
188
+ "notNull": false,
189
+ "autoincrement": false
190
+ },
191
+ "filter_drop_count": {
192
+ "name": "filter_drop_count",
193
+ "type": "integer",
194
+ "primaryKey": false,
195
+ "notNull": true,
196
+ "autoincrement": false,
197
+ "default": 0
198
+ },
199
+ "created_at": {
200
+ "name": "created_at",
201
+ "type": "integer",
202
+ "primaryKey": false,
203
+ "notNull": true,
204
+ "autoincrement": false
205
+ },
206
+ "last_delivered_at": {
207
+ "name": "last_delivered_at",
208
+ "type": "integer",
209
+ "primaryKey": false,
210
+ "notNull": false,
211
+ "autoincrement": false
212
+ },
213
+ "last_event_subject": {
214
+ "name": "last_event_subject",
215
+ "type": "text",
216
+ "primaryKey": false,
217
+ "notNull": false,
218
+ "autoincrement": false
219
+ },
220
+ "delivery_count": {
221
+ "name": "delivery_count",
222
+ "type": "integer",
223
+ "primaryKey": false,
224
+ "notNull": true,
225
+ "autoincrement": false,
226
+ "default": 0
227
+ },
228
+ "last_delivery_lag_ms": {
229
+ "name": "last_delivery_lag_ms",
230
+ "type": "integer",
231
+ "primaryKey": false,
232
+ "notNull": false,
233
+ "autoincrement": false
234
+ },
235
+ "custom_payload": {
236
+ "name": "custom_payload",
237
+ "type": "text",
238
+ "primaryKey": false,
239
+ "notNull": false,
240
+ "autoincrement": false
241
+ }
242
+ },
243
+ "indexes": {
244
+ "event_routes_name_idx": {
245
+ "name": "event_routes_name_idx",
246
+ "columns": [
247
+ "name"
248
+ ],
249
+ "isUnique": true
250
+ },
251
+ "event_routes_pattern_idx": {
252
+ "name": "event_routes_pattern_idx",
253
+ "columns": [
254
+ "pattern"
255
+ ],
256
+ "isUnique": false
257
+ },
258
+ "event_routes_target_idx": {
259
+ "name": "event_routes_target_idx",
260
+ "columns": [
261
+ "target"
262
+ ],
263
+ "isUnique": false
264
+ }
265
+ },
266
+ "foreignKeys": {},
267
+ "compositePrimaryKeys": {},
268
+ "uniqueConstraints": {},
269
+ "checkConstraints": {}
270
+ },
271
+ "execution_logs": {
272
+ "name": "execution_logs",
273
+ "columns": {
274
+ "id": {
275
+ "name": "id",
276
+ "type": "text",
277
+ "primaryKey": true,
278
+ "notNull": true,
279
+ "autoincrement": false
280
+ },
281
+ "entity_type": {
282
+ "name": "entity_type",
283
+ "type": "text",
284
+ "primaryKey": false,
285
+ "notNull": true,
286
+ "autoincrement": false
287
+ },
288
+ "entity_id": {
289
+ "name": "entity_id",
290
+ "type": "text",
291
+ "primaryKey": false,
292
+ "notNull": true,
293
+ "autoincrement": false
294
+ },
295
+ "action": {
296
+ "name": "action",
297
+ "type": "text",
298
+ "primaryKey": false,
299
+ "notNull": true,
300
+ "autoincrement": false
301
+ },
302
+ "subject": {
303
+ "name": "subject",
304
+ "type": "text",
305
+ "primaryKey": false,
306
+ "notNull": true,
307
+ "autoincrement": false
308
+ },
309
+ "detail": {
310
+ "name": "detail",
311
+ "type": "text",
312
+ "primaryKey": false,
313
+ "notNull": false,
314
+ "autoincrement": false
315
+ },
316
+ "success": {
317
+ "name": "success",
318
+ "type": "integer",
319
+ "primaryKey": false,
320
+ "notNull": true,
321
+ "autoincrement": false,
322
+ "default": true
323
+ },
324
+ "created_at": {
325
+ "name": "created_at",
326
+ "type": "integer",
327
+ "primaryKey": false,
328
+ "notNull": true,
329
+ "autoincrement": false
330
+ }
331
+ },
332
+ "indexes": {
333
+ "execution_logs_entity_idx": {
334
+ "name": "execution_logs_entity_idx",
335
+ "columns": [
336
+ "entity_type",
337
+ "entity_id"
338
+ ],
339
+ "isUnique": false
340
+ },
341
+ "execution_logs_created_at_idx": {
342
+ "name": "execution_logs_created_at_idx",
343
+ "columns": [
344
+ "created_at"
345
+ ],
346
+ "isUnique": false
347
+ }
348
+ },
349
+ "foreignKeys": {},
350
+ "compositePrimaryKeys": {},
351
+ "uniqueConstraints": {},
352
+ "checkConstraints": {}
353
+ },
354
+ "pending_events": {
355
+ "name": "pending_events",
356
+ "columns": {
357
+ "id": {
358
+ "name": "id",
359
+ "type": "text",
360
+ "primaryKey": true,
361
+ "notNull": true,
362
+ "autoincrement": false
363
+ },
364
+ "session_key": {
365
+ "name": "session_key",
366
+ "type": "text",
367
+ "primaryKey": false,
368
+ "notNull": true,
369
+ "autoincrement": false
370
+ },
371
+ "subject": {
372
+ "name": "subject",
373
+ "type": "text",
374
+ "primaryKey": false,
375
+ "notNull": true,
376
+ "autoincrement": false
377
+ },
378
+ "payload": {
379
+ "name": "payload",
380
+ "type": "text",
381
+ "primaryKey": false,
382
+ "notNull": false,
383
+ "autoincrement": false
384
+ },
385
+ "priority": {
386
+ "name": "priority",
387
+ "type": "integer",
388
+ "primaryKey": false,
389
+ "notNull": true,
390
+ "autoincrement": false,
391
+ "default": 5
392
+ },
393
+ "created_at": {
394
+ "name": "created_at",
395
+ "type": "integer",
396
+ "primaryKey": false,
397
+ "notNull": true,
398
+ "autoincrement": false
399
+ },
400
+ "delivered_at": {
401
+ "name": "delivered_at",
402
+ "type": "integer",
403
+ "primaryKey": false,
404
+ "notNull": false,
405
+ "autoincrement": false
406
+ }
407
+ },
408
+ "indexes": {},
409
+ "foreignKeys": {},
410
+ "compositePrimaryKeys": {},
411
+ "uniqueConstraints": {},
412
+ "checkConstraints": {}
413
+ },
414
+ "timer_jobs": {
415
+ "name": "timer_jobs",
416
+ "columns": {
417
+ "id": {
418
+ "name": "id",
419
+ "type": "text",
420
+ "primaryKey": true,
421
+ "notNull": true,
422
+ "autoincrement": false
423
+ },
424
+ "name": {
425
+ "name": "name",
426
+ "type": "text",
427
+ "primaryKey": false,
428
+ "notNull": true,
429
+ "autoincrement": false
430
+ },
431
+ "subject": {
432
+ "name": "subject",
433
+ "type": "text",
434
+ "primaryKey": false,
435
+ "notNull": true,
436
+ "autoincrement": false
437
+ },
438
+ "payload": {
439
+ "name": "payload",
440
+ "type": "text",
441
+ "primaryKey": false,
442
+ "notNull": false,
443
+ "autoincrement": false
444
+ },
445
+ "delay_ms": {
446
+ "name": "delay_ms",
447
+ "type": "integer",
448
+ "primaryKey": false,
449
+ "notNull": true,
450
+ "autoincrement": false
451
+ },
452
+ "fire_at": {
453
+ "name": "fire_at",
454
+ "type": "integer",
455
+ "primaryKey": false,
456
+ "notNull": true,
457
+ "autoincrement": false
458
+ },
459
+ "fired": {
460
+ "name": "fired",
461
+ "type": "integer",
462
+ "primaryKey": false,
463
+ "notNull": true,
464
+ "autoincrement": false,
465
+ "default": false
466
+ },
467
+ "created_at": {
468
+ "name": "created_at",
469
+ "type": "integer",
470
+ "primaryKey": false,
471
+ "notNull": true,
472
+ "autoincrement": false
473
+ }
474
+ },
475
+ "indexes": {
476
+ "timer_jobs_name_unique": {
477
+ "name": "timer_jobs_name_unique",
478
+ "columns": [
479
+ "name"
480
+ ],
481
+ "isUnique": true
482
+ },
483
+ "timer_jobs_name_idx": {
484
+ "name": "timer_jobs_name_idx",
485
+ "columns": [
486
+ "name"
487
+ ],
488
+ "isUnique": false
489
+ },
490
+ "timer_jobs_fire_at_idx": {
491
+ "name": "timer_jobs_fire_at_idx",
492
+ "columns": [
493
+ "fire_at"
494
+ ],
495
+ "isUnique": false
496
+ }
497
+ },
498
+ "foreignKeys": {},
499
+ "compositePrimaryKeys": {},
500
+ "uniqueConstraints": {},
501
+ "checkConstraints": {}
502
+ }
503
+ },
504
+ "views": {},
505
+ "enums": {},
506
+ "_meta": {
507
+ "schemas": {},
508
+ "tables": {},
509
+ "columns": {}
510
+ },
511
+ "internal": {
512
+ "indexes": {}
513
+ }
514
+ }
@@ -57,6 +57,20 @@
57
57
  "when": 1774293614203,
58
58
  "tag": "0007_dizzy_komodo",
59
59
  "breakpoints": true
60
+ },
61
+ {
62
+ "idx": 8,
63
+ "version": "6",
64
+ "when": 1774530462833,
65
+ "tag": "0008_fluffy_pestilence",
66
+ "breakpoints": true
67
+ },
68
+ {
69
+ "idx": 9,
70
+ "version": "6",
71
+ "when": 1774535149039,
72
+ "tag": "0009_sour_romulus",
73
+ "breakpoints": true
60
74
  }
61
75
  ]
62
76
  }
@@ -1,4 +1,5 @@
1
- import { sqliteTable, text, integer, index } from '@onebun/drizzle/sqlite';
1
+ import { sqliteTable, text, integer, index, uniqueIndex } from '@onebun/drizzle/sqlite';
2
+ import type { FilterExpression } from '../route-filter/filter-expression';
2
3
 
3
4
  export const dedupEvents = sqliteTable('dedup_events', {
4
5
  eventId: text('event_id').primaryKey(),
@@ -23,16 +24,21 @@ export type NewPendingEvent = typeof pendingEvents.$inferInsert;
23
24
 
24
25
  export const eventRoutes = sqliteTable('event_routes', {
25
26
  id: text('id').primaryKey(),
26
- pattern: text('pattern').notNull().unique(),
27
+ name: text('name').notNull(),
28
+ pattern: text('pattern').notNull(),
27
29
  target: text('target').notNull().default('main'),
28
30
  enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
29
31
  priority: integer('priority').notNull().default(5),
32
+ filter: text('filter', { mode: 'json' }).$type<FilterExpression | null>(),
33
+ filterDropCount: integer('filter_drop_count').notNull().default(0),
30
34
  createdAt: integer('created_at', { mode: 'timestamp_ms' }).notNull(),
31
35
  lastDeliveredAt: integer('last_delivered_at', { mode: 'timestamp_ms' }),
32
36
  lastEventSubject: text('last_event_subject'),
33
37
  deliveryCount: integer('delivery_count').notNull().default(0),
34
38
  lastDeliveryLagMs: integer('last_delivery_lag_ms'),
39
+ customPayload: text('custom_payload', { mode: 'json' }).$type<unknown>(),
35
40
  }, (table) => [
41
+ uniqueIndex('event_routes_name_idx').on(table.name),
36
42
  index('event_routes_pattern_idx').on(table.pattern),
37
43
  index('event_routes_target_idx').on(table.target),
38
44
  ]);
@@ -0,0 +1,10 @@
1
+ export interface FilterCondition {
2
+ field: string; // dot-path: "amount", "order.status", "tags.0"
3
+ op: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'exists';
4
+ value: unknown;
5
+ }
6
+
7
+ export interface FilterExpression {
8
+ logic: 'and' | 'or'; // default 'and'
9
+ conditions: FilterCondition[];
10
+ }
@@ -0,0 +1,8 @@
1
+ import { Module } from '@onebun/core';
2
+ import { RouteFilterService } from './route-filter.service';
3
+
4
+ @Module({
5
+ services: [RouteFilterService],
6
+ exports: [RouteFilterService],
7
+ })
8
+ export class RouteFilterModule {}
@@ -0,0 +1,61 @@
1
+ import { Service, BaseService } from '@onebun/core';
2
+ import type { FilterExpression, FilterCondition } from './filter-expression';
3
+
4
+ @Service()
5
+ export class RouteFilterService extends BaseService {
6
+ evaluate(payload: unknown, filter: FilterExpression | null): boolean {
7
+ if (!filter || !filter.conditions || filter.conditions.length === 0) return true;
8
+ const results = filter.conditions.map(c => this.evalCondition(payload, c));
9
+ return filter.logic === 'or' ? results.some(Boolean) : results.every(Boolean);
10
+ }
11
+
12
+ private resolveField(obj: unknown, path: string): { found: boolean; value: unknown } {
13
+ const parts = path.split('.');
14
+ let current: unknown = obj;
15
+ for (const part of parts) {
16
+ if (current === null || current === undefined) return { found: false, value: undefined };
17
+ if (typeof current === 'object') {
18
+ if (!(part in (current as Record<string, unknown>))) return { found: false, value: undefined };
19
+ current = (current as Record<string, unknown>)[part];
20
+ } else {
21
+ return { found: false, value: undefined };
22
+ }
23
+ }
24
+ return { found: true, value: current };
25
+ }
26
+
27
+ private evalCondition(payload: unknown, cond: FilterCondition): boolean {
28
+ const { found, value } = this.resolveField(payload, cond.field);
29
+
30
+ if (cond.op === 'exists') {
31
+ return cond.value ? found : !found;
32
+ }
33
+
34
+ if (!found) return false;
35
+
36
+ switch (cond.op) {
37
+ case 'eq':
38
+ return value === cond.value;
39
+ case 'neq':
40
+ return value !== cond.value;
41
+ case 'gt':
42
+ return typeof value === 'number' && typeof cond.value === 'number' && value > cond.value;
43
+ case 'gte':
44
+ return typeof value === 'number' && typeof cond.value === 'number' && value >= cond.value;
45
+ case 'lt':
46
+ return typeof value === 'number' && typeof cond.value === 'number' && value < cond.value;
47
+ case 'lte':
48
+ return typeof value === 'number' && typeof cond.value === 'number' && value <= cond.value;
49
+ case 'in':
50
+ return Array.isArray(cond.value) && cond.value.includes(value);
51
+ case 'nin':
52
+ return Array.isArray(cond.value) && !cond.value.includes(value);
53
+ case 'contains':
54
+ if (typeof value === 'string' && typeof cond.value === 'string') return value.includes(cond.value);
55
+ if (Array.isArray(value)) return value.includes(cond.value);
56
+ return false;
57
+ default:
58
+ return false;
59
+ }
60
+ }
61
+ }
@@ -37,10 +37,14 @@ export class RouterController extends BaseController {
37
37
  const routes = await this.routerService.listRoutes();
38
38
  const result = routes.map(r => ({
39
39
  id: r.id,
40
+ name: r.name,
40
41
  pattern: r.pattern,
41
42
  target: r.target,
42
43
  priority: r.priority,
43
44
  enabled: r.enabled,
45
+ filter: r.filter ?? null,
46
+ filterDropCount: r.filterDropCount ?? 0,
47
+ customPayload: r.customPayload ?? null,
44
48
  lastDeliveredAt: r.lastDeliveredAt?.toISOString() ?? null,
45
49
  lastEventSubject: r.lastEventSubject ?? null,
46
50
  deliveryCount: r.deliveryCount ?? 0,
@@ -66,10 +70,14 @@ export class RouterController extends BaseController {
66
70
  if (!isValidAgentSubject(body.pattern)) {
67
71
  return this.error('pattern must start with "agent.events." followed by at least one token and must not end with "."', 400, 400);
68
72
  }
73
+ const name = body.name ?? body.pattern;
69
74
  const { route, created } = await this.routerService.subscribe(
75
+ name,
70
76
  body.pattern,
71
77
  body.target ?? 'main',
72
78
  body.priority ?? 5,
79
+ body.filter ?? null,
80
+ body.payload,
73
81
  );
74
82
  return this.success({ ...route, created });
75
83
  }
@@ -79,7 +87,12 @@ export class RouterController extends BaseController {
79
87
  @Param('id') id: string,
80
88
  @Body(updateRouteBodySchema) body: UpdateRouteBody,
81
89
  ): Promise<OneBunResponse> {
82
- const updated = await this.routerService.updateById(id, body);
90
+ const { payload: customPayload, filter, ...rest } = body;
91
+ const updated = await this.routerService.updateById(id, {
92
+ ...rest,
93
+ ...(customPayload !== undefined ? { customPayload } : {}),
94
+ ...(filter !== undefined ? { filter: filter as any } : {}),
95
+ });
83
96
  if (!updated) {
84
97
  return this.error('Route not found', 404, 404);
85
98
  }