@harrylabsj/kiwi 0.7.9 → 0.7.11
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/contracts/commerce-intent/1.0/schema.json +124 -0
- package/contracts/delegation-policy/1.0/schema.json +143 -0
- package/contracts/effective-authorization/1.0/schema.json +167 -0
- package/contracts/kiwi-contracts.lock.json +1 -1
- package/contracts/manifest.json +25 -1
- package/contracts/persistent-task/1.0/schema.json +191 -0
- package/contracts/vectors/commerce-intent.invalid-no-items.json +7 -0
- package/contracts/vectors/commerce-intent.valid.json +32 -0
- package/contracts/vectors/delegation-policy.invalid-payment-auto.json +15 -0
- package/contracts/vectors/delegation-policy.valid.json +25 -0
- package/contracts/vectors/effective-authorization.invalid-deny-wins.json +20 -0
- package/contracts/vectors/effective-authorization.valid.json +23 -0
- package/contracts/vectors/persistent-task.invalid-no-idempotency.json +7 -0
- package/contracts/vectors/persistent-task.valid.json +45 -0
- package/dist/a2a/client/url-policy.js +52 -14
- package/dist/a2a/client/url-policy.js.map +1 -1
- package/dist/a2a/node.js +27 -1
- package/dist/a2a/node.js.map +1 -1
- package/dist/a2a/server/merchant-handler.d.ts +27 -0
- package/dist/a2a/server/merchant-handler.js +88 -10
- package/dist/a2a/server/merchant-handler.js.map +1 -1
- package/dist/a2a/signing-key.js +18 -0
- package/dist/a2a/signing-key.js.map +1 -1
- package/dist/buyer-core/a2a-knp.d.ts +71 -0
- package/dist/buyer-core/a2a-knp.js +139 -0
- package/dist/buyer-core/a2a-knp.js.map +1 -0
- package/dist/buyer-core/a2a-negotiator.d.ts +37 -0
- package/dist/buyer-core/a2a-negotiator.js +152 -0
- package/dist/buyer-core/a2a-negotiator.js.map +1 -0
- package/dist/buyer-core/a2a-quote-fetcher.d.ts +44 -0
- package/dist/buyer-core/a2a-quote-fetcher.js +178 -0
- package/dist/buyer-core/a2a-quote-fetcher.js.map +1 -0
- package/dist/buyer-core/build-service.d.ts +44 -0
- package/dist/buyer-core/build-service.js +76 -0
- package/dist/buyer-core/build-service.js.map +1 -0
- package/dist/buyer-core/errors.d.ts +31 -0
- package/dist/buyer-core/errors.js +52 -0
- package/dist/buyer-core/errors.js.map +1 -0
- package/dist/buyer-core/merchant-index.d.ts +64 -0
- package/dist/buyer-core/merchant-index.js +213 -0
- package/dist/buyer-core/merchant-index.js.map +1 -0
- package/dist/buyer-core/negotiator.d.ts +51 -0
- package/dist/buyer-core/negotiator.js +181 -0
- package/dist/buyer-core/negotiator.js.map +1 -0
- package/dist/buyer-core/quote-fetcher.d.ts +46 -0
- package/dist/buyer-core/quote-fetcher.js +166 -0
- package/dist/buyer-core/quote-fetcher.js.map +1 -0
- package/dist/buyer-core/service.d.ts +218 -0
- package/dist/buyer-core/service.js +558 -0
- package/dist/buyer-core/service.js.map +1 -0
- package/dist/buyer-core/store.d.ts +84 -0
- package/dist/buyer-core/store.js +256 -0
- package/dist/buyer-core/store.js.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/profile.d.ts +9 -0
- package/dist/config/profile.js +23 -0
- package/dist/config/profile.js.map +1 -1
- package/dist/contracts/northbound-schema.d.ts +27 -0
- package/dist/contracts/northbound-schema.js +102 -0
- package/dist/contracts/northbound-schema.js.map +1 -0
- package/dist/handoff/destination.js +16 -5
- package/dist/handoff/destination.js.map +1 -1
- package/dist/http/cli.d.ts +16 -0
- package/dist/http/cli.js +123 -0
- package/dist/http/cli.js.map +1 -0
- package/dist/http/server.d.ts +39 -0
- package/dist/http/server.js +297 -0
- package/dist/http/server.js.map +1 -0
- package/dist/mcp/cli.d.ts +70 -0
- package/dist/mcp/cli.js +147 -0
- package/dist/mcp/cli.js.map +1 -0
- package/dist/mcp/server.d.ts +41 -0
- package/dist/mcp/server.js +141 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +28 -0
- package/dist/mcp/tools.js +299 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/types.d.ts +107 -0
- package/dist/mcp/types.js +52 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/merchant/decision-backend.d.ts +82 -0
- package/dist/merchant/decision-backend.js +275 -0
- package/dist/merchant/decision-backend.js.map +1 -0
- package/dist/merchant/ops.d.ts +80 -0
- package/dist/merchant/ops.js +75 -0
- package/dist/merchant/ops.js.map +1 -0
- package/dist/merchant/ucp-profile.d.ts +39 -0
- package/dist/merchant/ucp-profile.js +55 -0
- package/dist/merchant/ucp-profile.js.map +1 -0
- package/dist/product-cli.d.ts +1 -1
- package/dist/product-cli.js +1 -1
- package/dist/product-cli.js.map +1 -1
- package/dist/trust/identity/auth-verifier.js +19 -1
- package/dist/trust/identity/auth-verifier.js.map +1 -1
- package/dist/trust/identity/message-signature.js +14 -8
- package/dist/trust/identity/message-signature.js.map +1 -1
- package/examples/profiles/merchant.deepseek.yaml +5 -0
- package/examples/profiles/merchant.fake.yaml +4 -0
- package/package.json +4 -2
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
import { buildA2AClient, buildCounterEnvelope, envelopeToMessage, extractKnpEnvelope, } from "./a2a-knp.js";
|
|
17
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
18
|
+
const DEFAULT_DISCOUNT_RATE = 0.1;
|
|
19
|
+
function utcNow() {
|
|
20
|
+
return new Date().toISOString();
|
|
21
|
+
}
|
|
22
|
+
/** 从 offer envelope JSON(provenance.reply_text)解析商家单价(minor/元)。 */
|
|
23
|
+
function parseOfferPriceMinor(replyText) {
|
|
24
|
+
if (replyText === undefined || replyText === "")
|
|
25
|
+
return undefined;
|
|
26
|
+
try {
|
|
27
|
+
const envelope = JSON.parse(replyText);
|
|
28
|
+
const minor = envelope.payload?.terms?.items?.[0]?.unit_price?.amount_minor;
|
|
29
|
+
return typeof minor === "number" && Number.isFinite(minor) ? minor : undefined;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function intentQuantity(intent) {
|
|
36
|
+
const items = Array.isArray(intent.items) ? intent.items : [];
|
|
37
|
+
const qty = items[0]?.quantity;
|
|
38
|
+
if (typeof qty === "object" && qty !== null) {
|
|
39
|
+
const value = qty.value;
|
|
40
|
+
if (typeof value === "number" && value > 0)
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
function targetPriceMinor(intent, current, discountRate) {
|
|
46
|
+
const constraints = intent.constraints;
|
|
47
|
+
if (typeof constraints === "object" && constraints !== null) {
|
|
48
|
+
// 契约:target_unit_price 是 money 对象 {currency, amount_minor}(commerce-intent/1.0)。
|
|
49
|
+
const raw = constraints.target_unit_price;
|
|
50
|
+
let value;
|
|
51
|
+
if (typeof raw === "object" && raw !== null) {
|
|
52
|
+
const minor = raw.amount_minor;
|
|
53
|
+
if (typeof minor === "number" && Number.isFinite(minor) && minor > 0)
|
|
54
|
+
value = minor;
|
|
55
|
+
}
|
|
56
|
+
else if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
|
|
57
|
+
value = raw; // 宽松兼容:裸数字按 minor 处理
|
|
58
|
+
}
|
|
59
|
+
else if (typeof raw === "string" && Number.isFinite(Number(raw)) && Number(raw) > 0) {
|
|
60
|
+
value = Number(raw);
|
|
61
|
+
}
|
|
62
|
+
if (value !== undefined)
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
if (current !== undefined)
|
|
66
|
+
return Math.round(current * (1 - discountRate) * 100) / 100;
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
export class A2ANegotiator {
|
|
70
|
+
allowPrivateRanges;
|
|
71
|
+
skipDnsCheck;
|
|
72
|
+
timeoutMs;
|
|
73
|
+
bearerToken;
|
|
74
|
+
defaultDiscountRate;
|
|
75
|
+
fetchImpl;
|
|
76
|
+
constructor(options = {}) {
|
|
77
|
+
this.allowPrivateRanges = options.allowPrivateRanges ?? false;
|
|
78
|
+
this.skipDnsCheck = options.skipDnsCheck ?? false;
|
|
79
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
this.bearerToken = options.bearerToken;
|
|
81
|
+
this.defaultDiscountRate = options.defaultDiscountRate ?? DEFAULT_DISCOUNT_RATE;
|
|
82
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
83
|
+
}
|
|
84
|
+
async negotiate(_taskId, intent, current, candidates) {
|
|
85
|
+
const candidate = candidates.find((c) => c.provenance?.a2a_endpoint !== undefined);
|
|
86
|
+
if (candidate === undefined) {
|
|
87
|
+
return { ...current, summary: `${current.summary}(无 A2A 会话上下文,无法真实还价)` };
|
|
88
|
+
}
|
|
89
|
+
const { negotiation_id, offer_id, merchant_reply_id, reply_text, sku, a2a_endpoint } = candidate.provenance ?? {};
|
|
90
|
+
if (a2a_endpoint === undefined ||
|
|
91
|
+
negotiation_id === undefined ||
|
|
92
|
+
offer_id === undefined ||
|
|
93
|
+
merchant_reply_id === undefined) {
|
|
94
|
+
return { ...current, summary: `${current.summary}(A2A 会话上下文不完整,无法真实还价)` };
|
|
95
|
+
}
|
|
96
|
+
const endpoint = String(a2a_endpoint);
|
|
97
|
+
const negotiationId = String(negotiation_id);
|
|
98
|
+
const offerId = String(offer_id);
|
|
99
|
+
const inReplyTo = String(merchant_reply_id);
|
|
100
|
+
const itemSku = typeof sku === "string" && sku !== "" ? sku : "item-1";
|
|
101
|
+
try {
|
|
102
|
+
const currentPrice = parseOfferPriceMinor(reply_text);
|
|
103
|
+
const target = targetPriceMinor(intent, currentPrice, this.defaultDiscountRate);
|
|
104
|
+
if (target === undefined) {
|
|
105
|
+
return { ...current, summary: `${current.summary}(无目标价可还:意图无 target_unit_price 且商家回复无单价)` };
|
|
106
|
+
}
|
|
107
|
+
const envelope = buildCounterEnvelope({
|
|
108
|
+
negotiationId,
|
|
109
|
+
inReplyTo,
|
|
110
|
+
respondingToOfferId: offerId,
|
|
111
|
+
sku: itemSku,
|
|
112
|
+
quantity: intentQuantity(intent),
|
|
113
|
+
counterPriceMinor: target,
|
|
114
|
+
now: utcNow,
|
|
115
|
+
});
|
|
116
|
+
const client = buildA2AClient(endpoint, {
|
|
117
|
+
bearerToken: this.bearerToken,
|
|
118
|
+
allowPrivateRanges: this.allowPrivateRanges,
|
|
119
|
+
skipDnsCheck: this.skipDnsCheck,
|
|
120
|
+
timeoutMs: this.timeoutMs,
|
|
121
|
+
fetchImpl: this.fetchImpl,
|
|
122
|
+
});
|
|
123
|
+
const task = await client.sendMessage(envelopeToMessage(envelope));
|
|
124
|
+
const reply = await this.waitForMerchantReply(client, task, envelope.message_id);
|
|
125
|
+
if (reply === null) {
|
|
126
|
+
return { ...current, summary: `${current.summary}(商家处理中,未回还价)` };
|
|
127
|
+
}
|
|
128
|
+
return { ...current, reply: JSON.stringify(reply) };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
...current,
|
|
133
|
+
summary: `${current.summary}(还价失败:${error instanceof Error ? error.message : String(error)})`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** 轮询直到 merchant 回复 envelope 出现(排除自家 outbound 回声)或超时。 */
|
|
138
|
+
async waitForMerchantReply(client, initial, outboundMessageId) {
|
|
139
|
+
const deadline = Date.now() + this.timeoutMs;
|
|
140
|
+
let task = initial;
|
|
141
|
+
for (;;) {
|
|
142
|
+
const envelope = extractKnpEnvelope(task);
|
|
143
|
+
if (envelope !== null && envelope.message_id !== outboundMessageId)
|
|
144
|
+
return envelope;
|
|
145
|
+
if (Date.now() >= deadline)
|
|
146
|
+
return null;
|
|
147
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
148
|
+
task = await client.getTask(task.id);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=a2a-negotiator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-negotiator.js","sourceRoot":"","sources":["../../src/buyer-core/a2a-negotiator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
import type { QuoteCandidateInput, QuoteFetcher, MerchantRecord } from "./service.js";
|
|
17
|
+
export interface A2AQuoteFetcherOptions {
|
|
18
|
+
/** 允许打到私网/保留网段(SSRF 逃生门;本地试点直连时开)。 */
|
|
19
|
+
allowPrivateRanges?: boolean;
|
|
20
|
+
/** 跳过 DNS 保留网段复查(测试/本机直连)。 */
|
|
21
|
+
skipDnsCheck?: boolean;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
pollIntervalMs?: number;
|
|
24
|
+
/** 出站 bearer(A2A 认证;服务器为 signature 时匿名放行可省)。 */
|
|
25
|
+
bearerToken?: string;
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
}
|
|
28
|
+
export declare class A2AQuoteFetcher implements QuoteFetcher {
|
|
29
|
+
private readonly allowPrivateRanges;
|
|
30
|
+
private readonly skipDnsCheck;
|
|
31
|
+
private readonly timeoutMs;
|
|
32
|
+
private readonly pollIntervalMs;
|
|
33
|
+
private readonly bearerToken?;
|
|
34
|
+
private readonly fetchImpl;
|
|
35
|
+
private readonly endpointCache;
|
|
36
|
+
constructor(options?: A2AQuoteFetcherOptions);
|
|
37
|
+
/** 对每个 Merchant 独立发起真实 RFQ 并收集回复(部分失败语义)。 */
|
|
38
|
+
requestQuotes(intent: Record<string, unknown>, merchants: MerchantRecord[]): Promise<QuoteCandidateInput[]>;
|
|
39
|
+
private requestQuote;
|
|
40
|
+
/** 解析 agent card JSONRPC 端点(按 card URL 缓存;拒绝非 http(s))。 */
|
|
41
|
+
private resolveEndpoint;
|
|
42
|
+
/** 发送 RFQ 后轮询直到 merchant 回复 envelope(非 rfq action)或超时。 */
|
|
43
|
+
private waitForMerchantReply;
|
|
44
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
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
|
+
import { newNegotiationId } from "../negotiation/domain/identifiers.js";
|
|
17
|
+
import { buildA2AClient, buildRfqEnvelope, envelopeToMessage, extractKnpEnvelope, resolveA2aEndpoint, } from "./a2a-knp.js";
|
|
18
|
+
const DEFAULT_POLL_MS = 2000;
|
|
19
|
+
const DEFAULT_TIMEOUT_MS = 20_000;
|
|
20
|
+
function utcNow() {
|
|
21
|
+
return new Date().toISOString();
|
|
22
|
+
}
|
|
23
|
+
function sleep(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
function firstItem(intent) {
|
|
27
|
+
const items = Array.isArray(intent.items) ? intent.items : [];
|
|
28
|
+
return items[0] ?? {};
|
|
29
|
+
}
|
|
30
|
+
function firstSku(intent) {
|
|
31
|
+
const first = firstItem(intent);
|
|
32
|
+
if (typeof first.sku === "string" && first.sku !== "")
|
|
33
|
+
return first.sku;
|
|
34
|
+
if (typeof first.query === "string" && first.query !== "")
|
|
35
|
+
return first.query;
|
|
36
|
+
return "item-1";
|
|
37
|
+
}
|
|
38
|
+
function firstQuantity(intent) {
|
|
39
|
+
const qty = firstItem(intent).quantity;
|
|
40
|
+
if (typeof qty === "object" && qty !== null) {
|
|
41
|
+
const value = qty.value;
|
|
42
|
+
const unit = qty.unit;
|
|
43
|
+
if (typeof value === "number" && value > 0) {
|
|
44
|
+
return { value, ...(typeof unit === "string" && unit !== "" ? { unit } : {}) };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { value: 1 };
|
|
48
|
+
}
|
|
49
|
+
function constraintsDeadline(intent) {
|
|
50
|
+
const constraints = intent.constraints;
|
|
51
|
+
if (typeof constraints === "object" && constraints !== null) {
|
|
52
|
+
const deadline = constraints.deadline;
|
|
53
|
+
if (typeof deadline === "string" && deadline !== "")
|
|
54
|
+
return deadline;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
export class A2AQuoteFetcher {
|
|
59
|
+
allowPrivateRanges;
|
|
60
|
+
skipDnsCheck;
|
|
61
|
+
timeoutMs;
|
|
62
|
+
pollIntervalMs;
|
|
63
|
+
bearerToken;
|
|
64
|
+
fetchImpl;
|
|
65
|
+
endpointCache = new Map();
|
|
66
|
+
constructor(options = {}) {
|
|
67
|
+
this.allowPrivateRanges = options.allowPrivateRanges ?? false;
|
|
68
|
+
this.skipDnsCheck = options.skipDnsCheck ?? false;
|
|
69
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
70
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
71
|
+
this.bearerToken = options.bearerToken;
|
|
72
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
73
|
+
}
|
|
74
|
+
/** 对每个 Merchant 独立发起真实 RFQ 并收集回复(部分失败语义)。 */
|
|
75
|
+
async requestQuotes(intent, merchants) {
|
|
76
|
+
return Promise.all(merchants.map((merchant) => this.requestQuote(intent, merchant)));
|
|
77
|
+
}
|
|
78
|
+
async requestQuote(intent, merchant) {
|
|
79
|
+
const { merchant_id: merchantId, agent_card_url: agentCardUrl } = merchant;
|
|
80
|
+
if (agentCardUrl === undefined || agentCardUrl === "") {
|
|
81
|
+
return {
|
|
82
|
+
merchant_id: merchantId,
|
|
83
|
+
status: "failed",
|
|
84
|
+
failure: {
|
|
85
|
+
classification: "unreachable",
|
|
86
|
+
retryable: true,
|
|
87
|
+
detail: "merchant has no agent card URL",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const endpoint = await this.resolveEndpoint(agentCardUrl);
|
|
93
|
+
const sku = merchant.matching_skus?.[0] ?? firstSku(intent);
|
|
94
|
+
const negotiationId = newNegotiationId();
|
|
95
|
+
const envelope = buildRfqEnvelope({
|
|
96
|
+
negotiationId,
|
|
97
|
+
sku,
|
|
98
|
+
quantity: firstQuantity(intent),
|
|
99
|
+
deliveryBefore: constraintsDeadline(intent),
|
|
100
|
+
now: utcNow,
|
|
101
|
+
});
|
|
102
|
+
const client = buildA2AClient(endpoint, {
|
|
103
|
+
bearerToken: this.bearerToken,
|
|
104
|
+
allowPrivateRanges: this.allowPrivateRanges,
|
|
105
|
+
skipDnsCheck: this.skipDnsCheck,
|
|
106
|
+
timeoutMs: this.timeoutMs,
|
|
107
|
+
fetchImpl: this.fetchImpl,
|
|
108
|
+
});
|
|
109
|
+
const task = await client.sendMessage(envelopeToMessage(envelope));
|
|
110
|
+
const reply = await this.waitForMerchantReply(client, task);
|
|
111
|
+
if (reply === null) {
|
|
112
|
+
return {
|
|
113
|
+
merchant_id: merchantId,
|
|
114
|
+
status: "failed",
|
|
115
|
+
failure: {
|
|
116
|
+
classification: "timeout",
|
|
117
|
+
retryable: true,
|
|
118
|
+
detail: `no merchant reply within ${this.timeoutMs}ms`,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const offerId = typeof reply.payload === "object" && reply.payload !== null
|
|
123
|
+
? reply.payload.offer_id
|
|
124
|
+
: undefined;
|
|
125
|
+
return {
|
|
126
|
+
merchant_id: merchantId,
|
|
127
|
+
status: "succeeded",
|
|
128
|
+
provenance: {
|
|
129
|
+
...(typeof offerId === "string" && offerId !== "" ? { offer_id: offerId } : {}),
|
|
130
|
+
negotiation_id: negotiationId,
|
|
131
|
+
merchant_reply_id: reply.message_id,
|
|
132
|
+
source: "a2a",
|
|
133
|
+
reply_text: JSON.stringify(reply),
|
|
134
|
+
sku,
|
|
135
|
+
a2a_endpoint: endpoint,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return {
|
|
141
|
+
merchant_id: merchantId,
|
|
142
|
+
status: "failed",
|
|
143
|
+
failure: {
|
|
144
|
+
classification: "protocol_error",
|
|
145
|
+
retryable: true,
|
|
146
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** 解析 agent card JSONRPC 端点(按 card URL 缓存;拒绝非 http(s))。 */
|
|
152
|
+
async resolveEndpoint(agentCardUrl) {
|
|
153
|
+
const cached = this.endpointCache.get(agentCardUrl);
|
|
154
|
+
if (cached !== undefined)
|
|
155
|
+
return cached;
|
|
156
|
+
const endpoint = await resolveA2aEndpoint(this.fetchImpl, agentCardUrl, this.timeoutMs);
|
|
157
|
+
this.endpointCache.set(agentCardUrl, endpoint);
|
|
158
|
+
return endpoint;
|
|
159
|
+
}
|
|
160
|
+
/** 发送 RFQ 后轮询直到 merchant 回复 envelope(非 rfq action)或超时。 */
|
|
161
|
+
async waitForMerchantReply(client, initial) {
|
|
162
|
+
const direct = extractKnpEnvelope(initial);
|
|
163
|
+
if (direct !== null && direct.action !== "rfq")
|
|
164
|
+
return direct;
|
|
165
|
+
const deadline = Date.now() + this.timeoutMs;
|
|
166
|
+
let task = initial;
|
|
167
|
+
for (;;) {
|
|
168
|
+
await sleep(this.pollIntervalMs);
|
|
169
|
+
task = await client.getTask(task.id);
|
|
170
|
+
const envelope = extractKnpEnvelope(task);
|
|
171
|
+
if (envelope !== null && envelope.action !== "rfq")
|
|
172
|
+
return envelope;
|
|
173
|
+
if (Date.now() >= deadline)
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=a2a-quote-fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-quote-fetcher.js","sourceRoot":"","sources":["../../src/buyer-core/a2a-quote-fetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
import { KiwiBuyerService } from "./service.js";
|
|
17
|
+
export interface BuyerServiceConfig {
|
|
18
|
+
dbPath: string;
|
|
19
|
+
principal: string;
|
|
20
|
+
buyerAgentId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
policy: Record<string, unknown>;
|
|
23
|
+
catalogUrl?: string;
|
|
24
|
+
marketplaceUrl?: string;
|
|
25
|
+
buyerBootstrapToken?: string;
|
|
26
|
+
/** A2A 出站 bearer(服务器为 signature 认证时匿名放行可省)。 */
|
|
27
|
+
a2aBearerToken?: string;
|
|
28
|
+
/** A2A 允许打到私网/保留网段(SSRF 逃生门;本地试点直连时开)。 */
|
|
29
|
+
a2aAllowPrivateRanges?: boolean;
|
|
30
|
+
/** A2A 跳过 DNS 保留网段复查(测试/本机直连)。 */
|
|
31
|
+
a2aSkipDnsCheck?: boolean;
|
|
32
|
+
/** A2A 单请求超时 ms。 */
|
|
33
|
+
a2aTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 构建 Buyer Core:store + merchant index + quote fetcher + negotiator。
|
|
37
|
+
*
|
|
38
|
+
* 执行 seam 接线(战略 v2.5 Phase 2):
|
|
39
|
+
* - marketplaceUrl 优先(试点向后兼容)→ MarketplaceMerchantIndex +
|
|
40
|
+
* MarketplaceQuoteFetcher + MarketplaceNegotiator(shopping-cli 直连)。
|
|
41
|
+
* - 否则 catalogUrl → KiwiCatalogMerchantIndex(listings 感知发现)+ A2AQuoteFetcher
|
|
42
|
+
* + A2ANegotiator(catalog 发现 → A2A 直连 merchant 磋商)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildBuyerService(config: BuyerServiceConfig): KiwiBuyerService;
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
* 共享 Buyer Core 构建器(战略 v2.5 §6.3 单核心多包装)。
|
|
18
|
+
*
|
|
19
|
+
* MCP / HTTP / CLI / SDK 等包装都调用同一个 buildBuyerService:注入 merchant
|
|
20
|
+
* index、quote fetcher、negotiator 与持久 store,返回 KiwiBuyerService。
|
|
21
|
+
* 包装层只负责 manifest/tool description/路由,业务判断全部回到 buyer-core。
|
|
22
|
+
*/
|
|
23
|
+
import { assertNorthboundContractValid } from "../contracts/northbound-schema.js";
|
|
24
|
+
import { A2ANegotiator } from "./a2a-negotiator.js";
|
|
25
|
+
import { A2AQuoteFetcher } from "./a2a-quote-fetcher.js";
|
|
26
|
+
import { KiwiCatalogMerchantIndex, MarketplaceMerchantIndex } from "./merchant-index.js";
|
|
27
|
+
import { MarketplaceNegotiator } from "./negotiator.js";
|
|
28
|
+
import { MarketplaceQuoteFetcher } from "./quote-fetcher.js";
|
|
29
|
+
import { KiwiBuyerService } from "./service.js";
|
|
30
|
+
import { TaskApprovalStore } from "./store.js";
|
|
31
|
+
/**
|
|
32
|
+
* 构建 Buyer Core:store + merchant index + quote fetcher + negotiator。
|
|
33
|
+
*
|
|
34
|
+
* 执行 seam 接线(战略 v2.5 Phase 2):
|
|
35
|
+
* - marketplaceUrl 优先(试点向后兼容)→ MarketplaceMerchantIndex +
|
|
36
|
+
* MarketplaceQuoteFetcher + MarketplaceNegotiator(shopping-cli 直连)。
|
|
37
|
+
* - 否则 catalogUrl → KiwiCatalogMerchantIndex(listings 感知发现)+ A2AQuoteFetcher
|
|
38
|
+
* + A2ANegotiator(catalog 发现 → A2A 直连 merchant 磋商)。
|
|
39
|
+
*/
|
|
40
|
+
export function buildBuyerService(config) {
|
|
41
|
+
assertNorthboundContractValid("delegation-policy", config.policy, "delegation policy");
|
|
42
|
+
const store = new TaskApprovalStore({ dbPath: config.dbPath });
|
|
43
|
+
let merchantIndex;
|
|
44
|
+
let quoteFetcher;
|
|
45
|
+
let negotiator;
|
|
46
|
+
if (config.marketplaceUrl !== undefined) {
|
|
47
|
+
merchantIndex = new MarketplaceMerchantIndex({ baseUrl: config.marketplaceUrl });
|
|
48
|
+
quoteFetcher = new MarketplaceQuoteFetcher({
|
|
49
|
+
baseUrl: config.marketplaceUrl,
|
|
50
|
+
buyerBootstrapToken: config.buyerBootstrapToken ?? "",
|
|
51
|
+
});
|
|
52
|
+
negotiator = new MarketplaceNegotiator({ baseUrl: config.marketplaceUrl });
|
|
53
|
+
}
|
|
54
|
+
else if (config.catalogUrl !== undefined) {
|
|
55
|
+
merchantIndex = new KiwiCatalogMerchantIndex({ baseUrl: config.catalogUrl });
|
|
56
|
+
const a2a = {
|
|
57
|
+
bearerToken: config.a2aBearerToken,
|
|
58
|
+
allowPrivateRanges: config.a2aAllowPrivateRanges,
|
|
59
|
+
skipDnsCheck: config.a2aSkipDnsCheck,
|
|
60
|
+
timeoutMs: config.a2aTimeoutMs,
|
|
61
|
+
};
|
|
62
|
+
quoteFetcher = new A2AQuoteFetcher(a2a);
|
|
63
|
+
negotiator = new A2ANegotiator(a2a);
|
|
64
|
+
}
|
|
65
|
+
return new KiwiBuyerService({
|
|
66
|
+
store,
|
|
67
|
+
principal: config.principal,
|
|
68
|
+
buyerAgentId: config.buyerAgentId,
|
|
69
|
+
sessionId: config.sessionId,
|
|
70
|
+
delegationPolicy: config.policy,
|
|
71
|
+
...(merchantIndex !== undefined ? { merchantIndex } : {}),
|
|
72
|
+
...(quoteFetcher !== undefined ? { quoteFetcher } : {}),
|
|
73
|
+
...(negotiator !== undefined ? { negotiator } : {}),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=build-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-service.js","sourceRoot":"","sources":["../../src/buyer-core/build-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;GAMG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAqB/C;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA0B;IAC1D,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,aAAa,CAAC;IAClB,IAAI,YAAY,CAAC;IACjB,IAAI,UAAU,CAAC;IACf,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACxC,aAAa,GAAG,IAAI,wBAAwB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QACjF,YAAY,GAAG,IAAI,uBAAuB,CAAC;YACzC,OAAO,EAAE,MAAM,CAAC,cAAc;YAC9B,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,IAAI,EAAE;SACtD,CAAC,CAAC;QACH,UAAU,GAAG,IAAI,qBAAqB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IAC7E,CAAC;SAAM,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3C,aAAa,GAAG,IAAI,wBAAwB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7E,MAAM,GAAG,GAAG;YACV,WAAW,EAAE,MAAM,CAAC,cAAc;YAClC,kBAAkB,EAAE,MAAM,CAAC,qBAAqB;YAChD,YAAY,EAAE,MAAM,CAAC,eAAe;YACpC,SAAS,EAAE,MAAM,CAAC,YAAY;SAC/B,CAAC;QACF,YAAY,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;QACxC,UAAU,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,gBAAgB,CAAC;QAC1B,KAAK;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM;QAC/B,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
* kiwi-buyer-mcp 类型化错误(fail-closed,同仓库 `*Error` + code 约定)。
|
|
18
|
+
* 错误码在跨宿主 Compatibility Matrix(§6.10)里作为语义不变量被验收。
|
|
19
|
+
*/
|
|
20
|
+
export type McpErrorCode = "invalid_request" | "invalid_params" | "method_not_found" | "not_initialized" | "tool_not_found" | "contract_violation" | "idempotency_conflict" | "delegation_denied" | "authorization_denied" | "approval_required" | "approval_denied" | "task_not_found" | "task_expired" | "agreement_not_found" | "store_corrupted" | "internal_error";
|
|
21
|
+
export declare class McpError extends Error {
|
|
22
|
+
readonly code: McpErrorCode;
|
|
23
|
+
readonly detail?: unknown;
|
|
24
|
+
constructor(code: McpErrorCode, message: string, detail?: unknown);
|
|
25
|
+
/** 映射为 JSON-RPC 错误负载(§6.10 错误分类语义)。 */
|
|
26
|
+
toJsonRpc(): {
|
|
27
|
+
code: number;
|
|
28
|
+
message: string;
|
|
29
|
+
data: unknown;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
const CODE_TO_JSON_RPC = {
|
|
17
|
+
invalid_request: -32600,
|
|
18
|
+
invalid_params: -32602,
|
|
19
|
+
method_not_found: -32601,
|
|
20
|
+
not_initialized: -32002,
|
|
21
|
+
tool_not_found: -32002,
|
|
22
|
+
contract_violation: -32602,
|
|
23
|
+
idempotency_conflict: -32602,
|
|
24
|
+
delegation_denied: -32000,
|
|
25
|
+
authorization_denied: -32000,
|
|
26
|
+
approval_required: -32000,
|
|
27
|
+
approval_denied: -32000,
|
|
28
|
+
task_not_found: -32602,
|
|
29
|
+
task_expired: -32000,
|
|
30
|
+
agreement_not_found: -32602,
|
|
31
|
+
store_corrupted: -32603,
|
|
32
|
+
internal_error: -32603,
|
|
33
|
+
};
|
|
34
|
+
export class McpError extends Error {
|
|
35
|
+
code;
|
|
36
|
+
detail;
|
|
37
|
+
constructor(code, message, detail) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "McpError";
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.detail = detail;
|
|
42
|
+
}
|
|
43
|
+
/** 映射为 JSON-RPC 错误负载(§6.10 错误分类语义)。 */
|
|
44
|
+
toJsonRpc() {
|
|
45
|
+
return {
|
|
46
|
+
code: CODE_TO_JSON_RPC[this.code],
|
|
47
|
+
message: `${this.code}: ${this.message}`,
|
|
48
|
+
data: this.detail,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/buyer-core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAyBH,MAAM,gBAAgB,GAAiC;IACrD,eAAe,EAAE,CAAC,KAAK;IACvB,cAAc,EAAE,CAAC,KAAK;IACtB,gBAAgB,EAAE,CAAC,KAAK;IACxB,eAAe,EAAE,CAAC,KAAK;IACvB,cAAc,EAAE,CAAC,KAAK;IACtB,kBAAkB,EAAE,CAAC,KAAK;IAC1B,oBAAoB,EAAE,CAAC,KAAK;IAC5B,iBAAiB,EAAE,CAAC,KAAK;IACzB,oBAAoB,EAAE,CAAC,KAAK;IAC5B,iBAAiB,EAAE,CAAC,KAAK;IACzB,eAAe,EAAE,CAAC,KAAK;IACvB,cAAc,EAAE,CAAC,KAAK;IACtB,YAAY,EAAE,CAAC,KAAK;IACpB,mBAAmB,EAAE,CAAC,KAAK;IAC3B,eAAe,EAAE,CAAC,KAAK;IACvB,cAAc,EAAE,CAAC,KAAK;CACvB,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAe;IACnB,MAAM,CAAW;IAE1B,YAAY,IAAkB,EAAE,OAAe,EAAE,MAAgB;QAC/D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,uCAAuC;IACvC,SAAS;QACP,OAAO;YACL,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,EAAE;YACxC,IAAI,EAAE,IAAI,CAAC,MAAM;SAClB,CAAC;IACJ,CAAC;CACF"}
|