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