@lesliechan721/aw 0.1.0-beta.4 → 0.1.0-beta.40

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 (571) 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-version-check.d.mts +15 -0
  11. package/dist/authoring/scripts/lib/plugin-version-check.mjs +239 -232
  12. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  13. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
  14. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
  15. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  16. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  17. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  18. package/dist/authoring/scripts/remove-plugin.mjs +367 -0
  19. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  20. package/dist/authoring/scripts/validate-plugin.mjs +353 -1175
  21. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  22. package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
  23. package/dist/scripts/aw-skill-eval-materializer.js +306 -0
  24. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  25. package/dist/scripts/bundle-plugins.d.ts +1 -2
  26. package/dist/scripts/bundle-plugins.js +24 -7
  27. package/dist/scripts/bundle-plugins.js.map +1 -1
  28. package/dist/scripts/plugin-staging.d.ts +7 -24
  29. package/dist/scripts/plugin-staging.js +22 -142
  30. package/dist/scripts/plugin-staging.js.map +1 -1
  31. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  32. package/dist/scripts/test-plugin-scripts.js +30 -75
  33. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  34. package/dist/src/adapters/helpers.js +2 -2
  35. package/dist/src/adapters/helpers.js.map +1 -1
  36. package/dist/src/authoring/workspace-commands.d.ts +26 -2
  37. package/dist/src/authoring/workspace-commands.js +44 -19
  38. package/dist/src/authoring/workspace-commands.js.map +1 -1
  39. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  40. package/dist/src/commands/aw-capabilities.js +263 -0
  41. package/dist/src/commands/aw-capabilities.js.map +1 -0
  42. package/dist/src/commands/catalog-dev-source.d.ts +154 -0
  43. package/dist/src/commands/catalog-dev-source.js +655 -0
  44. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  45. package/dist/src/commands/catalog-generation.d.ts +21 -0
  46. package/dist/src/commands/catalog-generation.js +9 -0
  47. package/dist/src/commands/catalog-generation.js.map +1 -0
  48. package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
  49. package/dist/src/commands/catalog-publication-inspection.js +757 -0
  50. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  51. package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
  52. package/dist/src/commands/catalog-publication-journal.js +292 -0
  53. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  54. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  55. package/dist/src/commands/catalog-publication-target.js +95 -0
  56. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  57. package/dist/src/commands/catalog-publish.d.ts +77 -7
  58. package/dist/src/commands/catalog-publish.js +746 -316
  59. package/dist/src/commands/catalog-publish.js.map +1 -1
  60. package/dist/src/commands/catalog-source.d.ts +60 -22
  61. package/dist/src/commands/catalog-source.js +311 -91
  62. package/dist/src/commands/catalog-source.js.map +1 -1
  63. package/dist/src/commands/catalog-status.d.ts +56 -0
  64. package/dist/src/commands/catalog-status.js +217 -0
  65. package/dist/src/commands/catalog-status.js.map +1 -0
  66. package/dist/src/commands/catalog-store.d.ts +104 -0
  67. package/dist/src/commands/catalog-store.js +685 -0
  68. package/dist/src/commands/catalog-store.js.map +1 -0
  69. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  70. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  71. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  72. package/dist/src/commands/console.js +28 -17
  73. package/dist/src/commands/console.js.map +1 -1
  74. package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
  75. package/dist/src/commands/dependency-operation-plan.js +289 -7
  76. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  77. package/dist/src/commands/init.d.ts +5 -11
  78. package/dist/src/commands/init.js +30 -51
  79. package/dist/src/commands/init.js.map +1 -1
  80. package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
  81. package/dist/src/commands/offline-cleanup-evidence.js +181 -0
  82. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  83. package/dist/src/commands/operation-plan.d.ts +146 -0
  84. package/dist/src/commands/operation-plan.js +71 -0
  85. package/dist/src/commands/operation-plan.js.map +1 -0
  86. package/dist/src/commands/operation-planner.d.ts +46 -0
  87. package/dist/src/commands/operation-planner.js +141 -0
  88. package/dist/src/commands/operation-planner.js.map +1 -0
  89. package/dist/src/commands/options.d.ts +9 -7
  90. package/dist/src/commands/options.js +83 -21
  91. package/dist/src/commands/options.js.map +1 -1
  92. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  93. package/dist/src/commands/plugin-capabilities.js +163 -0
  94. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  95. package/dist/src/commands/rebuild-command.d.ts +78 -30
  96. package/dist/src/commands/rebuild-command.js +4877 -713
  97. package/dist/src/commands/rebuild-command.js.map +1 -1
  98. package/dist/src/commands/rebuild-integration.d.ts +132 -21
  99. package/dist/src/commands/rebuild-integration.js +2853 -1327
  100. package/dist/src/commands/rebuild-integration.js.map +1 -1
  101. package/dist/src/commands/rebuild-operations.d.ts +58 -30
  102. package/dist/src/commands/rebuild-operations.js +260 -319
  103. package/dist/src/commands/rebuild-operations.js.map +1 -1
  104. package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
  105. package/dist/src/commands/reconcile-operation-plan.js +47 -0
  106. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  107. package/dist/src/commands/review-inspection.d.ts +228 -0
  108. package/dist/src/commands/review-inspection.js +1341 -0
  109. package/dist/src/commands/review-inspection.js.map +1 -0
  110. package/dist/src/commands/review-preparation.d.ts +49 -0
  111. package/dist/src/commands/review-preparation.js +427 -0
  112. package/dist/src/commands/review-preparation.js.map +1 -0
  113. package/dist/src/commands/runtime.d.ts +1 -5
  114. package/dist/src/commands/runtime.js +24 -50
  115. package/dist/src/commands/runtime.js.map +1 -1
  116. package/dist/src/commands/skill-eval.d.ts +39 -0
  117. package/dist/src/commands/skill-eval.js +400 -0
  118. package/dist/src/commands/skill-eval.js.map +1 -0
  119. package/dist/src/commands/status-v6.d.ts +279 -0
  120. package/dist/src/commands/status-v6.js +2787 -0
  121. package/dist/src/commands/status-v6.js.map +1 -0
  122. package/dist/src/commands/sync-command.d.ts +40 -0
  123. package/dist/src/commands/sync-command.js +250 -0
  124. package/dist/src/commands/sync-command.js.map +1 -0
  125. package/dist/src/commands/wizard.d.ts +8 -5
  126. package/dist/src/commands/wizard.js +6 -4
  127. package/dist/src/commands/wizard.js.map +1 -1
  128. package/dist/src/commands/write-commands.d.ts +9 -3
  129. package/dist/src/commands/write-commands.js +127 -31
  130. package/dist/src/commands/write-commands.js.map +1 -1
  131. package/dist/src/constants.d.ts +7 -1
  132. package/dist/src/constants.js +7 -1
  133. package/dist/src/constants.js.map +1 -1
  134. package/dist/src/eval/manifest-v1.d.ts +71 -0
  135. package/dist/src/eval/manifest-v1.js +370 -0
  136. package/dist/src/eval/manifest-v1.js.map +1 -0
  137. package/dist/src/generators/local-plugin.d.ts +5 -1
  138. package/dist/src/generators/local-plugin.js +228 -104
  139. package/dist/src/generators/local-plugin.js.map +1 -1
  140. package/dist/src/index.d.ts +10 -4
  141. package/dist/src/index.js +1835 -469
  142. package/dist/src/index.js.map +1 -1
  143. package/dist/src/output/command-receipt.d.ts +43 -46
  144. package/dist/src/output/command-receipt.js +256 -80
  145. package/dist/src/output/command-receipt.js.map +1 -1
  146. package/dist/src/output/model.d.ts +3 -44
  147. package/dist/src/output/model.js +1 -144
  148. package/dist/src/output/model.js.map +1 -1
  149. package/dist/src/output/rebuild-progress.js +1 -13
  150. package/dist/src/output/rebuild-progress.js.map +1 -1
  151. package/dist/src/portable/commands.d.ts +46 -7
  152. package/dist/src/portable/commands.js +349 -37
  153. package/dist/src/portable/commands.js.map +1 -1
  154. package/dist/src/portable/materializer.d.ts +17 -4
  155. package/dist/src/portable/materializer.js +179 -41
  156. package/dist/src/portable/materializer.js.map +1 -1
  157. package/dist/src/portable/skills-cli.d.ts +15 -7
  158. package/dist/src/portable/skills-cli.js +66 -19
  159. package/dist/src/portable/skills-cli.js.map +1 -1
  160. package/dist/src/review/eval-source-input.d.ts +43 -0
  161. package/dist/src/review/eval-source-input.js +207 -0
  162. package/dist/src/review/eval-source-input.js.map +1 -0
  163. package/dist/src/review/review-profile.d.ts +17 -0
  164. package/dist/src/review/review-profile.js +234 -0
  165. package/dist/src/review/review-profile.js.map +1 -0
  166. package/dist/src/review/review-selection.d.ts +30 -0
  167. package/dist/src/review/review-selection.js +527 -0
  168. package/dist/src/review/review-selection.js.map +1 -0
  169. package/dist/src/review/review-types.d.ts +68 -0
  170. package/dist/src/review/review-types.js +16 -0
  171. package/dist/src/review/review-types.js.map +1 -0
  172. package/dist/src/review/skill-materialization.d.ts +13 -0
  173. package/dist/src/review/skill-materialization.js +75 -0
  174. package/dist/src/review/skill-materialization.js.map +1 -0
  175. package/dist/src/review/source-binding.d.ts +183 -0
  176. package/dist/src/review/source-binding.js +708 -0
  177. package/dist/src/review/source-binding.js.map +1 -0
  178. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  179. package/dist/src/tui/catalog-freshness.js +2 -0
  180. package/dist/src/tui/catalog-freshness.js.map +1 -0
  181. package/dist/src/tui/catalog-generation.d.ts +21 -0
  182. package/dist/src/tui/catalog-generation.js +156 -0
  183. package/dist/src/tui/catalog-generation.js.map +1 -0
  184. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  185. package/dist/src/tui/catalog-sources/projection.js +17 -0
  186. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  187. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  188. package/dist/src/tui/catalog-sources/review.js +13 -0
  189. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  190. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  191. package/dist/src/tui/catalog-sources/screens.js +40 -0
  192. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  193. package/dist/src/tui/catalog-sources/session.d.ts +32 -0
  194. package/dist/src/tui/catalog-sources/session.js +300 -0
  195. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  196. package/dist/src/tui/catalog-sources/types.d.ts +54 -0
  197. package/dist/src/tui/catalog-sources/types.js +2 -0
  198. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  199. package/dist/src/tui/catalog-store-task.d.ts +16 -0
  200. package/dist/src/tui/catalog-store-task.js +19 -0
  201. package/dist/src/tui/catalog-store-task.js.map +1 -0
  202. package/dist/src/tui/clipboard.d.ts +13 -0
  203. package/dist/src/tui/clipboard.js +95 -0
  204. package/dist/src/tui/clipboard.js.map +1 -0
  205. package/dist/src/tui/components.d.ts +177 -0
  206. package/dist/src/tui/components.js +297 -7
  207. package/dist/src/tui/components.js.map +1 -1
  208. package/dist/src/tui/config-array-draft.d.ts +29 -0
  209. package/dist/src/tui/config-array-draft.js +48 -0
  210. package/dist/src/tui/config-array-draft.js.map +1 -0
  211. package/dist/src/tui/config-leaf-editor.d.ts +16 -0
  212. package/dist/src/tui/config-leaf-editor.js +206 -0
  213. package/dist/src/tui/config-leaf-editor.js.map +1 -0
  214. package/dist/src/tui/config-value-presentation.d.ts +20 -0
  215. package/dist/src/tui/config-value-presentation.js +104 -0
  216. package/dist/src/tui/config-value-presentation.js.map +1 -0
  217. package/dist/src/tui/console-app-v4.d.ts +14 -4
  218. package/dist/src/tui/console-app-v4.js +9659 -1558
  219. package/dist/src/tui/console-app-v4.js.map +1 -1
  220. package/dist/src/tui/content-item-card.d.ts +13 -0
  221. package/dist/src/tui/content-item-card.js +51 -0
  222. package/dist/src/tui/content-item-card.js.map +1 -0
  223. package/dist/src/tui/controller-runtime.d.ts +13 -1
  224. package/dist/src/tui/controller-runtime.js +3323 -349
  225. package/dist/src/tui/controller-runtime.js.map +1 -1
  226. package/dist/src/tui/controller.d.ts +643 -40
  227. package/dist/src/tui/controller.js +3756 -705
  228. package/dist/src/tui/controller.js.map +1 -1
  229. package/dist/src/tui/dependency-graph-view.d.ts +39 -0
  230. package/dist/src/tui/dependency-graph-view.js +121 -0
  231. package/dist/src/tui/dependency-graph-view.js.map +1 -0
  232. package/dist/src/tui/dependency-graph.d.ts +81 -0
  233. package/dist/src/tui/dependency-graph.js +452 -0
  234. package/dist/src/tui/dependency-graph.js.map +1 -0
  235. package/dist/src/tui/diagnostic-projection.d.ts +23 -0
  236. package/dist/src/tui/diagnostic-projection.js +567 -0
  237. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  238. package/dist/src/tui/diagnostics.d.ts +31 -0
  239. package/dist/src/tui/diagnostics.js +83 -0
  240. package/dist/src/tui/diagnostics.js.map +1 -0
  241. package/dist/src/tui/entity-cards.d.ts +55 -0
  242. package/dist/src/tui/entity-cards.js +220 -0
  243. package/dist/src/tui/entity-cards.js.map +1 -0
  244. package/dist/src/tui/glossary.d.ts +23 -0
  245. package/dist/src/tui/glossary.js +42 -0
  246. package/dist/src/tui/glossary.js.map +1 -0
  247. package/dist/src/tui/index.d.ts +11 -4
  248. package/dist/src/tui/index.js +8 -2
  249. package/dist/src/tui/index.js.map +1 -1
  250. package/dist/src/tui/input-display.d.ts +9 -0
  251. package/dist/src/tui/input-display.js +58 -0
  252. package/dist/src/tui/input-display.js.map +1 -0
  253. package/dist/src/tui/keys.d.ts +44 -0
  254. package/dist/src/tui/keys.js +43 -15
  255. package/dist/src/tui/keys.js.map +1 -1
  256. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  257. package/dist/src/tui/lifecycle-action-view.js +36 -0
  258. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  259. package/dist/src/tui/model.d.ts +248 -27
  260. package/dist/src/tui/model.js +1662 -314
  261. package/dist/src/tui/model.js.map +1 -1
  262. package/dist/src/tui/operation-diagnostic-screens.d.ts +8 -0
  263. package/dist/src/tui/operation-diagnostic-screens.js +71 -0
  264. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  265. package/dist/src/tui/operation-history.d.ts +81 -0
  266. package/dist/src/tui/operation-history.js +136 -0
  267. package/dist/src/tui/operation-history.js.map +1 -0
  268. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  269. package/dist/src/tui/operation-review-planner.js +1295 -0
  270. package/dist/src/tui/operation-review-planner.js.map +1 -0
  271. package/dist/src/tui/plugin-content.d.ts +31 -0
  272. package/dist/src/tui/plugin-content.js +680 -0
  273. package/dist/src/tui/plugin-content.js.map +1 -0
  274. package/dist/src/tui/plugin-detail.d.ts +52 -0
  275. package/dist/src/tui/plugin-detail.js +165 -0
  276. package/dist/src/tui/plugin-detail.js.map +1 -0
  277. package/dist/src/tui/plugin-write-task.d.ts +69 -0
  278. package/dist/src/tui/plugin-write-task.js +286 -0
  279. package/dist/src/tui/plugin-write-task.js.map +1 -0
  280. package/dist/src/tui/project-inventory.d.ts +68 -0
  281. package/dist/src/tui/project-inventory.js +203 -0
  282. package/dist/src/tui/project-inventory.js.map +1 -0
  283. package/dist/src/tui/prompt.js +4 -15
  284. package/dist/src/tui/prompt.js.map +1 -1
  285. package/dist/src/tui/query-header.d.ts +30 -0
  286. package/dist/src/tui/query-header.js +40 -0
  287. package/dist/src/tui/query-header.js.map +1 -0
  288. package/dist/src/tui/reader.d.ts +37 -0
  289. package/dist/src/tui/reader.js +47 -0
  290. package/dist/src/tui/reader.js.map +1 -0
  291. package/dist/src/tui/router.d.ts +207 -14
  292. package/dist/src/tui/router.js +352 -29
  293. package/dist/src/tui/router.js.map +1 -1
  294. package/dist/src/tui/screen-registry.d.ts +701 -0
  295. package/dist/src/tui/screen-registry.js +230 -0
  296. package/dist/src/tui/screen-registry.js.map +1 -0
  297. package/dist/src/tui/session-console-app.d.ts +6 -0
  298. package/dist/src/tui/session-console-app.js +13 -0
  299. package/dist/src/tui/session-console-app.js.map +1 -0
  300. package/dist/src/tui/session-host.d.ts +45 -0
  301. package/dist/src/tui/session-host.js +209 -0
  302. package/dist/src/tui/session-host.js.map +1 -0
  303. package/dist/src/tui/source-detail-card.d.ts +29 -0
  304. package/dist/src/tui/source-detail-card.js +45 -0
  305. package/dist/src/tui/source-detail-card.js.map +1 -0
  306. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  307. package/dist/src/tui/source-ref-presets.js +11 -0
  308. package/dist/src/tui/source-ref-presets.js.map +1 -0
  309. package/dist/src/tui/theme.d.ts +30 -10
  310. package/dist/src/tui/theme.js +30 -10
  311. package/dist/src/tui/theme.js.map +1 -1
  312. package/dist/src/tui/viewport.d.ts +21 -0
  313. package/dist/src/tui/viewport.js +52 -0
  314. package/dist/src/tui/viewport.js.map +1 -0
  315. package/dist/src/tui/views.js +31 -3
  316. package/dist/src/tui/views.js.map +1 -1
  317. package/dist/src/tui/wizard.js +37 -13
  318. package/dist/src/tui/wizard.js.map +1 -1
  319. package/dist/src/types.d.ts +244 -20
  320. package/dist/src/types.js +1 -0
  321. package/dist/src/types.js.map +1 -1
  322. package/dist/src/utils/agent-version.d.ts +7 -1
  323. package/dist/src/utils/agent-version.js +41 -8
  324. package/dist/src/utils/agent-version.js.map +1 -1
  325. package/dist/src/utils/artifact-plan.d.ts +5 -4
  326. package/dist/src/utils/artifact-plan.js +18 -7
  327. package/dist/src/utils/artifact-plan.js.map +1 -1
  328. package/dist/src/utils/artifact-renderer.d.ts +17 -7
  329. package/dist/src/utils/artifact-renderer.js +214 -216
  330. package/dist/src/utils/artifact-renderer.js.map +1 -1
  331. package/dist/src/utils/artifact-request.d.ts +21 -0
  332. package/dist/src/utils/artifact-request.js +78 -0
  333. package/dist/src/utils/artifact-request.js.map +1 -0
  334. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  335. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  336. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  337. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  338. package/dist/src/utils/bundle-integrity.js +0 -25
  339. package/dist/src/utils/bundle-integrity.js.map +1 -1
  340. package/dist/src/utils/bundle-layout.js +22 -29
  341. package/dist/src/utils/bundle-layout.js.map +1 -1
  342. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  343. package/dist/src/utils/catalog-capability-projection.js +14 -0
  344. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  345. package/dist/src/utils/catalog-digests.d.ts +10 -0
  346. package/dist/src/utils/catalog-digests.js +50 -2
  347. package/dist/src/utils/catalog-digests.js.map +1 -1
  348. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  349. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  350. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  351. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  352. package/dist/src/utils/catalog-generation-error.js +30 -0
  353. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  354. package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
  355. package/dist/src/utils/catalog-generation-resolver.js +273 -0
  356. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  357. package/dist/src/utils/catalog-generation-service.d.ts +172 -0
  358. package/dist/src/utils/catalog-generation-service.js +1271 -0
  359. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  360. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  361. package/dist/src/utils/catalog-generation-state.js +192 -0
  362. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  363. package/dist/src/utils/catalog-generation-store.d.ts +58 -0
  364. package/dist/src/utils/catalog-generation-store.js +452 -0
  365. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  366. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  367. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
  368. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  369. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  370. package/dist/src/utils/catalog-operation-context.js +142 -0
  371. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  372. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  373. package/dist/src/utils/catalog-public-error.js +28 -8
  374. package/dist/src/utils/catalog-public-error.js.map +1 -1
  375. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -7
  376. package/dist/src/utils/catalog-runtime-projection.js +7 -102
  377. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  378. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  379. package/dist/src/utils/catalog-snapshot.js +11 -3
  380. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  381. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  382. package/dist/src/utils/catalog-source-identity.js +21 -2
  383. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  384. package/dist/src/utils/catalog-source-impact.d.ts +21 -0
  385. package/dist/src/utils/catalog-source-impact.js +344 -0
  386. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  387. package/dist/src/utils/catalog-source-references.d.ts +3 -2
  388. package/dist/src/utils/catalog-source-references.js +195 -347
  389. package/dist/src/utils/catalog-source-references.js.map +1 -1
  390. package/dist/src/utils/catalog-sources.d.ts +15 -1
  391. package/dist/src/utils/catalog-sources.js +206 -27
  392. package/dist/src/utils/catalog-sources.js.map +1 -1
  393. package/dist/src/utils/codex-config.d.ts +2 -0
  394. package/dist/src/utils/codex-config.js +26 -5
  395. package/dist/src/utils/codex-config.js.map +1 -1
  396. package/dist/src/utils/codex-manager.d.ts +79 -0
  397. package/dist/src/utils/codex-manager.js +745 -0
  398. package/dist/src/utils/codex-manager.js.map +1 -0
  399. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  400. package/dist/src/utils/codex-marketplace-path.js +45 -0
  401. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  402. package/dist/src/utils/codex-native-observation.d.ts +49 -0
  403. package/dist/src/utils/codex-native-observation.js +154 -0
  404. package/dist/src/utils/codex-native-observation.js.map +1 -0
  405. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  406. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  407. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  408. package/dist/src/utils/codex-reconciler.d.ts +126 -0
  409. package/dist/src/utils/codex-reconciler.js +298 -0
  410. package/dist/src/utils/codex-reconciler.js.map +1 -0
  411. package/dist/src/utils/codex-shared-native.d.ts +43 -0
  412. package/dist/src/utils/codex-shared-native.js +81 -0
  413. package/dist/src/utils/codex-shared-native.js.map +1 -0
  414. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  415. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  416. package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
  417. package/dist/src/utils/codex-surface-analysis.js +247 -0
  418. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  419. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  420. package/dist/src/utils/dynamic-skill-tree.js +36 -6
  421. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  422. package/dist/src/utils/git-catalog-provider.d.ts +16 -0
  423. package/dist/src/utils/git-catalog-provider.js +169 -144
  424. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  425. package/dist/src/utils/local-marketplace.d.ts +17 -1
  426. package/dist/src/utils/local-marketplace.js +301 -84
  427. package/dist/src/utils/local-marketplace.js.map +1 -1
  428. package/dist/src/utils/manifest.d.ts +1 -4
  429. package/dist/src/utils/manifest.js +1 -4
  430. package/dist/src/utils/manifest.js.map +1 -1
  431. package/dist/src/utils/multi-source-catalog.d.ts +3 -9
  432. package/dist/src/utils/multi-source-catalog.js +54 -110
  433. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  434. package/dist/src/utils/native-cli.d.ts +55 -7
  435. package/dist/src/utils/native-cli.js +386 -57
  436. package/dist/src/utils/native-cli.js.map +1 -1
  437. package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
  438. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  439. package/dist/src/utils/ownership.d.ts +84 -20
  440. package/dist/src/utils/ownership.js +179 -222
  441. package/dist/src/utils/ownership.js.map +1 -1
  442. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  443. package/dist/src/utils/path-catalog-provider.js +54 -65
  444. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  445. package/dist/src/utils/paths.d.ts +0 -1
  446. package/dist/src/utils/paths.js +0 -1
  447. package/dist/src/utils/paths.js.map +1 -1
  448. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  449. package/dist/src/utils/plugin-config-impact.js +298 -0
  450. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  451. package/dist/src/utils/plugin-config-write.d.ts +52 -27
  452. package/dist/src/utils/plugin-config-write.js +1000 -296
  453. package/dist/src/utils/plugin-config-write.js.map +1 -1
  454. package/dist/src/utils/plugin-config.d.ts +153 -93
  455. package/dist/src/utils/plugin-config.js +1163 -1017
  456. package/dist/src/utils/plugin-config.js.map +1 -1
  457. package/dist/src/utils/plugin-intent-state.d.ts +49 -0
  458. package/dist/src/utils/plugin-intent-state.js +320 -0
  459. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  460. package/dist/src/utils/plugin-scripts.d.ts +65 -125
  461. package/dist/src/utils/plugin-scripts.js +482 -524
  462. package/dist/src/utils/plugin-scripts.js.map +1 -1
  463. package/dist/src/utils/process-tree.d.ts +2 -2
  464. package/dist/src/utils/process-tree.js +1 -1
  465. package/dist/src/utils/process-tree.js.map +1 -1
  466. package/dist/src/utils/project-boundary.d.ts +11 -13
  467. package/dist/src/utils/project-boundary.js +28 -34
  468. package/dist/src/utils/project-boundary.js.map +1 -1
  469. package/dist/src/utils/runtime-config.d.ts +20 -0
  470. package/dist/src/utils/runtime-config.js +75 -0
  471. package/dist/src/utils/runtime-config.js.map +1 -0
  472. package/dist/src/utils/safe-fs.d.ts +21 -0
  473. package/dist/src/utils/safe-fs.js +111 -0
  474. package/dist/src/utils/safe-fs.js.map +1 -0
  475. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  476. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  477. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  478. package/dist/src/utils/sensitive-content.d.ts +4 -0
  479. package/dist/src/utils/sensitive-content.js +36 -0
  480. package/dist/src/utils/sensitive-content.js.map +1 -0
  481. package/dist/src/utils/skill-validation.d.ts +1 -1
  482. package/dist/src/utils/skill-validation.js +1 -1
  483. package/dist/src/utils/skill-validation.js.map +1 -1
  484. package/dist/src/utils/state-paths.d.ts +1 -0
  485. package/dist/src/utils/state-paths.js +12 -2
  486. package/dist/src/utils/state-paths.js.map +1 -1
  487. package/dist/src/utils/target-eligibility.d.ts +8 -2
  488. package/dist/src/utils/target-eligibility.js +17 -8
  489. package/dist/src/utils/target-eligibility.js.map +1 -1
  490. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  491. package/dist/src/utils/windows-claude-shim.js +141 -0
  492. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  493. package/dist/vitest.config.js +91 -10
  494. package/dist/vitest.config.js.map +1 -1
  495. package/package.json +22 -19
  496. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  497. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  498. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  499. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  500. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  501. package/dist/scripts/codex-eval-app-server.d.ts +0 -89
  502. package/dist/scripts/codex-eval-app-server.js +0 -1228
  503. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  504. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  505. package/dist/scripts/codex-eval-isolation.js +0 -318
  506. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  507. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  508. package/dist/scripts/codex-eval-process-management.js +0 -69
  509. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  510. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  511. package/dist/scripts/codex-eval-process-tree.js +0 -803
  512. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  513. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  514. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  515. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  516. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  517. package/dist/scripts/codex-trigger-eval.js +0 -968
  518. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  519. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  520. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  521. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  522. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  523. package/dist/scripts/eval-plugin-skills.js +0 -1586
  524. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  525. package/dist/scripts/eval-run-layout.d.ts +0 -17
  526. package/dist/scripts/eval-run-layout.js +0 -26
  527. package/dist/scripts/eval-run-layout.js.map +0 -1
  528. package/dist/scripts/plugin-eval-contract.d.ts +0 -185
  529. package/dist/scripts/plugin-eval-contract.js +0 -695
  530. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  531. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  532. package/dist/scripts/plugin-eval-materializer.js +0 -421
  533. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  534. package/dist/scripts/skill-reporting.d.ts +0 -212
  535. package/dist/scripts/skill-reporting.js +0 -922
  536. package/dist/scripts/skill-reporting.js.map +0 -1
  537. package/dist/src/commands/status-v4.d.ts +0 -102
  538. package/dist/src/commands/status-v4.js +0 -844
  539. package/dist/src/commands/status-v4.js.map +0 -1
  540. package/dist/src/commands/status.d.ts +0 -93
  541. package/dist/src/commands/status.js +0 -442
  542. package/dist/src/commands/status.js.map +0 -1
  543. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  544. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  545. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  546. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  547. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  548. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  549. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
  550. package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
  551. package/dist/src/utils/catalog-source-cache.d.ts +0 -61
  552. package/dist/src/utils/catalog-source-cache.js +0 -276
  553. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  554. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  555. package/dist/src/utils/codex-project-surface.js +0 -49
  556. package/dist/src/utils/codex-project-surface.js.map +0 -1
  557. package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
  558. package/dist/src/utils/lifecycle-active-store.js +0 -1276
  559. package/dist/src/utils/lifecycle-active-store.js.map +0 -1
  560. package/dist/src/utils/plugin-authoring-capabilities.d.ts +0 -23
  561. package/dist/src/utils/plugin-authoring-capabilities.js +0 -37
  562. package/dist/src/utils/plugin-authoring-capabilities.js.map +0 -1
  563. package/dist/src/utils/plugin-authoring-capabilities.json +0 -92
  564. package/dist/src/utils/plugin-release.d.ts +0 -2
  565. package/dist/src/utils/plugin-release.js +0 -2
  566. package/dist/src/utils/plugin-release.js.map +0 -1
  567. package/dist/src/utils/skill-frontmatter-capabilities.d.ts +0 -201
  568. package/dist/src/utils/skill-frontmatter-capabilities.js +0 -5
  569. package/dist/src/utils/skill-frontmatter-capabilities.js.map +0 -1
  570. package/dist/src/utils/skill-frontmatter-capabilities.json +0 -200
  571. /package/dist/authoring/scripts/{check-plugin-versions.d.mts → remove-plugin.d.mts} +0 -0
@@ -1,101 +1,836 @@
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';
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
+ }
1
131
  function compareText(left, right) {
2
132
  return left.localeCompare(right, 'en');
3
133
  }
4
134
  function targetHealth(target) {
5
- if (target.state === 'present')
135
+ if (target.state === 'ready')
6
136
  return 'ready';
7
- if (target.state === 'partial')
137
+ if (target.state === 'needsSync' || target.state === 'orphaned')
8
138
  return 'incomplete';
9
- 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
+ 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'));
10
205
  }
11
206
  function isInstalledScopeEvidence(target) {
12
- 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
+ }));
13
264
  }
14
265
  function dependencyPluginIdFromMessage(message) {
15
266
  return message.match(/(?:[a-z0-9]+(?:-[a-z0-9]+)*)\/(?:[a-z0-9]+(?:-[a-z0-9]+)*)/u)?.[0];
16
267
  }
17
- export function projectPlatformPlacement(platform, targets) {
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
+ }
319
+ export function projectPlatformPlacement(platform, targets, revisions = []) {
320
+ const revisionFor = (target) => revisions.find((entry) => entry.pluginId === target.pluginId
321
+ && entry.platform === target.platform
322
+ && entry.scope === target.scope)?.revision
323
+ ?? { state: 'unknown', ...(target.version ? { availableVersion: target.version } : {}) };
18
324
  const supportedScopes = [...new Set(targets.map((target) => target.scope))];
19
325
  const installed = targets.filter(isInstalledScopeEvidence);
20
326
  if (installed.length === 0) {
21
- 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');
22
331
  if (!errorTarget)
23
332
  return { platform, state: 'notInstalled', supportedScopes };
24
- return { platform, state: 'installed', scope: errorTarget.scope, health: 'error' };
333
+ return {
334
+ platform,
335
+ state: 'installed',
336
+ scope: errorTarget.scope,
337
+ health: 'error',
338
+ revision: revisionFor(errorTarget),
339
+ };
25
340
  }
26
- if (installed.length === 1) {
27
- const target = installed[0];
28
- return { platform, state: 'installed', scope: target.scope, health: targetHealth(target) };
341
+ if (installed.length > 1) {
342
+ const projected = installed.map((target) => projectPlatformPlacement(platform, [target], revisions));
343
+ return {
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),
358
+ };
29
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;
30
371
  return {
31
372
  platform,
32
- state: 'scopeConflict',
33
- placements: installed.map((target) => ({
34
- scope: target.scope,
35
- health: targetHealth(target),
36
- })).sort((left, right) => compareText(left.scope, right.scope)),
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 } : {}),
37
386
  };
38
387
  }
388
+ function hasIncompletePlacement(plugin) {
389
+ return plugin.installedPlacements.some((placement) => placement.state === 'scopeConflict'
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));
395
+ }
396
+ function hasMissingPlatform(plugin) {
397
+ return plugin.supportedPlatforms.some((platform) => plugin.installedPlacements.find((placement) => placement.platform === platform)?.state === 'notInstalled');
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
+ }
404
+ function capabilityActions(plugin) {
405
+ return [
406
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
407
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
408
+ ];
409
+ }
410
+ function capabilityUnavailableActions() {
411
+ return [{ id: 'upgradeAwCli' }, { id: 'capabilityTechnical' }];
412
+ }
413
+ function isCapabilityUnavailable(plugin) {
414
+ return plugin.capabilityUnsupported === true && plugin.sourceUnavailable !== true;
415
+ }
416
+ export function buildPluginPreviewActions(plugin) {
417
+ const installed = hasInstalledPlacement(plugin);
418
+ if (plugin.aggregateStatus === 'unavailable') {
419
+ if (isCapabilityUnavailable(plugin)) {
420
+ return [...capabilityUnavailableActions(), ...(installed ? [{ id: 'manage' }] : [])];
421
+ }
422
+ return [{ id: 'sourceProblem' }, ...(installed ? [{ id: 'manage' }] : [])];
423
+ }
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' };
440
+ return [primary, ...capabilityActions(plugin), { id: 'source' }];
441
+ }
442
+ export function buildPluginDetailActions(plugin) {
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
+ }
453
+ return [
454
+ ...plugin.installedPlacements.flatMap((placement) => placement.state === 'notInstalled'
455
+ ? []
456
+ : [{ id: 'platform', platform: placement.platform }]),
457
+ ...(plugin.savedConfigScopes.length > 0 ? [{ id: 'settings' }] : []),
458
+ { id: 'sourceProblem' },
459
+ { id: 'about' },
460
+ ];
461
+ }
462
+ return [
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
+ ]),
483
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
484
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
485
+ { id: 'about' },
486
+ ];
487
+ }
488
+ export function buildPlatformActions(plugin, platform) {
489
+ if (plugin.aggregateStatus === 'unavailable') {
490
+ if (isCapabilityUnavailable(plugin))
491
+ return capabilityUnavailableActions();
492
+ return [{ id: 'sourceProblem' }, { id: 'refreshSource' }];
493
+ }
494
+ const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
495
+ if (placement?.state === 'blocked') {
496
+ return [{ id: 'outputCollision' }, { id: 'source' }, { id: 'about' }];
497
+ }
498
+ if (!placement || placement.state === 'notInstalled')
499
+ return [{ id: 'add' }, { id: 'about' }];
500
+ if (placement.state === 'scopeConflict')
501
+ return [{ id: 'scopeConflict', platform }];
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') {
517
+ return [
518
+ { id: 'outputCollision' },
519
+ { id: 'remove', platform, scope: placement.scope },
520
+ { id: 'technical' },
521
+ ];
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
+ }
562
+ return [
563
+ ...missingPureNativeScopes(plugin, placement).map((scope) => ({
564
+ id: 'addScope', platform, scope,
565
+ })),
566
+ ...lifecycleActions,
567
+ { id: 'technical' },
568
+ ];
569
+ }
39
570
  export function hasInstalledPlacement(plugin) {
40
- return plugin.installedPlacements.some((placement) => placement.state !== 'notInstalled');
571
+ return plugin.installedPlacements.some((placement) => placement.state === 'installed' || placement.state === 'scopeConflict');
41
572
  }
42
- function sourceState(source, health) {
43
- if (health?.state === 'ready')
44
- return 'ready';
45
- if (health?.state === 'path')
46
- return 'path';
47
- if (health?.state === 'degraded')
48
- return 'cache';
49
- if (health?.state === 'unavailable')
50
- return 'error';
51
- if (source.error)
52
- return 'error';
53
- if (source.type === 'path' || source.freshness === 'path')
54
- return 'path';
55
- return source.freshness === 'cache' ? 'cache' : 'ready';
56
- }
57
- function sourceIssue(health, impactedTargets = 0) {
58
- if (health.state !== 'cache' && health.state !== 'degraded' && health.state !== 'unavailable')
573
+ function sourceIssue(health, targetIssues = []) {
574
+ if (health.state !== 'unavailable')
59
575
  return undefined;
60
- const cached = health.state === 'cache' || health.state === 'degraded';
61
- 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');
62
584
  return {
63
585
  issueId,
64
- category: cached ? 'sourceDegraded' : 'sourceUnavailable',
65
- title: `${health.sourceId} · ${cached ? '来源使用缓存' : '来源不可用'}`,
66
- explanation: cached
67
- ? '远端检查失败。当前仍使用已验证的缓存数据。'
68
- : '远端检查失败,并且没有可用缓存数据。',
69
- impact: [cached ? '插件仍可浏览,但数据可能不是最新。' : '当前不能浏览或安装这个来源的插件。',
70
- ...(impactedTargets > 0 ? [`${impactedTargets} 个安装位置受此来源根问题影响。`] : [])],
586
+ category: 'sourceUnavailable',
587
+ title: `${health.sourceId} · 来源不可用`,
588
+ explanation: '来源检查失败。',
589
+ impact: ['当前不能浏览或安装这个来源的插件。',
590
+ ...(affectedTargets.length > 0 ? [`${affectedTargets.length} 个安装位置受此来源根问题影响。`] : [])],
71
591
  entity: { kind: 'source', id: health.sourceId },
72
592
  sourceId: health.sourceId,
73
- actions: cached
74
- ? [
75
- { id: 'refreshSource', label: '重新检查来源', description: '不会修改已安装内容' },
76
- { id: 'continueWithCache', label: '继续使用缓存', description: '当前缓存仍可安全读取' },
77
- { id: 'viewTechnical', label: '技术详情' },
78
- ]
79
- : [
80
- { id: 'editSource', label: '编辑来源', description: '检查 URL、路径或 ref' },
81
- { id: 'refreshSource', label: '稍后重试', description: '只重新检查来源' },
82
- { id: 'viewTechnical', label: '技术详情' },
83
- ],
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
+ ]),
84
600
  technicalDetails: [{
85
- code: cached ? 'catalogSourceDegraded' : 'catalogSourceUnavailable',
86
- message: cached ? '远端检查失败;已验证缓存仍可用。' : '来源检查失败;没有可用缓存。',
601
+ code: 'catalogSourceUnavailable',
602
+ message: '来源检查失败。',
87
603
  }],
88
604
  };
89
605
  }
90
- 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 = []) {
91
652
  const issues = [];
92
- 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
+ }
676
+ for (const target of payload.targets) {
677
+ if (target.platform !== 'codex' || !target.reasons.some((reason) => reason.code === 'codexExtensionScopeConflict'))
678
+ continue;
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
+ }
93
714
  for (const target of payload.targets) {
94
- const key = `${target.pluginId}\0${target.platform}`;
95
- byPlacement.set(key, [...(byPlacement.get(key) ?? []), target]);
96
- if (target.state !== 'partial' && target.state !== 'error')
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))
97
778
  continue;
98
- const codes = new Set(target.errors.map((error) => error.code));
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: '现场状态与期望状态不一致,需要重新检查并同步。' };
99
834
  const dependencyCodes = [
100
835
  'dependencyMissing',
101
836
  'dependencyNotReady',
@@ -103,159 +838,256 @@ function targetIssues(payload) {
103
838
  'dependencyStillRequired',
104
839
  'dependencyGraphInvalid',
105
840
  ];
106
- const dependencyRoots = [...new Map(target.errors.flatMap((error) => {
107
- if (!dependencyCodes.includes(error.code))
108
- return [];
109
- const code = error.code;
110
- const dependencyPluginId = dependencyPluginIdFromMessage(error.message);
111
- return [[`${code}\0${dependencyPluginId ?? ''}`, {
112
- code,
113
- category: code,
114
- mergeEligible: false,
115
- error,
116
- dependencyPluginId,
117
- }]];
118
- })).values()];
119
- const roots = dependencyRoots.length > 0
120
- ? dependencyRoots
121
- : target.state === 'partial'
122
- ? [{ code: 'partial', category: 'incompleteInstallation', mergeEligible: false }]
123
- : codes.has('sourceMissing')
124
- ? [{ code: 'sourceMissing', category: 'sourceMissing', mergeEligible: false }]
125
- : codes.has('sourceChanged')
126
- ? [{ code: 'sourceChanged', category: 'sourceIdentityChanged', mergeEligible: false }]
127
- : [
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
+ ? [
128
884
  ...(codes.has('catalogSourceUnavailable')
129
885
  ? [{ code: 'catalogSourceUnavailable', category: 'sourceUnavailable', mergeEligible: true }]
130
886
  : []),
131
887
  ...(codes.has('bundleUnavailable')
132
888
  ? [{ code: 'bundleUnavailable', category: 'sourceUnavailable', mergeEligible: false }]
133
889
  : []),
134
- ...(!codes.has('catalogSourceUnavailable') && !codes.has('bundleUnavailable')
135
- ? [{ code: 'targetDiagnostic', category: 'diagnostic', mergeEligible: false }]
890
+ ...(codes.has('codexIntentSourceUnavailable')
891
+ ? [{ code: 'codexIntentSourceUnavailable', category: 'sourceUnavailable', mergeEligible: false }]
136
892
  : []),
137
- ];
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
+ ];
138
901
  for (const root of roots) {
139
- 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);
140
903
  const dependencyPluginId = root.dependencyPluginId
904
+ ?? rootError?.dependencyPluginId
141
905
  ?? (rootError ? dependencyPluginIdFromMessage(rootError.message) : undefined);
906
+ const dependencyPlatform = rootError?.dependencyPlatform;
907
+ const dependencyScope = rootError?.dependencyScope;
908
+ const dependencyProject = rootError?.dependencyProject;
142
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;
143
926
  const actions = root.category === 'dependencyMissing'
144
927
  ? [
145
- ...(dependencyPluginId ? [{ id: 'installDependency', label: '补齐依赖插件',
146
- pluginId: dependencyPluginId, writeAction: 'install' }] : []),
928
+ ...consumerRecoveryActions,
929
+ ...(dependencyPluginId ? [
930
+ { id: 'viewDependencyIssue', label: '查看依赖插件问题', pluginId: dependencyPluginId },
931
+ ] : []),
147
932
  { id: 'viewDependencies', label: '查看依赖关系' },
148
933
  { id: 'viewTechnical', label: '技术详情' },
149
934
  ]
150
935
  : root.category === 'dependencyNotReady'
151
936
  ? [
937
+ ...consumerRecoveryActions,
152
938
  ...(dependencyPluginId ? [
153
939
  { id: 'viewDependencyIssue', label: '查看依赖插件问题', pluginId: dependencyPluginId },
154
- { id: 'reinstallDependency', label: '重新安装依赖插件',
155
- pluginId: dependencyPluginId, writeAction: 'reinstall' },
156
940
  ] : []),
157
941
  { id: 'viewDependencies', label: '查看依赖关系' },
158
942
  { id: 'viewTechnical', label: '技术详情' },
159
943
  ]
160
944
  : dependencyCategory
161
945
  ? [
946
+ ...consumerRecoveryActions,
162
947
  { id: 'viewDependencies', label: '查看依赖关系' },
163
948
  { id: 'viewTechnical', label: '技术详情' },
164
949
  ]
165
- : root.category === 'incompleteInstallation'
950
+ : root.category === 'artifactCollision'
166
951
  ? [
167
- { id: 'reinstall', label: '重新安装', description: '保留插件设置,重新生成缺失内容' },
168
- { id: 'remove', label: '移除安装', description: '保留插件设置' },
169
- { id: 'viewTechnical', label: '技术详情' },
952
+ { id: 'viewTechnical', label: '查看冲突原因' },
953
+ { id: 'retryLater', label: '处理冲突后重试' },
170
954
  ]
171
- : root.category === 'sourceMissing'
955
+ : root.category === 'incompleteInstallation'
172
956
  ? [
173
- { id: 'reAddSource', label: '重新添加同一来源', description: '保留来源 ID;来源类型和位置由你选择' },
957
+ ...consumerRecoveryActions,
174
958
  { id: 'viewTechnical', label: '技术详情' },
175
959
  ]
176
- : root.category === 'sourceIdentityChanged'
960
+ : root.category === 'sourceMissing'
177
961
  ? [
178
- { id: 'viewSourceChange', label: '查看来源变更', description: '不会自动覆盖现场状态' },
962
+ ...consumerRecoveryActions,
963
+ { id: 'reAddSource', label: '重新添加同一来源', description: '保留来源 ID;来源类型和位置由你选择' },
179
964
  { id: 'viewTechnical', label: '技术详情' },
180
965
  ]
181
- : root.category === 'sourceUnavailable'
966
+ : root.category === 'sourceIdentityChanged'
182
967
  ? [
183
- { id: 'editSource', label: '查看来源', description: root.code === 'bundleUnavailable' ? '检查这个插件包或稍后重试' : '修复来源配置或稍后重试' },
968
+ { id: 'viewSourceChange', label: '查看来源变更', description: '不会自动覆盖现场状态' },
184
969
  { id: 'viewTechnical', label: '技术详情' },
185
970
  ]
186
- : [{ id: 'viewTechnical', label: '技术详情' }, { id: 'retryLater', label: '稍后重试' }];
187
- const issueId = `target:${target.pluginId}:${target.platform}:${target.scope}:${target.state}${roots.length > 1 ? `:${root.code}` : ''}${dependencyPluginId ? `:${dependencyPluginId}` : ''}`;
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: '稍后重试' }];
188
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}` : ''}`);
189
1007
  issues.push({
190
1008
  issueId,
191
1009
  category: root.category,
192
1010
  rootCode: root.code,
193
1011
  mergeEligible: root.mergeEligible,
194
- 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 ? {
195
1021
  dependencyMissing: '缺少依赖',
196
1022
  dependencyNotReady: '依赖安装不完整',
197
1023
  dependencyTargetUnsupported: '依赖目标不兼容',
198
1024
  dependencyStillRequired: '依赖仍被使用',
199
1025
  dependencyGraphInvalid: '依赖关系无效',
200
- }[root.category] : target.state === 'partial' ? '安装不完整' : bundleUnavailable ? '插件包不可用' : '检查失败'}`,
201
- explanation: dependencyCategory
202
- ? dependencyPluginId
203
- ? `${dependencyPluginId} 导致当前依赖检查不能通过。`
204
- : '当前依赖检查不能安全通过。'
205
- : target.state === 'partial'
206
- ? '必要内容缺失,插件可能不能稳定运行。'
207
- : bundleUnavailable
208
- ? '当前来源中无法取得这个插件的可信 bundle。'
209
- : '当前目标不能安全分类为正常安装。',
210
- impact: [bundleUnavailable
211
- ? '当前目标不能使用这个插件包;这不表示来源缓存整体不可用。'
212
- : '写操作前需要重新检查这个目标。'],
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
+ : '写操作前需要重新检查这个目标。'],
213
1057
  entity: { kind: 'plugin', id: target.pluginId },
214
1058
  pluginId: target.pluginId,
215
1059
  ...(dependencyPluginId ? { dependencyPluginId } : {}),
1060
+ ...(dependencyPlatform ? { dependencyPlatform } : {}),
1061
+ ...(dependencyScope ? { dependencyScope } : {}),
1062
+ ...(dependencyProject !== undefined ? { dependencyProject } : {}),
216
1063
  platform: target.platform,
217
1064
  scope: target.scope,
218
1065
  sourceId: target.sourceId,
219
1066
  sourceIdentity: target.sourceIdentity,
220
1067
  actions,
221
- technicalDetails: target.errors.length > 0
222
- ? target.errors.filter((error) => root.code === 'targetDiagnostic'
1068
+ technicalDetails: target.reasons.length > 0
1069
+ ? target.reasons.filter((error) => root.code === 'targetDiagnostic'
223
1070
  || root.code === 'partial'
224
- || error.code === root.code)
225
- .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
+ }))
226
1083
  : [{ code: 'targetStateError', message: '目标状态异常。' }],
227
1084
  });
228
1085
  }
229
1086
  }
230
- for (const targets of byPlacement.values()) {
231
- const installed = targets.filter(isInstalledScopeEvidence);
232
- if (new Set(installed.map((target) => target.scope)).size < 2)
233
- continue;
234
- const sample = installed[0];
235
- issues.push({
236
- issueId: `scope:${sample.pluginId}:${sample.platform}`,
237
- category: 'scopeConflict',
238
- title: `${sample.pluginId} · ${sample.platform === 'codex' ? 'Codex' : 'Claude Code'} · 安装位置冲突`,
239
- explanation: '所有项目和当前项目同时发现了这个插件。',
240
- impact: ['正常状态只能保留一个位置。'],
241
- entity: { kind: 'plugin', id: sample.pluginId },
242
- pluginId: sample.pluginId,
243
- platform: sample.platform,
244
- sourceId: sample.sourceId,
245
- sourceIdentity: sample.sourceIdentity,
246
- placements: installed.map((target) => target.scope).sort(compareText),
247
- actions: [
248
- { id: 'keepUserScope', label: '保留所有项目', description: '移除当前项目中的安装;设置保留' },
249
- { id: 'keepProjectScope', label: '保留当前项目', description: '移除所有项目中的安装;设置保留' },
250
- { id: 'viewTechnical', label: '查看两个位置的详情' },
251
- ],
252
- technicalDetails: [{ code: 'scopeConflict', message: '同一插件和平台存在两个安装位置。' }],
253
- });
254
- }
255
1087
  return issues;
256
1088
  }
257
1089
  function statusIssues(payload) {
258
- 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)));
259
1091
  const issues = new Map();
260
1092
  for (const error of payload.errors) {
261
1093
  if (targetCodes.has(error.code) || issues.has(error.code))
@@ -299,102 +1131,320 @@ function statusIssues(payload) {
299
1131
  function configIssues(pluginSummaries) {
300
1132
  return pluginSummaries.flatMap((summary) => {
301
1133
  const sourceId = summary.pluginId.slice(0, summary.pluginId.indexOf('/'));
1134
+ const issues = [];
302
1135
  if (summary.configDiagnostic) {
303
- return [{
304
- issueId: `config:${summary.pluginId}:read`,
305
- category: 'diagnostic',
306
- title: `${summary.displayName ?? summary.pluginName} · 设置状态不可读`,
307
- explanation: '当前不能安全读取这个插件的设置状态。',
308
- impact: ['其他插件仍可检查;当前插件的设置值不会显示。'],
309
- entity: { kind: 'plugin', id: summary.pluginId },
310
- pluginId: summary.pluginId,
311
- sourceId,
312
- actions: [
313
- { id: 'retryLater', label: '稍后重新检查' },
314
- { id: 'viewTechnical', label: '查看技术详情' },
315
- ],
316
- technicalDetails: [{ code: summary.configDiagnostic.code, message: summary.configDiagnostic.message }],
317
- }];
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
+ });
318
1154
  }
319
1155
  if (!summary.configIssues?.length)
320
- return [];
321
- const types = new Set(summary.configIssues.map((issue) => issue.type));
322
- const invalid = types.has('invalid-config');
323
- const sources = new Set(summary.configIssues.map((issue) => issue.source));
324
- const onlySource = sources.size === 1 ? [...sources][0] : undefined;
325
- const configLayer = onlySource === 'user'
326
- ? { scope: 'user', projectLayer: 'shared' }
327
- : onlySource === 'project'
328
- ? { scope: 'project', projectLayer: 'shared' }
329
- : onlySource === 'projectLocal'
330
- ? { scope: 'project', projectLayer: 'local' }
331
- : undefined;
332
- return [{
333
- issueId: `config:${summary.pluginId}:${invalid ? 'invalid' : 'required'}`,
334
- category: 'requiredConfigMissing',
335
- title: `${summary.displayName ?? summary.pluginName} · ${invalid ? '设置值无效' : '缺少必填设置'}`,
336
- explanation: invalid
337
- ? '已有设置值不符合当前字段规则。'
338
- : '插件缺少必填设置,写操作会在预检时被阻止。',
339
- impact: [invalid ? '修正所列字段后才能稳定生成插件内容。' : '完成所列字段后才能稳定生成插件内容。'],
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: ['旧值不会参与生成。可以进入项目设置删除旧值。'],
340
1165
  entity: { kind: 'plugin', id: summary.pluginId },
341
1166
  pluginId: summary.pluginId,
342
1167
  sourceId,
343
- requiredFields: summary.configIssues.map(({ key, title, type, source }) => ({ key, title, type, source })),
344
- ...(configLayer ? { configLayer } : {}),
1168
+ configLayer: { scope: 'project' },
345
1169
  actions: [
346
- { id: 'openConfig', label: invalid ? '修正设置' : '完成设置', description: configLayer ? '进入建议的设置层' : '选择个人或项目设置层' },
1170
+ { id: 'openConfig', label: '删除旧值', description: '进入项目设置并恢复继承' },
347
1171
  { id: 'viewTechnical', label: '查看字段详情' },
348
1172
  ],
349
- technicalDetails: summary.configIssues.map((entry) => ({
1173
+ technicalDetails: unsupported.map((entry) => ({
350
1174
  code: entry.type,
351
- message: `${entry.title} · ${entry.source}`,
1175
+ message: `${entry.title} · project · 实际使用 user/default`,
352
1176
  })),
353
- }];
1177
+ });
1178
+ }
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;
354
1216
  });
355
1217
  }
356
- function enrichDependencyIssuePlacements(issues, overviews) {
357
- const overviewById = new Map(overviews.map((overview) => [overview.pluginId, overview]));
358
- return issues.map((issue) => {
359
- if ((issue.category !== 'dependencyMissing' && issue.category !== 'dependencyNotReady')
360
- || !issue.pluginId || !issue.dependencyPluginId || !issue.platform || !issue.scope)
361
- return issue;
362
- const dependency = overviewById.get(issue.pluginId)?.dependencies.find((candidate) => candidate.pluginId === issue.dependencyPluginId);
363
- const references = (dependency?.placementReferences ?? []).filter((reference) => reference.consumer.pluginId === issue.pluginId
364
- && reference.consumer.platform === issue.platform
365
- && reference.consumer.scope === issue.scope
366
- && (issue.scope === 'user' || reference.consumer.project === 'current'))
367
- .sort((left, right) => [
368
- left.dependency.pluginId,
369
- left.dependency.platform,
370
- left.dependency.scope,
371
- left.dependency.project ?? '',
372
- ].join('\0').localeCompare([
373
- right.dependency.pluginId,
374
- right.dependency.platform,
375
- right.dependency.scope,
376
- right.dependency.project ?? '',
377
- ].join('\0'), 'en'));
378
- const target = references[0]?.dependency;
379
- if (!target) {
380
- return {
381
- ...issue,
382
- actions: issue.actions.filter((action) => action.id !== 'installDependency' && action.id !== 'reinstallDependency'),
383
- };
384
- }
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
+ }
1251
+ function issueDiagnostic(issue, sourceDetails) {
1252
+ const source = issue.sourceId ? sourceDetails.get(issue.sourceId) : undefined;
1253
+ const sourceUnavailable = issue.category === 'sourceUnavailable' && issue.entity.kind === 'source';
1254
+ const configLayerUnsupported = issue.category === 'configLayerUnsupported';
1255
+ const configUnavailable = issue.category === 'diagnostic' && issue.issueId.startsWith('config:');
1256
+ const configInvalid = issue.category === 'requiredConfigMissing';
1257
+ const dependency = issue.category.startsWith('dependency');
1258
+ const platformState = issue.issueId.startsWith('status:') || issue.rootCode === 'targetDiagnostic';
1259
+ const preserved = sourceUnavailable
1260
+ ? '已安装插件和已保存设置不会被删除。'
1261
+ : configUnavailable
1262
+ ? '已保存的设置保持不变。'
1263
+ : '现有安装和已保存设置不会因本页自动改变。';
1264
+ const impact = sourceUnavailable
1265
+ ? [
1266
+ { state: 'blocked', message: '当前不能浏览或安装这个来源中的插件。' },
1267
+ { state: 'preserved', message: preserved },
1268
+ ]
1269
+ : [
1270
+ ...issue.impact.map((message) => ({
1271
+ state: dependency || configUnavailable || configInvalid || platformState ? 'blocked' : 'degraded',
1272
+ message,
1273
+ })),
1274
+ { state: 'preserved', message: preserved },
1275
+ ];
1276
+ const primaryAction = issue.actions.find((action) => action.id !== 'viewTechnical');
1277
+ const nextStep = primaryAction
1278
+ ? `${primaryAction.label},然后重新检查当前状态。`
1279
+ : '打开技术详情,记录安全错误代码后重新检查。';
1280
+ const sourceDiagnostic = sourceUnavailable;
1281
+ const rawCode = sourceDiagnostic
1282
+ ? source?.errorCode ?? issue.technicalDetails[0]?.code
1283
+ : issue.technicalDetails[0]?.code;
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';
1292
+ const context = [
1293
+ ...(issue.pluginId ? [{ key: 'pluginId', value: issue.pluginId }] : []),
1294
+ ...(issue.dependencyPluginId
1295
+ ? [{ key: 'dependencyPluginId', value: issue.dependencyPluginId }]
1296
+ : []),
1297
+ ...(issue.platform ? [{ key: 'platform', value: issue.platform }] : []),
1298
+ ...(issue.scope ? [{ key: 'scope', value: issue.scope }] : []),
1299
+ ...(issue.requiredFields?.slice(0, 1).map((field) => ({
1300
+ key: 'field',
1301
+ value: field.title,
1302
+ })) ?? []),
1303
+ ];
1304
+ const checks = issue.category === 'legacyState'
1305
+ ? [
1306
+ '不自动迁移旧版状态。',
1307
+ '运行 aw status --json,记录公开错误 code。',
1308
+ '备份相关 AW 配置。',
1309
+ '按技术详情使用受支持命令或人工处理。',
1310
+ '再次运行状态检查。',
1311
+ ]
1312
+ : configLayerUnsupported
1313
+ ? ['打开项目设置。', '删除本项目旧值。', '重新检查插件设置。']
1314
+ : sourceUnavailable
1315
+ ? ['检查 Git URL、ref 或本地目录。', '确认当前终端具备来源授权。', '运行“重新检查来源”验证修复。']
1316
+ : dependency
1317
+ ? ['检查依赖关系和依赖插件状态。', '完成恢复动作后重新检查根插件。']
1318
+ : configUnavailable
1319
+ ? ['恢复插件来源和可信字段定义。', '重新检查来源后再次打开设置。']
1320
+ : configInvalid
1321
+ ? ['完成或修正列出的设置字段。', '重新检查插件设置。']
1322
+ : ['检查目标平台配置和访问权限。', '完成恢复动作后重新运行状态检查。'];
1323
+ return {
1324
+ severity: configLayerUnsupported ? 'warning' : 'error',
1325
+ title: issue.title,
1326
+ userMessage: issue.explanation,
1327
+ impact,
1328
+ nextStep,
1329
+ technical: {
1330
+ code,
1331
+ stage,
1332
+ ...(sourceDiagnostic && issue.sourceId ? { sourceId: issue.sourceId } : {}),
1333
+ ...(sourceDiagnostic && source?.requestedRef ? { requestedRef: source.requestedRef } : {}),
1334
+ ...(sourceDiagnostic && source?.resolvedCommit ? { resolvedCommit: source.resolvedCommit } : {}),
1335
+ ...(context.length > 0 ? { context } : {}),
1336
+ checks,
1337
+ },
1338
+ };
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;
385
1440
  return {
386
- ...issue,
387
- dependencyPlatform: target.platform,
388
- dependencyScope: target.scope,
389
- dependencyProject: target.project,
390
- actions: issue.actions.map((action) => action.id === 'installDependency' || action.id === 'reinstallDependency'
391
- ? {
392
- ...action,
393
- platform: target.platform,
394
- scope: target.scope,
395
- project: target.project,
396
- }
397
- : action),
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)),
398
1448
  };
399
1449
  });
400
1450
  }
@@ -402,23 +1452,77 @@ export function buildTuiViewModel(options) {
402
1452
  const dependencyById = new Map((options.dependencyOverviews ?? [])
403
1453
  .map((overview) => [overview.pluginId, overview]));
404
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]));
405
1456
  const targetsByPlugin = new Map();
406
- for (const target of options.status.targets) {
1457
+ for (const target of flattenStatusTargets(options.status.targets)) {
407
1458
  targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
408
1459
  }
409
- 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
+ ]);
410
1475
  const plugins = [...pluginIds].map((pluginId) => {
411
1476
  const summary = summaryById.get(pluginId);
412
- const targets = targetsByPlugin.get(pluginId) ?? [];
1477
+ const targets = summary || retainedStatusIds.has(pluginId)
1478
+ ? targetsByPlugin.get(pluginId) ?? [] : [];
413
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'));
414
1484
  const placements = ['codex', 'claude'].flatMap((platform) => {
415
1485
  const platformTargets = targets.filter((target) => target.platform === platform);
416
- return platformTargets.length > 0 ? [projectPlatformPlacement(platform, platformTargets)] : [];
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
+ }
1501
+ return platformTargets.length > 0
1502
+ ? [projectPlatformPlacement(platform, platformTargets, options.targetRevisions)]
1503
+ : [];
417
1504
  });
418
1505
  const attention = placements.some((placement) => placement.state === 'scopeConflict'
419
- || (placement.state === 'installed' && placement.health !== 'ready'));
420
- const installed = placements.some((placement) => placement.state !== 'notInstalled');
421
- const unavailable = options.sourceHealth?.get(sourceId)?.state === 'unavailable';
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
+ });
422
1526
  return {
423
1527
  pluginId,
424
1528
  sourceId,
@@ -433,9 +1537,23 @@ export function buildTuiViewModel(options) {
433
1537
  supportedPlatforms: summary?.supportedTargets
434
1538
  ? ['codex', 'claude'].filter((platform) => summary.supportedTargets[platform].length > 0)
435
1539
  : placements.map((placement) => placement.platform),
1540
+ ...(summary?.supportedTargets ? { supportedScopes: {
1541
+ codex: [...summary.supportedTargets.codex],
1542
+ claude: [...summary.supportedTargets.claude],
1543
+ } } : {}),
436
1544
  installedPlacements: placements,
437
1545
  aggregateStatus: unavailable ? 'unavailable' : attention ? 'needsAttention' : installed ? 'installed' : 'available',
438
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 } : {}),
439
1557
  ...(dependencyById.get(pluginId)
440
1558
  ? { dependencySummary: dependencyById.get(pluginId).summary }
441
1559
  : summary && (summary.dependencyTotalCount ?? 0) > 0
@@ -448,69 +1566,133 @@ export function buildTuiViewModel(options) {
448
1566
  issueCount: summary.dependencyIssueCount ?? 0,
449
1567
  } }
450
1568
  : {}),
451
- technicalComponents: targets.flatMap((target) => target.components.map((component) => ({
452
- platform: target.platform,
453
- scope: target.scope,
454
- key: component.key,
455
- state: component.state,
456
- ...(component.error ? { errorCode: component.error.code } : {}),
457
- }))),
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,
458
1595
  };
459
1596
  }).sort((left, right) => compareText(left.displayName, right.displayName)
460
1597
  || compareText(left.sourceId, right.sourceId));
461
- const sourceTabs = options.status.catalog.sources.map((source) => ({
462
- id: source.id,
463
- label: source.id,
464
- count: plugins.filter((plugin) => plugin.sourceId === source.id).length,
465
- primary: source.primary,
466
- state: sourceState(source, options.sourceHealth?.get(source.id)),
467
- }));
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
+ });
468
1611
  const sourceRootIds = new Set([...(options.sourceHealth?.values() ?? [])]
469
- .filter((health) => health.state === 'cache' || health.state === 'degraded' || health.state === 'unavailable')
1612
+ .filter((health) => health.state === 'unavailable')
470
1613
  .map((health) => health.sourceId));
471
- const allTargetIssues = targetIssues(options.status);
472
- const targetImpactCounts = new Map();
1614
+ const allTargetIssues = targetIssues(options.status, options.targetRevisions);
1615
+ const mergedTargetIssues = new Map();
473
1616
  for (const issue of allTargetIssues) {
474
1617
  if (!issue.mergeEligible || !issue.sourceId || !sourceRootIds.has(issue.sourceId))
475
1618
  continue;
476
- targetImpactCounts.set(issue.sourceId, (targetImpactCounts.get(issue.sourceId) ?? 0) + 1);
1619
+ mergedTargetIssues.set(issue.sourceId, [...(mergedTargetIssues.get(issue.sourceId) ?? []), issue]);
477
1620
  }
478
- const issues = enrichDependencyIssuePlacements([
1621
+ const sourceDetails = new Map((options.sourceDetails ?? []).map((detail) => [detail.id, detail]));
1622
+ const issueDrafts = [
479
1623
  ...allTargetIssues.filter((issue) => !issue.mergeEligible
480
1624
  || !issue.sourceId || !sourceRootIds.has(issue.sourceId)),
481
1625
  ...statusIssues(options.status),
482
1626
  ...configIssues(options.pluginSummaries),
483
- ...[...(options.sourceHealth?.values() ?? [])].flatMap((health) => sourceIssue(health, targetImpactCounts.get(health.sourceId) ?? 0) ?? []),
484
- ], options.dependencyOverviews ?? []);
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) => ({
1635
+ ...issue,
1636
+ group: issueGroup(issue),
1637
+ diagnostic: issueDiagnostic(issue, sourceDetails),
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
+ });
485
1658
  return {
486
- plugins,
1659
+ plugins: pluginsWithAttention,
487
1660
  sourceTabs,
488
- sourceDetails: (options.sourceDetails ?? []).map((detail) => ({
489
- ...detail,
490
- pluginCount: plugins.filter((plugin) => plugin.sourceId === detail.id).length,
491
- })),
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
+ }),
492
1665
  issues,
493
1666
  dependencyOverviews: options.dependencyOverviews ?? [],
1667
+ contentOverviews: options.contentOverviews ?? {},
494
1668
  home: {
495
- availablePlugins: plugins.filter((plugin) => plugin.aggregateStatus === 'available').length,
496
- 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,
497
1673
  issueCount: issues.length,
498
1674
  sourceCount: sourceTabs.length,
499
- cachedSourceCount: sourceTabs.filter((source) => source.state === 'cache').length,
500
- 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,
501
1684
  },
502
1685
  };
503
1686
  }
504
1687
  function displayConfigValue(options) {
505
- if (!options.configured)
506
- return options.inherited ? '继承' : '未设置';
507
- if (options.privateValue)
508
- return '已设置';
509
- if (typeof options.value === 'string')
510
- return options.value;
511
- if (options.value === undefined || options.value === null)
512
- return '已设置';
513
- 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;
514
1696
  }
515
1697
  function configSourceLabel(source) {
516
1698
  if (!source)
@@ -519,57 +1701,233 @@ function configSourceLabel(source) {
519
1701
  default: '默认值',
520
1702
  user: '个人默认',
521
1703
  project: '当前项目',
522
- local: '当前项目(仅本机)',
523
1704
  }[source];
524
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
+ }
525
1751
  export function buildConfigGroups(options) {
526
1752
  const entryByKey = new Map(options.read.fields.map((field) => [field.key, field]));
1753
+ const effectiveEntryByKey = new Map((options.effectiveRead ?? options.read).fields.map((field) => [field.key, field]));
527
1754
  const effectivePage = options.read.scope === 'effective';
1755
+ const layerEntry = (layer, key) => options.layerReads?.[layer]?.fields.find((field) => field.key === key);
528
1756
  const fieldModel = (key) => {
529
- const schema = options.schema.schema[key];
1757
+ const schema = options.schema.fields[key];
530
1758
  const entry = entryByKey.get(key);
531
- const presentation = options.schema.presentation?.fields?.[key];
1759
+ const effectiveEntry = effectiveEntryByKey.get(key);
532
1760
  const inherited = effectivePage
533
- ? entry?.sourceLayer === undefined || entry?.sourceLayer === null
1761
+ ? false
534
1762
  : entry?.storedSourceLayer === undefined;
535
1763
  const configured = effectivePage
536
- ? entry?.valueState === 'configured' || entry?.valueState === 'default'
1764
+ ? entry?.sourceLayer !== undefined
1765
+ && entry.sourceLayer !== null
1766
+ && entry.effectiveValue !== undefined
1767
+ && entry.effectiveValue !== null
1768
+ && (entry.valueState === 'configured' || entry.valueState === 'default')
537
1769
  : entry?.storedSourceLayer !== undefined;
538
- 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;
539
1777
  const selectedValue = effectivePage ? entry?.effectiveValue : entry?.storedValue;
1778
+ const selectedRawValue = effectivePage
1779
+ ? options.effectiveRawValues?.[key]
1780
+ : options.rawValues?.[key];
1781
+ const selectedStoredInvalid = !effectivePage
1782
+ && entry?.storedSourceLayer !== undefined
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;
1794
+ const effectiveConfigured = effectiveEntry?.sourceLayer !== undefined
1795
+ && effectiveEntry.sourceLayer !== null
1796
+ && effectiveEntry.effectiveValue !== undefined
1797
+ && effectiveEntry.effectiveValue !== null
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);
540
1833
  return {
541
1834
  key,
542
1835
  title: schema.title ?? key,
543
1836
  ...(schema.description ? { description: schema.description } : {}),
544
1837
  type: schema.type,
545
- displayValue: displayConfigValue({
1838
+ definition: configEditorDefinition(schema),
1839
+ ...(summary ? { summary } : {}),
1840
+ ...(itemSummary ? { itemSummary } : {}),
1841
+ displayValue: selectedStoredInvalid ? '需修正' : displayConfigValue({
1842
+ field: schema,
1843
+ summary,
1844
+ itemSummary,
546
1845
  privateValue,
547
1846
  inherited,
548
1847
  configured,
549
- value: selectedValue,
1848
+ value: schema.type === 'object' ? selectedRawValue ?? selectedValue : selectedValue,
550
1849
  }),
551
1850
  required: schema.required === true,
552
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,
553
1857
  dirty: options.dirtyKeys?.has(key) === true,
554
1858
  inherited,
555
1859
  configured,
556
1860
  sourceLabel: effectivePage
557
1861
  ? configSourceLabel(entry?.sourceLayer ?? null)
558
1862
  : configured ? '本层已设置' : '继承',
1863
+ effectiveDisplayValue: effectiveEntry?.invalidOverride ? '需修正' : displayConfigValue({
1864
+ field: schema,
1865
+ summary,
1866
+ itemSummary,
1867
+ privateValue,
1868
+ inherited: false,
1869
+ configured: effectiveConfigured,
1870
+ value: schema.type === 'object'
1871
+ ? options.effectiveRawValues?.[key] ?? effectiveEntry?.effectiveValue
1872
+ : effectiveEntry?.effectiveValue,
1873
+ }),
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
+ } : {}),
559
1904
  ...(!privateValue && selectedValue !== undefined
560
1905
  ? { value: selectedValue }
561
1906
  : {}),
1907
+ ...(!privateValue && effectiveEntry?.effectiveValue !== undefined && effectiveEntry.effectiveValue !== null
1908
+ ? { effectiveValue: effectiveEntry.effectiveValue }
1909
+ : {}),
1910
+ ...(!privateValue && (effectivePage
1911
+ ? options.effectiveRawValues?.[key]
1912
+ : selectedRawValue ?? (schema.type === 'object' && !configured
1913
+ ? options.effectiveRawValues?.[key]
1914
+ : undefined)) !== undefined
1915
+ ? { draftValue: (effectivePage
1916
+ ? options.effectiveRawValues?.[key]
1917
+ : selectedRawValue ?? options.effectiveRawValues?.[key]) }
1918
+ : {}),
562
1919
  ...(!privateValue && schema.default !== undefined ? { defaultValue: schema.default } : {}),
563
- ...(schema.values ? { enumValues: [...schema.values] } : {}),
564
- ...(presentation?.valueLabels ? { valueLabels: { ...presentation.valueLabels } } : {}),
565
- ...(presentation?.recommendedValue !== undefined
566
- ? { recommendedValue: presentation.recommendedValue }
1920
+ ...(!privateValue && choices ? { choices: choices.presets } : {}),
1921
+ allowCustom: choices?.allowCustom ?? true,
1922
+ ...(!privateValue && !configObjectHasSensitiveChild(schema)
1923
+ && (selectedRawValue ?? selectedValue) !== undefined && (selectedRawValue ?? selectedValue) !== null
1924
+ && matchPluginConfigPreset(schema, (selectedRawValue ?? selectedValue))
1925
+ ? { matchedPresetId: matchPluginConfigPreset(schema, (selectedRawValue ?? selectedValue)).id }
567
1926
  : {}),
568
- ...(presentation?.placeholder ? { placeholder: presentation.placeholder } : {}),
569
- ...(presentation?.unit ? { unit: presentation.unit } : {}),
570
- ...(schema.min !== undefined ? { min: schema.min } : {}),
571
- ...(schema.max !== undefined ? { max: schema.max } : {}),
572
- advanced: presentation?.advanced === true,
1927
+ ...(schema.type === 'number' && schema.validation?.minimum !== undefined ? { min: schema.validation.minimum } : {}),
1928
+ ...(schema.type === 'number' && schema.validation?.maximum !== undefined ? { max: schema.validation.maximum } : {}),
1929
+ advanced: false,
1930
+ sharedNativeRender: options.sharedNativeRefreshKeys?.has(key) === true,
573
1931
  };
574
1932
  };
575
1933
  const assigned = new Set();
@@ -593,18 +1951,20 @@ export function buildConfigGroups(options) {
593
1951
  }
594
1952
  return id;
595
1953
  };
596
- const remaining = Object.keys(options.schema.schema).filter((key) => !assigned.has(key));
1954
+ const remaining = Object.keys(options.schema.fields)
1955
+ .filter((key) => !assigned.has(key));
597
1956
  if (!options.schema.presentation) {
598
1957
  groups.push({
599
1958
  id: 'general',
600
1959
  title: '插件设置',
601
1960
  advanced: false,
602
- fields: Object.keys(options.schema.schema).map(fieldModel),
1961
+ fields: Object.keys(options.schema.fields)
1962
+ .map(fieldModel),
603
1963
  });
604
1964
  }
605
1965
  else {
606
- const ordinary = remaining.filter((key) => options.schema.presentation?.fields?.[key]?.advanced !== true);
607
- const advanced = remaining.filter((key) => options.schema.presentation?.fields?.[key]?.advanced === true);
1966
+ const ordinary = remaining;
1967
+ const advanced = [];
608
1968
  if (ordinary.length > 0) {
609
1969
  groups.push({
610
1970
  id: syntheticGroupId('general'),
@@ -625,28 +1985,16 @@ export function buildConfigGroups(options) {
625
1985
  return groups;
626
1986
  }
627
1987
  export function buildConfigPage(options) {
628
- const privateFields = new Set(Object.entries(options.schema.schema)
629
- .filter(([, field]) => field.sensitive === true || field.type === 'file' || field.type === 'directory')
630
- .map(([key]) => key));
631
- const presets = (options.schema.presentation?.presets ?? []).map((preset) => {
632
- const privateFieldTitles = Object.keys(preset.values)
633
- .filter((key) => privateFields.has(key))
634
- .map((key) => options.schema.schema[key]?.title ?? key);
635
- return {
636
- id: preset.id,
637
- title: preset.title,
638
- ...(preset.description ? { description: preset.description } : {}),
639
- values: Object.fromEntries(Object.entries(preset.values)
640
- .filter(([key]) => !privateFields.has(key))),
641
- ...(privateFieldTitles.length > 0 ? { privateFieldTitles } : {}),
642
- };
643
- });
644
1988
  return {
645
1989
  pluginId: options.pluginId,
646
- layer: { scope: options.read.scope, projectLayer: options.read.projectLayer },
1990
+ layer: { scope: options.read.scope },
647
1991
  groups: buildConfigGroups(options),
648
- presets,
1992
+ presets: [],
649
1993
  storedCount: options.read.fields.filter((field) => field.storedSourceLayer !== undefined).length,
650
1994
  };
651
1995
  }
1996
+ export function configValuesEqual(left, right) {
1997
+ return left !== undefined && right !== undefined
1998
+ && stablePluginConfigStringify(left) === stablePluginConfigStringify(right);
1999
+ }
652
2000
  //# sourceMappingURL=model.js.map