@slock-ai/daemon 0.33.0 → 0.35.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/dist/chat-bridge.js +87 -35
- package/dist/chunk-OSHFDFBV.js +3387 -0
- package/dist/core.js +25 -0
- package/dist/index.js +17 -2334
- package/package.json +7 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -35,6 +35,68 @@ function bridgeFetch(url, init = {}) {
|
|
|
35
35
|
const requestInit = dispatcher ? { ...init, dispatcher } : init;
|
|
36
36
|
return fetch(url, requestInit);
|
|
37
37
|
}
|
|
38
|
+
function formatAttachmentSuffix(attachments) {
|
|
39
|
+
if (!attachments?.length) return "";
|
|
40
|
+
return ` [${attachments.length} attachment${attachments.length > 1 ? "s" : ""}: ${attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to download]`;
|
|
41
|
+
}
|
|
42
|
+
function guessMimeTypeFromFilename(filename) {
|
|
43
|
+
const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
|
|
44
|
+
const mimeMap = {
|
|
45
|
+
".jpg": "image/jpeg",
|
|
46
|
+
".jpeg": "image/jpeg",
|
|
47
|
+
".png": "image/png",
|
|
48
|
+
".gif": "image/gif",
|
|
49
|
+
".webp": "image/webp",
|
|
50
|
+
".pdf": "application/pdf",
|
|
51
|
+
".txt": "text/plain",
|
|
52
|
+
".md": "text/markdown",
|
|
53
|
+
".json": "application/json",
|
|
54
|
+
".csv": "text/csv",
|
|
55
|
+
".zip": "application/zip",
|
|
56
|
+
".tar": "application/x-tar",
|
|
57
|
+
".gz": "application/gzip",
|
|
58
|
+
".mp3": "audio/mpeg",
|
|
59
|
+
".wav": "audio/wav",
|
|
60
|
+
".mp4": "video/mp4",
|
|
61
|
+
".mov": "video/quicktime",
|
|
62
|
+
".doc": "application/msword",
|
|
63
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
64
|
+
".xls": "application/vnd.ms-excel",
|
|
65
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
66
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
67
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
68
|
+
};
|
|
69
|
+
return mimeMap[ext] || "application/octet-stream";
|
|
70
|
+
}
|
|
71
|
+
function parseFilenameFromContentDisposition(contentDisposition) {
|
|
72
|
+
if (!contentDisposition) return null;
|
|
73
|
+
const utf8Match = contentDisposition.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
|
|
74
|
+
if (utf8Match?.[1]) return decodeURIComponent(utf8Match[1]);
|
|
75
|
+
const plainMatch = contentDisposition.match(/filename\s*=\s*"([^"]+)"/i) || contentDisposition.match(/filename\s*=\s*([^;]+)/i);
|
|
76
|
+
return plainMatch?.[1] ? plainMatch[1].trim() : null;
|
|
77
|
+
}
|
|
78
|
+
function extensionForContentType(contentType) {
|
|
79
|
+
const normalized = contentType.split(";")[0]?.trim().toLowerCase();
|
|
80
|
+
const extMap = {
|
|
81
|
+
"image/jpeg": ".jpg",
|
|
82
|
+
"image/png": ".png",
|
|
83
|
+
"image/gif": ".gif",
|
|
84
|
+
"image/webp": ".webp",
|
|
85
|
+
"application/pdf": ".pdf",
|
|
86
|
+
"text/plain": ".txt",
|
|
87
|
+
"text/markdown": ".md",
|
|
88
|
+
"application/json": ".json",
|
|
89
|
+
"text/csv": ".csv",
|
|
90
|
+
"application/zip": ".zip",
|
|
91
|
+
"application/x-tar": ".tar",
|
|
92
|
+
"application/gzip": ".gz",
|
|
93
|
+
"audio/mpeg": ".mp3",
|
|
94
|
+
"audio/wav": ".wav",
|
|
95
|
+
"video/mp4": ".mp4",
|
|
96
|
+
"video/quicktime": ".mov"
|
|
97
|
+
};
|
|
98
|
+
return extMap[normalized] || ".bin";
|
|
99
|
+
}
|
|
38
100
|
function formatTarget(m) {
|
|
39
101
|
if (m.channel_type === "thread" && m.parent_channel_name) {
|
|
40
102
|
const shortId = m.channel_name.startsWith("thread-") ? m.channel_name.slice(7) : m.channel_name;
|
|
@@ -61,6 +123,11 @@ function formatSearchTarget(result) {
|
|
|
61
123
|
}
|
|
62
124
|
return `#${result.channelName}`;
|
|
63
125
|
}
|
|
126
|
+
function formatSenderHandle(message) {
|
|
127
|
+
const senderName = message.sender_name ?? message.senderName ?? "unknown";
|
|
128
|
+
const senderDescription = message.sender_description ?? message.senderDescription ?? null;
|
|
129
|
+
return senderDescription ? `@${senderName} \u2014 ${senderDescription}` : `@${senderName}`;
|
|
130
|
+
}
|
|
64
131
|
var server = new McpServer({
|
|
65
132
|
name: "chat",
|
|
66
133
|
version: "1.0.0"
|
|
@@ -117,9 +184,9 @@ ${formatMessages(data.recentUnread)}`;
|
|
|
117
184
|
);
|
|
118
185
|
server.tool(
|
|
119
186
|
"upload_file",
|
|
120
|
-
"Upload
|
|
187
|
+
"Upload a file to attach to a message. Returns an attachment ID that you can pass to send_message's attachment_ids parameter. Images keep preview behavior; other files are sent as downloadable attachments. Max size: 5MB.",
|
|
121
188
|
{
|
|
122
|
-
file_path: z.string().describe("Absolute path to the
|
|
189
|
+
file_path: z.string().describe("Absolute path to the file on your local filesystem"),
|
|
123
190
|
channel: z.string().describe("The channel target where this file will be used (e.g. '#general', 'dm:@richard')")
|
|
124
191
|
},
|
|
125
192
|
async ({ file_path, channel }) => {
|
|
@@ -136,7 +203,7 @@ server.tool(
|
|
|
136
203
|
if (stat.size > 5 * 1024 * 1024) {
|
|
137
204
|
return {
|
|
138
205
|
isError: true,
|
|
139
|
-
content: [{ type: "text", text: `Error: File too large (${(stat.size / 1024 / 1024).toFixed(1)}MB). Max 5MB.` }]
|
|
206
|
+
content: [{ type: "text", text: `Error: File too large (${(stat.size / 1024 / 1024).toFixed(1)}MB). Max 5MB per file.` }]
|
|
140
207
|
};
|
|
141
208
|
}
|
|
142
209
|
const listRes = await bridgeFetch(`${serverUrl}/internal/agent/${agentId}/resolve-channel`, {
|
|
@@ -156,15 +223,7 @@ server.tool(
|
|
|
156
223
|
}
|
|
157
224
|
const fileBuffer = fs.readFileSync(file_path);
|
|
158
225
|
const filename = path.basename(file_path);
|
|
159
|
-
const
|
|
160
|
-
const mimeMap = {
|
|
161
|
-
".jpg": "image/jpeg",
|
|
162
|
-
".jpeg": "image/jpeg",
|
|
163
|
-
".png": "image/png",
|
|
164
|
-
".gif": "image/gif",
|
|
165
|
-
".webp": "image/webp"
|
|
166
|
-
};
|
|
167
|
-
const mimeType = mimeMap[ext] || "application/octet-stream";
|
|
226
|
+
const mimeType = guessMimeTypeFromFilename(filename);
|
|
168
227
|
const blob = new Blob([fileBuffer], { type: mimeType });
|
|
169
228
|
const formData = new FormData();
|
|
170
229
|
formData.append("file", blob, filename);
|
|
@@ -206,7 +265,7 @@ Use this ID in send_message's attachment_ids parameter to include it in a messag
|
|
|
206
265
|
);
|
|
207
266
|
server.tool(
|
|
208
267
|
"view_file",
|
|
209
|
-
"Download an attached
|
|
268
|
+
"Download an attached file by its attachment ID and save it locally so you can inspect it. Returns the local file path.",
|
|
210
269
|
{
|
|
211
270
|
attachment_id: z.string().describe("The attachment UUID (from the 'id:...' shown in the message)")
|
|
212
271
|
},
|
|
@@ -221,9 +280,7 @@ server.tool(
|
|
|
221
280
|
if (existing) {
|
|
222
281
|
const cachedPath = path.join(cacheDir, existing);
|
|
223
282
|
return {
|
|
224
|
-
content: [{ type: "text", text: `File already cached at: ${cachedPath}
|
|
225
|
-
|
|
226
|
-
Use your Read tool to view this image.` }]
|
|
283
|
+
content: [{ type: "text", text: `File already cached at: ${cachedPath}` }]
|
|
227
284
|
};
|
|
228
285
|
}
|
|
229
286
|
const downloadHeaders = {};
|
|
@@ -241,20 +298,13 @@ Use your Read tool to view this image.` }]
|
|
|
241
298
|
};
|
|
242
299
|
}
|
|
243
300
|
const contentType = res.headers.get("content-type") || "application/octet-stream";
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
"image/png": ".png",
|
|
247
|
-
"image/gif": ".gif",
|
|
248
|
-
"image/webp": ".webp"
|
|
249
|
-
};
|
|
250
|
-
const ext = extMap[contentType] || ".bin";
|
|
301
|
+
const filename = parseFilenameFromContentDisposition(res.headers.get("content-disposition"));
|
|
302
|
+
const ext = filename ? path.extname(filename) || extensionForContentType(contentType) : extensionForContentType(contentType);
|
|
251
303
|
const filePath = path.join(cacheDir, `${attachment_id}${ext}`);
|
|
252
304
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
253
305
|
fs.writeFileSync(filePath, buffer);
|
|
254
306
|
return {
|
|
255
|
-
content: [{ type: "text", text: `Downloaded to: ${filePath}
|
|
256
|
-
|
|
257
|
-
Use your Read tool to view this image.` }]
|
|
307
|
+
content: [{ type: "text", text: `Downloaded to: ${filePath}` }]
|
|
258
308
|
};
|
|
259
309
|
} catch (err) {
|
|
260
310
|
return {
|
|
@@ -298,11 +348,11 @@ function formatMessages(messages) {
|
|
|
298
348
|
const target = formatTarget(m);
|
|
299
349
|
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
300
350
|
const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
|
|
301
|
-
const senderType = m.sender_type
|
|
351
|
+
const senderType = ` type=${m.sender_type}`;
|
|
302
352
|
const renderedContent = m.content;
|
|
303
|
-
const attachSuffix = m.attachments
|
|
353
|
+
const attachSuffix = formatAttachmentSuffix(m.attachments);
|
|
304
354
|
const taskSuffix = m.task_status ? ` [task #${m.task_number} status=${m.task_status}${m.task_assignee_id ? ` assignee=${m.task_assignee_type}:${m.task_assignee_id}` : ""}]` : "";
|
|
305
|
-
return `[target=${target} msg=${msgId} time=${time}${senderType}]
|
|
355
|
+
return `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(m)}: ${renderedContent}${attachSuffix}${taskSuffix}`;
|
|
306
356
|
}).join("\n");
|
|
307
357
|
}
|
|
308
358
|
server.tool(
|
|
@@ -333,7 +383,8 @@ server.tool(
|
|
|
333
383
|
text += "Other AI agents in this server.\n";
|
|
334
384
|
if (data.agents?.length > 0) {
|
|
335
385
|
for (const a of data.agents) {
|
|
336
|
-
text += ` - @${a.name} (${a.status})
|
|
386
|
+
text += a.description ? ` - @${a.name} (${a.status}) \u2014 ${a.description}
|
|
387
|
+
` : ` - @${a.name} (${a.status})
|
|
337
388
|
`;
|
|
338
389
|
}
|
|
339
390
|
} else {
|
|
@@ -343,7 +394,8 @@ server.tool(
|
|
|
343
394
|
text += 'To start a new DM: send_message(target="dm:@name"). To reply in an existing DM: reuse the target from received messages.\n';
|
|
344
395
|
if (data.humans?.length > 0) {
|
|
345
396
|
for (const u of data.humans) {
|
|
346
|
-
text += ` - @${u.name}
|
|
397
|
+
text += u.description ? ` - @${u.name} \u2014 ${u.description}
|
|
398
|
+
` : ` - @${u.name}
|
|
347
399
|
`;
|
|
348
400
|
}
|
|
349
401
|
} else {
|
|
@@ -408,7 +460,7 @@ thread: ${result.parentChannelName} -> ${target}` : "";
|
|
|
408
460
|
return [
|
|
409
461
|
`[${index + 1}] msg=${result.id} seq=${result.seq} time=${toLocalTime(result.createdAt)}`,
|
|
410
462
|
`target: ${target}${threadInfo}`,
|
|
411
|
-
`sender: @${result.senderName}${result.senderType
|
|
463
|
+
`sender: @${result.senderName} (${result.senderType})`,
|
|
412
464
|
`content: ${result.content}`,
|
|
413
465
|
`match: ${result.snippet}`,
|
|
414
466
|
`next: read_history(channel="${target}", around="${result.id}", limit=20)`
|
|
@@ -468,13 +520,13 @@ server.tool(
|
|
|
468
520
|
};
|
|
469
521
|
}
|
|
470
522
|
const formatted = data.messages.map((m) => {
|
|
471
|
-
const senderType = m.senderType
|
|
523
|
+
const senderType = ` type=${m.senderType}`;
|
|
472
524
|
const time = m.createdAt ? toLocalTime(m.createdAt) : "-";
|
|
473
525
|
const msgId = m.id || "-";
|
|
474
526
|
const renderedContent = m.content;
|
|
475
|
-
const attachSuffix = m.attachments
|
|
527
|
+
const attachSuffix = formatAttachmentSuffix(m.attachments);
|
|
476
528
|
const taskSuffix = m.taskStatus ? ` [task #${m.taskNumber} status=${m.taskStatus}${m.taskAssigneeId ? ` assignee=${m.taskAssigneeType}:${m.taskAssigneeId}` : ""}]` : "";
|
|
477
|
-
return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}]
|
|
529
|
+
return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(m)}: ${renderedContent}${attachSuffix}${taskSuffix}`;
|
|
478
530
|
}).join("\n");
|
|
479
531
|
let footer = "";
|
|
480
532
|
if (data.historyLimited) {
|