claude-threads 0.15.0 → 0.16.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 (79) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +5 -5
  3. package/dist/index.js +20410 -387
  4. package/dist/mcp/permission-server.js +34038 -139
  5. package/package.json +14 -18
  6. package/dist/changelog.d.ts +0 -20
  7. package/dist/changelog.js +0 -134
  8. package/dist/claude/cli.d.ts +0 -50
  9. package/dist/claude/cli.js +0 -181
  10. package/dist/config/migration.d.ts +0 -45
  11. package/dist/config/migration.js +0 -35
  12. package/dist/config.d.ts +0 -21
  13. package/dist/config.js +0 -7
  14. package/dist/git/worktree.d.ts +0 -46
  15. package/dist/git/worktree.js +0 -228
  16. package/dist/index.d.ts +0 -2
  17. package/dist/logo.d.ts +0 -14
  18. package/dist/logo.js +0 -41
  19. package/dist/mattermost/api.d.ts +0 -85
  20. package/dist/mattermost/api.js +0 -124
  21. package/dist/mattermost/api.test.d.ts +0 -1
  22. package/dist/mattermost/api.test.js +0 -319
  23. package/dist/mcp/permission-server.d.ts +0 -2
  24. package/dist/onboarding.d.ts +0 -1
  25. package/dist/onboarding.js +0 -318
  26. package/dist/persistence/session-store.d.ts +0 -71
  27. package/dist/persistence/session-store.js +0 -152
  28. package/dist/platform/client.d.ts +0 -140
  29. package/dist/platform/client.js +0 -1
  30. package/dist/platform/formatter.d.ts +0 -74
  31. package/dist/platform/formatter.js +0 -1
  32. package/dist/platform/index.d.ts +0 -11
  33. package/dist/platform/index.js +0 -8
  34. package/dist/platform/mattermost/client.d.ts +0 -70
  35. package/dist/platform/mattermost/client.js +0 -404
  36. package/dist/platform/mattermost/formatter.d.ts +0 -20
  37. package/dist/platform/mattermost/formatter.js +0 -46
  38. package/dist/platform/mattermost/permission-api.d.ts +0 -10
  39. package/dist/platform/mattermost/permission-api.js +0 -139
  40. package/dist/platform/mattermost/types.d.ts +0 -71
  41. package/dist/platform/mattermost/types.js +0 -1
  42. package/dist/platform/permission-api-factory.d.ts +0 -11
  43. package/dist/platform/permission-api-factory.js +0 -21
  44. package/dist/platform/permission-api.d.ts +0 -67
  45. package/dist/platform/permission-api.js +0 -8
  46. package/dist/platform/types.d.ts +0 -70
  47. package/dist/platform/types.js +0 -7
  48. package/dist/session/commands.d.ts +0 -52
  49. package/dist/session/commands.js +0 -323
  50. package/dist/session/events.d.ts +0 -25
  51. package/dist/session/events.js +0 -368
  52. package/dist/session/index.d.ts +0 -7
  53. package/dist/session/index.js +0 -6
  54. package/dist/session/lifecycle.d.ts +0 -70
  55. package/dist/session/lifecycle.js +0 -456
  56. package/dist/session/manager.d.ts +0 -96
  57. package/dist/session/manager.js +0 -537
  58. package/dist/session/reactions.d.ts +0 -25
  59. package/dist/session/reactions.js +0 -151
  60. package/dist/session/streaming.d.ts +0 -47
  61. package/dist/session/streaming.js +0 -152
  62. package/dist/session/types.d.ts +0 -78
  63. package/dist/session/types.js +0 -9
  64. package/dist/session/worktree.d.ts +0 -56
  65. package/dist/session/worktree.js +0 -339
  66. package/dist/update-notifier.d.ts +0 -3
  67. package/dist/update-notifier.js +0 -41
  68. package/dist/utils/emoji.d.ts +0 -43
  69. package/dist/utils/emoji.js +0 -65
  70. package/dist/utils/emoji.test.d.ts +0 -1
  71. package/dist/utils/emoji.test.js +0 -131
  72. package/dist/utils/logger.d.ts +0 -34
  73. package/dist/utils/logger.js +0 -42
  74. package/dist/utils/logger.test.d.ts +0 -1
  75. package/dist/utils/logger.test.js +0 -121
  76. package/dist/utils/tool-formatter.d.ts +0 -53
  77. package/dist/utils/tool-formatter.js +0 -252
  78. package/dist/utils/tool-formatter.test.d.ts +0 -1
  79. package/dist/utils/tool-formatter.test.js +0 -372
@@ -1,404 +0,0 @@
1
- import WebSocket from 'ws';
2
- import { EventEmitter } from 'events';
3
- import { wsLogger } from '../../utils/logger.js';
4
- import { MattermostFormatter } from './formatter.js';
5
- // Escape special regex characters to prevent regex injection
6
- function escapeRegExp(string) {
7
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
8
- }
9
- export class MattermostClient extends EventEmitter {
10
- // Platform identity (required by PlatformClient)
11
- platformId;
12
- platformType = 'mattermost';
13
- displayName;
14
- ws = null;
15
- url;
16
- token;
17
- channelId;
18
- botName;
19
- allowedUsers;
20
- reconnectAttempts = 0;
21
- maxReconnectAttempts = 10;
22
- reconnectDelay = 1000;
23
- userCache = new Map();
24
- botUserId = null;
25
- formatter = new MattermostFormatter();
26
- // Heartbeat to detect dead connections
27
- pingInterval = null;
28
- lastMessageAt = Date.now();
29
- PING_INTERVAL_MS = 30000; // Send ping every 30s
30
- PING_TIMEOUT_MS = 60000; // Reconnect if no message for 60s
31
- constructor(platformConfig) {
32
- super();
33
- this.platformId = platformConfig.id;
34
- this.displayName = platformConfig.displayName;
35
- this.url = platformConfig.url;
36
- this.token = platformConfig.token;
37
- this.channelId = platformConfig.channelId;
38
- this.botName = platformConfig.botName;
39
- this.allowedUsers = platformConfig.allowedUsers;
40
- }
41
- // ============================================================================
42
- // Type Normalization (Mattermost → Platform)
43
- // ============================================================================
44
- normalizePlatformUser(mattermostUser) {
45
- return {
46
- id: mattermostUser.id,
47
- username: mattermostUser.username,
48
- email: mattermostUser.email,
49
- };
50
- }
51
- normalizePlatformPost(mattermostPost) {
52
- // Normalize metadata.files if present
53
- const metadata = mattermostPost.metadata
54
- ? {
55
- ...mattermostPost.metadata,
56
- files: mattermostPost.metadata.files?.map((f) => this.normalizePlatformFile(f)),
57
- }
58
- : undefined;
59
- return {
60
- id: mattermostPost.id,
61
- platformId: this.platformId,
62
- channelId: mattermostPost.channel_id,
63
- userId: mattermostPost.user_id,
64
- message: mattermostPost.message,
65
- rootId: mattermostPost.root_id,
66
- createAt: mattermostPost.create_at,
67
- metadata,
68
- };
69
- }
70
- normalizePlatformReaction(mattermostReaction) {
71
- return {
72
- userId: mattermostReaction.user_id,
73
- postId: mattermostReaction.post_id,
74
- emojiName: mattermostReaction.emoji_name,
75
- createAt: mattermostReaction.create_at,
76
- };
77
- }
78
- normalizePlatformFile(mattermostFile) {
79
- return {
80
- id: mattermostFile.id,
81
- name: mattermostFile.name,
82
- size: mattermostFile.size,
83
- mimeType: mattermostFile.mime_type,
84
- extension: mattermostFile.extension,
85
- };
86
- }
87
- // REST API helper
88
- async api(method, path, body) {
89
- const url = `${this.url}/api/v4${path}`;
90
- const response = await fetch(url, {
91
- method,
92
- headers: {
93
- Authorization: `Bearer ${this.token}`,
94
- 'Content-Type': 'application/json',
95
- },
96
- body: body ? JSON.stringify(body) : undefined,
97
- });
98
- if (!response.ok) {
99
- const text = await response.text();
100
- throw new Error(`Mattermost API error ${response.status}: ${text}`);
101
- }
102
- return response.json();
103
- }
104
- // Get current bot user info
105
- async getBotUser() {
106
- const user = await this.api('GET', '/users/me');
107
- this.botUserId = user.id;
108
- return this.normalizePlatformUser(user);
109
- }
110
- // Get user by ID (cached)
111
- async getUser(userId) {
112
- const cached = this.userCache.get(userId);
113
- if (cached) {
114
- return this.normalizePlatformUser(cached);
115
- }
116
- try {
117
- const user = await this.api('GET', `/users/${userId}`);
118
- this.userCache.set(userId, user);
119
- return this.normalizePlatformUser(user);
120
- }
121
- catch {
122
- return null;
123
- }
124
- }
125
- // Post a message
126
- async createPost(message, threadId) {
127
- const request = {
128
- channel_id: this.channelId,
129
- message,
130
- root_id: threadId,
131
- };
132
- const post = await this.api('POST', '/posts', request);
133
- return this.normalizePlatformPost(post);
134
- }
135
- // Update a message (for streaming updates)
136
- async updatePost(postId, message) {
137
- const request = {
138
- id: postId,
139
- message,
140
- };
141
- const post = await this.api('PUT', `/posts/${postId}`, request);
142
- return this.normalizePlatformPost(post);
143
- }
144
- // Add a reaction to a post
145
- async addReaction(postId, emojiName) {
146
- await this.api('POST', '/reactions', {
147
- user_id: this.botUserId,
148
- post_id: postId,
149
- emoji_name: emojiName,
150
- });
151
- }
152
- /**
153
- * Create a post with reaction options for user interaction
154
- *
155
- * This is a common pattern for interactive posts that need user response
156
- * via reactions (e.g., approval prompts, questions, permission requests).
157
- *
158
- * @param message - Post message content
159
- * @param reactions - Array of emoji names to add as reaction options
160
- * @param threadId - Optional thread root ID
161
- * @returns The created post
162
- */
163
- async createInteractivePost(message, reactions, threadId) {
164
- const post = await this.createPost(message, threadId);
165
- // Add each reaction option, continuing even if some fail
166
- for (const emoji of reactions) {
167
- try {
168
- await this.addReaction(post.id, emoji);
169
- }
170
- catch (err) {
171
- console.error(` ⚠️ Failed to add reaction ${emoji}:`, err);
172
- }
173
- }
174
- return post;
175
- }
176
- // Download a file attachment
177
- async downloadFile(fileId) {
178
- const url = `${this.url}/api/v4/files/${fileId}`;
179
- const response = await fetch(url, {
180
- headers: {
181
- Authorization: `Bearer ${this.token}`,
182
- },
183
- });
184
- if (!response.ok) {
185
- throw new Error(`Failed to download file ${fileId}: ${response.status}`);
186
- }
187
- const arrayBuffer = await response.arrayBuffer();
188
- return Buffer.from(arrayBuffer);
189
- }
190
- // Get file info (metadata)
191
- async getFileInfo(fileId) {
192
- const file = await this.api('GET', `/files/${fileId}/info`);
193
- return this.normalizePlatformFile(file);
194
- }
195
- // Get a post by ID (used to verify thread still exists on resume)
196
- async getPost(postId) {
197
- try {
198
- const post = await this.api('GET', `/posts/${postId}`);
199
- return this.normalizePlatformPost(post);
200
- }
201
- catch {
202
- return null; // Post doesn't exist or was deleted
203
- }
204
- }
205
- // Connect to WebSocket
206
- async connect() {
207
- // Get bot user first
208
- await this.getBotUser();
209
- wsLogger.debug(`Bot user ID: ${this.botUserId}`);
210
- const wsUrl = this.url
211
- .replace(/^http/, 'ws')
212
- .concat('/api/v4/websocket');
213
- return new Promise((resolve, reject) => {
214
- this.ws = new WebSocket(wsUrl);
215
- this.ws.on('open', () => {
216
- wsLogger.debug('WebSocket connected');
217
- // Authenticate
218
- if (this.ws) {
219
- this.ws.send(JSON.stringify({
220
- seq: 1,
221
- action: 'authentication_challenge',
222
- data: { token: this.token },
223
- }));
224
- }
225
- });
226
- this.ws.on('message', (data) => {
227
- this.lastMessageAt = Date.now(); // Track activity for heartbeat
228
- try {
229
- const event = JSON.parse(data.toString());
230
- this.handleEvent(event);
231
- // Authentication success
232
- if (event.event === 'hello') {
233
- this.reconnectAttempts = 0;
234
- this.startHeartbeat();
235
- this.emit('connected');
236
- resolve();
237
- }
238
- }
239
- catch (err) {
240
- wsLogger.debug(`Failed to parse message: ${err}`);
241
- }
242
- });
243
- this.ws.on('close', () => {
244
- wsLogger.debug('WebSocket disconnected');
245
- this.stopHeartbeat();
246
- this.emit('disconnected');
247
- this.scheduleReconnect();
248
- });
249
- this.ws.on('error', (err) => {
250
- wsLogger.debug(`WebSocket error: ${err}`);
251
- this.emit('error', err);
252
- reject(err);
253
- });
254
- this.ws.on('pong', () => {
255
- this.lastMessageAt = Date.now(); // Pong received, connection is alive
256
- wsLogger.debug('Pong received');
257
- });
258
- });
259
- }
260
- handleEvent(event) {
261
- // Handle posted events
262
- if (event.event === 'posted') {
263
- const data = event.data;
264
- if (!data.post)
265
- return;
266
- try {
267
- const post = JSON.parse(data.post);
268
- // Ignore messages from ourselves
269
- if (post.user_id === this.botUserId)
270
- return;
271
- // Only handle messages in our channel
272
- if (post.channel_id !== this.channelId)
273
- return;
274
- // Get user info and emit (with normalized types)
275
- this.getUser(post.user_id).then((user) => {
276
- this.emit('message', this.normalizePlatformPost(post), user);
277
- });
278
- }
279
- catch (err) {
280
- wsLogger.debug(`Failed to parse post: ${err}`);
281
- }
282
- return;
283
- }
284
- // Handle reaction_added events
285
- if (event.event === 'reaction_added') {
286
- const data = event.data;
287
- if (!data.reaction)
288
- return;
289
- try {
290
- const reaction = JSON.parse(data.reaction);
291
- // Ignore reactions from ourselves
292
- if (reaction.user_id === this.botUserId)
293
- return;
294
- // Get user info and emit (with normalized types)
295
- this.getUser(reaction.user_id).then((user) => {
296
- this.emit('reaction', this.normalizePlatformReaction(reaction), user);
297
- });
298
- }
299
- catch (err) {
300
- wsLogger.debug(`Failed to parse reaction: ${err}`);
301
- }
302
- }
303
- }
304
- scheduleReconnect() {
305
- if (this.reconnectAttempts >= this.maxReconnectAttempts) {
306
- console.error(' ⚠️ Max reconnection attempts reached');
307
- return;
308
- }
309
- this.reconnectAttempts++;
310
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
311
- console.log(` 🔄 Reconnecting... (attempt ${this.reconnectAttempts})`);
312
- setTimeout(() => {
313
- this.connect().catch((err) => {
314
- console.error(` ❌ Reconnection failed: ${err}`);
315
- });
316
- }, delay);
317
- }
318
- startHeartbeat() {
319
- this.stopHeartbeat(); // Clear any existing
320
- this.lastMessageAt = Date.now();
321
- this.pingInterval = setInterval(() => {
322
- const silentFor = Date.now() - this.lastMessageAt;
323
- // If no message received for too long, connection is dead
324
- if (silentFor > this.PING_TIMEOUT_MS) {
325
- console.log(` 💔 Connection dead (no activity for ${Math.round(silentFor / 1000)}s), reconnecting...`);
326
- this.stopHeartbeat();
327
- if (this.ws) {
328
- this.ws.terminate(); // Force close (triggers reconnect via 'close' event)
329
- }
330
- return;
331
- }
332
- // Send ping to keep connection alive and verify it's working
333
- if (this.ws?.readyState === WebSocket.OPEN) {
334
- this.ws.ping();
335
- wsLogger.debug(`Ping sent (last activity ${Math.round(silentFor / 1000)}s ago)`);
336
- }
337
- }, this.PING_INTERVAL_MS);
338
- }
339
- stopHeartbeat() {
340
- if (this.pingInterval) {
341
- clearInterval(this.pingInterval);
342
- this.pingInterval = null;
343
- }
344
- }
345
- // Check if user is allowed to use the bot
346
- isUserAllowed(username) {
347
- if (this.allowedUsers.length === 0) {
348
- // If no allowlist configured, allow all
349
- return true;
350
- }
351
- return this.allowedUsers.includes(username);
352
- }
353
- // Check if message mentions the bot
354
- isBotMentioned(message) {
355
- const botName = escapeRegExp(this.botName);
356
- // Match @botname at start or with space before
357
- const mentionPattern = new RegExp(`(^|\\s)@${botName}\\b`, 'i');
358
- return mentionPattern.test(message);
359
- }
360
- // Extract prompt from message (remove bot mention)
361
- extractPrompt(message) {
362
- const botName = escapeRegExp(this.botName);
363
- return message
364
- .replace(new RegExp(`(^|\\s)@${botName}\\b`, 'gi'), ' ')
365
- .trim();
366
- }
367
- // Get the bot name
368
- getBotName() {
369
- return this.botName;
370
- }
371
- // Get MCP config for permission server
372
- getMcpConfig() {
373
- return {
374
- type: 'mattermost',
375
- url: this.url,
376
- token: this.token,
377
- channelId: this.channelId,
378
- allowedUsers: this.allowedUsers,
379
- };
380
- }
381
- // Get platform-specific markdown formatter
382
- getFormatter() {
383
- return this.formatter;
384
- }
385
- // Send typing indicator via WebSocket
386
- sendTyping(parentId) {
387
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
388
- return;
389
- this.ws.send(JSON.stringify({
390
- action: 'user_typing',
391
- seq: Date.now(),
392
- data: {
393
- channel_id: this.channelId,
394
- parent_id: parentId || '',
395
- },
396
- }));
397
- }
398
- disconnect() {
399
- if (this.ws) {
400
- this.ws.close();
401
- this.ws = null;
402
- }
403
- }
404
- }
@@ -1,20 +0,0 @@
1
- import type { PlatformFormatter } from '../formatter.js';
2
- /**
3
- * Mattermost markdown formatter
4
- *
5
- * Mattermost uses standard markdown syntax.
6
- */
7
- export declare class MattermostFormatter implements PlatformFormatter {
8
- formatBold(text: string): string;
9
- formatItalic(text: string): string;
10
- formatCode(text: string): string;
11
- formatCodeBlock(code: string, language?: string): string;
12
- formatUserMention(username: string): string;
13
- formatLink(text: string, url: string): string;
14
- formatListItem(text: string): string;
15
- formatNumberedListItem(number: number, text: string): string;
16
- formatBlockquote(text: string): string;
17
- formatHorizontalRule(): string;
18
- formatHeading(text: string, level: number): string;
19
- escapeText(text: string): string;
20
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Mattermost markdown formatter
3
- *
4
- * Mattermost uses standard markdown syntax.
5
- */
6
- export class MattermostFormatter {
7
- formatBold(text) {
8
- return `**${text}**`;
9
- }
10
- formatItalic(text) {
11
- return `_${text}_`;
12
- }
13
- formatCode(text) {
14
- return `\`${text}\``;
15
- }
16
- formatCodeBlock(code, language) {
17
- const lang = language || '';
18
- return `\`\`\`${lang}\n${code}\n\`\`\``;
19
- }
20
- formatUserMention(username) {
21
- return `@${username}`;
22
- }
23
- formatLink(text, url) {
24
- return `[${text}](${url})`;
25
- }
26
- formatListItem(text) {
27
- return `- ${text}`;
28
- }
29
- formatNumberedListItem(number, text) {
30
- return `${number}. ${text}`;
31
- }
32
- formatBlockquote(text) {
33
- return `> ${text}`;
34
- }
35
- formatHorizontalRule() {
36
- return '---';
37
- }
38
- formatHeading(text, level) {
39
- const hashes = '#'.repeat(Math.min(Math.max(level, 1), 6));
40
- return `${hashes} ${text}`;
41
- }
42
- escapeText(text) {
43
- // Escape markdown special characters
44
- return text.replace(/([*_`[\]()#+\-.!])/g, '\\$1');
45
- }
46
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Mattermost implementation of Permission API
3
- *
4
- * Handles permission requests via Mattermost API and WebSocket.
5
- */
6
- import type { PermissionApi, PermissionApiConfig } from '../permission-api.js';
7
- /**
8
- * Create a Mattermost permission API instance
9
- */
10
- export declare function createMattermostPermissionApi(config: PermissionApiConfig): PermissionApi;
@@ -1,139 +0,0 @@
1
- /**
2
- * Mattermost implementation of Permission API
3
- *
4
- * Handles permission requests via Mattermost API and WebSocket.
5
- */
6
- import WebSocket from 'ws';
7
- import { MattermostFormatter } from './formatter.js';
8
- import { getMe, getUser, createInteractivePost, updatePost, isUserAllowed, } from '../../mattermost/api.js';
9
- import { mcpLogger } from '../../utils/logger.js';
10
- /**
11
- * Mattermost Permission API implementation
12
- */
13
- class MattermostPermissionApi {
14
- apiConfig;
15
- config;
16
- formatter = new MattermostFormatter();
17
- botUserIdCache = null;
18
- constructor(config) {
19
- this.config = config;
20
- this.apiConfig = {
21
- url: config.url,
22
- token: config.token,
23
- };
24
- }
25
- getFormatter() {
26
- return this.formatter;
27
- }
28
- async getBotUserId() {
29
- if (this.botUserIdCache)
30
- return this.botUserIdCache;
31
- const me = await getMe(this.apiConfig);
32
- this.botUserIdCache = me.id;
33
- return me.id;
34
- }
35
- async getUsername(userId) {
36
- try {
37
- const user = await getUser(this.apiConfig, userId);
38
- return user?.username ?? null;
39
- }
40
- catch {
41
- return null;
42
- }
43
- }
44
- isUserAllowed(username) {
45
- return isUserAllowed(username, this.config.allowedUsers);
46
- }
47
- async createInteractivePost(message, reactions, threadId) {
48
- const botUserId = await this.getBotUserId();
49
- const post = await createInteractivePost(this.apiConfig, this.config.channelId, message, reactions, threadId, botUserId);
50
- return { id: post.id };
51
- }
52
- async updatePost(postId, message) {
53
- await updatePost(this.apiConfig, postId, message);
54
- }
55
- async waitForReaction(postId, botUserId, timeoutMs) {
56
- return new Promise((resolve) => {
57
- // Parse WebSocket URL from HTTP URL
58
- const wsUrl = this.config.url.replace(/^http/, 'ws') + '/api/v4/websocket';
59
- mcpLogger.debug(`Connecting to WebSocket: ${wsUrl}`);
60
- const ws = new WebSocket(wsUrl);
61
- let resolved = false;
62
- const cleanup = () => {
63
- if (ws.readyState === WebSocket.OPEN) {
64
- ws.close();
65
- }
66
- };
67
- const timeout = setTimeout(() => {
68
- if (!resolved) {
69
- resolved = true;
70
- cleanup();
71
- resolve(null);
72
- }
73
- }, timeoutMs);
74
- ws.on('open', () => {
75
- mcpLogger.debug('WebSocket connected, sending auth...');
76
- ws.send(JSON.stringify({
77
- seq: 1,
78
- action: 'authentication_challenge',
79
- data: { token: this.config.token },
80
- }));
81
- });
82
- ws.on('message', async (data) => {
83
- if (resolved)
84
- return;
85
- try {
86
- const event = JSON.parse(data.toString());
87
- mcpLogger.debug(`WebSocket event: ${event.event}`);
88
- if (event.event === 'reaction_added') {
89
- // Mattermost sends reaction as JSON string
90
- const reaction = typeof event.data.reaction === 'string'
91
- ? JSON.parse(event.data.reaction)
92
- : event.data.reaction;
93
- // Must be on our post
94
- if (reaction.post_id !== postId)
95
- return;
96
- // Must not be the bot's own reaction (adding the options)
97
- if (reaction.user_id === botUserId)
98
- return;
99
- mcpLogger.debug(`Reaction received: ${reaction.emoji_name} from user: ${reaction.user_id}`);
100
- // Got a valid reaction
101
- resolved = true;
102
- clearTimeout(timeout);
103
- cleanup();
104
- resolve({
105
- postId: reaction.post_id,
106
- userId: reaction.user_id,
107
- emojiName: reaction.emoji_name,
108
- });
109
- }
110
- }
111
- catch (err) {
112
- mcpLogger.debug(`Error parsing WebSocket message: ${err}`);
113
- }
114
- });
115
- ws.on('error', (error) => {
116
- mcpLogger.error(`WebSocket error: ${error.message}`);
117
- if (!resolved) {
118
- resolved = true;
119
- clearTimeout(timeout);
120
- resolve(null);
121
- }
122
- });
123
- ws.on('close', () => {
124
- mcpLogger.debug('WebSocket closed');
125
- if (!resolved) {
126
- resolved = true;
127
- clearTimeout(timeout);
128
- resolve(null);
129
- }
130
- });
131
- });
132
- }
133
- }
134
- /**
135
- * Create a Mattermost permission API instance
136
- */
137
- export function createMattermostPermissionApi(config) {
138
- return new MattermostPermissionApi(config);
139
- }
@@ -1,71 +0,0 @@
1
- export interface MattermostWebSocketEvent {
2
- event: string;
3
- data: Record<string, unknown>;
4
- broadcast: {
5
- channel_id?: string;
6
- user_id?: string;
7
- team_id?: string;
8
- };
9
- seq: number;
10
- }
11
- export interface MattermostFile {
12
- id: string;
13
- name: string;
14
- size: number;
15
- mime_type: string;
16
- extension: string;
17
- width?: number;
18
- height?: number;
19
- }
20
- export interface MattermostPost {
21
- id: string;
22
- create_at: number;
23
- update_at: number;
24
- delete_at: number;
25
- user_id: string;
26
- channel_id: string;
27
- root_id: string;
28
- message: string;
29
- type: string;
30
- props: Record<string, unknown>;
31
- metadata?: {
32
- embeds?: unknown[];
33
- files?: MattermostFile[];
34
- };
35
- }
36
- export interface MattermostUser {
37
- id: string;
38
- username: string;
39
- email: string;
40
- first_name: string;
41
- last_name: string;
42
- nickname: string;
43
- }
44
- export interface PostedEventData {
45
- channel_display_name: string;
46
- channel_name: string;
47
- channel_type: string;
48
- post: string;
49
- sender_name: string;
50
- team_id: string;
51
- }
52
- export interface ReactionAddedEventData {
53
- reaction: string;
54
- }
55
- export interface MattermostReaction {
56
- user_id: string;
57
- post_id: string;
58
- emoji_name: string;
59
- create_at: number;
60
- }
61
- export interface CreatePostRequest {
62
- channel_id: string;
63
- message: string;
64
- root_id?: string;
65
- props?: Record<string, unknown>;
66
- }
67
- export interface UpdatePostRequest {
68
- id: string;
69
- message: string;
70
- props?: Record<string, unknown>;
71
- }
@@ -1 +0,0 @@
1
- export {};