@harrylabsj/kiwi 0.6.0 → 0.6.2
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 +21 -0
- package/contracts/candidate-agent-dto-1.0.README.md +12 -8
- package/contracts/conformance/python-service/README.md +19 -0
- package/contracts/conformance/python-service/run.py +134 -0
- package/contracts/kiwi-catalog/1.0/listing-record.schema.json +11 -1
- package/contracts/kiwi-contracts.lock.json +7 -0
- package/contracts/manifest.json +61 -0
- package/contracts/vectors/candidate-agent.invalid-private-field.json +7 -0
- package/contracts/vectors/candidate-agent.valid.json +26 -0
- package/dist/a2a/client/client.js +39 -34
- package/dist/a2a/client/client.js.map +1 -1
- package/dist/a2a/client/url-policy.d.ts +8 -1
- package/dist/a2a/client/url-policy.js +18 -2
- package/dist/a2a/client/url-policy.js.map +1 -1
- package/dist/a2a/clock.d.ts +30 -0
- package/dist/a2a/clock.js +39 -0
- package/dist/a2a/clock.js.map +1 -0
- package/dist/a2a/negotiate.d.ts +6 -0
- package/dist/a2a/negotiate.js +10 -12
- package/dist/a2a/negotiate.js.map +1 -1
- package/dist/a2a/node.d.ts +23 -3
- package/dist/a2a/node.js +128 -18
- package/dist/a2a/node.js.map +1 -1
- package/dist/a2a/server/merchant-handler.d.ts +4 -0
- package/dist/a2a/server/merchant-handler.js +238 -25
- package/dist/a2a/server/merchant-handler.js.map +1 -1
- package/dist/a2a/server/pipeline.d.ts +17 -0
- package/dist/a2a/server/pipeline.js +89 -1
- package/dist/a2a/server/pipeline.js.map +1 -1
- package/dist/a2a/server/ucp.d.ts +30 -2
- package/dist/a2a/server/ucp.js +23 -14
- package/dist/a2a/server/ucp.js.map +1 -1
- package/dist/agent/agent-db.js +5 -0
- package/dist/agent/agent-db.js.map +1 -1
- package/dist/agent/buyer/buyer-tools.d.ts +5 -0
- package/dist/agent/buyer/buyer-tools.js +8 -1
- package/dist/agent/buyer/buyer-tools.js.map +1 -1
- package/dist/agent/buyer/types.d.ts +0 -25
- package/dist/agent/buyer/types.js.map +1 -1
- package/dist/agent/kernel-builder.d.ts +27 -0
- package/dist/agent/kernel-builder.js +180 -0
- package/dist/agent/kernel-builder.js.map +1 -0
- package/dist/agent/kernel.d.ts +6 -0
- package/dist/agent/kernel.js +95 -8
- package/dist/agent/kernel.js.map +1 -1
- package/dist/agent/memory/store.d.ts +5 -0
- package/dist/agent/memory/store.js +29 -1
- package/dist/agent/memory/store.js.map +1 -1
- package/dist/agent/merchant/action-candidate.js +15 -1
- package/dist/agent/merchant/action-candidate.js.map +1 -1
- package/dist/agent/merchant/merchant-client.js +22 -4
- package/dist/agent/merchant/merchant-client.js.map +1 -1
- package/dist/agent/merchant/types.d.ts +5 -2
- package/dist/agent/merchant/types.js +6 -2
- package/dist/agent/merchant/types.js.map +1 -1
- package/dist/agent/negotiation-chat.js +34 -14
- package/dist/agent/negotiation-chat.js.map +1 -1
- package/dist/cli.d.ts +56 -0
- package/dist/cli.js +141 -156
- package/dist/cli.js.map +1 -1
- package/dist/commerce/data-source.d.ts +7 -3
- package/dist/commerce/data-source.js.map +1 -1
- package/dist/commerce/http-client.js +35 -31
- package/dist/commerce/http-client.js.map +1 -1
- package/dist/commerce/shopping-cli-source.js +66 -48
- package/dist/commerce/shopping-cli-source.js.map +1 -1
- package/dist/config/profile.d.ts +7 -0
- package/dist/config/profile.js +23 -0
- package/dist/config/profile.js.map +1 -1
- package/dist/counterparty/a2a-direct/index.d.ts +5 -0
- package/dist/counterparty/a2a-direct/index.js +24 -0
- package/dist/counterparty/a2a-direct/index.js.map +1 -1
- package/dist/counterparty/shopping-cli-hosted/index.js +52 -19
- package/dist/counterparty/shopping-cli-hosted/index.js.map +1 -1
- package/dist/discovery/catalog-source/kiwi-record.d.ts +3 -0
- package/dist/discovery/catalog-source/kiwi-record.js.map +1 -1
- package/dist/discovery/catalog-source/kiwi-source.js +46 -36
- package/dist/discovery/catalog-source/kiwi-source.js.map +1 -1
- package/dist/discovery/catalog-source/register.d.ts +6 -0
- package/dist/discovery/catalog-source/register.js +27 -8
- package/dist/discovery/catalog-source/register.js.map +1 -1
- package/dist/discovery/catalog-source/source.js +39 -35
- package/dist/discovery/catalog-source/source.js.map +1 -1
- package/dist/discovery/resolve.d.ts +15 -0
- package/dist/discovery/resolve.js +59 -35
- package/dist/discovery/resolve.js.map +1 -1
- package/dist/discovery/ucp/index.d.ts +1 -1
- package/dist/discovery/ucp/index.js +1 -1
- package/dist/discovery/ucp/index.js.map +1 -1
- package/dist/discovery/ucp/resolver.d.ts +5 -0
- package/dist/discovery/ucp/resolver.js +65 -38
- package/dist/discovery/ucp/resolver.js.map +1 -1
- package/dist/discovery/ucp/vendor.d.ts +1 -1
- package/dist/discovery/ucp/vendor.js +1 -1
- package/dist/handoff/delivery.d.ts +4 -2
- package/dist/handoff/delivery.js +6 -1
- package/dist/handoff/delivery.js.map +1 -1
- package/dist/handoff/errors.d.ts +1 -1
- package/dist/handoff/errors.js +1 -0
- package/dist/handoff/errors.js.map +1 -1
- package/dist/handoff/idempotency.d.ts +10 -1
- package/dist/handoff/idempotency.js +13 -3
- package/dist/handoff/idempotency.js.map +1 -1
- package/dist/handoff/index.d.ts +1 -1
- package/dist/handoff/index.js +1 -1
- package/dist/handoff/index.js.map +1 -1
- package/dist/handoff/ledger.d.ts +9 -0
- package/dist/handoff/ledger.js +66 -0
- package/dist/handoff/ledger.js.map +1 -1
- package/dist/handoff/transaction.d.ts +21 -3
- package/dist/handoff/transaction.js +50 -11
- package/dist/handoff/transaction.js.map +1 -1
- package/dist/handoff/ucp-checkout/channel.js +15 -10
- package/dist/handoff/ucp-checkout/channel.js.map +1 -1
- package/dist/negotiation/jcs.js +11 -8
- package/dist/negotiation/jcs.js.map +1 -1
- package/dist/negotiation/lease/store.d.ts +25 -0
- package/dist/negotiation/lease/store.js +101 -0
- package/dist/negotiation/lease/store.js.map +1 -0
- package/dist/negotiation/recovery/recover.js +25 -1
- package/dist/negotiation/recovery/recover.js.map +1 -1
- package/dist/negotiation/state/phase.js +12 -4
- package/dist/negotiation/state/phase.js.map +1 -1
- package/dist/operator/runner.d.ts +11 -0
- package/dist/operator/runner.js +20 -1
- package/dist/operator/runner.js.map +1 -1
- package/dist/product-catalog.d.ts +43 -0
- package/dist/product-catalog.js +60 -0
- package/dist/product-catalog.js.map +1 -0
- package/dist/product-cli.d.ts +1 -1
- package/dist/product-cli.js +1 -1
- package/dist/product-publish.d.ts +15 -17
- package/dist/product-publish.js +279 -46
- package/dist/product-publish.js.map +1 -1
- package/dist/supervisor/stack-config.d.ts +6 -1
- package/dist/supervisor/stack-config.js +11 -3
- package/dist/supervisor/stack-config.js.map +1 -1
- package/dist/trust/identity/message-signature.js +17 -0
- package/dist/trust/identity/message-signature.js.map +1 -1
- package/dist/weixin/channel.d.ts +89 -0
- package/dist/weixin/channel.js +370 -0
- package/dist/weixin/channel.js.map +1 -0
- package/dist/weixin/cli-weixin.d.ts +29 -0
- package/dist/weixin/cli-weixin.js +130 -0
- package/dist/weixin/cli-weixin.js.map +1 -0
- package/dist/weixin/credentials.d.ts +33 -0
- package/dist/weixin/credentials.js +107 -0
- package/dist/weixin/credentials.js.map +1 -0
- package/dist/weixin/ilink-client.d.ts +44 -0
- package/dist/weixin/ilink-client.js +287 -0
- package/dist/weixin/ilink-client.js.map +1 -0
- package/dist/weixin/login.d.ts +37 -0
- package/dist/weixin/login.js +93 -0
- package/dist/weixin/login.js.map +1 -0
- package/dist/weixin/qr.d.ts +26 -0
- package/dist/weixin/qr.js +525 -0
- package/dist/weixin/qr.js.map +1 -0
- package/dist/weixin/types.d.ts +114 -0
- package/dist/weixin/types.js +48 -0
- package/dist/weixin/types.js.map +1 -0
- package/package.json +12 -2
- package/dist/commerce/erp-source.d.ts +0 -51
- package/dist/commerce/erp-source.js +0 -184
- package/dist/commerce/erp-source.js.map +0 -1
- package/dist/commerce/local-db-source.d.ts +0 -50
- package/dist/commerce/local-db-source.js +0 -162
- package/dist/commerce/local-db-source.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -30,21 +30,18 @@
|
|
|
30
30
|
import { existsSync, realpathSync } from "node:fs";
|
|
31
31
|
import path from "node:path";
|
|
32
32
|
import { fileURLToPath } from "node:url";
|
|
33
|
-
import { agentDataDir, ensurePathsForDir } from "./agent/agent-db.js";
|
|
34
33
|
import { startA2aNode } from "./a2a/node.js";
|
|
35
34
|
import { runChatTui } from "./agent/chat-tui.js";
|
|
36
|
-
import {
|
|
37
|
-
import { isAgentMode } from "./agent/mode.js";
|
|
35
|
+
import { buildChatKernel, buildClient, defaultChatProfile } from "./agent/kernel-builder.js";
|
|
38
36
|
import { AgentKernel } from "./agent/kernel.js";
|
|
39
|
-
import { loadProfile, ProfileError
|
|
40
|
-
import { HttpCommerceClient } from "./commerce/http-client.js";
|
|
37
|
+
import { loadProfile, ProfileError } from "./config/profile.js";
|
|
41
38
|
import { CommerceError } from "./commerce/types.js";
|
|
42
39
|
import { runDoctor } from "./doctor.js";
|
|
43
40
|
import { EXIT } from "./exit-codes.js";
|
|
44
41
|
import { createDeterministicStreamFn } from "./runtime/fake-model.js";
|
|
45
42
|
import { runForeground } from "./runtime/foreground.js";
|
|
46
43
|
import { runNegotiationTurn } from "./runtime/negotiation-turn.js";
|
|
47
|
-
import { isFakeProvider, realStreamFn
|
|
44
|
+
import { isFakeProvider, realStreamFn } from "./runtime/model.js";
|
|
48
45
|
import { OperatorController } from "./operator/controller.js";
|
|
49
46
|
import { DeterministicNegotiationRunner } from "./operator/runner.js";
|
|
50
47
|
import { FileOperatorEventStore, OperatorStoreError } from "./operator/store.js";
|
|
@@ -55,9 +52,11 @@ import { runDown, runStatus, runUp, SupervisorError } from "./supervisor/manage.
|
|
|
55
52
|
import { parseLogLines, runLogs } from "./supervisor/logs.js";
|
|
56
53
|
import { StackConfigError } from "./supervisor/stack-config.js";
|
|
57
54
|
import { cmdProductDoctor, productHelp, PRODUCT_VERSION, DEFAULT_CATALOG_URL } from "./product-cli.js";
|
|
55
|
+
import { cmdWeixin, weixinUsage } from "./weixin/cli-weixin.js";
|
|
58
56
|
import { merchantInit } from "./product-init.js";
|
|
59
57
|
import { buyerInit, buyerSearch, buyerTasks } from "./product-buyer.js";
|
|
60
58
|
import { merchantPublish } from "./product-publish.js";
|
|
59
|
+
import { catalogServe } from "./product-catalog.js";
|
|
61
60
|
const USAGE = `kiwi ${PRODUCT_VERSION} — commerce negotiation agent runtime
|
|
62
61
|
|
|
63
62
|
Usage:
|
|
@@ -81,7 +80,12 @@ Usage:
|
|
|
81
80
|
/discover /negotiate /register in-session
|
|
82
81
|
kiwi chat --profile <file> [--data-dir <dir>]
|
|
83
82
|
Main conversation with Principal Memory (v0.3.0-A)
|
|
83
|
+
kiwi weixin [--profile <file>] [--allow id,...] [--relogin] [--a2a]
|
|
84
|
+
WeChat remote control (scan QR to log in)
|
|
84
85
|
kiwi metrics --dir <dir> Handoff/negotiation ledger metrics (JSONL)
|
|
86
|
+
kiwi catalog serve [--db <file>] [--host <host>] [--port <port>]
|
|
87
|
+
Start the standalone kiwi-catalog service
|
|
88
|
+
(foreground; requires pip install kiwi-catalog)
|
|
85
89
|
|
|
86
90
|
Product layer (product-strategy rev1.1 §10/§19):
|
|
87
91
|
kiwi buyer --help Kiwi Buyer command tree
|
|
@@ -100,7 +104,8 @@ contain secrets or private policy values. Secrets come from environment
|
|
|
100
104
|
variables named in the profile (commerce.token_env, model.api_key_env);
|
|
101
105
|
they are never read from or written to the profile file.
|
|
102
106
|
`;
|
|
103
|
-
|
|
107
|
+
/** 导出供测试(审查 P2-L:--a2a flag 解析回归锁定)。 */
|
|
108
|
+
export function parseArgs(argv) {
|
|
104
109
|
const command = [];
|
|
105
110
|
let profile;
|
|
106
111
|
let dir;
|
|
@@ -114,6 +119,7 @@ function parseArgs(argv) {
|
|
|
114
119
|
let output;
|
|
115
120
|
let force = false;
|
|
116
121
|
let noInstall = false;
|
|
122
|
+
let allowEmptyProjectionReconcile = false;
|
|
117
123
|
let agentId;
|
|
118
124
|
let ownerId;
|
|
119
125
|
let autoNegotiate = false;
|
|
@@ -124,10 +130,17 @@ function parseArgs(argv) {
|
|
|
124
130
|
let dataDir;
|
|
125
131
|
let catalog;
|
|
126
132
|
let port;
|
|
133
|
+
let catalogDb;
|
|
134
|
+
let catalogHost;
|
|
127
135
|
let once = false;
|
|
128
136
|
let fake = false;
|
|
129
137
|
let noChat = false;
|
|
130
138
|
let noA2a = false;
|
|
139
|
+
let a2aExplicit = false;
|
|
140
|
+
let weixinAllow;
|
|
141
|
+
let relogin = false;
|
|
142
|
+
let qrScale = 1;
|
|
143
|
+
let noQr = false;
|
|
131
144
|
for (let i = 0; i < argv.length; i++) {
|
|
132
145
|
const arg = argv[i];
|
|
133
146
|
if (arg === "--profile") {
|
|
@@ -163,6 +176,10 @@ function parseArgs(argv) {
|
|
|
163
176
|
else if (arg === "--force") {
|
|
164
177
|
force = true;
|
|
165
178
|
}
|
|
179
|
+
else if (arg === "--allow-empty-projection") {
|
|
180
|
+
// 审查 P1-B:显式放行"投影为空时 reconcile 全量下架"(默认拒绝)
|
|
181
|
+
allowEmptyProjectionReconcile = true;
|
|
182
|
+
}
|
|
166
183
|
else if (arg === "--no-install") {
|
|
167
184
|
noInstall = true;
|
|
168
185
|
}
|
|
@@ -199,6 +216,12 @@ function parseArgs(argv) {
|
|
|
199
216
|
const parsed = /^\d+$/.test(raw ?? "") ? Number(raw) : Number.NaN;
|
|
200
217
|
port = Number.isInteger(parsed) ? parsed : undefined;
|
|
201
218
|
}
|
|
219
|
+
else if (arg === "--host") {
|
|
220
|
+
catalogHost = argv[++i];
|
|
221
|
+
}
|
|
222
|
+
else if (arg === "--db") {
|
|
223
|
+
catalogDb = argv[++i];
|
|
224
|
+
}
|
|
202
225
|
else if (arg === "--once") {
|
|
203
226
|
once = true;
|
|
204
227
|
}
|
|
@@ -211,6 +234,26 @@ function parseArgs(argv) {
|
|
|
211
234
|
else if (arg === "--no-a2a") {
|
|
212
235
|
noA2a = true;
|
|
213
236
|
}
|
|
237
|
+
else if (arg === "--a2a") {
|
|
238
|
+
// 审查 P2-L:weixin 命令文档承诺"A2A 节点默认关,--a2a 开启"——
|
|
239
|
+
// 此前 parseArgs 无此分支(Unknown argument: --a2a,实测 exit 2),
|
|
240
|
+
// 且默认 noA2a=false 让 A2A 恒开(与文档相反,需 --no-a2a 才能关)。
|
|
241
|
+
// 裸 kiwi 命令的 A2A 默认开不受影响(见 cmdWeixin 的解析)。
|
|
242
|
+
a2aExplicit = true;
|
|
243
|
+
}
|
|
244
|
+
else if (arg === "--allow") {
|
|
245
|
+
weixinAllow = argv[++i];
|
|
246
|
+
}
|
|
247
|
+
else if (arg === "--relogin") {
|
|
248
|
+
relogin = true;
|
|
249
|
+
}
|
|
250
|
+
else if (arg === "--qr-scale") {
|
|
251
|
+
const raw = argv[++i];
|
|
252
|
+
qrScale = raw === "2" ? 2 : 1;
|
|
253
|
+
}
|
|
254
|
+
else if (arg === "--no-qr") {
|
|
255
|
+
noQr = true;
|
|
256
|
+
}
|
|
214
257
|
else if (arg !== undefined && arg.startsWith("--profile=")) {
|
|
215
258
|
profile = arg.slice("--profile=".length);
|
|
216
259
|
}
|
|
@@ -239,7 +282,22 @@ function parseArgs(argv) {
|
|
|
239
282
|
throw new ProfileError(`Unknown argument: ${arg ?? ""}`);
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
|
-
const out = {
|
|
285
|
+
const out = {
|
|
286
|
+
command,
|
|
287
|
+
once,
|
|
288
|
+
fake,
|
|
289
|
+
noChat,
|
|
290
|
+
noA2a,
|
|
291
|
+
a2aExplicit,
|
|
292
|
+
force,
|
|
293
|
+
noInstall,
|
|
294
|
+
autoNegotiate,
|
|
295
|
+
allowEmptyProjectionReconcile,
|
|
296
|
+
weixinAllow,
|
|
297
|
+
relogin,
|
|
298
|
+
qrScale,
|
|
299
|
+
noQr,
|
|
300
|
+
};
|
|
243
301
|
if (profile !== undefined)
|
|
244
302
|
out.profile = profile;
|
|
245
303
|
if (dir !== undefined)
|
|
@@ -254,6 +312,8 @@ function parseArgs(argv) {
|
|
|
254
312
|
out.shoppingCliPath = shoppingCliPath;
|
|
255
313
|
if (shoppingCliMerchant !== undefined)
|
|
256
314
|
out.shoppingCliMerchant = shoppingCliMerchant;
|
|
315
|
+
if (allowEmptyProjectionReconcile)
|
|
316
|
+
out.allowEmptyProjectionReconcile = true;
|
|
257
317
|
if (merchantId !== undefined)
|
|
258
318
|
out.merchantId = merchantId;
|
|
259
319
|
if (merchantName !== undefined)
|
|
@@ -278,6 +338,10 @@ function parseArgs(argv) {
|
|
|
278
338
|
out.catalog = catalog;
|
|
279
339
|
if (port !== undefined)
|
|
280
340
|
out.port = port;
|
|
341
|
+
if (catalogDb !== undefined)
|
|
342
|
+
out.catalogDb = catalogDb;
|
|
343
|
+
if (catalogHost !== undefined)
|
|
344
|
+
out.catalogHost = catalogHost;
|
|
281
345
|
return out;
|
|
282
346
|
}
|
|
283
347
|
function requireDir(args) {
|
|
@@ -292,13 +356,6 @@ function requireProfile(args) {
|
|
|
292
356
|
}
|
|
293
357
|
return loadProfile(args.profile);
|
|
294
358
|
}
|
|
295
|
-
function buildClient(profile) {
|
|
296
|
-
const token = resolveSecret(profile.commerce.token_env, "commerce token");
|
|
297
|
-
return new HttpCommerceClient({
|
|
298
|
-
baseUrl: profile.commerce.base_url,
|
|
299
|
-
token,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
359
|
function printReportJsonl(report) {
|
|
303
360
|
process.stdout.write(`${JSON.stringify(report)}\n`);
|
|
304
361
|
}
|
|
@@ -473,7 +530,16 @@ async function cmdTui(args) {
|
|
|
473
530
|
*
|
|
474
531
|
* kiwi agent serve --profile <merchant.yaml> [--catalog <url>] # 缺省官方 catalog,本地自托管用 --catalog http://127.0.0.1:8600
|
|
475
532
|
* [--port 9000] [--data-dir <dir>]
|
|
533
|
+
*
|
|
534
|
+
* 公网暴露:节点仍监听 127.0.0.1,但可用 KIWI_A2A_PUBLIC_URL=<https://domain>
|
|
535
|
+
* 覆盖 Agent Card / UCP / catalog 注册广告的公网地址(配合 Caddy/Nginx 反代)。
|
|
476
536
|
*/
|
|
537
|
+
/** 审查 P1-09:agent serve 的 A2A 状态目录。缺省用稳定路径
|
|
538
|
+
* `<cwd>/.kiwi/agents/<agent_id>`(与 operator 路径共用同一缺省)——重启后
|
|
539
|
+
* Ledger/幂等/终态可恢复;显式 --data-dir 覆盖。绝不回退到临时目录。 */
|
|
540
|
+
export function resolveServeDataDir(dataDir, agentId) {
|
|
541
|
+
return dataDir ?? path.resolve(".kiwi", "agents", agentId);
|
|
542
|
+
}
|
|
477
543
|
async function cmdAgentServe(args) {
|
|
478
544
|
const profile = requireProfile(args);
|
|
479
545
|
if (profile.role !== "merchant") {
|
|
@@ -481,13 +547,22 @@ async function cmdAgentServe(args) {
|
|
|
481
547
|
return EXIT.CONFIG;
|
|
482
548
|
}
|
|
483
549
|
const catalog = args.catalog ?? process.env.KIWI_CATALOG_URL ?? DEFAULT_CATALOG_URL;
|
|
550
|
+
const merchantToken = process.env.KIWI_MERCHANT_TOKEN || "";
|
|
551
|
+
// 审查 P1-09:serve 的初始 A2A 节点必须用稳定 dataDir——此前初始节点缺省走
|
|
552
|
+
// mkdtemp 临时目录且 stop 时删除,重启后 Ledger/幂等/终态全丢:已终态
|
|
553
|
+
// negotiation 可重开、已发 conditional offer 返回 offer_unknown。稳定目录
|
|
554
|
+
// 让重启可恢复状态(resolveServeDataDir 见上)。
|
|
555
|
+
const serveDataDir = resolveServeDataDir(args.dataDir, profile.agent_id);
|
|
484
556
|
let node = await startA2aNode({
|
|
485
557
|
profile,
|
|
486
558
|
catalog,
|
|
487
559
|
preferredPort: args.port,
|
|
560
|
+
dataDir: serveDataDir,
|
|
561
|
+
...(merchantToken ? { ownerToken: merchantToken } : {}),
|
|
488
562
|
ownerTokenSecret: process.env.KIWI_CATALOG_OWNER_TOKEN_SECRET,
|
|
489
563
|
});
|
|
490
|
-
console.log(`[agent serve] merchant ${profile.agent_id} A2A server: ${node.agentCardUrl}`
|
|
564
|
+
console.log(`[agent serve] merchant ${profile.agent_id} A2A server: ${node.agentCardUrl}` +
|
|
565
|
+
(node.advertisedUrl !== node.url ? ` (local ${node.url})` : ""));
|
|
491
566
|
console.log(`[agent serve] registered in catalog ${catalog}: ${node.catalogAgentId ?? "?"}`);
|
|
492
567
|
const a2aNode = {
|
|
493
568
|
status: () => node === null
|
|
@@ -504,6 +579,8 @@ async function cmdAgentServe(args) {
|
|
|
504
579
|
profile: p,
|
|
505
580
|
catalog,
|
|
506
581
|
preferredPort: args.port,
|
|
582
|
+
dataDir: serveDataDir,
|
|
583
|
+
...(merchantToken ? { ownerToken: merchantToken } : {}),
|
|
507
584
|
ownerTokenSecret: process.env.KIWI_CATALOG_OWNER_TOKEN_SECRET,
|
|
508
585
|
});
|
|
509
586
|
},
|
|
@@ -559,149 +636,12 @@ async function cmdAgentServe(args) {
|
|
|
559
636
|
* - KIWI_MODEL_API_KEY_ENV(默认 DEEPSEEK_API_KEY)
|
|
560
637
|
* - KIWI_MODEL_BASE_URL(可选覆盖)
|
|
561
638
|
*/
|
|
562
|
-
function defaultChatProfile() {
|
|
563
|
-
const provider = process.env.KIWI_MODEL_PROVIDER ?? "deepseek";
|
|
564
|
-
const model = process.env.KIWI_MODEL ?? "deepseek-v4-flash";
|
|
565
|
-
const apiKeyEnv = process.env.KIWI_MODEL_API_KEY_ENV ?? "DEEPSEEK_API_KEY";
|
|
566
|
-
const baseUrl = process.env.KIWI_MODEL_BASE_URL;
|
|
567
|
-
return {
|
|
568
|
-
runtime_version: RUNTIME_VERSION,
|
|
569
|
-
protocol_version: "shopping.negotiation/0.1",
|
|
570
|
-
agent_id: "kiwi-assistant",
|
|
571
|
-
role: "buyer",
|
|
572
|
-
owner_id: "kiwi-user",
|
|
573
|
-
commerce: {
|
|
574
|
-
base_url: "http://127.0.0.1:8765",
|
|
575
|
-
token_env: "SHOPPING_BUYER_TOKEN",
|
|
576
|
-
backend: "local_marketplace",
|
|
577
|
-
},
|
|
578
|
-
model: {
|
|
579
|
-
provider,
|
|
580
|
-
model,
|
|
581
|
-
api_key_env: apiKeyEnv,
|
|
582
|
-
...(baseUrl !== undefined ? { base_url: baseUrl } : {}),
|
|
583
|
-
},
|
|
584
|
-
runtime: {
|
|
585
|
-
mode: "once",
|
|
586
|
-
poll_interval_seconds: 5,
|
|
587
|
-
turn_timeout_seconds: 90,
|
|
588
|
-
max_model_steps: 4,
|
|
589
|
-
max_retries: 2,
|
|
590
|
-
},
|
|
591
|
-
buyer_policy: {
|
|
592
|
-
target_skus: [],
|
|
593
|
-
quantity: 1,
|
|
594
|
-
max_total_price_private: 1_000_000,
|
|
595
|
-
acceptable_eta_latest: "2099-12-31T23:59:59+08:00",
|
|
596
|
-
required_after_sales_terms: [],
|
|
597
|
-
auto_negotiate: true,
|
|
598
|
-
human_review_on: [],
|
|
599
|
-
},
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
639
|
/**
|
|
603
640
|
* Main conversation (v0.3.0-A): build the AgentKernel for a profile.
|
|
604
641
|
* provider=fake runs the deterministic offline chat fake; real providers
|
|
605
642
|
* resolve models and auth through pi-ai's built-in provider catalog.
|
|
606
643
|
* `catalog` 是 agent catalog base URL(buyer 的 negotiate_buyer_task 发现商家用)。
|
|
607
644
|
*/
|
|
608
|
-
async function buildChatKernel(profile, dataDir, catalog) {
|
|
609
|
-
const paths = ensurePathsForDir(dataDir ?? agentDataDir(profile.agent_id));
|
|
610
|
-
let models;
|
|
611
|
-
let model;
|
|
612
|
-
let connector;
|
|
613
|
-
let thinkingLevel;
|
|
614
|
-
let merchantClient;
|
|
615
|
-
let broker;
|
|
616
|
-
let commerceClient;
|
|
617
|
-
if (isFakeProvider(profile)) {
|
|
618
|
-
({ models, model } = createFakeChatModels());
|
|
619
|
-
if (profile.role === "buyer") {
|
|
620
|
-
const { FakeCommerceConnector, fakeConnectorProduct } = await import("./agent/connector/fake-connector.js");
|
|
621
|
-
connector = new FakeCommerceConnector([
|
|
622
|
-
fakeConnectorProduct(),
|
|
623
|
-
fakeConnectorProduct({
|
|
624
|
-
sku: "sku-002",
|
|
625
|
-
title: "机制陶瓷杯",
|
|
626
|
-
price: 59,
|
|
627
|
-
stock: 0,
|
|
628
|
-
merchant_id: "merchant-002",
|
|
629
|
-
}),
|
|
630
|
-
]);
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
// Offline merchant chat: deterministic catalog client + dummy-scope
|
|
634
|
-
// credentials so the capability pack is exercisable without a gateway.
|
|
635
|
-
const { FakeMerchantClient, fakeMerchantProduct } = await import("./agent/merchant/fake-merchant-client.js");
|
|
636
|
-
const { StaticCredentialBroker } = await import("./agent/merchant/credential-broker.js");
|
|
637
|
-
merchantClient = new FakeMerchantClient({
|
|
638
|
-
products: [fakeMerchantProduct()],
|
|
639
|
-
});
|
|
640
|
-
broker = new StaticCredentialBroker({
|
|
641
|
-
negotiation: "fake-negotiation-token",
|
|
642
|
-
catalog: "fake-catalog-token",
|
|
643
|
-
inventory: "fake-inventory-token",
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
const { builtinModels } = await import("@earendil-works/pi-ai/providers/all");
|
|
649
|
-
const collection = builtinModels();
|
|
650
|
-
const found = collection.getModel(profile.model.provider, profile.model.model);
|
|
651
|
-
if (found === undefined) {
|
|
652
|
-
process.stderr.write(`no built-in model ${profile.model.provider}/${profile.model.model}; check the profile\n`);
|
|
653
|
-
throw new ProfileError(`no built-in model ${profile.model.provider}/${profile.model.model}`);
|
|
654
|
-
}
|
|
655
|
-
models = collection;
|
|
656
|
-
model = found;
|
|
657
|
-
thinkingLevel = resolveThinkingLevel(profile);
|
|
658
|
-
if (profile.role === "buyer") {
|
|
659
|
-
const { ShoppingCliConnector } = await import("./agent/connector/http-connector.js");
|
|
660
|
-
connector = new ShoppingCliConnector(profile.commerce.base_url, {
|
|
661
|
-
buyerBootstrapToken: process.env.SHOPPING_BUYER_BOOTSTRAP_TOKEN,
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
// Real gateway: negotiation client + scoped merchant client + broker.
|
|
665
|
-
const { ProfileCredentialBroker } = await import("./agent/merchant/credential-broker.js");
|
|
666
|
-
broker = new ProfileCredentialBroker(profile);
|
|
667
|
-
try {
|
|
668
|
-
commerceClient = buildClient(profile);
|
|
669
|
-
}
|
|
670
|
-
catch {
|
|
671
|
-
// No negotiation token: negotiation tools fail closed at call time.
|
|
672
|
-
// Catalog/inventory 只读工具走公开搜索端点,不依赖该 token,仍会挂载。
|
|
673
|
-
commerceClient = undefined;
|
|
674
|
-
if (profile.role === "merchant") {
|
|
675
|
-
process.stderr.write(`[kiwi] 未设置 ${profile.commerce.token_env}:磋商工具停用(fail closed),` +
|
|
676
|
-
"目录/库存只读工具仍可用。\n");
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
if (profile.role === "merchant") {
|
|
680
|
-
const { HttpMerchantClient } = await import("./agent/merchant/merchant-client.js");
|
|
681
|
-
merchantClient = new HttpMerchantClient(profile.commerce.base_url, broker);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
// KIWI_MODE env: start the chat already in the given write mode (e.g.
|
|
685
|
-
// autopilot for autonomous negotiation) — no manual /mode needed.
|
|
686
|
-
const kiwiMode = process.env.KIWI_MODE;
|
|
687
|
-
const mode = isAgentMode(kiwiMode) ? kiwiMode : undefined;
|
|
688
|
-
if (kiwiMode !== undefined && mode === undefined) {
|
|
689
|
-
process.stderr.write(`unknown KIWI_MODE ${kiwiMode}(可选 ${["manual", "supervised", "autopilot"].join("/")})\n`);
|
|
690
|
-
}
|
|
691
|
-
return AgentKernel.open({
|
|
692
|
-
profile,
|
|
693
|
-
paths,
|
|
694
|
-
models,
|
|
695
|
-
model,
|
|
696
|
-
...(mode !== undefined ? { mode } : {}),
|
|
697
|
-
...(connector !== undefined ? { connector } : {}),
|
|
698
|
-
...(commerceClient !== undefined ? { commerceClient } : {}),
|
|
699
|
-
...(merchantClient !== undefined ? { merchantClient } : {}),
|
|
700
|
-
...(broker !== undefined ? { broker } : {}),
|
|
701
|
-
...(catalog !== undefined ? { catalog } : {}),
|
|
702
|
-
...(thinkingLevel !== undefined ? { thinkingLevel } : {}),
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
645
|
/** kiwi 包根目录(dist/cli.js 的上级)——全局 `kiwi` 在任意 cwd 也能解析示例 profile。 */
|
|
706
646
|
function packageRoot() {
|
|
707
647
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -735,6 +675,7 @@ async function cmdChat(args) {
|
|
|
735
675
|
const profile = args.profile !== undefined ? loadProfile(args.profile) : defaultChatProfile();
|
|
736
676
|
// A2A 节点 + buyer 磋商工具共用一个 catalog:kernel 构建前先解析。
|
|
737
677
|
const catalog = args.catalog ?? process.env.KIWI_CATALOG_URL ?? DEFAULT_CATALOG_URL;
|
|
678
|
+
const merchantToken = process.env.KIWI_MERCHANT_TOKEN || "";
|
|
738
679
|
const kernel = await buildChatKernel(profile, args.dataDir, catalog);
|
|
739
680
|
const kernels = [kernel];
|
|
740
681
|
const reload = async (file) => {
|
|
@@ -758,6 +699,7 @@ async function cmdChat(args) {
|
|
|
758
699
|
profile: p,
|
|
759
700
|
catalog,
|
|
760
701
|
preferredPort: args.port,
|
|
702
|
+
...(merchantToken ? { ownerToken: merchantToken } : {}),
|
|
761
703
|
ownerTokenSecret: process.env.KIWI_CATALOG_OWNER_TOKEN_SECRET,
|
|
762
704
|
});
|
|
763
705
|
process.stderr.write(`[a2a] ${node.role}@${node.url}${node.catalogAgentId !== undefined ? ` registered ${node.catalogAgentId}` : ""}\n`);
|
|
@@ -969,20 +911,25 @@ async function cmdMerchantPublish(args) {
|
|
|
969
911
|
return EXIT.CONFIG;
|
|
970
912
|
}
|
|
971
913
|
const ownerTokenSecret = process.env.KIWI_CATALOG_OWNER_TOKEN_SECRET;
|
|
972
|
-
|
|
973
|
-
|
|
914
|
+
const merchantToken = process.env.KIWI_MERCHANT_TOKEN || "";
|
|
915
|
+
if (!merchantToken && !ownerTokenSecret) {
|
|
916
|
+
process.stderr.write("需要 KIWI_MERCHANT_TOKEN(随机 token,推荐)或 KIWI_CATALOG_OWNER_TOKEN_SECRET(legacy HMAC)\n");
|
|
974
917
|
return EXIT.CONFIG;
|
|
975
918
|
}
|
|
976
919
|
const catalog = args.catalog ?? process.env.KIWI_CATALOG_URL ?? DEFAULT_CATALOG_URL;
|
|
977
920
|
const report = await merchantPublish({
|
|
978
921
|
profile,
|
|
979
922
|
catalogBaseUrl: catalog,
|
|
980
|
-
|
|
923
|
+
...(merchantToken ? { ownerToken: merchantToken } : {}),
|
|
924
|
+
...(ownerTokenSecret ? { ownerTokenSecret } : {}),
|
|
981
925
|
shoppingCliDb,
|
|
982
926
|
...(args.shoppingCliPath !== undefined ? { shoppingCliPath: args.shoppingCliPath } : {}),
|
|
983
927
|
...(args.shoppingCliMerchant !== undefined
|
|
984
928
|
? { shoppingCliMerchant: args.shoppingCliMerchant }
|
|
985
929
|
: {}),
|
|
930
|
+
...(args.allowEmptyProjectionReconcile
|
|
931
|
+
? { allowEmptyProjectionReconcile: true }
|
|
932
|
+
: {}),
|
|
986
933
|
});
|
|
987
934
|
printJson(report);
|
|
988
935
|
return report.ok ? EXIT.OK : EXIT.CONFIG;
|
|
@@ -1003,6 +950,10 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1003
950
|
const hasHelp = argv.includes("--help") || argv.includes("-h");
|
|
1004
951
|
const firstArg = argv.find((a) => !a.startsWith("-"));
|
|
1005
952
|
// 产品层命令组帮助优先于全局帮助(`kiwi buyer --help` → buyer 帮助)
|
|
953
|
+
if (hasHelp && firstArg === "weixin") {
|
|
954
|
+
process.stdout.write(weixinUsage());
|
|
955
|
+
return EXIT.OK;
|
|
956
|
+
}
|
|
1006
957
|
if (hasHelp && (firstArg === "buyer" || firstArg === "merchant" || firstArg === "network")) {
|
|
1007
958
|
const help = productHelp(firstArg);
|
|
1008
959
|
if (help !== "") {
|
|
@@ -1050,6 +1001,22 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1050
1001
|
return await cmdTui(args);
|
|
1051
1002
|
if (cmd === "chat")
|
|
1052
1003
|
return await cmdChat(args);
|
|
1004
|
+
if (cmd === "weixin") {
|
|
1005
|
+
return await cmdWeixin({
|
|
1006
|
+
profile: args.profile,
|
|
1007
|
+
dataDir: args.dataDir,
|
|
1008
|
+
allow: args.weixinAllow,
|
|
1009
|
+
relogin: args.relogin,
|
|
1010
|
+
// 审查 P2-L:weixin 命令 A2A 节点默认关(headless 控制面收窄攻击面),
|
|
1011
|
+
// 仅 --a2a 显式开启(--no-a2a 仍可覆盖)。裸 kiwi/chat 命令的
|
|
1012
|
+
// A2A 默认开不受影响(各自按 noA2a 判断)。
|
|
1013
|
+
a2a: args.a2aExplicit && !args.noA2a,
|
|
1014
|
+
port: args.port,
|
|
1015
|
+
qrScale: args.qrScale,
|
|
1016
|
+
noQr: args.noQr,
|
|
1017
|
+
catalog: args.catalog,
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1053
1020
|
if (cmd === "init") {
|
|
1054
1021
|
const result = await runInit({
|
|
1055
1022
|
dir: requireDir(args),
|
|
@@ -1088,6 +1055,24 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1088
1055
|
printJson(computeHandoffMetrics(byNegotiation));
|
|
1089
1056
|
return EXIT.OK;
|
|
1090
1057
|
}
|
|
1058
|
+
if (cmd === "catalog") {
|
|
1059
|
+
// `kiwi catalog serve` —— 前台启动独立 kiwi-catalog 服务(CURRENT-DOCS)。
|
|
1060
|
+
const sub = args.command[1] ?? "";
|
|
1061
|
+
if (sub === "serve") {
|
|
1062
|
+
const result = catalogServe({
|
|
1063
|
+
db: args.catalogDb,
|
|
1064
|
+
host: args.catalogHost,
|
|
1065
|
+
port: args.port,
|
|
1066
|
+
});
|
|
1067
|
+
if (!result.ok) {
|
|
1068
|
+
process.stderr.write(`${result.error ?? "kiwi-catalog 启动失败"}\n`);
|
|
1069
|
+
return EXIT.CONFIG;
|
|
1070
|
+
}
|
|
1071
|
+
return EXIT.OK;
|
|
1072
|
+
}
|
|
1073
|
+
process.stderr.write("usage: kiwi catalog serve [--db <file>] [--host <host>] [--port <port>]\n");
|
|
1074
|
+
return EXIT.CONFIG;
|
|
1075
|
+
}
|
|
1091
1076
|
if (cmd === "down") {
|
|
1092
1077
|
const result = await runDown(requireDir(args));
|
|
1093
1078
|
printJson(result);
|