@zhin.js/adapter-onebot12 1.1.0 → 1.1.2

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 (40) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +5 -3
  3. package/adapters/{onebot12.js → onebot12/index.js} +10 -10
  4. package/adapters/{onebot12.ts → onebot12/index.ts} +17 -17
  5. package/commands/{endpoint/add/[id].js → onebot12/endpoint/add/[id]/index.js} +1 -1
  6. package/commands/onebot12/endpoint/add/[id]/index.ts +3 -0
  7. package/{lib/onebot12-endpoint-commands.js → commands/onebot12/endpoint/definition.js} +4 -3
  8. package/{src/onebot12-endpoint-commands.ts → commands/onebot12/endpoint/definition.ts} +3 -3
  9. package/commands/{endpoint/list.js → onebot12/endpoint/list/index.js} +1 -1
  10. package/commands/onebot12/endpoint/list/index.ts +3 -0
  11. package/commands/{endpoint/remove/[id].js → onebot12/endpoint/remove/[id]/index.js} +1 -1
  12. package/commands/onebot12/endpoint/remove/[id]/index.ts +3 -0
  13. package/lib/client.js +1 -1
  14. package/lib/index.d.ts +1 -1
  15. package/lib/onebot12-runtime-state.js +1 -1
  16. package/lib/protocol.d.ts +6 -22
  17. package/lib/protocol.js +44 -28
  18. package/lib/webhook.js +1 -5
  19. package/lib/ws-endpoint.js +16 -52
  20. package/lib/ws-transport.d.ts +12 -0
  21. package/lib/ws-transport.js +58 -0
  22. package/lib/ws-types.d.ts +2 -1
  23. package/lib/wss-endpoint.js +63 -92
  24. package/package.json +19 -14
  25. package/plugin.js +1 -1
  26. package/schema.json +19 -1
  27. package/src/client.ts +1 -1
  28. package/src/index.ts +0 -1
  29. package/src/onebot12-runtime-state.ts +1 -1
  30. package/src/protocol.ts +52 -51
  31. package/src/webhook.ts +1 -5
  32. package/src/ws-endpoint.ts +24 -60
  33. package/src/ws-transport.ts +88 -0
  34. package/src/ws-types.ts +3 -1
  35. package/src/wss-endpoint.ts +69 -99
  36. package/agent/skills/onebot12.md +0 -36
  37. package/commands/endpoint/add/[id].ts +0 -3
  38. package/commands/endpoint/list.ts +0 -3
  39. package/commands/endpoint/remove/[id].ts +0 -3
  40. package/lib/onebot12-endpoint-commands.d.ts +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,90 @@
1
1
  # @zhin.js/adapter-onebot12
2
2
 
3
+ ## 1.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - a9e4a40: Derive Command user routes only from explicit `commands/**/*/index.ts(x)` entry paths instead of prepending the plugin owner. Child plugins can now publish top-level static or dynamic commands, while duplicate routes across owners fail during generation construction. Adapter endpoint commands move to explicit paths such as `commands/qq/endpoint/list/index.ts` and are invoked as `qq endpoint list`.
8
+ - 8740059: Standardize TypeScript capabilities on named module directories such as `commands/foo/index.ts`, `middlewares/audit/index.ts`, `handlers/message-receive/index.ts`, `pages/workroom/index.tsx`, and `mcps/filesystem/index.ts`. Only the fixed `index` entry is discovered; sibling files remain private helpers.
9
+
10
+ Command route segments come from directories, while `[name]`, `[[name]]`, `[...name]`, and `[[...name]]` directories declare dynamic parameters. Plugin owners do not enter the route unless their config explicitly sets `commandNamespace`; Endpoint `commandPrefix` remains platform-owned and defaults to an empty string.
11
+
12
+ Migrate the built-in adapters, plugins, examples, generators, migration tooling, hot reload classification, Agent authoring surfaces, documentation, and release artifacts to the explicit entry convention.
13
+
14
+ Make Tool ownership and progressive disclosure explicit across all four supported locations: plugin-public `tools/`, Agent-private `agents/<name>/tools/`, Skill-private `skills/<name>/tools/`, and Agent-Skill-private `agents/<name>/skills/<name>/tools/`. Move adapter and group-suite operations that require domain instructions into their owning Skills so `load_skill` is the only path that unlocks their schemas.
15
+
16
+ Remove the package-root `agent/` convention. Public capabilities now use named package-root directories, schedules use `schedules/<name>/index.ts` or `plugin.ts` injection, MCP connections use `mcps/<name>/index.ts`, Prompt Sections use `prompt-sections/<name>/index.ts`, Agent definitions use `agents/<name>/`, and permission vocabulary is published as `PERMITS.md`.
17
+
18
+ - 25a845a: Unify Skills on `skills/<name>/SKILL.md`, support Agent-private Skills and nested Skill-private Tools, publish and mount existing plugin Skills, add governed Skill metadata and Turn access filtering, unlock only already-admitted same-owner Tools, and make `on-risk` and `once` approval behavior precise.
19
+ - 4380cf9: Colocate adapter command definitions and Agent Skill handlers with their owning capabilities, and provide a shared validated platform permission constructor.
20
+ - bfb646b: Require one canonical expanded OneBot 12 endpoint configuration and a complete Webhook `api_url`. Remove nested endpoint and environment compatibility, centralize WebSocket request correlation, and route reverse WebSocket replacement, heartbeat, pending actions, and stale-event admission through the endpoint lifecycle.
21
+ - 5c3858e: Make `@zhin.js/im-contract` the explicit zero-dependency owner of transport identities, canonical segments, media guards, conversation facts, stores, and delivery contracts. Platform adapters now read media contracts from that foundation instead of reaching through Core.
22
+ - Updated dependencies [c861789]
23
+ - Updated dependencies [1414ccb]
24
+ - Updated dependencies [743d470]
25
+ - Updated dependencies [62dee52]
26
+ - Updated dependencies [cd54131]
27
+ - Updated dependencies [244e475]
28
+ - Updated dependencies [a9e4a40]
29
+ - Updated dependencies [5855db7]
30
+ - Updated dependencies [ec921d2]
31
+ - Updated dependencies [8740059]
32
+ - Updated dependencies [25a845a]
33
+ - Updated dependencies [103f2c3]
34
+ - Updated dependencies [9110ab8]
35
+ - Updated dependencies [cd2a888]
36
+ - Updated dependencies [eb3227a]
37
+ - Updated dependencies [b853dba]
38
+ - Updated dependencies [e561309]
39
+ - Updated dependencies [d4c6175]
40
+ - Updated dependencies [7a0e1ca]
41
+ - Updated dependencies [103b5d3]
42
+ - Updated dependencies [5a7a7f7]
43
+ - Updated dependencies [b076eae]
44
+ - Updated dependencies [73a24b7]
45
+ - Updated dependencies [2dbbc15]
46
+ - Updated dependencies [1cb1163]
47
+ - Updated dependencies [be3061e]
48
+ - Updated dependencies [75f8332]
49
+ - Updated dependencies [81935e2]
50
+ - Updated dependencies [f9ed01b]
51
+ - Updated dependencies [ac0ab50]
52
+ - Updated dependencies [5140ce1]
53
+ - Updated dependencies [522d75f]
54
+ - Updated dependencies [a7611b3]
55
+ - Updated dependencies [8823044]
56
+ - Updated dependencies [379439b]
57
+ - Updated dependencies [11c9352]
58
+ - Updated dependencies [6b70e46]
59
+ - Updated dependencies [135ac91]
60
+ - Updated dependencies [140cf0f]
61
+ - Updated dependencies [251e4d2]
62
+ - Updated dependencies [203ad34]
63
+ - Updated dependencies [e6c5113]
64
+ - Updated dependencies [e0f6478]
65
+ - Updated dependencies [baef56b]
66
+ - Updated dependencies [da0a8e3]
67
+ - Updated dependencies [2fd8017]
68
+ - Updated dependencies [5c3858e]
69
+ - Updated dependencies [507d602]
70
+ - Updated dependencies [df9f76b]
71
+ - @zhin.js/core@1.1.37
72
+ - @zhin.js/host-http@1.1.1
73
+ - @zhin.js/adapter@1.1.14
74
+ - @zhin.js/feature-kit@1.1.1
75
+ - @zhin.js/skill@1.1.1
76
+ - @zhin.js/logger@1.1.1
77
+ - @zhin.js/im-contract@1.1.1
78
+
79
+ ## 1.1.1
80
+
81
+ ### Patch Changes
82
+
83
+ - 33b1582: Update vulnerable dependencies and replace unsafe HTML and protocol-text regular expressions with bounded scanners.
84
+ - Updated dependencies [769e67f]
85
+ - @zhin.js/adapter@1.1.13
86
+ - @zhin.js/core@1.1.36
87
+
3
88
  ## 1.1.0
4
89
 
5
90
  ### Minor Changes
package/README.md CHANGED
@@ -18,11 +18,13 @@ pnpm add @zhin.js/adapter-onebot12
18
18
 
19
19
  ## Plugin Runtime
20
20
 
21
- - `@zhin.js/adapter` — 约定式 `adapters/onebot12.ts`(`defineAdapter`)
21
+ - `@zhin.js/adapter` — 约定式 `adapters/onebot12/index.ts`(`defineAdapter`)
22
22
  - `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
23
23
  - `zhin.js` — `plugin.ts`(`definePlugin`)
24
24
  - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
25
25
 
26
+ `AdapterIndex` 会把实例默认值与 `endpoints[]` 的逐项覆盖合并;协议层只接收一个已经展开的 endpoint 配置,不再读取嵌套 endpoint 或从进程环境推断身份。Webhook 配置必须同时提供 `path` 与 `api_url`,从而在启动前建立完整的双向能力。
27
+
26
28
  入站:`gateway.receive({ conversation: ConversationRef, message: { conversation, id }, content, sender, metadata })`
27
29
  出站:`send({ conversation, payload })` → WS `send_message`(payload 已由 gateway/core 渲染;无 segment-mapper)
28
30
 
@@ -46,7 +48,7 @@ plugins:
46
48
  reconnect_interval: 5000
47
49
  heartbeat_interval: 30000
48
50
  endpoints:
49
- - name: ob12-bot
51
+ - id: ob12-bot
50
52
  url: "ws://127.0.0.1:6700"
51
53
  access_token: "${ONEBOT12_ACCESS_TOKEN}"
52
54
  ```
@@ -73,7 +75,7 @@ plugins:
73
75
 
74
76
  ## AI 工具
75
77
 
76
- 技能说明见 `agent/skills/onebot12.md`。
78
+ 技能说明见 `skills/onebot12/SKILL.md`。
77
79
 
78
80
  ## 文档链接
79
81
 
@@ -1,17 +1,17 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  /**
3
- * Convention entry: discover `adapters/onebot12.ts` → defineAdapter.
3
+ * Convention entry: discover `adapters/onebot12/index.ts` → defineAdapter.
4
4
  */
5
5
  import { defineAdapter } from 'zhin.js/adapter';
6
6
  import { httpHostToken } from '@zhin.js/host-http';
7
- import { OneBot12WebhookEndpoint } from "../lib/webhook.js";
8
- import { OneBot12WsEndpoint } from "../lib/ws-endpoint.js";
9
- import { OneBot12WssEndpoint } from "../lib/wss-endpoint.js";
10
- import { resolveOneBot12Config, } from "../lib/protocol.js";
11
- import { onebot12RuntimeStateToken } from "../lib/onebot12-runtime-state.js";
12
- export { OneBot12WebhookEndpoint } from "../lib/webhook.js";
13
- export { OneBot12WsEndpoint } from "../lib/ws-endpoint.js";
14
- export { OneBot12WssEndpoint } from "../lib/wss-endpoint.js";
7
+ import { OneBot12WebhookEndpoint } from "../../lib/webhook.js";
8
+ import { OneBot12WsEndpoint } from "../../lib/ws-endpoint.js";
9
+ import { OneBot12WssEndpoint } from "../../lib/wss-endpoint.js";
10
+ import { resolveOneBot12Config, } from "../../lib/protocol.js";
11
+ import { onebot12RuntimeStateToken } from "../../lib/onebot12-runtime-state.js";
12
+ export { OneBot12WebhookEndpoint } from "../../lib/webhook.js";
13
+ export { OneBot12WsEndpoint } from "../../lib/ws-endpoint.js";
14
+ export { OneBot12WssEndpoint } from "../../lib/wss-endpoint.js";
15
15
  export default defineAdapter({
16
16
  capabilities: ['inbound', 'outbound'],
17
17
  operations: ['recall'],
@@ -23,7 +23,7 @@ export default defineAdapter({
23
23
  },
24
24
  create(context) {
25
25
  const config = resolveOneBot12Config(context.config);
26
- // 注册到插件运行时状态(onebot12.endpoint list 的"运行中"数据源)
26
+ // 注册到插件运行时状态(onebot12 endpoint list 的"运行中"数据源)
27
27
  context.use(onebot12RuntimeStateToken).endpoints.set(config.id, {
28
28
  id: config.id,
29
29
  mode: config.connection,
@@ -1,32 +1,32 @@
1
1
  /**
2
- * Convention entry: discover `adapters/onebot12.ts` → defineAdapter.
2
+ * Convention entry: discover `adapters/onebot12/index.ts` → defineAdapter.
3
3
  */
4
4
  import { defineAdapter } from 'zhin.js/adapter';
5
5
  import { httpHostToken } from '@zhin.js/host-http';
6
- import { OneBot12WebhookEndpoint } from '../src/webhook.js';
7
- import { OneBot12WsEndpoint } from '../src/ws-endpoint.js';
8
- import { OneBot12WssEndpoint } from '../src/wss-endpoint.js';
6
+ import { OneBot12WebhookEndpoint } from '../../src/webhook.js';
7
+ import { OneBot12WsEndpoint } from '../../src/ws-endpoint.js';
8
+ import { OneBot12WssEndpoint } from '../../src/wss-endpoint.js';
9
9
  import {
10
10
  resolveOneBot12Config,
11
- type OneBot12AdapterConfig,
12
- } from '../src/protocol.js';
13
- import { onebot12RuntimeStateToken } from '../src/onebot12-runtime-state.js';
11
+ type OneBot12EndpointConfig,
12
+ } from '../../src/protocol.js';
13
+ import { onebot12RuntimeStateToken } from '../../src/onebot12-runtime-state.js';
14
14
 
15
- export { OneBot12WebhookEndpoint } from '../src/webhook.js';
16
- export type { OneBot12WebhookEndpointOptions } from '../src/webhook.js';
17
- export { OneBot12WsEndpoint } from '../src/ws-endpoint.js';
18
- export type { OneBot12WsEndpointOptions } from '../src/ws-endpoint.js';
19
- export { OneBot12WssEndpoint } from '../src/wss-endpoint.js';
20
- export type { OneBot12WssEndpointOptions } from '../src/wss-endpoint.js';
21
- export type { OneBot12WsSocket, OneBot12WsCreateOptions } from '../src/ws-types.js';
15
+ export { OneBot12WebhookEndpoint } from '../../src/webhook.js';
16
+ export type { OneBot12WebhookEndpointOptions } from '../../src/webhook.js';
17
+ export { OneBot12WsEndpoint } from '../../src/ws-endpoint.js';
18
+ export type { OneBot12WsEndpointOptions } from '../../src/ws-endpoint.js';
19
+ export { OneBot12WssEndpoint } from '../../src/wss-endpoint.js';
20
+ export type { OneBot12WssEndpointOptions } from '../../src/wss-endpoint.js';
21
+ export type { OneBot12WsSocket, OneBot12WsCreateOptions } from '../../src/ws-types.js';
22
22
 
23
23
  declare module '@zhin.js/core' {
24
24
  interface AdapterEndpoints {
25
- onebot12: import('../src/ws-endpoint.js').OneBot12WsEndpoint;
25
+ onebot12: import('../../src/ws-endpoint.js').OneBot12WsEndpoint;
26
26
  }
27
27
  }
28
28
 
29
- export default defineAdapter<OneBot12AdapterConfig>({
29
+ export default defineAdapter<OneBot12EndpointConfig>({
30
30
  capabilities: ['inbound', 'outbound'],
31
31
  operations: ['recall'],
32
32
  // 媒体段经 upload_file 物化为 file_id(spec 正式投递);上传失败降级扩展字段透传。
@@ -37,7 +37,7 @@ export default defineAdapter<OneBot12AdapterConfig>({
37
37
  },
38
38
  create(context) {
39
39
  const config = resolveOneBot12Config(context.config);
40
- // 注册到插件运行时状态(onebot12.endpoint list 的"运行中"数据源)
40
+ // 注册到插件运行时状态(onebot12 endpoint list 的"运行中"数据源)
41
41
  context.use(onebot12RuntimeStateToken).endpoints.set(config.id, {
42
42
  id: config.id,
43
43
  mode: config.connection,
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { onebot12EndpointCommands } from "../../../lib/onebot12-endpoint-commands.js";
2
+ import { onebot12EndpointCommands } from '../../definition.js';
3
3
  export default onebot12EndpointCommands.add;
@@ -0,0 +1,3 @@
1
+ import { onebot12EndpointCommands } from '../../definition.js';
2
+
3
+ export default onebot12EndpointCommands.add;
@@ -1,10 +1,11 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
1
2
  /**
2
- * `onebot12.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
3
+ * `onebot12 endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
4
+ * commands/onebot12/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
5
  */
5
6
  import { createEndpointCommands } from 'zhin.js/adapter';
6
7
  import { defineCommand } from 'zhin.js/command';
7
- import { onebot12RuntimeStateToken } from './onebot12-runtime-state.js';
8
+ import { onebot12RuntimeStateToken } from "../../../lib/onebot12-runtime-state.js";
8
9
  export const onebot12EndpointCommands = createEndpointCommands({
9
10
  adapterKey: 'onebot12',
10
11
  adapterDisplayName: 'OneBot 12',
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `onebot12.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
2
+ * `onebot12 endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/onebot12/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
4
  */
5
5
  import { createEndpointCommands } from 'zhin.js/adapter';
6
6
  import { defineCommand } from 'zhin.js/command';
7
- import { onebot12RuntimeStateToken } from './onebot12-runtime-state.js';
7
+ import { onebot12RuntimeStateToken } from '../../../src/onebot12-runtime-state.js';
8
8
 
9
9
  export const onebot12EndpointCommands = createEndpointCommands({
10
10
  adapterKey: 'onebot12',
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { onebot12EndpointCommands } from "../../lib/onebot12-endpoint-commands.js";
2
+ import { onebot12EndpointCommands } from '../definition.js';
3
3
  export default onebot12EndpointCommands.list;
@@ -0,0 +1,3 @@
1
+ import { onebot12EndpointCommands } from '../definition.js';
2
+
3
+ export default onebot12EndpointCommands.list;
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { onebot12EndpointCommands } from "../../../lib/onebot12-endpoint-commands.js";
2
+ import { onebot12EndpointCommands } from '../../definition.js';
3
3
  export default onebot12EndpointCommands.remove;
@@ -0,0 +1,3 @@
1
+ import { onebot12EndpointCommands } from '../../definition.js';
2
+
3
+ export default onebot12EndpointCommands.remove;
package/lib/client.js CHANGED
@@ -5,7 +5,7 @@ export { OneBotV12Client as Onebot12Client } from '@imhelper/onebot-v12';
5
5
  export function createOnebot12EndpointClient(config, request) {
6
6
  const baseUrl = config.connection === 'ws'
7
7
  ? config.url.replace(/^ws(s?):/, 'http$1:')
8
- : config.connection === 'webhook' && config.api_url
8
+ : config.connection === 'webhook'
9
9
  ? config.api_url
10
10
  : 'http://localhost';
11
11
  return new OneBotV12Client({
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { buildSendMessageParams, buildWsConnectOptions, callOneBot12Action, formatInboundContent, formatOutboundSegments, getChannelId, isBotMentioned, isMessageEvent, mediaRefToOneBot12Fields, mediaRefToOneBot12UploadParams, onebot12InboundConversation, resolveOneBot12Config, senderNickname, senderUserId, uploadOneBot12MediaSegments, type OneBot12ActionRequest, type OneBot12ActionResponse, type OneBot12AdapterConfig, type OneBot12CallAction, type OneBot12ConfigBase, type OneBot12EndpointConfig, type OneBot12Event, type OneBot12HttpOptions, type OneBot12Segment, type OneBot12Self, type OneBot12WebhookConfig, type OneBot12WireSegment, type OneBot12WsConfig, type OneBot12WssConfig, type ResolvedOneBot12Config, } from './protocol.js';
1
+ export { buildSendMessageParams, buildWsConnectOptions, callOneBot12Action, formatInboundContent, formatOutboundSegments, getChannelId, isBotMentioned, isMessageEvent, mediaRefToOneBot12Fields, mediaRefToOneBot12UploadParams, onebot12InboundConversation, resolveOneBot12Config, senderNickname, senderUserId, uploadOneBot12MediaSegments, type OneBot12ActionRequest, type OneBot12ActionResponse, type OneBot12CallAction, type OneBot12ConfigBase, type OneBot12EndpointConfig, type OneBot12Event, type OneBot12HttpOptions, type OneBot12Segment, type OneBot12Self, type OneBot12WebhookConfig, type OneBot12WireSegment, type OneBot12WsConfig, type OneBot12WssConfig, type ResolvedOneBot12Config, } from './protocol.js';
2
2
  export { OneBot12WebhookEndpoint, type OneBot12WebhookEndpointOptions, } from './webhook.js';
3
3
  export { OneBot12WsEndpoint, type OneBot12WsEndpointOptions, } from './ws-endpoint.js';
4
4
  export { OneBot12WssEndpoint, type OneBot12WssEndpointOptions, } from './wss-endpoint.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * OneBot12 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `onebot12.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `onebot12 endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6
  export const onebot12RuntimeStateToken = defineEndpointRuntimeStateToken('onebot12');
package/lib/protocol.d.ts CHANGED
@@ -3,32 +3,17 @@
3
3
  * Canonicalization is owned by gateway/core before endpoint.send.
4
4
  * Spec: https://12.onebot.dev/
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: onebot12`). */
9
- export interface OneBot12LegacyEndpointRow {
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 OneBot12EndpointConfig {
11
9
  readonly connection?: 'ws' | 'webhook' | 'wss';
12
- readonly id?: string;
13
- readonly access_token?: string;
14
- readonly url?: string;
15
- readonly path?: string;
16
- readonly api_url?: string;
17
- readonly reconnect_interval?: number;
18
- readonly heartbeat_interval?: number;
19
- }
20
- /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
21
- export interface OneBot12AdapterConfig {
22
- readonly connection?: 'ws' | 'webhook' | 'wss';
23
- readonly id?: string;
10
+ readonly id: string;
24
11
  readonly access_token?: string;
25
12
  readonly url?: string;
26
13
  readonly path?: string;
27
14
  readonly api_url?: string;
28
15
  readonly reconnect_interval?: number;
29
16
  readonly heartbeat_interval?: number;
30
- /** Transitional: legacy root `endpoints[]` with `context: onebot12`. */
31
- readonly endpoints?: ReadonlyArray<OneBot12LegacyEndpointRow>;
32
17
  }
33
18
  export interface OneBot12ConfigBase {
34
19
  readonly context: 'onebot12';
@@ -46,7 +31,7 @@ export interface OneBot12WsConfig extends OneBot12ConfigBase {
46
31
  export interface OneBot12WebhookConfig extends OneBot12ConfigBase {
47
32
  readonly connection: 'webhook';
48
33
  readonly path: string;
49
- readonly api_url?: string;
34
+ readonly api_url: string;
50
35
  }
51
36
  /** 反向 WebSocket:httpHostToken WS upgrade 入站/出站 */
52
37
  export interface OneBot12WssConfig extends OneBot12ConfigBase {
@@ -55,7 +40,6 @@ export interface OneBot12WssConfig extends OneBot12ConfigBase {
55
40
  readonly heartbeat_interval: number;
56
41
  }
57
42
  export type ResolvedOneBot12Config = OneBot12WsConfig | OneBot12WebhookConfig | OneBot12WssConfig;
58
- export type OneBot12EndpointConfig = ResolvedOneBot12Config;
59
43
  export interface OneBot12Self {
60
44
  readonly platform: string;
61
45
  readonly user_id: string;
@@ -101,7 +85,7 @@ export interface OneBot12WireSegment {
101
85
  readonly type: string;
102
86
  readonly data?: Record<string, unknown>;
103
87
  }
104
- export declare function resolveOneBot12Config(config?: OneBot12AdapterConfig): ResolvedOneBot12Config;
88
+ export declare function resolveOneBot12Config(config: OneBot12EndpointConfig): ResolvedOneBot12Config;
105
89
  /** 判断是否为消息事件(type=message) */
106
90
  export declare function isMessageEvent(ev: OneBot12Event): ev is OneBot12Event & {
107
91
  message_id: string;
package/lib/protocol.js CHANGED
@@ -3,64 +3,60 @@
3
3
  * Canonicalization is owned by gateway/core before endpoint.send.
4
4
  * Spec: https://12.onebot.dev/
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('onebot12');
9
- export function resolveOneBot12Config(config = {}) {
10
- const entry = config.endpoints?.find((item) => item.context === 'onebot12');
11
- const connection = config.connection
12
- ?? entry?.connection
13
- ?? 'ws';
14
- const id = (typeof config.id === 'string' && config.id)
15
- || (typeof entry?.id === 'string' && entry.id)
16
- || process.env.ONEBOT12_BOT_NAME
17
- || 'onebot12-bot';
18
- const access_token = config.access_token ?? entry?.access_token;
9
+ export function resolveOneBot12Config(config) {
10
+ const connection = config.connection ?? 'ws';
11
+ const id = requiredEndpointField(config.id, 'id');
12
+ const access_token = optionalEndpointField(config.access_token);
19
13
  if (connection === 'ws') {
20
- const url = config.url ?? entry?.url;
21
- if (!url) {
22
- throw new TypeError('OneBot12 connection:ws requires url (plugins.<key>.url or endpoints with context: onebot12)');
23
- }
14
+ const url = requiredEndpointField(config.url, 'url');
24
15
  return {
25
16
  context: 'onebot12',
26
17
  connection: 'ws',
27
18
  id,
28
19
  access_token,
29
20
  url,
30
- reconnect_interval: config.reconnect_interval ?? entry?.reconnect_interval ?? 5000,
31
- heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
21
+ reconnect_interval: config.reconnect_interval ?? 5000,
22
+ heartbeat_interval: config.heartbeat_interval ?? 30_000,
32
23
  };
33
24
  }
34
25
  if (connection === 'webhook') {
35
- const path = config.path ?? entry?.path;
36
- if (!path) {
37
- throw new TypeError('OneBot12 connection:webhook requires path');
38
- }
26
+ const path = requiredEndpointField(config.path, 'path');
27
+ const api_url = requiredEndpointField(config.api_url, 'api_url');
39
28
  return {
40
29
  context: 'onebot12',
41
30
  connection: 'webhook',
42
31
  id,
43
32
  access_token,
44
33
  path,
45
- api_url: config.api_url ?? entry?.api_url,
34
+ api_url,
46
35
  };
47
36
  }
48
37
  if (connection === 'wss') {
49
- const path = config.path ?? entry?.path;
50
- if (!path) {
51
- throw new TypeError('OneBot12 connection:wss requires path');
52
- }
38
+ const path = requiredEndpointField(config.path, 'path');
53
39
  return {
54
40
  context: 'onebot12',
55
41
  connection: 'wss',
56
42
  id,
57
43
  access_token,
58
44
  path,
59
- heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
45
+ heartbeat_interval: config.heartbeat_interval ?? 30_000,
60
46
  };
61
47
  }
62
48
  throw new TypeError(`Unknown OneBot12 connection: ${String(connection)}`);
63
49
  }
50
+ function requiredEndpointField(value, field) {
51
+ const resolved = optionalEndpointField(value);
52
+ if (!resolved) {
53
+ throw new TypeError(`OneBot12 endpoint requires a non-empty ${field}`);
54
+ }
55
+ return resolved;
56
+ }
57
+ function optionalEndpointField(value) {
58
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
59
+ }
64
60
  /** 判断是否为消息事件(type=message) */
65
61
  export function isMessageEvent(ev) {
66
62
  return ev.type === 'message' && !!ev.message_id;
@@ -111,9 +107,29 @@ export function formatInboundContent(ev) {
111
107
  .join('');
112
108
  }
113
109
  return typeof ev.alt_message === 'string'
114
- ? ev.alt_message.replace(/\[[^\]]*(?:image|audio|video|file)[^\]]*\]/gi, '').trim()
110
+ ? stripMediaPlaceholders(ev.alt_message).trim()
115
111
  : '';
116
112
  }
113
+ function stripMediaPlaceholders(input) {
114
+ const mediaNames = ['image', 'audio', 'video', 'file'];
115
+ let result = '';
116
+ let cursor = 0;
117
+ while (cursor < input.length) {
118
+ const start = input.indexOf('[', cursor);
119
+ if (start < 0)
120
+ return result + input.slice(cursor);
121
+ const end = input.indexOf(']', start + 1);
122
+ if (end < 0)
123
+ return result + input.slice(cursor);
124
+ result += input.slice(cursor, start);
125
+ const placeholder = input.slice(start + 1, end).toLowerCase();
126
+ if (!mediaNames.some((name) => placeholder.includes(name))) {
127
+ result += input.slice(start, end + 1);
128
+ }
129
+ cursor = end + 1;
130
+ }
131
+ return result;
132
+ }
117
133
  /**
118
134
  * Runtime Message sender 必须是用户 ID(agent bridge 以 sender 与 endpointMaster 比对)。
119
135
  * 显示名经 {@link senderNickname} 放入 metadata.nickname。
package/lib/webhook.js CHANGED
@@ -84,11 +84,7 @@ export class OneBot12WebhookEndpoint extends ClientEndpoint {
84
84
  await callOnebot12Client(this.client, 'delete_message', { message_id: messageId });
85
85
  }
86
86
  async #callApi(action, params = {}) {
87
- const apiUrl = this.#options.config.api_url;
88
- if (!apiUrl) {
89
- throw new Error('OneBot12 connection:webhook requires api_url for outbound api');
90
- }
91
- return this.#callAction({ url: apiUrl, access_token: this.#options.config.access_token }, action, params);
87
+ return this.#callAction({ url: this.#options.config.api_url, access_token: this.#options.config.access_token }, action, params);
92
88
  }
93
89
  #admitRaw(ev) {
94
90
  if (!isMessageEvent(ev)) {
@@ -2,7 +2,6 @@
2
2
  * OneBot12 WS client endpoint — outbound connect to OneBot implementation.
3
3
  */
4
4
  import WebSocket from 'ws';
5
- import { clearTimeout } from 'node:timers';
6
5
  import { ClientEndpoint, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
7
6
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
8
7
  import { createOneBot12EndpointManagement } from './endpoint-management.js';
@@ -10,7 +9,7 @@ import { buildSendMessageParams, buildWsConnectOptions, formatInboundContent, fo
10
9
  import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
11
10
  import { createOneBot12ContentPort } from './content-port.js';
12
11
  import { callOnebot12Client, createOnebot12EndpointClient, forwardOnebot12ClientEvents } from './client.js';
13
- import { WS_OPEN, } from './ws-types.js';
12
+ import { callOneBot12WsAction, handleOneBot12WsMessage, rejectAllPending, } from './ws-transport.js';
14
13
  export class OneBot12WsEndpoint extends ClientEndpoint {
15
14
  client;
16
15
  #logger;
@@ -20,7 +19,7 @@ export class OneBot12WsEndpoint extends ClientEndpoint {
20
19
  content;
21
20
  #lifecycle;
22
21
  #ws;
23
- #requestId = 0;
22
+ #requestId = { value: 0 };
24
23
  #pending = new Map();
25
24
  constructor(options) {
26
25
  super();
@@ -71,11 +70,7 @@ export class OneBot12WsEndpoint extends ClientEndpoint {
71
70
  this.close();
72
71
  // 基座负责:清重连/心跳定时器、强关 ws、唤醒 stop-during-connect 竞态
73
72
  await this.#lifecycle.stop();
74
- for (const [, pending] of this.#pending) {
75
- clearTimeout(pending.timeout);
76
- pending.reject(new Error('连接已关闭'));
77
- }
78
- this.#pending.clear();
73
+ rejectAllPending(this.#pending);
79
74
  this.#ws = undefined;
80
75
  }
81
76
  async send({ conversation, payload }) {
@@ -175,7 +170,14 @@ export class OneBot12WsEndpoint extends ClientEndpoint {
175
170
  resolve();
176
171
  });
177
172
  ws.on('message', (data) => {
178
- this.#onMessage(data);
173
+ if (this.#ws !== ws)
174
+ return;
175
+ this.#lifecycle.notifyHeartbeatAck();
176
+ handleOneBot12WsMessage(data, {
177
+ endpointId: this.#options.config.id,
178
+ pending: this.#pending,
179
+ ingest: (event) => this.client.ingest(event),
180
+ });
179
181
  });
180
182
  ws.on('close', (code, reason) => {
181
183
  const reasonStr = typeof reason === 'string'
@@ -188,6 +190,10 @@ export class OneBot12WsEndpoint extends ClientEndpoint {
188
190
  settled = true;
189
191
  reject(new Error(`OneBot12 WS 关闭: ${codeNum} ${reasonStr}`));
190
192
  }
193
+ if (this.#ws === ws) {
194
+ this.#ws = undefined;
195
+ rejectAllPending(this.#pending);
196
+ }
191
197
  // 断开日志与重连武装均由基座负责;仅曾 open 的连接才会武装重连,
192
198
  // 初始连接失败由 start() 的拒绝路径复位,不产生僵尸重连。
193
199
  handle.notifyClosed(`OneBot12 WS 关闭: ${codeNum} ${reasonStr || 'closed'}`);
@@ -207,49 +213,7 @@ export class OneBot12WsEndpoint extends ClientEndpoint {
207
213
  });
208
214
  });
209
215
  }
210
- #onMessage(data) {
211
- try {
212
- const raw = typeof data === 'string'
213
- ? data
214
- : Buffer.isBuffer(data)
215
- ? data.toString()
216
- : data instanceof ArrayBuffer
217
- ? new TextDecoder().decode(data)
218
- : String(data ?? '');
219
- const msg = JSON.parse(raw);
220
- if ('echo' in msg && typeof msg.echo === 'string') {
221
- const resp = msg;
222
- const pending = this.#pending.get(resp.echo);
223
- if (pending) {
224
- this.#pending.delete(resp.echo);
225
- clearTimeout(pending.timeout);
226
- pending.resolve(resp);
227
- }
228
- return;
229
- }
230
- this.client.ingest(msg);
231
- }
232
- catch (error) {
233
- this.#logger.warn(formatCompact({
234
- op: 'onebot12_parse_failed',
235
- endpoint: this.#options.config.id,
236
- error: error instanceof Error ? error.message : String(error),
237
- }));
238
- }
239
- }
240
216
  #callAction(action, params) {
241
- if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
242
- return Promise.reject(new Error('WebSocket 未连接'));
243
- }
244
- const echo = `ob12_${++this.#requestId}`;
245
- const req = { action, params, echo };
246
- return new Promise((resolve, reject) => {
247
- const timeout = setTimeout(() => {
248
- this.#pending.delete(echo);
249
- reject(new Error(`OneBot12 动作超时: ${action}`));
250
- }, 30_000);
251
- this.#pending.set(echo, { resolve, reject, timeout });
252
- this.#ws.send(JSON.stringify(req));
253
- });
217
+ return callOneBot12WsAction(this.#ws, this.#pending, this.#requestId, action, params);
254
218
  }
255
219
  }
@@ -0,0 +1,12 @@
1
+ import type { OneBot12ActionResponse, OneBot12Event } from './protocol.js';
2
+ import { type OneBot12PendingAction, type OneBot12WsSocket } from './ws-types.js';
3
+ export interface OneBot12WsMessageOptions {
4
+ readonly endpointId: string;
5
+ readonly pending: Map<string, OneBot12PendingAction>;
6
+ readonly ingest: (event: OneBot12Event) => void;
7
+ }
8
+ export declare function handleOneBot12WsMessage(data: unknown, options: OneBot12WsMessageOptions): void;
9
+ export declare function callOneBot12WsAction(ws: OneBot12WsSocket | undefined, pending: Map<string, OneBot12PendingAction>, requestId: {
10
+ value: number;
11
+ }, action: string, params: Record<string, unknown>): Promise<OneBot12ActionResponse>;
12
+ export declare function rejectAllPending(pending: Map<string, OneBot12PendingAction>, message?: string): void;