@zhin.js/adapter-milky 1.0.0 → 1.1.0

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.
Files changed (100) hide show
  1. package/CHANGELOG.md +962 -0
  2. package/README.md +58 -117
  3. package/adapters/milky.js +51 -0
  4. package/adapters/milky.ts +74 -0
  5. package/agent/PERMITS.md +14 -0
  6. package/{skills/milky/SKILL.md → agent/skills/milky.md} +1 -1
  7. package/commands/endpoint/add/[id].js +3 -0
  8. package/commands/endpoint/add/[id].ts +3 -0
  9. package/commands/endpoint/list.js +3 -0
  10. package/commands/endpoint/list.ts +3 -0
  11. package/commands/endpoint/remove/[id].js +3 -0
  12. package/commands/endpoint/remove/[id].ts +3 -0
  13. package/lib/client.d.ts +21 -0
  14. package/lib/client.js +33 -0
  15. package/lib/endpoint-management.d.ts +17 -0
  16. package/lib/endpoint-management.js +47 -0
  17. package/lib/endpoint.d.ts +7 -0
  18. package/lib/endpoint.js +6 -0
  19. package/lib/index.d.ts +6 -20
  20. package/lib/index.js +4 -33
  21. package/lib/milky-auth.d.ts +2 -0
  22. package/lib/milky-auth.js +16 -0
  23. package/lib/milky-endpoint-commands.d.ts +1 -0
  24. package/lib/milky-endpoint-commands.js +18 -0
  25. package/lib/milky-runtime-state.d.ts +1 -0
  26. package/lib/milky-runtime-state.js +6 -0
  27. package/lib/protocol.d.ts +174 -0
  28. package/lib/protocol.js +494 -0
  29. package/lib/sse-client.d.ts +20 -0
  30. package/lib/sse-client.js +85 -0
  31. package/lib/sse-endpoint.d.ts +32 -0
  32. package/lib/sse-endpoint.js +207 -0
  33. package/lib/webhook-endpoint.d.ts +22 -0
  34. package/lib/webhook-endpoint.js +149 -0
  35. package/lib/ws-endpoint.d.ts +23 -0
  36. package/lib/ws-endpoint.js +233 -0
  37. package/lib/ws-types.d.ts +11 -0
  38. package/lib/ws-types.js +1 -0
  39. package/lib/wss-endpoint.d.ts +25 -0
  40. package/lib/wss-endpoint.js +184 -0
  41. package/package.json +73 -19
  42. package/plugin.js +14 -0
  43. package/schema.json +100 -0
  44. package/src/client.ts +80 -0
  45. package/src/endpoint-management.ts +74 -0
  46. package/src/endpoint.ts +26 -0
  47. package/src/index.ts +63 -45
  48. package/src/milky-auth.ts +14 -0
  49. package/src/milky-endpoint-commands.ts +19 -0
  50. package/src/milky-runtime-state.ts +7 -0
  51. package/src/protocol.ts +647 -0
  52. package/src/sse-client.ts +106 -0
  53. package/src/sse-endpoint.ts +258 -0
  54. package/src/webhook-endpoint.ts +196 -0
  55. package/src/ws-endpoint.ts +291 -0
  56. package/src/ws-types.ts +12 -0
  57. package/src/wss-endpoint.ts +228 -0
  58. package/lib/adapter.d.ts +0 -28
  59. package/lib/adapter.d.ts.map +0 -1
  60. package/lib/adapter.js +0 -90
  61. package/lib/adapter.js.map +0 -1
  62. package/lib/api.d.ts +0 -10
  63. package/lib/api.d.ts.map +0 -1
  64. package/lib/api.js +0 -48
  65. package/lib/api.js.map +0 -1
  66. package/lib/bot-sse.d.ts +0 -31
  67. package/lib/bot-sse.d.ts.map +0 -1
  68. package/lib/bot-sse.js +0 -195
  69. package/lib/bot-sse.js.map +0 -1
  70. package/lib/bot-webhook.d.ts +0 -34
  71. package/lib/bot-webhook.d.ts.map +0 -1
  72. package/lib/bot-webhook.js +0 -187
  73. package/lib/bot-webhook.js.map +0 -1
  74. package/lib/bot-ws.d.ts +0 -35
  75. package/lib/bot-ws.d.ts.map +0 -1
  76. package/lib/bot-ws.js +0 -262
  77. package/lib/bot-ws.js.map +0 -1
  78. package/lib/bot-wss.d.ts +0 -34
  79. package/lib/bot-wss.d.ts.map +0 -1
  80. package/lib/bot-wss.js +0 -225
  81. package/lib/bot-wss.js.map +0 -1
  82. package/lib/index.d.ts.map +0 -1
  83. package/lib/index.js.map +0 -1
  84. package/lib/types.d.ts +0 -75
  85. package/lib/types.d.ts.map +0 -1
  86. package/lib/types.js +0 -5
  87. package/lib/types.js.map +0 -1
  88. package/lib/utils.d.ts +0 -35
  89. package/lib/utils.d.ts.map +0 -1
  90. package/lib/utils.js +0 -157
  91. package/lib/utils.js.map +0 -1
  92. package/plugin.yml +0 -3
  93. package/src/adapter.ts +0 -101
  94. package/src/api.ts +0 -63
  95. package/src/bot-sse.ts +0 -228
  96. package/src/bot-webhook.ts +0 -221
  97. package/src/bot-ws.ts +0 -296
  98. package/src/bot-wss.ts +0 -268
  99. package/src/types.ts +0 -73
  100. package/src/utils.ts +0 -174
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Milky WS client endpoint — outbound connect to Milky protocol server.
3
+ */
4
+ import WebSocket from 'ws';
5
+ import { ClientEndpoint, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
6
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
+ import { createMilkyEndpointManagement } from './endpoint-management.js';
8
+ import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents, } from './client.js';
9
+ import { buildSendAction, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
10
+ const WS_OPEN = 1;
11
+ export class MilkyWsEndpoint extends ClientEndpoint {
12
+ client;
13
+ #logger;
14
+ #options;
15
+ #callApi;
16
+ management;
17
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
18
+ #lifecycle;
19
+ #ws;
20
+ #clientSocketRelease;
21
+ constructor(options) {
22
+ super();
23
+ this.#logger = getAdapterLogger('milky', options.config.id);
24
+ this.#options = options;
25
+ this.#callApi = options.callApi ?? callApi;
26
+ this.client = createMilkyEndpointClient(options.config, this.#callApi);
27
+ this.management = createMilkyEndpointManagement({
28
+ callApi: (action, params) => callMilkyClient(this.client, action, params),
29
+ });
30
+ this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
31
+ if (name === 'event')
32
+ this.#admitRaw(payload);
33
+ }, (_name, error) => this.#warnPlatformEvent(error));
34
+ this.#lifecycle = createEndpointLifecycle({
35
+ name: options.config.id,
36
+ // reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
37
+ reconnect: {
38
+ initialIntervalMs: options.config.reconnect_interval,
39
+ multiplier: 1,
40
+ maxIntervalMs: Number.MAX_SAFE_INTEGER,
41
+ jitterMs: 0,
42
+ },
43
+ });
44
+ }
45
+ async start() {
46
+ if (this.#lifecycle.started)
47
+ return;
48
+ await this.#lifecycle.start((handle) => this.#connect(handle));
49
+ }
50
+ close() {
51
+ super.close();
52
+ this.#clientSocketRelease?.();
53
+ this.#clientSocketRelease = undefined;
54
+ }
55
+ async stop() {
56
+ this.close();
57
+ await this.#lifecycle.stop();
58
+ if (this.#ws) {
59
+ try {
60
+ this.#ws.close();
61
+ }
62
+ catch {
63
+ /* ignore */
64
+ }
65
+ this.#ws = undefined;
66
+ }
67
+ }
68
+ async send({ conversation, payload }) {
69
+ const message = formatOutboundSegments(payload);
70
+ const { action, params } = buildSendAction(conversation, message);
71
+ const data = await callMilkyClient(this.client, action, params);
72
+ const messageId = formatOutboundMessageId(conversation, data?.message_seq);
73
+ this.#logger.debug(formatCompact({
74
+ op: 'milky_send',
75
+ endpoint: this.#options.config.id,
76
+ target: `${conversation.kind}:${conversation.id}`,
77
+ messageId,
78
+ }));
79
+ return messageId;
80
+ }
81
+ async recallMessage(id) {
82
+ const parsed = parseMilkyMessageId(id);
83
+ if (!parsed)
84
+ throw new Error(`Invalid message id: ${id}`);
85
+ if (parsed.message_scene === 'group') {
86
+ await callMilkyClient(this.client, 'recall_group_message', {
87
+ group_id: parsed.peer_id,
88
+ message_seq: parsed.message_seq,
89
+ });
90
+ }
91
+ else {
92
+ await callMilkyClient(this.client, 'recall_private_message', {
93
+ user_id: parsed.peer_id,
94
+ message_seq: parsed.message_seq,
95
+ });
96
+ }
97
+ }
98
+ #admitRaw(event) {
99
+ const data = parseMessageReceiveData(event);
100
+ if (!data)
101
+ return;
102
+ this.#admitMessage(data, event);
103
+ }
104
+ #warnPlatformEvent(error) {
105
+ this.#logger.warn(formatCompact({
106
+ op: 'milky_platform_event_failed',
107
+ error: error instanceof Error ? error.message : String(error),
108
+ }));
109
+ }
110
+ #admitMessage(data, event) {
111
+ const conversation = milkyInboundConversation(String(this.#options.id), data);
112
+ const target = `${conversation.kind}:${conversation.id}`;
113
+ const content = formatInboundContent(data);
114
+ const segments = formatInboundSegments(data);
115
+ const audioUrl = extractInboundAudioUrl(data);
116
+ const nickname = senderNickname(data);
117
+ const mentioned = isMentioned(data, event.self_id);
118
+ void this.emit('message.receive', {
119
+ conversation,
120
+ message: { conversation, id: formatInboundMessageId(data) },
121
+ content,
122
+ segments,
123
+ sender: { id: String(data.sender_id), name: nickname },
124
+ endpointId: this.#options.config.id,
125
+ ...(mentioned ? { mentioned: true } : {}),
126
+ metadata: Object.freeze({
127
+ message_scene: data.message_scene,
128
+ peer_id: String(data.peer_id),
129
+ sender_id: String(data.sender_id),
130
+ message_seq: data.message_seq,
131
+ time: data.time ?? event.time,
132
+ self_id: event.self_id != null ? String(event.self_id) : undefined,
133
+ ...(nickname ? { nickname } : {}),
134
+ }),
135
+ }).catch((err) => {
136
+ this.#logger.warn(formatCompact({
137
+ op: 'milky_gateway_receive_failed',
138
+ target,
139
+ error: err instanceof Error ? err.message : String(err),
140
+ }));
141
+ });
142
+ }
143
+ async #connect(handle) {
144
+ const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
145
+ const create = this.#options.createWebSocket
146
+ ?? ((connectUrl, options) => new WebSocket(connectUrl, { headers: options.headers }));
147
+ await new Promise((resolve, reject) => {
148
+ let settled = false;
149
+ const ws = create(url, { headers });
150
+ this.#ws = ws;
151
+ handle.onForceClose(() => {
152
+ try {
153
+ ws.close();
154
+ }
155
+ catch {
156
+ /* ignore */
157
+ }
158
+ });
159
+ ws.on('open', () => {
160
+ if (settled)
161
+ return;
162
+ settled = true;
163
+ if (!this.#options.config.access_token) {
164
+ this.#logger.warn(formatCompact({
165
+ endpoint: this.#options.config.id,
166
+ ok: false,
167
+ error: 'missing access_token',
168
+ }));
169
+ }
170
+ this.#logger.debug(formatCompact({
171
+ endpoint: this.#options.config.id,
172
+ mode: 'ws',
173
+ url: safeUrl,
174
+ }));
175
+ // stop-during-connect 竞态:已停止则不再武装心跳(基座 stop 已清理定时器)
176
+ if (this.#lifecycle.started) {
177
+ this.#lifecycle.startHeartbeat(() => {
178
+ try {
179
+ if (ws.readyState === WS_OPEN)
180
+ ws.ping?.();
181
+ }
182
+ catch {
183
+ /* ignore */
184
+ }
185
+ }, this.#options.config.heartbeat_interval);
186
+ }
187
+ resolve();
188
+ });
189
+ this.#clientSocketRelease?.();
190
+ this.#clientSocketRelease = this.client.acceptWebSocket(ws);
191
+ ws.on('close', (code, reason) => {
192
+ this.#clientSocketRelease?.();
193
+ this.#clientSocketRelease = undefined;
194
+ const reasonStr = typeof reason === 'string'
195
+ ? reason
196
+ : Buffer.isBuffer(reason)
197
+ ? reason.toString()
198
+ : String(reason ?? '');
199
+ const codeNum = typeof code === 'number' ? code : Number(code ?? 0);
200
+ const codeHint = codeNum === 1005
201
+ ? ' [无状态,多为服务端/代理未发 close 帧即断开]'
202
+ : codeNum === 1006
203
+ ? ' [异常关闭]'
204
+ : '';
205
+ this.#logger.warn(formatCompact({
206
+ op: 'disconnect',
207
+ code: codeNum,
208
+ error: `${reasonStr || 'closed'}${codeHint}`,
209
+ reconnect_ms: this.#options.config.reconnect_interval,
210
+ }));
211
+ // 基座语义:仅曾 open 的连接才武装重连;初始连接失败由 start() 的 catch 复位
212
+ handle.notifyClosed(new Error(`Milky WS 关闭: ${codeNum} ${reasonStr}`));
213
+ if (!settled) {
214
+ settled = true;
215
+ reject(new Error(`Milky WS 关闭: ${codeNum} ${reasonStr}`));
216
+ }
217
+ });
218
+ ws.on('error', (err) => {
219
+ const error = err instanceof Error ? err : new Error(String(err));
220
+ this.#logger.warn(formatCompact({
221
+ op: 'ws_error',
222
+ endpoint: this.#options.config.id,
223
+ ok: false,
224
+ error: error.message,
225
+ }));
226
+ if (!settled) {
227
+ settled = true;
228
+ reject(error);
229
+ }
230
+ });
231
+ });
232
+ }
233
+ }
@@ -0,0 +1,11 @@
1
+ /** Minimal WS surface used by the endpoint (real `ws` or test mock). */
2
+ export interface MilkyWsSocket {
3
+ readonly readyState: number;
4
+ send?(data: string): void;
5
+ close(code?: number, reason?: string): void;
6
+ ping?(): void;
7
+ on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: unknown[]) => void): void;
8
+ }
9
+ export interface MilkyWsCreateOptions {
10
+ readonly headers?: Record<string, string>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
3
+ */
4
+ import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
5
+ import type { HttpHost } from '@zhin.js/host-http';
6
+ import type { CapabilityId } from 'zhin.js';
7
+ import { type MilkyClient } from './client.js';
8
+ import { callApi, type MilkyWssConfig } from './protocol.js';
9
+ export interface MilkyWssEndpointOptions {
10
+ readonly id: CapabilityId;
11
+ readonly http: HttpHost;
12
+ readonly config: MilkyWssConfig;
13
+ readonly callApi?: typeof callApi;
14
+ }
15
+ export declare class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
16
+ #private;
17
+ readonly client: MilkyClient;
18
+ readonly management: EndpointManagement;
19
+ readonly control: EndpointControl;
20
+ constructor(options: MilkyWssEndpointOptions);
21
+ start(): Promise<void>;
22
+ stop(): Promise<void>;
23
+ send({ conversation, payload }: EndpointSendRequest): Promise<string>;
24
+ recallMessage(id: string): Promise<void>;
25
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
3
+ */
4
+ import { ClientEndpoint, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
5
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
6
+ import { verifyMilkyAccessToken } from './milky-auth.js';
7
+ import { createMilkyEndpointManagement } from './endpoint-management.js';
8
+ import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents } from './client.js';
9
+ import { buildSendAction, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
10
+ const WS_OPEN = 1;
11
+ export class MilkyWssEndpoint extends ClientEndpoint {
12
+ client;
13
+ #logger;
14
+ #options;
15
+ #callApi;
16
+ management;
17
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
18
+ #ws;
19
+ #wsRelease;
20
+ #clientSocketRelease;
21
+ #lifecycle;
22
+ constructor(options) {
23
+ super();
24
+ this.#logger = getAdapterLogger('milky', options.config.id);
25
+ this.#options = options;
26
+ this.#callApi = options.callApi ?? callApi;
27
+ this.#lifecycle = createEndpointLifecycle({
28
+ name: options.config.id,
29
+ reconnect: false,
30
+ heartbeat: { intervalMs: options.config.heartbeat_interval },
31
+ });
32
+ this.client = createMilkyEndpointClient(options.config, this.#callApi);
33
+ this.management = createMilkyEndpointManagement({
34
+ callApi: (action, params) => callMilkyClient(this.client, action, params),
35
+ });
36
+ this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
37
+ if (name === 'event')
38
+ this.#admitRaw(payload);
39
+ }, (_name, error) => this.#warnPlatformEvent(error));
40
+ }
41
+ async start() {
42
+ await this.#lifecycle.start(async (lifecycleHandle) => {
43
+ const handle = this.#options.http.ws(this.#options.config.path);
44
+ this.#wsRelease = handle.onConnection((connection) => {
45
+ this.#acceptConnection(connection);
46
+ });
47
+ lifecycleHandle.onForceClose(() => {
48
+ this.#wsRelease?.();
49
+ this.#wsRelease = undefined;
50
+ this.#clientSocketRelease?.();
51
+ this.#clientSocketRelease = undefined;
52
+ try {
53
+ this.#ws?.close();
54
+ }
55
+ catch {
56
+ /* ignore */
57
+ }
58
+ this.#ws = undefined;
59
+ });
60
+ });
61
+ this.#logger.info(formatCompact({
62
+ op: 'listen',
63
+ endpoint: this.#options.config.id,
64
+ mode: 'wss',
65
+ path: this.#options.config.path,
66
+ }));
67
+ }
68
+ async stop() {
69
+ this.close();
70
+ await this.#lifecycle.stop();
71
+ }
72
+ async send({ conversation, payload }) {
73
+ const message = formatOutboundSegments(payload);
74
+ const { action, params } = buildSendAction(conversation, message);
75
+ const data = await callMilkyClient(this.client, action, params);
76
+ const messageId = formatOutboundMessageId(conversation, data?.message_seq);
77
+ this.#logger.debug(formatCompact({
78
+ op: 'milky_send',
79
+ endpoint: this.#options.config.id,
80
+ target: `${conversation.kind}:${conversation.id}`,
81
+ messageId,
82
+ }));
83
+ return messageId;
84
+ }
85
+ async recallMessage(id) {
86
+ const parsed = parseMilkyMessageId(id);
87
+ if (!parsed)
88
+ throw new Error(`Invalid message id: ${id}`);
89
+ if (parsed.message_scene === 'group') {
90
+ await callMilkyClient(this.client, 'recall_group_message', {
91
+ group_id: parsed.peer_id,
92
+ message_seq: parsed.message_seq,
93
+ });
94
+ }
95
+ else {
96
+ await callMilkyClient(this.client, 'recall_private_message', {
97
+ user_id: parsed.peer_id,
98
+ message_seq: parsed.message_seq,
99
+ });
100
+ }
101
+ }
102
+ #admitRaw(event) {
103
+ const data = parseMessageReceiveData(event);
104
+ if (!data)
105
+ return;
106
+ this.#admitMessage(data, event);
107
+ }
108
+ #warnPlatformEvent(error) {
109
+ this.#logger.warn(formatCompact({ op: 'milky_platform_event_failed', error: error instanceof Error ? error.message : String(error) }));
110
+ }
111
+ #admitMessage(data, event) {
112
+ const conversation = milkyInboundConversation(String(this.#options.id), data);
113
+ const target = `${conversation.kind}:${conversation.id}`;
114
+ const content = formatInboundContent(data);
115
+ const segments = formatInboundSegments(data);
116
+ const audioUrl = extractInboundAudioUrl(data);
117
+ const nickname = senderNickname(data);
118
+ const mentioned = isMentioned(data, event.self_id);
119
+ void this.emit('message.receive', {
120
+ conversation,
121
+ message: { conversation, id: formatInboundMessageId(data) },
122
+ content,
123
+ segments,
124
+ sender: { id: String(data.sender_id) },
125
+ endpointId: this.#options.config.id,
126
+ ...(mentioned ? { mentioned: true } : {}),
127
+ metadata: Object.freeze({
128
+ message_scene: data.message_scene,
129
+ peer_id: String(data.peer_id),
130
+ sender_id: String(data.sender_id),
131
+ message_seq: data.message_seq,
132
+ time: data.time ?? event.time,
133
+ self_id: event.self_id != null ? String(event.self_id) : undefined,
134
+ ...(nickname ? { nickname } : {}),
135
+ }),
136
+ }).catch((err) => {
137
+ this.#logger.warn(formatCompact({
138
+ op: 'milky_gateway_receive_failed',
139
+ target,
140
+ error: err instanceof Error ? err.message : String(err),
141
+ }));
142
+ });
143
+ }
144
+ #acceptConnection(connection) {
145
+ if (!verifyMilkyAccessToken(this.#options.config.access_token, connection.request)) {
146
+ connection.socket.close(4003, 'Unauthorized');
147
+ return;
148
+ }
149
+ const socket = connection.socket;
150
+ if (this.#ws) {
151
+ try {
152
+ this.#ws.close();
153
+ }
154
+ catch {
155
+ /* ignore */
156
+ }
157
+ }
158
+ this.#ws = socket;
159
+ this.#lifecycle.startHeartbeat(() => {
160
+ try {
161
+ if (this.#ws?.readyState === WS_OPEN)
162
+ this.#ws.ping?.();
163
+ }
164
+ catch {
165
+ /* ignore */
166
+ }
167
+ });
168
+ this.#clientSocketRelease?.();
169
+ this.#clientSocketRelease = this.client.acceptWebSocket(socket);
170
+ socket.on('close', () => {
171
+ this.#clientSocketRelease?.();
172
+ this.#clientSocketRelease = undefined;
173
+ if (this.#ws === socket) {
174
+ this.#ws = undefined;
175
+ this.#lifecycle.stopHeartbeat();
176
+ }
177
+ });
178
+ this.#logger.debug(formatCompact({
179
+ endpoint: this.#options.config.id,
180
+ mode: 'wss',
181
+ peer: connection.request.socket.remoteAddress,
182
+ }));
183
+ }
184
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-milky",
3
- "version": "1.0.0",
4
- "description": "Zhin.js adapter for Milky protocol",
3
+ "version": "1.1.0",
4
+ "description": "Zhin.js Milky adapter for Plugin Runtime (WebSocket client)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -13,13 +13,15 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
+ "adapters",
17
+ "commands",
18
+ "plugin.js",
19
+ "schema.json",
16
20
  "src",
17
21
  "lib",
18
- "client",
19
- "dist",
20
- "skills",
21
- "plugin.yml",
22
- "README.md"
22
+ "agent",
23
+ "README.md",
24
+ "CHANGELOG.md"
23
25
  ],
24
26
  "keywords": [
25
27
  "zhin",
@@ -37,29 +39,81 @@
37
39
  "directory": "plugins/adapters/milky"
38
40
  },
39
41
  "dependencies": {
40
- "eventsource": "^2.0.2",
41
- "ws": "^8.21.0"
42
+ "@imhelper/milky-v1": "1.0.7",
43
+ "imhelper": "1.0.7",
44
+ "ws": "^8.21.1",
45
+ "@zhin.js/adapter": "1.1.12",
46
+ "@zhin.js/core": "1.1.35",
47
+ "@zhin.js/feature-kit": "1.1.0",
48
+ "@zhin.js/host-http": "1.1.0",
49
+ "@zhin.js/im-contract": "1.1.0",
50
+ "@zhin.js/logger": "1.1.0"
42
51
  },
43
52
  "devDependencies": {
44
- "@types/eventsource": "^1.1.15",
45
- "@types/node": "^25.9.1",
53
+ "@types/node": "^26.1.2",
46
54
  "@types/ws": "^8.18.1",
47
55
  "typescript": "^6.0.3",
48
- "@zhin.js/cli": "1.0.88",
49
- "@zhin.js/client": "2.0.2",
50
- "zhin.js": "2.0.0"
56
+ "vitest": "^4.1.10",
57
+ "zod": "^4.4.3",
58
+ "@zhin.js/agent": "1.1.23",
59
+ "@zhin.js/host-http": "1.1.0",
60
+ "zhin.js": "1.1.0"
51
61
  },
52
62
  "peerDependencies": {
53
- "@zhin.js/host-router": "1.0.0",
54
- "zhin.js": "2.0.0"
63
+ "zod": "^4.0.0",
64
+ "@zhin.js/adapter": "^1.1.12",
65
+ "@zhin.js/agent": "^1.1.23",
66
+ "@zhin.js/command": "^1.1.0",
67
+ "@zhin.js/core": "^1.1.35",
68
+ "zhin.js": "^1.1.0"
55
69
  },
56
70
  "peerDependenciesMeta": {
57
- "@zhin.js/host-router": {
71
+ "@zhin.js/agent": {
72
+ "optional": true
73
+ },
74
+ "@zhin.js/command": {
75
+ "optional": true
76
+ },
77
+ "zhin.js": {
78
+ "optional": true
79
+ },
80
+ "zod": {
58
81
  "optional": true
59
82
  }
60
83
  },
84
+ "author": {
85
+ "name": "lc-cn",
86
+ "email": "admin@liucl.cn",
87
+ "url": "https://github.com/lc-cn"
88
+ },
89
+ "publishConfig": {
90
+ "access": "public",
91
+ "registry": "https://registry.npmjs.org"
92
+ },
93
+ "engines": {
94
+ "node": "^20.19.0 || >=22.12.0"
95
+ },
96
+ "zhin": {
97
+ "protocol": 1,
98
+ "type": "plugin",
99
+ "entry": "./plugin.js",
100
+ "engine": "^1.0.0",
101
+ "runtime": "trusted",
102
+ "features": [
103
+ {
104
+ "package": "@zhin.js/adapter",
105
+ "api": "^1.0.0"
106
+ },
107
+ {
108
+ "package": "@zhin.js/command",
109
+ "api": "^1.0.0"
110
+ }
111
+ ],
112
+ "plugins": []
113
+ },
61
114
  "scripts": {
62
- "build": "zhin build",
63
- "clean": "rimraf lib"
115
+ "build": "tsc",
116
+ "clean": "rimraf lib",
117
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/milky/tests"
64
118
  }
65
119
  }
package/plugin.js ADDED
@@ -0,0 +1,14 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { createEndpointRuntimeState } from 'zhin.js/adapter';
3
+ import { definePlugin } from 'zhin.js';
4
+ import { milkyRuntimeStateToken } from "./lib/milky-runtime-state.js";
5
+ export default definePlugin({
6
+ name: 'milky',
7
+ metadata: {
8
+ displayName: 'Milky Adapter',
9
+ },
10
+ setup(context) {
11
+ // 运行中 endpoint 注册表(milky.endpoint list 的"运行中"数据源)
12
+ context.resources.provide(milkyRuntimeStateToken, createEndpointRuntimeState());
13
+ },
14
+ });