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

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 (237) hide show
  1. package/dist/authoring/scripts/init-plugin.mjs +4 -33
  2. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  3. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  4. package/dist/authoring/scripts/plugin-status.mjs +8 -11
  5. package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
  6. package/dist/authoring/scripts/remove-plugin.mjs +1 -0
  7. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
  8. package/dist/authoring/scripts/validate-plugin.mjs +104 -54
  9. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  10. package/dist/scripts/codex-eval-app-server.d.ts +2 -0
  11. package/dist/scripts/codex-eval-app-server.js +107 -1
  12. package/dist/scripts/codex-eval-app-server.js.map +1 -1
  13. package/dist/scripts/eval-plugin-skills.js +5 -2
  14. package/dist/scripts/eval-plugin-skills.js.map +1 -1
  15. package/dist/scripts/plugin-eval-contract.d.ts +5 -1
  16. package/dist/scripts/plugin-eval-contract.js +54 -4
  17. package/dist/scripts/plugin-eval-contract.js.map +1 -1
  18. package/dist/scripts/plugin-staging.d.ts +6 -7
  19. package/dist/scripts/plugin-staging.js +11 -23
  20. package/dist/scripts/plugin-staging.js.map +1 -1
  21. package/dist/scripts/test-plugin-scripts.js +2 -33
  22. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  23. package/dist/src/adapters/helpers.js +2 -2
  24. package/dist/src/adapters/helpers.js.map +1 -1
  25. package/dist/src/authoring/workspace-commands.d.ts +17 -2
  26. package/dist/src/authoring/workspace-commands.js +21 -4
  27. package/dist/src/authoring/workspace-commands.js.map +1 -1
  28. package/dist/src/commands/aw-capabilities.d.ts +78 -0
  29. package/dist/src/commands/aw-capabilities.js +259 -0
  30. package/dist/src/commands/aw-capabilities.js.map +1 -0
  31. package/dist/src/commands/catalog-context.d.ts +52 -0
  32. package/dist/src/commands/catalog-context.js +156 -0
  33. package/dist/src/commands/catalog-context.js.map +1 -0
  34. package/dist/src/commands/catalog-dev-source.d.ts +112 -0
  35. package/dist/src/commands/catalog-dev-source.js +481 -0
  36. package/dist/src/commands/catalog-dev-source.js.map +1 -0
  37. package/dist/src/commands/catalog-generation.d.ts +26 -0
  38. package/dist/src/commands/catalog-generation.js +14 -0
  39. package/dist/src/commands/catalog-generation.js.map +1 -0
  40. package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
  41. package/dist/src/commands/catalog-publication-inspection.js +4 -5
  42. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  43. package/dist/src/commands/catalog-publication-target.d.ts +17 -0
  44. package/dist/src/commands/catalog-publication-target.js +60 -0
  45. package/dist/src/commands/catalog-publication-target.js.map +1 -0
  46. package/dist/src/commands/catalog-publish.d.ts +8 -1
  47. package/dist/src/commands/catalog-publish.js +25 -2
  48. package/dist/src/commands/catalog-publish.js.map +1 -1
  49. package/dist/src/commands/catalog-source.d.ts +23 -1
  50. package/dist/src/commands/catalog-source.js +126 -40
  51. package/dist/src/commands/catalog-source.js.map +1 -1
  52. package/dist/src/commands/catalog-status.d.ts +18 -2
  53. package/dist/src/commands/catalog-status.js +122 -18
  54. package/dist/src/commands/catalog-status.js.map +1 -1
  55. package/dist/src/commands/init.js +1 -1
  56. package/dist/src/commands/init.js.map +1 -1
  57. package/dist/src/commands/offline-cleanup-evidence.d.ts +23 -0
  58. package/dist/src/commands/offline-cleanup-evidence.js +38 -0
  59. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
  60. package/dist/src/commands/options.d.ts +1 -0
  61. package/dist/src/commands/options.js +1 -0
  62. package/dist/src/commands/options.js.map +1 -1
  63. package/dist/src/commands/plugin-capabilities.js +1 -1
  64. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  65. package/dist/src/commands/rebuild-command.d.ts +19 -7
  66. package/dist/src/commands/rebuild-command.js +415 -205
  67. package/dist/src/commands/rebuild-command.js.map +1 -1
  68. package/dist/src/commands/rebuild-integration.d.ts +10 -3
  69. package/dist/src/commands/rebuild-integration.js +71 -76
  70. package/dist/src/commands/rebuild-integration.js.map +1 -1
  71. package/dist/src/commands/rebuild-operations.d.ts +0 -8
  72. package/dist/src/commands/rebuild-operations.js +17 -115
  73. package/dist/src/commands/rebuild-operations.js.map +1 -1
  74. package/dist/src/commands/runtime.js +21 -34
  75. package/dist/src/commands/runtime.js.map +1 -1
  76. package/dist/src/commands/{status-v4.d.ts → status-v5.d.ts} +67 -21
  77. package/dist/src/commands/{status-v4.js → status-v5.js} +260 -30
  78. package/dist/src/commands/status-v5.js.map +1 -0
  79. package/dist/src/commands/wizard.d.ts +2 -2
  80. package/dist/src/commands/wizard.js +2 -2
  81. package/dist/src/commands/wizard.js.map +1 -1
  82. package/dist/src/commands/write-commands.js +73 -17
  83. package/dist/src/commands/write-commands.js.map +1 -1
  84. package/dist/src/constants.d.ts +6 -1
  85. package/dist/src/constants.js +6 -1
  86. package/dist/src/constants.js.map +1 -1
  87. package/dist/src/generators/local-plugin.js +57 -3
  88. package/dist/src/generators/local-plugin.js.map +1 -1
  89. package/dist/src/index.d.ts +4 -1
  90. package/dist/src/index.js +562 -85
  91. package/dist/src/index.js.map +1 -1
  92. package/dist/src/output/model.d.ts +1 -34
  93. package/dist/src/output/model.js +1 -144
  94. package/dist/src/output/model.js.map +1 -1
  95. package/dist/src/output/rebuild-progress.js +0 -3
  96. package/dist/src/output/rebuild-progress.js.map +1 -1
  97. package/dist/src/portable/commands.d.ts +4 -0
  98. package/dist/src/portable/commands.js +61 -4
  99. package/dist/src/portable/commands.js.map +1 -1
  100. package/dist/src/portable/materializer.d.ts +3 -0
  101. package/dist/src/portable/materializer.js +61 -10
  102. package/dist/src/portable/materializer.js.map +1 -1
  103. package/dist/src/tui/catalog-generation.d.ts +56 -0
  104. package/dist/src/tui/catalog-generation.js +244 -0
  105. package/dist/src/tui/catalog-generation.js.map +1 -0
  106. package/dist/src/tui/console-app-v4.d.ts +1 -1
  107. package/dist/src/tui/console-app-v4.js +446 -126
  108. package/dist/src/tui/console-app-v4.js.map +1 -1
  109. package/dist/src/tui/controller-runtime.d.ts +8 -2
  110. package/dist/src/tui/controller-runtime.js +606 -92
  111. package/dist/src/tui/controller-runtime.js.map +1 -1
  112. package/dist/src/tui/controller.d.ts +71 -6
  113. package/dist/src/tui/controller.js +377 -83
  114. package/dist/src/tui/controller.js.map +1 -1
  115. package/dist/src/tui/index.d.ts +1 -0
  116. package/dist/src/tui/index.js +1 -0
  117. package/dist/src/tui/index.js.map +1 -1
  118. package/dist/src/tui/input-display.d.ts +8 -0
  119. package/dist/src/tui/input-display.js +36 -0
  120. package/dist/src/tui/input-display.js.map +1 -0
  121. package/dist/src/tui/model.d.ts +27 -7
  122. package/dist/src/tui/model.js +273 -65
  123. package/dist/src/tui/model.js.map +1 -1
  124. package/dist/src/tui/router.d.ts +26 -0
  125. package/dist/src/tui/router.js.map +1 -1
  126. package/dist/src/tui/source-ref-presets.d.ts +3 -0
  127. package/dist/src/tui/source-ref-presets.js +11 -0
  128. package/dist/src/tui/source-ref-presets.js.map +1 -0
  129. package/dist/src/tui/views.js +31 -3
  130. package/dist/src/tui/views.js.map +1 -1
  131. package/dist/src/types.d.ts +90 -6
  132. package/dist/src/types.js.map +1 -1
  133. package/dist/src/utils/agent-version.d.ts +3 -0
  134. package/dist/src/utils/agent-version.js +13 -5
  135. package/dist/src/utils/agent-version.js.map +1 -1
  136. package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
  137. package/dist/src/utils/aw-home-mutation-lock.js +32 -4
  138. package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
  139. package/dist/src/utils/bundle-integrity.d.ts +0 -1
  140. package/dist/src/utils/bundle-integrity.js +0 -25
  141. package/dist/src/utils/bundle-integrity.js.map +1 -1
  142. package/dist/src/utils/bundle-layout.js +8 -6
  143. package/dist/src/utils/bundle-layout.js.map +1 -1
  144. package/dist/src/utils/catalog-digests.js +13 -2
  145. package/dist/src/utils/catalog-digests.js.map +1 -1
  146. package/dist/src/utils/catalog-generation-error.d.ts +12 -0
  147. package/dist/src/utils/catalog-generation-error.js +16 -0
  148. package/dist/src/utils/catalog-generation-error.js.map +1 -0
  149. package/dist/src/utils/catalog-generation-resolver.d.ts +13 -0
  150. package/dist/src/utils/catalog-generation-resolver.js +129 -0
  151. package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
  152. package/dist/src/utils/catalog-generation-service.d.ts +52 -0
  153. package/dist/src/utils/catalog-generation-service.js +620 -0
  154. package/dist/src/utils/catalog-generation-service.js.map +1 -0
  155. package/dist/src/utils/catalog-generation-state.d.ts +13 -0
  156. package/dist/src/utils/catalog-generation-state.js +201 -0
  157. package/dist/src/utils/catalog-generation-state.js.map +1 -0
  158. package/dist/src/utils/catalog-generation-store.d.ts +44 -0
  159. package/dist/src/utils/catalog-generation-store.js +387 -0
  160. package/dist/src/utils/catalog-generation-store.js.map +1 -0
  161. package/dist/src/utils/catalog-manifest-v5.d.ts +5 -0
  162. package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v5.js} +39 -35
  163. package/dist/src/utils/{catalog-manifest-v4.js.map → catalog-manifest-v5.js.map} +1 -1
  164. package/dist/src/utils/catalog-operation-context.d.ts +28 -0
  165. package/dist/src/utils/catalog-operation-context.js +242 -0
  166. package/dist/src/utils/catalog-operation-context.js.map +1 -0
  167. package/dist/src/utils/catalog-public-error.d.ts +7 -3
  168. package/dist/src/utils/catalog-public-error.js +20 -4
  169. package/dist/src/utils/catalog-public-error.js.map +1 -1
  170. package/dist/src/utils/catalog-snapshot.d.ts +5 -3
  171. package/dist/src/utils/catalog-snapshot.js +11 -3
  172. package/dist/src/utils/catalog-snapshot.js.map +1 -1
  173. package/dist/src/utils/catalog-source-cache.d.ts +1 -1
  174. package/dist/src/utils/catalog-source-cache.js +37 -12
  175. package/dist/src/utils/catalog-source-cache.js.map +1 -1
  176. package/dist/src/utils/catalog-source-identity.d.ts +1 -0
  177. package/dist/src/utils/catalog-source-identity.js +21 -2
  178. package/dist/src/utils/catalog-source-identity.js.map +1 -1
  179. package/dist/src/utils/catalog-sources.d.ts +1 -0
  180. package/dist/src/utils/catalog-sources.js +30 -11
  181. package/dist/src/utils/catalog-sources.js.map +1 -1
  182. package/dist/src/utils/codex-config.js +4 -4
  183. package/dist/src/utils/codex-config.js.map +1 -1
  184. package/dist/src/utils/codex-manager.d.ts +33 -0
  185. package/dist/src/utils/codex-manager.js +656 -0
  186. package/dist/src/utils/codex-manager.js.map +1 -0
  187. package/dist/src/utils/codex-skill-staging.d.ts +2 -0
  188. package/dist/src/utils/codex-skill-staging.js.map +1 -1
  189. package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
  190. package/dist/src/utils/dynamic-skill-tree.js +35 -5
  191. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  192. package/dist/src/utils/git-catalog-provider.js +10 -25
  193. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  194. package/dist/src/utils/lifecycle-active-store.d.ts +0 -28
  195. package/dist/src/utils/lifecycle-active-store.js +35 -247
  196. package/dist/src/utils/lifecycle-active-store.js.map +1 -1
  197. package/dist/src/utils/multi-source-catalog.d.ts +3 -0
  198. package/dist/src/utils/multi-source-catalog.js +120 -10
  199. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  200. package/dist/src/utils/native-cli.d.ts +15 -6
  201. package/dist/src/utils/native-cli.js +31 -39
  202. package/dist/src/utils/native-cli.js.map +1 -1
  203. package/dist/src/utils/orphan-marketplace-cleanup.js +0 -3
  204. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  205. package/dist/src/utils/ownership.d.ts +10 -1
  206. package/dist/src/utils/ownership.js +52 -24
  207. package/dist/src/utils/ownership.js.map +1 -1
  208. package/dist/src/utils/path-catalog-provider.js +12 -8
  209. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  210. package/dist/src/utils/plugin-config-write.js +18 -35
  211. package/dist/src/utils/plugin-config-write.js.map +1 -1
  212. package/dist/src/utils/plugin-config.d.ts +1 -12
  213. package/dist/src/utils/plugin-config.js +107 -226
  214. package/dist/src/utils/plugin-config.js.map +1 -1
  215. package/dist/src/utils/plugin-scripts.d.ts +1 -1
  216. package/dist/src/utils/plugin-scripts.js +1 -1
  217. package/dist/src/utils/plugin-scripts.js.map +1 -1
  218. package/dist/src/utils/state-paths.js +7 -2
  219. package/dist/src/utils/state-paths.js.map +1 -1
  220. package/dist/src/utils/target-eligibility.d.ts +7 -2
  221. package/dist/src/utils/target-eligibility.js +6 -3
  222. package/dist/src/utils/target-eligibility.js.map +1 -1
  223. package/package.json +4 -3
  224. package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
  225. package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
  226. package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
  227. package/dist/src/commands/status-v4.js.map +0 -1
  228. package/dist/src/commands/status.d.ts +0 -93
  229. package/dist/src/commands/status.js +0 -442
  230. package/dist/src/commands/status.js.map +0 -1
  231. package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
  232. package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
  233. package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
  234. package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
  235. package/dist/src/utils/catalog-manifest-v3.js +0 -3
  236. package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
  237. package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
@@ -1,25 +1,31 @@
1
1
  import path from 'node:path';
2
2
  import fs from 'fs-extra';
3
3
  import { probeAgentVersion } from '../utils/agent-version.js';
4
+ import { previewCodexManager, resolveCodexManager, } from '../utils/codex-manager.js';
4
5
  import { createNoopDebugLogger } from '../output/debug-log.js';
5
6
  import { createNoopReporter } from '../output/reporter.js';
6
7
  import { getDefaultNativeCliRunner } from '../utils/native-cli.js';
7
- import { readPluginManifest, resolvePluginsFromCatalog } from '../utils/manifest.js';
8
+ import { readPluginManifest } from '../utils/manifest.js';
8
9
  import { resolveAwHome } from '../utils/paths.js';
9
10
  import { resolveCliPackageRoot } from '../utils/paths.js';
10
11
  import { supportsPlatform } from '../utils/platform-support.js';
11
12
  import { hasCodexProjectManagedArtifacts } from '../utils/codex-project-surface.js';
12
- import { createRebuildActiveStore, executeRebuildOperation, releaseRebuildOperationLease, } from './rebuild-operations.js';
13
- import { assertNoClaudeUnboundNativeNameCollision, createRebuildIntegration, discoverRebuildTargetNames, inspectNativeMarketplaceRegistrations, } from './rebuild-integration.js';
13
+ import { createRebuildActiveStore, executeRebuildOperation, } from './rebuild-operations.js';
14
+ import { assertNoClaudeUnboundNativeNameCollision, codexNativeRemovalRequired, createRebuildIntegration, discoverRebuildTargetNames, inspectNativeMarketplaceRegistrations, } from './rebuild-integration.js';
14
15
  import { getNamespacedMarketplaceName } from '../utils/local-marketplace.js';
15
16
  import { formatPluginId, parsePluginId, planPluginSelectorSourceIds, preflightDesiredArtifactCollisions, withResolvedMultiSourceCatalog, } from '../utils/multi-source-catalog.js';
17
+ import { resolveCatalogOperationContext, } from '../utils/catalog-operation-context.js';
16
18
  import { assertStrictSemVer } from '../utils/manifest.js';
17
19
  import { assertCatalogSourceInventoryComplete, canonicalMarketplaceRegistrationNames, inventoryCatalogSourceReferences, } from '../utils/catalog-source-references.js';
18
20
  import { resolveEffectivePluginConfig } from '../utils/plugin-config.js';
19
21
  import { cleanupCorruptExclusivePluginDirectory, inspectOwnedComponents, } from '../utils/ownership.js';
20
22
  import { codexProjectConfigAliasesUserHome, getClaudeHome, getCodexHome, resolveCanonicalRepoProjectKey, resolveRepoRootSync, } from '../utils/state-paths.js';
21
23
  import { resolvePluginMetaDependencyClosure } from '../utils/plugin-dependencies.js';
24
+ import { validateCatalogSnapshotPluginEntriesComplete } from '../utils/catalog-snapshot.js';
25
+ import { AwCapabilityUnsupportedError, IMPLEMENTED_AW_CAPABILITIES, } from './aw-capabilities.js';
26
+ import { checkAwCapabilitySupport } from '../utils/manifest.js';
22
27
  import { createDependencyOperationPlan, } from './dependency-operation-plan.js';
28
+ import { offlineCleanupEvidenceFromInventory, offlineCleanupEvidenceMatches, } from './offline-cleanup-evidence.js';
23
29
  let rebuildCommandTestHooks;
24
30
  async function emitRebuildActionEvent(runtime, event) {
25
31
  await Promise.allSettled([
@@ -27,49 +33,6 @@ async function emitRebuildActionEvent(runtime, event) {
27
33
  Promise.resolve().then(() => runtime.debugLogger.log(event)),
28
34
  ]);
29
35
  }
30
- async function withRebuildActionEvent(options) {
31
- const source = {
32
- plugin: options.identity.pluginName,
33
- platform: options.identity.platform,
34
- scope: options.identity.scope,
35
- action: options.command,
36
- };
37
- const startedAt = Date.now();
38
- await emitRebuildActionEvent(options.runtime, {
39
- type: 'rebuild:action',
40
- source,
41
- data: { name: options.name, phase: 'start' },
42
- });
43
- let result;
44
- try {
45
- result = await options.operation();
46
- }
47
- catch (error) {
48
- await emitRebuildActionEvent(options.runtime, {
49
- type: 'rebuild:action',
50
- source,
51
- data: {
52
- name: options.name,
53
- phase: 'failure',
54
- durationMs: Math.max(0, Date.now() - startedAt),
55
- message: options.name === 'operation.lock'
56
- ? '获取操作锁失败。'
57
- : '释放操作锁失败。',
58
- },
59
- });
60
- throw error;
61
- }
62
- await emitRebuildActionEvent(options.runtime, {
63
- type: 'rebuild:action',
64
- source,
65
- data: {
66
- name: options.name,
67
- phase: 'success',
68
- durationMs: Math.max(0, Date.now() - startedAt),
69
- },
70
- });
71
- return result;
72
- }
73
36
  export function setRebuildCommandTestHooksForTests(hooks) {
74
37
  rebuildCommandTestHooks = hooks;
75
38
  }
@@ -82,11 +45,10 @@ function createSyntheticPlugin(pluginName, env) {
82
45
  rootDir,
83
46
  manifestPath: path.join(rootDir, '.agents-plugin', 'plugin.json'),
84
47
  manifest: {
85
- schemaVersion: 1,
48
+ schemaVersion: 2,
86
49
  name: pluginName,
87
50
  version: '0.0.0',
88
51
  description: 'Cleanup-only target for a plugin missing from the current catalog.',
89
- compatibility: { aw: { minVersion: '0.0.0' } },
90
52
  marketplace: {
91
53
  category: 'cleanup',
92
54
  strict: true,
@@ -110,17 +72,6 @@ function failedResult(command, identity, message, phase = 'preflight') {
110
72
  error: { phase, message },
111
73
  };
112
74
  }
113
- function appendOperationReleaseFailure(result, message) {
114
- result.actions.push({ name: 'operation.release', status: 'failed' });
115
- result.failures.push({ phase: 'operation.release', message });
116
- if (result.status === 'failed')
117
- return result;
118
- return {
119
- ...result,
120
- status: 'failed',
121
- error: { phase: 'operation.release', message },
122
- };
123
- }
124
75
  function toTargetResult(result) {
125
76
  return {
126
77
  target: {
@@ -138,17 +89,17 @@ function toTargetResult(result) {
138
89
  ...(result.status === 'failed' ? { message: result.error.message } : {}),
139
90
  };
140
91
  }
141
- async function assertAgentVersions(options) {
92
+ async function assertClaudeVersion(options) {
93
+ if (!options.platforms.includes('claude'))
94
+ return;
142
95
  const failures = [];
143
- for (const platform of options.platforms) {
144
- const result = await probeAgentVersion(platform, {
145
- cwd: options.cwd,
146
- env: options.env,
147
- runner: options.runtime.nativeCliRunner,
148
- });
149
- if (result.status !== 'supported')
150
- failures.push(result);
151
- }
96
+ const result = await probeAgentVersion('claude', {
97
+ cwd: options.cwd,
98
+ env: options.env,
99
+ runner: options.runtime.nativeCliRunner,
100
+ });
101
+ if (result.status !== 'supported')
102
+ failures.push(result);
152
103
  if (failures.length > 0) {
153
104
  throw new Error(failures.map((failure) => `${failure.command} 版本检查失败:${failure.details ?? failure.status};最低版本 ${failure.requiredMinVersion}。`)
154
105
  .join(';'));
@@ -177,8 +128,7 @@ async function runSingleSourceRebuildCommand(options) {
177
128
  nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
178
129
  reporter: options.runtime?.reporter ?? createNoopReporter(),
179
130
  debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
180
- runtimeLeaseHeld: options.runtime?.runtimeLeaseHeld,
181
- lifecycleLeaseHeld: options.runtime?.lifecycleLeaseHeld,
131
+ ...(options.runtime?.codexManager ? { codexManager: options.runtime.codexManager } : {}),
182
132
  expectedCatalogConfigDigest: options.runtime?.expectedCatalogConfigDigest,
183
133
  };
184
134
  const env = runtime.env;
@@ -186,7 +136,23 @@ async function runSingleSourceRebuildCommand(options) {
186
136
  const catalog = options.runtime?.catalogResolution;
187
137
  if (!catalog)
188
138
  throw new Error('Internal error: a resolved catalog source is required.');
189
- const catalogPlugins = await resolvePluginsFromCatalog(catalog.rootDir, undefined);
139
+ const requestedByPlatform = await resolveRequestedNames({
140
+ command: options.command,
141
+ pluginNames: options.pluginNames,
142
+ all: options.all,
143
+ platforms: selected,
144
+ scope: options.commandOptions.scope,
145
+ cwd: options.commandOptions.cwd,
146
+ env,
147
+ nativeCliRunner: runtime.nativeCliRunner,
148
+ });
149
+ const exactPluginNames = [...new Set([...requestedByPlatform.values()].flat())];
150
+ const catalogPlugins = options.runtime?.resolvedCatalogPlugins ?? (await Promise.all(exactPluginNames.map(async (pluginName) => {
151
+ const pluginRoot = path.join(catalog.rootDir, 'plugins', pluginName);
152
+ return await fs.pathExists(pluginRoot)
153
+ ? readPluginManifest(pluginRoot, { mode: 'bundle' })
154
+ : null;
155
+ }))).filter((plugin) => plugin !== null);
190
156
  if (options.runtime?.namespacedSource) {
191
157
  for (const plugin of catalogPlugins) {
192
158
  plugin.sourceId = options.runtime.namespacedSource.sourceId;
@@ -199,16 +165,6 @@ async function runSingleSourceRebuildCommand(options) {
199
165
  }
200
166
  }
201
167
  const catalogByName = new Map(catalogPlugins.map((plugin) => [plugin.manifest.name, plugin]));
202
- const requestedByPlatform = await resolveRequestedNames({
203
- command: options.command,
204
- pluginNames: options.pluginNames,
205
- all: options.all,
206
- platforms: selected,
207
- scope: options.commandOptions.scope,
208
- cwd: options.commandOptions.cwd,
209
- env,
210
- nativeCliRunner: runtime.nativeCliRunner,
211
- });
212
168
  if (options.commandOptions.platform === 'both'
213
169
  && options.command !== 'cleanup'
214
170
  && options.command !== 'uninstall') {
@@ -220,12 +176,14 @@ async function runSingleSourceRebuildCommand(options) {
220
176
  }
221
177
  }
222
178
  const platforms = selected.filter((platform) => (requestedByPlatform.get(platform)?.length ?? 0) > 0);
223
- await assertAgentVersions({
224
- platforms,
225
- cwd: options.commandOptions.cwd,
226
- env,
227
- runtime,
228
- });
179
+ if (options.runtime?.bypassDependencyPlan) {
180
+ await assertClaudeVersion({
181
+ platforms,
182
+ cwd: options.commandOptions.cwd,
183
+ env,
184
+ runtime,
185
+ });
186
+ }
229
187
  const integration = options.runtime?.preparedTarget
230
188
  ? {
231
189
  driver: options.runtime.preparedTarget.driver,
@@ -238,7 +196,6 @@ async function runSingleSourceRebuildCommand(options) {
238
196
  const activeStore = createRebuildActiveStore({
239
197
  env,
240
198
  loadPlugin: (bundleRoot) => readPluginManifest(bundleRoot, { mode: 'bundle' }),
241
- isLifecycleLeaseHeld: () => runtime.lifecycleLeaseHeld === true,
242
199
  });
243
200
  const targets = [];
244
201
  const results = [];
@@ -304,26 +261,6 @@ async function runSingleSourceRebuildCommand(options) {
304
261
  results.push(failedResult(options.command, identity, `${pluginName} 没有 Codex project scope 可生成的 Skills、instructions、roles、MCP、hooks 或 permissions。`));
305
262
  continue;
306
263
  }
307
- let operationLock;
308
- if (!options.runtime?.operationLeaseHeld) {
309
- try {
310
- await rebuildCommandTestHooks?.beforeOperationLockAcquire?.({
311
- command: options.command,
312
- identity,
313
- });
314
- operationLock = await withRebuildActionEvent({
315
- runtime,
316
- command: options.command,
317
- identity,
318
- name: 'operation.lock',
319
- operation: () => activeStore.acquireOperation(identity),
320
- });
321
- }
322
- catch (error) {
323
- results.push(failedResult(options.command, identity, error instanceof Error ? error.message : String(error), 'operation.lock'));
324
- continue;
325
- }
326
- }
327
264
  let result;
328
265
  try {
329
266
  if (options.runtime?.preparedTargetError)
@@ -358,34 +295,17 @@ async function runSingleSourceRebuildCommand(options) {
358
295
  ...options.runtime.resolvedReviewEvidenceByPlugin?.[pluginName],
359
296
  });
360
297
  }
361
- const { acquireOperation: _acquireOperation, ...operationHeldStore } = activeStore;
362
298
  result = await executeRebuildOperation({
363
299
  command: options.command,
364
300
  target,
365
301
  driver: integration.driver,
366
- activeStore: operationHeldStore,
302
+ activeStore,
367
303
  onActionEvent: (event) => emitRebuildActionEvent(runtime, event),
368
304
  });
369
305
  }
370
306
  catch (error) {
371
307
  result = error.result ?? failedResult(options.command, identity, error instanceof Error ? error.message : String(error), 'preflight');
372
308
  }
373
- result.actions.unshift({ name: 'operation.lock', status: 'succeeded' });
374
- if (operationLock) {
375
- try {
376
- await withRebuildActionEvent({
377
- runtime,
378
- command: options.command,
379
- identity,
380
- name: 'operation.release',
381
- operation: () => releaseRebuildOperationLease(operationLock),
382
- });
383
- result.actions.push({ name: 'operation.release', status: 'succeeded' });
384
- }
385
- catch (error) {
386
- result = appendOperationReleaseFailure(result, error instanceof Error ? error.message : String(error));
387
- }
388
- }
389
309
  results.push(result);
390
310
  }
391
311
  }
@@ -431,7 +351,6 @@ function toCatalogResolution(source) {
431
351
  source: 'local',
432
352
  catalogVersion: source.manifest.catalogVersion,
433
353
  channel: source.manifest.channel,
434
- minAwVersion: source.manifest.compatibility.aw.minVersion,
435
354
  };
436
355
  }
437
356
  function mergeRebuildCommandResults(results) {
@@ -460,6 +379,9 @@ function mergeRebuildCommandResults(results) {
460
379
  ...(results.find((result) => result.dependencyPlan)?.dependencyPlan
461
380
  ? { dependencyPlan: results.find((result) => result.dependencyPlan).dependencyPlan }
462
381
  : {}),
382
+ ...(results.find((result) => result.codexManagerPreview)?.codexManagerPreview
383
+ ? { codexManagerPreview: results.find((result) => result.codexManagerPreview).codexManagerPreview }
384
+ : {}),
463
385
  };
464
386
  }
465
387
  function leaseSafeRebuildResult(result) {
@@ -553,6 +475,10 @@ function resolveLiveCleanupTargets(options) {
553
475
  platform: reference.platform,
554
476
  scope: reference.scope,
555
477
  ...(marketplaceName ? { marketplaceName } : {}),
478
+ ...((liveNames.size === 1
479
+ || (marketplaceName !== undefined && unboundNativeNames.has(marketplaceName)))
480
+ ? { nativeRegistration: true }
481
+ : {}),
556
482
  ...(marketplaceIdentityUnresolved ? { marketplaceIdentityUnresolved: true } : {}),
557
483
  };
558
484
  const key = [target.pluginId, target.sourceIdentity, target.platform, target.scope].join('\0');
@@ -625,9 +551,40 @@ async function cleanupOfflineOwnedComponents(options) {
625
551
  }
626
552
  const results = [];
627
553
  const warnings = [];
554
+ const nativeCliRunner = options.nativeCliRunner ?? getDefaultNativeCliRunner();
555
+ let codexManager = options.codexManager;
556
+ const codexManagerNeedFailures = new Map();
557
+ if (!codexManager) {
558
+ const codexManagerRequired = (await Promise.all(options.targets.map(async (target) => {
559
+ if (target.platform !== 'codex' || target.scope !== 'user')
560
+ return false;
561
+ try {
562
+ return await codexNativeRemovalRequired({
563
+ pluginName: target.pluginName,
564
+ cwd: options.cwd,
565
+ env: options.env,
566
+ marketplaceName: target.marketplaceName,
567
+ marketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces', target.sourceId),
568
+ });
569
+ }
570
+ catch (error) {
571
+ codexManagerNeedFailures.set(target, new Error('codexNativeResidueProbeFailed: Codex native residue was retained because its config or cache evidence could not be read. Repair the damaged Codex weak state, then retry cleanup; you can also upgrade AW or install a compatible official Codex CLI.', { cause: error }));
572
+ return false;
573
+ }
574
+ }))).some(Boolean);
575
+ if (codexManagerRequired) {
576
+ codexManager = await (options.codexManagerResolver ?? resolveCodexManager)({
577
+ cwd: options.cwd,
578
+ env: options.env,
579
+ runner: nativeCliRunner,
580
+ authorizer: options.managedCodexAuthorizer,
581
+ });
582
+ }
583
+ }
628
584
  const context = {
629
585
  env: options.env,
630
- nativeCliRunner: options.nativeCliRunner ?? getDefaultNativeCliRunner(),
586
+ nativeCliRunner,
587
+ ...(codexManager ? { codexManager } : {}),
631
588
  reporter: createNoopReporter(),
632
589
  debugLogger: createNoopDebugLogger(),
633
590
  };
@@ -635,7 +592,6 @@ async function cleanupOfflineOwnedComponents(options) {
635
592
  const activeStore = createRebuildActiveStore({
636
593
  env: options.env,
637
594
  loadPlugin: (bundleRoot) => readPluginManifest(bundleRoot, { mode: 'bundle' }),
638
- isLifecycleLeaseHeld: () => context.lifecycleLeaseHeld === true,
639
595
  });
640
596
  for (const target of options.targets) {
641
597
  const exclusiveRecovery = {
@@ -690,6 +646,7 @@ async function cleanupOfflineOwnedComponents(options) {
690
646
  plugin.pluginId = target.pluginId;
691
647
  const marketplaceName = target.marketplaceName ?? 'aw';
692
648
  plugin.marketplaceName = marketplaceName;
649
+ const codexManagerNeedFailure = codexManagerNeedFailures.get(target);
693
650
  const desired = await integration.buildTarget({
694
651
  sourceId: target.sourceId,
695
652
  sourceIdentity: target.sourceIdentity,
@@ -706,23 +663,38 @@ async function cleanupOfflineOwnedComponents(options) {
706
663
  source: 'local',
707
664
  catalogVersion: '0.0.0',
708
665
  channel: 'beta',
709
- minAwVersion: '0.0.0',
710
666
  },
711
667
  includeHistoricalLifecycleArtifacts: false,
668
+ includeNativeCleanupCandidate: target.nativeRegistration === true && !codexManagerNeedFailure,
712
669
  });
713
- let result;
714
- const operationLock = await activeStore.acquireOperation(desired.identity);
715
- try {
716
- const { acquireOperation: _acquireOperation, ...operationHeldStore } = activeStore;
717
- result = await executeRebuildOperation({
718
- command: 'cleanup',
719
- target: desired,
720
- driver: integration.driver,
721
- activeStore: operationHeldStore,
722
- });
723
- }
724
- finally {
725
- await releaseRebuildOperationLease(operationLock);
670
+ const driver = codexManagerNeedFailure && integration.driver.probeNative
671
+ ? {
672
+ ...integration.driver,
673
+ probeNative: async () => {
674
+ throw codexManagerNeedFailure;
675
+ },
676
+ }
677
+ : integration.driver;
678
+ let result = await executeRebuildOperation({
679
+ command: 'cleanup',
680
+ target: desired,
681
+ driver,
682
+ activeStore,
683
+ });
684
+ if (codexManagerNeedFailure) {
685
+ const residual = `${target.pluginName}@${marketplaceName}`;
686
+ result = {
687
+ ...result,
688
+ status: 'failed',
689
+ actions: [...result.actions, { name: 'cleanup.owned', status: 'failed' }],
690
+ residual: [...new Set([...result.residual, residual])],
691
+ failures: [...result.failures, {
692
+ phase: 'cleanup.owned',
693
+ label: residual,
694
+ message: codexManagerNeedFailure.message,
695
+ }],
696
+ error: { phase: 'cleanup.owned', message: codexManagerNeedFailure.message },
697
+ };
726
698
  }
727
699
  const exclusiveResidual = (await Promise.all(exclusiveRecovery.residual.map(async (entry) => await fs.pathExists(entry) ? entry : null))).filter((entry) => entry !== null);
728
700
  const exclusiveQuarantined = (await Promise.all(exclusiveRecovery.quarantined.map(async (entry) => await fs.pathExists(entry) ? entry : null))).filter((entry) => entry !== null);
@@ -781,8 +753,33 @@ async function cleanupOfflineOwnedComponents(options) {
781
753
  })),
782
754
  });
783
755
  }
756
+ async function previewOfflineOwnedComponents(options) {
757
+ if (options.targets.some((target) => target.marketplaceIdentityUnresolved)) {
758
+ throw namespacedPreflightError('marketplaceIdentityUnavailable: Cannot uniquely recover the native marketplace name from live inventory or a canonical native registration path.', options.targets);
759
+ }
760
+ await options.onPreviewStage?.('inventory');
761
+ const preview = mergeRebuildCommandResults([]);
762
+ const codexManagerRequired = (await Promise.all(options.targets.flatMap((target) => target.platform === 'codex' && target.scope === 'user'
763
+ ? [codexNativeRemovalRequired({
764
+ pluginName: target.pluginName,
765
+ cwd: options.cwd,
766
+ env: options.env,
767
+ marketplaceName: target.marketplaceName,
768
+ marketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces', target.sourceId),
769
+ })]
770
+ : []))).some(Boolean);
771
+ await options.onPreviewStage?.('dependencyPlan');
772
+ if (codexManagerRequired) {
773
+ preview.codexManagerPreview = await (options.codexManagerPreviewer ?? previewCodexManager)({
774
+ cwd: options.cwd,
775
+ env: options.env,
776
+ runner: options.nativeCliRunner,
777
+ });
778
+ }
779
+ return preview;
780
+ }
784
781
  async function assertOfflineCleanupDependents(options) {
785
- const inventory = await inventoryCatalogSourceReferences({
782
+ const inventory = await options.readInventory({
786
783
  cwd: options.cwd,
787
784
  env: options.env,
788
785
  includeHistoricalProjects: true,
@@ -817,19 +814,27 @@ export async function runRebuildCommand(options) {
817
814
  return runSingleSourceRebuildCommand(options);
818
815
  }
819
816
  const runtimeEnv = options.runtime?.env ?? process.env;
817
+ const readInventory = options.runtime?.inventoryCatalogSourceReferences
818
+ ?? inventoryCatalogSourceReferences;
819
+ let ordinaryInventory;
820
+ const readOrdinaryInventory = async () => {
821
+ ordinaryInventory ??= await readInventory({
822
+ cwd: options.commandOptions.cwd,
823
+ env: runtimeEnv,
824
+ });
825
+ return ordinaryInventory;
826
+ };
820
827
  let requestedPluginNames = options.pluginNames;
821
828
  const cleanupForce = options.command === 'cleanup'
822
829
  && options.commandOptions.force === true;
830
+ const expectedOfflineCleanup = options.runtime?.expectedOfflineCleanupEvidence;
823
831
  let cleanupInventory;
824
832
  let cleanupLiveTargets = [];
825
833
  let cleanupHasDamagedExclusiveTarget = false;
826
834
  let liveTargets = [];
827
835
  let legacyLiveTargets = [];
828
836
  if (options.command === 'cleanup' && !options.all) {
829
- cleanupInventory = await inventoryCatalogSourceReferences({
830
- cwd: options.commandOptions.cwd,
831
- env: runtimeEnv,
832
- });
837
+ cleanupInventory = await readOrdinaryInventory();
833
838
  const live = resolveLiveCleanupTargets({
834
839
  selectors: requestedPluginNames,
835
840
  platforms: selectedPlatforms(options.commandOptions.platform),
@@ -838,7 +843,27 @@ export async function runRebuildCommand(options) {
838
843
  });
839
844
  requestedPluginNames = live.selectors;
840
845
  cleanupLiveTargets = live.targets;
841
- if (cleanupForce) {
846
+ if (expectedOfflineCleanup) {
847
+ const liveTarget = cleanupLiveTargets.find((target) => target.pluginId === expectedOfflineCleanup.pluginId
848
+ && target.pluginName === expectedOfflineCleanup.pluginName
849
+ && target.sourceId === expectedOfflineCleanup.sourceId
850
+ && target.sourceIdentity === expectedOfflineCleanup.sourceIdentity
851
+ && target.platform === expectedOfflineCleanup.platform
852
+ && target.scope === expectedOfflineCleanup.scope);
853
+ const actualOfflineCleanup = liveTarget
854
+ ? offlineCleanupEvidenceFromInventory({
855
+ ...expectedOfflineCleanup,
856
+ inventory: cleanupInventory,
857
+ })
858
+ : null;
859
+ if (!liveTarget
860
+ || !actualOfflineCleanup
861
+ || !offlineCleanupEvidenceMatches(expectedOfflineCleanup, actualOfflineCleanup)) {
862
+ throw namespacedPreflightError('operationReviewStale: offline cleanup ownership evidence changed before mutation.', cleanupLiveTargets);
863
+ }
864
+ cleanupLiveTargets = [liveTarget];
865
+ }
866
+ if (cleanupForce && !expectedOfflineCleanup) {
842
867
  const damagedTargets = (await Promise.all(requestedPluginNames.map(async (selector) => parsePluginId(selector).sourceId
843
868
  ? discoverDamagedExclusiveCleanupTargets({
844
869
  selector,
@@ -857,6 +882,16 @@ export async function runRebuildCommand(options) {
857
882
  liveTargets = cleanupLiveTargets;
858
883
  }
859
884
  if (cleanupHasDamagedExclusiveTarget) {
885
+ if (options.runtime?.previewDependencyPlanOnly) {
886
+ return previewOfflineOwnedComponents({
887
+ targets: cleanupLiveTargets,
888
+ cwd: options.commandOptions.cwd,
889
+ env: runtimeEnv,
890
+ nativeCliRunner: options.runtime.nativeCliRunner,
891
+ codexManagerPreviewer: options.runtime.codexManagerPreviewer,
892
+ onPreviewStage: options.runtime.onPreviewStage,
893
+ });
894
+ }
860
895
  return cleanupOfflineOwnedComponents({
861
896
  targets: cleanupLiveTargets,
862
897
  cwd: options.commandOptions.cwd,
@@ -864,14 +899,14 @@ export async function runRebuildCommand(options) {
864
899
  force: true,
865
900
  removeObsoleteInstallationContracts: options.runtime?.removeObsoleteInstallationContracts,
866
901
  nativeCliRunner: options.runtime?.nativeCliRunner,
902
+ codexManager: options.runtime?.codexManager,
903
+ codexManagerResolver: options.runtime?.codexManagerResolver,
904
+ managedCodexAuthorizer: options.runtime?.managedCodexAuthorizer,
867
905
  corruptExclusiveDirectoryOperations: options.runtime?.corruptExclusiveDirectoryOperations,
868
906
  });
869
907
  }
870
908
  if (options.all) {
871
- const inventory = await inventoryCatalogSourceReferences({
872
- cwd: options.commandOptions.cwd,
873
- env: runtimeEnv,
874
- });
909
+ const inventory = await readOrdinaryInventory();
875
910
  if (options.command !== 'cleanup')
876
911
  assertCatalogSourceInventoryComplete(inventory);
877
912
  const requestedPlatforms = new Set(selectedPlatforms(options.commandOptions.platform));
@@ -928,34 +963,52 @@ export async function runRebuildCommand(options) {
928
963
  if (options.runtime?.previewDependencyPlanOnly) {
929
964
  await options.runtime.onPreviewStage?.('catalog');
930
965
  }
966
+ if (options.runtime?.catalogBootstrapError !== undefined) {
967
+ throw options.runtime.catalogBootstrapError;
968
+ }
969
+ const catalogOperation = options.runtime?.catalogOperationContext
970
+ ?? (options.command === 'cleanup'
971
+ ? undefined
972
+ : await resolveCatalogOperationContext({
973
+ cliVersion: resolveRuntimeCliVersion(),
974
+ ...(sourceIds ? { sourceIds } : {}),
975
+ env: runtimeEnv,
976
+ ...(options.runtime?.catalogGenerationRuntime
977
+ ? { runtime: options.runtime.catalogGenerationRuntime }
978
+ : {}),
979
+ ...(options.runtime?.awHomeMutationToken
980
+ ? { bootstrapMissingActive: false }
981
+ : {}),
982
+ }));
983
+ if (options.runtime?.expectedGenerationSetDigest
984
+ && catalogOperation?.generationSetDigest !== options.runtime.expectedGenerationSetDigest) {
985
+ throw new Error('operationReviewStale: Active Catalog generation changed before mutation.');
986
+ }
931
987
  const resolvedResult = await withResolvedMultiSourceCatalog({
932
988
  cliVersion: resolveRuntimeCliVersion(),
933
- freshnessPolicy: options.command === 'cleanup'
934
- ? 'cacheFirst'
935
- : options.runtime?.previewDependencyPlanOnly
936
- ? options.runtime.previewCatalogFreshnessPolicy ?? 'requireRemote'
937
- : 'requireRemote',
989
+ freshnessPolicy: 'cacheFirst',
938
990
  ...(sourceIds ? { sourceIds } : {}),
991
+ ...(catalogOperation ? { operationContext: catalogOperation } : {}),
992
+ ...(options.command === 'cleanup' || options.runtime?.awHomeMutationToken
993
+ ? { bootstrapMissingActive: false }
994
+ : {}),
939
995
  env: runtimeEnv,
940
996
  consume: async (catalog) => {
941
997
  const failedSources = catalog.sources.filter((source) => source.error);
942
998
  if (failedSources.length > 0) {
943
999
  throw namespacedPreflightError(`Cannot resolve required catalog sources: ${failedSources.map((source) => source.sourceId).join(', ')}.`, liveTargets, legacyLiveTargets);
944
1000
  }
945
- const available = (await Promise.all(catalog.sources.map(async (item) => {
1001
+ const availableEntries = catalog.sources.flatMap((item) => {
946
1002
  const source = item.resolution;
947
- const plugins = await resolvePluginsFromCatalog(source.rootDir, undefined);
948
- return plugins.map((plugin) => {
949
- const pluginId = formatPluginId({ sourceId: source.sourceId, pluginName: plugin.manifest.name });
950
- plugin.sourceId = source.sourceId;
951
- plugin.sourceIdentity = source.sourceIdentity;
952
- plugin.pluginId = pluginId;
953
- return { source, plugin, pluginId };
954
- });
955
- }))).flat();
956
- const selected = selectors.map((selector, index) => {
957
- const matches = available.filter((entry) => entry.plugin.manifest.name === selector.pluginName
958
- && (selector.sourceId === undefined || entry.source.sourceId === selector.sourceId));
1003
+ return source.manifest.entries.map((entry) => ({
1004
+ source,
1005
+ entry,
1006
+ pluginId: formatPluginId({ sourceId: source.sourceId, pluginName: entry.name }),
1007
+ }));
1008
+ });
1009
+ const selectedEntries = selectors.map((selector, index) => {
1010
+ const matches = availableEntries.filter((candidate) => candidate.entry.name === selector.pluginName
1011
+ && (selector.sourceId === undefined || candidate.source.sourceId === selector.sourceId));
959
1012
  if (matches.length === 0) {
960
1013
  throw new Error(`Plugin "${requestedPluginNames[index]}" does not exist in the required catalog source set.`);
961
1014
  }
@@ -967,6 +1020,65 @@ export async function runRebuildCommand(options) {
967
1020
  const dependencyEnabled = options.command === 'install'
968
1021
  || options.command === 'upgrade'
969
1022
  || options.command === 'reinstall';
1023
+ const entryClosure = (roots) => {
1024
+ const result = [];
1025
+ const seen = new Set();
1026
+ const visiting = new Set();
1027
+ const byId = new Map(availableEntries.map((candidate) => [candidate.pluginId, candidate]));
1028
+ const visit = (candidate) => {
1029
+ if (seen.has(candidate.pluginId))
1030
+ return;
1031
+ if (visiting.has(candidate.pluginId))
1032
+ throw new Error(`Catalog dependency cycle includes "${candidate.pluginId}".`);
1033
+ visiting.add(candidate.pluginId);
1034
+ for (const dependency of candidate.entry.dependencies) {
1035
+ const dependencyId = formatPluginId({ sourceId: candidate.source.sourceId, pluginName: dependency });
1036
+ const resolved = byId.get(dependencyId);
1037
+ if (!resolved)
1038
+ throw new Error(`Catalog plugin "${candidate.pluginId}" depends on missing plugin "${dependency}".`);
1039
+ visit(resolved);
1040
+ }
1041
+ visiting.delete(candidate.pluginId);
1042
+ seen.add(candidate.pluginId);
1043
+ result.push(candidate);
1044
+ };
1045
+ for (const root of roots)
1046
+ visit(root);
1047
+ return result;
1048
+ };
1049
+ const removalConsumerEntries = options.command === 'cleanup' || options.command === 'uninstall'
1050
+ ? availableEntries.filter((candidate) => selectedEntries.some((selectedEntry) => candidate.source.sourceId === selectedEntry.source.sourceId
1051
+ && candidate.entry.dependencies.includes(selectedEntry.entry.name)))
1052
+ : [];
1053
+ const requiredEntryClosure = dependencyEnabled
1054
+ ? entryClosure(selectedEntries)
1055
+ : [...new Map([...selectedEntries, ...removalConsumerEntries].map((candidate) => [candidate.pluginId, candidate])).values()];
1056
+ if (dependencyEnabled) {
1057
+ for (const candidate of requiredEntryClosure) {
1058
+ const requiredCapabilities = candidate.entry.requires.aw.capabilities ?? [];
1059
+ const support = checkAwCapabilitySupport(requiredCapabilities, IMPLEMENTED_AW_CAPABILITIES);
1060
+ if (!support.supported) {
1061
+ throw new AwCapabilityUnsupportedError(candidate.pluginId, support.missing);
1062
+ }
1063
+ }
1064
+ for (const item of catalog.sources) {
1065
+ const source = item.resolution;
1066
+ const names = requiredEntryClosure
1067
+ .filter((candidate) => candidate.source.sourceId === source.sourceId)
1068
+ .map((candidate) => candidate.entry.name);
1069
+ if (names.length > 0) {
1070
+ await validateCatalogSnapshotPluginEntriesComplete(source.rootDir, names);
1071
+ }
1072
+ }
1073
+ }
1074
+ const available = await Promise.all(requiredEntryClosure.map(async (candidate) => {
1075
+ const plugin = await readPluginManifest(path.join(candidate.source.rootDir, 'plugins', candidate.entry.name), { mode: 'bundle' });
1076
+ plugin.sourceId = candidate.source.sourceId;
1077
+ plugin.sourceIdentity = candidate.source.sourceIdentity;
1078
+ plugin.pluginId = candidate.pluginId;
1079
+ return { source: candidate.source, plugin, pluginId: candidate.pluginId };
1080
+ }));
1081
+ const selected = selectedEntries.map((entry) => available.find((candidate) => candidate.pluginId === entry.pluginId));
970
1082
  const selectedIds = new Set(selected.map((entry) => entry.pluginId));
971
1083
  const resolveClosureEntries = (rootsBySource) => catalog.sources.flatMap((item) => {
972
1084
  const source = item.resolution;
@@ -1034,10 +1146,7 @@ export async function runRebuildCommand(options) {
1034
1146
  if (options.runtime?.previewDependencyPlanOnly) {
1035
1147
  await options.runtime.onPreviewStage?.('inventory');
1036
1148
  }
1037
- const stateInventory = await inventoryCatalogSourceReferences({
1038
- cwd: options.commandOptions.cwd,
1039
- env: runtimeEnv,
1040
- });
1149
+ const stateInventory = await readOrdinaryInventory();
1041
1150
  if (options.command !== 'cleanup')
1042
1151
  assertCatalogSourceInventoryComplete(stateInventory);
1043
1152
  const commandProjectKey = await resolveCanonicalRepoProjectKey(options.commandOptions.cwd);
@@ -1121,19 +1230,13 @@ export async function runRebuildCommand(options) {
1121
1230
  nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1122
1231
  reporter: options.runtime?.reporter ?? createNoopReporter(),
1123
1232
  debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1124
- runtimeLeaseHeld: options.runtime?.runtimeLeaseHeld,
1125
- lifecycleLeaseHeld: options.runtime?.lifecycleLeaseHeld,
1126
1233
  expectedCatalogConfigDigest: catalog.configDigest,
1127
1234
  };
1128
1235
  const inspection = createRebuildIntegration({
1129
1236
  context: executionContext,
1130
1237
  scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
1131
1238
  });
1132
- const createPlan = async (preparedInventory) => {
1133
- const inventory = preparedInventory ?? await inventoryCatalogSourceReferences({
1134
- cwd: options.commandOptions.cwd,
1135
- env: runtimeEnv,
1136
- });
1239
+ const createPlan = async (inventory) => {
1137
1240
  if (options.command !== 'cleanup')
1138
1241
  assertCatalogSourceInventoryComplete(inventory);
1139
1242
  const targets = [];
@@ -1172,8 +1275,8 @@ export async function runRebuildCommand(options) {
1172
1275
  const hasUnexpectedPresent = result.components.some((entry) => entry.state === 'present' && !required.has(entry.component) && !optional.has(entry.component));
1173
1276
  const hasPartial = result.components.some((entry) => entry.state === 'partial');
1174
1277
  const hasAnyPresent = result.components.some((entry) => entry.state === 'present');
1175
- const state = !hasAnyPresent
1176
- && result.components.every((entry) => entry.state === 'absent')
1278
+ const hasAnyOwned = hasAnyPresent || hasPartial;
1279
+ const state = !hasAnyOwned
1177
1280
  ? 'absent'
1178
1281
  : hasPartial || hasUnexpectedPresent || !presentRequired
1179
1282
  ? 'partial'
@@ -1195,7 +1298,7 @@ export async function runRebuildCommand(options) {
1195
1298
  : options.command === 'reinstall' ? 'reinstall' : 'upgrade';
1196
1299
  let errorCode;
1197
1300
  const hasForeignCollision = [...requestedInspection.components, ...otherInspection.components]
1198
- .some((component) => component.diagnostic?.includes('foreign collision'));
1301
+ .some((component) => component.collision !== undefined);
1199
1302
  if (dependencyEnabled && hasForeignCollision) {
1200
1303
  action = 'block';
1201
1304
  errorCode = 'artifactCollision';
@@ -1305,11 +1408,77 @@ export async function runRebuildCommand(options) {
1305
1408
  targets,
1306
1409
  });
1307
1410
  };
1411
+ const operationNeedsNativeCli = async (platform, plan) => {
1412
+ for (const { entry, platform: selectedPlatform } of executionSelections) {
1413
+ if (selectedPlatform !== platform)
1414
+ continue;
1415
+ const planned = plan?.targets.find((target) => target.pluginId === entry.pluginId && target.platform === selectedPlatform);
1416
+ if (dependencyEnabled && (!planned
1417
+ || planned.action === 'keep'
1418
+ || planned.action === 'block'))
1419
+ continue;
1420
+ const scope = planned?.resolvedScope ?? options.commandOptions.scope;
1421
+ const marketplaceName = marketplaceNameFor(entry, selectedPlatform, scope);
1422
+ entry.plugin.marketplaceName = marketplaceName;
1423
+ const marketplaceRoot = path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId);
1424
+ if (!dependencyEnabled && selectedPlatform === 'codex') {
1425
+ if (scope === 'user' && await codexNativeRemovalRequired({
1426
+ pluginName: entry.plugin.manifest.name,
1427
+ cwd: options.commandOptions.cwd,
1428
+ env: runtimeEnv,
1429
+ marketplaceName,
1430
+ marketplaceRoot,
1431
+ }))
1432
+ return true;
1433
+ continue;
1434
+ }
1435
+ const target = await inspection.buildTarget({
1436
+ sourceId: entry.source.sourceId,
1437
+ sourceIdentity: entry.source.sourceIdentity,
1438
+ pluginId: entry.pluginId,
1439
+ marketplaceName,
1440
+ marketplaceRoot,
1441
+ plugin: entry.plugin,
1442
+ platform: selectedPlatform,
1443
+ scope,
1444
+ cwd: options.commandOptions.cwd,
1445
+ command: dependencyEnabled
1446
+ ? planned.action
1447
+ : options.command,
1448
+ catalog: toCatalogResolution(entry.source),
1449
+ includeHistoricalLifecycleArtifacts: false,
1450
+ readOnlyInspectionPlan: true,
1451
+ });
1452
+ if (dependencyEnabled && target.requiresNative)
1453
+ return true;
1454
+ const nativeComponents = target.ownedComponents.filter((component) => component.kind === 'native-plugin'
1455
+ || (component.kind === 'shared-config'
1456
+ && component.identity.artifactKey.startsWith('marketplace-')));
1457
+ if (nativeComponents.length === 0)
1458
+ continue;
1459
+ const nativeInspection = await inspectOwnedComponents({
1460
+ components: nativeComponents,
1461
+ nativeProbe: inspection.driver.probeNative,
1462
+ });
1463
+ if (nativeInspection.components.some((component) => component.state === 'present' || component.state === 'partial')) {
1464
+ return true;
1465
+ }
1466
+ }
1467
+ return false;
1468
+ };
1308
1469
  if (options.runtime?.previewDependencyPlanOnly) {
1309
1470
  await options.runtime.onPreviewStage?.('dependencyPlan');
1310
- const initialPlan = await createPlan(stateInventory);
1471
+ const initialPlan = dependencyEnabled ? await createPlan(stateInventory) : undefined;
1311
1472
  const preview = mergeRebuildCommandResults([]);
1312
- preview.dependencyPlan = initialPlan;
1473
+ if (initialPlan)
1474
+ preview.dependencyPlan = initialPlan;
1475
+ if (await operationNeedsNativeCli('codex', initialPlan)) {
1476
+ preview.codexManagerPreview = await (options.runtime.codexManagerPreviewer ?? previewCodexManager)({
1477
+ cwd: options.commandOptions.cwd,
1478
+ env: runtimeEnv,
1479
+ runner: options.runtime?.nativeCliRunner,
1480
+ });
1481
+ }
1313
1482
  return preview;
1314
1483
  }
1315
1484
  for (const { entry, platform } of executionSelections.filter((selection) => selectedIds.has(selection.entry.pluginId))) {
@@ -1326,6 +1495,9 @@ export async function runRebuildCommand(options) {
1326
1495
  cwd: options.commandOptions.cwd,
1327
1496
  },
1328
1497
  configDigest: catalog.configDigest,
1498
+ ...(catalogOperation
1499
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
1500
+ : {}),
1329
1501
  sourceId: entry.source.sourceId,
1330
1502
  sourceIdentity: entry.source.sourceIdentity,
1331
1503
  resolvedCommit: entry.source.resolvedCommit,
@@ -1334,7 +1506,11 @@ export async function runRebuildCommand(options) {
1334
1506
  contentDigest: entry.source.manifest.entries.find((candidate) => candidate.name === entry.plugin.manifest.name)?.contentDigest,
1335
1507
  });
1336
1508
  }
1337
- const lockedPlan = await createPlan();
1509
+ const lockedPlan = await createPlan(stateInventory);
1510
+ if (options.runtime?.expectedOperationDependencyPlan
1511
+ && JSON.stringify(lockedPlan) !== JSON.stringify(options.runtime.expectedOperationDependencyPlan)) {
1512
+ throw namespacedPreflightError('operationReviewStale: dependency plan changed before mutation.', preflightTargets);
1513
+ }
1338
1514
  for (const pluginName of new Set(lockedPlan.targets.flatMap((target) => target.platform === 'claude'
1339
1515
  && (target.action === 'install'
1340
1516
  || target.action === 'upgrade'
@@ -1347,16 +1523,25 @@ export async function runRebuildCommand(options) {
1347
1523
  env: runtimeEnv,
1348
1524
  });
1349
1525
  }
1350
- await assertAgentVersions({
1351
- platforms: [...new Set(executionSelections.flatMap(({ entry, platform }) => supportsPlatform(entry.plugin.manifest, platform) ? [platform] : []))],
1526
+ const codexManagerRequired = await operationNeedsNativeCli('codex', lockedPlan);
1527
+ const claudeCliRequired = await operationNeedsNativeCli('claude', lockedPlan);
1528
+ if (codexManagerRequired) {
1529
+ executionContext.codexManager = await (options.runtime?.codexManagerResolver ?? resolveCodexManager)({
1530
+ cwd: options.commandOptions.cwd,
1531
+ env: runtimeEnv,
1532
+ runner: executionContext.nativeCliRunner,
1533
+ authorizer: options.runtime?.managedCodexAuthorizer,
1534
+ });
1535
+ }
1536
+ await assertClaudeVersion({
1537
+ platforms: claudeCliRequired ? ['claude'] : [],
1352
1538
  cwd: options.commandOptions.cwd,
1353
1539
  env: runtimeEnv,
1354
- runtime: {
1355
- env: runtimeEnv,
1356
- nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1357
- reporter: options.runtime?.reporter ?? createNoopReporter(),
1358
- debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1359
- },
1540
+ runtime: executionContext,
1541
+ });
1542
+ const executionIntegration = createRebuildIntegration({
1543
+ context: executionContext,
1544
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
1360
1545
  });
1361
1546
  const preparedTargets = new Map();
1362
1547
  const preparedTargetErrors = new Map();
@@ -1369,7 +1554,7 @@ export async function runRebuildCommand(options) {
1369
1554
  continue;
1370
1555
  const marketplaceName = marketplaceNameFor(entry, platform, planned.resolvedScope);
1371
1556
  try {
1372
- const target = await inspection.buildTarget({
1557
+ const target = await executionIntegration.buildTarget({
1373
1558
  sourceId: entry.source.sourceId,
1374
1559
  sourceIdentity: entry.source.sourceIdentity,
1375
1560
  pluginId: entry.pluginId,
@@ -1385,7 +1570,7 @@ export async function runRebuildCommand(options) {
1385
1570
  });
1386
1571
  preparedTargets.set(`${entry.pluginId}\0${platform}`, {
1387
1572
  target,
1388
- driver: inspection.driver,
1573
+ driver: executionIntegration.driver,
1389
1574
  context: executionContext,
1390
1575
  });
1391
1576
  if (selectedIds.has(entry.pluginId)) {
@@ -1417,7 +1602,7 @@ export async function runRebuildCommand(options) {
1417
1602
  }
1418
1603
  const removalRequiredConsumers = new Map();
1419
1604
  if ((options.command === 'cleanup' && !cleanupForce) || options.command === 'uninstall') {
1420
- const removalStateInventory = await inventoryCatalogSourceReferences({
1605
+ const removalStateInventory = await readInventory({
1421
1606
  cwd: options.commandOptions.cwd,
1422
1607
  env: runtimeEnv,
1423
1608
  includeHistoricalProjects: true,
@@ -1628,9 +1813,14 @@ export async function runRebuildCommand(options) {
1628
1813
  runtime: {
1629
1814
  ...options.runtime,
1630
1815
  env: runtimeEnv,
1816
+ ...(executionContext.codexManager
1817
+ ? { codexManager: executionContext.codexManager }
1818
+ : {}),
1631
1819
  catalogResolution: toCatalogResolution(entry.source),
1820
+ resolvedCatalogPlugins: available
1821
+ .filter((candidate) => candidate.source.sourceId === entry.source.sourceId)
1822
+ .map((candidate) => candidate.plugin),
1632
1823
  namespacedSource,
1633
- operationLeaseHeld: true,
1634
1824
  dependencyReviewPreflightHandled: true,
1635
1825
  targetBuiltHookHandled: true,
1636
1826
  preparedTarget: preparedTargets.get(`${entry.pluginId}\0${platform}`),
@@ -1639,6 +1829,9 @@ export async function runRebuildCommand(options) {
1639
1829
  resolvedReviewEvidenceByPlugin: {
1640
1830
  [entry.plugin.manifest.name]: {
1641
1831
  configDigest: catalog.configDigest,
1832
+ ...(catalogOperation
1833
+ ? { generationSetDigest: catalogOperation.generationSetDigest }
1834
+ : {}),
1642
1835
  sourceId: entry.source.sourceId,
1643
1836
  sourceIdentity: entry.source.sourceIdentity,
1644
1837
  resolvedCommit: entry.source.resolvedCommit,
@@ -1657,7 +1850,7 @@ export async function runRebuildCommand(options) {
1657
1850
  return leaseSafeRebuildResult(merged);
1658
1851
  },
1659
1852
  });
1660
- if (cleanupForce) {
1853
+ if (cleanupForce && !options.runtime?.previewDependencyPlanOnly) {
1661
1854
  try {
1662
1855
  await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
1663
1856
  }
@@ -1677,25 +1870,42 @@ export async function runRebuildCommand(options) {
1677
1870
  const message = error instanceof Error ? error.message : String(error);
1678
1871
  const catalogUnavailable = message.startsWith('Cannot resolve required catalog sources:')
1679
1872
  || message.includes('does not exist in the required catalog source set')
1680
- || (message.includes('Catalog source') && message.includes('does not exist'));
1873
+ || (message.includes('Catalog source')
1874
+ && (message.includes('does not exist')
1875
+ || message.includes('is not configured')
1876
+ || message.includes('has no Active generation')));
1681
1877
  if (options.command === 'cleanup'
1682
1878
  && (catalogUnavailable || message.includes('sourceChanged'))
1683
1879
  && cleanupInventory
1684
1880
  && cleanupLiveTargets.length > 0) {
1685
- if (!cleanupForce) {
1881
+ if (!cleanupForce || expectedOfflineCleanup) {
1686
1882
  await assertOfflineCleanupDependents({
1687
1883
  targets: cleanupLiveTargets,
1688
1884
  cwd: options.commandOptions.cwd,
1689
1885
  env: runtimeEnv,
1886
+ readInventory,
1887
+ });
1888
+ }
1889
+ if (options.runtime?.previewDependencyPlanOnly) {
1890
+ return previewOfflineOwnedComponents({
1891
+ targets: cleanupLiveTargets,
1892
+ cwd: options.commandOptions.cwd,
1893
+ env: runtimeEnv,
1894
+ nativeCliRunner: options.runtime.nativeCliRunner,
1895
+ codexManagerPreviewer: options.runtime.codexManagerPreviewer,
1896
+ onPreviewStage: options.runtime.onPreviewStage,
1690
1897
  });
1691
1898
  }
1692
1899
  return cleanupOfflineOwnedComponents({
1693
1900
  targets: cleanupLiveTargets,
1694
1901
  cwd: options.commandOptions.cwd,
1695
1902
  env: runtimeEnv,
1696
- force: cleanupForce,
1903
+ force: cleanupForce && !expectedOfflineCleanup,
1697
1904
  removeObsoleteInstallationContracts: options.runtime?.removeObsoleteInstallationContracts,
1698
1905
  nativeCliRunner: options.runtime?.nativeCliRunner,
1906
+ codexManager: options.runtime?.codexManager,
1907
+ codexManagerResolver: options.runtime?.codexManagerResolver,
1908
+ managedCodexAuthorizer: options.runtime?.managedCodexAuthorizer,
1699
1909
  corruptExclusiveDirectoryOperations: options.runtime?.corruptExclusiveDirectoryOperations,
1700
1910
  });
1701
1911
  }