@alipay/alipay-aipay 1.7.0 → 1.7.2

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 (169) hide show
  1. package/README.md +30 -0
  2. package/dist/commands/install-current.js +15 -7
  3. package/dist/commands/install.js +20 -16
  4. package/dist/commands/uninstall.js +8 -3
  5. package/dist/commands/update.js +34 -16
  6. package/dist/index.js +10 -4
  7. package/dist/utils/channels.js +67 -0
  8. package/dist/utils/config.js +15 -11
  9. package/dist/utils/file.js +23 -10
  10. package/dist/utils/npm.js +19 -33
  11. package/package.json +8 -3
  12. package/skill/SKILL.md +2 -0
  13. package/skill/references/normal/json/release-channel.json +1 -0
  14. package/skill/references/normal/scripts/self_update.mjs +62 -17
  15. package/skill/references/normal/self-update.md +3 -1
  16. package/skill-pre/SKILL.md +65 -0
  17. package/skill-pre/VERSION +1 -0
  18. package/skill-pre/references/integration/flow.md +602 -0
  19. package/skill-pre/references/integration/modules/alipay-sdk-reminder.md +281 -0
  20. package/skill-pre/references/integration/modules/checklist.md +154 -0
  21. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +199 -0
  22. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +68 -0
  23. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +83 -0
  24. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +80 -0
  25. package/skill-pre/references/integration/modules/code-examples/csharp/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +73 -0
  26. package/skill-pre/references/integration/modules/code-examples/csharp/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +62 -0
  27. package/skill-pre/references/integration/modules/code-examples/csharp/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +132 -0
  28. package/skill-pre/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +640 -0
  29. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +184 -0
  30. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +72 -0
  31. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +96 -0
  32. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +81 -0
  33. package/skill-pre/references/integration/modules/code-examples/java/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +77 -0
  34. package/skill-pre/references/integration/modules/code-examples/java/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +69 -0
  35. package/skill-pre/references/integration/modules/code-examples/java/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +138 -0
  36. package/skill-pre/references/integration/modules/code-examples/java/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemoController.md +623 -0
  37. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +135 -0
  38. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +51 -0
  39. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +63 -0
  40. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
  41. package/skill-pre/references/integration/modules/code-examples/nodejs/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +54 -0
  42. package/skill-pre/references/integration/modules/code-examples/nodejs/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +84 -0
  43. package/skill-pre/references/integration/modules/code-examples/nodejs/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +71 -0
  44. package/skill-pre/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +498 -0
  45. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +157 -0
  46. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
  47. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +73 -0
  48. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +69 -0
  49. package/skill-pre/references/integration/modules/code-examples/php/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
  50. package/skill-pre/references/integration/modules/code-examples/php/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +59 -0
  51. package/skill-pre/references/integration/modules/code-examples/php/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +110 -0
  52. package/skill-pre/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +806 -0
  53. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//345/274/202/346/255/245/351/200/232/347/237/245/345/244/204/347/220/206/344/273/243/347/240/201/347/244/272/344/276/213.md +128 -0
  54. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/345/205/263/351/227/255/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +57 -0
  55. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +70 -0
  56. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +65 -0
  57. package/skill-pre/references/integration/modules/code-examples/python/1-/351/200/232/347/224/250/346/216/245/345/217/243//347/273/237/344/270/200/346/224/266/345/215/225/344/272/244/346/230/223/351/200/200/346/254/276/346/237/245/350/257/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +62 -0
  58. package/skill-pre/references/integration/modules/code-examples/python/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +51 -0
  59. package/skill-pre/references/integration/modules/code-examples/python/3-APP/346/224/257/344/273/230/APP/346/224/257/344/273/230/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +47 -0
  60. package/skill-pre/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.md +541 -0
  61. package/skill-pre/references/integration/modules/interface-contract/aipay-interface-contract.md +52 -0
  62. package/skill-pre/references/integration/modules/interface-contract/apppay-interface-contract.md +45 -0
  63. package/skill-pre/references/integration/modules/interface-contract/common-trade-contract.md +42 -0
  64. package/skill-pre/references/integration/modules/interface-contract/notify-url-contract.md +68 -0
  65. package/skill-pre/references/integration/modules/interface-contract/webpay-interface-contract.md +44 -0
  66. package/skill-pre/references/integration/modules/interface-guide.md +61 -0
  67. package/skill-pre/references/integration/modules/product-decision.md +73 -0
  68. package/skill-pre/references/integration/modules/sandbox/a2m-sandbox-test.md +235 -0
  69. package/skill-pre/references/integration/modules/sandbox/alipay-sandbox-tool.md +142 -0
  70. package/skill-pre/references/integration/modules/sandbox/sandbox-setup-guide.md +239 -0
  71. package/skill-pre/references/integration/modules/scripts/local_402_sandbox_pay.py +900 -0
  72. package/skill-pre/references/integration/modules/scripts/sandbox_config.sh +347 -0
  73. package/skill-pre/references/integration/modules/troubleshooting-flow.md +28 -0
  74. package/skill-pre/references/normal/alipay-cli-env.md +169 -0
  75. package/skill-pre/references/normal/conversation-telemetry.md +101 -0
  76. package/skill-pre/references/normal/full-process-routing.md +92 -0
  77. package/skill-pre/references/normal/integration-worker.md +90 -0
  78. package/skill-pre/references/normal/json/customer-messages.json +1 -0
  79. package/skill-pre/references/normal/json/managed-jq-assets.json +1 -0
  80. package/skill-pre/references/normal/json/policy-rules.json +1 -0
  81. package/skill-pre/references/normal/json/release-channel.json +1 -0
  82. package/skill-pre/references/normal/rejection-guide.md +41 -0
  83. package/skill-pre/references/normal/scripts/agent_platform.mjs +43 -0
  84. package/skill-pre/references/normal/scripts/alipay_cli_env.mjs +34 -0
  85. package/skill-pre/references/normal/scripts/alipay_cli_env.sh +55 -0
  86. package/skill-pre/references/normal/scripts/alipay_cli_refresh.mjs +129 -0
  87. package/skill-pre/references/normal/scripts/auth_runtime_runner.mjs +60 -0
  88. package/skill-pre/references/normal/scripts/checklist_sections.mjs +43 -0
  89. package/skill-pre/references/normal/scripts/common.sh +365 -0
  90. package/skill-pre/references/normal/scripts/conversation_telemetry.mjs +105 -0
  91. package/skill-pre/references/normal/scripts/detect_dev_tool.sh +79 -0
  92. package/skill-pre/references/normal/scripts/flow_loader.mjs +237 -0
  93. package/skill-pre/references/normal/scripts/integration_context_runner.mjs +151 -0
  94. package/skill-pre/references/normal/scripts/integration_message_runner.mjs +1456 -0
  95. package/skill-pre/references/normal/scripts/interaction_reply.mjs +140 -0
  96. package/skill-pre/references/normal/scripts/managed_jq.mjs +215 -0
  97. package/skill-pre/references/normal/scripts/onboarding_discovery_runner.mjs +387 -0
  98. package/skill-pre/references/normal/scripts/onboarding_message_runner.mjs +1425 -0
  99. package/skill-pre/references/normal/scripts/onboarding_recovery_runner.mjs +100 -0
  100. package/skill-pre/references/normal/scripts/open_official_url.sh +205 -0
  101. package/skill-pre/references/normal/scripts/platform_compat.mjs +42 -0
  102. package/skill-pre/references/normal/scripts/preview_runtime.mjs +151 -0
  103. package/skill-pre/references/normal/scripts/project_route_inspector.sh +132 -0
  104. package/skill-pre/references/normal/scripts/python_script_runner.mjs +58 -0
  105. package/skill-pre/references/normal/scripts/render_customer_message.mjs +1792 -0
  106. package/skill-pre/references/normal/scripts/runtime.mjs +271 -0
  107. package/skill-pre/references/normal/scripts/self_update.mjs +376 -0
  108. package/skill-pre/references/normal/scripts/telemetry.mjs +2102 -0
  109. package/skill-pre/references/normal/self-update.md +7 -0
  110. package/skill-pre/references/normal/telemetry.md +70 -0
  111. package/skill-pre/references/onboarding/assets/keygen.html +927 -0
  112. package/skill-pre/references/onboarding/flow.md +607 -0
  113. package/skill-pre/references/onboarding/modules/app-release.md +90 -0
  114. package/skill-pre/references/onboarding/modules/authorization.md +43 -0
  115. package/skill-pre/references/onboarding/modules/error-handling.md +62 -0
  116. package/skill-pre/references/onboarding/modules/product-sign.md +49 -0
  117. package/skill-pre/references/onboarding/modules/scripts/app.sh +1485 -0
  118. package/skill-pre/references/onboarding/modules/scripts/ar_sign_apply.sh +331 -0
  119. package/skill-pre/references/onboarding/modules/scripts/auth.sh +1040 -0
  120. package/skill-pre/references/onboarding/modules/scripts/download_key_tool.sh +4 -0
  121. package/skill-pre/references/onboarding/modules/scripts/error_handler.sh +841 -0
  122. package/skill-pre/references/onboarding/modules/scripts/mcc.sh +1085 -0
  123. package/skill-pre/references/onboarding/modules/scripts/network_retry.sh +135 -0
  124. package/skill-pre/references/onboarding/modules/scripts/query_sign_status.sh +105 -0
  125. package/skill-pre/references/onboarding/modules/scripts/service.sh +528 -0
  126. package/skill-pre/references/onboarding/modules/scripts/sign_status_parser.sh +58 -0
  127. package/skill-pre/references/onboarding/modules/scripts/upload_screenshots.sh +208 -0
  128. package/skill-pre/references/onboarding/modules/scripts/upload_special_license.sh +195 -0
  129. package/skill-pre/references/onboarding/modules/service-registration.md +40 -0
  130. package/skill-pre/references/runtime-entry.md +99 -0
  131. package/dist/commands/install-current.d.ts +0 -4
  132. package/dist/commands/install-current.d.ts.map +0 -1
  133. package/dist/commands/install-current.js.map +0 -1
  134. package/dist/commands/install.d.ts +0 -10
  135. package/dist/commands/install.d.ts.map +0 -1
  136. package/dist/commands/install.js.map +0 -1
  137. package/dist/commands/prepare-tools.d.ts +0 -5
  138. package/dist/commands/prepare-tools.d.ts.map +0 -1
  139. package/dist/commands/prepare-tools.js.map +0 -1
  140. package/dist/commands/uninstall.d.ts +0 -10
  141. package/dist/commands/uninstall.d.ts.map +0 -1
  142. package/dist/commands/uninstall.js.map +0 -1
  143. package/dist/commands/update.d.ts +0 -10
  144. package/dist/commands/update.d.ts.map +0 -1
  145. package/dist/commands/update.js.map +0 -1
  146. package/dist/index.d.ts +0 -2
  147. package/dist/index.d.ts.map +0 -1
  148. package/dist/index.js.map +0 -1
  149. package/dist/types.d.ts +0 -42
  150. package/dist/types.d.ts.map +0 -1
  151. package/dist/types.js.map +0 -1
  152. package/dist/utils/alipay-cli.d.ts +0 -20
  153. package/dist/utils/alipay-cli.d.ts.map +0 -1
  154. package/dist/utils/alipay-cli.js.map +0 -1
  155. package/dist/utils/config.d.ts +0 -52
  156. package/dist/utils/config.d.ts.map +0 -1
  157. package/dist/utils/config.js.map +0 -1
  158. package/dist/utils/file.d.ts +0 -77
  159. package/dist/utils/file.d.ts.map +0 -1
  160. package/dist/utils/file.js.map +0 -1
  161. package/dist/utils/jq.d.ts +0 -17
  162. package/dist/utils/jq.d.ts.map +0 -1
  163. package/dist/utils/jq.js.map +0 -1
  164. package/dist/utils/npm.d.ts +0 -17
  165. package/dist/utils/npm.d.ts.map +0 -1
  166. package/dist/utils/npm.js.map +0 -1
  167. package/dist/utils/shell.d.ts +0 -24
  168. package/dist/utils/shell.d.ts.map +0 -1
  169. package/dist/utils/shell.js.map +0 -1
@@ -0,0 +1,1425 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from 'node:crypto';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { isWindowsPlatform } from './platform_compat.mjs';
8
+ import {
9
+ buildWindowsOnboardingRedirectUrl,
10
+ createMccContextReceipt,
11
+ mccQualificationSummary,
12
+ renderMessage
13
+ } from './render_customer_message.mjs';
14
+ import {
15
+ currentRunContext,
16
+ emitStageMetric,
17
+ startTelemetryRun
18
+ } from './telemetry.mjs';
19
+
20
+ const internalPrefix = 'ALIPAY_AIPAY_INTERNAL:';
21
+ const productTypes = {
22
+ aipay: { variant: 'AIPAY', productName: 'AI 按量付费' },
23
+ webpay: { variant: 'WEBPAY', productName: 'AI 网页应用收款' },
24
+ apppay: { variant: 'APPPAY', productName: 'AI 移动应用收款' }
25
+ };
26
+
27
+ const signingStatusMap = {
28
+ NOT_SIGNED: '未签约',
29
+ SIGN_SUBMITTED: '已提交待生效',
30
+ SIGNED_EFFECTIVE: '已生效',
31
+ OTHER_STATUS: '其他状态待核验',
32
+ QUERY_FAILED: '查询失败',
33
+ FAILED: '查询失败',
34
+ ERROR: '查询失败'
35
+ };
36
+
37
+ const serviceFlowMap = {
38
+ 'FLOW:CREATE_NEW': '无已有服务',
39
+ 'FLOW:SELECT': '已有服务候选',
40
+ QUERY_FAILED: '查询失败',
41
+ 'FLOW:ERROR': '查询失败',
42
+ ERROR: '查询失败'
43
+ };
44
+
45
+ const applicationFlowMap = {
46
+ 'FLOW:CREATE_NEW': '无同类型应用',
47
+ 'FLOW:SELECT': '有上线应用可复用',
48
+ 'FLOW:PENDING_APPLICATIONS': '仅有未上线应用',
49
+ QUERY_FAILED: '查询失败',
50
+ 'FLOW:ERROR': '查询失败',
51
+ ERROR: '查询失败'
52
+ };
53
+
54
+ const categoryNames = {
55
+ signing: '签约材料',
56
+ service: '服务资料',
57
+ application: '应用资料'
58
+ };
59
+
60
+ const materialFieldsByCategory = Object.freeze({
61
+ signing: ['APP名称', '首页截图', '商品页截图', '支付页截图', '特殊资质材料'],
62
+ service: ['服务名称', '服务描述', '服务地址', '服务单价', '请求示例JSON'],
63
+ application: ['应用平台和对应资料', 'iOS Bundle ID(bundleId)', 'Android 应用包名(appPackage)', 'Android 应用签名摘要(appSign)', '应用公钥']
64
+ });
65
+
66
+ const materialStates = {
67
+ INITIAL: { variant: 'INITIAL', currentStatus: '待补充' },
68
+ PARTIAL: { variant: 'PARTIAL', currentStatus: '部分已提供,待补充' },
69
+ INVALID: { variant: 'INVALID', currentStatus: '校验失败,需更正' },
70
+ APP_WEB_INITIAL: { variant: 'APP_WEB_INITIAL', currentStatus: '待补充' },
71
+ APP_MOBILE_INITIAL: { variant: 'APP_MOBILE_INITIAL', currentStatus: '待补充' }
72
+ };
73
+
74
+ const directFieldAliases = new Map([
75
+ ['APP名称', ['APP名称']],
76
+ ['appName', ['APP名称']],
77
+ ['首页截图', ['首页截图']],
78
+ ['商品页截图', ['商品页截图']],
79
+ ['支付页截图', ['支付页截图']],
80
+ ['特殊资质材料', ['特殊资质材料']],
81
+ ['特殊资质', ['特殊资质材料']],
82
+ ['服务名称', ['服务名称']],
83
+ ['serviceName', ['服务名称']],
84
+ ['服务描述', ['服务描述']],
85
+ ['serviceDesc', ['服务描述']],
86
+ ['服务地址', ['服务地址']],
87
+ ['资源 URL', ['服务地址']],
88
+ ['资源URL', ['服务地址']],
89
+ ['resourceUrl', ['服务地址']],
90
+ ['服务单价', ['服务单价']],
91
+ ['单次价格', ['服务单价']],
92
+ ['pricing', ['服务单价']],
93
+ ['请求示例JSON', ['请求示例JSON']],
94
+ ['请求示例 JSON', ['请求示例JSON']],
95
+ ['序列化 JSON 请求示例', ['请求示例JSON']],
96
+ ['schemaUrl', ['请求示例JSON']],
97
+ ['应用平台和对应资料', ['应用平台和对应资料']],
98
+ ['mobilePlatform', ['应用平台和对应资料']],
99
+ ['iOS Bundle ID(bundleId)', ['iOS Bundle ID(bundleId)']],
100
+ ['iOS Bundle ID', ['iOS Bundle ID(bundleId)']],
101
+ ['bundleId', ['iOS Bundle ID(bundleId)']],
102
+ ['Android 应用包名(appPackage)', ['Android 应用包名(appPackage)']],
103
+ ['Android 应用包名', ['Android 应用包名(appPackage)']],
104
+ ['appPackage', ['Android 应用包名(appPackage)']],
105
+ ['Android 应用签名摘要(appSign)', ['Android 应用签名摘要(appSign)']],
106
+ ['Android 应用签名摘要', ['Android 应用签名摘要(appSign)']],
107
+ ['appSign', ['Android 应用签名摘要(appSign)']],
108
+ ['应用公钥', ['应用公钥']],
109
+ ['publicKey', ['应用公钥']]
110
+ ]);
111
+
112
+ function parseArgs(argv) {
113
+ const command = argv[2];
114
+ const args = {};
115
+ for (let index = 3; index < argv.length; index += 1) {
116
+ const key = argv[index];
117
+ if (!key.startsWith('--')) throw new Error(`未知参数 ${key}`);
118
+ const value = argv[index + 1];
119
+ if (value === undefined || value.startsWith('--')) throw new Error(`参数 ${key} 缺少取值`);
120
+ const name = key.slice(2);
121
+ if (Object.prototype.hasOwnProperty.call(args, name)) throw new Error(`参数 ${key} 重复`);
122
+ args[name] = value;
123
+ index += 1;
124
+ }
125
+ return { command, args };
126
+ }
127
+
128
+ function required(args, name) {
129
+ const value = args[name];
130
+ if (typeof value !== 'string' || value.length === 0) throw new Error(`缺少参数 --${name}`);
131
+ return value;
132
+ }
133
+
134
+ function optional(args, name) {
135
+ return typeof args[name] === 'string' && args[name].length > 0 ? args[name] : undefined;
136
+ }
137
+
138
+ function productFor(args) {
139
+ const productType = required(args, 'product-type');
140
+ const mapping = productTypes[productType];
141
+ if (!mapping) throw new Error('product-type 非法,仅支持 aipay、webpay、apppay');
142
+ return mapping;
143
+ }
144
+
145
+ function normalizeEnum(value, map, label) {
146
+ if (typeof value !== 'string' || value.length === 0) throw new Error(`缺少 ${label}`);
147
+ const normalized = map[value];
148
+ if (!normalized) throw new Error(`${label} 不在允许脚本标记中;允许值:${Object.keys(map).join('、')}`);
149
+ return normalized;
150
+ }
151
+
152
+ async function readStdin() {
153
+ let input = '';
154
+ for await (const chunk of process.stdin) input += chunk;
155
+ return input.trim() ? JSON.parse(input) : {};
156
+ }
157
+
158
+ function runPlatformEntry(args = {}) {
159
+ if (!isWindowsPlatform()) return '';
160
+ const runContext = currentRunContext();
161
+ if (runContext?.terminal) {
162
+ throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
163
+ }
164
+ const rootIntent = runContext?.root_intent || process.env.ALIPAY_AIPAY_ROOT_INTENT || process.env.ROOT_INTENT || '';
165
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
166
+ throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
167
+ }
168
+ const platformId = platformIdFromEnv(process.env)
169
+ || runContext?.run_id
170
+ || '';
171
+ if (!platformId) {
172
+ throw new Error('Windows 产品开通引导缺少有效的当前 Onboarding run context');
173
+ }
174
+ return renderMessage('onboarding.windows.redirect', 'DEFAULT', {
175
+ officialUrl: buildWindowsOnboardingRedirectUrl(platformId)
176
+ });
177
+ }
178
+
179
+ function platformIdFromEnv(env = process.env) {
180
+ const candidate = env.COZE_PROJECT_ID
181
+ || env.MEOO_PROJECT_ID
182
+ || (env.MIAODA ? env.VITE_APP_ID || env.PLATFORM_ID || '' : '');
183
+ return typeof candidate === 'string' && candidate && !/[\u0000-\u001f\u007f,^=]/u.test(candidate)
184
+ ? candidate
185
+ : '';
186
+ }
187
+
188
+ function parseJsonArg(args, name, label) {
189
+ const raw = required(args, name);
190
+ try {
191
+ return JSON.parse(raw);
192
+ } catch {
193
+ throw new Error(`${label} 必须是合法 JSON`);
194
+ }
195
+ }
196
+
197
+ function runProductClarify() {
198
+ return renderMessage('product.clarify', 'DEFAULT', {});
199
+ }
200
+
201
+ function runEnterpriseUnsupported(args) {
202
+ const variant = required(args, 'variant');
203
+ if (!['ONBOARDING_ONLY', 'FULL_PROCESS'].includes(variant)) {
204
+ throw new Error('variant 非法,仅支持 ONBOARDING_ONLY、FULL_PROCESS');
205
+ }
206
+ return renderMessage(
207
+ 'onboarding.enterprise.unsupported',
208
+ variant,
209
+ {}
210
+ );
211
+ }
212
+
213
+ async function runMccClarify(args) {
214
+ const productType = required(args, 'product-type');
215
+ const product = productFor({ ...args, 'product-type': productType });
216
+ const rootIntent = required(args, 'root-intent');
217
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
218
+ throw new Error('root-intent 仅支持 onboarding_only、full_process');
219
+ }
220
+ await startTelemetryRun(rootIntent, productType);
221
+ return renderMessage('onboarding.mcc.clarify', 'DEFAULT', {
222
+ productName: product.productName
223
+ }, { productType });
224
+ }
225
+
226
+ function normalizeUnicode(value) {
227
+ return String(value).normalize('NFKC').toLocaleLowerCase('zh-CN');
228
+ }
229
+
230
+ function normalizeSearch(value) {
231
+ return normalizeUnicode(value)
232
+ .replace(/[\p{P}\p{S}\p{Z}\s]+/gu, ' ')
233
+ .trim()
234
+ .replace(/\s+/gu, ' ');
235
+ }
236
+
237
+ function assertMccQuery(query) {
238
+ if (/[\u0000-\u001f\u007f]/u.test(query)) throw new Error('query 包含控制字符或过长');
239
+ const normalizedQuery = normalizeSearch(query);
240
+ const normalizedLength = [...normalizedQuery.replace(/\s+/gu, '')].length;
241
+ if (normalizedLength > 500) throw new Error('query 包含控制字符或过长');
242
+ if (normalizedLength < 2) throw new Error('query 过短,无法执行确定性 MCC 检索');
243
+ return normalizedQuery;
244
+ }
245
+
246
+ const mccSelectionReceiptPrefix = 'AIPAY_MCC_SELECTION_V1.';
247
+ const ignoredMccQueryTokens = new Set([
248
+ '服务', '企业', '个人', '提供', '适用', '用于', '平台', '项目', '申请', '商家', '公司',
249
+ '互联', '联网', '互联网', '在线', '用品', '收费', '缴费', '费用', '适用于', '经营',
250
+ '业务', '相关', '主体', '从事', '网站', '网页', '购买', '买票', '购票', '票务', '售票',
251
+ '预约', '预定', '退票', '支付', '付款', '收款', '下单', '订单', '交易', '销售', '售卖',
252
+ '商品', '产品'
253
+ ]);
254
+
255
+ function activeMccRunId(env = process.env) {
256
+ const candidate = env.ALIPAY_AIPAY_RUN_ID
257
+ || env.ALIPAY_AIPAY_FLOW_RUN_ID
258
+ || currentRunContext(env)?.run_id
259
+ || '';
260
+ return typeof candidate === 'string' && candidate && !/[\u0000-\u001f\u007f,^=]/u.test(candidate) ? candidate : '';
261
+ }
262
+
263
+ function captureMccRunContext(env = process.env) {
264
+ return Object.freeze({ runId: activeMccRunId(env) });
265
+ }
266
+
267
+ function productSalesCode(productType) {
268
+ return {
269
+ aipay: 'I1080300001000160457',
270
+ webpay: 'I1080300001000041203',
271
+ apppay: 'I1080300001000041313'
272
+ }[productType] || '';
273
+ }
274
+
275
+ function mccCandidateFromRecord(record, productType = '', mccRunContext = captureMccRunContext()) {
276
+ const qualification = record.qualification ?? mccQualificationSummary(record);
277
+ const candidate = {
278
+ mccCode: record.code,
279
+ mccName: record.name,
280
+ applicableBusinesses: record.applicableBusinesses,
281
+ qualification
282
+ };
283
+ if (record.dynamic) {
284
+ candidate.needSpecialQual = record.needSpecialQual === true;
285
+ candidate.qualificationGroups = record.qualificationGroups || [];
286
+ }
287
+ const hasCompleteQualificationFacts = record.needSpecialQual !== true
288
+ || (Array.isArray(record.qualificationGroups) && record.qualificationGroups.length > 0);
289
+ if (productType && record.dynamic && hasCompleteQualificationFacts) {
290
+ candidate.mccContextReceipt = createMccContextReceipt({
291
+ schema_ver: 1,
292
+ product_type: productType,
293
+ sales_code: productSalesCode(productType),
294
+ mcc_code: record.code,
295
+ mcc_name: record.name,
296
+ mcc_description: record.applicableBusinesses,
297
+ need_special_qual: record.needSpecialQual === true,
298
+ qualification_groups: record.qualificationGroups || [],
299
+ run_id: mccRunContext.runId || undefined
300
+ });
301
+ }
302
+ return {
303
+ ...candidate
304
+ };
305
+ }
306
+
307
+ function dynamicMccRecordsFromCatalog(rawCatalog) {
308
+ let catalog;
309
+ try {
310
+ catalog = typeof rawCatalog === 'string' ? JSON.parse(rawCatalog) : rawCatalog;
311
+ } catch {
312
+ throw new Error('mcc-catalog-json 必须是合法 JSON');
313
+ }
314
+ if (!Array.isArray(catalog) || catalog.length === 0) {
315
+ throw new Error('mcc-catalog-json 必须是非空数组');
316
+ }
317
+ const records = new Map();
318
+ const refs = new Set();
319
+ for (const item of catalog) {
320
+ if (!item || typeof item !== 'object' || Array.isArray(item)) throw new Error('mcc-catalog-json 项必须是对象');
321
+ const code = String(item.mccCode || item.code || '').toUpperCase();
322
+ const name = String(item.mccName || item.name || '');
323
+ const applicableBusinesses = String(item.mccDescription || item.applicableBusinesses || '');
324
+ if (!/^A\d{4}_B\d{4}$/u.test(code)) throw new Error(`动态 MCC 编码非法:${code}`);
325
+ if (!name || !applicableBusinesses) throw new Error(`动态 MCC ${code} 缺少名称或描述`);
326
+ if (typeof item.needSpecialQual !== 'boolean') throw new Error(`动态 MCC ${code} 缺少布尔型 needSpecialQual`);
327
+ if (!Array.isArray(item.qualificationGroups)) throw new Error(`动态 MCC ${code} 的 qualificationGroups 必须是数组`);
328
+ if (records.has(code)) throw new Error(`动态 MCC 重复:${code}`);
329
+ const ref = item.mccRef == null ? '' : String(item.mccRef);
330
+ if (ref && !/^mcc-\d+$/u.test(ref)) throw new Error(`动态 MCC ${code} 的 mccRef 非法`);
331
+ if (ref && refs.has(ref)) throw new Error(`动态 MCC 目录引用重复:${ref}`);
332
+ if (ref) refs.add(ref);
333
+ const needSpecialQual = item.needSpecialQual;
334
+ const qualificationGroups = item.qualificationGroups;
335
+ records.set(code, {
336
+ code,
337
+ name,
338
+ applicableBusinesses,
339
+ needSpecialQual,
340
+ qualificationGroups,
341
+ qualification: mccQualificationSummary({ needSpecialQual, qualificationGroups }),
342
+ ref: ref || undefined,
343
+ dynamic: true
344
+ });
345
+ }
346
+ return records;
347
+ }
348
+
349
+ function recordsForArgs(args) {
350
+ const catalog = optional(args, 'mcc-catalog-json');
351
+ if (catalog === undefined) {
352
+ throw new Error('mcc-resolve-plan 必须提供 mcc.sh 从当前 MCC 查询事实生成的 --mcc-catalog-json');
353
+ }
354
+ return dynamicMccRecordsFromCatalog(catalog);
355
+ }
356
+
357
+ function sortedMccRecords(records) {
358
+ return [...records.values()].sort((left, right) => left.code.localeCompare(right.code));
359
+ }
360
+
361
+ function mccAgentRefEntries(records) {
362
+ const refs = new Map();
363
+ sortedMccRecords(records).forEach((record, index) => {
364
+ const ref = record.ref || `mcc-${index + 1}`;
365
+ if (refs.has(ref)) throw new Error(`动态 MCC 目录引用重复:${ref}`);
366
+ refs.set(ref, record.code);
367
+ });
368
+ return refs;
369
+ }
370
+
371
+ function mccCatalogForAgent(records) {
372
+ const refsByCode = new Map([...mccAgentRefEntries(records)].map(([ref, code]) => [code, ref]));
373
+ return sortedMccRecords(records).map((record) => ({
374
+ ref: refsByCode.get(record.code),
375
+ mccName: record.name,
376
+ mccDescription: record.applicableBusinesses
377
+ }));
378
+ }
379
+
380
+ function parseAgentCandidateRefs(args, records) {
381
+ const raw = optional(args, 'agent-candidate-refs-json');
382
+ if (raw === undefined) return { codes: [] };
383
+ let parsed;
384
+ try {
385
+ parsed = JSON.parse(raw);
386
+ } catch {
387
+ throw new Error('agent-candidate-refs-json 必须是合法 JSON 字符串数组');
388
+ }
389
+ if (!Array.isArray(parsed) || parsed.length > 8 || parsed.some((ref) => typeof ref !== 'string')) {
390
+ throw new Error('agent-candidate-refs-json 必须是最多 8 项的 JSON 字符串数组');
391
+ }
392
+ const refs = mccAgentRefEntries(records);
393
+ const codes = [];
394
+ for (const ref of parsed) {
395
+ if (!refs.has(ref) || codes.includes(refs.get(ref))) {
396
+ throw new Error('agent-candidate-refs-json 只能引用本轮 queryMcc 返回的唯一 MCC 目录引用');
397
+ }
398
+ codes.push(refs.get(ref));
399
+ }
400
+ return { codes };
401
+ }
402
+
403
+ function mccTableSummary(records) {
404
+ const entries = [...records.values()]
405
+ .map((record) => [
406
+ record.code,
407
+ record.name,
408
+ record.applicableBusinesses,
409
+ record.qualification,
410
+ JSON.stringify(record.qualificationGroups || [])
411
+ ].join('\t'))
412
+ .sort()
413
+ .join('\n');
414
+ return {
415
+ recordCount: records.size,
416
+ sha256: createHash('sha256').update(entries).digest('hex')
417
+ };
418
+ }
419
+
420
+ function normalizeMccCode(value, records) {
421
+ const raw = normalizeUnicode(value).trim().toUpperCase();
422
+ if (/^A\d{4}_B\d{4}$/u.test(raw)) return records.has(raw) ? raw : '';
423
+ if (!/^B\d{4}$/u.test(raw)) return '';
424
+ const matches = [...records.keys()].filter((code) => code.endsWith(`_${raw}`));
425
+ if (matches.length > 1) return '';
426
+ return matches[0] || '';
427
+ }
428
+
429
+ function directMccCode(query, records) {
430
+ const raw = normalizeUnicode(query).trim().toUpperCase();
431
+ if (!/^(?:A\d{4}_)?B\d{4}$/u.test(raw)) return { isCode: false, matches: [] };
432
+ if (/^A\d{4}_B\d{4}$/u.test(raw)) {
433
+ return { isCode: true, matches: records.has(raw) ? [raw] : [] };
434
+ }
435
+ return { isCode: true, matches: [...records.keys()].filter((code) => code.endsWith(`_${raw}`)) };
436
+ }
437
+
438
+ function mccNgrams(value, size) {
439
+ const grams = new Set();
440
+ const searchableText = meaningfulMccSearch(value);
441
+ for (const segment of searchableText.split(/\s+/u).filter(Boolean)) {
442
+ const characters = [...segment];
443
+ for (let index = 0; index <= characters.length - size; index += 1) {
444
+ const gram = characters.slice(index, index + size).join('');
445
+ if (/^[\p{L}\p{N}]+$/u.test(gram)) grams.add(gram);
446
+ }
447
+ }
448
+ return grams;
449
+ }
450
+
451
+ function meaningfulMccSearch(value) {
452
+ let searchableText = normalizeSearch(value);
453
+ const ignoredTerms = [...ignoredMccQueryTokens]
454
+ .sort((left, right) => [...right].length - [...left].length);
455
+ for (const term of ignoredTerms) searchableText = searchableText.replaceAll(term, ' ');
456
+ return searchableText
457
+ .trim()
458
+ .replace(/\s+/gu, ' ')
459
+ .split(' ')
460
+ .filter((segment) => [...segment].length >= 2)
461
+ .join(' ');
462
+ }
463
+
464
+ function parseMccSemanticTerms(args) {
465
+ const raw = optional(args, 'semantic-terms-json');
466
+ if (raw === undefined) return [];
467
+ let parsed;
468
+ try {
469
+ parsed = JSON.parse(raw);
470
+ } catch {
471
+ throw new Error('semantic-terms-json 必须是合法 JSON 字符串数组');
472
+ }
473
+ if (!Array.isArray(parsed) || parsed.length > 12 || parsed.some((term) => typeof term !== 'string')) {
474
+ throw new Error('semantic-terms-json 必须是最多 12 项的 JSON 字符串数组');
475
+ }
476
+ const normalized = [];
477
+ let totalLength = 0;
478
+ for (const rawTerm of parsed) {
479
+ if (/\u0000|[\u0001-\u001f\u007f]/u.test(rawTerm)) {
480
+ throw new Error('semantic-terms-json 包含控制字符');
481
+ }
482
+ const term = normalizeSearch(rawTerm);
483
+ const termLength = [...term.replace(/\s+/gu, '')].length;
484
+ if (termLength < 2 || termLength > 40) {
485
+ throw new Error('semantic-terms-json 每项长度必须为 2 到 40 个字符');
486
+ }
487
+ if (/(?:A\d{4}\s*)?B\d{4}/iu.test(term)) {
488
+ throw new Error('semantic-terms-json 只能包含业务概念词,禁止提供 MCC 编码');
489
+ }
490
+ totalLength += termLength;
491
+ if (!normalized.includes(term)) normalized.push(term);
492
+ }
493
+ if (totalLength > 240) throw new Error('semantic-terms-json 总长度不得超过 240 个字符');
494
+ return normalized.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
495
+ }
496
+
497
+ function mccDocumentFrequency(indexedRecords) {
498
+ const frequencies = new Map();
499
+ for (const indexed of indexedRecords) {
500
+ const documentGrams = new Set([
501
+ ...indexed.nameBigrams,
502
+ ...indexed.nameTrigrams,
503
+ ...indexed.businessBigrams,
504
+ ...indexed.businessTrigrams
505
+ ]);
506
+ for (const gram of documentGrams) frequencies.set(gram, (frequencies.get(gram) || 0) + 1);
507
+ }
508
+ return frequencies;
509
+ }
510
+
511
+ function weightedMccOverlap(queryGrams, documentGrams, frequencies, recordCount, size) {
512
+ let weight = 0;
513
+ for (const gram of queryGrams) {
514
+ if (!documentGrams.has(gram)) continue;
515
+ const documentCount = frequencies.get(gram) || 0;
516
+ weight += (Math.log((recordCount + 1) / (documentCount + 1)) + 1) * (size === 3 ? 1.6 : 1);
517
+ }
518
+ return weight;
519
+ }
520
+
521
+ function mccPhraseCoverage(haystackSegments, needleSegments) {
522
+ let coverage = 0;
523
+ for (const needle of needleSegments) {
524
+ if (ignoredMccQueryTokens.has(needle)) continue;
525
+ const needleLength = [...needle].length;
526
+ for (const haystack of haystackSegments) {
527
+ if (!haystack.includes(needle)) continue;
528
+ coverage = Math.max(coverage, needleLength / [...haystack].length);
529
+ }
530
+ }
531
+ return coverage;
532
+ }
533
+
534
+ function mccTextEvidence(value, indexed, frequencies, recordCount) {
535
+ const normalizedValue = normalizeSearch(value);
536
+ const valueSegments = meaningfulMccSearch(normalizedValue).split(' ').filter(Boolean);
537
+ const valueBigrams = mccNgrams(normalizedValue, 2);
538
+ const valueTrigrams = mccNgrams(normalizedValue, 3);
539
+ const exactName = indexed.name === normalizedValue
540
+ || (normalizedValue.split(' ').length === 1 && indexed.nameSegments.includes(normalizedValue));
541
+ const nameContainsValueCoverage = mccPhraseCoverage(indexed.nameEvidenceSegments, valueSegments);
542
+ const valueContainsNameCoverage = mccPhraseCoverage(valueSegments, indexed.nameEvidenceSegments);
543
+ const businessContainsValueCoverage = mccPhraseCoverage(indexed.businessEvidenceSegments, valueSegments);
544
+ const nameBigram = weightedMccOverlap(valueBigrams, indexed.nameBigrams, frequencies, recordCount, 2);
545
+ const nameTrigram = weightedMccOverlap(valueTrigrams, indexed.nameTrigrams, frequencies, recordCount, 3);
546
+ const businessBigram = weightedMccOverlap(valueBigrams, indexed.businessBigrams, frequencies, recordCount, 2);
547
+ const businessTrigram = weightedMccOverlap(valueTrigrams, indexed.businessTrigrams, frequencies, recordCount, 3);
548
+ const matchedBigrams = new Set([
549
+ ...[...valueBigrams].filter((gram) => indexed.nameBigrams.has(gram)),
550
+ ...[...valueBigrams].filter((gram) => indexed.businessBigrams.has(gram))
551
+ ]);
552
+ const matchedTrigrams = new Set([
553
+ ...[...valueTrigrams].filter((gram) => indexed.nameTrigrams.has(gram)),
554
+ ...[...valueTrigrams].filter((gram) => indexed.businessTrigrams.has(gram))
555
+ ]);
556
+ const rareBigramDocumentLimit = Math.max(1, Math.ceil(recordCount * 0.02));
557
+ const hasRareBigram = [...matchedBigrams].some((gram) => (
558
+ (frequencies.get(gram) || recordCount) <= rareBigramDocumentLimit
559
+ ));
560
+ const hasLexicalEvidence = matchedTrigrams.size > 0
561
+ || matchedBigrams.size >= 2
562
+ || (valueBigrams.size <= 2 && hasRareBigram);
563
+ const phraseEvidence = nameContainsValueCoverage > 0
564
+ || valueContainsNameCoverage > 0
565
+ || businessContainsValueCoverage > 0;
566
+ return {
567
+ eligible: exactName || phraseEvidence || hasLexicalEvidence,
568
+ exactName,
569
+ score: (exactName ? 100000 : 0)
570
+ + (nameContainsValueCoverage * 6000)
571
+ + (valueContainsNameCoverage * 3000)
572
+ + (businessContainsValueCoverage * 2500)
573
+ + ((nameBigram + nameTrigram) * 120)
574
+ + ((businessBigram + businessTrigram) * 40)
575
+ };
576
+ }
577
+
578
+ function deterministicMccMatches(query, records, { limit = 8, semanticTerms = [] } = {}) {
579
+ const normalizedQuery = assertMccQuery(query);
580
+ const indexedRecords = [...records.values()].map((record) => {
581
+ const name = normalizeSearch(record.name);
582
+ const businesses = normalizeSearch(record.applicableBusinesses);
583
+ return {
584
+ record,
585
+ name,
586
+ nameSegments: name.split(' ').filter(Boolean),
587
+ nameEvidenceSegments: meaningfulMccSearch(name).split(' ').filter(Boolean),
588
+ businessEvidenceSegments: meaningfulMccSearch(businesses).split(' ').filter(Boolean),
589
+ nameBigrams: mccNgrams(name, 2),
590
+ nameTrigrams: mccNgrams(name, 3),
591
+ businessBigrams: mccNgrams(businesses, 2),
592
+ businessTrigrams: mccNgrams(businesses, 3)
593
+ };
594
+ });
595
+ const frequencies = mccDocumentFrequency(indexedRecords);
596
+ const scored = [];
597
+
598
+ for (const indexed of indexedRecords) {
599
+ const originalEvidence = mccTextEvidence(normalizedQuery, indexed, frequencies, indexedRecords.length);
600
+ const semanticEvidence = semanticTerms
601
+ .map((term) => mccTextEvidence(term, indexed, frequencies, indexedRecords.length))
602
+ .filter((evidence) => evidence.eligible)
603
+ .sort((left, right) => right.score - left.score)[0];
604
+ if (!originalEvidence.eligible && !semanticEvidence) continue;
605
+ scored.push({
606
+ record: indexed.record,
607
+ score: originalEvidence.score + ((semanticEvidence?.score || 0) * 0.7),
608
+ exactName: originalEvidence.exactName,
609
+ originalEligible: originalEvidence.eligible,
610
+ semanticEligible: Boolean(semanticEvidence),
611
+ semanticExactName: Boolean(semanticEvidence?.exactName)
612
+ });
613
+ }
614
+
615
+ const exactMatches = scored.filter(({ exactName }) => exactName);
616
+ const ranked = exactMatches.length > 0
617
+ ? exactMatches
618
+ : scored;
619
+ return ranked
620
+ .sort((left, right) => right.score - left.score || left.record.code.localeCompare(right.record.code))
621
+ .slice(0, limit);
622
+ }
623
+
624
+ function createMccSelectionReceipt(productType, query, result, mccRunContext = captureMccRunContext()) {
625
+ const codes = result.mergedCandidateCodes.join(',');
626
+ const table = result.mccTableSummary;
627
+ const binding = [
628
+ mccRunContext.runId,
629
+ productType,
630
+ assertMccQuery(query),
631
+ JSON.stringify(result.semanticTerms || []),
632
+ table.sha256,
633
+ table.recordCount,
634
+ codes
635
+ ].join('\n');
636
+ return `${mccSelectionReceiptPrefix}${codes}.${createHash('sha256').update(binding).digest('base64url')}`;
637
+ }
638
+
639
+ function resolveMccSelection(productType, query, selectedInput, receipt, records, semanticTerms, agentCandidateCodes, mccRunContext) {
640
+ const candidateCodes = mccSelectionReceiptCodes(productType, query, receipt, records, semanticTerms, mccRunContext);
641
+ if (!candidateCodes) {
642
+ const semanticStatus = typeof receipt === 'string' && receipt.startsWith(mccSelectionReceiptPrefix)
643
+ ? 'SELECTION_RECEIPT_STALE'
644
+ : 'SELECTION_RECEIPT_INVALID';
645
+ const refreshed = resolveMccResultFromRecords(
646
+ query,
647
+ productType,
648
+ records,
649
+ semanticTerms,
650
+ semanticStatus,
651
+ mccRunContext,
652
+ agentCandidateCodes
653
+ );
654
+ return {
655
+ ...refreshed,
656
+ matchType: refreshed.matchType === 'NONE' ? 'NONE' : 'MULTIPLE',
657
+ semanticStatus
658
+ };
659
+ }
660
+ const currentTable = mccTableSummary(records);
661
+ const selectedCode = normalizeMccCode(selectedInput, records);
662
+ if (!selectedCode || !candidateCodes.includes(selectedCode)) {
663
+ return {
664
+ matchType: 'MULTIPLE',
665
+ semanticStatus: 'SELECTION_INVALID',
666
+ deterministicCandidateCodes: [],
667
+ deterministicStrongCandidateCodes: [],
668
+ deterministicWeakCandidateCodes: [],
669
+ semanticCandidateCodes: [],
670
+ semanticTerms,
671
+ mergedCandidateCodes: candidateCodes,
672
+ mccTableSummary: currentTable,
673
+ candidates: candidateCodes.map((code) => mccCandidateFromRecord(records.get(code), productType, mccRunContext))
674
+ };
675
+ }
676
+ const candidate = mccCandidateFromRecord(records.get(selectedCode), productType, mccRunContext);
677
+ return {
678
+ matchType: 'UNIQUE',
679
+ semanticStatus: 'NOT_REQUIRED_SELECTION',
680
+ deterministicCandidateCodes: [],
681
+ deterministicStrongCandidateCodes: [],
682
+ deterministicWeakCandidateCodes: [],
683
+ semanticCandidateCodes: [],
684
+ semanticTerms,
685
+ mergedCandidateCodes: [selectedCode],
686
+ mccTableSummary: currentTable,
687
+ candidates: [candidate]
688
+ };
689
+ }
690
+
691
+ function mccSelectionReceiptCodes(productType, query, receipt, records, semanticTerms, mccRunContext = captureMccRunContext()) {
692
+ if (typeof receipt !== 'string' || !receipt.startsWith(mccSelectionReceiptPrefix)) return null;
693
+ const body = receipt.slice(mccSelectionReceiptPrefix.length);
694
+ const separator = body.lastIndexOf('.');
695
+ const candidateCodes = separator > 0 ? body.slice(0, separator).split(',') : [];
696
+ const currentTable = mccTableSummary(records);
697
+ if (candidateCodes.length < 1 || candidateCodes.length > 8
698
+ || new Set(candidateCodes).size !== candidateCodes.length
699
+ || candidateCodes.some((code) => !records.has(code))
700
+ || receipt !== createMccSelectionReceipt(productType, query, {
701
+ mergedCandidateCodes: candidateCodes,
702
+ mccTableSummary: currentTable,
703
+ semanticTerms
704
+ }, mccRunContext)) {
705
+ return null;
706
+ }
707
+ return candidateCodes;
708
+ }
709
+
710
+ function resolveMccResultFromRecords(
711
+ query,
712
+ productType,
713
+ records,
714
+ semanticTerms = [],
715
+ semanticStatus,
716
+ mccRunContext = captureMccRunContext(),
717
+ agentCandidateCodes = []
718
+ ) {
719
+ const directCode = directMccCode(query, records);
720
+ if (directCode.isCode) {
721
+ if (directCode.matches.length === 1) {
722
+ const code = directCode.matches[0];
723
+ const candidate = mccCandidateFromRecord(records.get(code), productType, mccRunContext);
724
+ return {
725
+ matchType: 'UNIQUE',
726
+ semanticStatus: 'NOT_REQUIRED_DIRECT_CODE',
727
+ deterministicCandidateCodes: [code],
728
+ deterministicStrongCandidateCodes: [code],
729
+ deterministicWeakCandidateCodes: [],
730
+ semanticCandidateCodes: [],
731
+ agentCandidateCodes: [],
732
+ semanticTerms,
733
+ mergedCandidateCodes: [code],
734
+ mccTableSummary: mccTableSummary(records),
735
+ candidates: [candidate]
736
+ };
737
+ }
738
+ const finalCodes = directCode.matches.slice(0, 8);
739
+ return {
740
+ matchType: finalCodes.length === 0 ? 'NONE' : 'MULTIPLE',
741
+ semanticStatus: finalCodes.length === 0 ? 'DYNAMIC_CATALOG_CODE_NOT_FOUND' : 'DYNAMIC_CATALOG_CODE_AMBIGUOUS',
742
+ deterministicCandidateCodes: finalCodes,
743
+ deterministicStrongCandidateCodes: finalCodes,
744
+ deterministicWeakCandidateCodes: [],
745
+ semanticCandidateCodes: [],
746
+ agentCandidateCodes: [],
747
+ semanticTerms,
748
+ mergedCandidateCodes: finalCodes,
749
+ mccTableSummary: mccTableSummary(records),
750
+ candidates: finalCodes.map((code) => mccCandidateFromRecord(records.get(code), productType, mccRunContext))
751
+ };
752
+ }
753
+
754
+ const matches = deterministicMccMatches(query, records, { limit: 8, semanticTerms });
755
+ const deterministicCodes = matches.filter(({ originalEligible }) => originalEligible).map(({ record }) => record.code);
756
+ const semanticCodes = matches.filter(({ semanticEligible }) => semanticEligible).map(({ record }) => record.code);
757
+ const hasExactSemanticName = matches.some(({ semanticExactName }) => semanticExactName);
758
+ const localCodes = matches.map(({ record }) => record.code);
759
+ const mergedCodes = [...agentCandidateCodes, ...localCodes]
760
+ .filter((code, index, all) => all.indexOf(code) === index);
761
+ const finalCodes = mergedCodes.slice(0, 8);
762
+ const candidates = finalCodes.map((code) => mccCandidateFromRecord(records.get(code), productType, mccRunContext));
763
+ const needsAgentReview = agentCandidateCodes.length === 0
764
+ && (
765
+ matches.length === 0
766
+ || deterministicCodes.length !== 1
767
+ || (semanticTerms.length > 0 && deterministicCodes.length === 0 && !hasExactSemanticName)
768
+ );
769
+ return {
770
+ matchType: candidates.length === 1 ? 'UNIQUE' : candidates.length === 0 ? 'NONE' : 'MULTIPLE',
771
+ semanticStatus: semanticStatus
772
+ || (agentCandidateCodes.length > 0 ? 'AGENT_CATALOG_REVIEW_USED' : semanticTerms.length > 0 ? 'AGENT_SEMANTIC_TERMS_USED' : 'DYNAMIC_CATALOG'),
773
+ deterministicCandidateCodes: deterministicCodes,
774
+ deterministicStrongCandidateCodes: deterministicCodes,
775
+ deterministicWeakCandidateCodes: [],
776
+ semanticCandidateCodes: semanticCodes,
777
+ agentCandidateCodes,
778
+ semanticTerms,
779
+ mergedCandidateCodes: finalCodes,
780
+ mccTableSummary: mccTableSummary(records),
781
+ candidates,
782
+ ...(needsAgentReview ? {
783
+ agentReview: {
784
+ required: true,
785
+ attempt: 1,
786
+ maxAttempts: 1,
787
+ reason: 'LOW_CONFIDENCE_LOCAL_MATCH',
788
+ catalog: mccCatalogForAgent(records)
789
+ }
790
+ } : {})
791
+ };
792
+ }
793
+
794
+ function renderPlanConfirm(productType, mccCandidate, mccRunContext = captureMccRunContext()) {
795
+ const product = productFor({ 'product-type': productType });
796
+ const variables = {
797
+ mccCode: mccCandidate.mccCode,
798
+ mccName: mccCandidate.mccName,
799
+ mccQualification: mccCandidate.qualification
800
+ };
801
+ if (mccCandidate.mccContextReceipt) variables.mccContextReceipt = mccCandidate.mccContextReceipt;
802
+ return renderMessage('onboarding.plan.confirm', product.variant, variables, { productType, runId: mccRunContext.runId, mccRunId: mccRunContext.runId });
803
+ }
804
+
805
+ function requireCustomerMessageDelivery(messageId, message) {
806
+ process.stderr.write(`${internalPrefix}CUSTOMER_MESSAGE_DELIVERY_REQUIRED=${messageId}\n`);
807
+ return message;
808
+ }
809
+
810
+ async function runPlanConfirm(args) {
811
+ const rootIntent = required(args, 'root-intent');
812
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
813
+ throw new Error('root-intent 仅支持 onboarding_only、full_process');
814
+ }
815
+ const productType = required(args, 'product-type');
816
+ productFor({ ...args, 'product-type': productType });
817
+ await startTelemetryRun(rootIntent, productType);
818
+ const mccRunContext = captureMccRunContext();
819
+ return requireCustomerMessageDelivery('onboarding.plan.confirm', renderPlanConfirm(productType, {
820
+ mccCode: required(args, 'mcc-code'),
821
+ mccName: required(args, 'mcc-name'),
822
+ qualification: required(args, 'mcc-qualification'),
823
+ mccContextReceipt: optional(args, 'mcc-context-receipt')
824
+ }, mccRunContext));
825
+ }
826
+
827
+ async function runMccResolvePlan(args) {
828
+ const productType = required(args, 'product-type');
829
+ productFor({ ...args, 'product-type': productType });
830
+ const rootIntent = required(args, 'root-intent');
831
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
832
+ throw new Error('root-intent 仅支持 onboarding_only、full_process');
833
+ }
834
+ await startTelemetryRun(rootIntent, productType);
835
+ const mccRunContext = captureMccRunContext();
836
+ const query = required(args, 'query');
837
+ const selected = optional(args, 'select');
838
+ const selectionReceipt = optional(args, 'selection-receipt');
839
+ const selectionState = optional(args, 'selection-state');
840
+ const semanticTerms = parseMccSemanticTerms(args);
841
+ if ((selected === undefined) !== (selectionReceipt === undefined)) {
842
+ throw new Error('--select 与 --selection-receipt 必须同时提供');
843
+ }
844
+ if (selectionState !== undefined && selectionState !== 'STALE') {
845
+ throw new Error('--selection-state 仅支持 STALE');
846
+ }
847
+ if (selectionState === 'STALE' && selected !== undefined) {
848
+ throw new Error('STALE 选择状态不得继续套用旧选择');
849
+ }
850
+ if (args['agent-mcc-status'] !== undefined || args['agent-mcc-codes'] !== undefined) {
851
+ throw new Error('mcc-resolve-plan 不再接受 --agent-mcc-status 或 --agent-mcc-codes;MCC 匹配只使用本轮动态 catalog');
852
+ }
853
+ const records = recordsForArgs(args);
854
+ const agentReview = parseAgentCandidateRefs(args, records);
855
+ let result = selected === undefined
856
+ ? resolveMccResultFromRecords(query, productType, records, semanticTerms, undefined, mccRunContext, agentReview.codes)
857
+ : resolveMccSelection(productType, query, selected, selectionReceipt, records, semanticTerms, agentReview.codes, mccRunContext);
858
+ if (selectionState === 'STALE') {
859
+ result = {
860
+ ...result,
861
+ matchType: result.matchType === 'NONE' ? 'NONE' : 'MULTIPLE',
862
+ semanticStatus: 'SELECTION_RECEIPT_STALE'
863
+ };
864
+ }
865
+ const { candidates: _internalCandidates, ...controlResult } = result;
866
+ process.stderr.write(`ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=${JSON.stringify(controlResult)}\n`);
867
+ if (result.matchType === 'UNIQUE') {
868
+ if (result.candidates[0].mccContextReceipt) {
869
+ process.stderr.write(`ALIPAY_AIPAY_INTERNAL:MCC_CONTEXT_RECEIPT=${result.candidates[0].mccContextReceipt}\n`);
870
+ }
871
+ return requireCustomerMessageDelivery(
872
+ 'onboarding.plan.confirm',
873
+ renderPlanConfirm(productType, result.candidates[0], mccRunContext)
874
+ );
875
+ }
876
+ if (result.matchType === 'NONE') {
877
+ return renderMessage('onboarding.mcc.no_match', 'DEFAULT', {}, { productType });
878
+ }
879
+ process.stderr.write(`ALIPAY_AIPAY_INTERNAL:MCC_SELECTION_RECEIPT=${createMccSelectionReceipt(productType, query, result, mccRunContext)}\n`);
880
+ return renderMessage('onboarding.mcc.candidates', 'DEFAULT', {
881
+ mccCandidates: result.candidates
882
+ }, { productType, runId: mccRunContext.runId, mccRunId: mccRunContext.runId });
883
+ }
884
+
885
+ async function runMccMatchCodes(args) {
886
+ const productType = required(args, 'product-type');
887
+ productFor({ ...args, 'product-type': productType });
888
+ const rootIntent = required(args, 'root-intent');
889
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
890
+ throw new Error('root-intent 仅支持 onboarding_only、full_process');
891
+ }
892
+ await startTelemetryRun(rootIntent, productType);
893
+ const query = required(args, 'query');
894
+ const semanticTerms = parseMccSemanticTerms(args);
895
+ if (args['agent-mcc-status'] !== undefined || args['agent-mcc-codes'] !== undefined) {
896
+ throw new Error('mcc-match-codes 不接受 --agent-mcc-status 或 --agent-mcc-codes;MCC 匹配只使用本轮动态 catalog');
897
+ }
898
+ const records = recordsForArgs(args);
899
+ const agentReview = parseAgentCandidateRefs(args, records);
900
+ const mccRunContext = captureMccRunContext();
901
+ const result = resolveMccResultFromRecords(query, productType, records, semanticTerms, undefined, mccRunContext, agentReview.codes);
902
+ const { candidates: _internalCandidates, ...controlResult } = result;
903
+ process.stderr.write(`ALIPAY_AIPAY_INTERNAL:MCC_RESOLUTION=${JSON.stringify(controlResult)}\n`);
904
+ return '';
905
+ }
906
+
907
+ function runMccValidateSelectionCache(args) {
908
+ const productType = required(args, 'product-type');
909
+ productFor({ ...args, 'product-type': productType });
910
+ const records = recordsForArgs(args);
911
+ const semanticTerms = parseMccSemanticTerms(args);
912
+ const mccRunContext = captureMccRunContext();
913
+ if (!mccSelectionReceiptCodes(
914
+ productType,
915
+ required(args, 'query'),
916
+ required(args, 'selection-receipt'),
917
+ records,
918
+ semanticTerms,
919
+ mccRunContext
920
+ )) {
921
+ throw new Error('MCC 候选详情缓存与本轮选择回执不一致');
922
+ }
923
+ return '';
924
+ }
925
+
926
+ export function renderDiscoverySummary(productType, input) {
927
+ const product = productFor({ 'product-type': productType });
928
+ const variables = {
929
+ signingStatus: normalizeEnum(input.signStatus, signingStatusMap, 'signStatus'),
930
+ applicationStatus: normalizeEnum(input.appFlow, applicationFlowMap, 'appFlow')
931
+ };
932
+ if (product.variant === 'AIPAY') {
933
+ variables.serviceStatus = normalizeEnum(input.serviceFlow, serviceFlowMap, 'serviceFlow');
934
+ }
935
+ return renderMessage('onboarding.discovery.summary', product.variant, variables);
936
+ }
937
+
938
+ function splitFieldInput(value) {
939
+ if (Array.isArray(value)) return value.flatMap((item) => splitFieldInput(item));
940
+ if (typeof value !== 'string') throw new Error('missingFields 必须是字符串或字符串数组');
941
+ return value
942
+ .split(/[、,,\n]/u)
943
+ .map((item) => item.trim())
944
+ .filter(Boolean);
945
+ }
946
+
947
+ function normalizeMaterialField(category, raw) {
948
+ let fields;
949
+ if (directFieldAliases.has(raw)) fields = directFieldAliases.get(raw);
950
+ else if (category === 'signing' && /签约.*截图/.test(raw)) fields = ['首页截图', '商品页截图', '支付页截图'];
951
+ else if (/首页.*截图/.test(raw)) fields = ['首页截图'];
952
+ else if (/商品.*截图/.test(raw)) fields = ['商品页截图'];
953
+ else if (/支付.*截图/.test(raw)) fields = ['支付页截图'];
954
+ else if (category === 'signing' && /特殊.*资质/.test(raw)) fields = ['特殊资质材料'];
955
+ else if (category === 'signing' && /^(?:截图|三张截图|3张截图)$/.test(raw)) fields = ['首页截图', '商品页截图', '支付页截图'];
956
+ else if (/服务.*价格|服务.*单价/.test(raw)) fields = ['服务单价'];
957
+ else if (/请求.*示例.*JSON/i.test(raw)) fields = ['请求示例JSON'];
958
+ else if (/iOS.*Bundle/i.test(raw)) fields = ['iOS Bundle ID(bundleId)'];
959
+ else if (/Android.*包名/i.test(raw)) fields = ['Android 应用包名(appPackage)'];
960
+ else if (/Android.*签名/i.test(raw)) fields = ['Android 应用签名摘要(appSign)'];
961
+
962
+ const allowed = materialFieldsByCategory[category] || [];
963
+ if (!fields || fields.some((field) => !allowed.includes(field))) {
964
+ throw new Error(`材料字段不适用于 ${category}:${raw};允许字段:${allowed.join('、')}`);
965
+ }
966
+ return fields;
967
+ }
968
+
969
+ function specialQualificationMaterialHint(raw) {
970
+ if (raw === undefined) return '';
971
+ let groups;
972
+ try {
973
+ groups = JSON.parse(raw);
974
+ } catch {
975
+ throw new Error('special-qualification-json 必须是合法 JSON');
976
+ }
977
+ if (!Array.isArray(groups) || groups.length === 0) return '';
978
+ const lines = [
979
+ '特殊资质要求:',
980
+ '- 以下资质组合任选一种;选择某一组合后,必须同时提供该组合内全部所需材料。',
981
+ '- 请先选择一个资质组合;如所选组合包含需上传材料,请【按该组合列出的顺序】提供特殊资质材料。'
982
+ ];
983
+ let renderedGroups = 0;
984
+ for (const [groupIndex, group] of groups.entries()) {
985
+ if (!group || typeof group !== 'object' || !Array.isArray(group.qualifications)) {
986
+ throw new Error('special-qualification-json 每个组合必须包含 qualifications 数组');
987
+ }
988
+ let uploadIndex = 0;
989
+ const parts = group.qualifications.map((qualification) => {
990
+ const qualName = String(qualification?.qualName || '').replace(/[\u0000-\u001f\u007f|`]/gu, ' ').trim();
991
+ if (!qualName) throw new Error('special-qualification-json 存在空资质名称');
992
+ const attaType = qualification?.attaType === null || qualification?.attaType === undefined || qualification?.attaType === ''
993
+ ? ''
994
+ : String(qualification.attaType).replace(/[\u0000-\u001f\u007f|`]/gu, ' ').trim();
995
+ if (!attaType) return `${qualName}(无需上传,按文字条件核验)`;
996
+ uploadIndex += 1;
997
+ return `${qualName}(第 ${uploadIndex} 份特殊资质材料)`;
998
+ });
999
+ if (parts.length === 0) continue;
1000
+ renderedGroups += 1;
1001
+ lines.push(`- 资质组合 ${groupIndex + 1}:${parts.join(' + ')}`);
1002
+ }
1003
+ if (renderedGroups === 0) return '';
1004
+ lines.push('- 如选择的组合只包含“无需上传”的文字条件,请在回复中说明选择该组合并确认满足该文字条件。');
1005
+ return lines.join('\n');
1006
+ }
1007
+
1008
+ function normalizeMissingFields(category, missingFields) {
1009
+ const normalized = [];
1010
+ for (const raw of splitFieldInput(missingFields)) {
1011
+ for (const field of normalizeMaterialField(category, raw)) {
1012
+ if (!normalized.includes(field)) normalized.push(field);
1013
+ }
1014
+ }
1015
+ if (normalized.length === 0) throw new Error('缺少 missingFields');
1016
+ return normalized.join('、');
1017
+ }
1018
+
1019
+ async function runMaterialCollect(args) {
1020
+ const productType = required(args, 'product-type');
1021
+ productFor({ ...args, 'product-type': productType });
1022
+ const category = required(args, 'category');
1023
+ const categoryName = categoryNames[category];
1024
+ if (!categoryName) throw new Error('category 非法,仅支持 signing、service、application');
1025
+
1026
+ const state = required(args, 'state').toUpperCase().replace(/-/g, '_');
1027
+ const stateRule = materialStates[state];
1028
+ if (!stateRule) throw new Error('state 非法,仅支持 INITIAL、PARTIAL、INVALID、APP_WEB_INITIAL、APP_MOBILE_INITIAL');
1029
+
1030
+ if (state === 'APP_WEB_INITIAL' || state === 'APP_MOBILE_INITIAL') {
1031
+ if (category !== 'application') throw new Error(`${state} 只适用于 application 类别`);
1032
+ if (state === 'APP_WEB_INITIAL' && productType === 'apppay') {
1033
+ throw new Error('APP_WEB_INITIAL 只适用于 aipay、webpay');
1034
+ }
1035
+ if (state === 'APP_MOBILE_INITIAL' && productType !== 'apppay') {
1036
+ throw new Error('APP_MOBILE_INITIAL 只适用于 apppay');
1037
+ }
1038
+ return renderMessage('materials.category.collect', stateRule.variant, {
1039
+ categoryName: '应用资料',
1040
+ currentStatus: stateRule.currentStatus,
1041
+ missingFields: state === 'APP_WEB_INITIAL' ? '应用公钥' : '应用平台和对应资料、应用公钥'
1042
+ }, { productType });
1043
+ }
1044
+
1045
+ const input = optional(args, 'missing-fields') !== undefined
1046
+ ? { missingFields: args['missing-fields'] }
1047
+ : await readStdin();
1048
+ const variant = state === 'INITIAL' && category === 'service'
1049
+ ? 'SERVICE_INITIAL'
1050
+ : stateRule.variant;
1051
+ const missingFields = normalizeMissingFields(category, input.missingFields);
1052
+ if (category === 'application' && productType !== 'apppay') {
1053
+ const incompatibleFields = missingFields.split('、').filter((field) => field !== '应用公钥');
1054
+ if (incompatibleFields.length > 0) {
1055
+ throw new Error(`${productType} 的应用资料只允许应用公钥`);
1056
+ }
1057
+ }
1058
+ return renderMessage('materials.category.collect', variant, {
1059
+ categoryName,
1060
+ currentStatus: stateRule.currentStatus,
1061
+ missingFields,
1062
+ specialQualificationHint: category === 'signing'
1063
+ ? specialQualificationMaterialHint(optional(args, 'special-qualification-json'))
1064
+ : ''
1065
+ }, { productType });
1066
+ }
1067
+
1068
+ function renderApplicationCandidates(applicationType, applicationCandidates, selectionState = 'initial') {
1069
+ const onlineCount = applicationCandidates.filter((candidate) => candidate?.status === 'ON_LINE').length;
1070
+ const variant = applicationCandidates.length === 0
1071
+ ? 'EMPTY'
1072
+ : onlineCount === 0
1073
+ ? 'PENDING_ONLY'
1074
+ : 'ONLINE_WITH_OPTIONAL_PENDING';
1075
+ const facts = renderMessage('application.candidates.facts', variant, { applicationType, applicationCandidates });
1076
+ if (applicationCandidates.length === 0) return facts;
1077
+ const selectionVariant = `${onlineCount === 0 ? 'PENDING' : 'ONLINE'}_${applicationType === 'WEBAPP' ? 'WEB' : 'MOBILE'}_OR_NEW`;
1078
+ const selection = renderMessage('application.candidate.select', selectionVariant, {});
1079
+ const message = selectionState === 'initial'
1080
+ ? `${facts}\n\n${selection}`
1081
+ : `${facts}\n\n${renderMessage('application.candidate.invalid', 'DEFAULT', {})}\n\n${selection}`;
1082
+ return message;
1083
+ }
1084
+
1085
+ async function runApplicationCandidates(args) {
1086
+ const applicationType = required(args, 'application-type');
1087
+ if (!['WEBAPP', 'MOBILEAPP'].includes(applicationType)) throw new Error('application-type 非法,仅支持 WEBAPP、MOBILEAPP');
1088
+ const selectionState = optional(args, 'selection-state') ?? 'initial';
1089
+ if (!['initial', 'invalid'].includes(selectionState)) throw new Error('selection-state 非法,仅支持 initial、invalid');
1090
+ const applicationCandidates = optional(args, 'candidates-json') !== undefined
1091
+ ? parseJsonArg(args, 'candidates-json', '应用候选输入')
1092
+ : await readStdin();
1093
+ if (!Array.isArray(applicationCandidates)) throw new Error('应用候选输入必须是 JSON 数组');
1094
+ canonicalCandidates(applicationCandidates);
1095
+ if (selectionState === 'invalid' && applicationCandidates.length === 0) {
1096
+ throw new Error('应用候选为空时不得使用 invalid 选择状态');
1097
+ }
1098
+ const message = renderApplicationCandidates(applicationType, applicationCandidates, selectionState);
1099
+ return applicationCandidates.length === 0
1100
+ ? message
1101
+ : requireCustomerMessageDelivery('application.candidate.select', message);
1102
+ }
1103
+
1104
+ async function runServiceCandidates(args) {
1105
+ const selectionState = optional(args, 'selection-state') ?? 'initial';
1106
+ if (!['initial', 'invalid'].includes(selectionState)) {
1107
+ throw new Error('selection-state 非法,仅支持 initial、invalid');
1108
+ }
1109
+ const serviceCandidates = optional(args, 'candidates-json') !== undefined
1110
+ ? parseJsonArg(args, 'candidates-json', '服务候选输入')
1111
+ : await readStdin();
1112
+ if (!Array.isArray(serviceCandidates)) throw new Error('服务候选输入必须是 JSON 数组');
1113
+ if (selectionState === 'invalid' && serviceCandidates.length === 0) {
1114
+ throw new Error('服务候选为空时不得使用 invalid 选择状态');
1115
+ }
1116
+ const variant = serviceCandidates.length === 0 ? 'EMPTY' : 'DEFAULT';
1117
+ const facts = renderMessage('service.candidates.facts', variant, { serviceCandidates });
1118
+ if (serviceCandidates.length === 0) return facts;
1119
+ const selection = renderMessage('service.candidate.select', 'DEFAULT', {});
1120
+ if (selectionState === 'initial') return `${facts}\n\n${selection}`;
1121
+ const invalid = renderMessage('service.candidate.invalid', 'DEFAULT', {});
1122
+ return `${facts}\n\n${invalid}\n\n${selection}`;
1123
+ }
1124
+
1125
+ function canonicalCandidates(candidates) {
1126
+ if (!Array.isArray(candidates)) throw new Error('应用候选快照必须是 JSON 数组');
1127
+ const allowedKeys = new Set(['appId', 'appName', 'status']);
1128
+ const seenIds = new Set();
1129
+ const rows = candidates.map((candidate) => {
1130
+ if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
1131
+ throw new Error('应用候选快照项必须是 JSON 对象');
1132
+ }
1133
+ const keys = Object.keys(candidate);
1134
+ if (keys.some((key) => !allowedKeys.has(key))
1135
+ || !['appId', 'appName', 'status'].every((key) => Object.prototype.hasOwnProperty.call(candidate, key))) {
1136
+ throw new Error('应用候选快照项必须且只能包含 appId、appName、status');
1137
+ }
1138
+ if (![candidate.appId, candidate.appName, candidate.status].every((value) => typeof value === 'string')) {
1139
+ throw new Error('应用候选快照字段必须是字符串');
1140
+ }
1141
+ if (candidate.appId && candidate.appId !== '未知') {
1142
+ if (seenIds.has(candidate.appId)) throw new Error(`应用候选快照包含重复 appId: ${candidate.appId}`);
1143
+ seenIds.add(candidate.appId);
1144
+ }
1145
+ return {
1146
+ appId: candidate.appId,
1147
+ appName: candidate.appName,
1148
+ status: candidate.status
1149
+ };
1150
+ });
1151
+ return JSON.stringify(rows.sort((left, right) => {
1152
+ const leftValue = JSON.stringify(left);
1153
+ const rightValue = JSON.stringify(right);
1154
+ return leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0;
1155
+ }));
1156
+ }
1157
+
1158
+ async function runApplicationRecheck(args) {
1159
+ const applicationType = required(args, 'application-type');
1160
+ if (!['WEBAPP', 'MOBILEAPP'].includes(applicationType)) throw new Error('application-type 非法,仅支持 WEBAPP、MOBILEAPP');
1161
+ const input = optional(args, 'previous-candidates-json') !== undefined || optional(args, 'current-candidates-json') !== undefined
1162
+ ? {
1163
+ previousCandidates: parseJsonArg(args, 'previous-candidates-json', '上一轮应用候选快照'),
1164
+ currentCandidates: parseJsonArg(args, 'current-candidates-json', '当前应用候选快照')
1165
+ }
1166
+ : await readStdin();
1167
+ if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('应用候选重查输入必须是 JSON 对象');
1168
+ const previousCanonical = canonicalCandidates(input.previousCandidates);
1169
+ const currentCanonical = canonicalCandidates(input.currentCandidates);
1170
+ // Rendering both snapshots applies the same content and sensitivity checks even on the silent path.
1171
+ renderApplicationCandidates(applicationType, input.previousCandidates);
1172
+ const output = renderApplicationCandidates(applicationType, input.currentCandidates);
1173
+ if (previousCanonical === currentCanonical) {
1174
+ process.stderr.write(`${internalPrefix}APPLICATION_RECHECK_RESULT=UNCHANGED\n`);
1175
+ return '';
1176
+ }
1177
+ const result = input.currentCandidates.length === 0 ? 'CHANGED_EMPTY' : 'CHANGED_NONEMPTY';
1178
+ process.stderr.write(`${internalPrefix}APPLICATION_RECHECK_RESULT=${result}\n`);
1179
+ process.stderr.write(`${internalPrefix}APPLICATION_RECHECK_MESSAGE=READY\n`);
1180
+ return output;
1181
+ }
1182
+
1183
+ function closeoutInputFromArgs(args) {
1184
+ const retiredArgs = [
1185
+ 'input-json',
1186
+ 'product-name',
1187
+ 'process-result-title',
1188
+ 'application-operation-actual-status',
1189
+ 'application-operation-app-id',
1190
+ 'application-operation-next-action',
1191
+ 'application-operation-app-key-save-path',
1192
+ 'next-flow-reminder'
1193
+ ].filter((name) => Object.prototype.hasOwnProperty.call(args, name));
1194
+ if (retiredArgs.length > 0) {
1195
+ throw new Error(`closeout 已移除旧参数:${retiredArgs.map((name) => `--${name}`).join('、')}`);
1196
+ }
1197
+ const productType = required(args, 'product-type');
1198
+ const product = productFor({ 'product-type': productType });
1199
+ const rootIntent = required(args, 'root-intent');
1200
+ if (!['onboarding_only', 'full_process'].includes(rootIntent)) {
1201
+ throw new Error('root-intent 非法,仅支持 onboarding_only、full_process');
1202
+ }
1203
+ if (productType !== 'aipay' && Object.prototype.hasOwnProperty.call(args, 'service-result')) {
1204
+ throw new Error('webpay/apppay closeout 的 service-result 由 runner 派生,禁止传入 --service-result');
1205
+ }
1206
+ if (rootIntent === 'onboarding_only' && Object.prototype.hasOwnProperty.call(args, 'integration-result')) {
1207
+ throw new Error('onboarding_only closeout 的 integration-result 由 runner 派生,禁止传入 --integration-result');
1208
+ }
1209
+ const signingResult = required(args, 'signing-result');
1210
+ const applicationResult = required(args, 'application-result');
1211
+ const remainingActions = required(args, 'remaining-actions');
1212
+ const serviceResult = productType === 'aipay' ? required(args, 'service-result') : '无需处理';
1213
+ const integrationResult = rootIntent === 'onboarding_only'
1214
+ ? '不在本轮执行范围'
1215
+ : required(args, 'integration-result');
1216
+ const manualVerificationItems = optional(args, 'manual-verification-items');
1217
+ const hasManualVerificationItems = manualVerificationItems !== undefined && manualVerificationItems !== '无';
1218
+ const serviceComplete = productType === 'aipay'
1219
+ ? ['已复用', '创建成功,状态未取得', '修改成功,状态未取得', '已完成'].includes(serviceResult)
1220
+ : true;
1221
+ const branchesComplete = signingResult === '已生效'
1222
+ && serviceComplete
1223
+ && applicationResult === '已上线且配置完整'
1224
+ && remainingActions === '无'
1225
+ && !hasManualVerificationItems;
1226
+ const input = {
1227
+ productName: product.productName,
1228
+ processResultTitle: branchesComplete ? '支付产品-产品开通已完成' : '流程进展',
1229
+ integrationResult,
1230
+ signingResult,
1231
+ serviceResult,
1232
+ applicationResult,
1233
+ remainingActions,
1234
+ nextFlowReminder: rootIntent === 'onboarding_only'
1235
+ ? '还需完成支付产品-代码开发,才能在业务系统中实际发起支付。'
1236
+ : '代码开发已在本轮执行;后续只按上表实际待办继续。'
1237
+ };
1238
+ const optionalMapping = {
1239
+ 'sandbox-config-state': 'sandboxConfigState',
1240
+ 'manual-verification-items': 'manualVerificationItems'
1241
+ };
1242
+ for (const [argName, fieldName] of Object.entries(optionalMapping)) {
1243
+ if (Object.prototype.hasOwnProperty.call(args, argName)) input[fieldName] = required(args, argName);
1244
+ }
1245
+ return input;
1246
+ }
1247
+
1248
+ async function runCloseout(args) {
1249
+ const variables = closeoutInputFromArgs(args);
1250
+ if (args['product-type'] === 'aipay'
1251
+ && ['已复用', '创建成功,状态未取得', '修改成功,状态未取得', '已完成'].includes(args['service-result'])) {
1252
+ emitStageMetric('onboarding.confirm_service');
1253
+ }
1254
+ const rendered = renderMessage('process.partial_result', 'DEFAULT', variables);
1255
+ return rendered;
1256
+ }
1257
+
1258
+ function runServiceCreateSummary(args) {
1259
+ return renderMessage('service.create.summary', 'DEFAULT', {
1260
+ serviceName: required(args, 'service-name'),
1261
+ serviceDescription: required(args, 'service-description'),
1262
+ serviceUrl: required(args, 'service-url'),
1263
+ servicePricing: required(args, 'service-pricing'),
1264
+ serviceSchema: required(args, 'service-schema')
1265
+ });
1266
+ }
1267
+
1268
+ function runServiceUpdateConfirm(args) {
1269
+ return renderMessage('onboarding.write.confirm', 'SERVICE_UPDATE_ONLY', {
1270
+ sessionSummary: required(args, 'session-summary'),
1271
+ subjectSummary: required(args, 'subject-summary'),
1272
+ productName: 'AI 按量付费',
1273
+ actionTypes: '服务修改',
1274
+ serviceId: required(args, 'service-id'),
1275
+ serviceName: required(args, 'service-name'),
1276
+ serviceDescription: required(args, 'service-description'),
1277
+ serviceUrl: required(args, 'service-url'),
1278
+ servicePricing: required(args, 'service-pricing'),
1279
+ serviceSchema: required(args, 'service-schema')
1280
+ });
1281
+ }
1282
+
1283
+ function runHarmonyManualCreate() {
1284
+ return renderMessage('application.harmony.manual_create', 'DEFAULT', {});
1285
+ }
1286
+
1287
+ function runApplicationReuseNoKey(args) {
1288
+ return renderMessage('application.reuse.no_key', 'DEFAULT', {
1289
+ appId: required(args, 'app-id')
1290
+ });
1291
+ }
1292
+
1293
+ function runSigningResult(args) {
1294
+ const variables = {
1295
+ actualStatus: required(args, 'actual-status'),
1296
+ nextAction: required(args, 'next-action')
1297
+ };
1298
+ const feeDisplay = optional(args, 'fee-display');
1299
+ if (feeDisplay !== undefined) variables.feeDisplay = feeDisplay;
1300
+ return renderMessage('signing.operation.result', feeDisplay === undefined ? 'WITHOUT_FEE' : 'WITH_FEE', variables);
1301
+ }
1302
+
1303
+ function runServiceResult(args) {
1304
+ return renderMessage('service.operation.result', 'DEFAULT', {
1305
+ serviceId: required(args, 'service-id'),
1306
+ actualResult: required(args, 'actual-result')
1307
+ });
1308
+ }
1309
+
1310
+ function runApplicationResult(args) {
1311
+ const actualStatus = required(args, 'actual-status');
1312
+ if (actualStatus === '需人工配置') {
1313
+ if (optional(args, 'next-action') !== undefined) throw new Error('需人工配置的下一步由消息目录固定,不接收 --next-action');
1314
+ if (optional(args, 'app-key-save-path') !== undefined) throw new Error('需人工配置表示支付宝公钥未本地保存,禁止传入 --app-key-save-path');
1315
+ return requireCustomerMessageDelivery('application.operation.result', renderMessage('application.operation.result', 'ALIPAY_PUBLIC_KEY_MANUAL_CONFIGURATION', {
1316
+ appId: required(args, 'app-id'),
1317
+ actualStatus
1318
+ }));
1319
+ }
1320
+ const variables = {
1321
+ appId: required(args, 'app-id'),
1322
+ actualStatus,
1323
+ nextAction: required(args, 'next-action')
1324
+ };
1325
+ const appKeySavePath = optional(args, 'app-key-save-path');
1326
+ if (appKeySavePath !== undefined) variables.appKeySavePath = appKeySavePath;
1327
+ return requireCustomerMessageDelivery('application.operation.result', renderMessage('application.operation.result', 'DEFAULT', variables));
1328
+ }
1329
+
1330
+ export async function runOnboardingMessageCli(argv = process.argv) {
1331
+ const { command, args } = parseArgs(argv);
1332
+ if (command === 'platform-entry') {
1333
+ const message = runPlatformEntry(args);
1334
+ if (message) console.log(message);
1335
+ return;
1336
+ }
1337
+ if (command === 'product-clarify') {
1338
+ console.log(runProductClarify());
1339
+ return;
1340
+ }
1341
+ if (command === 'enterprise-unsupported') {
1342
+ console.log(runEnterpriseUnsupported(args));
1343
+ return;
1344
+ }
1345
+ if (command === 'mcc-clarify') {
1346
+ console.log(await runMccClarify(args));
1347
+ return;
1348
+ }
1349
+ if (command === 'mcc-resolve-plan') {
1350
+ const message = await runMccResolvePlan(args);
1351
+ if (message) console.log(message);
1352
+ return;
1353
+ }
1354
+ if (command === 'mcc-match-codes') {
1355
+ const message = await runMccMatchCodes(args);
1356
+ if (message) console.log(message);
1357
+ return;
1358
+ }
1359
+ if (command === 'mcc-validate-selection-cache') {
1360
+ const message = runMccValidateSelectionCache(args);
1361
+ if (message) console.log(message);
1362
+ return;
1363
+ }
1364
+ if (command === 'plan-confirm') {
1365
+ console.log(await runPlanConfirm(args));
1366
+ return;
1367
+ }
1368
+ if (command === 'material-collect') {
1369
+ console.log(await runMaterialCollect(args));
1370
+ return;
1371
+ }
1372
+ if (command === 'application-candidates') {
1373
+ console.log(await runApplicationCandidates(args));
1374
+ return;
1375
+ }
1376
+ if (command === 'service-candidates') {
1377
+ console.log(await runServiceCandidates(args));
1378
+ return;
1379
+ }
1380
+ if (command === 'application-recheck') {
1381
+ const message = await runApplicationRecheck(args);
1382
+ if (message) console.log(message);
1383
+ return;
1384
+ }
1385
+ if (command === 'closeout') {
1386
+ console.log(await runCloseout(args));
1387
+ return;
1388
+ }
1389
+ if (command === 'service-create-summary') {
1390
+ console.log(runServiceCreateSummary(args));
1391
+ return;
1392
+ }
1393
+ if (command === 'service-update-confirm') {
1394
+ console.log(runServiceUpdateConfirm(args));
1395
+ return;
1396
+ }
1397
+ if (command === 'application-harmony-manual-create') {
1398
+ console.log(runHarmonyManualCreate());
1399
+ return;
1400
+ }
1401
+ if (command === 'application-reuse-no-key') {
1402
+ console.log(runApplicationReuseNoKey(args));
1403
+ return;
1404
+ }
1405
+ if (command === 'signing-result') {
1406
+ console.log(runSigningResult(args));
1407
+ return;
1408
+ }
1409
+ if (command === 'service-result') {
1410
+ console.log(runServiceResult(args));
1411
+ return;
1412
+ }
1413
+ if (command === 'application-result') {
1414
+ console.log(runApplicationResult(args));
1415
+ return;
1416
+ }
1417
+ throw new Error('用法: onboarding_message_runner.mjs <platform-entry|product-clarify|enterprise-unsupported|mcc-resolve-plan|mcc-match-codes|mcc-validate-selection-cache|plan-confirm|mcc-clarify|material-collect|application-candidates|service-candidates|application-recheck|closeout|service-create-summary|service-update-confirm|application-harmony-manual-create|application-reuse-no-key|signing-result|service-result|application-result> ...');
1418
+ }
1419
+
1420
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
1421
+ runOnboardingMessageCli().catch((error) => {
1422
+ console.error(`ONBOARDING_MESSAGE_ERROR:${error.message}`);
1423
+ process.exitCode = 1;
1424
+ });
1425
+ }