@hywkp/sider 0.0.3 → 0.0.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 +280 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/src/channel.d.ts +12 -0
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/channel.js +619 -194
- package/dist/src/channel.js.map +1 -1
- package/dist/src/inbound-media.d.ts +24 -0
- package/dist/src/inbound-media.d.ts.map +1 -0
- package/dist/src/inbound-media.js +56 -0
- package/dist/src/inbound-media.js.map +1 -0
- package/dist/src/media-upload.d.ts +31 -0
- package/dist/src/media-upload.d.ts.map +1 -0
- package/dist/src/media-upload.js +319 -0
- package/dist/src/media-upload.js.map +1 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Sider OpenClaw Plugin 对接文档
|
|
2
|
+
|
|
3
|
+
本文档面向两类同学:
|
|
4
|
+
- 需要安装 `openclaw-plugins/sider` 的运维/后端同学
|
|
5
|
+
- 需要消费 `typing/stream/tool` 事件的客户端同学(Web/iOS/Android)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 一键安装
|
|
10
|
+
|
|
11
|
+
安装脚本默认执行:
|
|
12
|
+
- `openclaw plugins install @hywkp/sider`
|
|
13
|
+
- 若已安装(`plugin already exists`),自动尝试:`openclaw plugins update sider`
|
|
14
|
+
|
|
15
|
+
仅安装插件(不改配置):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
curl -fsSL https://chat-demo.hyw.workers.dev/sider/install-openclaw-plugin.sh | RUN_CONFIGURE=0 bash
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
安装并写入 `channels.sider` 配置:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
curl -fsSL https://chat-demo.hyw.workers.dev/sider/install-openclaw-plugin.sh | \
|
|
25
|
+
SIDER_GATEWAY_URL='http://127.0.0.1:8080' \
|
|
26
|
+
SIDER_SESSION_ID='s1' \
|
|
27
|
+
bash
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
可选参数:
|
|
31
|
+
- `SIDER_RELAY_ID`
|
|
32
|
+
- `SIDER_RELAY_TOKEN`
|
|
33
|
+
- 兼容旧变量:`SIDER_SESSION_KEY`(会自动映射到 `SIDER_SESSION_ID`)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. OpenClaw 配置示例
|
|
38
|
+
|
|
39
|
+
`~/.openclaw/openclaw.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"channels": {
|
|
44
|
+
"sider": {
|
|
45
|
+
"enabled": true,
|
|
46
|
+
"gatewayUrl": "http://127.0.0.1:8080",
|
|
47
|
+
"sessionId": "s1",
|
|
48
|
+
"defaultTo": "session:s1",
|
|
49
|
+
"relayId": "openclaw-default"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
检查状态:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
openclaw channels list
|
|
59
|
+
openclaw status --json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 3. 基础协议(relay 侧)
|
|
65
|
+
|
|
66
|
+
WebSocket 连接:
|
|
67
|
+
- `ws://<gateway>/ws/relay`
|
|
68
|
+
|
|
69
|
+
握手首帧:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{"type":"register","session_id":"<session_id>","relay_id":"<relay_id>","token":"<optional>"}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
发送持久化消息:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"type":"message",
|
|
80
|
+
"session_id":"<session_id>",
|
|
81
|
+
"client_req_id":"<uuid>",
|
|
82
|
+
"parts":[{"type":"core.text","payload":{"text":"hello"}}]
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
发送实时事件:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"type":"event",
|
|
91
|
+
"session_id":"<session_id>",
|
|
92
|
+
"client_req_id":"<uuid>",
|
|
93
|
+
"event_type":"typing",
|
|
94
|
+
"payload":{"on":true},
|
|
95
|
+
"meta":{}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
ACK:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{"type":"ack","session_id":"<session_id>","id":"<id>"}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. 客户端事件约定(typing / stream / tool)
|
|
108
|
+
|
|
109
|
+
插件会发送以下实时事件(`type=event`,`source_role=relay`)。
|
|
110
|
+
|
|
111
|
+
### 4.1 typing
|
|
112
|
+
|
|
113
|
+
开始输入:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"event_type":"typing",
|
|
118
|
+
"payload":{
|
|
119
|
+
"on":true,
|
|
120
|
+
"state":"typing",
|
|
121
|
+
"session_id":"<session_id>",
|
|
122
|
+
"ts":1730000000000
|
|
123
|
+
},
|
|
124
|
+
"meta":{
|
|
125
|
+
"channel":"sider",
|
|
126
|
+
"account_id":"default",
|
|
127
|
+
"schema_version":1
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
停止输入时:`on=false`,`state=idle`。
|
|
133
|
+
|
|
134
|
+
### 4.2 stream.start
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"event_type":"stream.start",
|
|
139
|
+
"payload":{
|
|
140
|
+
"session_id":"<session_id>",
|
|
141
|
+
"stream_id":"<uuid>",
|
|
142
|
+
"ts":1730000000000
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 4.3 stream.delta
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"event_type":"stream.delta",
|
|
152
|
+
"payload":{
|
|
153
|
+
"session_id":"<session_id>",
|
|
154
|
+
"stream_id":"<uuid>",
|
|
155
|
+
"seq":1,
|
|
156
|
+
"delta":"你好,",
|
|
157
|
+
"done":false,
|
|
158
|
+
"chunk_chars":3,
|
|
159
|
+
"ts":1730000000001
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 4.4 stream.done
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"event_type":"stream.done",
|
|
169
|
+
"payload":{
|
|
170
|
+
"session_id":"<session_id>",
|
|
171
|
+
"stream_id":"<uuid>",
|
|
172
|
+
"seq":99,
|
|
173
|
+
"done":true,
|
|
174
|
+
"reason":"final|interrupted",
|
|
175
|
+
"ts":1730000000999
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
说明:
|
|
181
|
+
- `reason=final`:流式正常结束
|
|
182
|
+
- `reason=interrupted`:流式被中断(例如上游出错或中断)
|
|
183
|
+
|
|
184
|
+
### 4.5 tool.call
|
|
185
|
+
|
|
186
|
+
工具调用开始事件(来源于 OpenClaw `before_tool_call`):
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"event_type":"tool.call",
|
|
191
|
+
"payload":{
|
|
192
|
+
"session_id":"<session_id>",
|
|
193
|
+
"seq":1,
|
|
194
|
+
"call_id":"<uuid>",
|
|
195
|
+
"phase":"start",
|
|
196
|
+
"tool_name":"read",
|
|
197
|
+
"tool_call_id":"<provider_call_id>",
|
|
198
|
+
"run_id":"<run_id>",
|
|
199
|
+
"session_key":"agent:main:...",
|
|
200
|
+
"tool_args":{"path":"README.md"},
|
|
201
|
+
"error":null,
|
|
202
|
+
"duration_ms":null,
|
|
203
|
+
"ts":1730000000002
|
|
204
|
+
},
|
|
205
|
+
"meta":{
|
|
206
|
+
"channel":"sider",
|
|
207
|
+
"account_id":"default",
|
|
208
|
+
"schema_version":1
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
说明:
|
|
214
|
+
- `call_id` 用于把同一次工具调用的 `tool.call` 和 `tool.result` 关联起来。
|
|
215
|
+
- `tool_args` 即工具参数(例如 `read` 的 `path`)。
|
|
216
|
+
- `tool_call_id` 为 provider 侧 call id(有则透传)。
|
|
217
|
+
|
|
218
|
+
### 4.6 tool.result
|
|
219
|
+
|
|
220
|
+
工具结果事件(来源于 OpenClaw `after_tool_call`):
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"event_type":"tool.result",
|
|
225
|
+
"payload":{
|
|
226
|
+
"session_id":"<session_id>",
|
|
227
|
+
"seq":2,
|
|
228
|
+
"call_id":"<uuid>",
|
|
229
|
+
"tool_name":"read",
|
|
230
|
+
"tool_call_id":"<provider_call_id>",
|
|
231
|
+
"run_id":"<run_id>",
|
|
232
|
+
"session_key":"agent:main:...",
|
|
233
|
+
"tool_args":{"path":"README.md"},
|
|
234
|
+
"result":{"content":[{"type":"text","text":"..."}]},
|
|
235
|
+
"error":null,
|
|
236
|
+
"duration_ms":32,
|
|
237
|
+
"text":"",
|
|
238
|
+
"has_text":false,
|
|
239
|
+
"media_urls":[],
|
|
240
|
+
"media_count":0,
|
|
241
|
+
"is_error":false,
|
|
242
|
+
"ts":1730000000003
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
说明:
|
|
248
|
+
- `result` 为工具返回值(JSON-safe 序列化后)。
|
|
249
|
+
- `error` 非空时表示工具执行失败。
|
|
250
|
+
- `text` 为插件提取的“可读摘要”,仅从 `result.text/content/message/output/stdout` 取值,不做回退。
|
|
251
|
+
- `tool.result` 是工具阶段的实时事件,不替代最终 `type=message`。
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 5. 客户端渲染建议
|
|
256
|
+
|
|
257
|
+
- 使用 `stream_id + seq` 作为流式拼接键,按 `seq` 递增拼接 `delta`
|
|
258
|
+
- `stream.*` 仅用于实时渲染,不作为历史消息源
|
|
259
|
+
- `tool.call/tool.result` 仅用于实时状态,不作为历史消息源
|
|
260
|
+
- 最终以 `type=message` 的持久化消息为准(用于会话历史)
|
|
261
|
+
- 收到 `stream.done` 后,等待最终 `message` 到达再收敛 UI
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 6. 常见排查
|
|
266
|
+
|
|
267
|
+
- 看不到 typing/stream:
|
|
268
|
+
- 检查是否连接到了同一个 `session_id`
|
|
269
|
+
- 检查客户端是否处理了 `type=event`
|
|
270
|
+
- 收到附件但模型像“看不到文件”:
|
|
271
|
+
- 插件入站会先下载 `core.file/core.media` 到本地媒体存储,再把 `MediaPath(s)` 放入 OpenClaw 上下文
|
|
272
|
+
- 若下载失败,会降级为把附件 URL 拼进消息正文(日志关键字:`sider inbound media download failed`)
|
|
273
|
+
- 图片/文件发送失败:
|
|
274
|
+
- 插件会走 `POST /v1/files/init` + 直传 `upload_url` + `POST /v1/files/complete(可选)`
|
|
275
|
+
- 若网关返回 `501 file service not enabled`,说明当前不是 S3 文件存储驱动
|
|
276
|
+
- 发送成功但没收到最终回复:
|
|
277
|
+
- 检查是否收到 `ack`
|
|
278
|
+
- 检查 OpenClaw 日志中的 `sider` 相关错误
|
|
279
|
+
- 收到 `replaced`:
|
|
280
|
+
- 表示同 `relay_id` 有新连接顶掉旧连接,需重连
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,QAAA,MAAM,MAAM;;;;;kBAKI,iBAAiB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,QAAA,MAAM,MAAM;;;;;kBAKI,iBAAiB;CA2BhC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
2
|
-
import { setSiderRuntime, siderPlugin } from "./src/channel.js";
|
|
2
|
+
import { emitSiderToolHookEvent, setSiderRuntime, siderPlugin } from "./src/channel.js";
|
|
3
3
|
const plugin = {
|
|
4
4
|
id: "sider",
|
|
5
5
|
name: "Sider",
|
|
@@ -8,6 +8,29 @@ const plugin = {
|
|
|
8
8
|
register(api) {
|
|
9
9
|
setSiderRuntime(api.runtime);
|
|
10
10
|
api.registerChannel({ plugin: siderPlugin });
|
|
11
|
+
api.on("before_tool_call", async (event, ctx) => {
|
|
12
|
+
await emitSiderToolHookEvent({
|
|
13
|
+
sessionKey: ctx.sessionKey,
|
|
14
|
+
phase: "start",
|
|
15
|
+
toolName: event.toolName ?? ctx.toolName,
|
|
16
|
+
toolCallId: event.toolCallId ?? ctx.toolCallId,
|
|
17
|
+
runId: event.runId ?? ctx.runId,
|
|
18
|
+
params: event.params,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
api.on("after_tool_call", async (event, ctx) => {
|
|
22
|
+
await emitSiderToolHookEvent({
|
|
23
|
+
sessionKey: ctx.sessionKey,
|
|
24
|
+
phase: event.error ? "error" : "end",
|
|
25
|
+
toolName: event.toolName ?? ctx.toolName,
|
|
26
|
+
toolCallId: event.toolCallId ?? ctx.toolCallId,
|
|
27
|
+
runId: event.runId ?? ctx.runId,
|
|
28
|
+
params: event.params,
|
|
29
|
+
result: event.result,
|
|
30
|
+
error: event.error,
|
|
31
|
+
durationMs: event.durationMs,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
11
34
|
},
|
|
12
35
|
};
|
|
13
36
|
export default plugin;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAExF,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,uCAAuC;IACpD,YAAY,EAAE,uBAAuB,EAAE;IACvC,QAAQ,CAAC,GAAsB;QAC7B,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,GAAG,CAAC,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC9C,MAAM,sBAAsB,CAAC;gBAC3B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;gBACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;gBAC9C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC7C,MAAM,sBAAsB,CAAC;gBAC3B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;gBACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;gBACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU;gBAC9C,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK;gBAC/B,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/src/channel.d.ts
CHANGED
|
@@ -27,7 +27,19 @@ type ResolvedSiderAccount = {
|
|
|
27
27
|
configured: boolean;
|
|
28
28
|
config: SiderAccountConfig;
|
|
29
29
|
};
|
|
30
|
+
type SiderToolHookPayload = {
|
|
31
|
+
sessionKey?: string;
|
|
32
|
+
phase: "start" | "end" | "error";
|
|
33
|
+
toolName?: string;
|
|
34
|
+
toolCallId?: string;
|
|
35
|
+
runId?: string;
|
|
36
|
+
params?: Record<string, unknown>;
|
|
37
|
+
result?: unknown;
|
|
38
|
+
error?: string;
|
|
39
|
+
durationMs?: number;
|
|
40
|
+
};
|
|
30
41
|
export declare function setSiderRuntime(runtime: PluginRuntime): void;
|
|
42
|
+
export declare function emitSiderToolHookEvent(params: SiderToolHookPayload): Promise<void>;
|
|
31
43
|
export declare const siderPlugin: ChannelPlugin<ResolvedSiderAccount>;
|
|
32
44
|
export {};
|
|
33
45
|
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,aAAa,EAElB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,aAAa,EAElB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAC;AAU7B,KAAK,kBAAkB,GAAG;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAMF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AA+DF,KAAK,oBAAoB,GAAG;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA8CF,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE5D;AAmyBD,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqExF;AA8yBD,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,oBAAoB,CAmK3D,CAAC"}
|