@lesliechan721/aw 0.1.0-beta.64 → 0.1.0-beta.65

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 (302) hide show
  1. package/dist/authoring/assets/aw-runtime-config.mjs +3 -3
  2. package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
  3. package/dist/authoring/scripts/add-skill.mjs +3 -17
  4. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  5. package/dist/authoring/scripts/init-plugin.mjs +1 -0
  6. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  8. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +2 -0
  9. package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
  10. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  11. package/dist/authoring/scripts/validate-plugin.mjs +95 -698
  12. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  13. package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
  14. package/dist/scripts/aw-skill-eval-materializer.js +74 -44
  15. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
  16. package/dist/scripts/bundle-plugins.js +10 -2
  17. package/dist/scripts/bundle-plugins.js.map +1 -1
  18. package/dist/scripts/plugin-staging.d.ts +1 -1
  19. package/dist/scripts/plugin-staging.js +1 -1
  20. package/dist/scripts/plugin-staging.js.map +1 -1
  21. package/dist/scripts/test-plugin-scripts.js +31 -31
  22. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  23. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  24. package/dist/src/commands/aw-capabilities.js +2 -0
  25. package/dist/src/commands/aw-capabilities.js.map +1 -1
  26. package/dist/src/commands/console.js +4 -3
  27. package/dist/src/commands/console.js.map +1 -1
  28. package/dist/src/commands/intent-command.d.ts +30 -0
  29. package/dist/src/commands/intent-command.js +164 -0
  30. package/dist/src/commands/intent-command.js.map +1 -0
  31. package/dist/src/commands/operation-plan.d.ts +2 -0
  32. package/dist/src/commands/operation-plan.js +2 -1
  33. package/dist/src/commands/operation-plan.js.map +1 -1
  34. package/dist/src/commands/options.d.ts +3 -11
  35. package/dist/src/commands/options.js +11 -17
  36. package/dist/src/commands/options.js.map +1 -1
  37. package/dist/src/commands/plugin-capabilities.js +27 -33
  38. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  39. package/dist/src/commands/rebuild-command.d.ts +108 -6
  40. package/dist/src/commands/rebuild-command.js +617 -1549
  41. package/dist/src/commands/rebuild-command.js.map +1 -1
  42. package/dist/src/commands/rebuild-integration.d.ts +19 -3
  43. package/dist/src/commands/rebuild-integration.js +500 -459
  44. package/dist/src/commands/rebuild-integration.js.map +1 -1
  45. package/dist/src/commands/rebuild-operations.js +1 -2
  46. package/dist/src/commands/rebuild-operations.js.map +1 -1
  47. package/dist/src/commands/review-inspection.d.ts +2 -2
  48. package/dist/src/commands/review-inspection.js +30 -86
  49. package/dist/src/commands/review-inspection.js.map +1 -1
  50. package/dist/src/commands/review-preparation.js +16 -7
  51. package/dist/src/commands/review-preparation.js.map +1 -1
  52. package/dist/src/commands/skill-eval.d.ts +2 -0
  53. package/dist/src/commands/skill-eval.js +15 -9
  54. package/dist/src/commands/skill-eval.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +38 -5
  56. package/dist/src/commands/status-v6.js +269 -102
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/targets-command.d.ts +27 -0
  59. package/dist/src/commands/targets-command.js +77 -0
  60. package/dist/src/commands/targets-command.js.map +1 -0
  61. package/dist/src/commands/write-commands.js +5 -5
  62. package/dist/src/commands/write-commands.js.map +1 -1
  63. package/dist/src/eval/manifest-v1.d.ts +2 -0
  64. package/dist/src/eval/manifest-v1.js +7 -2
  65. package/dist/src/eval/manifest-v1.js.map +1 -1
  66. package/dist/src/generators/local-plugin.d.ts +2 -1
  67. package/dist/src/generators/local-plugin.js +111 -108
  68. package/dist/src/generators/local-plugin.js.map +1 -1
  69. package/dist/src/index.d.ts +10 -17
  70. package/dist/src/index.js +585 -1349
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/intent/deployment-preferences.d.ts +63 -0
  73. package/dist/src/intent/deployment-preferences.js +170 -0
  74. package/dist/src/intent/deployment-preferences.js.map +1 -0
  75. package/dist/src/intent/diagnostics.d.ts +11 -0
  76. package/dist/src/intent/diagnostics.js +14 -0
  77. package/dist/src/intent/diagnostics.js.map +1 -0
  78. package/dist/src/intent/intent-codec.d.ts +19 -0
  79. package/dist/src/intent/intent-codec.js +117 -0
  80. package/dist/src/intent/intent-codec.js.map +1 -0
  81. package/dist/src/intent/intent-store.d.ts +43 -0
  82. package/dist/src/intent/intent-store.js +118 -0
  83. package/dist/src/intent/intent-store.js.map +1 -0
  84. package/dist/src/intent/scope-context.d.ts +33 -0
  85. package/dist/src/intent/scope-context.js +145 -0
  86. package/dist/src/intent/scope-context.js.map +1 -0
  87. package/dist/src/intent/work-context.d.ts +13 -0
  88. package/dist/src/intent/work-context.js +22 -0
  89. package/dist/src/intent/work-context.js.map +1 -0
  90. package/dist/src/output/command-receipt.js +16 -9
  91. package/dist/src/output/command-receipt.js.map +1 -1
  92. package/dist/src/portable/commands.d.ts +18 -8
  93. package/dist/src/portable/commands.js +46 -562
  94. package/dist/src/portable/commands.js.map +1 -1
  95. package/dist/src/portable/historical-materialization.d.ts +22 -0
  96. package/dist/src/portable/historical-materialization.js +124 -0
  97. package/dist/src/portable/historical-materialization.js.map +1 -0
  98. package/dist/src/portable/materializer.d.ts +8 -0
  99. package/dist/src/portable/materializer.js +75 -44
  100. package/dist/src/portable/materializer.js.map +1 -1
  101. package/dist/src/portable/recovery-layout.d.ts +17 -0
  102. package/dist/src/portable/recovery-layout.js +55 -0
  103. package/dist/src/portable/recovery-layout.js.map +1 -0
  104. package/dist/src/portable/recovery.d.ts +143 -0
  105. package/dist/src/portable/recovery.js +339 -0
  106. package/dist/src/portable/recovery.js.map +1 -0
  107. package/dist/src/portable/writer.d.ts +64 -0
  108. package/dist/src/portable/writer.js +316 -0
  109. package/dist/src/portable/writer.js.map +1 -0
  110. package/dist/src/recovery/coordinator.d.ts +137 -0
  111. package/dist/src/recovery/coordinator.js +1198 -0
  112. package/dist/src/recovery/coordinator.js.map +1 -0
  113. package/dist/src/recovery/demand.d.ts +44 -0
  114. package/dist/src/recovery/demand.js +178 -0
  115. package/dist/src/recovery/demand.js.map +1 -0
  116. package/dist/src/recovery/joint-coordinator.d.ts +24 -0
  117. package/dist/src/recovery/joint-coordinator.js +770 -0
  118. package/dist/src/recovery/joint-coordinator.js.map +1 -0
  119. package/dist/src/recovery/joint-demand.d.ts +68 -0
  120. package/dist/src/recovery/joint-demand.js +166 -0
  121. package/dist/src/recovery/joint-demand.js.map +1 -0
  122. package/dist/src/recovery/receipt.d.ts +81 -0
  123. package/dist/src/recovery/receipt.js +2 -0
  124. package/dist/src/recovery/receipt.js.map +1 -0
  125. package/dist/src/recovery/retirement.d.ts +45 -0
  126. package/dist/src/recovery/retirement.js +366 -0
  127. package/dist/src/recovery/retirement.js.map +1 -0
  128. package/dist/src/recovery/status.d.ts +108 -0
  129. package/dist/src/recovery/status.js +417 -0
  130. package/dist/src/recovery/status.js.map +1 -0
  131. package/dist/src/recovery/types.d.ts +86 -0
  132. package/dist/src/recovery/types.js +2 -0
  133. package/dist/src/recovery/types.js.map +1 -0
  134. package/dist/src/review/review-profile.d.ts +2 -1
  135. package/dist/src/review/review-profile.js +15 -15
  136. package/dist/src/review/review-profile.js.map +1 -1
  137. package/dist/src/review/review-selection.d.ts +1 -2
  138. package/dist/src/review/review-selection.js +40 -114
  139. package/dist/src/review/review-selection.js.map +1 -1
  140. package/dist/src/review/review-types.d.ts +4 -4
  141. package/dist/src/review/review-types.js.map +1 -1
  142. package/dist/src/review/skill-materialization.js +15 -6
  143. package/dist/src/review/skill-materialization.js.map +1 -1
  144. package/dist/src/review/source-binding.d.ts +14 -4
  145. package/dist/src/review/source-binding.js +62 -117
  146. package/dist/src/review/source-binding.js.map +1 -1
  147. package/dist/src/tui/agent-management.d.ts +23 -0
  148. package/dist/src/tui/agent-management.js +217 -0
  149. package/dist/src/tui/agent-management.js.map +1 -0
  150. package/dist/src/tui/agent-settings.d.ts +44 -0
  151. package/dist/src/tui/agent-settings.js +98 -0
  152. package/dist/src/tui/agent-settings.js.map +1 -0
  153. package/dist/src/tui/components.d.ts +3 -0
  154. package/dist/src/tui/components.js +10 -1
  155. package/dist/src/tui/components.js.map +1 -1
  156. package/dist/src/tui/config-references.d.ts +6 -3
  157. package/dist/src/tui/config-references.js +17 -12
  158. package/dist/src/tui/config-references.js.map +1 -1
  159. package/dist/src/tui/console-app-v4.d.ts +1 -2
  160. package/dist/src/tui/console-app-v4.js +1533 -1047
  161. package/dist/src/tui/console-app-v4.js.map +1 -1
  162. package/dist/src/tui/controller-runtime.d.ts +2 -1
  163. package/dist/src/tui/controller-runtime.js +405 -1626
  164. package/dist/src/tui/controller-runtime.js.map +1 -1
  165. package/dist/src/tui/controller.d.ts +131 -81
  166. package/dist/src/tui/controller.js +307 -227
  167. package/dist/src/tui/controller.js.map +1 -1
  168. package/dist/src/tui/entity-cards.d.ts +2 -2
  169. package/dist/src/tui/entity-cards.js +98 -20
  170. package/dist/src/tui/entity-cards.js.map +1 -1
  171. package/dist/src/tui/glossary.d.ts +2 -0
  172. package/dist/src/tui/glossary.js +20 -0
  173. package/dist/src/tui/glossary.js.map +1 -1
  174. package/dist/src/tui/model.d.ts +28 -10
  175. package/dist/src/tui/model.js +41 -41
  176. package/dist/src/tui/model.js.map +1 -1
  177. package/dist/src/tui/operation-review-planner.d.ts +2 -0
  178. package/dist/src/tui/operation-review-planner.js +58 -122
  179. package/dist/src/tui/operation-review-planner.js.map +1 -1
  180. package/dist/src/tui/plugin-content.js +7 -4
  181. package/dist/src/tui/plugin-content.js.map +1 -1
  182. package/dist/src/tui/plugin-detail.d.ts +22 -3
  183. package/dist/src/tui/plugin-detail.js +113 -26
  184. package/dist/src/tui/plugin-detail.js.map +1 -1
  185. package/dist/src/tui/reader.d.ts +1 -0
  186. package/dist/src/tui/reader.js +3 -1
  187. package/dist/src/tui/reader.js.map +1 -1
  188. package/dist/src/tui/router.d.ts +23 -33
  189. package/dist/src/tui/router.js.map +1 -1
  190. package/dist/src/tui/scoped-flow.d.ts +45 -0
  191. package/dist/src/tui/scoped-flow.js +96 -0
  192. package/dist/src/tui/scoped-flow.js.map +1 -0
  193. package/dist/src/tui/scoped-inventory.d.ts +122 -0
  194. package/dist/src/tui/scoped-inventory.js +225 -0
  195. package/dist/src/tui/scoped-inventory.js.map +1 -0
  196. package/dist/src/tui/screen-registry.d.ts +25 -25
  197. package/dist/src/tui/screen-registry.js +3 -7
  198. package/dist/src/tui/screen-registry.js.map +1 -1
  199. package/dist/src/tui/session-console-app.js +1 -1
  200. package/dist/src/tui/session-console-app.js.map +1 -1
  201. package/dist/src/tui/session-host.js +12 -2
  202. package/dist/src/tui/session-host.js.map +1 -1
  203. package/dist/src/types.d.ts +15 -12
  204. package/dist/src/types.js.map +1 -1
  205. package/dist/src/utils/agent-version.js +2 -0
  206. package/dist/src/utils/agent-version.js.map +1 -1
  207. package/dist/src/utils/artifact-plan.js +13 -6
  208. package/dist/src/utils/artifact-plan.js.map +1 -1
  209. package/dist/src/utils/artifact-renderer.d.ts +6 -3
  210. package/dist/src/utils/artifact-renderer.js +27 -32
  211. package/dist/src/utils/artifact-renderer.js.map +1 -1
  212. package/dist/src/utils/artifact-request.d.ts +1 -0
  213. package/dist/src/utils/artifact-request.js +22 -12
  214. package/dist/src/utils/artifact-request.js.map +1 -1
  215. package/dist/src/utils/artifact-sources.d.ts +2 -2
  216. package/dist/src/utils/bundle-layout.js +18 -16
  217. package/dist/src/utils/bundle-layout.js.map +1 -1
  218. package/dist/src/utils/catalog-digests.js +2 -0
  219. package/dist/src/utils/catalog-digests.js.map +1 -1
  220. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
  221. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  222. package/dist/src/utils/catalog-source-references.d.ts +5 -0
  223. package/dist/src/utils/catalog-source-references.js +42 -57
  224. package/dist/src/utils/catalog-source-references.js.map +1 -1
  225. package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
  226. package/dist/src/utils/claude-native-render-receipt.js +26 -0
  227. package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
  228. package/dist/src/utils/claude-skills-root.js +14 -3
  229. package/dist/src/utils/claude-skills-root.js.map +1 -1
  230. package/dist/src/utils/codex-manager.js +6 -12
  231. package/dist/src/utils/codex-manager.js.map +1 -1
  232. package/dist/src/utils/codex-native-observation.js +29 -13
  233. package/dist/src/utils/codex-native-observation.js.map +1 -1
  234. package/dist/src/utils/codex-reconciler.d.ts +2 -5
  235. package/dist/src/utils/codex-reconciler.js +25 -1
  236. package/dist/src/utils/codex-reconciler.js.map +1 -1
  237. package/dist/src/utils/codex-surface-analysis.js +5 -3
  238. package/dist/src/utils/codex-surface-analysis.js.map +1 -1
  239. package/dist/src/utils/local-marketplace.d.ts +14 -2
  240. package/dist/src/utils/local-marketplace.js +209 -42
  241. package/dist/src/utils/local-marketplace.js.map +1 -1
  242. package/dist/src/utils/managed-command-evidence.js +1 -1
  243. package/dist/src/utils/managed-command-evidence.js.map +1 -1
  244. package/dist/src/utils/managed-command-materialization.d.ts +4 -0
  245. package/dist/src/utils/managed-command-materialization.js +15 -6
  246. package/dist/src/utils/managed-command-materialization.js.map +1 -1
  247. package/dist/src/utils/managed-command-store.d.ts +15 -0
  248. package/dist/src/utils/managed-command-store.js +73 -8
  249. package/dist/src/utils/managed-command-store.js.map +1 -1
  250. package/dist/src/utils/native-cli.d.ts +2 -0
  251. package/dist/src/utils/native-cli.js +22 -0
  252. package/dist/src/utils/native-cli.js.map +1 -1
  253. package/dist/src/utils/native-demand.d.ts +10 -0
  254. package/dist/src/utils/native-demand.js +4 -0
  255. package/dist/src/utils/native-demand.js.map +1 -0
  256. package/dist/src/utils/ownership.js +2 -0
  257. package/dist/src/utils/ownership.js.map +1 -1
  258. package/dist/src/utils/paths.js +1 -1
  259. package/dist/src/utils/paths.js.map +1 -1
  260. package/dist/src/utils/platform-support.d.ts +1 -1
  261. package/dist/src/utils/platform-support.js +1 -1
  262. package/dist/src/utils/platform-support.js.map +1 -1
  263. package/dist/src/utils/plugin-config-impact.d.ts +3 -0
  264. package/dist/src/utils/plugin-config-impact.js +115 -273
  265. package/dist/src/utils/plugin-config-impact.js.map +1 -1
  266. package/dist/src/utils/plugin-config-references.d.ts +9 -2
  267. package/dist/src/utils/plugin-config-references.js +75 -85
  268. package/dist/src/utils/plugin-config-references.js.map +1 -1
  269. package/dist/src/utils/plugin-config-write.d.ts +58 -1
  270. package/dist/src/utils/plugin-config-write.js +756 -809
  271. package/dist/src/utils/plugin-config-write.js.map +1 -1
  272. package/dist/src/utils/plugin-config.d.ts +47 -46
  273. package/dist/src/utils/plugin-config.js +292 -833
  274. package/dist/src/utils/plugin-config.js.map +1 -1
  275. package/dist/src/utils/plugin-scripts.d.ts +11 -6
  276. package/dist/src/utils/plugin-scripts.js +52 -35
  277. package/dist/src/utils/plugin-scripts.js.map +1 -1
  278. package/dist/src/utils/runtime-config.js +5 -4
  279. package/dist/src/utils/runtime-config.js.map +1 -1
  280. package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
  281. package/dist/src/utils/runtime-skill-closure.js.map +1 -1
  282. package/dist/src/utils/state-paths.d.ts +8 -3
  283. package/dist/src/utils/state-paths.js +53 -16
  284. package/dist/src/utils/state-paths.js.map +1 -1
  285. package/dist/src/utils/windows-npm-shim.d.ts +2 -0
  286. package/dist/src/utils/windows-npm-shim.js +5 -7
  287. package/dist/src/utils/windows-npm-shim.js.map +1 -1
  288. package/dist/vitest.config.js +1 -0
  289. package/dist/vitest.config.js.map +1 -1
  290. package/package.json +3 -3
  291. package/dist/src/commands/sync-command.d.ts +0 -40
  292. package/dist/src/commands/sync-command.js +0 -254
  293. package/dist/src/commands/sync-command.js.map +0 -1
  294. package/dist/src/portable/skills-cli.d.ts +0 -51
  295. package/dist/src/portable/skills-cli.js +0 -202
  296. package/dist/src/portable/skills-cli.js.map +0 -1
  297. package/dist/src/tui/project-inventory.d.ts +0 -68
  298. package/dist/src/tui/project-inventory.js +0 -203
  299. package/dist/src/tui/project-inventory.js.map +0 -1
  300. package/dist/src/utils/plugin-intent-state.d.ts +0 -49
  301. package/dist/src/utils/plugin-intent-state.js +0 -320
  302. package/dist/src/utils/plugin-intent-state.js.map +0 -1
@@ -1,18 +1,25 @@
1
- import { getCommandPackageId } from '@lesliechan721/aw-plugin-core/manifest';
2
- import { flattenStatusTargets, statusCommandV8, statusTargetIntent, } from '../commands/status-v6.js';
1
+ import { resolveWorkContext } from '../intent/work-context.js';
2
+ import { statusTargetHasLiveConfigConsumer } from '../commands/status-v6.js';
3
+ import { readDemand, demandNativeProjection } from '../recovery/demand.js';
4
+ import { createScopeContext } from '../intent/scope-context.js';
5
+ import { compilePluginConfigDefinition, getCommandPackageId } from '@lesliechan721/aw-plugin-core/manifest';
6
+ import { flattenStatusTargets, statusCommandV8, } from '../commands/status-v6.js';
3
7
  import { syncWriteCommand } from '../commands/write-commands.js';
4
8
  import { CONFIG_MUTATION_RECEIPT_SCHEMA_VERSION, } from '../types.js';
5
9
  import { getDefaultNativeCliRunner, prepareNativeCliExecution } from './native-cli.js';
6
- import { assertReadyPluginConfigStore, assertPluginConfigMutationCandidate, assertSafePluginConfigSegment, codexNativeRefreshKeys, coercePluginConfigUpdates, patchPluginConfigStore, readPluginConfigStore, readNamespacedPluginConfigStoreIdentity, resetPluginConfigStore, resolveConfigRead, resolveEffectivePluginConfig, resolvePluginConfigStorePath, resolvePluginConfigSchemaAccess, summarizeEffectivePluginConfig, } from './plugin-config.js';
10
+ import { assertPluginConfigMutationCandidate, assertSafePluginConfigSegment, coercePluginConfigUpdates, patchPluginConfigStore, PluginConfigSchemaResolutionError, readPluginConfigStore, readNamespacedPluginConfigStoreIdentity, resetPluginConfigStore, resolveConfigRead, resolveEffectivePluginConfig, resolvePluginConfigStorePath, resolvePluginConfigSchemaAccess, summarizeEffectivePluginConfig, } from './plugin-config.js';
7
11
  import { analyzePluginConfigMutationImpact, discoverPluginConfigManagedCommandPackages, pluginConfigTargetRenderScopes, pluginSharedNativeConfigTargets } from './plugin-config-impact.js';
8
- import { resolvePluginConfigReferencePlugins, resolvePluginConfigReferenceConsumers } from './plugin-config-references.js';
12
+ import { collectPluginConfigReferenceEvidence, resolvePluginConfigReferencePlugins, resolvePluginConfigReferenceConsumers } from './plugin-config-references.js';
9
13
  import { orderPluginMetasByDependencies, resolvePluginMetaDependencyClosure } from './plugin-dependencies.js';
10
- import { assertManagedCommandPackageNativeToolsUnchanged, syncPluginManagedCommandPackage, } from './managed-command-materialization.js';
11
- import { managedCommandDiagnostic } from './managed-command-store.js';
14
+ import { assertManagedCommandPackageNativeToolsUnchanged, discoverInstalledPluginManagedCommandPackages, syncPluginManagedCommandPackage, } from './managed-command-materialization.js';
15
+ import { listManagedCommandPackages, managedCommandDiagnostic, readInstalledCommandProducer } from './managed-command-store.js';
12
16
  import { collectSensitivePluginConfigValues, redactPluginScriptOutput, } from './plugin-scripts.js';
13
- import { resolveClaudeProjectKey, resolveCodexProjectKey } from './paths.js';
17
+ import { resolveClaudeProjectKey, resolveCodexProjectKey, resolveCliPackageRoot } from './paths.js';
18
+ import { readSafeOperationError } from './safe-operation-diagnostic.js';
14
19
  import { withAwHomeMutationLock, } from './aw-home-mutation-lock.js';
15
- import { assertProjectSharedConfigPathVisible, } from './project-boundary.js';
20
+ import fs from 'fs-extra';
21
+ import path from 'node:path';
22
+ import crypto from 'node:crypto';
16
23
  export function buildConfigMutationReceipt(command, receipt) {
17
24
  return {
18
25
  ...receipt,
@@ -128,21 +135,21 @@ async function discoverVisibleTargets(options) {
128
135
  : {}),
129
136
  });
130
137
  for (const statusTarget of flattenStatusTargets(status.payload.targets)) {
131
- const intent = statusTargetIntent(statusTarget);
132
- if ((!selectors.includes(statusTarget.pluginId ?? '') && !consumers.some((plugin) => plugin.manifest.name === statusTarget.pluginName)
138
+ if (!selectors.includes(statusTarget.pluginId ?? '')
139
+ && !consumers.some((plugin) => plugin.manifest.name === statusTarget.pluginName)
133
140
  && statusTarget.pluginId !== selector && statusTarget.pluginName !== options.pluginName)
134
- || !intent.enabled)
135
141
  continue;
136
- for (const scope of intent.scopes) {
137
- if (options.scope === 'project' && scope !== 'project')
138
- continue;
139
- targets.push(await createCandidateTarget({
140
- pluginName: statusTarget.pluginName,
141
- platform: statusTarget.platform,
142
- scope,
143
- cwd: options.cwd,
144
- }));
145
- }
142
+ // A declaration alone is not a consumer. Config apply cannot install it.
143
+ if (!statusTargetHasLiveConfigConsumer(statusTarget))
144
+ continue;
145
+ if (options.scope === 'user' && statusTarget.scope !== 'user')
146
+ continue;
147
+ targets.push(await createCandidateTarget({
148
+ pluginName: statusTarget.pluginName,
149
+ platform: statusTarget.platform,
150
+ scope: statusTarget.scope,
151
+ cwd: options.cwd,
152
+ }));
146
153
  }
147
154
  return { targets: dedupeTargets(targets), failures };
148
155
  }
@@ -181,7 +188,7 @@ async function reinstallVisibleTargets(options) {
181
188
  failed.add(`${plugin.manifest.name}\0${target.platform}\0${target.scope}`);
182
189
  results.push(...blocked.map((target) => ({ target, action: 'skipped',
183
190
  state: 'skipped', errorCode: 'dependencyApplyFailed',
184
- message: `A required provider failed. Run aw sync ${plugin.pluginId ?? plugin.manifest.name} --scope ${target.scope} --platform ${target.platform} --yes.` })));
191
+ message: `A required provider failed. Run aw plugin sync ${plugin.pluginId ?? plugin.manifest.name} --context ${target.scope} --target ${target.platform} --yes.` })));
185
192
  }
186
193
  const blockedPackages = packages.filter((target) => {
187
194
  const platforms = plugin.manifest.managedCommands?.packages.find((entry) => entry.key === target.packageKey
@@ -203,7 +210,7 @@ async function reinstallVisibleTargets(options) {
203
210
  const own = plugin.manifest.name === options.plugin.manifest.name;
204
211
  const result = await reinstallVisibleTargets({ ...options, plugin,
205
212
  changedConfigKeys: own ? options.changedConfigKeys : [],
206
- rebuildRuntime: { ...options.rebuildRuntime, managedCommandProducerCache },
213
+ rebuildRuntime: { ...options.rebuildRuntime, referencePlugins: plugins, managedCommandProducerCache },
207
214
  discovered: { targets: available, failures: [], commandPackages: availablePackages,
208
215
  commandDiscoveryErrors: [] } });
209
216
  if (result.state !== 'succeeded' && result.state !== 'skipped') {
@@ -229,277 +236,320 @@ async function reinstallVisibleTargets(options) {
229
236
  return { state: partial ? 'partial' : 'succeeded', targetResults: results,
230
237
  ...(commandResults.length ? { commandResults } : {}), ...(errors.length ? { errors } : {}) };
231
238
  }
232
- const targetResults = [...discovered.failures];
233
- const commandResults = new Map();
234
- let runtimeFailed = discovered.failures.length > 0
235
- || (discovered.commandDiscoveryErrors?.length ?? 0) > 0;
236
- let invalidTargets = 0;
237
- let unavailableTargets = 0;
238
- const errors = [];
239
- if ((discovered.commandDiscoveryErrors?.length ?? 0) > 0) {
240
- errors.push(...discovered.commandDiscoveryErrors.map((error) => ({
241
- stage: 'targetDiscovery', code: error.code, message: error.message,
242
- })));
243
- }
244
- const reinstall = options.reinstallCommandImpl ?? syncWriteCommand;
245
- if (options.plugin.sourceId && !options.plugin.pluginId) {
246
- throw new Error(`Plugin "${options.plugin.manifest.name}" is missing its namespaced plugin ID.`);
247
- }
248
- const selector = options.plugin.sourceId
249
- ? options.plugin.pluginId
250
- : options.plugin.manifest.name;
251
- const nativeFailures = [];
252
- for (const target of discovered.commandPackages ?? []) {
253
- try {
254
- await assertManagedCommandPackageNativeToolsUnchanged({ plugin: options.plugin, producer: target.producer, env: options.env });
255
- }
256
- catch (error) {
257
- nativeFailures.push({ producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
258
- errors: [managedCommandDiagnostic(error)],
259
- recoveryActions: [`aw command package sync ${selector} ${target.packageKey} --scope ${target.producer.scope} --yes`] });
260
- }
261
- }
262
- if (nativeFailures.length > 0) {
263
- return { state: 'partial', commandResults: nativeFailures,
264
- targetResults: [...targetResults, ...discovered.targets.map((target) => ({
265
- target, action: 'reinstall', state: 'failed', errorCode: 'nativeToolSyncRequired',
266
- message: 'Run a reviewed producer sync or package sync before configuration apply.',
267
- }))] };
268
- }
269
- const sanitizeFailure = async (message, target) => {
270
- let sensitiveValues = [];
271
- try {
272
- const effective = await resolveEffectivePluginConfig({
273
- ...(options.plugin.sourceId ? {
274
- sourceId: options.plugin.sourceId,
275
- sourceIdentity: options.plugin.sourceIdentity,
276
- logicalIdentity: options.plugin.catalogLogicalIdentity,
277
- } : {}),
278
- pluginName: options.plugin.manifest.name,
279
- manifest: options.plugin.manifest,
280
- referencePlugins: options.rebuildRuntime?.referencePlugins,
281
- targetScope: target.scope,
282
- cwd: options.cwd,
283
- env: options.env,
284
- });
285
- sensitiveValues = collectSensitivePluginConfigValues(options.plugin.manifest, effective.values);
239
+ const contexts = new Map();
240
+ const demands = new Map();
241
+ const failedScopes = new Set();
242
+ try {
243
+ const targetResults = [...discovered.failures];
244
+ const commandResults = new Map();
245
+ let runtimeFailed = discovered.failures.length > 0
246
+ || (discovered.commandDiscoveryErrors?.length ?? 0) > 0;
247
+ let invalidTargets = 0;
248
+ let unavailableTargets = 0;
249
+ const errors = [];
250
+ for (const scope of new Set(discovered.targets.map((target) => target.scope))) {
251
+ try {
252
+ const context = await createScopeContext({ scope, cwd: options.cwd, env: options.env });
253
+ contexts.set(scope, context);
254
+ const demand = await readDemand({ scope, cwd: context.executionCwd, env: context.env });
255
+ demandNativeProjection(demand);
256
+ demands.set(scope, demand);
257
+ }
258
+ catch {
259
+ failedScopes.add(scope);
260
+ runtimeFailed = true;
261
+ for (const target of discovered.targets.filter((entry) => entry.scope === scope)) {
262
+ targetResults.push({ target, action: 'reinstall', state: 'failed', errorCode: 'configApplyContextUnavailable',
263
+ message: 'The target scope or its demand cannot be read safely.' });
264
+ }
265
+ errors.push({ stage: 'targetValidation', code: 'configApplyContextUnavailable',
266
+ message: 'A visible target scope or its demand cannot be read safely.' });
267
+ }
286
268
  }
287
- catch {
288
- // Generic credential redaction and known config paths still apply.
269
+ if ((discovered.commandDiscoveryErrors?.length ?? 0) > 0) {
270
+ errors.push(...discovered.commandDiscoveryErrors.map((error) => ({
271
+ stage: 'targetDiscovery', code: error.code, message: error.message,
272
+ })));
289
273
  }
290
- let sanitized = redactPluginScriptOutput(message, sensitiveValues);
291
- for (const privatePath of [target.projectKey, options.cwd]) {
292
- if (privatePath)
293
- sanitized = sanitized.split(privatePath).join('<project>');
274
+ const reinstall = options.reinstallCommandImpl ?? syncWriteCommand;
275
+ if (options.plugin.sourceId && !options.plugin.pluginId) {
276
+ throw new Error(`Plugin "${options.plugin.manifest.name}" is missing its namespaced plugin ID.`);
294
277
  }
295
- for (const scope of ['user', 'project']) {
296
- const configPath = resolvePluginConfigStorePath({
297
- sourceId: options.plugin.sourceId,
298
- pluginName: options.plugin.manifest.name,
299
- scope,
300
- cwd: options.cwd,
301
- env: options.env,
302
- });
303
- const safeTarget = `${scope}/plugin-config/${[
304
- options.plugin.sourceId,
305
- options.plugin.manifest.name,
306
- 'config.json',
307
- ].filter(Boolean).join('/')}`;
308
- sanitized = sanitized.split(configPath).join(safeTarget);
278
+ const selector = options.plugin.sourceId
279
+ ? options.plugin.pluginId
280
+ : options.plugin.manifest.name;
281
+ const nativeFailures = [];
282
+ for (const target of discovered.commandPackages ?? []) {
283
+ try {
284
+ await assertManagedCommandPackageNativeToolsUnchanged({ plugin: options.plugin, producer: target.producer, env: options.env });
285
+ }
286
+ catch (error) {
287
+ nativeFailures.push({ producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
288
+ errors: [managedCommandDiagnostic(error)],
289
+ recoveryActions: [`aw command package sync ${selector} ${target.packageKey} --scope ${target.producer.scope} --yes`] });
290
+ }
309
291
  }
310
- return sanitized;
311
- };
312
- const sanitizeProjection = async (value, target, freeText = false) => {
313
- if (typeof value === 'string')
314
- return freeText ? sanitizeFailure(value, target) : value;
315
- if (value === null || typeof value !== 'object')
316
- return value;
317
- if (Array.isArray(value)) {
318
- return Promise.all(value.map((entry) => sanitizeProjection(entry, target, freeText)));
292
+ if (nativeFailures.length > 0) {
293
+ return { state: 'partial', commandResults: nativeFailures,
294
+ targetResults: [...targetResults, ...discovered.targets.map((target) => ({
295
+ target, action: 'reinstall', state: 'failed', errorCode: 'nativeToolSyncRequired',
296
+ message: 'Run a reviewed producer sync or package sync before configuration apply.',
297
+ }))] };
319
298
  }
320
- return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, entry]) => [
321
- key,
322
- await sanitizeProjection(entry, target, typeof entry === 'string'
323
- ? /^(?:message|error|stack|stdout|stderr|output|warning)$/u.test(key)
324
- : Array.isArray(entry) && /^(?:messages|warnings)$/u.test(key)),
325
- ])));
326
- };
327
- const preApplyExecutionCache = new Map();
328
- for (const target of discovered.targets) {
329
- try {
330
- const configs = await Promise.all(pluginConfigTargetRenderScopes(options.plugin, target).map((scope) => resolveEffectivePluginConfig({
331
- ...(options.plugin.sourceId ? {
299
+ const sanitizeFailure = async (message, target) => {
300
+ let sensitiveValues = [];
301
+ try {
302
+ const effective = await resolveEffectivePluginConfig({
303
+ ...(options.plugin.sourceId ? {
304
+ sourceId: options.plugin.sourceId,
305
+ sourceIdentity: options.plugin.sourceIdentity,
306
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
307
+ } : {}),
308
+ pluginName: options.plugin.manifest.name,
309
+ manifest: options.plugin.manifest,
310
+ referencePlugins: options.rebuildRuntime?.referencePlugins,
311
+ targetScope: target.scope,
312
+ platform: target.platform ?? undefined,
313
+ cwd: options.cwd,
314
+ env: options.env,
315
+ });
316
+ sensitiveValues = collectSensitivePluginConfigValues(options.plugin.manifest, effective.values, target.platform ?? undefined);
317
+ }
318
+ catch {
319
+ // Generic credential redaction and known config paths still apply.
320
+ }
321
+ let sanitized = redactPluginScriptOutput(message, sensitiveValues);
322
+ for (const privatePath of [target.projectKey, options.cwd]) {
323
+ if (privatePath)
324
+ sanitized = sanitized.split(privatePath).join('<project>');
325
+ }
326
+ for (const scope of ['user', 'project']) {
327
+ const configPath = resolvePluginConfigStorePath({
332
328
  sourceId: options.plugin.sourceId,
333
- sourceIdentity: options.plugin.sourceIdentity,
334
- logicalIdentity: options.plugin.catalogLogicalIdentity,
335
- } : {}),
336
- pluginName: options.plugin.manifest.name,
337
- manifest: options.plugin.manifest,
338
- referencePlugins: options.rebuildRuntime?.referencePlugins,
339
- targetScope: scope,
340
- cwd: options.cwd,
341
- env: options.env,
342
- })));
343
- if (configs.some((effective) => effective.issues.length > 0)) {
344
- invalidTargets += 1;
329
+ pluginName: options.plugin.manifest.name,
330
+ scope,
331
+ cwd: options.cwd,
332
+ env: options.env,
333
+ });
334
+ const safeTarget = `${scope}/plugin-config/${[
335
+ options.plugin.sourceId,
336
+ options.plugin.manifest.name,
337
+ 'config.json',
338
+ ].filter(Boolean).join('/')}`;
339
+ sanitized = sanitized.split(configPath).join(safeTarget);
340
+ }
341
+ return sanitized;
342
+ };
343
+ const sanitizeProjection = async (value, target, freeText = false) => {
344
+ if (typeof value === 'string')
345
+ return freeText ? sanitizeFailure(value, target) : value;
346
+ if (value === null || typeof value !== 'object')
347
+ return value;
348
+ if (Array.isArray(value)) {
349
+ return Promise.all(value.map((entry) => sanitizeProjection(entry, target, freeText)));
350
+ }
351
+ return Object.fromEntries(await Promise.all(Object.entries(value).map(async ([key, entry]) => [
352
+ key,
353
+ await sanitizeProjection(entry, target, typeof entry === 'string'
354
+ ? /^(?:message|error|stack|stdout|stderr|output|warning)$/u.test(key)
355
+ : Array.isArray(entry) && /^(?:messages|warnings)$/u.test(key)),
356
+ ])));
357
+ };
358
+ const preApplyExecutionCache = new Map();
359
+ for (const target of discovered.targets) {
360
+ if (failedScopes.has(target.scope))
361
+ continue;
362
+ try {
363
+ const configs = await Promise.all(pluginConfigTargetRenderScopes(options.plugin, target).map((scope) => resolveEffectivePluginConfig({
364
+ ...(options.plugin.sourceId ? {
365
+ sourceId: options.plugin.sourceId,
366
+ sourceIdentity: options.plugin.sourceIdentity,
367
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
368
+ } : {}),
369
+ pluginName: options.plugin.manifest.name,
370
+ manifest: options.plugin.manifest,
371
+ referencePlugins: options.rebuildRuntime?.referencePlugins,
372
+ targetScope: scope,
373
+ platform: target.platform ?? undefined,
374
+ cwd: options.cwd,
375
+ env: options.env,
376
+ })));
377
+ if (configs.some((effective) => effective.issues.length > 0)) {
378
+ invalidTargets += 1;
379
+ targetResults.push({
380
+ target,
381
+ action: 'reinstall',
382
+ state: 'skipped',
383
+ errorCode: 'invalidConfig',
384
+ message: 'The effective config is invalid for this target.',
385
+ });
386
+ continue;
387
+ }
388
+ }
389
+ catch {
390
+ runtimeFailed = true;
391
+ unavailableTargets += 1;
345
392
  targetResults.push({
346
393
  target,
347
394
  action: 'reinstall',
348
- state: 'skipped',
349
- errorCode: 'invalidConfig',
350
- message: 'The effective config is invalid for this target.',
395
+ state: 'failed',
396
+ errorCode: 'effectiveConfigUnavailable',
397
+ message: 'The effective config cannot be read for this target.',
398
+ });
399
+ errors.push({
400
+ stage: 'targetValidation',
401
+ code: 'effectiveConfigUnavailable',
402
+ message: 'The effective config cannot be read for a visible target.',
351
403
  });
352
404
  continue;
353
405
  }
354
- }
355
- catch {
356
- runtimeFailed = true;
357
- unavailableTargets += 1;
358
- targetResults.push({
359
- target,
360
- action: 'reinstall',
361
- state: 'failed',
362
- errorCode: 'effectiveConfigUnavailable',
363
- message: 'The effective config cannot be read for this target.',
364
- });
365
- errors.push({
366
- stage: 'targetValidation',
367
- code: 'effectiveConfigUnavailable',
368
- message: 'The effective config cannot be read for a visible target.',
369
- });
370
- continue;
371
- }
372
- try {
373
- const debugLogger = options.rebuildRuntime?.debugLogger;
374
- const reporter = options.rebuildRuntime?.reporter;
375
- const result = await reinstall([selector], {
376
- cwd: options.cwd,
377
- platform: target.platform,
378
- scope: target.scope,
379
- ...(options.rebuildRuntime?.scriptTimeoutMs === undefined
380
- ? {}
381
- : { scriptTimeoutMs: options.rebuildRuntime.scriptTimeoutMs }),
382
- }, {
383
- ...options.rebuildRuntime,
384
- env: options.env,
385
- nativeCliRunner,
386
- preApplyConfirmed: options.preApplyConfirmed === true,
387
- preApplyExecutionCache,
388
- managedCommandProducerCache,
389
- ...(debugLogger ? {
390
- debugLogger: {
391
- ...debugLogger,
392
- log: async (value) => debugLogger.log(await sanitizeProjection(value, target)),
393
- },
394
- } : {}),
395
- ...(reporter ? {
396
- reporter: {
397
- event: async (value) => reporter.event(await sanitizeProjection(value, target)),
398
- notice: async (value) => reporter.notice(await sanitizeProjection(value, target)),
406
+ try {
407
+ const debugLogger = options.rebuildRuntime?.debugLogger;
408
+ const reporter = options.rebuildRuntime?.reporter;
409
+ const result = await reinstall([selector], {
410
+ cwd: contexts.get(target.scope).executionCwd,
411
+ platform: target.platform,
412
+ scope: target.scope,
413
+ ...(options.rebuildRuntime?.scriptTimeoutMs === undefined
414
+ ? {}
415
+ : { scriptTimeoutMs: options.rebuildRuntime.scriptTimeoutMs }),
416
+ }, {
417
+ ...options.rebuildRuntime,
418
+ env: contexts.get(target.scope).env,
419
+ nativeCliRunner,
420
+ executionInput: {
421
+ context: contexts.get(target.scope),
422
+ requirements: demandNativeProjection(demands.get(target.scope)),
423
+ selectedTargets: [{ pluginId: selector, platform: target.platform, scope: target.scope }],
424
+ preserveOtherObservedTargets: true,
399
425
  },
400
- } : {}),
401
- });
402
- options.rebuildRuntime?.operationDiagnosticSink?.(result.rebuildResults);
403
- for (const producer of result.commandResults ?? []) {
404
- commandResults.set(getCommandPackageId(producer.producer), producer);
405
- if (producer.state !== 'completed')
426
+ preApplyConfirmed: options.preApplyConfirmed === true,
427
+ preApplyExecutionCache,
428
+ managedCommandProducerCache,
429
+ ...(debugLogger ? {
430
+ debugLogger: {
431
+ ...debugLogger,
432
+ log: async (value) => debugLogger.log(await sanitizeProjection(value, target)),
433
+ },
434
+ } : {}),
435
+ ...(reporter ? {
436
+ reporter: {
437
+ event: async (value) => reporter.event(await sanitizeProjection(value, target)),
438
+ notice: async (value) => reporter.notice(await sanitizeProjection(value, target)),
439
+ },
440
+ } : {}),
441
+ });
442
+ options.rebuildRuntime?.operationDiagnosticSink?.(result.rebuildResults);
443
+ for (const producer of result.commandResults ?? []) {
444
+ commandResults.set(getCommandPackageId(producer.producer), producer);
445
+ if (producer.state !== 'completed')
446
+ runtimeFailed = true;
447
+ }
448
+ if (result.overallState !== 'succeeded')
406
449
  runtimeFailed = true;
450
+ targetResults.push(...await Promise.all(result.targetResults.map(async (entry) => ({
451
+ ...entry,
452
+ state: entry.action === 'skipped' ? 'skipped' : entry.state,
453
+ ...(entry.message ? { message: await sanitizeFailure(entry.message, entry.target) } : {}),
454
+ }))));
407
455
  }
408
- if (result.overallState !== 'succeeded')
456
+ catch (error) {
409
457
  runtimeFailed = true;
410
- targetResults.push(...await Promise.all(result.targetResults.map(async (entry) => ({
411
- ...entry,
412
- state: entry.action === 'skipped' ? 'skipped' : entry.state,
413
- ...(entry.message ? { message: await sanitizeFailure(entry.message, entry.target) } : {}),
414
- }))));
415
- }
416
- catch (error) {
417
- runtimeFailed = true;
418
- const message = await sanitizeFailure(error instanceof Error ? error.message : String(error), target);
419
- targetResults.push({
420
- target,
421
- action: 'reinstall',
422
- state: 'failed',
423
- message,
424
- });
425
- errors.push({
426
- stage: 'apply',
427
- code: 'targetApplyFailed',
428
- message: 'A visible config apply target failed.',
429
- });
430
- }
431
- }
432
- for (const target of discovered.commandPackages ?? []) {
433
- await options.rebuildRuntime?.reporter?.event({
434
- type: 'managed-command:producer',
435
- source: { plugin: options.plugin.manifest.name, scope: target.producer.scope },
436
- data: { packageKey: target.packageKey, phase: 'start' },
437
- });
438
- let result;
439
- if (!target.declared) {
440
- result = {
441
- producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
442
- errors: [{ code: 'commandPackageDeclarationMissing',
443
- message: 'The installed command package is no longer declared by this plugin.' }],
444
- recoveryActions: [`aw command package remove ${target.packageId} --yes`],
445
- };
446
- }
447
- else {
448
- try {
449
- result = await syncPluginManagedCommandPackage({
450
- plugin: options.plugin, packageKey: target.packageKey, scope: target.producer.scope,
451
- cwd: options.cwd, env: options.env, managedCommandProducerCache,
452
- referencePlugins: options.rebuildRuntime?.referencePlugins,
453
- event: options.changedConfigKeys?.length ? 'refresh' : 'install',
454
- changedConfigKeys: options.changedConfigKeys ?? [],
458
+ const message = await sanitizeFailure(error instanceof Error ? error.message : String(error), target);
459
+ targetResults.push({
460
+ target,
461
+ action: 'reinstall',
462
+ state: 'failed',
463
+ message,
464
+ });
465
+ errors.push({
466
+ stage: 'apply',
467
+ code: 'targetApplyFailed',
468
+ message: 'A visible config apply target failed.',
455
469
  });
456
470
  }
457
- catch {
471
+ }
472
+ for (const target of discovered.commandPackages ?? []) {
473
+ await options.rebuildRuntime?.reporter?.event({
474
+ type: 'managed-command:producer',
475
+ source: { plugin: options.plugin.manifest.name, scope: target.producer.scope },
476
+ data: { packageKey: target.packageKey, phase: 'start' },
477
+ });
478
+ let result;
479
+ if (!target.declared) {
458
480
  result = {
459
481
  producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
460
- errors: [{ code: 'commandPackageApplyFailed', message: 'The managed command package could not be refreshed.' }],
461
- recoveryActions: [`aw command package sync ${selector} ${target.packageKey} --scope ${target.producer.scope} --yes`,
462
- `aw command package remove ${target.packageId} --yes`],
482
+ errors: [{ code: 'commandPackageDeclarationMissing',
483
+ message: 'The installed command package is no longer declared by this plugin.' }],
484
+ recoveryActions: [`aw command package remove ${target.packageId} --yes`],
463
485
  };
464
486
  }
487
+ else {
488
+ try {
489
+ result = await syncPluginManagedCommandPackage({
490
+ plugin: options.plugin, packageKey: target.packageKey, scope: target.producer.scope,
491
+ cwd: options.cwd, env: options.env, managedCommandProducerCache,
492
+ referencePlugins: options.rebuildRuntime?.referencePlugins,
493
+ event: options.changedConfigKeys?.length ? 'refresh' : 'install',
494
+ changedConfigKeys: options.changedConfigKeys ?? [],
495
+ signal: options.rebuildRuntime?.signal,
496
+ scriptTimeoutMs: options.rebuildRuntime?.scriptTimeoutMs,
497
+ });
498
+ }
499
+ catch (error) {
500
+ if (options.rebuildRuntime?.signal?.aborted
501
+ || error instanceof Error && error.name === 'AbortError'
502
+ || readSafeOperationError(error))
503
+ throw error;
504
+ result = {
505
+ producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
506
+ errors: [{ code: 'commandPackageApplyFailed', message: 'The managed command package could not be refreshed.' }],
507
+ recoveryActions: [`aw command package sync ${selector} ${target.packageKey} --scope ${target.producer.scope} --yes`,
508
+ `aw command package remove ${target.packageId} --yes`],
509
+ };
510
+ }
511
+ }
512
+ result = { ...result, errors: await Promise.all(result.errors.map(async (error) => ({
513
+ ...error, message: await sanitizeFailure(error.message, { scope: target.producer.scope,
514
+ ...(target.producer.projectRoot ? { projectKey: target.producer.projectRoot } : {}) }),
515
+ }))) };
516
+ commandResults.set(getCommandPackageId(result.producer), result);
517
+ await options.rebuildRuntime?.reporter?.event({
518
+ type: 'managed-command:producer',
519
+ source: { plugin: options.plugin.manifest.name, scope: target.producer.scope },
520
+ data: { packageKey: target.packageKey, phase: result.state === 'completed' ? 'success' : 'failure' },
521
+ });
522
+ if (result.state !== 'completed')
523
+ runtimeFailed = true;
465
524
  }
466
- result = { ...result, errors: await Promise.all(result.errors.map(async (error) => ({
467
- ...error, message: await sanitizeFailure(error.message, { scope: target.producer.scope,
468
- ...(target.producer.projectRoot ? { projectKey: target.producer.projectRoot } : {}) }),
469
- }))) };
470
- commandResults.set(getCommandPackageId(result.producer), result);
471
- await options.rebuildRuntime?.reporter?.event({
472
- type: 'managed-command:producer',
473
- source: { plugin: options.plugin.manifest.name, scope: target.producer.scope },
474
- data: { packageKey: target.packageKey, phase: result.state === 'completed' ? 'success' : 'failure' },
475
- });
476
- if (result.state !== 'completed')
477
- runtimeFailed = true;
478
- }
479
- const commands = commandResults.size > 0 ? { commandResults: [...commandResults.values()] } : {};
480
- const commandTargetCount = (discovered.commandPackages?.length ?? 0);
481
- if (discovered.targets.length === 0 && discovered.failures.length === 0
482
- && commandTargetCount === 0 && !runtimeFailed) {
483
- return skippedApply('noVisibleTarget');
484
- }
485
- if (discovered.targets.length > 0 && invalidTargets === discovered.targets.length && commandTargetCount === 0) {
486
- return { ...unavailableApply('invalidConfig', 'targetValidation', targetResults), ...commands };
487
- }
488
- if (discovered.targets.length > 0 && unavailableTargets === discovered.targets.length && commandTargetCount === 0) {
489
- return { ...unavailableApply('effectiveConfigUnavailable', 'targetValidation', targetResults), ...commands };
525
+ const commands = commandResults.size > 0 ? { commandResults: [...commandResults.values()] } : {};
526
+ const commandTargetCount = (discovered.commandPackages?.length ?? 0);
527
+ if (discovered.targets.length === 0 && discovered.failures.length === 0
528
+ && commandTargetCount === 0 && !runtimeFailed) {
529
+ return skippedApply('noVisibleTarget');
530
+ }
531
+ if (discovered.targets.length > 0 && invalidTargets === discovered.targets.length && commandTargetCount === 0) {
532
+ return { ...unavailableApply('invalidConfig', 'targetValidation', targetResults), ...commands };
533
+ }
534
+ if (discovered.targets.length > 0 && unavailableTargets === discovered.targets.length && commandTargetCount === 0) {
535
+ return { ...unavailableApply('effectiveConfigUnavailable', 'targetValidation', targetResults), ...commands };
536
+ }
537
+ if (runtimeFailed
538
+ || invalidTargets > 0
539
+ || unavailableTargets > 0
540
+ || targetResults.some((entry) => entry.state === 'failed' || entry.state === 'skipped')) {
541
+ return {
542
+ state: 'partial',
543
+ targetResults,
544
+ ...commands,
545
+ ...(errors.length > 0 ? { errors } : {}),
546
+ };
547
+ }
548
+ return { state: 'succeeded', targetResults, ...commands };
490
549
  }
491
- if (runtimeFailed
492
- || invalidTargets > 0
493
- || unavailableTargets > 0
494
- || targetResults.some((entry) => entry.state === 'failed' || entry.state === 'skipped')) {
495
- return {
496
- state: 'partial',
497
- targetResults,
498
- ...commands,
499
- ...(errors.length > 0 ? { errors } : {}),
500
- };
550
+ finally {
551
+ await Promise.all([...contexts.values()].map((context) => context.dispose()));
501
552
  }
502
- return { state: 'succeeded', targetResults, ...commands };
503
553
  }
504
554
  async function resolveConfigApplyTargets(options) {
505
555
  const platformTargets = options.applyTargets !== undefined
@@ -523,48 +573,19 @@ async function resolveConfigApplyTargets(options) {
523
573
  async function configApplyHasValidSharedNativeTarget(options) {
524
574
  const plugins = await resolvePluginConfigReferenceConsumers({ plugin: options.plugin, env: options.env, plugins: options.plugins });
525
575
  for (const plugin of plugins) {
526
- const sharedTargets = pluginSharedNativeConfigTargets(plugin);
527
- if (!options.targets.some((target) => target.pluginName === plugin.manifest.name
528
- && sharedTargets.some((shared) => (shared.platform ?? 'codex') === target.platform)))
529
- continue;
530
- const effective = await resolveEffectivePluginConfig({
531
- ...(plugin.sourceId ? {
532
- sourceId: plugin.sourceId,
533
- sourceIdentity: plugin.sourceIdentity,
534
- logicalIdentity: plugin.catalogLogicalIdentity,
535
- } : {}),
536
- pluginName: plugin.manifest.name,
537
- manifest: plugin.manifest,
538
- referencePlugins: options.plugins,
539
- targetScope: 'user',
540
- cwd: options.cwd,
541
- env: options.env,
542
- });
543
- if (effective.issues.length === 0)
544
- return true;
545
- }
546
- return false;
547
- }
548
- async function applyConfigToVisibleTargets(options) {
549
- let discovered;
550
- if (options.discovered) {
551
- discovered = options.discovered;
552
- }
553
- else {
554
- try {
555
- discovered = await resolveConfigApplyTargets(options);
556
- }
557
- catch {
558
- return unavailableApply('targetDiscoveryFailed', 'targetDiscovery');
576
+ for (const target of options.targets.filter((target) => target.pluginName === plugin.manifest.name)) {
577
+ const shared = pluginSharedNativeConfigTargets(plugin);
578
+ if (!shared.some((entry) => (entry.platform ?? 'codex') === target.platform))
579
+ continue;
580
+ const effective = await resolveEffectivePluginConfig({ sourceId: plugin.sourceId,
581
+ sourceIdentity: plugin.sourceIdentity, logicalIdentity: plugin.catalogLogicalIdentity,
582
+ manifest: plugin.manifest, platform: target.platform, targetScope: 'user',
583
+ cwd: options.cwd, env: options.env, referencePlugins: options.plugins });
584
+ if (!effective.issues.length)
585
+ return true;
559
586
  }
560
587
  }
561
- if (discovered.targets.length === 0
562
- && discovered.failures.length === 0
563
- && (discovered.commandPackages?.length ?? 0) === 0
564
- && options.noVisibleTargetHasInvalidConfig) {
565
- return unavailableApply('invalidConfig', 'targetValidation');
566
- }
567
- return reinstallVisibleTargets({ ...options, discovered });
588
+ return false;
568
589
  }
569
590
  export async function resolveConfigPreApplyAuthorization(options) {
570
591
  const applyTargets = dedupeTargets(options.applyTargets);
@@ -609,17 +630,27 @@ export async function resolveConfigPreApplyAuthorization(options) {
609
630
  const resolveGroup = async (targets) => {
610
631
  const platforms = new Set(targets.map((target) => target.platform));
611
632
  const platform = platforms.size === 2 ? 'both' : [...platforms][0];
633
+ let context;
612
634
  try {
635
+ context = await createScopeContext({ scope: targets[0].scope, cwd: options.cwd, env: options.env });
636
+ const demand = await readDemand({ scope: targets[0].scope,
637
+ cwd: context.executionCwd, env: context.env });
613
638
  const preview = await (options.reinstallCommandImpl ?? syncWriteCommand)([selector], {
614
- cwd: options.cwd,
639
+ cwd: context.executionCwd,
615
640
  platform,
616
641
  scope: targets[0].scope,
617
642
  }, {
618
643
  ...options.rebuildRuntime,
619
- env: options.env ?? process.env,
644
+ env: context.env,
620
645
  nativeCliRunner: options.nativeCliRunner,
621
646
  previewOperationPlan: true,
622
647
  resolvePreApplyAuthorization: true,
648
+ executionInput: {
649
+ context, requirements: demandNativeProjection(demand),
650
+ selectedTargets: targets.map((target) => ({ pluginId: selector,
651
+ platform: target.platform, scope: target.scope })),
652
+ preserveOtherObservedTargets: true,
653
+ },
623
654
  });
624
655
  if (preview.overallState !== 'succeeded' || !preview.preApplyAuthorization)
625
656
  return false;
@@ -630,6 +661,9 @@ export async function resolveConfigPreApplyAuthorization(options) {
630
661
  catch {
631
662
  return false;
632
663
  }
664
+ finally {
665
+ await context?.dispose();
666
+ }
633
667
  };
634
668
  for (const targets of groups.values()) {
635
669
  if (await resolveGroup(targets))
@@ -686,7 +720,7 @@ function preApplyAuthorizationIsConfirmed(options) {
686
720
  }
687
721
  function otherProjectsWarning(scope) {
688
722
  return scope === 'user'
689
- ? ['其它项目不会由本次命令扫描或更新,可能需要在对应项目目录中运行 aw sync。']
723
+ ? ['其它项目不会由本次命令扫描或更新,可能需要在对应项目目录中运行 aw plugin sync --context project。']
690
724
  : [];
691
725
  }
692
726
  function buildReceiptFieldEntry(options) {
@@ -816,303 +850,397 @@ export async function replayConfigRuntimeWrites(options) {
816
850
  },
817
851
  });
818
852
  }
819
- function throwSharedNativeApplyConfirmationRequired() {
820
- const error = new Error('sharedNativeConfigApplyConfirmationRequired: Applying this user config changes a shared native artifact. Re-run with --yes.');
821
- error.code = 'sharedNativeConfigApplyConfirmationRequired';
822
- throw error;
853
+ function configApplyRequestedTargets(plan) {
854
+ return plan.receipt.requestedTargets.filter((target) => !plan.receipt.targetResults.some((result) => result.pluginId === target.pluginId && result.agent === target.agent && result.scope === target.scope
855
+ && (result.satisfaction === 'unsupported' || result.satisfaction === 'excluded')));
823
856
  }
824
- async function applyPluginConfigWriteUnlocked(options) {
825
- const requestedPluginName = options.pluginName ?? options.plugin?.manifest.name;
826
- if (!requestedPluginName)
827
- throw new Error('Plugin name is required for config write.');
828
- const pluginName = assertSafePluginConfigSegment(requestedPluginName);
829
- const configIdentity = options.plugin?.sourceId ? {
830
- sourceId: options.plugin.sourceId,
831
- sourceIdentity: options.plugin.sourceIdentity,
832
- logicalIdentity: options.plugin.catalogLogicalIdentity,
833
- } : {};
834
- const requestedUpdates = options.request.updates ?? {};
835
- const requestedRemoveKeys = uniqueInOrder(options.request.removeKeys ?? []);
836
- const duplicateKeys = requestedRemoveKeys.filter((key) => Object.hasOwn(requestedUpdates, key));
837
- if (duplicateKeys.length > 0) {
838
- throw new Error(`Config write cannot update and remove the same key: ${duplicateKeys.join(', ')}.`);
839
- }
840
- const schemaAccess = await resolvePluginConfigSchemaAccess({
841
- pluginName,
842
- currentPlugin: options.plugin,
843
- currentPluginError: options.pluginError,
844
- scope: options.scope,
845
- cwd: options.cwd,
846
- env: options.env,
847
- });
848
- const schemaPlugin = schemaAccess.plugin;
849
- const schema = schemaPlugin?.manifest.config?.fields;
850
- if (!schema || !schemaPlugin) {
851
- throw new Error(`configSchemaUnavailable: Plugin "${pluginName}" does not declare an AW config schema.`);
852
- }
853
- if (options.scope === 'project') {
854
- const userStore = await readPluginConfigStore({
855
- ...configIdentity,
856
- pluginName,
857
- scope: 'user',
858
- cwd: options.cwd,
859
- env: options.env,
860
- schema,
861
- });
862
- assertReadyPluginConfigStore(userStore);
863
- }
864
- if (options.scope === 'project') {
865
- const rejectedSensitiveKeys = Object.keys(requestedUpdates)
866
- .filter((key) => schema[key]?.sensitive === true);
867
- if (rejectedSensitiveKeys.length > 0) {
868
- const error = new Error('sensitiveProjectConfigUnsupported: Sensitive config can only be set in user scope.');
869
- error.code = 'sensitiveProjectConfigUnsupported';
870
- throw error;
871
- }
872
- const userOnlyKeys = codexNativeRefreshKeys(schemaPlugin.manifest);
873
- const rejectedKeys = Object.keys(requestedUpdates).filter((key) => userOnlyKeys.has(key));
874
- if (rejectedKeys.length > 0) {
875
- const error = new Error(`sharedNativeConfigUserScopeRequired: Codex native Skill refresh keys can only be set in user scope: ${rejectedKeys.join(', ')}.`);
876
- error.code = 'sharedNativeConfigUserScopeRequired';
877
- throw error;
857
+ async function prepareConfigApplyPackages(options, plugins, plan, referencePlugins) {
858
+ const env = options.env ?? process.env;
859
+ const context = options.context ?? options.scope;
860
+ const discoveries = [];
861
+ const scopes = context === 'project' ? ['user', 'project'] : ['user'];
862
+ const nativeTargets = configApplyRequestedTargets(plan).flatMap((target) => target.agent === 'codex' || target.agent === 'claude'
863
+ ? [{ pluginName: target.pluginId.split('/').at(-1), pluginId: target.pluginId, platform: target.agent, scope: target.scope }] : []);
864
+ for (const plugin of plugins) {
865
+ for (const scope of scopes) {
866
+ discoveries.push(await discoverInstalledPluginManagedCommandPackages({ plugin, scope, cwd: options.cwd, env,
867
+ platformTargets: nativeTargets.filter((target) => target.pluginId === (plugin.pluginId ?? plugin.manifest.name)
868
+ && (scope === 'user' || target.scope === 'project')) }));
878
869
  }
879
870
  }
880
- let discovered;
881
- let targetDiscoveryFailed = false;
882
- if (options.apply !== false) {
883
- try {
884
- discovered = await resolveConfigApplyTargets({
885
- plugin: schemaPlugin,
886
- scope: options.scope,
887
- cwd: options.cwd,
888
- env: options.env ?? process.env,
889
- applyTargets: options.applyTargets,
890
- nativeCliRunner: options.nativeCliRunner,
891
- rebuildRuntime: options.rebuildRuntime,
892
- });
871
+ const packages = [...new Map(discoveries.flatMap((discovery) => discovery.packages).filter((target) => {
872
+ if (options.applyCommandPackages && !options.applyCommandPackages.includes(target.packageId))
873
+ return false;
874
+ if (options.platform)
875
+ return false;
876
+ if (!options.targets)
877
+ return true;
878
+ const plugin = plugins.find((entry) => entry.manifest.name === target.producer.pluginName);
879
+ const declaration = plugin?.manifest.managedCommands?.packages.find((entry) => entry.key === target.packageKey && entry.scope === target.producer.scope);
880
+ return declaration?.platforms.some((platform) => options.targets.includes(platform)) ?? false;
881
+ }).map((target) => [target.packageId, target])).values()];
882
+ const inventory = await listManagedCommandPackages(env);
883
+ const entries = await Promise.all(packages.map(async (target) => {
884
+ const plugin = plugins.find((entry) => entry.manifest.name === target.producer.pluginName);
885
+ const installed = inventory.packages.find((entry) => entry.id === target.packageId);
886
+ let recordDigest;
887
+ let blocker;
888
+ if (!target.installed || !installed) {
889
+ blocker = { code: 'configApplyRequiresExistingOutput', message: 'Configuration apply requires an existing command package. Run package sync first.' };
893
890
  }
894
- catch {
895
- targetDiscoveryFailed = true;
891
+ else if (!target.declared) {
892
+ blocker = { code: 'commandPackageDeclarationMissing', message: 'The installed command package is no longer declared by this plugin.' };
896
893
  }
897
- }
898
- const impact = await analyzePluginConfigMutationImpact({
899
- plugin: schemaPlugin,
900
- scope: options.scope,
901
- cwd: options.cwd,
902
- env: options.env,
903
- visibleTargets: discovered?.targets ?? [],
904
- referencePlugins: options.rebuildRuntime?.referencePlugins,
905
- ...(discovered ? { commandDiscovery: {
906
- packages: discovered.commandPackages ?? [], errors: discovered.commandDiscoveryErrors ?? [],
907
- } } : {}),
908
- mutation: {
909
- kind: 'patch',
910
- updates: requestedUpdates,
911
- removeKeys: requestedRemoveKeys,
894
+ else {
895
+ try {
896
+ const record = await readInstalledCommandProducer(installed.resourceRoot);
897
+ recordDigest = `sha256:${crypto.createHash('sha256').update(JSON.stringify(record)).digest('hex')}`;
898
+ await assertManagedCommandPackageNativeToolsUnchanged({ plugin, producer: target.producer, env });
899
+ }
900
+ catch (error) {
901
+ blocker = managedCommandDiagnostic(error);
902
+ }
903
+ }
904
+ return { target, recordDigest, blocker };
905
+ }));
906
+ const errors = [...new Map(discoveries.flatMap((discovery) => discovery.errors).map((error) => [JSON.stringify(error), error])).values()];
907
+ const evidence = { packages: entries, errors };
908
+ const targets = [...new Map(entries.map(({ target }) => {
909
+ const plugin = plugins.find((entry) => entry.manifest.name === target.producer.pluginName);
910
+ const pluginId = plugin.pluginId ?? plugin.manifest.name;
911
+ const dependencies = resolvePluginMetaDependencyClosure(referencePlugins, [plugin.manifest.name])
912
+ .filter((entry) => entry.manifest.name !== plugin.manifest.name).map((entry) => entry.pluginId ?? entry.manifest.name);
913
+ return [`${pluginId}\0${target.producer.scope}`, { pluginId, scope: target.producer.scope, dependencies }];
914
+ })).values()];
915
+ const cache = plan.runtime.managedCommandProducerCache ??= new Map();
916
+ const hook = { targets, evidence,
917
+ blocked: (pluginId, scope, code) => {
918
+ const plugin = plugins.find((entry) => (entry.pluginId ?? entry.manifest.name) === pluginId);
919
+ return entries.filter(({ target }) => target.producer.pluginName === plugin.manifest.name && target.producer.scope === scope)
920
+ .map(({ target }) => ({
921
+ producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
922
+ errors: [{ code, message: 'A required provider failed; the command package was not refreshed.' }],
923
+ recoveryActions: [`aw command package sync ${pluginId} ${target.packageKey} --scope ${scope} --yes`],
924
+ }));
912
925
  },
913
- });
914
- const updates = await coercePluginConfigUpdates({ schema, updates: requestedUpdates, cwd: options.cwd });
915
- await assertPluginConfigMutationCandidate({
916
- ...configIdentity,
917
- pluginName,
918
- manifest: schemaPlugin.manifest,
919
- scope: options.scope,
926
+ execute: async (pluginId, scope, token) => {
927
+ const plugin = plugins.find((entry) => (entry.pluginId ?? entry.manifest.name) === pluginId);
928
+ const selected = entries.filter(({ target }) => target.producer.pluginName === plugin.manifest.name && target.producer.scope === scope);
929
+ const results = [];
930
+ let executionContext;
931
+ try {
932
+ executionContext = await createScopeContext({ scope, cwd: options.cwd, env });
933
+ for (const { target, blocker } of selected) {
934
+ if (blocker) {
935
+ const result = { producer: target.producer, state: 'failed', commandIds: [], removedCommandIds: [],
936
+ errors: [blocker], recoveryActions: [target.declared
937
+ ? `aw command package sync ${pluginId} ${target.packageKey} --scope ${scope} --yes`
938
+ : `aw command package remove ${target.packageId} --yes`] };
939
+ cache.set(target.packageId, Promise.resolve(result));
940
+ results.push(result);
941
+ continue;
942
+ }
943
+ const applied = await reinstallVisibleTargets({ plugin, scope, cwd: executionContext.executionCwd, env: executionContext.env,
944
+ changedConfigKeys: [...target.refreshKeys],
945
+ rebuildRuntime: { ...options.rebuildRuntime, awHomeMutationToken: token, referencePlugins, managedCommandProducerCache: cache },
946
+ discovered: { targets: [], failures: [], commandPackages: [target], commandDiscoveryErrors: [] } });
947
+ results.push(...('commandResults' in applied ? applied.commandResults ?? [] : []));
948
+ }
949
+ return results;
950
+ }
951
+ finally {
952
+ await executionContext?.dispose();
953
+ }
954
+ } };
955
+ return { packages, evidence, errors, hook };
956
+ }
957
+ /** Prepare the same existing-output plan used by apply, without saving config or output. */
958
+ export async function preparePluginConfigApply(options) {
959
+ const { prepareRecovery, describeRecoveryPlan } = await import('../recovery/coordinator.js');
960
+ const context = options.context ?? options.scope;
961
+ const referencePlugins = await resolvePluginConfigReferencePlugins({ sourceId: options.plugin.sourceId,
962
+ sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity,
963
+ env: options.env, plugins: options.rebuildRuntime?.referencePlugins });
964
+ const plugins = await resolvePluginConfigReferenceConsumers({ plugin: options.plugin, env: options.env,
965
+ plugins: referencePlugins });
966
+ const selectors = plugins.map((plugin) => plugin.pluginId ?? plugin.manifest.name);
967
+ const version = options.cliVersion
968
+ ?? (await fs.readJson(path.join(resolveCliPackageRoot(), 'package.json'))).version;
969
+ let exactTargets = options.applyTargets?.map((target) => ({
970
+ pluginId: target.pluginId ?? plugins.find((plugin) => plugin.manifest.name === target.pluginName)?.pluginId ?? target.pluginName,
971
+ agent: target.platform,
972
+ scope: target.scope,
973
+ }));
974
+ const retainedLegacyTargets = [];
975
+ if (exactTargets?.some((target) => target.agent === 'codex' || target.agent === 'claude')) {
976
+ const { readRecoveryStatus } = await import('../recovery/status.js');
977
+ const observed = await readRecoveryStatus({ context, cwd: options.cwd, env: options.env, cliVersion: version,
978
+ nativeCliRunner: options.nativeCliRunner ?? options.rebuildRuntime?.nativeCliRunner,
979
+ codexObservationSession: options.rebuildRuntime?.codexObservationSession });
980
+ const key = (target) => `${target.pluginId}\0${target.agent}\0${target.scope}`;
981
+ const live = new Set(observed.existingTargets?.map(key));
982
+ const owned = new Set(observed.ownedTargets?.map(key));
983
+ exactTargets = exactTargets.filter((target, index) => {
984
+ if ((target.agent === 'codex' || target.agent === 'claude') && owned.has(key(target)) && !live.has(key(target))) {
985
+ retainedLegacyTargets.push(options.applyTargets[index]);
986
+ return false;
987
+ }
988
+ return true;
989
+ });
990
+ }
991
+ const plan = await prepareRecovery('sync', selectors, {
992
+ scope: context,
993
+ context,
920
994
  cwd: options.cwd,
921
995
  env: options.env,
922
- updates,
923
- removeKeys: requestedRemoveKeys,
924
- referencePlugins: options.rebuildRuntime?.referencePlugins,
996
+ cliVersion: version,
997
+ existingOnly: true,
998
+ ...(exactTargets ? { exactTargets } : {}),
999
+ ...(options.targets ? { targets: options.targets } : options.platform ? { targets: [options.platform] } : {}),
1000
+ }, {
1001
+ ...options.rebuildRuntime,
1002
+ referencePlugins,
1003
+ nativeCliRunner: options.nativeCliRunner,
1004
+ awHomeMutationToken: options.awHomeMutationToken,
1005
+ preApplyConfirmed: options.preApplyConfirmed,
925
1006
  });
926
- if (impact.referenceBlockers?.length) {
927
- throw Object.assign(new Error(`configReferenceInUse: ${impact.referenceBlockers.join(' ')}`), { code: 'configReferenceInUse' });
1007
+ try {
1008
+ plan.warnings.push(...retainedLegacyTargets.map((target) => `${target.pluginId ?? target.pluginName}/${target.platform}/${target.scope}:保留已停用的历史输出;配置 apply 不创建激活。`));
1009
+ const commands = await prepareConfigApplyPackages(options, plugins, plan, referencePlugins);
1010
+ plan.runtime.configApplyPackages = commands.hook;
1011
+ const profiles = configApplyRequestedTargets(plan).map((target) => ({
1012
+ pluginName: target.pluginId.split('/').at(-1), scope: target.scope, platform: target.agent,
1013
+ }));
1014
+ // A package has platform=null and always consumes the public common profile.
1015
+ profiles.push(...commands.packages.map((target) => ({ pluginName: target.producer.pluginName,
1016
+ scope: target.producer.scope, platform: undefined })));
1017
+ const references = await collectPluginConfigReferenceEvidence({ plugin: options.plugin, plugins: referencePlugins,
1018
+ profiles, cwd: options.cwd, env: options.env });
1019
+ const authorization = await resolveConfigPreApplyAuthorization({
1020
+ plugin: options.plugin, scope: context, cwd: options.cwd, env: options.env,
1021
+ applyTargets: configApplyRequestedTargets(plan).flatMap((target) => target.agent === 'codex' || target.agent === 'claude'
1022
+ ? [{ pluginName: target.pluginId.split('/').at(-1), pluginId: target.pluginId, platform: target.agent, scope: target.scope }] : []),
1023
+ nativeCliRunner: options.nativeCliRunner,
1024
+ rebuildRuntime: { ...plan.runtime, referencePlugins },
1025
+ });
1026
+ const portableAuthorization = (current) => [
1027
+ ...current.preApplyAuthorization ?? [],
1028
+ ...(current.joint?.children ?? []).flatMap(portableAuthorization),
1029
+ ];
1030
+ const preApplyAuthorization = [...new Map([...authorization.targets, ...portableAuthorization(plan)]
1031
+ .map((target) => [[target.pluginId, target.platform, target.scope].join('\0'), target])).values()];
1032
+ return { plan, retainedLegacyTargets, commandPackages: commands.packages, commandDiscoveryErrors: commands.errors,
1033
+ preApplyAuthorization,
1034
+ evidence: { recovery: describeRecoveryPlan(plan), references, commands: commands.evidence,
1035
+ preApplyAuthorization, retainedLegacyTargets } };
928
1036
  }
929
- const warnings = [...impact.referenceWarnings ?? []];
930
- const { updatedKeys, removedKeys, staleRemovedKeys } = impact;
931
- let preApplyUnavailableReason;
932
- let preApplyUnavailableTargetResults = [];
933
- let applyDiscovery = discovered
934
- ? { ...discovered, targets: impact.affectedTargets, failures: discovered.failures,
935
- commandPackages: impact.affectedCommandPackages }
936
- : undefined;
937
- const shouldApply = options.apply !== false && impact.storeChanged && !impact.noEffectiveChange;
938
- if (shouldApply && (pluginMayRunPreApply(schemaPlugin)
939
- || applyDiscovery?.targets.some((target) => target.pluginName !== schemaPlugin.manifest.name))) {
940
- if (applyDiscovery) {
941
- const preApplyAuthorization = await resolveConfigPreApplyAuthorization({
942
- plugin: schemaPlugin,
943
- scope: options.scope,
944
- cwd: options.cwd,
945
- env: options.env,
946
- applyTargets: applyDiscovery.targets,
947
- nativeCliRunner: options.nativeCliRunner,
948
- rebuildRuntime: options.rebuildRuntime,
949
- reinstallCommandImpl: options.reinstallCommandImpl,
950
- });
951
- if (preApplyAuthorization.targets.length > 0 && !preApplyAuthorizationIsConfirmed({
952
- required: preApplyAuthorization.targets,
953
- confirmed: options.preApplyConfirmed,
954
- authorizedTargets: options.preApplyAuthorizedTargets,
955
- })) {
956
- throwPreApplyAuthorizationRequired(preApplyAuthorization.targets);
1037
+ catch (error) {
1038
+ await disposePluginConfigApply(plan);
1039
+ throw error;
1040
+ }
1041
+ }
1042
+ export async function disposePluginConfigApply(plan) {
1043
+ for (const child of plan.joint?.children ?? [])
1044
+ await disposePluginConfigApply(child);
1045
+ await plan.context.dispose();
1046
+ }
1047
+ export async function applyPluginConfig(options) {
1048
+ return withAwHomeMutationLock({ env: options.env, token: options.awHomeMutationToken, action: async (token) => {
1049
+ await options.reviewPreflight?.();
1050
+ let prepared;
1051
+ try {
1052
+ const { executeRecovery } = await import('../recovery/coordinator.js');
1053
+ prepared = await preparePluginConfigApply({ ...options, awHomeMutationToken: token });
1054
+ if (options.expectedEvidence !== undefined
1055
+ && JSON.stringify(options.expectedEvidence) !== JSON.stringify(prepared.evidence)) {
1056
+ throw Object.assign(new Error('operationPlanStale: Configuration apply inputs changed. Prepare a new Review.'), { code: 'operationPlanStale' });
1057
+ }
1058
+ const authorization = prepared.preApplyAuthorization;
1059
+ if (authorization.length && !preApplyAuthorizationIsConfirmed({ required: authorization,
1060
+ confirmed: options.preApplyConfirmed, authorizedTargets: options.preApplyAuthorizedTargets })) {
1061
+ throwPreApplyAuthorizationRequired(authorization);
1062
+ }
1063
+ if (options.sharedNativeApplyConfirmed !== true && await configApplyHasValidSharedNativeTarget({
1064
+ plugin: options.plugin, cwd: options.cwd, env: options.env, plugins: options.rebuildRuntime?.referencePlugins,
1065
+ targets: configApplyRequestedTargets(prepared.plan).flatMap((target) => target.agent === 'codex' || target.agent === 'claude'
1066
+ ? [{ pluginName: target.pluginId.split('/').at(-1), pluginId: target.pluginId, platform: target.agent, scope: target.scope }] : []),
1067
+ }))
1068
+ throwSharedNativeApplyConfirmationRequired();
1069
+ const discoveryErrors = prepared.commandDiscoveryErrors.map((error) => ({
1070
+ stage: 'targetDiscovery', code: error.code, message: error.message,
1071
+ }));
1072
+ const receipt = await executeRecovery(prepared.plan, {
1073
+ yes: options.sharedNativeApplyConfirmed === true || options.preApplyConfirmed === true,
1074
+ runtime: { ...options.rebuildRuntime, awHomeMutationToken: token, preApplyConfirmed: options.preApplyConfirmed },
1075
+ });
1076
+ const retainedLegacyTargets = prepared.retainedLegacyTargets.filter((target) => !receipt.targetResults.some((result) => (target.pluginId ? result.pluginId === target.pluginId : result.pluginId.split('/').at(-1) === target.pluginName)
1077
+ && result.agent === target.platform && result.scope === target.scope));
1078
+ prepared = undefined;
1079
+ const ok = receipt.ok && discoveryErrors.length === 0;
1080
+ const errors = [...discoveryErrors, ...receipt.blockers, ...receipt.errors].map((error) => ({
1081
+ stage: 'stage' in error ? error.stage : 'apply', code: error.code, message: error.message,
1082
+ }));
1083
+ const apply = {
1084
+ state: ok ? 'succeeded' : 'partial',
1085
+ targetResults: [...receipt.targetResults.filter((target) => target.satisfaction !== 'unsupported' && target.satisfaction !== 'excluded').map((target) => ({
1086
+ target: { pluginName: target.pluginId.split('/').at(-1), pluginId: target.pluginId,
1087
+ platform: target.agent, scope: target.scope },
1088
+ action: 'refresh',
1089
+ state: target.execution === 'applied' || target.execution === 'no-op' ? 'completed'
1090
+ : target.execution === 'not-run' ? 'skipped' : 'failed',
1091
+ ...(target.blockers.length ? { errorCode: target.blockers[0].code,
1092
+ message: target.blockers.map((item) => item.message).join(' ') } : {}),
1093
+ })), ...retainedLegacyTargets.map((target) => ({
1094
+ target, action: 'refresh', state: 'skipped',
1095
+ message: 'The inactive legacy output was preserved. Configuration apply does not create an activation.',
1096
+ }))],
1097
+ commandResults: receipt.commandResults,
1098
+ ...(errors.length ? { errors } : {}),
1099
+ };
1100
+ return { configWrite: 'notStarted', overallState: ok ? 'succeeded' : 'partial', apply,
1101
+ otherProjectsMayRequireSync: true };
957
1102
  }
958
- preApplyUnavailableTargetResults = preApplyAuthorization.unavailableTargetResults;
959
- applyDiscovery = {
960
- ...applyDiscovery,
961
- targets: preApplyAuthorization.availableApplyTargets,
962
- failures: [...applyDiscovery.failures, ...preApplyUnavailableTargetResults],
963
- };
964
- if (applyDiscovery.targets.length === 0 && preApplyUnavailableTargetResults.length > 0
965
- && (applyDiscovery.commandPackages?.length ?? 0) === 0) {
966
- preApplyUnavailableReason = 'sourceUnavailable';
1103
+ catch (error) {
1104
+ if (options.rebuildRuntime?.signal?.aborted || error instanceof Error && error.name === 'AbortError'
1105
+ || readSafeOperationError(error))
1106
+ throw error;
1107
+ const apply = unavailableApply('targetDiscoveryFailed', 'targetDiscovery');
1108
+ const code = error?.code;
1109
+ if (typeof code === 'string')
1110
+ apply.error = { stage: 'apply', code,
1111
+ message: code === 'operationPlanStale' ? 'Configuration apply inputs changed. Prepare a new Review.'
1112
+ : 'The existing outputs cannot be refreshed with the current authorization.' };
1113
+ return { configWrite: 'notStarted', overallState: 'partial', apply, otherProjectsMayRequireSync: true };
967
1114
  }
968
- }
969
- }
970
- if (options.apply !== false
971
- && options.scope === 'user'
972
- && options.sharedNativeApplyConfirmed !== true
973
- && impact.sharedNativeRenderTargets.some((shared) => impact.affectedTargets
974
- .some((target) => (!shared.pluginName || shared.pluginName === target.pluginName)
975
- && target.platform === (shared.platform ?? 'codex')))) {
976
- throwSharedNativeApplyConfirmationRequired();
977
- }
1115
+ finally {
1116
+ if (prepared)
1117
+ await disposePluginConfigApply(prepared.plan);
1118
+ }
1119
+ } });
1120
+ }
1121
+ function throwSharedNativeApplyConfirmationRequired() {
1122
+ const error = new Error('sharedNativeConfigApplyConfirmationRequired: Applying this user config changes a shared native artifact. Re-run with --yes.');
1123
+ error.code = 'sharedNativeConfigApplyConfirmationRequired';
1124
+ throw error;
1125
+ }
1126
+ async function applyPluginConfigWriteUnlocked(options) {
1127
+ const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.plugin?.manifest.name ?? '');
1128
+ const identity = options.plugin?.sourceId ? { sourceId: options.plugin.sourceId,
1129
+ sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity } : {};
1130
+ const requestedUpdates = options.request.updates ?? {};
1131
+ const removeKeys = uniqueInOrder(options.request.removeKeys ?? []);
1132
+ const duplicateKeys = removeKeys.filter((key) => Object.hasOwn(requestedUpdates, key));
1133
+ if (duplicateKeys.length)
1134
+ throw new Error(`Config write cannot update and remove the same key: ${duplicateKeys.join(', ')}.`);
1135
+ const schemaAccess = await resolvePluginConfigSchemaAccess({ pluginName, currentPlugin: options.plugin,
1136
+ currentPluginError: options.pluginError, scope: options.scope, platform: options.platform, cwd: options.cwd, env: options.env });
1137
+ const plugin = schemaAccess.plugin;
1138
+ const definition = plugin?.manifest.config ? compilePluginConfigDefinition(plugin.manifest.config, options.platform) : undefined;
1139
+ if (!plugin || !definition)
1140
+ throw new PluginConfigSchemaResolutionError('configSchemaUnavailable', `Plugin "${pluginName}" does not declare an AW config schema.`);
1141
+ const updates = await coercePluginConfigUpdates({ schema: definition.fields, updates: requestedUpdates, cwd: options.cwd });
1142
+ const impact = await analyzePluginConfigMutationImpact({ plugin, scope: options.scope, platform: options.platform,
1143
+ cwd: options.cwd, env: options.env, visibleTargets: [], commandDiscovery: { packages: [], errors: [] },
1144
+ referencePlugins: options.rebuildRuntime?.referencePlugins, mutation: { kind: 'patch', updates, removeKeys } });
1145
+ await assertPluginConfigMutationCandidate({ ...identity, pluginName, manifest: plugin.manifest,
1146
+ scope: options.scope, platform: options.platform, cwd: options.cwd, env: options.env, updates, removeKeys,
1147
+ referencePlugins: options.rebuildRuntime?.referencePlugins });
1148
+ if (impact.referenceBlockers?.length)
1149
+ throw Object.assign(new Error(`configReferenceInUse: ${impact.referenceBlockers.join(' ')}`), { code: 'configReferenceInUse' });
978
1150
  if (impact.storeChanged) {
979
1151
  try {
980
- const write = await patchPluginConfigStore({
981
- ...configIdentity,
982
- pluginName,
983
- scope: options.scope,
984
- cwd: options.cwd,
985
- env: options.env,
986
- updates: Object.fromEntries(updatedKeys.map((key) => [key, updates[key]])),
987
- removeKeys: requestedRemoveKeys,
988
- schema,
989
- });
990
- warnings.push(...write.warnings);
1152
+ await patchPluginConfigStore({ ...identity, pluginName, scope: 'user', platform: options.platform,
1153
+ cwd: options.cwd, env: options.env, updates: Object.fromEntries(impact.updatedKeys.map((key) => [key, updates[key]])), removeKeys });
991
1154
  }
992
1155
  catch (error) {
993
1156
  throw markConfigWriteFailed(error);
994
1157
  }
995
1158
  }
1159
+ const warnings = [...impact.referenceWarnings ?? []];
996
1160
  let effective;
997
1161
  try {
998
- effective = summarizeEffectivePluginConfig(await resolveEffectivePluginConfig({
999
- ...configIdentity,
1000
- pluginName,
1001
- manifest: schemaPlugin.manifest,
1002
- referencePlugins: options.rebuildRuntime?.referencePlugins,
1003
- targetScope: options.scope,
1004
- cwd: options.cwd,
1005
- env: options.env,
1006
- }));
1162
+ effective = summarizeEffectivePluginConfig(await resolveEffectivePluginConfig({ ...identity, pluginName,
1163
+ manifest: plugin.manifest, platform: options.platform, targetScope: options.scope, cwd: options.cwd, env: options.env,
1164
+ referencePlugins: options.rebuildRuntime?.referencePlugins }));
1007
1165
  }
1008
1166
  catch {
1009
- const apply = unavailableApply('effectiveConfigUnavailable', 'effectiveConfig', [], impact.affectedCommandPackages);
1010
- const otherProjectsMayRequireSync = options.scope === 'user' && impact.storeChanged;
1011
- if (otherProjectsMayRequireSync)
1012
- warnings.push(...otherProjectsWarning(options.scope));
1013
- return {
1014
- plugin: pluginName,
1015
- scope: options.scope,
1016
- ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
1017
- updatedKeys,
1018
- removedKeys,
1019
- staleRemovedKeys,
1020
- configWrite: 'succeeded',
1021
- overallState: 'partial',
1022
- apply: publicConfigApplyResult(apply),
1023
- otherProjectsMayRequireSync,
1024
- warnings,
1025
- };
1167
+ warnings.push('The saved configuration cannot be previewed. Check its schema and reference sources before applying it.');
1026
1168
  }
1027
- let apply;
1169
+ if (impact.storeChanged)
1170
+ warnings.push(...otherProjectsWarning('user'));
1171
+ return { plugin: pluginName, scope: 'user', context: options.scope, platform: options.platform,
1172
+ ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}), updatedKeys: impact.updatedKeys,
1173
+ removedKeys: impact.removedKeys, staleRemovedKeys: impact.staleRemovedKeys, effective,
1174
+ configWrite: 'succeeded', overallState: 'succeeded',
1175
+ apply: skippedApply(impact.storeChanged ? 'notRequested' : 'noConfigChange'),
1176
+ otherProjectsMayRequireSync: impact.storeChanged, warnings };
1177
+ }
1178
+ async function prepareConfigMutationApplySelection(options) {
1179
+ if (options.apply !== true || !options.plugin)
1180
+ return undefined;
1181
+ let prepared;
1028
1182
  try {
1029
- apply = !impact.storeChanged
1030
- ? skippedApply('noConfigChange')
1031
- : options.apply === false
1032
- ? skippedApply('notRequested')
1033
- : targetDiscoveryFailed
1034
- ? unavailableApply('targetDiscoveryFailed', 'targetDiscovery')
1035
- : impact.noEffectiveChange
1036
- && ((discovered?.targets.length ?? 0) + impact.commandPackages.length > 0
1037
- || staleRemovedKeys.length === impact.changedKeys.length)
1038
- ? skippedApply('noEffectiveConfigChange')
1039
- : preApplyUnavailableReason
1040
- ? unavailableApply(preApplyUnavailableReason, preApplyUnavailableReason === 'sourceUnavailable' ? 'source' : 'targetDiscovery', preApplyUnavailableTargetResults)
1041
- : await applyConfigToVisibleTargets({
1042
- plugin: schemaPlugin,
1043
- scope: options.scope,
1044
- cwd: options.cwd,
1045
- env: options.env ?? process.env,
1046
- applyTargets: applyDiscovery?.targets ?? impact.affectedTargets,
1047
- nativeCliRunner: options.nativeCliRunner,
1048
- reinstallCommandImpl: options.reinstallCommandImpl,
1049
- rebuildRuntime: options.rebuildRuntime,
1050
- noVisibleTargetHasInvalidConfig: (discovered?.targets.length ?? 0) > 0
1051
- && effective.issues.length > 0,
1052
- preApplyConfirmed: options.preApplyConfirmed,
1053
- discovered: applyDiscovery,
1054
- changedConfigKeys: impact.changedKeys,
1055
- });
1183
+ prepared = await preparePluginConfigApply({ ...options, plugin: options.plugin });
1184
+ const requestedTargets = configApplyRequestedTargets(prepared.plan);
1185
+ if (requestedTargets.some((target) => target.agent !== 'codex' && target.agent !== 'claude'))
1186
+ return undefined;
1187
+ const visibleTargets = requestedTargets.map((target) => ({
1188
+ pluginName: target.pluginId.split('/').at(-1), pluginId: target.pluginId,
1189
+ platform: target.agent, scope: target.scope,
1190
+ }));
1191
+ if (!visibleTargets.length && !prepared.commandPackages.length && !prepared.commandDiscoveryErrors.length) {
1192
+ if (prepared.plan.blockExecution || prepared.plan.receipt.blockers.length || prepared.plan.receipt.errors.length)
1193
+ return undefined;
1194
+ return { skipReason: 'noVisibleTarget' };
1195
+ }
1196
+ const impact = await analyzePluginConfigMutationImpact({ plugin: options.plugin, scope: options.context ?? options.scope,
1197
+ platform: options.platform, cwd: options.cwd, env: options.env, visibleTargets,
1198
+ commandDiscovery: { packages: prepared.commandPackages, errors: prepared.commandDiscoveryErrors },
1199
+ referencePlugins: options.rebuildRuntime?.referencePlugins,
1200
+ mutation: 'request' in options ? { kind: 'patch', ...options.request } : { kind: 'reset' } });
1201
+ return { applyTargets: impact.affectedTargets,
1202
+ applyCommandPackages: impact.affectedCommandPackages.map((target) => target.packageId),
1203
+ ...(impact.noEffectiveChange ? { skipReason: 'noEffectiveConfigChange' } : {}) };
1056
1204
  }
1057
- catch (error) {
1058
- const failure = error instanceof Error ? error : new Error(String(error));
1059
- throw Object.assign(failure, { configWrite: 'succeeded' });
1205
+ catch {
1206
+ // A failed output discovery must not prevent a valid configuration save.
1207
+ return undefined;
1208
+ }
1209
+ finally {
1210
+ if (prepared)
1211
+ await disposePluginConfigApply(prepared.plan);
1060
1212
  }
1061
- const otherProjectsMayRequireSync = options.scope === 'user' && impact.storeChanged;
1062
- if (otherProjectsMayRequireSync)
1063
- warnings.push(...otherProjectsWarning(options.scope));
1064
- return {
1065
- plugin: pluginName,
1066
- scope: options.scope,
1067
- ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
1068
- updatedKeys,
1069
- removedKeys,
1070
- staleRemovedKeys,
1071
- effective,
1072
- configWrite: 'succeeded',
1073
- overallState: overallStateForApply(apply),
1074
- apply: publicConfigApplyResult(apply),
1075
- otherProjectsMayRequireSync,
1076
- warnings,
1077
- };
1078
1213
  }
1079
1214
  export async function applyPluginConfigWrite(options) {
1215
+ if (options.context)
1216
+ await resolveWorkContext({ cwd: options.cwd, context: options.context });
1080
1217
  const requestedPluginName = options.pluginName ?? options.plugin?.manifest.name;
1081
1218
  if (!requestedPluginName)
1082
1219
  throw new Error('Plugin name is required for config write.');
1083
1220
  assertSafePluginConfigSegment(requestedPluginName);
1084
- if (options.scope === 'project') {
1085
- const targetPath = resolvePluginConfigStorePath({
1086
- sourceId: options.plugin?.sourceId,
1087
- pluginName: requestedPluginName,
1088
- scope: 'project',
1089
- cwd: options.cwd,
1090
- env: options.env,
1091
- });
1092
- await assertProjectSharedConfigPathVisible({
1093
- cwd: options.cwd,
1094
- targetPath,
1095
- env: options.env,
1096
- });
1097
- }
1098
- if (options.apply !== false && !options.awHomeMutationToken) {
1099
- await prepareNativeCliExecution({ env: options.env, runner: options.nativeCliRunner });
1100
- }
1101
1221
  return withAwHomeMutationLock({
1102
1222
  env: options.env,
1103
1223
  token: options.awHomeMutationToken,
1104
1224
  action: async (awHomeMutationToken) => {
1105
1225
  await options.reviewPreflight?.();
1226
+ let configCommitted = false;
1106
1227
  try {
1107
- return await applyPluginConfigWriteUnlocked({
1108
- ...options,
1109
- awHomeMutationToken,
1110
- rebuildRuntime: { ...options.rebuildRuntime, awHomeMutationToken },
1111
- });
1228
+ const selection = await prepareConfigMutationApplySelection({ ...options, awHomeMutationToken });
1229
+ const saved = await applyPluginConfigWriteUnlocked({ ...options, scope: options.context ?? options.scope,
1230
+ apply: false, awHomeMutationToken, rebuildRuntime: { ...options.rebuildRuntime, awHomeMutationToken } });
1231
+ configCommitted = true;
1232
+ if (options.apply !== true || !options.plugin)
1233
+ return saved;
1234
+ if (saved.apply.state === 'skipped' && saved.apply.reason === 'noConfigChange')
1235
+ return saved;
1236
+ if (selection?.skipReason)
1237
+ return { ...saved, apply: skippedApply(selection.skipReason) };
1238
+ const applied = await applyPluginConfig({ ...options, ...selection, plugin: options.plugin, awHomeMutationToken });
1239
+ return { ...saved, overallState: applied.overallState, apply: applied.apply };
1112
1240
  }
1113
1241
  catch (error) {
1114
1242
  let sensitiveValues = options.plugin
1115
- ? collectSensitivePluginConfigValues(options.plugin.manifest, (options.request.updates ?? {}))
1243
+ ? collectSensitivePluginConfigValues(options.plugin.manifest, (options.request.updates ?? {}), options.platform)
1116
1244
  : [];
1117
1245
  if (options.plugin) {
1118
1246
  try {
@@ -1126,12 +1254,13 @@ export async function applyPluginConfigWrite(options) {
1126
1254
  manifest: options.plugin.manifest,
1127
1255
  referencePlugins: options.rebuildRuntime?.referencePlugins,
1128
1256
  targetScope: options.scope,
1257
+ platform: options.platform,
1129
1258
  cwd: options.cwd,
1130
1259
  env: options.env,
1131
1260
  });
1132
1261
  sensitiveValues = [
1133
1262
  ...sensitiveValues,
1134
- ...collectSensitivePluginConfigValues(options.plugin.manifest, effective.values),
1263
+ ...collectSensitivePluginConfigValues(options.plugin.manifest, effective.values, options.platform),
1135
1264
  ];
1136
1265
  }
1137
1266
  catch {
@@ -1160,8 +1289,8 @@ export async function applyPluginConfigWrite(options) {
1160
1289
  : message.startsWith('schemaVersionUnsupported:') ? 'schemaVersionUnsupported'
1161
1290
  : 'configApplyFailed';
1162
1291
  const configWrite = error.configWrite;
1163
- if (configWrite === 'succeeded' || configWriteStateForError(error) === 'failed') {
1164
- safeError.configWrite = configWrite === 'succeeded' ? 'succeeded' : 'failed';
1292
+ if (configCommitted || configWrite === 'succeeded' || configWriteStateForError(error) === 'failed') {
1293
+ safeError.configWrite = configCommitted || configWrite === 'succeeded' ? 'succeeded' : 'failed';
1165
1294
  }
1166
1295
  if (error && typeof error === 'object' && 'operationDiagnostic' in error) {
1167
1296
  safeError.operationDiagnostic = error.operationDiagnostic;
@@ -1173,277 +1302,90 @@ export async function applyPluginConfigWrite(options) {
1173
1302
  }
1174
1303
  async function resetPluginConfigWithHelperUnlocked(options) {
1175
1304
  const pluginName = assertSafePluginConfigSegment(options.pluginName);
1176
- let configIdentity = options.plugin?.sourceId ? {
1177
- sourceId: options.plugin.sourceId,
1178
- sourceIdentity: options.plugin.sourceIdentity,
1179
- logicalIdentity: options.plugin.catalogLogicalIdentity,
1180
- } : options.sourceId ? {
1181
- sourceId: options.sourceId,
1182
- sourceIdentity: options.sourceIdentity,
1183
- logicalIdentity: options.logicalIdentity,
1184
- } : {};
1185
- if (options.sourceId && !options.sourceIdentity) {
1305
+ let identity = options.plugin?.sourceId ? { sourceId: options.plugin.sourceId,
1306
+ sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity }
1307
+ : { sourceId: options.sourceId, sourceIdentity: options.sourceIdentity, logicalIdentity: options.logicalIdentity };
1308
+ if (identity.sourceId && !identity.sourceIdentity) {
1186
1309
  try {
1187
- const identity = await readNamespacedPluginConfigStoreIdentity({
1188
- sourceId: options.sourceId,
1189
- pluginName,
1190
- scope: options.scope,
1191
- cwd: options.cwd,
1192
- env: options.env,
1193
- });
1194
- if (identity?.binding.kind === 'source')
1195
- configIdentity = {
1196
- sourceId: options.sourceId,
1197
- sourceIdentity: identity.binding.sourceIdentity,
1198
- };
1199
- if (identity?.binding.kind === 'catalog')
1200
- configIdentity = {
1201
- sourceId: options.sourceId,
1202
- logicalIdentity: identity.binding.logicalIdentity,
1203
- };
1204
- }
1205
- catch {
1206
- // Reset does not require a readable or valid envelope.
1310
+ const stored = await readNamespacedPluginConfigStoreIdentity({ sourceId: identity.sourceId, pluginName,
1311
+ scope: 'user', platform: options.platform, cwd: options.cwd, env: options.env });
1312
+ if (stored)
1313
+ identity = { ...identity, sourceIdentity: stored.binding.sourceIdentity };
1207
1314
  }
1315
+ catch { /* Reset remains available for unreadable identity metadata. */ }
1208
1316
  }
1209
1317
  let removedKeys = [];
1210
1318
  try {
1211
- const store = await readPluginConfigStore({
1212
- ...configIdentity,
1213
- pluginName,
1214
- scope: options.scope,
1215
- cwd: options.cwd,
1216
- env: options.env,
1217
- });
1319
+ const store = await readPluginConfigStore({ ...identity, pluginName, scope: 'user', platform: options.platform,
1320
+ cwd: options.cwd, env: options.env });
1218
1321
  removedKeys = collectRemovedConfigKeys(store.values);
1219
1322
  }
1220
- catch {
1221
- // Reset is the final recovery boundary and does not require a readable store.
1222
- }
1323
+ catch { /* Broken stores still have the path-based reset boundary. */ }
1223
1324
  const warnings = [];
1224
- let impact;
1225
- let targetDiscoveryFailed = false;
1226
- let visibleTargets = [];
1227
- let discovered;
1228
1325
  if (options.plugin) {
1229
- if (options.apply !== false) {
1230
- try {
1231
- discovered = await resolveConfigApplyTargets({
1232
- plugin: options.plugin,
1233
- scope: options.scope,
1234
- cwd: options.cwd,
1235
- env: options.env ?? process.env,
1236
- applyTargets: options.applyTargets,
1237
- nativeCliRunner: options.nativeCliRunner,
1238
- rebuildRuntime: options.rebuildRuntime,
1239
- });
1240
- visibleTargets = discovered.targets;
1241
- }
1242
- catch {
1243
- targetDiscoveryFailed = true;
1244
- }
1245
- }
1246
1326
  try {
1247
- impact = await analyzePluginConfigMutationImpact({
1248
- plugin: options.plugin,
1249
- scope: options.scope,
1250
- cwd: options.cwd,
1251
- env: options.env,
1252
- visibleTargets,
1253
- referencePlugins: options.rebuildRuntime?.referencePlugins,
1254
- ...(discovered ? { commandDiscovery: {
1255
- packages: discovered.commandPackages ?? [], errors: discovered.commandDiscoveryErrors ?? [],
1256
- } } : {}),
1257
- mutation: { kind: 'reset' },
1258
- });
1259
- removedKeys = impact.removedKeys;
1260
- warnings.push(...impact.referenceBlockers ?? []);
1261
- warnings.push(...impact.referenceWarnings ?? []);
1327
+ const impact = await analyzePluginConfigMutationImpact({ plugin: options.plugin, scope: options.scope,
1328
+ platform: options.platform, cwd: options.cwd, env: options.env, visibleTargets: [],
1329
+ commandDiscovery: { packages: [], errors: [] }, referencePlugins: options.rebuildRuntime?.referencePlugins,
1330
+ mutation: { kind: 'reset' } });
1331
+ warnings.push(...impact.referenceWarnings ?? [], ...impact.referenceBlockers ?? []);
1262
1332
  }
1263
1333
  catch {
1264
- // Reset remains available when its before state cannot be read.
1334
+ warnings.push('Configuration references could not be checked. Reset will still clear the selected layer.');
1265
1335
  }
1266
1336
  }
1267
- let preApplyUnavailableReason;
1268
- let preApplyUnavailableTargetResults = [];
1269
- let applyDiscovery = options.plugin && !targetDiscoveryFailed
1270
- ? { targets: impact?.affectedTargets ?? visibleTargets, failures: [],
1271
- commandPackages: impact?.affectedCommandPackages ?? discovered?.commandPackages,
1272
- commandDiscoveryErrors: impact?.commandDiscoveryErrors ?? discovered?.commandDiscoveryErrors }
1273
- : undefined;
1274
- if (options.plugin
1275
- && options.apply !== false
1276
- && impact?.storeChanged
1277
- && !impact.noEffectiveChange
1278
- && applyDiscovery
1279
- && (pluginMayRunPreApply(options.plugin)
1280
- || applyDiscovery.targets.some((target) => target.pluginName !== options.plugin.manifest.name))) {
1281
- const preApplyAuthorization = await resolveConfigPreApplyAuthorization({
1282
- plugin: options.plugin,
1283
- scope: options.scope,
1284
- cwd: options.cwd,
1285
- env: options.env,
1286
- applyTargets: applyDiscovery.targets,
1287
- nativeCliRunner: options.nativeCliRunner,
1288
- rebuildRuntime: options.rebuildRuntime,
1289
- reinstallCommandImpl: options.reinstallCommandImpl,
1290
- });
1291
- if (preApplyAuthorization.targets.length > 0 && !preApplyAuthorizationIsConfirmed({
1292
- required: preApplyAuthorization.targets,
1293
- confirmed: options.preApplyConfirmed,
1294
- authorizedTargets: options.preApplyAuthorizedTargets,
1295
- })) {
1296
- throwPreApplyAuthorizationRequired(preApplyAuthorization.targets);
1297
- }
1298
- preApplyUnavailableTargetResults = preApplyAuthorization.unavailableTargetResults;
1299
- applyDiscovery = {
1300
- ...applyDiscovery,
1301
- targets: preApplyAuthorization.availableApplyTargets,
1302
- failures: preApplyUnavailableTargetResults,
1303
- };
1304
- if (applyDiscovery.targets.length === 0 && preApplyUnavailableTargetResults.length > 0
1305
- && (applyDiscovery.commandPackages?.length ?? 0) === 0) {
1306
- preApplyUnavailableReason = 'sourceUnavailable';
1307
- }
1308
- }
1309
- if (options.plugin
1310
- && options.apply !== false
1311
- && options.scope === 'user'
1312
- && options.sharedNativeApplyConfirmed !== true
1313
- && (impact
1314
- ? impact.sharedNativeRenderTargets.some((shared) => impact.affectedTargets
1315
- .some((target) => (!shared.pluginName || shared.pluginName === target.pluginName)
1316
- && target.platform === (shared.platform ?? 'codex')))
1317
- : pluginSharedNativeConfigTargets(options.plugin).some((shared) => visibleTargets
1318
- .some((target) => (!shared.pluginName || shared.pluginName === target.pluginName)
1319
- && target.platform === (shared.platform ?? 'codex'))))) {
1320
- throwSharedNativeApplyConfirmationRequired();
1321
- }
1322
1337
  let reset;
1323
1338
  try {
1324
- reset = await resetPluginConfigStore({
1325
- ...configIdentity,
1326
- pluginName,
1327
- scope: options.scope,
1328
- cwd: options.cwd,
1329
- env: options.env,
1330
- });
1339
+ reset = await resetPluginConfigStore({ ...identity, pluginName, scope: 'user', platform: options.platform,
1340
+ cwd: options.cwd, env: options.env });
1331
1341
  }
1332
1342
  catch (error) {
1333
1343
  throw markConfigWriteFailed(error);
1334
1344
  }
1335
- let schemaAccess;
1336
- let apply = reset.deleted ? skippedApply('notRequested') : skippedApply('noConfigChange');
1337
1345
  let effective;
1346
+ let schema;
1338
1347
  if (options.plugin) {
1339
1348
  try {
1340
- schemaAccess = await resolvePluginConfigSchemaAccess({
1341
- pluginName,
1342
- currentPlugin: options.plugin,
1343
- scope: options.scope,
1344
- cwd: options.cwd,
1345
- env: options.env,
1346
- });
1349
+ schema = (await resolvePluginConfigSchemaAccess({ pluginName, currentPlugin: options.plugin,
1350
+ scope: options.scope, platform: options.platform, cwd: options.cwd, env: options.env })).schema;
1351
+ effective = summarizeEffectivePluginConfig(await resolveEffectivePluginConfig({ ...identity, pluginName,
1352
+ manifest: options.plugin.manifest, platform: options.platform, targetScope: options.scope, cwd: options.cwd,
1353
+ env: options.env, referencePlugins: options.rebuildRuntime?.referencePlugins }));
1347
1354
  }
1348
1355
  catch {
1349
- if (reset.deleted && options.apply !== false) {
1350
- apply = unavailableApply('sourceUnavailable', 'source');
1351
- }
1352
- }
1353
- const schemaPlugin = schemaAccess?.plugin;
1354
- if (schemaPlugin?.manifest.config?.fields) {
1355
- try {
1356
- effective = summarizeEffectivePluginConfig(await resolveEffectivePluginConfig({
1357
- ...configIdentity,
1358
- pluginName,
1359
- manifest: schemaPlugin.manifest,
1360
- referencePlugins: options.rebuildRuntime?.referencePlugins,
1361
- targetScope: options.scope,
1362
- cwd: options.cwd,
1363
- env: options.env,
1364
- }));
1365
- }
1366
- catch {
1367
- if (reset.deleted && options.apply !== false) {
1368
- apply = unavailableApply('effectiveConfigUnavailable', 'effectiveConfig', [], impact?.affectedCommandPackages);
1369
- }
1370
- }
1371
- if (reset.deleted
1372
- && options.apply !== false
1373
- && apply.state === 'skipped'
1374
- && impact?.noEffectiveChange
1375
- && visibleTargets.length + (impact?.commandPackages.length ?? 0) > 0) {
1376
- apply = skippedApply('noEffectiveConfigChange');
1377
- }
1378
- else if (reset.deleted
1379
- && options.apply !== false
1380
- && apply.state === 'skipped'
1381
- && targetDiscoveryFailed) {
1382
- apply = unavailableApply('targetDiscoveryFailed', 'targetDiscovery');
1383
- }
1384
- else if (reset.deleted && options.apply !== false && apply.state === 'skipped') {
1385
- apply = preApplyUnavailableReason
1386
- ? unavailableApply(preApplyUnavailableReason, 'source', preApplyUnavailableTargetResults)
1387
- : await applyConfigToVisibleTargets({
1388
- plugin: schemaPlugin,
1389
- scope: options.scope,
1390
- cwd: options.cwd,
1391
- env: options.env ?? process.env,
1392
- applyTargets: applyDiscovery?.targets ?? impact?.affectedTargets ?? options.applyTargets,
1393
- nativeCliRunner: options.nativeCliRunner,
1394
- reinstallCommandImpl: options.reinstallCommandImpl,
1395
- rebuildRuntime: options.rebuildRuntime,
1396
- noVisibleTargetHasInvalidConfig: (effective?.issues.length ?? 0) > 0,
1397
- preApplyConfirmed: options.preApplyConfirmed,
1398
- discovered: applyDiscovery,
1399
- changedConfigKeys: impact?.changedKeys ?? removedKeys,
1400
- });
1401
- }
1356
+ warnings.push('The cleared configuration cannot be previewed. Check its schema and references before applying it.');
1402
1357
  }
1403
- else if (reset.deleted && options.apply !== false && apply.state === 'skipped') {
1404
- apply = unavailableApply('schemaUnavailable', 'schema');
1405
- }
1406
- }
1407
- else if (reset.deleted && options.apply !== false) {
1408
- apply = options.pluginError
1409
- ? unavailableApply('sourceUnavailable', 'source')
1410
- : unavailableApply('schemaUnavailable', 'schema');
1411
1358
  }
1412
- const otherProjectsMayRequireSync = options.scope === 'user' && reset.deleted;
1413
- if (otherProjectsMayRequireSync)
1414
- warnings.push(...otherProjectsWarning(options.scope));
1415
- return {
1416
- plugin: pluginName,
1417
- scope: options.scope,
1418
- ...(schemaAccess?.schema ? { schema: schemaAccess.schema } : {}),
1419
- existed: reset.existed,
1420
- deleted: reset.deleted,
1421
- resetDisposition: reset.resetDisposition,
1422
- ...(reset.quarantineBasename ? { quarantineBasename: reset.quarantineBasename } : {}),
1423
- removedKeys,
1424
- ...(effective ? { effective } : {}),
1425
- configWrite: 'succeeded',
1426
- overallState: overallStateForApply(apply),
1427
- apply: publicConfigApplyResult(apply),
1428
- otherProjectsMayRequireSync,
1429
- warnings,
1430
- };
1359
+ if (reset.existed)
1360
+ warnings.push(...otherProjectsWarning('user'));
1361
+ return { plugin: pluginName, scope: 'user', context: options.scope, platform: options.platform, schema,
1362
+ existed: reset.existed, deleted: reset.deleted, resetDisposition: reset.resetDisposition,
1363
+ ...(reset.quarantineBasename ? { quarantineBasename: reset.quarantineBasename } : {}), removedKeys, effective,
1364
+ configWrite: 'succeeded', overallState: 'succeeded', apply: skippedApply(reset.existed ? 'notRequested' : 'noConfigChange'),
1365
+ otherProjectsMayRequireSync: reset.existed, warnings };
1431
1366
  }
1432
1367
  export async function resetPluginConfigWithHelper(options) {
1368
+ if (options.context)
1369
+ await resolveWorkContext({ cwd: options.cwd, context: options.context });
1433
1370
  assertSafePluginConfigSegment(options.pluginName);
1434
- if (options.apply !== false && !options.awHomeMutationToken) {
1435
- await prepareNativeCliExecution({ env: options.env, runner: options.nativeCliRunner });
1436
- }
1437
1371
  return withAwHomeMutationLock({
1438
1372
  env: options.env,
1439
1373
  token: options.awHomeMutationToken,
1440
1374
  action: async (awHomeMutationToken) => {
1441
1375
  await options.reviewPreflight?.();
1442
- return resetPluginConfigWithHelperUnlocked({
1443
- ...options,
1444
- awHomeMutationToken,
1445
- rebuildRuntime: { ...options.rebuildRuntime, awHomeMutationToken },
1446
- });
1376
+ const selection = await prepareConfigMutationApplySelection({ ...options, awHomeMutationToken });
1377
+ const saved = await resetPluginConfigWithHelperUnlocked({ ...options, scope: options.context ?? options.scope,
1378
+ apply: false, awHomeMutationToken, rebuildRuntime: { ...options.rebuildRuntime, awHomeMutationToken } });
1379
+ if (options.apply !== true)
1380
+ return saved;
1381
+ if (!options.plugin)
1382
+ return { ...saved, overallState: 'partial', apply: unavailableApply('sourceUnavailable', 'source') };
1383
+ if (saved.apply.state === 'skipped' && saved.apply.reason === 'noConfigChange')
1384
+ return saved;
1385
+ if (selection?.skipReason)
1386
+ return { ...saved, apply: skippedApply(selection.skipReason) };
1387
+ const applied = await applyPluginConfig({ ...options, ...selection, plugin: options.plugin, awHomeMutationToken });
1388
+ return { ...saved, overallState: applied.overallState, apply: applied.apply };
1447
1389
  },
1448
1390
  });
1449
1391
  }
@@ -1454,6 +1396,8 @@ export async function describeResetAffectedFields(options) {
1454
1396
  plugin: options.plugin,
1455
1397
  ...(options.pluginError ? { pluginError: options.pluginError } : {}),
1456
1398
  scope: options.scope,
1399
+ context: options.context,
1400
+ platform: options.platform,
1457
1401
  cwd: options.cwd,
1458
1402
  env: options.env,
1459
1403
  });
@@ -1463,7 +1407,8 @@ export async function describeResetAffectedFields(options) {
1463
1407
  kind: field.kind,
1464
1408
  reserved: field.reserved,
1465
1409
  action: 'reset',
1466
- scope: options.scope,
1410
+ scope: 'user',
1411
+ platform: options.platform,
1467
1412
  valueState: field.valueState,
1468
1413
  }));
1469
1414
  }
@@ -1479,7 +1424,8 @@ export async function describeResetAffectedFields(options) {
1479
1424
  logicalIdentity: options.logicalIdentity,
1480
1425
  } : {}),
1481
1426
  pluginName: options.pluginName,
1482
- scope: options.scope,
1427
+ scope: 'user',
1428
+ platform: options.platform,
1483
1429
  cwd: options.cwd,
1484
1430
  env: options.env,
1485
1431
  });
@@ -1489,7 +1435,8 @@ export async function describeResetAffectedFields(options) {
1489
1435
  kind: 'manifest',
1490
1436
  reserved: false,
1491
1437
  action: 'reset',
1492
- scope: options.scope,
1438
+ scope: 'user',
1439
+ platform: options.platform,
1493
1440
  }));
1494
1441
  }
1495
1442
  }