agent-slack 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +121 -28
- package/dist/index.js.map +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -293,7 +293,7 @@ When to use which:
|
|
|
293
293
|
|
|
294
294
|
### Files (snippets/images/attachments)
|
|
295
295
|
|
|
296
|
-
`message get/list` auto-download attached files to an agent-friendly temp directory and return
|
|
296
|
+
`message get/list` auto-download attached files to an agent-friendly temp directory and return file metadata in `message.files[]`, including `name` when Slack provides the original filename and `path` for the local download. Failed downloads keep the attachment entry, preserve `message.files[].path` with a local `.download-error.txt` file, and include `message.files[].error`. `search messages` and `search all` use the same attachment shape for message results, while `search files` skips entries whose download fails.
|
|
297
297
|
|
|
298
298
|
- macOS default: `~/.agent-slack/tmp/downloads/`
|
|
299
299
|
|
package/dist/index.js
CHANGED
|
@@ -2417,6 +2417,14 @@ function registerAuthCommand(input) {
|
|
|
2417
2417
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
|
|
2418
2418
|
import { basename, join as join9, resolve } from "node:path";
|
|
2419
2419
|
import { existsSync as existsSync7 } from "node:fs";
|
|
2420
|
+
class SlackDownloadError extends Error {
|
|
2421
|
+
httpStatus;
|
|
2422
|
+
constructor(message, httpStatus) {
|
|
2423
|
+
super(message);
|
|
2424
|
+
this.httpStatus = httpStatus;
|
|
2425
|
+
this.name = "SlackDownloadError";
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2420
2428
|
async function downloadSlackFile(input) {
|
|
2421
2429
|
const { auth, url, destDir, preferredName, options } = input;
|
|
2422
2430
|
const absDir = resolve(destDir);
|
|
@@ -2435,19 +2443,54 @@ async function downloadSlackFile(input) {
|
|
|
2435
2443
|
headers.Referer = "https://app.slack.com/";
|
|
2436
2444
|
headers["User-Agent"] = getUserAgent();
|
|
2437
2445
|
}
|
|
2438
|
-
|
|
2446
|
+
let resp;
|
|
2447
|
+
try {
|
|
2448
|
+
resp = await fetch(url, { headers });
|
|
2449
|
+
} catch (err) {
|
|
2450
|
+
throw new SlackDownloadError(`Network error: ${err instanceof Error ? err.message : String(err)}`);
|
|
2451
|
+
}
|
|
2439
2452
|
if (!resp.ok) {
|
|
2440
|
-
throw new
|
|
2453
|
+
throw new SlackDownloadError(`Failed to download file (${resp.status})`, resp.status);
|
|
2441
2454
|
}
|
|
2442
2455
|
const contentType = resp.headers.get("content-type") || "";
|
|
2443
2456
|
if (!options?.allowHtml && contentType.includes("text/html")) {
|
|
2444
|
-
|
|
2445
|
-
|
|
2457
|
+
let text;
|
|
2458
|
+
try {
|
|
2459
|
+
text = await resp.text();
|
|
2460
|
+
} catch (err) {
|
|
2461
|
+
throw new SlackDownloadError(`Failed to read download response body: ${err instanceof Error ? err.message : String(err)}`, resp.status);
|
|
2462
|
+
}
|
|
2463
|
+
throw new SlackDownloadError(`Downloaded HTML instead of file (auth likely failed). First bytes: ${JSON.stringify(text.slice(0, 120))}`, resp.status);
|
|
2464
|
+
}
|
|
2465
|
+
let arrayBuffer;
|
|
2466
|
+
try {
|
|
2467
|
+
arrayBuffer = await resp.arrayBuffer();
|
|
2468
|
+
} catch (err) {
|
|
2469
|
+
throw new SlackDownloadError(`Failed to read download response body: ${err instanceof Error ? err.message : String(err)}`, resp.status);
|
|
2446
2470
|
}
|
|
2447
|
-
const buf = Buffer.from(
|
|
2471
|
+
const buf = Buffer.from(arrayBuffer);
|
|
2448
2472
|
await writeFile2(path, buf);
|
|
2449
2473
|
return path;
|
|
2450
2474
|
}
|
|
2475
|
+
async function tryDownloadSlackFile(input) {
|
|
2476
|
+
try {
|
|
2477
|
+
const path = await downloadSlackFile(input);
|
|
2478
|
+
return { ok: true, path };
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
if (err instanceof SlackDownloadError) {
|
|
2481
|
+
return { ok: false, error: err.message, httpStatus: err.httpStatus };
|
|
2482
|
+
}
|
|
2483
|
+
throw err;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
async function writeDownloadErrorFile(input) {
|
|
2487
|
+
const absDir = resolve(input.destDir);
|
|
2488
|
+
await mkdir3(absDir, { recursive: true });
|
|
2489
|
+
const path = join9(absDir, sanitizeFilename(`${input.fileId}.download-error.txt`));
|
|
2490
|
+
await writeFile2(path, `${input.error}
|
|
2491
|
+
`, "utf8");
|
|
2492
|
+
return path;
|
|
2493
|
+
}
|
|
2451
2494
|
function sanitizeFilename(name) {
|
|
2452
2495
|
return name.replace(/[\\/<>:"|?*]/g, "_");
|
|
2453
2496
|
}
|
|
@@ -3129,14 +3172,16 @@ function toCompactMessage(msg, input) {
|
|
|
3129
3172
|
const content = maxBodyChars >= 0 && rendered.length > maxBodyChars ? `${rendered.slice(0, maxBodyChars)}
|
|
3130
3173
|
…` : rendered;
|
|
3131
3174
|
const files = msg.files?.map((f) => {
|
|
3132
|
-
const
|
|
3133
|
-
if (!
|
|
3175
|
+
const entry = input?.downloadedPaths?.[f.id];
|
|
3176
|
+
if (!entry) {
|
|
3134
3177
|
return null;
|
|
3135
3178
|
}
|
|
3136
|
-
return {
|
|
3179
|
+
return entry.ok ? { name: f.name, mimetype: f.mimetype, mode: f.mode, path: entry.path } : {
|
|
3180
|
+
name: f.name,
|
|
3137
3181
|
mimetype: f.mimetype,
|
|
3138
3182
|
mode: f.mode,
|
|
3139
|
-
path
|
|
3183
|
+
path: entry.path,
|
|
3184
|
+
error: entry.error
|
|
3140
3185
|
};
|
|
3141
3186
|
}).filter((f) => Boolean(f));
|
|
3142
3187
|
return {
|
|
@@ -3743,7 +3788,7 @@ async function downloadCanvasAsMarkdown(input) {
|
|
|
3743
3788
|
});
|
|
3744
3789
|
const html = await readFile6(htmlPath, "utf8");
|
|
3745
3790
|
if (looksLikeAuthPage(html)) {
|
|
3746
|
-
throw new
|
|
3791
|
+
throw new SlackDownloadError("Downloaded auth/login page instead of canvas content (token may be expired)");
|
|
3747
3792
|
}
|
|
3748
3793
|
const markdown = htmlToMarkdown(html).trim();
|
|
3749
3794
|
const safeName = `${input.fileId.replace(/[\\/<>"|?*]/g, "_")}.md`;
|
|
@@ -3764,25 +3809,53 @@ async function downloadMessageFiles(input) {
|
|
|
3764
3809
|
if (!url) {
|
|
3765
3810
|
continue;
|
|
3766
3811
|
}
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3812
|
+
if (isCanvas) {
|
|
3813
|
+
try {
|
|
3814
|
+
const path = await downloadCanvasAsMarkdown({
|
|
3770
3815
|
auth: input.auth,
|
|
3771
3816
|
fileId: file.id,
|
|
3772
3817
|
url,
|
|
3773
3818
|
destDir: downloadsDir
|
|
3774
3819
|
});
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3820
|
+
downloadedPaths[file.id] = { ok: true, path };
|
|
3821
|
+
} catch (err) {
|
|
3822
|
+
if (!(err instanceof SlackDownloadError)) {
|
|
3823
|
+
throw err;
|
|
3824
|
+
}
|
|
3825
|
+
const path = await writeDownloadErrorFile({
|
|
3780
3826
|
destDir: downloadsDir,
|
|
3781
|
-
|
|
3827
|
+
fileId: file.id,
|
|
3828
|
+
error: err.message
|
|
3782
3829
|
});
|
|
3830
|
+
downloadedPaths[file.id] = {
|
|
3831
|
+
ok: false,
|
|
3832
|
+
error: err.message,
|
|
3833
|
+
httpStatus: err.httpStatus,
|
|
3834
|
+
path
|
|
3835
|
+
};
|
|
3836
|
+
console.error(`Warning: skipping file ${file.id}: ${err.message}`);
|
|
3837
|
+
}
|
|
3838
|
+
} else {
|
|
3839
|
+
const ext = inferFileExtension(file);
|
|
3840
|
+
const result = await tryDownloadSlackFile({
|
|
3841
|
+
auth: input.auth,
|
|
3842
|
+
url,
|
|
3843
|
+
destDir: downloadsDir,
|
|
3844
|
+
preferredName: `${file.id}${ext ? `.${ext}` : ""}`
|
|
3845
|
+
});
|
|
3846
|
+
if (!result.ok) {
|
|
3847
|
+
downloadedPaths[file.id] = {
|
|
3848
|
+
...result,
|
|
3849
|
+
path: await writeDownloadErrorFile({
|
|
3850
|
+
destDir: downloadsDir,
|
|
3851
|
+
fileId: file.id,
|
|
3852
|
+
error: result.error
|
|
3853
|
+
})
|
|
3854
|
+
};
|
|
3855
|
+
console.error(`Warning: skipping file ${file.id}: ${result.error}`);
|
|
3856
|
+
} else {
|
|
3857
|
+
downloadedPaths[file.id] = result;
|
|
3783
3858
|
}
|
|
3784
|
-
} catch (err) {
|
|
3785
|
-
console.error(`Warning: skipping file ${file.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3786
3859
|
}
|
|
3787
3860
|
}
|
|
3788
3861
|
}
|
|
@@ -6614,18 +6687,22 @@ async function searchFilesViaSearchApi(client, input) {
|
|
|
6614
6687
|
if (!id) {
|
|
6615
6688
|
continue;
|
|
6616
6689
|
}
|
|
6617
|
-
const
|
|
6690
|
+
const result = await tryDownloadSlackFile({
|
|
6618
6691
|
auth: input.auth,
|
|
6619
6692
|
url,
|
|
6620
6693
|
destDir: downloadsDir,
|
|
6621
6694
|
preferredName: `${id}${ext ? `.${ext}` : ""}`
|
|
6622
6695
|
});
|
|
6696
|
+
if (!result.ok) {
|
|
6697
|
+
console.warn(`Warning: skipping file ${id}: ${result.error}`);
|
|
6698
|
+
continue;
|
|
6699
|
+
}
|
|
6623
6700
|
const title = (getString(f.title) || getString(f.name) || "").trim();
|
|
6624
6701
|
out.push({
|
|
6625
6702
|
title: title || undefined,
|
|
6626
6703
|
mimetype,
|
|
6627
6704
|
mode,
|
|
6628
|
-
path
|
|
6705
|
+
path: result.path
|
|
6629
6706
|
});
|
|
6630
6707
|
if (out.length >= input.limit) {
|
|
6631
6708
|
break;
|
|
@@ -6680,17 +6757,21 @@ async function searchFilesInChannelsFallback(client, input) {
|
|
|
6680
6757
|
if (!id) {
|
|
6681
6758
|
continue;
|
|
6682
6759
|
}
|
|
6683
|
-
const
|
|
6760
|
+
const result = await tryDownloadSlackFile({
|
|
6684
6761
|
auth: input.auth,
|
|
6685
6762
|
url,
|
|
6686
6763
|
destDir: downloadsDir,
|
|
6687
6764
|
preferredName: `${id}${ext ? `.${ext}` : ""}`
|
|
6688
6765
|
});
|
|
6766
|
+
if (!result.ok) {
|
|
6767
|
+
console.warn(`Warning: skipping file ${id}: ${result.error}`);
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6689
6770
|
out.push({
|
|
6690
6771
|
title: title || undefined,
|
|
6691
6772
|
mimetype,
|
|
6692
6773
|
mode,
|
|
6693
|
-
path
|
|
6774
|
+
path: result.path
|
|
6694
6775
|
});
|
|
6695
6776
|
if (out.length >= input.limit) {
|
|
6696
6777
|
return out;
|
|
@@ -6906,13 +6987,25 @@ async function downloadFilesForMessage(input) {
|
|
|
6906
6987
|
continue;
|
|
6907
6988
|
}
|
|
6908
6989
|
const ext = inferExt(f);
|
|
6909
|
-
const
|
|
6990
|
+
const result = await tryDownloadSlackFile({
|
|
6910
6991
|
auth: input.auth,
|
|
6911
6992
|
url,
|
|
6912
6993
|
destDir: input.downloadsDir,
|
|
6913
6994
|
preferredName: `${f.id}${ext ? `.${ext}` : ""}`
|
|
6914
6995
|
});
|
|
6915
|
-
|
|
6996
|
+
if (!result.ok) {
|
|
6997
|
+
input.downloadedPaths[f.id] = {
|
|
6998
|
+
...result,
|
|
6999
|
+
path: await writeDownloadErrorFile({
|
|
7000
|
+
destDir: input.downloadsDir,
|
|
7001
|
+
fileId: f.id,
|
|
7002
|
+
error: result.error
|
|
7003
|
+
})
|
|
7004
|
+
};
|
|
7005
|
+
console.warn(`Warning: file ${f.id}: ${result.error}`);
|
|
7006
|
+
} else {
|
|
7007
|
+
input.downloadedPaths[f.id] = result;
|
|
7008
|
+
}
|
|
6916
7009
|
}
|
|
6917
7010
|
}
|
|
6918
7011
|
function messageSummaryFromApiMessage(channelId, msg) {
|
|
@@ -7848,5 +7941,5 @@ if (subcommand && subcommand !== "update") {
|
|
|
7848
7941
|
backgroundUpdateCheck();
|
|
7849
7942
|
}
|
|
7850
7943
|
|
|
7851
|
-
//# debugId=
|
|
7944
|
+
//# debugId=3EBC31FAC3ADA2FD64756E2164756E21
|
|
7852
7945
|
//# sourceMappingURL=index.js.map
|