z-biz-tool-shared 0.1.6 → 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.
@@ -1,4 +1,5 @@
1
1
  export * from './AIManager.js';
2
2
  export * from './types.js';
3
3
  export * from './context.js';
4
+ export * from './privateEndpoint.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -2,4 +2,5 @@
2
2
  export * from './AIManager.js';
3
3
  export * from './types.js';
4
4
  export * from './context.js';
5
+ export * from './privateEndpoint.js';
5
6
  //# sourceMappingURL=index.js.map
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,gBAAgB;IAChB,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QACtC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC,CAAC,QAAQ,CAAC;IACb,sBAAsB;IACtB,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED,+CAA+C;AAC/C,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC3B,kCAAkC;QAClC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAwC,CAAC;IACxD,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,iBAAiB;IAC7C,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC,CAAC,aAAa;IACxC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,CAAC,gBAAgB;IAClE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,iBAAiB;IAC1D,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,4BAA4B;IACrE,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,cAAc;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnD,6CAA6C;IAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAChD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,MAAM,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAErD,0CAA0C;IAC1C,+CAA+C;IAC/C,IACE,CAAC,KAAK,WAAW;QACjB,CAAC,KAAK,sBAAsB;QAC5B,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACpB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAClB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,EACvB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CACzB,oBAAyC,EACzC,OAAe;IAEf,IAAI,oBAAoB,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,oBAAoB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './product.js';
2
+ export * from './productManifest.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  // 家族级类型定义。
2
2
  export * from './product.js';
3
+ export * from './productManifest.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC"}
@@ -27,7 +27,7 @@ import type { DeepLinkResult } from "../protocol/deeplink.js";
27
27
  * 与 bundle id / 数据目录命名保持一致(`z-biz-tool-db` → `db`),
28
28
  * 减少用户与开发者的心智负担。
29
29
  */
30
- export type ProductId = "aigen" | "box" | "cpu" | "db" | "file" | "kb" | "math" | "mind" | "music" | "note" | "pet" | "sys" | "terminal" | "worker";
30
+ export type ProductId = "aigen" | "box" | "cpu" | "db" | "file" | "history" | "kb" | "math" | "mind" | "music" | "note" | "pet" | "remote" | "sys" | "terminal" | "worker";
31
31
  /** 全部产品 id 的运行时列表。 */
32
32
  export declare const PRODUCT_IDS: readonly ProductId[];
33
33
  /** 判断一个字符串是不是合法的产品 id。 */
@@ -1 +1 @@
1
- {"version":3,"file":"product.d.ts","sourceRoot":"","sources":["../../src/types/product.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAM3D;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,KAAK,GACL,KAAK,GACL,IAAI,GACJ,MAAM,GACN,IAAI,GACJ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,KAAK,GACL,KAAK,GACL,UAAU,GACV,QAAQ,CAAC;AAEb,sBAAsB;AACtB,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAelC,CAAC;AAEX,0BAA0B;AAC1B,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAE7D;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,aAAa;IACb,EAAE,EAAE,SAAS,CAAC;IACd,iBAAiB;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,mCAAmC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC,WAAW,CAAC,CA4B9E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;CACxD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,eAAe,CAajB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,GACxB,WAAW,EAAE,CAOf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,WAAW,EAAE,CAEf"}
1
+ {"version":3,"file":"product.d.ts","sourceRoot":"","sources":["../../src/types/product.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAM3D;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,KAAK,GACL,KAAK,GACL,IAAI,GACJ,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,KAAK,GACL,QAAQ,GACR,KAAK,GACL,UAAU,GACV,QAAQ,CAAC;AAEb,sBAAsB;AACtB,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAiBlC,CAAC;AAEX,0BAA0B;AAC1B,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,SAAS,CAE7D;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,aAAa;IACb,EAAE,EAAE,SAAS,CAAC;IACd,iBAAiB;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,mCAAmC;IACnC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC,WAAW,CAAC,CA4B9E;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;CACxD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,eAAe,CAajB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,GACxB,WAAW,EAAE,CAOf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,WAAW,EAAE,CAEf"}
@@ -27,12 +27,14 @@ export const PRODUCT_IDS = [
27
27
  "cpu",
28
28
  "db",
29
29
  "file",
30
+ "history",
30
31
  "kb",
31
32
  "math",
32
33
  "mind",
33
34
  "music",
34
35
  "note",
35
36
  "pet",
37
+ "remote",
36
38
  "sys",
37
39
  "terminal",
38
40
  "worker",
@@ -1 +1 @@
1
- {"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/types/product.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA8BH,sBAAsB;AACtB,MAAM,CAAC,MAAM,WAAW,GAAyB;IAC/C,OAAO;IACP,KAAK;IACL,KAAK;IACL,IAAI;IACJ,MAAM;IACN,IAAI;IACJ,MAAM;IACN,MAAM;IACN,OAAO;IACP,MAAM;IACN,KAAK;IACL,KAAK;IACL,UAAU;IACV,QAAQ;CACA,CAAC;AAEX,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAQ,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAiCD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAa;IAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC5C,CAAC;IACD,MAAM,CAAC,GAAG,IAA4B,CAAC;IACvC,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5D,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5D,CAAC;IACD,IAAI,OAAO,CAAC,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,yBAAyB,EAAE,CAAC;IACpE,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3D,CAAC;IACD,8BAA8B;IAC9B,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,eAAe,KAAK,KAAK,EAAE,CAAC;QACxD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,mCAAmC;SACtD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAgB,EAAE,CAAC;AAC/C,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAA6B;IAE7B,MAAM,GAAG,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC9C,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,CAAC,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAyB;IAEzB,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,CAAC,eAAe;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAyB,EACzB,MAAc;IAEd,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC"}
1
+ {"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/types/product.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * 产品自报文件的**读取与汇总** —— 「每条记录都有主人」的机器那一半。
3
+ *
4
+ * ## 为什么需要这一层
5
+ *
6
+ * `product.ts` 提供类型与校验,但真值分散在 16 个仓各自的 `product.json` 里。
7
+ * 谁来读、读完怎么对账,是这一层的事:
8
+ *
9
+ * - **读取**:把一个仓根目录下的 `product.json` 读成 `ProductMeta`,
10
+ * 并在读的时候就地校验(不合规的仓不该等到汇总时才被发现)。
11
+ * - **汇总**:把 N 份自报汇成一份 registry,重复 id 直接炸。
12
+ * - **对账**:把「自报的真值」与「协议层认识的产品集合」对齐 ——
13
+ * 少一个仓自报、或某个仓声明了协议层不存在的 id,都属于**家族契约破损**,
14
+ * 必须在 CI 里红,不能等用户在 box 面板上看到一张打不开的卡片才发现。
15
+ *
16
+ * ## 为什么不内置真值
17
+ *
18
+ * 见 `product.ts` 顶部:本模块只做**机制**,一条真值都不带。
19
+ * 谁的产品叫什么、支持什么动作,只有那个仓知道。
20
+ */
21
+ import { isProductId, type ProductId, type ProductMeta, type ProductRegistry } from "./product.js";
22
+ import type { DeepLinkResult } from "../protocol/deeplink.js";
23
+ /**
24
+ * 自报文件在仓根的**相对路径**。
25
+ *
26
+ * 固定在仓根而不是 `src/` 里:这份文件是「这个仓向家族报什么」,
27
+ * 属于仓级契约,不是前端代码的一部分 —— 放在 `src/` 下会被
28
+ * 「前端源码」的构建/打包规则误伤。
29
+ */
30
+ export declare const PRODUCT_MANIFEST_PATH = "product.json";
31
+ /**
32
+ * 自报文件的磁盘形状(camelCase,与 `ProductMeta` 的 JSON 序列化一致)。
33
+ *
34
+ * 与 `ProductMeta` 分开声明,是因为磁盘上的字段是**外部输入**:
35
+ * 多一个字段少一个字段都可能是别人的笔误,解析时要能指名报错,
36
+ * 而不是被 TS 的类型断言悄悄放过。
37
+ */
38
+ export interface ProductManifestFile {
39
+ readonly id: string;
40
+ readonly displayName: string;
41
+ readonly description: string;
42
+ readonly acceptsDeepLink: boolean;
43
+ readonly actions: readonly string[];
44
+ readonly keywords?: readonly string[];
45
+ }
46
+ /**
47
+ * 解析一个自报文件的内容。
48
+ *
49
+ * 与 `validateProductMeta` 的区别:这里管**磁盘形状**(字段在不在、类型对不对),
50
+ * 那里管**语义自洽**(id 合不合法、声明了动作却说自己不接深链这种矛盾)。
51
+ * 两层都要过。
52
+ */
53
+ export declare function parseProductManifest(raw: unknown): DeepLinkResult<ProductMeta>;
54
+ /**
55
+ * 把若干份自报汇总成 registry。
56
+ *
57
+ * 重复 id 直接抛错 —— 两个仓声明同一个产品 id 意味着有人改了协议没同步,
58
+ * 这在协议层必须炸出来,不能静默取最后一份。
59
+ */
60
+ export declare function buildRegistryFromManifests(manifests: readonly ProductManifestFile[]): ProductRegistry;
61
+ /**
62
+ * 对账结果。
63
+ *
64
+ * `missing` = 协议层认识、但没人生存自报的产品(**没人认领**)。
65
+ * `unknown` = 自报了自己、但协议层不认这个 id(**协议没同步**)。
66
+ */
67
+ export interface ManifestReconciliation {
68
+ readonly ok: boolean;
69
+ /** 协议层认识但无人自报的产品 id */
70
+ readonly missing: readonly ProductId[];
71
+ /** 自报了但协议层不认识的 id */
72
+ readonly unknown: readonly string[];
73
+ /** 重复声明的 id */
74
+ readonly duplicated: readonly string[];
75
+ }
76
+ /**
77
+ * 把「自报的真值」与「协议层认识的产品集合」对账。
78
+ *
79
+ * 两种破损都要报出来:
80
+ * - **无人认领**(missing):协议层说这个产品存在,但没有任何仓声明它 ——
81
+ * 要么那个仓忘了加 `product.json`,要么它已经下线了。
82
+ * - **协议没同步**(unknown):某个仓声明了协议层不存在的 id ——
83
+ * 多半是加了新产品但忘了同步 `cap-hub` / `shared` 的产品集合。
84
+ *
85
+ * @param declared 自报的产品 id 集合
86
+ * @param known 协议层认识的产品 id 集合
87
+ */
88
+ export declare function reconcileManifests(declared: readonly string[], known: readonly string[]): ManifestReconciliation;
89
+ /** 便捷判断:id 是否是协议层认识的产品 id(供各仓自检用)。 */
90
+ export { isProductId };
91
+ //# sourceMappingURL=productManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productManifest.d.ts","sourceRoot":"","sources":["../../src/types/productManifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAEL,WAAW,EAEX,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,WAAW,CAAC;AAInB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,GACX,cAAc,CAAC,WAAW,CAAC,CA4B7B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,mBAAmB,EAAE,GACxC,eAAe,CAUjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,uBAAuB;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,qBAAqB;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,eAAe;IACf,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,sBAAsB,CAkBxB;AAED,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * 产品自报文件的**读取与汇总** —— 「每条记录都有主人」的机器那一半。
3
+ *
4
+ * ## 为什么需要这一层
5
+ *
6
+ * `product.ts` 提供类型与校验,但真值分散在 16 个仓各自的 `product.json` 里。
7
+ * 谁来读、读完怎么对账,是这一层的事:
8
+ *
9
+ * - **读取**:把一个仓根目录下的 `product.json` 读成 `ProductMeta`,
10
+ * 并在读的时候就地校验(不合规的仓不该等到汇总时才被发现)。
11
+ * - **汇总**:把 N 份自报汇成一份 registry,重复 id 直接炸。
12
+ * - **对账**:把「自报的真值」与「协议层认识的产品集合」对齐 ——
13
+ * 少一个仓自报、或某个仓声明了协议层不存在的 id,都属于**家族契约破损**,
14
+ * 必须在 CI 里红,不能等用户在 box 面板上看到一张打不开的卡片才发现。
15
+ *
16
+ * ## 为什么不内置真值
17
+ *
18
+ * 见 `product.ts` 顶部:本模块只做**机制**,一条真值都不带。
19
+ * 谁的产品叫什么、支持什么动作,只有那个仓知道。
20
+ */
21
+ import { buildProductRegistry, isProductId, validateProductMeta, } from "./product.js";
22
+ /**
23
+ * 自报文件在仓根的**相对路径**。
24
+ *
25
+ * 固定在仓根而不是 `src/` 里:这份文件是「这个仓向家族报什么」,
26
+ * 属于仓级契约,不是前端代码的一部分 —— 放在 `src/` 下会被
27
+ * 「前端源码」的构建/打包规则误伤。
28
+ */
29
+ export const PRODUCT_MANIFEST_PATH = "product.json";
30
+ /**
31
+ * 解析一个自报文件的内容。
32
+ *
33
+ * 与 `validateProductMeta` 的区别:这里管**磁盘形状**(字段在不在、类型对不对),
34
+ * 那里管**语义自洽**(id 合不合法、声明了动作却说自己不接深链这种矛盾)。
35
+ * 两层都要过。
36
+ */
37
+ export function parseProductManifest(raw) {
38
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
39
+ return { ok: false, reason: "产品自报文件不是对象" };
40
+ }
41
+ const m = raw;
42
+ const missing = (field) => ({
43
+ ok: false,
44
+ reason: `产品自报文件缺少字段 ${field}`,
45
+ });
46
+ if (typeof m.id !== "string" || m.id.length === 0)
47
+ return missing("id");
48
+ if (typeof m.displayName !== "string")
49
+ return missing("displayName");
50
+ if (typeof m.description !== "string")
51
+ return missing("description");
52
+ if (typeof m.acceptsDeepLink !== "boolean")
53
+ return missing("acceptsDeepLink");
54
+ if (!Array.isArray(m.actions))
55
+ return missing("actions");
56
+ if (m.actions.some((a) => typeof a !== "string")) {
57
+ return { ok: false, reason: "产品自报文件的 actions 里有非字符串" };
58
+ }
59
+ if (m.keywords !== undefined) {
60
+ if (!Array.isArray(m.keywords))
61
+ return missing("keywords");
62
+ if (m.keywords.some((k) => typeof k !== "string")) {
63
+ return { ok: false, reason: "产品自报文件的 keywords 里有非字符串" };
64
+ }
65
+ }
66
+ // 交给语义层校验(id 合法性、动作与 acceptsDeepLink 的自洽性)
67
+ return validateProductMeta(m);
68
+ }
69
+ /**
70
+ * 把若干份自报汇总成 registry。
71
+ *
72
+ * 重复 id 直接抛错 —— 两个仓声明同一个产品 id 意味着有人改了协议没同步,
73
+ * 这在协议层必须炸出来,不能静默取最后一份。
74
+ */
75
+ export function buildRegistryFromManifests(manifests) {
76
+ return buildProductRegistry(manifests.map((m) => {
77
+ const v = parseProductManifest(m);
78
+ if (!v.ok) {
79
+ throw new Error(`产品自报校验失败:${v.reason}`);
80
+ }
81
+ return v.value;
82
+ }));
83
+ }
84
+ /**
85
+ * 把「自报的真值」与「协议层认识的产品集合」对账。
86
+ *
87
+ * 两种破损都要报出来:
88
+ * - **无人认领**(missing):协议层说这个产品存在,但没有任何仓声明它 ——
89
+ * 要么那个仓忘了加 `product.json`,要么它已经下线了。
90
+ * - **协议没同步**(unknown):某个仓声明了协议层不存在的 id ——
91
+ * 多半是加了新产品但忘了同步 `cap-hub` / `shared` 的产品集合。
92
+ *
93
+ * @param declared 自报的产品 id 集合
94
+ * @param known 协议层认识的产品 id 集合
95
+ */
96
+ export function reconcileManifests(declared, known) {
97
+ const seen = new Set();
98
+ const duplicated = [];
99
+ for (const id of declared) {
100
+ if (seen.has(id))
101
+ duplicated.push(id);
102
+ seen.add(id);
103
+ }
104
+ const knownSet = new Set(known);
105
+ const missing = known.filter((id) => !seen.has(id));
106
+ const unknown = [...seen].filter((id) => !knownSet.has(id));
107
+ return {
108
+ ok: missing.length === 0 && unknown.length === 0 && duplicated.length === 0,
109
+ missing: missing,
110
+ unknown,
111
+ duplicated,
112
+ };
113
+ }
114
+ /** 便捷判断:id 是否是协议层认识的产品 id(供各仓自检用)。 */
115
+ export { isProductId };
116
+ //# sourceMappingURL=productManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"productManifest.js","sourceRoot":"","sources":["../../src/types/productManifest.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-biz-tool-shared",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "license": "MIT",
5
5
  "description": "z-biz-tool 核心共享模块(AI/Agent/主题/快捷键/组件)",
6
6
  "type": "module",