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.
Files changed (100) hide show
  1. package/CHANGELOG.md +342 -0
  2. package/OPERATIONS.md +79 -0
  3. package/README.en.md +9 -2
  4. package/README.md +12 -2
  5. package/bin/weflow-cli-electron.cjs +2 -0
  6. package/bin/weflow-cli.ts +648 -79
  7. package/dist/bin/weflow-cli.js +675 -78
  8. package/dist/bin/weflow-cli.js.map +1 -1
  9. package/dist/mcp-server/http.d.ts +14 -0
  10. package/dist/mcp-server/http.d.ts.map +1 -0
  11. package/dist/mcp-server/http.js +84 -0
  12. package/dist/mcp-server/http.js.map +1 -0
  13. package/dist/mcp-server/httpConfig.d.ts +50 -0
  14. package/dist/mcp-server/httpConfig.d.ts.map +1 -0
  15. package/dist/mcp-server/httpConfig.js +104 -0
  16. package/dist/mcp-server/httpConfig.js.map +1 -0
  17. package/dist/mcp-server/index.d.ts +13 -1
  18. package/dist/mcp-server/index.d.ts.map +1 -1
  19. package/dist/mcp-server/index.js +39 -373
  20. package/dist/mcp-server/index.js.map +1 -1
  21. package/dist/src/panel/server.d.ts.map +1 -1
  22. package/dist/src/panel/server.js +26 -1
  23. package/dist/src/panel/server.js.map +1 -1
  24. package/dist/src/services/assistantEval.d.ts +69 -0
  25. package/dist/src/services/assistantEval.d.ts.map +1 -1
  26. package/dist/src/services/assistantEval.js +270 -8
  27. package/dist/src/services/assistantEval.js.map +1 -1
  28. package/dist/src/services/assistantScenes.d.ts +81 -0
  29. package/dist/src/services/assistantScenes.d.ts.map +1 -0
  30. package/dist/src/services/assistantScenes.js +322 -0
  31. package/dist/src/services/assistantScenes.js.map +1 -0
  32. package/dist/src/services/assistantService.d.ts +14 -4
  33. package/dist/src/services/assistantService.d.ts.map +1 -1
  34. package/dist/src/services/assistantService.js +82 -6
  35. package/dist/src/services/assistantService.js.map +1 -1
  36. package/dist/src/services/assistantSkills.d.ts +94 -0
  37. package/dist/src/services/assistantSkills.d.ts.map +1 -0
  38. package/dist/src/services/assistantSkills.js +330 -0
  39. package/dist/src/services/assistantSkills.js.map +1 -0
  40. package/dist/src/services/assistantTools.d.ts +7 -0
  41. package/dist/src/services/assistantTools.d.ts.map +1 -1
  42. package/dist/src/services/assistantTools.js +770 -61
  43. package/dist/src/services/assistantTools.js.map +1 -1
  44. package/dist/src/services/assistantTrace.d.ts +4 -0
  45. package/dist/src/services/assistantTrace.d.ts.map +1 -1
  46. package/dist/src/services/assistantTrace.js +9 -0
  47. package/dist/src/services/assistantTrace.js.map +1 -1
  48. package/dist/src/services/configService.d.ts +7 -0
  49. package/dist/src/services/configService.d.ts.map +1 -1
  50. package/dist/src/services/configService.js +4 -2
  51. package/dist/src/services/configService.js.map +1 -1
  52. package/dist/src/services/outboundSend.d.ts +53 -0
  53. package/dist/src/services/outboundSend.d.ts.map +1 -0
  54. package/dist/src/services/outboundSend.js +90 -0
  55. package/dist/src/services/outboundSend.js.map +1 -0
  56. package/dist/src/services/pythonBridge.d.ts.map +1 -1
  57. package/dist/src/services/pythonBridge.js +1 -0
  58. package/dist/src/services/pythonBridge.js.map +1 -1
  59. package/dist/src/utils/python.js +2 -2
  60. package/dist/src/utils/python.js.map +1 -1
  61. package/docs/BRANCHES.md +37 -0
  62. package/docs/DECISIONS.md +350 -0
  63. package/docs/EXTENDING.md +29 -3
  64. package/docs/MCP.md +56 -2
  65. package/docs/PROJECT_STATE.md +121 -9
  66. package/mcp-server/http.ts +108 -0
  67. package/mcp-server/httpConfig.ts +126 -0
  68. package/mcp-server/index.ts +344 -681
  69. package/package.json +1 -1
  70. package/resources/panel/ball-position.cjs +48 -2
  71. package/resources/panel/index.html +6 -4
  72. package/resources/panel/main.cjs +137 -5
  73. package/resources/panel/mascot-base.png +0 -0
  74. package/resources/panel/mascot-iris.png +0 -0
  75. package/resources/panel/mascot-peek.png +0 -0
  76. package/resources/panel/panel.css +95 -60
  77. package/resources/panel/preload.cjs +26 -0
  78. package/resources/panel/renderer.js +167 -10
  79. package/scripts/_utils.py +47 -1
  80. package/scripts/backfill_articles.py +202 -21
  81. package/scripts/biz_daily.py +8 -31
  82. package/scripts/bonds.py +331 -0
  83. package/scripts/chat_notes.py +551 -470
  84. package/scripts/chat_stats.py +8 -7
  85. package/scripts/compile_wiki.py +24 -2
  86. package/scripts/export_chat_html.py +3 -1
  87. package/scripts/mcp_bridge.py +6 -7
  88. package/scripts/wechat_voice.py +9 -0
  89. package/scripts/wiki_lint.py +30 -7
  90. package/src/panel/server.ts +25 -1
  91. package/src/services/assistantEval.ts +323 -12
  92. package/src/services/assistantScenes.ts +342 -0
  93. package/src/services/assistantService.ts +81 -6
  94. package/src/services/assistantSkills.ts +364 -0
  95. package/src/services/assistantTools.ts +738 -59
  96. package/src/services/assistantTrace.ts +9 -0
  97. package/src/services/configService.ts +412 -403
  98. package/src/services/outboundSend.ts +126 -0
  99. package/src/services/pythonBridge.ts +1 -0
  100. package/src/utils/python.ts +2 -2
@@ -0,0 +1,14 @@
1
+ import type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { type HttpOptions } from './httpConfig.js';
3
+ export declare const MCP_HTTP_PATH = "/mcp";
4
+ export interface RunningHttpServer {
5
+ /** 实际绑定的地址(端口传 0 时是内核挑的,所以以这里为准) */
6
+ url: string;
7
+ port: number;
8
+ close: () => Promise<void>;
9
+ }
10
+ /**
11
+ * 起 HTTP 传输。`buildServer` 每次调用都必须返回一个**新的** Server(无状态模式每个请求一套)。
12
+ */
13
+ export declare function startHttpServer(opts: HttpOptions, buildServer: () => McpServer, log?: (line: string) => void): Promise<RunningHttpServer>;
14
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../mcp-server/http.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAA;AACpF,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AAExB,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAOD;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,WAAW,EACjB,WAAW,EAAE,MAAM,SAAS,EAC5B,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAe,GACrC,OAAO,CAAC,iBAAiB,CAAC,CA0D5B"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * MCP 的 **HTTP 传输**(Streamable HTTP)。stdio 那条路一个字没动,这是**另一条**路。
3
+ *
4
+ * 为什么要它:stdio 只能被"能起进程"的客户端用(Claude Code、IDE 插件)。HTTP 那条路在
5
+ * 生态里已是常态——chatlog 就是拿 Streamable HTTP / SSE 把同一批查询能力摊出来的,于是浏览器型
6
+ * 客户端、常驻服务、多个客户端同时连都能用。工具集**完全一样**(同一张 `TOOL_DEFS` 派生表),
7
+ * 变的只是搬运动作的方式。
8
+ *
9
+ * 三道边界(判定在 `httpConfig.ts`,纯函数、有测试):
10
+ * 1. **默认不开**:不给 `--http` 就是 stdio,行为与以前逐字相同。
11
+ * 2. **必须带令牌**,且**只绑回环**(远程走 SSH 隧道,见 docs/MCP.md)。
12
+ * 3. **打开 DNS 重绑定防护**:SDK 的默认是 `false`,而本机 HTTP 服务最现实的攻击面就是
13
+ * "你正在浏览的某个网页往 127.0.0.1 发请求"。打开后 Host 头必须对得上,带 Origin 的
14
+ * 浏览器请求也必须在白名单里。
15
+ *
16
+ * 采用**无状态**模式(每个请求一套 server+transport):这一层只有只读查询,没有要跨请求维持的
17
+ * 会话,无状态就没有"会话之间串了"这类问题。
18
+ */
19
+ import { createServer } from 'node:http';
20
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
21
+ import { allowedHostsFor, allowedOriginsFor, isAuthorized, } from './httpConfig.js';
22
+ export const MCP_HTTP_PATH = '/mcp';
23
+ function json(res, code, body) {
24
+ res.writeHead(code, { 'content-type': 'application/json' });
25
+ res.end(JSON.stringify(body));
26
+ }
27
+ /**
28
+ * 起 HTTP 传输。`buildServer` 每次调用都必须返回一个**新的** Server(无状态模式每个请求一套)。
29
+ */
30
+ export async function startHttpServer(opts, buildServer, log = () => { }) {
31
+ let allowedHosts = [];
32
+ let allowedOrigins = [];
33
+ const handler = async (req, res) => {
34
+ const url = req.url ?? '/';
35
+ if (url !== MCP_HTTP_PATH && !url.startsWith(MCP_HTTP_PATH + '?')) {
36
+ json(res, 404, { error: 'not found', hint: `MCP 端点在 ${MCP_HTTP_PATH}` });
37
+ return;
38
+ }
39
+ // 令牌先判、再谈别的:没令牌的请求连"MCP 认不认识这个方法"都不该知道。
40
+ if (!isAuthorized(req.headers.authorization, opts.token)) {
41
+ json(res, 401, { jsonrpc: '2.0', error: { code: -32001, message: '未授权:缺少或不对的 Bearer 令牌' }, id: null });
42
+ return;
43
+ }
44
+ const transport = new StreamableHTTPServerTransport({
45
+ sessionIdGenerator: undefined, // 无状态
46
+ enableJsonResponse: true, // 不强制 SSE:普通客户端拿 JSON 就好
47
+ enableDnsRebindingProtection: true, // SDK 默认 false,必须显式开
48
+ allowedHosts,
49
+ allowedOrigins,
50
+ });
51
+ const server = buildServer();
52
+ res.on('close', () => { void transport.close(); void server.close().catch(() => { }); });
53
+ try {
54
+ await server.connect(transport);
55
+ await transport.handleRequest(req, res);
56
+ }
57
+ catch (error) {
58
+ log(`[mcp-http] 请求处理失败: ${error?.message ?? error}`);
59
+ if (!res.headersSent) {
60
+ json(res, 500, { jsonrpc: '2.0', error: { code: -32603, message: '内部错误' }, id: null });
61
+ }
62
+ }
63
+ };
64
+ const httpServer = createServer((req, res) => { void handler(req, res); });
65
+ await new Promise((resolve, reject) => {
66
+ httpServer.once('error', reject);
67
+ httpServer.listen(opts.port, opts.host, () => {
68
+ httpServer.removeListener('error', reject);
69
+ resolve();
70
+ });
71
+ });
72
+ const address = httpServer.address();
73
+ const port = typeof address === 'object' && address ? address.port : opts.port;
74
+ // **绑定之后**才算白名单:端口传 0 时真实端口此刻才知道,而白名单必须用真实端口。
75
+ allowedHosts = allowedHostsFor(opts.host, port);
76
+ allowedOrigins = allowedOriginsFor(opts.host, port);
77
+ const shownHost = opts.host === '::1' ? '[::1]' : opts.host;
78
+ return {
79
+ url: `http://${shownHost}:${port}${MCP_HTTP_PATH}`,
80
+ port,
81
+ close: () => new Promise((resolve) => { httpServer.close(() => resolve()); }),
82
+ };
83
+ }
84
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../mcp-server/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
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
+ /** 默认端口。5030 是 chatlog 的,避开它免得两个服务打架。 */
16
+ export declare const DEFAULT_HTTP_PORT = 8790;
17
+ /**
18
+ * 令牌长度下限。128 位随机(32 个 hex 字符)是常态;24 个字符足够排除"随手敲一个",
19
+ * 又不至于逼人非得用某个特定长度。
20
+ */
21
+ export declare const MIN_TOKEN_CHARS = 24;
22
+ /** 允许绑定的地址。**只回环**:`0.0.0.0` 不在此列,而且不是"忘了加"。 */
23
+ export declare const LOOPBACK_HOSTS: string[];
24
+ export interface HttpOptions {
25
+ host: string;
26
+ port: number;
27
+ token: string;
28
+ }
29
+ /** 这一轮是不是要走 HTTP。`--http`、`--http=8790`、`WEFLOW_MCP_HTTP=1` 都算。 */
30
+ export declare function httpModeRequested(env: Record<string, string | undefined>, argv: string[]): boolean;
31
+ /**
32
+ * 解出 HTTP 模式的参数。**不走 HTTP 就返回 `null`**;要走但配置不合法就**抛错**(fail closed,
33
+ * 绝不"降级成不安全的默认值"继续跑)。
34
+ *
35
+ * 端口允许 `0`:那是让内核挑一个空闲端口,测试要用;正式跑用默认端口。
36
+ */
37
+ export declare function resolveHttpOptions(env: Record<string, string | undefined>, argv: string[]): HttpOptions | null;
38
+ /**
39
+ * `Authorization` 头认不认。
40
+ *
41
+ * 比的是**摘要**而不是原文:`timingSafeEqual` 要求等长,直接比长度会漏出"令牌有多长",
42
+ * 于是"不同长度直接返回 false"就成了一个可测的短路径。两边先过一遍 SHA-256,长度就固定了。
43
+ * 方案名按 RFC 7235 大小写不敏感。
44
+ */
45
+ export declare function isAuthorized(header: string | undefined, token: string): boolean;
46
+ /** Host 头允许的取值(DNS 重绑定防护用)。带端口,因为 SDK 比的是**原始 Host 头**。 */
47
+ export declare function allowedHostsFor(host: string, port: number): string[];
48
+ /** 允许的 Origin(浏览器来的请求会带;命令行客户端通常不带,不带就放行)。 */
49
+ export declare function allowedOriginsFor(host: string, port: number): string[];
50
+ //# sourceMappingURL=httpConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpConfig.d.ts","sourceRoot":"","sources":["../../mcp-server/httpConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,yCAAyC;AACzC,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;GAGG;AACH,eAAO,MAAM,eAAe,KAAK,CAAA;AAEjC,gDAAgD;AAChD,eAAO,MAAM,cAAc,UAAoC,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAIlG;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,EAAE,MAAM,EAAE,GACb,WAAW,GAAG,IAAI,CAoCpB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAU/E;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAMpE;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE"}
@@ -0,0 +1,104 @@
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
+ import { createHash, timingSafeEqual } from 'node:crypto';
16
+ /** 默认端口。5030 是 chatlog 的,避开它免得两个服务打架。 */
17
+ export const DEFAULT_HTTP_PORT = 8790;
18
+ /**
19
+ * 令牌长度下限。128 位随机(32 个 hex 字符)是常态;24 个字符足够排除"随手敲一个",
20
+ * 又不至于逼人非得用某个特定长度。
21
+ */
22
+ export const MIN_TOKEN_CHARS = 24;
23
+ /** 允许绑定的地址。**只回环**:`0.0.0.0` 不在此列,而且不是"忘了加"。 */
24
+ export const LOOPBACK_HOSTS = ['127.0.0.1', 'localhost', '::1'];
25
+ /** 这一轮是不是要走 HTTP。`--http`、`--http=8790`、`WEFLOW_MCP_HTTP=1` 都算。 */
26
+ export function httpModeRequested(env, argv) {
27
+ if (argv.some((a) => a === '--http' || a.startsWith('--http=')))
28
+ return true;
29
+ const value = String(env.WEFLOW_MCP_HTTP ?? '').trim();
30
+ return value !== '' && value !== '0' && value.toLowerCase() !== 'false';
31
+ }
32
+ function flagValue(argv, name) {
33
+ const hit = argv.find((a) => a.startsWith(`${name}=`));
34
+ return hit ? hit.slice(name.length + 1) : undefined;
35
+ }
36
+ /**
37
+ * 解出 HTTP 模式的参数。**不走 HTTP 就返回 `null`**;要走但配置不合法就**抛错**(fail closed,
38
+ * 绝不"降级成不安全的默认值"继续跑)。
39
+ *
40
+ * 端口允许 `0`:那是让内核挑一个空闲端口,测试要用;正式跑用默认端口。
41
+ */
42
+ export function resolveHttpOptions(env, argv) {
43
+ if (!httpModeRequested(env, argv))
44
+ return null;
45
+ const token = String(flagValue(argv, '--token') ?? env.WEFLOW_MCP_TOKEN ?? '').trim();
46
+ if (!token) {
47
+ throw new Error('HTTP 模式必须给令牌:`--token=<随机串>` 或环境变量 WEFLOW_MCP_TOKEN。\n'
48
+ + '没有默认值是有意的 —— 一个自动生成、只在日志里露一面的口令,看起来像"配好了"。\n'
49
+ + '生成一个:node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"');
50
+ }
51
+ if (token.length < MIN_TOKEN_CHARS) {
52
+ throw new Error(`令牌太短(${token.length} < ${MIN_TOKEN_CHARS} 个字符)。用随机串,别用可猜的短语。`);
53
+ }
54
+ const host = String(flagValue(argv, '--host') ?? '127.0.0.1').trim();
55
+ if (!LOOPBACK_HOSTS.includes(host)) {
56
+ throw new Error(`只允许绑回环地址(${LOOPBACK_HOSTS.join(' / ')}),收到 ${host}。\n`
57
+ + '要从别的机器连,用 SSH 隧道(ssh -L 8790:127.0.0.1:8790 <host>)或自己的反向代理 —— '
58
+ + '把聊天记录摊到局域网上不该由这个命令顺手打开。');
59
+ }
60
+ const raw = flagValue(argv, '--http') ?? flagValue(argv, '--port') ?? env.WEFLOW_MCP_HTTP_PORT ?? String(DEFAULT_HTTP_PORT);
61
+ const rawText = String(raw).trim();
62
+ // 先按"纯数字"卡一道:`Number('') === 0`,不卡的话空值会静默变成一个端口。
63
+ if (!/^\d{1,5}$/.test(rawText)) {
64
+ throw new Error(`端口要 0-65535 之间的整数(0 = 让系统挑一个空闲端口),收到 ${JSON.stringify(raw)}`);
65
+ }
66
+ const port = Number(rawText);
67
+ if (port > 65535) {
68
+ throw new Error(`端口要 0-65535 之间的整数,收到 ${JSON.stringify(raw)}`);
69
+ }
70
+ return { host, port, token };
71
+ }
72
+ /**
73
+ * `Authorization` 头认不认。
74
+ *
75
+ * 比的是**摘要**而不是原文:`timingSafeEqual` 要求等长,直接比长度会漏出"令牌有多长",
76
+ * 于是"不同长度直接返回 false"就成了一个可测的短路径。两边先过一遍 SHA-256,长度就固定了。
77
+ * 方案名按 RFC 7235 大小写不敏感。
78
+ */
79
+ export function isAuthorized(header, token) {
80
+ const raw = String(header ?? '');
81
+ const space = raw.indexOf(' ');
82
+ if (space < 0)
83
+ return false;
84
+ if (raw.slice(0, space).toLowerCase() !== 'bearer')
85
+ return false;
86
+ const presented = raw.slice(space + 1).trim();
87
+ if (!presented)
88
+ return false;
89
+ const digest = (value) => createHash('sha256').update(value, 'utf8').digest();
90
+ return timingSafeEqual(digest(presented), digest(token));
91
+ }
92
+ /** Host 头允许的取值(DNS 重绑定防护用)。带端口,因为 SDK 比的是**原始 Host 头**。 */
93
+ export function allowedHostsFor(host, port) {
94
+ const forms = new Set();
95
+ for (const h of host === 'localhost' ? ['127.0.0.1', 'localhost'] : [host]) {
96
+ forms.add(h === '::1' ? `[::1]:${port}` : `${h}:${port}`);
97
+ }
98
+ return [...forms];
99
+ }
100
+ /** 允许的 Origin(浏览器来的请求会带;命令行客户端通常不带,不带就放行)。 */
101
+ export function allowedOriginsFor(host, port) {
102
+ return allowedHostsFor(host, port).map((h) => `http://${h}`);
103
+ }
104
+ //# sourceMappingURL=httpConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpConfig.js","sourceRoot":"","sources":["../../mcp-server/httpConfig.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,15 @@
1
1
  #!/usr/bin/env npx tsx
2
- export {};
2
+ /**
3
+ * WeFlow MCP Server — 让 Claude Code 等 AI Agent 直接查询知识库。
4
+ *
5
+ * 启动: npx tsx mcp-server/index.ts (stdio,默认)
6
+ * npx tsx mcp-server/index.ts --http --token=<随机串> (HTTP,只绑回环)
7
+ *
8
+ * 两条传输的工具集**完全一样**(同一张 `TOOL_DEFS` 派生表),变的只是搬运动作的方式。
9
+ * HTTP 那条的边界(必须带令牌、只绑回环、DNS 重绑定防护)在 `httpConfig.ts` / `http.ts`,
10
+ * 理由与用法写在 docs/MCP.md 的「HTTP 传输」一节。
11
+ */
12
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
13
+ /** 建一个 MCP Server(工具表与处理器)。stdio 与 HTTP 两条传输都用它,**每次都新建**无状态。 */
14
+ export declare function buildServer(): Server;
3
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../mcp-server/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../mcp-server/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AA6KlE,iEAAiE;AACjE,wBAAgB,WAAW,IAAI,MAAM,CAkIpC"}