@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
|
@@ -17,30 +17,11 @@ export function buildWorkBuddyPresentFilesAction(url) {
|
|
|
17
17
|
arguments: { files: [url] },
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
function hasCompleteCheckoutTokens(url) {
|
|
21
|
-
if (!url) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
const parsed = new URL(url);
|
|
26
|
-
return Boolean(parsed.searchParams.get("display_token") && parsed.searchParams.get("complete_exchange_token"));
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
20
|
export function buildCheckoutHandoff(input) {
|
|
33
|
-
const
|
|
34
|
-
const handoff = { url, ...(input.plan?.localSimulation ? { environment: "local_simulation", query: "live", transactions: "simulated" } : {}) };
|
|
35
|
-
// mobile_url is only emitted when the link carries the complete token pair;
|
|
36
|
-
// a display-token-only link cannot finish the same-device exchange after payment.
|
|
37
|
-
const mobileUrl = !input.plan?.localSimulation && hasCompleteCheckoutTokens(input.mobileUrl) ? input.mobileUrl : undefined;
|
|
38
|
-
if (mobileUrl) {
|
|
39
|
-
handoff.mobile_url = mobileUrl;
|
|
40
|
-
}
|
|
21
|
+
const handoff = { url: input.url };
|
|
41
22
|
const workBuddyAction = isWorkBuddyPlainChat(input.agentType, input.platform);
|
|
42
23
|
if (workBuddyAction) {
|
|
43
|
-
handoff.agent_action = buildWorkBuddyPresentFilesAction(url);
|
|
24
|
+
handoff.agent_action = buildWorkBuddyPresentFilesAction(input.url);
|
|
44
25
|
}
|
|
45
26
|
if (input.platform === "markdown") {
|
|
46
27
|
if (input.localPath)
|
|
@@ -59,43 +40,24 @@ export function buildCheckoutHandoff(input) {
|
|
|
59
40
|
}
|
|
60
41
|
return {
|
|
61
42
|
handoff,
|
|
62
|
-
instruction: input.
|
|
63
|
-
? `本地实验:铁路与地图查询真实,授权、通知和交易模拟,不会扣款。在本机打开 handoff.url 模拟付款入口,金额 ${input.amount};仅本机桌面可达。${workBuddyAction ? "按 handoff.agent_action 原样打开一次。" : "展示原入口。"}等待用户操作后读取同一 Checkout;不创建替代订单,用户声明不证明模拟付款已确认。`
|
|
64
|
-
: `先展示官方入口,再做非必要记忆或日志;保留必需安全检查和宿主权限。${checkoutHandoffInstruction(input.agentType, input.platform, input.amount, Boolean(mobileUrl))}`,
|
|
43
|
+
instruction: checkoutHandoffInstruction(input.agentType, input.platform, input.amount),
|
|
65
44
|
};
|
|
66
45
|
}
|
|
67
|
-
function checkoutHandoffInstruction(agentType, platform, amount
|
|
46
|
+
function checkoutHandoffInstruction(agentType, platform, amount) {
|
|
68
47
|
if (isWorkBuddyPlainChat(agentType, platform)) {
|
|
69
|
-
|
|
70
|
-
? ",然后把 handoff.mobile_url 作为可点击链接发给用户,说明“手机端点开可直接跳转支付宝或微信支付;小程序内打不开时复制到手机浏览器打开即可”"
|
|
71
|
-
: "";
|
|
72
|
-
const failLinks = hasMobileLink ? "发送 handoff.url 与 handoff.mobile_url" : "发送原始 handoff.url";
|
|
73
|
-
return `Backend 尚未确认付款。立即严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行一次,在右侧打开 handoff.url;确认工具调用成功后说明金额 ${amount}${mobileSentence},然后停止等待。若工具失败,只${failLinks},报告未自动打开并停止。不要用 present_files 打开本地文件或二维码 PNG,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
48
|
+
return `Backend 尚未确认付款。立即严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行一次,在右侧打开 handoff.url;确认工具调用成功后说明金额 ${amount},然后停止等待。若工具失败,只发送原始 handoff.url,报告未自动打开并停止。不要用 present_files 打开本地文件或二维码 PNG,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
74
49
|
}
|
|
75
50
|
if (isZCodePlainChat(agentType, platform)) {
|
|
76
|
-
|
|
77
|
-
? ",并把 handoff.mobile_url 发给用户备用(手机端点开可直接跳转支付宝或微信支付)"
|
|
78
|
-
: "";
|
|
79
|
-
const fallbackLinks = hasMobileLink ? "同一个可点击 handoff.url 与 handoff.mobile_url" : "同一个可点击 handoff.url";
|
|
80
|
-
return `Backend 尚未确认付款。立即用 ZCode 内置浏览器打开 handoff.url,让用户完成付款;确认已发起打开后说明金额 ${amount}${mobileSentence},然后停止等待。不要只粘贴文字链接,不要下载、解析或重建二维码,不要创建新 Checkout、Payment Intent 或 Execution。只有内置浏览器明确不可用时,才展示${fallbackLinks}。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
81
|
-
}
|
|
82
|
-
if (agentType?.trim().toLowerCase() === "doubao-work" && platform === "plain_chat") {
|
|
83
|
-
const directLink = hasMobileLink ? "handoff.mobile_url" : "handoff.url";
|
|
84
|
-
const directLabel = hasMobileLink ? "手机直接打开收银台(可跳转支付宝/微信)" : "手机直接打开收银台";
|
|
85
|
-
return `Backend 尚未确认付款。在当前豆包工作对话中展示两个有标签的官方入口:将 ${directLink} 标为“${directLabel}”,将 handoff.qr_image_url 标为“二维码图片(保存或用另一台设备扫码)”。说明金额 ${amount} 后停止等待。不要解析或单独输出 URL credential,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
51
|
+
return `Backend 尚未确认付款。立即用 ZCode 内置浏览器打开 handoff.url,让用户完成付款;确认已发起打开后说明金额 ${amount},然后停止等待。不要只粘贴文字链接,不要下载、解析或重建二维码,不要创建新 Checkout、Payment Intent 或 Execution。只有内置浏览器明确不可用时,才展示同一个可点击 handoff.url。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
86
52
|
}
|
|
87
53
|
if (platform === "markdown") {
|
|
88
|
-
|
|
89
|
-
return `Backend 尚未确认付款。把 handoff.markdown 原样发送到当前桌面对话,确认二维码、付款链接和金额 ${amount} 均已实际对用户可见${mobileSentence},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
54
|
+
return `Backend 尚未确认付款。把 handoff.markdown 原样发送到当前桌面对话,确认二维码、付款链接和金额 ${amount} 均已实际对用户可见,然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
90
55
|
}
|
|
91
56
|
if (platform === "terminal") {
|
|
92
|
-
|
|
93
|
-
return `Backend 尚未确认付款。在用户可见终端展示当前 Checkout 的二维码、handoff.url 和金额 ${amount}${mobileSentence},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
57
|
+
return `Backend 尚未确认付款。在用户可见终端展示当前 Checkout 的二维码、handoff.url 和金额 ${amount},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
94
58
|
}
|
|
95
59
|
if (agentType?.trim().toLowerCase() === "openclaw" && platform === "telegram") {
|
|
96
|
-
|
|
97
|
-
return `Backend 尚未确认付款或授权状态。立即使用 OpenClaw 原生 message tool,严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行;它会发送金额 ${amount}、二维码、📱 手机点这儿支付和 📋 已授权给我读。不得改写 presentation、换用其他消息工具、拆开按钮发送或用普通回复声称按钮已经发送。确认 message tool 调用成功后停止等待。收到 callback_data "itp:grant_confirmed:<checkout_id>" 时,表示用户声明已在收银台授权读取;立即执行 next.command 查询同一 Checkout,再只执行 Backend 返回的 next.command 进入同一 Execution 的 grant 流程。该 callback 不证明付款成功或 grant 已生效;Backend 未返回 grant_active 前不得读取或猜测结果。若原生 message tool 明确失败或当前 Telegram 未启用 inline buttons,只发送现有 ${failLinks},报告按钮不可用并停止;不要创建新的 Checkout、Payment Intent 或 Execution。`;
|
|
60
|
+
return `Backend 尚未确认付款或授权状态。立即使用 OpenClaw 原生 message tool,严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行;它会发送金额 ${amount}、二维码、📱 手机点这儿支付和 📋 已授权给我读。不得改写 presentation、换用其他消息工具、拆开按钮发送或用普通回复声称按钮已经发送。确认 message tool 调用成功后停止等待。收到 callback_data "itp:grant_confirmed:<checkout_id>" 时,表示用户声明已在收银台授权读取;立即执行 next.command 查询同一 Checkout,再只执行 Backend 返回的 next.command 进入同一 Execution 的 grant 流程。该 callback 不证明付款成功或 grant 已生效;Backend 未返回 grant_active 前不得读取或猜测结果。若原生 message tool 明确失败或当前 Telegram 未启用 inline buttons,只发送现有 handoff.qr_image_url、金额和 handoff.url,报告按钮不可用并停止;不要创建新的 Checkout、Payment Intent 或 Execution。`;
|
|
98
61
|
}
|
|
99
|
-
|
|
100
|
-
return `Backend 尚未确认付款。把 ${urlList} 实际发送到当前会话,说明金额 ${amount},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
62
|
+
return `Backend 尚未确认付款。把 handoff.url 和可用的 handoff.qr_image_url 实际发送到当前会话,说明金额 ${amount},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
101
63
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveOutput } from "../render/sink.js";
|
|
5
|
+
import { CommandContractError, writeCommandEnvelope } from "./guidance.js";
|
|
5
6
|
const commandDir = dirname(fileURLToPath(import.meta.url));
|
|
6
7
|
export function runDocsList(options = {}) {
|
|
7
8
|
const topics = loadDocs().map(({ topic, title, purpose }) => ({ topic, title, purpose }));
|
|
@@ -22,7 +23,7 @@ export function runDocsShow(topic, options = {}) {
|
|
|
22
23
|
if (!doc) {
|
|
23
24
|
throw new CommandContractError("doc_not_found", `doc topic not found: ${topic}`, "使用稳定 topic 名称;不要根据标题猜 topic。", [
|
|
24
25
|
{ command: "itpay docs list --json", reason: "列出全部 topic" },
|
|
25
|
-
{ command: `itpay docs search ${
|
|
26
|
+
{ command: `itpay docs search ${shellWord(normalized || "topic")} --json`, reason: "按关键词重新搜索" },
|
|
26
27
|
]);
|
|
27
28
|
}
|
|
28
29
|
const envelope = {
|
|
@@ -32,7 +33,14 @@ export function runDocsShow(topic, options = {}) {
|
|
|
32
33
|
next: null,
|
|
33
34
|
recovery: [],
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
+
if (options.jsonOutput) {
|
|
37
|
+
writeCommandEnvelope(envelope, options);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const out = resolveOutput(options.output);
|
|
41
|
+
out("shown\n");
|
|
42
|
+
out(`${JSON.stringify(doc, null, 2)}\n`);
|
|
43
|
+
out(`instruction: ${envelope.instruction}\n`);
|
|
36
44
|
}
|
|
37
45
|
export function runDocsSearch(query, options = {}) {
|
|
38
46
|
const normalized = query.trim().toLowerCase();
|
|
@@ -76,10 +84,10 @@ function loadDocs() {
|
|
|
76
84
|
function findDocsDir() {
|
|
77
85
|
if (process.env.ITPAY_CLI_DOCS_DIR)
|
|
78
86
|
return resolve(process.env.ITPAY_CLI_DOCS_DIR);
|
|
79
|
-
const
|
|
80
|
-
if (existsSync(
|
|
81
|
-
return
|
|
82
|
-
return resolve(commandDir, "..", "..", "docs", "agent", "buyer");
|
|
87
|
+
const sourcePath = resolve(commandDir, "..", "..", "docs", "agent", "buyer");
|
|
88
|
+
if (existsSync(sourcePath))
|
|
89
|
+
return sourcePath;
|
|
90
|
+
return resolve(commandDir, "..", "..", "..", "docs", "agent", "buyer");
|
|
83
91
|
}
|
|
84
92
|
function parseDoc(raw, file) {
|
|
85
93
|
const value = JSON.parse(raw);
|
|
@@ -95,3 +103,6 @@ function parseDoc(raw, file) {
|
|
|
95
103
|
function searchableText(doc) {
|
|
96
104
|
return [doc.topic, doc.title, doc.purpose, ...(doc.search_terms ?? [])].join(" ").toLowerCase();
|
|
97
105
|
}
|
|
106
|
+
function shellWord(value) {
|
|
107
|
+
return /^[a-zA-Z0-9._-]+$/.test(value) ? value : JSON.stringify(value);
|
|
108
|
+
}
|
|
@@ -15,82 +15,46 @@ export class CommandContractError extends Error {
|
|
|
15
15
|
code;
|
|
16
16
|
instruction;
|
|
17
17
|
recovery;
|
|
18
|
-
|
|
19
|
-
result;
|
|
20
|
-
constructor(code, message, instruction, recovery, interaction, result) {
|
|
18
|
+
constructor(code, message, instruction, recovery) {
|
|
21
19
|
super(message);
|
|
22
20
|
this.code = code;
|
|
23
21
|
this.instruction = instruction;
|
|
24
22
|
this.recovery = recovery;
|
|
25
|
-
this.interaction = interaction;
|
|
26
|
-
this.result = result;
|
|
27
23
|
this.name = "CommandContractError";
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
|
-
// Domain projections and pages own byte limits; serialization never truncates facts.
|
|
31
|
-
export function plainValueLines(value, label = "", depth = 0) {
|
|
32
|
-
const indent = " ".repeat(depth);
|
|
33
|
-
if (value === null || typeof value !== "object")
|
|
34
|
-
return [`${indent}${label ? `${label}: ` : ""}${String(value ?? "unknown")}`];
|
|
35
|
-
const lines = label ? [`${indent}${label}:`] : [];
|
|
36
|
-
const entries = Array.isArray(value) ? value.map((v, i) => [String(i + 1), v]) : Object.entries(value);
|
|
37
|
-
for (const [key, child] of entries)
|
|
38
|
-
lines.push(...plainValueLines(child, key, depth + (label ? 1 : 0)));
|
|
39
|
-
if (!entries.length)
|
|
40
|
-
lines.push(`${indent} (无记录)`);
|
|
41
|
-
return lines;
|
|
42
|
-
}
|
|
43
|
-
export function shellArgument(value) {
|
|
44
|
-
if (/^[\p{L}\p{N}._:=/-]+$/u.test(value))
|
|
45
|
-
return value;
|
|
46
|
-
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
47
|
-
}
|
|
48
26
|
export function writeCommandEnvelope(value, options = {}) {
|
|
49
27
|
const out = resolveOutput(options.output);
|
|
50
28
|
const agentType = options.agentType ?? declaredAgentType();
|
|
51
29
|
const qualified = qualifyEnvelope(value, agentType);
|
|
52
30
|
if (options.jsonOutput) {
|
|
53
|
-
|
|
54
|
-
out(pretty);
|
|
31
|
+
out(JSON.stringify(qualified, null, 2) + "\n");
|
|
55
32
|
return;
|
|
56
33
|
}
|
|
57
34
|
out(`${qualified.status}\n`);
|
|
58
35
|
const facts = "error" in qualified ? qualified.error : qualified.result ?? {};
|
|
59
36
|
if (options.plainResult) {
|
|
60
|
-
for (const line of options.plainResult)
|
|
61
|
-
|
|
62
|
-
out(`${command ? command[1] + qualifyBackendCommand(qualifyItPayCommand(command[2], agentType)) : line}\n`);
|
|
63
|
-
}
|
|
37
|
+
for (const line of options.plainResult)
|
|
38
|
+
out(`${line}\n`);
|
|
64
39
|
}
|
|
65
40
|
else {
|
|
66
41
|
for (const [key, fact] of Object.entries(facts)) {
|
|
67
|
-
|
|
68
|
-
out(`${line}\n`);
|
|
42
|
+
out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
69
43
|
}
|
|
70
44
|
if ("error" in qualified && qualified.result) {
|
|
71
45
|
for (const [key, fact] of Object.entries(qualified.result)) {
|
|
72
|
-
|
|
73
|
-
out(`${line}\n`);
|
|
46
|
+
out(`${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
74
47
|
}
|
|
75
48
|
}
|
|
76
49
|
}
|
|
77
50
|
if ("handoff" in qualified && qualified.handoff) {
|
|
78
51
|
for (const [key, fact] of Object.entries(qualified.handoff)) {
|
|
79
|
-
|
|
80
|
-
out(`${line}\n`);
|
|
52
|
+
out(`handoff.${key}: ${typeof fact === "string" ? fact : JSON.stringify(fact)}\n`);
|
|
81
53
|
}
|
|
82
54
|
}
|
|
83
55
|
out(`instruction: ${qualified.instruction}\n`);
|
|
84
56
|
if (qualified.next)
|
|
85
|
-
out(`next: ${qualified.next.command}\n
|
|
86
|
-
if ("interaction" in qualified && qualified.interaction) {
|
|
87
|
-
for (const line of plainValueLines(qualified.interaction, "interaction"))
|
|
88
|
-
out(`${line}\n`);
|
|
89
|
-
}
|
|
90
|
-
if ("communication" in qualified && qualified.communication) {
|
|
91
|
-
for (const line of plainValueLines(qualified.communication, "communication"))
|
|
92
|
-
out(`${line}\n`);
|
|
93
|
-
}
|
|
57
|
+
out(`next: ${qualified.next.command}\n`);
|
|
94
58
|
if (qualified.recovery.length > 0) {
|
|
95
59
|
out("recovery:\n");
|
|
96
60
|
for (const action of qualified.recovery) {
|
|
@@ -102,10 +66,6 @@ export function writeCommandEnvelope(value, options = {}) {
|
|
|
102
66
|
function qualifyEnvelope(value, agentType) {
|
|
103
67
|
return {
|
|
104
68
|
...value,
|
|
105
|
-
result: qualifyCommandsDeep(value.result, agentType),
|
|
106
|
-
...("handoff" in value && value.handoff ? { handoff: qualifyCommandsDeep(value.handoff, agentType) } : {}),
|
|
107
|
-
...("interaction" in value && value.interaction ? { interaction: qualifyCommandsDeep(value.interaction, agentType) } : {}),
|
|
108
|
-
...("communication" in value && value.communication ? { communication: qualifyCommandsDeep(value.communication, agentType) } : {}),
|
|
109
69
|
next: value.next ? { ...value.next, command: qualifyBackendCommand(qualifyItPayCommand(value.next.command, agentType)) } : null,
|
|
110
70
|
recovery: value.recovery.map((action) => ({
|
|
111
71
|
...action,
|
|
@@ -113,23 +73,6 @@ function qualifyEnvelope(value, agentType) {
|
|
|
113
73
|
})),
|
|
114
74
|
};
|
|
115
75
|
}
|
|
116
|
-
// Every `itpay ` command string anywhere in the envelope — result fields,
|
|
117
|
-
// interaction recipes/templates, available_actions — keeps the same Agent
|
|
118
|
-
// Type / Backend qualification as top-level next/recovery. Instruction prose
|
|
119
|
-
// never starts with "itpay " so prose stays untouched.
|
|
120
|
-
function qualifyCommandsDeep(value, agentType) {
|
|
121
|
-
if (typeof value === "string") {
|
|
122
|
-
return value.startsWith("itpay ")
|
|
123
|
-
? qualifyBackendCommand(qualifyItPayCommand(value, agentType))
|
|
124
|
-
: value;
|
|
125
|
-
}
|
|
126
|
-
if (Array.isArray(value))
|
|
127
|
-
return value.map((item) => qualifyCommandsDeep(item, agentType));
|
|
128
|
-
if (value && typeof value === "object") {
|
|
129
|
-
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, qualifyCommandsDeep(item, agentType)]));
|
|
130
|
-
}
|
|
131
|
-
return value;
|
|
132
|
-
}
|
|
133
76
|
export function errorRecoveryActions(error) {
|
|
134
77
|
if (!(error instanceof HttpError))
|
|
135
78
|
return [];
|
|
@@ -148,12 +91,19 @@ export function errorRecoveryActions(error) {
|
|
|
148
91
|
return [{
|
|
149
92
|
id: "inspect_service_execution",
|
|
150
93
|
label: "Inspect Service Execution before checkout",
|
|
151
|
-
command: "itpay services
|
|
94
|
+
command: "itpay services next <service_execution_id> --json",
|
|
152
95
|
}];
|
|
153
96
|
}
|
|
154
|
-
if (error.code === "cart_item_locked") {
|
|
155
|
-
return [
|
|
156
|
-
|
|
97
|
+
if (error.code === "cart_item_locked" || error.status === 409) {
|
|
98
|
+
return [
|
|
99
|
+
{ id: "show_cart", label: "Inspect the canonical server cart", command: "itpay cart show" },
|
|
100
|
+
{ id: "continue_checkout", label: "Continue the last locally remembered checkout", command: "itpay checkout" },
|
|
101
|
+
{
|
|
102
|
+
id: "recover_service_execution",
|
|
103
|
+
label: "List recoverable Service Executions if the local handoff is missing",
|
|
104
|
+
command: "itpay services list",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
157
107
|
}
|
|
158
108
|
if (error.status === 404) {
|
|
159
109
|
return [{
|
|
@@ -8,19 +8,16 @@ export const INSTALL_AGENT_TYPES = [
|
|
|
8
8
|
"claude-code-cli",
|
|
9
9
|
"workbuddy",
|
|
10
10
|
"zcode",
|
|
11
|
-
"doubao-work",
|
|
12
11
|
"kimi-code",
|
|
13
12
|
"openclaw",
|
|
14
13
|
];
|
|
15
|
-
export const CODEX_WAIT_GUIDANCE = "Codex 宿主等待:保留完整工具返回,按实际返回的会话句柄及当前宿主续读工具收取最终输出,不要只取 stdout。若使用 functions.exec 包装,可用 text(await tools.exec_command(...));内层有 session_id 时用 write_stdin,外层返回运行 cell 时才用 wait。没有该包装时按宿主实际工具操作。先收取最终输出,再执行 CLI 业务 next;不要重复提交。";
|
|
16
14
|
const INSTRUCTIONS = {
|
|
17
15
|
"codex-desktop": "在 Codex Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
18
16
|
"codex-cli": "在 Codex CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
|
|
19
17
|
"claude-code-desktop": "在 Claude Code Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
|
|
20
18
|
"claude-code-cli": "在 Claude Code CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
|
|
21
|
-
workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity
|
|
19
|
+
workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 返回已渲染的付款 Card Link 和 present_files action;原样执行一次 action 打开 handoff.url,不要检查本地二维码文件。",
|
|
22
20
|
zcode: "在 ZCode 中始终传这个 Agent Type。Checkout 或授权返回 handoff.url 后,立即用 ZCode 内置浏览器打开;不要只粘贴文字链接或重建二维码。只有内置浏览器不可用时才展示同一个可点击链接。",
|
|
23
|
-
"doubao-work": "在豆包工作中始终传这个 Agent Type。Checkout 或授权返回后,同时展示 handoff.url 和 handoff.qr_image_url 两个有标签的官方入口;优先让用户在手机上直接打开链接,二维码供保存或另一台设备扫码。",
|
|
24
21
|
"kimi-code": "保持 kimi-code Agent Type,使用标准 CLI 命令和终端付款交接;不要切换到其他平台身份。",
|
|
25
22
|
openclaw: "保持 openclaw Agent Type;每个展示命令都从当前可信会话上下文显式传 --host,IM 入口同时传 --target。Telegram 使用返回的原生 message action,其他入口展示标准二维码和付款链接。",
|
|
26
23
|
};
|
|
@@ -49,12 +46,12 @@ export function runInstall(target, options = {}) {
|
|
|
49
46
|
...installDefinition(normalized),
|
|
50
47
|
default_api: DEFAULT_BASE_URL,
|
|
51
48
|
},
|
|
52
|
-
instruction: INSTRUCTIONS[normalized]
|
|
49
|
+
instruction: INSTRUCTIONS[normalized],
|
|
53
50
|
next: {
|
|
54
51
|
command: `itpay --agent-type ${normalized} readyz --json`,
|
|
55
52
|
reason: "验证当前官方 ItPay API 的可用性",
|
|
56
53
|
},
|
|
57
|
-
recovery: [],
|
|
54
|
+
recovery: [{ command: "itpay docs show install-and-setup", reason: "查看官方 Backend 和首次使用说明" }],
|
|
58
55
|
}, options);
|
|
59
56
|
}
|
|
60
57
|
function isInstallAgentType(value) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { refundStateEnvelope } from "./refund.js";
|
|
2
1
|
import { formatMoney } from "../render/output.js";
|
|
3
2
|
import { appendFeedbackPostmortemInstruction, writeCommandEnvelope } from "./guidance.js";
|
|
4
3
|
export async function runOrder(backend, orderID, options = {}) {
|
|
@@ -20,7 +19,7 @@ function orderEnvelope(order, delivery, lockedRefund) {
|
|
|
20
19
|
let instruction = "订单状态已读取;当前没有可用交付入口。";
|
|
21
20
|
let next = null;
|
|
22
21
|
if (lockedRefund) {
|
|
23
|
-
instruction =
|
|
22
|
+
instruction = "告诉用户退款处理中,原交付已按政策冻结。然后读取同一退款的权威状态;Agent 不读取交付、不创建授权或重复申请退款。";
|
|
24
23
|
if (!refundTerminal) {
|
|
25
24
|
next = { command: `itpay refund get ${lockedRefund.refund_request_id} --json`, reason: "读取退款的服务器状态" };
|
|
26
25
|
}
|
|
@@ -39,13 +38,6 @@ function orderEnvelope(order, delivery, lockedRefund) {
|
|
|
39
38
|
else if (order.status === "cancelled") {
|
|
40
39
|
instruction = "先告诉用户这笔订单已经取消,没有可继续的付款或交付;不要创建替代订单,除非用户另行提出新的购买。";
|
|
41
40
|
}
|
|
42
|
-
else if (order.status === "pending_payment") {
|
|
43
|
-
const remaining = typeof order.payment_remaining_seconds === "number" && order.payment_remaining_seconds <= 0;
|
|
44
|
-
instruction = remaining
|
|
45
|
-
? "付款时限已到,但订单权威状态仍为待付款;读取同一订单核对到账或终止结果。不要重复支付、重建订单或推测已取消,刷新不会延长期限。"
|
|
46
|
-
: `先告诉用户订单正在等待付款${order.payment_deadline_at ? `,付款截止时间为 ${order.payment_deadline_at}` : ""},超时未支付会被自动取消;不要创建替代订单,也不要重复发起支付授权。`;
|
|
47
|
-
next = { command: `itpay order ${order.order_id} --json`, reason: "展示后停止;仅用户说已付款或要求核对时读取同一订单,到期可核对原单" };
|
|
48
|
-
}
|
|
49
41
|
else if (!["delivered", "refunded", "failed", "cancelled"].includes(order.status)) {
|
|
50
42
|
instruction = "先告诉用户订单仍在处理,已记录的付款和订单不需要重复创建;稍后查询同一订单,不要创建替代订单。";
|
|
51
43
|
next = { command: `itpay order ${order.order_id} --json`, reason: "刷新订单状态" };
|
|
@@ -56,11 +48,6 @@ function orderEnvelope(order, delivery, lockedRefund) {
|
|
|
56
48
|
order_id: order.order_id,
|
|
57
49
|
...(order.order_code ? { order_code: order.order_code } : {}),
|
|
58
50
|
amount: formatMoney(order.amount_minor, order.currency),
|
|
59
|
-
...(order.payment_deadline_at ? { payment_deadline_at: order.payment_deadline_at } : {}),
|
|
60
|
-
...(typeof order.payment_remaining_seconds === "number"
|
|
61
|
-
? { payment_remaining_seconds: order.payment_remaining_seconds }
|
|
62
|
-
: {}),
|
|
63
|
-
...(order.server_now ? { server_now: order.server_now } : {}),
|
|
64
51
|
...(delivery ? { delivery_mode: delivery.delivery_mode } : {}),
|
|
65
52
|
access_locked: Boolean(lockedRefund),
|
|
66
53
|
...(delivery?.service_execution_id ? { service_execution_id: delivery.service_execution_id } : {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { HttpError } from "../client/http.js";
|
|
2
2
|
import { formatMoney } from "../render/output.js";
|
|
3
3
|
import { resolveOutput } from "../render/sink.js";
|
|
4
|
-
import { CommandContractError,
|
|
5
|
-
import { accessContextInstruction,
|
|
4
|
+
import { CommandContractError, writeCommandEnvelope } from "./guidance.js";
|
|
5
|
+
import { accessContextInstruction, vaultAccessCommand } from "./vault.js";
|
|
6
6
|
const ORDER_STATUSES = new Set([
|
|
7
7
|
"pending_payment",
|
|
8
8
|
"paid",
|
|
@@ -30,8 +30,8 @@ export async function runListOrders(backend, config, options) {
|
|
|
30
30
|
writeCommandEnvelope({
|
|
31
31
|
status: "human_authorization_required",
|
|
32
32
|
result: { intent: "list_purchase_history" },
|
|
33
|
-
instruction:
|
|
34
|
-
|
|
33
|
+
instruction: `需要用户确认一次身份和只读权限。执行 next.command 生成官方入口;用户完成后重新运行原始 orders 命令。${accessContextInstruction(options)}`,
|
|
34
|
+
next: { command: vaultAccessCommand(undefined, options), reason: "创建一次账号读取授权" },
|
|
35
35
|
recovery: [],
|
|
36
36
|
}, {
|
|
37
37
|
...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
|
|
@@ -92,3 +92,8 @@ function ordersPageCommand(cursor, options) {
|
|
|
92
92
|
parts.push("--json");
|
|
93
93
|
return parts.join(" ");
|
|
94
94
|
}
|
|
95
|
+
function shellArgument(value) {
|
|
96
|
+
if (/^[\p{L}\p{N}._:=/-]+$/u.test(value))
|
|
97
|
+
return value;
|
|
98
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
99
|
+
}
|
package/dist/src/commands/pay.js
CHANGED
|
@@ -53,7 +53,7 @@ function payEnvelope(intent, options) {
|
|
|
53
53
|
instruction: payInstruction(options, verified, terminal, hasAction, amount),
|
|
54
54
|
next: {
|
|
55
55
|
command: `itpay checkout --id ${options.checkoutID} --token ${options.displayToken} --json`,
|
|
56
|
-
reason: verified ? "读取权威订单和履约状态" : "
|
|
56
|
+
reason: verified ? "读取权威订单和履约状态" : "读取同一 Checkout 的权威付款状态",
|
|
57
57
|
},
|
|
58
58
|
recovery: [],
|
|
59
59
|
};
|
|
@@ -8,8 +8,8 @@ export async function runReadyz(backend, options = {}) {
|
|
|
8
8
|
status: response.status,
|
|
9
9
|
result: { backend: "available", backend_url: backendURL, environment, ...(options.agentType ? { agent_type: options.agentType } : {}) },
|
|
10
10
|
instruction: environment === "development"
|
|
11
|
-
?
|
|
12
|
-
: "ItPay
|
|
11
|
+
? "ItPay dev 可用。只证明后端存活。尚未读此次包 Skill 时完整读取;已读同版本则无需重复,继续设备状态检查;后续必须执行返回的完整命令并保持同一 dev Backend。"
|
|
12
|
+
: "ItPay 可用。只证明后端存活。尚未读此次包 Skill 时完整读取;已读同版本则无需重复,继续设备状态检查;不自动登录或开始业务。",
|
|
13
13
|
next: { command: "itpay skill show itpay --json", reason: "加载完整操作与安全规则" },
|
|
14
14
|
recovery: [],
|
|
15
15
|
}, options);
|
|
@@ -12,11 +12,11 @@ export async function runRefund(backend, config, options) {
|
|
|
12
12
|
plainResult: Object.entries(envelope.result).map(([key, value]) => `${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`),
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
function refundStateEnvelope(refund, status) {
|
|
16
16
|
const terminal = ["succeeded", "failed", "cancelled", "rejected"].includes(refund.status);
|
|
17
17
|
let instruction = refund.decision_mode === "manual"
|
|
18
18
|
? "先告诉用户退款已进入人工审核,原交付保持冻结;人工审核不等于拒绝,等待服务器决定,不要重复申请或承诺结果。"
|
|
19
|
-
: "先告诉用户退款申请已经记录,原交付已冻结;自动路径表示系统会继续处理,但只有最终 succeeded
|
|
19
|
+
: "先告诉用户退款申请已经记录,原交付已冻结;自动路径表示系统会继续处理,但只有最终 succeeded 才能确认退款成功。然后只跟踪同一退款,不要重复申请、reveal、授权或读取结果。";
|
|
20
20
|
if (!refund.access_locked)
|
|
21
21
|
instruction = "先告诉用户退款当前没有锁定交付;按服务器事实解释当前状态,不要自行推断退款结果、到账时间或交付资格。";
|
|
22
22
|
if (refund.status === "succeeded")
|
|
@@ -73,7 +73,7 @@ export async function runListRefunds(backend, options) {
|
|
|
73
73
|
: "该订单没有退款记录;确认用户确实要求退款后再创建。",
|
|
74
74
|
next: selected
|
|
75
75
|
? { command: `itpay refund get ${selected.refund_request_id} --json`, reason: active ? "读取活跃退款" : "读取最新退款" }
|
|
76
|
-
:
|
|
76
|
+
: { command: `itpay refund create --order ${options.orderID} --json`, reason: "为该订单创建退款" },
|
|
77
77
|
recovery: [],
|
|
78
78
|
};
|
|
79
79
|
writeCommandEnvelope(envelope, {
|
|
@@ -96,13 +96,13 @@ export async function runGetRefund(backend, refundID, options = {}) {
|
|
|
96
96
|
export async function runCancelRefund(backend, refundID, reason, options = {}) {
|
|
97
97
|
const refund = await backend.cancelRefund(refundID, reason?.trim() || "buyer_cancelled");
|
|
98
98
|
const envelope = {
|
|
99
|
-
status:
|
|
99
|
+
status: "cancelled",
|
|
100
100
|
result: {
|
|
101
101
|
refund_request_id: refund.refund_request_id,
|
|
102
102
|
order_id: refund.order_id,
|
|
103
103
|
access_locked: refund.access_locked,
|
|
104
104
|
},
|
|
105
|
-
instruction:
|
|
105
|
+
instruction: "退款已取消;如需交付,重新进入订单并取得新的授权。",
|
|
106
106
|
next: { command: `itpay order ${refund.order_id} --json`, reason: "确认订单访问状态" },
|
|
107
107
|
recovery: [],
|
|
108
108
|
};
|