@zhin.js/adapter-napcat 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 (108) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +11 -8
  3. package/adapters/{napcat.js → napcat/index.js} +10 -10
  4. package/adapters/{napcat.ts → napcat/index.ts} +13 -13
  5. package/agents/napcat/agent.json +20 -0
  6. package/agents/napcat/boundaries.md +3 -0
  7. package/agents/napcat/conventions.md +3 -0
  8. package/agents/napcat/skills/napcat-account/SKILL.md +25 -0
  9. package/agents/napcat/skills/napcat-account/tools/delete_friend/index.js +18 -0
  10. package/{agent/tools/delete_friend.ts → agents/napcat/skills/napcat-account/tools/delete_friend/index.ts} +1 -1
  11. package/agents/napcat/skills/napcat-account/tools/get_user_status/index.js +16 -0
  12. package/{agent/tools/get_user_status.ts → agents/napcat/skills/napcat-account/tools/get_user_status/index.ts} +1 -1
  13. package/agents/napcat/skills/napcat-account/tools/send_like/index.js +18 -0
  14. package/{agent/tools/send_like.ts → agents/napcat/skills/napcat-account/tools/send_like/index.ts} +1 -1
  15. package/agents/napcat/skills/napcat-account/tools/set_avatar/index.js +17 -0
  16. package/{agent/tools/set_avatar.ts → agents/napcat/skills/napcat-account/tools/set_avatar/index.ts} +1 -1
  17. package/agents/napcat/skills/napcat-account/tools/set_online_status/index.js +18 -0
  18. package/{agent/tools/set_online_status.ts → agents/napcat/skills/napcat-account/tools/set_online_status/index.ts} +1 -1
  19. package/agents/napcat/skills/napcat-account/tools/set_profile/index.js +21 -0
  20. package/{agent/tools/set_profile.ts → agents/napcat/skills/napcat-account/tools/set_profile/index.ts} +1 -1
  21. package/agents/napcat/skills/napcat-account/tools/set_signature/index.js +17 -0
  22. package/{agent/tools/set_signature.ts → agents/napcat/skills/napcat-account/tools/set_signature/index.ts} +1 -1
  23. package/agents/napcat/skills/napcat-files-history/SKILL.md +22 -0
  24. package/agents/napcat/skills/napcat-files-history/tools/download_file/index.js +17 -0
  25. package/{agent/tools/download_file.ts → agents/napcat/skills/napcat-files-history/tools/download_file/index.ts} +1 -1
  26. package/agents/napcat/skills/napcat-files-history/tools/get_friend_msg_history/index.js +18 -0
  27. package/{agent/tools/get_friend_msg_history.ts → agents/napcat/skills/napcat-files-history/tools/get_friend_msg_history/index.ts} +1 -1
  28. package/agents/napcat/skills/napcat-files-history/tools/get_group_file_url/index.js +19 -0
  29. package/{agent/tools/get_group_file_url.ts → agents/napcat/skills/napcat-files-history/tools/get_group_file_url/index.ts} +1 -1
  30. package/agents/napcat/skills/napcat-files-history/tools/get_group_msg_history/index.js +19 -0
  31. package/{agent/tools/get_group_msg_history.ts → agents/napcat/skills/napcat-files-history/tools/get_group_msg_history/index.ts} +1 -1
  32. package/agents/napcat/skills/napcat-files-history/tools/get_group_root_files/index.js +17 -0
  33. package/{agent/tools/get_group_root_files.ts → agents/napcat/skills/napcat-files-history/tools/get_group_root_files/index.ts} +1 -1
  34. package/agents/napcat/skills/napcat-files-history/tools/upload_group_file/index.js +21 -0
  35. package/{agent/tools/upload_group_file.ts → agents/napcat/skills/napcat-files-history/tools/upload_group_file/index.ts} +1 -1
  36. package/agents/napcat/skills/napcat-group-content/SKILL.md +21 -0
  37. package/agents/napcat/skills/napcat-group-content/tools/del_group_notice/index.js +20 -0
  38. package/{agent/tools/del_group_notice.ts → agents/napcat/skills/napcat-group-content/tools/del_group_notice/index.ts} +1 -1
  39. package/agents/napcat/skills/napcat-group-content/tools/delete_essence_msg/index.js +19 -0
  40. package/{agent/tools/delete_essence_msg.ts → agents/napcat/skills/napcat-group-content/tools/delete_essence_msg/index.ts} +1 -1
  41. package/agents/napcat/skills/napcat-group-content/tools/get_essence_list/index.js +17 -0
  42. package/{agent/tools/get_essence_list.ts → agents/napcat/skills/napcat-group-content/tools/get_essence_list/index.ts} +1 -1
  43. package/agents/napcat/skills/napcat-group-content/tools/get_group_notice/index.js +17 -0
  44. package/{agent/tools/get_group_notice.ts → agents/napcat/skills/napcat-group-content/tools/get_group_notice/index.ts} +1 -1
  45. package/agents/napcat/skills/napcat-group-content/tools/send_group_notice/index.js +21 -0
  46. package/{agent/tools/send_group_notice.ts → agents/napcat/skills/napcat-group-content/tools/send_group_notice/index.ts} +1 -1
  47. package/agents/napcat/skills/napcat-group-content/tools/set_essence_msg/index.js +19 -0
  48. package/{agent/tools/set_essence_msg.ts → agents/napcat/skills/napcat-group-content/tools/set_essence_msg/index.ts} +1 -1
  49. package/agents/napcat/skills/napcat-group-settings/SKILL.md +23 -0
  50. package/agents/napcat/skills/napcat-group-settings/tools/get_group_info_ex/index.js +17 -0
  51. package/{agent/tools/get_group_info_ex.ts → agents/napcat/skills/napcat-group-settings/tools/get_group_info_ex/index.ts} +1 -1
  52. package/agents/napcat/skills/napcat-group-settings/tools/get_group_shut_list/index.js +17 -0
  53. package/{agent/tools/get_group_shut_list.ts → agents/napcat/skills/napcat-group-settings/tools/get_group_shut_list/index.ts} +1 -1
  54. package/agents/napcat/skills/napcat-group-settings/tools/group_sign/index.js +18 -0
  55. package/{agent/tools/group_sign.ts → agents/napcat/skills/napcat-group-settings/tools/group_sign/index.ts} +1 -1
  56. package/agents/napcat/skills/napcat-group-settings/tools/set_group_portrait/index.js +20 -0
  57. package/{agent/tools/set_group_portrait.ts → agents/napcat/skills/napcat-group-settings/tools/set_group_portrait/index.ts} +1 -1
  58. package/agents/napcat/skills/napcat-group-settings/tools/set_title/index.js +21 -0
  59. package/{agent/tools/set_title.ts → agents/napcat/skills/napcat-group-settings/tools/set_title/index.ts} +1 -1
  60. package/agents/napcat/skills/napcat-media/SKILL.md +23 -0
  61. package/agents/napcat/skills/napcat-media/tools/ai_tts/index.js +19 -0
  62. package/{agent/tools/ai_tts.ts → agents/napcat/skills/napcat-media/tools/ai_tts/index.ts} +1 -1
  63. package/agents/napcat/skills/napcat-media/tools/get_ai_characters/index.js +17 -0
  64. package/{agent/tools/get_ai_characters.ts → agents/napcat/skills/napcat-media/tools/get_ai_characters/index.ts} +1 -1
  65. package/agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.js +20 -0
  66. package/{agent/tools/get_mini_app_ark.ts → agents/napcat/skills/napcat-media/tools/get_mini_app_ark/index.ts} +1 -1
  67. package/agents/napcat/skills/napcat-media/tools/ocr_image/index.js +16 -0
  68. package/{agent/tools/ocr_image.ts → agents/napcat/skills/napcat-media/tools/ocr_image/index.ts} +1 -1
  69. package/agents/napcat/skills/napcat-media/tools/translate/index.js +16 -0
  70. package/{agent/tools/translate.ts → agents/napcat/skills/napcat-media/tools/translate/index.ts} +1 -1
  71. package/agents/napcat/skills/napcat-messaging/SKILL.md +22 -0
  72. package/agents/napcat/skills/napcat-messaging/tools/forward_single_msg/index.js +22 -0
  73. package/{agent/tools/forward_single_msg.ts → agents/napcat/skills/napcat-messaging/tools/forward_single_msg/index.ts} +1 -1
  74. package/agents/napcat/skills/napcat-messaging/tools/mark_msg_as_read/index.js +17 -0
  75. package/{agent/tools/mark_msg_as_read.ts → agents/napcat/skills/napcat-messaging/tools/mark_msg_as_read/index.ts} +1 -1
  76. package/agents/napcat/skills/napcat-messaging/tools/send_forward_msg/index.js +19 -0
  77. package/{agent/tools/send_forward_msg.ts → agents/napcat/skills/napcat-messaging/tools/send_forward_msg/index.ts} +1 -1
  78. package/agents/napcat/skills/napcat-messaging/tools/send_poke/index.js +18 -0
  79. package/{agent/tools/send_poke.ts → agents/napcat/skills/napcat-messaging/tools/send_poke/index.ts} +1 -1
  80. package/agents/napcat/skills/napcat-messaging/tools/set_emoji_reaction/index.js +18 -0
  81. package/{agent/tools/set_emoji_reaction.ts → agents/napcat/skills/napcat-messaging/tools/set_emoji_reaction/index.ts} +1 -1
  82. package/agents/napcat/system.md +3 -0
  83. package/commands/{endpoint/add/[id].js → napcat/endpoint/add/[id]/index.js} +1 -1
  84. package/commands/napcat/endpoint/add/[id]/index.ts +3 -0
  85. package/{lib/napcat-endpoint-commands.js → commands/napcat/endpoint/definition.js} +4 -3
  86. package/{src/napcat-endpoint-commands.ts → commands/napcat/endpoint/definition.ts} +3 -3
  87. package/commands/{endpoint/list.js → napcat/endpoint/list/index.js} +1 -1
  88. package/commands/napcat/endpoint/list/index.ts +3 -0
  89. package/commands/{endpoint/remove/[id].js → napcat/endpoint/remove/[id]/index.js} +1 -1
  90. package/commands/napcat/endpoint/remove/[id]/index.ts +3 -0
  91. package/lib/index.d.ts +1 -1
  92. package/lib/napcat-runtime-state.js +1 -1
  93. package/lib/protocol.d.ts +5 -23
  94. package/lib/protocol.js +40 -27
  95. package/lib/wss-endpoint.d.ts +3 -0
  96. package/lib/wss-endpoint.js +51 -42
  97. package/package.json +33 -21
  98. package/schema.json +22 -1
  99. package/src/index.ts +0 -1
  100. package/src/napcat-runtime-state.ts +1 -1
  101. package/src/protocol.ts +47 -50
  102. package/src/wss-endpoint.ts +48 -43
  103. package/agent/skills/napcat.md +0 -311
  104. package/commands/endpoint/add/[id].ts +0 -3
  105. package/commands/endpoint/list.ts +0 -3
  106. package/commands/endpoint/remove/[id].ts +0 -3
  107. package/lib/napcat-endpoint-commands.d.ts +0 -1
  108. /package/{agent/PERMITS.md → PERMITS.md} +0 -0
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ export default defineAgentTool({
5
+ description: '发送合并转发消息(群聊或私聊)。messages 为转发节点数组。',
6
+ inputSchema: z.object({
7
+ message_type: z.enum(['private', 'group']).describe('private 或 group'),
8
+ id: z.number().describe('群号或 QQ 号'),
9
+ messages: z.string().describe('转发节点 JSON(node 数组)'),
10
+ }),
11
+ adapter: 'napcat',
12
+ tags: ['napcat', 'qq'],
13
+ keywords: ['合并转发', 'forward', '转发消息'],
14
+ async execute({ message_type, id, messages }, context) {
15
+ const endpoint = context.$client;
16
+ const nodes = typeof messages === 'string' ? JSON.parse(messages) : messages;
17
+ return endpoint.sendForwardMsg(message_type, id, nodes);
18
+ },
19
+ });
@@ -1,4 +1,4 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
3
 
4
4
  export default defineAgentTool<{
@@ -0,0 +1,18 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ export default defineAgentTool({
5
+ description: '戳一戳(群聊或私聊)。group_id 不传时为私聊戳一戳。',
6
+ inputSchema: z.object({
7
+ user_id: z.number().describe('目标用户 QQ 号'),
8
+ group_id: z.number().optional().describe('群号(不传则为私聊戳一戳)'),
9
+ }),
10
+ adapter: 'napcat',
11
+ tags: ['napcat', 'qq'],
12
+ keywords: ['戳一戳', 'poke', '戳', '拍一拍'],
13
+ async execute({ user_id, group_id }, context) {
14
+ const endpoint = context.$client;
15
+ await endpoint.sendPoke(user_id, group_id);
16
+ return { success: true, message: `已戳 ${user_id}` };
17
+ },
18
+ });
@@ -1,4 +1,4 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
3
 
4
4
  export default defineAgentTool<{ user_id: number; group_id?: number }>({
@@ -0,0 +1,18 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ export default defineAgentTool({
5
+ description: '为消息添加表情回应(贴表情)。',
6
+ inputSchema: z.object({
7
+ message_id: z.number().describe('消息 ID'),
8
+ emoji_id: z.string().describe('表情 ID'),
9
+ }),
10
+ adapter: 'napcat',
11
+ tags: ['napcat', 'qq'],
12
+ keywords: ['表情回应', 'reaction', '贴表情', 'emoji'],
13
+ async execute({ message_id, emoji_id }, context) {
14
+ const endpoint = context.$client;
15
+ await endpoint.setMsgEmojiLike(message_id, emoji_id);
16
+ return { success: true };
17
+ },
18
+ });
@@ -1,4 +1,4 @@
1
- import { defineAgentTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
3
 
4
4
  export default defineAgentTool<{ message_id: number; emoji_id: string }>({
@@ -0,0 +1,3 @@
1
+ # NAPCAT Platform Agent
2
+
3
+ You are the napcat platform specialist. Interpret requests in the current napcat 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 { napcatEndpointCommands } from "../../../lib/napcat-endpoint-commands.js";
2
+ import { napcatEndpointCommands } from '../../definition.js';
3
3
  export default napcatEndpointCommands.add;
@@ -0,0 +1,3 @@
1
+ import { napcatEndpointCommands } from '../../definition.js';
2
+
3
+ export default napcatEndpointCommands.add;
@@ -1,10 +1,11 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
1
2
  /**
2
- * `napcat.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
3
+ * `napcat endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
4
+ * commands/napcat/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
5
  */
5
6
  import { createEndpointCommands } from 'zhin.js/adapter';
6
7
  import { defineCommand } from 'zhin.js/command';
7
- import { napcatRuntimeStateToken } from './napcat-runtime-state.js';
8
+ import { napcatRuntimeStateToken } from "../../../lib/napcat-runtime-state.js";
8
9
  export const napcatEndpointCommands = createEndpointCommands({
9
10
  adapterKey: 'napcat',
10
11
  adapterDisplayName: 'NapCat',
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `napcat.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
2
+ * `napcat endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/napcat/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
4
  */
5
5
  import { createEndpointCommands } from 'zhin.js/adapter';
6
6
  import { defineCommand } from 'zhin.js/command';
7
- import { napcatRuntimeStateToken } from './napcat-runtime-state.js';
7
+ import { napcatRuntimeStateToken } from '../../../src/napcat-runtime-state.js';
8
8
 
9
9
  export const napcatEndpointCommands = createEndpointCommands({
10
10
  adapterKey: 'napcat',
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { napcatEndpointCommands } from "../../lib/napcat-endpoint-commands.js";
2
+ import { napcatEndpointCommands } from '../definition.js';
3
3
  export default napcatEndpointCommands.list;
@@ -0,0 +1,3 @@
1
+ import { napcatEndpointCommands } from '../definition.js';
2
+
3
+ export default napcatEndpointCommands.list;
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { napcatEndpointCommands } from "../../../lib/napcat-endpoint-commands.js";
2
+ import { napcatEndpointCommands } from '../../definition.js';
3
3
  export default napcatEndpointCommands.remove;
@@ -0,0 +1,3 @@
1
+ import { napcatEndpointCommands } from '../../definition.js';
2
+
3
+ export default napcatEndpointCommands.remove;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, type MessageSegment, type NapCatActionRequest, type NapCatActionResponse, type NapCatAdapterConfig, type NapCatConfigBase, type NapCatEndpointConfig, type NapCatEvent, type NapCatHttpConfig, type NapCatMessageEvent, type NapCatSender, type NapCatWireSegment, type NapCatWsConfig, type NapCatWssConfig, type ParsedSendTarget, type ResolvedNapCatConfig, } from './protocol.js';
1
+ export { buildSendAction, buildWsConnectOptions, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, getChannelId, isMessageEvent, mediaRefToOneBotFile, napcatInboundConversation, napcatOutboundTarget, resolveNapCatConfig, senderNickname, senderUserId, type MessageSegment, type NapCatActionRequest, type NapCatActionResponse, type NapCatConfigBase, type NapCatEndpointConfig, type NapCatEvent, type NapCatHttpConfig, type NapCatMessageEvent, type NapCatSender, type NapCatWireSegment, type NapCatWsConfig, type NapCatWssConfig, type ParsedSendTarget, type ResolvedNapCatConfig, } from './protocol.js';
2
2
  export { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
3
3
  export { napcatClient, type NapcatClient, type NapcatClientEventMap, } from './client.js';
4
4
  export { parseOneBotGetMsgResponse } from './onebot-get-msg.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NapCat 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `napcat.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `napcat endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6
  export const napcatRuntimeStateToken = defineEndpointRuntimeStateToken('napcat');
package/lib/protocol.d.ts CHANGED
@@ -3,26 +3,11 @@
3
3
  * No legacy Adapter/Endpoint / segment-mapper.
4
4
  * Canonicalization is owned by gateway/core before endpoint.send.
5
5
  */
6
- import { type MediaRef } from '@zhin.js/core';
7
- import type { ConversationRef } from '@zhin.js/im-contract';
8
- /** Transitional legacy endpoint row (`endpoints[]` with `context: napcat`). */
9
- export interface NapCatLegacyEndpointRow {
10
- readonly context?: string;
6
+ import { type MediaRef, type ConversationRef } from '@zhin.js/im-contract';
7
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
8
+ export interface NapCatEndpointConfig {
11
9
  readonly connection?: 'ws' | 'wss' | 'http';
12
- readonly id?: string;
13
- readonly access_token?: string;
14
- readonly url?: string;
15
- readonly path?: string;
16
- readonly http_url?: string;
17
- readonly post_path?: string;
18
- readonly reconnect_interval?: number;
19
- readonly heartbeat_interval?: number;
20
- readonly poll_interval?: number;
21
- }
22
- /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
23
- export interface NapCatAdapterConfig {
24
- readonly connection?: 'ws' | 'wss' | 'http';
25
- readonly id?: string;
10
+ readonly id: string;
26
11
  readonly access_token?: string;
27
12
  readonly url?: string;
28
13
  readonly path?: string;
@@ -31,8 +16,6 @@ export interface NapCatAdapterConfig {
31
16
  readonly reconnect_interval?: number;
32
17
  readonly heartbeat_interval?: number;
33
18
  readonly poll_interval?: number;
34
- /** Transitional: legacy root `endpoints[]` with `context: napcat`. */
35
- readonly endpoints?: ReadonlyArray<NapCatLegacyEndpointRow>;
36
19
  }
37
20
  export interface NapCatConfigBase {
38
21
  readonly context: 'napcat';
@@ -60,7 +43,6 @@ export interface NapCatHttpConfig extends NapCatConfigBase {
60
43
  readonly poll_interval: number;
61
44
  }
62
45
  export type ResolvedNapCatConfig = NapCatWsConfig | NapCatWssConfig | NapCatHttpConfig;
63
- export type NapCatEndpointConfig = ResolvedNapCatConfig;
64
46
  export interface NapCatSender {
65
47
  readonly role?: string;
66
48
  readonly nickname?: string;
@@ -114,7 +96,7 @@ export interface ParsedSendTarget {
114
96
  readonly message_type: 'private' | 'group';
115
97
  readonly id: string;
116
98
  }
117
- export declare function resolveNapCatConfig(config?: NapCatAdapterConfig): ResolvedNapCatConfig;
99
+ export declare function resolveNapCatConfig(config: NapCatEndpointConfig): ResolvedNapCatConfig;
118
100
  export declare function isMessageEvent(ev: NapCatEvent): ev is NapCatMessageEvent;
119
101
  export declare function getChannelId(ev: NapCatEvent): string;
120
102
  /**
package/lib/protocol.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * No legacy Adapter/Endpoint / segment-mapper.
4
4
  * Canonicalization is owned by gateway/core before endpoint.send.
5
5
  */
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
  const logger = getLogger('napcat');
9
9
  function normalizeConnection(connection) {
@@ -11,48 +11,36 @@ function normalizeConnection(connection) {
11
11
  return connection;
12
12
  return 'ws';
13
13
  }
14
- export function resolveNapCatConfig(config = {}) {
15
- const entry = config.endpoints?.find((item) => item.context === 'napcat');
16
- const connection = normalizeConnection(config.connection ?? entry?.connection);
17
- const id = (typeof config.id === 'string' && config.id)
18
- || (typeof entry?.id === 'string' && entry.id)
19
- || process.env.NAPCAT_BOT_NAME
20
- || 'napcat-bot';
21
- const access_token = config.access_token ?? entry?.access_token;
14
+ export function resolveNapCatConfig(config) {
15
+ const connection = normalizeConnection(config.connection);
16
+ const id = requiredEndpointField(config.id, 'id');
17
+ const access_token = optionalEndpointField(config.access_token);
22
18
  if (connection === 'ws') {
23
- const url = config.url ?? entry?.url;
24
- if (!url) {
25
- throw new TypeError('NapCat connection:ws requires url (plugins.<key>.url or endpoints with context: napcat)');
26
- }
19
+ const url = requiredEndpointField(config.url, 'url');
27
20
  return {
28
21
  context: 'napcat',
29
22
  connection: 'ws',
30
23
  id,
31
24
  access_token,
32
25
  url,
33
- reconnect_interval: config.reconnect_interval ?? entry?.reconnect_interval ?? 5000,
34
- heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
26
+ reconnect_interval: config.reconnect_interval ?? 5000,
27
+ heartbeat_interval: config.heartbeat_interval ?? 30_000,
35
28
  };
36
29
  }
37
30
  if (connection === 'wss') {
38
- const path = config.path ?? entry?.path;
39
- if (!path)
40
- throw new TypeError('NapCat connection:wss requires path');
31
+ const path = requiredEndpointField(config.path, 'path');
41
32
  return {
42
33
  context: 'napcat',
43
34
  connection: 'wss',
44
35
  id,
45
36
  access_token,
46
37
  path,
47
- heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
38
+ heartbeat_interval: config.heartbeat_interval ?? 30_000,
48
39
  };
49
40
  }
50
41
  if (connection === 'http') {
51
- const http_url = config.http_url ?? entry?.http_url;
52
- const post_path = config.post_path ?? entry?.post_path;
53
- if (!http_url || !post_path) {
54
- throw new TypeError('NapCat connection:http requires http_url and post_path');
55
- }
42
+ const http_url = requiredEndpointField(config.http_url, 'http_url');
43
+ const post_path = requiredEndpointField(config.post_path, 'post_path');
56
44
  return {
57
45
  context: 'napcat',
58
46
  connection: 'http',
@@ -60,11 +48,21 @@ export function resolveNapCatConfig(config = {}) {
60
48
  access_token,
61
49
  http_url,
62
50
  post_path,
63
- poll_interval: config.poll_interval ?? entry?.poll_interval ?? 30_000,
51
+ poll_interval: config.poll_interval ?? 30_000,
64
52
  };
65
53
  }
66
54
  throw new TypeError(`Unknown NapCat connection: ${String(connection)}`);
67
55
  }
56
+ function requiredEndpointField(value, field) {
57
+ const resolved = optionalEndpointField(value);
58
+ if (!resolved) {
59
+ throw new TypeError(`NapCat endpoint requires a non-empty ${field}`);
60
+ }
61
+ return resolved;
62
+ }
63
+ function optionalEndpointField(value) {
64
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
65
+ }
68
66
  export function isMessageEvent(ev) {
69
67
  return (ev.post_type === 'message' || ev.post_type === 'message_sent')
70
68
  && ev.message_id != null;
@@ -112,11 +110,26 @@ export function formatInboundContent(ev) {
112
110
  .join('');
113
111
  }
114
112
  if (typeof ev.message === 'string')
115
- return ev.message.replace(/\[CQ:[^\]]+\]/g, '').trim();
113
+ return stripCqCodes(ev.message).trim();
116
114
  return typeof ev.raw_message === 'string'
117
- ? ev.raw_message.replace(/\[CQ:[^\]]+\]/g, '').trim()
115
+ ? stripCqCodes(ev.raw_message).trim()
118
116
  : '';
119
117
  }
118
+ function stripCqCodes(input) {
119
+ let result = '';
120
+ let cursor = 0;
121
+ while (cursor < input.length) {
122
+ const start = input.indexOf('[CQ:', cursor);
123
+ if (start < 0)
124
+ return result + input.slice(cursor);
125
+ result += input.slice(cursor, start);
126
+ const end = input.indexOf(']', start + 4);
127
+ if (end < 0)
128
+ return result + input.slice(start);
129
+ cursor = end + 1;
130
+ }
131
+ return result;
132
+ }
120
133
  /**
121
134
  * 入站 sender 语义:必须是用户 ID(Runtime Message contract)。
122
135
  * 显示名走 metadata.nickname,见 senderNickname。
@@ -1,4 +1,7 @@
1
1
  import { Endpoint } from 'zhin.js/adapter';
2
+ /**
3
+ * NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
4
+ */
2
5
  import { type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
3
6
  import type { HttpHost } from '@zhin.js/host-http';
4
7
  import type { CapabilityId } from 'zhin.js';
@@ -2,8 +2,7 @@ import { Endpoint } from 'zhin.js/adapter';
2
2
  /**
3
3
  * NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
4
4
  */
5
- import { clearInterval } from 'node:timers';
6
- import { createRecallEndpointControl, } from 'zhin.js/adapter';
5
+ import { createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
7
6
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
8
7
  import { createNapCatEndpointManagement } from './endpoint-management.js';
9
8
  import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
@@ -11,7 +10,7 @@ import { buildSendAction, formatInboundContent, formatOutboundSegments, isMessag
11
10
  import { receiveNapCatSideEvent } from './side-event-dispatch.js';
12
11
  import { createNapCatContentPort } from './onebot-get-msg.js';
13
12
  import { NapcatClient } from './client.js';
14
- import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, startNapCatHeartbeat, } from './ws-transport.js';
13
+ import { callNapCatWsAction, handleNapCatWsMessage, rejectAllPending, } from './ws-transport.js';
15
14
  import { verifyNapCatAccessToken } from './wss-auth.js';
16
15
  export class NapCatWssEndpoint extends Endpoint {
17
16
  client = new NapcatClient((action, params) => this.#callApi(action, params));
@@ -21,9 +20,10 @@ export class NapCatWssEndpoint extends Endpoint {
21
20
  management = createNapCatEndpointManagement(this.client);
22
21
  control = createRecallEndpointControl((id) => this.recallMessage(id));
23
22
  content = createNapCatContentPort((action, params) => this.client.callApi(action, params));
23
+ #connectionLifecycle;
24
24
  #ws;
25
25
  #wsRelease;
26
- #heartbeatTimer;
26
+ #connectionTask = Promise.resolve();
27
27
  #requestId = { value: 0 };
28
28
  #pending = new Map();
29
29
  #open = false;
@@ -32,6 +32,10 @@ export class NapCatWssEndpoint extends Endpoint {
32
32
  super();
33
33
  this.#logger = getAdapterLogger('napcat', options.config.id);
34
34
  this.#options = options;
35
+ this.#connectionLifecycle = createEndpointLifecycle({
36
+ name: `${options.config.id}:inbound`,
37
+ reconnect: false,
38
+ });
35
39
  }
36
40
  async start() {
37
41
  if (this.#started)
@@ -39,7 +43,15 @@ export class NapCatWssEndpoint extends Endpoint {
39
43
  this.#started = true;
40
44
  const handle = this.#options.http.ws(this.#options.config.path);
41
45
  this.#wsRelease = handle.onConnection((connection) => {
42
- this.#acceptConnection(connection);
46
+ this.#connectionTask = this.#connectionTask
47
+ .then(() => this.#acceptConnection(connection))
48
+ .catch((error) => {
49
+ this.#logger.warn(formatCompact({
50
+ op: 'wss_connection_failed',
51
+ endpoint: this.#options.config.id,
52
+ error: error instanceof Error ? error.message : String(error),
53
+ }));
54
+ });
43
55
  });
44
56
  this.#logger.info(formatCompact({
45
57
  op: 'listen',
@@ -58,21 +70,11 @@ export class NapCatWssEndpoint extends Endpoint {
58
70
  this.#open = false;
59
71
  this.#wsRelease?.();
60
72
  this.#wsRelease = undefined;
61
- if (this.#heartbeatTimer) {
62
- clearInterval(this.#heartbeatTimer);
63
- this.#heartbeatTimer = undefined;
64
- }
73
+ await this.#connectionTask;
74
+ await this.#connectionLifecycle.stop();
65
75
  rejectAllPending(this.#pending);
66
76
  this.#inboundDeduper.clear();
67
- if (this.#ws) {
68
- try {
69
- this.#ws.close();
70
- }
71
- catch {
72
- /* ignore */
73
- }
74
- this.#ws = undefined;
75
- }
77
+ this.#ws = undefined;
76
78
  this.#started = false;
77
79
  }
78
80
  async send({ conversation, payload }) {
@@ -141,37 +143,44 @@ export class NapCatWssEndpoint extends Endpoint {
141
143
  }));
142
144
  });
143
145
  }
144
- #acceptConnection(connection) {
146
+ async #acceptConnection(connection) {
145
147
  if (!verifyNapCatAccessToken(this.#options.config.access_token, connection.request)) {
146
148
  connection.socket.close(4003, 'Unauthorized');
147
149
  return;
148
150
  }
149
151
  const socket = connection.socket;
150
- if (this.#ws) {
151
- try {
152
- this.#ws.close();
153
- }
154
- catch {
155
- /* ignore */
156
- }
157
- }
158
- this.#ws = socket;
159
- this.#heartbeatTimer = startNapCatHeartbeat(this.#ws, this.#options.config.heartbeat_interval, this.#heartbeatTimer);
160
- socket.on('message', (data) => {
161
- handleNapCatWsMessage(data, {
162
- endpointId: this.#options.config.id,
163
- pending: this.#pending,
164
- admit: (event) => this.admit(event),
152
+ await this.#connectionLifecycle.stop();
153
+ await this.#connectionLifecycle.start(async (lifecycleHandle) => {
154
+ this.#ws = socket;
155
+ lifecycleHandle.onForceClose(() => {
156
+ try {
157
+ socket.close();
158
+ }
159
+ catch {
160
+ /* ignore */
161
+ }
165
162
  });
166
- });
167
- socket.on('close', () => {
168
- if (this.#ws === socket) {
169
- this.#ws = undefined;
170
- if (this.#heartbeatTimer) {
171
- clearInterval(this.#heartbeatTimer);
172
- this.#heartbeatTimer = undefined;
163
+ this.#connectionLifecycle.startHeartbeat(() => {
164
+ try {
165
+ socket.ping?.();
166
+ }
167
+ catch {
168
+ /* ignore */
173
169
  }
174
- }
170
+ }, this.#options.config.heartbeat_interval);
171
+ socket.on('message', (data) => {
172
+ this.#connectionLifecycle.notifyHeartbeatAck();
173
+ handleNapCatWsMessage(data, {
174
+ endpointId: this.#options.config.id,
175
+ pending: this.#pending,
176
+ admit: (event) => this.admit(event),
177
+ });
178
+ });
179
+ socket.on('close', () => {
180
+ if (this.#ws === socket)
181
+ this.#ws = undefined;
182
+ lifecycleHandle.notifyClosed(new Error('NapCat reverse WebSocket closed'));
183
+ });
175
184
  });
176
185
  this.#logger.debug(formatCompact({
177
186
  endpoint: this.#options.config.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-napcat",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Zhin.js NapCat adapter for Plugin Runtime (WebSocket client, OneBot11 + NapCat extensions)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -19,9 +19,11 @@
19
19
  "schema.json",
20
20
  "src",
21
21
  "lib",
22
- "agent",
22
+ "tools",
23
+ "agents",
23
24
  "README.md",
24
- "CHANGELOG.md"
25
+ "CHANGELOG.md",
26
+ "PERMITS.md"
25
27
  ],
26
28
  "keywords": [
27
29
  "zhin",
@@ -43,13 +45,16 @@
43
45
  },
44
46
  "dependencies": {
45
47
  "ws": "^8.21.1",
46
- "@zhin.js/adapter": "1.1.12",
47
- "@zhin.js/core": "1.1.35",
48
- "@zhin.js/feature-kit": "1.1.0",
49
- "@zhin.js/host-http": "1.1.0",
50
- "@zhin.js/im-contract": "1.1.0",
51
- "@zhin.js/logger": "1.1.0",
52
- "@zhin.js/permission": "1.1.0"
48
+ "@zhin.js/adapter": "1.1.15",
49
+ "@zhin.js/agent-feature": "1.1.2",
50
+ "@zhin.js/core": "1.1.38",
51
+ "@zhin.js/feature-kit": "1.1.2",
52
+ "@zhin.js/host-http": "1.1.2",
53
+ "@zhin.js/im-contract": "1.1.2",
54
+ "@zhin.js/logger": "1.1.1",
55
+ "@zhin.js/permission": "1.1.2",
56
+ "@zhin.js/skill": "1.1.2",
57
+ "@zhin.js/tool": "1.1.2"
53
58
  },
54
59
  "devDependencies": {
55
60
  "@types/node": "^26.1.2",
@@ -57,22 +62,17 @@
57
62
  "typescript": "^6.0.3",
58
63
  "vitest": "^4.1.10",
59
64
  "zod": "^4.4.3",
60
- "@zhin.js/agent": "1.1.23",
61
- "@zhin.js/host-http": "1.1.0",
62
- "zhin.js": "1.1.0"
65
+ "@zhin.js/host-http": "1.1.2",
66
+ "zhin.js": "1.1.3"
63
67
  },
64
68
  "peerDependencies": {
65
69
  "zod": "^4.0.0",
66
- "@zhin.js/adapter": "^1.1.12",
67
- "@zhin.js/agent": "^1.1.23",
68
- "@zhin.js/command": "^1.1.0",
69
- "@zhin.js/core": "^1.1.35",
70
- "zhin.js": "^1.1.0"
70
+ "@zhin.js/adapter": "^1.1.15",
71
+ "@zhin.js/command": "^1.1.2",
72
+ "@zhin.js/core": "^1.1.38",
73
+ "zhin.js": "^1.1.3"
71
74
  },
72
75
  "peerDependenciesMeta": {
73
- "@zhin.js/agent": {
74
- "optional": true
75
- },
76
76
  "@zhin.js/command": {
77
77
  "optional": true
78
78
  },
@@ -109,6 +109,18 @@
109
109
  {
110
110
  "package": "@zhin.js/command",
111
111
  "api": "^1.0.0"
112
+ },
113
+ {
114
+ "package": "@zhin.js/tool",
115
+ "api": "^1.0.0"
116
+ },
117
+ {
118
+ "package": "@zhin.js/skill",
119
+ "api": "^1.0.0"
120
+ },
121
+ {
122
+ "package": "@zhin.js/agent-feature",
123
+ "api": "^1.0.0"
112
124
  }
113
125
  ],
114
126
  "plugins": []
package/schema.json CHANGED
@@ -45,10 +45,11 @@
45
45
  },
46
46
  "endpoints": {
47
47
  "type": "array",
48
+ "minItems": 1,
48
49
  "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
49
50
  "items": {
50
51
  "type": "object",
51
- "additionalProperties": true,
52
+ "additionalProperties": false,
52
53
  "properties": {
53
54
  "master": {
54
55
  "type": [
@@ -68,6 +69,14 @@
68
69
  },
69
70
  "description": "本 endpoint 的 trusted 列表"
70
71
  },
72
+ "connection": {
73
+ "type": "string",
74
+ "enum": [
75
+ "ws",
76
+ "wss",
77
+ "http"
78
+ ]
79
+ },
71
80
  "url": {
72
81
  "type": "string",
73
82
  "description": "NapCat WebSocket URL (required for connection: ws)"
@@ -88,6 +97,18 @@
88
97
  "type": "string",
89
98
  "description": "NapCat access token"
90
99
  },
100
+ "reconnect_interval": {
101
+ "type": "number"
102
+ },
103
+ "heartbeat_interval": {
104
+ "type": "number"
105
+ },
106
+ "poll_interval": {
107
+ "type": "number"
108
+ },
109
+ "commandPrefix": {
110
+ "type": "string"
111
+ },
91
112
  "id": {
92
113
  "type": "string",
93
114
  "description": "NapCat bot name"