@zhin.js/adapter-wechat-mp 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,102 @@
1
1
  # @zhin.js/adapter-wechat-mp
2
2
 
3
+ ## 1.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [5b5d270]
8
+ - Updated dependencies [36c2eb9]
9
+ - @zhin.js/im-contract@1.1.2
10
+ - @zhin.js/core@1.1.38
11
+ - @zhin.js/adapter@1.1.15
12
+ - @zhin.js/host-http@1.1.2
13
+ - @zhin.js/feature-kit@1.1.2
14
+ - @zhin.js/skill@1.1.2
15
+
16
+ ## 1.1.2
17
+
18
+ ### Patch Changes
19
+
20
+ - 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`.
21
+ - 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.
22
+
23
+ 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.
24
+
25
+ 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.
26
+
27
+ 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.
28
+
29
+ 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`.
30
+
31
+ - 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.
32
+ - 4380cf9: Colocate adapter command definitions and Agent Skill handlers with their owning capabilities, and provide a shared validated platform permission constructor.
33
+ - 224f267: Require one canonical expanded WeChat MP endpoint configuration. The protocol no longer reinterprets nested `endpoints` or reads endpoint identity and credentials from `process.env`.
34
+ - 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.
35
+ - Updated dependencies [c861789]
36
+ - Updated dependencies [1414ccb]
37
+ - Updated dependencies [743d470]
38
+ - Updated dependencies [62dee52]
39
+ - Updated dependencies [cd54131]
40
+ - Updated dependencies [244e475]
41
+ - Updated dependencies [a9e4a40]
42
+ - Updated dependencies [5855db7]
43
+ - Updated dependencies [ec921d2]
44
+ - Updated dependencies [8740059]
45
+ - Updated dependencies [25a845a]
46
+ - Updated dependencies [103f2c3]
47
+ - Updated dependencies [9110ab8]
48
+ - Updated dependencies [cd2a888]
49
+ - Updated dependencies [eb3227a]
50
+ - Updated dependencies [b853dba]
51
+ - Updated dependencies [e561309]
52
+ - Updated dependencies [d4c6175]
53
+ - Updated dependencies [7a0e1ca]
54
+ - Updated dependencies [103b5d3]
55
+ - Updated dependencies [5a7a7f7]
56
+ - Updated dependencies [b076eae]
57
+ - Updated dependencies [73a24b7]
58
+ - Updated dependencies [2dbbc15]
59
+ - Updated dependencies [1cb1163]
60
+ - Updated dependencies [be3061e]
61
+ - Updated dependencies [75f8332]
62
+ - Updated dependencies [81935e2]
63
+ - Updated dependencies [f9ed01b]
64
+ - Updated dependencies [ac0ab50]
65
+ - Updated dependencies [5140ce1]
66
+ - Updated dependencies [522d75f]
67
+ - Updated dependencies [a7611b3]
68
+ - Updated dependencies [8823044]
69
+ - Updated dependencies [379439b]
70
+ - Updated dependencies [11c9352]
71
+ - Updated dependencies [6b70e46]
72
+ - Updated dependencies [135ac91]
73
+ - Updated dependencies [140cf0f]
74
+ - Updated dependencies [251e4d2]
75
+ - Updated dependencies [203ad34]
76
+ - Updated dependencies [e6c5113]
77
+ - Updated dependencies [e0f6478]
78
+ - Updated dependencies [baef56b]
79
+ - Updated dependencies [da0a8e3]
80
+ - Updated dependencies [2fd8017]
81
+ - Updated dependencies [5c3858e]
82
+ - Updated dependencies [507d602]
83
+ - Updated dependencies [df9f76b]
84
+ - @zhin.js/core@1.1.37
85
+ - @zhin.js/host-http@1.1.1
86
+ - @zhin.js/adapter@1.1.14
87
+ - @zhin.js/feature-kit@1.1.1
88
+ - @zhin.js/skill@1.1.1
89
+ - @zhin.js/logger@1.1.1
90
+ - @zhin.js/im-contract@1.1.1
91
+
92
+ ## 1.1.1
93
+
94
+ ### Patch Changes
95
+
96
+ - Updated dependencies [769e67f]
97
+ - @zhin.js/adapter@1.1.13
98
+ - @zhin.js/core@1.1.36
99
+
3
100
  ## 1.1.0
4
101
 
5
102
  ### Minor Changes
package/README.md CHANGED
@@ -18,7 +18,7 @@ pnpm add @zhin.js/adapter-wechat-mp
18
18
 
19
19
  ## Plugin Runtime
20
20
 
21
- - `@zhin.js/adapter` — 约定式 `adapters/wechat-mp.ts`(`defineAdapter`)
21
+ - `@zhin.js/adapter` — 约定式 `adapters/wechat-mp/index.ts`(`defineAdapter`)
22
22
  - `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
23
23
  - `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
24
24
  - `zhin.js` — `plugin.ts`(`definePlugin`)
@@ -38,7 +38,8 @@ pnpm add @zhin.js/adapter-wechat-mp
38
38
  | **回复模式** | 默认 `replyMode: passive`(订阅号被动回复);服务号可设 `customer_service` |
39
39
  | **消息加密** | 可选;`encrypt: true` + `encodingAESKey`;`encryptMode: compatible`(默认)或 `secure` |
40
40
 
41
- 必填字段(`endpoints[i]`):`name`、`appId`、`appSecret`、`token`。
41
+ 必填字段(`endpoints[i]`):`id`、`appId`、`appSecret`、`token`。运行时由 AdapterIndex
42
+ 把实例级默认值与每个 endpoint 合并;协议实现只接收一份展开后的 endpoint 配置。
42
43
 
43
44
  ## 最小配置
44
45
 
@@ -48,7 +49,7 @@ plugins:
48
49
  wechat-mp:
49
50
  path: /wechat/webhook
50
51
  endpoints:
51
- - name: my-wechat-bot
52
+ - id: my-wechat-bot
52
53
  appId: "${WECHAT_APP_ID}"
53
54
  appSecret: "${WECHAT_APP_SECRET}"
54
55
  token: "${WECHAT_TOKEN}"
@@ -84,7 +85,7 @@ Runtime Host(`http`)须已 listen,Webhook 才可达。
84
85
 
85
86
  ## AI 工具
86
87
 
87
- 技能说明见 `agent/skills/wechat-mp.md`。
88
+ 技能说明见 `skills/wechat-mp/SKILL.md`。
88
89
 
89
90
  ## 文档链接
90
91
 
@@ -1,13 +1,13 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  /**
3
- * Convention entry: discover `adapters/wechat-mp.ts` → defineAdapter.
3
+ * Convention entry: discover `adapters/wechat-mp/index.ts` → defineAdapter.
4
4
  */
5
5
  import { defineAdapter } from 'zhin.js/adapter';
6
6
  import { httpHostToken } from '@zhin.js/host-http';
7
- import { WeChatMpEndpoint } from "../lib/endpoint.js";
8
- import { resolveWeChatMpConfig, } from "../lib/protocol.js";
9
- import { wechatMpRuntimeStateToken } from "../lib/wechat-mp-runtime-state.js";
10
- export { WeChatMpEndpoint } from "../lib/endpoint.js";
7
+ import { WeChatMpEndpoint } from "../../lib/endpoint.js";
8
+ import { resolveWeChatMpConfig, } from "../../lib/protocol.js";
9
+ import { wechatMpRuntimeStateToken } from "../../lib/wechat-mp-runtime-state.js";
10
+ export { WeChatMpEndpoint } from "../../lib/endpoint.js";
11
11
  export default defineAdapter({
12
12
  capabilities: ['inbound', 'outbound'],
13
13
  // 客服消息媒体统一经 /cgi-bin/media/upload 物化为 media_id(url 下载后上传,
@@ -18,7 +18,7 @@ export default defineAdapter({
18
18
  },
19
19
  create(context) {
20
20
  const config = resolveWeChatMpConfig(context.config);
21
- // 注册到插件运行时状态(wechat-mp.endpoint list 的"运行中"数据源)
21
+ // 注册到插件运行时状态(wechat-mp endpoint list 的"运行中"数据源)
22
22
  context.use(wechatMpRuntimeStateToken).endpoints.set(config.id, {
23
23
  id: config.id,
24
24
  mode: 'webhook',
@@ -1,20 +1,20 @@
1
1
  /**
2
- * Convention entry: discover `adapters/wechat-mp.ts` → defineAdapter.
2
+ * Convention entry: discover `adapters/wechat-mp/index.ts` → defineAdapter.
3
3
  */
4
4
  import { defineAdapter } from 'zhin.js/adapter';
5
5
  import { httpHostToken } from '@zhin.js/host-http';
6
- import { WeChatMpEndpoint } from '../src/endpoint.js';
6
+ import { WeChatMpEndpoint } from '../../src/endpoint.js';
7
7
  import {
8
8
  resolveWeChatMpConfig,
9
- type WeChatMpAdapterConfig,
10
- } from '../src/protocol.js';
11
- import { wechatMpRuntimeStateToken } from '../src/wechat-mp-runtime-state.js';
9
+ type WeChatMpEndpointConfig,
10
+ } from '../../src/protocol.js';
11
+ import { wechatMpRuntimeStateToken } from '../../src/wechat-mp-runtime-state.js';
12
12
 
13
- export { WeChatMpEndpoint } from '../src/endpoint.js';
14
- export type { WeChatMpEndpointOptions } from '../src/endpoint.js';
15
- export type { WeChatMpFetch } from '../src/client.js';
13
+ export { WeChatMpEndpoint } from '../../src/endpoint.js';
14
+ export type { WeChatMpEndpointOptions } from '../../src/endpoint.js';
15
+ export type { WeChatMpFetch } from '../../src/client.js';
16
16
 
17
- export default defineAdapter<WeChatMpAdapterConfig>({
17
+ export default defineAdapter<WeChatMpEndpointConfig>({
18
18
  capabilities: ['inbound', 'outbound'],
19
19
  // 客服消息媒体统一经 /cgi-bin/media/upload 物化为 media_id(url 下载后上传,
20
20
  // kind=file 的 MediaRef 视为既有 media_id 直传);公众号无卡片交互面,交互段降级纯文本。
@@ -24,7 +24,7 @@ export default defineAdapter<WeChatMpAdapterConfig>({
24
24
  },
25
25
  create(context) {
26
26
  const config = resolveWeChatMpConfig(context.config);
27
- // 注册到插件运行时状态(wechat-mp.endpoint list 的"运行中"数据源)
27
+ // 注册到插件运行时状态(wechat-mp endpoint list 的"运行中"数据源)
28
28
  context.use(wechatMpRuntimeStateToken).endpoints.set(config.id, {
29
29
  id: config.id,
30
30
  mode: 'webhook',
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { wechatMpEndpointCommands } from "../../../lib/wechat-mp-endpoint-commands.js";
2
+ import { wechatMpEndpointCommands } from '../../definition.js';
3
3
  export default wechatMpEndpointCommands.add;
@@ -0,0 +1,3 @@
1
+ import { wechatMpEndpointCommands } from '../../definition.js';
2
+
3
+ export default wechatMpEndpointCommands.add;
@@ -1,10 +1,11 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
1
2
  /**
2
- * `wechat-mp.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
3
+ * `wechat-mp endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
4
+ * commands/wechat-mp/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
5
  */
5
6
  import { createEndpointCommands } from 'zhin.js/adapter';
6
7
  import { defineCommand } from 'zhin.js/command';
7
- import { wechatMpRuntimeStateToken } from './wechat-mp-runtime-state.js';
8
+ import { wechatMpRuntimeStateToken } from "../../../lib/wechat-mp-runtime-state.js";
8
9
  export const wechatMpEndpointCommands = createEndpointCommands({
9
10
  adapterKey: 'wechat-mp',
10
11
  adapterDisplayName: 'WeChat MP',
@@ -1,10 +1,10 @@
1
1
  /**
2
- * `wechat-mp.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
- * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
2
+ * `wechat-mp endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/wechat-mp/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
4
  */
5
5
  import { createEndpointCommands } from 'zhin.js/adapter';
6
6
  import { defineCommand } from 'zhin.js/command';
7
- import { wechatMpRuntimeStateToken } from './wechat-mp-runtime-state.js';
7
+ import { wechatMpRuntimeStateToken } from '../../../src/wechat-mp-runtime-state.js';
8
8
 
9
9
  export const wechatMpEndpointCommands = createEndpointCommands({
10
10
  adapterKey: 'wechat-mp',
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { wechatMpEndpointCommands } from "../../lib/wechat-mp-endpoint-commands.js";
2
+ import { wechatMpEndpointCommands } from '../definition.js';
3
3
  export default wechatMpEndpointCommands.list;
@@ -0,0 +1,3 @@
1
+ import { wechatMpEndpointCommands } from '../definition.js';
2
+
3
+ export default wechatMpEndpointCommands.list;
@@ -1,3 +1,3 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { wechatMpEndpointCommands } from "../../../lib/wechat-mp-endpoint-commands.js";
2
+ import { wechatMpEndpointCommands } from '../../definition.js';
3
3
  export default wechatMpEndpointCommands.remove;
@@ -0,0 +1,3 @@
1
+ import { wechatMpEndpointCommands } from '../../definition.js';
2
+
3
+ export default wechatMpEndpointCommands.remove;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { buildTextReply, computeSignatureHash, decryptEchostr, decryptMessage, encryptMessage, extractOutboundText, formatCustomerServiceBody, formatInboundContent, formatInboundId, isEncryptedEchostr, normalizeEchostrParam, parseXMLMessage, queryParam, readTextBody, resolveEventPassiveReply, resolveWeChatMpConfig, verifySignature, type ResolvedWeChatMpConfig, type TokenResponse, type WeChatAPIResponse, type WeChatMessage, type WeChatMpAdapterConfig, type WeChatWireSegment, } from './protocol.js';
1
+ export { buildTextReply, computeSignatureHash, decryptEchostr, decryptMessage, encryptMessage, extractOutboundText, formatCustomerServiceBody, formatInboundContent, formatInboundId, isEncryptedEchostr, normalizeEchostrParam, parseXMLMessage, queryParam, readTextBody, resolveEventPassiveReply, resolveWeChatMpConfig, verifySignature, type ResolvedWeChatMpConfig, type TokenResponse, type WeChatAPIResponse, type WeChatMessage, type WeChatMpEndpointConfig, type WeChatWireSegment, } from './protocol.js';
2
2
  export { getPassiveReplyCapture, recordPassiveReplyText, runWithPassiveReplyCapture, type PassiveReplyCapture, } from './passive-reply.js';
3
3
  export { WeChatMpClient, wechatMpClient, type WeChatMpClientEventMap, type WeChatMpFetch, } from './client.js';
4
4
  export { WeChatMpEndpoint, type WeChatMpEndpointOptions, } from './endpoint.js';
@@ -1,4 +1,4 @@
1
- import { type MediaRef } from '@zhin.js/core';
1
+ import { type MediaRef } from '@zhin.js/im-contract';
2
2
  export interface MediaBinary {
3
3
  readonly data: Buffer;
4
4
  readonly mimeType: string;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { readFile } from 'node:fs/promises';
8
8
  import { basename } from 'node:path';
9
- import { isMediaRef } from '@zhin.js/core';
9
+ import { isMediaRef } from '@zhin.js/im-contract';
10
10
  const MIME_EXT = {
11
11
  'image/jpeg': 'jpg',
12
12
  'image/png': 'png',
package/lib/protocol.d.ts CHANGED
@@ -4,11 +4,12 @@
4
4
  */
5
5
  import type { IncomingMessage } from 'node:http';
6
6
  import type { ConversationRef } from '@zhin.js/im-contract';
7
- export interface WeChatMpAdapterConfig {
8
- readonly id?: string;
9
- readonly appId?: string;
10
- readonly appSecret?: string;
11
- readonly token?: string;
7
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
8
+ export interface WeChatMpEndpointConfig {
9
+ readonly id: string;
10
+ readonly appId: string;
11
+ readonly appSecret: string;
12
+ readonly token: string;
12
13
  readonly encodingAESKey?: string;
13
14
  readonly path?: string;
14
15
  readonly encrypt?: boolean;
@@ -25,10 +26,6 @@ export interface WeChatMpAdapterConfig {
25
26
  readonly replyMode?: 'passive' | 'customer_service';
26
27
  /** 被动回复等待入站处理的最长时间(毫秒),默认 4500 */
27
28
  readonly passiveReplyTimeoutMs?: number;
28
- /** Transitional: legacy root `endpoints[]` with `context: wechat-mp`. */
29
- readonly endpoints?: ReadonlyArray<Partial<ResolvedWeChatMpConfig> & {
30
- readonly context?: string;
31
- }>;
32
29
  }
33
30
  export interface ResolvedWeChatMpConfig {
34
31
  readonly context: 'wechat-mp';
@@ -79,7 +76,7 @@ export interface WeChatWireSegment {
79
76
  readonly type: string;
80
77
  readonly data?: Record<string, unknown>;
81
78
  }
82
- export declare function resolveWeChatMpConfig(config?: WeChatMpAdapterConfig): ResolvedWeChatMpConfig;
79
+ export declare function resolveWeChatMpConfig(config: WeChatMpEndpointConfig): ResolvedWeChatMpConfig;
83
80
  export declare function queryParam(value: string | null | undefined): string;
84
81
  /** URL 查询里的 Base64 可能把 `+` 解码成空格 */
85
82
  export declare function normalizeEchostrParam(echostr: string): string;
package/lib/protocol.js CHANGED
@@ -4,26 +4,17 @@
4
4
  */
5
5
  import { createHash, createDecipheriv, createCipheriv, randomBytes } from 'node:crypto';
6
6
  import * as xml2js from 'xml2js';
7
- export function resolveWeChatMpConfig(config = {}) {
8
- const entry = config.endpoints?.find((item) => item.context === 'wechat-mp');
9
- const appId = config.appId ?? entry?.appId ?? process.env.WECHAT_APP_ID;
10
- const appSecret = config.appSecret ?? entry?.appSecret ?? process.env.WECHAT_APP_SECRET;
11
- const token = config.token ?? entry?.token ?? process.env.WECHAT_TOKEN;
12
- if (!appId || !appSecret || !token) {
13
- throw new TypeError('WeChat MP adapter requires appId + appSecret + token (plugins.<key> or endpoints with context: wechat-mp)');
14
- }
15
- const id = (typeof config.id === 'string' && config.id)
16
- || (typeof entry?.id === 'string' && entry.id)
17
- || process.env.WECHAT_BOT_NAME
18
- || 'wechat-mp-bot';
19
- const path = config.path ?? entry?.path ?? '/wechat/webhook';
20
- const encodingAESKey = config.encodingAESKey ?? entry?.encodingAESKey;
21
- const encrypt = config.encrypt ?? entry?.encrypt ?? false;
22
- const encryptMode = config.encryptMode ?? entry?.encryptMode ?? (encrypt ? 'compatible' : 'plain');
23
- const replyMode = config.replyMode ?? entry?.replyMode ?? 'passive';
24
- const passiveReplyTimeoutMs = config.passiveReplyTimeoutMs
25
- ?? entry?.passiveReplyTimeoutMs
26
- ?? 4500;
7
+ export function resolveWeChatMpConfig(config) {
8
+ const id = requiredEndpointField(config.id, 'id');
9
+ const appId = requiredEndpointField(config.appId, 'appId');
10
+ const appSecret = requiredEndpointField(config.appSecret, 'appSecret');
11
+ const token = requiredEndpointField(config.token, 'token');
12
+ const path = optionalEndpointField(config.path) ?? '/wechat/webhook';
13
+ const encodingAESKey = optionalEndpointField(config.encodingAESKey);
14
+ const encrypt = config.encrypt ?? false;
15
+ const encryptMode = config.encryptMode ?? (encrypt ? 'compatible' : 'plain');
16
+ const replyMode = config.replyMode ?? 'passive';
17
+ const passiveReplyTimeoutMs = config.passiveReplyTimeoutMs ?? 4500;
27
18
  return {
28
19
  context: 'wechat-mp',
29
20
  id,
@@ -38,6 +29,16 @@ export function resolveWeChatMpConfig(config = {}) {
38
29
  passiveReplyTimeoutMs,
39
30
  };
40
31
  }
32
+ function requiredEndpointField(value, field) {
33
+ const resolved = optionalEndpointField(value);
34
+ if (!resolved) {
35
+ throw new TypeError(`WeChat MP endpoint requires a non-empty ${field}`);
36
+ }
37
+ return resolved;
38
+ }
39
+ function optionalEndpointField(value) {
40
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
41
+ }
41
42
  export function queryParam(value) {
42
43
  return value ?? '';
43
44
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * WeChat MP 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `wechat-mp.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `wechat-mp endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6
  export const wechatMpRuntimeStateToken = defineEndpointRuntimeStateToken('wechat-mp');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-wechat-mp",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "type": "module",
5
5
  "description": "Zhin.js WeChat Official Account adapter for Plugin Runtime (HTTP webhook)",
6
6
  "main": "./lib/index.js",
@@ -8,19 +8,20 @@
8
8
  "dependencies": {
9
9
  "axios": "^1.19.0",
10
10
  "xml2js": "^0.6.2",
11
- "@zhin.js/adapter": "1.1.12",
12
- "@zhin.js/core": "1.1.35",
13
- "@zhin.js/feature-kit": "1.1.0",
14
- "@zhin.js/host-http": "1.1.0",
15
- "@zhin.js/im-contract": "1.1.0",
16
- "@zhin.js/logger": "1.1.0"
11
+ "@zhin.js/adapter": "1.1.15",
12
+ "@zhin.js/core": "1.1.38",
13
+ "@zhin.js/feature-kit": "1.1.2",
14
+ "@zhin.js/host-http": "1.1.2",
15
+ "@zhin.js/im-contract": "1.1.2",
16
+ "@zhin.js/logger": "1.1.1",
17
+ "@zhin.js/skill": "1.1.2"
17
18
  },
18
19
  "peerDependencies": {
19
- "@zhin.js/adapter": "^1.1.12",
20
- "@zhin.js/command": "^1.1.0",
21
- "@zhin.js/core": "^1.1.35",
22
- "@zhin.js/host-http": "^1.1.0",
23
- "zhin.js": "^1.1.0"
20
+ "@zhin.js/adapter": "^1.1.15",
21
+ "@zhin.js/command": "^1.1.2",
22
+ "@zhin.js/core": "^1.1.38",
23
+ "@zhin.js/host-http": "^1.1.2",
24
+ "zhin.js": "^1.1.3"
24
25
  },
25
26
  "peerDependenciesMeta": {
26
27
  "@zhin.js/command": {
@@ -35,7 +36,7 @@
35
36
  "@types/xml2js": "^0.4.14",
36
37
  "typescript": "^6.0.3",
37
38
  "vitest": "^4.1.10",
38
- "zhin.js": "1.1.0"
39
+ "zhin.js": "1.1.3"
39
40
  },
40
41
  "keywords": [
41
42
  "zhin",
@@ -67,7 +68,7 @@
67
68
  "schema.json",
68
69
  "src",
69
70
  "lib",
70
- "agent",
71
+ "skills",
71
72
  "README.md",
72
73
  "CHANGELOG.md"
73
74
  ],
@@ -99,6 +100,10 @@
99
100
  {
100
101
  "package": "@zhin.js/command",
101
102
  "api": "^1.0.0"
103
+ },
104
+ {
105
+ "package": "@zhin.js/skill",
106
+ "api": "^1.0.0"
102
107
  }
103
108
  ],
104
109
  "plugins": []
package/plugin.js CHANGED
@@ -8,7 +8,7 @@ export default definePlugin({
8
8
  displayName: 'WeChat Official Account Adapter',
9
9
  },
10
10
  setup(context) {
11
- // 运行中 endpoint 注册表(wechat-mp.endpoint list 的"运行中"数据源)
11
+ // 运行中 endpoint 注册表(wechat-mp endpoint list 的"运行中"数据源)
12
12
  context.resources.provide(wechatMpRuntimeStateToken, createEndpointRuntimeState());
13
13
  },
14
14
  });
package/schema.json CHANGED
@@ -52,10 +52,11 @@
52
52
  },
53
53
  "endpoints": {
54
54
  "type": "array",
55
+ "minItems": 1,
55
56
  "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
56
57
  "items": {
57
58
  "type": "object",
58
- "additionalProperties": true,
59
+ "additionalProperties": false,
59
60
  "properties": {
60
61
  "master": {
61
62
  "type": [
@@ -91,6 +92,33 @@
91
92
  "type": "string",
92
93
  "description": "WeChat MP encoding AES key"
93
94
  },
95
+ "path": {
96
+ "type": "string"
97
+ },
98
+ "encrypt": {
99
+ "type": "boolean"
100
+ },
101
+ "encryptMode": {
102
+ "type": "string",
103
+ "enum": [
104
+ "plain",
105
+ "compatible",
106
+ "secure"
107
+ ]
108
+ },
109
+ "replyMode": {
110
+ "type": "string",
111
+ "enum": [
112
+ "passive",
113
+ "customer_service"
114
+ ]
115
+ },
116
+ "passiveReplyTimeoutMs": {
117
+ "type": "number"
118
+ },
119
+ "commandPrefix": {
120
+ "type": "string"
121
+ },
94
122
  "id": {
95
123
  "type": "string",
96
124
  "description": "WeChat MP bot name"
package/src/index.ts CHANGED
@@ -20,7 +20,7 @@ export {
20
20
  type TokenResponse,
21
21
  type WeChatAPIResponse,
22
22
  type WeChatMessage,
23
- type WeChatMpAdapterConfig,
23
+ type WeChatMpEndpointConfig,
24
24
  type WeChatWireSegment,
25
25
  } from './protocol.js';
26
26
 
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { readFile } from 'node:fs/promises';
8
8
  import { basename } from 'node:path';
9
- import { isMediaRef, type MediaRef } from '@zhin.js/core';
9
+ import { isMediaRef, type MediaRef } from '@zhin.js/im-contract';
10
10
 
11
11
  export interface MediaBinary {
12
12
  readonly data: Buffer;
package/src/protocol.ts CHANGED
@@ -8,11 +8,12 @@ import type { IncomingMessage } from 'node:http';
8
8
  import * as xml2js from 'xml2js';
9
9
  import type { ConversationRef } from '@zhin.js/im-contract';
10
10
 
11
- export interface WeChatMpAdapterConfig {
12
- readonly id?: string;
13
- readonly appId?: string;
14
- readonly appSecret?: string;
15
- readonly token?: string;
11
+ /** One endpoint config after AdapterIndex expands `plugins.<instanceKey>.endpoints`. */
12
+ export interface WeChatMpEndpointConfig {
13
+ readonly id: string;
14
+ readonly appId: string;
15
+ readonly appSecret: string;
16
+ readonly token: string;
16
17
  readonly encodingAESKey?: string;
17
18
  readonly path?: string;
18
19
  readonly encrypt?: boolean;
@@ -29,10 +30,6 @@ export interface WeChatMpAdapterConfig {
29
30
  readonly replyMode?: 'passive' | 'customer_service';
30
31
  /** 被动回复等待入站处理的最长时间(毫秒),默认 4500 */
31
32
  readonly passiveReplyTimeoutMs?: number;
32
- /** Transitional: legacy root `endpoints[]` with `context: wechat-mp`. */
33
- readonly endpoints?: ReadonlyArray<Partial<ResolvedWeChatMpConfig> & {
34
- readonly context?: string;
35
- }>;
36
33
  }
37
34
 
38
35
  export interface ResolvedWeChatMpConfig {
@@ -89,28 +86,17 @@ export interface WeChatWireSegment {
89
86
  readonly data?: Record<string, unknown>;
90
87
  }
91
88
 
92
- export function resolveWeChatMpConfig(config: WeChatMpAdapterConfig = {}): ResolvedWeChatMpConfig {
93
- const entry = config.endpoints?.find((item) => item.context === 'wechat-mp');
94
- const appId = config.appId ?? entry?.appId ?? process.env.WECHAT_APP_ID;
95
- const appSecret = config.appSecret ?? entry?.appSecret ?? process.env.WECHAT_APP_SECRET;
96
- const token = config.token ?? entry?.token ?? process.env.WECHAT_TOKEN;
97
- if (!appId || !appSecret || !token) {
98
- throw new TypeError(
99
- 'WeChat MP adapter requires appId + appSecret + token (plugins.<key> or endpoints with context: wechat-mp)',
100
- );
101
- }
102
- const id = (typeof config.id === 'string' && config.id)
103
- || (typeof entry?.id === 'string' && entry.id)
104
- || process.env.WECHAT_BOT_NAME
105
- || 'wechat-mp-bot';
106
- const path = config.path ?? entry?.path ?? '/wechat/webhook';
107
- const encodingAESKey = config.encodingAESKey ?? entry?.encodingAESKey;
108
- const encrypt = config.encrypt ?? entry?.encrypt ?? false;
109
- const encryptMode = config.encryptMode ?? entry?.encryptMode ?? (encrypt ? 'compatible' : 'plain');
110
- const replyMode = config.replyMode ?? entry?.replyMode ?? 'passive';
111
- const passiveReplyTimeoutMs = config.passiveReplyTimeoutMs
112
- ?? entry?.passiveReplyTimeoutMs
113
- ?? 4500;
89
+ export function resolveWeChatMpConfig(config: WeChatMpEndpointConfig): ResolvedWeChatMpConfig {
90
+ const id = requiredEndpointField(config.id, 'id');
91
+ const appId = requiredEndpointField(config.appId, 'appId');
92
+ const appSecret = requiredEndpointField(config.appSecret, 'appSecret');
93
+ const token = requiredEndpointField(config.token, 'token');
94
+ const path = optionalEndpointField(config.path) ?? '/wechat/webhook';
95
+ const encodingAESKey = optionalEndpointField(config.encodingAESKey);
96
+ const encrypt = config.encrypt ?? false;
97
+ const encryptMode = config.encryptMode ?? (encrypt ? 'compatible' : 'plain');
98
+ const replyMode = config.replyMode ?? 'passive';
99
+ const passiveReplyTimeoutMs = config.passiveReplyTimeoutMs ?? 4500;
114
100
  return {
115
101
  context: 'wechat-mp',
116
102
  id,
@@ -126,6 +112,21 @@ export function resolveWeChatMpConfig(config: WeChatMpAdapterConfig = {}): Resol
126
112
  };
127
113
  }
128
114
 
115
+ function requiredEndpointField(
116
+ value: unknown,
117
+ field: 'id' | 'appId' | 'appSecret' | 'token',
118
+ ): string {
119
+ const resolved = optionalEndpointField(value);
120
+ if (!resolved) {
121
+ throw new TypeError(`WeChat MP endpoint requires a non-empty ${field}`);
122
+ }
123
+ return resolved;
124
+ }
125
+
126
+ function optionalEndpointField(value: unknown): string | undefined {
127
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
128
+ }
129
+
129
130
  export function queryParam(value: string | null | undefined): string {
130
131
  return value ?? '';
131
132
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * WeChat MP 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `wechat-mp.endpoint` 命令共享(同一 owner generation)。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `wechat-mp endpoint` 命令共享(同一 owner generation)。
4
4
  */
5
5
  import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
6
 
@@ -1,33 +0,0 @@
1
- ---
2
- name: wechat-mp
3
- platforms:
4
- - wechat-mp
5
- description: >-
6
- 微信公众号适配器:通过 Webhook 接收用户消息、自动管理 Access Token、
7
- 支持 XML 消息解析和 AES 消息加解密。纯消息通道,无额外 AI 工具。
8
- 当上下文涉及微信公众号消息交互时可参考此技能了解能力边界。
9
- keywords:
10
- - wechat
11
- - weixin
12
- - adapter:wechat-mp
13
- - 微信
14
- - 公众号
15
- - official-account
16
- - mp
17
- tags:
18
- - wechat
19
- - adapter
20
- - social
21
- tools: []
22
- ---
23
-
24
- # 微信公众号适配器
25
-
26
- 纯消息通道,收发微信公众号用户消息。无 AI 工具可调用。
27
-
28
- ## 能力
29
-
30
- - 通过 HTTP 回调接收微信推送的用户消息事件
31
- - 自动刷新 Access Token,无需手动维护
32
- - 支持签名验证和 AES 消息加解密
33
- - 回复通过常规 `$reply` 发送
@@ -1,3 +0,0 @@
1
- import { wechatMpEndpointCommands } from '../../../src/wechat-mp-endpoint-commands.js';
2
-
3
- export default wechatMpEndpointCommands.add;
@@ -1,3 +0,0 @@
1
- import { wechatMpEndpointCommands } from '../../src/wechat-mp-endpoint-commands.js';
2
-
3
- export default wechatMpEndpointCommands.list;
@@ -1,3 +0,0 @@
1
- import { wechatMpEndpointCommands } from '../../../src/wechat-mp-endpoint-commands.js';
2
-
3
- export default wechatMpEndpointCommands.remove;
@@ -1 +0,0 @@
1
- export declare const wechatMpEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;