@lesliechan721/aw 0.1.0-beta.6 → 0.1.0-beta.60

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