@zhin.js/adapter-slack 1.1.26 → 1.1.27
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/CHANGELOG.md +177 -0
- package/LICENSE +21 -0
- package/PERMITS.md +10 -17
- package/README.md +8 -4
- package/agents/slack/conversation.js +15 -0
- package/agents/slack/conversation.ts +17 -0
- package/agents/slack/messages.js +4 -0
- package/agents/slack/messages.ts +4 -0
- package/agents/slack/skills/slack-channels/tools/archive_channel/index.js +6 -7
- package/agents/slack/skills/slack-channels/tools/archive_channel/index.ts +7 -10
- package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.js +5 -5
- package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.ts +5 -6
- package/agents/slack/skills/slack-channels/tools/set_purpose/index.js +5 -5
- package/agents/slack/skills/slack-channels/tools/set_purpose/index.ts +5 -6
- package/agents/slack/skills/slack-channels/tools/set_topic/index.js +5 -5
- package/agents/slack/skills/slack-channels/tools/set_topic/index.ts +5 -6
- package/agents/slack/skills/slack-channels/tools/unarchive/index.js +2 -3
- package/agents/slack/skills/slack-channels/tools/unarchive/index.ts +2 -3
- package/agents/slack/skills/slack-messages/tools/add_reaction/index.js +5 -4
- package/agents/slack/skills/slack-messages/tools/add_reaction/index.ts +5 -5
- package/agents/slack/skills/slack-messages/tools/edit_message/index.js +6 -5
- package/agents/slack/skills/slack-messages/tools/edit_message/index.ts +6 -6
- package/agents/slack/skills/slack-messages/tools/pin_message/index.js +5 -5
- package/agents/slack/skills/slack-messages/tools/pin_message/index.ts +5 -6
- package/agents/slack/skills/slack-messages/tools/remove_reaction/index.js +5 -4
- package/agents/slack/skills/slack-messages/tools/remove_reaction/index.ts +5 -5
- package/agents/slack/skills/slack-messages/tools/unpin_message/index.js +5 -5
- package/agents/slack/skills/slack-messages/tools/unpin_message/index.ts +5 -6
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/platform-permit.d.ts +4 -12
- package/lib/platform-permit.js +7 -41
- package/lib/protocol.d.ts +1 -5
- package/lib/protocol.js +1 -19
- package/lib/slack-message-ref.d.ts +0 -1
- package/lib/slack-message-ref.js +0 -3
- package/lib/webhook.d.ts +1 -2
- package/lib/webhook.js +12 -29
- package/package.json +24 -26
- package/plugin.js +0 -6
- package/schema.json +0 -56
- package/src/index.ts +1 -7
- package/src/platform-permit.ts +5 -54
- package/src/protocol.ts +2 -23
- package/src/slack-message-ref.ts +0 -4
- package/src/webhook.ts +12 -32
package/schema.json
CHANGED
|
@@ -17,34 +17,6 @@
|
|
|
17
17
|
"default": 15000,
|
|
18
18
|
"description": "Socket Mode client ping timeout (ms)"
|
|
19
19
|
},
|
|
20
|
-
"master": {
|
|
21
|
-
"type": [
|
|
22
|
-
"string",
|
|
23
|
-
"number"
|
|
24
|
-
],
|
|
25
|
-
"description": "框架 master(Slack user id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖",
|
|
26
|
-
"x-i18n": {
|
|
27
|
-
"en": {
|
|
28
|
-
"description": "Framework masters (Slack user id; AI/tool permissions and endpoint management). endpoints[i].master can override this value."
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"trusted": {
|
|
33
|
-
"type": "array",
|
|
34
|
-
"items": {
|
|
35
|
-
"type": [
|
|
36
|
-
"string",
|
|
37
|
-
"number"
|
|
38
|
-
],
|
|
39
|
-
"description": "Trusted Slack user id"
|
|
40
|
-
},
|
|
41
|
-
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加",
|
|
42
|
-
"x-i18n": {
|
|
43
|
-
"en": {
|
|
44
|
-
"description": "Framework trusted users, below master authority. endpoints[i].trusted can add users."
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
20
|
"endpoints": {
|
|
49
21
|
"type": "array",
|
|
50
22
|
"minItems": 1,
|
|
@@ -53,34 +25,6 @@
|
|
|
53
25
|
"type": "object",
|
|
54
26
|
"additionalProperties": false,
|
|
55
27
|
"properties": {
|
|
56
|
-
"master": {
|
|
57
|
-
"type": [
|
|
58
|
-
"string",
|
|
59
|
-
"number"
|
|
60
|
-
],
|
|
61
|
-
"description": "本 endpoint 的框架 master(Slack user id);覆盖顶层 master",
|
|
62
|
-
"x-i18n": {
|
|
63
|
-
"en": {
|
|
64
|
-
"description": "Framework masters for this endpoint (Slack user id), overriding the top-level master list."
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
"trusted": {
|
|
69
|
-
"type": "array",
|
|
70
|
-
"items": {
|
|
71
|
-
"type": [
|
|
72
|
-
"string",
|
|
73
|
-
"number"
|
|
74
|
-
],
|
|
75
|
-
"description": "Trusted Slack user id"
|
|
76
|
-
},
|
|
77
|
-
"description": "本 endpoint 的 trusted 列表",
|
|
78
|
-
"x-i18n": {
|
|
79
|
-
"en": {
|
|
80
|
-
"description": "Trusted users for this endpoint."
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
28
|
"token": {
|
|
85
29
|
"type": "string",
|
|
86
30
|
"description": "Bot User OAuth Token (xoxb-...)"
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,6 @@ export {
|
|
|
6
6
|
headerValue,
|
|
7
7
|
keyboardToBlockKitBlocks,
|
|
8
8
|
normalizeWebhookPath,
|
|
9
|
-
readTextBody,
|
|
10
9
|
resolveSlackChannelType,
|
|
11
10
|
resolveSlackConfig,
|
|
12
11
|
slackInboundConversation,
|
|
@@ -41,12 +40,7 @@ export {
|
|
|
41
40
|
type SlackUserInfo,
|
|
42
41
|
} from './client.js';
|
|
43
42
|
|
|
44
|
-
export {
|
|
45
|
-
checkSlackPlatformPermit,
|
|
46
|
-
normalizeSlackSenderForPermit,
|
|
47
|
-
platformPermit,
|
|
48
|
-
slackGroupPermitResolver,
|
|
49
|
-
} from './platform-permit.js';
|
|
43
|
+
export { platformPermit } from './platform-permit.js';
|
|
50
44
|
|
|
51
45
|
export { normalizeSlackReactionName } from './slack-reaction.js';
|
|
52
46
|
export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
|
package/src/platform-permit.ts
CHANGED
|
@@ -1,59 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Slack platform permit
|
|
2
|
+
* Slack platform permit helper(ADR 0056 §5)。
|
|
3
|
+
*
|
|
4
|
+
* 平台身份由适配器上报到 `sender.platform`,permit 由内置评估器(`@zhin.js/permission`)判断;
|
|
5
|
+
* 适配器不再导出自有的 permit 解析器或检查函数。
|
|
3
6
|
*/
|
|
4
|
-
import type { PermissionSubject } from '@zhin.js/permission';
|
|
5
|
-
|
|
6
|
-
const ADAPTER = 'slack';
|
|
7
7
|
|
|
8
8
|
export function platformPermit(perm: string): string {
|
|
9
|
-
return `platform
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
13
|
-
scene_admin: 'channel_manager',
|
|
14
|
-
scene_owner: 'workspace_owner',
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function slackGroupPermitResolver(logicalPerm: string): string {
|
|
18
|
-
return platformPermit(FACTORY_PERM_MAP[logicalPerm] ?? logicalPerm);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function normalizeSlackSenderForPermit(input: {
|
|
22
|
-
isWorkspaceOwner?: boolean;
|
|
23
|
-
isWorkspaceAdmin?: boolean;
|
|
24
|
-
isChannelManager?: boolean;
|
|
25
|
-
}): { role?: string; permissions?: string[] } {
|
|
26
|
-
const permissions: string[] = [];
|
|
27
|
-
if (input.isWorkspaceOwner) {
|
|
28
|
-
permissions.push('workspace_owner', 'workspace_admin', 'channel_manager');
|
|
29
|
-
return { role: 'owner', permissions };
|
|
30
|
-
}
|
|
31
|
-
if (input.isWorkspaceAdmin) {
|
|
32
|
-
permissions.push('workspace_admin', 'channel_manager');
|
|
33
|
-
return { role: 'admin', permissions };
|
|
34
|
-
}
|
|
35
|
-
if (input.isChannelManager) {
|
|
36
|
-
permissions.push('channel_manager');
|
|
37
|
-
return { role: 'channel_admin', permissions };
|
|
38
|
-
}
|
|
39
|
-
return { role: 'member', permissions };
|
|
9
|
+
return `platform-permission:${perm}`;
|
|
40
10
|
}
|
|
41
|
-
|
|
42
|
-
export function checkSlackPlatformPermit(perm: string, subject: PermissionSubject): boolean {
|
|
43
|
-
const role = subject.sender?.role?.[0];
|
|
44
|
-
const permissions = subject.sender?.permissions ?? [];
|
|
45
|
-
const has = (t: string) => permissions.includes(t);
|
|
46
|
-
|
|
47
|
-
switch (perm) {
|
|
48
|
-
case 'workspace_owner':
|
|
49
|
-
return role === 'owner' || has('workspace_owner');
|
|
50
|
-
case 'workspace_admin':
|
|
51
|
-
return role === 'owner' || role === 'admin' || has('workspace_admin') || has('workspace_owner');
|
|
52
|
-
case 'channel_manager':
|
|
53
|
-
return role === 'owner' || role === 'admin' || role === 'channel_admin'
|
|
54
|
-
|| has('channel_manager') || has('workspace_admin') || has('workspace_owner');
|
|
55
|
-
default:
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
package/src/protocol.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { EndpointProfileConfiguration } from 'zhin.js/adapter';
|
|
|
4
4
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
5
5
|
*/
|
|
6
6
|
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
7
|
-
import type { IncomingMessage } from 'node:http';
|
|
8
7
|
import { isMediaRef, type MediaRef, type ConversationKind, type ConversationRef } from '@zhin.js/im-contract';
|
|
9
8
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
10
9
|
import { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
|
|
@@ -417,30 +416,10 @@ export function verifySlackSignature(
|
|
|
417
416
|
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
|
|
418
417
|
}
|
|
419
418
|
|
|
420
|
-
export async function readTextBody(
|
|
421
|
-
request: IncomingMessage,
|
|
422
|
-
options: { readonly limit?: number } = {},
|
|
423
|
-
): Promise<string> {
|
|
424
|
-
const limit = options.limit ?? 1_048_576;
|
|
425
|
-
const chunks: Buffer[] = [];
|
|
426
|
-
let size = 0;
|
|
427
|
-
for await (const chunk of request) {
|
|
428
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
429
|
-
size += buffer.length;
|
|
430
|
-
if (size > limit) {
|
|
431
|
-
request.destroy();
|
|
432
|
-
throw new Error(`Request body exceeds ${limit} bytes`);
|
|
433
|
-
}
|
|
434
|
-
chunks.push(buffer);
|
|
435
|
-
}
|
|
436
|
-
return Buffer.concat(chunks).toString('utf8');
|
|
437
|
-
}
|
|
438
419
|
|
|
439
420
|
export function headerValue(
|
|
440
|
-
headers:
|
|
421
|
+
headers: Headers,
|
|
441
422
|
name: string,
|
|
442
423
|
): string {
|
|
443
|
-
|
|
444
|
-
if (Array.isArray(raw)) return raw[0] ?? '';
|
|
445
|
-
return raw ?? '';
|
|
424
|
+
return headers.get(name) ?? '';
|
|
446
425
|
}
|
package/src/slack-message-ref.ts
CHANGED
package/src/webhook.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slack webhook HTTP: signature → parse → admit.
|
|
3
3
|
*/
|
|
4
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
4
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
5
|
+
import { readTextBody } from '@zhin.js/host-http-contract';
|
|
6
6
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
7
7
|
import {
|
|
8
8
|
headerValue,
|
|
9
|
-
readTextBody,
|
|
10
9
|
verifySlackSignature,
|
|
11
10
|
type ResolvedSlackConfig,
|
|
12
11
|
type SlackInteractionPayload,
|
|
@@ -29,26 +28,21 @@ export function registerSlackWebhookRoutes(
|
|
|
29
28
|
): HttpRouteRegistration[] {
|
|
30
29
|
const path = handler.config.webhookPath;
|
|
31
30
|
return [
|
|
32
|
-
http.route('POST', path,
|
|
33
|
-
await handleSlackWebhookRequest(request, response, handler);
|
|
34
|
-
}, { summary: 'Slack Events API / Interactivity', tags: ['slack'] }),
|
|
31
|
+
http.route('POST', path, (request) => handleSlackWebhookRequest(request, handler), { summary: 'Slack Events API / Interactivity', tags: ['slack'] }),
|
|
35
32
|
];
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
export async function handleSlackWebhookRequest(
|
|
39
|
-
request:
|
|
40
|
-
response: ServerResponse,
|
|
36
|
+
request: Request,
|
|
41
37
|
handler: SlackWebhookHandler,
|
|
42
|
-
): Promise<
|
|
38
|
+
): Promise<Response> {
|
|
43
39
|
try {
|
|
44
40
|
const rawBody = await readTextBody(request);
|
|
45
41
|
const timestamp = headerValue(request.headers, 'x-slack-request-timestamp');
|
|
46
42
|
const signature = headerValue(request.headers, 'x-slack-signature');
|
|
47
43
|
|
|
48
44
|
if (!verifySlackSignature(handler.config.signingSecret, rawBody, timestamp, signature)) {
|
|
49
|
-
|
|
50
|
-
response.end('Invalid signature');
|
|
51
|
-
return;
|
|
45
|
+
return new Response('Invalid signature', { status: 401, headers: { 'Content-Type': 'text/plain' } });
|
|
52
46
|
}
|
|
53
47
|
|
|
54
48
|
const contentType = headerValue(request.headers, 'content-type');
|
|
@@ -57,17 +51,13 @@ export async function handleSlackWebhookRequest(
|
|
|
57
51
|
const payloadStr = params.get('payload');
|
|
58
52
|
if (payloadStr) {
|
|
59
53
|
const payload = JSON.parse(payloadStr) as SlackInteractionPayload;
|
|
60
|
-
response.writeHead(200);
|
|
61
|
-
response.end('');
|
|
62
54
|
handler.admitInteraction(payload);
|
|
63
|
-
return;
|
|
55
|
+
return new Response('', { status: 200 });
|
|
64
56
|
}
|
|
65
57
|
const body = Object.fromEntries(params) as unknown as SlackSlashCommand;
|
|
66
58
|
if (body.command) {
|
|
67
|
-
response.writeHead(200);
|
|
68
|
-
response.end('');
|
|
69
59
|
handler.admitSlashCommand(body);
|
|
70
|
-
return;
|
|
60
|
+
return new Response('', { status: 200 });
|
|
71
61
|
}
|
|
72
62
|
}
|
|
73
63
|
|
|
@@ -75,32 +65,22 @@ export async function handleSlackWebhookRequest(
|
|
|
75
65
|
try {
|
|
76
66
|
envelope = JSON.parse(rawBody) as Record<string, unknown>;
|
|
77
67
|
} catch {
|
|
78
|
-
|
|
79
|
-
response.end('');
|
|
80
|
-
return;
|
|
68
|
+
return new Response('', { status: 200 });
|
|
81
69
|
}
|
|
82
70
|
|
|
83
71
|
if (envelope.type === 'url_verification') {
|
|
84
72
|
const challenge = (envelope as unknown as SlackUrlVerification).challenge;
|
|
85
|
-
|
|
86
|
-
response.end(JSON.stringify({ challenge }));
|
|
87
|
-
return;
|
|
73
|
+
return new Response(JSON.stringify({ challenge }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
88
74
|
}
|
|
89
75
|
|
|
90
76
|
if (envelope.type === 'event_callback' && envelope.event) {
|
|
91
|
-
response.writeHead(200);
|
|
92
|
-
response.end('');
|
|
93
77
|
handler.handleEnvelope(envelope);
|
|
94
|
-
return;
|
|
78
|
+
return new Response('', { status: 200 });
|
|
95
79
|
}
|
|
96
80
|
|
|
97
|
-
|
|
98
|
-
response.end('');
|
|
81
|
+
return new Response('', { status: 200 });
|
|
99
82
|
} catch (error) {
|
|
100
83
|
logger.error('Slack webhook error:', error);
|
|
101
|
-
|
|
102
|
-
response.writeHead(200);
|
|
103
|
-
response.end('');
|
|
104
|
-
}
|
|
84
|
+
return new Response('', { status: 200 });
|
|
105
85
|
}
|
|
106
86
|
}
|