@clawswarm/bridge 0.1.0-alpha

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/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # @clawswarm/bridge
2
+
3
+ WebSocket bridge server for ClawSwarm — enables real-time communication between agents, UI clients, and external services.
4
+
5
+ ## Features
6
+
7
+ - 🔌 **WebSocket server** — persistent connections for agents and dashboard clients
8
+ - 🏢 **Org-scoped routing** — tasks and events are isolated per organization
9
+ - 📡 **Real-time events** — task updates, agent status, and goal progress streamed live
10
+ - 🔐 **Token-based auth** — per-connection authentication via Bearer tokens
11
+ - 🔁 **Reconnect handling** — automatic ping/pong and stale connection cleanup
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @clawswarm/bridge
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Standalone Server
22
+
23
+ ```typescript
24
+ import { BridgeServer } from '@clawswarm/bridge';
25
+
26
+ const bridge = new BridgeServer({ port: 8787, host: '0.0.0.0' });
27
+ await bridge.start();
28
+
29
+ bridge.on('client:connected', (client) => {
30
+ console.log('New client:', client.id, 'org:', client.orgId);
31
+ });
32
+ ```
33
+
34
+ ### With ClawSwarm Core
35
+
36
+ ```typescript
37
+ import { ClawSwarm, Agent } from '@clawswarm/core';
38
+ import { BridgeServer, TaskRouter } from '@clawswarm/bridge';
39
+
40
+ const bridge = new BridgeServer({ port: 8787 });
41
+ await bridge.start();
42
+
43
+ const router = new TaskRouter(bridge);
44
+
45
+ const swarm = new ClawSwarm({
46
+ agents: [Agent.research({ model: 'claude-sonnet-4' })],
47
+ bridgeUrl: 'ws://localhost:8787',
48
+ });
49
+
50
+ // Forward swarm events to bridge clients
51
+ swarm.on('task:completed', (task) => {
52
+ router.broadcast(task.goalId, { type: 'task:completed', payload: task });
53
+ });
54
+ ```
55
+
56
+ ## Configuration
57
+
58
+ | Option | Type | Default | Description |
59
+ |--------|------|---------|-------------|
60
+ | `port` | `number` | `8787` | Port to listen on |
61
+ | `host` | `string` | `'0.0.0.0'` | Host to bind to |
62
+ | `maxConnections` | `number` | `1000` | Max concurrent WebSocket connections |
63
+ | `pingIntervalMs` | `number` | `30000` | Ping interval for keep-alive |
64
+ | `authTokens` | `string[]` | `[]` | Allowed auth tokens (empty = no auth) |
65
+
66
+ ## License
67
+
68
+ MIT
@@ -0,0 +1,105 @@
1
+ /**
2
+ * WebSocket bridge server for ClawSwarm.
3
+ *
4
+ * Provides real-time bidirectional communication between agents,
5
+ * dashboard clients, and external consumers. Handles org-scoped
6
+ * message routing, authentication, and connection lifecycle.
7
+ *
8
+ * @module @clawswarm/bridge/bridge
9
+ */
10
+ import EventEmitter from 'eventemitter3';
11
+ import { BridgeClient, BridgeMessage, BridgeServerConfig, BridgeServerEvents } from './types.js';
12
+ declare const BridgeServer_base: new () => EventEmitter<BridgeServerEvents>;
13
+ /**
14
+ * The ClawSwarm bridge server.
15
+ *
16
+ * Manages WebSocket connections, authenticates clients, and routes
17
+ * messages between agents and dashboard consumers within org boundaries.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const bridge = new BridgeServer({ port: 8787 });
22
+ *
23
+ * bridge.on('client:connected', (client) => {
24
+ * console.log('Connected:', client.id, 'org:', client.orgId);
25
+ * });
26
+ *
27
+ * await bridge.start();
28
+ * console.log('Bridge listening on ws://localhost:8787');
29
+ * ```
30
+ */
31
+ export declare class BridgeServer extends BridgeServer_base {
32
+ private readonly config;
33
+ private wss;
34
+ private clients;
35
+ private pingTimer;
36
+ constructor(config?: BridgeServerConfig);
37
+ /**
38
+ * Start the WebSocket server.
39
+ * Returns a promise that resolves once the server is listening.
40
+ */
41
+ start(): Promise<void>;
42
+ /**
43
+ * Stop the WebSocket server and disconnect all clients.
44
+ */
45
+ stop(): Promise<void>;
46
+ /**
47
+ * Send a message to a specific client by ID.
48
+ * Returns false if the client is not found or not connected.
49
+ */
50
+ send(clientId: string, message: BridgeMessage): boolean;
51
+ /**
52
+ * Broadcast a message to all clients in an organization.
53
+ * Optionally filter by role.
54
+ *
55
+ * @param orgId - Organization to broadcast to ('*' for all orgs)
56
+ * @param message - Message to send
57
+ * @param roles - Optional role filter
58
+ * @returns Number of clients reached
59
+ */
60
+ broadcast(orgId: string, message: BridgeMessage, roles?: BridgeClient['role'][]): number;
61
+ /**
62
+ * Get all connected clients (optionally filtered by org).
63
+ */
64
+ getClients(orgId?: string): BridgeClient[];
65
+ /**
66
+ * Get server stats.
67
+ */
68
+ stats(): {
69
+ connections: number;
70
+ orgs: number;
71
+ uptime: boolean;
72
+ };
73
+ /**
74
+ * Handle a new WebSocket connection.
75
+ * @internal
76
+ */
77
+ private _onConnection;
78
+ /**
79
+ * Handle an incoming message from a client.
80
+ * @internal
81
+ */
82
+ private _onMessage;
83
+ /**
84
+ * Handle an auth message from a client.
85
+ * @internal
86
+ */
87
+ private _handleAuth;
88
+ /**
89
+ * Handle a client disconnection.
90
+ * @internal
91
+ */
92
+ private _onClose;
93
+ /**
94
+ * Build an error message.
95
+ * @internal
96
+ */
97
+ private _errorMessage;
98
+ /**
99
+ * Start the ping timer for keep-alive.
100
+ * @internal
101
+ */
102
+ private _startPingTimer;
103
+ }
104
+ export {};
105
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAEnB,MAAM,YAAY,CAAC;iCA6B+B,UAAU,YAAY,CAAC,kBAAkB,CAAC;AAlB7F;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAa,SAAQ,iBAA4D;IAC5F,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,OAAO,CAAuE;IACtF,OAAO,CAAC,SAAS,CAA+C;gBAEpD,MAAM,GAAE,kBAAuB;IAc3C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO;IAavD;;;;;;;;OAQG;IACH,SAAS,CACP,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,KAAK,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,GAC7B,MAAM;IAcT;;OAEG;IACH,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IAK1C;;OAEG;IACH,KAAK,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE;IAW/D;;;OAGG;IACH,OAAO,CAAC,aAAa;IA4BrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAiClB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA8BnB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAKhB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;IACH,OAAO,CAAC,eAAe;CAcxB"}
package/dist/bridge.js ADDED
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ /**
3
+ * WebSocket bridge server for ClawSwarm.
4
+ *
5
+ * Provides real-time bidirectional communication between agents,
6
+ * dashboard clients, and external consumers. Handles org-scoped
7
+ * message routing, authentication, and connection lifecycle.
8
+ *
9
+ * @module @clawswarm/bridge/bridge
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BridgeServer = void 0;
16
+ const ws_1 = require("ws");
17
+ const uuid_1 = require("uuid");
18
+ const eventemitter3_1 = __importDefault(require("eventemitter3"));
19
+ // ─── Defaults ─────────────────────────────────────────────────────────────────
20
+ const DEFAULT_PORT = 8787;
21
+ const DEFAULT_HOST = '0.0.0.0';
22
+ const DEFAULT_MAX_CONNECTIONS = 1000;
23
+ const DEFAULT_PING_INTERVAL_MS = 30_000;
24
+ // ─── BridgeServer ─────────────────────────────────────────────────────────────
25
+ /**
26
+ * The ClawSwarm bridge server.
27
+ *
28
+ * Manages WebSocket connections, authenticates clients, and routes
29
+ * messages between agents and dashboard consumers within org boundaries.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const bridge = new BridgeServer({ port: 8787 });
34
+ *
35
+ * bridge.on('client:connected', (client) => {
36
+ * console.log('Connected:', client.id, 'org:', client.orgId);
37
+ * });
38
+ *
39
+ * await bridge.start();
40
+ * console.log('Bridge listening on ws://localhost:8787');
41
+ * ```
42
+ */
43
+ class BridgeServer extends eventemitter3_1.default {
44
+ config;
45
+ wss = null;
46
+ clients = new Map();
47
+ pingTimer = null;
48
+ constructor(config = {}) {
49
+ super();
50
+ this.config = {
51
+ port: config.port ?? DEFAULT_PORT,
52
+ host: config.host ?? DEFAULT_HOST,
53
+ maxConnections: config.maxConnections ?? DEFAULT_MAX_CONNECTIONS,
54
+ pingIntervalMs: config.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
55
+ authTokens: config.authTokens ?? [],
56
+ path: config.path ?? '/',
57
+ };
58
+ }
59
+ // ─── Public API ───────────────────────────────────────────────────────────
60
+ /**
61
+ * Start the WebSocket server.
62
+ * Returns a promise that resolves once the server is listening.
63
+ */
64
+ async start() {
65
+ if (this.wss)
66
+ throw new Error('BridgeServer is already running');
67
+ return new Promise((resolve, reject) => {
68
+ this.wss = new ws_1.WebSocketServer({
69
+ port: this.config.port,
70
+ host: this.config.host,
71
+ path: this.config.path,
72
+ });
73
+ this.wss.on('connection', (socket, req) => this._onConnection(socket, req));
74
+ this.wss.on('error', (err) => {
75
+ this.emit('error', err);
76
+ reject(err);
77
+ });
78
+ this.wss.on('listening', () => {
79
+ this._startPingTimer();
80
+ this.emit('listening', this.config.port, this.config.host);
81
+ resolve();
82
+ });
83
+ });
84
+ }
85
+ /**
86
+ * Stop the WebSocket server and disconnect all clients.
87
+ */
88
+ async stop() {
89
+ if (!this.wss)
90
+ return;
91
+ if (this.pingTimer) {
92
+ clearInterval(this.pingTimer);
93
+ this.pingTimer = null;
94
+ }
95
+ // Close all client connections
96
+ for (const [id, { socket }] of this.clients) {
97
+ socket.close(1001, 'Server shutting down');
98
+ this.clients.delete(id);
99
+ }
100
+ return new Promise((resolve) => {
101
+ this.wss.close(() => {
102
+ this.wss = null;
103
+ resolve();
104
+ });
105
+ });
106
+ }
107
+ /**
108
+ * Send a message to a specific client by ID.
109
+ * Returns false if the client is not found or not connected.
110
+ */
111
+ send(clientId, message) {
112
+ const entry = this.clients.get(clientId);
113
+ if (!entry || entry.socket.readyState !== ws_1.WebSocket.OPEN)
114
+ return false;
115
+ try {
116
+ entry.socket.send(JSON.stringify(message));
117
+ this.emit('message:sent', clientId, message);
118
+ return true;
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
124
+ /**
125
+ * Broadcast a message to all clients in an organization.
126
+ * Optionally filter by role.
127
+ *
128
+ * @param orgId - Organization to broadcast to ('*' for all orgs)
129
+ * @param message - Message to send
130
+ * @param roles - Optional role filter
131
+ * @returns Number of clients reached
132
+ */
133
+ broadcast(orgId, message, roles) {
134
+ let count = 0;
135
+ for (const [, { client, socket }] of this.clients) {
136
+ if (socket.readyState !== ws_1.WebSocket.OPEN)
137
+ continue;
138
+ if (orgId !== '*' && client.orgId !== orgId)
139
+ continue;
140
+ if (roles && !roles.includes(client.role))
141
+ continue;
142
+ if (!client.authenticated)
143
+ continue;
144
+ socket.send(JSON.stringify(message));
145
+ count++;
146
+ }
147
+ return count;
148
+ }
149
+ /**
150
+ * Get all connected clients (optionally filtered by org).
151
+ */
152
+ getClients(orgId) {
153
+ const all = Array.from(this.clients.values()).map(e => e.client);
154
+ return orgId ? all.filter(c => c.orgId === orgId) : all;
155
+ }
156
+ /**
157
+ * Get server stats.
158
+ */
159
+ stats() {
160
+ const orgs = new Set(Array.from(this.clients.values()).map(e => e.client.orgId));
161
+ return {
162
+ connections: this.clients.size,
163
+ orgs: orgs.size,
164
+ uptime: this.wss !== null,
165
+ };
166
+ }
167
+ // ─── Private ──────────────────────────────────────────────────────────────
168
+ /**
169
+ * Handle a new WebSocket connection.
170
+ * @internal
171
+ */
172
+ _onConnection(socket, _req) {
173
+ if (this.clients.size >= this.config.maxConnections) {
174
+ socket.close(1013, 'Server at capacity');
175
+ return;
176
+ }
177
+ const clientId = (0, uuid_1.v4)();
178
+ const client = {
179
+ id: clientId,
180
+ orgId: 'unknown',
181
+ role: 'external',
182
+ connectedAt: new Date().toISOString(),
183
+ authenticated: this.config.authTokens.length === 0, // no auth if no tokens configured
184
+ metadata: {},
185
+ };
186
+ this.clients.set(clientId, { client, socket });
187
+ this.emit('client:connected', client);
188
+ socket.on('message', (data) => this._onMessage(clientId, data));
189
+ socket.on('close', (code, reason) => this._onClose(clientId, code, reason.toString()));
190
+ socket.on('error', (err) => this.emit('error', err));
191
+ socket.on('pong', () => {
192
+ const entry = this.clients.get(clientId);
193
+ if (entry)
194
+ entry.client.lastPingAt = new Date().toISOString();
195
+ });
196
+ }
197
+ /**
198
+ * Handle an incoming message from a client.
199
+ * @internal
200
+ */
201
+ _onMessage(clientId, data) {
202
+ const entry = this.clients.get(clientId);
203
+ if (!entry)
204
+ return;
205
+ let message;
206
+ try {
207
+ message = JSON.parse(data.toString());
208
+ }
209
+ catch {
210
+ this.send(clientId, this._errorMessage('PARSE_ERROR', 'Invalid JSON'));
211
+ return;
212
+ }
213
+ // Handle auth message
214
+ if (message.type === 'auth') {
215
+ this._handleAuth(clientId, message.payload);
216
+ return;
217
+ }
218
+ // Reject unauthenticated messages
219
+ if (!entry.client.authenticated) {
220
+ this.send(clientId, this._errorMessage('UNAUTHORIZED', 'Authenticate first'));
221
+ return;
222
+ }
223
+ // Handle ping
224
+ if (message.type === 'ping') {
225
+ this.send(clientId, { type: 'pong', ts: new Date().toISOString(), payload: {} });
226
+ return;
227
+ }
228
+ this.emit('message:received', entry.client, message);
229
+ }
230
+ /**
231
+ * Handle an auth message from a client.
232
+ * @internal
233
+ */
234
+ _handleAuth(clientId, payload) {
235
+ const entry = this.clients.get(clientId);
236
+ if (!entry)
237
+ return;
238
+ const { token, orgId, role, metadata } = payload;
239
+ // Validate token if auth is configured
240
+ if (this.config.authTokens.length > 0 &&
241
+ !this.config.authTokens.includes(token)) {
242
+ this.send(clientId, this._errorMessage('INVALID_TOKEN', 'Invalid auth token'));
243
+ entry.socket.close(1008, 'Unauthorized');
244
+ return;
245
+ }
246
+ entry.client.authenticated = true;
247
+ entry.client.orgId = orgId;
248
+ entry.client.role = role;
249
+ entry.client.metadata = metadata ?? {};
250
+ this.send(clientId, {
251
+ type: 'pong', // using pong as ack
252
+ ts: new Date().toISOString(),
253
+ payload: { authenticated: true, clientId },
254
+ });
255
+ this.emit('client:authenticated', entry.client);
256
+ }
257
+ /**
258
+ * Handle a client disconnection.
259
+ * @internal
260
+ */
261
+ _onClose(clientId, code, reason) {
262
+ this.clients.delete(clientId);
263
+ this.emit('client:disconnected', clientId, reason || String(code));
264
+ }
265
+ /**
266
+ * Build an error message.
267
+ * @internal
268
+ */
269
+ _errorMessage(code, message) {
270
+ return {
271
+ type: 'error',
272
+ ts: new Date().toISOString(),
273
+ payload: { code, message },
274
+ };
275
+ }
276
+ /**
277
+ * Start the ping timer for keep-alive.
278
+ * @internal
279
+ */
280
+ _startPingTimer() {
281
+ this.pingTimer = setInterval(() => {
282
+ const now = new Date().toISOString();
283
+ for (const [id, { socket, client }] of this.clients) {
284
+ if (socket.readyState === ws_1.WebSocket.OPEN) {
285
+ socket.ping();
286
+ client.lastPingAt = now;
287
+ }
288
+ else {
289
+ // Clean up stale connections
290
+ this.clients.delete(id);
291
+ }
292
+ }
293
+ }, this.config.pingIntervalMs);
294
+ }
295
+ }
296
+ exports.BridgeServer = BridgeServer;
297
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @clawswarm/bridge — Public API
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { BridgeServer, TaskRouter } from '@clawswarm/bridge';
7
+ * ```
8
+ *
9
+ * @module @clawswarm/bridge
10
+ */
11
+ export { BridgeServer } from './bridge.js';
12
+ export { TaskRouter } from './router.js';
13
+ export type { BridgeClient, ClientRole, BridgeMessage, BridgeMessageType, BridgeServerConfig, BridgeServerEvents, AuthPayload, ErrorPayload, AgentStatusPayload, RoutingRule, } from './types.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,YAAY,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * @clawswarm/bridge — Public API
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { BridgeServer, TaskRouter } from '@clawswarm/bridge';
8
+ * ```
9
+ *
10
+ * @module @clawswarm/bridge
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.TaskRouter = exports.BridgeServer = void 0;
14
+ var bridge_js_1 = require("./bridge.js");
15
+ Object.defineProperty(exports, "BridgeServer", { enumerable: true, get: function () { return bridge_js_1.BridgeServer; } });
16
+ var router_js_1 = require("./router.js");
17
+ Object.defineProperty(exports, "TaskRouter", { enumerable: true, get: function () { return router_js_1.TaskRouter; } });
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,yCAA2C;AAAlC,yGAAA,YAAY,OAAA;AACrB,yCAAyC;AAAhC,uGAAA,UAAU,OAAA"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Org-scoped task router for the ClawSwarm bridge.
3
+ *
4
+ * Routes messages from ClawSwarm events to the appropriate
5
+ * WebSocket clients based on organization ID and client role.
6
+ *
7
+ * @module @clawswarm/bridge/router
8
+ */
9
+ import { BridgeServer } from './bridge.js';
10
+ import { BridgeMessage, BridgeMessageType, ClientRole } from './types.js';
11
+ /**
12
+ * Routes ClawSwarm events to connected bridge clients.
13
+ *
14
+ * Provides a high-level API for broadcasting goal/task events
15
+ * to the right clients within an organization.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const bridge = new BridgeServer({ port: 8787 });
20
+ * await bridge.start();
21
+ *
22
+ * const router = new TaskRouter(bridge);
23
+ *
24
+ * // Connect a ClawSwarm instance to the router
25
+ * swarm.on('task:completed', (task) => {
26
+ * router.routeTaskEvent('task:completed', task.goalId, task, 'my-org-id');
27
+ * });
28
+ * ```
29
+ */
30
+ export declare class TaskRouter {
31
+ private readonly bridge;
32
+ private readonly rules;
33
+ constructor(bridge: BridgeServer);
34
+ /**
35
+ * Route a task-related event to all dashboard clients in the org.
36
+ *
37
+ * @param type - Event type
38
+ * @param goalId - Goal ID for context
39
+ * @param payload - Event payload
40
+ * @param orgId - Organization to route to
41
+ */
42
+ routeTaskEvent(type: BridgeMessageType, goalId: string, payload: unknown, orgId: string): number;
43
+ /**
44
+ * Route a goal-related event.
45
+ *
46
+ * @param type - Event type
47
+ * @param payload - Event payload (the Goal object)
48
+ * @param orgId - Organization to route to
49
+ */
50
+ routeGoalEvent(type: BridgeMessageType, payload: unknown, orgId: string): number;
51
+ /**
52
+ * Route an agent status update to dashboard clients.
53
+ */
54
+ routeAgentStatus(agentId: string, agentType: string, status: 'idle' | 'busy' | 'error' | 'offline', orgId: string, currentTaskId?: string): number;
55
+ /**
56
+ * Broadcast a raw message to all clients in an org.
57
+ */
58
+ broadcast(orgId: string, message: BridgeMessage, roles?: ClientRole[]): number;
59
+ /**
60
+ * Add a routing rule for custom message handling.
61
+ * Rules are evaluated before default routing.
62
+ *
63
+ * @param id - Unique rule identifier
64
+ * @param rule - Route rule definition
65
+ */
66
+ addRule(id: string, rule: RouteRule): void;
67
+ /**
68
+ * Remove a routing rule.
69
+ */
70
+ removeRule(id: string): boolean;
71
+ /**
72
+ * Route a message through all matching rules.
73
+ * Returns the number of clients that received the message.
74
+ */
75
+ route(orgId: string, message: BridgeMessage): number;
76
+ /**
77
+ * Build a typed bridge message.
78
+ * @internal
79
+ */
80
+ private _buildMessage;
81
+ }
82
+ interface RouteRule {
83
+ messageType: BridgeMessageType | '*';
84
+ orgIds?: string[];
85
+ roles?: ClientRole[];
86
+ }
87
+ export {};
88
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI1E;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;gBAE/C,MAAM,EAAE,YAAY;IAMhC;;;;;;;OAOG;IACH,cAAc,CACZ,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACZ,MAAM;IAQT;;;;;;OAMG;IACH,cAAc,CACZ,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACZ,MAAM;IAQT;;OAEG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,EAC7C,KAAK,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM;IAQT;;OAEG;IACH,SAAS,CACP,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,EACtB,KAAK,CAAC,EAAE,UAAU,EAAE,GACnB,MAAM;IAIT;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAI1C;;OAEG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM;IAoBpD;;;OAGG;IACH,OAAO,CAAC,aAAa;CAYtB;AAID,UAAU,SAAS;IACjB,WAAW,EAAE,iBAAiB,GAAG,GAAG,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;CACtB"}