@zhin.js/adapter-slack 1.1.0 → 1.1.3

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 (55) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +13 -14
  3. package/adapters/{slack.js → slack/index.js} +6 -6
  4. package/adapters/{slack.ts → slack/index.ts} +9 -9
  5. package/agents/slack/agent.json +20 -0
  6. package/agents/slack/boundaries.md +3 -0
  7. package/agents/slack/conventions.md +3 -0
  8. package/agents/slack/skills/slack-channels/SKILL.md +24 -0
  9. package/agents/slack/skills/slack-channels/tools/archive_channel/index.js +19 -0
  10. package/{agent/tools/archive_channel.ts → agents/slack/skills/slack-channels/tools/archive_channel/index.ts} +3 -3
  11. package/agents/slack/skills/slack-channels/tools/invite_to_channel/index.js +20 -0
  12. package/{agent/tools/invite_to_channel.ts → agents/slack/skills/slack-channels/tools/invite_to_channel/index.ts} +3 -3
  13. package/agents/slack/skills/slack-channels/tools/set_purpose/index.js +20 -0
  14. package/{agent/tools/set_purpose.ts → agents/slack/skills/slack-channels/tools/set_purpose/index.ts} +3 -3
  15. package/agents/slack/skills/slack-channels/tools/set_topic/index.js +20 -0
  16. package/{agent/tools/set_topic.ts → agents/slack/skills/slack-channels/tools/set_topic/index.ts} +3 -3
  17. package/agents/slack/skills/slack-channels/tools/unarchive/index.js +19 -0
  18. package/{agent/tools/unarchive.ts → agents/slack/skills/slack-channels/tools/unarchive/index.ts} +3 -3
  19. package/agents/slack/skills/slack-channels/tools/user_info/index.js +27 -0
  20. package/{agent/tools/user_info.ts → agents/slack/skills/slack-channels/tools/user_info/index.ts} +2 -2
  21. package/agents/slack/skills/slack-messages/SKILL.md +22 -0
  22. package/agents/slack/skills/slack-messages/tools/add_reaction/index.js +27 -0
  23. package/{agent/tools/add_reaction.ts → agents/slack/skills/slack-messages/tools/add_reaction/index.ts} +2 -2
  24. package/agents/slack/skills/slack-messages/tools/edit_message/index.js +21 -0
  25. package/{agent/tools/edit_message.ts → agents/slack/skills/slack-messages/tools/edit_message/index.ts} +2 -2
  26. package/agents/slack/skills/slack-messages/tools/pin_message/index.js +20 -0
  27. package/{agent/tools/pin_message.ts → agents/slack/skills/slack-messages/tools/pin_message/index.ts} +3 -3
  28. package/agents/slack/skills/slack-messages/tools/remove_reaction/index.js +30 -0
  29. package/{agent/tools/remove_reaction.ts → agents/slack/skills/slack-messages/tools/remove_reaction/index.ts} +2 -2
  30. package/agents/slack/skills/slack-messages/tools/unpin_message/index.js +20 -0
  31. package/{agent/tools/unpin_message.ts → agents/slack/skills/slack-messages/tools/unpin_message/index.ts} +3 -3
  32. package/agents/slack/system.md +3 -0
  33. package/commands/{endpoint/add/[id].js → slack/endpoint/add/[id]/index.js} +1 -1
  34. package/commands/slack/endpoint/add/[id]/index.ts +3 -0
  35. package/{lib/slack-endpoint-commands.js → commands/slack/endpoint/definition.js} +4 -3
  36. package/{src/slack-endpoint-commands.ts → commands/slack/endpoint/definition.ts} +3 -3
  37. package/commands/{endpoint/list.js → slack/endpoint/list/index.js} +1 -1
  38. package/commands/slack/endpoint/list/index.ts +3 -0
  39. package/commands/{endpoint/remove/[id].js → slack/endpoint/remove/[id]/index.js} +1 -1
  40. package/commands/slack/endpoint/remove/[id]/index.ts +3 -0
  41. package/lib/index.d.ts +1 -1
  42. package/lib/protocol.d.ts +6 -15
  43. package/lib/protocol.js +19 -33
  44. package/lib/slack-runtime-state.js +1 -1
  45. package/package.json +33 -21
  46. package/schema.json +14 -1
  47. package/src/index.ts +1 -1
  48. package/src/protocol.ts +27 -56
  49. package/src/slack-runtime-state.ts +1 -1
  50. package/agent/skills/slack.md +0 -93
  51. package/commands/endpoint/add/[id].ts +0 -3
  52. package/commands/endpoint/list.ts +0 -3
  53. package/commands/endpoint/remove/[id].ts +0 -3
  54. package/lib/slack-endpoint-commands.d.ts +0 -1
  55. /package/{agent/PERMITS.md → PERMITS.md} +0 -0
@@ -0,0 +1,20 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { platformPermission } from '@zhin.js/permission';
5
+ export default defineAgentTool({
6
+ description: '置顶 Slack 消息',
7
+ inputSchema: z.object({
8
+ channel: z.string().describe('频道 ID'),
9
+ timestamp: z.string().describe('消息时间戳'),
10
+ }),
11
+ adapter: 'slack',
12
+ tags: ['slack'],
13
+ permissions: [platformPermission('slack', 'channel_manager')],
14
+ async execute({ channel, timestamp }, context) {
15
+ const client = context.$client;
16
+ await client.pins.add({ channel, timestamp });
17
+ const success = true;
18
+ return { success, message: success ? '已置顶消息' : '操作失败' };
19
+ },
20
+ });
@@ -1,6 +1,6 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
- import { platformPermit } from '../../src/platform-permit.js';
3
+ import { platformPermission } from '@zhin.js/permission';
4
4
 
5
5
  export default defineAgentTool<{
6
6
  channel: string;
@@ -13,7 +13,7 @@ export default defineAgentTool<{
13
13
  }),
14
14
  adapter: 'slack',
15
15
  tags: ['slack'],
16
- permissions: [platformPermit('channel_manager')],
16
+ permissions: [platformPermission('slack', 'channel_manager')],
17
17
  async execute({ channel, timestamp }, context) {
18
18
  const client = context.$client;
19
19
  await client.pins.add({ channel, timestamp });
@@ -0,0 +1,30 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { normalizeSlackReactionName } from '@zhin.js/adapter-slack';
5
+ export default defineAgentTool({
6
+ description: '移除 Slack 消息上的表情反应',
7
+ inputSchema: z.object({
8
+ channel_id: z.string().describe('频道 ID'),
9
+ timestamp: z.string().describe('消息时间戳'),
10
+ name: z.string().describe('表情名称(如 thumbsup、heart)'),
11
+ }),
12
+ adapter: 'slack',
13
+ tags: ['slack'],
14
+ async execute({ channel_id, timestamp, name }, context) {
15
+ const client = context.$client;
16
+ try {
17
+ await client.reactions.remove({
18
+ channel: channel_id,
19
+ timestamp,
20
+ name: normalizeSlackReactionName(name),
21
+ });
22
+ }
23
+ catch (error) {
24
+ if (error?.data?.error !== 'no_reaction')
25
+ throw error;
26
+ }
27
+ const success = true;
28
+ return { success, message: success ? `已移除反应 :${name}:` : '操作失败' };
29
+ },
30
+ });
@@ -1,6 +1,6 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
- import { normalizeSlackReactionName } from '../../src/slack-reaction.js';
3
+ import { normalizeSlackReactionName } from '@zhin.js/adapter-slack';
4
4
 
5
5
  export default defineAgentTool<{
6
6
  channel_id: string;
@@ -0,0 +1,20 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { platformPermission } from '@zhin.js/permission';
5
+ export default defineAgentTool({
6
+ description: '取消 Slack 频道中消息的置顶',
7
+ inputSchema: z.object({
8
+ channel_id: z.string().describe('频道 ID'),
9
+ timestamp: z.string().describe('消息时间戳'),
10
+ }),
11
+ adapter: 'slack',
12
+ tags: ['slack'],
13
+ permissions: [platformPermission('slack', 'channel_manager')],
14
+ async execute({ channel_id, timestamp }, context) {
15
+ const client = context.$client;
16
+ await client.pins.remove({ channel: channel_id, timestamp });
17
+ const success = true;
18
+ return { success, message: success ? '已取消置顶' : '操作失败' };
19
+ },
20
+ });
@@ -1,6 +1,6 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
- import { platformPermit } from '../../src/platform-permit.js';
3
+ import { platformPermission } from '@zhin.js/permission';
4
4
 
5
5
  export default defineAgentTool<{
6
6
  channel_id: string;
@@ -13,7 +13,7 @@ export default defineAgentTool<{
13
13
  }),
14
14
  adapter: 'slack',
15
15
  tags: ['slack'],
16
- permissions: [platformPermit('channel_manager')],
16
+ permissions: [platformPermission('slack', 'channel_manager')],
17
17
  async execute({ channel_id, timestamp }, context) {
18
18
  const client = context.$client;
19
19
  await client.pins.remove({ channel: channel_id, timestamp });
@@ -0,0 +1,3 @@
1
+ # SLACK Platform Agent
2
+
3
+ You are the slack platform specialist. Interpret requests in the current slack conversation and load only the narrowest private Skill needed for the task.
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { slackEndpointCommands } from "../../../lib/slack-endpoint-commands.js";
2
+ import { slackEndpointCommands } from '../../definition.js';
3
3
  export default slackEndpointCommands.add;
@@ -0,0 +1,3 @@
1
+ import { slackEndpointCommands } from '../../definition.js';
2
+
3
+ export default slackEndpointCommands.add;
@@ -1,10 +1,11 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
1
2
  /**
2
- * `slack.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
3
+ * `slack endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
4
+ * commands/slack/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
5
  */
5
6
  import { createEndpointCommands } from 'zhin.js/adapter';
6
7
  import { defineCommand } from 'zhin.js/command';
7
- import { slackRuntimeStateToken } from './slack-runtime-state.js';
8
+ import { slackRuntimeStateToken } from "../../../lib/slack-runtime-state.js";
8
9
  export const slackEndpointCommands = createEndpointCommands({
9
10
  adapterKey: 'slack',
10
11
  adapterDisplayName: 'Slack',
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `slack.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
2
+ * `slack endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/slack/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
4
  */
5
5
  import { createEndpointCommands } from 'zhin.js/adapter';
6
6
  import { defineCommand } from 'zhin.js/command';
7
- import { slackRuntimeStateToken } from './slack-runtime-state.js';
7
+ import { slackRuntimeStateToken } from '../../../src/slack-runtime-state.js';
8
8
 
9
9
  export const slackEndpointCommands = createEndpointCommands({
10
10
  adapterKey: 'slack',
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { slackEndpointCommands } from "../../lib/slack-endpoint-commands.js";
2
+ import { slackEndpointCommands } from '../definition.js';
3
3
  export default slackEndpointCommands.list;
@@ -0,0 +1,3 @@
1
+ import { slackEndpointCommands } from '../definition.js';
2
+
3
+ export default slackEndpointCommands.list;
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { slackEndpointCommands } from "../../../lib/slack-endpoint-commands.js";
2
+ import { slackEndpointCommands } from '../../definition.js';
3
3
  export default slackEndpointCommands.remove;
@@ -0,0 +1,3 @@
1
+ import { slackEndpointCommands } from '../../definition.js';
2
+
3
+ export default slackEndpointCommands.remove;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { formatInboundContent, formatInteractionContent, formatOutboundWire, formatSlashContent, headerValue, keyboardToBlockKitBlocks, normalizeWebhookPath, readTextBody, resolveSlackChannelType, resolveSlackConfig, slackInboundConversation, verifySlackSignature, type ResolvedSlackConfig, type SlackAdapterConfig, type SlackBlockAction, type SlackEvent, type SlackEventEnvelope, type SlackInteractionPayload, type SlackMessageEvent, type SlackSlashCommand, type SlackUrlVerification, type SlackWireSegment, } from './protocol.js';
1
+ export { formatInboundContent, formatInteractionContent, formatOutboundWire, formatSlashContent, headerValue, keyboardToBlockKitBlocks, normalizeWebhookPath, readTextBody, resolveSlackChannelType, resolveSlackConfig, slackInboundConversation, verifySlackSignature, type ResolvedSlackConfig, type SlackEndpointConfig, type SlackBlockAction, type SlackEvent, type SlackEventEnvelope, type SlackInteractionPayload, type SlackMessageEvent, type SlackSlashCommand, type SlackUrlVerification, type SlackWireSegment, } from './protocol.js';
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 { slackClient, type SlackClientEventMap, type SlackUserInfo, } from './client.js';
package/lib/protocol.d.ts CHANGED
@@ -1,24 +1,15 @@
1
1
  import type { IncomingMessage } from 'node:http';
2
- import type { ConversationRef } from '@zhin.js/im-contract';
3
- /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
4
- export interface SlackAdapterConfig {
5
- readonly id?: string;
6
- readonly token?: string;
2
+ import { type ConversationRef } from '@zhin.js/im-contract';
3
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
4
+ export interface SlackEndpointConfig {
5
+ readonly id: string;
6
+ readonly token: string;
7
7
  readonly signingSecret?: string;
8
8
  readonly appToken?: string;
9
9
  /** Default true (Socket Mode). Set false for HTTP Events API via httpHostToken. */
10
10
  readonly socketMode?: boolean;
11
11
  readonly webhookPath?: string;
12
12
  readonly clientPingTimeout?: number;
13
- /** Transitional: legacy root `endpoints[]` with `context: slack`. */
14
- readonly endpoints?: ReadonlyArray<Partial<ResolvedSlackConfig> & {
15
- readonly context?: string;
16
- readonly socketMode?: boolean;
17
- readonly signingSecret?: string;
18
- readonly appToken?: string;
19
- readonly webhookPath?: string;
20
- readonly clientPingTimeout?: number;
21
- }>;
22
13
  }
23
14
  export interface ResolvedSlackConfig {
24
15
  readonly context: 'slack';
@@ -113,7 +104,7 @@ export interface SlackWireSegment {
113
104
  readonly type: string;
114
105
  readonly data?: Record<string, unknown>;
115
106
  }
116
- export declare function resolveSlackConfig(config?: SlackAdapterConfig): ResolvedSlackConfig;
107
+ export declare function resolveSlackConfig(config: SlackEndpointConfig): ResolvedSlackConfig;
117
108
  export declare function normalizeWebhookPath(path: string): string;
118
109
  export declare function resolveSlackChannelType(event: Pick<SlackEvent, 'channel_type'>): 'private' | 'group';
119
110
  /**
package/lib/protocol.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Canonicalization is owned by gateway/core before endpoint.send.
4
4
  */
5
5
  import { createHmac, timingSafeEqual } from 'node:crypto';
6
- import { isMediaRef } from '@zhin.js/core';
6
+ import { isMediaRef } from '@zhin.js/im-contract';
7
7
  import { formatCompact, getLogger } from '@zhin.js/logger';
8
8
  import { mrkdwnToMarkdown } from './mrkdwn-to-markdown.js';
9
9
  const logger = getLogger('slack');
@@ -13,36 +13,18 @@ function dropOutboundMedia(type, reason) {
13
13
  }
14
14
  const SLACK_SIG_VERSION = 'v0';
15
15
  const MAX_TIMESTAMP_DRIFT_SECONDS = 300;
16
- export function resolveSlackConfig(config = {}) {
17
- const entry = config.endpoints?.find((item) => item.context === 'slack');
18
- const token = config.token
19
- ?? entry?.token
20
- ?? process.env.SLACK_BOT_TOKEN
21
- ?? process.env.SLACK_TOKEN;
22
- if (!token) {
23
- throw new TypeError('Slack adapter requires token (plugins.<key>.token or endpoints with context: slack)');
24
- }
25
- const id = (typeof config.id === 'string' && config.id)
26
- || (typeof entry?.id === 'string' && entry.id)
27
- || process.env.SLACK_BOT_NAME
28
- || 'slack-bot';
29
- const socketMode = config.socketMode ?? entry?.socketMode;
16
+ export function resolveSlackConfig(config) {
17
+ const id = requiredEndpointField(config.id, 'id');
18
+ const token = requiredEndpointField(config.token, 'token');
19
+ const socketMode = config.socketMode;
30
20
  // Prefer Socket Mode (default true) — no public URL required.
31
21
  const mode = socketMode === false ? 'http' : 'socket';
32
- const signingSecret = config.signingSecret
33
- ?? entry?.signingSecret
34
- ?? process.env.SLACK_SIGNING_SECRET
35
- ?? '';
36
- const appToken = config.appToken
37
- ?? entry?.appToken
38
- ?? process.env.SLACK_APP_TOKEN
39
- ?? undefined;
40
- if (mode === 'socket' && !appToken) {
41
- throw new TypeError('Slack Socket Mode requires appToken (xapp-...); set socketMode: false for HTTP Events API');
42
- }
43
- if (mode === 'http' && !signingSecret) {
44
- throw new TypeError('Slack HTTP Events API requires signingSecret');
45
- }
22
+ const signingSecret = mode === 'http'
23
+ ? requiredEndpointField(config.signingSecret, 'signingSecret')
24
+ : config.signingSecret?.trim() ?? '';
25
+ const appToken = mode === 'socket'
26
+ ? requiredEndpointField(config.appToken, 'appToken')
27
+ : config.appToken?.trim();
46
28
  return {
47
29
  context: 'slack',
48
30
  id,
@@ -50,12 +32,16 @@ export function resolveSlackConfig(config = {}) {
50
32
  mode,
51
33
  signingSecret,
52
34
  appToken,
53
- webhookPath: normalizeWebhookPath(config.webhookPath ?? entry?.webhookPath ?? '/slack/events'),
54
- clientPingTimeout: config.clientPingTimeout
55
- ?? entry?.clientPingTimeout
56
- ?? 15_000,
35
+ webhookPath: normalizeWebhookPath(config.webhookPath ?? '/slack/events'),
36
+ clientPingTimeout: config.clientPingTimeout ?? 15_000,
57
37
  };
58
38
  }
39
+ function requiredEndpointField(value, field) {
40
+ if (typeof value !== 'string' || !value.trim()) {
41
+ throw new TypeError(`Slack endpoint requires a non-empty ${field}`);
42
+ }
43
+ return value.trim();
44
+ }
59
45
  export function normalizeWebhookPath(path) {
60
46
  const trimmed = path.trim() || '/slack/events';
61
47
  return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Slack 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `slack.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `slack endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6
  export const slackRuntimeStateToken = defineEndpointRuntimeStateToken('slack');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-slack",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
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,27 +34,26 @@
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.12",
38
- "@zhin.js/core": "1.1.35",
39
- "@zhin.js/feature-kit": "1.1.0",
40
- "@zhin.js/host-http": "1.1.0",
41
- "@zhin.js/im-contract": "1.1.0",
42
- "@zhin.js/logger": "1.1.0"
37
+ "@zhin.js/adapter": "1.1.15",
38
+ "@zhin.js/agent-feature": "1.1.2",
39
+ "@zhin.js/core": "1.1.38",
40
+ "@zhin.js/feature-kit": "1.1.2",
41
+ "@zhin.js/host-http": "1.1.2",
42
+ "@zhin.js/im-contract": "1.1.2",
43
+ "@zhin.js/logger": "1.1.1",
44
+ "@zhin.js/skill": "1.1.2",
45
+ "@zhin.js/tool": "1.1.2"
43
46
  },
44
47
  "peerDependencies": {
45
48
  "zod": "^4.0.0",
46
- "@zhin.js/adapter": "^1.1.12",
47
- "@zhin.js/agent": "^1.1.23",
48
- "@zhin.js/command": "^1.1.0",
49
- "@zhin.js/core": "^1.1.35",
50
- "@zhin.js/host-http": "^1.1.0",
51
- "@zhin.js/permission": "^1.1.0",
52
- "zhin.js": "^1.1.0"
49
+ "@zhin.js/adapter": "^1.1.15",
50
+ "@zhin.js/command": "^1.1.2",
51
+ "@zhin.js/core": "^1.1.38",
52
+ "@zhin.js/host-http": "^1.1.2",
53
+ "@zhin.js/permission": "^1.1.2",
54
+ "zhin.js": "^1.1.3"
53
55
  },
54
56
  "peerDependenciesMeta": {
55
- "@zhin.js/agent": {
56
- "optional": true
57
- },
58
57
  "@zhin.js/command": {
59
58
  "optional": true
60
59
  },
@@ -70,8 +69,7 @@
70
69
  "typescript": "^6.0.3",
71
70
  "vitest": "^4.1.10",
72
71
  "zod": "^4.4.3",
73
- "@zhin.js/agent": "1.1.23",
74
- "zhin.js": "1.1.0"
72
+ "zhin.js": "1.1.3"
75
73
  },
76
74
  "files": [
77
75
  "adapters",
@@ -80,9 +78,11 @@
80
78
  "schema.json",
81
79
  "src",
82
80
  "lib",
83
- "agent",
81
+ "tools",
82
+ "agents",
84
83
  "README.md",
85
- "CHANGELOG.md"
84
+ "CHANGELOG.md",
85
+ "PERMITS.md"
86
86
  ],
87
87
  "publishConfig": {
88
88
  "access": "public",
@@ -105,6 +105,18 @@
105
105
  {
106
106
  "package": "@zhin.js/command",
107
107
  "api": "^1.0.0"
108
+ },
109
+ {
110
+ "package": "@zhin.js/tool",
111
+ "api": "^1.0.0"
112
+ },
113
+ {
114
+ "package": "@zhin.js/skill",
115
+ "api": "^1.0.0"
116
+ },
117
+ {
118
+ "package": "@zhin.js/agent-feature",
119
+ "api": "^1.0.0"
108
120
  }
109
121
  ],
110
122
  "plugins": []
package/schema.json CHANGED
@@ -37,10 +37,11 @@
37
37
  },
38
38
  "endpoints": {
39
39
  "type": "array",
40
+ "minItems": 1,
40
41
  "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
41
42
  "items": {
42
43
  "type": "object",
43
- "additionalProperties": true,
44
+ "additionalProperties": false,
44
45
  "properties": {
45
46
  "master": {
46
47
  "type": [
@@ -72,6 +73,18 @@
72
73
  "type": "string",
73
74
  "description": "App-Level Token (xapp-...) for Socket Mode"
74
75
  },
76
+ "socketMode": {
77
+ "type": "boolean"
78
+ },
79
+ "webhookPath": {
80
+ "type": "string"
81
+ },
82
+ "clientPingTimeout": {
83
+ "type": "number"
84
+ },
85
+ "commandPrefix": {
86
+ "type": "string"
87
+ },
75
88
  "id": {
76
89
  "type": "string",
77
90
  "description": "Slack bot name"
package/src/index.ts CHANGED
@@ -12,7 +12,7 @@ export {
12
12
  slackInboundConversation,
13
13
  verifySlackSignature,
14
14
  type ResolvedSlackConfig,
15
- type SlackAdapterConfig,
15
+ type SlackEndpointConfig,
16
16
  type SlackBlockAction,
17
17
  type SlackEvent,
18
18
  type SlackEventEnvelope,
package/src/protocol.ts CHANGED
@@ -4,8 +4,7 @@
4
4
  */
5
5
  import { createHmac, timingSafeEqual } from 'node:crypto';
6
6
  import type { IncomingMessage } from 'node:http';
7
- import { isMediaRef, type MediaRef } from '@zhin.js/core';
8
- import type { ConversationKind, ConversationRef } from '@zhin.js/im-contract';
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';
11
10
 
@@ -19,25 +18,16 @@ function dropOutboundMedia(type: string, reason: string): void {
19
18
  const SLACK_SIG_VERSION = 'v0';
20
19
  const MAX_TIMESTAMP_DRIFT_SECONDS = 300;
21
20
 
22
- /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
23
- export interface SlackAdapterConfig {
24
- readonly id?: string;
25
- readonly token?: string;
21
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
22
+ export interface SlackEndpointConfig {
23
+ readonly id: string;
24
+ readonly token: string;
26
25
  readonly signingSecret?: string;
27
26
  readonly appToken?: string;
28
27
  /** Default true (Socket Mode). Set false for HTTP Events API via httpHostToken. */
29
28
  readonly socketMode?: boolean;
30
29
  readonly webhookPath?: string;
31
30
  readonly clientPingTimeout?: number;
32
- /** Transitional: legacy root `endpoints[]` with `context: slack`. */
33
- readonly endpoints?: ReadonlyArray<Partial<ResolvedSlackConfig> & {
34
- readonly context?: string;
35
- readonly socketMode?: boolean;
36
- readonly signingSecret?: string;
37
- readonly appToken?: string;
38
- readonly webhookPath?: string;
39
- readonly clientPingTimeout?: number;
40
- }>;
41
31
  }
42
32
 
43
33
  export interface ResolvedSlackConfig {
@@ -127,46 +117,19 @@ export interface SlackWireSegment {
127
117
  readonly data?: Record<string, unknown>;
128
118
  }
129
119
 
130
- export function resolveSlackConfig(config: SlackAdapterConfig = {}): ResolvedSlackConfig {
131
- const entry = config.endpoints?.find((item) => item.context === 'slack');
132
- const token = config.token
133
- ?? entry?.token
134
- ?? process.env.SLACK_BOT_TOKEN
135
- ?? process.env.SLACK_TOKEN;
136
- if (!token) {
137
- throw new TypeError(
138
- 'Slack adapter requires token (plugins.<key>.token or endpoints with context: slack)',
139
- );
140
- }
141
-
142
- const id = (typeof config.id === 'string' && config.id)
143
- || (typeof entry?.id === 'string' && entry.id)
144
- || process.env.SLACK_BOT_NAME
145
- || 'slack-bot';
146
-
147
- const socketMode = config.socketMode ?? entry?.socketMode;
120
+ export function resolveSlackConfig(config: SlackEndpointConfig): ResolvedSlackConfig {
121
+ const id = requiredEndpointField(config.id, 'id');
122
+ const token = requiredEndpointField(config.token, 'token');
123
+ const socketMode = config.socketMode;
148
124
  // Prefer Socket Mode (default true) — no public URL required.
149
125
  const mode: 'socket' | 'http' = socketMode === false ? 'http' : 'socket';
150
126
 
151
- const signingSecret = config.signingSecret
152
- ?? entry?.signingSecret
153
- ?? process.env.SLACK_SIGNING_SECRET
154
- ?? '';
155
- const appToken = config.appToken
156
- ?? entry?.appToken
157
- ?? process.env.SLACK_APP_TOKEN
158
- ?? undefined;
159
-
160
- if (mode === 'socket' && !appToken) {
161
- throw new TypeError(
162
- 'Slack Socket Mode requires appToken (xapp-...); set socketMode: false for HTTP Events API',
163
- );
164
- }
165
- if (mode === 'http' && !signingSecret) {
166
- throw new TypeError(
167
- 'Slack HTTP Events API requires signingSecret',
168
- );
169
- }
127
+ const signingSecret = mode === 'http'
128
+ ? requiredEndpointField(config.signingSecret, 'signingSecret')
129
+ : config.signingSecret?.trim() ?? '';
130
+ const appToken = mode === 'socket'
131
+ ? requiredEndpointField(config.appToken, 'appToken')
132
+ : config.appToken?.trim();
170
133
 
171
134
  return {
172
135
  context: 'slack',
@@ -176,14 +139,22 @@ export function resolveSlackConfig(config: SlackAdapterConfig = {}): ResolvedSla
176
139
  signingSecret,
177
140
  appToken,
178
141
  webhookPath: normalizeWebhookPath(
179
- config.webhookPath ?? entry?.webhookPath ?? '/slack/events',
142
+ config.webhookPath ?? '/slack/events',
180
143
  ),
181
- clientPingTimeout: config.clientPingTimeout
182
- ?? entry?.clientPingTimeout
183
- ?? 15_000,
144
+ clientPingTimeout: config.clientPingTimeout ?? 15_000,
184
145
  };
185
146
  }
186
147
 
148
+ function requiredEndpointField(
149
+ value: unknown,
150
+ field: 'id' | 'token' | 'signingSecret' | 'appToken',
151
+ ): string {
152
+ if (typeof value !== 'string' || !value.trim()) {
153
+ throw new TypeError(`Slack endpoint requires a non-empty ${field}`);
154
+ }
155
+ return value.trim();
156
+ }
157
+
187
158
  export function normalizeWebhookPath(path: string): string {
188
159
  const trimmed = path.trim() || '/slack/events';
189
160
  return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Slack 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `slack.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `slack endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6