@itpay/cli 2.0.25 → 2.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,16 +5,25 @@ export function shouldPrepareLocalCheckoutImage(platform) {
5
5
  export function isWorkBuddyPlainChat(agentType, platform) {
6
6
  return agentType?.trim().toLowerCase() === "workbuddy" && platform === "plain_chat";
7
7
  }
8
+ export function buildWorkBuddyPresentFilesAction(url) {
9
+ return {
10
+ tool: "present_files",
11
+ arguments: { files: [url] },
12
+ };
13
+ }
8
14
  export function buildCheckoutHandoff(input) {
9
15
  const handoff = { url: input.url };
10
- const workBuddyLinkOnly = isWorkBuddyPlainChat(input.agentType, input.platform);
16
+ const workBuddyAction = isWorkBuddyPlainChat(input.agentType, input.platform);
17
+ if (workBuddyAction) {
18
+ handoff.agent_action = buildWorkBuddyPresentFilesAction(input.url);
19
+ }
11
20
  if (input.platform === "markdown") {
12
21
  if (input.localPath)
13
22
  handoff.qr_local_path = input.localPath;
14
23
  if (input.markdown)
15
24
  handoff.markdown = input.markdown;
16
25
  }
17
- else if (input.platform === "plain_chat" && input.qrImageURL && !workBuddyLinkOnly) {
26
+ else if (input.platform === "plain_chat" && input.qrImageURL && !workBuddyAction) {
18
27
  handoff.qr_image_url = input.qrImageURL;
19
28
  }
20
29
  else if (input.platform === "telegram" && input.qrImageURL) {
@@ -30,7 +39,7 @@ export function buildCheckoutHandoff(input) {
30
39
  }
31
40
  function checkoutHandoffInstruction(agentType, platform, amount) {
32
41
  if (isWorkBuddyPlainChat(agentType, platform)) {
33
- return `Backend 尚未确认付款。向用户说明金额 ${amount},直接发送并打开 handoff.url,然后停止等待。不要调用 present_files,不要检查本地文件,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
42
+ 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;用户的话不是付款成功证明。`;
34
43
  }
35
44
  if (platform === "markdown") {
36
45
  return `Backend 尚未确认付款。把 handoff.markdown 原样发送到当前桌面对话,确认二维码、付款链接和金额 ${amount} 均已实际对用户可见,然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
@@ -15,7 +15,7 @@ const INSTRUCTIONS = {
15
15
  "codex-cli": "在 Codex CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
16
16
  "claude-code-desktop": "在 Claude Code Desktop 中始终传这个 Agent Type;付款时把返回的二维码和链接实际展示到当前对话。",
17
17
  "claude-code-cli": "在 Claude Code CLI 中始终传这个 Agent Type;付款交接只应显示在用户可见终端。",
18
- workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 只返回已渲染的付款 Card Link;直接打开 handoff.url,不要调用 present_files,也不要检查本地二维码文件。",
18
+ workbuddy: "在 WorkBuddy 中始终传这个 Agent Type。运行会持久化 ~/.itpay-v3 的 ItPay Bash 命令前,把 Shell 工具参数 dangerouslyDisableSandbox 设为 true;用户拒绝权限时停止,不要切换 Node、删除 identity 或处理 lock。Checkout 返回已渲染的付款 Card Link 和 present_files action;原样执行一次 action 打开 handoff.url,不要检查本地二维码文件。",
19
19
  "kimi-code": "保持 kimi-code Agent Type,使用标准 CLI 命令和终端付款交接;不要切换到其他平台身份。",
20
20
  openclaw: "保持 openclaw Agent Type;每个展示命令都从当前可信会话上下文显式传 --host,IM 入口同时传 --target。Telegram 使用返回的原生 message action,其他入口展示标准二维码和付款链接。",
21
21
  };
@@ -2,7 +2,7 @@
2
2
  // Checkout page; this command exists for controlled integration recovery.
3
3
  import { formatMoney } from "../render/output.js";
4
4
  import { writeCommandEnvelope } from "./guidance.js";
5
- import { isWorkBuddyPlainChat } from "./checkout_handoff.js";
5
+ import { buildWorkBuddyPresentFilesAction, isWorkBuddyPlainChat } from "./checkout_handoff.js";
6
6
  import { platformKeyForHost } from "../render/plan.js";
7
7
  export async function runPay(backend, options) {
8
8
  const intent = await backend.createPaymentIntent(options.checkoutID, {
@@ -20,11 +20,13 @@ function payEnvelope(intent, options) {
20
20
  const terminal = ["failed", "expired", "refunded"].includes(intent.status);
21
21
  const verified = intent.status === "verified" || intent.status === "partially_refunded";
22
22
  const handoff = {};
23
- const workBuddyLinkOnly = isWorkBuddyPlainChat(options.agentType, platformKeyForHost(options.host));
24
- if (!terminal && !verified && workBuddyLinkOnly) {
23
+ const workBuddyAction = isWorkBuddyPlainChat(options.agentType, platformKeyForHost(options.host));
24
+ if (!terminal && !verified && workBuddyAction) {
25
25
  const url = intent.action?.mobile_wallet_url ?? intent.action?.qr_image_url;
26
- if (url)
26
+ if (url) {
27
27
  handoff.url = url;
28
+ handoff.agent_action = buildWorkBuddyPresentFilesAction(url);
29
+ }
28
30
  }
29
31
  else {
30
32
  if (!terminal && !verified && intent.action?.qr_image_url)
@@ -60,7 +62,7 @@ function payInstruction(options, verified, terminal, hasAction, amount) {
60
62
  return "Payment Intent 尚未返回可展示动作;不要猜测渠道链接,回到同一 Checkout 查询。";
61
63
  const platform = platformKeyForHost(options.host);
62
64
  if (isWorkBuddyPlainChat(options.agentType, platform)) {
63
- return `这是受控逃生入口。说明金额 ${amount},把 handoff.url 作为可点击链接发送给用户,然后停止等待。不要调用 present_files,不要立即查询、创建替代 Checkout 或 Payment Intent。`;
65
+ return `这是受控逃生入口。立即严格按 handoff.agent_action 原样执行一次,在右侧打开 handoff.url;确认工具调用成功后说明金额 ${amount} 并停止等待。若工具失败,只发送原始 handoff.url,报告未自动打开并停止。不要用 present_files 打开本地文件或二维码 PNG,不要立即查询、创建替代 Checkout 或 Payment Intent。`;
64
66
  }
65
67
  if (options.host === "codex" || options.host === "claude-code")
66
68
  return "这是受控逃生入口;把 handoff 中的二维码或钱包链接实际发到当前桌面对话,然后停止等待。";
@@ -12,7 +12,7 @@ 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.25";
15
+ export const CLI_VERSION = "2.0.26";
16
16
  export const API_CONTRACT_REVISION = "sha256:d4e94049c01b38bc77dce76bf4e49e8243adfc588541e33af6477cd5722a5bc4";
17
17
  const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
18
18
  const CART_SESSION_FILENAME = "cart.json";
@@ -31,7 +31,7 @@
31
31
  "Use only handoff fields returned for the current Host and make them actually visible to the human.",
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
- "For workbuddy with plain-chat, send or open the fully rendered Card Link in handoff.url and never call present_files or create another payment resource.",
34
+ "For workbuddy with plain-chat, execute handoff.agent_action exactly once to open the fully rendered Card Link in handoff.url; if it fails, send only the original URL and stop without creating another payment resource.",
35
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.",
36
36
  "Payment is verified only by Backend Checkout or Order state, never by QR rendering, redirect, or user claim.",
37
37
  "A terminal payment state must never display another payment handoff."
@@ -33,7 +33,7 @@
33
33
  {
34
34
  "agent_type": "workbuddy",
35
35
  "default_host": "plain-chat",
36
- "responsibility": "show the amount and send or open handoff.url, which is the fully rendered HTML Card Link; never call present_files; then stop"
36
+ "responsibility": "execute handoff.agent_action exactly once to open the fully rendered handoff.url with present_files; show the amount and stop"
37
37
  },
38
38
  {
39
39
  "agent_type": "kimi-code",
@@ -63,7 +63,8 @@
63
63
  "agent_rules": [
64
64
  "Agent Type is stable identity; Host is presentation and may be explicitly overridden.",
65
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.",
66
- "For workbuddy with plain-chat, send or open only the rendered handoff.url and never call present_files, inspect files, or rebuild a QR.",
66
+ "For workbuddy with plain-chat, execute handoff.agent_action exactly once; it calls present_files with handoff.url as the only files element and opens the rendered Card Link in the right-side browser.",
67
+ "If the WorkBuddy action fails, send the original handoff.url, report that it did not auto-open, and stop without creating another payment resource.",
67
68
  "Do not claim the handoff was shown until both a usable payment image or QR and the Checkout URL are visible.",
68
69
  "IM Hosts that require a target must receive --target before Checkout creation. OpenClaw has no default Host; Kimi Code uses terminal.",
69
70
  "OpenClaw Telegram uses flat url/value buttons for deployed-version compatibility. A grant_confirmed callback carries only the Checkout ID, never a display token, and never proves payment or an active grant."
@@ -72,7 +73,7 @@
72
73
  "Do not expose display tokens separately from the tokenized Checkout handoff.",
73
74
  "Do not print base64 images, mirror lists, renderer debug state, or provider QR URLs.",
74
75
  "Do not claim a desktop attachment exists when only a filesystem path was printed.",
75
- "Do not call present_files or inspect, download, or regenerate a local QR file for WorkBuddy."
76
+ "Do not use present_files for a local file, QR PNG, downloaded copy, or rebuilt URL in WorkBuddy."
76
77
  ],
77
78
  "next_docs": [
78
79
  {
@@ -14,7 +14,7 @@
14
14
  | `codex-cli` | `terminal` | 在用户可见终端渲染二维码并输出付款链接;若用户不看该终端,要求改用正确 Host。 |
15
15
  | `claude-code-desktop` | `claude-code` | 返回桌面对话可展示的 Markdown 图片和付款链接,要求先展示再等待。 |
16
16
  | `claude-code-cli` | `terminal` | 在用户可见终端输出二维码和链接,不声称已在桌面对话展示。 |
17
- | `workbuddy` | `plain-chat` | 只返回完整渲染的 HTML Card Link;直接打开链接,不调用 `present_files`,不返回或检查本地图片路径。 |
17
+ | `workbuddy` | `plain-chat` | 返回完整渲染的 HTML Card Link 和可原样执行的 `present_files` action;立即打开 Card Link,不返回或检查本地图片路径。 |
18
18
  | `kimi-code` | `terminal` | 使用标准 CLI 引导,在用户可见终端渲染二维码和付款链接。 |
19
19
  | `openclaw` | 无;必须显式传入 | `--host telegram` 使用 OpenClaw 原生 `message` action;其他入口返回标准 HTTPS 二维码和付款链接。 |
20
20
 
@@ -47,7 +47,8 @@
47
47
  "url": "<checkout_or_rendered_card_url>",
48
48
  "qr_local_path": "<desktop_optional_local_path>",
49
49
  "qr_image_url": "<non_workbuddy_optional_absolute_https_png>",
50
- "markdown": "<desktop_optional_host_ready_markdown>"
50
+ "markdown": "<desktop_optional_host_ready_markdown>",
51
+ "agent_action": "<host_optional_native_action>"
51
52
  },
52
53
  "instruction": "<agent-type-specific instruction>",
53
54
  "next": {
@@ -67,11 +68,11 @@
67
68
  | `claude-code-desktop / claude-code` | `url, qr_local_path, markdown` |
68
69
  | `codex-cli / terminal` | `url`;非 JSON 输出另外渲染终端二维码 |
69
70
  | `claude-code-cli / terminal` | `url`;非 JSON 输出另外渲染终端二维码 |
70
- | `workbuddy / plain-chat` | `url`(完整渲染的 HTML Card Link) |
71
+ | `workbuddy / plain-chat` | `url, agent_action`(`present_files(files=[url])`,打开完整渲染的 HTML Card Link) |
71
72
  | `kimi-code / terminal` | `url`;非 JSON 输出另外渲染终端二维码 |
72
73
  | `openclaw / telegram` | `url, qr_image_url, agent_action` |
73
74
  | `openclaw / other` | `url, qr_image_url` |
74
75
 
75
- WorkBuddy instruction 必须直接发送并打开 `handoff.url`,明确禁止 `present_files`。这个 URL 指向 Backend 已渲染的 HTML Card;不能检查本地文件、下载或重建二维码、调用 `pay` 或创建替代付款资源。显式 `--host` 仍覆盖默认展示方式。
76
+ WorkBuddy instruction 必须要求 Agent 原样执行一次 `handoff.agent_action`,即调用 `present_files(files=[handoff.url])` 在右侧打开 Backend 已渲染的 HTML Card。调用成功后说明金额并停止;调用失败时只发送原始 `handoff.url` 并如实报告未自动打开。禁止把 `present_files` 用于本地文件或二维码 PNG,也不能下载或重建二维码、调用 `pay` 或创建替代付款资源。显式 `--host` 仍覆盖默认展示方式。
76
77
 
77
78
  OpenClaw Telegram 的 `handoff.agent_action` 是可原样执行的原生 `message` tool action。`presentation` 只包含标准 `blocks.buttons`:`📱 手机点这儿支付` 使用扁平 `url`,`📋 已授权给我读` 使用扁平 `value=itp:grant_confirmed:<checkout_id>`;二维码单独使用 action 的 `media`。CLI `instruction` 必须要求 Agent 原样执行该 action,不得改写 Presentation、换用其他消息工具或声称普通文本回复等同于已发送按钮。收到授权 callback 后立即执行 `next.command` 查询同一 Checkout,再只跟随后端返回的同一 Execution grant 流程;callback 只携带 Checkout ID,不携带 display token,也不证明付款或 grant 已生效。OpenClaw `target` 使用原生 chat target(如 `5559456744` 或 `-1001234567890:topic:42`),不添加 `telegram:` 前缀。
@@ -167,7 +167,7 @@ itpay buy \
167
167
  | `codex-cli` | `terminal` | `url` | 非 JSON 模式在用户可见终端渲染二维码;始终保留付款链接。 |
168
168
  | `claude-code-desktop` | `claude-code` | `url`、可用时 `qr_local_path` 和 `markdown` | 把 Markdown handoff 发到当前桌面对话,不能只输出本地路径。 |
169
169
  | `claude-code-cli` | `terminal` | `url` | 在用户可见终端展示;不能声称桌面对话已收到图片。 |
170
- | `workbuddy` | `plain-chat` | `url` | `url` 是完整渲染的 HTML Card Link;直接发送/打开,不调用 `present_files`,然后停止。 |
170
+ | `workbuddy` | `plain-chat` | `url,agent_action` | 原样执行一次 `present_files(files=[url])`,在右侧打开完整渲染的 HTML Card Link,然后停止。 |
171
171
  | `kimi-code` | `terminal` | `url` | 使用标准 CLI 非 JSON 终端二维码和链接。 |
172
172
  | `openclaw` | 必须显式 | Telegram 返回 `url,qr_image_url,agent_action`;其他入口返回 `url,qr_image_url` | Telegram 执行原生 `message` action;其他入口直接展示图片和链接。 |
173
173
 
@@ -73,7 +73,7 @@ token 缺失或不匹配时使用本机句柄恢复。只有请求的 Checkout
73
73
  | `codex-cli` | `url`;普通文本模式渲染终端二维码。 |
74
74
  | `claude-code-desktop` | `url, qr_local_path, markdown`;原样发送 Markdown。 |
75
75
  | `claude-code-cli` | `url`;普通文本模式渲染终端二维码。 |
76
- | `workbuddy` | 只返回 `url`;它是完整渲染的 HTML Card Link。直接打开,不调用 `present_files`,不生成本地文件。 |
76
+ | `workbuddy` | 返回 `url,agent_action`;原样执行一次 `present_files(files=[url])` 打开完整渲染的 HTML Card Link,不生成本地文件。 |
77
77
  | `kimi-code` | `url`;普通文本模式渲染标准终端二维码。 |
78
78
  | `openclaw` | Telegram 为 `url,qr_image_url,agent_action`;instruction 强制原样执行 action。`📋 已授权给我读` callback 触发同一 Checkout 查询,再由 Backend 决定是否进入 grant 读取;其他显式 Host 为 `url,qr_image_url`。 |
79
79
 
@@ -117,7 +117,7 @@ OpenClaw 额外明确没有默认入口:
117
117
  | `codex-cli` | `terminal` | 只在用户可见终端展示付款交接。 |
118
118
  | `claude-code-desktop` | `claude-code` | 桌面对话必须实际展示二维码和付款链接。 |
119
119
  | `claude-code-cli` | `terminal` | 只在用户可见终端展示付款交接。 |
120
- | `workbuddy` | `plain-chat` | 会持久化 `~/.itpay-v3` 的 Bash 调用先设 Shell 工具参数 `dangerouslyDisableSandbox=true`;拒绝权限时停止。Checkout 只发送/打开渲染后的 Card Link,不调用 `present_files`。 |
120
+ | `workbuddy` | `plain-chat` | 会持久化 `~/.itpay-v3` 的 Bash 调用先设 Shell 工具参数 `dangerouslyDisableSandbox=true`;拒绝权限时停止。Checkout 原样执行返回的 `present_files` action,打开渲染后的 Card Link |
121
121
  | `kimi-code` | `terminal` | 使用标准 CLI 流程和终端展示,不增加 Kimi 专属命令。 |
122
122
  | `openclaw` | 无 | 必须显式提供当前入口;Telegram 使用原生 action,其他入口展示标准二维码和链接。 |
123
123
 
@@ -100,7 +100,7 @@ API 安全合同要求后端验证 display token 是该 Checkout 当前有效的
100
100
  | `codex-cli` | `terminal` | 只在用户可见终端展示渠道动作。 |
101
101
  | `claude-code-desktop` | `claude-code` | 把安全 handoff 发到当前桌面对话。 |
102
102
  | `claude-code-cli` | `terminal` | 只在用户可见终端展示渠道动作。 |
103
- | `workbuddy` | `plain-chat` | 受控逃生入口只返回一个可点击的 `handoff.url`;不得调用 `present_files`。展示后停止,不立即查询或创建替代付款。 |
103
+ | `workbuddy` | `plain-chat` | 受控逃生入口返回 `handoff.url` 和可原样执行的 `present_files` action;打开一次后停止,不立即查询或创建替代付款。 |
104
104
  | `kimi-code` | `terminal` | 使用标准 CLI 终端展示渠道动作。 |
105
105
  | `openclaw` | 必须显式提供 | 按显式 Host 返回安全渠道动作;IM Host 仍要求真实 target。 |
106
106
 
@@ -82,14 +82,14 @@ itpay services checkout <service_execution_id> --resume
82
82
  | `codex-cli` | `handoff={url}`;普通文本模式在用户可见终端渲染二维码。 |
83
83
  | `claude-code-desktop` | `handoff={url,qr_local_path,markdown}`;把 `handoff.markdown` 原样发送到当前桌面对话。 |
84
84
  | `claude-code-cli` | `handoff={url}`;普通文本模式在用户可见终端渲染二维码。 |
85
- | `workbuddy` | `handoff={url}`;`url` 是完整渲染的 HTML Card Link。直接发送/打开并停止,不得调用 `present_files`、检查或生成本地文件。 |
85
+ | `workbuddy` | `handoff={url,agent_action}`;原样执行一次 `present_files(files=[url])` 打开完整渲染的 HTML Card Link,然后停止;不得检查或生成本地文件。 |
86
86
  | `kimi-code` | `handoff={url}`;复用标准 CLI 终端展示。 |
87
87
  | `openclaw` | 必须显式传 Host;Telegram 还必须传 OpenClaw 原生 Target,并返回必须原样执行的 `message` action;其他入口返回标准 `url,qr_image_url`。 |
88
88
 
89
89
  WorkBuddy 的准确 instruction 语义必须完整包含:
90
90
 
91
91
  ```text
92
- Backend 尚未确认付款。向用户说明金额,直接发送并打开 handoff.url,然后停止等待。不要调用 present_files,不要检查本地文件,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。
92
+ Backend 尚未确认付款。立即严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行一次,在右侧打开 handoff.url;确认工具调用成功后说明金额,然后停止等待。若工具失败,只发送原始 handoff.url,报告未自动打开并停止。不要用 present_files 打开本地文件或二维码 PNG,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。
93
93
  ```
94
94
 
95
95
  `--locale` 默认 `zh-CN`,可显式使用 `--locale en`。语言只影响 Card 渲染,不改变 Checkout、付款或恢复状态。
@@ -213,7 +213,9 @@ bundle 不包含凭据、.env、~/.itpay-v3 或 npm token
213
213
 
214
214
  ### Step 4:CLI 发布后创建同步 PR
215
215
 
216
- CLI 主仓库在 `main` 提供 reusable workflow。各平台仓库每小时错峰运行 caller workflow,并可手动触发;npm `dist-tags.latest` 或请求的 bundle format 与当前 `bundle.lock.json` 不同时更新。平台仓库自身的 `GITHUB_TOKEN` 写入本仓库,因此不需要跨仓 PAT,也不会在 CLI 发布失败时提前同步未发布版本。
216
+ CLI 主仓库在 `main` 提供 reusable workflow。各平台仓库每小时错峰运行 caller workflow,并可手动触发;npm `dist-tags.latest` 或请求的 bundle format 与当前 `bundle.lock.json` 不同时更新。同步优先使用只安装到分发仓库、只拥有 Contents/Pull requests 写权限的 `itpay-bundle-sync` GitHub App 短期 token;未配置 App 时回落到平台仓库自己的 `GITHUB_TOKEN`,但该模式创建的 PR checks 需要仓库写权限用户批准。禁止使用个人 PAT。
217
+
218
+ 同步决策同时读取 main 和当前版本的 open automation PR。目标版本、format 和 bundle directory 已存在于 open PR 时必须返回 `pr-current`,不得每小时重建、提交或 force-push 同一产物。
217
219
 
218
220
  检测到新版本后:
219
221
 
@@ -221,8 +223,9 @@ CLI 主仓库在 `main` 提供 reusable workflow。各平台仓库每小时错
221
223
  - 更新 manifest 版本、lock、changelog;
222
224
  - 跑全套测试;
223
225
  - 对启用 Skill 差异跟踪的平台,比较旧、新 `sourceGitSha` 的中心 `skills/itpay/SKILL.md`;有差异时创建 Draft PR、附 diff 和人工合并清单,但不覆盖平台 Skill;
224
- - 创建或刷新 `automation/itpay-cli-X.Y.Z` 分支和同步 PR;
225
- - PR 描述列出 CLI commit、integrity、平台测试和是否需要商店重新审核。
226
+ - 创建或刷新 `automation/itpay-cli-X.Y.Z` 分支和同步 PR;同版本的后续计划任务必须为 no-op
227
+ - PR 描述列出 CLI commit、integrity、dependency lock、同步 run、平台测试和是否需要商店重新审核;
228
+ - 新版本 PR 验证成功后,只关闭没有人工提交的旧机器人同步 PR;保留远程分支用于审计和恢复。
226
229
 
227
230
  同步 workflow 只开 PR,不合并、不打 tag、不发布平台商店版本。
228
231
 
@@ -21,7 +21,7 @@
21
21
 
22
22
  ## CLI 更新机制
23
23
 
24
- 四个平台仓库每小时错峰检查 npm `@itpay/cli` 的正式版。发现版本高于各自 `bundle.lock.json` 后,调用 CLI `main` 上的统一 reusable workflow,重建对应格式的 bundle、运行仓库测试,并以平台仓库自己的 `GITHUB_TOKEN` 创建更新 PR。该流程不需要 PAT,也不会自动合并或发布商店版本。
24
+ 五个平台仓库每小时错峰检查 npm `@itpay/cli` 的正式版。发现版本高于各自 `bundle.lock.json` 后,调用 CLI `main` 上的统一 reusable workflow,重建对应格式的 bundle并运行仓库测试。更新 PR 优先由最小权限 `itpay-bundle-sync` GitHub App 创建;仓库未配置 App 时回落到 `GITHUB_TOKEN` 并明确要求人工批准 PR checks。该流程禁止个人 PAT,不会自动合并或发布商店版本,同一个 CLI 版本已有有效 open PR 时不会重复 force-push。
25
25
 
26
26
  ## 已发现的现有资产
27
27
 
@@ -0,0 +1,62 @@
1
+ # Bundle 同步运维与事故处理
2
+
3
+ ## 正常状态
4
+
5
+ 每次同步必须在 Job Summary 中给出 published version、automation branch、同步决策、凭据模式、PR URL 和关闭的 superseded PR 数量。
6
+
7
+ 同步决策只有以下正常结果:
8
+
9
+ - `base-current`:main 已经包含目标版本,无需更新。
10
+ - `pr-current`:当前版本的 open PR 已包含正确 bundle,不得产生新 commit。
11
+ - `update-required`:目标版本不存在,需要构建并创建 PR。
12
+ - `open-pr-artifact-missing`:PR 分支缺少 lock,需要重新构建。
13
+ - `open-pr-artifact-mismatch`:PR 中的 version、format 或 bundle directory 不符合 caller 合同,需要重新构建。
14
+
15
+ ## 失败分类
16
+
17
+ ### GitHub Actions 启动故障
18
+
19
+ 以下证据表示仓库代码没有运行:
20
+
21
+ - run 的 `steps` 为空;
22
+ - 失败发生在 `Set up job`;
23
+ - `Failed to resolve action download info`;
24
+ - Hosted Runner 长时间 queued 后被 cancelled 或 timed out。
25
+
26
+ 先查看 GitHub Status。官方确认恢复后重跑原 SHA;不得为了这种故障修改 bundle、测试或业务代码。
27
+
28
+ ### Bundle 构建或测试故障
29
+
30
+ 以下步骤失败才属于仓库或产物问题:
31
+
32
+ - Resolve published version
33
+ - Rebuild bundle
34
+ - Inspect upstream Skill changes
35
+ - Verify platform bundle
36
+
37
+ 保留失败分支和日志,不创建或更新 PR,不跳过 `npm test`。
38
+
39
+ ### `action_required`
40
+
41
+ 由 `GITHUB_TOKEN` 创建或更新的 PR workflow 会进入 approval-required 状态。这不是测试失败,但测试尚未执行。仓库写权限用户可以批准该 run;要求零人工同步时,必须配置最小权限 GitHub App,禁止使用个人 PAT。
42
+
43
+ ## 重跑规则
44
+
45
+ 1. 只重跑仍指向当前 head SHA 的基础设施失败。
46
+ 2. 新 commit 已存在时不重跑旧 SHA。
47
+ 3. 同一个版本第二次同步必须保持 automation PR head SHA 不变。
48
+ 4. 不把 cancelled、action-required 或旧 SHA 的 success 当作当前版本通过。
49
+
50
+ ## Superseded PR
51
+
52
+ 新版本 PR 建立并通过平台 `npm test` 后,可以关闭旧机器人同步 PR。自动关闭只允许处理带同步标记、分支符合 `automation/itpay-cli-X.Y.Z`、且所有提交均由 `itpay-bundle-bot` 产生的 PR。检测到人工提交时必须保留并在 summary 中提示。关闭时保留远程分支,不自动删除历史。
53
+
54
+ ## GitHub App 权限
55
+
56
+ `itpay-bundle-sync` 只安装到分发仓库,权限限定为:
57
+
58
+ - Metadata: read
59
+ - Contents: read/write
60
+ - Pull requests: read/write
61
+
62
+ App 不需要 Actions、Secrets、Administration、Deployments 或 Packages 写权限。私钥只存于组织 Actions secrets,通过 reusable workflow 生成当前仓库、当前 job 有效的短期 token。
@@ -16,6 +16,7 @@
16
16
  - [任务二:多平台 Bundle Skill 仓库与同步](./02-platform-bundle-repositories.md)
17
17
  - [各平台制作、验证和上传手册](./03-platform-publishing.md)
18
18
  - [首批平台执行状态(2026-07-22)](./04-first-wave-platforms.md)
19
+ - [Bundle 同步运维与事故处理](./05-sync-operations.md)
19
20
 
20
21
  ## 已确认的当前状态
21
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "2.0.25",
3
+ "version": "2.0.26",
4
4
  "description": "The single ItPay CLI entry point for buy workflows and future sell workflows.",
5
5
  "type": "module",
6
6
  "bin": {