@itpay/cli 2.0.14 → 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.
package/README.md CHANGED
@@ -15,7 +15,7 @@ itpay --agent-type codex-desktop readyz --json
15
15
  # follow next.command: typed skill show, then catalog list
16
16
  ```
17
17
 
18
- The production Backend is permanently pinned to `https://app.itpay.ai`. Runtime environment variables cannot redirect the CLI to another Backend.
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.
19
19
 
20
20
  ## Output Contract
21
21
 
@@ -46,11 +46,11 @@ Normative per-command contracts: [CLI Command Reference](docs/cli-reference/inde
46
46
 
47
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.
48
48
 
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.
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.
50
50
 
51
51
  ## Command Families
52
52
 
53
- - `readyz`: fixed production Backend liveness; `catalog list`: compatibility-gated discovery.
53
+ - `readyz`: selected official Backend liveness; `catalog list`: compatibility-gated discovery.
54
54
  - `services start/invoke/action/checkout/next`: generic Service Execution flow.
55
55
  - `cart add/show/remove/clear/next`, `buy`: canonical Cart and ordinary Checkout flow.
56
56
  - `checkout`: authoritative payment and fulfillment recovery.
@@ -75,11 +75,12 @@ itpay --agent-type <agent_type> services checkout <service_execution_id> --resum
75
75
  itpay checkout --id <checkout_id> --token <display_token> --json
76
76
  ```
77
77
 
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.
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.
79
79
 
80
80
  ## Environment
81
81
 
82
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.
83
84
  - `ITPAY_BEARER_TOKEN`: account-scoped Buyer session for account-only commands such as `orders`.
84
85
  - `ITPAY_CART_SESSION_PATH`: local recovery-state path override.
85
86
  - `ITPAY_CURRENCY`: ordinary Cart currency, default `CNY`.
@@ -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
  }
@@ -304,7 +305,7 @@ export function errorRecoveryActions(error) {
304
305
  return [
305
306
  {
306
307
  id: "retry_after_backend_recovers",
307
- label: "Retry after app.itpay.ai is reachable",
308
+ label: "Retry after the selected official Backend is reachable",
308
309
  command: "itpay readyz",
309
310
  },
310
311
  ];
@@ -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: "验证固定生产 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,10 +2,14 @@
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 可用;先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。sell 将来也使用同一入口,但当前尚未实现。",
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 将来也使用同一入口,但当前尚未实现。",
9
13
  next: { command: "itpay skill show itpay --json", reason: "加载完整操作与安全规则" },
10
14
  recovery: [],
11
15
  }, options);
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";
@@ -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) => ({
@@ -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,31 +212,34 @@ program
209
212
  const config = loadConfig();
210
213
  const backend = newBackendClient(config);
211
214
  try {
212
- 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
+ });
213
219
  }
214
220
  catch (error) {
215
221
  reportCLIError(error, {
216
222
  jsonOutput: Boolean(options.json),
217
223
  code: "backend_unavailable",
218
- instruction: "固定生产后端 https://app.itpay.ai 当前不可用;后端恢复前不要继续下单,也不要切换到其他地址。",
224
+ instruction: `当前官方 Backend ${config.baseURL} 不可用;恢复前不要继续下单,也不要切换环境。`,
219
225
  recovery: [
220
- { command: "itpay readyz", reason: "重试固定生产后端的可用性检查" },
226
+ { command: "itpay readyz", reason: "重试当前官方 Backend 的可用性检查" },
221
227
  ],
222
228
  });
223
229
  }
224
230
  });
225
231
  // --- device ---------------------------------------------------------------
226
- 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");
227
233
  deviceCmd
228
234
  .command("recover")
229
- .description("Forget only the app.itpay.ai registration while preserving the local private key")
235
+ .description("Forget only the current official Backend registration while preserving the local private key")
230
236
  .option("--confirm-backend-reset", "confirm that an operator reset the selected Backend registration database")
231
237
  .option("--json", "output JSON instead of terminal text")
232
238
  .action(async (options) => {
233
239
  const config = loadConfig();
234
240
  try {
235
241
  if (!config.agentType) {
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" }]);
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" }]);
237
243
  }
238
244
  if (!options.confirmBackendReset) {
239
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,5 +1,5 @@
1
- // CLI configuration loader. The production Backend is pinned to app.itpay.ai;
2
- // environment variables configure only non-Backend runtime details. Checkout
1
+ // CLI configuration loader. Production defaults to app.itpay.ai; the only
2
+ // allowed override is the official dev Backend. Checkout
3
3
  // display-token persistence belongs to the cart session file, protected with
4
4
  // owner-only permissions. Provider secrets are explicitly out of scope here.
5
5
  import { homedir } from "node:os";
@@ -11,21 +11,54 @@ import { declaredAgentType } from "./agent_type.js";
11
11
  import { DeviceAuthority } from "./device_authority.js";
12
12
  import { OperationJournal } from "./operation_journal.js";
13
13
  export const DEFAULT_BASE_URL = "https://app.itpay.ai";
14
- export const CLI_VERSION = "2.0.14";
14
+ export const DEV_BASE_URL = "https://dev.itpay.ai";
15
+ export const CLI_VERSION = "2.0.15";
15
16
  export const API_CONTRACT_REVISION = "sha256:7f4c40b082292bf823631bcd37d452f4a8537153e30636d5eb3a2b24a77ce602";
16
17
  const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
17
18
  const CART_SESSION_FILENAME = "cart.json";
18
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
+ }
19
52
  export function cartSessionPath(env = process.env) {
20
53
  if (env.ITPAY_CART_SESSION_PATH) {
21
54
  return resolve(env.ITPAY_CART_SESSION_PATH);
22
55
  }
23
- const dir = resolve(homedir(), CART_SESSION_DEFAULT_DIR);
56
+ const dir = stateDir(env);
24
57
  mkdirSync(dir, { recursive: true });
25
- return resolve(dir, CART_SESSION_FILENAME);
58
+ return resolve(dir, stateFilename(CART_SESSION_FILENAME, resolveBackendURL(env)));
26
59
  }
27
60
  export function loadConfig(env = process.env) {
28
- const baseURL = DEFAULT_BASE_URL;
61
+ const baseURL = resolveBackendURL(env);
29
62
  const bearerToken = env.ITPAY_BEARER_TOKEN || undefined;
30
63
  const agentType = declaredAgentType(env);
31
64
  const checkoutCurrency = env.ITPAY_CURRENCY || "CNY";
@@ -34,10 +67,11 @@ export function loadConfig(env = process.env) {
34
67
  const ideImageDirOverride = env.ITPAY_IDE_IMAGE_DIR_OVERRIDE;
35
68
  return {
36
69
  baseURL,
70
+ environment: baseURL === DEV_BASE_URL ? "development" : "production",
37
71
  ...(agentType ? { agentType } : {}),
38
72
  checkoutCurrency,
39
73
  idempotencyKey,
40
- ...(!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))) } : {}),
41
75
  ideImageAttach,
42
76
  ...(ideImageDirOverride ? { ideImageDirOverride } : {}),
43
77
  ...(bearerToken ? { bearerToken } : {}),
@@ -4,16 +4,16 @@
4
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.",
5
5
  "topic": "identity-and-sessions",
6
6
  "title": "Device Identity And Session Recovery",
7
- "purpose": "Explain stable local identity, the fixed app.itpay.ai production registration, 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.",
8
8
  "when_to_use": [
9
9
  "A different Agent window or runtime starts using an existing CLI installation.",
10
10
  "The CLI reports device_state_unwritable, or the Backend reports agent_identity_required, agent_device_session_required, or agent_device_revoked.",
11
- "An operator confirms that the app.itpay.ai Device registration database was reset."
11
+ "An operator confirms that the currently selected official Backend registration database was reset."
12
12
  ],
13
13
  "identity_model": {
14
14
  "local_key": "One owner-only Ed25519 private key for the local installation.",
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.",
15
+ "backend_registration": "One registration per official Backend: app.itpay.ai by default and dev.itpay.ai only with the exact test override.",
16
+ "agent_instance": "One instance per agent_type under each Backend registration; same-type windows and chats reuse it.",
17
17
  "presentation": "Host and target route output only and never identify the Agent or provide business input."
18
18
  },
19
19
  "automatic_recovery": [
@@ -34,27 +34,27 @@
34
34
  "success_signal": "the full Skill is returned and next keeps the same Agent Type"
35
35
  },
36
36
  {
37
- "intent": "confirm app.itpay.ai is available",
37
+ "intent": "confirm the selected official Backend is available",
38
38
  "command": "itpay --agent-type <agent_type> readyz --json",
39
39
  "success_signal": "Backend is available and onboarding returns to the Skill"
40
40
  },
41
41
  {
42
- "intent": "recover after an operator-confirmed reset of the app.itpay.ai Device registration database",
42
+ "intent": "recover after an operator-confirmed reset of the selected official Backend registration database",
43
43
  "command": "itpay --agent-type <agent_type> device recover --confirm-backend-reset --json",
44
- "success_signal": "only the app.itpay.ai registration is removed and the private key remains"
44
+ "success_signal": "only the selected Backend registration is removed and the private key and other Backend registration remain"
45
45
  }
46
46
  ],
47
47
  "agent_rules": [
48
48
  "Use the real runtime type and keep it stable across every command in the flow.",
49
49
  "Keep the same CLI/Node launcher and Host-approved permission context for the whole flow.",
50
50
  "Treat a new window, chat, task, or process of the same runtime as the same Agent Type, not a new identity.",
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."
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."
53
53
  ],
54
54
  "forbidden": [
55
55
  "Do not delete ~/.itpay-v3, rotate the key, switch Agent Type, or repeatedly retry to obtain fresh quota.",
56
56
  "Do not use device recover for session expiry, revocation, quota recovery, or an unconfirmed Backend failure.",
57
- "Do not redirect the CLI to dev, test, local, or any non-app.itpay.ai Backend.",
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.",
58
58
  "Do not use --target as identity or service input."
59
59
  ],
60
60
  "next_docs": [
@@ -33,20 +33,19 @@
33
33
  ],
34
34
  "agent_rules": [
35
35
  "Install with npm install -g @itpay/cli.",
36
- "The production Backend is fixed to https://app.itpay.ai and cannot be redirected by runtime environment variables.",
36
+ "Production defaults to https://app.itpay.ai. Testing may set ITPAY_BACKEND_URL to the exact official URL https://dev.itpay.ai; no other Backend is allowed.",
37
37
  "If backend_contract_incompatible includes result.required_cli_version, stop all ItPay business commands and run only the exact npm install recovery returned by the CLI; never replace its version with latest.",
38
38
  "After upgrading, require itpay --version to equal result.required_cli_version before running readyz again; never change Agent Type or Device identity to recover compatibility.",
39
- "The production Backend is fixed to https://app.itpay.ai and cannot be redirected by runtime environment variables.",
40
39
  "Use one exact type: codex-desktop, codex-cli, claude-code-desktop, claude-code-cli, or workbuddy.",
41
- "One local private key is reused with the https://app.itpay.ai production Device registration and quota lineage.",
42
- "Within the production Backend registration, each Agent Type has one Agent Instance; all windows and chats of the same type reuse it.",
40
+ "One local private key is reused, while Device registrations and quota lineage remain separate for app.itpay.ai and dev.itpay.ai.",
41
+ "Within each official Backend registration, each Agent Type has one Agent Instance; all windows and chats of the same type reuse it.",
43
42
  "Agent Type identifies the runtime. Host controls rendering and target only identifies a presentation destination.",
44
43
  "Keep the exact Agent Type in every next or recovery command; do not fall back to a type previously used by another runtime.",
45
44
  "Do not change Agent Type or rotate local identity to reset quota or recover a failed command."
46
45
  ],
47
46
  "forbidden": [
48
47
  "Do not use codex, terminal, claude-code, or plain-chat as Agent Types.",
49
- "Do not set or suggest a Backend URL override; production CLI traffic always targets https://app.itpay.ai.",
48
+ "Do not set or suggest any Backend except the default https://app.itpay.ai or the exact test override https://dev.itpay.ai.",
50
49
  "Do not claim that install writes host configuration or registers a device; it only prints instructions.",
51
50
  "Do not create a new identity for a different window, task, chat, or process of the same Agent Type."
52
51
  ],
@@ -12,7 +12,7 @@
12
12
  "required_state": {
13
13
  "needs": [
14
14
  "the real supported Agent Type",
15
- "the fixed production Backend https://app.itpay.ai"
15
+ "one official Backend: production https://app.itpay.ai by default, or explicit test https://dev.itpay.ai"
16
16
  ],
17
17
  "must_not_need": [
18
18
  "Admin access",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "commands": [
24
24
  {
25
- "intent": "verify fixed production Backend availability",
25
+ "intent": "verify the selected official Backend availability",
26
26
  "command": "itpay --agent-type <agent_type> readyz --json",
27
27
  "success_signal": "status is ready and next points to the complete packaged ItPay Skill"
28
28
  },
@@ -57,6 +57,7 @@
57
57
  "Treat result as current facts, instruction as how to use those facts, next as the preferred executable action, and recovery as exceptional paths.",
58
58
  "Run one state-changing command at a time. Treat next.command as the preferred continuation: execute it unchanged after filling required user data only when the current result has not already satisfied the user's goal.",
59
59
  "Keep the same explicit Agent Type through next and recovery commands; same-type windows reuse one Backend Agent Instance.",
60
+ "When testing dev, execute every returned command unchanged so its ITPAY_BACKEND_URL=https://dev.itpay.ai prefix is preserved.",
60
61
  "Use --target only for a Host presentation destination. Put capability business values in --input key=value exactly as required by the schema.",
61
62
  "Start a separate Service Execution for each independent service intent; quota remains shared according to Backend identity policy.",
62
63
  "Ask the user for required email or contact values and explain their purpose; never invent them.",
@@ -18,7 +18,7 @@ itpay cart remove --local --variant <catalog_variant_id> --offer <offer_id> [--j
18
18
 
19
19
  | 参数 | 必填 | 说明 |
20
20
  | --- | --- | --- |
21
- | `--line <cart_item_id>` | 否 | canonical Cart line;省略时使用固定 `https://app.itpay.ai` 后端下最后保存的 line。 |
21
+ | `--line <cart_item_id>` | 否 | canonical Cart line;省略时使用当前官方 Backend 对应本地状态中最后保存的 line。 |
22
22
  | `--local` | 否 | 只修改显式本地草稿,不请求 Backend。 |
23
23
  | `--variant <catalog_variant_id>` | local 时是 | 与 `--offer` 一起标识本地草稿 line。 |
24
24
  | `--offer <offer_id>` | local 时是 | 与 `--variant` 一起标识本地草稿 line。 |
@@ -10,6 +10,6 @@
10
10
  itpay --agent-type <agent_type> device recover --confirm-backend-reset --json
11
11
  ```
12
12
 
13
- 命令只作用于固定生产后端 `https://app.itpay.ai` 的 Device registration,并保留本地 Ed25519 私钥、Cart 和业务资源。CLI 不接受 Backend URL 覆盖。该命令不访问 Backend、不自动创建新身份;返回的只读 `services list` 是重新登记入口。
13
+ 命令只作用于当前官方 Backend 的 Device registration,并保留本地 Ed25519 私钥、Cart 和业务资源。默认是 `https://app.itpay.ai`;显式测试可使用准确的 `ITPAY_BACKEND_URL=https://dev.itpay.ai`。该命令不访问 Backend、不自动创建新身份;返回的只读 `services list` 会保留同一 Backend,是重新登记入口。
14
14
 
15
15
  缺少确认参数返回 `backend_reset_confirmation_required`。普通 session 失效由 CLI 自动续期;revoked、quota、权限或未知 Backend 故障不得使用本命令。所有 Agent Type 使用相同输入和输出合同。
@@ -37,12 +37,12 @@ itpay install [target] [--json]
37
37
  "instruction": "在 Codex Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
38
38
  "next": {
39
39
  "command": "itpay --agent-type codex-desktop readyz --json",
40
- "reason": "验证固定生产 ItPay API 的可用性"
40
+ "reason": "验证当前官方 ItPay API 的可用性"
41
41
  },
42
42
  "recovery": [
43
43
  {
44
44
  "command": "itpay docs show install-and-setup",
45
- "reason": "查看固定生产后端和首次使用说明"
45
+ "reason": "查看官方 Backend 和首次使用说明"
46
46
  }
47
47
  ]
48
48
  }
@@ -4,9 +4,9 @@
4
4
 
5
5
  ## 范围与意义
6
6
 
7
- 检查固定生产 Backend `https://app.itpay.ai` 是否可用。它只调用 `/v1/readyz` 做 liveness 诊断,不执行平台兼容性 gate、不登记设备、不创建业务资源;需要服务端合同的命令仍会在各自入口严格检查 compatibility。
7
+ 检查当前官方 Backend 是否可用。默认使用生产环境 `https://app.itpay.ai`;仅测试时可通过 `ITPAY_BACKEND_URL=https://dev.itpay.ai` 选择官方开发环境。它只调用 `/v1/readyz` 做 liveness 诊断,不执行平台兼容性 gate、不登记设备、不创建业务资源;需要服务端合同的命令仍会在各自入口严格检查 compatibility。
8
8
 
9
- **上游:** CLI 安装;Backend 固定为 `https://app.itpay.ai`,不可由运行时环境覆盖。
9
+ **上游:** CLI 安装;Backend 只能是官方 `https://app.itpay.ai` 或 `https://dev.itpay.ai`,其他 override 在网络或本地状态写入前被拒绝。
10
10
  **下游:** 完整 `itpay` Skill,随后选择 Agent Type 或进入当前已支持的 Buyer Catalog。
11
11
 
12
12
  ## 语法与参数
@@ -24,16 +24,43 @@ itpay readyz [--json]
24
24
  ```json
25
25
  {
26
26
  "status": "ready",
27
- "result": { "backend": "available" },
27
+ "result": { "backend": "available", "backend_url": "https://app.itpay.ai", "environment": "production" },
28
28
  "instruction": "ItPay 可用;先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。sell 将来也使用同一入口,但当前尚未实现。",
29
29
  "next": { "command": "itpay skill show itpay --json", "reason": "加载完整操作与安全规则" },
30
30
  "recovery": []
31
31
  }
32
32
  ```
33
33
 
34
+ 开发环境返回同一 envelope,但明确标记环境并在每个后续命令中保留 dev Backend:
35
+
36
+ ```json
37
+ {
38
+ "status": "ready",
39
+ "result": { "backend": "available", "backend_url": "https://dev.itpay.ai", "environment": "development" },
40
+ "instruction": "ItPay dev 可用;后续必须执行返回的完整命令,并继续使用同一个 dev Backend。先完整读取内置 ItPay Skill,再进入当前已支持的 buy 流程。",
41
+ "next": { "command": "ITPAY_BACKEND_URL=https://dev.itpay.ai itpay skill show itpay --json", "reason": "加载完整操作与安全规则" },
42
+ "recovery": []
43
+ }
44
+ ```
45
+
34
46
  ## 异常处理
35
47
 
36
- 连接失败时返回 `backend_unavailable`,要求等待 `https://app.itpay.ai` 恢复后重试同一命令,不得切换后端或继续下单。
48
+ 连接失败时返回 `backend_unavailable`,要求等待当前官方 Backend 恢复后重试同一完整命令,不得在失败时切换环境或继续下单。
49
+
50
+ 非官方 URL 返回 `backend_override_forbidden`,且不提供自动 recovery:
51
+
52
+ ```json
53
+ {
54
+ "status": "error",
55
+ "error": {
56
+ "code": "backend_override_forbidden",
57
+ "message": "ITPAY_BACKEND_URL only supports https://app.itpay.ai or https://dev.itpay.ai"
58
+ },
59
+ "instruction": "移除 ITPAY_BACKEND_URL 使用正式环境,或准确设置为 https://dev.itpay.ai。",
60
+ "next": null,
61
+ "recovery": []
62
+ }
63
+ ```
37
64
 
38
65
  CLI 已取得 Backend 的兼容性合同、但当前版本或 contract hash 不匹配时,返回一个可执行且版本固定的恢复动作:
39
66
 
@@ -42,17 +69,17 @@ CLI 已取得 Backend 的兼容性合同、但当前版本或 contract hash 不
42
69
  "status": "error",
43
70
  "error": {
44
71
  "code": "backend_contract_incompatible",
45
- "message": "CLI 2.0.14 contract sha256:client is incompatible with platform v3.example contract sha256:server (minimum CLI 2.0.15, maximum major 2)"
72
+ "message": "CLI 2.0.15 contract sha256:client is incompatible with platform v3.example contract sha256:server (minimum CLI 2.0.16, maximum major 2)"
46
73
  },
47
74
  "result": {
48
- "current_cli_version": "2.0.14",
49
- "required_cli_version": "2.0.15"
75
+ "current_cli_version": "2.0.15",
76
+ "required_cli_version": "2.0.16"
50
77
  },
51
78
  "instruction": "当前 CLI 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,将 @itpay/cli 更新到 Backend 指定的精确版本。安装完成后确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要安装 latest、猜测版本、切换 Agent Type 或删除 Device 身份。",
52
79
  "next": null,
53
80
  "recovery": [
54
81
  {
55
- "command": "npm install -g @itpay/cli@2.0.15",
82
+ "command": "npm install -g @itpay/cli@2.0.16",
56
83
  "reason": "安装 Backend 指定的兼容 CLI 版本"
57
84
  }
58
85
  ]