claude-threads 1.39.1 → 1.39.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.
- package/CHANGELOG.md +18 -0
- package/dist/index.js +106 -51
- package/dist/mcp/mcp-server.js +211 -164
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.39.3] - 2026-10-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Mattermost: a skin-toned 👍 no longer denies** (#629). Mattermost names an emoji picked with a skin tone `+1_medium_skin_tone` (verified against Mattermost v11), and the permission prompt treats any emoji it does not recognize as a denial. 1.39.2 fixed Slack's `+1::skin-tone-3` form; the Mattermost form is now matched on its base name too, for permission prompts, plan approvals and the other reaction controls.
|
|
12
|
+
- **One failed update no longer turns a code block into a post per flush** (#620). When the platform refused an update of the post Claude was writing a code block into, the bot gave up on that post at once. The continuation started in the middle of the block without its opening fence, so the rest rendered as prose, the height check split it every few lines (a new post per flush, close to 200 posts for a 2000-line file), and the closing fence opened a new block. A failed update is now retried on the next flush, which recovers the post completely for a passing error; only after three failures in a row is the post given up, with its code block reopened at the start of the next one.
|
|
13
|
+
|
|
14
|
+
## [1.39.2] - 2026-10-01
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **Slack: reacting to a permission prompt works again** (#622, thanks @elganfd). With interactive permissions on Slack every 👍 / ✅ / 👎 on a tool permission prompt was ignored, so the prompt sat until it timed out and denied. Two causes, each enough on its own: the permission server sent its `users.info` lookup as a JSON body, which Slack ignores on read methods, so every reacting user came back `user_not_found` and counted as unauthorized; and it opened a Socket Mode connection of its own, while Slack spreads an app's events over all its connections, so it saw only part of the reactions (and took that part of the bot's own events while a prompt waited). Read methods now go out as GET requests, and the prompt is polled for reactions instead of watched over a second connection. The same JSON-body problem affected `read_post`, `read_thread`, `read_channel_history`, the channel lookups and DM recipient resolution on Slack; those are fixed too. Plan approvals and questions were never affected.
|
|
18
|
+
- **A skin-toned 👍 no longer denies** (#628). Slack names an emoji picked with a skin tone `+1::skin-tone-3`, and the permission prompt treats any emoji it does not recognize as a denial. Emoji are now matched on their base name, which also covers plan approvals and the other reaction controls.
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
- `fast-uri` 4.1.4 → 4.2.1 (#625) and `ip-address` 10.4.0 → 10.7.2 (#626), both bundled into what users run.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- `@modelcontextprotocol/sdk` 1.30.1, `hono` 4.13.9, `ws` 8.22.0 (#624).
|
|
25
|
+
|
|
8
26
|
## [1.39.1] - 2026-09-25
|
|
9
27
|
|
|
10
28
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -5290,8 +5290,8 @@ function buildPermissionArgs(opts) {
|
|
|
5290
5290
|
mcpEnv[AGENT_FEATURES_ENV.DCM] = "1";
|
|
5291
5291
|
}
|
|
5292
5292
|
}
|
|
5293
|
-
if (opts.platformConfig.
|
|
5294
|
-
mcpEnv.
|
|
5293
|
+
if (opts.platformConfig.apiUrl) {
|
|
5294
|
+
mcpEnv.PLATFORM_API_URL = opts.platformConfig.apiUrl;
|
|
5295
5295
|
}
|
|
5296
5296
|
if (opts.workingDir) {
|
|
5297
5297
|
mcpEnv[OUTBOUND_ENV.SESSION_WORKING_DIR] = opts.workingDir;
|
|
@@ -6032,23 +6032,26 @@ var init_usage = __esm(() => {
|
|
|
6032
6032
|
});
|
|
6033
6033
|
|
|
6034
6034
|
// src/utils/emoji.ts
|
|
6035
|
+
function withoutSkinTone(emoji) {
|
|
6036
|
+
return emoji.replace(/::skin-tone-\d$/, "").replace(/(?<=.)_(?:light|medium_light|medium|medium_dark|dark)_skin_tone$/, "");
|
|
6037
|
+
}
|
|
6035
6038
|
function isApprovalEmoji(emoji) {
|
|
6036
|
-
return APPROVAL_EMOJIS.includes(emoji);
|
|
6039
|
+
return APPROVAL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6037
6040
|
}
|
|
6038
6041
|
function isDenialEmoji(emoji) {
|
|
6039
|
-
return DENIAL_EMOJIS.includes(emoji);
|
|
6042
|
+
return DENIAL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6040
6043
|
}
|
|
6041
6044
|
function isAllowAllEmoji(emoji) {
|
|
6042
|
-
return ALLOW_ALL_EMOJIS.includes(emoji);
|
|
6045
|
+
return ALLOW_ALL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6043
6046
|
}
|
|
6044
6047
|
function isCancelEmoji(emoji) {
|
|
6045
|
-
return CANCEL_EMOJIS.includes(emoji);
|
|
6048
|
+
return CANCEL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6046
6049
|
}
|
|
6047
6050
|
function isEscapeEmoji(emoji) {
|
|
6048
|
-
return ESCAPE_EMOJIS.includes(emoji);
|
|
6051
|
+
return ESCAPE_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6049
6052
|
}
|
|
6050
6053
|
function isResumeEmoji(emoji) {
|
|
6051
|
-
return RESUME_EMOJIS.includes(emoji);
|
|
6054
|
+
return RESUME_EMOJIS.includes(withoutSkinTone(emoji));
|
|
6052
6055
|
}
|
|
6053
6056
|
function isMinimizeToggleEmoji(emoji) {
|
|
6054
6057
|
return MINIMIZE_TOGGLE_EMOJIS.includes(emoji);
|
|
@@ -17137,11 +17140,23 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17137
17140
|
this._isServer = false;
|
|
17138
17141
|
this._redirects = 0;
|
|
17139
17142
|
if (protocols === undefined) {
|
|
17140
|
-
protocols
|
|
17143
|
+
if (!options || options.protocols === undefined) {
|
|
17144
|
+
protocols = [];
|
|
17145
|
+
} else if (Array.isArray(options.protocols)) {
|
|
17146
|
+
protocols = options.protocols;
|
|
17147
|
+
} else {
|
|
17148
|
+
protocols = [options.protocols];
|
|
17149
|
+
}
|
|
17141
17150
|
} else if (!Array.isArray(protocols)) {
|
|
17142
17151
|
if (typeof protocols === "object" && protocols !== null) {
|
|
17143
17152
|
options = protocols;
|
|
17144
|
-
protocols
|
|
17153
|
+
if (options.protocols === undefined) {
|
|
17154
|
+
protocols = [];
|
|
17155
|
+
} else if (Array.isArray(options.protocols)) {
|
|
17156
|
+
protocols = options.protocols;
|
|
17157
|
+
} else {
|
|
17158
|
+
protocols = [options.protocols];
|
|
17159
|
+
}
|
|
17145
17160
|
} else {
|
|
17146
17161
|
protocols = [protocols];
|
|
17147
17162
|
}
|
|
@@ -17260,7 +17275,6 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17260
17275
|
}
|
|
17261
17276
|
return;
|
|
17262
17277
|
}
|
|
17263
|
-
this._readyState = WebSocket.CLOSING;
|
|
17264
17278
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
17265
17279
|
if (err)
|
|
17266
17280
|
return;
|
|
@@ -17269,6 +17283,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17269
17283
|
this._socket.end();
|
|
17270
17284
|
}
|
|
17271
17285
|
});
|
|
17286
|
+
this._readyState = WebSocket.CLOSING;
|
|
17272
17287
|
setCloseTimer(this);
|
|
17273
17288
|
}
|
|
17274
17289
|
pause() {
|
|
@@ -17456,6 +17471,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17456
17471
|
socketPath: undefined,
|
|
17457
17472
|
hostname: undefined,
|
|
17458
17473
|
protocol: undefined,
|
|
17474
|
+
protocols: undefined,
|
|
17459
17475
|
timeout: undefined,
|
|
17460
17476
|
method: "GET",
|
|
17461
17477
|
host: undefined,
|
|
@@ -30090,7 +30106,7 @@ var require_react_reconciler = __commonJS(function(exports, module) {
|
|
|
30090
30106
|
});
|
|
30091
30107
|
|
|
30092
30108
|
// node_modules/ws/wrapper.mjs
|
|
30093
|
-
var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol,
|
|
30109
|
+
var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket4, import_websocket_server, wrapper_default;
|
|
30094
30110
|
var init_wrapper = __esm(() => {
|
|
30095
30111
|
import_stream = __toESM(require_stream(), 1);
|
|
30096
30112
|
import_extension = __toESM(require_extension(), 1);
|
|
@@ -30098,9 +30114,9 @@ var init_wrapper = __esm(() => {
|
|
|
30098
30114
|
import_receiver = __toESM(require_receiver(), 1);
|
|
30099
30115
|
import_sender = __toESM(require_sender(), 1);
|
|
30100
30116
|
import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
30101
|
-
|
|
30117
|
+
import_websocket4 = __toESM(require_websocket(), 1);
|
|
30102
30118
|
import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
30103
|
-
wrapper_default =
|
|
30119
|
+
wrapper_default = import_websocket4.default;
|
|
30104
30120
|
});
|
|
30105
30121
|
|
|
30106
30122
|
// node_modules/ink/build/devtools-window-polyfill.js
|
|
@@ -60281,7 +60297,8 @@ class ContentExecutor extends BaseExecutor {
|
|
|
60281
60297
|
headerPostId: null,
|
|
60282
60298
|
headerBody: "",
|
|
60283
60299
|
turnOpen: false,
|
|
60284
|
-
paragraphBreak: false
|
|
60300
|
+
paragraphBreak: false,
|
|
60301
|
+
updateFailures: 0
|
|
60285
60302
|
};
|
|
60286
60303
|
}
|
|
60287
60304
|
setHeader(line) {
|
|
@@ -60302,6 +60319,7 @@ class ContentExecutor extends BaseExecutor {
|
|
|
60302
60319
|
this.state.headerPostId = null;
|
|
60303
60320
|
this.state.headerBody = "";
|
|
60304
60321
|
this.state.turnOpen = false;
|
|
60322
|
+
this.state.updateFailures = 0;
|
|
60305
60323
|
}
|
|
60306
60324
|
getRenderedCurrentPost() {
|
|
60307
60325
|
const postId = this.state.currentPostId;
|
|
@@ -60328,7 +60346,9 @@ ${body}` : this.state.header;
|
|
|
60328
60346
|
this.state.headerDirty = false;
|
|
60329
60347
|
if (this.state.headerPostId) {
|
|
60330
60348
|
const postId = this.state.headerPostId;
|
|
60349
|
+
const failures = this.state.updateFailures;
|
|
60331
60350
|
await this.tryUpdatePost(ctx, postId, this.state.headerBody, "header", { reason: "header_update", headerLength: this.state.header?.length ?? 0 }, { reason: "header_update_failed" }, () => {}, () => {});
|
|
60351
|
+
this.state.updateFailures = failures;
|
|
60332
60352
|
return;
|
|
60333
60353
|
}
|
|
60334
60354
|
if (this.state.header && this.state.turnOpen) {
|
|
@@ -60351,6 +60371,7 @@ ${body}` : this.state.header;
|
|
|
60351
60371
|
this.state.headerBody = content;
|
|
60352
60372
|
this.state.headerDirty = false;
|
|
60353
60373
|
}
|
|
60374
|
+
this.state.updateFailures = 0;
|
|
60354
60375
|
onSuccess();
|
|
60355
60376
|
ctx.threadLogger?.logExecutor("content", "update", postId, successDetails, logTag);
|
|
60356
60377
|
} catch (err) {
|
|
@@ -60363,8 +60384,11 @@ ${body}` : this.state.header;
|
|
|
60363
60384
|
closeCurrentPost(ctx) {
|
|
60364
60385
|
const oldPostId = this.state.currentPostId;
|
|
60365
60386
|
const contentLength = this.state.currentPostContent.length;
|
|
60387
|
+
if (this.state.updateFailures > 0 && this.state.pendingContent.trim())
|
|
60388
|
+
this.giveUpCurrentPost();
|
|
60366
60389
|
this.state.currentPostId = null;
|
|
60367
60390
|
this.state.currentPostContent = "";
|
|
60391
|
+
this.state.updateFailures = 0;
|
|
60368
60392
|
if (ctx?.threadLogger && oldPostId) {
|
|
60369
60393
|
ctx.threadLogger.logExecutor("content", "close", oldPostId, {
|
|
60370
60394
|
contentLength,
|
|
@@ -60420,13 +60444,22 @@ ${body}` : this.state.header;
|
|
|
60420
60444
|
this.state.turnOpen = false;
|
|
60421
60445
|
return;
|
|
60422
60446
|
}
|
|
60423
|
-
|
|
60424
|
-
|
|
60447
|
+
const postBefore = this.state.currentPostId;
|
|
60448
|
+
await this.flushPending(ctx, reason);
|
|
60449
|
+
if (reason === "result") {
|
|
60450
|
+
if (this.state.updateFailures > 0 && this.state.currentPostId !== null && this.state.pendingContent.trim()) {
|
|
60451
|
+
await this.flushPending(ctx, reason);
|
|
60452
|
+
}
|
|
60453
|
+
if (postBefore !== null && this.state.currentPostId === null && this.state.pendingContent.trim()) {
|
|
60454
|
+
await this.flushPending(ctx, reason);
|
|
60455
|
+
}
|
|
60456
|
+
}
|
|
60457
|
+
if (this.state.headerDirty && this.state.updateFailures === 0)
|
|
60425
60458
|
await this.renderHeaderOnly(ctx);
|
|
60426
60459
|
if (reason === "result")
|
|
60427
60460
|
this.state.turnOpen = false;
|
|
60428
60461
|
}
|
|
60429
|
-
async flushPending(ctx) {
|
|
60462
|
+
async flushPending(ctx, reason) {
|
|
60430
60463
|
const pendingAtFlushStart = this.state.pendingContent;
|
|
60431
60464
|
let content = ctx.formatter.formatMarkdown(pendingAtFlushStart).trim();
|
|
60432
60465
|
const { maxLength: MAX_POST_LENGTH, hardThreshold: HARD_CONTINUATION_THRESHOLD } = ctx.platform.getMessageLimits();
|
|
@@ -60444,7 +60477,7 @@ ${body}` : this.state.header;
|
|
|
60444
60477
|
const shouldBreakEarly = this.state.currentPostId && combinedContent.length > MIN_BREAK_THRESHOLD && ctx.contentBreaker.shouldFlushEarly(combinedContent);
|
|
60445
60478
|
const reserve = this.headerReserve(this.state.currentPostId);
|
|
60446
60479
|
if (this.state.currentPostId && (combinedContent.length + reserve > HARD_CONTINUATION_THRESHOLD || shouldBreakEarly)) {
|
|
60447
|
-
await this.handleSplit(ctx, combinedContent, pendingAtFlushStart, HARD_CONTINUATION_THRESHOLD);
|
|
60480
|
+
await this.handleSplit(ctx, combinedContent, pendingAtFlushStart, HARD_CONTINUATION_THRESHOLD, reason);
|
|
60448
60481
|
return;
|
|
60449
60482
|
}
|
|
60450
60483
|
if (this.state.currentPostId && content.length + reserve > MAX_POST_LENGTH) {
|
|
@@ -60479,10 +60512,7 @@ ${body}` : this.state.header;
|
|
|
60479
60512
|
await this.tryUpdatePost(ctx, postId, combinedContent, "flush", { newContentLength: content.length, combinedLength: combinedContent.length }, (err) => ({ failedOp: "updatePost", error: String(err) }), () => {
|
|
60480
60513
|
this.state.currentPostContent = combinedContent;
|
|
60481
60514
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60482
|
-
}, () =>
|
|
60483
|
-
this.state.currentPostId = null;
|
|
60484
|
-
this.state.currentPostContent = "";
|
|
60485
|
-
});
|
|
60515
|
+
}, () => this.onInPlaceUpdateFailed(reason));
|
|
60486
60516
|
} else {
|
|
60487
60517
|
const chunks = splitContentForHeight(content, ctx.contentBreaker).flatMap((chunk) => chunk.length + reserve > MAX_POST_LENGTH ? splitByLength(chunk, HARD_CONTINUATION_THRESHOLD - reserve) : [chunk]);
|
|
60488
60518
|
ctx.threadLogger?.logExecutor("content", "create_start", "none", {
|
|
@@ -60493,7 +60523,7 @@ ${body}` : this.state.header;
|
|
|
60493
60523
|
await this.postChunks(ctx, chunks, pendingAtFlushStart, false);
|
|
60494
60524
|
}
|
|
60495
60525
|
}
|
|
60496
|
-
async splitOversized(ctx, content, pendingAtFlushStart, hardThreshold) {
|
|
60526
|
+
async splitOversized(ctx, content, pendingAtFlushStart, hardThreshold, reason) {
|
|
60497
60527
|
const postId = this.state.currentPostId;
|
|
60498
60528
|
if (!postId)
|
|
60499
60529
|
return false;
|
|
@@ -60504,17 +60534,35 @@ ${body}` : this.state.header;
|
|
|
60504
60534
|
let landed = false;
|
|
60505
60535
|
await this.tryUpdatePost(ctx, postId, firstPiece, "handleSplit", { reason: "oversized_code_block", contentLength: content.length, pieces: rest.length + 1 }, { reason: "oversized_code_block_failed" }, () => {
|
|
60506
60536
|
landed = true;
|
|
60507
|
-
}, () => {
|
|
60508
|
-
|
|
60509
|
-
|
|
60510
|
-
|
|
60537
|
+
}, () => {});
|
|
60538
|
+
if (!landed) {
|
|
60539
|
+
this.onInPlaceUpdateFailed(reason);
|
|
60540
|
+
return true;
|
|
60541
|
+
}
|
|
60511
60542
|
this.state.currentPostId = null;
|
|
60512
60543
|
this.state.currentPostContent = "";
|
|
60513
|
-
|
|
60514
|
-
|
|
60515
|
-
await this.postChunks(ctx, rest, pendingAtFlushStart, landed);
|
|
60544
|
+
this.clearFlushedContent(pendingAtFlushStart);
|
|
60545
|
+
await this.postChunks(ctx, rest, pendingAtFlushStart, true);
|
|
60516
60546
|
return true;
|
|
60517
60547
|
}
|
|
60548
|
+
onInPlaceUpdateFailed(reason) {
|
|
60549
|
+
this.state.updateFailures++;
|
|
60550
|
+
const limit = reason === "result" ? 2 : MAX_IN_PLACE_UPDATE_FAILURES;
|
|
60551
|
+
if (this.state.updateFailures < limit)
|
|
60552
|
+
return;
|
|
60553
|
+
this.giveUpCurrentPost();
|
|
60554
|
+
}
|
|
60555
|
+
giveUpCurrentPost() {
|
|
60556
|
+
const opener = openFenceOf(this.state.currentPostContent);
|
|
60557
|
+
this.state.currentPostId = null;
|
|
60558
|
+
this.state.currentPostContent = "";
|
|
60559
|
+
this.state.updateFailures = 0;
|
|
60560
|
+
if (!opener)
|
|
60561
|
+
return;
|
|
60562
|
+
const rest = this.state.pendingContent.replace(/^\n+/, "");
|
|
60563
|
+
this.state.pendingContent = /^```[ \t]*(\n|$)/.test(rest) ? rest.replace(/^```[ \t]*\n?/, "") : `${opener}
|
|
60564
|
+
${rest}`;
|
|
60565
|
+
}
|
|
60518
60566
|
async postChunks(ctx, chunks, pendingAtFlushStart, flushCleared) {
|
|
60519
60567
|
let cleared = flushCleared;
|
|
60520
60568
|
for (let i = 0;i < chunks.length; i++) {
|
|
@@ -60538,7 +60586,7 @@ ${this.state.pendingContent}` : "");
|
|
|
60538
60586
|
}
|
|
60539
60587
|
}
|
|
60540
60588
|
}
|
|
60541
|
-
async handleSplit(ctx, content, pendingAtFlushStart, hardThreshold) {
|
|
60589
|
+
async handleSplit(ctx, content, pendingAtFlushStart, hardThreshold, reason) {
|
|
60542
60590
|
let breakPoint;
|
|
60543
60591
|
let codeBlockOpenPosition;
|
|
60544
60592
|
if (content.length > hardThreshold) {
|
|
@@ -60586,26 +60634,21 @@ ${this.state.pendingContent}` : "");
|
|
|
60586
60634
|
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "soft_break_no_breakpoint", contentLength: content.length }, { reason: "soft_break_no_breakpoint_failed" }, () => {
|
|
60587
60635
|
this.state.currentPostContent = content;
|
|
60588
60636
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60589
|
-
}, () =>
|
|
60590
|
-
this.state.currentPostId = null;
|
|
60591
|
-
});
|
|
60637
|
+
}, () => this.onInPlaceUpdateFailed(reason));
|
|
60592
60638
|
}
|
|
60593
60639
|
return;
|
|
60594
60640
|
}
|
|
60595
60641
|
}
|
|
60596
60642
|
if (codeBlockOpenPosition !== undefined) {
|
|
60597
60643
|
if (codeBlockOpenPosition === 0) {
|
|
60598
|
-
if (await this.splitOversized(ctx, content, pendingAtFlushStart, hardThreshold))
|
|
60644
|
+
if (await this.splitOversized(ctx, content, pendingAtFlushStart, hardThreshold, reason))
|
|
60599
60645
|
return;
|
|
60600
60646
|
if (this.state.currentPostId) {
|
|
60601
60647
|
const postId = this.state.currentPostId;
|
|
60602
60648
|
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "code_block_at_start", contentLength: content.length }, { reason: "code_block_at_start_failed" }, () => {
|
|
60603
60649
|
this.state.currentPostContent = content;
|
|
60604
60650
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60605
|
-
}, () =>
|
|
60606
|
-
this.state.currentPostId = null;
|
|
60607
|
-
this.state.currentPostContent = "";
|
|
60608
|
-
});
|
|
60651
|
+
}, () => this.onInPlaceUpdateFailed(reason));
|
|
60609
60652
|
}
|
|
60610
60653
|
return;
|
|
60611
60654
|
}
|
|
@@ -60614,17 +60657,14 @@ ${this.state.pendingContent}` : "");
|
|
|
60614
60657
|
if (breakBeforeCodeBlock > 0) {
|
|
60615
60658
|
breakPoint = breakBeforeCodeBlock;
|
|
60616
60659
|
} else {
|
|
60617
|
-
if (await this.splitOversized(ctx, content, pendingAtFlushStart, hardThreshold))
|
|
60660
|
+
if (await this.splitOversized(ctx, content, pendingAtFlushStart, hardThreshold, reason))
|
|
60618
60661
|
return;
|
|
60619
60662
|
if (this.state.currentPostId) {
|
|
60620
60663
|
const postId = this.state.currentPostId;
|
|
60621
60664
|
await this.tryUpdatePost(ctx, postId, content, "handleSplit", { reason: "no_break_before_code_block", contentLength: content.length }, { reason: "no_break_before_code_block_failed" }, () => {
|
|
60622
60665
|
this.state.currentPostContent = content;
|
|
60623
60666
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60624
|
-
}, () =>
|
|
60625
|
-
this.state.currentPostId = null;
|
|
60626
|
-
this.state.currentPostContent = "";
|
|
60627
|
-
});
|
|
60667
|
+
}, () => this.onInPlaceUpdateFailed(reason));
|
|
60628
60668
|
}
|
|
60629
60669
|
return;
|
|
60630
60670
|
}
|
|
@@ -60636,11 +60676,11 @@ ${this.state.pendingContent}` : "");
|
|
|
60636
60676
|
const postId = this.state.currentPostId;
|
|
60637
60677
|
await this.tryUpdatePost(ctx, postId, firstPart, "handleSplit", { reason: "split_first_part", firstPartLength: firstPart.length, remainderLength: remainder.length }, { reason: "split_first_part_failed" }, () => {
|
|
60638
60678
|
firstPartLanded = true;
|
|
60639
|
-
}, () => {
|
|
60640
|
-
|
|
60641
|
-
|
|
60642
|
-
|
|
60643
|
-
}
|
|
60679
|
+
}, () => {});
|
|
60680
|
+
if (!firstPartLanded) {
|
|
60681
|
+
this.onInPlaceUpdateFailed(reason);
|
|
60682
|
+
return;
|
|
60683
|
+
}
|
|
60644
60684
|
}
|
|
60645
60685
|
this.state.currentPostId = null;
|
|
60646
60686
|
this.state.currentPostContent = "";
|
|
@@ -60673,6 +60713,7 @@ ${content}` : header : content;
|
|
|
60673
60713
|
if (bumpedPostId) {
|
|
60674
60714
|
adoptAsHeaderPost(bumpedPostId);
|
|
60675
60715
|
this.state.currentPostId = bumpedPostId;
|
|
60716
|
+
this.state.updateFailures = 0;
|
|
60676
60717
|
this.state.currentPostContent = content;
|
|
60677
60718
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60678
60719
|
ctx.threadLogger?.logExecutor("content", "create", bumpedPostId, {
|
|
@@ -60689,6 +60730,7 @@ ${content}` : header : content;
|
|
|
60689
60730
|
const post = await ctx.createPost(rendered, { type: "content" });
|
|
60690
60731
|
adoptAsHeaderPost(post.id);
|
|
60691
60732
|
this.state.currentPostId = post.id;
|
|
60733
|
+
this.state.updateFailures = 0;
|
|
60692
60734
|
this.state.currentPostContent = content;
|
|
60693
60735
|
this.clearFlushedContent(pendingAtFlushStart);
|
|
60694
60736
|
ctx.logger.debug(`Created post ${formatShortId(post.id)}`);
|
|
@@ -60714,6 +60756,11 @@ ${content}` : header : content;
|
|
|
60714
60756
|
}
|
|
60715
60757
|
}
|
|
60716
60758
|
var FENCE_LINE = /^```.*$/gm;
|
|
60759
|
+
var MAX_IN_PLACE_UPDATE_FAILURES = 3;
|
|
60760
|
+
function openFenceOf(text) {
|
|
60761
|
+
const fences = text.match(FENCE_LINE) ?? [];
|
|
60762
|
+
return fences.length % 2 === 1 ? fences[fences.length - 1] : null;
|
|
60763
|
+
}
|
|
60717
60764
|
var FENCE_CLOSE = "\n```";
|
|
60718
60765
|
function splitByLength(text, max) {
|
|
60719
60766
|
const room = max - FENCE_CLOSE.length;
|
|
@@ -71889,7 +71936,7 @@ class SlackClient extends BasePlatformClient {
|
|
|
71889
71936
|
token: this.botToken,
|
|
71890
71937
|
channelId: this.channelId,
|
|
71891
71938
|
allowedUsers: this.allowedUsers,
|
|
71892
|
-
|
|
71939
|
+
apiUrl: this.apiUrl,
|
|
71893
71940
|
outboundFiles: this.outboundFiles,
|
|
71894
71941
|
mcpServers: this.mcpServers,
|
|
71895
71942
|
strictMcpConfig: this.strictMcpConfig,
|
|
@@ -72197,6 +72244,14 @@ var apiLog = createLogger("mm-api");
|
|
|
72197
72244
|
|
|
72198
72245
|
// src/platform/slack/mcp-platform-api.ts
|
|
72199
72246
|
init_logger();
|
|
72247
|
+
var READ_METHODS = new Set([
|
|
72248
|
+
"users.info",
|
|
72249
|
+
"conversations.history",
|
|
72250
|
+
"conversations.replies",
|
|
72251
|
+
"conversations.info",
|
|
72252
|
+
"conversations.members",
|
|
72253
|
+
"reactions.get"
|
|
72254
|
+
]);
|
|
72200
72255
|
// src/session/manager.ts
|
|
72201
72256
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
72202
72257
|
|
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -3547,6 +3547,22 @@ var require_utils = __commonJS(function(exports, module) {
|
|
|
3547
3547
|
}
|
|
3548
3548
|
return output;
|
|
3549
3549
|
}
|
|
3550
|
+
function recomposeZonedIPv6Host(component) {
|
|
3551
|
+
const zone = component.ipv6Zone;
|
|
3552
|
+
if (zone === undefined)
|
|
3553
|
+
return;
|
|
3554
|
+
const host = component.host;
|
|
3555
|
+
const separator = host.indexOf("%");
|
|
3556
|
+
if (separator === -1)
|
|
3557
|
+
return;
|
|
3558
|
+
const address = host.slice(0, separator);
|
|
3559
|
+
const escaped = address + "%25" + zone;
|
|
3560
|
+
const derived = normalizeIPv6("[" + escaped + "]");
|
|
3561
|
+
if (derived.isIPV6 === true && derived.host === host) {
|
|
3562
|
+
return "[" + escaped + "]";
|
|
3563
|
+
}
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3550
3566
|
function recomposeAuthority(component) {
|
|
3551
3567
|
const uriTokens = [];
|
|
3552
3568
|
if (component.userinfo !== undefined) {
|
|
@@ -3556,15 +3572,20 @@ var require_utils = __commonJS(function(exports, module) {
|
|
|
3556
3572
|
if (component.host !== undefined) {
|
|
3557
3573
|
let host = component.host;
|
|
3558
3574
|
if (!isIPv4(host)) {
|
|
3559
|
-
|
|
3560
|
-
if (
|
|
3561
|
-
host =
|
|
3562
|
-
ipV6res = normalizeIPv6(host);
|
|
3563
|
-
}
|
|
3564
|
-
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3565
|
-
host = `[${ipV6res.escapedHost}]`;
|
|
3575
|
+
const zonedHost = recomposeZonedIPv6Host(component);
|
|
3576
|
+
if (zonedHost !== undefined) {
|
|
3577
|
+
host = zonedHost;
|
|
3566
3578
|
} else {
|
|
3567
|
-
|
|
3579
|
+
let ipV6res = normalizeIPv6(host);
|
|
3580
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
3581
|
+
host = normalizePercentEncoding(host, true);
|
|
3582
|
+
ipV6res = normalizeIPv6(host);
|
|
3583
|
+
}
|
|
3584
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
3585
|
+
host = `[${ipV6res.escapedHost}]`;
|
|
3586
|
+
} else {
|
|
3587
|
+
host = reescapeHostDelimiters(host, false);
|
|
3588
|
+
}
|
|
3568
3589
|
}
|
|
3569
3590
|
}
|
|
3570
3591
|
uriTokens.push(host);
|
|
@@ -3777,6 +3798,7 @@ var require_schemes = __commonJS(function(exports, module) {
|
|
|
3777
3798
|
var HEX_PAIR = /^[\da-f]{2}$/iu;
|
|
3778
3799
|
var MAILTO_DOMAIN_LITERAL = /^\[[\x21-\x5A\x5E-\x7E]*\]$/u;
|
|
3779
3800
|
var MAILTO_DOMAIN_ERROR = "URI mailto has an invalid recipient domain.";
|
|
3801
|
+
var MAILTO_AUTHORITY_ERROR = "URI mailto must not have an authority component.";
|
|
3780
3802
|
var HAS_SURROGATE = /[\uD800-\uDFFF]/u;
|
|
3781
3803
|
function decodeHex(str) {
|
|
3782
3804
|
if (typeof str !== "string" || str.indexOf("%") === -1) {
|
|
@@ -3891,30 +3913,37 @@ var require_schemes = __commonJS(function(exports, module) {
|
|
|
3891
3913
|
mailtoComponent.error = mailtoComponent.error || "URI mailto has malformed header fields.";
|
|
3892
3914
|
continue;
|
|
3893
3915
|
}
|
|
3894
|
-
const name = eqIdx === -1 ? token : token.slice(0, eqIdx);
|
|
3916
|
+
const name = decodeHex(eqIdx === -1 ? token : token.slice(0, eqIdx));
|
|
3917
|
+
const normalizedName = name.toLowerCase();
|
|
3895
3918
|
const value = eqIdx === -1 ? "" : token.slice(eqIdx + 1);
|
|
3896
|
-
if (
|
|
3919
|
+
if (normalizedName === "to") {
|
|
3897
3920
|
const addrs = value.split(",");
|
|
3898
3921
|
for (let j = 0;j < addrs.length; j++)
|
|
3899
3922
|
to.push(addrs[j]);
|
|
3900
3923
|
continue;
|
|
3901
3924
|
}
|
|
3902
|
-
if (
|
|
3925
|
+
if (normalizedName === "subject") {
|
|
3903
3926
|
mailtoComponent.subject = decodeHex(value);
|
|
3904
3927
|
continue;
|
|
3905
3928
|
}
|
|
3906
|
-
if (
|
|
3929
|
+
if (normalizedName === "body") {
|
|
3907
3930
|
mailtoComponent.body = decodeHex(value);
|
|
3908
3931
|
continue;
|
|
3909
3932
|
}
|
|
3910
3933
|
if (headers === null)
|
|
3911
3934
|
headers = Object.create(null);
|
|
3912
|
-
headers[
|
|
3935
|
+
headers[name] = decodeHex(value);
|
|
3913
3936
|
}
|
|
3914
3937
|
if (headers !== null)
|
|
3915
3938
|
mailtoComponent.headers = headers;
|
|
3916
3939
|
}
|
|
3917
3940
|
mailtoComponent.query = undefined;
|
|
3941
|
+
if (to.length > 0 && (mailtoComponent.userinfo !== undefined || mailtoComponent.host !== undefined || mailtoComponent.port !== undefined)) {
|
|
3942
|
+
mailtoComponent.userinfo = undefined;
|
|
3943
|
+
mailtoComponent.host = undefined;
|
|
3944
|
+
mailtoComponent.port = undefined;
|
|
3945
|
+
mailtoComponent.error = mailtoComponent.error || MAILTO_AUTHORITY_ERROR;
|
|
3946
|
+
}
|
|
3918
3947
|
for (let i = 0;i < to.length; i++) {
|
|
3919
3948
|
const rawAddr = to[i];
|
|
3920
3949
|
const atIdx = rawAddr.lastIndexOf("@");
|
|
@@ -3931,10 +3960,35 @@ var require_schemes = __commonJS(function(exports, module) {
|
|
|
3931
3960
|
mailtoComponent.to = to;
|
|
3932
3961
|
return mailtoComponent;
|
|
3933
3962
|
}
|
|
3963
|
+
function mailtoEncodeHeaderName(name) {
|
|
3964
|
+
return encodeWithAllow(name.replace(/%/gu, "%25"), HFNAME);
|
|
3965
|
+
}
|
|
3934
3966
|
function mailtoSerialize(component, options) {
|
|
3935
3967
|
const mailtoComponent = component;
|
|
3936
3968
|
const to = Array.isArray(mailtoComponent.to) ? mailtoComponent.to.slice() : [];
|
|
3969
|
+
const sourceHeaders = mailtoComponent.headers && typeof mailtoComponent.headers === "object" ? Object.assign(Object.create(null), mailtoComponent.headers) : Object.create(null);
|
|
3970
|
+
const headers = Object.create(null);
|
|
3971
|
+
for (const name in sourceHeaders) {
|
|
3972
|
+
const normalizedName = name.toLowerCase();
|
|
3973
|
+
if (normalizedName === "to") {
|
|
3974
|
+
const addrs = String(sourceHeaders[name]).split(",");
|
|
3975
|
+
for (let i = 0;i < addrs.length; i++)
|
|
3976
|
+
to.push(addrs[i]);
|
|
3977
|
+
} else if (normalizedName === "subject" || normalizedName === "body") {
|
|
3978
|
+
headers[normalizedName] = sourceHeaders[name];
|
|
3979
|
+
} else {
|
|
3980
|
+
headers[name] = sourceHeaders[name];
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
if (mailtoComponent.subject !== undefined)
|
|
3984
|
+
headers.subject = mailtoComponent.subject;
|
|
3985
|
+
if (mailtoComponent.body !== undefined)
|
|
3986
|
+
headers.body = mailtoComponent.body;
|
|
3987
|
+
mailtoComponent.headers = headers;
|
|
3937
3988
|
if (to.length) {
|
|
3989
|
+
mailtoComponent.userinfo = undefined;
|
|
3990
|
+
mailtoComponent.host = undefined;
|
|
3991
|
+
mailtoComponent.port = undefined;
|
|
3938
3992
|
for (let i = 0;i < to.length; i++) {
|
|
3939
3993
|
const addr = String(to[i]);
|
|
3940
3994
|
const atIdx = addr.lastIndexOf("@");
|
|
@@ -3950,18 +4004,12 @@ var require_schemes = __commonJS(function(exports, module) {
|
|
|
3950
4004
|
} else {
|
|
3951
4005
|
mailtoComponent.path = undefined;
|
|
3952
4006
|
}
|
|
3953
|
-
const headers = mailtoComponent.headers && typeof mailtoComponent.headers === "object" ? Object.assign(Object.create(null), mailtoComponent.headers) : Object.create(null);
|
|
3954
|
-
if (mailtoComponent.subject)
|
|
3955
|
-
headers.subject = mailtoComponent.subject;
|
|
3956
|
-
if (mailtoComponent.body)
|
|
3957
|
-
headers.body = mailtoComponent.body;
|
|
3958
|
-
mailtoComponent.headers = headers;
|
|
3959
4007
|
let query = "";
|
|
3960
4008
|
let count = 0;
|
|
3961
4009
|
for (const name in headers) {
|
|
3962
4010
|
if (count++ !== 0)
|
|
3963
4011
|
query += "&";
|
|
3964
|
-
query +=
|
|
4012
|
+
query += mailtoEncodeHeaderName(name) + "=" + encodeWithAllow(String(headers[name]), HFNAME);
|
|
3965
4013
|
}
|
|
3966
4014
|
if (count !== 0) {
|
|
3967
4015
|
mailtoComponent.query = query;
|
|
@@ -4054,6 +4102,12 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4054
4102
|
schemelessOptions.skipEscape = true;
|
|
4055
4103
|
return serialize(resolved, schemelessOptions);
|
|
4056
4104
|
}
|
|
4105
|
+
function copyHost(target, source) {
|
|
4106
|
+
target.host = source.host;
|
|
4107
|
+
if (source.ipv6Zone !== undefined) {
|
|
4108
|
+
target.ipv6Zone = source.ipv6Zone;
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4057
4111
|
function resolveComponent(base, relative, options, skipNormalization) {
|
|
4058
4112
|
const target = {};
|
|
4059
4113
|
if (!skipNormalization) {
|
|
@@ -4064,14 +4118,14 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4064
4118
|
if (!options.tolerant && relative.scheme) {
|
|
4065
4119
|
target.scheme = relative.scheme;
|
|
4066
4120
|
target.userinfo = relative.userinfo;
|
|
4067
|
-
target
|
|
4121
|
+
copyHost(target, relative);
|
|
4068
4122
|
target.port = relative.port;
|
|
4069
4123
|
target.path = removeDotSegments(relative.path || "");
|
|
4070
4124
|
target.query = relative.query;
|
|
4071
4125
|
} else {
|
|
4072
4126
|
if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) {
|
|
4073
4127
|
target.userinfo = relative.userinfo;
|
|
4074
|
-
target
|
|
4128
|
+
copyHost(target, relative);
|
|
4075
4129
|
target.port = relative.port;
|
|
4076
4130
|
target.path = removeDotSegments(relative.path || "");
|
|
4077
4131
|
target.query = relative.query;
|
|
@@ -4099,7 +4153,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4099
4153
|
target.query = relative.query;
|
|
4100
4154
|
}
|
|
4101
4155
|
target.userinfo = base.userinfo;
|
|
4102
|
-
target
|
|
4156
|
+
copyHost(target, base);
|
|
4103
4157
|
target.port = base.port;
|
|
4104
4158
|
}
|
|
4105
4159
|
target.scheme = base.scheme;
|
|
@@ -4115,6 +4169,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4115
4169
|
function serialize(cmpts, opts) {
|
|
4116
4170
|
const component = {
|
|
4117
4171
|
host: cmpts.host,
|
|
4172
|
+
ipv6Zone: cmpts.ipv6Zone,
|
|
4118
4173
|
scheme: cmpts.scheme,
|
|
4119
4174
|
userinfo: cmpts.userinfo,
|
|
4120
4175
|
port: cmpts.port,
|
|
@@ -4311,6 +4366,9 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4311
4366
|
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
4312
4367
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4313
4368
|
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4369
|
+
if (isIP && ipv6result.isIPV6 === true && parsed.host.indexOf("%") !== -1) {
|
|
4370
|
+
parsed.ipv6Zone = parsed.host.slice(parsed.host.indexOf("%") + 1);
|
|
4371
|
+
}
|
|
4314
4372
|
if (malformedIPLiteral) {
|
|
4315
4373
|
parsed.error = parsed.error || "URI host is malformed.";
|
|
4316
4374
|
malformedAuthorityOrPort = true;
|
|
@@ -4335,13 +4393,14 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
4335
4393
|
if (!malformedIPLiteral) {
|
|
4336
4394
|
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
4337
4395
|
}
|
|
4338
|
-
if (
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4343
|
-
}
|
|
4396
|
+
if (uri.indexOf("%") !== -1 && parsed.host !== undefined && !malformedIPLiteral) {
|
|
4397
|
+
let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4398
|
+
if (!isIP) {
|
|
4399
|
+
host = normalizePercentEncoding(host.toLowerCase());
|
|
4344
4400
|
}
|
|
4401
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4402
|
+
}
|
|
4403
|
+
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
4345
4404
|
if (parsed.path) {
|
|
4346
4405
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
4347
4406
|
}
|
|
@@ -12663,11 +12722,14 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
12663
12722
|
});
|
|
12664
12723
|
|
|
12665
12724
|
// src/utils/emoji.ts
|
|
12725
|
+
function withoutSkinTone(emoji) {
|
|
12726
|
+
return emoji.replace(/::skin-tone-\d$/, "").replace(/(?<=.)_(?:light|medium_light|medium|medium_dark|dark)_skin_tone$/, "");
|
|
12727
|
+
}
|
|
12666
12728
|
function isApprovalEmoji(emoji) {
|
|
12667
|
-
return APPROVAL_EMOJIS.includes(emoji);
|
|
12729
|
+
return APPROVAL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
12668
12730
|
}
|
|
12669
12731
|
function isAllowAllEmoji(emoji) {
|
|
12670
|
-
return ALLOW_ALL_EMOJIS.includes(emoji);
|
|
12732
|
+
return ALLOW_ALL_EMOJIS.includes(withoutSkinTone(emoji));
|
|
12671
12733
|
}
|
|
12672
12734
|
var APPROVAL_EMOJIS, DENIAL_EMOJIS, ALLOW_ALL_EMOJIS;
|
|
12673
12735
|
var init_emoji = __esm(() => {
|
|
@@ -17234,11 +17296,23 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17234
17296
|
this._isServer = false;
|
|
17235
17297
|
this._redirects = 0;
|
|
17236
17298
|
if (protocols === undefined) {
|
|
17237
|
-
protocols
|
|
17299
|
+
if (!options || options.protocols === undefined) {
|
|
17300
|
+
protocols = [];
|
|
17301
|
+
} else if (Array.isArray(options.protocols)) {
|
|
17302
|
+
protocols = options.protocols;
|
|
17303
|
+
} else {
|
|
17304
|
+
protocols = [options.protocols];
|
|
17305
|
+
}
|
|
17238
17306
|
} else if (!Array.isArray(protocols)) {
|
|
17239
17307
|
if (typeof protocols === "object" && protocols !== null) {
|
|
17240
17308
|
options = protocols;
|
|
17241
|
-
protocols
|
|
17309
|
+
if (options.protocols === undefined) {
|
|
17310
|
+
protocols = [];
|
|
17311
|
+
} else if (Array.isArray(options.protocols)) {
|
|
17312
|
+
protocols = options.protocols;
|
|
17313
|
+
} else {
|
|
17314
|
+
protocols = [options.protocols];
|
|
17315
|
+
}
|
|
17242
17316
|
} else {
|
|
17243
17317
|
protocols = [protocols];
|
|
17244
17318
|
}
|
|
@@ -17357,7 +17431,6 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17357
17431
|
}
|
|
17358
17432
|
return;
|
|
17359
17433
|
}
|
|
17360
|
-
this._readyState = WebSocket.CLOSING;
|
|
17361
17434
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
17362
17435
|
if (err)
|
|
17363
17436
|
return;
|
|
@@ -17366,6 +17439,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17366
17439
|
this._socket.end();
|
|
17367
17440
|
}
|
|
17368
17441
|
});
|
|
17442
|
+
this._readyState = WebSocket.CLOSING;
|
|
17369
17443
|
setCloseTimer(this);
|
|
17370
17444
|
}
|
|
17371
17445
|
pause() {
|
|
@@ -17553,6 +17627,7 @@ var require_websocket = __commonJS(function(exports, module) {
|
|
|
17553
17627
|
socketPath: undefined,
|
|
17554
17628
|
hostname: undefined,
|
|
17555
17629
|
protocol: undefined,
|
|
17630
|
+
protocols: undefined,
|
|
17556
17631
|
timeout: undefined,
|
|
17557
17632
|
method: "GET",
|
|
17558
17633
|
host: undefined,
|
|
@@ -40251,16 +40326,49 @@ async function uploadFileSlack(args) {
|
|
|
40251
40326
|
|
|
40252
40327
|
// src/platform/slack/mcp-platform-api.ts
|
|
40253
40328
|
var SLACK_API_BASE = "https://slack.com/api";
|
|
40254
|
-
|
|
40255
|
-
|
|
40256
|
-
|
|
40257
|
-
|
|
40258
|
-
|
|
40259
|
-
|
|
40260
|
-
|
|
40261
|
-
|
|
40262
|
-
|
|
40263
|
-
|
|
40329
|
+
var READ_METHODS = new Set([
|
|
40330
|
+
"users.info",
|
|
40331
|
+
"conversations.history",
|
|
40332
|
+
"conversations.replies",
|
|
40333
|
+
"conversations.info",
|
|
40334
|
+
"conversations.members",
|
|
40335
|
+
"reactions.get"
|
|
40336
|
+
]);
|
|
40337
|
+
|
|
40338
|
+
class SlackRateLimitError extends Error {
|
|
40339
|
+
retryAfterMs;
|
|
40340
|
+
constructor(retryAfterMs) {
|
|
40341
|
+
super(`Slack API error: 429 rate limited, retry after ${retryAfterMs} ms`);
|
|
40342
|
+
this.retryAfterMs = retryAfterMs;
|
|
40343
|
+
}
|
|
40344
|
+
}
|
|
40345
|
+
async function slackApi(method, token, args, apiBase = SLACK_API_BASE) {
|
|
40346
|
+
let response;
|
|
40347
|
+
if (READ_METHODS.has(method)) {
|
|
40348
|
+
const query = new URLSearchParams;
|
|
40349
|
+
for (const [key, value] of Object.entries(args ?? {})) {
|
|
40350
|
+
if (value !== undefined && value !== null)
|
|
40351
|
+
query.set(key, String(value));
|
|
40352
|
+
}
|
|
40353
|
+
const qs = query.toString();
|
|
40354
|
+
response = await fetch(`${apiBase}/${method}${qs ? `?${qs}` : ""}`, {
|
|
40355
|
+
method: "GET",
|
|
40356
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
40357
|
+
});
|
|
40358
|
+
} else {
|
|
40359
|
+
response = await fetch(`${apiBase}/${method}`, {
|
|
40360
|
+
method: "POST",
|
|
40361
|
+
headers: {
|
|
40362
|
+
Authorization: `Bearer ${token}`,
|
|
40363
|
+
"Content-Type": "application/json; charset=utf-8"
|
|
40364
|
+
},
|
|
40365
|
+
body: args ? JSON.stringify(args) : undefined
|
|
40366
|
+
});
|
|
40367
|
+
}
|
|
40368
|
+
if (response.status === 429) {
|
|
40369
|
+
const seconds = Number(response.headers.get("retry-after"));
|
|
40370
|
+
throw new SlackRateLimitError(Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : 5000);
|
|
40371
|
+
}
|
|
40264
40372
|
if (!response.ok) {
|
|
40265
40373
|
throw new Error(`Slack API error: ${response.status} ${response.statusText}`);
|
|
40266
40374
|
}
|
|
@@ -40272,6 +40380,9 @@ async function slackApi(method, token, body) {
|
|
|
40272
40380
|
}
|
|
40273
40381
|
|
|
40274
40382
|
class SlackMcpPlatformApi {
|
|
40383
|
+
slackApi(method, token, args) {
|
|
40384
|
+
return slackApi(method, token, args, this.config.apiUrl || SLACK_API_BASE);
|
|
40385
|
+
}
|
|
40275
40386
|
config;
|
|
40276
40387
|
formatter = new SlackFormatter;
|
|
40277
40388
|
botUserIdCache = null;
|
|
@@ -40287,7 +40398,7 @@ class SlackMcpPlatformApi {
|
|
|
40287
40398
|
return this.botUserIdCache;
|
|
40288
40399
|
}
|
|
40289
40400
|
mcpLogger.debug("Fetching bot user ID via auth.test...");
|
|
40290
|
-
const response = await slackApi("auth.test", this.config.botToken);
|
|
40401
|
+
const response = await this.slackApi("auth.test", this.config.botToken);
|
|
40291
40402
|
this.botUserIdCache = response.user_id;
|
|
40292
40403
|
mcpLogger.debug(`Bot user ID: ${response.user_id}`);
|
|
40293
40404
|
return response.user_id;
|
|
@@ -40295,7 +40406,7 @@ class SlackMcpPlatformApi {
|
|
|
40295
40406
|
async getUsername(userId) {
|
|
40296
40407
|
try {
|
|
40297
40408
|
mcpLogger.debug(`Looking up username for user ${userId}`);
|
|
40298
|
-
const response = await slackApi("users.info", this.config.botToken, { user: userId });
|
|
40409
|
+
const response = await this.slackApi("users.info", this.config.botToken, { user: userId });
|
|
40299
40410
|
const username = response.user?.name;
|
|
40300
40411
|
if (username) {
|
|
40301
40412
|
mcpLogger.debug(`User ${userId} is @${username}`);
|
|
@@ -40317,7 +40428,7 @@ class SlackMcpPlatformApi {
|
|
|
40317
40428
|
}
|
|
40318
40429
|
async createInteractivePost(message, reactions, threadTs) {
|
|
40319
40430
|
mcpLogger.debug(`Creating interactive post with ${reactions.length} reaction options`);
|
|
40320
|
-
const response = await slackApi("chat.postMessage", this.config.botToken, {
|
|
40431
|
+
const response = await this.slackApi("chat.postMessage", this.config.botToken, {
|
|
40321
40432
|
channel: this.config.channelId,
|
|
40322
40433
|
text: message,
|
|
40323
40434
|
thread_ts: resolvePostThreadId(threadTs || this.config.threadTs),
|
|
@@ -40328,7 +40439,7 @@ class SlackMcpPlatformApi {
|
|
|
40328
40439
|
for (const emoji of reactions) {
|
|
40329
40440
|
try {
|
|
40330
40441
|
const emojiName = getEmojiName(emoji);
|
|
40331
|
-
await slackApi("reactions.add", this.config.botToken, {
|
|
40442
|
+
await this.slackApi("reactions.add", this.config.botToken, {
|
|
40332
40443
|
channel: this.config.channelId,
|
|
40333
40444
|
timestamp: messageTs,
|
|
40334
40445
|
name: emojiName
|
|
@@ -40342,120 +40453,54 @@ class SlackMcpPlatformApi {
|
|
|
40342
40453
|
}
|
|
40343
40454
|
async updatePost(postId, message) {
|
|
40344
40455
|
mcpLogger.debug(`Updating post ${postId}`);
|
|
40345
|
-
await slackApi("chat.update", this.config.botToken, {
|
|
40456
|
+
await this.slackApi("chat.update", this.config.botToken, {
|
|
40346
40457
|
channel: this.config.channelId,
|
|
40347
40458
|
ts: postId,
|
|
40348
40459
|
text: message,
|
|
40349
40460
|
mrkdwn: true
|
|
40350
40461
|
});
|
|
40351
40462
|
}
|
|
40463
|
+
reactionPollMs = 2000;
|
|
40464
|
+
reactionSlowAfterMs = 30000;
|
|
40465
|
+
reactionSlowPollMs = 5000;
|
|
40466
|
+
seenTtlMs = 30000;
|
|
40467
|
+
seenReactions = new Map;
|
|
40352
40468
|
async waitForReaction(postId, botUserId, timeoutMs) {
|
|
40353
|
-
|
|
40354
|
-
|
|
40355
|
-
|
|
40356
|
-
|
|
40357
|
-
|
|
40358
|
-
|
|
40359
|
-
|
|
40360
|
-
|
|
40361
|
-
|
|
40362
|
-
|
|
40363
|
-
|
|
40364
|
-
|
|
40365
|
-
|
|
40366
|
-
|
|
40367
|
-
|
|
40368
|
-
|
|
40369
|
-
|
|
40370
|
-
|
|
40371
|
-
|
|
40372
|
-
mcpLogger.debug(`Connecting to Socket Mode: ${wsUrl.substring(0, 50)}...`);
|
|
40373
|
-
ws = new WS(wsUrl);
|
|
40374
|
-
ws.onopen = () => {
|
|
40375
|
-
mcpLogger.debug("Socket Mode WebSocket connected");
|
|
40376
|
-
};
|
|
40377
|
-
ws.onmessage = (event) => {
|
|
40378
|
-
if (resolved)
|
|
40379
|
-
return;
|
|
40380
|
-
try {
|
|
40381
|
-
const data = typeof event.data === "string" ? event.data : event.data.toString();
|
|
40382
|
-
const socketEvent = JSON.parse(data);
|
|
40383
|
-
if (socketEvent.envelope_id) {
|
|
40384
|
-
ws?.send(JSON.stringify({ envelope_id: socketEvent.envelope_id }));
|
|
40385
|
-
}
|
|
40386
|
-
mcpLogger.debug(`Socket Mode event type: ${socketEvent.type}`);
|
|
40387
|
-
if (socketEvent.type === "events_api" && socketEvent.payload?.event) {
|
|
40388
|
-
const slackEvent = socketEvent.payload.event;
|
|
40389
|
-
if (slackEvent.type === "reaction_added") {
|
|
40390
|
-
const item = slackEvent.item;
|
|
40391
|
-
if (item?.type !== "message" || item.ts !== postId || item.channel !== this.config.channelId) {
|
|
40392
|
-
return;
|
|
40393
|
-
}
|
|
40394
|
-
if (slackEvent.user === botUserId) {
|
|
40395
|
-
mcpLogger.debug("Ignoring bot's own reaction");
|
|
40396
|
-
return;
|
|
40397
|
-
}
|
|
40398
|
-
const emojiName = slackEvent.reaction || "";
|
|
40399
|
-
const userId = slackEvent.user || "";
|
|
40400
|
-
mcpLogger.debug(`Reaction received: :${emojiName}: from user: ${userId}`);
|
|
40401
|
-
resolved = true;
|
|
40402
|
-
clearTimeout(timeout);
|
|
40403
|
-
cleanup();
|
|
40404
|
-
resolve({
|
|
40405
|
-
postId: item.ts,
|
|
40406
|
-
userId,
|
|
40407
|
-
emojiName
|
|
40408
|
-
});
|
|
40409
|
-
}
|
|
40410
|
-
}
|
|
40411
|
-
if (socketEvent.type === "hello") {
|
|
40412
|
-
mcpLogger.debug("Socket Mode hello received, connection established");
|
|
40413
|
-
}
|
|
40414
|
-
if (socketEvent.type === "disconnect") {
|
|
40415
|
-
mcpLogger.debug("Socket Mode disconnect requested");
|
|
40416
|
-
if (!resolved) {
|
|
40417
|
-
resolved = true;
|
|
40418
|
-
clearTimeout(timeout);
|
|
40419
|
-
cleanup();
|
|
40420
|
-
resolve(null);
|
|
40421
|
-
}
|
|
40422
|
-
}
|
|
40423
|
-
} catch (err) {
|
|
40424
|
-
mcpLogger.debug(`Error parsing Socket Mode message: ${err}`);
|
|
40425
|
-
}
|
|
40426
|
-
};
|
|
40427
|
-
ws.onerror = (event) => {
|
|
40428
|
-
mcpLogger.error(`Socket Mode WebSocket error: ${formatWebSocketError(event)}`);
|
|
40429
|
-
if (!resolved) {
|
|
40430
|
-
resolved = true;
|
|
40431
|
-
clearTimeout(timeout);
|
|
40432
|
-
cleanup();
|
|
40433
|
-
resolve(null);
|
|
40434
|
-
}
|
|
40435
|
-
};
|
|
40436
|
-
ws.onclose = () => {
|
|
40437
|
-
mcpLogger.debug("Socket Mode WebSocket closed");
|
|
40438
|
-
if (!resolved) {
|
|
40439
|
-
resolved = true;
|
|
40440
|
-
clearTimeout(timeout);
|
|
40441
|
-
resolve(null);
|
|
40469
|
+
const started = Date.now();
|
|
40470
|
+
const deadline = started + timeoutMs;
|
|
40471
|
+
let warned = false;
|
|
40472
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Math.min(ms, deadline - Date.now()))));
|
|
40473
|
+
while (Date.now() < deadline) {
|
|
40474
|
+
let wait = Date.now() - started >= this.reactionSlowAfterMs ? this.reactionSlowPollMs : this.reactionPollMs;
|
|
40475
|
+
try {
|
|
40476
|
+
const response = await this.slackApi("reactions.get", this.config.botToken, { channel: this.config.channelId, timestamp: postId, full: true });
|
|
40477
|
+
for (const reaction of response.message?.reactions ?? []) {
|
|
40478
|
+
for (const userId of reaction.users ?? []) {
|
|
40479
|
+
if (userId === botUserId)
|
|
40480
|
+
continue;
|
|
40481
|
+
const key = `${postId}|${userId}|${reaction.name}`;
|
|
40482
|
+
const seenAt = this.seenReactions.get(key);
|
|
40483
|
+
if (seenAt !== undefined && Date.now() - seenAt < this.seenTtlMs)
|
|
40484
|
+
continue;
|
|
40485
|
+
this.seenReactions.set(key, Date.now());
|
|
40486
|
+
mcpLogger.debug(`Reaction received: :${reaction.name}: from user: ${userId}`);
|
|
40487
|
+
return { postId, userId, emojiName: reaction.name };
|
|
40442
40488
|
}
|
|
40443
|
-
};
|
|
40444
|
-
}).catch((err) => {
|
|
40445
|
-
mcpLogger.error(`Failed to get Socket Mode URL: ${err}`);
|
|
40446
|
-
if (!resolved) {
|
|
40447
|
-
resolved = true;
|
|
40448
|
-
clearTimeout(timeout);
|
|
40449
|
-
resolve(null);
|
|
40450
40489
|
}
|
|
40451
|
-
})
|
|
40452
|
-
|
|
40453
|
-
|
|
40454
|
-
|
|
40455
|
-
|
|
40456
|
-
|
|
40457
|
-
|
|
40458
|
-
|
|
40490
|
+
} catch (err) {
|
|
40491
|
+
if (err instanceof SlackRateLimitError) {
|
|
40492
|
+
wait = err.retryAfterMs;
|
|
40493
|
+
} else if (!warned) {
|
|
40494
|
+
warned = true;
|
|
40495
|
+
mcpLogger.warn(`reactions.get failed, will keep retrying: ${err}`);
|
|
40496
|
+
} else {
|
|
40497
|
+
mcpLogger.debug(`reactions.get failed, retrying: ${err}`);
|
|
40498
|
+
}
|
|
40499
|
+
}
|
|
40500
|
+
await sleep(wait);
|
|
40501
|
+
}
|
|
40502
|
+
mcpLogger.debug(`Reaction wait timed out after ${timeoutMs}ms`);
|
|
40503
|
+
return null;
|
|
40459
40504
|
}
|
|
40460
40505
|
async uploadFile(filePath, threadId, options) {
|
|
40461
40506
|
const filename = sanitizeFilename(options?.filename ?? filePath);
|
|
@@ -40466,14 +40511,15 @@ class SlackMcpPlatformApi {
|
|
|
40466
40511
|
threadTs: threadId,
|
|
40467
40512
|
filePath,
|
|
40468
40513
|
filename,
|
|
40469
|
-
caption: options?.caption
|
|
40514
|
+
caption: options?.caption,
|
|
40515
|
+
apiUrl: this.config.apiUrl
|
|
40470
40516
|
});
|
|
40471
40517
|
return { postId: result.postId };
|
|
40472
40518
|
}
|
|
40473
40519
|
async readPost(postId) {
|
|
40474
40520
|
mcpLogger.debug(`readPost: ts ${postId}`);
|
|
40475
40521
|
try {
|
|
40476
|
-
const response = await slackApi("conversations.history", this.config.botToken, {
|
|
40522
|
+
const response = await this.slackApi("conversations.history", this.config.botToken, {
|
|
40477
40523
|
channel: this.config.channelId,
|
|
40478
40524
|
latest: postId,
|
|
40479
40525
|
oldest: postId,
|
|
@@ -40493,7 +40539,7 @@ class SlackMcpPlatformApi {
|
|
|
40493
40539
|
async addReaction(postId, emojiName) {
|
|
40494
40540
|
const name = getEmojiName(emojiName);
|
|
40495
40541
|
mcpLogger.debug(`addReaction: :${name}: on ts ${postId}`);
|
|
40496
|
-
await slackApi("reactions.add", this.config.botToken, {
|
|
40542
|
+
await this.slackApi("reactions.add", this.config.botToken, {
|
|
40497
40543
|
channel: this.config.channelId,
|
|
40498
40544
|
timestamp: postId,
|
|
40499
40545
|
name
|
|
@@ -40502,7 +40548,7 @@ class SlackMcpPlatformApi {
|
|
|
40502
40548
|
async readThread(threadRootId, options) {
|
|
40503
40549
|
mcpLogger.debug(`readThread: ts ${threadRootId}`);
|
|
40504
40550
|
try {
|
|
40505
|
-
const response = await slackApi("conversations.replies", this.config.botToken, {
|
|
40551
|
+
const response = await this.slackApi("conversations.replies", this.config.botToken, {
|
|
40506
40552
|
channel: this.config.channelId,
|
|
40507
40553
|
ts: threadRootId,
|
|
40508
40554
|
limit: options?.limit ?? 100
|
|
@@ -40525,7 +40571,7 @@ class SlackMcpPlatformApi {
|
|
|
40525
40571
|
const limit = options?.limit ?? 20;
|
|
40526
40572
|
mcpLogger.debug(`readChannelHistory: ${channelId} (limit=${limit})`);
|
|
40527
40573
|
try {
|
|
40528
|
-
const response = await slackApi("conversations.history", this.config.botToken, {
|
|
40574
|
+
const response = await this.slackApi("conversations.history", this.config.botToken, {
|
|
40529
40575
|
channel: channelId,
|
|
40530
40576
|
limit
|
|
40531
40577
|
});
|
|
@@ -40545,7 +40591,7 @@ class SlackMcpPlatformApi {
|
|
|
40545
40591
|
async getChannelInfo(channelId) {
|
|
40546
40592
|
mcpLogger.debug(`getChannelInfo: ${channelId}`);
|
|
40547
40593
|
try {
|
|
40548
|
-
const response = await slackApi("conversations.info", this.config.botToken, { channel: channelId });
|
|
40594
|
+
const response = await this.slackApi("conversations.info", this.config.botToken, { channel: channelId });
|
|
40549
40595
|
const ch = response.channel;
|
|
40550
40596
|
const isPrivate = ch.is_private || ch.is_im || ch.is_mpim || false;
|
|
40551
40597
|
return {
|
|
@@ -40570,7 +40616,7 @@ class SlackMcpPlatformApi {
|
|
|
40570
40616
|
};
|
|
40571
40617
|
if (cursor)
|
|
40572
40618
|
params.cursor = cursor;
|
|
40573
|
-
const response = await slackApi("conversations.members", this.config.botToken, params);
|
|
40619
|
+
const response = await this.slackApi("conversations.members", this.config.botToken, params);
|
|
40574
40620
|
all.push(...response.members ?? []);
|
|
40575
40621
|
cursor = response.response_metadata?.next_cursor;
|
|
40576
40622
|
if (!cursor)
|
|
@@ -40589,7 +40635,7 @@ class SlackMcpPlatformApi {
|
|
|
40589
40635
|
return null;
|
|
40590
40636
|
}
|
|
40591
40637
|
try {
|
|
40592
|
-
const response = await slackApi("users.info", this.config.botToken, { user: id });
|
|
40638
|
+
const response = await this.slackApi("users.info", this.config.botToken, { user: id });
|
|
40593
40639
|
return { id: response.user.id, username: response.user.name ?? null };
|
|
40594
40640
|
} catch (err) {
|
|
40595
40641
|
mcpLogger.debug(`resolveRecipient ${id} failed: ${err}`);
|
|
@@ -40597,9 +40643,9 @@ class SlackMcpPlatformApi {
|
|
|
40597
40643
|
}
|
|
40598
40644
|
}
|
|
40599
40645
|
async sendDirectMessage(recipientUserId, message) {
|
|
40600
|
-
const opened = await slackApi("conversations.open", this.config.botToken, { users: recipientUserId });
|
|
40646
|
+
const opened = await this.slackApi("conversations.open", this.config.botToken, { users: recipientUserId });
|
|
40601
40647
|
const dmChannelId = opened.channel.id;
|
|
40602
|
-
const post = await slackApi("chat.postMessage", this.config.botToken, {
|
|
40648
|
+
const post = await this.slackApi("chat.postMessage", this.config.botToken, {
|
|
40603
40649
|
channel: dmChannelId,
|
|
40604
40650
|
text: message,
|
|
40605
40651
|
mrkdwn: true
|
|
@@ -41046,7 +41092,8 @@ var SKIP_STANDARD_PERMISSION_PROMPT = new Set([
|
|
|
41046
41092
|
var apiConfig = PLATFORM_TYPE === "slack" ? {
|
|
41047
41093
|
platformType: "slack",
|
|
41048
41094
|
botToken: PLATFORM_TOKEN,
|
|
41049
|
-
appToken:
|
|
41095
|
+
appToken: "",
|
|
41096
|
+
apiUrl: process.env.PLATFORM_API_URL || undefined,
|
|
41050
41097
|
channelId: PLATFORM_CHANNEL_ID,
|
|
41051
41098
|
threadTs: PLATFORM_THREAD_ID || undefined,
|
|
41052
41099
|
allowedUsers: ALLOWED_USERS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-threads",
|
|
3
|
-
"version": "1.39.
|
|
3
|
+
"version": "1.39.3",
|
|
4
4
|
"description": "Run Claude Code from Slack or Mattermost. Sessions stream live into threads where your whole team can watch and steer.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"test:integration:slack:setup": "bun run tests/integration/fixtures/slack/mock-server.ts &",
|
|
25
25
|
"test:integration:slack:run": "TEST_PLATFORMS=slack INTEGRATION_TEST=1 bun test tests/integration/suites --timeout 120000",
|
|
26
26
|
"test:integration:slack:teardown": "pkill -f 'mock-server.ts' || true",
|
|
27
|
-
"test:integration:slack": "bun run test:integration:slack:teardown; bun run tests/integration/fixtures/slack/mock-server.ts & sleep 2 && TEST_PLATFORMS=slack INTEGRATION_TEST=1 bun test tests/integration/suites --timeout 120000; bun run test:integration:slack:teardown",
|
|
27
|
+
"test:integration:slack": "bun run test:integration:slack:teardown; bun run tests/integration/fixtures/slack/mock-server.ts & sleep 2 && TEST_PLATFORMS=slack INTEGRATION_TEST=1 bun test tests/integration/suites --timeout 120000; status=$?; bun run test:integration:slack:teardown; exit $status",
|
|
28
28
|
"test:integration:all": "bun run test:integration:setup && bun run tests/integration/fixtures/slack/mock-server.ts & sleep 2 && TEST_PLATFORMS=mattermost,slack INTEGRATION_TEST=1 bun test tests/integration/suites --timeout 120000; bun run test:integration:slack:teardown",
|
|
29
29
|
"lint": "eslint src/",
|
|
30
30
|
"lint:fix": "eslint src/ --fix",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"cross-spawn": "^7.0.6",
|
|
79
79
|
"diff": "^8.0.3",
|
|
80
80
|
"express-rate-limit": "^8.3.0",
|
|
81
|
-
"hono": "4.13.
|
|
81
|
+
"hono": "4.13.9",
|
|
82
82
|
"ink": "^6.6.0",
|
|
83
83
|
"ink-scroll-view": "^0.3.5",
|
|
84
84
|
"js-yaml": "^4.3.1",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"react": "^19.2.3",
|
|
87
87
|
"semver": "7.8.5",
|
|
88
88
|
"update-notifier": "^7.3.1",
|
|
89
|
-
"ws": "8.
|
|
89
|
+
"ws": "8.22.0",
|
|
90
90
|
"zod": "^4.3.6"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@types/ws": "^8.18.0",
|
|
103
103
|
"eslint": "^10.7.0",
|
|
104
104
|
"husky": "^9.1.7",
|
|
105
|
-
"knip": "6.
|
|
105
|
+
"knip": "6.38.0",
|
|
106
106
|
"lint-staged": "^17.0.4",
|
|
107
107
|
"prettier": "^3.9.5",
|
|
108
108
|
"react-devtools-core": "^8.0.0",
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"flatted": ">=3.4.0",
|
|
127
127
|
"picomatch": ">=2.3.2",
|
|
128
128
|
"path-to-regexp": ">=8.4.0",
|
|
129
|
-
"fast-uri": ">=4.1
|
|
129
|
+
"fast-uri": ">=4.2.1",
|
|
130
130
|
"brace-expansion": ">=5.0.9",
|
|
131
131
|
"ip-address": ">=10.3.1",
|
|
132
132
|
"ws": "$ws",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"flatted": ">=3.4.0",
|
|
140
140
|
"picomatch": ">=2.3.2",
|
|
141
141
|
"path-to-regexp": ">=8.4.0",
|
|
142
|
-
"fast-uri": ">=4.1
|
|
142
|
+
"fast-uri": ">=4.2.1",
|
|
143
143
|
"brace-expansion": ">=5.0.9",
|
|
144
144
|
"ip-address": ">=10.3.1",
|
|
145
145
|
"ws": "$ws",
|