@itpay/cli 2.1.40 → 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.
Files changed (148) hide show
  1. package/README.md +5 -12
  2. package/dist/src/client/backend.js +2 -38
  3. package/dist/src/client/http.js +0 -2
  4. package/dist/src/commands/buy.js +58 -17
  5. package/dist/src/commands/cart.js +15 -17
  6. package/dist/src/commands/catalog.js +14 -39
  7. package/dist/src/commands/checkout.js +5 -146
  8. package/dist/src/commands/checkout_handoff.js +61 -11
  9. package/dist/src/commands/docs.js +18 -7
  10. package/dist/src/commands/guidance.js +134 -1
  11. package/dist/src/commands/install.js +3 -6
  12. package/dist/src/commands/order.js +3 -34
  13. package/dist/src/commands/orders.js +9 -4
  14. package/dist/src/commands/pay.js +1 -1
  15. package/dist/src/commands/readyz.js +1 -1
  16. package/dist/src/commands/refund.js +57 -13
  17. package/dist/src/commands/services.js +1068 -14
  18. package/dist/src/commands/skill.js +5 -8
  19. package/dist/src/commands/vault.js +20 -27
  20. package/dist/src/commands/vault_handoff.js +0 -18
  21. package/dist/src/main.js +43 -330
  22. package/dist/src/render/markdown.js +3 -0
  23. package/dist/src/render/qr.js +1 -1
  24. package/dist/src/state/cart_session.js +1 -4
  25. package/dist/src/state/client_context.js +0 -72
  26. package/dist/src/state/config.js +14 -44
  27. package/dist/src/state/device_authority.js +41 -141
  28. package/docs/agent/buyer/cart-checkout.json +6 -8
  29. package/docs/agent/buyer/catalog-list.json +9 -29
  30. package/docs/agent/buyer/identity-and-sessions.json +9 -26
  31. package/docs/agent/buyer/install-and-setup.json +9 -10
  32. package/docs/agent/buyer/orders-refunds.json +9 -25
  33. package/docs/agent/buyer/payment-flow.json +4 -6
  34. package/docs/agent/buyer/purchased-content.json +4 -18
  35. package/docs/agent/buyer/quickstart.json +17 -26
  36. package/docs/agent/buyer/render-hosts.json +12 -23
  37. package/docs/agent/buyer/service-feedback.json +3 -4
  38. package/docs/cli-reference/agent-types.md +2 -7
  39. package/docs/cli-reference/commands/buy.md +1 -2
  40. package/docs/cli-reference/commands/cart/add.md +1 -5
  41. package/docs/cli-reference/commands/cart/clear.md +1 -1
  42. package/docs/cli-reference/commands/cart/index.md +1 -1
  43. package/docs/cli-reference/commands/cart/next.md +1 -5
  44. package/docs/cli-reference/commands/cart/remove.md +1 -1
  45. package/docs/cli-reference/commands/cart/show.md +1 -1
  46. package/docs/cli-reference/commands/catalog/index.md +1 -1
  47. package/docs/cli-reference/commands/catalog/list.md +2 -2
  48. package/docs/cli-reference/commands/checkout.md +2 -12
  49. package/docs/cli-reference/commands/device.md +11 -18
  50. package/docs/cli-reference/commands/docs/index.md +1 -1
  51. package/docs/cli-reference/commands/docs/list.md +1 -1
  52. package/docs/cli-reference/commands/docs/search.md +1 -1
  53. package/docs/cli-reference/commands/docs/show.md +1 -1
  54. package/docs/cli-reference/commands/install.md +6 -8
  55. package/docs/cli-reference/commands/next.md +1 -1
  56. package/docs/cli-reference/commands/order.md +1 -1
  57. package/docs/cli-reference/commands/pay.md +1 -1
  58. package/docs/cli-reference/commands/readyz.md +11 -11
  59. package/docs/cli-reference/commands/refund/cancel.md +1 -1
  60. package/docs/cli-reference/commands/refund/create.md +1 -1
  61. package/docs/cli-reference/commands/refund/get.md +2 -2
  62. package/docs/cli-reference/commands/refund/index.md +1 -1
  63. package/docs/cli-reference/commands/refund/list.md +1 -1
  64. package/docs/cli-reference/commands/refund/watch.md +1 -1
  65. package/docs/cli-reference/commands/services/action.md +3 -24
  66. package/docs/cli-reference/commands/services/checkout.md +2 -17
  67. package/docs/cli-reference/commands/services/events.md +1 -1
  68. package/docs/cli-reference/commands/services/get.md +2 -4
  69. package/docs/cli-reference/commands/services/index.md +3 -4
  70. package/docs/cli-reference/commands/services/invoke.md +4 -6
  71. package/docs/cli-reference/commands/services/list.md +1 -1
  72. package/docs/cli-reference/commands/services/next.md +12 -78
  73. package/docs/cli-reference/commands/services/quote.md +4 -4
  74. package/docs/cli-reference/commands/services/read-result.md +6 -17
  75. package/docs/cli-reference/commands/services/start.md +3 -5
  76. package/docs/cli-reference/commands/vault/access.md +1 -5
  77. package/docs/cli-reference/conventions.md +6 -4
  78. package/docs/cli-reference/index.md +1 -10
  79. package/docs/skill-bundle-rollout/01-mcp-authentication.md +134 -0
  80. package/docs/skill-bundle-rollout/02-platform-bundle-repositories.md +210 -0
  81. package/docs/skill-bundle-rollout/03-platform-publishing.md +161 -0
  82. package/docs/skill-bundle-rollout/04-first-wave-platforms.md +69 -0
  83. package/docs/skill-bundle-rollout/05-sync-operations.md +62 -0
  84. package/docs/skill-bundle-rollout/README.md +151 -0
  85. package/package.json +12 -20
  86. package/skills/itpay/SKILL.md +102 -110
  87. package/assets/sell-preview/index.html +0 -6
  88. package/assets/sell-preview/preview.js +0 -9
  89. package/bin/darwin-amd64/itpay-sell +0 -0
  90. package/bin/darwin-arm64/itpay-sell +0 -0
  91. package/bin/linux-amd64/itpay-sell +0 -0
  92. package/bin/linux-arm64/itpay-sell +0 -0
  93. package/bin/windows-amd64/itpay-sell.exe +0 -0
  94. package/dist/src/commands/aftercare.js +0 -12
  95. package/dist/src/commands/presentation.js +0 -153
  96. package/dist/src/commands/rail_catalog.js +0 -1
  97. package/dist/src/commands/services/actions.js +0 -133
  98. package/dist/src/commands/services/commerce.js +0 -197
  99. package/dist/src/commands/services/lifecycle.js +0 -418
  100. package/dist/src/commands/services/results.js +0 -130
  101. package/dist/src/render/browser.js +0 -65
  102. package/dist/src/render/checkout_handoff.js +0 -101
  103. package/dist/src/render/checkout_plan.js +0 -47
  104. package/dist/src/sell/commands.js +0 -80
  105. package/dist/src/sell/contract.js +0 -86
  106. package/dist/src/sell/local.js +0 -260
  107. package/dist/src/sell/mcp.js +0 -49
  108. package/dist/src/sell/preview.js +0 -70
  109. package/dist/src/sell/sync.js +0 -145
  110. package/dist/src/services/adapter.js +0 -1
  111. package/dist/src/services/presentation.js +0 -198
  112. package/dist/src/services/rail/actions.js +0 -51
  113. package/dist/src/services/rail/adapter.js +0 -27
  114. package/dist/src/services/rail/aftercare.js +0 -45
  115. package/dist/src/services/rail/catalog.js +0 -261
  116. package/dist/src/services/rail/checkout.js +0 -19
  117. package/dist/src/services/rail/fulfillment.js +0 -76
  118. package/dist/src/services/rail/guide.js +0 -110
  119. package/dist/src/services/rail/invocation.js +0 -54
  120. package/dist/src/services/rail/page.js +0 -76
  121. package/dist/src/services/rail/pagination.js +0 -6
  122. package/dist/src/services/rail/query.js +0 -71
  123. package/dist/src/services/rail/readers.js +0 -107
  124. package/dist/src/services/rail/reading.js +0 -125
  125. package/dist/src/services/rail/recovery.js +0 -51
  126. package/dist/src/services/rail/results.js +0 -378
  127. package/dist/src/services/rail/review.js +0 -268
  128. package/dist/src/services/rail/saved.js +0 -49
  129. package/dist/src/services/rail/wait.js +0 -11
  130. package/dist/src/services/registry.js +0 -31
  131. package/dist/src/services/schema.js +0 -21
  132. package/dist/src/services/shared/envelope.js +0 -184
  133. package/dist/src/services/shared/input.js +0 -39
  134. package/dist/src/services/shared/pagination.js +0 -28
  135. package/dist/src/services/shared/platform.js +0 -365
  136. package/dist/src/services/shared/refund.js +0 -40
  137. package/dist/src/services/shared/workflow.js +0 -28
  138. package/dist/src/state/account_auth.js +0 -347
  139. package/dist/src/state/task_journal.js +0 -90
  140. package/docs/agent/buyer/rail-booking.json +0 -176
  141. package/docs/agent/buyer/rail-fast-checkout.json +0 -48
  142. package/docs/cli-reference/commands/aftercare.md +0 -15
  143. package/docs/cli-reference/commands/auth.md +0 -96
  144. package/docs/cli-reference/commands/sell.md +0 -1364
  145. package/docs/cli-reference/commands/services/page.md +0 -71
  146. package/docs/cli-reference/commands/services/run.md +0 -152
  147. package/docs/sell.md +0 -84
  148. /package/bin/{itp.mjs → itp} +0 -0
@@ -1,133 +0,0 @@
1
- import { serviceAdapter } from "../../services/registry.js";
2
- import { CommandContractError, writeCommandEnvelope } from "../../services/shared/envelope.js";
3
- import { normalizeServiceActionStatus, paidContinuation, serviceAllowedActionCommand, purchaseConfirmationInstruction, actionInputError } from "../../services/shared/platform.js";
4
- import { servicesNextEnvelope } from "../../services/presentation.js";
5
- export async function runServicesAction(backend, serviceExecutionID, actionType, input, options = {}) {
6
- const selection = await resolveCandidateSelection(backend, serviceExecutionID, actionType, options);
7
- const request = {
8
- action_type: actionType,
9
- input_snapshot: input,
10
- };
11
- if (options.actorType)
12
- request.actor_type = options.actorType;
13
- if (options.actorID)
14
- request.actor_id = options.actorID;
15
- if (options.status)
16
- request.status = normalizeServiceActionStatus(options.status, serviceExecutionID);
17
- const resultItemID = selection?.resultItemID ?? options.resultItemID;
18
- if (resultItemID)
19
- request.result_item_id = resultItemID;
20
- if (options.requiredBefore)
21
- request.required_before = options.requiredBefore;
22
- let response;
23
- try {
24
- response = await backend.recordServiceExecutionAction(serviceExecutionID, request);
25
- }
26
- catch (error) {
27
- // A failed write is never replayed. Only the locked execution identifies its adapter.
28
- try {
29
- const model = await backend.getServiceExecution(serviceExecutionID);
30
- serviceAdapter(model.execution).actionError(error, serviceExecutionID, actionType);
31
- }
32
- catch (recoveryError) {
33
- if (recoveryError instanceof CommandContractError)
34
- throw recoveryError;
35
- }
36
- throw error;
37
- }
38
- if (actionType === "approve_purchase" && "status" in response && response.status === "approved") {
39
- const quoteID = typeof input.service_quote_lock_id === "string" ? input.service_quote_lock_id : "";
40
- writeCommandEnvelope({
41
- status: "purchase_preparation_approved",
42
- result: { service_execution_id: serviceExecutionID, evidence_source: "agent_reported", scope: "prepare_checkout", service_quote_lock_id: quoteID },
43
- instruction: "已记录同范围的准备付款入口批准;这是Agent报告的真实会话同意,不是已付款或出票。继续使用本报价;条件未变不重复询问,官方页实际支付仍由用户完成。",
44
- next: quoteID ? { command: `itpay cart add --quote ${quoteID} --json`, reason: "加入本次已批准报价,准备同一个付款入口" } : null,
45
- recovery: [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取原执行与付款状态,不重新购买" }],
46
- }, { ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}), ...(options.output ? { output: options.output } : {}) });
47
- return;
48
- }
49
- if ("state" in response) {
50
- const updated = await backend.getServiceExecution(serviceExecutionID);
51
- const envelope = servicesNextEnvelope(updated);
52
- envelope.result = {
53
- ...envelope.result,
54
- planning_action: { action_id: response.action_id, state: response.state, replayed: response.replayed },
55
- };
56
- writeCommandEnvelope(envelope, {
57
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
58
- ...(options.output ? { output: options.output } : {}),
59
- });
60
- return;
61
- }
62
- if (selection && actionType === "select_candidate" && response.status === "approved") {
63
- const updated = await backend.getServiceExecution(serviceExecutionID);
64
- const preferred = updated.allowed_actions?.[0];
65
- const continuation = preferred?.type === "prepare_quote"
66
- ? paidContinuation(updated, preferred, {})
67
- : null;
68
- const next = continuation?.next ?? (preferred ? serviceAllowedActionCommand(updated, preferred) : null);
69
- writeCommandEnvelope({
70
- status: "candidate_selected",
71
- result: {
72
- service_execution_id: response.service_execution_id,
73
- candidate: { rank: selection.rank, title: selection.title },
74
- ...(continuation ? { checkout: continuation.checkout } : {}),
75
- },
76
- instruction: continuation
77
- ? purchaseConfirmationInstruction("candidate_selected", continuation.price, continuation.capability.delivery_email_required, continuation.capability.delivery_email_purpose, selection.title)
78
- : "候选已绑定到来源 Execution;后续动作必须继续使用该 Execution。",
79
- next,
80
- recovery: [{
81
- command: `itpay services next ${response.service_execution_id} --json`,
82
- reason: "重新读取服务端允许的动作",
83
- }],
84
- }, {
85
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
86
- ...(options.output ? { output: options.output } : {}),
87
- });
88
- return;
89
- }
90
- writeCommandEnvelope({
91
- status: "action_recorded",
92
- result: {
93
- service_execution_id: response.service_execution_id,
94
- action_type: response.action_type,
95
- action_status: response.status,
96
- evidence_source: "agent_reported",
97
- reported_actor_type: response.actor_type,
98
- },
99
- instruction: "动作已记录(Agent转述;actor_type并非官方页面真人确认的证明)。读取服务端计算的新状态;不要自行假设下一 capability。",
100
- next: {
101
- command: `itpay services next ${response.service_execution_id} --json`,
102
- reason: "取得更新后的首选动作",
103
- },
104
- recovery: [],
105
- }, {
106
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
107
- ...(options.output ? { output: options.output } : {}),
108
- });
109
- }
110
- export async function resolveCandidateSelection(backend, serviceExecutionID, actionType, options) {
111
- if (options.candidateRank === undefined)
112
- return undefined;
113
- if (actionType !== "select_candidate") {
114
- throw actionInputError(serviceExecutionID, "--candidate is only valid with --action select_candidate");
115
- }
116
- if (options.resultItemID) {
117
- throw actionInputError(serviceExecutionID, "--candidate cannot be combined with --result-item");
118
- }
119
- if (!Number.isInteger(options.candidateRank) || options.candidateRank < 1) {
120
- throw actionInputError(serviceExecutionID, "--candidate must be a positive integer result rank");
121
- }
122
- const execution = await backend.getServiceExecution(serviceExecutionID);
123
- const currentItems = execution.current_result_items ?? [];
124
- const result = currentItems.find((item) => item.rank === options.candidateRank);
125
- if (!result) {
126
- throw actionInputError(serviceExecutionID, `candidate ${options.candidateRank} is not available on service execution ${serviceExecutionID}`, "candidate_not_found");
127
- }
128
- return {
129
- resultItemID: result.service_capability_result_item_id,
130
- rank: result.rank,
131
- title: result.display_title,
132
- };
133
- }
@@ -1,197 +0,0 @@
1
- import { assertCheckoutPayable } from "../checkout_handoff.js";
2
- import { serviceAdapter } from "../../services/registry.js";
3
- import { HttpError } from "../../client/http.js";
4
- import { validateContext } from "../../state/client_context.js";
5
- import { dispatchRender } from "../../render/index.js";
6
- import { ensureIdeImageAttach } from "../../render/ide.js";
7
- import { shouldPrepareLocalCheckoutImage } from "../../render/checkout_handoff.js";
8
- import { localizeCardURL, normalizeCardLocale } from "../../render/locale.js";
9
- import { platformKeyForHost } from "../../render/plan.js";
10
- import { renderTerminalQR } from "../../render/qr.js";
11
- import { buildCheckoutQRPlan } from "../../render/checkout_plan.js";
12
- import { CommandContractError, writeCommandEnvelope, plainValueLines } from "../../services/shared/envelope.js";
13
- import { missingRequiredInput, checkoutCommand, formatInputOptions, tokenizedCheckoutURL, absolutePublicURL, fallbackCardURL, fallbackCardPNGURL, buildServicesCheckoutEnvelope, quoteCommand, formatMoney } from "../../services/shared/platform.js";
14
- export async function runServicesCheckout(backend, config, serviceExecutionID, capabilityID, options = {}) {
15
- const host = options.host ?? "terminal";
16
- const contextError = validateContext(host, options.target);
17
- if (contextError) {
18
- throw new CommandContractError(contextError.code, contextError.message, "从当前可信会话上下文补齐 Host/target;本次未创建 Checkout。", []);
19
- }
20
- const deliveryContact = {
21
- ...(options.deliveryContact ?? {}),
22
- ...(options.email ? { email: options.email } : {}),
23
- };
24
- if (!options.resume && !capabilityID) {
25
- const model = await backend.getServiceExecution(serviceExecutionID);
26
- capabilityID = model.workflow_entry?.capability_id;
27
- }
28
- if (!options.resume && !capabilityID) {
29
- throw new CommandContractError("capability_required", "--capability is required when creating a service checkout", "使用当前 Service Execution 返回的付费 capability;恢复已有 Checkout 时改用 --resume。", [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取当前允许的付费 capability" }]);
30
- }
31
- if (!options.resume) {
32
- const readModel = await backend.getServiceExecution(serviceExecutionID);
33
- const capability = readModel.capabilities.find((item) => item.capability_id === capabilityID);
34
- if (!capability || !capability.requires_payment) {
35
- throw new CommandContractError("capability_not_checkoutable", `capability ${capabilityID} is not available for checkout on service execution ${serviceExecutionID}`, "只为当前 Service Execution 返回的 requires_payment capability 创建 Checkout。", [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取当前允许的下一步" }]);
36
- }
37
- if (readModel.checkout_bindings.length === 0 && !readModel.actions.some(action => action.action_type === "approve_purchase" && action.status === "approved")) {
38
- await runServicesQuote(backend, serviceExecutionID, capability.capability_id, options.lockedInput ?? {}, options);
39
- return;
40
- }
41
- const lockedInput = options.lockedInput ?? {};
42
- const missingInput = missingRequiredInput(capability.input_schema, lockedInput);
43
- if (missingInput.length > 0 && !readModel.workflow_entry && readModel.execution.next_action !== "create_checkout") {
44
- throw new CommandContractError("capability_input_invalid", `missing required capability input: ${missingInput.join(", ")}`, "补齐付费 capability 的 required_input;本次没有创建 quote、Checkout 或订单。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: checkoutCommand(serviceExecutionID, capability, lockedInput), required_input: missingInput, executable: false } });
45
- }
46
- if (capability.delivery_email_required && readModel.workflow_entry?.capability_id !== capability.capability_id && String(deliveryContact.email ?? "").trim() === "") {
47
- throw new CommandContractError("delivery_email_required", "delivery email is required before creating this service checkout", "该 capability 的交付链接会发送到用户邮箱;先向用户说明用途并询问邮箱,不要代填。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: `itpay services checkout ${serviceExecutionID} --capability ${capability.capability_id}${formatInputOptions(lockedInput)} --email <email> --json`, required_input: ["email"], executable: false } });
48
- }
49
- }
50
- let response;
51
- try {
52
- response = await backend.createServiceExecutionCheckout(serviceExecutionID, {
53
- ...(capabilityID ? { capability_id: capabilityID } : {}),
54
- ...(Object.keys(deliveryContact).length > 0 ? { delivery_contact: deliveryContact } : {}),
55
- ...(options.lockedInput && Object.keys(options.lockedInput).length > 0 ? { locked_input: options.lockedInput } : {}),
56
- ...(options.resume ? { resume: true } : {}),
57
- });
58
- }
59
- catch (error) {
60
- if (error instanceof HttpError && error.code === "purchase_approval_required") {
61
- throw new CommandContractError(error.code, error.message, "先读当前报价及完整范围模板。沿用覆盖同条件的真实批准;缺少哪部分才补哪部分,不把候选选择当作价格同意,也不代付。", [{ command: capabilityID ? `itpay services quote ${serviceExecutionID} --capability ${capabilityID}${formatInputOptions(options.lockedInput ?? {})} --json` : `itpay services next ${serviceExecutionID} --json`, reason: "读取同一执行的当前批准范围,不新建购买" }]);
62
- }
63
- throw error;
64
- }
65
- const checkout = response.checkout;
66
- const authoritative = checkout.display_token ? await backend.getCheckoutPresentation(checkout.checkout.checkout_id, checkout.display_token) : undefined;
67
- assertCheckoutPayable(checkout, authoritative, response.cart.cart_id, serviceExecutionID);
68
- if (authoritative)
69
- response.cart.items = authoritative.items;
70
- const checkoutID = checkout.checkout.checkout_id;
71
- const displayToken = checkout.display_token;
72
- const locale = normalizeCardLocale(options.locale);
73
- const checkoutURL = tokenizedCheckoutURL(checkout.checkout_url, displayToken, checkout.qr_payload);
74
- const cardURL = localizeCardURL(absolutePublicURL(config.baseURL, checkout.card_url ?? fallbackCardURL(config.baseURL, checkoutID, displayToken)), locale);
75
- const cardPNGURL = localizeCardURL(absolutePublicURL(config.baseURL, checkout.card_png_url ?? checkout.qr_png_url ?? fallbackCardPNGURL(config.baseURL, checkoutID, displayToken)), locale);
76
- let itineraryTitle;
77
- let itinerary;
78
- try {
79
- const booking = await backend.getServiceExecution(serviceExecutionID);
80
- ({ itinerary, itineraryTitle } = await serviceAdapter(booking.execution).checkout(backend, booking, checkoutID, displayToken));
81
- }
82
- catch {
83
- // Checkout already exists; a display-only title lookup cannot hide its official handoff.
84
- }
85
- const plan = buildCheckoutQRPlan({
86
- localSimulation: backend.localSimulation,
87
- host,
88
- checkoutID,
89
- checkoutURL,
90
- cardURL,
91
- displayToken,
92
- qrPayload: checkout.qr_payload,
93
- qrPNGURL: cardPNGURL,
94
- nextAction: checkout.checkout.next_action,
95
- orderItems: response.cart.items.map((item) => ({
96
- title: itineraryTitle || item.title,
97
- quantity: item.quantity,
98
- amountMinor: item.amount_minor,
99
- currency: item.currency,
100
- })),
101
- orderCurrency: checkout.checkout.currency,
102
- ...(options.agentType ? { agentType: options.agentType } : {}),
103
- locale,
104
- });
105
- options.persistHandoff?.({
106
- serviceExecutionID,
107
- cartID: response.cart.cart_id,
108
- checkoutID,
109
- displayToken,
110
- checkoutURL,
111
- });
112
- const platform = platformKeyForHost(plan.host);
113
- if (!options.jsonOutput && (platform === "telegram" || platform === "feishu" || platform === "lark")) {
114
- await dispatchRender(plan, {
115
- host,
116
- ...(options.target ? { target: options.target } : {}),
117
- ...(options.qrFormat ? { qrFormat: options.qrFormat } : {}),
118
- ...(options.qrFilePath ? { qrFilePath: options.qrFilePath } : {}),
119
- ...(options.output ? { output: options.output } : {}),
120
- ...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
121
- baseURL: config.baseURL,
122
- });
123
- return;
124
- }
125
- if (shouldPrepareLocalCheckoutImage(platform)) {
126
- await ensureIdeImageAttach(plan, {
127
- enabled: config.ideImageAttach,
128
- ...(config.baseURL ? { baseURL: config.baseURL } : {}),
129
- ...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),
130
- });
131
- }
132
- const envelope = buildServicesCheckoutEnvelope(response, checkoutURL, plan, options.agentType, options.target);
133
- if (itinerary) {
134
- envelope.result = { ...envelope.result, itinerary };
135
- envelope.instruction = "整程一次付款,各段分别出票;展示入口不等于付款,付款成功不等于全部出票。" + envelope.instruction;
136
- }
137
- const plainResult = plainValueLines(envelope.result);
138
- if (!options.jsonOutput && platform === "terminal") {
139
- plainResult.push("qr:", await renderTerminalQR(checkoutURL, options.qrFormat ?? "terminal"));
140
- }
141
- writeCommandEnvelope(envelope, {
142
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
143
- ...(options.output ? { output: options.output } : {}),
144
- plainResult,
145
- });
146
- }
147
- export async function runServicesQuote(backend, serviceExecutionID, capabilityID, input, options = {}) {
148
- const model = await backend.getServiceExecution(serviceExecutionID);
149
- const capability = model.capabilities.find((item) => item.capability_id === capabilityID);
150
- if (!capability || !capability.requires_payment) {
151
- throw new CommandContractError("capability_not_quoteable", `capability ${capabilityID} is not available for quote on service execution ${serviceExecutionID}`, "只为当前 Service Execution 返回的付费 capability 创建报价。", [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取当前合法动作" }]);
152
- }
153
- const selectionBacked = model.execution.status === "human_action_approved" &&
154
- model.allowed_actions?.some((action) => action.type === "prepare_quote" && action.capability_id === capabilityID);
155
- const missingInput = missingRequiredInput(capability.input_schema, input);
156
- if (missingInput.length > 0 && !selectionBacked && model.workflow_entry?.capability_id !== capability.capability_id) {
157
- throw new CommandContractError("capability_input_invalid", `missing required capability input: ${missingInput.join(", ")}`, "补齐付费 capability 输入;本次没有创建 Quote、Cart 或 Checkout。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: quoteCommand(serviceExecutionID, capability, input), required_input: missingInput, executable: false } });
158
- }
159
- const deliveryContact = {
160
- ...(options.deliveryContact ?? {}),
161
- ...(options.email ? { email: options.email } : {}),
162
- };
163
- if (capability.delivery_email_required && model.workflow_entry?.capability_id !== capability.capability_id && String(deliveryContact.email ?? "").trim() === "") {
164
- throw new CommandContractError("delivery_email_required", "delivery email is required before preparing this service quote", "交付链接会发送到用户邮箱;说明用途并询问邮箱,不要代填。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: quoteCommand(serviceExecutionID, capability, input), required_input: ["email"], executable: false } });
165
- }
166
- const quote = await backend.prepareServiceQuote(serviceExecutionID, {
167
- capability_id: capabilityID,
168
- ...(Object.keys(deliveryContact).length > 0 ? { delivery_contact: deliveryContact } : {}),
169
- ...(Object.keys(input).length > 0 ? { locked_input: input } : {}),
170
- });
171
- const result = {
172
- service_quote_lock_id: quote.service_quote_lock_id,
173
- service_execution_id: quote.service_execution_id,
174
- capability_id: quote.capability_id,
175
- price: formatMoney(quote.amount_minor, quote.currency),
176
- ...(quote.purchase_summary ? { purchase_summary: quote.purchase_summary } : {}),
177
- expires_at: quote.expires_at,
178
- ...(quote.approval_template ? { approval_template: quote.approval_template } : {}),
179
- };
180
- writeCommandEnvelope({
181
- status: "quote_ready",
182
- result,
183
- instruction: quote.approval_template
184
- ? "报价已锁定。先展示服务端商品名称、对象、范围、数量和金额;与用户请求不符时让用户决定,不自称基础报告或换商品。选择候选不是价格同意;模板中的user_approved=false表示尚未记录批准。仅在用户已有真实批准覆盖本范围时改为true并提交,不重复询问同条件已明确同意。用户授权预算时用capped及真实maximum_minor;仅委托准备入口、最终金额在官方页确认时用confirm_on_official_page。不要伪造接受未知金额或条款。"
185
- : "报价已锁定当前 Execution 的可信输入和价格;可单独付款,也可与其他独立 Execution 的报价合并。",
186
- ...(quote.approval_template ? { interaction: { schema_version: "itpay.interaction.v1", stage: "purchase_approval_required", input_template: { command: `itpay services action ${serviceExecutionID} --action approve_purchase --actor-type agent --status approved --input-json <file> --json`, input: quote.approval_template, required_input: ["file"], executable: false } } } : {}),
187
- next: quote.approval_template ? null : {
188
- command: `itpay cart add --quote ${quote.service_quote_lock_id} --json`,
189
- reason: "加入 canonical Cart",
190
- },
191
- recovery: [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "重新读取当前 Execution 状态" }],
192
- }, {
193
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
194
- ...(options.output ? { output: options.output } : {}),
195
- plainResult: plainValueLines(result),
196
- });
197
- }