weflow-cli 1.15.1 → 1.17.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/CHANGELOG.md +59 -1
- package/OPERATIONS.md +16 -1
- package/README.en.md +21 -2
- package/README.md +34 -6
- package/bin/weflow-cli.ts +389 -6
- package/dist/bin/weflow-cli.js +422 -8
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/src/core/ntCore.d.ts +47 -0
- package/dist/src/core/ntCore.d.ts.map +1 -1
- package/dist/src/core/ntCore.js +24 -0
- package/dist/src/core/ntCore.js.map +1 -1
- package/dist/src/services/assistantService.d.ts +8 -1
- package/dist/src/services/assistantService.d.ts.map +1 -1
- package/dist/src/services/assistantService.js +47 -8
- package/dist/src/services/assistantService.js.map +1 -1
- package/dist/src/services/assistantTools.d.ts +8 -0
- package/dist/src/services/assistantTools.d.ts.map +1 -1
- package/dist/src/services/assistantTools.js +67 -11
- package/dist/src/services/assistantTools.js.map +1 -1
- package/dist/src/services/configService.d.ts.map +1 -1
- package/dist/src/services/configService.js +3 -2
- package/dist/src/services/configService.js.map +1 -1
- package/dist/src/services/cursorProbe.d.ts +44 -0
- package/dist/src/services/cursorProbe.d.ts.map +1 -0
- package/dist/src/services/cursorProbe.js +183 -0
- package/dist/src/services/cursorProbe.js.map +1 -0
- package/dist/src/services/qchatImport.d.ts +89 -0
- package/dist/src/services/qchatImport.d.ts.map +1 -0
- package/dist/src/services/qchatImport.js +273 -0
- package/dist/src/services/qchatImport.js.map +1 -0
- package/dist/src/services/setupVenv.d.ts +43 -0
- package/dist/src/services/setupVenv.d.ts.map +1 -0
- package/dist/src/services/setupVenv.js +198 -0
- package/dist/src/services/setupVenv.js.map +1 -0
- package/dist/src/utils/python.d.ts +10 -0
- package/dist/src/utils/python.d.ts.map +1 -1
- package/dist/src/utils/python.js +27 -4
- package/dist/src/utils/python.js.map +1 -1
- package/docs/DATA_CONTRACT.md +25 -0
- package/docs/DECISIONS.md +92 -30
- 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/POSITIONING.md +68 -68
- package/docs/PROJECT_STATE.md +6 -3
- package/docs/REPLAY.md +169 -0
- package/docs/SYNC_CONTRACT.md +197 -180
- package/package.json +89 -88
- 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/js/graph3d/viewer2d.js +550 -448
- package/resources/panel/quick-menu.cjs +197 -197
- package/scripts/_utils.py +1338 -1338
- package/scripts/article_notes.py +423 -423
- package/scripts/bonds.py +11 -0
- package/scripts/chat_notes.py +86 -3
- package/scripts/check_readme_commands.py +427 -426
- package/scripts/contact_schema.py +339 -339
- package/scripts/create_reading_notes.py +11 -11
- package/scripts/daily_task.ps1 +213 -177
- package/scripts/fav_notes.py +321 -321
- package/scripts/graph_2d.py +156 -151
- package/scripts/graph_3d.py +672 -646
- package/scripts/health_check.py +473 -473
- package/scripts/imported_chats.py +68 -0
- package/scripts/install_tasks.ps1 +105 -91
- package/scripts/leads.py +179 -0
- package/scripts/network.py +206 -206
- package/scripts/nt_decrypt.py +135 -1
- package/scripts/on_this_day.py +178 -178
- package/scripts/opinion_timeline.py +138 -138
- package/scripts/promises.py +185 -185
- package/scripts/replay.py +803 -0
- package/scripts/run_hidden.vbs +25 -0
- package/scripts/semantic_search.py +73 -5
- 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/src/core/ntCore.ts +59 -0
- package/src/services/assistantService.ts +48 -8
- package/src/services/assistantTools.ts +73 -10
- package/src/services/configService.ts +3 -2
- package/src/services/cursorProbe.ts +222 -0
- package/src/services/qchatImport.ts +327 -0
- package/src/services/setupVenv.ts +238 -0
- package/src/services/syncService.ts +363 -363
- package/src/services/syncState.ts +275 -275
- package/src/services/wechatMedia.ts +166 -166
- package/src/utils/python.ts +33 -4
package/docs/DATA_CONTRACT.md
CHANGED
|
@@ -72,3 +72,28 @@ This is a best-effort synchronization recipe. It must not treat a timestamp as a
|
|
|
72
72
|
## Future Direction
|
|
73
73
|
|
|
74
74
|
The existing CLI export is the compatibility layer for the first integration. The same contract is available through the versioned, read-only MCP tool `wechat.export_messages`. It accepts `contact` and optional `limit`, `from`, and `to` values, returns JSON directly, and never returns database paths or keys. It preserves the same authorization, local-only default, field semantics, and privacy rules rather than bypassing the CLI boundary.
|
|
75
|
+
|
|
76
|
+
## QQ Imports (qchat-cli)
|
|
77
|
+
|
|
78
|
+
A second upstream now feeds this contract's vocabulary: `weflow-cli import qchat <file-or-dir>`
|
|
79
|
+
reads a `qchat-cli` JSON export (`qce export --format json`) and lands it in a local private
|
|
80
|
+
store `output/imports/qq/sessions/` (schema `weflow-import/qq-v1`, D-129). The store itself is
|
|
81
|
+
not a stable contract; what is stable is the **field semantics**, which are exactly
|
|
82
|
+
`weflow-message/v1`'s:
|
|
83
|
+
|
|
84
|
+
| qchat-cli field | imported as | Notes |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `messageId` | `serverId` | Server-side globally unique (OneBot) - dedup/merge keys on it, no overlapping-window dance needed. |
|
|
87
|
+
| `timestamp` | `createTime` | Unix seconds. |
|
|
88
|
+
| `sender.id` | `senderUsername` | A QQ number; treat as sensitive, same level as a wxid. |
|
|
89
|
+
| `sender.card` / `sender.name` | `senderName` | Group card first, then nickname. |
|
|
90
|
+
| `content` / `rawMessage` | `parsedContent` / `rawContent` | Same guidance as the table above. |
|
|
91
|
+
| (derived from `segments`) | `messageType` | OneBot segment types map onto the same stable names (`record→voice`, `reply→quote`, `json/xml→card`, …). |
|
|
92
|
+
| — | `isSend` | Only present when the import ran with `--own-id`; absent means unknown, not false. |
|
|
93
|
+
|
|
94
|
+
Consumers wired today (both opt-in, defaults unchanged): `chat-notes --source qq|all` produces
|
|
95
|
+
QQ knowledge cards (names prefixed `QQ-`, same card shape and same downstream wiki consumer as
|
|
96
|
+
WeChat conversations), and `search-index --include-imports` adds QQ sessions to the semantic
|
|
97
|
+
index (record ids carry a `chat:qq:` segment, so a record's origin is always readable from the
|
|
98
|
+
id; the phrase index derives from the semantic index and follows). Querying QQ sessions through
|
|
99
|
+
`sessions` / `messages` is deliberately not part of this round (D-129).
|
package/docs/DECISIONS.md
CHANGED
|
@@ -4017,33 +4017,95 @@ State the decision in one or two sentences.
|
|
|
4017
4017
|
|
|
4018
4018
|
**Consequences:** List compatibility, migration, security, or documentation follow-up when relevant.
|
|
4019
4019
|
```
|
|
4020
|
-
|
|
4021
|
-
## D-127: 决策模型可以走本机(`judgeProvider=local`),概率默认当作未校准
|
|
4022
|
-
|
|
4023
|
-
**Decision.** 新增第四条判断通路 `judgeProvider=local`:`LocalJevClient` 把本机决策模型服务
|
|
4024
|
-
(`decide.py serve`,APUS Reflex 4B + llama-server 的 `/decide`)当 Jev 用,**不需要 key、不联网**。
|
|
4025
|
-
`score` 没有连续分,改用 `choice` 在有序档位上选、把选中档位的下标当分数(下游 `score_to_relevance`
|
|
4026
|
-
本来就是四舍五入取整,语义等价)。
|
|
4027
|
-
|
|
4028
|
-
**Reason.** 原生 `api.typesafe.ai` 在本机被地区封锁(D-120),`chat` 与 `openrouter` 都要把文章正文
|
|
4029
|
-
发给第三方。本机 4B 决策模型在官方 Frozen80 上 **66/80 = 82.50%**、**0.3 s/题**(2026-10-22 本机实测,
|
|
4030
|
-
RTX 5060 8GB),对"主题 / 相关度 / 该不该收录"这类**窄口径、可验证**的判断够用,而正文不出机器。
|
|
4031
|
-
|
|
4032
|
-
**Consequences.**
|
|
4033
|
-
- 本机服务的概率是**候选集上的相对 softmax、未校准**(APUS 契约原文)。`confidence` 只作参考,
|
|
4034
|
-
不能直接当自动放行阈值;要用阈值先在自有样本上标定。
|
|
4035
|
-
- **一个问题一次 HTTP**:原生 `systemone` 一次答一组,本机 `/decide` 一次只答一个 primitive。
|
|
4036
|
-
日报 3 个问题 ⇒ 3 次调用,实测整篇 0.3–0.5 s(本机回环)。
|
|
4037
|
-
- `score` 用 **n-1 次阈值判断**(`达到「中」或「高」吗?`)合成档位,不是三选一:
|
|
4038
|
-
同一批 44 篇(2026-10-16~21,Jev 实判)上,三选一对 Jev 的一致率 45.5%(`低→中` 23 次,
|
|
4039
|
-
系统性偏高),阈值问法 **72.7%**、平均差 0.55 → 0.27 档。**措辞影响巨大**(同一模型同一批样本:
|
|
4040
|
-
纯判据句 59.1%、带主语 + 档位 + 判据 72.7%),改这句话要重测。
|
|
4041
|
-
- 服务没起时抛 `JevError`(调用方按既有降级路径退到 LLM 路径),**不静默改用云端**。
|
|
4042
|
-
- 新增配置键 `judgeLocalUrl`(默认 `http://127.0.0.1:8090`),已登记进 `CliConfig` 与 `config set`。
|
|
4043
|
-
- **按主题的相关度下限(实测校准)**:211 篇同题对比(2026-10-16~21,Jev 实判)显示本机模型对
|
|
4044
|
-
`AI` 类文章的相关度**系统性低估一档**(该类一致率 20.0%,`低→中` 33 次;政治/文学/新闻 90%+)。
|
|
4045
|
-
`LocalJevClient.TOPIC_RELEVANCE_FLOOR = {'AI': 1}` 只把 `AI` 的 `低` 抬到 `中`:
|
|
4046
|
-
该类 77.5%、总体 73.9% → 84.4%(68 篇复测 80.9%)。统一 +1 只有 82.9% 且会越界,故不采用。
|
|
4047
|
-
这张表是**数据产物**:换语料/换模型要重测,`usage.calibration` 会落盘,便于事后核对。
|
|
4048
|
-
- 收录阈值:Jev 0.5 时收录 32/211;本机同阈值收录 30 篇、判定一致 86.7%;降到 0.35 收录 32 篇、
|
|
4049
|
-
一致率不变 —— 要与 Jev 的"收录量"对齐就把阈值降到 0.35,要更保守就留 0.5。
|
|
4020
|
+
|
|
4021
|
+
## D-127: 决策模型可以走本机(`judgeProvider=local`),概率默认当作未校准
|
|
4022
|
+
|
|
4023
|
+
**Decision.** 新增第四条判断通路 `judgeProvider=local`:`LocalJevClient` 把本机决策模型服务
|
|
4024
|
+
(`decide.py serve`,APUS Reflex 4B + llama-server 的 `/decide`)当 Jev 用,**不需要 key、不联网**。
|
|
4025
|
+
`score` 没有连续分,改用 `choice` 在有序档位上选、把选中档位的下标当分数(下游 `score_to_relevance`
|
|
4026
|
+
本来就是四舍五入取整,语义等价)。
|
|
4027
|
+
|
|
4028
|
+
**Reason.** 原生 `api.typesafe.ai` 在本机被地区封锁(D-120),`chat` 与 `openrouter` 都要把文章正文
|
|
4029
|
+
发给第三方。本机 4B 决策模型在官方 Frozen80 上 **66/80 = 82.50%**、**0.3 s/题**(2026-10-22 本机实测,
|
|
4030
|
+
RTX 5060 8GB),对"主题 / 相关度 / 该不该收录"这类**窄口径、可验证**的判断够用,而正文不出机器。
|
|
4031
|
+
|
|
4032
|
+
**Consequences.**
|
|
4033
|
+
- 本机服务的概率是**候选集上的相对 softmax、未校准**(APUS 契约原文)。`confidence` 只作参考,
|
|
4034
|
+
不能直接当自动放行阈值;要用阈值先在自有样本上标定。
|
|
4035
|
+
- **一个问题一次 HTTP**:原生 `systemone` 一次答一组,本机 `/decide` 一次只答一个 primitive。
|
|
4036
|
+
日报 3 个问题 ⇒ 3 次调用,实测整篇 0.3–0.5 s(本机回环)。
|
|
4037
|
+
- `score` 用 **n-1 次阈值判断**(`达到「中」或「高」吗?`)合成档位,不是三选一:
|
|
4038
|
+
同一批 44 篇(2026-10-16~21,Jev 实判)上,三选一对 Jev 的一致率 45.5%(`低→中` 23 次,
|
|
4039
|
+
系统性偏高),阈值问法 **72.7%**、平均差 0.55 → 0.27 档。**措辞影响巨大**(同一模型同一批样本:
|
|
4040
|
+
纯判据句 59.1%、带主语 + 档位 + 判据 72.7%),改这句话要重测。
|
|
4041
|
+
- 服务没起时抛 `JevError`(调用方按既有降级路径退到 LLM 路径),**不静默改用云端**。
|
|
4042
|
+
- 新增配置键 `judgeLocalUrl`(默认 `http://127.0.0.1:8090`),已登记进 `CliConfig` 与 `config set`。
|
|
4043
|
+
- **按主题的相关度下限(实测校准)**:211 篇同题对比(2026-10-16~21,Jev 实判)显示本机模型对
|
|
4044
|
+
`AI` 类文章的相关度**系统性低估一档**(该类一致率 20.0%,`低→中` 33 次;政治/文学/新闻 90%+)。
|
|
4045
|
+
`LocalJevClient.TOPIC_RELEVANCE_FLOOR = {'AI': 1}` 只把 `AI` 的 `低` 抬到 `中`:
|
|
4046
|
+
该类 77.5%、总体 73.9% → 84.4%(68 篇复测 80.9%)。统一 +1 只有 82.9% 且会越界,故不采用。
|
|
4047
|
+
这张表是**数据产物**:换语料/换模型要重测,`usage.calibration` 会落盘,便于事后核对。
|
|
4048
|
+
- 收录阈值:Jev 0.5 时收录 32/211;本机同阈值收录 30 篇、判定一致 86.7%;降到 0.35 收录 32 篇、
|
|
4049
|
+
一致率不变 —— 要与 Jev 的"收录量"对齐就把阈值降到 0.35,要更保守就留 0.5。
|
|
4050
|
+
|
|
4051
|
+
|
|
4052
|
+
## D-128: 入站图片可以给助手看——它是一类新的出境,门与 D-042 同构
|
|
4053
|
+
|
|
4054
|
+
**Status:** Active
|
|
4055
|
+
|
|
4056
|
+
**Decision.** Bot 通道收到、且已按 `wechatMediaDownload=true` 落盘解密(D-042 的下载侧)的**图片**,
|
|
4057
|
+
助手可以用 `look_at_image` 的 `#信N` 句柄查看。门与 D-042 看聊天图片的那道门**同构、双层**:
|
|
4058
|
+
strict 模式(且非本地推理)下工具拒绝取图、`toApiMessages` 再拦一层;balanced / open 放行;
|
|
4059
|
+
`IMAGE_SENT` / `IMAGE_HELD` 审计与 `MAX_IMAGES_PER_TURN` 预算**共用**,不分来源。语音、视频、
|
|
4060
|
+
文件的消费**不在本决策内**——各自需要转写 / 压缩 / 解析,是另外的出境类别,不搭车。
|
|
4061
|
+
|
|
4062
|
+
**Reason.** 图片消息此前整条被忽略(日志里的「忽略非文本消息 (image)」),而文件其实已经在
|
|
4063
|
+
本机磁盘上、下载本身已是显式 opt-in(`wechatMediaDownload` 默认 false)。但 D-042 立过的规矩
|
|
4064
|
+
仍然成立:「一句关于聊天的话」与「聊天里的那张照片」不是同一种披露——入站图片是**别人发给
|
|
4065
|
+
用户的照片**,比聊天截图又多一层:出境的内容甚至不是用户自己写的。所以决策先行、范围收窄:
|
|
4066
|
+
仅图片、仅 balanced / open(strict + 本地推理等于不出机器,照放)、句柄只在能看时展示
|
|
4067
|
+
(与 `get_messages` 的 `#N` 同一规则——不摆出一个工具必定会拒绝的东西)。
|
|
4068
|
+
|
|
4069
|
+
**Consequences.**
|
|
4070
|
+
- 一条**只有图片**的来信从此会产生一轮(此前被忽略):文本占位「(发来一张图片)」,白名单、
|
|
4071
|
+
配额、串行队列照常走;未开下载或下载失败(`filePath` 为空)的图片消息**仍被忽略**,行为不变。
|
|
4072
|
+
- `#信N` 只指本轮来信自带的图片;`#N` 仍指聊天记录里的 `local_id`。两个句柄空间不混——
|
|
4073
|
+
前者是磁盘上现成的文件,后者要跑 `read_image.py` 建媒体索引,成本差一个数量级。
|
|
4074
|
+
- 图片**不缩放**,超过 4 MB 直接说「太大」:入站文件是证据形态,缩了再给模型看,模型说的
|
|
4075
|
+
就不是那张图(导出路径做缩放是因为那是**给人看**的页面,两者方向相反)。
|
|
4076
|
+
- MIME 从文件头魔数判定(png / jpeg / webp),不信任文件名扩展名——文件名来自服务端报文。
|
|
4077
|
+
- MCP 面不变:`look_at_image` 本来就不在 MCP 表里(文本传输接不到侧信道的图)。
|
|
4078
|
+
- 审计 `IMAGE_SENT` 的 `ids=` 对入站图片为空(没有 local_id)——`n=` 照记,能对上"这轮发了几张"。
|
|
4079
|
+
|
|
4080
|
+
|
|
4081
|
+
## D-129: qchat-cli 的消息从**文件契约**进来,不做进程耦合,也不当第五个消息后端
|
|
4082
|
+
|
|
4083
|
+
**Status:** Active
|
|
4084
|
+
|
|
4085
|
+
**Decision.** `weflow-cli import qchat <文件|目录>` 读 qchat-cli `qce export --format json`
|
|
4086
|
+
的产物,归一化落到本地私有存储 `output/imports/qq/sessions/`(schema `weflow-import/qq-v1`,
|
|
4087
|
+
字段语义与 `weflow-message/v1` 对齐:serverId/createTime/parsedContent/rawContent 与 13 个稳定
|
|
4088
|
+
messageType)。下游两条已落地的管道各开一个**显式开关**:`chat-notes --source qq|all` 产 QQ
|
|
4089
|
+
知识卡(卡片名带 `QQ-` 前缀,与微信卡同一形状、同一消费端),`search-index --include-imports`
|
|
4090
|
+
把 QQ 会话收进语义索引(记录 id 带 `chat:qq:` 段,与微信记录天然不撞;短语索引从语义索引
|
|
4091
|
+
派生,自动跟上)。默认行为逐字不变。
|
|
4092
|
+
|
|
4093
|
+
**Reason.** 两个仓库的数据获取层性质不同(weflow 直读本地加密库、离线可查;qchat 走
|
|
4094
|
+
NapCatQQ/OneBot 在线服务)。让 weflow 直连 OneBot 当第五个 `chatService` 后端,等于把 qchat
|
|
4095
|
+
的整个客户端层复刻进 weflow、两个工具抢同一个轮询——且 `chatService` 的四个后端全是"解本地
|
|
4096
|
+
库"的,混进一个在线 API 客户端会改变这层的性质。文件契约让边界可枚举、可版本化,也正是
|
|
4097
|
+
ROADMAP P1 给 she-love-me 预留的同一条路;qchat-cli 因此**一行不用改**。
|
|
4098
|
+
|
|
4099
|
+
**Consequences.**
|
|
4100
|
+
- QQ 的 `messageId` 是服务端全局唯一:去重/合并按它做,**不需要**微信侧那套重叠时间窗;
|
|
4101
|
+
对 D-027 而言 qq 来源天然满足 stable-identity 判据。
|
|
4102
|
+
- `isSend` 只在导入时给了 `--own-id` 才写;缺省就是未知,消费方按未知处理(知识卡的行
|
|
4103
|
+
标签落到群名片/昵称)。不为"好看"猜方向。
|
|
4104
|
+
- 存储是 **weflow 的私有形状**,不承诺稳定;对外承诺的边界是 `weflow-message/v1` 那套
|
|
4105
|
+
字段语义。将来 qchat 侧产出中立契约(如 `im-message/v1`)时,读取器多认一个 schema 即可。
|
|
4106
|
+
- 查询层(`sessions`/`messages` 按 QQ 会话直查、per-source 路由)**明确不在本决策内**:
|
|
4107
|
+
等真实需求出现再做,届时 QQ 号与 wxid 的命名空间隔离(`qq:` 前缀已备好)是第一件事。
|
|
4108
|
+
- 隐私边界对齐:导入的是用户本人授权的 QQ 数据(qchat 侧靠 OneBot 授权),落盘后继承
|
|
4109
|
+
weflow 的纪律——QQ 号与 wxid 同级敏感;知识卡与索引沿用各自既有的出境确认门。
|
|
4110
|
+
- `--source all` 在微信库不可用时**降级**为只处理 QQ(说一声,不是静默);`build
|
|
4111
|
+
--include-imports` 在没有微信库的机器上也能建索引(纯 QQ 用户是真实场景)。
|