@itpay/cli 0.1.1 → 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,10 +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") {
849
+ const claimedSession = await ensureBuyerSessionForVaultAccess(flags);
841
850
  const grants = await listBuyerAgentReadGrants(flags);
842
851
  output(buyerRunOutput({
843
852
  status: "agent_read_grants",
844
853
  ...grants,
854
+ buyer_session: buyerSessionClaimStatus(claimedSession),
845
855
  agent_next_actions: grants.agent_readable_grants?.length ? ["read_agent_grant_view"] : ["wait_for_human_agent_read_grant"]
846
856
  }));
847
857
  return;
@@ -849,6 +859,7 @@ async function buyer(command, rest, flags) {
849
859
  if (action === "read" || action === "show") {
850
860
  const grantID = flags.grant || flags.grant_id || flags.agent_read_grant_id || positional(rest, 2);
851
861
  if (!grantID) throw new Error("agent_read_grant_id is required");
862
+ await ensureBuyerSessionForVaultAccess(flags);
852
863
  const view = await readBuyerAgentReadGrant(grantID, flags);
853
864
  output(buyerRunOutput({
854
865
  status: "agent_read_grant_view",
@@ -859,6 +870,7 @@ async function buyer(command, rest, flags) {
859
870
  }
860
871
  }
861
872
  if (subcommand === "read") {
873
+ await ensureBuyerSessionForVaultAccess(flags);
862
874
  const view = await readBuyerVaultArtifactGrant(flags);
863
875
  output(buyerRunOutput({
864
876
  status: "agent_read_grant_view",
@@ -1317,6 +1329,20 @@ async function maybeClaimBuyerSessionFromAuthAction(action, flags = {}) {
1317
1329
  }
1318
1330
  }
1319
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
+
1320
1346
  async function maybeClaimBuyerSessionForCheckout(checkout, flags = {}) {
1321
1347
  if (!checkout?.checkout_id) return null;
1322
1348
  if (readSessionToken()) return null;
@@ -1325,19 +1351,76 @@ async function maybeClaimBuyerSessionForCheckout(checkout, flags = {}) {
1325
1351
  return await maybeClaimBuyerSessionFromAuthAction(action, flags);
1326
1352
  }
1327
1353
 
1354
+ async function ensureBuyerSessionForVaultAccess(flags = {}) {
1355
+ if (readSessionToken()) return null;
1356
+ const checkoutID =
1357
+ flags.checkout ||
1358
+ flags.checkout_id ||
1359
+ readState().last_core_checkout_id ||
1360
+ readState().last_core_auth_checkout_id;
1361
+ if (checkoutID) {
1362
+ try {
1363
+ const checkout = await getBuyerCheckout(checkoutID, flags);
1364
+ rememberCoreAuthAction(checkout.checkout_id || checkoutID, checkout.human_action);
1365
+ const claimed = await maybeClaimBuyerSessionForCheckout(checkout, { ...flags, quiet: true });
1366
+ if (claimed || readSessionToken()) return claimed;
1367
+ } catch {
1368
+ // Continue with the locally remembered auth action below.
1369
+ }
1370
+ const action = readCoreAuthAction(checkoutID);
1371
+ const claimed = await maybeClaimBuyerSessionFromAuthAction(action, { ...flags, quiet: true });
1372
+ if (claimed || readSessionToken()) return claimed;
1373
+ }
1374
+ const state = readState();
1375
+ const entries = Object.entries(state.core_auth_actions || {});
1376
+ for (const [, action] of entries.reverse()) {
1377
+ const claimed = await maybeClaimBuyerSessionFromAuthAction(action, { ...flags, quiet: true });
1378
+ if (claimed || readSessionToken()) return claimed;
1379
+ }
1380
+ return null;
1381
+ }
1382
+
1328
1383
  function parseBuyerAuthActionURL(action) {
1329
- const rawURL = action?.url || action?.auth_url || "";
1330
- if (!rawURL) return {};
1331
- try {
1332
- const parsed = new URL(rawURL);
1333
- const match = parsed.pathname.match(/\/v1\/buyer\/auth-sessions\/([^/]+)$/);
1334
- return {
1335
- authSessionID: match ? decodeURIComponent(match[1]) : "",
1336
- displayToken: parsed.searchParams.get("display_token") || ""
1337
- };
1338
- } catch {
1339
- 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
+ }
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]));
1340
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;
1341
1424
  }
1342
1425
 
1343
1426
  function rememberCoreAuthAction(checkoutID, action) {
@@ -1351,9 +1434,9 @@ function rememberCoreAuthAction(checkoutID, action) {
1351
1434
  next[checkoutID] = {
1352
1435
  kind: "auth_qr",
1353
1436
  id: action.id || action.auth_session_id || parsed.authSessionID,
1354
- auth_session_id: action.auth_session_id || parsed.authSessionID,
1355
- url: action.url,
1356
- 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,
1357
1440
  expires_at: action.expires_at || null,
1358
1441
  saved_at: new Date().toISOString()
1359
1442
  };
@@ -1487,7 +1570,7 @@ function isBuyerDeliveryComplete(result) {
1487
1570
  }
1488
1571
 
1489
1572
  function buyerRunOutput(value = {}) {
1490
- return stripInternalBuyerFields({
1573
+ return normalizeBuyerMoneyFields(stripInternalBuyerFields({
1491
1574
  schema_version: "itp.buyer.v1",
1492
1575
  docs: value.docs || buyerDocsFor(value),
1493
1576
  ...value,
@@ -1496,7 +1579,29 @@ function buyerRunOutput(value = {}) {
1496
1579
  claim_token_included: false,
1497
1580
  provider_raw_payload_included: false
1498
1581
  }
1499
- });
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;
1500
1605
  }
1501
1606
 
1502
1607
  function buyerDocsFor(value = {}) {
@@ -3602,7 +3707,7 @@ function coreURL(pathname, flags = {}) {
3602
3707
  }
3603
3708
 
3604
3709
  function coreApiBase(flags = {}) {
3605
- 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";
3606
3711
  return String(base).replace(/\/$/, "");
3607
3712
  }
3608
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
  ],
@@ -30,12 +30,14 @@
30
30
  "The claim link is short-lived and single-use.",
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
- "After the human grants access, the agent should discover the grant with `itp buyer vault grants list ...`; do not ask the human to paste the grant id.",
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": [
37
38
  "Do not ask the human to paste the claim link or key into chat.",
38
39
  "Do not fetch the claim page with CLI, curl, browser automation, or an agent tool.",
40
+ "Do not ask the human for auth session IDs, display tokens, buyer session tokens, or portal tokens.",
39
41
  "Do not store raw content in local run files."
40
42
  ],
41
43
  "next_docs": [
@@ -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.",
@@ -10,7 +10,7 @@
10
10
  "A checkout/order has delivered a vault artifact and the human granted agent-readable access."
11
11
  ],
12
12
  "required_state": {
13
- "needs": ["authenticated buyer account session", "current agent device binding", "order_id or checkout_id or vault_artifact_id"],
13
+ "needs": ["checkout_id or authenticated buyer account session", "current agent device binding", "order_id or checkout_id or vault_artifact_id"],
14
14
  "must_not_need": ["human portal token", "claim token", "passkey credential", "raw protected payload", "grant id copied by the human"]
15
15
  },
16
16
  "commands": [
@@ -37,14 +37,17 @@
37
37
  ],
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
+ "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.",
40
42
  "Do not ask the human to copy or paste `agent_read_grant_id`; discover it with `buyer vault grants list`.",
41
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.",
42
44
  "Use only fields returned in the grant view. Do not infer that unreturned fields are accessible.",
43
- "If the command returns 401 or buyer_session_invalid, run `itp buyer auth status --json` and ask the human to reauthorize through a normal checkout/account flow."
45
+ "If the command still returns 401 or buyer_session_invalid after using `--checkout`, run `itp buyer checkout status <checkout_id> --json`, then retry `itp buyer vault grants list --checkout <checkout_id> --json`. Ask the human to reauthorize only if the checkout/auth handoff has expired."
44
46
  ],
45
47
  "forbidden": [
48
+ "Do not open, click through, scrape, or automate the human web UI yourself.",
46
49
  "Do not open, scrape, or screenshot the human portal to obtain protected content.",
47
- "Do not request passkey credentials, portal tokens, claim links, claim tokens, storage refs, provider AppCode, provider keys, or raw payloads.",
50
+ "Do not request passkey credentials, portal tokens, claim links, claim tokens, auth session IDs, display tokens, session tokens, storage refs, provider AppCode, provider keys, or raw payloads.",
48
51
  "Do not use another agent's grant id. Grants are scoped to the exact buyer account session and agent device.",
49
52
  "Do not cache selected fields beyond the task context unless the user explicitly asks you to create a local artifact."
50
53
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "0.1.1",
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
@@ -126,8 +136,13 @@ registered name or run fuzzy search first.
126
136
  `delivery_claimable`, `check_email`, and `claim_link_sent`, but must not
127
137
  fetch or reveal protected content.
128
138
  11. If the human uses Passkey to authorize agent-readable vault access, do not
129
- ask them to paste content, portal text, claim links, or grant IDs. Run
139
+ ask them to paste content, portal text, claim links, session tokens, auth
140
+ session IDs, display tokens, or grant IDs. Run
130
141
  `itp buyer vault grants list ...` and then `itp buyer vault read ...`.
142
+ The CLI automatically restores the buyer agent session from the checkout
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.
131
146
  Use only the fields returned by that command.
132
147
  12. If the user asks you to analyze, compare, summarize, install, or otherwise
133
148
  use a delivered result, you may ask them to open the ItPay claim/account
@@ -140,6 +155,10 @@ registered name or run fuzzy search first.
140
155
  broad keyword. For enterprise precise lookup, `company_name_or_credit_no`
141
156
  must be exact; otherwise warn the user that the query may waste the paid
142
157
  lookup.
158
+ 15. Do not operate ItPay by opening the human web UI yourself. Use the CLI for
159
+ catalog, cart, checkout, payment wait, delivery status, grant discovery,
160
+ and vault reads. Browser/UI pages are for the human to scan, pay, claim,
161
+ reveal, and approve.
143
162
 
144
163
  ## Docs Directory
145
164