@lesliechan721/aw 0.1.0-beta.7 → 0.1.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/dist/authoring/scripts/add-skill.mjs +4 -4
  2. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  3. package/dist/authoring/scripts/init-plugin.mjs +8 -37
  4. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  5. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  6. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
  7. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
  8. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  9. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  10. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  11. package/dist/authoring/scripts/remove-plugin.mjs +2 -1
  12. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
  13. package/dist/authoring/scripts/validate-plugin.mjs +64 -758
  14. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  15. package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
  16. package/dist/scripts/{plugin-eval-materializer.js → aw-skill-eval-materializer.js} +34 -145
  17. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  18. package/dist/scripts/bundle-plugins.d.ts +1 -0
  19. package/dist/scripts/bundle-plugins.js +2 -1
  20. package/dist/scripts/bundle-plugins.js.map +1 -1
  21. package/dist/scripts/plugin-staging.d.ts +6 -8
  22. package/dist/scripts/plugin-staging.js +15 -28
  23. package/dist/scripts/plugin-staging.js.map +1 -1
  24. package/dist/scripts/test-plugin-scripts.js +2 -33
  25. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  26. package/dist/src/adapters/helpers.js +2 -2
  27. package/dist/src/adapters/helpers.js.map +1 -1
  28. package/dist/src/authoring/workspace-commands.d.ts +17 -1
  29. package/dist/src/authoring/workspace-commands.js +21 -4
  30. package/dist/src/authoring/workspace-commands.js.map +1 -1
  31. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  32. package/dist/src/commands/aw-capabilities.js +254 -0
  33. package/dist/src/commands/aw-capabilities.js.map +1 -0
  34. package/dist/src/commands/catalog-dev-source.d.ts +118 -0
  35. package/dist/src/commands/catalog-dev-source.js +417 -0
  36. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  37. package/dist/src/commands/catalog-generation.d.ts +13 -0
  38. package/dist/src/commands/catalog-generation.js +13 -0
  39. package/dist/src/commands/catalog-generation.js.map +1 -0
  40. package/dist/src/commands/catalog-publication-inspection.d.ts +29 -28
  41. package/dist/src/commands/catalog-publication-inspection.js +138 -50
  42. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  43. package/dist/src/commands/catalog-publication-journal.d.ts +49 -0
  44. package/dist/src/commands/catalog-publication-journal.js +147 -0
  45. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  46. package/dist/src/commands/catalog-publication-target.d.ts +18 -0
  47. package/dist/src/commands/catalog-publication-target.js +95 -0
  48. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  49. package/dist/src/commands/catalog-publish.d.ts +58 -15
  50. package/dist/src/commands/catalog-publish.js +590 -210
  51. package/dist/src/commands/catalog-publish.js.map +1 -1
  52. package/dist/src/commands/catalog-source.d.ts +12 -15
  53. package/dist/src/commands/catalog-source.js +61 -86
  54. package/dist/src/commands/catalog-source.js.map +1 -1
  55. package/dist/src/commands/catalog-status.d.ts +18 -22
  56. package/dist/src/commands/catalog-status.js +81 -115
  57. package/dist/src/commands/catalog-status.js.map +1 -1
  58. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  59. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  60. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  61. package/dist/src/commands/dependency-operation-plan.d.ts +88 -9
  62. package/dist/src/commands/dependency-operation-plan.js +331 -6
  63. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  64. package/dist/src/commands/init.d.ts +5 -11
  65. package/dist/src/commands/init.js +30 -51
  66. package/dist/src/commands/init.js.map +1 -1
  67. package/dist/src/commands/offline-cleanup-evidence.d.ts +43 -0
  68. package/dist/src/commands/offline-cleanup-evidence.js +116 -0
  69. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  70. package/dist/src/commands/options.d.ts +7 -1
  71. package/dist/src/commands/options.js +65 -6
  72. package/dist/src/commands/options.js.map +1 -1
  73. package/dist/src/commands/plugin-capabilities.js +5 -1
  74. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  75. package/dist/src/commands/rebuild-command.d.ts +27 -13
  76. package/dist/src/commands/rebuild-command.js +1472 -471
  77. package/dist/src/commands/rebuild-command.js.map +1 -1
  78. package/dist/src/commands/rebuild-integration.d.ts +63 -8
  79. package/dist/src/commands/rebuild-integration.js +761 -439
  80. package/dist/src/commands/rebuild-integration.js.map +1 -1
  81. package/dist/src/commands/rebuild-operations.d.ts +2 -8
  82. package/dist/src/commands/rebuild-operations.js +48 -132
  83. package/dist/src/commands/rebuild-operations.js.map +1 -1
  84. package/dist/src/commands/runtime.js +21 -34
  85. package/dist/src/commands/runtime.js.map +1 -1
  86. package/dist/src/commands/skill-eval.d.ts +33 -0
  87. package/dist/src/commands/skill-eval.js +363 -0
  88. package/dist/src/commands/skill-eval.js.map +1 -0
  89. package/dist/src/commands/status-v6.d.ts +202 -0
  90. package/dist/src/commands/status-v6.js +1938 -0
  91. package/dist/src/commands/status-v6.js.map +1 -0
  92. package/dist/src/commands/sync-command.d.ts +21 -0
  93. package/dist/src/commands/sync-command.js +206 -0
  94. package/dist/src/commands/sync-command.js.map +1 -0
  95. package/dist/src/commands/wizard.d.ts +2 -2
  96. package/dist/src/commands/wizard.js +2 -2
  97. package/dist/src/commands/wizard.js.map +1 -1
  98. package/dist/src/commands/write-commands.js +33 -19
  99. package/dist/src/commands/write-commands.js.map +1 -1
  100. package/dist/src/constants.d.ts +7 -1
  101. package/dist/src/constants.js +7 -1
  102. package/dist/src/constants.js.map +1 -1
  103. package/dist/src/eval/manifest-v1.d.ts +66 -0
  104. package/dist/src/eval/manifest-v1.js +326 -0
  105. package/dist/src/eval/manifest-v1.js.map +1 -0
  106. package/dist/src/generators/local-plugin.d.ts +3 -0
  107. package/dist/src/generators/local-plugin.js +167 -45
  108. package/dist/src/generators/local-plugin.js.map +1 -1
  109. package/dist/src/index.d.ts +6 -1
  110. package/dist/src/index.js +863 -126
  111. package/dist/src/index.js.map +1 -1
  112. package/dist/src/output/command-receipt.d.ts +58 -8
  113. package/dist/src/output/command-receipt.js +141 -11
  114. package/dist/src/output/command-receipt.js.map +1 -1
  115. package/dist/src/output/model.d.ts +3 -44
  116. package/dist/src/output/model.js +1 -144
  117. package/dist/src/output/model.js.map +1 -1
  118. package/dist/src/output/rebuild-progress.js +0 -3
  119. package/dist/src/output/rebuild-progress.js.map +1 -1
  120. package/dist/src/portable/commands.d.ts +6 -2
  121. package/dist/src/portable/commands.js +57 -6
  122. package/dist/src/portable/commands.js.map +1 -1
  123. package/dist/src/portable/materializer.d.ts +4 -4
  124. package/dist/src/portable/materializer.js +61 -12
  125. package/dist/src/portable/materializer.js.map +1 -1
  126. package/dist/src/tui/catalog-generation.d.ts +35 -0
  127. package/dist/src/tui/catalog-generation.js +85 -0
  128. package/dist/src/tui/catalog-generation.js.map +1 -0
  129. package/dist/src/tui/components.js +1 -2
  130. package/dist/src/tui/components.js.map +1 -1
  131. package/dist/src/tui/console-app-v4.d.ts +1 -1
  132. package/dist/src/tui/console-app-v4.js +647 -196
  133. package/dist/src/tui/console-app-v4.js.map +1 -1
  134. package/dist/src/tui/controller-runtime.d.ts +6 -2
  135. package/dist/src/tui/controller-runtime.js +646 -137
  136. package/dist/src/tui/controller-runtime.js.map +1 -1
  137. package/dist/src/tui/controller.d.ts +77 -15
  138. package/dist/src/tui/controller.js +647 -306
  139. package/dist/src/tui/controller.js.map +1 -1
  140. package/dist/src/tui/diagnostics.d.ts +1 -2
  141. package/dist/src/tui/diagnostics.js.map +1 -1
  142. package/dist/src/tui/index.d.ts +1 -0
  143. package/dist/src/tui/index.js +1 -0
  144. package/dist/src/tui/index.js.map +1 -1
  145. package/dist/src/tui/input-display.d.ts +8 -0
  146. package/dist/src/tui/input-display.js +36 -0
  147. package/dist/src/tui/input-display.js.map +1 -0
  148. package/dist/src/tui/model.d.ts +102 -14
  149. package/dist/src/tui/model.js +630 -139
  150. package/dist/src/tui/model.js.map +1 -1
  151. package/dist/src/tui/router.d.ts +35 -0
  152. package/dist/src/tui/router.js.map +1 -1
  153. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  154. package/dist/src/tui/source-ref-presets.js +11 -0
  155. package/dist/src/tui/source-ref-presets.js.map +1 -0
  156. package/dist/src/tui/views.js +31 -3
  157. package/dist/src/tui/views.js.map +1 -1
  158. package/dist/src/types.d.ts +104 -13
  159. package/dist/src/types.js.map +1 -1
  160. package/dist/src/utils/agent-version.d.ts +7 -1
  161. package/dist/src/utils/agent-version.js +41 -8
  162. package/dist/src/utils/agent-version.js.map +1 -1
  163. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  164. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  165. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  166. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  167. package/dist/src/utils/bundle-integrity.js +0 -25
  168. package/dist/src/utils/bundle-integrity.js.map +1 -1
  169. package/dist/src/utils/bundle-layout.js +8 -6
  170. package/dist/src/utils/bundle-layout.js.map +1 -1
  171. package/dist/src/utils/catalog-digests.d.ts +7 -0
  172. package/dist/src/utils/catalog-digests.js +40 -2
  173. package/dist/src/utils/catalog-digests.js.map +1 -1
  174. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  175. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  176. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  177. package/dist/src/utils/catalog-generation-error.d.ts +13 -0
  178. package/dist/src/utils/catalog-generation-error.js +28 -0
  179. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  180. package/dist/src/utils/catalog-generation-resolver.d.ts +20 -0
  181. package/dist/src/utils/catalog-generation-resolver.js +143 -0
  182. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  183. package/dist/src/utils/catalog-generation-service.d.ts +43 -0
  184. package/dist/src/utils/catalog-generation-service.js +306 -0
  185. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  186. package/dist/src/utils/catalog-generation-state.d.ts +17 -0
  187. package/dist/src/utils/catalog-generation-state.js +168 -0
  188. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  189. package/dist/src/utils/catalog-generation-store.d.ts +34 -0
  190. package/dist/src/utils/catalog-generation-store.js +251 -0
  191. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  192. package/dist/src/utils/catalog-manifest-v6.d.ts +5 -0
  193. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v6.js} +35 -43
  194. package/dist/src/utils/catalog-manifest-v6.js.map +1 -0
  195. package/dist/src/utils/catalog-operation-context.d.ts +20 -0
  196. package/dist/src/utils/catalog-operation-context.js +141 -0
  197. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  198. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  199. package/dist/src/utils/catalog-public-error.js +21 -8
  200. package/dist/src/utils/catalog-public-error.js.map +1 -1
  201. package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
  202. package/dist/src/utils/catalog-runtime-projection.js +5 -40
  203. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  204. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  205. package/dist/src/utils/catalog-snapshot.js +11 -3
  206. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  207. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  208. package/dist/src/utils/catalog-source-identity.js +21 -2
  209. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  210. package/dist/src/utils/catalog-source-references.d.ts +7 -0
  211. package/dist/src/utils/catalog-source-references.js +44 -55
  212. package/dist/src/utils/catalog-source-references.js.map +1 -1
  213. package/dist/src/utils/catalog-sources.d.ts +1 -0
  214. package/dist/src/utils/catalog-sources.js +55 -20
  215. package/dist/src/utils/catalog-sources.js.map +1 -1
  216. package/dist/src/utils/codex-config.d.ts +2 -0
  217. package/dist/src/utils/codex-config.js +26 -5
  218. package/dist/src/utils/codex-config.js.map +1 -1
  219. package/dist/src/utils/codex-manager.d.ts +81 -0
  220. package/dist/src/utils/codex-manager.js +810 -0
  221. package/dist/src/utils/codex-manager.js.map +1 -0
  222. package/dist/src/utils/codex-reconciler.d.ts +131 -0
  223. package/dist/src/utils/codex-reconciler.js +327 -0
  224. package/dist/src/utils/codex-reconciler.js.map +1 -0
  225. package/dist/src/utils/codex-shared-native.d.ts +56 -0
  226. package/dist/src/utils/codex-shared-native.js +87 -0
  227. package/dist/src/utils/codex-shared-native.js.map +1 -0
  228. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  229. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  230. package/dist/src/utils/codex-surface-analysis.d.ts +44 -0
  231. package/dist/src/utils/codex-surface-analysis.js +247 -0
  232. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  233. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  234. package/dist/src/utils/dynamic-skill-tree.js +35 -5
  235. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  236. package/dist/src/utils/git-catalog-provider.js +66 -140
  237. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  238. package/dist/src/utils/lifecycle-active-store.d.ts +0 -28
  239. package/dist/src/utils/lifecycle-active-store.js +35 -247
  240. package/dist/src/utils/lifecycle-active-store.js.map +1 -1
  241. package/dist/src/utils/manifest.d.ts +1 -3
  242. package/dist/src/utils/manifest.js +1 -3
  243. package/dist/src/utils/manifest.js.map +1 -1
  244. package/dist/src/utils/multi-source-catalog.d.ts +3 -9
  245. package/dist/src/utils/multi-source-catalog.js +53 -110
  246. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  247. package/dist/src/utils/native-cli.d.ts +49 -6
  248. package/dist/src/utils/native-cli.js +116 -33
  249. package/dist/src/utils/native-cli.js.map +1 -1
  250. package/dist/src/utils/orphan-marketplace-cleanup.js +1 -3
  251. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  252. package/dist/src/utils/ownership.d.ts +10 -11
  253. package/dist/src/utils/ownership.js +55 -112
  254. package/dist/src/utils/ownership.js.map +1 -1
  255. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  256. package/dist/src/utils/path-catalog-provider.js +52 -65
  257. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  258. package/dist/src/utils/paths.d.ts +0 -1
  259. package/dist/src/utils/paths.js +0 -1
  260. package/dist/src/utils/paths.js.map +1 -1
  261. package/dist/src/utils/plugin-config-write.js +18 -40
  262. package/dist/src/utils/plugin-config-write.js.map +1 -1
  263. package/dist/src/utils/plugin-config.d.ts +1 -12
  264. package/dist/src/utils/plugin-config.js +107 -226
  265. package/dist/src/utils/plugin-config.js.map +1 -1
  266. package/dist/src/utils/plugin-intent-state.d.ts +39 -0
  267. package/dist/src/utils/plugin-intent-state.js +325 -0
  268. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  269. package/dist/src/utils/plugin-scripts.d.ts +1 -1
  270. package/dist/src/utils/plugin-scripts.js +1 -1
  271. package/dist/src/utils/plugin-scripts.js.map +1 -1
  272. package/dist/src/utils/state-paths.js +7 -2
  273. package/dist/src/utils/state-paths.js.map +1 -1
  274. package/dist/src/utils/target-eligibility.d.ts +8 -2
  275. package/dist/src/utils/target-eligibility.js +9 -5
  276. package/dist/src/utils/target-eligibility.js.map +1 -1
  277. package/package.json +6 -4
  278. package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
  279. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  280. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  281. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  282. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  283. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  284. package/dist/scripts/codex-eval-app-server.d.ts +0 -89
  285. package/dist/scripts/codex-eval-app-server.js +0 -1228
  286. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  287. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  288. package/dist/scripts/codex-eval-isolation.js +0 -318
  289. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  290. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  291. package/dist/scripts/codex-eval-process-management.js +0 -69
  292. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  293. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  294. package/dist/scripts/codex-eval-process-tree.js +0 -803
  295. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  296. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  297. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  298. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  299. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  300. package/dist/scripts/codex-trigger-eval.js +0 -968
  301. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  302. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  303. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  304. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  305. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  306. package/dist/scripts/eval-plugin-skills.js +0 -1586
  307. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  308. package/dist/scripts/eval-run-layout.d.ts +0 -17
  309. package/dist/scripts/eval-run-layout.js +0 -26
  310. package/dist/scripts/eval-run-layout.js.map +0 -1
  311. package/dist/scripts/plugin-eval-contract.d.ts +0 -185
  312. package/dist/scripts/plugin-eval-contract.js +0 -695
  313. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  314. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  315. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  316. package/dist/scripts/skill-reporting.d.ts +0 -212
  317. package/dist/scripts/skill-reporting.js +0 -922
  318. package/dist/scripts/skill-reporting.js.map +0 -1
  319. package/dist/src/commands/status-v4.d.ts +0 -123
  320. package/dist/src/commands/status-v4.js +0 -905
  321. package/dist/src/commands/status-v4.js.map +0 -1
  322. package/dist/src/commands/status.d.ts +0 -93
  323. package/dist/src/commands/status.js +0 -442
  324. package/dist/src/commands/status.js.map +0 -1
  325. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  326. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  327. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  328. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  329. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  330. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  331. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
  332. package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
  333. package/dist/src/utils/catalog-source-cache.d.ts +0 -62
  334. package/dist/src/utils/catalog-source-cache.js +0 -296
  335. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  336. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  337. package/dist/src/utils/codex-project-surface.js +0 -49
  338. package/dist/src/utils/codex-project-surface.js.map +0 -1
  339. package/dist/src/utils/plugin-release.d.ts +0 -2
  340. package/dist/src/utils/plugin-release.js +0 -2
  341. package/dist/src/utils/plugin-release.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
- import { statusCommandV4, } from '../commands/status-v4.js';
2
+ import path from 'node:path';
3
+ import { statusCommandV6, } from '../commands/status-v6.js';
3
4
  import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
4
5
  import { cleanupWriteCommand, installWriteCommand, reinstallWriteCommand, uninstallWriteCommand, upgradeWriteCommand, } from '../commands/write-commands.js';
5
6
  import { readCliPackageInfo } from '../utils/local-marketplace.js';
@@ -7,28 +8,42 @@ import { readCatalogSourcesConfigSnapshot, } from '../utils/catalog-sources.js';
7
8
  import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
8
9
  import { coercePluginConfigUpdates, readNamespacedPluginConfigStoreIdentity, readPluginConfigStore, resolveConfigRead, } from '../utils/plugin-config.js';
9
10
  import { applyPluginConfigWrite, resetPluginConfigWithHelper, } from '../utils/plugin-config-write.js';
11
+ import { offlineCleanupEvidenceFromInventory, } from '../commands/offline-cleanup-evidence.js';
12
+ import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
10
13
  import { operationEvidenceFingerprint, TuiController, TuiOperationBlockedError, } from './controller.js';
11
- import { buildConfigGroups, buildConfigPage, projectPlatformPlacement, } from './model.js';
14
+ import { buildConfigGroups, buildConfigPage, isPureNativeCodexTarget, projectPlatformPlacement, } from './model.js';
12
15
  import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
13
- import { targetCanBeUninstalled } from '../utils/target-eligibility.js';
16
+ import { targetCanBeUninstalled, targetHasArtifactCollision, } from '../utils/target-eligibility.js';
14
17
  import { calculateCatalogSourceIdentity } from '../utils/catalog-source-identity.js';
15
- import { resolvePluginsFromCatalog } from '../utils/manifest.js';
18
+ import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.js';
16
19
  import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
20
+ import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
21
+ import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
22
+ import { loadCatalogGenerationViewModels } from './catalog-generation.js';
17
23
  import { installPortableSkills, preparePortableInstall, PortableCommandError, } from '../portable/commands.js';
24
+ import { MANAGED_CODEX_PACKAGE_NAME, MANAGED_CODEX_VERSION, } from '../utils/codex-manager.js';
25
+ import { calculatePluginIntentDigest, canonicalizePluginIntentState, readPluginIntentState, writePluginIntentState, } from '../utils/plugin-intent-state.js';
18
26
  async function loadPortablePluginIds(options) {
19
27
  return withResolvedMultiSourceCatalog({
20
28
  cliVersion: options.cliVersion,
21
- freshnessPolicy: 'cacheFirst',
22
29
  env: options.context.env,
23
30
  signal: options.signal,
31
+ ...(options.catalogOperationContext
32
+ ? { operationContext: options.catalogOperationContext }
33
+ : {}),
24
34
  consume: async (catalog) => (await Promise.all(catalog.sources.flatMap((item) => item.resolution
25
- ? [resolvePluginsFromCatalog(item.resolution.rootDir, undefined).then((plugins) => plugins
26
- .filter((plugin) => plugin.manifest.distributions?.agentSkills)
27
- .map((plugin) => formatPluginId({
28
- sourceId: item.resolution.sourceId,
29
- pluginName: plugin.manifest.name,
30
- })))]
31
- : []))).flat(),
35
+ ? item.resolution.manifest.entries.flatMap((entry) => {
36
+ const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
37
+ return support.supported
38
+ ? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => plugin.manifest.distributions?.agentSkills
39
+ ? formatPluginId({
40
+ sourceId: item.resolution.sourceId,
41
+ pluginName: entry.name,
42
+ })
43
+ : null)]
44
+ : [];
45
+ })
46
+ : []))).filter((pluginId) => pluginId !== null),
32
47
  });
33
48
  }
34
49
  let tuiControllerRuntimeTestHooks;
@@ -60,6 +75,13 @@ export function statusTargetFingerprint(targets, revisions = []) {
60
75
  .localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
61
76
  });
62
77
  }
78
+ export function configAffectedStatusTargets(targets, pluginId, configScope) {
79
+ return targets.filter((target) => target.pluginId === pluginId
80
+ && (configScope === 'user' || target.scope === 'project')
81
+ && (target.state === 'present' || target.state === 'partial'))
82
+ .sort((left, right) => `${left.platform}\0${left.scope}`
83
+ .localeCompare(`${right.platform}\0${right.scope}`, 'en'));
84
+ }
63
85
  function operationReviewStale(message) {
64
86
  return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`);
65
87
  }
@@ -73,6 +95,8 @@ function sourceEvidenceForPlugin(source, pluginName) {
73
95
  manifestDigest: source.manifestDigest,
74
96
  snapshotDigest: source.snapshotDigest,
75
97
  contentDigest: entry.contentDigest,
98
+ catalogVersion: source.manifest.catalogVersion,
99
+ channel: source.manifest.channel,
76
100
  };
77
101
  }
78
102
  function assertSourceEvidenceMatches(expected, actual) {
@@ -92,8 +116,6 @@ function throwIfWriteReviewStale(result) {
92
116
  }
93
117
  }
94
118
  const SOURCE_PROGRESS_PHASES = {
95
- refreshingCatalog: '刷新 Catalog 缓存',
96
- catalogReady: 'Catalog 已读取,索引已刷新',
97
119
  savingConfig: '保存来源配置',
98
120
  };
99
121
  function pluginIntent(intent) {
@@ -104,20 +126,48 @@ function assertIntentIsLegal(intent, targets, revisions = []) {
104
126
  ? ['codex', 'claude']
105
127
  : [intent.platform];
106
128
  for (const platform of platforms) {
107
- const selected = targets.find((target) => target.pluginId === intent.pluginId
108
- && target.platform === platform && target.scope === intent.scope);
129
+ const platformTargets = targets.filter((target) => target.pluginId === intent.pluginId
130
+ && target.platform === platform);
131
+ const selected = platformTargets.find((target) => target.scope === intent.scope)
132
+ ?? (platform === 'codex' ? platformTargets[0] : undefined);
109
133
  if (!selected || selected.errors.some((error) => error.code === 'unsupportedTarget')) {
110
134
  throw new TuiOperationBlockedError('unsupportedTarget', '所选插件不支持这个平台和使用位置。');
111
135
  }
112
- const otherScopeInstalled = targets.some((target) => target.platform === platform
113
- && target.scope !== intent.scope
114
- && (target.state === 'present' || target.state === 'partial'));
115
- if (intent.action === 'install' && otherScopeInstalled) {
136
+ if (intent.action === 'sync' && platform !== 'codex') {
137
+ throw new TuiOperationBlockedError('syncPlatformUnsupported', 'sync 只支持 Codex。');
138
+ }
139
+ if (intent.dependencyPlacementOnly === 'desired')
140
+ continue;
141
+ const otherScope = intent.scope === 'user' ? 'project' : 'user';
142
+ const otherScopeInstalled = platform === 'codex'
143
+ ? platformTargets.some((target) => target.desired.scopes.includes(otherScope)
144
+ || target.activation?.[otherScope] === true)
145
+ : targets.some((target) => target.platform === platform
146
+ && target.scope !== intent.scope
147
+ && (target.state === 'present' || target.state === 'partial'));
148
+ const pureNativeCodex = isPureNativeCodexTarget(selected);
149
+ if (intent.action === 'install' && otherScopeInstalled && !pureNativeCodex
150
+ && !intent.dependencyPlacementAdjustment) {
116
151
  throw new TuiOperationBlockedError('scopeConflict', '这个平台已安装在另一个使用位置。请先移除原位置。');
117
152
  }
118
- if (intent.action === 'install' && selected.state !== 'absent') {
153
+ if ((intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')
154
+ && targetHasArtifactCollision(selected)) {
155
+ throw new TuiOperationBlockedError('artifactCollision', '所选位置存在输出冲突。请先移除或迁移冲突内容。');
156
+ }
157
+ const selectedScopeInstalled = platform === 'codex'
158
+ ? selected.desired.scopes.includes(intent.scope)
159
+ || selected.activation?.[intent.scope] === true
160
+ : selected.state !== 'absent';
161
+ if (intent.action === 'install' && selectedScopeInstalled
162
+ && !intent.dependencyPlacementAdjustment) {
119
163
  throw new TuiOperationBlockedError('targetAlreadyInstalled', '所选位置已经安装。');
120
164
  }
165
+ if (intent.action === 'sync'
166
+ && selected.actual.state !== 'needsSync'
167
+ && selected.actual.state !== 'drifted'
168
+ && selected.actual.state !== 'conflict') {
169
+ throw new TuiOperationBlockedError('targetStateChanged', '当前安装状态不需要同步。');
170
+ }
121
171
  const revision = revisions.find((entry) => entry.pluginId === intent.pluginId
122
172
  && entry.platform === platform && entry.scope === intent.scope)?.revision;
123
173
  if (intent.action === 'upgrade'
@@ -140,7 +190,9 @@ async function assertTypedIssueOrigin(intent, targets) {
140
190
  };
141
191
  const selected = targets.find((target) => target.pluginId === intent.pluginId
142
192
  && target.platform === intent.platform
143
- && target.scope === intent.scope);
193
+ && target.scope === intent.scope)
194
+ ?? targets.find((target) => target.pluginId === intent.pluginId
195
+ && target.platform === 'codex');
144
196
  if (intent.originIssue.category === 'dependencyMissing') {
145
197
  if (intent.originIssue.actionId !== 'installDependency'
146
198
  || intent.action !== 'install' || !selected || selected.state !== 'absent') {
@@ -169,10 +221,25 @@ async function assertTypedIssueOrigin(intent, targets) {
169
221
  }
170
222
  return;
171
223
  }
224
+ else if (intent.originIssue.category === 'sourceMissing') {
225
+ if (intent.originIssue.actionId !== 'cleanup' || intent.action !== 'cleanup'
226
+ || !selected || !selected.errors.some((error) => error.code === 'sourceMissing')) {
227
+ stale('原来源缺失问题已变化。');
228
+ }
229
+ return;
230
+ }
172
231
  else if (intent.originIssue.category === 'scopeConflict') {
173
- const installedScopes = new Set(targets.filter((target) => target.pluginId === intent.pluginId
174
- && target.platform === intent.platform
175
- && (target.state === 'present' || target.state === 'partial')).map((target) => target.scope));
232
+ const installedScopes = new Set();
233
+ for (const target of targets.filter((candidate) => candidate.pluginId === intent.pluginId && candidate.platform === intent.platform)) {
234
+ if (target.state === 'present' || target.state === 'partial')
235
+ installedScopes.add(target.scope);
236
+ for (const scope of target.desired.scopes)
237
+ installedScopes.add(scope);
238
+ if (target.activation?.user === true)
239
+ installedScopes.add('user');
240
+ if (target.activation?.project === true)
241
+ installedScopes.add('project');
242
+ }
176
243
  const correctBranch = intent.action === 'uninstall'
177
244
  && ((intent.originIssue.actionId === 'keepUserScope' && intent.scope === 'project')
178
245
  || (intent.originIssue.actionId === 'keepProjectScope' && intent.scope === 'user'));
@@ -202,25 +269,29 @@ async function assertTypedIssueOrigin(intent, targets) {
202
269
  async function loadDependencyOverviews(options) {
203
270
  const graph = await withResolvedMultiSourceCatalog({
204
271
  cliVersion: options.cliVersion,
205
- freshnessPolicy: 'cacheFirst',
206
272
  env: options.context.env,
207
273
  signal: options.signal,
274
+ ...(options.catalogOperationContext
275
+ ? { operationContext: options.catalogOperationContext }
276
+ : {}),
208
277
  consume: async (catalog) => ({
209
- nodes: (await Promise.all(catalog.sources.flatMap((item) => item.resolution
210
- ? [resolvePluginsFromCatalog(item.resolution.rootDir, undefined).then((plugins) => plugins.map((plugin) => ({
211
- pluginId: formatPluginId({
212
- sourceId: item.resolution.sourceId,
213
- pluginName: plugin.manifest.name,
214
- }),
215
- displayName: plugin.manifest.interface?.displayName ?? plugin.manifest.name,
216
- dependencies: (plugin.manifest.dependencies ?? []).map((pluginName) => formatPluginId({ sourceId: item.resolution.sourceId, pluginName })),
217
- })))] : []))).flat(),
278
+ nodes: catalog.sources.flatMap((item) => item.resolution
279
+ ? item.resolution.manifest.entries.map((entry) => ({
280
+ pluginId: formatPluginId({
281
+ sourceId: item.resolution.sourceId,
282
+ pluginName: entry.name,
283
+ }),
284
+ displayName: entry.interface?.displayName ?? entry.name,
285
+ dependencies: entry.dependencies.map((pluginName) => formatPluginId({ sourceId: item.resolution.sourceId, pluginName })),
286
+ }))
287
+ : []),
218
288
  sourceErrors: catalog.sources.flatMap((item) => item.error
219
289
  ? [{ sourceId: item.sourceId, code: 'dependencyGraphInvalid' }]
220
290
  : []),
221
291
  }),
222
292
  });
223
293
  const nodes = new Map(graph.nodes.map((node) => [node.pluginId, node]));
294
+ const summaryById = new Map(options.status.pluginSummaries.map((summary) => [summary.pluginId, summary]));
224
295
  const displayName = (pluginId) => nodes.get(pluginId)?.displayName
225
296
  ?? parsePluginId(pluginId).pluginName;
226
297
  const reverse = new Map();
@@ -233,6 +304,9 @@ async function loadDependencyOverviews(options) {
233
304
  for (const target of options.status.payload.targets) {
234
305
  targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
235
306
  }
307
+ const unsupportedPluginIds = new Set(options.status.pluginSummaries
308
+ .filter((summary) => (summary.missingCapabilities?.length ?? 0) > 0)
309
+ .map((summary) => summary.pluginId));
236
310
  const placementReferences = new Map();
237
311
  for (const consumer of options.status.payload.targets.filter((target) => target.state !== 'absent')) {
238
312
  const consumerNode = nodes.get(consumer.pluginId);
@@ -279,13 +353,15 @@ async function loadDependencyOverviews(options) {
279
353
  };
280
354
  const aggregateFor = (pluginId, placements) => {
281
355
  const sourceId = parsePluginId(pluginId).sourceId;
282
- if (graph.sourceErrors.some((error) => error.sourceId === sourceId))
356
+ if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
357
+ || unsupportedPluginIds.has(pluginId))
283
358
  return 'unavailable';
284
359
  if (placements.length === 0 || placements.every((placement) => placement.state === 'notInstalled')) {
285
360
  return 'notInstalled';
286
361
  }
287
362
  return placements.some((placement) => placement.state === 'scopeConflict'
288
- || (placement.state === 'installed' && placement.health !== 'ready'))
363
+ || (placement.state === 'installed'
364
+ && (placement.health !== 'ready' || placement.blockingReason !== undefined)))
289
365
  ? 'needsAttention'
290
366
  : 'ready';
291
367
  };
@@ -359,6 +435,13 @@ async function loadDependencyOverviews(options) {
359
435
  title: '依赖关系需要处理',
360
436
  explanation: '当前目标的依赖证据不能安全通过检查。',
361
437
  })));
438
+ const consumerTargets = summaryById.get(pluginId)?.supportedTargets;
439
+ const unsupportedDependency = dependencyItems.find((dependency) => {
440
+ const dependencyTargets = summaryById.get(dependency.pluginId)?.supportedTargets;
441
+ if (!consumerTargets || !dependencyTargets)
442
+ return true;
443
+ return ['codex', 'claude'].some((platform) => consumerTargets[platform].length > 0 && dependencyTargets[platform].length === 0);
444
+ });
362
445
  const sourceId = parsePluginId(pluginId).sourceId;
363
446
  const blockers = [
364
447
  ...graph.sourceErrors.filter((error) => error.sourceId === sourceId).map((error) => ({
@@ -371,6 +454,11 @@ async function loadDependencyOverviews(options) {
371
454
  title: '依赖关系无效',
372
455
  explanation: '依赖关系形成环。',
373
456
  }] : []),
457
+ ...(unsupportedDependency ? [{
458
+ code: 'dependencyTargetUnsupported',
459
+ title: '依赖目标不兼容',
460
+ explanation: `${unsupportedDependency.displayName} 不支持根插件所需的平台。`,
461
+ }] : []),
374
462
  ...targetBlockers,
375
463
  ];
376
464
  const directCount = dependencyItems.filter((item) => item.relation === 'direct').length;
@@ -418,40 +506,105 @@ function createReviewStageReporter(options) {
418
506
  };
419
507
  }
420
508
  export function createTuiControllerDependencies(context, progressFeed, options = {}) {
509
+ const readInventory = options.inventoryCatalogSourceReferences
510
+ ?? inventoryCatalogSourceReferences;
511
+ const validatedStoreEntries = new Map();
512
+ const validatingStoreEntries = new Map();
513
+ const loadCatalogGenerations = async () => {
514
+ const generations = await loadCatalogGenerationViewModels({
515
+ env: context.env,
516
+ validatedStoreEntries,
517
+ validatingStoreEntries,
518
+ onStage: (event) => context.debugLogger.log({
519
+ type: 'performance:stage',
520
+ level: 'debug',
521
+ target: 'debugLog',
522
+ message: 'Catalog generation load stage completed.',
523
+ data: event,
524
+ }),
525
+ });
526
+ const retained = new Set(generations.flatMap((source) => [
527
+ source.active?.storeKey,
528
+ ].filter((value) => value !== undefined)));
529
+ for (const storeKey of validatedStoreEntries.keys()) {
530
+ if (!retained.has(storeKey))
531
+ validatedStoreEntries.delete(storeKey);
532
+ }
533
+ return generations;
534
+ };
535
+ const createOperationInventoryReader = () => {
536
+ let ordinaryInventory;
537
+ return (inventoryOptions) => {
538
+ if (inventoryOptions.includeHistoricalProjects === true) {
539
+ return readInventory(inventoryOptions);
540
+ }
541
+ ordinaryInventory ??= readInventory(inventoryOptions);
542
+ return ordinaryInventory;
543
+ };
544
+ };
545
+ const catalogOperationByEvidence = new WeakMap();
546
+ let sessionCatalogOperationContext;
547
+ let sessionStatusResult;
548
+ const sessionContextForPlugin = (pluginId) => {
549
+ const sourceId = parsePluginId(pluginId).sourceId;
550
+ if (!sourceId || !sessionCatalogOperationContext)
551
+ return undefined;
552
+ return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
553
+ };
421
554
  const loadStatus = async (signal) => {
422
555
  signal.throwIfAborted();
423
556
  const packageInfo = await readCliPackageInfo();
557
+ const catalogOperationContext = sessionCatalogOperationContext
558
+ ?? await resolvePartialCatalogOperationContext({
559
+ env: context.env,
560
+ signal,
561
+ validatedStoreEntries,
562
+ validatingStoreEntries,
563
+ }).catch(() => undefined);
564
+ if (catalogOperationContext)
565
+ sessionCatalogOperationContext = catalogOperationContext;
424
566
  for (let attempt = 0; attempt < 3; attempt += 1) {
425
- const result = await statusCommandV4({
567
+ const result = await statusCommandV6({
426
568
  cwd: context.cwd,
427
569
  env: context.env,
428
570
  cliVersion: packageInfo.version,
429
571
  verbose: context.diagnostics.verbose,
572
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
573
+ inventoryCatalogSourceReferences: readInventory,
430
574
  });
431
575
  signal.throwIfAborted();
432
576
  const configured = await listCatalogSources({ env: context.env });
433
577
  signal.throwIfAborted();
434
578
  if (result.payload.catalog.configDigest !== configured.configDigest)
435
579
  continue;
580
+ sessionStatusResult = result;
436
581
  const dependencyOverviews = await loadDependencyOverviews({
437
582
  context,
438
583
  cliVersion: packageInfo.version,
439
584
  status: result,
440
585
  signal,
586
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
441
587
  });
442
588
  const portablePluginIds = await loadPortablePluginIds({
443
589
  context,
444
590
  cliVersion: packageInfo.version,
445
591
  signal,
592
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
446
593
  });
447
594
  signal.throwIfAborted();
595
+ const catalogGenerations = await loadCatalogGenerations();
596
+ signal.throwIfAborted();
448
597
  const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
449
598
  return {
599
+ cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
450
600
  payload: result.payload,
451
601
  pluginSummaries: result.pluginSummaries,
452
602
  targetRevisions: result.targetRevisions,
453
603
  dependencyOverviews,
454
604
  portablePluginIds,
605
+ catalogGenerations,
606
+ sessionGenerationKeys: Object.fromEntries((catalogOperationContext?.generationSet.sources ?? [])
607
+ .map((source) => [source.sourceId, source.storeKey])),
455
608
  sourceDetails: configured.sources.map((source) => {
456
609
  const status = statusById.get(source.id);
457
610
  return {
@@ -479,7 +632,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
479
632
  }
480
633
  throw new TuiOperationBlockedError('operationReviewStale', '状态读取期间来源配置持续变化。请重试。');
481
634
  };
482
- const captureEvidence = async (intent, signal, freshnessPolicy = 'cacheFirst', preparation) => {
635
+ const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false) => {
483
636
  if (intent.kind === 'sourceMutation') {
484
637
  signal?.throwIfAborted();
485
638
  if (intent.action === 'add' && !intent.source
@@ -501,10 +654,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
501
654
  await preparation?.reporter.emit('evidence');
502
655
  signal?.throwIfAborted();
503
656
  const evidence = {
504
- catalog: { configDigest: preview.configDigest, compositeDigest: preview.candidateDigest },
657
+ catalog: { configDigest: preview.configDigest, compositeDigest: preview.proposedConfigDigest },
505
658
  sourceWrite: {
506
659
  configDigest: preview.configDigest,
507
- candidateDigest: preview.candidateDigest,
660
+ proposedConfigDigest: preview.proposedConfigDigest,
508
661
  ...(preview.sourceIdentity ? { sourceIdentity: preview.sourceIdentity } : {}),
509
662
  inventoryDigest: digest({ references: preview.references.references, errors: preview.references.errors }),
510
663
  referenceCount: preview.references.references.length,
@@ -512,7 +665,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
512
665
  },
513
666
  };
514
667
  if (preparation?.validateIntent
515
- && evidence.sourceWrite?.configDigest === evidence.sourceWrite?.candidateDigest) {
668
+ && evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
516
669
  throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
517
670
  }
518
671
  return evidence;
@@ -522,8 +675,37 @@ export function createTuiControllerDependencies(context, progressFeed, options =
522
675
  }
523
676
  signal?.throwIfAborted();
524
677
  const packageInfo = await readCliPackageInfo();
678
+ const offlineCleanupReason = intent.kind === 'pluginWrite'
679
+ && intent.action === 'cleanup'
680
+ && intent.originIssue?.actionId === 'cleanup'
681
+ && (intent.originIssue?.category === 'sourceMissing'
682
+ || intent.originIssue?.category === 'sourceUnavailable')
683
+ ? intent.originIssue.category === 'sourceMissing' ? 'sourceMissing' : 'bundleUnavailable'
684
+ : undefined;
685
+ let catalogOperation;
686
+ let catalogOperationError;
687
+ if (!offlineCleanupReason && intent.kind !== 'portableInstall') {
688
+ try {
689
+ const sourceIds = [parsePluginId(intent.pluginId).sourceId];
690
+ catalogOperation = !freshCatalog && sessionCatalogOperationContext
691
+ ? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
692
+ : await resolveCatalogOperationContext({
693
+ sourceIds,
694
+ env: context.env,
695
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
696
+ ...(signal ? { signal } : {}),
697
+ });
698
+ }
699
+ catch (error) {
700
+ catalogOperationError = error;
701
+ }
702
+ }
525
703
  if (intent.kind === 'portableInstall') {
526
704
  await preparation?.reporter.emit('portable');
705
+ const portableSourceId = parsePluginId(intent.pluginId).sourceId;
706
+ const portableCatalogOperation = sessionCatalogOperationContext
707
+ ? selectCatalogOperationContext(sessionCatalogOperationContext, [portableSourceId])
708
+ : undefined;
527
709
  const portable = await preparePortableInstall({
528
710
  selector: intent.pluginId,
529
711
  scope: intent.scope,
@@ -533,20 +715,105 @@ export function createTuiControllerDependencies(context, progressFeed, options =
533
715
  env: context.env,
534
716
  cliVersion: packageInfo.version,
535
717
  signal,
718
+ ...(portableCatalogOperation
719
+ ? { catalogOperationContext: portableCatalogOperation }
720
+ : {}),
536
721
  });
537
722
  signal?.throwIfAborted();
538
723
  await preparation?.reporter.emit('evidence');
539
724
  signal?.throwIfAborted();
540
- return {
725
+ const evidence = {
541
726
  catalog: {
542
727
  configDigest: portable.configDigest,
543
728
  compositeDigest: portable.evidenceDigest,
729
+ ...(portable.generationSetDigest
730
+ ? { generationSetDigest: portable.generationSetDigest }
731
+ : {}),
544
732
  },
545
733
  portable,
546
734
  };
735
+ if (catalogOperation)
736
+ catalogOperationByEvidence.set(evidence, catalogOperation);
737
+ return evidence;
738
+ }
739
+ if (preparation && (intent.kind === 'configSave' || intent.kind === 'configReset')) {
740
+ if (!sessionStatusResult || !catalogOperation) {
741
+ throw operationReviewStale('当前 TUI session 没有可复用的设置证据。');
742
+ }
743
+ await preparation.reporter.emit('config');
744
+ const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
745
+ signal?.throwIfAborted();
746
+ if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
747
+ throw operationReviewStale('准备设置确认期间来源配置发生变化。');
748
+ }
749
+ const selected = await withResolvedCatalogPlugin({
750
+ selector: intent.pluginId,
751
+ cliVersion: packageInfo.version,
752
+ configSnapshot,
753
+ env: context.env,
754
+ operationContext: catalogOperation,
755
+ ...(signal ? { signal } : {}),
756
+ consume: async ({ plugin, source }) => {
757
+ if (!plugin.manifest.config) {
758
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
759
+ }
760
+ if (intent.kind === 'configSave') {
761
+ await coercePluginConfigUpdates({
762
+ schema: plugin.manifest.config.schema,
763
+ updates: intent.updates ?? {},
764
+ cwd: context.cwd,
765
+ });
766
+ for (const key of intent.removeKeys ?? []) {
767
+ if (!plugin.manifest.config.schema[key]) {
768
+ throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
769
+ }
770
+ }
771
+ }
772
+ return {
773
+ pluginName: plugin.manifest.name,
774
+ source: sourceEvidenceForPlugin(source, plugin.manifest.name),
775
+ };
776
+ },
777
+ });
778
+ const store = await readPluginConfigStore({
779
+ sourceId: selected.source.sourceId,
780
+ sourceIdentity: selected.source.sourceIdentity,
781
+ pluginName: selected.pluginName,
782
+ scope: intent.scope,
783
+ projectLayer: intent.projectLayer,
784
+ cwd: context.cwd,
785
+ env: context.env,
786
+ });
787
+ signal?.throwIfAborted();
788
+ const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope);
789
+ const relevantRevisions = (sessionStatusResult.targetRevisions ?? []).filter((revision) => relevantTargets.some((target) => target.platform === revision.platform
790
+ && target.scope === revision.scope
791
+ && target.pluginId === revision.pluginId));
792
+ await preparation.reporter.emit('evidence');
793
+ const evidence = {
794
+ catalog: {
795
+ configDigest: sessionStatusResult.payload.catalog.configDigest,
796
+ compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
797
+ generationSetDigest: catalogOperation.generationSetDigest,
798
+ },
799
+ source: selected.source,
800
+ targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
801
+ config: {
802
+ scope: intent.scope,
803
+ projectLayer: intent.projectLayer,
804
+ schemaIdentity: selected.source.manifestDigest,
805
+ storeDigest: digest({ exists: store.exists, values: store.values }),
806
+ affectedTargets: relevantTargets.map(({ platform, scope }) => ({ platform, scope })),
807
+ },
808
+ };
809
+ catalogOperationByEvidence.set(evidence, catalogOperation);
810
+ return evidence;
547
811
  }
548
812
  await preparation?.reporter.emit('status');
549
- const status = await statusCommandV4({
813
+ const statusCatalogOperation = offlineCleanupReason
814
+ ? undefined
815
+ : catalogOperation ?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
816
+ const status = await statusCommandV6({
550
817
  plugins: [...new Set([intent.pluginId,
551
818
  ...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
552
819
  ? [intent.originIssue.consumerPluginId] : [])])],
@@ -557,39 +824,72 @@ export function createTuiControllerDependencies(context, progressFeed, options =
557
824
  cwd: context.cwd,
558
825
  env: context.env,
559
826
  cliVersion: packageInfo.version,
827
+ ...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
828
+ inventoryCatalogSourceReferences: operationInventory,
560
829
  });
561
830
  signal?.throwIfAborted();
831
+ const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
832
+ signal?.throwIfAborted();
833
+ if (configSnapshot.digest !== status.payload.catalog.configDigest) {
834
+ throw operationReviewStale('准备确认信息期间来源配置发生变化。');
835
+ }
836
+ if (catalogOperationError)
837
+ throw catalogOperationError;
562
838
  if (intent.kind === 'pluginWrite') {
563
839
  await assertTypedIssueOrigin(intent, status.payload.targets);
564
840
  if (preparation?.validateIntent) {
565
841
  assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
566
842
  }
567
843
  }
568
- const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
569
- signal?.throwIfAborted();
570
- if (configSnapshot.digest !== status.payload.catalog.configDigest) {
571
- throw operationReviewStale('准备确认信息期间来源配置发生变化。');
844
+ if (!offlineCleanupReason && !catalogOperation) {
845
+ catalogOperation = await resolveCatalogOperationContext({
846
+ sourceIds: [parsePluginId(intent.pluginId).sourceId],
847
+ configSnapshot,
848
+ env: context.env,
849
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
850
+ ...(signal ? { signal } : {}),
851
+ });
572
852
  }
573
853
  let dependencyPlan;
854
+ let codexReconcilePlans;
574
855
  await preparation?.reporter.emit('catalog');
575
- const bundleUnavailableCleanupTarget = intent.kind === 'pluginWrite'
576
- && intent.action === 'cleanup'
577
- && intent.originIssue?.category === 'sourceUnavailable'
578
- && intent.originIssue.actionId === 'cleanup'
856
+ const offlineCleanupTarget = offlineCleanupReason && intent.kind === 'pluginWrite'
579
857
  ? status.payload.targets.find((target) => target.pluginId === intent.pluginId
580
858
  && target.platform === intent.platform
581
859
  && target.scope === intent.scope
582
- && target.errors.some((error) => error.code === 'bundleUnavailable'))
860
+ && target.errors.some((error) => error.code === offlineCleanupReason))
583
861
  : undefined;
584
- const catalogSource = bundleUnavailableCleanupTarget
585
- ? status.payload.catalog.sources.find((source) => source.id === bundleUnavailableCleanupTarget.sourceId)
862
+ const catalogSource = offlineCleanupTarget
863
+ ? status.payload.catalog.sources.find((source) => source.id === offlineCleanupTarget.sourceId)
586
864
  : undefined;
587
- const selected = bundleUnavailableCleanupTarget
865
+ let offlineCleanup;
866
+ if (offlineCleanupReason && offlineCleanupTarget) {
867
+ await preparation?.reporter.emit('inventory');
868
+ const inventory = await operationInventory({
869
+ cwd: context.cwd,
870
+ env: context.env,
871
+ });
872
+ signal?.throwIfAborted();
873
+ offlineCleanup = offlineCleanupEvidenceFromInventory({
874
+ reason: offlineCleanupReason,
875
+ pluginId: offlineCleanupTarget.pluginId,
876
+ pluginName: offlineCleanupTarget.pluginName,
877
+ sourceId: offlineCleanupTarget.sourceId,
878
+ sourceIdentity: offlineCleanupTarget.sourceIdentity,
879
+ platform: offlineCleanupTarget.platform,
880
+ scope: offlineCleanupTarget.scope,
881
+ inventory,
882
+ }) ?? undefined;
883
+ if (!offlineCleanup) {
884
+ throw operationReviewStale('离线清理现场证据已消失或无法验证。');
885
+ }
886
+ }
887
+ const selected = offlineCleanupTarget
588
888
  ? {
589
- pluginName: bundleUnavailableCleanupTarget.pluginName,
889
+ pluginName: offlineCleanupTarget.pluginName,
590
890
  source: {
591
- sourceId: bundleUnavailableCleanupTarget.sourceId,
592
- sourceIdentity: bundleUnavailableCleanupTarget.sourceIdentity,
891
+ sourceId: offlineCleanupTarget.sourceId,
892
+ sourceIdentity: offlineCleanupTarget.sourceIdentity,
593
893
  ...(catalogSource?.requestedRef ? { requestedRef: catalogSource.requestedRef } : {}),
594
894
  ...(catalogSource?.resolvedCommit ? { resolvedCommit: catalogSource.resolvedCommit } : {}),
595
895
  },
@@ -597,9 +897,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
597
897
  : await withResolvedCatalogPlugin({
598
898
  selector: intent.pluginId,
599
899
  cliVersion: packageInfo.version,
600
- freshnessPolicy,
601
900
  configSnapshot,
602
901
  env: context.env,
902
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
603
903
  ...(signal ? { signal } : {}),
604
904
  consume: async ({ plugin, source }) => {
605
905
  if (preparation?.validateIntent && intent.kind === 'configSave') {
@@ -624,48 +924,112 @@ export function createTuiControllerDependencies(context, progressFeed, options =
624
924
  };
625
925
  },
626
926
  });
927
+ let codexManager;
627
928
  const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
628
- if (intent.kind === 'pluginWrite'
629
- && hasDeclaredDependencies
630
- && (intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')) {
929
+ if (intent.kind === 'pluginWrite') {
631
930
  const command = {
632
931
  install: installWriteCommand,
633
932
  upgrade: upgradeWriteCommand,
634
933
  reinstall: reinstallWriteCommand,
934
+ uninstall: uninstallWriteCommand,
935
+ cleanup: cleanupWriteCommand,
936
+ sync: installWriteCommand,
635
937
  }[intent.action];
636
938
  const preview = await command([intent.pluginId], {
637
939
  cwd: context.cwd,
638
940
  platform: intent.platform,
639
941
  scope: intent.scope,
942
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
943
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
944
+ : {}),
945
+ ...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
640
946
  }, {
641
947
  ...context,
948
+ inventoryCatalogSourceReferences: operationInventory,
642
949
  expectedCatalogConfigDigest: status.payload.catalog.configDigest,
950
+ ...(catalogOperation ? {
951
+ catalogOperationContext: catalogOperation,
952
+ expectedGenerationSetDigest: catalogOperation.generationSetDigest,
953
+ } : {}),
643
954
  previewDependencyPlanOnly: true,
955
+ ...(intent.action === 'sync' ? { reconcileIntentOnly: true } : {}),
644
956
  ...(preparation ? {
645
- previewCatalogFreshnessPolicy: 'cacheFirst',
646
957
  onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
647
958
  } : {}),
648
959
  });
649
960
  signal?.throwIfAborted();
650
- dependencyPlan = preview.dependencyPlan;
651
- if (!dependencyPlan) {
652
- throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
961
+ codexReconcilePlans = intent.dependencyPlacementOnly === 'desired'
962
+ ? undefined
963
+ : preview?.codexReconcilePlans;
964
+ if (hasDeclaredDependencies
965
+ && (intent.action === 'install'
966
+ || intent.action === 'upgrade'
967
+ || intent.action === 'reinstall'
968
+ || intent.action === 'sync')) {
969
+ dependencyPlan = preview.dependencyPlan;
970
+ if (!dependencyPlan) {
971
+ throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
972
+ }
973
+ const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
974
+ if (blocker) {
975
+ throw new TuiOperationBlockedError(blocker.errorCode ?? 'dependencyNotReady', `${blocker.errorCode ?? 'dependencyNotReady'}: 依赖安装计划被阻止。`);
976
+ }
653
977
  }
654
- const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
655
- if (blocker) {
656
- throw new TuiOperationBlockedError(blocker.errorCode ?? 'dependencyNotReady', `${blocker.errorCode ?? 'dependencyNotReady'}: 依赖安装计划被阻止。`);
978
+ const managerPreview = preview?.codexManagerPreview;
979
+ if (managerPreview?.state === 'blocked' && intent.dependencyPlacementOnly !== 'desired') {
980
+ const message = managerPreview.code === 'codexSystemCliUnsupported'
981
+ ? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
982
+ : 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
983
+ throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
984
+ }
985
+ if (managerPreview && managerPreview.state !== 'blocked'
986
+ && intent.dependencyPlacementOnly !== 'desired') {
987
+ codexManager = {
988
+ state: managerPreview.state,
989
+ packageName: MANAGED_CODEX_PACKAGE_NAME,
990
+ version: managerPreview.state === 'requires-install'
991
+ ? MANAGED_CODEX_VERSION
992
+ : managerPreview.manager.version,
993
+ };
657
994
  }
658
995
  }
659
996
  await preparation?.reporter.emit('evidence');
660
997
  signal?.throwIfAborted();
998
+ let projectIntentDigest;
999
+ if (intent.kind === 'pluginWrite' && intent.dependencyPlacementOnly === 'desired') {
1000
+ if (intent.scope !== 'project') {
1001
+ throw new TuiOperationBlockedError('dependencyPlacementScopeInvalid', '缩小依赖位置只能更新当前项目 desired intent。');
1002
+ }
1003
+ projectIntentDigest = calculatePluginIntentDigest(await readPluginIntentState({
1004
+ scope: 'project',
1005
+ cwd: context.cwd,
1006
+ env: context.env,
1007
+ }));
1008
+ signal?.throwIfAborted();
1009
+ }
1010
+ const evidenceTargets = intent.kind === 'configSave' || intent.kind === 'configReset'
1011
+ ? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.scope)
1012
+ : status.payload.targets;
1013
+ const evidenceRevisions = intent.kind === 'configSave' || intent.kind === 'configReset'
1014
+ ? (status.targetRevisions ?? []).filter((revision) => evidenceTargets.some((target) => target.platform === revision.platform
1015
+ && target.scope === revision.scope
1016
+ && target.pluginId === revision.pluginId))
1017
+ : (status.targetRevisions ?? []);
661
1018
  const evidence = {
662
1019
  catalog: {
663
1020
  configDigest: status.payload.catalog.configDigest,
664
1021
  compositeDigest: status.payload.catalog.compositeDigest,
1022
+ ...(catalogOperation
1023
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
1024
+ : {}),
665
1025
  },
666
1026
  source: selected.source,
1027
+ ...(offlineCleanup ? { offlineCleanup } : {}),
667
1028
  ...(dependencyPlan ? { dependencyPlan } : {}),
668
- targetFingerprint: statusTargetFingerprint(status.payload.targets, status.targetRevisions ?? []),
1029
+ ...(projectIntentDigest ? { projectIntentDigest } : {}),
1030
+ ...(codexReconcilePlans ? { codexReconcilePlans } : {}),
1031
+ ...(codexManager ? { codexManager } : {}),
1032
+ targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
669
1033
  };
670
1034
  if (intent.kind === 'configSave' || intent.kind === 'configReset') {
671
1035
  const store = await readPluginConfigStore({
@@ -683,11 +1047,21 @@ export function createTuiControllerDependencies(context, progressFeed, options =
683
1047
  projectLayer: intent.projectLayer,
684
1048
  schemaIdentity: selected.source.manifestDigest,
685
1049
  storeDigest: digest({ exists: store.exists, values: store.values }),
1050
+ affectedTargets: evidenceTargets.map(({ platform, scope }) => ({ platform, scope })),
686
1051
  };
687
1052
  }
1053
+ if (catalogOperation)
1054
+ catalogOperationByEvidence.set(evidence, catalogOperation);
688
1055
  return evidence;
689
1056
  };
690
1057
  const dependencies = {
1058
+ beginCatalogSession(sessionOptions) {
1059
+ sessionCatalogOperationContext = undefined;
1060
+ if (sessionOptions?.reuseValidatedGenerations !== true) {
1061
+ validatedStoreEntries.clear();
1062
+ validatingStoreEntries.clear();
1063
+ }
1064
+ },
691
1065
  loadStatus,
692
1066
  async inspectSourceRemoval(_sourceId, signal) {
693
1067
  signal.throwIfAborted();
@@ -708,7 +1082,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
708
1082
  now: options.now ?? Date.now,
709
1083
  });
710
1084
  try {
711
- return await captureEvidence(intent, signal, 'cacheFirst', {
1085
+ return await captureEvidence(intent, signal, {
712
1086
  reporter,
713
1087
  validateIntent: true,
714
1088
  });
@@ -717,10 +1091,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
717
1091
  await reporter.finish();
718
1092
  }
719
1093
  },
1094
+ async previewDependencyPlacement(intent, signal) {
1095
+ const evidence = await captureEvidence(intent, signal);
1096
+ return evidence.dependencyPlan;
1097
+ },
720
1098
  async validateIntent(intent, signal) {
721
1099
  if (intent.kind === 'sourceMutation') {
722
1100
  const evidence = await captureEvidence(intent, signal);
723
- if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.candidateDigest) {
1101
+ if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
724
1102
  throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
725
1103
  }
726
1104
  return;
@@ -730,9 +1108,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
730
1108
  await withResolvedCatalogPlugin({
731
1109
  selector: intent.pluginId,
732
1110
  cliVersion: packageInfo.version,
733
- freshnessPolicy: 'cacheFirst',
734
1111
  env: context.env,
735
1112
  signal,
1113
+ ...(sessionContextForPlugin(intent.pluginId)
1114
+ ? { operationContext: sessionContextForPlugin(intent.pluginId) }
1115
+ : {}),
736
1116
  consume: async ({ plugin }) => {
737
1117
  if (!plugin.manifest.config) {
738
1118
  throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
@@ -755,39 +1135,53 @@ export function createTuiControllerDependencies(context, progressFeed, options =
755
1135
  return;
756
1136
  signal.throwIfAborted();
757
1137
  const packageInfo = await readCliPackageInfo();
758
- const status = await statusCommandV4({
759
- plugins: [...new Set([intent.pluginId,
760
- ...(intent.originIssue?.consumerPluginId ? [intent.originIssue.consumerPluginId] : [])])],
1138
+ const pluginIds = [...new Set([intent.pluginId,
1139
+ ...(intent.originIssue?.consumerPluginId ? [intent.originIssue.consumerPluginId] : [])])];
1140
+ const offlineCleanup = intent.action === 'cleanup'
1141
+ && intent.originIssue?.actionId === 'cleanup'
1142
+ && (intent.originIssue.category === 'sourceMissing'
1143
+ || intent.originIssue.category === 'sourceUnavailable');
1144
+ const sourceIds = pluginIds.map((pluginId) => parsePluginId(pluginId).sourceId);
1145
+ let catalogOperation = offlineCleanup
1146
+ ? undefined
1147
+ : sessionCatalogOperationContext
1148
+ ? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
1149
+ : undefined;
1150
+ const statusOptions = {
1151
+ plugins: pluginIds,
761
1152
  platform: intent.platform,
762
1153
  cwd: context.cwd,
763
1154
  env: context.env,
764
1155
  cliVersion: packageInfo.version,
1156
+ inventoryCatalogSourceReferences: readInventory,
1157
+ };
1158
+ let status = await statusCommandV6({
1159
+ ...statusOptions,
1160
+ ...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
765
1161
  });
1162
+ const platforms = intent.platform === 'both'
1163
+ ? ['codex', 'claude']
1164
+ : [intent.platform];
1165
+ const missingSelectedTarget = platforms.some((platform) => !status.payload.targets.some((target) => target.pluginId === intent.pluginId
1166
+ && target.platform === platform
1167
+ && (platform === 'codex' || target.scope === intent.scope)));
1168
+ if (!offlineCleanup && !catalogOperation && missingSelectedTarget) {
1169
+ catalogOperation = await resolveCatalogOperationContext({
1170
+ sourceIds,
1171
+ env: context.env,
1172
+ validatedStoreEntries,
1173
+ validatingStoreEntries,
1174
+ signal,
1175
+ });
1176
+ status = await statusCommandV6({
1177
+ ...statusOptions,
1178
+ catalogOperationContext: catalogOperation,
1179
+ });
1180
+ }
766
1181
  signal.throwIfAborted();
767
1182
  await assertTypedIssueOrigin(intent, status.payload.targets);
768
1183
  assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
769
1184
  },
770
- async checkSource(sourceId, signal) {
771
- const packageInfo = await readCliPackageInfo();
772
- const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
773
- return withResolvedMultiSourceCatalog({
774
- cliVersion: packageInfo.version,
775
- freshnessPolicy: 'forceRemote',
776
- sourceIds: [sourceId],
777
- configSnapshot,
778
- env: context.env,
779
- signal,
780
- consume: (catalog) => {
781
- const item = catalog.sources[0];
782
- if (item.error)
783
- throw item.error;
784
- return {
785
- sourceIdentity: item.resolution.sourceIdentity,
786
- requestedRef: item.resolution.requestedRef,
787
- };
788
- },
789
- });
790
- },
791
1185
  captureEvidence,
792
1186
  logError(_error, code, logContext) {
793
1187
  return context.debugLogger.log({
@@ -805,9 +1199,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
805
1199
  return withResolvedCatalogPlugin({
806
1200
  selector: pluginId,
807
1201
  cliVersion: packageInfo.version,
808
- freshnessPolicy: 'cacheFirst',
809
1202
  env: context.env,
810
1203
  signal,
1204
+ ...(sessionContextForPlugin(pluginId)
1205
+ ? { operationContext: sessionContextForPlugin(pluginId) }
1206
+ : {}),
811
1207
  consume: async ({ plugin }) => {
812
1208
  if (!plugin.manifest.config) {
813
1209
  throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
@@ -837,9 +1233,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
837
1233
  return withResolvedCatalogPlugin({
838
1234
  selector: pluginId,
839
1235
  cliVersion: packageInfo.version,
840
- freshnessPolicy: 'cacheFirst',
841
1236
  env: context.env,
842
1237
  signal,
1238
+ ...(sessionContextForPlugin(pluginId)
1239
+ ? { operationContext: sessionContextForPlugin(pluginId) }
1240
+ : {}),
843
1241
  consume: async ({ plugin }) => {
844
1242
  if (!plugin.manifest.config) {
845
1243
  throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
@@ -860,7 +1258,39 @@ export function createTuiControllerDependencies(context, progressFeed, options =
860
1258
  cwd: context.cwd,
861
1259
  env: context.env,
862
1260
  });
863
- return buildConfigPage({ pluginId, schema: plugin.manifest.config, read, effectiveRead });
1261
+ const layerReads = layer.scope === 'effective' ? undefined : {
1262
+ user: await resolveConfigRead({
1263
+ pluginName: plugin.manifest.name,
1264
+ plugin,
1265
+ scope: 'user',
1266
+ projectLayer: 'shared',
1267
+ cwd: context.cwd,
1268
+ env: context.env,
1269
+ }),
1270
+ project: await resolveConfigRead({
1271
+ pluginName: plugin.manifest.name,
1272
+ plugin,
1273
+ scope: 'project',
1274
+ projectLayer: 'shared',
1275
+ cwd: context.cwd,
1276
+ env: context.env,
1277
+ }),
1278
+ local: await resolveConfigRead({
1279
+ pluginName: plugin.manifest.name,
1280
+ plugin,
1281
+ scope: 'project',
1282
+ projectLayer: 'local',
1283
+ cwd: context.cwd,
1284
+ env: context.env,
1285
+ }),
1286
+ };
1287
+ return buildConfigPage({
1288
+ pluginId,
1289
+ schema: plugin.manifest.config,
1290
+ read,
1291
+ effectiveRead,
1292
+ ...(layerReads ? { layerReads } : {}),
1293
+ });
864
1294
  },
865
1295
  });
866
1296
  },
@@ -920,6 +1350,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
920
1350
  env: context.env,
921
1351
  cliVersion: packageInfo.version,
922
1352
  expectedEvidenceDigest: expectedEvidence.portable.evidenceDigest,
1353
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
923
1354
  onProgress(stage) {
924
1355
  const phase = stage === 'prepare' ? '检查插件依赖并生成候选'
925
1356
  : stage === 'add' ? '安装已选择的 Skills' : '验证安装结果';
@@ -934,49 +1365,87 @@ export function createTuiControllerDependencies(context, progressFeed, options =
934
1365
  throw error;
935
1366
  }
936
1367
  }
1368
+ if (intent.kind === 'sourceMutation') {
1369
+ const currentEvidence = await captureEvidence(intent);
1370
+ if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
1371
+ throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
1372
+ }
1373
+ await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
1374
+ const packageInfo = await readCliPackageInfo();
1375
+ const runtime = {
1376
+ onProgress: async (event) => {
1377
+ const phase = SOURCE_PROGRESS_PHASES[event.stage];
1378
+ progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
1379
+ await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
1380
+ },
1381
+ };
1382
+ const common = {
1383
+ cwd: context.cwd,
1384
+ env: context.env,
1385
+ expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
1386
+ runtime,
1387
+ };
1388
+ if (intent.action === 'add' && intent.source) {
1389
+ return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
1390
+ }
1391
+ if (intent.action === 'update' && intent.update) {
1392
+ return updateCatalogSourceCommand({ ...common, sourceId: intent.sourceId, update: intent.update, cliVersion: packageInfo.version });
1393
+ }
1394
+ if (intent.action === 'setPrimary') {
1395
+ return setPrimaryCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
1396
+ }
1397
+ if (intent.action === 'remove') {
1398
+ return removeCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
1399
+ }
1400
+ throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
1401
+ }
937
1402
  return withAwHomeMutationLock({
938
1403
  env: context.env,
939
1404
  action: async (awHomeMutationToken) => {
940
- const currentEvidence = await captureEvidence(intent, undefined, 'requireRemote');
1405
+ await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
1406
+ const operationInventory = createOperationInventoryReader();
1407
+ const currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true);
941
1408
  if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
942
1409
  throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
943
1410
  }
944
- await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
945
- if (intent.kind === 'sourceMutation') {
946
- const packageInfo = await readCliPackageInfo();
947
- const runtime = {
948
- awHomeMutationToken,
949
- onProgress: async (event) => {
950
- const phase = SOURCE_PROGRESS_PHASES[event.stage];
951
- progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
952
- await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
953
- },
954
- };
955
- const common = {
956
- cwd: context.cwd,
957
- env: context.env,
958
- expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
959
- runtime,
960
- };
961
- if (intent.action === 'add' && intent.source) {
962
- return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
963
- }
964
- if (intent.action === 'update' && intent.update) {
965
- return updateCatalogSourceCommand({ ...common, sourceId: intent.sourceId, update: intent.update, cliVersion: packageInfo.version });
966
- }
967
- if (intent.action === 'setPrimary') {
968
- return setPrimaryCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
969
- }
970
- if (intent.action === 'remove') {
971
- return removeCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
972
- }
973
- throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
1411
+ const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
1412
+ if (expectedEvidence.catalog.generationSetDigest
1413
+ && catalogOperation?.generationSetDigest !== expectedEvidence.catalog.generationSetDigest) {
1414
+ throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
974
1415
  }
1416
+ await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
975
1417
  if (intent.kind === 'pluginWrite') {
1418
+ if (intent.dependencyPlacementOnly === 'desired') {
1419
+ const plan = currentEvidence.dependencyPlan;
1420
+ if (!plan || !expectedEvidence.projectIntentDigest) {
1421
+ throw operationReviewStale('确认信息缺少依赖位置或 project intent 证据。');
1422
+ }
1423
+ const location = { scope: 'project', cwd: context.cwd, env: context.env };
1424
+ const currentIntent = await readPluginIntentState(location);
1425
+ if (calculatePluginIntentDigest(currentIntent) !== expectedEvidence.projectIntentDigest) {
1426
+ throw operationReviewStale('确认后 project desired intent 发生变化。');
1427
+ }
1428
+ const plannedKeys = new Set(plan.targets.map((target) => `${target.pluginId}\0${target.platform}`));
1429
+ const nextIntent = canonicalizePluginIntentState({
1430
+ ...currentIntent,
1431
+ dependencyScopeOverrides: [
1432
+ ...currentIntent.dependencyScopeOverrides.filter((override) => !plannedKeys.has(`${override.pluginId}\0${override.platform}`)),
1433
+ ...plan.scopeOverrides,
1434
+ ],
1435
+ });
1436
+ await writePluginIntentState(location, nextIntent);
1437
+ return { dependencyPlan: plan, desiredPlacementUpdated: true };
1438
+ }
976
1439
  const options = {
977
1440
  cwd: context.cwd,
978
1441
  platform: intent.platform,
979
1442
  scope: intent.scope,
1443
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
1444
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
1445
+ : {}),
1446
+ ...(expectedEvidence.codexManager?.state === 'requires-install'
1447
+ ? { allowManagedCodexInstall: true }
1448
+ : {}),
980
1449
  ...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
981
1450
  };
982
1451
  const command = {
@@ -985,16 +1454,35 @@ export function createTuiControllerDependencies(context, progressFeed, options =
985
1454
  reinstall: reinstallWriteCommand,
986
1455
  uninstall: uninstallWriteCommand,
987
1456
  cleanup: cleanupWriteCommand,
1457
+ sync: installWriteCommand,
988
1458
  }[intent.action];
989
1459
  const currentSource = currentEvidence.source;
990
- if (!expectedEvidence.source || !currentSource) {
1460
+ const offlineCleanup = currentEvidence.offlineCleanup;
1461
+ if (!offlineCleanup && (!expectedEvidence.source || !currentSource)) {
991
1462
  throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
992
1463
  }
993
1464
  const result = await command([intent.pluginId], options, {
994
1465
  ...context,
995
1466
  awHomeMutationToken,
1467
+ inventoryCatalogSourceReferences: operationInventory,
1468
+ ...(currentEvidence.dependencyPlan
1469
+ ? { expectedOperationDependencyPlan: currentEvidence.dependencyPlan }
1470
+ : {}),
1471
+ ...(currentEvidence.codexReconcilePlans
1472
+ ? { expectedCodexReconcilePlans: currentEvidence.codexReconcilePlans }
1473
+ : {}),
1474
+ ...(intent.action === 'sync' ? { reconcileIntentOnly: true } : {}),
996
1475
  expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
997
- reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual),
1476
+ ...(catalogOperation ? {
1477
+ catalogOperationContext: catalogOperation,
1478
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1479
+ } : {}),
1480
+ ...(expectedEvidence.codexManager?.state === 'requires-install'
1481
+ ? { managedCodexAuthorizer: async () => true }
1482
+ : {}),
1483
+ ...(offlineCleanup
1484
+ ? { expectedOfflineCleanupEvidence: offlineCleanup }
1485
+ : { reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual) }),
998
1486
  });
999
1487
  throwIfWriteReviewStale(result);
1000
1488
  return result;
@@ -1008,10 +1496,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1008
1496
  return withResolvedCatalogPlugin({
1009
1497
  selector: intent.pluginId,
1010
1498
  cliVersion: packageInfo.version,
1011
- freshnessPolicy: 'requireRemote',
1012
1499
  env: context.env,
1500
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
1013
1501
  consume: async ({ plugin, source }) => {
1014
1502
  assertSourceEvidenceMatches(currentSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
1503
+ const applyTargets = currentEvidence.config?.affectedTargets.map((target) => ({
1504
+ pluginName: plugin.manifest.name,
1505
+ platform: target.platform,
1506
+ scope: target.scope,
1507
+ })) ?? [];
1015
1508
  const result = intent.kind === 'configSave'
1016
1509
  ? await applyPluginConfigWrite({
1017
1510
  plugin,
@@ -1021,8 +1514,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1021
1514
  cwd: context.cwd,
1022
1515
  env: context.env,
1023
1516
  nativeCliRunner: context.nativeCliRunner,
1024
- rebuildRuntime: { ...context, awHomeMutationToken },
1517
+ rebuildRuntime: {
1518
+ ...context,
1519
+ awHomeMutationToken,
1520
+ ...(catalogOperation ? {
1521
+ catalogOperationContext: catalogOperation,
1522
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1523
+ } : {}),
1524
+ },
1025
1525
  awHomeMutationToken,
1526
+ applyTargets,
1026
1527
  request: { updates: intent.updates, removeKeys: intent.removeKeys },
1027
1528
  })
1028
1529
  : await resetPluginConfigWithHelper({
@@ -1033,8 +1534,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1033
1534
  cwd: context.cwd,
1034
1535
  env: context.env,
1035
1536
  nativeCliRunner: context.nativeCliRunner,
1036
- rebuildRuntime: { ...context, awHomeMutationToken },
1537
+ rebuildRuntime: {
1538
+ ...context,
1539
+ awHomeMutationToken,
1540
+ ...(catalogOperation ? {
1541
+ catalogOperationContext: catalogOperation,
1542
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1543
+ } : {}),
1544
+ },
1037
1545
  awHomeMutationToken,
1546
+ applyTargets,
1038
1547
  });
1039
1548
  return result;
1040
1549
  },