@itpay/cli 2.1.34 → 2.1.41
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 +5 -12
- package/dist/src/client/backend.js +2 -33
- package/dist/src/client/http.js +0 -2
- package/dist/src/commands/buy.js +4 -7
- package/dist/src/commands/cart.js +15 -15
- package/dist/src/commands/catalog.js +7 -17
- package/dist/src/commands/checkout.js +5 -146
- package/dist/src/commands/checkout_handoff.js +10 -48
- package/dist/src/commands/docs.js +18 -7
- package/dist/src/commands/guidance.js +19 -69
- package/dist/src/commands/install.js +3 -6
- package/dist/src/commands/order.js +1 -14
- package/dist/src/commands/orders.js +9 -4
- package/dist/src/commands/pay.js +1 -1
- package/dist/src/commands/readyz.js +2 -2
- package/dist/src/commands/refund.js +5 -5
- package/dist/src/commands/services.js +167 -1712
- package/dist/src/commands/skill.js +5 -8
- package/dist/src/commands/vault.js +20 -27
- package/dist/src/commands/vault_handoff.js +0 -18
- package/dist/src/main.js +58 -294
- package/dist/src/render/markdown.js +3 -0
- package/dist/src/render/qr.js +1 -1
- package/dist/src/state/cart_session.js +1 -4
- package/dist/src/state/client_context.js +0 -72
- package/dist/src/state/config.js +14 -44
- package/dist/src/state/device_authority.js +41 -141
- package/docs/agent/buyer/cart-checkout.json +5 -6
- package/docs/agent/buyer/catalog-list.json +6 -24
- package/docs/agent/buyer/identity-and-sessions.json +9 -25
- package/docs/agent/buyer/install-and-setup.json +15 -11
- package/docs/agent/buyer/orders-refunds.json +5 -12
- package/docs/agent/buyer/payment-flow.json +3 -4
- package/docs/agent/buyer/purchased-content.json +4 -18
- package/docs/agent/buyer/quickstart.json +16 -24
- package/docs/agent/buyer/render-hosts.json +12 -23
- package/docs/agent/buyer/service-feedback.json +1 -2
- package/docs/cli-reference/agent-types.md +2 -7
- package/docs/cli-reference/commands/buy.md +1 -2
- package/docs/cli-reference/commands/cart/add.md +1 -1
- package/docs/cli-reference/commands/cart/clear.md +1 -1
- package/docs/cli-reference/commands/cart/index.md +1 -1
- package/docs/cli-reference/commands/cart/next.md +1 -1
- package/docs/cli-reference/commands/cart/remove.md +1 -1
- package/docs/cli-reference/commands/cart/show.md +1 -1
- package/docs/cli-reference/commands/catalog/index.md +1 -1
- package/docs/cli-reference/commands/catalog/list.md +2 -2
- package/docs/cli-reference/commands/checkout.md +2 -12
- package/docs/cli-reference/commands/device.md +11 -18
- package/docs/cli-reference/commands/docs/index.md +1 -1
- package/docs/cli-reference/commands/docs/list.md +1 -1
- package/docs/cli-reference/commands/docs/search.md +1 -1
- package/docs/cli-reference/commands/docs/show.md +1 -1
- package/docs/cli-reference/commands/install.md +6 -8
- package/docs/cli-reference/commands/next.md +1 -1
- package/docs/cli-reference/commands/order.md +1 -1
- package/docs/cli-reference/commands/pay.md +1 -1
- package/docs/cli-reference/commands/readyz.md +10 -58
- package/docs/cli-reference/commands/refund/cancel.md +1 -1
- package/docs/cli-reference/commands/refund/create.md +1 -1
- package/docs/cli-reference/commands/refund/get.md +2 -2
- package/docs/cli-reference/commands/refund/index.md +1 -1
- package/docs/cli-reference/commands/refund/list.md +1 -1
- package/docs/cli-reference/commands/refund/watch.md +1 -1
- package/docs/cli-reference/commands/services/action.md +3 -24
- package/docs/cli-reference/commands/services/checkout.md +2 -13
- package/docs/cli-reference/commands/services/events.md +1 -1
- package/docs/cli-reference/commands/services/get.md +2 -4
- package/docs/cli-reference/commands/services/index.md +3 -4
- package/docs/cli-reference/commands/services/invoke.md +4 -6
- package/docs/cli-reference/commands/services/list.md +1 -1
- package/docs/cli-reference/commands/services/next.md +12 -105
- package/docs/cli-reference/commands/services/quote.md +3 -3
- package/docs/cli-reference/commands/services/read-result.md +6 -17
- package/docs/cli-reference/commands/services/start.md +2 -2
- package/docs/cli-reference/commands/vault/access.md +1 -5
- package/docs/cli-reference/conventions.md +6 -4
- package/docs/cli-reference/index.md +1 -8
- package/docs/skill-bundle-rollout/01-mcp-authentication.md +134 -0
- package/docs/skill-bundle-rollout/02-platform-bundle-repositories.md +210 -0
- package/docs/skill-bundle-rollout/03-platform-publishing.md +161 -0
- package/docs/skill-bundle-rollout/04-first-wave-platforms.md +69 -0
- package/docs/skill-bundle-rollout/05-sync-operations.md +62 -0
- package/docs/skill-bundle-rollout/README.md +151 -0
- package/package.json +13 -20
- package/skills/itpay/SKILL.md +105 -101
- package/assets/sell-preview/index.html +0 -6
- package/assets/sell-preview/preview.js +0 -9
- package/bin/darwin-amd64/itpay-sell +0 -0
- package/bin/darwin-arm64/itpay-sell +0 -0
- package/bin/linux-amd64/itpay-sell +0 -0
- package/bin/linux-arm64/itpay-sell +0 -0
- package/bin/windows-amd64/itpay-sell.exe +0 -0
- package/dist/src/commands/presentation.js +0 -153
- package/dist/src/commands/rail_catalog.js +0 -261
- package/dist/src/render/browser.js +0 -65
- package/dist/src/sell/commands.js +0 -80
- package/dist/src/sell/contract.js +0 -86
- package/dist/src/sell/local.js +0 -260
- package/dist/src/sell/mcp.js +0 -49
- package/dist/src/sell/preview.js +0 -70
- package/dist/src/sell/sync.js +0 -145
- package/dist/src/state/account_auth.js +0 -332
- package/dist/src/state/task_journal.js +0 -90
- package/docs/agent/buyer/rail-booking.json +0 -162
- package/docs/agent/buyer/rail-fast-checkout.json +0 -48
- package/docs/cli-reference/commands/auth.md +0 -96
- package/docs/cli-reference/commands/sell.md +0 -1364
- package/docs/cli-reference/commands/services/page.md +0 -71
- package/docs/cli-reference/commands/services/run.md +0 -156
- package/docs/sell.md +0 -84
- /package/bin/{itp.mjs → itp} +0 -0
package/dist/src/main.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { agentAuth } from "./state/account_auth.js";
|
|
2
|
-
import { registerSell } from "./sell/commands.js";
|
|
3
|
-
import { readFileSync as readWorkflowInputFile, statSync as statWorkflowInputFile } from "node:fs";
|
|
4
|
-
import { runServicesRun } from "./commands/services.js";
|
|
5
1
|
// V3 CLI entrypoint. Each command maps 1:1 to a route family in
|
|
6
2
|
// services/backend/internal/httpapi/handlers/*.go. Commands only
|
|
7
3
|
// orchestrate; HTTP and rendering live in src/client and src/render.
|
|
8
4
|
import { Command } from "commander";
|
|
9
|
-
import { BackendOverrideError, CLI_VERSION, cliDistribution, loadConfig, cartSessionPath, newBackendClient } from "./state/config.js";
|
|
10
|
-
import { DeviceAuthority, DeviceAuthorizationError,
|
|
5
|
+
import { BackendOverrideError, API_CONTRACT_REVISION, CLI_VERSION, cliDistribution, loadConfig, cartSessionPath, newBackendClient } from "./state/config.js";
|
|
6
|
+
import { DeviceAuthority, DeviceAuthorizationError, DeviceStateError } from "./state/device_authority.js";
|
|
11
7
|
import { CartSession } from "./state/cart_session.js";
|
|
12
8
|
import { defaultHostForAgentType, normalizeHost, validateContext } from "./state/client_context.js";
|
|
13
9
|
import { HttpError } from "./client/http.js";
|
|
@@ -25,11 +21,11 @@ import { runCancelRefund, runGetRefund, runListRefunds, runRefund, runWatchRefun
|
|
|
25
21
|
import { runCartAdd, runCartAddQuoteServer, runCartAddServer, runCartAbandonServer, runCartClear, runCartNext, runCartRemove, runCartRemoveServer, runCartShow, runCartShowServer, } from "./commands/cart.js";
|
|
26
22
|
import { CommandContractError, errorRecoveryActions, printErrorRecovery, writeCommandEnvelope } from "./commands/guidance.js";
|
|
27
23
|
import { runDocsList, runDocsShow, runDocsSearch } from "./commands/docs.js";
|
|
28
|
-
import {
|
|
24
|
+
import { runInstall } from "./commands/install.js";
|
|
29
25
|
import { runSkillShow } from "./commands/skill.js";
|
|
30
26
|
import { runNext } from "./commands/next.js";
|
|
31
27
|
import { runVaultAccess, runVaultList, runVaultRead } from "./commands/vault.js";
|
|
32
|
-
import { collectOption, parseKeyValueList, runServicesAction, runServicesCheckout, runServicesEvents, runServicesGet, runServicesInvoke, runServicesList, runServicesNext,
|
|
28
|
+
import { collectOption, parseKeyValueList, runServicesAction, runServicesCheckout, runServicesEvents, runServicesGet, runServicesInvoke, runServicesList, runServicesNext, runServicesReadResult, runServicesQuote, runServicesStart, } from "./commands/services.js";
|
|
33
29
|
const program = new Command();
|
|
34
30
|
program
|
|
35
31
|
.name("itpay")
|
|
@@ -116,7 +112,6 @@ function reportCLIError(error, contract) {
|
|
|
116
112
|
const backendOverrideError = error instanceof BackendOverrideError ? error : undefined;
|
|
117
113
|
const deviceError = error instanceof DeviceAuthorizationError ? error : undefined;
|
|
118
114
|
const stateError = error instanceof DeviceStateError ? error : undefined;
|
|
119
|
-
const lockError = error instanceof DeviceLockBusyError ? error : undefined;
|
|
120
115
|
const transportError = error instanceof HttpTransportError ? error : undefined;
|
|
121
116
|
const httpRecovery = errorRecoveryActions(error).map((action) => ({
|
|
122
117
|
command: action.command,
|
|
@@ -128,9 +123,6 @@ function reportCLIError(error, contract) {
|
|
|
128
123
|
error.code === "client_compatibility_headers_required" ||
|
|
129
124
|
error.code === "platform_release_unavailable" ||
|
|
130
125
|
(error.status === 404 && error.code === "unknown_error"));
|
|
131
|
-
const requiredCLIVersion = incompatible && error instanceof HttpError && /^\d+\.\d+\.\d+$/.test(error.payload?.minimum_cli_version ?? "")
|
|
132
|
-
? error.payload.minimum_cli_version
|
|
133
|
-
: undefined;
|
|
134
126
|
const backendInternal = error instanceof HttpError && error.status === 500 && error.code === "internal_error";
|
|
135
127
|
const providerConnectionUnavailable = error instanceof HttpError && error.code === "provider_connection_unavailable";
|
|
136
128
|
const providerTemporary = error instanceof HttpError && error.code === "provider_temporarily_unavailable";
|
|
@@ -138,48 +130,37 @@ function reportCLIError(error, contract) {
|
|
|
138
130
|
const providerInputRejected = error instanceof HttpError && error.code === "provider_input_rejected";
|
|
139
131
|
const providerContractMismatch = error instanceof HttpError && error.code === "provider_contract_mismatch";
|
|
140
132
|
const capabilityInputInvalid = error instanceof HttpError && error.code === "capability_input_invalid";
|
|
141
|
-
const
|
|
142
|
-
deviceError.code === "agent_device_key_conflict"));
|
|
143
|
-
const deviceRecovery = deviceError ? [
|
|
144
|
-
{
|
|
133
|
+
const deviceRecovery = deviceError ? [{
|
|
145
134
|
command: "itpay skill show itpay --json",
|
|
146
135
|
reason: "读取 ItPay 身份边界;该错误需要用户或运营恢复 Backend 登记,不能通过换类型或删除本地身份绕过",
|
|
147
|
-
}
|
|
148
|
-
] : [];
|
|
136
|
+
}] : [];
|
|
149
137
|
const stateRecovery = stateError ? [{
|
|
150
138
|
command: "itpay skill show itpay --json",
|
|
151
139
|
reason: "读取 Device 状态边界;修复当前 Host 的持久写权限后重试原命令",
|
|
152
140
|
}] : [];
|
|
153
|
-
const lockRecovery = lockError ? [{
|
|
154
|
-
command: "itpay device repair-lock --json",
|
|
155
|
-
reason: "检查锁持有者;只回收已退出进程留下的锁,不改变设备身份",
|
|
156
|
-
}] : [];
|
|
157
141
|
const authorizationInstruction = stateError
|
|
158
|
-
?
|
|
159
|
-
:
|
|
160
|
-
? "
|
|
161
|
-
:
|
|
162
|
-
? "
|
|
163
|
-
: deviceError
|
|
164
|
-
? "
|
|
165
|
-
:
|
|
166
|
-
|
|
167
|
-
: deviceKeyResettable
|
|
168
|
-
? "服务端拒绝当前设备私钥登记(轮换或冲突)。停止自动恢复;换钥会放弃旧身份,旧执行访问不会迁移。仅用户或运营明确决定放弃旧身份后,按 device reset-key 帮助执行。"
|
|
169
|
-
: deviceError
|
|
170
|
-
? "Device 身份验证失败;停止重试,不要切换 Agent Type、删除状态或旋转私钥。"
|
|
171
|
-
: undefined;
|
|
172
|
-
if (contract || commandError || backendOverrideError || lockError || stateError) {
|
|
142
|
+
? "当前运行环境无法写入 owner-only Device 状态;请保持同一 Node、CLI 和 Agent Type,在允许持久写入 ~/.itpay-v3 的执行环境中重试。不要手工创建 lock、删除 identity 或换运行时碰运气。"
|
|
143
|
+
: error instanceof HttpError && error.code === "agent_device_session_required"
|
|
144
|
+
? "CLI 已自动续期并重试同一请求一次,仍被拒绝;停止重试,不要切换 Agent Type 或旋转身份。"
|
|
145
|
+
: deviceError?.code === "agent_device_revoked"
|
|
146
|
+
? "Backend 已撤销当前 Device 登记;CLI 没有自动创建替代身份。停止重试并请用户或运营恢复登记。"
|
|
147
|
+
: deviceError
|
|
148
|
+
? "Device 身份验证失败;停止重试,不要切换 Agent Type、删除状态或旋转私钥。"
|
|
149
|
+
: undefined;
|
|
150
|
+
if (contract || commandError || backendOverrideError) {
|
|
173
151
|
writeCommandEnvelope({
|
|
174
152
|
status: "error",
|
|
175
153
|
error: {
|
|
176
|
-
code: incompatible ? "backend_contract_incompatible" : backendOverrideError?.code ?? commandError?.code ?? (error instanceof HttpError ? error.code : transportError?.code ?? stateError?.code ??
|
|
154
|
+
code: incompatible ? "backend_contract_incompatible" : backendOverrideError?.code ?? commandError?.code ?? (error instanceof HttpError ? error.code : transportError?.code ?? stateError?.code ?? deviceError?.code ?? contract?.code ?? "command_failed"),
|
|
177
155
|
message: error instanceof Error ? error.message : String(error),
|
|
178
156
|
},
|
|
179
|
-
...(
|
|
157
|
+
...(incompatible && error instanceof HttpError ? {
|
|
180
158
|
result: {
|
|
181
159
|
current_cli_version: CLI_VERSION,
|
|
182
|
-
|
|
160
|
+
current_api_contract_revision: API_CONTRACT_REVISION,
|
|
161
|
+
platform_api_contract_revision: error.payload?.api_contract_revision ?? null,
|
|
162
|
+
minimum_cli_version: error.payload?.minimum_cli_version ?? null,
|
|
163
|
+
maximum_cli_major: error.payload?.maximum_cli_major ?? null,
|
|
183
164
|
},
|
|
184
165
|
} : transportError ? {
|
|
185
166
|
result: {
|
|
@@ -199,9 +180,7 @@ function reportCLIError(error, contract) {
|
|
|
199
180
|
},
|
|
200
181
|
} : {}),
|
|
201
182
|
instruction: incompatible
|
|
202
|
-
?
|
|
203
|
-
? distributionUpgradeInstruction()
|
|
204
|
-
: "告诉用户 ItPay 当前版本信息无法验证,因此本次服务不能安全继续。停止所有 ItPay 和 npm 操作;不要转述技术错误、猜版本、切换 Agent Type 或删除 Device 身份。"
|
|
183
|
+
? "当前 CLI 与 Backend 合约不兼容或无法核验。minimum_cli_version 仅是协议门槛,不是已验证的推荐安装包。停止本轮,请维护方提供与当前 Backend 配套的制品;不要猜版本、降级、切换 Agent Type 或删除 Device 身份。"
|
|
205
184
|
: backendInternal
|
|
206
185
|
? "告诉用户 ItPay 当前暂时无法完成这项服务并停止。不要转述内部错误、重试、修改身份、创建替代服务或进入任何付费路径。"
|
|
207
186
|
: providerConnectionUnavailable
|
|
@@ -215,26 +194,25 @@ function reportCLIError(error, contract) {
|
|
|
215
194
|
: providerRejected
|
|
216
195
|
? "告诉用户数据来源没有接受本次请求,但没有说明是输入错误,并按 result.quota 说明额度状态,然后停止。不要转述内部错误、修改输入、重试或创建新查询。"
|
|
217
196
|
: capabilityInputInvalid
|
|
218
|
-
? "
|
|
197
|
+
? "告诉用户当前输入不完整或格式不正确;数据来源尚未调用,额度没有变化。不要转述技术错误或原样重试;用户提供修正信息后继续同一次服务。"
|
|
219
198
|
: transportError
|
|
220
|
-
?
|
|
221
|
-
? "
|
|
222
|
-
:
|
|
199
|
+
? contract?.initialization
|
|
200
|
+
? "本轮初始化检查未完成;CLI 已结束有限的网络尝试,尚未确认后端或设备就绪。结束本轮,网络恢复后可重新检查;不要继续登录或业务操作。"
|
|
201
|
+
: transportError.attempts > 1
|
|
202
|
+
? "临时网络故障;CLI 已仅对可安全重放的操作完成有限自动重试,但仍未获得完整响应。按 recovery 查询同一资源的权威状态;不要创建替代 Checkout、Execution、Payment 或 Refund。"
|
|
203
|
+
: "网络在完整响应前中断;当前写操作没有安全重放合同,因此 CLI 未自动重试。按 recovery 查询权威状态;不要原样重放或创建替代 Checkout、Execution、Payment 或 Refund。"
|
|
223
204
|
: backendOverrideError
|
|
224
|
-
? "移除 ITPAY_BACKEND_URL
|
|
205
|
+
? "移除 ITPAY_BACKEND_URL 使用正式环境,或准确设置为 https://dev.itpay.ai。"
|
|
225
206
|
: commandError?.instruction ?? authorizationInstruction ?? contract?.instruction ?? "检查命令参数后重试。",
|
|
226
207
|
next: null,
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
? requiredCLIVersion
|
|
230
|
-
? [distributionUpgradeAction(requiredCLIVersion)]
|
|
231
|
-
: []
|
|
208
|
+
recovery: incompatible || (transportError && contract?.initialization)
|
|
209
|
+
? []
|
|
232
210
|
: backendInternal || providerConnectionUnavailable || providerTemporary || providerInputRejected || providerContractMismatch || providerRejected || capabilityInputInvalid
|
|
233
211
|
? []
|
|
234
|
-
: backendOverrideError ? [] : commandError?.recovery ?? (stateError ? stateRecovery :
|
|
212
|
+
: backendOverrideError ? [] : commandError?.recovery ?? (stateError ? stateRecovery : deviceError ? deviceRecovery : identityRecovery ? httpRecovery : contract?.recovery ?? []),
|
|
235
213
|
}, {
|
|
236
|
-
...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : backendOverrideError
|
|
237
|
-
output: (text) => { process.
|
|
214
|
+
...(contract?.jsonOutput !== undefined ? { jsonOutput: contract.jsonOutput } : backendOverrideError ? { jsonOutput: process.argv.includes("--json") } : {}),
|
|
215
|
+
output: (text) => { process.stderr.write(text); },
|
|
238
216
|
});
|
|
239
217
|
process.exitCode = 1;
|
|
240
218
|
return;
|
|
@@ -275,14 +253,8 @@ function distributionUpgradeAction(version) {
|
|
|
275
253
|
reason: `更新包含 ItPay CLI ${version} 的 Kimi plugin`,
|
|
276
254
|
};
|
|
277
255
|
default:
|
|
278
|
-
return { command: `npm install -g @itpay/cli@${version}`, reason: "
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function distributionUpgradeInstruction() {
|
|
282
|
-
if (cliDistribution() === "npm") {
|
|
283
|
-
return "当前 CLI 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,将 @itpay/cli 更新到 Backend 指定的精确版本。安装完成后确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要安装 latest、猜测版本、切换 Agent Type 或删除 Device 身份。";
|
|
256
|
+
return { command: `npm install -g @itpay/cli@${version}`, reason: "恢复当前 CLI 版本的完整安装包" };
|
|
284
257
|
}
|
|
285
|
-
return "当前平台 bundle 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,更新平台 Skill/plugin 后启动新会话,确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要运行 npm、猜测版本、切换 Agent Type 或删除 Device 身份。";
|
|
286
258
|
}
|
|
287
259
|
program
|
|
288
260
|
.command("readyz")
|
|
@@ -301,61 +273,37 @@ program
|
|
|
301
273
|
reportCLIError(error, {
|
|
302
274
|
jsonOutput: Boolean(options.json),
|
|
303
275
|
code: "backend_unavailable",
|
|
276
|
+
initialization: true,
|
|
304
277
|
instruction: `当前官方 Backend ${config.baseURL} 不可用;恢复前不要继续下单,也不要切换环境。`,
|
|
305
|
-
recovery: [
|
|
306
|
-
{ command: "itpay readyz", reason: "重试当前官方 Backend 的可用性检查" },
|
|
307
|
-
],
|
|
278
|
+
recovery: [],
|
|
308
279
|
});
|
|
309
280
|
}
|
|
310
281
|
});
|
|
311
|
-
const authCmd = program.command("auth").description("Log in and bind this enrolled Agent to your ItPay account");
|
|
312
|
-
for (const action of ["login", "status"]) {
|
|
313
|
-
authCmd.command(action).option("--json", "output JSON").action(async (options) => {
|
|
314
|
-
const config = loadConfig();
|
|
315
|
-
try {
|
|
316
|
-
const result = await agentAuth(action, config.baseURL, newBackendClient(config));
|
|
317
|
-
// AUTH06: auth output goes through the same envelope renderer as every
|
|
318
|
-
// other command — next/recovery keep Agent Type + Backend qualification
|
|
319
|
-
// and non-JSON output is human-readable instead of a raw JSON dump.
|
|
320
|
-
const envelope = {
|
|
321
|
-
status: String(result.status ?? "error"),
|
|
322
|
-
result: (result.result ?? Object.fromEntries(Object.entries(result).filter(([key]) => !["status", "instruction", "next", "recovery", "handoff"].includes(key)))),
|
|
323
|
-
...(result.handoff ? { handoff: result.handoff } : {}),
|
|
324
|
-
instruction: typeof result.instruction === "string" ? result.instruction : "按返回状态继续。",
|
|
325
|
-
next: (result.next ?? null),
|
|
326
|
-
recovery: (result.recovery ?? []),
|
|
327
|
-
};
|
|
328
|
-
writeCommandEnvelope(envelope, { jsonOutput: Boolean(options.json) });
|
|
329
|
-
}
|
|
330
|
-
catch (error) {
|
|
331
|
-
reportCLIError(error, { jsonOutput: Boolean(options.json), code: "account_login_failed", instruction: "完成官方网页登录后重试 itpay auth status;不要清除设备登记。" });
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
282
|
// --- device ---------------------------------------------------------------
|
|
336
283
|
const deviceCmd = program.command("device").description("Recover the current official Backend registration after an operator-confirmed reset");
|
|
337
|
-
deviceCmd
|
|
338
|
-
.
|
|
339
|
-
.description("Safely inspect and recover a local Device identity lock left by an exited process")
|
|
284
|
+
deviceCmd.command("status")
|
|
285
|
+
.description("Verify this device with the current Backend; account binding is not queried")
|
|
340
286
|
.option("--json", "output JSON instead of terminal text")
|
|
341
|
-
.action((options) => {
|
|
287
|
+
.action(async (options) => {
|
|
288
|
+
const config = loadConfig();
|
|
342
289
|
try {
|
|
343
|
-
|
|
344
|
-
const
|
|
290
|
+
await requirePlatformCompatibility(newBackendClient(config));
|
|
291
|
+
const authority = new DeviceAuthority({
|
|
345
292
|
baseURL: config.baseURL,
|
|
346
293
|
...(config.agentType ? { requestedAgentType: config.agentType } : {}),
|
|
347
|
-
compatibilityHeaders: {},
|
|
348
|
-
})
|
|
294
|
+
compatibilityHeaders: { "X-ItPay-CLI-Version": CLI_VERSION, "X-ItPay-Contract-Revision": API_CONTRACT_REVISION },
|
|
295
|
+
});
|
|
296
|
+
const verified = await authority.status();
|
|
349
297
|
writeCommandEnvelope({
|
|
350
|
-
status:
|
|
351
|
-
result: {
|
|
352
|
-
instruction:
|
|
298
|
+
status: "device_ready",
|
|
299
|
+
result: { ...verified, backend_url: config.baseURL, account_binding: "unavailable" },
|
|
300
|
+
instruction: "当前 Backend 已验证设备身份。此版本不查询账号绑定,不能据此认定已登录或未登录;无需在安装阶段登录,不自动开始业务。首次检查可登记设备,后续复用同一身份。",
|
|
353
301
|
next: null,
|
|
354
302
|
recovery: [],
|
|
355
|
-
}, { jsonOutput: Boolean(options.json)
|
|
303
|
+
}, { jsonOutput: Boolean(options.json) });
|
|
356
304
|
}
|
|
357
305
|
catch (error) {
|
|
358
|
-
reportCLIError(error, { jsonOutput: Boolean(options.json), code: "
|
|
306
|
+
reportCLIError(error, { jsonOutput: Boolean(options.json), code: "device_status_unavailable", initialization: true, instruction: "本轮设备检查未完成;停止本轮,保留原身份,不自动发起登录。" });
|
|
359
307
|
}
|
|
360
308
|
});
|
|
361
309
|
deviceCmd
|
|
@@ -410,56 +358,6 @@ deviceCmd
|
|
|
410
358
|
});
|
|
411
359
|
}
|
|
412
360
|
});
|
|
413
|
-
deviceCmd
|
|
414
|
-
.command("reset-key")
|
|
415
|
-
.description("Discard the local Ed25519 device key and all Backend registrations so the next command enrolls as a new device")
|
|
416
|
-
.option("--confirm-key-reset", "confirm that the current device key must be abandoned and a new identity created")
|
|
417
|
-
.option("--json", "output JSON instead of terminal text")
|
|
418
|
-
.action(async (options) => {
|
|
419
|
-
const config = loadConfig();
|
|
420
|
-
try {
|
|
421
|
-
if (!config.agentType || !INSTALL_AGENT_TYPES.some(type => type === config.agentType)) {
|
|
422
|
-
throw new CommandContractError("agent_type_required", "supported Agent Type required before key reset", "先按真实宿主配置正式支持的Agent Type;尚未修改本地密钥。", [{ command: "itpay install --json", reason: "读取当前宿主配置" }]);
|
|
423
|
-
}
|
|
424
|
-
if (!options.confirmKeyReset) {
|
|
425
|
-
throw new CommandContractError("key_reset_confirmation_required", "--confirm-key-reset is required", "仅在用户或运营明确决定放弃旧身份后使用;会丢弃本地私钥,旧执行访问不会迁移。internal_error 需服务端恢复,不能换钥。", [{ command: "itpay docs show identity-and-sessions --json", reason: "检查适用边界" }]);
|
|
426
|
-
}
|
|
427
|
-
const reset = await new DeviceAuthority({
|
|
428
|
-
baseURL: config.baseURL,
|
|
429
|
-
...(config.agentType ? { requestedAgentType: config.agentType } : {}),
|
|
430
|
-
compatibilityHeaders: {},
|
|
431
|
-
}).resetDeviceKey();
|
|
432
|
-
writeCommandEnvelope({
|
|
433
|
-
status: "device_key_reset",
|
|
434
|
-
result: {
|
|
435
|
-
removed_backend_registrations: reset.removedBackends,
|
|
436
|
-
private_key_preserved: false,
|
|
437
|
-
server_side_device: "orphaned_under_previous_key",
|
|
438
|
-
},
|
|
439
|
-
instruction: "本地设备私钥已重置;下一次需要设备身份的命令会以全新 Ed25519 密钥重新登记;这不能恢复旧身份的订单或用于刷新额度。服务端旧设备记录保留为孤儿;如需清理请走运营流程。",
|
|
440
|
-
next: {
|
|
441
|
-
command: `itpay --agent-type ${config.agentType} services list --limit 1 --json`,
|
|
442
|
-
reason: "用无业务写入的签名请求完成新密钥的重新登记",
|
|
443
|
-
},
|
|
444
|
-
recovery: [],
|
|
445
|
-
}, {
|
|
446
|
-
jsonOutput: Boolean(options.json),
|
|
447
|
-
plainResult: [
|
|
448
|
-
`removed_backend_registrations: ${reset.removedBackends.join(", ") || "none"}`,
|
|
449
|
-
"private_key: rotated (previous key discarded)",
|
|
450
|
-
"server_side_device: orphaned under previous key",
|
|
451
|
-
],
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
reportCLIError(error, {
|
|
456
|
-
jsonOutput: Boolean(options.json),
|
|
457
|
-
code: "device_key_reset_failed",
|
|
458
|
-
instruction: "仅在确认要放弃当前设备私钥时使用;普通 session 失效或 revoked 不需要本命令。",
|
|
459
|
-
recovery: [{ command: "itpay docs show identity-and-sessions --json", reason: "检查 Device 恢复边界" }],
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
361
|
// --- skill ----------------------------------------------------------------
|
|
464
362
|
const skillCmd = program.command("skill").description("Read complete packaged Agent skills");
|
|
465
363
|
skillCmd
|
|
@@ -934,13 +832,6 @@ program
|
|
|
934
832
|
.option("--id <checkout_id>")
|
|
935
833
|
.option("--token <display_token>")
|
|
936
834
|
.option("--locale <locale>", "payment card language: zh-CN|en", "zh-CN")
|
|
937
|
-
.option("--present <method>", "display once: auto|browser|image|link|none (JSON default plans only)")
|
|
938
|
-
.option("--no-open", "never open a browser window")
|
|
939
|
-
.option("--viewer <viewer>", "device the human is looking at: desktop|mobile|unknown")
|
|
940
|
-
.option("--relay-option <ref>", "server-issued contact option reference for message relay")
|
|
941
|
-
.option("--confirm-relay", "confirm the human explicitly consented to this relay send")
|
|
942
|
-
.option("--request-key <key>", "stable idempotency key for a relay request")
|
|
943
|
-
.option("--relay-status <relay_id>", "read a prior relay result; never resends")
|
|
944
835
|
.option("--json", "output compact JSON")
|
|
945
836
|
.action(async (options) => {
|
|
946
837
|
const config = loadConfig();
|
|
@@ -958,22 +849,12 @@ program
|
|
|
958
849
|
await runCheckoutPresentation(backend, {
|
|
959
850
|
checkoutID,
|
|
960
851
|
displayToken,
|
|
961
|
-
...(snap.lastCheckoutID === checkoutID && snap.lastCheckoutURL
|
|
962
|
-
? { savedCheckoutURL: snap.lastCheckoutURL }
|
|
963
|
-
: {}),
|
|
964
852
|
host,
|
|
965
853
|
...(options.target ? { target: options.target } : {}),
|
|
966
854
|
...(config.agentType ? { agentType: config.agentType } : {}),
|
|
967
855
|
baseURL: config.baseURL,
|
|
968
856
|
locale: options.locale,
|
|
969
857
|
jsonOutput: Boolean(options.json),
|
|
970
|
-
...(options.present ? { present: options.present } : {}),
|
|
971
|
-
...(options.noOpen ? { noOpen: true } : {}),
|
|
972
|
-
...(options.viewer ? { viewer: options.viewer } : {}),
|
|
973
|
-
...(options.relayOption ? { relayOption: options.relayOption } : {}),
|
|
974
|
-
...(options.confirmRelay ? { confirmRelay: true } : {}),
|
|
975
|
-
...(options.requestKey ? { requestKey: options.requestKey } : {}),
|
|
976
|
-
...(options.relayStatus ? { relayStatus: options.relayStatus } : {}),
|
|
977
858
|
});
|
|
978
859
|
}
|
|
979
860
|
catch (error) {
|
|
@@ -1349,98 +1230,7 @@ vault
|
|
|
1349
1230
|
}
|
|
1350
1231
|
});
|
|
1351
1232
|
// --- service execution ----------------------------------------------------
|
|
1352
|
-
const WORKFLOW_INPUT_JSON_MAX_BYTES = 256 * 1024;
|
|
1353
|
-
function readInputJsonObject(file, code, recovery) {
|
|
1354
|
-
let parsed;
|
|
1355
|
-
try {
|
|
1356
|
-
if (statWorkflowInputFile(file).size > WORKFLOW_INPUT_JSON_MAX_BYTES) {
|
|
1357
|
-
throw new CommandContractError(code, `--input-json file exceeds ${WORKFLOW_INPUT_JSON_MAX_BYTES} bytes`, "输入文件过大;只提交服务端 input_schema 声明的字段。", recovery);
|
|
1358
|
-
}
|
|
1359
|
-
parsed = JSON.parse(readWorkflowInputFile(file, "utf8"));
|
|
1360
|
-
}
|
|
1361
|
-
catch (error) {
|
|
1362
|
-
if (error instanceof CommandContractError)
|
|
1363
|
-
throw error;
|
|
1364
|
-
throw new CommandContractError(code, `--input-json is not readable valid JSON: ${error instanceof Error ? error.message : String(error)}`, "确认文件存在且为合法 JSON;按服务端 input_schema 字段填写。", recovery);
|
|
1365
|
-
}
|
|
1366
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1367
|
-
throw new CommandContractError(code, "--input-json must contain a JSON object", "确认输入必须是 JSON 对象;按服务端 input_schema 字段填写。", recovery);
|
|
1368
|
-
}
|
|
1369
|
-
return parsed;
|
|
1370
|
-
}
|
|
1371
1233
|
const services = program.command("services").description("Generic V3 Service Execution commands");
|
|
1372
|
-
services
|
|
1373
|
-
.command("run")
|
|
1374
|
-
.description("Run a published service and present Checkout when payment is required")
|
|
1375
|
-
.argument("<service_id>")
|
|
1376
|
-
.option("--input-json <file>", "JSON object containing the service input")
|
|
1377
|
-
.option("--execution <execution_id>", "resume the same execution")
|
|
1378
|
-
.option("--timeout <seconds>", "maximum wait before returning current progress", Number, 120)
|
|
1379
|
-
.option("--host <host>")
|
|
1380
|
-
.option("--target <target>")
|
|
1381
|
-
.option("--json", "output JSON")
|
|
1382
|
-
.action(async (serviceID, options) => {
|
|
1383
|
-
const config = loadConfig();
|
|
1384
|
-
try {
|
|
1385
|
-
const input = options.inputJson
|
|
1386
|
-
? readInputJsonObject(options.inputJson, "workflow_input_invalid", [
|
|
1387
|
-
{ command: `itpay services run ${serviceID} --json`, reason: "重新读取服务输入声明" },
|
|
1388
|
-
])
|
|
1389
|
-
: undefined;
|
|
1390
|
-
if (!Number.isFinite(options.timeout) || options.timeout < 0 || options.timeout > 600) {
|
|
1391
|
-
throw new Error("timeout must be between 0 and 600 seconds");
|
|
1392
|
-
}
|
|
1393
|
-
await runServicesRun(newBackendClient(config), config, serviceID, input, {
|
|
1394
|
-
...(options.execution ? { executionID: options.execution } : {}),
|
|
1395
|
-
jsonOutput: Boolean(options.json),
|
|
1396
|
-
host: withHost(options.host, config.agentType, options.target),
|
|
1397
|
-
...(options.target ? { target: options.target } : {}),
|
|
1398
|
-
timeoutSeconds: options.timeout,
|
|
1399
|
-
});
|
|
1400
|
-
}
|
|
1401
|
-
catch (error) {
|
|
1402
|
-
reportCLIError(error, {
|
|
1403
|
-
jsonOutput: Boolean(options.json), code: "workflow_run_failed",
|
|
1404
|
-
instruction: "本次操作失败;按已返回的具体原因和恢复动作处理。没有恢复动作时停止,不猜字段或重复创建购买。", recovery: [],
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
1408
|
-
services
|
|
1409
|
-
.command("page")
|
|
1410
|
-
.description("Read one page of a saved service result set (same stored version, no re-query, no quota)")
|
|
1411
|
-
.argument("<service_execution_id>")
|
|
1412
|
-
.argument("<result_item_id>")
|
|
1413
|
-
.option("--offset <offset>", "zero-based candidate offset", Number, 0)
|
|
1414
|
-
.option("--cursor <cursor>", "opaque v2 page cursor (rcur_<offset>); takes precedence over --offset")
|
|
1415
|
-
.option("--limit <limit>", "page size (1-20)", Number, 20)
|
|
1416
|
-
.option("--json", "output JSON")
|
|
1417
|
-
.action(async (serviceExecutionID, resultItemID, options) => {
|
|
1418
|
-
const config = loadConfig();
|
|
1419
|
-
const backend = newBackendClient(config);
|
|
1420
|
-
try {
|
|
1421
|
-
const cursorOffset = typeof options.cursor === "string" && /^rcur_\d+$/.test(options.cursor)
|
|
1422
|
-
? Number(options.cursor.slice(5))
|
|
1423
|
-
: undefined;
|
|
1424
|
-
if (options.cursor !== undefined && cursorOffset === undefined) {
|
|
1425
|
-
throw new CommandContractError("cursor_invalid", "--cursor must be an opaque rcur_<offset> token from a previous page response", "--cursor 需为上一页返回的 rcur_<offset> 不透明游标。", []);
|
|
1426
|
-
}
|
|
1427
|
-
await runServicesPage(backend, serviceExecutionID, resultItemID, {
|
|
1428
|
-
offset: cursorOffset ?? options.offset,
|
|
1429
|
-
limit: options.limit,
|
|
1430
|
-
jsonOutput: Boolean(options.json),
|
|
1431
|
-
});
|
|
1432
|
-
}
|
|
1433
|
-
catch (error) {
|
|
1434
|
-
reportCLIError(error, {
|
|
1435
|
-
jsonOutput: Boolean(options.json),
|
|
1436
|
-
code: "service_page_failed",
|
|
1437
|
-
instruction: "分页读取的是已保存的同版本结果;不要重新发起查询或新建 execution。",
|
|
1438
|
-
recovery: [
|
|
1439
|
-
{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取当前结果页与合法动作" },
|
|
1440
|
-
],
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
});
|
|
1444
1234
|
services
|
|
1445
1235
|
.command("start")
|
|
1446
1236
|
.description("Start a contract-backed service execution")
|
|
@@ -1476,18 +1266,12 @@ services
|
|
|
1476
1266
|
.argument("<service_execution_id>")
|
|
1477
1267
|
.requiredOption("--capability <capability_id>")
|
|
1478
1268
|
.option("--input <key=value>", "redacted input summary", collectOption, [])
|
|
1479
|
-
.option("--input-json <file>", "JSON object containing the complete capability input")
|
|
1480
1269
|
.option("--json", "output JSON")
|
|
1481
1270
|
.action(async (serviceExecutionID, options) => {
|
|
1482
1271
|
const config = loadConfig();
|
|
1483
1272
|
const backend = newBackendClient(config);
|
|
1484
1273
|
try {
|
|
1485
|
-
|
|
1486
|
-
if (options.inputJson && options.input.length > 0) {
|
|
1487
|
-
throw new CommandContractError("capability_input_invalid", "--input and --input-json cannot be combined", "嵌套输入用 --input-json <file>,扁平输入用 --input key=value,二选一;本次未调用供应商。", recovery);
|
|
1488
|
-
}
|
|
1489
|
-
const input = options.inputJson ? readInputJsonObject(options.inputJson, "capability_input_invalid", recovery) : parseKeyValueList(options.input);
|
|
1490
|
-
await runServicesInvoke(backend, config, serviceExecutionID, options.capability, input, { jsonOutput: Boolean(options.json) });
|
|
1274
|
+
await runServicesInvoke(backend, config, serviceExecutionID, options.capability, parseKeyValueList(options.input), { jsonOutput: Boolean(options.json) });
|
|
1491
1275
|
}
|
|
1492
1276
|
catch (error) {
|
|
1493
1277
|
reportCLIError(error, {
|
|
@@ -1513,18 +1297,12 @@ services
|
|
|
1513
1297
|
.option("--result-item <service_capability_result_item_id>")
|
|
1514
1298
|
.option("--required-before <step>")
|
|
1515
1299
|
.option("--input <key=value>", "action input snapshot", collectOption, [])
|
|
1516
|
-
.option("--input-json <file>", "JSON object containing the full action input snapshot")
|
|
1517
1300
|
.option("--json", "output JSON instead of terminal text")
|
|
1518
1301
|
.action(async (serviceExecutionID, options) => {
|
|
1519
1302
|
const config = loadConfig();
|
|
1520
1303
|
const backend = newBackendClient(config);
|
|
1521
1304
|
try {
|
|
1522
|
-
|
|
1523
|
-
if (options.inputJson && options.input.length > 0) {
|
|
1524
|
-
throw new CommandContractError("service_action_invalid", "--input and --input-json cannot be combined", "--input 与 --input-json 只能二选一:嵌套结构用 --input-json <file>,扁平键值用 --input key=value。", recovery);
|
|
1525
|
-
}
|
|
1526
|
-
const fileInput = options.inputJson ? readInputJsonObject(options.inputJson, "service_action_invalid", recovery) : {};
|
|
1527
|
-
await runServicesAction(backend, serviceExecutionID, options.action, { ...fileInput, ...parseKeyValueList(options.input) }, {
|
|
1305
|
+
await runServicesAction(backend, serviceExecutionID, options.action, parseKeyValueList(options.input), {
|
|
1528
1306
|
...(options.actorType ? { actorType: options.actorType } : {}),
|
|
1529
1307
|
...(options.actorId ? { actorID: options.actorId } : {}),
|
|
1530
1308
|
...(options.status ? { status: options.status } : {}),
|
|
@@ -1669,17 +1447,12 @@ services
|
|
|
1669
1447
|
.command("next")
|
|
1670
1448
|
.description("Show the next recommended agent action for a Service Execution")
|
|
1671
1449
|
.argument("<service_execution_id>")
|
|
1672
|
-
.option("--since-snapshot <snapshot_id>", "delta read: suppress the journey payload when unchanged")
|
|
1673
|
-
.option("--timeout <seconds>", "wait up to 120 seconds for a useful result on this execution", (value) => Number(value))
|
|
1674
1450
|
.option("--json", "output JSON instead of terminal text")
|
|
1675
1451
|
.action(async (serviceExecutionID, options) => {
|
|
1676
1452
|
const config = loadConfig();
|
|
1677
1453
|
const backend = newBackendClient(config);
|
|
1678
1454
|
try {
|
|
1679
|
-
|
|
1680
|
-
throw new Error("--timeout must be an integer from 0 to 120");
|
|
1681
|
-
}
|
|
1682
|
-
await runServicesNext(backend, serviceExecutionID, { jsonOutput: Boolean(options.json), sinceSnapshot: options.sinceSnapshot, timeoutSeconds: options.timeout });
|
|
1455
|
+
await runServicesNext(backend, serviceExecutionID, { jsonOutput: Boolean(options.json) });
|
|
1683
1456
|
}
|
|
1684
1457
|
catch (error) {
|
|
1685
1458
|
reportCLIError(error, {
|
|
@@ -1692,28 +1465,20 @@ services
|
|
|
1692
1465
|
});
|
|
1693
1466
|
services
|
|
1694
1467
|
.command("read-result")
|
|
1695
|
-
.description("Read
|
|
1468
|
+
.description("Read a human-granted service result for this agent")
|
|
1696
1469
|
.argument("<service_execution_id>")
|
|
1697
|
-
.option("--snapshot <snapshot_id>", "Smart: saved snapshot catalog; add --journey for a journey detail. Exact: saved station-pair rows without --snapshot")
|
|
1698
|
-
.option("--journey <journey_id>", "rail.progressive.v2: journey id to read")
|
|
1699
|
-
.option("--offset <offset>", "saved journey offset", Number, 0)
|
|
1700
|
-
.option("--limit <limit>", "rows per page (1-20): Exact 20, Smart 3", Number)
|
|
1701
|
-
.option("--all", "read every saved journey page")
|
|
1702
1470
|
.option("--json", "output JSON instead of terminal text")
|
|
1703
1471
|
.action(async (serviceExecutionID, options) => {
|
|
1704
1472
|
const config = loadConfig();
|
|
1705
1473
|
const backend = newBackendClient(config);
|
|
1706
1474
|
try {
|
|
1707
|
-
await runServicesReadResult(backend, serviceExecutionID, { jsonOutput: Boolean(options.json)
|
|
1708
|
-
offset: options.offset, limit: options.limit, all: Boolean(options.all) });
|
|
1475
|
+
await runServicesReadResult(backend, serviceExecutionID, { jsonOutput: Boolean(options.json) });
|
|
1709
1476
|
}
|
|
1710
1477
|
catch (error) {
|
|
1711
1478
|
reportCLIError(error, {
|
|
1712
1479
|
jsonOutput: Boolean(options.json),
|
|
1713
|
-
code:
|
|
1714
|
-
instruction:
|
|
1715
|
-
? "请用户在订单页面重新授权;不要使用开发者权限绕过授权或退款锁。"
|
|
1716
|
-
: "读取同一服务的已保存结果;若快照已不可读,先查看该服务当前状态和有效动作。",
|
|
1480
|
+
code: "agent_access_denied",
|
|
1481
|
+
instruction: "请用户在订单页面重新授权;不要使用开发者权限绕过授权或退款锁。",
|
|
1717
1482
|
recovery: [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "检查交付模式和 grant 状态" }],
|
|
1718
1483
|
});
|
|
1719
1484
|
}
|
|
@@ -1747,7 +1512,6 @@ services
|
|
|
1747
1512
|
});
|
|
1748
1513
|
}
|
|
1749
1514
|
});
|
|
1750
|
-
registerSell(program);
|
|
1751
1515
|
program.parseAsync(process.argv).catch((error) => {
|
|
1752
1516
|
reportCLIError(error);
|
|
1753
1517
|
});
|
|
@@ -24,6 +24,9 @@ export function buildAgentChatHandoff(plan) {
|
|
|
24
24
|
if (plan.orderItems?.length) {
|
|
25
25
|
lines.push(`项目:${plan.orderItems.map((item) => `${item.title} x${item.quantity}`).join("、")}`);
|
|
26
26
|
}
|
|
27
|
+
if (plan.afterActionCommand) {
|
|
28
|
+
lines.push("", `付款后查询:\`${plan.afterActionCommand}\``);
|
|
29
|
+
}
|
|
27
30
|
return {
|
|
28
31
|
type: "send_payment_handoff",
|
|
29
32
|
must_send_to_user: true,
|
package/dist/src/render/qr.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function renderTerminalQR(url, format) {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
export async function writeLocalPNG(url) {
|
|
23
|
-
const dir = await fs.mkdtemp(path.join(
|
|
23
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "itpay-v3-"));
|
|
24
24
|
const filePath = path.join(dir, `itpay-${randomUUID()}.png`);
|
|
25
25
|
await QRCode.toFile(filePath, url, {
|
|
26
26
|
type: "png",
|
|
@@ -55,12 +55,9 @@ export class CartSession {
|
|
|
55
55
|
...(this.state.lastDisplayToken ? { lastDisplayToken: this.state.lastDisplayToken } : {}),
|
|
56
56
|
...(this.state.lastCheckoutURL ? { lastCheckoutURL: this.state.lastCheckoutURL } : {}),
|
|
57
57
|
};
|
|
58
|
-
const contents = JSON.stringify(toSave, null, 2);
|
|
59
|
-
if (existsSync(path) && readFileSync(path, "utf-8") === contents)
|
|
60
|
-
return;
|
|
61
58
|
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
62
59
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
63
|
-
writeFileSync(temporary,
|
|
60
|
+
writeFileSync(temporary, JSON.stringify(toSave, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
64
61
|
chmodSync(temporary, 0o600);
|
|
65
62
|
renameSync(temporary, path);
|
|
66
63
|
chmodSync(path, 0o600);
|