@itpay/cli 2.0.15 → 2.0.16
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/dist/src/commands/buy.js +3 -0
- package/dist/src/commands/checkout.js +4 -2
- package/dist/src/commands/checkout_handoff.js +7 -0
- package/dist/src/commands/compatibility.js +0 -1
- package/dist/src/commands/install.js +13 -8
- package/dist/src/commands/services.js +14 -6
- package/dist/src/main.js +43 -16
- package/dist/src/render/telegram.js +51 -25
- package/dist/src/state/client_context.js +2 -0
- package/dist/src/state/config.js +8 -1
- package/docs/agent/buyer/install-and-setup.json +9 -7
- package/docs/agent/buyer/payment-flow.json +1 -0
- package/docs/agent/buyer/quickstart.json +1 -0
- package/docs/agent/buyer/render-hosts.json +18 -2
- package/docs/cli-reference/agent-types.md +12 -2
- package/docs/cli-reference/commands/buy.md +9 -4
- 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/checkout.md +5 -3
- 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 +44 -10
- package/docs/cli-reference/commands/next.md +1 -1
- package/docs/cli-reference/commands/readyz.md +26 -2
- 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/index.md +1 -1
- package/docs/cli-reference/commands/refund/list.md +1 -1
- package/docs/cli-reference/commands/services/action.md +1 -1
- package/docs/cli-reference/commands/services/checkout.md +5 -1
- package/docs/cli-reference/commands/services/events.md +1 -1
- package/docs/cli-reference/commands/services/get.md +1 -1
- package/docs/cli-reference/commands/services/index.md +1 -1
- package/docs/cli-reference/commands/services/invoke.md +1 -1
- package/docs/cli-reference/commands/services/list.md +1 -1
- package/docs/cli-reference/commands/services/read-result.md +1 -1
- package/docs/cli-reference/conventions.md +7 -1
- package/docs/skill-bundle-rollout/02-platform-bundle-repositories.md +20 -23
- package/docs/skill-bundle-rollout/03-platform-publishing.md +2 -1
- package/docs/skill-bundle-rollout/04-first-wave-platforms.md +32 -0
- package/docs/skill-bundle-rollout/README.md +3 -2
- package/package.json +1 -1
- package/skills/itpay/SKILL.md +1 -1
package/dist/src/commands/buy.js
CHANGED
|
@@ -141,6 +141,7 @@ export async function runBuy(backend, config, options) {
|
|
|
141
141
|
...(qrPNGURL ? { qrPNGURL } : {}),
|
|
142
142
|
...(paymentIntent ? { paymentIntent } : {}),
|
|
143
143
|
...(options.agentType ? { agentType: options.agentType } : {}),
|
|
144
|
+
...(options.target ? { target: options.target } : {}),
|
|
144
145
|
});
|
|
145
146
|
writeCommandEnvelope(envelope, { jsonOutput: true, ...(options.output ? { output: options.output } : {}) });
|
|
146
147
|
return {
|
|
@@ -198,7 +199,9 @@ function buildBuyEnvelope(input) {
|
|
|
198
199
|
platform,
|
|
199
200
|
url: input.checkoutURL,
|
|
200
201
|
amount,
|
|
202
|
+
plan: input.plan,
|
|
201
203
|
...(input.agentType ? { agentType: input.agentType } : {}),
|
|
204
|
+
...(input.target ? { target: input.target } : {}),
|
|
202
205
|
...(input.qrPNGURL ? { qrImageURL: input.qrPNGURL } : {}),
|
|
203
206
|
...(input.plan.ideImageAttach?.status === "downloaded" && input.plan.ideImageAttach.localPath
|
|
204
207
|
? { localPath: input.plan.ideImageAttach.localPath }
|
|
@@ -46,7 +46,7 @@ export async function runCheckoutPresentation(backend, options) {
|
|
|
46
46
|
...(options.baseURL ? { baseURL: options.baseURL } : {}),
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
const envelope = pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand, options.agentType);
|
|
49
|
+
const envelope = pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand, options.agentType, options.target);
|
|
50
50
|
const plainResult = checkoutPlainResult(envelope.result);
|
|
51
51
|
if (!options.jsonOutput && platformKeyForHost(host) === "terminal") {
|
|
52
52
|
plainResult.push("qr:", await renderTerminalQR(checkoutURL, "terminal"));
|
|
@@ -57,14 +57,16 @@ export async function runCheckoutPresentation(backend, options) {
|
|
|
57
57
|
plainResult,
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
-
function pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand, agentType) {
|
|
60
|
+
function pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand, agentType, target) {
|
|
61
61
|
const platform = platformKeyForHost(plan.host);
|
|
62
62
|
const amount = formatMoney(presentation.checkout.amount_minor, presentation.checkout.currency);
|
|
63
63
|
const presentationHandoff = buildCheckoutHandoff({
|
|
64
64
|
platform,
|
|
65
65
|
url: checkoutURL,
|
|
66
66
|
amount,
|
|
67
|
+
plan,
|
|
67
68
|
...(agentType ? { agentType } : {}),
|
|
69
|
+
...(target ? { target } : {}),
|
|
68
70
|
...(plan.preferredQRSources[0] ? { qrImageURL: plan.preferredQRSources[0] } : {}),
|
|
69
71
|
...(plan.ideImageAttach?.status === "downloaded" && plan.ideImageAttach.localPath
|
|
70
72
|
? { localPath: plan.ideImageAttach.localPath }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildOpenClawTelegramAction } from "../render/telegram.js";
|
|
1
2
|
export function shouldPrepareLocalCheckoutImage(platform) {
|
|
2
3
|
return platform === "markdown";
|
|
3
4
|
}
|
|
@@ -15,6 +16,12 @@ export function buildCheckoutHandoff(input) {
|
|
|
15
16
|
else if (input.platform === "plain_chat" && input.qrImageURL) {
|
|
16
17
|
handoff.qr_image_url = input.qrImageURL;
|
|
17
18
|
}
|
|
19
|
+
else if (input.platform === "telegram" && input.qrImageURL) {
|
|
20
|
+
handoff.qr_image_url = input.qrImageURL;
|
|
21
|
+
}
|
|
22
|
+
if (input.agentType?.trim().toLowerCase() === "openclaw" && input.platform === "telegram" && input.plan && input.target) {
|
|
23
|
+
handoff.agent_action = buildOpenClawTelegramAction(input.plan, input.target);
|
|
24
|
+
}
|
|
18
25
|
return {
|
|
19
26
|
handoff,
|
|
20
27
|
instruction: checkoutHandoffInstruction(input.agentType, input.platform, input.amount, Boolean(input.qrImageURL)),
|
|
@@ -14,7 +14,6 @@ export async function requirePlatformCompatibility(backend) {
|
|
|
14
14
|
maximum_cli_major: platform.maximum_cli_major,
|
|
15
15
|
platform_revision: platform.platform_revision,
|
|
16
16
|
api_contract_revision: platform.api_contract_revision,
|
|
17
|
-
upgrade_command: `npm install -g @itpay/cli@${platform.minimum_cli_version}`,
|
|
18
17
|
}, "CLI is incompatible with the active ItPay platform release");
|
|
19
18
|
}
|
|
20
19
|
function compareVersions(left, right) {
|
|
@@ -7,6 +7,8 @@ export const INSTALL_AGENT_TYPES = [
|
|
|
7
7
|
"claude-code-desktop",
|
|
8
8
|
"claude-code-cli",
|
|
9
9
|
"workbuddy",
|
|
10
|
+
"kimi-code",
|
|
11
|
+
"openclaw",
|
|
10
12
|
];
|
|
11
13
|
const INSTRUCTIONS = {
|
|
12
14
|
"codex-desktop": "在 Codex Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
@@ -14,13 +16,12 @@ const INSTRUCTIONS = {
|
|
|
14
16
|
"claude-code-desktop": "在 Claude Code Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
15
17
|
"claude-code-cli": "在 Claude Code CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
|
|
16
18
|
workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 返回 qr_image_url 时,按当次 instruction 调用 present_files 在右侧打开二维码,不要检查本地二维码文件。",
|
|
19
|
+
"kimi-code": "保持 kimi-code Agent Type,使用标准 CLI 命令和终端付款交接;不要切换到其他平台身份。",
|
|
20
|
+
openclaw: "保持 openclaw Agent Type;每个展示命令都从当前可信会话上下文显式传 --host,IM 入口同时传 --target。Telegram 使用返回的原生 message action,其他入口展示标准二维码和付款链接。",
|
|
17
21
|
};
|
|
18
22
|
export function runInstall(target, options = {}) {
|
|
19
23
|
if (!target || target === "list") {
|
|
20
|
-
const agentTypes = INSTALL_AGENT_TYPES.map(
|
|
21
|
-
agent_type: agentType,
|
|
22
|
-
default_host: defaultHostForAgentType(agentType),
|
|
23
|
-
}));
|
|
24
|
+
const agentTypes = INSTALL_AGENT_TYPES.map(installDefinition);
|
|
24
25
|
writeCommandEnvelope({
|
|
25
26
|
status: "install_targets",
|
|
26
27
|
result: { agent_types: agentTypes },
|
|
@@ -29,7 +30,7 @@ export function runInstall(target, options = {}) {
|
|
|
29
30
|
recovery: [{ command: "itpay docs show install-and-setup", reason: "查看安装与环境说明" }],
|
|
30
31
|
}, {
|
|
31
32
|
...options,
|
|
32
|
-
plainResult: agentTypes.map((item) => `${item.agent_type}: ${item.default_host}`),
|
|
33
|
+
plainResult: agentTypes.map((item) => `${item.agent_type}: ${item.default_host ?? "explicit --host required"}`),
|
|
33
34
|
});
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
@@ -40,10 +41,8 @@ export function runInstall(target, options = {}) {
|
|
|
40
41
|
writeCommandEnvelope({
|
|
41
42
|
status: "instructions_ready",
|
|
42
43
|
result: {
|
|
43
|
-
|
|
44
|
-
default_host: defaultHostForAgentType(normalized),
|
|
44
|
+
...installDefinition(normalized),
|
|
45
45
|
default_api: DEFAULT_BASE_URL,
|
|
46
|
-
install_command: "npm install -g @itpay/cli",
|
|
47
46
|
},
|
|
48
47
|
instruction: INSTRUCTIONS[normalized],
|
|
49
48
|
next: {
|
|
@@ -56,3 +55,9 @@ export function runInstall(target, options = {}) {
|
|
|
56
55
|
function isInstallAgentType(value) {
|
|
57
56
|
return INSTALL_AGENT_TYPES.includes(value);
|
|
58
57
|
}
|
|
58
|
+
function installDefinition(agentType) {
|
|
59
|
+
if (agentType === "openclaw") {
|
|
60
|
+
return { agent_type: agentType, default_host: null, host_required: true, native_hosts: ["telegram"] };
|
|
61
|
+
}
|
|
62
|
+
return { agent_type: agentType, default_host: defaultHostForAgentType(agentType) ?? null };
|
|
63
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { operationID } from "../state/config.js";
|
|
2
|
+
import { validateContext } from "../state/client_context.js";
|
|
2
3
|
import { dispatchRender } from "../render/index.js";
|
|
3
4
|
import { ensureIdeImageAttach } from "../render/ide.js";
|
|
4
5
|
import { buildCheckoutHandoff, shouldPrepareLocalCheckoutImage } from "./checkout_handoff.js";
|
|
@@ -373,6 +374,11 @@ function actionInputError(serviceExecutionID, message, code = "service_action_in
|
|
|
373
374
|
: "使用当前 safe result 中的合法 action 和 candidate rank;需要人确认时先询问用户。", [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "重新读取同一 Execution 的当前可选动作" }]);
|
|
374
375
|
}
|
|
375
376
|
export async function runServicesCheckout(backend, config, serviceExecutionID, capabilityID, options = {}) {
|
|
377
|
+
const host = options.host ?? "terminal";
|
|
378
|
+
const contextError = validateContext(host, options.target);
|
|
379
|
+
if (contextError) {
|
|
380
|
+
throw new CommandContractError(contextError.code, contextError.message, "从当前可信会话上下文补齐 Host/target;本次未创建 Checkout。", []);
|
|
381
|
+
}
|
|
376
382
|
const deliveryContact = {
|
|
377
383
|
...(options.deliveryContact ?? {}),
|
|
378
384
|
...(options.email ? { email: options.email } : {}),
|
|
@@ -409,12 +415,12 @@ export async function runServicesCheckout(backend, config, serviceExecutionID, c
|
|
|
409
415
|
const displayToken = checkout.display_token;
|
|
410
416
|
const checkoutURL = tokenizedCheckoutURL(checkout.checkout_url, displayToken, checkout.qr_payload);
|
|
411
417
|
const plan = buildCheckoutQRPlan({
|
|
412
|
-
host
|
|
418
|
+
host,
|
|
413
419
|
checkoutID,
|
|
414
420
|
checkoutURL,
|
|
415
421
|
displayToken,
|
|
416
422
|
qrPayload: checkout.qr_payload,
|
|
417
|
-
...(checkout.qr_png_url ? { qrPNGURL: checkout.qr_png_url } : {}),
|
|
423
|
+
...(checkout.qr_png_url ? { qrPNGURL: absolutePublicURL(config.baseURL, checkout.qr_png_url) } : {}),
|
|
418
424
|
nextAction: checkout.checkout.next_action,
|
|
419
425
|
orderItems: response.cart.items.map((item) => ({
|
|
420
426
|
title: item.title,
|
|
@@ -433,9 +439,9 @@ export async function runServicesCheckout(backend, config, serviceExecutionID, c
|
|
|
433
439
|
checkoutURL,
|
|
434
440
|
});
|
|
435
441
|
const platform = platformKeyForHost(plan.host);
|
|
436
|
-
if (platform === "telegram" || platform === "feishu" || platform === "lark") {
|
|
442
|
+
if (!options.jsonOutput && (platform === "telegram" || platform === "feishu" || platform === "lark")) {
|
|
437
443
|
await dispatchRender(plan, {
|
|
438
|
-
host
|
|
444
|
+
host,
|
|
439
445
|
...(options.target ? { target: options.target } : {}),
|
|
440
446
|
...(options.qrFormat ? { qrFormat: options.qrFormat } : {}),
|
|
441
447
|
...(options.qrFilePath ? { qrFilePath: options.qrFilePath } : {}),
|
|
@@ -452,7 +458,7 @@ export async function runServicesCheckout(backend, config, serviceExecutionID, c
|
|
|
452
458
|
...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
|
|
453
459
|
});
|
|
454
460
|
}
|
|
455
|
-
const envelope = buildServicesCheckoutEnvelope(response, checkoutURL, plan, config.baseURL, options.agentType);
|
|
461
|
+
const envelope = buildServicesCheckoutEnvelope(response, checkoutURL, plan, config.baseURL, options.agentType, options.target);
|
|
456
462
|
const plainResult = [
|
|
457
463
|
`service_execution_id: ${response.binding.service_execution_id}`,
|
|
458
464
|
`checkout_id: ${checkoutID}`,
|
|
@@ -933,7 +939,7 @@ function parseValue(value) {
|
|
|
933
939
|
return Number(value);
|
|
934
940
|
return value;
|
|
935
941
|
}
|
|
936
|
-
function buildServicesCheckoutEnvelope(response, checkoutURL, plan, baseURL, agentType) {
|
|
942
|
+
function buildServicesCheckoutEnvelope(response, checkoutURL, plan, baseURL, agentType, target) {
|
|
937
943
|
const checkout = response.checkout;
|
|
938
944
|
const platform = platformKeyForHost(plan.host);
|
|
939
945
|
const amount = formatMoney(checkout.checkout.amount_minor, checkout.checkout.currency);
|
|
@@ -941,7 +947,9 @@ function buildServicesCheckoutEnvelope(response, checkoutURL, plan, baseURL, age
|
|
|
941
947
|
platform,
|
|
942
948
|
url: checkoutURL,
|
|
943
949
|
amount,
|
|
950
|
+
plan,
|
|
944
951
|
...(agentType ? { agentType } : {}),
|
|
952
|
+
...(target ? { target } : {}),
|
|
945
953
|
...(checkout.qr_png_url ? { qrImageURL: absolutePublicURL(baseURL, checkout.qr_png_url) } : {}),
|
|
946
954
|
...(plan.ideImageAttach?.status === "downloaded" && plan.ideImageAttach.localPath
|
|
947
955
|
? { localPath: plan.ideImageAttach.localPath }
|
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 { BackendOverrideError, CLI_VERSION, loadConfig, cartSessionPath, newBackendClient } from "./state/config.js";
|
|
5
|
+
import { BackendOverrideError, CLI_VERSION, cliDistribution, 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";
|
|
@@ -29,10 +29,17 @@ program
|
|
|
29
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
|
-
function withHost(value) {
|
|
33
|
-
|
|
32
|
+
function withHost(value, agentType, target) {
|
|
33
|
+
if (!value && agentType?.trim().toLowerCase() === "openclaw") {
|
|
34
|
+
throw new CommandContractError("host_required", "OpenClaw requires an explicit --host entry", "从当前可信 OpenClaw 会话上下文传入 --host;IM 入口同时传 --target。本次未创建或修改资源。", []);
|
|
35
|
+
}
|
|
36
|
+
const host = normalizeHost(value ?? defaultHostForAgentType(agentType));
|
|
34
37
|
if (!host) {
|
|
35
|
-
throw new
|
|
38
|
+
throw new CommandContractError("invalid_host", `invalid --host "${value ?? ""}". Supported: terminal, codex, claude-code, telegram, discord, whatsapp, feishu, lark, plain-chat`, "传入受支持的展示 Host;本次未创建或修改资源。", []);
|
|
39
|
+
}
|
|
40
|
+
const contextError = agentType?.trim().toLowerCase() === "openclaw" ? validateContext(host, target) : undefined;
|
|
41
|
+
if (contextError) {
|
|
42
|
+
throw new CommandContractError(contextError.code, contextError.message, "从当前可信会话上下文补齐 --target;本次未创建或修改资源。", []);
|
|
36
43
|
}
|
|
37
44
|
return host;
|
|
38
45
|
}
|
|
@@ -149,7 +156,7 @@ function reportCLIError(error, contract) {
|
|
|
149
156
|
} : {}),
|
|
150
157
|
instruction: incompatible
|
|
151
158
|
? requiredCLIVersion
|
|
152
|
-
?
|
|
159
|
+
? distributionUpgradeInstruction()
|
|
153
160
|
: "立即向用户报告 error.message 并结束本次任务。Backend 未提供可验证的兼容 CLI 版本;不要运行其他 ItPay 或 npm 命令,不要猜测版本、切换 Agent Type 或删除 Device 身份。"
|
|
154
161
|
: backendInternal
|
|
155
162
|
? "Backend 内部故障;立即停止并向用户报告。不要重试、检查或删除 Device 身份、创建替代 Execution、切换 Backend,或尝试 quote、checkout、cart、buy、pay 等付费路径。"
|
|
@@ -171,7 +178,7 @@ function reportCLIError(error, contract) {
|
|
|
171
178
|
next: null,
|
|
172
179
|
recovery: incompatible
|
|
173
180
|
? requiredCLIVersion
|
|
174
|
-
? [
|
|
181
|
+
? [distributionUpgradeAction(requiredCLIVersion)]
|
|
175
182
|
: []
|
|
176
183
|
: backendInternal || providerConnectionUnavailable || providerTemporary || providerInputRejected || providerContractMismatch || providerRejected || capabilityInputInvalid
|
|
177
184
|
? []
|
|
@@ -200,10 +207,29 @@ function docsErrorFallback(jsonOutput) {
|
|
|
200
207
|
return {
|
|
201
208
|
jsonOutput,
|
|
202
209
|
code: "docs_unavailable",
|
|
203
|
-
instruction: "
|
|
204
|
-
recovery: [
|
|
210
|
+
instruction: "内置文档缺失或损坏;通过当前分发方式恢复同版本 CLI 后重试。",
|
|
211
|
+
recovery: [distributionUpgradeAction(CLI_VERSION)],
|
|
205
212
|
};
|
|
206
213
|
}
|
|
214
|
+
function distributionUpgradeAction(version) {
|
|
215
|
+
switch (cliDistribution()) {
|
|
216
|
+
case "openclaw-skill-bundle":
|
|
217
|
+
return { command: "openclaw skills update itpay", reason: `更新包含 ItPay CLI ${version} 的 Skill bundle` };
|
|
218
|
+
case "kimi-plugin-bundle":
|
|
219
|
+
return {
|
|
220
|
+
command: "/plugins install https://github.com/itpay-ai/itpay-plugin-kimi-work",
|
|
221
|
+
reason: `更新包含 ItPay CLI ${version} 的 Kimi plugin`,
|
|
222
|
+
};
|
|
223
|
+
default:
|
|
224
|
+
return { command: `npm install -g @itpay/cli@${version}`, reason: "安装 Backend 指定的兼容 CLI 版本" };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function distributionUpgradeInstruction() {
|
|
228
|
+
if (cliDistribution() === "npm") {
|
|
229
|
+
return "当前 CLI 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,将 @itpay/cli 更新到 Backend 指定的精确版本。安装完成后确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要安装 latest、猜测版本、切换 Agent Type 或删除 Device 身份。";
|
|
230
|
+
}
|
|
231
|
+
return "当前平台 bundle 与 Backend 合约不兼容。停止所有 ItPay 业务命令;只执行 recovery.command,更新平台 Skill/plugin 后启动新会话,确认 itpay --version 与 result.required_cli_version 完全一致,再重新运行 readyz。不要运行 npm、猜测版本、切换 Agent Type 或删除 Device 身份。";
|
|
232
|
+
}
|
|
207
233
|
program
|
|
208
234
|
.command("readyz")
|
|
209
235
|
.description("Probe the V3 backend readiness endpoint")
|
|
@@ -298,8 +324,8 @@ skillCmd
|
|
|
298
324
|
reportCLIError(error, {
|
|
299
325
|
jsonOutput: Boolean(options.json),
|
|
300
326
|
code: "skill_unavailable",
|
|
301
|
-
instruction: "内置 Skill
|
|
302
|
-
recovery: [
|
|
327
|
+
instruction: "内置 Skill 缺失或损坏;通过当前分发方式恢复同版本 CLI 后重试。",
|
|
328
|
+
recovery: [distributionUpgradeAction(CLI_VERSION)],
|
|
303
329
|
});
|
|
304
330
|
}
|
|
305
331
|
});
|
|
@@ -456,7 +482,7 @@ cart
|
|
|
456
482
|
runCartAdd(session, { ...addOptions, jsonOutput });
|
|
457
483
|
}
|
|
458
484
|
else {
|
|
459
|
-
const host = withHost(options.host
|
|
485
|
+
const host = withHost(options.host, config.agentType, options.target);
|
|
460
486
|
const contextError = validateContext(host, options.target);
|
|
461
487
|
if (contextError) {
|
|
462
488
|
throw new CommandContractError(contextError.code, contextError.message, "补齐当前客户端所需的 Host/target;本次未创建或修改 Cart。", [{ command: "itpay cart add --help", reason: "查看客户端参数" }]);
|
|
@@ -692,7 +718,7 @@ program
|
|
|
692
718
|
}
|
|
693
719
|
const quantity = positiveInteger(options.quantity, "--quantity");
|
|
694
720
|
const timeout = positiveInteger(options.timeout, "--timeout");
|
|
695
|
-
const host = withHost(options.host
|
|
721
|
+
const host = withHost(options.host, config.agentType, options.target);
|
|
696
722
|
const contact = {};
|
|
697
723
|
if (options.contactEmail)
|
|
698
724
|
contact.email = options.contactEmail;
|
|
@@ -756,11 +782,11 @@ program
|
|
|
756
782
|
.option("--json", "output compact JSON")
|
|
757
783
|
.action(async (options) => {
|
|
758
784
|
const config = loadConfig();
|
|
759
|
-
const host = withHost(options.host ?? defaultHostForAgentType(config.agentType));
|
|
760
785
|
const session = CartSession.loadFromFile(cartSessionPath(), config.checkoutCurrency);
|
|
761
786
|
const snap = session.show();
|
|
762
787
|
const backend = newBackendClient(config);
|
|
763
788
|
try {
|
|
789
|
+
const host = withHost(options.host, config.agentType, options.target);
|
|
764
790
|
const { checkoutID, displayToken } = resolveCheckoutPresentationArgs({
|
|
765
791
|
...(options.id ? { requestedCheckoutID: options.id } : {}),
|
|
766
792
|
...(options.token ? { requestedDisplayToken: options.token } : {}),
|
|
@@ -771,6 +797,7 @@ program
|
|
|
771
797
|
checkoutID,
|
|
772
798
|
displayToken,
|
|
773
799
|
host,
|
|
800
|
+
...(options.target ? { target: options.target } : {}),
|
|
774
801
|
...(config.agentType ? { agentType: config.agentType } : {}),
|
|
775
802
|
baseURL: config.baseURL,
|
|
776
803
|
jsonOutput: Boolean(options.json),
|
|
@@ -817,7 +844,7 @@ program
|
|
|
817
844
|
if (!displayToken) {
|
|
818
845
|
throw new CommandContractError("checkout_token_required", "display token is required for this checkout", "提供同一 Checkout 的 display token;不要拼接其他 Checkout 的 token。", [{ command: "itpay next --json", reason: "恢复本机保存的同一 Checkout" }]);
|
|
819
846
|
}
|
|
820
|
-
const host = withHost(options.host
|
|
847
|
+
const host = withHost(options.host, config.agentType, options.target);
|
|
821
848
|
const contextError = validateContext(host, options.target);
|
|
822
849
|
if (contextError) {
|
|
823
850
|
throw new CommandContractError(contextError.code, contextError.message, "为当前 Host 提供有效 target;本次未创建 Payment Intent。", [
|
|
@@ -1024,7 +1051,7 @@ services
|
|
|
1024
1051
|
const backend = newBackendClient(config);
|
|
1025
1052
|
try {
|
|
1026
1053
|
await runServicesStart(backend, serviceID, {
|
|
1027
|
-
host: withHost(options.host
|
|
1054
|
+
host: withHost(options.host, config.agentType, options.target),
|
|
1028
1055
|
...(options.target ? { target: options.target } : {}),
|
|
1029
1056
|
jsonOutput: Boolean(options.json),
|
|
1030
1057
|
});
|
|
@@ -1150,7 +1177,7 @@ services
|
|
|
1150
1177
|
...(options.email ? { email: options.email } : {}),
|
|
1151
1178
|
lockedInput: parseKeyValueList(options.input),
|
|
1152
1179
|
resume: Boolean(options.resume),
|
|
1153
|
-
host: withHost(options.host
|
|
1180
|
+
host: withHost(options.host, config.agentType, options.target),
|
|
1154
1181
|
...(config.agentType ? { agentType: config.agentType } : {}),
|
|
1155
1182
|
...(options.target ? { target: options.target } : {}),
|
|
1156
1183
|
...(options.qrFormat ? { qrFormat: options.qrFormat } : {}),
|
|
@@ -28,14 +28,49 @@ function buttonsFor(plan) {
|
|
|
28
28
|
: []),
|
|
29
29
|
];
|
|
30
30
|
}
|
|
31
|
+
function nativeButton(button) {
|
|
32
|
+
if (button.kind === "url") {
|
|
33
|
+
return { label: button.label, action: { type: "url", url: button.url ?? "" } };
|
|
34
|
+
}
|
|
35
|
+
const value = button.intent === "check_checkout_status"
|
|
36
|
+
? `itp:checkout:${button.ref ?? ""}`
|
|
37
|
+
: `itp:${button.intent ?? "callback"}:${button.ref ?? ""}`;
|
|
38
|
+
return { label: button.label, action: { type: "callback", value } };
|
|
39
|
+
}
|
|
31
40
|
function format(plan) {
|
|
32
41
|
return plan.kind === "payment_qr" ? "photo_text_inline_buttons" : "text_inline_buttons";
|
|
33
42
|
}
|
|
34
43
|
export function renderTelegram(plan, options) {
|
|
35
44
|
const out = options.output ?? ((line) => process.stdout.write(line));
|
|
36
|
-
const
|
|
45
|
+
const agentAction = buildOpenClawTelegramAction(plan, options.target);
|
|
46
|
+
const presentation = agentAction.arguments.presentation;
|
|
47
|
+
const media = collectTelegramMedia(plan);
|
|
48
|
+
const text = agentAction.arguments.message;
|
|
49
|
+
const openclawMessage = {
|
|
50
|
+
command: [
|
|
51
|
+
"openclaw",
|
|
52
|
+
"message",
|
|
53
|
+
"send",
|
|
54
|
+
"--channel",
|
|
55
|
+
"telegram",
|
|
56
|
+
"--target",
|
|
57
|
+
options.target,
|
|
58
|
+
"--message",
|
|
59
|
+
text,
|
|
60
|
+
...(media.length > 0 ? ["--media", media[0].url] : []),
|
|
61
|
+
"--presentation",
|
|
62
|
+
JSON.stringify(presentation),
|
|
63
|
+
],
|
|
64
|
+
// Hint: agents that cannot run openclaw must stop and tell the user
|
|
65
|
+
// they lack the native Telegram buttons tool — never downgrade.
|
|
66
|
+
if_unavailable: "Current agent cannot run `openclaw message send`. Stop and tell the user the native Telegram inline-button tool is missing; do not downgrade to a markdown table or plain link.",
|
|
67
|
+
};
|
|
68
|
+
out(JSON.stringify({ presentation, agent_action: agentAction, openclaw_message: openclawMessage }, null, 2) + "\n");
|
|
69
|
+
}
|
|
70
|
+
export function buildOpenClawTelegramAction(plan, target) {
|
|
71
|
+
const buttons = buttonsFor(plan).map(nativeButton);
|
|
37
72
|
const media = collectTelegramMedia(plan);
|
|
38
|
-
const
|
|
73
|
+
const message = plan.kind === "payment_qr"
|
|
39
74
|
? `ItPay payment QR — ${plan.summary}`
|
|
40
75
|
: plan.kind === "auth_qr"
|
|
41
76
|
? `ItPay auth required — ${plan.summary}`
|
|
@@ -43,12 +78,12 @@ export function renderTelegram(plan, options) {
|
|
|
43
78
|
const presentation = {
|
|
44
79
|
format: format(plan),
|
|
45
80
|
media,
|
|
46
|
-
text,
|
|
81
|
+
text: message,
|
|
47
82
|
links: plan.platform.links,
|
|
48
83
|
buttons,
|
|
49
84
|
interactions: plan.platform.interactions ?? [],
|
|
50
85
|
blocks: [
|
|
51
|
-
{ type: "text", text },
|
|
86
|
+
{ type: "text", text: message },
|
|
52
87
|
...(media.length > 0 ? [{ type: "image", url: media[0].url }] : []),
|
|
53
88
|
{ type: "buttons", buttons },
|
|
54
89
|
],
|
|
@@ -64,31 +99,22 @@ export function renderTelegram(plan, options) {
|
|
|
64
99
|
must_render_reason: plan.ideImageAttach.mustRenderReason,
|
|
65
100
|
...(plan.ideImageAttach.error ? { error: plan.ideImageAttach.error } : {}),
|
|
66
101
|
action: "agent_must_render_into_ide_chat",
|
|
67
|
-
instructions: ideImageAttachBlock(plan.ideImageAttach).filter((
|
|
102
|
+
instructions: ideImageAttachBlock(plan.ideImageAttach).filter((line) => line.length > 0),
|
|
68
103
|
},
|
|
69
104
|
}
|
|
70
105
|
: {}),
|
|
71
106
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
text,
|
|
83
|
-
...(media.length > 0 ? ["--media", media[0].url] : []),
|
|
84
|
-
"--presentation",
|
|
85
|
-
JSON.stringify(presentation),
|
|
86
|
-
],
|
|
87
|
-
// Hint: agents that cannot run openclaw must stop and tell the user
|
|
88
|
-
// they lack the native Telegram buttons tool — never downgrade.
|
|
89
|
-
if_unavailable: "Current agent cannot run `openclaw message send`. Stop and tell the user the native Telegram inline-button tool is missing; do not downgrade to a markdown table or plain link.",
|
|
107
|
+
return {
|
|
108
|
+
tool: "message",
|
|
109
|
+
arguments: {
|
|
110
|
+
action: "send",
|
|
111
|
+
channel: "telegram",
|
|
112
|
+
target,
|
|
113
|
+
message,
|
|
114
|
+
...(media[0]?.url ? { media: media[0].url } : {}),
|
|
115
|
+
presentation,
|
|
116
|
+
},
|
|
90
117
|
};
|
|
91
|
-
out(JSON.stringify({ presentation, openclaw_message: openclawMessage }, null, 2) + "\n");
|
|
92
118
|
}
|
|
93
119
|
export function renderTelegramInteraction(request, options) {
|
|
94
120
|
const out = options.output ?? ((line) => process.stdout.write(line));
|
|
@@ -97,7 +123,7 @@ export function renderTelegramInteraction(request, options) {
|
|
|
97
123
|
mimeType: item.mimeType ?? "image/png",
|
|
98
124
|
}));
|
|
99
125
|
const buttons = request.kind === "selector"
|
|
100
|
-
? request.options.map((option) => selectorButton(request.id, option))
|
|
126
|
+
? request.options.map((option) => nativeButton(selectorButton(request.id, option)))
|
|
101
127
|
: [];
|
|
102
128
|
const text = `${request.title} — ${request.prompt}`;
|
|
103
129
|
const presentation = {
|
|
@@ -62,6 +62,8 @@ export function defaultHostForAgentType(agentType) {
|
|
|
62
62
|
return "claude-code";
|
|
63
63
|
if (normalized === "workbuddy")
|
|
64
64
|
return "plain-chat";
|
|
65
|
+
if (normalized === "openclaw")
|
|
66
|
+
return undefined;
|
|
65
67
|
return "terminal";
|
|
66
68
|
}
|
|
67
69
|
export function validateContext(host, target) {
|
package/dist/src/state/config.js
CHANGED
|
@@ -12,11 +12,18 @@ 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
14
|
export const DEV_BASE_URL = "https://dev.itpay.ai";
|
|
15
|
-
export const CLI_VERSION = "2.0.
|
|
15
|
+
export const CLI_VERSION = "2.0.16";
|
|
16
16
|
export const API_CONTRACT_REVISION = "sha256:7f4c40b082292bf823631bcd37d452f4a8537153e30636d5eb3a2b24a77ce602";
|
|
17
17
|
const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
|
|
18
18
|
const CART_SESSION_FILENAME = "cart.json";
|
|
19
19
|
const OPERATION_JOURNAL_FILENAME = "operations.json";
|
|
20
|
+
export function cliDistribution(env = process.env) {
|
|
21
|
+
if (env.ITPAY_DISTRIBUTION === "openclaw-skill-bundle")
|
|
22
|
+
return "openclaw-skill-bundle";
|
|
23
|
+
if (env.ITPAY_DISTRIBUTION === "kimi-plugin-bundle")
|
|
24
|
+
return "kimi-plugin-bundle";
|
|
25
|
+
return "npm";
|
|
26
|
+
}
|
|
20
27
|
export class BackendOverrideError extends Error {
|
|
21
28
|
code = "backend_override_forbidden";
|
|
22
29
|
constructor() {
|
|
@@ -4,7 +4,7 @@
|
|
|
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": "install-and-setup",
|
|
6
6
|
"title": "Install And Identify The ItPay Agent Runtime",
|
|
7
|
-
"purpose": "
|
|
7
|
+
"purpose": "Load the complete ItPay Skill and select one stable Agent Type without confusing distribution, identity, Backend, Host, target, or chat window.",
|
|
8
8
|
"when_to_use": [
|
|
9
9
|
"The CLI is being installed or upgraded.",
|
|
10
10
|
"The agent needs to confirm its stable runtime identity and default output Host."
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
{
|
|
24
24
|
"intent": "list supported Agent Types",
|
|
25
25
|
"command": "itpay install --json",
|
|
26
|
-
"success_signal": "status is install_targets and exactly
|
|
26
|
+
"success_signal": "status is install_targets and exactly seven supported Agent Type definitions are returned"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"intent": "read setup for the real runtime",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"agent_rules": [
|
|
35
|
-
"
|
|
35
|
+
"Use the CLI distribution already provided by the current npm package, Skill, or plugin; install only through that distribution's own instructions.",
|
|
36
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
|
-
"If backend_contract_incompatible includes result.required_cli_version, stop all ItPay business commands and
|
|
37
|
+
"If backend_contract_incompatible includes result.required_cli_version, stop all ItPay business commands and use only the exact distribution-specific update 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
|
-
"Use one exact type: codex-desktop, codex-cli, claude-code-desktop, claude-code-cli, or
|
|
39
|
+
"Use one exact type: codex-desktop, codex-cli, claude-code-desktop, claude-code-cli, workbuddy, kimi-code, or openclaw.",
|
|
40
40
|
"One local private key is reused, while Device registrations and quota lineage remain separate for app.itpay.ai and dev.itpay.ai.",
|
|
41
41
|
"Within each official Backend registration, each Agent Type has one Agent Instance; all windows and chats of the same type reuse it.",
|
|
42
|
-
"Agent Type identifies the runtime. Host controls rendering and target only identifies a presentation destination.",
|
|
42
|
+
"Agent Type identifies the runtime. Host controls rendering and target only identifies a presentation destination. OpenClaw requires an explicit Host and IM target; Kimi Code uses the standard terminal CLI contract.",
|
|
43
43
|
"Keep the exact Agent Type in every next or recovery command; do not fall back to a type previously used by another runtime.",
|
|
44
44
|
"Do not change Agent Type or rotate local identity to reset quota or recover a failed command."
|
|
45
45
|
],
|
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
"backend",
|
|
67
67
|
"codex",
|
|
68
68
|
"claude code",
|
|
69
|
-
"workbuddy"
|
|
69
|
+
"workbuddy",
|
|
70
|
+
"kimi code",
|
|
71
|
+
"openclaw"
|
|
70
72
|
]
|
|
71
73
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"Before creating a single-Service Checkout, send the exact price confirmation requested by the current instruction, stop, and wait for explicit human agreement.",
|
|
33
33
|
"After presenting a Checkout, stop. Run the returned next.command only after the human completes the payment action or asks to query the same Checkout.",
|
|
34
34
|
"For workbuddy with plain-chat, present the returned HTTPS QR with present_files exactly as instructed; presentation failure falls back only to handoff.url and never creates another payment resource.",
|
|
35
|
+
"For openclaw with telegram, execute the returned native message action using the current trusted target. For any other explicit OpenClaw Host, show the returned HTTPS QR image and Checkout URL.",
|
|
35
36
|
"Payment is verified only by Backend Checkout or Order state, never by QR rendering, redirect, or user claim.",
|
|
36
37
|
"A terminal payment state must never display another payment handoff."
|
|
37
38
|
],
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"Keep the same explicit Agent Type through next and recovery commands; same-type windows reuse one Backend Agent Instance.",
|
|
60
60
|
"When testing dev, execute every returned command unchanged so its ITPAY_BACKEND_URL=https://dev.itpay.ai prefix is preserved.",
|
|
61
61
|
"Use --target only for a Host presentation destination. Put capability business values in --input key=value exactly as required by the schema.",
|
|
62
|
+
"OpenClaw must pass the current entry as --host and an IM destination as --target. Kimi Code follows the standard terminal CLI flow.",
|
|
62
63
|
"Start a separate Service Execution for each independent service intent; quota remains shared according to Backend identity policy.",
|
|
63
64
|
"Ask the user for required email or contact values and explain their purpose; never invent them.",
|
|
64
65
|
"Agent-visible delivery is returned by services next. Protected delivery is read only by services read-result while a human grant is active.",
|
|
@@ -34,6 +34,18 @@
|
|
|
34
34
|
"agent_type": "workbuddy",
|
|
35
35
|
"default_host": "plain-chat",
|
|
36
36
|
"responsibility": "when handoff.qr_image_url exists, use its exact HTTPS value as the only files element in present_files; otherwise send the Checkout URL without calling present_files; show the amount and stop"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"agent_type": "kimi-code",
|
|
40
|
+
"default_host": "terminal",
|
|
41
|
+
"responsibility": "use the standard CLI terminal QR and Checkout URL"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"agent_type": "openclaw",
|
|
45
|
+
"default_host": null,
|
|
46
|
+
"host_required": true,
|
|
47
|
+
"native_hosts": ["telegram"],
|
|
48
|
+
"responsibility": "pass the current entry explicitly; for Telegram execute handoff.agent_action with the current target, otherwise show the returned HTTPS QR image and Checkout URL"
|
|
37
49
|
}
|
|
38
50
|
],
|
|
39
51
|
"commands": [
|
|
@@ -53,7 +65,8 @@
|
|
|
53
65
|
"A local QR path is not visible until a desktop Agent attaches or renders that file on the human-facing surface; WorkBuddy never receives a local QR path.",
|
|
54
66
|
"For workbuddy with plain-chat, call present_files exactly as the returned instruction says. If it fails, send only handoff.url, report that the preview did not open, and stop.",
|
|
55
67
|
"Do not claim the handoff was shown until both a usable payment image or QR and the Checkout URL are visible.",
|
|
56
|
-
"IM Hosts that require a target must receive --target
|
|
68
|
+
"IM Hosts that require a target must receive --target before Checkout creation. OpenClaw has no default Host; Kimi Code uses terminal.",
|
|
69
|
+
"OpenClaw Telegram typed callback actions carry only the Checkout ID, never a display token, and never prove payment."
|
|
57
70
|
],
|
|
58
71
|
"forbidden": [
|
|
59
72
|
"Do not expose display tokens separately from the tokenized Checkout handoff.",
|
|
@@ -74,6 +87,9 @@
|
|
|
74
87
|
"markdown",
|
|
75
88
|
"terminal",
|
|
76
89
|
"desktop",
|
|
77
|
-
"workbuddy"
|
|
90
|
+
"workbuddy",
|
|
91
|
+
"kimi code",
|
|
92
|
+
"openclaw",
|
|
93
|
+
"telegram"
|
|
78
94
|
]
|
|
79
95
|
}
|