@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,2011 @@
1
+ import crypto from 'node:crypto';
2
+ import path from 'node:path';
3
+ import { statusCommandV7, statusTargetIntent, } from '../commands/status-v6.js';
4
+ import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
5
+ import { cleanupWriteCommand, installWriteCommand, reinstallWriteCommand, syncWriteCommand, uninstallWriteCommand, } from '../commands/write-commands.js';
6
+ import { readCliPackageInfo } from '../utils/local-marketplace.js';
7
+ import { readCatalogSourcesConfigSnapshot, recoverInvalidCatalogSourcesConfig, } from '../utils/catalog-sources.js';
8
+ import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
9
+ import { coercePluginConfigUpdates, inspectPluginConfigResetTarget, readPluginConfigStore, resolveConfigRead, resolveEffectivePluginConfig, } from '../utils/plugin-config.js';
10
+ import { applyPluginConfigWrite, replayConfigRuntimeWrites, 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';
13
+ import { operationEvidenceMatches, operationEvidenceFingerprint, TuiConfigInvalidStoreError, TuiController, TuiOperationBlockedError, } from './controller.js';
14
+ import { buildConfigGroups, buildConfigPage, projectPlatformPlacement, } from './model.js';
15
+ import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
16
+ import { calculateCatalogSourceConfigFingerprint, calculateCatalogSourceIdentity, } from '../utils/catalog-source-identity.js';
17
+ import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.js';
18
+ import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
19
+ import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
20
+ import { calculateCatalogGenerationSetDigest, resolveActiveCatalogGeneration, } from '../utils/catalog-generation-resolver.js';
21
+ import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
22
+ import { createCatalogStoreKey, readCatalogStoreEntry, } from '../utils/catalog-generation-store.js';
23
+ import { activateCatalogSourceGeneration, compareCatalogGenerationFreshness, probeCatalogSourceFreshness, probeCatalogSourceGeneration, probeCatalogSourceRepairGeneration, recoverCatalogGenerationStateAndActivate, repairCatalogSourceGeneration, replaceDamagedCatalogSourceGeneration, syncCatalogSourceGeneration, } from '../utils/catalog-generation-service.js';
24
+ import { calculateCatalogSourceImpactDigest, inspectCatalogSourceImpact, } from '../utils/catalog-source-impact.js';
25
+ import { readCatalogGenerationState } from '../utils/catalog-generation-state.js';
26
+ import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
27
+ import { CatalogProviderCompatibilityError } from '../utils/catalog-public-error.js';
28
+ import { loadCatalogGenerationViewModels } from './catalog-generation.js';
29
+ import { installPortableSkills, preparePortableInstall, PortableCommandError, } from '../portable/commands.js';
30
+ import { MANAGED_CODEX_PACKAGE_NAME, MANAGED_CODEX_VERSION, } from '../utils/codex-manager.js';
31
+ function reusableCandidateEvidenceIsComplete(candidate) {
32
+ const evidence = candidate.probe.candidate;
33
+ return [
34
+ evidence.sourceConfigFingerprint,
35
+ evidence.sourceIdentity,
36
+ evidence.logicalIdentity,
37
+ evidence.requestedRef,
38
+ evidence.resolvedCommit,
39
+ evidence.catalogVersion,
40
+ evidence.channel,
41
+ evidence.manifestDigest,
42
+ evidence.snapshotDigest,
43
+ ].every((value) => typeof value === 'string' && value.length > 0)
44
+ && typeof candidate.probe.manifest === 'object'
45
+ && candidate.probe.manifest !== null;
46
+ }
47
+ async function loadPortablePluginIds(options) {
48
+ return withResolvedMultiSourceCatalog({
49
+ cliVersion: options.cliVersion,
50
+ env: options.context.env,
51
+ signal: options.signal,
52
+ ...(options.catalogOperationContext
53
+ ? { operationContext: options.catalogOperationContext }
54
+ : {}),
55
+ consume: async (catalog) => (await Promise.all(catalog.sources.flatMap((item) => item.resolution
56
+ ? item.resolution.manifest.entries.flatMap((entry) => {
57
+ const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
58
+ return support.supported
59
+ ? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => plugin.manifest.distributions?.agentSkills
60
+ ? formatPluginId({
61
+ sourceId: item.resolution.sourceId,
62
+ pluginName: entry.name,
63
+ })
64
+ : null)]
65
+ : [];
66
+ })
67
+ : []))).filter((pluginId) => pluginId !== null),
68
+ });
69
+ }
70
+ let tuiControllerRuntimeTestHooks;
71
+ export function setTuiControllerRuntimeTestHooksForTests(hooks) {
72
+ tuiControllerRuntimeTestHooks = hooks;
73
+ }
74
+ function digest(value) {
75
+ return `sha256:${crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
76
+ }
77
+ export function statusTargetFingerprint(targets, revisions = []) {
78
+ return digest({
79
+ targets: targets.map((target) => ({
80
+ pluginId: target.pluginId,
81
+ sourceIdentity: target.sourceIdentity,
82
+ platform: target.platform,
83
+ scope: target.scope,
84
+ state: target.state,
85
+ errors: target.reasons.map((error) => error.code).sort(),
86
+ components: target.components.map((component) => [component.key, component.state])
87
+ .sort(([left], [right]) => left.localeCompare(right, 'en')),
88
+ })).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
89
+ .localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
90
+ revisions: revisions.map((entry) => ({
91
+ pluginId: entry.pluginId,
92
+ platform: entry.platform,
93
+ scope: entry.scope,
94
+ revision: entry.revision,
95
+ })).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
96
+ .localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
97
+ });
98
+ }
99
+ export function configAffectedStatusTargets(targets, pluginId, configScope) {
100
+ const affected = new Map();
101
+ for (const target of targets) {
102
+ const intent = statusTargetIntent(target);
103
+ if (target.pluginId !== pluginId || !intent.enabled)
104
+ continue;
105
+ for (const scope of intent.scopes) {
106
+ if (configScope === 'project' && scope !== 'project')
107
+ continue;
108
+ affected.set(`${target.platform}\0${scope}`, { ...target, scope });
109
+ }
110
+ }
111
+ return [...affected.values()]
112
+ .sort((left, right) => `${left.platform}\0${left.scope}`
113
+ .localeCompare(`${right.platform}\0${right.scope}`, 'en'));
114
+ }
115
+ function configSharedNativeRenderTargets(plugin, changedKeys) {
116
+ const changed = new Set(changedKeys);
117
+ return (plugin.manifest.artifacts?.targets ?? []).flatMap((target) => target.platform === 'codex'
118
+ && target.target === 'codex.skill'
119
+ && target.delivery.mode === 'codex-native'
120
+ && target.skillKey
121
+ && target.refreshKeys.some((key) => changed.has(key))
122
+ ? [{ id: target.id, skillKey: target.skillKey, refreshKeys: [...target.refreshKeys] }]
123
+ : []);
124
+ }
125
+ function operationReviewStale(message) {
126
+ return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`);
127
+ }
128
+ function sourceEvidenceForPlugin(source, pluginName) {
129
+ const entry = source.manifest.entries.find((candidate) => candidate.name === pluginName);
130
+ return {
131
+ sourceId: source.sourceId,
132
+ sourceIdentity: source.sourceIdentity,
133
+ requestedRef: source.requestedRef,
134
+ resolvedCommit: source.resolvedCommit,
135
+ manifestDigest: source.manifestDigest,
136
+ snapshotDigest: source.snapshotDigest,
137
+ contentDigest: entry.contentDigest,
138
+ pluginVersion: entry.version,
139
+ catalogVersion: source.manifest.catalogVersion,
140
+ channel: source.manifest.channel,
141
+ };
142
+ }
143
+ function assertSourceEvidenceMatches(expected, actual) {
144
+ if (actual.sourceId !== expected.sourceId
145
+ || actual.sourceIdentity !== expected.sourceIdentity
146
+ || actual.resolvedCommit !== expected.resolvedCommit
147
+ || actual.manifestDigest !== expected.manifestDigest
148
+ || actual.snapshotDigest !== expected.snapshotDigest
149
+ || actual.contentDigest !== expected.contentDigest) {
150
+ throw operationReviewStale('确认后来源内容发生变化。');
151
+ }
152
+ }
153
+ function throwIfWriteReviewStale(result) {
154
+ const stale = result.targetResults.find((target) => target.errorCode === 'operationReviewStale'
155
+ || target.errorCode === 'operationPlanStale');
156
+ if (stale) {
157
+ throw operationReviewStale('确认后实际写入计划发生变化。');
158
+ }
159
+ }
160
+ const SOURCE_PROGRESS_PHASES = {
161
+ savingConfig: '保存来源配置',
162
+ };
163
+ function pluginIntent(intent) {
164
+ return 'pluginId' in intent;
165
+ }
166
+ async function loadDependencyOverviews(options) {
167
+ const graph = await withResolvedMultiSourceCatalog({
168
+ cliVersion: options.cliVersion,
169
+ env: options.context.env,
170
+ signal: options.signal,
171
+ ...(options.catalogOperationContext
172
+ ? { operationContext: options.catalogOperationContext }
173
+ : {}),
174
+ consume: async (catalog) => ({
175
+ nodes: catalog.sources.flatMap((item) => item.resolution
176
+ ? item.resolution.manifest.entries.map((entry) => ({
177
+ pluginId: formatPluginId({
178
+ sourceId: item.resolution.sourceId,
179
+ pluginName: entry.name,
180
+ }),
181
+ displayName: entry.interface?.displayName ?? entry.name,
182
+ dependencies: entry.dependencies.map((pluginName) => formatPluginId({ sourceId: item.resolution.sourceId, pluginName })),
183
+ }))
184
+ : []),
185
+ sourceErrors: catalog.sources.flatMap((item) => item.error
186
+ ? [{ sourceId: item.sourceId, code: 'dependencyGraphInvalid' }]
187
+ : []),
188
+ }),
189
+ });
190
+ const nodes = new Map(graph.nodes.map((node) => [node.pluginId, node]));
191
+ const summaryById = new Map(options.status.pluginSummaries.map((summary) => [summary.pluginId, summary]));
192
+ const displayName = (pluginId) => nodes.get(pluginId)?.displayName
193
+ ?? parsePluginId(pluginId).pluginName;
194
+ const reverse = new Map();
195
+ for (const node of graph.nodes) {
196
+ for (const dependency of node.dependencies) {
197
+ reverse.set(dependency, [...(reverse.get(dependency) ?? []), node.pluginId]);
198
+ }
199
+ }
200
+ const targetsByPlugin = new Map();
201
+ for (const target of options.status.payload.targets) {
202
+ targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
203
+ }
204
+ const unsupportedPluginIds = new Set(options.status.pluginSummaries
205
+ .filter((summary) => (summary.missingCapabilities?.length ?? 0) > 0)
206
+ .map((summary) => summary.pluginId));
207
+ const placementReferences = new Map();
208
+ for (const consumer of options.status.payload.targets.filter((target) => target.state !== 'absent')) {
209
+ const consumerNode = nodes.get(consumer.pluginId);
210
+ if (!consumerNode)
211
+ continue;
212
+ for (const dependencyPluginId of consumerNode.dependencies) {
213
+ const dependencyTargets = (targetsByPlugin.get(dependencyPluginId) ?? [])
214
+ .filter((target) => target.platform === consumer.platform);
215
+ const projectTarget = dependencyTargets.find((target) => target.scope === 'project');
216
+ const userTarget = dependencyTargets.find((target) => target.scope === 'user');
217
+ const dependencyScope = consumer.scope === 'user'
218
+ ? 'user'
219
+ : userTarget && (!projectTarget
220
+ || (userTarget.state !== 'absent' && projectTarget.state === 'absent'))
221
+ ? 'user'
222
+ : 'project';
223
+ const reference = {
224
+ consumer: {
225
+ pluginId: consumer.pluginId,
226
+ platform: consumer.platform,
227
+ scope: consumer.scope,
228
+ project: consumer.scope === 'project' ? 'current' : null,
229
+ },
230
+ dependency: {
231
+ pluginId: dependencyPluginId,
232
+ platform: consumer.platform,
233
+ scope: dependencyScope,
234
+ project: dependencyScope === 'project' ? 'current' : null,
235
+ },
236
+ };
237
+ const key = `${consumer.pluginId}\0${dependencyPluginId}`;
238
+ placementReferences.set(key, [...(placementReferences.get(key) ?? []), reference]);
239
+ }
240
+ }
241
+ const placementsFor = (pluginId) => {
242
+ const targets = targetsByPlugin.get(pluginId) ?? [];
243
+ if (targets.length > 0) {
244
+ return ['codex', 'claude'].flatMap((platform) => {
245
+ const selected = targets.filter((target) => target.platform === platform);
246
+ return selected.length > 0 ? [projectPlatformPlacement(platform, selected)] : [];
247
+ });
248
+ }
249
+ return [];
250
+ };
251
+ const aggregateFor = (pluginId, placements) => {
252
+ const sourceId = parsePluginId(pluginId).sourceId;
253
+ if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
254
+ || unsupportedPluginIds.has(pluginId))
255
+ return 'unavailable';
256
+ if (placements.length === 0 || placements.every((placement) => placement.state === 'notInstalled')) {
257
+ return 'notInstalled';
258
+ }
259
+ return placements.some((placement) => placement.state === 'scopeConflict'
260
+ || (placement.state === 'installed'
261
+ && (placement.health !== 'ready' || placement.blockingReason !== undefined)))
262
+ ? 'needsAttention'
263
+ : 'ready';
264
+ };
265
+ const allPluginIds = new Set([
266
+ ...nodes.keys(),
267
+ ...reverse.keys(),
268
+ ...targetsByPlugin.keys(),
269
+ ]);
270
+ return [...allPluginIds].sort().map((pluginId) => {
271
+ const dependencies = [];
272
+ const seen = new Set([pluginId]);
273
+ let frontier = [pluginId];
274
+ let depth = 0;
275
+ let graphInvalid = false;
276
+ while (frontier.length > 0) {
277
+ depth += 1;
278
+ const next = [];
279
+ const requiredBy = new Map();
280
+ for (const parent of frontier) {
281
+ for (const dependency of nodes.get(parent)?.dependencies ?? []) {
282
+ const parents = requiredBy.get(dependency) ?? new Set();
283
+ parents.add(parent);
284
+ requiredBy.set(dependency, parents);
285
+ if (seen.has(dependency)) {
286
+ if (dependency === pluginId)
287
+ graphInvalid = true;
288
+ continue;
289
+ }
290
+ seen.add(dependency);
291
+ next.push(dependency);
292
+ }
293
+ }
294
+ for (const dependency of next) {
295
+ dependencies.push({ pluginId: dependency, depth, requiredBy: [...(requiredBy.get(dependency) ?? [])] });
296
+ }
297
+ frontier = next;
298
+ }
299
+ const dependencyItems = dependencies.map((dependency) => {
300
+ const placements = placementsFor(dependency.pluginId);
301
+ return {
302
+ pluginId: dependency.pluginId,
303
+ displayName: displayName(dependency.pluginId),
304
+ relation: dependency.depth === 1 ? 'direct' : 'transitive',
305
+ depth: dependency.depth,
306
+ requiredBy: dependency.requiredBy.map((requiredBy) => ({
307
+ pluginId: requiredBy,
308
+ displayName: displayName(requiredBy),
309
+ })),
310
+ placements,
311
+ aggregateStatus: aggregateFor(dependency.pluginId, placements),
312
+ placementReferences: placementReferences.get(`${pluginId}\0${dependency.pluginId}`) ?? [],
313
+ };
314
+ });
315
+ const dependentIds = [...new Set(reverse.get(pluginId) ?? [])].sort();
316
+ const dependents = dependentIds.map((dependentId) => {
317
+ const placements = placementsFor(dependentId);
318
+ return {
319
+ pluginId: dependentId,
320
+ displayName: displayName(dependentId),
321
+ relation: 'dependent',
322
+ depth: 1,
323
+ requiredBy: [],
324
+ placements,
325
+ aggregateStatus: aggregateFor(dependentId, placements),
326
+ placementReferences: placementReferences.get(`${dependentId}\0${pluginId}`) ?? [],
327
+ };
328
+ });
329
+ const targetBlockers = (targetsByPlugin.get(pluginId) ?? []).flatMap((target) => target.reasons.filter((error) => error.code.startsWith('dependency') || error.code === 'sourceChanged')
330
+ .map((error) => ({
331
+ code: error.code,
332
+ title: '依赖关系需要处理',
333
+ explanation: '当前目标的依赖证据不能安全通过检查。',
334
+ })));
335
+ const consumerTargets = summaryById.get(pluginId)?.supportedTargets;
336
+ const unsupportedDependency = dependencyItems.find((dependency) => {
337
+ const dependencyTargets = summaryById.get(dependency.pluginId)?.supportedTargets;
338
+ if (!consumerTargets || !dependencyTargets)
339
+ return true;
340
+ return ['codex', 'claude'].some((platform) => consumerTargets[platform].length > 0 && dependencyTargets[platform].length === 0);
341
+ });
342
+ const sourceId = parsePluginId(pluginId).sourceId;
343
+ const blockers = [
344
+ ...graph.sourceErrors.filter((error) => error.sourceId === sourceId).map((error) => ({
345
+ code: error.code,
346
+ title: '来源依赖关系不可用',
347
+ explanation: '当前来源快照不能安全读取。反向安装记录仍可显示。',
348
+ })),
349
+ ...(graphInvalid ? [{
350
+ code: 'dependencyGraphInvalid',
351
+ title: '依赖关系无效',
352
+ explanation: '依赖关系形成环。',
353
+ }] : []),
354
+ ...(unsupportedDependency ? [{
355
+ code: 'dependencyTargetUnsupported',
356
+ title: '依赖目标不兼容',
357
+ explanation: `${unsupportedDependency.displayName} 不支持根插件所需的平台。`,
358
+ }] : []),
359
+ ...targetBlockers,
360
+ ];
361
+ const directCount = dependencyItems.filter((item) => item.relation === 'direct').length;
362
+ const readyCount = dependencyItems.filter((item) => item.aggregateStatus === 'ready').length;
363
+ const missingCount = dependencyItems.filter((item) => item.aggregateStatus === 'notInstalled').length;
364
+ const issueCount = dependencyItems.length - readyCount - missingCount + blockers.length;
365
+ return {
366
+ pluginId,
367
+ summary: {
368
+ directCount,
369
+ transitiveCount: dependencyItems.length - directCount,
370
+ totalCount: dependencyItems.length,
371
+ readyCount,
372
+ missingCount,
373
+ issueCount,
374
+ },
375
+ dependencies: dependencyItems,
376
+ dependents,
377
+ blockers,
378
+ };
379
+ });
380
+ }
381
+ function createReviewStageReporter(options) {
382
+ let active;
383
+ const finish = async () => {
384
+ if (!active)
385
+ return;
386
+ const completed = active;
387
+ active = undefined;
388
+ await Promise.allSettled([Promise.resolve().then(() => options.context.debugLogger.log({
389
+ type: `review.prepare.${completed.stage}`,
390
+ level: 'debug',
391
+ target: 'debugLog',
392
+ message: 'TUI review preparation stage completed.',
393
+ data: { durationMs: Math.max(0, options.now() - completed.startedAt) },
394
+ }))]);
395
+ };
396
+ return {
397
+ async emit(stage) {
398
+ await finish();
399
+ active = { stage, startedAt: options.now() };
400
+ await options.onStage(stage);
401
+ },
402
+ finish,
403
+ };
404
+ }
405
+ export function createTuiControllerDependencies(context, progressFeed, options = {}) {
406
+ const readInventory = options.inventoryCatalogSourceReferences
407
+ ?? inventoryCatalogSourceReferences;
408
+ const validatedStoreEntries = new Map();
409
+ const validatingStoreEntries = new Map();
410
+ const loadCatalogGenerations = async () => {
411
+ const generations = await loadCatalogGenerationViewModels({
412
+ env: context.env,
413
+ validatedStoreEntries,
414
+ validatingStoreEntries,
415
+ onStage: (event) => context.debugLogger.log({
416
+ type: 'performance:stage',
417
+ level: 'debug',
418
+ target: 'debugLog',
419
+ message: 'Catalog generation load stage completed.',
420
+ data: event,
421
+ }),
422
+ });
423
+ const retained = new Set(generations.flatMap((source) => [
424
+ source.active?.storeKey,
425
+ ].filter((value) => value !== undefined)));
426
+ for (const storeKey of validatedStoreEntries.keys()) {
427
+ if (!retained.has(storeKey))
428
+ validatedStoreEntries.delete(storeKey);
429
+ }
430
+ return generations;
431
+ };
432
+ const createOperationInventoryReader = () => {
433
+ let ordinaryInventory;
434
+ return (inventoryOptions) => {
435
+ ordinaryInventory ??= readInventory(inventoryOptions);
436
+ return ordinaryInventory;
437
+ };
438
+ };
439
+ const catalogOperationByEvidence = new WeakMap();
440
+ let sessionCatalogOperationContext;
441
+ let sessionStatusResult;
442
+ const sessionContextForPlugin = (pluginId) => {
443
+ const sourceId = parsePluginId(pluginId).sourceId;
444
+ if (!sourceId || !sessionCatalogOperationContext)
445
+ return undefined;
446
+ return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
447
+ };
448
+ const readConfigStoresForTui = async (config) => {
449
+ const scopes = config.scope === 'user' ? ['user'] : ['user', 'project'];
450
+ const stores = await Promise.all(scopes.map((scope) => readPluginConfigStore({
451
+ sourceId: config.sourceId,
452
+ sourceIdentity: config.sourceIdentity,
453
+ pluginName: config.pluginName,
454
+ scope,
455
+ cwd: context.cwd,
456
+ env: context.env,
457
+ schema: config.schema,
458
+ })));
459
+ const user = stores[0];
460
+ const project = config.scope === 'user' ? undefined : stores[1];
461
+ config.signal.throwIfAborted();
462
+ const invalidStores = ([
463
+ { scope: 'user', store: user },
464
+ ...(project ? [{ scope: 'project', store: project }] : []),
465
+ ]).flatMap(({ scope, store }) => store.state === 'invalid' ? [{
466
+ scope,
467
+ code: store.invalidCode,
468
+ pathSummary: scope === 'user'
469
+ ? `AW_HOME/plugin-config/${config.sourceId ?? '<source>'}/${config.pluginName}/config.json`
470
+ : `<project>/.agent-workflows/plugin-config/${config.sourceId ?? '<source>'}/${config.pluginName}/config.json`,
471
+ }] : []);
472
+ if (invalidStores.length > 0)
473
+ throw new TuiConfigInvalidStoreError(invalidStores);
474
+ return { user, ...(project ? { project } : {}) };
475
+ };
476
+ const loadStatus = async (signal) => {
477
+ signal.throwIfAborted();
478
+ const packageInfo = await readCliPackageInfo();
479
+ const catalogOperationContext = sessionCatalogOperationContext
480
+ ?? await resolvePartialCatalogOperationContext({
481
+ env: context.env,
482
+ signal,
483
+ validatedStoreEntries,
484
+ validatingStoreEntries,
485
+ }).catch(async (error) => {
486
+ if (!(error instanceof CatalogGenerationError)
487
+ || error.code !== 'catalogGenerationStateInvalid')
488
+ return undefined;
489
+ const snapshot = await readCatalogSourcesConfigSnapshot(context.env);
490
+ const primarySourceId = snapshot.config.sources.find((source) => source.primary).id;
491
+ const generationSetDigest = calculateCatalogGenerationSetDigest(snapshot.digest, primarySourceId, []);
492
+ return {
493
+ generationSet: {
494
+ generationSetDigest,
495
+ configDigest: snapshot.digest,
496
+ primarySourceId,
497
+ sources: [],
498
+ },
499
+ generationSetDigest,
500
+ catalog: {
501
+ sources: snapshot.config.sources.map((source) => ({
502
+ sourceId: source.id,
503
+ error,
504
+ })),
505
+ primarySourceId,
506
+ configDigest: snapshot.digest,
507
+ compositeDigest: generationSetDigest,
508
+ },
509
+ };
510
+ });
511
+ if (catalogOperationContext)
512
+ sessionCatalogOperationContext = catalogOperationContext;
513
+ for (let attempt = 0; attempt < 3; attempt += 1) {
514
+ const result = await statusCommandV7({
515
+ cwd: context.cwd,
516
+ env: context.env,
517
+ cliVersion: packageInfo.version,
518
+ verbose: context.diagnostics.verbose,
519
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
520
+ inventoryCatalogSourceReferences: readInventory,
521
+ });
522
+ signal.throwIfAborted();
523
+ const configured = await listCatalogSources({ env: context.env });
524
+ signal.throwIfAborted();
525
+ if (result.payload.catalog.configDigest !== configured.configDigest)
526
+ continue;
527
+ sessionStatusResult = result;
528
+ const dependencyOverviews = await loadDependencyOverviews({
529
+ context,
530
+ cliVersion: packageInfo.version,
531
+ status: result,
532
+ signal,
533
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
534
+ });
535
+ const portablePluginIds = await loadPortablePluginIds({
536
+ context,
537
+ cliVersion: packageInfo.version,
538
+ signal,
539
+ ...(catalogOperationContext ? { catalogOperationContext } : {}),
540
+ });
541
+ signal.throwIfAborted();
542
+ const catalogGenerations = await loadCatalogGenerations();
543
+ signal.throwIfAborted();
544
+ const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
545
+ return {
546
+ cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
547
+ payload: result.payload,
548
+ pluginSummaries: result.pluginSummaries,
549
+ targetRevisions: result.targetRevisions,
550
+ dependencyOverviews,
551
+ portablePluginIds,
552
+ catalogGenerations,
553
+ sessionGenerationKeys: Object.fromEntries((catalogOperationContext?.generationSet.sources ?? [])
554
+ .map((source) => [source.sourceId, source.storeKey])),
555
+ sourceDetails: configured.sources.map((source) => {
556
+ const status = statusById.get(source.id);
557
+ return {
558
+ id: source.id,
559
+ type: source.type,
560
+ primary: source.primary,
561
+ autoCheck: source.autoCheck,
562
+ locationSummary: projectCatalogSourceLocation(source.type === 'git' ? source.url : source.path),
563
+ ...(source.type === 'git' ? {
564
+ refPolicy: source.refPolicy.type,
565
+ ...(source.refPolicy.type === 'explicit' ? { explicitRef: source.refPolicy.ref } : {}),
566
+ ...(source.subdir ? { subdir: source.subdir } : {}),
567
+ } : {}),
568
+ pluginCount: 0,
569
+ sourceIdentity: source.sourceIdentity,
570
+ requestedRef: status?.requestedRef ?? (source.type === 'path' ? 'path' : 'AW channel'),
571
+ ...(status?.resolvedCommit ? { resolvedCommit: status.resolvedCommit } : {}),
572
+ ...(status?.catalogVersion ? { catalogVersion: status.catalogVersion } : {}),
573
+ ...(status?.channel ? { channel: status.channel } : {}),
574
+ ...(status?.freshness ? { freshness: status.freshness } : {}),
575
+ ...(status?.errorCode ? { errorCode: status.errorCode } : {}),
576
+ configDigest: configured.configDigest,
577
+ };
578
+ }),
579
+ };
580
+ }
581
+ throw new TuiOperationBlockedError('operationReviewStale', '状态读取期间来源配置持续变化。请重试。');
582
+ };
583
+ const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false, reusableCandidate) => {
584
+ if (intent.kind === 'configReset') {
585
+ const parsed = parsePluginId(intent.pluginId);
586
+ if (!parsed.sourceId || !parsed.pluginName) {
587
+ throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
588
+ }
589
+ await preparation?.reporter.emit('config');
590
+ const target = await inspectPluginConfigResetTarget({
591
+ sourceId: parsed.sourceId,
592
+ pluginName: parsed.pluginName,
593
+ scope: intent.scope,
594
+ cwd: context.cwd,
595
+ env: context.env,
596
+ });
597
+ signal?.throwIfAborted();
598
+ if (target.kind === 'special') {
599
+ throw new TuiOperationBlockedError('configResetTargetUnsupported', '设置目标是特殊文件系统节点,不能自动重置。请手动移动后重试。');
600
+ }
601
+ let source;
602
+ try {
603
+ const packageInfo = await readCliPackageInfo();
604
+ source = await withResolvedCatalogPlugin({
605
+ selector: intent.pluginId,
606
+ cliVersion: packageInfo.version,
607
+ env: context.env,
608
+ ...(preparation && sessionContextForPlugin(intent.pluginId)
609
+ ? { operationContext: sessionContextForPlugin(intent.pluginId) }
610
+ : {}),
611
+ ...(signal ? { signal } : {}),
612
+ consume: async ({ plugin, source: resolvedSource }) => sourceEvidenceForPlugin(resolvedSource, plugin.manifest.name),
613
+ });
614
+ }
615
+ catch {
616
+ source = undefined;
617
+ }
618
+ signal?.throwIfAborted();
619
+ const relevantTargets = sessionStatusResult
620
+ ? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope)
621
+ : [];
622
+ await preparation?.reporter.emit('evidence');
623
+ return {
624
+ catalog: sessionStatusResult ? {
625
+ configDigest: sessionStatusResult.payload.catalog.configDigest,
626
+ compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
627
+ } : {
628
+ configDigest: 'config-reset-offline',
629
+ compositeDigest: 'config-reset-target',
630
+ },
631
+ ...(source ? { source } : {}),
632
+ config: {
633
+ scope: intent.scope,
634
+ storeDigest: target.fingerprint,
635
+ affectedTargets: relevantTargets.map(({ platform, scope }) => ({ platform, scope })),
636
+ },
637
+ resetTarget: {
638
+ sourceId: parsed.sourceId,
639
+ pluginName: parsed.pluginName,
640
+ scope: intent.scope,
641
+ kind: target.kind,
642
+ fingerprint: target.fingerprint,
643
+ },
644
+ };
645
+ }
646
+ if (intent.kind === 'catalogSourcesRecovery') {
647
+ signal?.throwIfAborted();
648
+ await preparation?.reporter.emit('validate');
649
+ await preparation?.reporter.emit('evidence');
650
+ return {
651
+ catalog: {
652
+ configDigest: 'invalid-catalog-sources-config',
653
+ compositeDigest: 'bootstrap-recovery',
654
+ },
655
+ };
656
+ }
657
+ if (intent.kind === 'catalogGeneration') {
658
+ signal?.throwIfAborted();
659
+ await preparation?.reporter.emit('validate');
660
+ const packageInfo = await readCliPackageInfo();
661
+ const config = await readCatalogSourcesConfigSnapshot(context.env);
662
+ const configuredSource = config.config.sources.find((source) => source.id === intent.sourceId);
663
+ let proposedSource;
664
+ let proposedConfigDigest;
665
+ let explicitSourceChange = false;
666
+ let sourceIdentityChangeImpact;
667
+ if (intent.sourceMutation) {
668
+ const preview = await preflightCatalogSourceMutation({
669
+ request: intent.sourceMutation.action === 'add'
670
+ ? intent.sourceMutation
671
+ : { action: 'update', sourceId: intent.sourceId, update: intent.sourceMutation.update },
672
+ cwd: context.cwd,
673
+ env: context.env,
674
+ });
675
+ proposedSource = preview.proposedSource;
676
+ proposedConfigDigest = preview.proposedConfigDigest;
677
+ if (preview.configDigest !== config.digest) {
678
+ throw operationReviewStale('准备 Catalog 候选期间来源配置发生变化。');
679
+ }
680
+ const sourceFingerprintChanged = Boolean(proposedSource && (!configuredSource
681
+ || calculateCatalogSourceConfigFingerprint(proposedSource)
682
+ !== calculateCatalogSourceConfigFingerprint(configuredSource)));
683
+ explicitSourceChange = preview.proposedConfigDigest !== preview.configDigest
684
+ && (preview.identityChanged || sourceFingerprintChanged);
685
+ if (preview.identityChanged)
686
+ sourceIdentityChangeImpact = {
687
+ impactSchemaVersion: 1,
688
+ sourceId: preview.sourceId,
689
+ inventoryComplete: preview.references.inventoryComplete,
690
+ references: preview.references.references,
691
+ affectedReferences: preview.references.references,
692
+ changes: { added: [], removed: [], changed: [], newlyIncompatible: [], dependencyChanged: [] },
693
+ errors: preview.references.errors,
694
+ };
695
+ if (!explicitSourceChange) {
696
+ throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
697
+ }
698
+ }
699
+ let previousActive;
700
+ let healthyActive;
701
+ let previousActiveVersion;
702
+ let previousStoreReady = false;
703
+ try {
704
+ previousActive = (await readCatalogGenerationState(context.env))
705
+ .sources[intent.sourceId]?.active;
706
+ }
707
+ catch (error) {
708
+ if (intent.action !== 'recoverState'
709
+ || !(error instanceof CatalogGenerationError)
710
+ || error.code !== 'catalogGenerationStateInvalid')
711
+ throw error;
712
+ }
713
+ const activeStoreKey = previousActive
714
+ ? createCatalogStoreKey(previousActive.logicalIdentity, previousActive.snapshotDigest)
715
+ : null;
716
+ const configuredFingerprint = configuredSource
717
+ ? calculateCatalogSourceConfigFingerprint(configuredSource)
718
+ : undefined;
719
+ const canReuseCandidate = Boolean(reusableCandidate
720
+ && !intent.sourceMutation
721
+ && (intent.action === 'activate' || intent.action === 'update')
722
+ && configuredFingerprint
723
+ && reusableCandidate.sourceId === intent.sourceId
724
+ && reusableCandidate.sourceConfigFingerprint === configuredFingerprint
725
+ && reusableCandidate.probe.candidate.sourceConfigFingerprint === configuredFingerprint
726
+ && reusableCandidate.activeStoreKey === activeStoreKey
727
+ && reusableCandidate.probe.sourceId === intent.sourceId
728
+ && reusableCandidateEvidenceIsComplete(reusableCandidate));
729
+ const probe = intent.action === 'repair'
730
+ ? await probeCatalogSourceRepairGeneration({
731
+ sourceId: intent.sourceId,
732
+ cliVersion: packageInfo.version,
733
+ ...(previousActive ? { expectedActive: previousActive } : {}),
734
+ expectedConfigDigest: config.digest,
735
+ env: context.env,
736
+ signal,
737
+ runtime: options.catalogGenerationRuntime,
738
+ })
739
+ : canReuseCandidate
740
+ ? structuredClone(reusableCandidate.probe)
741
+ : await probeCatalogSourceGeneration({
742
+ sourceId: intent.sourceId,
743
+ ...(proposedSource ? { source: proposedSource } : {}),
744
+ cliVersion: packageInfo.version,
745
+ env: context.env,
746
+ signal,
747
+ runtime: options.catalogGenerationRuntime,
748
+ });
749
+ signal?.throwIfAborted();
750
+ await preparation?.reporter.emit('evidence');
751
+ if (intent.action === 'activate' || intent.action === 'update') {
752
+ try {
753
+ healthyActive = await resolveActiveCatalogGeneration({ sourceId: intent.sourceId, env: context.env });
754
+ }
755
+ catch (error) {
756
+ if (!(error instanceof CatalogGenerationError)
757
+ && !(error instanceof CatalogProviderCompatibilityError))
758
+ throw error;
759
+ }
760
+ }
761
+ const comparisonSource = proposedSource
762
+ ?? configuredSource;
763
+ if (previousActive) {
764
+ const store = await readCatalogStoreEntry({
765
+ logicalIdentity: previousActive.logicalIdentity,
766
+ snapshotDigest: previousActive.snapshotDigest,
767
+ env: context.env,
768
+ }).catch(() => undefined);
769
+ previousStoreReady = Boolean(store);
770
+ previousActiveVersion = healthyActive?.catalogVersion
771
+ ?? store?.metadata.catalogVersion;
772
+ }
773
+ if (intent.action === 'update' && !intent.sourceMutation && previousActive && comparisonSource) {
774
+ explicitSourceChange = previousActive.sourceConfigFingerprint
775
+ !== calculateCatalogSourceConfigFingerprint(comparisonSource)
776
+ || previousActive.sourceIdentity !== await calculateCatalogSourceIdentity(comparisonSource);
777
+ }
778
+ const comparisonActive = healthyActive
779
+ ? {
780
+ logicalIdentity: healthyActive.logicalIdentity,
781
+ catalogVersion: healthyActive.catalogVersion,
782
+ snapshotDigest: healthyActive.snapshotDigest,
783
+ resolvedCommit: healthyActive.resolvedCommit,
784
+ }
785
+ : previousActive && previousActiveVersion
786
+ ? {
787
+ logicalIdentity: previousActive.logicalIdentity,
788
+ catalogVersion: previousActiveVersion,
789
+ snapshotDigest: previousActive.snapshotDigest,
790
+ resolvedCommit: previousActive.resolvedCommit,
791
+ }
792
+ : undefined;
793
+ const candidateRelation = comparisonActive && comparisonSource
794
+ ? compareCatalogGenerationFreshness({
795
+ sourceType: comparisonSource.type,
796
+ active: comparisonActive,
797
+ candidate: probe.candidate,
798
+ }).state
799
+ : undefined;
800
+ const exactStoreTarget = Boolean(previousActive)
801
+ && createCatalogStoreKey(previousActive.logicalIdentity, previousActive.snapshotDigest)
802
+ === createCatalogStoreKey(probe.candidate.logicalIdentity, probe.candidate.snapshotDigest);
803
+ if (intent.action === 'repair' && !exactStoreTarget) {
804
+ throw new CatalogGenerationError({
805
+ code: 'catalogReplaceDamagedRequired', sourceId: intent.sourceId,
806
+ message: `Catalog source "${intent.sourceId}" cannot reproduce the damaged Active entry. Use the current candidate replacement Review.`,
807
+ });
808
+ }
809
+ const exactRepair = (intent.action === 'repair' || intent.action === 'update')
810
+ && exactStoreTarget && !previousStoreReady;
811
+ if (previousActive && !previousStoreReady && !exactStoreTarget
812
+ && !intent.sourceMutation && intent.action !== 'replaceDamaged') {
813
+ throw new CatalogGenerationError({
814
+ code: 'catalogReplaceDamagedRequired',
815
+ sourceId: intent.sourceId,
816
+ message: `Catalog source "${intent.sourceId}" has a damaged Active entry whose version cannot be compared safely. The current Active remains unchanged. Edit the source explicitly, or remove and add it again.`,
817
+ });
818
+ }
819
+ if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'incompatible') {
820
+ throw new CatalogGenerationError({
821
+ code: 'catalogLogicalIdentityChanged', sourceId: intent.sourceId,
822
+ message: `Catalog source "${intent.sourceId}" changed logical identity. Edit or remove and re-add the source.`,
823
+ });
824
+ }
825
+ if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'versionConflict') {
826
+ throw new TuiOperationBlockedError('catalogVersionConflict', '候选 Catalog 与当前 Active 版本相同,但内容摘要不同。普通更新已阻止。');
827
+ }
828
+ if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'rollbackDetected') {
829
+ throw new TuiOperationBlockedError('catalogRollbackDetected', '候选 Catalog 版本低于当前 Active。普通更新已阻止。');
830
+ }
831
+ const impact = intent.action === 'repair' ? undefined
832
+ : sourceIdentityChangeImpact ?? await inspectCatalogSourceImpact({
833
+ sourceId: intent.sourceId,
834
+ cwd: context.cwd,
835
+ env: context.env,
836
+ sourcePrimary: comparisonSource?.primary === true,
837
+ mode: 'generation',
838
+ candidateManifest: probe.manifest,
839
+ });
840
+ return {
841
+ catalog: {
842
+ configDigest: config.digest,
843
+ compositeDigest: probe.candidate.snapshotDigest,
844
+ },
845
+ catalogGeneration: {
846
+ action: intent.action,
847
+ ...(previousActive ? { previousActive } : {}),
848
+ ...(previousActiveVersion ? { previousActiveVersion } : {}),
849
+ candidate: probe.candidate,
850
+ ...(candidateRelation ? { candidateRelation } : {}),
851
+ ...(explicitSourceChange ? { explicitSourceChange: true } : {}),
852
+ ...(exactRepair ? { exactRepair: true } : {}),
853
+ activationMode: intent.action === 'replaceDamaged' ? 'replaceDamaged'
854
+ : explicitSourceChange ? 'explicitSourceChange' : 'normal',
855
+ ...(impact ? {
856
+ impact,
857
+ impactDigest: calculateCatalogSourceImpactDigest(impact),
858
+ impactMode: sourceIdentityChangeImpact ? 'identityChange' : 'generation',
859
+ } : {}),
860
+ configDigest: config.digest,
861
+ ...(proposedConfigDigest ? { proposedConfigDigest } : {}),
862
+ ...(proposedSource ? { proposedSource } : {}),
863
+ ...(intent.sourceMutation ? { sourceMutation: intent.sourceMutation } : {}),
864
+ ...(intent.setPrimaryAfterActivation ? { setPrimaryAfterActivation: true } : {}),
865
+ },
866
+ };
867
+ }
868
+ if (intent.kind === 'sourceMutation') {
869
+ signal?.throwIfAborted();
870
+ if (intent.action === 'add' && !intent.source
871
+ || intent.action === 'update' && !intent.update) {
872
+ throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
873
+ }
874
+ const request = intent.action === 'add'
875
+ ? { action: 'add', source: intent.source }
876
+ : intent.action === 'update'
877
+ ? { action: 'update', sourceId: intent.sourceId, update: intent.update }
878
+ : { action: intent.action, sourceId: intent.sourceId };
879
+ await preparation?.reporter.emit('validate');
880
+ const preview = await preflightCatalogSourceMutation({
881
+ request,
882
+ cwd: context.cwd,
883
+ env: context.env,
884
+ });
885
+ signal?.throwIfAborted();
886
+ await preparation?.reporter.emit('evidence');
887
+ signal?.throwIfAborted();
888
+ const evidence = {
889
+ catalog: { configDigest: preview.configDigest, compositeDigest: preview.proposedConfigDigest },
890
+ sourceWrite: {
891
+ configDigest: preview.configDigest,
892
+ proposedConfigDigest: preview.proposedConfigDigest,
893
+ ...(preview.sourceIdentity ? { sourceIdentity: preview.sourceIdentity } : {}),
894
+ inventoryDigest: digest({ references: preview.references.references, errors: preview.references.errors }),
895
+ impactDigest: preview.impactDigest,
896
+ referenceCount: preview.references.references.length,
897
+ inventoryComplete: preview.references.inventoryComplete,
898
+ impact: {
899
+ impactSchemaVersion: 1,
900
+ sourceId: preview.sourceId,
901
+ inventoryComplete: preview.references.inventoryComplete,
902
+ references: preview.references.references,
903
+ affectedReferences: preview.references.references,
904
+ changes: { added: [], removed: [], changed: [], newlyIncompatible: [], dependencyChanged: [] },
905
+ errors: preview.references.errors,
906
+ },
907
+ },
908
+ };
909
+ if (preparation?.validateIntent
910
+ && evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
911
+ throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
912
+ }
913
+ return evidence;
914
+ }
915
+ if (!pluginIntent(intent)) {
916
+ throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
917
+ }
918
+ signal?.throwIfAborted();
919
+ const packageInfo = await readCliPackageInfo();
920
+ const offlineCleanupReason = intent.kind === 'pluginWrite'
921
+ && intent.action === 'cleanup'
922
+ && intent.originIssue?.actionId === 'cleanup'
923
+ && (intent.originIssue?.category === 'sourceMissing'
924
+ || intent.originIssue?.category === 'sourceUnavailable')
925
+ ? intent.originIssue.category === 'sourceMissing' ? 'sourceMissing' : 'bundleUnavailable'
926
+ : undefined;
927
+ let catalogOperation;
928
+ let catalogOperationError;
929
+ if (!offlineCleanupReason && intent.kind !== 'portableInstall') {
930
+ try {
931
+ const sourceIds = [parsePluginId(intent.pluginId).sourceId];
932
+ catalogOperation = !freshCatalog && sessionCatalogOperationContext
933
+ ? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
934
+ : await resolveCatalogOperationContext({
935
+ sourceIds,
936
+ env: context.env,
937
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
938
+ ...(signal ? { signal } : {}),
939
+ });
940
+ }
941
+ catch (error) {
942
+ catalogOperationError = error;
943
+ }
944
+ }
945
+ if (intent.kind === 'portableInstall') {
946
+ await preparation?.reporter.emit('portable');
947
+ const portableSourceId = parsePluginId(intent.pluginId).sourceId;
948
+ const portableCatalogOperation = sessionCatalogOperationContext
949
+ ? selectCatalogOperationContext(sessionCatalogOperationContext, [portableSourceId])
950
+ : undefined;
951
+ const portable = await preparePortableInstall({
952
+ selector: intent.pluginId,
953
+ scope: intent.scope,
954
+ ...(intent.selectedSkills.length > 0 ? { requestedSkills: intent.selectedSkills } : {}),
955
+ ...(intent.requestedAgents ? { requestedAgents: intent.requestedAgents } : {}),
956
+ cwd: context.cwd,
957
+ env: context.env,
958
+ cliVersion: packageInfo.version,
959
+ signal,
960
+ ...(portableCatalogOperation
961
+ ? { catalogOperationContext: portableCatalogOperation }
962
+ : {}),
963
+ });
964
+ signal?.throwIfAborted();
965
+ await preparation?.reporter.emit('evidence');
966
+ signal?.throwIfAborted();
967
+ const evidence = {
968
+ catalog: {
969
+ configDigest: portable.configDigest,
970
+ compositeDigest: portable.evidenceDigest,
971
+ ...(portable.generationSetDigest
972
+ ? { generationSetDigest: portable.generationSetDigest }
973
+ : {}),
974
+ },
975
+ portable,
976
+ };
977
+ if (catalogOperation)
978
+ catalogOperationByEvidence.set(evidence, catalogOperation);
979
+ return evidence;
980
+ }
981
+ if (preparation && intent.kind === 'configSave') {
982
+ if (!sessionStatusResult || !catalogOperation) {
983
+ throw operationReviewStale('当前 TUI session 没有可复用的设置证据。');
984
+ }
985
+ await preparation.reporter.emit('config');
986
+ const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
987
+ signal?.throwIfAborted();
988
+ if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
989
+ throw operationReviewStale('准备设置确认期间来源配置发生变化。');
990
+ }
991
+ const selected = await withResolvedCatalogPlugin({
992
+ selector: intent.pluginId,
993
+ cliVersion: packageInfo.version,
994
+ configSnapshot,
995
+ env: context.env,
996
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
997
+ ...(signal ? { signal } : {}),
998
+ consume: async ({ plugin, source }) => {
999
+ if (!plugin.manifest.config) {
1000
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
1001
+ }
1002
+ if (intent.kind === 'configSave') {
1003
+ await coercePluginConfigUpdates({
1004
+ schema: plugin.manifest.config.fields,
1005
+ updates: intent.updates ?? {},
1006
+ cwd: context.cwd,
1007
+ });
1008
+ for (const key of intent.removeKeys ?? []) {
1009
+ if (!plugin.manifest.config.fields[key]) {
1010
+ throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
1011
+ }
1012
+ }
1013
+ }
1014
+ return {
1015
+ pluginName: plugin.manifest.name,
1016
+ source: sourceEvidenceForPlugin(source, plugin.manifest.name),
1017
+ sharedNativeRenderTargets: intent.kind === 'configSave'
1018
+ ? configSharedNativeRenderTargets(plugin, [
1019
+ ...Object.keys(intent.updates ?? {}),
1020
+ ...(intent.removeKeys ?? []),
1021
+ ])
1022
+ : [],
1023
+ };
1024
+ },
1025
+ });
1026
+ const store = await readPluginConfigStore({
1027
+ sourceId: selected.source?.sourceId ?? parsePluginId(intent.pluginId).sourceId,
1028
+ ...(selected.source?.sourceIdentity
1029
+ ? { sourceIdentity: selected.source.sourceIdentity }
1030
+ : {}),
1031
+ pluginName: selected.pluginName,
1032
+ scope: intent.scope,
1033
+ cwd: context.cwd,
1034
+ env: context.env,
1035
+ });
1036
+ signal?.throwIfAborted();
1037
+ const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope);
1038
+ const relevantRevisions = (sessionStatusResult.targetRevisions ?? []).filter((revision) => relevantTargets.some((target) => target.platform === revision.platform
1039
+ && target.scope === revision.scope
1040
+ && target.pluginId === revision.pluginId));
1041
+ await preparation.reporter.emit('evidence');
1042
+ const evidence = {
1043
+ catalog: {
1044
+ configDigest: sessionStatusResult.payload.catalog.configDigest,
1045
+ compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
1046
+ ...(catalogOperation ? { generationSetDigest: catalogOperation.generationSetDigest } : {}),
1047
+ },
1048
+ ...(selected.source ? { source: selected.source } : {}),
1049
+ targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
1050
+ config: {
1051
+ scope: intent.scope,
1052
+ ...(selected.source?.manifestDigest ? { schemaIdentity: selected.source.manifestDigest } : {}),
1053
+ storeDigest: digest({ exists: store.exists, rawDigest: store.rawDigest }),
1054
+ affectedTargets: relevantTargets.map(({ platform, scope }) => ({ platform, scope })),
1055
+ ...(selected.sharedNativeRenderTargets.length > 0
1056
+ ? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
1057
+ : {}),
1058
+ },
1059
+ };
1060
+ if (catalogOperation)
1061
+ catalogOperationByEvidence.set(evidence, catalogOperation);
1062
+ return evidence;
1063
+ }
1064
+ await preparation?.reporter.emit('status');
1065
+ const statusCatalogOperation = offlineCleanupReason
1066
+ ? undefined
1067
+ : catalogOperation ?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
1068
+ const status = await statusCommandV7({
1069
+ plugins: [...new Set([intent.pluginId,
1070
+ ...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
1071
+ ? [intent.originIssue.consumerPluginId] : [])])],
1072
+ ...('platform' in intent ? { platform: intent.platform } : {}),
1073
+ ...(intent.kind !== 'pluginWrite' && intent.scope === 'project'
1074
+ ? { scope: intent.scope }
1075
+ : {}),
1076
+ cwd: context.cwd,
1077
+ env: context.env,
1078
+ cliVersion: packageInfo.version,
1079
+ ...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
1080
+ inventoryCatalogSourceReferences: operationInventory,
1081
+ });
1082
+ signal?.throwIfAborted();
1083
+ const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
1084
+ signal?.throwIfAborted();
1085
+ if (configSnapshot.digest !== status.payload.catalog.configDigest) {
1086
+ throw operationReviewStale('准备确认信息期间来源配置发生变化。');
1087
+ }
1088
+ if (catalogOperationError)
1089
+ throw catalogOperationError;
1090
+ if (!offlineCleanupReason && !catalogOperation) {
1091
+ catalogOperation = await resolveCatalogOperationContext({
1092
+ sourceIds: [parsePluginId(intent.pluginId).sourceId],
1093
+ configSnapshot,
1094
+ env: context.env,
1095
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
1096
+ ...(signal ? { signal } : {}),
1097
+ });
1098
+ }
1099
+ let dependencyPlan;
1100
+ let codexReconcilePlans;
1101
+ let operationPlan;
1102
+ await preparation?.reporter.emit('catalog');
1103
+ const offlineCleanupTarget = offlineCleanupReason && intent.kind === 'pluginWrite'
1104
+ ? status.payload.targets.find((target) => target.pluginId === intent.pluginId
1105
+ && target.platform === intent.platform
1106
+ && target.scope === intent.scope
1107
+ && target.reasons.some((error) => error.code === offlineCleanupReason))
1108
+ : undefined;
1109
+ const catalogSource = offlineCleanupTarget
1110
+ ? status.payload.catalog.sources.find((source) => source.id === offlineCleanupTarget.sourceId)
1111
+ : undefined;
1112
+ let offlineCleanup;
1113
+ if (offlineCleanupReason && offlineCleanupTarget) {
1114
+ await preparation?.reporter.emit('inventory');
1115
+ const inventory = await operationInventory({
1116
+ cwd: context.cwd,
1117
+ env: context.env,
1118
+ });
1119
+ signal?.throwIfAborted();
1120
+ offlineCleanup = offlineCleanupEvidenceFromInventory({
1121
+ reason: offlineCleanupReason,
1122
+ pluginId: offlineCleanupTarget.pluginId,
1123
+ pluginName: offlineCleanupTarget.pluginName,
1124
+ sourceId: offlineCleanupTarget.sourceId,
1125
+ sourceIdentity: offlineCleanupTarget.sourceIdentity,
1126
+ platform: offlineCleanupTarget.platform,
1127
+ scope: offlineCleanupTarget.scope,
1128
+ inventory,
1129
+ }) ?? undefined;
1130
+ if (!offlineCleanup) {
1131
+ throw operationReviewStale('离线清理现场证据已消失或无法验证。');
1132
+ }
1133
+ }
1134
+ const selected = offlineCleanupTarget
1135
+ ? {
1136
+ pluginName: offlineCleanupTarget.pluginName,
1137
+ source: {
1138
+ sourceId: offlineCleanupTarget.sourceId,
1139
+ sourceIdentity: offlineCleanupTarget.sourceIdentity,
1140
+ ...(catalogSource?.requestedRef ? { requestedRef: catalogSource.requestedRef } : {}),
1141
+ ...(catalogSource?.resolvedCommit ? { resolvedCommit: catalogSource.resolvedCommit } : {}),
1142
+ },
1143
+ }
1144
+ : await withResolvedCatalogPlugin({
1145
+ selector: intent.pluginId,
1146
+ cliVersion: packageInfo.version,
1147
+ configSnapshot,
1148
+ env: context.env,
1149
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
1150
+ ...(signal ? { signal } : {}),
1151
+ consume: async ({ plugin, source }) => {
1152
+ if (preparation?.validateIntent && intent.kind === 'configSave') {
1153
+ await preparation.reporter.emit('config');
1154
+ if (!plugin.manifest.config) {
1155
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
1156
+ }
1157
+ await coercePluginConfigUpdates({
1158
+ schema: plugin.manifest.config.fields,
1159
+ updates: intent.updates ?? {},
1160
+ cwd: context.cwd,
1161
+ });
1162
+ for (const key of intent.removeKeys ?? []) {
1163
+ if (!plugin.manifest.config.fields[key]) {
1164
+ throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
1165
+ }
1166
+ }
1167
+ }
1168
+ return {
1169
+ pluginName: plugin.manifest.name,
1170
+ source: sourceEvidenceForPlugin(source, plugin.manifest.name),
1171
+ sharedNativeRenderTargets: intent.kind === 'configSave'
1172
+ ? configSharedNativeRenderTargets(plugin, [
1173
+ ...Object.keys(intent.updates ?? {}),
1174
+ ...(intent.removeKeys ?? []),
1175
+ ])
1176
+ : [],
1177
+ };
1178
+ },
1179
+ });
1180
+ let codexManager;
1181
+ const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
1182
+ if (intent.kind === 'pluginWrite') {
1183
+ const command = {
1184
+ install: installWriteCommand,
1185
+ refreshAndSync: syncWriteCommand,
1186
+ reinstall: reinstallWriteCommand,
1187
+ uninstall: uninstallWriteCommand,
1188
+ cleanup: cleanupWriteCommand,
1189
+ sync: syncWriteCommand,
1190
+ }[intent.action];
1191
+ const preview = await command([intent.pluginId], {
1192
+ cwd: context.cwd,
1193
+ platform: intent.platform,
1194
+ scope: intent.scope,
1195
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
1196
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
1197
+ : {}),
1198
+ ...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
1199
+ }, {
1200
+ ...context,
1201
+ inventoryCatalogSourceReferences: operationInventory,
1202
+ expectedCatalogConfigDigest: status.payload.catalog.configDigest,
1203
+ ...(catalogOperation ? {
1204
+ catalogOperationContext: catalogOperation,
1205
+ expectedGenerationSetDigest: catalogOperation.generationSetDigest,
1206
+ } : {}),
1207
+ previewOperationPlan: true,
1208
+ ...(preparation ? {
1209
+ onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
1210
+ } : {}),
1211
+ });
1212
+ signal?.throwIfAborted();
1213
+ codexReconcilePlans = preview?.codexReconcilePlans;
1214
+ operationPlan = preview?.operationPlan;
1215
+ if (hasDeclaredDependencies
1216
+ && (intent.action === 'install'
1217
+ || intent.action === 'refreshAndSync'
1218
+ || intent.action === 'reinstall'
1219
+ || intent.action === 'sync')) {
1220
+ dependencyPlan = preview.dependencyPlan;
1221
+ if (!dependencyPlan) {
1222
+ throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
1223
+ }
1224
+ const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
1225
+ if (blocker) {
1226
+ throw new TuiOperationBlockedError(blocker.errorCode ?? 'dependencyNotReady', `${blocker.errorCode ?? 'dependencyNotReady'}: 依赖安装计划被阻止。`);
1227
+ }
1228
+ }
1229
+ const managerPreview = preview?.codexManagerPreview;
1230
+ if (managerPreview?.state === 'blocked') {
1231
+ const message = managerPreview.code === 'codexSystemCliUnsupported'
1232
+ ? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
1233
+ : 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
1234
+ throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
1235
+ }
1236
+ if (managerPreview) {
1237
+ codexManager = {
1238
+ state: managerPreview.state,
1239
+ packageName: MANAGED_CODEX_PACKAGE_NAME,
1240
+ version: managerPreview.state === 'requires-install'
1241
+ ? MANAGED_CODEX_VERSION
1242
+ : managerPreview.manager.version,
1243
+ };
1244
+ }
1245
+ }
1246
+ await preparation?.reporter.emit('evidence');
1247
+ signal?.throwIfAborted();
1248
+ const evidenceTargets = intent.kind === 'configSave'
1249
+ ? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.scope)
1250
+ : status.payload.targets;
1251
+ const evidenceRevisions = intent.kind === 'configSave'
1252
+ ? (status.targetRevisions ?? []).filter((revision) => evidenceTargets.some((target) => target.platform === revision.platform
1253
+ && target.scope === revision.scope
1254
+ && target.pluginId === revision.pluginId))
1255
+ : (status.targetRevisions ?? []);
1256
+ const evidence = {
1257
+ catalog: {
1258
+ configDigest: status.payload.catalog.configDigest,
1259
+ compositeDigest: status.payload.catalog.compositeDigest,
1260
+ ...(catalogOperation
1261
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
1262
+ : {}),
1263
+ },
1264
+ source: selected.source,
1265
+ ...(offlineCleanup ? { offlineCleanup } : {}),
1266
+ ...(dependencyPlan ? { dependencyPlan } : {}),
1267
+ ...(codexReconcilePlans ? { codexReconcilePlans } : {}),
1268
+ ...(operationPlan ? { operationPlan } : {}),
1269
+ ...(codexManager ? { codexManager } : {}),
1270
+ targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
1271
+ };
1272
+ if (intent.kind === 'configSave') {
1273
+ const store = await readPluginConfigStore({
1274
+ sourceId: selected.source.sourceId,
1275
+ sourceIdentity: selected.source.sourceIdentity,
1276
+ pluginName: selected.pluginName,
1277
+ scope: intent.scope,
1278
+ cwd: context.cwd,
1279
+ env: context.env,
1280
+ });
1281
+ signal?.throwIfAborted();
1282
+ evidence.config = {
1283
+ scope: intent.scope,
1284
+ ...('manifestDigest' in selected.source
1285
+ ? { schemaIdentity: selected.source.manifestDigest }
1286
+ : {}),
1287
+ storeDigest: digest({ exists: store.exists, rawDigest: store.rawDigest }),
1288
+ affectedTargets: evidenceTargets.map(({ platform, scope }) => ({ platform, scope })),
1289
+ ...('sharedNativeRenderTargets' in selected
1290
+ && selected.sharedNativeRenderTargets.length > 0
1291
+ ? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
1292
+ : {}),
1293
+ };
1294
+ }
1295
+ if (catalogOperation)
1296
+ catalogOperationByEvidence.set(evidence, catalogOperation);
1297
+ return evidence;
1298
+ };
1299
+ const dependencies = {
1300
+ beginCatalogSession(sessionOptions) {
1301
+ sessionCatalogOperationContext = undefined;
1302
+ if (sessionOptions?.reuseValidatedGenerations !== true) {
1303
+ validatedStoreEntries.clear();
1304
+ validatingStoreEntries.clear();
1305
+ }
1306
+ },
1307
+ loadStatus,
1308
+ async probeCatalogFreshness(sourceId, freshnessOptions, signal) {
1309
+ signal.throwIfAborted();
1310
+ const packageInfo = await readCliPackageInfo();
1311
+ return probeCatalogSourceFreshness({
1312
+ sourceId,
1313
+ cliVersion: packageInfo.version,
1314
+ env: context.env,
1315
+ signal,
1316
+ fullValidation: freshnessOptions.fullValidation,
1317
+ runtime: {
1318
+ ...options.catalogGenerationRuntime,
1319
+ validatedStoreEntries,
1320
+ validatingStoreEntries,
1321
+ },
1322
+ });
1323
+ },
1324
+ async inspectSourceRemoval(sourceId, signal) {
1325
+ signal.throwIfAborted();
1326
+ const config = await readCatalogSourcesConfigSnapshot(context.env);
1327
+ const source = config.config.sources.find((entry) => entry.id === sourceId);
1328
+ if (!source)
1329
+ throw new TuiOperationBlockedError('catalogGenerationSourceMissing', '来源不存在。');
1330
+ const impact = await inspectCatalogSourceImpact({
1331
+ sourceId,
1332
+ cwd: context.cwd,
1333
+ env: context.env,
1334
+ sourcePrimary: source.primary,
1335
+ mode: 'remove',
1336
+ });
1337
+ const counts = (category) => impact.affectedReferences.filter((reference) => reference.category === category).length;
1338
+ return {
1339
+ blocked: impact.affectedReferences.length > 0 || !impact.inventoryComplete,
1340
+ inventoryComplete: impact.inventoryComplete,
1341
+ installationCount: counts('installation'),
1342
+ configCount: counts('config'),
1343
+ lifecycleCount: counts('lifecycle'),
1344
+ marketplaceCount: counts('marketplace'),
1345
+ dependencies: impact.affectedReferences,
1346
+ };
1347
+ },
1348
+ async prepareReviewEvidence(intent, signal, onStage, reusableCandidate) {
1349
+ const reporter = createReviewStageReporter({
1350
+ context,
1351
+ onStage,
1352
+ now: options.now ?? Date.now,
1353
+ });
1354
+ try {
1355
+ const preparationWarnings = [];
1356
+ if (intent.kind === 'pluginWrite' && intent.action === 'refreshAndSync') {
1357
+ await reporter.emit('refresh');
1358
+ const packageInfo = await readCliPackageInfo();
1359
+ const sourceId = parsePluginId(intent.pluginId).sourceId;
1360
+ await syncCatalogSourceGeneration({
1361
+ sourceId,
1362
+ cliVersion: packageInfo.version,
1363
+ cwd: context.cwd,
1364
+ env: context.env,
1365
+ signal,
1366
+ }).catch((error) => {
1367
+ preparationWarnings.push(`catalogSourceRefreshFailed: ${sourceId}: ${error instanceof Error ? error.message : String(error)}`);
1368
+ });
1369
+ sessionCatalogOperationContext = undefined;
1370
+ sessionStatusResult = undefined;
1371
+ validatedStoreEntries.clear();
1372
+ validatingStoreEntries.clear();
1373
+ }
1374
+ const evidence = await captureEvidence(intent, signal, {
1375
+ reporter,
1376
+ validateIntent: true,
1377
+ }, createOperationInventoryReader(), intent.kind === 'pluginWrite'
1378
+ && intent.action === 'refreshAndSync', reusableCandidate);
1379
+ if (preparationWarnings.length > 0)
1380
+ evidence.preparationWarnings = preparationWarnings;
1381
+ return evidence;
1382
+ }
1383
+ finally {
1384
+ await reporter.finish();
1385
+ }
1386
+ },
1387
+ async previewDependencyPlacement(intent, signal) {
1388
+ const evidence = await captureEvidence(intent, signal);
1389
+ return evidence.dependencyPlan;
1390
+ },
1391
+ async validateIntent(intent, signal) {
1392
+ if (intent.kind === 'catalogSourcesRecovery') {
1393
+ signal.throwIfAborted();
1394
+ return;
1395
+ }
1396
+ if (intent.kind === 'catalogGeneration') {
1397
+ signal.throwIfAborted();
1398
+ return;
1399
+ }
1400
+ if (intent.kind === 'sourceMutation') {
1401
+ const evidence = await captureEvidence(intent, signal);
1402
+ if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
1403
+ throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
1404
+ }
1405
+ return;
1406
+ }
1407
+ if (intent.kind === 'configSave') {
1408
+ const packageInfo = await readCliPackageInfo();
1409
+ await withResolvedCatalogPlugin({
1410
+ selector: intent.pluginId,
1411
+ cliVersion: packageInfo.version,
1412
+ env: context.env,
1413
+ signal,
1414
+ ...(sessionContextForPlugin(intent.pluginId)
1415
+ ? { operationContext: sessionContextForPlugin(intent.pluginId) }
1416
+ : {}),
1417
+ consume: async ({ plugin }) => {
1418
+ if (!plugin.manifest.config) {
1419
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
1420
+ }
1421
+ await coercePluginConfigUpdates({
1422
+ schema: plugin.manifest.config.fields,
1423
+ updates: intent.updates ?? {},
1424
+ cwd: context.cwd,
1425
+ });
1426
+ for (const key of intent.removeKeys ?? []) {
1427
+ if (!plugin.manifest.config.fields[key]) {
1428
+ throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
1429
+ }
1430
+ }
1431
+ },
1432
+ });
1433
+ return;
1434
+ }
1435
+ if (intent.kind !== 'pluginWrite')
1436
+ return;
1437
+ signal.throwIfAborted();
1438
+ const parsed = parsePluginId(intent.pluginId);
1439
+ if (!parsed.sourceId || !parsed.pluginName) {
1440
+ throw new TuiOperationBlockedError('invalidPluginId', '操作必须使用 canonical pluginId。');
1441
+ }
1442
+ },
1443
+ captureEvidence,
1444
+ logError(_error, code, logContext) {
1445
+ return context.debugLogger.log({
1446
+ type: 'tui:controller-error',
1447
+ level: 'debug',
1448
+ target: 'debugLog',
1449
+ message: logContext?.privateConfigOperation
1450
+ ? 'TUI config operation failed.'
1451
+ : 'TUI controller operation failed.',
1452
+ data: { code },
1453
+ });
1454
+ },
1455
+ async getConfigGroups(pluginId, layer, signal) {
1456
+ const packageInfo = await readCliPackageInfo();
1457
+ return withResolvedCatalogPlugin({
1458
+ selector: pluginId,
1459
+ cliVersion: packageInfo.version,
1460
+ env: context.env,
1461
+ signal,
1462
+ ...(sessionContextForPlugin(pluginId)
1463
+ ? { operationContext: sessionContextForPlugin(pluginId) }
1464
+ : {}),
1465
+ consume: async ({ plugin }) => {
1466
+ if (!plugin.manifest.config) {
1467
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
1468
+ }
1469
+ const read = await resolveConfigRead({
1470
+ pluginName: plugin.manifest.name,
1471
+ plugin,
1472
+ scope: layer.scope,
1473
+ cwd: context.cwd,
1474
+ env: context.env,
1475
+ });
1476
+ const effectiveRead = await resolveConfigRead({
1477
+ pluginName: plugin.manifest.name,
1478
+ plugin,
1479
+ scope: 'effective',
1480
+ cwd: context.cwd,
1481
+ env: context.env,
1482
+ });
1483
+ return buildConfigGroups({ schema: plugin.manifest.config, read, effectiveRead });
1484
+ },
1485
+ });
1486
+ },
1487
+ async getConfigPage(pluginId, layer, signal) {
1488
+ const packageInfo = await readCliPackageInfo();
1489
+ return withResolvedCatalogPlugin({
1490
+ selector: pluginId,
1491
+ cliVersion: packageInfo.version,
1492
+ env: context.env,
1493
+ signal,
1494
+ ...(sessionContextForPlugin(pluginId)
1495
+ ? { operationContext: sessionContextForPlugin(pluginId) }
1496
+ : {}),
1497
+ consume: async ({ plugin }) => {
1498
+ if (!plugin.manifest.config) {
1499
+ throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
1500
+ }
1501
+ const stores = await readConfigStoresForTui({
1502
+ sourceId: plugin.sourceId,
1503
+ sourceIdentity: plugin.sourceIdentity,
1504
+ pluginName: plugin.manifest.name,
1505
+ schema: plugin.manifest.config.fields,
1506
+ scope: layer.scope,
1507
+ signal,
1508
+ });
1509
+ const userStore = stores.user;
1510
+ const projectStore = stores.project;
1511
+ const read = await resolveConfigRead({
1512
+ pluginName: plugin.manifest.name,
1513
+ plugin,
1514
+ scope: layer.scope,
1515
+ cwd: context.cwd,
1516
+ env: context.env,
1517
+ });
1518
+ const effectiveRead = layer.scope === 'effective' ? read : await resolveConfigRead({
1519
+ pluginName: plugin.manifest.name,
1520
+ plugin,
1521
+ scope: 'effective',
1522
+ cwd: context.cwd,
1523
+ env: context.env,
1524
+ });
1525
+ const layerReads = layer.scope === 'effective' ? undefined : {
1526
+ user: layer.scope === 'user' ? read : await resolveConfigRead({
1527
+ pluginName: plugin.manifest.name,
1528
+ plugin,
1529
+ scope: 'user',
1530
+ cwd: context.cwd,
1531
+ env: context.env,
1532
+ }),
1533
+ ...(layer.scope === 'user' ? {} : { project: read }),
1534
+ };
1535
+ const effectiveRaw = await resolveEffectivePluginConfig({
1536
+ sourceId: plugin.sourceId,
1537
+ sourceIdentity: plugin.sourceIdentity,
1538
+ pluginName: plugin.manifest.name,
1539
+ manifest: plugin.manifest,
1540
+ targetScope: layer.scope === 'user' ? 'user' : 'project',
1541
+ cwd: context.cwd,
1542
+ env: context.env,
1543
+ });
1544
+ const layerStore = layer.scope === 'effective'
1545
+ ? undefined
1546
+ : layer.scope === 'user' ? userStore : projectStore;
1547
+ return buildConfigPage({
1548
+ pluginId,
1549
+ schema: plugin.manifest.config,
1550
+ read,
1551
+ effectiveRead,
1552
+ ...(layerReads ? { layerReads } : {}),
1553
+ ...(layerStore ? { rawValues: layerStore.values } : {}),
1554
+ rawLayerValues: {
1555
+ user: userStore.values,
1556
+ ...(projectStore ? { project: projectStore.values } : {}),
1557
+ },
1558
+ effectiveRawValues: effectiveRaw.values,
1559
+ sharedNativeRefreshKeys: new Set((plugin.manifest.artifacts?.targets ?? []).flatMap((target) => target.target === 'codex.skill' && target.delivery.mode === 'codex-native'
1560
+ ? target.refreshKeys
1561
+ : [])),
1562
+ });
1563
+ },
1564
+ });
1565
+ },
1566
+ async getConfigResetAvailability(pluginId, layer, signal) {
1567
+ const parsed = parsePluginId(pluginId);
1568
+ if (!parsed.sourceId || !parsed.pluginName) {
1569
+ throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
1570
+ }
1571
+ let sourceIdentity;
1572
+ try {
1573
+ const packageInfo = await readCliPackageInfo();
1574
+ sourceIdentity = await withResolvedCatalogPlugin({
1575
+ selector: pluginId,
1576
+ cliVersion: packageInfo.version,
1577
+ env: context.env,
1578
+ signal,
1579
+ ...(sessionContextForPlugin(pluginId)
1580
+ ? { operationContext: sessionContextForPlugin(pluginId) }
1581
+ : {}),
1582
+ consume: async ({ source }) => source.sourceIdentity,
1583
+ });
1584
+ }
1585
+ catch {
1586
+ sourceIdentity = undefined;
1587
+ }
1588
+ signal.throwIfAborted();
1589
+ const target = await inspectPluginConfigResetTarget({
1590
+ sourceId: parsed.sourceId,
1591
+ pluginName: parsed.pluginName,
1592
+ scope: layer.scope,
1593
+ cwd: context.cwd,
1594
+ env: context.env,
1595
+ });
1596
+ signal.throwIfAborted();
1597
+ if (target.kind === 'regularFile') {
1598
+ const store = await readPluginConfigStore({
1599
+ sourceId: parsed.sourceId,
1600
+ ...(sourceIdentity ? { sourceIdentity } : {}),
1601
+ pluginName: parsed.pluginName,
1602
+ scope: layer.scope,
1603
+ cwd: context.cwd,
1604
+ env: context.env,
1605
+ });
1606
+ signal.throwIfAborted();
1607
+ return {
1608
+ exists: store.exists,
1609
+ state: store.state,
1610
+ ...(store.invalidCode ? { invalidCode: store.invalidCode } : {}),
1611
+ sourceIdentityPresent: sourceIdentity !== undefined,
1612
+ ...(sourceIdentity ? { sourceIdentity } : {}),
1613
+ targetKind: target.kind,
1614
+ resettable: target.resettable,
1615
+ storeDigest: target.fingerprint,
1616
+ };
1617
+ }
1618
+ return {
1619
+ exists: target.exists,
1620
+ state: target.kind === 'absent' ? 'missing' : 'invalid',
1621
+ sourceIdentityPresent: sourceIdentity !== undefined,
1622
+ ...(sourceIdentity ? { sourceIdentity } : {}),
1623
+ targetKind: target.kind,
1624
+ resettable: target.resettable,
1625
+ storeDigest: target.fingerprint,
1626
+ };
1627
+ },
1628
+ async executeIntent(intent, expectedEvidence, signal) {
1629
+ if (intent.kind === 'catalogSourcesRecovery') {
1630
+ signal?.throwIfAborted();
1631
+ return recoverInvalidCatalogSourcesConfig({ env: context.env });
1632
+ }
1633
+ if (intent.kind === 'catalogGeneration') {
1634
+ signal?.throwIfAborted();
1635
+ const expected = expectedEvidence.catalogGeneration;
1636
+ if (!expected)
1637
+ throw new TuiOperationBlockedError('catalogGenerationReviewStale', '确认信息缺少 Catalog candidate。');
1638
+ const packageInfo = await readCliPackageInfo();
1639
+ let configSaved = false;
1640
+ if (intent.sourceMutation) {
1641
+ const common = {
1642
+ cwd: context.cwd,
1643
+ env: context.env,
1644
+ cliVersion: packageInfo.version,
1645
+ expectedConfigDigest: expected.configDigest,
1646
+ };
1647
+ if (intent.sourceMutation.action === 'add') {
1648
+ await addCatalogSourceCommand({ ...common, source: intent.sourceMutation.source });
1649
+ }
1650
+ else {
1651
+ await updateCatalogSourceCommand({
1652
+ ...common,
1653
+ sourceId: intent.sourceId,
1654
+ update: intent.sourceMutation.update,
1655
+ expectedImpactDigest: expected.impactDigest,
1656
+ requireIdentityChangeConfirmation: true,
1657
+ confirmIdentityChange: true,
1658
+ });
1659
+ }
1660
+ configSaved = true;
1661
+ await tuiControllerRuntimeTestHooks?.afterCatalogSourceConfigCommit?.();
1662
+ }
1663
+ try {
1664
+ let generation;
1665
+ if (intent.action === 'recoverState') {
1666
+ generation = await recoverCatalogGenerationStateAndActivate({
1667
+ sourceId: intent.sourceId,
1668
+ cliVersion: packageInfo.version,
1669
+ cwd: context.cwd,
1670
+ impactConfirmed: true,
1671
+ expectedImpactDigest: expected.impactDigest,
1672
+ expectedCandidate: expected.candidate,
1673
+ env: context.env,
1674
+ signal,
1675
+ runtime: options.catalogGenerationRuntime,
1676
+ });
1677
+ }
1678
+ else if (intent.action === 'repair') {
1679
+ generation = await repairCatalogSourceGeneration({
1680
+ sourceId: intent.sourceId,
1681
+ cliVersion: packageInfo.version,
1682
+ cwd: context.cwd,
1683
+ expectedCandidate: expected.candidate,
1684
+ expectedActive: expected.previousActive ?? null,
1685
+ expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
1686
+ env: context.env,
1687
+ signal,
1688
+ runtime: {
1689
+ ...options.catalogGenerationRuntime,
1690
+ afterCandidateResolved: tuiControllerRuntimeTestHooks
1691
+ ?.afterCatalogGenerationCandidateResolved,
1692
+ },
1693
+ });
1694
+ }
1695
+ else if (intent.action === 'update' && expected.exactRepair) {
1696
+ generation = await repairCatalogSourceGeneration({
1697
+ sourceId: intent.sourceId,
1698
+ cliVersion: packageInfo.version,
1699
+ cwd: context.cwd,
1700
+ expectedActive: expected.previousActive ?? null,
1701
+ expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
1702
+ env: context.env,
1703
+ signal,
1704
+ runtime: options.catalogGenerationRuntime,
1705
+ });
1706
+ }
1707
+ else if (intent.action === 'replaceDamaged') {
1708
+ generation = await replaceDamagedCatalogSourceGeneration({
1709
+ sourceId: intent.sourceId,
1710
+ cliVersion: packageInfo.version,
1711
+ cwd: context.cwd,
1712
+ expectedCandidate: expected.candidate,
1713
+ expectedActive: expected.previousActive,
1714
+ expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
1715
+ impactConfirmed: true,
1716
+ expectedImpactDigest: expected.impactDigest,
1717
+ env: context.env,
1718
+ signal,
1719
+ runtime: {
1720
+ ...options.catalogGenerationRuntime,
1721
+ afterCandidateResolved: tuiControllerRuntimeTestHooks
1722
+ ?.afterCatalogGenerationCandidateResolved,
1723
+ },
1724
+ });
1725
+ }
1726
+ else {
1727
+ generation = await activateCatalogSourceGeneration({
1728
+ sourceId: intent.sourceId,
1729
+ cliVersion: packageInfo.version,
1730
+ cwd: context.cwd,
1731
+ activationMode: expected.activationMode,
1732
+ impactConfirmed: true,
1733
+ expectedImpactDigest: expected.impactDigest,
1734
+ impactMode: expected.impactMode,
1735
+ expectedCandidate: expected.candidate,
1736
+ expectedActive: expected.previousActive ?? null,
1737
+ expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
1738
+ env: context.env,
1739
+ signal,
1740
+ runtime: {
1741
+ ...options.catalogGenerationRuntime,
1742
+ afterCandidateResolved: tuiControllerRuntimeTestHooks
1743
+ ?.afterCatalogGenerationCandidateResolved,
1744
+ },
1745
+ });
1746
+ }
1747
+ let primaryChanged = false;
1748
+ if (intent.setPrimaryAfterActivation) {
1749
+ try {
1750
+ await setPrimaryCatalogSourceCommand({
1751
+ sourceId: intent.sourceId,
1752
+ cwd: context.cwd,
1753
+ env: context.env,
1754
+ });
1755
+ primaryChanged = true;
1756
+ }
1757
+ catch (error) {
1758
+ return {
1759
+ status: 'partial',
1760
+ partialReason: 'primary',
1761
+ configSaved,
1762
+ generation,
1763
+ activeAvailable: true,
1764
+ errorCode: error instanceof Error ? error.name : 'catalogSourcePrimaryFailed',
1765
+ };
1766
+ }
1767
+ }
1768
+ return { status: 'succeeded', generation, configSaved, primaryChanged };
1769
+ }
1770
+ catch (error) {
1771
+ if (!configSaved)
1772
+ throw error;
1773
+ return {
1774
+ status: 'partial',
1775
+ partialReason: 'activation',
1776
+ configSaved: true,
1777
+ errorCode: error instanceof CatalogGenerationError ? error.code : 'catalogGenerationRefreshFailed',
1778
+ };
1779
+ }
1780
+ }
1781
+ if (intent.kind === 'portableInstall') {
1782
+ if (!expectedEvidence.portable) {
1783
+ throw operationReviewStale('确认信息缺少 Portable evidence。');
1784
+ }
1785
+ const packageInfo = await readCliPackageInfo();
1786
+ try {
1787
+ return await installPortableSkills({
1788
+ selector: intent.pluginId,
1789
+ scope: intent.scope,
1790
+ requestedSkills: intent.selectedSkills,
1791
+ ...(intent.requestedAgents ? { requestedAgents: intent.requestedAgents } : {}),
1792
+ yes: true,
1793
+ cwd: context.cwd,
1794
+ env: context.env,
1795
+ cliVersion: packageInfo.version,
1796
+ expectedEvidenceDigest: expectedEvidence.portable.evidenceDigest,
1797
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1798
+ onProgress(stage) {
1799
+ const phase = stage === 'prepare' ? '检查插件依赖并生成候选'
1800
+ : stage === 'add' ? '安装已选择的 Skills' : '验证安装结果';
1801
+ progressFeed?.setCurrent({ target: 'Skills CLI', phase }, `Portable Skills:${phase}`);
1802
+ },
1803
+ });
1804
+ }
1805
+ catch (error) {
1806
+ if (error instanceof PortableCommandError && error.code === 'reviewStale') {
1807
+ throw operationReviewStale('确认后 Portable 安装证据发生变化。');
1808
+ }
1809
+ throw error;
1810
+ }
1811
+ }
1812
+ if (intent.kind === 'sourceMutation') {
1813
+ const currentEvidence = await captureEvidence(intent);
1814
+ if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
1815
+ throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
1816
+ }
1817
+ await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
1818
+ const packageInfo = await readCliPackageInfo();
1819
+ const runtime = {
1820
+ onProgress: async (event) => {
1821
+ const phase = SOURCE_PROGRESS_PHASES[event.stage];
1822
+ progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
1823
+ await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
1824
+ },
1825
+ };
1826
+ const common = {
1827
+ cwd: context.cwd,
1828
+ env: context.env,
1829
+ expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
1830
+ expectedImpactDigest: expectedEvidence.sourceWrite?.impactDigest,
1831
+ runtime,
1832
+ };
1833
+ if (intent.action === 'add' && intent.source) {
1834
+ return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
1835
+ }
1836
+ if (intent.action === 'update' && intent.update) {
1837
+ return updateCatalogSourceCommand({
1838
+ ...common,
1839
+ sourceId: intent.sourceId,
1840
+ update: intent.update,
1841
+ cliVersion: packageInfo.version,
1842
+ requireIdentityChangeConfirmation: true,
1843
+ confirmIdentityChange: true,
1844
+ });
1845
+ }
1846
+ if (intent.action === 'setPrimary') {
1847
+ return setPrimaryCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
1848
+ }
1849
+ if (intent.action === 'remove') {
1850
+ return removeCatalogSourceCommand({
1851
+ ...common,
1852
+ sourceId: intent.sourceId,
1853
+ requireImpactConfirmation: true,
1854
+ confirmImpact: true,
1855
+ });
1856
+ }
1857
+ throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
1858
+ }
1859
+ return withAwHomeMutationLock({
1860
+ env: context.env,
1861
+ action: async (awHomeMutationToken) => {
1862
+ await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
1863
+ const operationInventory = createOperationInventoryReader();
1864
+ const currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true);
1865
+ if (intent.kind !== 'pluginWrite'
1866
+ && !operationEvidenceMatches(expectedEvidence, currentEvidence)) {
1867
+ throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
1868
+ }
1869
+ const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
1870
+ if (expectedEvidence.catalog.generationSetDigest
1871
+ && catalogOperation?.generationSetDigest !== expectedEvidence.catalog.generationSetDigest) {
1872
+ throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
1873
+ }
1874
+ await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
1875
+ if (intent.kind === 'pluginWrite') {
1876
+ const options = {
1877
+ cwd: context.cwd,
1878
+ platform: intent.platform,
1879
+ scope: intent.scope,
1880
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
1881
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
1882
+ : {}),
1883
+ ...(expectedEvidence.codexManager?.state === 'requires-install'
1884
+ ? { allowManagedCodexInstall: true }
1885
+ : {}),
1886
+ ...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
1887
+ };
1888
+ const command = {
1889
+ install: installWriteCommand,
1890
+ refreshAndSync: syncWriteCommand,
1891
+ reinstall: reinstallWriteCommand,
1892
+ uninstall: uninstallWriteCommand,
1893
+ cleanup: cleanupWriteCommand,
1894
+ sync: syncWriteCommand,
1895
+ }[intent.action];
1896
+ const currentSource = currentEvidence.source;
1897
+ const offlineCleanup = currentEvidence.offlineCleanup;
1898
+ if (!offlineCleanup && (!expectedEvidence.source || !currentSource)) {
1899
+ throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
1900
+ }
1901
+ const result = await command([intent.pluginId], options, {
1902
+ ...context,
1903
+ awHomeMutationToken,
1904
+ inventoryCatalogSourceReferences: operationInventory,
1905
+ ...(expectedEvidence.operationPlan
1906
+ ? { expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint }
1907
+ : {}),
1908
+ expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
1909
+ ...(catalogOperation ? {
1910
+ catalogOperationContext: catalogOperation,
1911
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1912
+ } : {}),
1913
+ ...(expectedEvidence.codexManager?.state === 'requires-install'
1914
+ ? { managedCodexAuthorizer: async () => true }
1915
+ : {}),
1916
+ ...(offlineCleanup
1917
+ ? { expectedOfflineCleanupEvidence: offlineCleanup }
1918
+ : { reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual) }),
1919
+ });
1920
+ throwIfWriteReviewStale(result);
1921
+ return result;
1922
+ }
1923
+ if (intent.kind === 'configSave' || intent.kind === 'configReset') {
1924
+ const currentSource = currentEvidence.source;
1925
+ if (intent.kind === 'configReset') {
1926
+ const parsed = parsePluginId(intent.pluginId);
1927
+ if (!parsed.sourceId || !parsed.pluginName) {
1928
+ throw operationReviewStale('确认信息缺少 qualified 设置目标。');
1929
+ }
1930
+ return resetPluginConfigWithHelper({
1931
+ sourceId: parsed.sourceId,
1932
+ pluginName: parsed.pluginName,
1933
+ scope: intent.scope,
1934
+ cwd: context.cwd,
1935
+ env: context.env,
1936
+ nativeCliRunner: context.nativeCliRunner,
1937
+ rebuildRuntime: { ...context, awHomeMutationToken },
1938
+ awHomeMutationToken,
1939
+ apply: false,
1940
+ });
1941
+ }
1942
+ if (!currentSource) {
1943
+ throw operationReviewStale('确认信息缺少来源身份。');
1944
+ }
1945
+ const packageInfo = await readCliPackageInfo();
1946
+ return withResolvedCatalogPlugin({
1947
+ selector: intent.pluginId,
1948
+ cliVersion: packageInfo.version,
1949
+ env: context.env,
1950
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
1951
+ consume: async ({ plugin, source }) => {
1952
+ assertSourceEvidenceMatches(currentSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
1953
+ const applyTargets = intent.apply
1954
+ ? currentEvidence.config?.affectedTargets.map((target) => ({
1955
+ pluginName: plugin.manifest.name,
1956
+ platform: target.platform,
1957
+ scope: target.scope,
1958
+ })) ?? []
1959
+ : [];
1960
+ const result = intent.retryApply
1961
+ ? await replayConfigRuntimeWrites({
1962
+ plugin,
1963
+ scope: intent.scope,
1964
+ cwd: context.cwd,
1965
+ env: context.env,
1966
+ nativeCliRunner: context.nativeCliRunner,
1967
+ rebuildRuntime: {
1968
+ ...context,
1969
+ awHomeMutationToken,
1970
+ ...(catalogOperation ? {
1971
+ catalogOperationContext: catalogOperation,
1972
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1973
+ } : {}),
1974
+ },
1975
+ awHomeMutationToken,
1976
+ applyTargets,
1977
+ })
1978
+ : await applyPluginConfigWrite({
1979
+ plugin,
1980
+ pluginName: plugin.manifest.name,
1981
+ scope: intent.scope,
1982
+ cwd: context.cwd,
1983
+ env: context.env,
1984
+ nativeCliRunner: context.nativeCliRunner,
1985
+ rebuildRuntime: {
1986
+ ...context,
1987
+ awHomeMutationToken,
1988
+ ...(catalogOperation ? {
1989
+ catalogOperationContext: catalogOperation,
1990
+ expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
1991
+ } : {}),
1992
+ },
1993
+ awHomeMutationToken,
1994
+ applyTargets,
1995
+ request: { updates: intent.updates, removeKeys: intent.removeKeys },
1996
+ });
1997
+ return result;
1998
+ },
1999
+ });
2000
+ }
2001
+ throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
2002
+ },
2003
+ });
2004
+ },
2005
+ };
2006
+ return dependencies;
2007
+ }
2008
+ export function createConsoleTuiV4Controller(context, progressFeed) {
2009
+ return new TuiController(createTuiControllerDependencies(context, progressFeed), progressFeed);
2010
+ }
2011
+ //# sourceMappingURL=controller-runtime.js.map