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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
' Silent launcher for the WeFlow daily task.
|
|
2
|
+
'
|
|
3
|
+
' Why this file exists: a scheduled action that runs "powershell.exe -File ..." starts a
|
|
4
|
+
' CONSOLE program, so Windows creates a console window before PowerShell can hide it - you
|
|
5
|
+
' see a black box appear, and it is especially intrusive over a full-screen game. wscript.exe
|
|
6
|
+
' is a GUI process with no console at all, and WshShell.Run with window style 0 keeps the
|
|
7
|
+
' child hidden too.
|
|
8
|
+
'
|
|
9
|
+
' Arguments are forwarded verbatim, so "run_hidden.vbs noon" is the noon pass.
|
|
10
|
+
'
|
|
11
|
+
' ASCII only on purpose: Windows Script Host reads this file with the ANSI code page.
|
|
12
|
+
|
|
13
|
+
Dim fso, here, script, cmd, sh
|
|
14
|
+
Set fso = CreateObject("Scripting.FileSystemObject")
|
|
15
|
+
here = fso.GetParentFolderName(WScript.ScriptFullName)
|
|
16
|
+
script = fso.BuildPath(here, "daily_task.ps1")
|
|
17
|
+
|
|
18
|
+
cmd = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & script & """"
|
|
19
|
+
If WScript.Arguments.Count > 0 Then
|
|
20
|
+
cmd = cmd & " " & WScript.Arguments(0)
|
|
21
|
+
End If
|
|
22
|
+
|
|
23
|
+
Set sh = CreateObject("WScript.Shell")
|
|
24
|
+
' 0 = hidden window, True = wait for the child so the task's last result reflects the run.
|
|
25
|
+
sh.Run cmd, 0, True
|
|
@@ -268,6 +268,36 @@ def collect_chat_messages(conns, name_map, days=90, per_talker=500):
|
|
|
268
268
|
return items
|
|
269
269
|
|
|
270
270
|
|
|
271
|
+
def collect_imported_chat_items(days=90):
|
|
272
|
+
"""导入的 QQ 会话 → 与微信聊天同形状的索引记录(--include-imports)。
|
|
273
|
+
|
|
274
|
+
形状必须与 collect_chat_messages 逐字段一致(id/type/talker/sender/time/text),
|
|
275
|
+
否则同一条记录在两半索引里语义不同。id 带 qq: 段:与微信记录的
|
|
276
|
+
chat:<talker>:<ts>:<rid> 天然不撞,也永远可分辨来源。文本阈值(>=10 字)与
|
|
277
|
+
截断(500 字)同微信侧——索引是要花钱嵌的,两侧口径不同就是两种相关性。
|
|
278
|
+
"""
|
|
279
|
+
import imported_chats
|
|
280
|
+
|
|
281
|
+
items = []
|
|
282
|
+
for sess in imported_chats.sessions():
|
|
283
|
+
meta = sess.get('session') or {}
|
|
284
|
+
talker = 'QQ-%s' % (meta.get('name') or meta.get('id') or '未命名')
|
|
285
|
+
for m in imported_chats.session_messages(sess, days=days):
|
|
286
|
+
text = (m.get('parsedContent') or '').strip()
|
|
287
|
+
if len(text) < 10:
|
|
288
|
+
continue
|
|
289
|
+
dt = datetime.fromtimestamp(m.get('createTime') or 0, tz=TZ)
|
|
290
|
+
items.append({
|
|
291
|
+
'id': 'chat:qq:%s:%s' % (meta.get('id') or '', m.get('serverId') or ''),
|
|
292
|
+
'type': 'chat',
|
|
293
|
+
'talker': talker,
|
|
294
|
+
'sender': m.get('senderName') or '',
|
|
295
|
+
'time': dt.strftime('%Y-%m-%d %H:%M'),
|
|
296
|
+
'text': text[:500],
|
|
297
|
+
})
|
|
298
|
+
return items
|
|
299
|
+
|
|
300
|
+
|
|
271
301
|
_ARTICLE_BOILERPLATE = frozenset({
|
|
272
302
|
'在小说阅读器读本章', '去阅读', '在公众号小说中沉浸阅读',
|
|
273
303
|
})
|
|
@@ -396,7 +426,7 @@ def _save_index(base_vectors, base_meta, pairs):
|
|
|
396
426
|
os.replace(tmp_m, META_FILE)
|
|
397
427
|
return len(meta)
|
|
398
428
|
|
|
399
|
-
def build_index(api_key: str, full: bool = False, days: int = 90, article_days: int = 30):
|
|
429
|
+
def build_index(api_key: str, full: bool = False, days: int = 90, article_days: int = 30, include_imports: bool = False):
|
|
400
430
|
"""Build or update the semantic index.
|
|
401
431
|
|
|
402
432
|
`days` / `article_days` 决定这个索引**记多久**:它们原先写死在两处调用里
|
|
@@ -421,8 +451,12 @@ def build_index(api_key: str, full: bool = False, days: int = 90, article_days:
|
|
|
421
451
|
# Load DB
|
|
422
452
|
config = load_config()
|
|
423
453
|
nt_db = config.get('ntDbPath', '')
|
|
424
|
-
|
|
454
|
+
# --include-imports 时没有微信库也能建索引(纯 QQ 用户是真实场景);
|
|
455
|
+
# 但要说出"聊天半边只有导入的",不然窗口行读起来像微信也收了。
|
|
456
|
+
if not nt_db and not include_imports:
|
|
425
457
|
return {"error": "未初始化,请先运行 weflow-cli init"}
|
|
458
|
+
if not nt_db and include_imports:
|
|
459
|
+
print('[WARN] 未配置微信库:聊天半边只用导入的会话', file=sys.stderr)
|
|
426
460
|
|
|
427
461
|
nt_key = decrypt_lock(config.get('ntKey', ''))
|
|
428
462
|
nt_salt = config.get('ntSalt', '')
|
|
@@ -439,6 +473,16 @@ def build_index(api_key: str, full: bool = False, days: int = 90, article_days:
|
|
|
439
473
|
# Collect items
|
|
440
474
|
items = []
|
|
441
475
|
shards_failed = []
|
|
476
|
+
if not nt_db:
|
|
477
|
+
# 没有微信库:跳过 NT 那半(name_map/分片都不存在),导入与文章照收。
|
|
478
|
+
# 收完直接走同一个收尾(嵌入与写盘),不在这里复制粘贴后半段。
|
|
479
|
+
if include_imports:
|
|
480
|
+
items.extend(collect_imported_chat_items(days=days))
|
|
481
|
+
items.extend(collect_articles(article_days=article_days))
|
|
482
|
+
new_items = [item for item in items if item['id'] not in existing_ids]
|
|
483
|
+
print(f"总数据: {len(items)} 条, 新数据: {len(new_items)} 条", file=sys.stderr)
|
|
484
|
+
return _build_embed_and_write(api_key, items, new_items, existing_ids,
|
|
485
|
+
full, days, article_days, shards_failed=[])
|
|
442
486
|
try:
|
|
443
487
|
import nt_decrypt # 懒加载:纯逻辑路径(关键词/重排/测试)不该被它拖住
|
|
444
488
|
passphrase = decrypt_lock(config.get('favPassphrase') or config.get('decryptKey') or '')
|
|
@@ -460,13 +504,29 @@ def build_index(api_key: str, full: bool = False, days: int = 90, article_days:
|
|
|
460
504
|
except Exception as e:
|
|
461
505
|
print(f"[WARN] 聊天消息收集失败: {e}", file=sys.stderr)
|
|
462
506
|
|
|
507
|
+
if include_imports:
|
|
508
|
+
imported = collect_imported_chat_items(days=days)
|
|
509
|
+
items.extend(imported)
|
|
510
|
+
print(f"导入会话: +{len(imported)} 条", file=sys.stderr)
|
|
511
|
+
|
|
463
512
|
article_items = collect_articles(article_days=article_days)
|
|
464
513
|
items.extend(article_items)
|
|
465
514
|
|
|
466
|
-
# Filter new items
|
|
467
515
|
new_items = [item for item in items if item['id'] not in existing_ids]
|
|
468
516
|
print(f"总数据: {len(items)} 条, 新数据: {len(new_items)} 条", file=sys.stderr)
|
|
469
517
|
|
|
518
|
+
return _build_embed_and_write(api_key, items, new_items, existing_ids,
|
|
519
|
+
full, days, article_days, shards_failed)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _build_embed_and_write(api_key, items, new_items, existing_ids, full,
|
|
524
|
+
days, article_days, shards_failed):
|
|
525
|
+
"""build_index 的收尾:去重过滤在调用方做完了,这里管嵌入、断点落盘与返回。
|
|
526
|
+
|
|
527
|
+
抽出来是因为聊天半边现在有两条收集路(微信库 / 导入会话),尾巴必须只有一份——
|
|
528
|
+
复制一份的代价是断点续跑、全零批次不落盘这些坑只在一半路径上修过。
|
|
529
|
+
"""
|
|
470
530
|
if not new_items:
|
|
471
531
|
return {"status": "up_to_date", "total": len(items),
|
|
472
532
|
"chatDays": days, "articleDays": article_days,
|
|
@@ -1522,11 +1582,15 @@ def main():
|
|
|
1522
1582
|
p = subparsers.add_parser('build')
|
|
1523
1583
|
p.add_argument('--api-key', help='DeepSeek API key(优先从 config 读取)')
|
|
1524
1584
|
p.add_argument('--full', action='store_true', help='全量重建')
|
|
1585
|
+
p.add_argument('--include-imports', action='store_true',
|
|
1586
|
+
help='把 weflow-cli import qchat 导入的 QQ 会话也收进索引(聊天半边;id 带 qq: 段)')
|
|
1525
1587
|
add_window_args(p)
|
|
1526
1588
|
|
|
1527
1589
|
# update
|
|
1528
1590
|
p = subparsers.add_parser('update')
|
|
1529
1591
|
p.add_argument('--api-key', help='DeepSeek API key(优先从 config 读取)')
|
|
1592
|
+
p.add_argument('--include-imports', action='store_true',
|
|
1593
|
+
help='同 build:把导入的 QQ 会话收进增量')
|
|
1530
1594
|
add_window_args(p)
|
|
1531
1595
|
|
|
1532
1596
|
# search
|
|
@@ -1582,9 +1646,13 @@ def main():
|
|
|
1582
1646
|
or get_dashscope_key(config))
|
|
1583
1647
|
|
|
1584
1648
|
if args.command == 'build':
|
|
1585
|
-
result = build_index(api_key, full=args.full, days=args.days,
|
|
1649
|
+
result = build_index(api_key, full=args.full, days=args.days,
|
|
1650
|
+
article_days=args.article_days,
|
|
1651
|
+
include_imports=getattr(args, 'include_imports', False))
|
|
1586
1652
|
elif args.command == 'update':
|
|
1587
|
-
result = build_index(api_key, full=False, days=args.days,
|
|
1653
|
+
result = build_index(api_key, full=False, days=args.days,
|
|
1654
|
+
article_days=args.article_days,
|
|
1655
|
+
include_imports=getattr(args, 'include_imports', False))
|
|
1588
1656
|
elif args.command == 'phrase-build':
|
|
1589
1657
|
result = build_phrase_index(line=args.line)
|
|
1590
1658
|
result['success'] = 'error' not in result
|