eniac-slack 0.1.3 → 0.1.5
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/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/handlers/mention.d.ts +3 -2
- package/dist/handlers/mention.d.ts.map +1 -1
- package/dist/handlers/mention.js +20 -10
- package/dist/handlers/mention.js.map +1 -1
- package/dist/handlers/thread.d.ts.map +1 -1
- package/dist/handlers/thread.js +11 -5
- package/dist/handlers/thread.js.map +1 -1
- package/dist/services/claude.d.ts +2 -1
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +27 -2
- package/dist/services/claude.js.map +1 -1
- package/dist/services/permissions.d.ts.map +1 -1
- package/dist/services/permissions.js +2 -0
- package/dist/services/permissions.js.map +1 -1
- package/dist/services/slack-messenger.d.ts.map +1 -1
- package/dist/services/slack-messenger.js +2 -0
- package/dist/services/slack-messenger.js.map +1 -1
- package/dist/utils/slack-files.d.ts +16 -0
- package/dist/utils/slack-files.d.ts.map +1 -0
- package/dist/utils/slack-files.js +44 -0
- package/dist/utils/slack-files.js.map +1 -0
- package/package.json +1 -1
- package/src/constants.ts +1 -0
- package/src/handlers/mention.ts +24 -10
- package/src/handlers/thread.ts +12 -5
- package/src/services/claude.ts +31 -2
- package/src/services/permissions.ts +2 -0
- package/src/services/slack-messenger.ts +2 -0
- package/src/utils/slack-files.ts +57 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,2CAA2C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,wCAAwC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AllMiddlewareArgs, SlackEventMiddlewareArgs } from "@slack/bolt";
|
|
2
2
|
import { type GithubRepo } from "../utils/parse.js";
|
|
3
|
+
import { type SlackImageFile } from "../utils/slack-files.js";
|
|
3
4
|
/**
|
|
4
5
|
* Pending repo creation requests — keyed by a unique request ID.
|
|
5
6
|
*/
|
|
@@ -23,9 +24,9 @@ export declare function handleMention(args: AllMiddlewareArgs & SlackEventMiddle
|
|
|
23
24
|
/**
|
|
24
25
|
* Start a session with a GitHub repository working directory.
|
|
25
26
|
*/
|
|
26
|
-
export declare function startSessionWithRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, repoInfo: GithubRepo, cleanText: string, authorUserId: string): Promise<void>;
|
|
27
|
+
export declare function startSessionWithRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, repoInfo: GithubRepo, cleanText: string, authorUserId: string, images?: SlackImageFile[]): Promise<void>;
|
|
27
28
|
/**
|
|
28
29
|
* Start a session without a repository (temp directory).
|
|
29
30
|
*/
|
|
30
|
-
export declare function startSessionWithoutRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, cleanText: string, authorUserId: string): Promise<void>;
|
|
31
|
+
export declare function startSessionWithoutRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, cleanText: string, authorUserId: string, images?: SlackImageFile[]): Promise<void>;
|
|
31
32
|
//# sourceMappingURL=mention.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAItF,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMnF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,UAAU,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,SAAS,CAEhC;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEhE;AAID,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD;AASD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,CAAC,aAAa,CAAC,GAChE,OAAO,CAAC,IAAI,CAAC,CA+Ff;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,cAAc,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,cAAc,EAAE,GACxB,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
package/dist/handlers/mention.js
CHANGED
|
@@ -2,6 +2,8 @@ import { removeMention, extractGithubRepo } from "../utils/parse.js";
|
|
|
2
2
|
import { createSession, getSession, chat } from "../services/claude.js";
|
|
3
3
|
import { prepareWorkDir, checkRepoExists } from "../services/git.js";
|
|
4
4
|
import { postStreamingReply } from "../services/slack-messenger.js";
|
|
5
|
+
import { downloadSlackImages } from "../utils/slack-files.js";
|
|
6
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
5
7
|
import fs from "node:fs/promises";
|
|
6
8
|
import os from "node:os";
|
|
7
9
|
import path from "node:path";
|
|
@@ -33,9 +35,12 @@ export async function handleMention(args) {
|
|
|
33
35
|
const { event, client } = args;
|
|
34
36
|
const { text, channel, ts, thread_ts } = event;
|
|
35
37
|
const authorUserId = event.user ?? "unknown";
|
|
38
|
+
const files = "files" in event ? event.files : undefined;
|
|
39
|
+
// Download any image attachments
|
|
40
|
+
const images = files?.length ? await downloadSlackImages(client, files) : [];
|
|
36
41
|
// If the mention is inside an existing thread, treat it as a thread reply
|
|
37
42
|
if (thread_ts) {
|
|
38
|
-
await handleThreadedMention(client, channel, thread_ts, text, authorUserId);
|
|
43
|
+
await handleThreadedMention(client, channel, thread_ts, text, authorUserId, images);
|
|
39
44
|
return;
|
|
40
45
|
}
|
|
41
46
|
// New conversation — ts becomes the thread_ts
|
|
@@ -46,6 +51,7 @@ export async function handleMention(args) {
|
|
|
46
51
|
channel,
|
|
47
52
|
thread_ts: threadTs,
|
|
48
53
|
text: "How can I help you? You can ask me coding questions, or mention a GitHub repo (e.g. `owner/repo`) to work with its code.",
|
|
54
|
+
icon_url: CLAUDE_ICON_URL,
|
|
49
55
|
});
|
|
50
56
|
return;
|
|
51
57
|
}
|
|
@@ -68,6 +74,7 @@ export async function handleMention(args) {
|
|
|
68
74
|
channel,
|
|
69
75
|
thread_ts: threadTs,
|
|
70
76
|
text: `:warning: \`${repoInfo.owner}/${repoInfo.repo}\` 리포지토리가 존재하지 않습니다. 생성할까요?`,
|
|
77
|
+
icon_url: CLAUDE_ICON_URL,
|
|
71
78
|
blocks: [
|
|
72
79
|
{
|
|
73
80
|
type: "section",
|
|
@@ -105,23 +112,24 @@ export async function handleMention(args) {
|
|
|
105
112
|
return;
|
|
106
113
|
}
|
|
107
114
|
// Repo exists — proceed with normal setup
|
|
108
|
-
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId);
|
|
115
|
+
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId, images);
|
|
109
116
|
}
|
|
110
117
|
else {
|
|
111
118
|
// No repo mentioned — use temp directory
|
|
112
|
-
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId);
|
|
119
|
+
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId, images);
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
/**
|
|
116
123
|
* Start a session with a GitHub repository working directory.
|
|
117
124
|
*/
|
|
118
|
-
export async function startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId) {
|
|
125
|
+
export async function startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId, images) {
|
|
119
126
|
let workDir;
|
|
120
127
|
try {
|
|
121
128
|
await client.chat.postMessage({
|
|
122
129
|
channel,
|
|
123
130
|
thread_ts: threadTs,
|
|
124
131
|
text: `:file_folder: Setting up repository \`${repoInfo.owner}/${repoInfo.repo}\`...`,
|
|
132
|
+
icon_url: CLAUDE_ICON_URL,
|
|
125
133
|
});
|
|
126
134
|
workDir = await prepareWorkDir(repoInfo, getReposBaseDir());
|
|
127
135
|
}
|
|
@@ -131,36 +139,38 @@ export async function startSessionWithRepo(client, channel, threadTs, repoInfo,
|
|
|
131
139
|
channel,
|
|
132
140
|
thread_ts: threadTs,
|
|
133
141
|
text: `:x: Failed to set up repository: ${message}`,
|
|
142
|
+
icon_url: CLAUDE_ICON_URL,
|
|
134
143
|
});
|
|
135
144
|
return;
|
|
136
145
|
}
|
|
137
146
|
createSession(threadTs, workDir, authorUserId);
|
|
138
147
|
const userMessage = `[Working directory: ${workDir} — Repository: ${repoInfo.owner}/${repoInfo.repo}]\n\n${cleanText}`;
|
|
139
|
-
const stream = chat(threadTs, userMessage, client, channel);
|
|
148
|
+
const stream = chat(threadTs, userMessage, client, channel, images?.length ? images : undefined);
|
|
140
149
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
141
150
|
}
|
|
142
151
|
/**
|
|
143
152
|
* Start a session without a repository (temp directory).
|
|
144
153
|
*/
|
|
145
|
-
export async function startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId) {
|
|
154
|
+
export async function startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId, images) {
|
|
146
155
|
const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "eniac-"));
|
|
147
156
|
createSession(threadTs, workDir, authorUserId);
|
|
148
|
-
const stream = chat(threadTs, cleanText, client, channel);
|
|
157
|
+
const stream = chat(threadTs, cleanText, client, channel, images?.length ? images : undefined);
|
|
149
158
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
150
159
|
}
|
|
151
160
|
/**
|
|
152
161
|
* Handle a mention that occurs inside an existing thread.
|
|
153
162
|
*/
|
|
154
|
-
async function handleThreadedMention(client, channel, threadTs, text, authorUserId) {
|
|
163
|
+
async function handleThreadedMention(client, channel, threadTs, text, authorUserId, images) {
|
|
155
164
|
const cleanText = removeMention(text);
|
|
156
|
-
if (!cleanText)
|
|
165
|
+
if (!cleanText && (!images || images.length === 0))
|
|
157
166
|
return;
|
|
158
167
|
let session = getSession(threadTs);
|
|
159
168
|
if (!session) {
|
|
160
169
|
const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "eniac-"));
|
|
161
170
|
session = createSession(threadTs, workDir, authorUserId);
|
|
162
171
|
}
|
|
163
|
-
const
|
|
172
|
+
const userMessage = cleanText || "이미지를 분석해주세요.";
|
|
173
|
+
const stream = chat(threadTs, userMessage, client, channel, images?.length ? images : undefined);
|
|
164
174
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
165
175
|
}
|
|
166
176
|
//# sourceMappingURL=mention.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAmB,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,oBAAoB,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAA8B,CAAC;AAElE,IAAI,gBAAgB,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,qBAAqB,CACnC,SAAiB;IAEjB,OAAO,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AACxC,CAAC;AAED,IAAI,YAAoB,CAAC;AAEzB,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,YAAY,GAAG,GAAG,CAAC;AACrB,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAiE;IAEjE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,IAAI,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAmB,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAoB,MAAM,oBAAoB,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAuB,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAA8B,CAAC;AAElE,IAAI,gBAAgB,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,qBAAqB,CACnC,SAAiB;IAEjB,OAAO,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AACxC,CAAC;AAED,IAAI,YAAoB,CAAC;AAEzB,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,YAAY,GAAG,GAAG,CAAC;AACrB,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAiE;IAEjE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,IAAI,SAAS,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAE,KAAkH,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAEvK,iCAAiC;IACjC,MAAM,MAAM,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7E,0EAA0E;IAC1E,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QACpF,OAAO;IACT,CAAC;IAED,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YAC5B,OAAO;YACP,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,0HAA0H;YAChI,QAAQ,EAAE,eAAe;SAC1B,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,mCAAmC;IACnC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAE9C,IAAI,QAAQ,EAAE,CAAC;QACb,iCAAiC;QACjC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEpE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,yDAAyD;YACzD,MAAM,SAAS,GAAG,QAAQ,EAAE,gBAAgB,EAAE,CAAC;YAC/C,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE;gBACjC,QAAQ;gBACR,QAAQ;gBACR,OAAO;gBACP,SAAS;gBACT,YAAY;aACb,CAAC,CAAC;YAEH,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;gBAC5B,OAAO;gBACP,SAAS,EAAE,QAAQ;gBACnB,IAAI,EAAE,eAAe,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,6BAA6B;gBACjF,QAAQ,EAAE,eAAe;gBACzB,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,SAAS;wBACf,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,eAAe,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,8BAA8B;yBACnF;qBACF;oBACD;wBACE,IAAI,EAAE,SAAS;wBACf,QAAQ,EAAE;4BACR;gCACE,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,kBAAkB,EAAE;gCACtD,KAAK,EAAE,SAAS;gCAChB,SAAS,EAAE,qBAAqB;gCAChC,KAAK,EAAE,SAAS;6BACjB;4BACD;gCACE,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,iCAAiC,EAAE;gCACrE,SAAS,EAAE,oBAAoB;gCAC/B,KAAK,EAAE,SAAS;6BACjB;4BACD;gCACE,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE;gCAC9C,SAAS,EAAE,WAAW;gCACtB,KAAK,EAAE,SAAS;6BACjB;yBACF;qBACF;iBACF;aACF,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,0CAA0C;QAC1C,MAAM,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IACnG,CAAC;SAAM,CAAC;QACN,yCAAyC;QACzC,MAAM,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC5F,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAmC,EACnC,OAAe,EACf,QAAgB,EAChB,QAAoB,EACpB,SAAiB,EACjB,YAAoB,EACpB,MAAyB;IAEzB,IAAI,OAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YAC5B,OAAO;YACP,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,yCAAyC,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,OAAO;YACrF,QAAQ,EAAE,eAAe;SAC1B,CAAC,CAAC;QAEH,OAAO,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YAC5B,OAAO;YACP,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,oCAAoC,OAAO,EAAE;YACnD,QAAQ,EAAE,eAAe;SAC1B,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAE/C,MAAM,WAAW,GAAG,uBAAuB,OAAO,kBAAkB,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,QAAQ,SAAS,EAAE,CAAC;IACvH,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjG,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAmC,EACnC,OAAe,EACf,QAAgB,EAChB,SAAiB,EACjB,YAAoB,EACpB,MAAyB;IAEzB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnE,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC/F,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,qBAAqB,CAClC,MAAmC,EACnC,OAAe,EACf,QAAgB,EAChB,IAAY,EACZ,YAAoB,EACpB,MAAyB;IAEzB,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO;IAE3D,IAAI,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;QACnE,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,IAAI,cAAc,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjG,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAK/E;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,CAAC,SAAS,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC,CAsDf"}
|
package/dist/handlers/thread.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getSession, chat } from "../services/claude.js";
|
|
2
2
|
import { postStreamingReply } from "../services/slack-messenger.js";
|
|
3
|
+
import { downloadSlackImages } from "../utils/slack-files.js";
|
|
3
4
|
/**
|
|
4
5
|
* Handle messages posted in threads where the bot has an active session.
|
|
5
6
|
*
|
|
@@ -29,6 +30,7 @@ export async function handleThreadMessage(args) {
|
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
const text = "text" in event ? event.text : undefined;
|
|
33
|
+
const files = "files" in event ? event.files : undefined;
|
|
32
34
|
const channel = event.channel;
|
|
33
35
|
// Only respond if we have an active session for this thread
|
|
34
36
|
const session = getSession(threadTs);
|
|
@@ -36,14 +38,18 @@ export async function handleThreadMessage(args) {
|
|
|
36
38
|
console.log(`[thread] skipped: no session for thread_ts=${threadTs}`);
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
39
|
-
console.log(`[thread] session found! sessionId=${session.sessionId}, text=${text?.slice(0, 50)}`);
|
|
40
|
-
|
|
41
|
+
console.log(`[thread] session found! sessionId=${session.sessionId}, text=${text?.slice(0, 50)}, files=${files?.length ?? 0}`);
|
|
42
|
+
// Download any image attachments
|
|
43
|
+
const images = files?.length ? await downloadSlackImages(client, files) : [];
|
|
44
|
+
const hasText = text && text.trim().length > 0;
|
|
45
|
+
if (!hasText && images.length === 0)
|
|
41
46
|
return;
|
|
42
47
|
// Strip any bot mentions from the text (user might @mention the bot in-thread)
|
|
43
|
-
const cleanText = text.replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
44
|
-
if (!cleanText)
|
|
48
|
+
const cleanText = (text ?? "").replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
49
|
+
if (!cleanText && images.length === 0)
|
|
45
50
|
return;
|
|
46
|
-
const
|
|
51
|
+
const userMessage = cleanText || "이미지를 분석해주세요.";
|
|
52
|
+
const stream = chat(threadTs, userMessage, client, channel, images.length > 0 ? images : undefined);
|
|
47
53
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
48
54
|
}
|
|
49
55
|
//# sourceMappingURL=thread.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SlackImageFile } from "../utils/slack-files.js";
|
|
1
2
|
import type { WebClient } from "@slack/web-api";
|
|
2
3
|
interface Session {
|
|
3
4
|
sessionId: string;
|
|
@@ -22,6 +23,6 @@ export declare function getSession(threadTs: string): Session | undefined;
|
|
|
22
23
|
* Uses `canUseTool` callback to handle permission requests
|
|
23
24
|
* through Slack interactive buttons.
|
|
24
25
|
*/
|
|
25
|
-
export declare function chat(threadTs: string, userMessage: string, slackClient: WebClient, channel: string): AsyncGenerator<ChatEvent, void, unknown>;
|
|
26
|
+
export declare function chat(threadTs: string, userMessage: string, slackClient: WebClient, channel: string, images?: SlackImageFile[]): AsyncGenerator<ChatEvent, void, unknown>;
|
|
26
27
|
export {};
|
|
27
28
|
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAK9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,UAAU,OAAO;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAoJvC,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAaT;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEhE;AAkBD;;;;;GAKG;AACH,wBAAuB,IAAI,CACzB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,SAAS,EACtB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,cAAc,EAAE,GACxB,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CA6J1C"}
|
package/dist/services/claude.js
CHANGED
|
@@ -160,7 +160,7 @@ function describeToolInput(toolName, input) {
|
|
|
160
160
|
* Uses `canUseTool` callback to handle permission requests
|
|
161
161
|
* through Slack interactive buttons.
|
|
162
162
|
*/
|
|
163
|
-
export async function* chat(threadTs, userMessage, slackClient, channel) {
|
|
163
|
+
export async function* chat(threadTs, userMessage, slackClient, channel, images) {
|
|
164
164
|
const session = sessions.get(threadTs);
|
|
165
165
|
if (!session) {
|
|
166
166
|
throw new Error(`No session found for thread ${threadTs}`);
|
|
@@ -207,9 +207,34 @@ export async function* chat(threadTs, userMessage, slackClient, channel) {
|
|
|
207
207
|
session.lastActivityAt = Date.now();
|
|
208
208
|
saveSessions();
|
|
209
209
|
console.log(`[claude] starting query, cwd=${session.workDir}, hasStarted=${session.hasStarted}`);
|
|
210
|
+
// Build prompt: plain string when no images, SDKUserMessage iterable otherwise
|
|
211
|
+
const hasImages = images && images.length > 0;
|
|
212
|
+
const prompt = hasImages
|
|
213
|
+
? (async function* () {
|
|
214
|
+
yield {
|
|
215
|
+
type: "user",
|
|
216
|
+
message: {
|
|
217
|
+
role: "user",
|
|
218
|
+
content: [
|
|
219
|
+
...images.map((img) => ({
|
|
220
|
+
type: "image",
|
|
221
|
+
source: {
|
|
222
|
+
type: "base64",
|
|
223
|
+
media_type: img.mediaType,
|
|
224
|
+
data: img.base64,
|
|
225
|
+
},
|
|
226
|
+
})),
|
|
227
|
+
{ type: "text", text: userMessage },
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
parent_tool_use_id: null,
|
|
231
|
+
session_id: session.sessionId,
|
|
232
|
+
};
|
|
233
|
+
})()
|
|
234
|
+
: userMessage;
|
|
210
235
|
try {
|
|
211
236
|
const q = query({
|
|
212
|
-
prompt
|
|
237
|
+
prompt,
|
|
213
238
|
options: {
|
|
214
239
|
cwd: session.workDir,
|
|
215
240
|
canUseTool,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/services/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/services/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAahD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,CAAC,CAmDlB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
1
2
|
const pending = new Map();
|
|
2
3
|
/**
|
|
3
4
|
* Register a pending permission request and post Slack buttons.
|
|
@@ -9,6 +10,7 @@ export async function requestPermission(client, channel, threadTs, permissionId,
|
|
|
9
10
|
channel,
|
|
10
11
|
thread_ts: threadTs,
|
|
11
12
|
text: `Permission request: ${toolName}`,
|
|
13
|
+
icon_url: CLAUDE_ICON_URL,
|
|
12
14
|
blocks: [
|
|
13
15
|
{
|
|
14
16
|
type: "section",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/services/permissions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/services/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-messenger.d.ts","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-messenger.d.ts","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,GACpD,OAAO,CAAC,MAAM,CAAC,CAqFjB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { markdownToSlackMrkdwn } from "../utils/slack-format.js";
|
|
2
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
2
3
|
const THROTTLE_MS = 500;
|
|
3
4
|
/**
|
|
4
5
|
* Post a streaming reply in a Slack thread, handling both text and tool events.
|
|
@@ -12,6 +13,7 @@ export async function postStreamingReply(client, channel, threadTs, eventStream)
|
|
|
12
13
|
channel,
|
|
13
14
|
thread_ts: threadTs,
|
|
14
15
|
text: ":hourglass_flowing_sand: Thinking...",
|
|
16
|
+
icon_url: CLAUDE_ICON_URL,
|
|
15
17
|
});
|
|
16
18
|
const messageTs = initial.ts;
|
|
17
19
|
if (!messageTs) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-messenger.js","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-messenger.js","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WebClient } from "@slack/web-api";
|
|
2
|
+
export interface SlackImageFile {
|
|
3
|
+
base64: string;
|
|
4
|
+
mediaType: "image/jpeg" | "image/png" | "image/gif" | "image/webp";
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Download image files from a Slack message's `files` array.
|
|
8
|
+
*
|
|
9
|
+
* Only images with supported media types are returned; non-image files are skipped.
|
|
10
|
+
*/
|
|
11
|
+
export declare function downloadSlackImages(client: WebClient, files: Array<{
|
|
12
|
+
url_private_download?: string;
|
|
13
|
+
url_private?: string;
|
|
14
|
+
mimetype?: string;
|
|
15
|
+
}>): Promise<SlackImageFile[]>;
|
|
16
|
+
//# sourceMappingURL=slack-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-files.d.ts","sourceRoot":"","sources":["../../src/utils/slack-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;CACpE;AASD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,KAAK,CAAC;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACvF,OAAO,CAAC,cAAc,EAAE,CAAC,CAkC3B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const SUPPORTED_TYPES = new Set([
|
|
2
|
+
"image/jpeg",
|
|
3
|
+
"image/png",
|
|
4
|
+
"image/gif",
|
|
5
|
+
"image/webp",
|
|
6
|
+
]);
|
|
7
|
+
/**
|
|
8
|
+
* Download image files from a Slack message's `files` array.
|
|
9
|
+
*
|
|
10
|
+
* Only images with supported media types are returned; non-image files are skipped.
|
|
11
|
+
*/
|
|
12
|
+
export async function downloadSlackImages(client, files) {
|
|
13
|
+
const token = client.token;
|
|
14
|
+
if (!token)
|
|
15
|
+
return [];
|
|
16
|
+
const results = [];
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const mime = file.mimetype;
|
|
19
|
+
if (!mime || !SUPPORTED_TYPES.has(mime))
|
|
20
|
+
continue;
|
|
21
|
+
const url = file.url_private_download ?? file.url_private;
|
|
22
|
+
if (!url)
|
|
23
|
+
continue;
|
|
24
|
+
try {
|
|
25
|
+
const resp = await fetch(url, {
|
|
26
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
27
|
+
});
|
|
28
|
+
if (!resp.ok) {
|
|
29
|
+
console.warn(`[slack-files] failed to download ${url}: ${resp.status}`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
33
|
+
results.push({
|
|
34
|
+
base64: buf.toString("base64"),
|
|
35
|
+
mediaType: mime,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
console.warn(`[slack-files] error downloading file:`, err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return results;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=slack-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-files.js","sourceRoot":"","sources":["../../src/utils/slack-files.ts"],"names":[],"mappings":"AAOA,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,YAAY;IACZ,WAAW;IACX,WAAW;IACX,YAAY;CACb,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAiB,EACjB,KAAwF;IAExF,MAAM,KAAK,GAAI,MAAwC,CAAC,KAAK,CAAC;IAC9D,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3B,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAElD,MAAM,GAAG,GAAG,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,WAAW,CAAC;QAC1D,IAAI,CAAC,GAAG;YAAE,SAAS;QAEnB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC5B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;aAC9C,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,GAAG,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBACxE,SAAS;YACX,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC;gBACX,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAC9B,SAAS,EAAE,IAAmC;aAC/C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/package.json
CHANGED
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CLAUDE_ICON_URL = "https://claude.ai/apple-touch-icon.png";
|
package/src/handlers/mention.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { removeMention, extractGithubRepo, type GithubRepo } from "../utils/pars
|
|
|
3
3
|
import { createSession, getSession, chat } from "../services/claude.js";
|
|
4
4
|
import { prepareWorkDir, checkRepoExists, createGithubRepo } from "../services/git.js";
|
|
5
5
|
import { postStreamingReply } from "../services/slack-messenger.js";
|
|
6
|
+
import { downloadSlackImages, type SlackImageFile } from "../utils/slack-files.js";
|
|
7
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
6
8
|
import fs from "node:fs/promises";
|
|
7
9
|
import os from "node:os";
|
|
8
10
|
import path from "node:path";
|
|
@@ -57,10 +59,14 @@ export async function handleMention(
|
|
|
57
59
|
const { event, client } = args;
|
|
58
60
|
const { text, channel, ts, thread_ts } = event;
|
|
59
61
|
const authorUserId = event.user ?? "unknown";
|
|
62
|
+
const files = "files" in event ? (event as unknown as { files?: Array<{ url_private_download?: string; url_private?: string; mimetype?: string }> }).files : undefined;
|
|
63
|
+
|
|
64
|
+
// Download any image attachments
|
|
65
|
+
const images = files?.length ? await downloadSlackImages(client, files) : [];
|
|
60
66
|
|
|
61
67
|
// If the mention is inside an existing thread, treat it as a thread reply
|
|
62
68
|
if (thread_ts) {
|
|
63
|
-
await handleThreadedMention(client, channel, thread_ts, text, authorUserId);
|
|
69
|
+
await handleThreadedMention(client, channel, thread_ts, text, authorUserId, images);
|
|
64
70
|
return;
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -73,6 +79,7 @@ export async function handleMention(
|
|
|
73
79
|
channel,
|
|
74
80
|
thread_ts: threadTs,
|
|
75
81
|
text: "How can I help you? You can ask me coding questions, or mention a GitHub repo (e.g. `owner/repo`) to work with its code.",
|
|
82
|
+
icon_url: CLAUDE_ICON_URL,
|
|
76
83
|
});
|
|
77
84
|
return;
|
|
78
85
|
}
|
|
@@ -99,6 +106,7 @@ export async function handleMention(
|
|
|
99
106
|
channel,
|
|
100
107
|
thread_ts: threadTs,
|
|
101
108
|
text: `:warning: \`${repoInfo.owner}/${repoInfo.repo}\` 리포지토리가 존재하지 않습니다. 생성할까요?`,
|
|
109
|
+
icon_url: CLAUDE_ICON_URL,
|
|
102
110
|
blocks: [
|
|
103
111
|
{
|
|
104
112
|
type: "section",
|
|
@@ -137,10 +145,10 @@ export async function handleMention(
|
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
// Repo exists — proceed with normal setup
|
|
140
|
-
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId);
|
|
148
|
+
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId, images);
|
|
141
149
|
} else {
|
|
142
150
|
// No repo mentioned — use temp directory
|
|
143
|
-
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId);
|
|
151
|
+
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId, images);
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
|
|
@@ -153,7 +161,8 @@ export async function startSessionWithRepo(
|
|
|
153
161
|
threadTs: string,
|
|
154
162
|
repoInfo: GithubRepo,
|
|
155
163
|
cleanText: string,
|
|
156
|
-
authorUserId: string
|
|
164
|
+
authorUserId: string,
|
|
165
|
+
images?: SlackImageFile[]
|
|
157
166
|
): Promise<void> {
|
|
158
167
|
let workDir: string;
|
|
159
168
|
|
|
@@ -162,6 +171,7 @@ export async function startSessionWithRepo(
|
|
|
162
171
|
channel,
|
|
163
172
|
thread_ts: threadTs,
|
|
164
173
|
text: `:file_folder: Setting up repository \`${repoInfo.owner}/${repoInfo.repo}\`...`,
|
|
174
|
+
icon_url: CLAUDE_ICON_URL,
|
|
165
175
|
});
|
|
166
176
|
|
|
167
177
|
workDir = await prepareWorkDir(repoInfo, getReposBaseDir());
|
|
@@ -171,6 +181,7 @@ export async function startSessionWithRepo(
|
|
|
171
181
|
channel,
|
|
172
182
|
thread_ts: threadTs,
|
|
173
183
|
text: `:x: Failed to set up repository: ${message}`,
|
|
184
|
+
icon_url: CLAUDE_ICON_URL,
|
|
174
185
|
});
|
|
175
186
|
return;
|
|
176
187
|
}
|
|
@@ -178,7 +189,7 @@ export async function startSessionWithRepo(
|
|
|
178
189
|
createSession(threadTs, workDir, authorUserId);
|
|
179
190
|
|
|
180
191
|
const userMessage = `[Working directory: ${workDir} — Repository: ${repoInfo.owner}/${repoInfo.repo}]\n\n${cleanText}`;
|
|
181
|
-
const stream = chat(threadTs, userMessage, client, channel);
|
|
192
|
+
const stream = chat(threadTs, userMessage, client, channel, images?.length ? images : undefined);
|
|
182
193
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
183
194
|
}
|
|
184
195
|
|
|
@@ -190,12 +201,13 @@ export async function startSessionWithoutRepo(
|
|
|
190
201
|
channel: string,
|
|
191
202
|
threadTs: string,
|
|
192
203
|
cleanText: string,
|
|
193
|
-
authorUserId: string
|
|
204
|
+
authorUserId: string,
|
|
205
|
+
images?: SlackImageFile[]
|
|
194
206
|
): Promise<void> {
|
|
195
207
|
const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "eniac-"));
|
|
196
208
|
createSession(threadTs, workDir, authorUserId);
|
|
197
209
|
|
|
198
|
-
const stream = chat(threadTs, cleanText, client, channel);
|
|
210
|
+
const stream = chat(threadTs, cleanText, client, channel, images?.length ? images : undefined);
|
|
199
211
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
200
212
|
}
|
|
201
213
|
|
|
@@ -207,10 +219,11 @@ async function handleThreadedMention(
|
|
|
207
219
|
channel: string,
|
|
208
220
|
threadTs: string,
|
|
209
221
|
text: string,
|
|
210
|
-
authorUserId: string
|
|
222
|
+
authorUserId: string,
|
|
223
|
+
images?: SlackImageFile[]
|
|
211
224
|
): Promise<void> {
|
|
212
225
|
const cleanText = removeMention(text);
|
|
213
|
-
if (!cleanText) return;
|
|
226
|
+
if (!cleanText && (!images || images.length === 0)) return;
|
|
214
227
|
|
|
215
228
|
let session = getSession(threadTs);
|
|
216
229
|
|
|
@@ -219,6 +232,7 @@ async function handleThreadedMention(
|
|
|
219
232
|
session = createSession(threadTs, workDir, authorUserId);
|
|
220
233
|
}
|
|
221
234
|
|
|
222
|
-
const
|
|
235
|
+
const userMessage = cleanText || "이미지를 분석해주세요.";
|
|
236
|
+
const stream = chat(threadTs, userMessage, client, channel, images?.length ? images : undefined);
|
|
223
237
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
224
238
|
}
|
package/src/handlers/thread.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AllMiddlewareArgs, SlackEventMiddlewareArgs } from "@slack/bolt";
|
|
2
2
|
import { getSession, chat } from "../services/claude.js";
|
|
3
3
|
import { postStreamingReply } from "../services/slack-messenger.js";
|
|
4
|
+
import { downloadSlackImages } from "../utils/slack-files.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Handle messages posted in threads where the bot has an active session.
|
|
@@ -38,6 +39,7 @@ export async function handleThreadMessage(
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const text = "text" in event ? event.text : undefined;
|
|
42
|
+
const files = "files" in event ? (event as unknown as { files?: Array<{ url_private_download?: string; url_private?: string; mimetype?: string }> }).files : undefined;
|
|
41
43
|
const channel = event.channel;
|
|
42
44
|
|
|
43
45
|
// Only respond if we have an active session for this thread
|
|
@@ -47,14 +49,19 @@ export async function handleThreadMessage(
|
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
console.log(`[thread] session found! sessionId=${session.sessionId}, text=${text?.slice(0, 50)}`);
|
|
52
|
+
console.log(`[thread] session found! sessionId=${session.sessionId}, text=${text?.slice(0, 50)}, files=${files?.length ?? 0}`);
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
// Download any image attachments
|
|
55
|
+
const images = files?.length ? await downloadSlackImages(client, files) : [];
|
|
56
|
+
|
|
57
|
+
const hasText = text && text.trim().length > 0;
|
|
58
|
+
if (!hasText && images.length === 0) return;
|
|
53
59
|
|
|
54
60
|
// Strip any bot mentions from the text (user might @mention the bot in-thread)
|
|
55
|
-
const cleanText = text.replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
56
|
-
if (!cleanText) return;
|
|
61
|
+
const cleanText = (text ?? "").replace(/<@[A-Z0-9]+>/g, "").trim();
|
|
62
|
+
if (!cleanText && images.length === 0) return;
|
|
57
63
|
|
|
58
|
-
const
|
|
64
|
+
const userMessage = cleanText || "이미지를 분석해주세요.";
|
|
65
|
+
const stream = chat(threadTs, userMessage, client, channel, images.length > 0 ? images : undefined);
|
|
59
66
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
60
67
|
}
|
package/src/services/claude.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import type {
|
|
3
3
|
SDKMessage,
|
|
4
|
+
SDKUserMessage,
|
|
4
5
|
CanUseTool,
|
|
5
6
|
PermissionResult,
|
|
6
7
|
McpServerConfig,
|
|
7
8
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
9
|
+
import type { SlackImageFile } from "../utils/slack-files.js";
|
|
8
10
|
import { randomUUID } from "node:crypto";
|
|
9
11
|
import fs from "node:fs";
|
|
10
12
|
import path from "node:path";
|
|
@@ -220,7 +222,8 @@ export async function* chat(
|
|
|
220
222
|
threadTs: string,
|
|
221
223
|
userMessage: string,
|
|
222
224
|
slackClient: WebClient,
|
|
223
|
-
channel: string
|
|
225
|
+
channel: string,
|
|
226
|
+
images?: SlackImageFile[]
|
|
224
227
|
): AsyncGenerator<ChatEvent, void, unknown> {
|
|
225
228
|
const session = sessions.get(threadTs);
|
|
226
229
|
if (!session) {
|
|
@@ -292,9 +295,35 @@ export async function* chat(
|
|
|
292
295
|
|
|
293
296
|
console.log(`[claude] starting query, cwd=${session.workDir}, hasStarted=${session.hasStarted}`);
|
|
294
297
|
|
|
298
|
+
// Build prompt: plain string when no images, SDKUserMessage iterable otherwise
|
|
299
|
+
const hasImages = images && images.length > 0;
|
|
300
|
+
const prompt: string | AsyncIterable<SDKUserMessage> = hasImages
|
|
301
|
+
? (async function* () {
|
|
302
|
+
yield {
|
|
303
|
+
type: "user",
|
|
304
|
+
message: {
|
|
305
|
+
role: "user",
|
|
306
|
+
content: [
|
|
307
|
+
...images.map((img) => ({
|
|
308
|
+
type: "image" as const,
|
|
309
|
+
source: {
|
|
310
|
+
type: "base64" as const,
|
|
311
|
+
media_type: img.mediaType,
|
|
312
|
+
data: img.base64,
|
|
313
|
+
},
|
|
314
|
+
})),
|
|
315
|
+
{ type: "text" as const, text: userMessage },
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
parent_tool_use_id: null,
|
|
319
|
+
session_id: session.sessionId,
|
|
320
|
+
} satisfies SDKUserMessage;
|
|
321
|
+
})()
|
|
322
|
+
: userMessage;
|
|
323
|
+
|
|
295
324
|
try {
|
|
296
325
|
const q = query({
|
|
297
|
-
prompt
|
|
326
|
+
prompt,
|
|
298
327
|
options: {
|
|
299
328
|
cwd: session.workDir,
|
|
300
329
|
canUseTool,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WebClient } from "@slack/web-api";
|
|
2
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
2
3
|
|
|
3
4
|
interface PendingPermission {
|
|
4
5
|
resolve: (granted: boolean) => void;
|
|
@@ -29,6 +30,7 @@ export async function requestPermission(
|
|
|
29
30
|
channel,
|
|
30
31
|
thread_ts: threadTs,
|
|
31
32
|
text: `Permission request: ${toolName}`,
|
|
33
|
+
icon_url: CLAUDE_ICON_URL,
|
|
32
34
|
blocks: [
|
|
33
35
|
{
|
|
34
36
|
type: "section",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WebClient } from "@slack/web-api";
|
|
2
2
|
import type { ChatEvent } from "./claude.js";
|
|
3
3
|
import { markdownToSlackMrkdwn } from "../utils/slack-format.js";
|
|
4
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
4
5
|
|
|
5
6
|
const THROTTLE_MS = 500;
|
|
6
7
|
|
|
@@ -21,6 +22,7 @@ export async function postStreamingReply(
|
|
|
21
22
|
channel,
|
|
22
23
|
thread_ts: threadTs,
|
|
23
24
|
text: ":hourglass_flowing_sand: Thinking...",
|
|
25
|
+
icon_url: CLAUDE_ICON_URL,
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
const messageTs = initial.ts;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { WebClient } from "@slack/web-api";
|
|
2
|
+
|
|
3
|
+
export interface SlackImageFile {
|
|
4
|
+
base64: string;
|
|
5
|
+
mediaType: "image/jpeg" | "image/png" | "image/gif" | "image/webp";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const SUPPORTED_TYPES = new Set([
|
|
9
|
+
"image/jpeg",
|
|
10
|
+
"image/png",
|
|
11
|
+
"image/gif",
|
|
12
|
+
"image/webp",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Download image files from a Slack message's `files` array.
|
|
17
|
+
*
|
|
18
|
+
* Only images with supported media types are returned; non-image files are skipped.
|
|
19
|
+
*/
|
|
20
|
+
export async function downloadSlackImages(
|
|
21
|
+
client: WebClient,
|
|
22
|
+
files: Array<{ url_private_download?: string; url_private?: string; mimetype?: string }>
|
|
23
|
+
): Promise<SlackImageFile[]> {
|
|
24
|
+
const token = (client as unknown as { token?: string }).token;
|
|
25
|
+
if (!token) return [];
|
|
26
|
+
|
|
27
|
+
const results: SlackImageFile[] = [];
|
|
28
|
+
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
const mime = file.mimetype;
|
|
31
|
+
if (!mime || !SUPPORTED_TYPES.has(mime)) continue;
|
|
32
|
+
|
|
33
|
+
const url = file.url_private_download ?? file.url_private;
|
|
34
|
+
if (!url) continue;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const resp = await fetch(url, {
|
|
38
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!resp.ok) {
|
|
42
|
+
console.warn(`[slack-files] failed to download ${url}: ${resp.status}`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
47
|
+
results.push({
|
|
48
|
+
base64: buf.toString("base64"),
|
|
49
|
+
mediaType: mime as SlackImageFile["mediaType"],
|
|
50
|
+
});
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.warn(`[slack-files] error downloading file:`, err);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return results;
|
|
57
|
+
}
|