@lesliechan721/aw 0.1.0-beta.3 → 0.1.0-beta.30

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 (529) hide show
  1. package/dist/authoring/assets/aw-config.mjs +100 -501
  2. package/dist/authoring/assets/aw-config.mjs.map +1 -1
  3. package/dist/authoring/scripts/add-skill.mjs +4 -4
  4. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  5. package/dist/authoring/scripts/init-plugin.mjs +67 -97
  6. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  7. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +15 -0
  8. package/dist/authoring/scripts/lib/plugin-version-check.mjs +239 -232
  9. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  10. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
  11. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
  12. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  13. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  14. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  15. package/dist/authoring/scripts/remove-plugin.mjs +367 -0
  16. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  17. package/dist/authoring/scripts/validate-plugin.mjs +307 -1173
  18. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  19. package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
  20. package/dist/scripts/aw-skill-eval-materializer.js +306 -0
  21. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  22. package/dist/scripts/bundle-plugins.d.ts +1 -2
  23. package/dist/scripts/bundle-plugins.js +24 -7
  24. package/dist/scripts/bundle-plugins.js.map +1 -1
  25. package/dist/scripts/plugin-staging.d.ts +7 -24
  26. package/dist/scripts/plugin-staging.js +22 -142
  27. package/dist/scripts/plugin-staging.js.map +1 -1
  28. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  29. package/dist/scripts/test-plugin-scripts.js +27 -75
  30. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  31. package/dist/src/adapters/helpers.js +2 -2
  32. package/dist/src/adapters/helpers.js.map +1 -1
  33. package/dist/src/authoring/workspace-commands.d.ts +26 -2
  34. package/dist/src/authoring/workspace-commands.js +44 -19
  35. package/dist/src/authoring/workspace-commands.js.map +1 -1
  36. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  37. package/dist/src/commands/aw-capabilities.js +256 -0
  38. package/dist/src/commands/aw-capabilities.js.map +1 -0
  39. package/dist/src/commands/catalog-dev-source.d.ts +154 -0
  40. package/dist/src/commands/catalog-dev-source.js +655 -0
  41. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  42. package/dist/src/commands/catalog-generation.d.ts +21 -0
  43. package/dist/src/commands/catalog-generation.js +9 -0
  44. package/dist/src/commands/catalog-generation.js.map +1 -0
  45. package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
  46. package/dist/src/commands/catalog-publication-inspection.js +757 -0
  47. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  48. package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
  49. package/dist/src/commands/catalog-publication-journal.js +292 -0
  50. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  51. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  52. package/dist/src/commands/catalog-publication-target.js +95 -0
  53. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  54. package/dist/src/commands/catalog-publish.d.ts +77 -7
  55. package/dist/src/commands/catalog-publish.js +746 -316
  56. package/dist/src/commands/catalog-publish.js.map +1 -1
  57. package/dist/src/commands/catalog-source.d.ts +60 -22
  58. package/dist/src/commands/catalog-source.js +311 -91
  59. package/dist/src/commands/catalog-source.js.map +1 -1
  60. package/dist/src/commands/catalog-status.d.ts +56 -0
  61. package/dist/src/commands/catalog-status.js +217 -0
  62. package/dist/src/commands/catalog-status.js.map +1 -0
  63. package/dist/src/commands/catalog-store.d.ts +104 -0
  64. package/dist/src/commands/catalog-store.js +685 -0
  65. package/dist/src/commands/catalog-store.js.map +1 -0
  66. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  67. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  68. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  69. package/dist/src/commands/console.js +28 -17
  70. package/dist/src/commands/console.js.map +1 -1
  71. package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
  72. package/dist/src/commands/dependency-operation-plan.js +289 -7
  73. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  74. package/dist/src/commands/init.d.ts +5 -11
  75. package/dist/src/commands/init.js +30 -51
  76. package/dist/src/commands/init.js.map +1 -1
  77. package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
  78. package/dist/src/commands/offline-cleanup-evidence.js +181 -0
  79. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  80. package/dist/src/commands/operation-plan.d.ts +127 -0
  81. package/dist/src/commands/operation-plan.js +71 -0
  82. package/dist/src/commands/operation-plan.js.map +1 -0
  83. package/dist/src/commands/operation-planner.d.ts +46 -0
  84. package/dist/src/commands/operation-planner.js +141 -0
  85. package/dist/src/commands/operation-planner.js.map +1 -0
  86. package/dist/src/commands/options.d.ts +9 -7
  87. package/dist/src/commands/options.js +83 -21
  88. package/dist/src/commands/options.js.map +1 -1
  89. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  90. package/dist/src/commands/plugin-capabilities.js +159 -0
  91. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  92. package/dist/src/commands/rebuild-command.d.ts +77 -30
  93. package/dist/src/commands/rebuild-command.js +3993 -711
  94. package/dist/src/commands/rebuild-command.js.map +1 -1
  95. package/dist/src/commands/rebuild-integration.d.ts +120 -21
  96. package/dist/src/commands/rebuild-integration.js +2765 -1326
  97. package/dist/src/commands/rebuild-integration.js.map +1 -1
  98. package/dist/src/commands/rebuild-operations.d.ts +58 -30
  99. package/dist/src/commands/rebuild-operations.js +259 -319
  100. package/dist/src/commands/rebuild-operations.js.map +1 -1
  101. package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
  102. package/dist/src/commands/reconcile-operation-plan.js +47 -0
  103. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  104. package/dist/src/commands/review-inspection.d.ts +228 -0
  105. package/dist/src/commands/review-inspection.js +1341 -0
  106. package/dist/src/commands/review-inspection.js.map +1 -0
  107. package/dist/src/commands/review-preparation.d.ts +49 -0
  108. package/dist/src/commands/review-preparation.js +427 -0
  109. package/dist/src/commands/review-preparation.js.map +1 -0
  110. package/dist/src/commands/runtime.d.ts +1 -5
  111. package/dist/src/commands/runtime.js +24 -50
  112. package/dist/src/commands/runtime.js.map +1 -1
  113. package/dist/src/commands/skill-eval.d.ts +38 -0
  114. package/dist/src/commands/skill-eval.js +388 -0
  115. package/dist/src/commands/skill-eval.js.map +1 -0
  116. package/dist/src/commands/status-v6.d.ts +279 -0
  117. package/dist/src/commands/status-v6.js +2697 -0
  118. package/dist/src/commands/status-v6.js.map +1 -0
  119. package/dist/src/commands/sync-command.d.ts +40 -0
  120. package/dist/src/commands/sync-command.js +250 -0
  121. package/dist/src/commands/sync-command.js.map +1 -0
  122. package/dist/src/commands/wizard.d.ts +3 -4
  123. package/dist/src/commands/wizard.js +2 -2
  124. package/dist/src/commands/wizard.js.map +1 -1
  125. package/dist/src/commands/write-commands.d.ts +9 -3
  126. package/dist/src/commands/write-commands.js +124 -31
  127. package/dist/src/commands/write-commands.js.map +1 -1
  128. package/dist/src/constants.d.ts +7 -1
  129. package/dist/src/constants.js +7 -1
  130. package/dist/src/constants.js.map +1 -1
  131. package/dist/src/eval/manifest-v1.d.ts +71 -0
  132. package/dist/src/eval/manifest-v1.js +370 -0
  133. package/dist/src/eval/manifest-v1.js.map +1 -0
  134. package/dist/src/generators/local-plugin.d.ts +5 -1
  135. package/dist/src/generators/local-plugin.js +225 -104
  136. package/dist/src/generators/local-plugin.js.map +1 -1
  137. package/dist/src/index.d.ts +10 -4
  138. package/dist/src/index.js +1787 -476
  139. package/dist/src/index.js.map +1 -1
  140. package/dist/src/output/command-receipt.d.ts +43 -46
  141. package/dist/src/output/command-receipt.js +256 -80
  142. package/dist/src/output/command-receipt.js.map +1 -1
  143. package/dist/src/output/model.d.ts +3 -44
  144. package/dist/src/output/model.js +1 -144
  145. package/dist/src/output/model.js.map +1 -1
  146. package/dist/src/output/rebuild-progress.js +1 -13
  147. package/dist/src/output/rebuild-progress.js.map +1 -1
  148. package/dist/src/portable/commands.d.ts +46 -7
  149. package/dist/src/portable/commands.js +349 -37
  150. package/dist/src/portable/commands.js.map +1 -1
  151. package/dist/src/portable/materializer.d.ts +17 -4
  152. package/dist/src/portable/materializer.js +160 -38
  153. package/dist/src/portable/materializer.js.map +1 -1
  154. package/dist/src/portable/skills-cli.d.ts +15 -7
  155. package/dist/src/portable/skills-cli.js +66 -19
  156. package/dist/src/portable/skills-cli.js.map +1 -1
  157. package/dist/src/review/eval-source-input.d.ts +43 -0
  158. package/dist/src/review/eval-source-input.js +207 -0
  159. package/dist/src/review/eval-source-input.js.map +1 -0
  160. package/dist/src/review/review-profile.d.ts +17 -0
  161. package/dist/src/review/review-profile.js +177 -0
  162. package/dist/src/review/review-profile.js.map +1 -0
  163. package/dist/src/review/review-selection.d.ts +30 -0
  164. package/dist/src/review/review-selection.js +527 -0
  165. package/dist/src/review/review-selection.js.map +1 -0
  166. package/dist/src/review/review-types.d.ts +68 -0
  167. package/dist/src/review/review-types.js +16 -0
  168. package/dist/src/review/review-types.js.map +1 -0
  169. package/dist/src/review/skill-materialization.d.ts +13 -0
  170. package/dist/src/review/skill-materialization.js +75 -0
  171. package/dist/src/review/skill-materialization.js.map +1 -0
  172. package/dist/src/review/source-binding.d.ts +182 -0
  173. package/dist/src/review/source-binding.js +682 -0
  174. package/dist/src/review/source-binding.js.map +1 -0
  175. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  176. package/dist/src/tui/catalog-freshness.js +2 -0
  177. package/dist/src/tui/catalog-freshness.js.map +1 -0
  178. package/dist/src/tui/catalog-generation.d.ts +21 -0
  179. package/dist/src/tui/catalog-generation.js +156 -0
  180. package/dist/src/tui/catalog-generation.js.map +1 -0
  181. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  182. package/dist/src/tui/catalog-sources/projection.js +17 -0
  183. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  184. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  185. package/dist/src/tui/catalog-sources/review.js +13 -0
  186. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  187. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  188. package/dist/src/tui/catalog-sources/screens.js +40 -0
  189. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  190. package/dist/src/tui/catalog-sources/session.d.ts +28 -0
  191. package/dist/src/tui/catalog-sources/session.js +295 -0
  192. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  193. package/dist/src/tui/catalog-sources/types.d.ts +53 -0
  194. package/dist/src/tui/catalog-sources/types.js +2 -0
  195. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  196. package/dist/src/tui/catalog-store-task.d.ts +16 -0
  197. package/dist/src/tui/catalog-store-task.js +19 -0
  198. package/dist/src/tui/catalog-store-task.js.map +1 -0
  199. package/dist/src/tui/components.d.ts +128 -0
  200. package/dist/src/tui/components.js +251 -7
  201. package/dist/src/tui/components.js.map +1 -1
  202. package/dist/src/tui/console-app-v4.d.ts +14 -4
  203. package/dist/src/tui/console-app-v4.js +9248 -1461
  204. package/dist/src/tui/console-app-v4.js.map +1 -1
  205. package/dist/src/tui/controller-runtime.d.ts +13 -1
  206. package/dist/src/tui/controller-runtime.js +3338 -410
  207. package/dist/src/tui/controller-runtime.js.map +1 -1
  208. package/dist/src/tui/controller.d.ts +614 -40
  209. package/dist/src/tui/controller.js +3478 -708
  210. package/dist/src/tui/controller.js.map +1 -1
  211. package/dist/src/tui/dependency-graph.d.ts +81 -0
  212. package/dist/src/tui/dependency-graph.js +455 -0
  213. package/dist/src/tui/dependency-graph.js.map +1 -0
  214. package/dist/src/tui/diagnostic-projection.d.ts +23 -0
  215. package/dist/src/tui/diagnostic-projection.js +567 -0
  216. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  217. package/dist/src/tui/diagnostics.d.ts +30 -0
  218. package/dist/src/tui/diagnostics.js +76 -0
  219. package/dist/src/tui/diagnostics.js.map +1 -0
  220. package/dist/src/tui/glossary.d.ts +23 -0
  221. package/dist/src/tui/glossary.js +42 -0
  222. package/dist/src/tui/glossary.js.map +1 -0
  223. package/dist/src/tui/index.d.ts +11 -4
  224. package/dist/src/tui/index.js +8 -2
  225. package/dist/src/tui/index.js.map +1 -1
  226. package/dist/src/tui/input-display.d.ts +8 -0
  227. package/dist/src/tui/input-display.js +36 -0
  228. package/dist/src/tui/input-display.js.map +1 -0
  229. package/dist/src/tui/keys.d.ts +44 -0
  230. package/dist/src/tui/keys.js +43 -15
  231. package/dist/src/tui/keys.js.map +1 -1
  232. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  233. package/dist/src/tui/lifecycle-action-view.js +36 -0
  234. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  235. package/dist/src/tui/model.d.ts +235 -27
  236. package/dist/src/tui/model.js +1652 -304
  237. package/dist/src/tui/model.js.map +1 -1
  238. package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
  239. package/dist/src/tui/operation-diagnostic-screens.js +102 -0
  240. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  241. package/dist/src/tui/operation-history.d.ts +81 -0
  242. package/dist/src/tui/operation-history.js +135 -0
  243. package/dist/src/tui/operation-history.js.map +1 -0
  244. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  245. package/dist/src/tui/operation-review-planner.js +1254 -0
  246. package/dist/src/tui/operation-review-planner.js.map +1 -0
  247. package/dist/src/tui/plugin-content.d.ts +23 -0
  248. package/dist/src/tui/plugin-content.js +336 -0
  249. package/dist/src/tui/plugin-content.js.map +1 -0
  250. package/dist/src/tui/plugin-write-task.d.ts +69 -0
  251. package/dist/src/tui/plugin-write-task.js +286 -0
  252. package/dist/src/tui/plugin-write-task.js.map +1 -0
  253. package/dist/src/tui/prompt.js +4 -15
  254. package/dist/src/tui/prompt.js.map +1 -1
  255. package/dist/src/tui/router.d.ts +183 -11
  256. package/dist/src/tui/router.js +352 -29
  257. package/dist/src/tui/router.js.map +1 -1
  258. package/dist/src/tui/screen-registry.d.ts +702 -0
  259. package/dist/src/tui/screen-registry.js +231 -0
  260. package/dist/src/tui/screen-registry.js.map +1 -0
  261. package/dist/src/tui/session-console-app.d.ts +6 -0
  262. package/dist/src/tui/session-console-app.js +13 -0
  263. package/dist/src/tui/session-console-app.js.map +1 -0
  264. package/dist/src/tui/session-host.d.ts +45 -0
  265. package/dist/src/tui/session-host.js +209 -0
  266. package/dist/src/tui/session-host.js.map +1 -0
  267. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  268. package/dist/src/tui/source-ref-presets.js +11 -0
  269. package/dist/src/tui/source-ref-presets.js.map +1 -0
  270. package/dist/src/tui/theme.d.ts +18 -10
  271. package/dist/src/tui/theme.js +18 -10
  272. package/dist/src/tui/theme.js.map +1 -1
  273. package/dist/src/tui/viewport.d.ts +20 -0
  274. package/dist/src/tui/viewport.js +29 -0
  275. package/dist/src/tui/viewport.js.map +1 -0
  276. package/dist/src/tui/views.js +31 -3
  277. package/dist/src/tui/views.js.map +1 -1
  278. package/dist/src/tui/wizard.js +11 -9
  279. package/dist/src/tui/wizard.js.map +1 -1
  280. package/dist/src/types.d.ts +242 -20
  281. package/dist/src/types.js +1 -0
  282. package/dist/src/types.js.map +1 -1
  283. package/dist/src/utils/agent-version.d.ts +7 -1
  284. package/dist/src/utils/agent-version.js +41 -8
  285. package/dist/src/utils/agent-version.js.map +1 -1
  286. package/dist/src/utils/artifact-plan.d.ts +5 -4
  287. package/dist/src/utils/artifact-plan.js +18 -7
  288. package/dist/src/utils/artifact-plan.js.map +1 -1
  289. package/dist/src/utils/artifact-renderer.d.ts +13 -7
  290. package/dist/src/utils/artifact-renderer.js +196 -219
  291. package/dist/src/utils/artifact-renderer.js.map +1 -1
  292. package/dist/src/utils/artifact-request.d.ts +21 -0
  293. package/dist/src/utils/artifact-request.js +82 -0
  294. package/dist/src/utils/artifact-request.js.map +1 -0
  295. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  296. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  297. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  298. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  299. package/dist/src/utils/bundle-integrity.js +0 -25
  300. package/dist/src/utils/bundle-integrity.js.map +1 -1
  301. package/dist/src/utils/bundle-layout.js +14 -29
  302. package/dist/src/utils/bundle-layout.js.map +1 -1
  303. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  304. package/dist/src/utils/catalog-capability-projection.js +14 -0
  305. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  306. package/dist/src/utils/catalog-digests.d.ts +10 -0
  307. package/dist/src/utils/catalog-digests.js +50 -2
  308. package/dist/src/utils/catalog-digests.js.map +1 -1
  309. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  310. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  311. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  312. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  313. package/dist/src/utils/catalog-generation-error.js +30 -0
  314. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  315. package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
  316. package/dist/src/utils/catalog-generation-resolver.js +273 -0
  317. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  318. package/dist/src/utils/catalog-generation-service.d.ts +172 -0
  319. package/dist/src/utils/catalog-generation-service.js +1271 -0
  320. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  321. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  322. package/dist/src/utils/catalog-generation-state.js +192 -0
  323. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  324. package/dist/src/utils/catalog-generation-store.d.ts +58 -0
  325. package/dist/src/utils/catalog-generation-store.js +452 -0
  326. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  327. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  328. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
  329. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  330. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  331. package/dist/src/utils/catalog-operation-context.js +142 -0
  332. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  333. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  334. package/dist/src/utils/catalog-public-error.js +28 -8
  335. package/dist/src/utils/catalog-public-error.js.map +1 -1
  336. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -7
  337. package/dist/src/utils/catalog-runtime-projection.js +7 -102
  338. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  339. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  340. package/dist/src/utils/catalog-snapshot.js +11 -3
  341. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  342. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  343. package/dist/src/utils/catalog-source-identity.js +21 -2
  344. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  345. package/dist/src/utils/catalog-source-impact.d.ts +21 -0
  346. package/dist/src/utils/catalog-source-impact.js +344 -0
  347. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  348. package/dist/src/utils/catalog-source-references.d.ts +3 -2
  349. package/dist/src/utils/catalog-source-references.js +195 -347
  350. package/dist/src/utils/catalog-source-references.js.map +1 -1
  351. package/dist/src/utils/catalog-sources.d.ts +15 -1
  352. package/dist/src/utils/catalog-sources.js +206 -27
  353. package/dist/src/utils/catalog-sources.js.map +1 -1
  354. package/dist/src/utils/codex-config.d.ts +2 -0
  355. package/dist/src/utils/codex-config.js +26 -5
  356. package/dist/src/utils/codex-config.js.map +1 -1
  357. package/dist/src/utils/codex-manager.d.ts +79 -0
  358. package/dist/src/utils/codex-manager.js +745 -0
  359. package/dist/src/utils/codex-manager.js.map +1 -0
  360. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  361. package/dist/src/utils/codex-marketplace-path.js +45 -0
  362. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  363. package/dist/src/utils/codex-native-observation.d.ts +49 -0
  364. package/dist/src/utils/codex-native-observation.js +154 -0
  365. package/dist/src/utils/codex-native-observation.js.map +1 -0
  366. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  367. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  368. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  369. package/dist/src/utils/codex-reconciler.d.ts +126 -0
  370. package/dist/src/utils/codex-reconciler.js +298 -0
  371. package/dist/src/utils/codex-reconciler.js.map +1 -0
  372. package/dist/src/utils/codex-shared-native.d.ts +43 -0
  373. package/dist/src/utils/codex-shared-native.js +81 -0
  374. package/dist/src/utils/codex-shared-native.js.map +1 -0
  375. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  376. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  377. package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
  378. package/dist/src/utils/codex-surface-analysis.js +247 -0
  379. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  380. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  381. package/dist/src/utils/dynamic-skill-tree.js +36 -6
  382. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  383. package/dist/src/utils/git-catalog-provider.d.ts +18 -0
  384. package/dist/src/utils/git-catalog-provider.js +170 -144
  385. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  386. package/dist/src/utils/local-marketplace.d.ts +15 -1
  387. package/dist/src/utils/local-marketplace.js +307 -84
  388. package/dist/src/utils/local-marketplace.js.map +1 -1
  389. package/dist/src/utils/manifest.d.ts +1 -4
  390. package/dist/src/utils/manifest.js +1 -4
  391. package/dist/src/utils/manifest.js.map +1 -1
  392. package/dist/src/utils/multi-source-catalog.d.ts +3 -9
  393. package/dist/src/utils/multi-source-catalog.js +54 -110
  394. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  395. package/dist/src/utils/native-cli.d.ts +55 -7
  396. package/dist/src/utils/native-cli.js +386 -57
  397. package/dist/src/utils/native-cli.js.map +1 -1
  398. package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
  399. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  400. package/dist/src/utils/ownership.d.ts +84 -20
  401. package/dist/src/utils/ownership.js +179 -222
  402. package/dist/src/utils/ownership.js.map +1 -1
  403. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  404. package/dist/src/utils/path-catalog-provider.js +54 -65
  405. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  406. package/dist/src/utils/paths.d.ts +0 -1
  407. package/dist/src/utils/paths.js +0 -1
  408. package/dist/src/utils/paths.js.map +1 -1
  409. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  410. package/dist/src/utils/plugin-config-impact.js +298 -0
  411. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  412. package/dist/src/utils/plugin-config-write.d.ts +52 -27
  413. package/dist/src/utils/plugin-config-write.js +990 -298
  414. package/dist/src/utils/plugin-config-write.js.map +1 -1
  415. package/dist/src/utils/plugin-config.d.ts +140 -93
  416. package/dist/src/utils/plugin-config.js +992 -980
  417. package/dist/src/utils/plugin-config.js.map +1 -1
  418. package/dist/src/utils/plugin-intent-state.d.ts +49 -0
  419. package/dist/src/utils/plugin-intent-state.js +320 -0
  420. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  421. package/dist/src/utils/plugin-scripts.d.ts +62 -125
  422. package/dist/src/utils/plugin-scripts.js +468 -525
  423. package/dist/src/utils/plugin-scripts.js.map +1 -1
  424. package/dist/src/utils/process-tree.d.ts +2 -2
  425. package/dist/src/utils/process-tree.js +1 -1
  426. package/dist/src/utils/process-tree.js.map +1 -1
  427. package/dist/src/utils/project-boundary.d.ts +11 -13
  428. package/dist/src/utils/project-boundary.js +28 -34
  429. package/dist/src/utils/project-boundary.js.map +1 -1
  430. package/dist/src/utils/safe-fs.d.ts +21 -0
  431. package/dist/src/utils/safe-fs.js +111 -0
  432. package/dist/src/utils/safe-fs.js.map +1 -0
  433. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  434. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  435. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  436. package/dist/src/utils/sensitive-content.d.ts +4 -0
  437. package/dist/src/utils/sensitive-content.js +36 -0
  438. package/dist/src/utils/sensitive-content.js.map +1 -0
  439. package/dist/src/utils/skill-validation.d.ts +1 -1
  440. package/dist/src/utils/skill-validation.js +1 -1
  441. package/dist/src/utils/skill-validation.js.map +1 -1
  442. package/dist/src/utils/state-paths.d.ts +1 -0
  443. package/dist/src/utils/state-paths.js +12 -2
  444. package/dist/src/utils/state-paths.js.map +1 -1
  445. package/dist/src/utils/target-eligibility.d.ts +8 -2
  446. package/dist/src/utils/target-eligibility.js +17 -8
  447. package/dist/src/utils/target-eligibility.js.map +1 -1
  448. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  449. package/dist/src/utils/windows-claude-shim.js +141 -0
  450. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  451. package/dist/vitest.config.js +91 -10
  452. package/dist/vitest.config.js.map +1 -1
  453. package/package.json +22 -19
  454. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  455. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  456. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  457. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  458. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  459. package/dist/scripts/codex-eval-app-server.d.ts +0 -89
  460. package/dist/scripts/codex-eval-app-server.js +0 -1228
  461. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  462. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  463. package/dist/scripts/codex-eval-isolation.js +0 -318
  464. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  465. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  466. package/dist/scripts/codex-eval-process-management.js +0 -69
  467. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  468. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  469. package/dist/scripts/codex-eval-process-tree.js +0 -803
  470. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  471. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  472. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  473. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  474. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  475. package/dist/scripts/codex-trigger-eval.js +0 -968
  476. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  477. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  478. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  479. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  480. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  481. package/dist/scripts/eval-plugin-skills.js +0 -1586
  482. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  483. package/dist/scripts/eval-run-layout.d.ts +0 -17
  484. package/dist/scripts/eval-run-layout.js +0 -26
  485. package/dist/scripts/eval-run-layout.js.map +0 -1
  486. package/dist/scripts/plugin-eval-contract.d.ts +0 -185
  487. package/dist/scripts/plugin-eval-contract.js +0 -695
  488. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  489. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  490. package/dist/scripts/plugin-eval-materializer.js +0 -421
  491. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  492. package/dist/scripts/skill-reporting.d.ts +0 -212
  493. package/dist/scripts/skill-reporting.js +0 -922
  494. package/dist/scripts/skill-reporting.js.map +0 -1
  495. package/dist/src/commands/status-v4.d.ts +0 -102
  496. package/dist/src/commands/status-v4.js +0 -844
  497. package/dist/src/commands/status-v4.js.map +0 -1
  498. package/dist/src/commands/status.d.ts +0 -93
  499. package/dist/src/commands/status.js +0 -442
  500. package/dist/src/commands/status.js.map +0 -1
  501. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  502. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  503. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  504. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  505. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  506. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  507. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
  508. package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
  509. package/dist/src/utils/catalog-source-cache.d.ts +0 -61
  510. package/dist/src/utils/catalog-source-cache.js +0 -276
  511. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  512. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  513. package/dist/src/utils/codex-project-surface.js +0 -49
  514. package/dist/src/utils/codex-project-surface.js.map +0 -1
  515. package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
  516. package/dist/src/utils/lifecycle-active-store.js +0 -1276
  517. package/dist/src/utils/lifecycle-active-store.js.map +0 -1
  518. package/dist/src/utils/plugin-authoring-capabilities.d.ts +0 -23
  519. package/dist/src/utils/plugin-authoring-capabilities.js +0 -37
  520. package/dist/src/utils/plugin-authoring-capabilities.js.map +0 -1
  521. package/dist/src/utils/plugin-authoring-capabilities.json +0 -92
  522. package/dist/src/utils/plugin-release.d.ts +0 -2
  523. package/dist/src/utils/plugin-release.js +0 -2
  524. package/dist/src/utils/plugin-release.js.map +0 -1
  525. package/dist/src/utils/skill-frontmatter-capabilities.d.ts +0 -201
  526. package/dist/src/utils/skill-frontmatter-capabilities.js +0 -5
  527. package/dist/src/utils/skill-frontmatter-capabilities.js.map +0 -1
  528. package/dist/src/utils/skill-frontmatter-capabilities.json +0 -200
  529. /package/dist/authoring/scripts/{check-plugin-versions.d.mts → remove-plugin.d.mts} +0 -0
@@ -1,101 +1,840 @@
1
+ import { matchPluginConfigPreset, stablePluginConfigStringify, } from '@lesliechan721/aw-plugin-core/manifest';
2
+ import { flattenStatusTargets, statusTargetActivation, statusTargetExtensions, statusTargetIntent, statusTargetOwnershipState, statusTargetSharedNative, } from '../commands/status-v6.js';
3
+ import { buildAllowedLifecycleActions } from '../commands/operation-planner.js';
4
+ import { safeDiagnosticCode, } from './diagnostics.js';
5
+ import { projectCatalogSourceState } from './catalog-sources/projection.js';
6
+ import { projectLifecycleActionView } from './lifecycle-action-view.js';
7
+ import { compareStrictSemVer, tryParseStrictSemVer } from '../utils/manifest.js';
8
+ import { formatPluginId } from '../utils/multi-source-catalog.js';
9
+ import { createCatalogStoreKey } from '../utils/catalog-generation-store.js';
10
+ function candidateForPlugin(pluginId, sourceId, freshness) {
11
+ return freshness?.candidatePlugins?.find((entry) => formatPluginId({
12
+ sourceId,
13
+ pluginName: entry.name,
14
+ }) === pluginId);
15
+ }
16
+ export function buildPluginUpdateSummary(options) {
17
+ const candidate = candidateForPlugin(options.pluginId, options.sourceId, options.freshness);
18
+ let sameVersionCandidateChange = false;
19
+ let blockedCandidate = Boolean(options.freshness?.candidate
20
+ && options.freshness.candidatePlugins && !candidate);
21
+ const targets = options.targetRevisions
22
+ .filter((entry) => entry.pluginId === options.pluginId)
23
+ .map((entry) => {
24
+ const installedVersion = entry.revision.installedVersion;
25
+ const activeVersion = entry.revision.availableVersion;
26
+ const liveTarget = options.targets.find((target) => target.pluginId === entry.pluginId
27
+ && target.platform === entry.platform && target.scope === entry.scope);
28
+ const needsRepair = liveTarget !== undefined && liveTarget.state !== 'ready'
29
+ && !isDirectCodexVersionUpgrade(liveTarget, entry.revision);
30
+ const installedValid = installedVersion !== undefined && tryParseStrictSemVer(installedVersion) !== null;
31
+ const activeValid = activeVersion !== undefined && tryParseStrictSemVer(activeVersion) !== null;
32
+ const candidateValid = candidate !== undefined && tryParseStrictSemVer(candidate.version) !== null;
33
+ const candidateCompatible = (candidate?.missingCapabilities.length ?? 0) === 0;
34
+ let targetCandidateBlocked = false;
35
+ const candidateEvidence = candidate && options.freshness?.candidate
36
+ ? createCatalogStoreKey(options.freshness.candidate.logicalIdentity, options.freshness.candidate.snapshotDigest)
37
+ : undefined;
38
+ if (!installedValid) {
39
+ return {
40
+ platform: entry.platform,
41
+ scope: entry.scope,
42
+ ...(installedVersion ? { installedVersion } : {}),
43
+ ...(activeVersion ? { activeVersion } : {}),
44
+ ...(candidate ? { candidateVersion: candidate.version } : {}),
45
+ ...(candidateEvidence ? { candidateEvidence } : {}),
46
+ eligibility: 'unverified',
47
+ };
48
+ }
49
+ if (entry.revision.state === 'upgradeAvailable' && activeValid) {
50
+ return {
51
+ platform: entry.platform,
52
+ scope: entry.scope,
53
+ installedVersion,
54
+ activeVersion,
55
+ ...(candidate ? { candidateVersion: candidate.version } : {}),
56
+ availableVersion: activeVersion,
57
+ ...(candidateEvidence ? { candidateEvidence } : {}),
58
+ eligibility: needsRepair ? 'needsRepair' : 'allowed',
59
+ };
60
+ }
61
+ if (candidateValid) {
62
+ const candidateRelation = compareStrictSemVer(installedVersion, candidate.version);
63
+ const candidateUsable = candidateCompatible
64
+ && options.freshness?.state !== 'incompatible'
65
+ && options.freshness?.state !== 'rollbackDetected'
66
+ && options.freshness?.state !== 'versionConflict';
67
+ if (candidateRelation < 0 && candidateUsable) {
68
+ return {
69
+ platform: entry.platform,
70
+ scope: entry.scope,
71
+ installedVersion,
72
+ ...(activeVersion ? { activeVersion } : {}),
73
+ candidateVersion: candidate.version,
74
+ availableVersion: candidate.version,
75
+ ...(candidateEvidence ? { candidateEvidence } : {}),
76
+ ...(candidate.missingCapabilities.length > 0
77
+ ? { missingCapabilities: [...candidate.missingCapabilities] }
78
+ : {}),
79
+ eligibility: needsRepair ? 'needsRepair' : 'allowed',
80
+ requiresCatalogActivation: true,
81
+ };
82
+ }
83
+ if (candidateRelation < 0 || candidateRelation > 0) {
84
+ blockedCandidate = true;
85
+ targetCandidateBlocked = true;
86
+ }
87
+ if (candidateRelation === 0 && activeVersion === candidate.version
88
+ && options.freshness?.state !== 'current')
89
+ sameVersionCandidateChange = true;
90
+ }
91
+ const activeDowngrade = activeValid
92
+ && compareStrictSemVer(installedVersion, activeVersion) > 0;
93
+ return {
94
+ platform: entry.platform,
95
+ scope: entry.scope,
96
+ installedVersion,
97
+ ...(activeVersion ? { activeVersion } : {}),
98
+ ...(candidate ? { candidateVersion: candidate.version } : {}),
99
+ ...(candidateEvidence ? { candidateEvidence } : {}),
100
+ ...(candidate && candidate.missingCapabilities.length > 0
101
+ ? { missingCapabilities: [...candidate.missingCapabilities] }
102
+ : {}),
103
+ eligibility: activeDowngrade || targetCandidateBlocked ? 'incompatible'
104
+ : needsRepair ? 'needsRepair' : activeValid ? 'allowed' : 'unverified',
105
+ };
106
+ })
107
+ .sort((left, right) => `${left.platform}\0${left.scope}`.localeCompare(`${right.platform}\0${right.scope}`, 'en'));
108
+ const hasAvailable = targets.some((target) => target.availableVersion !== undefined);
109
+ const hasInstalledVersion = targets.some((target) => target.installedVersion !== undefined);
110
+ const allCurrent = targets.length > 0 && targets.every((target) => target.activeVersion !== undefined
111
+ && target.installedVersion !== undefined
112
+ && tryParseStrictSemVer(target.activeVersion) !== null
113
+ && tryParseStrictSemVer(target.installedVersion) !== null
114
+ && compareStrictSemVer(target.installedVersion, target.activeVersion) === 0);
115
+ return {
116
+ pluginId: options.pluginId,
117
+ state: hasAvailable ? 'available'
118
+ : sameVersionCandidateChange ? 'sourceChangedUnverified'
119
+ : blockedCandidate || targets.some((target) => target.eligibility === 'incompatible') ? 'blocked'
120
+ : allCurrent ? 'currentAgainstCatalog'
121
+ : hasInstalledVersion ? 'unknown' : 'notChecked',
122
+ ...(options.freshness?.checkedAt ? { checkedAt: options.freshness.checkedAt } : {}),
123
+ ...(options.freshness?.lastSuccessful ? { lastSuccessful: true } : {}),
124
+ ...(options.sourceIdentity ? { sourceIdentity: options.sourceIdentity } : {}),
125
+ ...(options.generationKey ? { generationKey: options.generationKey } : {}),
126
+ targets,
127
+ };
128
+ }
129
+ export function configObjectHasSensitiveChild(field) {
130
+ if (field.type !== 'object')
131
+ return false;
132
+ return Object.values(field.properties ?? {}).some((property) => property.sensitive === true)
133
+ || (field.additionalProperties !== false && field.additionalProperties?.sensitive === true);
134
+ }
1
135
  function compareText(left, right) {
2
136
  return left.localeCompare(right, 'en');
3
137
  }
4
138
  function targetHealth(target) {
5
- if (target.state === 'present')
139
+ if (target.state === 'ready')
6
140
  return 'ready';
7
- if (target.state === 'partial')
141
+ if (target.state === 'needsSync' || target.state === 'orphaned')
8
142
  return 'incomplete';
9
- return 'error';
143
+ return target.state === 'blocked' ? 'error' : 'incomplete';
144
+ }
145
+ export function isDirectCodexVersionUpgrade(target, revision) {
146
+ const sharedNative = statusTargetSharedNative(target);
147
+ const reasons = [...target.reasons, ...(target.sharedReasons ?? [])];
148
+ return target.platform === 'codex'
149
+ && target.state === 'needsSync'
150
+ && revision.state === 'upgradeAvailable'
151
+ && sharedNative?.reason === 'outdated'
152
+ && typeof sharedNative.version === 'string'
153
+ && typeof sharedNative.desiredVersion === 'string'
154
+ && sharedNative.version === revision.installedVersion
155
+ && sharedNative.desiredVersion === revision.availableVersion
156
+ && compareStrictSemVer(sharedNative.version, sharedNative.desiredVersion) < 0
157
+ && reasons.length > 0
158
+ && reasons.every((reason) => reason.code === 'codexSharedNativeUpdateRequired');
159
+ }
160
+ export function isPureNativeCodexTarget(target) {
161
+ const sharedNative = statusTargetSharedNative(target);
162
+ const extensions = statusTargetExtensions(target);
163
+ return target.platform === 'codex'
164
+ && sharedNative !== undefined
165
+ && (extensions?.kinds.length ?? 0) === 0;
166
+ }
167
+ function isPureNativeCodexPlacement(placement) {
168
+ return placement.platform === 'codex'
169
+ && placement.state === 'installed'
170
+ && placement.sharedNative !== undefined
171
+ && (placement.extensions?.kinds.length ?? 0) === 0;
172
+ }
173
+ function parsedScopedUninstallRecovery(recovery, expectedPlatform) {
174
+ if (recovery.command !== 'uninstall' || !recovery.scope)
175
+ return undefined;
176
+ const match = /(?:^|\s)aw uninstall ([^\s]+) --platform (codex|claude) --scope (user|project)(?:,|,|\s|$)/u
177
+ .exec(recovery.description);
178
+ if (!match || match[2] !== expectedPlatform || match[3] !== recovery.scope)
179
+ return undefined;
180
+ return { pluginId: match[1], scope: recovery.scope, description: recovery.description };
181
+ }
182
+ function scopeConflictRecoveryMenuActions(placement) {
183
+ if (placement.actualState !== 'blocked')
184
+ return [];
185
+ return (placement.recovery ?? []).flatMap((recovery) => {
186
+ const parsed = parsedScopedUninstallRecovery(recovery, placement.platform);
187
+ return parsed ? [{
188
+ id: 'resolveScopeConflict',
189
+ pluginId: parsed.pluginId,
190
+ platform: placement.platform,
191
+ scope: parsed.scope,
192
+ keepScope: parsed.scope === 'user' ? 'project' : 'user',
193
+ description: parsed.description,
194
+ }] : [];
195
+ });
196
+ }
197
+ function missingPureNativeScopes(plugin, placement) {
198
+ if (!isPureNativeCodexPlacement(placement) || placement.actualState !== 'ready')
199
+ return [];
200
+ const supported = plugin.supportedScopes?.codex ?? ['user', 'project'];
201
+ return supported.filter((scope) => !placement.desiredScopes?.includes(scope) && placement.activation?.[scope] !== true);
202
+ }
203
+ function hasScopedExtensionOwnership(target) {
204
+ return target.components.some((component) => component.kind === 'ownership'
205
+ && component.key !== 'native'
206
+ && !component.key.startsWith('activation-')
207
+ && !component.key.startsWith('marketplace-')
208
+ && (component.state === 'present' || component.state === 'partial'));
10
209
  }
11
210
  function isInstalledScopeEvidence(target) {
12
- return target.state === 'present' || target.state === 'partial';
211
+ if (target.state === 'absent')
212
+ return false;
213
+ const activation = statusTargetActivation(target);
214
+ const extensions = statusTargetExtensions(target);
215
+ return statusTargetIntent(target).enabled
216
+ || target.state === 'needsSync' && target.reasons.some((reason) => reason.code === 'codexActivationDrift')
217
+ || target.platform === 'claude' && (statusTargetOwnershipState(target) === 'present'
218
+ || statusTargetOwnershipState(target) === 'partial')
219
+ || target.platform === 'codex' && hasScopedExtensionOwnership(target)
220
+ || activation?.[target.scope] === true
221
+ || extensions?.state === 'ready'
222
+ || extensions?.state === 'drifted'
223
+ || extensions?.state === 'conflict';
224
+ }
225
+ export function hasActualScopeEvidence(target) {
226
+ if (target.state === 'absent')
227
+ return false;
228
+ const activation = statusTargetActivation(target);
229
+ const extensions = statusTargetExtensions(target);
230
+ return target.platform === 'claude' && (statusTargetOwnershipState(target) === 'present'
231
+ || statusTargetOwnershipState(target) === 'partial')
232
+ || target.platform === 'codex' && hasScopedExtensionOwnership(target)
233
+ || activation?.[target.scope] === true
234
+ || extensions?.state === 'ready'
235
+ || extensions?.state === 'drifted'
236
+ || extensions?.state === 'conflict';
237
+ }
238
+ function dependencyAssignmentExplanation(assignmentSource, requiredBy) {
239
+ const consumer = requiredBy[0];
240
+ return {
241
+ root: '当前插件位置',
242
+ default: '推荐使用当前项目',
243
+ existingWiderTarget: '复用健康的所有项目安装',
244
+ userOverride: '你选择的位置',
245
+ consumerPromotion: consumer
246
+ ? `因 ${consumer} 的位置而一并调整`
247
+ : '因上游依赖位置而一并调整',
248
+ platformRequirement: '平台只支持这个位置',
249
+ }[assignmentSource];
250
+ }
251
+ export function dependencyPlacementTargets(plan) {
252
+ return plan.targets.filter((target) => target.relation === 'dependency').map((target) => ({
253
+ key: `${target.pluginId}\0${target.platform}`,
254
+ pluginId: target.pluginId,
255
+ displayName: target.pluginName,
256
+ platform: target.platform,
257
+ relation: target.depth === 1 ? 'direct' : 'transitive',
258
+ scope: target.resolvedScope,
259
+ minimumScope: target.minimumScope,
260
+ allowedScopes: [...target.allowedScopes],
261
+ locked: target.locked && target.lockReason !== 'dependencyScopeOverride',
262
+ requiredBy: [...target.requiredBy],
263
+ assignmentSource: target.assignmentSource,
264
+ explanation: dependencyAssignmentExplanation(target.assignmentSource, target.requiredBy),
265
+ affectsOtherProjects: target.affectsOtherProjects,
266
+ action: target.action,
267
+ }));
13
268
  }
14
269
  function dependencyPluginIdFromMessage(message) {
15
270
  return message.match(/(?:[a-z0-9]+(?:-[a-z0-9]+)*)\/(?:[a-z0-9]+(?:-[a-z0-9]+)*)/u)?.[0];
16
271
  }
17
- export function projectPlatformPlacement(platform, targets) {
272
+ function rootPlacementFromIssueId(issueId, fallback) {
273
+ if (issueId) {
274
+ for (const platform of ['claude', 'codex']) {
275
+ for (const scope of ['project', 'user']) {
276
+ if (issueId.includes(`:${platform}:${scope}:`)
277
+ || issueId.endsWith(`:${platform}:${scope}`))
278
+ return { platform, scope };
279
+ }
280
+ }
281
+ }
282
+ return fallback;
283
+ }
284
+ function rootPluginIdFromIssueId(issueId, rootIssueCode) {
285
+ if (!issueId || !rootIssueCode || !issueId.startsWith(`${rootIssueCode}:`))
286
+ return undefined;
287
+ for (const platform of ['claude', 'codex']) {
288
+ for (const scope of ['project', 'user']) {
289
+ const marker = `:${platform}:${scope}`;
290
+ const markerIndex = issueId.indexOf(marker, rootIssueCode.length + 1);
291
+ if (markerIndex > rootIssueCode.length + 1) {
292
+ return issueId.slice(rootIssueCode.length + 1, markerIndex);
293
+ }
294
+ }
295
+ }
296
+ return undefined;
297
+ }
298
+ function targetIssueRelationship(options) {
299
+ const consumer = options.error?.rootIssueId !== undefined
300
+ && options.error.rootIssueCode !== undefined
301
+ && (options.error.code !== options.error.rootIssueCode
302
+ || options.dependencyPluginId !== undefined
303
+ && options.dependencyPluginId !== options.target.pluginId);
304
+ if (!consumer) {
305
+ return {
306
+ relationship: 'direct',
307
+ rootEntity: {
308
+ pluginId: options.target.pluginId,
309
+ platform: options.target.platform,
310
+ scope: options.target.scope,
311
+ },
312
+ };
313
+ }
314
+ const placement = rootPlacementFromIssueId(options.error?.rootIssueId, options.target);
315
+ return {
316
+ relationship: 'consumer',
317
+ rootEntity: {
318
+ pluginId: rootPluginIdFromIssueId(options.error?.rootIssueId, options.error?.rootIssueCode) ?? options.dependencyPluginId ?? options.target.pluginId,
319
+ ...placement,
320
+ },
321
+ };
322
+ }
323
+ export function projectPlatformPlacement(platform, targets, revisions = []) {
324
+ const revisionFor = (target) => revisions.find((entry) => entry.pluginId === target.pluginId
325
+ && entry.platform === target.platform
326
+ && entry.scope === target.scope)?.revision
327
+ ?? { state: 'unknown', ...(target.version ? { availableVersion: target.version } : {}) };
18
328
  const supportedScopes = [...new Set(targets.map((target) => target.scope))];
19
329
  const installed = targets.filter(isInstalledScopeEvidence);
20
330
  if (installed.length === 0) {
21
- const errorTarget = targets.find((target) => target.state === 'error');
331
+ if (targets.some((target) => target.reasons.some((reason) => reason.code === 'artifactCollision' && reason.severity === 'blocking'))) {
332
+ return { platform, state: 'blocked', supportedScopes, reason: 'artifactCollision' };
333
+ }
334
+ const errorTarget = targets.find((target) => target.state === 'blocked');
22
335
  if (!errorTarget)
23
336
  return { platform, state: 'notInstalled', supportedScopes };
24
- return { platform, state: 'installed', scope: errorTarget.scope, health: 'error' };
337
+ return {
338
+ platform,
339
+ state: 'installed',
340
+ scope: errorTarget.scope,
341
+ health: 'error',
342
+ revision: revisionFor(errorTarget),
343
+ };
25
344
  }
26
- if (installed.length === 1) {
27
- const target = installed[0];
28
- return { platform, state: 'installed', scope: target.scope, health: targetHealth(target) };
345
+ if (installed.length > 1) {
346
+ const projected = installed.map((target) => projectPlatformPlacement(platform, [target], revisions));
347
+ return {
348
+ ...(projected.find((placement) => placement.health === 'error')
349
+ ?? projected.find((placement) => placement.health === 'incomplete')
350
+ ?? projected[0]),
351
+ scopedPlacements: projected.map((placement) => ({
352
+ scope: placement.scope,
353
+ health: placement.health,
354
+ revision: placement.revision,
355
+ ...(placement.actualState ? { actualState: placement.actualState } : {}),
356
+ ...(placement.recovery ? { recovery: placement.recovery } : {}),
357
+ ...(placement.blockingReason ? { blockingReason: placement.blockingReason } : {}),
358
+ })),
359
+ desiredScopes: [...new Set(projected.flatMap((placement) => placement.desiredScopes ?? []))].sort(compareText),
360
+ actualScopes: [...new Set(installed.filter(hasActualScopeEvidence)
361
+ .map((target) => target.scope))].sort(compareText),
362
+ };
29
363
  }
364
+ const target = installed.find((candidate) => candidate.state === 'blocked')
365
+ ?? installed.find((candidate) => candidate.state === 'needsSync')
366
+ ?? installed.find((candidate) => candidate.state === 'orphaned')
367
+ ?? installed[0];
368
+ const intent = statusTargetIntent(target);
369
+ const activation = statusTargetActivation(target);
370
+ const sharedNative = statusTargetSharedNative(target);
371
+ const extensions = statusTargetExtensions(target);
372
+ const blockingReason = target.reasons.some((reason) => reason.code === 'artifactCollision' && reason.severity === 'blocking')
373
+ ? 'artifactCollision'
374
+ : undefined;
30
375
  return {
31
376
  platform,
32
- state: 'scopeConflict',
33
- placements: installed.map((target) => ({
34
- scope: target.scope,
35
- health: targetHealth(target),
36
- })).sort((left, right) => compareText(left.scope, right.scope)),
377
+ state: 'installed',
378
+ scope: target.scope,
379
+ health: targetHealth(target),
380
+ revision: revisionFor(target),
381
+ desiredScopes: [...intent.scopes],
382
+ actualScopes: hasActualScopeEvidence(target) ? [target.scope] : [],
383
+ relation: intent.relation,
384
+ actualState: target.state,
385
+ ...(activation ? { activation } : {}),
386
+ ...(sharedNative ? { sharedNative } : {}),
387
+ ...(extensions ? { extensions } : {}),
388
+ recovery: [...target.recoveryActions],
389
+ ...(blockingReason ? { blockingReason } : {}),
37
390
  };
38
391
  }
392
+ function hasIncompletePlacement(plugin) {
393
+ return plugin.installedPlacements.some((placement) => placement.state === 'scopeConflict'
394
+ || placement.state === 'installed'
395
+ && placement.scopedPlacements?.some((entry) => entry.health !== 'ready'
396
+ || entry.blockingReason !== undefined)
397
+ || placement.state === 'installed'
398
+ && (placement.health !== 'ready' || placement.blockingReason !== undefined));
399
+ }
400
+ function hasMissingPlatform(plugin) {
401
+ return plugin.supportedPlatforms.some((platform) => plugin.installedPlacements.find((placement) => placement.platform === platform)?.state === 'notInstalled');
402
+ }
403
+ function allSupportedPlatformsBlocked(plugin) {
404
+ return plugin.supportedPlatforms.length > 0 && plugin.supportedPlatforms.every((platform) => plugin.installedPlacements.some((placement) => placement.platform === platform
405
+ && (placement.state === 'blocked'
406
+ || placement.state === 'installed' && placement.blockingReason === 'artifactCollision')));
407
+ }
408
+ function capabilityActions(plugin) {
409
+ return [
410
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
411
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
412
+ ];
413
+ }
414
+ function capabilityUnavailableActions() {
415
+ return [{ id: 'upgradeAwCli' }, { id: 'capabilityTechnical' }];
416
+ }
417
+ function isCapabilityUnavailable(plugin) {
418
+ return plugin.capabilityUnsupported === true && plugin.sourceUnavailable !== true;
419
+ }
420
+ export function buildPluginPreviewActions(plugin) {
421
+ const installed = hasInstalledPlacement(plugin);
422
+ if (plugin.aggregateStatus === 'unavailable') {
423
+ if (isCapabilityUnavailable(plugin)) {
424
+ return [...capabilityUnavailableActions(), ...(installed ? [{ id: 'manage' }] : [])];
425
+ }
426
+ return [{ id: 'sourceProblem' }, ...(installed ? [{ id: 'manage' }] : [])];
427
+ }
428
+ if (allSupportedPlatformsBlocked(plugin)) {
429
+ return [
430
+ { id: 'outputCollision' },
431
+ ...(hasInstalledPlacement(plugin) ? [{ id: 'manage' }] : []),
432
+ { id: 'source' },
433
+ { id: 'about' },
434
+ ];
435
+ }
436
+ const collisionWithInstallablePlatform = hasMissingPlatform(plugin)
437
+ && plugin.installedPlacements.some((placement) => placement.state === 'blocked'
438
+ || placement.state === 'installed' && placement.blockingReason === 'artifactCollision');
439
+ const primary = collisionWithInstallablePlatform
440
+ ? { id: 'add' }
441
+ : hasIncompletePlacement(plugin) || (plugin.dependencySummary?.issueCount ?? 0) > 0
442
+ ? { id: 'repair' }
443
+ : installed && !hasMissingPlatform(plugin) ? { id: 'manage' } : { id: 'add' };
444
+ return [primary, ...capabilityActions(plugin), { id: 'source' }];
445
+ }
446
+ export function buildPluginDetailActions(plugin) {
447
+ if (plugin.aggregateStatus === 'unavailable') {
448
+ if (isCapabilityUnavailable(plugin)) {
449
+ return [
450
+ ...plugin.installedPlacements.flatMap((placement) => placement.state === 'notInstalled'
451
+ ? []
452
+ : [{ id: 'platform', platform: placement.platform }]),
453
+ ...capabilityUnavailableActions(),
454
+ { id: 'about' },
455
+ ];
456
+ }
457
+ return [
458
+ ...plugin.installedPlacements.flatMap((placement) => placement.state === 'notInstalled'
459
+ ? []
460
+ : [{ id: 'platform', platform: placement.platform }]),
461
+ ...(plugin.savedConfigScopes.length > 0 ? [{ id: 'settings' }] : []),
462
+ { id: 'sourceProblem' },
463
+ { id: 'about' },
464
+ ];
465
+ }
466
+ return [
467
+ ...(plugin.update?.targets ?? []).flatMap((target) => target.availableVersion !== undefined && target.installedVersion !== undefined
468
+ && plugin.update.sourceIdentity !== undefined && plugin.update.generationKey !== undefined
469
+ && target.requiresCatalogActivation !== true
470
+ && target.eligibility === 'allowed'
471
+ ? [{
472
+ id: 'refreshAndSync',
473
+ platform: target.platform,
474
+ scope: target.scope,
475
+ availableVersion: target.availableVersion,
476
+ installedVersion: target.installedVersion,
477
+ sourceIdentity: plugin.update.sourceIdentity,
478
+ generationKey: plugin.update.generationKey,
479
+ }]
480
+ : []),
481
+ ...plugin.installedPlacements.flatMap((placement) => [
482
+ ...(placement.state === 'installed' ? scopeConflictRecoveryMenuActions(placement) : []),
483
+ placement.state === 'scopeConflict'
484
+ ? { id: 'scopeConflict', platform: placement.platform }
485
+ : { id: 'platform', platform: placement.platform },
486
+ ]),
487
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
488
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
489
+ { id: 'about' },
490
+ ];
491
+ }
492
+ export function buildPlatformActions(plugin, platform) {
493
+ if (plugin.aggregateStatus === 'unavailable') {
494
+ if (isCapabilityUnavailable(plugin))
495
+ return capabilityUnavailableActions();
496
+ return [{ id: 'sourceProblem' }, { id: 'refreshSource' }];
497
+ }
498
+ const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
499
+ if (placement?.state === 'blocked') {
500
+ return [{ id: 'outputCollision' }, { id: 'source' }, { id: 'about' }];
501
+ }
502
+ if (!placement || placement.state === 'notInstalled')
503
+ return [{ id: 'add' }, { id: 'about' }];
504
+ if (placement.state === 'scopeConflict')
505
+ return [{ id: 'scopeConflict', platform }];
506
+ if (placement.scopedPlacements) {
507
+ const actions = placement.scopedPlacements.flatMap((entry) => buildPlatformActions({
508
+ ...plugin,
509
+ installedPlacements: [{
510
+ ...placement,
511
+ ...entry,
512
+ scopedPlacements: undefined,
513
+ }],
514
+ }, platform));
515
+ return [...new Map(actions.map((action) => [JSON.stringify(action), action])).values()];
516
+ }
517
+ const scopedRecovery = scopeConflictRecoveryMenuActions(placement);
518
+ if (scopedRecovery.length > 0)
519
+ return [...scopedRecovery, { id: 'technical' }];
520
+ if (placement.blockingReason === 'artifactCollision') {
521
+ return [
522
+ { id: 'outputCollision' },
523
+ { id: 'remove', platform, scope: placement.scope },
524
+ { id: 'technical' },
525
+ ];
526
+ }
527
+ const lifecycleActions = buildAllowedLifecycleActions({
528
+ pluginId: plugin.pluginId,
529
+ platform,
530
+ scope: placement.scope,
531
+ state: placement.actualState ?? (placement.health === 'ready'
532
+ ? 'ready' : placement.health === 'incomplete' ? 'needsSync' : 'blocked'),
533
+ recoveryActions: placement.recovery ?? [],
534
+ revision: placement.revision,
535
+ }).flatMap((action) => {
536
+ if (action.id === 'install')
537
+ return [{ id: 'add' }];
538
+ if (action.id === 'refreshAndSync') {
539
+ const updateTarget = plugin.update?.targets.find((target) => target.platform === action.platform && target.scope === action.scope
540
+ && target.availableVersion === action.availableVersion);
541
+ return updateTarget?.installedVersion && plugin.update.sourceIdentity && plugin.update.generationKey
542
+ ? [{
543
+ id: 'refreshAndSync',
544
+ platform: action.platform,
545
+ scope: action.scope,
546
+ availableVersion: action.availableVersion,
547
+ installedVersion: updateTarget.installedVersion,
548
+ sourceIdentity: plugin.update.sourceIdentity,
549
+ generationKey: plugin.update.generationKey,
550
+ }]
551
+ : [];
552
+ }
553
+ if (action.id === 'sync')
554
+ return [{
555
+ id: 'sync', platform: action.platform, scope: action.scope,
556
+ }];
557
+ if (action.id === 'reinstall')
558
+ return [{ id: 'reinstall', platform: action.platform, scope: action.scope }];
559
+ if (action.id === 'uninstall')
560
+ return [{ id: 'remove', platform: action.platform, scope: action.scope }];
561
+ return [{ id: 'cleanup', platform: action.platform, scope: action.scope }];
562
+ });
563
+ if (placement.health !== 'ready') {
564
+ return [...lifecycleActions, { id: 'technical' }, ...(lifecycleActions.length === 0 ? [{ id: 'back' }] : [])];
565
+ }
566
+ return [
567
+ ...missingPureNativeScopes(plugin, placement).map((scope) => ({
568
+ id: 'addScope', platform, scope,
569
+ })),
570
+ ...lifecycleActions,
571
+ { id: 'technical' },
572
+ ];
573
+ }
39
574
  export function hasInstalledPlacement(plugin) {
40
- return plugin.installedPlacements.some((placement) => placement.state !== 'notInstalled');
575
+ return plugin.installedPlacements.some((placement) => placement.state === 'installed' || placement.state === 'scopeConflict');
41
576
  }
42
- function sourceState(source, health) {
43
- if (health?.state === 'ready')
44
- return 'ready';
45
- if (health?.state === 'path')
46
- return 'path';
47
- if (health?.state === 'degraded')
48
- return 'cache';
49
- if (health?.state === 'unavailable')
50
- return 'error';
51
- if (source.error)
52
- return 'error';
53
- if (source.type === 'path' || source.freshness === 'path')
54
- return 'path';
55
- return source.freshness === 'cache' ? 'cache' : 'ready';
56
- }
57
- function sourceIssue(health, impactedTargets = 0) {
58
- if (health.state !== 'cache' && health.state !== 'degraded' && health.state !== 'unavailable')
577
+ function sourceIssue(health, targetIssues = []) {
578
+ if (health.state !== 'unavailable')
59
579
  return undefined;
60
- const cached = health.state === 'cache' || health.state === 'degraded';
61
- const issueId = health.issueId ?? `source:${health.sourceId}:${cached ? 'cache' : 'unavailable'}`;
580
+ const issueId = health.issueId ?? `source:${health.sourceId}:unavailable`;
581
+ const affectedTargets = [...new Map(targetIssues.flatMap((issue) => issue.affectedTargets ?? [])
582
+ .map((target) => [`${target.pluginId}\0${target.platform}\0${target.scope}`, target])).values()]
583
+ .sort((left, right) => compareText(left.pluginId, right.pluginId)
584
+ || compareText(left.platform, right.platform)
585
+ || compareText(left.scope, right.scope));
586
+ const cleanupActions = targetIssues.flatMap((issue) => issue.actions)
587
+ .filter((action) => action.id === 'cleanup');
62
588
  return {
63
589
  issueId,
64
- category: cached ? 'sourceDegraded' : 'sourceUnavailable',
65
- title: `${health.sourceId} · ${cached ? '来源使用缓存' : '来源不可用'}`,
66
- explanation: cached
67
- ? '远端检查失败。当前仍使用已验证的缓存数据。'
68
- : '远端检查失败,并且没有可用缓存数据。',
69
- impact: [cached ? '插件仍可浏览,但数据可能不是最新。' : '当前不能浏览或安装这个来源的插件。',
70
- ...(impactedTargets > 0 ? [`${impactedTargets} 个安装位置受此来源根问题影响。`] : [])],
590
+ category: 'sourceUnavailable',
591
+ title: `${health.sourceId} · 来源不可用`,
592
+ explanation: '来源检查失败。',
593
+ impact: ['当前不能浏览或安装这个来源的插件。',
594
+ ...(affectedTargets.length > 0 ? [`${affectedTargets.length} 个安装位置受此来源根问题影响。`] : [])],
71
595
  entity: { kind: 'source', id: health.sourceId },
72
596
  sourceId: health.sourceId,
73
- actions: cached
74
- ? [
75
- { id: 'refreshSource', label: '重新检查来源', description: '不会修改已安装内容' },
76
- { id: 'continueWithCache', label: '继续使用缓存', description: '当前缓存仍可安全读取' },
77
- { id: 'viewTechnical', label: '技术详情' },
78
- ]
79
- : [
80
- { id: 'editSource', label: '编辑来源', description: '检查 URL、路径或 ref' },
81
- { id: 'refreshSource', label: '稍后重试', description: '只重新检查来源' },
82
- { id: 'viewTechnical', label: '技术详情' },
83
- ],
597
+ ...(affectedTargets.length > 0 ? { affectedTargets } : {}),
598
+ actions: dedupeIssueActions([
599
+ ...cleanupActions,
600
+ { id: 'editSource', label: '编辑来源', description: '检查 URL、路径或 ref' },
601
+ { id: 'refreshSource', label: '稍后重试', description: '只重新检查来源' },
602
+ { id: 'viewTechnical', label: '技术详情' },
603
+ ]),
84
604
  technicalDetails: [{
85
- code: cached ? 'catalogSourceDegraded' : 'catalogSourceUnavailable',
86
- message: cached ? '远端检查失败;已验证缓存仍可用。' : '来源检查失败;没有可用缓存。',
605
+ code: 'catalogSourceUnavailable',
606
+ message: '来源检查失败。',
87
607
  }],
88
608
  };
89
609
  }
90
- function targetIssues(payload) {
610
+ function lifecycleIssueActions(target, options = {}) {
611
+ return buildAllowedLifecycleActions(target).flatMap((action) => {
612
+ const view = projectLifecycleActionView(action);
613
+ if (options.dependency) {
614
+ if (action.id === 'install')
615
+ return [{
616
+ id: 'installDependency',
617
+ label: '补齐依赖插件',
618
+ pluginId: action.pluginId,
619
+ platform: action.platform,
620
+ scope: action.scope,
621
+ project: options.project,
622
+ writeAction: 'install',
623
+ }];
624
+ if (action.id === 'sync' || action.id === 'reinstall')
625
+ return [{
626
+ id: 'reinstallDependency',
627
+ label: view.label,
628
+ description: view.description,
629
+ pluginId: action.pluginId,
630
+ platform: action.platform,
631
+ scope: action.scope,
632
+ project: options.project,
633
+ writeAction: action.id,
634
+ }];
635
+ return [];
636
+ }
637
+ const projection = {
638
+ install: { id: 'install', writeAction: 'install' },
639
+ sync: { id: 'sync', writeAction: 'sync' },
640
+ refreshAndSync: { id: 'refreshAndSync', writeAction: 'refreshAndSync' },
641
+ reinstall: { id: 'reinstall', writeAction: 'reinstall' },
642
+ uninstall: { id: 'remove', writeAction: 'uninstall' },
643
+ cleanup: { id: 'cleanup', writeAction: 'cleanup' },
644
+ }[action.id];
645
+ return [{
646
+ ...projection,
647
+ label: view.label,
648
+ description: view.description,
649
+ pluginId: action.pluginId,
650
+ platform: action.platform,
651
+ scope: action.scope,
652
+ }];
653
+ });
654
+ }
655
+ function targetIssues(payload, targetRevisions = []) {
91
656
  const issues = [];
92
- const byPlacement = new Map();
657
+ const statusTargets = flattenStatusTargets(payload.targets);
658
+ const canonicalRootsByEntity = new Map();
659
+ for (const target of statusTargets) {
660
+ for (const error of target.reasons) {
661
+ if (!error.rootIssueId || !error.rootIssueCode)
662
+ continue;
663
+ const dependencyPluginId = error.dependencyPluginId
664
+ ?? dependencyPluginIdFromMessage(error.message);
665
+ const relationship = targetIssueRelationship({ target, error, dependencyPluginId });
666
+ if (relationship.relationship !== 'consumer')
667
+ continue;
668
+ const key = [
669
+ error.rootIssueCode,
670
+ relationship.rootEntity.pluginId,
671
+ relationship.rootEntity.platform,
672
+ relationship.rootEntity.scope,
673
+ ].join('\0');
674
+ const current = canonicalRootsByEntity.get(key);
675
+ if (!current || compareText(error.rootIssueId, current) < 0) {
676
+ canonicalRootsByEntity.set(key, error.rootIssueId);
677
+ }
678
+ }
679
+ }
93
680
  for (const target of payload.targets) {
94
- const key = `${target.pluginId}\0${target.platform}`;
95
- byPlacement.set(key, [...(byPlacement.get(key) ?? []), target]);
96
- if (target.state !== 'partial' && target.state !== 'error')
681
+ if (target.platform !== 'codex' || !target.reasons.some((reason) => reason.code === 'codexExtensionScopeConflict'))
97
682
  continue;
98
- const codes = new Set(target.errors.map((error) => error.code));
683
+ const recoveries = target.recoveryActions.flatMap((recovery) => {
684
+ const parsed = parsedScopedUninstallRecovery(recovery, target.platform);
685
+ return parsed ? [{ ...parsed, keepScope: parsed.scope === 'user'
686
+ ? 'project' : 'user' }] : [];
687
+ });
688
+ issues.push({
689
+ issueId: `scope:${target.pluginId}:${target.platform}`,
690
+ category: 'scopeConflict',
691
+ title: `${target.pluginId} · Codex · 扩展位置冲突`,
692
+ explanation: 'AW 管理附加能力不能同时使用所有项目和当前项目位置。',
693
+ impact: ['选择要保留的使用位置。系统将移除另一位置,再同步保留的位置。'],
694
+ entity: { kind: 'plugin', id: target.pluginId },
695
+ pluginId: target.pluginId,
696
+ platform: target.platform,
697
+ sourceId: target.sourceId,
698
+ sourceIdentity: target.sourceIdentity,
699
+ placements: target.placements.map((placement) => placement.scope).sort(compareText),
700
+ actions: [
701
+ ...recoveries.map((recovery) => ({
702
+ id: recovery.keepScope === 'user' ? 'keepUserScope' : 'keepProjectScope',
703
+ label: `保留${recovery.keepScope === 'user' ? '所有项目' : '当前项目'}`,
704
+ description: recovery.description,
705
+ pluginId: recovery.pluginId,
706
+ platform: target.platform,
707
+ scope: recovery.scope,
708
+ writeAction: 'uninstall',
709
+ })),
710
+ { id: 'viewTechnical', label: '查看冲突详情' },
711
+ ],
712
+ technicalDetails: [{
713
+ code: 'codexExtensionScopeConflict',
714
+ message: '扩展同时使用 user 和 project scope。',
715
+ }],
716
+ });
717
+ }
718
+ for (const target of payload.targets) {
719
+ if (target.platform !== 'codex')
720
+ continue;
721
+ const sharedReasons = target.reasons.filter((reason) => reason.code.startsWith('codexSharedNative') || reason.code === 'staleCacheObserved');
722
+ if (sharedReasons.length === 0 || target.state === 'ready' || target.state === 'absent')
723
+ continue;
724
+ const installedTargets = statusTargets.filter((candidate) => candidate.pluginId === target.pluginId
725
+ && candidate.platform === 'codex' && candidate.state !== 'absent');
726
+ const directUpgrade = installedTargets.length > 0 && installedTargets.every((candidate) => {
727
+ const revision = targetRevisions.find((entry) => entry.pluginId === candidate.pluginId
728
+ && entry.platform === candidate.platform && entry.scope === candidate.scope);
729
+ return revision !== undefined && isDirectCodexVersionUpgrade(candidate, revision.revision);
730
+ });
731
+ if (directUpgrade)
732
+ continue;
733
+ const codes = new Set(sharedReasons.map((reason) => reason.code));
734
+ const sharedNative = statusTargetSharedNative(target);
735
+ const statusIssue = codes.has('codexSharedNativeForeign')
736
+ ? { title: '平台由多个项目共用的内容被占用', explanation: 'Codex 平台位置含有不属于插件的内容。' }
737
+ : codes.has('codexSharedNativeUpdateRequired')
738
+ ? { title: '版本待同步', explanation: 'Codex 由平台共用的内容版本与当前插件版本不一致。' }
739
+ : codes.has('codexSharedNativeUnobserved')
740
+ ? { title: '状态未确认', explanation: '未取得有效的 Codex 被共用内容的状态证据。' }
741
+ : { title: '由平台共用的内容待同步', explanation: 'Codex 由平台共用的内容缺失、损坏或与当前渲染输入不一致。' };
742
+ const recoveryActions = target.recoveryActions.flatMap((recovery) => {
743
+ if (!recovery.scope)
744
+ return [];
745
+ const scopedTarget = statusTargets.find((candidate) => candidate.pluginId === target.pluginId
746
+ && candidate.platform === target.platform && candidate.scope === recovery.scope);
747
+ return scopedTarget && isInstalledScopeEvidence(scopedTarget)
748
+ ? lifecycleIssueActions({ ...scopedTarget, recoveryActions: [recovery] })
749
+ : [];
750
+ });
751
+ issues.push({
752
+ issueId: `sharedNative:${target.pluginId}`,
753
+ category: target.state === 'blocked' ? 'diagnostic' : 'incompleteInstallation',
754
+ title: `${target.pluginId} · Codex · ${statusIssue.title}`,
755
+ explanation: statusIssue.explanation,
756
+ impact: [sharedNative?.state === 'blocked'
757
+ ? '被共用的内容恢复前,当前插件不能安全同步。'
758
+ : '被共用的内容恢复前,相关 Codex 使用位置可能不可用。'],
759
+ entity: { kind: 'plugin', id: target.pluginId },
760
+ pluginId: target.pluginId,
761
+ platform: target.platform,
762
+ sourceId: target.sourceId,
763
+ sourceIdentity: target.sourceIdentity,
764
+ placements: target.placements.map((placement) => placement.scope).sort(compareText),
765
+ actions: [
766
+ ...new Map(recoveryActions.map((action) => [
767
+ `${action.id}\0${action.scope ?? ''}`,
768
+ action,
769
+ ])).values(),
770
+ { id: 'viewTechnical', label: '技术详情' },
771
+ ],
772
+ technicalDetails: sharedReasons.map((reason) => ({
773
+ code: reason.code,
774
+ message: reason.message,
775
+ })),
776
+ });
777
+ }
778
+ for (const target of statusTargets) {
779
+ const revision = targetRevisions.find((entry) => entry.pluginId === target.pluginId
780
+ && entry.platform === target.platform && entry.scope === target.scope);
781
+ if (revision && isDirectCodexVersionUpgrade(target, revision.revision))
782
+ continue;
783
+ if (target.reasons.length === 0 && (target.sharedReasons?.length ?? 0) > 0
784
+ && isPureNativeCodexTarget(target))
785
+ continue;
786
+ const incomplete = target.state === 'needsSync' || target.state === 'orphaned';
787
+ if (!incomplete && target.state !== 'blocked'
788
+ && !target.reasons.some((error) => error.code === 'artifactCollision'
789
+ || error.dependencyPluginId !== undefined && error.rootIssueId !== undefined))
790
+ continue;
791
+ const codes = new Set(target.reasons.map((error) => error.code));
792
+ const orphanedActivation = target.state === 'orphaned'
793
+ && codes.has('codexActivationDrift') && isPureNativeCodexTarget(target);
794
+ const locationLabel = target.scope === 'project' ? '当前项目' : '所有项目';
795
+ const projectDisablesUserActivation = target.platform === 'codex'
796
+ && target.scope === 'project' && codes.has('codexActivationDrift')
797
+ && statusTargetActivation(target)?.user === true
798
+ && statusTargetActivation(target)?.project === false;
799
+ const sharedNativeRenderDrift = target.state === 'needsSync'
800
+ && codes.has('codexSharedNativeRenderDrift');
801
+ const sharedNativeDamaged = target.state === 'needsSync'
802
+ && codes.has('codexSharedNativeCorrupt');
803
+ const sharedNativeProjectionIssue = sharedNativeRenderDrift || sharedNativeDamaged;
804
+ const sharedNative = statusTargetSharedNative(target);
805
+ const systemConsumer = statusTargetActivation(target)?.systemConsumer;
806
+ const statusUnconfirmed = systemConsumer?.state === 'unresolved'
807
+ || codes.has('codexSharedNativeUnobserved')
808
+ || codes.has('codexNativeObservationFailed')
809
+ || (sharedNative?.state === 'blocked' && sharedNative.reason === 'unreadable');
810
+ const contentMissing = codes.has('missingContent')
811
+ || sharedNative?.state === 'missing'
812
+ || statusTargetExtensions(target)?.state === 'missing'
813
+ || target.components.some((component) => component.kind === 'ownership'
814
+ && component.state === 'absent');
815
+ const statusIssue = systemConsumer?.state === 'incompatible'
816
+ ? { title: 'Codex 版本不兼容', explanation: '当前 Codex 版本低于要求,请升级后重试。' }
817
+ : statusUnconfirmed
818
+ ? { title: '状态未确认', explanation: '未取得有效的 Codex 状态证据,暂时无法确认插件是否可用。' }
819
+ : projectDisablesUserActivation
820
+ ? {
821
+ title: '插件在当前项目被关闭',
822
+ explanation: '这个插件已为所有项目启用,但当前项目的设置将它关闭了,因此在这里无法使用。',
823
+ }
824
+ : orphanedActivation
825
+ ? {
826
+ title: `${locationLabel}有多余的启用记录`,
827
+ explanation: `按当前使用设置,不需要在${locationLabel}单独启用这个插件,但 Codex 仍保留着这条启用记录。`,
828
+ }
829
+ : codes.has('codexActivationDrift')
830
+ ? { title: '激活范围不一致', explanation: 'Codex 激活配置与期望的使用范围不一致。' }
831
+ : target.state === 'orphaned'
832
+ ? { title: '有多余的插件内容', explanation: '按当前使用设置,这个位置不再需要安装此插件,但仍留有插件配置或文件。' }
833
+ : codes.has('codexSharedNativeUpdateRequired')
834
+ ? { title: '版本待同步', explanation: '已安装的 Codex 由平台共用的内容版本与当前插件版本不一致。' }
835
+ : sharedNativeDamaged || contentMissing
836
+ ? { title: '安装不完整', explanation: '必要内容缺失或损坏,需要运行同步。' }
837
+ : { title: '状态待同步', explanation: '现场状态与期望状态不一致,需要重新检查并同步。' };
99
838
  const dependencyCodes = [
100
839
  'dependencyMissing',
101
840
  'dependencyNotReady',
@@ -103,159 +842,256 @@ function targetIssues(payload) {
103
842
  'dependencyStillRequired',
104
843
  'dependencyGraphInvalid',
105
844
  ];
106
- const dependencyRoots = [...new Map(target.errors.flatMap((error) => {
107
- if (!dependencyCodes.includes(error.code))
108
- return [];
109
- const code = error.code;
110
- const dependencyPluginId = dependencyPluginIdFromMessage(error.message);
111
- return [[`${code}\0${dependencyPluginId ?? ''}`, {
112
- code,
113
- category: code,
114
- mergeEligible: false,
115
- error,
116
- dependencyPluginId,
117
- }]];
118
- })).values()];
119
- const roots = dependencyRoots.length > 0
120
- ? dependencyRoots
121
- : target.state === 'partial'
122
- ? [{ code: 'partial', category: 'incompleteInstallation', mergeEligible: false }]
123
- : codes.has('sourceMissing')
124
- ? [{ code: 'sourceMissing', category: 'sourceMissing', mergeEligible: false }]
125
- : codes.has('sourceChanged')
126
- ? [{ code: 'sourceChanged', category: 'sourceIdentityChanged', mergeEligible: false }]
127
- : [
845
+ const dependencyRootMap = new Map();
846
+ for (const error of target.reasons) {
847
+ if (!dependencyCodes.includes(error.code))
848
+ continue;
849
+ if (error.rootIssueCode === 'artifactCollision') {
850
+ dependencyRootMap.set(error.rootIssueId ?? `artifactCollision\0${error.dependencyPluginId ?? ''}`, {
851
+ code: 'artifactCollision',
852
+ category: 'artifactCollision',
853
+ mergeEligible: false,
854
+ error,
855
+ dependencyPluginId: error.dependencyPluginId,
856
+ });
857
+ continue;
858
+ }
859
+ const code = error.code;
860
+ const dependencyPluginId = error.dependencyPluginId ?? dependencyPluginIdFromMessage(error.message);
861
+ dependencyRootMap.set(`${code}\0${dependencyPluginId ?? ''}`, {
862
+ code,
863
+ category: code,
864
+ mergeEligible: false,
865
+ error,
866
+ dependencyPluginId,
867
+ });
868
+ }
869
+ const dependencyRoots = [...dependencyRootMap.values()];
870
+ const directCollision = target.reasons.find((error) => error.code === 'artifactCollision');
871
+ const projectedCollisionRoots = dependencyRoots.filter((root) => root.code === 'artifactCollision');
872
+ const ordinaryDependencyRoots = dependencyRoots.filter((root) => root.code !== 'artifactCollision');
873
+ const roots = codes.has('sourceMissing')
874
+ ? [{ code: 'sourceMissing', category: 'sourceMissing', mergeEligible: false }]
875
+ : directCollision || projectedCollisionRoots.length > 0
876
+ ? [{
877
+ code: 'artifactCollision',
878
+ category: 'artifactCollision',
879
+ mergeEligible: false,
880
+ error: directCollision ?? projectedCollisionRoots[0].error,
881
+ dependencyPluginId: projectedCollisionRoots[0]?.dependencyPluginId,
882
+ }]
883
+ : codes.has('sourceChanged')
884
+ ? [{ code: 'sourceChanged', category: 'sourceIdentityChanged', mergeEligible: false }]
885
+ : codes.has('catalogSourceUnavailable') || codes.has('bundleUnavailable')
886
+ || codes.has('codexIntentSourceUnavailable')
887
+ ? [
128
888
  ...(codes.has('catalogSourceUnavailable')
129
889
  ? [{ code: 'catalogSourceUnavailable', category: 'sourceUnavailable', mergeEligible: true }]
130
890
  : []),
131
891
  ...(codes.has('bundleUnavailable')
132
892
  ? [{ code: 'bundleUnavailable', category: 'sourceUnavailable', mergeEligible: false }]
133
893
  : []),
134
- ...(!codes.has('catalogSourceUnavailable') && !codes.has('bundleUnavailable')
135
- ? [{ code: 'targetDiagnostic', category: 'diagnostic', mergeEligible: false }]
894
+ ...(codes.has('codexIntentSourceUnavailable')
895
+ ? [{ code: 'codexIntentSourceUnavailable', category: 'sourceUnavailable', mergeEligible: false }]
136
896
  : []),
137
- ];
897
+ ]
898
+ : ordinaryDependencyRoots.length > 0
899
+ ? ordinaryDependencyRoots
900
+ : incomplete
901
+ ? [{ code: 'partial', category: 'incompleteInstallation', mergeEligible: false }]
902
+ : [
903
+ { code: 'targetDiagnostic', category: 'diagnostic', mergeEligible: false },
904
+ ];
138
905
  for (const root of roots) {
139
- const rootError = root.error ?? target.errors.find((error) => error.code === root.code);
906
+ const rootError = root.error ?? target.reasons.find((error) => error.code === root.code);
140
907
  const dependencyPluginId = root.dependencyPluginId
908
+ ?? rootError?.dependencyPluginId
141
909
  ?? (rootError ? dependencyPluginIdFromMessage(rootError.message) : undefined);
910
+ const dependencyPlatform = rootError?.dependencyPlatform;
911
+ const dependencyScope = rootError?.dependencyScope;
912
+ const dependencyProject = rootError?.dependencyProject;
142
913
  const dependencyCategory = root.category.startsWith('dependency');
914
+ const directLifecycleActions = lifecycleIssueActions(target).map((action) => projectDisablesUserActivation && action.id === 'sync' ? {
915
+ ...action,
916
+ label: '恢复在当前项目使用',
917
+ description: '移除当前项目的关闭设置,沿用所有项目的启用设置。',
918
+ } : orphanedActivation && action.id === 'cleanup' ? {
919
+ ...action,
920
+ label: '清理多余的启用记录',
921
+ description: `清理${locationLabel}的多余记录;保留其他位置的使用设置和插件文件。`,
922
+ } : action);
923
+ const desiredConsumer = statusTargetIntent(target).enabled
924
+ && statusTargetIntent(target).scopes.includes(target.scope);
925
+ const consumerRecoveryActions = dependencyCategory
926
+ ? directLifecycleActions.filter((action) => desiredConsumer
927
+ ? action.id === 'sync' || action.id === 'reinstall' || action.id === 'refreshAndSync'
928
+ : action.id === 'cleanup')
929
+ : directLifecycleActions;
143
930
  const actions = root.category === 'dependencyMissing'
144
931
  ? [
145
- ...(dependencyPluginId ? [{ id: 'installDependency', label: '补齐依赖插件',
146
- pluginId: dependencyPluginId, writeAction: 'install' }] : []),
932
+ ...consumerRecoveryActions,
933
+ ...(dependencyPluginId ? [
934
+ { id: 'viewDependencyIssue', label: '查看依赖插件问题', pluginId: dependencyPluginId },
935
+ ] : []),
147
936
  { id: 'viewDependencies', label: '查看依赖关系' },
148
937
  { id: 'viewTechnical', label: '技术详情' },
149
938
  ]
150
939
  : root.category === 'dependencyNotReady'
151
940
  ? [
941
+ ...consumerRecoveryActions,
152
942
  ...(dependencyPluginId ? [
153
943
  { id: 'viewDependencyIssue', label: '查看依赖插件问题', pluginId: dependencyPluginId },
154
- { id: 'reinstallDependency', label: '重新安装依赖插件',
155
- pluginId: dependencyPluginId, writeAction: 'reinstall' },
156
944
  ] : []),
157
945
  { id: 'viewDependencies', label: '查看依赖关系' },
158
946
  { id: 'viewTechnical', label: '技术详情' },
159
947
  ]
160
948
  : dependencyCategory
161
949
  ? [
950
+ ...consumerRecoveryActions,
162
951
  { id: 'viewDependencies', label: '查看依赖关系' },
163
952
  { id: 'viewTechnical', label: '技术详情' },
164
953
  ]
165
- : root.category === 'incompleteInstallation'
954
+ : root.category === 'artifactCollision'
166
955
  ? [
167
- { id: 'reinstall', label: '重新安装', description: '保留插件设置,重新生成缺失内容' },
168
- { id: 'remove', label: '移除安装', description: '保留插件设置' },
169
- { id: 'viewTechnical', label: '技术详情' },
956
+ { id: 'viewTechnical', label: '查看冲突原因' },
957
+ { id: 'retryLater', label: '处理冲突后重试' },
170
958
  ]
171
- : root.category === 'sourceMissing'
959
+ : root.category === 'incompleteInstallation'
172
960
  ? [
173
- { id: 'reAddSource', label: '重新添加同一来源', description: '保留来源 ID;来源类型和位置由你选择' },
961
+ ...consumerRecoveryActions,
174
962
  { id: 'viewTechnical', label: '技术详情' },
175
963
  ]
176
- : root.category === 'sourceIdentityChanged'
964
+ : root.category === 'sourceMissing'
177
965
  ? [
178
- { id: 'viewSourceChange', label: '查看来源变更', description: '不会自动覆盖现场状态' },
966
+ ...consumerRecoveryActions,
967
+ { id: 'reAddSource', label: '重新添加同一来源', description: '保留来源 ID;来源类型和位置由你选择' },
179
968
  { id: 'viewTechnical', label: '技术详情' },
180
969
  ]
181
- : root.category === 'sourceUnavailable'
970
+ : root.category === 'sourceIdentityChanged'
182
971
  ? [
183
- { id: 'editSource', label: '查看来源', description: root.code === 'bundleUnavailable' ? '检查这个插件包或稍后重试' : '修复来源配置或稍后重试' },
972
+ { id: 'viewSourceChange', label: '查看来源变更', description: '不会自动覆盖现场状态' },
184
973
  { id: 'viewTechnical', label: '技术详情' },
185
974
  ]
186
- : [{ id: 'viewTechnical', label: '技术详情' }, { id: 'retryLater', label: '稍后重试' }];
187
- const issueId = `target:${target.pluginId}:${target.platform}:${target.scope}:${target.state}${roots.length > 1 ? `:${root.code}` : ''}${dependencyPluginId ? `:${dependencyPluginId}` : ''}`;
975
+ : root.category === 'sourceUnavailable'
976
+ ? [
977
+ ...consumerRecoveryActions.filter((action) => action.id === 'cleanup'),
978
+ { id: 'editSource', label: '查看来源', description: root.code === 'bundleUnavailable'
979
+ ? '检查当前插件目录或稍后重试'
980
+ : root.code === 'codexIntentSourceUnavailable'
981
+ ? '恢复来源后可重新同步已保存的使用设置'
982
+ : '修复来源配置或稍后重试' },
983
+ { id: 'viewTechnical', label: '技术详情' },
984
+ ]
985
+ : [...consumerRecoveryActions,
986
+ { id: 'viewTechnical', label: '技术详情' },
987
+ { id: 'retryLater', label: '稍后重试' }];
188
988
  const bundleUnavailable = root.code === 'bundleUnavailable';
989
+ const codexIntentSourceUnavailable = root.code === 'codexIntentSourceUnavailable';
990
+ const relationship = targetIssueRelationship({
991
+ target,
992
+ error: rootError,
993
+ dependencyPluginId,
994
+ });
995
+ const directRootCode = root.code === 'partial' ? 'incompleteInstallation'
996
+ : root.code === 'targetDiagnostic' ? 'diagnostic'
997
+ : undefined;
998
+ const canonicalRoot = relationship.relationship === 'direct' && directRootCode
999
+ ? canonicalRootsByEntity.get([
1000
+ directRootCode,
1001
+ relationship.rootEntity.pluginId,
1002
+ relationship.rootEntity.platform,
1003
+ relationship.rootEntity.scope,
1004
+ ].join('\0'))
1005
+ : undefined;
1006
+ const issueId = rootError?.rootIssueId
1007
+ ?? canonicalRoot
1008
+ ?? (directRootCode
1009
+ ? `${directRootCode}:${target.pluginId}:${target.platform}:${target.scope}`
1010
+ : `target:${target.pluginId}:${target.platform}:${target.scope}:${target.state}${roots.length > 1 ? `:${root.code}` : ''}${dependencyPluginId ? `:${dependencyPluginId}` : ''}`);
189
1011
  issues.push({
190
1012
  issueId,
191
1013
  category: root.category,
192
1014
  rootCode: root.code,
193
1015
  mergeEligible: root.mergeEligible,
194
- title: `${target.pluginId} · ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${dependencyCategory ? {
1016
+ relationship: relationship.relationship,
1017
+ rootEntity: relationship.rootEntity,
1018
+ affectedTargets: [{
1019
+ pluginId: target.pluginId,
1020
+ platform: target.platform,
1021
+ scope: target.scope,
1022
+ relationship: relationship.relationship,
1023
+ }],
1024
+ title: `${target.pluginId} · ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${dependencyCategory ? `${target.scope === 'user' ? '所有项目' : '当前项目'} · ` : ''}${sharedNativeProjectionIssue ? '由平台共用的内容待同步' : root.category === 'artifactCollision' ? '输出冲突' : dependencyCategory ? {
195
1025
  dependencyMissing: '缺少依赖',
196
1026
  dependencyNotReady: '依赖安装不完整',
197
1027
  dependencyTargetUnsupported: '依赖目标不兼容',
198
1028
  dependencyStillRequired: '依赖仍被使用',
199
1029
  dependencyGraphInvalid: '依赖关系无效',
200
- }[root.category] : target.state === 'partial' ? '安装不完整' : bundleUnavailable ? '插件包不可用' : '检查失败'}`,
201
- explanation: dependencyCategory
202
- ? dependencyPluginId
203
- ? `${dependencyPluginId} 导致当前依赖检查不能通过。`
204
- : '当前依赖检查不能安全通过。'
205
- : target.state === 'partial'
206
- ? '必要内容缺失,插件可能不能稳定运行。'
207
- : bundleUnavailable
208
- ? '当前来源中无法取得这个插件的可信 bundle。'
209
- : '当前目标不能安全分类为正常安装。',
210
- impact: [bundleUnavailable
211
- ? '当前目标不能使用这个插件包;这不表示来源缓存整体不可用。'
212
- : '写操作前需要重新检查这个目标。'],
1030
+ }[root.category] : incomplete || statusUnconfirmed || systemConsumer?.state === 'incompatible' ? statusIssue.title : bundleUnavailable ? '当前插件目录不再包含此插件' : codexIntentSourceUnavailable ? '已保存使用设置不能解析' : '检查失败'}`,
1031
+ explanation: root.category === 'artifactCollision'
1032
+ ? '目标位置已有不属于当前插件的内容。AW 不会覆盖或删除这些内容。'
1033
+ : dependencyCategory
1034
+ ? dependencyPluginId
1035
+ ? `依赖 ${dependencyPluginId}${dependencyPlatform && dependencyScope
1036
+ ? `(${dependencyPlatform === 'codex' ? 'Codex' : 'Claude Code'} · ${dependencyScope === 'user' ? '所有项目' : '当前项目'})`
1037
+ : ''}未就绪;影响 ${target.pluginId}(${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'})。`
1038
+ : `依赖检查未通过;影响 ${target.pluginId}(${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'})。`
1039
+ : sharedNativeDamaged
1040
+ ? 'Codex 平台由多个项目共用的生成内容缺失或损坏。'
1041
+ : sharedNativeRenderDrift
1042
+ ? 'Codex 平台由多个项目共用的生成内容与当前渲染输入不一致。'
1043
+ : incomplete || statusUnconfirmed || systemConsumer?.state === 'incompatible'
1044
+ ? statusIssue.explanation
1045
+ : bundleUnavailable
1046
+ ? '当前插件目录不再包含此插件,但仍发现 AW 管理的已安装内容。'
1047
+ : codexIntentSourceUnavailable
1048
+ ? '当前插件目录不能解析这个已保存的使用设置。'
1049
+ : '当前目标不能安全分类为正常安装。',
1050
+ impact: [sharedNativeProjectionIssue
1051
+ ? '运行同步可重新生成由平台共用的 Skill。'
1052
+ : projectDisablesUserActivation
1053
+ ? '恢复后,当前项目可以继续使用已经安装的插件。'
1054
+ : orphanedActivation
1055
+ ? '清理只移除多余的启用记录,不会删除其他位置正在使用的插件。'
1056
+ : root.category === 'artifactCollision'
1057
+ ? '请先移除或迁移冲突内容,然后重试安装或重新安装。'
1058
+ : bundleUnavailable || codexIntentSourceUnavailable || root.code === 'catalogSourceUnavailable'
1059
+ ? '可以核验后取消这个位置的使用设置,并清理可信的 AW 现场。'
1060
+ : '写操作前需要重新检查这个目标。'],
213
1061
  entity: { kind: 'plugin', id: target.pluginId },
214
1062
  pluginId: target.pluginId,
215
1063
  ...(dependencyPluginId ? { dependencyPluginId } : {}),
1064
+ ...(dependencyPlatform ? { dependencyPlatform } : {}),
1065
+ ...(dependencyScope ? { dependencyScope } : {}),
1066
+ ...(dependencyProject !== undefined ? { dependencyProject } : {}),
216
1067
  platform: target.platform,
217
1068
  scope: target.scope,
218
1069
  sourceId: target.sourceId,
219
1070
  sourceIdentity: target.sourceIdentity,
220
1071
  actions,
221
- technicalDetails: target.errors.length > 0
222
- ? target.errors.filter((error) => root.code === 'targetDiagnostic'
1072
+ technicalDetails: target.reasons.length > 0
1073
+ ? target.reasons.filter((error) => root.code === 'targetDiagnostic'
223
1074
  || root.code === 'partial'
224
- || error.code === root.code)
225
- .map((error) => ({ code: error.code, message: '状态检查报告了这个安全错误代码。' }))
1075
+ || error.code === root.code
1076
+ || error.rootIssueId === issueId)
1077
+ .map((error) => ({
1078
+ code: error.code,
1079
+ message: error.collision
1080
+ ? ({
1081
+ ownerMismatch: '目标由其他 owner 占用。',
1082
+ unownedContent: '目标包含没有 AW owner 标记的内容。',
1083
+ typeMismatch: '目标节点类型不符合预期。',
1084
+ })[error.collision.reason]
1085
+ : '状态检查报告了这个安全错误代码。',
1086
+ }))
226
1087
  : [{ code: 'targetStateError', message: '目标状态异常。' }],
227
1088
  });
228
1089
  }
229
1090
  }
230
- for (const targets of byPlacement.values()) {
231
- const installed = targets.filter(isInstalledScopeEvidence);
232
- if (new Set(installed.map((target) => target.scope)).size < 2)
233
- continue;
234
- const sample = installed[0];
235
- issues.push({
236
- issueId: `scope:${sample.pluginId}:${sample.platform}`,
237
- category: 'scopeConflict',
238
- title: `${sample.pluginId} · ${sample.platform === 'codex' ? 'Codex' : 'Claude Code'} · 安装位置冲突`,
239
- explanation: '所有项目和当前项目同时发现了这个插件。',
240
- impact: ['正常状态只能保留一个位置。'],
241
- entity: { kind: 'plugin', id: sample.pluginId },
242
- pluginId: sample.pluginId,
243
- platform: sample.platform,
244
- sourceId: sample.sourceId,
245
- sourceIdentity: sample.sourceIdentity,
246
- placements: installed.map((target) => target.scope).sort(compareText),
247
- actions: [
248
- { id: 'keepUserScope', label: '保留所有项目', description: '移除当前项目中的安装;设置保留' },
249
- { id: 'keepProjectScope', label: '保留当前项目', description: '移除所有项目中的安装;设置保留' },
250
- { id: 'viewTechnical', label: '查看两个位置的详情' },
251
- ],
252
- technicalDetails: [{ code: 'scopeConflict', message: '同一插件和平台存在两个安装位置。' }],
253
- });
254
- }
255
1091
  return issues;
256
1092
  }
257
1093
  function statusIssues(payload) {
258
- const targetCodes = new Set(payload.targets.flatMap((target) => target.errors.map((error) => error.code)));
1094
+ const targetCodes = new Set(payload.targets.flatMap((target) => target.reasons.map((error) => error.code)));
259
1095
  const issues = new Map();
260
1096
  for (const error of payload.errors) {
261
1097
  if (targetCodes.has(error.code) || issues.has(error.code))
@@ -299,102 +1135,320 @@ function statusIssues(payload) {
299
1135
  function configIssues(pluginSummaries) {
300
1136
  return pluginSummaries.flatMap((summary) => {
301
1137
  const sourceId = summary.pluginId.slice(0, summary.pluginId.indexOf('/'));
1138
+ const issues = [];
302
1139
  if (summary.configDiagnostic) {
303
- return [{
304
- issueId: `config:${summary.pluginId}:read`,
305
- category: 'diagnostic',
306
- title: `${summary.displayName ?? summary.pluginName} · 设置状态不可读`,
307
- explanation: '当前不能安全读取这个插件的设置状态。',
308
- impact: ['其他插件仍可检查;当前插件的设置值不会显示。'],
309
- entity: { kind: 'plugin', id: summary.pluginId },
310
- pluginId: summary.pluginId,
311
- sourceId,
312
- actions: [
313
- { id: 'retryLater', label: '稍后重新检查' },
314
- { id: 'viewTechnical', label: '查看技术详情' },
315
- ],
316
- technicalDetails: [{ code: summary.configDiagnostic.code, message: summary.configDiagnostic.message }],
317
- }];
1140
+ issues.push({
1141
+ issueId: `config:${summary.pluginId}:read`,
1142
+ category: 'diagnostic',
1143
+ title: `${summary.displayName ?? summary.pluginName} · 设置状态不可读`,
1144
+ explanation: '当前不能安全读取这个插件的设置状态。',
1145
+ impact: ['其他插件仍可检查;当前插件的设置值不会显示。'],
1146
+ entity: { kind: 'plugin', id: summary.pluginId },
1147
+ pluginId: summary.pluginId,
1148
+ sourceId,
1149
+ actions: [
1150
+ { id: 'retryLater', label: '稍后重新检查' },
1151
+ { id: 'viewTechnical', label: '查看技术详情' },
1152
+ ],
1153
+ technicalDetails: [{
1154
+ code: summary.configDiagnostic.code,
1155
+ message: '设置读取报告了这个安全错误代码。',
1156
+ }],
1157
+ });
318
1158
  }
319
1159
  if (!summary.configIssues?.length)
320
- return [];
321
- const types = new Set(summary.configIssues.map((issue) => issue.type));
322
- const invalid = types.has('invalid-config');
323
- const sources = new Set(summary.configIssues.map((issue) => issue.source));
324
- const onlySource = sources.size === 1 ? [...sources][0] : undefined;
325
- const configLayer = onlySource === 'user'
326
- ? { scope: 'user', projectLayer: 'shared' }
327
- : onlySource === 'project'
328
- ? { scope: 'project', projectLayer: 'shared' }
329
- : onlySource === 'projectLocal'
330
- ? { scope: 'project', projectLayer: 'local' }
331
- : undefined;
332
- return [{
333
- issueId: `config:${summary.pluginId}:${invalid ? 'invalid' : 'required'}`,
334
- category: 'requiredConfigMissing',
335
- title: `${summary.displayName ?? summary.pluginName} · ${invalid ? '设置值无效' : '缺少必填设置'}`,
336
- explanation: invalid
337
- ? '已有设置值不符合当前字段规则。'
338
- : '插件缺少必填设置,写操作会在预检时被阻止。',
339
- impact: [invalid ? '修正所列字段后才能稳定生成插件内容。' : '完成所列字段后才能稳定生成插件内容。'],
1160
+ return issues;
1161
+ const unsupported = summary.configIssues.filter((issue) => issue.type === 'configLayerUnsupported');
1162
+ if (unsupported.length > 0) {
1163
+ issues.push({
1164
+ issueId: `config:${summary.pluginId}:unsupported-project-layer`,
1165
+ category: 'configLayerUnsupported',
1166
+ title: `${summary.displayName ?? summary.pluginName} · 项目设置包含旧值`,
1167
+ explanation: '本项目旧值不再受支持;当前继承个人或插件默认。',
1168
+ impact: ['旧值不会参与生成。可以进入项目设置删除旧值。'],
340
1169
  entity: { kind: 'plugin', id: summary.pluginId },
341
1170
  pluginId: summary.pluginId,
342
1171
  sourceId,
343
- requiredFields: summary.configIssues.map(({ key, title, type, source }) => ({ key, title, type, source })),
344
- ...(configLayer ? { configLayer } : {}),
1172
+ configLayer: { scope: 'project' },
345
1173
  actions: [
346
- { id: 'openConfig', label: invalid ? '修正设置' : '完成设置', description: configLayer ? '进入建议的设置层' : '选择个人或项目设置层' },
1174
+ { id: 'openConfig', label: '删除旧值', description: '进入项目设置并恢复继承' },
347
1175
  { id: 'viewTechnical', label: '查看字段详情' },
348
1176
  ],
349
- technicalDetails: summary.configIssues.map((entry) => ({
1177
+ technicalDetails: unsupported.map((entry) => ({
350
1178
  code: entry.type,
351
- message: `${entry.title} · ${entry.source}`,
1179
+ message: `${entry.title} · project · 实际使用 user/default`,
352
1180
  })),
353
- }];
1181
+ });
1182
+ }
1183
+ const blockingIssues = summary.configIssues.filter((issue) => issue.type !== 'configLayerUnsupported');
1184
+ if (blockingIssues.length === 0)
1185
+ return issues;
1186
+ const types = new Set(blockingIssues.map((issue) => issue.type));
1187
+ const invalid = types.has('invalid-config') || types.has('sharedCompositeContainsLocalPath');
1188
+ const sources = new Set(blockingIssues.map((issue) => issue.source));
1189
+ const onlySource = sources.size === 1 ? [...sources][0] : undefined;
1190
+ const configLayer = onlySource === 'user'
1191
+ ? { scope: 'user' }
1192
+ : onlySource === 'project'
1193
+ ? { scope: 'project' }
1194
+ : undefined;
1195
+ issues.push({
1196
+ issueId: `config:${summary.pluginId}:${invalid ? 'invalid' : 'required'}`,
1197
+ category: 'requiredConfigMissing',
1198
+ title: `${summary.displayName ?? summary.pluginName} · ${invalid ? '设置值无效' : '缺少必填设置'}`,
1199
+ explanation: invalid
1200
+ ? '已有设置值不符合当前字段规则。'
1201
+ : '插件缺少必填设置,写操作会在预检时被阻止。',
1202
+ impact: [invalid ? '修正所列字段后才能稳定生成插件内容。' : '完成所列字段后才能稳定生成插件内容。'],
1203
+ entity: { kind: 'plugin', id: summary.pluginId },
1204
+ pluginId: summary.pluginId,
1205
+ sourceId,
1206
+ requiredFields: blockingIssues.map(({ key, title, type, source, scope }) => ({
1207
+ key, title, type, source, scope,
1208
+ })),
1209
+ ...(configLayer ? { configLayer } : {}),
1210
+ actions: [
1211
+ { id: 'openConfig', label: invalid ? '修正设置' : '完成设置', description: configLayer ? '进入建议的设置层' : '选择个人或项目设置层' },
1212
+ { id: 'viewTechnical', label: '查看字段详情' },
1213
+ ],
1214
+ technicalDetails: blockingIssues.map((entry) => ({
1215
+ code: entry.type,
1216
+ message: `${entry.title} · ${entry.scope} · ${entry.source}`,
1217
+ })),
1218
+ });
1219
+ return issues;
354
1220
  });
355
1221
  }
356
- function enrichDependencyIssuePlacements(issues, overviews) {
357
- const overviewById = new Map(overviews.map((overview) => [overview.pluginId, overview]));
358
- return issues.map((issue) => {
359
- if ((issue.category !== 'dependencyMissing' && issue.category !== 'dependencyNotReady')
360
- || !issue.pluginId || !issue.dependencyPluginId || !issue.platform || !issue.scope)
361
- return issue;
362
- const dependency = overviewById.get(issue.pluginId)?.dependencies.find((candidate) => candidate.pluginId === issue.dependencyPluginId);
363
- const references = (dependency?.placementReferences ?? []).filter((reference) => reference.consumer.pluginId === issue.pluginId
364
- && reference.consumer.platform === issue.platform
365
- && reference.consumer.scope === issue.scope
366
- && (issue.scope === 'user' || reference.consumer.project === 'current'))
367
- .sort((left, right) => [
368
- left.dependency.pluginId,
369
- left.dependency.platform,
370
- left.dependency.scope,
371
- left.dependency.project ?? '',
372
- ].join('\0').localeCompare([
373
- right.dependency.pluginId,
374
- right.dependency.platform,
375
- right.dependency.scope,
376
- right.dependency.project ?? '',
377
- ].join('\0'), 'en'));
378
- const target = references[0]?.dependency;
379
- if (!target) {
380
- return {
381
- ...issue,
382
- actions: issue.actions.filter((action) => action.id !== 'installDependency' && action.id !== 'reinstallDependency'),
383
- };
384
- }
1222
+ function dependencyOverviewIssues(overviews, pluginSummaries, status) {
1223
+ const summaryById = new Map(pluginSummaries.map((summary) => [summary.pluginId, summary]));
1224
+ return overviews.flatMap((overview) => overview.blockers.flatMap((blocker) => {
1225
+ if (blocker.code !== 'dependencyGraphInvalid'
1226
+ && blocker.code !== 'dependencyTargetUnsupported')
1227
+ return [];
1228
+ if (status.targets.some((target) => target.pluginId === overview.pluginId
1229
+ && target.reasons.some((error) => error.code === blocker.code)))
1230
+ return [];
1231
+ const summary = summaryById.get(overview.pluginId);
1232
+ const sourceId = overview.pluginId.slice(0, overview.pluginId.indexOf('/'));
1233
+ const category = blocker.code;
1234
+ return [{
1235
+ issueId: `dependency:${overview.pluginId}:${blocker.code}`,
1236
+ category,
1237
+ rootCode: category,
1238
+ mergeEligible: false,
1239
+ title: `${summary?.displayName ?? summary?.pluginName ?? overview.pluginId} · ${blocker.title}`,
1240
+ explanation: blocker.explanation,
1241
+ impact: [category === 'dependencyGraphInvalid'
1242
+ ? '修复依赖关系后才能安全计算安装顺序。'
1243
+ : '当前不能在所有声明的平台上安全安装这个插件。'],
1244
+ entity: { kind: 'plugin', id: overview.pluginId },
1245
+ pluginId: overview.pluginId,
1246
+ sourceId,
1247
+ actions: [
1248
+ { id: 'viewDependencies', label: '查看依赖关系' },
1249
+ { id: 'viewTechnical', label: '技术详情' },
1250
+ ],
1251
+ technicalDetails: [{ code: blocker.code, message: blocker.explanation }],
1252
+ }];
1253
+ }));
1254
+ }
1255
+ function issueDiagnostic(issue, sourceDetails) {
1256
+ const source = issue.sourceId ? sourceDetails.get(issue.sourceId) : undefined;
1257
+ const sourceUnavailable = issue.category === 'sourceUnavailable' && issue.entity.kind === 'source';
1258
+ const configLayerUnsupported = issue.category === 'configLayerUnsupported';
1259
+ const configUnavailable = issue.category === 'diagnostic' && issue.issueId.startsWith('config:');
1260
+ const configInvalid = issue.category === 'requiredConfigMissing';
1261
+ const dependency = issue.category.startsWith('dependency');
1262
+ const platformState = issue.issueId.startsWith('status:') || issue.rootCode === 'targetDiagnostic';
1263
+ const preserved = sourceUnavailable
1264
+ ? '已安装插件和已保存设置不会被删除。'
1265
+ : configUnavailable
1266
+ ? '已保存的设置保持不变。'
1267
+ : '现有安装和已保存设置不会因本页自动改变。';
1268
+ const impact = sourceUnavailable
1269
+ ? [
1270
+ { state: 'blocked', message: '当前不能浏览或安装这个来源中的插件。' },
1271
+ { state: 'preserved', message: preserved },
1272
+ ]
1273
+ : [
1274
+ ...issue.impact.map((message) => ({
1275
+ state: dependency || configUnavailable || configInvalid || platformState ? 'blocked' : 'degraded',
1276
+ message,
1277
+ })),
1278
+ { state: 'preserved', message: preserved },
1279
+ ];
1280
+ const primaryAction = issue.actions.find((action) => action.id !== 'viewTechnical');
1281
+ const nextStep = primaryAction
1282
+ ? `${primaryAction.label},然后重新检查当前状态。`
1283
+ : '打开技术详情,记录安全错误代码后重新检查。';
1284
+ const sourceDiagnostic = sourceUnavailable;
1285
+ const rawCode = sourceDiagnostic
1286
+ ? source?.errorCode ?? issue.technicalDetails[0]?.code
1287
+ : issue.technicalDetails[0]?.code;
1288
+ const code = safeDiagnosticCode(rawCode, sourceUnavailable
1289
+ ? 'catalogSourceUnavailable' : 'diagnosticUnavailable');
1290
+ const stage = sourceUnavailable ? 'resolve-source'
1291
+ : configLayerUnsupported ? 'read-config'
1292
+ : configUnavailable ? 'read-config'
1293
+ : configInvalid ? 'validate-config'
1294
+ : dependency ? 'resolve-dependencies'
1295
+ : platformState ? 'read-platform-state' : 'inspect-target';
1296
+ const context = [
1297
+ ...(issue.pluginId ? [{ key: 'pluginId', value: issue.pluginId }] : []),
1298
+ ...(issue.dependencyPluginId
1299
+ ? [{ key: 'dependencyPluginId', value: issue.dependencyPluginId }]
1300
+ : []),
1301
+ ...(issue.platform ? [{ key: 'platform', value: issue.platform }] : []),
1302
+ ...(issue.scope ? [{ key: 'scope', value: issue.scope }] : []),
1303
+ ...(issue.requiredFields?.slice(0, 1).map((field) => ({
1304
+ key: 'field',
1305
+ value: field.title,
1306
+ })) ?? []),
1307
+ ];
1308
+ const checks = issue.category === 'legacyState'
1309
+ ? [
1310
+ '不自动迁移旧版状态。',
1311
+ '运行 aw status --json,记录公开错误 code。',
1312
+ '备份相关 AW 配置。',
1313
+ '按技术详情使用受支持命令或人工处理。',
1314
+ '再次运行状态检查。',
1315
+ ]
1316
+ : configLayerUnsupported
1317
+ ? ['打开项目设置。', '删除本项目旧值。', '重新检查插件设置。']
1318
+ : sourceUnavailable
1319
+ ? ['检查 Git URL、ref 或本地目录。', '确认当前终端具备来源授权。', '运行“重新检查来源”验证修复。']
1320
+ : dependency
1321
+ ? ['检查依赖关系和依赖插件状态。', '完成恢复动作后重新检查根插件。']
1322
+ : configUnavailable
1323
+ ? ['恢复插件来源和可信字段定义。', '重新检查来源后再次打开设置。']
1324
+ : configInvalid
1325
+ ? ['完成或修正列出的设置字段。', '重新检查插件设置。']
1326
+ : ['检查目标平台配置和访问权限。', '完成恢复动作后重新运行状态检查。'];
1327
+ return {
1328
+ severity: configLayerUnsupported ? 'warning' : 'error',
1329
+ title: issue.title,
1330
+ userMessage: issue.explanation,
1331
+ impact,
1332
+ nextStep,
1333
+ technical: {
1334
+ code,
1335
+ stage,
1336
+ ...(sourceDiagnostic && issue.sourceId ? { sourceId: issue.sourceId } : {}),
1337
+ ...(sourceDiagnostic && source?.requestedRef ? { requestedRef: source.requestedRef } : {}),
1338
+ ...(sourceDiagnostic && source?.resolvedCommit ? { resolvedCommit: source.resolvedCommit } : {}),
1339
+ ...(context.length > 0 ? { context } : {}),
1340
+ checks,
1341
+ },
1342
+ };
1343
+ }
1344
+ const issueGroupOrder = {
1345
+ sourceRemoved: 0,
1346
+ artifactCollision: 1,
1347
+ sourceUnavailable: 2,
1348
+ dependency: 3,
1349
+ scope: 4,
1350
+ config: 5,
1351
+ installation: 6,
1352
+ diagnostic: 7,
1353
+ };
1354
+ function issueGroup(issue) {
1355
+ if (issue.category === 'sourceMissing')
1356
+ return 'sourceRemoved';
1357
+ if (issue.category === 'artifactCollision')
1358
+ return 'artifactCollision';
1359
+ if (issue.category === 'sourceUnavailable'
1360
+ || issue.category === 'sourceIdentityChanged')
1361
+ return 'sourceUnavailable';
1362
+ if (issue.category.startsWith('dependency'))
1363
+ return 'dependency';
1364
+ if (issue.category === 'scopeConflict')
1365
+ return 'scope';
1366
+ if (issue.category === 'requiredConfigMissing' || issue.category === 'configLayerUnsupported')
1367
+ return 'config';
1368
+ if (issue.category === 'incompleteInstallation')
1369
+ return 'installation';
1370
+ return 'diagnostic';
1371
+ }
1372
+ function primaryAttentionCode(issue) {
1373
+ if (issue.category === 'requiredConfigMissing') {
1374
+ return issue.requiredFields?.some((field) => field.type === 'invalid-config')
1375
+ ? 'invalidConfig'
1376
+ : 'requiredConfigMissing';
1377
+ }
1378
+ if (issue.category === 'configLayerUnsupported')
1379
+ return 'configLayerUnsupported';
1380
+ if (issue.category === 'incompleteInstallation')
1381
+ return 'incompleteInstallation';
1382
+ if (issue.category === 'sourceIdentityChanged')
1383
+ return 'sourceUnavailable';
1384
+ return issue.rootCode ?? issue.category;
1385
+ }
1386
+ function issueReasonPriority(issue) {
1387
+ const code = primaryAttentionCode(issue);
1388
+ return {
1389
+ sourceMissing: 0,
1390
+ artifactCollision: 1,
1391
+ sourceUnavailable: 2,
1392
+ catalogSourceUnavailable: 2,
1393
+ bundleUnavailable: 2,
1394
+ codexIntentSourceUnavailable: 2,
1395
+ dependencyGraphInvalid: 3,
1396
+ dependencyTargetUnsupported: 4,
1397
+ dependencyNotReady: 5,
1398
+ dependencyMissing: 5,
1399
+ scopeConflict: 6,
1400
+ requiredConfigMissing: 7,
1401
+ invalidConfig: 7,
1402
+ incompleteInstallation: 8,
1403
+ diagnostic: 9,
1404
+ }[code] ?? 9;
1405
+ }
1406
+ function dedupeIssueActions(actions) {
1407
+ return [...new Map(actions.map((action) => [[
1408
+ action.id,
1409
+ action.pluginId ?? '',
1410
+ action.platform ?? '',
1411
+ action.scope ?? '',
1412
+ ].join('\0'), action])).values()];
1413
+ }
1414
+ function collapseRootIssues(issues) {
1415
+ const byId = new Map();
1416
+ for (const issue of issues)
1417
+ byId.set(issue.issueId, [...(byId.get(issue.issueId) ?? []), issue]);
1418
+ return [...byId.values()].map((group) => {
1419
+ const ordered = [...group].sort((left, right) => Number(right.relationship === 'direct') - Number(left.relationship === 'direct')
1420
+ || compareText(left.pluginId ?? '', right.pluginId ?? '')
1421
+ || compareText(left.platform ?? '', right.platform ?? '')
1422
+ || compareText(left.scope ?? '', right.scope ?? ''));
1423
+ const primary = ordered[0];
1424
+ const direct = ordered.find((issue) => issue.relationship === 'direct');
1425
+ const rootEntity = direct?.rootEntity ?? ordered.map((issue) => issue.rootEntity)
1426
+ .filter((entity) => entity !== undefined)
1427
+ .sort((left, right) => compareText(left.pluginId, right.pluginId)
1428
+ || compareText(left.platform, right.platform)
1429
+ || compareText(left.scope, right.scope))[0];
1430
+ const affectedTargets = [...new Map(ordered.flatMap((issue) => issue.affectedTargets ?? [])
1431
+ .map((target) => [`${target.pluginId}\0${target.platform}\0${target.scope}`, target])).values()]
1432
+ .sort((left, right) => compareText(left.pluginId, right.pluginId)
1433
+ || compareText(left.platform, right.platform)
1434
+ || compareText(left.scope, right.scope));
1435
+ const projected = rootEntity && !direct ? {
1436
+ ...primary,
1437
+ title: `${rootEntity.pluginId} · ${rootEntity.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${primary.category === 'artifactCollision' ? '输出冲突' : primary.title.split(' · ').at(-1) ?? primary.title}`,
1438
+ entity: { kind: 'plugin', id: rootEntity.pluginId },
1439
+ pluginId: rootEntity.pluginId,
1440
+ platform: rootEntity.platform,
1441
+ scope: rootEntity.scope,
1442
+ sourceId: rootEntity.pluginId.split('/', 1)[0],
1443
+ } : primary;
385
1444
  return {
386
- ...issue,
387
- dependencyPlatform: target.platform,
388
- dependencyScope: target.scope,
389
- dependencyProject: target.project,
390
- actions: issue.actions.map((action) => action.id === 'installDependency' || action.id === 'reinstallDependency'
391
- ? {
392
- ...action,
393
- platform: target.platform,
394
- scope: target.scope,
395
- project: target.project,
396
- }
397
- : action),
1445
+ ...projected,
1446
+ ...(rootEntity ? { rootEntity } : {}),
1447
+ ...(affectedTargets.length > 0 ? { affectedTargets } : {}),
1448
+ impact: [...new Set(ordered.flatMap((issue) => issue.impact))],
1449
+ technicalDetails: [...new Map(ordered.flatMap((issue) => issue.technicalDetails)
1450
+ .map((detail) => [`${detail.code}\0${detail.message}`, detail])).values()],
1451
+ actions: dedupeIssueActions(ordered.flatMap((issue) => issue.actions)),
398
1452
  };
399
1453
  });
400
1454
  }
@@ -402,23 +1456,71 @@ export function buildTuiViewModel(options) {
402
1456
  const dependencyById = new Map((options.dependencyOverviews ?? [])
403
1457
  .map((overview) => [overview.pluginId, overview]));
404
1458
  const summaryById = new Map(options.pluginSummaries.map((summary) => [summary.pluginId, summary]));
1459
+ const sourceById = new Map(options.status.catalog.sources.map((source) => [source.id, source]));
405
1460
  const targetsByPlugin = new Map();
406
- for (const target of options.status.targets) {
1461
+ for (const target of flattenStatusTargets(options.status.targets)) {
407
1462
  targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
408
1463
  }
409
- const pluginIds = new Set([...summaryById.keys(), ...targetsByPlugin.keys()]);
1464
+ const configScopesByPlugin = new Map();
1465
+ for (const store of options.configStoreInventory ?? []) {
1466
+ const scopes = configScopesByPlugin.get(store.pluginId) ?? new Set();
1467
+ scopes.add(store.scope);
1468
+ configScopesByPlugin.set(store.pluginId, scopes);
1469
+ }
1470
+ const pluginIds = new Set([
1471
+ ...summaryById.keys(),
1472
+ ...targetsByPlugin.keys(),
1473
+ ...configScopesByPlugin.keys(),
1474
+ ]);
410
1475
  const plugins = [...pluginIds].map((pluginId) => {
411
1476
  const summary = summaryById.get(pluginId);
412
1477
  const targets = targetsByPlugin.get(pluginId) ?? [];
413
1478
  const sourceId = targets[0]?.sourceId ?? pluginId.slice(0, pluginId.indexOf('/'));
1479
+ const savedConfigScopes = [...(configScopesByPlugin.get(pluginId) ?? [])]
1480
+ .sort((left, right) => (left === 'user' ? 0 : 1) - (right === 'user' ? 0 : 1));
1481
+ const configOnly = summary === undefined && targets.length === 0 && savedConfigScopes.length > 0;
414
1482
  const placements = ['codex', 'claude'].flatMap((platform) => {
415
1483
  const platformTargets = targets.filter((target) => target.platform === platform);
416
- return platformTargets.length > 0 ? [projectPlatformPlacement(platform, platformTargets)] : [];
1484
+ const aggregateConflict = platform === 'codex' && options.status.targets.some((target) => target.pluginId === pluginId
1485
+ && target.platform === platform
1486
+ && target.state === 'blocked'
1487
+ && target.reasons.some((reason) => reason.code === 'codexExtensionScopeConflict'));
1488
+ if (aggregateConflict) {
1489
+ const scoped = platformTargets.flatMap((target) => {
1490
+ const projected = projectPlatformPlacement(platform, [target], options.targetRevisions);
1491
+ return projected.state === 'installed' ? [{
1492
+ scope: projected.scope,
1493
+ health: projected.health,
1494
+ revision: projected.revision,
1495
+ }] : [];
1496
+ });
1497
+ return [{ platform, state: 'scopeConflict', placements: scoped }];
1498
+ }
1499
+ return platformTargets.length > 0
1500
+ ? [projectPlatformPlacement(platform, platformTargets, options.targetRevisions)]
1501
+ : [];
417
1502
  });
418
1503
  const attention = placements.some((placement) => placement.state === 'scopeConflict'
419
- || (placement.state === 'installed' && placement.health !== 'ready'));
420
- const installed = placements.some((placement) => placement.state !== 'notInstalled');
421
- const unavailable = options.sourceHealth?.get(sourceId)?.state === 'unavailable';
1504
+ || (placement.state === 'installed'
1505
+ && (placement.health !== 'ready' || placement.blockingReason !== undefined)));
1506
+ const installed = placements.some((placement) => placement.state === 'installed' || placement.state === 'scopeConflict');
1507
+ const sourceUnavailable = options.sourceHealth?.get(sourceId)?.state === 'unavailable'
1508
+ || sourceById.get(sourceId)?.error !== undefined;
1509
+ const capabilityTargets = targets.filter((target) => target.reasons.some((reason) => reason.code === 'awCapabilityUnsupported'));
1510
+ const capabilityUnsupported = (summary?.missingCapabilities?.length ?? 0) > 0
1511
+ || capabilityTargets.length > 0;
1512
+ const capabilityRecovery = capabilityTargets.flatMap((target) => target.recoveryActions)
1513
+ .find((action) => action.command === 'manual')?.description;
1514
+ const unavailable = sourceUnavailable || capabilityUnsupported || configOnly;
1515
+ const update = buildPluginUpdateSummary({
1516
+ pluginId,
1517
+ sourceId,
1518
+ targetRevisions: options.targetRevisions ?? [],
1519
+ targets,
1520
+ freshness: options.catalogFreshness?.get(sourceId),
1521
+ sourceIdentity: sourceById.get(sourceId)?.identity,
1522
+ generationKey: options.catalogGenerations?.get(sourceId)?.active?.storeKey,
1523
+ });
422
1524
  return {
423
1525
  pluginId,
424
1526
  sourceId,
@@ -433,9 +1535,21 @@ export function buildTuiViewModel(options) {
433
1535
  supportedPlatforms: summary?.supportedTargets
434
1536
  ? ['codex', 'claude'].filter((platform) => summary.supportedTargets[platform].length > 0)
435
1537
  : placements.map((placement) => placement.platform),
1538
+ ...(summary?.supportedTargets ? { supportedScopes: {
1539
+ codex: [...summary.supportedTargets.codex],
1540
+ claude: [...summary.supportedTargets.claude],
1541
+ } } : {}),
436
1542
  installedPlacements: placements,
437
1543
  aggregateStatus: unavailable ? 'unavailable' : attention ? 'needsAttention' : installed ? 'installed' : 'available',
438
1544
  portableSkills: options.portablePluginIds?.includes(pluginId) ?? false,
1545
+ ...(summary?.missingCapabilities
1546
+ ? { missingCapabilities: summary.missingCapabilities }
1547
+ : {}),
1548
+ ...(capabilityUnsupported ? { capabilityUnsupported: true } : {}),
1549
+ ...(capabilityRecovery ? { capabilityRecovery } : {}),
1550
+ ...(sourceUnavailable ? { sourceUnavailable: true } : {}),
1551
+ savedConfigScopes,
1552
+ ...(configOnly ? { configOnly: true } : {}),
439
1553
  ...(dependencyById.get(pluginId)
440
1554
  ? { dependencySummary: dependencyById.get(pluginId).summary }
441
1555
  : summary && (summary.dependencyTotalCount ?? 0) > 0
@@ -448,56 +1562,121 @@ export function buildTuiViewModel(options) {
448
1562
  issueCount: summary.dependencyIssueCount ?? 0,
449
1563
  } }
450
1564
  : {}),
451
- technicalComponents: targets.flatMap((target) => target.components.map((component) => ({
452
- platform: target.platform,
453
- scope: target.scope,
454
- key: component.key,
455
- state: component.state,
456
- ...(component.error ? { errorCode: component.error.code } : {}),
457
- }))),
1565
+ technicalComponents: [
1566
+ ...targets.flatMap((target) => [
1567
+ ...target.components,
1568
+ ...(target.sharedComponents ?? []).filter((component) => component.kind === 'systemConsumer'),
1569
+ ].map((component) => ({
1570
+ platform: target.platform,
1571
+ scope: target.scope,
1572
+ key: component.key,
1573
+ state: component.state,
1574
+ ...(typeof component.metadata?.errorCode === 'string'
1575
+ ? { errorCode: component.metadata.errorCode }
1576
+ : {}),
1577
+ }))),
1578
+ ...new Map(targets.flatMap((target) => (target.sharedComponents ?? [])
1579
+ .filter((component) => component.kind === 'sharedNative')
1580
+ .map((component) => [`${target.platform}\0${component.key}`, {
1581
+ platform: target.platform,
1582
+ shared: true,
1583
+ key: component.key,
1584
+ state: component.state,
1585
+ ...(typeof component.metadata?.errorCode === 'string'
1586
+ ? { errorCode: component.metadata.errorCode }
1587
+ : {}),
1588
+ }]))).values(),
1589
+ ],
1590
+ update,
458
1591
  };
459
1592
  }).sort((left, right) => compareText(left.displayName, right.displayName)
460
1593
  || compareText(left.sourceId, right.sourceId));
461
- const sourceTabs = options.status.catalog.sources.map((source) => ({
462
- id: source.id,
463
- label: source.id,
464
- count: plugins.filter((plugin) => plugin.sourceId === source.id).length,
465
- primary: source.primary,
466
- state: sourceState(source, options.sourceHealth?.get(source.id)),
467
- }));
1594
+ const sourceTabs = options.status.catalog.sources.map((source) => {
1595
+ const generation = options.catalogGenerations?.get(source.id);
1596
+ const countKnown = generation
1597
+ ? generation.state === 'ready' || generation.state === undefined && generation.active !== undefined
1598
+ : source.error === undefined;
1599
+ return {
1600
+ id: source.id,
1601
+ label: source.id,
1602
+ ...(countKnown ? { count: plugins.filter((plugin) => plugin.sourceId === source.id).length } : {}),
1603
+ primary: source.primary,
1604
+ state: projectCatalogSourceState(source, options.sourceHealth?.get(source.id), options.catalogFreshness?.get(source.id), generation),
1605
+ };
1606
+ });
468
1607
  const sourceRootIds = new Set([...(options.sourceHealth?.values() ?? [])]
469
- .filter((health) => health.state === 'cache' || health.state === 'degraded' || health.state === 'unavailable')
1608
+ .filter((health) => health.state === 'unavailable')
470
1609
  .map((health) => health.sourceId));
471
- const allTargetIssues = targetIssues(options.status);
472
- const targetImpactCounts = new Map();
1610
+ const allTargetIssues = targetIssues(options.status, options.targetRevisions);
1611
+ const mergedTargetIssues = new Map();
473
1612
  for (const issue of allTargetIssues) {
474
1613
  if (!issue.mergeEligible || !issue.sourceId || !sourceRootIds.has(issue.sourceId))
475
1614
  continue;
476
- targetImpactCounts.set(issue.sourceId, (targetImpactCounts.get(issue.sourceId) ?? 0) + 1);
1615
+ mergedTargetIssues.set(issue.sourceId, [...(mergedTargetIssues.get(issue.sourceId) ?? []), issue]);
477
1616
  }
478
- const issues = enrichDependencyIssuePlacements([
1617
+ const sourceDetails = new Map((options.sourceDetails ?? []).map((detail) => [detail.id, detail]));
1618
+ const issueDrafts = [
479
1619
  ...allTargetIssues.filter((issue) => !issue.mergeEligible
480
1620
  || !issue.sourceId || !sourceRootIds.has(issue.sourceId)),
481
1621
  ...statusIssues(options.status),
482
1622
  ...configIssues(options.pluginSummaries),
483
- ...[...(options.sourceHealth?.values() ?? [])].flatMap((health) => sourceIssue(health, targetImpactCounts.get(health.sourceId) ?? 0) ?? []),
484
- ], options.dependencyOverviews ?? []);
1623
+ ...dependencyOverviewIssues(options.dependencyOverviews ?? [], options.pluginSummaries, options.status),
1624
+ ...[...(options.sourceHealth?.values() ?? [])].flatMap((health) => sourceIssue(health, mergedTargetIssues.get(health.sourceId) ?? []) ?? []),
1625
+ ];
1626
+ const stableIssueDrafts = collapseRootIssues(issueDrafts).sort((left, right) => issueGroupOrder[issueGroup(left)] - issueGroupOrder[issueGroup(right)]
1627
+ || issueReasonPriority(left) - issueReasonPriority(right)
1628
+ || compareText(primaryAttentionCode(left), primaryAttentionCode(right))
1629
+ || compareText(left.issueId, right.issueId));
1630
+ const issues = stableIssueDrafts.map((issue) => ({
1631
+ ...issue,
1632
+ group: issueGroup(issue),
1633
+ diagnostic: issueDiagnostic(issue, sourceDetails),
1634
+ }));
1635
+ const pluginsWithAttention = plugins.map((plugin) => {
1636
+ const primary = stableIssueDrafts.filter((issue) => issue.pluginId === plugin.pluginId
1637
+ || issue.dependencyPluginId === plugin.pluginId
1638
+ || issue.affectedTargets?.some((target) => target.pluginId === plugin.pluginId)
1639
+ || issue.entity.kind === 'source' && issue.sourceId === plugin.sourceId)
1640
+ .sort((left, right) => issueReasonPriority(left) - issueReasonPriority(right)
1641
+ || compareText(primaryAttentionCode(left), primaryAttentionCode(right))
1642
+ || compareText(left.issueId, right.issueId))[0];
1643
+ if (!primary)
1644
+ return plugin;
1645
+ return {
1646
+ ...plugin,
1647
+ primaryAttention: {
1648
+ severity: primary.category === 'configLayerUnsupported' ? 'warning' : 'error',
1649
+ code: primaryAttentionCode(primary),
1650
+ message: primary.explanation,
1651
+ },
1652
+ };
1653
+ });
485
1654
  return {
486
- plugins,
1655
+ plugins: pluginsWithAttention,
487
1656
  sourceTabs,
488
- sourceDetails: (options.sourceDetails ?? []).map((detail) => ({
489
- ...detail,
490
- pluginCount: plugins.filter((plugin) => plugin.sourceId === detail.id).length,
491
- })),
1657
+ sourceDetails: (options.sourceDetails ?? []).map((detail) => {
1658
+ const count = sourceTabs.find((source) => source.id === detail.id)?.count;
1659
+ return { ...detail, ...(count === undefined ? {} : { pluginCount: count }) };
1660
+ }),
492
1661
  issues,
493
1662
  dependencyOverviews: options.dependencyOverviews ?? [],
1663
+ contentOverviews: options.contentOverviews ?? {},
494
1664
  home: {
495
- availablePlugins: plugins.filter((plugin) => plugin.aggregateStatus === 'available').length,
496
- installedPlugins: plugins.filter(hasInstalledPlacement).length,
1665
+ availablePlugins: pluginsWithAttention.filter((plugin) => plugin.aggregateStatus === 'available').length,
1666
+ installedPlugins: new Set(pluginsWithAttention
1667
+ .filter(hasInstalledPlacement)
1668
+ .map((plugin) => plugin.pluginId)).size,
497
1669
  issueCount: issues.length,
498
1670
  sourceCount: sourceTabs.length,
499
- cachedSourceCount: sourceTabs.filter((source) => source.state === 'cache').length,
500
- unavailableSourceCount: sourceTabs.filter((source) => source.state === 'error').length,
1671
+ unavailableSourceCount: options.status.catalog.sources.filter((source) => source.error
1672
+ || options.sourceHealth?.get(source.id)?.state === 'unavailable').length,
1673
+ catalogUpdateCount: sourceTabs.filter((source) => source.state === 'update').length,
1674
+ catalogRecoveryCount: sourceTabs.filter((source) => source.state === 'recovery'
1675
+ || source.state === 'error').length,
1676
+ catalogCheckingCount: sourceTabs.filter((source) => source.state === 'checking').length,
1677
+ pluginUpdateCount: pluginsWithAttention.filter((plugin) => plugin.update.state === 'available').length,
1678
+ directPluginUpdateCount: pluginsWithAttention.filter((plugin) => plugin.update.targets.some((target) => target.availableVersion !== undefined && target.requiresCatalogActivation !== true
1679
+ && target.eligibility === 'allowed')).length,
501
1680
  },
502
1681
  };
503
1682
  }
@@ -506,6 +1685,22 @@ function displayConfigValue(options) {
506
1685
  return options.inherited ? '继承' : '未设置';
507
1686
  if (options.privateValue)
508
1687
  return '已设置';
1688
+ if (options.field.type === 'object'
1689
+ && typeof options.value === 'object'
1690
+ && options.value !== null
1691
+ && !Array.isArray(options.value)) {
1692
+ const object = options.value;
1693
+ if (configObjectHasSensitiveChild(options.field)) {
1694
+ const count = Object.keys(object).length;
1695
+ return `已设置 · ${count === 0 ? '空对象' : `${count} 项`}`;
1696
+ }
1697
+ const preset = matchPluginConfigPreset(options.field, object);
1698
+ const keys = Object.keys(object).sort((left, right) => left.localeCompare(right, 'en'));
1699
+ const keySummary = keys.length === 0
1700
+ ? '空对象'
1701
+ : `${keys.length} 项${keys.length > 0 ? `(${keys.slice(0, 2).join('、')}${keys.length > 2 ? '…' : ''})` : ''}`;
1702
+ return preset ? `方案“${preset.title}” · ${keySummary}` : `自定义 · ${keySummary}`;
1703
+ }
509
1704
  if (typeof options.value === 'string')
510
1705
  return options.value;
511
1706
  if (options.value === undefined || options.value === null)
@@ -519,57 +1714,220 @@ function configSourceLabel(source) {
519
1714
  default: '默认值',
520
1715
  user: '个人默认',
521
1716
  project: '当前项目',
522
- local: '当前项目(仅本机)',
523
1717
  }[source];
524
1718
  }
1719
+ function configEditorDefinition(field) {
1720
+ const definition = structuredClone(field);
1721
+ delete definition.default;
1722
+ if (definition.sensitive === true)
1723
+ delete definition.choices;
1724
+ return definition;
1725
+ }
1726
+ function configObjectChildChanges(field, before, after) {
1727
+ const fixed = Object.keys(field.properties ?? {});
1728
+ const summary = (property, value) => {
1729
+ if (value === undefined)
1730
+ return '未设置';
1731
+ const definition = field.properties?.[property]
1732
+ ?? (field.additionalProperties === false ? undefined : field.additionalProperties);
1733
+ if (field.sensitive || definition?.sensitive)
1734
+ return '已设置';
1735
+ if (typeof value === 'boolean')
1736
+ return value ? '开启' : '关闭';
1737
+ return String(value);
1738
+ };
1739
+ return [...new Set([...Object.keys(before), ...Object.keys(after ?? {})])]
1740
+ .sort((left, right) => {
1741
+ const leftIndex = fixed.indexOf(left);
1742
+ const rightIndex = fixed.indexOf(right);
1743
+ if (leftIndex >= 0 || rightIndex >= 0) {
1744
+ if (leftIndex < 0)
1745
+ return 1;
1746
+ if (rightIndex < 0)
1747
+ return -1;
1748
+ return leftIndex - rightIndex;
1749
+ }
1750
+ return left.localeCompare(right, 'en');
1751
+ })
1752
+ .filter((property) => before[property] !== after?.[property])
1753
+ .map((property) => {
1754
+ const definition = field.properties?.[property]
1755
+ ?? (field.additionalProperties === false ? undefined : field.additionalProperties);
1756
+ return {
1757
+ property,
1758
+ before: summary(property, before[property]),
1759
+ after: summary(property, after?.[property]),
1760
+ ...(field.sensitive || definition?.sensitive ? { private: true } : {}),
1761
+ };
1762
+ });
1763
+ }
525
1764
  export function buildConfigGroups(options) {
526
1765
  const entryByKey = new Map(options.read.fields.map((field) => [field.key, field]));
1766
+ const effectiveEntryByKey = new Map((options.effectiveRead ?? options.read).fields.map((field) => [field.key, field]));
527
1767
  const effectivePage = options.read.scope === 'effective';
1768
+ const layerEntry = (layer, key) => options.layerReads?.[layer]?.fields.find((field) => field.key === key);
528
1769
  const fieldModel = (key) => {
529
- const schema = options.schema.schema[key];
1770
+ const schema = options.schema.fields[key];
530
1771
  const entry = entryByKey.get(key);
531
- const presentation = options.schema.presentation?.fields?.[key];
1772
+ const effectiveEntry = effectiveEntryByKey.get(key);
532
1773
  const inherited = effectivePage
533
- ? entry?.sourceLayer === undefined || entry?.sourceLayer === null
1774
+ ? false
534
1775
  : entry?.storedSourceLayer === undefined;
535
1776
  const configured = effectivePage
536
- ? entry?.valueState === 'configured' || entry?.valueState === 'default'
1777
+ ? entry?.sourceLayer !== undefined
1778
+ && entry.sourceLayer !== null
1779
+ && entry.effectiveValue !== undefined
1780
+ && entry.effectiveValue !== null
1781
+ && (entry.valueState === 'configured' || entry.valueState === 'default')
537
1782
  : entry?.storedSourceLayer !== undefined;
538
- const privateValue = schema.sensitive === true || schema.type === 'file' || schema.type === 'directory';
1783
+ const privateValue = schema.sensitive === true;
1784
+ const choices = schema.choices;
539
1785
  const selectedValue = effectivePage ? entry?.effectiveValue : entry?.storedValue;
1786
+ const selectedRawValue = effectivePage
1787
+ ? options.effectiveRawValues?.[key]
1788
+ : options.rawValues?.[key];
1789
+ const selectedStoredInvalid = !effectivePage
1790
+ && entry?.storedSourceLayer !== undefined
1791
+ && entry.storedValue === undefined;
1792
+ const invalidOverrideSourceCandidate = selectedStoredInvalid
1793
+ ? (effectivePage ? entry?.invalidOverride?.sourceLayer : options.read.scope)
1794
+ : entry?.invalidOverride?.sourceLayer;
1795
+ const invalidOverrideSource = invalidOverrideSourceCandidate === 'user'
1796
+ || invalidOverrideSourceCandidate === 'project'
1797
+ ? invalidOverrideSourceCandidate
1798
+ : undefined;
1799
+ const invalidOverride = invalidOverrideSource !== undefined;
1800
+ const invalidOverrideOwnedByLayer = !effectivePage
1801
+ && invalidOverrideSource === options.read.scope;
1802
+ const effectiveConfigured = effectiveEntry?.sourceLayer !== undefined
1803
+ && effectiveEntry.sourceLayer !== null
1804
+ && effectiveEntry.effectiveValue !== undefined
1805
+ && effectiveEntry.effectiveValue !== null
1806
+ && (effectiveEntry.valueState === 'configured' || effectiveEntry.valueState === 'default');
1807
+ const omittedLayer = options.read.scope === 'user'
1808
+ ? 'user'
1809
+ : 'project';
1810
+ const fallback = options.read.scope === 'user'
1811
+ ? undefined
1812
+ : ['project', 'user']
1813
+ .filter((layer) => layer !== omittedLayer)
1814
+ .map((layer) => ({ layer, entry: layerEntry(layer, key) }))
1815
+ .find((candidate) => candidate.entry?.storedSourceLayer !== undefined);
1816
+ const projectedFallback = entry?.fallback;
1817
+ const fallbackInvalid = projectedFallback?.state === 'invalid';
1818
+ const fallbackValue = projectedFallback?.state === 'valid'
1819
+ ? projectedFallback.effectiveValue
1820
+ : projectedFallback
1821
+ ? undefined
1822
+ : fallback?.entry?.storedValue ?? schema.default;
1823
+ const fallbackRawValue = projectedFallback?.state === 'valid'
1824
+ ? projectedFallback.sourceLayer === 'user'
1825
+ ? options.rawLayerValues?.user?.[key] ?? fallbackValue
1826
+ : fallbackValue
1827
+ : projectedFallback
1828
+ ? undefined
1829
+ : fallback
1830
+ ? options.rawLayerValues?.[fallback.layer]?.[key] ?? fallbackValue
1831
+ : schema.default;
1832
+ const fallbackConfigured = projectedFallback
1833
+ ? projectedFallback.state !== 'missing'
1834
+ : fallback !== undefined || schema.default !== undefined;
1835
+ const fallbackSource = projectedFallback?.state === 'valid'
1836
+ || projectedFallback?.state === 'invalid'
1837
+ ? projectedFallback.sourceLayer
1838
+ : projectedFallback
1839
+ ? undefined
1840
+ : fallback?.layer ?? (schema.default !== undefined ? 'default' : undefined);
540
1841
  return {
541
1842
  key,
542
1843
  title: schema.title ?? key,
543
1844
  ...(schema.description ? { description: schema.description } : {}),
544
1845
  type: schema.type,
545
- displayValue: displayConfigValue({
1846
+ definition: configEditorDefinition(schema),
1847
+ displayValue: selectedStoredInvalid ? '值无效' : displayConfigValue({
1848
+ field: schema,
546
1849
  privateValue,
547
1850
  inherited,
548
1851
  configured,
549
- value: selectedValue,
1852
+ value: schema.type === 'object' ? selectedRawValue ?? selectedValue : selectedValue,
550
1853
  }),
551
1854
  required: schema.required === true,
552
1855
  sensitive: schema.sensitive === true,
1856
+ writable: (entry?.writable ?? effectivePage)
1857
+ && !(options.read.scope === 'project' && privateValue),
1858
+ canSet: (entry?.canSet ?? effectivePage)
1859
+ && !(options.read.scope === 'project' && privateValue),
1860
+ canUnset: entry?.canUnset ?? false,
553
1861
  dirty: options.dirtyKeys?.has(key) === true,
554
1862
  inherited,
555
1863
  configured,
556
1864
  sourceLabel: effectivePage
557
1865
  ? configSourceLabel(entry?.sourceLayer ?? null)
558
1866
  : configured ? '本层已设置' : '继承',
1867
+ effectiveDisplayValue: displayConfigValue({
1868
+ field: schema,
1869
+ privateValue,
1870
+ inherited: false,
1871
+ configured: effectiveConfigured,
1872
+ value: schema.type === 'object'
1873
+ ? options.effectiveRawValues?.[key] ?? effectiveEntry?.effectiveValue
1874
+ : effectiveEntry?.effectiveValue,
1875
+ }),
1876
+ effectiveSourceLabel: configSourceLabel(effectiveEntry?.sourceLayer ?? null),
1877
+ ...(invalidOverride ? {
1878
+ invalidOverrideOwnedByLayer,
1879
+ invalidOverrideIssue: invalidOverrideOwnedByLayer
1880
+ ? schema.type === 'object'
1881
+ ? `字段“${schema.title ?? key}”在本层的对象值无效。请修改或删除此字段。`
1882
+ : `字段“${schema.title ?? key}”在本层的值无效。请修改或删除此字段。`
1883
+ : `字段“${schema.title ?? key}”在${configSourceLabel(invalidOverrideSource ?? null)}中的值无效。请切换到该设置层修改或删除此字段。`,
1884
+ } : {}),
1885
+ ...(!effectivePage && configured ? {
1886
+ fallbackDisplayValue: fallbackInvalid
1887
+ ? '值无效;保存可成功,但应用会被阻止'
1888
+ : displayConfigValue({
1889
+ field: schema,
1890
+ privateValue,
1891
+ inherited: false,
1892
+ configured: fallbackConfigured,
1893
+ value: schema.type === 'object' ? fallbackRawValue : fallbackValue,
1894
+ }),
1895
+ fallbackSourceLabel: fallbackSource ? configSourceLabel(fallbackSource) : '未设置',
1896
+ ...(!fallbackInvalid && schema.type === 'object'
1897
+ && typeof (selectedRawValue ?? selectedValue) === 'object'
1898
+ && (selectedRawValue ?? selectedValue) !== null
1899
+ ? { fallbackObjectChanges: configObjectChildChanges(schema, (selectedRawValue ?? selectedValue), typeof fallbackRawValue === 'object' && fallbackRawValue !== null
1900
+ ? fallbackRawValue
1901
+ : undefined) }
1902
+ : {}),
1903
+ } : {}),
559
1904
  ...(!privateValue && selectedValue !== undefined
560
1905
  ? { value: selectedValue }
561
1906
  : {}),
1907
+ ...(!privateValue && effectiveEntry?.effectiveValue !== undefined && effectiveEntry.effectiveValue !== null
1908
+ ? { effectiveValue: effectiveEntry.effectiveValue }
1909
+ : {}),
1910
+ ...(!privateValue && (effectivePage
1911
+ ? options.effectiveRawValues?.[key]
1912
+ : selectedRawValue ?? (schema.type === 'object' && !configured
1913
+ ? options.effectiveRawValues?.[key]
1914
+ : undefined)) !== undefined
1915
+ ? { draftValue: (effectivePage
1916
+ ? options.effectiveRawValues?.[key]
1917
+ : selectedRawValue ?? options.effectiveRawValues?.[key]) }
1918
+ : {}),
562
1919
  ...(!privateValue && schema.default !== undefined ? { defaultValue: schema.default } : {}),
563
- ...(schema.values ? { enumValues: [...schema.values] } : {}),
564
- ...(presentation?.valueLabels ? { valueLabels: { ...presentation.valueLabels } } : {}),
565
- ...(presentation?.recommendedValue !== undefined
566
- ? { recommendedValue: presentation.recommendedValue }
1920
+ ...(!privateValue && choices ? { choices: choices.presets } : {}),
1921
+ allowCustom: choices?.allowCustom ?? true,
1922
+ ...(!privateValue && !configObjectHasSensitiveChild(schema)
1923
+ && (selectedRawValue ?? selectedValue) !== undefined && (selectedRawValue ?? selectedValue) !== null
1924
+ && matchPluginConfigPreset(schema, (selectedRawValue ?? selectedValue))
1925
+ ? { matchedPresetId: matchPluginConfigPreset(schema, (selectedRawValue ?? selectedValue)).id }
567
1926
  : {}),
568
- ...(presentation?.placeholder ? { placeholder: presentation.placeholder } : {}),
569
- ...(presentation?.unit ? { unit: presentation.unit } : {}),
570
- ...(schema.min !== undefined ? { min: schema.min } : {}),
571
- ...(schema.max !== undefined ? { max: schema.max } : {}),
572
- advanced: presentation?.advanced === true,
1927
+ ...(schema.type === 'number' && schema.validation?.minimum !== undefined ? { min: schema.validation.minimum } : {}),
1928
+ ...(schema.type === 'number' && schema.validation?.maximum !== undefined ? { max: schema.validation.maximum } : {}),
1929
+ advanced: false,
1930
+ sharedNativeRender: options.sharedNativeRefreshKeys?.has(key) === true,
573
1931
  };
574
1932
  };
575
1933
  const assigned = new Set();
@@ -593,18 +1951,20 @@ export function buildConfigGroups(options) {
593
1951
  }
594
1952
  return id;
595
1953
  };
596
- const remaining = Object.keys(options.schema.schema).filter((key) => !assigned.has(key));
1954
+ const remaining = Object.keys(options.schema.fields)
1955
+ .filter((key) => !assigned.has(key));
597
1956
  if (!options.schema.presentation) {
598
1957
  groups.push({
599
1958
  id: 'general',
600
1959
  title: '插件设置',
601
1960
  advanced: false,
602
- fields: Object.keys(options.schema.schema).map(fieldModel),
1961
+ fields: Object.keys(options.schema.fields)
1962
+ .map(fieldModel),
603
1963
  });
604
1964
  }
605
1965
  else {
606
- const ordinary = remaining.filter((key) => options.schema.presentation?.fields?.[key]?.advanced !== true);
607
- const advanced = remaining.filter((key) => options.schema.presentation?.fields?.[key]?.advanced === true);
1966
+ const ordinary = remaining;
1967
+ const advanced = [];
608
1968
  if (ordinary.length > 0) {
609
1969
  groups.push({
610
1970
  id: syntheticGroupId('general'),
@@ -625,28 +1985,16 @@ export function buildConfigGroups(options) {
625
1985
  return groups;
626
1986
  }
627
1987
  export function buildConfigPage(options) {
628
- const privateFields = new Set(Object.entries(options.schema.schema)
629
- .filter(([, field]) => field.sensitive === true || field.type === 'file' || field.type === 'directory')
630
- .map(([key]) => key));
631
- const presets = (options.schema.presentation?.presets ?? []).map((preset) => {
632
- const privateFieldTitles = Object.keys(preset.values)
633
- .filter((key) => privateFields.has(key))
634
- .map((key) => options.schema.schema[key]?.title ?? key);
635
- return {
636
- id: preset.id,
637
- title: preset.title,
638
- ...(preset.description ? { description: preset.description } : {}),
639
- values: Object.fromEntries(Object.entries(preset.values)
640
- .filter(([key]) => !privateFields.has(key))),
641
- ...(privateFieldTitles.length > 0 ? { privateFieldTitles } : {}),
642
- };
643
- });
644
1988
  return {
645
1989
  pluginId: options.pluginId,
646
- layer: { scope: options.read.scope, projectLayer: options.read.projectLayer },
1990
+ layer: { scope: options.read.scope },
647
1991
  groups: buildConfigGroups(options),
648
- presets,
1992
+ presets: [],
649
1993
  storedCount: options.read.fields.filter((field) => field.storedSourceLayer !== undefined).length,
650
1994
  };
651
1995
  }
1996
+ export function configValuesEqual(left, right) {
1997
+ return left !== undefined && right !== undefined
1998
+ && stablePluginConfigStringify(left) === stablePluginConfigStringify(right);
1999
+ }
652
2000
  //# sourceMappingURL=model.js.map