@harrylabsj/kiwi 0.8.0 → 0.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.
Files changed (214) hide show
  1. package/README.md +1 -1
  2. package/dist/a2a/negotiate.d.ts +6 -2
  3. package/dist/a2a/negotiate.js +10 -3
  4. package/dist/a2a/negotiate.js.map +1 -1
  5. package/dist/a2a/node.d.ts +4 -1
  6. package/dist/a2a/node.js +56 -3
  7. package/dist/a2a/node.js.map +1 -1
  8. package/dist/a2a/server/merchant-handler.d.ts +11 -3
  9. package/dist/a2a/server/merchant-handler.js +84 -26
  10. package/dist/a2a/server/merchant-handler.js.map +1 -1
  11. package/dist/agent/buyer/buyer-tools.js +2 -2
  12. package/dist/agent/buyer/buyer-tools.js.map +1 -1
  13. package/dist/agent/kernel.js +5 -1
  14. package/dist/agent/kernel.js.map +1 -1
  15. package/dist/agent/memory/schema.d.ts +1 -1
  16. package/dist/agent/memory/schema.js +9 -1
  17. package/dist/agent/memory/schema.js.map +1 -1
  18. package/dist/agent/merchant/action-candidate.d.ts +18 -0
  19. package/dist/agent/merchant/action-candidate.js +51 -0
  20. package/dist/agent/merchant/action-candidate.js.map +1 -1
  21. package/dist/agent/merchant/fake-merchant-client.js +6 -1
  22. package/dist/agent/merchant/fake-merchant-client.js.map +1 -1
  23. package/dist/agent/merchant/intelligence/default-backend.js +25 -2
  24. package/dist/agent/merchant/intelligence/default-backend.js.map +1 -1
  25. package/dist/agent/merchant/merchant-client.d.ts +50 -1
  26. package/dist/agent/merchant/merchant-client.js +157 -8
  27. package/dist/agent/merchant/merchant-client.js.map +1 -1
  28. package/dist/agent/merchant/merchant-presentations.js +25 -2
  29. package/dist/agent/merchant/merchant-presentations.js.map +1 -1
  30. package/dist/agent/merchant/merchant-tools.js +102 -188
  31. package/dist/agent/merchant/merchant-tools.js.map +1 -1
  32. package/dist/agent/merchant/types.d.ts +6 -2
  33. package/dist/agent/merchant/types.js +45 -0
  34. package/dist/agent/merchant/types.js.map +1 -1
  35. package/dist/auth/merchant-authorization.d.ts +49 -0
  36. package/dist/auth/merchant-authorization.js +61 -0
  37. package/dist/auth/merchant-authorization.js.map +1 -0
  38. package/dist/auth/merchant-oauth.d.ts +255 -0
  39. package/dist/auth/merchant-oauth.js +764 -0
  40. package/dist/auth/merchant-oauth.js.map +1 -0
  41. package/dist/auth/merchant-pairing.d.ts +62 -0
  42. package/dist/auth/merchant-pairing.js +199 -0
  43. package/dist/auth/merchant-pairing.js.map +1 -0
  44. package/dist/auth/merchant-sessions.d.ts +71 -0
  45. package/dist/auth/merchant-sessions.js +168 -0
  46. package/dist/auth/merchant-sessions.js.map +1 -0
  47. package/dist/buyer-core/build-service.d.ts +5 -0
  48. package/dist/buyer-core/build-service.js +12 -0
  49. package/dist/buyer-core/build-service.js.map +1 -1
  50. package/dist/buyer-core/errors.d.ts +1 -1
  51. package/dist/buyer-core/errors.js +2 -0
  52. package/dist/buyer-core/errors.js.map +1 -1
  53. package/dist/buyer-core/merchant-index.d.ts +19 -4
  54. package/dist/buyer-core/merchant-index.js +137 -16
  55. package/dist/buyer-core/merchant-index.js.map +1 -1
  56. package/dist/buyer-core/negotiator.d.ts +0 -10
  57. package/dist/buyer-core/negotiator.js +12 -1
  58. package/dist/buyer-core/negotiator.js.map +1 -1
  59. package/dist/buyer-core/quote-fetcher.js +2 -1
  60. package/dist/buyer-core/quote-fetcher.js.map +1 -1
  61. package/dist/buyer-core/service.d.ts +104 -0
  62. package/dist/buyer-core/service.js +105 -1
  63. package/dist/buyer-core/service.js.map +1 -1
  64. package/dist/cli.d.ts +4 -2
  65. package/dist/cli.js +612 -20
  66. package/dist/cli.js.map +1 -1
  67. package/dist/config/profile.d.ts +30 -0
  68. package/dist/config/profile.js +126 -61
  69. package/dist/config/profile.js.map +1 -1
  70. package/dist/demo/demo-runner.js +12 -9
  71. package/dist/demo/demo-runner.js.map +1 -1
  72. package/dist/discovery/catalog-source/buyer-follows.d.ts +82 -0
  73. package/dist/discovery/catalog-source/buyer-follows.js +228 -0
  74. package/dist/discovery/catalog-source/buyer-follows.js.map +1 -0
  75. package/dist/discovery/catalog-source/connector-identity.d.ts +75 -0
  76. package/dist/discovery/catalog-source/connector-identity.js +175 -0
  77. package/dist/discovery/catalog-source/connector-identity.js.map +1 -0
  78. package/dist/discovery/catalog-source/errors.d.ts +1 -1
  79. package/dist/discovery/catalog-source/errors.js +2 -0
  80. package/dist/discovery/catalog-source/errors.js.map +1 -1
  81. package/dist/discovery/catalog-source/merchant-publications.d.ts +65 -0
  82. package/dist/discovery/catalog-source/merchant-publications.js +207 -0
  83. package/dist/discovery/catalog-source/merchant-publications.js.map +1 -0
  84. package/dist/discovery/catalog-source/register.d.ts +24 -0
  85. package/dist/discovery/catalog-source/register.js +46 -0
  86. package/dist/discovery/catalog-source/register.js.map +1 -1
  87. package/dist/discovery/catalog-source/source.js +2 -1
  88. package/dist/discovery/catalog-source/source.js.map +1 -1
  89. package/dist/fs/atomic-write.d.ts +18 -0
  90. package/dist/fs/atomic-write.js +29 -0
  91. package/dist/fs/atomic-write.js.map +1 -0
  92. package/dist/http/server.js +4 -1
  93. package/dist/http/server.js.map +1 -1
  94. package/dist/mcp/cli.d.ts +2 -0
  95. package/dist/mcp/cli.js +7 -6
  96. package/dist/mcp/cli.js.map +1 -1
  97. package/dist/mcp/merchant-auth.d.ts +78 -0
  98. package/dist/mcp/merchant-auth.js +146 -0
  99. package/dist/mcp/merchant-auth.js.map +1 -0
  100. package/dist/mcp/merchant-dirs.d.ts +26 -0
  101. package/dist/mcp/merchant-dirs.js +44 -0
  102. package/dist/mcp/merchant-dirs.js.map +1 -0
  103. package/dist/mcp/merchant-resources.d.ts +38 -0
  104. package/dist/mcp/merchant-resources.js +146 -0
  105. package/dist/mcp/merchant-resources.js.map +1 -0
  106. package/dist/mcp/merchant-server.d.ts +106 -0
  107. package/dist/mcp/merchant-server.js +615 -0
  108. package/dist/mcp/merchant-server.js.map +1 -0
  109. package/dist/mcp/merchant-tools.d.ts +103 -0
  110. package/dist/mcp/merchant-tools.js +517 -0
  111. package/dist/mcp/merchant-tools.js.map +1 -0
  112. package/dist/mcp/server.js +4 -3
  113. package/dist/mcp/server.js.map +1 -1
  114. package/dist/mcp/tools.d.ts +5 -3
  115. package/dist/mcp/tools.js +102 -5
  116. package/dist/mcp/tools.js.map +1 -1
  117. package/dist/mcp/types.d.ts +1 -1
  118. package/dist/mcp/types.js +4 -0
  119. package/dist/mcp/types.js.map +1 -1
  120. package/dist/merchant/ops.d.ts +0 -11
  121. package/dist/merchant/ops.js +13 -1
  122. package/dist/merchant/ops.js.map +1 -1
  123. package/dist/merchant/workbench-service.d.ts +189 -0
  124. package/dist/merchant/workbench-service.js +417 -0
  125. package/dist/merchant/workbench-service.js.map +1 -0
  126. package/dist/merchant-admin/pending-page.d.ts +35 -0
  127. package/dist/merchant-admin/pending-page.js +69 -0
  128. package/dist/merchant-admin/pending-page.js.map +1 -0
  129. package/dist/merchant-core/commands.d.ts +99 -0
  130. package/dist/merchant-core/commands.js +179 -0
  131. package/dist/merchant-core/commands.js.map +1 -0
  132. package/dist/merchant-core/executor.d.ts +69 -0
  133. package/dist/merchant-core/executor.js +218 -0
  134. package/dist/merchant-core/executor.js.map +1 -0
  135. package/dist/merchant-core/negotiation-adapters.d.ts +55 -0
  136. package/dist/merchant-core/negotiation-adapters.js +55 -0
  137. package/dist/merchant-core/negotiation-adapters.js.map +1 -0
  138. package/dist/merchant-core/network-checks.d.ts +65 -0
  139. package/dist/merchant-core/network-checks.js +111 -0
  140. package/dist/merchant-core/network-checks.js.map +1 -0
  141. package/dist/merchant-core/operations.d.ts +64 -0
  142. package/dist/merchant-core/operations.js +136 -0
  143. package/dist/merchant-core/operations.js.map +1 -0
  144. package/dist/merchant-core/policy-runtime.d.ts +70 -0
  145. package/dist/merchant-core/policy-runtime.js +181 -0
  146. package/dist/merchant-core/policy-runtime.js.map +1 -0
  147. package/dist/merchant-core/product-import.d.ts +67 -0
  148. package/dist/merchant-core/product-import.js +160 -0
  149. package/dist/merchant-core/product-import.js.map +1 -0
  150. package/dist/merchant-core/service.d.ts +203 -0
  151. package/dist/merchant-core/service.js +402 -0
  152. package/dist/merchant-core/service.js.map +1 -0
  153. package/dist/merchant-gateway/catalog-publications.d.ts +96 -0
  154. package/dist/merchant-gateway/catalog-publications.js +212 -0
  155. package/dist/merchant-gateway/catalog-publications.js.map +1 -0
  156. package/dist/merchant-gateway/catalog-tools.d.ts +46 -0
  157. package/dist/merchant-gateway/catalog-tools.js +315 -0
  158. package/dist/merchant-gateway/catalog-tools.js.map +1 -0
  159. package/dist/merchant-gateway/cli.d.ts +75 -0
  160. package/dist/merchant-gateway/cli.js +461 -0
  161. package/dist/merchant-gateway/cli.js.map +1 -0
  162. package/dist/merchant-gateway/credential-vault.d.ts +44 -0
  163. package/dist/merchant-gateway/credential-vault.js +111 -0
  164. package/dist/merchant-gateway/credential-vault.js.map +1 -0
  165. package/dist/merchant-gateway/entry-server.d.ts +117 -0
  166. package/dist/merchant-gateway/entry-server.js +747 -0
  167. package/dist/merchant-gateway/entry-server.js.map +1 -0
  168. package/dist/merchant-gateway/instance-registration.d.ts +144 -0
  169. package/dist/merchant-gateway/instance-registration.js +391 -0
  170. package/dist/merchant-gateway/instance-registration.js.map +1 -0
  171. package/dist/merchant-gateway/instance-tools.d.ts +58 -0
  172. package/dist/merchant-gateway/instance-tools.js +186 -0
  173. package/dist/merchant-gateway/instance-tools.js.map +1 -0
  174. package/dist/merchant-gateway/mcp-proxy.d.ts +25 -0
  175. package/dist/merchant-gateway/mcp-proxy.js +192 -0
  176. package/dist/merchant-gateway/mcp-proxy.js.map +1 -0
  177. package/dist/merchant-gateway/pinned-fetch.d.ts +54 -0
  178. package/dist/merchant-gateway/pinned-fetch.js +231 -0
  179. package/dist/merchant-gateway/pinned-fetch.js.map +1 -0
  180. package/dist/merchant-gateway/tenant-registry.d.ts +82 -0
  181. package/dist/merchant-gateway/tenant-registry.js +190 -0
  182. package/dist/merchant-gateway/tenant-registry.js.map +1 -0
  183. package/dist/merchant-gateway/tool-bundle.d.ts +26 -0
  184. package/dist/merchant-gateway/tool-bundle.js +56 -0
  185. package/dist/merchant-gateway/tool-bundle.js.map +1 -0
  186. package/dist/merchant-runtime/backup.d.ts +48 -0
  187. package/dist/merchant-runtime/backup.js +226 -0
  188. package/dist/merchant-runtime/backup.js.map +1 -0
  189. package/dist/merchant-runtime/health.d.ts +99 -0
  190. package/dist/merchant-runtime/health.js +206 -0
  191. package/dist/merchant-runtime/health.js.map +1 -0
  192. package/dist/merchant-runtime/jobs.d.ts +64 -0
  193. package/dist/merchant-runtime/jobs.js +107 -0
  194. package/dist/merchant-runtime/jobs.js.map +1 -0
  195. package/dist/merchant-runtime/manager.d.ts +94 -0
  196. package/dist/merchant-runtime/manager.js +305 -0
  197. package/dist/merchant-runtime/manager.js.map +1 -0
  198. package/dist/merchant-runtime/services.d.ts +30 -0
  199. package/dist/merchant-runtime/services.js +51 -0
  200. package/dist/merchant-runtime/services.js.map +1 -0
  201. package/dist/net/url.d.ts +26 -0
  202. package/dist/net/url.js +32 -0
  203. package/dist/net/url.js.map +1 -0
  204. package/dist/product-cli.js +22 -3
  205. package/dist/product-cli.js.map +1 -1
  206. package/dist/product-compat.d.ts +9 -1
  207. package/dist/product-compat.js +12 -1
  208. package/dist/product-compat.js.map +1 -1
  209. package/dist/product-init.js +17 -5
  210. package/dist/product-init.js.map +1 -1
  211. package/dist/product-publish.js +2 -1
  212. package/dist/product-publish.js.map +1 -1
  213. package/package.json +3 -2
  214. package/skills/kiwi-buyer/SKILL.md +2 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * 面向商家自有实例的**钉住式**出站请求(SSRF / DNS 重绑定加固)。
18
+ *
19
+ * 背景:商家实例地址由商家自己提供(自助绑定 / 配对码),属于不可信输入。
20
+ * 只做静态校验(https + 非私网字面 IP)挡不住两类攻击:
21
+ *
22
+ * 1. **解析后落到内网**:域名静态看没问题,但解析出 10.x / 169.254 / ::1;
23
+ * 2. **DNS 重绑定(TOCTOU)**:校验时解析到公网 IP,随后 fetch 再解析一次
24
+ * 却拿到内网 IP。
25
+ *
26
+ * 本模块的做法:**一次解析、校验、然后直接用该 IP 建连**(`host: <ip>` +
27
+ * `servername`/`Host: <主机名>`),不再给运行时第二次解析的机会——这就是钉住。
28
+ * 校验规则复用 A2A 出站守卫(`assertResolvableTargetUrl`):任一解析结果落在
29
+ * 保留/私网段、或"公网域名解析到 loopback",一律 fail-closed 拒绝。
30
+ *
31
+ * loopback 与字面 IP 不走钉住:前者是本地/同机形态(静态策略已允许且已在
32
+ * 白名单内),后者没有解析步骤可被劫持。
33
+ *
34
+ * 只覆盖网关需要的调用形态(JSON/文本体的 GET/POST,不跟随重定向、读完整
35
+ * 响应体),不是 fetch 的完整实现。
36
+ */
37
+ import { request as httpRequest } from "node:http";
38
+ import { request as httpsRequest } from "node:https";
39
+ import { lookup } from "node:dns/promises";
40
+ import { isIP } from "node:net";
41
+ import { assertResolvableTargetUrl, isLoopbackHost } from "../a2a/client/url-policy.js";
42
+ export class PinnedFetchError extends Error {
43
+ constructor(message) {
44
+ super(message);
45
+ this.name = "PinnedFetchError";
46
+ }
47
+ }
48
+ async function defaultResolve(hostname) {
49
+ const addresses = await lookup(hostname, { all: true, verbatim: true });
50
+ return addresses.map((entry) => entry.address);
51
+ }
52
+ /**
53
+ * 请求头归一化:接受 Headers / 键值对数组 / 普通对象(调用方可能传 fetch 的
54
+ * RequestInit.headers 或我们自己的字面量)。用 unknown 收口,避免依赖
55
+ * 各类型定义里对 HeadersInit 的差异。
56
+ */
57
+ function headersToObject(init) {
58
+ if (init === undefined || init === null)
59
+ return {};
60
+ if (init instanceof Headers)
61
+ return Object.fromEntries(init.entries());
62
+ if (Array.isArray(init)) {
63
+ return Object.fromEntries(init);
64
+ }
65
+ if (typeof init === "object")
66
+ return { ...init };
67
+ return {};
68
+ }
69
+ /** 过滤掉 undefined 值(Node 的请求头不接受 undefined)。 */
70
+ function definedHeaders(headers) {
71
+ const result = {};
72
+ for (const [key, value] of Object.entries(headers)) {
73
+ if (typeof value === "string")
74
+ result[key] = value;
75
+ }
76
+ return result;
77
+ }
78
+ /** 字面 IP(v4/v6)判定:这些地址没有可被劫持的解析步骤。 */
79
+ function isIpLiteral(hostname) {
80
+ return isIP(hostname.replace(/^\[|\]$/g, "")) !== 0;
81
+ }
82
+ function nodeHeadersToPairs(headers) {
83
+ const pairs = [];
84
+ for (const [key, value] of Object.entries(headers)) {
85
+ if (value === undefined)
86
+ continue;
87
+ pairs.push([key, Array.isArray(value) ? value.join(", ") : value]);
88
+ }
89
+ return pairs;
90
+ }
91
+ /**
92
+ * 解析 + 校验 + 选定一个可直连的 IP。
93
+ *
94
+ * 与 `assertResolvableTargetUrl` 同一套规则,但把「校验用的解析结果」原样
95
+ * 返回给调用方去建连,消除两次解析之间的窗口。
96
+ */
97
+ export async function resolvePinnedAddress(hostname, resolve) {
98
+ let addresses;
99
+ try {
100
+ addresses = await resolve(hostname);
101
+ }
102
+ catch {
103
+ throw new PinnedFetchError(`无法解析实例主机名:${hostname}`);
104
+ }
105
+ if (addresses.length === 0) {
106
+ throw new PinnedFetchError(`实例主机名未解析出任何地址:${hostname}`);
107
+ }
108
+ // 复用 A2A 守卫的判定(含"公网域名解析到 loopback 一律拒绝"),
109
+ // 并固定用同一批解析结果做校验与建连。拒绝原因统一包装成 PinnedFetchError。
110
+ try {
111
+ await assertResolvableTargetUrl(new URL(`https://${hostname}/`), {
112
+ resolveIp: async () => addresses,
113
+ });
114
+ }
115
+ catch (err) {
116
+ throw new PinnedFetchError(`实例主机名被出站策略拒绝:${err instanceof Error ? err.message : String(err)}`);
117
+ }
118
+ const [first] = addresses;
119
+ if (first === undefined) {
120
+ throw new PinnedFetchError(`实例主机名未解析出任何地址:${hostname}`);
121
+ }
122
+ return first;
123
+ }
124
+ /**
125
+ * 按**给定地址**建连(钉住的落点),HTTP 适配层:
126
+ * `host: <address>` + `Host: <主机名>`(https 另加 servername 供 SNI/证书校验)。
127
+ * 单独导出便于对本地服务做往返测试(不经过 DNS 策略)。
128
+ */
129
+ export async function requestViaAddress(url, address, init = {}, maxResponseBytes = 1_048_576) {
130
+ const method = init.method ?? "GET";
131
+ const headers = headersToObject(init.headers);
132
+ const body = init.body;
133
+ const secure = url.protocol === "https:";
134
+ const request = secure ? httpsRequest : httpRequest;
135
+ const port = url.port === "" ? (secure ? 443 : 80) : Number(url.port);
136
+ const defaultPort = secure ? "443" : "80";
137
+ const hostHeader = url.port === "" || url.port === defaultPort ? url.hostname : `${url.hostname}:${url.port}`;
138
+ return await new Promise((resolvePromise, rejectPromise) => {
139
+ const req = request({
140
+ host: address,
141
+ port,
142
+ path: `${url.pathname}${url.search}`,
143
+ method,
144
+ headers: {
145
+ ...definedHeaders(headers),
146
+ host: hostHeader,
147
+ },
148
+ ...(secure ? { servername: url.hostname } : {}),
149
+ ...(init.signal !== undefined && init.signal !== null
150
+ ? { signal: init.signal }
151
+ : {}),
152
+ }, (res) => {
153
+ const chunks = [];
154
+ let size = 0;
155
+ let settled = false;
156
+ const rejectOnce = (error) => {
157
+ if (settled)
158
+ return;
159
+ settled = true;
160
+ rejectPromise(error);
161
+ };
162
+ res.on("data", (chunk) => {
163
+ size += chunk.length;
164
+ if (size > maxResponseBytes) {
165
+ res.destroy();
166
+ rejectOnce(new PinnedFetchError(`实例响应体超过 ${maxResponseBytes} 字节上限`));
167
+ return;
168
+ }
169
+ chunks.push(chunk);
170
+ });
171
+ res.on("end", () => {
172
+ if (settled)
173
+ return;
174
+ settled = true;
175
+ resolvePromise(new Response(Buffer.concat(chunks), {
176
+ status: res.statusCode ?? 0,
177
+ statusText: res.statusMessage ?? "",
178
+ headers: nodeHeadersToPairs(res.headers),
179
+ }));
180
+ });
181
+ res.on("error", rejectOnce);
182
+ });
183
+ req.on("error", (err) => {
184
+ rejectPromise(err instanceof Error && err.name === "AbortError"
185
+ ? err
186
+ : new PinnedFetchError(`实例请求失败(${url.hostname}):${err.message}`));
187
+ });
188
+ if (typeof body === "string")
189
+ req.write(body);
190
+ req.end();
191
+ });
192
+ }
193
+ /**
194
+ * 构造一个把目标主机**钉在已校验 IP** 上的 fetch 实现。
195
+ *
196
+ * 语义与 fetch 的差异(故意为之):不跟随重定向;响应体一次性读完(JSON-RPC
197
+ * 体量小);不处理 cookie / 流式上传。
198
+ */
199
+ export function createPinnedFetch(options = {}) {
200
+ const resolve = options.resolveIp ?? defaultResolve;
201
+ const pinnedFetch = async (input, init) => {
202
+ const requestInit = init ?? {};
203
+ const url = new URL(typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url);
204
+ const method = requestInit.method ?? (input instanceof Request ? input.method : "GET");
205
+ const headers = {
206
+ ...headersToObject(input instanceof Request ? input.headers : undefined),
207
+ ...headersToObject(requestInit.headers),
208
+ };
209
+ const body = requestInit.body;
210
+ // loopback / 字面 IP:无解析步骤可被劫持,直接按该主机建连(仍走同一适配层)
211
+ // ——统一路径的另一个好处:不经过全局 fetch 的连接池,实例重启后不会因为
212
+ // 复用到失效的 keep-alive 连接而出现一次 ECONNRESET(表现为 backend_unavailable)。
213
+ if (isLoopbackHost(url.hostname) || isIpLiteral(url.hostname)) {
214
+ return await requestViaAddress(url, url.hostname.replace(/^\[|\]$/g, ""), {
215
+ method,
216
+ headers,
217
+ ...(body !== undefined && body !== null ? { body } : {}),
218
+ ...(requestInit.signal !== undefined ? { signal: requestInit.signal } : {}),
219
+ }, options.maxResponseBytes);
220
+ }
221
+ const pinnedIp = await resolvePinnedAddress(url.hostname, resolve);
222
+ return await requestViaAddress(url, pinnedIp, {
223
+ method,
224
+ headers,
225
+ ...(body !== undefined && body !== null ? { body } : {}),
226
+ ...(requestInit.signal !== undefined ? { signal: requestInit.signal } : {}),
227
+ }, options.maxResponseBytes);
228
+ };
229
+ return pinnedFetch;
230
+ }
231
+ //# sourceMappingURL=pinned-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinned-fetch.js","sourceRoot":"","sources":["../../src/merchant-gateway/pinned-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAExF,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAqBD,KAAK,UAAU,cAAc,CAAC,QAAgB;IAC5C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACnD,IAAI,IAAI,YAAY,OAAO;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,WAAW,CAAC,IAA+B,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAI,IAA2C,EAAE,CAAC;IACzF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,+CAA+C;AAC/C,SAAS,cAAc,CAAC,OAA2C;IACjE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACrD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sCAAsC;AACtC,SAAS,WAAW,CAAC,QAAgB;IACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,kBAAkB,CACzB,OAAsD;IAEtD,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAgB,EAChB,OAAgD;IAEhD,IAAI,SAAmB,CAAC;IACxB,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,gBAAgB,CAAC,aAAa,QAAQ,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,0CAA0C;IAC1C,gDAAgD;IAChD,IAAI,CAAC;QACH,MAAM,yBAAyB,CAAC,IAAI,GAAG,CAAC,WAAW,QAAQ,GAAG,CAAC,EAAE;YAC/D,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;SACjC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,gBAAgB,CACxB,gBAAgB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,QAAQ,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAQ,EACR,OAAe,EACf,OAAkB,EAAE,EACpB,gBAAgB,GAAG,SAAS;IAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;IACpC,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACzC,MAAM,OAAO,GAAc,MAAM,CAAC,CAAC,CAAE,YAA0B,CAAC,CAAC,CAAE,WAAyB,CAAC;IAC7F,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,MAAM,UAAU,GACd,GAAG,CAAC,IAAI,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;IAE7F,OAAO,MAAM,IAAI,OAAO,CAAW,CAAC,cAAc,EAAE,aAAa,EAAE,EAAE;QACnE,MAAM,GAAG,GAAG,OAAO,CACjB;YACE,IAAI,EAAE,OAAO;YACb,IAAI;YACJ,IAAI,EAAE,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE;YACpC,MAAM;YACN,OAAO,EAAE;gBACP,GAAG,cAAc,CAAC,OAAO,CAAC;gBAC1B,IAAI,EAAE,UAAU;aACjB;YACD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;gBACnD,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAqB,EAAE;gBACxC,CAAC,CAAC,EAAE,CAAC;SACR,EACD,CAAC,GAAG,EAAE,EAAE;YACN,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,MAAM,UAAU,GAAG,CAAC,KAAY,EAAQ,EAAE;gBACxC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC,CAAC;YACF,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;gBAC/B,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;gBACrB,IAAI,IAAI,GAAG,gBAAgB,EAAE,CAAC;oBAC5B,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,UAAU,CAAC,IAAI,gBAAgB,CAAC,WAAW,gBAAgB,OAAO,CAAC,CAAC,CAAC;oBACrE,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,cAAc,CACZ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;oBAClC,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC;oBAC3B,UAAU,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE;oBACnC,OAAO,EAAE,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC;iBACzC,CAAC,CACH,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC9B,CAAC,CACF,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,aAAa,CACX,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY;gBAC/C,CAAC,CAAC,GAAG;gBACL,CAAC,CAAC,IAAI,gBAAgB,CAAC,UAAU,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CACnE,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAA8B,EAAE;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,cAAc,CAAC;IACpD,MAAM,WAAW,GAAG,KAAK,EACvB,KAA6B,EAC7B,IAAgB,EACG,EAAE;QACrB,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CACjB,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CACxF,CAAC;QACF,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACvF,MAAM,OAAO,GAAG;YACd,GAAG,eAAe,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YACxE,GAAG,eAAe,CAAC,WAAW,CAAC,OAAO,CAAC;SACxC,CAAC;QACF,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC;QAE9B,+CAA+C;QAC/C,0CAA0C;QAC1C,iEAAiE;QACjE,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,OAAO,MAAM,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE;gBACxE,MAAM;gBACN,OAAO;gBACP,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,GAAG,CAAC,WAAW,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5E,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC/B,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnE,OAAO,MAAM,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE;YAC5C,MAAM;YACN,OAAO;YACP,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,WAAW,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5E,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/B,CAAC,CAAC;IACF,OAAO,WAA2B,CAAC;AACrC,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * 商家连接器入口的受信任后端注册表(第 1 版实例路由)。
3
+ *
4
+ * 入口只知道自己的公网 URL;它从**已验证 OAuth token 的 merchant_id** 选择
5
+ * 服务端注册的商家实例,绝不使用模型工具参数或客户端可填写的 URL 路由。
6
+ *
7
+ * URL 策略(fail-closed,对齐商家连接器发布计划 §3.3):
8
+ * - 非 loopback 必须是 https(明文 http 仅限 loopback 开发实例);
9
+ * - 复用 A2A 出站守卫 `assertSafeTargetUrl`:拒绝 userinfo、保留主机名、
10
+ * 字面保留 IP;路径必须恰为 `/mcp`,不得带查询串或片段;
11
+ * - loopback 实例必须显式指定端口(避免误连本机其它服务)。
12
+ *
13
+ * **仍未覆盖**(不得宣称已支持异地自托管):请求时的 DNS/IP 钉住与 mTLS
14
+ * 尚未实现——`assertResolvableTargetUrl` 只做解析后私网复查,不能替代钉住;
15
+ * 服务认领(配对码)与凭证轮换属后续增量。
16
+ */
17
+ import type { MerchantAuthorization } from "../auth/merchant-authorization.js";
18
+ import type { MerchantCredentialStore } from "./credential-vault.js";
19
+ /** 实例内部凭据的保管库键前缀(与目录凭据 cmt_ 分开放)。 */
20
+ export declare const INSTANCE_CREDENTIAL_KIND = "instance";
21
+ export interface TenantBackendConfig {
22
+ merchantId: string;
23
+ /** 商家自有实例的 MCP 地址:https,或 loopback http;路径恰为 /mcp。 */
24
+ mcpUrl: string;
25
+ /** 内部凭据来源:进程环境变量名(运维侧注入)。 */
26
+ tokenEnv?: string;
27
+ /** 内部凭据来源:入口凭据保管库(加密存储;键 `<merchant_id>`,kind=instance)。 */
28
+ credentialKind?: "env" | "vault";
29
+ }
30
+ export interface ResolvedTenantBackend {
31
+ merchantId: string;
32
+ mcpUrl: string;
33
+ bearerToken: string;
34
+ }
35
+ export declare class TenantBackendError extends Error {
36
+ readonly code: "invalid_config" | "service_not_connected" | "backend_credentials_missing";
37
+ constructor(message: string, code: "invalid_config" | "service_not_connected" | "backend_credentials_missing");
38
+ }
39
+ /** 实例 MCP 地址规范化 + 出站策略校验(绑定页与注册表共用同一口径)。 */
40
+ export declare function canonicalInstanceUrl(value: string): string;
41
+ export interface TenantBackendRegistryOptions {
42
+ /** 凭据保管库(`credentialKind: "vault"` 的实例从这里取内部凭据)。 */
43
+ credentials?: MerchantCredentialStore;
44
+ /**
45
+ * 动态注册(商家自助绑定,§8.4 第一期):静态配置未命中时按 merchant_id
46
+ * 查询商家自行绑定的实例地址;凭据同样从保管库取(键 `instance:<id>`)。
47
+ */
48
+ dynamic?: {
49
+ lookup(merchantId: string): {
50
+ mcpUrl: string;
51
+ } | undefined;
52
+ };
53
+ }
54
+ /**
55
+ * 解析运维提供的租户配置(JSON 文本)→ 注册表配置。
56
+ *
57
+ * 形状:
58
+ * ```json
59
+ * { "tenants": [
60
+ * { "merchant_id": "mkt_acme_1", "mcp_url": "https://merchant.acme.example/mcp",
61
+ * "token_env": "ACME_MCP_TOKEN" },
62
+ * { "merchant_id": "mkt_dev_1", "mcp_url": "http://127.0.0.1:9100/mcp",
63
+ * "credential_kind": "vault" }
64
+ * ] }
65
+ * ```
66
+ * 字段缺失/类型不符一律抛 TenantBackendError(invalid_config),不做默认填充。
67
+ */
68
+ export declare function loadTenantBackendConfigs(raw: string): TenantBackendConfig[];
69
+ /** 只由运维配置构造;尚未配对的商家不进入注册表(第 0 版能力不受影响)。 */
70
+ export declare class TenantBackendRegistry {
71
+ private readonly backends;
72
+ private readonly env;
73
+ private readonly credentials;
74
+ private readonly dynamic;
75
+ constructor(configs: TenantBackendConfig[], env?: Record<string, string | undefined>, options?: TenantBackendRegistryOptions);
76
+ /** 该商家是否已注册实例(静态配置或商家自助绑定)。 */
77
+ has(merchantId: string): boolean;
78
+ /** 已注册实例数量(启动摘要用)。 */
79
+ get size(): number;
80
+ /** OAuth 校验之后调用;不接受 tool arguments 中的 merchant_id 作为路由依据。 */
81
+ resolve(authorization: MerchantAuthorization): ResolvedTenantBackend;
82
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * 商家连接器入口的受信任后端注册表(第 1 版实例路由)。
3
+ *
4
+ * 入口只知道自己的公网 URL;它从**已验证 OAuth token 的 merchant_id** 选择
5
+ * 服务端注册的商家实例,绝不使用模型工具参数或客户端可填写的 URL 路由。
6
+ *
7
+ * URL 策略(fail-closed,对齐商家连接器发布计划 §3.3):
8
+ * - 非 loopback 必须是 https(明文 http 仅限 loopback 开发实例);
9
+ * - 复用 A2A 出站守卫 `assertSafeTargetUrl`:拒绝 userinfo、保留主机名、
10
+ * 字面保留 IP;路径必须恰为 `/mcp`,不得带查询串或片段;
11
+ * - loopback 实例必须显式指定端口(避免误连本机其它服务)。
12
+ *
13
+ * **仍未覆盖**(不得宣称已支持异地自托管):请求时的 DNS/IP 钉住与 mTLS
14
+ * 尚未实现——`assertResolvableTargetUrl` 只做解析后私网复查,不能替代钉住;
15
+ * 服务认领(配对码)与凭证轮换属后续增量。
16
+ */
17
+ import { assertSafeTargetUrl, isLoopbackHost } from "../a2a/client/url-policy.js";
18
+ /** 实例内部凭据的保管库键前缀(与目录凭据 cmt_ 分开放)。 */
19
+ export const INSTANCE_CREDENTIAL_KIND = "instance";
20
+ export class TenantBackendError extends Error {
21
+ code;
22
+ constructor(message, code) {
23
+ super(message);
24
+ this.code = code;
25
+ this.name = "TenantBackendError";
26
+ }
27
+ }
28
+ /** 实例 MCP 地址规范化 + 出站策略校验(绑定页与注册表共用同一口径)。 */
29
+ export function canonicalInstanceUrl(value) {
30
+ let url;
31
+ try {
32
+ url = new URL(value);
33
+ }
34
+ catch {
35
+ throw new TenantBackendError("商家 MCP 后端 URL 非法", "invalid_config");
36
+ }
37
+ // 明文 http 仅限 loopback;非 loopback 必须 https。保留主机名/字面保留 IP 拒绝。
38
+ try {
39
+ assertSafeTargetUrl(value, { allowLoopback: true });
40
+ }
41
+ catch (err) {
42
+ throw new TenantBackendError(`商家 MCP 后端地址被出站策略拒绝:${err instanceof Error ? err.message : String(err)}`, "invalid_config");
43
+ }
44
+ if (url.pathname !== "/mcp" || url.search !== "" || url.hash !== "") {
45
+ throw new TenantBackendError("商家 MCP 后端路径必须恰为 /mcp,且不含查询串或片段", "invalid_config");
46
+ }
47
+ if (isLoopbackHost(url.hostname)) {
48
+ if (url.port === "" || Number(url.port) <= 0 || Number(url.port) > 65535) {
49
+ throw new TenantBackendError("loopback 商家后端必须显式指定有效端口", "invalid_config");
50
+ }
51
+ }
52
+ else if (url.port !== "" && (Number(url.port) <= 0 || Number(url.port) > 65535)) {
53
+ throw new TenantBackendError("商家 MCP 后端端口非法", "invalid_config");
54
+ }
55
+ return url.toString();
56
+ }
57
+ /**
58
+ * 解析运维提供的租户配置(JSON 文本)→ 注册表配置。
59
+ *
60
+ * 形状:
61
+ * ```json
62
+ * { "tenants": [
63
+ * { "merchant_id": "mkt_acme_1", "mcp_url": "https://merchant.acme.example/mcp",
64
+ * "token_env": "ACME_MCP_TOKEN" },
65
+ * { "merchant_id": "mkt_dev_1", "mcp_url": "http://127.0.0.1:9100/mcp",
66
+ * "credential_kind": "vault" }
67
+ * ] }
68
+ * ```
69
+ * 字段缺失/类型不符一律抛 TenantBackendError(invalid_config),不做默认填充。
70
+ */
71
+ export function loadTenantBackendConfigs(raw) {
72
+ let parsed;
73
+ try {
74
+ parsed = JSON.parse(raw);
75
+ }
76
+ catch (err) {
77
+ throw new TenantBackendError(`租户配置不是合法 JSON:${err instanceof Error ? err.message : String(err)}`, "invalid_config");
78
+ }
79
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
80
+ throw new TenantBackendError("租户配置必须是对象", "invalid_config");
81
+ }
82
+ const tenants = parsed.tenants;
83
+ if (!Array.isArray(tenants)) {
84
+ throw new TenantBackendError('租户配置缺 "tenants" 数组', "invalid_config");
85
+ }
86
+ const configs = [];
87
+ for (const element of tenants) {
88
+ if (element === null || typeof element !== "object" || Array.isArray(element)) {
89
+ throw new TenantBackendError("tenants[] 必须是对象", "invalid_config");
90
+ }
91
+ const record = element;
92
+ const merchantId = typeof record.merchant_id === "string" ? record.merchant_id.trim() : "";
93
+ const mcpUrl = typeof record.mcp_url === "string" ? record.mcp_url.trim() : "";
94
+ if (merchantId === "" || mcpUrl === "") {
95
+ throw new TenantBackendError("tenants[] 必须含 merchant_id 与 mcp_url", "invalid_config");
96
+ }
97
+ const tokenEnv = typeof record.token_env === "string" ? record.token_env.trim() : undefined;
98
+ const kindRaw = record.credential_kind;
99
+ const credentialKind = kindRaw === undefined ? undefined : kindRaw === "vault" ? "vault" : undefined;
100
+ if (kindRaw !== undefined && credentialKind === undefined) {
101
+ throw new TenantBackendError(`tenants[] credential_kind 只支持 "vault"(实际 ${String(kindRaw)})`, "invalid_config");
102
+ }
103
+ configs.push({
104
+ merchantId,
105
+ mcpUrl,
106
+ ...(tokenEnv !== undefined ? { tokenEnv } : {}),
107
+ ...(credentialKind !== undefined ? { credentialKind } : {}),
108
+ });
109
+ }
110
+ return configs;
111
+ }
112
+ /** 只由运维配置构造;尚未配对的商家不进入注册表(第 0 版能力不受影响)。 */
113
+ export class TenantBackendRegistry {
114
+ backends = new Map();
115
+ env;
116
+ credentials;
117
+ dynamic;
118
+ constructor(configs, env = process.env, options = {}) {
119
+ this.env = env;
120
+ this.credentials = options.credentials;
121
+ this.dynamic = options.dynamic;
122
+ for (const config of configs) {
123
+ const merchantId = config.merchantId.trim();
124
+ if (!merchantId || !/^[a-zA-Z0-9:_-]+$/.test(merchantId)) {
125
+ throw new TenantBackendError("商家后端配置缺合法 merchantId", "invalid_config");
126
+ }
127
+ if (this.backends.has(merchantId)) {
128
+ throw new TenantBackendError(`重复商家后端配置:${merchantId}`, "invalid_config");
129
+ }
130
+ const kind = config.credentialKind ?? "env";
131
+ if (kind === "env") {
132
+ if (config.tokenEnv === undefined || !/^[A-Z][A-Z0-9_]*$/.test(config.tokenEnv)) {
133
+ throw new TenantBackendError("商家后端 tokenEnv 必须是环境变量名", "invalid_config");
134
+ }
135
+ }
136
+ else if (config.tokenEnv !== undefined) {
137
+ throw new TenantBackendError(`商家后端 ${merchantId} 的凭据来源只能二选一(tokenEnv 或 credentialKind)`, "invalid_config");
138
+ }
139
+ this.backends.set(merchantId, {
140
+ merchantId,
141
+ mcpUrl: canonicalInstanceUrl(config.mcpUrl),
142
+ ...(config.tokenEnv !== undefined ? { tokenEnv: config.tokenEnv } : {}),
143
+ credentialKind: kind,
144
+ });
145
+ }
146
+ }
147
+ /** 该商家是否已注册实例(静态配置或商家自助绑定)。 */
148
+ has(merchantId) {
149
+ if (this.backends.has(merchantId))
150
+ return true;
151
+ return this.dynamic?.lookup(merchantId) !== undefined;
152
+ }
153
+ /** 已注册实例数量(启动摘要用)。 */
154
+ get size() {
155
+ return this.backends.size;
156
+ }
157
+ /** OAuth 校验之后调用;不接受 tool arguments 中的 merchant_id 作为路由依据。 */
158
+ resolve(authorization) {
159
+ if (!authorization.principal_id || !authorization.merchant_id) {
160
+ throw new TenantBackendError("未认证的商家主体", "service_not_connected");
161
+ }
162
+ const backend = this.backends.get(authorization.merchant_id);
163
+ if (backend === undefined) {
164
+ // 商家自助绑定(§8.4):地址来自注册表、凭据来自加密保管库;地址在这里
165
+ // 再校验一次(防配置被改动后绕过出站策略)。
166
+ const registered = this.dynamic?.lookup(authorization.merchant_id);
167
+ if (registered === undefined) {
168
+ throw new TenantBackendError("当前商家尚未连接 Kiwi Merchant 实例", "service_not_connected");
169
+ }
170
+ const bearerToken = this.credentials?.get(`${INSTANCE_CREDENTIAL_KIND}:${authorization.merchant_id}`)?.token ??
171
+ "";
172
+ if (!bearerToken) {
173
+ throw new TenantBackendError("商家后端内部凭据未配置", "backend_credentials_missing");
174
+ }
175
+ return {
176
+ merchantId: authorization.merchant_id,
177
+ mcpUrl: canonicalInstanceUrl(registered.mcpUrl),
178
+ bearerToken,
179
+ };
180
+ }
181
+ const bearerToken = backend.credentialKind === "vault"
182
+ ? (this.credentials?.get(`${INSTANCE_CREDENTIAL_KIND}:${backend.merchantId}`)?.token ?? "")
183
+ : (this.env[String(backend.tokenEnv)] ?? "");
184
+ if (!bearerToken) {
185
+ throw new TenantBackendError("商家后端内部凭据未配置", "backend_credentials_missing");
186
+ }
187
+ return { merchantId: backend.merchantId, mcpUrl: backend.mcpUrl, bearerToken };
188
+ }
189
+ }
190
+ //# sourceMappingURL=tenant-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-registry.js","sourceRoot":"","sources":["../../src/merchant-gateway/tenant-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * 把多个带 scope 的工具束合成一个(入口同时提供第 0 版目录工具与第 1 版
18
+ * 实例工具)。
19
+ *
20
+ * 语义:
21
+ * - `listTools`:按名去重后拼接(同名工具以先出现的束为准);
22
+ * - `call`:按工具名路由到提供它的束;都没有则返回可解释错误;
23
+ * - 空数组返回 `undefined`(调用方据此对调用方隐藏全部工具)。
24
+ */
25
+ import type { ScopedMcpTools } from "../mcp/merchant-server.js";
26
+ export declare function combineScopedTools(bundles: Array<ScopedMcpTools | undefined>): ScopedMcpTools | undefined;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Copyright 2026 harrylabsj
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export function combineScopedTools(bundles) {
17
+ const active = bundles.filter((bundle) => bundle !== undefined);
18
+ if (active.length === 0)
19
+ return undefined;
20
+ if (active.length === 1)
21
+ return active[0];
22
+ const callTool = async (name, args, scopes) => {
23
+ for (const bundle of active) {
24
+ const tools = await bundle.listTools(scopes);
25
+ if (tools.some((tool) => tool.name === name)) {
26
+ return await bundle.call(name, args, scopes);
27
+ }
28
+ }
29
+ return undefined;
30
+ };
31
+ return {
32
+ listTools: async (scopes) => {
33
+ const merged = [];
34
+ const seen = new Set();
35
+ for (const bundle of active) {
36
+ for (const tool of await bundle.listTools(scopes)) {
37
+ if (seen.has(tool.name))
38
+ continue;
39
+ seen.add(tool.name);
40
+ merged.push(tool);
41
+ }
42
+ }
43
+ return merged;
44
+ },
45
+ call: async (name, args, scopes) => {
46
+ const result = await callTool(name, args, scopes);
47
+ if (result !== undefined)
48
+ return result;
49
+ return {
50
+ content: [{ type: "text", text: `未提供工具 ${name}(可能未授权、或该商家尚未连接实例)` }],
51
+ isError: true,
52
+ };
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=tool-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-bundle.js","sourceRoot":"","sources":["../../src/merchant-gateway/tool-bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAeH,MAAM,UAAU,kBAAkB,CAChC,OAA0C;IAE1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAA4B,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IAC1F,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,KAAK,EACpB,IAAY,EACZ,IAA6B,EAC7B,MAA4B,EACgB,EAAE;QAC9C,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC7C,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO;QACL,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAC1B,MAAM,MAAM,GAAgC,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;YAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;gBAC5B,KAAK,MAAM,IAAI,IAAI,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;oBAClD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;wBAAE,SAAS;oBAClC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACpB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAClD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YACxC,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,IAAI,oBAAoB,EAAE,CAAC;gBACpE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}