@zhin.js/adapter-wecom 1.1.18 → 1.1.20

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/src/endpoint.ts CHANGED
@@ -1,388 +1,35 @@
1
- import { Endpoint } from 'zhin.js/adapter';
2
- /**
3
- * WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
4
- */
5
- import {
6
- createRecallEndpointControl,
7
- type EndpointControl,
8
- type EndpointSendRequest,
9
- } from 'zhin.js/adapter';
10
- import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
11
- import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
12
- import type { CapabilityId } from 'zhin.js';
13
- import {
14
- buildMediaUploadForm,
15
- readOutboundImageMedia,
16
- resolveMediaBinary,
17
- type WecomMediaUploadResult,
18
- } from './media-upload.js';
19
- import {
20
- buildSendRequestBody,
21
- formatInboundContent,
22
- formatOutboundBody,
23
- resolveChatType,
24
- wecomInboundConversation,
25
- type AccessToken,
26
- type ResolvedWecomConfig,
27
- type WecomApiResponse,
28
- type WecomMessage,
29
- } from './protocol.js';
30
- import { registerWecomWebhookRoutes } from './webhook.js';
31
- import { receiveWecomSideEvent } from './side-event-dispatch.js';
32
-
33
- export type WecomFetch = (
34
- url: string,
35
- init?: {
36
- readonly method?: string;
37
- readonly headers?: Record<string, string>;
38
- readonly body?: string | FormData;
39
- },
40
- ) => Promise<{
41
- readonly ok: boolean;
42
- readonly status: number;
43
- text(): Promise<string>;
44
- json(): Promise<unknown>;
45
- }>;
46
-
47
- export interface WecomEndpointOptions {
48
- readonly id: CapabilityId;
49
- readonly http: HttpHost;
50
- readonly config: ResolvedWecomConfig;
51
- readonly fetch?: WecomFetch;
52
- }
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
-
70
- /**
71
- * 企业微信服务端 API 无 bot 群列表/群成员列表接口(客户群接口属「客户联系」
72
- * 独立授权域,非 bot 社交面),好友/频道概念亦不存在;
73
- * 因此本 endpoint 不暴露 EndpointManagement(Console 社交面 RPC 对该平台保持未接线)。
74
- */
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
- });
82
- readonly #logger!: ReturnType<typeof getAdapterLogger>;
83
-
84
- readonly #options: WecomEndpointOptions;
85
- readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
86
- readonly #fetch: WecomFetch;
87
- #routeReleases: HttpRouteRegistration[] = [];
88
- #accessToken: AccessToken = { access_token: '', expires_in: 0, timestamp: 0 };
89
- #refreshPromise: Promise<string> | null = null;
90
- #open = false;
91
- #started = false;
92
-
93
- constructor(options: WecomEndpointOptions) {
94
- super();
95
- this.#logger = getAdapterLogger('wecom', options.config.id);
96
- this.#options = options;
97
- this.#fetch = options.fetch ?? globalThis.fetch;
98
- }
99
-
100
- /** Used by webhook handler. */
101
- get isOpen(): boolean {
102
- return this.#open;
103
- }
104
-
105
- get config(): ResolvedWecomConfig {
106
- return this.#options.config;
107
- }
108
-
109
- async start(): Promise<void> {
110
- if (this.#started) return;
111
- this.#started = true;
112
- try {
113
- await this.#refreshAccessToken();
114
- this.#routeReleases.push(...registerWecomWebhookRoutes(this.#options.http, this));
115
- this.#logger.debug(formatCompact({
116
- endpoint: this.#options.config.id,
117
- op: 'webhook',
118
- path: this.#options.config.webhookPath,
119
- }));
120
- } catch (error) {
121
- await this.stop();
122
- this.#logger.error('Failed to connect WeCom endpoint:', error);
123
- throw error;
124
- }
125
- }
126
-
127
- open(): void {
128
- this.#open = true;
129
- }
130
-
131
- close(): void {
132
- this.#open = false;
133
- }
134
-
135
- async stop(): Promise<void> {
136
- this.#open = false;
137
- for (const release of this.#routeReleases.splice(0)) release();
138
- this.#started = false;
139
- this.#logger.debug(formatCompact({ op: 'disconnect' }));
140
- }
141
-
142
- async send({ conversation, payload, locale }: EndpointSendRequest): Promise<string> {
143
- const materialized = await this.#materializeOutboundMedia(payload);
144
- const content = formatOutboundBody(materialized, locale);
145
- const body = buildSendRequestBody(
146
- conversation.id,
147
- content,
148
- // Legacy field: historically written into agentid (preserved for cutover).
149
- this.#options.config.agentSecret,
150
- );
151
- const data = await this.#request('/cgi-bin/message/send', {
152
- method: 'POST',
153
- body,
154
- });
155
- if (data.errcode !== 0) {
156
- throw new Error(`Failed to send message: ${data.errmsg} (${data.errcode})`);
157
- }
158
- this.#logger.debug(formatCompact({ op: 'send', to: `${conversation.kind}:${conversation.id}`,
159
- }));
160
- return (data.msgid as string) || `${Date.now()}`;
161
- }
162
-
163
- async recallMessage(messageId: string): Promise<void> {
164
- if (!messageId) return;
165
- await this.#request('/cgi-bin/message/recall', {
166
- method: 'POST',
167
- body: { msgid: messageId },
168
- });
169
- }
170
-
171
- /**
172
- * message/send 的 image 段只接受 media_id:canonical MediaRef 按 kind 投递——
173
- * file(平台不透明引用)直用为 media_id;base64/本地路径/URL 先经
174
- * /cgi-bin/media/upload 物化;上传失败降级为文本(alt 优先),不阻断发送。
175
- * 无 canonical MediaRef 的媒体段 warn + 丢弃。
176
- */
177
- async #materializeOutboundMedia(payload: unknown): Promise<unknown> {
178
- if (!Array.isArray(payload)) return payload;
179
- const items = await Promise.all(payload.map(async (item) => {
180
- if (typeof item === 'string' || !item || typeof item !== 'object') return item;
181
- const seg = item as { type?: unknown; data?: Record<string, unknown> };
182
- if (seg.type !== 'image') return item;
183
- const data = seg.data ?? {};
184
- const media = readOutboundImageMedia(data);
185
- if (!media) {
186
- this.#logger.warn(formatCompact({
187
- op: 'wecom_outbound_media_dropped',
188
- endpoint: this.#options.config.id,
189
- type: 'image',
190
- reason: 'missing_media_ref',
191
- }));
192
- return null;
193
- }
194
- if (media.kind === 'file') {
195
- return { type: 'image', data: { media_id: media.value } };
196
- }
197
- try {
198
- const mediaId = await this.#uploadMedia('image', media);
199
- return { type: 'image', data: { media_id: mediaId } };
200
- } catch (error) {
201
- this.#logger.warn(formatCompact({
202
- op: 'wecom_media_upload_failed',
203
- endpoint: this.#options.config.id,
204
- error: error instanceof Error ? error.message : String(error),
205
- }));
206
- const alt = typeof data.alt === 'string' && data.alt ? data.alt : '[image]';
207
- return { type: 'text', data: { text: alt } };
208
- }
209
- }));
210
- return items.filter((item) => item !== null);
211
- }
212
-
213
- /** POST /cgi-bin/media/upload(临时素材,3 天有效),返回 media_id。 */
214
- async #uploadMedia(type: 'image', media: Parameters<typeof resolveMediaBinary>[0]): Promise<string> {
215
- await this.#ensureAccessToken();
216
- const binary = await resolveMediaBinary(media);
217
- const form = buildMediaUploadForm(binary);
218
- const url = `${this.#options.config.apiBaseUrl}/cgi-bin/media/upload?access_token=${this.#accessToken.access_token}&type=${type}`;
219
- const response = await this.#fetch(url, { method: 'POST', body: form });
220
- const data = await response.json() as WecomMediaUploadResult;
221
- if (data.errcode === 0 && data.media_id) return data.media_id;
222
- throw new Error(`WeCom media upload failed: ${data.errmsg ?? 'unknown'} (${data.errcode ?? '?'})`);
223
- }
224
-
225
- /** Test / internal: admit a parsed message when open (non-webhook path). */
226
- admit(msg: WecomMessage): void {
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
- });
235
- if (receiveWecomSideEvent(
236
- (name, payload) => this.emit(name, payload),
237
- String(this.#options.id),
238
- this.#options.config.id,
239
- msg,
240
- this.#logger,
241
- )) {
242
- return;
243
- }
244
- const chatType = resolveChatType(msg.FromUserName);
245
- const conversation = wecomInboundConversation(String(this.#options.id), msg);
246
- void this.emit('message.receive', {
247
- conversation,
248
- message: { conversation, id: msg.MsgId || `${msg.CreateTime}` },
249
- content: formatInboundContent(msg),
250
- sender: { id: msg.FromUserName },
251
- endpointId: this.#options.config.id,
252
- metadata: Object.freeze({
253
- msgType: msg.MsgType,
254
- event: msg.Event,
255
- chatType,
256
- toUserName: msg.ToUserName,
257
- agentId: msg.AgentID,
258
- }),
259
- }).catch((err) => {
260
- this.#logger.warn(formatCompact({
261
- op: 'wecom_gateway_receive_failed',
262
- target: `${conversation.kind}:${conversation.id}`,
263
- error: err instanceof Error ? err.message : String(err),
264
- }));
265
- });
266
- }
267
-
268
- async #getUserInfo(userId: string): Promise<WecomApiResponse | null> {
269
- try {
270
- const data = await this.#request('/cgi-bin/user/get', {
271
- params: { userid: userId },
272
- });
273
- if (data.errcode === 0) return data;
274
- throw new Error(`Failed to get user info: ${data.errmsg}`);
275
- } catch (error) {
276
- this.#logger.error('Failed to get user info:', error);
277
- return null;
278
- }
279
- }
280
-
281
- async #getDepartmentUsers(deptId: number): Promise<unknown[]> {
282
- try {
283
- const data = await this.#request('/cgi-bin/user/simplelist', {
284
- params: { department_id: deptId },
285
- });
286
- if (data.errcode === 0) return (data.userlist as unknown[]) || [];
287
- throw new Error(`Failed to get department users: ${data.errmsg}`);
288
- } catch (error) {
289
- this.#logger.error('Failed to get department users:', error);
290
- return [];
291
- }
292
- }
293
-
294
- async #getDepartmentList(deptId: number = 1): Promise<unknown[]> {
295
- try {
296
- const data = await this.#request('/cgi-bin/department/list', {
297
- params: { id: deptId },
298
- });
299
- if (data.errcode === 0) return (data.department as unknown[]) || [];
300
- throw new Error(`Failed to get department list: ${data.errmsg}`);
301
- } catch (error) {
302
- this.#logger.error('Failed to get department list:', error);
303
- return [];
304
- }
305
- }
306
-
307
- async #sendTextMessage(userId: string, content: string): Promise<boolean> {
308
- try {
309
- const endpointKey = String(this.#options.id);
310
- await this.send({
311
- conversation: {
312
- endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
313
- kind: resolveChatType(userId),
314
- id: userId,
315
- },
316
- payload: content,
317
- });
318
- return true;
319
- } catch (error) {
320
- this.#logger.error('Failed to send text message:', error);
321
- return false;
322
- }
323
- }
324
-
325
- async #request(
326
- path: string,
327
- options: {
328
- method?: 'GET' | 'POST';
329
- params?: Record<string, string | number>;
330
- body?: Record<string, unknown>;
331
- } = {},
332
- ): Promise<WecomApiResponse> {
333
- await this.#ensureAccessToken();
334
- const { method = 'GET', params = {}, body } = options;
335
- const urlParams = new URLSearchParams({
336
- ...Object.fromEntries(
337
- Object.entries(params).map(([key, value]) => [key, String(value)]),
338
- ),
339
- access_token: this.#accessToken.access_token,
340
- });
341
- const url = `${this.#options.config.apiBaseUrl}${path}?${urlParams.toString()}`;
342
- const response = await this.#fetch(url, {
343
- method,
344
- headers: { 'Content-Type': 'application/json; charset=utf-8' },
345
- body: body && method === 'POST' ? JSON.stringify(body) : undefined,
346
- });
347
- if (!response.ok) {
348
- const text = await response.text().catch(() => '');
349
- throw new Error(`WeCom API error ${response.status}: ${text}`);
350
- }
351
- return await response.json() as WecomApiResponse;
352
- }
353
-
354
- async #ensureAccessToken(): Promise<void> {
355
- const now = Date.now();
356
- if (
357
- this.#accessToken.access_token
358
- && now < this.#accessToken.timestamp + (this.#accessToken.expires_in - 300) * 1000
359
- ) {
360
- return;
361
- }
362
- if (this.#refreshPromise) {
363
- await this.#refreshPromise;
364
- return;
365
- }
366
- this.#refreshPromise = this.#refreshAccessToken()
367
- .then(() => this.#accessToken.access_token)
368
- .finally(() => { this.#refreshPromise = null; });
369
- await this.#refreshPromise;
370
- }
371
-
372
- async #refreshAccessToken(): Promise<void> {
373
- const { corpId, agentSecret, apiBaseUrl } = this.#options.config;
374
- const url = `${apiBaseUrl}/cgi-bin/gettoken?corpid=${corpId}&corpsecret=${agentSecret}`;
375
- const response = await this.#fetch(url);
376
- const data = await response.json() as WecomApiResponse;
377
- if (data.errcode === 0 && data.access_token) {
378
- this.#accessToken = {
379
- access_token: data.access_token,
380
- expires_in: data.expires_in ?? 7200,
381
- timestamp: Date.now(),
382
- };
383
- this.#logger.debug('Access token refreshed successfully');
384
- return;
385
- }
386
- throw new Error(`Failed to get access token: ${data.errmsg} (${data.errcode})`);
1
+ import { createEndpointSpecification, endpointSpec, type Endpoint } from 'zhin.js/adapter';
2
+ import { WecomClient, type WecomEndpointOptions } from './client.js';
3
+ export type { WecomEndpointOptions } from './client.js';
4
+ export type { WecomFetch } from './client.js';
5
+
6
+ /** Actual protocol Client; framework lifecycle and admission stay in their symbol namespace. */
7
+ export class WecomEndpoint extends WecomClient implements Endpoint {
8
+ readonly [endpointSpec] = this.#createSpecification();
9
+ constructor(options: WecomEndpointOptions) { super(options); }
10
+ #createSpecification() {
11
+ const hooks = {
12
+ start: (signal: AbortSignal) => this.#startFramework(signal),
13
+ open: () => this.openInput(), close: () => this.closeInput(), stop: () => this.disconnectTransport(),
14
+ send: (request: import('zhin.js/adapter').EndpointSendRequest) => this.sendPayload(request),
15
+ name: this.configName(),
16
+ control: this.endpointControl,
17
+ };
18
+ Object.defineProperty(hooks, 'transportState', { get: () => this.transportState });
19
+ return createEndpointSpecification(this, hooks);
20
+ }
21
+ #startFramework(signal: AbortSignal) { return this.connectTransport(signal); }
22
+ protected override receiveInboundCallback<T>(receive: () => T | Promise<T>): Promise<T> {
23
+ return this[endpointSpec].events.run(receive);
24
+ }
25
+ protected override publishEvent(name: string, payload: unknown): Promise<unknown> {
26
+ return this[endpointSpec].events.emit(name, payload);
27
+ }
28
+ protected override publishAccepted(name: string, payload: unknown): Promise<void> {
29
+ return this[endpointSpec].events.emitAccepted(name, payload).then(() => undefined);
30
+ }
31
+ protected override async publishPlatform(name: string, event: unknown): Promise<unknown> {
32
+ await super.publishPlatform(name, event);
33
+ return this[endpointSpec].events.platform(name, event);
387
34
  }
388
35
  }
package/src/index.ts CHANGED
@@ -22,7 +22,7 @@ export {
22
22
  type WecomWireSegment,
23
23
  } from './protocol.js';
24
24
 
25
- export { wecomClient, type WecomClientEventMap } from './client.js';
25
+ export { type WecomClientEventMap } from './client.js';
26
26
 
27
27
  export {
28
28
  checkWecomPlatformPermit,
@@ -32,9 +32,7 @@ export {
32
32
  } from './platform-permit.js';
33
33
 
34
34
  export {
35
- WecomClient,
36
35
  WecomEndpoint,
37
- type WecomClientApi,
38
36
  type WecomEndpointOptions,
39
37
  type WecomFetch,
40
38
  } from './endpoint.js';
@@ -53,3 +51,6 @@ export {
53
51
  handleWecomWebhookRequest,
54
52
  type WecomWebhookHandler,
55
53
  } from './webhook.js';
54
+
55
+ export { WecomClient, type WecomClientApi } from './client.js';
56
+ export { WecomAdapter } from './adapter.js';
@@ -30,10 +30,15 @@ const MIME_EXT: Record<string, string> = {
30
30
  };
31
31
 
32
32
  /** 从 canonical MediaRef 解析二进制:base64 解码 / 本地读盘 / URL 下载。 */
33
+ export interface MediaResolveOptions {
34
+ readonly download?: (url: string, signal?: AbortSignal) => Promise<Buffer>;
35
+ readonly signal?: AbortSignal;
36
+ }
33
37
  export async function resolveMediaBinary(
34
38
  media: MediaRef,
35
- download: (url: string) => Promise<Buffer> = defaultDownload,
39
+ { download = defaultDownload, signal }: MediaResolveOptions = {},
36
40
  ): Promise<MediaBinary> {
41
+ signal?.throwIfAborted();
37
42
  const mimeType = media.mime_type ?? 'image/png';
38
43
  const ext = MIME_EXT[mimeType] ?? 'png';
39
44
  if (media.kind === 'base64') {
@@ -44,17 +49,17 @@ export async function resolveMediaBinary(
44
49
  }
45
50
  if (media.kind === 'path') {
46
51
  const path = media.value.startsWith('file://') ? media.value.slice('file://'.length) : media.value;
47
- return { data: await readFile(path), mimeType, fileName: basename(path) };
52
+ return { data: await readFile(path, { signal }), mimeType, fileName: basename(path) };
48
53
  }
49
54
  if (media.kind === 'url') {
50
- return { data: await download(media.value), mimeType, fileName: `image.${ext}` };
55
+ return { data: await download(media.value, signal), mimeType, fileName: `image.${ext}` };
51
56
  }
52
57
  // kind=file 为平台不透明引用(media_id),无二进制可解,调用方应直用 value
53
58
  throw new Error(`cannot resolve binary from media kind: ${media.kind}`);
54
59
  }
55
60
 
56
- async function defaultDownload(url: string): Promise<Buffer> {
57
- const response = await fetch(url, { signal: AbortSignal.timeout(30_000) });
61
+ async function defaultDownload(url: string, signal?: AbortSignal): Promise<Buffer> {
62
+ const response = await fetch(url, { signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000) });
58
63
  if (!response.ok) throw new Error(`download failed: HTTP ${response.status}`);
59
64
  return Buffer.from(await response.arrayBuffer());
60
65
  }
package/src/protocol.ts CHANGED
@@ -12,6 +12,7 @@ import type { ConversationRef } from '@zhin.js/im-contract';
12
12
  export interface WecomEndpointConfig {
13
13
  readonly id: string;
14
14
  readonly corpId: string;
15
+ readonly agentId: string;
15
16
  readonly agentSecret: string;
16
17
  readonly token: string;
17
18
  readonly encodingAESKey: string;
@@ -23,6 +24,7 @@ export interface ResolvedWecomConfig {
23
24
  readonly context: 'wecom';
24
25
  readonly id: string;
25
26
  readonly corpId: string;
27
+ readonly agentId: string;
26
28
  readonly agentSecret: string;
27
29
  readonly token: string;
28
30
  readonly encodingAESKey: string;
@@ -31,6 +33,8 @@ export interface ResolvedWecomConfig {
31
33
  }
32
34
 
33
35
  export interface WecomMessage {
36
+ /** Complete decrypted callback XML; preserved before field projection. */
37
+ readonly rawXml?: string;
34
38
  readonly ToUserName: string;
35
39
  readonly FromUserName: string;
36
40
  readonly CreateTime: number;
@@ -84,6 +88,7 @@ export interface WecomSendBody {
84
88
  export function resolveWecomConfig(config: WecomEndpointConfig): ResolvedWecomConfig {
85
89
  const id = requiredEndpointField(config.id, 'id');
86
90
  const corpId = requiredEndpointField(config.corpId, 'corpId');
91
+ const agentId = requiredEndpointField(config.agentId, 'agentId');
87
92
  const agentSecret = requiredEndpointField(config.agentSecret, 'agentSecret');
88
93
  const token = requiredEndpointField(config.token, 'token');
89
94
  const encodingAESKey = requiredEndpointField(config.encodingAESKey, 'encodingAESKey');
@@ -91,6 +96,7 @@ export function resolveWecomConfig(config: WecomEndpointConfig): ResolvedWecomCo
91
96
  context: 'wecom',
92
97
  id,
93
98
  corpId,
99
+ agentId,
94
100
  agentSecret,
95
101
  token,
96
102
  encodingAESKey,
@@ -103,7 +109,7 @@ export function resolveWecomConfig(config: WecomEndpointConfig): ResolvedWecomCo
103
109
 
104
110
  function requiredEndpointField(
105
111
  value: unknown,
106
- field: 'id' | 'corpId' | 'agentSecret' | 'token' | 'encodingAESKey',
112
+ field: 'id' | 'corpId' | 'agentId' | 'agentSecret' | 'token' | 'encodingAESKey',
107
113
  ): string {
108
114
  const resolved = optionalEndpointField(value);
109
115
  if (!resolved) {
@@ -198,6 +204,7 @@ export function parseXmlMessage(xml: string): WecomMessage | null {
198
204
  if (!msgType) return null;
199
205
 
200
206
  const msg: WecomMessage = {
207
+ rawXml: xml,
201
208
  ToUserName: get('ToUserName') || '',
202
209
  FromUserName: get('FromUserName') || '',
203
210
  CreateTime: Number(get('CreateTime') || Date.now()),
@@ -1 +0,0 @@
1
- export declare const wecomRuntimeStateToken: import("zhin.js").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
@@ -1,6 +0,0 @@
1
- /**
2
- * WeCom 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `wecom endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
- export const wecomRuntimeStateToken = defineEndpointRuntimeStateToken('wecom');
@@ -1,7 +0,0 @@
1
- /**
2
- * WeCom 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `wecom endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
-
7
- export const wecomRuntimeStateToken = defineEndpointRuntimeStateToken('wecom');