z-biz-tool-shared 0.1.7 → 0.1.8
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/dist/ai/index.d.ts +1 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +1 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/privateEndpoint.d.ts +68 -0
- package/dist/ai/privateEndpoint.d.ts.map +1 -0
- package/dist/ai/privateEndpoint.js +148 -0
- package/dist/ai/privateEndpoint.js.map +1 -0
- package/package.json +1 -1
package/dist/ai/index.d.ts
CHANGED
package/dist/ai/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC"}
|
package/dist/ai/index.js
CHANGED
package/dist/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「这份配置指向本机 / 局域网吗」—— 前端侧的免密钥判定。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么前端也要有这份判据
|
|
5
|
+
*
|
|
6
|
+
* 后端 cap-llm 的 `LlmConfig::needs_api_key()` 已经统一了免密钥判定:
|
|
7
|
+
* ① 选了登记「免 Key」的预设;② 地址落在本机 / 局域网。
|
|
8
|
+
* 但前端也得判同一件事 —— 否则会出现**判据分裂**:
|
|
9
|
+
* 后端放行、前端却说「还没配置 API Key」,用户点一下就被弹回设置页,
|
|
10
|
+
* 而设置页里后端根本不要求填 Key(填了也无处可用)。
|
|
11
|
+
*
|
|
12
|
+
* 分裂的两副面孔:
|
|
13
|
+
* - AI 对话框的「密钥框该藏不该藏」—— 藏错了用户找不到该填的地方;
|
|
14
|
+
* - AI 功能入口的「已配置没有」—— 判错了功能按钮直接禁用,用户无从知道原因。
|
|
15
|
+
*
|
|
16
|
+
* ## 为什么规则必须与 Rust 侧逐条对齐
|
|
17
|
+
*
|
|
18
|
+
* Rust 的 `is_private_endpoint()` 在 `z-biz-tool-capability/crates/cap-llm/src/config.rs`。
|
|
19
|
+
* 这份实现是它的**逐条镜像**,两边认同一批地址:
|
|
20
|
+
*
|
|
21
|
+
* - 回环:`localhost` / `127.0.0.0/8` / `::1` / `0.0.0.0`
|
|
22
|
+
* - 私有段:`10/8`、`172.16/12`、`192.168/16`
|
|
23
|
+
* - 本地域名:`.local`(mDNS)、`.lan`、`.internal`、`host.docker.internal`
|
|
24
|
+
*
|
|
25
|
+
* 只看 host,**不看端口**:llama.cpp 8200、Ollama 11434、LM Studio 1234、
|
|
26
|
+
* vLLM 8000,按端口列举必然过时;而 host 属于私有段的判断依据是
|
|
27
|
+
* IPv4/IPv6 保留地址(RFC 1918 / RFC 4193),不会随时间失效。
|
|
28
|
+
*
|
|
29
|
+
* ## 改这份代码之前
|
|
30
|
+
*
|
|
31
|
+
* 先确认 Rust 侧没变。判据分裂的根因永远是「两份实现漂移」,
|
|
32
|
+
* 而 Rust 侧是金标(它决定请求实际发不发鉴权头)。
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* 从 URL 里取 host(去掉 scheme、userinfo、端口、路径)。
|
|
36
|
+
*
|
|
37
|
+
* 不引 `url` 包:这里的输入只有 baseUrl 一种,且历史配置里存在
|
|
38
|
+
* `192.168.1.2:8200/v1`(无 scheme)这种写法,严格解析器反而要额外容错。
|
|
39
|
+
* 取不到就返回空串,由调用方当作「不是私有地址」处理。
|
|
40
|
+
*/
|
|
41
|
+
export declare function hostOf(url: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* 该地址是否指向「本机 / 局域网」。
|
|
44
|
+
*
|
|
45
|
+
* 与 Rust 侧 `cap_llm::is_private_endpoint()` 逐条对齐。
|
|
46
|
+
*/
|
|
47
|
+
export declare function isPrivateEndpoint(url: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* 这份 AI 配置要不要 API Key。
|
|
50
|
+
*
|
|
51
|
+
* 与 Rust 侧 `LlmConfig::needs_api_key()` **判据顺序严格一致**:
|
|
52
|
+
*
|
|
53
|
+
* ① 预设登记为「免 Key」→ 免(显式声明,优先);
|
|
54
|
+
* ② 预设登记为「要 Key」→ **要**(哪怕地址被改成本机代理,也按预设走 ——
|
|
55
|
+
* 用户显式选的厂商优先于地址猜测。这条反向约束是 Rust 侧
|
|
56
|
+
* `private_endpoint_does_not_rescue_key_requiring_preset` 钉死的);
|
|
57
|
+
* ③ 没匹配到预设(自定义服务)→ 看地址:私网免、公网要。
|
|
58
|
+
*
|
|
59
|
+
* 第②条最容易被写反:只判「预设说不免」而不判「预设说要」,
|
|
60
|
+
* 会让「选了 DeepSeek 但把地址指向本机代理」的场景被私网规则救掉,
|
|
61
|
+
* 于是请求发出去了、没带鉴权头、拿到 401 —— 而用户完全不知道为什么。
|
|
62
|
+
*
|
|
63
|
+
* @param presetRequiresApiKey 预设施置里的 `requires_api_key`;
|
|
64
|
+
* 传 `undefined` 表示「没匹配到预设」(自定义服务)。
|
|
65
|
+
* @param baseUrl 服务地址。
|
|
66
|
+
*/
|
|
67
|
+
export declare function needsApiKey(presetRequiresApiKey: boolean | undefined, baseUrl: string): boolean;
|
|
68
|
+
//# sourceMappingURL=privateEndpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privateEndpoint.d.ts","sourceRoot":"","sources":["../../src/ai/privateEndpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAa1C;AAoCD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAmBtD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,oBAAoB,EAAE,OAAO,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,GACd,OAAO,CAIT"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「这份配置指向本机 / 局域网吗」—— 前端侧的免密钥判定。
|
|
3
|
+
*
|
|
4
|
+
* ## 为什么前端也要有这份判据
|
|
5
|
+
*
|
|
6
|
+
* 后端 cap-llm 的 `LlmConfig::needs_api_key()` 已经统一了免密钥判定:
|
|
7
|
+
* ① 选了登记「免 Key」的预设;② 地址落在本机 / 局域网。
|
|
8
|
+
* 但前端也得判同一件事 —— 否则会出现**判据分裂**:
|
|
9
|
+
* 后端放行、前端却说「还没配置 API Key」,用户点一下就被弹回设置页,
|
|
10
|
+
* 而设置页里后端根本不要求填 Key(填了也无处可用)。
|
|
11
|
+
*
|
|
12
|
+
* 分裂的两副面孔:
|
|
13
|
+
* - AI 对话框的「密钥框该藏不该藏」—— 藏错了用户找不到该填的地方;
|
|
14
|
+
* - AI 功能入口的「已配置没有」—— 判错了功能按钮直接禁用,用户无从知道原因。
|
|
15
|
+
*
|
|
16
|
+
* ## 为什么规则必须与 Rust 侧逐条对齐
|
|
17
|
+
*
|
|
18
|
+
* Rust 的 `is_private_endpoint()` 在 `z-biz-tool-capability/crates/cap-llm/src/config.rs`。
|
|
19
|
+
* 这份实现是它的**逐条镜像**,两边认同一批地址:
|
|
20
|
+
*
|
|
21
|
+
* - 回环:`localhost` / `127.0.0.0/8` / `::1` / `0.0.0.0`
|
|
22
|
+
* - 私有段:`10/8`、`172.16/12`、`192.168/16`
|
|
23
|
+
* - 本地域名:`.local`(mDNS)、`.lan`、`.internal`、`host.docker.internal`
|
|
24
|
+
*
|
|
25
|
+
* 只看 host,**不看端口**:llama.cpp 8200、Ollama 11434、LM Studio 1234、
|
|
26
|
+
* vLLM 8000,按端口列举必然过时;而 host 属于私有段的判断依据是
|
|
27
|
+
* IPv4/IPv6 保留地址(RFC 1918 / RFC 4193),不会随时间失效。
|
|
28
|
+
*
|
|
29
|
+
* ## 改这份代码之前
|
|
30
|
+
*
|
|
31
|
+
* 先确认 Rust 侧没变。判据分裂的根因永远是「两份实现漂移」,
|
|
32
|
+
* 而 Rust 侧是金标(它决定请求实际发不发鉴权头)。
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* 从 URL 里取 host(去掉 scheme、userinfo、端口、路径)。
|
|
36
|
+
*
|
|
37
|
+
* 不引 `url` 包:这里的输入只有 baseUrl 一种,且历史配置里存在
|
|
38
|
+
* `192.168.1.2:8200/v1`(无 scheme)这种写法,严格解析器反而要额外容错。
|
|
39
|
+
* 取不到就返回空串,由调用方当作「不是私有地址」处理。
|
|
40
|
+
*/
|
|
41
|
+
export function hostOf(url) {
|
|
42
|
+
const rest = url.includes('://') ? url.slice(url.indexOf('://') + 3) : url;
|
|
43
|
+
const hostPort = rest.split('/')[0] ?? '';
|
|
44
|
+
// userinfo@host
|
|
45
|
+
const afterUser = hostPort.includes('@')
|
|
46
|
+
? hostPort.slice(hostPort.lastIndexOf('@') + 1)
|
|
47
|
+
: hostPort;
|
|
48
|
+
// IPv6 字面量 [::1]:8200
|
|
49
|
+
if (afterUser.startsWith('[')) {
|
|
50
|
+
const end = afterUser.indexOf(']');
|
|
51
|
+
return end === -1 ? afterUser : afterUser.slice(0, end + 1);
|
|
52
|
+
}
|
|
53
|
+
return afterUser.split(':')[0] ?? '';
|
|
54
|
+
}
|
|
55
|
+
/** IPv4 是否落在回环 / 私有段 / link-local / 未指定地址里。 */
|
|
56
|
+
function isPrivateV4(host) {
|
|
57
|
+
const parts = host.split('.');
|
|
58
|
+
if (parts.length !== 4)
|
|
59
|
+
return false;
|
|
60
|
+
const nums = parts.map((p) => {
|
|
61
|
+
// 只接受纯数字段 —— "1e2" 这类不是合法 IPv4 写法
|
|
62
|
+
if (!/^\d{1,3}$/.test(p))
|
|
63
|
+
return -1;
|
|
64
|
+
const n = Number(p);
|
|
65
|
+
return n >= 0 && n <= 255 ? n : -1;
|
|
66
|
+
});
|
|
67
|
+
if (nums.some((n) => n < 0))
|
|
68
|
+
return false;
|
|
69
|
+
const [a, b] = nums;
|
|
70
|
+
if (a === 127)
|
|
71
|
+
return true; // 127.0.0.0/8 回环
|
|
72
|
+
if (a === 10)
|
|
73
|
+
return true; // 10.0.0.0/8
|
|
74
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
75
|
+
return true; // 172.16.0.0/12
|
|
76
|
+
if (a === 192 && b === 168)
|
|
77
|
+
return true; // 192.168.0.0/16
|
|
78
|
+
if (a === 169 && b === 254)
|
|
79
|
+
return true; // 169.254.0.0/16 link-local
|
|
80
|
+
if (a === 0)
|
|
81
|
+
return true; // 0.0.0.0 未指定
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/** IPv6 是否是回环或唯一本地地址(fc00::/7)。 */
|
|
85
|
+
function isPrivateV6(host) {
|
|
86
|
+
const inner = host.replace(/^\[|\]$/g, '');
|
|
87
|
+
if (!inner.includes(':'))
|
|
88
|
+
return false;
|
|
89
|
+
const lower = inner.toLowerCase();
|
|
90
|
+
if (lower === '::1' || lower === '::')
|
|
91
|
+
return true;
|
|
92
|
+
// fc00::/7 —— 唯一本地地址(ULA)。段首的高两位判据:fc/fd 开头。
|
|
93
|
+
const head = lower.split(':')[0] ?? '';
|
|
94
|
+
if (!/^[0-9a-f]{1,4}$/.test(head))
|
|
95
|
+
return false;
|
|
96
|
+
const n = parseInt(head, 16);
|
|
97
|
+
return (n & 0xfe00) === 0xfc00;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 该地址是否指向「本机 / 局域网」。
|
|
101
|
+
*
|
|
102
|
+
* 与 Rust 侧 `cap_llm::is_private_endpoint()` 逐条对齐。
|
|
103
|
+
*/
|
|
104
|
+
export function isPrivateEndpoint(url) {
|
|
105
|
+
const host = hostOf(url);
|
|
106
|
+
if (!host)
|
|
107
|
+
return false;
|
|
108
|
+
const h = host.replace(/^\[|\]$/g, '').toLowerCase();
|
|
109
|
+
// 名称类:mDNS / 局域网后缀 / 容器回流名。这些后缀不可能属于公网服务,
|
|
110
|
+
// 但它们解析不成 IP,必须显式列出,否则 `.local` 的 mini 会被当成公网。
|
|
111
|
+
if (h === 'localhost' ||
|
|
112
|
+
h === 'host.docker.internal' ||
|
|
113
|
+
h.endsWith('.local') ||
|
|
114
|
+
h.endsWith('.lan') ||
|
|
115
|
+
h.endsWith('.internal')) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (h.includes(':'))
|
|
119
|
+
return isPrivateV6(host);
|
|
120
|
+
return isPrivateV4(h);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* 这份 AI 配置要不要 API Key。
|
|
124
|
+
*
|
|
125
|
+
* 与 Rust 侧 `LlmConfig::needs_api_key()` **判据顺序严格一致**:
|
|
126
|
+
*
|
|
127
|
+
* ① 预设登记为「免 Key」→ 免(显式声明,优先);
|
|
128
|
+
* ② 预设登记为「要 Key」→ **要**(哪怕地址被改成本机代理,也按预设走 ——
|
|
129
|
+
* 用户显式选的厂商优先于地址猜测。这条反向约束是 Rust 侧
|
|
130
|
+
* `private_endpoint_does_not_rescue_key_requiring_preset` 钉死的);
|
|
131
|
+
* ③ 没匹配到预设(自定义服务)→ 看地址:私网免、公网要。
|
|
132
|
+
*
|
|
133
|
+
* 第②条最容易被写反:只判「预设说不免」而不判「预设说要」,
|
|
134
|
+
* 会让「选了 DeepSeek 但把地址指向本机代理」的场景被私网规则救掉,
|
|
135
|
+
* 于是请求发出去了、没带鉴权头、拿到 401 —— 而用户完全不知道为什么。
|
|
136
|
+
*
|
|
137
|
+
* @param presetRequiresApiKey 预设施置里的 `requires_api_key`;
|
|
138
|
+
* 传 `undefined` 表示「没匹配到预设」(自定义服务)。
|
|
139
|
+
* @param baseUrl 服务地址。
|
|
140
|
+
*/
|
|
141
|
+
export function needsApiKey(presetRequiresApiKey, baseUrl) {
|
|
142
|
+
if (presetRequiresApiKey === false)
|
|
143
|
+
return false;
|
|
144
|
+
if (presetRequiresApiKey === true)
|
|
145
|
+
return true;
|
|
146
|
+
return !isPrivateEndpoint(baseUrl);
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=privateEndpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privateEndpoint.js","sourceRoot":"","sources":["../../src/ai/privateEndpoint.ts"],"names":[],"mappings":"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"}
|