instar 0.24.13 → 0.24.14
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/.claude/skills/setup-wizard/skill.md +281 -5
- package/dashboard/index.html +341 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +188 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/slack-cli.d.ts +16 -0
- package/dist/commands/slack-cli.d.ts.map +1 -0
- package/dist/commands/slack-cli.js +198 -0
- package/dist/commands/slack-cli.js.map +1 -0
- package/dist/core/AgentRegistry.d.ts.map +1 -1
- package/dist/core/AgentRegistry.js +24 -6
- package/dist/core/AgentRegistry.js.map +1 -1
- package/dist/core/SleepWakeDetector.d.ts +11 -0
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +16 -1
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +13 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +129 -0
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/messaging/SessionSummarySentinel.js +1 -1
- package/dist/messaging/TelegramAdapter.d.ts +1 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +4 -1
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/slack/ChannelManager.d.ts +36 -0
- package/dist/messaging/slack/ChannelManager.d.ts.map +1 -0
- package/dist/messaging/slack/ChannelManager.js +100 -0
- package/dist/messaging/slack/ChannelManager.js.map +1 -0
- package/dist/messaging/slack/FileHandler.d.ts +30 -0
- package/dist/messaging/slack/FileHandler.d.ts.map +1 -0
- package/dist/messaging/slack/FileHandler.js +87 -0
- package/dist/messaging/slack/FileHandler.js.map +1 -0
- package/dist/messaging/slack/RingBuffer.d.ts +22 -0
- package/dist/messaging/slack/RingBuffer.d.ts.map +1 -0
- package/dist/messaging/slack/RingBuffer.js +48 -0
- package/dist/messaging/slack/RingBuffer.js.map +1 -0
- package/dist/messaging/slack/SlackAdapter.d.ts +136 -0
- package/dist/messaging/slack/SlackAdapter.d.ts.map +1 -0
- package/dist/messaging/slack/SlackAdapter.js +572 -0
- package/dist/messaging/slack/SlackAdapter.js.map +1 -0
- package/dist/messaging/slack/SlackApiClient.d.ts +51 -0
- package/dist/messaging/slack/SlackApiClient.d.ts.map +1 -0
- package/dist/messaging/slack/SlackApiClient.js +94 -0
- package/dist/messaging/slack/SlackApiClient.js.map +1 -0
- package/dist/messaging/slack/SocketModeClient.d.ts +44 -0
- package/dist/messaging/slack/SocketModeClient.d.ts.map +1 -0
- package/dist/messaging/slack/SocketModeClient.js +209 -0
- package/dist/messaging/slack/SocketModeClient.js.map +1 -0
- package/dist/messaging/slack/index.d.ts +12 -0
- package/dist/messaging/slack/index.d.ts.map +1 -0
- package/dist/messaging/slack/index.js +15 -0
- package/dist/messaging/slack/index.js.map +1 -0
- package/dist/messaging/slack/sanitize.d.ts +39 -0
- package/dist/messaging/slack/sanitize.d.ts.map +1 -0
- package/dist/messaging/slack/sanitize.js +71 -0
- package/dist/messaging/slack/sanitize.js.map +1 -0
- package/dist/messaging/slack/types.d.ts +155 -0
- package/dist/messaging/slack/types.d.ts.map +1 -0
- package/dist/messaging/slack/types.js +54 -0
- package/dist/messaging/slack/types.js.map +1 -0
- package/dist/monitoring/PresenceProxy.d.ts +157 -0
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -0
- package/dist/monitoring/PresenceProxy.js +891 -0
- package/dist/monitoring/PresenceProxy.js.map +1 -0
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +2 -0
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +49 -47
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +213 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +94 -78
- package/src/templates/hooks/slack-channel-context.sh +98 -0
- package/src/templates/scripts/slack-reply.sh +64 -0
- package/upgrades/0.24.11.md +23 -0
- package/upgrades/0.24.14.md +26 -0
- package/upgrades/0.24.6.md +20 -0
- package/upgrades/0.24.7.md +24 -0
- package/upgrades/0.24.8.md +19 -0
- package/upgrades/0.24.9.md +19 -0
- package/upgrades/NEXT.md +35 -0
- /package/.claude/skills/secret-setup/{skill.md → SKILL.md} +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelManager — Slack channel CRUD operations.
|
|
3
|
+
*
|
|
4
|
+
* Handles channel creation (with naming conventions), archiving,
|
|
5
|
+
* listing, and history retrieval. Uses conversations.* API methods.
|
|
6
|
+
*/
|
|
7
|
+
import { validateChannelName } from './sanitize.js';
|
|
8
|
+
export class ChannelManager {
|
|
9
|
+
api;
|
|
10
|
+
agentName;
|
|
11
|
+
constructor(api, agentName) {
|
|
12
|
+
this.api = api;
|
|
13
|
+
this.agentName = agentName;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a channel. Returns the channel ID.
|
|
17
|
+
* Validates name format. Checks for existing channel first (idempotent).
|
|
18
|
+
*/
|
|
19
|
+
async createChannel(name, isPrivate = false) {
|
|
20
|
+
if (!validateChannelName(name)) {
|
|
21
|
+
throw new Error(`Invalid channel name: "${name}". Must be lowercase alphanumeric with hyphens/underscores, max 80 chars.`);
|
|
22
|
+
}
|
|
23
|
+
// Check if channel already exists
|
|
24
|
+
const existing = await this.findChannelByName(name);
|
|
25
|
+
if (existing) {
|
|
26
|
+
// Unarchive if it was archived
|
|
27
|
+
if (existing.is_archived) {
|
|
28
|
+
await this.unarchiveChannel(existing.id);
|
|
29
|
+
}
|
|
30
|
+
return existing.id;
|
|
31
|
+
}
|
|
32
|
+
const result = await this.api.call('conversations.create', {
|
|
33
|
+
name,
|
|
34
|
+
is_private: isPrivate,
|
|
35
|
+
});
|
|
36
|
+
return result.channel.id;
|
|
37
|
+
}
|
|
38
|
+
/** Create a system channel with the agent prefix. */
|
|
39
|
+
async createSystemChannel(category, descriptor) {
|
|
40
|
+
const name = `${this.agentName}-${category}-${descriptor}`;
|
|
41
|
+
return this.createChannel(name);
|
|
42
|
+
}
|
|
43
|
+
/** Archive a channel (reversible). */
|
|
44
|
+
async archiveChannel(channelId) {
|
|
45
|
+
try {
|
|
46
|
+
await this.api.call('conversations.archive', { channel: channelId });
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// Ignore "already_archived" error
|
|
50
|
+
if (err.message?.includes('already_archived'))
|
|
51
|
+
return;
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Unarchive a channel. */
|
|
56
|
+
async unarchiveChannel(channelId) {
|
|
57
|
+
try {
|
|
58
|
+
await this.api.call('conversations.unarchive', { channel: channelId });
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
// Ignore "not_archived" error
|
|
62
|
+
if (err.message?.includes('not_archived'))
|
|
63
|
+
return;
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** List all channels the bot is in. */
|
|
68
|
+
async listChannels() {
|
|
69
|
+
const result = await this.api.call('conversations.list', {
|
|
70
|
+
types: 'public_channel,private_channel',
|
|
71
|
+
exclude_archived: false,
|
|
72
|
+
limit: 200,
|
|
73
|
+
});
|
|
74
|
+
return result.channels ?? [];
|
|
75
|
+
}
|
|
76
|
+
/** Get channel info. */
|
|
77
|
+
async getChannelInfo(channelId) {
|
|
78
|
+
const result = await this.api.call('conversations.info', { channel: channelId });
|
|
79
|
+
return result.channel;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get channel message history (for cold start / cache miss only).
|
|
83
|
+
* Use ring buffer for hot-path reads.
|
|
84
|
+
*/
|
|
85
|
+
async getChannelHistory(channelId, limit = 50) {
|
|
86
|
+
const result = await this.api.call('conversations.history', {
|
|
87
|
+
channel: channelId,
|
|
88
|
+
limit: Math.min(limit, 200),
|
|
89
|
+
});
|
|
90
|
+
const messages = result.messages ?? [];
|
|
91
|
+
// Slack returns newest-first; reverse to oldest-first
|
|
92
|
+
return messages.reverse();
|
|
93
|
+
}
|
|
94
|
+
/** Find a channel by name (returns first match or null). */
|
|
95
|
+
async findChannelByName(name) {
|
|
96
|
+
const channels = await this.listChannels();
|
|
97
|
+
return channels.find(c => c.name === name) ?? null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=ChannelManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChannelManager.js","sourceRoot":"","sources":["../../../src/messaging/slack/ChannelManager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,OAAO,cAAc;IACjB,GAAG,CAAiB;IACpB,SAAS,CAAS;IAE1B,YAAY,GAAmB,EAAE,SAAiB;QAChD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,SAAS,GAAG,KAAK;QACjD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,2EAA2E,CAAC,CAAC;QAC7H,CAAC;QAED,kCAAkC;QAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,QAAQ,EAAE,CAAC;YACb,+BAA+B;YAC/B,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACzB,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC3C,CAAC;YACD,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE;YACzD,IAAI;YACJ,UAAU,EAAE,SAAS;SACtB,CAAC,CAAC;QAEH,OAAQ,MAAM,CAAC,OAAwB,CAAC,EAAE,CAAC;IAC7C,CAAC;IAED,qDAAqD;IACrD,KAAK,CAAC,mBAAmB,CAAC,QAAgB,EAAE,UAAkB;QAC5D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,QAAQ,IAAI,UAAU,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,sCAAsC;IACtC,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,kCAAkC;YAClC,IAAK,GAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,kBAAkB,CAAC;gBAAE,OAAO;YACjE,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QACtC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,8BAA8B;YAC9B,IAAK,GAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC;gBAAE,OAAO;YAC7D,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,KAAK,CAAC,YAAY;QAChB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE;YACvD,KAAK,EAAE,gCAAgC;YACvC,gBAAgB,EAAE,KAAK;YACvB,KAAK,EAAE,GAAG;SACX,CAAC,CAAC;QACH,OAAQ,MAAM,CAAC,QAA2B,IAAI,EAAE,CAAC;IACnD,CAAC;IAED,wBAAwB;IACxB,KAAK,CAAC,cAAc,CAAC,SAAiB;QACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACjF,OAAO,MAAM,CAAC,OAAuB,CAAC;IACxC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB,CAAC,SAAiB,EAAE,KAAK,GAAG,EAAE;QACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;YAC1D,OAAO,EAAE,SAAS;YAClB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;SAC5B,CAAC,CAAC;QACH,MAAM,QAAQ,GAAI,MAAM,CAAC,QAA2B,IAAI,EAAE,CAAC;QAC3D,sDAAsD;QACtD,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED,4DAA4D;IACpD,KAAK,CAAC,iBAAiB,CAAC,IAAY;QAC1C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;IACrD,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileHandler — Slack file upload (v2 API) and download with security guards.
|
|
3
|
+
*
|
|
4
|
+
* Upload uses the three-step flow required for apps created after May 2024:
|
|
5
|
+
* 1. files.getUploadURLExternal → get upload URL
|
|
6
|
+
* 2. PUT file content to upload URL (validate hostname first)
|
|
7
|
+
* 3. files.completeUploadExternal → share to channel
|
|
8
|
+
*
|
|
9
|
+
* Download validates paths to prevent traversal attacks.
|
|
10
|
+
*/
|
|
11
|
+
import type { SlackApiClient } from './SlackApiClient.js';
|
|
12
|
+
export declare class FileHandler {
|
|
13
|
+
private api;
|
|
14
|
+
private botToken;
|
|
15
|
+
private filesDir;
|
|
16
|
+
constructor(api: SlackApiClient, botToken: string, stateDir: string);
|
|
17
|
+
/**
|
|
18
|
+
* Upload a file to a Slack channel using the v2 three-step flow.
|
|
19
|
+
* files.upload is deprecated and unavailable for new apps.
|
|
20
|
+
*/
|
|
21
|
+
uploadFile(channelId: string, filePath: string, title?: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Download a file from Slack.
|
|
24
|
+
* Validates destPath to prevent path traversal — must resolve inside filesDir.
|
|
25
|
+
*/
|
|
26
|
+
downloadFile(url: string, destPath: string): Promise<string>;
|
|
27
|
+
/** Get the base directory for downloaded files. */
|
|
28
|
+
get downloadDir(): string;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=FileHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileHandler.d.ts","sourceRoot":"","sources":["../../../src/messaging/slack/FileHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAOnE;;;OAGG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCpF;;;OAGG;IACG,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BlE,mDAAmD;IACnD,IAAI,WAAW,IAAI,MAAM,CAExB;CACF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileHandler — Slack file upload (v2 API) and download with security guards.
|
|
3
|
+
*
|
|
4
|
+
* Upload uses the three-step flow required for apps created after May 2024:
|
|
5
|
+
* 1. files.getUploadURLExternal → get upload URL
|
|
6
|
+
* 2. PUT file content to upload URL (validate hostname first)
|
|
7
|
+
* 3. files.completeUploadExternal → share to channel
|
|
8
|
+
*
|
|
9
|
+
* Download validates paths to prevent traversal attacks.
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { validateSlackHostname } from './sanitize.js';
|
|
14
|
+
export class FileHandler {
|
|
15
|
+
api;
|
|
16
|
+
botToken;
|
|
17
|
+
filesDir;
|
|
18
|
+
constructor(api, botToken, stateDir) {
|
|
19
|
+
this.api = api;
|
|
20
|
+
this.botToken = botToken;
|
|
21
|
+
this.filesDir = path.join(stateDir, 'slack-files');
|
|
22
|
+
fs.mkdirSync(this.filesDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Upload a file to a Slack channel using the v2 three-step flow.
|
|
26
|
+
* files.upload is deprecated and unavailable for new apps.
|
|
27
|
+
*/
|
|
28
|
+
async uploadFile(channelId, filePath, title) {
|
|
29
|
+
const stats = await fs.promises.stat(filePath);
|
|
30
|
+
const fileName = path.basename(filePath);
|
|
31
|
+
// Step 1: Get upload URL
|
|
32
|
+
const urlResponse = await this.api.call('files.getUploadURLExternal', {
|
|
33
|
+
filename: fileName,
|
|
34
|
+
length: stats.size,
|
|
35
|
+
});
|
|
36
|
+
const uploadUrl = urlResponse.upload_url;
|
|
37
|
+
// SSRF prevention: validate hostname is *.slack.com
|
|
38
|
+
if (!validateSlackHostname(uploadUrl)) {
|
|
39
|
+
throw new Error(`[slack-file] Refusing upload: URL hostname is not *.slack.com: ${new URL(uploadUrl).hostname}`);
|
|
40
|
+
}
|
|
41
|
+
// Step 2: PUT file content
|
|
42
|
+
const fileContent = await fs.promises.readFile(filePath);
|
|
43
|
+
const putResponse = await fetch(uploadUrl, {
|
|
44
|
+
method: 'PUT',
|
|
45
|
+
body: fileContent,
|
|
46
|
+
});
|
|
47
|
+
if (!putResponse.ok) {
|
|
48
|
+
throw new Error(`[slack-file] Upload PUT failed: ${putResponse.status} ${putResponse.statusText}`);
|
|
49
|
+
}
|
|
50
|
+
// Step 3: Complete upload and share to channel
|
|
51
|
+
await this.api.call('files.completeUploadExternal', {
|
|
52
|
+
files: [{ id: urlResponse.file_id, title: title || fileName }],
|
|
53
|
+
channel_id: channelId,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Download a file from Slack.
|
|
58
|
+
* Validates destPath to prevent path traversal — must resolve inside filesDir.
|
|
59
|
+
*/
|
|
60
|
+
async downloadFile(url, destPath) {
|
|
61
|
+
// Path traversal protection
|
|
62
|
+
const resolvedPath = path.resolve(this.filesDir, destPath);
|
|
63
|
+
if (!resolvedPath.startsWith(this.filesDir + path.sep) && resolvedPath !== this.filesDir) {
|
|
64
|
+
throw new Error(`[slack-file] Path traversal blocked: ${destPath} resolves outside ${this.filesDir}`);
|
|
65
|
+
}
|
|
66
|
+
// Ensure destination directory exists
|
|
67
|
+
const destDir = path.dirname(resolvedPath);
|
|
68
|
+
await fs.promises.mkdir(destDir, { recursive: true });
|
|
69
|
+
// Download with auth header
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
headers: {
|
|
72
|
+
'Authorization': `Bearer ${this.botToken}`,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`[slack-file] Download failed: ${response.status} ${response.statusText}`);
|
|
77
|
+
}
|
|
78
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
79
|
+
await fs.promises.writeFile(resolvedPath, buffer);
|
|
80
|
+
return resolvedPath;
|
|
81
|
+
}
|
|
82
|
+
/** Get the base directory for downloaded files. */
|
|
83
|
+
get downloadDir() {
|
|
84
|
+
return this.filesDir;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=FileHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileHandler.js","sourceRoot":"","sources":["../../../src/messaging/slack/FileHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic fixed-capacity ring buffer.
|
|
3
|
+
*
|
|
4
|
+
* When full, the oldest item is silently overwritten.
|
|
5
|
+
* Used for per-channel message history caching (capacity: 50).
|
|
6
|
+
*/
|
|
7
|
+
export declare class RingBuffer<T> {
|
|
8
|
+
private buffer;
|
|
9
|
+
private head;
|
|
10
|
+
private count;
|
|
11
|
+
readonly capacity: number;
|
|
12
|
+
constructor(capacity: number);
|
|
13
|
+
/** Add an item. Overwrites oldest if at capacity. */
|
|
14
|
+
push(item: T): void;
|
|
15
|
+
/** Return all items in insertion order (oldest first). */
|
|
16
|
+
toArray(): T[];
|
|
17
|
+
/** Number of items currently stored. */
|
|
18
|
+
get size(): number;
|
|
19
|
+
/** Remove all items. */
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=RingBuffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RingBuffer.d.ts","sourceRoot":"","sources":["../../../src/messaging/slack/RingBuffer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,UAAU,CAAC,CAAC;IACvB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;IAM5B,qDAAqD;IACrD,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAMnB,0DAA0D;IAC1D,OAAO,IAAI,CAAC,EAAE;IAWd,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,wBAAwB;IACxB,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic fixed-capacity ring buffer.
|
|
3
|
+
*
|
|
4
|
+
* When full, the oldest item is silently overwritten.
|
|
5
|
+
* Used for per-channel message history caching (capacity: 50).
|
|
6
|
+
*/
|
|
7
|
+
export class RingBuffer {
|
|
8
|
+
buffer;
|
|
9
|
+
head = 0;
|
|
10
|
+
count = 0;
|
|
11
|
+
capacity;
|
|
12
|
+
constructor(capacity) {
|
|
13
|
+
if (capacity < 1)
|
|
14
|
+
throw new Error('RingBuffer capacity must be >= 1');
|
|
15
|
+
this.capacity = capacity;
|
|
16
|
+
this.buffer = new Array(capacity);
|
|
17
|
+
}
|
|
18
|
+
/** Add an item. Overwrites oldest if at capacity. */
|
|
19
|
+
push(item) {
|
|
20
|
+
this.buffer[this.head] = item;
|
|
21
|
+
this.head = (this.head + 1) % this.capacity;
|
|
22
|
+
if (this.count < this.capacity)
|
|
23
|
+
this.count++;
|
|
24
|
+
}
|
|
25
|
+
/** Return all items in insertion order (oldest first). */
|
|
26
|
+
toArray() {
|
|
27
|
+
if (this.count === 0)
|
|
28
|
+
return [];
|
|
29
|
+
const result = [];
|
|
30
|
+
const start = this.count < this.capacity ? 0 : this.head;
|
|
31
|
+
for (let i = 0; i < this.count; i++) {
|
|
32
|
+
const idx = (start + i) % this.capacity;
|
|
33
|
+
result.push(this.buffer[idx]);
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/** Number of items currently stored. */
|
|
38
|
+
get size() {
|
|
39
|
+
return this.count;
|
|
40
|
+
}
|
|
41
|
+
/** Remove all items. */
|
|
42
|
+
clear() {
|
|
43
|
+
this.buffer = new Array(this.capacity);
|
|
44
|
+
this.head = 0;
|
|
45
|
+
this.count = 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=RingBuffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RingBuffer.js","sourceRoot":"","sources":["../../../src/messaging/slack/RingBuffer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACb,MAAM,CAAoB;IAC1B,IAAI,GAAG,CAAC,CAAC;IACT,KAAK,GAAG,CAAC,CAAC;IACT,QAAQ,CAAS;IAE1B,YAAY,QAAgB;QAC1B,IAAI,QAAQ,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,qDAAqD;IACrD,IAAI,CAAC,IAAO;QACV,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5C,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,0DAA0D;IAC1D,OAAO;QACL,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAChC,MAAM,MAAM,GAAQ,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAM,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,wCAAwC;IACxC,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,wBAAwB;IACxB,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SlackAdapter — Native Slack messaging adapter for Instar.
|
|
3
|
+
*
|
|
4
|
+
* Implements the MessagingAdapter interface using Socket Mode (WebSocket)
|
|
5
|
+
* for event intake and the Slack Web API for outbound messages.
|
|
6
|
+
*
|
|
7
|
+
* Key design decisions:
|
|
8
|
+
* - DIY app model (each user creates their own Slack app)
|
|
9
|
+
* - Socket Mode (no public URLs, no webhooks)
|
|
10
|
+
* - Zero external SDK (direct HTTP to Slack Web API)
|
|
11
|
+
* - authorizedUserIds is required and fail-closed
|
|
12
|
+
* - Ring buffer scoped to authorized users only
|
|
13
|
+
* - JSON-encoded context files (no delimiter-based injection)
|
|
14
|
+
*/
|
|
15
|
+
import type { MessagingAdapter, Message, OutgoingMessage } from '../../core/types.js';
|
|
16
|
+
import { SlackApiClient } from './SlackApiClient.js';
|
|
17
|
+
import { type LogEntry } from '../shared/MessageLogger.js';
|
|
18
|
+
import type { SlackMessage, InteractionPayload } from './types.js';
|
|
19
|
+
export declare class SlackAdapter implements MessagingAdapter {
|
|
20
|
+
readonly platform = "slack";
|
|
21
|
+
private config;
|
|
22
|
+
private stateDir;
|
|
23
|
+
private apiClient;
|
|
24
|
+
private socketClient;
|
|
25
|
+
private channelManager;
|
|
26
|
+
private fileHandler;
|
|
27
|
+
private logger;
|
|
28
|
+
private messageHandler;
|
|
29
|
+
private started;
|
|
30
|
+
private authorizedUsers;
|
|
31
|
+
private channelHistory;
|
|
32
|
+
private pendingPrompts;
|
|
33
|
+
private userCache;
|
|
34
|
+
private promptEvictionTimer;
|
|
35
|
+
private housekeepingTimer;
|
|
36
|
+
private logPurgeTimer;
|
|
37
|
+
/** Called when a prompt gate response is received */
|
|
38
|
+
onPromptResponse: ((channelId: string, promptId: string, value: string) => void) | null;
|
|
39
|
+
/** Called when a message is logged (for dual-write to SQLite) */
|
|
40
|
+
onMessageLogged: ((entry: LogEntry) => void) | null;
|
|
41
|
+
constructor(config: Record<string, unknown>, stateDir: string);
|
|
42
|
+
start(): Promise<void>;
|
|
43
|
+
stop(): Promise<void>;
|
|
44
|
+
send(message: OutgoingMessage): Promise<void | unknown>;
|
|
45
|
+
onMessage(handler: (message: Message) => Promise<void>): void;
|
|
46
|
+
resolveUser(channelIdentifier: string): Promise<string | null>;
|
|
47
|
+
/** Check if a user is authorized. */
|
|
48
|
+
isAuthorized(userId: string): boolean;
|
|
49
|
+
/** Send a message to a specific channel. */
|
|
50
|
+
sendToChannel(channelId: string, text: string, options?: {
|
|
51
|
+
thread_ts?: string;
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
/** Add a reaction (fire-and-forget). */
|
|
54
|
+
addReaction(channelId: string, timestamp: string, emoji: string): void;
|
|
55
|
+
/** Remove a reaction (fire-and-forget). */
|
|
56
|
+
removeReaction(channelId: string, timestamp: string, emoji: string): void;
|
|
57
|
+
/** Update an existing message. */
|
|
58
|
+
updateMessage(channelId: string, timestamp: string, text: string): Promise<void>;
|
|
59
|
+
/** Pin a message. */
|
|
60
|
+
pinMessage(channelId: string, timestamp: string): Promise<void>;
|
|
61
|
+
/** Send an ephemeral message (visible only to one user). */
|
|
62
|
+
postEphemeral(channelId: string, userId: string, text: string): Promise<void>;
|
|
63
|
+
/** Send a message with Block Kit blocks. */
|
|
64
|
+
sendBlocks(channelId: string, blocks: unknown[], text?: string): Promise<string>;
|
|
65
|
+
/** Get cached channel messages from ring buffer. */
|
|
66
|
+
getChannelMessages(channelId: string, limit?: number): SlackMessage[];
|
|
67
|
+
/** Get user info (cached for 5 minutes). */
|
|
68
|
+
getUserInfo(userId: string): Promise<{
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
}>;
|
|
72
|
+
/** Create a channel. */
|
|
73
|
+
createChannel(name: string, isPrivate?: boolean): Promise<string>;
|
|
74
|
+
/** Archive a channel. */
|
|
75
|
+
archiveChannel(channelId: string): Promise<void>;
|
|
76
|
+
/** Upload a file. */
|
|
77
|
+
uploadFile(channelId: string, filePath: string, title?: string): Promise<void>;
|
|
78
|
+
/** Download a file. */
|
|
79
|
+
downloadFile(url: string, destPath: string): Promise<string>;
|
|
80
|
+
/** Get the underlying API client (for routes). */
|
|
81
|
+
get api(): SlackApiClient;
|
|
82
|
+
/** Inject a simulated message for testing. */
|
|
83
|
+
_testInjectMessage(event: Record<string, unknown>): Promise<void>;
|
|
84
|
+
/** Inject a simulated interaction for testing. */
|
|
85
|
+
_testInjectInteraction(payload: InteractionPayload): Promise<void>;
|
|
86
|
+
private _handleEvent;
|
|
87
|
+
private _handleMessage;
|
|
88
|
+
private _handleInteraction;
|
|
89
|
+
private _handleFileShared;
|
|
90
|
+
/** Register a pending prompt (for interaction validation). */
|
|
91
|
+
registerPendingPrompt(messageTs: string, promptId: string, channelId: string): void;
|
|
92
|
+
private _startPromptEviction;
|
|
93
|
+
/**
|
|
94
|
+
* Relay a prompt to the user via Block Kit interactive message.
|
|
95
|
+
* Registers the prompt for validation against spoofed button presses.
|
|
96
|
+
*/
|
|
97
|
+
relayPrompt(channelId: string, promptId: string, question: string, options: Array<{
|
|
98
|
+
label: string;
|
|
99
|
+
value: string;
|
|
100
|
+
primary?: boolean;
|
|
101
|
+
}>): Promise<void>;
|
|
102
|
+
/** Search the JSONL message log. */
|
|
103
|
+
searchLog(params: {
|
|
104
|
+
query?: string;
|
|
105
|
+
channelId?: string;
|
|
106
|
+
since?: Date;
|
|
107
|
+
limit?: number;
|
|
108
|
+
}): LogEntry[];
|
|
109
|
+
/** Get message log statistics. */
|
|
110
|
+
getLogStats(): {
|
|
111
|
+
totalMessages: number;
|
|
112
|
+
logSizeBytes: number;
|
|
113
|
+
logPath: string;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Auto-archive channels idle for more than AUTO_ARCHIVE_DAYS.
|
|
117
|
+
* Runs periodically. Only archives session channels (sess- prefix).
|
|
118
|
+
*/
|
|
119
|
+
private _archiveIdleChannels;
|
|
120
|
+
private _startHousekeeping;
|
|
121
|
+
/** Purge log entries older than logRetentionDays. */
|
|
122
|
+
private _purgeOldLogs;
|
|
123
|
+
private _startLogPurge;
|
|
124
|
+
/**
|
|
125
|
+
* Broadcast the tunnel URL to the dashboard channel.
|
|
126
|
+
* Called by server.ts when tunnel is established.
|
|
127
|
+
*/
|
|
128
|
+
broadcastDashboardUrl(tunnelUrl: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Get count of unanswered user messages in a channel.
|
|
131
|
+
* A message is "unanswered" if it's from a user and no agent reply follows.
|
|
132
|
+
*/
|
|
133
|
+
getUnansweredCount(channelId: string): number;
|
|
134
|
+
private _chunkText;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=SlackAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackAdapter.d.ts","sourceRoot":"","sources":["../../../src/messaging/slack/SlackAdapter.ts"],"names":[],"mappings":"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"}
|