weflow-cli 1.5.0 → 1.6.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/ARCHITECTURE.md +105 -0
- package/CHANGELOG.md +121 -0
- package/CONTRIBUTING.md +45 -0
- package/LICENSE +21 -21
- package/OPERATIONS.md +272 -0
- package/README.en.md +281 -0
- package/README.md +334 -149
- package/SECURITY.md +47 -0
- package/bin/weflow-cli.ts +4050 -581
- package/dist/bin/weflow-cli.js +4310 -588
- 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 +201 -65
- 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/RELEASING.md +124 -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 +702 -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 +215 -62
- 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
package/dist/mcp-server/index.js
CHANGED
|
@@ -8,22 +8,63 @@
|
|
|
8
8
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
9
9
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
10
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
-
import { readFileSync, readdirSync, existsSync, statSync
|
|
12
|
-
import { join
|
|
13
|
-
import {
|
|
11
|
+
import { readFileSync, readdirSync, existsSync, statSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
import { isAllowedWeChatArticleUrl, safeChildPath, safeDate } from '../src/utils/mcpSecurity.js';
|
|
14
|
+
import { resolvePackageRoot } from '../src/utils/packageRoot.js';
|
|
15
|
+
import { chatService } from '../src/services/chatService.js';
|
|
16
|
+
import { createWeFlowEnvelope } from '../src/services/messageContract.js';
|
|
14
17
|
// ---- 微信公众号文章抓取 ----
|
|
18
|
+
async function readResponseTextLimited(response, maximumBytes) {
|
|
19
|
+
const contentLength = Number(response.headers.get('content-length') || 0);
|
|
20
|
+
if (contentLength > maximumBytes)
|
|
21
|
+
throw new Error('RESPONSE_TOO_LARGE');
|
|
22
|
+
if (!response.body)
|
|
23
|
+
return '';
|
|
24
|
+
const reader = response.body.getReader();
|
|
25
|
+
const chunks = [];
|
|
26
|
+
let size = 0;
|
|
27
|
+
while (true) {
|
|
28
|
+
const { done, value } = await reader.read();
|
|
29
|
+
if (done)
|
|
30
|
+
break;
|
|
31
|
+
size += value.byteLength;
|
|
32
|
+
if (size > maximumBytes) {
|
|
33
|
+
await reader.cancel();
|
|
34
|
+
throw new Error('RESPONSE_TOO_LARGE');
|
|
35
|
+
}
|
|
36
|
+
chunks.push(value);
|
|
37
|
+
}
|
|
38
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
39
|
+
}
|
|
15
40
|
async function fetchWeChatArticle(url) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
let currentUrl = url;
|
|
42
|
+
let resp = null;
|
|
43
|
+
for (let redirectCount = 0; redirectCount <= 3; redirectCount++) {
|
|
44
|
+
if (!isAllowedWeChatArticleUrl(currentUrl))
|
|
45
|
+
throw new Error('URL_NOT_ALLOWED');
|
|
46
|
+
resp = await fetch(currentUrl, {
|
|
47
|
+
headers: {
|
|
48
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
49
|
+
'Accept': 'text/html,application/xhtml+xml',
|
|
50
|
+
'Accept-Language': 'zh-CN,zh;q=0.9',
|
|
51
|
+
},
|
|
52
|
+
redirect: 'manual',
|
|
53
|
+
signal: AbortSignal.timeout(20_000),
|
|
54
|
+
});
|
|
55
|
+
if (resp.status < 300 || resp.status >= 400)
|
|
56
|
+
break;
|
|
57
|
+
const location = resp.headers.get('location');
|
|
58
|
+
if (!location)
|
|
59
|
+
throw new Error('REDIRECT_WITHOUT_LOCATION');
|
|
60
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
61
|
+
resp = null;
|
|
62
|
+
}
|
|
63
|
+
if (!resp)
|
|
64
|
+
throw new Error('TOO_MANY_REDIRECTS');
|
|
24
65
|
if (!resp.ok)
|
|
25
66
|
throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
|
|
26
|
-
const html = await resp
|
|
67
|
+
const html = await readResponseTextLimited(resp, 5 * 1024 * 1024);
|
|
27
68
|
// 提取 meta 信息
|
|
28
69
|
const title = html.match(/<meta[^>]*property="og:title"[^>]*content="([^"]+)"/i)?.[1]
|
|
29
70
|
|| html.match(/<title>([^<]+)<\/title>/i)?.[1]?.trim()
|
|
@@ -94,75 +135,74 @@ function htmlToMarkdown(html) {
|
|
|
94
135
|
return md;
|
|
95
136
|
}
|
|
96
137
|
import { formatWeChatArticle, listThemes } from '../src/services/wechat-formatter.js';
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
138
|
+
import { boundedToolInteger, MCP_READ_ONLY_TOOL_DEFS, executeTool } from '../src/services/assistantTools.js';
|
|
139
|
+
import { AssistantMemory } from '../src/services/assistantMemory.js';
|
|
140
|
+
// ---- 本地微信数据工具 (复用 assistant 工具层, 微信 bot / MCP server 同源) ----
|
|
141
|
+
// MCP 无微信用户身份, 记忆固定挂 'mcp' 用户下; 与 assistant 守护进程共用同一持久化文件
|
|
142
|
+
const mcpMemory = new AssistantMemory();
|
|
143
|
+
const MCP_TOOL_CTX = { userId: 'mcp', memory: mcpMemory };
|
|
144
|
+
/** assistant 工具 (OpenAI function 格式) → MCP 工具格式; get_stats 并入现有 wechat.get_stats */
|
|
145
|
+
const ASSISTANT_TOOLS = MCP_READ_ONLY_TOOL_DEFS
|
|
146
|
+
.filter(t => t.function.name !== 'get_stats')
|
|
147
|
+
.map(t => ({
|
|
148
|
+
name: `wechat.${t.function.name}`,
|
|
149
|
+
description: t.function.description,
|
|
150
|
+
inputSchema: t.function.parameters,
|
|
151
|
+
}));
|
|
152
|
+
const PKG_ROOT = resolvePackageRoot(import.meta.url);
|
|
153
|
+
const BIZ_DAILY = join(PKG_ROOT, 'output', 'biz-daily');
|
|
154
|
+
const VAULT_WIKI = join(PKG_ROOT, 'output', 'wechat-vault', 'Wiki', 'Concepts');
|
|
155
|
+
const VAULT_INDEX = join(PKG_ROOT, 'output', 'wechat-vault', 'Wiki', '00-Overview.md');
|
|
156
|
+
const REVIEWS = join(PKG_ROOT, 'output', 'reviews', 'Daily');
|
|
157
|
+
const MCP_MESSAGE_LIMIT_DEFAULT = 100;
|
|
158
|
+
const MCP_MESSAGE_LIMIT_MAX = 1000;
|
|
159
|
+
function parseMessageDate(value, label) {
|
|
160
|
+
if (value === undefined || value === null || value === '')
|
|
161
|
+
return undefined;
|
|
162
|
+
const date = safeDate(value);
|
|
163
|
+
if (!date)
|
|
164
|
+
throw new Error(`${label} 必须是有效的 YYYY-MM-DD 日期`);
|
|
165
|
+
const [year, month, day] = date.split('-').map(Number);
|
|
166
|
+
const start = new Date(year, month - 1, day);
|
|
167
|
+
if (start.getFullYear() !== year || start.getMonth() !== month - 1 || start.getDate() !== day) {
|
|
168
|
+
throw new Error(`${label} 必须是有效的日历日期`);
|
|
112
169
|
}
|
|
113
|
-
|
|
114
|
-
token: data.access_token,
|
|
115
|
-
expiresAt: Date.now() + (data.expires_in - 60) * 1000,
|
|
116
|
-
};
|
|
117
|
-
return _tokenCache.token;
|
|
170
|
+
return Math.floor(start.getTime() / 1000);
|
|
118
171
|
}
|
|
119
|
-
async function
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const body = new Uint8Array(headerBytes.length + buf.length + trailerBytes.length);
|
|
135
|
-
body.set(headerBytes, 0);
|
|
136
|
-
body.set(buf, headerBytes.length);
|
|
137
|
-
body.set(trailerBytes, headerBytes.length + buf.length);
|
|
138
|
-
const resp = await fetch(`https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=${token}&type=image`, {
|
|
139
|
-
method: 'POST',
|
|
140
|
-
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
|
|
141
|
-
body,
|
|
142
|
-
});
|
|
143
|
-
const data = await resp.json();
|
|
144
|
-
if (data.errcode) {
|
|
145
|
-
throw new Error(`上传封面图失败: ${data.errmsg} (${data.errcode})`);
|
|
146
|
-
}
|
|
147
|
-
return data.media_id;
|
|
172
|
+
async function resolveMcpTalker(input) {
|
|
173
|
+
const contact = String(input || '').trim();
|
|
174
|
+
if (!contact)
|
|
175
|
+
throw new Error('contact 不能为空');
|
|
176
|
+
if (contact.startsWith('wxid_') || contact.includes('@chatroom') || contact.includes('@openim'))
|
|
177
|
+
return contact;
|
|
178
|
+
const sessions = await chatService.listSessions(contact, 20);
|
|
179
|
+
const exact = sessions.filter(session => session.username === contact || session.displayName === contact);
|
|
180
|
+
if (exact.length === 1)
|
|
181
|
+
return exact[0].username;
|
|
182
|
+
if (exact.length > 1 || sessions.length > 1)
|
|
183
|
+
throw new Error('contact 匹配到多个会话,请使用会话 ID');
|
|
184
|
+
if (sessions.length === 1)
|
|
185
|
+
return sessions[0].username;
|
|
186
|
+
throw new Error('未找到对应会话,请使用会话 ID 或准确的会话名称');
|
|
148
187
|
}
|
|
149
|
-
async function
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
body: JSON.stringify({ articles }),
|
|
154
|
-
});
|
|
155
|
-
const data = await resp.json();
|
|
156
|
-
if (data.errcode) {
|
|
157
|
-
throw new Error(`创建草稿失败: ${data.errmsg} (${data.errcode})`);
|
|
188
|
+
async function exportMessagesForMcp(args) {
|
|
189
|
+
const limit = args.limit === undefined ? MCP_MESSAGE_LIMIT_DEFAULT : Number(args.limit);
|
|
190
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MCP_MESSAGE_LIMIT_MAX) {
|
|
191
|
+
throw new Error(`limit 必须是 1-${MCP_MESSAGE_LIMIT_MAX} 的整数`);
|
|
158
192
|
}
|
|
159
|
-
|
|
193
|
+
const from = parseMessageDate(args.from, 'from');
|
|
194
|
+
const toStart = parseMessageDate(args.to, 'to');
|
|
195
|
+
const to = toStart === undefined ? undefined : toStart + 24 * 60 * 60 - 1;
|
|
196
|
+
if (from !== undefined && to !== undefined && from > to)
|
|
197
|
+
throw new Error('from 不能晚于 to');
|
|
198
|
+
const talker = await resolveMcpTalker(args.contact);
|
|
199
|
+
const messages = await chatService.getMessagesInRange(talker, limit, from, to);
|
|
200
|
+
return JSON.stringify(createWeFlowEnvelope(messages, new Date().toISOString(), {
|
|
201
|
+
requestedFrom: from,
|
|
202
|
+
requestedTo: to,
|
|
203
|
+
requestedLimit: limit,
|
|
204
|
+
}), null, 2);
|
|
160
205
|
}
|
|
161
|
-
const PKG_ROOT = resolve(join(import.meta.dirname || '.', '..'));
|
|
162
|
-
const BIZ_DAILY = join(PKG_ROOT, 'output', 'biz-daily');
|
|
163
|
-
const VAULT_WIKI = join(PKG_ROOT, 'output', 'wechat-vault', 'Wiki', 'Concepts');
|
|
164
|
-
const VAULT_INDEX = join(PKG_ROOT, 'output', 'wechat-vault', 'Wiki', '00-Overview.md');
|
|
165
|
-
const REVIEWS = join(PKG_ROOT, 'output', 'reviews', 'Daily');
|
|
166
206
|
function parseFrontmatter(text) {
|
|
167
207
|
if (!text.startsWith('---'))
|
|
168
208
|
return {};
|
|
@@ -229,70 +269,12 @@ function searchArticles(args) {
|
|
|
229
269
|
if (args.date) {
|
|
230
270
|
articles = articles.filter(a => a.dateDir === args.date);
|
|
231
271
|
}
|
|
232
|
-
const limit =
|
|
272
|
+
const limit = boundedToolInteger(args.limit, 20, 100);
|
|
233
273
|
const top = articles.slice(0, limit);
|
|
234
274
|
if (!top.length)
|
|
235
275
|
return '未找到匹配文章';
|
|
236
276
|
return top.map(a => `- [${a.date}] **${a.title || '(无标题)'}** — ${a.source || ''} [${a.topic || ''}] [${(a.tags || []).join(', ')}]\n ${(a.description || '').slice(0, 120)}`).join('\n\n');
|
|
237
277
|
}
|
|
238
|
-
/** 自动生成封面图(纯色主题渐变 PNG,无外部依赖) */
|
|
239
|
-
function generateCoverImage(title, theme) {
|
|
240
|
-
const colors = {
|
|
241
|
-
warm: [230, 126, 34],
|
|
242
|
-
default: [52, 152, 219],
|
|
243
|
-
minimal: [51, 51, 51],
|
|
244
|
-
green: [39, 174, 96],
|
|
245
|
-
};
|
|
246
|
-
const [r, g, b] = colors[theme] || colors.default;
|
|
247
|
-
const w = 900, h = 500;
|
|
248
|
-
function crc32(buf) {
|
|
249
|
-
let c = 0xffffffff;
|
|
250
|
-
for (let i = 0; i < buf.length; i++) {
|
|
251
|
-
c ^= buf[i];
|
|
252
|
-
for (let j = 0; j < 8; j++)
|
|
253
|
-
c = (c >>> 1) ^ (c & 1 ? 0xedb88320 : 0);
|
|
254
|
-
}
|
|
255
|
-
return (c ^ 0xffffffff) >>> 0;
|
|
256
|
-
}
|
|
257
|
-
function chunk(type, data) {
|
|
258
|
-
const head = Buffer.alloc(8);
|
|
259
|
-
head.writeUInt32BE(data.length, 0);
|
|
260
|
-
head.write(type, 4);
|
|
261
|
-
const crcBuf = Buffer.alloc(4);
|
|
262
|
-
crcBuf.writeUInt32BE(crc32(Buffer.concat([head.slice(4, 8), data])), 0);
|
|
263
|
-
return Buffer.concat([head, data, crcBuf]);
|
|
264
|
-
}
|
|
265
|
-
const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
266
|
-
const ihdr = Buffer.alloc(13);
|
|
267
|
-
ihdr.writeUInt32BE(w, 0);
|
|
268
|
-
ihdr.writeUInt32BE(h, 4);
|
|
269
|
-
ihdr[8] = 8;
|
|
270
|
-
ihdr[9] = 2;
|
|
271
|
-
const raw = Buffer.alloc(h * (1 + w * 3));
|
|
272
|
-
for (let y = 0; y < h; y++) {
|
|
273
|
-
const offset = y * (1 + w * 3);
|
|
274
|
-
raw[offset] = 0;
|
|
275
|
-
for (let x = 0; x < w; x++) {
|
|
276
|
-
const px = offset + 1 + x * 3;
|
|
277
|
-
const darken = 0.9 + 0.1 * (y / h);
|
|
278
|
-
raw[px] = Math.floor(r * darken);
|
|
279
|
-
raw[px + 1] = Math.floor(g * darken);
|
|
280
|
-
raw[px + 2] = Math.floor(b * darken);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
const png = Buffer.concat([
|
|
284
|
-
signature,
|
|
285
|
-
chunk('IHDR', ihdr),
|
|
286
|
-
chunk('IDAT', deflateSync(raw)),
|
|
287
|
-
chunk('IEND', Buffer.alloc(0)),
|
|
288
|
-
]);
|
|
289
|
-
const tmpDir = join(PKG_ROOT, 'output', '.tmp');
|
|
290
|
-
if (!existsSync(tmpDir))
|
|
291
|
-
mkdirSync(tmpDir, { recursive: true });
|
|
292
|
-
const path = join(tmpDir, `cover-${Date.now()}.png`);
|
|
293
|
-
writeFileSync(path, png);
|
|
294
|
-
return path;
|
|
295
|
-
}
|
|
296
278
|
async function main() {
|
|
297
279
|
const server = new Server({ name: 'weflow-mcp', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
298
280
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -348,7 +330,7 @@ async function main() {
|
|
|
348
330
|
},
|
|
349
331
|
{
|
|
350
332
|
name: 'wechat.get_stats',
|
|
351
|
-
description: '
|
|
333
|
+
description: '统计概览: 知识库文章/概念页 + 本地微信数据(会话数/收藏总数)',
|
|
352
334
|
inputSchema: { type: 'object', properties: {} },
|
|
353
335
|
},
|
|
354
336
|
{
|
|
@@ -363,22 +345,6 @@ async function main() {
|
|
|
363
345
|
required: ['content'],
|
|
364
346
|
},
|
|
365
347
|
},
|
|
366
|
-
{
|
|
367
|
-
name: 'wechat.publish_article',
|
|
368
|
-
description: '排版并发布 Markdown 文章到微信公众号草稿箱。需要设置环境变量 WECHAT_APPID 和 WECHAT_APPSECRET',
|
|
369
|
-
inputSchema: {
|
|
370
|
-
type: 'object',
|
|
371
|
-
properties: {
|
|
372
|
-
title: { type: 'string', description: '文章标题(≤64字符)' },
|
|
373
|
-
content: { type: 'string', description: 'Markdown 格式的文章内容' },
|
|
374
|
-
author: { type: 'string', description: '作者名(≤8字符),默认 "AI Assistant"' },
|
|
375
|
-
theme: { type: 'string', description: '排版主题: default | warm | minimal | green,默认 default' },
|
|
376
|
-
cover_image: { type: 'string', description: '封面图本地路径(可选)' },
|
|
377
|
-
preview_only: { type: 'boolean', description: '仅排版预览不发布草稿,默认 false' },
|
|
378
|
-
},
|
|
379
|
-
required: ['title', 'content'],
|
|
380
|
-
},
|
|
381
|
-
},
|
|
382
348
|
{
|
|
383
349
|
name: 'wechat.list_themes',
|
|
384
350
|
description: '列出微信公众号排版可用的所有主题',
|
|
@@ -407,6 +373,22 @@ async function main() {
|
|
|
407
373
|
required: ['url'],
|
|
408
374
|
},
|
|
409
375
|
},
|
|
376
|
+
{
|
|
377
|
+
name: 'wechat.export_messages',
|
|
378
|
+
description: '以 weflow-message/v1 JSON 返回指定会话的本地消息。只读,不返回数据库路径、密钥或配置。',
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: 'object',
|
|
381
|
+
properties: {
|
|
382
|
+
contact: { type: 'string', description: '会话 ID,或唯一匹配的会话名称' },
|
|
383
|
+
limit: { type: 'number', description: '返回上限,默认 100,最大 1000' },
|
|
384
|
+
from: { type: 'string', description: '起始日期 YYYY-MM-DD(含当天)' },
|
|
385
|
+
to: { type: 'string', description: '结束日期 YYYY-MM-DD(含当天)' },
|
|
386
|
+
},
|
|
387
|
+
required: ['contact'],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
// ---- 本地微信数据 (与 weflow-cli assistant / 微信 bot 共享工具层) ----
|
|
391
|
+
...ASSISTANT_TOOLS,
|
|
410
392
|
],
|
|
411
393
|
}));
|
|
412
394
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -417,9 +399,11 @@ async function main() {
|
|
|
417
399
|
return { content: [{ type: 'text', text: searchArticles(args) }] };
|
|
418
400
|
case 'wechat.get_daily': {
|
|
419
401
|
const dates = existsSync(BIZ_DAILY) ? readdirSync(BIZ_DAILY).sort().reverse() : [];
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
402
|
+
if (args.date && !safeDate(args.date))
|
|
403
|
+
return { content: [{ type: 'text', text: '日期无效,请使用 YYYY-MM-DD' }] };
|
|
404
|
+
const date = safeDate(args.date) || dates.find(d => /^\d{4}-\d{2}-\d{2}$/.test(d)) || '';
|
|
405
|
+
const readme = safeChildPath(BIZ_DAILY, join(date, 'README.md'));
|
|
406
|
+
if (!readme || !existsSync(readme))
|
|
423
407
|
return { content: [{ type: 'text', text: `未找到 ${date} 的日报` }] };
|
|
424
408
|
return { content: [{ type: 'text', text: readFileSync(readme, 'utf-8') }] };
|
|
425
409
|
}
|
|
@@ -430,9 +414,12 @@ async function main() {
|
|
|
430
414
|
}
|
|
431
415
|
case 'wechat.get_concept': {
|
|
432
416
|
const name = String(args.name);
|
|
417
|
+
if (!name || name.includes('/') || name.includes('\\') || name.includes('..')) {
|
|
418
|
+
return { content: [{ type: 'text', text: '概念名无效' }] };
|
|
419
|
+
}
|
|
433
420
|
for (const ext of ['', '.md']) {
|
|
434
|
-
const path =
|
|
435
|
-
if (existsSync(path)) {
|
|
421
|
+
const path = safeChildPath(VAULT_WIKI, name + ext);
|
|
422
|
+
if (path && existsSync(path)) {
|
|
436
423
|
return { content: [{ type: 'text', text: readFileSync(path, 'utf-8') }] };
|
|
437
424
|
}
|
|
438
425
|
}
|
|
@@ -440,7 +427,9 @@ async function main() {
|
|
|
440
427
|
if (existsSync(VAULT_WIKI)) {
|
|
441
428
|
for (const f of readdirSync(VAULT_WIKI)) {
|
|
442
429
|
if (f.includes(name)) {
|
|
443
|
-
|
|
430
|
+
const path = safeChildPath(VAULT_WIKI, f);
|
|
431
|
+
if (path)
|
|
432
|
+
return { content: [{ type: 'text', text: readFileSync(path, 'utf-8') }] };
|
|
444
433
|
}
|
|
445
434
|
}
|
|
446
435
|
}
|
|
@@ -451,7 +440,7 @@ async function main() {
|
|
|
451
440
|
const date = String(args.date || '');
|
|
452
441
|
let path;
|
|
453
442
|
if (date) {
|
|
454
|
-
path = join(REVIEWS, `Daily-${date}.md`);
|
|
443
|
+
path = safeDate(date) ? join(REVIEWS, `Daily-${date}.md`) : '';
|
|
455
444
|
}
|
|
456
445
|
else {
|
|
457
446
|
path = files.length ? join(REVIEWS, files[0]) : '';
|
|
@@ -489,72 +478,6 @@ async function main() {
|
|
|
489
478
|
}]
|
|
490
479
|
};
|
|
491
480
|
}
|
|
492
|
-
case 'wechat.publish_article': {
|
|
493
|
-
const title = String(args.title || '').slice(0, 64);
|
|
494
|
-
const content = String(args.content || '');
|
|
495
|
-
const author = String(args.author || 'AI Assistant').slice(0, 8);
|
|
496
|
-
const theme = String(args.theme || 'default');
|
|
497
|
-
const coverImage = args.cover_image ? String(args.cover_image) : '';
|
|
498
|
-
const previewOnly = args.preview_only === true || args.preview_only === 'true';
|
|
499
|
-
if (!title || !content) {
|
|
500
|
-
return { content: [{ type: 'text', text: '错误: title 和 content 不能为空' }] };
|
|
501
|
-
}
|
|
502
|
-
// 1. 排版
|
|
503
|
-
const html = formatWeChatArticle(content, { theme: theme });
|
|
504
|
-
const wordCount = html.replace(/<[^>]*>/g, '').replace(/\s+/g, '').length;
|
|
505
|
-
if (previewOnly) {
|
|
506
|
-
return {
|
|
507
|
-
content: [{
|
|
508
|
-
type: 'text',
|
|
509
|
-
text: [
|
|
510
|
-
`[仅预览] 标题: ${title}`,
|
|
511
|
-
`作者: ${author} | 主题: ${theme} | 字数: ${wordCount}`,
|
|
512
|
-
`---`,
|
|
513
|
-
html,
|
|
514
|
-
].join('\n')
|
|
515
|
-
}]
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
// 2. 发布到草稿箱
|
|
519
|
-
try {
|
|
520
|
-
const token = await getWeChatToken();
|
|
521
|
-
// 上传封面图(如果提供,否则自动生成)
|
|
522
|
-
let thumbMediaId = '';
|
|
523
|
-
if (coverImage && existsSync(coverImage)) {
|
|
524
|
-
thumbMediaId = await uploadWeChatImage(token, coverImage);
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
// 自动生成封面图
|
|
528
|
-
const autoCover = generateCoverImage(title, theme);
|
|
529
|
-
thumbMediaId = await uploadWeChatImage(token, autoCover);
|
|
530
|
-
}
|
|
531
|
-
// 创建草稿
|
|
532
|
-
const mediaId = await createWeChatDraft(token, [{
|
|
533
|
-
title,
|
|
534
|
-
author,
|
|
535
|
-
digest: html.replace(/<[^>]*>/g, '').replace(/\s+/g, ' ').slice(0, 120),
|
|
536
|
-
content: html,
|
|
537
|
-
thumb_media_id: thumbMediaId || undefined,
|
|
538
|
-
}]);
|
|
539
|
-
return {
|
|
540
|
-
content: [{
|
|
541
|
-
type: 'text',
|
|
542
|
-
text: [
|
|
543
|
-
`✅ 已发布到公众号草稿箱`,
|
|
544
|
-
`media_id: ${mediaId}`,
|
|
545
|
-
`标题: ${title}`,
|
|
546
|
-
`作者: ${author} | 主题: ${theme} | 字数: ${wordCount}`,
|
|
547
|
-
`封面图: ${thumbMediaId ? '已上传' : '未提供(使用默认)'}`,
|
|
548
|
-
'',
|
|
549
|
-
`请登录 mp.weixin.qq.com 草稿箱查看和正式发布。`,
|
|
550
|
-
].join('\n')
|
|
551
|
-
}]
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
catch (e) {
|
|
555
|
-
return { content: [{ type: 'text', text: `发布失败: ${e.message}\n\n——以下为排版预览——\n\n${html}` }] };
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
481
|
case 'wechat.get_stats': {
|
|
559
482
|
const articles = scanArticles();
|
|
560
483
|
const dates = [...new Set(articles.map(a => a.dateDir))].sort();
|
|
@@ -570,13 +493,22 @@ async function main() {
|
|
|
570
493
|
if (m)
|
|
571
494
|
stats += `- 概念页: ${m[1]} 个\n`;
|
|
572
495
|
}
|
|
496
|
+
// 追加本地微信数据统计 (会话/收藏; 数据库未配置时返回提示)
|
|
497
|
+
const chatStats = await executeTool('get_stats', {}, MCP_TOOL_CTX);
|
|
498
|
+
stats += `\n## 本地微信数据\n\n${chatStats}\n`;
|
|
573
499
|
return { content: [{ type: 'text', text: stats }] };
|
|
574
500
|
}
|
|
575
501
|
case 'wechat.search_public': {
|
|
576
502
|
const keyword = String(args.keyword || '').trim();
|
|
577
503
|
if (!keyword)
|
|
578
504
|
return { content: [{ type: 'text', text: '错误: keyword 不能为空' }] };
|
|
579
|
-
|
|
505
|
+
let limit = 10;
|
|
506
|
+
try {
|
|
507
|
+
limit = boundedToolInteger(args.limit, 10, 20);
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return { content: [{ type: 'text', text: '错误: limit 必须是 1-20 的整数' }] };
|
|
511
|
+
}
|
|
580
512
|
try {
|
|
581
513
|
// 使用搜狗微信搜索
|
|
582
514
|
const resp = await fetch(`https://weixin.sogou.com/weixin?type=2&query=${encodeURIComponent(keyword)}`, {
|
|
@@ -586,8 +518,9 @@ async function main() {
|
|
|
586
518
|
'Accept': 'text/html',
|
|
587
519
|
'Accept-Language': 'zh-CN,zh;q=0.9',
|
|
588
520
|
},
|
|
521
|
+
signal: AbortSignal.timeout(20_000),
|
|
589
522
|
});
|
|
590
|
-
const html = await resp
|
|
523
|
+
const html = await readResponseTextLimited(resp, 2 * 1024 * 1024);
|
|
591
524
|
// 从搜狗结果中提取标题、公众号名、摘要
|
|
592
525
|
// 搜狗将结果放在 li 标签中,每篇文章包含标题链接 + 摘要
|
|
593
526
|
const items = [];
|
|
@@ -645,16 +578,16 @@ async function main() {
|
|
|
645
578
|
}]
|
|
646
579
|
};
|
|
647
580
|
}
|
|
648
|
-
catch
|
|
649
|
-
return { content: [{ type: 'text', text:
|
|
581
|
+
catch {
|
|
582
|
+
return { content: [{ type: 'text', text: '搜索失败,请检查网络连接后重试' }] };
|
|
650
583
|
}
|
|
651
584
|
}
|
|
652
585
|
case 'wechat.fetch_article': {
|
|
653
586
|
const url = String(args.url || '').trim();
|
|
654
587
|
if (!url)
|
|
655
588
|
return { content: [{ type: 'text', text: '错误: url 不能为空' }] };
|
|
656
|
-
if (!url
|
|
657
|
-
return { content: [{ type: 'text', text: '错误:
|
|
589
|
+
if (!isAllowedWeChatArticleUrl(url)) {
|
|
590
|
+
return { content: [{ type: 'text', text: '错误: 仅支持 HTTPS 微信公众号文章链接 (mp.weixin.qq.com)' }] };
|
|
658
591
|
}
|
|
659
592
|
try {
|
|
660
593
|
const article = await fetchWeChatArticle(url);
|
|
@@ -671,16 +604,25 @@ async function main() {
|
|
|
671
604
|
}]
|
|
672
605
|
};
|
|
673
606
|
}
|
|
674
|
-
catch
|
|
675
|
-
return { content: [{ type: 'text', text:
|
|
607
|
+
catch {
|
|
608
|
+
return { content: [{ type: 'text', text: '抓取失败,请检查链接和网络连接。部分文章需要微信客户端环境才能访问。' }] };
|
|
676
609
|
}
|
|
677
610
|
}
|
|
678
|
-
|
|
611
|
+
case 'wechat.export_messages': {
|
|
612
|
+
return { content: [{ type: 'text', text: await exportMessagesForMcp(args) }] };
|
|
613
|
+
}
|
|
614
|
+
default: {
|
|
615
|
+
// assistant 工具层统一分发 (wechat.list_sessions / wechat.get_messages / ...)
|
|
616
|
+
if (name.startsWith('wechat.')) {
|
|
617
|
+
const text = await executeTool(name.slice('wechat.'.length), args, MCP_TOOL_CTX);
|
|
618
|
+
return { content: [{ type: 'text', text }] };
|
|
619
|
+
}
|
|
679
620
|
return { content: [{ type: 'text', text: `未知工具: ${name}` }] };
|
|
621
|
+
}
|
|
680
622
|
}
|
|
681
623
|
}
|
|
682
|
-
catch
|
|
683
|
-
return { content: [{ type: 'text', text:
|
|
624
|
+
catch {
|
|
625
|
+
return { content: [{ type: 'text', text: '工具执行失败,请检查输入、本地配置或运行状态' }] };
|
|
684
626
|
}
|
|
685
627
|
});
|
|
686
628
|
const transport = new StdioServerTransport();
|