weflow-cli 1.8.2 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +342 -0
- package/OPERATIONS.md +79 -0
- package/README.en.md +9 -2
- package/README.md +12 -2
- package/bin/weflow-cli-electron.cjs +2 -0
- package/bin/weflow-cli.ts +648 -79
- package/dist/bin/weflow-cli.js +675 -78
- package/dist/bin/weflow-cli.js.map +1 -1
- package/dist/mcp-server/http.d.ts +14 -0
- package/dist/mcp-server/http.d.ts.map +1 -0
- package/dist/mcp-server/http.js +84 -0
- package/dist/mcp-server/http.js.map +1 -0
- package/dist/mcp-server/httpConfig.d.ts +50 -0
- package/dist/mcp-server/httpConfig.d.ts.map +1 -0
- package/dist/mcp-server/httpConfig.js +104 -0
- package/dist/mcp-server/httpConfig.js.map +1 -0
- package/dist/mcp-server/index.d.ts +13 -1
- package/dist/mcp-server/index.d.ts.map +1 -1
- package/dist/mcp-server/index.js +39 -373
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/src/panel/server.d.ts.map +1 -1
- package/dist/src/panel/server.js +26 -1
- package/dist/src/panel/server.js.map +1 -1
- package/dist/src/services/assistantEval.d.ts +69 -0
- package/dist/src/services/assistantEval.d.ts.map +1 -1
- package/dist/src/services/assistantEval.js +270 -8
- package/dist/src/services/assistantEval.js.map +1 -1
- package/dist/src/services/assistantScenes.d.ts +81 -0
- package/dist/src/services/assistantScenes.d.ts.map +1 -0
- package/dist/src/services/assistantScenes.js +322 -0
- package/dist/src/services/assistantScenes.js.map +1 -0
- package/dist/src/services/assistantService.d.ts +14 -4
- package/dist/src/services/assistantService.d.ts.map +1 -1
- package/dist/src/services/assistantService.js +82 -6
- package/dist/src/services/assistantService.js.map +1 -1
- package/dist/src/services/assistantSkills.d.ts +94 -0
- package/dist/src/services/assistantSkills.d.ts.map +1 -0
- package/dist/src/services/assistantSkills.js +330 -0
- package/dist/src/services/assistantSkills.js.map +1 -0
- package/dist/src/services/assistantTools.d.ts +7 -0
- package/dist/src/services/assistantTools.d.ts.map +1 -1
- package/dist/src/services/assistantTools.js +770 -61
- package/dist/src/services/assistantTools.js.map +1 -1
- package/dist/src/services/assistantTrace.d.ts +4 -0
- package/dist/src/services/assistantTrace.d.ts.map +1 -1
- package/dist/src/services/assistantTrace.js +9 -0
- package/dist/src/services/assistantTrace.js.map +1 -1
- package/dist/src/services/configService.d.ts +7 -0
- package/dist/src/services/configService.d.ts.map +1 -1
- package/dist/src/services/configService.js +4 -2
- package/dist/src/services/configService.js.map +1 -1
- package/dist/src/services/outboundSend.d.ts +53 -0
- package/dist/src/services/outboundSend.d.ts.map +1 -0
- package/dist/src/services/outboundSend.js +90 -0
- package/dist/src/services/outboundSend.js.map +1 -0
- package/dist/src/services/pythonBridge.d.ts.map +1 -1
- package/dist/src/services/pythonBridge.js +1 -0
- package/dist/src/services/pythonBridge.js.map +1 -1
- package/dist/src/utils/python.js +2 -2
- package/dist/src/utils/python.js.map +1 -1
- package/docs/BRANCHES.md +37 -0
- package/docs/DECISIONS.md +350 -0
- package/docs/EXTENDING.md +29 -3
- package/docs/MCP.md +56 -2
- package/docs/PROJECT_STATE.md +121 -9
- package/mcp-server/http.ts +108 -0
- package/mcp-server/httpConfig.ts +126 -0
- package/mcp-server/index.ts +344 -681
- package/package.json +1 -1
- package/resources/panel/ball-position.cjs +48 -2
- package/resources/panel/index.html +6 -4
- package/resources/panel/main.cjs +137 -5
- package/resources/panel/mascot-base.png +0 -0
- package/resources/panel/mascot-iris.png +0 -0
- package/resources/panel/mascot-peek.png +0 -0
- package/resources/panel/panel.css +95 -60
- package/resources/panel/preload.cjs +26 -0
- package/resources/panel/renderer.js +167 -10
- package/scripts/_utils.py +47 -1
- package/scripts/backfill_articles.py +202 -21
- package/scripts/biz_daily.py +8 -31
- package/scripts/bonds.py +331 -0
- package/scripts/chat_notes.py +551 -470
- package/scripts/chat_stats.py +8 -7
- package/scripts/compile_wiki.py +24 -2
- package/scripts/export_chat_html.py +3 -1
- package/scripts/mcp_bridge.py +6 -7
- package/scripts/wechat_voice.py +9 -0
- package/scripts/wiki_lint.py +30 -7
- package/src/panel/server.ts +25 -1
- package/src/services/assistantEval.ts +323 -12
- package/src/services/assistantScenes.ts +342 -0
- package/src/services/assistantService.ts +81 -6
- package/src/services/assistantSkills.ts +364 -0
- package/src/services/assistantTools.ts +738 -59
- package/src/services/assistantTrace.ts +9 -0
- package/src/services/configService.ts +412 -403
- package/src/services/outboundSend.ts +126 -0
- package/src/services/pythonBridge.ts +1 -0
- package/src/utils/python.ts +2 -2
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 传输的**判定**——纯函数,不碰 socket、不连 MCP,所以能离线测。
|
|
3
|
+
*
|
|
4
|
+
* 为什么值得单独一个文件:这里每一条规则决定的都是"要不要把本机的私密数据摊到一个网络
|
|
5
|
+
* 端口上"。服务器本体要起 socket、要装 SDK,测起来重;把判定抽出来,规则就能一条一条钉住
|
|
6
|
+
* (`test/mcp-http.test.ts`)。
|
|
7
|
+
*
|
|
8
|
+
* 边界(写死在这里,不靠调用方自觉):
|
|
9
|
+
* - **默认不开**:不给 `--http` 也不给 `WEFLOW_MCP_HTTP` 就还是 stdio,行为与以前逐字相同。
|
|
10
|
+
* - **必须带令牌**:没给就**拒绝启动**,不给默认值 —— 一个悄悄生成的默认口令比没有更坏,
|
|
11
|
+
* 因为它看起来是"配好了"。
|
|
12
|
+
* - **只绑回环**:远程访问自己走 SSH 隧道或反向代理。把私密聊天记录摊到局域网上,
|
|
13
|
+
* 不该由这个文件顺手打开。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createHash, timingSafeEqual } from 'node:crypto'
|
|
17
|
+
|
|
18
|
+
/** 默认端口。5030 是 chatlog 的,避开它免得两个服务打架。 */
|
|
19
|
+
export const DEFAULT_HTTP_PORT = 8790
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 令牌长度下限。128 位随机(32 个 hex 字符)是常态;24 个字符足够排除"随手敲一个",
|
|
23
|
+
* 又不至于逼人非得用某个特定长度。
|
|
24
|
+
*/
|
|
25
|
+
export const MIN_TOKEN_CHARS = 24
|
|
26
|
+
|
|
27
|
+
/** 允许绑定的地址。**只回环**:`0.0.0.0` 不在此列,而且不是"忘了加"。 */
|
|
28
|
+
export const LOOPBACK_HOSTS = ['127.0.0.1', 'localhost', '::1']
|
|
29
|
+
|
|
30
|
+
export interface HttpOptions {
|
|
31
|
+
host: string
|
|
32
|
+
port: number
|
|
33
|
+
token: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** 这一轮是不是要走 HTTP。`--http`、`--http=8790`、`WEFLOW_MCP_HTTP=1` 都算。 */
|
|
37
|
+
export function httpModeRequested(env: Record<string, string | undefined>, argv: string[]): boolean {
|
|
38
|
+
if (argv.some((a) => a === '--http' || a.startsWith('--http='))) return true
|
|
39
|
+
const value = String(env.WEFLOW_MCP_HTTP ?? '').trim()
|
|
40
|
+
return value !== '' && value !== '0' && value.toLowerCase() !== 'false'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function flagValue(argv: string[], name: string): string | undefined {
|
|
44
|
+
const hit = argv.find((a) => a.startsWith(`${name}=`))
|
|
45
|
+
return hit ? hit.slice(name.length + 1) : undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 解出 HTTP 模式的参数。**不走 HTTP 就返回 `null`**;要走但配置不合法就**抛错**(fail closed,
|
|
50
|
+
* 绝不"降级成不安全的默认值"继续跑)。
|
|
51
|
+
*
|
|
52
|
+
* 端口允许 `0`:那是让内核挑一个空闲端口,测试要用;正式跑用默认端口。
|
|
53
|
+
*/
|
|
54
|
+
export function resolveHttpOptions(
|
|
55
|
+
env: Record<string, string | undefined>,
|
|
56
|
+
argv: string[],
|
|
57
|
+
): HttpOptions | null {
|
|
58
|
+
if (!httpModeRequested(env, argv)) return null
|
|
59
|
+
|
|
60
|
+
const token = String(flagValue(argv, '--token') ?? env.WEFLOW_MCP_TOKEN ?? '').trim()
|
|
61
|
+
if (!token) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
'HTTP 模式必须给令牌:`--token=<随机串>` 或环境变量 WEFLOW_MCP_TOKEN。\n'
|
|
64
|
+
+ '没有默认值是有意的 —— 一个自动生成、只在日志里露一面的口令,看起来像"配好了"。\n'
|
|
65
|
+
+ '生成一个:node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"',
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
if (token.length < MIN_TOKEN_CHARS) {
|
|
69
|
+
throw new Error(`令牌太短(${token.length} < ${MIN_TOKEN_CHARS} 个字符)。用随机串,别用可猜的短语。`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const host = String(flagValue(argv, '--host') ?? '127.0.0.1').trim()
|
|
73
|
+
if (!LOOPBACK_HOSTS.includes(host)) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`只允许绑回环地址(${LOOPBACK_HOSTS.join(' / ')}),收到 ${host}。\n`
|
|
76
|
+
+ '要从别的机器连,用 SSH 隧道(ssh -L 8790:127.0.0.1:8790 <host>)或自己的反向代理 —— '
|
|
77
|
+
+ '把聊天记录摊到局域网上不该由这个命令顺手打开。',
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const raw = flagValue(argv, '--http') ?? flagValue(argv, '--port') ?? env.WEFLOW_MCP_HTTP_PORT ?? String(DEFAULT_HTTP_PORT)
|
|
82
|
+
const rawText = String(raw).trim()
|
|
83
|
+
// 先按"纯数字"卡一道:`Number('') === 0`,不卡的话空值会静默变成一个端口。
|
|
84
|
+
if (!/^\d{1,5}$/.test(rawText)) {
|
|
85
|
+
throw new Error(`端口要 0-65535 之间的整数(0 = 让系统挑一个空闲端口),收到 ${JSON.stringify(raw)}`)
|
|
86
|
+
}
|
|
87
|
+
const port = Number(rawText)
|
|
88
|
+
if (port > 65535) {
|
|
89
|
+
throw new Error(`端口要 0-65535 之间的整数,收到 ${JSON.stringify(raw)}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { host, port, token }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* `Authorization` 头认不认。
|
|
97
|
+
*
|
|
98
|
+
* 比的是**摘要**而不是原文:`timingSafeEqual` 要求等长,直接比长度会漏出"令牌有多长",
|
|
99
|
+
* 于是"不同长度直接返回 false"就成了一个可测的短路径。两边先过一遍 SHA-256,长度就固定了。
|
|
100
|
+
* 方案名按 RFC 7235 大小写不敏感。
|
|
101
|
+
*/
|
|
102
|
+
export function isAuthorized(header: string | undefined, token: string): boolean {
|
|
103
|
+
const raw = String(header ?? '')
|
|
104
|
+
const space = raw.indexOf(' ')
|
|
105
|
+
if (space < 0) return false
|
|
106
|
+
if (raw.slice(0, space).toLowerCase() !== 'bearer') return false
|
|
107
|
+
const presented = raw.slice(space + 1).trim()
|
|
108
|
+
if (!presented) return false
|
|
109
|
+
|
|
110
|
+
const digest = (value: string) => createHash('sha256').update(value, 'utf8').digest()
|
|
111
|
+
return timingSafeEqual(digest(presented), digest(token))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Host 头允许的取值(DNS 重绑定防护用)。带端口,因为 SDK 比的是**原始 Host 头**。 */
|
|
115
|
+
export function allowedHostsFor(host: string, port: number): string[] {
|
|
116
|
+
const forms = new Set<string>()
|
|
117
|
+
for (const h of host === 'localhost' ? ['127.0.0.1', 'localhost'] : [host]) {
|
|
118
|
+
forms.add(h === '::1' ? `[::1]:${port}` : `${h}:${port}`)
|
|
119
|
+
}
|
|
120
|
+
return [...forms]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** 允许的 Origin(浏览器来的请求会带;命令行客户端通常不带,不带就放行)。 */
|
|
124
|
+
export function allowedOriginsFor(host: string, port: number): string[] {
|
|
125
|
+
return allowedHostsFor(host, port).map((h) => `http://${h}`)
|
|
126
|
+
}
|