@itpay/cli 0.1.2 → 0.1.4

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 CHANGED
@@ -34,16 +34,18 @@ openclaw
34
34
  Default API endpoint:
35
35
 
36
36
  ```text
37
- http://localhost:3000
37
+ https://dev.api.itpay.ai
38
38
  ```
39
39
 
40
- Override it for ItPay staging or production:
40
+ Override it for local development, staging, or production:
41
41
 
42
42
  ```bash
43
- export ITPAY_API_BASE=https://your-itpay-core.example.com
44
- export ITPAY_CORE_BASE_URL=https://your-itpay-core.example.com
43
+ export ITPAY_API_BASE=http://127.0.0.1:18080
44
+ export ITPAY_CORE_API_BASE=http://127.0.0.1:18080
45
45
  ```
46
46
 
47
+ Production release will switch the package default to `https://api.itpay.ai`.
48
+
47
49
  ## Repository Layout
48
50
 
49
51
  ```text
@@ -184,10 +186,11 @@ node ./bin/itp --version
184
186
 
185
187
  ## Basic User Flow
186
188
 
187
- Set API endpoint if not using local backend:
189
+ The default endpoint is the AWS dev backend. Set API endpoint only when testing
190
+ local or another environment:
188
191
 
189
192
  ```bash
190
- export ITPAY_API_BASE=https://your-itpay-core.example.com
193
+ export ITPAY_API_BASE=http://127.0.0.1:18080
191
194
  ```
192
195
 
193
196
  For the current buyer commerce flow, search the catalog, create a cart/checkout,
package/bin/itp CHANGED
@@ -8,8 +8,8 @@ import { execFileSync } from "node:child_process";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import QRCode from "qrcode";
10
10
 
11
- const VERSION = "0.1.2";
12
- const DEFAULT_API_BASE = process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_BASE_URL || "http://localhost:3000";
11
+ const VERSION = "0.1.4";
12
+ const DEFAULT_API_BASE = process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_API_BASE || process.env.ITPAY_CORE_BASE_URL || "https://dev.api.itpay.ai";
13
13
  const CONFIG_DIR = path.join(os.homedir(), ".itp");
14
14
  const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
15
15
  const STATE_PATH = path.join(CONFIG_DIR, "state.json");
@@ -198,9 +198,9 @@ async function main() {
198
198
  "buyer catalog search --query 企业工商 --category business_data_api --provider itpay_enterprise_data --json",
199
199
  "buyer catalog get --variant var_pubg_couple_skin_cny20 --json",
200
200
  "buyer cart create --variant var_pubg_couple_skin_cny20 --json",
201
- "buyer cart create --variants var_itpay_enterprise_precise_lookup_cny05,var_itpay_enterprise_fuzzy_search_cny01 --quantities 1,1 --json",
201
+ "buyer cart create --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=京东 --json",
202
202
  "buyer cart show <cart_id> --json",
203
- "buyer cart add <cart_id> --variant var_itpay_enterprise_fuzzy_search_cny01 --quantity 1 --json",
203
+ "buyer cart add <cart_id> --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=美团 --quantity 1 --json",
204
204
  "buyer cart remove <cart_id> --line <cart_line_item_id> --json",
205
205
  "buyer shelf manifest --json",
206
206
  "buyer shelf snapshot --version <catalog_version> --json",
@@ -715,11 +715,12 @@ async function buyer(command, rest, flags) {
715
715
  const checkoutID = flags.checkout || flags.checkout_id || positional(rest, 1) || readState().last_core_checkout_id;
716
716
  if (!checkoutID) throw new Error("checkout_id is required");
717
717
  const checkout = await getBuyerCheckout(checkoutID, flags);
718
- await maybeClaimBuyerSessionForCheckout(checkout, flags);
718
+ const claimedSession = await maybeClaimBuyerSessionForCheckout(checkout, flags);
719
719
  output(buyerRunOutput({
720
720
  status: checkout.delivery_status || checkout.status,
721
721
  checkout,
722
722
  delivery: checkout.delivery,
723
+ buyer_session: buyerSessionClaimStatus(claimedSession),
723
724
  agent_next_actions: deliveryAwareAgentNextActions(checkout),
724
725
  optional_agent_read_grant: optionalAgentReadGrantHint(checkout.checkout_id, checkout)
725
726
  }));
@@ -759,15 +760,21 @@ async function buyer(command, rest, flags) {
759
760
  }
760
761
  }));
761
762
  return;
763
+ }
764
+ }
765
+ const claimedSession = await maybeClaimBuyerSessionForCheckout(checkout, flags);
766
+ if (checkout.payment_intent_id) {
767
+ const intent = await getBuyerPaymentIntent(checkout.payment_intent_id, flags);
768
+ await renderItPayPaymentAction(intent, flags);
769
+ output(buyerRunOutput({
770
+ status: intent.status === "verified" ? "payment_verified" : "waiting_user_payment",
771
+ checkout,
772
+ payment_intent: intent,
773
+ buyer_session: buyerSessionClaimStatus(claimedSession),
774
+ agent_next_actions: intent.agent_next_actions || checkout.agent_next_actions || ["wait_payment"]
775
+ }));
776
+ return;
762
777
  }
763
- }
764
- await maybeClaimBuyerSessionForCheckout(checkout, flags);
765
- if (checkout.payment_intent_id) {
766
- const intent = await getBuyerPaymentIntent(checkout.payment_intent_id, flags);
767
- await renderItPayPaymentAction(intent, flags);
768
- output(buyerRunOutput({ status: intent.status === "verified" ? "payment_verified" : "waiting_user_payment", checkout, payment_intent: intent, agent_next_actions: intent.agent_next_actions || checkout.agent_next_actions || ["wait_payment"] }));
769
- return;
770
- }
771
778
  if (checkout.agent_next_actions?.includes("create_payment_intent") || checkout.next_required_action === "create_payment_intent") {
772
779
  const intent = await createBuyerPaymentIntent(checkout.checkout_id, flags);
773
780
  await renderItPayPaymentAction(intent, flags);
@@ -778,6 +785,7 @@ async function buyer(command, rest, flags) {
778
785
  status: checkout.delivery_status || checkout.status,
779
786
  checkout,
780
787
  delivery: checkout.delivery,
788
+ buyer_session: buyerSessionClaimStatus(claimedSession),
781
789
  agent_next_actions: deliveryAwareAgentNextActions(checkout),
782
790
  optional_agent_read_grant: optionalAgentReadGrantHint(checkout.checkout_id, checkout)
783
791
  }));
@@ -838,11 +846,12 @@ async function buyer(command, rest, flags) {
838
846
  if (subcommand === "grants") {
839
847
  const action = rest[1] && !String(rest[1]).startsWith("--") ? rest[1] : "list";
840
848
  if (action === "list") {
841
- await ensureBuyerSessionForVaultAccess(flags);
849
+ const claimedSession = await ensureBuyerSessionForVaultAccess(flags);
842
850
  const grants = await listBuyerAgentReadGrants(flags);
843
851
  output(buyerRunOutput({
844
852
  status: "agent_read_grants",
845
853
  ...grants,
854
+ buyer_session: buyerSessionClaimStatus(claimedSession),
846
855
  agent_next_actions: grants.agent_readable_grants?.length ? ["read_agent_grant_view"] : ["wait_for_human_agent_read_grant"]
847
856
  }));
848
857
  return;
@@ -1320,6 +1329,20 @@ async function maybeClaimBuyerSessionFromAuthAction(action, flags = {}) {
1320
1329
  }
1321
1330
  }
1322
1331
 
1332
+ function buyerSessionClaimStatus(claimed = null) {
1333
+ const config = readConfig();
1334
+ const hasSession = Boolean(readSessionToken());
1335
+ if (!claimed && !hasSession) return undefined;
1336
+ return {
1337
+ status: "buyer_session_saved",
1338
+ session_stored: true,
1339
+ buyer_account_id: claimed?.buyer_account_id || config.account_id || null,
1340
+ agent_device_id: claimed?.agent_device_id || config.device_id || null,
1341
+ token_included: false,
1342
+ agent_next_actions: ["reuse_buyer_session", "list_agent_read_grants"]
1343
+ };
1344
+ }
1345
+
1323
1346
  async function maybeClaimBuyerSessionForCheckout(checkout, flags = {}) {
1324
1347
  if (!checkout?.checkout_id) return null;
1325
1348
  if (readSessionToken()) return null;
@@ -1358,18 +1381,46 @@ async function ensureBuyerSessionForVaultAccess(flags = {}) {
1358
1381
  }
1359
1382
 
1360
1383
  function parseBuyerAuthActionURL(action) {
1361
- const rawURL = action?.url || action?.auth_url || "";
1362
- if (!rawURL) return {};
1363
- try {
1364
- const parsed = new URL(rawURL);
1365
- const match = parsed.pathname.match(/\/v1\/buyer\/auth-sessions\/([^/]+)$/);
1366
- return {
1367
- authSessionID: match ? decodeURIComponent(match[1]) : "",
1368
- displayToken: parsed.searchParams.get("display_token") || ""
1369
- };
1370
- } catch {
1371
- return {};
1384
+ if (!action || typeof action !== "object") return {};
1385
+ let authSessionID = String(action.auth_session_id || "").trim();
1386
+ if (!authSessionID && String(action.id || "").startsWith("auth_")) {
1387
+ authSessionID = String(action.id).trim();
1388
+ }
1389
+ let displayToken = "";
1390
+ let sourceURL = "";
1391
+ for (const rawURL of buyerAuthActionCandidateURLs(action)) {
1392
+ try {
1393
+ const parsed = new URL(rawURL);
1394
+ const token = parsed.searchParams.get("display_token") || "";
1395
+ if (token && !displayToken) displayToken = token;
1396
+ const match = parsed.pathname.match(/(?:^|\/)(?:v1\/buyer\/auth-sessions|auth)\/([^/?#]+)(?:\/|$)/);
1397
+ if (match && !authSessionID) authSessionID = decodeURIComponent(match[1]);
1398
+ if (!sourceURL && (match || token)) sourceURL = rawURL;
1399
+ if (authSessionID && displayToken) break;
1400
+ } catch {
1401
+ // Ignore non-URL display entries.
1402
+ }
1372
1403
  }
1404
+ return { authSessionID, displayToken, sourceURL };
1405
+ }
1406
+
1407
+ function buyerAuthActionCandidateURLs(action) {
1408
+ const urls = [];
1409
+ for (const key of ["url", "web_url", "auth_url", "oauth_start_url", "mobile_wallet_url"]) {
1410
+ if (action?.[key]) urls.push(String(action[key]));
1411
+ }
1412
+ const presentationDisplay = action?.presentation?.display;
1413
+ if (Array.isArray(presentationDisplay)) {
1414
+ for (const entry of presentationDisplay) {
1415
+ if (entry?.url) urls.push(String(entry.url));
1416
+ }
1417
+ }
1418
+ if (Array.isArray(action?.display)) {
1419
+ for (const entry of action.display) {
1420
+ if (entry?.url) urls.push(String(entry.url));
1421
+ }
1422
+ }
1423
+ return urls;
1373
1424
  }
1374
1425
 
1375
1426
  function rememberCoreAuthAction(checkoutID, action) {
@@ -1383,9 +1434,9 @@ function rememberCoreAuthAction(checkoutID, action) {
1383
1434
  next[checkoutID] = {
1384
1435
  kind: "auth_qr",
1385
1436
  id: action.id || action.auth_session_id || parsed.authSessionID,
1386
- auth_session_id: action.auth_session_id || parsed.authSessionID,
1387
- url: action.url,
1388
- web_url: action.web_url || action.url,
1437
+ auth_session_id: parsed.authSessionID,
1438
+ url: action.url || parsed.sourceURL,
1439
+ web_url: action.web_url || action.url || parsed.sourceURL,
1389
1440
  expires_at: action.expires_at || null,
1390
1441
  saved_at: new Date().toISOString()
1391
1442
  };
@@ -1519,7 +1570,7 @@ function isBuyerDeliveryComplete(result) {
1519
1570
  }
1520
1571
 
1521
1572
  function buyerRunOutput(value = {}) {
1522
- return stripInternalBuyerFields({
1573
+ return normalizeBuyerMoneyFields(stripInternalBuyerFields({
1523
1574
  schema_version: "itp.buyer.v1",
1524
1575
  docs: value.docs || buyerDocsFor(value),
1525
1576
  ...value,
@@ -1528,7 +1579,29 @@ function buyerRunOutput(value = {}) {
1528
1579
  claim_token_included: false,
1529
1580
  provider_raw_payload_included: false
1530
1581
  }
1531
- });
1582
+ }));
1583
+ }
1584
+
1585
+ function normalizeBuyerMoneyFields(value) {
1586
+ if (Array.isArray(value)) return value.map((item) => normalizeBuyerMoneyFields(item));
1587
+ if (!value || typeof value !== "object") return value;
1588
+ const next = {};
1589
+ for (const [key, item] of Object.entries(value)) {
1590
+ next[key] = normalizeBuyerMoneyFields(item);
1591
+ }
1592
+ if (
1593
+ typeof next.amount === "number" &&
1594
+ Number.isFinite(next.amount) &&
1595
+ typeof next.currency === "string" &&
1596
+ next.currency.trim()
1597
+ ) {
1598
+ const currency = next.currency.trim().toUpperCase();
1599
+ next.amount_minor = Number.isInteger(next.amount) ? next.amount : Math.round(next.amount);
1600
+ next.amount_major = Number((next.amount_minor / 100).toFixed(2));
1601
+ next.display_amount = `${currency} ${(next.amount_minor / 100).toFixed(2)}`;
1602
+ next.amount_unit = "minor";
1603
+ }
1604
+ return next;
1532
1605
  }
1533
1606
 
1534
1607
  function buyerDocsFor(value = {}) {
@@ -3634,7 +3707,7 @@ function coreURL(pathname, flags = {}) {
3634
3707
  }
3635
3708
 
3636
3709
  function coreApiBase(flags = {}) {
3637
- const base = flags.api_base || flags.core_api_base || process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_API_BASE || process.env.ITPAY_CORE_BASE_URL || "http://127.0.0.1:18080";
3710
+ const base = flags.api_base || flags.core_api_base || process.env.ITPAY_API_BASE || process.env.ITPAY_CORE_API_BASE || process.env.ITPAY_CORE_BASE_URL || "https://dev.api.itpay.ai";
3638
3711
  return String(base).replace(/\/$/, "");
3639
3712
  }
3640
3713
 
@@ -53,8 +53,8 @@
53
53
  },
54
54
  {
55
55
  "intent": "add one more selected UCP Variant.id to an existing cart before checkout",
56
- "command": "itp buyer cart add <cart_id> --variant <variant_id> --quantity 1 --json",
57
- "success_signal": "response.cart.cart_id is unchanged and response.cart.line_items includes the new line"
56
+ "command": "itp buyer cart add <cart_id> --variant <variant_id> --input key=value --quantity 1 --json",
57
+ "success_signal": "response.cart.cart_id is unchanged; response.cart.line_items either includes a new line for different input/settings or increments quantity for an identical line"
58
58
  },
59
59
  {
60
60
  "intent": "remove one line from an existing cart before checkout",
@@ -77,12 +77,15 @@
77
77
  "Before cart creation, inspect product metadata input_schema_json and collect every required input field. Do not wait until after payment to ask for required query inputs.",
78
78
  "Enterprise fuzzy search requires input.company_name. This can be a keyword, brand, short name, or partial company name supplied by the user, such as 京东.",
79
79
  "Enterprise precise lookup requires input.company_name_or_credit_no. This must be a complete China mainland registered company name or unified social credit code. If the user only provides a brand/short name, resolve the exact name first or use fuzzy search before precise lookup.",
80
- "Use --input key=value for service-specific inputs. For multiple lines, prefer separate cart add commands when each line needs different input.",
80
+ "Use --input key=value for service-specific inputs. For multiple query lines, prefer separate cart add commands so each line locks its own input.",
81
+ "Cart line identity includes variant, offer, price, provider product, and normalized input. Only a completely identical line should merge by increasing quantity.",
82
+ "If the same variant has a different company_name, company_name_or_credit_no, page number, setting, or any other input value, it must remain a separate cart line.",
81
83
  "Before checkout create, confirm buyer email is available for secure delivery. If it is missing, ask the human for the email before checkout; do not guess or use a placeholder.",
82
84
  "Use cart_id as the checkout handoff.",
83
- "Use buyer cart show whenever you need current cart contents. Do not rely on chat memory for cart state.",
85
+ "Use buyer cart show before changing an existing cart and whenever you need current cart contents. Do not rely on chat memory for cart state.",
84
86
  "Use buyer cart add/remove before checkout when the user changes their mind. After checkout is created, treat the cart as locked.",
85
87
  "Do not override cart line_items during checkout.",
88
+ "Cart, checkout, and catalog JSON money fields use minor units. For CNY, amount=10 or expected_amount=10 means CNY 0.10. Tell humans the display_amount or converted major-unit amount, never interpret amount as yuan.",
86
89
  "If the user asks for several compatible products, add them to one cart and create one checkout. Do not split into separate checkouts unless ItPay rejects the cart or says split checkout is required.",
87
90
  "If the same cart is retried, expect the same checkout unless contact/client reference changes.",
88
91
  "The backend revalidates catalog, price, and delivery requirements.",
@@ -91,7 +94,8 @@
91
94
  "Do not tell the user auth_qr is a completed payment or payment proof.",
92
95
  "For first purchase, show the ItPay auth entry as one human orchestration entry; after provider OAuth callback the same checkout exposes payment.",
93
96
  "After showing auth_qr, poll checkout or run buyer checkout resume until identity_status becomes identity_resolved and payment_intent_id appears.",
94
- "When auth completes, CLI should claim and store the buyer session so a repeat purchase by the same agent/device can skip auth."
97
+ "When auth completes, CLI should claim and store the buyer session so a repeat purchase by the same agent/device can skip auth.",
98
+ "If checkout status/resume returns buyer_session.status=buyer_session_saved, the agent is now bound to that buyer account on this device. Continue with payment, delivery polling, or buyer vault commands; do not ask the human for a buyer session token."
95
99
  ],
96
100
  "forbidden": [
97
101
  "Use the UCP cart and buyer checkout commands for external-agent tests.",
@@ -47,6 +47,7 @@
47
47
  "Use stable categories: business_data_api, business_verification_api, identity_verification_api, phone_verification_api, risk_compliance_api, location_weather_api, finance_data_api.",
48
48
  "Use safe facets such as --use-case, --input-facet, --output-facet, --sensitivity-level, --delivery-mode, and --provider.",
49
49
  "Use product and variant metadata to explain options in user language.",
50
+ "Money values from catalog JSON use minor units. For CNY, amount=10 means CNY 0.10, not CNY 10. Prefer display_amount when present, or divide amount/amount_minor by 100 before telling the user.",
50
51
  "For products with requires_human_input=true, do not ask for identity numbers or phone numbers in chat; rely on ItPay human authorization/input.",
51
52
  "Do not create checkout until the user intent maps to one selected variant or the user confirms a cart."
52
53
  ],
@@ -31,6 +31,7 @@
31
31
  "The agent may explain that ItPay intentionally separates agent operations from raw secret access.",
32
32
  "If the human wants the agent to analyze or use the delivered result, ask them to click 'Give to Agent / 一键给 Agent' in the ItPay page and confirm with Passkey.",
33
33
  "After the human grants access, the agent should discover the grant with `itp buyer vault grants list --checkout <checkout_id> --json`; do not ask the human to paste the grant id. The CLI restores the buyer agent session from the checkout auth handoff when possible.",
34
+ "If `buyer vault grants list` returns buyer_session.status=buyer_session_saved, the agent should proceed with the returned grant list; the saved session is intentionally not printed.",
34
35
  "If the human later wants the agent to install or use something, wait for an explicit human-granted install/capability flow."
35
36
  ],
36
37
  "forbidden": [
@@ -28,6 +28,7 @@
28
28
  ],
29
29
  "agent_rules": [
30
30
  "Map user intent to the variant description and price.",
31
+ "Catalog JSON money fields use minor units. For CNY, amount=10 means CNY 0.10 and amount=50 means CNY 0.50. Use display_amount when present, or divide by 100 before explaining price to the user.",
31
32
  "For API products, explain what the API checks or returns, quota unit, sensitivity level, required human input, and whether Passkey/WebAuthn reveal is required.",
32
33
  "If metadata says agent_may_view_raw_result=false, tell the user secure delivery/reveal is human-first and do not request the raw result.",
33
34
  "For enterprise fuzzy search, tell the user the required input is a company keyword/short name/brand. The user can say something broad like 京东; the service is designed to return candidates.",
@@ -39,10 +39,12 @@
39
39
  "Use --json for every ItPay command.",
40
40
  "Search and explain options before buying unless the user already named a variant.",
41
41
  "Add the selected UCP Variant.id to cart; do not bypass cart for CORE-028 flows.",
42
+ "When explaining prices, remember ItPay JSON money fields use minor units. For CNY, amount=10 means CNY 0.10. Prefer display_amount when present.",
42
43
  "Before checkout, make sure a buyer delivery email is available. If no email is known from the current buyer context, ask the human for it before creating checkout.",
43
44
  "Do not invent, guess, or use a placeholder email. Secure delivery and account/order access depend on the buyer email.",
44
45
  "For first purchase, show auth_qr as the ItPay first-purchase entry: the human first approves provider login/registration/profile sharing, then ItPay should continue the same checkout to payment.",
45
46
  "After showing auth_qr, keep running/resuming the same checkout unless the human explicitly asks you to pause. Do not stop merely because a QR was displayed.",
47
+ "When a buyer command returns buyer_session.status=buyer_session_saved, the CLI has stored the buyer account session for this agent device. You may continue with checkout/payment or `buyer vault` commands without asking the human for a token.",
46
48
  "Use high-level itp buy when possible; it is designed to show the first-purchase entry, wait/resume, continue to payment, wait for verification, and poll delivery.",
47
49
  "Show the returned payment QR exactly as provided.",
48
50
  "Payment truth comes only from payment_intent.verified.",
@@ -38,6 +38,7 @@
38
38
  "agent_rules": [
39
39
  "Only read through `buyer vault` commands after the human has explicitly approved the agent grant in the ItPay portal.",
40
40
  "Prefer `itp buyer vault grants list --checkout <checkout_id> --json`; the CLI can automatically restore the buyer agent session from the checkout auth handoff when possible.",
41
+ "If the response includes buyer_session.status=buyer_session_saved, continue directly to `buyer vault grants read <agent_read_grant_id> --json`; do not ask the human for session tokens, auth IDs, or grant IDs.",
41
42
  "Do not ask the human to copy or paste `agent_read_grant_id`; discover it with `buyer vault grants list`.",
42
43
  "If no grant is returned, tell the human to open their ItPay account portal, reveal with Passkey, choose fields, and confirm one-key agent authorization.",
43
44
  "Use only fields returned in the grant view. Do not infer that unreturned fields are accessible.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "ItPay CLI, buyer skill, and agent-readable docs for agent-native commerce.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,7 +67,7 @@ itp buyer catalog get --variant <variant_id> --json
67
67
  itp buyer cart create --variant <variant_id> --json
68
68
  itp buyer cart create --variants <variant_id_1>,<variant_id_2> --quantities 1,1 --json
69
69
  itp buyer cart show <cart_id> --json
70
- itp buyer cart add <cart_id> --variant <variant_id> --quantity 1 --json
70
+ itp buyer cart add <cart_id> --variant <variant_id> --input key=value --quantity 1 --json
71
71
  itp buyer cart remove <cart_id> --line <cart_line_item_id> --json
72
72
  itp buyer checkout create --cart <cart_id> --email <buyer_email> --phone <buyer_phone> --json
73
73
  itp buyer checkout resume <checkout_id> --json
@@ -82,10 +82,18 @@ Enterprise data products require query input at cart time:
82
82
 
83
83
  ```bash
84
84
  itp buyer cart create --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=京东 --json
85
+ itp buyer cart show <cart_id> --json
86
+ itp buyer cart add <cart_id> --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=美团 --json
85
87
  itp buyer cart create --variant var_itpay_enterprise_precise_lookup_cny05 --input company_name_or_credit_no=北京京东世纪贸易有限公司 --json
86
88
  itp buy var_itpay_enterprise_fuzzy_search_cny01 --sandbox --email <buyer_email> --input company_name=京东 --json
87
89
  ```
88
90
 
91
+ For cart edits, always read the server cart first with `buyer cart show`.
92
+ Cart line identity includes the variant, offer, price, provider product, and
93
+ normalized input. A fully identical line increments quantity; a different
94
+ company name, exact name, page number, setting, or other input must stay as a
95
+ separate line.
96
+
89
97
  Use fuzzy search when the user gives a short name, brand, keyword, or uncertain
90
98
  entity. Use precise lookup only after you have the exact China mainland
91
99
  registered company name or unified social credit code. If the user says
@@ -97,9 +105,11 @@ registered name or run fuzzy search first.
97
105
  1. Use `--json` for every ItPay command.
98
106
  2. Do not invent service IDs, variant IDs, checkout IDs, payment URLs, QR URLs,
99
107
  payment intent IDs, delivery IDs, or claim links.
100
- 3. When the user asks for several compatible services, create one cart with
101
- `--variants` and one checkout. Split only when ItPay rejects the cart or
102
- explicitly says split checkout is required.
108
+ 3. When the user asks for several compatible services, use one cart and one
109
+ checkout. Prefer `buyer cart create` for the first line, then `buyer cart
110
+ show` and `buyer cart add` for each additional query line so each service
111
+ input is locked to the correct cart line. Split only when ItPay rejects the
112
+ cart or explicitly says split checkout is required.
103
113
  4. Before checkout, make sure a buyer delivery email is available. If the CLI
104
114
  has no known buyer email, ask the human for the email; do not invent one,
105
115
  do not use placeholders, and do not proceed to checkout without it. The
@@ -130,7 +140,10 @@ registered name or run fuzzy search first.
130
140
  session IDs, display tokens, or grant IDs. Run
131
141
  `itp buyer vault grants list ...` and then `itp buyer vault read ...`.
132
142
  The CLI automatically restores the buyer agent session from the checkout
133
- auth handoff when possible. Use only the fields returned by that command.
143
+ auth handoff when possible. If the JSON includes
144
+ `buyer_session.status=buyer_session_saved`, continue with the returned
145
+ grants; the session token is intentionally stored locally and not printed.
146
+ Use only the fields returned by that command.
134
147
  12. If the user asks you to analyze, compare, summarize, install, or otherwise
135
148
  use a delivered result, you may ask them to open the ItPay claim/account
136
149
  page, click "Give to Agent / 一键给 Agent", choose fields, and confirm with