@zhin.js/adapter-milky 6.0.3 → 6.0.5
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 +70 -0
- package/adapters/milky.js +2 -2
- package/adapters/milky.ts +2 -2
- package/lib/milky-agent-deps.d.ts +2 -2
- package/lib/milky-agent-deps.js +7 -7
- package/lib/protocol.d.ts +4 -4
- package/lib/protocol.js +5 -5
- package/lib/sse-endpoint.js +18 -19
- package/lib/webhook-endpoint.js +14 -14
- package/lib/ws-endpoint.js +20 -21
- package/lib/wss-endpoint.js +16 -16
- package/package.json +15 -15
- package/schema.json +41 -5
- package/src/milky-agent-deps.ts +8 -8
- package/src/protocol.ts +8 -8
- package/src/sse-endpoint.ts +20 -21
- package/src/webhook-endpoint.ts +15 -15
- package/src/ws-endpoint.ts +21 -21
- package/src/wss-endpoint.ts +17 -16
- /package/commands/endpoint/add/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name].ts → [id].ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-milky",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.5",
|
|
4
4
|
"description": "Zhin.js Milky adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"ws": "^8.21.1",
|
|
43
|
-
"@zhin.js/adapter": "1.1.
|
|
44
|
-
"@zhin.js/command": "1.0.
|
|
45
|
-
"@zhin.js/core": "1.5.
|
|
46
|
-
"@zhin.js/host-http": "1.0.
|
|
47
|
-
"@zhin.js/im-contract": "1.0.
|
|
48
|
-
"@zhin.js/logger": "1.0.
|
|
49
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
43
|
+
"@zhin.js/adapter": "1.1.7",
|
|
44
|
+
"@zhin.js/command": "1.0.9",
|
|
45
|
+
"@zhin.js/core": "1.5.4",
|
|
46
|
+
"@zhin.js/host-http": "1.0.8",
|
|
47
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
48
|
+
"@zhin.js/logger": "1.0.76",
|
|
49
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^26.1.2",
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
"typescript": "^6.0.3",
|
|
55
55
|
"vitest": "^4.1.10",
|
|
56
56
|
"zod": "^4.4.3",
|
|
57
|
-
"@zhin.js/agent": "1.1.
|
|
58
|
-
"@zhin.js/host-http": "1.0.
|
|
57
|
+
"@zhin.js/agent": "1.1.5",
|
|
58
|
+
"@zhin.js/host-http": "1.0.8"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"zod": "^4.0.0",
|
|
62
|
-
"@zhin.js/adapter": "1.1.
|
|
63
|
-
"@zhin.js/agent": "1.1.
|
|
64
|
-
"@zhin.js/core": "1.5.
|
|
65
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
66
|
-
"zhin.js": "6.0.
|
|
62
|
+
"@zhin.js/adapter": "1.1.7",
|
|
63
|
+
"@zhin.js/agent": "1.1.5",
|
|
64
|
+
"@zhin.js/core": "1.5.4",
|
|
65
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
66
|
+
"zhin.js": "6.0.4"
|
|
67
67
|
},
|
|
68
68
|
"peerDependenciesMeta": {
|
|
69
69
|
"zhin.js": {
|
package/schema.json
CHANGED
|
@@ -22,16 +22,48 @@
|
|
|
22
22
|
"type": "number",
|
|
23
23
|
"default": 30000
|
|
24
24
|
},
|
|
25
|
+
"master": {
|
|
26
|
+
"type": [
|
|
27
|
+
"string",
|
|
28
|
+
"number"
|
|
29
|
+
],
|
|
30
|
+
"description": "框架 master(QQ uin;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
31
|
+
},
|
|
32
|
+
"trusted": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": {
|
|
35
|
+
"type": [
|
|
36
|
+
"string",
|
|
37
|
+
"number"
|
|
38
|
+
],
|
|
39
|
+
"description": "Trusted QQ uin"
|
|
40
|
+
},
|
|
41
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
42
|
+
},
|
|
25
43
|
"endpoints": {
|
|
26
44
|
"type": "array",
|
|
27
|
-
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(
|
|
45
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
28
46
|
"items": {
|
|
29
47
|
"type": "object",
|
|
30
48
|
"additionalProperties": true,
|
|
31
49
|
"properties": {
|
|
32
|
-
"
|
|
33
|
-
"type":
|
|
34
|
-
|
|
50
|
+
"master": {
|
|
51
|
+
"type": [
|
|
52
|
+
"string",
|
|
53
|
+
"number"
|
|
54
|
+
],
|
|
55
|
+
"description": "本 endpoint 的框架 master(QQ uin);覆盖顶层 master"
|
|
56
|
+
},
|
|
57
|
+
"trusted": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": [
|
|
61
|
+
"string",
|
|
62
|
+
"number"
|
|
63
|
+
],
|
|
64
|
+
"description": "Trusted QQ uin"
|
|
65
|
+
},
|
|
66
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
35
67
|
},
|
|
36
68
|
"baseUrl": {
|
|
37
69
|
"type": "string",
|
|
@@ -44,10 +76,14 @@
|
|
|
44
76
|
"access_token": {
|
|
45
77
|
"type": "string",
|
|
46
78
|
"description": "Milky access token"
|
|
79
|
+
},
|
|
80
|
+
"id": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Milky bot name"
|
|
47
83
|
}
|
|
48
84
|
},
|
|
49
85
|
"required": [
|
|
50
|
-
"
|
|
86
|
+
"id",
|
|
51
87
|
"baseUrl"
|
|
52
88
|
]
|
|
53
89
|
}
|
package/src/milky-agent-deps.ts
CHANGED
|
@@ -18,20 +18,20 @@ export interface MilkyAgentEndpoint {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface MilkyAgentDeps {
|
|
21
|
-
getEndpoint: (
|
|
21
|
+
getEndpoint: (endpointKey: string) => MilkyAgentEndpoint;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const endpoints = new Map<string, MilkyAgentEndpoint>();
|
|
25
25
|
let override: MilkyAgentDeps | null = null;
|
|
26
26
|
|
|
27
27
|
export function registerMilkyAgentEndpoint(
|
|
28
|
-
|
|
28
|
+
endpointKey: string,
|
|
29
29
|
endpoint: MilkyAgentEndpoint,
|
|
30
30
|
): () => void {
|
|
31
|
-
endpoints.set(
|
|
31
|
+
endpoints.set(endpointKey, endpoint);
|
|
32
32
|
return () => {
|
|
33
|
-
if (endpoints.get(
|
|
34
|
-
endpoints.delete(
|
|
33
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
34
|
+
endpoints.delete(endpointKey);
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
}
|
|
@@ -44,9 +44,9 @@ export function setMilkyAgentDeps(deps: MilkyAgentDeps | null): void {
|
|
|
44
44
|
export function getMilkyAgentDeps(): MilkyAgentDeps {
|
|
45
45
|
if (override) return override;
|
|
46
46
|
return {
|
|
47
|
-
getEndpoint(
|
|
48
|
-
const registered = endpoints.get(
|
|
49
|
-
if (!registered) throw new Error(`Endpoint ${
|
|
47
|
+
getEndpoint(endpointKey: string): MilkyAgentEndpoint {
|
|
48
|
+
const registered = endpoints.get(endpointKey);
|
|
49
|
+
if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
50
50
|
return registered;
|
|
51
51
|
},
|
|
52
52
|
};
|
package/src/protocol.ts
CHANGED
|
@@ -15,7 +15,7 @@ const logger = getLogger('milky');
|
|
|
15
15
|
export interface MilkyLegacyEndpointRow {
|
|
16
16
|
readonly context?: string;
|
|
17
17
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
18
|
-
readonly
|
|
18
|
+
readonly id?: string;
|
|
19
19
|
readonly baseUrl?: string;
|
|
20
20
|
readonly access_token?: string;
|
|
21
21
|
readonly path?: string;
|
|
@@ -26,7 +26,7 @@ export interface MilkyLegacyEndpointRow {
|
|
|
26
26
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
27
27
|
export interface MilkyAdapterConfig {
|
|
28
28
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
29
|
-
readonly
|
|
29
|
+
readonly id?: string;
|
|
30
30
|
readonly baseUrl?: string;
|
|
31
31
|
readonly access_token?: string;
|
|
32
32
|
readonly path?: string;
|
|
@@ -38,7 +38,7 @@ export interface MilkyAdapterConfig {
|
|
|
38
38
|
|
|
39
39
|
export interface MilkyConfigBase {
|
|
40
40
|
readonly context: 'milky';
|
|
41
|
-
readonly
|
|
41
|
+
readonly id: string;
|
|
42
42
|
readonly baseUrl: string;
|
|
43
43
|
readonly access_token?: string;
|
|
44
44
|
}
|
|
@@ -130,8 +130,8 @@ function normalizeConnection(connection: string | undefined): 'ws' | 'sse' | 'we
|
|
|
130
130
|
export function resolveMilkyConfig(config: MilkyAdapterConfig = {}): ResolvedMilkyConfig {
|
|
131
131
|
const entry = config.endpoints?.find((item) => item.context === 'milky');
|
|
132
132
|
const connection = normalizeConnection(config.connection ?? entry?.connection);
|
|
133
|
-
const
|
|
134
|
-
|| (typeof entry?.
|
|
133
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
134
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
135
135
|
|| process.env.MILKY_BOT_NAME
|
|
136
136
|
|| 'milky-bot';
|
|
137
137
|
const baseUrl = config.baseUrl ?? entry?.baseUrl;
|
|
@@ -143,7 +143,7 @@ export function resolveMilkyConfig(config: MilkyAdapterConfig = {}): ResolvedMil
|
|
|
143
143
|
const access_token = config.access_token ?? entry?.access_token;
|
|
144
144
|
const base = {
|
|
145
145
|
context: 'milky' as const,
|
|
146
|
-
|
|
146
|
+
id,
|
|
147
147
|
baseUrl,
|
|
148
148
|
access_token,
|
|
149
149
|
};
|
|
@@ -262,11 +262,11 @@ export function isMessageReceiveEvent(
|
|
|
262
262
|
* `temp`(群临时会话)→ 群容器内的 `private` 会话(parent = 来源群)。
|
|
263
263
|
*/
|
|
264
264
|
export function milkyInboundConversation(
|
|
265
|
-
|
|
265
|
+
endpointKey: string,
|
|
266
266
|
data: MilkyIncomingMessage,
|
|
267
267
|
): ConversationRef {
|
|
268
268
|
return {
|
|
269
|
-
endpoint: { id:
|
|
269
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
270
270
|
kind: data.message_scene === 'group' ? 'group' : 'private',
|
|
271
271
|
id: String(data.peer_id),
|
|
272
272
|
...(data.message_scene === 'temp' && data.group
|
package/src/sse-endpoint.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type EndpointSendRequest,
|
|
11
11
|
} from '@zhin.js/adapter';
|
|
12
12
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
13
|
-
import { formatCompact,
|
|
13
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
14
14
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
15
15
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
16
16
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
@@ -35,8 +35,6 @@ import {
|
|
|
35
35
|
} from './protocol.js';
|
|
36
36
|
import { openSseStream, type SseClientHandle } from './sse-client.js';
|
|
37
37
|
|
|
38
|
-
const logger = getLogger('milky');
|
|
39
|
-
|
|
40
38
|
export type CreateMilkySseStream = (options: {
|
|
41
39
|
readonly url: string;
|
|
42
40
|
readonly headers: Record<string, string>;
|
|
@@ -54,6 +52,8 @@ export interface MilkySseEndpointOptions {
|
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
export class MilkySseEndpoint implements EndpointInstance {
|
|
55
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
56
|
+
|
|
57
57
|
readonly #options: MilkySseEndpointOptions;
|
|
58
58
|
readonly #callApi: typeof callApi;
|
|
59
59
|
readonly management: EndpointManagement = createMilkyEndpointManagement(this);
|
|
@@ -63,10 +63,11 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
63
63
|
#unregisterAgent?: () => void;
|
|
64
64
|
|
|
65
65
|
constructor(options: MilkySseEndpointOptions) {
|
|
66
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
66
67
|
this.#options = options;
|
|
67
68
|
this.#callApi = options.callApi ?? callApi;
|
|
68
69
|
this.#lifecycle = createEndpointLifecycle({
|
|
69
|
-
name: options.config.
|
|
70
|
+
name: options.config.id,
|
|
70
71
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
71
72
|
reconnect: {
|
|
72
73
|
initialIntervalMs: options.config.reconnect_interval,
|
|
@@ -79,7 +80,7 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
79
80
|
|
|
80
81
|
async start(): Promise<void> {
|
|
81
82
|
if (this.#lifecycle.started) return;
|
|
82
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
83
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
83
84
|
try {
|
|
84
85
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
85
86
|
} catch (err) {
|
|
@@ -112,9 +113,9 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
112
113
|
const { action, params } = buildSendAction(conversation, message);
|
|
113
114
|
const data = await this.callApi(action, params) as { message_seq?: number } | undefined;
|
|
114
115
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
115
|
-
logger.debug(formatCompact({
|
|
116
|
+
this.#logger.debug(formatCompact({
|
|
116
117
|
op: 'milky_send',
|
|
117
|
-
endpoint: this.#options.config.
|
|
118
|
+
endpoint: this.#options.config.id,
|
|
118
119
|
target: `${conversation.kind}:${conversation.id}`,
|
|
119
120
|
messageId,
|
|
120
121
|
mode: 'sse',
|
|
@@ -231,21 +232,20 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
231
232
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
232
233
|
content,
|
|
233
234
|
segments,
|
|
234
|
-
sender: String(data.sender_id),
|
|
235
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
236
|
+
endpointId: this.#options.config.id,
|
|
237
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
235
238
|
metadata: Object.freeze({
|
|
236
239
|
message_scene: data.message_scene,
|
|
237
240
|
peer_id: String(data.peer_id),
|
|
238
241
|
sender_id: String(data.sender_id),
|
|
239
242
|
message_seq: data.message_seq,
|
|
240
|
-
endpoint: this.#options.config.name,
|
|
241
243
|
time: data.time ?? event.time,
|
|
242
244
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
243
245
|
...(nickname ? { nickname } : {}),
|
|
244
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
245
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
246
246
|
}),
|
|
247
247
|
}).catch((err) => {
|
|
248
|
-
logger.warn(formatCompact({
|
|
248
|
+
this.#logger.warn(formatCompact({
|
|
249
249
|
op: 'milky_gateway_receive_failed',
|
|
250
250
|
target,
|
|
251
251
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -265,8 +265,8 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
265
265
|
onOpen: () => {
|
|
266
266
|
if (settled) return;
|
|
267
267
|
settled = true;
|
|
268
|
-
logger.debug(formatCompact({
|
|
269
|
-
endpoint: this.#options.config.
|
|
268
|
+
this.#logger.debug(formatCompact({
|
|
269
|
+
endpoint: this.#options.config.id,
|
|
270
270
|
mode: 'sse',
|
|
271
271
|
url: safeUrl,
|
|
272
272
|
}));
|
|
@@ -274,9 +274,9 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
274
274
|
},
|
|
275
275
|
onMessage: (data) => this.#onMessage(data),
|
|
276
276
|
onError: (error) => {
|
|
277
|
-
logger.warn(formatCompact({
|
|
277
|
+
this.#logger.warn(formatCompact({
|
|
278
278
|
op: 'sse_error',
|
|
279
|
-
endpoint: this.#options.config.
|
|
279
|
+
endpoint: this.#options.config.id,
|
|
280
280
|
ok: false,
|
|
281
281
|
error: error.message,
|
|
282
282
|
}));
|
|
@@ -297,9 +297,8 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
297
297
|
void stream.closed.then(() => {
|
|
298
298
|
// stop-during-connect 竞态由基座静默 settle(主动停止不算失败),此处仅对运行期断开告警
|
|
299
299
|
if (this.#lifecycle.state !== 'stopped') {
|
|
300
|
-
logger.warn(formatCompact({
|
|
301
|
-
|
|
302
|
-
endpoint: this.#options.config.name,
|
|
300
|
+
this.#logger.warn(formatCompact({
|
|
301
|
+
op: 'disconnect',
|
|
303
302
|
mode: 'sse',
|
|
304
303
|
reconnect_ms: this.#options.config.reconnect_interval,
|
|
305
304
|
}));
|
|
@@ -319,9 +318,9 @@ export class MilkySseEndpoint implements EndpointInstance {
|
|
|
319
318
|
const event = JSON.parse(data) as MilkyEvent;
|
|
320
319
|
this.admit(event);
|
|
321
320
|
} catch (error) {
|
|
322
|
-
logger.warn(formatCompact({
|
|
321
|
+
this.#logger.warn(formatCompact({
|
|
323
322
|
op: 'milky_parse_failed',
|
|
324
|
-
endpoint: this.#options.config.
|
|
323
|
+
endpoint: this.#options.config.id,
|
|
325
324
|
mode: 'sse',
|
|
326
325
|
error: error instanceof Error ? error.message : String(error),
|
|
327
326
|
}));
|
package/src/webhook-endpoint.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
} from '@zhin.js/adapter';
|
|
10
10
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
11
11
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
12
|
-
import { formatCompact,
|
|
12
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
13
13
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
14
14
|
import { readRequestBody, verifyMilkyAccessToken } from './milky-auth.js';
|
|
15
15
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
@@ -33,8 +33,6 @@ import {
|
|
|
33
33
|
type MilkyWebhookConfig,
|
|
34
34
|
} from './protocol.js';
|
|
35
35
|
|
|
36
|
-
const logger = getLogger('milky');
|
|
37
|
-
|
|
38
36
|
export interface MilkyWebhookEndpointOptions {
|
|
39
37
|
readonly id: CapabilityId;
|
|
40
38
|
readonly gateway: MessageGateway;
|
|
@@ -44,6 +42,8 @@ export interface MilkyWebhookEndpointOptions {
|
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
45
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
46
|
+
|
|
47
47
|
readonly #options: MilkyWebhookEndpointOptions;
|
|
48
48
|
readonly #callApi: typeof callApi;
|
|
49
49
|
readonly management: EndpointManagement = createMilkyEndpointManagement(this);
|
|
@@ -53,6 +53,7 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
53
53
|
#unregisterAgent?: () => void;
|
|
54
54
|
|
|
55
55
|
constructor(options: MilkyWebhookEndpointOptions) {
|
|
56
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
56
57
|
this.#options = options;
|
|
57
58
|
this.#callApi = options.callApi ?? callApi;
|
|
58
59
|
}
|
|
@@ -60,11 +61,11 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
60
61
|
async start(): Promise<void> {
|
|
61
62
|
if (this.#started) return;
|
|
62
63
|
this.#started = true;
|
|
63
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
64
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
64
65
|
this.#setupRoutes();
|
|
65
|
-
logger.info(formatCompact({
|
|
66
|
+
this.#logger.info(formatCompact({
|
|
66
67
|
op: 'listen',
|
|
67
|
-
endpoint: this.#options.config.
|
|
68
|
+
endpoint: this.#options.config.id,
|
|
68
69
|
mode: 'webhook',
|
|
69
70
|
path: this.#options.config.path,
|
|
70
71
|
}));
|
|
@@ -84,7 +85,7 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
84
85
|
this.#unregisterAgent?.();
|
|
85
86
|
this.#unregisterAgent = undefined;
|
|
86
87
|
this.#started = false;
|
|
87
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
88
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
@@ -92,9 +93,9 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
92
93
|
const { action, params } = buildSendAction(conversation, message);
|
|
93
94
|
const data = await this.callApi(action, params) as { message_seq?: number } | undefined;
|
|
94
95
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
95
|
-
logger.debug(formatCompact({
|
|
96
|
+
this.#logger.debug(formatCompact({
|
|
96
97
|
op: 'milky_send',
|
|
97
|
-
endpoint: this.#options.config.
|
|
98
|
+
endpoint: this.#options.config.id,
|
|
98
99
|
target: `${conversation.kind}:${conversation.id}`,
|
|
99
100
|
messageId,
|
|
100
101
|
}));
|
|
@@ -210,21 +211,20 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
210
211
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
211
212
|
content,
|
|
212
213
|
segments,
|
|
213
|
-
sender: String(data.sender_id),
|
|
214
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
215
|
+
endpointId: this.#options.config.id,
|
|
216
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
214
217
|
metadata: Object.freeze({
|
|
215
218
|
message_scene: data.message_scene,
|
|
216
219
|
peer_id: String(data.peer_id),
|
|
217
220
|
sender_id: String(data.sender_id),
|
|
218
221
|
message_seq: data.message_seq,
|
|
219
|
-
endpoint: this.#options.config.name,
|
|
220
222
|
time: data.time ?? event.time,
|
|
221
223
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
222
224
|
...(nickname ? { nickname } : {}),
|
|
223
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
224
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
225
225
|
}),
|
|
226
226
|
}).catch((err) => {
|
|
227
|
-
logger.warn(formatCompact({
|
|
227
|
+
this.#logger.warn(formatCompact({
|
|
228
228
|
op: 'milky_gateway_receive_failed',
|
|
229
229
|
target,
|
|
230
230
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -261,7 +261,7 @@ export class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
261
261
|
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
262
262
|
response.end(JSON.stringify({ status: 'ok' }));
|
|
263
263
|
} catch (error) {
|
|
264
|
-
logger.error('Milky webhook error:', error);
|
|
264
|
+
this.#logger.error('Milky webhook error:', error);
|
|
265
265
|
if (!response.headersSent) {
|
|
266
266
|
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
267
267
|
response.end(JSON.stringify({ message: 'Internal Server Error' }));
|
package/src/ws-endpoint.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type EndpointSendRequest,
|
|
12
12
|
} from '@zhin.js/adapter';
|
|
13
13
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
14
|
-
import { formatCompact,
|
|
14
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
15
15
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
16
16
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
17
17
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
} from './protocol.js';
|
|
37
37
|
import type { MilkyWsCreateOptions, MilkyWsSocket } from './ws-types.js';
|
|
38
38
|
|
|
39
|
-
const logger = getLogger('milky');
|
|
40
39
|
const WS_OPEN = 1;
|
|
41
40
|
|
|
42
41
|
export interface MilkyWsEndpointOptions {
|
|
@@ -51,6 +50,8 @@ export interface MilkyWsEndpointOptions {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
export class MilkyWsEndpoint implements EndpointInstance {
|
|
53
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
54
|
+
|
|
54
55
|
readonly #options: MilkyWsEndpointOptions;
|
|
55
56
|
readonly #callApi: typeof callApi;
|
|
56
57
|
readonly management: EndpointManagement = createMilkyEndpointManagement(this);
|
|
@@ -60,10 +61,11 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
60
61
|
#unregisterAgent?: () => void;
|
|
61
62
|
|
|
62
63
|
constructor(options: MilkyWsEndpointOptions) {
|
|
64
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
63
65
|
this.#options = options;
|
|
64
66
|
this.#callApi = options.callApi ?? callApi;
|
|
65
67
|
this.#lifecycle = createEndpointLifecycle({
|
|
66
|
-
name: options.config.
|
|
68
|
+
name: options.config.id,
|
|
67
69
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
68
70
|
reconnect: {
|
|
69
71
|
initialIntervalMs: options.config.reconnect_interval,
|
|
@@ -76,7 +78,7 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
76
78
|
|
|
77
79
|
async start(): Promise<void> {
|
|
78
80
|
if (this.#lifecycle.started) return;
|
|
79
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
81
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
80
82
|
try {
|
|
81
83
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
82
84
|
} catch (err) {
|
|
@@ -115,9 +117,9 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
115
117
|
const { action, params } = buildSendAction(conversation, message);
|
|
116
118
|
const data = await this.callApi(action, params) as { message_seq?: number } | undefined;
|
|
117
119
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
118
|
-
logger.debug(formatCompact({
|
|
120
|
+
this.#logger.debug(formatCompact({
|
|
119
121
|
op: 'milky_send',
|
|
120
|
-
endpoint: this.#options.config.
|
|
122
|
+
endpoint: this.#options.config.id,
|
|
121
123
|
target: `${conversation.kind}:${conversation.id}`,
|
|
122
124
|
messageId,
|
|
123
125
|
}));
|
|
@@ -235,21 +237,20 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
235
237
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
236
238
|
content,
|
|
237
239
|
segments,
|
|
238
|
-
sender: String(data.sender_id),
|
|
240
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
241
|
+
endpointId: this.#options.config.id,
|
|
242
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
239
243
|
metadata: Object.freeze({
|
|
240
244
|
message_scene: data.message_scene,
|
|
241
245
|
peer_id: String(data.peer_id),
|
|
242
246
|
sender_id: String(data.sender_id),
|
|
243
247
|
message_seq: data.message_seq,
|
|
244
|
-
endpoint: this.#options.config.name,
|
|
245
248
|
time: data.time ?? event.time,
|
|
246
249
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
247
250
|
...(nickname ? { nickname } : {}),
|
|
248
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
249
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
250
251
|
}),
|
|
251
252
|
}).catch((err) => {
|
|
252
|
-
logger.warn(formatCompact({
|
|
253
|
+
this.#logger.warn(formatCompact({
|
|
253
254
|
op: 'milky_gateway_receive_failed',
|
|
254
255
|
target,
|
|
255
256
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -279,14 +280,14 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
279
280
|
if (settled) return;
|
|
280
281
|
settled = true;
|
|
281
282
|
if (!this.#options.config.access_token) {
|
|
282
|
-
logger.warn(formatCompact({
|
|
283
|
-
endpoint: this.#options.config.
|
|
283
|
+
this.#logger.warn(formatCompact({
|
|
284
|
+
endpoint: this.#options.config.id,
|
|
284
285
|
ok: false,
|
|
285
286
|
error: 'missing access_token',
|
|
286
287
|
}));
|
|
287
288
|
}
|
|
288
|
-
logger.debug(formatCompact({
|
|
289
|
-
endpoint: this.#options.config.
|
|
289
|
+
this.#logger.debug(formatCompact({
|
|
290
|
+
endpoint: this.#options.config.id,
|
|
290
291
|
mode: 'ws',
|
|
291
292
|
url: safeUrl,
|
|
292
293
|
}));
|
|
@@ -319,9 +320,8 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
319
320
|
: codeNum === 1006
|
|
320
321
|
? ' [异常关闭]'
|
|
321
322
|
: '';
|
|
322
|
-
logger.warn(formatCompact({
|
|
323
|
+
this.#logger.warn(formatCompact({
|
|
323
324
|
op: 'disconnect',
|
|
324
|
-
endpoint: this.#options.config.name,
|
|
325
325
|
code: codeNum,
|
|
326
326
|
error: `${reasonStr || 'closed'}${codeHint}`,
|
|
327
327
|
reconnect_ms: this.#options.config.reconnect_interval,
|
|
@@ -336,9 +336,9 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
336
336
|
|
|
337
337
|
ws.on('error', (err) => {
|
|
338
338
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
339
|
-
logger.warn(formatCompact({
|
|
339
|
+
this.#logger.warn(formatCompact({
|
|
340
340
|
op: 'ws_error',
|
|
341
|
-
endpoint: this.#options.config.
|
|
341
|
+
endpoint: this.#options.config.id,
|
|
342
342
|
ok: false,
|
|
343
343
|
error: error.message,
|
|
344
344
|
}));
|
|
@@ -362,9 +362,9 @@ export class MilkyWsEndpoint implements EndpointInstance {
|
|
|
362
362
|
const event = JSON.parse(raw) as MilkyEvent;
|
|
363
363
|
this.admit(event);
|
|
364
364
|
} catch (error) {
|
|
365
|
-
logger.warn(formatCompact({
|
|
365
|
+
this.#logger.warn(formatCompact({
|
|
366
366
|
op: 'milky_parse_failed',
|
|
367
|
-
endpoint: this.#options.config.
|
|
367
|
+
endpoint: this.#options.config.id,
|
|
368
368
|
error: error instanceof Error ? error.message : String(error),
|
|
369
369
|
}));
|
|
370
370
|
}
|