weflow-cli 1.5.0 → 1.6.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/ARCHITECTURE.md +105 -0
- package/CHANGELOG.md +110 -0
- package/CONTRIBUTING.md +45 -0
- package/LICENSE +21 -21
- package/OPERATIONS.md +272 -0
- package/README.en.md +281 -0
- package/README.md +333 -149
- package/SECURITY.md +47 -0
- package/bin/weflow-cli.ts +4032 -583
- package/dist/bin/weflow-cli.js +4296 -593
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/mcp-server/index.js +178 -236
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/src/core/dbPathService.d.ts +15 -1
- package/dist/src/core/dbPathService.d.ts.map +1 -1
- package/dist/src/core/dbPathService.js +177 -19
- package/dist/src/core/dbPathService.js.map +1 -1
- package/dist/src/core/keyService.d.ts +8 -0
- package/dist/src/core/keyService.d.ts.map +1 -1
- package/dist/src/core/keyService.js +127 -22
- package/dist/src/core/keyService.js.map +1 -1
- package/dist/src/core/ntCore.d.ts +59 -1
- package/dist/src/core/ntCore.d.ts.map +1 -1
- package/dist/src/core/ntCore.js +188 -64
- package/dist/src/core/ntCore.js.map +1 -1
- package/dist/src/core/sqlcipherCore.d.ts.map +1 -1
- package/dist/src/core/sqlcipherCore.js +18 -1
- package/dist/src/core/sqlcipherCore.js.map +1 -1
- package/dist/src/core/wcdbCore.d.ts.map +1 -1
- package/dist/src/core/wcdbCore.js +3 -6
- package/dist/src/core/wcdbCore.js.map +1 -1
- package/dist/src/services/assistantDaemon.d.ts +19 -0
- package/dist/src/services/assistantDaemon.d.ts.map +1 -0
- package/dist/src/services/assistantDaemon.js +117 -0
- package/dist/src/services/assistantDaemon.js.map +1 -0
- package/dist/src/services/assistantMemory.d.ts +36 -0
- package/dist/src/services/assistantMemory.d.ts.map +1 -0
- package/dist/src/services/assistantMemory.js +182 -0
- package/dist/src/services/assistantMemory.js.map +1 -0
- package/dist/src/services/assistantPrivacy.d.ts +25 -0
- package/dist/src/services/assistantPrivacy.d.ts.map +1 -0
- package/dist/src/services/assistantPrivacy.js +77 -0
- package/dist/src/services/assistantPrivacy.js.map +1 -0
- package/dist/src/services/assistantRouting.d.ts +18 -0
- package/dist/src/services/assistantRouting.d.ts.map +1 -0
- package/dist/src/services/assistantRouting.js +68 -0
- package/dist/src/services/assistantRouting.js.map +1 -0
- package/dist/src/services/assistantService.d.ts +37 -0
- package/dist/src/services/assistantService.d.ts.map +1 -0
- package/dist/src/services/assistantService.js +284 -0
- package/dist/src/services/assistantService.js.map +1 -0
- package/dist/src/services/assistantTools.d.ts +24 -0
- package/dist/src/services/assistantTools.d.ts.map +1 -0
- package/dist/src/services/assistantTools.js +582 -0
- package/dist/src/services/assistantTools.js.map +1 -0
- package/dist/src/services/chatService.d.ts +44 -0
- package/dist/src/services/chatService.d.ts.map +1 -1
- package/dist/src/services/chatService.js +105 -4
- package/dist/src/services/chatService.js.map +1 -1
- package/dist/src/services/configService.d.ts +55 -0
- package/dist/src/services/configService.d.ts.map +1 -1
- package/dist/src/services/configService.js +147 -6
- package/dist/src/services/configService.js.map +1 -1
- package/dist/src/services/evidenceService.d.ts +30 -0
- package/dist/src/services/evidenceService.d.ts.map +1 -0
- package/dist/src/services/evidenceService.js +98 -0
- package/dist/src/services/evidenceService.js.map +1 -0
- package/dist/src/services/exportService.d.ts +13 -20
- package/dist/src/services/exportService.d.ts.map +1 -1
- package/dist/src/services/exportService.js +113 -47
- package/dist/src/services/exportService.js.map +1 -1
- package/dist/src/services/initKeyService.d.ts +19 -0
- package/dist/src/services/initKeyService.d.ts.map +1 -0
- package/dist/src/services/initKeyService.js +80 -0
- package/dist/src/services/initKeyService.js.map +1 -0
- package/dist/src/services/messageContract.d.ts +23 -0
- package/dist/src/services/messageContract.d.ts.map +1 -0
- package/dist/src/services/messageContract.js +49 -0
- package/dist/src/services/messageContract.js.map +1 -0
- package/dist/src/services/messageQuery.d.ts +7 -0
- package/dist/src/services/messageQuery.d.ts.map +1 -0
- package/dist/src/services/messageQuery.js +30 -0
- package/dist/src/services/messageQuery.js.map +1 -0
- package/dist/src/services/wechat-formatter.js +14 -14
- package/dist/src/services/wechatMessageService.d.ts.map +1 -1
- package/dist/src/services/wechatMessageService.js +31 -3
- package/dist/src/services/wechatMessageService.js.map +1 -1
- package/dist/src/services/whitelistService.d.ts +52 -4
- package/dist/src/services/whitelistService.d.ts.map +1 -1
- package/dist/src/services/whitelistService.js +183 -17
- package/dist/src/services/whitelistService.js.map +1 -1
- package/dist/src/types.d.ts +20 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/dateRange.d.ts +15 -0
- package/dist/src/utils/dateRange.d.ts.map +1 -0
- package/dist/src/utils/dateRange.js +48 -0
- package/dist/src/utils/dateRange.js.map +1 -0
- package/dist/src/utils/mcpSecurity.d.ts +5 -0
- package/dist/src/utils/mcpSecurity.d.ts.map +1 -0
- package/dist/src/utils/mcpSecurity.js +39 -0
- package/dist/src/utils/mcpSecurity.js.map +1 -0
- package/dist/src/utils/packageRoot.d.ts +2 -0
- package/dist/src/utils/packageRoot.d.ts.map +1 -0
- package/dist/src/utils/packageRoot.js +16 -0
- package/dist/src/utils/packageRoot.js.map +1 -0
- package/dist/src/utils/python.d.ts +2 -0
- package/dist/src/utils/python.d.ts.map +1 -0
- package/dist/src/utils/python.js +72 -0
- package/dist/src/utils/python.js.map +1 -0
- package/dist/src/utils/pythonProcessEnv.d.ts +3 -0
- package/dist/src/utils/pythonProcessEnv.d.ts.map +1 -0
- package/dist/src/utils/pythonProcessEnv.js +46 -0
- package/dist/src/utils/pythonProcessEnv.js.map +1 -0
- package/dist/src/utils/talkerUtils.d.ts +4 -1
- package/dist/src/utils/talkerUtils.d.ts.map +1 -1
- package/dist/src/utils/talkerUtils.js +9 -9
- package/dist/src/utils/talkerUtils.js.map +1 -1
- package/dist/src/utils/wechatEmoji.d.ts +5 -0
- package/dist/src/utils/wechatEmoji.d.ts.map +1 -0
- package/dist/src/utils/wechatEmoji.js +35 -0
- package/dist/src/utils/wechatEmoji.js.map +1 -0
- package/docs/AI_INTERFACE.md +189 -0
- package/docs/BRANCHES.md +87 -0
- package/docs/DATA_CONTRACT.md +74 -0
- package/docs/DECISIONS.md +270 -0
- package/docs/DEEPSEEK_V4_FLASH/346/226/275/345/267/245/346/226/207/346/241/243.md +234 -0
- package/docs/EVIDENCE_GUIDE.md +96 -0
- package/docs/MCP.md +95 -0
- package/docs/PARTNERS.md +41 -0
- package/docs/PROJECT_STATE.md +96 -0
- package/docs/ROADMAP.md +191 -0
- package/docs/SETUP.md +103 -0
- package/docs/images/weflow-architecture-gpt-image-2.png +0 -0
- package/docs/images/weflow-architecture.png +0 -0
- package/docs/images/weflow-architecture.svg +65 -0
- package/mcp-server/index.ts +169 -269
- package/package.json +81 -72
- package/requirements-3x.txt +4 -0
- package/requirements-voice.txt +15 -0
- package/requirements.txt +9 -0
- package/resources/js/marked.min.js +69 -0
- package/resources/wechat-emoji/666.png +0 -0
- package/resources/wechat-emoji/Awesome.png +0 -0
- package/resources/wechat-emoji/Concerned.png +0 -0
- package/resources/wechat-emoji/Cry.png +0 -0
- package/resources/wechat-emoji/Emm.png +0 -0
- package/resources/wechat-emoji/Facepalm.png +0 -0
- package/resources/wechat-emoji/Grin.png +0 -0
- package/resources/wechat-emoji/OK.png +0 -0
- package/resources/wechat-emoji/Respect.png +0 -0
- package/resources/wechat-emoji/Sick.png +0 -0
- package/resources/wechat-emoji/Sleep.png +0 -0
- package/resources/wechat-emoji/Smile.png +0 -0
- package/resources/wechat-emoji//344/272/262/344/272/262.png +0 -0
- package/resources/wechat-emoji//344/276/277/344/276/277.png +0 -0
- package/resources/wechat-emoji//345/201/267/347/254/221.png +0 -0
- package/resources/wechat-emoji//345/202/262/346/205/242.png +0 -0
- package/resources/wechat-emoji//345/206/215/350/247/201.png +0 -0
- package/resources/wechat-emoji//345/207/213/350/260/242.png +0 -0
- package/resources/wechat-emoji//345/212/240/346/262/271.png +0 -0
- package/resources/wechat-emoji//345/213/276/345/274/225.png +0 -0
- package/resources/wechat-emoji//345/217/221/345/221/206.png +0 -0
- package/resources/wechat-emoji//345/217/221/346/200/222.png +0 -0
- package/resources/wechat-emoji//345/217/221/346/212/226.png +0 -0
- package/resources/wechat-emoji//345/217/257/346/200/234.png +0 -0
- package/resources/wechat-emoji//345/217/263/345/223/274/345/223/274.png +0 -0
- package/resources/wechat-emoji//345/217/271/346/260/224.png +0 -0
- package/resources/wechat-emoji//345/220/203/347/223/234.png +0 -0
- package/resources/wechat-emoji//345/220/210/345/215/201.png +0 -0
- package/resources/wechat-emoji//345/220/220.png +0 -0
- package/resources/wechat-emoji//345/221/262/347/211/231.png +0 -0
- package/resources/wechat-emoji//345/222/222/351/252/202.png +0 -0
- package/resources/wechat-emoji//345/222/226/345/225/241.png +0 -0
- package/resources/wechat-emoji//345/223/207.png +0 -0
- package/resources/wechat-emoji//345/225/244/351/205/222.png +0 -0
- package/resources/wechat-emoji//345/230/230.png +0 -0
- package/resources/wechat-emoji//345/230/264/345/224/207.png +0 -0
- package/resources/wechat-emoji//345/230/277/345/223/210.png +0 -0
- package/resources/wechat-emoji//345/233/247.png +0 -0
- package/resources/wechat-emoji//345/233/260.png +0 -0
- package/resources/wechat-emoji//345/235/217/347/254/221.png +0 -0
- package/resources/wechat-emoji//345/244/247/345/223/255.png +0 -0
- package/resources/wechat-emoji//345/244/251/345/225/212.png +0 -0
- package/resources/wechat-emoji//345/244/252/351/230/263.png +0 -0
- package/resources/wechat-emoji//345/244/261/346/234/233.png +0 -0
- package/resources/wechat-emoji//345/245/270/347/254/221.png +0 -0
- package/resources/wechat-emoji//345/245/275/347/232/204.png +0 -0
- package/resources/wechat-emoji//345/247/224/345/261/210.png +0 -0
- package/resources/wechat-emoji//345/256/263/347/276/236.png +0 -0
- package/resources/wechat-emoji//345/260/264/345/260/254.png +0 -0
- package/resources/wechat-emoji//345/272/206/347/245/235.png +0 -0
- package/resources/wechat-emoji//345/274/261.png +0 -0
- package/resources/wechat-emoji//345/274/272.png +0 -0
- package/resources/wechat-emoji//345/276/227/346/204/217.png +0 -0
- package/resources/wechat-emoji//345/276/256/347/254/221.png +0 -0
- package/resources/wechat-emoji//345/277/203/347/242/216.png +0 -0
- package/resources/wechat-emoji//345/277/253/345/223/255/344/272/206.png +0 -0
- package/resources/wechat-emoji//346/201/220/346/203/247.png +0 -0
- package/resources/wechat-emoji//346/202/240/351/227/262.png +0 -0
- package/resources/wechat-emoji//346/203/212/346/201/220.png +0 -0
- package/resources/wechat-emoji//346/203/212/350/256/266.png +0 -0
- package/resources/wechat-emoji//346/204/211/345/277/253.png +0 -0
- package/resources/wechat-emoji//346/206/250/347/254/221.png +0 -0
- package/resources/wechat-emoji//346/211/223/350/204/270.png +0 -0
- package/resources/wechat-emoji//346/212/223/347/213/202.png +0 -0
- package/resources/wechat-emoji//346/212/240/351/274/273.png +0 -0
- package/resources/wechat-emoji//346/212/261/346/213/263.png +0 -0
- package/resources/wechat-emoji//346/213/245/346/212/261.png +0 -0
- package/resources/wechat-emoji//346/213/263/345/244/264.png +0 -0
- package/resources/wechat-emoji//346/215/202/350/204/270.png +0 -0
- package/resources/wechat-emoji//346/217/241/346/211/213.png +0 -0
- package/resources/wechat-emoji//346/222/207/345/230/264.png +0 -0
- package/resources/wechat-emoji//346/223/246/346/261/227.png +0 -0
- package/resources/wechat-emoji//346/225/262/346/211/223.png +0 -0
- package/resources/wechat-emoji//346/227/240/350/257/255.png +0 -0
- package/resources/wechat-emoji//346/227/272/346/237/264.png +0 -0
- package/resources/wechat-emoji//346/231/225.png +0 -0
- package/resources/wechat-emoji//346/234/210/344/272/256.png +0 -0
- package/resources/wechat-emoji//346/234/272/346/231/272.png +0 -0
- package/resources/wechat-emoji//346/261/227.png +0 -0
- package/resources/wechat-emoji//346/265/201/346/263/252.png +0 -0
- package/resources/wechat-emoji//347/202/270/345/274/271.png +0 -0
- package/resources/wechat-emoji//347/203/237/350/212/261.png +0 -0
- package/resources/wechat-emoji//347/210/206/347/253/271.png +0 -0
- package/resources/wechat-emoji//347/210/261/345/277/203.png +0 -0
- package/resources/wechat-emoji//347/214/252/345/244/264.png +0 -0
- package/resources/wechat-emoji//347/216/253/347/221/260.png +0 -0
- package/resources/wechat-emoji//347/224/237/347/227/205.png +0 -0
- package/resources/wechat-emoji//347/226/221/351/227/256.png +0 -0
- package/resources/wechat-emoji//347/231/274.png +0 -0
- package/resources/wechat-emoji//347/231/275/347/234/274.png +0 -0
- package/resources/wechat-emoji//347/232/261/347/234/211.png +0 -0
- package/resources/wechat-emoji//347/235/241.png +0 -0
- package/resources/wechat-emoji//347/240/264/346/266/225/344/270/272/347/254/221.png +0 -0
- package/resources/wechat-emoji//347/244/274/347/211/251.png +0 -0
- package/resources/wechat-emoji//347/244/276/344/274/232/347/244/276/344/274/232.png +0 -0
- package/resources/wechat-emoji//347/246/217.png +0 -0
- package/resources/wechat-emoji//347/254/221/350/204/270.png +0 -0
- package/resources/wechat-emoji//347/272/242/345/214/205.png +0 -0
- package/resources/wechat-emoji//347/277/273/347/231/275/347/234/274.png +0 -0
- package/resources/wechat-emoji//350/200/266.png +0 -0
- package/resources/wechat-emoji//350/203/234/345/210/251.png +0 -0
- package/resources/wechat-emoji//350/204/270/347/272/242.png +0 -0
- package/resources/wechat-emoji//350/211/262.png +0 -0
- package/resources/wechat-emoji//350/213/246/346/266/251.png +0 -0
- package/resources/wechat-emoji//350/217/234/345/210/200.png +0 -0
- package/resources/wechat-emoji//350/233/213/347/263/225.png +0 -0
- package/resources/wechat-emoji//350/241/260.png +0 -0
- package/resources/wechat-emoji//350/243/202/345/274/200.png +0 -0
- package/resources/wechat-emoji//350/256/251/346/210/221/347/234/213/347/234/213.png +0 -0
- package/resources/wechat-emoji//350/260/203/347/232/256.png +0 -0
- package/resources/wechat-emoji//350/267/263/350/267/263.png +0 -0
- package/resources/wechat-emoji//350/275/254/345/234/210.png +0 -0
- package/resources/wechat-emoji//351/204/231/350/247/206.png +0 -0
- package/resources/wechat-emoji//351/227/255/345/230/264.png +0 -0
- package/resources/wechat-emoji//351/230/264/351/231/251.png +0 -0
- package/resources/wechat-emoji//351/232/276/350/277/207.png +0 -0
- package/resources/wechat-emoji//351/252/267/351/253/205.png +0 -0
- package/resources/wechat-emoji//351/274/223/346/216/214.png +0 -0
- package/scripts/_batch_link_sources.py +73 -73
- package/scripts/_utils.py +29 -0
- package/scripts/annual_report.py +796 -796
- package/scripts/auto_tag.py +128 -128
- package/scripts/biz_daily.py +210 -53
- package/scripts/chat_report.py +10 -0
- package/scripts/chat_stats.py +702 -702
- package/scripts/classify_daily.py +17 -11
- package/scripts/create_reading_notes.py +370 -370
- package/scripts/daily_stats.py +132 -0
- package/scripts/enrich_backlinks.py +163 -163
- package/scripts/export_chat_html.py +1704 -107
- package/scripts/extract_todos.py +445 -404
- package/scripts/fav_server.py +119 -12
- package/scripts/fix_topics.py +111 -111
- package/scripts/generate_ai_report.py +1 -1
- package/scripts/generate_html.py +597 -277
- package/scripts/inspection_records.py +354 -0
- package/scripts/mcp_bridge.py +398 -398
- package/scripts/nt_decrypt.py +693 -48
- package/scripts/pipeline.py +27 -19
- package/scripts/promote_all.py +312 -310
- package/scripts/promote_ideas.py +299 -297
- package/scripts/rag_chat.py +194 -191
- package/scripts/semantic_search.py +435 -431
- package/scripts/sync_fav.py +201 -146
- package/scripts/sync_weread.py +192 -192
- package/scripts/vault_rag.py +140 -139
- package/scripts/vault_search.py +141 -141
- package/scripts/wechat_emoji.py +132 -0
- package/scripts/wechat_emoticon.py +433 -0
- package/scripts/wechat_image.py +258 -0
- package/scripts/wechat_voice.py +327 -0
- package/src/core/dbPathService.ts +163 -18
- package/src/core/keyService.ts +137 -25
- package/src/core/ntCore.ts +202 -61
- package/src/core/sqlcipherCore.ts +17 -1
- package/src/core/wcdbCore.ts +3 -7
- package/src/services/assistantDaemon.ts +111 -0
- package/src/services/assistantMemory.ts +193 -0
- package/src/services/assistantPrivacy.ts +87 -0
- package/src/services/assistantRouting.ts +92 -0
- package/src/services/assistantService.ts +295 -0
- package/src/services/assistantTools.ts +549 -0
- package/src/services/chatService.ts +133 -4
- package/src/services/configService.ts +194 -6
- package/src/services/evidenceService.ts +129 -0
- package/src/services/exportService.ts +113 -48
- package/src/services/initKeyService.ts +83 -0
- package/src/services/messageContract.ts +77 -0
- package/src/services/messageQuery.ts +38 -0
- package/src/services/wechat-formatter.ts +348 -348
- package/src/services/wechatMessageService.ts +25 -3
- package/src/services/wereadService.ts +308 -308
- package/src/services/whitelistService.ts +209 -22
- package/src/types.ts +20 -0
- package/src/utils/dateRange.ts +58 -0
- package/src/utils/mcpSecurity.ts +39 -0
- package/src/utils/packageRoot.ts +14 -0
- package/src/utils/python.ts +73 -0
- package/src/utils/pythonProcessEnv.ts +45 -0
- package/src/utils/talkerUtils.ts +14 -2
- package/dist/src/utils/errors.d.ts +0 -25
- package/dist/src/utils/errors.d.ts.map +0 -1
- package/dist/src/utils/errors.js +0 -43
- package/dist/src/utils/errors.js.map +0 -1
- package/dist/src/utils/pythonRunner.d.ts +0 -27
- package/dist/src/utils/pythonRunner.d.ts.map +0 -1
- package/dist/src/utils/pythonRunner.js +0 -62
- package/dist/src/utils/pythonRunner.js.map +0 -1
- package/src/utils/errors.ts +0 -42
- package/src/utils/pythonRunner.ts +0 -81
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { join, basename } from 'path'
|
|
2
|
-
import { existsSync, readdirSync, statSync, readFileSync } from 'fs'
|
|
1
|
+
import { join, basename, dirname } from 'path'
|
|
2
|
+
import { existsSync, readdirSync, statSync, lstatSync, readFileSync } from 'fs'
|
|
3
3
|
import { homedir } from 'os'
|
|
4
4
|
import { createDecipheriv } from 'crypto'
|
|
5
5
|
import { expandHomePath } from '../utils/pathUtils.js'
|
|
@@ -12,6 +12,100 @@ export interface WxidInfo {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export class DbPathService {
|
|
15
|
+
/**
|
|
16
|
+
* Validate a user-selected WeChat data location. WeChat's picker may expose
|
|
17
|
+
* either the data root, an account folder, or that account's db_storage.
|
|
18
|
+
*/
|
|
19
|
+
resolveDataRoot(inputPath: string): string | null {
|
|
20
|
+
const rootPath = expandHomePath(inputPath)
|
|
21
|
+
if (!rootPath || !existsSync(rootPath)) return null
|
|
22
|
+
|
|
23
|
+
const ancestorPath = this.findAccountAncestor(rootPath)
|
|
24
|
+
if (ancestorPath) return dirname(ancestorPath)
|
|
25
|
+
|
|
26
|
+
const descendantPath = this.findAccountDescendant(rootPath, 3)
|
|
27
|
+
if (descendantPath) {
|
|
28
|
+
return dirname(descendantPath)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private findAccountAncestor(inputPath: string): string | null {
|
|
35
|
+
let currentPath = inputPath
|
|
36
|
+
for (let level = 0; level <= 4; level++) {
|
|
37
|
+
if (this.isAccountDir(currentPath)) return currentPath
|
|
38
|
+
const parentPath = dirname(currentPath)
|
|
39
|
+
if (parentPath === currentPath) break
|
|
40
|
+
currentPath = parentPath
|
|
41
|
+
}
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private findAccountDescendant(rootPath: string, maxDepth: number): string | null {
|
|
46
|
+
if (maxDepth < 0) return null
|
|
47
|
+
if (this.isAccountDir(rootPath)) return rootPath
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
for (const entry of readdirSync(rootPath)) {
|
|
51
|
+
const entryPath = join(rootPath, entry)
|
|
52
|
+
let stat: ReturnType<typeof statSync>
|
|
53
|
+
try {
|
|
54
|
+
if (lstatSync(entryPath).isSymbolicLink()) continue
|
|
55
|
+
stat = statSync(entryPath)
|
|
56
|
+
} catch {
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
if (!stat.isDirectory()) continue
|
|
60
|
+
if (this.shouldSkipSearchDirectory(entry)) continue
|
|
61
|
+
const accountPath = this.findAccountDescendant(entryPath, maxDepth - 1)
|
|
62
|
+
if (accountPath) return accountPath
|
|
63
|
+
}
|
|
64
|
+
} catch { }
|
|
65
|
+
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private shouldSkipSearchDirectory(name: string): boolean {
|
|
70
|
+
return [
|
|
71
|
+
'$recycle.bin', 'node_modules', 'windows', 'program files',
|
|
72
|
+
'program files (x86)', 'programdata', 'system volume information',
|
|
73
|
+
].includes(name.toLowerCase())
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private findKnownDataRoots(basePath: string, maxDepth: number): string[] {
|
|
77
|
+
const roots: string[] = []
|
|
78
|
+
const visit = (currentPath: string, depth: number): void => {
|
|
79
|
+
if (depth > maxDepth) return
|
|
80
|
+
let entries: string[]
|
|
81
|
+
try {
|
|
82
|
+
entries = readdirSync(currentPath)
|
|
83
|
+
} catch {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const entryPath = join(currentPath, entry)
|
|
89
|
+
try {
|
|
90
|
+
if (lstatSync(entryPath).isSymbolicLink()) continue
|
|
91
|
+
} catch {
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
94
|
+
if (!this.isDirectory(entryPath)) continue
|
|
95
|
+
const lower = entry.toLowerCase()
|
|
96
|
+
if (lower === 'xwechat_files' || lower === 'wechat files') {
|
|
97
|
+
roots.push(entryPath)
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
if (depth < maxDepth && !this.shouldSkipSearchDirectory(lower) && lower !== 'appdata') {
|
|
101
|
+
visit(entryPath, depth + 1)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (this.isDirectory(basePath)) visit(basePath, 0)
|
|
106
|
+
return roots
|
|
107
|
+
}
|
|
108
|
+
|
|
15
109
|
private readVarint(buf: Buffer, offset: number): { value: number, length: number } {
|
|
16
110
|
let value = 0;
|
|
17
111
|
let length = 0;
|
|
@@ -89,7 +183,7 @@ export class DbPathService {
|
|
|
89
183
|
/**
|
|
90
184
|
* 自动检测微信数据库根目录
|
|
91
185
|
*/
|
|
92
|
-
async autoDetect(): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
186
|
+
async autoDetect(options: { searchDrives?: boolean; fullScan?: boolean } = {}): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
93
187
|
try {
|
|
94
188
|
const possiblePaths: string[] = []
|
|
95
189
|
const home = homedir()
|
|
@@ -110,6 +204,12 @@ export class DbPathService {
|
|
|
110
204
|
}
|
|
111
205
|
// macOS 旧路径兜底
|
|
112
206
|
possiblePaths.push(join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files'))
|
|
207
|
+
} else if (process.platform === 'linux') {
|
|
208
|
+
// Linux 原生微信 4.x:数据结构与 Windows 相同(xwechat_files),XDG 文档目录随 locale 变化
|
|
209
|
+
possiblePaths.push(join(home, '.local', 'share', 'xwechat_files'))
|
|
210
|
+
possiblePaths.push(join(home, 'xwechat_files'))
|
|
211
|
+
possiblePaths.push(join(home, 'Documents', 'xwechat_files'))
|
|
212
|
+
possiblePaths.push(join(home, '文档', 'xwechat_files'))
|
|
113
213
|
} else {
|
|
114
214
|
// Windows: 优先检测 NT 格式 (xwechat_files)
|
|
115
215
|
possiblePaths.push(join(home, 'xwechat_files'))
|
|
@@ -117,20 +217,36 @@ export class DbPathService {
|
|
|
117
217
|
possiblePaths.push(join(home, 'Documents', 'xwechat_files'))
|
|
118
218
|
// Windows 微信3.x 数据目录
|
|
119
219
|
possiblePaths.push(join(home, 'Documents', 'WeChat Files'))
|
|
220
|
+
for (const basePath of [
|
|
221
|
+
process.env.LOCALAPPDATA,
|
|
222
|
+
process.env.APPDATA,
|
|
223
|
+
process.env.USERPROFILE,
|
|
224
|
+
process.env.OneDrive,
|
|
225
|
+
]) {
|
|
226
|
+
if (basePath) possiblePaths.push(...this.findKnownDataRoots(basePath, 3))
|
|
227
|
+
}
|
|
120
228
|
}
|
|
121
229
|
|
|
122
230
|
for (const path of possiblePaths) {
|
|
123
|
-
|
|
231
|
+
const dataRoot = this.resolveDataRoot(path)
|
|
232
|
+
if (dataRoot) {
|
|
233
|
+
return { success: true, path: dataRoot }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
124
236
|
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
237
|
+
if (process.platform === 'win32' && (options.searchDrives || options.fullScan)) {
|
|
238
|
+
for (const volumeRoot of this.getVolumeRoots()) {
|
|
239
|
+
for (const dataRootPath of this.findKnownDataRoots(volumeRoot, 12)) {
|
|
240
|
+
const dataRoot = this.resolveDataRoot(dataRootPath)
|
|
241
|
+
if (dataRoot) return { success: true, path: dataRoot }
|
|
242
|
+
}
|
|
129
243
|
}
|
|
244
|
+
}
|
|
130
245
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
246
|
+
if (options.fullScan) {
|
|
247
|
+
for (const volumeRoot of this.getVolumeRoots()) {
|
|
248
|
+
const accountPath = this.findAccountDescendant(volumeRoot, 32)
|
|
249
|
+
if (accountPath) return { success: true, path: dirname(accountPath) }
|
|
134
250
|
}
|
|
135
251
|
}
|
|
136
252
|
|
|
@@ -175,13 +291,31 @@ export class DbPathService {
|
|
|
175
291
|
|
|
176
292
|
private isAccountDir(entryPath: string): boolean {
|
|
177
293
|
return (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
294
|
+
this.isDirectory(join(entryPath, 'db_storage')) || // WeChat 4.x
|
|
295
|
+
this.isDirectory(join(entryPath, 'Msg')) || // WeChat 3.x
|
|
296
|
+
this.isDirectory(join(entryPath, 'FileStorage', 'Image')) ||
|
|
297
|
+
this.isDirectory(join(entryPath, 'FileStorage', 'Image2'))
|
|
182
298
|
)
|
|
183
299
|
}
|
|
184
300
|
|
|
301
|
+
private getVolumeRoots(): string[] {
|
|
302
|
+
if (process.platform !== 'win32') return [homedir()]
|
|
303
|
+
const roots: string[] = []
|
|
304
|
+
for (let code = 65; code <= 90; code++) {
|
|
305
|
+
const volumeRoot = `${String.fromCharCode(code)}:\\`
|
|
306
|
+
if (this.isDirectory(volumeRoot)) roots.push(volumeRoot)
|
|
307
|
+
}
|
|
308
|
+
return roots
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private isDirectory(entryPath: string): boolean {
|
|
312
|
+
try {
|
|
313
|
+
return statSync(entryPath).isDirectory()
|
|
314
|
+
} catch {
|
|
315
|
+
return false
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
185
319
|
private isPotentialAccountName(name: string): boolean {
|
|
186
320
|
const lower = name.toLowerCase()
|
|
187
321
|
if (lower.startsWith('all') || lower.startsWith('applet') || lower.startsWith('backup') || lower.startsWith('wmpf')) {
|
|
@@ -223,7 +357,7 @@ export class DbPathService {
|
|
|
223
357
|
* 扫描目录名候选(仅包含下划线的文件夹,排除 all_users)
|
|
224
358
|
*/
|
|
225
359
|
scanWxidCandidates(rootPath: string): WxidInfo[] {
|
|
226
|
-
const resolvedRootPath = expandHomePath(rootPath)
|
|
360
|
+
const resolvedRootPath = this.resolveDataRoot(rootPath) || expandHomePath(rootPath)
|
|
227
361
|
const wxids: WxidInfo[] = []
|
|
228
362
|
|
|
229
363
|
try {
|
|
@@ -237,6 +371,7 @@ export class DbPathService {
|
|
|
237
371
|
const lower = entry.toLowerCase()
|
|
238
372
|
if (lower === 'all_users') continue
|
|
239
373
|
if (!entry.includes('_')) continue
|
|
374
|
+
if (!this.isAccountDir(entryPath)) continue
|
|
240
375
|
wxids.push({ wxid: entry, modifiedTime: stat.mtimeMs })
|
|
241
376
|
}
|
|
242
377
|
}
|
|
@@ -244,7 +379,7 @@ export class DbPathService {
|
|
|
244
379
|
|
|
245
380
|
if (wxids.length === 0) {
|
|
246
381
|
const rootName = basename(resolvedRootPath)
|
|
247
|
-
if (rootName.includes('_') && rootName.toLowerCase() !== 'all_users') {
|
|
382
|
+
if (rootName.includes('_') && rootName.toLowerCase() !== 'all_users' && this.isAccountDir(resolvedRootPath)) {
|
|
248
383
|
const rootStat = statSync(resolvedRootPath)
|
|
249
384
|
wxids.push({ wxid: rootName, modifiedTime: rootStat.mtimeMs })
|
|
250
385
|
}
|
|
@@ -274,7 +409,7 @@ export class DbPathService {
|
|
|
274
409
|
* 扫描 wxid 列表
|
|
275
410
|
*/
|
|
276
411
|
scanWxids(rootPath: string): WxidInfo[] {
|
|
277
|
-
const resolvedRootPath = expandHomePath(rootPath)
|
|
412
|
+
const resolvedRootPath = this.resolveDataRoot(rootPath) || expandHomePath(rootPath)
|
|
278
413
|
const wxids: WxidInfo[] = []
|
|
279
414
|
|
|
280
415
|
try {
|
|
@@ -332,6 +467,16 @@ export class DbPathService {
|
|
|
332
467
|
// 旧版本路径兜底
|
|
333
468
|
return join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files')
|
|
334
469
|
}
|
|
470
|
+
if (process.platform === 'linux') {
|
|
471
|
+
for (const p of [
|
|
472
|
+
join(home, '.local', 'share', 'xwechat_files'),
|
|
473
|
+
join(home, 'xwechat_files'),
|
|
474
|
+
join(home, 'Documents', 'xwechat_files'),
|
|
475
|
+
join(home, '文档', 'xwechat_files'),
|
|
476
|
+
]) {
|
|
477
|
+
if (existsSync(p)) return p
|
|
478
|
+
}
|
|
479
|
+
}
|
|
335
480
|
// 优先返回 4.x 路径
|
|
336
481
|
const xwechatPath = join(home, 'Documents', 'xwechat_files')
|
|
337
482
|
if (existsSync(xwechatPath)) return xwechatPath
|
package/src/core/keyService.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { join } from 'path'
|
|
2
2
|
import { existsSync, copyFileSync, mkdirSync } from 'fs'
|
|
3
|
-
import { execFile
|
|
3
|
+
import { execFile } from 'child_process'
|
|
4
4
|
import { promisify } from 'util'
|
|
5
5
|
import { createRequire } from 'module'
|
|
6
6
|
import os from 'os'
|
|
7
7
|
import type { DbKeyResult } from '../types.js'
|
|
8
|
+
import { configService } from '../services/configService.js'
|
|
9
|
+
import { getPythonCommand } from '../utils/python.js'
|
|
10
|
+
import { createPythonProcessEnv } from '../utils/pythonProcessEnv.js'
|
|
11
|
+
import { resolvePackageRoot } from '../utils/packageRoot.js'
|
|
8
12
|
|
|
9
13
|
const require = createRequire(import.meta.url)
|
|
10
14
|
const execFileAsync = promisify(execFile)
|
|
15
|
+
const PACKAGE_ROOT = resolvePackageRoot(import.meta.url)
|
|
16
|
+
|
|
17
|
+
/** 基于模块位置定位包根目录(全局安装时 process.cwd() 不可靠) */
|
|
18
|
+
function resolveScriptPath(scriptName: string): string {
|
|
19
|
+
return join(PACKAGE_ROOT, 'scripts', scriptName)
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
export class KeyService {
|
|
13
23
|
private koffi: any = null
|
|
@@ -26,6 +36,8 @@ export class KeyService {
|
|
|
26
36
|
private QueryFullProcessImageNameW: any = null
|
|
27
37
|
|
|
28
38
|
private readonly PROCESS_ALL_ACCESS = 0x1F0FFF
|
|
39
|
+
private readonly PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
|
40
|
+
private readonly exitedPids = new Set<number>()
|
|
29
41
|
|
|
30
42
|
private getDllPath(): string {
|
|
31
43
|
const archDir = process.arch === 'arm64' ? 'arm64' : 'x64'
|
|
@@ -35,10 +47,15 @@ export class KeyService {
|
|
|
35
47
|
candidates.push(process.env.WX_KEY_DLL_PATH)
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
// 基于模块位置定位(全局安装时 cwd 不是包根)
|
|
51
|
+
candidates.push(join(PACKAGE_ROOT, 'resources', 'key', 'win32', archDir, 'wx_key.dll'))
|
|
52
|
+
candidates.push(join(PACKAGE_ROOT, 'resources', 'key', 'win32', 'x64', 'wx_key.dll'))
|
|
53
|
+
candidates.push(join(PACKAGE_ROOT, 'resources', 'key', 'win32', 'wx_key.dll'))
|
|
54
|
+
candidates.push(join(PACKAGE_ROOT, 'resources', 'wx_key.dll'))
|
|
55
|
+
|
|
56
|
+
// cwd 兜底(开发模式在项目根运行)
|
|
38
57
|
const cwd = process.cwd()
|
|
39
58
|
candidates.push(join(cwd, 'resources', 'key', 'win32', archDir, 'wx_key.dll'))
|
|
40
|
-
candidates.push(join(cwd, 'resources', 'key', 'win32', 'x64', 'wx_key.dll'))
|
|
41
|
-
candidates.push(join(cwd, 'resources', 'key', 'win32', 'wx_key.dll'))
|
|
42
59
|
candidates.push(join(cwd, 'resources', 'wx_key.dll'))
|
|
43
60
|
|
|
44
61
|
for (const path of candidates) {
|
|
@@ -139,7 +156,7 @@ export class KeyService {
|
|
|
139
156
|
const parts = line.split('","').map((p) => p.replace(/^"|"$/g, ''))
|
|
140
157
|
if (parts[0]?.toLowerCase() === imageName.toLowerCase()) {
|
|
141
158
|
const pid = Number(parts[1])
|
|
142
|
-
if (!Number.isNaN(pid)) return pid
|
|
159
|
+
if (!Number.isNaN(pid) && await this.isProcessRunning(pid)) return pid
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
return null
|
|
@@ -148,6 +165,20 @@ export class KeyService {
|
|
|
148
165
|
}
|
|
149
166
|
}
|
|
150
167
|
|
|
168
|
+
private async isProcessRunning(pid: number): Promise<boolean> {
|
|
169
|
+
if (this.exitedPids.has(pid)) return false
|
|
170
|
+
try {
|
|
171
|
+
const command = `$process = Get-Process -Id ${pid} -ErrorAction SilentlyContinue; if ($process -and -not $process.HasExited) { 'running' }`
|
|
172
|
+
const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', command])
|
|
173
|
+
const running = stdout.trim() === 'running'
|
|
174
|
+
if (!running) this.exitedPids.add(pid)
|
|
175
|
+
return running
|
|
176
|
+
} catch {
|
|
177
|
+
this.exitedPids.add(pid)
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
151
182
|
async findWeChatPid(): Promise<number | null> {
|
|
152
183
|
const names = ['Weixin.exe', 'WeChat.exe']
|
|
153
184
|
for (const name of names) {
|
|
@@ -159,7 +190,7 @@ export class KeyService {
|
|
|
159
190
|
|
|
160
191
|
private async getProcessExecutablePath(pid: number): Promise<string | null> {
|
|
161
192
|
if (!this.ensureKernel32()) return null
|
|
162
|
-
const hProcess = this.OpenProcess(
|
|
193
|
+
const hProcess = this.OpenProcess(this.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
|
|
163
194
|
if (!hProcess) return null
|
|
164
195
|
|
|
165
196
|
try {
|
|
@@ -215,7 +246,7 @@ export class KeyService {
|
|
|
215
246
|
return { success: false, error: '等待超时,未检测到微信进程' }
|
|
216
247
|
}
|
|
217
248
|
|
|
218
|
-
onStatus?.(
|
|
249
|
+
onStatus?.('检测到微信进程,正在准备初始化...')
|
|
219
250
|
return this.extractKeyFromPid(pid, logs, onStatus)
|
|
220
251
|
}
|
|
221
252
|
|
|
@@ -244,8 +275,8 @@ export class KeyService {
|
|
|
244
275
|
return { success: false, error: '未找到微信进程,请先启动微信并登录' }
|
|
245
276
|
}
|
|
246
277
|
|
|
247
|
-
onStatus?.(
|
|
248
|
-
return this.extractKeyFromPid(pid, logs, onStatus)
|
|
278
|
+
onStatus?.('检测到微信进程,正在准备初始化...')
|
|
279
|
+
return this.extractKeyFromPid(pid, logs, onStatus, timeoutMs)
|
|
249
280
|
}
|
|
250
281
|
|
|
251
282
|
private async extractKeyFromPid(
|
|
@@ -254,7 +285,7 @@ export class KeyService {
|
|
|
254
285
|
onStatus?: (message: string) => void,
|
|
255
286
|
timeoutMs = 90_000
|
|
256
287
|
): Promise<DbKeyResult> {
|
|
257
|
-
|
|
288
|
+
onStatus?.('正在安装本地访问组件...')
|
|
258
289
|
const ok = this.initHook(pid)
|
|
259
290
|
if (!ok) {
|
|
260
291
|
const error = this.getLastErrorMsg ? this.decodeCString(this.getLastErrorMsg()) : ''
|
|
@@ -262,11 +293,13 @@ export class KeyService {
|
|
|
262
293
|
if (error.includes('0xC0000022') || error.includes('ACCESS_DENIED')) {
|
|
263
294
|
return { success: false, error: '权限不足,请以管理员身份运行 CLI' }
|
|
264
295
|
}
|
|
265
|
-
return { success: false, error }
|
|
296
|
+
return { success: false, error: '本地访问组件初始化失败' }
|
|
266
297
|
}
|
|
267
298
|
return { success: false, error: '初始化 Hook 失败' }
|
|
268
299
|
}
|
|
269
300
|
|
|
301
|
+
onStatus?.('本地访问组件已就绪,等待数据库初始化事件...')
|
|
302
|
+
|
|
270
303
|
const keyBuffer = Buffer.alloc(128)
|
|
271
304
|
const start = Date.now()
|
|
272
305
|
let pollCount = 0
|
|
@@ -277,27 +310,19 @@ export class KeyService {
|
|
|
277
310
|
const pollResult = this.pollKeyData(keyBuffer, keyBuffer.length)
|
|
278
311
|
if (pollResult) {
|
|
279
312
|
const key = this.decodeUtf8(keyBuffer)
|
|
280
|
-
onStatus?.(`[调试] pollKeyData=true, key长度=${key.length}, 内容=${key.slice(0, 20)}...`)
|
|
281
313
|
if (key.length === 64) {
|
|
282
314
|
onStatus?.('密钥获取成功!')
|
|
283
315
|
return { success: true, key, logs }
|
|
284
316
|
}
|
|
285
|
-
} else if (pollCount
|
|
286
|
-
|
|
287
|
-
const hexSample = keyBuffer.slice(0, 16).toString('hex')
|
|
288
|
-
onStatus?.(`[调试] poll #${pollCount}: pollResult=${pollResult}, buffer=${hexSample}`)
|
|
317
|
+
} else if (pollCount % 100 === 0) {
|
|
318
|
+
onStatus?.('仍在等待数据库初始化事件...')
|
|
289
319
|
}
|
|
290
320
|
|
|
291
|
-
//
|
|
321
|
+
// Drain native status messages without forwarding process details to the terminal.
|
|
292
322
|
for (let i = 0; i < 5; i++) {
|
|
293
323
|
const statusBuffer = Buffer.alloc(256)
|
|
294
324
|
const levelOut = [0]
|
|
295
325
|
if (!this.getStatusMessage(statusBuffer, statusBuffer.length, levelOut)) break
|
|
296
|
-
const msg = this.decodeUtf8(statusBuffer)
|
|
297
|
-
if (msg) {
|
|
298
|
-
logs.push(msg)
|
|
299
|
-
onStatus?.(msg)
|
|
300
|
-
}
|
|
301
326
|
}
|
|
302
327
|
|
|
303
328
|
await new Promise(resolve => setTimeout(resolve, 120))
|
|
@@ -306,7 +331,94 @@ export class KeyService {
|
|
|
306
331
|
try { this.cleanupHook() } catch { }
|
|
307
332
|
}
|
|
308
333
|
|
|
309
|
-
return { success: false, error: '
|
|
334
|
+
return { success: false, error: '等待超时,未观察到数据库初始化事件', logs }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 捕获 sns.db 解密密钥。通过 Hook 微信数据库打开操作,
|
|
339
|
+
* 捕获 sns.db 被访问时的密钥并测试匹配。
|
|
340
|
+
*/
|
|
341
|
+
async captureSnsKey(
|
|
342
|
+
timeoutMs = 30000,
|
|
343
|
+
snsDbPath: string,
|
|
344
|
+
onStatus?: (message: string) => void
|
|
345
|
+
): Promise<DbKeyResult> {
|
|
346
|
+
if (process.platform !== 'win32') {
|
|
347
|
+
return { success: false, error: '朋友圈密钥捕获仅支持 Windows(依赖进程 Hook)' }
|
|
348
|
+
}
|
|
349
|
+
if (!this.ensureLoaded()) {
|
|
350
|
+
return { success: false, error: 'wx_key.dll 未加载,请确保 resources/key/ 目录存在' }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const pid = await this.findWeChatPid()
|
|
354
|
+
if (!pid) {
|
|
355
|
+
return { success: false, error: '未找到微信进程 (Weixin.exe),请确保微信已登录' }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const ok = this.initHook(pid)
|
|
359
|
+
if (!ok) {
|
|
360
|
+
const error = this.getLastErrorMsg ? this.decodeCString(this.getLastErrorMsg()) : ''
|
|
361
|
+
if (error) return { success: false, error }
|
|
362
|
+
return { success: false, error: '初始化 Hook 失败,请以管理员身份运行' }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
onStatus?.('本地访问组件已就绪,等待数据库访问...')
|
|
366
|
+
|
|
367
|
+
const keyBuffer = Buffer.alloc(128)
|
|
368
|
+
const capturedKeys = new Set<string>()
|
|
369
|
+
const start = Date.now()
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
while (Date.now() - start < timeoutMs) {
|
|
373
|
+
const pollResult = this.pollKeyData(keyBuffer, keyBuffer.length)
|
|
374
|
+
if (pollResult) {
|
|
375
|
+
const key = this.decodeUtf8(keyBuffer)
|
|
376
|
+
if (key.length === 64 && !capturedKeys.has(key)) {
|
|
377
|
+
capturedKeys.add(key)
|
|
378
|
+
onStatus?.('捕获到密钥,正在测试 sns.db...')
|
|
379
|
+
|
|
380
|
+
// Test against sns.db using Python
|
|
381
|
+
const snsSalt = configService.get('snsSalt')
|
|
382
|
+
try {
|
|
383
|
+
const scriptPath = resolveScriptPath('nt_decrypt.py')
|
|
384
|
+
const { stdout } = await execFileAsync(getPythonCommand(), [
|
|
385
|
+
scriptPath, 'sns-stats',
|
|
386
|
+
], {
|
|
387
|
+
timeout: 15000, encoding: 'utf8',
|
|
388
|
+
env: createPythonProcessEnv({
|
|
389
|
+
WEFLOW_DB_PATH: snsDbPath,
|
|
390
|
+
WEFLOW_NT_KEY: key,
|
|
391
|
+
WEFLOW_NT_SALT: snsSalt || '00000000000000000000000000000000',
|
|
392
|
+
}),
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
const lines = stdout.trim().split('\n')
|
|
396
|
+
const result = JSON.parse(lines[lines.length - 1] || '{}')
|
|
397
|
+
if (result.success) {
|
|
398
|
+
return { success: true, key }
|
|
399
|
+
}
|
|
400
|
+
} catch {
|
|
401
|
+
// Test fails for this key, continue polling
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Read status
|
|
407
|
+
for (let i = 0; i < 2; i++) {
|
|
408
|
+
const statusBuffer = Buffer.alloc(256)
|
|
409
|
+
const levelOut = [0]
|
|
410
|
+
if (!this.getStatusMessage(statusBuffer, statusBuffer.length, levelOut)) break
|
|
411
|
+
const msg = this.decodeUtf8(statusBuffer)
|
|
412
|
+
if (msg) onStatus?.(msg)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
await new Promise(resolve => setTimeout(resolve, 200))
|
|
416
|
+
}
|
|
417
|
+
} finally {
|
|
418
|
+
try { this.cleanupHook() } catch { }
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return { success: false, error: `超时 (${timeoutMs / 1000}s),未捕获到 sns.db 的密钥。请确保在微信中打开了朋友圈` }
|
|
310
422
|
}
|
|
311
423
|
|
|
312
424
|
/**
|
|
@@ -339,16 +451,16 @@ export class KeyService {
|
|
|
339
451
|
return { success: false, error: '未找到 WeChat 3.x (WeChat.exe) 进程,请先启动 3.x 微信并登录' }
|
|
340
452
|
}
|
|
341
453
|
|
|
342
|
-
onStatus?.(
|
|
454
|
+
onStatus?.('检测到 3.x 微信进程,正在准备初始化...')
|
|
343
455
|
|
|
344
456
|
try {
|
|
345
|
-
const scriptPath =
|
|
457
|
+
const scriptPath = resolveScriptPath('extract_3x_key.py')
|
|
346
458
|
|
|
347
459
|
if (!existsSync(scriptPath)) {
|
|
348
460
|
return { success: false, error: `Python 脚本不存在: ${scriptPath}` }
|
|
349
461
|
}
|
|
350
462
|
|
|
351
|
-
const { stdout } = await execFileAsync(
|
|
463
|
+
const { stdout } = await execFileAsync(getPythonCommand(), [scriptPath], {
|
|
352
464
|
timeout: 120_000,
|
|
353
465
|
maxBuffer: 1024 * 1024
|
|
354
466
|
})
|