@itpay/cli 2.1.29 → 2.1.34

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.
@@ -5,6 +5,7 @@ export class BackendClient {
5
5
  constructor(http) {
6
6
  this.http = http;
7
7
  }
8
+ get localSimulation() { return this.http.localSimulation; }
8
9
  // Called only through the fixed Sell operation contract, never arbitrary Agent URLs.
9
10
  sellRequest(request) {
10
11
  if (!request.path.startsWith("/v1/seller/organizations") && !request.path.startsWith("/v1/library/"))
@@ -123,8 +124,10 @@ export class BackendClient {
123
124
  advanceServiceExecution(id, input, idempotencyKey) {
124
125
  return this.http.post(`/v1/service-executions/${encodeURIComponent(id)}/advance`, { input, idempotency_key: idempotencyKey });
125
126
  }
126
- getServiceExecutionResultItemPage(serviceExecutionID, resultItemID, offset, limit) {
127
+ getServiceExecutionResultItemPage(serviceExecutionID, resultItemID, offset, limit, view) {
127
128
  const qs = new URLSearchParams({ offset: String(offset), limit: String(limit) });
129
+ if (view)
130
+ qs.set("view", view);
128
131
  return this.http.get(`/v1/service-executions/${encodeURIComponent(serviceExecutionID)}/result-items/${encodeURIComponent(resultItemID)}?${qs}`);
129
132
  }
130
133
  invokeServiceCapability(serviceExecutionID, capabilityID, input) {
@@ -14,6 +14,7 @@ export class HttpError extends Error {
14
14
  }
15
15
  }
16
16
  export class HttpClient {
17
+ localSimulation = false;
17
18
  static MAX_TRANSPORT_RETRIES = 2;
18
19
  baseURL;
19
20
  fetchImpl;
@@ -70,6 +71,7 @@ export class HttpClient {
70
71
  ...(options.signal ? { signal: options.signal } : {}),
71
72
  });
72
73
  text = await response.text();
74
+ this.localSimulation = response.headers.get("X-ItPay-Environment") === "local_simulation";
73
75
  }
74
76
  catch (error) {
75
77
  if (options.signal?.aborted)
@@ -106,6 +106,7 @@ export async function runBuy(backend, config, options) {
106
106
  }
107
107
  // --- Build render plan (after payment if --pay) ---
108
108
  const planInput = {
109
+ localSimulation: backend.localSimulation,
109
110
  host: options.host,
110
111
  checkoutID,
111
112
  checkoutURL,
@@ -246,7 +247,7 @@ async function waitForPaymentSSE(backend, checkoutID, displayToken, timeoutSec)
246
247
  }
247
248
  // --- checkout QR plan ---
248
249
  export function buildCheckoutQRPlan(input) {
249
- const summary = `Scan the QR or open ${input.checkoutURL} to start the human checkout flow.`;
250
+ const summary = input.localSimulation ? "在本机打开模拟付款入口;铁路与地图查询真实,授权、通知和交易模拟,不会扣款。" : `Scan the QR or open ${input.checkoutURL} to start the human checkout flow.`;
250
251
  const isPayment = input.paymentIntentID != null;
251
252
  const afterCommand = qualifyItPayCommand(`itpay checkout --id ${input.checkoutID} --token ${input.displayToken}${input.locale === "en" ? " --locale en" : ""} --json`, input.agentType);
252
253
  const platform = {
@@ -265,6 +266,7 @@ export function buildCheckoutQRPlan(input) {
265
266
  };
266
267
  const plan = {
267
268
  kind: isPayment ? "payment_qr" : "checkout_qr",
269
+ ...(input.localSimulation ? { localSimulation: true } : {}),
268
270
  host: input.host,
269
271
  summary,
270
272
  url: input.qrPayload,
@@ -57,6 +57,7 @@ export async function runCheckoutPresentation(backend, options) {
57
57
  const localizedPNGURL = localizeCardURL(qrPNGURL, locale);
58
58
  const nextCommand = `itpay checkout --id ${options.checkoutID} --token ${options.displayToken}${locale === "en" ? " --locale en" : ""} --json`;
59
59
  const plan = buildCheckoutQRPlan({
60
+ localSimulation: backend.localSimulation,
60
61
  host,
61
62
  checkoutID: options.checkoutID,
62
63
  checkoutURL,
@@ -30,16 +30,17 @@ function hasCompleteCheckoutTokens(url) {
30
30
  }
31
31
  }
32
32
  export function buildCheckoutHandoff(input) {
33
- const handoff = { url: input.url };
33
+ const url = input.plan?.localSimulation && input.mobileUrl && hasCompleteCheckoutTokens(input.mobileUrl) ? input.mobileUrl : input.url;
34
+ const handoff = { url, ...(input.plan?.localSimulation ? { environment: "local_simulation", query: "live", transactions: "simulated" } : {}) };
34
35
  // mobile_url is only emitted when the link carries the complete token pair;
35
36
  // a display-token-only link cannot finish the same-device exchange after payment.
36
- const mobileUrl = hasCompleteCheckoutTokens(input.mobileUrl) ? input.mobileUrl : undefined;
37
+ const mobileUrl = !input.plan?.localSimulation && hasCompleteCheckoutTokens(input.mobileUrl) ? input.mobileUrl : undefined;
37
38
  if (mobileUrl) {
38
39
  handoff.mobile_url = mobileUrl;
39
40
  }
40
41
  const workBuddyAction = isWorkBuddyPlainChat(input.agentType, input.platform);
41
42
  if (workBuddyAction) {
42
- handoff.agent_action = buildWorkBuddyPresentFilesAction(input.url);
43
+ handoff.agent_action = buildWorkBuddyPresentFilesAction(url);
43
44
  }
44
45
  if (input.platform === "markdown") {
45
46
  if (input.localPath)
@@ -58,7 +59,9 @@ export function buildCheckoutHandoff(input) {
58
59
  }
59
60
  return {
60
61
  handoff,
61
- instruction: checkoutHandoffInstruction(input.agentType, input.platform, input.amount, Boolean(mobileUrl)),
62
+ instruction: input.plan?.localSimulation
63
+ ? `本地实验:铁路与地图查询真实,授权、通知和交易模拟,不会扣款。在本机打开 handoff.url 模拟付款入口,金额 ${input.amount};仅本机桌面可达。${workBuddyAction ? "按 handoff.agent_action 原样打开一次。" : "展示原入口。"}等待用户操作后读取同一 Checkout;不创建替代订单,用户声明不证明模拟付款已确认。`
64
+ : `先展示官方入口,再做非必要记忆或日志;保留必需安全检查和宿主权限。${checkoutHandoffInstruction(input.agentType, input.platform, input.amount, Boolean(mobileUrl))}`,
62
65
  };
63
66
  }
64
67
  function checkoutHandoffInstruction(agentType, platform, amount, hasMobileLink) {
@@ -16,12 +16,14 @@ export class CommandContractError extends Error {
16
16
  instruction;
17
17
  recovery;
18
18
  interaction;
19
- constructor(code, message, instruction, recovery, interaction) {
19
+ result;
20
+ constructor(code, message, instruction, recovery, interaction, result) {
20
21
  super(message);
21
22
  this.code = code;
22
23
  this.instruction = instruction;
23
24
  this.recovery = recovery;
24
25
  this.interaction = interaction;
26
+ this.result = result;
25
27
  this.name = "CommandContractError";
26
28
  }
27
29
  }
@@ -12,6 +12,7 @@ export const INSTALL_AGENT_TYPES = [
12
12
  "kimi-code",
13
13
  "openclaw",
14
14
  ];
15
+ export const CODEX_WAIT_GUIDANCE = "Codex 宿主等待:保留完整工具返回,按实际返回的会话句柄及当前宿主续读工具收取最终输出,不要只取 stdout。若使用 functions.exec 包装,可用 text(await tools.exec_command(...));内层有 session_id 时用 write_stdin,外层返回运行 cell 时才用 wait。没有该包装时按宿主实际工具操作。先收取最终输出,再执行 CLI 业务 next;不要重复提交。";
15
16
  const INSTRUCTIONS = {
16
17
  "codex-desktop": "在 Codex Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
17
18
  "codex-cli": "在 Codex CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
@@ -48,7 +49,7 @@ export function runInstall(target, options = {}) {
48
49
  ...installDefinition(normalized),
49
50
  default_api: DEFAULT_BASE_URL,
50
51
  },
51
- instruction: INSTRUCTIONS[normalized],
52
+ instruction: INSTRUCTIONS[normalized] + (normalized.startsWith("codex-") ? ` ${CODEX_WAIT_GUIDANCE}` : ""),
52
53
  next: {
53
54
  command: `itpay --agent-type ${normalized} readyz --json`,
54
55
  reason: "验证当前官方 ItPay API 的可用性",