@zhin.js/adapter-wecom 5.0.0 → 5.0.1

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,27 @@
1
1
  # @zhin.js/adapter-wecom
2
2
 
3
+ ## 5.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 1fc78bc: Unify native platform Client access behind the literal `adapter` discriminant. Handlers infer both native events and Clients, while command, inbound/outbound middleware, and both Agent tool authoring surfaces expose the exact operation-scoped Client through a lazy `$client` getter. Definitions without `adapter` keep `$client` typed as `unknown`, and runtime dispatch rejects adapter mismatches before resolving the Client. Bundled platform tools now use this single path instead of model-provided endpoint ids and adapter-specific dependency wrappers. Every adapter registers one Client/EventMap contract, and protocol adapters including NapCat, Milky, OneBot and Satori now produce transport-independent Client objects rather than letting Endpoint instances impersonate Clients.
8
+ - Updated dependencies [e9c6a73]
9
+ - Updated dependencies [4e8117c]
10
+ - Updated dependencies [902fa35]
11
+ - Updated dependencies [54bfd6b]
12
+ - Updated dependencies [12025ee]
13
+ - Updated dependencies [09b14d6]
14
+ - Updated dependencies [1fc78bc]
15
+ - @zhin.js/agent@1.1.16
16
+ - @zhin.js/adapter@1.2.1
17
+ - @zhin.js/core@1.5.14
18
+ - @zhin.js/host-http@1.0.13
19
+ - @zhin.js/command@1.0.16
20
+ - @zhin.js/logger@1.0.77
21
+ - zhin.js@6.0.14
22
+ - @zhin.js/feature-kit@1.0.13
23
+ - @zhin.js/permission@1.0.4
24
+
3
25
  ## 5.0.0
4
26
 
5
27
  ### Patch Changes
package/README.md CHANGED
@@ -18,7 +18,7 @@ pnpm add @zhin.js/adapter-wecom
18
18
  ## Plugin Runtime
19
19
 
20
20
  - `@zhin.js/adapter` — 约定式 `adapters/wecom.ts`(`defineAdapter`)
21
- - `@zhin.js/core` — `messageGatewayToken` 入站/出站
21
+ - `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
22
22
  - `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
23
23
  - `zhin.js` — `plugin.ts`(`definePlugin`)
24
24
  - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
package/adapters/wecom.js CHANGED
@@ -4,7 +4,6 @@
4
4
  * Implementation lives under `src/` (endpoint / webhook / protocol).
5
5
  */
6
6
  import { defineAdapter } from 'zhin.js/adapter';
7
- import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
8
7
  import { httpHostToken } from '@zhin.js/host-http';
9
8
  import { WecomEndpoint } from "../lib/endpoint.js";
10
9
  import { resolveWecomConfig, } from "../lib/protocol.js";
@@ -28,8 +27,6 @@ export default defineAdapter({
28
27
  });
29
28
  return new WecomEndpoint({
30
29
  id: context.id,
31
- gateway: context.use(messageGatewayToken),
32
- sideEvents: context.use(sideEventGatewayToken),
33
30
  http: context.use(httpHostToken),
34
31
  config,
35
32
  });
package/adapters/wecom.ts CHANGED
@@ -3,7 +3,6 @@
3
3
  * Implementation lives under `src/` (endpoint / webhook / protocol).
4
4
  */
5
5
  import { defineAdapter } from 'zhin.js/adapter';
6
- import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
7
6
  import { httpHostToken } from '@zhin.js/host-http';
8
7
  import { WecomEndpoint } from '../src/endpoint.js';
9
8
  import {
@@ -33,8 +32,6 @@ export default defineAdapter<WecomAdapterConfig>({
33
32
  });
34
33
  return new WecomEndpoint({
35
34
  id: context.id,
36
- gateway: context.use(messageGatewayToken),
37
- sideEvents: context.use(sideEventGatewayToken),
38
35
  http: context.use(httpHostToken),
39
36
  config,
40
37
  });
@@ -1,16 +1,14 @@
1
1
  import { defineAgentTool } from '@zhin.js/agent/tools';
2
2
  import { z } from 'zod';
3
- import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
- export default defineAgentTool<{ endpoint_id: string; dept_id: string }>({
3
+ export default defineAgentTool<{ dept_id: string }>({
5
4
  description: '获取企业微信部门用户列表',
6
5
  inputSchema: z.object({
7
- endpoint_id: z.string().describe('Endpoint 名称'),
8
6
  dept_id: z.string().describe('部门 ID'),
9
7
  }),
10
- platforms: ['wecom'],
8
+ adapter: 'wecom',
11
9
  tags: ['wecom'],
12
- async execute({ endpoint_id, dept_id }: { endpoint_id: string; dept_id: string }) {
13
- const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
10
+ async execute({ dept_id }: { dept_id: string }, context) {
11
+ const endpoint = context.$client;
14
12
  const users = await endpoint.getDepartmentUsers(Number(dept_id));
15
13
  return { users, count: users.length };
16
14
  },
@@ -1,16 +1,14 @@
1
1
  import { defineAgentTool } from '@zhin.js/agent/tools';
2
2
  import { z } from 'zod';
3
- import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
- export default defineAgentTool<{ endpoint_id: string; user_id: string }>({
3
+ export default defineAgentTool<{ user_id: string }>({
5
4
  description: '获取企业微信用户信息',
6
5
  inputSchema: z.object({
7
- endpoint_id: z.string().describe('Endpoint 名称'),
8
6
  user_id: z.string().describe('用户 ID'),
9
7
  }),
10
- platforms: ['wecom'],
8
+ adapter: 'wecom',
11
9
  tags: ['wecom'],
12
- async execute({ endpoint_id, user_id }: { endpoint_id: string; user_id: string }) {
13
- const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
10
+ async execute({ user_id }: { user_id: string }, context) {
11
+ const endpoint = context.$client;
14
12
  return await endpoint.getUserInfo(user_id);
15
13
  },
16
14
  });
@@ -1,16 +1,14 @@
1
1
  import { defineAgentTool } from '@zhin.js/agent/tools';
2
2
  import { z } from 'zod';
3
- import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
- export default defineAgentTool<{ endpoint_id: string; dept_id?: string }>({
3
+ export default defineAgentTool<{ dept_id?: string }>({
5
4
  description: '获取企业微信部门列表',
6
5
  inputSchema: z.object({
7
- endpoint_id: z.string().describe('Endpoint 名称'),
8
6
  dept_id: z.string().optional().describe('父部门 ID,默认 1(根部门)'),
9
7
  }),
10
- platforms: ['wecom'],
8
+ adapter: 'wecom',
11
9
  tags: ['wecom'],
12
- async execute({ endpoint_id, dept_id }: { endpoint_id: string; dept_id?: string }) {
13
- const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
10
+ async execute({ dept_id }: { dept_id?: string }, context) {
11
+ const endpoint = context.$client;
14
12
  const departments = await endpoint.getDepartmentList(Number(dept_id) || 1);
15
13
  return { departments, count: departments.length };
16
14
  },
@@ -1,17 +1,15 @@
1
1
  import { defineAgentTool } from '@zhin.js/agent/tools';
2
2
  import { z } from 'zod';
3
- import { getWecomAgentDeps } from '../../src/wecom-agent-deps.js';
4
- export default defineAgentTool<{ endpoint_id: string; user_id: string; content: string }>({
3
+ export default defineAgentTool<{ user_id: string; content: string }>({
5
4
  description: '向指定企业微信用户发送文本消息',
6
5
  inputSchema: z.object({
7
- endpoint_id: z.string().describe('Endpoint 名称'),
8
6
  user_id: z.string().describe('用户 ID'),
9
7
  content: z.string().describe('消息内容'),
10
8
  }),
11
- platforms: ['wecom'],
9
+ adapter: 'wecom',
12
10
  tags: ['wecom'],
13
- async execute({ endpoint_id, user_id, content }: { endpoint_id: string; user_id: string; content: string }) {
14
- const endpoint = getWecomAgentDeps().getEndpoint(endpoint_id);
11
+ async execute({ user_id, content }: { user_id: string; content: string }, context) {
12
+ const endpoint = context.$client;
15
13
  const success = await endpoint.sendTextMessage(user_id, content);
16
14
  return { success, message: success ? '消息已发送' : '发送失败' };
17
15
  },
@@ -0,0 +1,12 @@
1
+ import type { WecomClient } from './endpoint.js';
2
+ import type { WecomMessage } from './protocol.js';
3
+ export type WecomClientEventMap = Record<string, WecomMessage>;
4
+ declare module '@zhin.js/feature-kit' {
5
+ interface AdapterClientRegistry {
6
+ readonly wecom: {
7
+ readonly client: WecomClient;
8
+ readonly events: WecomClientEventMap;
9
+ };
10
+ }
11
+ }
12
+ export declare const wecomClient: import("@zhin.js/adapter").EndpointClientToken<WecomClient, WecomClientEventMap>;
package/lib/client.js ADDED
@@ -0,0 +1,2 @@
1
+ import { defineEndpointClient } from 'zhin.js/adapter';
2
+ export const wecomClient = defineEndpointClient('wecom');
package/lib/endpoint.d.ts CHANGED
@@ -1,11 +1,11 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
1
2
  /**
2
3
  * WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
3
4
  */
4
- import { type EndpointControl, type EndpointInstance, type EndpointSendRequest } from 'zhin.js/adapter';
5
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
5
+ import { type EndpointControl, type EndpointSendRequest } from 'zhin.js/adapter';
6
6
  import type { HttpHost } from '@zhin.js/host-http';
7
7
  import type { CapabilityId } from 'zhin.js';
8
- import { type ResolvedWecomConfig, type WecomApiResponse, type WecomMessage } from './protocol.js';
8
+ import { type ResolvedWecomConfig, type WecomMessage } from './protocol.js';
9
9
  export type WecomFetch = (url: string, init?: {
10
10
  readonly method?: string;
11
11
  readonly headers?: Record<string, string>;
@@ -18,19 +18,33 @@ export type WecomFetch = (url: string, init?: {
18
18
  }>;
19
19
  export interface WecomEndpointOptions {
20
20
  readonly id: CapabilityId;
21
- readonly gateway: MessageGateway;
22
- readonly sideEvents?: SideEventGateway;
23
21
  readonly http: HttpHost;
24
22
  readonly config: ResolvedWecomConfig;
25
23
  readonly fetch?: WecomFetch;
26
24
  }
25
+ export interface WecomClientApi {
26
+ getUserInfo(userId: string): Promise<unknown>;
27
+ getDepartmentUsers(deptId: number): Promise<unknown[]>;
28
+ getDepartmentList(deptId?: number): Promise<unknown[]>;
29
+ sendTextMessage(userId: string, content: string): Promise<boolean>;
30
+ }
31
+ /** WeCom API surface exposed to plugin events without Endpoint lifecycle methods. */
32
+ export declare class WecomClient implements WecomClientApi {
33
+ readonly api: WecomClientApi;
34
+ constructor(api: WecomClientApi);
35
+ getUserInfo: (userId: string) => Promise<unknown>;
36
+ getDepartmentUsers: (deptId: number) => Promise<unknown[]>;
37
+ getDepartmentList: (deptId?: number) => Promise<unknown[]>;
38
+ sendTextMessage: (userId: string, content: string) => Promise<boolean>;
39
+ }
27
40
  /**
28
41
  * 企业微信服务端 API 无 bot 群列表/群成员列表接口(客户群接口属「客户联系」
29
42
  * 独立授权域,非 bot 社交面),好友/频道概念亦不存在;
30
43
  * 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
31
44
  */
32
- export declare class WecomEndpoint implements EndpointInstance {
45
+ export declare class WecomEndpoint extends Endpoint<WecomClient> {
33
46
  #private;
47
+ readonly client: WecomClient;
34
48
  readonly control: EndpointControl;
35
49
  constructor(options: WecomEndpointOptions);
36
50
  /** Used by webhook handler. */
@@ -44,8 +58,4 @@ export declare class WecomEndpoint implements EndpointInstance {
44
58
  recallMessage(messageId: string): Promise<void>;
45
59
  /** Test / internal: admit a parsed message when open (non-webhook path). */
46
60
  admit(msg: WecomMessage): void;
47
- getUserInfo(userId: string): Promise<WecomApiResponse | null>;
48
- getDepartmentUsers(deptId: number): Promise<unknown[]>;
49
- getDepartmentList(deptId?: number): Promise<unknown[]>;
50
- sendTextMessage(userId: string, content: string): Promise<boolean>;
51
61
  }
package/lib/endpoint.js CHANGED
@@ -1,19 +1,36 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
1
2
  /**
2
3
  * WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
3
4
  */
4
5
  import { createRecallEndpointControl, } from 'zhin.js/adapter';
5
6
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
6
- import { registerWecomAgentEndpoint } from './wecom-agent-deps.js';
7
7
  import { buildMediaUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
8
8
  import { buildSendRequestBody, formatInboundContent, formatOutboundBody, resolveChatType, wecomInboundConversation, } from './protocol.js';
9
9
  import { registerWecomWebhookRoutes } from './webhook.js';
10
10
  import { receiveWecomSideEvent } from './side-event-dispatch.js';
11
+ /** WeCom API surface exposed to plugin events without Endpoint lifecycle methods. */
12
+ export class WecomClient {
13
+ api;
14
+ constructor(api) {
15
+ this.api = api;
16
+ }
17
+ getUserInfo = (userId) => this.api.getUserInfo(userId);
18
+ getDepartmentUsers = (deptId) => this.api.getDepartmentUsers(deptId);
19
+ getDepartmentList = (deptId) => this.api.getDepartmentList(deptId);
20
+ sendTextMessage = (userId, content) => this.api.sendTextMessage(userId, content);
21
+ }
11
22
  /**
12
23
  * 企业微信服务端 API 无 bot 群列表/群成员列表接口(客户群接口属「客户联系」
13
24
  * 独立授权域,非 bot 社交面),好友/频道概念亦不存在;
14
25
  * 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
15
26
  */
16
- export class WecomEndpoint {
27
+ export class WecomEndpoint extends Endpoint {
28
+ client = new WecomClient({
29
+ getUserInfo: (userId) => this.#getUserInfo(userId),
30
+ getDepartmentUsers: (deptId) => this.#getDepartmentUsers(deptId),
31
+ getDepartmentList: (deptId) => this.#getDepartmentList(deptId),
32
+ sendTextMessage: (userId, content) => this.#sendTextMessage(userId, content),
33
+ });
17
34
  #logger;
18
35
  #options;
19
36
  control = createRecallEndpointControl((id) => this.recallMessage(id));
@@ -23,8 +40,8 @@ export class WecomEndpoint {
23
40
  #refreshPromise = null;
24
41
  #open = false;
25
42
  #started = false;
26
- #unregisterAgent;
27
43
  constructor(options) {
44
+ super();
28
45
  this.#logger = getAdapterLogger('wecom', options.config.id);
29
46
  this.#options = options;
30
47
  this.#fetch = options.fetch ?? globalThis.fetch;
@@ -42,7 +59,6 @@ export class WecomEndpoint {
42
59
  this.#started = true;
43
60
  try {
44
61
  await this.#refreshAccessToken();
45
- this.#unregisterAgent = registerWecomAgentEndpoint(this.#options.config.id, this);
46
62
  this.#routeReleases.push(...registerWecomWebhookRoutes(this.#options.http, this));
47
63
  this.#logger.debug(formatCompact({
48
64
  endpoint: this.#options.config.id,
@@ -66,8 +82,6 @@ export class WecomEndpoint {
66
82
  this.#open = false;
67
83
  for (const release of this.#routeReleases.splice(0))
68
84
  release();
69
- this.#unregisterAgent?.();
70
- this.#unregisterAgent = undefined;
71
85
  this.#started = false;
72
86
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
73
87
  }
@@ -157,12 +171,19 @@ export class WecomEndpoint {
157
171
  admit(msg) {
158
172
  if (!this.#open)
159
173
  return;
160
- if (receiveWecomSideEvent(this.#options.sideEvents, String(this.#options.id), this.#options.config.id, msg, this.#logger)) {
174
+ void this.emitPlatform(msg.Event || msg.MsgType || 'event', msg).catch((error) => {
175
+ this.#logger.warn(formatCompact({
176
+ op: 'wecom_platform_event_failed',
177
+ event: msg.Event || msg.MsgType,
178
+ error: error instanceof Error ? error.message : String(error),
179
+ }));
180
+ });
181
+ if (receiveWecomSideEvent((name, payload) => this.emit(name, payload), String(this.#options.id), this.#options.config.id, msg, this.#logger)) {
161
182
  return;
162
183
  }
163
184
  const chatType = resolveChatType(msg.FromUserName);
164
185
  const conversation = wecomInboundConversation(String(this.#options.id), msg);
165
- void this.#options.gateway.receive({
186
+ void this.emit('message.receive', {
166
187
  conversation,
167
188
  message: { conversation, id: msg.MsgId || `${msg.CreateTime}` },
168
189
  content: formatInboundContent(msg),
@@ -183,7 +204,7 @@ export class WecomEndpoint {
183
204
  }));
184
205
  });
185
206
  }
186
- async getUserInfo(userId) {
207
+ async #getUserInfo(userId) {
187
208
  try {
188
209
  const data = await this.#request('/cgi-bin/user/get', {
189
210
  params: { userid: userId },
@@ -197,7 +218,7 @@ export class WecomEndpoint {
197
218
  return null;
198
219
  }
199
220
  }
200
- async getDepartmentUsers(deptId) {
221
+ async #getDepartmentUsers(deptId) {
201
222
  try {
202
223
  const data = await this.#request('/cgi-bin/user/simplelist', {
203
224
  params: { department_id: deptId },
@@ -211,7 +232,7 @@ export class WecomEndpoint {
211
232
  return [];
212
233
  }
213
234
  }
214
- async getDepartmentList(deptId = 1) {
235
+ async #getDepartmentList(deptId = 1) {
215
236
  try {
216
237
  const data = await this.#request('/cgi-bin/department/list', {
217
238
  params: { id: deptId },
@@ -225,7 +246,7 @@ export class WecomEndpoint {
225
246
  return [];
226
247
  }
227
248
  }
228
- async sendTextMessage(userId, content) {
249
+ async #sendTextMessage(userId, content) {
229
250
  try {
230
251
  const endpointKey = String(this.#options.id);
231
252
  await this.send({
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { buildSendRequestBody, decryptMessage, extractEncryptFromXml, formatInboundContent, formatOutboundBody, getAesKey, normalizeEchostrParam, normalizeWebhookPath, parseXmlMessage, queryParam, readTextBody, resolveChatType, resolveWecomConfig, verifySignature, type AccessToken, type ResolvedWecomConfig, type WecomAdapterConfig, type WecomApiResponse, type WecomMessage, type WecomSendBody, type WecomWireSegment, } from './protocol.js';
2
- export { getWecomAgentDeps, registerWecomAgentEndpoint, setWecomAgentDeps, type WecomAgentDeps, type WecomAgentEndpoint, } from './wecom-agent-deps.js';
2
+ export { wecomClient, type WecomClientEventMap } from './client.js';
3
3
  export { checkWecomPlatformPermit, normalizeWecomSenderForPermit, platformPermit, wecomGroupPermitResolver, } from './platform-permit.js';
4
- export { WecomEndpoint, type WecomEndpointOptions, type WecomFetch, } from './endpoint.js';
4
+ export { WecomClient, WecomEndpoint, type WecomClientApi, type WecomEndpointOptions, type WecomFetch, } from './endpoint.js';
5
5
  export { buildMediaUploadForm, readOutboundImageMedia, resolveMediaBinary, type MediaBinary, type WecomMediaUploadResult, } from './media-upload.js';
6
6
  export { registerWecomWebhookRoutes, handleWecomVerificationRequest, handleWecomWebhookRequest, type WecomWebhookHandler, } from './webhook.js';
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { buildSendRequestBody, decryptMessage, extractEncryptFromXml, formatInboundContent, formatOutboundBody, getAesKey, normalizeEchostrParam, normalizeWebhookPath, parseXmlMessage, queryParam, readTextBody, resolveChatType, resolveWecomConfig, verifySignature, } from './protocol.js';
2
- export { getWecomAgentDeps, registerWecomAgentEndpoint, setWecomAgentDeps, } from './wecom-agent-deps.js';
2
+ export { wecomClient } from './client.js';
3
3
  export { checkWecomPlatformPermit, normalizeWecomSenderForPermit, platformPermit, wecomGroupPermitResolver, } from './platform-permit.js';
4
- export { WecomEndpoint, } from './endpoint.js';
4
+ export { WecomClient, WecomEndpoint, } from './endpoint.js';
5
5
  export { buildMediaUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
6
6
  export { registerWecomWebhookRoutes, handleWecomVerificationRequest, handleWecomWebhookRequest, } from './webhook.js';
package/lib/protocol.d.ts CHANGED
@@ -84,7 +84,7 @@ export declare function verifySignature(token: string, timestamp: string, nonce:
84
84
  export declare function decryptMessage(encrypted: string, encodingAESKey: string, corpId: string): string | null;
85
85
  export declare function extractEncryptFromXml(xml: string): string | null;
86
86
  export declare function parseXmlMessage(xml: string): WecomMessage | null;
87
- /** Build inbound text for MessageGateway.receive. */
87
+ /** Build inbound text for OutboundMessageService.receive. */
88
88
  export declare function formatInboundContent(msg: WecomMessage): string;
89
89
  export declare function resolveChatType(fromUserName: string): 'group' | 'private';
90
90
  /**
package/lib/protocol.js CHANGED
@@ -128,7 +128,7 @@ export function parseXmlMessage(xml) {
128
128
  return null;
129
129
  }
130
130
  }
131
- /** Build inbound text for MessageGateway.receive. */
131
+ /** Build inbound text for OutboundMessageService.receive. */
132
132
  export function formatInboundContent(msg) {
133
133
  switch (msg.MsgType) {
134
134
  case 'text':
@@ -1,4 +1,4 @@
1
- import type { SideEventGateway } from '@zhin.js/core/runtime';
1
+ import type { EndpointEventEmitter } from 'zhin.js/adapter';
2
2
  import { type getAdapterLogger } from '@zhin.js/logger';
3
3
  import { type WecomMessage } from './protocol.js';
4
- export declare function receiveWecomSideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, configId: string, msg: WecomMessage, logger: ReturnType<typeof getAdapterLogger>): boolean;
4
+ export declare function receiveWecomSideEvent(emit: EndpointEventEmitter, endpointKey: string, configId: string, msg: WecomMessage, logger: ReturnType<typeof getAdapterLogger>): boolean;
@@ -13,12 +13,12 @@ function mapWecomEventParts(eventName) {
13
13
  return { scene_type: 'wecom', sub_type: eventName || 'unknown' };
14
14
  }
15
15
  }
16
- export function receiveWecomSideEvent(sideEvents, endpointKey, configId, msg, logger) {
17
- if (!sideEvents || msg.MsgType !== 'event')
16
+ export function receiveWecomSideEvent(emit, endpointKey, configId, msg, logger) {
17
+ if (msg.MsgType !== 'event')
18
18
  return false;
19
19
  const eventName = msg.Event ?? 'unknown';
20
20
  if (eventName === 'enter_agent') {
21
- void sideEvents.receiveSystem(buildSystem(msg, {
21
+ void emit('system.receive', buildSystem(msg, {
22
22
  $id: `wecom:enter_agent:${msg.FromUserName}:${msg.CreateTime ?? Date.now()}`,
23
23
  $adapter: 'wecom',
24
24
  $endpoint: configId,
@@ -41,7 +41,7 @@ export function receiveWecomSideEvent(sideEvents, endpointKey, configId, msg, lo
41
41
  if (!parts)
42
42
  return false;
43
43
  const sceneType = resolveChatType(msg.FromUserName);
44
- void sideEvents.receiveNotice(buildNotice(msg, {
44
+ void emit('notice.receive', buildNotice(msg, {
45
45
  $id: `wecom:${eventName}:${msg.FromUserName}:${msg.CreateTime ?? Date.now()}`,
46
46
  $adapter: 'wecom',
47
47
  $endpoint: configId,
@@ -1 +1 @@
1
- export declare const wecomEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
1
+ export declare const wecomEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-wecom",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "Zhin.js WeCom (企业微信) adapter for Plugin Runtime (HTTP webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,21 +34,22 @@
34
34
  "directory": "plugins/adapters/wecom"
35
35
  },
36
36
  "dependencies": {
37
- "@zhin.js/adapter": "1.2.0",
38
- "@zhin.js/core": "1.5.13",
39
- "@zhin.js/host-http": "1.0.12",
37
+ "@zhin.js/adapter": "1.2.1",
38
+ "@zhin.js/core": "1.5.14",
39
+ "@zhin.js/feature-kit": "1.0.13",
40
+ "@zhin.js/host-http": "1.0.13",
40
41
  "@zhin.js/im-contract": "1.0.4",
41
- "@zhin.js/logger": "1.0.76"
42
+ "@zhin.js/logger": "1.0.77"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "zod": "^4.0.0",
45
- "@zhin.js/adapter": "1.2.0",
46
- "@zhin.js/agent": "1.1.15",
47
- "@zhin.js/command": "1.0.15",
48
- "@zhin.js/core": "1.5.13",
49
- "@zhin.js/host-http": "1.0.12",
50
- "@zhin.js/permission": "1.0.3",
51
- "zhin.js": "6.0.13"
46
+ "@zhin.js/adapter": "1.2.1",
47
+ "@zhin.js/agent": "1.1.16",
48
+ "@zhin.js/command": "1.0.16",
49
+ "@zhin.js/core": "1.5.14",
50
+ "@zhin.js/host-http": "1.0.13",
51
+ "@zhin.js/permission": "1.0.4",
52
+ "zhin.js": "6.0.14"
52
53
  },
53
54
  "peerDependenciesMeta": {
54
55
  "@zhin.js/agent": {
@@ -69,8 +70,8 @@
69
70
  "typescript": "^6.0.3",
70
71
  "vitest": "^4.1.10",
71
72
  "zod": "^4.4.3",
72
- "@zhin.js/agent": "1.1.15",
73
- "zhin.js": "6.0.13"
73
+ "@zhin.js/agent": "1.1.16",
74
+ "zhin.js": "6.0.14"
74
75
  },
75
76
  "files": [
76
77
  "adapters",
package/src/client.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineEndpointClient } from 'zhin.js/adapter';
2
+ import type { WecomClient } from './endpoint.js';
3
+ import type { WecomMessage } from './protocol.js';
4
+
5
+ export type WecomClientEventMap = Record<string, WecomMessage>;
6
+
7
+ declare module '@zhin.js/feature-kit' {
8
+ interface AdapterClientRegistry {
9
+ readonly wecom: {
10
+ readonly client: WecomClient;
11
+ readonly events: WecomClientEventMap;
12
+ };
13
+ }
14
+ }
15
+
16
+ export const wecomClient = defineEndpointClient<WecomClient, WecomClientEventMap>('wecom');
package/src/endpoint.ts CHANGED
@@ -1,17 +1,15 @@
1
+ import { Endpoint } from 'zhin.js/adapter';
1
2
  /**
2
3
  * WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
3
4
  */
4
5
  import {
5
6
  createRecallEndpointControl,
6
7
  type EndpointControl,
7
- type EndpointInstance,
8
8
  type EndpointSendRequest,
9
9
  } from 'zhin.js/adapter';
10
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
11
10
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
12
11
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
13
12
  import type { CapabilityId } from 'zhin.js';
14
- import { registerWecomAgentEndpoint } from './wecom-agent-deps.js';
15
13
  import {
16
14
  buildMediaUploadForm,
17
15
  readOutboundImageMedia,
@@ -48,19 +46,39 @@ export type WecomFetch = (
48
46
 
49
47
  export interface WecomEndpointOptions {
50
48
  readonly id: CapabilityId;
51
- readonly gateway: MessageGateway;
52
- readonly sideEvents?: SideEventGateway;
53
49
  readonly http: HttpHost;
54
50
  readonly config: ResolvedWecomConfig;
55
51
  readonly fetch?: WecomFetch;
56
52
  }
57
53
 
54
+ export interface WecomClientApi {
55
+ getUserInfo(userId: string): Promise<unknown>;
56
+ getDepartmentUsers(deptId: number): Promise<unknown[]>;
57
+ getDepartmentList(deptId?: number): Promise<unknown[]>;
58
+ sendTextMessage(userId: string, content: string): Promise<boolean>;
59
+ }
60
+
61
+ /** WeCom API surface exposed to plugin events without Endpoint lifecycle methods. */
62
+ export class WecomClient implements WecomClientApi {
63
+ constructor(readonly api: WecomClientApi) {}
64
+ getUserInfo = (userId: string) => this.api.getUserInfo(userId);
65
+ getDepartmentUsers = (deptId: number) => this.api.getDepartmentUsers(deptId);
66
+ getDepartmentList = (deptId?: number) => this.api.getDepartmentList(deptId);
67
+ sendTextMessage = (userId: string, content: string) => this.api.sendTextMessage(userId, content);
68
+ }
69
+
58
70
  /**
59
71
  * 企业微信服务端 API 无 bot 群列表/群成员列表接口(客户群接口属「客户联系」
60
72
  * 独立授权域,非 bot 社交面),好友/频道概念亦不存在;
61
73
  * 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
62
74
  */
63
- export class WecomEndpoint implements EndpointInstance {
75
+ export class WecomEndpoint extends Endpoint<WecomClient> {
76
+ readonly client = new WecomClient({
77
+ getUserInfo: (userId) => this.#getUserInfo(userId),
78
+ getDepartmentUsers: (deptId) => this.#getDepartmentUsers(deptId),
79
+ getDepartmentList: (deptId) => this.#getDepartmentList(deptId),
80
+ sendTextMessage: (userId, content) => this.#sendTextMessage(userId, content),
81
+ });
64
82
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
65
83
 
66
84
  readonly #options: WecomEndpointOptions;
@@ -71,9 +89,9 @@ export class WecomEndpoint implements EndpointInstance {
71
89
  #refreshPromise: Promise<string> | null = null;
72
90
  #open = false;
73
91
  #started = false;
74
- #unregisterAgent?: () => void;
75
92
 
76
93
  constructor(options: WecomEndpointOptions) {
94
+ super();
77
95
  this.#logger = getAdapterLogger('wecom', options.config.id);
78
96
  this.#options = options;
79
97
  this.#fetch = options.fetch ?? globalThis.fetch;
@@ -93,7 +111,6 @@ export class WecomEndpoint implements EndpointInstance {
93
111
  this.#started = true;
94
112
  try {
95
113
  await this.#refreshAccessToken();
96
- this.#unregisterAgent = registerWecomAgentEndpoint(this.#options.config.id, this);
97
114
  this.#routeReleases.push(...registerWecomWebhookRoutes(this.#options.http, this));
98
115
  this.#logger.debug(formatCompact({
99
116
  endpoint: this.#options.config.id,
@@ -118,8 +135,6 @@ export class WecomEndpoint implements EndpointInstance {
118
135
  async stop(): Promise<void> {
119
136
  this.#open = false;
120
137
  for (const release of this.#routeReleases.splice(0)) release();
121
- this.#unregisterAgent?.();
122
- this.#unregisterAgent = undefined;
123
138
  this.#started = false;
124
139
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
125
140
  }
@@ -210,8 +225,15 @@ export class WecomEndpoint implements EndpointInstance {
210
225
  /** Test / internal: admit a parsed message when open (non-webhook path). */
211
226
  admit(msg: WecomMessage): void {
212
227
  if (!this.#open) return;
228
+ void this.emitPlatform(msg.Event || msg.MsgType || 'event', msg).catch((error) => {
229
+ this.#logger.warn(formatCompact({
230
+ op: 'wecom_platform_event_failed',
231
+ event: msg.Event || msg.MsgType,
232
+ error: error instanceof Error ? error.message : String(error),
233
+ }));
234
+ });
213
235
  if (receiveWecomSideEvent(
214
- this.#options.sideEvents,
236
+ (name, payload) => this.emit(name, payload),
215
237
  String(this.#options.id),
216
238
  this.#options.config.id,
217
239
  msg,
@@ -221,7 +243,7 @@ export class WecomEndpoint implements EndpointInstance {
221
243
  }
222
244
  const chatType = resolveChatType(msg.FromUserName);
223
245
  const conversation = wecomInboundConversation(String(this.#options.id), msg);
224
- void this.#options.gateway.receive({
246
+ void this.emit('message.receive', {
225
247
  conversation,
226
248
  message: { conversation, id: msg.MsgId || `${msg.CreateTime}` },
227
249
  content: formatInboundContent(msg),
@@ -243,7 +265,7 @@ export class WecomEndpoint implements EndpointInstance {
243
265
  });
244
266
  }
245
267
 
246
- async getUserInfo(userId: string): Promise<WecomApiResponse | null> {
268
+ async #getUserInfo(userId: string): Promise<WecomApiResponse | null> {
247
269
  try {
248
270
  const data = await this.#request('/cgi-bin/user/get', {
249
271
  params: { userid: userId },
@@ -256,7 +278,7 @@ export class WecomEndpoint implements EndpointInstance {
256
278
  }
257
279
  }
258
280
 
259
- async getDepartmentUsers(deptId: number): Promise<unknown[]> {
281
+ async #getDepartmentUsers(deptId: number): Promise<unknown[]> {
260
282
  try {
261
283
  const data = await this.#request('/cgi-bin/user/simplelist', {
262
284
  params: { department_id: deptId },
@@ -269,7 +291,7 @@ export class WecomEndpoint implements EndpointInstance {
269
291
  }
270
292
  }
271
293
 
272
- async getDepartmentList(deptId: number = 1): Promise<unknown[]> {
294
+ async #getDepartmentList(deptId: number = 1): Promise<unknown[]> {
273
295
  try {
274
296
  const data = await this.#request('/cgi-bin/department/list', {
275
297
  params: { id: deptId },
@@ -282,7 +304,7 @@ export class WecomEndpoint implements EndpointInstance {
282
304
  }
283
305
  }
284
306
 
285
- async sendTextMessage(userId: string, content: string): Promise<boolean> {
307
+ async #sendTextMessage(userId: string, content: string): Promise<boolean> {
286
308
  try {
287
309
  const endpointKey = String(this.#options.id);
288
310
  await this.send({
package/src/index.ts CHANGED
@@ -22,13 +22,7 @@ export {
22
22
  type WecomWireSegment,
23
23
  } from './protocol.js';
24
24
 
25
- export {
26
- getWecomAgentDeps,
27
- registerWecomAgentEndpoint,
28
- setWecomAgentDeps,
29
- type WecomAgentDeps,
30
- type WecomAgentEndpoint,
31
- } from './wecom-agent-deps.js';
25
+ export { wecomClient, type WecomClientEventMap } from './client.js';
32
26
 
33
27
  export {
34
28
  checkWecomPlatformPermit,
@@ -38,7 +32,9 @@ export {
38
32
  } from './platform-permit.js';
39
33
 
40
34
  export {
35
+ WecomClient,
41
36
  WecomEndpoint,
37
+ type WecomClientApi,
42
38
  type WecomEndpointOptions,
43
39
  type WecomFetch,
44
40
  } from './endpoint.js';
package/src/protocol.ts CHANGED
@@ -227,7 +227,7 @@ export function parseXmlMessage(xml: string): WecomMessage | null {
227
227
  }
228
228
  }
229
229
 
230
- /** Build inbound text for MessageGateway.receive. */
230
+ /** Build inbound text for OutboundMessageService.receive. */
231
231
  export function formatInboundContent(msg: WecomMessage): string {
232
232
  switch (msg.MsgType) {
233
233
  case 'text':
@@ -1,5 +1,5 @@
1
1
  import { buildNotice, buildSystem, senderFromId } from '@zhin.js/core';
2
- import type { SideEventGateway } from '@zhin.js/core/runtime';
2
+ import type { EndpointEventEmitter } from 'zhin.js/adapter';
3
3
  import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
4
4
  import { resolveChatType, type WecomMessage } from './protocol.js';
5
5
 
@@ -17,16 +17,16 @@ function mapWecomEventParts(eventName: string): { scene_type: string; sub_type:
17
17
  }
18
18
 
19
19
  export function receiveWecomSideEvent(
20
- sideEvents: SideEventGateway | undefined,
20
+ emit: EndpointEventEmitter,
21
21
  endpointKey: string,
22
22
  configId: string,
23
23
  msg: WecomMessage,
24
24
  logger: ReturnType<typeof getAdapterLogger>,
25
25
  ): boolean {
26
- if (!sideEvents || msg.MsgType !== 'event') return false;
26
+ if (msg.MsgType !== 'event') return false;
27
27
  const eventName = msg.Event ?? 'unknown';
28
28
  if (eventName === 'enter_agent') {
29
- void sideEvents.receiveSystem(buildSystem(msg, {
29
+ void emit('system.receive', buildSystem(msg, {
30
30
  $id: `wecom:enter_agent:${msg.FromUserName}:${msg.CreateTime ?? Date.now()}`,
31
31
  $adapter: 'wecom' as never,
32
32
  $endpoint: configId,
@@ -48,7 +48,7 @@ export function receiveWecomSideEvent(
48
48
  const parts = mapWecomEventParts(eventName);
49
49
  if (!parts) return false;
50
50
  const sceneType = resolveChatType(msg.FromUserName);
51
- void sideEvents.receiveNotice(buildNotice(msg, {
51
+ void emit('notice.receive', buildNotice(msg, {
52
52
  $id: `wecom:${eventName}:${msg.FromUserName}:${msg.CreateTime ?? Date.now()}`,
53
53
  $adapter: 'wecom' as never,
54
54
  $endpoint: configId,
@@ -1,17 +0,0 @@
1
- /**
2
- * Agent tool deps for wecom (get_user / departments / send_text).
3
- * Endpoints register themselves on start; tools look up by endpoint id.
4
- */
5
- export interface WecomAgentEndpoint {
6
- getUserInfo(userId: string): Promise<unknown>;
7
- getDepartmentUsers(deptId: number): Promise<unknown[]>;
8
- getDepartmentList(deptId?: number): Promise<unknown[]>;
9
- sendTextMessage(userId: string, content: string): Promise<boolean>;
10
- }
11
- export interface WecomAgentDeps {
12
- getEndpoint: (endpointKey: string) => WecomAgentEndpoint;
13
- }
14
- export declare function registerWecomAgentEndpoint(endpointKey: string, endpoint: WecomAgentEndpoint): () => void;
15
- /** Optional override used by tests / transitional callers. Pass `null` to clear. */
16
- export declare function setWecomAgentDeps(deps: WecomAgentDeps | null): void;
17
- export declare function getWecomAgentDeps(): WecomAgentDeps;
@@ -1,30 +0,0 @@
1
- /**
2
- * Agent tool deps for wecom (get_user / departments / send_text).
3
- * Endpoints register themselves on start; tools look up by endpoint id.
4
- */
5
- const endpoints = new Map();
6
- let override = null;
7
- export function registerWecomAgentEndpoint(endpointKey, endpoint) {
8
- endpoints.set(endpointKey, endpoint);
9
- return () => {
10
- if (endpoints.get(endpointKey) === endpoint) {
11
- endpoints.delete(endpointKey);
12
- }
13
- };
14
- }
15
- /** Optional override used by tests / transitional callers. Pass `null` to clear. */
16
- export function setWecomAgentDeps(deps) {
17
- override = deps;
18
- }
19
- export function getWecomAgentDeps() {
20
- if (override)
21
- return override;
22
- return {
23
- getEndpoint(endpointKey) {
24
- const endpoint = endpoints.get(endpointKey);
25
- if (!endpoint)
26
- throw new Error(`Endpoint ${endpointKey} 不存在`);
27
- return endpoint;
28
- },
29
- };
30
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Agent tool deps for wecom (get_user / departments / send_text).
3
- * Endpoints register themselves on start; tools look up by endpoint id.
4
- */
5
-
6
- export interface WecomAgentEndpoint {
7
- getUserInfo(userId: string): Promise<unknown>;
8
- getDepartmentUsers(deptId: number): Promise<unknown[]>;
9
- getDepartmentList(deptId?: number): Promise<unknown[]>;
10
- sendTextMessage(userId: string, content: string): Promise<boolean>;
11
- }
12
-
13
- export interface WecomAgentDeps {
14
- getEndpoint: (endpointKey: string) => WecomAgentEndpoint;
15
- }
16
-
17
- const endpoints = new Map<string, WecomAgentEndpoint>();
18
- let override: WecomAgentDeps | null = null;
19
-
20
- export function registerWecomAgentEndpoint(
21
- endpointKey: string,
22
- endpoint: WecomAgentEndpoint,
23
- ): () => void {
24
- endpoints.set(endpointKey, endpoint);
25
- return () => {
26
- if (endpoints.get(endpointKey) === endpoint) {
27
- endpoints.delete(endpointKey);
28
- }
29
- };
30
- }
31
-
32
- /** Optional override used by tests / transitional callers. Pass `null` to clear. */
33
- export function setWecomAgentDeps(deps: WecomAgentDeps | null): void {
34
- override = deps;
35
- }
36
-
37
- export function getWecomAgentDeps(): WecomAgentDeps {
38
- if (override) return override;
39
- return {
40
- getEndpoint(endpointKey) {
41
- const endpoint = endpoints.get(endpointKey);
42
- if (!endpoint) throw new Error(`Endpoint ${endpointKey} 不存在`);
43
- return endpoint;
44
- },
45
- };
46
- }