@zhin.js/adapter-slack 6.0.3 → 6.0.6

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 CHANGED
@@ -1,5 +1,95 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [2d0a622]
8
+ - @zhin.js/command@1.0.10
9
+ - @zhin.js/adapter@1.1.7
10
+ - @zhin.js/core@1.5.5
11
+ - @zhin.js/agent@1.1.6
12
+ - zhin.js@6.0.5
13
+
14
+ ## 6.0.5
15
+
16
+ ### Patch Changes
17
+
18
+ - 51015d6: refactor(adapters): migrate platform-permit to PermissionSubject
19
+
20
+ All adapter platform-permit checkers now accept PermissionSubject
21
+ (duck-typed from Message/CommandSession) instead of Message directly.
22
+ registerPlatformPermitChecker is replaced by host.registerPlatform in
23
+ the adapter plugin setup. activity-feedback service updated for new
24
+ PermissionHost integration.
25
+
26
+ - Updated dependencies [6f9c366]
27
+ - Updated dependencies [373a56b]
28
+ - Updated dependencies [0de46a8]
29
+ - Updated dependencies [c106ecc]
30
+ - Updated dependencies [ca92e03]
31
+ - Updated dependencies [b0f37ae]
32
+ - Updated dependencies [ba08a2f]
33
+ - Updated dependencies [b08f7fe]
34
+ - Updated dependencies [daffd4c]
35
+ - Updated dependencies [36c7400]
36
+ - Updated dependencies [a9fa72e]
37
+ - Updated dependencies [c8de3ef]
38
+ - Updated dependencies [60f0fc8]
39
+ - Updated dependencies [574c990]
40
+ - Updated dependencies [d096f16]
41
+ - Updated dependencies [3f29623]
42
+ - Updated dependencies [2916852]
43
+ - Updated dependencies [d047869]
44
+ - Updated dependencies [162fa34]
45
+ - Updated dependencies [e62561e]
46
+ - Updated dependencies [3eeeb46]
47
+ - Updated dependencies [85d0f82]
48
+ - Updated dependencies [61bfc1c]
49
+ - Updated dependencies [04bad47]
50
+ - Updated dependencies [e40b048]
51
+ - Updated dependencies [5a5b1bb]
52
+ - Updated dependencies [f1708c3]
53
+ - Updated dependencies [d254a81]
54
+ - Updated dependencies [7123c47]
55
+ - Updated dependencies [05befc1]
56
+ - Updated dependencies [0663b6a]
57
+ - Updated dependencies [f28f9b3]
58
+ - Updated dependencies [9f340f7]
59
+ - Updated dependencies [e53444f]
60
+ - Updated dependencies [5eedd26]
61
+ - Updated dependencies [92b0dd7]
62
+ - Updated dependencies [f919b6f]
63
+ - Updated dependencies [098e411]
64
+ - Updated dependencies [e1b7c01]
65
+ - Updated dependencies [9b94f87]
66
+ - Updated dependencies [a7df753]
67
+ - @zhin.js/agent@1.1.5
68
+ - @zhin.js/host-http@1.0.8
69
+ - @zhin.js/permission@1.0.1
70
+ - @zhin.js/im-contract@1.0.3
71
+ - @zhin.js/adapter@1.1.7
72
+ - @zhin.js/plugin-runtime@1.1.5
73
+ - @zhin.js/command@1.0.9
74
+ - @zhin.js/core@1.5.4
75
+ - zhin.js@6.0.4
76
+
77
+ ## 6.0.4
78
+
79
+ ### Patch Changes
80
+
81
+ - f8c7a54: fix: im
82
+ - Updated dependencies [f8c7a54]
83
+ - @zhin.js/logger@1.0.76
84
+ - @zhin.js/host-http@1.0.7
85
+ - @zhin.js/adapter@1.1.6
86
+ - @zhin.js/agent@1.1.4
87
+ - @zhin.js/command@1.0.8
88
+ - @zhin.js/core@1.5.3
89
+ - @zhin.js/im-contract@1.0.2
90
+ - @zhin.js/plugin-runtime@1.1.4
91
+ - zhin.js@6.0.3
92
+
3
93
  ## 6.0.3
4
94
 
5
95
  ### Patch Changes
package/README.md CHANGED
@@ -6,7 +6,7 @@ Zhin.js Slack 适配器(Plugin Runtime),优先 Socket Mode,也可经 Run
6
6
 
7
7
  - **Socket Mode**(默认):WebSocket 长连接,无需公网 URL
8
8
  - **HTTP Events API**:`httpHostToken` POST(签名验证),**非** legacy host-router/Koa
9
- - 入站经 `messageGatewayToken`;出站 `send({ target, payload })` → `chat.postMessage` / Block Kit
9
+ - 入站经 `messageGatewayToken`;出站 `send({ conversation, payload })` → `chat.postMessage` / Block Kit
10
10
  - 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
11
11
  - Block Kit 按钮、斜杠命令、消息编辑、表情反应等(见 `agent/tools/`)
12
12
 
package/adapters/slack.js CHANGED
@@ -21,8 +21,8 @@ export default defineAdapter({
21
21
  create(context) {
22
22
  const config = resolveSlackConfig(context.config);
23
23
  // 注册到插件运行时状态(slack.endpoint list 的"运行中"数据源)
24
- context.use(slackRuntimeStateToken).endpoints.set(config.name, {
25
- name: config.name,
24
+ context.use(slackRuntimeStateToken).endpoints.set(config.id, {
25
+ id: config.id,
26
26
  mode: config.mode,
27
27
  });
28
28
  if (config.mode === 'http') {
package/adapters/slack.ts CHANGED
@@ -26,8 +26,8 @@ export default defineAdapter<SlackAdapterConfig>({
26
26
  create(context) {
27
27
  const config = resolveSlackConfig(context.config);
28
28
  // 注册到插件运行时状态(slack.endpoint list 的"运行中"数据源)
29
- context.use(slackRuntimeStateToken).endpoints.set(config.name, {
30
- name: config.name,
29
+ context.use(slackRuntimeStateToken).endpoints.set(config.id, {
30
+ id: config.id,
31
31
  mode: config.mode,
32
32
  });
33
33
  if (config.mode === 'http') {
@@ -17,6 +17,7 @@ export default defineAgentTool<{
17
17
  const { getEndpoint } = getSlackAgentDeps();
18
18
  const endpoint = getEndpoint(endpoint_id);
19
19
  const user = await endpoint.getUserInfo(user_id);
20
+ if (!user) throw new Error(`Slack 用户不存在: ${user_id}`);
20
21
  return {
21
22
  id: user.id,
22
23
  name: user.name,
package/lib/endpoint.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { MessageGateway } from '@zhin.js/core/runtime';
3
3
  import type { HttpHost } from '@zhin.js/host-http';
4
4
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
5
5
  import { type ResolvedSlackConfig, type SlackEvent, type SlackInteractionPayload, type SlackMessageEvent, type SlackSlashCommand } from './protocol.js';
6
+ import { type SlackUserInfo } from './slack-agent-deps.js';
6
7
  import { type SlackChatClient } from './slack-outbound.js';
7
8
  import { type SlackWebhookHandler } from './webhook.js';
8
9
  export interface SlackSocketLike {
@@ -160,7 +161,7 @@ export declare class SlackEndpoint implements EndpointInstance, SlackWebhookHand
160
161
  renameChannel(channel: string, name: string): Promise<boolean>;
161
162
  getChannelMembers(channel: string): Promise<string[]>;
162
163
  getChannelInfo(channel: string): Promise<unknown>;
163
- getUserInfo(user: string): Promise<unknown>;
164
+ getUserInfo(user: string): Promise<SlackUserInfo | undefined>;
164
165
  addReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
165
166
  removeReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
166
167
  pinMessage(channel: string, timestamp: string): Promise<boolean>;
package/lib/endpoint.js CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { SocketModeClient } from '@slack/socket-mode';
5
5
  import { WebClient } from '@slack/web-api';
6
- import { formatCompact, getLogger } from '@zhin.js/logger';
6
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
7
  import { formatInboundContent, formatInteractionContent, formatSlashContent, resolveSlackChannelType, slackInboundConversation, } from './protocol.js';
8
8
  import { registerSlackAgentEndpoint } from './slack-agent-deps.js';
9
9
  import { createSlackInboundFilterState, shouldDropSlackInboundMessage, } from './slack-inbound-filter.js';
@@ -12,8 +12,8 @@ import { normalizeSlackReactionName } from './slack-reaction.js';
12
12
  import { postSlackEphemeral } from './slack-response-url.js';
13
13
  import { editSlackContent, sendSlackContent } from './slack-outbound.js';
14
14
  import { registerSlackWebhookRoutes } from './webhook.js';
15
- const logger = getLogger('slack');
16
15
  export class SlackEndpoint {
16
+ #logger;
17
17
  #options;
18
18
  #inboundFilter = createSlackInboundFilterState();
19
19
  #messageChannelMap = new Map();
@@ -26,11 +26,12 @@ export class SlackEndpoint {
26
26
  #unregisterAgent;
27
27
  management = createSlackEndpointManagement(this);
28
28
  constructor(options) {
29
+ this.#logger = getAdapterLogger('slack', options.config.id);
29
30
  this.#options = options;
30
31
  }
31
32
  /** Console 展示 / AdapterIndex live name(多 endpoint 时与 entry name 一致)。 */
32
33
  get name() {
33
- return this.#options.config.name;
34
+ return this.#options.config.id;
34
35
  }
35
36
  get client() {
36
37
  return this.#client;
@@ -49,7 +50,7 @@ export class SlackEndpoint {
49
50
  const { config } = this.#options;
50
51
  this.#client = this.#options.createClient?.(config.token)
51
52
  ?? new WebClient(config.token);
52
- this.#unregisterAgent = registerSlackAgentEndpoint(config.name, this);
53
+ this.#unregisterAgent = registerSlackAgentEndpoint(config.id, this);
53
54
  if (config.mode === 'socket') {
54
55
  await this.#startSocket();
55
56
  }
@@ -59,8 +60,8 @@ export class SlackEndpoint {
59
60
  throw new Error('Slack HTTP Events API requires httpHostToken (Runtime Host)');
60
61
  }
61
62
  this.#routeReleases.push(...registerSlackWebhookRoutes(http, this));
62
- logger.debug(formatCompact({
63
- endpoint: config.name,
63
+ this.#logger.debug(formatCompact({
64
+ endpoint: config.id,
64
65
  op: 'webhook',
65
66
  path: config.webhookPath,
66
67
  }));
@@ -68,16 +69,12 @@ export class SlackEndpoint {
68
69
  const authTest = await this.#client.auth.test();
69
70
  if (authTest.user_id)
70
71
  this.#botUserId = String(authTest.user_id);
71
- logger.info(formatCompact({
72
- op: 'connect',
73
- endpoint: config.name,
74
- mode: config.mode,
75
- platform_user_id: this.#botUserId,
76
- }));
72
+ this.#logger.info(`connected (${config.mode})`
73
+ + (this.#botUserId ? ` | user: ${this.#botUserId}` : ''));
77
74
  }
78
75
  catch (error) {
79
76
  await this.stop();
80
- logger.error('Failed to connect Slack endpoint:', error);
77
+ this.#logger.error('Failed to connect Slack endpoint:', error);
81
78
  throw error;
82
79
  }
83
80
  }
@@ -104,14 +101,14 @@ export class SlackEndpoint {
104
101
  this.#unregisterAgent = undefined;
105
102
  this.#client = undefined;
106
103
  this.#started = false;
107
- logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
104
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
108
105
  }
109
106
  async send({ conversation, payload }) {
110
107
  if (!this.#client)
111
108
  throw new Error('Slack client not connected');
112
109
  const channel = conversation.id;
113
110
  const threadTs = conversation.threadId;
114
- const result = await sendSlackContent(this.#client, payload, { channel, threadTs }, logger);
111
+ const result = await sendSlackContent(this.#client, payload, { channel, threadTs }, this.#logger);
115
112
  if (result.ts)
116
113
  this.trackMessageChannel(result.ts, channel);
117
114
  return formatSlackMessageRef(channel, result.ts || String(Date.now()));
@@ -138,18 +135,17 @@ export class SlackEndpoint {
138
135
  conversation,
139
136
  message: { conversation, id: msg.ts },
140
137
  content: formatInboundContent(msg),
141
- sender: msg.user ?? msg.channel,
138
+ sender: { id: msg.user ?? msg.channel ?? '' },
139
+ endpointId: this.#options.config.id,
140
+ ...(msg.type === 'app_mention' ? { mentioned: true } : {}),
142
141
  metadata: Object.freeze({
143
- endpoint: this.#options.config.name,
144
142
  channelType: resolveSlackChannelType(msg),
145
143
  userId: msg.user,
146
144
  threadTs,
147
145
  ts: msg.ts,
148
- // app_mention 事件本身即 @ 机器人;新 Runtime 纯文本 content 需经 metadata 传递
149
- ...(msg.type === 'app_mention' ? { mentioned: true } : {}),
150
146
  }),
151
147
  }).catch((err) => {
152
- logger.warn(formatCompact({
148
+ this.#logger.warn(formatCompact({
153
149
  op: 'slack_gateway_receive_failed',
154
150
  target: `${conversation.kind}:${conversation.id}`,
155
151
  error: err instanceof Error ? err.message : String(err),
@@ -165,7 +161,7 @@ export class SlackEndpoint {
165
161
  const userId = payload.user.id;
166
162
  const messageTs = payload.message?.ts ?? '';
167
163
  if (payload.response_url) {
168
- postSlackEphemeral(payload.response_url, '已收到', logger);
164
+ postSlackEphemeral(payload.response_url, '已收到', this.#logger);
169
165
  }
170
166
  const actionTs = payload.actions[0]?.action_ts ?? messageTs ?? `action-${Date.now()}`;
171
167
  const conversation = slackInboundConversation(String(this.#options.id), {
@@ -177,15 +173,15 @@ export class SlackEndpoint {
177
173
  conversation,
178
174
  message: { conversation, id: actionTs },
179
175
  content: formatInteractionContent(payload),
180
- sender: userId,
176
+ sender: { id: userId },
177
+ endpointId: this.#options.config.id,
181
178
  metadata: Object.freeze({
182
- endpoint: this.#options.config.name,
183
179
  eventType: 'block_actions',
184
180
  actionId: payload.actions[0]?.action_id,
185
181
  threadTs: messageTs || undefined,
186
182
  }),
187
183
  }).catch((err) => {
188
- logger.warn(formatCompact({
184
+ this.#logger.warn(formatCompact({
189
185
  op: 'slack_gateway_receive_failed',
190
186
  target: `${conversation.kind}:${conversation.id}`,
191
187
  error: err instanceof Error ? err.message : String(err),
@@ -195,7 +191,7 @@ export class SlackEndpoint {
195
191
  admitSlashCommand(cmd) {
196
192
  if (!this.#open)
197
193
  return;
198
- postSlackEphemeral(cmd.response_url, '处理中…', logger);
194
+ postSlackEphemeral(cmd.response_url, '处理中…', this.#logger);
199
195
  const conversation = slackInboundConversation(String(this.#options.id), {
200
196
  channelId: cmd.channel_id,
201
197
  });
@@ -203,14 +199,14 @@ export class SlackEndpoint {
203
199
  conversation,
204
200
  message: { conversation, id: cmd.trigger_id },
205
201
  content: formatSlashContent(cmd),
206
- sender: cmd.user_id,
202
+ sender: { id: cmd.user_id, name: cmd.user_name },
203
+ endpointId: this.#options.config.id,
207
204
  metadata: Object.freeze({
208
- endpoint: this.#options.config.name,
209
205
  eventType: 'slash_command',
210
206
  command: cmd.command,
211
207
  }),
212
208
  }).catch((err) => {
213
- logger.warn(formatCompact({
209
+ this.#logger.warn(formatCompact({
214
210
  op: 'slack_gateway_receive_failed',
215
211
  target: `${conversation.kind}:${conversation.id}`,
216
212
  error: err instanceof Error ? err.message : String(err),
package/lib/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { formatInboundContent, formatInteractionContent, formatOutboundWire, for
2
2
  export { SlackEndpoint, type SlackEndpointOptions, type SlackSocketLike, type SlackWebClientLike, } from './endpoint.js';
3
3
  export { registerSlackWebhookRoutes, handleSlackWebhookRequest, type SlackWebhookHandler, } from './webhook.js';
4
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';
5
+ export { checkSlackPlatformPermit, normalizeSlackSenderForPermit, platformPermit, slackGroupPermitResolver, } from './platform-permit.js';
6
6
  export { normalizeSlackReactionName } from './slack-reaction.js';
7
7
  export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
8
8
  export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ export { formatInboundContent, formatInteractionContent, formatOutboundWire, for
2
2
  export { SlackEndpoint, } from './endpoint.js';
3
3
  export { registerSlackWebhookRoutes, handleSlackWebhookRequest, } from './webhook.js';
4
4
  export { getSlackAgentDeps, registerSlackAgentEndpoint, setSlackAgentDeps, } from './slack-agent-deps.js';
5
- export { checkSlackPlatformPermit, normalizeSlackSenderForPermit, platformPermit, registerSlackPlatformPermitChecker, slackGroupPermitResolver, } from './platform-permit.js';
5
+ export { checkSlackPlatformPermit, normalizeSlackSenderForPermit, platformPermit, slackGroupPermitResolver, } from './platform-permit.js';
6
6
  export { normalizeSlackReactionName } from './slack-reaction.js';
7
7
  export { markdownToMrkdwn, mrkdwnToPlainFallback, splitMrkdwnText } from './markdown-to-mrkdwn.js';
8
8
  export { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Slack platform permit — workspace + channel 权限
3
3
  */
4
- import { type Message } from '@zhin.js/core';
4
+ import type { PermissionSubject } from '@zhin.js/permission';
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: {
@@ -12,5 +12,4 @@ export declare function normalizeSlackSenderForPermit(input: {
12
12
  role?: string;
13
13
  permissions?: string[];
14
14
  };
15
- export declare function checkSlackPlatformPermit(perm: string, message: Message<any>): boolean;
16
- export declare function registerSlackPlatformPermitChecker(): () => void;
15
+ export declare function checkSlackPlatformPermit(perm: string, subject: PermissionSubject): boolean;
@@ -1,7 +1,3 @@
1
- /**
2
- * Slack platform permit — workspace + channel 权限
3
- */
4
- import { registerPlatformPermitChecker } from '@zhin.js/core';
5
1
  const ADAPTER = 'slack';
6
2
  export function platformPermit(perm) {
7
3
  return `platform(${ADAPTER},${perm})`;
@@ -29,10 +25,9 @@ export function normalizeSlackSenderForPermit(input) {
29
25
  }
30
26
  return { role: 'member', permissions };
31
27
  }
32
- export function checkSlackPlatformPermit(perm, message) {
33
- const sender = message.$sender;
34
- const permissions = sender.permissions ?? [];
35
- const role = sender.role;
28
+ export function checkSlackPlatformPermit(perm, subject) {
29
+ const role = subject.sender?.role?.[0];
30
+ const permissions = subject.sender?.permissions ?? [];
36
31
  const has = (t) => permissions.includes(t);
37
32
  switch (perm) {
38
33
  case 'workspace_owner':
@@ -46,6 +41,3 @@ export function checkSlackPlatformPermit(perm, message) {
46
41
  return false;
47
42
  }
48
43
  }
49
- export function registerSlackPlatformPermitChecker() {
50
- return registerPlatformPermitChecker(ADAPTER, checkSlackPlatformPermit);
51
- }
package/lib/protocol.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { IncomingMessage } from 'node:http';
2
2
  import type { ConversationRef } from '@zhin.js/im-contract';
3
3
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
4
4
  export interface SlackAdapterConfig {
5
- readonly name?: string;
5
+ readonly id?: string;
6
6
  readonly token?: string;
7
7
  readonly signingSecret?: string;
8
8
  readonly appToken?: string;
@@ -22,7 +22,7 @@ export interface SlackAdapterConfig {
22
22
  }
23
23
  export interface ResolvedSlackConfig {
24
24
  readonly context: 'slack';
25
- readonly name: string;
25
+ readonly id: string;
26
26
  readonly token: string;
27
27
  readonly mode: 'socket' | 'http';
28
28
  readonly signingSecret: string;
@@ -121,7 +121,7 @@ export declare function resolveSlackChannelType(event: Pick<SlackEvent, 'channel
121
121
  * → group;交互/斜杠命令无 channel_type 时按平台 id 前缀推断(D* 为 DM)。Slack 无
122
122
  * guild 容器概念(team 为 workspace 级,不进 parent);线程根 ts 进 `threadId`。
123
123
  */
124
- export declare function slackInboundConversation(endpointId: string, opts: {
124
+ export declare function slackInboundConversation(endpointKey: string, opts: {
125
125
  readonly channelId: string;
126
126
  readonly channelType?: string;
127
127
  readonly threadId?: string;
package/lib/protocol.js CHANGED
@@ -22,8 +22,8 @@ export function resolveSlackConfig(config = {}) {
22
22
  if (!token) {
23
23
  throw new TypeError('Slack adapter requires token (plugins.<key>.token or endpoints with context: slack)');
24
24
  }
25
- const name = (typeof config.name === 'string' && config.name)
26
- || (typeof entry?.name === 'string' && entry.name)
25
+ const id = (typeof config.id === 'string' && config.id)
26
+ || (typeof entry?.id === 'string' && entry.id)
27
27
  || process.env.SLACK_BOT_NAME
28
28
  || 'slack-bot';
29
29
  const socketMode = config.socketMode ?? entry?.socketMode;
@@ -45,7 +45,7 @@ export function resolveSlackConfig(config = {}) {
45
45
  }
46
46
  return {
47
47
  context: 'slack',
48
- name,
48
+ id,
49
49
  token,
50
50
  mode,
51
51
  signingSecret,
@@ -68,12 +68,12 @@ export function resolveSlackChannelType(event) {
68
68
  * → group;交互/斜杠命令无 channel_type 时按平台 id 前缀推断(D* 为 DM)。Slack 无
69
69
  * guild 容器概念(team 为 workspace 级,不进 parent);线程根 ts 进 `threadId`。
70
70
  */
71
- export function slackInboundConversation(endpointId, opts) {
71
+ export function slackInboundConversation(endpointKey, opts) {
72
72
  const kind = opts.channelType != null
73
73
  ? (opts.channelType === 'im' ? 'private' : 'group')
74
74
  : (opts.channelId.startsWith('D') ? 'private' : 'group');
75
75
  return {
76
- endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
76
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
77
77
  kind,
78
78
  id: opts.channelId,
79
79
  ...(opts.threadId ? { threadId: opts.threadId } : {}),
@@ -12,17 +12,30 @@ export interface SlackAgentEndpoint {
12
12
  renameChannel(channel: string, name: string): Promise<boolean>;
13
13
  getChannelMembers(channel: string): Promise<string[]>;
14
14
  getChannelInfo(channel: string): Promise<unknown>;
15
- getUserInfo(user: string): Promise<unknown>;
15
+ getUserInfo(user: string): Promise<SlackUserInfo | undefined>;
16
16
  addReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
17
17
  removeReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
18
18
  pinMessage(channel: string, timestamp: string): Promise<boolean>;
19
19
  unpinMessage(channel: string, timestamp: string): Promise<boolean>;
20
20
  editMessage(channel: string, messageTs: string, content: unknown): Promise<void>;
21
21
  }
22
+ /** Slack users.info 返回的用户对象(SDK 响应字段对 agent 工具的投影)。 */
23
+ export interface SlackUserInfo {
24
+ id?: string;
25
+ name?: string;
26
+ real_name?: string;
27
+ is_admin?: boolean;
28
+ is_bot?: boolean;
29
+ profile?: {
30
+ display_name?: string;
31
+ email?: string;
32
+ status_text?: string;
33
+ };
34
+ }
22
35
  export interface SlackAgentDeps {
23
- getEndpoint: (endpointId: string) => SlackAgentEndpoint;
36
+ getEndpoint: (endpointKey: string) => SlackAgentEndpoint;
24
37
  }
25
- export declare function registerSlackAgentEndpoint(endpointId: string, endpoint: SlackAgentEndpoint): () => void;
38
+ export declare function registerSlackAgentEndpoint(endpointKey: string, endpoint: SlackAgentEndpoint): () => void;
26
39
  /** Optional override used by tests / transitional callers. Pass `null` to clear. */
27
40
  export declare function setSlackAgentDeps(deps: SlackAgentDeps | null): void;
28
41
  export declare function getSlackAgentDeps(): SlackAgentDeps;
@@ -4,11 +4,11 @@
4
4
  */
5
5
  const endpoints = new Map();
6
6
  let override = null;
7
- export function registerSlackAgentEndpoint(endpointId, endpoint) {
8
- endpoints.set(endpointId, endpoint);
7
+ export function registerSlackAgentEndpoint(endpointKey, endpoint) {
8
+ endpoints.set(endpointKey, endpoint);
9
9
  return () => {
10
- if (endpoints.get(endpointId) === endpoint) {
11
- endpoints.delete(endpointId);
10
+ if (endpoints.get(endpointKey) === endpoint) {
11
+ endpoints.delete(endpointKey);
12
12
  }
13
13
  };
14
14
  }
@@ -20,10 +20,10 @@ export function getSlackAgentDeps() {
20
20
  if (override)
21
21
  return override;
22
22
  return {
23
- getEndpoint(endpointId) {
24
- const registered = endpoints.get(endpointId);
23
+ getEndpoint(endpointKey) {
24
+ const registered = endpoints.get(endpointKey);
25
25
  if (!registered)
26
- throw new Error(`Endpoint ${endpointId} 不存在`);
26
+ throw new Error(`Endpoint ${endpointKey} 不存在`);
27
27
  return registered;
28
28
  },
29
29
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-slack",
3
- "version": "6.0.3",
3
+ "version": "6.0.6",
4
4
  "description": "Zhin.js Slack adapter for Plugin Runtime (Socket Mode / HTTP Events)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,22 +34,23 @@
34
34
  "dependencies": {
35
35
  "@slack/socket-mode": "^2.0.7",
36
36
  "@slack/web-api": "^7.19.0",
37
- "@zhin.js/adapter": "1.1.5",
38
- "@zhin.js/command": "1.0.7",
39
- "@zhin.js/core": "1.5.2",
40
- "@zhin.js/host-http": "1.0.6",
41
- "@zhin.js/im-contract": "1.0.1",
42
- "@zhin.js/logger": "1.0.75",
43
- "@zhin.js/plugin-runtime": "1.1.3"
37
+ "@zhin.js/adapter": "1.1.7",
38
+ "@zhin.js/command": "1.0.10",
39
+ "@zhin.js/core": "1.5.5",
40
+ "@zhin.js/host-http": "1.0.8",
41
+ "@zhin.js/im-contract": "1.0.3",
42
+ "@zhin.js/logger": "1.0.76",
43
+ "@zhin.js/plugin-runtime": "1.1.5"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "zod": "^4.0.0",
47
- "@zhin.js/adapter": "1.1.5",
48
- "@zhin.js/agent": "1.1.3",
49
- "@zhin.js/core": "1.5.2",
50
- "@zhin.js/host-http": "1.0.6",
51
- "@zhin.js/plugin-runtime": "1.1.3",
52
- "zhin.js": "6.0.2"
47
+ "@zhin.js/adapter": "1.1.7",
48
+ "@zhin.js/agent": "1.1.6",
49
+ "@zhin.js/core": "1.5.5",
50
+ "@zhin.js/host-http": "1.0.8",
51
+ "@zhin.js/permission": "1.0.1",
52
+ "@zhin.js/plugin-runtime": "1.1.5",
53
+ "zhin.js": "6.0.5"
53
54
  },
54
55
  "peerDependenciesMeta": {
55
56
  "zhin.js": {
@@ -67,8 +68,8 @@
67
68
  "typescript": "^6.0.3",
68
69
  "vitest": "^4.1.10",
69
70
  "zod": "^4.4.3",
70
- "@zhin.js/agent": "1.1.3",
71
- "zhin.js": "6.0.2"
71
+ "@zhin.js/agent": "1.1.6",
72
+ "zhin.js": "6.0.5"
72
73
  },
73
74
  "files": [
74
75
  "adapters",
package/plugin.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  import { createEndpointRuntimeState } from '@zhin.js/adapter';
3
3
  import { definePlugin } from '@zhin.js/plugin-runtime';
4
- import { registerSlackPlatformPermitChecker } from "./lib/platform-permit.js";
4
+ import { permissionHostToken } from '@zhin.js/permission';
5
+ import { checkSlackPlatformPermit } from "./lib/platform-permit.js";
5
6
  import { slackRuntimeStateToken } from "./lib/slack-runtime-state.js";
6
7
  export default definePlugin({
7
8
  name: 'slack',
@@ -9,9 +10,10 @@ export default definePlugin({
9
10
  displayName: 'Slack Adapter',
10
11
  },
11
12
  setup(context) {
12
- // 运行中 endpoint 注册表(slack.endpoint list 的"运行中"数据源)
13
13
  context.resources.provide(slackRuntimeStateToken, createEndpointRuntimeState());
14
- // 平台权限门禁:workspace_owner / channel_manager 等(agent 工具 platformPermit)
15
- return registerSlackPlatformPermitChecker();
14
+ if (context.resources.has(permissionHostToken)) {
15
+ const host = context.resources.use(permissionHostToken);
16
+ return host.registerPlatform('slack', checkSlackPlatformPermit);
17
+ }
16
18
  },
17
19
  });
package/schema.json CHANGED
@@ -17,16 +17,48 @@
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
+ },
27
+ "trusted": {
28
+ "type": "array",
29
+ "items": {
30
+ "type": [
31
+ "string",
32
+ "number"
33
+ ],
34
+ "description": "Trusted Slack user id"
35
+ },
36
+ "description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
37
+ },
20
38
  "endpoints": {
21
39
  "type": "array",
22
- "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(name 必填,其余覆盖顶层)",
40
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
23
41
  "items": {
24
42
  "type": "object",
25
43
  "additionalProperties": true,
26
44
  "properties": {
27
- "name": {
28
- "type": "string",
29
- "description": "Slack bot name"
45
+ "master": {
46
+ "type": [
47
+ "string",
48
+ "number"
49
+ ],
50
+ "description": "本 endpoint 的框架 master(Slack user id);覆盖顶层 master"
51
+ },
52
+ "trusted": {
53
+ "type": "array",
54
+ "items": {
55
+ "type": [
56
+ "string",
57
+ "number"
58
+ ],
59
+ "description": "Trusted Slack user id"
60
+ },
61
+ "description": "本 endpoint 的 trusted 列表"
30
62
  },
31
63
  "token": {
32
64
  "type": "string",
@@ -39,10 +71,14 @@
39
71
  "appToken": {
40
72
  "type": "string",
41
73
  "description": "App-Level Token (xapp-...) for Socket Mode"
74
+ },
75
+ "id": {
76
+ "type": "string",
77
+ "description": "Slack bot name"
42
78
  }
43
79
  },
44
80
  "required": [
45
- "name",
81
+ "id",
46
82
  "token"
47
83
  ]
48
84
  }
package/src/endpoint.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  } from '@zhin.js/adapter';
13
13
  import type { MessageGateway } from '@zhin.js/core/runtime';
14
14
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
15
- import { formatCompact, getLogger } from '@zhin.js/logger';
15
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
16
16
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
17
17
  import {
18
18
  formatInboundContent,
@@ -27,7 +27,7 @@ import {
27
27
  type SlackMessageEvent,
28
28
  type SlackSlashCommand,
29
29
  } from './protocol.js';
30
- import { registerSlackAgentEndpoint } from './slack-agent-deps.js';
30
+ import { registerSlackAgentEndpoint, type SlackUserInfo } from './slack-agent-deps.js';
31
31
  import {
32
32
  createSlackInboundFilterState,
33
33
  shouldDropSlackInboundMessage,
@@ -38,8 +38,6 @@ import { postSlackEphemeral } from './slack-response-url.js';
38
38
  import { editSlackContent, sendSlackContent, type SlackChatClient } from './slack-outbound.js';
39
39
  import { registerSlackWebhookRoutes, type SlackWebhookHandler } from './webhook.js';
40
40
 
41
- const logger = getLogger('slack');
42
-
43
41
  export interface SlackSocketLike {
44
42
  on(
45
43
  event: string,
@@ -103,6 +101,8 @@ export interface SlackEndpointOptions {
103
101
  }
104
102
 
105
103
  export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
104
+ readonly #logger!: ReturnType<typeof getAdapterLogger>;
105
+
106
106
  readonly #options: SlackEndpointOptions;
107
107
  readonly #inboundFilter = createSlackInboundFilterState();
108
108
  readonly #messageChannelMap = new Map<string, string>();
@@ -116,12 +116,13 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
116
116
  readonly management: EndpointManagement = createSlackEndpointManagement(this);
117
117
 
118
118
  constructor(options: SlackEndpointOptions) {
119
+ this.#logger = getAdapterLogger('slack', options.config.id);
119
120
  this.#options = options;
120
121
  }
121
122
 
122
123
  /** Console 展示 / AdapterIndex live name(多 endpoint 时与 entry name 一致)。 */
123
124
  get name(): string {
124
- return this.#options.config.name;
125
+ return this.#options.config.id;
125
126
  }
126
127
 
127
128
  get client(): SlackWebClientLike | undefined {
@@ -144,7 +145,7 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
144
145
  this.#client = this.#options.createClient?.(config.token)
145
146
  ?? (new WebClient(config.token) as unknown as SlackWebClientLike);
146
147
 
147
- this.#unregisterAgent = registerSlackAgentEndpoint(config.name, this);
148
+ this.#unregisterAgent = registerSlackAgentEndpoint(config.id, this);
148
149
 
149
150
  if (config.mode === 'socket') {
150
151
  await this.#startSocket();
@@ -154,8 +155,8 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
154
155
  throw new Error('Slack HTTP Events API requires httpHostToken (Runtime Host)');
155
156
  }
156
157
  this.#routeReleases.push(...registerSlackWebhookRoutes(http, this));
157
- logger.debug(formatCompact({
158
- endpoint: config.name,
158
+ this.#logger.debug(formatCompact({
159
+ endpoint: config.id,
159
160
  op: 'webhook',
160
161
  path: config.webhookPath,
161
162
  }));
@@ -164,15 +165,13 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
164
165
  const authTest = await this.#client.auth.test();
165
166
  if (authTest.user_id) this.#botUserId = String(authTest.user_id);
166
167
 
167
- logger.info(formatCompact({
168
- op: 'connect',
169
- endpoint: config.name,
170
- mode: config.mode,
171
- platform_user_id: this.#botUserId,
172
- }));
168
+ this.#logger.info(
169
+ `connected (${config.mode})`
170
+ + (this.#botUserId ? ` | user: ${this.#botUserId}` : ''),
171
+ );
173
172
  } catch (error) {
174
173
  await this.stop();
175
- logger.error('Failed to connect Slack endpoint:', error);
174
+ this.#logger.error('Failed to connect Slack endpoint:', error);
176
175
  throw error;
177
176
  }
178
177
  }
@@ -200,7 +199,7 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
200
199
  this.#unregisterAgent = undefined;
201
200
  this.#client = undefined;
202
201
  this.#started = false;
203
- logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
202
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
204
203
  }
205
204
 
206
205
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
@@ -211,7 +210,7 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
211
210
  this.#client,
212
211
  payload,
213
212
  { channel, threadTs },
214
- logger,
213
+ this.#logger,
215
214
  );
216
215
  if (result.ts) this.trackMessageChannel(result.ts, channel);
217
216
  return formatSlackMessageRef(channel, result.ts || String(Date.now()));
@@ -236,18 +235,17 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
236
235
  conversation,
237
236
  message: { conversation, id: msg.ts },
238
237
  content: formatInboundContent(msg),
239
- sender: msg.user ?? msg.channel,
238
+ sender: { id: msg.user ?? msg.channel ?? '' },
239
+ endpointId: this.#options.config.id,
240
+ ...(msg.type === 'app_mention' ? { mentioned: true } : {}),
240
241
  metadata: Object.freeze({
241
- endpoint: this.#options.config.name,
242
242
  channelType: resolveSlackChannelType(msg),
243
243
  userId: msg.user,
244
244
  threadTs,
245
245
  ts: msg.ts,
246
- // app_mention 事件本身即 @ 机器人;新 Runtime 纯文本 content 需经 metadata 传递
247
- ...(msg.type === 'app_mention' ? { mentioned: true } : {}),
248
246
  }),
249
247
  }).catch((err) => {
250
- logger.warn(formatCompact({
248
+ this.#logger.warn(formatCompact({
251
249
  op: 'slack_gateway_receive_failed',
252
250
  target: `${conversation.kind}:${conversation.id}`,
253
251
  error: err instanceof Error ? err.message : String(err),
@@ -262,7 +260,7 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
262
260
  const userId = payload.user.id;
263
261
  const messageTs = payload.message?.ts ?? '';
264
262
  if (payload.response_url) {
265
- postSlackEphemeral(payload.response_url, '已收到', logger);
263
+ postSlackEphemeral(payload.response_url, '已收到', this.#logger);
266
264
  }
267
265
  const actionTs = payload.actions[0]?.action_ts ?? messageTs ?? `action-${Date.now()}`;
268
266
  const conversation = slackInboundConversation(String(this.#options.id), {
@@ -274,15 +272,15 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
274
272
  conversation,
275
273
  message: { conversation, id: actionTs },
276
274
  content: formatInteractionContent(payload),
277
- sender: userId,
275
+ sender: { id: userId },
276
+ endpointId: this.#options.config.id,
278
277
  metadata: Object.freeze({
279
- endpoint: this.#options.config.name,
280
278
  eventType: 'block_actions',
281
279
  actionId: payload.actions[0]?.action_id,
282
280
  threadTs: messageTs || undefined,
283
281
  }),
284
282
  }).catch((err) => {
285
- logger.warn(formatCompact({
283
+ this.#logger.warn(formatCompact({
286
284
  op: 'slack_gateway_receive_failed',
287
285
  target: `${conversation.kind}:${conversation.id}`,
288
286
  error: err instanceof Error ? err.message : String(err),
@@ -292,7 +290,7 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
292
290
 
293
291
  admitSlashCommand(cmd: SlackSlashCommand): void {
294
292
  if (!this.#open) return;
295
- postSlackEphemeral(cmd.response_url, '处理中…', logger);
293
+ postSlackEphemeral(cmd.response_url, '处理中…', this.#logger);
296
294
  const conversation = slackInboundConversation(String(this.#options.id), {
297
295
  channelId: cmd.channel_id,
298
296
  });
@@ -300,14 +298,14 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
300
298
  conversation,
301
299
  message: { conversation, id: cmd.trigger_id },
302
300
  content: formatSlashContent(cmd),
303
- sender: cmd.user_id,
301
+ sender: { id: cmd.user_id, name: cmd.user_name },
302
+ endpointId: this.#options.config.id,
304
303
  metadata: Object.freeze({
305
- endpoint: this.#options.config.name,
306
304
  eventType: 'slash_command',
307
305
  command: cmd.command,
308
306
  }),
309
307
  }).catch((err) => {
310
- logger.warn(formatCompact({
308
+ this.#logger.warn(formatCompact({
311
309
  op: 'slack_gateway_receive_failed',
312
310
  target: `${conversation.kind}:${conversation.id}`,
313
311
  error: err instanceof Error ? err.message : String(err),
@@ -403,9 +401,9 @@ export class SlackEndpoint implements EndpointInstance, SlackWebhookHandler {
403
401
  return result.channel;
404
402
  }
405
403
 
406
- async getUserInfo(user: string): Promise<unknown> {
404
+ async getUserInfo(user: string): Promise<SlackUserInfo | undefined> {
407
405
  const result = await this.#client!.users.info({ user });
408
- return result.user;
406
+ return result.user as SlackUserInfo | undefined;
409
407
  }
410
408
 
411
409
  async addReaction(channel: string, timestamp: string, name: string): Promise<boolean> {
package/src/index.ts CHANGED
@@ -48,7 +48,6 @@ export {
48
48
  checkSlackPlatformPermit,
49
49
  normalizeSlackSenderForPermit,
50
50
  platformPermit,
51
- registerSlackPlatformPermitChecker,
52
51
  slackGroupPermitResolver,
53
52
  } from './platform-permit.js';
54
53
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Slack platform permit — workspace + channel 权限
3
3
  */
4
- import { registerPlatformPermitChecker, type Message } from '@zhin.js/core';
4
+ import type { PermissionSubject } from '@zhin.js/permission';
5
5
 
6
6
  const ADAPTER = 'slack';
7
7
 
@@ -39,10 +39,9 @@ export function normalizeSlackSenderForPermit(input: {
39
39
  return { role: 'member', permissions };
40
40
  }
41
41
 
42
- export function checkSlackPlatformPermit(perm: string, message: Message<any>): boolean {
43
- const sender = message.$sender as { role?: string; permissions?: string[] };
44
- const permissions = sender.permissions ?? [];
45
- const role = sender.role;
42
+ export function checkSlackPlatformPermit(perm: string, subject: PermissionSubject): boolean {
43
+ const role = subject.sender?.role?.[0];
44
+ const permissions = subject.sender?.permissions ?? [];
46
45
  const has = (t: string) => permissions.includes(t);
47
46
 
48
47
  switch (perm) {
@@ -58,6 +57,3 @@ export function checkSlackPlatformPermit(perm: string, message: Message<any>): b
58
57
  }
59
58
  }
60
59
 
61
- export function registerSlackPlatformPermitChecker(): () => void {
62
- return registerPlatformPermitChecker(ADAPTER, checkSlackPlatformPermit);
63
- }
package/src/protocol.ts CHANGED
@@ -21,7 +21,7 @@ const MAX_TIMESTAMP_DRIFT_SECONDS = 300;
21
21
 
22
22
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
23
23
  export interface SlackAdapterConfig {
24
- readonly name?: string;
24
+ readonly id?: string;
25
25
  readonly token?: string;
26
26
  readonly signingSecret?: string;
27
27
  readonly appToken?: string;
@@ -42,7 +42,7 @@ export interface SlackAdapterConfig {
42
42
 
43
43
  export interface ResolvedSlackConfig {
44
44
  readonly context: 'slack';
45
- readonly name: string;
45
+ readonly id: string;
46
46
  readonly token: string;
47
47
  readonly mode: 'socket' | 'http';
48
48
  readonly signingSecret: string;
@@ -139,8 +139,8 @@ export function resolveSlackConfig(config: SlackAdapterConfig = {}): ResolvedSla
139
139
  );
140
140
  }
141
141
 
142
- const name = (typeof config.name === 'string' && config.name)
143
- || (typeof entry?.name === 'string' && entry.name)
142
+ const id = (typeof config.id === 'string' && config.id)
143
+ || (typeof entry?.id === 'string' && entry.id)
144
144
  || process.env.SLACK_BOT_NAME
145
145
  || 'slack-bot';
146
146
 
@@ -170,7 +170,7 @@ export function resolveSlackConfig(config: SlackAdapterConfig = {}): ResolvedSla
170
170
 
171
171
  return {
172
172
  context: 'slack',
173
- name,
173
+ id,
174
174
  token,
175
175
  mode,
176
176
  signingSecret,
@@ -199,7 +199,7 @@ export function resolveSlackChannelType(event: Pick<SlackEvent, 'channel_type'>)
199
199
  * guild 容器概念(team 为 workspace 级,不进 parent);线程根 ts 进 `threadId`。
200
200
  */
201
201
  export function slackInboundConversation(
202
- endpointId: string,
202
+ endpointKey: string,
203
203
  opts: {
204
204
  readonly channelId: string;
205
205
  readonly channelType?: string;
@@ -210,7 +210,7 @@ export function slackInboundConversation(
210
210
  ? (opts.channelType === 'im' ? 'private' : 'group')
211
211
  : (opts.channelId.startsWith('D') ? 'private' : 'group');
212
212
  return {
213
- endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
213
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
214
214
  kind,
215
215
  id: opts.channelId,
216
216
  ...(opts.threadId ? { threadId: opts.threadId } : {}),
@@ -13,7 +13,7 @@ export interface SlackAgentEndpoint {
13
13
  renameChannel(channel: string, name: string): Promise<boolean>;
14
14
  getChannelMembers(channel: string): Promise<string[]>;
15
15
  getChannelInfo(channel: string): Promise<unknown>;
16
- getUserInfo(user: string): Promise<unknown>;
16
+ getUserInfo(user: string): Promise<SlackUserInfo | undefined>;
17
17
  addReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
18
18
  removeReaction(channel: string, timestamp: string, name: string): Promise<boolean>;
19
19
  pinMessage(channel: string, timestamp: string): Promise<boolean>;
@@ -21,21 +21,35 @@ export interface SlackAgentEndpoint {
21
21
  editMessage(channel: string, messageTs: string, content: unknown): Promise<void>;
22
22
  }
23
23
 
24
+ /** Slack users.info 返回的用户对象(SDK 响应字段对 agent 工具的投影)。 */
25
+ export interface SlackUserInfo {
26
+ id?: string;
27
+ name?: string;
28
+ real_name?: string;
29
+ is_admin?: boolean;
30
+ is_bot?: boolean;
31
+ profile?: {
32
+ display_name?: string;
33
+ email?: string;
34
+ status_text?: string;
35
+ };
36
+ }
37
+
24
38
  export interface SlackAgentDeps {
25
- getEndpoint: (endpointId: string) => SlackAgentEndpoint;
39
+ getEndpoint: (endpointKey: string) => SlackAgentEndpoint;
26
40
  }
27
41
 
28
42
  const endpoints = new Map<string, SlackAgentEndpoint>();
29
43
  let override: SlackAgentDeps | null = null;
30
44
 
31
45
  export function registerSlackAgentEndpoint(
32
- endpointId: string,
46
+ endpointKey: string,
33
47
  endpoint: SlackAgentEndpoint,
34
48
  ): () => void {
35
- endpoints.set(endpointId, endpoint);
49
+ endpoints.set(endpointKey, endpoint);
36
50
  return () => {
37
- if (endpoints.get(endpointId) === endpoint) {
38
- endpoints.delete(endpointId);
51
+ if (endpoints.get(endpointKey) === endpoint) {
52
+ endpoints.delete(endpointKey);
39
53
  }
40
54
  };
41
55
  }
@@ -48,9 +62,9 @@ export function setSlackAgentDeps(deps: SlackAgentDeps | null): void {
48
62
  export function getSlackAgentDeps(): SlackAgentDeps {
49
63
  if (override) return override;
50
64
  return {
51
- getEndpoint(endpointId: string): SlackAgentEndpoint {
52
- const registered = endpoints.get(endpointId);
53
- if (!registered) throw new Error(`Endpoint ${endpointId} 不存在`);
65
+ getEndpoint(endpointKey: string): SlackAgentEndpoint {
66
+ const registered = endpoints.get(endpointKey);
67
+ if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
54
68
  return registered;
55
69
  },
56
70
  };
File without changes
File without changes
File without changes
File without changes