@ohos-cpf/3rdloop 0.0.1 → 0.0.3

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 (393) hide show
  1. package/README.md +245 -425
  2. package/bin/3rdloop.mjs +46 -46
  3. package/lib/cli.js +781 -740
  4. package/lib/config-cmd.js +452 -0
  5. package/lib/config.js +318 -317
  6. package/lib/doctor.js +113 -113
  7. package/lib/exit-codes.js +30 -30
  8. package/lib/opencode.js +155 -155
  9. package/lib/orch.js +426 -426
  10. package/lib/queries.js +263 -263
  11. package/lib/step.js +325 -325
  12. package/lib/ui.js +222 -222
  13. package/lib/update.js +280 -0
  14. package/lib/workflow.js +308 -290
  15. package/lib/workflows.js +131 -131
  16. package/package.json +50 -50
  17. package/vendor/Server/Agent/SkillSelector/README.md +343 -343
  18. package/vendor/Server/Agent/SkillSelector/core/Agent.js +207 -207
  19. package/vendor/Server/Agent/SkillSelector/index.js +14 -14
  20. package/vendor/Server/Agent/SkillSelector/llm/llmClient.js +313 -313
  21. package/vendor/Server/Agent/SkillSelector/llm/prompts.js +161 -161
  22. package/vendor/Server/Agent/SkillSelector/llm/reactSteps.js +144 -144
  23. package/vendor/Server/Agent/SkillSelector/matcher/fuzzyMatcher.js +433 -433
  24. package/vendor/Server/Agent/SkillSelector/scanner/skillScanner.js +382 -382
  25. package/vendor/Server/Agent/SkillSelector/utils/actionWords.js +59 -59
  26. package/vendor/Server/Brain/AnalysisStrategies.js +422 -422
  27. package/vendor/Server/Brain/Brain.js +686 -686
  28. package/vendor/Server/Brain/DecompositionStrategies.js +621 -621
  29. package/vendor/Server/Brain/PlanPersistence.js +196 -196
  30. package/vendor/Server/Brain/SkillExecutor.js +251 -251
  31. package/vendor/Server/Brain/index.js +11 -11
  32. package/vendor/Server/CLI/cli.js +275 -243
  33. package/vendor/Server/CLI/deveco-code/index.js +71 -0
  34. package/vendor/Server/CLI/deveco-code.js +5 -0
  35. package/vendor/Server/CLI/opencode/index.js +817 -804
  36. package/vendor/Server/CLI/opencode.js +5 -5
  37. package/vendor/Server/DbUse/MessageArchive.js +61 -61
  38. package/vendor/Server/DbUse/StorageManager.js +107 -107
  39. package/vendor/Server/DbUse/adapters/BaseAdapter.js +94 -94
  40. package/vendor/Server/DbUse/adapters/FileAdapter.js +138 -138
  41. package/vendor/Server/DbUse/adapters/JsonAdapter.js +138 -138
  42. package/vendor/Server/DbUse/adapters/JsonlAdapter.js +114 -114
  43. package/vendor/Server/DbUse/adapters/SqliteAdapter.js +157 -157
  44. package/vendor/Server/DbUse/index.js +160 -160
  45. package/vendor/Server/DbUse/stores/SessionStore.js +279 -279
  46. package/vendor/Server/DbUse/stores/TaskExecutionStore.js +133 -133
  47. package/vendor/Server/DbUse/stores/TaskRegistryStore.js +262 -262
  48. package/vendor/Server/DbUse/stores/TaskStepStore.js +279 -279
  49. package/vendor/Server/FlexRunner/FlexRunner.js +1759 -1759
  50. package/vendor/Server/FlexRunner/index.js +12 -12
  51. package/vendor/Server/Knowledge/KnowledgeImporter.js +523 -523
  52. package/vendor/Server/Knowledge/KnowledgeManager.js +341 -341
  53. package/vendor/Server/Knowledge/index.js +33 -33
  54. package/vendor/Server/LoopEngine/LoopEngine.js +1133 -1133
  55. package/vendor/Server/LoopEngine/index.js +11 -11
  56. package/vendor/Server/Orchestrator/Orchestrator.js +0 -3
  57. package/vendor/Server/Orchestrator/StepNode.js +312 -312
  58. package/vendor/Server/Orchestrator/index.js +13 -13
  59. package/vendor/Server/Routes/controllers/BrainController.js +241 -241
  60. package/vendor/Server/Routes/controllers/FlexRunnerController.js +1125 -1125
  61. package/vendor/Server/Routes/controllers/HealthController.js +44 -44
  62. package/vendor/Server/Routes/controllers/KnowledgeImportController.js +355 -355
  63. package/vendor/Server/Routes/controllers/LoopEngineController.js +788 -788
  64. package/vendor/Server/Routes/controllers/StatsController.js +57 -57
  65. package/vendor/Server/Routes/controllers/StreamController.js +67 -67
  66. package/vendor/Server/Routes/library/logger.js +179 -179
  67. package/vendor/Server/Routes/library/zipArchive.js +172 -172
  68. package/vendor/Server/Routes/router/Router.js +204 -204
  69. package/vendor/Server/Routes/routes/brain.js +47 -47
  70. package/vendor/Server/Routes/routes/flexrunner.js +72 -72
  71. package/vendor/Server/Routes/routes/health.js +28 -28
  72. package/vendor/Server/Routes/routes/index.js +57 -57
  73. package/vendor/Server/Routes/routes/knowledge.js +55 -55
  74. package/vendor/Server/Routes/routes/loopengine.js +58 -58
  75. package/vendor/Server/Routes/routes/orchestrator.js +61 -61
  76. package/vendor/Server/Routes/routes/registry.js +97 -97
  77. package/vendor/Server/Routes/routes/stats.js +23 -23
  78. package/vendor/Server/Routes/routes/stream.js +30 -30
  79. package/vendor/Server/Routes/server.js +203 -202
  80. package/vendor/Server/Routes/sse/SseHub.js +308 -308
  81. package/vendor/Server/Skills/acceptance-check/SKILL.md +191 -174
  82. package/vendor/Server/Skills/acceptance-check/references/PHASE1_ARTIFACT_COLLECTION.md +52 -52
  83. package/vendor/Server/Skills/acceptance-check/references/PHASE2_CRITERIA_EVALUATION.md +77 -77
  84. package/vendor/Server/Skills/acceptance-check/references/PHASE2_EVIDENCE_VERIFICATION.md +70 -0
  85. package/vendor/Server/Skills/arkts-code-check/SKILL.md +320 -313
  86. package/vendor/Server/Skills/arkts-code-check/references/CODE_CHECK_GUIDE.md +210 -159
  87. package/vendor/Server/Skills/arkts-code-check/references/FILE_TYPE_GUIDE.md +28 -28
  88. package/vendor/Server/Skills/arkts-code-check/references/PHASE1_INFO_COLLECTION.md +233 -222
  89. package/vendor/Server/Skills/arkts-code-check/references/REPORT_TEMPLATE.md +219 -219
  90. package/vendor/Server/Skills/arkts-library-compile-validate/SKILL.md +328 -328
  91. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/build.ps1 +93 -93
  92. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/code-linter.cjs +432 -432
  93. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/migrate.cjs +851 -851
  94. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/ohpm-switch.cjs +788 -788
  95. package/vendor/Server/Skills/arkts-library-demo-code/SKILL.md +291 -291
  96. package/vendor/Server/Skills/arkts-library-demo-code/references/API_SIGNATURE_CHECKS.md +68 -68
  97. package/vendor/Server/Skills/arkts-library-demo-code/references/BUILD_VALIDATION.md +184 -184
  98. package/vendor/Server/Skills/arkts-library-demo-code/references/CODE_STANDARDS.md +337 -337
  99. package/vendor/Server/Skills/arkts-library-demo-code/references/EXAMPLES.md +400 -400
  100. package/vendor/Server/Skills/arkts-library-demo-code/references/SYSTEM_API_LOOKUP.md +98 -98
  101. package/vendor/Server/Skills/arkts-library-demo-code/references/TEMPLATE.md +544 -544
  102. package/vendor/Server/Skills/arkts-library-demo-code/scripts/build.ps1 +93 -93
  103. package/vendor/Server/Skills/arkts-library-demo-code/scripts/code-linter.cjs +432 -432
  104. package/vendor/Server/Skills/arkts-library-demo-code/scripts/migrate.cjs +851 -851
  105. package/vendor/Server/Skills/arkts-library-demo-code/scripts/ohpm-switch.cjs +788 -788
  106. package/vendor/Server/Skills/arkts-library-demo-coverage/SKILL.md +174 -174
  107. package/vendor/Server/Skills/arkts-library-demo-scenario/SKILL.md +321 -321
  108. package/vendor/Server/Skills/arkts-library-demo-scenario/references/EXAMPLES.md +100 -100
  109. package/vendor/Server/Skills/arkts-library-demo-scenario/references/TEMPLATE.md +185 -185
  110. package/vendor/Server/Skills/arkts-library-documentation-quality-check/SKILL.md +233 -233
  111. package/vendor/Server/Skills/arkts-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +102 -102
  112. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/CHECK_DIMENSIONS.md +89 -89
  113. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/COMPLIANCE_CHECKLIST.md +62 -62
  114. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/KB_QUERY_GUIDE.md +107 -107
  115. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/MCP_TOOL_DISCOVERY.md +124 -124
  116. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/REPORT_TEMPLATE.md +90 -90
  117. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/SCORING_FORMULA.md +57 -57
  118. package/vendor/Server/Skills/arkts-library-documentation-quality-check/scripts/validate-skill.cjs +259 -259
  119. package/vendor/Server/Skills/arkts-library-interface-spec/SKILL.md +97 -97
  120. package/vendor/Server/Skills/arkts-library-memory-leak-check/SKILL.md +391 -391
  121. package/vendor/Server/Skills/arkts-library-source-clone/SKILL.md +113 -113
  122. package/vendor/Server/Skills/arkts-library-stability-check/SKILL.md +312 -312
  123. package/vendor/Server/Skills/arkts-library-stability-check/assets/REPORT_TEMPLATE.md +154 -154
  124. package/vendor/Server/Skills/arkts-library-stability-check/references/CHECKLIST.md +165 -165
  125. package/vendor/Server/Skills/arkts-library-stability-check/references/CODE_EXAMPLES.md +308 -308
  126. package/vendor/Server/Skills/arkts-library-stability-check/references/SCORING_RULES.md +90 -90
  127. package/vendor/Server/Skills/arkts-library-stability-check/scripts/validate-skill.cjs +282 -282
  128. package/vendor/Server/Skills/arkts-library-test-coverage-check/SKILL.md +360 -360
  129. package/vendor/Server/Skills/arkts-library-test-coverage-check/assets/REPORT_TEMPLATE.md +145 -145
  130. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/CHECKLIST.md +252 -252
  131. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +114 -114
  132. package/vendor/Server/Skills/arkts-library-test-coverage-check/scripts/validate-skill.cjs +262 -262
  133. package/vendor/Server/Skills/arkts-library-xts-code/SKILL.md +455 -455
  134. package/vendor/Server/Skills/arkts-library-xts-code/assets/report-template.md +60 -60
  135. package/vendor/Server/Skills/arkts-library-xts-code/references/ACCESS_MODIFIER_PRECHECK.md +76 -76
  136. package/vendor/Server/Skills/arkts-library-xts-code/references/BUILD_VERIFICATION.md +200 -200
  137. package/vendor/Server/Skills/arkts-library-xts-code/references/CODE_STANDARDS.md +518 -518
  138. package/vendor/Server/Skills/arkts-library-xts-code/references/SUBAGENT_PROMPT_TEMPLATE.md +276 -276
  139. package/vendor/Server/Skills/arkts-library-xts-code/references/SYSTEM_API_LOOKUP.md +92 -92
  140. package/vendor/Server/Skills/arkts-library-xts-code/references/XTS_TEMPLATE.md +767 -767
  141. package/vendor/Server/Skills/arkts-library-xts-code/scripts/build.ps1 +93 -93
  142. package/vendor/Server/Skills/arkts-library-xts-code/scripts/code-linter.cjs +432 -432
  143. package/vendor/Server/Skills/arkts-library-xts-code/scripts/migrate.cjs +851 -851
  144. package/vendor/Server/Skills/arkts-library-xts-code/scripts/ohpm-switch.cjs +788 -788
  145. package/vendor/Server/Skills/arkts-library-xts-code/scripts/precheck-access-modifiers.cjs +221 -221
  146. package/vendor/Server/Skills/arkts-library-xts-code-fix/SKILL.md +228 -228
  147. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +97 -97
  148. package/vendor/Server/Skills/arkts-library-xts-doc/SKILL.md +194 -194
  149. package/vendor/Server/Skills/arkts-library-xts-doc/references/EXAMPLES.md +575 -575
  150. package/vendor/Server/Skills/arkts-library-xts-doc/references/TEMPLATE.md +574 -574
  151. package/vendor/Server/Skills/arkts-library-xts-doc/references/TESTABILITY_GUIDE.md +182 -182
  152. package/vendor/Server/Skills/arkts-library-xts-execution/SKILL.md +490 -490
  153. package/vendor/Server/Skills/arkts-library-xts-execution/references/error-fixing-examples.md +217 -217
  154. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/build.ps1 +93 -93
  155. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/code-linter.cjs +432 -432
  156. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/migrate.cjs +851 -851
  157. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/ohpm-switch.cjs +788 -788
  158. package/vendor/Server/Skills/flutter-code-check/SKILL.md +299 -299
  159. package/vendor/Server/Skills/flutter-code-check/assets/OUTPUT_TEMPLATE.md +311 -311
  160. package/vendor/Server/Skills/flutter-code-check/references/CHECKLIST.md +190 -190
  161. package/vendor/Server/Skills/flutter-code-check/references/PLATFORMS.md +133 -133
  162. package/vendor/Server/Skills/flutter-code-check/scripts/validate-skill.cjs +259 -259
  163. package/vendor/Server/Skills/flutter-interface/README.md +75 -75
  164. package/vendor/Server/Skills/flutter-interface/SKILL.md +291 -291
  165. package/vendor/Server/Skills/flutter-interface/references/EXAMPLES.md +336 -336
  166. package/vendor/Server/Skills/flutter-interface/references/GIT_CLONE_GUIDE.md +190 -190
  167. package/vendor/Server/Skills/flutter-interface/references/SCRIPT_REFERENCE.md +258 -258
  168. package/vendor/Server/Skills/flutter-interface/references/TEMPLATE.md +254 -254
  169. package/vendor/Server/Skills/flutter-library-consistency-check/SKILL.md +517 -517
  170. package/vendor/Server/Skills/flutter-library-consistency-check/references/CHECKLIST.md +381 -381
  171. package/vendor/Server/Skills/flutter-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +341 -341
  172. package/vendor/Server/Skills/flutter-library-consistency-check/references/PLATFORM_LIMITATION_GUIDE.md +71 -71
  173. package/vendor/Server/Skills/flutter-library-documentation-quality-check/SKILL.md +518 -518
  174. package/vendor/Server/Skills/flutter-library-documentation-quality-check/references/CHECKLIST.md +406 -406
  175. package/vendor/Server/Skills/flutter-library-memory-leak-check/SKILL.md +652 -652
  176. package/vendor/Server/Skills/flutter-library-memory-leak-check/references/CHECKLIST.md +396 -396
  177. package/vendor/Server/Skills/flutter-library-stability-check/SKILL.md +744 -744
  178. package/vendor/Server/Skills/flutter-library-stability-check/references/CHECKLIST.md +720 -720
  179. package/vendor/Server/Skills/flutter-library-test-coverage-check/SKILL.md +414 -414
  180. package/vendor/Server/Skills/flutter-library-test-coverage-check/assets/REPORT_TEMPLATE.md +150 -150
  181. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/CHECKLIST.md +304 -304
  182. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCORING_RULES.md +265 -265
  183. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCRIPT_USAGE.md +286 -286
  184. package/vendor/Server/Skills/knowledge-import/SKILL.md +383 -383
  185. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +165 -165
  186. package/vendor/Server/Skills/knowledge-import/references/SKILL_LOCATOR_GUIDE.md +91 -91
  187. package/vendor/Server/Skills/knowledge-import/references/SKILL_OPTIMIZATION_GUIDE.md +127 -127
  188. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +453 -301
  189. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_CHECKLIST.md +151 -0
  190. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_ISSUE_TEMPLATE.md +257 -105
  191. package/vendor/Server/Skills/ohos-lib-pr-push/references/GITCODE_API.md +191 -127
  192. package/vendor/Server/Skills/ohos-lib-pr-push/scripts/gitcode-api.cjs +317 -208
  193. package/vendor/Server/Skills/rn-code-check/SKILL.md +320 -320
  194. package/vendor/Server/Skills/rn-code-check/references/API-GUIDE.md +234 -234
  195. package/vendor/Server/Skills/rn-code-check/references/CHECKLIST.md +188 -188
  196. package/vendor/Server/Skills/rn-code-check/references/REPORT-TEMPLATE.md +246 -246
  197. package/vendor/Server/Skills/rn-lib-add-example/SKILL.md +198 -198
  198. package/vendor/Server/Skills/rn-lib-add-example/references/APP_RENAME_GUIDE.md +40 -40
  199. package/vendor/Server/Skills/rn-lib-add-example/references/SHELL_ADAPTATION_CHECKLIST.md +78 -78
  200. package/vendor/Server/Skills/rn-lib-add-example/references/TEMPLATE_REPO_GUIDE.md +70 -70
  201. package/vendor/Server/Skills/rn-lib-add-example/references/VERIFICATION_GUIDE.md +47 -47
  202. package/vendor/Server/Skills/rn-lib-add-example/scripts/create-example.cjs +252 -252
  203. package/vendor/Server/Skills/rn-lib-update-doc/SKILL.md +176 -176
  204. package/vendor/Server/Skills/rn-lib-update-doc/config.json +5 -5
  205. package/vendor/Server/Skills/rn-lib-update-doc/references/changelog-template.md +35 -35
  206. package/vendor/Server/Skills/rn-lib-update-doc/references/format-diff.md +137 -137
  207. package/vendor/Server/Skills/rn-lib-update-doc/references/link-registry.json +33 -33
  208. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2-overview.md +40 -40
  209. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2.md +429 -429
  210. package/vendor/Server/Skills/rn-lib-update-doc/scripts/detect-readme-format.cjs +105 -105
  211. package/vendor/Server/Skills/rn-lib-update-doc/scripts/find-usage-doc.cjs +90 -90
  212. package/vendor/Server/Skills/rn-lib-update-doc/scripts/link-verify.cjs +213 -213
  213. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-changelog.cjs +73 -73
  214. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-package-meta.cjs +258 -258
  215. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-readme-links.cjs +341 -341
  216. package/vendor/Server/Skills/rn-lib-update-doc/scripts/resolve-usage-docs.cjs +67 -67
  217. package/vendor/Server/Skills/rn-library-consistency-check/SKILL.md +299 -299
  218. package/vendor/Server/Skills/rn-library-consistency-check/assets/REPORT_TEMPLATE.md +293 -293
  219. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_COMPONENT_CHECK_GUIDE.md +857 -857
  220. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_TURBOMODULE_CHECK_GUIDE.md +856 -856
  221. package/vendor/Server/Skills/rn-library-consistency-check/references/CHECKLIST.md +157 -157
  222. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_COMPONENT_CHECK_GUIDE.md +801 -801
  223. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_TURBOMODULE_CHECK_GUIDE.md +683 -683
  224. package/vendor/Server/Skills/rn-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +233 -233
  225. package/vendor/Server/Skills/rn-library-consistency-check/references/NATIVE_COMPONENT_IDENTIFICATION.md +1373 -1373
  226. package/vendor/Server/Skills/rn-library-consistency-check/references/SCORING_STANDARD.md +220 -220
  227. package/vendor/Server/Skills/rn-library-consistency-check/scripts/validate-skill.py +245 -245
  228. package/vendor/Server/Skills/rn-library-documentation-quality-check/SKILL.md +263 -263
  229. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL.md +414 -414
  230. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL_en.md +413 -413
  231. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +311 -311
  232. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CHECK_CHECKLIST.md +355 -355
  233. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CROSS_VALIDATION_GUIDE.md +115 -115
  234. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/DOC_COLLECTION_GUIDE.md +139 -139
  235. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/EXAMPLES.md +586 -586
  236. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/GOTCHAS.md +57 -57
  237. package/vendor/Server/Skills/rn-library-documentation-quality-check/scripts/validate-skill.py +259 -259
  238. package/vendor/Server/Skills/rn-library-interface-analyzer/SKILL.md +301 -301
  239. package/vendor/Server/Skills/rn-library-interface-analyzer/assets/REPORT_TEMPLATE.md +148 -148
  240. package/vendor/Server/Skills/rn-library-interface-analyzer/references/DOC_ANNOTATION_GUIDE.md +118 -118
  241. package/vendor/Server/Skills/rn-library-interface-analyzer/references/SCRIPT_USAGE_GUIDE.md +168 -168
  242. package/vendor/Server/Skills/rn-library-interface-analyzer/references/URL_PARSING_RULES.md +176 -176
  243. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +274 -274
  244. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/case-template.md +45 -45
  245. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/rule-template.md +25 -25
  246. package/vendor/Server/Skills/rn-library-issue-generalizer/references/PR_FETCH_GUIDE.md +61 -61
  247. package/vendor/Server/Skills/rn-library-issue-generalizer/references/RULE_TEMPLATE_GUIDE.md +165 -165
  248. package/vendor/Server/Skills/rn-library-issue-generalizer/scripts/fetch-pr-info.py +239 -239
  249. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +263 -263
  250. package/vendor/Server/Skills/rn-library-known-issue-check/assets/REPORT_TEMPLATE.md +96 -96
  251. package/vendor/Server/Skills/rn-library-known-issue-check/references/COMPLIANCE_CHECKLIST.md +57 -57
  252. package/vendor/Server/Skills/rn-library-known-issue-check/references/INSPECTION_GUIDE.md +99 -99
  253. package/vendor/Server/Skills/rn-library-known-issue-check/references/PRESCREEN_GUIDE.md +64 -64
  254. package/vendor/Server/Skills/rn-library-known-issue-check/references/SOURCE_PREP_GUIDE.md +74 -74
  255. package/vendor/Server/Skills/rn-library-known-issue-check/scripts/validate-known-issue-report.py +164 -164
  256. package/vendor/Server/Skills/rn-library-memory-leak-check/SKILL.md +356 -339
  257. package/vendor/Server/Skills/rn-library-memory-leak-check/assets/REPORT_TEMPLATE.md +85 -83
  258. package/vendor/Server/Skills/rn-library-memory-leak-check/references/CHECKLIST.md +905 -859
  259. package/vendor/Server/Skills/rn-library-memory-leak-check/references/HARMONY_CODE_PATTERNS.md +124 -122
  260. package/vendor/Server/Skills/rn-library-memory-leak-check/scripts/scan_harmony_code.py +287 -279
  261. package/vendor/Server/Skills/rn-library-stability-check/SKILL.md +273 -273
  262. package/vendor/Server/Skills/rn-library-stability-check/assets/REPORT_TEMPLATE.md +129 -129
  263. package/vendor/Server/Skills/rn-library-stability-check/references/CHECKLIST.md +581 -581
  264. package/vendor/Server/Skills/rn-library-stability-check/references/SCORING_RULES.md +138 -138
  265. package/vendor/Server/Skills/rn-library-stability-check/scripts/scan-harmony-code.py +168 -168
  266. package/vendor/Server/Skills/rn-library-test-coverage-check/SKILL.md +526 -506
  267. package/vendor/Server/Skills/rn-library-test-coverage-check/references/CHECKLIST.md +321 -280
  268. package/vendor/Server/Skills/rn-library-test-coverage-check/references/DEMO_LOCATION.md +221 -221
  269. package/vendor/Server/Skills/rn-library-test-coverage-check/references/REPORT_TEMPLATE.md +220 -209
  270. package/vendor/Server/Skills/rn-library-test-coverage-check/references/SCRIPT_USAGE.md +325 -325
  271. package/vendor/Server/Skills/rn-ohos-xts-generator/SKILL.md +279 -279
  272. package/vendor/Server/Skills/rn-ohos-xts-generator/assets/TEST_FILE_TEMPLATES.md +404 -404
  273. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_TROUBLESHOOTING.md +185 -185
  274. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_WORKFLOW.md +265 -265
  275. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COMMON_CONFIG.md +189 -189
  276. package/vendor/Server/Skills/rn-ohos-xts-generator/references/CONFIG_GUIDE.md +203 -203
  277. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COVERAGE_ANALYZER.md +147 -147
  278. package/vendor/Server/Skills/rn-ohos-xts-generator/references/DESIGN_DOC_GUIDE.md +148 -148
  279. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_FLOW.md +165 -165
  280. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_STRATEGY.md +195 -195
  281. package/vendor/Server/Skills/rn-ohos-xts-generator/references/HARMONY_SCANNER.md +249 -249
  282. package/vendor/Server/Skills/rn-ohos-xts-generator/references/JEST_FRAMEWORK.md +235 -235
  283. package/vendor/Server/Skills/rn-ohos-xts-generator/references/PLATFORM_COMPAT.md +161 -161
  284. package/vendor/Server/Skills/rn-ohos-xts-generator/references/RN_TEST_PATTERNS.md +413 -413
  285. package/vendor/Server/Skills/rn-ohos-xts-generator/references/SOURCE_PARSER.md +235 -235
  286. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TEST_CONVENTIONS.md +245 -245
  287. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TROUBLESHOOTING.md +282 -282
  288. package/vendor/Server/Skills/rn-ohos-xts-generator/references/USAGE_GUIDE.md +251 -251
  289. package/vendor/Server/Skills/rnoh-cptf-api-test/SKILL.md +469 -469
  290. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/CPTF_TEST_TEMPLATE.md +310 -310
  291. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/DEMO_PAGE_TEMPLATE.md +303 -303
  292. package/vendor/Server/Skills/rnoh-cptf-api-test/references/CPTF_TEST_GENERATION_GUIDE.md +421 -421
  293. package/vendor/Server/Skills/rnoh-cptf-api-test/references/DEMO_GENERATION_GUIDE.md +358 -358
  294. package/vendor/Server/Skills/rnoh-cptf-api-test/references/QUALITY_CHECKLIST.md +114 -114
  295. package/vendor/Server/Skills/rnoh-cptf-api-test/references/SPEC_PARSING_GUIDE.md +274 -274
  296. package/vendor/Server/Skills/rnoh-cptf-api-test/scripts/validate-rnoh-cptf-api-test.sh +185 -185
  297. package/vendor/Server/Skills/rnoh-cptf-demo-test/SKILL.md +191 -191
  298. package/vendor/Server/Skills/rnoh-cptf-demo-test/assets/TEST_TEMPLATE.md +216 -216
  299. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/CPTF_SETUP.md +107 -107
  300. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/DEMO_ANALYSIS.md +120 -120
  301. package/vendor/Server/Skills/rnoh-cptf-demo-test/scripts/validate-rnoh-cptf-test.sh +108 -108
  302. package/vendor/Server/Skills/rnoh-cptf-test-run/SKILL.md +237 -237
  303. package/vendor/Server/Skills/rnoh-cptf-test-run/references/DEBUG_PLAYBOOK.md +261 -261
  304. package/vendor/Server/Skills/rnoh-cptf-test-run/scripts/validate-run-env.sh +112 -112
  305. package/vendor/Server/Skills/rnoh-lib-code-adapt/SKILL.md +497 -497
  306. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/adapt-checklist.md +270 -270
  307. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/api-migration-checklist.md +164 -164
  308. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/common-compile-errors.md +556 -556
  309. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/deveco-cli-tools.md +459 -459
  310. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/edge-cases.md +65 -65
  311. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/har-tgz-build.md +512 -512
  312. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/version-compat-matrix.md +132 -132
  313. package/vendor/Server/Skills/rnoh-lib-code-adapt/scripts/auto-integrate.sh +424 -424
  314. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +137 -137
  315. package/vendor/Server/Skills/rnoh-lib-demo-create/SKILL.md +306 -306
  316. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/DEMO_PAGE_TEMPLATE.md +485 -485
  317. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/INDEX_TEMPLATE.md +172 -172
  318. package/vendor/Server/Skills/rnoh-lib-demo-create/references/DEMO_CODE_RULES.md +422 -422
  319. package/vendor/Server/Skills/rnoh-lib-demo-create/references/QUALITY_CHECKLIST.md +95 -95
  320. package/vendor/Server/Skills/rnoh-lib-demo-create/references/SOURCE_EXPLORATION.md +169 -169
  321. package/vendor/Server/Skills/rnoh-lib-demo-doc/SKILL.md +554 -554
  322. package/vendor/Server/Skills/rnoh-lib-demo-doc/assets/DEMO_DOC_TEMPLATE.md +106 -106
  323. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/COVERAGE_INCREMENTAL.md +216 -216
  324. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/DEMO_DESIGN_RULES.md +413 -413
  325. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/OUTPUT_FORMAT.md +269 -269
  326. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/QUALITY_CHECKLIST.md +98 -98
  327. package/vendor/Server/Skills/rnoh-lib-func-test/SKILL.md +337 -337
  328. package/vendor/Server/Skills/rnoh-lib-rnt-search/SKILL.md +722 -722
  329. package/vendor/Server/Skills/rnoh-lib-rnt-search/assets/PORT_REPORT_TEMPLATE.md +219 -219
  330. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/DEMO_PORTING_GUIDE.md +345 -345
  331. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/PORT_VERIFICATION.md +269 -269
  332. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/RNT_PROJECT_STRUCTURE.md +173 -173
  333. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/analyze-demo-deps.cjs +267 -267
  334. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/detect-rnoh-version.cjs +174 -174
  335. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/find-rnt-demo.sh +111 -111
  336. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/port-demo.sh +186 -186
  337. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/read-lib-meta.cjs +150 -150
  338. package/vendor/Server/Skills/rnoh-lib-test-align/SKILL.md +296 -296
  339. package/vendor/Server/Skills/rnoh-lib-test-align/references/jest-troubleshooting.md +371 -371
  340. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +212 -212
  341. package/vendor/Server/Skills/rnoh-lib-test-align/references/runtime-troubleshooting.md +345 -345
  342. package/vendor/Server/Skills/rnoh-lib-test-align/references/unit-test-template.md +223 -223
  343. package/vendor/Server/Skills/rnoh-lib-test-align/scripts/port-test-demo.sh +1053 -1053
  344. package/vendor/Server/Skills/rnoh-lib-version-diff/SKILL.md +295 -255
  345. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_ADAPTATION.md +82 -77
  346. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_API_LOOKUP.md +224 -224
  347. package/vendor/Server/Skills/rnoh-lib-version-diff/references/script-reference.md +175 -174
  348. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/detect-upstream-version.js +967 -967
  349. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/generate-diff.js +1798 -1798
  350. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/package.json +5 -5
  351. package/vendor/Server/Skills/rnoh-lib-version-diff/templates/report-template.md +180 -137
  352. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +145 -145
  353. package/vendor/Server/Skills/skill-generator/SKILL.md +365 -365
  354. package/vendor/Server/Skills/skill-generator/assets/SKILL_TEMPLATE.md +183 -183
  355. package/vendor/Server/Skills/skill-generator/references/COMPLIANCE_CHECKLIST.md +82 -82
  356. package/vendor/Server/Skills/skill-generator/references/KB_QUERY_GUIDE.md +110 -110
  357. package/vendor/Server/Skills/skill-generator/references/MCP_TOOL_DISCOVERY.md +122 -122
  358. package/vendor/Server/Skills/skill-generator/references/SKILL_SPEC.md +121 -121
  359. package/vendor/Server/Skills/skill-generator/scripts/validate-skill.cjs +259 -259
  360. package/vendor/Server/Skills/task-analysis/SKILL.md +234 -234
  361. package/vendor/Server/Skills/task-analysis/assets/OUTPUT_EXAMPLES.md +160 -160
  362. package/vendor/Server/Skills/task-analysis/references/ACCEPTANCE_CRITERIA_GUIDE.md +190 -190
  363. package/vendor/Server/Skills/task-analysis/references/ANALYSIS_SCHEMA.md +224 -224
  364. package/vendor/Server/Skills/task-analysis/references/INTEGRATION_CONTEXT.md +162 -162
  365. package/vendor/Server/Skills/task-analysis/references/LESSONS_LEARNED.md +55 -55
  366. package/vendor/Server/Skills/task-analysis/references/REPLAN_CONTEXT_GUIDE.md +126 -126
  367. package/vendor/Server/Skills/task-analysis/scripts/validate-analysis-output.sh +256 -256
  368. package/vendor/Server/Skills/task-decomposition/SKILL.md +528 -528
  369. package/vendor/Server/Skills/task-decomposition/assets/OUTPUT_EXAMPLES.md +451 -451
  370. package/vendor/Server/Skills/task-decomposition/references/ACCEPTANCE_CRITERIA_GUIDE.md +179 -179
  371. package/vendor/Server/Skills/task-decomposition/references/DEPENDENCY_GRAPH_GUIDE.md +152 -152
  372. package/vendor/Server/Skills/task-decomposition/references/INTEGRATION_CONTEXT.md +175 -175
  373. package/vendor/Server/Skills/task-decomposition/references/REPLAN_CONTEXT_GUIDE.md +195 -195
  374. package/vendor/Server/Skills/task-decomposition/references/TASK_DECOMPOSITION_TEMPLATES.md +226 -226
  375. package/vendor/Server/Skills/task-decomposition/scripts/list-available-skills.cjs +263 -263
  376. package/vendor/Server/Skills/task-decomposition/scripts/skill-blacklist.json +29 -29
  377. package/vendor/Server/Skills/task-decomposition/scripts/validate-decomposition-output.cjs +376 -376
  378. package/vendor/Server/Skills/task-decomposition/scripts/validate-param-consistency.cjs +259 -259
  379. package/vendor/Server/Skills/task-decomposition/scripts/validate-specified-skills.cjs +216 -216
  380. package/vendor/Server/Skills/task-decomposition/scripts/validate-step-graph.cjs +424 -424
  381. package/vendor/Server/Skills/tpc-cpp-check/SKILL.md +427 -427
  382. package/vendor/Server/Skills/tpc-cpp-check/references/CHECKLIST.md +214 -214
  383. package/vendor/Server/Skills/tpc-cpp-check/references/HPKBUILD_GUIDE.md +235 -235
  384. package/vendor/Server/Skills/tpc-cpp-check/references/REPORT_TEMPLATE.md +250 -250
  385. package/vendor/Server/TestCheck/TestCheck.js +775 -775
  386. package/vendor/Server/TestCheck/index.js +11 -11
  387. package/vendor/Server/library/McpToolGuide.js +280 -280
  388. package/vendor/Server/library/StatsService.js +126 -126
  389. package/vendor/Server/library/cstTime.js +16 -16
  390. package/vendor/VERSION +3 -3
  391. package/workflows/pr-check.mjs +105 -105
  392. package/workflows/pr-push.mjs +193 -0
  393. package/vendor/Server/Agent/SkillSelector/types.js +0 -63
@@ -1,789 +1,789 @@
1
- #!/usr/bin/env node
2
- /**
3
- * ohpm-switch.cjs — 鸿蒙工程「ohpm 线上版本替换」标准化脚本。
4
- *
5
- * 背景:ArkTS 三方库源工程通常以 `file:` 或相对路径依赖本地原生模块
6
- * (FFmpeg / libyuv / NDK / GN / Ninja 预编译等),在预编译产物缺失或构建链
7
- * 不完整时 devecocli build 会失败(如 libyuv.a 缺失导致 ninja 报错)。
8
- * 本脚本将 entry 模块对该库的依赖从本地路径替换为 ohpm 线上发行版本,
9
- * 绕过源库原生构建问题,并对齐三个 SKILL(DemoCode / XTSCode / XTSExecution)
10
- * 的「ohpm 线上版本替换」流程。
11
- *
12
- * 流程:
13
- * 1. 扫描工程内所有 oh-package.json5,定位 file: / 相对路径依赖
14
- * 2. 匹配工程级 build-profile.json5 `modules[]` 中注册的同名本地模块
15
- * 3. 通过 `ohpm info <pkg>` 查询线上版本列表(或 --version 显式指定)
16
- * 4. 将 oh-package.json5 中该库依赖值改为线上版本号
17
- * 5. 从 build-profile.json5 的 modules[] 移除本地模块条目
18
- * 6. 输出改动清单到 <source>/ohpm-switch-report.json,供 SKILL 报告引用
19
- *
20
- * 设计原则:
21
- * - 零第三方依赖;JSON5 采用文本级正则 + 括号配对操作(与 migrate.cjs 同风格),
22
- * 保留原文件注释与尾逗号
23
- * - 不修改 @ohos/hypium 等 devDependencies;只处理 file: / 相对路径依赖
24
- * - Windows PowerShell 与跨平台兼容(ohpm 调用经 shell,支持 exe/.cmd/.bat)
25
- * - 修改保留不回滚,通过 report 文件如实记录改动,供最终报告引用
26
- *
27
- * 用法:
28
- * node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
29
- * [--dry-run] [--run-install] [--help]
30
- *
31
- * 示例:
32
- * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --dry-run
33
- * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --pkg @ohos/ijkplayer --version 2.0.9
34
- * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --run-install
35
- *
36
- * 退出码:
37
- * 0 成功(或 dry-run 定位完成)
38
- * 1 参数/环境错误、ohpm 不可用且未指定版本、写入失败
39
- * 2 未找到可替换的本地 file 依赖,或依赖无对应 ohpm 线上版本
40
- */
41
-
42
- 'use strict';
43
-
44
- const fs = require('fs');
45
- const path = require('path');
46
- const { execFileSync } = require('child_process');
47
-
48
- // ── 日志(统一 [ohpm-switch] 前缀) ───────────────────────────
49
- const PREFIX = '[ohpm-switch]';
50
-
51
- function info(msg) {
52
- console.log(`${PREFIX} ${msg}`);
53
- }
54
-
55
- function ok(msg) {
56
- console.log(`${PREFIX} [OK] ${msg}`);
57
- }
58
-
59
- function warn(msg) {
60
- console.log(`${PREFIX} [WARN] ${msg}`);
61
- }
62
-
63
- function error(msg) {
64
- console.error(`${PREFIX} [ERROR] ${msg}`);
65
- }
66
-
67
- // ── 工具函数 ────────────────────────────────────────────────
68
-
69
- /**
70
- * 解析 --key value 形式的命令行参数;--flag 形式的布尔参数值为 true。
71
- * @returns {Record<string, string|true>}
72
- */
73
- function parseArgs() {
74
- const args = process.argv.slice(2);
75
- const parsed = {};
76
- for (let i = 0; i < args.length; i++) {
77
- if (args[i].startsWith('--')) {
78
- const key = args[i].substring(2);
79
- if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
80
- parsed[key] = args[i + 1];
81
- i++;
82
- } else {
83
- parsed[key] = true;
84
- }
85
- }
86
- }
87
- return parsed;
88
- }
89
-
90
- /**
91
- * 读取文件文本,文件不存在返回 null。
92
- * @param {string} filePath
93
- * @returns {string|null}
94
- */
95
- function readFileSafe(filePath) {
96
- try {
97
- return fs.readFileSync(filePath, 'utf-8');
98
- } catch {
99
- return null;
100
- }
101
- }
102
-
103
- /**
104
- * 转义正则中的特殊字符。
105
- * @param {string} str
106
- * @returns {string}
107
- */
108
- function escapeRegExp(str) {
109
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
110
- }
111
-
112
- /**
113
- * 从指定起始索引查找配对的闭合括号位置。
114
- * @param {string} text
115
- * @param {number} openIdx 起始括号的索引
116
- * @param {string} openChar 左括号字符
117
- * @param {string} closeChar 右括号字符
118
- * @returns {number} 配对右括号索引;未找到返回 -1
119
- */
120
- function findMatchingBracket(text, openIdx, openChar, closeChar) {
121
- let depth = 0;
122
- let inString = false;
123
- let quoteChar = '';
124
- for (let i = openIdx; i < text.length; i++) {
125
- const ch = text[i];
126
- if (inString) {
127
- if (ch === '\\') { i++; continue; }
128
- if (ch === quoteChar) inString = false;
129
- continue;
130
- }
131
- if (ch === '"' || ch === "'") { inString = true; quoteChar = ch; continue; }
132
- if (ch === openChar) { depth++; continue; }
133
- if (ch === closeChar) {
134
- depth--;
135
- if (depth === 0) return i;
136
- }
137
- }
138
- return -1;
139
- }
140
-
141
- /**
142
- * 去除 JSON5 字符串值两端的引号。
143
- * @param {string} str
144
- * @returns {string}
145
- */
146
- function unquote(str) {
147
- const s = str.trim();
148
- if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
149
- return s.slice(1, -1);
150
- }
151
- return s;
152
- }
153
-
154
- /**
155
- * 在对象文本中查找指定 key 的简单字符串/数字值。
156
- * @param {string} objText 对象片段(不含外层 {})
157
- * @param {string} key
158
- * @returns {string|null}
159
- */
160
- function getFieldValueFrom(objText, key) {
161
- const escaped = escapeRegExp(key);
162
- const re = new RegExp(
163
- `["']?${escaped}["']?\\s*:\\s*("([^"\\\\]|\\\\.)*"|'([^'\\\\]|\\\\.)*'|[0-9]+|true|false|null)`
164
- );
165
- const m = re.exec(objText);
166
- if (!m) return null;
167
- return m[1];
168
- }
169
-
170
- // ── 工程扫描 ────────────────────────────────────────────────
171
-
172
- /**
173
- * 从 build-profile.json5 文本中提取 modules[] 数组内的所有模块对象。
174
- * 支持 JSON5 中键带引号或不带引号两种写法。
175
- * @param {string} content
176
- * @returns {Array<{name: string, srcPath: string, rawStart: number, rawEnd: number}>}
177
- */
178
- function extractModules(content) {
179
- const match = content.match(/(?:^|[,{:])[ \t]*(?:"modules"|'modules'|modules)[ \t]*:[ \t]*\[/m);
180
- if (!match) return [];
181
- const arrStart = match.index + match[0].lastIndexOf('['); // 指向 [
182
- const arrEnd = findMatchingBracket(content, arrStart, '[', ']');
183
- if (arrEnd === -1) return [];
184
-
185
- const arrContent = content.slice(arrStart + 1, arrEnd);
186
- const result = [];
187
- let i = 0;
188
- while (i < arrContent.length) {
189
- if (arrContent[i] === '{') {
190
- const objEnd = findMatchingBracket(arrContent, i, '{', '}');
191
- if (objEnd === -1) break;
192
- const objStr = arrContent.slice(i + 1, objEnd);
193
- const nameRaw = getFieldValueFrom(objStr, 'name');
194
- const srcPathRaw = getFieldValueFrom(objStr, 'srcPath');
195
- result.push({
196
- name: nameRaw ? unquote(nameRaw) : '',
197
- srcPath: srcPathRaw ? unquote(srcPathRaw) : '',
198
- absStart: arrStart + 1 + i,
199
- absEnd: arrStart + 1 + objEnd,
200
- });
201
- i = objEnd + 1;
202
- } else {
203
- i++;
204
- }
205
- }
206
- return result;
207
- }
208
-
209
- /**
210
- * 提取 JSON5 对象文本中全部 key: string-value 对。
211
- * @param {string} objText 对象片段(不含外层 {})
212
- * @returns {Array<{key: string, value: string}>} 去引号后的键值
213
- */
214
- function extractKeyValuePairs(objText) {
215
- const pairs = [];
216
- const re = /("([^"\\]|\\.)*"|'([^'\\]|\\.)*')\s*:\s*("([^"\\]|\\.)*"|'([^'\\]|\\.)*')/g;
217
- let m;
218
- while ((m = re.exec(objText)) !== null) {
219
- pairs.push({ key: unquote(m[1]), value: unquote(m[4]) });
220
- }
221
- return pairs;
222
- }
223
-
224
- /**
225
- * 提取对象文本中指定顶层字段(如 dependencies)的对象内部内容。
226
- * 支持字段键带引号或不带引号写法。
227
- * @param {string} content
228
- * @param {string} field 字段名(如 dependencies / devDependencies)
229
- * @returns {string|null} 找到返回内部文本(不含外层 {}),否则 null
230
- */
231
- function getObjectBody(content, field) {
232
- const re = new RegExp(`(?:"${escapeRegExp(field)}"|'${escapeRegExp(field)}'|${escapeRegExp(field)})\\s*:\\s*\\{`);
233
- const m = re.exec(content);
234
- if (!m) return null;
235
- const bodyStart = m.index + m[0].lastIndexOf('{'); // 指向 {
236
- const bodyEnd = findMatchingBracket(content, bodyStart, '{', '}');
237
- if (bodyEnd === -1) return null;
238
- return content.slice(bodyStart + 1, bodyEnd);
239
- }
240
-
241
- /**
242
- * 递归扫描工程目录树内所有 oh-package.json5,定位 file:/相对路径依赖。
243
- * 跳过 dependencies/oh_modules/node_modules/.git/build 等目录,避免进入依赖产物目录。
244
- * @param {string} projectRoot
245
- * @returns {Array<{pkgFile: string, section: string, depKey: string, depValue: string}>}
246
- */
247
- function scanFileDeps(projectRoot) {
248
- const IGNORE_DIRS = new Set([
249
- 'oh_modules', 'node_modules', '.git', 'build',
250
- 'default', 'intermediates', '.idea', '.hvigor', '.cxx',
251
- ]);
252
- const pkgFiles = [];
253
-
254
- (function walk(dir) {
255
- let entries;
256
- try {
257
- entries = fs.readdirSync(dir, { withFileTypes: true });
258
- } catch {
259
- return;
260
- }
261
- for (const entry of entries) {
262
- const full = path.join(dir, entry.name);
263
- if (entry.isDirectory()) {
264
- if (entry.name.startsWith('.') || IGNORE_DIRS.has(entry.name)) continue;
265
- walk(full);
266
- } else if (entry.name === 'oh-package.json5') {
267
- pkgFiles.push(full);
268
- }
269
- }
270
- })(projectRoot);
271
-
272
- const results = [];
273
- for (const pkgFile of pkgFiles) {
274
- const content = readFileSafe(pkgFile);
275
- if (!content) continue;
276
-
277
- for (const section of ['dependencies', 'devDependencies']) {
278
- const body = getObjectBody(content, section);
279
- if (body === null) continue;
280
- for (const { key, value } of extractKeyValuePairs(body)) {
281
- const v = value.trim();
282
- // 排除编译产物/NAPI types 伪依赖(如 "libijkplayer_audio_napi.so": "file:./src/main/cpp/types/...")
283
- // 这类依赖是库打包后的二进制映射,无法通过 ohpm 线上安装,替换会破坏构建
284
- if (/\.(so|aar|har)$/i.test(key)) continue;
285
- // 命中 file: 或相对路径(../ ./ ..\ 等)
286
- if (/^(file:|\.\.?[\\/])/i.test(v)) {
287
- results.push({
288
- pkgFile,
289
- section,
290
- depKey: key,
291
- depValue: v,
292
- });
293
- }
294
- }
295
- }
296
- }
297
- return results;
298
- }
299
-
300
- /**
301
- * 匹配 file 依赖与 build-profile.json5 modules[] 中的本地模块。
302
- * 依赖键 @ohos/ijkplayer 与本地模块 name=ijkplayer / srcPath=./ijkplayer 匹配规则:
303
- * - 精确匹配
304
- * - 去掉 scope(@ohos/ 等)后与模块 name 匹配
305
- * - 依赖键以 "/<模块name>" 结尾
306
- * - srcPath 文件目录名与依赖键去 scope 后匹配
307
- * @param {string} depKey
308
- * @param {Array<{name: string, srcPath: string}>} modules
309
- * @returns {{name: string, srcPath: string}|null}
310
- */
311
- function matchModuleForDep(depKey, modules) {
312
- const normalized = depKey.replace(/^@[^/]+\//, '');
313
- for (const mod of modules) {
314
- const srcName = mod.srcPath ? mod.srcPath.split(/[\\/]+/).filter(Boolean).pop() : '';
315
- if (mod.name && mod.name === normalized) return mod;
316
- if (mod.name && depKey.endsWith('/' + mod.name)) return mod;
317
- if (mod.name && normalized === srcName && srcName) return mod;
318
- if (srcName && srcName === normalized) return mod;
319
- if (mod.name && normalized.includes(mod.name)) return mod;
320
- }
321
- return null;
322
- }
323
-
324
- // ── ohpm 查询 ───────────────────────────────────────────────
325
-
326
- /**
327
- * 探测 ohpm 是否可用。Windows 下经 shell 以兼容 ohpm.cmd/ohpm.bat。
328
- * @returns {string|null} ohpm 版本字符串;不可用返回 null
329
- */
330
- function checkOhpm() {
331
- try {
332
- const out = execFileSync('ohpm', ['--version'], {
333
- encoding: 'utf-8',
334
- shell: process.platform === 'win32',
335
- timeout: 30000,
336
- });
337
- const lines = out.split(/\r?\n/).filter((l) => l.trim());
338
- return lines.length > 0 ? lines[lines.length - 1].trim() : 'unknown';
339
- } catch {
340
- return null;
341
- }
342
- }
343
-
344
- /**
345
- * 执行 `ohpm info <pkg>` 查询线上包信息。
346
- * @param {string} pkg
347
- * @returns {string|null} 原始输出;失败返回 null
348
- */
349
- function queryOhpmInfo(pkg) {
350
- try {
351
- return execFileSync('ohpm', ['info', pkg], {
352
- encoding: 'utf-8',
353
- shell: process.platform === 'win32',
354
- timeout: 60000,
355
- });
356
- } catch (e) {
357
- const msg = String((e && (e.stderr || e.message)) || '').trim().slice(0, 300);
358
- warn(`ohpm info ${pkg} 失败: ${msg || '未知错误'}`);
359
- return null;
360
- }
361
- }
362
-
363
- /**
364
- * 从任意文本中宽松提取所有语义化版本号。
365
- * @param {string} text
366
- * @returns {string[]} 去重后的版本号列表
367
- */
368
- function extractVersions(text) {
369
- const set = new Set();
370
- const re = /(\d+\.\d+\.\d+)(?:[-+][0-9A-Za-z.-]+)?/g;
371
- let m;
372
- while ((m = re.exec(text)) !== null) {
373
- set.add(m[0]);
374
- }
375
- return [...set];
376
- }
377
-
378
- /**
379
- * 解析语义化版本号为结构化对象。
380
- * @param {string} version
381
- * @returns {{major: number, minor: number, patch: number, prerelease: string}}
382
- */
383
- function parseSemver(version) {
384
- const [mainPart, prePart] = version.split('-');
385
- const parts = mainPart.split('.');
386
- return {
387
- major: parseInt(parts[0] || '0', 10),
388
- minor: parseInt(parts[1] || '0', 10),
389
- patch: parseInt(parts[2] || '0', 10),
390
- prerelease: prePart || '',
391
- };
392
- }
393
-
394
- /**
395
- * 比较两个语义化版本号(含 prerelease 优先级)。
396
- * @param {string} v1
397
- * @param {string} v2
398
- * @returns {number} -1 | 0 | 1
399
- */
400
- function compareSemver(v1, v2) {
401
- const p1 = parseSemver(v1);
402
- const p2 = parseSemver(v2);
403
- if (p1.major !== p2.major) return p1.major < p2.major ? -1 : 1;
404
- if (p1.minor !== p2.minor) return p1.minor < p2.minor ? -1 : 1;
405
- if (p1.patch !== p2.patch) return p1.patch < p2.patch ? -1 : 1;
406
- if (!p1.prerelease && p2.prerelease) return 1;
407
- if (p1.prerelease && !p2.prerelease) return -1;
408
- if (!p1.prerelease && !p2.prerelease) return 0;
409
- const a = p1.prerelease.split('.');
410
- const b = p2.prerelease.split('.');
411
- const maxLen = Math.max(a.length, b.length);
412
- for (let i = 0; i < maxLen; i++) {
413
- if (!a[i]) return -1;
414
- if (!b[i]) return 1;
415
- const na = parseInt(a[i], 10);
416
- const nb = parseInt(b[i], 10);
417
- if (!isNaN(na) && !isNaN(nb)) {
418
- if (na !== nb) return na < nb ? -1 : 1;
419
- } else if (a[i] !== b[i]) {
420
- return a[i] < b[i] ? -1 : 1;
421
- }
422
- }
423
- return 0;
424
- }
425
-
426
- /**
427
- * 将版本号数组按语义化版本降序排序(最新在前)。
428
- * @param {string[]} versions
429
- * @returns {string[]}
430
- */
431
- function sortVersionsDesc(versions) {
432
- return [...versions].sort((a, b) => compareSemver(b, a));
433
- }
434
-
435
- /**
436
- * 从版本列表挑选最新版本(优先稳定版,全部为预发布时取最高预发布版)。
437
- * @param {string[]} versions
438
- * @returns {string|null}
439
- */
440
- function pickLatestVersion(versions) {
441
- if (!versions || versions.length === 0) return null;
442
- const sorted = sortVersionsDesc(versions);
443
- const stable = sorted.filter((v) => !v.includes('-'));
444
- return stable.length > 0 ? stable[0] : sorted[0];
445
- }
446
-
447
- /**
448
- * 从源工程 README 中判断是否出现 ohpm 安装提示的包名(辅助确认包存在)。
449
- * @param {string} projectRoot
450
- * @param {string} pkg
451
- * @returns {boolean}
452
- */
453
- function hasInstallHint(projectRoot, pkg) {
454
- for (const name of ['README.md', 'README_zh.md', 'README_CN.md']) {
455
- const content = readFileSafe(path.join(projectRoot, name));
456
- if (content && content.includes(`ohpm install ${pkg}`)) return true;
457
- }
458
- return false;
459
- }
460
-
461
- // ── 文件修改(文本级 JSON5 操作) ───────────────────────────
462
-
463
- /**
464
- * 将 oh-package.json5 中指定依赖键的值替换为新版本号(保留注释与尾逗号)。
465
- * @param {string} content 原始文本
466
- * @param {string} depKey 依赖键
467
- * @param {string} newValue 新依赖值(版本号)
468
- * @returns {string|null} 修改后的文本;未找到返回 null
469
- */
470
- function replaceDependencyValue(content, depKey, newValue) {
471
- const escaped = escapeRegExp(depKey);
472
- const patterns = [
473
- // "key": "value" 或 'key': "value"(值可能含转义)
474
- new RegExp(`("${escaped}"\\s*:\\s*)("[^"]*"|'[^']*')`),
475
- new RegExp(`('${escaped}'\\s*:\\s*)("[^"]*"|'[^']*')`),
476
- ];
477
- for (const p of patterns) {
478
- if (p.test(content)) {
479
- return content.replace(p, `$1"${newValue}"`);
480
- }
481
- }
482
- return null;
483
- }
484
-
485
- /**
486
- * 从 build-profile.json5 文本中删除指定本地模块对象(含前导缩进、行尾与逗号)。
487
- * @param {string} content 原始文本
488
- * @param {{absStart: number, absEnd: number}} module 模块对象位置
489
- * @returns {string|null} 修改后的文本;异常返回 null
490
- */
491
- function removeModuleFromContent(content, module) {
492
- const start = module.absStart;
493
- const end = module.absEnd; // 指向 }
494
-
495
- // 定位对象起行行首
496
- let lineStart = start;
497
- while (lineStart > 0 && content[lineStart - 1] !== '\n') lineStart--;
498
- // 定位对象结行行尾(含换行)
499
- let lineEnd = end + 1;
500
- while (lineEnd < content.length && content[lineEnd] !== '\n') lineEnd++;
501
- if (lineEnd < content.length) lineEnd++; // 含换行
502
-
503
- // 若 } 后紧跟逗号,一并删除(处理同行逗号)
504
- if (lineEnd < content.length && content[lineEnd] === ',') {
505
- lineEnd++;
506
- }
507
-
508
- const removed = content.slice(lineStart, lineEnd);
509
- if (!removed.includes('{')) return null;
510
-
511
- let result = content.slice(0, lineStart) + content.slice(lineEnd);
512
-
513
- // 若 } 后逗号换行的情况已覆盖;再清理可能遗留的重复空行
514
- result = result.replace(/\n{3,}/g, '\n\n');
515
-
516
- // 若 modules 数组因此为空([ 与 ] 之间仅剩空白),收拢为 []
517
- const emptyArrRe = /(\bmodules\s*:\s*\[)\s+(\s*\])/m;
518
- result = result.replace(emptyArrRe, '$1$2');
519
-
520
- return result;
521
- }
522
-
523
- // ── 主流程 ──────────────────────────────────────────────────
524
-
525
- /**
526
- * 打印用法。
527
- */
528
- function usage() {
529
- console.log(`
530
- 用法:
531
- node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
532
- [--dry-run] [--run-install] [--help]
533
-
534
- 选项:
535
- --source <路径> 目标鸿蒙工程根目录(必填)
536
- --pkg <包名> 指定要替换的依赖包名(如 @ohos/ijkplayer);缺省处理全部 file 依赖
537
- --version <版本号> 指定线上版本号(如 2.0.9);缺省通过 ohpm info 自动查询最新稳定版
538
- --dry-run 只定位与查询,不修改任何文件(在 stdout 打印计划改动)
539
- --run-install 修改完成后自动执行 ohpm install
540
- --help 显示本帮助
541
-
542
- 退出码: 0=成功 1=环境/参数/写入错误 2=无 file 依赖或无线上版本
543
- `);
544
- }
545
-
546
- /**
547
- * 主流程。
548
- */
549
- function main() {
550
- const args = parseArgs();
551
-
552
- if (args.help === true) {
553
- usage();
554
- process.exit(0);
555
- }
556
-
557
- if (!args.source || args.source === true) {
558
- error('缺少 --source 参数。用法: node ohpm-switch.cjs --source <工程根> [--pkg <库包名>]');
559
- usage();
560
- process.exit(1);
561
- }
562
-
563
- const projectRoot = path.resolve(String(args.source));
564
- if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
565
- error(`--source 路径不存在或非目录: ${projectRoot}`);
566
- process.exit(1);
567
- }
568
-
569
- const dryRun = args['dry-run'] === true;
570
- const runInstall = args['run-install'] === true;
571
- const pkgFilter = args.pkg ? String(args.pkg) : null;
572
- const explicitVersion = args.version ? String(args.version).trim() : null;
573
-
574
- info(`开始 ohpm 线上版本替换分析: ${projectRoot}`);
575
- info(`模式: ${dryRun ? 'DRY-RUN(不写文件)' : '直接修改'}`);
576
-
577
- // 0. ohpm 探测
578
- const ohpmAvailable = checkOhpm();
579
- if (ohpmAvailable) {
580
- ok(`ohpm 可用,版本: ${ohpmAvailable}`);
581
- } else if (explicitVersion) {
582
- warn('ohpm 不可用,但已指定 --version,跳过 ohpm info 自动查询');
583
- } else {
584
- error('ohpm 不可用(ohpm --version 执行失败),且未指定 --version,无法查询线上版本。');
585
- error('请确保 ohpm 在 PATH 中,或使用 --version 显式指定版本号。');
586
- process.exit(1);
587
- }
588
-
589
- // 1. 扫描 file 依赖
590
- const deps = scanFileDeps(projectRoot);
591
- if (deps.length === 0) {
592
- error('未在任意 oh-package.json5 中发现 file: / 相对路径依赖,无需替换。');
593
- process.exit(2);
594
- }
595
-
596
- // 2. 读取 build-profile modules
597
- const bpPath = path.join(projectRoot, 'build-profile.json5');
598
- const bpContent = readFileSafe(bpPath);
599
- const modules = bpContent ? extractModules(bpContent) : [];
600
- info(`build-profile.json5: ${bpContent ? `已读取,modules[] 含 ${modules.length} 个模块` : '不存在(跳过模块条目移除)'}`);
601
-
602
- // 3. 过滤 + 匹配 + 查版本
603
- const candidates = [];
604
- for (const dep of deps) {
605
- if (pkgFilter && dep.depKey !== pkgFilter) continue;
606
-
607
- const matchedModule = bpContent ? matchModuleForDep(dep.depKey, modules) : null;
608
-
609
- let targetVersion = explicitVersion || null;
610
- if (!targetVersion && ohpmAvailable) {
611
- const out = queryOhpmInfo(dep.depKey);
612
- if (out) {
613
- const versions = extractVersions(out);
614
- if (versions.length > 0) {
615
- targetVersion = pickLatestVersion(versions);
616
- info(` ${dep.depKey} 线上可用版本(${versions.length} 个),选取: ${targetVersion}`);
617
- } else {
618
- warn(` ohpm info ${dep.depKey} 输出中未提取到版本号`);
619
- }
620
- }
621
- }
622
-
623
- candidates.push({
624
- pkgFile: dep.pkgFile,
625
- section: dep.section,
626
- depKey: dep.depKey,
627
- depValue: dep.depValue,
628
- matchedModule,
629
- targetVersion,
630
- });
631
- }
632
-
633
- if (candidates.length === 0) {
634
- error(pkgFilter ? `未找到依赖键为 ${pkgFilter} 的 file/相对路径依赖。` : '未找到匹配的 file 依赖。');
635
- process.exit(2);
636
- }
637
-
638
- // 4. 输出定位结果
639
- info('=== 定位到本地 file 依赖 ===');
640
- for (const c of candidates) {
641
- const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
642
- info(` ${c.depKey} (${relPkg} ${c.section}) = ${c.depValue}`);
643
- if (c.matchedModule) {
644
- info(` -> 匹配本地模块: name=${c.matchedModule.name}, srcPath=${c.matchedModule.srcPath}`);
645
- } else {
646
- warn(` -> build-profile.json5 modules[] 中未注册该模块(仅替换依赖值)`);
647
- }
648
- if (c.targetVersion) {
649
- info(` -> 线上版本: ${c.targetVersion}`);
650
- } else {
651
- warn(` -> 未获取到线上版本(需 --version 指定或检查 ohpm registry)`);
652
- }
653
- }
654
-
655
- // 5. dry-run 提前结束
656
- if (dryRun) {
657
- info('=== DRY-RUN 计划改动(未写入任何文件) ===');
658
- for (const c of candidates) {
659
- if (!c.targetVersion) {
660
- warn(` [跳过] ${c.depKey}: 无目标版本`);
661
- continue;
662
- }
663
- const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
664
- info(` ${relPkg}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
665
- if (c.matchedModule) {
666
- info(` build-profile.json5: 移除 modules[] 中 ${c.matchedModule.name}`);
667
- }
668
- }
669
- process.exit(0);
670
- }
671
-
672
- // 6. 确定无版本依赖的处理
673
- const noVersion = candidates.filter((c) => !c.targetVersion);
674
- if (noVersion.length > 0) {
675
- error(`以下依赖未获取到线上版本,中止修改(避免写入不完整改动):`);
676
- for (const c of noVersion) {
677
- error(` - ${c.depKey}(原值 ${c.depValue})`);
678
- }
679
- error('请为这些包通过 --version 指定版本号,或修正 ohpm registry 后重试。');
680
- process.exit(2);
681
- }
682
-
683
- // 7. 执行修改
684
- const changes = [];
685
- let failed = false;
686
-
687
- for (const c of candidates) {
688
- // 修改 oh-package.json5
689
- const pkgContent = readFileSafe(c.pkgFile);
690
- if (pkgContent === null) {
691
- error(`读取失败: ${c.pkgFile}`);
692
- failed = true;
693
- continue;
694
- }
695
- const newContent = replaceDependencyValue(pkgContent, c.depKey, c.targetVersion);
696
- if (newContent === null || newContent === pkgContent) {
697
- error(`在 ${c.pkgFile} 中未找到依赖键 ${c.depKey},无法替换`);
698
- failed = true;
699
- continue;
700
- }
701
- fs.writeFileSync(c.pkgFile, newContent, 'utf-8');
702
- ok(`${path.relative(projectRoot, c.pkgFile)}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
703
- changes.push({
704
- file: path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile),
705
- action: 'replace-dependency',
706
- key: c.depKey,
707
- oldValue: c.depValue,
708
- newValue: c.targetVersion,
709
- });
710
-
711
- // 移除 build-profile.json5 中的本地模块条目
712
- if (c.matchedModule && bpContent) {
713
- for (const mod of modules) {
714
- if (
715
- mod.absStart === c.matchedModule.absStart &&
716
- mod.absEnd === c.matchedModule.absEnd
717
- ) {
718
- const removed = removeModuleFromContent(bpContent, mod);
719
- if (removed !== null) {
720
- fs.writeFileSync(bpPath, removed, 'utf-8');
721
- ok(`build-profile.json5: 移除本地模块 ${mod.name}(srcPath=${mod.srcPath})`);
722
- changes.push({
723
- file: 'build-profile.json5',
724
- action: 'remove-module',
725
- module: mod.name,
726
- });
727
- } else {
728
- error(`build-profile.json5 移除模块 ${mod.name} 失败(文本结构异常)`);
729
- failed = true;
730
- }
731
- break;
732
- }
733
- }
734
- }
735
- }
736
-
737
- if (failed) {
738
- error('存在修改失败项,请检查上述错误后重试。已成功的修改保留不回滚,请核对后再继续。');
739
- process.exit(1);
740
- }
741
-
742
- // 8. 可选 ohpm install
743
- let installResult = null;
744
- if (runInstall) {
745
- info('执行 ohpm install ...');
746
- try {
747
- execFileSync('ohpm', ['install'], {
748
- cwd: projectRoot,
749
- encoding: 'utf-8',
750
- shell: process.platform === 'win32',
751
- timeout: 300000,
752
- stdio: 'inherit',
753
- });
754
- installResult = { ok: true };
755
- ok('ohpm install 执行成功');
756
- } catch (e) {
757
- installResult = { ok: false, message: String(e.message || '').slice(0, 300) };
758
- error('ohpm install 执行失败,请手动在工程目录执行 ohpm install 后重新编译。');
759
- error(` ${installResult.message}`);
760
- }
761
- }
762
-
763
- // 9. 输出改动清单报告
764
- const report = {
765
- tool: 'ohpm-switch',
766
- version: '1.0.0',
767
- timestamp: new Date().toISOString(),
768
- sourcePath: projectRoot,
769
- dryRun: false,
770
- ohpmAvailable: !!ohpmAvailable,
771
- ohpmVersion: ohpmAvailable || null,
772
- changes,
773
- runInstall,
774
- installResult,
775
- };
776
- const reportPath = path.join(projectRoot, 'ohpm-switch-report.json');
777
- fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf-8');
778
- ok(`改动清单已写入: ${reportPath}`);
779
-
780
- if (runInstall && installResult && !installResult.ok) {
781
- // install 失败但文件修改已生效:返回 1 提醒用户,仍保留 report
782
- process.exit(1);
783
- }
784
-
785
- ok('ohpm 线上版本替换完成。请使用 build.ps1 重新编译验证。');
786
- process.exit(0);
787
- }
788
-
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ohpm-switch.cjs — 鸿蒙工程「ohpm 线上版本替换」标准化脚本。
4
+ *
5
+ * 背景:ArkTS 三方库源工程通常以 `file:` 或相对路径依赖本地原生模块
6
+ * (FFmpeg / libyuv / NDK / GN / Ninja 预编译等),在预编译产物缺失或构建链
7
+ * 不完整时 devecocli build 会失败(如 libyuv.a 缺失导致 ninja 报错)。
8
+ * 本脚本将 entry 模块对该库的依赖从本地路径替换为 ohpm 线上发行版本,
9
+ * 绕过源库原生构建问题,并对齐三个 SKILL(DemoCode / XTSCode / XTSExecution)
10
+ * 的「ohpm 线上版本替换」流程。
11
+ *
12
+ * 流程:
13
+ * 1. 扫描工程内所有 oh-package.json5,定位 file: / 相对路径依赖
14
+ * 2. 匹配工程级 build-profile.json5 `modules[]` 中注册的同名本地模块
15
+ * 3. 通过 `ohpm info <pkg>` 查询线上版本列表(或 --version 显式指定)
16
+ * 4. 将 oh-package.json5 中该库依赖值改为线上版本号
17
+ * 5. 从 build-profile.json5 的 modules[] 移除本地模块条目
18
+ * 6. 输出改动清单到 <source>/ohpm-switch-report.json,供 SKILL 报告引用
19
+ *
20
+ * 设计原则:
21
+ * - 零第三方依赖;JSON5 采用文本级正则 + 括号配对操作(与 migrate.cjs 同风格),
22
+ * 保留原文件注释与尾逗号
23
+ * - 不修改 @ohos/hypium 等 devDependencies;只处理 file: / 相对路径依赖
24
+ * - Windows PowerShell 与跨平台兼容(ohpm 调用经 shell,支持 exe/.cmd/.bat)
25
+ * - 修改保留不回滚,通过 report 文件如实记录改动,供最终报告引用
26
+ *
27
+ * 用法:
28
+ * node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
29
+ * [--dry-run] [--run-install] [--help]
30
+ *
31
+ * 示例:
32
+ * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --dry-run
33
+ * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --pkg @ohos/ijkplayer --version 2.0.9
34
+ * node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --run-install
35
+ *
36
+ * 退出码:
37
+ * 0 成功(或 dry-run 定位完成)
38
+ * 1 参数/环境错误、ohpm 不可用且未指定版本、写入失败
39
+ * 2 未找到可替换的本地 file 依赖,或依赖无对应 ohpm 线上版本
40
+ */
41
+
42
+ 'use strict';
43
+
44
+ const fs = require('fs');
45
+ const path = require('path');
46
+ const { execFileSync } = require('child_process');
47
+
48
+ // ── 日志(统一 [ohpm-switch] 前缀) ───────────────────────────
49
+ const PREFIX = '[ohpm-switch]';
50
+
51
+ function info(msg) {
52
+ console.log(`${PREFIX} ${msg}`);
53
+ }
54
+
55
+ function ok(msg) {
56
+ console.log(`${PREFIX} [OK] ${msg}`);
57
+ }
58
+
59
+ function warn(msg) {
60
+ console.log(`${PREFIX} [WARN] ${msg}`);
61
+ }
62
+
63
+ function error(msg) {
64
+ console.error(`${PREFIX} [ERROR] ${msg}`);
65
+ }
66
+
67
+ // ── 工具函数 ────────────────────────────────────────────────
68
+
69
+ /**
70
+ * 解析 --key value 形式的命令行参数;--flag 形式的布尔参数值为 true。
71
+ * @returns {Record<string, string|true>}
72
+ */
73
+ function parseArgs() {
74
+ const args = process.argv.slice(2);
75
+ const parsed = {};
76
+ for (let i = 0; i < args.length; i++) {
77
+ if (args[i].startsWith('--')) {
78
+ const key = args[i].substring(2);
79
+ if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
80
+ parsed[key] = args[i + 1];
81
+ i++;
82
+ } else {
83
+ parsed[key] = true;
84
+ }
85
+ }
86
+ }
87
+ return parsed;
88
+ }
89
+
90
+ /**
91
+ * 读取文件文本,文件不存在返回 null。
92
+ * @param {string} filePath
93
+ * @returns {string|null}
94
+ */
95
+ function readFileSafe(filePath) {
96
+ try {
97
+ return fs.readFileSync(filePath, 'utf-8');
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * 转义正则中的特殊字符。
105
+ * @param {string} str
106
+ * @returns {string}
107
+ */
108
+ function escapeRegExp(str) {
109
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
110
+ }
111
+
112
+ /**
113
+ * 从指定起始索引查找配对的闭合括号位置。
114
+ * @param {string} text
115
+ * @param {number} openIdx 起始括号的索引
116
+ * @param {string} openChar 左括号字符
117
+ * @param {string} closeChar 右括号字符
118
+ * @returns {number} 配对右括号索引;未找到返回 -1
119
+ */
120
+ function findMatchingBracket(text, openIdx, openChar, closeChar) {
121
+ let depth = 0;
122
+ let inString = false;
123
+ let quoteChar = '';
124
+ for (let i = openIdx; i < text.length; i++) {
125
+ const ch = text[i];
126
+ if (inString) {
127
+ if (ch === '\\') { i++; continue; }
128
+ if (ch === quoteChar) inString = false;
129
+ continue;
130
+ }
131
+ if (ch === '"' || ch === "'") { inString = true; quoteChar = ch; continue; }
132
+ if (ch === openChar) { depth++; continue; }
133
+ if (ch === closeChar) {
134
+ depth--;
135
+ if (depth === 0) return i;
136
+ }
137
+ }
138
+ return -1;
139
+ }
140
+
141
+ /**
142
+ * 去除 JSON5 字符串值两端的引号。
143
+ * @param {string} str
144
+ * @returns {string}
145
+ */
146
+ function unquote(str) {
147
+ const s = str.trim();
148
+ if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
149
+ return s.slice(1, -1);
150
+ }
151
+ return s;
152
+ }
153
+
154
+ /**
155
+ * 在对象文本中查找指定 key 的简单字符串/数字值。
156
+ * @param {string} objText 对象片段(不含外层 {})
157
+ * @param {string} key
158
+ * @returns {string|null}
159
+ */
160
+ function getFieldValueFrom(objText, key) {
161
+ const escaped = escapeRegExp(key);
162
+ const re = new RegExp(
163
+ `["']?${escaped}["']?\\s*:\\s*("([^"\\\\]|\\\\.)*"|'([^'\\\\]|\\\\.)*'|[0-9]+|true|false|null)`
164
+ );
165
+ const m = re.exec(objText);
166
+ if (!m) return null;
167
+ return m[1];
168
+ }
169
+
170
+ // ── 工程扫描 ────────────────────────────────────────────────
171
+
172
+ /**
173
+ * 从 build-profile.json5 文本中提取 modules[] 数组内的所有模块对象。
174
+ * 支持 JSON5 中键带引号或不带引号两种写法。
175
+ * @param {string} content
176
+ * @returns {Array<{name: string, srcPath: string, rawStart: number, rawEnd: number}>}
177
+ */
178
+ function extractModules(content) {
179
+ const match = content.match(/(?:^|[,{:])[ \t]*(?:"modules"|'modules'|modules)[ \t]*:[ \t]*\[/m);
180
+ if (!match) return [];
181
+ const arrStart = match.index + match[0].lastIndexOf('['); // 指向 [
182
+ const arrEnd = findMatchingBracket(content, arrStart, '[', ']');
183
+ if (arrEnd === -1) return [];
184
+
185
+ const arrContent = content.slice(arrStart + 1, arrEnd);
186
+ const result = [];
187
+ let i = 0;
188
+ while (i < arrContent.length) {
189
+ if (arrContent[i] === '{') {
190
+ const objEnd = findMatchingBracket(arrContent, i, '{', '}');
191
+ if (objEnd === -1) break;
192
+ const objStr = arrContent.slice(i + 1, objEnd);
193
+ const nameRaw = getFieldValueFrom(objStr, 'name');
194
+ const srcPathRaw = getFieldValueFrom(objStr, 'srcPath');
195
+ result.push({
196
+ name: nameRaw ? unquote(nameRaw) : '',
197
+ srcPath: srcPathRaw ? unquote(srcPathRaw) : '',
198
+ absStart: arrStart + 1 + i,
199
+ absEnd: arrStart + 1 + objEnd,
200
+ });
201
+ i = objEnd + 1;
202
+ } else {
203
+ i++;
204
+ }
205
+ }
206
+ return result;
207
+ }
208
+
209
+ /**
210
+ * 提取 JSON5 对象文本中全部 key: string-value 对。
211
+ * @param {string} objText 对象片段(不含外层 {})
212
+ * @returns {Array<{key: string, value: string}>} 去引号后的键值
213
+ */
214
+ function extractKeyValuePairs(objText) {
215
+ const pairs = [];
216
+ const re = /("([^"\\]|\\.)*"|'([^'\\]|\\.)*')\s*:\s*("([^"\\]|\\.)*"|'([^'\\]|\\.)*')/g;
217
+ let m;
218
+ while ((m = re.exec(objText)) !== null) {
219
+ pairs.push({ key: unquote(m[1]), value: unquote(m[4]) });
220
+ }
221
+ return pairs;
222
+ }
223
+
224
+ /**
225
+ * 提取对象文本中指定顶层字段(如 dependencies)的对象内部内容。
226
+ * 支持字段键带引号或不带引号写法。
227
+ * @param {string} content
228
+ * @param {string} field 字段名(如 dependencies / devDependencies)
229
+ * @returns {string|null} 找到返回内部文本(不含外层 {}),否则 null
230
+ */
231
+ function getObjectBody(content, field) {
232
+ const re = new RegExp(`(?:"${escapeRegExp(field)}"|'${escapeRegExp(field)}'|${escapeRegExp(field)})\\s*:\\s*\\{`);
233
+ const m = re.exec(content);
234
+ if (!m) return null;
235
+ const bodyStart = m.index + m[0].lastIndexOf('{'); // 指向 {
236
+ const bodyEnd = findMatchingBracket(content, bodyStart, '{', '}');
237
+ if (bodyEnd === -1) return null;
238
+ return content.slice(bodyStart + 1, bodyEnd);
239
+ }
240
+
241
+ /**
242
+ * 递归扫描工程目录树内所有 oh-package.json5,定位 file:/相对路径依赖。
243
+ * 跳过 dependencies/oh_modules/node_modules/.git/build 等目录,避免进入依赖产物目录。
244
+ * @param {string} projectRoot
245
+ * @returns {Array<{pkgFile: string, section: string, depKey: string, depValue: string}>}
246
+ */
247
+ function scanFileDeps(projectRoot) {
248
+ const IGNORE_DIRS = new Set([
249
+ 'oh_modules', 'node_modules', '.git', 'build',
250
+ 'default', 'intermediates', '.idea', '.hvigor', '.cxx',
251
+ ]);
252
+ const pkgFiles = [];
253
+
254
+ (function walk(dir) {
255
+ let entries;
256
+ try {
257
+ entries = fs.readdirSync(dir, { withFileTypes: true });
258
+ } catch {
259
+ return;
260
+ }
261
+ for (const entry of entries) {
262
+ const full = path.join(dir, entry.name);
263
+ if (entry.isDirectory()) {
264
+ if (entry.name.startsWith('.') || IGNORE_DIRS.has(entry.name)) continue;
265
+ walk(full);
266
+ } else if (entry.name === 'oh-package.json5') {
267
+ pkgFiles.push(full);
268
+ }
269
+ }
270
+ })(projectRoot);
271
+
272
+ const results = [];
273
+ for (const pkgFile of pkgFiles) {
274
+ const content = readFileSafe(pkgFile);
275
+ if (!content) continue;
276
+
277
+ for (const section of ['dependencies', 'devDependencies']) {
278
+ const body = getObjectBody(content, section);
279
+ if (body === null) continue;
280
+ for (const { key, value } of extractKeyValuePairs(body)) {
281
+ const v = value.trim();
282
+ // 排除编译产物/NAPI types 伪依赖(如 "libijkplayer_audio_napi.so": "file:./src/main/cpp/types/...")
283
+ // 这类依赖是库打包后的二进制映射,无法通过 ohpm 线上安装,替换会破坏构建
284
+ if (/\.(so|aar|har)$/i.test(key)) continue;
285
+ // 命中 file: 或相对路径(../ ./ ..\ 等)
286
+ if (/^(file:|\.\.?[\\/])/i.test(v)) {
287
+ results.push({
288
+ pkgFile,
289
+ section,
290
+ depKey: key,
291
+ depValue: v,
292
+ });
293
+ }
294
+ }
295
+ }
296
+ }
297
+ return results;
298
+ }
299
+
300
+ /**
301
+ * 匹配 file 依赖与 build-profile.json5 modules[] 中的本地模块。
302
+ * 依赖键 @ohos/ijkplayer 与本地模块 name=ijkplayer / srcPath=./ijkplayer 匹配规则:
303
+ * - 精确匹配
304
+ * - 去掉 scope(@ohos/ 等)后与模块 name 匹配
305
+ * - 依赖键以 "/<模块name>" 结尾
306
+ * - srcPath 文件目录名与依赖键去 scope 后匹配
307
+ * @param {string} depKey
308
+ * @param {Array<{name: string, srcPath: string}>} modules
309
+ * @returns {{name: string, srcPath: string}|null}
310
+ */
311
+ function matchModuleForDep(depKey, modules) {
312
+ const normalized = depKey.replace(/^@[^/]+\//, '');
313
+ for (const mod of modules) {
314
+ const srcName = mod.srcPath ? mod.srcPath.split(/[\\/]+/).filter(Boolean).pop() : '';
315
+ if (mod.name && mod.name === normalized) return mod;
316
+ if (mod.name && depKey.endsWith('/' + mod.name)) return mod;
317
+ if (mod.name && normalized === srcName && srcName) return mod;
318
+ if (srcName && srcName === normalized) return mod;
319
+ if (mod.name && normalized.includes(mod.name)) return mod;
320
+ }
321
+ return null;
322
+ }
323
+
324
+ // ── ohpm 查询 ───────────────────────────────────────────────
325
+
326
+ /**
327
+ * 探测 ohpm 是否可用。Windows 下经 shell 以兼容 ohpm.cmd/ohpm.bat。
328
+ * @returns {string|null} ohpm 版本字符串;不可用返回 null
329
+ */
330
+ function checkOhpm() {
331
+ try {
332
+ const out = execFileSync('ohpm', ['--version'], {
333
+ encoding: 'utf-8',
334
+ shell: process.platform === 'win32',
335
+ timeout: 30000,
336
+ });
337
+ const lines = out.split(/\r?\n/).filter((l) => l.trim());
338
+ return lines.length > 0 ? lines[lines.length - 1].trim() : 'unknown';
339
+ } catch {
340
+ return null;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * 执行 `ohpm info <pkg>` 查询线上包信息。
346
+ * @param {string} pkg
347
+ * @returns {string|null} 原始输出;失败返回 null
348
+ */
349
+ function queryOhpmInfo(pkg) {
350
+ try {
351
+ return execFileSync('ohpm', ['info', pkg], {
352
+ encoding: 'utf-8',
353
+ shell: process.platform === 'win32',
354
+ timeout: 60000,
355
+ });
356
+ } catch (e) {
357
+ const msg = String((e && (e.stderr || e.message)) || '').trim().slice(0, 300);
358
+ warn(`ohpm info ${pkg} 失败: ${msg || '未知错误'}`);
359
+ return null;
360
+ }
361
+ }
362
+
363
+ /**
364
+ * 从任意文本中宽松提取所有语义化版本号。
365
+ * @param {string} text
366
+ * @returns {string[]} 去重后的版本号列表
367
+ */
368
+ function extractVersions(text) {
369
+ const set = new Set();
370
+ const re = /(\d+\.\d+\.\d+)(?:[-+][0-9A-Za-z.-]+)?/g;
371
+ let m;
372
+ while ((m = re.exec(text)) !== null) {
373
+ set.add(m[0]);
374
+ }
375
+ return [...set];
376
+ }
377
+
378
+ /**
379
+ * 解析语义化版本号为结构化对象。
380
+ * @param {string} version
381
+ * @returns {{major: number, minor: number, patch: number, prerelease: string}}
382
+ */
383
+ function parseSemver(version) {
384
+ const [mainPart, prePart] = version.split('-');
385
+ const parts = mainPart.split('.');
386
+ return {
387
+ major: parseInt(parts[0] || '0', 10),
388
+ minor: parseInt(parts[1] || '0', 10),
389
+ patch: parseInt(parts[2] || '0', 10),
390
+ prerelease: prePart || '',
391
+ };
392
+ }
393
+
394
+ /**
395
+ * 比较两个语义化版本号(含 prerelease 优先级)。
396
+ * @param {string} v1
397
+ * @param {string} v2
398
+ * @returns {number} -1 | 0 | 1
399
+ */
400
+ function compareSemver(v1, v2) {
401
+ const p1 = parseSemver(v1);
402
+ const p2 = parseSemver(v2);
403
+ if (p1.major !== p2.major) return p1.major < p2.major ? -1 : 1;
404
+ if (p1.minor !== p2.minor) return p1.minor < p2.minor ? -1 : 1;
405
+ if (p1.patch !== p2.patch) return p1.patch < p2.patch ? -1 : 1;
406
+ if (!p1.prerelease && p2.prerelease) return 1;
407
+ if (p1.prerelease && !p2.prerelease) return -1;
408
+ if (!p1.prerelease && !p2.prerelease) return 0;
409
+ const a = p1.prerelease.split('.');
410
+ const b = p2.prerelease.split('.');
411
+ const maxLen = Math.max(a.length, b.length);
412
+ for (let i = 0; i < maxLen; i++) {
413
+ if (!a[i]) return -1;
414
+ if (!b[i]) return 1;
415
+ const na = parseInt(a[i], 10);
416
+ const nb = parseInt(b[i], 10);
417
+ if (!isNaN(na) && !isNaN(nb)) {
418
+ if (na !== nb) return na < nb ? -1 : 1;
419
+ } else if (a[i] !== b[i]) {
420
+ return a[i] < b[i] ? -1 : 1;
421
+ }
422
+ }
423
+ return 0;
424
+ }
425
+
426
+ /**
427
+ * 将版本号数组按语义化版本降序排序(最新在前)。
428
+ * @param {string[]} versions
429
+ * @returns {string[]}
430
+ */
431
+ function sortVersionsDesc(versions) {
432
+ return [...versions].sort((a, b) => compareSemver(b, a));
433
+ }
434
+
435
+ /**
436
+ * 从版本列表挑选最新版本(优先稳定版,全部为预发布时取最高预发布版)。
437
+ * @param {string[]} versions
438
+ * @returns {string|null}
439
+ */
440
+ function pickLatestVersion(versions) {
441
+ if (!versions || versions.length === 0) return null;
442
+ const sorted = sortVersionsDesc(versions);
443
+ const stable = sorted.filter((v) => !v.includes('-'));
444
+ return stable.length > 0 ? stable[0] : sorted[0];
445
+ }
446
+
447
+ /**
448
+ * 从源工程 README 中判断是否出现 ohpm 安装提示的包名(辅助确认包存在)。
449
+ * @param {string} projectRoot
450
+ * @param {string} pkg
451
+ * @returns {boolean}
452
+ */
453
+ function hasInstallHint(projectRoot, pkg) {
454
+ for (const name of ['README.md', 'README_zh.md', 'README_CN.md']) {
455
+ const content = readFileSafe(path.join(projectRoot, name));
456
+ if (content && content.includes(`ohpm install ${pkg}`)) return true;
457
+ }
458
+ return false;
459
+ }
460
+
461
+ // ── 文件修改(文本级 JSON5 操作) ───────────────────────────
462
+
463
+ /**
464
+ * 将 oh-package.json5 中指定依赖键的值替换为新版本号(保留注释与尾逗号)。
465
+ * @param {string} content 原始文本
466
+ * @param {string} depKey 依赖键
467
+ * @param {string} newValue 新依赖值(版本号)
468
+ * @returns {string|null} 修改后的文本;未找到返回 null
469
+ */
470
+ function replaceDependencyValue(content, depKey, newValue) {
471
+ const escaped = escapeRegExp(depKey);
472
+ const patterns = [
473
+ // "key": "value" 或 'key': "value"(值可能含转义)
474
+ new RegExp(`("${escaped}"\\s*:\\s*)("[^"]*"|'[^']*')`),
475
+ new RegExp(`('${escaped}'\\s*:\\s*)("[^"]*"|'[^']*')`),
476
+ ];
477
+ for (const p of patterns) {
478
+ if (p.test(content)) {
479
+ return content.replace(p, `$1"${newValue}"`);
480
+ }
481
+ }
482
+ return null;
483
+ }
484
+
485
+ /**
486
+ * 从 build-profile.json5 文本中删除指定本地模块对象(含前导缩进、行尾与逗号)。
487
+ * @param {string} content 原始文本
488
+ * @param {{absStart: number, absEnd: number}} module 模块对象位置
489
+ * @returns {string|null} 修改后的文本;异常返回 null
490
+ */
491
+ function removeModuleFromContent(content, module) {
492
+ const start = module.absStart;
493
+ const end = module.absEnd; // 指向 }
494
+
495
+ // 定位对象起行行首
496
+ let lineStart = start;
497
+ while (lineStart > 0 && content[lineStart - 1] !== '\n') lineStart--;
498
+ // 定位对象结行行尾(含换行)
499
+ let lineEnd = end + 1;
500
+ while (lineEnd < content.length && content[lineEnd] !== '\n') lineEnd++;
501
+ if (lineEnd < content.length) lineEnd++; // 含换行
502
+
503
+ // 若 } 后紧跟逗号,一并删除(处理同行逗号)
504
+ if (lineEnd < content.length && content[lineEnd] === ',') {
505
+ lineEnd++;
506
+ }
507
+
508
+ const removed = content.slice(lineStart, lineEnd);
509
+ if (!removed.includes('{')) return null;
510
+
511
+ let result = content.slice(0, lineStart) + content.slice(lineEnd);
512
+
513
+ // 若 } 后逗号换行的情况已覆盖;再清理可能遗留的重复空行
514
+ result = result.replace(/\n{3,}/g, '\n\n');
515
+
516
+ // 若 modules 数组因此为空([ 与 ] 之间仅剩空白),收拢为 []
517
+ const emptyArrRe = /(\bmodules\s*:\s*\[)\s+(\s*\])/m;
518
+ result = result.replace(emptyArrRe, '$1$2');
519
+
520
+ return result;
521
+ }
522
+
523
+ // ── 主流程 ──────────────────────────────────────────────────
524
+
525
+ /**
526
+ * 打印用法。
527
+ */
528
+ function usage() {
529
+ console.log(`
530
+ 用法:
531
+ node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
532
+ [--dry-run] [--run-install] [--help]
533
+
534
+ 选项:
535
+ --source <路径> 目标鸿蒙工程根目录(必填)
536
+ --pkg <包名> 指定要替换的依赖包名(如 @ohos/ijkplayer);缺省处理全部 file 依赖
537
+ --version <版本号> 指定线上版本号(如 2.0.9);缺省通过 ohpm info 自动查询最新稳定版
538
+ --dry-run 只定位与查询,不修改任何文件(在 stdout 打印计划改动)
539
+ --run-install 修改完成后自动执行 ohpm install
540
+ --help 显示本帮助
541
+
542
+ 退出码: 0=成功 1=环境/参数/写入错误 2=无 file 依赖或无线上版本
543
+ `);
544
+ }
545
+
546
+ /**
547
+ * 主流程。
548
+ */
549
+ function main() {
550
+ const args = parseArgs();
551
+
552
+ if (args.help === true) {
553
+ usage();
554
+ process.exit(0);
555
+ }
556
+
557
+ if (!args.source || args.source === true) {
558
+ error('缺少 --source 参数。用法: node ohpm-switch.cjs --source <工程根> [--pkg <库包名>]');
559
+ usage();
560
+ process.exit(1);
561
+ }
562
+
563
+ const projectRoot = path.resolve(String(args.source));
564
+ if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
565
+ error(`--source 路径不存在或非目录: ${projectRoot}`);
566
+ process.exit(1);
567
+ }
568
+
569
+ const dryRun = args['dry-run'] === true;
570
+ const runInstall = args['run-install'] === true;
571
+ const pkgFilter = args.pkg ? String(args.pkg) : null;
572
+ const explicitVersion = args.version ? String(args.version).trim() : null;
573
+
574
+ info(`开始 ohpm 线上版本替换分析: ${projectRoot}`);
575
+ info(`模式: ${dryRun ? 'DRY-RUN(不写文件)' : '直接修改'}`);
576
+
577
+ // 0. ohpm 探测
578
+ const ohpmAvailable = checkOhpm();
579
+ if (ohpmAvailable) {
580
+ ok(`ohpm 可用,版本: ${ohpmAvailable}`);
581
+ } else if (explicitVersion) {
582
+ warn('ohpm 不可用,但已指定 --version,跳过 ohpm info 自动查询');
583
+ } else {
584
+ error('ohpm 不可用(ohpm --version 执行失败),且未指定 --version,无法查询线上版本。');
585
+ error('请确保 ohpm 在 PATH 中,或使用 --version 显式指定版本号。');
586
+ process.exit(1);
587
+ }
588
+
589
+ // 1. 扫描 file 依赖
590
+ const deps = scanFileDeps(projectRoot);
591
+ if (deps.length === 0) {
592
+ error('未在任意 oh-package.json5 中发现 file: / 相对路径依赖,无需替换。');
593
+ process.exit(2);
594
+ }
595
+
596
+ // 2. 读取 build-profile modules
597
+ const bpPath = path.join(projectRoot, 'build-profile.json5');
598
+ const bpContent = readFileSafe(bpPath);
599
+ const modules = bpContent ? extractModules(bpContent) : [];
600
+ info(`build-profile.json5: ${bpContent ? `已读取,modules[] 含 ${modules.length} 个模块` : '不存在(跳过模块条目移除)'}`);
601
+
602
+ // 3. 过滤 + 匹配 + 查版本
603
+ const candidates = [];
604
+ for (const dep of deps) {
605
+ if (pkgFilter && dep.depKey !== pkgFilter) continue;
606
+
607
+ const matchedModule = bpContent ? matchModuleForDep(dep.depKey, modules) : null;
608
+
609
+ let targetVersion = explicitVersion || null;
610
+ if (!targetVersion && ohpmAvailable) {
611
+ const out = queryOhpmInfo(dep.depKey);
612
+ if (out) {
613
+ const versions = extractVersions(out);
614
+ if (versions.length > 0) {
615
+ targetVersion = pickLatestVersion(versions);
616
+ info(` ${dep.depKey} 线上可用版本(${versions.length} 个),选取: ${targetVersion}`);
617
+ } else {
618
+ warn(` ohpm info ${dep.depKey} 输出中未提取到版本号`);
619
+ }
620
+ }
621
+ }
622
+
623
+ candidates.push({
624
+ pkgFile: dep.pkgFile,
625
+ section: dep.section,
626
+ depKey: dep.depKey,
627
+ depValue: dep.depValue,
628
+ matchedModule,
629
+ targetVersion,
630
+ });
631
+ }
632
+
633
+ if (candidates.length === 0) {
634
+ error(pkgFilter ? `未找到依赖键为 ${pkgFilter} 的 file/相对路径依赖。` : '未找到匹配的 file 依赖。');
635
+ process.exit(2);
636
+ }
637
+
638
+ // 4. 输出定位结果
639
+ info('=== 定位到本地 file 依赖 ===');
640
+ for (const c of candidates) {
641
+ const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
642
+ info(` ${c.depKey} (${relPkg} ${c.section}) = ${c.depValue}`);
643
+ if (c.matchedModule) {
644
+ info(` -> 匹配本地模块: name=${c.matchedModule.name}, srcPath=${c.matchedModule.srcPath}`);
645
+ } else {
646
+ warn(` -> build-profile.json5 modules[] 中未注册该模块(仅替换依赖值)`);
647
+ }
648
+ if (c.targetVersion) {
649
+ info(` -> 线上版本: ${c.targetVersion}`);
650
+ } else {
651
+ warn(` -> 未获取到线上版本(需 --version 指定或检查 ohpm registry)`);
652
+ }
653
+ }
654
+
655
+ // 5. dry-run 提前结束
656
+ if (dryRun) {
657
+ info('=== DRY-RUN 计划改动(未写入任何文件) ===');
658
+ for (const c of candidates) {
659
+ if (!c.targetVersion) {
660
+ warn(` [跳过] ${c.depKey}: 无目标版本`);
661
+ continue;
662
+ }
663
+ const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
664
+ info(` ${relPkg}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
665
+ if (c.matchedModule) {
666
+ info(` build-profile.json5: 移除 modules[] 中 ${c.matchedModule.name}`);
667
+ }
668
+ }
669
+ process.exit(0);
670
+ }
671
+
672
+ // 6. 确定无版本依赖的处理
673
+ const noVersion = candidates.filter((c) => !c.targetVersion);
674
+ if (noVersion.length > 0) {
675
+ error(`以下依赖未获取到线上版本,中止修改(避免写入不完整改动):`);
676
+ for (const c of noVersion) {
677
+ error(` - ${c.depKey}(原值 ${c.depValue})`);
678
+ }
679
+ error('请为这些包通过 --version 指定版本号,或修正 ohpm registry 后重试。');
680
+ process.exit(2);
681
+ }
682
+
683
+ // 7. 执行修改
684
+ const changes = [];
685
+ let failed = false;
686
+
687
+ for (const c of candidates) {
688
+ // 修改 oh-package.json5
689
+ const pkgContent = readFileSafe(c.pkgFile);
690
+ if (pkgContent === null) {
691
+ error(`读取失败: ${c.pkgFile}`);
692
+ failed = true;
693
+ continue;
694
+ }
695
+ const newContent = replaceDependencyValue(pkgContent, c.depKey, c.targetVersion);
696
+ if (newContent === null || newContent === pkgContent) {
697
+ error(`在 ${c.pkgFile} 中未找到依赖键 ${c.depKey},无法替换`);
698
+ failed = true;
699
+ continue;
700
+ }
701
+ fs.writeFileSync(c.pkgFile, newContent, 'utf-8');
702
+ ok(`${path.relative(projectRoot, c.pkgFile)}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
703
+ changes.push({
704
+ file: path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile),
705
+ action: 'replace-dependency',
706
+ key: c.depKey,
707
+ oldValue: c.depValue,
708
+ newValue: c.targetVersion,
709
+ });
710
+
711
+ // 移除 build-profile.json5 中的本地模块条目
712
+ if (c.matchedModule && bpContent) {
713
+ for (const mod of modules) {
714
+ if (
715
+ mod.absStart === c.matchedModule.absStart &&
716
+ mod.absEnd === c.matchedModule.absEnd
717
+ ) {
718
+ const removed = removeModuleFromContent(bpContent, mod);
719
+ if (removed !== null) {
720
+ fs.writeFileSync(bpPath, removed, 'utf-8');
721
+ ok(`build-profile.json5: 移除本地模块 ${mod.name}(srcPath=${mod.srcPath})`);
722
+ changes.push({
723
+ file: 'build-profile.json5',
724
+ action: 'remove-module',
725
+ module: mod.name,
726
+ });
727
+ } else {
728
+ error(`build-profile.json5 移除模块 ${mod.name} 失败(文本结构异常)`);
729
+ failed = true;
730
+ }
731
+ break;
732
+ }
733
+ }
734
+ }
735
+ }
736
+
737
+ if (failed) {
738
+ error('存在修改失败项,请检查上述错误后重试。已成功的修改保留不回滚,请核对后再继续。');
739
+ process.exit(1);
740
+ }
741
+
742
+ // 8. 可选 ohpm install
743
+ let installResult = null;
744
+ if (runInstall) {
745
+ info('执行 ohpm install ...');
746
+ try {
747
+ execFileSync('ohpm', ['install'], {
748
+ cwd: projectRoot,
749
+ encoding: 'utf-8',
750
+ shell: process.platform === 'win32',
751
+ timeout: 300000,
752
+ stdio: 'inherit',
753
+ });
754
+ installResult = { ok: true };
755
+ ok('ohpm install 执行成功');
756
+ } catch (e) {
757
+ installResult = { ok: false, message: String(e.message || '').slice(0, 300) };
758
+ error('ohpm install 执行失败,请手动在工程目录执行 ohpm install 后重新编译。');
759
+ error(` ${installResult.message}`);
760
+ }
761
+ }
762
+
763
+ // 9. 输出改动清单报告
764
+ const report = {
765
+ tool: 'ohpm-switch',
766
+ version: '1.0.0',
767
+ timestamp: new Date().toISOString(),
768
+ sourcePath: projectRoot,
769
+ dryRun: false,
770
+ ohpmAvailable: !!ohpmAvailable,
771
+ ohpmVersion: ohpmAvailable || null,
772
+ changes,
773
+ runInstall,
774
+ installResult,
775
+ };
776
+ const reportPath = path.join(projectRoot, 'ohpm-switch-report.json');
777
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf-8');
778
+ ok(`改动清单已写入: ${reportPath}`);
779
+
780
+ if (runInstall && installResult && !installResult.ok) {
781
+ // install 失败但文件修改已生效:返回 1 提醒用户,仍保留 report
782
+ process.exit(1);
783
+ }
784
+
785
+ ok('ohpm 线上版本替换完成。请使用 build.ps1 重新编译验证。');
786
+ process.exit(0);
787
+ }
788
+
789
789
  main();