@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,841 @@
1
+ #!/bin/bash
2
+ #=============================================================================
3
+ # 脚本名称: error_handler.sh
4
+ # 功能描述: 签约流程统一错误检测与处理,供所有 .sh 脚本 source 引用
5
+ # 源文件: error-handling.md(本文档为可执行版本)
6
+ # 用法: source scripts/error_handler.sh
7
+ # if ! handle_error "$RESULT"; then return 1; fi
8
+ #=============================================================================
9
+
10
+ ERROR_HANDLER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ COMMON_SCRIPT="${ERROR_HANDLER_DIR}/../../../normal/scripts/common.sh"
12
+ ERROR_RENDERER="${ERROR_HANDLER_DIR}/../../../normal/scripts/render_customer_message.mjs"
13
+ if [ -f "$COMMON_SCRIPT" ]; then
14
+ # shellcheck source=/dev/null
15
+ source "$COMMON_SCRIPT"
16
+ init_alipay_cli_context
17
+ else
18
+ echo "❌ 缺少公共脚本: $COMMON_SCRIPT"
19
+ return 1 2>/dev/null || exit 1
20
+ fi
21
+
22
+ render_error_message() {
23
+ local MESSAGE_ID="$1" VARIANT="$2" INPUT_JSON="$3"
24
+ if printf '%s' "$INPUT_JSON" \
25
+ | ALIPAY_AIPAY_RENDERER_MANAGED_CALLER=error_handler.sh node "$ERROR_RENDERER" "$MESSAGE_ID" --variant "$VARIANT"; then
26
+ [ "${ALIPAY_AIPAY_CAPTURE_CHILD_OUTPUT:-0}" != "1" ] || emit_control "CHILD_CUSTOMER_MESSAGE_RENDERED"
27
+ return 0
28
+ fi
29
+ return 1
30
+ }
31
+
32
+ # ─── MCP 信封解包 ──────────────────────────────────────────────────────────
33
+ # alipay-cli mcp call 返回 MCP 协议信封:
34
+ # { "content": [ { "text": "<业务JSON>", "type": "text" } ] }
35
+ # 业务 JSON(含 success / resultObj / errorCode)被包在 content[0].text 里。
36
+ # 本函数负责解包:若检测到信封结构,提取 content[0].text;否则原样返回。
37
+ # 当 stdout 混有日志时,只接受唯一可确定的 MCP 信封或 JSON;候选不唯一时
38
+ # 保留原始文本,让后续错误检测安全阻断,禁止猜测业务响应。
39
+ #
40
+ # 对于非 mcp call 的命令(如 alipay-cli login / whoami),返回不带信封,
41
+ # 保证这些命令的原样透传。
42
+ extract_unique_json() {
43
+ local RAW="$1"
44
+ printf '%s' "$RAW" | jq -Rsc '
45
+ explode as $chars |
46
+ reduce range(0; ($chars | length)) as $index (
47
+ {
48
+ depth: 0,
49
+ start: null,
50
+ in_string: false,
51
+ escaped: false,
52
+ raw_candidates: []
53
+ };
54
+ ($chars[$index]) as $char |
55
+ if .depth == 0 then
56
+ if $char == 123 or $char == 91 then
57
+ .depth = 1 |
58
+ .start = $index |
59
+ .in_string = false |
60
+ .escaped = false
61
+ else
62
+ .
63
+ end
64
+ elif .in_string then
65
+ if .escaped then
66
+ .escaped = false
67
+ elif $char == 92 then
68
+ .escaped = true
69
+ elif $char == 34 then
70
+ .in_string = false
71
+ else
72
+ .
73
+ end
74
+ elif $char == 34 then
75
+ .in_string = true
76
+ elif $char == 123 or $char == 91 then
77
+ .depth += 1
78
+ elif $char == 125 or $char == 93 then
79
+ .depth -= 1 |
80
+ if .depth == 0 then
81
+ ($chars[.start:($index + 1)] | implode) as $candidate |
82
+ .raw_candidates += [$candidate] |
83
+ .start = null
84
+ else
85
+ .
86
+ end
87
+ else
88
+ .
89
+ end
90
+ ) |
91
+ [
92
+ .raw_candidates[] |
93
+ fromjson? |
94
+ select(type == "object" or type == "array")
95
+ ] |
96
+ unique as $candidates |
97
+ (
98
+ $candidates |
99
+ map(select(
100
+ type == "object" and
101
+ (.content | type) == "array" and
102
+ any(.content[]?; (.text? | type) == "string")
103
+ )) |
104
+ unique
105
+ ) as $envelopes |
106
+ if ($envelopes | length) == 1 then
107
+ {status: "ok", value: $envelopes[0]}
108
+ elif ($envelopes | length) == 0 and ($candidates | length) == 1 then
109
+ {status: "ok", value: $candidates[0]}
110
+ elif ($candidates | length) == 0 then
111
+ {status: "none"}
112
+ else
113
+ {status: "ambiguous"}
114
+ end
115
+ ' 2>/dev/null
116
+ }
117
+
118
+ # 对客错误详情只保留受限单行文本。解析和错误分类仍使用原始值,
119
+ # 但任何疑似凭据、临时 URL 或密钥内容都不能进入普通终端输出。
120
+ sanitize_customer_error_text() {
121
+ local VALUE="${1:-}" NORMALIZED
122
+ NORMALIZED=$(printf '%s' "$VALUE" | tr '\000-\037\177' ' ' | awk '{$1=$1; print}')
123
+ if [ -z "$NORMALIZED" ]; then
124
+ echo "未知错误"
125
+ return
126
+ fi
127
+ if printf '%s' "$NORMALIZED" | grep -qiE '\{\{|\}\}|<INTERNAL_'; then
128
+ echo "错误详情包含内部标记或模板占位符,已隐藏"
129
+ return
130
+ fi
131
+ if printf '%s' "$NORMALIZED" | grep -qiE \
132
+ 'https?://|verification_url|device[_-]?code|authorization:[[:space:]]*bearer|payment-proof|-----BEGIN|private[_ -]?key|public[_ -]?key|["'\'']?(password|passwd|secret|api[_-]?key|access[_-]?token)["'\'']?[[:space:]]*[:=]|(^|[^[:alnum:]_])token[=:]'; then
133
+ echo "错误详情包含临时链接或敏感字段,已隐藏"
134
+ return
135
+ fi
136
+ printf '%s' "$NORMALIZED" | cut -c1-1000
137
+ }
138
+
139
+ unwrap_mcp() {
140
+ local RAW="$1"
141
+ if [ -z "$RAW" ]; then
142
+ echo ""
143
+ return
144
+ fi
145
+
146
+ local ANALYSIS STATUS JSON
147
+ ANALYSIS=$(extract_unique_json "$RAW")
148
+ STATUS=$(echo "$ANALYSIS" | jq -r '.status // "none"' 2>/dev/null)
149
+ case "$STATUS" in
150
+ ok)
151
+ JSON=$(echo "$ANALYSIS" | jq -c '.value' 2>/dev/null)
152
+ ;;
153
+ ambiguous)
154
+ echo "CLI 输出包含多个 JSON 候选,无法唯一解析"
155
+ return
156
+ ;;
157
+ *)
158
+ echo "$RAW"
159
+ return
160
+ ;;
161
+ esac
162
+
163
+ local TEXT
164
+ TEXT=$(echo "$JSON" | jq -r '
165
+ if (.content | type) == "array" then
166
+ [ .content[]? | .text? | select(type == "string") ] as $texts |
167
+ ([
168
+ $texts[] |
169
+ fromjson? |
170
+ select(type == "object" or type == "array") |
171
+ tojson
172
+ ] | unique) as $jsonTexts |
173
+ if ($jsonTexts | length) == 1 then
174
+ $jsonTexts[0]
175
+ elif ($jsonTexts | length) == 0 and ($texts | length) > 0 then
176
+ $texts[0]
177
+ else
178
+ "CLI 输出包含多个 JSON 候选,无法唯一解析"
179
+ end
180
+ else
181
+ empty
182
+ end
183
+ ' 2>/dev/null)
184
+ if [ -n "$TEXT" ]; then
185
+ echo "$TEXT"
186
+ else
187
+ echo "$JSON"
188
+ fi
189
+ }
190
+
191
+ # ─── JSON 错误字段提取 ─────────────────────────────────────────────────────
192
+ # 部分 MCP 后端会把真实业务响应包在 data/response 或 errorContext.errorStack 下。
193
+ # 下面的提取函数优先定位同一个错误对象,避免 success=false 时丢失真实错误体。
194
+ extract_first_named_field() {
195
+ local JSON="$1"
196
+ local FIELD="$2"
197
+ echo "$JSON" | jq -r --arg field "$FIELD" '
198
+ [.. | objects | .[$field]? | select(. != null and (tostring != ""))][0] // ""
199
+ ' 2>/dev/null
200
+ }
201
+
202
+ extract_error_object() {
203
+ echo "$1" | jq -c '
204
+ [
205
+ .,
206
+ (.error? | objects),
207
+ (.data? | objects),
208
+ (.data.error? | objects),
209
+ (.response? | objects),
210
+ (.data.response? | objects),
211
+ (.errorContext.errorStack[]? | objects)
212
+ |
213
+ select(
214
+ ((.errorCode? | type) == "string" and .errorCode != "") or
215
+ ((.errorCode? | type) == "number") or
216
+ ((.errorCode? | type) == "object" and
217
+ (((.errorCode.code? // "") | tostring) != "" or
218
+ (((.errorCode.status? // "") | tostring) != "")))
219
+ )
220
+ ][0] // empty
221
+ ' 2>/dev/null
222
+ }
223
+
224
+ extract_error_code() {
225
+ local ERROR_OBJECT
226
+ ERROR_OBJECT=$(extract_error_object "$1")
227
+ if [ -n "$ERROR_OBJECT" ]; then
228
+ echo "$ERROR_OBJECT" | jq -r '
229
+ if (.errorCode | type) == "string" then .errorCode
230
+ elif (.errorCode | type) == "object" then (.errorCode.code // "")
231
+ else ""
232
+ end
233
+ ' 2>/dev/null
234
+ return
235
+ fi
236
+
237
+ echo ""
238
+ }
239
+
240
+ extract_error_message() {
241
+ local JSON="$1"
242
+ local ERROR_OBJECT
243
+ ERROR_OBJECT=$(extract_error_object "$JSON")
244
+ if [ -n "$ERROR_OBJECT" ]; then
245
+ echo "$ERROR_OBJECT" | jq -r '
246
+ .errorMessage //
247
+ .errorMsg //
248
+ .error.message //
249
+ .message //
250
+ (if (.errorCode | type) == "object" then (.errorCode.desc // .errorCode.message) else empty end) //
251
+ "未知错误"
252
+ ' 2>/dev/null
253
+ return
254
+ fi
255
+
256
+ echo "$JSON" | jq -r '
257
+ if .data.success? == false then
258
+ [
259
+ .data.subMsg?,
260
+ .data.msg?,
261
+ .subMsg?,
262
+ .errorMessage?,
263
+ .errorMsg?,
264
+ .message?,
265
+ (.error? | objects | .message?),
266
+ .msg?,
267
+ (
268
+ .. | objects |
269
+ (.errorMessage?, .errorMsg?, .message?, (.error? | objects | .message?))
270
+ )
271
+ ] |
272
+ [
273
+ .[] |
274
+ select(
275
+ . != null and
276
+ (tostring != "") and
277
+ ((tostring | ascii_downcase) != "success")
278
+ )
279
+ ][0] // "未知错误"
280
+ else
281
+ [
282
+ .. | objects |
283
+ (.errorMessage?, .errorMsg?, .message?, (.error? | objects | .message?)) |
284
+ select(. != null and (tostring != ""))
285
+ ][0] // "未知错误"
286
+ end
287
+ ' 2>/dev/null
288
+ }
289
+
290
+ extract_biz_tips() {
291
+ local ERROR_OBJECT
292
+ ERROR_OBJECT=$(extract_error_object "$1")
293
+ if [ -n "$ERROR_OBJECT" ]; then
294
+ echo "$ERROR_OBJECT" | jq -r '.bizTips // ""' 2>/dev/null
295
+ return
296
+ fi
297
+
298
+ extract_first_named_field "$1" "bizTips"
299
+ }
300
+
301
+ extract_need_retry() {
302
+ local JSON="$1"
303
+ local ERROR_OBJECT
304
+ ERROR_OBJECT=$(extract_error_object "$JSON")
305
+ if [ -n "$ERROR_OBJECT" ]; then
306
+ echo "$ERROR_OBJECT" | jq -r 'if .needRetry == true then "true" else "false" end' 2>/dev/null
307
+ return
308
+ fi
309
+
310
+ echo "$JSON" | jq -r '
311
+ if any(.. | objects; .needRetry? == true) then "true" else "false" end
312
+ ' 2>/dev/null
313
+ }
314
+
315
+ extract_error_object_field() {
316
+ local JSON="$1"
317
+ local FIELD="$2"
318
+ local ERROR_OBJECT
319
+ ERROR_OBJECT=$(extract_error_object "$JSON")
320
+ if [ -n "$ERROR_OBJECT" ]; then
321
+ echo "$ERROR_OBJECT" | jq -r --arg field "$FIELD" '
322
+ .[$field] //
323
+ (if (.errorCode | type) == "object" then (.errorCode[$field] // "") else "" end)
324
+ ' 2>/dev/null
325
+ return
326
+ fi
327
+
328
+ extract_first_named_field "$JSON" "$FIELD"
329
+ }
330
+
331
+ extract_success_state() {
332
+ local JSON="$1"
333
+ echo "$JSON" | jq -r '
334
+ if .success? == false or
335
+ ((.data? | type) == "object" and .data.success? == false) or
336
+ ((.response? | type) == "object" and .response.success? == false) or
337
+ ((.data.response? | type) == "object" and .data.response.success? == false) then "false"
338
+ elif .success? == true or
339
+ ((.data? | type) == "object" and .data.success? == true) or
340
+ ((.response? | type) == "object" and .response.success? == true) or
341
+ ((.data.response? | type) == "object" and .data.response.success? == true) or
342
+ (((.code? // "") | tostring) == "10000") then "true"
343
+ else "null"
344
+ end
345
+ ' 2>/dev/null
346
+ }
347
+
348
+ extract_checked_errors() {
349
+ local JSON="$1"
350
+ echo "$JSON" | jq -c '
351
+ [
352
+ .. | objects |
353
+ (
354
+ .checkedError?,
355
+ (.resultObj? | objects | .checkedError?),
356
+ (.data? | objects | .checkedError?)
357
+ ) |
358
+ select(. != null)
359
+ ][0] // empty
360
+ ' 2>/dev/null
361
+ }
362
+
363
+ # ─── 统一错误检测 ──────────────────────────────────────────────────────────
364
+ # 参数: $1 - CLI 执行结果文本(原始输出,含信封或不含)
365
+ # $2 - 可选调用上下文;MCP_AUTHENTICATED_CALL 允许识别资源响应中的明确未登录状态
366
+ # 返回: MCP_AUTH_ERROR | MCP_SERVICE_ERROR | AUTH_MISMATCH | SERVICE_UNSTABLE | CLI_LOCAL_FS_PERMISSION | ERROR:xxx | CLI_ERROR:xxx | SUCCESS
367
+ detect_error() {
368
+ local CLI_RESULT="$1"
369
+ local ERROR_CONTEXT="${2:-}"
370
+
371
+ # 空输入视为异常,不应被当作 SUCCESS
372
+ if [ -z "$CLI_RESULT" ]; then
373
+ echo "CLI_ERROR:CLI 返回为空"
374
+ return
375
+ fi
376
+
377
+ if alipay_cli_has_local_state_permission_error "$CLI_RESULT"; then
378
+ echo "CLI_LOCAL_FS_PERMISSION"
379
+ return
380
+ fi
381
+
382
+ # 先解包 MCP 信封,再进行错误检测
383
+ local UNWRAPPED=$(unwrap_mcp "$CLI_RESULT")
384
+
385
+ # 1. 优先检测 MCP 认证错误。非 JSON 只接受传输层强证据;JSON 只检查
386
+ # 顶层响应和登记的 error/response 层,不递归扫描成功候选或示例数据。
387
+ if ! echo "$UNWRAPPED" | jq -e . >/dev/null 2>&1; then
388
+ if echo "$UNWRAPPED" | grep -qiE \
389
+ "HTTP[[:space:]]*401|Authorization is empty|非法的?认证信息|用户未登录|not[ _-]+logged[ _-]+in|(authorization|authentication|login)[-[:space:]_:=]+(is[-[:space:]_:=]+)?required|(access|auth|authentication|authorization)[ _-]+token[-[:space:]_:=]+(is[-[:space:]_:=]+)?(missing|expired)|(^|[^[:alnum:]_])(missing|expired)[-[:space:]_:=]+(access|auth|authentication|authorization)[ _-]+token([^[:alnum:]_]|$)|(^|[^[:alnum:]_])token[-[:space:]_:=]+(is[-[:space:]_:=]+)?(missing|expired)([^[:alnum:]_]|$)"; then
390
+ echo "MCP_AUTH_ERROR"
391
+ return
392
+ fi
393
+ elif echo "$UNWRAPPED" | jq -e --arg context "$ERROR_CONTEXT" '
394
+ def failure_shaped:
395
+ type == "object" and (
396
+ .success? == false or
397
+ (.error? != null) or
398
+ (.errorCode? != null) or
399
+ ((.errorMessage? | type) == "string" and .errorMessage != "") or
400
+ ((.errorMsg? | type) == "string" and .errorMsg != "") or
401
+ (((.code? // "") | tostring) as $code |
402
+ $code != "" and $code != "0" and $code != "10000")
403
+ );
404
+ def auth_code:
405
+ if type != "string" and type != "number" then false
406
+ else
407
+ (tostring | ascii_downcase | gsub("^[[:space:]]+|[[:space:]]+$"; "")) as $code |
408
+ ([
409
+ "401",
410
+ "unauthorized",
411
+ "not_logged_in",
412
+ "login_required",
413
+ "unauthenticated",
414
+ "authorization_required",
415
+ "auth_required",
416
+ "access_token_missing",
417
+ "access_token_expired",
418
+ "auth_token_missing",
419
+ "auth_token_expired"
420
+ ] | index($code) != null)
421
+ end;
422
+ def auth_message:
423
+ type == "string" and test(
424
+ "HTTP[[:space:]]*401|Authorization is empty|非法的?认证信息|用户未登录|not[ _-]+logged[ _-]+in|(authorization|authentication|login)[-[:space:]_:=]+(is[-[:space:]_:=]+)?required|(access|auth|authentication|authorization)[ _-]+token[-[:space:]_:=]+(is[-[:space:]_:=]+)?(missing|expired)|(^|[^[:alnum:]_])(missing|expired)[-[:space:]_:=]+(access|auth|authentication|authorization)[ _-]+token([^[:alnum:]_]|$)|(^|[^[:alnum:]_])token[-[:space:]_:=]+(is[-[:space:]_:=]+)?(missing|expired)([^[:alnum:]_]|$)";
425
+ "i"
426
+ );
427
+ . as $root |
428
+ ([
429
+ $root,
430
+ ($root.data? | objects | select(failure_shaped)),
431
+ ($root.response? | objects | select(failure_shaped)),
432
+ ($root.data.response? | objects | select(failure_shaped))
433
+ ]) as $response_nodes |
434
+ ([
435
+ ($root.error? | objects),
436
+ ($root.errorCode? | objects),
437
+ ($root.data.error? | objects),
438
+ ($root.data.errorCode? | objects),
439
+ ($root.response.error? | objects),
440
+ ($root.data.response.error? | objects),
441
+ ($root.errorContext.errorStack[]? | objects)
442
+ ]) as $error_nodes |
443
+ (($response_nodes + $error_nodes) | any(.[];
444
+ [
445
+ .code?, .errorCode?, .status?, .reason?,
446
+ (.errorCode? | objects | (.code?, .status?, .reason?))
447
+ ] | any(.[]; auth_code)
448
+ )) or
449
+ ($response_nodes | any(.[];
450
+ failure_shaped and
451
+ ([.message?, .errorMessage?, .errorMsg?, .msg?, .subMsg?] | any(.[]; auth_message))
452
+ )) or
453
+ ($error_nodes | any(.[];
454
+ [.message?, .errorMessage?, .errorMsg?, .msg?, .subMsg?] | any(.[]; auth_message)
455
+ )) or
456
+ ($context == "MCP_AUTHENTICATED_CALL" and (
457
+ (($root.logged_in? | type) == "boolean" and $root.logged_in == false) or
458
+ (($root.data? | type) == "object" and
459
+ ($root.data.logged_in? | type) == "boolean" and $root.data.logged_in == false) or
460
+ (($root.data? | type) == "object" and
461
+ ([$root.data.code?, $root.data.errorCode?, $root.data.status?, $root.data.reason?] |
462
+ any(.[]; auth_code)))
463
+ ))
464
+ ' >/dev/null 2>&1; then
465
+ echo "MCP_AUTH_ERROR"
466
+ return
467
+ fi
468
+
469
+ local SUCCESS_STATE
470
+ SUCCESS_STATE=$(extract_success_state "$UNWRAPPED")
471
+
472
+ # 2. MCP 服务不稳定(后端返回的服务异常)。已登记成功响应中的
473
+ # 候选描述和示例不参与错误关键词分类。
474
+ if [ "$SUCCESS_STATE" != "true" ] && echo "$UNWRAPPED" | grep -qiE "MCP.*服务.*不稳定|服务暂时不可用"; then
475
+ echo "SERVICE_UNSTABLE"
476
+ return
477
+ fi
478
+
479
+ # 3. MCP 调用失败(网络/连接错误)
480
+ if [ "$SUCCESS_STATE" != "true" ] && echo "$UNWRAPPED" | grep -qiE "MCP 调用失败|connection refused|timeout|network[ _]error|网络连接失败"; then
481
+ echo "MCP_SERVICE_ERROR"
482
+ return
483
+ fi
484
+
485
+ # 4. 授权信息不匹配(MCC/产品/scope 未授权)
486
+ if [ "$SUCCESS_STATE" != "true" ] && echo "$UNWRAPPED" | grep -qiE "mccCode.*is not auth|salesProductCodes.*is not auth|scope.*is not auth"; then
487
+ echo "AUTH_MISMATCH"
488
+ return
489
+ fi
490
+
491
+ if ! echo "$UNWRAPPED" | jq -e . >/dev/null 2>&1; then
492
+ echo "CLI_ERROR:CLI 返回非 JSON 内容,原始输出已隐藏"
493
+ return
494
+ fi
495
+
496
+ # 5. 通用业务错误(从解包后的 JSON 中提取 errorCode)
497
+ local ERROR_CODE=$(extract_error_code "$UNWRAPPED")
498
+ if [ -n "$ERROR_CODE" ] && [ "$ERROR_CODE" != "null" ]; then
499
+ echo "ERROR:$ERROR_CODE"
500
+ return
501
+ fi
502
+
503
+ # 6. CLI 命令本身的错误(success: false,从解包后的 JSON 提取)
504
+ # 注意: MCP 业务响应可能嵌套在登记的 data/response 层。
505
+ if [ "$SUCCESS_STATE" = "false" ]; then
506
+ local ERROR_MSG=$(extract_error_message "$UNWRAPPED")
507
+ echo "CLI_ERROR:$ERROR_MSG"
508
+ return
509
+ fi
510
+
511
+ echo "SUCCESS"
512
+ }
513
+
514
+ extract_captured_json() {
515
+ local RAW="$1"
516
+ local ANALYSIS STATUS
517
+ ANALYSIS=$(extract_unique_json "$RAW" 2>/dev/null || echo '{"status":"none"}')
518
+ STATUS=$(echo "$ANALYSIS" | jq -r '.status // "none"' 2>/dev/null)
519
+ [ "$STATUS" = "ok" ] || return 1
520
+ echo "$ANALYSIS" | jq -ce '.value | select(type == "object")' 2>/dev/null
521
+ }
522
+
523
+ run_alipay_cli_json_capture() {
524
+ local stderr_file stdout stderr_output combined_output
525
+
526
+ ALIPAY_CLI_JSON_CAPTURE=""
527
+ ALIPAY_CLI_JSON_EXIT_CODE=0
528
+ stderr_file="$(mktemp "${TMPDIR:-/tmp}/alipay_cli_capture_stderr.XXXXXX")" || return 1
529
+ stdout=$(PLATFORM="${DEV_TOOL_NAME:-unknown}" PLATFORM_ID="${PLATFORM_ID:-}" PRODUCT="${PRODUCT:-}" "$@" 2>"$stderr_file")
530
+ ALIPAY_CLI_JSON_EXIT_CODE=$?
531
+ stderr_output=$(cat "$stderr_file")
532
+ rm -f "$stderr_file"
533
+
534
+ combined_output=$(printf '%s\n%s' "$stdout" "$stderr_output")
535
+ if type auth_has_local_state_permission_error >/dev/null 2>&1 &&
536
+ auth_has_local_state_permission_error "$combined_output"; then
537
+ ALIPAY_CLI_JSON_CAPTURE=""
538
+ return "${AUTH_LOCAL_STATE_PERMISSION_EXIT_CODE:-77}"
539
+ fi
540
+ if ALIPAY_CLI_JSON_CAPTURE=$(extract_captured_json "$combined_output"); then
541
+ return 0
542
+ fi
543
+
544
+ ALIPAY_CLI_JSON_CAPTURE=""
545
+ return 1
546
+ }
547
+
548
+ confirm_logout_postcondition() {
549
+ local logout_error_type whoami_error_type whoami_state
550
+ local logout_result_state logout_capture_rc whoami_capture_rc attempt=1
551
+ local max_attempts=3
552
+ local interval_seconds=3
553
+
554
+ ALIPAY_LOGOUT_POSTCONDITION=""
555
+ ALIPAY_LOGOUT_ERROR_MESSAGE=""
556
+
557
+ if [ "${ALIPAY_AIPAY_TEST_MODE:-0}" = "1" ]; then
558
+ interval_seconds=0
559
+ fi
560
+
561
+ while [ "$attempt" -le "$max_attempts" ]; do
562
+ logout_result_state="UNCONFIRMED"
563
+ run_alipay_cli_json_capture alipay-cli logout --json
564
+ logout_capture_rc=$?
565
+ if [ "$logout_capture_rc" -eq 0 ]; then
566
+ logout_error_type=$(detect_error "$ALIPAY_CLI_JSON_CAPTURE")
567
+ if [ "$ALIPAY_CLI_JSON_EXIT_CODE" -eq 0 ] && [ "$logout_error_type" = "SUCCESS" ]; then
568
+ ALIPAY_LOGOUT_POSTCONDITION="COMMAND_CONFIRMED_SUCCESS"
569
+ return 0
570
+ fi
571
+ ALIPAY_LOGOUT_ERROR_MESSAGE=$(sanitize_customer_error_text "$(extract_error_message "$ALIPAY_CLI_JSON_CAPTURE")")
572
+ case "$logout_error_type" in
573
+ MCP_SERVICE_ERROR|SERVICE_UNSTABLE)
574
+ logout_result_state="RETRYABLE"
575
+ ;;
576
+ SUCCESS)
577
+ # A success payload paired with a non-zero exit code cannot prove the command result.
578
+ logout_result_state="UNCONFIRMED"
579
+ ;;
580
+ *)
581
+ logout_result_state="EXPLICIT_FAILURE"
582
+ ;;
583
+ esac
584
+ elif [ "$logout_capture_rc" -eq "${AUTH_LOCAL_STATE_PERMISSION_EXIT_CODE:-77}" ]; then
585
+ ALIPAY_LOGOUT_POSTCONDITION="LOCAL_FS_PERMISSION"
586
+ return 1
587
+ else
588
+ ALIPAY_LOGOUT_ERROR_MESSAGE="退出命令未返回可唯一解析结果"
589
+ fi
590
+
591
+ run_alipay_cli_json_capture alipay-cli whoami --json
592
+ whoami_capture_rc=$?
593
+ if [ "$whoami_capture_rc" -eq "${AUTH_LOCAL_STATE_PERMISSION_EXIT_CODE:-77}" ]; then
594
+ ALIPAY_LOGOUT_POSTCONDITION="LOCAL_FS_PERMISSION"
595
+ return 1
596
+ fi
597
+ if [ "$whoami_capture_rc" -ne 0 ]; then
598
+ ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
599
+ return 1
600
+ fi
601
+
602
+ whoami_error_type=$(detect_error "$ALIPAY_CLI_JSON_CAPTURE")
603
+ if [ "$ALIPAY_CLI_JSON_EXIT_CODE" -ne 0 ] || [ "$whoami_error_type" != "SUCCESS" ]; then
604
+ ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
605
+ return 1
606
+ fi
607
+
608
+ whoami_state=$(echo "$ALIPAY_CLI_JSON_CAPTURE" | jq -r '
609
+ if (.data | type) != "object" then "UNCONFIRMED"
610
+ elif (.data.logged_in? | type) == "boolean" and .data.logged_in == false then "LOGGED_OUT"
611
+ elif (.data.is_expired? | type) == "boolean" and .data.is_expired == true then "LOGGED_OUT"
612
+ elif (.data.logged_in? | type) == "boolean" and .data.logged_in == true and
613
+ ((.data | has("is_expired") | not) or ((.data.is_expired? | type) == "boolean" and .data.is_expired == false)) then "STILL_LOGGED_IN"
614
+ else "UNCONFIRMED"
615
+ end
616
+ ' 2>/dev/null)
617
+ if [ "$whoami_state" = "LOGGED_OUT" ]; then
618
+ ALIPAY_LOGOUT_POSTCONDITION="POSTCONDITION_LOGGED_OUT"
619
+ return 0
620
+ fi
621
+ if [ "$whoami_state" != "STILL_LOGGED_IN" ] || [ "$logout_result_state" != "EXPLICIT_FAILURE" ]; then
622
+ ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
623
+ return 1
624
+ fi
625
+
626
+ if [ "$attempt" -eq "$max_attempts" ]; then
627
+ ALIPAY_LOGOUT_POSTCONDITION="STILL_LOGGED_IN"
628
+ return 1
629
+ fi
630
+ sleep "$interval_seconds"
631
+ attempt=$((attempt + 1))
632
+ done
633
+ }
634
+
635
+ render_logout_unconfirmed() {
636
+ render_error_message error.mcp.auth LOGOUT_UNCONFIRMED '{}'
637
+ }
638
+
639
+ render_logout_still_logged_in() {
640
+ local message input
641
+ message="${ALIPAY_LOGOUT_ERROR_MESSAGE:-退出命令未能使当前登录态失效}"
642
+ input=$(jq -cn --arg errorMessage "$message" '{errorMessage:$errorMessage}')
643
+ render_error_message error.mcp.auth LOGOUT_FAILED "$input"
644
+ }
645
+
646
+ # ─── MCP 认证错误处理 ──────────────────────────────────────────────────────
647
+ handle_mcp_auth_error() {
648
+ if [ "${ALIPAY_AIPAY_SUPPRESS_AUTH_LOGOUT:-}" = "1" ]; then
649
+ emit_control "AUTH_FLOW:AUTH_REQUIRED"
650
+ return 1
651
+ fi
652
+ render_error_message error.mcp.auth START '{}' || {
653
+ emit_control "AUTH_FLOW:FAILED"
654
+ return 1
655
+ }
656
+ confirm_logout_postcondition
657
+ case "$ALIPAY_LOGOUT_POSTCONDITION" in
658
+ COMMAND_CONFIRMED_SUCCESS|POSTCONDITION_LOGGED_OUT)
659
+ render_error_message error.mcp.auth LOGOUT_SUCCESS '{}' || {
660
+ emit_control "AUTH_FLOW:FAILED"
661
+ return 1
662
+ }
663
+ emit_control "AUTH_FLOW:AUTH_REQUIRED"
664
+ ;;
665
+ STILL_LOGGED_IN)
666
+ render_logout_still_logged_in || {
667
+ emit_control "AUTH_FLOW:FAILED"
668
+ return 1
669
+ }
670
+ emit_control "AUTH_FLOW:LOGOUT_STILL_LOGGED_IN"
671
+ ;;
672
+ LOCAL_FS_PERMISSION)
673
+ if type auth_local_state_permission_failure >/dev/null 2>&1; then
674
+ auth_local_state_permission_failure
675
+ else
676
+ emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
677
+ fi
678
+ ;;
679
+ *)
680
+ render_logout_unconfirmed || {
681
+ emit_control "AUTH_FLOW:FAILED"
682
+ return 1
683
+ }
684
+ emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
685
+ ;;
686
+ esac
687
+ }
688
+
689
+ # ─── MCP 服务不可用处理 ────────────────────────────────────────────────────
690
+ handle_mcp_service_error() {
691
+ local CLI_RESULT="$1"
692
+ local UNWRAPPED=$(unwrap_mcp "$CLI_RESULT")
693
+
694
+ if [ "${DEV_TOOL_NAME:-unknown}" != "unknown" ]; then
695
+ render_error_message error.mcp.service AGENT_NETWORK '{}'
696
+ return
697
+ fi
698
+
699
+ local SERVICE_ERROR_MSG
700
+ SERVICE_ERROR_MSG=$(echo "$UNWRAPPED" | jq -r '.error.message // .errorMessage // .message // "未知错误"' 2>/dev/null) || SERVICE_ERROR_MSG="服务调用失败,原始输出已隐藏"
701
+ SERVICE_ERROR_MSG=$(sanitize_customer_error_text "$SERVICE_ERROR_MSG")
702
+ MESSAGE_INPUT=$(jq -cn --arg errorDetail "$SERVICE_ERROR_MSG" '{errorDetail:$errorDetail}')
703
+ render_error_message error.mcp.service GENERIC "$MESSAGE_INPUT"
704
+ }
705
+
706
+ # ─── 授权信息不匹配处理 ────────────────────────────────────────────────────
707
+ handle_auth_mismatch() {
708
+ local UNWRAPPED
709
+ UNWRAPPED=$(unwrap_mcp "${1:-}")
710
+ if echo "$UNWRAPPED" | grep -qiE "mccCode.*is not auth"; then
711
+ emit_control "AUTH_FLOW:MCC_MISMATCH"
712
+ else
713
+ emit_control "AUTH_FLOW:SCOPE_MISMATCH"
714
+ fi
715
+ }
716
+
717
+ # ─── 后端业务错误处理(完整透出错误信息) ──────────────────────────────────
718
+ handle_backend_error() {
719
+ local CLI_RESULT="$1"
720
+ local ERROR_CODE="$2"
721
+ local UNWRAPPED=$(unwrap_mcp "$CLI_RESULT")
722
+
723
+ # 提取错误信息
724
+ local ERROR_MSG=$(extract_error_message "$UNWRAPPED")
725
+ local BIZ_TIPS=$(extract_biz_tips "$UNWRAPPED")
726
+ local ERROR_SCENE=$(extract_error_object_field "$UNWRAPPED" "errorScene")
727
+ local ERROR_SPECIFIC=$(extract_error_object_field "$UNWRAPPED" "errorSpecific")
728
+
729
+ ERROR_MSG=$(sanitize_customer_error_text "$ERROR_MSG")
730
+ if [ -n "$ERROR_SCENE" ]; then ERROR_SCENE=$(sanitize_customer_error_text "$ERROR_SCENE"); fi
731
+ if [ -n "$ERROR_SPECIFIC" ]; then ERROR_SPECIFIC=$(sanitize_customer_error_text "$ERROR_SPECIFIC"); fi
732
+
733
+ # 透出 checkedError 中的详细错误描述(如 ar-sign.apply 返回的字段级校验错误)
734
+ local CHECKED_ERRORS=$(extract_checked_errors "$UNWRAPPED")
735
+ local CHECKED_ERRORS_JSON='[]'
736
+ if [ -n "$CHECKED_ERRORS" ] && [ "$CHECKED_ERRORS" != "null" ] && [ "$CHECKED_ERRORS" != "[]" ]; then
737
+ while IFS= read -r line; do
738
+ if [ -n "$line" ]; then
739
+ line=$(sanitize_customer_error_text "$line")
740
+ CHECKED_ERRORS_JSON=$(jq -cn --argjson items "$CHECKED_ERRORS_JSON" --arg item "$line" '$items + [$item]')
741
+ fi
742
+ done < <(echo "$CHECKED_ERRORS" | jq -r '.[]? | .errorDesc // .message // . // empty' 2>/dev/null)
743
+ fi
744
+
745
+ if [ -n "$BIZ_TIPS" ] && [ "$BIZ_TIPS" != "null" ]; then
746
+ BIZ_TIPS=$(sanitize_customer_error_text "$BIZ_TIPS")
747
+ else
748
+ BIZ_TIPS=""
749
+ fi
750
+ local APP_MAX="NO"
751
+ if [ "$ERROR_CODE" = "APP_MAX_ERROR" ]; then
752
+ APP_MAX="YES"
753
+ fi
754
+ MESSAGE_INPUT=$(jq -cn \
755
+ --arg errorCode "$(sanitize_customer_error_text "$ERROR_CODE")" \
756
+ --arg errorMessage "$ERROR_MSG" \
757
+ --arg errorScene "$ERROR_SCENE" \
758
+ --arg errorSpecific "$ERROR_SPECIFIC" \
759
+ --argjson checkedErrors "$CHECKED_ERRORS_JSON" \
760
+ --arg bizTips "$BIZ_TIPS" \
761
+ --arg appMax "$APP_MAX" \
762
+ '{errorCode:$errorCode,errorMessage:$errorMessage,errorScene:$errorScene,errorSpecific:$errorSpecific,checkedErrors:$checkedErrors,bizTips:$bizTips,appMax:$appMax}')
763
+ render_error_message error.backend DEFAULT "$MESSAGE_INPUT"
764
+ }
765
+
766
+ # ─── MCP 服务不稳定处理 ────────────────────────────────────────────────────
767
+ handle_service_unstable() {
768
+ render_error_message error.service.unstable DEFAULT '{}'
769
+ }
770
+
771
+ # ─── 统一错误处理入口 ──────────────────────────────────────────────────────
772
+ # 参数: $1 - CLI 执行结果文本(原始输出,含信封或不含)
773
+ # $2 - 可选调用上下文,透传给 detect_error
774
+ # 返回: 0=成功, 1=当前动作失败(由主流程判断恢复、阻断或继续独立分支)
775
+ handle_error() {
776
+ local CLI_RESULT="$1"
777
+ local ERROR_CONTEXT="${2:-}"
778
+ local ERROR_TYPE=$(detect_error "$CLI_RESULT" "$ERROR_CONTEXT")
779
+ # 解包一次供 CLI_ERROR 分支使用(其他分支由各自的 handler 自己解包)
780
+ local _UNWRAPPED=$(unwrap_mcp "$CLI_RESULT")
781
+
782
+ case "$ERROR_TYPE" in
783
+ "MCP_AUTH_ERROR")
784
+ handle_mcp_auth_error
785
+ ;;
786
+ "MCP_SERVICE_ERROR")
787
+ handle_mcp_service_error "$CLI_RESULT"
788
+ ;;
789
+ "AUTH_MISMATCH")
790
+ handle_auth_mismatch "$CLI_RESULT"
791
+ ;;
792
+ "SERVICE_UNSTABLE")
793
+ handle_service_unstable
794
+ ;;
795
+ "CLI_LOCAL_FS_PERMISSION")
796
+ if type auth_local_state_permission_failure >/dev/null 2>&1; then
797
+ auth_local_state_permission_failure
798
+ else
799
+ MESSAGE_INPUT=$(jq -cn \
800
+ --arg errorMessage "alipay-cli 本地状态目录权限不足" \
801
+ '{errorMessage:$errorMessage,bizTips:"",checkedErrors:[]}')
802
+ render_error_message error.cli DEFAULT "$MESSAGE_INPUT"
803
+ fi
804
+ ;;
805
+ "ERROR:"*)
806
+ local CODE="${ERROR_TYPE#ERROR:}"
807
+ handle_backend_error "$CLI_RESULT" "$CODE"
808
+ ;;
809
+ "CLI_ERROR:"*)
810
+ local MSG="${ERROR_TYPE#CLI_ERROR:}"
811
+ # CLI_ERROR 也透出 bizTips 和 checkedError(如果有的话)
812
+ local CLI_BIZ_TIPS=$(extract_biz_tips "$_UNWRAPPED")
813
+ if [ -n "$CLI_BIZ_TIPS" ] && [ "$CLI_BIZ_TIPS" != "null" ]; then
814
+ CLI_BIZ_TIPS=$(sanitize_customer_error_text "$CLI_BIZ_TIPS")
815
+ else
816
+ CLI_BIZ_TIPS=""
817
+ fi
818
+ local CLI_CHECKED=$(extract_checked_errors "$_UNWRAPPED")
819
+ local CLI_CHECKED_JSON='[]'
820
+ if [ -n "$CLI_CHECKED" ] && [ "$CLI_CHECKED" != "null" ] && [ "$CLI_CHECKED" != "[]" ]; then
821
+ while IFS= read -r line; do
822
+ if [ -n "$line" ]; then
823
+ line=$(sanitize_customer_error_text "$line")
824
+ CLI_CHECKED_JSON=$(jq -cn --argjson items "$CLI_CHECKED_JSON" --arg item "$line" '$items + [$item]')
825
+ fi
826
+ done < <(echo "$CLI_CHECKED" | jq -r '.[]? | .errorDesc // .message // . // empty' 2>/dev/null)
827
+ fi
828
+ MESSAGE_INPUT=$(jq -cn \
829
+ --arg errorMessage "$(sanitize_customer_error_text "$MSG")" \
830
+ --arg bizTips "$CLI_BIZ_TIPS" \
831
+ --argjson checkedErrors "$CLI_CHECKED_JSON" \
832
+ '{errorMessage:$errorMessage,bizTips:$bizTips,checkedErrors:$checkedErrors}')
833
+ render_error_message error.cli DEFAULT "$MESSAGE_INPUT"
834
+ ;;
835
+ "SUCCESS")
836
+ return 0
837
+ ;;
838
+ esac
839
+
840
+ return 1
841
+ }