@lesliechan721/aw 0.1.0-beta.10

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 (508) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +51 -0
  2. package/dist/authoring/assets/aw-config.mjs +1025 -0
  3. package/dist/authoring/assets/aw-config.mjs.map +1 -0
  4. package/dist/authoring/scripts/add-skill.d.mts +2 -0
  5. package/dist/authoring/scripts/add-skill.mjs +355 -0
  6. package/dist/authoring/scripts/add-skill.mjs.map +1 -0
  7. package/dist/authoring/scripts/init-plugin.d.mts +2 -0
  8. package/dist/authoring/scripts/init-plugin.mjs +487 -0
  9. package/dist/authoring/scripts/init-plugin.mjs.map +1 -0
  10. package/dist/authoring/scripts/lib/javascript-import-scanner.d.mts +4 -0
  11. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs +261 -0
  12. package/dist/authoring/scripts/lib/javascript-import-scanner.mjs.map +1 -0
  13. package/dist/authoring/scripts/lib/plugin-root.d.mts +26 -0
  14. package/dist/authoring/scripts/lib/plugin-root.mjs +125 -0
  15. package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -0
  16. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +69 -0
  17. package/dist/authoring/scripts/lib/plugin-version-check.mjs +563 -0
  18. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -0
  19. package/dist/authoring/scripts/lib/semver.d.mts +9 -0
  20. package/dist/authoring/scripts/lib/semver.mjs +63 -0
  21. package/dist/authoring/scripts/lib/semver.mjs.map +1 -0
  22. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +111 -0
  23. package/dist/authoring/scripts/lib/skill-scaffold.mjs +353 -0
  24. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -0
  25. package/dist/authoring/scripts/plugin-status.d.mts +2 -0
  26. package/dist/authoring/scripts/plugin-status.mjs +93 -0
  27. package/dist/authoring/scripts/plugin-status.mjs.map +1 -0
  28. package/dist/authoring/scripts/remove-plugin.d.mts +2 -0
  29. package/dist/authoring/scripts/remove-plugin.mjs +367 -0
  30. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  31. package/dist/authoring/scripts/validate-plugin.d.mts +2 -0
  32. package/dist/authoring/scripts/validate-plugin.mjs +3465 -0
  33. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -0
  34. package/dist/authoring/scripts/validate-skill.d.mts +2 -0
  35. package/dist/authoring/scripts/validate-skill.mjs +126 -0
  36. package/dist/authoring/scripts/validate-skill.mjs.map +1 -0
  37. package/dist/authoring/templates/aw-config-runtime.d.mts +2 -0
  38. package/dist/authoring/templates/aw-config-runtime.mjs +407 -0
  39. package/dist/authoring/templates/aw-config-runtime.mjs.map +1 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +8 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
  44. package/dist/scripts/aw-skill-eval-materializer.js +309 -0
  45. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  46. package/dist/scripts/bundle-plugins.d.ts +17 -0
  47. package/dist/scripts/bundle-plugins.js +126 -0
  48. package/dist/scripts/bundle-plugins.js.map +1 -0
  49. package/dist/scripts/plugin-staging.d.ts +26 -0
  50. package/dist/scripts/plugin-staging.js +76 -0
  51. package/dist/scripts/plugin-staging.js.map +1 -0
  52. package/dist/scripts/test-plugin-scripts.d.ts +7 -0
  53. package/dist/scripts/test-plugin-scripts.js +610 -0
  54. package/dist/scripts/test-plugin-scripts.js.map +1 -0
  55. package/dist/src/adapters/helpers.d.ts +44 -0
  56. package/dist/src/adapters/helpers.js +95 -0
  57. package/dist/src/adapters/helpers.js.map +1 -0
  58. package/dist/src/authoring/workspace-commands.d.ts +70 -0
  59. package/dist/src/authoring/workspace-commands.js +158 -0
  60. package/dist/src/authoring/workspace-commands.js.map +1 -0
  61. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  62. package/dist/src/commands/aw-capabilities.js +254 -0
  63. package/dist/src/commands/aw-capabilities.js.map +1 -0
  64. package/dist/src/commands/catalog-dev-source.d.ts +120 -0
  65. package/dist/src/commands/catalog-dev-source.js +434 -0
  66. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  67. package/dist/src/commands/catalog-generation.d.ts +16 -0
  68. package/dist/src/commands/catalog-generation.js +9 -0
  69. package/dist/src/commands/catalog-generation.js.map +1 -0
  70. package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
  71. package/dist/src/commands/catalog-publication-inspection.js +711 -0
  72. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  73. package/dist/src/commands/catalog-publication-journal.d.ts +56 -0
  74. package/dist/src/commands/catalog-publication-journal.js +215 -0
  75. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  76. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  77. package/dist/src/commands/catalog-publication-target.js +95 -0
  78. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  79. package/dist/src/commands/catalog-publish.d.ts +103 -0
  80. package/dist/src/commands/catalog-publish.js +804 -0
  81. package/dist/src/commands/catalog-publish.js.map +1 -0
  82. package/dist/src/commands/catalog-source.d.ts +134 -0
  83. package/dist/src/commands/catalog-source.js +377 -0
  84. package/dist/src/commands/catalog-source.js.map +1 -0
  85. package/dist/src/commands/catalog-status.d.ts +56 -0
  86. package/dist/src/commands/catalog-status.js +217 -0
  87. package/dist/src/commands/catalog-status.js.map +1 -0
  88. package/dist/src/commands/catalog-store.d.ts +80 -0
  89. package/dist/src/commands/catalog-store.js +348 -0
  90. package/dist/src/commands/catalog-store.js.map +1 -0
  91. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  92. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  93. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  94. package/dist/src/commands/console.d.ts +3 -0
  95. package/dist/src/commands/console.js +44 -0
  96. package/dist/src/commands/console.js.map +1 -0
  97. package/dist/src/commands/dependency-operation-plan.d.ts +58 -0
  98. package/dist/src/commands/dependency-operation-plan.js +261 -0
  99. package/dist/src/commands/dependency-operation-plan.js.map +1 -0
  100. package/dist/src/commands/init.d.ts +25 -0
  101. package/dist/src/commands/init.js +42 -0
  102. package/dist/src/commands/init.js.map +1 -0
  103. package/dist/src/commands/offline-cleanup-evidence.d.ts +59 -0
  104. package/dist/src/commands/offline-cleanup-evidence.js +163 -0
  105. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  106. package/dist/src/commands/operation-plan.d.ts +126 -0
  107. package/dist/src/commands/operation-plan.js +47 -0
  108. package/dist/src/commands/operation-plan.js.map +1 -0
  109. package/dist/src/commands/operation-planner.d.ts +46 -0
  110. package/dist/src/commands/operation-planner.js +139 -0
  111. package/dist/src/commands/operation-planner.js.map +1 -0
  112. package/dist/src/commands/options.d.ts +70 -0
  113. package/dist/src/commands/options.js +194 -0
  114. package/dist/src/commands/options.js.map +1 -0
  115. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  116. package/dist/src/commands/plugin-capabilities.js +159 -0
  117. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  118. package/dist/src/commands/rebuild-command.d.ts +88 -0
  119. package/dist/src/commands/rebuild-command.js +3626 -0
  120. package/dist/src/commands/rebuild-command.js.map +1 -0
  121. package/dist/src/commands/rebuild-integration.d.ts +190 -0
  122. package/dist/src/commands/rebuild-integration.js +4439 -0
  123. package/dist/src/commands/rebuild-integration.js.map +1 -0
  124. package/dist/src/commands/rebuild-operations.d.ts +100 -0
  125. package/dist/src/commands/rebuild-operations.js +625 -0
  126. package/dist/src/commands/rebuild-operations.js.map +1 -0
  127. package/dist/src/commands/reconcile-operation-plan.d.ts +26 -0
  128. package/dist/src/commands/reconcile-operation-plan.js +28 -0
  129. package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
  130. package/dist/src/commands/review-publication.d.ts +99 -0
  131. package/dist/src/commands/review-publication.js +904 -0
  132. package/dist/src/commands/review-publication.js.map +1 -0
  133. package/dist/src/commands/runtime.d.ts +172 -0
  134. package/dist/src/commands/runtime.js +720 -0
  135. package/dist/src/commands/runtime.js.map +1 -0
  136. package/dist/src/commands/skill-eval.d.ts +33 -0
  137. package/dist/src/commands/skill-eval.js +363 -0
  138. package/dist/src/commands/skill-eval.js.map +1 -0
  139. package/dist/src/commands/status-v6.d.ts +206 -0
  140. package/dist/src/commands/status-v6.js +2175 -0
  141. package/dist/src/commands/status-v6.js.map +1 -0
  142. package/dist/src/commands/sync-command.d.ts +23 -0
  143. package/dist/src/commands/sync-command.js +242 -0
  144. package/dist/src/commands/sync-command.js.map +1 -0
  145. package/dist/src/commands/wizard.d.ts +56 -0
  146. package/dist/src/commands/wizard.js +58 -0
  147. package/dist/src/commands/wizard.js.map +1 -0
  148. package/dist/src/commands/write-commands.d.ts +7 -0
  149. package/dist/src/commands/write-commands.js +190 -0
  150. package/dist/src/commands/write-commands.js.map +1 -0
  151. package/dist/src/constants.d.ts +31 -0
  152. package/dist/src/constants.js +32 -0
  153. package/dist/src/constants.js.map +1 -0
  154. package/dist/src/eval/manifest-v1.d.ts +67 -0
  155. package/dist/src/eval/manifest-v1.js +347 -0
  156. package/dist/src/eval/manifest-v1.js.map +1 -0
  157. package/dist/src/generators/agents.d.ts +3 -0
  158. package/dist/src/generators/agents.js +46 -0
  159. package/dist/src/generators/agents.js.map +1 -0
  160. package/dist/src/generators/hooks.d.ts +15 -0
  161. package/dist/src/generators/hooks.js +76 -0
  162. package/dist/src/generators/hooks.js.map +1 -0
  163. package/dist/src/generators/instructions.d.ts +13 -0
  164. package/dist/src/generators/instructions.js +66 -0
  165. package/dist/src/generators/instructions.js.map +1 -0
  166. package/dist/src/generators/local-plugin.d.ts +56 -0
  167. package/dist/src/generators/local-plugin.js +1373 -0
  168. package/dist/src/generators/local-plugin.js.map +1 -0
  169. package/dist/src/generators/permissions.d.ts +10 -0
  170. package/dist/src/generators/permissions.js +37 -0
  171. package/dist/src/generators/permissions.js.map +1 -0
  172. package/dist/src/generators/shared.d.ts +2 -0
  173. package/dist/src/generators/shared.js +15 -0
  174. package/dist/src/generators/shared.js.map +1 -0
  175. package/dist/src/index.d.ts +41 -0
  176. package/dist/src/index.js +3327 -0
  177. package/dist/src/index.js.map +1 -0
  178. package/dist/src/output/command-receipt.d.ts +87 -0
  179. package/dist/src/output/command-receipt.js +302 -0
  180. package/dist/src/output/command-receipt.js.map +1 -0
  181. package/dist/src/output/debug-log.d.ts +4 -0
  182. package/dist/src/output/debug-log.js +97 -0
  183. package/dist/src/output/debug-log.js.map +1 -0
  184. package/dist/src/output/equivalent-command.d.ts +9 -0
  185. package/dist/src/output/equivalent-command.js +39 -0
  186. package/dist/src/output/equivalent-command.js.map +1 -0
  187. package/dist/src/output/model.d.ts +76 -0
  188. package/dist/src/output/model.js +2 -0
  189. package/dist/src/output/model.js.map +1 -0
  190. package/dist/src/output/rebuild-progress.d.ts +21 -0
  191. package/dist/src/output/rebuild-progress.js +128 -0
  192. package/dist/src/output/rebuild-progress.js.map +1 -0
  193. package/dist/src/output/redact.d.ts +3 -0
  194. package/dist/src/output/redact.js +39 -0
  195. package/dist/src/output/redact.js.map +1 -0
  196. package/dist/src/output/reporter.d.ts +9 -0
  197. package/dist/src/output/reporter.js +133 -0
  198. package/dist/src/output/reporter.js.map +1 -0
  199. package/dist/src/portable/commands.d.ts +124 -0
  200. package/dist/src/portable/commands.js +557 -0
  201. package/dist/src/portable/commands.js.map +1 -0
  202. package/dist/src/portable/materializer.d.ts +50 -0
  203. package/dist/src/portable/materializer.js +462 -0
  204. package/dist/src/portable/materializer.js.map +1 -0
  205. package/dist/src/portable/skills-cli.d.ts +43 -0
  206. package/dist/src/portable/skills-cli.js +148 -0
  207. package/dist/src/portable/skills-cli.js.map +1 -0
  208. package/dist/src/tui/catalog-freshness.d.ts +1 -0
  209. package/dist/src/tui/catalog-freshness.js +2 -0
  210. package/dist/src/tui/catalog-freshness.js.map +1 -0
  211. package/dist/src/tui/catalog-generation.d.ts +21 -0
  212. package/dist/src/tui/catalog-generation.js +116 -0
  213. package/dist/src/tui/catalog-generation.js.map +1 -0
  214. package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
  215. package/dist/src/tui/catalog-sources/projection.js +17 -0
  216. package/dist/src/tui/catalog-sources/projection.js.map +1 -0
  217. package/dist/src/tui/catalog-sources/review.d.ts +3 -0
  218. package/dist/src/tui/catalog-sources/review.js +13 -0
  219. package/dist/src/tui/catalog-sources/review.js.map +1 -0
  220. package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
  221. package/dist/src/tui/catalog-sources/screens.js +33 -0
  222. package/dist/src/tui/catalog-sources/screens.js.map +1 -0
  223. package/dist/src/tui/catalog-sources/session.d.ts +22 -0
  224. package/dist/src/tui/catalog-sources/session.js +225 -0
  225. package/dist/src/tui/catalog-sources/session.js.map +1 -0
  226. package/dist/src/tui/catalog-sources/types.d.ts +41 -0
  227. package/dist/src/tui/catalog-sources/types.js +2 -0
  228. package/dist/src/tui/catalog-sources/types.js.map +1 -0
  229. package/dist/src/tui/components.d.ts +102 -0
  230. package/dist/src/tui/components.js +184 -0
  231. package/dist/src/tui/components.js.map +1 -0
  232. package/dist/src/tui/console-app-v4.d.ts +20 -0
  233. package/dist/src/tui/console-app-v4.js +5066 -0
  234. package/dist/src/tui/console-app-v4.js.map +1 -0
  235. package/dist/src/tui/controller-runtime.d.ts +23 -0
  236. package/dist/src/tui/controller-runtime.js +2011 -0
  237. package/dist/src/tui/controller-runtime.js.map +1 -0
  238. package/dist/src/tui/controller.d.ts +557 -0
  239. package/dist/src/tui/controller.js +2120 -0
  240. package/dist/src/tui/controller.js.map +1 -0
  241. package/dist/src/tui/diagnostics.d.ts +30 -0
  242. package/dist/src/tui/diagnostics.js +71 -0
  243. package/dist/src/tui/diagnostics.js.map +1 -0
  244. package/dist/src/tui/index.d.ts +17 -0
  245. package/dist/src/tui/index.js +17 -0
  246. package/dist/src/tui/index.js.map +1 -0
  247. package/dist/src/tui/input-display.d.ts +8 -0
  248. package/dist/src/tui/input-display.js +36 -0
  249. package/dist/src/tui/input-display.js.map +1 -0
  250. package/dist/src/tui/keys.d.ts +12 -0
  251. package/dist/src/tui/keys.js +22 -0
  252. package/dist/src/tui/keys.js.map +1 -0
  253. package/dist/src/tui/layout.d.ts +12 -0
  254. package/dist/src/tui/layout.js +19 -0
  255. package/dist/src/tui/layout.js.map +1 -0
  256. package/dist/src/tui/model.d.ts +424 -0
  257. package/dist/src/tui/model.js +1493 -0
  258. package/dist/src/tui/model.js.map +1 -0
  259. package/dist/src/tui/operation-review-planner.d.ts +21 -0
  260. package/dist/src/tui/operation-review-planner.js +622 -0
  261. package/dist/src/tui/operation-review-planner.js.map +1 -0
  262. package/dist/src/tui/prompt.d.ts +8 -0
  263. package/dist/src/tui/prompt.js +231 -0
  264. package/dist/src/tui/prompt.js.map +1 -0
  265. package/dist/src/tui/router.d.ts +206 -0
  266. package/dist/src/tui/router.js +137 -0
  267. package/dist/src/tui/router.js.map +1 -0
  268. package/dist/src/tui/runner.d.ts +11 -0
  269. package/dist/src/tui/runner.js +71 -0
  270. package/dist/src/tui/runner.js.map +1 -0
  271. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  272. package/dist/src/tui/source-ref-presets.js +11 -0
  273. package/dist/src/tui/source-ref-presets.js.map +1 -0
  274. package/dist/src/tui/theme.d.ts +26 -0
  275. package/dist/src/tui/theme.js +28 -0
  276. package/dist/src/tui/theme.js.map +1 -0
  277. package/dist/src/tui/viewport.d.ts +20 -0
  278. package/dist/src/tui/viewport.js +29 -0
  279. package/dist/src/tui/viewport.js.map +1 -0
  280. package/dist/src/tui/views.d.ts +57 -0
  281. package/dist/src/tui/views.js +201 -0
  282. package/dist/src/tui/views.js.map +1 -0
  283. package/dist/src/tui/wizard.d.ts +11 -0
  284. package/dist/src/tui/wizard.js +323 -0
  285. package/dist/src/tui/wizard.js.map +1 -0
  286. package/dist/src/types.d.ts +474 -0
  287. package/dist/src/types.js +34 -0
  288. package/dist/src/types.js.map +1 -0
  289. package/dist/src/utils/agent-version.d.ts +19 -0
  290. package/dist/src/utils/agent-version.js +205 -0
  291. package/dist/src/utils/agent-version.js.map +1 -0
  292. package/dist/src/utils/artifact-plan.d.ts +27 -0
  293. package/dist/src/utils/artifact-plan.js +453 -0
  294. package/dist/src/utils/artifact-plan.js.map +1 -0
  295. package/dist/src/utils/artifact-renderer.d.ts +129 -0
  296. package/dist/src/utils/artifact-renderer.js +400 -0
  297. package/dist/src/utils/artifact-renderer.js.map +1 -0
  298. package/dist/src/utils/artifact-request.d.ts +16 -0
  299. package/dist/src/utils/artifact-request.js +42 -0
  300. package/dist/src/utils/artifact-request.js.map +1 -0
  301. package/dist/src/utils/artifact-sources.d.ts +8 -0
  302. package/dist/src/utils/artifact-sources.js +10 -0
  303. package/dist/src/utils/artifact-sources.js.map +1 -0
  304. package/dist/src/utils/aw-home-mutation-lock.d.ts +11 -0
  305. package/dist/src/utils/aw-home-mutation-lock.js +83 -0
  306. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -0
  307. package/dist/src/utils/bundle-integrity.d.ts +2 -0
  308. package/dist/src/utils/bundle-integrity.js +63 -0
  309. package/dist/src/utils/bundle-integrity.js.map +1 -0
  310. package/dist/src/utils/bundle-layout.d.ts +4 -0
  311. package/dist/src/utils/bundle-layout.js +531 -0
  312. package/dist/src/utils/bundle-layout.js.map +1 -0
  313. package/dist/src/utils/catalog-digests.d.ts +18 -0
  314. package/dist/src/utils/catalog-digests.js +156 -0
  315. package/dist/src/utils/catalog-digests.js.map +1 -0
  316. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  317. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  318. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  319. package/dist/src/utils/catalog-generation-error.d.ts +16 -0
  320. package/dist/src/utils/catalog-generation-error.js +30 -0
  321. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  322. package/dist/src/utils/catalog-generation-resolver.d.ts +32 -0
  323. package/dist/src/utils/catalog-generation-resolver.js +251 -0
  324. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  325. package/dist/src/utils/catalog-generation-service.d.ts +161 -0
  326. package/dist/src/utils/catalog-generation-service.js +1237 -0
  327. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  328. package/dist/src/utils/catalog-generation-state.d.ts +22 -0
  329. package/dist/src/utils/catalog-generation-state.js +192 -0
  330. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  331. package/dist/src/utils/catalog-generation-store.d.ts +57 -0
  332. package/dist/src/utils/catalog-generation-store.js +397 -0
  333. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  334. package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
  335. package/dist/src/utils/catalog-manifest-v7.js +98 -0
  336. package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
  337. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  338. package/dist/src/utils/catalog-operation-context.js +141 -0
  339. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  340. package/dist/src/utils/catalog-public-error.d.ts +19 -0
  341. package/dist/src/utils/catalog-public-error.js +100 -0
  342. package/dist/src/utils/catalog-public-error.js.map +1 -0
  343. package/dist/src/utils/catalog-runtime-projection.d.ts +33 -0
  344. package/dist/src/utils/catalog-runtime-projection.js +77 -0
  345. package/dist/src/utils/catalog-runtime-projection.js.map +1 -0
  346. package/dist/src/utils/catalog-snapshot.d.ts +10 -0
  347. package/dist/src/utils/catalog-snapshot.js +20 -0
  348. package/dist/src/utils/catalog-snapshot.js.map +1 -0
  349. package/dist/src/utils/catalog-source-identity.d.ts +5 -0
  350. package/dist/src/utils/catalog-source-identity.js +137 -0
  351. package/dist/src/utils/catalog-source-identity.js.map +1 -0
  352. package/dist/src/utils/catalog-source-impact.d.ts +20 -0
  353. package/dist/src/utils/catalog-source-impact.js +222 -0
  354. package/dist/src/utils/catalog-source-impact.js.map +1 -0
  355. package/dist/src/utils/catalog-source-presentation.d.ts +1 -0
  356. package/dist/src/utils/catalog-source-presentation.js +32 -0
  357. package/dist/src/utils/catalog-source-presentation.js.map +1 -0
  358. package/dist/src/utils/catalog-source-references.d.ts +66 -0
  359. package/dist/src/utils/catalog-source-references.js +1180 -0
  360. package/dist/src/utils/catalog-source-references.js.map +1 -0
  361. package/dist/src/utils/catalog-sources.d.ts +53 -0
  362. package/dist/src/utils/catalog-sources.js +456 -0
  363. package/dist/src/utils/catalog-sources.js.map +1 -0
  364. package/dist/src/utils/codex-config.d.ts +126 -0
  365. package/dist/src/utils/codex-config.js +1032 -0
  366. package/dist/src/utils/codex-config.js.map +1 -0
  367. package/dist/src/utils/codex-manager.d.ts +81 -0
  368. package/dist/src/utils/codex-manager.js +810 -0
  369. package/dist/src/utils/codex-manager.js.map +1 -0
  370. package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
  371. package/dist/src/utils/codex-marketplace-path.js +45 -0
  372. package/dist/src/utils/codex-marketplace-path.js.map +1 -0
  373. package/dist/src/utils/codex-native-observation.d.ts +16 -0
  374. package/dist/src/utils/codex-native-observation.js +22 -0
  375. package/dist/src/utils/codex-native-observation.js.map +1 -0
  376. package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
  377. package/dist/src/utils/codex-native-render-receipt.js +205 -0
  378. package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
  379. package/dist/src/utils/codex-reconciler.d.ts +128 -0
  380. package/dist/src/utils/codex-reconciler.js +294 -0
  381. package/dist/src/utils/codex-reconciler.js.map +1 -0
  382. package/dist/src/utils/codex-roles.d.ts +71 -0
  383. package/dist/src/utils/codex-roles.js +257 -0
  384. package/dist/src/utils/codex-roles.js.map +1 -0
  385. package/dist/src/utils/codex-shared-native.d.ts +63 -0
  386. package/dist/src/utils/codex-shared-native.js +142 -0
  387. package/dist/src/utils/codex-shared-native.js.map +1 -0
  388. package/dist/src/utils/codex-skill-staging.d.ts +11 -0
  389. package/dist/src/utils/codex-skill-staging.js +8 -0
  390. package/dist/src/utils/codex-skill-staging.js.map +1 -0
  391. package/dist/src/utils/codex-surface-analysis.d.ts +45 -0
  392. package/dist/src/utils/codex-surface-analysis.js +245 -0
  393. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  394. package/dist/src/utils/config-value.d.ts +3 -0
  395. package/dist/src/utils/config-value.js +23 -0
  396. package/dist/src/utils/config-value.js.map +1 -0
  397. package/dist/src/utils/dynamic-skill-tree.d.ts +36 -0
  398. package/dist/src/utils/dynamic-skill-tree.js +466 -0
  399. package/dist/src/utils/dynamic-skill-tree.js.map +1 -0
  400. package/dist/src/utils/file-rules.d.ts +1 -0
  401. package/dist/src/utils/file-rules.js +2 -0
  402. package/dist/src/utils/file-rules.js.map +1 -0
  403. package/dist/src/utils/git-catalog-provider.d.ts +22 -0
  404. package/dist/src/utils/git-catalog-provider.js +497 -0
  405. package/dist/src/utils/git-catalog-provider.js.map +1 -0
  406. package/dist/src/utils/hooks-contract.d.ts +1 -0
  407. package/dist/src/utils/hooks-contract.js +2 -0
  408. package/dist/src/utils/hooks-contract.js.map +1 -0
  409. package/dist/src/utils/json.d.ts +4 -0
  410. package/dist/src/utils/json.js +29 -0
  411. package/dist/src/utils/json.js.map +1 -0
  412. package/dist/src/utils/local-marketplace.d.ts +41 -0
  413. package/dist/src/utils/local-marketplace.js +316 -0
  414. package/dist/src/utils/local-marketplace.js.map +1 -0
  415. package/dist/src/utils/manifest.d.ts +11 -0
  416. package/dist/src/utils/manifest.js +33 -0
  417. package/dist/src/utils/manifest.js.map +1 -0
  418. package/dist/src/utils/marketplace.d.ts +3 -0
  419. package/dist/src/utils/marketplace.js +76 -0
  420. package/dist/src/utils/marketplace.js.map +1 -0
  421. package/dist/src/utils/multi-source-catalog.d.ts +66 -0
  422. package/dist/src/utils/multi-source-catalog.js +345 -0
  423. package/dist/src/utils/multi-source-catalog.js.map +1 -0
  424. package/dist/src/utils/native-cli.d.ts +118 -0
  425. package/dist/src/utils/native-cli.js +518 -0
  426. package/dist/src/utils/native-cli.js.map +1 -0
  427. package/dist/src/utils/orphan-marketplace-cleanup.d.ts +18 -0
  428. package/dist/src/utils/orphan-marketplace-cleanup.js +198 -0
  429. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -0
  430. package/dist/src/utils/ownership.d.ts +194 -0
  431. package/dist/src/utils/ownership.js +723 -0
  432. package/dist/src/utils/ownership.js.map +1 -0
  433. package/dist/src/utils/path-catalog-provider.d.ts +7 -0
  434. package/dist/src/utils/path-catalog-provider.js +74 -0
  435. package/dist/src/utils/path-catalog-provider.js.map +1 -0
  436. package/dist/src/utils/paths.d.ts +40 -0
  437. package/dist/src/utils/paths.js +137 -0
  438. package/dist/src/utils/paths.js.map +1 -0
  439. package/dist/src/utils/platform-entries.d.ts +1 -0
  440. package/dist/src/utils/platform-entries.js +2 -0
  441. package/dist/src/utils/platform-entries.js.map +1 -0
  442. package/dist/src/utils/platform-support.d.ts +1 -0
  443. package/dist/src/utils/platform-support.js +2 -0
  444. package/dist/src/utils/platform-support.js.map +1 -0
  445. package/dist/src/utils/plugin-config-write.d.ts +110 -0
  446. package/dist/src/utils/plugin-config-write.js +773 -0
  447. package/dist/src/utils/plugin-config-write.js.map +1 -0
  448. package/dist/src/utils/plugin-config.d.ts +389 -0
  449. package/dist/src/utils/plugin-config.js +1642 -0
  450. package/dist/src/utils/plugin-config.js.map +1 -0
  451. package/dist/src/utils/plugin-dependencies.d.ts +12 -0
  452. package/dist/src/utils/plugin-dependencies.js +162 -0
  453. package/dist/src/utils/plugin-dependencies.js.map +1 -0
  454. package/dist/src/utils/plugin-intent-state.d.ts +34 -0
  455. package/dist/src/utils/plugin-intent-state.js +289 -0
  456. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  457. package/dist/src/utils/plugin-script-contract.d.ts +1 -0
  458. package/dist/src/utils/plugin-script-contract.js +2 -0
  459. package/dist/src/utils/plugin-script-contract.js.map +1 -0
  460. package/dist/src/utils/plugin-scripts.d.ts +101 -0
  461. package/dist/src/utils/plugin-scripts.js +253 -0
  462. package/dist/src/utils/plugin-scripts.js.map +1 -0
  463. package/dist/src/utils/process-lock.d.ts +47 -0
  464. package/dist/src/utils/process-lock.js +532 -0
  465. package/dist/src/utils/process-lock.js.map +1 -0
  466. package/dist/src/utils/process-tree.d.ts +2 -0
  467. package/dist/src/utils/process-tree.js +2 -0
  468. package/dist/src/utils/process-tree.js.map +1 -0
  469. package/dist/src/utils/project-boundary.d.ts +9 -0
  470. package/dist/src/utils/project-boundary.js +67 -0
  471. package/dist/src/utils/project-boundary.js.map +1 -0
  472. package/dist/src/utils/rendered-contract.d.ts +8 -0
  473. package/dist/src/utils/rendered-contract.js +134 -0
  474. package/dist/src/utils/rendered-contract.js.map +1 -0
  475. package/dist/src/utils/runtime-skill-closure.d.ts +13 -0
  476. package/dist/src/utils/runtime-skill-closure.js +863 -0
  477. package/dist/src/utils/runtime-skill-closure.js.map +1 -0
  478. package/dist/src/utils/safe-fs.d.ts +21 -0
  479. package/dist/src/utils/safe-fs.js +111 -0
  480. package/dist/src/utils/safe-fs.js.map +1 -0
  481. package/dist/src/utils/skill-authoring.d.ts +1 -0
  482. package/dist/src/utils/skill-authoring.js +2 -0
  483. package/dist/src/utils/skill-authoring.js.map +1 -0
  484. package/dist/src/utils/skill-validation.d.ts +37 -0
  485. package/dist/src/utils/skill-validation.js +326 -0
  486. package/dist/src/utils/skill-validation.js.map +1 -0
  487. package/dist/src/utils/slash-commands.d.ts +1 -0
  488. package/dist/src/utils/slash-commands.js +2 -0
  489. package/dist/src/utils/slash-commands.js.map +1 -0
  490. package/dist/src/utils/state-paths.d.ts +14 -0
  491. package/dist/src/utils/state-paths.js +139 -0
  492. package/dist/src/utils/state-paths.js.map +1 -0
  493. package/dist/src/utils/target-eligibility.d.ts +13 -0
  494. package/dist/src/utils/target-eligibility.js +35 -0
  495. package/dist/src/utils/target-eligibility.js.map +1 -0
  496. package/dist/src/utils/target-order.d.ts +7 -0
  497. package/dist/src/utils/target-order.js +10 -0
  498. package/dist/src/utils/target-order.js.map +1 -0
  499. package/dist/src/utils/template-engine.d.ts +1 -0
  500. package/dist/src/utils/template-engine.js +2 -0
  501. package/dist/src/utils/template-engine.js.map +1 -0
  502. package/dist/src/utils/test-path-fence.d.ts +12 -0
  503. package/dist/src/utils/test-path-fence.js +31 -0
  504. package/dist/src/utils/test-path-fence.js.map +1 -0
  505. package/dist/vitest.config.d.ts +2 -0
  506. package/dist/vitest.config.js +16 -0
  507. package/dist/vitest.config.js.map +1 -0
  508. package/package.json +64 -0
@@ -0,0 +1,3626 @@
1
+ import path from 'node:path';
2
+ import fs from 'fs-extra';
3
+ import { getCodexOperationRequirement, probeAgentVersion } from '../utils/agent-version.js';
4
+ import { previewCodexManager, resolveCodexOperationConsumer, } from '../utils/codex-manager.js';
5
+ import { createNoopDebugLogger } from '../output/debug-log.js';
6
+ import { createNoopReporter } from '../output/reporter.js';
7
+ import { getDefaultNativeCliRunner, } from '../utils/native-cli.js';
8
+ import { readPluginManifest } from '../utils/manifest.js';
9
+ import { resolveAwHome, resolveCodexPaths } from '../utils/paths.js';
10
+ import { resolveCliPackageRoot } from '../utils/paths.js';
11
+ import { supportsPlatform } from '../utils/platform-support.js';
12
+ import { analyzeCodexSurface } from '../utils/codex-surface-analysis.js';
13
+ import { buildGeneratedName } from '../generators/shared.js';
14
+ import { createCodexReconcilePlan, } from '../utils/codex-reconciler.js';
15
+ import { readCodexConfigFile } from '../utils/codex-config.js';
16
+ import { executeRebuildOperation, } from './rebuild-operations.js';
17
+ import { assertNoClaudeUnboundNativeNameCollision, CodexActivationPostcheckError, createRebuildIntegration, executeCodexReconcileActivations, inspectCodexPluginRegistrationLive, inspectCodexSharedNativeLive, inspectNativeMarketplaceRegistrations, } from './rebuild-integration.js';
18
+ import { getNamespacedMarketplaceName } from '../utils/local-marketplace.js';
19
+ import { formatPluginId, parsePluginId, planPluginSelectorSourceIds, preflightDesiredArtifactCollisions, withResolvedMultiSourceCatalog, } from '../utils/multi-source-catalog.js';
20
+ import { calculateCodexNativeRenderPayloadDigest, calculateCodexSharedNativePayloadDigest, } from '../utils/catalog-digests.js';
21
+ import { resolveCatalogOperationContext, } from '../utils/catalog-operation-context.js';
22
+ import { assertStrictSemVer } from '../utils/manifest.js';
23
+ import { assertCatalogSourceInventoryComplete, canonicalMarketplaceRegistrationNames, inventoryCatalogSourceReferences, } from '../utils/catalog-source-references.js';
24
+ import { sanitizeCatalogSourceErrorMessage } from '../utils/catalog-public-error.js';
25
+ import { resolveEffectivePluginConfig } from '../utils/plugin-config.js';
26
+ import { resolveArtifactRenderConfig } from '../utils/artifact-renderer.js';
27
+ import { inspectOwnedComponents, parseOwnedTextHeader, readSkillOwnerMarker, SKILL_OWNER_MARKER, } from '../utils/ownership.js';
28
+ import { resolveActiveCatalogPluginShallow } from '../utils/catalog-generation-resolver.js';
29
+ import { canonicalizePathThroughExistingAncestorSync, codexProjectConfigAliasesUserHome, getClaudeHome, getCodexHome, resolveCanonicalRepoProjectKey, resolveRepoRootSync, } from '../utils/state-paths.js';
30
+ import { resolvePluginMetaDependencyClosure } from '../utils/plugin-dependencies.js';
31
+ import { readCatalogSourcesConfigSnapshot } from '../utils/catalog-sources.js';
32
+ import { catalogGenerationErrorCode, isOfflineCleanupCatalogGenerationError, } from '../utils/catalog-generation-error.js';
33
+ import { canonicalizePluginIntentState, calculatePluginIntentDigest, createEmptyPluginIntentState, mergePluginIntentRootPlatforms, readPluginIntentState, removePluginIntentRootPlatforms, writePluginIntentState, } from '../utils/plugin-intent-state.js';
34
+ import { assertOperationPlanFingerprint, createOperationPlanConfigDigest, createOperationPlan, } from './operation-plan.js';
35
+ import { validateCatalogSnapshotPluginEntriesComplete } from '../utils/catalog-snapshot.js';
36
+ import { runPostApplyScript } from '../utils/plugin-scripts.js';
37
+ import { AwCapabilityUnsupportedError, IMPLEMENTED_AW_CAPABILITIES, } from './aw-capabilities.js';
38
+ import { checkAwCapabilitySupport } from '../utils/manifest.js';
39
+ import { createDependencyPlacementPlan, } from './dependency-operation-plan.js';
40
+ import { createReconcileOperationPlan, } from './reconcile-operation-plan.js';
41
+ import { forceOfflineCleanupEvidence, offlineCleanupEvidenceFromInventory, offlineCleanupEvidenceMatches, } from './offline-cleanup-evidence.js';
42
+ async function measureCommandPerformanceStage(runtime, name, operation) {
43
+ const startedAt = Date.now();
44
+ let phase = 'success';
45
+ try {
46
+ return await operation();
47
+ }
48
+ catch (error) {
49
+ phase = 'failure';
50
+ throw error;
51
+ }
52
+ finally {
53
+ await Promise.resolve().then(() => runtime?.debugLogger?.log({
54
+ type: 'performance:stage',
55
+ level: 'debug',
56
+ target: 'debugLog',
57
+ message: 'Command performance stage completed.',
58
+ data: {
59
+ name,
60
+ phase,
61
+ durationMs: Math.max(0, Date.now() - startedAt),
62
+ },
63
+ })).catch(() => undefined);
64
+ }
65
+ }
66
+ let rebuildCommandTestHooks;
67
+ async function emitRebuildActionEvent(runtime, event) {
68
+ await Promise.allSettled([
69
+ Promise.resolve().then(() => runtime.reporter.event(event)),
70
+ Promise.resolve().then(() => runtime.debugLogger.log(event)),
71
+ ]);
72
+ }
73
+ export function setRebuildCommandTestHooksForTests(hooks) {
74
+ rebuildCommandTestHooks = hooks;
75
+ }
76
+ function assertRebuildCatalogSourceInventoryComplete(inventory, targetScope) {
77
+ if (targetScope !== 'user') {
78
+ assertCatalogSourceInventoryComplete(inventory);
79
+ return;
80
+ }
81
+ assertCatalogSourceInventoryComplete({
82
+ ...inventory,
83
+ errors: inventory.errors.filter((error) => error.relevance.global
84
+ || error.relevance.scope !== 'project'
85
+ || !error.kind.startsWith('config')),
86
+ });
87
+ }
88
+ const MALFORMED_CONFIG_PRESERVED_WARNING = 'pluginConfigPreserved: 目标插件的无效配置文件已保留。可稍后运行 aw config reset 清理。';
89
+ function omitSelectedPluginConfigErrors(inventory, targets) {
90
+ let omitted = false;
91
+ const errors = inventory.errors.filter((error) => {
92
+ const relevance = error.relevance;
93
+ const selected = error.kind.startsWith('config') && targets.some((target) => {
94
+ if (!relevance.global
95
+ && relevance.sourceId === target.sourceId
96
+ && relevance.pluginName === target.pluginName)
97
+ return true;
98
+ const location = error.location.replaceAll('\\', '/');
99
+ return location.endsWith(`/plugin-config/${target.sourceId}/${target.pluginName}/config.json`);
100
+ });
101
+ if (selected)
102
+ omitted = true;
103
+ return !selected;
104
+ });
105
+ return { inventory: { ...inventory, errors }, omitted };
106
+ }
107
+ function selectedPlatforms(selection) {
108
+ return selection === 'both' ? ['codex', 'claude'] : [selection];
109
+ }
110
+ function selectCodexActivationPlans(plans, pluginIds) {
111
+ return plans.flatMap((plan) => {
112
+ if (!plan.desiredTargets.some((target) => pluginIds.has(target.pluginId)))
113
+ return [];
114
+ const sharedNativeActions = plan.sharedNativeActions.filter((action) => pluginIds.has(action.pluginId));
115
+ const userActions = plan.activationActions.user.filter((action) => pluginIds.has(action.pluginId));
116
+ const projectActions = plan.activationActions.project.filter((action) => pluginIds.has(action.pluginId));
117
+ const postcheckPlugins = plan.postcheck.plugins.filter((plugin) => pluginIds.has(plugin.pluginId));
118
+ const codexPluginIds = new Set([
119
+ ...sharedNativeActions.map((action) => action.codexPluginId),
120
+ ...userActions.map((action) => action.codexPluginId),
121
+ ...projectActions.map((action) => action.codexPluginId),
122
+ ...postcheckPlugins.map((plugin) => plugin.codexPluginId),
123
+ ]);
124
+ return [{
125
+ ...plan,
126
+ activationActions: { user: userActions, project: projectActions },
127
+ staleActivationRemovals: plan.staleActivationRemovals.filter((action) => codexPluginIds.has(action.codexPluginId)),
128
+ postcheck: { ...plan.postcheck, plugins: postcheckPlugins },
129
+ }];
130
+ });
131
+ }
132
+ function failedCodexActivationPluginIds(options) {
133
+ const error = options.error;
134
+ if (!(error instanceof CodexActivationPostcheckError)) {
135
+ return new Set(options.successfulPluginIds);
136
+ }
137
+ const ownersByCodexPluginId = new Map();
138
+ for (const entry of options.plans.flatMap((plan) => [
139
+ ...plan.activationActions.user,
140
+ ...plan.activationActions.project,
141
+ ...plan.postcheck.plugins,
142
+ ])) {
143
+ const owners = ownersByCodexPluginId.get(entry.codexPluginId) ?? new Set();
144
+ owners.add(entry.pluginId);
145
+ ownersByCodexPluginId.set(entry.codexPluginId, owners);
146
+ }
147
+ const failed = new Set();
148
+ for (const codexPluginId of error.codexPluginIds) {
149
+ const owners = ownersByCodexPluginId.get(codexPluginId);
150
+ if (!owners || owners.size === 0)
151
+ return new Set(options.successfulPluginIds);
152
+ for (const pluginId of owners) {
153
+ if (options.successfulPluginIds.has(pluginId))
154
+ failed.add(pluginId);
155
+ }
156
+ }
157
+ return failed.size > 0 ? failed : new Set(options.successfulPluginIds);
158
+ }
159
+ function updateIntentOverrides(options) {
160
+ const plannedKeys = new Set(options.plan.targets.map((target) => `${target.pluginId}\0${target.platform}`));
161
+ return canonicalizePluginIntentState({
162
+ ...options.state,
163
+ dependencyScopeOverrides: [
164
+ ...options.state.dependencyScopeOverrides.filter((override) => !plannedKeys.has(`${override.pluginId}\0${override.platform}`)),
165
+ ...options.plan.scopeOverrides,
166
+ ],
167
+ });
168
+ }
169
+ function effectiveDependencyScopeOverrides(options) {
170
+ const nodeKeys = new Set(options.nodes.map((node) => `${node.pluginId}\0${node.platform}`));
171
+ const explicitMatches = (pluginId, platform) => options.explicit.some((override) => override.pluginId === pluginId
172
+ && (override.platform === undefined || override.platform === platform));
173
+ return [
174
+ ...options.state.dependencyScopeOverrides.filter((override) => nodeKeys.has(`${override.pluginId}\0${override.platform}`)
175
+ && !options.ignoredPersistedRootIds?.has(override.pluginId)
176
+ && !explicitMatches(override.pluginId, override.platform)),
177
+ ...options.explicit.filter((override) => options.nodes.some((node) => node.pluginId === override.pluginId
178
+ && (override.platform === undefined || node.platform === override.platform))),
179
+ ];
180
+ }
181
+ function pruneUnreachableDependencyScopeOverrides(state, catalogEntries) {
182
+ if (!catalogEntries || state.dependencyScopeOverrides.length === 0)
183
+ return state;
184
+ const byId = new Map(catalogEntries.map((entry) => [entry.pluginId, entry]));
185
+ const reachable = new Set();
186
+ const visiting = new Set();
187
+ const visited = new Set();
188
+ const visit = (pluginId, platform, root) => {
189
+ const entry = byId.get(pluginId);
190
+ if (!entry || visiting.has(pluginId))
191
+ return false;
192
+ const visitKey = `${pluginId}\0${platform}`;
193
+ if (visited.has(visitKey)) {
194
+ if (!root)
195
+ reachable.add(visitKey);
196
+ return true;
197
+ }
198
+ visiting.add(pluginId);
199
+ for (const dependencyPluginId of entry.dependencyPluginIds) {
200
+ if (!visit(dependencyPluginId, platform, false))
201
+ return false;
202
+ }
203
+ visiting.delete(pluginId);
204
+ visited.add(visitKey);
205
+ if (!root)
206
+ reachable.add(visitKey);
207
+ return true;
208
+ };
209
+ for (const root of state.roots) {
210
+ for (const platform of root.platforms) {
211
+ visiting.clear();
212
+ if (!visit(root.pluginId, platform, true))
213
+ return state;
214
+ }
215
+ }
216
+ return canonicalizePluginIntentState({
217
+ ...state,
218
+ dependencyScopeOverrides: state.dependencyScopeOverrides.filter((override) => reachable.has(`${override.pluginId}\0${override.platform}`)),
219
+ });
220
+ }
221
+ function deriveDesiredIntentState(options) {
222
+ let next = options.state;
223
+ if (options.command === 'install') {
224
+ for (const root of options.selectedRoots)
225
+ next = mergePluginIntentRootPlatforms(next, root);
226
+ next = updateIntentOverrides({ state: next, plan: options.plan });
227
+ }
228
+ else if (options.command === 'uninstall') {
229
+ for (const root of options.selectedRoots)
230
+ next = removePluginIntentRootPlatforms(next, root);
231
+ next = pruneUnreachableDependencyScopeOverrides(next, options.catalogEntries);
232
+ }
233
+ return next;
234
+ }
235
+ function removeSelectedRootPlatformsFromOtherScope(options) {
236
+ let next = options.state;
237
+ for (const root of options.selectedRoots) {
238
+ const platforms = root.platforms.filter((platform) => platform !== 'codex' || !options.preserveCodexRootIds.has(root.pluginId));
239
+ if (platforms.length > 0) {
240
+ next = removePluginIntentRootPlatforms(next, { ...root, platforms });
241
+ }
242
+ }
243
+ return next;
244
+ }
245
+ async function writeDesiredIntentSuperset(options) {
246
+ const location = { scope: options.scope, cwd: options.cwd, env: options.env };
247
+ let next = deriveDesiredIntentState({
248
+ command: options.command,
249
+ state: await readPluginIntentState(location),
250
+ selectedRoots: options.selectedRoots,
251
+ plan: options.plan,
252
+ catalogEntries: options.catalogEntries,
253
+ });
254
+ const superset = next;
255
+ await writePluginIntentState(location, superset);
256
+ const writes = [{ location, superset }];
257
+ if (options.command === 'install' || options.codexPlan) {
258
+ const otherScope = options.scope === 'user' ? 'project' : 'user';
259
+ const otherLocation = { scope: otherScope, cwd: options.cwd, env: options.env };
260
+ let other = await readPluginIntentState(otherLocation);
261
+ if (options.command === 'install') {
262
+ other = removeSelectedRootPlatformsFromOtherScope({
263
+ state: other,
264
+ selectedRoots: options.selectedRoots,
265
+ preserveCodexRootIds: options.preserveOtherCodexScopeRootIds,
266
+ });
267
+ }
268
+ const otherSuperset = other;
269
+ await writePluginIntentState(otherLocation, otherSuperset);
270
+ writes.push({
271
+ location: otherLocation,
272
+ superset: otherSuperset,
273
+ });
274
+ }
275
+ return writes;
276
+ }
277
+ function createSyntheticPlugin(pluginName, env) {
278
+ const rootDir = path.join(resolveAwHome(env), 'plugins', pluginName);
279
+ return {
280
+ rootDir,
281
+ manifestPath: path.join(rootDir, '.agents-plugin', 'plugin.json'),
282
+ manifest: {
283
+ schemaVersion: 3,
284
+ name: pluginName,
285
+ version: '0.0.0',
286
+ description: 'Cleanup-only target for a plugin missing from the current catalog.',
287
+ marketplace: {
288
+ category: 'cleanup',
289
+ strict: true,
290
+ policy: { installation: 'AVAILABLE', authentication: 'ON_INSTALL' },
291
+ },
292
+ },
293
+ };
294
+ }
295
+ function failedResult(command, identity, message, phase = 'preflight') {
296
+ return {
297
+ status: 'failed',
298
+ command,
299
+ target: identity,
300
+ actions: phase === 'preflight' ? [] : [{ name: phase, status: 'failed' }],
301
+ cleanupAttempted: false,
302
+ removed: [],
303
+ absent: [],
304
+ residual: [],
305
+ quarantined: [],
306
+ failures: [{ phase, message }],
307
+ error: { phase, message },
308
+ };
309
+ }
310
+ function toTargetResult(result) {
311
+ const errorPrefix = result.status === 'failed'
312
+ ? result.error.message.split(':', 1)[0]
313
+ : undefined;
314
+ return {
315
+ target: {
316
+ pluginName: result.target.pluginName,
317
+ ...(result.target.sourceId ? {
318
+ sourceId: result.target.sourceId,
319
+ sourceIdentity: result.target.sourceIdentity,
320
+ pluginId: result.target.pluginId,
321
+ } : {}),
322
+ platform: result.target.platform,
323
+ scope: result.target.scope,
324
+ },
325
+ action: result.command,
326
+ state: result.status === 'succeeded' ? 'completed' : 'failed',
327
+ ...(result.status === 'failed' ? {
328
+ message: result.error.message,
329
+ errorCode: errorPrefix && /^[A-Za-z][A-Za-z0-9]*$/u.test(errorPrefix)
330
+ ? errorPrefix
331
+ : 'writeFailed',
332
+ } : {}),
333
+ };
334
+ }
335
+ async function assertClaudeVersion(options) {
336
+ if (!options.platforms.includes('claude'))
337
+ return;
338
+ const failures = [];
339
+ const result = await probeAgentVersion('claude', {
340
+ cwd: options.cwd,
341
+ env: options.env,
342
+ runner: options.runtime.nativeCliRunner,
343
+ });
344
+ if (result.status !== 'supported')
345
+ failures.push(result);
346
+ if (failures.length > 0) {
347
+ throw new Error(failures.map((failure) => `${failure.command} 版本检查失败:${failure.details ?? failure.status};最低版本 ${failure.requiredMinVersion}。`)
348
+ .join(';'));
349
+ }
350
+ }
351
+ async function resolveRequestedNames(options) {
352
+ const result = new Map();
353
+ for (const platform of options.platforms) {
354
+ const names = options.pluginNames;
355
+ result.set(platform, [...new Set(names)]);
356
+ }
357
+ return result;
358
+ }
359
+ async function runSingleSourceRebuildCommand(options) {
360
+ const runtime = {
361
+ env: options.runtime?.env ?? process.env,
362
+ nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
363
+ reporter: options.runtime?.reporter ?? createNoopReporter(),
364
+ debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
365
+ ...(options.runtime?.codexManager ? { codexManager: options.runtime.codexManager } : {}),
366
+ expectedCatalogConfigDigest: options.runtime?.expectedCatalogConfigDigest,
367
+ codexUserActivationDesired: options.runtime?.codexUserActivationDesired,
368
+ };
369
+ const env = runtime.env;
370
+ const selected = selectedPlatforms(options.commandOptions.platform);
371
+ const catalog = options.runtime?.catalogResolution;
372
+ if (!catalog)
373
+ throw new Error('Internal error: a resolved catalog source is required.');
374
+ const requestedByPlatform = await resolveRequestedNames({
375
+ command: options.command,
376
+ pluginNames: options.pluginNames,
377
+ all: options.all,
378
+ platforms: selected,
379
+ scope: options.commandOptions.scope,
380
+ cwd: options.commandOptions.cwd,
381
+ env,
382
+ nativeCliRunner: runtime.nativeCliRunner,
383
+ });
384
+ const exactPluginNames = [...new Set([...requestedByPlatform.values()].flat())];
385
+ const catalogPlugins = options.runtime?.resolvedCatalogPlugins ?? (await Promise.all(exactPluginNames.map(async (pluginName) => {
386
+ const pluginRoot = path.join(catalog.rootDir, 'plugins', pluginName);
387
+ return await fs.pathExists(pluginRoot)
388
+ ? readPluginManifest(pluginRoot, { mode: 'bundle' })
389
+ : null;
390
+ }))).filter((plugin) => plugin !== null);
391
+ if (options.runtime?.namespacedSource) {
392
+ for (const plugin of catalogPlugins) {
393
+ plugin.sourceId = options.runtime.namespacedSource.sourceId;
394
+ plugin.sourceIdentity = options.runtime.namespacedSource.sourceIdentity;
395
+ plugin.pluginId = formatPluginId({
396
+ sourceId: options.runtime.namespacedSource.sourceId,
397
+ pluginName: plugin.manifest.name,
398
+ });
399
+ plugin.marketplaceName = options.runtime.namespacedSource.marketplaceName;
400
+ }
401
+ }
402
+ const catalogByName = new Map(catalogPlugins.map((plugin) => [plugin.manifest.name, plugin]));
403
+ if (options.commandOptions.platform === 'both'
404
+ && options.command !== 'cleanup'
405
+ && options.command !== 'uninstall') {
406
+ for (const platform of selected) {
407
+ requestedByPlatform.set(platform, (requestedByPlatform.get(platform) ?? []).filter((pluginName) => {
408
+ const plugin = catalogByName.get(pluginName);
409
+ return !plugin || supportsPlatform(plugin.manifest, platform);
410
+ }));
411
+ }
412
+ }
413
+ const platforms = selected.filter((platform) => (requestedByPlatform.get(platform)?.length ?? 0) > 0);
414
+ const integration = createRebuildIntegration({
415
+ context: runtime,
416
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
417
+ ...(options.execution?.prepared
418
+ ? { preparedTargets: [options.execution.prepared.target] }
419
+ : {}),
420
+ });
421
+ const targets = [];
422
+ const results = [];
423
+ const warnings = [];
424
+ const recordWarning = (error) => {
425
+ const message = error instanceof Error ? error.message : String(error);
426
+ if (!warnings.includes(message))
427
+ warnings.push(message);
428
+ };
429
+ for (const platform of platforms) {
430
+ for (const pluginName of requestedByPlatform.get(platform) ?? []) {
431
+ const currentPlugin = catalogByName.get(pluginName);
432
+ const identity = {
433
+ ...(options.runtime?.namespacedSource ?? {}),
434
+ ...(options.runtime?.namespacedSource ? {
435
+ pluginId: formatPluginId({
436
+ sourceId: options.runtime.namespacedSource.sourceId,
437
+ pluginName,
438
+ }),
439
+ } : currentPlugin?.pluginId ? { pluginId: currentPlugin.pluginId } : {}),
440
+ pluginName,
441
+ pluginVersion: currentPlugin?.manifest.version ?? '0.0.0',
442
+ platform,
443
+ scope: options.commandOptions.scope,
444
+ cwd: options.commandOptions.cwd,
445
+ };
446
+ if (!currentPlugin && options.command !== 'cleanup' && options.command !== 'uninstall') {
447
+ results.push(failedResult(options.command, identity, `${pluginName} 当前不在 current catalog 中。`));
448
+ continue;
449
+ }
450
+ const plugin = currentPlugin ?? createSyntheticPlugin(pluginName, env);
451
+ if (currentPlugin
452
+ && options.command !== 'cleanup'
453
+ && options.command !== 'uninstall'
454
+ && !supportsPlatform(currentPlugin.manifest, platform)) {
455
+ results.push(failedResult(options.command, identity, `${pluginName} 当前 catalog 不支持 ${platform}。`));
456
+ continue;
457
+ }
458
+ if (currentPlugin
459
+ && platform === 'codex'
460
+ && options.commandOptions.scope === 'project'
461
+ && options.command !== 'cleanup'
462
+ && options.command !== 'uninstall'
463
+ && codexProjectConfigAliasesUserHome(options.commandOptions.cwd, env)) {
464
+ results.push(failedResult(options.command, identity, 'codexScopePathAlias: Codex project config 与用户配置目录相同。请进入实际项目目录后重试。'));
465
+ continue;
466
+ }
467
+ let result;
468
+ try {
469
+ if (options.execution?.preparationError)
470
+ throw options.execution.preparationError;
471
+ const target = options.execution?.prepared?.target ?? await integration.buildTarget({
472
+ ...(options.runtime?.namespacedSource ?? {}),
473
+ ...(options.runtime?.namespacedSource ? {
474
+ pluginId: formatPluginId({
475
+ sourceId: options.runtime.namespacedSource.sourceId,
476
+ pluginName,
477
+ }),
478
+ } : plugin.pluginId ? { pluginId: plugin.pluginId } : {}),
479
+ plugin,
480
+ platform,
481
+ scope: options.commandOptions.scope,
482
+ cwd: options.commandOptions.cwd,
483
+ command: options.command,
484
+ catalog,
485
+ });
486
+ targets.push(target);
487
+ if (!options.execution?.targetHookCompleted) {
488
+ await rebuildCommandTestHooks?.afterTargetBuilt?.({
489
+ command: options.command,
490
+ identity,
491
+ });
492
+ }
493
+ if (!options.execution?.reviewCompleted) {
494
+ await options.runtime?.reviewPreflight?.({
495
+ command: options.command,
496
+ identity,
497
+ ...options.runtime.resolvedReviewEvidenceByPlugin?.[pluginName],
498
+ });
499
+ }
500
+ const driver = options.execution?.deferredPostApplyTasks
501
+ ? {
502
+ ...integration.driver,
503
+ runPostApply: async (invocation) => {
504
+ options.execution.deferredPostApplyTasks.push({
505
+ pluginId: identity.pluginId ?? identity.pluginName,
506
+ run: async () => {
507
+ const startedAt = Date.now();
508
+ const emit = (data) => emitRebuildActionEvent(runtime, {
509
+ type: 'rebuild:action',
510
+ source: {
511
+ plugin: invocation.target.identity.pluginName,
512
+ platform: invocation.target.identity.platform,
513
+ scope: invocation.target.identity.scope,
514
+ action: options.command,
515
+ },
516
+ data,
517
+ });
518
+ await emit({ name: 'postApply', phase: 'start' });
519
+ try {
520
+ await integration.driver.runPostApply(invocation);
521
+ }
522
+ catch (error) {
523
+ await emit({
524
+ name: 'postApply',
525
+ phase: 'failure',
526
+ durationMs: Math.max(0, Date.now() - startedAt),
527
+ message: '运行 postApply 失败。',
528
+ });
529
+ throw error;
530
+ }
531
+ await emit({
532
+ name: 'postApply',
533
+ phase: 'success',
534
+ durationMs: Math.max(0, Date.now() - startedAt),
535
+ });
536
+ },
537
+ onFailure: (error) => {
538
+ recordWarning(error);
539
+ const action = result.actions.find((entry) => entry.name === 'postApply');
540
+ if (action)
541
+ action.status = 'failed';
542
+ },
543
+ });
544
+ },
545
+ }
546
+ : {
547
+ ...integration.driver,
548
+ runPostApply: async (invocation) => {
549
+ try {
550
+ await integration.driver.runPostApply(invocation);
551
+ }
552
+ catch (error) {
553
+ recordWarning(error);
554
+ throw error;
555
+ }
556
+ },
557
+ };
558
+ result = await executeRebuildOperation({
559
+ command: options.command,
560
+ target,
561
+ driver,
562
+ onActionEvent: (event) => options.execution?.deferredPostApplyTasks
563
+ && event.data.name === 'postApply'
564
+ ? undefined
565
+ : emitRebuildActionEvent(runtime, event),
566
+ });
567
+ }
568
+ catch (error) {
569
+ result = error.result ?? failedResult(options.command, identity, error instanceof Error ? error.message : String(error), 'preflight');
570
+ }
571
+ results.push(result);
572
+ }
573
+ }
574
+ const successful = results.filter((result) => result.status === 'succeeded');
575
+ return {
576
+ platforms,
577
+ plugins: [...new Set(successful.map((result) => result.target.pluginName))],
578
+ warnings: [...new Set(warnings)],
579
+ rebuildResults: results,
580
+ targetResults: results.map(toTargetResult),
581
+ overallState: successful.length === results.length
582
+ ? 'succeeded'
583
+ : successful.length > 0 ? 'partial' : 'failed',
584
+ receiptTargets: results.map((result) => ({
585
+ pluginName: result.target.pluginName,
586
+ ...(result.target.sourceId ? {
587
+ sourceId: result.target.sourceId,
588
+ pluginId: result.target.pluginId,
589
+ } : {}),
590
+ platform: result.target.platform,
591
+ scope: result.target.scope,
592
+ })),
593
+ };
594
+ }
595
+ function resolveRuntimeCliVersion() {
596
+ const value = fs.readJsonSync(path.join(resolveCliPackageRoot(), 'package.json'));
597
+ return assertStrictSemVer(value.version, 'packages/cli/package.json version');
598
+ }
599
+ function toCatalogResolution(source) {
600
+ return {
601
+ rootDir: source.rootDir,
602
+ source: 'local',
603
+ catalogVersion: source.manifest.catalogVersion,
604
+ channel: source.manifest.channel,
605
+ };
606
+ }
607
+ function mergeRebuildCommandResults(results) {
608
+ const rebuildResults = results.flatMap((result) => result.rebuildResults);
609
+ const platforms = [...new Set(results.flatMap((result) => result.platforms))];
610
+ return {
611
+ platforms,
612
+ plugins: [...new Set(results.flatMap((result) => result.plugins))],
613
+ warnings: [...new Set(results.flatMap((result) => result.warnings))],
614
+ rebuildResults,
615
+ targetResults: results.flatMap((result) => result.targetResults),
616
+ overallState: rebuildResults.every((result) => result.status === 'succeeded')
617
+ ? 'succeeded'
618
+ : rebuildResults.some((result) => result.status === 'succeeded') ? 'partial' : 'failed',
619
+ receiptTargets: results.flatMap((result) => result.receiptTargets),
620
+ ...(results.find((result) => result.dependencyPlan)?.dependencyPlan
621
+ ? { dependencyPlan: results.find((result) => result.dependencyPlan).dependencyPlan }
622
+ : {}),
623
+ ...(results.find((result) => result.codexManagerPreview)?.codexManagerPreview
624
+ ? { codexManagerPreview: results.find((result) => result.codexManagerPreview).codexManagerPreview }
625
+ : {}),
626
+ ...(results.find((result) => result.operationPlan)?.operationPlan
627
+ ? { operationPlan: results.find((result) => result.operationPlan).operationPlan }
628
+ : {}),
629
+ ...(results.some((result) => result.offlineCleanupPlans !== undefined)
630
+ ? { offlineCleanupPlans: results.flatMap((result) => result.offlineCleanupPlans ?? []) }
631
+ : {}),
632
+ };
633
+ }
634
+ function reconcileOverallState(targetResults, plan) {
635
+ const failed = targetResults.some((result) => result.state === 'failed' || result.action === 'skipped');
636
+ if (!failed)
637
+ return 'succeeded';
638
+ const requested = new Set(plan.targets
639
+ .filter((target) => target.relation === 'requested')
640
+ .map((target) => `${target.pluginId}\0${target.platform}`));
641
+ const completedRequested = targetResults.some((result) => result.state === 'completed'
642
+ && result.action !== 'skipped'
643
+ && result.target.pluginId !== undefined
644
+ && requested.has(`${result.target.pluginId}\0${result.target.platform}`));
645
+ return completedRequested ? 'partial' : 'failed';
646
+ }
647
+ function createRebuildOperationPlan(options) {
648
+ const selectedRoots = [...new Map(options.dependencyPlan.targets.flatMap((target) => target.relation === 'requested'
649
+ ? [[target.pluginId, {
650
+ pluginId: target.pluginId,
651
+ platforms: options.dependencyPlan.targets.flatMap((candidate) => candidate.relation === 'requested' && candidate.pluginId === target.pluginId
652
+ ? [candidate.platform]
653
+ : []),
654
+ }]]
655
+ : [])).values()];
656
+ const intentBefore = options.intentBeforeByScope[options.scope];
657
+ const intentAfter = deriveDesiredIntentState({
658
+ command: options.command,
659
+ state: intentBefore,
660
+ selectedRoots,
661
+ plan: options.dependencyPlan,
662
+ catalogEntries: options.catalogEntries,
663
+ });
664
+ const beforeDigest = calculatePluginIntentDigest(intentBefore);
665
+ const afterDigest = calculatePluginIntentDigest(intentAfter);
666
+ const actions = [
667
+ ...(options.targetActions ?? []),
668
+ ...options.codexPlans.flatMap((plan) => [
669
+ ...plan.sharedNativeActions.flatMap((action) => action.action === 'install' || action.action === 'replace' || action.action === 'repair'
670
+ ? [{
671
+ kind: 'ensureNativeRegistration',
672
+ pluginId: action.pluginId,
673
+ platform: 'codex',
674
+ scope: options.dependencyPlan.targets.find((target) => target.pluginId === action.pluginId && target.platform === 'codex')?.resolvedScope ?? 'user',
675
+ registration: action.codexPluginId,
676
+ }]
677
+ : []),
678
+ ...['user', 'project'].flatMap((scope) => plan.activationActions[scope].flatMap((action) => action.action === 'none'
679
+ ? []
680
+ : [{
681
+ kind: 'setActivation',
682
+ pluginId: action.pluginId,
683
+ platform: 'codex',
684
+ scope,
685
+ enabled: action.desired,
686
+ }])),
687
+ ...plan.staleActivationRemovals.map((action) => ({
688
+ kind: 'setActivation',
689
+ pluginId: action.codexPluginId,
690
+ platform: 'codex',
691
+ scope: action.scope,
692
+ enabled: false,
693
+ })),
694
+ ]),
695
+ ];
696
+ return createOperationPlan({
697
+ intentChanges: beforeDigest === afterDigest
698
+ ? []
699
+ : [{ scope: options.scope, beforeDigest, afterDigest }],
700
+ actions,
701
+ changes: options.dependencyPlan.targets.map((target) => [
702
+ target.pluginId,
703
+ target.platform,
704
+ target.resolvedScope,
705
+ target.action,
706
+ ].join(':')),
707
+ warnings: options.codexPlans.flatMap((plan) => plan.warnings),
708
+ evidence: {
709
+ catalogDigests: options.catalogSources.map((source) => ({
710
+ sourceId: source.sourceId,
711
+ digest: source.manifestDigest,
712
+ })),
713
+ intentDigests: ['user', 'project'].map((scope) => ({
714
+ scope,
715
+ digest: calculatePluginIntentDigest(options.intentBeforeByScope[scope]),
716
+ })),
717
+ configDigests: options.configDigests,
718
+ targetOwnerFingerprints: options.dependencyPlan.targets.map((target) => ({
719
+ pluginId: target.pluginId,
720
+ platform: target.platform,
721
+ scope: target.resolvedScope,
722
+ fingerprint: JSON.stringify({ state: target.state, errorCode: target.errorCode ?? null }),
723
+ })),
724
+ },
725
+ });
726
+ }
727
+ function leaseSafeRebuildResult(result) {
728
+ const projectPath = (value) => path.basename(value);
729
+ return {
730
+ ...result,
731
+ rebuildResults: result.rebuildResults.map((entry) => ({
732
+ ...entry,
733
+ target: { ...entry.target, cwd: '.' },
734
+ removed: entry.removed.map(projectPath),
735
+ absent: entry.absent.map(projectPath),
736
+ residual: entry.residual.map(projectPath),
737
+ quarantined: entry.quarantined.map(projectPath),
738
+ })),
739
+ };
740
+ }
741
+ function namespacedPreflightError(message, targets, legacyTargets = []) {
742
+ return Object.assign(new Error(message), { namespacedTargets: targets, legacyTargets });
743
+ }
744
+ function selectedLegacyLiveReferences(references, platforms, scope) {
745
+ return references.filter((reference) => (reference.kind === 'legacyOwner' || reference.kind === 'legacyMarketplace')
746
+ && reference.pluginName
747
+ && reference.scope === scope
748
+ && reference.platform
749
+ && platforms.has(reference.platform));
750
+ }
751
+ function projectLegacyTargets(references) {
752
+ return [...new Map(references.map((reference) => {
753
+ const target = {
754
+ pluginName: reference.pluginName,
755
+ platform: reference.platform,
756
+ scope: reference.scope,
757
+ };
758
+ return [`${target.pluginName}\0${target.platform}\0${target.scope}`, target];
759
+ })).values()];
760
+ }
761
+ function resolveLiveCleanupTargets(options) {
762
+ const visible = options.references.filter((reference) => (reference.kind === 'namespacedOwner'
763
+ || reference.kind === 'namespacedMarketplace')
764
+ && reference.sourceId
765
+ && reference.pluginName
766
+ && reference.platform
767
+ && options.platforms.includes(reference.platform)
768
+ && reference.scope === options.scope);
769
+ const targets = [];
770
+ const selectors = options.selectors.map((value) => {
771
+ const parsed = parsePluginId(value);
772
+ const matchesByTarget = new Map();
773
+ for (const reference of visible.filter((entry) => entry.pluginName === parsed.pluginName
774
+ && (parsed.sourceId === undefined || entry.sourceId === parsed.sourceId))) {
775
+ const stableSourceIdentity = reference.sourceIdentity
776
+ ?? options.references.find((entry) => entry.kind === 'namespacedMarketplace'
777
+ && entry.sourceId === reference.sourceId
778
+ && entry.sourceIdentity)?.sourceIdentity
779
+ ?? 'unavailable';
780
+ const liveNames = new Set(options.references.flatMap((candidate) => candidate.sourceId === reference.sourceId
781
+ && candidate.pluginName === reference.pluginName
782
+ && candidate.platform === reference.platform
783
+ && candidate.scope === reference.scope
784
+ && candidate.marketplaceName
785
+ ? [candidate.marketplaceName]
786
+ : []));
787
+ const registeredNames = canonicalMarketplaceRegistrationNames(options.references, reference.sourceId);
788
+ const unboundNativeNames = new Set(options.references.flatMap((candidate) => candidate.kind === 'namespacedMarketplace'
789
+ && candidate.sourceId === undefined
790
+ && candidate.pluginName === reference.pluginName
791
+ && candidate.platform === reference.platform
792
+ && candidate.scope === reference.scope
793
+ && candidate.marketplaceName
794
+ ? [candidate.marketplaceName]
795
+ : []));
796
+ const marketplaceName = liveNames.size === 1
797
+ ? [...liveNames][0]
798
+ : liveNames.size === 0 && registeredNames.length === 1
799
+ ? registeredNames[0]
800
+ : undefined;
801
+ const marketplaceIdentityUnresolved = liveNames.size > 1
802
+ || (liveNames.size === 0 && registeredNames.length > 1)
803
+ || (liveNames.size === 0 && registeredNames.length === 0 && unboundNativeNames.size > 0);
804
+ const target = {
805
+ pluginId: formatPluginId({ sourceId: reference.sourceId, pluginName: reference.pluginName }),
806
+ pluginName: reference.pluginName,
807
+ sourceId: reference.sourceId,
808
+ sourceIdentity: stableSourceIdentity,
809
+ platform: reference.platform,
810
+ scope: reference.scope,
811
+ ...(marketplaceName ? { marketplaceName } : {}),
812
+ ...((liveNames.size === 1
813
+ || (marketplaceName !== undefined && unboundNativeNames.has(marketplaceName)))
814
+ ? { nativeRegistration: true }
815
+ : {}),
816
+ ...(marketplaceIdentityUnresolved ? { marketplaceIdentityUnresolved: true } : {}),
817
+ };
818
+ const key = [target.pluginId, target.sourceIdentity, target.platform, target.scope].join('\0');
819
+ const previous = matchesByTarget.get(key);
820
+ matchesByTarget.set(key, previous?.marketplaceName ? previous : target);
821
+ }
822
+ const matches = [...matchesByTarget.values()];
823
+ const identities = [...new Set(matches.map((target) => target.pluginId))];
824
+ if (parsed.sourceId === undefined && identities.length > 1) {
825
+ throw new Error(`pluginSelectorAmbiguous: Plugin "${parsed.pluginName}" has multiple live matches: ${identities.sort().join(', ')}.`);
826
+ }
827
+ targets.push(...matches);
828
+ return identities.length === 1 ? identities[0] : value;
829
+ });
830
+ return {
831
+ selectors,
832
+ targets: [...new Map(targets.map((target) => [[
833
+ target.pluginId, target.sourceIdentity, target.platform, target.scope,
834
+ ].join('\0'), target])).values()],
835
+ };
836
+ }
837
+ async function cleanupOfflineOwnedComponents(options) {
838
+ const results = [];
839
+ const warnings = [];
840
+ const nativeCliRunner = options.nativeCliRunner ?? getDefaultNativeCliRunner();
841
+ const codexManager = options.codexManager;
842
+ const context = {
843
+ env: options.env,
844
+ nativeCliRunner,
845
+ ...(codexManager ? { codexManager } : {}),
846
+ reporter: createNoopReporter(),
847
+ debugLogger: createNoopDebugLogger(),
848
+ };
849
+ const integration = createRebuildIntegration({ context });
850
+ for (const target of options.targets) {
851
+ const inventoryErrors = (options.inventoryErrors ?? []).filter((error) => error.relevance.global || ((error.relevance.sourceId === undefined || error.relevance.sourceId === target.sourceId)
852
+ && (error.relevance.pluginName === undefined || error.relevance.pluginName === target.pluginName)
853
+ && (error.relevance.platform === undefined || error.relevance.platform === target.platform)
854
+ && (error.relevance.scope === undefined || error.relevance.scope === target.scope)));
855
+ const evidence = options.evidence?.find((candidate) => candidate.pluginId === target.pluginId
856
+ && candidate.sourceIdentity === target.sourceIdentity
857
+ && candidate.platform === target.platform
858
+ && candidate.scope === target.scope);
859
+ const activationAction = target.platform === 'codex'
860
+ ? evidence?.plan.actions.find((action) => action.action === 'removeNativeRegistration'
861
+ && action.activationScope === target.scope)
862
+ : undefined;
863
+ let activationDisposition = 'remove';
864
+ if (activationAction && target.scope === 'user') {
865
+ activationDisposition = 'disable';
866
+ }
867
+ const plugin = createSyntheticPlugin(target.pluginName, options.env);
868
+ plugin.sourceId = target.sourceId;
869
+ plugin.sourceIdentity = target.sourceIdentity;
870
+ plugin.pluginId = target.pluginId;
871
+ const marketplaceName = target.marketplaceName ?? 'aw';
872
+ plugin.marketplaceName = marketplaceName;
873
+ const desired = await integration.buildTarget({
874
+ sourceId: target.sourceId,
875
+ sourceIdentity: target.sourceIdentity,
876
+ pluginId: target.pluginId,
877
+ marketplaceName,
878
+ marketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces', target.sourceId),
879
+ plugin,
880
+ platform: target.platform,
881
+ scope: target.scope,
882
+ cwd: options.cwd,
883
+ command: 'cleanup',
884
+ catalog: {
885
+ rootDir: resolveAwHome(options.env),
886
+ source: 'local',
887
+ catalogVersion: '0.0.0',
888
+ channel: 'beta',
889
+ },
890
+ includeNativeCleanupCandidate: target.nativeRegistration === true,
891
+ preserveCodexSharedNativePayload: target.platform === 'codex',
892
+ ...(activationAction
893
+ ? {
894
+ codexActivationCleanup: [{
895
+ codexPluginId: activationAction.target,
896
+ scope: activationAction.activationScope,
897
+ disposition: activationDisposition,
898
+ }],
899
+ }
900
+ : {}),
901
+ });
902
+ const result = await executeRebuildOperation({
903
+ command: 'cleanup',
904
+ target: desired,
905
+ driver: integration.driver,
906
+ });
907
+ if (target.marketplaceIdentityUnresolved) {
908
+ result.residual.push('nativeMarketplaceIdentityUnresolved');
909
+ warnings.push(`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`);
910
+ }
911
+ let finalResult = result;
912
+ if (inventoryErrors.length > 0) {
913
+ const message = 'stateInventoryResidual: Some weak state could not be verified. Retry cleanup, use cleanup --force only for exclusively owned damaged output, or run reinstall/sync to restore the target.';
914
+ result.residual = [...new Set([
915
+ ...result.residual,
916
+ ...inventoryErrors.map((error) => error.location),
917
+ ])];
918
+ warnings.push(...inventoryErrors.map((error) => `stateInventoryResidual: ${error.kind}: ${error.message}`));
919
+ if (result.status === 'succeeded') {
920
+ finalResult = {
921
+ ...result,
922
+ status: 'failed',
923
+ failures: [...result.failures, { phase: 'cleanup.owned', message }],
924
+ error: { phase: 'cleanup.owned', message },
925
+ };
926
+ }
927
+ }
928
+ results.push(finalResult);
929
+ }
930
+ const obsoleteContracts = path.join(resolveAwHome(options.env), 'installation-contracts');
931
+ try {
932
+ await (options.removeObsoleteInstallationContracts ?? fs.remove)(obsoleteContracts);
933
+ }
934
+ catch (error) {
935
+ warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
936
+ }
937
+ return leaseSafeRebuildResult({
938
+ platforms: [...new Set(results.map((result) => result.target.platform))],
939
+ plugins: results.filter((result) => result.status === 'succeeded').map((result) => result.target.pluginName),
940
+ warnings: [...new Set(warnings)],
941
+ rebuildResults: results,
942
+ targetResults: results.map(toTargetResult),
943
+ overallState: results.every((result) => result.status === 'succeeded') ? 'succeeded' : 'failed',
944
+ receiptTargets: results.map((result) => ({
945
+ pluginName: result.target.pluginName,
946
+ pluginId: result.target.pluginId,
947
+ sourceId: result.target.sourceId,
948
+ platform: result.target.platform,
949
+ scope: result.target.scope,
950
+ })),
951
+ ...(options.evidence ? { offlineCleanupPlans: options.evidence } : {}),
952
+ });
953
+ }
954
+ async function previewOfflineOwnedComponents(options) {
955
+ await options.onPreviewStage?.('inventory');
956
+ const preview = mergeRebuildCommandResults([]);
957
+ preview.warnings.push(...options.targets.flatMap((target) => target.marketplaceIdentityUnresolved
958
+ ? [`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`]
959
+ : []));
960
+ if (options.evidence) {
961
+ preview.offlineCleanupPlans = options.evidence;
962
+ preview.operationPlan = createOfflineCleanupOperationPlan(options.evidence);
963
+ }
964
+ await options.onPreviewStage?.('dependencyPlan');
965
+ return preview;
966
+ }
967
+ function createOfflineCleanupOperationPlan(evidence) {
968
+ return createOperationPlan({
969
+ intentChanges: [],
970
+ actions: evidence.flatMap((target) => target.plan.actions.map((action) => action.action === 'removeNativeRegistration'
971
+ ? {
972
+ kind: 'removeNativeRegistration',
973
+ pluginId: target.pluginId,
974
+ platform: target.platform,
975
+ scope: target.scope,
976
+ registration: action.target,
977
+ }
978
+ : {
979
+ kind: 'removeOwnedArtifact',
980
+ pluginId: target.pluginId,
981
+ platform: target.platform,
982
+ scope: target.scope,
983
+ path: action.target,
984
+ ownerFingerprint: target.targetFingerprint,
985
+ })),
986
+ changes: evidence.flatMap((target) => target.plan.actions.map((action) => `${target.pluginId}:${action.action}:${action.target}`)),
987
+ warnings: [],
988
+ evidence: {
989
+ catalogDigests: evidence.map((target) => ({
990
+ sourceId: target.sourceId,
991
+ digest: target.sourceIdentity,
992
+ })),
993
+ intentDigests: [],
994
+ configDigests: [],
995
+ targetOwnerFingerprints: evidence.map((target) => ({
996
+ pluginId: target.pluginId,
997
+ platform: target.platform,
998
+ scope: target.scope,
999
+ fingerprint: target.targetFingerprint,
1000
+ })),
1001
+ },
1002
+ });
1003
+ }
1004
+ async function buildForceExclusiveCleanupPlan(options) {
1005
+ const targets = [];
1006
+ const evidence = [];
1007
+ for (const selector of options.selectors) {
1008
+ const parsed = parsePluginId(selector);
1009
+ const sourceId = parsed.sourceId ?? options.primarySourceId;
1010
+ if (!sourceId) {
1011
+ throw namespacedPreflightError('forceCleanupPathUnavailable: The selected plugin has no local Active source identity. Restore the local Active bundle, then retry cleanup --force, or inspect and remove the exact output manually.', []);
1012
+ }
1013
+ let active;
1014
+ let plugin;
1015
+ try {
1016
+ active = await resolveActiveCatalogPluginShallow({
1017
+ sourceId,
1018
+ pluginName: parsed.pluginName,
1019
+ env: options.env,
1020
+ });
1021
+ plugin = active.plugin;
1022
+ }
1023
+ catch {
1024
+ const unavailableTarget = options.platforms.map((platform) => ({
1025
+ pluginId: formatPluginId({ sourceId, pluginName: parsed.pluginName }),
1026
+ pluginName: parsed.pluginName,
1027
+ sourceId,
1028
+ sourceIdentity: 'unavailable',
1029
+ platform,
1030
+ scope: options.scope,
1031
+ }));
1032
+ throw namespacedPreflightError('forceCleanupPathUnavailable: The exact exclusive output path is unavailable from live evidence and the local Active bundle. Restore the local Active bundle, then retry cleanup --force, or inspect and remove the exact output manually.', unavailableTarget);
1033
+ }
1034
+ const pluginId = formatPluginId({ sourceId, pluginName: parsed.pluginName });
1035
+ plugin.sourceId = sourceId;
1036
+ plugin.sourceIdentity = active.sourceIdentity;
1037
+ plugin.pluginId = pluginId;
1038
+ plugin.marketplaceName = sourceId === options.primarySourceId ? 'aw' : `aw-${sourceId}`;
1039
+ const context = {
1040
+ env: options.env,
1041
+ nativeCliRunner: getDefaultNativeCliRunner(),
1042
+ reporter: createNoopReporter(),
1043
+ debugLogger: createNoopDebugLogger(),
1044
+ };
1045
+ const integration = createRebuildIntegration({ context });
1046
+ for (const platform of options.platforms) {
1047
+ const inspectedTarget = await integration.buildTarget({
1048
+ sourceId,
1049
+ sourceIdentity: active.sourceIdentity,
1050
+ pluginId,
1051
+ marketplaceName: plugin.marketplaceName,
1052
+ marketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces', sourceId),
1053
+ plugin,
1054
+ platform,
1055
+ scope: options.scope,
1056
+ cwd: options.cwd,
1057
+ command: 'install',
1058
+ catalog: {
1059
+ rootDir: active.snapshotPath,
1060
+ source: 'local',
1061
+ catalogVersion: active.catalogVersion,
1062
+ },
1063
+ readOnlyInspectionPlan: true,
1064
+ readOnlyShallowInspectionPlan: true,
1065
+ });
1066
+ const expected = [...new Set([
1067
+ ...(inspectedTarget.desiredOwnedComponents ?? []),
1068
+ ...(inspectedTarget.optionalOwnedComponents ?? []),
1069
+ ])].filter((component) => component.kind === 'skill-directory' || component.kind === 'owned-text-file');
1070
+ const inspection = await inspectOwnedComponents({
1071
+ components: expected,
1072
+ executor: integration.driver.ownership,
1073
+ forceExclusive: true,
1074
+ });
1075
+ const ordinaryInspection = await inspectOwnedComponents({
1076
+ components: expected,
1077
+ executor: integration.driver.ownership,
1078
+ });
1079
+ const removable = inspection.components.flatMap((entry) => entry.state === 'present'
1080
+ && (options.damagedOnly !== true
1081
+ || ordinaryInspection.components.find((candidate) => candidate.component === entry.component)?.state !== 'present')
1082
+ && 'path' in entry.component
1083
+ ? [entry.component]
1084
+ : []);
1085
+ if (removable.length === 0)
1086
+ continue;
1087
+ const target = {
1088
+ ...inspectedTarget,
1089
+ ownedComponents: removable,
1090
+ desiredOwnedComponents: removable,
1091
+ optionalOwnedComponents: [],
1092
+ requiresNative: false,
1093
+ writeNativeRequired: false,
1094
+ artifacts: inspectedTarget.artifacts.filter((artifact) => removable.some((component) => path.resolve(component.path) === path.resolve(artifact.path))),
1095
+ operationActions: [],
1096
+ forceExclusiveCleanup: true,
1097
+ };
1098
+ targets.push(target);
1099
+ evidence.push(await forceOfflineCleanupEvidence({
1100
+ reason: 'bundleUnavailable',
1101
+ pluginId,
1102
+ pluginName: parsed.pluginName,
1103
+ sourceId,
1104
+ sourceIdentity: active.sourceIdentity,
1105
+ platform,
1106
+ scope: options.scope,
1107
+ components: removable,
1108
+ }));
1109
+ }
1110
+ }
1111
+ if (targets.length === 0) {
1112
+ return { targets, evidence };
1113
+ }
1114
+ return { targets, evidence };
1115
+ }
1116
+ async function buildLiveForceExclusiveCleanupPlan(options) {
1117
+ const targets = [];
1118
+ const evidence = [];
1119
+ for (const target of options.targets) {
1120
+ const components = [];
1121
+ const references = options.inventory.references.filter((reference) => reference.kind === 'namespacedOwner'
1122
+ && reference.sourceId === target.sourceId
1123
+ && reference.pluginName === target.pluginName
1124
+ && reference.platform === target.platform
1125
+ && reference.scope === target.scope);
1126
+ for (const reference of references) {
1127
+ if (path.basename(reference.location) === SKILL_OWNER_MARKER) {
1128
+ const skillRoot = path.dirname(reference.location);
1129
+ const allowedRoot = path.dirname(skillRoot);
1130
+ const marker = await readSkillOwnerMarker({ skillRoot, allowedRoot }).catch(() => null);
1131
+ if (marker
1132
+ && marker.sourceId === target.sourceId
1133
+ && marker.pluginId === target.pluginId
1134
+ && marker.platform === target.platform
1135
+ && marker.scope === target.scope) {
1136
+ components.push({ kind: 'skill-directory', path: skillRoot, allowedRoot, identity: marker });
1137
+ }
1138
+ continue;
1139
+ }
1140
+ const parsed = await fs.readFile(reference.location, 'utf8')
1141
+ .then((content) => parseOwnedTextHeader(content))
1142
+ .catch(() => null);
1143
+ if (parsed
1144
+ && parsed.identity.sourceId === target.sourceId
1145
+ && parsed.identity.pluginId === target.pluginId
1146
+ && parsed.identity.platform === target.platform
1147
+ && parsed.identity.scope === target.scope) {
1148
+ components.push({
1149
+ kind: 'owned-text-file',
1150
+ path: reference.location,
1151
+ allowedRoot: path.dirname(reference.location),
1152
+ identity: parsed.identity,
1153
+ });
1154
+ }
1155
+ }
1156
+ if (components.length === 0)
1157
+ continue;
1158
+ const plugin = createSyntheticPlugin(target.pluginName, options.env);
1159
+ plugin.sourceId = target.sourceId;
1160
+ plugin.sourceIdentity = target.sourceIdentity;
1161
+ plugin.pluginId = target.pluginId;
1162
+ plugin.marketplaceName = target.marketplaceName;
1163
+ targets.push({
1164
+ plugin,
1165
+ identity: {
1166
+ sourceId: target.sourceId,
1167
+ sourceIdentity: target.sourceIdentity,
1168
+ pluginId: target.pluginId,
1169
+ pluginName: target.pluginName,
1170
+ pluginVersion: plugin.manifest.version,
1171
+ platform: target.platform,
1172
+ scope: target.scope,
1173
+ cwd: options.cwd,
1174
+ },
1175
+ ownedComponents: components,
1176
+ desiredOwnedComponents: components,
1177
+ optionalOwnedComponents: [],
1178
+ requiresNative: false,
1179
+ writeNativeRequired: false,
1180
+ artifacts: [],
1181
+ operationActions: [],
1182
+ postApplySource: plugin,
1183
+ forceExclusiveCleanup: true,
1184
+ });
1185
+ evidence.push(await forceOfflineCleanupEvidence({
1186
+ reason: 'bundleUnavailable',
1187
+ pluginId: target.pluginId,
1188
+ pluginName: target.pluginName,
1189
+ sourceId: target.sourceId,
1190
+ sourceIdentity: target.sourceIdentity,
1191
+ platform: target.platform,
1192
+ scope: target.scope,
1193
+ components,
1194
+ }));
1195
+ }
1196
+ return { targets, evidence };
1197
+ }
1198
+ async function mergeForceCleanupPlans(...plans) {
1199
+ const targets = new Map();
1200
+ for (const target of plans.flatMap((plan) => plan.targets)) {
1201
+ const key = `${target.identity.pluginId}\0${target.identity.platform}\0${target.identity.scope}`;
1202
+ const previous = targets.get(key);
1203
+ if (!previous) {
1204
+ targets.set(key, target);
1205
+ continue;
1206
+ }
1207
+ const components = [...previous.ownedComponents];
1208
+ for (const component of target.ownedComponents) {
1209
+ if ('path' in component && !components.some((candidate) => 'path' in candidate && path.resolve(candidate.path) === path.resolve(component.path))) {
1210
+ components.push(component);
1211
+ }
1212
+ }
1213
+ previous.ownedComponents = components;
1214
+ previous.desiredOwnedComponents = components;
1215
+ }
1216
+ const mergedTargets = [...targets.values()];
1217
+ return {
1218
+ targets: mergedTargets,
1219
+ evidence: await Promise.all(mergedTargets.map((target) => forceOfflineCleanupEvidence({
1220
+ reason: 'bundleUnavailable',
1221
+ pluginId: target.identity.pluginId,
1222
+ pluginName: target.identity.pluginName,
1223
+ sourceId: target.identity.sourceId,
1224
+ sourceIdentity: target.identity.sourceIdentity,
1225
+ platform: target.identity.platform,
1226
+ scope: target.identity.scope,
1227
+ components: target.ownedComponents,
1228
+ }))),
1229
+ };
1230
+ }
1231
+ export async function runRebuildCommand(options) {
1232
+ const runtimeEnv = options.runtime?.env ?? process.env;
1233
+ const readInventory = options.runtime?.inventoryCatalogSourceReferences
1234
+ ?? inventoryCatalogSourceReferences;
1235
+ let ordinaryInventory;
1236
+ const readOrdinaryInventory = async () => {
1237
+ ordinaryInventory ??= await measureCommandPerformanceStage(options.runtime, 'inventory', () => readInventory({
1238
+ cwd: options.commandOptions.cwd,
1239
+ env: runtimeEnv,
1240
+ }));
1241
+ return ordinaryInventory;
1242
+ };
1243
+ let requestedPluginNames = options.pluginNames;
1244
+ const cleanupForce = options.command === 'cleanup'
1245
+ && options.commandOptions.force === true;
1246
+ const expectedOfflineCleanup = options.runtime?.expectedOfflineCleanupEvidence;
1247
+ let cleanupInventory;
1248
+ let cleanupLiveTargets = [];
1249
+ let liveTargets = [];
1250
+ let legacyLiveTargets = [];
1251
+ let malformedSelectedConfigPreserved = false;
1252
+ if (options.command === 'cleanup' && !options.all) {
1253
+ cleanupInventory = await readOrdinaryInventory();
1254
+ const live = resolveLiveCleanupTargets({
1255
+ selectors: requestedPluginNames,
1256
+ platforms: selectedPlatforms(options.commandOptions.platform),
1257
+ scope: options.commandOptions.scope,
1258
+ references: cleanupInventory.references,
1259
+ });
1260
+ requestedPluginNames = live.selectors;
1261
+ cleanupLiveTargets = live.targets;
1262
+ if (expectedOfflineCleanup && expectedOfflineCleanup.forceExclusive !== true) {
1263
+ const liveTarget = cleanupLiveTargets.find((target) => target.pluginId === expectedOfflineCleanup.pluginId
1264
+ && target.pluginName === expectedOfflineCleanup.pluginName
1265
+ && target.sourceId === expectedOfflineCleanup.sourceId
1266
+ && target.sourceIdentity === expectedOfflineCleanup.sourceIdentity
1267
+ && target.platform === expectedOfflineCleanup.platform
1268
+ && target.scope === expectedOfflineCleanup.scope);
1269
+ const actualOfflineCleanup = liveTarget
1270
+ ? offlineCleanupEvidenceFromInventory({
1271
+ ...expectedOfflineCleanup,
1272
+ inventory: cleanupInventory,
1273
+ })
1274
+ : null;
1275
+ if (!liveTarget
1276
+ || !actualOfflineCleanup
1277
+ || !offlineCleanupEvidenceMatches(expectedOfflineCleanup, actualOfflineCleanup)) {
1278
+ throw namespacedPreflightError('operationReviewStale: offline cleanup ownership evidence changed before mutation.', cleanupLiveTargets);
1279
+ }
1280
+ cleanupLiveTargets = [liveTarget];
1281
+ }
1282
+ liveTargets = cleanupLiveTargets;
1283
+ if (cleanupLiveTargets.length > 0) {
1284
+ const filtered = omitSelectedPluginConfigErrors(cleanupInventory, cleanupLiveTargets);
1285
+ cleanupInventory = filtered.inventory;
1286
+ malformedSelectedConfigPreserved ||= filtered.omitted;
1287
+ }
1288
+ }
1289
+ if (options.all) {
1290
+ const inventory = await readOrdinaryInventory();
1291
+ if (options.command !== 'cleanup') {
1292
+ assertRebuildCatalogSourceInventoryComplete(inventory, options.commandOptions.scope);
1293
+ }
1294
+ const requestedPlatforms = new Set(selectedPlatforms(options.commandOptions.platform));
1295
+ legacyLiveTargets = projectLegacyTargets(selectedLegacyLiveReferences(inventory.references, requestedPlatforms, options.commandOptions.scope));
1296
+ const live = inventory.references.filter((reference) => (reference.kind === 'namespacedOwner' || reference.kind === 'namespacedMarketplace')
1297
+ && reference.sourceId
1298
+ && reference.pluginName
1299
+ && reference.scope === options.commandOptions.scope
1300
+ && reference.platform
1301
+ && requestedPlatforms.has(reference.platform));
1302
+ const projectedTargets = [...new Map(live.map((reference) => {
1303
+ const target = {
1304
+ pluginId: formatPluginId({
1305
+ sourceId: reference.sourceId,
1306
+ pluginName: reference.pluginName,
1307
+ }),
1308
+ pluginName: reference.pluginName,
1309
+ sourceId: reference.sourceId,
1310
+ sourceIdentity: reference.sourceIdentity ?? 'unavailable',
1311
+ platform: reference.platform,
1312
+ scope: reference.scope,
1313
+ };
1314
+ return [[
1315
+ target.pluginId,
1316
+ target.sourceIdentity,
1317
+ target.platform,
1318
+ target.scope,
1319
+ ].join('\0'), target];
1320
+ })).values()];
1321
+ liveTargets = projectedTargets;
1322
+ if (options.command === 'cleanup') {
1323
+ cleanupInventory = inventory;
1324
+ cleanupLiveTargets = projectedTargets;
1325
+ }
1326
+ const identities = new Set();
1327
+ for (const reference of live) {
1328
+ const pluginId = formatPluginId({ sourceId: reference.sourceId, pluginName: reference.pluginName });
1329
+ identities.add(pluginId);
1330
+ }
1331
+ requestedPluginNames = [...identities];
1332
+ }
1333
+ const selectors = requestedPluginNames.map(parsePluginId);
1334
+ const sourceIds = planPluginSelectorSourceIds(selectors);
1335
+ if (options.all && selectors.length === 0) {
1336
+ if (legacyLiveTargets.length > 0) {
1337
+ throw namespacedPreflightError(`legacyStateUnsupported: legacy live runtime state exists for ${legacyLiveTargets.map((target) => `${target.pluginName}:${target.platform}/${target.scope}`).join(', ')}.`, [], legacyLiveTargets);
1338
+ }
1339
+ return mergeRebuildCommandResults([]);
1340
+ }
1341
+ if (options.command === 'cleanup') {
1342
+ if (cleanupForce) {
1343
+ const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
1344
+ const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
1345
+ const primarySource = cleanupSourceSnapshot?.config.sources.find((source) => source.primary)?.id;
1346
+ const livePlan = await buildLiveForceExclusiveCleanupPlan({
1347
+ targets: cleanupLiveTargets,
1348
+ inventory: cleanupInventory,
1349
+ env: runtimeEnv,
1350
+ cwd: options.commandOptions.cwd,
1351
+ });
1352
+ const selectorsNeedingSupplement = requestedPluginNames.filter((selector) => {
1353
+ const parsed = parsePluginId(selector);
1354
+ const sourceId = parsed.sourceId ?? primarySource;
1355
+ if (!sourceId)
1356
+ return true;
1357
+ const missingLiveTarget = requestedPlatforms.some((platform) => !livePlan.targets.some((target) => target.identity.pluginId === formatPluginId({ sourceId, pluginName: parsed.pluginName })
1358
+ && target.identity.platform === platform
1359
+ && target.identity.scope === options.commandOptions.scope));
1360
+ const damagedExclusiveEvidence = cleanupInventory.errors.some((error) => (error.kind === 'ownerMarkerRead'
1361
+ || error.kind === 'ownerMarkerValidation'
1362
+ || error.kind === 'ownerHeaderRead'
1363
+ || error.kind === 'ownerHeaderParse'
1364
+ || error.kind === 'ownerHeaderPathValidation')
1365
+ && !error.relevance.global
1366
+ && (error.relevance.sourceId === undefined || error.relevance.sourceId === sourceId)
1367
+ && (error.relevance.pluginName === undefined || error.relevance.pluginName === parsed.pluginName)
1368
+ && (error.relevance.platform === undefined || requestedPlatforms.includes(error.relevance.platform))
1369
+ && (error.relevance.scope === undefined || error.relevance.scope === options.commandOptions.scope));
1370
+ return missingLiveTarget || damagedExclusiveEvidence;
1371
+ });
1372
+ const supplement = selectorsNeedingSupplement.length > 0
1373
+ ? await buildForceExclusiveCleanupPlan({
1374
+ selectors: selectorsNeedingSupplement,
1375
+ platforms: requestedPlatforms,
1376
+ scope: options.commandOptions.scope,
1377
+ cwd: options.commandOptions.cwd,
1378
+ env: runtimeEnv,
1379
+ primarySourceId: primarySource,
1380
+ damagedOnly: cleanupLiveTargets.length > 0,
1381
+ })
1382
+ : { targets: [], evidence: [] };
1383
+ const forcePlan = await mergeForceCleanupPlans(livePlan, supplement);
1384
+ const requestedTargets = requestedPluginNames.flatMap((selector) => {
1385
+ const parsed = parsePluginId(selector);
1386
+ const sourceId = parsed.sourceId ?? primarySource;
1387
+ return sourceId ? requestedPlatforms.map((platform) => ({
1388
+ pluginId: formatPluginId({ sourceId, pluginName: parsed.pluginName }),
1389
+ pluginName: parsed.pluginName,
1390
+ sourceId,
1391
+ sourceIdentity: 'unavailable',
1392
+ platform,
1393
+ scope: options.commandOptions.scope,
1394
+ })) : [];
1395
+ });
1396
+ const unresolvedTargets = requestedTargets.filter((requested) => !forcePlan.targets.some((target) => target.identity.pluginId === requested.pluginId
1397
+ && target.identity.platform === requested.platform
1398
+ && target.identity.scope === requested.scope));
1399
+ if (expectedOfflineCleanup?.forceExclusive === true) {
1400
+ const actual = forcePlan.evidence.find((candidate) => candidate.pluginId === expectedOfflineCleanup.pluginId
1401
+ && candidate.platform === expectedOfflineCleanup.platform
1402
+ && candidate.scope === expectedOfflineCleanup.scope);
1403
+ if (!actual || !offlineCleanupEvidenceMatches(expectedOfflineCleanup, actual)) {
1404
+ throw namespacedPreflightError('operationReviewStale: force cleanup path or ownership evidence changed before mutation.', cleanupLiveTargets);
1405
+ }
1406
+ }
1407
+ if (unresolvedTargets.length > 0) {
1408
+ throw namespacedPreflightError('forceCleanupPathUnavailable: No exact exclusive output is available from live evidence or the local Active shallow plan. Restore the local Active bundle, then retry cleanup --force, or inspect and remove the exact output manually.', unresolvedTargets);
1409
+ }
1410
+ if (forcePlan.targets.length === 0) {
1411
+ throw namespacedPreflightError('forceCleanupPathUnavailable: No exact exclusive output is available from live evidence or the local Active shallow plan. Restore the local Active bundle, then retry cleanup --force, or inspect and remove the exact output manually.', requestedTargets);
1412
+ }
1413
+ const operationPlan = createOfflineCleanupOperationPlan(forcePlan.evidence);
1414
+ if (options.runtime?.expectedPlanFingerprint) {
1415
+ assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, operationPlan);
1416
+ }
1417
+ if (options.runtime?.previewOperationPlan) {
1418
+ return {
1419
+ ...await previewOfflineOwnedComponents({
1420
+ targets: cleanupLiveTargets,
1421
+ evidence: forcePlan.evidence,
1422
+ cwd: options.commandOptions.cwd,
1423
+ env: runtimeEnv,
1424
+ onPreviewStage: options.runtime.onPreviewStage,
1425
+ }),
1426
+ operationPlan,
1427
+ };
1428
+ }
1429
+ const forceResults = await Promise.all(forcePlan.targets.map(async (target) => {
1430
+ const integration = createRebuildIntegration({
1431
+ context: {
1432
+ env: runtimeEnv,
1433
+ nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1434
+ reporter: options.runtime?.reporter ?? createNoopReporter(),
1435
+ debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1436
+ },
1437
+ });
1438
+ const { finalizeCleanup: _finalizeCleanup, ...pathOnlyDriver } = integration.driver;
1439
+ const result = await executeRebuildOperation({ command: 'cleanup', target, driver: pathOnlyDriver });
1440
+ if (cleanupLiveTargets.some((liveTarget) => liveTarget.marketplaceIdentityUnresolved
1441
+ && liveTarget.pluginId === result.target.pluginId
1442
+ && liveTarget.platform === result.target.platform
1443
+ && liveTarget.scope === result.target.scope)) {
1444
+ result.residual.push('nativeMarketplaceIdentityUnresolved');
1445
+ }
1446
+ return result;
1447
+ }));
1448
+ const warnings = cleanupLiveTargets.flatMap((target) => target.marketplaceIdentityUnresolved
1449
+ ? [`nativeMarketplaceIdentityUnresolved: Preserved ambiguous native registration for ${target.pluginId} on ${target.platform}/${target.scope}.`]
1450
+ : []);
1451
+ return leaseSafeRebuildResult({
1452
+ platforms: [...new Set(forceResults.map((entry) => entry.target.platform))],
1453
+ plugins: forceResults.filter((entry) => entry.status === 'succeeded')
1454
+ .map((entry) => entry.target.pluginName),
1455
+ warnings,
1456
+ rebuildResults: forceResults,
1457
+ targetResults: forceResults.map(toTargetResult),
1458
+ overallState: forceResults.every((entry) => entry.status === 'succeeded')
1459
+ ? 'succeeded'
1460
+ : forceResults.some((entry) => entry.status === 'succeeded') ? 'partial' : 'failed',
1461
+ receiptTargets: forceResults.map((entry) => entry.target),
1462
+ offlineCleanupPlans: forcePlan.evidence,
1463
+ operationPlan,
1464
+ });
1465
+ }
1466
+ if (cleanupLiveTargets.length === 0) {
1467
+ const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
1468
+ const primarySource = await readCatalogSourcesConfigSnapshot(runtimeEnv)
1469
+ .then((snapshot) => snapshot.config.sources.find((source) => source.primary)?.id)
1470
+ .catch(() => undefined);
1471
+ const unresolvedTargets = requestedPluginNames.flatMap((selector) => {
1472
+ const parsed = parsePluginId(selector);
1473
+ const sourceId = parsed.sourceId ?? primarySource;
1474
+ return sourceId ? requestedPlatforms.map((platform) => ({
1475
+ pluginId: formatPluginId({ sourceId, pluginName: parsed.pluginName }),
1476
+ pluginName: parsed.pluginName,
1477
+ sourceId,
1478
+ sourceIdentity: 'unavailable',
1479
+ platform,
1480
+ scope: options.commandOptions.scope,
1481
+ })) : [];
1482
+ });
1483
+ const qualifiedTargets = requestedPluginNames.flatMap((selector) => {
1484
+ const parsed = parsePluginId(selector);
1485
+ return parsed.sourceId ? [{ sourceId: parsed.sourceId, pluginName: parsed.pluginName }] : [];
1486
+ });
1487
+ const filtered = cleanupInventory && qualifiedTargets.length > 0
1488
+ ? omitSelectedPluginConfigErrors(cleanupInventory, qualifiedTargets)
1489
+ : { inventory: cleanupInventory, omitted: false };
1490
+ malformedSelectedConfigPreserved ||= filtered.omitted;
1491
+ const inventoryError = filtered.inventory?.errors.find((error) => {
1492
+ if (!cleanupForce
1493
+ || (error.kind !== 'ownerMarkerRead' && error.kind !== 'ownerMarkerValidation')
1494
+ || error.relevance.global)
1495
+ return true;
1496
+ const relevant = unresolvedTargets.some((target) => !error.relevance.global
1497
+ && (error.relevance.sourceId === undefined || error.relevance.sourceId === target.sourceId)
1498
+ && (error.relevance.pluginName === undefined || error.relevance.pluginName === target.pluginName)
1499
+ && (error.relevance.platform === undefined || error.relevance.platform === target.platform)
1500
+ && (error.relevance.scope === undefined || error.relevance.scope === target.scope));
1501
+ return relevant
1502
+ && !(error.relevance.platform === 'codex' && error.relevance.scope === 'project');
1503
+ });
1504
+ if (inventoryError) {
1505
+ throw namespacedPreflightError(`stateInventoryFailed: ${inventoryError.message}`, unresolvedTargets);
1506
+ }
1507
+ const result = mergeRebuildCommandResults([]);
1508
+ if (malformedSelectedConfigPreserved) {
1509
+ result.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
1510
+ }
1511
+ if (cleanupForce && !options.runtime?.previewOperationPlan) {
1512
+ try {
1513
+ await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
1514
+ }
1515
+ catch (error) {
1516
+ result.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
1517
+ }
1518
+ }
1519
+ return result;
1520
+ }
1521
+ const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
1522
+ const configuredSources = new Set(cleanupSourceSnapshot?.config.sources.map((source) => source.id) ?? []);
1523
+ let evidence = expectedOfflineCleanup
1524
+ ? [expectedOfflineCleanup]
1525
+ : cleanupLiveTargets.flatMap((target) => {
1526
+ const value = offlineCleanupEvidenceFromInventory({
1527
+ reason: configuredSources.has(target.sourceId) ? 'bundleUnavailable' : 'sourceMissing',
1528
+ pluginId: target.pluginId,
1529
+ pluginName: target.pluginName,
1530
+ sourceId: target.sourceId,
1531
+ sourceIdentity: target.sourceIdentity,
1532
+ platform: target.platform,
1533
+ scope: target.scope,
1534
+ inventory: cleanupInventory,
1535
+ });
1536
+ return value ? [value] : [];
1537
+ });
1538
+ if (options.runtime?.expectedPlanFingerprint) {
1539
+ assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, createOfflineCleanupOperationPlan(evidence));
1540
+ }
1541
+ if (options.runtime?.previewOperationPlan) {
1542
+ const previewResult = await previewOfflineOwnedComponents({
1543
+ targets: cleanupLiveTargets,
1544
+ evidence,
1545
+ cwd: options.commandOptions.cwd,
1546
+ env: runtimeEnv,
1547
+ onPreviewStage: options.runtime.onPreviewStage,
1548
+ });
1549
+ return previewResult;
1550
+ }
1551
+ const cleanupResult = await cleanupOfflineOwnedComponents({
1552
+ targets: cleanupLiveTargets,
1553
+ evidence,
1554
+ cwd: options.commandOptions.cwd,
1555
+ env: runtimeEnv,
1556
+ inventoryErrors: cleanupInventory.errors,
1557
+ removeObsoleteInstallationContracts: options.runtime?.removeObsoleteInstallationContracts,
1558
+ nativeCliRunner: options.runtime?.nativeCliRunner,
1559
+ codexManager: options.runtime?.codexManager,
1560
+ });
1561
+ if (malformedSelectedConfigPreserved) {
1562
+ cleanupResult.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
1563
+ }
1564
+ return cleanupResult;
1565
+ }
1566
+ try {
1567
+ if (options.runtime?.previewOperationPlan) {
1568
+ await options.runtime.onPreviewStage?.('catalog');
1569
+ }
1570
+ if (options.runtime?.catalogBootstrapError !== undefined) {
1571
+ throw options.runtime.catalogBootstrapError;
1572
+ }
1573
+ const catalogOperation = options.runtime?.catalogOperationContext
1574
+ ?? await measureCommandPerformanceStage(options.runtime, 'catalog.load', () => resolveCatalogOperationContext({
1575
+ ...(sourceIds ? { sourceIds } : {}),
1576
+ env: runtimeEnv,
1577
+ }));
1578
+ if (options.runtime?.expectedGenerationSetDigest
1579
+ && catalogOperation?.generationSetDigest !== options.runtime.expectedGenerationSetDigest) {
1580
+ throw new Error('operationReviewStale: Active Catalog generation changed before mutation.');
1581
+ }
1582
+ const resolvedResult = await withResolvedMultiSourceCatalog({
1583
+ cliVersion: resolveRuntimeCliVersion(),
1584
+ ...(sourceIds ? { sourceIds } : {}),
1585
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
1586
+ env: runtimeEnv,
1587
+ consume: async (catalog) => {
1588
+ const failedSources = catalog.sources.filter((source) => source.error);
1589
+ if (failedSources.length > 0) {
1590
+ throw namespacedPreflightError(`Cannot resolve required catalog sources: ${failedSources.map((source) => source.sourceId).join(', ')}.`, liveTargets, legacyLiveTargets);
1591
+ }
1592
+ const availableEntries = catalog.sources.flatMap((item) => {
1593
+ const source = item.resolution;
1594
+ return source.manifest.entries.map((entry) => ({
1595
+ source,
1596
+ entry,
1597
+ pluginId: formatPluginId({ sourceId: source.sourceId, pluginName: entry.name }),
1598
+ }));
1599
+ });
1600
+ const intentCatalogEntries = availableEntries.map((candidate) => ({
1601
+ pluginId: candidate.pluginId,
1602
+ dependencyPluginIds: candidate.entry.dependencies.map((dependency) => formatPluginId({
1603
+ sourceId: candidate.source.sourceId,
1604
+ pluginName: dependency,
1605
+ })),
1606
+ }));
1607
+ const selectedEntries = selectors.map((selector, index) => {
1608
+ const matches = availableEntries.filter((candidate) => candidate.entry.name === selector.pluginName
1609
+ && (selector.sourceId === undefined || candidate.source.sourceId === selector.sourceId));
1610
+ if (matches.length === 0) {
1611
+ throw new Error(`Plugin "${requestedPluginNames[index]}" does not exist in the required catalog source set.`);
1612
+ }
1613
+ if (matches.length > 1) {
1614
+ throw new Error(`Plugin "${selector.pluginName}" is ambiguous across sources: ${matches.map((entry) => entry.source.sourceId).join(', ')}.`);
1615
+ }
1616
+ return matches[0];
1617
+ });
1618
+ const dependencyEnabled = options.command === 'install'
1619
+ || options.command === 'sync'
1620
+ || options.command === 'reinstall';
1621
+ const entryClosure = (roots) => {
1622
+ const result = [];
1623
+ const seen = new Set();
1624
+ const visiting = new Set();
1625
+ const byId = new Map(availableEntries.map((candidate) => [candidate.pluginId, candidate]));
1626
+ const visit = (candidate) => {
1627
+ if (seen.has(candidate.pluginId))
1628
+ return;
1629
+ if (visiting.has(candidate.pluginId))
1630
+ throw new Error(`Catalog dependency cycle includes "${candidate.pluginId}".`);
1631
+ visiting.add(candidate.pluginId);
1632
+ for (const dependency of candidate.entry.dependencies) {
1633
+ const dependencyId = formatPluginId({ sourceId: candidate.source.sourceId, pluginName: dependency });
1634
+ const resolved = byId.get(dependencyId);
1635
+ if (!resolved)
1636
+ throw new Error(`Catalog plugin "${candidate.pluginId}" depends on missing plugin "${dependency}".`);
1637
+ visit(resolved);
1638
+ }
1639
+ visiting.delete(candidate.pluginId);
1640
+ seen.add(candidate.pluginId);
1641
+ result.push(candidate);
1642
+ };
1643
+ for (const root of roots)
1644
+ visit(root);
1645
+ return result;
1646
+ };
1647
+ const removalConsumerEntries = options.command === 'cleanup' || options.command === 'uninstall'
1648
+ ? availableEntries.filter((candidate) => selectedEntries.some((selectedEntry) => candidate.source.sourceId === selectedEntry.source.sourceId
1649
+ && candidate.entry.dependencies.includes(selectedEntry.entry.name)))
1650
+ : [];
1651
+ const removalDesiredEntries = options.command === 'cleanup' || options.command === 'uninstall'
1652
+ ? entryClosure(selectedEntries)
1653
+ : [];
1654
+ const requiredEntryClosure = dependencyEnabled
1655
+ ? entryClosure(selectedEntries)
1656
+ : [...new Map([
1657
+ ...selectedEntries,
1658
+ ...removalDesiredEntries,
1659
+ ...removalConsumerEntries,
1660
+ ].map((candidate) => [candidate.pluginId, candidate])).values()];
1661
+ if (dependencyEnabled) {
1662
+ for (const candidate of requiredEntryClosure) {
1663
+ const requiredCapabilities = candidate.entry.requires.aw.capabilities ?? [];
1664
+ const support = checkAwCapabilitySupport(requiredCapabilities, IMPLEMENTED_AW_CAPABILITIES);
1665
+ if (!support.supported) {
1666
+ throw new AwCapabilityUnsupportedError(candidate.pluginId, support.missing);
1667
+ }
1668
+ }
1669
+ await measureCommandPerformanceStage(options.runtime, 'catalog.validation', async () => {
1670
+ for (const item of catalog.sources) {
1671
+ const source = item.resolution;
1672
+ const names = requiredEntryClosure
1673
+ .filter((candidate) => candidate.source.sourceId === source.sourceId)
1674
+ .map((candidate) => candidate.entry.name);
1675
+ if (names.length > 0) {
1676
+ await validateCatalogSnapshotPluginEntriesComplete(source.rootDir, names);
1677
+ }
1678
+ }
1679
+ });
1680
+ }
1681
+ const available = await Promise.all(requiredEntryClosure.map(async (candidate) => {
1682
+ const plugin = await readPluginManifest(path.join(candidate.source.rootDir, 'plugins', candidate.entry.name), { mode: 'bundle' });
1683
+ plugin.sourceId = candidate.source.sourceId;
1684
+ plugin.sourceIdentity = candidate.source.sourceIdentity;
1685
+ plugin.pluginId = candidate.pluginId;
1686
+ return { source: candidate.source, plugin, pluginId: candidate.pluginId };
1687
+ }));
1688
+ const selected = selectedEntries.map((entry) => available.find((candidate) => candidate.pluginId === entry.pluginId));
1689
+ const selectedIds = new Set(selected.map((entry) => entry.pluginId));
1690
+ const preserveOtherCodexScopeRootIds = new Set(selected.flatMap((entry) => analyzeCodexSurface(entry.plugin.manifest).scopeOverlap === 'native-compatible'
1691
+ ? [entry.pluginId]
1692
+ : []));
1693
+ const resolveClosureEntries = (rootsBySource) => catalog.sources.flatMap((item) => {
1694
+ const source = item.resolution;
1695
+ const roots = rootsBySource
1696
+ .filter((entry) => entry.source.sourceId === source.sourceId)
1697
+ .map((entry) => entry.plugin.manifest.name);
1698
+ if (roots.length === 0)
1699
+ return [];
1700
+ const sourcePlugins = available
1701
+ .filter((entry) => entry.source.sourceId === source.sourceId)
1702
+ .map((entry) => entry.plugin);
1703
+ const closure = resolvePluginMetaDependencyClosure(sourcePlugins, roots);
1704
+ return closure.map((plugin) => available.find((entry) => entry.source.sourceId === source.sourceId
1705
+ && entry.plugin.manifest.name === plugin.manifest.name));
1706
+ });
1707
+ const closureEntriesByPlatform = new Map();
1708
+ if (dependencyEnabled) {
1709
+ for (const platform of selectedPlatforms(options.commandOptions.platform)) {
1710
+ const platformRoots = options.commandOptions.platform === 'both'
1711
+ ? selected.filter((entry) => supportsPlatform(entry.plugin.manifest, platform))
1712
+ : selected;
1713
+ closureEntriesByPlatform.set(platform, resolveClosureEntries(platformRoots));
1714
+ }
1715
+ }
1716
+ const closureEntries = dependencyEnabled
1717
+ ? [...new Map([...closureEntriesByPlatform.values()].flat().map((entry) => [entry.pluginId, entry])).values()]
1718
+ : options.command === 'cleanup'
1719
+ ? resolveClosureEntries(selected)
1720
+ : selected;
1721
+ const rawExecutionSelections = options.all
1722
+ ? liveTargets.flatMap((target) => {
1723
+ const entry = selected.find((candidate) => candidate.pluginId === target.pluginId);
1724
+ if (target.sourceIdentity !== 'unavailable'
1725
+ && entry.source.sourceIdentity !== target.sourceIdentity) {
1726
+ throw namespacedPreflightError(`sourceChanged: ${target.pluginId} live target has a different source identity.`, liveTargets, legacyLiveTargets);
1727
+ }
1728
+ const sourcePlugins = available
1729
+ .filter((candidate) => candidate.source.sourceId === entry.source.sourceId)
1730
+ .map((candidate) => candidate.plugin);
1731
+ return (options.command === 'uninstall'
1732
+ ? [entry.plugin]
1733
+ : resolvePluginMetaDependencyClosure(sourcePlugins, [entry.plugin.manifest.name])).map((plugin) => ({
1734
+ entry: available.find((candidate) => candidate.source.sourceId === entry.source.sourceId
1735
+ && candidate.plugin.manifest.name === plugin.manifest.name),
1736
+ platform: target.platform,
1737
+ }));
1738
+ })
1739
+ : selectedPlatforms(options.commandOptions.platform).flatMap((platform) => (dependencyEnabled ? closureEntriesByPlatform.get(platform) ?? [] : closureEntries)
1740
+ .flatMap((entry) => options.command !== 'cleanup'
1741
+ && options.command !== 'uninstall'
1742
+ && options.commandOptions.platform === 'both'
1743
+ && !supportsPlatform(entry.plugin.manifest, platform)
1744
+ ? []
1745
+ : [{ entry, platform }]));
1746
+ let executionSelections = [...new Map(rawExecutionSelections.map((selection) => [[
1747
+ selection.entry.pluginId,
1748
+ selection.entry.source.sourceIdentity,
1749
+ selection.platform,
1750
+ options.commandOptions.scope,
1751
+ ].join('\0'), selection])).values()];
1752
+ const preflightTargets = executionSelections.map(({ entry, platform }) => ({
1753
+ pluginId: entry.pluginId,
1754
+ pluginName: entry.plugin.manifest.name,
1755
+ sourceId: entry.source.sourceId,
1756
+ sourceIdentity: entry.source.sourceIdentity,
1757
+ pluginVersion: entry.plugin.manifest.version,
1758
+ platform,
1759
+ scope: options.commandOptions.scope,
1760
+ }));
1761
+ if (options.runtime?.previewOperationPlan) {
1762
+ await options.runtime.onPreviewStage?.('inventory');
1763
+ }
1764
+ const stateInventory = await readOrdinaryInventory();
1765
+ const assertWriteInventoryComplete = (inventory, exactOwnedTextErrorLocations, exactSkillMarkerErrorLocations) => {
1766
+ if (options.command === 'cleanup')
1767
+ return;
1768
+ const isExpectedOwnedTextHeaderError = (error) => {
1769
+ if (error.kind !== 'ownerHeaderParse'
1770
+ && error.kind !== 'ownerHeaderPathValidation')
1771
+ return false;
1772
+ if (exactOwnedTextErrorLocations === undefined)
1773
+ return true;
1774
+ const relevance = error.relevance;
1775
+ if (relevance.global || relevance.platform === undefined || relevance.scope === undefined) {
1776
+ return false;
1777
+ }
1778
+ return [...exactOwnedTextErrorLocations].some((target) => {
1779
+ const [sourceId, pluginName, platform, scope, location] = target.split('\0');
1780
+ return (relevance.sourceId === undefined || relevance.sourceId === sourceId)
1781
+ && (relevance.pluginName === undefined || relevance.pluginName === pluginName)
1782
+ && relevance.platform === platform
1783
+ && relevance.scope === scope
1784
+ && error.location.replaceAll('\\', '/') === location;
1785
+ });
1786
+ };
1787
+ const isExpectedSkillMarkerError = (error) => {
1788
+ if (error.kind !== 'ownerMarkerRead'
1789
+ && error.kind !== 'ownerMarkerValidation')
1790
+ return false;
1791
+ if (exactSkillMarkerErrorLocations === undefined)
1792
+ return true;
1793
+ const relevance = error.relevance;
1794
+ if (relevance.global || relevance.platform === undefined || relevance.scope === undefined) {
1795
+ return false;
1796
+ }
1797
+ return exactSkillMarkerErrorLocations.has([
1798
+ relevance.platform,
1799
+ relevance.scope,
1800
+ error.location.replaceAll('\\', '/'),
1801
+ ].join('\0'));
1802
+ };
1803
+ assertRebuildCatalogSourceInventoryComplete(options.command === 'reinstall'
1804
+ ? {
1805
+ ...inventory,
1806
+ errors: inventory.errors.filter((error) => !isExpectedSkillMarkerError(error)
1807
+ && !isExpectedOwnedTextHeaderError(error)),
1808
+ }
1809
+ : inventory, options.commandOptions.scope);
1810
+ };
1811
+ const checked = options.command === 'uninstall'
1812
+ ? omitSelectedPluginConfigErrors(stateInventory, preflightTargets)
1813
+ : { inventory: stateInventory, omitted: false };
1814
+ malformedSelectedConfigPreserved ||= checked.omitted;
1815
+ if (checked.omitted)
1816
+ stateInventory.errors = checked.inventory.errors;
1817
+ assertWriteInventoryComplete(checked.inventory);
1818
+ const hasCodexSelection = executionSelections.some((selection) => selection.platform === 'codex');
1819
+ const intentBeforeByScope = hasCodexSelection || dependencyEnabled
1820
+ ? {
1821
+ user: await readPluginIntentState({
1822
+ scope: 'user', cwd: options.commandOptions.cwd, env: runtimeEnv,
1823
+ }),
1824
+ project: await readPluginIntentState({
1825
+ scope: 'project', cwd: options.commandOptions.cwd, env: runtimeEnv,
1826
+ }),
1827
+ }
1828
+ : {
1829
+ user: createEmptyPluginIntentState(),
1830
+ project: createEmptyPluginIntentState(),
1831
+ };
1832
+ const commandProjectKey = await resolveCanonicalRepoProjectKey(options.commandOptions.cwd);
1833
+ const codexScopePathsAlias = codexProjectConfigAliasesUserHome(options.commandOptions.cwd, runtimeEnv);
1834
+ const selectedLegacy = selectedLegacyLiveReferences(stateInventory.references, new Set(executionSelections.map((selection) => selection.platform)), options.commandOptions.scope);
1835
+ const legacy = options.all
1836
+ ? selectedLegacy
1837
+ : selectedLegacy.filter((reference) => executionSelections.some((selection) => selection.entry.plugin.manifest.name === reference.pluginName
1838
+ && selection.platform === reference.platform));
1839
+ if (legacy.length > 0) {
1840
+ const blockingLegacyTargets = projectLegacyTargets(legacy);
1841
+ throw namespacedPreflightError(`legacyStateUnsupported: legacy live runtime state exists at ${legacy.map((reference) => reference.location).join(', ')}.`, preflightTargets, blockingLegacyTargets);
1842
+ }
1843
+ for (const entry of closureEntries) {
1844
+ const changed = stateInventory.references.find((reference) => reference.sourceId === entry.source.sourceId
1845
+ && reference.sourceIdentity
1846
+ && reference.sourceIdentity !== entry.source.sourceIdentity);
1847
+ if (changed) {
1848
+ throw namespacedPreflightError(`sourceChanged: ${entry.source.sourceId} state at ${changed.location} has a different source identity.`, options.all
1849
+ ? preflightTargets
1850
+ : preflightTargets.filter((target) => target.pluginId === entry.pluginId));
1851
+ }
1852
+ }
1853
+ const marketplaceNamesBySource = new Map();
1854
+ for (const entry of new Map(closureEntries.map((candidate) => [
1855
+ candidate.source.sourceId,
1856
+ candidate,
1857
+ ])).values()) {
1858
+ const sourceNames = new Set(stateInventory.references.flatMap((reference) => (reference.kind === 'namespacedOwner' || reference.kind === 'namespacedMarketplace')
1859
+ && reference.sourceId === entry.source.sourceId
1860
+ && (reference.sourceIdentity === undefined
1861
+ || reference.sourceIdentity === entry.source.sourceIdentity)
1862
+ && reference.marketplaceName
1863
+ ? [reference.marketplaceName]
1864
+ : []));
1865
+ if (sourceNames.size > 1) {
1866
+ throw new Error('marketplaceIdentityUnavailable: Multiple canonical native marketplace registrations match the source.');
1867
+ }
1868
+ const persistedName = [...sourceNames][0];
1869
+ const marketplaceRoot = path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId);
1870
+ let marketplaceName = persistedName
1871
+ ?? (entry.source.sourceId === catalog.primarySourceId
1872
+ ? 'aw'
1873
+ : getNamespacedMarketplaceName(entry.source.sourceId));
1874
+ if (options.command === 'cleanup' || options.command === 'uninstall') {
1875
+ marketplaceNamesBySource.set(entry.source.sourceId, marketplaceName);
1876
+ continue;
1877
+ }
1878
+ let registrations = await inspectNativeMarketplaceRegistrations({
1879
+ marketplaceName,
1880
+ marketplaceRoot,
1881
+ cwd: options.commandOptions.cwd,
1882
+ env: runtimeEnv,
1883
+ });
1884
+ if (!persistedName
1885
+ && marketplaceName === 'aw'
1886
+ && Object.values(registrations).includes('foreign')) {
1887
+ marketplaceName = getNamespacedMarketplaceName(entry.source.sourceId);
1888
+ registrations = await inspectNativeMarketplaceRegistrations({
1889
+ marketplaceName,
1890
+ marketplaceRoot,
1891
+ cwd: options.commandOptions.cwd,
1892
+ env: runtimeEnv,
1893
+ });
1894
+ }
1895
+ const foreignPlatforms = Object.entries(registrations).flatMap(([platform, state]) => state === 'foreign' ? [platform] : []);
1896
+ if (foreignPlatforms.length > 0) {
1897
+ throw new Error(`externalOwnershipPresent: Marketplace ${marketplaceName} is foreign on ${foreignPlatforms.join(', ')}.`);
1898
+ }
1899
+ marketplaceNamesBySource.set(entry.source.sourceId, marketplaceName);
1900
+ }
1901
+ const marketplaceNameFor = (entry, ..._placement) => {
1902
+ const marketplaceName = marketplaceNamesBySource.get(entry.source.sourceId);
1903
+ if (!marketplaceName) {
1904
+ throw new Error('marketplaceIdentityUnavailable: No marketplace name was selected for the source.');
1905
+ }
1906
+ return marketplaceName;
1907
+ };
1908
+ const executionContext = {
1909
+ env: runtimeEnv,
1910
+ nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1911
+ reporter: options.runtime?.reporter ?? createNoopReporter(),
1912
+ debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1913
+ expectedCatalogConfigDigest: catalog.configDigest,
1914
+ };
1915
+ const inspection = createRebuildIntegration({
1916
+ context: executionContext,
1917
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
1918
+ });
1919
+ const selectedRootsForPlan = (plan) => [...new Map(plan.targets.flatMap((target) => target.relation === 'requested'
1920
+ ? [[target.pluginId, {
1921
+ pluginId: target.pluginId,
1922
+ platforms: plan.targets.flatMap((candidate) => candidate.relation === 'requested'
1923
+ && candidate.pluginId === target.pluginId
1924
+ ? [candidate.platform]
1925
+ : []),
1926
+ }]]
1927
+ : [])).values()];
1928
+ let plannedCodexRegistrations = new Map();
1929
+ let plannedOwnedTextTargets = new Map();
1930
+ let plannedSkillMarkerTargets = new Map();
1931
+ const exactOwnedTextTargets = (plan) => {
1932
+ const output = new Set();
1933
+ for (const target of plan.targets) {
1934
+ for (const location of plannedOwnedTextTargets.get([
1935
+ target.pluginId, target.platform, target.resolvedScope,
1936
+ ].join('\0')) ?? []) {
1937
+ output.add([
1938
+ target.pluginId.split('/', 1)[0],
1939
+ target.pluginName,
1940
+ target.platform,
1941
+ target.resolvedScope,
1942
+ location,
1943
+ ].join('\0'));
1944
+ }
1945
+ }
1946
+ return output;
1947
+ };
1948
+ const exactSkillMarkerTargets = (plan) => {
1949
+ const output = new Set();
1950
+ for (const target of plan.targets) {
1951
+ for (const location of plannedSkillMarkerTargets.get([
1952
+ target.pluginId, target.platform, target.resolvedScope,
1953
+ ].join('\0')) ?? []) {
1954
+ output.add([target.platform, target.resolvedScope, location].join('\0'));
1955
+ }
1956
+ }
1957
+ return output;
1958
+ };
1959
+ const createPlan = async (inventory, codexManager) => {
1960
+ plannedCodexRegistrations = new Map();
1961
+ plannedOwnedTextTargets = new Map();
1962
+ plannedSkillMarkerTargets = new Map();
1963
+ assertWriteInventoryComplete(inventory);
1964
+ const targets = [];
1965
+ const placementNodes = [];
1966
+ const blockingTargets = new Map();
1967
+ const actualStates = new Map();
1968
+ for (const [order, { entry, platform }] of executionSelections.entries()) {
1969
+ const relation = selectedIds.has(entry.pluginId) ? 'requested' : 'dependency';
1970
+ let resolvedScope = options.commandOptions.scope;
1971
+ const inspectScope = async (scope) => {
1972
+ const marketplaceName = marketplaceNameFor(entry, platform, scope);
1973
+ entry.plugin.marketplaceName = marketplaceName;
1974
+ const desired = await inspection.buildTarget({
1975
+ sourceId: entry.source.sourceId,
1976
+ sourceIdentity: entry.source.sourceIdentity,
1977
+ pluginId: entry.pluginId,
1978
+ marketplaceName,
1979
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
1980
+ plugin: entry.plugin,
1981
+ platform,
1982
+ scope,
1983
+ cwd: options.commandOptions.cwd,
1984
+ command: options.command,
1985
+ catalog: toCatalogResolution(entry.source),
1986
+ readOnlyInspectionPlan: true,
1987
+ });
1988
+ const targetKey = [entry.pluginId, platform, scope].join('\0');
1989
+ const ownedTextTargets = plannedOwnedTextTargets.get(targetKey) ?? new Set();
1990
+ const skillMarkerTargets = plannedSkillMarkerTargets.get(targetKey) ?? new Set();
1991
+ const rawRepoRoot = resolveRepoRootSync(options.commandOptions.cwd);
1992
+ const sensitiveRoots = [
1993
+ canonicalizePathThroughExistingAncestorSync(getCodexHome(runtimeEnv)),
1994
+ canonicalizePathThroughExistingAncestorSync(getClaudeHome(runtimeEnv)),
1995
+ canonicalizePathThroughExistingAncestorSync(rawRepoRoot),
1996
+ getCodexHome(runtimeEnv),
1997
+ getClaudeHome(runtimeEnv),
1998
+ rawRepoRoot,
1999
+ ];
2000
+ for (const component of desired.ownedComponents) {
2001
+ if (component.kind === 'owned-text-file') {
2002
+ ownedTextTargets.add(sanitizeCatalogSourceErrorMessage(component.path, runtimeEnv, sensitiveRoots).replaceAll('\\', '/'));
2003
+ }
2004
+ else if (component.kind === 'skill-directory') {
2005
+ skillMarkerTargets.add(sanitizeCatalogSourceErrorMessage(path.join(component.path, SKILL_OWNER_MARKER), runtimeEnv, sensitiveRoots).replaceAll('\\', '/'));
2006
+ }
2007
+ }
2008
+ plannedOwnedTextTargets.set(targetKey, ownedTextTargets);
2009
+ plannedSkillMarkerTargets.set(targetKey, skillMarkerTargets);
2010
+ const result = await inspectOwnedComponents({
2011
+ components: desired.ownedComponents,
2012
+ executor: inspection.driver.ownership,
2013
+ nativeProbe: inspection.driver.probeNative,
2014
+ verifyHealth: true,
2015
+ });
2016
+ if (result.state === 'error'
2017
+ || result.components.some((component) => component.state === 'error'))
2018
+ return result;
2019
+ const required = new Set(desired.desiredOwnedComponents ?? desired.ownedComponents);
2020
+ const optional = new Set(desired.optionalOwnedComponents ?? []);
2021
+ const presentRequired = [...required].every((component) => result.components.some((entry) => entry.component === component && entry.state === 'present'));
2022
+ const hasUnexpectedPresent = result.components.some((entry) => entry.state === 'present' && !required.has(entry.component) && !optional.has(entry.component));
2023
+ const hasPartial = result.components.some((entry) => entry.state === 'partial');
2024
+ const hasAnyPresent = result.components.some((entry) => entry.state === 'present');
2025
+ const hasAnyOwned = hasAnyPresent || hasPartial;
2026
+ const state = !hasAnyOwned
2027
+ ? 'absent'
2028
+ : hasPartial || hasUnexpectedPresent || !presentRequired
2029
+ ? 'partial'
2030
+ : required.size > 0
2031
+ ? 'present'
2032
+ : hasAnyPresent ? 'present' : 'absent';
2033
+ return { ...result, state };
2034
+ };
2035
+ const otherScope = resolvedScope === 'user' ? 'project' : 'user';
2036
+ const [requestedInspection, otherInspection] = await Promise.all([
2037
+ inspectScope(resolvedScope),
2038
+ platform === 'codex' && codexScopePathsAlias && resolvedScope === 'user'
2039
+ ? Promise.resolve({ state: 'absent', components: [], errors: [] })
2040
+ : inspectScope(otherScope),
2041
+ ]);
2042
+ const surface = platform === 'codex'
2043
+ ? analyzeCodexSurface(entry.plugin.manifest)
2044
+ : undefined;
2045
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceNameFor(entry, platform, resolvedScope)}`;
2046
+ const codexRegistration = platform === 'codex'
2047
+ && surface?.classification === 'native'
2048
+ ? await inspectCodexPluginRegistrationLive({
2049
+ pluginId: codexPluginId,
2050
+ cwd: options.commandOptions.cwd,
2051
+ env: runtimeEnv,
2052
+ nativeCliRunner: executionContext.nativeCliRunner,
2053
+ ...(codexManager ? { manager: codexManager } : {}),
2054
+ })
2055
+ : undefined;
2056
+ if (codexRegistration) {
2057
+ plannedCodexRegistrations.set(codexPluginId, codexRegistration);
2058
+ }
2059
+ const placementState = (inspected, scope) => {
2060
+ if (platform !== 'codex'
2061
+ || surface?.classification !== 'native'
2062
+ || options.command === 'cleanup'
2063
+ || options.command === 'uninstall')
2064
+ return inspected.state;
2065
+ if (!codexRegistration)
2066
+ return inspected.state;
2067
+ if (codexRegistration.observation.state === 'failed')
2068
+ return 'error';
2069
+ if (codexRegistration.observation.state === 'unobserved')
2070
+ return 'partial';
2071
+ if (codexRegistration.observation.value === 'absent')
2072
+ return 'absent';
2073
+ return codexRegistration.observations[scope]
2074
+ && codexRegistration.activations[scope] === true
2075
+ ? inspected.state
2076
+ : 'partial';
2077
+ };
2078
+ const requestedPlacementState = placementState(requestedInspection, options.commandOptions.scope);
2079
+ let otherPlacementState = placementState(otherInspection, otherScope);
2080
+ if (surface?.classification === 'native'
2081
+ && requestedPlacementState === 'present'
2082
+ && otherPlacementState === 'present') {
2083
+ otherPlacementState = 'absent';
2084
+ }
2085
+ actualStates.set(`${entry.pluginId}\0${platform}\0${options.commandOptions.scope}`, requestedPlacementState);
2086
+ actualStates.set(`${entry.pluginId}\0${platform}\0${otherScope}`, otherPlacementState);
2087
+ const allowedScopes = platform === 'codex'
2088
+ ? [
2089
+ ...(surface?.ok ? ['user'] : []),
2090
+ ...(surface?.ok && !codexScopePathsAlias ? ['project'] : []),
2091
+ ]
2092
+ : ['project', 'user'];
2093
+ if (allowedScopes.length === 0)
2094
+ allowedScopes.push(options.commandOptions.scope);
2095
+ const hasWiderDesiredRoot = relation === 'dependency'
2096
+ && options.commandOptions.scope === 'project'
2097
+ && intentBeforeByScope.user.roots.some((root) => root.pluginId === entry.pluginId && root.platforms.includes(platform));
2098
+ placementNodes.push({
2099
+ pluginId: entry.pluginId,
2100
+ pluginName: entry.plugin.manifest.name,
2101
+ platform,
2102
+ relation,
2103
+ depth: order,
2104
+ allowedScopes,
2105
+ ...(relation === 'dependency'
2106
+ && options.commandOptions.scope === 'project'
2107
+ && (otherPlacementState === 'present' || hasWiderDesiredRoot)
2108
+ ? {
2109
+ preferredScope: 'user',
2110
+ ...(otherPlacementState === 'present'
2111
+ ? { preferredScopeReason: 'existingWiderTarget' }
2112
+ : {}),
2113
+ }
2114
+ : {}),
2115
+ });
2116
+ let state = requestedPlacementState;
2117
+ let action = relation === 'requested'
2118
+ ? options.command === 'cleanup' || options.command === 'uninstall'
2119
+ ? 'keep'
2120
+ : options.command === 'reinstall' ? 'reinstall' : 'install'
2121
+ : options.command === 'reinstall' ? 'reinstall' : 'install';
2122
+ let errorCode;
2123
+ if (dependencyEnabled
2124
+ && platform === 'codex'
2125
+ && relation === 'requested'
2126
+ && surface?.scopeOverlap === 'aw-owned-conflict'
2127
+ && options.command !== 'sync'
2128
+ && !(options.command === 'install'
2129
+ && intentBeforeByScope[otherScope].roots.some((root) => root.pluginId === entry.pluginId && root.platforms.includes(platform)))
2130
+ && otherInspection.components.some((component) => component.state !== 'absent'
2131
+ && component.component.kind !== 'native-plugin'
2132
+ && !component.component.identity.artifactKey.startsWith('activation-')
2133
+ && !component.component.identity.artifactKey.startsWith('marketplace-'))) {
2134
+ action = 'block';
2135
+ errorCode = 'codexExtensionScopeConflict';
2136
+ }
2137
+ else if (dependencyEnabled && relation === 'requested'
2138
+ && (requestedInspection.state === 'error'
2139
+ || (requestedInspection.state === 'partial'
2140
+ && platform !== 'codex'
2141
+ && options.command !== 'reinstall'
2142
+ && options.command !== 'cleanup'))) {
2143
+ action = 'block';
2144
+ errorCode = 'dependencyNotReady';
2145
+ }
2146
+ if (relation === 'dependency'
2147
+ && requestedPlacementState === 'present'
2148
+ && otherPlacementState === 'present') {
2149
+ action = 'block';
2150
+ state = 'partial';
2151
+ errorCode = 'dependencyScopeConflict';
2152
+ }
2153
+ else if (relation === 'dependency'
2154
+ && options.commandOptions.scope === 'project'
2155
+ && otherInspection.state === 'present') {
2156
+ resolvedScope = 'user';
2157
+ state = 'present';
2158
+ }
2159
+ else if (relation === 'dependency'
2160
+ && options.commandOptions.scope === 'project'
2161
+ && (otherPlacementState === 'partial' || otherPlacementState === 'error')) {
2162
+ resolvedScope = 'user';
2163
+ state = otherPlacementState;
2164
+ if (otherPlacementState === 'error') {
2165
+ action = 'block';
2166
+ errorCode = 'dependencyNotReady';
2167
+ }
2168
+ }
2169
+ else if (relation === 'dependency'
2170
+ && options.commandOptions.scope === 'user'
2171
+ && otherPlacementState === 'error') {
2172
+ action = 'block';
2173
+ errorCode = 'dependencyNotReady';
2174
+ }
2175
+ const resolvedInspection = resolvedScope === options.commandOptions.scope
2176
+ ? requestedInspection
2177
+ : otherInspection;
2178
+ const collisionComponents = relation === 'dependency'
2179
+ ? resolvedInspection.components
2180
+ : [...requestedInspection.components, ...otherInspection.components];
2181
+ const invalidOwnerComponents = relation === 'dependency'
2182
+ ? resolvedInspection.components
2183
+ : requestedInspection.components;
2184
+ const hasForeignCollision = collisionComponents
2185
+ .some((component) => component.collision !== undefined);
2186
+ const hasInvalidExclusiveOwner = options.command === 'reinstall'
2187
+ && invalidOwnerComponents.some((component) => (component.component.kind === 'skill-directory'
2188
+ || component.component.kind === 'owned-text-file')
2189
+ && component.collision?.reason === 'unownedContent');
2190
+ state = resolvedScope === options.commandOptions.scope
2191
+ ? requestedPlacementState
2192
+ : otherPlacementState;
2193
+ if (dependencyEnabled && hasForeignCollision) {
2194
+ action = 'block';
2195
+ errorCode = hasInvalidExclusiveOwner
2196
+ ? 'exclusiveOwnerEvidenceInvalid'
2197
+ : 'artifactCollision';
2198
+ }
2199
+ if (!hasForeignCollision && !errorCode && relation === 'dependency') {
2200
+ action = state === 'present' ? 'keep' : state === 'partial' ? 'reinstall' : 'install';
2201
+ }
2202
+ if (!hasForeignCollision && relation === 'dependency'
2203
+ && resolvedInspection.state === 'error') {
2204
+ action = 'block';
2205
+ state = resolvedInspection.state;
2206
+ errorCode ??= 'dependencyNotReady';
2207
+ }
2208
+ if (entry.plugin.manifest.config?.fields
2209
+ && (action === 'install' || action === 'reinstall')) {
2210
+ const effective = resolvedScope === 'project'
2211
+ ? await resolveArtifactRenderConfig({
2212
+ sourceId: entry.source.sourceId,
2213
+ sourceIdentity: entry.source.sourceIdentity,
2214
+ pluginName: entry.plugin.manifest.name,
2215
+ manifest: entry.plugin.manifest,
2216
+ scope: 'project',
2217
+ cwd: options.commandOptions.cwd,
2218
+ env: runtimeEnv,
2219
+ })
2220
+ : await resolveEffectivePluginConfig({
2221
+ sourceId: entry.source.sourceId,
2222
+ sourceIdentity: entry.source.sourceIdentity,
2223
+ pluginName: entry.plugin.manifest.name,
2224
+ manifest: entry.plugin.manifest,
2225
+ targetScope: 'user',
2226
+ cwd: options.commandOptions.cwd,
2227
+ env: runtimeEnv,
2228
+ });
2229
+ if (effective.issues.length > 0) {
2230
+ action = 'block';
2231
+ errorCode = 'invalidConfig';
2232
+ }
2233
+ }
2234
+ if (dependencyEnabled
2235
+ && platform === 'codex'
2236
+ && resolvedScope === 'project'
2237
+ && codexScopePathsAlias) {
2238
+ action = 'block';
2239
+ errorCode = 'codexScopePathAlias';
2240
+ }
2241
+ if (dependencyEnabled && platform === 'codex' && !surface?.ok) {
2242
+ action = 'block';
2243
+ errorCode = surface?.errorCode ?? 'codexNativeKindUnsupported';
2244
+ }
2245
+ if (errorCode)
2246
+ blockingTargets.set(`${entry.pluginId}\0${platform}`, errorCode);
2247
+ targets.push({
2248
+ pluginId: entry.pluginId,
2249
+ pluginName: entry.plugin.manifest.name,
2250
+ platform,
2251
+ requestedScope: options.commandOptions.scope,
2252
+ resolvedScope,
2253
+ relation,
2254
+ requiredBy: executionSelections.filter((candidate) => candidate.platform === platform
2255
+ && candidate.entry.source.sourceId === entry.source.sourceId
2256
+ && candidate.entry.plugin.manifest.dependencies?.includes(entry.plugin.manifest.name))
2257
+ .map((candidate) => candidate.entry.pluginId).sort(),
2258
+ state,
2259
+ action,
2260
+ order,
2261
+ ...(errorCode ? { errorCode } : {}),
2262
+ });
2263
+ }
2264
+ if (dependencyEnabled) {
2265
+ const placement = createDependencyPlacementPlan({
2266
+ requestedPluginIds: selected.map((entry) => entry.pluginId),
2267
+ nodes: placementNodes,
2268
+ roots: placementNodes.flatMap((node) => selectedIds.has(node.pluginId)
2269
+ ? [{ pluginId: node.pluginId, platform: node.platform, scope: options.commandOptions.scope }]
2270
+ : []),
2271
+ dependencyEdges: closureEntries.flatMap((entry) => (entry.plugin.manifest.dependencies ?? []).flatMap((dependency) => {
2272
+ const dependencyId = formatPluginId({
2273
+ sourceId: entry.source.sourceId,
2274
+ pluginName: dependency,
2275
+ });
2276
+ return ['codex', 'claude'].flatMap((platform) => placementNodes.some((node) => node.pluginId === entry.pluginId && node.platform === platform)
2277
+ && placementNodes.some((node) => node.pluginId === dependencyId && node.platform === platform)
2278
+ ? [{ from: entry.pluginId, to: dependencyId, platform }]
2279
+ : []);
2280
+ })),
2281
+ scopeOverrides: effectiveDependencyScopeOverrides({
2282
+ state: intentBeforeByScope[options.commandOptions.scope],
2283
+ explicit: options.commandOptions
2284
+ .dependencyScopeOverrides ?? [],
2285
+ nodes: placementNodes,
2286
+ ignoredPersistedRootIds: selectedIds,
2287
+ }),
2288
+ });
2289
+ return createReconcileOperationPlan({
2290
+ placement,
2291
+ actual: placement.targets.map((target) => {
2292
+ const prior = targets.find((candidate) => candidate.pluginId === target.pluginId && candidate.platform === target.platform);
2293
+ const state = actualStates.get(`${target.pluginId}\0${target.platform}\0${target.resolvedScope}`) ?? prior.state;
2294
+ const errorCode = blockingTargets.get(`${target.pluginId}\0${target.platform}`)
2295
+ ?? prior.errorCode;
2296
+ const otherScope = target.resolvedScope === 'user' ? 'project' : 'user';
2297
+ const otherState = actualStates.get(`${target.pluginId}\0${target.platform}\0${otherScope}`);
2298
+ const movesExistingPlacement = target.relation === 'dependency'
2299
+ && target.resolvedScope === 'user'
2300
+ && (state === 'absent' || state === 'partial')
2301
+ && (otherState === 'present' || otherState === 'partial');
2302
+ const preservesWiderPlacement = target.relation === 'dependency'
2303
+ && target.resolvedScope === 'project'
2304
+ && (state === 'absent' || state === 'partial')
2305
+ && (otherState === 'present' || otherState === 'partial');
2306
+ const action = errorCode
2307
+ ? 'block'
2308
+ : options.command === 'cleanup' || options.command === 'uninstall'
2309
+ ? 'keep'
2310
+ : movesExistingPlacement
2311
+ ? 'move'
2312
+ : target.relation === 'dependency' && state === 'present'
2313
+ ? 'keep'
2314
+ : state === 'partial' || options.command === 'reinstall'
2315
+ ? 'reinstall'
2316
+ : 'install';
2317
+ return {
2318
+ ...prior,
2319
+ state,
2320
+ action,
2321
+ ...(errorCode ? { errorCode } : {}),
2322
+ ...(preservesWiderPlacement ? { preservedScope: 'user' } : {}),
2323
+ };
2324
+ }),
2325
+ });
2326
+ }
2327
+ const directPlacement = createDependencyPlacementPlan({
2328
+ requestedPluginIds: selected.map((entry) => entry.pluginId),
2329
+ nodes: placementNodes,
2330
+ roots: placementNodes.map((node) => ({
2331
+ pluginId: node.pluginId,
2332
+ platform: node.platform,
2333
+ scope: options.commandOptions.scope,
2334
+ })),
2335
+ dependencyEdges: [],
2336
+ });
2337
+ return createReconcileOperationPlan({ placement: directPlacement, actual: targets });
2338
+ };
2339
+ const createCodexPlans = async (plan, activationSupport = 'observed', codexManager) => {
2340
+ const codexTargets = plan.targets.filter((target) => target.platform === 'codex');
2341
+ const selectedRoots = selectedRootsForPlan(plan);
2342
+ const currentScope = options.commandOptions.scope;
2343
+ const otherScope = currentScope === 'user' ? 'project' : 'user';
2344
+ const reconciledCodexTargets = codexTargets;
2345
+ if (reconciledCodexTargets.length === 0)
2346
+ return [];
2347
+ const beforeByScope = intentBeforeByScope;
2348
+ const afterByScope = {
2349
+ user: beforeByScope.user,
2350
+ project: beforeByScope.project,
2351
+ };
2352
+ afterByScope[currentScope] = deriveDesiredIntentState({
2353
+ command: options.command,
2354
+ state: beforeByScope[currentScope],
2355
+ selectedRoots,
2356
+ plan,
2357
+ catalogEntries: intentCatalogEntries,
2358
+ });
2359
+ if (options.command === 'install') {
2360
+ afterByScope[otherScope] = removeSelectedRootPlatformsFromOtherScope({
2361
+ state: afterByScope[otherScope],
2362
+ selectedRoots,
2363
+ preserveCodexRootIds: preserveOtherCodexScopeRootIds,
2364
+ });
2365
+ }
2366
+ const desiredScopesForEntry = (entry) => {
2367
+ const scopes = new Set();
2368
+ for (const intentScope of ['user', 'project']) {
2369
+ const intent = afterByScope[intentScope];
2370
+ for (const root of intent.roots.filter((candidate) => candidate.platforms.includes('codex'))) {
2371
+ const rootEntry = availableEntries.find((candidate) => candidate.pluginId === root.pluginId);
2372
+ if (!rootEntry || rootEntry.source.sourceId !== entry.source.sourceId)
2373
+ continue;
2374
+ let closure;
2375
+ try {
2376
+ closure = entryClosure([rootEntry]);
2377
+ }
2378
+ catch {
2379
+ closure = [rootEntry];
2380
+ }
2381
+ if (!closure.some((candidate) => candidate.pluginId === entry.pluginId)) {
2382
+ continue;
2383
+ }
2384
+ const currentPlannedScope = intentScope === currentScope
2385
+ && selectedRoots.some((selectedRoot) => selectedRoot.pluginId === root.pluginId)
2386
+ ? plan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === 'codex')?.resolvedScope
2387
+ : undefined;
2388
+ const override = intent.dependencyScopeOverrides.find((candidate) => candidate.pluginId === entry.pluginId && candidate.platform === 'codex');
2389
+ const reusedUserDependency = intentScope === 'project'
2390
+ && root.pluginId !== entry.pluginId
2391
+ && scopes.has('user');
2392
+ scopes.add(currentPlannedScope
2393
+ ?? override?.scope
2394
+ ?? (reusedUserDependency ? 'user' : intentScope));
2395
+ }
2396
+ }
2397
+ return [...scopes].sort();
2398
+ };
2399
+ const intentScopesForEntry = (entry) => {
2400
+ const scopes = new Set();
2401
+ for (const intentScope of ['user', 'project']) {
2402
+ for (const root of afterByScope[intentScope].roots.filter((candidate) => candidate.platforms.includes('codex'))) {
2403
+ const rootEntry = availableEntries.find((candidate) => candidate.pluginId === root.pluginId);
2404
+ if (!rootEntry || rootEntry.source.sourceId !== entry.source.sourceId)
2405
+ continue;
2406
+ let closure;
2407
+ try {
2408
+ closure = entryClosure([rootEntry]);
2409
+ }
2410
+ catch {
2411
+ closure = [rootEntry];
2412
+ }
2413
+ if (closure.some((candidate) => candidate.pluginId === entry.pluginId)) {
2414
+ scopes.add(intentScope);
2415
+ }
2416
+ }
2417
+ }
2418
+ return [...scopes].sort();
2419
+ };
2420
+ const inspectBothScopes = options.command !== 'cleanup' && options.command !== 'uninstall';
2421
+ const readActivationConfig = async (scope) => {
2422
+ return readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, scope, runtimeEnv).configTomlPath);
2423
+ };
2424
+ const [userConfig, projectConfig] = await Promise.all([
2425
+ readActivationConfig('user'),
2426
+ readActivationConfig('project'),
2427
+ ]);
2428
+ const catalogClosure = [];
2429
+ const liveEvidence = [];
2430
+ for (const planned of reconciledCodexTargets) {
2431
+ const entry = available.find((candidate) => candidate.pluginId === planned.pluginId);
2432
+ const surface = analyzeCodexSurface(entry.plugin.manifest);
2433
+ const marketplaceName = marketplaceNameFor(entry, 'codex', planned.resolvedScope);
2434
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceName}`;
2435
+ const hasNativeRenderTargets = (entry.plugin.manifest.artifacts?.targets ?? []).some((target) => target.platform === 'codex'
2436
+ && target.target === 'codex.skill'
2437
+ && target.delivery?.mode === 'codex-native');
2438
+ const staticContentDigest = hasNativeRenderTargets
2439
+ ? undefined
2440
+ : await calculateCodexSharedNativePayloadDigest(entry.plugin.rootDir);
2441
+ const desiredScopes = desiredScopesForEntry(entry);
2442
+ const sharedInspection = surface.nativeKinds.length > 0
2443
+ ? await inspectCodexSharedNativeLive({
2444
+ pluginName: entry.plugin.manifest.name,
2445
+ sourceId: entry.source.sourceId,
2446
+ desiredVersion: entry.plugin.manifest.version,
2447
+ ...(staticContentDigest ? { expectedPayloadDigest: staticContentDigest } : {}),
2448
+ cwd: options.commandOptions.cwd,
2449
+ env: runtimeEnv,
2450
+ marketplaceName,
2451
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
2452
+ nativeCliRunner: executionContext.nativeCliRunner,
2453
+ ...(codexManager ? { manager: codexManager } : {}),
2454
+ ...(plannedCodexRegistrations.get(codexPluginId)
2455
+ ? { pluginRegistration: plannedCodexRegistrations.get(codexPluginId) }
2456
+ : {}),
2457
+ ...(hasNativeRenderTargets ? { nativeRenderPlugin: entry.plugin } : {}),
2458
+ })
2459
+ : undefined;
2460
+ const contentDigest = sharedInspection?.expectedPayloadDigest
2461
+ ?? staticContentDigest
2462
+ ?? await calculateCodexNativeRenderPayloadDigest(entry.plugin.rootDir);
2463
+ catalogClosure.push({
2464
+ pluginId: entry.pluginId,
2465
+ pluginName: entry.plugin.manifest.name,
2466
+ codexPluginId,
2467
+ version: entry.plugin.manifest.version,
2468
+ payloadDigest: contentDigest,
2469
+ relation: planned.relation,
2470
+ resolvedScope: planned.resolvedScope,
2471
+ desiredScopes,
2472
+ intentScopes: intentScopesForEntry(entry),
2473
+ surface,
2474
+ });
2475
+ const sharedNative = sharedInspection?.sharedNative
2476
+ ?? { state: 'unobserved' };
2477
+ const projectionEntries = [];
2478
+ const extensions = [];
2479
+ for (const scope of (inspectBothScopes
2480
+ ? ['user', 'project']
2481
+ : [currentScope])) {
2482
+ const desired = await inspection.buildTarget({
2483
+ sourceId: entry.source.sourceId,
2484
+ sourceIdentity: entry.source.sourceIdentity,
2485
+ pluginId: entry.pluginId,
2486
+ marketplaceName,
2487
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
2488
+ plugin: entry.plugin,
2489
+ platform: 'codex',
2490
+ scope,
2491
+ cwd: options.commandOptions.cwd,
2492
+ command: options.command,
2493
+ catalog: toCatalogResolution(entry.source),
2494
+ readOnlyInspectionPlan: true,
2495
+ });
2496
+ const inspected = await inspectOwnedComponents({
2497
+ components: desired.ownedComponents,
2498
+ executor: inspection.driver.ownership,
2499
+ nativeProbe: inspection.driver.probeNative,
2500
+ verifyHealth: true,
2501
+ });
2502
+ const projected = inspected.components.filter((component) => component.component.kind !== 'native-plugin'
2503
+ && !component.component.identity.artifactKey.startsWith('activation-')
2504
+ && !component.component.identity.artifactKey.startsWith('marketplace-'));
2505
+ projectionEntries.push(...projected.map((component) => ({
2506
+ scope,
2507
+ artifactKey: component.component.identity.artifactKey,
2508
+ state: component.state,
2509
+ collision: component.collision?.reason,
2510
+ })));
2511
+ for (const component of surface.components.filter((candidate) => candidate.owner === 'aw')) {
2512
+ const artifactKeys = new Set();
2513
+ if (component.key.startsWith('extensions.instructions:')) {
2514
+ artifactKeys.add('developer-instructions');
2515
+ }
2516
+ else if ((component.key.startsWith('extensions.permissions:')
2517
+ || component.key.startsWith('extensions.agents:')) && component.name) {
2518
+ artifactKeys.add(buildGeneratedName(entry.plugin.manifest.name, component.name));
2519
+ }
2520
+ else if (component.key.startsWith('artifacts.targets:')) {
2521
+ const targetId = component.key.slice('artifacts.targets:'.length);
2522
+ const target = entry.plugin.manifest.artifacts?.targets?.find((candidate) => candidate.id === targetId);
2523
+ const artifactKey = target?.target === 'codex.developerInstructionsBlock'
2524
+ ? 'developer-instructions'
2525
+ : target?.skillKey ?? target?.roleKey;
2526
+ if (artifactKey)
2527
+ artifactKeys.add(artifactKey);
2528
+ }
2529
+ const componentInspections = projected.filter((candidate) => artifactKeys.has(candidate.component.identity.artifactKey)
2530
+ || (candidate.component.kind === 'shared-config'
2531
+ && candidate.component.locator.kind === 'codex-role-registration'
2532
+ && artifactKeys.has(candidate.component.locator.roleKey)));
2533
+ const projectionState = componentInspections.some((candidate) => candidate.collision)
2534
+ ? planned.action === 'block' ? 'absent' : 'foreign'
2535
+ : componentInspections.length > 0
2536
+ && componentInspections.every((candidate) => candidate.state === 'present')
2537
+ ? 'present'
2538
+ : componentInspections.some((candidate) => candidate.state !== 'absent')
2539
+ ? 'drifted'
2540
+ : 'absent';
2541
+ extensions.push({ componentKey: component.key, scope, state: projectionState });
2542
+ }
2543
+ }
2544
+ liveEvidence.push({
2545
+ codexPluginId,
2546
+ sharedNative,
2547
+ ...(sharedInspection?.actualPayloadDigest
2548
+ ? { sharedNativePayloadDigest: sharedInspection.actualPayloadDigest }
2549
+ : {}),
2550
+ ...(sharedInspection?.payloadPath
2551
+ ? { sharedNativePayloadPath: sharedInspection.payloadPath }
2552
+ : {}),
2553
+ activations: {
2554
+ user: sharedInspection?.activations.user
2555
+ ?? userConfig?.snapshot.plugins[codexPluginId]?.enabled,
2556
+ project: projectConfig?.snapshot.plugins[codexPluginId]?.enabled,
2557
+ },
2558
+ extensions,
2559
+ projectionDigest: JSON.stringify(projectionEntries.sort((left, right) => `${left.scope}\0${left.artifactKey}`.localeCompare(`${right.scope}\0${right.artifactKey}`, 'en'))),
2560
+ });
2561
+ }
2562
+ const codexPlan = createCodexReconcilePlan({
2563
+ scope: currentScope,
2564
+ cwd: options.commandOptions.cwd,
2565
+ operation: options.command,
2566
+ intentBefore: beforeByScope[currentScope],
2567
+ intentAfter: afterByScope[currentScope],
2568
+ relatedIntentBefore: beforeByScope[otherScope],
2569
+ relatedIntentAfter: afterByScope[otherScope],
2570
+ catalogClosure,
2571
+ liveEvidence,
2572
+ activationSupport,
2573
+ });
2574
+ const preservedScopes = new Map(plan.targets.flatMap((target) => target.platform === 'codex' && target.preservedScope
2575
+ ? [[target.pluginId, target.preservedScope]]
2576
+ : []));
2577
+ if (preservedScopes.size === 0)
2578
+ return [codexPlan];
2579
+ const preservedNativePluginIds = new Set(catalogClosure.flatMap((target) => (options.command === 'install' || options.command === 'reinstall')
2580
+ && preservedScopes.get(target.pluginId) === 'user'
2581
+ && liveEvidence.find((evidence) => evidence.codexPluginId === target.codexPluginId)?.activations.user === true
2582
+ ? [target.pluginId]
2583
+ : []));
2584
+ const preservedCodexIds = new Set(codexPlan.sharedNativeActions.flatMap((action) => preservedNativePluginIds.has(action.pluginId) ? [action.codexPluginId] : []));
2585
+ return [{
2586
+ ...codexPlan,
2587
+ desiredTargets: codexPlan.desiredTargets.map((target) => {
2588
+ const preservedScope = preservedNativePluginIds.has(target.pluginId)
2589
+ ? preservedScopes.get(target.pluginId)
2590
+ : undefined;
2591
+ return preservedScope
2592
+ ? { ...target, scopes: [...new Set([...target.scopes, preservedScope])].sort() }
2593
+ : target;
2594
+ }),
2595
+ activationActions: {
2596
+ user: codexPlan.activationActions.user.map((action) => preservedNativePluginIds.has(action.pluginId)
2597
+ ? { ...action, action: 'none', desired: true }
2598
+ : action),
2599
+ project: codexPlan.activationActions.project,
2600
+ },
2601
+ extensionRemovals: codexPlan.extensionRemovals.filter((action) => preservedScopes.get(action.pluginId) !== action.scope),
2602
+ staleActivationRemovals: codexPlan.staleActivationRemovals.filter((action) => !(action.scope === 'user' && preservedCodexIds.has(action.codexPluginId))),
2603
+ postcheck: {
2604
+ ...codexPlan.postcheck,
2605
+ plugins: codexPlan.postcheck.plugins.map((plugin) => preservedNativePluginIds.has(plugin.pluginId)
2606
+ ? { ...plugin, expectedUserEnabled: true }
2607
+ : plugin),
2608
+ },
2609
+ }];
2610
+ };
2611
+ const operationNeedsNativeCli = async (platform, plan) => {
2612
+ for (const { entry, platform: selectedPlatform } of executionSelections) {
2613
+ if (selectedPlatform !== platform)
2614
+ continue;
2615
+ const planned = plan?.targets.find((target) => target.pluginId === entry.pluginId && target.platform === selectedPlatform);
2616
+ if (dependencyEnabled && (!planned
2617
+ || planned.action === 'keep'
2618
+ || planned.action === 'block'))
2619
+ continue;
2620
+ const scope = planned?.resolvedScope ?? options.commandOptions.scope;
2621
+ const marketplaceName = marketplaceNameFor(entry, selectedPlatform, scope);
2622
+ entry.plugin.marketplaceName = marketplaceName;
2623
+ const marketplaceRoot = path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId);
2624
+ if (!dependencyEnabled && selectedPlatform === 'codex') {
2625
+ continue;
2626
+ }
2627
+ const target = await inspection.buildTarget({
2628
+ sourceId: entry.source.sourceId,
2629
+ sourceIdentity: entry.source.sourceIdentity,
2630
+ pluginId: entry.pluginId,
2631
+ marketplaceName,
2632
+ marketplaceRoot,
2633
+ plugin: entry.plugin,
2634
+ platform: selectedPlatform,
2635
+ scope,
2636
+ cwd: options.commandOptions.cwd,
2637
+ command: dependencyEnabled
2638
+ ? planned.action === 'move'
2639
+ ? planned.state === 'partial' ? 'reinstall' : 'install'
2640
+ : planned.action
2641
+ : options.command,
2642
+ catalog: toCatalogResolution(entry.source),
2643
+ readOnlyInspectionPlan: true,
2644
+ });
2645
+ if (dependencyEnabled && (target.writeNativeRequired ?? target.requiresNative))
2646
+ return true;
2647
+ const nativeComponents = target.ownedComponents.filter((component) => component.kind === 'native-plugin'
2648
+ || (component.kind === 'shared-config'
2649
+ && component.identity.artifactKey.startsWith('marketplace-')));
2650
+ if (nativeComponents.length === 0)
2651
+ continue;
2652
+ const nativeInspection = await inspectOwnedComponents({
2653
+ components: nativeComponents,
2654
+ executor: inspection.driver.ownership,
2655
+ nativeProbe: inspection.driver.probeNative,
2656
+ });
2657
+ if (nativeInspection.components.some((component) => component.state === 'present' || component.state === 'partial')) {
2658
+ return true;
2659
+ }
2660
+ }
2661
+ return false;
2662
+ };
2663
+ const prepareRenderedTargets = async (plan, codexPlans, runTargetHook) => {
2664
+ const integration = createRebuildIntegration({
2665
+ context: executionContext,
2666
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
2667
+ });
2668
+ const preparedTargets = new Map();
2669
+ const preparedTargetErrors = new Map();
2670
+ const collisionSelections = [];
2671
+ for (const { entry, platform } of executionSelections) {
2672
+ const planned = plan.targets.find((candidate) => candidate.pluginId === entry.pluginId && candidate.platform === platform);
2673
+ if (planned.action === 'block')
2674
+ continue;
2675
+ const desiredCleanup = platform === 'codex'
2676
+ && options.command === 'cleanup'
2677
+ && codexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.includes(planned.resolvedScope)));
2678
+ const reconcileObsolete = platform === 'codex'
2679
+ && options.command === 'sync'
2680
+ && codexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && !target.scopes.includes(planned.resolvedScope)));
2681
+ const buildCommand = reconcileObsolete
2682
+ ? 'cleanup'
2683
+ : desiredCleanup || planned.action === 'move'
2684
+ ? planned.state === 'partial' ? 'reinstall' : 'install'
2685
+ : planned.relation === 'dependency'
2686
+ && (planned.action === 'install' || planned.action === 'reinstall')
2687
+ ? planned.action
2688
+ : options.command;
2689
+ const plannedNative = platform === 'codex'
2690
+ ? codexPlans.flatMap((codexPlan) => codexPlan.sharedNativeActions).find((action) => action.pluginId === entry.pluginId)
2691
+ : undefined;
2692
+ const preserveSharedNative = plannedNative?.action === 'reuse'
2693
+ || plannedNative?.action === 'none';
2694
+ const marketplaceName = marketplaceNameFor(entry, platform, planned.resolvedScope);
2695
+ try {
2696
+ const target = await measureCommandPerformanceStage(options.runtime, 'target.build', () => integration.buildTarget({
2697
+ sourceId: entry.source.sourceId,
2698
+ sourceIdentity: entry.source.sourceIdentity,
2699
+ pluginId: entry.pluginId,
2700
+ marketplaceName,
2701
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
2702
+ plugin: entry.plugin,
2703
+ platform,
2704
+ scope: planned.resolvedScope,
2705
+ cwd: options.commandOptions.cwd,
2706
+ command: buildCommand,
2707
+ catalog: toCatalogResolution(entry.source),
2708
+ preserveCodexSharedNativePayload: platform === 'codex'
2709
+ && (buildCommand === 'cleanup'
2710
+ || buildCommand === 'uninstall'
2711
+ || preserveSharedNative),
2712
+ }));
2713
+ if (plannedNative) {
2714
+ const writesSharedNative = plannedNative.action === 'install'
2715
+ || plannedNative.action === 'replace'
2716
+ || plannedNative.action === 'repair';
2717
+ const isActivationComponent = (component) => component.kind === 'shared-config'
2718
+ && component.locator.kind === 'codex-activation';
2719
+ const activationArtifactKeys = new Set(target.ownedComponents
2720
+ .filter(isActivationComponent)
2721
+ .map((component) => component.identity.artifactKey));
2722
+ target.ownedComponents = target.ownedComponents.filter((component) => !isActivationComponent(component));
2723
+ target.desiredOwnedComponents = target.desiredOwnedComponents?.filter((component) => !isActivationComponent(component));
2724
+ target.optionalOwnedComponents = target.optionalOwnedComponents?.filter((component) => !isActivationComponent(component));
2725
+ target.artifacts = target.artifacts.filter((artifact) => artifact.artifactKey === undefined
2726
+ || !activationArtifactKeys.has(artifact.artifactKey));
2727
+ target.operationActions = target.operationActions?.filter((action) => action.kind !== 'setActivation');
2728
+ target.writeNativeRequired = writesSharedNative;
2729
+ const hasExtensionMutation = codexPlans.some((codexPlan) => [
2730
+ ...codexPlan.extensionWrites,
2731
+ ...codexPlan.extensionRemovals,
2732
+ ].some((action) => action.pluginId === entry.pluginId
2733
+ && action.scope === planned.resolvedScope));
2734
+ target.codexNativeObservationExpectation = preserveSharedNative && hasExtensionMutation
2735
+ ? (plannedNative.action === 'reuse' ? 'installed' : 'observed')
2736
+ : undefined;
2737
+ if (!writesSharedNative) {
2738
+ const isSharedNativeComponent = (component) => component.kind === 'native-plugin'
2739
+ || (component.kind === 'shared-config'
2740
+ && component.identity.artifactKey.startsWith('marketplace-'));
2741
+ target.requiresNative = false;
2742
+ target.ownedComponents = target.ownedComponents.filter((component) => !isSharedNativeComponent(component));
2743
+ target.desiredOwnedComponents = target.desiredOwnedComponents?.filter((component) => !isSharedNativeComponent(component));
2744
+ target.optionalOwnedComponents = target.optionalOwnedComponents?.filter((component) => !isSharedNativeComponent(component));
2745
+ target.operationActions = target.operationActions?.filter((action) => action.kind !== 'ensureNativeRegistration');
2746
+ }
2747
+ }
2748
+ preparedTargets.set(`${entry.pluginId}\0${platform}`, {
2749
+ target,
2750
+ });
2751
+ if (runTargetHook) {
2752
+ await rebuildCommandTestHooks?.afterTargetBuilt?.({
2753
+ command: options.command,
2754
+ identity: target.identity,
2755
+ });
2756
+ }
2757
+ collisionSelections.push({
2758
+ pluginId: entry.pluginId,
2759
+ targetKey: `${platform}/${target.identity.scope}`,
2760
+ artifactKeys: (target.desiredOwnedComponents ?? target.ownedComponents)
2761
+ .filter((component) => !(component.kind === 'shared-config'
2762
+ && component.identity.artifactKey.startsWith('marketplace-')))
2763
+ .map((component) => 'path' in component
2764
+ ? path.resolve(component.path)
2765
+ : component.kind === 'native-plugin'
2766
+ ? `native:${component.pluginId}`
2767
+ : `shared:${component.label}`),
2768
+ });
2769
+ }
2770
+ catch (error) {
2771
+ preparedTargetErrors.set(`${entry.pluginId}\0${platform}`, error);
2772
+ }
2773
+ }
2774
+ const collisions = preflightDesiredArtifactCollisions(collisionSelections);
2775
+ if (collisions.length > 0) {
2776
+ throw namespacedPreflightError(`artifactCollision: ${collisions.map((collision) => `${collision.targetKey} (${collision.pluginIds.join(', ')})`).join('; ')}`, preflightTargets);
2777
+ }
2778
+ const configTargets = new Map();
2779
+ if (options.command !== 'cleanup' && options.command !== 'uninstall') {
2780
+ for (const { entry, platform } of executionSelections) {
2781
+ const planned = plan.targets.find((candidate) => candidate.pluginId === entry.pluginId && candidate.platform === platform);
2782
+ if (!entry.plugin.manifest.config?.fields
2783
+ || planned.action === 'block'
2784
+ || !preparedTargets.has(`${entry.pluginId}\0${platform}`))
2785
+ continue;
2786
+ configTargets.set(`${entry.pluginId}\0${planned.resolvedScope}`, {
2787
+ entry,
2788
+ scope: planned.resolvedScope,
2789
+ });
2790
+ }
2791
+ }
2792
+ const configDigests = await Promise.all([...configTargets.values()].map(async ({ entry, scope, }) => {
2793
+ const effective = await resolveEffectivePluginConfig({
2794
+ sourceId: entry.source.sourceId,
2795
+ sourceIdentity: entry.source.sourceIdentity,
2796
+ pluginName: entry.plugin.manifest.name,
2797
+ manifest: entry.plugin.manifest,
2798
+ targetScope: scope,
2799
+ cwd: options.commandOptions.cwd,
2800
+ env: runtimeEnv,
2801
+ });
2802
+ return createOperationPlanConfigDigest({
2803
+ pluginId: entry.pluginId,
2804
+ scope,
2805
+ effectiveValues: effective.values,
2806
+ });
2807
+ }));
2808
+ return {
2809
+ preparedTargets,
2810
+ preparedTargetErrors,
2811
+ targetActions: [...preparedTargets.values()].flatMap((prepared) => prepared.target.operationActions ?? []),
2812
+ configDigests,
2813
+ };
2814
+ };
2815
+ if (options.runtime?.previewOperationPlan) {
2816
+ await options.runtime.onPreviewStage?.('dependencyPlan');
2817
+ const initialPlan = await measureCommandPerformanceStage(options.runtime, 'dependency.plan', () => createPlan(stateInventory));
2818
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(initialPlan), exactSkillMarkerTargets(initialPlan));
2819
+ const provisionalCodexPlans = await createCodexPlans(initialPlan);
2820
+ const previewNeedsNativeMaterialization = provisionalCodexPlans.some((plan) => plan.sharedNativeActions.some((action) => action.action === 'install'
2821
+ || action.action === 'replace'
2822
+ || action.action === 'repair'));
2823
+ const previewActivationOnly = !previewNeedsNativeMaterialization
2824
+ && provisionalCodexPlans.some((plan) => plan.postcheck.support === 'observed'
2825
+ && plan.postcheck.plugins.length > 0);
2826
+ const previewActivationScope = provisionalCodexPlans.some((plan) => plan.requirement.requiresProjectPluginContract) ? 'project' : 'user';
2827
+ const previewConsumer = previewActivationOnly
2828
+ ? await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
2829
+ cwd: options.commandOptions.cwd,
2830
+ env: runtimeEnv,
2831
+ runner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
2832
+ operationRequirement: getCodexOperationRequirement(previewActivationScope),
2833
+ requiresNativeMaterialization: false,
2834
+ })
2835
+ : undefined;
2836
+ const initialCodexPlans = previewConsumer?.support === 'assumed'
2837
+ ? await createCodexPlans(initialPlan, 'assumed')
2838
+ : provisionalCodexPlans;
2839
+ const previewPreparation = await prepareRenderedTargets(initialPlan, initialCodexPlans, false);
2840
+ const preview = mergeRebuildCommandResults([]);
2841
+ preview.dependencyPlan = initialPlan;
2842
+ preview.operationPlan = createRebuildOperationPlan({
2843
+ command: options.command,
2844
+ scope: options.commandOptions.scope,
2845
+ dependencyPlan: initialPlan,
2846
+ codexPlans: initialCodexPlans,
2847
+ targetActions: previewPreparation.targetActions,
2848
+ catalogSources: catalog.sources.flatMap((source) => source.resolution ? [source.resolution] : []),
2849
+ intentBeforeByScope,
2850
+ configDigests: previewPreparation.configDigests,
2851
+ catalogEntries: intentCatalogEntries,
2852
+ });
2853
+ if (initialCodexPlans.length > 0)
2854
+ preview.codexReconcilePlans = initialCodexPlans;
2855
+ if (initialCodexPlans.some((plan) => plan.sharedNativeActions.some((action) => action.action === 'install'
2856
+ || action.action === 'replace'
2857
+ || action.action === 'repair'))) {
2858
+ preview.codexManagerPreview = await (options.runtime.codexManagerPreviewer ?? previewCodexManager)({
2859
+ cwd: options.commandOptions.cwd,
2860
+ env: runtimeEnv,
2861
+ runner: options.runtime?.nativeCliRunner,
2862
+ });
2863
+ }
2864
+ return preview;
2865
+ }
2866
+ for (const { entry, platform } of executionSelections.filter((selection) => selectedIds.has(selection.entry.pluginId))) {
2867
+ await options.runtime?.reviewPreflight?.({
2868
+ command: options.command,
2869
+ identity: {
2870
+ sourceId: entry.source.sourceId,
2871
+ sourceIdentity: entry.source.sourceIdentity,
2872
+ pluginId: entry.pluginId,
2873
+ pluginName: entry.plugin.manifest.name,
2874
+ pluginVersion: entry.plugin.manifest.version,
2875
+ platform,
2876
+ scope: options.commandOptions.scope,
2877
+ cwd: options.commandOptions.cwd,
2878
+ },
2879
+ configDigest: catalog.configDigest,
2880
+ ...(catalogOperation
2881
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
2882
+ : {}),
2883
+ sourceId: entry.source.sourceId,
2884
+ sourceIdentity: entry.source.sourceIdentity,
2885
+ resolvedCommit: entry.source.resolvedCommit,
2886
+ manifestDigest: entry.source.manifestDigest,
2887
+ snapshotDigest: entry.source.snapshotDigest,
2888
+ contentDigest: entry.source.manifest.entries.find((candidate) => candidate.name === entry.plugin.manifest.name)?.contentDigest,
2889
+ });
2890
+ }
2891
+ let lockedPlan = await measureCommandPerformanceStage(options.runtime, 'dependency.plan', () => createPlan(stateInventory));
2892
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
2893
+ const provisionalLockedCodexPlans = await createCodexPlans(lockedPlan);
2894
+ const codexManagerRequired = provisionalLockedCodexPlans.some((plan) => plan.sharedNativeActions.some((action) => action.action === 'install'
2895
+ || action.action === 'replace'
2896
+ || action.action === 'repair'));
2897
+ const activationOnlyOperation = !codexManagerRequired
2898
+ && provisionalLockedCodexPlans.some((plan) => plan.postcheck.support === 'observed'
2899
+ && plan.postcheck.plugins.length > 0);
2900
+ const activationScope = provisionalLockedCodexPlans.some((plan) => plan.requirement.requiresProjectPluginContract) ? 'project' : 'user';
2901
+ const activationConsumer = activationOnlyOperation
2902
+ ? await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
2903
+ cwd: options.commandOptions.cwd,
2904
+ env: runtimeEnv,
2905
+ runner: executionContext.nativeCliRunner,
2906
+ operationRequirement: getCodexOperationRequirement(activationScope),
2907
+ requiresNativeMaterialization: false,
2908
+ })
2909
+ : undefined;
2910
+ if (activationConsumer?.support === 'observed') {
2911
+ executionContext.codexManager = activationConsumer.manager;
2912
+ }
2913
+ if (codexManagerRequired) {
2914
+ const codexRequirementScope = provisionalLockedCodexPlans.some((plan) => plan.requirement.requiresProjectPluginContract)
2915
+ ? 'project'
2916
+ : 'user';
2917
+ if (options.runtime?.codexManagerResolver) {
2918
+ executionContext.codexManager = await options.runtime.codexManagerResolver({
2919
+ cwd: options.commandOptions.cwd,
2920
+ env: runtimeEnv,
2921
+ runner: executionContext.nativeCliRunner,
2922
+ authorizer: options.runtime?.managedCodexAuthorizer,
2923
+ operationRequirement: getCodexOperationRequirement(codexRequirementScope),
2924
+ });
2925
+ }
2926
+ else {
2927
+ const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
2928
+ cwd: options.commandOptions.cwd,
2929
+ env: runtimeEnv,
2930
+ runner: executionContext.nativeCliRunner,
2931
+ authorizer: options.runtime?.managedCodexAuthorizer,
2932
+ operationRequirement: getCodexOperationRequirement(codexRequirementScope),
2933
+ requiresNativeMaterialization: true,
2934
+ });
2935
+ if (consumer.support !== 'observed') {
2936
+ throw new Error('Codex native materialization requires an observed CLI consumer.');
2937
+ }
2938
+ executionContext.codexManager = consumer.manager;
2939
+ }
2940
+ }
2941
+ let lockedCodexPlans = activationConsumer?.support === 'assumed'
2942
+ ? await createCodexPlans(lockedPlan, 'assumed')
2943
+ : provisionalLockedCodexPlans;
2944
+ if (executionContext.codexManager) {
2945
+ lockedPlan = await createPlan(stateInventory, executionContext.codexManager);
2946
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
2947
+ lockedCodexPlans = await createCodexPlans(lockedPlan, 'observed', executionContext.codexManager);
2948
+ }
2949
+ const lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, true);
2950
+ const lockedOperationPlan = createRebuildOperationPlan({
2951
+ command: options.command,
2952
+ scope: options.commandOptions.scope,
2953
+ dependencyPlan: lockedPlan,
2954
+ codexPlans: lockedCodexPlans,
2955
+ targetActions: lockedPreparation.targetActions,
2956
+ catalogSources: catalog.sources.flatMap((source) => source.resolution ? [source.resolution] : []),
2957
+ intentBeforeByScope,
2958
+ configDigests: lockedPreparation.configDigests,
2959
+ catalogEntries: intentCatalogEntries,
2960
+ });
2961
+ if (options.runtime?.expectedPlanFingerprint) {
2962
+ try {
2963
+ assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, lockedOperationPlan);
2964
+ }
2965
+ catch (error) {
2966
+ throw namespacedPreflightError(error instanceof Error ? error.message : String(error), preflightTargets);
2967
+ }
2968
+ }
2969
+ if (options.runtime?.directCli
2970
+ && options.commandOptions.yes !== true
2971
+ && (options.command === 'install'
2972
+ || options.command === 'sync'
2973
+ || options.command === 'reinstall')) {
2974
+ const sharedNativeImpact = lockedCodexPlans.some((plan) => plan.review.requiresConfirmation);
2975
+ if (sharedNativeImpact) {
2976
+ throw namespacedPreflightError('sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
2977
+ }
2978
+ }
2979
+ if (options.runtime?.awHomeMutationToken) {
2980
+ const selectedRoots = selectedRootsForPlan(lockedPlan);
2981
+ await writeDesiredIntentSuperset({
2982
+ command: options.command,
2983
+ cwd: options.commandOptions.cwd,
2984
+ scope: options.commandOptions.scope,
2985
+ selectedRoots,
2986
+ plan: lockedPlan,
2987
+ codexPlan: lockedCodexPlans[0],
2988
+ preserveOtherCodexScopeRootIds,
2989
+ catalogEntries: intentCatalogEntries,
2990
+ env: runtimeEnv,
2991
+ });
2992
+ }
2993
+ for (const pluginName of new Set(lockedPlan.targets.flatMap((target) => target.platform === 'claude'
2994
+ && (target.action === 'install'
2995
+ || target.action === 'reinstall')
2996
+ ? [target.pluginName]
2997
+ : []))) {
2998
+ await assertNoClaudeUnboundNativeNameCollision({
2999
+ pluginName,
3000
+ cwd: options.commandOptions.cwd,
3001
+ env: runtimeEnv,
3002
+ });
3003
+ }
3004
+ const claudeCliRequired = await operationNeedsNativeCli('claude', lockedPlan);
3005
+ const codexUserActivationDesired = new Set(lockedCodexPlans.flatMap((plan) => plan.activationActions.user.flatMap((action) => action.desired ? [action.codexPluginId] : [])));
3006
+ executionContext.codexUserActivationDesired = codexUserActivationDesired;
3007
+ await assertClaudeVersion({
3008
+ platforms: claudeCliRequired ? ['claude'] : [],
3009
+ cwd: options.commandOptions.cwd,
3010
+ env: runtimeEnv,
3011
+ runtime: executionContext,
3012
+ });
3013
+ const preparedTargets = lockedPreparation.preparedTargets;
3014
+ const preparedTargetErrors = lockedPreparation.preparedTargetErrors;
3015
+ const removalRequiredConsumers = new Map();
3016
+ if ((options.command === 'cleanup' && !cleanupForce)
3017
+ || options.command === 'uninstall'
3018
+ || options.command === 'sync') {
3019
+ const removalStateInventory = await readInventory({
3020
+ cwd: options.commandOptions.cwd,
3021
+ env: runtimeEnv,
3022
+ });
3023
+ if (options.command !== 'cleanup') {
3024
+ const checked = options.command === 'uninstall'
3025
+ ? omitSelectedPluginConfigErrors(removalStateInventory, preflightTargets)
3026
+ : { inventory: removalStateInventory, omitted: false };
3027
+ malformedSelectedConfigPreserved ||= checked.omitted;
3028
+ assertRebuildCatalogSourceInventoryComplete(checked.inventory, options.commandOptions.scope);
3029
+ }
3030
+ const inspectConsumer = async (options_) => {
3031
+ const marketplaceName = marketplaceNameFor(options_.entry, options_.platform, options_.scope, options_.scope === 'project' ? options_.cwd : null);
3032
+ options_.entry.plugin.marketplaceName = marketplaceName;
3033
+ const consumerTarget = await inspection.buildTarget({
3034
+ sourceId: options_.entry.source.sourceId,
3035
+ sourceIdentity: options_.entry.source.sourceIdentity,
3036
+ pluginId: options_.entry.pluginId,
3037
+ marketplaceName,
3038
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', options_.entry.source.sourceId),
3039
+ plugin: options_.entry.plugin,
3040
+ platform: options_.platform,
3041
+ scope: options_.scope,
3042
+ cwd: options_.cwd,
3043
+ command: 'cleanup',
3044
+ catalog: toCatalogResolution(options_.entry.source),
3045
+ readOnlyInspectionPlan: true,
3046
+ });
3047
+ const consumerInspection = await inspectOwnedComponents({
3048
+ components: consumerTarget.ownedComponents,
3049
+ executor: inspection.driver.ownership,
3050
+ nativeProbe: inspection.driver.probeNative,
3051
+ verifyHealth: true,
3052
+ });
3053
+ return consumerInspection.state === 'absent' ? 'absent' : 'live';
3054
+ };
3055
+ const currentConsumerCandidates = [...new Map(removalStateInventory.references.flatMap((reference) => {
3056
+ if ((reference.kind !== 'namespacedOwner' && reference.kind !== 'namespacedMarketplace')
3057
+ || !reference.sourceId
3058
+ || !reference.pluginName
3059
+ || (reference.platform !== 'codex' && reference.platform !== 'claude')
3060
+ || (reference.scope !== 'user' && reference.scope !== 'project'))
3061
+ return [];
3062
+ const entry = available.find((candidate) => candidate.source.sourceId === reference.sourceId
3063
+ && (reference.sourceIdentity === undefined
3064
+ || candidate.source.sourceIdentity === reference.sourceIdentity)
3065
+ && candidate.plugin.manifest.name === reference.pluginName);
3066
+ if (!entry)
3067
+ return [];
3068
+ if (reference.scope === 'project' && !reference.projectKey) {
3069
+ throw namespacedPreflightError(`dependencyStillRequired: cannot inspect project consumer ${entry.pluginId} without its project identity.`, preflightTargets);
3070
+ }
3071
+ const key = [
3072
+ entry.pluginId,
3073
+ reference.platform,
3074
+ reference.scope,
3075
+ reference.projectKey ?? '',
3076
+ ].join('\0');
3077
+ return [[key, {
3078
+ entry,
3079
+ platform: reference.platform,
3080
+ scope: reference.scope,
3081
+ projectKey: reference.projectKey ?? null,
3082
+ }]];
3083
+ })).values()];
3084
+ for (const dependencyTarget of executionSelections) {
3085
+ const dependencyPluginName = dependencyTarget.entry.plugin.manifest.name;
3086
+ for (const consumer of currentConsumerCandidates) {
3087
+ if (consumer.entry.source.sourceId
3088
+ !== dependencyTarget.entry.source.sourceId
3089
+ || !consumer.entry.plugin.manifest.dependencies?.includes(dependencyPluginName)
3090
+ || consumer.platform !== dependencyTarget.platform
3091
+ || (consumer.scope === 'user'
3092
+ && options.commandOptions.scope !== 'user')
3093
+ || (options.commandOptions.scope === 'project'
3094
+ && consumer.scope === 'project'
3095
+ && consumer.projectKey !== commandProjectKey))
3096
+ continue;
3097
+ const plannedConsumer = executionSelections.find((selection) => selection.entry.pluginId === consumer.entry.pluginId
3098
+ && selection.platform === consumer.platform
3099
+ && lockedPlan.targets.some((target) => target.pluginId === selection.entry.pluginId
3100
+ && target.platform === selection.platform
3101
+ && target.resolvedScope === consumer.scope
3102
+ && (consumer.scope === 'user' || consumer.projectKey === commandProjectKey)));
3103
+ if (plannedConsumer) {
3104
+ const dependencyExecutionKey = `${dependencyTarget.entry.pluginId}\0${consumer.platform}`;
3105
+ const required = removalRequiredConsumers.get(dependencyExecutionKey) ?? new Set();
3106
+ required.add(`${consumer.entry.pluginId}\0${consumer.platform}`);
3107
+ removalRequiredConsumers.set(dependencyExecutionKey, required);
3108
+ continue;
3109
+ }
3110
+ try {
3111
+ if (await inspectConsumer({
3112
+ entry: consumer.entry,
3113
+ platform: consumer.platform,
3114
+ scope: consumer.scope,
3115
+ cwd: consumer.projectKey ?? options.commandOptions.cwd,
3116
+ }) !== 'absent') {
3117
+ throw new Error('consumer is present, partial, or unreadable');
3118
+ }
3119
+ }
3120
+ catch {
3121
+ throw namespacedPreflightError(`dependencyStillRequired: ${dependencyTarget.entry.pluginId} is required by ${consumer.entry.pluginId}.`, preflightTargets);
3122
+ }
3123
+ }
3124
+ }
3125
+ const orderedSelections = [];
3126
+ const remaining = new Set(executionSelections);
3127
+ while (remaining.size > 0) {
3128
+ const ready = [...remaining].filter((candidate) => ![...remaining].some((consumer) => consumer.platform === candidate.platform
3129
+ && consumer.entry.source.sourceId === candidate.entry.source.sourceId
3130
+ && consumer.entry.plugin.manifest.dependencies?.includes(candidate.entry.plugin.manifest.name)))
3131
+ .sort((left, right) => left.entry.pluginId.localeCompare(right.entry.pluginId, 'en'));
3132
+ if (ready.length === 0) {
3133
+ throw namespacedPreflightError('dependencyGraphInvalid: removal graph contains a cycle.', preflightTargets);
3134
+ }
3135
+ for (const selection of ready) {
3136
+ orderedSelections.push(selection);
3137
+ remaining.delete(selection);
3138
+ }
3139
+ }
3140
+ if (options.command === 'cleanup' || options.command === 'uninstall') {
3141
+ executionSelections = orderedSelections;
3142
+ }
3143
+ }
3144
+ const results = [];
3145
+ const deferredPostApplyTasks = [];
3146
+ const dependencyStates = new Map();
3147
+ for (const { entry, platform } of executionSelections) {
3148
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3149
+ const directDependencyIds = (entry.plugin.manifest.dependencies ?? []).map((pluginName) => formatPluginId({ sourceId: entry.source.sourceId, pluginName }));
3150
+ const failedDependency = directDependencyIds.find((pluginId) => {
3151
+ const state = dependencyStates.get(`${pluginId}\0${platform}`);
3152
+ return state === 'failed' || state === 'skipped';
3153
+ });
3154
+ const failedConsumer = [...(removalRequiredConsumers.get(`${entry.pluginId}\0${platform}`) ?? [])]
3155
+ .find((consumerKey) => {
3156
+ const state = dependencyStates.get(consumerKey);
3157
+ return state === 'failed' || state === 'skipped';
3158
+ });
3159
+ const identity = {
3160
+ sourceId: entry.source.sourceId,
3161
+ sourceIdentity: entry.source.sourceIdentity,
3162
+ pluginId: entry.pluginId,
3163
+ pluginName: entry.plugin.manifest.name,
3164
+ pluginVersion: entry.plugin.manifest.version,
3165
+ platform,
3166
+ scope: planned.resolvedScope,
3167
+ cwd: options.commandOptions.cwd,
3168
+ };
3169
+ const codexReconcilePlan = platform === 'codex'
3170
+ ? lockedCodexPlans.find((plan) => plan.desiredTargets.some((target) => target.pluginId === entry.pluginId))
3171
+ : undefined;
3172
+ const desiredInCurrentScope = codexReconcilePlan?.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.includes(planned.resolvedScope)) === true;
3173
+ const reconcileObsolete = platform === 'codex'
3174
+ && options.command === 'sync'
3175
+ && !desiredInCurrentScope;
3176
+ const desiredCleanupMutation = codexReconcilePlan !== undefined && (codexReconcilePlan.sharedNativeActions.some((action) => action.pluginId === entry.pluginId
3177
+ && (action.action === 'install'
3178
+ || action.action === 'replace'
3179
+ || action.action === 'repair'))
3180
+ || [...codexReconcilePlan.activationActions.user,
3181
+ ...codexReconcilePlan.activationActions.project].some((action) => action.pluginId === entry.pluginId && action.action !== 'none')
3182
+ || [...codexReconcilePlan.extensionWrites,
3183
+ ...codexReconcilePlan.extensionRemovals].some((action) => action.pluginId === entry.pluginId));
3184
+ const plannedNativeAction = codexReconcilePlan?.sharedNativeActions.find((action) => action.pluginId === entry.pluginId);
3185
+ const hasCodexExtensionMutation = codexReconcilePlan !== undefined
3186
+ && [...codexReconcilePlan.extensionWrites, ...codexReconcilePlan.extensionRemovals]
3187
+ .some((action) => action.pluginId === entry.pluginId);
3188
+ const reconcileActivationOnly = platform === 'codex'
3189
+ && plannedNativeAction !== undefined
3190
+ && (plannedNativeAction.action === 'reuse'
3191
+ || plannedNativeAction.action === 'none')
3192
+ && !hasCodexExtensionMutation;
3193
+ if (failedDependency || failedConsumer || planned.action === 'block') {
3194
+ const code = failedConsumer
3195
+ ? 'dependencyStillRequired'
3196
+ : failedDependency ? 'dependencyInstallFailed' : planned.errorCode ?? 'dependencyNotReady';
3197
+ const message = `${code}: ${failedConsumer
3198
+ ? `${entry.pluginId} retained because consumer ${failedConsumer.split('\0', 1)[0]} was not removed.`
3199
+ : failedDependency
3200
+ ? `${entry.pluginId} requires failed dependency ${failedDependency}.`
3201
+ : code === 'exclusiveOwnerEvidenceInvalid'
3202
+ ? `${entry.pluginId} has damaged or missing exclusive owner evidence. Run aw cleanup ${entry.pluginId} --platform ${platform} --scope ${planned.resolvedScope} --force, then run aw reinstall ${entry.pluginId} --platform ${platform} --scope ${planned.resolvedScope}.`
3203
+ : code === 'artifactCollision'
3204
+ ? `${entry.pluginId} has foreign-owned output. Remove or relocate it, then run cleanup and reinstall.`
3205
+ : code === 'dependencyNotReady'
3206
+ ? `${entry.pluginId} live state is unreadable. Run cleanup, then reinstall.`
3207
+ : `${entry.pluginId} dependency preflight is blocked.`}`;
3208
+ const result = failedResult(options.command, identity, message);
3209
+ results.push({
3210
+ platforms: [platform], plugins: [], warnings: [], rebuildResults: [result],
3211
+ targetResults: [{
3212
+ target: identity,
3213
+ action: failedDependency || failedConsumer ? 'skipped' : planned.action,
3214
+ state: 'failed',
3215
+ errorCode: code,
3216
+ message,
3217
+ ...(code === 'exclusiveOwnerEvidenceInvalid' ? {
3218
+ recoveryActions: [
3219
+ `run aw cleanup ${entry.pluginId} --platform ${platform} --scope ${planned.resolvedScope} --force`,
3220
+ `then run aw reinstall ${entry.pluginId} --platform ${platform} --scope ${planned.resolvedScope}`,
3221
+ ],
3222
+ } : {}),
3223
+ }],
3224
+ overallState: 'failed',
3225
+ receiptTargets: [{
3226
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3227
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3228
+ }],
3229
+ });
3230
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, failedDependency || failedConsumer ? 'skipped' : 'failed');
3231
+ continue;
3232
+ }
3233
+ if (reconcileActivationOnly) {
3234
+ const preparedError = preparedTargetErrors.get(`${entry.pluginId}\0${platform}`);
3235
+ if (preparedError) {
3236
+ const message = preparedError instanceof Error
3237
+ ? preparedError.message
3238
+ : String(preparedError);
3239
+ const result = failedResult(options.command, identity, message);
3240
+ results.push({
3241
+ platforms: [platform], plugins: [], warnings: [], rebuildResults: [result],
3242
+ targetResults: [{
3243
+ target: identity,
3244
+ action: planned.action,
3245
+ state: 'failed',
3246
+ errorCode: message.split(':', 1)[0] ?? 'targetPreparationFailed',
3247
+ message,
3248
+ }],
3249
+ overallState: 'failed',
3250
+ receiptTargets: [{
3251
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3252
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3253
+ }],
3254
+ });
3255
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3256
+ continue;
3257
+ }
3258
+ const warnings = [];
3259
+ const result = {
3260
+ status: 'succeeded', command: options.command, target: identity,
3261
+ actions: [{ name: 'postApply', status: 'succeeded' }],
3262
+ cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3263
+ };
3264
+ deferredPostApplyTasks.push({
3265
+ pluginId: entry.pluginId,
3266
+ run: () => runPostApplyScript({
3267
+ plugin: entry.plugin,
3268
+ platform,
3269
+ scope: planned.resolvedScope,
3270
+ cwd: options.commandOptions.cwd,
3271
+ context: executionContext,
3272
+ artifacts: [],
3273
+ components: [{
3274
+ kind: 'target',
3275
+ key: `${platform}:${planned.resolvedScope}`,
3276
+ outcome: 'unchanged',
3277
+ }],
3278
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3279
+ }),
3280
+ onFailure: (error) => {
3281
+ warnings.push(error instanceof Error ? error.message : String(error));
3282
+ result.actions[0].status = 'failed';
3283
+ },
3284
+ });
3285
+ results.push({
3286
+ platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3287
+ targetResults: [{ target: identity, action: planned.action, state: 'completed' }],
3288
+ overallState: 'succeeded',
3289
+ receiptTargets: [{
3290
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3291
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3292
+ }],
3293
+ });
3294
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'completed');
3295
+ continue;
3296
+ }
3297
+ if (platform === 'codex'
3298
+ && desiredInCurrentScope
3299
+ && (options.command === 'uninstall'
3300
+ || (options.command === 'cleanup' && !desiredCleanupMutation))) {
3301
+ const result = {
3302
+ status: 'succeeded', command: options.command, target: identity, actions: [],
3303
+ cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3304
+ };
3305
+ results.push({
3306
+ platforms: [platform], plugins: [identity.pluginName], warnings: [], rebuildResults: [result],
3307
+ targetResults: [{ target: identity, action: 'keep', state: 'completed' }],
3308
+ overallState: 'succeeded',
3309
+ receiptTargets: [{
3310
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3311
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3312
+ }],
3313
+ });
3314
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'kept');
3315
+ continue;
3316
+ }
3317
+ if (planned.action === 'keep'
3318
+ && !reconcileObsolete
3319
+ && options.command !== 'cleanup'
3320
+ && options.command !== 'uninstall') {
3321
+ const warnings = [];
3322
+ try {
3323
+ await runPostApplyScript({
3324
+ plugin: entry.plugin,
3325
+ platform,
3326
+ scope: planned.resolvedScope,
3327
+ cwd: options.commandOptions.cwd,
3328
+ context: executionContext,
3329
+ artifacts: [],
3330
+ components: [{
3331
+ kind: 'target',
3332
+ key: `${platform}:${planned.resolvedScope}`,
3333
+ outcome: 'unchanged',
3334
+ }],
3335
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3336
+ });
3337
+ }
3338
+ catch (error) {
3339
+ warnings.push(error instanceof Error ? error.message : String(error));
3340
+ }
3341
+ const result = {
3342
+ status: 'succeeded', command: options.command, target: identity,
3343
+ actions: [{ name: 'postApply', status: warnings.length > 0 ? 'failed' : 'succeeded' }],
3344
+ cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3345
+ };
3346
+ results.push({
3347
+ platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3348
+ targetResults: [{ target: identity, action: 'keep', state: 'completed' }],
3349
+ overallState: 'succeeded',
3350
+ receiptTargets: [{
3351
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3352
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3353
+ }],
3354
+ });
3355
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'kept');
3356
+ continue;
3357
+ }
3358
+ const marketplaceName = marketplaceNameFor(entry, platform, planned.resolvedScope);
3359
+ const namespacedSource = {
3360
+ sourceId: entry.source.sourceId,
3361
+ sourceIdentity: entry.source.sourceIdentity,
3362
+ marketplaceName,
3363
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3364
+ };
3365
+ entry.plugin.marketplaceName = marketplaceName;
3366
+ const reconcileDesiredCleanup = platform === 'codex'
3367
+ && options.command === 'cleanup'
3368
+ && desiredInCurrentScope;
3369
+ const executionResult = await runSingleSourceRebuildCommand({
3370
+ ...options,
3371
+ command: reconcileObsolete
3372
+ ? 'cleanup'
3373
+ : reconcileDesiredCleanup
3374
+ ? 'install'
3375
+ : planned.action === 'move'
3376
+ ? planned.state === 'partial' ? 'reinstall' : 'install'
3377
+ : planned.relation === 'dependency'
3378
+ ? planned.action
3379
+ : options.command,
3380
+ pluginNames: [entry.plugin.manifest.name],
3381
+ all: false,
3382
+ commandOptions: { ...options.commandOptions, platform, scope: planned.resolvedScope },
3383
+ runtime: {
3384
+ ...options.runtime,
3385
+ env: runtimeEnv,
3386
+ ...(executionContext.codexManager
3387
+ ? { codexManager: executionContext.codexManager }
3388
+ : {}),
3389
+ catalogResolution: toCatalogResolution(entry.source),
3390
+ resolvedCatalogPlugins: available
3391
+ .filter((candidate) => candidate.source.sourceId === entry.source.sourceId)
3392
+ .map((candidate) => candidate.plugin),
3393
+ namespacedSource,
3394
+ expectedCatalogConfigDigest: catalog.configDigest,
3395
+ resolvedReviewEvidenceByPlugin: {
3396
+ [entry.plugin.manifest.name]: {
3397
+ configDigest: catalog.configDigest,
3398
+ ...(catalogOperation
3399
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
3400
+ : {}),
3401
+ sourceId: entry.source.sourceId,
3402
+ sourceIdentity: entry.source.sourceIdentity,
3403
+ resolvedCommit: entry.source.resolvedCommit,
3404
+ manifestDigest: entry.source.manifestDigest,
3405
+ snapshotDigest: entry.source.snapshotDigest,
3406
+ contentDigest: entry.source.manifest.entries.find((candidate) => candidate.name === entry.plugin.manifest.name)?.contentDigest,
3407
+ },
3408
+ },
3409
+ },
3410
+ execution: {
3411
+ reviewCompleted: true,
3412
+ targetHookCompleted: true,
3413
+ prepared: preparedTargets.get(`${entry.pluginId}\0${platform}`),
3414
+ preparationError: preparedTargetErrors.get(`${entry.pluginId}\0${platform}`),
3415
+ ...(platform === 'codex' && lockedCodexPlans.length > 0
3416
+ ? { deferredPostApplyTasks }
3417
+ : {}),
3418
+ },
3419
+ });
3420
+ if (reconcileDesiredCleanup) {
3421
+ for (const result of executionResult.rebuildResults)
3422
+ result.command = 'cleanup';
3423
+ for (const result of executionResult.targetResults)
3424
+ result.action = 'cleanup';
3425
+ }
3426
+ results.push(executionResult);
3427
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, executionResult.overallState === 'succeeded' ? 'completed' : 'failed');
3428
+ }
3429
+ const successfulCodexPluginIds = new Set(executionSelections.flatMap(({ entry, platform }) => platform === 'codex'
3430
+ && (dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'completed'
3431
+ || dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'kept')
3432
+ ? [entry.pluginId]
3433
+ : []));
3434
+ const activationPlans = selectCodexActivationPlans(lockedCodexPlans, successfulCodexPluginIds);
3435
+ if (activationPlans.length > 0 || deferredPostApplyTasks.length > 0) {
3436
+ let failedActivationPluginIds = new Set();
3437
+ try {
3438
+ if (activationPlans.length > 0) {
3439
+ await executeCodexReconcileActivations({
3440
+ plans: activationPlans,
3441
+ cwd: options.commandOptions.cwd,
3442
+ env: runtimeEnv,
3443
+ manager: executionContext.codexManager,
3444
+ runner: executionContext.nativeCliRunner,
3445
+ });
3446
+ }
3447
+ }
3448
+ catch (error) {
3449
+ failedActivationPluginIds = failedCodexActivationPluginIds({
3450
+ error,
3451
+ plans: activationPlans,
3452
+ successfulPluginIds: successfulCodexPluginIds,
3453
+ });
3454
+ for (const pluginId of failedActivationPluginIds) {
3455
+ dependencyStates.set(`${pluginId}\0codex`, 'failed');
3456
+ }
3457
+ const message = error instanceof Error ? error.message : String(error);
3458
+ const failedSelections = executionSelections.filter((candidate) => candidate.platform === 'codex'
3459
+ && failedActivationPluginIds.has(candidate.entry.pluginId));
3460
+ if (failedSelections.length === 0)
3461
+ throw error;
3462
+ for (const selection of failedSelections) {
3463
+ const planned = lockedPlan.targets.find((target) => target.pluginId === selection.entry.pluginId && target.platform === 'codex');
3464
+ const identity = {
3465
+ sourceId: selection.entry.source.sourceId,
3466
+ sourceIdentity: selection.entry.source.sourceIdentity,
3467
+ pluginId: selection.entry.pluginId,
3468
+ pluginName: selection.entry.plugin.manifest.name,
3469
+ pluginVersion: selection.entry.plugin.manifest.version,
3470
+ platform: 'codex',
3471
+ scope: planned.resolvedScope,
3472
+ cwd: options.commandOptions.cwd,
3473
+ };
3474
+ const resultIndex = results.findIndex((candidate) => candidate.targetResults.some((target) => target.target.pluginId === identity.pluginId
3475
+ && target.target.platform === 'codex'
3476
+ && target.target.scope === identity.scope));
3477
+ if (resultIndex === -1)
3478
+ throw error;
3479
+ const existing = results[resultIndex];
3480
+ const failed = failedResult(options.command, identity, message, 'postcheck.inspect');
3481
+ results[resultIndex] = {
3482
+ ...existing,
3483
+ plugins: existing.plugins.filter((pluginName) => pluginName !== identity.pluginName),
3484
+ rebuildResults: existing.rebuildResults.map((rebuild) => rebuild.target.pluginId === identity.pluginId
3485
+ && rebuild.target.platform === 'codex'
3486
+ && rebuild.target.scope === identity.scope
3487
+ ? failed
3488
+ : rebuild),
3489
+ targetResults: existing.targetResults.map((target) => target.target.pluginId === identity.pluginId
3490
+ && target.target.platform === 'codex'
3491
+ && target.target.scope === identity.scope
3492
+ ? {
3493
+ target: identity,
3494
+ action: target.action,
3495
+ state: 'failed',
3496
+ errorCode: message.split(':', 1)[0] ?? 'codexActivationPostcheckFailed',
3497
+ message,
3498
+ }
3499
+ : target),
3500
+ overallState: 'failed',
3501
+ };
3502
+ }
3503
+ }
3504
+ for (const task of deferredPostApplyTasks) {
3505
+ if (failedActivationPluginIds.has(task.pluginId))
3506
+ continue;
3507
+ try {
3508
+ await task.run();
3509
+ }
3510
+ catch (error) {
3511
+ task.onFailure(error);
3512
+ }
3513
+ }
3514
+ }
3515
+ for (const result of results) {
3516
+ result.targetResults = result.targetResults.map((target) => {
3517
+ if (target.state !== 'completed')
3518
+ return target;
3519
+ const planned = lockedPlan.targets.find((candidate) => candidate.pluginId === target.target.pluginId
3520
+ && candidate.platform === target.target.platform
3521
+ && candidate.resolvedScope === target.target.scope);
3522
+ if (!planned?.preservedScope)
3523
+ return target;
3524
+ return {
3525
+ ...target,
3526
+ recoveryActions: [
3527
+ `run aw cleanup ${planned.pluginId} --platform ${planned.platform} --scope ${planned.preservedScope}`,
3528
+ `then run aw sync --all --platform ${planned.platform} --scope ${planned.resolvedScope}`,
3529
+ ],
3530
+ };
3531
+ });
3532
+ }
3533
+ if (options.command === 'install' || options.command === 'sync') {
3534
+ for (const { entry, platform } of executionSelections) {
3535
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3536
+ if (dependencyStates.get(`${entry.pluginId}\0${platform}`) !== 'completed')
3537
+ continue;
3538
+ const previousScope = planned.resolvedScope === 'user' ? 'project' : 'user';
3539
+ const removesPreviousScope = planned.resolvedScope === 'user'
3540
+ && (planned.action === 'move'
3541
+ || platform === 'codex' && lockedCodexPlans.some((plan) => plan.extensionRemovals.some((action) => action.pluginId === entry.pluginId && action.scope === previousScope)));
3542
+ if (!removesPreviousScope) {
3543
+ continue;
3544
+ }
3545
+ const marketplaceName = marketplaceNameFor(entry, platform, previousScope);
3546
+ entry.plugin.marketplaceName = marketplaceName;
3547
+ const migration = await runSingleSourceRebuildCommand({
3548
+ command: 'cleanup',
3549
+ pluginNames: [entry.plugin.manifest.name],
3550
+ commandOptions: { ...options.commandOptions, platform, scope: previousScope },
3551
+ runtime: {
3552
+ ...options.runtime,
3553
+ env: runtimeEnv,
3554
+ ...(executionContext.codexManager
3555
+ ? { codexManager: executionContext.codexManager }
3556
+ : {}),
3557
+ catalogResolution: toCatalogResolution(entry.source),
3558
+ resolvedCatalogPlugins: available
3559
+ .filter((candidate) => candidate.source.sourceId === entry.source.sourceId)
3560
+ .map((candidate) => candidate.plugin),
3561
+ namespacedSource: {
3562
+ sourceId: entry.source.sourceId,
3563
+ sourceIdentity: entry.source.sourceIdentity,
3564
+ marketplaceName,
3565
+ marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3566
+ },
3567
+ },
3568
+ execution: { reviewCompleted: true },
3569
+ });
3570
+ results.push({
3571
+ ...migration,
3572
+ targetResults: migration.targetResults.map((target) => ({
3573
+ ...target,
3574
+ relation: planned.relation,
3575
+ requiredBy: planned.requiredBy,
3576
+ })),
3577
+ });
3578
+ if (migration.overallState !== 'succeeded') {
3579
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3580
+ }
3581
+ }
3582
+ }
3583
+ const merged = mergeRebuildCommandResults(results);
3584
+ if (malformedSelectedConfigPreserved) {
3585
+ merged.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
3586
+ }
3587
+ merged.dependencyPlan = lockedPlan;
3588
+ merged.overallState = reconcileOverallState(merged.targetResults, lockedPlan);
3589
+ if (lockedCodexPlans.length > 0)
3590
+ merged.codexReconcilePlans = lockedCodexPlans;
3591
+ merged.operationPlan = lockedOperationPlan;
3592
+ return leaseSafeRebuildResult(merged);
3593
+ },
3594
+ });
3595
+ if (cleanupForce && !options.runtime?.previewOperationPlan) {
3596
+ try {
3597
+ await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
3598
+ }
3599
+ catch (error) {
3600
+ resolvedResult.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
3601
+ }
3602
+ }
3603
+ return resolvedResult;
3604
+ }
3605
+ catch (error) {
3606
+ if (options.all && (liveTargets.length > 0 || legacyLiveTargets.length > 0)) {
3607
+ const projected = error;
3608
+ error = namespacedPreflightError(error instanceof Error ? error.message : String(error), projected.namespacedTargets ?? liveTargets, projected.legacyTargets ?? legacyLiveTargets);
3609
+ }
3610
+ if (options.command !== 'uninstall')
3611
+ throw error;
3612
+ const message = error instanceof Error ? error.message : String(error);
3613
+ const catalogUnavailable = isOfflineCleanupCatalogGenerationError(error)
3614
+ || message.startsWith('Cannot resolve required catalog sources:')
3615
+ || message.includes('does not exist in the required catalog source set');
3616
+ if (options.command === 'uninstall' && catalogUnavailable) {
3617
+ throw namespacedPreflightError(`catalogSourceUnavailable: ${message} Restore the source to run lifecycle uninstall, or use cleanup; use cleanup --force only for exclusively owned damaged output.`, liveTargets, legacyLiveTargets);
3618
+ }
3619
+ if (catalogGenerationErrorCode(error) === 'catalogGenerationSourceMissing'
3620
+ || message.includes('does not exist in the required catalog source set')) {
3621
+ throw namespacedPreflightError(`sourceMissing: ${message} Run cleanup for owner recovery, or cleanup --force for an exclusively owned damaged target.`, liveTargets, legacyLiveTargets);
3622
+ }
3623
+ throw error;
3624
+ }
3625
+ }
3626
+ //# sourceMappingURL=rebuild-command.js.map