@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,71 +0,0 @@
1
- export function locationConfirmationEnvelope(executionID, capabilityID, input, confirmation) {
2
- input = (confirmation.input ?? input);
3
- const endpoints = (confirmation.endpoints ?? []);
4
- const choices = Object.fromEntries(endpoints.map((endpoint) => [endpoint.side, "<用户选择的候选 id>"]));
5
- const missing = endpoints.filter((endpoint) => !endpoint.candidates?.length);
6
- const reason = missing[0]?.reason?.toUpperCase();
7
- const instruction = missing.length
8
- ? reason === "STATION_LOCATION_EVIDENCE_REQUIRED"
9
- ? "尚未查票。车站身份已知,但 Smart 缺少该站地理坐标证据;请说明此系统缺口。若用户仅要双站直达,可按原站名使用 Exact;不要要求用户换站。"
10
- : reason === "ADDRESS_REQUIRED" || reason === "ADMINISTRATIVE_AREA_UNRESOLVED"
11
- ? "尚未查票。先用已有上下文、地图或浏览补该端地点证据;含义仍不清且影响范围才问用户一个关键问题。保留另一端和原条件;只有返回完整新查询模板且已有新证据才另查,没有模板则停止,不猜旧执行输入。"
12
- : reason === "COORDINATE_IDENTITY_UNVERIFIED" || reason === "COORDINATE_NAME_MISMATCH"
13
- ? "尚未查票。提交的地点名称和坐标尚未得到一致证据;请核实该端点的真实名称或位置,保留另一端,不要把坐标声明当成人工确认。"
14
- : "尚未查票。地点证据不足;说明返回的原因与受影响端,先补真实证据,不改kind绕过、不把外部故障当用户歧义。当前无候选确认或完整新查询模板则停止。"
15
- : "尚未查票。仅向用户确认真实地点歧义,展示候选名称、地址和高德链接。具体住宅仍是目的地,附近车站只作候选;不得把选地点改成锁车站。用户选择后保留原输入,回到同一服务。";
16
- return {
17
- status: "location_confirmation_required",
18
- result: { service_execution_id: executionID, query: input, location_confirmation: confirmation },
19
- instruction,
20
- next: null,
21
- ...(confirmation.can_resume && !missing.length && endpoints.length ? {
22
- interaction: {
23
- schema_version: "itpay.interaction.v1",
24
- stage: "location_confirmation_required",
25
- input_template: {
26
- command: `itpay services invoke ${executionID} --capability ${capabilityID} --input-json <地点确认.json> --json`,
27
- input: { ...input, location_confirmation: { plan_id: confirmation.plan_id, token: confirmation.token, choices } },
28
- required_input: ["choices"],
29
- executable: false,
30
- },
31
- },
32
- } : {}),
33
- recovery: [],
34
- };
35
- }
36
- export function exactSmartTemplate(pair, executionID, resultItemID, publicBinding) {
37
- if (!pair || typeof pair !== "object")
38
- return null;
39
- const value = pair;
40
- if (!["origin_name", "origin_code", "destination_name", "destination_code", "travel_date"].every((key) => typeof value[key] === "string" && value[key]))
41
- return null;
42
- const input = { endpoints: { origin: { text: "<原始出发行政范围/地点/用户指定站>" }, destination: { text: "<原始到达行政范围/地点/用户指定站>" } }, travel_date: value.travel_date, passengers: "<真实人数>", reuse_from: { execution_id: executionID, ...(resultItemID ? { result_item_id: resultItemID } : {}) } };
43
- return { executable: false, verified_station_pair: value,
44
- instruction: "已核验站对是复用事实,不是完整用户查询。仅用户明确锁站才填 kind=station;区域/地点从原始需求填回,保留真实人数、时限和偏好,不默认为一人。",
45
- command: publicBinding ? `itpay services run ${publicBinding.public_service_id} --operation query_smart --input-json <原始需求.json> --json` : "itpay services run itpay-rail-smart --input-json <原始需求.json> --json",
46
- required_input: ["endpoints", "passengers", "用户时限与偏好"],
47
- input_example: publicBinding ? { input } : input };
48
- }
49
- export function exactTrainRow(candidate, executionID, itemID, offset) {
50
- return { ...compactExactTrain(candidate), result_item_id: itemID, result_offset: offset,
51
- detail: { command: `itpay services page ${executionID} ${itemID} --offset ${offset} --limit 1 --json`, reason: "读取此车次席别与服务端购买模板" } };
52
- }
53
- export function compactExactTrain(candidate) {
54
- const seats = Array.isArray(candidate.seats) ? candidate.seats : [];
55
- return {
56
- candidate_id: candidate.candidate_id,
57
- train_code: candidate.train_code,
58
- from_station: candidate.from_station,
59
- from_station_code: candidate.from_station_code,
60
- to_station: candidate.to_station,
61
- to_station_code: candidate.to_station_code,
62
- travel_date: candidate.travel_date,
63
- departure: candidate.departure,
64
- arrival: candidate.arrival,
65
- purchase_supported: candidate.purchase_supported ?? Boolean(candidate.booking_offer),
66
- seats: seats.map((seat) => ({ seat_type: seat.seat_type, seat_name: seat.seat_name,
67
- availability_text: seat.availability_text, remaining: seat.remaining,
68
- fare_minor: seat.fare_minor, quoted_total_minor: seat.quoted_total_minor,
69
- purchase_supported: seat.purchase_supported, requires_price_refresh: seat.requires_price_refresh })),
70
- };
71
- }
@@ -1,107 +0,0 @@
1
- import { writeCommandEnvelope, CommandContractError } from "../shared/envelope.js";
2
- import { writeSavedRailPage } from "./pagination.js";
3
- import { exactSmartTemplate, exactTrainRow } from "./query.js";
4
- import { compactRailJourney, compactRailTicketPlan } from "./results.js";
5
- export function renderExactRows(serviceExecutionID, itemID, binding, resolvedStationPair, rows, total, nextOffset, startOffset, limit, options) {
6
- writeSavedRailPage({
7
- status: "ready",
8
- result: { service_execution_id: serviceExecutionID, scope: "station_pair", resolved_station_pair: resolvedStationPair,
9
- total, offset: startOffset, count: rows.length, read_count: rows.length,
10
- ...(exactSmartTemplate(resolvedStationPair, serviceExecutionID, itemID, binding)
11
- ? { smart_template: exactSmartTemplate(resolvedStationPair, serviceExecutionID, itemID, binding) } : {}),
12
- trains: rows, ...(nextOffset !== null ? { next_offset: nextOffset } : {}) },
13
- instruction: "这是同一已保存站对的车次行。按用户时限、席别和人数筛选;零条仅代表这一站对。纯比较答复后结束;已有购票委托则执行所选行 detail.command 读取当前席别、库存和购买凭据。",
14
- next: nextOffset !== null ? { command: `itpay services read-result ${serviceExecutionID} --offset ${nextOffset} --limit ${limit} --json`, reason: "继续读取同一结果的剩余车次" } : null,
15
- recovery: [],
16
- }, { ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
17
- ...(options.output ? { output: options.output } : {}),
18
- all: Boolean(options.all),
19
- }, "trains", startOffset, next => ({ command: `itpay services read-result ${serviceExecutionID} --offset ${next} --limit ${limit} --json`, reason: "读取同一保存结果的下一页" }));
20
- }
21
- export function renderJourneyPage(serviceExecutionID, planID, queryRevision, total, qualificationCounts, limit, truncatedReason, offset, nextOffset, journeys, options) {
22
- writeSavedRailPage({
23
- status: "ready",
24
- result: {
25
- service_execution_id: serviceExecutionID,
26
- plan_id: planID,
27
- snapshot_id: options.snapshot,
28
- query_revision: queryRevision,
29
- total,
30
- qualification_counts: qualificationCounts,
31
- requested_limit: limit,
32
- truncated_reason: truncatedReason,
33
- offset,
34
- count: journeys.length,
35
- next_offset: nextOffset,
36
- journeys,
37
- },
38
- instruction: qualificationCounts === undefined
39
- ? "此历史快照没有完整资格计数;线路仅供核对铁路事实,不能据此认定可购买。沿同快照 detail.command 查看详情。"
40
- : qualificationCounts.eligible === 0
41
- ? `查到 ${total} 个时刻组合,当前 0 条满足购票资格。请读取诊断线路的 detail.command 查看具体原因;修改条件须依据用户意愿。`
42
- : `已查范围内合格时刻组合 ${qualificationCounts.eligible};本次展示 ${journeys.length} 个方案(首屏优先既有主方案/代表,计数不是中转通道数)。最快/低价须按对应方案和比较范围说明;未全范围排序只能说已读方案中。更多条件优先同执行 refine_preferences 复用已存事实;页大小受字节预算限制,每页可能不满,只沿 next_offset/next 读取,不计算偏移或无收益并行,不随机翻页、不无条件读完组合。未知费用不能当免费;detail.command 读取席别和整程购买条件,购买前实时报价。`,
43
- next: nextOffset === null ? null : { command: `itpay services read-result ${serviceExecutionID} --snapshot ${options.snapshot} --offset ${nextOffset} --limit ${limit} --json`, reason: "仅当前问题还需未读线路时继续;已有合格完整结果可答复或按委托继续" },
44
- recovery: [],
45
- }, {
46
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
47
- ...(options.output ? { output: options.output } : {}),
48
- all: Boolean(options.all),
49
- }, "journeys", offset, next => ({ command: `itpay services read-result ${serviceExecutionID} --snapshot ${options.snapshot} --offset ${next} --limit ${limit} --json`, reason: "仅当前问题还需未读线路时继续;已有合格完整结果可答复或按委托继续" }));
50
- }
51
- export function renderJourneyDetail(serviceExecutionID, detail, options) {
52
- const planOffset = options.offset ?? 0;
53
- const planLimit = options.limit ?? 3;
54
- if (!Number.isInteger(planOffset) || planOffset < 0 || !Number.isInteger(planLimit) || planLimit < 1 || planLimit > 20) {
55
- throw new CommandContractError("page_invalid", "offset must be non-negative and limit 1-20", "席别方案页码无效;请按返回的 next.command 读取。", []);
56
- }
57
- const plans = detail.journey.ticket_plans ?? [];
58
- const selected = options.all ? plans : plans.slice(planOffset, planOffset + planLimit);
59
- const nextOffset = !options.all && planOffset + selected.length < plans.length ? planOffset + selected.length : null;
60
- writeCommandEnvelope({
61
- status: "ready",
62
- result: {
63
- service_execution_id: serviceExecutionID,
64
- plan_id: detail.plan_id,
65
- snapshot_id: detail.snapshot_id,
66
- query_revision: detail.query_revision,
67
- journey: compactRailJourney(detail.journey, serviceExecutionID, detail.snapshot_id),
68
- ticket_plan_total: plans.length,
69
- ticket_plan_offset: planOffset,
70
- ticket_plans: selected.map(plan => compactRailTicketPlan(plan, detail.journey.qualification_status === "eligible")),
71
- },
72
- instruction: "同一线路的完整车次与席别方案。已有所需席别和有效凭据即可按真实委托继续;只需比较则答复并停止,需要其他席别才读下一页。使用所选 ticket_plan 的整程 booking_template:一个 booking、统一报价、一个官方付款入口,各段分别出票,无联合锁票或接续保证。未返回整程模板时不能拼不同方案的腿或退回逐腿付款,按同一执行恢复。身份资料只在受保护 Checkout 页面填写。",
73
- next: nextOffset === null ? null : { command: `itpay services read-result ${serviceExecutionID} --snapshot ${detail.snapshot_id} --journey ${options.journey} --offset ${nextOffset} --limit ${planLimit} --json`, reason: "仅需要其他席别时读取;已满足用户条件则停止翻页" },
74
- ...(detail.journey?.booking_support === "single_leg" ? { interaction: {
75
- schema_version: "itpay.interaction.v1", stage: "query_results_ready",
76
- select_user_chosen: `itpay services action ${serviceExecutionID} --action select_journey --actor-type human --status approved --input journey_id=${detail.journey.journey_id} --json`,
77
- select_delegated: `itpay services action ${serviceExecutionID} --action select_journey --actor-type agent --status approved --input journey_id=${detail.journey.journey_id} --input selection_mode=delegated --json`,
78
- } } : {}),
79
- recovery: [],
80
- }, {
81
- ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}),
82
- ...(options.output ? { output: options.output } : {}),
83
- });
84
- }
85
- export function exactSavedPage(response, executionID, itemID, offset) {
86
- const container = (response.page.result ?? response.page);
87
- const candidates = Array.isArray(container.candidates) ? container.candidates : [];
88
- const page = (container.catalog_page ?? {});
89
- return { rows: candidates.map((value, index) => exactTrainRow(value, executionID, itemID, offset + index)), resolvedStationPair: container.resolved_station_pair, total: typeof container.catalog_total === "number" ? container.catalog_total : undefined, nextOffset: typeof page.next_offset === "number" && page.next_offset > offset ? page.next_offset : null };
90
- }
91
- export function savedJourneyPage(response, executionID, snapshotID, offset) {
92
- const container = (response.page.result ?? response.page);
93
- const page = container.journey_page;
94
- const rows = Array.isArray(container.journeys) ? container.journeys : [];
95
- const next = page?.next_offset;
96
- if (typeof next === "number" && next <= offset)
97
- throw new Error("saved page cursor did not advance");
98
- return { total: typeof page?.total === "number" ? page.total : rows.length, qualificationCounts: page?.qualification_counts, truncatedReason: page?.truncated_reason, rows: rows.map(card => compactRailJourney(card, executionID, snapshotID)), nextOffset: typeof next === "number" ? next : null };
99
- }
100
- export function railSavedReadKind(execution) { return execution.service_id === "itpay-rail-exact" ? "exact" : execution.service_id === "itpay-rail-booking" ? "booking" : "planning"; }
101
- export function renderExactNoResult(preview, executionID, binding, options) {
102
- const template = exactSmartTemplate(preview?.resolved_station_pair, executionID, undefined, binding);
103
- if (!template || preview?.catalog_total !== 0)
104
- return false;
105
- writeCommandEnvelope({ status: "no_result", result: { service_execution_id: executionID, scope: "station_pair", resolved_station_pair: preview.resolved_station_pair, total: 0, smart_template: template }, instruction: "这一站对的成功观测为0条,不代表附近区域无车。若用户允许中转或原需求范围更广,保留原始区域/地点意图并沿 smart_template 引用本次事实;只要直达时不要自动转中转。", next: null, recovery: [] }, { ...(options.jsonOutput !== undefined ? { jsonOutput: options.jsonOutput } : {}), ...(options.output ? { output: options.output } : {}) });
106
- return true;
107
- }
@@ -1,125 +0,0 @@
1
- import { renderExactRows, renderJourneyPage, renderJourneyDetail, exactSavedPage, savedJourneyPage, railSavedReadKind, renderExactNoResult } from "./readers.js";
2
- import { HttpError } from "../../client/http.js";
3
- import { decodeRailCatalogJourneys } from "./catalog.js";
4
- import { publicPurchaseTemplate } from "../shared/platform.js";
5
- import { CommandContractError } from "../shared/envelope.js";
6
- export async function readSavedResult(backend, model, serviceExecutionID, options) {
7
- if (!options.snapshot && !options.journey) {
8
- if (railSavedReadKind(model.execution) === "exact") {
9
- const item = (model.current_result_items?.length ? model.current_result_items : model.result_items)?.[0];
10
- if (!item) {
11
- const preview = model.provider_invocations.at(-1)?.safe_result_preview;
12
- if (renderExactNoResult(preview, serviceExecutionID, model.execution.public_binding, options))
13
- return "handled";
14
- return "state";
15
- }
16
- const rows = [];
17
- const startOffset = options.offset ?? 0;
18
- const limit = options.limit ?? 20;
19
- if (!Number.isInteger(startOffset) || startOffset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 20) {
20
- throw new CommandContractError("page_invalid", "offset must be non-negative and limit 1-20", "offset 必须是非负整数,limit 为 1–20。保留同一已保存结果,按当前模板修正技术参数;不重新查询。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: `itpay services read-result ${serviceExecutionID} --offset <offset> --limit 20 --json`, required_input: ["offset"], executable: false } });
21
- }
22
- let offset = startOffset;
23
- let total = 0;
24
- let resolvedStationPair;
25
- let nextOffset = startOffset;
26
- do {
27
- const response = await backend.getServiceExecutionResultItemPage(serviceExecutionID, item.service_capability_result_item_id, offset, limit);
28
- const page = exactSavedPage(response, serviceExecutionID, item.service_capability_result_item_id, offset);
29
- resolvedStationPair ??= page.resolvedStationPair;
30
- rows.push(...page.rows);
31
- total = page.total ?? Math.max(total, rows.length);
32
- nextOffset = page.nextOffset;
33
- offset = nextOffset ?? offset;
34
- } while (options.all && nextOffset !== null);
35
- renderExactRows(serviceExecutionID, item.service_capability_result_item_id, model.execution.public_binding, resolvedStationPair, rows, total, nextOffset, startOffset, limit, options);
36
- return "handled";
37
- }
38
- }
39
- // rail.progressive.v2 planning read: --journey/--snapshot select committed
40
- // planning evidence — a free owner-validated read that never touches the
41
- // grant/Vault path. Without the selectors the authorized-delivery flow is
42
- // unchanged.
43
- // rail.planning catalog read: --snapshot returns compact rows for the
44
- // committed rail.catalog.v3; journey detail remains available separately.
45
- if (options.snapshot && !options.journey) {
46
- const offset = options.offset ?? 0;
47
- const limit = options.limit ?? 3;
48
- if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 20) {
49
- throw new CommandContractError("page_invalid", "offset must be non-negative and limit 1-20", "offset 必须是非负整数,limit 为 1–20。保留同一快照,按当前模板修正技术参数;不重新查询。", [], { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: { command: `itpay services read-result ${serviceExecutionID} --snapshot ${options.snapshot} --offset <offset> --limit 20 --json`, required_input: ["offset"], executable: false } });
50
- }
51
- const journeys = [];
52
- let nextOffset = offset;
53
- let total = 0;
54
- let truncatedReason = null;
55
- let qualificationCounts;
56
- let planID;
57
- let queryRevision;
58
- while (nextOffset !== null) {
59
- let response;
60
- try {
61
- response = await backend.getServiceExecutionResultItemPage(serviceExecutionID, options.snapshot, nextOffset, limit, "summary");
62
- }
63
- catch (error) {
64
- if (error instanceof HttpError && error.code === "result_not_pageable") {
65
- const committed = await backend.getRailPlanningCatalog(serviceExecutionID, options.snapshot);
66
- decodeRailCatalogJourneys(committed.catalog);
67
- }
68
- throw error;
69
- }
70
- if (response.snapshot_id !== options.snapshot)
71
- throw new Error("saved page snapshot changed");
72
- planID = response.plan_id;
73
- queryRevision = response.query_revision;
74
- const page = savedJourneyPage(response, serviceExecutionID, options.snapshot, nextOffset);
75
- total = page.total;
76
- qualificationCounts = page.qualificationCounts ?? qualificationCounts;
77
- truncatedReason = page.truncatedReason ?? truncatedReason;
78
- journeys.push(...page.rows);
79
- nextOffset = page.nextOffset;
80
- if (!options.all)
81
- break;
82
- }
83
- renderJourneyPage(serviceExecutionID, planID, queryRevision, total, qualificationCounts, limit, truncatedReason, offset, nextOffset, journeys, options);
84
- return "handled";
85
- }
86
- if (options.journey) {
87
- let detail = await backend.getRailJourneyDetail(serviceExecutionID, options.journey, options.snapshot);
88
- const validate = () => {
89
- if (!model.execution.public_binding || detail.journey.qualification_status !== "eligible")
90
- return;
91
- for (const plan of detail.journey.ticket_plans ?? []) {
92
- if (plan.qualification_status === "eligible" && plan.booking_input) {
93
- if (!plan.purchase_operation)
94
- throw new CommandContractError("public_operation_contract_invalid", "missing public purchase operation", "购买合同缺失", []);
95
- publicPurchaseTemplate(plan.purchase_operation);
96
- }
97
- }
98
- if (detail.journey.booking_support === "single_leg" && detail.journey.booking_offer) {
99
- if (!detail.journey.purchase_operation)
100
- throw new CommandContractError("public_operation_contract_invalid", "missing public purchase operation", "购买合同缺失", []);
101
- publicPurchaseTemplate(detail.journey.purchase_operation);
102
- }
103
- };
104
- try {
105
- validate();
106
- }
107
- catch (error) {
108
- if (!(error instanceof CommandContractError) || error.code !== "public_operation_contract_invalid")
109
- throw error;
110
- // One bounded owner-validated read, pinned to the returned snapshot. No provider or purchase call.
111
- detail = await backend.getRailJourneyDetail(serviceExecutionID, options.journey, detail.snapshot_id);
112
- try {
113
- validate();
114
- }
115
- catch (repeated) {
116
- if (!(repeated instanceof CommandContractError) || repeated.code !== "public_operation_contract_invalid")
117
- throw repeated;
118
- throw new CommandContractError(repeated.code, repeated.message, "同一详情只读重取一次后购买合同仍不完整,停止。保留原执行和快照,需平台修复;不要重查、换候选、拼凭据或退旧入口。", [], undefined, { reason: "purchase_contract_missing", service_execution_id: serviceExecutionID, snapshot_id: detail.snapshot_id, journey_id: options.journey, readonly_recovery_attempts: 1 });
119
- }
120
- }
121
- renderJourneyDetail(serviceExecutionID, detail, options);
122
- return "handled";
123
- }
124
- return undefined;
125
- }
@@ -1,51 +0,0 @@
1
- import { HttpError } from "../../client/http.js";
2
- import { CommandContractError } from "../shared/envelope.js";
3
- export const RAIL_LOCATION_EVIDENCE_REASONS = new Set(["endpoint_kind_conflict", "place_identity_uncertain", "endpoint_adcode_name_conflict", "endpoint_poi_name_conflict", "endpoint_station_name_code_conflict", "location_evidence_required", "coordinate_name_mismatch", "area_scope_too_broad", "address_required", "administrative_area_unresolved", "station_location_evidence_required", "coordinate_identity_unverified"]);
4
- export const RAIL_QUOTE_FAILURE_GUIDANCE = {
5
- quote_source_unavailable: "实时车票数据暂不可用;保留原选择,稍后按用户意愿重新核验,不创建替代订单。",
6
- quote_train_changed: "所选车次的时刻或运行信息已变化;向用户说明并征询是否重新查票。",
7
- quote_seat_changed: "所选席别已变化;向用户说明并征询新的席别选择。",
8
- quote_seat_unavailable: "所选席别当前无足够余票;告知用户并由其决定是否换方案。",
9
- quote_refresh_expired: "实时报价过程超时;保留原选择,不把旧价当可付款价格。",
10
- };
11
- export function railRunError(cause, serviceID, id) {
12
- if (cause instanceof HttpError && ["invalid_selection", "selection_wrong_buyer"].includes(cause.code ?? "")) {
13
- const reason = cause.payload?.reason;
14
- const inputInvalid = reason === "input_constraint_invalid";
15
- throw new CommandContractError(cause.code, cause.message, inputInvalid ? "输入违反当前完整计划约束。读取原查询的完整购买模板,修正人数、完整段数和顺序;不要追加未声明字段或拆单。"
16
- : cause.code === "selection_wrong_buyer" ? "当前身份无权使用这份购买凭据。停止;不要切换身份绕过,也不展示其他买家资料。"
17
- : reason === "stored_selection_inconsistent" ? "服务端凭据与已存目录不一致,补参数无法修复。停止购买并提供诊断标识 stored_selection_inconsistent;不猜字段、不重新创建购买。"
18
- : "购买凭据未通过校验,原因尚未确定且没有安全自动恢复。停止,不把推测报成原因或追加隐藏字段。", inputInvalid ? [{ command: "itpay services list --json", reason: "定位原查询并读取当前完整购买模板" }, { command: "itpay docs show rail-booking --json", reason: "读取正式输入约束" }] : [], undefined, { service_id: serviceID, ...(id ? { service_execution_id: id } : {}), ...(reason ? { reason } : {}), execution_created: Boolean(id) });
19
- }
20
- if (cause instanceof HttpError && cause.code === "rail_selection_expired") {
21
- const expiry = cause.payload?.selection_expiry;
22
- const bookingID = expiry?.booking_execution_id ?? id;
23
- const refreshInput = !bookingID && expiry?.safe_new_query && ["itpay-rail-smart", "itpay-rail-exact"].includes(expiry.query_service_id) ? expiry.query_input : undefined;
24
- const refresh = refreshInput !== undefined;
25
- throw new CommandContractError(cause.code, cause.message, refresh ? "刚才余票信息已过时,按原条件刷新一次真实库存(可能消耗查询额度);不需要重复批准同条件查票。有价格、库存或条款变化再确认;不复用旧库存、不换站或日期。"
26
- : bookingID ? "保留原购买执行,先读取订单与付款事实;不要重新查票、创建替代购买或重复付款。"
27
- : "旧选择凭据已过期,历史详情不能续期;缺少完整原查询条件或安全购买状态证据,停止自动刷新。", bookingID ? [{ command: `itpay services next ${bookingID} --json`, reason: "核对原购买执行和付款状态" }] : [], refreshInput && expiry ? { schema_version: "itpay.interaction.v1", stage: "input_required", input_template: {
28
- command: `itpay services run ${expiry.query_service_id} --input-json <file> --json`, input: refreshInput, required_input: ["file"], executable: false,
29
- } } : undefined, { service_id: serviceID, ...(bookingID ? { service_execution_id: bookingID } : {}), ...(expiry ? { selection_expiry: expiry } : {}) });
30
- }
31
- if (cause instanceof HttpError && cause.code === "rail_journey_selection_required") {
32
- throw new CommandContractError(cause.code, cause.message, "整程必须使用原查询当前完整 ticket plan 的 selections 模板;手填 legs 只兼容单腿。缺少整程模板时读取原方案;不拆单、不拼腿、不擅自换站或日期。", [{ command: "itpay services list --json", reason: "找回原查询并读取其完整方案模板" }, { command: "itpay docs show rail-booking --json", reason: "读取正式整程输入合同" }], undefined, { service_id: serviceID, ...(id ? { service_execution_id: id } : {}) });
33
- }
34
- if (cause instanceof HttpError && cause.code === "rail_seat_code_invalid" && id) {
35
- throw new CommandContractError(cause.code, cause.message, "席别必须使用原查询 booking_template.seat_choices 中与名称对应的代码,不能填“二等座”等显示名。用原 selection token 与真实人数修正输入,继续同一booking execution;若只是代码写法修正且车次、席别名称、人数等购买条件未变,沿用用户已给的确认,不再重复询问。", [{ command: `itpay services next ${id} --json`, reason: "读取同一booking当前状态后修正输入" }]);
36
- }
37
- }
38
- export function railActionError(error, serviceExecutionID, actionType) {
39
- if (error instanceof HttpError && ["booking_review_invalid", "booking_review_changed", "booking_review_locked"].includes(error.code)) {
40
- throw new CommandContractError(error.code, error.message, error.code === "booking_review_invalid"
41
- ? "确认输入与当前草稿不符。使用返回 interaction.input_template.input 当前review模板,席别使用返回的代码,不填显示名或猜新值。若仅修正代码写法,且车次、实际席别、人数、价格及条款等已确认内容未变,可沿用用户已有明确确认后提交;草稿实质变化或缺少真实同意时,先请用户确认变化内容。"
42
- : error.code === "booking_review_changed"
43
- ? "草稿版本已变化;使用同一booking的当前review模板。仅技术修正且车次、实际席别、人数、价格及条款未变时沿用已有明确同意;实质变化或缺少真实同意才补确认。"
44
- : "当前购买已有付款尝试;读取同一booking及订单状态,不创建新订单或重做付款。", error.payload?.review ? [] : [{ command: `itpay services next ${serviceExecutionID} --json`, reason: "读取同一booking的当前状态与review" }], error.payload?.review?.input_template ? {
45
- schema_version: "itpay.interaction.v1", stage: "consent_required",
46
- consent_rule: "没有用户明确条款回答或等价已存同意时保持 false,展示 review.consent_question 后等待。技术修正不产生同意;Agent提交是转述。",
47
- input_template: { command: `itpay services action ${serviceExecutionID} --action ${actionType} --actor-type agent --status approved --input-json <file> --json`, input: error.payload?.review.input_template, required_input: ["file"], executable: false },
48
- } : undefined, { service_execution_id: serviceExecutionID, review: error.payload?.review, input_errors: error.payload?.input_errors });
49
- }
50
- }
51
- export const railInputRecoveryInstruction = "Agent按当前模板自行修正错误字段和JSON层级,保留已有摘要的端点含义及原地点/日期/人数,不重复研究或事后补写冒充查询前摘要;不要向用户询问技术字段。只有真实业务歧义才问人;使用返回的执行身份恢复,未创建时用原服务。";