meow-push-bridge 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +526 -2
- package/cordis.debug.patch.yml +23 -0
- package/cordis.patch.yml +26 -0
- package/dist/auto-push.d.ts +75 -0
- package/dist/auto-push.d.ts.map +1 -0
- package/dist/auto-push.js +173 -0
- package/dist/auto-push.js.map +1 -0
- package/dist/config.d.ts +67 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +112 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +304 -0
- package/dist/index.js.map +1 -0
- package/dist/invariant.d.ts +78 -0
- package/dist/invariant.d.ts.map +1 -0
- package/dist/invariant.js +257 -0
- package/dist/invariant.js.map +1 -0
- package/dist/meow-api.d.ts +98 -0
- package/dist/meow-api.d.ts.map +1 -0
- package/dist/meow-api.js +301 -0
- package/dist/meow-api.js.map +1 -0
- package/dist/prompt.d.ts +53 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +88 -0
- package/dist/prompt.js.map +1 -0
- package/dist/schema.d.ts +223 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +510 -0
- package/dist/schema.js.map +1 -0
- package/dist/tool.d.ts +75 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +38 -0
- package/dist/tool.js.map +1 -0
- package/lib/client.js +388 -0
- package/package.json +82 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 JunJunLoveProgramming
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,527 @@
|
|
|
1
|
-
#
|
|
1
|
+
# meow-push-bridge · 喵桥
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 让 DSH Agent 在完成任务后,通过 [MeoW](http://api.chuckfang.com) API 向你的鸿蒙手机推送通知。
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](#开发与测试)
|
|
8
|
+
|
|
9
|
+
`meow-push-bridge` 是一个 DSH / Cordis 双半体插件:
|
|
10
|
+
|
|
11
|
+
- **宿主半体**注册工具 `meow_push_bridge`,让 Agent 能主动推送通知;
|
|
12
|
+
- **浏览器半体**在 DSH 设置页提供配置表单,填昵称即可,无需手写 YAML;
|
|
13
|
+
- **宿主侧自动推送**在每轮对话结束时由插件直接发通知,**不依赖模型自觉调用**。
|
|
14
|
+
|
|
15
|
+
| | |
|
|
16
|
+
|---|---|
|
|
17
|
+
| npm 包名 | `meow-push-bridge` |
|
|
18
|
+
| 插件 ID | `meow-push-bridge` |
|
|
19
|
+
| 工具名 | `meow_push_bridge` |
|
|
20
|
+
| 运行时依赖 | **零**(只用 Node 内置 `fetch` / `AbortSignal`) |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 目录
|
|
25
|
+
|
|
26
|
+
- [特性](#特性)
|
|
27
|
+
- [环境要求](#环境要求)
|
|
28
|
+
- [安装](#安装)
|
|
29
|
+
- [配置](#配置)
|
|
30
|
+
- [使用](#使用)
|
|
31
|
+
- [工作方式](#工作方式)
|
|
32
|
+
- [MeoW API 行为](#meow-api-行为)
|
|
33
|
+
- [错误排查](#错误排查)
|
|
34
|
+
- [开发与测试](#开发与测试)
|
|
35
|
+
- [设计说明](#设计说明)
|
|
36
|
+
- [安全建议](#安全建议)
|
|
37
|
+
- [作者](#作者)
|
|
38
|
+
- [反馈](#反馈)
|
|
39
|
+
- [许可](#许可)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 特性
|
|
44
|
+
|
|
45
|
+
- **设置页可视化配置**:DSH 设置 →「内置插件」→「喵桥」,填昵称、选自动推送模式
|
|
46
|
+
- **自动推送保证触发**:`pushMode: every-turn` 时每轮回答结束都由宿主推送,不受模型是否遵守提示词影响
|
|
47
|
+
- **加载期强校验**:`nickname` 未配置时拒绝注册工具并给出明确指引,避免误推到他人账号
|
|
48
|
+
- **完整 URL 编码**:路径段先做全角替换再编码,中文、空格、`/`、`?`、`#`、`&` 都不会破坏请求
|
|
49
|
+
- **错误分类清晰**:区分 `invalid` / `timeout` / `aborted` / `network` / `http` / `business`,每类给出可操作提示
|
|
50
|
+
- **双重超时保护**:请求超时(默认 10s)与上游取消信号,任一触发都能干净退出
|
|
51
|
+
- **节流防刷屏**:工具调用默认 5 秒内不重复推送;自动推送按轮次去重
|
|
52
|
+
- **零运行时依赖**:不引入任何 `@deepseek-ai/*`,避免版本漂移与私有 registry 解析问题
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 环境要求
|
|
57
|
+
|
|
58
|
+
| 项 | 要求 |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Node.js | ≥ 20.6(需要内置 `fetch`、`AbortSignal.timeout`、`AbortSignal.any`) |
|
|
61
|
+
| DSH | 已安装,profile 使用 Cordis 配置 |
|
|
62
|
+
| MeoW | 鸿蒙手机已安装 MeoW App,且已设置用户昵称 |
|
|
63
|
+
| 网络 | 能访问 `http://api.chuckfang.com` |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 安装
|
|
68
|
+
|
|
69
|
+
### 从 npm 安装
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
dsh plugin --profile <你的profile> add meow-push-bridge
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 从本地 tarball 安装
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pnpm install
|
|
79
|
+
pnpm pack
|
|
80
|
+
dsh plugin --profile <你的profile> add ./meow-push-bridge-0.1.0.tgz
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
安装成功后,`dsh plugin` 会把包写入 profile 的 `package.json` 并自动登记为 bundle:
|
|
84
|
+
|
|
85
|
+
```jsonc
|
|
86
|
+
{
|
|
87
|
+
"dependencies": {
|
|
88
|
+
"meow-push-bridge": "file:.../meow-push-bridge-0.1.0.tgz"
|
|
89
|
+
},
|
|
90
|
+
"dsh": {
|
|
91
|
+
"profile": {
|
|
92
|
+
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "meow-push-bridge"]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
用 `dsh --profile <name> --dump-config` 可确认挂载结果:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
# == meow-push-bridge
|
|
102
|
+
- id: meow-push-bridge
|
|
103
|
+
name: meow-push-bridge
|
|
104
|
+
config:
|
|
105
|
+
nickname: ''
|
|
106
|
+
pushMode: every-turn
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> **装完必须重启 DSH**:已加载的模块不会因为重装而热更新。
|
|
110
|
+
> 部分 profile(如桌面应用的 profile)由应用独占管理,不能用 CLI 启动,只能通过应用重启加载。
|
|
111
|
+
|
|
112
|
+
### 源码调试(不安装)
|
|
113
|
+
|
|
114
|
+
把 [cordis.debug.patch.yml](cordis.debug.patch.yml) 作为叠加层挂进 profile:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
dsh --profile <name> --patch ./cordis.debug.patch.yml
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
该文件的 `name` 指向 `./src/index.ts`,Loader 会直接加载 TypeScript 源码。
|
|
121
|
+
|
|
122
|
+
> 仓库内两个 patch 文件的区别只在 `name`:
|
|
123
|
+
> `cordis.patch.yml` → `meow-push-bridge`(发布用,解析到 `dist/index.js`);
|
|
124
|
+
> `cordis.debug.patch.yml` → `./src/index.ts`(调试用)。
|
|
125
|
+
> npm 包的 `files` 白名单只含 `dist/` 与 `lib/`,发布配置里不能写 `./src/index.ts`。
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 配置
|
|
130
|
+
|
|
131
|
+
### 在 DSH 设置页配置(推荐)
|
|
132
|
+
|
|
133
|
+
**设置 → 内置插件 →「喵桥」**:
|
|
134
|
+
|
|
135
|
+
| 字段 | 控件 | 说明 | 默认值 |
|
|
136
|
+
|---|---|---|---|
|
|
137
|
+
| `nickname` | 文本框 | **必填**:你的 MeoW 用户昵称 | — |
|
|
138
|
+
| `apiBase` | 文本框 | MeoW API 基础地址(可指向自建反代) | `http://api.chuckfang.com` |
|
|
139
|
+
| `timeout` | 数字框 | 单次请求超时毫秒数,范围 100–120000 | `10000` |
|
|
140
|
+
| `throttleMs` | 数字框 | 工具调用的同会话节流窗口,`0` 关闭 | `5000` |
|
|
141
|
+
| `pushMode` | 下拉框 | 自动推送模式,见下 | `every-turn` |
|
|
142
|
+
|
|
143
|
+
保存后,DSH 的 `settings` 服务会把值写入该 profile 的 `cordis.patch.yml` 并热应用:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
- id: meow-push-bridge
|
|
147
|
+
name: meow-push-bridge
|
|
148
|
+
config:
|
|
149
|
+
nickname: 你的昵称
|
|
150
|
+
apiBase: http://api.chuckfang.com
|
|
151
|
+
timeout: 10000
|
|
152
|
+
throttleMs: 5000
|
|
153
|
+
pushMode: every-turn
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 直接编辑配置文件
|
|
157
|
+
|
|
158
|
+
在 profile 的 `cordis.patch.yml` 中加入:
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
- id: meow-push-bridge
|
|
162
|
+
config:
|
|
163
|
+
nickname: "你的MeoW昵称" # 必填
|
|
164
|
+
apiBase: "http://api.chuckfang.com"
|
|
165
|
+
timeout: 10000
|
|
166
|
+
throttleMs: 5000
|
|
167
|
+
pushMode: every-turn
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 字段说明
|
|
171
|
+
|
|
172
|
+
| 字段 | 类型 | 必填 | 默认值 | 非法时 |
|
|
173
|
+
|---|---|---|---|---|
|
|
174
|
+
| `nickname` | string | ✅ | — | 拒绝注册工具 + error 日志 |
|
|
175
|
+
| `apiBase` | string | — | `http://api.chuckfang.com` | 回退默认值 + warn 日志 |
|
|
176
|
+
| `timeout` | number | — | `10000` | 回退默认值 + warn 日志 |
|
|
177
|
+
| `throttleMs` | number | — | `5000` | 回退默认值 + warn 日志 |
|
|
178
|
+
| `pushMode` | string | — | `every-turn` | 回退默认值 + warn 日志 |
|
|
179
|
+
|
|
180
|
+
归一化规则:`nickname` 与 `apiBase` 去掉首尾空格,`apiBase` 末尾多余的 `/` 会被清理。
|
|
181
|
+
配置文件**不会**展开 `${ENV_VAR}`,请填写字面量昵称。
|
|
182
|
+
|
|
183
|
+
> 若 `nickname` 为空,插件会在日志中提示:
|
|
184
|
+
> `❌ 插件未启用:MeoW 昵称未配置,请在 cordis.patch.yml 中填写 nickname 字段后重载插件。`
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 使用
|
|
189
|
+
|
|
190
|
+
### 工具参数
|
|
191
|
+
|
|
192
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
193
|
+
|---|---|---|---|
|
|
194
|
+
| `title` | string | — | 通知标题,缺省 `MeoW Push Bridge` |
|
|
195
|
+
| `message` | string | ✅ | 通知正文 |
|
|
196
|
+
| `url` | string | — | 点击通知后跳转的链接 |
|
|
197
|
+
|
|
198
|
+
昵称不是工具参数 —— 推送目标由配置决定,模型无法在调用时改写。
|
|
199
|
+
|
|
200
|
+
### 自动推送
|
|
201
|
+
|
|
202
|
+
由 `pushMode` 控制,宿主监听轮次结束事件后直接推送,**不依赖模型**:
|
|
203
|
+
|
|
204
|
+
| 值 | 行为 |
|
|
205
|
+
|---|---|
|
|
206
|
+
| `every-turn` | 每轮回答结束都推 |
|
|
207
|
+
| `substantial` | 仅当本轮调用过工具才推 |
|
|
208
|
+
| `off` | 关闭自动推送,只保留手动 |
|
|
209
|
+
|
|
210
|
+
正文为统计式描述:
|
|
211
|
+
|
|
212
|
+
```text
|
|
213
|
+
本轮已完成:调用 3 次工具(pwsh、read、grep),耗时 12s
|
|
214
|
+
本轮已完成(纯问答,未使用工具,耗时 2s)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 手动推送
|
|
218
|
+
|
|
219
|
+
直接对 Agent 说:
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
把这次的结果推送到我手机上
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
插件同时向系统提示注册了使用约定,因此 Agent 也会在完成实质任务后主动推送一条**带语义总结**的通知。
|
|
226
|
+
模型那条在轮内发出,宿主那条在轮末补位,所以统计式描述总是排在最后。
|
|
227
|
+
|
|
228
|
+
### 命令行验证昵称
|
|
229
|
+
|
|
230
|
+
排障时可绕过插件直接确认昵称与网络:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Linux / macOS
|
|
234
|
+
curl -sS "http://api.chuckfang.com/你的昵称/测试/hi"
|
|
235
|
+
|
|
236
|
+
# Windows PowerShell
|
|
237
|
+
curl.exe -sS "http://api.chuckfang.com/你的昵称/测试/hi"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 工作方式
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
┌─ 宿主半体 dist/(Node,DSH 主进程)
|
|
246
|
+
│ apply(ctx, config)
|
|
247
|
+
│ ├─ 校验配置:nickname 缺失 → 记 error 并 return,不注册任何工具
|
|
248
|
+
│ ├─ 注册工具 meow_push_bridge
|
|
249
|
+
│ ├─ 注册提示词区段 tool:meow-push-bridge
|
|
250
|
+
│ └─ 按 pushMode 注册自动推送(监听 agent/turn-stopping)
|
|
251
|
+
│
|
|
252
|
+
└─ 浏览器半体 lib/client.js(Web 前端)
|
|
253
|
+
apply(ctx)
|
|
254
|
+
└─ 注册设置页签「喵桥」
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
配置从设置页到生效的路径:
|
|
258
|
+
|
|
259
|
+
| 步骤 | 执行者 |
|
|
260
|
+
|---|---|
|
|
261
|
+
| 1. 用户在设置页填值并保存 | 浏览器半体 |
|
|
262
|
+
| 2. `ctx.remote.settings.update(ns, patch, revision)` | Remote RPC |
|
|
263
|
+
| 3. 定位并改写 profile 的 `cordis.patch.yml` | `@deepseek-ai/dsh-settings` |
|
|
264
|
+
| 4. 走正常 Loader 路径热应用 | Cordis Loader |
|
|
265
|
+
| 5. `Config['~standard'].validate()` 校验并补默认值 | 宿主半体 |
|
|
266
|
+
|
|
267
|
+
### 两个半体的构建
|
|
268
|
+
|
|
269
|
+
| 命令 | 产出 | 说明 |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| `pnpm build:node` | `dist/*.js` + `*.d.ts` | 宿主半体(tsc 生成) |
|
|
272
|
+
| `pnpm build:client` | `lib/client.js` | 浏览器半体(预构建 bundle → 复制并自检格式) |
|
|
273
|
+
|
|
274
|
+
浏览器半体必须是 `window.__ModuleLoader__.load({ id, factory })` 格式的**预构建文件**
|
|
275
|
+
(`package.json` 的 `exports["./client"]` 指向它,宿主直接读取该文件字节);
|
|
276
|
+
缺失时 DSH 会报 `MissingClientBundleError`。本插件的表单较简单,因此手写该 bundle、不引入打包器。
|
|
277
|
+
|
|
278
|
+
### 事件依赖
|
|
279
|
+
|
|
280
|
+
| 事件 | 用途 |
|
|
281
|
+
|---|---|
|
|
282
|
+
| `agent/turn-stopping` | 轮次即将结束 → 触发自动推送 |
|
|
283
|
+
| `session/event`(`turn/start`、`tool/call`) | 统计本轮使用了哪些工具,用于生成通知正文 |
|
|
284
|
+
|
|
285
|
+
统计是尽力而为:若某环境拿不到 `session/event`,正文退化为「本轮已完成」,推送照常发出。
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## MeoW API 行为
|
|
290
|
+
|
|
291
|
+
### 请求形状
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
http://api.chuckfang.com/{nickname}/{title}/{message}?url={url}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 响应
|
|
298
|
+
|
|
299
|
+
```jsonc
|
|
300
|
+
// 成功
|
|
301
|
+
{ "status": 200, "data": true, "msg": "发送成功" }
|
|
302
|
+
|
|
303
|
+
// 昵称不存在(HTTP 状态码仍然为 200)
|
|
304
|
+
{ "status": 404, "data": false, "msg": "发送失败,该昵称没有注册,一天之内累计3次输错对方昵称您的IP将被封禁" }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
- **HTTP 状态码恒为 200**,真正的结果在 JSON 的 `status` / `data` 里,不能只看 HTTP 状态判断成败
|
|
308
|
+
- MeoW 提示:**一天内累计 3 次输错昵称会封 IP**,调试时不要反复试错昵称
|
|
309
|
+
|
|
310
|
+
### 路径段中的斜杠
|
|
311
|
+
|
|
312
|
+
URL **路径段**里的 `/` 编码后是 `%2F`,而 MeoW 前置的 Tomcat 默认拒绝它
|
|
313
|
+
(`ALLOW_ENCODED_SLASH=false`),会在请求到达应用前返回 `HTTP 400`:
|
|
314
|
+
|
|
315
|
+
| 请求形状 | 结果 |
|
|
316
|
+
|---|---|
|
|
317
|
+
| 路径段含 `%2F`(正文里有 `/`) | ❌ 400 |
|
|
318
|
+
| `/` 换成全角 `/` 或 `_` | ✅ 200 |
|
|
319
|
+
| 查询串含 `%2F`(`?url=a%2Fb`) | ✅ 200 |
|
|
320
|
+
| 路径段含空格 / `:` / `#` / `&` / `?`(编码后) | ✅ 200 |
|
|
321
|
+
|
|
322
|
+
因此三个路径段(`nickname` / `title` / `message`)会先做全角替换再编码:
|
|
323
|
+
`/`→`/`、`\`→`\`、`"`→`"`、`'`→`'`、`<`→`<`、`>`→`>`、`%`→`%`。
|
|
324
|
+
选全角而非删除是为了保留可读性:`72 通过 / 0 失败` 仍能看懂。
|
|
325
|
+
|
|
326
|
+
### 工具返回值
|
|
327
|
+
|
|
328
|
+
成功:
|
|
329
|
+
|
|
330
|
+
```
|
|
331
|
+
✅ 通知已推送到 MeoW:{title} - {message}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
失败时返回 `❌` 开头的中文说明,按分类给出建议:
|
|
335
|
+
|
|
336
|
+
| 分类 | 触发条件 | 示例 |
|
|
337
|
+
|---|---|---|
|
|
338
|
+
| `invalid` | `message` 为空 | `⚠️ 推送失败:message 不能为空。` |
|
|
339
|
+
| 被取消 | 调用被上游中止 | `⚠️ 推送已取消:调用在开始前被中止。` |
|
|
340
|
+
| 被节流 | 同会话 `throttleMs` 内重复调用工具 | `⚠️ 推送已节流:同一会话在 5000ms 内只能推送一次,请 3214ms 后重试。` |
|
|
341
|
+
| `timeout` | 超时未响应 | `❌ 推送超时:MeoW 在 10000ms 内未响应…` |
|
|
342
|
+
| `aborted` | 取消信号触发 | `❌ 推送已取消:调用方中止了操作。` |
|
|
343
|
+
| `network` | DNS / 连接失败 / 代理问题 | `❌ 推送失败:无法连接 MeoW(fetch failed)…` |
|
|
344
|
+
| `http` | HTTP 状态码非 2xx | `❌ 推送失败:MeoW 返回 HTTP 400…` |
|
|
345
|
+
| `business` | HTTP 2xx 但返回体表示失败 | `❌ 推送被 MeoW 拒绝(HTTP 200):发送失败,该昵称没有注册…` |
|
|
346
|
+
|
|
347
|
+
其他保证:仅成功推送才计入节流;节流按会话独立;工具级超时比请求超时多留 2 秒;
|
|
348
|
+
服务端返回体超过 300 字符会截断。
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 错误排查
|
|
353
|
+
|
|
354
|
+
### 启动阶段
|
|
355
|
+
|
|
356
|
+
| 现象 | 原因 | 解决 |
|
|
357
|
+
|---|---|---|
|
|
358
|
+
| 日志出现 `❌ 插件未启用:MeoW 昵称未配置…` | `config.nickname` 为空 | 在设置页或 `cordis.patch.yml` 填写字面量昵称并重启 |
|
|
359
|
+
| 日志出现 `❌ 插件未启用:tools 服务不可用…` | 宿主未提供 tools 服务 | 确认 DSH 版本包含 `@deepseek-ai/dsh-tools`,并检查该 entry 是否被禁用 |
|
|
360
|
+
| 日志出现 `❌ 不变式不满足(meow-push-bridge/…)` | 插件契约被改动 | 按日志中的 `id` 对照 [src/invariant.ts](src/invariant.ts) 还原 |
|
|
361
|
+
| 日志出现 `warn`:`apiBase "…" 不是合法的 http(s) 地址` | `apiBase` 写法有误 | 改为 `http://…` 或 `https://…`,或删除该字段用默认值 |
|
|
362
|
+
| 工具列表里没有 `meow_push_bridge` | 插件未加载或配置未通过校验 | 先看启动日志的 `❌` 行;再确认 profile 的 patch 含该 entry |
|
|
363
|
+
| 设置页里没有「喵桥」页签 | ① 插件装在其他 profile;② 发行版缺少 `lib/client.js` | ① 用 `dsh plugin --profile <名> add …` 装到同一 profile;② 确认 `node_modules/meow-push-bridge/lib/client.js` 存在 |
|
|
364
|
+
| 重装插件后行为没变 | 已加载的模块不会热更新 | **重启 DSH** |
|
|
365
|
+
|
|
366
|
+
### 运行阶段
|
|
367
|
+
|
|
368
|
+
| 现象 | 原因 | 解决 |
|
|
369
|
+
|---|---|---|
|
|
370
|
+
| `HTTP 400 Bad Request` + Tomcat 页面 | 路径段出现 `%2F` | 使用未做全角替换的旧版本;升级并重启 DSH |
|
|
371
|
+
| `HTTP 404` / 返回体含「该昵称没有注册」 | 昵称错误或未在 MeoW 注册 | 用 `curl` 验证昵称;注意大小写与特殊字符,并留意「输错 3 次封 IP」 |
|
|
372
|
+
| `HTTP 5xx` | MeoW 服务端异常 | 稍后重试 |
|
|
373
|
+
| `无法连接 MeoW(fetch failed)` | 断网 / DNS / 代理 / 防火墙 | 确认能访问 `api.chuckfang.com`;如走代理,为 DSH 进程配置 `HTTP_PROXY` / `HTTPS_PROXY` |
|
|
374
|
+
| `推送超时` | 网络或 MeoW 响应慢 | 调大 `timeout` |
|
|
375
|
+
| `推送已节流` | 同一会话 5 秒内重复调用**工具** | 等待提示的毫秒数,或调小 `throttleMs`。自动推送不受此限制 |
|
|
376
|
+
| 某些回合没有自动推送 | `pushMode` 为 `substantial` 且本轮未用工具,或为 `off` | 设为 `every-turn` |
|
|
377
|
+
| 自动推送正文只有「本轮已完成」 | 该环境拿不到会话事件,统计为空 | 属预期降级:推送照常发出,只是没有工具明细 |
|
|
378
|
+
| 推送成功但手机没收到 | MeoW App 未登录 / 通知权限被关 / 免打扰 | 先看 MeoW 的**消息列表**:列表有记录说明投递成功,只是通知未弹出 |
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## 开发与测试
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
pnpm install # 只装 typescript + @types/node
|
|
386
|
+
pnpm typecheck # 严格模式类型检查(src)
|
|
387
|
+
pnpm build # build:node(tsc → dist/)+ build:client(→ lib/client.js)
|
|
388
|
+
pnpm test # pretest: build + build:tests,随后跑 tests-dist/*.spec.js
|
|
389
|
+
pnpm pack # 打 tarball(prepack 自动 build)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
测试覆盖 **90 个用例**:
|
|
393
|
+
|
|
394
|
+
| 文件 | 用例数 | 覆盖内容 |
|
|
395
|
+
|---|---|---|
|
|
396
|
+
| [tests/register.spec.ts](tests/register.spec.ts) | 14 | 注册契约、配置校验、不变式、节流判定、服务探测异常 |
|
|
397
|
+
| [tests/impl.spec.ts](tests/impl.spec.ts) | 24 | URL 构造与编码、全角替换、超时/取消/错误分类、`execute` 端到端 |
|
|
398
|
+
| [tests/schema.spec.ts](tests/schema.spec.ts) | 21 | Standard Schema 契约、schemastery 形状、设置页表单闸门、枚举字段 |
|
|
399
|
+
| [tests/client.spec.ts](tests/client.spec.ts) | 5 | 浏览器半体格式、页签注册形状、RPC 参数透传、`dsh.client` 声明 |
|
|
400
|
+
| [tests/prompt.spec.ts](tests/prompt.spec.ts) | 8 | 提示词区段注册契约、排序区间、文案完整性 |
|
|
401
|
+
| [tests/auto-push.spec.ts](tests/auto-push.spec.ts) | 13 | 三种 `pushMode`、轮次去重、统计累计、降级行为 |
|
|
402
|
+
|
|
403
|
+
### 目录结构
|
|
404
|
+
|
|
405
|
+
```text
|
|
406
|
+
meow-push-bridge/
|
|
407
|
+
├── package.json # dsh.bundle.patch + dsh.client 双声明
|
|
408
|
+
├── cordis.patch.yml # 发布用挂载与配置
|
|
409
|
+
├── cordis.debug.patch.yml # 源码调试用
|
|
410
|
+
├── tsconfig.json # src → dist
|
|
411
|
+
├── tsconfig.tests.json # tests → tests-dist
|
|
412
|
+
├── src/
|
|
413
|
+
│ ├── index.ts # 宿主半体入口:配置校验 → 不变式 → 注册工具与自动推送
|
|
414
|
+
│ ├── auto-push.ts # 轮次结束时的宿主侧自动推送
|
|
415
|
+
│ ├── client.js # 浏览器半体(预构建 bundle)
|
|
416
|
+
│ ├── config.ts # 配置归一化与校验
|
|
417
|
+
│ ├── meow-api.ts # URL 构造、超时与错误分类
|
|
418
|
+
│ ├── invariant.ts # 包自有不变式
|
|
419
|
+
│ ├── prompt.ts # 系统提示区段(提示词层)
|
|
420
|
+
│ ├── schema.ts # 零依赖 Config schema
|
|
421
|
+
│ └── tool.ts # ToolDefinition 的结构化契约
|
|
422
|
+
├── lib/client.js # 构建产物(浏览器半体)
|
|
423
|
+
├── dist/ # 构建产物(宿主半体)
|
|
424
|
+
├── tests/ # 6 个 spec 文件
|
|
425
|
+
└── scripts/
|
|
426
|
+
├── copy-client.mjs # src/client.js → lib/client.js(带格式自检)
|
|
427
|
+
└── run-tests.mjs # 显式列文件跑 node --test
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 发布
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npm login
|
|
434
|
+
pnpm pack # 先验证 tarball 内容
|
|
435
|
+
npm publish --access public
|
|
436
|
+
|
|
437
|
+
git tag v0.1.0
|
|
438
|
+
git push origin v0.1.0 # 随后在 GitHub 创建 Release 并上传 .tgz
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 设计说明
|
|
444
|
+
|
|
445
|
+
面向二次开发者的取舍说明。
|
|
446
|
+
|
|
447
|
+
1. **配置不合法时记日志并返回,而不是抛异常**
|
|
448
|
+
抛异常会让 Loader 显示加载失败并打出堆栈,还可能影响同一 patch 中的其他 entry。
|
|
449
|
+
返回则得到「插件在册但零工具」的干净状态,日志仍然明确。
|
|
450
|
+
|
|
451
|
+
2. **`nickname` 不进工具入参**
|
|
452
|
+
推送目标是安全边界,只能由使用者通过配置决定,不能让模型在调用时改写。
|
|
453
|
+
该约束由不变式 `schema-no-nickname` 强制保证。
|
|
454
|
+
|
|
455
|
+
3. **不 import `@deepseek-ai/*`**
|
|
456
|
+
插件安装位置不保证能解析宿主的私有包。`src/tool.ts` 手写了与 DSH `ToolDefinition`
|
|
457
|
+
对齐的结构化类型,运行时只使用全局 `fetch` / `AbortSignal`。
|
|
458
|
+
|
|
459
|
+
4. **自动推送采用「提示词 + 宿主事件」两层**
|
|
460
|
+
仅靠提示词让模型自觉调用工具是概率性合规,不同环境表现不一致;
|
|
461
|
+
仅靠宿主事件则拿不到有语义的正文。因此两者并存:宿主层保证触发(统计式正文),
|
|
462
|
+
提示词层负责语义总结。宿主层刻意绕过工具级节流,改为按 `(会话, 轮次)` 去重。
|
|
463
|
+
|
|
464
|
+
5. **业务错误的判定是启发式的**
|
|
465
|
+
MeoW 未公开完整返回契约。插件只在 HTTP 2xx 且返回体包含
|
|
466
|
+
`fail` / `error` / `失败` / `不存在` 等关键词时判为 `business`。
|
|
467
|
+
如需更严格的判定,可改为按 `data === false` 处理。
|
|
468
|
+
|
|
469
|
+
6. **测试针对编译产物**
|
|
470
|
+
DSH 实际加载的是 `dist/*.js`,测试对准它才有意义。
|
|
471
|
+
|
|
472
|
+
7. **同时发布宿主半体与浏览器半体**
|
|
473
|
+
DSH 没有「第三方插件自动生成配置表单」的机制,内置插件的配置页也都是各自手写的客户端包,
|
|
474
|
+
因此本插件必须自带浏览器半体,否则用户只能手写 YAML。
|
|
475
|
+
|
|
476
|
+
8. **`Config` 需同时满足两套契约**
|
|
477
|
+
|
|
478
|
+
| 读取方 | 需要的形状 |
|
|
479
|
+
|---|---|
|
|
480
|
+
| Cordis `resolveConfig` | 字符串键 `'~standard'`(Standard Schema,必须同步) |
|
|
481
|
+
| `dsh-settings` 的 `volatileForm()` | schemastery 内部形状:`type` 为数据类型、字段带 `meta.volatile`、`dict`、`toJSON()` |
|
|
482
|
+
|
|
483
|
+
注意 `type` 字段不能被挪作标识用途,否则设置页表单会失效。
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## 安全建议
|
|
488
|
+
|
|
489
|
+
- **`cordis.patch.yml` 通常会被提交到 Git**:昵称本身不是密钥,但若 `apiBase` 指向含 token 的自建网关,请改用环境变量注入或将该文件加入 `.gitignore`
|
|
490
|
+
- **MeoW API 使用 HTTP**:不可信网络下通知内容可能被中间人看到,敏感信息建议自建 HTTPS 反代并把 `apiBase` 指向它
|
|
491
|
+
- **不要在通知正文里放密钥**:通知会经第三方服务传输,并在手机锁屏上可见
|
|
492
|
+
- **昵称会明文存在 profile 配置中**:分享该文件前请先抹掉
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## 作者
|
|
497
|
+
|
|
498
|
+
| 角色 | 姓名 |
|
|
499
|
+
|---|---|
|
|
500
|
+
| 作者 | **骏骏爱编程** |
|
|
501
|
+
| 技术支持 | **方程 chuckfang**(MeoW API 与 MeoW App) |
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## 反馈
|
|
506
|
+
|
|
507
|
+
| 类型 | 联系方式 |
|
|
508
|
+
|---|---|
|
|
509
|
+
| 项目反馈(插件本身的问题、建议、使用疑问) | [junjunloveprogramming@junjunloveprogramming.cn](mailto:junjunloveprogramming@junjunloveprogramming.cn) |
|
|
510
|
+
| MeoW App 反馈(推送未送达、账号问题、App 功能建议) | [chuckfang@qq.com](mailto:chuckfang@qq.com) |
|
|
511
|
+
|
|
512
|
+
提交项目问题时,建议附上以下信息以便快速定位:
|
|
513
|
+
|
|
514
|
+
- DSH 版本与操作系统
|
|
515
|
+
- profile 中该 entry 的 `config` 段(**请先抹掉昵称**)
|
|
516
|
+
- 启动日志里与 `meow-push-bridge` 相关的行
|
|
517
|
+
- 推送失败时的完整返回文案(例如 `❌ 推送失败:MeoW 返回 HTTP 400…`)
|
|
518
|
+
|
|
519
|
+
> 若「推送成功但手机没收到」,请先查看 **MeoW App 内的消息列表**:
|
|
520
|
+
> 列表中有记录说明已投递成功,问题在手机通知权限或免打扰设置;
|
|
521
|
+
> 列表中没有记录再联系 MeoW App 反馈邮箱。
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## 许可
|
|
526
|
+
|
|
527
|
+
[MIT](LICENSE) © 2025 骏骏爱编程
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# meow-push-bridge / MeoW Push Bridge (喵桥) —— 源码调试用配置补丁
|
|
2
|
+
#
|
|
3
|
+
# 用途:不安装、不构建,直接把本仓库的 TypeScript 源码挂进 profile 调试。
|
|
4
|
+
# Loader 会直接加载 ./src/index.ts,配合 dsh-hmr 保存即热重载。
|
|
5
|
+
#
|
|
6
|
+
# 用法:把下面内容复制进 profile 的 cordis.patch.yml(或作为 --patch 叠加层):
|
|
7
|
+
# dsh --profile web --patch ./cordis.debug.patch.yml
|
|
8
|
+
#
|
|
9
|
+
# 与 cordis.patch.yml 的唯一区别是 name:这里指向源码,那里指向包名。
|
|
10
|
+
# ────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
- insert:
|
|
13
|
+
- id: meow-push-bridge
|
|
14
|
+
name: ./src/index.ts
|
|
15
|
+
config:
|
|
16
|
+
# 【必填】你的 MeoW 用户昵称
|
|
17
|
+
nickname: ""
|
|
18
|
+
# 【可选】MeoW API 基础地址,默认 http://api.chuckfang.com
|
|
19
|
+
apiBase: "http://api.chuckfang.com"
|
|
20
|
+
# 【可选】请求超时毫秒数,默认 10000
|
|
21
|
+
timeout: 10000
|
|
22
|
+
# 【可选】同一会话内的推送节流窗口毫秒数,默认 5000
|
|
23
|
+
throttleMs: 5000
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# meow-push-bridge / MeoW Push Bridge (喵桥) —— 发布用配置补丁
|
|
2
|
+
#
|
|
3
|
+
# 本文件是 DSH 的「插件配置补丁」(cordis patch)。作为 Bundle 安装后,DSH 的
|
|
4
|
+
# Loader 会把它叠加到 profile 配置层上,插入本插件并注入 config。
|
|
5
|
+
#
|
|
6
|
+
# ⚠️ name 必须是包名(解析到 package.json 的 main → ./dist/index.js)。
|
|
7
|
+
# 源码调试请改用同目录的 cordis.debug.patch.yml(那里 name 为 ./src/index.ts)。
|
|
8
|
+
# 原因:npm 包的 files 白名单只含 dist/,装到 profile 后并没有 src/ 目录。
|
|
9
|
+
#
|
|
10
|
+
# ⚠️ nickname 为必填项。留空时本插件会拒绝注册 meow_push_bridge 工具并打印错误,
|
|
11
|
+
# 这是刻意设计的行为,用于防止误推到未知账号。
|
|
12
|
+
# 修改昵称后,重载插件(保存本文件触发 hmr,或重启 dsh)即可生效。
|
|
13
|
+
# ────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
- insert:
|
|
16
|
+
- id: meow-push-bridge
|
|
17
|
+
name: meow-push-bridge
|
|
18
|
+
config:
|
|
19
|
+
# 【必填】你的 MeoW 用户昵称,对应推送地址 http://api.chuckfang.com/{nickname}/...
|
|
20
|
+
nickname: ""
|
|
21
|
+
# 【可选】MeoW API 基础地址,默认 http://api.chuckfang.com
|
|
22
|
+
apiBase: "http://api.chuckfang.com"
|
|
23
|
+
# 【可选】请求超时毫秒数,默认 10000
|
|
24
|
+
timeout: 10000
|
|
25
|
+
# 【可选】同一会话内的推送节流窗口毫秒数,默认 5000(5 秒内不重复推送)
|
|
26
|
+
throttleMs: 5000
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meow-push-bridge / MeoW Push Bridge (喵桥)
|
|
3
|
+
* src/auto-push.ts —— 宿主侧「每轮必推」的确定性自动推送
|
|
4
|
+
*
|
|
5
|
+
* 为什么需要它(这是本项目最重要的一次设计修正):
|
|
6
|
+
* 只靠 `systemPrompt` 里的约定让**模型自觉调用**工具,是概率性合规 ——
|
|
7
|
+
* 实测同一个模型在不同上下文/任务/会话里会忽略它,于是「部分环境不推」。
|
|
8
|
+
* 本模块改为在宿主侧监听轮次结束事件后**直接推送**,不依赖模型。
|
|
9
|
+
*
|
|
10
|
+
* DSH 契约(来自运行时的服务/事件目录,非推断):
|
|
11
|
+
* - 事件 `'agent/turn-stopping'(this: Scoped<Agent>, payload: { agent, turn, signal })`
|
|
12
|
+
* —— 「本轮即将结束」的串行事件,全局注册的插件可以收到。
|
|
13
|
+
* - 事件 `'session/event'(this: Scoped<Session>, session, event)`
|
|
14
|
+
* —— 会话事件的提交后观察流,用于统计「本轮用了哪些工具」。
|
|
15
|
+
* 注意它带 carrier(作用域过滤),所以统计是**尽力而为**:
|
|
16
|
+
* 拿不到统计时退化为简洁文案,但**轮次结束的推送照常发出**。
|
|
17
|
+
*
|
|
18
|
+
* 设计取舍:
|
|
19
|
+
* - 推送**不经过工具**,因此不受工具级 5 秒节流限制(否则每轮必推做不到);
|
|
20
|
+
* 改为按 (会话, 轮次) 去重,保证同一轮只推一条。
|
|
21
|
+
* - 模型自己推的那条排在前面(它在轮内调用),宿主这条在轮末补位,符合
|
|
22
|
+
* 「统计式描述放在最后」的预期。
|
|
23
|
+
*/
|
|
24
|
+
import { type PushOutcome } from './meow-api.js';
|
|
25
|
+
/** 自动推送模式 */
|
|
26
|
+
export type PushMode = 'every-turn' | 'substantial' | 'off';
|
|
27
|
+
/** 所有合法模式(供配置归一化与测试引用) */
|
|
28
|
+
export declare const PUSH_MODES: readonly PushMode[];
|
|
29
|
+
/** 默认模式:每轮必推 */
|
|
30
|
+
export declare const DEFAULT_PUSH_MODE: PushMode;
|
|
31
|
+
/** 与 index.ts 的 PluginContext 对齐的最小上下文(避免循环依赖) */
|
|
32
|
+
export interface AutoPushContext {
|
|
33
|
+
readonly logger?: {
|
|
34
|
+
error?(...args: unknown[]): void;
|
|
35
|
+
warn?(...args: unknown[]): void;
|
|
36
|
+
info?(...args: unknown[]): void;
|
|
37
|
+
};
|
|
38
|
+
/** 事件订阅:Cordis 上下文成员 */
|
|
39
|
+
on?(event: string, listener: (...args: unknown[]) => void, options?: Record<string, unknown>): unknown;
|
|
40
|
+
}
|
|
41
|
+
/** 一轮的统计 */
|
|
42
|
+
export interface TurnStats {
|
|
43
|
+
turn: number;
|
|
44
|
+
startedAt: number;
|
|
45
|
+
toolCalls: number;
|
|
46
|
+
/** 工具名 → 调用次数 */
|
|
47
|
+
tools: Map<string, number>;
|
|
48
|
+
}
|
|
49
|
+
/** 自动推送的注入项 */
|
|
50
|
+
export interface AutoPushOptions {
|
|
51
|
+
mode: PushMode;
|
|
52
|
+
/** 实际执行推送(由 index.ts 注入,内部会复用工具级节流) */
|
|
53
|
+
send: (title: string, message: string) => Promise<PushOutcome>;
|
|
54
|
+
}
|
|
55
|
+
/** 注册结果 */
|
|
56
|
+
export interface AutoPushRegistration {
|
|
57
|
+
registered: boolean;
|
|
58
|
+
reason?: string;
|
|
59
|
+
}
|
|
60
|
+
/** 把统计渲染成中文文案(用户要求:统计式描述,放在最后) */
|
|
61
|
+
export declare function renderTurnStats(stats: TurnStats, now?: number): string;
|
|
62
|
+
/**
|
|
63
|
+
* 注册宿主侧自动推送。
|
|
64
|
+
*
|
|
65
|
+
* 行为:
|
|
66
|
+
* - `off`:不注册任何监听(只保留模型主动调用)
|
|
67
|
+
* - `every-turn`:每轮回答结束都推
|
|
68
|
+
* - `substantial`:仅当本轮调用过工具才推
|
|
69
|
+
*
|
|
70
|
+
* 失败**不抛错** —— 自动推送是增强项,工具本身必须始终可用。
|
|
71
|
+
*/
|
|
72
|
+
export declare function registerAutoPush(ctx: AutoPushContext, options: AutoPushOptions): AutoPushRegistration;
|
|
73
|
+
/** 便捷函数:归一化 pushMode 配置(非法值回退默认) */
|
|
74
|
+
export declare function normalizePushMode(value: unknown): PushMode;
|
|
75
|
+
//# sourceMappingURL=auto-push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-push.d.ts","sourceRoot":"","sources":["../src/auto-push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAE/D,aAAa;AACb,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,KAAK,CAAA;AAE3D,0BAA0B;AAC1B,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAAyC,CAAA;AAEnF,gBAAgB;AAChB,eAAO,MAAM,iBAAiB,EAAE,QAAuB,CAAA;AAEvD,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QAChC,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;QAC/B,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KAChC,CAAA;IACD,wBAAwB;IACxB,EAAE,CAAC,CACD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,EACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAA;CACX;AAED,YAAY;AACZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3B;AAED,eAAe;AACf,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,CAAA;IACd,uCAAuC;IACvC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;CAC/D;AAED,WAAW;AACX,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAcD,kCAAkC;AAClC,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CASlF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,eAAe,GACvB,oBAAoB,CAgHtB;AAED,oCAAoC;AACpC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAK1D"}
|