@zhin.js/core 1.3.3 → 1.3.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/README.md +12 -0
- package/lib/adapter.js +2 -2
- package/lib/built/ai-outbound/parse.js +26 -12
- package/lib/built/authorization.js +2 -2
- package/lib/built/component.d.ts +1 -2
- package/lib/built/component.js +1 -1
- package/lib/built/database.d.ts +1 -2
- package/lib/built/database.js +1 -1
- package/lib/built/dispatcher.d.ts +1 -1
- package/lib/built/endpoint-lifecycle-service.js +2 -1
- package/lib/built/endpoint-lifecycle.js +4 -3
- package/lib/built/html-to-text.js +15 -14
- package/lib/built/inbound-pipeline.d.ts +1 -1
- package/lib/built/inbound-pipeline.js +1 -1
- package/lib/built/interactive-segments/action.js +16 -1
- package/lib/built/message-enrich.d.ts +1 -1
- package/lib/built/permit-check.d.ts +1 -1
- package/lib/built/platform-permit.js +35 -10
- package/lib/built/schedule.d.ts +1 -2
- package/lib/built/schedule.js +1 -1
- package/lib/component.js +28 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/notice.d.ts +15 -58
- package/lib/notice.js +1 -3
- package/lib/plugin-runtime/im/contracts.d.ts +71 -0
- package/lib/plugin-runtime/im/contracts.js +55 -0
- package/lib/plugin-runtime/im/im-runtime.d.ts +70 -0
- package/lib/plugin-runtime/im/im-runtime.js +299 -0
- package/lib/plugin-runtime/im/index.d.ts +5 -0
- package/lib/plugin-runtime/im/index.js +5 -0
- package/lib/plugin-runtime/im/message-dispatcher.d.ts +7 -0
- package/lib/plugin-runtime/im/message-dispatcher.js +26 -0
- package/lib/plugin-runtime/im/outbound-renderer.d.ts +6 -0
- package/lib/plugin-runtime/im/outbound-renderer.js +31 -0
- package/lib/plugin-runtime/im/outbound-segments.d.ts +22 -0
- package/lib/plugin-runtime/im/outbound-segments.js +59 -0
- package/lib/plugin.d.ts +2 -4
- package/lib/plugin.js +12 -8
- package/lib/request.d.ts +16 -59
- package/lib/request.js +0 -3
- package/lib/side-event/base.d.ts +45 -0
- package/lib/side-event/base.js +30 -0
- package/lib/side-event/index.d.ts +3 -0
- package/lib/side-event/index.js +3 -0
- package/lib/side-event/normalize.d.ts +40 -0
- package/lib/side-event/normalize.js +117 -0
- package/lib/side-event/types.d.ts +25 -0
- package/lib/side-event/types.js +55 -0
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @zhin.js/core
|
|
2
2
|
|
|
3
|
+
## Plugin Runtime 子路径
|
|
4
|
+
|
|
5
|
+
新的 owner-aware IM 能力已经并入 Core,作为 Plugin Runtime 的正式领域接口:
|
|
6
|
+
|
|
7
|
+
- `@zhin.js/core/runtime`
|
|
8
|
+
|
|
9
|
+
`@zhin.js/adapter`、`@zhin.js/command`、`@zhin.js/component` 与
|
|
10
|
+
`@zhin.js/middleware` 提供纯 definition、约定发现 provider 和 generation projection;
|
|
11
|
+
Core Runtime 只消费它们发布的 snapshot。
|
|
12
|
+
旧根入口的 `addCommand`、`addComponent`、`addMiddleware` 暂时作为作者兼容接口保留,后续
|
|
13
|
+
只向 RuntimeSnapshot 投影,不再维护第二套运行时权威。
|
|
14
|
+
|
|
3
15
|
Zhin.js **IM/多通道运行时**包:Plugin、Adapter、**Endpoint**、MessageDispatcher 与统一出站链。**AI 编排(ZhinAgent、工具安全、MCP)在 [`@zhin.js/agent`](../agent/README.md)**;本包仅 selective re-export `@zhin.js/ai` 的 Provider / Agent 原语供插件直接使用。
|
|
4
16
|
|
|
5
17
|
领域词汇见 [CONTEXT.md](./CONTEXT.md);入站/出站流程见 [消息如何流转](../../docs/essentials/message-flow.md)。
|
package/lib/adapter.js
CHANGED
|
@@ -194,7 +194,7 @@ export class Adapter extends EventEmitter {
|
|
|
194
194
|
if (!endpoint)
|
|
195
195
|
throw new Error(`Endpoint ${options.endpoint} not found`);
|
|
196
196
|
assertOutbound(endpoint);
|
|
197
|
-
this.logger.
|
|
197
|
+
this.logger.debug(formatCompact({
|
|
198
198
|
send: `${options.type}(${options.id})`,
|
|
199
199
|
endpoint: options.endpoint,
|
|
200
200
|
preview: truncatePreview(segment.raw(options.content)),
|
|
@@ -231,7 +231,7 @@ export class Adapter extends EventEmitter {
|
|
|
231
231
|
content: options.content,
|
|
232
232
|
});
|
|
233
233
|
await editable.$editMessage({ ...options, content: rendered.content });
|
|
234
|
-
this.logger.
|
|
234
|
+
this.logger.debug(formatCompact({
|
|
235
235
|
edit: `${options.type}(${options.id})`,
|
|
236
236
|
endpoint: options.endpoint,
|
|
237
237
|
messageId: options.messageId,
|
|
@@ -21,19 +21,17 @@ export function parseOutboundSegment(raw) {
|
|
|
21
21
|
/** 去掉嵌入 JSON 候选末尾的 markdown 围栏(模型常见 ```json … ``` 误输出)。 */
|
|
22
22
|
export function trimTrailingMarkdownFence(raw) {
|
|
23
23
|
let s = raw.trim();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
s = trailingFence[1].trim();
|
|
24
|
+
if (s.endsWith('```'))
|
|
25
|
+
s = s.slice(0, -3).trimEnd();
|
|
27
26
|
return s;
|
|
28
27
|
}
|
|
29
28
|
export function unwrapAiOutboundJsonCandidate(raw) {
|
|
30
29
|
const trimmed = raw.trim();
|
|
31
|
-
const fenced = trimmed
|
|
32
|
-
if (fenced)
|
|
33
|
-
return fenced
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return trimTrailingMarkdownFence(embeddedFence[1].trim());
|
|
30
|
+
const fenced = readMarkdownFence(trimmed);
|
|
31
|
+
if (fenced && fenced.full)
|
|
32
|
+
return fenced.body.trim();
|
|
33
|
+
if (fenced && trimmed.indexOf('{') >= 0) {
|
|
34
|
+
return trimTrailingMarkdownFence(fenced.body.trim());
|
|
37
35
|
}
|
|
38
36
|
return trimTrailingMarkdownFence(trimmed);
|
|
39
37
|
}
|
|
@@ -42,12 +40,12 @@ export function extractEmbeddedAiOutboundJson(plain) {
|
|
|
42
40
|
const trimmed = plain.trim();
|
|
43
41
|
if (!trimmed.includes('{'))
|
|
44
42
|
return null;
|
|
45
|
-
const fenced = trimmed
|
|
43
|
+
const fenced = readMarkdownFence(trimmed);
|
|
46
44
|
if (fenced) {
|
|
47
|
-
const jsonRaw = trimTrailingMarkdownFence(fenced
|
|
45
|
+
const jsonRaw = trimTrailingMarkdownFence(fenced.body.trim());
|
|
48
46
|
const payload = parseAiOutboundJson(jsonRaw);
|
|
49
47
|
if (payload?.mentions?.length && payload.text?.trim()) {
|
|
50
|
-
const prose = trimmed.slice(0, fenced.
|
|
48
|
+
const prose = trimmed.slice(0, fenced.start).trim();
|
|
51
49
|
return { prose, jsonRaw };
|
|
52
50
|
}
|
|
53
51
|
}
|
|
@@ -62,6 +60,22 @@ export function extractEmbeddedAiOutboundJson(plain) {
|
|
|
62
60
|
}
|
|
63
61
|
return null;
|
|
64
62
|
}
|
|
63
|
+
function readMarkdownFence(text) {
|
|
64
|
+
const start = text.indexOf('```');
|
|
65
|
+
if (start < 0)
|
|
66
|
+
return null;
|
|
67
|
+
let bodyStart = start + 3;
|
|
68
|
+
const langEnd = text.indexOf('\n', bodyStart);
|
|
69
|
+
if (langEnd >= 0) {
|
|
70
|
+
const lang = text.slice(bodyStart, langEnd).trim().toLowerCase();
|
|
71
|
+
if (lang === '' || lang === 'json')
|
|
72
|
+
bodyStart = langEnd + 1;
|
|
73
|
+
}
|
|
74
|
+
const end = text.indexOf('```', bodyStart);
|
|
75
|
+
if (end < 0)
|
|
76
|
+
return null;
|
|
77
|
+
return { start, body: text.slice(bodyStart, end), full: start === 0 && text.slice(end + 3).trim() === '' };
|
|
78
|
+
}
|
|
65
79
|
export function parseAiOutboundJson(raw) {
|
|
66
80
|
const candidate = unwrapAiOutboundJsonCandidate(raw);
|
|
67
81
|
if (!candidate.startsWith('{'))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mergeAITriggerConfig, resolveSenderRoles, } from './ai-trigger.js';
|
|
2
|
-
import { formatCompact,
|
|
3
|
-
const logger =
|
|
2
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
3
|
+
const logger = getLogger('Authorization');
|
|
4
4
|
function findEndpointEntryFromConfig(config, adapter, endpointId) {
|
|
5
5
|
const endpoints = config.endpoints;
|
|
6
6
|
if (!Array.isArray(endpoints))
|
package/lib/built/component.d.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* ComponentFeature
|
|
3
3
|
* 管理所有插件注册的组件,继承自 Feature 抽象类
|
|
4
4
|
*/
|
|
5
|
-
import { Feature, FeatureJSON } from
|
|
5
|
+
import { Feature, FeatureJSON, type PluginLike } from '@zhin.js/kernel';
|
|
6
6
|
import { Component } from "../component.js";
|
|
7
|
-
import type { PluginLike } from '@zhin.js/kernel';
|
|
8
7
|
/**
|
|
9
8
|
* ComponentContext 扩展方法类型
|
|
10
9
|
*/
|
package/lib/built/component.js
CHANGED
package/lib/built/database.d.ts
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Definition, Database } from "@zhin.js/database";
|
|
6
6
|
import { DatabaseConfig, Models } from "../types.js";
|
|
7
|
-
import { Feature, FeatureJSON } from
|
|
8
|
-
import type { PluginLike } from '@zhin.js/kernel';
|
|
7
|
+
import { Feature, FeatureJSON, type PluginLike } from '@zhin.js/kernel';
|
|
9
8
|
/**
|
|
10
9
|
* 模型定义记录
|
|
11
10
|
*/
|
package/lib/built/database.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Registry } from "@zhin.js/database";
|
|
6
6
|
import { formatCompact } from '@zhin.js/logger';
|
|
7
|
-
import { Feature } from
|
|
7
|
+
import { Feature } from '@zhin.js/kernel';
|
|
8
8
|
import { SystemLogDefinition } from "../models/system-log.js";
|
|
9
9
|
import { UserDefinition } from "../models/user.js";
|
|
10
10
|
const databaseAfterStartHooks = [];
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* 默认路由为 exclusive(命令与 AI 互斥);需双轨时请显式 dualRoute.mode: 'dual'。
|
|
28
28
|
*/
|
|
29
29
|
import { Message } from '../message.js';
|
|
30
|
+
import { type Context } from '../plugin.js';
|
|
30
31
|
import type { MessageMiddleware, RegisteredAdapter, MaybePromise, SendContent, OutboundReplySource, OutboundPolishMiddleware, OutboundReplyStore } from '../types.js';
|
|
31
|
-
import type { Context } from '../plugin.js';
|
|
32
32
|
export declare function getOutboundReplyStore(): OutboundReplyStore | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* 路由判定结果(互斥模式 legacy)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatDisplayPath } from '@zhin.js/logger';
|
|
1
2
|
import { Adapter } from '../adapter.js';
|
|
2
3
|
import { segment } from '../utils.js';
|
|
3
4
|
import { connectEndpointInstance, disconnectEndpointInstance } from './connect-endpoint-instance.js';
|
|
@@ -81,7 +82,7 @@ export class EndpointLifecycleService {
|
|
|
81
82
|
const loader = configService.configs.get(configService.primaryFile);
|
|
82
83
|
const endpoints = readAllEndpoints(this.root);
|
|
83
84
|
await this.persistEndpoints(endpoints);
|
|
84
|
-
const configPath = loader.resolvedPath;
|
|
85
|
+
const configPath = formatDisplayPath(loader.resolvedPath);
|
|
85
86
|
const names = endpoints.map((e) => `${e.context}/${e.name}`).join(', ') || '(无)';
|
|
86
87
|
return {
|
|
87
88
|
message: `✅ 已将 ${endpoints.length} 个 endpoint 写入 \`${configPath}\`:${names}`,
|
|
@@ -26,9 +26,10 @@ export async function emitEndpointLifecycle(plugin, adapter, endpoint, kind, det
|
|
|
26
26
|
$id: `endpoint-lifecycle:${endpoint.$id}:${kind}:${Date.now()}`,
|
|
27
27
|
$adapter: adapter.name,
|
|
28
28
|
$endpoint: endpoint.$id,
|
|
29
|
-
$type: '
|
|
30
|
-
$
|
|
31
|
-
$
|
|
29
|
+
$type: 'notice',
|
|
30
|
+
$scene_id: endpoint.$id,
|
|
31
|
+
$scene_type: 'endpoint',
|
|
32
|
+
$sub_type: kind,
|
|
32
33
|
$timestamp: Date.now(),
|
|
33
34
|
});
|
|
34
35
|
adapter.emit('notice.receive', notice);
|
|
@@ -79,20 +79,21 @@ function stripRemainingTags(html) {
|
|
|
79
79
|
return out;
|
|
80
80
|
}
|
|
81
81
|
function decodeCommonEntities(text) {
|
|
82
|
-
return text
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
return text.replace(/&(#x[0-9a-fA-F]+|#\d+|nbsp|amp|lt|gt|quot|apos);/gi, (entity) => {
|
|
83
|
+
const key = entity.slice(1, -1).toLowerCase();
|
|
84
|
+
if (key === 'nbsp')
|
|
85
|
+
return ' ';
|
|
86
|
+
if (key === 'amp')
|
|
87
|
+
return '&';
|
|
88
|
+
if (key === 'lt')
|
|
89
|
+
return '<';
|
|
90
|
+
if (key === 'gt')
|
|
91
|
+
return '>';
|
|
92
|
+
if (key === 'quot')
|
|
93
|
+
return '"';
|
|
94
|
+
if (key === 'apos')
|
|
95
|
+
return "'";
|
|
96
|
+
const n = key.startsWith('#x') ? parseInt(key.slice(2), 16) : Number(key.slice(1));
|
|
96
97
|
return Number.isFinite(n) && n >= 0 && n <= 0x10ffff ? String.fromCodePoint(n) : '';
|
|
97
98
|
});
|
|
98
99
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Extracted from Adapter.emit() override to improve testability and decoupling.
|
|
5
5
|
* Handles: event routing, logging, backpressure, pipeline execution, observer emission.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type Logger } from '@zhin.js/logger';
|
|
8
8
|
import { type Message as MessageType } from '../message.js';
|
|
9
9
|
import type { Plugin } from '../plugin.js';
|
|
10
10
|
export interface InboundPipelineOptions {
|
|
@@ -54,7 +54,7 @@ export class InboundMessagePipeline {
|
|
|
54
54
|
return result;
|
|
55
55
|
}
|
|
56
56
|
logIncoming(message) {
|
|
57
|
-
this.options.logger.
|
|
57
|
+
this.options.logger.debug(formatCompact({
|
|
58
58
|
recv: `${message.$channel.type}(${message.$channel.id})`,
|
|
59
59
|
endpoint: message.$endpoint,
|
|
60
60
|
preview: truncatePreview(segment.raw(message.$content)),
|
|
@@ -19,11 +19,26 @@ const PAYLOAD_PATTERN = /^[a-z0-9_]+:[^:\s]+:[a-z0-9_-]+$/i;
|
|
|
19
19
|
/** 去掉 @bot、XML at 段、首尾空白(QQ 指令预填文本归一化) */
|
|
20
20
|
export function stripInteractiveCommandText(raw) {
|
|
21
21
|
let text = raw.trim();
|
|
22
|
-
text = text
|
|
22
|
+
text = stripXmlAtSegments(text);
|
|
23
23
|
text = text.replace(/^@\S+\s+/u, '');
|
|
24
24
|
text = text.replace(/\s+/g, ' ').trim();
|
|
25
25
|
return text;
|
|
26
26
|
}
|
|
27
|
+
function stripXmlAtSegments(text) {
|
|
28
|
+
let out = '';
|
|
29
|
+
let cursor = 0;
|
|
30
|
+
while (cursor < text.length) {
|
|
31
|
+
const start = text.toLowerCase().indexOf('<at ', cursor);
|
|
32
|
+
if (start < 0)
|
|
33
|
+
break;
|
|
34
|
+
const end = text.indexOf('/>', start + 4);
|
|
35
|
+
if (end < 0)
|
|
36
|
+
break;
|
|
37
|
+
out += text.slice(cursor, start) + ' ';
|
|
38
|
+
cursor = end + 2;
|
|
39
|
+
}
|
|
40
|
+
return out + text.slice(cursor);
|
|
41
|
+
}
|
|
27
42
|
/** 从文本降级输入解析 payload(需在 active session 的 fallback.map 中查找) */
|
|
28
43
|
export function resolveTextFallbackPayload(raw, map) {
|
|
29
44
|
const trimmed = raw.trim();
|
|
@@ -5,7 +5,7 @@ import type { Plugin } from '../plugin.js';
|
|
|
5
5
|
import { Message, type MessageChannel } from '../message.js';
|
|
6
6
|
import type { Adapters } from '../adapter.js';
|
|
7
7
|
import type { SendContent } from '../types.js';
|
|
8
|
-
import type
|
|
8
|
+
import { type SenderRole } from './roles.js';
|
|
9
9
|
/** Agent turn 可挂载在 Message 扩展字段上的元数据 */
|
|
10
10
|
export type AgentTurnMessage = Message<{
|
|
11
11
|
extra?: Record<string, unknown>;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* 内置 permit 同步校验(adapter/group/private/channel/user/role)
|
|
3
3
|
*/
|
|
4
4
|
import type { Message } from '../message.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type SenderRole } from './roles.js';
|
|
6
6
|
export declare function checkBuiltinPermit(name: string, message: Message<any>, roles: readonly SenderRole[]): boolean;
|
|
7
7
|
export declare function checkBuiltinPermitList(permits: readonly string[], message: Message<any>, roles: readonly SenderRole[]): boolean;
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { parsePlatformPermitName, isPlatformPermit } from './permit-parse.js';
|
|
2
2
|
const checkers = new Map();
|
|
3
|
-
const
|
|
3
|
+
const defaultSceneRegistrations = new Map();
|
|
4
4
|
export function registerPlatformPermitChecker(adapter, checker) {
|
|
5
5
|
const key = String(adapter);
|
|
6
|
-
checkers.
|
|
6
|
+
const registrations = checkers.get(key) ?? [];
|
|
7
|
+
registrations.push(checker);
|
|
8
|
+
checkers.set(key, registrations);
|
|
7
9
|
return () => {
|
|
8
|
-
|
|
10
|
+
const current = checkers.get(key);
|
|
11
|
+
if (!current)
|
|
12
|
+
return;
|
|
13
|
+
const index = current.lastIndexOf(checker);
|
|
14
|
+
if (index >= 0)
|
|
15
|
+
current.splice(index, 1);
|
|
16
|
+
if (current.length === 0)
|
|
9
17
|
checkers.delete(key);
|
|
10
|
-
}
|
|
11
18
|
};
|
|
12
19
|
}
|
|
13
20
|
export function clearPlatformPermitCheckers() {
|
|
14
21
|
checkers.clear();
|
|
15
|
-
|
|
22
|
+
defaultSceneRegistrations.clear();
|
|
16
23
|
}
|
|
17
24
|
export function checkPlatformPermit(name, message) {
|
|
18
25
|
const parsed = parsePlatformPermitName(name);
|
|
@@ -20,7 +27,8 @@ export function checkPlatformPermit(name, message) {
|
|
|
20
27
|
return false;
|
|
21
28
|
if (String(message.$adapter) !== parsed.adapter)
|
|
22
29
|
return false;
|
|
23
|
-
const
|
|
30
|
+
const registrations = checkers.get(parsed.adapter);
|
|
31
|
+
const checker = registrations?.[registrations.length - 1];
|
|
24
32
|
if (!checker)
|
|
25
33
|
return false;
|
|
26
34
|
return checker(parsed.perm, message);
|
|
@@ -58,9 +66,26 @@ export function createSceneRolePlatformChecker() {
|
|
|
58
66
|
/** 为适配器注册默认场景治理 platform checker(幂等) */
|
|
59
67
|
export function registerDefaultScenePlatformPermitChecker(adapter) {
|
|
60
68
|
const key = String(adapter);
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
const existing = defaultSceneRegistrations.get(key);
|
|
70
|
+
if (existing) {
|
|
71
|
+
existing.references += 1;
|
|
72
|
+
return () => releaseDefaultSceneChecker(key, existing);
|
|
63
73
|
}
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
const checker = createSceneRolePlatformChecker();
|
|
75
|
+
const registration = {
|
|
76
|
+
checker,
|
|
77
|
+
dispose: registerPlatformPermitChecker(key, checker),
|
|
78
|
+
references: 1,
|
|
79
|
+
};
|
|
80
|
+
defaultSceneRegistrations.set(key, registration);
|
|
81
|
+
return () => releaseDefaultSceneChecker(key, registration);
|
|
82
|
+
}
|
|
83
|
+
function releaseDefaultSceneChecker(key, registration) {
|
|
84
|
+
if (defaultSceneRegistrations.get(key) !== registration)
|
|
85
|
+
return;
|
|
86
|
+
registration.references -= 1;
|
|
87
|
+
if (registration.references > 0)
|
|
88
|
+
return;
|
|
89
|
+
defaultSceneRegistrations.delete(key);
|
|
90
|
+
registration.dispose();
|
|
66
91
|
}
|
package/lib/built/schedule.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ScheduleFeature — 管理插件注册的内存定时任务
|
|
3
3
|
*/
|
|
4
|
-
import { Feature, FeatureJSON, type ScheduleKind } from '@zhin.js/kernel';
|
|
5
|
-
import type { HolidayInput, ScatterInput } from '@zhin.js/kernel';
|
|
4
|
+
import { Feature, FeatureJSON, type ScheduleKind, type HolidayInput, type ScatterInput } from '@zhin.js/kernel';
|
|
6
5
|
export interface ScheduleDescriptor {
|
|
7
6
|
kind: ScheduleKind | 'every' | 'at';
|
|
8
7
|
/** 6 段 cron(solar/lunar/workday/freeDay/holiday) */
|
package/lib/built/schedule.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ScheduleFeature — 管理插件注册的内存定时任务
|
|
3
3
|
*/
|
|
4
|
-
import { Feature, ScheduleEngine, getScheduleEngine, setScheduleEngine
|
|
4
|
+
import { Feature, ScheduleEngine, getScheduleEngine, setScheduleEngine } from '@zhin.js/kernel';
|
|
5
5
|
function ensureEngine() {
|
|
6
6
|
let engine = getScheduleEngine();
|
|
7
7
|
if (!engine) {
|
package/lib/component.js
CHANGED
|
@@ -176,24 +176,41 @@ function parseAttributeValue(str, startIndex, context) {
|
|
|
176
176
|
function evaluateQuotedExpression(quotedValue, context) {
|
|
177
177
|
if (!context)
|
|
178
178
|
return quotedValue;
|
|
179
|
-
|
|
180
|
-
let
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
while ((match = expressionRegex.exec(quotedValue)) !== null) {
|
|
184
|
-
const expr = match[1];
|
|
179
|
+
let result = '';
|
|
180
|
+
let cursor = 0;
|
|
181
|
+
for (const match of readBraceExpressions(quotedValue)) {
|
|
182
|
+
result += quotedValue.slice(cursor, match.start);
|
|
185
183
|
try {
|
|
186
|
-
const value = context.getValue(expr);
|
|
187
|
-
if (value !== undefined && value !== expr) {
|
|
184
|
+
const value = context.getValue(match.expr);
|
|
185
|
+
if (value !== undefined && value !== match.expr) {
|
|
188
186
|
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
189
|
-
result
|
|
187
|
+
result += stringValue;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
result += quotedValue.slice(match.start, match.end);
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
catch (error) {
|
|
193
|
-
|
|
194
|
+
result += quotedValue.slice(match.start, match.end);
|
|
194
195
|
}
|
|
196
|
+
cursor = match.end;
|
|
195
197
|
}
|
|
196
|
-
return result;
|
|
198
|
+
return result + quotedValue.slice(cursor);
|
|
199
|
+
}
|
|
200
|
+
function readBraceExpressions(value) {
|
|
201
|
+
const matches = [];
|
|
202
|
+
let cursor = 0;
|
|
203
|
+
while (cursor < value.length) {
|
|
204
|
+
const start = value.indexOf('{', cursor);
|
|
205
|
+
if (start < 0)
|
|
206
|
+
break;
|
|
207
|
+
const end = value.indexOf('}', start + 1);
|
|
208
|
+
if (end < 0)
|
|
209
|
+
break;
|
|
210
|
+
matches.push({ start, end: end + 1, expr: value.slice(start + 1, end) });
|
|
211
|
+
cursor = end + 1;
|
|
212
|
+
}
|
|
213
|
+
return matches;
|
|
197
214
|
}
|
|
198
215
|
/**
|
|
199
216
|
* 解析表达式值
|
package/lib/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './message.js';
|
|
|
8
8
|
export * from './im-scene.js';
|
|
9
9
|
export * from './notice.js';
|
|
10
10
|
export * from './request.js';
|
|
11
|
+
export * from './side-event/index.js';
|
|
11
12
|
export * from './prompt.js';
|
|
12
13
|
export * from './types.js';
|
|
13
14
|
export * from './agent-prompt.js';
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './message.js';
|
|
|
9
9
|
export * from './im-scene.js';
|
|
10
10
|
export * from './notice.js';
|
|
11
11
|
export * from './request.js';
|
|
12
|
+
export * from './side-event/index.js';
|
|
12
13
|
export * from './prompt.js';
|
|
13
14
|
export * from './types.js';
|
|
14
15
|
export * from './agent-prompt.js';
|
package/lib/notice.d.ts
CHANGED
|
@@ -1,81 +1,38 @@
|
|
|
1
|
-
import { Adapters } from './adapter.js';
|
|
2
|
-
import type {
|
|
1
|
+
import type { Adapters } from './adapter.js';
|
|
2
|
+
import type { NoticeKind } from './side-event/types.js';
|
|
3
|
+
import type { SideEventBase } from './side-event/base.js';
|
|
4
|
+
export type { NoticeKind, ComposedNoticeName, NoticeType } from './side-event/types.js';
|
|
5
|
+
export type { SideEventBase } from './side-event/base.js';
|
|
6
|
+
export { composeSideEventName, formatSideEventName, matchesSideEventName, parseSideEventName, sideEventSendChannel, } from './side-event/base.js';
|
|
3
7
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 常见 IM 通知事件分类:
|
|
7
|
-
* - group_member_increase: 群成员增加(入群)
|
|
8
|
-
* - group_member_decrease: 群成员减少(退群/被踢)
|
|
9
|
-
* - group_admin_change: 群管理员变动
|
|
10
|
-
* - group_ban: 群禁言
|
|
11
|
-
* - group_recall: 群消息撤回
|
|
12
|
-
* - friend_recall: 好友消息撤回
|
|
13
|
-
* - friend_add: 新增好友
|
|
14
|
-
* - group_poke: 群戳一戳
|
|
15
|
-
* - friend_poke: 好友戳一戳
|
|
16
|
-
* - group_transfer: 群转让
|
|
17
|
-
* - endpoint.lifecycle: Endpoint 连接/断开/错误($subType: connect | disconnect | error)
|
|
18
|
-
*
|
|
19
|
-
* 适配器可自行扩展更多子类型
|
|
20
|
-
*/
|
|
21
|
-
export type NoticeType = 'group_member_increase' | 'group_member_decrease' | 'group_admin_change' | 'group_ban' | 'group_recall' | 'friend_recall' | 'friend_add' | 'group_poke' | 'friend_poke' | 'group_transfer' | (string & {});
|
|
22
|
-
/**
|
|
23
|
-
* 通知频道信息
|
|
24
|
-
*/
|
|
25
|
-
export interface NoticeChannel {
|
|
26
|
-
id: string;
|
|
27
|
-
type: 'group' | 'private' | 'channel';
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* 通知基础结构
|
|
31
|
-
*
|
|
32
|
-
* 与 MessageBase 同构设计,所有通知共享以下字段。
|
|
33
|
-
* 适配器通过 `Notice.from(raw, base)` 合并平台原始数据和标准字段。
|
|
8
|
+
* 通知基础结构 — 继承 SideEventBase。
|
|
34
9
|
*
|
|
35
10
|
* @example
|
|
36
11
|
* ```typescript
|
|
37
|
-
* // 适配器中格式化通知
|
|
38
12
|
* const notice = Notice.from(rawEvent, {
|
|
39
13
|
* $id: rawEvent.id,
|
|
40
14
|
* $adapter: 'icqq',
|
|
41
15
|
* $endpoint: endpointId,
|
|
42
|
-
* $type: '
|
|
43
|
-
* $
|
|
44
|
-
* $
|
|
45
|
-
* $
|
|
16
|
+
* $type: 'notice',
|
|
17
|
+
* $scene_id: groupId,
|
|
18
|
+
* $scene_type: 'group',
|
|
19
|
+
* $sub_type: 'member_decrease',
|
|
20
|
+
* $actor: { id: operatorId, name: '管理员' },
|
|
46
21
|
* $target: { id: userId, name: '被踢者' },
|
|
47
22
|
* $timestamp: Date.now(),
|
|
48
23
|
* });
|
|
24
|
+
* // formatSideEventName(notice) === 'notice.group.member_decrease'
|
|
49
25
|
* this.adapter.emit('notice.receive', notice);
|
|
50
26
|
* ```
|
|
51
27
|
*/
|
|
52
|
-
export interface NoticeBase {
|
|
53
|
-
/** 通知唯一 ID(平台提供或自生成) */
|
|
54
|
-
$id: string;
|
|
55
|
-
/** 适配器名称 */
|
|
28
|
+
export interface NoticeBase extends SideEventBase {
|
|
56
29
|
$adapter: keyof Adapters;
|
|
57
|
-
|
|
58
|
-
$endpoint: string;
|
|
59
|
-
/** 通知类型 */
|
|
60
|
-
$type: NoticeType;
|
|
61
|
-
/** 通知子类型(如 leave/kick、set/unset) */
|
|
62
|
-
$subType?: string;
|
|
63
|
-
/** 通知发生的频道/群/会话 */
|
|
64
|
-
$channel: NoticeChannel;
|
|
65
|
-
/** 操作者信息(如管理员) */
|
|
66
|
-
$operator?: MessageSender;
|
|
67
|
-
/** 被操作目标信息(如被踢的成员) */
|
|
68
|
-
$target?: MessageSender;
|
|
69
|
-
/** 通知时间戳 */
|
|
70
|
-
$timestamp: number;
|
|
30
|
+
$type: NoticeKind;
|
|
71
31
|
}
|
|
72
32
|
/**
|
|
73
33
|
* 完整通知类型,支持平台原始数据扩展
|
|
74
34
|
*/
|
|
75
35
|
export type Notice<T extends object = {}> = NoticeBase & T;
|
|
76
36
|
export declare namespace Notice {
|
|
77
|
-
/**
|
|
78
|
-
* 工具方法:合并自定义字段与基础通知结构
|
|
79
|
-
*/
|
|
80
37
|
function from<T extends object>(input: T, format: NoticeBase): Notice<T>;
|
|
81
38
|
}
|
package/lib/notice.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
export { composeSideEventName, formatSideEventName, matchesSideEventName, parseSideEventName, sideEventSendChannel, } from './side-event/base.js';
|
|
1
2
|
export var Notice;
|
|
2
3
|
(function (Notice) {
|
|
3
|
-
/**
|
|
4
|
-
* 工具方法:合并自定义字段与基础通知结构
|
|
5
|
-
*/
|
|
6
4
|
function from(input, format) {
|
|
7
5
|
return Object.assign(input, format);
|
|
8
6
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { CapabilityId, PluginId } from '@zhin.js/plugin-runtime';
|
|
2
|
+
declare const componentCallBrand: "zhin.component-call/1";
|
|
3
|
+
declare const rawContentBrand: "zhin.raw-content/1";
|
|
4
|
+
export interface ComponentCall<TProps = unknown> {
|
|
5
|
+
readonly $content: typeof componentCallBrand;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly props: TProps;
|
|
8
|
+
}
|
|
9
|
+
export interface RawContent<TPayload = unknown> {
|
|
10
|
+
readonly $content: typeof rawContentBrand;
|
|
11
|
+
readonly payload: TPayload;
|
|
12
|
+
}
|
|
13
|
+
export type SendContent = string | ComponentCall | RawContent | readonly SendContent[];
|
|
14
|
+
export declare function component<TProps>(name: string, props: TProps): ComponentCall<TProps>;
|
|
15
|
+
export declare function raw<TPayload>(payload: TPayload): RawContent<TPayload>;
|
|
16
|
+
export declare function isComponentCall(value: SendContent): value is ComponentCall;
|
|
17
|
+
export declare function isRawContent(value: SendContent): value is RawContent;
|
|
18
|
+
export interface IncomingMessage {
|
|
19
|
+
readonly adapter: CapabilityId;
|
|
20
|
+
readonly target: string;
|
|
21
|
+
readonly content: string;
|
|
22
|
+
readonly id?: string;
|
|
23
|
+
readonly sender?: string;
|
|
24
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
25
|
+
}
|
|
26
|
+
export interface SendRequest {
|
|
27
|
+
readonly adapter: CapabilityId;
|
|
28
|
+
readonly target: string;
|
|
29
|
+
readonly requester: PluginId;
|
|
30
|
+
readonly content: SendContent;
|
|
31
|
+
readonly parent?: ChannelParent;
|
|
32
|
+
}
|
|
33
|
+
/** Console 通道的来源场景(群临时会话 parent.group / QQ 子频道 parent.guild)。 */
|
|
34
|
+
export interface ChannelParent {
|
|
35
|
+
readonly type?: string;
|
|
36
|
+
readonly id?: string;
|
|
37
|
+
readonly name?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface OutboundEnvelope {
|
|
40
|
+
readonly adapter: CapabilityId;
|
|
41
|
+
readonly target: string;
|
|
42
|
+
readonly requester: PluginId;
|
|
43
|
+
readonly generation: number;
|
|
44
|
+
readonly payload: unknown;
|
|
45
|
+
readonly parent?: ChannelParent;
|
|
46
|
+
replace(payload: unknown): void;
|
|
47
|
+
}
|
|
48
|
+
export interface MessageGateway {
|
|
49
|
+
receive(input: IncomingMessage): Promise<MessageDispatchResult>;
|
|
50
|
+
send(request: SendRequest): Promise<unknown>;
|
|
51
|
+
}
|
|
52
|
+
export interface MessageDispatchResult {
|
|
53
|
+
readonly matched: boolean;
|
|
54
|
+
readonly command?: string;
|
|
55
|
+
readonly owner?: PluginId;
|
|
56
|
+
readonly value?: unknown;
|
|
57
|
+
}
|
|
58
|
+
export declare class Message {
|
|
59
|
+
readonly adapter: CapabilityId;
|
|
60
|
+
readonly target: string;
|
|
61
|
+
readonly content: string;
|
|
62
|
+
readonly generation: number;
|
|
63
|
+
readonly id?: string | undefined;
|
|
64
|
+
readonly sender?: string | undefined;
|
|
65
|
+
readonly metadata: Readonly<Record<string, unknown>>;
|
|
66
|
+
constructor(adapter: CapabilityId, target: string, content: string, generation: number, reply: (content: SendContent, requester?: PluginId) => Promise<unknown>, id?: string | undefined, sender?: string | undefined, metadata?: Readonly<Record<string, unknown>>);
|
|
67
|
+
readonly $reply: (content: SendContent) => Promise<unknown>;
|
|
68
|
+
readonly $replyFrom: (requester: PluginId, content: SendContent) => Promise<unknown>;
|
|
69
|
+
}
|
|
70
|
+
export declare function createOutboundEnvelope(request: Omit<OutboundEnvelope, 'payload' | 'replace'>, initialPayload: unknown): OutboundEnvelope;
|
|
71
|
+
export {};
|