weflow-cli 1.17.0 → 1.19.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/CHANGELOG.md +46 -0
- package/README.md +3 -0
- package/bin/weflow-cli.ts +167 -0
- package/dist/bin/weflow-cli.js +199 -0
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/src/services/diagnosticReport.d.ts +6 -0
- package/dist/src/services/diagnosticReport.d.ts.map +1 -1
- package/dist/src/services/diagnosticReport.js +16 -0
- package/dist/src/services/diagnosticReport.js.map +1 -1
- package/dist/src/services/wechatCompat.d.ts +29 -0
- package/dist/src/services/wechatCompat.d.ts.map +1 -0
- package/dist/src/services/wechatCompat.js +65 -0
- package/dist/src/services/wechatCompat.js.map +1 -0
- package/docs/DATA_CONTRACT.md +25 -25
- package/docs/DECISIONS.md +26 -0
- package/docs/FUNCTIONAL-IMPROVEMENT-SPEC.md +365 -365
- package/docs/HEALTH-CHECK.md +104 -104
- package/docs/JEV/345/272/224/347/224/250/346/226/275/345/267/245/346/226/207/346/241/243.md +310 -310
- package/docs/NEXT_ACTIONS.md +236 -236
- package/docs/NPM-PUBLISH.md +130 -130
- package/docs/NT_DATABASES.md +47 -0
- package/docs/POSITIONING.md +68 -68
- package/docs/PROJECT_STATE.md +3 -2
- package/docs/RELEASING.md +4 -0
- package/package.json +1 -1
- package/requirements-3x.txt +4 -4
- package/requirements-voice.txt +15 -15
- package/resources/js/graph3d/NOTICE.txt +35 -35
- package/resources/js/graph3d/d3-binarytree.min.js +2 -2
- package/resources/js/graph3d/d3-dispatch.min.js +2 -2
- package/resources/js/graph3d/d3-force-3d.min.js +2 -2
- package/resources/js/graph3d/d3-octree.min.js +2 -2
- package/resources/js/graph3d/d3-quadtree.min.js +2 -2
- package/resources/js/graph3d/d3-timer.min.js +2 -2
- package/resources/js/graph3d/three.min.js +6 -6
- package/resources/panel/quick-menu.cjs +197 -197
- package/scripts/_utils.py +1338 -1338
- package/scripts/annual_report.py +3 -1
- package/scripts/article_notes.py +423 -423
- package/scripts/contact_schema.py +339 -339
- package/scripts/create_reading_notes.py +11 -11
- package/scripts/daily_mail.py +51 -5
- package/scripts/daily_task.ps1 +216 -213
- package/scripts/extract_todos.py +3 -1
- package/scripts/fav_notes.py +321 -321
- package/scripts/health_check.py +473 -473
- package/scripts/imported_chats.py +68 -68
- package/scripts/install_tasks.ps1 +105 -105
- package/scripts/key_cache.py +232 -0
- package/scripts/leads.py +179 -179
- package/scripts/mcp_bridge.py +3 -1
- package/scripts/media_index.py +150 -0
- package/scripts/network.py +206 -206
- package/scripts/nt_common.py +32 -0
- package/scripts/nt_decrypt.py +2023 -1954
- package/scripts/opinion_timeline.py +138 -138
- package/scripts/replay.py +803 -803
- package/scripts/run_hidden.vbs +24 -24
- package/scripts/semantic_search.py +5 -1
- package/scripts/user_notes.py +315 -315
- package/scripts/voice_transcribe.py +139 -139
- package/scripts/watch_issues.py +212 -212
- package/scripts/watch_mail.py +131 -131
- package/scripts/wechat_avatar.py +185 -0
- package/scripts/wechat_ledger.py +190 -0
- package/src/services/cursorProbe.ts +222 -222
- package/src/services/diagnosticReport.ts +22 -0
- package/src/services/qchatImport.ts +327 -327
- package/src/services/setupVenv.ts +238 -238
- package/src/services/syncService.ts +363 -363
- package/src/services/syncState.ts +275 -275
- package/src/services/wechatCompat.ts +67 -0
- package/src/services/wechatMedia.ts +166 -166
package/docs/NPM-PUBLISH.md
CHANGED
|
@@ -1,130 +1,130 @@
|
|
|
1
|
-
# npm 发布 — 速查与故障对照
|
|
2
|
-
|
|
3
|
-
只讲 npm 这一侧。**流程清单**在 [RELEASING.md](RELEASING.md),这里只解决
|
|
4
|
-
「命令跑完了,现在到底算成功没有」。
|
|
5
|
-
|
|
6
|
-
单个环节按需查,不必通读。
|
|
7
|
-
|
|
8
|
-
## 0. 省上下文(先看这条)
|
|
9
|
-
|
|
10
|
-
发布命令会打印几百行 `Tarball Contents`。**永远加过滤**,否则整个文件清单
|
|
11
|
-
会灌进上下文,既没用又挤掉真正要看的信息:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm publish --registry=https://registry.npmjs.org 2>&1 \
|
|
15
|
-
| grep -vE "^npm notice [0-9.]+[kMB]+ |^npm notice [0-9.]+B " | tail -25
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
`npm pack --dry-run` 同理。要看文件清单时**输出到文件再 grep**,不要直接打印:
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm pack --dry-run 2>&1 | grep "npm notice [0-9]" > /tmp/packlist.txt
|
|
22
|
-
grep -c " dist/" /tmp/packlist.txt # 注意: 行首是体积, 不是路径
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## 1. 唯一可靠的判据
|
|
26
|
-
|
|
27
|
-
**别信命令的输出,信 registry 的 `versions` 表:**
|
|
28
|
-
|
|
29
|
-
下面用 `X.Y.Z` 代表这次要发的版本,**照抄时换成实际版本号**——写死在这里的版本
|
|
30
|
-
过几天就是错的。
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
V=X.Y.Z
|
|
34
|
-
curl -s "https://registry.npmjs.org/weflow-cli?nocache=$RANDOM" --max-time 30 \
|
|
35
|
-
| python -c "
|
|
36
|
-
import json,sys,os
|
|
37
|
-
d=json.load(sys.stdin)
|
|
38
|
-
print('latest:', d['dist-tags']['latest'])
|
|
39
|
-
print('目标版本存在:', os.environ['V'] in d['versions'])" V="$V"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
- `目标版本存在: True` → **发布成功**,剩下只是传播
|
|
43
|
-
- `False` → 没发出去,看第 2 节
|
|
44
|
-
|
|
45
|
-
`npm view` 会读到客户端缓存,`--prefer-online` 或上面的 `nocache` 才准。
|
|
46
|
-
|
|
47
|
-
## 2. 症状 → 含义 → 动作
|
|
48
|
-
|
|
49
|
-
| 现象 | 含义 | 动作 |
|
|
50
|
-
| --- | --- | --- |
|
|
51
|
-
| `+ weflow-cli@X.Y.Z` | 已被接受 | 继续,等传播 |
|
|
52
|
-
| `being processed and may take a few minutes` | 正常提示 | 等,别重发 |
|
|
53
|
-
| `npm view` 仍是旧版本 | CDN 传播中 | **不是失败**,等 2–5 分钟 |
|
|
54
|
-
| 重发报 `409 Conflict - Cannot publish over previously staged version` | **已经发布成功了** | 别再发,去等传播 |
|
|
55
|
-
| tarball `404`(`/-/weflow-cli-X.Y.Z.tgz`) | 元数据已发布,tarball 还在传播 | 等,不是失败 |
|
|
56
|
-
| `install` 报 `ETARGET No matching version` | 客户端缓存了旧 packument | 加 `--prefer-online` 重试 |
|
|
57
|
-
| `install` 报 `404` | 元数据在、tarball 未到 | 等传播 |
|
|
58
|
-
| `403 ... bypass 2fa enabled is required` | token 没勾 Bypass 2FA | 重新生成 token(见第 3 节) |
|
|
59
|
-
| `E404` 发布时 | 包名或权限问题 | 查 `npm whoami` |
|
|
60
|
-
|
|
61
|
-
> **404 / 409 / ETARGET 在发布后的几分钟内都是传播现象,不是错误。**
|
|
62
|
-
> 唯一例外:`403` 和发布时的 `E404`。
|
|
63
|
-
|
|
64
|
-
## 3. 凭据自检
|
|
65
|
-
|
|
66
|
-
不打印 token 本身:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
npm whoami --registry=https://registry.npmjs.org
|
|
70
|
-
|
|
71
|
-
TOKEN=$(sed -n 's/.*_authToken=//p' ~/.npmrc | tr -d '\r\n')
|
|
72
|
-
curl -s -H "Authorization: Bearer $TOKEN" \
|
|
73
|
-
"https://registry.npmjs.org/-/npm/v1/tokens" --max-time 30 \
|
|
74
|
-
| python -c "
|
|
75
|
-
import json,sys
|
|
76
|
-
for t in (json.load(sys.stdin).get('objects') or []):
|
|
77
|
-
print(t.get('tokenId','')[:12], 'bypass_2fa=', t.get('bypass2fa'), 'created=', t.get('created'))"
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`bypass_2fa` 必须是 `True`,否则发布会要 OTP。
|
|
81
|
-
|
|
82
|
-
## 4. 触发国内镜像同步
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
curl -s -X PUT "https://registry.npmmirror.com/-/package/weflow-cli/syncs" --max-time 40
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
`{"ok":true,...,"state":"waiting"}` 即受理,**约 5 分钟**生效。
|
|
89
|
-
|
|
90
|
-
两个源不一致时,先怀疑镜像同步,不要怀疑包:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
npm view weflow-cli version --registry=https://registry.npmjs.org
|
|
94
|
-
npm view weflow-cli version --registry=https://registry.npmmirror.com
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## 5. 装一份下来验(最终确认)
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
mkdir -p /tmp/verify && cd /tmp/verify && npm init -y >/dev/null
|
|
101
|
-
npm install weflow-cli@X.Y.Z --registry=https://registry.npmjs.org --prefer-online --no-audit --no-fund
|
|
102
|
-
node -e "console.log(require('./node_modules/weflow-cli/package.json').version)"
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
装不上但第 1 节显示版本存在 → 就是 tarball 传播中,等。
|
|
106
|
-
|
|
107
|
-
## 6. 打包安全检查(发布前,不能跳)
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
npm pack --dry-run 2>&1 | grep "npm notice [0-9]" > /tmp/packlist.txt
|
|
111
|
-
grep -E "output/|models/|node_modules|\.env|\.db$|\.dat$|_env\.json|secret|credential|\.key$|\.pem$" /tmp/packlist.txt
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**这条必须没有输出。** 有输出就停下查。
|
|
115
|
-
|
|
116
|
-
再确认新增文件确实**在**包里(`files` 白名单按目录配,容易漏):
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
for f in scripts/health_check.py docs/HEALTH-CHECK.md cli.cjs; do
|
|
120
|
-
grep -qF "$f" /tmp/packlist.txt && echo "✓ $f" || echo "✗ 缺失 $f"
|
|
121
|
-
done
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## 已知坑
|
|
125
|
-
|
|
126
|
-
- **`bin` 会被 npm 改写**:`"bin": {"weflow-cli": "./cli.cjs"}` 发布时报
|
|
127
|
-
`script name cli.cjs was invalid and removed`,同时 `repository.url` 被规范化。
|
|
128
|
-
是 warning,不影响发布结果,但要知道它每次都出现,不是本次引入的问题。
|
|
129
|
-
- **`.gitignore` 里 `*.md` 默认忽略、逐个放行**:新增文档必须同时加
|
|
130
|
-
`!docs/<名字>.md`,否则 `git add` 会被挡。
|
|
1
|
+
# npm 发布 — 速查与故障对照
|
|
2
|
+
|
|
3
|
+
只讲 npm 这一侧。**流程清单**在 [RELEASING.md](RELEASING.md),这里只解决
|
|
4
|
+
「命令跑完了,现在到底算成功没有」。
|
|
5
|
+
|
|
6
|
+
单个环节按需查,不必通读。
|
|
7
|
+
|
|
8
|
+
## 0. 省上下文(先看这条)
|
|
9
|
+
|
|
10
|
+
发布命令会打印几百行 `Tarball Contents`。**永远加过滤**,否则整个文件清单
|
|
11
|
+
会灌进上下文,既没用又挤掉真正要看的信息:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm publish --registry=https://registry.npmjs.org 2>&1 \
|
|
15
|
+
| grep -vE "^npm notice [0-9.]+[kMB]+ |^npm notice [0-9.]+B " | tail -25
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`npm pack --dry-run` 同理。要看文件清单时**输出到文件再 grep**,不要直接打印:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm pack --dry-run 2>&1 | grep "npm notice [0-9]" > /tmp/packlist.txt
|
|
22
|
+
grep -c " dist/" /tmp/packlist.txt # 注意: 行首是体积, 不是路径
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 1. 唯一可靠的判据
|
|
26
|
+
|
|
27
|
+
**别信命令的输出,信 registry 的 `versions` 表:**
|
|
28
|
+
|
|
29
|
+
下面用 `X.Y.Z` 代表这次要发的版本,**照抄时换成实际版本号**——写死在这里的版本
|
|
30
|
+
过几天就是错的。
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
V=X.Y.Z
|
|
34
|
+
curl -s "https://registry.npmjs.org/weflow-cli?nocache=$RANDOM" --max-time 30 \
|
|
35
|
+
| python -c "
|
|
36
|
+
import json,sys,os
|
|
37
|
+
d=json.load(sys.stdin)
|
|
38
|
+
print('latest:', d['dist-tags']['latest'])
|
|
39
|
+
print('目标版本存在:', os.environ['V'] in d['versions'])" V="$V"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `目标版本存在: True` → **发布成功**,剩下只是传播
|
|
43
|
+
- `False` → 没发出去,看第 2 节
|
|
44
|
+
|
|
45
|
+
`npm view` 会读到客户端缓存,`--prefer-online` 或上面的 `nocache` 才准。
|
|
46
|
+
|
|
47
|
+
## 2. 症状 → 含义 → 动作
|
|
48
|
+
|
|
49
|
+
| 现象 | 含义 | 动作 |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `+ weflow-cli@X.Y.Z` | 已被接受 | 继续,等传播 |
|
|
52
|
+
| `being processed and may take a few minutes` | 正常提示 | 等,别重发 |
|
|
53
|
+
| `npm view` 仍是旧版本 | CDN 传播中 | **不是失败**,等 2–5 分钟 |
|
|
54
|
+
| 重发报 `409 Conflict - Cannot publish over previously staged version` | **已经发布成功了** | 别再发,去等传播 |
|
|
55
|
+
| tarball `404`(`/-/weflow-cli-X.Y.Z.tgz`) | 元数据已发布,tarball 还在传播 | 等,不是失败 |
|
|
56
|
+
| `install` 报 `ETARGET No matching version` | 客户端缓存了旧 packument | 加 `--prefer-online` 重试 |
|
|
57
|
+
| `install` 报 `404` | 元数据在、tarball 未到 | 等传播 |
|
|
58
|
+
| `403 ... bypass 2fa enabled is required` | token 没勾 Bypass 2FA | 重新生成 token(见第 3 节) |
|
|
59
|
+
| `E404` 发布时 | 包名或权限问题 | 查 `npm whoami` |
|
|
60
|
+
|
|
61
|
+
> **404 / 409 / ETARGET 在发布后的几分钟内都是传播现象,不是错误。**
|
|
62
|
+
> 唯一例外:`403` 和发布时的 `E404`。
|
|
63
|
+
|
|
64
|
+
## 3. 凭据自检
|
|
65
|
+
|
|
66
|
+
不打印 token 本身:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm whoami --registry=https://registry.npmjs.org
|
|
70
|
+
|
|
71
|
+
TOKEN=$(sed -n 's/.*_authToken=//p' ~/.npmrc | tr -d '\r\n')
|
|
72
|
+
curl -s -H "Authorization: Bearer $TOKEN" \
|
|
73
|
+
"https://registry.npmjs.org/-/npm/v1/tokens" --max-time 30 \
|
|
74
|
+
| python -c "
|
|
75
|
+
import json,sys
|
|
76
|
+
for t in (json.load(sys.stdin).get('objects') or []):
|
|
77
|
+
print(t.get('tokenId','')[:12], 'bypass_2fa=', t.get('bypass2fa'), 'created=', t.get('created'))"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`bypass_2fa` 必须是 `True`,否则发布会要 OTP。
|
|
81
|
+
|
|
82
|
+
## 4. 触发国内镜像同步
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
curl -s -X PUT "https://registry.npmmirror.com/-/package/weflow-cli/syncs" --max-time 40
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`{"ok":true,...,"state":"waiting"}` 即受理,**约 5 分钟**生效。
|
|
89
|
+
|
|
90
|
+
两个源不一致时,先怀疑镜像同步,不要怀疑包:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm view weflow-cli version --registry=https://registry.npmjs.org
|
|
94
|
+
npm view weflow-cli version --registry=https://registry.npmmirror.com
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 5. 装一份下来验(最终确认)
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
mkdir -p /tmp/verify && cd /tmp/verify && npm init -y >/dev/null
|
|
101
|
+
npm install weflow-cli@X.Y.Z --registry=https://registry.npmjs.org --prefer-online --no-audit --no-fund
|
|
102
|
+
node -e "console.log(require('./node_modules/weflow-cli/package.json').version)"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
装不上但第 1 节显示版本存在 → 就是 tarball 传播中,等。
|
|
106
|
+
|
|
107
|
+
## 6. 打包安全检查(发布前,不能跳)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm pack --dry-run 2>&1 | grep "npm notice [0-9]" > /tmp/packlist.txt
|
|
111
|
+
grep -E "output/|models/|node_modules|\.env|\.db$|\.dat$|_env\.json|secret|credential|\.key$|\.pem$" /tmp/packlist.txt
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**这条必须没有输出。** 有输出就停下查。
|
|
115
|
+
|
|
116
|
+
再确认新增文件确实**在**包里(`files` 白名单按目录配,容易漏):
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
for f in scripts/health_check.py docs/HEALTH-CHECK.md cli.cjs; do
|
|
120
|
+
grep -qF "$f" /tmp/packlist.txt && echo "✓ $f" || echo "✗ 缺失 $f"
|
|
121
|
+
done
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 已知坑
|
|
125
|
+
|
|
126
|
+
- **`bin` 会被 npm 改写**:`"bin": {"weflow-cli": "./cli.cjs"}` 发布时报
|
|
127
|
+
`script name cli.cjs was invalid and removed`,同时 `repository.url` 被规范化。
|
|
128
|
+
是 warning,不影响发布结果,但要知道它每次都出现,不是本次引入的问题。
|
|
129
|
+
- **`.gitignore` 里 `*.md` 默认忽略、逐个放行**:新增文档必须同时加
|
|
130
|
+
`!docs/<名字>.md`,否则 `git add` 会被挡。
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# NT 数据库普查(db_storage 全量清单)
|
|
2
|
+
|
|
3
|
+
> 普查日期:2026-10-26,本机微信 4.1.15.56(D-130 verified)。方法:逐库用全库口令派生钥匙打开,
|
|
4
|
+
> 只读 `sqlite_master` / `PRAGMA table_info` / `COUNT(*)`,不导出行内容。脚本一次性(`probe_dbs.py` 用后即删),
|
|
5
|
+
> 本文档是留存。**行数是本机的观测值**,别的机器数量不同、表结构可能随版本变。
|
|
6
|
+
|
|
7
|
+
## 已解析(代码里有读取方)
|
|
8
|
+
|
|
9
|
+
| 库 | 读取方 | 备注 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `message/message_*.db` | `nt_decrypt` / 导出 / 全家 | 消息分片(按时间切) |
|
|
12
|
+
| `message/biz_message_0.db` | `_utils.biz_message_db()` | 公众号推送 |
|
|
13
|
+
| `contact/contact.db` | 联系人 / 群 ext_buffer | 结构笔记见 CONTACT_DB_SCHEMA.md |
|
|
14
|
+
| `favorite/favorite.db` | `fav` 命令族 | 收藏 |
|
|
15
|
+
| `sns/sns.db` | `sns` 命令族 | 朋友圈缓存 |
|
|
16
|
+
| `message/message_fts.db` | `route_cards`(只 `LIKE`,不 `MATCH`) | 分词器 MMFtsTokenizer 不可用 |
|
|
17
|
+
|
|
18
|
+
## 未解析(2026-10-26 普查新增,按价值排序)
|
|
19
|
+
|
|
20
|
+
| 库 | 表 / 行数(本机) | 是什么 | 价值判断 |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| `head_image/head_image.db` | `head_image(1083)`:`username, md5, image_buffer, update_time` | **头像本体**:JPEG/PNG 完整图片字节内嵌库中(实测 magic:1056 个 `FFD8FFE0`、27 个 `89504E47`),按 username 键控,一人多行 = 头像变更历史 | **高**:导出/报告/replay 配真实头像;文件缓存清理会丢,库里永远在。**已实现 `weflow-cli avatar`** |
|
|
23
|
+
| `general/general.db` | `redEnvelopeTable(15)`、`transferTable(22)`、`revokemessage`、`websearch_record(81)`、`SearchRecent(36)`、`biz_subscribe_status(41)` 等 24 张 | **资金流与行为**:红包(hb_status/hb_type/receive_status)、转账(pay 双方/时间)、撤回记录、**微信内置搜索历史**、公众号订阅状态 | **高**:红包/转账与 `leads`/`reply-debt` 的"涉钱"判断同域;搜索历史是"我在找什么"的维度 |
|
|
24
|
+
| `hardlink/hardlink.db` | `image_hardlink_info_v4(4638)`、`file_(1972)`、`video_(128)`:`md5, file_name, file_size, dir1, dir2` + `dir2id(123)` | **媒体文件索引**:md5 → 实际文件路径(目录 id 经 `dir2id` 映射)。D-107 提到的 `hardlink.dat`(16 字节)与它不是一回事 | **高**:当前媒体匹配靠启发式(D-014),这张表可能把它变成精确查找——覆盖率问题的正解候选 |
|
|
25
|
+
| `emoticon/emoticon.db` | `kNonStoreEmoticonTable(75)`:`md5, caption, aes_key, thumb_url, cdn_url`、`kStoreEmoticonPackageTable(5)` 等 | **表情库**:自定义表情的 **aes_key 直接在表里**、CDN 地址、表情包清单 | **高**:自定义表情导出目前靠"扫内存反推 seed"——这张表可能让那条 hack 退役 |
|
|
26
|
+
| `message/media_0/1.db` | `VoiceInfo(1779+346)`:`chat_name_id, create_time, local_id, svr_id, voice_data, data_index` + `Name2Id` | **语音本体**:`voice_data` 内嵌库中(实测首字节 `0x0223`,**不是**标准 SILK 头 `#!SILK`/`TwoLL`,格式待研究) | 中:若解出格式,语音转写不再依赖 msg/voice 文件缓存 |
|
|
27
|
+
| `session/session.db` | `SessionTable(709)`:`summary, unread_count, status, is_hidden…`、`SessionUnreadListTable_1(27021)`、`SessionDeleteTable(6)` | **会话表**:未读/摘要/隐藏/草稿;`SessionDeleteTable` 记录**用户何时删了会话**(取证价值) | 中:`sessions` 命令现从消息分片推导,这里是一手表 |
|
|
28
|
+
| `solitaire/solitaire.db` | `SolitaireFold/Valid_<md5>`(19+21):`front/behind_content, title` | **群接龙**:内容与参与记录 | 低(除非做接龙消息导出) |
|
|
29
|
+
| `contact/contact_fts.db`、`favorite/favorite_fts.db` | FTS5 结构(content/data/docsize/idx) | 联系人/收藏的搜索索引(衍生库) | 低:可从主库重建;分词器同样不可用 |
|
|
30
|
+
| `bizchat/bizchat.db` | `name2id(2)`、`chat_group(0)` | 企业微信联系人(本机几乎没用) | 低 |
|
|
31
|
+
| `chatbot/chatbot_message.db` | 4 张表全 0 行 | 微信内置 AI 助手的消息(本机未用) | 低 |
|
|
32
|
+
| `third_app_icon/third_app_icon.db` | 0 行 | 第三方应用图标 | 无 |
|
|
33
|
+
| `message/weclaw.db` | `wcdb_builtin_compression_record(0)` | WCDB 压缩簿记——**解开了 bonds 过滤 `@weclaw` 的疑惑**:它是客户端内部机制名 | 无(但解释了一个旧过滤项) |
|
|
34
|
+
|
|
35
|
+
## 钥匙
|
|
36
|
+
|
|
37
|
+
全部库用同一派生链打开(`nt_common.derive_database_key`:全库口令 PBKDF2 → 每库 salt),本次普查
|
|
38
|
+
14 个库全部一次打开成功——**NT 库没有按库独立的钥匙体系**,`emoticon.aes_key` 那类字段是内容层
|
|
39
|
+
(表情包 CDN 下载)的钥匙,不是库钥匙。
|
|
40
|
+
|
|
41
|
+
## 下一步候选
|
|
42
|
+
|
|
43
|
+
1. ✅ `avatar`(head_image)——已实现,见 `weflow-cli avatar`。
|
|
44
|
+
2. `general.db` 的红包/转账 → 与 leads/reply-debt 联动的"资金线索"只读命令。
|
|
45
|
+
3. `hardlink.db` → 媒体精确匹配(替换启发式,动 export 的媒体索引,工程量最大)。
|
|
46
|
+
4. `emoticon.db` 的 aes_key → 自定义表情导出去内存扫描化。
|
|
47
|
+
5. `media_*.db` 的 `voice_data` 格式 → 语音转写免文件缓存(格式未解,先研究)。
|
package/docs/POSITIONING.md
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
# 这个项目靠什么活着(定位与护城河)
|
|
2
|
-
|
|
3
|
-
> 写于 2026-10-19,起因是微信 4.1.15 把「问 AI 总结文件和公众号文章」做进了客户端。
|
|
4
|
-
> 这个问题值得认真回答,因为答案决定了后面所有功能该不该做:**如果价值只是"用 AI 把微信里
|
|
5
|
-
> 已有的内容处理一遍",那微信自己会做,而且做得更顺(右键就有)。**
|
|
6
|
-
|
|
7
|
-
## 一句话
|
|
8
|
-
|
|
9
|
-
**不是"更好用的微信",是"能被带走、能被追问、能穿越时间的个人历史"。**
|
|
10
|
-
|
|
11
|
-
判据只有一个:**能不能回答微信回答不了的问题。** 答不了,就只是给微信套了一层 AI,别人没有理由换。
|
|
12
|
-
|
|
13
|
-
## 微信最近做了什么(2026-09-15,4.1.15 Win/Mac)
|
|
14
|
-
|
|
15
|
-
官方只承认两条,实测多出四条([IT之家](https://www.ithome.com/1/003/287.htm)):
|
|
16
|
-
|
|
17
|
-
| 更新 | 影响 |
|
|
18
|
-
| --- | --- |
|
|
19
|
-
| **文件和公众号文章支持「问 AI」** | ⚠️ 直接撞在我们"AI 读内容"的一半上 |
|
|
20
|
-
| 双人语音/视频通话可邀请他人加入 | 通话功能,与数据无关 |
|
|
21
|
-
| 群通话可邀请不在群里的人 | 同上 |
|
|
22
|
-
| 侧边栏新增「发现」 | UI 收敛 |
|
|
23
|
-
| 群免打扰可设「仍通知」(@我/@所有人/群公告 + 4 个成员) | 消息管理 |
|
|
24
|
-
| 聊天旁三栏浏览区,可独立窗口/吸附 | 阅读体验 |
|
|
25
|
-
|
|
26
|
-
`.53` / `.56` 这类构建**没有公开更新日志**(官网日志仍停在 4.1.15 / 09-15,GitHub 归档只到 `.53`)。
|
|
27
|
-
从安装目录看,变动集中在 `RadiumWMPF.bin`(412 MB,小程序/浏览器内核)、`WeChatOcr.bin`、
|
|
28
|
-
`XFilesOfficeReader`、`WePlayer` 这些组件——热修通常动内核,不动产品形态。
|
|
29
|
-
|
|
30
|
-
**要盯的方向**:微信正在把 AI 往"单文件/单文章"上做。它越做,越说明"跨内容、跨时间"那层是空白。
|
|
31
|
-
|
|
32
|
-
## 五条护城河(按"腾讯为什么不会做"排序)
|
|
33
|
-
|
|
34
|
-
| 护城河 | 微信不做/做不到的原因 | 我们的证据(可复验) |
|
|
35
|
-
| --- | --- | --- |
|
|
36
|
-
| **数据出口** | 微信从不提供导出聊天/朋友圈/收藏;"迁移"只到手机。4.1.15 六条更新里**没有一条**开放数据 | `export` 输出 HTML/md/json/xlsx + 图片与文件本体;issue #11 已修 |
|
|
37
|
-
| **纵向时间** | 微信的搜索只服务"找那条消息",不做"这个人三个月的变化""这件事我答应过几次" | 欠话台账、关系温度(`bonds`)、承诺追踪(`todos`)、日报归档(372 天) |
|
|
38
|
-
| **跨库联合** | 微信是产品不是平台:不会读你的 Obsidian、代码仓库、订阅源 | 语义索引 ~16k 条(聊天 5.2k + 文章 10.7k),实测 recall@10 **0.95**;原样定位(FTS5);Vault 概念页 |
|
|
39
|
-
| **本地/隐私** | 微信的 AI 必走它自己的云;承诺"不出本机"等于砍掉它的商业模型 | 支持 Ollama/LM Studio;`judgeProvider=chat` 必须尊重 `aiEngine=local`(D-124) |
|
|
40
|
-
| **可溯源** | 微信的「问 AI」给你一段总结,**点不回原句** | 每条命中带 `id`/`sourceFile`/原文链接,能回到那两个字节 |
|
|
41
|
-
|
|
42
|
-
## 会被立刻吃掉的功能(别把力气花在这里)
|
|
43
|
-
|
|
44
|
-
- 单篇文章/单个文件摘要 —— 4.1.15 已做;
|
|
45
|
-
- 会话内关键词搜索 —— 微信自带(虽然不全);
|
|
46
|
-
- 通用"和 AI 聊微信记录" —— 门槛极低,人人能做;
|
|
47
|
-
- 任何"把微信 UI 做得更顺手"的尝试。
|
|
48
|
-
|
|
49
|
-
## 四个能验的问题(产品自测题)
|
|
50
|
-
|
|
51
|
-
只要这四类问题答得比微信好,这个项目就成立:
|
|
52
|
-
|
|
53
|
-
1. **"我三个月前答应过谁什么,兑现了吗"** —— 跨时间 + 承诺追踪。
|
|
54
|
-
2. **"我和谁的关系在变淡,还救得回来吗"** —— 纵向聚合;微信只给当下。
|
|
55
|
-
3. **"这个号今年发了几篇,观点怎么变的"** —— 跨文章统计;微信只给单篇。
|
|
56
|
-
4. **"那句话他原样写过吗,哪天"** —— 原样定位;微信搜不到三个月前的群聊。
|
|
57
|
-
|
|
58
|
-
## 因此,产品的三个"不做"
|
|
59
|
-
|
|
60
|
-
1. **不追微信的新功能**(右键问 AI、发现页、三栏阅读)——那是它的产品节奏,追不上也没意义。
|
|
61
|
-
2. **不做纯 UI 替代品** —— 用户没有理由因为"更好看的聊天窗口"而迁移。
|
|
62
|
-
3. **不把 AI 摘要当成卖点** —— 摘要人人能做;**能溯源、能跨时间、能带走**才是。
|
|
63
|
-
|
|
64
|
-
## 这个判断会过期的情形
|
|
65
|
-
|
|
66
|
-
如果哪天微信真的开放了"导出全部聊天记录"或"本地数据 API",第一条护城河会塌一半——
|
|
67
|
-
到那时要立刻重估:**是不是该转向"分析层"(纵向/跨库/证据链),而不是"读取层"**。
|
|
68
|
-
这件事目前没有任何迹象,但要盯住更新日志里出现 `export` / `开放平台` / `本地数据库` 这类字眼。
|
|
1
|
+
# 这个项目靠什么活着(定位与护城河)
|
|
2
|
+
|
|
3
|
+
> 写于 2026-10-19,起因是微信 4.1.15 把「问 AI 总结文件和公众号文章」做进了客户端。
|
|
4
|
+
> 这个问题值得认真回答,因为答案决定了后面所有功能该不该做:**如果价值只是"用 AI 把微信里
|
|
5
|
+
> 已有的内容处理一遍",那微信自己会做,而且做得更顺(右键就有)。**
|
|
6
|
+
|
|
7
|
+
## 一句话
|
|
8
|
+
|
|
9
|
+
**不是"更好用的微信",是"能被带走、能被追问、能穿越时间的个人历史"。**
|
|
10
|
+
|
|
11
|
+
判据只有一个:**能不能回答微信回答不了的问题。** 答不了,就只是给微信套了一层 AI,别人没有理由换。
|
|
12
|
+
|
|
13
|
+
## 微信最近做了什么(2026-09-15,4.1.15 Win/Mac)
|
|
14
|
+
|
|
15
|
+
官方只承认两条,实测多出四条([IT之家](https://www.ithome.com/1/003/287.htm)):
|
|
16
|
+
|
|
17
|
+
| 更新 | 影响 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| **文件和公众号文章支持「问 AI」** | ⚠️ 直接撞在我们"AI 读内容"的一半上 |
|
|
20
|
+
| 双人语音/视频通话可邀请他人加入 | 通话功能,与数据无关 |
|
|
21
|
+
| 群通话可邀请不在群里的人 | 同上 |
|
|
22
|
+
| 侧边栏新增「发现」 | UI 收敛 |
|
|
23
|
+
| 群免打扰可设「仍通知」(@我/@所有人/群公告 + 4 个成员) | 消息管理 |
|
|
24
|
+
| 聊天旁三栏浏览区,可独立窗口/吸附 | 阅读体验 |
|
|
25
|
+
|
|
26
|
+
`.53` / `.56` 这类构建**没有公开更新日志**(官网日志仍停在 4.1.15 / 09-15,GitHub 归档只到 `.53`)。
|
|
27
|
+
从安装目录看,变动集中在 `RadiumWMPF.bin`(412 MB,小程序/浏览器内核)、`WeChatOcr.bin`、
|
|
28
|
+
`XFilesOfficeReader`、`WePlayer` 这些组件——热修通常动内核,不动产品形态。
|
|
29
|
+
|
|
30
|
+
**要盯的方向**:微信正在把 AI 往"单文件/单文章"上做。它越做,越说明"跨内容、跨时间"那层是空白。
|
|
31
|
+
|
|
32
|
+
## 五条护城河(按"腾讯为什么不会做"排序)
|
|
33
|
+
|
|
34
|
+
| 护城河 | 微信不做/做不到的原因 | 我们的证据(可复验) |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| **数据出口** | 微信从不提供导出聊天/朋友圈/收藏;"迁移"只到手机。4.1.15 六条更新里**没有一条**开放数据 | `export` 输出 HTML/md/json/xlsx + 图片与文件本体;issue #11 已修 |
|
|
37
|
+
| **纵向时间** | 微信的搜索只服务"找那条消息",不做"这个人三个月的变化""这件事我答应过几次" | 欠话台账、关系温度(`bonds`)、承诺追踪(`todos`)、日报归档(372 天) |
|
|
38
|
+
| **跨库联合** | 微信是产品不是平台:不会读你的 Obsidian、代码仓库、订阅源 | 语义索引 ~16k 条(聊天 5.2k + 文章 10.7k),实测 recall@10 **0.95**;原样定位(FTS5);Vault 概念页 |
|
|
39
|
+
| **本地/隐私** | 微信的 AI 必走它自己的云;承诺"不出本机"等于砍掉它的商业模型 | 支持 Ollama/LM Studio;`judgeProvider=chat` 必须尊重 `aiEngine=local`(D-124) |
|
|
40
|
+
| **可溯源** | 微信的「问 AI」给你一段总结,**点不回原句** | 每条命中带 `id`/`sourceFile`/原文链接,能回到那两个字节 |
|
|
41
|
+
|
|
42
|
+
## 会被立刻吃掉的功能(别把力气花在这里)
|
|
43
|
+
|
|
44
|
+
- 单篇文章/单个文件摘要 —— 4.1.15 已做;
|
|
45
|
+
- 会话内关键词搜索 —— 微信自带(虽然不全);
|
|
46
|
+
- 通用"和 AI 聊微信记录" —— 门槛极低,人人能做;
|
|
47
|
+
- 任何"把微信 UI 做得更顺手"的尝试。
|
|
48
|
+
|
|
49
|
+
## 四个能验的问题(产品自测题)
|
|
50
|
+
|
|
51
|
+
只要这四类问题答得比微信好,这个项目就成立:
|
|
52
|
+
|
|
53
|
+
1. **"我三个月前答应过谁什么,兑现了吗"** —— 跨时间 + 承诺追踪。
|
|
54
|
+
2. **"我和谁的关系在变淡,还救得回来吗"** —— 纵向聚合;微信只给当下。
|
|
55
|
+
3. **"这个号今年发了几篇,观点怎么变的"** —— 跨文章统计;微信只给单篇。
|
|
56
|
+
4. **"那句话他原样写过吗,哪天"** —— 原样定位;微信搜不到三个月前的群聊。
|
|
57
|
+
|
|
58
|
+
## 因此,产品的三个"不做"
|
|
59
|
+
|
|
60
|
+
1. **不追微信的新功能**(右键问 AI、发现页、三栏阅读)——那是它的产品节奏,追不上也没意义。
|
|
61
|
+
2. **不做纯 UI 替代品** —— 用户没有理由因为"更好看的聊天窗口"而迁移。
|
|
62
|
+
3. **不把 AI 摘要当成卖点** —— 摘要人人能做;**能溯源、能跨时间、能带走**才是。
|
|
63
|
+
|
|
64
|
+
## 这个判断会过期的情形
|
|
65
|
+
|
|
66
|
+
如果哪天微信真的开放了"导出全部聊天记录"或"本地数据 API",第一条护城河会塌一半——
|
|
67
|
+
到那时要立刻重估:**是不是该转向"分析层"(纵向/跨库/证据链),而不是"读取层"**。
|
|
68
|
+
这件事目前没有任何迹象,但要盯住更新日志里出现 `export` / `开放平台` / `本地数据库` 这类字眼。
|
package/docs/PROJECT_STATE.md
CHANGED
|
@@ -39,8 +39,8 @@ WeFlow CLI is a local-first command-line tool and MCP server for user-authorized
|
|
|
39
39
|
|
|
40
40
|
| Area | Current state | Main entry points |
|
|
41
41
|
| --- | --- | --- |
|
|
42
|
-
| Local chat data | Query sessions, contacts, messages, favorites, Moments cache, and exports. HTML chat exports decode NT compressed media records, locally cached WeChat 4.x V2 image containers, entity-escaped emoji XML, and remote emoticons; app cards preserve links and embed available公众号/Bilibili covers. Media matching prefers server-message resources, exact local-message-ID plus timestamp pairs, and a unique local-ID fallback; high-bit encoded image rows are resolved through the same conversation cache. | `sessions`, `contacts`, `messages`, `export`, `fav`, `sns` |
|
|
43
|
-
| Initialization | Verify and reuse existing local database access by default; refresh only when needed. Missing-key tests can run without changing saved configuration. Data-root discovery asks the **running client** first (its command line carries `--wechat-files-path`; the other two routes guess from the folder name) and reports which source won; a hint still has to pass the same structural check. **`weflow-cli setup` (2026-10-23)** builds a managed venv at `~/.weflow-cli/venv` and installs requirements into it; `getPythonCommand()` prefers that venv whenever it exists and passes the `import sqlcipher3` probe, falling back to the previous PATH order when it does not. On Windows the source package `sqlcipher3` is substituted with the prebuilt `sqlcipher3-wheels` (same import name; `sqlcipher3-binary` has **no Windows wheels at all** - measured 2026-10-23, pip reports `No matching distribution`). Two-phase like everything mutating (`--dry-run` plan, `--yes` to execute), pip retried once on transient network failure, and the run verifies `import sqlcipher3` plus that the runtime actually prefers the venv. End-to-end verified on this machine in a redirected HOME (2026-10-23): create venv → 8 packages → import OK → `getPythonCommand()` points at the venv. | `init`, `init --refresh`, `init --test-missing-keys`, `config forget-keys`, `check`, `setup` |
|
|
42
|
+
| Local chat data | Query sessions, contacts, messages, favorites, Moments cache, and exports. HTML chat exports decode NT compressed media records, locally cached WeChat 4.x V2 image containers, entity-escaped emoji XML, and remote emoticons; app cards preserve links and embed available公众号/Bilibili covers. Media matching prefers server-message resources, exact local-message-ID plus timestamp pairs, and a unique local-ID fallback; high-bit encoded image rows are resolved through the same conversation cache. **Database key caching (2026-10-27)**: after the first memory scan, keys are stored in `~/.weflow-cli/keys/keys.enc` indexed by database salt (Windows: DPAPI-encrypted; other platforms: XOR + mode 0600). Subsequent queries check the cache first - cache hit returns in milliseconds without scanning process memory. Keys persist across WeChat restarts. End-to-end verified on this machine (2026-10-27): 10 keys stored in 1.76 ms, retrieved in 0.01 ms (**262.8x speedup**). All database-reading commands (`daily`/`search`/`export`/MCP tools) automatically benefit; the optimization is transparent to users. | `sessions`, `contacts`, `messages`, `export`, `fav`, `sns` |
|
|
43
|
+
| Initialization | Verify and reuse existing local database access by default; refresh only when needed. Missing-key tests can run without changing saved configuration. Data-root discovery asks the **running client** first (its command line carries `--wechat-files-path`; the other two routes guess from the folder name) and reports which source won; a hint still has to pass the same structural check. **`weflow-cli setup` (2026-10-23)** builds a managed venv at `~/.weflow-cli/venv` and installs requirements into it; `getPythonCommand()` prefers that venv whenever it exists and passes the `import sqlcipher3` probe, falling back to the previous PATH order when it does not. On Windows the source package `sqlcipher3` is substituted with the prebuilt `sqlcipher3-wheels` (same import name; `sqlcipher3-binary` has **no Windows wheels at all** - measured 2026-10-23, pip reports `No matching distribution`). Two-phase like everything mutating (`--dry-run` plan, `--yes` to execute), pip retried once on transient network failure, and the run verifies `import sqlcipher3` plus that the runtime actually prefers the venv. End-to-end verified on this machine in a redirected HOME (2026-10-23): create venv → 8 packages → import OK → `getPythonCommand()` points at the venv. **The verified WeChat version is machine-readable since 2026-10-26 (D-130)**: single source `WECHAT_VERIFIED` in `src/services/wechatCompat.ts` (4.1.15.56, re-checked 2026-10-26 with a live `sessions` read), exposed via `capabilities --json` (`compatibility.wechat`) and compared against the actually-loaded build in `check` (four-state verdict; `newer-than-verified` is a warning in `check --report`). | `init`, `init --refresh`, `init --test-missing-keys`, `config forget-keys`, `check`, `setup` |
|
|
44
44
|
| Environment / diagnosis | `check` (and the scheduled `health_check`) reports the **WeChat build actually loaded**, read from the live process's `--user-lib-dir` and - since 4.1.15.53 launches the main process with no switches (D-125) - falling back to the `Weixin.dll` that process actually has mapped, rather than to the version literal inside `Weixin.exe` (that one is only a fallback - D-107). It flags version directories that cannot work (no `Weixin.dll`). When the client is not running it says *unknown*, and a missing field counts as a failure so a stale build cannot pass. Verified on this machine: `4.1.15.53` loaded (2026-10-18, after updating from 4.1.15.13), `4.1.15.11\` (one file) flagged as a leftover. `--report` renders the same facts as a **paste-ready, redacted verdict** (environment, client, key presence, then "what is broken and the next command"), so a user can hand an AI or an issue something better than a description of the symptom (D-126). | `check`, `check --json`, `check --report`, `scripts/health_check.py` |
|
|
45
45
|
| Relationship temperature | Who is going quiet, who is heating up, and who is reachable **only** here. Reads the local message database directly: read-only, no model, no network (D-062). Three groups - `cooling` (substantial history then silence, ranked by messages x silent days), `heating` (last 7 days at least twice the long-run daily rate), `only-wechat` (no phone/email/meeting-up ever appears in the history). Only 1:1 chats, only text messages, and only accounts whose **id shape** is a person: anything with `@` (`@chatroom`/`@openim`/`@weclaw`), `filehelper` or `gh_` is out - ranking by volume alone produced no information, and the first irreplaceability rule was inverted (it is trivially true of bots, so it put a coffee-shop bot first). Accounts that look like a person but whose name reads as a shop are **flagged, not dropped**, and every exclusion is printed with its reason. Deliberately **not** in the assistant or MCP tool tables: its output is an inference about third parties. | `bonds` |
|
|
46
46
|
| Official-account daily | Filter configured sources, preserve source categories, backfill incomplete yesterday output before an unqualified today run, fetch articles, classify them, create summaries, generate a local HTML reader, and synchronize reader favorites into local files. Topic (6-way), relevance (3-level) and "does this belong in today's report" (yes/no) are decided by TypeSafe's Jev when `typesafeApiKey` is configured - concurrently, before the serial summary loop (12 articles in 3.3s at 6 workers against ~12s one at a time), and by the previous LLM-parse path otherwise; `--classifier llm` forces the latter. The report's admission rule is applied on both loader paths and can be bypassed with `--include-all`. Two flags exist for the unattended case: **`--yesterday`** (a scheduled run wants *the previous day*, since accounts publish until midnight, and "yesterday" is time-insensitive - the same data whenever the machine wakes up) and **`--prefetch-bodies [n]`** (default off; warms the body cache concurrently *before* the serial, deliberately throttled fetch phase, which then runs entirely from cache - the upstream request count is unchanged, only the window it is spread over). | `daily`, `daily favorites`, `daily-stats`, `daily-server` |
|
|
@@ -78,6 +78,7 @@ parser had already unwrapped. One character, silently, for any frontmatter list
|
|
|
78
78
|
Both now unwrap a matched pair only. Measured afterwards: **28 orphan pages → 1** (that one, `DESIGN`, is mentioned by
|
|
79
79
|
no card at all) and still 0 broken links. | `wiki`, `vault`, `search`, `chat`, `weread`, `review` |
|
|
80
80
|
| QQ chat imports | `weflow-cli import qchat <path>` reads a qchat-cli JSON export (`qce export --format json`) and lands it in `output/imports/qq/sessions/` (`weflow-import/qq-v1`; field semantics aligned with `weflow-message/v1`, mapping table in `docs/DATA_CONTRACT.md`). Two-phase (`--dry-run` / `--yes`), merge-on-reimport keyed on the globally-unique QQ `messageId` (added/updated/unchanged counted separately), foreign JSON rejected with a reason. Downstream, both opt-in: `chat-notes --source qq\|all` emits QQ knowledge cards (`QQ-` name prefix, same card shape and same wiki consumer as WeChat; `all` degrades to QQ-only with a warning when the WeChat DB is unavailable) and `search-index --include-imports` adds QQ sessions to the semantic index (ids carry `chat:qq:`; build works on a machine with no WeChat config at all - end-to-end tested with stubbed embeddings, `test/qchat_import_test.py`). qchat-cli itself is not modified; the boundary is the file contract, not process coupling (D-129). **Validated on real data end-to-end (2026-10-25)**: NapCat.Shell v4.18.1 + QQ NT 9.9.33 (winget) on this machine, OneBot HTTP on 127.0.0.1:3000; two private chats exported via `qce export --format json` (12 messages) landed through `import qchat --own-id` (2 sessions, read-back via `import list/show` verified), `chat-notes --source qq --dry-run` saw both sessions (12 messages / 352 chars), and the index collector turned the 12 messages into 7 records (5 dropped by the same ≥10-char threshold the WeChat side applies). The real export shape matches the parser field-for-field; one observed detail: qchat's `session.type` is `friend` (not `private` as the synthetic fixtures assumed - the field passes through, so no code change). NapCat pitfall recorded for reproduction: on first login it creates an **account-specific** `onebot11_<qq>.json` with EMPTY `httpServers`, silently overriding any pre-seeded `onebot11_1.json` template - the 3000 server only starts after that file is fixed. Group export failed inside qchat-cli itself (`fetchMessages`) and was not exercised here. Querying QQ sessions through `sessions`/`messages` is deliberately not in this round. | `import qchat`, `import list`, `import show`, `chat-notes --source qq`, `search-index --include-imports` |
|
|
81
|
+
| Avatars | `weflow-cli avatar <contact>` reads `head_image.db` - the avatar BYTES live in the table (`image_buffer`, measured 2026-10-26: 1,056 JPEG + 27 PNG among 1,083 rows keyed by username; one user can have several rows = avatar history, newest wins). `--list` names who has one, `--all` exports the latest avatar per user, `--json` metadata only (never base64). Found by the 2026-10-26 db_storage census (`docs/NT_DATABASES.md` - 14 databases probed, all open with the same passphrase-derived key chain; unparsed high-value candidates there: general.db money tables, hardlink.db media index, emoticon aes_key, media_*.db inline voice_data whose header `0x0223` is NOT standard SILK - format still unresearched). Read-only, local, no network. | `avatar`, `avatar --list`, `avatar --all` |
|
|
81
82
|
| Incremental read | `sync run` reads a time window, deduplicates against the previous checkpoint and writes coverage state; `sync status` reports it without touching the database; `sync verify` re-reads the recorded window and compares bounds and counts. Every run reports `coverage` as `complete`, `unverified` or `partial`, and a partial run never advances `lastSuccessfulRun`. The window lower bound is offered to the NT backend as a pushdown hint (`messages --from`), but `syncService` still applies the window itself, so backends without range support stay correct - the pushdown is asymptotic, and it measured no faster on a 925-message conversation. It does **not** offer a stable cursor - see D-027. | `sync run`, `sync status`, `sync verify` |
|
|
82
83
|
| Calibration | `scripts/quality_eval.py` samples real articles, scores each through the production path, and writes a three-field labelling file (`topic`/`relevance`/`include`); `score` reports topic agreement (Jev vs the stored labels), relevance agreement plus mean-level error, a calibration table by probability bucket, and **two** threshold sweeps (the inclusion cut and the relevance cuts - both were guessed, and both are now older than the first labelled set). The printed sheet is **blind**: it does not show Jev's answers, because seeing them first inflates agreement by an unmeasurable amount. `consistency` is the half that needs **no** labels: it reports how often the two questions answer the same thing two ways. Labels stay in `~/.weflow-cli/labels/`. A **dry-run** label file made by the assistant exists in that directory (`DRYRUN-by-claude-*.json`) so the scoring path could be exercised before any human label existed; its numbers are **not accuracy** - annotator and model are the same kind of model, and nothing in the data separates agreement-because-right from agreement-because-kin. Do not treat it as a baseline. | `python scripts/quality_eval.py sample`, `score <file>`, `consistency <file>` |
|
|
83
84
|
| Criteria A/B | `python scripts/jev_probe.py --criteria-ab --limit 24 --yes` asks the same articles twice - current criteria (Chinese, abstract score bins) vs an English variant whose score bins are concrete scenes, following `jev-chat-jarvis`'s two stated rules. It measures **what a wording change would move**, not whether it improves anything. Measured locally (24 + 12 articles): topic agreement 79% / 67%, relevance raw-score mean absolute difference 0.17 / 0.24, and the disagreements concentrate on the **AI↔学术** and **文学↔新闻** boundaries. | `python scripts/jev_probe.py --criteria-ab` |
|
package/docs/RELEASING.md
CHANGED
|
@@ -39,6 +39,10 @@ node -p "require('./package-lock.json').version"
|
|
|
39
39
|
- 把 `## Unreleased` 改名为 `## <新版本号>`
|
|
40
40
|
- 若上一版已写好但没发布,把它的条目并入,删掉那个空段
|
|
41
41
|
- 通用说明(如「npm 与 GitHub 分开发布」)移到文件顶部,别留在版本段里
|
|
42
|
+
- **每个版本段记录验证过的微信版本**(D-130):一行 `> 验证过的微信:4.1.15.56(2026-10-26)`,
|
|
43
|
+
来源是 `src/services/wechatCompat.ts` 的 `WECHAT_VERIFIED`——两边必须一致(测试钉着 capabilities 那半)。
|
|
44
|
+
本版完全不涉及微信数据读取的,写 `> 验证过的微信:沿用上一版(本版不涉及微信数据读取)`;
|
|
45
|
+
验证过**新**微信版本的,先实读真实数据再改常量,再写进这里。
|
|
42
46
|
|
|
43
47
|
## 3. 构建与测试
|
|
44
48
|
|
package/package.json
CHANGED
package/requirements-3x.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Optional dependencies for legacy WeChat 3.x key extraction.
|
|
2
|
-
# Install the base requirements first.
|
|
3
|
-
psutil
|
|
4
|
-
pywxdump
|
|
1
|
+
# Optional dependencies for legacy WeChat 3.x key extraction.
|
|
2
|
+
# Install the base requirements first.
|
|
3
|
+
psutil
|
|
4
|
+
pywxdump
|
package/requirements-voice.txt
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Optional: voice-message transcription for HTML chat export.
|
|
2
|
-
#
|
|
3
|
-
# Only needed if you want voice messages to appear as text. Install with:
|
|
4
|
-
# python -m pip install -r requirements-voice.txt
|
|
5
|
-
#
|
|
6
|
-
# `pilk` decodes WeChat's SILK v3 audio, which neither browsers nor ffmpeg
|
|
7
|
-
# can decode. `faster-whisper` does the recognition on-device; the model is
|
|
8
|
-
# fetched separately (see OPERATIONS.md, 「语音消息」).
|
|
9
|
-
#
|
|
10
|
-
# On an NVIDIA GPU, also install the CUDA runtime wheels the recognition
|
|
11
|
-
# library loads at runtime. Without them it falls back to CPU and is roughly
|
|
12
|
-
# 30x slower:
|
|
13
|
-
# python -m pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
|
|
14
|
-
pilk>=0.2.4
|
|
15
|
-
faster-whisper>=1.0.0
|
|
1
|
+
# Optional: voice-message transcription for HTML chat export.
|
|
2
|
+
#
|
|
3
|
+
# Only needed if you want voice messages to appear as text. Install with:
|
|
4
|
+
# python -m pip install -r requirements-voice.txt
|
|
5
|
+
#
|
|
6
|
+
# `pilk` decodes WeChat's SILK v3 audio, which neither browsers nor ffmpeg
|
|
7
|
+
# can decode. `faster-whisper` does the recognition on-device; the model is
|
|
8
|
+
# fetched separately (see OPERATIONS.md, 「语音消息」).
|
|
9
|
+
#
|
|
10
|
+
# On an NVIDIA GPU, also install the CUDA runtime wheels the recognition
|
|
11
|
+
# library loads at runtime. Without them it falls back to CPU and is roughly
|
|
12
|
+
# 30x slower:
|
|
13
|
+
# python -m pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
|
|
14
|
+
pilk>=0.2.4
|
|
15
|
+
faster-whisper>=1.0.0
|