@itpay/cli 2.0.12 → 2.0.14

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.
Files changed (62) hide show
  1. package/README.md +14 -7
  2. package/dist/src/commands/compatibility.js +5 -0
  3. package/dist/src/commands/docs.js +1 -0
  4. package/dist/src/commands/guidance.js +10 -22
  5. package/dist/src/commands/install.js +3 -3
  6. package/dist/src/commands/readyz.js +2 -2
  7. package/dist/src/commands/services.js +17 -7
  8. package/dist/src/commands/skill.js +11 -11
  9. package/dist/src/main.js +50 -18
  10. package/dist/src/state/config.js +5 -4
  11. package/docs/agent/buyer/cart-checkout.json +16 -3
  12. package/docs/agent/buyer/catalog-list.json +1 -0
  13. package/docs/agent/buyer/identity-and-sessions.json +35 -14
  14. package/docs/agent/buyer/install-and-setup.json +28 -8
  15. package/docs/agent/buyer/orders-refunds.json +13 -2
  16. package/docs/agent/buyer/payment-flow.json +17 -3
  17. package/docs/agent/buyer/quickstart.json +48 -13
  18. package/docs/agent/buyer/render-hosts.json +14 -2
  19. package/docs/cli-reference/agent-types.md +2 -0
  20. package/docs/cli-reference/commands/buy.md +2 -0
  21. package/docs/cli-reference/commands/cart/add.md +2 -0
  22. package/docs/cli-reference/commands/cart/clear.md +2 -0
  23. package/docs/cli-reference/commands/cart/index.md +2 -0
  24. package/docs/cli-reference/commands/cart/next.md +2 -0
  25. package/docs/cli-reference/commands/cart/remove.md +3 -1
  26. package/docs/cli-reference/commands/cart/show.md +2 -0
  27. package/docs/cli-reference/commands/catalog/index.md +2 -0
  28. package/docs/cli-reference/commands/catalog/list.md +2 -0
  29. package/docs/cli-reference/commands/checkout.md +2 -0
  30. package/docs/cli-reference/commands/device.md +3 -1
  31. package/docs/cli-reference/commands/docs/index.md +2 -0
  32. package/docs/cli-reference/commands/docs/list.md +2 -0
  33. package/docs/cli-reference/commands/docs/search.md +2 -0
  34. package/docs/cli-reference/commands/docs/show.md +2 -0
  35. package/docs/cli-reference/commands/install.md +5 -3
  36. package/docs/cli-reference/commands/next.md +2 -0
  37. package/docs/cli-reference/commands/order.md +2 -0
  38. package/docs/cli-reference/commands/orders.md +2 -0
  39. package/docs/cli-reference/commands/pay.md +2 -0
  40. package/docs/cli-reference/commands/readyz.md +34 -6
  41. package/docs/cli-reference/commands/refund/cancel.md +2 -0
  42. package/docs/cli-reference/commands/refund/create.md +2 -0
  43. package/docs/cli-reference/commands/refund/get.md +2 -0
  44. package/docs/cli-reference/commands/refund/index.md +2 -0
  45. package/docs/cli-reference/commands/refund/list.md +2 -0
  46. package/docs/cli-reference/commands/refund/watch.md +2 -0
  47. package/docs/cli-reference/commands/services/action.md +2 -0
  48. package/docs/cli-reference/commands/services/checkout.md +2 -0
  49. package/docs/cli-reference/commands/services/events.md +2 -0
  50. package/docs/cli-reference/commands/services/get.md +2 -0
  51. package/docs/cli-reference/commands/services/index.md +2 -0
  52. package/docs/cli-reference/commands/services/invoke.md +73 -4
  53. package/docs/cli-reference/commands/services/list.md +2 -0
  54. package/docs/cli-reference/commands/services/next.md +2 -0
  55. package/docs/cli-reference/commands/services/quote.md +2 -0
  56. package/docs/cli-reference/commands/services/read-result.md +2 -0
  57. package/docs/cli-reference/commands/services/start.md +2 -0
  58. package/docs/cli-reference/commands/skill.md +6 -4
  59. package/docs/cli-reference/conventions.md +5 -0
  60. package/docs/cli-reference/index.md +3 -1
  61. package/package.json +6 -3
  62. package/skills/{itpay-buyer → itpay}/SKILL.md +43 -16
package/README.md CHANGED
@@ -1,17 +1,21 @@
1
1
  # ItPay CLI
2
2
 
3
- The official V3 CLI for Agent-driven ItPay service discovery, checkout, delivery, order recovery, and refunds.
3
+ The official V3 CLI and the single ItPay entry point for Agent-driven commerce.
4
+
5
+ ## One Entry Point, Two Actions
6
+
7
+ `itpay` is the only public CLI entry point, and `$itpay` is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are `buy` and `sell`: Buyer workflows are available now, while Seller workflows will use the same entry point and are not implemented yet. Do not create separate Buyer or Seller product entry points.
4
8
 
5
9
  ```bash
6
10
  npm install -g @itpay/cli
7
11
  itpay readyz --json
8
- itpay skill show itpay-buyer --json
12
+ itpay skill show itpay --json
9
13
  itpay install --json
10
14
  itpay --agent-type codex-desktop readyz --json
11
15
  # follow next.command: typed skill show, then catalog list
12
16
  ```
13
17
 
14
- The default API is `https://app.itpay.ai`. Set `ITPAY_BACKEND_URL` only for an intentional environment override.
18
+ The production Backend is permanently pinned to `https://app.itpay.ai`. Runtime environment variables cannot redirect the CLI to another Backend.
15
19
 
16
20
  ## Output Contract
17
21
 
@@ -42,11 +46,11 @@ Normative per-command contracts: [CLI Command Reference](docs/cli-reference/inde
42
46
 
43
47
  `--agent-type` identifies the stable runtime and registered Agent instance. Every returned ItPay command preserves it. Different windows or chats of the same type reuse one Agent Instance; they are not separate identities. `--host` only selects the human presentation surface, and `--target` only routes output to a Host destination. Use `itpay install <agent_type> --json` for the exact responsibility.
44
48
 
45
- The local installation keeps one Ed25519 private key. Each normalized Backend API base URL (`dev`, `test`, `app`, or local) has an independent server device registration, quota lineage, Agent instances, and sessions. A rejected session is renewed and the same request is retried once; revoked v2 registrations are never silently replaced.
49
+ The local installation keeps one Ed25519 private key and one production Device registration for `https://app.itpay.ai`, with one Agent Instance per Agent Type. A rejected session is renewed and the same request is retried once; revoked v2 registrations are never silently replaced.
46
50
 
47
51
  ## Command Families
48
52
 
49
- - `readyz`, `catalog list`: compatibility and discovery.
53
+ - `readyz`: fixed production Backend liveness; `catalog list`: compatibility-gated discovery.
50
54
  - `services start/invoke/action/checkout/next`: generic Service Execution flow.
51
55
  - `cart add/show/remove/clear/next`, `buy`: canonical Cart and ordinary Checkout flow.
52
56
  - `checkout`: authoritative payment and fulfillment recovery.
@@ -71,11 +75,10 @@ itpay --agent-type <agent_type> services checkout <service_execution_id> --resum
71
75
  itpay checkout --id <checkout_id> --token <display_token> --json
72
76
  ```
73
77
 
74
- The local `~/.itpay-v3` directory stores one owner-only signing key, Backend-scoped Device registrations and Agent instances, idempotency operations, and recovery handles. Backend state remains authoritative. Do not delete or rotate this identity to recover quota.
78
+ The local `~/.itpay-v3` directory stores one owner-only signing key, the `https://app.itpay.ai` Device registration and Agent instances, idempotency operations, and recovery handles. Backend state remains authoritative. Do not delete or rotate this identity to recover quota.
75
79
 
76
80
  ## Environment
77
81
 
78
- - `ITPAY_BACKEND_URL`: deliberate API override.
79
82
  - `ITPAY_AGENT_TYPE`: stable alternative to global `--agent-type`.
80
83
  - `ITPAY_BEARER_TOKEN`: account-scoped Buyer session for account-only commands such as `orders`.
81
84
  - `ITPAY_CART_SESSION_PATH`: local recovery-state path override.
@@ -91,10 +94,14 @@ Provider credentials, Buyer identity, payment provider choice, amount, refund po
91
94
  ```bash
92
95
  npm run lint
93
96
  npm test
97
+ npm run test:coverage
94
98
  npm run test:package
95
99
  npm run pack:dry-run
96
100
  ```
97
101
 
102
+ `npm install` configures the repository's pre-commit hook. Every commit must pass
103
+ `npm run lint` and `npm test`; do not bypass the hook for pull-request changes.
104
+
98
105
  Source boundaries:
99
106
 
100
107
  - `src/main.ts`: parser and error-envelope wiring.
@@ -10,6 +10,11 @@ export async function requirePlatformCompatibility(backend) {
10
10
  throw new HttpError(426, {
11
11
  code: "client_upgrade_required",
12
12
  message: `CLI ${CLI_VERSION} contract ${API_CONTRACT_REVISION} is incompatible with platform ${platform.platform_revision} contract ${platform.api_contract_revision} (minimum CLI ${platform.minimum_cli_version}, maximum major ${platform.maximum_cli_major})`,
13
+ minimum_cli_version: platform.minimum_cli_version,
14
+ maximum_cli_major: platform.maximum_cli_major,
15
+ platform_revision: platform.platform_revision,
16
+ api_contract_revision: platform.api_contract_revision,
17
+ upgrade_command: `npm install -g @itpay/cli@${platform.minimum_cli_version}`,
13
18
  }, "CLI is incompatible with the active ItPay platform release");
14
19
  }
15
20
  function compareVersions(left, right) {
@@ -92,6 +92,7 @@ function findDocsDir() {
92
92
  function parseDoc(raw, file) {
93
93
  const value = JSON.parse(raw);
94
94
  if (typeof value.schema_version !== "string" ||
95
+ typeof value.product_scope !== "string" ||
95
96
  typeof value.topic !== "string" ||
96
97
  typeof value.title !== "string" ||
97
98
  typeof value.purpose !== "string") {
@@ -25,7 +25,7 @@ export function writeCommandEnvelope(value, options = {}) {
25
25
  return;
26
26
  }
27
27
  out(`${qualified.status}\n`);
28
- const facts = "result" in qualified ? qualified.result : qualified.error;
28
+ const facts = "error" in qualified ? qualified.error : qualified.result;
29
29
  if (options.plainResult) {
30
30
  for (const line of options.plainResult)
31
31
  out(`${line}\n`);
@@ -34,6 +34,11 @@ export function writeCommandEnvelope(value, options = {}) {
34
34
  for (const [key, fact] of Object.entries(facts)) {
35
35
  out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
36
36
  }
37
+ if ("error" in qualified && qualified.result) {
38
+ for (const [key, fact] of Object.entries(qualified.result)) {
39
+ out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
40
+ }
41
+ }
37
42
  }
38
43
  if ("handoff" in qualified && qualified.handoff) {
39
44
  for (const [key, fact] of Object.entries(qualified.handoff)) {
@@ -252,8 +257,8 @@ export function errorRecoveryActions(error) {
252
257
  return [
253
258
  {
254
259
  id: "read_agent_session_rules",
255
- label: "Read identity and session recovery rules",
256
- command: "itpay skill show itpay-buyer --json",
260
+ label: "Read ItPay identity and session recovery rules",
261
+ command: "itpay skill show itpay --json",
257
262
  reason: "The CLI already attempted one automatic session renewal; do not rotate identity or loop retries.",
258
263
  },
259
264
  ];
@@ -299,14 +304,9 @@ export function errorRecoveryActions(error) {
299
304
  return [
300
305
  {
301
306
  id: "retry_after_backend_recovers",
302
- label: "Retry after the ItPay backend is reachable",
307
+ label: "Retry after app.itpay.ai is reachable",
303
308
  command: "itpay readyz",
304
309
  },
305
- {
306
- id: "check_backend_url",
307
- label: "Check the configured backend URL",
308
- command: "echo $ITPAY_BACKEND_URL",
309
- },
310
310
  ];
311
311
  }
312
312
  return [];
@@ -417,13 +417,7 @@ function buildServiceGuidance(input) {
417
417
  }
418
418
  }
419
419
  else if ((input.providerCalled || execution.next_action === "select_candidate") && (input.resultItems?.length ?? 0) === 0) {
420
- nextActions.push({
421
- id: "start_refined_search",
422
- label: "Start a new execution with a more specific company name",
423
- command: `itpay services start ${execution.service_id}`,
424
- requires_human: true,
425
- reason: "No candidates were found. This execution is finished; use one new execution per new keyword.",
426
- });
420
+ // Terminal empty result: a later explicit human request starts a new flow.
427
421
  }
428
422
  else if (needsHumanSelection(execution, resultItem)) {
429
423
  nextActions.push({
@@ -433,12 +427,6 @@ function buildServiceGuidance(input) {
433
427
  requires_human: true,
434
428
  reason: "Do not choose a candidate without explicit human confirmation.",
435
429
  });
436
- nextActions.push({
437
- id: "start_another_search",
438
- label: "Search another company in a new execution",
439
- command: `itpay services start ${execution.service_id}`,
440
- reason: "This execution has completed its one keyword lookup; do not reuse it for another keyword.",
441
- });
442
430
  }
443
431
  else if (prePurchase) {
444
432
  const action = {
@@ -13,7 +13,7 @@ const INSTRUCTIONS = {
13
13
  "codex-cli": "在 Codex CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
14
14
  "claude-code-desktop": "在 Claude Code Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
15
15
  "claude-code-cli": "在 Claude Code CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
16
- workbuddy: "在 WorkBuddy 中始终传这个 Agent TypeCheckout 返回 qr_image_url 时,必须按当次 instruction 调用 present_files 在右侧打开二维码,不要检查本地二维码文件。",
16
+ workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 返回 qr_image_url 时,按当次 instruction 调用 present_files 在右侧打开二维码,不要检查本地二维码文件。",
17
17
  };
18
18
  export function runInstall(target, options = {}) {
19
19
  if (!target || target === "list") {
@@ -48,9 +48,9 @@ export function runInstall(target, options = {}) {
48
48
  instruction: INSTRUCTIONS[normalized],
49
49
  next: {
50
50
  command: `itpay --agent-type ${normalized} readyz --json`,
51
- reason: "验证 CLI 与默认 ItPay API 的兼容性",
51
+ reason: "验证固定生产 ItPay API 的可用性",
52
52
  },
53
- recovery: [{ command: "itpay docs show install-and-setup", reason: "查看环境覆盖和首次使用说明" }],
53
+ recovery: [{ command: "itpay docs show install-and-setup", reason: "查看固定生产后端和首次使用说明" }],
54
54
  }, options);
55
55
  }
56
56
  function isInstallAgentType(value) {
@@ -5,8 +5,8 @@ export async function runReadyz(backend, options = {}) {
5
5
  writeCommandEnvelope({
6
6
  status: response.status,
7
7
  result: { backend: "available", ...(options.agentType ? { agent_type: options.agentType } : {}) },
8
- instruction: "ItPay 可用;先完整读取内置 Buyer Skill,再开始服务流程。",
9
- next: { command: "itpay skill show itpay-buyer --json", reason: "加载完整操作与安全规则" },
8
+ instruction: "ItPay 可用;先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。sell 将来也使用同一入口,但当前尚未实现。",
9
+ next: { command: "itpay skill show itpay --json", reason: "加载完整操作与安全规则" },
10
10
  recovery: [],
11
11
  }, options);
12
12
  }
@@ -106,13 +106,14 @@ function invokedEnvelope(response, requestedCapability, capabilities, input) {
106
106
  const baseResult = {
107
107
  service_execution_id: response.execution.service_execution_id,
108
108
  capability_id: requestedCapability.capability_id,
109
+ query: input,
109
110
  items,
110
111
  ...(quota ? { quota } : {}),
111
112
  };
112
113
  let status = items.length > 0 ? "result_ready" : "no_result";
113
114
  let instruction = items.length > 0
114
115
  ? "向用户展示编号和 safe_payload;若候选列表已满足用户目标,在此停止。仅在用户明确选择并希望继续时,才在当前 Execution 提交对应 rank。"
115
- : "Provider 已返回空结果;不要重放当前 execution,按下一步恢复。";
116
+ : `没有找到与“${queryText(input)}”匹配的结果。向用户展示本次为 0 个结果并停止。不要修改、缩短或猜测其他输入;只有用户明确提供新输入后,才能启动新的查询。`;
116
117
  let next = null;
117
118
  if (response.effective_quota?.exhausted) {
118
119
  status = "quota_exhausted";
@@ -148,9 +149,7 @@ function invokedEnvelope(response, requestedCapability, capabilities, input) {
148
149
  };
149
150
  }
150
151
  else if (items.length === 0) {
151
- next = response.provider_called
152
- ? { command: `itpay services start ${response.execution.service_id}`, reason: "为新的服务输入启动新 execution" }
153
- : { command: `itpay services next ${response.execution.service_execution_id} --json`, reason: "读取服务端恢复动作" };
152
+ next = null;
154
153
  }
155
154
  return {
156
155
  value: { status, result: baseResult, instruction, next, recovery: [] },
@@ -162,11 +161,18 @@ function serviceResultPlainLines(result) {
162
161
  `service_execution_id: ${String(result.service_execution_id)}`,
163
162
  `capability_id: ${String(result.capability_id)}`,
164
163
  ];
164
+ const items = result.items;
165
+ const query = result.query;
166
+ if (query) {
167
+ for (const [key, value] of Object.entries(query))
168
+ lines.push(`${key}: ${String(value)}`);
169
+ }
170
+ if (items.length === 0)
171
+ lines.push("results: 0");
165
172
  if (result.quota)
166
173
  lines.push(`quota: ${JSON.stringify(result.quota)}`);
167
174
  if (result.checkout)
168
175
  lines.push(`checkout: ${JSON.stringify(result.checkout)}`);
169
- const items = result.items;
170
176
  if (items.length > 0) {
171
177
  lines.push("items:");
172
178
  for (const item of items) {
@@ -178,6 +184,10 @@ function serviceResultPlainLines(result) {
178
184
  }
179
185
  return lines;
180
186
  }
187
+ function queryText(input) {
188
+ const value = Object.values(input).find((item) => typeof item === "string" && item.trim() !== "");
189
+ return typeof value === "string" ? value : JSON.stringify(input);
190
+ }
181
191
  function missingRequiredInput(schema, input) {
182
192
  return requiredInputFields(schema).filter((field) => {
183
193
  if (!(field in input) || input[field] === null || input[field] === undefined)
@@ -695,12 +705,12 @@ function servicesNextEnvelope(model) {
695
705
  ? selection
696
706
  ? "Agent-visible 搜索已完成。向用户展示 items 中的编号、title 和 safe_payload,然后停止;不要调用 read-result。只有用户明确选择候选并要求继续时,才执行 next.command。"
697
707
  : "这是当前 Graph 步骤对应的交付;结果已可供 Agent 使用,只使用 safe_payload。"
698
- : "Agent-visible 交付已完成但没有结果项;不要调用 read-result 或重放当前 execution。",
708
+ : "Agent-visible 交付已完成但有 0 个结果。向用户展示空结果并停止;不要调用 read-result、重放当前 Execution、修改输入或创建新 Execution。",
699
709
  next: selection ? {
700
710
  command: `itpay services action ${execution.service_execution_id} --action select_candidate --actor-type human --status approved --candidate <rank> --json`,
701
711
  reason: "仅在用户明确选择后锁定来源候选",
702
712
  } : null,
703
- recovery: items.length > 0 ? [] : [{ command: `itpay services get ${execution.service_execution_id} --json`, reason: "检查交付时间线" }],
713
+ recovery: [],
704
714
  };
705
715
  }
706
716
  if (deliveryMode === "vault_artifact") {
@@ -4,27 +4,27 @@ import { fileURLToPath } from "node:url";
4
4
  import { declaredAgentType } from "../state/agent_type.js";
5
5
  import { CommandContractError, writeCommandEnvelope } from "./guidance.js";
6
6
  const commandDir = dirname(fileURLToPath(import.meta.url));
7
- const BUYER_SKILL = "itpay-buyer";
7
+ const ITPAY_SKILL = "itpay";
8
8
  export function runSkillShow(name, options = {}) {
9
9
  const normalized = name.trim().toLowerCase();
10
- if (normalized !== BUYER_SKILL) {
11
- throw new CommandContractError("skill_not_found", `skill not found: ${name}`, `当前 CLI 只内置 ${BUYER_SKILL};不要猜测 Skill 名称。`, [{ command: `itpay skill show ${BUYER_SKILL} --json`, reason: "读取完整 Buyer Skill" }]);
10
+ if (normalized !== ITPAY_SKILL) {
11
+ throw new CommandContractError("skill_not_found", `skill not found: ${name}`, `当前 CLI 只内置 ${ITPAY_SKILL};不要猜测 Skill 名称。`, [{ command: `itpay skill show ${ITPAY_SKILL} --json`, reason: "读取完整 ItPay Skill" }]);
12
12
  }
13
13
  let content;
14
14
  try {
15
15
  content = readFileSync(findSkillPath(), "utf8");
16
16
  }
17
17
  catch {
18
- throw new Error("packaged skill is unavailable: itpay-buyer");
18
+ throw new Error("packaged skill is unavailable: itpay");
19
19
  }
20
20
  validateSkill(content);
21
21
  const agentType = options.agentType ?? declaredAgentType();
22
22
  const envelope = {
23
23
  status: "shown",
24
- result: { skill: BUYER_SKILL, content },
24
+ result: { skill: ITPAY_SKILL, content },
25
25
  instruction: agentType
26
26
  ? agentType === "workbuddy"
27
- ? "完整读取并遵守 Skill;保持 workbuddy、同一 Node/CLI launcher 和可持久写入 Device 状态的执行权限。内部诊断不要逐步转述给用户。"
27
+ ? "完整读取并遵守 Skill;保持 workbuddy 和同一 Node/CLI launcher。运行会持久化 ~/.itpay-v3 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。"
28
28
  : `完整读取并遵守 Skill;当前 Agent Type 是 ${agentType},后续命令保持不变。`
29
29
  : "完整读取并遵守 Skill;先如实选择当前运行环境对应的 Agent Type。",
30
30
  next: agentType
@@ -40,16 +40,16 @@ export function runSkillShow(name, options = {}) {
40
40
  }
41
41
  function findSkillPath() {
42
42
  if (process.env.ITPAY_CLI_SKILLS_DIR) {
43
- return resolve(process.env.ITPAY_CLI_SKILLS_DIR, BUYER_SKILL, "SKILL.md");
43
+ return resolve(process.env.ITPAY_CLI_SKILLS_DIR, ITPAY_SKILL, "SKILL.md");
44
44
  }
45
- const packagePath = resolve(commandDir, "..", "..", "..", "skills", BUYER_SKILL, "SKILL.md");
45
+ const packagePath = resolve(commandDir, "..", "..", "..", "skills", ITPAY_SKILL, "SKILL.md");
46
46
  if (existsSync(packagePath))
47
47
  return packagePath;
48
- return resolve(commandDir, "..", "..", "skills", BUYER_SKILL, "SKILL.md");
48
+ return resolve(commandDir, "..", "..", "skills", ITPAY_SKILL, "SKILL.md");
49
49
  }
50
50
  function validateSkill(content) {
51
51
  const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1];
52
- if (!frontmatter || !/^name:\s*itpay-buyer\s*$/m.test(frontmatter) || !/^description:\s*(?:>|\S)/m.test(frontmatter)) {
53
- throw new Error("invalid packaged skill: itpay-buyer");
52
+ if (!frontmatter || !/^name:\s*itpay\s*$/m.test(frontmatter) || !/^description:\s*(?:>|\S)/m.test(frontmatter)) {
53
+ throw new Error("invalid packaged skill: itpay");
54
54
  }
55
55
  }
package/dist/src/main.js CHANGED
@@ -26,7 +26,7 @@ import { collectOption, parseKeyValueList, runServicesAction, runServicesCheckou
26
26
  const program = new Command();
27
27
  program
28
28
  .name("itpay")
29
- .description("V3 ItPay CLI — checkout, payment, order, and refund commands")
29
+ .description("V3 ItPay CLI — one entry point for buy workflows and future sell workflows")
30
30
  .option("--agent-type <type>", "agent runtime type used for device enrollment and client-specific guidance")
31
31
  .version(CLI_VERSION);
32
32
  function withHost(value) {
@@ -95,17 +95,22 @@ function reportCLIError(error, contract) {
95
95
  error.code === "client_compatibility_headers_required" ||
96
96
  error.code === "platform_release_unavailable" ||
97
97
  (error.status === 404 && error.code === "unknown_error"));
98
+ const requiredCLIVersion = incompatible && error instanceof HttpError && /^\d+\.\d+\.\d+$/.test(error.payload?.minimum_cli_version ?? "")
99
+ ? error.payload.minimum_cli_version
100
+ : undefined;
98
101
  const backendInternal = error instanceof HttpError && error.status === 500 && error.code === "internal_error";
99
102
  const providerConnectionUnavailable = error instanceof HttpError && error.code === "provider_connection_unavailable";
100
103
  const providerTemporary = error instanceof HttpError && error.code === "provider_temporarily_unavailable";
101
104
  const providerRejected = error instanceof HttpError && error.code === "provider_rejected";
105
+ const providerInputRejected = error instanceof HttpError && error.code === "provider_input_rejected";
106
+ const providerContractMismatch = error instanceof HttpError && error.code === "provider_contract_mismatch";
102
107
  const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
103
108
  const deviceRecovery = deviceError ? [{
104
- command: "itpay skill show itpay-buyer --json",
105
- reason: "读取身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
109
+ command: "itpay skill show itpay --json",
110
+ reason: "读取 ItPay 身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
106
111
  }] : [];
107
112
  const stateRecovery = stateError ? [{
108
- command: "itpay skill show itpay-buyer --json",
113
+ command: "itpay skill show itpay --json",
109
114
  reason: "读取 Device 状态边界;修复当前 Host 的持久写权限后重试原命令",
110
115
  }] : [];
111
116
  const authorizationInstruction = stateError
@@ -124,21 +129,50 @@ function reportCLIError(error, contract) {
124
129
  code: incompatible ? "backend_contract_incompatible" : commandError?.code ?? (error instanceof HttpError ? error.code : stateError?.code ?? deviceError?.code ?? contract?.code ?? "command_failed"),
125
130
  message: error instanceof Error ? error.message : String(error),
126
131
  },
132
+ ...(requiredCLIVersion ? {
133
+ result: {
134
+ current_cli_version: CLI_VERSION,
135
+ required_cli_version: requiredCLIVersion,
136
+ },
137
+ } : error instanceof HttpError && error.payload?.service_execution_id ? {
138
+ result: {
139
+ service_execution_id: error.payload.service_execution_id,
140
+ provider_called: error.payload.provider_called === true,
141
+ ...(error.payload.effective_quota ? {
142
+ quota: {
143
+ remaining: error.payload.effective_quota.remaining,
144
+ limit: error.payload.effective_quota.limit,
145
+ },
146
+ } : {}),
147
+ },
148
+ } : {}),
127
149
  instruction: incompatible
128
- ? "立即向用户报告 error.message 并结束本次任务。不要运行任何其他 itpay、npm、which、device、docs、cart、orders 或 services 命令;不要寻找、安装或切换其他 CLI。只有运营明确提供兼容 CLI 后,才能在新的任务中重新开始。"
150
+ ? requiredCLIVersion
151
+ ? "当前 CLI 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,将 @itpay/cli 更新到 Backend 指定的精确版本。安装完成后确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要安装 latest、猜测版本、切换 Agent Type 或删除 Device 身份。"
152
+ : "立即向用户报告 error.message 并结束本次任务。Backend 未提供可验证的兼容 CLI 版本;不要运行其他 ItPay 或 npm 命令,不要猜测版本、切换 Agent Type 或删除 Device 身份。"
129
153
  : backendInternal
130
154
  ? "Backend 内部故障;立即停止并向用户报告。不要重试、检查或删除 Device 身份、创建替代 Execution、切换 Backend,或尝试 quote、checkout、cart、buy、pay 等付费路径。"
131
155
  : providerConnectionUnavailable
132
156
  ? "Provider 请求未发出,预留免费额度已释放;当前 Execution 已失败。立即向用户报告 error.message 并停止,不要自动重试、不要继续同一 Execution,也不要进入任何付费路径。只有运营确认连接恢复且用户明确要求重新查询后,才启动新的 Service Execution。"
133
157
  : providerTemporary
134
- ? "上游服务暂时不可用;向用户逐字报告 error.message 并停止,不要自动重试。请求是否可能计费以 Backend 的消费事实为准;只有用户明确要求再次查询后,才可按 next/recovery 重新开始。"
135
- : providerRejected
136
- ? "上游拒绝了本次输入;向用户逐字报告 error.message 并停止,不要重试同一 Execution。Provider 已收到响应,本次请求可能计费;只有用户提供修正后的输入后,才可按 next/recovery 继续。"
137
- : capabilityInputInvalid
138
- ? "输入未通过本地校验,上游尚未被调用且用户额度未变化。向用户逐字报告 error.message 并停止,不要原样重试或运行其他恢复命令。用户提供修正后的输入后,继续使用当前未结束的 Execution。"
139
- : commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
158
+ ? "上游服务暂时不可用;向用户逐字报告 error.message result.quota 并停止,不要自动重试、不要创建新 Execution。只有用户之后明确提出新请求,才可重新开始。"
159
+ : providerInputRejected
160
+ ? `Provider 明确拒绝了该输入:${error instanceof Error ? error.message : String(error)}。请向用户报告 error.message result.quota 并停止。不要自行修改输入、不要重试、不要创建新 Execution;只有用户明确提供新输入后才能重新查询。`
161
+ : providerContractMismatch
162
+ ? "Provider 响应与已发布契约不一致。这不是用户输入问题。立即停止,不要修改输入、不要重试、不要创建新 Execution,也不要进入付费路径;向用户报告平台故障和 result.quota。"
163
+ : providerRejected
164
+ ? "Provider 拒绝了本次请求,但未声明这是输入错误;向用户逐字报告 error.message 和 result.quota 并停止。不要修改输入、不要重试、不要创建新 Execution。"
165
+ : capabilityInputInvalid
166
+ ? "输入未通过本地校验,上游尚未被调用且用户额度未变化。向用户逐字报告 error.message 并停止,不要原样重试或运行其他恢复命令。用户提供修正后的输入后,继续使用当前未结束的 Execution。"
167
+ : commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
140
168
  next: null,
141
- recovery: incompatible || backendInternal || providerConnectionUnavailable || providerTemporary || providerRejected || capabilityInputInvalid ? [] : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
169
+ recovery: incompatible
170
+ ? requiredCLIVersion
171
+ ? [{ command: `npm install -g @itpay/cli@${requiredCLIVersion}`, reason: "安装 Backend 指定的兼容 CLI 版本" }]
172
+ : []
173
+ : backendInternal || providerConnectionUnavailable || providerTemporary || providerInputRejected || providerContractMismatch || providerRejected || capabilityInputInvalid
174
+ ? []
175
+ : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
142
176
  }, {
143
177
  ...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : {}),
144
178
  output: (text) => { process.stderr.write(text); },
@@ -175,17 +209,15 @@ program
175
209
  const config = loadConfig();
176
210
  const backend = newBackendClient(config);
177
211
  try {
178
- await requirePlatformCompatibility(backend);
179
212
  await runReadyz(backend, { jsonOutput: Boolean(options.json), ...(config.agentType ? { agentType: config.agentType } : {}) });
180
213
  }
181
214
  catch (error) {
182
215
  reportCLIError(error, {
183
216
  jsonOutput: Boolean(options.json),
184
217
  code: "backend_unavailable",
185
- instruction: "检查 ITPAY_BACKEND_URL 后重试;后端恢复前不要继续下单。",
218
+ instruction: "固定生产后端 https://app.itpay.ai 当前不可用;后端恢复前不要继续下单,也不要切换到其他地址。",
186
219
  recovery: [
187
- { command: "echo $ITPAY_BACKEND_URL", reason: "确认当前 Backend URL" },
188
- { command: "itpay readyz", reason: "重试可用性检查" },
220
+ { command: "itpay readyz", reason: "重试固定生产后端的可用性检查" },
189
221
  ],
190
222
  });
191
223
  }
@@ -194,14 +226,14 @@ program
194
226
  const deviceCmd = program.command("device").description("Recover local Device registration state after an operator-confirmed Backend reset");
195
227
  deviceCmd
196
228
  .command("recover")
197
- .description("Forget only the selected Backend registration while preserving the local private key")
229
+ .description("Forget only the app.itpay.ai registration while preserving the local private key")
198
230
  .option("--confirm-backend-reset", "confirm that an operator reset the selected Backend registration database")
199
231
  .option("--json", "output JSON instead of terminal text")
200
232
  .action(async (options) => {
201
233
  const config = loadConfig();
202
234
  try {
203
235
  if (!config.agentType) {
204
- throw new CommandContractError("agent_type_required", "agent type is required for Backend-scoped Device recovery", "如实声明当前 Agent Type;恢复后必须用同一类型重新登记。", [{ command: "itpay install --json", reason: "选择当前真实 Agent Type" }]);
236
+ throw new CommandContractError("agent_type_required", "agent type is required for app.itpay.ai Device recovery", "如实声明当前 Agent Type;恢复后必须用同一类型重新登记。", [{ command: "itpay install --json", reason: "选择当前真实 Agent Type" }]);
205
237
  }
206
238
  if (!options.confirmBackendReset) {
207
239
  throw new CommandContractError("backend_reset_confirmation_required", "--confirm-backend-reset is required", "仅在运营已确认当前 Backend 的 Device 登记数据库被重建或清空后执行;普通 session 失效或 revoked 不得使用。", [{ command: "itpay docs show identity-and-sessions --json", reason: "检查适用边界" }]);
@@ -1,4 +1,5 @@
1
- // CLI configuration loader. Reads config from environment only. Checkout
1
+ // CLI configuration loader. The production Backend is pinned to app.itpay.ai;
2
+ // environment variables configure only non-Backend runtime details. Checkout
2
3
  // display-token persistence belongs to the cart session file, protected with
3
4
  // owner-only permissions. Provider secrets are explicitly out of scope here.
4
5
  import { homedir } from "node:os";
@@ -10,8 +11,8 @@ import { declaredAgentType } from "./agent_type.js";
10
11
  import { DeviceAuthority } from "./device_authority.js";
11
12
  import { OperationJournal } from "./operation_journal.js";
12
13
  export const DEFAULT_BASE_URL = "https://app.itpay.ai";
13
- export const CLI_VERSION = "2.0.12";
14
- export const API_CONTRACT_REVISION = "sha256:ea7d716f444a01e9d0109f12a53b56ce16d5ab813795a18a7a63f24a8c752073";
14
+ export const CLI_VERSION = "2.0.14";
15
+ export const API_CONTRACT_REVISION = "sha256:7f4c40b082292bf823631bcd37d452f4a8537153e30636d5eb3a2b24a77ce602";
15
16
  const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
16
17
  const CART_SESSION_FILENAME = "cart.json";
17
18
  const OPERATION_JOURNAL_FILENAME = "operations.json";
@@ -24,7 +25,7 @@ export function cartSessionPath(env = process.env) {
24
25
  return resolve(dir, CART_SESSION_FILENAME);
25
26
  }
26
27
  export function loadConfig(env = process.env) {
27
- const baseURL = env.ITPAY_BACKEND_URL || DEFAULT_BASE_URL;
28
+ const baseURL = DEFAULT_BASE_URL;
28
29
  const bearerToken = env.ITPAY_BEARER_TOKEN || undefined;
29
30
  const agentType = declaredAgentType(env);
30
31
  const checkoutCurrency = env.ITPAY_CURRENCY || "CNY";
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "schema_version": "itp.agent_doc.v1",
3
3
  "role": "buyer",
4
+ "product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
4
5
  "topic": "cart-checkout",
5
6
  "title": "Canonical Cart And Checkout Routing",
6
7
  "purpose": "Use the canonical server Cart and route service-backed lines through Service Execution without hardcoded service logic.",
@@ -43,8 +44,20 @@
43
44
  "Do not treat QR display as payment verification."
44
45
  ],
45
46
  "next_docs": [
46
- { "condition": "Checkout handoff is ready", "topic": "payment-flow" },
47
- { "condition": "Host rendering needs clarification", "topic": "render-hosts" }
47
+ {
48
+ "condition": "Checkout handoff is ready",
49
+ "topic": "payment-flow"
50
+ },
51
+ {
52
+ "condition": "Host rendering needs clarification",
53
+ "topic": "render-hosts"
54
+ }
48
55
  ],
49
- "search_terms": ["cart", "checkout", "service execution", "resume", "contact"]
56
+ "search_terms": [
57
+ "cart",
58
+ "checkout",
59
+ "service execution",
60
+ "resume",
61
+ "contact"
62
+ ]
50
63
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "schema_version": "itp.agent_doc.v1",
3
3
  "role": "buyer",
4
+ "product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
4
5
  "topic": "catalog-list",
5
6
  "title": "Browse ItPay V3 Service Catalog",
6
7
  "purpose": "Teach the agent how to browse and display available catalog services to the buyer.",
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "schema_version": "itp.agent_doc.v1",
3
3
  "role": "buyer",
4
+ "product_scope": "itpay is the single public CLI entry point, and $itpay is its user-facing Skill invocation. Under that one product entry point, the two top-level commerce actions are buy and sell: Buyer workflows are available now; Seller workflows will use the same entry point and are not implemented yet.",
4
5
  "topic": "identity-and-sessions",
5
6
  "title": "Device Identity And Session Recovery",
6
- "purpose": "Explain stable local identity, Backend-scoped registrations, Agent Type instances, and bounded automatic session recovery.",
7
+ "purpose": "Explain stable local identity, the fixed app.itpay.ai production registration, Agent Type instances, and bounded automatic session recovery.",
7
8
  "when_to_use": [
8
9
  "A different Agent window or runtime starts using an existing CLI installation.",
9
10
  "The CLI reports device_state_unwritable, or the Backend reports agent_identity_required, agent_device_session_required, or agent_device_revoked.",
10
- "The configured ITPAY_BACKEND_URL changes between dev, test, and app."
11
+ "An operator confirms that the app.itpay.ai Device registration database was reset."
11
12
  ],
12
13
  "identity_model": {
13
14
  "local_key": "One owner-only Ed25519 private key for the local installation.",
14
- "backend_registration": "One independent server registration per normalized Backend API base URL.",
15
- "agent_instance": "One instance per agent_type under each Backend registration; same-type windows and chats reuse it.",
15
+ "backend_registration": "One production Device registration for https://app.itpay.ai; runtime environment variables cannot redirect the CLI.",
16
+ "agent_instance": "One instance per agent_type under the production registration; same-type windows and chats reuse it.",
16
17
  "presentation": "Host and target route output only and never identify the Agent or provide business input."
17
18
  },
18
19
  "automatic_recovery": [
@@ -29,36 +30,56 @@
29
30
  "commands": [
30
31
  {
31
32
  "intent": "reload the complete rules",
32
- "command": "itpay --agent-type <agent_type> skill show itpay-buyer --json",
33
+ "command": "itpay --agent-type <agent_type> skill show itpay --json",
33
34
  "success_signal": "the full Skill is returned and next keeps the same Agent Type"
34
35
  },
35
36
  {
36
- "intent": "confirm the selected Backend is available",
37
+ "intent": "confirm app.itpay.ai is available",
37
38
  "command": "itpay --agent-type <agent_type> readyz --json",
38
39
  "success_signal": "Backend is available and onboarding returns to the Skill"
39
40
  },
40
41
  {
41
- "intent": "recover after an operator-confirmed reset of the selected Backend registration database",
42
+ "intent": "recover after an operator-confirmed reset of the app.itpay.ai Device registration database",
42
43
  "command": "itpay --agent-type <agent_type> device recover --confirm-backend-reset --json",
43
- "success_signal": "only the selected Backend registration is removed; the private key and other Backend registrations remain"
44
+ "success_signal": "only the app.itpay.ai registration is removed and the private key remains"
44
45
  }
45
46
  ],
46
47
  "agent_rules": [
47
48
  "Use the real runtime type and keep it stable across every command in the flow.",
48
49
  "Keep the same CLI/Node launcher and Host-approved permission context for the whole flow.",
49
50
  "Treat a new window, chat, task, or process of the same runtime as the same Agent Type, not a new identity.",
50
- "Expect dev, test, and app to have separate device IDs, quota lineage, Agent instances, and sessions even on the same machine.",
51
- "After automatic recovery fails, stop and report the exact code, Backend URL, Agent Type, and command family without exposing private keys or tokens."
51
+ "Expect all production CLI traffic to use https://app.itpay.ai; do not set or suggest a Backend URL override.",
52
+ "After automatic recovery fails, stop and report the exact code, app.itpay.ai, Agent Type, and command family without exposing private keys or tokens."
52
53
  ],
53
54
  "forbidden": [
54
55
  "Do not delete ~/.itpay-v3, rotate the key, switch Agent Type, or repeatedly retry to obtain fresh quota.",
55
56
  "Do not use device recover for session expiry, revocation, quota recovery, or an unconfirmed Backend failure.",
56
- "Do not copy a server device ID from one Backend registration into another.",
57
+ "Do not redirect the CLI to dev, test, local, or any non-app.itpay.ai Backend.",
57
58
  "Do not use --target as identity or service input."
58
59
  ],
59
60
  "next_docs": [
60
- { "condition": "Identity is healthy", "topic": "quickstart" },
61
- { "condition": "Need setup details", "topic": "install-and-setup" }
61
+ {
62
+ "condition": "Identity is healthy",
63
+ "topic": "quickstart"
64
+ },
65
+ {
66
+ "condition": "Need setup details",
67
+ "topic": "install-and-setup"
68
+ }
62
69
  ],
63
- "search_terms": ["identity", "device", "session", "login", "authentication", "agent type", "backend", "revoked", "recover", "unwritable", "lock", "window", "chat"]
70
+ "search_terms": [
71
+ "identity",
72
+ "device",
73
+ "session",
74
+ "login",
75
+ "authentication",
76
+ "agent type",
77
+ "backend",
78
+ "revoked",
79
+ "recover",
80
+ "unwritable",
81
+ "lock",
82
+ "window",
83
+ "chat"
84
+ ]
64
85
  }