claude-threads 1.6.0 → 1.6.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 +33 -0
- package/dist/index.js +49 -35
- package/dist/mcp/permission-server.js +29 -671
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@ 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.6.3] - 2026-04-21
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Skipped file attachments are now surfaced on session start** - When a user starts a session with an unsupported file (e.g. `.xlsx`), the bot posts the `⚠️ Some files could not be processed` warning instead of silently dropping it. The same warning now fires on the mid-thread context-prompt and worktree paths. (#325, thanks @shaders)
|
|
12
|
+
- **Recognize `.har` and `.log` as text** - Both extensions come through as `application/octet-stream` via Mattermost/Slack and were previously dropped as unsupported. (#325)
|
|
13
|
+
|
|
14
|
+
## [1.6.2] - 2026-04-20
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- **MCP server path resolution in bundled builds** - Fixes `__dirname` resolution when the project is bundled with `bun build` into a single `dist/index.js` (#316)
|
|
18
|
+
- **CLAUDE_THREADS_INTERACTIVE forwarding to daemon subprocess** - Parent no longer falsely advertises a TTY to the piped-stdio child in daemon mode (#312, #317)
|
|
19
|
+
- **Sticky message test regex** - Updated to match the actual header format (#319)
|
|
20
|
+
- **Flaky permissions integration test** - Uses pattern-based waits instead of fixed post counts to tolerate intermittent CI 500s (#320)
|
|
21
|
+
|
|
22
|
+
### Security
|
|
23
|
+
- **Override path-to-regexp to >=8.4.0** - Fixes CVE-2026-4926 (HIGH severity DoS) pulled in transitively via @modelcontextprotocol/sdk → express → router (#318)
|
|
24
|
+
- **Bump hono to 4.12.14** - Fixes GHSA-458j-xx4x-4375 (improper JSX attribute name handling in hono/jsx SSR) (#324)
|
|
25
|
+
|
|
26
|
+
### Dependencies
|
|
27
|
+
- **Bump production dependencies** - @hono/node-server, hono, @redactpii/node, react (#311, #326)
|
|
28
|
+
- **Bump dev dependencies** - @types/yazl, @types/bun, @types/node, prettier, typescript-eslint (#310, #322)
|
|
29
|
+
- **Bump CI actions** - actions/upload-pages-artifact 4 to 5 (#321)
|
|
30
|
+
|
|
31
|
+
## [1.6.1] - 2026-04-07
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
- **Bump path-to-regexp to 8.4.0** - Fixes CVE-2026-4926 (DoS via malicious route patterns) (#303)
|
|
35
|
+
|
|
36
|
+
### Dependencies
|
|
37
|
+
- **Bump production dependencies** - @hono/node-server, @modelcontextprotocol/sdk, express-rate-limit, yauzl (#308)
|
|
38
|
+
- **Bump dev dependencies** - TypeScript 5.9 to 6.0, @types/bun, @types/node, @types/react, lint-staged, typescript-eslint (#309)
|
|
39
|
+
- **Bump CI actions** - actions/configure-pages 5 to 6, actions/deploy-pages 4 to 5, schneegans/dynamic-badges-action 1.7 to 1.8 (#304, #305, #306)
|
|
40
|
+
|
|
8
41
|
## [1.6.0] - 2026-03-27
|
|
9
42
|
|
|
10
43
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -54372,11 +54372,19 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54372
54372
|
getMcpServerPath() {
|
|
54373
54373
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54374
54374
|
const __dirname4 = dirname6(__filename2);
|
|
54375
|
+
const bundledPath = resolve3(__dirname4, "mcp", "permission-server.js");
|
|
54376
|
+
if (existsSync8(bundledPath)) {
|
|
54377
|
+
return bundledPath;
|
|
54378
|
+
}
|
|
54375
54379
|
return resolve3(__dirname4, "..", "mcp", "permission-server.js");
|
|
54376
54380
|
}
|
|
54377
54381
|
getStatusLineWriterPath() {
|
|
54378
54382
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54379
54383
|
const __dirname4 = dirname6(__filename2);
|
|
54384
|
+
const bundledPath = resolve3(__dirname4, "statusline", "writer.js");
|
|
54385
|
+
if (existsSync8(bundledPath)) {
|
|
54386
|
+
return bundledPath;
|
|
54387
|
+
}
|
|
54380
54388
|
return resolve3(__dirname4, "..", "statusline", "writer.js");
|
|
54381
54389
|
}
|
|
54382
54390
|
}
|
|
@@ -59412,6 +59420,8 @@ var TEXT_FILE_EXTENSIONS = [
|
|
|
59412
59420
|
".xml",
|
|
59413
59421
|
".yaml",
|
|
59414
59422
|
".yml",
|
|
59423
|
+
".har",
|
|
59424
|
+
".log",
|
|
59415
59425
|
".js",
|
|
59416
59426
|
".ts",
|
|
59417
59427
|
".jsx",
|
|
@@ -59982,7 +59992,7 @@ function getUnsupportedFileSuggestion(file) {
|
|
|
59982
59992
|
async function buildMessageContent(text, platform, files, debug = false) {
|
|
59983
59993
|
const result = await processFiles(platform, files, debug);
|
|
59984
59994
|
if (result.blocks.length === 0) {
|
|
59985
|
-
return text;
|
|
59995
|
+
return { content: text, skipped: result.skipped };
|
|
59986
59996
|
}
|
|
59987
59997
|
if (text) {
|
|
59988
59998
|
result.blocks.push({
|
|
@@ -59990,7 +60000,12 @@ async function buildMessageContent(text, platform, files, debug = false) {
|
|
|
59990
60000
|
text
|
|
59991
60001
|
});
|
|
59992
60002
|
}
|
|
59993
|
-
return result.blocks;
|
|
60003
|
+
return { content: result.blocks, skipped: result.skipped };
|
|
60004
|
+
}
|
|
60005
|
+
async function postSkippedFilesFeedback(platform, threadId, skipped) {
|
|
60006
|
+
if (skipped.length === 0)
|
|
60007
|
+
return;
|
|
60008
|
+
await platform.createPost(formatSkippedFilesFeedback(skipped), threadId);
|
|
59994
60009
|
}
|
|
59995
60010
|
async function processFiles(platform, files, debug = false) {
|
|
59996
60011
|
const blocks = [];
|
|
@@ -60044,6 +60059,18 @@ async function processFiles(platform, files, debug = false) {
|
|
|
60044
60059
|
}
|
|
60045
60060
|
return { blocks, skipped };
|
|
60046
60061
|
}
|
|
60062
|
+
function formatSkippedFilesFeedback(skippedFiles) {
|
|
60063
|
+
const lines = ["⚠️ **Some files could not be processed:**"];
|
|
60064
|
+
for (const file of skippedFiles) {
|
|
60065
|
+
let line = `- **${file.name}**: ${file.reason}`;
|
|
60066
|
+
if (file.suggestion) {
|
|
60067
|
+
line += ` _(${file.suggestion})_`;
|
|
60068
|
+
}
|
|
60069
|
+
lines.push(line);
|
|
60070
|
+
}
|
|
60071
|
+
return lines.join(`
|
|
60072
|
+
`);
|
|
60073
|
+
}
|
|
60047
60074
|
function startTyping(session) {
|
|
60048
60075
|
if (session.timers.typingTimer)
|
|
60049
60076
|
return;
|
|
@@ -60490,20 +60517,15 @@ class MessageManager {
|
|
|
60490
60517
|
}
|
|
60491
60518
|
this.session.threadLogger?.logUserMessage(username || this.session.startedBy, message, displayName, files && files.length > 0);
|
|
60492
60519
|
await this.prepareForUserMessage();
|
|
60493
|
-
let skippedFiles = [];
|
|
60494
|
-
if (files && files.length > 0) {
|
|
60495
|
-
const fileResult = await processFiles(this.platform, files);
|
|
60496
|
-
skippedFiles = fileResult.skipped;
|
|
60497
|
-
}
|
|
60498
60520
|
let content = message;
|
|
60521
|
+
let skippedFiles = [];
|
|
60499
60522
|
if (this.buildMessageContentCallback) {
|
|
60500
|
-
|
|
60523
|
+
const built = await this.buildMessageContentCallback(message, this.platform, files);
|
|
60524
|
+
content = built.content;
|
|
60525
|
+
skippedFiles = built.skipped;
|
|
60501
60526
|
}
|
|
60502
60527
|
this.session.claude.sendMessage(content);
|
|
60503
|
-
|
|
60504
|
-
const feedback = this.formatSkippedFilesFeedback(skippedFiles);
|
|
60505
|
-
await this.platform.createPost(feedback, this.threadId);
|
|
60506
|
-
}
|
|
60528
|
+
await postSkippedFilesFeedback(this.platform, this.threadId, skippedFiles);
|
|
60507
60529
|
this.session.lastActivityAt = new Date;
|
|
60508
60530
|
this.session.isProcessing = true;
|
|
60509
60531
|
this.emitSessionUpdateCallback?.({ status: "active", isTyping: true });
|
|
@@ -60511,18 +60533,6 @@ class MessageManager {
|
|
|
60511
60533
|
logger.debug("User message sent to Claude");
|
|
60512
60534
|
return true;
|
|
60513
60535
|
}
|
|
60514
|
-
formatSkippedFilesFeedback(skippedFiles) {
|
|
60515
|
-
const lines = ["⚠️ **Some files could not be processed:**"];
|
|
60516
|
-
for (const file of skippedFiles) {
|
|
60517
|
-
let line = `- **${file.name}**: ${file.reason}`;
|
|
60518
|
-
if (file.suggestion) {
|
|
60519
|
-
line += ` _(${file.suggestion})_`;
|
|
60520
|
-
}
|
|
60521
|
-
lines.push(line);
|
|
60522
|
-
}
|
|
60523
|
-
return lines.join(`
|
|
60524
|
-
`);
|
|
60525
|
-
}
|
|
60526
60536
|
getSession() {
|
|
60527
60537
|
return this.session;
|
|
60528
60538
|
}
|
|
@@ -66286,9 +66296,10 @@ ${fmt.formatItalic("Claude Code restarted in the new worktree")}`);
|
|
|
66286
66296
|
const contextPrefix = options2.formatContextForClaude(threadMessages, workSummary);
|
|
66287
66297
|
const messageToSend = contextPrefix + session.firstPrompt;
|
|
66288
66298
|
session.messageCount++;
|
|
66289
|
-
const content = await options2.buildMessageContent(messageToSend, session, undefined);
|
|
66299
|
+
const { content, skipped } = await options2.buildMessageContent(messageToSend, session, undefined);
|
|
66290
66300
|
session.claude.sendMessage(content);
|
|
66291
66301
|
options2.startTyping(session);
|
|
66302
|
+
await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
|
|
66292
66303
|
sessionLog3(session).debug(`\uD83C\uDF3F Auto-included ${threadMessages.length} messages + work summary for mid-session worktree`);
|
|
66293
66304
|
}
|
|
66294
66305
|
session.worktreeResponsePostId = undefined;
|
|
@@ -66891,11 +66902,12 @@ async function handleContextPromptTimeout(session, ctx) {
|
|
|
66891
66902
|
}
|
|
66892
66903
|
session.messageCount++;
|
|
66893
66904
|
const messageToSend = ctx.injectMetadataReminder(queuedPrompt, session);
|
|
66894
|
-
const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66905
|
+
const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66895
66906
|
if (session.claude.isRunning()) {
|
|
66896
66907
|
session.claude.sendMessage(content);
|
|
66897
66908
|
ctx.startTyping(session);
|
|
66898
66909
|
}
|
|
66910
|
+
await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
|
|
66899
66911
|
ctx.persistSession(session);
|
|
66900
66912
|
sessionLog5(session).debug(`\uD83E\uDDF5 Context prompt timed out, continuing without thread context`);
|
|
66901
66913
|
}
|
|
@@ -66912,11 +66924,12 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
|
|
|
66912
66924
|
sessionLog5(session).debug(`\uD83E\uDDF5 Including work summary (no thread messages)`);
|
|
66913
66925
|
}
|
|
66914
66926
|
messageToSend = ctx.injectMetadataReminder(messageToSend, session);
|
|
66915
|
-
const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66927
|
+
const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66916
66928
|
if (session.claude.isRunning()) {
|
|
66917
66929
|
session.claude.sendMessage(content);
|
|
66918
66930
|
ctx.startTyping(session);
|
|
66919
66931
|
}
|
|
66932
|
+
await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
|
|
66920
66933
|
return false;
|
|
66921
66934
|
}
|
|
66922
66935
|
if (messageCount === 1) {
|
|
@@ -66930,11 +66943,12 @@ async function offerContextPrompt(session, queuedPrompt, queuedFiles, ctx, exclu
|
|
|
66930
66943
|
}
|
|
66931
66944
|
session.messageCount++;
|
|
66932
66945
|
messageToSend = ctx.injectMetadataReminder(messageToSend, session);
|
|
66933
|
-
const content = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66946
|
+
const { content, skipped } = await ctx.buildMessageContent(messageToSend, session, queuedFiles);
|
|
66934
66947
|
if (session.claude.isRunning()) {
|
|
66935
66948
|
session.claude.sendMessage(content);
|
|
66936
66949
|
ctx.startTyping(session);
|
|
66937
66950
|
}
|
|
66951
|
+
await postSkippedFilesFeedback(session.platform, session.threadId, skipped);
|
|
66938
66952
|
sessionLog5(session).debug(`\uD83E\uDDF5 Auto-included 1 message as context (thread starter)${previousWorkSummary ? " + work summary" : ""}`);
|
|
66939
66953
|
return false;
|
|
66940
66954
|
}
|
|
@@ -67111,7 +67125,7 @@ function createMessageManager(session, ctx) {
|
|
|
67111
67125
|
}
|
|
67112
67126
|
session.messageCount++;
|
|
67113
67127
|
messageToSend = maybeInjectMetadataReminder(messageToSend, session, ctx, session);
|
|
67114
|
-
const content = await ctx.ops.buildMessageContent(messageToSend, session.platform, undefined);
|
|
67128
|
+
const { content } = await ctx.ops.buildMessageContent(messageToSend, session.platform, undefined);
|
|
67115
67129
|
if (session.claude.isRunning()) {
|
|
67116
67130
|
session.claude.sendMessage(content);
|
|
67117
67131
|
ctx.ops.startTyping(session);
|
|
@@ -67417,17 +67431,19 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
67417
67431
|
await ctx.ops.updateStickyMessage();
|
|
67418
67432
|
return;
|
|
67419
67433
|
}
|
|
67420
|
-
const content = await ctx.ops.buildMessageContent(options2.prompt, session.platform, options2.files);
|
|
67434
|
+
const { content, skipped } = await ctx.ops.buildMessageContent(options2.prompt, session.platform, options2.files);
|
|
67421
67435
|
const messageText = typeof content === "string" ? content : options2.prompt;
|
|
67422
67436
|
if (replyToPostId) {
|
|
67423
67437
|
const excludePostId = triggeringPostId || replyToPostId;
|
|
67424
67438
|
const contextOffered = await ctx.ops.offerContextPrompt(session, messageText, options2.files, excludePostId);
|
|
67425
67439
|
if (contextOffered) {
|
|
67440
|
+
await postSkippedFilesFeedback(session.platform, actualThreadId, skipped);
|
|
67426
67441
|
return;
|
|
67427
67442
|
}
|
|
67428
67443
|
}
|
|
67429
67444
|
session.messageCount++;
|
|
67430
67445
|
claude.sendMessage(content);
|
|
67446
|
+
await postSkippedFilesFeedback(session.platform, actualThreadId, skipped);
|
|
67431
67447
|
}
|
|
67432
67448
|
async function resumeSession(state, ctx) {
|
|
67433
67449
|
if (!state.threadId || !state.platformId || !state.claudeSessionId || !state.workingDir) {
|
|
@@ -67612,9 +67628,7 @@ async function sendFollowUp(session, message, files, ctx, username, displayName)
|
|
|
67612
67628
|
if (session.needsContextPromptOnNextMessage) {
|
|
67613
67629
|
session.needsContextPromptOnNextMessage = false;
|
|
67614
67630
|
await session.messageManager?.prepareForUserMessage();
|
|
67615
|
-
const
|
|
67616
|
-
const messageText = typeof content === "string" ? content : message;
|
|
67617
|
-
const contextOffered = await ctx.ops.offerContextPrompt(session, messageText, files);
|
|
67631
|
+
const contextOffered = await ctx.ops.offerContextPrompt(session, message, files);
|
|
67618
67632
|
if (contextOffered) {
|
|
67619
67633
|
session.lastActivityAt = new Date;
|
|
67620
67634
|
return;
|
|
@@ -79624,7 +79638,7 @@ async function main() {
|
|
|
79624
79638
|
env: {
|
|
79625
79639
|
...process.env,
|
|
79626
79640
|
CLAUDE_THREADS_BIN: binPath,
|
|
79627
|
-
|
|
79641
|
+
CLAUDE_THREADS_INTERACTIVE: ""
|
|
79628
79642
|
}
|
|
79629
79643
|
});
|
|
79630
79644
|
} else {
|
|
@@ -79633,7 +79647,7 @@ async function main() {
|
|
|
79633
79647
|
env: {
|
|
79634
79648
|
...process.env,
|
|
79635
79649
|
CLAUDE_THREADS_BIN: binPath,
|
|
79636
|
-
|
|
79650
|
+
CLAUDE_THREADS_INTERACTIVE: ""
|
|
79637
79651
|
}
|
|
79638
79652
|
});
|
|
79639
79653
|
}
|
|
@@ -15849,7 +15849,7 @@ var require_validation3 = __commonJS((exports, module) => {
|
|
|
15849
15849
|
|
|
15850
15850
|
// node_modules/ws/lib/receiver.js
|
|
15851
15851
|
var require_receiver = __commonJS((exports, module) => {
|
|
15852
|
-
var { Writable
|
|
15852
|
+
var { Writable } = __require("stream");
|
|
15853
15853
|
var PerMessageDeflate = require_permessage_deflate();
|
|
15854
15854
|
var {
|
|
15855
15855
|
BINARY_TYPES,
|
|
@@ -15868,7 +15868,7 @@ var require_receiver = __commonJS((exports, module) => {
|
|
|
15868
15868
|
var INFLATING = 5;
|
|
15869
15869
|
var DEFER_EVENT = 6;
|
|
15870
15870
|
|
|
15871
|
-
class Receiver extends
|
|
15871
|
+
class Receiver extends Writable {
|
|
15872
15872
|
constructor(options2 = {}) {
|
|
15873
15873
|
super();
|
|
15874
15874
|
this._allowSynchronousEvents = options2.allowSynchronousEvents !== undefined ? options2.allowSynchronousEvents : true;
|
|
@@ -16906,7 +16906,7 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
16906
16906
|
var net = __require("net");
|
|
16907
16907
|
var tls = __require("tls");
|
|
16908
16908
|
var { randomBytes, createHash } = __require("crypto");
|
|
16909
|
-
var { Duplex, Readable
|
|
16909
|
+
var { Duplex, Readable } = __require("stream");
|
|
16910
16910
|
var { URL: URL2 } = __require("url");
|
|
16911
16911
|
var PerMessageDeflate = require_permessage_deflate();
|
|
16912
16912
|
var Receiver = require_receiver();
|
|
@@ -52407,661 +52407,28 @@ function createMessageManagerEvents() {
|
|
|
52407
52407
|
|
|
52408
52408
|
// src/operations/streaming/handler.ts
|
|
52409
52409
|
var import_yauzl = __toESM(require_yauzl(), 1);
|
|
52410
|
-
import { createGunzip } from "zlib";
|
|
52411
|
-
import { pipeline as pipeline2 } from "stream/promises";
|
|
52412
|
-
import { Readable, Writable } from "stream";
|
|
52413
52410
|
var log2 = createLogger("streaming");
|
|
52414
52411
|
var MAX_PDF_SIZE = 32 * 1024 * 1024;
|
|
52415
52412
|
var MAX_TEXT_FILE_SIZE = 1 * 1024 * 1024;
|
|
52416
52413
|
var MAX_DECOMPRESSED_SIZE = 10 * 1024 * 1024;
|
|
52417
52414
|
var MAX_ZIP_SIZE = 50 * 1024 * 1024;
|
|
52418
52415
|
var MAX_GZIP_SIZE = 50 * 1024 * 1024;
|
|
52419
|
-
|
|
52420
|
-
|
|
52421
|
-
|
|
52422
|
-
|
|
52423
|
-
"image/gif",
|
|
52424
|
-
"image/webp"
|
|
52425
|
-
];
|
|
52426
|
-
var SUPPORTED_TEXT_TYPES = [
|
|
52427
|
-
"text/plain",
|
|
52428
|
-
"text/markdown",
|
|
52429
|
-
"text/csv",
|
|
52430
|
-
"text/xml",
|
|
52431
|
-
"text/yaml",
|
|
52432
|
-
"text/x-yaml",
|
|
52433
|
-
"application/json",
|
|
52434
|
-
"application/xml",
|
|
52435
|
-
"application/x-yaml",
|
|
52436
|
-
"application/yaml"
|
|
52437
|
-
];
|
|
52438
|
-
var TEXT_FILE_EXTENSIONS = [
|
|
52439
|
-
".txt",
|
|
52440
|
-
".md",
|
|
52441
|
-
".markdown",
|
|
52442
|
-
".json",
|
|
52443
|
-
".csv",
|
|
52444
|
-
".xml",
|
|
52445
|
-
".yaml",
|
|
52446
|
-
".yml",
|
|
52447
|
-
".js",
|
|
52448
|
-
".ts",
|
|
52449
|
-
".jsx",
|
|
52450
|
-
".tsx",
|
|
52451
|
-
".py",
|
|
52452
|
-
".rb",
|
|
52453
|
-
".go",
|
|
52454
|
-
".rs",
|
|
52455
|
-
".java",
|
|
52456
|
-
".c",
|
|
52457
|
-
".cpp",
|
|
52458
|
-
".h",
|
|
52459
|
-
".hpp",
|
|
52460
|
-
".cs",
|
|
52461
|
-
".php",
|
|
52462
|
-
".swift",
|
|
52463
|
-
".kt",
|
|
52464
|
-
".scala",
|
|
52465
|
-
".sh",
|
|
52466
|
-
".bash",
|
|
52467
|
-
".zsh",
|
|
52468
|
-
".fish",
|
|
52469
|
-
".ps1",
|
|
52470
|
-
".bat",
|
|
52471
|
-
".cmd",
|
|
52472
|
-
".html",
|
|
52473
|
-
".htm",
|
|
52474
|
-
".css",
|
|
52475
|
-
".scss",
|
|
52476
|
-
".sass",
|
|
52477
|
-
".less",
|
|
52478
|
-
".sql",
|
|
52479
|
-
".graphql",
|
|
52480
|
-
".gql",
|
|
52481
|
-
".toml",
|
|
52482
|
-
".ini",
|
|
52483
|
-
".cfg",
|
|
52484
|
-
".conf",
|
|
52485
|
-
".env",
|
|
52486
|
-
".properties",
|
|
52487
|
-
".dockerfile",
|
|
52488
|
-
".gitignore",
|
|
52489
|
-
".gitattributes",
|
|
52490
|
-
".editorconfig"
|
|
52491
|
-
];
|
|
52492
|
-
function isImageFile(file2) {
|
|
52493
|
-
return file2.mimeType.startsWith("image/") && SUPPORTED_IMAGE_TYPES.includes(file2.mimeType);
|
|
52494
|
-
}
|
|
52495
|
-
function isPdfFile(file2) {
|
|
52496
|
-
return file2.mimeType === "application/pdf" || file2.name.toLowerCase().endsWith(".pdf");
|
|
52497
|
-
}
|
|
52498
|
-
function isTextFile(file2) {
|
|
52499
|
-
if (SUPPORTED_TEXT_TYPES.includes(file2.mimeType)) {
|
|
52500
|
-
return true;
|
|
52501
|
-
}
|
|
52502
|
-
const lowerName = file2.name.toLowerCase();
|
|
52503
|
-
return TEXT_FILE_EXTENSIONS.some((ext) => lowerName.endsWith(ext));
|
|
52504
|
-
}
|
|
52505
|
-
function isGzipFile(file2) {
|
|
52506
|
-
return file2.mimeType === "application/gzip" || file2.mimeType === "application/x-gzip" || file2.name.toLowerCase().endsWith(".gz");
|
|
52507
|
-
}
|
|
52508
|
-
function isZipFile(file2) {
|
|
52509
|
-
return file2.mimeType === "application/zip" || file2.mimeType === "application/x-zip-compressed" || file2.name.toLowerCase().endsWith(".zip");
|
|
52510
|
-
}
|
|
52511
|
-
function categorizeFile(file2) {
|
|
52512
|
-
if (isImageFile(file2))
|
|
52513
|
-
return "image";
|
|
52514
|
-
if (isPdfFile(file2))
|
|
52515
|
-
return "pdf";
|
|
52516
|
-
if (isZipFile(file2))
|
|
52517
|
-
return "zip";
|
|
52518
|
-
if (isGzipFile(file2))
|
|
52519
|
-
return "gzip";
|
|
52520
|
-
if (isTextFile(file2))
|
|
52521
|
-
return "text";
|
|
52522
|
-
return "unsupported";
|
|
52523
|
-
}
|
|
52524
|
-
async function processImageFile(file2, platform, debug = false) {
|
|
52525
|
-
try {
|
|
52526
|
-
if (!platform.downloadFile) {
|
|
52527
|
-
return {
|
|
52528
|
-
skipped: {
|
|
52529
|
-
name: file2.name,
|
|
52530
|
-
reason: "Platform does not support file downloads"
|
|
52531
|
-
}
|
|
52532
|
-
};
|
|
52533
|
-
}
|
|
52534
|
-
const buffer = await platform.downloadFile(file2.id);
|
|
52535
|
-
const base644 = buffer.toString("base64");
|
|
52536
|
-
if (debug) {
|
|
52537
|
-
log2.debug(`Attached image: ${file2.name} (${file2.mimeType}, ${Math.round(buffer.length / 1024)}KB)`);
|
|
52538
|
-
}
|
|
52539
|
-
return {
|
|
52540
|
-
block: {
|
|
52541
|
-
type: "image",
|
|
52542
|
-
source: {
|
|
52543
|
-
type: "base64",
|
|
52544
|
-
media_type: file2.mimeType,
|
|
52545
|
-
data: base644
|
|
52546
|
-
}
|
|
52547
|
-
}
|
|
52548
|
-
};
|
|
52549
|
-
} catch (err) {
|
|
52550
|
-
log2.error(`Failed to download image ${file2.name}: ${err}`);
|
|
52551
|
-
return {
|
|
52552
|
-
skipped: {
|
|
52553
|
-
name: file2.name,
|
|
52554
|
-
reason: `Download failed: ${err instanceof Error ? err.message : String(err)}`
|
|
52555
|
-
}
|
|
52556
|
-
};
|
|
52557
|
-
}
|
|
52558
|
-
}
|
|
52559
|
-
async function processPdfFile(file2, platform, debug = false) {
|
|
52560
|
-
try {
|
|
52561
|
-
if (!platform.downloadFile) {
|
|
52562
|
-
return {
|
|
52563
|
-
skipped: {
|
|
52564
|
-
name: file2.name,
|
|
52565
|
-
reason: "Platform does not support file downloads"
|
|
52566
|
-
}
|
|
52567
|
-
};
|
|
52568
|
-
}
|
|
52569
|
-
const buffer = await platform.downloadFile(file2.id);
|
|
52570
|
-
if (buffer.length > MAX_PDF_SIZE) {
|
|
52571
|
-
return {
|
|
52572
|
-
skipped: {
|
|
52573
|
-
name: file2.name,
|
|
52574
|
-
reason: `PDF exceeds ${Math.round(MAX_PDF_SIZE / 1024 / 1024)}MB limit (${Math.round(buffer.length / 1024 / 1024)}MB)`,
|
|
52575
|
-
suggestion: "Try splitting the PDF into smaller parts"
|
|
52576
|
-
}
|
|
52577
|
-
};
|
|
52578
|
-
}
|
|
52579
|
-
const base644 = buffer.toString("base64");
|
|
52580
|
-
if (debug) {
|
|
52581
|
-
log2.debug(`Attached PDF: ${file2.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
52582
|
-
}
|
|
52583
|
-
return {
|
|
52584
|
-
block: {
|
|
52585
|
-
type: "document",
|
|
52586
|
-
source: {
|
|
52587
|
-
type: "base64",
|
|
52588
|
-
media_type: "application/pdf",
|
|
52589
|
-
data: base644
|
|
52590
|
-
},
|
|
52591
|
-
title: file2.name
|
|
52592
|
-
}
|
|
52593
|
-
};
|
|
52594
|
-
} catch (err) {
|
|
52595
|
-
log2.error(`Failed to process PDF ${file2.name}: ${err}`);
|
|
52596
|
-
return {
|
|
52597
|
-
skipped: {
|
|
52598
|
-
name: file2.name,
|
|
52599
|
-
reason: `Processing failed: ${err instanceof Error ? err.message : String(err)}`
|
|
52600
|
-
}
|
|
52601
|
-
};
|
|
52602
|
-
}
|
|
52603
|
-
}
|
|
52604
|
-
async function processTextFile(file2, platform, debug = false) {
|
|
52605
|
-
try {
|
|
52606
|
-
if (!platform.downloadFile) {
|
|
52607
|
-
return {
|
|
52608
|
-
skipped: {
|
|
52609
|
-
name: file2.name,
|
|
52610
|
-
reason: "Platform does not support file downloads"
|
|
52611
|
-
}
|
|
52612
|
-
};
|
|
52613
|
-
}
|
|
52614
|
-
const buffer = await platform.downloadFile(file2.id);
|
|
52615
|
-
if (buffer.length > MAX_TEXT_FILE_SIZE) {
|
|
52616
|
-
return {
|
|
52617
|
-
skipped: {
|
|
52618
|
-
name: file2.name,
|
|
52619
|
-
reason: `File exceeds ${Math.round(MAX_TEXT_FILE_SIZE / 1024)}KB limit (${Math.round(buffer.length / 1024)}KB)`,
|
|
52620
|
-
suggestion: "Try splitting the file or extracting relevant portions"
|
|
52621
|
-
}
|
|
52622
|
-
};
|
|
52623
|
-
}
|
|
52624
|
-
const content = buffer.toString("utf-8");
|
|
52625
|
-
if (debug) {
|
|
52626
|
-
log2.debug(`Attached text file: ${file2.name} (${Math.round(buffer.length / 1024)}KB)`);
|
|
52627
|
-
}
|
|
52628
|
-
const wrappedContent = formatTextFileContent(file2.name, content);
|
|
52629
|
-
return {
|
|
52630
|
-
block: {
|
|
52631
|
-
type: "text",
|
|
52632
|
-
text: wrappedContent
|
|
52633
|
-
}
|
|
52634
|
-
};
|
|
52635
|
-
} catch (err) {
|
|
52636
|
-
log2.error(`Failed to process text file ${file2.name}: ${err}`);
|
|
52637
|
-
return {
|
|
52638
|
-
skipped: {
|
|
52639
|
-
name: file2.name,
|
|
52640
|
-
reason: `Processing failed: ${err instanceof Error ? err.message : String(err)}`
|
|
52641
|
-
}
|
|
52642
|
-
};
|
|
52643
|
-
}
|
|
52644
|
-
}
|
|
52645
|
-
function formatTextFileContent(filename, content) {
|
|
52646
|
-
return `\uD83D\uDCC4 **${filename}**:
|
|
52647
|
-
\`\`\`
|
|
52648
|
-
${content}
|
|
52649
|
-
\`\`\``;
|
|
52650
|
-
}
|
|
52651
|
-
async function decompressGzipStream(compressedBuffer) {
|
|
52652
|
-
const chunks = [];
|
|
52653
|
-
let totalSize = 0;
|
|
52654
|
-
const gunzip = createGunzip();
|
|
52655
|
-
const source = Readable.from(compressedBuffer);
|
|
52656
|
-
const collector = new Writable({
|
|
52657
|
-
write(chunk, _encoding, callback) {
|
|
52658
|
-
totalSize += chunk.length;
|
|
52659
|
-
if (totalSize > MAX_DECOMPRESSED_SIZE) {
|
|
52660
|
-
callback(new Error(`Decompressed size exceeds ${Math.round(MAX_DECOMPRESSED_SIZE / 1024 / 1024)}MB limit`));
|
|
52661
|
-
return;
|
|
52662
|
-
}
|
|
52663
|
-
chunks.push(chunk);
|
|
52664
|
-
callback();
|
|
52665
|
-
}
|
|
52666
|
-
});
|
|
52667
|
-
await pipeline2(source, gunzip, collector);
|
|
52668
|
-
return Buffer.concat(chunks);
|
|
52669
|
-
}
|
|
52670
|
-
async function processGzipFile(file2, platform, debug = false) {
|
|
52671
|
-
try {
|
|
52672
|
-
if (!platform.downloadFile) {
|
|
52673
|
-
return {
|
|
52674
|
-
skipped: {
|
|
52675
|
-
name: file2.name,
|
|
52676
|
-
reason: "Platform does not support file downloads"
|
|
52677
|
-
}
|
|
52678
|
-
};
|
|
52679
|
-
}
|
|
52680
|
-
if (file2.size && file2.size > MAX_GZIP_SIZE) {
|
|
52681
|
-
return {
|
|
52682
|
-
skipped: {
|
|
52683
|
-
name: file2.name,
|
|
52684
|
-
reason: `Gzip file exceeds ${Math.round(MAX_GZIP_SIZE / 1024 / 1024)}MB limit (${Math.round(file2.size / 1024 / 1024)}MB)`,
|
|
52685
|
-
suggestion: "Try compressing a smaller file or splitting the content"
|
|
52686
|
-
}
|
|
52687
|
-
};
|
|
52688
|
-
}
|
|
52689
|
-
let compressedBuffer;
|
|
52690
|
-
try {
|
|
52691
|
-
compressedBuffer = await platform.downloadFile(file2.id);
|
|
52692
|
-
} catch (err) {
|
|
52693
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
52694
|
-
log2.error(`Failed to download gzip file ${file2.name}: ${errorMessage}`);
|
|
52695
|
-
return {
|
|
52696
|
-
skipped: {
|
|
52697
|
-
name: file2.name,
|
|
52698
|
-
reason: `Download failed: ${errorMessage}`,
|
|
52699
|
-
suggestion: "Check if the file is still available and try again"
|
|
52700
|
-
}
|
|
52701
|
-
};
|
|
52702
|
-
}
|
|
52703
|
-
if (file2.size && compressedBuffer.length !== file2.size) {
|
|
52704
|
-
log2.warn(`Downloaded size mismatch for ${file2.name}: expected ${file2.size}, got ${compressedBuffer.length}`);
|
|
52705
|
-
}
|
|
52706
|
-
let decompressedBuffer;
|
|
52707
|
-
try {
|
|
52708
|
-
decompressedBuffer = await decompressGzipStream(compressedBuffer);
|
|
52709
|
-
} catch (err) {
|
|
52710
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
52711
|
-
let reason;
|
|
52712
|
-
let suggestion;
|
|
52713
|
-
if (errorMessage.includes("incorrect header check")) {
|
|
52714
|
-
reason = "Invalid gzip file: the file header is corrupted or this is not a gzip file";
|
|
52715
|
-
suggestion = "Verify the file is a valid gzip archive";
|
|
52716
|
-
} else if (errorMessage.includes("unexpected end of file")) {
|
|
52717
|
-
reason = "Incomplete gzip file: the file appears to be truncated";
|
|
52718
|
-
suggestion = "Re-download the file or check if the upload completed";
|
|
52719
|
-
} else if (errorMessage.includes("invalid stored block lengths")) {
|
|
52720
|
-
reason = "Corrupted gzip file: the compressed data is damaged";
|
|
52721
|
-
suggestion = "Try re-compressing the original file";
|
|
52722
|
-
} else if (errorMessage.includes("exceeds") && errorMessage.includes("limit")) {
|
|
52723
|
-
reason = errorMessage;
|
|
52724
|
-
suggestion = "Try extracting only the relevant portions of the file";
|
|
52725
|
-
} else {
|
|
52726
|
-
reason = `Decompression failed: ${errorMessage}`;
|
|
52727
|
-
suggestion = "Verify the file is a valid gzip archive";
|
|
52728
|
-
}
|
|
52729
|
-
return {
|
|
52730
|
-
skipped: {
|
|
52731
|
-
name: file2.name,
|
|
52732
|
-
reason,
|
|
52733
|
-
suggestion
|
|
52734
|
-
}
|
|
52735
|
-
};
|
|
52736
|
-
}
|
|
52737
|
-
const innerFilename = file2.name.toLowerCase().endsWith(".gz") ? file2.name.slice(0, -3) : file2.name;
|
|
52738
|
-
const contentType = detectDecompressedContentType(decompressedBuffer, innerFilename);
|
|
52739
|
-
if (debug) {
|
|
52740
|
-
log2.debug(`Decompressed ${file2.name}: ${Math.round(decompressedBuffer.length / 1024)}KB, detected type: ${contentType}`);
|
|
52741
|
-
}
|
|
52742
|
-
if (contentType === "pdf") {
|
|
52743
|
-
const base644 = decompressedBuffer.toString("base64");
|
|
52744
|
-
return {
|
|
52745
|
-
block: {
|
|
52746
|
-
type: "document",
|
|
52747
|
-
source: {
|
|
52748
|
-
type: "base64",
|
|
52749
|
-
media_type: "application/pdf",
|
|
52750
|
-
data: base644
|
|
52751
|
-
},
|
|
52752
|
-
title: innerFilename
|
|
52753
|
-
}
|
|
52754
|
-
};
|
|
52755
|
-
} else if (contentType === "text") {
|
|
52756
|
-
const content = decompressedBuffer.toString("utf-8");
|
|
52757
|
-
const wrappedContent = formatTextFileContent(innerFilename, content);
|
|
52758
|
-
return {
|
|
52759
|
-
block: {
|
|
52760
|
-
type: "text",
|
|
52761
|
-
text: wrappedContent
|
|
52762
|
-
}
|
|
52763
|
-
};
|
|
52764
|
-
} else {
|
|
52765
|
-
return {
|
|
52766
|
-
skipped: {
|
|
52767
|
-
name: file2.name,
|
|
52768
|
-
reason: "Decompressed content type not supported",
|
|
52769
|
-
suggestion: "Only text-based files and PDFs are supported after decompression"
|
|
52770
|
-
}
|
|
52771
|
-
};
|
|
52772
|
-
}
|
|
52773
|
-
} catch (err) {
|
|
52774
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
52775
|
-
log2.error(`Failed to process gzip file ${file2.name}: ${errorMessage}`);
|
|
52776
|
-
return {
|
|
52777
|
-
skipped: {
|
|
52778
|
-
name: file2.name,
|
|
52779
|
-
reason: `Processing failed: ${errorMessage}`,
|
|
52780
|
-
suggestion: "An unexpected error occurred. Please try again or contact support if the issue persists"
|
|
52781
|
-
}
|
|
52782
|
-
};
|
|
52783
|
-
}
|
|
52784
|
-
}
|
|
52785
|
-
async function extractZipEntry(zipfile, entry) {
|
|
52786
|
-
return new Promise((resolve2, reject) => {
|
|
52787
|
-
zipfile.openReadStream(entry, (err, readStream) => {
|
|
52788
|
-
if (err) {
|
|
52789
|
-
reject(err);
|
|
52790
|
-
return;
|
|
52791
|
-
}
|
|
52792
|
-
if (!readStream) {
|
|
52793
|
-
reject(new Error("No read stream"));
|
|
52794
|
-
return;
|
|
52795
|
-
}
|
|
52796
|
-
const chunks = [];
|
|
52797
|
-
readStream.on("data", (chunk) => chunks.push(chunk));
|
|
52798
|
-
readStream.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
52799
|
-
readStream.on("error", reject);
|
|
52800
|
-
});
|
|
52801
|
-
});
|
|
52802
|
-
}
|
|
52803
|
-
async function processZipFile(file2, platform, debug = false) {
|
|
52804
|
-
const blocks = [];
|
|
52805
|
-
const skipped = [];
|
|
52806
|
-
try {
|
|
52807
|
-
if (!platform.downloadFile) {
|
|
52808
|
-
return {
|
|
52809
|
-
blocks: [],
|
|
52810
|
-
skipped: [{
|
|
52811
|
-
name: file2.name,
|
|
52812
|
-
reason: "Platform does not support file downloads"
|
|
52813
|
-
}]
|
|
52814
|
-
};
|
|
52815
|
-
}
|
|
52816
|
-
if (file2.size && file2.size > MAX_ZIP_SIZE) {
|
|
52817
|
-
return {
|
|
52818
|
-
blocks: [],
|
|
52819
|
-
skipped: [{
|
|
52820
|
-
name: file2.name,
|
|
52821
|
-
reason: `Zip file exceeds ${Math.round(MAX_ZIP_SIZE / 1024 / 1024)}MB limit (${Math.round(file2.size / 1024 / 1024)}MB)`
|
|
52822
|
-
}]
|
|
52823
|
-
};
|
|
52824
|
-
}
|
|
52825
|
-
const zipBuffer = await platform.downloadFile(file2.id);
|
|
52826
|
-
if (debug) {
|
|
52827
|
-
log2.debug(`Processing zip file ${file2.name}: ${Math.round(zipBuffer.length / 1024)}KB`);
|
|
52828
|
-
}
|
|
52829
|
-
const zipfile = await new Promise((resolve2, reject) => {
|
|
52830
|
-
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
52831
|
-
if (err)
|
|
52832
|
-
reject(err);
|
|
52833
|
-
else if (!zf)
|
|
52834
|
-
reject(new Error("Failed to open zip file"));
|
|
52835
|
-
else
|
|
52836
|
-
resolve2(zf);
|
|
52837
|
-
});
|
|
52838
|
-
});
|
|
52839
|
-
const entries = [];
|
|
52840
|
-
await new Promise((resolve2, reject) => {
|
|
52841
|
-
zipfile.on("entry", (entry) => {
|
|
52842
|
-
if (!entry.fileName.endsWith("/")) {
|
|
52843
|
-
entries.push(entry);
|
|
52844
|
-
}
|
|
52845
|
-
zipfile.readEntry();
|
|
52846
|
-
});
|
|
52847
|
-
zipfile.on("end", resolve2);
|
|
52848
|
-
zipfile.on("error", reject);
|
|
52849
|
-
zipfile.readEntry();
|
|
52850
|
-
});
|
|
52851
|
-
if (entries.length > MAX_ZIP_FILES) {
|
|
52852
|
-
zipfile.close();
|
|
52853
|
-
return {
|
|
52854
|
-
blocks: [],
|
|
52855
|
-
skipped: [{
|
|
52856
|
-
name: file2.name,
|
|
52857
|
-
reason: `Zip contains too many files (${entries.length}). Maximum is ${MAX_ZIP_FILES} files.`,
|
|
52858
|
-
suggestion: "Extract and upload the most relevant files individually"
|
|
52859
|
-
}]
|
|
52860
|
-
};
|
|
52861
|
-
}
|
|
52862
|
-
if (entries.length === 0) {
|
|
52863
|
-
zipfile.close();
|
|
52864
|
-
return {
|
|
52865
|
-
blocks: [],
|
|
52866
|
-
skipped: [{
|
|
52867
|
-
name: file2.name,
|
|
52868
|
-
reason: "Zip archive is empty"
|
|
52869
|
-
}]
|
|
52870
|
-
};
|
|
52871
|
-
}
|
|
52872
|
-
const zipfile2 = await new Promise((resolve2, reject) => {
|
|
52873
|
-
import_yauzl.default.fromBuffer(zipBuffer, { lazyEntries: true }, (err, zf) => {
|
|
52874
|
-
if (err)
|
|
52875
|
-
reject(err);
|
|
52876
|
-
else if (!zf)
|
|
52877
|
-
reject(new Error("Failed to open zip file"));
|
|
52878
|
-
else
|
|
52879
|
-
resolve2(zf);
|
|
52880
|
-
});
|
|
52881
|
-
});
|
|
52882
|
-
let processedCount = 0;
|
|
52883
|
-
await new Promise((resolve2, reject) => {
|
|
52884
|
-
zipfile2.on("entry", async (entry) => {
|
|
52885
|
-
try {
|
|
52886
|
-
if (entry.fileName.endsWith("/")) {
|
|
52887
|
-
zipfile2.readEntry();
|
|
52888
|
-
return;
|
|
52889
|
-
}
|
|
52890
|
-
if (entry.uncompressedSize > MAX_DECOMPRESSED_SIZE) {
|
|
52891
|
-
skipped.push({
|
|
52892
|
-
name: entry.fileName,
|
|
52893
|
-
reason: `File exceeds ${Math.round(MAX_DECOMPRESSED_SIZE / 1024 / 1024)}MB decompressed size limit`
|
|
52894
|
-
});
|
|
52895
|
-
zipfile2.readEntry();
|
|
52896
|
-
return;
|
|
52897
|
-
}
|
|
52898
|
-
const buffer = await extractZipEntry(zipfile2, entry);
|
|
52899
|
-
const contentType = detectDecompressedContentType(buffer, entry.fileName);
|
|
52900
|
-
if (debug) {
|
|
52901
|
-
log2.debug(`Extracted ${entry.fileName}: ${Math.round(buffer.length / 1024)}KB, type: ${contentType}`);
|
|
52902
|
-
}
|
|
52903
|
-
if (contentType === "pdf") {
|
|
52904
|
-
const base644 = buffer.toString("base64");
|
|
52905
|
-
blocks.push({
|
|
52906
|
-
type: "document",
|
|
52907
|
-
source: {
|
|
52908
|
-
type: "base64",
|
|
52909
|
-
media_type: "application/pdf",
|
|
52910
|
-
data: base644
|
|
52911
|
-
}
|
|
52912
|
-
});
|
|
52913
|
-
processedCount++;
|
|
52914
|
-
} else if (contentType === "text") {
|
|
52915
|
-
const content = buffer.toString("utf-8");
|
|
52916
|
-
const wrappedContent = formatTextFileContent(entry.fileName, content);
|
|
52917
|
-
blocks.push({
|
|
52918
|
-
type: "text",
|
|
52919
|
-
text: wrappedContent
|
|
52920
|
-
});
|
|
52921
|
-
processedCount++;
|
|
52922
|
-
} else {
|
|
52923
|
-
skipped.push({
|
|
52924
|
-
name: entry.fileName,
|
|
52925
|
-
reason: "Unsupported file type inside zip",
|
|
52926
|
-
suggestion: "Only text-based files and PDFs are supported"
|
|
52927
|
-
});
|
|
52928
|
-
}
|
|
52929
|
-
zipfile2.readEntry();
|
|
52930
|
-
} catch (err) {
|
|
52931
|
-
skipped.push({
|
|
52932
|
-
name: entry.fileName,
|
|
52933
|
-
reason: `Failed to extract: ${err instanceof Error ? err.message : String(err)}`
|
|
52934
|
-
});
|
|
52935
|
-
zipfile2.readEntry();
|
|
52936
|
-
}
|
|
52937
|
-
});
|
|
52938
|
-
zipfile2.on("end", resolve2);
|
|
52939
|
-
zipfile2.on("error", reject);
|
|
52940
|
-
zipfile2.readEntry();
|
|
52941
|
-
});
|
|
52942
|
-
zipfile2.close();
|
|
52943
|
-
if (debug) {
|
|
52944
|
-
log2.debug(`Zip ${file2.name}: processed ${processedCount} files, skipped ${skipped.length}`);
|
|
52945
|
-
}
|
|
52946
|
-
return { blocks, skipped };
|
|
52947
|
-
} catch (err) {
|
|
52948
|
-
log2.error(`Failed to process zip file ${file2.name}: ${err}`);
|
|
52949
|
-
return {
|
|
52950
|
-
blocks: [],
|
|
52951
|
-
skipped: [{
|
|
52952
|
-
name: file2.name,
|
|
52953
|
-
reason: `Failed to process zip: ${err instanceof Error ? err.message : String(err)}`
|
|
52954
|
-
}]
|
|
52955
|
-
};
|
|
52956
|
-
}
|
|
52957
|
-
}
|
|
52958
|
-
function detectDecompressedContentType(buffer, filename) {
|
|
52959
|
-
if (buffer.length >= 5 && buffer.toString("ascii", 0, 5) === "%PDF-") {
|
|
52960
|
-
return "pdf";
|
|
52961
|
-
}
|
|
52962
|
-
const lowerFilename = filename.toLowerCase();
|
|
52963
|
-
if (lowerFilename.endsWith(".pdf")) {
|
|
52964
|
-
return "pdf";
|
|
52965
|
-
}
|
|
52966
|
-
if (TEXT_FILE_EXTENSIONS.some((ext) => lowerFilename.endsWith(ext))) {
|
|
52967
|
-
return "text";
|
|
52968
|
-
}
|
|
52969
|
-
if (buffer.length > 0) {
|
|
52970
|
-
const firstChar = String.fromCharCode(buffer[0]);
|
|
52971
|
-
if (firstChar === "{" || firstChar === "[") {
|
|
52972
|
-
return "text";
|
|
52973
|
-
}
|
|
52974
|
-
}
|
|
52975
|
-
try {
|
|
52976
|
-
const text = buffer.toString("utf-8");
|
|
52977
|
-
const printableRatio = countPrintableChars(text) / text.length;
|
|
52978
|
-
if (printableRatio > 0.9) {
|
|
52979
|
-
return "text";
|
|
52980
|
-
}
|
|
52981
|
-
} catch {}
|
|
52982
|
-
return "unknown";
|
|
52983
|
-
}
|
|
52984
|
-
function countPrintableChars(text) {
|
|
52985
|
-
let count = 0;
|
|
52986
|
-
for (let i = 0;i < text.length; i++) {
|
|
52987
|
-
const code = text.charCodeAt(i);
|
|
52988
|
-
if (code >= 32 && code <= 126 || code === 9 || code === 10 || code === 13) {
|
|
52989
|
-
count++;
|
|
52990
|
-
}
|
|
52991
|
-
}
|
|
52992
|
-
return count;
|
|
52993
|
-
}
|
|
52994
|
-
function getUnsupportedFileSuggestion(file2) {
|
|
52995
|
-
const ext = file2.name.toLowerCase().split(".").pop();
|
|
52996
|
-
const mime = file2.mimeType.toLowerCase();
|
|
52997
|
-
if (ext === "doc" || ext === "docx" || mime.includes("msword") || mime.includes("wordprocessingml")) {
|
|
52998
|
-
return "Convert to PDF for best results";
|
|
52999
|
-
}
|
|
53000
|
-
if (ext === "xls" || ext === "xlsx" || mime.includes("spreadsheet")) {
|
|
53001
|
-
return "Export as CSV for text-based analysis";
|
|
53002
|
-
}
|
|
53003
|
-
if (ext === "ppt" || ext === "pptx" || mime.includes("presentation")) {
|
|
53004
|
-
return "Convert to PDF for best results";
|
|
53005
|
-
}
|
|
53006
|
-
if (ext === "tar" || ext === "rar" || ext === "7z") {
|
|
53007
|
-
return "Extract files and upload them individually, or use .zip format";
|
|
53008
|
-
}
|
|
53009
|
-
if (ext === "exe" || ext === "dll" || ext === "so" || ext === "dylib") {
|
|
53010
|
-
return "Binary files are not supported";
|
|
53011
|
-
}
|
|
53012
|
-
return;
|
|
52416
|
+
async function postSkippedFilesFeedback(platform, threadId, skipped) {
|
|
52417
|
+
if (skipped.length === 0)
|
|
52418
|
+
return;
|
|
52419
|
+
await platform.createPost(formatSkippedFilesFeedback(skipped), threadId);
|
|
53013
52420
|
}
|
|
53014
|
-
|
|
53015
|
-
const
|
|
53016
|
-
const
|
|
53017
|
-
|
|
53018
|
-
|
|
53019
|
-
|
|
53020
|
-
for (const file2 of files) {
|
|
53021
|
-
const category = categorizeFile(file2);
|
|
53022
|
-
if (category === "zip") {
|
|
53023
|
-
const zipResult = await processZipFile(file2, platform, debug);
|
|
53024
|
-
blocks.push(...zipResult.blocks);
|
|
53025
|
-
for (const s of zipResult.skipped) {
|
|
53026
|
-
skipped.push(s);
|
|
53027
|
-
log2.warn(`Skipped file ${s.name}: ${s.reason}`);
|
|
53028
|
-
}
|
|
53029
|
-
continue;
|
|
53030
|
-
}
|
|
53031
|
-
let result;
|
|
53032
|
-
switch (category) {
|
|
53033
|
-
case "image":
|
|
53034
|
-
result = await processImageFile(file2, platform, debug);
|
|
53035
|
-
break;
|
|
53036
|
-
case "pdf":
|
|
53037
|
-
result = await processPdfFile(file2, platform, debug);
|
|
53038
|
-
break;
|
|
53039
|
-
case "text":
|
|
53040
|
-
result = await processTextFile(file2, platform, debug);
|
|
53041
|
-
break;
|
|
53042
|
-
case "gzip":
|
|
53043
|
-
result = await processGzipFile(file2, platform, debug);
|
|
53044
|
-
break;
|
|
53045
|
-
case "unsupported":
|
|
53046
|
-
default:
|
|
53047
|
-
result = {
|
|
53048
|
-
skipped: {
|
|
53049
|
-
name: file2.name,
|
|
53050
|
-
reason: `Unsupported file type: ${file2.mimeType}`,
|
|
53051
|
-
suggestion: getUnsupportedFileSuggestion(file2)
|
|
53052
|
-
}
|
|
53053
|
-
};
|
|
53054
|
-
break;
|
|
53055
|
-
}
|
|
53056
|
-
if (result.block) {
|
|
53057
|
-
blocks.push(result.block);
|
|
53058
|
-
}
|
|
53059
|
-
if (result.skipped) {
|
|
53060
|
-
skipped.push(result.skipped);
|
|
53061
|
-
log2.warn(`Skipped file ${result.skipped.name}: ${result.skipped.reason}`);
|
|
52421
|
+
function formatSkippedFilesFeedback(skippedFiles) {
|
|
52422
|
+
const lines = ["⚠️ **Some files could not be processed:**"];
|
|
52423
|
+
for (const file2 of skippedFiles) {
|
|
52424
|
+
let line = `- **${file2.name}**: ${file2.reason}`;
|
|
52425
|
+
if (file2.suggestion) {
|
|
52426
|
+
line += ` _(${file2.suggestion})_`;
|
|
53062
52427
|
}
|
|
52428
|
+
lines.push(line);
|
|
53063
52429
|
}
|
|
53064
|
-
return
|
|
52430
|
+
return lines.join(`
|
|
52431
|
+
`);
|
|
53065
52432
|
}
|
|
53066
52433
|
|
|
53067
52434
|
// src/operations/message-manager.ts
|
|
@@ -53495,20 +52862,15 @@ class MessageManager {
|
|
|
53495
52862
|
}
|
|
53496
52863
|
this.session.threadLogger?.logUserMessage(username || this.session.startedBy, message, displayName, files && files.length > 0);
|
|
53497
52864
|
await this.prepareForUserMessage();
|
|
53498
|
-
let skippedFiles = [];
|
|
53499
|
-
if (files && files.length > 0) {
|
|
53500
|
-
const fileResult = await processFiles(this.platform, files);
|
|
53501
|
-
skippedFiles = fileResult.skipped;
|
|
53502
|
-
}
|
|
53503
52865
|
let content = message;
|
|
52866
|
+
let skippedFiles = [];
|
|
53504
52867
|
if (this.buildMessageContentCallback) {
|
|
53505
|
-
|
|
52868
|
+
const built = await this.buildMessageContentCallback(message, this.platform, files);
|
|
52869
|
+
content = built.content;
|
|
52870
|
+
skippedFiles = built.skipped;
|
|
53506
52871
|
}
|
|
53507
52872
|
this.session.claude.sendMessage(content);
|
|
53508
|
-
|
|
53509
|
-
const feedback = this.formatSkippedFilesFeedback(skippedFiles);
|
|
53510
|
-
await this.platform.createPost(feedback, this.threadId);
|
|
53511
|
-
}
|
|
52873
|
+
await postSkippedFilesFeedback(this.platform, this.threadId, skippedFiles);
|
|
53512
52874
|
this.session.lastActivityAt = new Date;
|
|
53513
52875
|
this.session.isProcessing = true;
|
|
53514
52876
|
this.emitSessionUpdateCallback?.({ status: "active", isTyping: true });
|
|
@@ -53516,18 +52878,6 @@ class MessageManager {
|
|
|
53516
52878
|
logger.debug("User message sent to Claude");
|
|
53517
52879
|
return true;
|
|
53518
52880
|
}
|
|
53519
|
-
formatSkippedFilesFeedback(skippedFiles) {
|
|
53520
|
-
const lines = ["⚠️ **Some files could not be processed:**"];
|
|
53521
|
-
for (const file2 of skippedFiles) {
|
|
53522
|
-
let line = `- **${file2.name}**: ${file2.reason}`;
|
|
53523
|
-
if (file2.suggestion) {
|
|
53524
|
-
line += ` _(${file2.suggestion})_`;
|
|
53525
|
-
}
|
|
53526
|
-
lines.push(line);
|
|
53527
|
-
}
|
|
53528
|
-
return lines.join(`
|
|
53529
|
-
`);
|
|
53530
|
-
}
|
|
53531
52881
|
getSession() {
|
|
53532
52882
|
return this.session;
|
|
53533
52883
|
}
|
|
@@ -54444,11 +53794,19 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
54444
53794
|
getMcpServerPath() {
|
|
54445
53795
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54446
53796
|
const __dirname4 = dirname4(__filename2);
|
|
53797
|
+
const bundledPath = resolve3(__dirname4, "mcp", "permission-server.js");
|
|
53798
|
+
if (existsSync4(bundledPath)) {
|
|
53799
|
+
return bundledPath;
|
|
53800
|
+
}
|
|
54447
53801
|
return resolve3(__dirname4, "..", "mcp", "permission-server.js");
|
|
54448
53802
|
}
|
|
54449
53803
|
getStatusLineWriterPath() {
|
|
54450
53804
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
54451
53805
|
const __dirname4 = dirname4(__filename2);
|
|
53806
|
+
const bundledPath = resolve3(__dirname4, "statusline", "writer.js");
|
|
53807
|
+
if (existsSync4(bundledPath)) {
|
|
53808
|
+
return bundledPath;
|
|
53809
|
+
}
|
|
54452
53810
|
return resolve3(__dirname4, "..", "statusline", "writer.js");
|
|
54453
53811
|
}
|
|
54454
53812
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-threads",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"husky": "^9.1.7",
|
|
100
100
|
"lint-staged": "^16.2.7",
|
|
101
101
|
"prettier": "^3.4.2",
|
|
102
|
-
"typescript": "^
|
|
102
|
+
"typescript": "^6.0.2",
|
|
103
103
|
"typescript-eslint": "^8.57.2",
|
|
104
104
|
"yazl": "^3.3.1"
|
|
105
105
|
},
|
|
@@ -118,13 +118,15 @@
|
|
|
118
118
|
"@hono/node-server": "$@hono/node-server",
|
|
119
119
|
"express-rate-limit": "$express-rate-limit",
|
|
120
120
|
"flatted": ">=3.4.0",
|
|
121
|
-
"picomatch": ">=2.3.2"
|
|
121
|
+
"picomatch": ">=2.3.2",
|
|
122
|
+
"path-to-regexp": ">=8.4.0"
|
|
122
123
|
},
|
|
123
124
|
"resolutions": {
|
|
124
125
|
"hono": "$hono",
|
|
125
126
|
"@hono/node-server": "$@hono/node-server",
|
|
126
127
|
"express-rate-limit": "$express-rate-limit",
|
|
127
128
|
"flatted": ">=3.4.0",
|
|
128
|
-
"picomatch": ">=2.3.2"
|
|
129
|
+
"picomatch": ">=2.3.2",
|
|
130
|
+
"path-to-regexp": ">=8.4.0"
|
|
129
131
|
}
|
|
130
132
|
}
|