@harrylabsj/kiwi 0.7.22 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -10
- package/dist/a2a/negotiate.d.ts +6 -2
- package/dist/a2a/negotiate.js +10 -3
- package/dist/a2a/negotiate.js.map +1 -1
- package/dist/a2a/node.d.ts +4 -1
- package/dist/a2a/node.js +57 -4
- package/dist/a2a/node.js.map +1 -1
- package/dist/a2a/server/merchant-handler.d.ts +11 -3
- package/dist/a2a/server/merchant-handler.js +84 -26
- package/dist/a2a/server/merchant-handler.js.map +1 -1
- package/dist/agent/buyer/buyer-tools.js +17 -10
- package/dist/agent/buyer/buyer-tools.js.map +1 -1
- package/dist/agent/buyer/scheduler.d.ts +1 -0
- package/dist/agent/buyer/scheduler.js +5 -1
- package/dist/agent/buyer/scheduler.js.map +1 -1
- package/dist/agent/buyer/task-store.js +5 -2
- package/dist/agent/buyer/task-store.js.map +1 -1
- package/dist/agent/chat-tools.js +10 -3
- package/dist/agent/chat-tools.js.map +1 -1
- package/dist/agent/context/fencing.d.ts +27 -0
- package/dist/agent/context/fencing.js +126 -0
- package/dist/agent/context/fencing.js.map +1 -0
- package/dist/agent/context/grounding.d.ts +18 -0
- package/dist/agent/context/grounding.js +22 -0
- package/dist/agent/context/grounding.js.map +1 -0
- package/dist/agent/host/events.d.ts +31 -0
- package/dist/agent/host/events.js +62 -0
- package/dist/agent/host/events.js.map +1 -0
- package/dist/agent/host/tui-renderer.d.ts +17 -0
- package/dist/agent/host/tui-renderer.js +125 -0
- package/dist/agent/host/tui-renderer.js.map +1 -0
- package/dist/agent/host/web-renderer.d.ts +71 -0
- package/dist/agent/host/web-renderer.js +185 -0
- package/dist/agent/host/web-renderer.js.map +1 -0
- package/dist/agent/kernel-builder.d.ts +3 -1
- package/dist/agent/kernel-builder.js +4 -1
- package/dist/agent/kernel-builder.js.map +1 -1
- package/dist/agent/kernel.d.ts +20 -1
- package/dist/agent/kernel.js +255 -14
- package/dist/agent/kernel.js.map +1 -1
- package/dist/agent/memory/schema.d.ts +1 -1
- package/dist/agent/memory/schema.js +9 -1
- package/dist/agent/memory/schema.js.map +1 -1
- package/dist/agent/merchant/action-candidate.d.ts +18 -0
- package/dist/agent/merchant/action-candidate.js +51 -0
- package/dist/agent/merchant/action-candidate.js.map +1 -1
- package/dist/agent/merchant/fake-merchant-client.js +6 -1
- package/dist/agent/merchant/fake-merchant-client.js.map +1 -1
- package/dist/agent/merchant/intelligence/backend.d.ts +40 -0
- package/dist/agent/merchant/intelligence/backend.js +2 -0
- package/dist/agent/merchant/intelligence/backend.js.map +1 -0
- package/dist/agent/merchant/intelligence/default-backend.d.ts +38 -0
- package/dist/agent/merchant/intelligence/default-backend.js +477 -0
- package/dist/agent/merchant/intelligence/default-backend.js.map +1 -0
- package/dist/agent/merchant/intelligence/http-analytics-source.d.ts +55 -0
- package/dist/agent/merchant/intelligence/http-analytics-source.js +193 -0
- package/dist/agent/merchant/intelligence/http-analytics-source.js.map +1 -0
- package/dist/agent/merchant/intelligence/types.d.ts +109 -0
- package/dist/agent/merchant/intelligence/types.js +2 -0
- package/dist/agent/merchant/intelligence/types.js.map +1 -0
- package/dist/agent/merchant/merchant-client.d.ts +50 -1
- package/dist/agent/merchant/merchant-client.js +157 -8
- package/dist/agent/merchant/merchant-client.js.map +1 -1
- package/dist/agent/merchant/merchant-enrichment.d.ts +10 -0
- package/dist/agent/merchant/merchant-enrichment.js +38 -0
- package/dist/agent/merchant/merchant-enrichment.js.map +1 -0
- package/dist/agent/merchant/merchant-grounding.d.ts +5 -0
- package/dist/agent/merchant/merchant-grounding.js +76 -0
- package/dist/agent/merchant/merchant-grounding.js.map +1 -0
- package/dist/agent/merchant/merchant-presentations.d.ts +8 -0
- package/dist/agent/merchant/merchant-presentations.js +207 -0
- package/dist/agent/merchant/merchant-presentations.js.map +1 -0
- package/dist/agent/merchant/merchant-tools.d.ts +8 -0
- package/dist/agent/merchant/merchant-tools.js +291 -178
- package/dist/agent/merchant/merchant-tools.js.map +1 -1
- package/dist/agent/merchant/types.d.ts +6 -2
- package/dist/agent/merchant/types.js +45 -0
- package/dist/agent/merchant/types.js.map +1 -1
- package/dist/agent/presentation/registry.d.ts +14 -0
- package/dist/agent/presentation/registry.js +24 -0
- package/dist/agent/presentation/registry.js.map +1 -0
- package/dist/agent/presentation/runner.d.ts +11 -0
- package/dist/agent/presentation/runner.js +17 -0
- package/dist/agent/presentation/runner.js.map +1 -0
- package/dist/agent/presentation/sanitize.d.ts +16 -0
- package/dist/agent/presentation/sanitize.js +37 -0
- package/dist/agent/presentation/sanitize.js.map +1 -0
- package/dist/agent/presentation/types.d.ts +28 -0
- package/dist/agent/presentation/types.js +8 -0
- package/dist/agent/presentation/types.js.map +1 -0
- package/dist/agent/session.d.ts +1 -1
- package/dist/agent/session.js +2 -2
- package/dist/agent/session.js.map +1 -1
- package/dist/agent/skills/registry.d.ts +11 -0
- package/dist/agent/skills/registry.js +119 -0
- package/dist/agent/skills/registry.js.map +1 -0
- package/dist/agent/skills/tools.d.ts +6 -0
- package/dist/agent/skills/tools.js +36 -0
- package/dist/agent/skills/tools.js.map +1 -0
- package/dist/agent/skills/types.d.ts +9 -0
- package/dist/agent/skills/types.js +2 -0
- package/dist/agent/skills/types.js.map +1 -0
- package/dist/agent/supplier/scheduler.d.ts +2 -0
- package/dist/agent/supplier/scheduler.js +101 -22
- package/dist/agent/supplier/scheduler.js.map +1 -1
- package/dist/agent/supplier/store.d.ts +2 -0
- package/dist/agent/supplier/store.js +44 -0
- package/dist/agent/supplier/store.js.map +1 -1
- package/dist/agent/system-prompt.d.ts +5 -1
- package/dist/agent/system-prompt.js +20 -3
- package/dist/agent/system-prompt.js.map +1 -1
- package/dist/auth/merchant-authorization.d.ts +49 -0
- package/dist/auth/merchant-authorization.js +61 -0
- package/dist/auth/merchant-authorization.js.map +1 -0
- package/dist/auth/merchant-oauth.d.ts +255 -0
- package/dist/auth/merchant-oauth.js +764 -0
- package/dist/auth/merchant-oauth.js.map +1 -0
- package/dist/auth/merchant-pairing.d.ts +62 -0
- package/dist/auth/merchant-pairing.js +199 -0
- package/dist/auth/merchant-pairing.js.map +1 -0
- package/dist/auth/merchant-sessions.d.ts +71 -0
- package/dist/auth/merchant-sessions.js +168 -0
- package/dist/auth/merchant-sessions.js.map +1 -0
- package/dist/buyer-core/build-service.d.ts +5 -0
- package/dist/buyer-core/build-service.js +12 -0
- package/dist/buyer-core/build-service.js.map +1 -1
- package/dist/buyer-core/errors.d.ts +1 -1
- package/dist/buyer-core/errors.js +2 -0
- package/dist/buyer-core/errors.js.map +1 -1
- package/dist/buyer-core/merchant-index.d.ts +19 -4
- package/dist/buyer-core/merchant-index.js +137 -16
- package/dist/buyer-core/merchant-index.js.map +1 -1
- package/dist/buyer-core/negotiator.d.ts +0 -10
- package/dist/buyer-core/negotiator.js +12 -1
- package/dist/buyer-core/negotiator.js.map +1 -1
- package/dist/buyer-core/quote-fetcher.js +2 -1
- package/dist/buyer-core/quote-fetcher.js.map +1 -1
- package/dist/buyer-core/service.d.ts +104 -0
- package/dist/buyer-core/service.js +105 -1
- package/dist/buyer-core/service.js.map +1 -1
- package/dist/cli.d.ts +4 -2
- package/dist/cli.js +644 -32
- package/dist/cli.js.map +1 -1
- package/dist/config/profile.d.ts +51 -0
- package/dist/config/profile.js +185 -61
- package/dist/config/profile.js.map +1 -1
- package/dist/demo/demo-runner.js +12 -9
- package/dist/demo/demo-runner.js.map +1 -1
- package/dist/discovery/catalog-source/buyer-follows.d.ts +82 -0
- package/dist/discovery/catalog-source/buyer-follows.js +228 -0
- package/dist/discovery/catalog-source/buyer-follows.js.map +1 -0
- package/dist/discovery/catalog-source/connector-identity.d.ts +75 -0
- package/dist/discovery/catalog-source/connector-identity.js +175 -0
- package/dist/discovery/catalog-source/connector-identity.js.map +1 -0
- package/dist/discovery/catalog-source/errors.d.ts +1 -1
- package/dist/discovery/catalog-source/errors.js +2 -0
- package/dist/discovery/catalog-source/errors.js.map +1 -1
- package/dist/discovery/catalog-source/kiwi-record.d.ts +2 -0
- package/dist/discovery/catalog-source/kiwi-record.js.map +1 -1
- package/dist/discovery/catalog-source/kiwi-source.js +2 -0
- package/dist/discovery/catalog-source/kiwi-source.js.map +1 -1
- package/dist/discovery/catalog-source/merchant-publications.d.ts +65 -0
- package/dist/discovery/catalog-source/merchant-publications.js +207 -0
- package/dist/discovery/catalog-source/merchant-publications.js.map +1 -0
- package/dist/discovery/catalog-source/register.d.ts +24 -0
- package/dist/discovery/catalog-source/register.js +46 -0
- package/dist/discovery/catalog-source/register.js.map +1 -1
- package/dist/discovery/catalog-source/source.js +2 -1
- package/dist/discovery/catalog-source/source.js.map +1 -1
- package/dist/fs/atomic-write.d.ts +18 -0
- package/dist/fs/atomic-write.js +29 -0
- package/dist/fs/atomic-write.js.map +1 -0
- package/dist/handoff/authorization.js +1 -1
- package/dist/http/merchant-server.d.ts +65 -0
- package/dist/http/merchant-server.js +346 -0
- package/dist/http/merchant-server.js.map +1 -0
- package/dist/http/server.js +4 -1
- package/dist/http/server.js.map +1 -1
- package/dist/mcp/cli.d.ts +2 -0
- package/dist/mcp/cli.js +7 -6
- package/dist/mcp/cli.js.map +1 -1
- package/dist/mcp/merchant-auth.d.ts +78 -0
- package/dist/mcp/merchant-auth.js +146 -0
- package/dist/mcp/merchant-auth.js.map +1 -0
- package/dist/mcp/merchant-dirs.d.ts +26 -0
- package/dist/mcp/merchant-dirs.js +44 -0
- package/dist/mcp/merchant-dirs.js.map +1 -0
- package/dist/mcp/merchant-resources.d.ts +38 -0
- package/dist/mcp/merchant-resources.js +146 -0
- package/dist/mcp/merchant-resources.js.map +1 -0
- package/dist/mcp/merchant-server.d.ts +106 -0
- package/dist/mcp/merchant-server.js +615 -0
- package/dist/mcp/merchant-server.js.map +1 -0
- package/dist/mcp/merchant-tools.d.ts +103 -0
- package/dist/mcp/merchant-tools.js +517 -0
- package/dist/mcp/merchant-tools.js.map +1 -0
- package/dist/mcp/server.js +4 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +5 -3
- package/dist/mcp/tools.js +102 -5
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts +1 -1
- package/dist/mcp/types.js +4 -0
- package/dist/mcp/types.js.map +1 -1
- package/dist/merchant/ops.d.ts +0 -11
- package/dist/merchant/ops.js +13 -1
- package/dist/merchant/ops.js.map +1 -1
- package/dist/merchant/stats-store.d.ts +7 -0
- package/dist/merchant/stats-store.js +11 -0
- package/dist/merchant/stats-store.js.map +1 -1
- package/dist/merchant/workbench-service.d.ts +189 -0
- package/dist/merchant/workbench-service.js +417 -0
- package/dist/merchant/workbench-service.js.map +1 -0
- package/dist/merchant-admin/pending-page.d.ts +35 -0
- package/dist/merchant-admin/pending-page.js +69 -0
- package/dist/merchant-admin/pending-page.js.map +1 -0
- package/dist/merchant-core/commands.d.ts +99 -0
- package/dist/merchant-core/commands.js +179 -0
- package/dist/merchant-core/commands.js.map +1 -0
- package/dist/merchant-core/executor.d.ts +69 -0
- package/dist/merchant-core/executor.js +218 -0
- package/dist/merchant-core/executor.js.map +1 -0
- package/dist/merchant-core/negotiation-adapters.d.ts +55 -0
- package/dist/merchant-core/negotiation-adapters.js +55 -0
- package/dist/merchant-core/negotiation-adapters.js.map +1 -0
- package/dist/merchant-core/network-checks.d.ts +65 -0
- package/dist/merchant-core/network-checks.js +111 -0
- package/dist/merchant-core/network-checks.js.map +1 -0
- package/dist/merchant-core/operations.d.ts +64 -0
- package/dist/merchant-core/operations.js +136 -0
- package/dist/merchant-core/operations.js.map +1 -0
- package/dist/merchant-core/policy-runtime.d.ts +70 -0
- package/dist/merchant-core/policy-runtime.js +181 -0
- package/dist/merchant-core/policy-runtime.js.map +1 -0
- package/dist/merchant-core/product-import.d.ts +67 -0
- package/dist/merchant-core/product-import.js +160 -0
- package/dist/merchant-core/product-import.js.map +1 -0
- package/dist/merchant-core/service.d.ts +203 -0
- package/dist/merchant-core/service.js +402 -0
- package/dist/merchant-core/service.js.map +1 -0
- package/dist/merchant-gateway/catalog-publications.d.ts +71 -0
- package/dist/merchant-gateway/catalog-publications.js +175 -0
- package/dist/merchant-gateway/catalog-publications.js.map +1 -0
- package/dist/merchant-gateway/catalog-tools.d.ts +46 -0
- package/dist/merchant-gateway/catalog-tools.js +277 -0
- package/dist/merchant-gateway/catalog-tools.js.map +1 -0
- package/dist/merchant-gateway/cli.d.ts +75 -0
- package/dist/merchant-gateway/cli.js +458 -0
- package/dist/merchant-gateway/cli.js.map +1 -0
- package/dist/merchant-gateway/credential-vault.d.ts +44 -0
- package/dist/merchant-gateway/credential-vault.js +111 -0
- package/dist/merchant-gateway/credential-vault.js.map +1 -0
- package/dist/merchant-gateway/entry-server.d.ts +117 -0
- package/dist/merchant-gateway/entry-server.js +710 -0
- package/dist/merchant-gateway/entry-server.js.map +1 -0
- package/dist/merchant-gateway/instance-registration.d.ts +143 -0
- package/dist/merchant-gateway/instance-registration.js +382 -0
- package/dist/merchant-gateway/instance-registration.js.map +1 -0
- package/dist/merchant-gateway/instance-tools.d.ts +58 -0
- package/dist/merchant-gateway/instance-tools.js +184 -0
- package/dist/merchant-gateway/instance-tools.js.map +1 -0
- package/dist/merchant-gateway/mcp-proxy.d.ts +25 -0
- package/dist/merchant-gateway/mcp-proxy.js +192 -0
- package/dist/merchant-gateway/mcp-proxy.js.map +1 -0
- package/dist/merchant-gateway/pinned-fetch.d.ts +52 -0
- package/dist/merchant-gateway/pinned-fetch.js +212 -0
- package/dist/merchant-gateway/pinned-fetch.js.map +1 -0
- package/dist/merchant-gateway/tenant-registry.d.ts +82 -0
- package/dist/merchant-gateway/tenant-registry.js +190 -0
- package/dist/merchant-gateway/tenant-registry.js.map +1 -0
- package/dist/merchant-gateway/tool-bundle.d.ts +26 -0
- package/dist/merchant-gateway/tool-bundle.js +56 -0
- package/dist/merchant-gateway/tool-bundle.js.map +1 -0
- package/dist/merchant-runtime/backup.d.ts +48 -0
- package/dist/merchant-runtime/backup.js +226 -0
- package/dist/merchant-runtime/backup.js.map +1 -0
- package/dist/merchant-runtime/health.d.ts +99 -0
- package/dist/merchant-runtime/health.js +206 -0
- package/dist/merchant-runtime/health.js.map +1 -0
- package/dist/merchant-runtime/jobs.d.ts +64 -0
- package/dist/merchant-runtime/jobs.js +107 -0
- package/dist/merchant-runtime/jobs.js.map +1 -0
- package/dist/merchant-runtime/manager.d.ts +94 -0
- package/dist/merchant-runtime/manager.js +305 -0
- package/dist/merchant-runtime/manager.js.map +1 -0
- package/dist/merchant-runtime/services.d.ts +30 -0
- package/dist/merchant-runtime/services.js +51 -0
- package/dist/merchant-runtime/services.js.map +1 -0
- package/dist/negotiation/action-candidate.js +1 -1
- package/dist/net/url.d.ts +26 -0
- package/dist/net/url.js +32 -0
- package/dist/net/url.js.map +1 -0
- package/dist/operator/types.d.ts +1 -1
- package/dist/product-cli.d.ts +2 -2
- package/dist/product-cli.js +27 -6
- package/dist/product-cli.js.map +1 -1
- package/dist/product-compat.d.ts +9 -1
- package/dist/product-compat.js +12 -1
- package/dist/product-compat.js.map +1 -1
- package/dist/product-init.js +17 -5
- package/dist/product-init.js.map +1 -1
- package/dist/product-publish.js +2 -1
- package/dist/product-publish.js.map +1 -1
- package/dist/product-supplier.d.ts +73 -0
- package/dist/product-supplier.js +224 -5
- package/dist/product-supplier.js.map +1 -1
- package/dist/runtime/fake-model.d.ts +1 -1
- package/dist/trust/identity/jwk.d.ts +11 -0
- package/dist/trust/identity/jwk.js +2 -0
- package/dist/trust/identity/jwk.js.map +1 -0
- package/dist/trust/identity/jws.d.ts +2 -1
- package/dist/trust/identity/jws.js.map +1 -1
- package/dist/trust/identity/keys.d.ts +2 -1
- package/dist/trust/identity/keys.js.map +1 -1
- package/dist/trust/identity/message-signature.d.ts +1 -14
- package/dist/trust/identity/message-signature.js.map +1 -1
- package/dist/weixin/channel.d.ts +3 -0
- package/dist/weixin/channel.js +18 -2
- package/dist/weixin/channel.js.map +1 -1
- package/examples/profiles/merchant.deepseek.yaml +6 -0
- package/examples/profiles/merchant.fake.yaml +6 -0
- package/examples/profiles/merchant.local.yaml +6 -0
- package/package.json +9 -7
- package/skills/kiwi-buyer/SKILL.md +68 -0
- package/skills/merchant/catalog-operations/SKILL.md +17 -0
- package/skills/merchant/change-approval/SKILL.md +16 -0
- package/skills/merchant/human-review/SKILL.md +17 -0
- package/skills/merchant/inventory-operations/SKILL.md +17 -0
- package/skills/merchant/negotiation-review/SKILL.md +18 -0
- package/skills/merchant/performance-insights/SKILL.md +19 -0
- package/contracts/candidate-agent-dto-1.0.README.md +0 -43
- package/contracts/conformance/python-service/README.md +0 -19
|
@@ -0,0 +1,82 @@
|
|
|
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 { CatalogSourceDeps } from "./source.js";
|
|
17
|
+
export interface BuyerFollowsSourceDeps extends CatalogSourceDeps {
|
|
18
|
+
/** catalog 账号会话 token(作为 cookie kiwi_session 发送)。 */
|
|
19
|
+
sessionToken?: string;
|
|
20
|
+
}
|
|
21
|
+
/** 买家视角的关注视图(上游 follow_view;不含 buyer_subject——响应即买家本人)。 */
|
|
22
|
+
export interface BuyerFollowRecord {
|
|
23
|
+
readonly merchant_id: string;
|
|
24
|
+
readonly merchant_name?: string;
|
|
25
|
+
readonly category?: string;
|
|
26
|
+
readonly status: string;
|
|
27
|
+
readonly consent_version?: string;
|
|
28
|
+
readonly created_at?: string;
|
|
29
|
+
readonly last_seen_at?: string;
|
|
30
|
+
}
|
|
31
|
+
/** 商家公开事件类型(上游 EVENT_TYPES;新增类型上游可扩,运行时按非空字符串容忍)。 */
|
|
32
|
+
export type MerchantPublicEventType = "product_added" | "product_updated" | "faq_updated" | "service_notice" | "publication_withdrawn";
|
|
33
|
+
/** 商家公开事件(只含公开字段;payload 即 M0 公开投影)。 */
|
|
34
|
+
export interface MerchantPublicEvent {
|
|
35
|
+
readonly event_id: string;
|
|
36
|
+
readonly event_type: MerchantPublicEventType | string;
|
|
37
|
+
readonly publication_id?: string;
|
|
38
|
+
readonly version?: number;
|
|
39
|
+
readonly payload: Record<string, unknown>;
|
|
40
|
+
readonly created_at: string;
|
|
41
|
+
}
|
|
42
|
+
/** 一个商家的增量更新分组(updates 元素;last_seen_at 为本次推进后的水位)。 */
|
|
43
|
+
export interface BuyerFollowUpdateGroup {
|
|
44
|
+
readonly merchant_id: string;
|
|
45
|
+
readonly merchant_name?: string;
|
|
46
|
+
readonly events: MerchantPublicEvent[];
|
|
47
|
+
readonly last_seen_at?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function validateBuyerFollow(value: unknown): BuyerFollowRecord;
|
|
50
|
+
export declare function validateMerchantPublicEvent(value: unknown): MerchantPublicEvent;
|
|
51
|
+
export declare function validateFollowUpdateGroup(value: unknown): BuyerFollowUpdateGroup;
|
|
52
|
+
export declare class BuyerFollowsSource {
|
|
53
|
+
private readonly baseUrl;
|
|
54
|
+
private readonly deps;
|
|
55
|
+
constructor(deps: BuyerFollowsSourceDeps);
|
|
56
|
+
/**
|
|
57
|
+
* 核心请求:会话认证(cookie kiwi_session);超时 / HTTP / 网络失败统一映射
|
|
58
|
+
* 为 CatalogSourceError;HTTP 401/403 → session_rejected(fail-closed,不伪造
|
|
59
|
+
* 身份、不静默降级为匿名)。
|
|
60
|
+
*/
|
|
61
|
+
private request;
|
|
62
|
+
/** 显式关注(幂等;可选 category/consent_version)。仅买家主动调用构成订阅。 */
|
|
63
|
+
follow(merchantId: string, opts?: {
|
|
64
|
+
category?: string;
|
|
65
|
+
consent_version?: string;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
follow: BuyerFollowRecord;
|
|
68
|
+
created: boolean;
|
|
69
|
+
}>;
|
|
70
|
+
/** 取消关注(幂等;无活跃关注同样 ok)。 */
|
|
71
|
+
unfollow(merchantId: string): Promise<{
|
|
72
|
+
merchant_id: string;
|
|
73
|
+
following: false;
|
|
74
|
+
}>;
|
|
75
|
+
/** 我的活跃关注列表(cancelled 不在其列)。 */
|
|
76
|
+
listFollows(): Promise<BuyerFollowRecord[]>;
|
|
77
|
+
/**
|
|
78
|
+
* 主动拉取关注更新:仅响应买家主动查询。水位语义在上游(返回什么再推进
|
|
79
|
+
* last_seen_at,不丢不重);本客户端原样透传分组,不本地过滤、不缓存重放。
|
|
80
|
+
*/
|
|
81
|
+
getUpdates(): Promise<BuyerFollowUpdateGroup[]>;
|
|
82
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
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
|
+
* BuyerFollowsSource —— kiwi-catalog 买家关注(M4 拉取式订阅)读/写客户端。
|
|
18
|
+
*
|
|
19
|
+
* PUT {baseUrl}/v1/me/follows/{merchant_id} → {ok, follow, created}
|
|
20
|
+
* DELETE {baseUrl}/v1/me/follows/{merchant_id} → {ok, merchant_id, following: false}
|
|
21
|
+
* GET {baseUrl}/v1/me/follows → {ok, follows: […]}
|
|
22
|
+
* GET {baseUrl}/v1/me/follows/updates → {ok, updates: […]}
|
|
23
|
+
*
|
|
24
|
+
* 契约来源:kiwi-catalog 仓 kiwi_catalog/services/buyer_follows.py 与
|
|
25
|
+
* api/handlers/buyer_follows.py(schema v30,merchant-buddy 第 0 版设计 §4)。
|
|
26
|
+
*
|
|
27
|
+
* 与 KiwiCatalogSource / MerchantPublicationsSource 的关键差异:这四条路由走
|
|
28
|
+
* catalog **账号会话**(cookie kiwi_session),不是匿名公开读——buyer_subject
|
|
29
|
+
* 由服务端从会话解析(account:{account_id} 不透明字符串),客户端不传身份、
|
|
30
|
+
* 不伪造身份。会话 token 由部署方显式配置(KIWI_CATALOG_SESSION /
|
|
31
|
+
* --catalog-session);HTTP 401/403 映射为 session_rejected(fail-closed),
|
|
32
|
+
* 由上层转成"需要先在 Kiwi 目录登录"的可解释引导。
|
|
33
|
+
*
|
|
34
|
+
* 出站加固与兄弟 source 一致:不跟随重定向、超时、响应大小上限;响应手写校验
|
|
35
|
+
* (merchant-publications.ts 同风格,上游暂无 vendored schema)。
|
|
36
|
+
*/
|
|
37
|
+
import { CatalogSourceError } from "./errors.js";
|
|
38
|
+
import { PRODUCT_VERSION } from "../../product-cli.js";
|
|
39
|
+
import { isRedirectResponse, readJsonBody, SafeHttpError } from "../../net/safe-http.js";
|
|
40
|
+
import { validateBaseUrl } from "./source.js";
|
|
41
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
42
|
+
// ── 手写响应校验(fail-closed,同 merchant-publications.ts 风格)────────────
|
|
43
|
+
function requireObject(value, what) {
|
|
44
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
45
|
+
throw new CatalogSourceError("contract_violation", `${what} must be a JSON object`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function requireNonEmptyString(record, field, what) {
|
|
50
|
+
if (typeof record[field] !== "string" || record[field] === "") {
|
|
51
|
+
throw new CatalogSourceError("contract_violation", `${what} missing non-empty string field "${field}"`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function optionalString(record, field, what) {
|
|
55
|
+
const v = record[field];
|
|
56
|
+
if (v !== undefined && v !== null && typeof v !== "string") {
|
|
57
|
+
throw new CatalogSourceError("contract_violation", `${what} field "${field}" must be a string when present`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function validateBuyerFollow(value) {
|
|
61
|
+
const record = requireObject(value, "BuyerFollow");
|
|
62
|
+
requireNonEmptyString(record, "merchant_id", "BuyerFollow");
|
|
63
|
+
requireNonEmptyString(record, "status", "BuyerFollow");
|
|
64
|
+
for (const field of [
|
|
65
|
+
"merchant_name",
|
|
66
|
+
"category",
|
|
67
|
+
"consent_version",
|
|
68
|
+
"created_at",
|
|
69
|
+
"last_seen_at",
|
|
70
|
+
]) {
|
|
71
|
+
optionalString(record, field, "BuyerFollow");
|
|
72
|
+
}
|
|
73
|
+
return record;
|
|
74
|
+
}
|
|
75
|
+
export function validateMerchantPublicEvent(value) {
|
|
76
|
+
const record = requireObject(value, "MerchantPublicEvent");
|
|
77
|
+
requireNonEmptyString(record, "event_id", "MerchantPublicEvent");
|
|
78
|
+
requireNonEmptyString(record, "event_type", "MerchantPublicEvent");
|
|
79
|
+
requireNonEmptyString(record, "created_at", "MerchantPublicEvent");
|
|
80
|
+
optionalString(record, "publication_id", "MerchantPublicEvent");
|
|
81
|
+
if (record.version !== undefined &&
|
|
82
|
+
record.version !== null &&
|
|
83
|
+
typeof record.version !== "number") {
|
|
84
|
+
throw new CatalogSourceError("contract_violation", 'MerchantPublicEvent field "version" must be a number when present');
|
|
85
|
+
}
|
|
86
|
+
// payload 即商家公开投影(public-only 白名单由上游写入侧保证);必须是对象。
|
|
87
|
+
requireObject(record.payload, "MerchantPublicEvent.payload");
|
|
88
|
+
return record;
|
|
89
|
+
}
|
|
90
|
+
export function validateFollowUpdateGroup(value) {
|
|
91
|
+
const record = requireObject(value, "FollowUpdateGroup");
|
|
92
|
+
requireNonEmptyString(record, "merchant_id", "FollowUpdateGroup");
|
|
93
|
+
optionalString(record, "merchant_name", "FollowUpdateGroup");
|
|
94
|
+
optionalString(record, "last_seen_at", "FollowUpdateGroup");
|
|
95
|
+
if (!Array.isArray(record.events)) {
|
|
96
|
+
throw new CatalogSourceError("contract_violation", 'FollowUpdateGroup missing array field "events"');
|
|
97
|
+
}
|
|
98
|
+
const events = record.events.map(validateMerchantPublicEvent);
|
|
99
|
+
return { ...record, events };
|
|
100
|
+
}
|
|
101
|
+
export class BuyerFollowsSource {
|
|
102
|
+
baseUrl;
|
|
103
|
+
deps;
|
|
104
|
+
constructor(deps) {
|
|
105
|
+
this.baseUrl = validateBaseUrl(deps.baseUrl);
|
|
106
|
+
this.deps = deps;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 核心请求:会话认证(cookie kiwi_session);超时 / HTTP / 网络失败统一映射
|
|
110
|
+
* 为 CatalogSourceError;HTTP 401/403 → session_rejected(fail-closed,不伪造
|
|
111
|
+
* 身份、不静默降级为匿名)。
|
|
112
|
+
*/
|
|
113
|
+
async request(method, requestPath, body) {
|
|
114
|
+
const sessionToken = this.deps.sessionToken;
|
|
115
|
+
if (sessionToken === undefined || sessionToken === "") {
|
|
116
|
+
throw new CatalogSourceError("session_rejected", "kiwi-catalog buyer session token is not configured");
|
|
117
|
+
}
|
|
118
|
+
const fetchImpl = this.deps.fetchImpl ?? globalThis.fetch;
|
|
119
|
+
const timeoutMs = this.deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
120
|
+
const url = `${this.baseUrl}${requestPath}`;
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
123
|
+
let response;
|
|
124
|
+
let raw;
|
|
125
|
+
try {
|
|
126
|
+
try {
|
|
127
|
+
response = await fetchImpl(url, {
|
|
128
|
+
method,
|
|
129
|
+
// 出站加固:绝不跟随重定向(3xx 目标不经过校验,且可能转发会话 cookie)。
|
|
130
|
+
redirect: "manual",
|
|
131
|
+
signal: controller.signal,
|
|
132
|
+
headers: {
|
|
133
|
+
accept: "application/json",
|
|
134
|
+
"user-agent": `kiwi-buyer/${PRODUCT_VERSION}`,
|
|
135
|
+
cookie: `kiwi_session=${sessionToken}`,
|
|
136
|
+
...(body !== undefined ? { "content-type": "application/json" } : {}),
|
|
137
|
+
...(this.deps.buyerId !== undefined ? { "x-buyer-id": this.deps.buyerId } : {}),
|
|
138
|
+
},
|
|
139
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const name = err?.name;
|
|
144
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
145
|
+
throw new CatalogSourceError("request_failed", name === "AbortError"
|
|
146
|
+
? `kiwi-catalog request timed out after ${timeoutMs}ms: ${url}`
|
|
147
|
+
: `kiwi-catalog request failed: ${url} (${detail})`);
|
|
148
|
+
}
|
|
149
|
+
if (isRedirectResponse(response)) {
|
|
150
|
+
throw new CatalogSourceError("request_failed", `kiwi-catalog request must not follow redirects (HTTP ${response.status} from ${url})`);
|
|
151
|
+
}
|
|
152
|
+
if (response.status === 401 || response.status === 403) {
|
|
153
|
+
throw new CatalogSourceError("session_rejected", `kiwi-catalog buyer session rejected (HTTP ${response.status}) from ${url}`);
|
|
154
|
+
}
|
|
155
|
+
if (!response.ok) {
|
|
156
|
+
throw new CatalogSourceError("request_failed", `kiwi-catalog request returned HTTP ${response.status} from ${url}`);
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
raw = await readJsonBody(response, { signal: controller.signal });
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (controller.signal.aborted) {
|
|
163
|
+
throw new CatalogSourceError("request_failed", `kiwi-catalog request timed out after ${timeoutMs}ms while reading response: ${url}`);
|
|
164
|
+
}
|
|
165
|
+
throw new CatalogSourceError("response_invalid", err instanceof SafeHttpError && err.code === "response_too_large"
|
|
166
|
+
? `kiwi-catalog response from ${url}: ${err.message}`
|
|
167
|
+
: `kiwi-catalog response from ${url} is not valid JSON`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
}
|
|
173
|
+
return raw;
|
|
174
|
+
}
|
|
175
|
+
/** 显式关注(幂等;可选 category/consent_version)。仅买家主动调用构成订阅。 */
|
|
176
|
+
async follow(merchantId, opts) {
|
|
177
|
+
if (typeof merchantId !== "string" || merchantId === "") {
|
|
178
|
+
throw new CatalogSourceError("invalid_input", "merchantId must be a non-empty string");
|
|
179
|
+
}
|
|
180
|
+
const raw = await this.request("PUT", `/v1/me/follows/${encodeURIComponent(merchantId)}`, {
|
|
181
|
+
...(opts?.category !== undefined ? { category: opts.category } : {}),
|
|
182
|
+
...(opts?.consent_version !== undefined ? { consent_version: opts.consent_version } : {}),
|
|
183
|
+
});
|
|
184
|
+
const body = requireObject(raw, "follow response");
|
|
185
|
+
if (body.follow === undefined) {
|
|
186
|
+
throw new CatalogSourceError("response_invalid", 'follow response is missing field "follow"');
|
|
187
|
+
}
|
|
188
|
+
if (typeof body.created !== "boolean") {
|
|
189
|
+
throw new CatalogSourceError("response_invalid", 'follow response field "created" must be a boolean');
|
|
190
|
+
}
|
|
191
|
+
return { follow: validateBuyerFollow(body.follow), created: body.created };
|
|
192
|
+
}
|
|
193
|
+
/** 取消关注(幂等;无活跃关注同样 ok)。 */
|
|
194
|
+
async unfollow(merchantId) {
|
|
195
|
+
if (typeof merchantId !== "string" || merchantId === "") {
|
|
196
|
+
throw new CatalogSourceError("invalid_input", "merchantId must be a non-empty string");
|
|
197
|
+
}
|
|
198
|
+
const raw = await this.request("DELETE", `/v1/me/follows/${encodeURIComponent(merchantId)}`);
|
|
199
|
+
const body = requireObject(raw, "unfollow response");
|
|
200
|
+
if (body.following !== false) {
|
|
201
|
+
throw new CatalogSourceError("response_invalid", 'unfollow response field "following" must be false');
|
|
202
|
+
}
|
|
203
|
+
requireNonEmptyString(body, "merchant_id", "unfollow response");
|
|
204
|
+
return { merchant_id: body.merchant_id, following: false };
|
|
205
|
+
}
|
|
206
|
+
/** 我的活跃关注列表(cancelled 不在其列)。 */
|
|
207
|
+
async listFollows() {
|
|
208
|
+
const raw = await this.request("GET", "/v1/me/follows");
|
|
209
|
+
const body = requireObject(raw, "list follows response");
|
|
210
|
+
if (!Array.isArray(body.follows)) {
|
|
211
|
+
throw new CatalogSourceError("response_invalid", 'list follows response is missing array field "follows"');
|
|
212
|
+
}
|
|
213
|
+
return body.follows.map(validateBuyerFollow);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* 主动拉取关注更新:仅响应买家主动查询。水位语义在上游(返回什么再推进
|
|
217
|
+
* last_seen_at,不丢不重);本客户端原样透传分组,不本地过滤、不缓存重放。
|
|
218
|
+
*/
|
|
219
|
+
async getUpdates() {
|
|
220
|
+
const raw = await this.request("GET", "/v1/me/follows/updates");
|
|
221
|
+
const body = requireObject(raw, "follow updates response");
|
|
222
|
+
if (!Array.isArray(body.updates)) {
|
|
223
|
+
throw new CatalogSourceError("response_invalid", 'follow updates response is missing array field "updates"');
|
|
224
|
+
}
|
|
225
|
+
return body.updates.map(validateFollowUpdateGroup);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=buyer-follows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buyer-follows.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/buyer-follows.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
/** 授权请求(入口持有;login_url 是给商家浏览器打开的地址)。 */
|
|
17
|
+
export interface ConnectorIdentityRequest {
|
|
18
|
+
readonly requestId: string;
|
|
19
|
+
readonly loginUrl: string;
|
|
20
|
+
readonly expiresAt: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 身份兑换时同时签发的商家作用域凭据(目录侧 schema v32)。
|
|
24
|
+
*
|
|
25
|
+
* 入口用它代表**该商家**调用目录的商家接口(公开资料草稿/状态/撤回);发布
|
|
26
|
+
* 仍须商家在目录门户确认页批准,凭据本身不能把 draft 变成 published。
|
|
27
|
+
*/
|
|
28
|
+
export interface ConnectorMerchantCredential {
|
|
29
|
+
readonly accessToken: string;
|
|
30
|
+
readonly scope: string;
|
|
31
|
+
readonly expiresAt: string;
|
|
32
|
+
}
|
|
33
|
+
/** 兑换结果:已验证的商家身份(唯一可信来源)+ 该商家的作用域凭据。 */
|
|
34
|
+
export interface ConnectorIdentity {
|
|
35
|
+
readonly merchantId: string;
|
|
36
|
+
readonly merchantName: string;
|
|
37
|
+
readonly credential: ConnectorMerchantCredential;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 入口需要的身份能力。测试与部署可注入不同实现;生产实现是本文件的
|
|
41
|
+
* HTTP 客户端。
|
|
42
|
+
*/
|
|
43
|
+
export interface CatalogIdentityProvider {
|
|
44
|
+
createRequest(input: {
|
|
45
|
+
returnUrl: string;
|
|
46
|
+
clientLabel?: string;
|
|
47
|
+
}): Promise<ConnectorIdentityRequest>;
|
|
48
|
+
exchange(input: {
|
|
49
|
+
requestId: string;
|
|
50
|
+
code: string;
|
|
51
|
+
}): Promise<ConnectorIdentity>;
|
|
52
|
+
}
|
|
53
|
+
export interface CatalogConnectorIdentityDeps {
|
|
54
|
+
/** Catalog 服务根 URL(http/https)。 */
|
|
55
|
+
baseUrl: string;
|
|
56
|
+
/** 连接器机器凭据(catalog 侧 KIWI_CATALOG_CONNECTOR_TOKEN)。 */
|
|
57
|
+
connectorToken: string;
|
|
58
|
+
fetchImpl?: typeof fetch;
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
}
|
|
61
|
+
export declare class CatalogConnectorIdentityClient implements CatalogIdentityProvider {
|
|
62
|
+
private readonly baseUrl;
|
|
63
|
+
private readonly connectorToken;
|
|
64
|
+
private readonly deps;
|
|
65
|
+
constructor(deps: CatalogConnectorIdentityDeps);
|
|
66
|
+
createRequest(input: {
|
|
67
|
+
returnUrl: string;
|
|
68
|
+
clientLabel?: string;
|
|
69
|
+
}): Promise<ConnectorIdentityRequest>;
|
|
70
|
+
exchange(input: {
|
|
71
|
+
requestId: string;
|
|
72
|
+
code: string;
|
|
73
|
+
}): Promise<ConnectorIdentity>;
|
|
74
|
+
private postJson;
|
|
75
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
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
|
+
* CatalogConnectorIdentityClient —— kiwi-catalog 一次性身份授权(连接器侧)。
|
|
18
|
+
*
|
|
19
|
+
* POST {baseUrl}/v1/connector-identity/requests
|
|
20
|
+
* {return_url, client_label?} → {ok, request:{request_id, expires_at}, login_url}
|
|
21
|
+
* POST {baseUrl}/v1/connector-identity/exchange
|
|
22
|
+
* {request_id, code} → {ok, identity:{merchant_id, merchant_name}}
|
|
23
|
+
*
|
|
24
|
+
* 契约来源:kiwi-catalog 仓 kiwi_catalog/api/handlers/connector_identity.py
|
|
25
|
+
* (schema v31;merchant-buddy 第 1 版设计 §3.2 / 商家连接器发布计划 §3.1)。
|
|
26
|
+
* 调用方是**商家连接器**的远程入口——买方连接器(`kiwi-sourcing`,本地
|
|
27
|
+
* stdio)不使用本接口。
|
|
28
|
+
*
|
|
29
|
+
* 硬边界(fail-closed,调用方不得吞掉错误):
|
|
30
|
+
* - merchant_id 只能来自 exchange 的返回值——绝不从工具参数、URL 或
|
|
31
|
+
* 客户端自述值推断(商家连接器入口的租户判定唯一来源);
|
|
32
|
+
* - 出站加固与既有 catalog 客户端一致:不跟随重定向(3xx 可能转发凭据)、
|
|
33
|
+
* 超时、响应大小上限、响应结构逐字段校验。
|
|
34
|
+
*
|
|
35
|
+
* connector token(`KIWI_CATALOG_CONNECTOR_TOKEN`)是入口与目录之间的机器
|
|
36
|
+
* 凭据:它只够创建/兑换一次性授权请求,不能读写任何商家数据(目录侧在
|
|
37
|
+
* 商家确认前不会为该请求绑定 merchant_id)。
|
|
38
|
+
*/
|
|
39
|
+
import { PRODUCT_VERSION } from "../../product-cli.js";
|
|
40
|
+
import { isRedirectResponse, readJsonBody } from "../../net/safe-http.js";
|
|
41
|
+
import { CatalogSourceError } from "./errors.js";
|
|
42
|
+
import { validateBaseUrl } from "./source.js";
|
|
43
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
44
|
+
function nonEmptyString(value, field) {
|
|
45
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
46
|
+
throw new CatalogSourceError("response_invalid", `connector-identity response is missing non-empty string field "${field}"`);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
export class CatalogConnectorIdentityClient {
|
|
51
|
+
baseUrl;
|
|
52
|
+
connectorToken;
|
|
53
|
+
deps;
|
|
54
|
+
constructor(deps) {
|
|
55
|
+
this.baseUrl = validateBaseUrl(deps.baseUrl);
|
|
56
|
+
if (typeof deps.connectorToken !== "string" || deps.connectorToken.trim() === "") {
|
|
57
|
+
throw new CatalogSourceError("invalid_input", "connector-identity client requires a non-empty connector token");
|
|
58
|
+
}
|
|
59
|
+
this.connectorToken = deps.connectorToken;
|
|
60
|
+
this.deps = deps;
|
|
61
|
+
}
|
|
62
|
+
async createRequest(input) {
|
|
63
|
+
const body = await this.postJson("/v1/connector-identity/requests", {
|
|
64
|
+
return_url: input.returnUrl,
|
|
65
|
+
...(input.clientLabel !== undefined ? { client_label: input.clientLabel } : {}),
|
|
66
|
+
});
|
|
67
|
+
const request = body.request;
|
|
68
|
+
if (request === null || typeof request !== "object" || Array.isArray(request)) {
|
|
69
|
+
throw new CatalogSourceError("response_invalid", 'connector-identity response is missing object field "request"');
|
|
70
|
+
}
|
|
71
|
+
const record = request;
|
|
72
|
+
const loginUrl = nonEmptyString(body.login_url, "login_url");
|
|
73
|
+
let parsedLoginUrl;
|
|
74
|
+
try {
|
|
75
|
+
parsedLoginUrl = new URL(loginUrl);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
throw new CatalogSourceError("response_invalid", "connector-identity login_url is not an absolute URL");
|
|
79
|
+
}
|
|
80
|
+
if (parsedLoginUrl.protocol !== "https:" && parsedLoginUrl.protocol !== "http:") {
|
|
81
|
+
throw new CatalogSourceError("response_invalid", `connector-identity login_url must use http or https (got ${parsedLoginUrl.protocol})`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
requestId: nonEmptyString(record.request_id, "request.request_id"),
|
|
85
|
+
loginUrl,
|
|
86
|
+
expiresAt: nonEmptyString(record.expires_at, "request.expires_at"),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async exchange(input) {
|
|
90
|
+
const body = await this.postJson("/v1/connector-identity/exchange", {
|
|
91
|
+
request_id: input.requestId,
|
|
92
|
+
code: input.code,
|
|
93
|
+
});
|
|
94
|
+
const identity = body.identity;
|
|
95
|
+
if (identity === null || typeof identity !== "object" || Array.isArray(identity)) {
|
|
96
|
+
throw new CatalogSourceError("response_invalid", 'connector-identity response is missing object field "identity"');
|
|
97
|
+
}
|
|
98
|
+
const record = identity;
|
|
99
|
+
const credential = body.credential;
|
|
100
|
+
if (credential === null || typeof credential !== "object" || Array.isArray(credential)) {
|
|
101
|
+
throw new CatalogSourceError("response_invalid", 'connector-identity response is missing object field "credential"');
|
|
102
|
+
}
|
|
103
|
+
const credentialRecord = credential;
|
|
104
|
+
return {
|
|
105
|
+
merchantId: nonEmptyString(record.merchant_id, "identity.merchant_id"),
|
|
106
|
+
// 商家名允许为空字符串(未填名称的账号),不做非空校验。
|
|
107
|
+
merchantName: typeof record.merchant_name === "string" ? record.merchant_name : "",
|
|
108
|
+
credential: {
|
|
109
|
+
accessToken: nonEmptyString(credentialRecord.access_token, "credential.access_token"),
|
|
110
|
+
scope: typeof credentialRecord.scope === "string" ? credentialRecord.scope : "",
|
|
111
|
+
expiresAt: nonEmptyString(credentialRecord.expires_at, "credential.expires_at"),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async postJson(requestPath, body) {
|
|
116
|
+
const fetchImpl = this.deps.fetchImpl ?? globalThis.fetch;
|
|
117
|
+
const timeoutMs = this.deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
118
|
+
const url = `${this.baseUrl}${requestPath}`;
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
121
|
+
let response;
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
try {
|
|
125
|
+
response = await fetchImpl(url, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
// 出站加固:绝不跟随重定向(3xx 目标不经过校验,且可能转发凭据)。
|
|
128
|
+
redirect: "manual",
|
|
129
|
+
signal: controller.signal,
|
|
130
|
+
headers: {
|
|
131
|
+
accept: "application/json",
|
|
132
|
+
"content-type": "application/json",
|
|
133
|
+
authorization: `Bearer ${this.connectorToken}`,
|
|
134
|
+
"user-agent": `kiwi-connector/${PRODUCT_VERSION}`,
|
|
135
|
+
},
|
|
136
|
+
body: JSON.stringify(body),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const name = err?.name;
|
|
141
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
142
|
+
throw new CatalogSourceError("request_failed", name === "AbortError"
|
|
143
|
+
? `kiwi-catalog connector request timed out after ${timeoutMs}ms: ${url}`
|
|
144
|
+
: `kiwi-catalog connector request failed: ${url} (${detail})`);
|
|
145
|
+
}
|
|
146
|
+
if (isRedirectResponse(response)) {
|
|
147
|
+
throw new CatalogSourceError("request_failed", `kiwi-catalog connector request must not follow redirects (HTTP ${response.status} from ${url})`);
|
|
148
|
+
}
|
|
149
|
+
if (response.status === 401 || response.status === 403) {
|
|
150
|
+
throw new CatalogSourceError("session_rejected", `kiwi-catalog rejected the connector credential (HTTP ${response.status} from ${url})`);
|
|
151
|
+
}
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new CatalogSourceError("request_failed", `kiwi-catalog connector request returned HTTP ${response.status} from ${url}`);
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
raw = await readJsonBody(response, { signal: controller.signal });
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw new CatalogSourceError("response_invalid", `kiwi-catalog connector response from ${url} is not valid JSON`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
166
|
+
throw new CatalogSourceError("response_invalid", "connector-identity response must be a JSON object");
|
|
167
|
+
}
|
|
168
|
+
const envelope = raw;
|
|
169
|
+
if (envelope.ok !== true) {
|
|
170
|
+
throw new CatalogSourceError("response_invalid", `connector-identity response is not ok: ${typeof envelope.error === "string" ? envelope.error : "unknown error"}`);
|
|
171
|
+
}
|
|
172
|
+
return envelope;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=connector-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector-identity.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/connector-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* - contract_violation 候选元素未通过 CandidateAgent DTO schema 校验,或 contract
|
|
24
24
|
* 注解非 candidate-agent / 非 1.x(视为协议级违规)。
|
|
25
25
|
*/
|
|
26
|
-
export declare const CATALOG_SOURCE_ERROR_CODES: readonly ["invalid_input", "request_failed", "response_invalid", "contract_violation"];
|
|
26
|
+
export declare const CATALOG_SOURCE_ERROR_CODES: readonly ["invalid_input", "request_failed", "response_invalid", "contract_violation", "session_rejected"];
|
|
27
27
|
export type CatalogSourceErrorCode = (typeof CATALOG_SOURCE_ERROR_CODES)[number];
|
|
28
28
|
/** Catalog 读取失败。带错误码;fail-closed,调用方不得吞掉。 */
|
|
29
29
|
export declare class CatalogSourceError extends Error {
|
|
@@ -28,6 +28,8 @@ export const CATALOG_SOURCE_ERROR_CODES = [
|
|
|
28
28
|
"request_failed",
|
|
29
29
|
"response_invalid",
|
|
30
30
|
"contract_violation",
|
|
31
|
+
/** 会话认证端点拒绝了买家会话(HTTP 401/403):登录态缺失或已过期。 */
|
|
32
|
+
"session_rejected",
|
|
31
33
|
];
|
|
32
34
|
/** Catalog 读取失败。带错误码;fail-closed,调用方不得吞掉。 */
|
|
33
35
|
export class CatalogSourceError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,eAAe;IACf,gBAAgB;IAChB,kBAAkB;IAClB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,eAAe;IACf,gBAAgB;IAChB,kBAAkB;IAClB,oBAAoB;IACpB,6CAA6C;IAC7C,kBAAkB;CACV,CAAC;AAIX,6CAA6C;AAC7C,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAClC,IAAI,CAAyB;IAEtC,YAAY,IAA4B,EAAE,OAAe;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -163,6 +163,8 @@ export interface ListingSearchResult {
|
|
|
163
163
|
*/
|
|
164
164
|
export interface KiwiListingSearchQuery {
|
|
165
165
|
q?: string;
|
|
166
|
+
/** 精确限定 listing owner,避免先做全局限量搜索再在本地过滤。 */
|
|
167
|
+
owner_agent_id?: string;
|
|
166
168
|
listing_type?: ListingType;
|
|
167
169
|
category?: string;
|
|
168
170
|
brand?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kiwi-record.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/kiwi-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"kiwi-record.js","sourceRoot":"","sources":["../../../src/discovery/catalog-source/kiwi-record.ts"],"names":[],"mappings":"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"}
|