weflow-cli 1.7.0 → 1.8.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.
Files changed (194) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/CHANGELOG.md +1949 -1
  3. package/OPERATIONS.md +774 -1
  4. package/README.en.md +93 -7
  5. package/README.md +31 -6
  6. package/bin/weflow-cli.ts +934 -27
  7. package/dist/bin/weflow-cli.js +952 -27
  8. package/dist/bin/weflow-cli.js.map +1 -1
  9. package/dist/mcp-server/index.js +39 -13
  10. package/dist/mcp-server/index.js.map +1 -1
  11. package/dist/src/core/appMsgFormat.d.ts +37 -0
  12. package/dist/src/core/appMsgFormat.d.ts.map +1 -0
  13. package/dist/src/core/appMsgFormat.js +106 -0
  14. package/dist/src/core/appMsgFormat.js.map +1 -0
  15. package/dist/src/core/ntCore.d.ts +19 -2
  16. package/dist/src/core/ntCore.d.ts.map +1 -1
  17. package/dist/src/core/ntCore.js +11 -1
  18. package/dist/src/core/ntCore.js.map +1 -1
  19. package/dist/src/core/sqlcipherCore.d.ts +0 -12
  20. package/dist/src/core/sqlcipherCore.d.ts.map +1 -1
  21. package/dist/src/core/sqlcipherCore.js +5 -64
  22. package/dist/src/core/sqlcipherCore.js.map +1 -1
  23. package/dist/src/core/wcdbCore.d.ts +1 -1
  24. package/dist/src/core/wcdbCore.d.ts.map +1 -1
  25. package/dist/src/core/wcdbCore.js +5 -1
  26. package/dist/src/core/wcdbCore.js.map +1 -1
  27. package/dist/src/panel/client.d.ts +25 -0
  28. package/dist/src/panel/client.d.ts.map +1 -0
  29. package/dist/src/panel/client.js +34 -0
  30. package/dist/src/panel/client.js.map +1 -0
  31. package/dist/src/panel/endpoint.d.ts +45 -0
  32. package/dist/src/panel/endpoint.d.ts.map +1 -0
  33. package/dist/src/panel/endpoint.js +132 -0
  34. package/dist/src/panel/endpoint.js.map +1 -0
  35. package/dist/src/panel/launch.d.ts +50 -0
  36. package/dist/src/panel/launch.d.ts.map +1 -0
  37. package/dist/src/panel/launch.js +74 -0
  38. package/dist/src/panel/launch.js.map +1 -0
  39. package/dist/src/panel/server.d.ts +23 -0
  40. package/dist/src/panel/server.d.ts.map +1 -0
  41. package/dist/src/panel/server.js +413 -0
  42. package/dist/src/panel/server.js.map +1 -0
  43. package/dist/src/panel/userId.d.ts +37 -0
  44. package/dist/src/panel/userId.d.ts.map +1 -0
  45. package/dist/src/panel/userId.js +56 -0
  46. package/dist/src/panel/userId.js.map +1 -0
  47. package/dist/src/services/assistantDaemon.d.ts +20 -3
  48. package/dist/src/services/assistantDaemon.d.ts.map +1 -1
  49. package/dist/src/services/assistantDaemon.js +46 -9
  50. package/dist/src/services/assistantDaemon.js.map +1 -1
  51. package/dist/src/services/assistantEval.d.ts +141 -0
  52. package/dist/src/services/assistantEval.d.ts.map +1 -0
  53. package/dist/src/services/assistantEval.js +497 -0
  54. package/dist/src/services/assistantEval.js.map +1 -0
  55. package/dist/src/services/assistantMemory.d.ts +106 -4
  56. package/dist/src/services/assistantMemory.d.ts.map +1 -1
  57. package/dist/src/services/assistantMemory.js +396 -61
  58. package/dist/src/services/assistantMemory.js.map +1 -1
  59. package/dist/src/services/assistantPrivacy.d.ts +15 -2
  60. package/dist/src/services/assistantPrivacy.d.ts.map +1 -1
  61. package/dist/src/services/assistantPrivacy.js +18 -4
  62. package/dist/src/services/assistantPrivacy.js.map +1 -1
  63. package/dist/src/services/assistantRouter.d.ts +41 -0
  64. package/dist/src/services/assistantRouter.d.ts.map +1 -0
  65. package/dist/src/services/assistantRouter.js +151 -0
  66. package/dist/src/services/assistantRouter.js.map +1 -0
  67. package/dist/src/services/assistantService.d.ts +209 -3
  68. package/dist/src/services/assistantService.d.ts.map +1 -1
  69. package/dist/src/services/assistantService.js +590 -75
  70. package/dist/src/services/assistantService.js.map +1 -1
  71. package/dist/src/services/assistantTools.d.ts +205 -1
  72. package/dist/src/services/assistantTools.d.ts.map +1 -1
  73. package/dist/src/services/assistantTools.js +1089 -58
  74. package/dist/src/services/assistantTools.js.map +1 -1
  75. package/dist/src/services/assistantTrace.d.ts +47 -0
  76. package/dist/src/services/assistantTrace.d.ts.map +1 -0
  77. package/dist/src/services/assistantTrace.js +150 -0
  78. package/dist/src/services/assistantTrace.js.map +1 -0
  79. package/dist/src/services/chatService.d.ts +1 -1
  80. package/dist/src/services/chatService.d.ts.map +1 -1
  81. package/dist/src/services/chatService.js +5 -2
  82. package/dist/src/services/chatService.js.map +1 -1
  83. package/dist/src/services/configService.d.ts +9 -0
  84. package/dist/src/services/configService.d.ts.map +1 -1
  85. package/dist/src/services/configService.js +15 -8
  86. package/dist/src/services/configService.js.map +1 -1
  87. package/dist/src/services/pythonBridge.d.ts +41 -0
  88. package/dist/src/services/pythonBridge.d.ts.map +1 -0
  89. package/dist/src/services/pythonBridge.js +119 -0
  90. package/dist/src/services/pythonBridge.js.map +1 -0
  91. package/dist/src/services/syncService.d.ts +8 -2
  92. package/dist/src/services/syncService.d.ts.map +1 -1
  93. package/dist/src/services/syncService.js +26 -7
  94. package/dist/src/services/syncService.js.map +1 -1
  95. package/dist/src/services/wechatMessageService.d.ts +25 -0
  96. package/dist/src/services/wechatMessageService.d.ts.map +1 -1
  97. package/dist/src/services/wechatMessageService.js +69 -7
  98. package/dist/src/services/wechatMessageService.js.map +1 -1
  99. package/dist/src/utils/atomicWrite.d.ts +4 -0
  100. package/dist/src/utils/atomicWrite.d.ts.map +1 -0
  101. package/dist/src/utils/atomicWrite.js +60 -0
  102. package/dist/src/utils/atomicWrite.js.map +1 -0
  103. package/dist/src/utils/dateRange.d.ts +15 -0
  104. package/dist/src/utils/dateRange.d.ts.map +1 -1
  105. package/dist/src/utils/dateRange.js +47 -0
  106. package/dist/src/utils/dateRange.js.map +1 -1
  107. package/dist/src/utils/text.d.ts +18 -0
  108. package/dist/src/utils/text.d.ts.map +1 -0
  109. package/dist/src/utils/text.js +20 -0
  110. package/dist/src/utils/text.js.map +1 -0
  111. package/docs/AI_INTERFACE.md +2 -0
  112. package/docs/DECISIONS.md +1424 -0
  113. package/docs/EXTENDING.md +187 -0
  114. package/docs/JEV/345/272/224/347/224/250/346/226/275/345/267/245/346/226/207/346/241/243.md +310 -0
  115. package/docs/MCP.md +23 -4
  116. package/docs/PROJECT_STATE.md +299 -98
  117. package/docs/SETUP.md +22 -0
  118. package/docs/SYNC_CONTRACT.md +31 -3
  119. package/mcp-server/index.ts +681 -657
  120. package/package.json +84 -83
  121. package/resources/panel/ball-position.cjs +153 -0
  122. package/resources/panel/index.html +46 -0
  123. package/resources/panel/main.cjs +447 -0
  124. package/resources/panel/mascot-focus.png +0 -0
  125. package/resources/panel/mascot-happy.png +0 -0
  126. package/resources/panel/mascot-sorry.png +0 -0
  127. package/resources/panel/mascot-tired.png +0 -0
  128. package/resources/panel/mascot.png +0 -0
  129. package/resources/panel/package.json +7 -0
  130. package/resources/panel/panel.css +407 -0
  131. package/resources/panel/preload.cjs +72 -0
  132. package/resources/panel/quick-menu.cjs +197 -0
  133. package/resources/panel/renderer.js +441 -0
  134. package/resources/panel/tray-menu.cjs +40 -0
  135. package/resources/panel/tray.png +0 -0
  136. package/scripts/_utils.py +403 -54
  137. package/scripts/annual_report.py +4 -2
  138. package/scripts/article_notes.py +423 -0
  139. package/scripts/backfill_articles.py +438 -0
  140. package/scripts/biz_daily.py +590 -105
  141. package/scripts/chat_notes.py +470 -0
  142. package/scripts/classify_daily.py +10 -18
  143. package/scripts/compile_wiki.py +1059 -46
  144. package/scripts/create_reading_notes.py +199 -14
  145. package/scripts/decide.py +253 -0
  146. package/scripts/draft_reply.py +671 -0
  147. package/scripts/export_chat_html.py +35 -33
  148. package/scripts/extract_todos.py +31 -5
  149. package/scripts/fav_notes.py +318 -0
  150. package/scripts/fix_topics.py +2 -1
  151. package/scripts/generate_ai_report.py +214 -9
  152. package/scripts/generate_html.py +18 -3
  153. package/scripts/generate_review.py +2 -2
  154. package/scripts/jev_client.py +294 -0
  155. package/scripts/jev_probe.py +440 -0
  156. package/scripts/nt_common.py +82 -0
  157. package/scripts/nt_decrypt.py +1755 -1460
  158. package/scripts/pipeline.py +27 -2
  159. package/scripts/promote_ideas.py +19 -4
  160. package/scripts/quality_eval.py +707 -0
  161. package/scripts/rag_chat.py +4 -4
  162. package/scripts/read_image.py +187 -0
  163. package/scripts/reply_debt.py +488 -0
  164. package/scripts/route_cards.py +453 -0
  165. package/scripts/semantic_search.py +208 -49
  166. package/scripts/user_notes.py +315 -0
  167. package/scripts/vault_rag.py +13 -7
  168. package/scripts/vault_search.py +21 -4
  169. package/scripts/wiki_lint.py +397 -0
  170. package/src/core/appMsgFormat.ts +117 -0
  171. package/src/core/ntCore.ts +27 -2
  172. package/src/core/sqlcipherCore.ts +5 -61
  173. package/src/core/wcdbCore.ts +6 -2
  174. package/src/panel/client.ts +49 -0
  175. package/src/panel/endpoint.ts +144 -0
  176. package/src/panel/launch.ts +101 -0
  177. package/src/panel/server.ts +424 -0
  178. package/src/panel/userId.ts +72 -0
  179. package/src/services/assistantDaemon.ts +57 -9
  180. package/src/services/assistantEval.ts +611 -0
  181. package/src/services/assistantMemory.ts +413 -57
  182. package/src/services/assistantPrivacy.ts +18 -4
  183. package/src/services/assistantRouter.ts +191 -0
  184. package/src/services/assistantService.ts +671 -74
  185. package/src/services/assistantTools.ts +1118 -57
  186. package/src/services/assistantTrace.ts +175 -0
  187. package/src/services/chatService.ts +6 -2
  188. package/src/services/configService.ts +403 -385
  189. package/src/services/pythonBridge.ts +139 -0
  190. package/src/services/syncService.ts +37 -9
  191. package/src/services/wechatMessageService.ts +73 -7
  192. package/src/utils/atomicWrite.ts +56 -0
  193. package/src/utils/dateRange.ts +46 -0
  194. package/src/utils/text.ts +20 -0
@@ -18,7 +18,7 @@ RAG 智能聊天助手 — 基于语义搜索 + AI 引擎的对话式知识检
18
18
  import sys, os, json
19
19
 
20
20
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
21
- from _utils import load_config, create_engine
21
+ from _utils import load_config, create_engine, get_api_key, get_dashscope_key
22
22
  from semantic_search import search as semantic_search, build_index, INDEX_DIR, VECTORS_FILE
23
23
 
24
24
  PROMPT_TEMPLATE = """你是一个个人知识助手,可以访问用户的微信聊天记录和公众号文章。
@@ -168,11 +168,11 @@ def main():
168
168
  talker = args.talker or os.environ.get('WEFLOW_RAG_TALKER', '') or None
169
169
 
170
170
  config = load_config()
171
- embed_key = os.environ.get('DASHSCOPE_API_KEY', '') or config.get('dashscopeApiKey', '')
172
- chat_key = args.api_key or os.environ.get('DEEPSEEK_API_KEY', '') or config.get('deepseekApiKey', '')
171
+ embed_key = os.environ.get('DASHSCOPE_API_KEY', '') or get_dashscope_key(config)
172
+ chat_key = args.api_key or os.environ.get('DEEPSEEK_API_KEY', '') or get_api_key(config)
173
173
 
174
174
  if not embed_key:
175
- print('[ERROR] 缺少 Embedding API key。请在 ~/.weflow-cli/config.json 中设置 dashscopeApiKey')
175
+ print('[ERROR] 缺少 Embedding API key。设置:weflow-cli config set dashscopeApiKey "..."')
176
176
  sys.exit(1)
177
177
  if not chat_key:
178
178
  print('[ERROR] 缺少 AI API key。请在 ~/.weflow-cli/config.json 中设置 deepseekApiKey')
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env python3
2
+ """取一张聊天图片:本机解密、缩小、给出 base64(给助手的 `look_at_image` 用)。
3
+
4
+ 用法: read_image.py --talker <username> --local-id <id> [--json]
5
+
6
+ 输出(JSON 在最后一行):
7
+ {"success": true, "b64": "...", "mime": "image/jpeg", "width": 720, "height": 431, ...}
8
+ {"success": true, "reason": "...", "hint": "..."} # 取不到,且说清是哪一种取不到
9
+ {"success": false, "error": "..."} # 配置/解密层面的错
10
+
11
+ **为什么是独立脚本,而不是让助手进程 import 导出模块**:媒体索引是每次重建的(实测单聊
12
+ 约 2 秒、一个万级文件的大群 18–24 秒),而导出模块的缩略 memo 是模块级全局、没有淘汰。
13
+ 一次一个子进程,代价是重建索引,换来的是内存不随常驻时长增长。
14
+
15
+ **只做一件事**:把某条消息的图片取出来。找不到就说找不到——不猜、不漏。
16
+
17
+ 怎么找:**不重写**导出侧已经写好、且已被真实数据验证过的那套(`.dat` v2 解密、会话缓存
18
+ 索引、账号媒体索引、`MessageResourceInfo` 的 md5 映射),只按实测出来的顺序调它们。下面
19
+ 这三条是实测出来的,不是文档里写的:
20
+
21
+ 1. `resource_md5s` 是**承重的**:给了 50/50 能取到,不给 0/50(群聊);
22
+ 2. `shrink_embedded` 不传 `force=True` 是**空操作**(它默认只在 `--full-images` 下才缩);
23
+ 3. `resolve_v2_media_key` 依赖**微信在运行**。微信关着时它返回 None,于是绝大多数 `.dat`
24
+ 缩略图会**静默地**掉出索引。"这张图本机没有"与"我现在看不到"是两回事,所以下面把
25
+ 这两种情形分开报。
26
+ """
27
+ import argparse
28
+ import hashlib
29
+ import json
30
+ import os
31
+ import sys
32
+
33
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
34
+ sys.path.insert(0, SCRIPT_DIR)
35
+
36
+ import _utils # noqa: E402
37
+ import export_chat_html as E # noqa: E402
38
+
39
+ # 取到的图放在仓库的 output/.cache 下(与抓取缓存同一层),键是 (talker, local_id)。
40
+ # 只缓存**取到的**图:取不到可能是"微信没在运行"这种会变的状态,缓存住就永远取不到了。
41
+ CACHE_DIR = os.path.join(SCRIPT_DIR, 'output', '.cache', 'read-image')
42
+ MAX_SIDE = 720
43
+
44
+
45
+ def _ok(payload):
46
+ print(json.dumps(dict(success=True, **payload), ensure_ascii=False))
47
+ return 0
48
+
49
+
50
+ def _fail(message):
51
+ print(json.dumps({'success': False, 'error': message}, ensure_ascii=False))
52
+ return 1
53
+
54
+
55
+ def _cache_path(talker, local_id):
56
+ return os.path.join(CACHE_DIR, hashlib.md5(talker.encode()).hexdigest(), '%d.json' % local_id)
57
+
58
+
59
+ def _read_cache(path):
60
+ try:
61
+ with open(path, encoding='utf-8') as fh:
62
+ data = json.load(fh)
63
+ except Exception:
64
+ return None
65
+ return data if data.get('b64') else None
66
+
67
+
68
+ def _write_cache(path, data):
69
+ try:
70
+ os.makedirs(os.path.dirname(path), exist_ok=True)
71
+ tmp = path + '.tmp'
72
+ with open(tmp, 'w', encoding='utf-8', newline='') as fh:
73
+ json.dump(data, fh)
74
+ os.replace(tmp, path)
75
+ except Exception:
76
+ pass # 缓存写不进去不影响这一次取图
77
+
78
+
79
+ def _dimensions(b64_bytes):
80
+ """图的长宽。取不到就回 (None, None)——尺寸是附带信息,不是这条路的成败条件。"""
81
+ try:
82
+ import base64
83
+ import io
84
+ from PIL import Image
85
+ with Image.open(io.BytesIO(base64.b64decode(b64_bytes))) as img:
86
+ return img.width, img.height
87
+ except Exception:
88
+ return None, None
89
+
90
+
91
+ def _passphrase(config):
92
+ for name in ('favPassphrase', 'decryptKey'):
93
+ value = config.get(name) or ''
94
+ if not value:
95
+ continue
96
+ try:
97
+ value = _utils.decrypt_lock(value)
98
+ except Exception:
99
+ continue
100
+ if value:
101
+ return value
102
+ return ''
103
+
104
+
105
+ def main():
106
+ parser = argparse.ArgumentParser(description='取一张聊天图片')
107
+ parser.add_argument('--talker', required=True, help='会话的 username(不是显示名)')
108
+ parser.add_argument('--local-id', required=True, type=int, help='消息的 local_id')
109
+ parser.add_argument('--json', action='store_true', help='输出 JSON(本脚本只输出 JSON)')
110
+ args = parser.parse_args()
111
+
112
+ cache_file = _cache_path(args.talker, args.local_id)
113
+ cached = _read_cache(cache_file)
114
+ if cached:
115
+ cached['cached'] = True
116
+ print(json.dumps(cached, ensure_ascii=False))
117
+ return 0
118
+
119
+ config = _utils.load_config()
120
+ db_path = config.get('ntDbPath') or ''
121
+ if not db_path or not os.path.exists(db_path):
122
+ return _fail('配置里没有可用的 ntDbPath')
123
+ try:
124
+ key_hex = _utils.decrypt_lock(config.get('ntKey', ''))
125
+ except Exception as exc:
126
+ return _fail('解不出数据库密钥: %s' % exc)
127
+ salt = config.get('ntSalt', '')
128
+ passphrase = _passphrase(config)
129
+
130
+ account_dir = os.path.dirname(os.path.dirname(os.path.dirname(db_path)))
131
+ cache_dir = os.path.join(account_dir, 'cache')
132
+ own_wxid = E.clean_account_wxid(config.get('wxid', ''))
133
+
134
+ try:
135
+ messages = E.fetch_messages_from_shards(db_path, key_hex, salt, args.talker, '', passphrase)
136
+ except Exception as exc:
137
+ return _fail('读消息失败: %s' % exc)
138
+
139
+ row = next((r for r in messages if int(r[0] or 0) == args.local_id), None)
140
+ if row is None:
141
+ return _ok({'reason': '这个会话里没有编号 #%d 的消息' % args.local_id})
142
+ if int(row[2] or 0) != 3:
143
+ return _ok({'reason': '编号 #%d 不是图片(消息类型 %s)' % (args.local_id, row[2])})
144
+
145
+ server_id = row[1] or 0
146
+ create_time = row[5] or 0
147
+ content = row[8] or ''
148
+
149
+ image_map = E.scan_nt_cache(cache_dir, args.talker, account_dir, own_wxid)
150
+ account_media = E.scan_account_media(account_dir, own_wxid, args.talker)
151
+ for media_key, media in account_media.items():
152
+ if media_key in image_map:
153
+ continue
154
+ if len(media[0]) > E.EMBED_SHRINK_THRESHOLD:
155
+ media = E.shrink_embedded(media, max_side=MAX_SIDE, force=True)
156
+ image_map[media_key] = media
157
+
158
+ resource_map = {}
159
+ try:
160
+ resource_map = E.load_resource_media_map(account_dir, key_hex, salt, messages, image_map, passphrase)
161
+ except Exception:
162
+ resource_map = {} # 缺它只是取到率下降(群聊明显),不该让整次调用失败
163
+
164
+ hit = E.resolve_media(image_map, args.local_id, create_time, content,
165
+ resource_map.get('server:%s' % server_id), server_id)
166
+ if not hit:
167
+ hint = ''
168
+ try:
169
+ if E.resolve_v2_media_key(account_dir, own_wxid) is None:
170
+ hint = ('取缩略图密钥需要微信在运行;微信关着时这类图会看不到,'
171
+ '这不是"本机没有这张图"')
172
+ except Exception:
173
+ pass
174
+ return _ok({'reason': '本机没有这张图的副本', 'hint': hint})
175
+
176
+ b64, mime = E.shrink_embedded((hit[0], hit[1]), max_side=MAX_SIDE, force=True)
177
+ width, height = _dimensions(b64)
178
+ payload = {'b64': b64, 'mime': mime or 'image/jpeg', 'bytes': len(b64),
179
+ 'width': width, 'height': height, 'cached': False}
180
+ _write_cache(cache_file, payload)
181
+ print(json.dumps(dict(success=True, **payload), ensure_ascii=False))
182
+ return 0
183
+
184
+
185
+ if __name__ == '__main__':
186
+ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
187
+ sys.exit(main())