agent-slack 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/index.js +56 -16
- package/dist/index.js.map +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,6 +231,8 @@ Attach options for `message send`:
|
|
|
231
231
|
|
|
232
232
|
- `--attach <path>` upload a local file (repeatable)
|
|
233
233
|
|
|
234
|
+
`message send` returns `channel_id` plus the posted `ts` and a `permalink` (for non-attachment sends). `thread_ts` appears only when replying in a thread.
|
|
235
|
+
|
|
234
236
|
### List, create, and invite channels
|
|
235
237
|
|
|
236
238
|
```bash
|
|
@@ -335,6 +337,81 @@ agent-slack user get U12345678 --workspace "https://workspace.slack.com" | jq .
|
|
|
335
337
|
agent-slack user get "@alice" --workspace "https://workspace.slack.com" | jq .
|
|
336
338
|
```
|
|
337
339
|
|
|
340
|
+
### Unreads (inbox view)
|
|
341
|
+
|
|
342
|
+
See all unread messages across channels, DMs, and threads in one place:
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
# Show all unreads with message content
|
|
346
|
+
agent-slack unreads
|
|
347
|
+
|
|
348
|
+
# Show only unread counts (no message content)
|
|
349
|
+
agent-slack unreads --counts-only
|
|
350
|
+
|
|
351
|
+
# Limit messages per channel (default 10)
|
|
352
|
+
agent-slack unreads --max-messages 5
|
|
353
|
+
|
|
354
|
+
# Include system messages (joins, leaves, topic changes)
|
|
355
|
+
agent-slack unreads --include-system
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Output includes channels sorted by mention count, then unread count:
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
{
|
|
362
|
+
"channels": [
|
|
363
|
+
{
|
|
364
|
+
"channel_id": "C123...",
|
|
365
|
+
"channel_name": "general",
|
|
366
|
+
"channel_type": "channel",
|
|
367
|
+
"unread_count": 5,
|
|
368
|
+
"mention_count": 2,
|
|
369
|
+
"messages": [...]
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
"threads": {
|
|
373
|
+
"has_unreads": true,
|
|
374
|
+
"mention_count": 3
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Note: This feature uses the `client.counts` API which may be restricted in some Enterprise Grid workspaces (`team_is_restricted` error).
|
|
380
|
+
|
|
381
|
+
### Later (saved messages)
|
|
382
|
+
|
|
383
|
+
Manage your saved-for-later messages (Slack's Later tab):
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# List saved messages (in-progress by default)
|
|
387
|
+
agent-slack later list
|
|
388
|
+
|
|
389
|
+
# Show only counts per state
|
|
390
|
+
agent-slack later list --counts-only
|
|
391
|
+
|
|
392
|
+
# Filter by state: in_progress, completed, archived, all
|
|
393
|
+
agent-slack later list --state completed
|
|
394
|
+
|
|
395
|
+
# Save a message for later
|
|
396
|
+
agent-slack later save "https://workspace.slack.com/archives/C123/p1700000000000000"
|
|
397
|
+
|
|
398
|
+
# Mark as completed
|
|
399
|
+
agent-slack later complete "https://workspace.slack.com/archives/C123/p1700000000000000"
|
|
400
|
+
|
|
401
|
+
# Archive
|
|
402
|
+
agent-slack later archive "https://workspace.slack.com/archives/C123/p1700000000000000"
|
|
403
|
+
|
|
404
|
+
# Move back to in-progress
|
|
405
|
+
agent-slack later reopen "https://workspace.slack.com/archives/C123/p1700000000000000"
|
|
406
|
+
|
|
407
|
+
# Remove from saved
|
|
408
|
+
agent-slack later remove "https://workspace.slack.com/archives/C123/p1700000000000000"
|
|
409
|
+
|
|
410
|
+
# Set a reminder
|
|
411
|
+
agent-slack later remind "https://workspace.slack.com/archives/C123/p1700000000000000" --in 1h
|
|
412
|
+
agent-slack later remind "https://workspace.slack.com/archives/C123/p1700000000000000" --in tomorrow
|
|
413
|
+
```
|
|
414
|
+
|
|
338
415
|
### Fetch a Canvas as Markdown
|
|
339
416
|
|
|
340
417
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -3541,6 +3541,16 @@ function parseSlackMessageUrl(input) {
|
|
|
3541
3541
|
const workspace_url = `${url.protocol}//${url.host}`;
|
|
3542
3542
|
return { workspace_url, channel_id, message_ts, thread_ts_hint, raw: input, possiblyTruncated };
|
|
3543
3543
|
}
|
|
3544
|
+
function buildSlackMessageUrl(input) {
|
|
3545
|
+
const workspaceUrl = input.workspace_url.replace(/\/$/, "");
|
|
3546
|
+
const digits = input.message_ts.replace(".", "");
|
|
3547
|
+
const url = new URL(`${workspaceUrl}/archives/${input.channel_id}/p${digits}`);
|
|
3548
|
+
if (input.thread_ts && input.thread_ts !== input.message_ts) {
|
|
3549
|
+
url.searchParams.set("thread_ts", input.thread_ts);
|
|
3550
|
+
url.searchParams.set("cid", input.channel_id);
|
|
3551
|
+
}
|
|
3552
|
+
return url.toString();
|
|
3553
|
+
}
|
|
3544
3554
|
|
|
3545
3555
|
// src/cli/targets.ts
|
|
3546
3556
|
function parseMsgTarget(input) {
|
|
@@ -4013,12 +4023,13 @@ async function resolveUserId(client, input) {
|
|
|
4013
4023
|
if (!handle) {
|
|
4014
4024
|
return null;
|
|
4015
4025
|
}
|
|
4026
|
+
const handleLower = handle.toLowerCase();
|
|
4016
4027
|
let cursor;
|
|
4017
4028
|
for (;; ) {
|
|
4018
4029
|
const resp = await client.api("users.list", { limit: 200, cursor });
|
|
4019
4030
|
const members = asArray(resp.members).filter(isRecord);
|
|
4020
4031
|
const found = members.find((m) => {
|
|
4021
|
-
if (getString(m.name) ===
|
|
4032
|
+
if (getString(m.name)?.toLowerCase() === handleLower) {
|
|
4022
4033
|
return true;
|
|
4023
4034
|
}
|
|
4024
4035
|
if (looksLikeEmail) {
|
|
@@ -4589,14 +4600,15 @@ async function sendMessage(input) {
|
|
|
4589
4600
|
if (target.kind === "url") {
|
|
4590
4601
|
const { ref } = target;
|
|
4591
4602
|
warnOnTruncatedSlackUrl(ref);
|
|
4592
|
-
await input.ctx.withAutoRefresh({
|
|
4603
|
+
return await input.ctx.withAutoRefresh({
|
|
4593
4604
|
workspaceUrl: ref.workspace_url,
|
|
4594
4605
|
work: async () => {
|
|
4595
|
-
const { client } = await input.ctx.getClientForWorkspace(ref.workspace_url);
|
|
4606
|
+
const { client, workspace_url } = await input.ctx.getClientForWorkspace(ref.workspace_url);
|
|
4596
4607
|
const msg = await fetchMessage(client, { ref });
|
|
4597
4608
|
const threadTs = msg.thread_ts ?? msg.ts;
|
|
4598
|
-
await sendMessageToChannel({
|
|
4609
|
+
return await sendMessageToChannel({
|
|
4599
4610
|
client,
|
|
4611
|
+
workspaceUrl: workspace_url ?? ref.workspace_url,
|
|
4600
4612
|
channelId: ref.channel_id,
|
|
4601
4613
|
text: input.text,
|
|
4602
4614
|
blocks,
|
|
@@ -4605,17 +4617,17 @@ async function sendMessage(input) {
|
|
|
4605
4617
|
});
|
|
4606
4618
|
}
|
|
4607
4619
|
});
|
|
4608
|
-
return { ok: true };
|
|
4609
4620
|
}
|
|
4610
4621
|
if (target.kind === "user") {
|
|
4611
4622
|
const workspaceUrl2 = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
|
|
4612
|
-
await input.ctx.withAutoRefresh({
|
|
4623
|
+
return await input.ctx.withAutoRefresh({
|
|
4613
4624
|
workspaceUrl: workspaceUrl2,
|
|
4614
4625
|
work: async () => {
|
|
4615
|
-
const { client } = await input.ctx.getClientForWorkspace(workspaceUrl2);
|
|
4626
|
+
const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl2);
|
|
4616
4627
|
const dmChannelId = await openDmChannel(client, target.userId);
|
|
4617
|
-
await sendMessageToChannel({
|
|
4628
|
+
return await sendMessageToChannel({
|
|
4618
4629
|
client,
|
|
4630
|
+
workspaceUrl: workspace_url ?? workspaceUrl2,
|
|
4619
4631
|
channelId: dmChannelId,
|
|
4620
4632
|
text: input.text,
|
|
4621
4633
|
blocks,
|
|
@@ -4623,20 +4635,20 @@ async function sendMessage(input) {
|
|
|
4623
4635
|
});
|
|
4624
4636
|
}
|
|
4625
4637
|
});
|
|
4626
|
-
return { ok: true };
|
|
4627
4638
|
}
|
|
4628
4639
|
const workspaceUrl = input.ctx.effectiveWorkspaceUrl(input.options.workspace);
|
|
4629
4640
|
await input.ctx.assertWorkspaceSpecifiedForChannelNames({
|
|
4630
4641
|
workspaceUrl,
|
|
4631
4642
|
channels: [String(target.channel)]
|
|
4632
4643
|
});
|
|
4633
|
-
await input.ctx.withAutoRefresh({
|
|
4644
|
+
return await input.ctx.withAutoRefresh({
|
|
4634
4645
|
workspaceUrl,
|
|
4635
4646
|
work: async () => {
|
|
4636
|
-
const { client } = await input.ctx.getClientForWorkspace(workspaceUrl);
|
|
4647
|
+
const { client, workspace_url } = await input.ctx.getClientForWorkspace(workspaceUrl);
|
|
4637
4648
|
const channelId = await resolveChannelId(client, String(target.channel));
|
|
4638
|
-
await sendMessageToChannel({
|
|
4649
|
+
return await sendMessageToChannel({
|
|
4639
4650
|
client,
|
|
4651
|
+
workspaceUrl: workspace_url ?? workspaceUrl,
|
|
4640
4652
|
channelId,
|
|
4641
4653
|
text: input.text,
|
|
4642
4654
|
blocks,
|
|
@@ -4645,7 +4657,6 @@ async function sendMessage(input) {
|
|
|
4645
4657
|
});
|
|
4646
4658
|
}
|
|
4647
4659
|
});
|
|
4648
|
-
return { ok: true };
|
|
4649
4660
|
}
|
|
4650
4661
|
function normalizeAttachPaths(raw) {
|
|
4651
4662
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
@@ -4661,13 +4672,27 @@ function normalizeAttachPaths(raw) {
|
|
|
4661
4672
|
}
|
|
4662
4673
|
async function sendMessageToChannel(input) {
|
|
4663
4674
|
if (input.attachPaths.length === 0) {
|
|
4664
|
-
await input.client.api("chat.postMessage", {
|
|
4675
|
+
const resp = await input.client.api("chat.postMessage", {
|
|
4665
4676
|
channel: input.channelId,
|
|
4666
4677
|
text: input.text,
|
|
4667
4678
|
thread_ts: input.threadTs,
|
|
4668
4679
|
...input.blocks ? { blocks: input.blocks } : {}
|
|
4669
4680
|
});
|
|
4670
|
-
|
|
4681
|
+
const ts = typeof resp.ts === "string" ? resp.ts : undefined;
|
|
4682
|
+
const channelId = typeof resp.channel === "string" ? resp.channel : input.channelId;
|
|
4683
|
+
const permalink = input.workspaceUrl && ts ? buildSlackMessageUrl({
|
|
4684
|
+
workspace_url: input.workspaceUrl,
|
|
4685
|
+
channel_id: channelId,
|
|
4686
|
+
message_ts: ts,
|
|
4687
|
+
thread_ts: input.threadTs
|
|
4688
|
+
}) : undefined;
|
|
4689
|
+
return {
|
|
4690
|
+
ok: true,
|
|
4691
|
+
channel_id: channelId,
|
|
4692
|
+
ts,
|
|
4693
|
+
thread_ts: input.threadTs,
|
|
4694
|
+
permalink
|
|
4695
|
+
};
|
|
4671
4696
|
}
|
|
4672
4697
|
if (input.blocks) {
|
|
4673
4698
|
process.stderr.write(`Warning: rich text formatting is not supported with file attachments; sending as plain text.
|
|
@@ -4684,6 +4709,11 @@ async function sendMessageToChannel(input) {
|
|
|
4684
4709
|
});
|
|
4685
4710
|
initialComment = "";
|
|
4686
4711
|
}
|
|
4712
|
+
return {
|
|
4713
|
+
ok: true,
|
|
4714
|
+
channel_id: input.channelId,
|
|
4715
|
+
thread_ts: input.threadTs
|
|
4716
|
+
};
|
|
4687
4717
|
}
|
|
4688
4718
|
async function editMessage(input) {
|
|
4689
4719
|
const target = parseMsgTarget(String(input.targetInput));
|
|
@@ -8430,6 +8460,16 @@ async function performUpdate(latest) {
|
|
|
8430
8460
|
if (actual !== expected) {
|
|
8431
8461
|
return { success: false, message: `Checksum mismatch: expected ${expected}, got ${actual}` };
|
|
8432
8462
|
}
|
|
8463
|
+
if (process.platform === "darwin") {
|
|
8464
|
+
try {
|
|
8465
|
+
execSync2(`codesign --remove-signature ${JSON.stringify(binTmp)}`, {
|
|
8466
|
+
stdio: "ignore"
|
|
8467
|
+
});
|
|
8468
|
+
execSync2(`codesign --sign - ${JSON.stringify(binTmp)}`, {
|
|
8469
|
+
stdio: "ignore"
|
|
8470
|
+
});
|
|
8471
|
+
} catch {}
|
|
8472
|
+
}
|
|
8433
8473
|
const currentBin = process.execPath;
|
|
8434
8474
|
const backupPath = `${currentBin}.bak`;
|
|
8435
8475
|
await rename(currentBin, backupPath);
|
|
@@ -9145,5 +9185,5 @@ if (subcommand && subcommand !== "update") {
|
|
|
9145
9185
|
backgroundUpdateCheck();
|
|
9146
9186
|
}
|
|
9147
9187
|
|
|
9148
|
-
//# debugId=
|
|
9188
|
+
//# debugId=5E5A3A0EB875DD7164756E2164756E21
|
|
9149
9189
|
//# sourceMappingURL=index.js.map
|