@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,927 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="referrer" content="no-referrer">
7
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-e7wS6K22dPH0rLOPEIXiqarT3THYbebGQ6+mllUAkUQ='; style-src 'sha256-sVcbxXTlbZkV3rlitpaCCaR5w2QcAHI+aIs8/Og7Ang='; connect-src 'none'; img-src 'none'; font-src 'none'; media-src 'none'; object-src 'none'; frame-src 'none'; worker-src 'none'; manifest-src 'none'; base-uri 'none'; form-action 'none'">
8
+ <title>RSA2 应用密钥生成工具</title>
9
+ <style>
10
+ :root {
11
+ color-scheme: light;
12
+ --page: #f4f6f8;
13
+ --surface: #ffffff;
14
+ --surface-muted: #f7f8fa;
15
+ --text: #18212a;
16
+ --muted: #5e6975;
17
+ --border: #d7dde3;
18
+ --border-strong: #b8c1ca;
19
+ --primary: #1769aa;
20
+ --success: #176b45;
21
+ --danger: #a53535;
22
+ --danger-soft: #fff2f1;
23
+ --focus: #0a76d3;
24
+ }
25
+
26
+ * {
27
+ box-sizing: border-box;
28
+ letter-spacing: 0;
29
+ }
30
+
31
+ html {
32
+ min-width: 320px;
33
+ background: var(--page);
34
+ }
35
+
36
+ body {
37
+ margin: 0;
38
+ color: var(--text);
39
+ background: var(--page);
40
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
41
+ font-size: 15px;
42
+ line-height: 1.55;
43
+ }
44
+
45
+ button,
46
+ textarea {
47
+ font: inherit;
48
+ }
49
+
50
+ button {
51
+ min-height: 44px;
52
+ border: 1px solid transparent;
53
+ border-radius: 6px;
54
+ padding: 9px 16px;
55
+ cursor: pointer;
56
+ font-weight: 600;
57
+ transition: background-color 120ms ease, border-color 120ms ease;
58
+ }
59
+
60
+ button:disabled {
61
+ cursor: not-allowed;
62
+ opacity: 0.55;
63
+ }
64
+
65
+ button:focus-visible,
66
+ textarea:focus-visible {
67
+ outline: 3px solid var(--focus);
68
+ outline-offset: 2px;
69
+ border-color: var(--focus);
70
+ }
71
+
72
+ .page-header,
73
+ .tool-shell,
74
+ .page-footer {
75
+ width: min(100% - 32px, 800px);
76
+ margin-inline: auto;
77
+ }
78
+
79
+ .page-header {
80
+ padding: 42px 0 22px;
81
+ }
82
+
83
+ h1,
84
+ h2,
85
+ h3,
86
+ p {
87
+ margin-top: 0;
88
+ }
89
+
90
+ h1 {
91
+ margin-bottom: 8px;
92
+ font-size: 30px;
93
+ line-height: 1.25;
94
+ }
95
+
96
+ h2 {
97
+ margin-bottom: 8px;
98
+ font-size: 19px;
99
+ line-height: 1.4;
100
+ }
101
+
102
+ h3 {
103
+ margin-bottom: 10px;
104
+ font-size: 16px;
105
+ line-height: 1.4;
106
+ }
107
+
108
+ .page-footer {
109
+ color: var(--muted);
110
+ }
111
+
112
+ .security-notice {
113
+ padding: 0 0 14px;
114
+ }
115
+
116
+ .security-notice h2 {
117
+ margin-bottom: 8px;
118
+ color: var(--text);
119
+ font-size: 19px;
120
+ line-height: 1.4;
121
+ }
122
+
123
+ .security-notice p {
124
+ margin-bottom: 8px;
125
+ color: var(--muted);
126
+ font-size: 14px;
127
+ line-height: 1.65;
128
+ }
129
+
130
+ .security-notice ul {
131
+ margin: 0;
132
+ padding-left: 20px;
133
+ color: var(--muted);
134
+ font-size: 14px;
135
+ line-height: 1.65;
136
+ }
137
+
138
+ .security-notice li + li {
139
+ margin-top: 2px;
140
+ }
141
+
142
+ .capability-notice {
143
+ border-bottom: 1px solid #ebc1bd;
144
+ padding: 12px 22px;
145
+ color: var(--danger);
146
+ background: var(--danger-soft);
147
+ }
148
+
149
+ .panel {
150
+ padding: 4px 0 0;
151
+ }
152
+
153
+ .intro {
154
+ margin-bottom: 20px;
155
+ color: var(--muted);
156
+ }
157
+
158
+ .actions {
159
+ display: flex;
160
+ flex-wrap: wrap;
161
+ align-items: center;
162
+ gap: 10px;
163
+ }
164
+
165
+ .btn-main {
166
+ width: 240px;
167
+ border-color: var(--border-strong);
168
+ color: var(--text);
169
+ background: var(--surface);
170
+ }
171
+
172
+ .btn-main:hover:not(:disabled) {
173
+ background: var(--surface-muted);
174
+ }
175
+
176
+ .btn-main[data-state="success"] {
177
+ border-color: #9fcab5;
178
+ color: var(--success);
179
+ background: #f4fbf7;
180
+ }
181
+
182
+ .btn-danger {
183
+ color: var(--danger);
184
+ }
185
+
186
+ .status {
187
+ margin: 14px 0 0;
188
+ color: var(--muted);
189
+ }
190
+
191
+ .status:empty {
192
+ margin-top: 0;
193
+ }
194
+
195
+ .status[data-state="working"] {
196
+ color: var(--primary);
197
+ }
198
+
199
+ .status[data-state="success"] {
200
+ color: var(--success);
201
+ }
202
+
203
+ .status[data-state="error"] {
204
+ color: var(--danger);
205
+ }
206
+
207
+ .result {
208
+ margin-top: 14px;
209
+ padding-top: 0;
210
+ }
211
+
212
+ .field {
213
+ margin-bottom: 20px;
214
+ }
215
+
216
+ .field-heading {
217
+ display: flex;
218
+ align-items: center;
219
+ justify-content: space-between;
220
+ gap: 12px;
221
+ margin-bottom: 7px;
222
+ }
223
+
224
+ .field-heading label {
225
+ display: block;
226
+ min-width: 0;
227
+ margin-bottom: 0;
228
+ font-weight: 650;
229
+ }
230
+
231
+ .btn-copy {
232
+ width: 74px;
233
+ min-height: 36px;
234
+ flex: 0 0 74px;
235
+ border-color: var(--border-strong);
236
+ padding: 5px 8px;
237
+ color: var(--text);
238
+ background: var(--surface);
239
+ font-size: 13px;
240
+ }
241
+
242
+ .btn-copy:hover:not(:disabled) {
243
+ background: var(--surface-muted);
244
+ }
245
+
246
+ textarea {
247
+ width: 100%;
248
+ min-height: 112px;
249
+ border: 1px solid var(--border-strong);
250
+ border-radius: 6px;
251
+ padding: 10px 12px;
252
+ resize: vertical;
253
+ overflow-wrap: anywhere;
254
+ color: var(--text);
255
+ background: var(--surface-muted);
256
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
257
+ font-size: 13px;
258
+ line-height: 1.55;
259
+ }
260
+
261
+ .format-guide {
262
+ margin: 0 0 14px;
263
+ border-top: 1px solid var(--border);
264
+ border-bottom: 1px solid var(--border);
265
+ }
266
+
267
+ .format-row {
268
+ display: grid;
269
+ grid-template-columns: minmax(96px, 120px) minmax(0, 1fr);
270
+ gap: 16px;
271
+ padding: 12px 0;
272
+ }
273
+
274
+ .format-row + .format-row {
275
+ border-top: 1px solid var(--border);
276
+ }
277
+
278
+ .format-row dt {
279
+ font-weight: 650;
280
+ }
281
+
282
+ .format-row dd {
283
+ margin: 0;
284
+ color: var(--muted);
285
+ }
286
+
287
+ .page-footer {
288
+ padding: 18px 0 38px;
289
+ font-size: 13px;
290
+ text-align: center;
291
+ }
292
+
293
+ [hidden] {
294
+ display: none !important;
295
+ }
296
+
297
+ @media (max-width: 600px) {
298
+ .page-header,
299
+ .tool-shell,
300
+ .page-footer {
301
+ width: min(100% - 20px, 800px);
302
+ }
303
+
304
+ .page-header {
305
+ padding-top: 26px;
306
+ }
307
+
308
+ h1 {
309
+ font-size: 26px;
310
+ }
311
+
312
+ .security-notice {
313
+ padding-bottom: 12px;
314
+ }
315
+
316
+ .panel {
317
+ padding-top: 4px;
318
+ }
319
+
320
+ .format-row {
321
+ grid-template-columns: 1fr;
322
+ gap: 3px;
323
+ }
324
+
325
+ textarea {
326
+ font-size: 16px;
327
+ }
328
+ }
329
+ </style>
330
+ </head>
331
+ <body>
332
+ <header class="page-header">
333
+ <h1>RSA2 应用密钥生成工具</h1>
334
+ </header>
335
+
336
+ <main class="tool-shell">
337
+ <section class="security-notice" aria-label="安全提示">
338
+ <h2>本地生成说明</h2>
339
+ <ul>
340
+ <li>本浏览器页面在设备本地生成 RSA2 2048 位应用密钥对,不依赖联网服务。</li>
341
+ <li>【应用私钥】不会在页面明文显示,但会写入下载的压缩包,请妥善保管密钥并避免同步到网盘或共享目录等。</li>
342
+ <li>支付宝不会以任何形式要求您提供【应用私钥】,切勿将私钥告知他人或发送至 Agent 对话。</li>
343
+ <li>本页面底层代码公开可审查,请确保您当前使用的支付宝 AI 付接入 Skill 通过官方渠道获取,确认页面来源可信。</li>
344
+ </ul>
345
+ </section>
346
+
347
+ <div class="capability-notice" id="capabilityNotice" role="alert" hidden></div>
348
+
349
+ <section class="panel" aria-label="生成应用密钥对">
350
+ <button class="btn-main" id="generateButton" type="button">一键生成应用密钥对</button>
351
+ <p class="status" id="generateStatus" role="status" aria-live="polite"></p>
352
+
353
+ <section class="result" id="keyResult" aria-labelledby="resultTitle" hidden>
354
+ <h2 id="resultTitle" tabindex="-1">密钥生成成功</h2>
355
+
356
+ <div class="field">
357
+ <div class="field-heading">
358
+ <label for="publicKeyOutput">应用公钥(复制给 Agent 上传)</label>
359
+ <button class="btn-copy" id="copyPublicButton" type="button" disabled>复制</button>
360
+ </div>
361
+ <textarea id="publicKeyOutput" rows="6" readonly spellcheck="false" autocapitalize="off"></textarea>
362
+ </div>
363
+
364
+ <section aria-labelledby="privateKeyTitle">
365
+ <h3 id="privateKeyTitle">应用私钥使用格式</h3>
366
+ <p class="intro">下载的压缩包包含应用公钥和两种格式的应用私钥;页面清除不会删除已下载文件,请自行妥善保管。</p>
367
+ <dl class="format-guide">
368
+ <div class="format-row">
369
+ <dt>Java 语言</dt>
370
+ <dd>使用 PKCS#8 格式的应用私钥</dd>
371
+ </div>
372
+ <div class="format-row">
373
+ <dt>非 Java 语言</dt>
374
+ <dd>使用 PKCS#1 格式的应用私钥</dd>
375
+ </div>
376
+ </dl>
377
+ </section>
378
+
379
+ <div class="actions">
380
+ <button class="btn-main" id="downloadKeyPairButton" type="button" disabled>下载应用密钥对</button>
381
+ <button class="btn-main btn-danger" id="clearButton" type="button">清空密钥</button>
382
+ </div>
383
+ <p class="status" id="actionStatus" role="status" aria-live="polite"></p>
384
+ </section>
385
+ </section>
386
+ </main>
387
+
388
+ <footer class="page-footer">单文件本地运行 · Unix · macOS · Linux · Windows</footer>
389
+
390
+ <script>
391
+ 'use strict';
392
+
393
+ const RSA_ALGORITHM = Object.freeze({
394
+ name: 'RSASSA-PKCS1-v1_5',
395
+ modulusLength: 2048,
396
+ publicExponent: new Uint8Array([1, 0, 1]),
397
+ hash: 'SHA-256'
398
+ });
399
+ const cryptoApi = globalThis.crypto;
400
+
401
+ class UserFacingError extends Error {}
402
+
403
+ function wipe(value) {
404
+ if (value instanceof ArrayBuffer) {
405
+ new Uint8Array(value).fill(0);
406
+ } else if (ArrayBuffer.isView(value)) {
407
+ new Uint8Array(value.buffer, value.byteOffset, value.byteLength).fill(0);
408
+ }
409
+ }
410
+
411
+ function bytesToBase64(value) {
412
+ const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
413
+ let binary = '';
414
+ for (let offset = 0; offset < bytes.length; offset += 0x8000) {
415
+ const chunk = bytes.subarray(offset, Math.min(offset + 0x8000, bytes.length));
416
+ binary += String.fromCharCode.apply(null, chunk);
417
+ }
418
+ return btoa(binary);
419
+ }
420
+
421
+ function readDerElement(bytes, offset) {
422
+ if (!Number.isInteger(offset) || offset < 0 || offset + 2 > bytes.length) {
423
+ throw new UserFacingError('私钥 DER 结构不完整。');
424
+ }
425
+
426
+ const tag = bytes[offset];
427
+ const firstLength = bytes[offset + 1];
428
+ let contentLength = 0;
429
+ let lengthBytes = 1;
430
+
431
+ if (firstLength < 0x80) {
432
+ contentLength = firstLength;
433
+ } else {
434
+ const count = firstLength & 0x7f;
435
+ if (count === 0 || count > 4 || offset + 2 + count > bytes.length) {
436
+ throw new UserFacingError('私钥 DER 长度编码无效。');
437
+ }
438
+ if (bytes[offset + 2] === 0) {
439
+ throw new UserFacingError('私钥 DER 长度编码不规范。');
440
+ }
441
+ lengthBytes += count;
442
+ for (let index = 0; index < count; index += 1) {
443
+ contentLength = contentLength * 256 + bytes[offset + 2 + index];
444
+ }
445
+ if (contentLength < 0x80) {
446
+ throw new UserFacingError('私钥 DER 使用了非最短长度编码。');
447
+ }
448
+ }
449
+
450
+ const contentStart = offset + 1 + lengthBytes;
451
+ const end = contentStart + contentLength;
452
+ if (end > bytes.length) {
453
+ throw new UserFacingError('私钥 DER 内容越界。');
454
+ }
455
+ return { tag: tag, contentStart: contentStart, end: end };
456
+ }
457
+
458
+ function pkcs8ToPkcs1(pkcs8Bytes) {
459
+ const outer = readDerElement(pkcs8Bytes, 0);
460
+ if (outer.tag !== 0x30 || outer.end !== pkcs8Bytes.length) {
461
+ throw new UserFacingError('私钥不是规范的 PKCS#8。');
462
+ }
463
+
464
+ let position = outer.contentStart;
465
+ const version = readDerElement(pkcs8Bytes, position);
466
+ if (version.tag !== 0x02 || version.end - version.contentStart !== 1 || pkcs8Bytes[version.contentStart] !== 0) {
467
+ throw new UserFacingError('PKCS#8 版本字段无效。');
468
+ }
469
+ position = version.end;
470
+
471
+ const algorithm = readDerElement(pkcs8Bytes, position);
472
+ if (algorithm.tag !== 0x30) {
473
+ throw new UserFacingError('PKCS#8 算法字段无效。');
474
+ }
475
+ position = algorithm.end;
476
+
477
+ const privateKey = readDerElement(pkcs8Bytes, position);
478
+ if (privateKey.tag !== 0x04 || privateKey.end !== outer.end) {
479
+ throw new UserFacingError('PKCS#8 私钥字段无效。');
480
+ }
481
+
482
+ const pkcs1Bytes = pkcs8Bytes.slice(privateKey.contentStart, privateKey.end);
483
+ try {
484
+ const inner = readDerElement(pkcs1Bytes, 0);
485
+ if (inner.tag !== 0x30 || inner.end !== pkcs1Bytes.length) {
486
+ throw new UserFacingError('PKCS#8 内部不是规范的 PKCS#1 私钥。');
487
+ }
488
+ return pkcs1Bytes;
489
+ } catch (error) {
490
+ wipe(pkcs1Bytes);
491
+ throw error;
492
+ }
493
+ }
494
+
495
+ async function generateKeyMaterial() {
496
+ let pkcs8Bytes = null;
497
+ let pkcs1Bytes = null;
498
+
499
+ try {
500
+ const pair = await cryptoApi.subtle.generateKey(RSA_ALGORITHM, true, ['sign', 'verify']);
501
+ const publicKeyBytes = new Uint8Array(await cryptoApi.subtle.exportKey('spki', pair.publicKey));
502
+ pkcs8Bytes = new Uint8Array(await cryptoApi.subtle.exportKey('pkcs8', pair.privateKey));
503
+ pkcs1Bytes = pkcs8ToPkcs1(pkcs8Bytes);
504
+
505
+ const importedPublicKey = await cryptoApi.subtle.importKey(
506
+ 'spki',
507
+ publicKeyBytes,
508
+ { name: RSA_ALGORITHM.name, hash: RSA_ALGORITHM.hash },
509
+ false,
510
+ ['verify']
511
+ );
512
+ const importedPrivateKey = await cryptoApi.subtle.importKey(
513
+ 'pkcs8',
514
+ pkcs8Bytes,
515
+ { name: RSA_ALGORITHM.name, hash: RSA_ALGORITHM.hash },
516
+ false,
517
+ ['sign']
518
+ );
519
+
520
+ const algorithm = importedPrivateKey.algorithm;
521
+ const exponent = new Uint8Array(algorithm.publicExponent || []);
522
+ if (algorithm.modulusLength !== 2048 || exponent.length !== 3 || exponent[0] !== 1 || exponent[1] !== 0 || exponent[2] !== 1) {
523
+ throw new UserFacingError('生成的密钥不是 RSA-2048 / 65537。');
524
+ }
525
+
526
+ const challenge = cryptoApi.getRandomValues(new Uint8Array(32));
527
+ const signature = new Uint8Array(await cryptoApi.subtle.sign(RSA_ALGORITHM.name, importedPrivateKey, challenge));
528
+ const verified = await cryptoApi.subtle.verify(RSA_ALGORITHM.name, importedPublicKey, signature, challenge);
529
+ if (!verified) {
530
+ throw new UserFacingError('生成的公私钥无法通过配对验证。');
531
+ }
532
+
533
+ return {
534
+ publicKey: bytesToBase64(publicKeyBytes),
535
+ privateKeyPkcs8: bytesToBase64(pkcs8Bytes),
536
+ privateKeyPkcs1: bytesToBase64(pkcs1Bytes)
537
+ };
538
+ } finally {
539
+ wipe(pkcs8Bytes);
540
+ wipe(pkcs1Bytes);
541
+ }
542
+ }
543
+
544
+ function createPublicKeyFile(material) {
545
+ return [
546
+ 'RSA2 应用公钥',
547
+ '',
548
+ '[应用公钥 / SPKI Base64]',
549
+ material.publicKey,
550
+ ''
551
+ ].join('\n');
552
+ }
553
+
554
+ function createPrivateKeyFile(material) {
555
+ return [
556
+ 'RSA2 应用私钥',
557
+ '【提醒】',
558
+ '- 本文件包含明文应用私钥,请妥善保管,避免泄露、云同步或提交到代码仓库。',
559
+ '- Java 语言使用 PKCS#8 格式的应用私钥。',
560
+ '- 非 Java 语言使用 PKCS#1 格式的应用私钥。',
561
+ '',
562
+ '[应用私钥 / PKCS#8 Base64]',
563
+ material.privateKeyPkcs8,
564
+ '',
565
+ '[应用私钥 / PKCS#1 Base64]',
566
+ material.privateKeyPkcs1,
567
+ ''
568
+ ].join('\n');
569
+ }
570
+
571
+ let crc32Table = null;
572
+
573
+ function getCrc32Table() {
574
+ if (crc32Table) {
575
+ return crc32Table;
576
+ }
577
+
578
+ crc32Table = new Uint32Array(256);
579
+ for (let index = 0; index < 256; index += 1) {
580
+ let value = index;
581
+ for (let bit = 0; bit < 8; bit += 1) {
582
+ value = (value & 1) ? (0xedb88320 ^ (value >>> 1)) : (value >>> 1);
583
+ }
584
+ crc32Table[index] = value >>> 0;
585
+ }
586
+ return crc32Table;
587
+ }
588
+
589
+ function crc32(bytes) {
590
+ const table = getCrc32Table();
591
+ let value = 0xffffffff;
592
+ for (let index = 0; index < bytes.length; index += 1) {
593
+ value = table[(value ^ bytes[index]) & 0xff] ^ (value >>> 8);
594
+ }
595
+ return (value ^ 0xffffffff) >>> 0;
596
+ }
597
+
598
+ function writeUint16(bytes, offset, value) {
599
+ bytes[offset] = value & 0xff;
600
+ bytes[offset + 1] = (value >>> 8) & 0xff;
601
+ }
602
+
603
+ function writeUint32(bytes, offset, value) {
604
+ bytes[offset] = value & 0xff;
605
+ bytes[offset + 1] = (value >>> 8) & 0xff;
606
+ bytes[offset + 2] = (value >>> 16) & 0xff;
607
+ bytes[offset + 3] = (value >>> 24) & 0xff;
608
+ }
609
+
610
+ function createKeyPairZipBytes(material) {
611
+ const encoder = new TextEncoder();
612
+ const files = [
613
+ { name: '应用公钥.txt', content: createPublicKeyFile(material) },
614
+ { name: '应用私钥.txt', content: createPrivateKeyFile(material) }
615
+ ].map(function (file) {
616
+ return {
617
+ name: encoder.encode(file.name),
618
+ content: encoder.encode(file.content)
619
+ };
620
+ });
621
+
622
+ let localSize = 0;
623
+ let centralSize = 0;
624
+ for (const file of files) {
625
+ file.crc = crc32(file.content);
626
+ file.offset = localSize;
627
+ localSize += 30 + file.name.length + file.content.length;
628
+ centralSize += 46 + file.name.length;
629
+ }
630
+
631
+ const centralOffset = localSize;
632
+ const bytes = new Uint8Array(localSize + centralSize + 22);
633
+ let offset = 0;
634
+ for (const file of files) {
635
+ writeUint32(bytes, offset, 0x04034b50);
636
+ writeUint16(bytes, offset + 4, 20);
637
+ writeUint16(bytes, offset + 6, 0x0800);
638
+ writeUint16(bytes, offset + 8, 0);
639
+ writeUint16(bytes, offset + 10, 0);
640
+ writeUint16(bytes, offset + 12, 0);
641
+ writeUint32(bytes, offset + 14, file.crc);
642
+ writeUint32(bytes, offset + 18, file.content.length);
643
+ writeUint32(bytes, offset + 22, file.content.length);
644
+ writeUint16(bytes, offset + 26, file.name.length);
645
+ writeUint16(bytes, offset + 28, 0);
646
+ offset += 30;
647
+ bytes.set(file.name, offset);
648
+ offset += file.name.length;
649
+ bytes.set(file.content, offset);
650
+ offset += file.content.length;
651
+ }
652
+
653
+ for (const file of files) {
654
+ writeUint32(bytes, offset, 0x02014b50);
655
+ writeUint16(bytes, offset + 4, 20);
656
+ writeUint16(bytes, offset + 6, 20);
657
+ writeUint16(bytes, offset + 8, 0x0800);
658
+ writeUint16(bytes, offset + 10, 0);
659
+ writeUint16(bytes, offset + 12, 0);
660
+ writeUint16(bytes, offset + 14, 0);
661
+ writeUint32(bytes, offset + 16, file.crc);
662
+ writeUint32(bytes, offset + 20, file.content.length);
663
+ writeUint32(bytes, offset + 24, file.content.length);
664
+ writeUint16(bytes, offset + 28, file.name.length);
665
+ writeUint16(bytes, offset + 30, 0);
666
+ writeUint16(bytes, offset + 32, 0);
667
+ writeUint16(bytes, offset + 34, 0);
668
+ writeUint16(bytes, offset + 36, 0);
669
+ writeUint32(bytes, offset + 38, 0);
670
+ writeUint32(bytes, offset + 42, file.offset);
671
+ offset += 46;
672
+ bytes.set(file.name, offset);
673
+ offset += file.name.length;
674
+ }
675
+
676
+ writeUint32(bytes, offset, 0x06054b50);
677
+ writeUint16(bytes, offset + 8, files.length);
678
+ writeUint16(bytes, offset + 10, files.length);
679
+ writeUint32(bytes, offset + 12, centralSize);
680
+ writeUint32(bytes, offset + 16, centralOffset);
681
+ return bytes;
682
+ }
683
+
684
+ function setStatus(element, message, state) {
685
+ element.textContent = message || '';
686
+ if (state) {
687
+ element.dataset.state = state;
688
+ } else {
689
+ delete element.dataset.state;
690
+ }
691
+ }
692
+
693
+ function fallbackCopyPublicKey() {
694
+ ui.publicKeyOutput.focus();
695
+ ui.publicKeyOutput.select();
696
+ if (!document.execCommand('copy')) {
697
+ throw new Error('copy failed');
698
+ }
699
+ }
700
+
701
+ async function handleCopyPublicKey() {
702
+ const value = ui.publicKeyOutput.value;
703
+ if (!value || ui.copyPublicButton.disabled) {
704
+ return;
705
+ }
706
+
707
+ const operationId = appState.operationEpoch;
708
+ if (appState.copyLabelTimer) {
709
+ clearTimeout(appState.copyLabelTimer);
710
+ appState.copyLabelTimer = null;
711
+ }
712
+ ui.copyPublicButton.disabled = true;
713
+ let label = '已复制';
714
+ let message = '应用公钥已复制,可粘贴给 Agent。';
715
+ let state = 'success';
716
+
717
+ try {
718
+ let copied = false;
719
+ if (navigator.clipboard && globalThis.isSecureContext) {
720
+ try {
721
+ await navigator.clipboard.writeText(value);
722
+ copied = true;
723
+ } catch {}
724
+ }
725
+ if (!copied) {
726
+ fallbackCopyPublicKey();
727
+ }
728
+ } catch (error) {
729
+ if (operationId === appState.operationEpoch) {
730
+ ui.publicKeyOutput.focus();
731
+ ui.publicKeyOutput.select();
732
+ }
733
+ label = '复制失败';
734
+ message = '自动复制失败,应用公钥已选中,请使用系统复制命令。';
735
+ state = 'error';
736
+ }
737
+
738
+ if (operationId !== appState.operationEpoch || !appState.lastGenerated) {
739
+ return;
740
+ }
741
+
742
+ ui.copyPublicButton.disabled = false;
743
+ ui.copyPublicButton.textContent = label;
744
+ setStatus(ui.actionStatus, message, state);
745
+ appState.copyLabelTimer = setTimeout(function () {
746
+ if (operationId === appState.operationEpoch) {
747
+ ui.copyPublicButton.textContent = '复制';
748
+ appState.copyLabelTimer = null;
749
+ }
750
+ }, 1600);
751
+ }
752
+
753
+ function downloadBytes(filename, bytes, mimeType, statusElement) {
754
+ try {
755
+ const blob = new Blob([bytes], { type: mimeType });
756
+ const url = URL.createObjectURL(blob);
757
+ const anchor = document.createElement('a');
758
+ anchor.href = url;
759
+ anchor.download = filename;
760
+ document.body.appendChild(anchor);
761
+ anchor.click();
762
+ anchor.remove();
763
+ setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
764
+ setStatus(statusElement, '已向浏览器请求下载,页面清除不会删除已下载文件。', 'success');
765
+ } catch {
766
+ setStatus(statusElement, '下载失败,请检查浏览器下载权限后重试;确认保存前不要清除或关闭页面。', 'error');
767
+ }
768
+ }
769
+
770
+ let ui = null;
771
+ let appState = null;
772
+
773
+ function clearKeyMaterial(announce) {
774
+ appState.operationEpoch += 1;
775
+ if (appState.copyLabelTimer) {
776
+ clearTimeout(appState.copyLabelTimer);
777
+ appState.copyLabelTimer = null;
778
+ }
779
+ appState.lastGenerated = null;
780
+ ui.publicKeyOutput.value = '';
781
+ delete ui.generateButton.dataset.state;
782
+ ui.copyPublicButton.textContent = '复制';
783
+ ui.copyPublicButton.disabled = true;
784
+ ui.keyResult.hidden = true;
785
+ ui.downloadKeyPairButton.disabled = true;
786
+ if (announce) {
787
+ setStatus(ui.generateStatus, '当前页面中的密钥已清空。已下载文件不会被删除,请自行安全保管或删除。', 'success');
788
+ }
789
+ setStatus(ui.actionStatus, '', null);
790
+ }
791
+
792
+ function setGenerateBusy(busy) {
793
+ appState.generating = busy;
794
+ ui.generateButton.disabled = busy || !appState.supported;
795
+ if (busy) {
796
+ delete ui.generateButton.dataset.state;
797
+ ui.generateButton.textContent = '正在生成...';
798
+ } else if (appState.lastGenerated) {
799
+ ui.generateButton.dataset.state = 'success';
800
+ ui.generateButton.textContent = '重新生成应用密钥对';
801
+ } else {
802
+ delete ui.generateButton.dataset.state;
803
+ ui.generateButton.textContent = '一键生成应用密钥对';
804
+ }
805
+ }
806
+
807
+ async function handleGenerate() {
808
+ if (appState.generating || !appState.supported) {
809
+ return;
810
+ }
811
+
812
+ if (appState.lastGenerated && !globalThis.confirm('重新生成将清空当前页面中的密钥,但不会删除已下载文件。是否继续?')) {
813
+ return;
814
+ }
815
+
816
+ clearKeyMaterial(false);
817
+ const operationId = appState.operationEpoch;
818
+ setGenerateBusy(true);
819
+ setStatus(ui.generateStatus, '正在生成并验证 RSA2 应用密钥对...', 'working');
820
+
821
+ try {
822
+ const material = await generateKeyMaterial();
823
+ if (operationId !== appState.operationEpoch) {
824
+ return;
825
+ }
826
+ appState.lastGenerated = material;
827
+ ui.publicKeyOutput.value = material.publicKey;
828
+ ui.keyResult.hidden = false;
829
+ ui.copyPublicButton.disabled = false;
830
+ ui.downloadKeyPairButton.disabled = false;
831
+ setStatus(ui.generateStatus, '', null);
832
+ ui.resultTitle.focus();
833
+ } catch (error) {
834
+ if (operationId !== appState.operationEpoch) {
835
+ return;
836
+ }
837
+ clearKeyMaterial(false);
838
+ setGenerateBusy(false);
839
+ setStatus(ui.generateStatus, error instanceof UserFacingError ? error.message : '密钥生成失败,请确认浏览器支持 Web Crypto 后重试。', 'error');
840
+ } finally {
841
+ if (operationId === appState.operationEpoch) {
842
+ setGenerateBusy(false);
843
+ }
844
+ }
845
+ }
846
+
847
+ function detectCapabilities() {
848
+ const missing = [];
849
+ if (!cryptoApi || !cryptoApi.subtle || typeof cryptoApi.getRandomValues !== 'function') {
850
+ missing.push('Web Crypto');
851
+ }
852
+ if (typeof btoa !== 'function') {
853
+ missing.push('Base64');
854
+ }
855
+ if (typeof Blob === 'undefined' || !globalThis.URL || typeof URL.createObjectURL !== 'function') {
856
+ missing.push('文件下载');
857
+ }
858
+
859
+ appState.supported = missing.length === 0;
860
+ if (appState.supported) {
861
+ ui.capabilityNotice.hidden = true;
862
+ } else {
863
+ ui.capabilityNotice.textContent = '当前浏览器缺少必要能力:' + missing.join('、') + '。请使用最新版 Chrome、Edge、Firefox 或 Safari。';
864
+ ui.capabilityNotice.hidden = false;
865
+ }
866
+ }
867
+
868
+ function initialize() {
869
+ ui = {
870
+ capabilityNotice: document.getElementById('capabilityNotice'),
871
+ generateButton: document.getElementById('generateButton'),
872
+ generateStatus: document.getElementById('generateStatus'),
873
+ keyResult: document.getElementById('keyResult'),
874
+ resultTitle: document.getElementById('resultTitle'),
875
+ publicKeyOutput: document.getElementById('publicKeyOutput'),
876
+ copyPublicButton: document.getElementById('copyPublicButton'),
877
+ downloadKeyPairButton: document.getElementById('downloadKeyPairButton'),
878
+ clearButton: document.getElementById('clearButton'),
879
+ actionStatus: document.getElementById('actionStatus')
880
+ };
881
+ appState = {
882
+ supported: false,
883
+ generating: false,
884
+ lastGenerated: null,
885
+ copyLabelTimer: null,
886
+ operationEpoch: 0
887
+ };
888
+
889
+ detectCapabilities();
890
+ setGenerateBusy(false);
891
+ ui.generateButton.addEventListener('click', handleGenerate);
892
+ ui.copyPublicButton.addEventListener('click', handleCopyPublicKey);
893
+ ui.downloadKeyPairButton.addEventListener('click', function () {
894
+ if (appState.lastGenerated) {
895
+ downloadBytes('应用密钥对.zip', createKeyPairZipBytes(appState.lastGenerated), 'application/zip', ui.actionStatus);
896
+ }
897
+ });
898
+ ui.clearButton.addEventListener('click', function () {
899
+ if (appState.lastGenerated && globalThis.confirm('确定清空当前页面中的密钥?已下载文件不会被删除。')) {
900
+ clearKeyMaterial(true);
901
+ setGenerateBusy(false);
902
+ }
903
+ });
904
+ globalThis.addEventListener('pagehide', function () {
905
+ clearKeyMaterial(false);
906
+ setGenerateBusy(false);
907
+ });
908
+ }
909
+
910
+ if (typeof document === 'undefined') {
911
+ globalThis.__rsaKeygenCore = Object.freeze({
912
+ RSA_ALGORITHM: RSA_ALGORITHM,
913
+ bytesToBase64: bytesToBase64,
914
+ readDerElement: readDerElement,
915
+ pkcs8ToPkcs1: pkcs8ToPkcs1,
916
+ generateKeyMaterial: generateKeyMaterial,
917
+ createPublicKeyFile: createPublicKeyFile,
918
+ createPrivateKeyFile: createPrivateKeyFile,
919
+ createKeyPairZipBytes: createKeyPairZipBytes,
920
+ wipe: wipe
921
+ });
922
+ } else {
923
+ initialize();
924
+ }
925
+ </script>
926
+ </body>
927
+ </html>