dsh-qiyu-stack 0.0.0-stage → 0.15.1
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 +171 -2
- package/bin/qiyu.mjs +165 -0
- package/docs/conventions.md +111 -0
- package/docs/deployment.md +216 -0
- package/docs/settings-tabs.md +114 -0
- package/examples/README.md +18 -0
- package/package.json +45 -6
- package/qiyu.config.example.json +14 -0
- package/runtime/README.md +84 -0
- package/runtime/bridge/CHANGELOG.md +70 -0
- package/runtime/bridge/LICENSE +21 -0
- package/runtime/bridge/README.md +116 -0
- package/runtime/bridge/cordis.patch.yml +4 -0
- package/runtime/bridge/lib/client.js +289 -0
- package/runtime/bridge/lib/host.js +1823 -0
- package/runtime/bridge/lib/hostdeps.mjs +141 -0
- package/runtime/bridge/lib/selfupdate.mjs +151 -0
- package/runtime/bridge/lib/voice.mjs +102 -0
- package/runtime/bridge/package.json +48 -0
- package/runtime/bridge/tools//350/257/255/351/237/263/README.md +111 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//345/220/257/345/212/250-/345/275/225/350/257/255/351/237/263.bat +40 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//345/275/225/350/257/255/351/237/263.mjs +295 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//345/275/225/351/237/263/345/233/236/347/216/257.ps1 +399 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//346/234/227/350/257/273/344/277/241/345/217/267/345/223/250/345/205/265/background.js +6 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//346/234/227/350/257/273/344/277/241/345/217/267/345/223/250/345/205/265/content.js +29 -0
- package/runtime/bridge/tools//350/257/255/351/237/263//346/234/227/350/257/273/344/277/241/345/217/267/345/223/250/345/205/265/manifest.json +25 -0
- package/runtime/memory/LICENSE +21 -0
- package/runtime/memory/README.md +190 -0
- package/runtime/memory/cordis.patch.yml +3 -0
- package/runtime/memory/lib/client.js +3938 -0
- package/runtime/memory/lib/hostdeps.mjs +157 -0
- package/runtime/memory/lib/index.js +5898 -0
- package/runtime/memory/lib/selfupdate.mjs +151 -0
- package/runtime/memory/lib//347/224/237/346/210/220/346/241/243/346/241/210.js +565 -0
- package/runtime/memory/lib//350/257/215/345/205/270-/346/235/245/346/272/220.txt +53 -0
- package/runtime/memory/lib//350/257/215/345/205/270.txt +109750 -0
- package/runtime/memory/lib//350/257/215/351/242/221.txt +20000 -0
- package/runtime/memory/package.json +56 -0
- package/runtime/mood/CHANGELOG.md +271 -0
- package/runtime/mood/LICENSE +21 -0
- package/runtime/mood/README.md +174 -0
- package/runtime/mood/assets/face-good.png +0 -0
- package/runtime/mood/assets/face-great.png +0 -0
- package/runtime/mood/assets/face-ok.png +0 -0
- package/runtime/mood/assets/face-sad.png +0 -0
- package/runtime/mood/assets/face-worry.png +0 -0
- package/runtime/mood/assets/feiyu.png +0 -0
- package/runtime/mood/assets/widget.js +306 -0
- package/runtime/mood/client.js +389 -0
- package/runtime/mood/cordis.patch.yml +3 -0
- package/runtime/mood/lib/index.js +1090 -0
- package/runtime/mood/lib/selfupdate.mjs +151 -0
- package/runtime/mood/package.json +55 -0
- package/runtime/rhythm/CHANGELOG.md +85 -0
- package/runtime/rhythm/README.md +224 -0
- package/runtime/rhythm/cordis.patch.yml +7 -0
- package/runtime/rhythm/lib/client.js +656 -0
- package/runtime/rhythm/lib/host.js +1290 -0
- package/runtime/rhythm/lib/selfupdate.mjs +151 -0
- package/runtime/rhythm/package.json +46 -0
- package/runtime/rhythm/tests//345/256/277/344/270/273/345/215/212/350/276/271.mjs +499 -0
- package/runtime/rhythm/tests//347/275/221/351/241/265/345/215/212/350/276/271.mjs +354 -0
- package/runtime/voice//350/257/255/351/237/263/350/275/254/345/206/231.py +104 -0
- package/src/config.mjs +61 -0
- package/src/deploy.mjs +510 -0
- package/src/docker.mjs +95 -0
- package/src/doctor.mjs +323 -0
- package/src/init.mjs +228 -0
- package/src/install.mjs +414 -0
- package/src/preflight.mjs +412 -0
- package/src/qr.mjs +99 -0
- package/src/wizard.mjs +112 -0
- package/templates/blank/AGENTS.md.tpl +76 -0
- package/templates/blank/character.json +44 -0
- package/templates/blank/schedule.json +18 -0
- package/tools/merge-settings-tabs.mjs +104 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# 部署:把 QQ 这条路一次修对
|
|
2
|
+
|
|
3
|
+
这份文档只讲一件事:**怎么让一个智能体稳定地收发 QQ 消息**。
|
|
4
|
+
读完能省掉我们今天踩过的三个坑。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 结论先说
|
|
9
|
+
|
|
10
|
+
**用 Docker 里的 Linux QQ + NapCat,不要用 Windows 版 NapCat。**
|
|
11
|
+
|
|
12
|
+
Windows 上装 NapCat(OneKey / Shell / 注入 / LiteLoader)全部会失败,原因见下一节。
|
|
13
|
+
Linux QQ 没有那个限制。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 一、为什么 Windows 版一定失败
|
|
18
|
+
|
|
19
|
+
**QQ 新版启动时会校验 `package.json`。**
|
|
20
|
+
|
|
21
|
+
实测(Windows QQ 9.9.36):只往 `resources\app\package.json` 末尾加一个换行(4 字节,内容语义完全不变),QQ 一启动就弹:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
退出
|
|
25
|
+
文件已损坏, 请重新安装QQ。
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
还原原版,QQ 又能正常启动。
|
|
29
|
+
|
|
30
|
+
而 NapCat 在 Windows 上的所有形态,本质都是「让 QQ 的入口指向 NapCat」:
|
|
31
|
+
|
|
32
|
+
| 形态 | 做法 | 结果 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Shell(OneKey / 手动) | 改 `package.json` 的 `main` | QQ 报「文件已损坏」,崩在 `QQNT.dll`(`0x80000003`) |
|
|
35
|
+
| Shell(注入) | 运行时改内存 | 盘上文件没动也秒退(校验发生在加载后) |
|
|
36
|
+
| Framework(LiteLoader) | 同样要改入口 | 按机制推断同样被拦 |
|
|
37
|
+
|
|
38
|
+
> NapCat 补丁包里写的 `9.9.22-40990` 不是随便写的 —— 那是这道校验还没出现的 QQ 版本。
|
|
39
|
+
|
|
40
|
+
**Linux QQ 没有这道校验。**
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 一·五、前提:Docker(没装就卡在这)
|
|
45
|
+
|
|
46
|
+
NapCat 必须跑在 **Docker 里的 Linux QQ** 上。这不是偏好,是唯一可行的路——Windows 版那条被上面的校验堵死了。
|
|
47
|
+
|
|
48
|
+
**没装 Docker 的话:**
|
|
49
|
+
|
|
50
|
+
1. 下载 <https://www.docker.com/products/docker-desktop/>
|
|
51
|
+
2. 安装时勾上 **WSL2** 组件(Windows 需要 WSL2;没有就管理员开 PowerShell 跑 `wsl --install`)
|
|
52
|
+
3. 装完**重启这台电脑**(不是重启 DSH、也不是重启 Docker)
|
|
53
|
+
4. 打开 Docker Desktop,等左下角显示引擎已启动
|
|
54
|
+
5. 回来跑 `qiyu deploy`
|
|
55
|
+
|
|
56
|
+
命令行装法:`winget install -e --id Docker.DockerDesktop`
|
|
57
|
+
|
|
58
|
+
**占用**:Docker 本体 + 镜像大概 3–5 GB,之后数据慢慢长。
|
|
59
|
+
这些默认都在 **C 盘的 Docker 磁盘镜像**里(`Settings → Resources → Disk image location` 可以挪)。
|
|
60
|
+
`qiyu deploy --dir` 管的是宿主机上的配置/登录态/插件,**管不到 Docker 那份**——两处都要留意。
|
|
61
|
+
|
|
62
|
+
> `qiyu deploy` 会先分辨「没装」和「装了没开」,两种情况的提示不一样。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 二、起一个容器(三分钟)
|
|
67
|
+
|
|
68
|
+
### 最省事:一条命令
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx dsh-qiyu-stack deploy --qq 你的QQ号
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
它会依次做:查 Docker → 查 3000/3001/6099 有没有被占 → 建 `./napcat/{config,qq,plugins}`
|
|
75
|
+
→ 写 OneBot + WebUI 配置(令牌随机生成)→ 拉镜像 → 跑容器 → 等 WebUI → **直接把二维码弹到你屏幕上**。
|
|
76
|
+
跑完令牌和容器名会写进 `qiyu.config.json`,接着 `qiyu doctor` 就能用了。
|
|
77
|
+
|
|
78
|
+
加 `--dry-run` 只打印它准备执行的 docker 命令,什么都不动。
|
|
79
|
+
|
|
80
|
+
### 手动版(想自己控制参数时)
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
docker pull mlikiowa/napcat-docker:latest
|
|
84
|
+
|
|
85
|
+
docker run -d --name napcat-qq --restart unless-stopped `
|
|
86
|
+
-p 127.0.0.1:3000:3000 -p 127.0.0.1:3001:3001 -p 127.0.0.1:6099:6099 `
|
|
87
|
+
-e MODE=ws -e ACCOUNT=<你的QQ号> -e WEBUI_TOKEN=<自己定一个> -e TZ=Asia/Shanghai `
|
|
88
|
+
-v "D:\qiyu\napcat\config:/app/napcat/config" `
|
|
89
|
+
-v "D:\qiyu\napcat\qq:/app/.config/QQ" `
|
|
90
|
+
-v "D:\qiyu\napcat\plugins:/app/napcat/plugins" `
|
|
91
|
+
mlikiowa/napcat-docker:latest
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**三个挂载点必须挂**,否则重启就丢:
|
|
95
|
+
|
|
96
|
+
| 容器里 | 放什么 |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `/app/napcat/config` | OneBot 配置(`onebot11_<QQ号>.json`)、WebUI 配置 |
|
|
99
|
+
| `/app/.config/QQ` | QQ 登录态、消息数据库 |
|
|
100
|
+
| `/app/napcat/plugins` | 插件(不用可空)
|
|
101
|
+
|
|
102
|
+
### OneBot 配置
|
|
103
|
+
|
|
104
|
+
写进 `napcat/config/onebot11_<QQ号>.json`(或模板 `onebot11.json`):
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"network": {
|
|
109
|
+
"httpServers": [
|
|
110
|
+
{ "name": "http-default", "enable": true, "host": "0.0.0.0", "port": 3000,
|
|
111
|
+
"enableCors": true, "messagePostFormat": "array", "token": "你的HTTP令牌" }
|
|
112
|
+
],
|
|
113
|
+
"websocketServers": [
|
|
114
|
+
{ "name": "ws-default", "enable": true, "host": "0.0.0.0", "port": 3001,
|
|
115
|
+
"messagePostFormat": "array", "token": "你的WS令牌", "heartInterval": 30000 }
|
|
116
|
+
],
|
|
117
|
+
"httpClients": [], "websocketClients": [], "httpSseServers": [], "plugins": []
|
|
118
|
+
},
|
|
119
|
+
"musicSignUrl": "",
|
|
120
|
+
"enableLocalFile2Url": false,
|
|
121
|
+
"parseMultMsg": false
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> 同时提供 **HTTP 服务端**(谁来调都行)和 **正向 WS 服务端**(事件流),
|
|
126
|
+
> 上层框架按 OneBot 11 接就行,接口跟 SnowLuma 那种实现是通用的。
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 三、登录:二维码,以及一个必须勾的框
|
|
131
|
+
|
|
132
|
+
1. 跑 `qiyu qr` —— 二维码会**直接在你屏幕上打开**(同时终端里会说它几秒前生成的、大概什么时候失效)
|
|
133
|
+
- 已经登录了它会直接告诉你「不用扫」,不会拿一张过期的码糊弄你
|
|
134
|
+
- 也可以手动开 `http://127.0.0.1:6099/webui?token=<你定的 WEBUI_TOKEN>`,页面里是实时码
|
|
135
|
+
2. 用**目标账号**的手机 QQ 扫
|
|
136
|
+
3. ⚠️ **手机上确认时,勾「下次登录无需确认」**
|
|
137
|
+
|
|
138
|
+
第 3 步是关键。不勾的话每次容器重启都会要求「手Q验证」,等于每次都要人工点一下;
|
|
139
|
+
勾了之后 `docker restart` 能自动快速登录,免扫码:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
09:24:11 正在快速登录 <你的QQ号>
|
|
143
|
+
09:24:42 ✅ 已上线(coreReady=true)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**别用 `docker rm -f` 再 `docker run`** —— 换容器等于换设备,登录态失效,要重扫。
|
|
147
|
+
要重启**容器**用 `docker restart <容器名>`(不用重启电脑)。
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 四、要它自己起来,但别让用户看见
|
|
152
|
+
|
|
153
|
+
2026-10-23 改过一次:**不再开机自启,改成「用的时候才起来,起来也不弹窗」**。
|
|
154
|
+
|
|
155
|
+
### 为什么不要开机自启
|
|
156
|
+
|
|
157
|
+
用户根本不知道 Docker 是什么。开机时冒出一个窗口、任务栏多一个图标,只会让人困惑。
|
|
158
|
+
而且开机自启会把 Docker 常驻在后台,白占内存。
|
|
159
|
+
|
|
160
|
+
### 现在的做法
|
|
161
|
+
|
|
162
|
+
| 时机 | 谁来做 |
|
|
163
|
+
|---|---|
|
|
164
|
+
| 打开 DSH / qiyu | **QQ 桥在启动 5 秒后检查**:引擎不在就静默拉起(无窗口、无任务栏按钮),容器靠 `--restart unless-stopped` 自己回来 |
|
|
165
|
+
| `qiyu deploy` | 同样先查再拉,已经在跑就立刻跳过 |
|
|
166
|
+
| 电脑开机 | **什么都不做**(启动文件夹里的 `启动-NapCat.vbs` 已停用,Docker 的 `AutoStart` 也关着) |
|
|
167
|
+
|
|
168
|
+
### 关键的一条(这是弹窗的真正原因)
|
|
169
|
+
|
|
170
|
+
> **`Docker Desktop.exe` 在引擎已经在跑的时候再执行一次,会 `signaling show-dashboard`**
|
|
171
|
+
> —— 也就是用户看到一个莫名其妙弹出来的窗口。实测日志:`backend already running, signaling show-dashboard`。
|
|
172
|
+
|
|
173
|
+
所以代码里**必须先查引擎,再决定要不要碰那个 exe**。这一步已经写进:
|
|
174
|
+
- `runtime/bridge/lib/host.js` 的 `后台服务在线()` / `拉起后台服务()`
|
|
175
|
+
- `src/docker.mjs` 的 `确保引擎()`
|
|
176
|
+
|
|
177
|
+
### 剩下的唯一痕迹
|
|
178
|
+
|
|
179
|
+
Docker Desktop 启动后会在**右下角托盘**留一个小鲸鱼图标。这是它的固有行为,**没有关闭选项**。
|
|
180
|
+
但它不占任务栏、不弹窗口——用户平时看不见它。
|
|
181
|
+
|
|
182
|
+
想让启动更安静,设置里这两项要这样:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
// %APPDATA%\Docker\settings-store.json
|
|
186
|
+
"AutoStart": false, // 不开机自启
|
|
187
|
+
"OpenUIOnStartupDisabled": true // 启动时不弹面板
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`qiyu` 每次拉起引擎前会顺手把 `OpenUIOnStartupDisabled` 写一遍(写不进也不影响启动)。
|
|
191
|
+
|
|
192
|
+
> 容器起来的那 20~30 秒里,QQ 是收不到消息的;桥会在引擎好后自动重连,不用手动做任何事。
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 五、端口和冲突
|
|
197
|
+
|
|
198
|
+
| 端口 | 用途 |
|
|
199
|
+
|---|---|
|
|
200
|
+
| 3000 | OneBot HTTP |
|
|
201
|
+
| 3001 | OneBot 正向 WS |
|
|
202
|
+
| 6099 | NapCat WebUI |
|
|
203
|
+
|
|
204
|
+
**同一个号只能在一个地方登录**。如果机器上还有别的 QQ 机器人(比如 SnowLuma),先停掉,
|
|
205
|
+
否则两边抢 3000/3001,或者互相把对方挤下线。
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 六、自查
|
|
210
|
+
|
|
211
|
+
```powershell
|
|
212
|
+
qiyu doctor
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
会逐项检查:Docker、容器、三个端口、OneBot 身份、事件流、DSH 桥,
|
|
216
|
+
每项失败都带一句「怎么修」。
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# 把插件设置合并成一个页面 + 页签
|
|
2
|
+
|
|
3
|
+
装了几个插件之后,DSH 设置左侧栏会变成这样:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
账号与余额
|
|
7
|
+
通用设置
|
|
8
|
+
模型
|
|
9
|
+
内置插件
|
|
10
|
+
Agent 预设
|
|
11
|
+
刷 B 站 ← 每个插件各占一项
|
|
12
|
+
鱼的心情
|
|
13
|
+
鱼的记忆
|
|
14
|
+
主动搭话
|
|
15
|
+
通到 QQ
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
想要的是:**只有「内置插件」一项,点进去是页签**:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
内置插件
|
|
22
|
+
├─ 全部
|
|
23
|
+
├─ 刷 B 站
|
|
24
|
+
├─ 鱼的心情
|
|
25
|
+
├─ 鱼的记忆
|
|
26
|
+
├─ 主动搭话
|
|
27
|
+
└─ 通到 QQ
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 结论:DSH 本来就有这个槽位,不用自己造
|
|
31
|
+
|
|
32
|
+
在 DSH 的插件契约里(`dsh-cordis-client-runner`,slot 定义 `settings.plugins.tab`):
|
|
33
|
+
|
|
34
|
+
> One page inside the Plugins settings section.
|
|
35
|
+
> The section owner renders localized entry labels as tabs
|
|
36
|
+
> and mounts each contribution inside its corresponding tab panel.
|
|
37
|
+
> Options: `id`(页签 key)、`order`(顺序)、`label`(显示名)。
|
|
38
|
+
|
|
39
|
+
所以插件只要把注册目标从 `settings.section` 换成 `settings.plugins.tab`,
|
|
40
|
+
就会从左侧栏消失、变成插件页里的一个页签。**不改组件、不改样式、不写新面板。**
|
|
41
|
+
|
|
42
|
+
## 改法
|
|
43
|
+
|
|
44
|
+
一个插件只需要动两处字符串(都在它自己的 `client.js` 里):
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
// 改前
|
|
48
|
+
slots.inject('settings.section', () =>
|
|
49
|
+
slots.register(
|
|
50
|
+
{ name: 'settings.section', id: 'qq-bridge', order: 54, label: () => '通到 QQ' },
|
|
51
|
+
Section,
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// 改后
|
|
56
|
+
slots.inject('settings.plugins.tab', () =>
|
|
57
|
+
slots.register(
|
|
58
|
+
{ name: 'settings.plugins.tab', id: 'qq-bridge', order: 54, label: () => '通到 QQ' },
|
|
59
|
+
Section,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`order` 决定页签顺序(小的在前)。
|
|
65
|
+
|
|
66
|
+
## 用脚本批量做
|
|
67
|
+
|
|
68
|
+
栖语栈里带了一个脚本:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# 先看看有哪些插件会被改
|
|
72
|
+
node tools/merge-settings-tabs.mjs --dry-run
|
|
73
|
+
|
|
74
|
+
# 真改(每个文件自动备份成 client.js.bak-tab-<时间戳>)
|
|
75
|
+
node tools/merge-settings-tabs.mjs
|
|
76
|
+
|
|
77
|
+
# 想还原
|
|
78
|
+
node tools/merge-settings-tabs.mjs --restore
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
默认目录是 `~/.dsh/profiles/desktop/node_modules`,换 profile 用 `--dir`。
|
|
82
|
+
|
|
83
|
+
## 生效方式
|
|
84
|
+
|
|
85
|
+
**刷新页面就行(Ctrl+R)**,不用重启 DSH。
|
|
86
|
+
|
|
87
|
+
原因:DSH 给客户端 bundle 的 URL 带了 `rev`,而 `rev` 是按**文件 mtime** 算的
|
|
88
|
+
(见 `dsh-client-modules` 的 `String(baseline.mtimeMs)`),所以文件一改、版本号就变,
|
|
89
|
+
页面刷新会拉到新包。
|
|
90
|
+
|
|
91
|
+
如果刷新没变化,再试 `Ctrl+Shift+R` 硬刷新;仍然不行才需要重启 DSH。
|
|
92
|
+
|
|
93
|
+
## 踩过的坑
|
|
94
|
+
|
|
95
|
+
1. **注释里也有 `settings.section`** —— 批量替换会把注释一起换掉,无害,但看到别奇怪。
|
|
96
|
+
2. **跨行写法** —— 有的插件是
|
|
97
|
+
```js
|
|
98
|
+
{
|
|
99
|
+
name: 'settings.section',
|
|
100
|
+
id: 'memory',
|
|
101
|
+
...
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
所以别按「整行」去替换,要替换**引号里的字面量**。脚本就是这么做的。
|
|
105
|
+
3. **改完先 `node --check`** —— 这些 client.js 是打包好的字符串,替换坏了会整个设置页白屏。
|
|
106
|
+
4. **每个插件都要备份** —— 出问题能一条命令还原(脚本的 `--restore`)。
|
|
107
|
+
|
|
108
|
+
## 适用边界
|
|
109
|
+
|
|
110
|
+
- 这些插件得是**独立的 client bundle**(有自己的 `client.js`)。宿主内置的 section
|
|
111
|
+
(账号、通用、模型)不该动。
|
|
112
|
+
- 如果你是插件作者,新插件**一开始就注册到 `settings.plugins.tab`**,别占左侧栏。
|
|
113
|
+
- 想做「一个『栖语』页 + 自己的页签容器」也可以,但要新写一个 section 插件当容器,
|
|
114
|
+
再把各组件的注册目标指过去 —— 工作量大得多,收益只是名字和位置更整齐。
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# examples/
|
|
2
|
+
|
|
3
|
+
样板角色放这里,**默认不安装**,只是让人看看「能长成什么样」。
|
|
4
|
+
|
|
5
|
+
暂时是空的。
|
|
6
|
+
|
|
7
|
+
以后会把「云千语」那套整理一份放进来——去掉所有隐私(聊天记录、令牌、那封私人信件),
|
|
8
|
+
只保留结构和写法,比如:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
examples/qianyu/
|
|
12
|
+
├── AGENTS.md 人格长什么样(行为 + 性格都写满的那种)
|
|
13
|
+
├── schedule.json 三个时段怎么排
|
|
14
|
+
├── letters/ 写给她的信(示例,内容换成假的)
|
|
15
|
+
└── README.md 为什么这么写
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
在它出现之前,先用 `templates/blank` 起步,性格那几行自己填。
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-qiyu-stack",
|
|
3
|
+
"version": "0.15.1",
|
|
4
|
+
"description": "栖语栈:让一个智能体住进 QQ —— QQ 双向桥、长期记忆、心情、语音、作息,以及把 NapCat 部署这件事一次做对",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"qiyu": "./bin/qiyu.mjs"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": "\u003e=20"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"doctor": "node bin/qiyu.mjs doctor",
|
|
14
|
+
"init": "node bin/qiyu.mjs init"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"src",
|
|
20
|
+
"templates",
|
|
21
|
+
"docs",
|
|
22
|
+
"examples",
|
|
23
|
+
"qiyu.config.example.json",
|
|
24
|
+
"tools",
|
|
25
|
+
"runtime"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/aesfls/dsh-qiyu-stack.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/aesfls/dsh-qiyu-stack#readme",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/aesfls/dsh-qiyu-stack/issues"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"qq",
|
|
37
|
+
"qqbot",
|
|
38
|
+
"napcat",
|
|
39
|
+
"dsh",
|
|
40
|
+
"deepseek-harness",
|
|
41
|
+
"agent",
|
|
42
|
+
"persona",
|
|
43
|
+
"onebot"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# runtime/ —— 运行时五块
|
|
2
|
+
|
|
3
|
+
这里放「一个角色真正活着需要的代码」:QQ 桥、记忆、心情、语音、作息。
|
|
4
|
+
|
|
5
|
+
**来源**:全部是从一台真在跑的机器上(`~/.dsh/profiles/desktop/node_modules/`)原样搬过来的,
|
|
6
|
+
不是照文档重写的。搬完做了「去个性化」:私人路径、角色名、QQ 号、令牌,一个不留。
|
|
7
|
+
|
|
8
|
+
| 块 | 来源插件 | 体积 | 状态 |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| `bridge/` | dsh-qq-bridge 0.2.0 | 155 KB | ✅ 已搬 + 去个性化 |
|
|
11
|
+
| `memory/` | dsh-fish-memory | 1.8 MB | ✅ 已搬(含 1 MB 中文词典,分词用) |
|
|
12
|
+
| `mood/` | dsh-feiyu-mood | 500 KB | ✅ 已搬(含五张表情图) |
|
|
13
|
+
| `rhythm/` | dsh-speak-up 0.2.0 | 119 KB | ✅ 已搬 |
|
|
14
|
+
| `voice/` | 语音转写.py + `bridge/lib/voice.mjs` | 3.4 KB + 4 KB | ✅ 说/听都抽出来了 |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 各块现在是什么状态
|
|
19
|
+
|
|
20
|
+
### bridge/(QQ 双向)
|
|
21
|
+
|
|
22
|
+
已经做成**全配置驱动**的了(今天改的):
|
|
23
|
+
|
|
24
|
+
| 原来写死 | 现在 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `uid === 10001` | `配置.自己QQ`(0 = 自己问 NapCat 要) |
|
|
27
|
+
| `备注.startsWith('爸爸')` | `配置.身份表`(备注前缀 → 称呼,随便加人换叫法) |
|
|
28
|
+
| 出事找 `爸爸` | `配置.主人` |
|
|
29
|
+
| `转写脚本: 'W:\...\语音转写.py'` | `配置.转写脚本`(空 = 不转) |
|
|
30
|
+
| `PYTHONPATH: 'W:\...\pylibs'` | `配置.转写依赖路径` |
|
|
31
|
+
| 表情包/收件箱路径 | `~/.qiyu/表情包`、`~/.qiyu/收件箱`(可用环境变量覆盖) |
|
|
32
|
+
| 音色默认 `zh-CN-XiaoyiNeural` | `zh-CN-XiaoxiaoNeural`(中性) |
|
|
33
|
+
|
|
34
|
+
还留着 `爸爸`/`妈妈` 这两个词的地方,是**默认身份表**和工具说明里的举例
|
|
35
|
+
("给通讯录里的人发消息,比如爸爸、妈妈")。它们是通用亲属称谓、不是某个人的名字,
|
|
36
|
+
而且真认人走的是 `身份表` 配置,改一行就换。
|
|
37
|
+
|
|
38
|
+
### voice/(说 + 听)
|
|
39
|
+
|
|
40
|
+
语音原来是贴在 bridge 里的,现在拆成两块:
|
|
41
|
+
|
|
42
|
+
| 半 | 在哪 | 干什么 |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| **说**(TTS) | `bridge/lib/voice.mjs` 的 `合成语音()` | edge-tts(联网、自然)或 Windows SAPI(离线、机械) |
|
|
45
|
+
| **听**(ASR) | `bridge/lib/voice.mjs` 的 `转写音频()` + `voice/语音转写.py` | 本地脚本收 wav、往 stdout 打文字(SenseVoice 之类) |
|
|
46
|
+
| **网络那半** | 还在 bridge 里 | 向 NapCat 要 `get_record`(base64 wav),再交给上面两个 |
|
|
47
|
+
|
|
48
|
+
边界是这样切的:**voice 只管本地能力(本机的 python、本机的模型),bridge 只管网络和消息。**
|
|
49
|
+
所以 `转写语音()` 现在只剩「取 → 存 → 转」三行调用。
|
|
50
|
+
|
|
51
|
+
`qiyu install` 会顺手把 `语音转写.py` 放到 `~/.qiyu/语音转写.py`,
|
|
52
|
+
那正是 `voice.mjs` 的默认路径 —— 不配也能用。
|
|
53
|
+
|
|
54
|
+
实测:用抽出来的 `合成语音()` 单独跑一次,成功生成了 21 KB 的 mp3。
|
|
55
|
+
|
|
56
|
+
### memory/ 和 mood/、rhythm/
|
|
57
|
+
|
|
58
|
+
搬过来是干净的,也**没改过一行代码**。它们原本就没有私人值。
|
|
59
|
+
|
|
60
|
+
`memory` 那 1 MB 的 `词典.txt` 是分词用的,别删。
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 还没做的
|
|
65
|
+
|
|
66
|
+
- [x] ~~`qiyu install`~~ —— **做好了**:拷 node_modules + 写 profile package.json + 写 bundles,
|
|
67
|
+
默认只列清单,`--yes` 才动手,冲突跳过,改 package.json 前必留 `.bak`
|
|
68
|
+
- [x] ~~依赖路径悬空~~ —— `file:` 不指向 qiyu 包本身,而是先落一份稳定副本到
|
|
69
|
+
`~/.dsh/qiyu/runtime/`;npx 缓存清掉也不影响
|
|
70
|
+
- [x] ~~voice 的胶水~~ —— 抽成 `bridge/lib/voice.mjs`(说/听),网络那半留在桥里
|
|
71
|
+
- [x] ~~配置收敛~~ —— `install --character` 把角色包拷到 `~/.dsh/qiyu/`,bridge 读身份的顺序是
|
|
72
|
+
`character.json` > `qq-bridge-config.json` > 默认值(memory/mood/rhythm 还没接)
|
|
73
|
+
- [x] ~~`memory` / `mood` / `rhythm` 也改读活动角色~~ —— 三块都接上了:
|
|
74
|
+
`rhythm` 读 `schedule.json` 的时段、`mood` 读 `名字`/`称呼.主人`、`memory` 的名字兜底读 `名字`
|
|
75
|
+
- [x] ~~端到端~~ —— **在一个全新 profile 上真跑过**(2026-10-23 11:38):
|
|
76
|
+
空白 profile → `qiyu install` → 起独立 DSH → 桥接上备号 → 事件流连上
|
|
77
|
+
→ 往群里发了一条 ✅ → 群里 @ 她,桥把话转进了会话 ✅
|
|
78
|
+
(最后「她回话」那一步没验证:测试 profile 没配默认模型,那一轮跑不起来)
|
|
79
|
+
|
|
80
|
+
## 为什么先搬到这儿、先不装
|
|
81
|
+
|
|
82
|
+
因为**装**是有副作用的动作(改 profile、可能撞上已经装着的同名插件)。
|
|
83
|
+
先把源码收进来、把私人值洗干净,是这一阶段能确定做完的事;
|
|
84
|
+
装的部分单独做 `qiyu install`,带 `--dry-run` 和备份,才有资格碰别人的 profile。
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# 改动记录
|
|
2
|
+
|
|
3
|
+
> **版本号规矩**:每改一次 **+0.0.1**;末位满 10 **进位**(`0.1.9` → `0.2.0`)。
|
|
4
|
+
> 版本号只写一处真源:`package.json`;提交信息也以版本号开头。
|
|
5
|
+
|
|
6
|
+
## 0.1.5
|
|
7
|
+
|
|
8
|
+
**修:目标会话清单里混着「已归档」的会话;README 补上 QQ 后端怎么装**
|
|
9
|
+
|
|
10
|
+
- 🐛 归档只是把会话藏起来,`sessions.list()` 里**还在**;原来只调 list(),
|
|
11
|
+
于是设置页里把已归档的也列出来了(这台机器上有 40 个),选了也没用。
|
|
12
|
+
- ✅ `inject` 加 `workspaceRegistry`,从 `archivedSessionIds` 取归档名单(照朗读桥的写法),清单里直接过滤。
|
|
13
|
+
- ✅ 发送时也检查:盯的会话如果已经归档,这轮不转,日志写明。
|
|
14
|
+
- 📄 README:新增「装哪个 QQ 后端」——**NapCat(已验证)/ SnowLuma(同接口,未实测)**,
|
|
15
|
+
列出插件实际用到的 6 个 OneBot 11 接口,换后端时照着核对。
|
|
16
|
+
- 📄 修掉 package.json 和设置页里"先只做了 DSH → QQ 这半"的旧说法(两半都做完了)。
|
|
17
|
+
|
|
18
|
+
## 0.1.4
|
|
19
|
+
|
|
20
|
+
**`QQ → DSH` 这半彻底搬进插件,不再赖 BOT**
|
|
21
|
+
|
|
22
|
+
- 插件每 4 秒轮询 NapCat 的 HTTP 历史接口(`get_friend_msg_history` / `get_group_msg_history`),
|
|
23
|
+
发现她新发的私聊/群消息 → 直接 `followup` 进目标会话。
|
|
24
|
+
- 只认 `私聊QQ` 这个人发的;自己的回话(self 消息)不会自我繁殖成死循环。
|
|
25
|
+
- 靠 `message_id` + 时间戳去重,同一条不重复转;总开关关 / 没选会话时只推进时间、不处理旧消息。
|
|
26
|
+
- 结论:**这台桌面只要 NapCat + 这个插件,BOT 可以完全不要了。**
|
|
27
|
+
|
|
28
|
+
## 0.1.3
|
|
29
|
+
|
|
30
|
+
**QQ→DSH 这半的 BOT 半场也接上了**(改在 Estrella 那边,不在本仓库)
|
|
31
|
+
|
|
32
|
+
- Estrella(BOT)加了 `custom.qqBridge` 开关(**默认关**):打开后,私聊/群里**她本人**说的话
|
|
33
|
+
由 BOT 转发到本插件的 `/receive`,然后 BOT **闭嘴不回**,全交给 DSH 里那位伙伴。
|
|
34
|
+
- 兜底:转成功 → BOT 不回;转失败(DSH 没开 / 连不上)→ BOT **照常回**(桥断 ≠ 鱼哑)。
|
|
35
|
+
- 本仓库只是把这件事写进 README(见「怎么把 BOT 接上」);运行入口仍是 0.1.2 的 `/receive`。
|
|
36
|
+
|
|
37
|
+
## 0.1.2
|
|
38
|
+
|
|
39
|
+
**加了「QQ → DSH」这半的入口**(`/receive` 路由)
|
|
40
|
+
|
|
41
|
+
- BOT 把她说的话 POST 到 `/dsh-qq-bridge/receive`,这里用 `agent.followup(手搓 user 消息)` 走 DSH 自己的回合。
|
|
42
|
+
- 消息 `source.kind = 'qq'`(不冒充她亲手在 DSH 里打的)。
|
|
43
|
+
- 闸门:没选目标会话 / 总开关关 / 会话没活着 / 空 text 都拒绝,并回一句人话。
|
|
44
|
+
- BOT 那边(Estrella 改动)要在这半之后再接上。
|
|
45
|
+
|
|
46
|
+
## 0.1.1
|
|
47
|
+
|
|
48
|
+
**加了设置页(网页半边)**
|
|
49
|
+
|
|
50
|
+
- 设置 → 「通到 QQ」:总开关 / 试运行 / 目标会话(**下拉选,不手填**)/ 私聊 QQ 号 / 群号 /
|
|
51
|
+
转私聊 / 转群 / 「测试一句」/ 保存设置,外加一份最近记录。
|
|
52
|
+
- 页面顶上显示「连着的 QQ 是哪个号、昵称」(走 NapCat `/get_login_info` 探活)。
|
|
53
|
+
- 有改动时,底下照样浮出「⚠ 有改动还没保存 + 保存设置」条(照「主动搭话」那套)。
|
|
54
|
+
|
|
55
|
+
## 0.1.0
|
|
56
|
+
|
|
57
|
+
**上半场:DSH → QQ(宿主)**
|
|
58
|
+
|
|
59
|
+
- 盯「目标会话」,伙伴**一整轮最后一句**说完,自动发到 QQ 私聊/群。
|
|
60
|
+
- 走 NapCat OneBot HTTP(`127.0.0.1:3000`),**不经过 Estrella(BOT鱼)**。
|
|
61
|
+
- 安全默认:总开关关、试运行开。
|
|
62
|
+
- 多步只发最后一步;`interrupted` / `reason != completed` 不转发;只认目标会话。
|
|
63
|
+
- 附带 `/state` `/save` `/test-send` 三条路由 + 宿主半边冒烟测试。## 0.1.7
|
|
64
|
+
|
|
65
|
+
**文案通用化:不再出现作者自己的称呼;两个 QQ 后端都标为已测**
|
|
66
|
+
|
|
67
|
+
- 说明和设置页里的「大肥鱼 × 小鱿鱼」全部改成「你自己要的那一间」——别人装了看得懂。
|
|
68
|
+
- 后端表更正:**SnowLuma 和 NapCat 都是测过的**(之前把 NapCat 写成"未实测",是写错了)。
|
|
69
|
+
- 设置页文案里误留的 markdown 星号(`**一整轮**`)去掉,免得原样显示成星号。
|
|
70
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuuyuko-uu
|
|
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.
|