@zhin.js/adapter-milky 1.0.1 → 1.1.2

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 (99) hide show
  1. package/CHANGELOG.md +1046 -0
  2. package/{skills/milky/PERMITS.md → PERMITS.md} +2 -2
  3. package/README.md +60 -117
  4. package/adapters/milky/index.js +51 -0
  5. package/adapters/milky/index.ts +74 -0
  6. package/commands/milky/endpoint/add/[id]/index.js +3 -0
  7. package/commands/milky/endpoint/add/[id]/index.ts +3 -0
  8. package/commands/milky/endpoint/definition.js +19 -0
  9. package/commands/milky/endpoint/definition.ts +19 -0
  10. package/commands/milky/endpoint/list/index.js +3 -0
  11. package/commands/milky/endpoint/list/index.ts +3 -0
  12. package/commands/milky/endpoint/remove/[id]/index.js +3 -0
  13. package/commands/milky/endpoint/remove/[id]/index.ts +3 -0
  14. package/lib/client.d.ts +21 -0
  15. package/lib/client.js +33 -0
  16. package/lib/endpoint-management.d.ts +17 -0
  17. package/lib/endpoint-management.js +47 -0
  18. package/lib/endpoint.d.ts +7 -0
  19. package/lib/endpoint.js +6 -0
  20. package/lib/index.d.ts +6 -20
  21. package/lib/index.js +4 -35
  22. package/lib/milky-auth.d.ts +2 -0
  23. package/lib/milky-auth.js +16 -0
  24. package/lib/milky-runtime-state.d.ts +1 -0
  25. package/lib/milky-runtime-state.js +6 -0
  26. package/lib/protocol.d.ts +160 -0
  27. package/lib/protocol.js +491 -0
  28. package/lib/sse-client.d.ts +20 -0
  29. package/lib/sse-client.js +85 -0
  30. package/lib/sse-endpoint.d.ts +32 -0
  31. package/lib/sse-endpoint.js +207 -0
  32. package/lib/webhook-endpoint.d.ts +22 -0
  33. package/lib/webhook-endpoint.js +149 -0
  34. package/lib/ws-endpoint.d.ts +23 -0
  35. package/lib/ws-endpoint.js +233 -0
  36. package/lib/ws-types.d.ts +11 -0
  37. package/lib/ws-types.js +1 -0
  38. package/lib/wss-endpoint.d.ts +25 -0
  39. package/lib/wss-endpoint.js +199 -0
  40. package/package.json +78 -19
  41. package/plugin.js +14 -0
  42. package/schema.json +119 -0
  43. package/src/client.ts +80 -0
  44. package/src/endpoint-management.ts +74 -0
  45. package/src/endpoint.ts +26 -0
  46. package/src/index.ts +62 -47
  47. package/src/milky-auth.ts +14 -0
  48. package/src/milky-runtime-state.ts +7 -0
  49. package/src/protocol.ts +633 -0
  50. package/src/sse-client.ts +106 -0
  51. package/src/sse-endpoint.ts +258 -0
  52. package/src/webhook-endpoint.ts +196 -0
  53. package/src/ws-endpoint.ts +291 -0
  54. package/src/ws-types.ts +12 -0
  55. package/src/wss-endpoint.ts +241 -0
  56. package/lib/adapter.d.ts +0 -29
  57. package/lib/adapter.d.ts.map +0 -1
  58. package/lib/adapter.js +0 -91
  59. package/lib/adapter.js.map +0 -1
  60. package/lib/api.d.ts +0 -10
  61. package/lib/api.d.ts.map +0 -1
  62. package/lib/api.js +0 -48
  63. package/lib/api.js.map +0 -1
  64. package/lib/endpoint-sse.d.ts +0 -31
  65. package/lib/endpoint-sse.d.ts.map +0 -1
  66. package/lib/endpoint-sse.js +0 -195
  67. package/lib/endpoint-sse.js.map +0 -1
  68. package/lib/endpoint-webhook.d.ts +0 -34
  69. package/lib/endpoint-webhook.d.ts.map +0 -1
  70. package/lib/endpoint-webhook.js +0 -187
  71. package/lib/endpoint-webhook.js.map +0 -1
  72. package/lib/endpoint-ws.d.ts +0 -35
  73. package/lib/endpoint-ws.d.ts.map +0 -1
  74. package/lib/endpoint-ws.js +0 -262
  75. package/lib/endpoint-ws.js.map +0 -1
  76. package/lib/endpoint-wss.d.ts +0 -34
  77. package/lib/endpoint-wss.d.ts.map +0 -1
  78. package/lib/endpoint-wss.js +0 -225
  79. package/lib/endpoint-wss.js.map +0 -1
  80. package/lib/index.d.ts.map +0 -1
  81. package/lib/index.js.map +0 -1
  82. package/lib/types.d.ts +0 -75
  83. package/lib/types.d.ts.map +0 -1
  84. package/lib/types.js +0 -5
  85. package/lib/types.js.map +0 -1
  86. package/lib/utils.d.ts +0 -35
  87. package/lib/utils.d.ts.map +0 -1
  88. package/lib/utils.js +0 -165
  89. package/lib/utils.js.map +0 -1
  90. package/plugin.yml +0 -3
  91. package/skills/milky/SKILL.md +0 -65
  92. package/src/adapter.ts +0 -103
  93. package/src/api.ts +0 -63
  94. package/src/endpoint-sse.ts +0 -228
  95. package/src/endpoint-webhook.ts +0 -221
  96. package/src/endpoint-ws.ts +0 -296
  97. package/src/endpoint-wss.ts +0 -268
  98. package/src/types.ts +0 -73
  99. package/src/utils.ts +0 -181
package/schema.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "connection": {
7
+ "type": "string",
8
+ "enum": [
9
+ "ws",
10
+ "sse",
11
+ "webhook",
12
+ "wss"
13
+ ],
14
+ "default": "ws",
15
+ "description": "ws (default), sse (EventSource client), webhook or wss (webhook/wss via httpHostToken)"
16
+ },
17
+ "reconnect_interval": {
18
+ "type": "number",
19
+ "default": 5000
20
+ },
21
+ "heartbeat_interval": {
22
+ "type": "number",
23
+ "default": 30000
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
+ },
43
+ "endpoints": {
44
+ "type": "array",
45
+ "minItems": 1,
46
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
47
+ "items": {
48
+ "type": "object",
49
+ "additionalProperties": false,
50
+ "properties": {
51
+ "master": {
52
+ "type": [
53
+ "string",
54
+ "number"
55
+ ],
56
+ "description": "本 endpoint 的框架 master(QQ uin);覆盖顶层 master"
57
+ },
58
+ "trusted": {
59
+ "type": "array",
60
+ "items": {
61
+ "type": [
62
+ "string",
63
+ "number"
64
+ ],
65
+ "description": "Trusted QQ uin"
66
+ },
67
+ "description": "本 endpoint 的 trusted 列表"
68
+ },
69
+ "connection": {
70
+ "type": "string",
71
+ "enum": [
72
+ "ws",
73
+ "sse",
74
+ "webhook",
75
+ "wss"
76
+ ]
77
+ },
78
+ "baseUrl": {
79
+ "type": "string",
80
+ "description": "Milky HTTP API base URL (required); WS event path is derived as ws(s)://host/event"
81
+ },
82
+ "path": {
83
+ "type": "string",
84
+ "description": "Path for webhook / reverse-wss"
85
+ },
86
+ "access_token": {
87
+ "type": "string",
88
+ "description": "Milky access token"
89
+ },
90
+ "reconnect_interval": {
91
+ "type": "number"
92
+ },
93
+ "heartbeat_interval": {
94
+ "type": "number"
95
+ },
96
+ "commandPrefix": {
97
+ "type": "string"
98
+ },
99
+ "id": {
100
+ "type": "string",
101
+ "description": "Milky bot name"
102
+ }
103
+ },
104
+ "required": [
105
+ "id",
106
+ "baseUrl"
107
+ ]
108
+ }
109
+ },
110
+ "commandPrefix": {
111
+ "type": "string",
112
+ "default": "",
113
+ "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
114
+ }
115
+ },
116
+ "required": [
117
+ "endpoints"
118
+ ]
119
+ }
package/src/client.ts ADDED
@@ -0,0 +1,80 @@
1
+ import {
2
+ MilkyV1Client,
3
+ type MilkyV1Event,
4
+ type MilkyV1Response,
5
+ } from '@imhelper/milky-v1';
6
+ import {
7
+ EventFactory,
8
+ type EventMap,
9
+ type ImHelperEventMap,
10
+ } from 'imhelper';
11
+ import {
12
+ defineEndpointClient,
13
+ forwardEndpointClientEvents,
14
+ type ClientEventPayloads,
15
+ } from 'zhin.js/adapter';
16
+ import type { ResolvedMilkyConfig, callApi } from './protocol.js';
17
+
18
+ export { MilkyV1Client as MilkyClient } from '@imhelper/milky-v1';
19
+
20
+ type MilkyClientEvents = ImHelperEventMap<string, MilkyV1Event, EventMap<string>>;
21
+ export type MilkyClientEventMap = ClientEventPayloads<MilkyClientEvents>;
22
+
23
+ /** Construct the exact public imhelper Client without handing it transport ownership. */
24
+ export function createMilkyEndpointClient(
25
+ config: ResolvedMilkyConfig,
26
+ request: typeof callApi,
27
+ ): MilkyV1Client {
28
+ return new MilkyV1Client({
29
+ baseUrl: config.baseUrl,
30
+ selfId: config.id,
31
+ accessToken: config.access_token,
32
+ receiveMode: 'manual',
33
+ call: (action, params) => request(
34
+ {
35
+ baseUrl: config.baseUrl,
36
+ access_token: config.access_token,
37
+ },
38
+ action,
39
+ params,
40
+ ),
41
+ });
42
+ }
43
+
44
+ /** Use the Client's complete protocol response while keeping Endpoint internals data-oriented. */
45
+ export async function callMilkyClient<T = unknown>(
46
+ client: MilkyV1Client,
47
+ action: string,
48
+ params?: Record<string, unknown>,
49
+ ): Promise<T | undefined> {
50
+ const response: MilkyV1Response<T> = await client.call<T>(action, params);
51
+ if (response.status !== 'ok') {
52
+ throw new Error(
53
+ `Milky API ${action}: retcode=${response.retcode}${response.message ? ` ${response.message}` : ''}`,
54
+ );
55
+ }
56
+ return response.data;
57
+ }
58
+
59
+ const milkyClientEventNames = Object.freeze([
60
+ ...EventFactory.getSupportedEventTypes<string>(),
61
+ 'event',
62
+ ]);
63
+
64
+ export function forwardMilkyClientEvents(
65
+ client: MilkyV1Client,
66
+ receive: (name: string, payload: unknown) => void,
67
+ ): () => void {
68
+ return forwardEndpointClientEvents(client, milkyClientEventNames, receive);
69
+ }
70
+
71
+ declare module '@zhin.js/feature-kit' {
72
+ interface AdapterClientRegistry {
73
+ readonly milky: {
74
+ readonly client: MilkyV1Client;
75
+ readonly events: MilkyClientEventMap;
76
+ };
77
+ }
78
+ }
79
+
80
+ export const milkyClient = defineEndpointClient<MilkyV1Client, MilkyClientEventMap>('milky');
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Milky endpoint management 语义端口(Console 社交面 RPC 消费,见
3
+ * packages/im/adapter/src/endpoint-management.ts)。
4
+ *
5
+ * Milky 协议响应 data 为包裹对象:get_friend_list → {friends:[...]}、
6
+ * get_group_list → {groups:[...]}、get_group_member_list → {members:[...]}。
7
+ * 归一化取舍:
8
+ * - friend → {user_id:number, nickname, remark: remark ?? ''}
9
+ * - group → {group_id:number, name: group_name ?? name}
10
+ * - 群成员列表保持 Milky 原生形状,仅保证数组
11
+ */
12
+ import type {
13
+ EndpointFriend,
14
+ EndpointGroup,
15
+ EndpointManagement,
16
+ } from 'zhin.js/adapter';
17
+
18
+ /** 管理面只依赖 endpoint 的 callApi(ws/wss/sse/webhook 四种传输各自实现)。 */
19
+ export interface MilkyManagementCaller {
20
+ callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
21
+ }
22
+
23
+ export function createMilkyEndpointManagement(
24
+ endpoint: MilkyManagementCaller,
25
+ ): EndpointManagement {
26
+ return Object.freeze<EndpointManagement>({
27
+ async listFriends(): Promise<readonly EndpointFriend[]> {
28
+ const data = asRecord(await endpoint.callApi('get_friend_list'));
29
+ return toArray(data.friends).map((value) => {
30
+ const friend = asRecord(value);
31
+ return {
32
+ user_id: toNumberId(friend.user_id, 'user_id'),
33
+ nickname: String(friend.nickname ?? ''),
34
+ remark: String(friend.remark ?? ''),
35
+ };
36
+ });
37
+ },
38
+ async listGroups(): Promise<readonly EndpointGroup[]> {
39
+ const data = asRecord(await endpoint.callApi('get_group_list'));
40
+ return toArray(data.groups).map((value) => {
41
+ const group = asRecord(value);
42
+ return {
43
+ group_id: toNumberId(group.group_id, 'group_id'),
44
+ name: String(group.group_name ?? group.name ?? ''),
45
+ };
46
+ });
47
+ },
48
+ async listGroupMembers(groupId: string): Promise<readonly unknown[]> {
49
+ const data = asRecord(await endpoint.callApi('get_group_member_list', {
50
+ group_id: toNumberId(groupId, 'group_id'),
51
+ }));
52
+ return toArray(data.members);
53
+ },
54
+ });
55
+ }
56
+
57
+ function asRecord(value: unknown): Record<string, unknown> {
58
+ return value !== null && typeof value === 'object'
59
+ ? value as Record<string, unknown>
60
+ : {};
61
+ }
62
+
63
+ function toArray(value: unknown): readonly unknown[] {
64
+ return Array.isArray(value) ? value : [];
65
+ }
66
+
67
+ /** console RPC 传入的 gid/uid 可能是字符串,统一收敛为数字。 */
68
+ function toNumberId(value: unknown, label: string): number {
69
+ const n = Number(value);
70
+ if (!Number.isFinite(n) || String(value ?? '').trim() === '') {
71
+ throw new TypeError(`milky ${label} 必须是数字: ${String(value)}`);
72
+ }
73
+ return n;
74
+ }
@@ -0,0 +1,26 @@
1
+ export {
2
+ MilkyWsEndpoint,
3
+ type MilkyWsEndpointOptions,
4
+ } from './ws-endpoint.js';
5
+
6
+ export {
7
+ MilkyWebhookEndpoint,
8
+ type MilkyWebhookEndpointOptions,
9
+ } from './webhook-endpoint.js';
10
+
11
+ export {
12
+ MilkyWssEndpoint,
13
+ type MilkyWssEndpointOptions,
14
+ } from './wss-endpoint.js';
15
+
16
+ export {
17
+ MilkySseEndpoint,
18
+ type MilkySseEndpointOptions,
19
+ type CreateMilkySseStream,
20
+ } from './sse-endpoint.js';
21
+
22
+ export type { MilkyWsSocket, MilkyWsCreateOptions } from './ws-types.js';
23
+
24
+ export { verifyMilkyAccessToken } from './milky-auth.js';
25
+
26
+ export { openSseStream, consumeSseBuffer } from './sse-client.js';
package/src/index.ts CHANGED
@@ -1,50 +1,65 @@
1
- /**
2
- * Milky 适配器入口:单一适配器,支持 WS/SSE/Webhook/反向 WS,依赖 router 注册
3
- */
4
- import { usePlugin, type Plugin, type Context, createGroupManagementTools, registerDefaultGroupPlatformPermitChecker, type ToolFeature } from 'zhin.js';
5
- import type { Router } from '@zhin.js/host-router';
6
- import { MilkyAdapter } from './adapter.js';
7
- import type { IGroupManagement } from 'zhin.js';
1
+ export {
2
+ buildSendAction,
3
+ buildSseConnectOptions,
4
+ buildWsConnectOptions,
5
+ callApi,
6
+ extractInboundAudioUrl,
7
+ formatInboundContent,
8
+ formatOutboundSegments,
9
+ isMentioned,
10
+ isMessageReceiveEvent,
11
+ milkyInboundConversation,
12
+ parseMessageReceiveData,
13
+ resolveMilkyConfig,
14
+ senderNickname,
15
+ type MilkyApiClientOptions,
16
+ type MilkyApiResponse,
17
+ type MilkyConfigBase,
18
+ type MilkyEndpointConfig,
19
+ type MilkyEvent,
20
+ type MilkyIncomingMessage,
21
+ type MilkyIncomingSegment,
22
+ type MilkyOutgoingSegment,
23
+ type MilkySseConfig,
24
+ type MilkyWebhookConfig,
25
+ type MilkyWireSegment,
26
+ type MilkyWsConfig,
27
+ type MilkyWssConfig,
28
+ type ResolvedMilkyConfig,
29
+ } from './protocol.js';
8
30
 
9
- export * from './types.js';
10
- export { callApi } from './api.js';
11
- export * from './utils.js';
12
- export { MilkyWsClient } from './endpoint-ws.js';
13
- export { MilkySseClient } from './endpoint-sse.js';
14
- export { MilkyWebhookEndpoint } from './endpoint-webhook.js';
15
- export { MilkyWssServer } from './endpoint-wss.js';
16
- export { MilkyAdapter, type MilkyBot } from './adapter.js';
31
+ export {
32
+ MilkySseEndpoint,
33
+ MilkyWebhookEndpoint,
34
+ MilkyWssEndpoint,
35
+ MilkyWsEndpoint,
36
+ consumeSseBuffer,
37
+ openSseStream,
38
+ verifyMilkyAccessToken,
39
+ type CreateMilkySseStream,
40
+ type MilkySseEndpointOptions,
41
+ type MilkyWebhookEndpointOptions,
42
+ type MilkyWssEndpointOptions,
43
+ type MilkyWsCreateOptions,
44
+ type MilkyWsEndpointOptions,
45
+ type MilkyWsSocket,
46
+ } from './endpoint.js';
17
47
 
18
- declare module 'zhin.js' {
19
- namespace Plugin {
20
- interface Contexts {
21
- router: import('@zhin.js/host-router').Router;
22
- }
23
- }
24
- interface Adapters {
25
- milky: MilkyAdapter;
26
- }
27
- }
48
+ export {
49
+ MilkyClient,
50
+ milkyClient,
51
+ type MilkyClientEventMap,
52
+ } from './client.js';
28
53
 
29
- const plugin = usePlugin();
30
- const { provide, useContext } = plugin;
31
- provide({
32
- name: 'milky',
33
- description: 'Milky Adapter(WS 正向 / SSE / Webhook / 反向 WS)',
34
- mounted: async (p: Plugin) => {
35
- const adapter = new MilkyAdapter(p);
36
- await adapter.start();
37
- return adapter;
38
- },
39
- dispose: async (adapter: MilkyAdapter) => {
40
- await adapter.stop();
41
- },
42
- } as unknown as Context<'milky'>);
43
-
44
- useContext('tool', 'milky', (toolService: ToolFeature, milky: MilkyAdapter) => {
45
- const disposers: (() => void)[] = [];
46
- disposers.push(registerDefaultGroupPlatformPermitChecker('milky'));
47
- const groupTools = createGroupManagementTools(milky as unknown as IGroupManagement, 'milky');
48
- disposers.push(...groupTools.map(t => toolService.addTool(t, plugin.name)));
49
- return () => disposers.forEach(d => d());
50
- });
54
+ export type {
55
+ MilkyAdapterConfig as ImHelperMilkyAdapterConfig,
56
+ MilkyActionUrlResolver,
57
+ MilkyCall,
58
+ MilkyMessageReceiveEvent,
59
+ MilkyMessageRecallEvent,
60
+ MilkyV1ClientConfig,
61
+ MilkyV1Event,
62
+ MilkyV1Response,
63
+ } from '@imhelper/milky-v1';
64
+ export { ProtocolError } from '@imhelper/milky-v1';
65
+ export type { ProtocolErrorKind, ProtocolErrorOptions } from 'imhelper';
@@ -0,0 +1,14 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+
3
+ export function verifyMilkyAccessToken(accessToken: string | undefined, request: IncomingMessage): boolean {
4
+ if (!accessToken) return true;
5
+ const auth = request.headers.authorization ?? '';
6
+ if (auth === `Bearer ${accessToken}`) return true;
7
+ try {
8
+ const url = new URL(request.url ?? '/', 'http://localhost');
9
+ if (url.searchParams.get('access_token') === accessToken) return true;
10
+ } catch {
11
+ /* ignore */
12
+ }
13
+ return false;
14
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Milky 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `milky endpoint` 命令共享(同一 owner generation)。
4
+ */
5
+ import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
+
7
+ export const milkyRuntimeStateToken = defineEndpointRuntimeStateToken('milky');