@zhin.js/adapter-milky 6.0.14 → 7.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.
@@ -3,18 +3,24 @@
3
3
  */
4
4
  import WebSocket from 'ws';
5
5
  import {
6
+ ClientEndpoint,
7
+ createRecallEndpointControl,
6
8
  createEndpointLifecycle,
7
9
  type EndpointConnectHandle,
8
- type EndpointInstance,
10
+ type EndpointControl,
9
11
  type EndpointLifecycle,
10
12
  type EndpointManagement,
11
13
  type EndpointSendRequest,
12
14
  } from 'zhin.js/adapter';
13
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
14
15
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
15
16
  import type { CapabilityId } from 'zhin.js';
16
17
  import { createMilkyEndpointManagement } from './endpoint-management.js';
17
- import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
18
+ import {
19
+ callMilkyClient,
20
+ createMilkyEndpointClient,
21
+ forwardMilkyClientEvents,
22
+ type MilkyClient,
23
+ } from './client.js';
18
24
  import {
19
25
  buildSendAction,
20
26
  buildWsConnectOptions,
@@ -40,8 +46,6 @@ const WS_OPEN = 1;
40
46
 
41
47
  export interface MilkyWsEndpointOptions {
42
48
  readonly id: CapabilityId;
43
- readonly gateway: MessageGateway;
44
- readonly sideEvents?: SideEventGateway;
45
49
  readonly config: MilkyWsConfig;
46
50
  readonly createWebSocket?: (
47
51
  url: string,
@@ -50,21 +54,34 @@ export interface MilkyWsEndpointOptions {
50
54
  readonly callApi?: typeof callApi;
51
55
  }
52
56
 
53
- export class MilkyWsEndpoint implements EndpointInstance {
57
+ export class MilkyWsEndpoint extends ClientEndpoint<MilkyClient> {
58
+ readonly client: MilkyClient;
54
59
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
55
60
 
56
61
  readonly #options: MilkyWsEndpointOptions;
57
62
  readonly #callApi: typeof callApi;
58
- readonly management: EndpointManagement = createMilkyEndpointManagement(this);
63
+ readonly management: EndpointManagement;
64
+ readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
59
65
  readonly #lifecycle: EndpointLifecycle;
60
66
  #ws?: MilkyWsSocket;
61
- #open = false;
62
- #unregisterAgent?: () => void;
67
+ #clientSocketRelease?: () => void;
63
68
 
64
69
  constructor(options: MilkyWsEndpointOptions) {
70
+ super();
65
71
  this.#logger = getAdapterLogger('milky', options.config.id);
66
72
  this.#options = options;
67
73
  this.#callApi = options.callApi ?? callApi;
74
+ this.client = createMilkyEndpointClient(options.config, this.#callApi);
75
+ this.management = createMilkyEndpointManagement({
76
+ callApi: (action, params) => callMilkyClient(this.client, action, params),
77
+ });
78
+ this.bindClientEvents(
79
+ (receive) => forwardMilkyClientEvents(this.client, receive),
80
+ (name, payload) => {
81
+ if (name === 'event') this.#admitRaw(payload as MilkyEvent);
82
+ },
83
+ (_name, error) => this.#warnPlatformEvent(error),
84
+ );
68
85
  this.#lifecycle = createEndpointLifecycle({
69
86
  name: options.config.id,
70
87
  // reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
@@ -79,30 +96,18 @@ export class MilkyWsEndpoint implements EndpointInstance {
79
96
 
80
97
  async start(): Promise<void> {
81
98
  if (this.#lifecycle.started) return;
82
- this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
83
- try {
84
- await this.#lifecycle.start((handle) => this.#connect(handle));
85
- } catch (err) {
86
- // start 失败复位由基座保证;agent 注册/反注册是适配器专有依赖,留在适配器侧
87
- this.#unregisterAgent?.();
88
- this.#unregisterAgent = undefined;
89
- throw err;
90
- }
91
- }
92
-
93
- open(): void {
94
- this.#open = true;
99
+ await this.#lifecycle.start((handle) => this.#connect(handle));
95
100
  }
96
101
 
97
102
  close(): void {
98
- this.#open = false;
103
+ super.close();
104
+ this.#clientSocketRelease?.();
105
+ this.#clientSocketRelease = undefined;
99
106
  }
100
107
 
101
108
  async stop(): Promise<void> {
102
- this.#open = false;
109
+ this.close();
103
110
  await this.#lifecycle.stop();
104
- this.#unregisterAgent?.();
105
- this.#unregisterAgent = undefined;
106
111
  if (this.#ws) {
107
112
  try {
108
113
  this.#ws.close();
@@ -116,7 +121,7 @@ export class MilkyWsEndpoint implements EndpointInstance {
116
121
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
117
122
  const message = formatOutboundSegments(payload);
118
123
  const { action, params } = buildSendAction(conversation, message);
119
- const data = await this.callApi(action, params) as { message_seq?: number } | undefined;
124
+ const data = await callMilkyClient<{ message_seq?: number }>(this.client, action, params);
120
125
  const messageId = formatOutboundMessageId(conversation, data?.message_seq);
121
126
  this.#logger.debug(formatCompact({
122
127
  op: 'milky_send',
@@ -127,102 +132,33 @@ export class MilkyWsEndpoint implements EndpointInstance {
127
132
  return messageId;
128
133
  }
129
134
 
130
- /** Public API for agent tools / callers. */
131
- callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
132
- return this.#callApi(this.apiOptions(), action, params);
133
- }
134
-
135
135
  async recallMessage(id: string): Promise<void> {
136
136
  const parsed = parseMilkyMessageId(id);
137
137
  if (!parsed) throw new Error(`Invalid message id: ${id}`);
138
138
  if (parsed.message_scene === 'group') {
139
- await this.callApi('recall_group_message', {
139
+ await callMilkyClient(this.client, 'recall_group_message', {
140
140
  group_id: parsed.peer_id,
141
141
  message_seq: parsed.message_seq,
142
142
  });
143
143
  } else {
144
- await this.callApi('recall_private_message', {
144
+ await callMilkyClient(this.client, 'recall_private_message', {
145
145
  user_id: parsed.peer_id,
146
146
  message_seq: parsed.message_seq,
147
147
  });
148
148
  }
149
149
  }
150
150
 
151
- async kickMember(groupId: number, userId: number, rejectAddRequest = false): Promise<boolean> {
152
- await this.callApi('kick_group_member', {
153
- group_id: groupId,
154
- user_id: userId,
155
- reject_add_request: rejectAddRequest,
156
- });
157
- return true;
158
- }
159
-
160
- async muteMember(groupId: number, userId: number, duration = 600): Promise<boolean> {
161
- await this.callApi('set_group_member_mute', {
162
- group_id: groupId,
163
- user_id: userId,
164
- duration,
165
- });
166
- return true;
167
- }
168
-
169
- async muteAll(groupId: number, enable = true): Promise<boolean> {
170
- await this.callApi('set_group_whole_mute', { group_id: groupId, is_mute: enable });
171
- return true;
172
- }
173
-
174
- async setAdmin(groupId: number, userId: number, enable = true): Promise<boolean> {
175
- await this.callApi('set_group_member_admin', {
176
- group_id: groupId,
177
- user_id: userId,
178
- is_set: enable,
179
- });
180
- return true;
181
- }
182
-
183
- async setCard(groupId: number, userId: number, card: string): Promise<boolean> {
184
- await this.callApi('set_group_member_card', {
185
- group_id: groupId,
186
- user_id: userId,
187
- card,
188
- });
189
- return true;
190
- }
191
-
192
- async setTitle(groupId: number, userId: number, title: string): Promise<boolean> {
193
- await this.callApi('set_group_member_special_title', {
194
- group_id: groupId,
195
- user_id: userId,
196
- special_title: title,
197
- });
198
- return true;
199
- }
200
-
201
- async setGroupName(groupId: number, name: string): Promise<boolean> {
202
- await this.callApi('set_group_name', { group_id: groupId, new_group_name: name });
203
- return true;
204
- }
205
-
206
- async getMemberList(groupId: number): Promise<unknown[]> {
207
- return this.callApi('get_group_member_list', { group_id: groupId }) as Promise<unknown[]>;
208
- }
209
-
210
- async getGroupInfo(groupId: number): Promise<unknown> {
211
- return this.callApi('get_group_info', { group_id: groupId });
212
- }
213
-
214
- /** Test / internal: admit a parsed event when the endpoint is open. */
215
- admit(event: MilkyEvent): void {
151
+ #admitRaw(event: MilkyEvent): void {
216
152
  const data = parseMessageReceiveData(event);
217
- if (!this.#open || !data) return;
153
+ if (!data) return;
218
154
  this.#admitMessage(data, event);
219
155
  }
220
156
 
221
- apiOptions(): { baseUrl: string; access_token?: string } {
222
- return {
223
- baseUrl: this.#options.config.baseUrl,
224
- access_token: this.#options.config.access_token,
225
- };
157
+ #warnPlatformEvent(error: unknown): void {
158
+ this.#logger.warn(formatCompact({
159
+ op: 'milky_platform_event_failed',
160
+ error: error instanceof Error ? error.message : String(error),
161
+ }));
226
162
  }
227
163
 
228
164
  #admitMessage(data: MilkyIncomingMessage, event: MilkyEvent): void {
@@ -233,7 +169,7 @@ export class MilkyWsEndpoint implements EndpointInstance {
233
169
  const audioUrl = extractInboundAudioUrl(data);
234
170
  const nickname = senderNickname(data);
235
171
  const mentioned = isMentioned(data, event.self_id);
236
- void this.#options.gateway.receive({
172
+ void this.emit('message.receive', {
237
173
  conversation,
238
174
  message: { conversation, id: formatInboundMessageId(data) },
239
175
  content,
@@ -305,11 +241,12 @@ export class MilkyWsEndpoint implements EndpointInstance {
305
241
  resolve();
306
242
  });
307
243
 
308
- ws.on('message', (data) => {
309
- this.#onMessage(data);
310
- });
244
+ this.#clientSocketRelease?.();
245
+ this.#clientSocketRelease = this.client.acceptWebSocket(ws);
311
246
 
312
247
  ws.on('close', (code, reason) => {
248
+ this.#clientSocketRelease?.();
249
+ this.#clientSocketRelease = undefined;
313
250
  const reasonStr = typeof reason === 'string'
314
251
  ? reason
315
252
  : Buffer.isBuffer(reason)
@@ -351,23 +288,4 @@ export class MilkyWsEndpoint implements EndpointInstance {
351
288
  });
352
289
  }
353
290
 
354
- #onMessage(data: unknown): void {
355
- try {
356
- const raw = typeof data === 'string'
357
- ? data
358
- : Buffer.isBuffer(data)
359
- ? data.toString()
360
- : data instanceof ArrayBuffer
361
- ? new TextDecoder().decode(data)
362
- : String(data ?? '');
363
- const event = JSON.parse(raw) as MilkyEvent;
364
- this.admit(event);
365
- } catch (error) {
366
- this.#logger.warn(formatCompact({
367
- op: 'milky_parse_failed',
368
- endpoint: this.#options.config.id,
369
- error: error instanceof Error ? error.message : String(error),
370
- }));
371
- }
372
- }
373
291
  }
@@ -1,19 +1,21 @@
1
1
  /**
2
2
  * Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
3
3
  */
4
- import { clearInterval } from 'node:timers';
5
- import type {
6
- EndpointInstance,
7
- EndpointManagement,
8
- EndpointSendRequest,
4
+ import {
5
+ ClientEndpoint,
6
+ createEndpointLifecycle,
7
+ createRecallEndpointControl,
8
+ type EndpointLifecycle,
9
+ type EndpointControl,
10
+ type EndpointManagement,
11
+ type EndpointSendRequest,
9
12
  } from 'zhin.js/adapter';
10
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
11
13
  import type { HttpHost, WsConnection } from '@zhin.js/host-http';
12
14
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
13
15
  import type { CapabilityId } from 'zhin.js';
14
16
  import { verifyMilkyAccessToken } from './milky-auth.js';
15
17
  import { createMilkyEndpointManagement } from './endpoint-management.js';
16
- import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
18
+ import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents, type MilkyClient } from './client.js';
17
19
  import {
18
20
  buildSendAction,
19
21
  callApi,
@@ -38,39 +40,65 @@ const WS_OPEN = 1;
38
40
 
39
41
  export interface MilkyWssEndpointOptions {
40
42
  readonly id: CapabilityId;
41
- readonly gateway: MessageGateway;
42
- readonly sideEvents?: SideEventGateway;
43
43
  readonly http: HttpHost;
44
44
  readonly config: MilkyWssConfig;
45
45
  readonly callApi?: typeof callApi;
46
46
  }
47
47
 
48
- export class MilkyWssEndpoint implements EndpointInstance {
48
+ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
49
+ readonly client: MilkyClient;
49
50
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
50
51
 
51
52
  readonly #options: MilkyWssEndpointOptions;
52
53
  readonly #callApi: typeof callApi;
53
- readonly management: EndpointManagement = createMilkyEndpointManagement(this);
54
+ readonly management: EndpointManagement;
55
+ readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
54
56
  #ws?: MilkyWsSocket;
55
57
  #wsRelease?: () => void;
56
- #heartbeatTimer?: NodeJS.Timeout;
57
- #open = false;
58
- #started = false;
59
- #unregisterAgent?: () => void;
58
+ #clientSocketRelease?: () => void;
59
+ readonly #lifecycle: EndpointLifecycle;
60
60
 
61
61
  constructor(options: MilkyWssEndpointOptions) {
62
+ super();
62
63
  this.#logger = getAdapterLogger('milky', options.config.id);
63
64
  this.#options = options;
64
65
  this.#callApi = options.callApi ?? callApi;
66
+ this.#lifecycle = createEndpointLifecycle({
67
+ name: options.config.id,
68
+ reconnect: false,
69
+ heartbeat: { intervalMs: options.config.heartbeat_interval },
70
+ });
71
+ this.client = createMilkyEndpointClient(options.config, this.#callApi);
72
+ this.management = createMilkyEndpointManagement({
73
+ callApi: (action, params) => callMilkyClient(this.client, action, params),
74
+ });
75
+ this.bindClientEvents(
76
+ (receive) => forwardMilkyClientEvents(this.client, receive),
77
+ (name, payload) => {
78
+ if (name === 'event') this.#admitRaw(payload as MilkyEvent);
79
+ },
80
+ (_name, error) => this.#warnPlatformEvent(error),
81
+ );
65
82
  }
66
83
 
67
84
  async start(): Promise<void> {
68
- if (this.#started) return;
69
- this.#started = true;
70
- this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
71
- const handle = this.#options.http.ws(this.#options.config.path);
72
- this.#wsRelease = handle.onConnection((connection) => {
73
- this.#acceptConnection(connection);
85
+ await this.#lifecycle.start(async (lifecycleHandle) => {
86
+ const handle = this.#options.http.ws(this.#options.config.path);
87
+ this.#wsRelease = handle.onConnection((connection) => {
88
+ this.#acceptConnection(connection);
89
+ });
90
+ lifecycleHandle.onForceClose(() => {
91
+ this.#wsRelease?.();
92
+ this.#wsRelease = undefined;
93
+ this.#clientSocketRelease?.();
94
+ this.#clientSocketRelease = undefined;
95
+ try {
96
+ this.#ws?.close();
97
+ } catch {
98
+ /* ignore */
99
+ }
100
+ this.#ws = undefined;
101
+ });
74
102
  });
75
103
  this.#logger.info(formatCompact({
76
104
  op: 'listen',
@@ -80,39 +108,15 @@ export class MilkyWssEndpoint implements EndpointInstance {
80
108
  }));
81
109
  }
82
110
 
83
- open(): void {
84
- this.#open = true;
85
- }
86
-
87
- close(): void {
88
- this.#open = false;
89
- }
90
-
91
111
  async stop(): Promise<void> {
92
- this.#open = false;
93
- this.#unregisterAgent?.();
94
- this.#unregisterAgent = undefined;
95
- this.#wsRelease?.();
96
- this.#wsRelease = undefined;
97
- if (this.#heartbeatTimer) {
98
- clearInterval(this.#heartbeatTimer);
99
- this.#heartbeatTimer = undefined;
100
- }
101
- if (this.#ws) {
102
- try {
103
- this.#ws.close();
104
- } catch {
105
- /* ignore */
106
- }
107
- this.#ws = undefined;
108
- }
109
- this.#started = false;
112
+ this.close();
113
+ await this.#lifecycle.stop();
110
114
  }
111
115
 
112
116
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
113
117
  const message = formatOutboundSegments(payload);
114
118
  const { action, params } = buildSendAction(conversation, message);
115
- const data = await this.callApi(action, params) as { message_seq?: number } | undefined;
119
+ const data = await callMilkyClient<{ message_seq?: number }>(this.client, action, params);
116
120
  const messageId = formatOutboundMessageId(conversation, data?.message_seq);
117
121
  this.#logger.debug(formatCompact({
118
122
  op: 'milky_send',
@@ -123,100 +127,30 @@ export class MilkyWssEndpoint implements EndpointInstance {
123
127
  return messageId;
124
128
  }
125
129
 
126
- callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
127
- return this.#callApi(this.apiOptions(), action, params);
128
- }
129
-
130
130
  async recallMessage(id: string): Promise<void> {
131
131
  const parsed = parseMilkyMessageId(id);
132
132
  if (!parsed) throw new Error(`Invalid message id: ${id}`);
133
133
  if (parsed.message_scene === 'group') {
134
- await this.callApi('recall_group_message', {
134
+ await callMilkyClient(this.client, 'recall_group_message', {
135
135
  group_id: parsed.peer_id,
136
136
  message_seq: parsed.message_seq,
137
137
  });
138
138
  } else {
139
- await this.callApi('recall_private_message', {
139
+ await callMilkyClient(this.client, 'recall_private_message', {
140
140
  user_id: parsed.peer_id,
141
141
  message_seq: parsed.message_seq,
142
142
  });
143
143
  }
144
144
  }
145
145
 
146
- async kickMember(groupId: number, userId: number, rejectAddRequest = false): Promise<boolean> {
147
- await this.callApi('kick_group_member', {
148
- group_id: groupId,
149
- user_id: userId,
150
- reject_add_request: rejectAddRequest,
151
- });
152
- return true;
153
- }
154
-
155
- async muteMember(groupId: number, userId: number, duration = 600): Promise<boolean> {
156
- await this.callApi('set_group_member_mute', {
157
- group_id: groupId,
158
- user_id: userId,
159
- duration,
160
- });
161
- return true;
162
- }
163
-
164
- async muteAll(groupId: number, enable = true): Promise<boolean> {
165
- await this.callApi('set_group_whole_mute', { group_id: groupId, is_mute: enable });
166
- return true;
167
- }
168
-
169
- async setAdmin(groupId: number, userId: number, enable = true): Promise<boolean> {
170
- await this.callApi('set_group_member_admin', {
171
- group_id: groupId,
172
- user_id: userId,
173
- is_set: enable,
174
- });
175
- return true;
176
- }
177
-
178
- async setCard(groupId: number, userId: number, card: string): Promise<boolean> {
179
- await this.callApi('set_group_member_card', {
180
- group_id: groupId,
181
- user_id: userId,
182
- card,
183
- });
184
- return true;
185
- }
186
-
187
- async setTitle(groupId: number, userId: number, title: string): Promise<boolean> {
188
- await this.callApi('set_group_member_special_title', {
189
- group_id: groupId,
190
- user_id: userId,
191
- special_title: title,
192
- });
193
- return true;
194
- }
195
-
196
- async setGroupName(groupId: number, name: string): Promise<boolean> {
197
- await this.callApi('set_group_name', { group_id: groupId, new_group_name: name });
198
- return true;
199
- }
200
-
201
- async getMemberList(groupId: number): Promise<unknown[]> {
202
- return this.callApi('get_group_member_list', { group_id: groupId }) as Promise<unknown[]>;
203
- }
204
-
205
- async getGroupInfo(groupId: number): Promise<unknown> {
206
- return this.callApi('get_group_info', { group_id: groupId });
207
- }
208
-
209
- admit(event: MilkyEvent): void {
146
+ #admitRaw(event: MilkyEvent): void {
210
147
  const data = parseMessageReceiveData(event);
211
- if (!this.#open || !data) return;
148
+ if (!data) return;
212
149
  this.#admitMessage(data, event);
213
150
  }
214
151
 
215
- apiOptions(): { baseUrl: string; access_token?: string } {
216
- return {
217
- baseUrl: this.#options.config.baseUrl,
218
- access_token: this.#options.config.access_token,
219
- };
152
+ #warnPlatformEvent(error: unknown): void {
153
+ this.#logger.warn(formatCompact({ op: 'milky_platform_event_failed', error: error instanceof Error ? error.message : String(error) }));
220
154
  }
221
155
 
222
156
  #admitMessage(data: MilkyIncomingMessage, event: MilkyEvent): void {
@@ -227,7 +161,7 @@ export class MilkyWssEndpoint implements EndpointInstance {
227
161
  const audioUrl = extractInboundAudioUrl(data);
228
162
  const nickname = senderNickname(data);
229
163
  const mentioned = isMentioned(data, event.self_id);
230
- void this.#options.gateway.receive({
164
+ void this.emit('message.receive', {
231
165
  conversation,
232
166
  message: { conversation, id: formatInboundMessageId(data) },
233
167
  content,
@@ -267,12 +201,22 @@ export class MilkyWssEndpoint implements EndpointInstance {
267
201
  }
268
202
  }
269
203
  this.#ws = socket;
270
- this.#startHeartbeat();
271
- socket.on('message', (data) => {
272
- this.#onMessage(data);
204
+ this.#lifecycle.startHeartbeat(() => {
205
+ try {
206
+ if (this.#ws?.readyState === WS_OPEN) this.#ws.ping?.();
207
+ } catch {
208
+ /* ignore */
209
+ }
273
210
  });
211
+ this.#clientSocketRelease?.();
212
+ this.#clientSocketRelease = this.client.acceptWebSocket(socket);
274
213
  socket.on('close', () => {
275
- if (this.#ws === socket) this.#ws = undefined;
214
+ this.#clientSocketRelease?.();
215
+ this.#clientSocketRelease = undefined;
216
+ if (this.#ws === socket) {
217
+ this.#ws = undefined;
218
+ this.#lifecycle.stopHeartbeat();
219
+ }
276
220
  });
277
221
  this.#logger.debug(formatCompact({
278
222
  endpoint: this.#options.config.id,
@@ -281,36 +225,4 @@ export class MilkyWssEndpoint implements EndpointInstance {
281
225
  }));
282
226
  }
283
227
 
284
- #onMessage(data: unknown): void {
285
- try {
286
- const raw = typeof data === 'string'
287
- ? data
288
- : Buffer.isBuffer(data)
289
- ? data.toString()
290
- : data instanceof ArrayBuffer
291
- ? new TextDecoder().decode(data)
292
- : String(data ?? '');
293
- const event = JSON.parse(raw) as MilkyEvent;
294
- this.admit(event);
295
- } catch (error) {
296
- this.#logger.warn(formatCompact({
297
- op: 'milky_parse_failed',
298
- endpoint: this.#options.config.id,
299
- error: error instanceof Error ? error.message : String(error),
300
- }));
301
- }
302
- }
303
-
304
- #startHeartbeat(): void {
305
- if (this.#heartbeatTimer) clearInterval(this.#heartbeatTimer);
306
- const interval = this.#options.config.heartbeat_interval;
307
- if (interval <= 0) return;
308
- this.#heartbeatTimer = setInterval(() => {
309
- try {
310
- if (this.#ws?.readyState === WS_OPEN) this.#ws.ping?.();
311
- } catch {
312
- /* ignore */
313
- }
314
- }, interval);
315
- }
316
228
  }
@@ -1,24 +0,0 @@
1
- /**
2
- * Agent tool deps for milky (scene management / callApi).
3
- * Endpoints register themselves on start; tools look up by config name / endpoint id.
4
- */
5
- export interface MilkyAgentEndpoint {
6
- callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
7
- kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
8
- muteMember(groupId: number, userId: number, duration?: number): Promise<boolean>;
9
- muteAll(groupId: number, enable?: boolean): Promise<boolean>;
10
- setAdmin(groupId: number, userId: number, enable?: boolean): Promise<boolean>;
11
- setCard(groupId: number, userId: number, card: string): Promise<boolean>;
12
- setTitle(groupId: number, userId: number, title: string): Promise<boolean>;
13
- setGroupName(groupId: number, name: string): Promise<boolean>;
14
- getMemberList(groupId: number): Promise<unknown[]>;
15
- getGroupInfo(groupId: number): Promise<unknown>;
16
- recallMessage?(id: string): Promise<void>;
17
- }
18
- export interface MilkyAgentDeps {
19
- getEndpoint: (endpointKey: string) => MilkyAgentEndpoint;
20
- }
21
- export declare function registerMilkyAgentEndpoint(endpointKey: string, endpoint: MilkyAgentEndpoint): () => void;
22
- /** Optional override used by tests / transitional callers. Pass `null` to clear. */
23
- export declare function setMilkyAgentDeps(deps: MilkyAgentDeps | null): void;
24
- export declare function getMilkyAgentDeps(): MilkyAgentDeps;
@@ -1,30 +0,0 @@
1
- /**
2
- * Agent tool deps for milky (scene management / callApi).
3
- * Endpoints register themselves on start; tools look up by config name / endpoint id.
4
- */
5
- const endpoints = new Map();
6
- let override = null;
7
- export function registerMilkyAgentEndpoint(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 setMilkyAgentDeps(deps) {
17
- override = deps;
18
- }
19
- export function getMilkyAgentDeps() {
20
- if (override)
21
- return override;
22
- return {
23
- getEndpoint(endpointKey) {
24
- const registered = endpoints.get(endpointKey);
25
- if (!registered)
26
- throw new Error(`Endpoint ${endpointKey} 不存在`);
27
- return registered;
28
- },
29
- };
30
- }