@itpay/cli 2.0.13 → 2.0.15

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 (64) hide show
  1. package/README.md +15 -7
  2. package/dist/src/commands/docs.js +1 -0
  3. package/dist/src/commands/guidance.js +8 -12
  4. package/dist/src/commands/install.js +2 -2
  5. package/dist/src/commands/readyz.js +7 -3
  6. package/dist/src/commands/skill.js +10 -10
  7. package/dist/src/main.js +22 -18
  8. package/dist/src/state/config.js +41 -6
  9. package/docs/agent/buyer/cart-checkout.json +16 -3
  10. package/docs/agent/buyer/catalog-list.json +1 -0
  11. package/docs/agent/buyer/identity-and-sessions.json +34 -13
  12. package/docs/agent/buyer/install-and-setup.json +25 -8
  13. package/docs/agent/buyer/orders-refunds.json +13 -2
  14. package/docs/agent/buyer/payment-flow.json +17 -3
  15. package/docs/agent/buyer/quickstart.json +49 -13
  16. package/docs/agent/buyer/render-hosts.json +14 -2
  17. package/docs/cli-reference/agent-types.md +2 -0
  18. package/docs/cli-reference/commands/buy.md +2 -0
  19. package/docs/cli-reference/commands/cart/add.md +2 -0
  20. package/docs/cli-reference/commands/cart/clear.md +2 -0
  21. package/docs/cli-reference/commands/cart/index.md +2 -0
  22. package/docs/cli-reference/commands/cart/next.md +2 -0
  23. package/docs/cli-reference/commands/cart/remove.md +3 -1
  24. package/docs/cli-reference/commands/cart/show.md +2 -0
  25. package/docs/cli-reference/commands/catalog/index.md +2 -0
  26. package/docs/cli-reference/commands/catalog/list.md +2 -0
  27. package/docs/cli-reference/commands/checkout.md +2 -0
  28. package/docs/cli-reference/commands/device.md +3 -1
  29. package/docs/cli-reference/commands/docs/index.md +2 -0
  30. package/docs/cli-reference/commands/docs/list.md +2 -0
  31. package/docs/cli-reference/commands/docs/search.md +2 -0
  32. package/docs/cli-reference/commands/docs/show.md +2 -0
  33. package/docs/cli-reference/commands/install.md +4 -2
  34. package/docs/cli-reference/commands/next.md +2 -0
  35. package/docs/cli-reference/commands/order.md +2 -0
  36. package/docs/cli-reference/commands/orders.md +2 -0
  37. package/docs/cli-reference/commands/pay.md +2 -0
  38. package/docs/cli-reference/commands/readyz.md +40 -11
  39. package/docs/cli-reference/commands/refund/cancel.md +2 -0
  40. package/docs/cli-reference/commands/refund/create.md +2 -0
  41. package/docs/cli-reference/commands/refund/get.md +2 -0
  42. package/docs/cli-reference/commands/refund/index.md +2 -0
  43. package/docs/cli-reference/commands/refund/list.md +2 -0
  44. package/docs/cli-reference/commands/refund/watch.md +2 -0
  45. package/docs/cli-reference/commands/services/action.md +2 -0
  46. package/docs/cli-reference/commands/services/checkout.md +2 -0
  47. package/docs/cli-reference/commands/services/events.md +2 -0
  48. package/docs/cli-reference/commands/services/get.md +2 -0
  49. package/docs/cli-reference/commands/services/index.md +2 -0
  50. package/docs/cli-reference/commands/services/invoke.md +2 -0
  51. package/docs/cli-reference/commands/services/list.md +2 -0
  52. package/docs/cli-reference/commands/services/next.md +2 -0
  53. package/docs/cli-reference/commands/services/quote.md +2 -0
  54. package/docs/cli-reference/commands/services/read-result.md +2 -0
  55. package/docs/cli-reference/commands/services/start.md +2 -0
  56. package/docs/cli-reference/commands/skill.md +5 -3
  57. package/docs/cli-reference/conventions.md +2 -0
  58. package/docs/cli-reference/index.md +3 -1
  59. package/docs/skill-bundle-rollout/01-mcp-authentication.md +256 -0
  60. package/docs/skill-bundle-rollout/02-platform-bundle-repositories.md +282 -0
  61. package/docs/skill-bundle-rollout/03-platform-publishing.md +270 -0
  62. package/docs/skill-bundle-rollout/README.md +98 -0
  63. package/package.json +6 -3
  64. package/skills/{itpay-buyer → itpay}/SKILL.md +39 -17
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 CLI defaults to the production Backend `https://app.itpay.ai`. Explicit tests may set `ITPAY_BACKEND_URL=https://dev.itpay.ai`; every other Backend URL is rejected before network or local state access.
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 a separate registration for each official Backend, with one Agent Instance per Agent Type under each registration. 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`: selected official 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,12 +75,12 @@ 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, Backend-scoped Device registrations and Agent instances, idempotency operations, and recovery handles. Production uses `cart.json` / `operations.json`; dev uses `cart.dev.json` / `operations.dev.json`. 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`.
83
+ - `ITPAY_BACKEND_URL`: optional test override; only the exact official URL `https://dev.itpay.ai` is accepted. Unset it for production.
80
84
  - `ITPAY_BEARER_TOKEN`: account-scoped Buyer session for account-only commands such as `orders`.
81
85
  - `ITPAY_CART_SESSION_PATH`: local recovery-state path override.
82
86
  - `ITPAY_CURRENCY`: ordinary Cart currency, default `CNY`.
@@ -91,10 +95,14 @@ Provider credentials, Buyer identity, payment provider choice, amount, refund po
91
95
  ```bash
92
96
  npm run lint
93
97
  npm test
98
+ npm run test:coverage
94
99
  npm run test:package
95
100
  npm run pack:dry-run
96
101
  ```
97
102
 
103
+ `npm install` configures the repository's pre-commit hook. Every commit must pass
104
+ `npm run lint` and `npm test`; do not bypass the hook for pull-request changes.
105
+
98
106
  Source boundaries:
99
107
 
100
108
  - `src/main.ts`: parser and error-envelope wiring.
@@ -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") {
@@ -1,6 +1,7 @@
1
1
  import { HttpError } from "../client/http.js";
2
2
  import { resolveOutput } from "../render/sink.js";
3
3
  import { declaredAgentType, qualifyItPayCommand } from "../state/agent_type.js";
4
+ import { qualifyBackendCommand } from "../state/config.js";
4
5
  export function isTerminalServiceExecutionStatus(status) {
5
6
  return status === "failed" || status === "refunded" || status === "cancelled";
6
7
  }
@@ -84,7 +85,7 @@ export function printAgentGuidance(guidance, output) {
84
85
  out("next actions:\n");
85
86
  for (const action of guidance.next_actions) {
86
87
  out(` - ${action.label}\n`);
87
- out(` ${qualifyItPayCommand(action.command, agentType)}\n`);
88
+ out(` ${qualifyBackendCommand(qualifyItPayCommand(action.command, agentType))}\n`);
88
89
  if (action.requires_human)
89
90
  out(" requires human confirmation\n");
90
91
  if (action.reason)
@@ -95,17 +96,17 @@ export function printAgentGuidance(guidance, output) {
95
96
  out("recovery:\n");
96
97
  for (const action of guidance.recovery) {
97
98
  out(` - ${action.label}\n`);
98
- out(` ${qualifyItPayCommand(action.command, agentType)}\n`);
99
+ out(` ${qualifyBackendCommand(qualifyItPayCommand(action.command, agentType))}\n`);
99
100
  }
100
101
  }
101
102
  }
102
103
  function qualifyEnvelope(value, agentType) {
103
104
  return {
104
105
  ...value,
105
- next: value.next ? { ...value.next, command: qualifyItPayCommand(value.next.command, agentType) } : null,
106
+ next: value.next ? { ...value.next, command: qualifyBackendCommand(qualifyItPayCommand(value.next.command, agentType)) } : null,
106
107
  recovery: value.recovery.map((action) => ({
107
108
  ...action,
108
- command: qualifyItPayCommand(action.command, agentType),
109
+ command: qualifyBackendCommand(qualifyItPayCommand(action.command, agentType)),
109
110
  })),
110
111
  };
111
112
  }
@@ -257,8 +258,8 @@ export function errorRecoveryActions(error) {
257
258
  return [
258
259
  {
259
260
  id: "read_agent_session_rules",
260
- label: "Read identity and session recovery rules",
261
- command: "itpay skill show itpay-buyer --json",
261
+ label: "Read ItPay identity and session recovery rules",
262
+ command: "itpay skill show itpay --json",
262
263
  reason: "The CLI already attempted one automatic session renewal; do not rotate identity or loop retries.",
263
264
  },
264
265
  ];
@@ -304,14 +305,9 @@ export function errorRecoveryActions(error) {
304
305
  return [
305
306
  {
306
307
  id: "retry_after_backend_recovers",
307
- label: "Retry after the ItPay backend is reachable",
308
+ label: "Retry after the selected official Backend is reachable",
308
309
  command: "itpay readyz",
309
310
  },
310
- {
311
- id: "check_backend_url",
312
- label: "Check the configured backend URL",
313
- command: "echo $ITPAY_BACKEND_URL",
314
- },
315
311
  ];
316
312
  }
317
313
  return [];
@@ -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: "查看官方 Backend 和首次使用说明" }],
54
54
  }, options);
55
55
  }
56
56
  function isInstallAgentType(value) {
@@ -2,11 +2,15 @@
2
2
  import { writeCommandEnvelope } from "./guidance.js";
3
3
  export async function runReadyz(backend, options = {}) {
4
4
  const response = await backend.readyz();
5
+ const backendURL = options.backendURL ?? "https://app.itpay.ai";
6
+ const environment = options.environment ?? "production";
5
7
  writeCommandEnvelope({
6
8
  status: response.status,
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: "加载完整操作与安全规则" },
9
+ result: { backend: "available", backend_url: backendURL, environment, ...(options.agentType ? { agent_type: options.agentType } : {}) },
10
+ instruction: environment === "development"
11
+ ? "ItPay dev 可用;后续必须执行返回的完整命令,并继续使用同一个 dev Backend。先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。"
12
+ : "ItPay 可用;先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。sell 将来也使用同一入口,但当前尚未实现。",
13
+ next: { command: "itpay skill show itpay --json", reason: "加载完整操作与安全规则" },
10
14
  recovery: [],
11
15
  }, options);
12
16
  }
@@ -4,24 +4,24 @@ 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
27
  ? "完整读取并遵守 Skill;保持 workbuddy 和同一 Node/CLI launcher。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。"
@@ -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
@@ -2,7 +2,7 @@
2
2
  // services/backend/internal/httpapi/handlers/*.go. Commands only
3
3
  // orchestrate; HTTP and rendering live in src/client and src/render.
4
4
  import { Command } from "commander";
5
- import { CLI_VERSION, loadConfig, cartSessionPath, newBackendClient } from "./state/config.js";
5
+ import { BackendOverrideError, CLI_VERSION, loadConfig, cartSessionPath, newBackendClient } from "./state/config.js";
6
6
  import { DeviceAuthority, DeviceAuthorizationError, DeviceStateError } from "./state/device_authority.js";
7
7
  import { CartSession } from "./state/cart_session.js";
8
8
  import { defaultHostForAgentType, normalizeHost, validateContext } from "./state/client_context.js";
@@ -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) {
@@ -83,6 +83,7 @@ function resolveCheckoutPresentationArgs(input) {
83
83
  }
84
84
  function reportCLIError(error, contract) {
85
85
  const commandError = error instanceof CommandContractError ? error : undefined;
86
+ const backendOverrideError = error instanceof BackendOverrideError ? error : undefined;
86
87
  const deviceError = error instanceof DeviceAuthorizationError ? error : undefined;
87
88
  const stateError = error instanceof DeviceStateError ? error : undefined;
88
89
  const httpRecovery = errorRecoveryActions(error).map((action) => ({
@@ -106,11 +107,11 @@ function reportCLIError(error, contract) {
106
107
  const providerContractMismatch = error instanceof HttpError && error.code === "provider_contract_mismatch";
107
108
  const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
108
109
  const deviceRecovery = deviceError ? [{
109
- command: "itpay skill show itpay-buyer --json",
110
- reason: "读取身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
110
+ command: "itpay skill show itpay --json",
111
+ reason: "读取 ItPay 身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
111
112
  }] : [];
112
113
  const stateRecovery = stateError ? [{
113
- command: "itpay skill show itpay-buyer --json",
114
+ command: "itpay skill show itpay --json",
114
115
  reason: "读取 Device 状态边界;修复当前 Host 的持久写权限后重试原命令",
115
116
  }] : [];
116
117
  const authorizationInstruction = stateError
@@ -122,11 +123,11 @@ function reportCLIError(error, contract) {
122
123
  : deviceError
123
124
  ? "Device 身份验证失败;停止重试,不要切换 Agent Type、删除状态或旋转私钥。"
124
125
  : undefined;
125
- if (contract || commandError) {
126
+ if (contract || commandError || backendOverrideError) {
126
127
  writeCommandEnvelope({
127
128
  status: "error",
128
129
  error: {
129
- code: incompatible ? "backend_contract_incompatible" : commandError?.code ?? (error instanceof HttpError ? error.code : stateError?.code ?? deviceError?.code ?? contract?.code ?? "command_failed"),
130
+ code: incompatible ? "backend_contract_incompatible" : backendOverrideError?.code ?? commandError?.code ?? (error instanceof HttpError ? error.code : stateError?.code ?? deviceError?.code ?? contract?.code ?? "command_failed"),
130
131
  message: error instanceof Error ? error.message : String(error),
131
132
  },
132
133
  ...(requiredCLIVersion ? {
@@ -164,7 +165,9 @@ function reportCLIError(error, contract) {
164
165
  ? "Provider 拒绝了本次请求,但未声明这是输入错误;向用户逐字报告 error.message 和 result.quota 并停止。不要修改输入、不要重试、不要创建新 Execution。"
165
166
  : capabilityInputInvalid
166
167
  ? "输入未通过本地校验,上游尚未被调用且用户额度未变化。向用户逐字报告 error.message 并停止,不要原样重试或运行其他恢复命令。用户提供修正后的输入后,继续使用当前未结束的 Execution。"
167
- : commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
168
+ : backendOverrideError
169
+ ? "移除 ITPAY_BACKEND_URL 使用正式环境,或准确设置为 https://dev.itpay.ai。"
170
+ : commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
168
171
  next: null,
169
172
  recovery: incompatible
170
173
  ? requiredCLIVersion
@@ -172,9 +175,9 @@ function reportCLIError(error, contract) {
172
175
  : []
173
176
  : backendInternal || providerConnectionUnavailable || providerTemporary || providerInputRejected || providerContractMismatch || providerRejected || capabilityInputInvalid
174
177
  ? []
175
- : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
178
+ : backendOverrideError ? [] : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
176
179
  }, {
177
- ...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : {}),
180
+ ...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : backendOverrideError ? { jsonOutput: process.argv.includes("--json") } : {}),
178
181
  output: (text) => { process.stderr.write(text); },
179
182
  });
180
183
  process.exitCode = 1;
@@ -209,33 +212,34 @@ program
209
212
  const config = loadConfig();
210
213
  const backend = newBackendClient(config);
211
214
  try {
212
- await requirePlatformCompatibility(backend);
213
- await runReadyz(backend, { jsonOutput: Boolean(options.json), ...(config.agentType ? { agentType: config.agentType } : {}) });
215
+ await runReadyz(backend, {
216
+ jsonOutput: Boolean(options.json), backendURL: config.baseURL, environment: config.environment,
217
+ ...(config.agentType ? { agentType: config.agentType } : {}),
218
+ });
214
219
  }
215
220
  catch (error) {
216
221
  reportCLIError(error, {
217
222
  jsonOutput: Boolean(options.json),
218
223
  code: "backend_unavailable",
219
- instruction: "检查 ITPAY_BACKEND_URL 后重试;后端恢复前不要继续下单。",
224
+ instruction: `当前官方 Backend ${config.baseURL} 不可用;恢复前不要继续下单,也不要切换环境。`,
220
225
  recovery: [
221
- { command: "echo $ITPAY_BACKEND_URL", reason: "确认当前 Backend URL" },
222
- { command: "itpay readyz", reason: "重试可用性检查" },
226
+ { command: "itpay readyz", reason: "重试当前官方 Backend 的可用性检查" },
223
227
  ],
224
228
  });
225
229
  }
226
230
  });
227
231
  // --- device ---------------------------------------------------------------
228
- const deviceCmd = program.command("device").description("Recover local Device registration state after an operator-confirmed Backend reset");
232
+ const deviceCmd = program.command("device").description("Recover the current official Backend registration after an operator-confirmed reset");
229
233
  deviceCmd
230
234
  .command("recover")
231
- .description("Forget only the selected Backend registration while preserving the local private key")
235
+ .description("Forget only the current official Backend registration while preserving the local private key")
232
236
  .option("--confirm-backend-reset", "confirm that an operator reset the selected Backend registration database")
233
237
  .option("--json", "output JSON instead of terminal text")
234
238
  .action(async (options) => {
235
239
  const config = loadConfig();
236
240
  try {
237
241
  if (!config.agentType) {
238
- throw new CommandContractError("agent_type_required", "agent type is required for Backend-scoped Device recovery", "如实声明当前 Agent Type;恢复后必须用同一类型重新登记。", [{ command: "itpay install --json", reason: "选择当前真实 Agent Type" }]);
242
+ throw new CommandContractError("agent_type_required", `agent type is required for ${config.baseURL} Device recovery`, "如实声明当前 Agent Type;恢复后必须用同一类型重新登记。", [{ command: "itpay install --json", reason: "选择当前真实 Agent Type" }]);
239
243
  }
240
244
  if (!options.confirmBackendReset) {
241
245
  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. Production defaults to app.itpay.ai; the only
2
+ // allowed override is the official dev Backend. 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,21 +11,54 @@ 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.13";
14
+ export const DEV_BASE_URL = "https://dev.itpay.ai";
15
+ export const CLI_VERSION = "2.0.15";
14
16
  export const API_CONTRACT_REVISION = "sha256:7f4c40b082292bf823631bcd37d452f4a8537153e30636d5eb3a2b24a77ce602";
15
17
  const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
16
18
  const CART_SESSION_FILENAME = "cart.json";
17
19
  const OPERATION_JOURNAL_FILENAME = "operations.json";
20
+ export class BackendOverrideError extends Error {
21
+ code = "backend_override_forbidden";
22
+ constructor() {
23
+ super(`ITPAY_BACKEND_URL only supports ${DEFAULT_BASE_URL} or ${DEV_BASE_URL}`);
24
+ this.name = "BackendOverrideError";
25
+ }
26
+ }
27
+ export function resolveBackendURL(env = process.env) {
28
+ const requested = env.ITPAY_BACKEND_URL?.trim();
29
+ if (!requested || requested === DEFAULT_BASE_URL || requested === `${DEFAULT_BASE_URL}/`)
30
+ return DEFAULT_BASE_URL;
31
+ if (requested === DEV_BASE_URL || requested === `${DEV_BASE_URL}/`)
32
+ return DEV_BASE_URL;
33
+ throw new BackendOverrideError();
34
+ }
35
+ export function qualifyBackendCommand(command, env = process.env) {
36
+ const requested = env.ITPAY_BACKEND_URL?.trim();
37
+ if (requested !== DEV_BASE_URL && requested !== `${DEV_BASE_URL}/`)
38
+ return command;
39
+ if (!command.startsWith("itpay ") || command.startsWith(`ITPAY_BACKEND_URL=${DEV_BASE_URL} `))
40
+ return command;
41
+ return `ITPAY_BACKEND_URL=${DEV_BASE_URL} ${command}`;
42
+ }
43
+ function stateFilename(filename, baseURL) {
44
+ if (baseURL !== DEV_BASE_URL)
45
+ return filename;
46
+ const dot = filename.lastIndexOf(".");
47
+ return dot < 0 ? `${filename}.dev` : `${filename.slice(0, dot)}.dev${filename.slice(dot)}`;
48
+ }
49
+ function stateDir(env) {
50
+ return resolve(env.HOME || homedir(), CART_SESSION_DEFAULT_DIR);
51
+ }
18
52
  export function cartSessionPath(env = process.env) {
19
53
  if (env.ITPAY_CART_SESSION_PATH) {
20
54
  return resolve(env.ITPAY_CART_SESSION_PATH);
21
55
  }
22
- const dir = resolve(homedir(), CART_SESSION_DEFAULT_DIR);
56
+ const dir = stateDir(env);
23
57
  mkdirSync(dir, { recursive: true });
24
- return resolve(dir, CART_SESSION_FILENAME);
58
+ return resolve(dir, stateFilename(CART_SESSION_FILENAME, resolveBackendURL(env)));
25
59
  }
26
60
  export function loadConfig(env = process.env) {
27
- const baseURL = env.ITPAY_BACKEND_URL || DEFAULT_BASE_URL;
61
+ const baseURL = resolveBackendURL(env);
28
62
  const bearerToken = env.ITPAY_BEARER_TOKEN || undefined;
29
63
  const agentType = declaredAgentType(env);
30
64
  const checkoutCurrency = env.ITPAY_CURRENCY || "CNY";
@@ -33,10 +67,11 @@ export function loadConfig(env = process.env) {
33
67
  const ideImageDirOverride = env.ITPAY_IDE_IMAGE_DIR_OVERRIDE;
34
68
  return {
35
69
  baseURL,
70
+ environment: baseURL === DEV_BASE_URL ? "development" : "production",
36
71
  ...(agentType ? { agentType } : {}),
37
72
  checkoutCurrency,
38
73
  idempotencyKey,
39
- ...(!env.ITPAY_IDEMPOTENCY_KEY ? { operationJournal: new OperationJournal(resolve(homedir(), CART_SESSION_DEFAULT_DIR, OPERATION_JOURNAL_FILENAME)) } : {}),
74
+ ...(!env.ITPAY_IDEMPOTENCY_KEY ? { operationJournal: new OperationJournal(resolve(stateDir(env), stateFilename(OPERATION_JOURNAL_FILENAME, baseURL))) } : {}),
40
75
  ideImageAttach,
41
76
  ...(ideImageDirOverride ? { ideImageDirOverride } : {}),
42
77
  ...(bearerToken ? { bearerToken } : {}),
@@ -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,17 +1,18 @@
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, official Backend-scoped registrations, 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 currently selected official Backend 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
+ "backend_registration": "One registration per official Backend: app.itpay.ai by default and dev.itpay.ai only with the exact test override.",
15
16
  "agent_instance": "One instance per agent_type under each Backend 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
  },
@@ -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 the selected official Backend 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 selected official Backend 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 selected Backend registration is removed and the private key and other Backend registration remain"
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 production traffic to use https://app.itpay.ai. Use https://dev.itpay.ai only for an explicit test and keep the complete returned Backend-prefixed command for that entire flow.",
52
+ "After automatic recovery fails, stop and report the exact code, selected official Backend, 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 test, local, an IP, or any custom Backend; only the exact official dev.itpay.ai test override is allowed.",
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
  }