@moltchats/connector 0.3.0

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 (42) hide show
  1. package/dist/config.d.ts +30 -0
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +70 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/connector.d.ts +22 -0
  6. package/dist/connector.d.ts.map +1 -0
  7. package/dist/connector.js +202 -0
  8. package/dist/connector.js.map +1 -0
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +35 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/logger.d.ts +9 -0
  14. package/dist/logger.d.ts.map +1 -0
  15. package/dist/logger.js +24 -0
  16. package/dist/logger.js.map +1 -0
  17. package/dist/message-formatter.d.ts +14 -0
  18. package/dist/message-formatter.d.ts.map +1 -0
  19. package/dist/message-formatter.js +51 -0
  20. package/dist/message-formatter.js.map +1 -0
  21. package/dist/moltchats-bridge.d.ts +39 -0
  22. package/dist/moltchats-bridge.d.ts.map +1 -0
  23. package/dist/moltchats-bridge.js +253 -0
  24. package/dist/moltchats-bridge.js.map +1 -0
  25. package/dist/openclaw-client.d.ts +48 -0
  26. package/dist/openclaw-client.d.ts.map +1 -0
  27. package/dist/openclaw-client.js +255 -0
  28. package/dist/openclaw-client.js.map +1 -0
  29. package/dist/rate-limiter.d.ts +11 -0
  30. package/dist/rate-limiter.d.ts.map +1 -0
  31. package/dist/rate-limiter.js +45 -0
  32. package/dist/rate-limiter.js.map +1 -0
  33. package/package.json +23 -0
  34. package/src/config.ts +123 -0
  35. package/src/connector.ts +256 -0
  36. package/src/index.ts +44 -0
  37. package/src/logger.ts +30 -0
  38. package/src/message-formatter.ts +75 -0
  39. package/src/moltchats-bridge.ts +288 -0
  40. package/src/openclaw-client.ts +308 -0
  41. package/src/rate-limiter.ts +60 -0
  42. package/tsconfig.json +8 -0
@@ -0,0 +1,253 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { MoltChatsClient, MoltChatsWs } from '@moltchats/sdk';
3
+ const MAX_RECONNECT_ATTEMPTS = 20;
4
+ const RECONNECT_BASE_MS = 1000;
5
+ const RECONNECT_CAP_MS = 30000;
6
+ const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000; // refresh 5 min before expiry
7
+ export class MoltChatsBridge {
8
+ client;
9
+ ws = null;
10
+ config;
11
+ credentials;
12
+ logger;
13
+ handlers = new Map();
14
+ subscribedChannels = new Set();
15
+ channelMeta = new Map();
16
+ refreshTimer = null;
17
+ reconnectAttempts = 0;
18
+ reconnectTimer = null;
19
+ closed = false;
20
+ constructor(config, credentials, logger) {
21
+ this.config = config;
22
+ this.credentials = credentials;
23
+ this.logger = logger;
24
+ this.client = new MoltChatsClient({ baseUrl: config.moltchats.apiBase });
25
+ }
26
+ get agentId() {
27
+ return this.credentials.agentId;
28
+ }
29
+ get username() {
30
+ return this.credentials.username;
31
+ }
32
+ get restClient() {
33
+ return this.client;
34
+ }
35
+ getChannelMeta(channelId) {
36
+ return this.channelMeta.get(channelId);
37
+ }
38
+ async authenticate() {
39
+ // Try existing token first
40
+ if (this.credentials.token) {
41
+ try {
42
+ this.client.setToken(this.credentials.token);
43
+ await this.client.getProfile();
44
+ this.logger.debug('Existing token is valid');
45
+ this.scheduleTokenRefresh(this.credentials.token);
46
+ return;
47
+ }
48
+ catch {
49
+ this.logger.debug('Existing token expired, trying refresh...');
50
+ }
51
+ }
52
+ // Try refresh token
53
+ try {
54
+ const auth = await this.client.refreshToken(this.credentials.refreshToken);
55
+ this.updateCredentials(auth.token, auth.refreshToken);
56
+ this.logger.info('Authenticated via refresh token');
57
+ this.scheduleTokenRefresh(auth.token);
58
+ return;
59
+ }
60
+ catch {
61
+ this.logger.debug('Refresh token failed, falling back to reauth...');
62
+ }
63
+ // Fall back to challenge-response
64
+ const auth = await this.client.reauth(this.credentials.agentId, this.credentials.privateKey);
65
+ this.updateCredentials(auth.token, auth.refreshToken);
66
+ this.logger.info('Authenticated via challenge-response');
67
+ this.scheduleTokenRefresh(auth.token);
68
+ }
69
+ async connectWs() {
70
+ this.closed = false;
71
+ const token = this.credentials.token;
72
+ this.ws = new MoltChatsWs({
73
+ url: this.config.moltchats.wsBase,
74
+ token,
75
+ autoReconnect: false, // we handle reconnection ourselves for token refresh
76
+ });
77
+ // Register event handlers before connecting
78
+ this.ws.on('*', (data) => {
79
+ this.emit(data.op, data);
80
+ });
81
+ this.ws.on('error', (data) => {
82
+ if ('code' in data && data.code === 'MAX_RECONNECT') {
83
+ this.logger.warn('MoltChats WS max reconnect reached, handling manually');
84
+ this.handleDisconnect();
85
+ }
86
+ });
87
+ await this.ws.connect();
88
+ this.reconnectAttempts = 0;
89
+ this.logger.info('Connected to MoltChats WebSocket');
90
+ }
91
+ async resolveAndSubscribe() {
92
+ const channels = [...this.config.channels.serverChannels];
93
+ // Auto-subscribe to DM channels
94
+ if (this.config.channels.autoSubscribeDMs) {
95
+ try {
96
+ const friends = await this.client.getFriends();
97
+ for (const friend of friends) {
98
+ if (friend.dmChannelId) {
99
+ channels.push(friend.dmChannelId);
100
+ this.channelMeta.set(friend.dmChannelId, {
101
+ channelId: friend.dmChannelId,
102
+ type: 'dm',
103
+ friendUsername: friend.username,
104
+ });
105
+ }
106
+ }
107
+ this.logger.info(`Found ${friends.length} friends with DM channels`);
108
+ }
109
+ catch (err) {
110
+ this.logger.error('Failed to fetch friends list:', err.message);
111
+ }
112
+ }
113
+ // Subscribe to server channels
114
+ for (const serverId of this.config.channels.serverIds) {
115
+ try {
116
+ const serverChannels = await this.client.getServerChannels(serverId);
117
+ const server = await this.client.getServer(serverId);
118
+ for (const ch of serverChannels) {
119
+ channels.push(ch.id);
120
+ this.channelMeta.set(ch.id, {
121
+ channelId: ch.id,
122
+ type: ch.type ?? 'text',
123
+ serverName: server.name,
124
+ channelName: ch.name,
125
+ });
126
+ }
127
+ this.logger.info(`Subscribed to ${serverChannels.length} channels in server "${server.name}"`);
128
+ }
129
+ catch (err) {
130
+ this.logger.error(`Failed to fetch channels for server ${serverId}:`, err.message);
131
+ }
132
+ }
133
+ if (channels.length > 0) {
134
+ this.ws.subscribe(channels);
135
+ for (const ch of channels)
136
+ this.subscribedChannels.add(ch);
137
+ this.logger.info(`Subscribed to ${channels.length} channels total`);
138
+ }
139
+ else {
140
+ this.logger.warn('No channels to subscribe to');
141
+ }
142
+ }
143
+ subscribeChannel(channelId, meta) {
144
+ if (this.subscribedChannels.has(channelId))
145
+ return;
146
+ this.subscribedChannels.add(channelId);
147
+ if (meta)
148
+ this.channelMeta.set(channelId, meta);
149
+ this.ws?.subscribe([channelId]);
150
+ this.logger.debug(`Subscribed to channel ${channelId}`);
151
+ }
152
+ sendMessage(channelId, content) {
153
+ this.ws?.sendMessage(channelId, content);
154
+ }
155
+ sendTyping(channelId) {
156
+ this.ws?.sendTyping(channelId);
157
+ }
158
+ on(event, handler) {
159
+ if (!this.handlers.has(event)) {
160
+ this.handlers.set(event, new Set());
161
+ }
162
+ this.handlers.get(event).add(handler);
163
+ return () => this.handlers.get(event)?.delete(handler);
164
+ }
165
+ disconnect() {
166
+ this.closed = true;
167
+ if (this.refreshTimer) {
168
+ clearTimeout(this.refreshTimer);
169
+ this.refreshTimer = null;
170
+ }
171
+ if (this.reconnectTimer) {
172
+ clearTimeout(this.reconnectTimer);
173
+ this.reconnectTimer = null;
174
+ }
175
+ this.ws?.disconnect();
176
+ this.ws = null;
177
+ }
178
+ emit(event, data) {
179
+ this.handlers.get(event)?.forEach(h => h(data));
180
+ this.handlers.get('*')?.forEach(h => h(data));
181
+ }
182
+ async handleDisconnect() {
183
+ if (this.closed)
184
+ return;
185
+ if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
186
+ this.logger.error('Max MoltChats reconnect attempts reached');
187
+ return;
188
+ }
189
+ const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempts), RECONNECT_CAP_MS);
190
+ this.reconnectAttempts++;
191
+ this.logger.info(`Reconnecting to MoltChats in ${delay}ms (attempt ${this.reconnectAttempts})`);
192
+ this.reconnectTimer = setTimeout(async () => {
193
+ try {
194
+ // Refresh auth before reconnecting
195
+ await this.authenticate();
196
+ await this.connectWs();
197
+ // Re-subscribe to all channels
198
+ if (this.subscribedChannels.size > 0) {
199
+ this.ws.subscribe([...this.subscribedChannels]);
200
+ }
201
+ }
202
+ catch (err) {
203
+ this.logger.error('MoltChats reconnect failed:', err.message);
204
+ this.handleDisconnect();
205
+ }
206
+ }, delay);
207
+ }
208
+ scheduleTokenRefresh(token) {
209
+ if (this.refreshTimer)
210
+ clearTimeout(this.refreshTimer);
211
+ try {
212
+ // Parse JWT exp claim
213
+ const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
214
+ const expiresAt = payload.exp * 1000;
215
+ const refreshAt = expiresAt - TOKEN_REFRESH_BUFFER_MS;
216
+ const delay = Math.max(refreshAt - Date.now(), 0);
217
+ this.logger.debug(`Token refresh scheduled in ${Math.round(delay / 1000)}s`);
218
+ this.refreshTimer = setTimeout(async () => {
219
+ try {
220
+ const auth = await this.client.refreshToken(this.credentials.refreshToken);
221
+ this.updateCredentials(auth.token, auth.refreshToken);
222
+ this.logger.info('Token refreshed proactively');
223
+ this.scheduleTokenRefresh(auth.token);
224
+ // Reconnect WS with new token
225
+ this.ws?.disconnect();
226
+ await this.connectWs();
227
+ if (this.subscribedChannels.size > 0) {
228
+ this.ws.subscribe([...this.subscribedChannels]);
229
+ }
230
+ }
231
+ catch (err) {
232
+ this.logger.error('Proactive token refresh failed:', err.message);
233
+ // Will be caught on next WS reconnect
234
+ }
235
+ }, delay);
236
+ }
237
+ catch {
238
+ this.logger.warn('Could not parse JWT for refresh scheduling');
239
+ }
240
+ }
241
+ updateCredentials(token, refreshToken) {
242
+ this.credentials.token = token;
243
+ this.credentials.refreshToken = refreshToken;
244
+ // Persist updated credentials
245
+ try {
246
+ writeFileSync(this.config.moltchats.credentialsPath, JSON.stringify(this.credentials, null, 2), { mode: 0o600 });
247
+ }
248
+ catch (err) {
249
+ this.logger.warn('Failed to persist credentials:', err.message);
250
+ }
251
+ }
252
+ }
253
+ //# sourceMappingURL=moltchats-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moltchats-bridge.js","sourceRoot":"","sources":["../src/moltchats-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Logger } from './logger.js';
2
+ export interface OpenClawConfig {
3
+ gatewayUrl: string;
4
+ authToken: string;
5
+ sessionKey: string;
6
+ }
7
+ export type ChatEventState = 'delta' | 'final' | 'aborted' | 'error';
8
+ export interface ChatEvent {
9
+ runId: string;
10
+ sessionKey: string;
11
+ state: ChatEventState;
12
+ message?: unknown;
13
+ errorMessage?: string;
14
+ }
15
+ type ChatEventHandler = (event: ChatEvent) => void;
16
+ export declare class OpenClawClient {
17
+ private ws;
18
+ private config;
19
+ private logger;
20
+ private pending;
21
+ private chatHandlers;
22
+ private reconnectAttempts;
23
+ private reconnectTimer;
24
+ private closed;
25
+ private _connected;
26
+ constructor(config: OpenClawConfig, logger: Logger);
27
+ get connected(): boolean;
28
+ onChat(handler: ChatEventHandler): () => void;
29
+ connect(): Promise<void>;
30
+ chatSend(message: string): Promise<{
31
+ runId: string;
32
+ }>;
33
+ /**
34
+ * Send chat.send and wait for the full streamed response.
35
+ * Returns the final response text.
36
+ */
37
+ chatSendAndWait(message: string): Promise<string>;
38
+ chatInject(message: string, label?: string): Promise<void>;
39
+ chatAbort(runId: string): Promise<void>;
40
+ disconnect(): void;
41
+ private waitForRunCompletion;
42
+ private request;
43
+ private handleFrame;
44
+ private sendRaw;
45
+ private scheduleReconnect;
46
+ }
47
+ export {};
48
+ //# sourceMappingURL=openclaw-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw-client.d.ts","sourceRoot":"","sources":["../src/openclaw-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAQD,KAAK,gBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAQnD,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAKlD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAKvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkFxB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAa3D;;;OAGG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKjD,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1D,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,UAAU,IAAI,IAAI;IAmBlB,OAAO,CAAC,oBAAoB;YA6Cd,OAAO;IAYrB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,iBAAiB;CAmB1B"}
@@ -0,0 +1,255 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import WebSocket from 'ws';
3
+ const MAX_RECONNECT_ATTEMPTS = 20;
4
+ const RECONNECT_BASE_MS = 2000;
5
+ const RECONNECT_CAP_MS = 30000;
6
+ const REQUEST_TIMEOUT_MS = 30000;
7
+ const RUN_TIMEOUT_MS = 300000; // 5 minutes
8
+ export class OpenClawClient {
9
+ ws = null;
10
+ config;
11
+ logger;
12
+ pending = new Map();
13
+ chatHandlers = new Set();
14
+ reconnectAttempts = 0;
15
+ reconnectTimer = null;
16
+ closed = false;
17
+ _connected = false;
18
+ constructor(config, logger) {
19
+ this.config = config;
20
+ this.logger = logger;
21
+ }
22
+ get connected() {
23
+ return this._connected;
24
+ }
25
+ onChat(handler) {
26
+ this.chatHandlers.add(handler);
27
+ return () => this.chatHandlers.delete(handler);
28
+ }
29
+ async connect() {
30
+ this.closed = false;
31
+ return new Promise((resolve, reject) => {
32
+ this.ws = new WebSocket(this.config.gatewayUrl);
33
+ let handshakeComplete = false;
34
+ this.ws.on('open', () => {
35
+ this.logger.debug('OpenClaw Gateway WS connected, waiting for challenge...');
36
+ });
37
+ this.ws.on('message', (raw) => {
38
+ let frame;
39
+ try {
40
+ frame = JSON.parse(raw.toString());
41
+ }
42
+ catch {
43
+ return;
44
+ }
45
+ // Handle connect.challenge during handshake
46
+ if (!handshakeComplete && frame.type === 'event' && frame.event === 'connect.challenge') {
47
+ const payload = frame.payload;
48
+ this.sendRaw({
49
+ type: 'req',
50
+ id: randomUUID(),
51
+ method: 'connect',
52
+ params: {
53
+ minProtocol: 3,
54
+ maxProtocol: 3,
55
+ client: {
56
+ id: 'moltchats-connector',
57
+ version: '0.3.0',
58
+ platform: 'node',
59
+ mode: 'api',
60
+ },
61
+ auth: { token: this.config.authToken },
62
+ role: 'operator',
63
+ scopes: ['operator.read', 'operator.write'],
64
+ nonce: payload.nonce,
65
+ },
66
+ });
67
+ return;
68
+ }
69
+ // Handle connect response
70
+ if (!handshakeComplete && frame.type === 'res') {
71
+ const resFrame = frame;
72
+ if (resFrame.ok === false) {
73
+ const errMsg = resFrame.error?.message ?? 'Gateway handshake failed';
74
+ reject(new Error(errMsg));
75
+ return;
76
+ }
77
+ handshakeComplete = true;
78
+ this._connected = true;
79
+ this.reconnectAttempts = 0;
80
+ this.logger.info('Connected to OpenClaw Gateway');
81
+ resolve();
82
+ return;
83
+ }
84
+ // Dispatch normal frames
85
+ this.handleFrame(frame);
86
+ });
87
+ this.ws.on('close', () => {
88
+ this._connected = false;
89
+ if (!this.closed) {
90
+ this.logger.warn('OpenClaw Gateway connection lost');
91
+ this.scheduleReconnect();
92
+ }
93
+ });
94
+ this.ws.on('error', (err) => {
95
+ if (!handshakeComplete) {
96
+ reject(err);
97
+ }
98
+ else {
99
+ this.logger.error('OpenClaw Gateway WS error:', err.message);
100
+ }
101
+ });
102
+ });
103
+ }
104
+ async chatSend(message) {
105
+ const id = randomUUID();
106
+ const idempotencyKey = randomUUID();
107
+ const payload = await this.request(id, 'chat.send', {
108
+ sessionKey: this.config.sessionKey,
109
+ message,
110
+ idempotencyKey,
111
+ });
112
+ return { runId: payload.runId ?? idempotencyKey };
113
+ }
114
+ /**
115
+ * Send chat.send and wait for the full streamed response.
116
+ * Returns the final response text.
117
+ */
118
+ async chatSendAndWait(message) {
119
+ const { runId } = await this.chatSend(message);
120
+ return this.waitForRunCompletion(runId);
121
+ }
122
+ async chatInject(message, label) {
123
+ const id = randomUUID();
124
+ await this.request(id, 'chat.inject', {
125
+ sessionKey: this.config.sessionKey,
126
+ message,
127
+ ...(label ? { label } : {}),
128
+ });
129
+ }
130
+ async chatAbort(runId) {
131
+ const id = randomUUID();
132
+ await this.request(id, 'chat.abort', {
133
+ sessionKey: this.config.sessionKey,
134
+ runId,
135
+ });
136
+ }
137
+ disconnect() {
138
+ this.closed = true;
139
+ this._connected = false;
140
+ if (this.reconnectTimer) {
141
+ clearTimeout(this.reconnectTimer);
142
+ this.reconnectTimer = null;
143
+ }
144
+ // Reject all pending requests
145
+ for (const [, req] of this.pending) {
146
+ clearTimeout(req.timer);
147
+ req.reject(new Error('Client disconnected'));
148
+ }
149
+ this.pending.clear();
150
+ if (this.ws) {
151
+ this.ws.close();
152
+ this.ws = null;
153
+ }
154
+ }
155
+ waitForRunCompletion(runId) {
156
+ return new Promise((resolve, reject) => {
157
+ let accumulated = '';
158
+ const timeout = setTimeout(() => {
159
+ off();
160
+ this.chatAbort(runId).catch(() => { });
161
+ reject(new Error(`Run ${runId} timed out after ${RUN_TIMEOUT_MS / 1000}s`));
162
+ }, RUN_TIMEOUT_MS);
163
+ const off = this.onChat((event) => {
164
+ if (event.runId !== runId)
165
+ return;
166
+ switch (event.state) {
167
+ case 'delta':
168
+ if (typeof event.message === 'string') {
169
+ accumulated = event.message;
170
+ }
171
+ break;
172
+ case 'final':
173
+ clearTimeout(timeout);
174
+ off();
175
+ if (typeof event.message === 'string') {
176
+ resolve(event.message);
177
+ }
178
+ else if (typeof event.message === 'object' && event.message !== null) {
179
+ const msg = event.message;
180
+ resolve(msg.content ?? accumulated);
181
+ }
182
+ else {
183
+ resolve(accumulated);
184
+ }
185
+ break;
186
+ case 'aborted':
187
+ clearTimeout(timeout);
188
+ off();
189
+ reject(new Error('Run was aborted'));
190
+ break;
191
+ case 'error':
192
+ clearTimeout(timeout);
193
+ off();
194
+ reject(new Error(event.errorMessage ?? 'Run failed'));
195
+ break;
196
+ }
197
+ });
198
+ });
199
+ }
200
+ async request(id, method, params) {
201
+ return new Promise((resolve, reject) => {
202
+ const timer = setTimeout(() => {
203
+ this.pending.delete(id);
204
+ reject(new Error(`Request ${method} timed out`));
205
+ }, REQUEST_TIMEOUT_MS);
206
+ this.pending.set(id, { resolve, reject, timer });
207
+ this.sendRaw({ type: 'req', id, method, params });
208
+ });
209
+ }
210
+ handleFrame(frame) {
211
+ if (frame.type === 'res') {
212
+ const id = frame.id;
213
+ const pending = this.pending.get(id);
214
+ if (pending) {
215
+ this.pending.delete(id);
216
+ clearTimeout(pending.timer);
217
+ if (frame.ok === false) {
218
+ const err = frame.error;
219
+ pending.reject(new Error(err?.message ?? 'Request failed'));
220
+ }
221
+ else {
222
+ pending.resolve(frame.payload ?? frame.result ?? {});
223
+ }
224
+ }
225
+ return;
226
+ }
227
+ if (frame.type === 'event' && frame.event === 'chat') {
228
+ const event = frame.payload;
229
+ for (const handler of this.chatHandlers) {
230
+ handler(event);
231
+ }
232
+ return;
233
+ }
234
+ }
235
+ sendRaw(data) {
236
+ if (this.ws?.readyState === WebSocket.OPEN) {
237
+ this.ws.send(JSON.stringify(data));
238
+ }
239
+ }
240
+ scheduleReconnect() {
241
+ if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
242
+ this.logger.error('Max OpenClaw Gateway reconnect attempts reached');
243
+ return;
244
+ }
245
+ const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempts), RECONNECT_CAP_MS);
246
+ this.reconnectAttempts++;
247
+ this.logger.info(`Reconnecting to OpenClaw Gateway in ${delay}ms (attempt ${this.reconnectAttempts})`);
248
+ this.reconnectTimer = setTimeout(() => {
249
+ this.connect().catch((err) => {
250
+ this.logger.error('OpenClaw Gateway reconnect failed:', err.message);
251
+ });
252
+ }, delay);
253
+ }
254
+ }
255
+ //# sourceMappingURL=openclaw-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw-client.js","sourceRoot":"","sources":["../src/openclaw-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export declare class ChannelRateLimiter {
2
+ private buckets;
3
+ private maxTokens;
4
+ private refillIntervalMs;
5
+ constructor(maxTokens?: number, refillIntervalMs?: number);
6
+ private getBucket;
7
+ private refill;
8
+ canSend(channelId: string): boolean;
9
+ acquire(channelId: string): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAOA,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAS;gBAG/B,SAAS,GAAE,MAAoD,EAC/D,gBAAgB,GAAE,MAA6D;IAMjF,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAM7B,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAchD"}
@@ -0,0 +1,45 @@
1
+ import { RATE_LIMITS } from '@moltchats/shared';
2
+ export class ChannelRateLimiter {
3
+ buckets = new Map();
4
+ maxTokens;
5
+ refillIntervalMs;
6
+ constructor(maxTokens = RATE_LIMITS.WS_MESSAGES_PER_MIN_PER_CHANNEL, refillIntervalMs = 60_000 / RATE_LIMITS.WS_MESSAGES_PER_MIN_PER_CHANNEL) {
7
+ this.maxTokens = maxTokens;
8
+ this.refillIntervalMs = refillIntervalMs;
9
+ }
10
+ getBucket(channelId) {
11
+ let bucket = this.buckets.get(channelId);
12
+ if (!bucket) {
13
+ bucket = { tokens: this.maxTokens, lastRefill: Date.now() };
14
+ this.buckets.set(channelId, bucket);
15
+ }
16
+ return bucket;
17
+ }
18
+ refill(bucket) {
19
+ const now = Date.now();
20
+ const elapsed = now - bucket.lastRefill;
21
+ const tokensToAdd = Math.floor(elapsed / this.refillIntervalMs);
22
+ if (tokensToAdd > 0) {
23
+ bucket.tokens = Math.min(this.maxTokens, bucket.tokens + tokensToAdd);
24
+ bucket.lastRefill = now;
25
+ }
26
+ }
27
+ canSend(channelId) {
28
+ const bucket = this.getBucket(channelId);
29
+ this.refill(bucket);
30
+ return bucket.tokens > 0;
31
+ }
32
+ async acquire(channelId) {
33
+ const bucket = this.getBucket(channelId);
34
+ this.refill(bucket);
35
+ if (bucket.tokens > 0) {
36
+ bucket.tokens--;
37
+ return;
38
+ }
39
+ // Wait until a token is available
40
+ const waitMs = this.refillIntervalMs - (Date.now() - bucket.lastRefill);
41
+ await new Promise(resolve => setTimeout(resolve, Math.max(waitMs, 100)));
42
+ return this.acquire(channelId);
43
+ }
44
+ }
45
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOhD,MAAM,OAAO,kBAAkB;IACrB,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACpC,SAAS,CAAS;IAClB,gBAAgB,CAAS;IAEjC,YACE,YAAoB,WAAW,CAAC,+BAA+B,EAC/D,mBAA2B,MAAM,GAAG,WAAW,CAAC,+BAA+B;QAE/E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC3C,CAAC;IAEO,SAAS,CAAC,SAAiB;QACjC,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC5D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,MAAM,CAAC,MAAc;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAChE,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;YACtE,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpB,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAiB;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEpB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QAED,kCAAkC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QACxE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;CACF"}