weflow-cli 1.9.0 → 1.10.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 +62 -26
- package/CHANGELOG.md +948 -0
- package/OPERATIONS.md +65 -11
- package/README.en.md +14 -5
- package/README.md +21 -6
- package/bin/weflow-cli.ts +348 -39
- package/dist/bin/weflow-cli.js +363 -68
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/mcp-server/index.d.ts.map +1 -1
- package/dist/mcp-server/index.js +19 -1
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/src/core/dbPathService.d.ts +12 -0
- package/dist/src/core/dbPathService.d.ts.map +1 -1
- package/dist/src/core/dbPathService.js +20 -3
- package/dist/src/core/dbPathService.js.map +1 -1
- package/dist/src/panel/server.d.ts.map +1 -1
- package/dist/src/panel/server.js +35 -0
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/services/assistantService.d.ts +19 -0
- package/dist/src/services/assistantService.d.ts.map +1 -1
- package/dist/src/services/assistantService.js +20 -0
- package/dist/src/services/assistantService.js.map +1 -1
- package/dist/src/services/assistantTools.d.ts +27 -1
- package/dist/src/services/assistantTools.d.ts.map +1 -1
- package/dist/src/services/assistantTools.js +169 -35
- package/dist/src/services/assistantTools.js.map +1 -1
- package/dist/src/services/configService.d.ts +2 -0
- 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/messageQuery.d.ts +18 -0
- package/dist/src/services/messageQuery.d.ts.map +1 -1
- package/dist/src/services/messageQuery.js +29 -0
- package/dist/src/services/messageQuery.js.map +1 -1
- package/dist/src/services/wechatClient.d.ts +125 -0
- package/dist/src/services/wechatClient.d.ts.map +1 -0
- package/dist/src/services/wechatClient.js +344 -0
- package/dist/src/services/wechatClient.js.map +1 -0
- package/dist/src/services/wechatMedia.d.ts +44 -0
- package/dist/src/services/wechatMedia.d.ts.map +1 -0
- package/dist/src/services/wechatMedia.js +155 -0
- package/dist/src/services/wechatMedia.js.map +1 -0
- package/dist/src/services/wechatMessageService.d.ts +11 -0
- package/dist/src/services/wechatMessageService.d.ts.map +1 -1
- package/dist/src/services/wechatMessageService.js +55 -11
- package/dist/src/services/wechatMessageService.js.map +1 -1
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.d.ts.map +1 -1
- package/docs/CONTACT_DB_SCHEMA.md +1085 -0
- package/docs/DECISIONS.md +1274 -0
- package/docs/EXTENDING.md +69 -7
- package/docs/FUNCTIONAL-IMPROVEMENT-SPEC.md +6 -0
- package/docs/HEALTH-CHECK.md +12 -7
- package/docs/JEV/345/272/224/347/224/250/346/226/275/345/267/245/346/226/207/346/241/243.md +2 -2
- package/docs/MCP.md +2 -2
- package/docs/PROJECT_STATE.md +62 -5
- package/docs/RELEASING.md +115 -1
- package/docs/images/weflow-architecture-gpt-image-2.png +0 -0
- package/logo.png +0 -0
- package/mcp-server/index.ts +18 -3
- package/package.json +5 -3
- package/resources/js/graph3d/NOTICE.txt +35 -0
- package/resources/js/graph3d/d3-binarytree.min.js +2 -0
- package/resources/js/graph3d/d3-dispatch.min.js +2 -0
- package/resources/js/graph3d/d3-force-3d.min.js +2 -0
- package/resources/js/graph3d/d3-octree.min.js +2 -0
- package/resources/js/graph3d/d3-quadtree.min.js +2 -0
- package/resources/js/graph3d/d3-timer.min.js +2 -0
- package/resources/js/graph3d/layout.mjs +63 -0
- package/resources/js/graph3d/three.min.js +6 -0
- package/resources/js/graph3d/viewer2d.js +448 -0
- package/resources/panel/index.html +12 -0
- package/resources/panel/mascot-blink-1.png +0 -0
- package/resources/panel/mascot-blink-2.png +0 -0
- package/resources/panel/mascot-lift-down-1.png +0 -0
- package/resources/panel/mascot-lift-down-2.png +0 -0
- package/resources/panel/mascot-lift-down-3.png +0 -0
- package/resources/panel/mascot-lift-down-4.png +0 -0
- package/resources/panel/mascot-lift-down-5.png +0 -0
- package/resources/panel/mascot-lift-down-6.png +0 -0
- package/resources/panel/mascot-lift-up-1.png +0 -0
- package/resources/panel/mascot-lift-up-2.png +0 -0
- package/resources/panel/mascot-lift-up-3.png +0 -0
- package/resources/panel/mascot-lift-up-4.png +0 -0
- package/resources/panel/mascot-lift-up-5.png +0 -0
- package/resources/panel/mascot-lift-up-6.png +0 -0
- package/resources/panel/mascot-tickle-1.png +0 -0
- package/resources/panel/mascot-tickle-2.png +0 -0
- package/resources/panel/mascot-tickle-3.png +0 -0
- package/resources/panel/mascot-tickle-4.png +0 -0
- package/resources/panel/panel.css +111 -0
- package/resources/panel/renderer.js +480 -8
- package/scripts/_utils.py +1203 -977
- package/scripts/biz_daily.py +25 -10
- package/scripts/chat_notes.py +15 -5
- package/scripts/check_readme_commands.py +426 -0
- package/scripts/compile_wiki.py +163 -57
- package/scripts/contact_schema.py +339 -0
- package/scripts/data/wechat_emoji.json +225 -0
- package/scripts/fav_notes.py +4 -1
- package/scripts/graph_2d.py +151 -0
- package/scripts/graph_3d.py +646 -0
- package/scripts/health_check.py +163 -26
- package/scripts/nt_decrypt.py +3 -3
- package/scripts/panel_frames.py +683 -0
- package/scripts/pipeline.py +21 -10
- package/scripts/quality_eval.py +5 -0
- package/scripts/rag_chat.py +33 -16
- package/scripts/reply_debt.py +26 -6
- package/scripts/semantic_search.py +130 -22
- package/scripts/vault_rag.py +42 -16
- package/scripts/vault_search.py +44 -8
- package/scripts/wechat_emoticon.py +6 -0
- package/scripts/wiki_lint.py +94 -17
- package/src/core/dbPathService.ts +22 -4
- package/src/panel/server.ts +33 -0
- package/src/services/assistantService.ts +26 -0
- package/src/services/assistantTools.ts +2452 -2319
- package/src/services/configService.ts +5 -2
- package/src/services/messageQuery.ts +29 -0
- package/src/services/wechatClient.ts +380 -0
- package/src/services/wechatMedia.ts +166 -0
- package/src/services/wechatMessageService.ts +62 -11
- package/src/types.ts +4 -0
- package/dist/src/utils/wechatEmoji.d.ts +0 -5
- package/dist/src/utils/wechatEmoji.d.ts.map +0 -1
- package/dist/src/utils/wechatEmoji.js +0 -35
- package/dist/src/utils/wechatEmoji.js.map +0 -1
|
@@ -8,6 +8,8 @@ import path from 'path'
|
|
|
8
8
|
import { WechatClient } from '../core/wechatClient.js'
|
|
9
9
|
import { configService } from './configService.js'
|
|
10
10
|
import { resolveInboundRouting } from './assistantRouting.js'
|
|
11
|
+
import { mediaTypeOf, saveInboundMedia, wechatMediaDir } from './wechatMedia.js'
|
|
12
|
+
import type { InboundMediaType } from './wechatMedia.js'
|
|
11
13
|
import type { WechatOCConfig, WechatLoginSession, WechatInboundMessage, WechatMessageComponent } from '../types.js'
|
|
12
14
|
|
|
13
15
|
function sleep(ms: number): Promise<void> {
|
|
@@ -64,10 +66,16 @@ export class WechatMessageService {
|
|
|
64
66
|
private messageCallbacks: Array<(msg: WechatInboundMessage) => void> = []
|
|
65
67
|
/** 服务端说过 token 失效了。**只由长轮询置位**(见 `classifyPollResult`),恢复时清掉 */
|
|
66
68
|
private tokenExpired = false
|
|
69
|
+
/** 入站媒体是否落地。undefined = 每次去读配置(默认关,见 `wechatMedia.ts` 的边界 1) */
|
|
70
|
+
private mediaDownload?: boolean | string
|
|
71
|
+
/** 落地目录。undefined = 用 `wechatMediaDir()`(可被环境变量改,测试用临时目录) */
|
|
72
|
+
private mediaDirOverride?: string
|
|
67
73
|
|
|
68
74
|
constructor(config: WechatOCConfig = {}) {
|
|
69
75
|
this.config = config
|
|
70
76
|
this.syncBuf = config.syncBuf || ''
|
|
77
|
+
this.mediaDownload = config.mediaDownload
|
|
78
|
+
this.mediaDirOverride = config.mediaDir
|
|
71
79
|
this.client = new WechatClient({
|
|
72
80
|
baseUrl: config.baseUrl,
|
|
73
81
|
cdnBaseUrl: config.cdnBaseUrl,
|
|
@@ -225,7 +233,7 @@ export class WechatMessageService {
|
|
|
225
233
|
const msgs: any[] = data.msgs || []
|
|
226
234
|
for (const msg of msgs) {
|
|
227
235
|
if (this.shutdownFlag) return
|
|
228
|
-
const inbound = this.parseInboundMessage(msg)
|
|
236
|
+
const inbound = await this.parseInboundMessage(msg)
|
|
229
237
|
if (inbound) {
|
|
230
238
|
for (const cb of this.messageCallbacks) {
|
|
231
239
|
try { cb(inbound) } catch (error: any) {
|
|
@@ -409,7 +417,40 @@ export class WechatMessageService {
|
|
|
409
417
|
|
|
410
418
|
// ====== Private ======
|
|
411
419
|
|
|
412
|
-
|
|
420
|
+
/** 入站媒体落地的开关:构造时注入的优先,其次读配置。默认关 —— 关着就不发任何网络请求 */
|
|
421
|
+
private mediaDownloadEnabled(): boolean {
|
|
422
|
+
const raw = this.mediaDownload ?? configService.get('wechatMediaDownload')
|
|
423
|
+
return raw === true || raw === 'true'
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* 下载一件入站媒体并落盘,返回路径;**没开这个功能、或下载失败,都返回空串**。
|
|
428
|
+
* 失败会往日志里写一行原因 —— "没开"和"开了但失败了"必须分得开,否则出事时两种证据全无。
|
|
429
|
+
*/
|
|
430
|
+
private async inboundMediaPath(
|
|
431
|
+
item: any,
|
|
432
|
+
mediaType: InboundMediaType,
|
|
433
|
+
index: number,
|
|
434
|
+
messageId: string,
|
|
435
|
+
): Promise<string> {
|
|
436
|
+
if (!this.mediaDownloadEnabled()) return ''
|
|
437
|
+
const result = await saveInboundMedia({
|
|
438
|
+
item,
|
|
439
|
+
mediaType,
|
|
440
|
+
index,
|
|
441
|
+
messageId,
|
|
442
|
+
dir: this.mediaDirOverride || wechatMediaDir(),
|
|
443
|
+
serverFileName: mediaType === 'file' ? item?.file_item?.file_name : undefined,
|
|
444
|
+
download: (param, key) => this.client.downloadMedia(param, key),
|
|
445
|
+
})
|
|
446
|
+
if (!result.path) {
|
|
447
|
+
console.warn(`入站媒体未落地(${mediaType}):${result.reason}`)
|
|
448
|
+
return ''
|
|
449
|
+
}
|
|
450
|
+
return result.path
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private async parseInboundMessage(msg: any): Promise<WechatInboundMessage | null> {
|
|
413
454
|
const fromUserId: string = msg.from_user_id || ''
|
|
414
455
|
if (!fromUserId) return null
|
|
415
456
|
const routing = resolveInboundRouting(msg, String(configService.get('wechatOcAccountId') || ''))
|
|
@@ -423,19 +464,29 @@ export class WechatMessageService {
|
|
|
423
464
|
|
|
424
465
|
const components: WechatMessageComponent[] = []
|
|
425
466
|
const itemList: any[] = msg.item_list || []
|
|
467
|
+
const messageId: string = msg.client_id || uuidHex()
|
|
468
|
+
let mediaIndex = 0
|
|
426
469
|
|
|
427
470
|
for (const item of itemList) {
|
|
428
471
|
const itemType = item.type as number
|
|
429
472
|
if (itemType === 1 && item.text_item) {
|
|
430
473
|
components.push({ type: 'plain', text: item.text_item.text || '' })
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
474
|
+
continue
|
|
475
|
+
}
|
|
476
|
+
const mediaType = mediaTypeOf(itemType)
|
|
477
|
+
// 缺节点就整项跳过(与既有行为一致:形状不对的项不进 components)
|
|
478
|
+
if (!mediaType || !item[`${mediaType === 'record' ? 'voice' : mediaType}_item`]) continue
|
|
479
|
+
mediaIndex += 1
|
|
480
|
+
// 落盘(默认关,关着时这里不做任何 IO)
|
|
481
|
+
const filePath = await this.inboundMediaPath(item, mediaType, mediaIndex, messageId)
|
|
482
|
+
if (mediaType === 'image') {
|
|
483
|
+
components.push({ type: 'image', filePath })
|
|
484
|
+
} else if (mediaType === 'record') {
|
|
485
|
+
components.push({ type: 'record', filePath })
|
|
486
|
+
} else if (mediaType === 'file') {
|
|
487
|
+
components.push({ type: 'file', name: item.file_item.file_name || '', filePath })
|
|
488
|
+
} else {
|
|
489
|
+
components.push({ type: 'video', filePath })
|
|
439
490
|
}
|
|
440
491
|
}
|
|
441
492
|
|
|
@@ -454,7 +505,7 @@ export class WechatMessageService {
|
|
|
454
505
|
const messageStr = textComponents.map(c => (c as { type: 'plain'; text: string }).text).join('')
|
|
455
506
|
|
|
456
507
|
return {
|
|
457
|
-
messageId
|
|
508
|
+
messageId,
|
|
458
509
|
fromUserId,
|
|
459
510
|
...routing,
|
|
460
511
|
senderNickname: msg.from_user_id || '',
|
package/src/types.ts
CHANGED
|
@@ -65,6 +65,10 @@ export interface WechatOCConfig {
|
|
|
65
65
|
apiTimeoutMs?: number
|
|
66
66
|
typingKeepaliveIntervalS?: number
|
|
67
67
|
typingTicketTtlS?: number
|
|
68
|
+
/** 入站媒体是否落地(缺省=读 `wechatMediaDownload` 配置,默认关)。见 src/services/wechatMedia.ts */
|
|
69
|
+
mediaDownload?: boolean | string
|
|
70
|
+
/** 入站媒体落地目录(缺省=output/wechat-media/,可被 WEFLOW_WECHAT_MEDIA_DIR 改) */
|
|
71
|
+
mediaDir?: string
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
export interface WechatLoginSession {
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/** Replace known WeChat face codes like [害羞] with emoji; unknown codes pass through. */
|
|
2
|
-
export declare function convertFaces(text: string | undefined | null): string;
|
|
3
|
-
/** Convert faces, then truncate without splitting a surrogate pair. */
|
|
4
|
-
export declare function convertFacesTruncated(text: string | undefined | null, max: number): string;
|
|
5
|
-
//# sourceMappingURL=wechatEmoji.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wechatEmoji.d.ts","sourceRoot":"","sources":["../../../src/utils/wechatEmoji.ts"],"names":[],"mappings":"AA4BA,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAGpE;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1F"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { dirname, join } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = dirname(__filename);
|
|
6
|
-
// dist/src/utils -> package root. The table is shared with the Python side.
|
|
7
|
-
const DATA_FILE = join(__dirname, '..', '..', '..', 'scripts', 'data', 'wechat_emoji.json');
|
|
8
|
-
const faces = {};
|
|
9
|
-
try {
|
|
10
|
-
const parsed = JSON.parse(readFileSync(DATA_FILE, 'utf8'));
|
|
11
|
-
for (const [name, emoji] of Object.entries(parsed)) {
|
|
12
|
-
// Entries starting with "_" are documentation, not faces.
|
|
13
|
-
if (!name.startsWith('_') && typeof emoji === 'string') {
|
|
14
|
-
faces[name] = emoji;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
// Missing table just means faces stay as their [名称] source text.
|
|
20
|
-
}
|
|
21
|
-
const escapeRe = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
-
const names = Object.keys(faces).sort((a, b) => b.length - a.length);
|
|
23
|
-
// Longest name first so [左哼哼] can never be shadowed by a shorter prefix.
|
|
24
|
-
const pattern = names.length > 0 ? new RegExp(`\\[(${names.map(escapeRe).join('|')})\\]`, 'g') : null;
|
|
25
|
-
/** Replace known WeChat face codes like [害羞] with emoji; unknown codes pass through. */
|
|
26
|
-
export function convertFaces(text) {
|
|
27
|
-
if (!text || !pattern)
|
|
28
|
-
return text || '';
|
|
29
|
-
return text.replace(pattern, (match, name) => faces[name] ?? match);
|
|
30
|
-
}
|
|
31
|
-
/** Convert faces, then truncate without splitting a surrogate pair. */
|
|
32
|
-
export function convertFacesTruncated(text, max) {
|
|
33
|
-
return Array.from(convertFaces(text)).slice(0, max).join('');
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=wechatEmoji.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wechatEmoji.js","sourceRoot":"","sources":["../../../src/utils/wechatEmoji.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;AAErC,4EAA4E;AAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAA;AAE3F,MAAM,KAAK,GAA2B,EAAE,CAAA;AACxC,IAAI,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAA4B,CAAA;IACrF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,0DAA0D;QAC1D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvD,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;AACH,CAAC;AAAC,MAAM,CAAC;IACP,iEAAiE;AACnE,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACtF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;AACpE,yEAAyE;AACzE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AAErG,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,IAA+B;IAC1D,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,IAAI,EAAE,CAAA;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,qBAAqB,CAAC,IAA+B,EAAE,GAAW;IAChF,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC9D,CAAC"}
|