@router-agent/channel 0.3.31 → 0.3.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@router-agent/channel",
3
- "version": "0.3.31",
3
+ "version": "0.3.33",
4
4
  "type": "module",
5
5
  "description": "消息通道 SDK:端口、注册表、插件协议与入站管线",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  "registry": "https://registry.npmjs.org/"
32
32
  },
33
33
  "dependencies": {
34
- "@router-agent/core": "0.3.31",
35
- "@router-agent/infra": "0.3.31"
34
+ "@router-agent/core": "0.3.33",
35
+ "@router-agent/infra": "0.3.33"
36
36
  }
37
37
  }
@@ -1,146 +0,0 @@
1
- import { noopLogger } from '@router-agent/infra/logger.js';
2
- import { FeishuBotAdmin } from '../sources/feishu-bot-admin.js';
3
- import { attachImageText } from '../sources/feishu-image.js';
4
- import { FeishuConsumer, FeishuSender, attachQuotedMessage, normalizeFeishuEventDetailed, shouldSendAsText, } from '../sources/feishu.js';
5
- import { createLarkRunner, larkEnv } from '../sources/lark.js';
6
- import { DEFAULT_CHANNEL_ID } from '../port.js';
7
- /**
8
- * 飞书通道适配器。
9
- *
10
- * 把原先散在 CLI 与 Service 里的飞书细节收口到这里:事件消费、归一化、引用/图片富化、
11
- * 文本/卡片渲染、回执、云文档、凭据与授权。内核只看到 `ChannelAdapter`。
12
- */
13
- export class FeishuChannel {
14
- id = DEFAULT_CHANNEL_ID;
15
- kind = 'feishu';
16
- capabilities;
17
- admin;
18
- config;
19
- runner;
20
- reaction;
21
- directory;
22
- transcriber;
23
- logger;
24
- sender;
25
- opts;
26
- consumer = null;
27
- constructor(opts) {
28
- this.opts = opts;
29
- this.config = opts.config;
30
- this.runner = opts.runner;
31
- this.reaction = opts.reaction ?? '';
32
- this.directory = opts.directory;
33
- this.transcriber = opts.transcriber;
34
- this.logger = opts.logger ?? noopLogger;
35
- this.sender = opts.sender ?? new FeishuSender(opts.runner, this.logger);
36
- this.admin = opts.admin ?? new FeishuBotAdmin({ lark: opts.runner });
37
- this.capabilities = {
38
- threadReply: true,
39
- ack: Boolean(opts.reaction),
40
- richText: true,
41
- documents: true,
42
- directory: true,
43
- attachmentsIn: true,
44
- attachmentsOut: false,
45
- idempotency: true,
46
- maxTextLength: 3500,
47
- directMessage: opts.config.directMessages,
48
- };
49
- }
50
- normalize(raw) {
51
- const event = (raw && typeof raw === 'object' ? raw : {});
52
- const chatId = String(event.chat_id ?? '');
53
- const targetName = this.directory?.nameOf(chatId) ?? '';
54
- const result = normalizeFeishuEventDetailed(event, this.config, targetName, this.opts.allowUnaddressed);
55
- if (!result.trigger) {
56
- return {
57
- dropped: result.dropped,
58
- ...(result.chatId ? { targetId: result.chatId } : {}),
59
- ...(result.chatId ? { targetName } : {}),
60
- ...(result.actorId ? { actorId: result.actorId } : {}),
61
- };
62
- }
63
- return {
64
- trigger: { ...result.trigger, channelId: this.id },
65
- targetId: result.chatId,
66
- targetName,
67
- ...(result.actorId ? { actorId: result.actorId } : {}),
68
- };
69
- }
70
- /** 图片消息要先下载识别,耗时较久;CLI 据此决定是否提前加回执。 */
71
- needsEnrichment(trigger) {
72
- const raw = trigger.raw;
73
- return Boolean(trigger.mediaOnly) || Array.isArray(raw?.imageKeys);
74
- }
75
- async enrich(trigger) {
76
- const quoted = await attachQuotedMessage(trigger, this.runner, { logger: this.logger });
77
- return attachImageText(quoted, {
78
- runner: this.runner,
79
- transcriber: this.transcriber,
80
- ...(this.opts.imagesRoot ? { imagesRoot: this.opts.imagesRoot } : {}),
81
- logger: this.logger,
82
- });
83
- }
84
- async send(message) {
85
- const text = message.text.trim();
86
- if (!text)
87
- return {};
88
- const key = message.idempotencyKey;
89
- if (shouldSendAsText(text)) {
90
- if (message.threadRef)
91
- await this.sender.reply(message.threadRef, text, key);
92
- else
93
- await this.sender.send(message.targetId, text, key);
94
- return { degradedTo: 'plain' };
95
- }
96
- // Markdown 走互动卡片;卡片失败时 FeishuSender 内部降级为 post 富文本。
97
- if (message.threadRef)
98
- await this.sender.replyCard(message.threadRef, text, key);
99
- else
100
- await this.sender.sendCard(message.targetId, text, key);
101
- return { degradedTo: 'rich' };
102
- }
103
- async ack(messageId) {
104
- if (!this.reaction)
105
- return null;
106
- return this.sender.react(messageId, this.reaction);
107
- }
108
- async clearAck(messageId, handle) {
109
- await this.sender.unreact(messageId, handle);
110
- }
111
- async createDocument(title, markdown) {
112
- return this.sender.createDocument(title, markdown);
113
- }
114
- async start(sink) {
115
- if (this.consumer)
116
- return;
117
- this.consumer = new FeishuConsumer({
118
- ...(this.opts.bin ? { bin: this.opts.bin } : {}),
119
- env: this.opts.env ?? larkEnv(),
120
- onEvent: sink,
121
- ...(this.opts.hooks?.onReady ? { onReady: this.opts.hooks.onReady } : {}),
122
- ...(this.opts.hooks?.onReconnect ? { onReconnect: this.opts.hooks.onReconnect } : {}),
123
- ...(this.opts.hooks?.onError ? { onError: this.opts.hooks.onError } : {}),
124
- ...(this.opts.hooks?.onLine ? { onLine: this.opts.hooks.onLine } : {}),
125
- });
126
- this.consumer.start();
127
- }
128
- stop() {
129
- this.consumer?.stop();
130
- this.consumer = null;
131
- }
132
- }
133
- /** 生产装配:用固定 profile 的 lark-cli runner 构造飞书通道。 */
134
- export function createFeishuChannel(opts) {
135
- return new FeishuChannel({ ...opts, runner: opts.runner ?? createLarkRunner() });
136
- }
137
- /**
138
- * `ra doc` 用的轻量文档器。
139
- *
140
- * 一期仍直连飞书;等接入第二通道时改走 Router API,按工单所在通道选择实现。
141
- */
142
- export function createFeishuDocumenter(opts = {}) {
143
- const sender = new FeishuSender(opts.runner ?? createLarkRunner());
144
- return { createDocument: (title, markdown) => sender.createDocument(title, markdown) };
145
- }
146
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/channel/src/feishu/index.ts"],"names":[],"mappings":"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"}
package/dist/legacy.js DELETED
@@ -1,26 +0,0 @@
1
- import { shouldSendAsText } from './sources/feishu.js';
2
- export function createEffectsSender(sender) {
3
- return {
4
- async send(item) {
5
- if (item.channelKind !== 'feishu')
6
- return;
7
- const text = item.text.trim();
8
- if (!text)
9
- return;
10
- // 简单消息走纯文本:不开卡片,少一次渲染、少一类兼容问题。
11
- if (shouldSendAsText(text)) {
12
- if (item.threadId)
13
- await sender.reply(item.threadId, text, item.key);
14
- else
15
- await sender.send(item.channelId, text, item.key);
16
- return;
17
- }
18
- // 其余走互动卡片:Markdown 先在 feishuCard 内归一化,卡片失败时 Sender 内部降级为 post。
19
- if (item.threadId)
20
- await sender.replyCard(item.threadId, text, item.key);
21
- else
22
- await sender.sendCard(item.channelId, text, item.key);
23
- },
24
- };
25
- }
26
- //# sourceMappingURL=legacy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"legacy.js","sourceRoot":"","sources":["../../../../packages/channel/src/legacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAwBtD,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,IAAI;YACb,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ;gBAAE,OAAM;YACzC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;YAC7B,IAAI,CAAC,IAAI;gBAAE,OAAM;YACjB,+BAA+B;YAC/B,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,QAAQ;oBAAE,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;;oBAC/D,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;gBACtD,OAAM;YACR,CAAC;YACD,+DAA+D;YAC/D,IAAI,IAAI,CAAC,QAAQ;gBAAE,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;;gBACnE,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA;QAC5D,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -1,119 +0,0 @@
1
- import { mkdirSync } from 'node:fs';
2
- import { LARK_PROFILE_HOME, parseAuthStatus, parseDeviceLogin, } from './lark.js';
3
- /**
4
- * router-agent 实际用到的飞书业务域。
5
- *
6
- * 不要用默认的「全部域」授权:router-agent 只需要 IM(消息/群/成员)、
7
- * 云文档、云空间与通讯录,避免用户被要求授予日历、邮箱、审批等无关权限。
8
- */
9
- export const FEISHU_AUTH_DOMAINS = ['im', 'docs', 'drive', 'contact'];
10
- /**
11
- * 业务域之外必须显式申请的 scope。
12
- *
13
- * lark-cli 的 `contact` 域只映射到 `contact:user.basic_profile:readonly` 与
14
- * `contact:user:search`,**不含** `contact:user.id:readonly`。而 router-agent 要用
15
- * `contact/v3/users/batch_get_id` 把允许名单里的邮箱解析成 open_id,这个接口要的正是
16
- * `contact:user.id:readonly`:域映射不覆盖它 → 用户授权成功、token 里却没有这个权限,
17
- * 表现成「用户已经授权好飞书,但没有该权限」。
18
- *
19
- * lark-cli 的 `--scope` 与 `--domain` 是叠加关系,所以在这里补一个显式 scope:
20
- * 用户在授权页会看到这一项,授权后应用侧也一并开通。
21
- */
22
- export const FEISHU_AUTH_SCOPES = ['contact:user.id:readonly'];
23
- /** 授权参数:业务域 + 显式 scope(lark-cli 两者叠加)。 */
24
- export function feishuAuthScopeArgs() {
25
- return ['--domain', FEISHU_AUTH_DOMAINS.join(','), '--scope', FEISHU_AUTH_SCOPES.join(',')];
26
- }
27
- /**
28
- * 飞书机器人凭据与授权管理。
29
- *
30
- * 所有命令都通过已绑定固定 profile 的 lark-cli runner 执行:
31
- * - App Secret 只走 stdin,不写 config.yaml,也不进 argv / 日志;
32
- * - 授权走 Device Flow,先拿 verification_url,再由用户确认后完成轮询。
33
- */
34
- export class FeishuBotAdmin {
35
- lark;
36
- profileHome;
37
- constructor(opts) {
38
- this.lark = opts.lark;
39
- this.profileHome = opts.profileHome ?? LARK_PROFILE_HOME;
40
- }
41
- /** 把 App ID / App Secret 写入 lark-cli 的加密存储并校验。 */
42
- async configure(appId, appSecret) {
43
- const id = appId.trim();
44
- const secret = appSecret.trim();
45
- if (!id)
46
- return { ok: false, message: 'App ID 不能为空' };
47
- if (!secret)
48
- return { ok: false, message: 'App Secret 不能为空' };
49
- try {
50
- mkdirSync(this.profileHome, { recursive: true });
51
- }
52
- catch (error) {
53
- return { ok: false, message: `无法创建 lark-cli profile 目录:${errorMessage(error)}` };
54
- }
55
- const res = await this.lark.run(['config', 'init', '--app-id', id, '--app-secret-stdin'], { input: `${secret}\n`, timeoutMs: 30_000 });
56
- const detail = redact(`${res.stdout}\n${res.stderr}`, secret);
57
- if (res.code !== 0) {
58
- return { ok: false, message: detail || `lark-cli 退出码 ${res.code}` };
59
- }
60
- if (!/configuration saved|配置已保存/i.test(res.stdout)) {
61
- return { ok: false, message: detail || 'lark-cli 未确认配置已保存' };
62
- }
63
- return { ok: true, message: 'App Secret 已写入 lark-cli 加密存储' };
64
- }
65
- /** 读取机器人身份与用户授权状态。 */
66
- async status() {
67
- try {
68
- const res = await this.lark.run(['auth', 'status', '--json']);
69
- if (res.code !== 0)
70
- return this.emptyStatus(redact(`${res.stdout}\n${res.stderr}`));
71
- return parseAuthStatus(res.stdout);
72
- }
73
- catch (error) {
74
- return this.emptyStatus(errorMessage(error));
75
- }
76
- }
77
- /** 发起 Device Flow 授权,返回验证链接与 device code。 */
78
- async startLogin() {
79
- const res = await this.lark.run(['auth', 'login', '--no-wait', '--json', ...feishuAuthScopeArgs()], { timeoutMs: 30_000 });
80
- if (res.code !== 0) {
81
- throw new Error(redact(`${res.stdout}\n${res.stderr}`) || `lark-cli 退出码 ${res.code}`);
82
- }
83
- const parsed = parseDeviceLogin(res.stdout, res.stderr);
84
- if (!parsed.deviceCode) {
85
- throw new Error(`lark-cli 未返回 device code:${redact(`${res.stdout}\n${res.stderr}`) || '无输出'}`);
86
- }
87
- if (!parsed.verificationUrl) {
88
- throw new Error(`lark-cli 未返回验证链接:${redact(`${res.stdout}\n${res.stderr}`) || '无输出'}`);
89
- }
90
- return parsed;
91
- }
92
- /** 完成授权轮询;最长等待 3 分钟。 */
93
- async completeLogin(deviceCode) {
94
- const code = deviceCode.trim();
95
- if (!code)
96
- throw new Error('device code 不能为空');
97
- const res = await this.lark.run(['auth', 'login', '--device-code', code, '--json'], { timeoutMs: 180_000 });
98
- if (res.code !== 0) {
99
- throw new Error(redact(`${res.stdout}\n${res.stderr}`) || `lark-cli 退出码 ${res.code}`);
100
- }
101
- return this.status();
102
- }
103
- emptyStatus(message) {
104
- return {
105
- appId: '',
106
- bot: { available: false, status: '', message },
107
- user: { available: false, status: '', message: '', openId: '', userName: '' },
108
- };
109
- }
110
- }
111
- function errorMessage(error) {
112
- return error instanceof Error ? error.message : String(error);
113
- }
114
- /** 截断并去掉可能的敏感值,避免错误响应里回显 App Secret。 */
115
- function redact(text, secret = '') {
116
- const cleaned = secret ? text.split(secret).join('***') : text;
117
- return cleaned.trim().slice(0, 240);
118
- }
119
- //# sourceMappingURL=feishu-bot-admin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"feishu-bot-admin.js","sourceRoot":"","sources":["../../../../../packages/channel/src/sources/feishu-bot-admin.ts"],"names":[],"mappings":"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"}
@@ -1,77 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { noopLogger, errorMessage } from '@router-agent/infra/logger.js';
3
- /** 群名目录:定期从 lark-cli 拉取并缓存,用于面板与通知里的可读名称。 */
4
- export class ChatDirectory {
5
- file;
6
- logger;
7
- names = new Map();
8
- constructor(file, logger = noopLogger) {
9
- this.file = file;
10
- this.logger = logger;
11
- this.load();
12
- }
13
- nameOf(chatId) {
14
- return this.names.get(chatId) ?? '';
15
- }
16
- entries() {
17
- return [...this.names.entries()].map(([chatId, name]) => ({ chatId, name }));
18
- }
19
- /** 用 lark-cli 的真实返回刷新缓存;失败保留旧值。 */
20
- async refresh(runner) {
21
- const res = await runner.run(['im', '+chat-list', '--as', 'bot', '--format', 'json']);
22
- if (res.code !== 0) {
23
- this.logger.warn('chats:refresh-failed', { code: res.code, message: res.stderr.slice(0, 200) });
24
- return 0;
25
- }
26
- const parsed = parseChatList(res.stdout, this.logger);
27
- for (const entry of parsed)
28
- this.names.set(entry.chatId, entry.name);
29
- this.save();
30
- return parsed.length;
31
- }
32
- load() {
33
- if (!existsSync(this.file))
34
- return;
35
- try {
36
- const doc = JSON.parse(readFileSync(this.file, 'utf8'));
37
- for (const entry of doc.chats ?? [])
38
- this.names.set(entry.chatId, entry.name);
39
- }
40
- catch (error) {
41
- // 缓存损坏时当作空,但留一条日志,避免群名一直显示为空却查不到原因。
42
- this.logger.warn('chats:cache-invalid', { path: this.file, message: errorMessage(error) });
43
- }
44
- }
45
- save() {
46
- writeFileSync(this.file, `${JSON.stringify({ chats: this.entries() }, null, 2)}\n`, 'utf8');
47
- }
48
- }
49
- /** 兼容 `{data:{chats:[...]}}` / `{data:{items:[...]}}` / 裸数组。 */
50
- export function parseChatList(stdout, logger = noopLogger) {
51
- let json;
52
- try {
53
- json = JSON.parse(stdout);
54
- }
55
- catch (error) {
56
- if (stdout.trim())
57
- logger.warn('chats:parse-failed', { message: errorMessage(error), sample: stdout.slice(0, 200) });
58
- return [];
59
- }
60
- if (Array.isArray(json))
61
- return normalize(json);
62
- const root = json;
63
- const data = root?.data ?? root;
64
- const list = data?.chats ?? data?.items ?? [];
65
- return Array.isArray(list) ? normalize(list) : [];
66
- }
67
- function normalize(list) {
68
- const out = [];
69
- for (const raw of list) {
70
- const chatId = String(raw.chat_id ?? raw.chatId ?? '');
71
- const name = String(raw.name ?? raw.chat_name ?? '');
72
- if (chatId)
73
- out.push({ chatId, name });
74
- }
75
- return out;
76
- }
77
- //# sourceMappingURL=feishu-directory.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"feishu-directory.js","sourceRoot":"","sources":["../../../../../packages/channel/src/sources/feishu-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,kBAAkB,CAAA;AAOxE,6CAA6C;AAC7C,MAAM,OAAO,aAAa;IAChB,IAAI,CAAQ;IACZ,MAAM,CAAQ;IACd,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEzC,YAAY,IAAY,EAAE,SAAiB,UAAU;QACnD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,MAAM,CAAC,MAAc;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IACrC,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAC9E,CAAC;IAED,mCAAmC;IACnC,KAAK,CAAC,OAAO,CAAC,MAAqB;QACjC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAA;QACrF,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;YAC/F,OAAO,CAAC,CAAA;QACV,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QACrD,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QACpE,IAAI,CAAC,IAAI,EAAE,CAAA;QACX,OAAO,MAAM,CAAC,MAAM,CAAA;IACtB,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAM;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAA4B,CAAA;YAClF,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE;gBAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC/E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,oCAAoC;YACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC5F,CAAC;IACH,CAAC;IAEO,IAAI;QACV,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC7F,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,SAAiB,UAAU;IACvE,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,IAAI,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QACpH,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,IAAI,CAAC,CAAA;IAC/C,MAAM,IAAI,GAAG,IAA2B,CAAA;IACxC,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,IAAI,CAAA;IAC/B,MAAM,IAAI,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,CAAA;IAC7C,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACnD,CAAC;AAED,SAAS,SAAS,CAAC,IAA+B;IAChD,MAAM,GAAG,GAAgB,EAAE,CAAA;IAC3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;QACtD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;QACpD,IAAI,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}