@ohos-cpf/3rdloop 0.0.7 → 0.0.9

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 (230) hide show
  1. package/README.md +44 -25
  2. package/lib/cli.js +54 -29
  3. package/lib/config-cmd.js +1 -1
  4. package/lib/config.js +32 -0
  5. package/lib/doctor.js +266 -48
  6. package/lib/mcp.js +498 -0
  7. package/lib/opencode.js +75 -29
  8. package/lib/orch.js +32 -10
  9. package/lib/runner.js +8 -5
  10. package/lib/serve.js +162 -149
  11. package/lib/step.js +15 -4
  12. package/lib/web.js +107 -265
  13. package/lib/workflow.js +18 -8
  14. package/package.json +6 -2
  15. package/vendor/Archive/data/.index/meta.json +7 -0
  16. package/vendor/Archive/data/.index/search.db +0 -0
  17. package/vendor/Archive/data/docs/experiences/arkts-check-tool-pitfalls-baseline-comparison.md +13 -0
  18. package/vendor/Archive/data/docs/experiences/arkts-deep-path-import-recognition.md +31 -0
  19. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-entry-file-case-mismatch.md +25 -0
  20. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-import-trailing-slash.md +20 -0
  21. package/vendor/Archive/data/docs/experiences/arkts-library-source-files-may-be-ts.md +18 -0
  22. package/vendor/Archive/data/docs/experiences/arkts-typedarray-buffer-interface-cast-compile-fixes.md +36 -0
  23. package/vendor/Archive/data/docs/experiences/arkts-xts-compile-error-clustering-fix.md +24 -0
  24. package/vendor/Archive/data/docs/experiences/arkts-xts-doc-expected-value-runtime-probe.md +35 -0
  25. package/vendor/Archive/data/docs/experiences/arkts-xts-fixture-rawfile-testability-injection.md +33 -0
  26. package/vendor/Archive/data/docs/experiences/code-linter-chinese-comment-false-positive.md +16 -0
  27. package/vendor/Archive/data/docs/experiences/esobject-new-expression-variable-tracking.md +33 -0
  28. package/vendor/Archive/data/docs/experiences/filesystem-case-sensitivity-module-resolution.md +31 -0
  29. package/vendor/Archive/data/docs/experiences/gitcode-clone-redirect-warning-normal.md +13 -0
  30. package/vendor/Archive/data/docs/experiences/kn_msgvq1w585ec8319.md +77 -0
  31. package/vendor/Archive/data/docs/experiences/kn_msovnm6rd46de72c.md +98 -0
  32. package/vendor/Archive/data/docs/experiences/kn_mspt8vntfe708576.md +87 -0
  33. package/vendor/Archive/data/docs/experiences/kn_mspu1ei77adbbb48.md +133 -0
  34. package/vendor/Archive/data/docs/experiences/kn_mspubgak8d30bfce.md +128 -0
  35. package/vendor/Archive/data/docs/experiences/kn_mspug2de3cb1f7c3.md +115 -0
  36. package/vendor/Archive/data/docs/experiences/kn_skill_opt_001.md +38 -0
  37. package/vendor/Archive/data/docs/experiences/loop-artifacts-verification-evidence-triangle.md +19 -0
  38. package/vendor/Archive/data/docs/experiences/macos-bash-replace-build-ps1.md +19 -0
  39. package/vendor/Archive/data/docs/experiences/macos-bsd-diff-q-illegal-option.md +15 -0
  40. package/vendor/Archive/data/docs/experiences/migrate-cjs-compatible-api-version-map-limit.md +17 -0
  41. package/vendor/Archive/data/docs/experiences/mp3agic-arkts-library-behavior-quirks.md +21 -0
  42. package/vendor/Archive/data/docs/experiences/ohos-face-detection-scan-api-mapping.md +15 -0
  43. package/vendor/Archive/data/docs/experiences/pr-metadata-and-diff-integrity-verification.md +12 -0
  44. package/vendor/Archive/data/docs/experiences/rn-coverage-index-signature-pseudo-member-false-negative.md +32 -0
  45. package/vendor/Archive/data/docs/experiences/rn-memory-check-severity-calibration.md +21 -0
  46. package/vendor/Archive/data/docs/experiences/rn-platform-specific-api-exclusion-evidence.md +24 -0
  47. package/vendor/Archive/data/docs/experiences/rn-upstream-version-diff-pitfalls.md +17 -0
  48. package/vendor/Archive/data/docs/experiences/rn-version-gap-risk-rating-rules.md +13 -0
  49. package/vendor/Archive/data/docs/experiences/rn-view-lib-native-contract-diff-fabric-spec.md +16 -0
  50. package/vendor/Archive/data/docs/experiences/rnoh-adapter-package-version-field-semantics.md +18 -0
  51. package/vendor/Archive/data/docs/experiences/rnoh-arkts-sqlite-rows-array-format.md +18 -0
  52. package/vendor/Archive/data/docs/experiences/rnoh-dependency-residue-scan-strategy.md +18 -0
  53. package/vendor/Archive/data/docs/experiences/rnoh-example-local-lib-dep-use-tgz.md +53 -0
  54. package/vendor/Archive/data/docs/experiences/rnoh-example-prereserve-rntesterlist.md +17 -0
  55. package/vendor/Archive/data/docs/experiences/rnoh-example-template-compatiblesdkversion-upgrade.md +29 -0
  56. package/vendor/Archive/data/docs/experiences/rnoh-example-turbomodule-shell-adaptation-checklist.md +41 -0
  57. package/vendor/Archive/data/docs/experiences/rnoh-mcp-build-project-tool-risk.md +18 -0
  58. package/vendor/Archive/data/docs/experiences/rnoh-npm-devdependencies-version-precheck.md +29 -0
  59. package/vendor/Archive/data/docs/experiences/rnoh-repo-baseline-version-verification.md +13 -0
  60. package/vendor/Archive/data/docs/experiences/rnoh-repo-upstream-directory-mapping.md +18 -0
  61. package/vendor/Archive/data/docs/experiences/rnoh-stateless-library-zero-leak-verification.md +31 -0
  62. package/vendor/Archive/data/docs/experiences/rnoh-template-npmrc-credential-removal.md +15 -0
  63. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-codegen-wiring-verification.md +19 -0
  64. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-cpp-methodmap-compatibility-check.md +20 -0
  65. package/vendor/Archive/data/docs/experiences/scenario-doc-vs-source-signature-diff-four-patterns.md +24 -0
  66. package/vendor/Archive/data/docs/experiences/static-analysis-grep-cross-validation.md +29 -0
  67. package/vendor/Archive/demo.js +489 -0
  68. package/vendor/Archive/index.js +43 -0
  69. package/vendor/Archive/package.json +16 -0
  70. package/vendor/Archive/src/constants.js +83 -0
  71. package/vendor/Archive/src/data/stopwords.txt +110 -0
  72. package/vendor/Archive/src/data/synonyms.json +25 -0
  73. package/vendor/Archive/src/data/terms.txt +59 -0
  74. package/vendor/Archive/src/db/sqlite-backend.js +312 -0
  75. package/vendor/Archive/src/db/sqlite-schema.js +64 -0
  76. package/vendor/Archive/src/engine.js +501 -0
  77. package/vendor/Archive/src/index/index-manager.js +203 -0
  78. package/vendor/Archive/src/index/index-state.js +94 -0
  79. package/vendor/Archive/src/index/search-text-builder.js +106 -0
  80. package/vendor/Archive/src/index.js +22 -0
  81. package/vendor/Archive/src/parser/api-identifiers.js +121 -0
  82. package/vendor/Archive/src/parser/api-suffixes.js +48 -0
  83. package/vendor/Archive/src/parser/heading-normalize.js +40 -0
  84. package/vendor/Archive/src/parser/markdown-splitter.js +215 -0
  85. package/vendor/Archive/src/search/catalog-routing.js +87 -0
  86. package/vendor/Archive/src/search/fts-search.js +289 -0
  87. package/vendor/Archive/src/search/query-normalizer.js +159 -0
  88. package/vendor/Archive/src/search/query-rewriter.js +72 -0
  89. package/vendor/Archive/src/search/search-engine.js +72 -0
  90. package/vendor/Archive/src/search/snippet.js +64 -0
  91. package/vendor/Archive/src/store/dir-store.js +103 -0
  92. package/vendor/Archive/src/store/doc-store.js +36 -0
  93. package/vendor/Archive/src/tokenizer/lexicon.js +151 -0
  94. package/vendor/Archive/src/tokenizer/tokenizer.js +167 -0
  95. package/vendor/Archive/src/types.js +86 -0
  96. package/vendor/Archive/src/utils/hash.js +16 -0
  97. package/vendor/Archive/src/utils/lock.js +73 -0
  98. package/vendor/Archive/src/utils/path-safety.js +96 -0
  99. package/vendor/Archive/src/utils/paths.js +39 -0
  100. package/vendor/BadCase/rn/INDEX.md +127 -0
  101. package/vendor/BadCase/rn/cases/KI-001-webview/350/247/206/351/242/221/346/227/240/346/263/225/346/250/252/345/261/217.md +52 -0
  102. package/vendor/BadCase/rn/cases/KI-002-fastImage/347/203/255/346/233/264/346/226/260/345/233/276/347/211/207/345/244/261/350/264/245.md +37 -0
  103. package/vendor/BadCase/rn/cases/KI-003-wechat/347/274/251/347/225/245/345/233/276/346/234/254/345/234/260/345/233/276/347/211/207.md +62 -0
  104. package/vendor/BadCase/rn/cases/KI-004-wechat/345/233/236/350/260/203/346/227/266/345/272/217.md +47 -0
  105. package/vendor/BadCase/rn/cases/KI-005-geolocation/345/257/274/345/205/245/350/267/257/345/276/204/345/220/216/347/274/200.md +31 -0
  106. package/vendor/BadCase/rn/cases/KI-006-SmartRefreshLayout/346/231/272/350/203/275/346/214/207/351/222/210/345/264/251/346/272/203.md +55 -0
  107. package/vendor/BadCase/rn/cases/KI-007-orientation-locker/344/274/240/346/204/237/345/231/250/345/210/235/345/247/213/345/214/226.md +58 -0
  108. package/vendor/BadCase/rn/cases/KI-008-orientation-locker/344/274/240/346/204/237/345/231/250/345/274/225/347/224/250/350/256/241/346/225/260.md +47 -0
  109. package/vendor/BadCase/rn/cases/KI-009-pager-view/345/265/214/345/245/227/346/273/221/345/212/250.md +38 -0
  110. package/vendor/BadCase/rn/cases/KI-010-screens/346/211/213/345/212/277/345/206/262/347/252/201.md +48 -0
  111. package/vendor/BadCase/rn/cases/KI-011-spring-scrollview/346/211/213/345/212/277/351/207/215/345/244/215/347/273/221/345/256/232.md +49 -0
  112. package/vendor/BadCase/rn/cases/KI-012-qr-camera/346/250/252/345/261/217/351/200/202/351/205/215.md +59 -0
  113. package/vendor/BadCase/rn/cases/KI-013-webview/346/267/261/350/211/262/346/250/241/345/274/217/350/203/214/346/231/257/350/211/262.md +37 -0
  114. package/vendor/BadCase/rn/cases/KI-014-amap-geolocation/345/233/236/350/260/203/347/274/272/345/244/261.md +42 -0
  115. package/vendor/BadCase/rn/cases/KI-015-reanimated/350/277/220/350/241/214/346/227/266/346/236/220/346/236/204/345/264/251/346/272/203.md +54 -0
  116. package/vendor/BadCase/rn/cases/KI-016-fs/346/226/207/344/273/266/350/257/273/345/217/226/347/274/226/347/240/201.md +58 -0
  117. package/vendor/BadCase/rn/cases/KI-017-blob-util/345/217/202/346/225/260/347/274/226/347/240/201/351/224/231/350/257/257.md +39 -0
  118. package/vendor/BadCase/rn/cases/KI-018-modal/345/274/271/347/252/227/351/227/252/347/203/201.md +53 -0
  119. package/vendor/BadCase/rn/cases/KI-019-code-push/351/207/215/345/220/257/351/200/273/350/276/221/345/244/261/346/225/210.md +79 -0
  120. package/vendor/BadCase/rn/cases/KI-020-netinfo/346/225/217/346/204/237/344/277/241/346/201/257/346/263/204/351/234/262.md +60 -0
  121. package/vendor/BadCase/rn/cases/KI-021-video/346/234/254/345/234/260/350/247/206/351/242/221/346/222/255/346/224/276/345/274/202/345/270/270.md +79 -0
  122. package/vendor/BadCase/rn/cases/KI-022-audio-recorder-player/346/236/232/344/270/276/347/274/272/345/244/261.md +38 -0
  123. package/vendor/BadCase/rn/cases/KI-023-svg/346/270/220/345/217/230/347/273/247/346/211/277/345/244/261/346/225/210.md +62 -0
  124. package/vendor/BadCase/rn/generalization-log.md +55 -0
  125. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.json +328 -0
  126. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.md +382 -0
  127. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.json +1351 -0
  128. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.md +1176 -0
  129. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.json +538 -0
  130. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.md +1488 -0
  131. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.json +211 -0
  132. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.md +35 -0
  133. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.json +2048 -0
  134. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.md +3866 -0
  135. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.json +198 -0
  136. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.md +40 -0
  137. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.json +751 -0
  138. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.md +883 -0
  139. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.json +1544 -0
  140. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.md +2598 -0
  141. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.json +246 -0
  142. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.md +554 -0
  143. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.json +211 -0
  144. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.md +54 -0
  145. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.json +1626 -0
  146. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.md +2213 -0
  147. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.json +208 -0
  148. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.md +77 -0
  149. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.json +377 -0
  150. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.md +174 -0
  151. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.json +232 -0
  152. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.md +194 -0
  153. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.json +198 -0
  154. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.md +67 -0
  155. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.json +198 -0
  156. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.md +38 -0
  157. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.json +208 -0
  158. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.md +34 -0
  159. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.json +222 -0
  160. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.md +56 -0
  161. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.json +463 -0
  162. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.md +383 -0
  163. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.json +464 -0
  164. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.md +565 -0
  165. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.json +198 -0
  166. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.md +47 -0
  167. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.json +232 -0
  168. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.md +422 -0
  169. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.json +295 -0
  170. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.md +1038 -0
  171. package/vendor/BadCase/rn/rules/R-01-/345/205/250/345/261/217/347/252/227/345/217/243/347/212/266/346/200/201/347/256/241/347/220/206/345/256/214/346/225/264/346/200/247.md +20 -0
  172. package/vendor/BadCase/rn/rules/R-02-/350/265/204/346/272/220URI/345/275/242/346/200/201/345/210/206/346/224/257/350/246/206/347/233/226.md +21 -0
  173. package/vendor/BadCase/rn/rules/R-03-/351/231/204/345/261/236/346/223/215/344/275/234/345/244/261/350/264/245/351/230/273/346/226/255/344/270/273/346/265/201/347/250/213.md +20 -0
  174. package/vendor/BadCase/rn/rules/R-04-/345/274/202/346/255/245/345/233/236/350/260/203/346/263/250/345/206/214/346/227/266/345/272/217.md +20 -0
  175. package/vendor/BadCase/rn/rules/R-05-/346/250/241/345/235/227/345/257/274/345/205/245/350/267/257/345/276/204/350/247/204/350/214/203.md +19 -0
  176. package/vendor/BadCase/rn/rules/R-06-/344/274/240/346/204/237/345/231/250/347/241/254/344/273/266/346/235/203/351/231/220/346/214/211/351/234/200/350/216/267/345/217/226.md +21 -0
  177. package/vendor/BadCase/rn/rules/R-07-/346/231/272/350/203/275/346/214/207/351/222/210/347/224/237/345/221/275/345/221/250/346/234/237/347/256/241/347/220/206.md +20 -0
  178. package/vendor/BadCase/rn/rules/R-08-/346/211/213/345/212/277/344/272/213/344/273/266/346/263/250/345/206/214/344/270/216/345/206/262/347/252/201/345/244/204/347/220/206.md +22 -0
  179. package/vendor/BadCase/rn/rules/R-09-/346/250/252/347/253/226/345/261/217/345/244/232/345/275/242/346/200/201/345/270/203/345/261/200/351/200/202/351/205/215.md +20 -0
  180. package/vendor/BadCase/rn/rules/R-10-/346/267/261/350/211/262/346/250/241/345/274/217/344/270/273/351/242/230/351/242/234/350/211/262/351/200/202/351/205/215.md +19 -0
  181. package/vendor/BadCase/rn/rules/R-11-/346/216/245/345/217/243/345/233/236/350/260/203/345/256/236/347/216/260/345/256/214/346/225/264/346/200/247.md +20 -0
  182. package/vendor/BadCase/rn/rules/R-12-/345/216/237/347/224/237/350/277/220/350/241/214/346/227/266/350/265/204/346/272/220/346/236/220/346/236/204/346/227/266/345/272/217.md +20 -0
  183. package/vendor/BadCase/rn/rules/R-13-/346/226/207/344/273/266/346/225/260/346/215/256/347/274/226/347/240/201/344/270/216/346/240/274/345/274/217/345/205/274/345/256/271.md +21 -0
  184. package/vendor/BadCase/rn/rules/R-14-/345/212/250/347/224/273/345/210/235/345/247/213/345/214/226/346/227/266/345/272/217/344/270/216/347/212/266/346/200/201/345/220/214/346/255/245.md +20 -0
  185. package/vendor/BadCase/rn/rules/R-15-/347/212/266/346/200/201/346/214/201/344/271/205/345/214/226/344/270/216/351/207/215/345/220/257/351/200/273/350/276/221/344/270/200/350/207/264/346/200/247.md +20 -0
  186. package/vendor/BadCase/rn/rules/R-16-/346/225/217/346/204/237/344/277/241/346/201/257/346/227/245/345/277/227/346/263/204/351/234/262.md +19 -0
  187. package/vendor/BadCase/rn/rules/R-17-/345/252/222/344/275/223/350/265/204/346/272/220/347/224/237/345/221/275/345/221/250/346/234/237/344/270/216/351/224/231/350/257/257/345/244/204/347/220/206.md +21 -0
  188. package/vendor/BadCase/rn/rules/R-18-/350/203/275/345/212/233/346/236/232/344/270/276/351/205/215/347/275/256/345/256/214/346/225/264/346/200/247.md +19 -0
  189. package/vendor/BadCase/rn/rules/R-19-/345/233/276/345/275/242/346/270/262/346/237/223/345/261/236/346/200/247/347/273/247/346/211/277/344/270/216/351/207/215/347/275/256.md +19 -0
  190. package/vendor/MCP/README.md +490 -0
  191. package/vendor/MCP/config.json +29 -0
  192. package/vendor/MCP/package.json +23 -0
  193. package/vendor/MCP/scripts/analyze-demo-coverage.cjs +1203 -0
  194. package/vendor/MCP/scripts/analyze-xts-coverage.cjs +1245 -0
  195. package/vendor/MCP/scripts/deveco_docs.js +247 -0
  196. package/vendor/MCP/scripts/extract-interfaces.cjs +1487 -0
  197. package/vendor/MCP/scripts/flutter-analyze-demo-coverage.cjs +666 -0
  198. package/vendor/MCP/scripts/flutter-analyze-xts-coverage.cjs +684 -0
  199. package/vendor/MCP/scripts/flutter-extract-interfaces.cjs +1251 -0
  200. package/vendor/MCP/scripts/flutter-render-spec-doc.cjs +351 -0
  201. package/vendor/MCP/scripts/render-spec-doc.cjs +347 -0
  202. package/vendor/MCP/scripts/rn-analyze-demo-coverage.cjs +645 -0
  203. package/vendor/MCP/scripts/rn-analyze-test-coverage.cjs +614 -0
  204. package/vendor/MCP/scripts/rn-extract-interfaces.cjs +1462 -0
  205. package/vendor/MCP/scripts/rn-render-spec-doc.cjs +327 -0
  206. package/vendor/MCP/scripts/rn-scan-exports.cjs +1336 -0
  207. package/vendor/MCP/src/client/MCPClientPool.js +220 -0
  208. package/vendor/MCP/src/config/ConfigLoader.js +189 -0
  209. package/vendor/MCP/src/executor/ScriptExecutor.js +110 -0
  210. package/vendor/MCP/src/gateway/Gateway.js +451 -0
  211. package/vendor/MCP/src/index.js +87 -0
  212. package/vendor/MCP/src/library/KBToolProvider.js +229 -0
  213. package/vendor/MCP/src/library/logger.js +60 -0
  214. package/vendor/MCP/src/registry/ScriptRegistry.js +238 -0
  215. package/vendor/MCP/src/registry/ToolRegistry.js +111 -0
  216. package/vendor/MCP/src/types.js +115 -0
  217. package/vendor/Server/CLI/cli.js +3 -3
  218. package/vendor/Server/Routes/server.js +3 -3
  219. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +4 -2
  220. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +3 -3
  221. package/vendor/Server/Skills/knowledge-import/SKILL.md +1 -1
  222. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +12 -4
  223. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +5 -4
  224. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +2 -2
  225. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +2 -2
  226. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +1 -2
  227. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +1 -1
  228. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +1 -2
  229. package/vendor/Server/package.json +5 -1
  230. package/vendor/VERSION +3 -3
package/lib/web.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * web.js —— 3rdloop web 命令:启动 Web 前端服务
2
+ * web.js —— Web 前端服务库(由 3rdloop serve 内嵌启动)
3
3
  *
4
4
  * 架构(方案 A + D 组合):
5
5
  * - 核心前端资产:仓库 Web/public(开发态)/ cli/web(发布态,prepack 复制)
@@ -15,12 +15,11 @@
15
15
  * 3. /api/* 与 /stream* 反向代理到后端 Server(SSE 透传)
16
16
  * 4. 其余 核心前端静态文件
17
17
  *
18
- * 用法:
19
- * 3rdloop web [--port 8080] [--backend http://127.0.0.1:3000]
20
- * [--web-root <dir>] [--ext-root <dir>]... [--open]
21
- * 3rdloop web install-ext <dir> 安装扩展到 ~/.3lib/3rdloop/web-ext/
22
- * 3rdloop web list-ext 列出可用扩展(含来源与挂载点)
23
- * 3rdloop web remove-ext <name> 移除已安装扩展
18
+ * 本模块不再提供独立 CLI 命令(原 `3rdloop web` 已合并到 `3rdloop serve`,
19
+ * Web 前端随 serve 默认启动)。对外导出供 serve.js 组合使用:
20
+ * startWebServer() 一站式:解析资产/加载扩展/监听端口
21
+ * resolveUserExtRoot() 用户扩展根目录解析
22
+ * webExtInstall / webExtList / webExtRemove 扩展管理子命令
24
23
  */
25
24
 
26
25
  import http from 'node:http';
@@ -39,8 +38,6 @@ import {
39
38
  import { EXIT } from './exit-codes.js';
40
39
 
41
40
  const CLI_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
42
- const DEFAULT_PORT = 8080;
43
- const DEFAULT_BACKEND = 'http://127.0.0.1:3000';
44
41
 
45
42
  // ─── MIME ─────────────────────────────────────────────────────────
46
43
  const MIME = {
@@ -68,160 +65,24 @@ const MIME = {
68
65
  };
69
66
 
70
67
  // ═══════════════════════════════════════════════════════════════════
71
- // 命令入口(由 cli.js 分发)
68
+ // 目录解析
72
69
  // ═══════════════════════════════════════════════════════════════════
73
70
 
74
71
  /**
75
- * @param {object} opts
76
- * @param {object} opts.flags - parseGlobalFlags 结果
77
- * @param {string[]} opts.rest - 剩余参数(含子命令位置参数与 web 专属 flags)
78
- * @param {string} opts.user3libHome - 用户 3lib 主目录(~/.3lib)
79
- * @param {string} opts.dataDir - CLI 数据目录
80
- * @param {boolean} opts.jsonMode
81
- * @param {Function} opts.out - --json 输出函数
82
- * @returns {Promise<number>} 退出码
72
+ * 用户扩展根目录(3LIB_WEB_EXT_ROOT > ~/.3lib/3rdloop/web-ext)。
73
+ * @param {string} user3libHome - 用户 3lib 主目录(~/.3lib)
83
74
  */
84
- export async function cmdWeb({ flags, user3libHome, dataDir, jsonMode, out }) {
85
- // 注意:使用 flags.rest(全局 flags 如 --json/--data-dir 已被 parseGlobalFlags 消费)
86
- let argv;
87
- try {
88
- argv = _parseWebArgs(flags.rest);
89
- } catch (err) {
90
- process.stderr.write(`${err.message}\n`);
91
- _printWebHelp();
92
- return EXIT.ERROR;
93
- }
94
- const userExtRoot = process.env['3LIB_WEB_EXT_ROOT']
75
+ export function resolveUserExtRoot(user3libHome) {
76
+ return process.env['3LIB_WEB_EXT_ROOT']
95
77
  ? path.resolve(process.env['3LIB_WEB_EXT_ROOT'])
96
78
  : path.join(user3libHome, '3rdloop', 'web-ext');
97
-
98
- if (argv.help) {
99
- _printWebHelp();
100
- return EXIT.OK;
101
- }
102
-
103
- const positional = argv.positionals;
104
-
105
- // ── 子命令分发 ──
106
- if (positional[0] === 'install-ext') {
107
- return _cmdInstallExt(positional[1], { userExtRoot, jsonMode, out });
108
- }
109
- if (positional[0] === 'list-ext') {
110
- return _cmdListExt({ userExtRoot, jsonMode, out });
111
- }
112
- if (positional[0] === 'remove-ext') {
113
- return _cmdRemoveExt(positional[1], { userExtRoot, jsonMode, out });
114
- }
115
- if (positional[0] !== undefined) {
116
- process.stderr.write(`未知子命令: ${positional[0]}\n`);
117
- _printWebHelp();
118
- return EXIT.ERROR;
119
- }
120
-
121
- // ── 启动 Web 服务 ──
122
- return _startServer({ argv, userExtRoot, dataDir });
123
- }
124
-
125
- // ═══════════════════════════════════════════════════════════════════
126
- // 参数解析
127
- // ═══════════════════════════════════════════════════════════════════
128
-
129
- /**
130
- * 解析 web 命令专属参数。
131
- * parseGlobalFlags 已消费全局 flags(--json/--data-dir 等),这里处理剩余部分。
132
- * 非法参数直接 throw,由 cmdWeb 统一捕获打印帮助。
133
- */
134
- function _parseWebArgs(rest) {
135
- const opts = {
136
- port: DEFAULT_PORT,
137
- backend: DEFAULT_BACKEND,
138
- webRoot: '',
139
- extRoots: [],
140
- extNames: [],
141
- noExt: false,
142
- open: false,
143
- help: false,
144
- positionals: [],
145
- };
146
-
147
- for (let i = 0; i < rest.length; i++) {
148
- const arg = rest[i];
149
-
150
- if (arg === '--port') {
151
- const raw = rest[++i];
152
- const n = parseInt(raw, 10);
153
- if (!Number.isFinite(n) || n <= 0 || n > 65535) {
154
- throw new Error(`非法端口: "${raw}"`);
155
- }
156
- opts.port = n;
157
- } else if (arg === '--backend') {
158
- const raw = rest[++i];
159
- if (!raw || !/^https?:\/\//.test(raw)) {
160
- throw new Error(`非法后端地址: "${raw}"(须为 http(s):// 开头)`);
161
- }
162
- opts.backend = raw.replace(/\/+$/, '');
163
- } else if (arg === '--web-root') {
164
- opts.webRoot = rest[++i] || '';
165
- } else if (arg === '--ext-root') {
166
- const raw = rest[++i];
167
- if (raw) opts.extRoots.push(raw);
168
- } else if (arg === '--no-ext') {
169
- opts.noExt = true;
170
- } else if (arg === '--ext') {
171
- // 白名单:逗号分隔 + 可重复指定(--ext tag --ext issue 等效 --ext tag,issue)
172
- const raw = rest[++i];
173
- if (raw) {
174
- for (const n of raw.split(',')) {
175
- const t = n.trim();
176
- if (t) opts.extNames.push(t);
177
- }
178
- }
179
- } else if (arg === '--open') {
180
- opts.open = true;
181
- } else if (arg === '--help' || arg === '-h') {
182
- opts.help = true;
183
- } else if (arg.startsWith('--')) {
184
- throw new Error(`未知选项: ${arg}(查看帮助: 3rdloop web --help)`);
185
- } else {
186
- opts.positionals.push(arg);
187
- }
188
- }
189
- return opts;
190
- }
191
-
192
- /**
193
- * 应用扩展开关过滤:
194
- * --no-ext → 全部禁用(纯后端操作模式:仅核心工作台 + loop + knowledge)
195
- * --ext 名称集 → 白名单,仅挂载指定扩展(未发现的名字打 warning)
196
- * --no-ext 优先于 --ext(同时给出时白名单被忽略)。
197
- *
198
- * @param {Array<object>} extensions - loadExtensions 发现的全部扩展
199
- * @param {object} argv - _parseWebArgs 结果
200
- * @param {(msg: string) => void} warn
201
- * @returns {Array<object>} 过滤后的扩展列表
202
- */
203
- function _applyExtFilter(extensions, argv, warn) {
204
- if (argv.noExt) return [];
205
- if (argv.extNames.length === 0) return extensions;
206
-
207
- const known = extensions.map((e) => e.name);
208
- for (const name of argv.extNames) {
209
- if (!known.includes(name)) {
210
- warn(`[web-ext] --ext 指定的扩展 "${name}" 未发现(可用: ${known.join(', ') || '无'})`);
211
- }
212
- }
213
- return extensions.filter((e) => argv.extNames.includes(e.name));
214
79
  }
215
80
 
216
- // ═══════════════════════════════════════════════════════════════════
217
- // 目录解析
218
- // ═══════════════════════════════════════════════════════════════════
219
-
220
81
  /**
221
82
  * 核心前端资产目录。
222
83
  * 优先级:--web-root > 3LIB_WEB_ROOT > 仓库 Web/public(开发态)> cli/web(发布态内置)
223
84
  */
224
- function _resolveWebRoot(flagValue) {
85
+ export function resolveWebRoot(flagValue) {
225
86
  if (flagValue) {
226
87
  const dir = path.resolve(flagValue);
227
88
  if (!fs.existsSync(dir)) throw new Error(`--web-root 目录不存在: ${dir}`);
@@ -249,7 +110,7 @@ function _resolveWebRoot(flagValue) {
249
110
  * 扩展根目录列表(优先级从高到低,同名扩展先注册者生效)。
250
111
  * 顺序:--ext-root(可多个)> 3LIB_WEB_EXT_ROOT > 仓库 Web/ext(开发态)> 用户扩展目录。
251
112
  */
252
- function _resolveExtRoots(extRootFlags, userExtRoot) {
113
+ export function resolveExtRoots(extRootFlags, userExtRoot) {
253
114
  const roots = [];
254
115
  const push = (p) => {
255
116
  const r = path.resolve(p);
@@ -267,33 +128,80 @@ function _resolveExtRoots(extRootFlags, userExtRoot) {
267
128
  }
268
129
 
269
130
  // ═══════════════════════════════════════════════════════════════════
270
- // 服务启动
131
+ // 服务启动(由 3rdloop serve 调用)
271
132
  // ═══════════════════════════════════════════════════════════════════
272
133
 
273
- async function _startServer({ argv, userExtRoot, dataDir }) {
274
- const warn = (msg) => process.stderr.write(`${msg}\n`);
134
+ /**
135
+ * 应用扩展开关过滤:
136
+ * --no-ext → 全部禁用(纯后端操作模式:仅核心工作台 + loop + knowledge)
137
+ * --ext 名称集 → 白名单,仅挂载指定扩展(未发现的名字打 warning)
138
+ * --no-ext 优先于 --ext(同时给出时白名单被忽略)。
139
+ *
140
+ * @param {Array<object>} extensions - loadExtensions 发现的全部扩展
141
+ * @param {object} opts - { noExt, extNames }
142
+ * @param {(msg: string) => void} warn
143
+ * @returns {Array<object>} 过滤后的扩展列表
144
+ */
145
+ export function applyExtFilter(extensions, { noExt, extNames }, warn) {
146
+ if (noExt) return [];
147
+ if (!extNames || extNames.length === 0) return extensions;
275
148
 
276
- let webRoot;
277
- try {
278
- webRoot = _resolveWebRoot(argv.webRoot);
279
- } catch (err) {
280
- process.stderr.write(`[3rdloop web] ${err.message}\n`);
281
- return EXIT.ERROR;
149
+ const known = extensions.map((e) => e.name);
150
+ for (const name of extNames) {
151
+ if (!known.includes(name)) {
152
+ warn(`[web-ext] --ext 指定的扩展 "${name}" 未发现(可用: ${known.join(', ') || '无'})`);
153
+ }
282
154
  }
155
+ return extensions.filter((e) => extNames.includes(e.name));
156
+ }
157
+
158
+ /**
159
+ * 一站式启动 Web 前端服务:解析前端资产 → 加载扩展 → 创建服务 → 监听端口。
160
+ * 由 3rdloop serve 内嵌调用(后端反代地址指向同进程的 Server 后端)。
161
+ *
162
+ * @param {object} opts
163
+ * @param {number} opts.port - 监听端口(必填)
164
+ * @param {string} opts.backend - 后端 Server 地址(必填,如 http://127.0.0.1:3000)
165
+ * @param {string} [opts.webRootFlag] - --web-root 覆盖
166
+ * @param {string[]} [opts.extRootFlags] - --ext-root 追加扩展根目录
167
+ * @param {string} [opts.userExtRoot] - 用户扩展根目录(resolveUserExtRoot 结果)
168
+ * @param {boolean} [opts.noExt] - 禁用全部扩展
169
+ * @param {string[]} [opts.extNames] - 白名单扩展名
170
+ * @param {string} [opts.dataDir] - CLI 数据目录(扩展数据落 <dataDir>/web-ext/<name>)
171
+ * @param {boolean} [opts.open] - 启动后自动打开浏览器
172
+ * @param {(msg: string) => void} [opts.log] - 日志函数(默认 stderr;--json 模式传 no-op)
173
+ * @param {string} [opts.tag] - 日志前缀标签
174
+ * @returns {Promise<import('node:http').Server>} 已监听的 http.Server(不注册信号处理,生命周期由调用方管理)
175
+ * @throws 前端资产缺失 / 端口被占用(EADDRINUSE)等启动错误
176
+ */
177
+ export async function startWebServer({
178
+ port,
179
+ backend,
180
+ webRootFlag = '',
181
+ extRootFlags = [],
182
+ userExtRoot = '',
183
+ noExt = false,
184
+ extNames = [],
185
+ dataDir = '',
186
+ open = false,
187
+ log = (msg) => process.stderr.write(`${msg}\n`),
188
+ tag = '3rdloop serve',
189
+ }) {
190
+ const webRoot = resolveWebRoot(webRootFlag);
283
191
 
284
192
  // 加载扩展 → 应用 --no-ext / --ext 开关 → 初始化路由
285
- const extRoots = _resolveExtRoots(argv.extRoots, userExtRoot);
286
- const discovered = loadExtensions(extRoots, warn).extensions;
287
- const extensions = _applyExtFilter(discovered, argv, warn);
193
+ const extRoots = resolveExtRoots(extRootFlags, userExtRoot);
194
+ const discovered = loadExtensions(extRoots, log).extensions;
195
+ const extensions = applyExtFilter(discovered, { noExt, extNames }, log);
288
196
  for (const ext of extensions) {
289
- ext.router = initExtRouter(ext, { cliRoot: CLI_ROOT, dataDir, logger: warn });
197
+ ext.router = initExtRouter(ext, { cliRoot: CLI_ROOT, dataDir, logger: log });
290
198
  }
291
199
  const mounted = extensions.filter((e) => e.page || e.router);
292
200
 
293
201
  const server = http.createServer((req, res) => {
294
- _handleRequest(req, res, { webRoot, extensions, backend: argv.backend })
202
+ _handleRequest(req, res, { webRoot, extensions, backend })
295
203
  .catch((err) => {
296
- process.stderr.write(`[3rdloop web] 请求处理异常: ${err.message}\n`);
204
+ log(`[${tag}] 请求处理异常: ${err.message}`);
297
205
  if (!res.headersSent) {
298
206
  res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
299
207
  }
@@ -303,52 +211,32 @@ async function _startServer({ argv, userExtRoot, dataDir }) {
303
211
  });
304
212
  });
305
213
 
306
- return await new Promise((resolve) => {
307
- server.on('error', (err) => {
308
- if (err.code === 'EADDRINUSE') {
309
- process.stderr.write(`[3rdloop web] 端口 ${argv.port} 已被占用(可用 --port 指定其他端口)\n`);
310
- } else {
311
- process.stderr.write(`[3rdloop web] 服务启动失败: ${err.message}\n`);
312
- }
313
- resolve(EXIT.ERROR);
314
- });
315
-
316
- server.listen(argv.port, () => {
317
- process.stderr.write(`[3rdloop web] 前端服务已启动: http://localhost:${argv.port}\n`);
318
- process.stderr.write(`[3rdloop web] 前端资产: ${webRoot}\n`);
319
- process.stderr.write(`[3rdloop web] 后端代理: ${argv.backend}(/api/*、/stream*)\n`);
320
- if (argv.noExt) {
321
- process.stderr.write('[3rdloop web] 扩展挂载已禁用(--no-ext):仅核心工作台 + loop + knowledge\n');
322
- } else if (argv.extNames.length > 0) {
323
- if (mounted.length > 0) {
324
- const desc = mounted.map((e) => `${e.mount}(${e.title})`).join('、');
325
- process.stderr.write(`[3rdloop web] 白名单挂载扩展 ${mounted.length}/${discovered.length} 个: ${desc}\n`);
326
- } else {
327
- process.stderr.write(`[3rdloop web] 白名单未匹配到任何扩展(发现 ${discovered.length} 个,--ext: ${argv.extNames.join(', ')})\n`);
328
- }
329
- } else if (mounted.length > 0) {
330
- const desc = mounted.map((e) => `${e.mount}(${e.title})`).join('、');
331
- process.stderr.write(`[3rdloop web] 已挂载扩展 ${mounted.length} 个: ${desc}\n`);
332
- } else {
333
- process.stderr.write(
334
- '[3rdloop web] 未挂载扩展(tag/issue/prcheck 等二级功能可用 "3rdloop web install-ext <dir>" 启用)\n'
335
- );
336
- }
214
+ await new Promise((resolve, reject) => {
215
+ server.once('error', reject);
216
+ server.listen(port, () => resolve());
217
+ });
337
218
 
338
- if (argv.open) _openBrowser(`http://localhost:${argv.port}`);
339
- });
219
+ log(`[${tag}] Web 前端已启动: http://localhost:${port}`);
220
+ log(`[${tag}] 前端资产: ${webRoot}`);
221
+ log(`[${tag}] 后端代理: ${backend}(/api/*、/stream*)`);
222
+ if (noExt) {
223
+ log(`[${tag}] 扩展挂载已禁用(--no-ext):仅核心工作台 + loop + knowledge`);
224
+ } else if (extNames.length > 0) {
225
+ if (mounted.length > 0) {
226
+ const desc = mounted.map((e) => `${e.mount}(${e.title})`).join('、');
227
+ log(`[${tag}] 白名单挂载扩展 ${mounted.length}/${discovered.length} 个: ${desc}`);
228
+ } else {
229
+ log(`[${tag}] 白名单未匹配到任何扩展(发现 ${discovered.length} 个,--ext: ${extNames.join(', ')})`);
230
+ }
231
+ } else if (mounted.length > 0) {
232
+ const desc = mounted.map((e) => `${e.mount}(${e.title})`).join('、');
233
+ log(`[${tag}] 已挂载扩展 ${mounted.length} 个: ${desc}`);
234
+ } else {
235
+ log(`[${tag}] 未挂载扩展(tag/issue/prcheck 等二级功能可用 "3rdloop serve install-ext <dir>" 启用)`);
236
+ }
340
237
 
341
- // 退出码与 CLI 约定对齐:130 SIGINT / 143 SIGTERM
342
- const shutdown = (code) => {
343
- process.stderr.write('\n[3rdloop web] 正在关闭…\n');
344
- server.close(() => process.exit(code));
345
- // 兜底:close 回调被长连接阻塞时强制退出
346
- setTimeout(() => process.exit(code), 1500).unref();
347
- };
348
- process.on('SIGINT', () => shutdown(130));
349
- process.on('SIGTERM', () => shutdown(143));
350
- // cmdWeb 的 Promise 不 resolve:服务常驻,退出走信号处理
351
- });
238
+ if (open) _openBrowser(`http://localhost:${port}`);
239
+ return server;
352
240
  }
353
241
 
354
242
  // ═══════════════════════════════════════════════════════════════════
@@ -541,12 +429,12 @@ function _openBrowser(url) {
541
429
  }
542
430
 
543
431
  // ═══════════════════════════════════════════════════════════════════
544
- // 子命令:install-ext / list-ext / remove-ext
432
+ // 扩展管理子命令(由 3rdloop serve install-ext/list-ext/remove-ext 分发)
545
433
  // ═══════════════════════════════════════════════════════════════════
546
434
 
547
- function _cmdInstallExt(srcDir, { userExtRoot, jsonMode, out }) {
435
+ export function webExtInstall(srcDir, { userExtRoot, jsonMode, out }) {
548
436
  if (!srcDir) {
549
- process.stderr.write('用法: 3rdloop web install-ext <扩展目录>\n');
437
+ process.stderr.write('用法: 3rdloop serve install-ext <扩展目录>\n');
550
438
  return EXIT.ERROR;
551
439
  }
552
440
  try {
@@ -563,9 +451,9 @@ function _cmdInstallExt(srcDir, { userExtRoot, jsonMode, out }) {
563
451
  }
564
452
  }
565
453
 
566
- function _cmdListExt({ userExtRoot, jsonMode, out }) {
454
+ export function webExtList({ userExtRoot, jsonMode, out }) {
567
455
  const warn = (msg) => process.stderr.write(`${msg}\n`);
568
- const extRoots = _resolveExtRoots([], userExtRoot);
456
+ const extRoots = resolveExtRoots([], userExtRoot);
569
457
  const { extensions } = loadExtensions(extRoots, warn);
570
458
 
571
459
  const items = extensions.map((e) => ({
@@ -586,7 +474,7 @@ function _cmdListExt({ userExtRoot, jsonMode, out }) {
586
474
  if (items.length === 0) {
587
475
  process.stdout.write('暂无可用扩展。\n');
588
476
  process.stdout.write(`扩展目录(可手动放置或用 install-ext 安装): ${userExtRoot}\n`);
589
- process.stdout.write('仓库内置扩展源: <仓库>/Web/ext/,如 3rdloop web install-ext Web/ext/tag\n');
477
+ process.stdout.write('仓库内置扩展源: <仓库>/Web/ext/,如 3rdloop serve install-ext Web/ext/tag\n');
590
478
  return EXIT.OK;
591
479
  }
592
480
 
@@ -598,9 +486,9 @@ function _cmdListExt({ userExtRoot, jsonMode, out }) {
598
486
  return EXIT.OK;
599
487
  }
600
488
 
601
- function _cmdRemoveExt(name, { userExtRoot, jsonMode, out }) {
489
+ export function webExtRemove(name, { userExtRoot, jsonMode, out }) {
602
490
  if (!name) {
603
- process.stderr.write('用法: 3rdloop web remove-ext <扩展名>\n');
491
+ process.stderr.write('用法: 3rdloop serve remove-ext <扩展名>\n');
604
492
  return EXIT.ERROR;
605
493
  }
606
494
  try {
@@ -616,49 +504,3 @@ function _cmdRemoveExt(name, { userExtRoot, jsonMode, out }) {
616
504
  return EXIT.ERROR;
617
505
  }
618
506
  }
619
-
620
- // ═══════════════════════════════════════════════════════════════════
621
- // 帮助
622
- // ═══════════════════════════════════════════════════════════════════
623
-
624
- function _printWebHelp() {
625
- process.stdout.write(`
626
- 3rdloop web —— 启动 Web 前端服务(静态托管 + /api 反代 + 扩展挂载)
627
-
628
- 用法:
629
- 3rdloop web [选项] 启动前端服务
630
- 3rdloop web install-ext <dir> 安装扩展到 ~/.3lib/3rdloop/web-ext/
631
- 3rdloop web list-ext 列出可用扩展(含来源与挂载点)
632
- 3rdloop web remove-ext <name> 移除已安装扩展
633
-
634
- 选项:
635
- --port <N> 监听端口(默认 8080)
636
- --backend <url> 后端 Server 地址(默认 http://127.0.0.1:3000,/api/* 与 /stream* 反代)
637
- --web-root <dir> 核心前端资产目录覆盖(默认: 仓库 Web/public,发布包为内置 cli/web)
638
- --ext-root <dir> 追加扩展根目录(可多次指定;目录下每个子目录是一个扩展)
639
- --ext <name,...> 白名单:仅挂载指定扩展(逗号分隔,可重复指定;未发现的名字打警告)
640
- --no-ext 禁用全部扩展(纯后端操作模式;优先于 --ext)
641
- --open 启动后自动打开浏览器
642
- -h, --help 本帮助
643
-
644
- 两种典型模式:
645
- 本地全功能(仓库开发态):
646
- node cli/bin/3rdloop.mjs web
647
- = 核心前端(Web/public:PR/仓库检视 + loop + knowledge)+ 全部扩展(Web/ext:tag/issue/prcheck)
648
- 纯后端操作(经 3rdloop,不受已安装扩展影响):
649
- 3rdloop web --no-ext
650
- = 仅核心工作台,导航栏不出现 tag/issue/prcheck 等二级功能入口
651
-
652
- 扩展机制:
653
- - 扩展 = 自包含目录(manifest.json + router.cjs + public/),提供 /tag、
654
- /issue、/prcheck 等二级目录功能,不随 npm 包分发
655
- - 加载来源优先级: --ext-root > 3LIB_WEB_EXT_ROOT > 仓库 Web/ext(开发态)> ~/.3lib/3rdloop/web-ext
656
- - 扩展数据目录: <数据目录>/web-ext/<name>(跟随 --data-dir)
657
- - 启用示例: 3rdloop web install-ext Web/ext/tag && 3rdloop web
658
- - 前端导航栏通过 /api/ext-registry 自动渲染已挂载扩展入口
659
-
660
- 环境变量:
661
- 3LIB_WEB_ROOT 核心前端资产目录(等效 --web-root)
662
- 3LIB_WEB_EXT_ROOT 用户扩展根目录(默认 ~/.3lib/3rdloop/web-ext)
663
- `);
664
- }
package/lib/workflow.js CHANGED
@@ -134,6 +134,17 @@ export async function cmdWorkflow({ wf, wfWords, flags, env }) {
134
134
  return EXIT.OK;
135
135
  }
136
136
 
137
+ // ── 环境预检(正式执行前快速失败;dry-run 已在上方返回,不预检) ──
138
+ {
139
+ const { precheckEnv, reportPrecheckFailures } = await import('./doctor.js');
140
+ const pre = await precheckEnv({ projectRoot: env.projectRoot, dataDir, skillDir: env.skillDir });
141
+ if (!pre.ok) {
142
+ if (jsonMode) out({ ok: false, error: '环境预检未通过', failures: pre.failures });
143
+ else reportPrecheckFailures(pre.failures);
144
+ return EXIT.ERROR;
145
+ }
146
+ }
147
+
137
148
  // ── 正式执行:复用 orch 链路 ────────────────────────────────────
138
149
  // taskId 缺省由 runner 生成(generateTaskId),workflow 可覆盖
139
150
  const r = await _runner();
@@ -141,12 +152,11 @@ export async function cmdWorkflow({ wf, wfWords, flags, env }) {
141
152
  projectRoot: env.projectRoot,
142
153
  dataDir,
143
154
  skillDir: env.skillDir,
144
- cliType: 'opencode',
145
155
  verbose,
146
156
  });
147
157
 
148
- // 挂载 opencode 生命周期
149
- const oc = await _ensureOpcodene(ctx, jsonMode, out);
158
+ // 挂载 AI CLI serve 生命周期(类型由 CLI_TYPE 决定)
159
+ const oc = await _ensureCliServe(ctx, jsonMode, out);
150
160
  if (!oc) return EXIT.ERROR;
151
161
 
152
162
  // 信号处理:优雅中止(orchControl abort → kill 子进程)
@@ -292,15 +302,15 @@ function _queries() {
292
302
  return import('./queries.js');
293
303
  }
294
304
 
295
- /** 确保 opencode serve 运行(正式执行需要;dry-run 不需要)。 */
296
- async function _ensureOpcodene(ctx, jsonMode, out) {
297
- const { OpencodeManager } = await import('./opencode.js');
298
- const oc = new OpencodeManager({ projectRoot: ctx.projectRoot });
305
+ /** 确保 AI CLI serve 运行(正式执行需要;dry-run 不需要)。 */
306
+ async function _ensureCliServe(ctx, jsonMode, out) {
307
+ const { CliServeManager } = await import('./opencode.js');
308
+ const oc = new CliServeManager({ projectRoot: ctx.projectRoot, cliType: ctx.cliType });
299
309
  const ocHealth = await oc.ensureRunning();
300
310
  if (!ocHealth.ok) {
301
311
  if (jsonMode) out({ ok: false, error: ocHealth.message });
302
312
  process.stderr.write(`\n错误: ${ocHealth.message}\n`);
303
- process.stderr.write('请先启动 opencode serve(项目上一级目录: opencode serve)后重试。\n');
313
+ process.stderr.write(`请先启动 ${oc.cliType} serve(项目上一级目录: ${oc.bin} serve)后重试。\n`);
304
314
  await oc.stop();
305
315
  return null;
306
316
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohos-cpf/3rdloop",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "3rdLibraryLoop 三方库自动化检视 CLI:嵌入式复用核心引擎,任务生命周期管理(submit/run/wait/progress/abort/list/result/doctor)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -43,9 +43,13 @@
43
43
  "test": "node --test test/*.mjs"
44
44
  },
45
45
  "dependencies": {
46
+ "@modelcontextprotocol/sdk": "^1.0.0",
46
47
  "@opencode-ai/sdk": "^1.18.6",
47
48
  "better-sqlite3": "^13.0.1",
49
+ "chokidar": "^4.0.0",
48
50
  "dotenv": "^17.0.0",
49
- "openai": "^4.0.0"
51
+ "jieba-wasm": "^2.4.0",
52
+ "openai": "^4.0.0",
53
+ "typescript": "^5.5.4"
50
54
  }
51
55
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "indexVersion": "1.0.0",
3
+ "termsHash": "244653ada2b478d99365a03c7b7a50f73bf086d4966ce050d0db91494b883367",
4
+ "synonymsHash": "692fd79264100cf346359dcf57101ff7699d1e88c460a4ee7cc4c0d7b4e9936a",
5
+ "segmentCount": 44,
6
+ "builtAt": 1788338587090
7
+ }
@@ -0,0 +1,13 @@
1
+ ## arkts_check 独立工具使用坑:相对路径假阴性与环境误报甄别
2
+
3
+ ### 问题现象
4
+ 对 PR 新增的 `.ets` 文件以**相对路径**(如 `entry/src/ohosTest/...`)调用独立 arkts_check 工具时返回 "No errors found",看似零问题;改用**绝对路径**复查同一文件却立即报出多处错误(含真实 `arkts-no-any-unknown` 违规)。另有两类干扰:所有 `@ohos/*` import 均报 "Cannot find module";批量检查输出中混入大量非目标文件(master 既有文件)的历史存量错误,干扰统计。
5
+
6
+ ### 解决方案
7
+ 1. arkts_check 必须使用**绝对路径**调用;必要时用一个"已知有问题的文件"做**阳性对照**,验证工具确实在检查。
8
+ 2. `Cannot find module '@ohos/*'` 类错误是独立 linter 无法解析 ohpm 本地依赖(`@ohos/xxx` → `file:../library`)产生的**环境误报**,不计入问题统计。
9
+ 3. **基线对照法**:对 master 既有文件跑同样的检查作为基线——既有文件的违规属于历史存量,与本 PR 无关;将 PR 新增文件的报错与基线对比,即可区分"PR 引入"与"历史存量"。
10
+ 4. 批量调用 arkts_check 可能实际检查目录内全部文件,需对工具保存的完整输出**按目标文件名 grep 过滤**后再统计;单文件逐个检查更可控。
11
+
12
+ ### 结果
13
+ 在 mp3agic PR #53 检视中,通过绝对路径 + 基线对照确认:master 既有测试文件基线为 125+ 错误(大量历史存量 arkts-no-any-unknown),而 PR 新增代码仅 1 处真实违规,显著优于仓库基线。避免了"相对路径假阴性导致漏检"和"环境误报/历史存量导致误判"两类错误结论。
@@ -0,0 +1,31 @@
1
+ # ArkTS 测试代码的深层路径 import 风格与识别方法
2
+
3
+ ### 问题现象
4
+
5
+ 分析 ArkTS(鸿蒙)三方库的测试代码(XTS/Demo)时,测试文件的 import 存在两种风格:
6
+
7
+ ```ts
8
+ // 风格 A:包名导入
9
+ import { EncodedText, ID3v22Tag } from '@ohos/mp3agic'
10
+ // 风格 B:深层路径导入(直接指向库内源文件)
11
+ import { BufferTools } from '@ohos/mp3agic/src/main/ets/components/mp3agic/BufferTools'
12
+ import { MutableInteger } from '@ohos/mp3agic/src/main/ets/components/mp3agic/MutableInteger'
13
+ ```
14
+
15
+ 基于 import 匹配的静态分析工具若只匹配包名(`fromLib === libName || fromLib.endsWith('/' + libName.split('/').pop())`),风格 B 不满足任何条件,导致 `importedSymbols` 为空、整个测试文件被静默跳过。mp3agic 案例中一个 803 行、覆盖 28 个静态方法的测试文件被整体漏掉,且无任何告警。
16
+
17
+ ### 解决方案
18
+
19
+ 在包名匹配条件基础上增加深层路径前缀识别:
20
+
21
+ ```js
22
+ // AST 与正则引擎均需同步修改
23
+ if (fromLib !== libName && !fromLib.endsWith('/' + libName.split('/').pop())
24
+ && !fromLib.startsWith(libName + '/')) continue;
25
+ ```
26
+
27
+ AST 模式下还需在 `resolveModuleNames` 中将深层路径模块说明符映射到库目录下的真实源文件:`'@ohos/pkg/src/main/ets/...'` → `library/src/main/ets/...`(同时做小写键二次查找以兼容大小写差异)。
28
+
29
+ ### 结果
30
+
31
+ mp3agic 库 XTS 覆盖率分析结果从 8/271(2.95%)修正为 100/271(36.90%),与人工 grep 全量调用盘点逐类一致。结论:任何扫描 ArkTS 测试/示例代码做调用分析的工具,都必须同时兼容包名导入与深层路径导入两种风格,并在 import 均未被识别时输出告警。该修复已合入 `analyze-xts-coverage.cjs`(arkts-library-xts-coverage SKILL v1.1.0)。
@@ -0,0 +1,25 @@
1
+ ## ArkTS 库入口文件大小写不一致导致覆盖率分析静默低估
2
+
3
+ ### 问题现象
4
+
5
+ 修复 import 尾斜杠问题后,mp3agic Demo 覆盖率仍仅 58.67%(159/271),抽查发现 `Mp3File.getId3v1Tag`(6 处调用)、`Mp3v2CommentFrameData.setLanguage` 等明显漏判。用最小化调试脚本重现 AST Program 后发现 `let mp3file = new Mp3File(...)` 的推断类型是 `any`,且存在 `Cannot find module '@ohos/mp3agic'` 诊断。此缺陷只造成部分漏判而非全 0(共 29 个接口漏判),脚本对"有库 import 但匹配数为 0 的文件"不产生任何警告、退出码仍为 0,极难在常规测试中暴露。
6
+
7
+ ### 解决方案
8
+
9
+ **根因链**:`extract-interfaces.cjs` 的 `locateEntryFile` 候选路径硬编码 `library/Index.ets`(大写 I);mp3agic 仓库实际文件是 `library/index.ets`(小写 i);macOS 文件系统大小写不敏感,`fs.existsSync('Index.ets')` 通过,脚本拿到"看似有效"的错误大小写路径;`matchCoverageAST` 用该路径构造虚拟路径 key(`Index.ts`),而 `fileContents` Map 的 key 来自 `readdirSync` 的真实大小写(`index.ts`),精确匹配失败 → `resolveModuleNames` 解析不到模块 → 库类型全部坍缩为 `any` → 基于类型的调用匹配失效。
10
+
11
+ **修复方案**(2026-08-27 已落入脚本):
12
+ 1. `locateEntryFile` 返回前用 `fs.readdirSync(path.dirname(entryFile))` 核对文件名真实大小写并回写(toRealCase 辅助函数)
13
+ 2. `resolveModuleNames` 解析入口虚拟路径失败时对 `fileContents` keys 做大小写不敏感回退查找
14
+ 3. 新增静默失败告警防线:编译诊断出现 `Cannot find module '<库名>'`(TS 错误码 2307)时放弃 AST 结果、降级正则模式;文件有库 import 但匹配数为 0 时输出 ⚠️ 提示;覆盖率 0%/异常低(<10%)时在控制台与报告中输出「分析可信度警告」章节
15
+
16
+ **可复用诊断方法**:
17
+ - 用调试脚本打印 `checker.typeToString(getTypeAtLocation(varDecl))`,实例变量为 `any` 即模块解析失败
18
+ - 打印 `program.getSemanticDiagnostics()` 检查是否存在 `Cannot find module '<库名>'`
19
+ - 对报告"未覆盖"接口抽样 grep 真实调用数与报告交叉验证
20
+
21
+ **旧版本脚本应急 workaround**:复制 libroot 到临时目录(排除 .git),把 `library/index.ets` 重命名为 `Index.ets`,用 `--libroot` 指向副本。
22
+
23
+ ### 结果
24
+
25
+ 修复后在 macOS 上直接分析原始仓库(小写入口 + 尾斜杠 import)得到真实覆盖率 69.37%,与"临时副本改名"workaround 结果完全一致。平台差异提醒:入口大小写坑在 macOS(大小写不敏感文件系统)上表现为静默低估,在 Linux(大小写敏感)上表现为 `existsSync` 直接失败走降级逻辑,两者症状不同但同源。快速诊断口诀:"部分漏、变量 any → 查入口大小写"。