@zhin.js/adapter-slack 4.0.1 → 4.1.1

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.
Files changed (80) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +77 -160
  3. package/adapters/slack.ts +34 -0
  4. package/{skills/slack/SKILL.md → agent/skills/slack.md} +2 -0
  5. package/agent/tools/add_reaction.ts +26 -0
  6. package/agent/tools/archive_channel.ts +24 -0
  7. package/agent/tools/edit_message.ts +28 -0
  8. package/agent/tools/invite_to_channel.ts +26 -0
  9. package/agent/tools/pin_message.ts +26 -0
  10. package/agent/tools/remove_reaction.ts +26 -0
  11. package/agent/tools/set_purpose.ts +26 -0
  12. package/agent/tools/set_topic.ts +26 -0
  13. package/agent/tools/unarchive.ts +24 -0
  14. package/agent/tools/unpin_message.ts +26 -0
  15. package/agent/tools/user_info.ts +31 -0
  16. package/lib/endpoint.d.ts +133 -94
  17. package/lib/endpoint.js +270 -612
  18. package/lib/index.d.ts +11 -10
  19. package/lib/index.js +11 -252
  20. package/lib/markdown-to-mrkdwn.d.ts +9 -0
  21. package/lib/markdown-to-mrkdwn.js +121 -0
  22. package/lib/mrkdwn-to-markdown.d.ts +4 -0
  23. package/lib/mrkdwn-to-markdown.js +30 -0
  24. package/lib/platform-permit.d.ts +1 -2
  25. package/lib/platform-permit.js +4 -2
  26. package/lib/protocol.d.ts +143 -0
  27. package/lib/protocol.js +225 -0
  28. package/lib/slack-agent-deps.d.ts +28 -0
  29. package/lib/slack-agent-deps.js +30 -0
  30. package/lib/slack-inbound-filter.d.ts +12 -0
  31. package/lib/slack-inbound-filter.js +46 -0
  32. package/lib/slack-message-ref.d.ts +7 -0
  33. package/lib/slack-message-ref.js +17 -0
  34. package/lib/slack-outbound.d.ts +24 -0
  35. package/lib/slack-outbound.js +109 -0
  36. package/lib/slack-reaction.d.ts +1 -0
  37. package/lib/slack-reaction.js +26 -0
  38. package/lib/slack-response-url.d.ts +5 -0
  39. package/lib/slack-response-url.js +16 -0
  40. package/lib/webhook.d.ts +14 -0
  41. package/lib/webhook.js +69 -0
  42. package/package.json +53 -14
  43. package/plugin.ts +13 -0
  44. package/schema.json +38 -0
  45. package/src/endpoint.ts +339 -647
  46. package/src/index.ts +65 -277
  47. package/src/markdown-to-mrkdwn.ts +117 -0
  48. package/src/mrkdwn-to-markdown.ts +29 -0
  49. package/src/platform-permit.ts +1 -2
  50. package/src/protocol.ts +380 -0
  51. package/src/slack-agent-deps.ts +57 -0
  52. package/src/slack-inbound-filter.ts +60 -0
  53. package/src/slack-message-ref.ts +18 -0
  54. package/src/slack-outbound.ts +167 -0
  55. package/src/slack-reaction.ts +23 -0
  56. package/src/slack-response-url.ts +26 -0
  57. package/src/webhook.ts +103 -0
  58. package/lib/adapter.d.ts +0 -19
  59. package/lib/adapter.d.ts.map +0 -1
  60. package/lib/adapter.js +0 -46
  61. package/lib/adapter.js.map +0 -1
  62. package/lib/endpoint.d.ts.map +0 -1
  63. package/lib/endpoint.js.map +0 -1
  64. package/lib/index.d.ts.map +0 -1
  65. package/lib/index.js.map +0 -1
  66. package/lib/platform-permit.d.ts.map +0 -1
  67. package/lib/platform-permit.js.map +0 -1
  68. package/lib/segment-mapper.d.ts +0 -2
  69. package/lib/segment-mapper.d.ts.map +0 -1
  70. package/lib/segment-mapper.js +0 -2
  71. package/lib/segment-mapper.js.map +0 -1
  72. package/lib/types.d.ts +0 -17
  73. package/lib/types.d.ts.map +0 -1
  74. package/lib/types.js +0 -2
  75. package/lib/types.js.map +0 -1
  76. package/plugin.yml +0 -3
  77. package/src/adapter.ts +0 -54
  78. package/src/segment-mapper.ts +0 -1
  79. package/src/types.ts +0 -18
  80. /package/{skills/slack → agent}/PERMITS.md +0 -0
package/lib/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { SlackAdapter } from "./adapter.js";
2
- declare module "zhin.js" {
3
- interface Adapters {
4
- slack: SlackAdapter;
5
- }
6
- }
7
- export * from "./types.js";
8
- export { SlackEndpoint } from "./endpoint.js";
9
- export { SlackAdapter } from "./adapter.js";
10
- //# sourceMappingURL=index.d.ts.map
1
+ export { formatInboundContent, formatInteractionContent, formatOutboundWire, formatSlashContent, headerValue, inboundMessageId, keyboardToBlockKitBlocks, normalizeWebhookPath, readTextBody, resolveSlackChannelType, resolveSlackConfig, verifySlackSignature, type ResolvedSlackConfig, type SlackAdapterConfig, type SlackBlockAction, type SlackEvent, type SlackEventEnvelope, type SlackInteractionPayload, type SlackMessageEvent, type SlackSlashCommand, type SlackUrlVerification, type SlackWireSegment, } from './protocol.js';
2
+ export { SlackEndpoint, type SlackEndpointOptions, type SlackSocketLike, type SlackWebClientLike, } from './endpoint.js';
3
+ export { registerSlackWebhookRoutes, handleSlackWebhookRequest, type SlackWebhookHandler, } from './webhook.js';
4
+ export { getSlackAgentDeps, registerSlackAgentEndpoint, setSlackAgentDeps, type SlackAgentDeps, type SlackAgentEndpoint, } from './slack-agent-deps.js';
5
+ export { checkSlackPlatformPermit, normalizeSlackSenderForPermit, platformPermit, registerSlackPlatformPermitChecker, slackGroupPermitResolver, } from './platform-permit.js';
6
+ export { formatSlackMessageRef, parseSlackMessageRef, slackMessageTs, } from './slack-message-ref.js';
7
+ export { normalizeSlackReactionName } from './slack-reaction.js';
8
+ export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
9
+ export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
10
+ export { createSlackInboundFilterState, shouldDropSlackInboundMessage, } from './slack-inbound-filter.js';
11
+ export { editSlackContent, sendSlackContent } from './slack-outbound.js';
package/lib/index.js CHANGED
@@ -1,252 +1,11 @@
1
- /**
2
- * Slack 适配器入口:类型扩展、导出、注册
3
- */
4
- import { usePlugin, createSceneManagementTools } from "zhin.js";
5
- import { SlackAdapter } from "./adapter.js";
6
- import { platformPermit, registerSlackPlatformPermitChecker, slackGroupPermitResolver, } from "./platform-permit.js";
7
- export * from "./types.js";
8
- export { SlackEndpoint } from "./endpoint.js";
9
- export { SlackAdapter } from "./adapter.js";
10
- const plugin = usePlugin();
11
- const { provide, useContext } = plugin;
12
- provide({
13
- name: "slack",
14
- description: "Slack Endpoint Adapter",
15
- mounted: async (p) => {
16
- const adapter = new SlackAdapter(p);
17
- await adapter.start();
18
- return adapter;
19
- },
20
- dispose: async (adapter) => {
21
- await adapter.stop();
22
- },
23
- });
24
- useContext('tool', 'slack', (toolService, slack) => {
25
- const disposers = [];
26
- disposers.push(registerSlackPlatformPermitChecker());
27
- const sceneTools = createSceneManagementTools(slack, 'slack', { permitResolver: slackGroupPermitResolver, registerChecker: false });
28
- disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
29
- function getEndpoint(endpointId) {
30
- const endpoint = slack.endpoints.get(endpointId);
31
- if (!endpoint)
32
- throw new Error(`Endpoint ${endpointId} 不存在`);
33
- return endpoint;
34
- }
35
- disposers.push(toolService.addTool({
36
- name: 'slack_invite_to_channel',
37
- description: '邀请用户加入 Slack 频道',
38
- parameters: {
39
- type: 'object',
40
- properties: {
41
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
42
- channel: { type: 'string', description: '频道 ID' },
43
- users: { type: 'string', description: '用户 ID 列表(逗号分隔)' },
44
- },
45
- required: ['endpoint_id', 'channel', 'users'],
46
- },
47
- platforms: ['slack'],
48
- tags: ['slack'],
49
- permissions: [platformPermit('channel_manager')],
50
- execute: async (args) => {
51
- const endpoint = getEndpoint(args.endpoint_id);
52
- const success = await endpoint.inviteToChannel(args.channel, args.users.split(','));
53
- return { success, message: success ? '已邀请用户加入频道' : '操作失败' };
54
- },
55
- }, plugin.name));
56
- disposers.push(toolService.addTool({
57
- name: 'slack_set_topic',
58
- description: '设置 Slack 频道话题',
59
- parameters: {
60
- type: 'object',
61
- properties: {
62
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
63
- channel: { type: 'string', description: '频道 ID' },
64
- topic: { type: 'string', description: '新话题' },
65
- },
66
- required: ['endpoint_id', 'channel', 'topic'],
67
- },
68
- platforms: ['slack'],
69
- tags: ['slack'],
70
- permissions: [platformPermit('channel_manager')],
71
- execute: async (args) => {
72
- const endpoint = getEndpoint(args.endpoint_id);
73
- const success = await endpoint.setChannelTopic(args.channel, args.topic);
74
- return { success, message: success ? '已设置频道话题' : '操作失败' };
75
- },
76
- }, plugin.name));
77
- disposers.push(toolService.addTool({
78
- name: 'slack_archive_channel',
79
- description: '归档 Slack 频道',
80
- parameters: {
81
- type: 'object',
82
- properties: {
83
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
84
- channel: { type: 'string', description: '频道 ID' },
85
- },
86
- required: ['endpoint_id', 'channel'],
87
- },
88
- platforms: ['slack'],
89
- tags: ['slack'],
90
- permissions: [platformPermit('workspace_admin')],
91
- execute: async (args) => {
92
- const endpoint = getEndpoint(args.endpoint_id);
93
- const success = await endpoint.archiveChannel(args.channel);
94
- return { success, message: success ? '已归档频道' : '操作失败' };
95
- },
96
- }, plugin.name));
97
- disposers.push(toolService.addTool({
98
- name: 'slack_pin_message',
99
- description: '置顶 Slack 消息',
100
- parameters: {
101
- type: 'object',
102
- properties: {
103
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
104
- channel: { type: 'string', description: '频道 ID' },
105
- timestamp: { type: 'string', description: '消息时间戳' },
106
- },
107
- required: ['endpoint_id', 'channel', 'timestamp'],
108
- },
109
- platforms: ['slack'],
110
- tags: ['slack'],
111
- permissions: [platformPermit('channel_manager')],
112
- execute: async (args) => {
113
- const endpoint = getEndpoint(args.endpoint_id);
114
- const success = await endpoint.pinMessage(args.channel, args.timestamp);
115
- return { success, message: success ? '已置顶消息' : '操作失败' };
116
- },
117
- }, plugin.name));
118
- disposers.push(toolService.addTool({
119
- name: 'slack_add_reaction',
120
- description: '给 Slack 消息添加表情反应',
121
- parameters: {
122
- type: 'object',
123
- properties: {
124
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
125
- channel: { type: 'string', description: '频道 ID' },
126
- timestamp: { type: 'string', description: '消息时间戳' },
127
- emoji: { type: 'string', description: '表情名称(不含冒号)' },
128
- },
129
- required: ['endpoint_id', 'channel', 'timestamp', 'emoji'],
130
- },
131
- platforms: ['slack'],
132
- tags: ['slack'],
133
- execute: async (args) => {
134
- const endpoint = getEndpoint(args.endpoint_id);
135
- const success = await endpoint.addReaction(args.channel, args.timestamp, args.emoji);
136
- return { success, message: success ? `已添加反应 :${args.emoji}:` : '操作失败' };
137
- },
138
- }, plugin.name));
139
- disposers.push(toolService.addTool({
140
- name: 'slack_remove_reaction',
141
- description: '移除 Slack 消息上的表情反应',
142
- parameters: {
143
- type: 'object',
144
- properties: {
145
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
146
- channel_id: { type: 'string', description: '频道 ID' },
147
- timestamp: { type: 'string', description: '消息时间戳' },
148
- name: { type: 'string', description: '表情名称(如 thumbsup、heart)' },
149
- },
150
- required: ['endpoint_id', 'channel_id', 'timestamp', 'name'],
151
- },
152
- platforms: ['slack'],
153
- tags: ['slack'],
154
- execute: async (args) => {
155
- const endpoint = getEndpoint(args.endpoint_id);
156
- const success = await endpoint.removeReaction(args.channel_id, args.timestamp, args.name);
157
- return { success, message: success ? `已移除反应 :${args.name}:` : '操作失败' };
158
- },
159
- }, plugin.name));
160
- disposers.push(toolService.addTool({
161
- name: 'slack_unpin_message',
162
- description: '取消 Slack 频道中消息的置顶',
163
- parameters: {
164
- type: 'object',
165
- properties: {
166
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
167
- channel_id: { type: 'string', description: '频道 ID' },
168
- timestamp: { type: 'string', description: '消息时间戳' },
169
- },
170
- required: ['endpoint_id', 'channel_id', 'timestamp'],
171
- },
172
- platforms: ['slack'],
173
- tags: ['slack'],
174
- permissions: [platformPermit('channel_manager')],
175
- execute: async (args) => {
176
- const endpoint = getEndpoint(args.endpoint_id);
177
- const success = await endpoint.unpinMessage(args.channel_id, args.timestamp);
178
- return { success, message: success ? '已取消置顶' : '操作失败' };
179
- },
180
- }, plugin.name));
181
- disposers.push(toolService.addTool({
182
- name: 'slack_user_info',
183
- description: '查询 Slack 用户详细信息',
184
- parameters: {
185
- type: 'object',
186
- properties: {
187
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
188
- user_id: { type: 'string', description: '用户 ID' },
189
- },
190
- required: ['endpoint_id', 'user_id'],
191
- },
192
- platforms: ['slack'],
193
- tags: ['slack'],
194
- execute: async (args) => {
195
- const endpoint = getEndpoint(args.endpoint_id);
196
- const user = await endpoint.getUserInfo(args.user_id);
197
- return {
198
- id: user.id,
199
- name: user.name,
200
- real_name: user.real_name,
201
- display_name: user.profile?.display_name,
202
- email: user.profile?.email,
203
- is_admin: user.is_admin,
204
- is_bot: user.is_bot,
205
- status_text: user.profile?.status_text,
206
- };
207
- },
208
- }, plugin.name));
209
- disposers.push(toolService.addTool({
210
- name: 'slack_set_purpose',
211
- description: '设置 Slack 频道的用途/目的',
212
- parameters: {
213
- type: 'object',
214
- properties: {
215
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
216
- channel_id: { type: 'string', description: '频道 ID' },
217
- purpose: { type: 'string', description: '频道用途描述' },
218
- },
219
- required: ['endpoint_id', 'channel_id', 'purpose'],
220
- },
221
- platforms: ['slack'],
222
- tags: ['slack'],
223
- permissions: [platformPermit('channel_manager')],
224
- execute: async (args) => {
225
- const endpoint = getEndpoint(args.endpoint_id);
226
- const success = await endpoint.setChannelPurpose(args.channel_id, args.purpose);
227
- return { success, message: success ? '频道用途已更新' : '操作失败' };
228
- },
229
- }, plugin.name));
230
- disposers.push(toolService.addTool({
231
- name: 'slack_unarchive',
232
- description: '恢复已归档的 Slack 频道',
233
- parameters: {
234
- type: 'object',
235
- properties: {
236
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
237
- channel_id: { type: 'string', description: '频道 ID' },
238
- },
239
- required: ['endpoint_id', 'channel_id'],
240
- },
241
- platforms: ['slack'],
242
- tags: ['slack'],
243
- permissions: [platformPermit('workspace_admin')],
244
- execute: async (args) => {
245
- const endpoint = getEndpoint(args.endpoint_id);
246
- const success = await endpoint.unarchiveChannel(args.channel_id);
247
- return { success, message: success ? '频道已恢复' : '操作失败' };
248
- },
249
- }, plugin.name));
250
- return () => disposers.forEach(d => d());
251
- });
252
- //# sourceMappingURL=index.js.map
1
+ export { formatInboundContent, formatInteractionContent, formatOutboundWire, formatSlashContent, headerValue, inboundMessageId, keyboardToBlockKitBlocks, normalizeWebhookPath, readTextBody, resolveSlackChannelType, resolveSlackConfig, verifySlackSignature, } from './protocol.js';
2
+ export { SlackEndpoint, } from './endpoint.js';
3
+ export { registerSlackWebhookRoutes, handleSlackWebhookRequest, } from './webhook.js';
4
+ export { getSlackAgentDeps, registerSlackAgentEndpoint, setSlackAgentDeps, } from './slack-agent-deps.js';
5
+ export { checkSlackPlatformPermit, normalizeSlackSenderForPermit, platformPermit, registerSlackPlatformPermitChecker, slackGroupPermitResolver, } from './platform-permit.js';
6
+ export { formatSlackMessageRef, parseSlackMessageRef, slackMessageTs, } from './slack-message-ref.js';
7
+ export { normalizeSlackReactionName } from './slack-reaction.js';
8
+ export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
9
+ export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
10
+ export { createSlackInboundFilterState, shouldDropSlackInboundMessage, } from './slack-inbound-filter.js';
11
+ export { editSlackContent, sendSlackContent } from './slack-outbound.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 常见 Markdown → Slack mrkdwn(Slack 粗体为 *text*,链接为 <url|text>)
3
+ */
4
+ export declare const SLACK_MRKDWN_TEXT_MAX = 2900;
5
+ export declare function markdownToMrkdwn(text: string): string;
6
+ /** 按 Slack section mrkdwn 上限切分(尽量在换行/空格处断开) */
7
+ export declare function splitMrkdwnText(text: string, maxLen?: number): string[];
8
+ /** 通知栏 / 无障碍 fallback 用的纯文本 */
9
+ export declare function mrkdwnToPlainFallback(text: string): string;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * 常见 Markdown → Slack mrkdwn(Slack 粗体为 *text*,链接为 <url|text>)
3
+ */
4
+ export const SLACK_MRKDWN_TEXT_MAX = 2900;
5
+ export function markdownToMrkdwn(text) {
6
+ let result = replaceMarkdownLinks(text);
7
+ result = result.replace(/\*\*([^*]+)\*\*/g, '*$1*');
8
+ result = result.replace(/__([^_]+)__/g, '*$1*');
9
+ result = result.replace(/~~([^~]+)~~/g, '~$1~');
10
+ result = result.replace(/^#{1,6}\s+(.+)$/gm, '*$1*');
11
+ return result;
12
+ }
13
+ function replaceMarkdownLinks(text) {
14
+ let out = '';
15
+ let cursor = 0;
16
+ while (cursor < text.length) {
17
+ const labelStart = text.indexOf('[', cursor);
18
+ if (labelStart < 0)
19
+ break;
20
+ const labelEnd = text.indexOf(']', labelStart + 1);
21
+ if (labelEnd < 0 || text[labelEnd + 1] !== '(') {
22
+ out += text.slice(cursor, labelStart + 1);
23
+ cursor = labelStart + 1;
24
+ continue;
25
+ }
26
+ const urlEnd = text.indexOf(')', labelEnd + 2);
27
+ if (urlEnd < 0)
28
+ break;
29
+ out += text.slice(cursor, labelStart);
30
+ out += `<${text.slice(labelEnd + 2, urlEnd)}|${text.slice(labelStart + 1, labelEnd)}>`;
31
+ cursor = urlEnd + 1;
32
+ }
33
+ return out + text.slice(cursor);
34
+ }
35
+ /** 按 Slack section mrkdwn 上限切分(尽量在换行/空格处断开) */
36
+ export function splitMrkdwnText(text, maxLen = SLACK_MRKDWN_TEXT_MAX) {
37
+ if (text.length <= maxLen)
38
+ return [text];
39
+ const chunks = [];
40
+ let rest = text;
41
+ while (rest.length > maxLen) {
42
+ let cut = rest.lastIndexOf('\n', maxLen);
43
+ if (cut < maxLen * 0.4)
44
+ cut = rest.lastIndexOf(' ', maxLen);
45
+ if (cut <= 0)
46
+ cut = maxLen;
47
+ chunks.push(rest.slice(0, cut).trimEnd());
48
+ rest = rest.slice(cut).trimStart();
49
+ }
50
+ if (rest)
51
+ chunks.push(rest);
52
+ return chunks.length > 0 ? chunks : [text];
53
+ }
54
+ /** 通知栏 / 无障碍 fallback 用的纯文本 */
55
+ export function mrkdwnToPlainFallback(text) {
56
+ return stripMrkdwnMarkers(stripAngleBrackets(rewriteAngleLinks(text)));
57
+ }
58
+ /**
59
+ * `<url|text>` → `text (url)`。
60
+ * 线性扫描,语义等价于 `/<([^|>]+)\|([^>]+)>/g`(分组一不含 `|`,
61
+ * 因此切分点恒为首个 `|`),但避免量词重叠在超长 `<...` 输入上的
62
+ * 二次方回溯(js/polynomial-redos)。
63
+ */
64
+ function rewriteAngleLinks(text) {
65
+ let out = '';
66
+ let i = 0;
67
+ while (i < text.length) {
68
+ const open = text.indexOf('<', i);
69
+ if (open < 0)
70
+ break;
71
+ const close = text.indexOf('>', open + 1);
72
+ if (close < 0)
73
+ break;
74
+ const inner = text.slice(open + 1, close);
75
+ const pipe = inner.indexOf('|');
76
+ if (pipe > 0 && pipe < inner.length - 1) {
77
+ out += text.slice(i, open);
78
+ out += `${inner.slice(pipe + 1)} (${inner.slice(0, pipe)})`;
79
+ i = close + 1;
80
+ continue;
81
+ }
82
+ // 非链接形态:本趟不匹配,原样保留 `<` 并继续(与原正则行为一致)。
83
+ out += text.slice(i, open + 1);
84
+ i = open + 1;
85
+ }
86
+ return out + text.slice(i);
87
+ }
88
+ /**
89
+ * 剥掉剩余 `<...>` 的尖括号。线性扫描,等价于 `/<([^>]+)>/g`
90
+ * (空内容 `<>` 不匹配、原样保留)。
91
+ */
92
+ function stripAngleBrackets(text) {
93
+ let out = '';
94
+ let i = 0;
95
+ while (i < text.length) {
96
+ const open = text.indexOf('<', i);
97
+ if (open < 0)
98
+ break;
99
+ const close = text.indexOf('>', open + 1);
100
+ if (close < 0)
101
+ break;
102
+ if (close === open + 1) {
103
+ out += text.slice(i, open + 1);
104
+ i = open + 1;
105
+ continue;
106
+ }
107
+ out += text.slice(i, open);
108
+ out += text.slice(open + 1, close);
109
+ i = close + 1;
110
+ }
111
+ return out + text.slice(i);
112
+ }
113
+ /** 去掉 `*` `_` `~` `` ` `` 样式符号(等价于 `/[*_~`]/g`,逐字符线性)。 */
114
+ function stripMrkdwnMarkers(text) {
115
+ let out = '';
116
+ for (const ch of text) {
117
+ if (ch !== '*' && ch !== '_' && ch !== '~' && ch !== '`')
118
+ out += ch;
119
+ }
120
+ return out;
121
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Slack mrkdwn → 通用 Markdown(入站文本段)
3
+ */
4
+ export declare function mrkdwnToMarkdown(text: string): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Slack mrkdwn → 通用 Markdown(入站文本段)
3
+ */
4
+ export function mrkdwnToMarkdown(text) {
5
+ let result = replaceSlackLinks(text);
6
+ result = result.replace(/\*([^*]+)\*/g, '**$1**');
7
+ result = result.replace(/_([^_]+)_/g, '*$1*');
8
+ result = result.replace(/~([^~]+)~/g, '~~$1~~');
9
+ return result;
10
+ }
11
+ function replaceSlackLinks(text) {
12
+ let out = '';
13
+ let cursor = 0;
14
+ while (cursor < text.length) {
15
+ const start = text.indexOf('<', cursor);
16
+ if (start < 0)
17
+ break;
18
+ const end = text.indexOf('>', start + 1);
19
+ if (end < 0)
20
+ break;
21
+ out += text.slice(cursor, start);
22
+ const body = text.slice(start + 1, end);
23
+ const sep = body.indexOf('|');
24
+ const url = sep >= 0 ? body.slice(0, sep) : body;
25
+ const label = sep >= 0 ? body.slice(sep + 1) : url;
26
+ out += url.startsWith('http://') || url.startsWith('https://') ? `[${label}](${url})` : text.slice(start, end + 1);
27
+ cursor = end + 1;
28
+ }
29
+ return out + text.slice(cursor);
30
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Slack platform permit — workspace + channel 权限
3
3
  */
4
- import type { Message } from 'zhin.js';
4
+ import { type Message } from '@zhin.js/core';
5
5
  export declare function platformPermit(perm: string): string;
6
6
  export declare function slackGroupPermitResolver(logicalPerm: string): string;
7
7
  export declare function normalizeSlackSenderForPermit(input: {
@@ -14,4 +14,3 @@ export declare function normalizeSlackSenderForPermit(input: {
14
14
  };
15
15
  export declare function checkSlackPlatformPermit(perm: string, message: Message<any>): boolean;
16
16
  export declare function registerSlackPlatformPermitChecker(): () => void;
17
- //# sourceMappingURL=platform-permit.d.ts.map
@@ -1,4 +1,7 @@
1
- import { registerPlatformPermitChecker } from 'zhin.js';
1
+ /**
2
+ * Slack platform permit — workspace + channel 权限
3
+ */
4
+ import { registerPlatformPermitChecker } from '@zhin.js/core';
2
5
  const ADAPTER = 'slack';
3
6
  export function platformPermit(perm) {
4
7
  return `platform(${ADAPTER},${perm})`;
@@ -46,4 +49,3 @@ export function checkSlackPlatformPermit(perm, message) {
46
49
  export function registerSlackPlatformPermitChecker() {
47
50
  return registerPlatformPermitChecker(ADAPTER, checkSlackPlatformPermit);
48
51
  }
49
- //# sourceMappingURL=platform-permit.js.map
@@ -0,0 +1,143 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
3
+ export interface SlackAdapterConfig {
4
+ readonly name?: string;
5
+ readonly token?: string;
6
+ readonly signingSecret?: string;
7
+ readonly appToken?: string;
8
+ /** Default true (Socket Mode). Set false for HTTP Events API via httpHostToken. */
9
+ readonly socketMode?: boolean;
10
+ readonly webhookPath?: string;
11
+ readonly clientPingTimeout?: number;
12
+ /** Transitional: legacy root `endpoints[]` with `context: slack`. */
13
+ readonly endpoints?: ReadonlyArray<Partial<ResolvedSlackConfig> & {
14
+ readonly context?: string;
15
+ readonly socketMode?: boolean;
16
+ readonly signingSecret?: string;
17
+ readonly appToken?: string;
18
+ readonly webhookPath?: string;
19
+ readonly clientPingTimeout?: number;
20
+ }>;
21
+ }
22
+ export interface ResolvedSlackConfig {
23
+ readonly context: 'slack';
24
+ readonly name: string;
25
+ readonly token: string;
26
+ readonly mode: 'socket' | 'http';
27
+ readonly signingSecret: string;
28
+ readonly appToken?: string;
29
+ readonly webhookPath: string;
30
+ readonly clientPingTimeout: number;
31
+ }
32
+ export interface SlackEventEnvelope {
33
+ readonly token?: string;
34
+ readonly team_id?: string;
35
+ readonly api_app_id?: string;
36
+ readonly event: SlackEvent;
37
+ readonly type: 'event_callback';
38
+ readonly event_id?: string;
39
+ readonly event_time?: number;
40
+ }
41
+ export interface SlackUrlVerification {
42
+ readonly type: 'url_verification';
43
+ readonly token: string;
44
+ readonly challenge: string;
45
+ }
46
+ export interface SlackInteractionPayload {
47
+ readonly type: 'block_actions' | 'message_action' | 'shortcut' | 'view_submission' | 'view_closed';
48
+ readonly trigger_id?: string;
49
+ readonly user: {
50
+ id: string;
51
+ username?: string;
52
+ name?: string;
53
+ team_id?: string;
54
+ };
55
+ readonly channel?: {
56
+ id: string;
57
+ name?: string;
58
+ };
59
+ readonly message?: {
60
+ ts: string;
61
+ text?: string;
62
+ [key: string]: unknown;
63
+ };
64
+ readonly actions?: SlackBlockAction[];
65
+ readonly response_url?: string;
66
+ readonly [key: string]: unknown;
67
+ }
68
+ export interface SlackBlockAction {
69
+ readonly type: string;
70
+ readonly action_id: string;
71
+ readonly block_id: string;
72
+ readonly value?: string;
73
+ readonly text?: {
74
+ type: string;
75
+ text: string;
76
+ };
77
+ readonly action_ts?: string;
78
+ readonly [key: string]: unknown;
79
+ }
80
+ export interface SlackSlashCommand {
81
+ readonly token: string;
82
+ readonly team_id: string;
83
+ readonly channel_id: string;
84
+ readonly channel_name: string;
85
+ readonly user_id: string;
86
+ readonly user_name: string;
87
+ readonly command: string;
88
+ readonly text: string;
89
+ readonly response_url: string;
90
+ readonly trigger_id: string;
91
+ readonly api_app_id?: string;
92
+ }
93
+ export interface SlackEvent {
94
+ readonly type: string;
95
+ readonly ts?: string;
96
+ readonly event_ts?: string;
97
+ readonly user?: string;
98
+ readonly channel?: string;
99
+ readonly channel_type?: string;
100
+ readonly text?: string;
101
+ readonly thread_ts?: string;
102
+ readonly subtype?: string;
103
+ readonly bot_id?: string;
104
+ readonly [key: string]: unknown;
105
+ }
106
+ export type SlackMessageEvent = SlackEvent & {
107
+ readonly type: 'message' | 'app_mention';
108
+ readonly ts: string;
109
+ readonly channel: string;
110
+ };
111
+ export interface SlackWireSegment {
112
+ readonly type: string;
113
+ readonly data?: Record<string, unknown>;
114
+ }
115
+ export declare function resolveSlackConfig(config?: SlackAdapterConfig): ResolvedSlackConfig;
116
+ export declare function normalizeWebhookPath(path: string): string;
117
+ export declare function resolveSlackChannelType(event: Pick<SlackEvent, 'channel_type'>): 'private' | 'group';
118
+ /** Build inbound text for MessageGateway.receive. */
119
+ export declare function formatInboundContent(event: SlackMessageEvent | SlackEvent): string;
120
+ export declare function formatInteractionContent(payload: SlackInteractionPayload): string;
121
+ export declare function formatSlashContent(cmd: SlackSlashCommand): string;
122
+ export declare function inboundMessageId(event: SlackMessageEvent): string;
123
+ /**
124
+ * Wire-encode an already-rendered outbound payload into Slack text + Block Kit blocks.
125
+ * Segment canonicalization is intentionally not done here.
126
+ */
127
+ export declare function formatOutboundWire(payload: unknown): {
128
+ text: string;
129
+ blocks: Record<string, unknown>[];
130
+ attachments: Record<string, unknown>[];
131
+ files: Array<{
132
+ buffer?: Buffer;
133
+ url?: string;
134
+ path?: string;
135
+ name?: string;
136
+ }>;
137
+ };
138
+ export declare function keyboardToBlockKitBlocks(data: Record<string, unknown>): Record<string, unknown>[];
139
+ export declare function verifySlackSignature(signingSecret: string, rawBody: string, timestamp: string, signature: string): boolean;
140
+ export declare function readTextBody(request: IncomingMessage, options?: {
141
+ readonly limit?: number;
142
+ }): Promise<string>;
143
+ export declare function headerValue(headers: IncomingMessage['headers'], name: string): string;