@zhin.js/host-http 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -45,5 +45,6 @@ http:
45
45
  base: /api # auth prefix
46
46
  ```
47
47
 
48
- Still deferred to later host migration: full `@zhin.js/host-api` management-plane REST/RPC/SSE
49
- and retiring the legacy Koa `@zhin.js/host-router` stack.
48
+ The legacy Koa `@zhin.js/host-router` stack and the `@zhin.js/host-api` management-plane plugin have
49
+ been removed; the management-plane REST/RPC/SSE is now assembled by `@zhin.js/cli` (Console Host) on
50
+ top of this package — no Host plugins need to be installed or enabled.
@@ -0,0 +1,13 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ /**
3
+ * Console 实时事件枢纽(Plugin Runtime Host 的 `/api/events` SSE 事件源)。
4
+ * 单进程内 fan-out:`publish` 向所有已订阅的 SSE response 写帧;
5
+ * write 失败或连接断开自动摘除订阅。
6
+ */
7
+ export interface ConsoleEventHub {
8
+ publish(type: string, data: unknown): void;
9
+ /** 订阅事件流;返回 unsubscribe。连接断开时也会自动摘除。 */
10
+ subscribe(response: ServerResponse): () => void;
11
+ readonly subscriberCount: number;
12
+ }
13
+ export declare function createConsoleEventHub(): ConsoleEventHub;
@@ -0,0 +1,33 @@
1
+ export function createConsoleEventHub() {
2
+ let nextId = 0;
3
+ const subscribers = new Set();
4
+ const remove = (response) => {
5
+ subscribers.delete(response);
6
+ };
7
+ return {
8
+ get subscriberCount() {
9
+ return subscribers.size;
10
+ },
11
+ publish(type, data) {
12
+ nextId += 1;
13
+ const frame = `id: ${nextId}\nevent: ${type}\ndata: ${JSON.stringify(data)}\n\n`;
14
+ for (const response of [...subscribers]) {
15
+ try {
16
+ response.write(frame, (error) => {
17
+ if (error)
18
+ remove(response);
19
+ });
20
+ }
21
+ catch {
22
+ remove(response);
23
+ }
24
+ }
25
+ },
26
+ subscribe(response) {
27
+ subscribers.add(response);
28
+ response.once('close', () => remove(response));
29
+ response.once('error', () => remove(response));
30
+ return () => remove(response);
31
+ },
32
+ };
33
+ }
@@ -99,12 +99,19 @@ function registerLogsRoutes(route, base, ctx) {
99
99
  });
100
100
  return;
101
101
  }
102
- const total = await LogModel.select();
102
+ // 计数走 DB 侧聚合(count);模型未实现 count 时降级为 select('id') 只取主键列
103
+ const countRows = async (where) => {
104
+ if (typeof LogModel.count === 'function')
105
+ return LogModel.count(where);
106
+ const selection = LogModel.select('id');
107
+ const rows = await (where ? selection.where(where) : selection);
108
+ return rows.length;
109
+ };
110
+ const total = await countRows();
103
111
  const levels = ['info', 'warn', 'error'];
104
112
  const levelCounts = {};
105
113
  for (const level of levels) {
106
- const count = await LogModel.select().where({ level });
107
- levelCounts[level] = count.length;
114
+ levelCounts[level] = await countRows({ level });
108
115
  }
109
116
  const oldestLog = await LogModel.select('timestamp')
110
117
  .orderBy('timestamp', 'ASC')
@@ -116,7 +123,7 @@ function registerLogsRoutes(route, base, ctx) {
116
123
  : null;
117
124
  writeJson(response, 200, {
118
125
  success: true,
119
- data: { total: total.length, byLevel: levelCounts, oldestTimestamp },
126
+ data: { total, byLevel: levelCounts, oldestTimestamp },
120
127
  });
121
128
  }, { summary: 'System log stats', tags: ['console', 'logs'] });
122
129
  route('POST', `${base}/logs/cleanup`, async (request, response, _url, authScope) => {
@@ -9,9 +9,10 @@
9
9
  * - cron:`ctx.scheduleHost`(Plugin Runtime ScheduleHost,仅 register/list,
10
10
  * 无持久化引擎,故 cron:add/remove/pause/resume 报"未接线")。
11
11
  * - inbox:`ctx.databaseHost.models`(unified_inbox_message/request/notice 三张表,
12
- * 表不存在或查询失败 → 空数组 + inboxEnabled:false)。
13
- * - 社交/群管:`ctx.resolveEndpoint` 拿 live endpoint 实例,
14
- * 实例上探测到对应方法才调用,否则报"该平台不支持"。
12
+ * 表不存在或查询失败 → 空数组 + inboxEnabled:false;requestConsumed/noticeConsumed
13
+ * 按行 id update consumed=1,表/model 缺失时报"未接线")。
14
+ * - 社交/群管:`ctx.resolveEndpoint` 拿 live endpoint 实例,只消费其
15
+ * `management` 语义端口;平台 SDK 兼容与字段归一化由 Adapter 负责。
15
16
  */
16
17
  export interface ConsoleRpcExtendedCtx {
17
18
  /** full scope 才允许写操作;demo scope 只放行只读 RPC。 */
@@ -19,7 +20,9 @@ export interface ConsoleRpcExtendedCtx {
19
20
  projectRoot: string;
20
21
  /** Plugin Runtime ScheduleHost(basic/cli schedule-host-installer 提供)。 */
21
22
  scheduleHost?: unknown;
22
- /** 经 ImRuntime / AdapterEndpointIndex 解析 live endpoint 实例。 */
23
+ /** 经 ImRuntime / AdapterEndpointIndex 解析管理语义端口。 */
24
+ resolveEndpointManagement?: (adapter: string, endpointId: string) => EndpointManagementPort | null | undefined;
25
+ /** @deprecated Use `resolveEndpointManagement`; accepted for one compatibility cycle. */
23
26
  resolveEndpoint?: (adapter: string, endpointId: string) => unknown;
24
27
  /** Plugin Runtime DatabaseHost 的 models 视图。 */
25
28
  databaseHost?: {
@@ -27,10 +30,47 @@ export interface ConsoleRpcExtendedCtx {
27
30
  get(name: string): unknown;
28
31
  };
29
32
  };
33
+ /** Agent Host 持久化调度引擎(@zhin.js/agent getAssistantRuntime().engine),未 init 时返回 null。 */
34
+ resolveScheduleEngine?: () => ConsoleScheduleEngine | null | undefined;
35
+ }
36
+ export interface ConsoleScheduleEngine {
37
+ addJob(job: Record<string, unknown>): Promise<Record<string, unknown>>;
38
+ removeJob(id: string): Promise<boolean>;
39
+ pauseJob(id: string): Promise<boolean>;
40
+ resumeJob(id: string): Promise<boolean>;
41
+ listJobs(): Promise<Record<string, unknown>[]>;
30
42
  }
31
43
  export type ExtendedRpcResult = {
32
44
  data: unknown;
33
45
  } | {
34
46
  error: string;
35
47
  };
48
+ interface EndpointManagementPort {
49
+ listFriends?(): Promise<readonly {
50
+ user_id: number | string;
51
+ nickname: string;
52
+ remark: string;
53
+ }[]>;
54
+ listGroups?(): Promise<readonly {
55
+ group_id: number | string;
56
+ name: string;
57
+ }[]>;
58
+ listChannels?(): Promise<readonly {
59
+ id: string;
60
+ name?: string;
61
+ parent?: {
62
+ type: string;
63
+ id: string;
64
+ name?: string;
65
+ };
66
+ }[]>;
67
+ listGroupMembers?(groupId: string): Promise<readonly unknown[]>;
68
+ approveRequest?(requestId: string, remark?: string): Promise<void>;
69
+ rejectRequest?(requestId: string, reason?: string): Promise<void>;
70
+ kickGroupMember?(groupId: string, userId: string): Promise<void>;
71
+ muteGroupMember?(groupId: string, userId: string, durationSeconds: number): Promise<void>;
72
+ setGroupAdmin?(groupId: string, userId: string, enabled: boolean): Promise<void>;
73
+ deleteFriend?(userId: string): Promise<void>;
74
+ }
36
75
  export declare function dispatchExtendedConsoleRpc(type: string, data: Record<string, unknown> | undefined, ctx: ConsoleRpcExtendedCtx): Promise<ExtendedRpcResult | undefined>;
76
+ export {};