@lesliechan721/aw 0.1.0-beta.6 → 0.1.0-beta.61
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.mjs +100 -501
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/assets/aw-runtime-config.d.mts +13 -0
- package/dist/authoring/assets/aw-runtime-config.mjs +199 -0
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -0
- package/dist/authoring/scripts/add-skill.mjs +4 -4
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +67 -97
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-root.mjs +4 -4
- package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -92
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -124
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/plugin-status.mjs +8 -11
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
- package/dist/authoring/scripts/remove-plugin.mjs +3 -2
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +552 -1142
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/native-runtime/reader-runtime.mjs +34 -0
- package/dist/native-runtime/reader.d.mts +6 -0
- package/dist/native-runtime/reader.mjs +11 -0
- package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
- package/dist/scripts/aw-skill-eval-materializer.js +306 -0
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
- package/dist/scripts/bundle-plugins.d.ts +1 -0
- package/dist/scripts/bundle-plugins.js +19 -8
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +7 -9
- package/dist/scripts/plugin-staging.js +18 -29
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/plugin-test-reporter.d.mts +2 -0
- package/dist/scripts/plugin-test-reporter.mjs +21 -0
- package/dist/scripts/plugin-test-reporter.mjs.map +1 -0
- package/dist/scripts/test-plugin-scripts.d.ts +5 -0
- package/dist/scripts/test-plugin-scripts.js +208 -89
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/scripts/test-workers.d.mts +24 -0
- package/dist/scripts/test-workers.mjs +59 -0
- package/dist/scripts/test-workers.mjs.map +1 -0
- package/dist/src/adapters/helpers.d.ts +7 -0
- package/dist/src/adapters/helpers.js +2 -2
- package/dist/src/adapters/helpers.js.map +1 -1
- package/dist/src/authoring/workspace-commands.d.ts +19 -1
- package/dist/src/authoring/workspace-commands.js +24 -5
- package/dist/src/authoring/workspace-commands.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +78 -0
- package/dist/src/commands/aw-capabilities.js +266 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +154 -0
- package/dist/src/commands/catalog-dev-source.js +655 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +21 -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 +71 -30
- package/dist/src/commands/catalog-publication-inspection.js +475 -63
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
- package/dist/src/commands/catalog-publication-journal.js +292 -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 +73 -15
- package/dist/src/commands/catalog-publish.js +701 -212
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +60 -22
- package/dist/src/commands/catalog-source.js +311 -91
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-status.d.ts +18 -22
- package/dist/src/commands/catalog-status.js +81 -115
- package/dist/src/commands/catalog-status.js.map +1 -1
- package/dist/src/commands/catalog-store.d.ts +104 -0
- package/dist/src/commands/catalog-store.js +685 -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.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
- package/dist/src/commands/dependency-operation-plan.js +289 -7
- package/dist/src/commands/dependency-operation-plan.js.map +1 -1
- package/dist/src/commands/init.d.ts +5 -11
- package/dist/src/commands/init.js +30 -51
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/managed-commands.d.ts +28 -0
- package/dist/src/commands/managed-commands.js +315 -0
- package/dist/src/commands/managed-commands.js.map +1 -0
- package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
- package/dist/src/commands/offline-cleanup-evidence.js +181 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +182 -0
- package/dist/src/commands/operation-plan.js +71 -0
- package/dist/src/commands/operation-plan.js.map +1 -0
- package/dist/src/commands/operation-planner.d.ts +47 -0
- package/dist/src/commands/operation-planner.js +142 -0
- package/dist/src/commands/operation-planner.js.map +1 -0
- package/dist/src/commands/options.d.ts +9 -7
- package/dist/src/commands/options.js +83 -21
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +65 -4
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +78 -31
- package/dist/src/commands/rebuild-command.js +5229 -942
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +143 -21
- package/dist/src/commands/rebuild-integration.js +3384 -1342
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +60 -30
- package/dist/src/commands/rebuild-operations.js +267 -320
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
- package/dist/src/commands/reconcile-operation-plan.js +47 -0
- package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
- package/dist/src/commands/review-inspection.d.ts +60 -0
- package/dist/src/commands/review-inspection.js +422 -0
- package/dist/src/commands/review-inspection.js.map +1 -0
- package/dist/src/commands/review-preparation.d.ts +49 -0
- package/dist/src/commands/review-preparation.js +427 -0
- package/dist/src/commands/review-preparation.js.map +1 -0
- package/dist/src/commands/runtime.d.ts +1 -5
- package/dist/src/commands/runtime.js +27 -53
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +39 -0
- package/dist/src/commands/skill-eval.js +400 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +279 -0
- package/dist/src/commands/status-v6.js +2785 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +40 -0
- package/dist/src/commands/sync-command.js +254 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +8 -5
- package/dist/src/commands/wizard.js +6 -4
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +9 -3
- package/dist/src/commands/write-commands.js +136 -31
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -2
- package/dist/src/constants.js +7 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +68 -0
- package/dist/src/eval/manifest-v1.js +353 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +5 -1
- package/dist/src/generators/local-plugin.js +263 -109
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/generators/permissions.d.ts +5 -2
- package/dist/src/generators/permissions.js +28 -15
- package/dist/src/generators/permissions.js.map +1 -1
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.js +1774 -609
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +47 -46
- package/dist/src/output/command-receipt.js +277 -79
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/model.d.ts +3 -44
- package/dist/src/output/model.js +1 -144
- package/dist/src/output/model.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +15 -13
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +46 -7
- package/dist/src/portable/commands.js +349 -37
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +17 -4
- package/dist/src/portable/materializer.js +186 -41
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/skills-cli.d.ts +15 -7
- package/dist/src/portable/skills-cli.js +74 -20
- package/dist/src/portable/skills-cli.js.map +1 -1
- package/dist/src/review/eval-source-input.d.ts +43 -0
- package/dist/src/review/eval-source-input.js +207 -0
- package/dist/src/review/eval-source-input.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +17 -0
- package/dist/src/review/review-profile.js +234 -0
- package/dist/src/review/review-profile.js.map +1 -0
- package/dist/src/review/review-selection.d.ts +30 -0
- package/dist/src/review/review-selection.js +527 -0
- package/dist/src/review/review-selection.js.map +1 -0
- package/dist/src/review/review-types.d.ts +68 -0
- package/dist/src/review/review-types.js +16 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/review/skill-materialization.d.ts +26 -0
- package/dist/src/review/skill-materialization.js +217 -0
- package/dist/src/review/skill-materialization.js.map +1 -0
- package/dist/src/review/source-binding.d.ts +183 -0
- package/dist/src/review/source-binding.js +708 -0
- package/dist/src/review/source-binding.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 +156 -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 +40 -0
- package/dist/src/tui/catalog-sources/screens.js.map +1 -0
- package/dist/src/tui/catalog-sources/session.d.ts +32 -0
- package/dist/src/tui/catalog-sources/session.js +300 -0
- package/dist/src/tui/catalog-sources/session.js.map +1 -0
- package/dist/src/tui/catalog-sources/types.d.ts +54 -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/catalog-store-task.d.ts +16 -0
- package/dist/src/tui/catalog-store-task.js +19 -0
- package/dist/src/tui/catalog-store-task.js.map +1 -0
- package/dist/src/tui/clipboard.d.ts +13 -0
- package/dist/src/tui/clipboard.js +95 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/components.d.ts +110 -3
- package/dist/src/tui/components.js +183 -36
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-array-draft.d.ts +29 -0
- package/dist/src/tui/config-array-draft.js +48 -0
- package/dist/src/tui/config-array-draft.js.map +1 -0
- package/dist/src/tui/config-leaf-editor.d.ts +19 -0
- package/dist/src/tui/config-leaf-editor.js +212 -0
- package/dist/src/tui/config-leaf-editor.js.map +1 -0
- package/dist/src/tui/config-references.d.ts +34 -0
- package/dist/src/tui/config-references.js +70 -0
- package/dist/src/tui/config-references.js.map +1 -0
- package/dist/src/tui/config-value-presentation.d.ts +20 -0
- package/dist/src/tui/config-value-presentation.js +104 -0
- package/dist/src/tui/config-value-presentation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +14 -4
- package/dist/src/tui/console-app-v4.js +9205 -1951
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/content-item-card.d.ts +13 -0
- package/dist/src/tui/content-item-card.js +51 -0
- package/dist/src/tui/content-item-card.js.map +1 -0
- package/dist/src/tui/controller-runtime.d.ts +10 -2
- package/dist/src/tui/controller-runtime.js +3289 -388
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +622 -45
- package/dist/src/tui/controller.js +3589 -768
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph-view.d.ts +39 -0
- package/dist/src/tui/dependency-graph-view.js +121 -0
- package/dist/src/tui/dependency-graph-view.js.map +1 -0
- package/dist/src/tui/dependency-graph.d.ts +81 -0
- package/dist/src/tui/dependency-graph.js +452 -0
- package/dist/src/tui/dependency-graph.js.map +1 -0
- package/dist/src/tui/diagnostic-projection.d.ts +23 -0
- package/dist/src/tui/diagnostic-projection.js +595 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +3 -3
- package/dist/src/tui/diagnostics.js +14 -2
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +55 -0
- package/dist/src/tui/entity-cards.js +220 -0
- package/dist/src/tui/entity-cards.js.map +1 -0
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +6 -1
- package/dist/src/tui/index.js +5 -1
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +9 -0
- package/dist/src/tui/input-display.js +58 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +36 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +224 -31
- package/dist/src/tui/model.js +1510 -397
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +8 -0
- package/dist/src/tui/operation-diagnostic-screens.js +71 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +81 -0
- package/dist/src/tui/operation-history.js +136 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.d.ts +21 -0
- package/dist/src/tui/operation-review-planner.js +1392 -0
- package/dist/src/tui/operation-review-planner.js.map +1 -0
- package/dist/src/tui/plugin-content.d.ts +31 -0
- package/dist/src/tui/plugin-content.js +680 -0
- package/dist/src/tui/plugin-content.js.map +1 -0
- package/dist/src/tui/plugin-detail.d.ts +52 -0
- package/dist/src/tui/plugin-detail.js +165 -0
- package/dist/src/tui/plugin-detail.js.map +1 -0
- package/dist/src/tui/plugin-write-task.d.ts +69 -0
- package/dist/src/tui/plugin-write-task.js +286 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -0
- package/dist/src/tui/project-inventory.d.ts +68 -0
- package/dist/src/tui/project-inventory.js +203 -0
- package/dist/src/tui/project-inventory.js.map +1 -0
- package/dist/src/tui/prompt.js +4 -15
- package/dist/src/tui/prompt.js.map +1 -1
- package/dist/src/tui/query-header.d.ts +30 -0
- package/dist/src/tui/query-header.js +40 -0
- package/dist/src/tui/query-header.js.map +1 -0
- package/dist/src/tui/reader.d.ts +37 -0
- package/dist/src/tui/reader.js +47 -0
- package/dist/src/tui/reader.js.map +1 -0
- package/dist/src/tui/router.d.ts +197 -10
- package/dist/src/tui/router.js +338 -50
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +701 -0
- package/dist/src/tui/screen-registry.js +230 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +45 -0
- package/dist/src/tui/session-host.js +209 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/tui/source-detail-card.d.ts +29 -0
- package/dist/src/tui/source-detail-card.js +45 -0
- package/dist/src/tui/source-detail-card.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 +12 -0
- package/dist/src/tui/theme.js +12 -0
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +1 -0
- package/dist/src/tui/viewport.js +23 -0
- package/dist/src/tui/viewport.js.map +1 -1
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/tui/wizard.js +37 -13
- package/dist/src/tui/wizard.js.map +1 -1
- package/dist/src/types.d.ts +264 -21
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.d.ts +7 -1
- package/dist/src/utils/agent-version.js +36 -7
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.d.ts +8 -6
- package/dist/src/utils/artifact-plan.js +67 -13
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +24 -10
- package/dist/src/utils/artifact-renderer.js +230 -220
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +32 -0
- package/dist/src/utils/artifact-request.js +90 -0
- package/dist/src/utils/artifact-request.js.map +1 -0
- package/dist/src/utils/artifact-sources.d.ts +4 -0
- package/dist/src/utils/artifact-sources.js +17 -0
- package/dist/src/utils/artifact-sources.js.map +1 -1
- package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
- package/dist/src/utils/aw-home-mutation-lock.js +32 -4
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
- package/dist/src/utils/bundle-integrity.d.ts +0 -1
- package/dist/src/utils/bundle-integrity.js +0 -25
- package/dist/src/utils/bundle-integrity.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +29 -29
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-digests.d.ts +10 -0
- package/dist/src/utils/catalog-digests.js +57 -20
- package/dist/src/utils/catalog-digests.js.map +1 -1
- 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 +44 -0
- package/dist/src/utils/catalog-generation-resolver.js +273 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +172 -0
- package/dist/src/utils/catalog-generation-service.js +1271 -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 +58 -0
- package/dist/src/utils/catalog-generation-store.js +452 -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-v4.js → catalog-manifest-v7.js} +35 -43
- 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 +142 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +28 -8
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
- package/dist/src/utils/catalog-runtime-projection.js +5 -40
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-snapshot.d.ts +5 -3
- package/dist/src/utils/catalog-snapshot.js +11 -3
- package/dist/src/utils/catalog-snapshot.js.map +1 -1
- package/dist/src/utils/catalog-source-identity.d.ts +1 -0
- package/dist/src/utils/catalog-source-identity.js +21 -2
- package/dist/src/utils/catalog-source-identity.js.map +1 -1
- package/dist/src/utils/catalog-source-impact.d.ts +21 -0
- package/dist/src/utils/catalog-source-impact.js +344 -0
- package/dist/src/utils/catalog-source-impact.js.map +1 -0
- package/dist/src/utils/catalog-source-references.d.ts +3 -2
- package/dist/src/utils/catalog-source-references.js +195 -347
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +15 -1
- package/dist/src/utils/catalog-sources.js +206 -27
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/claude-skills-root.d.ts +8 -0
- package/dist/src/utils/claude-skills-root.js +76 -0
- package/dist/src/utils/claude-skills-root.js.map +1 -0
- package/dist/src/utils/codex-config.d.ts +2 -0
- package/dist/src/utils/codex-config.js +26 -5
- package/dist/src/utils/codex-config.js.map +1 -1
- package/dist/src/utils/codex-manager.d.ts +79 -0
- package/dist/src/utils/codex-manager.js +735 -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 +49 -0
- package/dist/src/utils/codex-native-observation.js +154 -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 +126 -0
- package/dist/src/utils/codex-reconciler.js +298 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +43 -0
- package/dist/src/utils/codex-shared-native.js +81 -0
- package/dist/src/utils/codex-shared-native.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +2 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -1
- package/dist/src/utils/codex-skills-root.d.ts +4 -0
- package/dist/src/utils/codex-skills-root.js +25 -0
- package/dist/src/utils/codex-skills-root.js.map +1 -0
- package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
- package/dist/src/utils/codex-surface-analysis.js +247 -0
- package/dist/src/utils/codex-surface-analysis.js.map +1 -0
- package/dist/src/utils/dynamic-skill-tree.d.ts +5 -0
- package/dist/src/utils/dynamic-skill-tree.js +56 -7
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +16 -0
- package/dist/src/utils/git-catalog-provider.js +177 -145
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +27 -1
- package/dist/src/utils/local-marketplace.js +434 -84
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/managed-command-closure.d.ts +3 -0
- package/dist/src/utils/managed-command-closure.js +35 -0
- package/dist/src/utils/managed-command-closure.js.map +1 -0
- package/dist/src/utils/managed-command-evidence.d.ts +7 -0
- package/dist/src/utils/managed-command-evidence.js +86 -0
- package/dist/src/utils/managed-command-evidence.js.map +1 -0
- package/dist/src/utils/managed-command-materialization.d.ts +115 -0
- package/dist/src/utils/managed-command-materialization.js +359 -0
- package/dist/src/utils/managed-command-materialization.js.map +1 -0
- package/dist/src/utils/managed-command-runtime.d.ts +33 -0
- package/dist/src/utils/managed-command-runtime.js +121 -0
- package/dist/src/utils/managed-command-runtime.js.map +1 -0
- package/dist/src/utils/managed-command-store.d.ts +128 -0
- package/dist/src/utils/managed-command-store.js +589 -0
- package/dist/src/utils/managed-command-store.js.map +1 -0
- package/dist/src/utils/manifest.d.ts +1 -3
- package/dist/src/utils/manifest.js +1 -3
- package/dist/src/utils/manifest.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.d.ts +3 -9
- package/dist/src/utils/multi-source-catalog.js +54 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +61 -7
- package/dist/src/utils/native-cli.js +426 -58
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/native-tool-context.d.ts +14 -0
- package/dist/src/utils/native-tool-context.js +38 -0
- package/dist/src/utils/native-tool-context.js.map +1 -0
- package/dist/src/utils/native-tool-review.d.ts +21 -0
- package/dist/src/utils/native-tool-review.js +102 -0
- package/dist/src/utils/native-tool-review.js.map +1 -0
- package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +88 -20
- package/dist/src/utils/ownership.js +185 -223
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.d.ts +1 -1
- package/dist/src/utils/path-catalog-provider.js +54 -65
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/paths.d.ts +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +40 -0
- package/dist/src/utils/plugin-config-impact.js +373 -0
- package/dist/src/utils/plugin-config-impact.js.map +1 -0
- package/dist/src/utils/plugin-config-write.d.ts +52 -27
- package/dist/src/utils/plugin-config-write.js +1112 -298
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +153 -93
- package/dist/src/utils/plugin-config.js +1116 -968
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +49 -0
- package/dist/src/utils/plugin-intent-state.js +320 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +66 -125
- package/dist/src/utils/plugin-scripts.js +518 -526
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-lock.js +14 -5
- package/dist/src/utils/process-lock.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -13
- package/dist/src/utils/project-boundary.js +28 -34
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/rendered-contract.js +18 -1
- package/dist/src/utils/rendered-contract.js.map +1 -1
- package/dist/src/utils/runtime-config.d.ts +20 -0
- package/dist/src/utils/runtime-config.js +75 -0
- package/dist/src/utils/runtime-config.js.map +1 -0
- package/dist/src/utils/runtime-skill-closure.js +47 -6
- package/dist/src/utils/runtime-skill-closure.js.map +1 -1
- 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/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +4 -0
- package/dist/src/utils/sensitive-content.js +36 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- package/dist/src/utils/state-paths.js +7 -2
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +8 -2
- package/dist/src/utils/target-eligibility.js +17 -8
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/dist/src/utils/windows-claude-shim.d.ts +7 -0
- package/dist/src/utils/windows-claude-shim.js +10 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/src/utils/windows-npm-shim.d.ts +14 -0
- package/dist/src/utils/windows-npm-shim.js +140 -0
- package/dist/src/utils/windows-npm-shim.js.map +1 -0
- package/dist/vitest.config.js +100 -9
- package/dist/vitest.config.js.map +1 -1
- package/package.json +31 -21
- package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
- package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
- package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
- package/dist/scripts/aw-skill-eval-engine.js +0 -31
- package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
- package/dist/scripts/codex-eval-app-server.d.ts +0 -89
- package/dist/scripts/codex-eval-app-server.js +0 -1228
- package/dist/scripts/codex-eval-app-server.js.map +0 -1
- package/dist/scripts/codex-eval-isolation.d.ts +0 -61
- package/dist/scripts/codex-eval-isolation.js +0 -318
- package/dist/scripts/codex-eval-isolation.js.map +0 -1
- package/dist/scripts/codex-eval-process-management.d.ts +0 -8
- package/dist/scripts/codex-eval-process-management.js +0 -69
- package/dist/scripts/codex-eval-process-management.js.map +0 -1
- package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
- package/dist/scripts/codex-eval-process-tree.js +0 -803
- package/dist/scripts/codex-eval-process-tree.js.map +0 -1
- package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
- package/dist/scripts/codex-eval-runtime-config.js +0 -843
- package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
- package/dist/scripts/codex-trigger-eval.d.ts +0 -84
- package/dist/scripts/codex-trigger-eval.js +0 -968
- package/dist/scripts/codex-trigger-eval.js.map +0 -1
- package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
- package/dist/scripts/eval-plugin-skills-audited.js +0 -261
- package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
- package/dist/scripts/eval-plugin-skills.d.ts +0 -45
- package/dist/scripts/eval-plugin-skills.js +0 -1586
- package/dist/scripts/eval-plugin-skills.js.map +0 -1
- package/dist/scripts/eval-run-layout.d.ts +0 -17
- package/dist/scripts/eval-run-layout.js +0 -26
- package/dist/scripts/eval-run-layout.js.map +0 -1
- package/dist/scripts/plugin-eval-contract.d.ts +0 -185
- package/dist/scripts/plugin-eval-contract.js +0 -695
- package/dist/scripts/plugin-eval-contract.js.map +0 -1
- package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
- package/dist/scripts/plugin-eval-materializer.js +0 -421
- package/dist/scripts/plugin-eval-materializer.js.map +0 -1
- package/dist/scripts/skill-reporting.d.ts +0 -212
- package/dist/scripts/skill-reporting.js +0 -922
- package/dist/scripts/skill-reporting.js.map +0 -1
- package/dist/src/commands/review-publication.d.ts +0 -99
- package/dist/src/commands/review-publication.js +0 -904
- package/dist/src/commands/review-publication.js.map +0 -1
- package/dist/src/commands/status-v4.d.ts +0 -123
- package/dist/src/commands/status-v4.js +0 -905
- package/dist/src/commands/status-v4.js.map +0 -1
- package/dist/src/commands/status.d.ts +0 -93
- package/dist/src/commands/status.js +0 -442
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
- package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
- package/dist/src/utils/catalog-manifest-v3.js +0 -3
- package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
- package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
- package/dist/src/utils/catalog-source-cache.d.ts +0 -62
- package/dist/src/utils/catalog-source-cache.js +0 -296
- package/dist/src/utils/catalog-source-cache.js.map +0 -1
- package/dist/src/utils/codex-project-surface.d.ts +0 -3
- package/dist/src/utils/codex-project-surface.js +0 -49
- package/dist/src/utils/codex-project-surface.js.map +0 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
- package/dist/src/utils/lifecycle-active-store.js +0 -1276
- package/dist/src/utils/lifecycle-active-store.js.map +0 -1
- package/dist/src/utils/plugin-release.d.ts +0 -2
- package/dist/src/utils/plugin-release.js +0 -2
- package/dist/src/utils/plugin-release.js.map +0 -1
|
@@ -1,34 +1,84 @@
|
|
|
1
|
+
import { getPluginConfigReferenceChoices } from '@lesliechan721/aw-plugin-core';
|
|
2
|
+
import { configReferenceKey, configReferenceScalars } from './config-references.js';
|
|
1
3
|
import crypto from 'node:crypto';
|
|
2
|
-
import
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { flattenStatusTargets, statusCommandV8, statusTargetActivation, statusTargetIntent, statusTargetOwnershipState, } from '../commands/status-v6.js';
|
|
3
6
|
import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
|
|
4
|
-
import { cleanupWriteCommand, installWriteCommand, reinstallWriteCommand,
|
|
7
|
+
import { cleanupWriteCommand, failedWriteCommandResult, installWriteCommand, mergeWriteCommandResults, reinstallWriteCommand, syncWriteCommand, taskWriteCommand, uninstallWriteCommand, } from '../commands/write-commands.js';
|
|
8
|
+
import { resolveSyncIntentSelection } from '../commands/sync-command.js';
|
|
9
|
+
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, } from '../commands/options.js';
|
|
5
10
|
import { readCliPackageInfo } from '../utils/local-marketplace.js';
|
|
6
|
-
import {
|
|
11
|
+
import { createCodexNativeObservationSession, } from '../utils/codex-native-observation.js';
|
|
12
|
+
import { prepareNativeCliExecution } from '../utils/native-cli.js';
|
|
13
|
+
import { readCatalogSourcesConfigSnapshot, recoverInvalidCatalogSourcesConfig, } from '../utils/catalog-sources.js';
|
|
7
14
|
import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
|
|
8
|
-
import { coercePluginConfigUpdates,
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
15
|
+
import { coercePluginConfigUpdates, inspectPluginConfigResetTarget, listNamespacedPluginConfigStoreIdentities, readPluginConfigStore, resolveConfigRead, resolveEffectivePluginConfig, resolveEffectivePluginConfigFromStores, resolveAwProjectRoot, resolvePluginConfigStorePath, } from '../utils/plugin-config.js';
|
|
16
|
+
import { assertProjectSharedConfigPathVisible } from '../utils/project-boundary.js';
|
|
17
|
+
import { calculatePluginIntentDigest, readPluginIntentState, } from '../utils/plugin-intent-state.js';
|
|
18
|
+
import { applyPluginConfigWrite, replayConfigRuntimeWrites, resolveConfigPreApplyAuthorization, resetPluginConfigWithHelper, } from '../utils/plugin-config-write.js';
|
|
19
|
+
import { analyzePluginConfigMutationImpact, pluginSharedNativeConfigTargets } from '../utils/plugin-config-impact.js';
|
|
20
|
+
import { discoverInstalledPluginManagedCommandPackages } from '../utils/managed-command-materialization.js';
|
|
21
|
+
import { createBatchIntentCompatiblePlanFingerprint } from '../commands/operation-plan.js';
|
|
22
|
+
import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
|
|
23
|
+
import { operationEvidenceMatches, operationEvidenceFingerprint, TuiConfigInvalidStoreError, TuiController, TuiOperationBlockedError, } from './controller.js';
|
|
24
|
+
import { buildConfigGroups, buildConfigPage, buildTuiViewModel, hasActualScopeEvidence, isDirectCodexVersionUpgrade, projectPlatformPlacement, } from './model.js';
|
|
25
|
+
import { buildPluginWriteTaskCandidates, buildPluginWriteTaskDraft, evaluatePluginWriteTaskRestoreVersion, pluginWriteTaskTargetKey, } from './plugin-write-task.js';
|
|
26
|
+
import { projectExecutionDiagnostics, projectThrownOperationDiagnostic } from './diagnostic-projection.js';
|
|
12
27
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { resolvePluginsFromCatalog } from '../utils/manifest.js';
|
|
28
|
+
import { calculateCatalogSourceConfigFingerprint, calculateCatalogSourceIdentity, } from '../utils/catalog-source-identity.js';
|
|
29
|
+
import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.js';
|
|
16
30
|
import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
|
|
17
|
-
import {
|
|
31
|
+
import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
|
|
32
|
+
import { calculateCatalogGenerationSetDigest, resolveActiveCatalogGeneration, } from '../utils/catalog-generation-resolver.js';
|
|
33
|
+
import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
|
|
34
|
+
import { createCatalogStoreKey, readCatalogStoreEntry, } from '../utils/catalog-generation-store.js';
|
|
35
|
+
import { activateCatalogSourceGeneration, compareCatalogGenerationFreshness, probeCatalogSourceFreshness, probeCatalogSourceGeneration, probeCatalogSourceRepairGeneration, recoverCatalogGenerationStateAndActivate, repairCatalogSourceGeneration, replaceDamagedCatalogSourceGeneration, syncCatalogSourceGeneration, } from '../utils/catalog-generation-service.js';
|
|
36
|
+
import { calculateCatalogSourceImpactDigest, inspectCatalogSourceImpact, } from '../utils/catalog-source-impact.js';
|
|
37
|
+
import { readCatalogGenerationState } from '../utils/catalog-generation-state.js';
|
|
38
|
+
import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
|
|
39
|
+
import { CatalogProviderCompatibilityError } from '../utils/catalog-public-error.js';
|
|
40
|
+
import { loadCatalogGenerationViewModels } from './catalog-generation.js';
|
|
41
|
+
import { executePortableRetry, installPortableSkills, preparePortableInstall, preparePortableRetry, PortableCommandError, recheckPortableInstall, } from '../portable/commands.js';
|
|
42
|
+
import { MANAGED_CODEX_PACKAGE_NAME, MANAGED_CODEX_VERSION, } from '../utils/codex-manager.js';
|
|
43
|
+
import { projectPluginContentOverview, } from './plugin-content.js';
|
|
44
|
+
import { executeCatalogStoreTask, inspectCatalogStoreTask, prepareCatalogStoreTask, } from './catalog-store-task.js';
|
|
45
|
+
function reusableCandidateEvidenceIsComplete(candidate) {
|
|
46
|
+
const evidence = candidate.probe.candidate;
|
|
47
|
+
return [
|
|
48
|
+
evidence.sourceConfigFingerprint,
|
|
49
|
+
evidence.sourceIdentity,
|
|
50
|
+
evidence.logicalIdentity,
|
|
51
|
+
evidence.requestedRef,
|
|
52
|
+
evidence.resolvedCommit,
|
|
53
|
+
evidence.catalogVersion,
|
|
54
|
+
evidence.channel,
|
|
55
|
+
evidence.manifestDigest,
|
|
56
|
+
evidence.snapshotDigest,
|
|
57
|
+
].every((value) => typeof value === 'string' && value.length > 0)
|
|
58
|
+
&& typeof candidate.probe.manifest === 'object'
|
|
59
|
+
&& candidate.probe.manifest !== null;
|
|
60
|
+
}
|
|
18
61
|
async function loadPortablePluginIds(options) {
|
|
19
62
|
return withResolvedMultiSourceCatalog({
|
|
20
63
|
cliVersion: options.cliVersion,
|
|
21
|
-
freshnessPolicy: 'cacheFirst',
|
|
22
64
|
env: options.context.env,
|
|
23
65
|
signal: options.signal,
|
|
66
|
+
...(options.catalogOperationContext
|
|
67
|
+
? { operationContext: options.catalogOperationContext }
|
|
68
|
+
: {}),
|
|
24
69
|
consume: async (catalog) => (await Promise.all(catalog.sources.flatMap((item) => item.resolution
|
|
25
|
-
?
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
70
|
+
? item.resolution.manifest.entries.flatMap((entry) => {
|
|
71
|
+
const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
|
|
72
|
+
return support.supported
|
|
73
|
+
? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => plugin.manifest.distributions?.agentSkills
|
|
74
|
+
? formatPluginId({
|
|
75
|
+
sourceId: item.resolution.sourceId,
|
|
76
|
+
pluginName: entry.name,
|
|
77
|
+
})
|
|
78
|
+
: null)]
|
|
79
|
+
: [];
|
|
80
|
+
})
|
|
81
|
+
: []))).filter((pluginId) => pluginId !== null),
|
|
32
82
|
});
|
|
33
83
|
}
|
|
34
84
|
let tuiControllerRuntimeTestHooks;
|
|
@@ -46,9 +96,14 @@ export function statusTargetFingerprint(targets, revisions = []) {
|
|
|
46
96
|
platform: target.platform,
|
|
47
97
|
scope: target.scope,
|
|
48
98
|
state: target.state,
|
|
49
|
-
errors: target.
|
|
99
|
+
errors: target.reasons.map((error) => error.code).sort(),
|
|
100
|
+
sharedReasons: [...(target.sharedReasons ?? [])]
|
|
101
|
+
.sort((left, right) => `${left.code}\0${left.message}`.localeCompare(`${right.code}\0${right.message}`, 'en')),
|
|
50
102
|
components: target.components.map((component) => [component.key, component.state])
|
|
51
103
|
.sort(([left], [right]) => left.localeCompare(right, 'en')),
|
|
104
|
+
sharedComponents: (target.sharedComponents ?? [])
|
|
105
|
+
.map((component) => [component.key, component.state, component.metadata])
|
|
106
|
+
.sort(([left], [right]) => left.localeCompare(right, 'en')),
|
|
52
107
|
})).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
|
|
53
108
|
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
|
|
54
109
|
revisions: revisions.map((entry) => ({
|
|
@@ -60,24 +115,121 @@ export function statusTargetFingerprint(targets, revisions = []) {
|
|
|
60
115
|
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
|
|
61
116
|
});
|
|
62
117
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
118
|
+
export function configAffectedStatusTargets(targets, pluginId, configScope) {
|
|
119
|
+
const affected = new Map();
|
|
120
|
+
for (const target of flattenStatusTargets(targets)) {
|
|
121
|
+
const intent = statusTargetIntent(target);
|
|
122
|
+
if (target.pluginId !== pluginId || !intent.enabled)
|
|
123
|
+
continue;
|
|
124
|
+
for (const scope of intent.scopes) {
|
|
125
|
+
if (configScope === 'project' && scope !== 'project')
|
|
126
|
+
continue;
|
|
127
|
+
affected.set(`${target.platform}\0${scope}`, { ...target, scope });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [...affected.values()]
|
|
131
|
+
.sort((left, right) => `${left.platform}\0${left.scope}`
|
|
132
|
+
.localeCompare(`${right.platform}\0${right.scope}`, 'en'));
|
|
133
|
+
}
|
|
134
|
+
function configRetrySharedNativeRenderTargets(plugin) {
|
|
135
|
+
return pluginSharedNativeConfigTargets(plugin);
|
|
136
|
+
}
|
|
137
|
+
function configImpactVisibleTargets(pluginName, targets) {
|
|
138
|
+
return targets.map((target) => ({
|
|
139
|
+
pluginName,
|
|
140
|
+
platform: target.platform,
|
|
141
|
+
scope: target.scope,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
function configIntentRequestsApply(intent) {
|
|
145
|
+
if (!intent.apply)
|
|
146
|
+
return false;
|
|
147
|
+
if (intent.retryApply === true)
|
|
148
|
+
return true;
|
|
149
|
+
if (intent.changes)
|
|
150
|
+
return intent.changes.length > 0;
|
|
151
|
+
return Object.keys(intent.updates ?? {}).length > 0
|
|
152
|
+
|| (intent.removeKeys?.length ?? 0) > 0;
|
|
153
|
+
}
|
|
154
|
+
function configSaveBatchGroupIntent(intent, group) {
|
|
155
|
+
return {
|
|
156
|
+
kind: 'configSave',
|
|
157
|
+
pluginId: intent.pluginId,
|
|
158
|
+
scope: group.scope,
|
|
159
|
+
apply: intent.apply,
|
|
160
|
+
...(intent.scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs: intent.scriptTimeoutMs }),
|
|
161
|
+
...(group.updates ? { updates: group.updates } : {}),
|
|
162
|
+
...(group.removeKeys ? { removeKeys: group.removeKeys } : {}),
|
|
163
|
+
...(group.sensitiveKeys ? { sensitiveKeys: group.sensitiveKeys } : {}),
|
|
164
|
+
...(group.privateKeys ? { privateKeys: group.privateKeys } : {}),
|
|
165
|
+
...(group.changes ? { changes: group.changes } : {}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function assertConfigSaveBatchIntent(intent) {
|
|
169
|
+
const order = { project: 0, user: 1 };
|
|
170
|
+
if (intent.groups.length === 0 || intent.groups.length > 2
|
|
171
|
+
|| new Set(intent.groups.map((group) => group.scope)).size !== intent.groups.length
|
|
172
|
+
|| intent.groups.some((group, index) => index > 0
|
|
173
|
+
&& order[intent.groups[index - 1].scope] >= order[group.scope])
|
|
174
|
+
|| intent.groups.some((group) => Object.keys(group.updates ?? {}).length === 0
|
|
175
|
+
&& (group.removeKeys?.length ?? 0) === 0)) {
|
|
176
|
+
throw new TuiOperationBlockedError('configBatchInvalid', '设置批次必须包含按当前项目、个人默认排序的唯一非空目标。');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function operationReviewStale(message, diagnostics) {
|
|
180
|
+
return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`, diagnostics);
|
|
65
181
|
}
|
|
66
182
|
function sourceEvidenceForPlugin(source, pluginName) {
|
|
67
183
|
const entry = source.manifest.entries.find((candidate) => candidate.name === pluginName);
|
|
68
184
|
return {
|
|
69
185
|
sourceId: source.sourceId,
|
|
70
186
|
sourceIdentity: source.sourceIdentity,
|
|
187
|
+
logicalIdentity: source.logicalIdentity,
|
|
71
188
|
requestedRef: source.requestedRef,
|
|
72
189
|
resolvedCommit: source.resolvedCommit,
|
|
73
190
|
manifestDigest: source.manifestDigest,
|
|
74
191
|
snapshotDigest: source.snapshotDigest,
|
|
75
192
|
contentDigest: entry.contentDigest,
|
|
193
|
+
pluginVersion: entry.version,
|
|
194
|
+
catalogVersion: source.manifest.catalogVersion,
|
|
195
|
+
channel: source.manifest.channel,
|
|
76
196
|
};
|
|
77
197
|
}
|
|
198
|
+
async function configLayerStoreDigest(options) {
|
|
199
|
+
const store = await readPluginConfigStore({
|
|
200
|
+
sourceId: options.source.sourceId,
|
|
201
|
+
sourceIdentity: options.source.sourceIdentity,
|
|
202
|
+
...('logicalIdentity' in options.source
|
|
203
|
+
? { logicalIdentity: options.source.logicalIdentity }
|
|
204
|
+
: {}),
|
|
205
|
+
pluginName: options.pluginName,
|
|
206
|
+
scope: options.scope,
|
|
207
|
+
cwd: options.cwd,
|
|
208
|
+
env: options.env,
|
|
209
|
+
});
|
|
210
|
+
return digest({ exists: store.exists, rawDigest: store.rawDigest });
|
|
211
|
+
}
|
|
212
|
+
async function inspectConfigShareability(options) {
|
|
213
|
+
if (options.scope !== 'project')
|
|
214
|
+
return undefined;
|
|
215
|
+
const targetPath = resolvePluginConfigStorePath({
|
|
216
|
+
sourceId: options.sourceId,
|
|
217
|
+
pluginName: options.pluginName,
|
|
218
|
+
scope: 'project',
|
|
219
|
+
cwd: options.cwd,
|
|
220
|
+
env: options.env,
|
|
221
|
+
});
|
|
222
|
+
const visibility = await assertProjectSharedConfigPathVisible({
|
|
223
|
+
cwd: options.cwd,
|
|
224
|
+
targetPath,
|
|
225
|
+
env: options.env,
|
|
226
|
+
});
|
|
227
|
+
return visibility.state === 'unverified' ? 'unverified' : 'verified';
|
|
228
|
+
}
|
|
78
229
|
function assertSourceEvidenceMatches(expected, actual) {
|
|
79
230
|
if (actual.sourceId !== expected.sourceId
|
|
80
231
|
|| actual.sourceIdentity !== expected.sourceIdentity
|
|
232
|
+
|| actual.logicalIdentity !== expected.logicalIdentity
|
|
81
233
|
|| actual.resolvedCommit !== expected.resolvedCommit
|
|
82
234
|
|| actual.manifestDigest !== expected.manifestDigest
|
|
83
235
|
|| actual.snapshotDigest !== expected.snapshotDigest
|
|
@@ -85,142 +237,194 @@ function assertSourceEvidenceMatches(expected, actual) {
|
|
|
85
237
|
throw operationReviewStale('确认后来源内容发生变化。');
|
|
86
238
|
}
|
|
87
239
|
}
|
|
240
|
+
function writeReviewIsStale(result) {
|
|
241
|
+
return result.targetResults.some((target) => target.errorCode === 'operationReviewStale'
|
|
242
|
+
|| target.errorCode === 'operationPlanStale');
|
|
243
|
+
}
|
|
88
244
|
function throwIfWriteReviewStale(result) {
|
|
89
|
-
|
|
90
|
-
if (stale) {
|
|
245
|
+
if (writeReviewIsStale(result)) {
|
|
91
246
|
throw operationReviewStale('确认后实际写入计划发生变化。');
|
|
92
247
|
}
|
|
93
248
|
}
|
|
94
249
|
const SOURCE_PROGRESS_PHASES = {
|
|
95
|
-
refreshingCatalog: '刷新 Catalog 缓存',
|
|
96
|
-
catalogReady: 'Catalog 已读取,索引已刷新',
|
|
97
250
|
savingConfig: '保存来源配置',
|
|
98
251
|
};
|
|
99
252
|
function pluginIntent(intent) {
|
|
100
253
|
return 'pluginId' in intent;
|
|
101
254
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
255
|
+
function reviewedScriptTimeoutMs(intent) {
|
|
256
|
+
if (intent.kind === 'pluginWriteTask') {
|
|
257
|
+
return parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
|
|
258
|
+
}
|
|
259
|
+
if ((intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch')
|
|
260
|
+
&& (intent.action === 'install' || intent.action === 'refreshAndSync'
|
|
261
|
+
|| intent.action === 'reinstall' || intent.action === 'sync')) {
|
|
262
|
+
return parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
|
|
263
|
+
}
|
|
264
|
+
if ((intent.kind === 'configSave' || intent.kind === 'configSaveBatch') && intent.apply) {
|
|
265
|
+
return parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
function selectedBatchGroups(intent) {
|
|
270
|
+
if (intent.selection.kind !== 'selected' && intent.selection.kind !== 'targets') {
|
|
271
|
+
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部同步必须从项目 intent roots 解析。');
|
|
272
|
+
}
|
|
273
|
+
const rawTargets = intent.selection.kind === 'targets'
|
|
274
|
+
? intent.selection.targets
|
|
275
|
+
: intent.selection.pluginIds.map((pluginId) => ({
|
|
276
|
+
pluginId, platform: intent.platform, scope: intent.scope,
|
|
277
|
+
}));
|
|
278
|
+
const normalizedTargets = rawTargets.map((target) => {
|
|
279
|
+
if ((target.platform !== 'codex' && target.platform !== 'claude' && target.platform !== 'both')
|
|
280
|
+
|| (target.scope !== 'project' && target.scope !== 'user')
|
|
281
|
+
|| (intent.selection.kind === 'targets' && target.platform === 'both')) {
|
|
282
|
+
throw new TuiOperationBlockedError('batchTargetInvalid', '批量精确目标必须使用有效 scope 和单个平台。');
|
|
126
283
|
}
|
|
127
|
-
|
|
128
|
-
|
|
284
|
+
const pluginId = target.pluginId;
|
|
285
|
+
const parsed = parsePluginId(pluginId);
|
|
286
|
+
if (!parsed.sourceId) {
|
|
287
|
+
throw new TuiOperationBlockedError('invalidPluginId', '批量操作必须使用 canonical pluginId。');
|
|
129
288
|
}
|
|
130
|
-
|
|
131
|
-
|
|
289
|
+
const canonicalPluginId = formatPluginId({ sourceId: parsed.sourceId, pluginName: parsed.pluginName });
|
|
290
|
+
if (intent.selection.kind === 'targets' && canonicalPluginId !== pluginId) {
|
|
291
|
+
throw new TuiOperationBlockedError('invalidPluginId', '批量精确目标必须使用 canonical pluginId。');
|
|
132
292
|
}
|
|
293
|
+
return {
|
|
294
|
+
pluginId: canonicalPluginId,
|
|
295
|
+
platform: target.platform,
|
|
296
|
+
scope: target.scope,
|
|
297
|
+
};
|
|
298
|
+
}).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
|
|
299
|
+
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en'));
|
|
300
|
+
const uniqueTargets = [...new Map(normalizedTargets.map((target) => [
|
|
301
|
+
[target.pluginId, target.platform, target.scope].join('\0'), target,
|
|
302
|
+
])).values()];
|
|
303
|
+
const requestedPluginIds = [...new Set(uniqueTargets.map((target) => target.pluginId))];
|
|
304
|
+
if (requestedPluginIds.length === 0) {
|
|
305
|
+
throw new TuiOperationBlockedError('syncSelectionRequired', '批量操作至少需要一个插件。');
|
|
306
|
+
}
|
|
307
|
+
const groups = new Map();
|
|
308
|
+
for (const target of uniqueTargets) {
|
|
309
|
+
const pluginId = target.pluginId;
|
|
310
|
+
const sourceId = parsePluginId(pluginId).sourceId;
|
|
311
|
+
const groupKey = intent.action === 'cleanup'
|
|
312
|
+
? ['cleanup', target.platform, target.scope].join('\0')
|
|
313
|
+
: [sourceId, target.platform, target.scope].join('\0');
|
|
314
|
+
const group = groups.get(groupKey) ?? {
|
|
315
|
+
groupId: groupKey,
|
|
316
|
+
sourceId: intent.action === 'cleanup' ? 'offline' : sourceId,
|
|
317
|
+
platform: target.platform,
|
|
318
|
+
scope: target.scope,
|
|
319
|
+
requestedPluginIds: [],
|
|
320
|
+
};
|
|
321
|
+
group.requestedPluginIds.push(pluginId);
|
|
322
|
+
groups.set(groupKey, group);
|
|
133
323
|
}
|
|
324
|
+
return { requestedPluginIds, groups: [...groups.values()] };
|
|
134
325
|
}
|
|
135
|
-
async function
|
|
136
|
-
if (
|
|
137
|
-
return;
|
|
138
|
-
const stale = (message) => {
|
|
139
|
-
throw new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`);
|
|
140
|
-
};
|
|
141
|
-
const selected = targets.find((target) => target.pluginId === intent.pluginId
|
|
142
|
-
&& target.platform === intent.platform
|
|
143
|
-
&& target.scope === intent.scope);
|
|
144
|
-
if (intent.originIssue.category === 'dependencyMissing') {
|
|
145
|
-
if (intent.originIssue.actionId !== 'installDependency'
|
|
146
|
-
|| intent.action !== 'install' || !selected || selected.state !== 'absent') {
|
|
147
|
-
stale('原缺失依赖问题已变化。');
|
|
148
|
-
}
|
|
326
|
+
async function resolveBatchGroups(intent, env, cwd, frozenRequestedPluginIds) {
|
|
327
|
+
if (intent.selection.kind === 'selected' || intent.selection.kind === 'targets') {
|
|
328
|
+
return selectedBatchGroups(intent);
|
|
149
329
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|| intent.action !== 'reinstall' || !selected
|
|
153
|
-
|| (selected.state !== 'partial' && selected.state !== 'error')) {
|
|
154
|
-
stale('原依赖安装问题已变化。');
|
|
155
|
-
}
|
|
330
|
+
if (intent.action !== 'sync' || intent.scope !== 'project') {
|
|
331
|
+
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部操作只支持 project scope sync。');
|
|
156
332
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
333
|
+
const state = await readPluginIntentState({ scope: 'project', cwd, env });
|
|
334
|
+
const projectIntentDigest = calculatePluginIntentDigest(state);
|
|
335
|
+
if (intent.selection.kind === 'currentProjectIntentTargets') {
|
|
336
|
+
if (intent.selection.targets.length === 0
|
|
337
|
+
|| intent.selection.targets.some((target) => target.scope !== 'project')) {
|
|
338
|
+
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须是非空 project targets。');
|
|
162
339
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
340
|
+
const exact = selectedBatchGroups({
|
|
341
|
+
...intent,
|
|
342
|
+
selection: { kind: 'targets', targets: intent.selection.targets },
|
|
343
|
+
});
|
|
344
|
+
const declaredPlatforms = new Map(state.roots.map((root) => [
|
|
345
|
+
root.pluginId,
|
|
346
|
+
new Set(root.platforms),
|
|
347
|
+
]));
|
|
348
|
+
if (intent.selection.targets.some((target) => !declaredPlatforms.get(target.pluginId)?.has(target.platform))) {
|
|
349
|
+
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须属于完整 project intent roots 声明。');
|
|
169
350
|
}
|
|
170
|
-
return;
|
|
351
|
+
return { ...exact, projectIntentDigest };
|
|
171
352
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
stale('原位置冲突已变化。');
|
|
181
|
-
}
|
|
182
|
-
return;
|
|
353
|
+
const selection = resolveSyncIntentSelection({
|
|
354
|
+
roots: state.roots,
|
|
355
|
+
selectors: frozenRequestedPluginIds ?? [],
|
|
356
|
+
all: frozenRequestedPluginIds === undefined,
|
|
357
|
+
platform: intent.platform,
|
|
358
|
+
});
|
|
359
|
+
if (selection.requestedPluginIds.length === 0) {
|
|
360
|
+
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目没有可同步的 intent roots。');
|
|
183
361
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
362
|
+
return {
|
|
363
|
+
requestedPluginIds: selection.requestedPluginIds,
|
|
364
|
+
projectIntentDigest,
|
|
365
|
+
groups: selection.groups.map((group) => ({
|
|
366
|
+
groupId: `${group.sourceId}\0${group.platform}\0${intent.scope}`,
|
|
367
|
+
sourceId: group.sourceId,
|
|
368
|
+
platform: group.platform,
|
|
369
|
+
scope: intent.scope,
|
|
370
|
+
requestedPluginIds: group.pluginIds,
|
|
371
|
+
})),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
async function runBatchWriteGroup(options) {
|
|
375
|
+
const dependencyScopeOverrides = options.intent.action === 'install'
|
|
376
|
+
? options.intent.dependencyScopeOverrides?.filter((override) => parsePluginId(override.pluginId).sourceId === options.group.sourceId
|
|
377
|
+
&& (options.group.platform === 'both' || override.platform === options.group.platform))
|
|
378
|
+
: undefined;
|
|
379
|
+
const commandOptions = {
|
|
380
|
+
cwd: options.cwd,
|
|
381
|
+
...(dependencyScopeOverrides?.length ? { dependencyScopeOverrides } : {}),
|
|
382
|
+
platform: options.group.platform,
|
|
383
|
+
scope: options.group.scope,
|
|
384
|
+
...(reviewedScriptTimeoutMs(options.intent) === undefined
|
|
385
|
+
? {}
|
|
386
|
+
: { scriptTimeoutMs: reviewedScriptTimeoutMs(options.intent) }),
|
|
387
|
+
};
|
|
388
|
+
switch (options.intent.action) {
|
|
389
|
+
case 'install':
|
|
390
|
+
return installWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
391
|
+
case 'sync':
|
|
392
|
+
return syncWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
393
|
+
case 'reinstall':
|
|
394
|
+
return reinstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
395
|
+
case 'uninstall':
|
|
396
|
+
return uninstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
397
|
+
case 'cleanup':
|
|
398
|
+
return cleanupWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
193
399
|
}
|
|
194
|
-
const consumer = targets.find((target) => target.pluginId === origin.consumerPluginId
|
|
195
|
-
&& target.sourceIdentity === origin.consumerSourceIdentity
|
|
196
|
-
&& target.platform === origin.consumerPlatform
|
|
197
|
-
&& target.scope === origin.consumerScope);
|
|
198
|
-
const consumerError = consumer?.errors.some((error) => error.code === origin.category && error.message.includes(origin.dependencyPluginId));
|
|
199
|
-
if (!consumer || !consumerError)
|
|
200
|
-
stale('原 consumer 依赖问题已消失或变化。');
|
|
201
400
|
}
|
|
202
401
|
async function loadDependencyOverviews(options) {
|
|
203
402
|
const graph = await withResolvedMultiSourceCatalog({
|
|
204
403
|
cliVersion: options.cliVersion,
|
|
205
|
-
freshnessPolicy: 'cacheFirst',
|
|
206
404
|
env: options.context.env,
|
|
207
405
|
signal: options.signal,
|
|
406
|
+
...(options.catalogOperationContext
|
|
407
|
+
? { operationContext: options.catalogOperationContext }
|
|
408
|
+
: {}),
|
|
208
409
|
consume: async (catalog) => ({
|
|
209
|
-
nodes:
|
|
210
|
-
?
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
410
|
+
nodes: catalog.sources.flatMap((item) => item.resolution
|
|
411
|
+
? item.resolution.manifest.entries.map((entry) => ({
|
|
412
|
+
pluginId: formatPluginId({
|
|
413
|
+
sourceId: item.resolution.sourceId,
|
|
414
|
+
pluginName: entry.name,
|
|
415
|
+
}),
|
|
416
|
+
sourceIdentity: item.resolution.sourceIdentity,
|
|
417
|
+
displayName: entry.interface?.displayName ?? entry.name,
|
|
418
|
+
dependencies: entry.dependencies.map((pluginName) => formatPluginId({ sourceId: item.resolution.sourceId, pluginName })),
|
|
419
|
+
}))
|
|
420
|
+
: []),
|
|
218
421
|
sourceErrors: catalog.sources.flatMap((item) => item.error
|
|
219
422
|
? [{ sourceId: item.sourceId, code: 'dependencyGraphInvalid' }]
|
|
220
423
|
: []),
|
|
221
424
|
}),
|
|
222
425
|
});
|
|
223
426
|
const nodes = new Map(graph.nodes.map((node) => [node.pluginId, node]));
|
|
427
|
+
const summaryById = new Map(options.status.pluginSummaries.map((summary) => [summary.pluginId, summary]));
|
|
224
428
|
const displayName = (pluginId) => nodes.get(pluginId)?.displayName
|
|
225
429
|
?? parsePluginId(pluginId).pluginName;
|
|
226
430
|
const reverse = new Map();
|
|
@@ -230,43 +434,61 @@ async function loadDependencyOverviews(options) {
|
|
|
230
434
|
}
|
|
231
435
|
}
|
|
232
436
|
const targetsByPlugin = new Map();
|
|
233
|
-
for (const target of options.status.payload.targets) {
|
|
437
|
+
for (const target of flattenStatusTargets(options.status.payload.targets)) {
|
|
234
438
|
targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
|
|
235
439
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
scope:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
440
|
+
const unsupportedPluginIds = new Set(options.status.pluginSummaries
|
|
441
|
+
.filter((summary) => (summary.missingCapabilities?.length ?? 0) > 0)
|
|
442
|
+
.map((summary) => summary.pluginId));
|
|
443
|
+
const availableReferencesFor = (consumerPluginId, dependencyPluginId) => {
|
|
444
|
+
const consumerSourceId = parsePluginId(consumerPluginId).sourceId;
|
|
445
|
+
const dependencySourceId = parsePluginId(dependencyPluginId).sourceId;
|
|
446
|
+
const consumerNode = nodes.get(consumerPluginId);
|
|
447
|
+
const dependencyNode = nodes.get(dependencyPluginId);
|
|
448
|
+
if (consumerSourceId !== dependencySourceId
|
|
449
|
+
|| !nodes.get(consumerPluginId)?.dependencies.includes(dependencyPluginId))
|
|
450
|
+
return [];
|
|
451
|
+
return (targetsByPlugin.get(consumerPluginId) ?? []).flatMap((consumer) => {
|
|
452
|
+
if (!hasActualScopeEvidence(consumer)
|
|
453
|
+
|| consumer.sourceId !== consumerSourceId
|
|
454
|
+
|| consumer.sourceIdentity !== consumerNode?.sourceIdentity)
|
|
455
|
+
return [];
|
|
456
|
+
return [...consumer.components, ...(consumer.sharedComponents ?? [])].flatMap((component) => {
|
|
457
|
+
if (component.kind !== 'dependency'
|
|
458
|
+
|| component.key !== dependencyPluginId
|
|
459
|
+
|| component.state !== 'ready')
|
|
460
|
+
return [];
|
|
461
|
+
const platform = component.metadata?.platform;
|
|
462
|
+
const scope = component.metadata?.scope;
|
|
463
|
+
const project = component.metadata?.project;
|
|
464
|
+
if ((platform !== 'codex' && platform !== 'claude')
|
|
465
|
+
|| (scope !== 'user' && scope !== 'project')
|
|
466
|
+
|| project !== (scope === 'project' ? 'current' : 'none'))
|
|
467
|
+
return [];
|
|
468
|
+
const dependency = (targetsByPlugin.get(dependencyPluginId) ?? []).find((target) => target.platform === platform
|
|
469
|
+
&& target.scope === scope
|
|
470
|
+
&& target.sourceId === dependencySourceId
|
|
471
|
+
&& target.sourceIdentity === dependencyNode?.sourceIdentity
|
|
472
|
+
&& target.sourceIdentity === consumer.sourceIdentity);
|
|
473
|
+
if (!dependency)
|
|
474
|
+
return [];
|
|
475
|
+
return [{
|
|
476
|
+
consumer: {
|
|
477
|
+
pluginId: consumerPluginId,
|
|
478
|
+
platform: consumer.platform,
|
|
479
|
+
scope: consumer.scope,
|
|
480
|
+
project: consumer.scope === 'project' ? 'current' : null,
|
|
481
|
+
},
|
|
482
|
+
dependency: {
|
|
483
|
+
pluginId: dependencyPluginId,
|
|
484
|
+
platform,
|
|
485
|
+
scope,
|
|
486
|
+
project: scope === 'project' ? 'current' : null,
|
|
487
|
+
},
|
|
488
|
+
}];
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
};
|
|
270
492
|
const placementsFor = (pluginId) => {
|
|
271
493
|
const targets = targetsByPlugin.get(pluginId) ?? [];
|
|
272
494
|
if (targets.length > 0) {
|
|
@@ -279,13 +501,15 @@ async function loadDependencyOverviews(options) {
|
|
|
279
501
|
};
|
|
280
502
|
const aggregateFor = (pluginId, placements) => {
|
|
281
503
|
const sourceId = parsePluginId(pluginId).sourceId;
|
|
282
|
-
if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
|
|
504
|
+
if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
|
|
505
|
+
|| unsupportedPluginIds.has(pluginId))
|
|
283
506
|
return 'unavailable';
|
|
284
507
|
if (placements.length === 0 || placements.every((placement) => placement.state === 'notInstalled')) {
|
|
285
508
|
return 'notInstalled';
|
|
286
509
|
}
|
|
287
510
|
return placements.some((placement) => placement.state === 'scopeConflict'
|
|
288
|
-
|| (placement.state === 'installed'
|
|
511
|
+
|| (placement.state === 'installed'
|
|
512
|
+
&& (placement.health !== 'ready' || placement.blockingReason !== undefined)))
|
|
289
513
|
? 'needsAttention'
|
|
290
514
|
: 'ready';
|
|
291
515
|
};
|
|
@@ -336,7 +560,8 @@ async function loadDependencyOverviews(options) {
|
|
|
336
560
|
})),
|
|
337
561
|
placements,
|
|
338
562
|
aggregateStatus: aggregateFor(dependency.pluginId, placements),
|
|
339
|
-
placementReferences:
|
|
563
|
+
placementReferences: [],
|
|
564
|
+
availablePlacementReferences: availableReferencesFor(pluginId, dependency.pluginId),
|
|
340
565
|
};
|
|
341
566
|
});
|
|
342
567
|
const dependentIds = [...new Set(reverse.get(pluginId) ?? [])].sort();
|
|
@@ -350,15 +575,23 @@ async function loadDependencyOverviews(options) {
|
|
|
350
575
|
requiredBy: [],
|
|
351
576
|
placements,
|
|
352
577
|
aggregateStatus: aggregateFor(dependentId, placements),
|
|
353
|
-
placementReferences:
|
|
578
|
+
placementReferences: [],
|
|
579
|
+
availablePlacementReferences: availableReferencesFor(dependentId, pluginId),
|
|
354
580
|
};
|
|
355
581
|
});
|
|
356
|
-
const targetBlockers = (targetsByPlugin.get(pluginId) ?? []).flatMap((target) => target.
|
|
582
|
+
const targetBlockers = (targetsByPlugin.get(pluginId) ?? []).flatMap((target) => target.reasons.filter((error) => error.code.startsWith('dependency') || error.code === 'sourceChanged')
|
|
357
583
|
.map((error) => ({
|
|
358
584
|
code: error.code,
|
|
359
585
|
title: '依赖关系需要处理',
|
|
360
586
|
explanation: '当前目标的依赖证据不能安全通过检查。',
|
|
361
587
|
})));
|
|
588
|
+
const consumerTargets = summaryById.get(pluginId)?.supportedTargets;
|
|
589
|
+
const unsupportedDependency = dependencyItems.find((dependency) => {
|
|
590
|
+
const dependencyTargets = summaryById.get(dependency.pluginId)?.supportedTargets;
|
|
591
|
+
if (!consumerTargets || !dependencyTargets)
|
|
592
|
+
return true;
|
|
593
|
+
return ['codex', 'claude'].some((platform) => consumerTargets[platform].length > 0 && dependencyTargets[platform].length === 0);
|
|
594
|
+
});
|
|
362
595
|
const sourceId = parsePluginId(pluginId).sourceId;
|
|
363
596
|
const blockers = [
|
|
364
597
|
...graph.sourceErrors.filter((error) => error.sourceId === sourceId).map((error) => ({
|
|
@@ -371,6 +604,11 @@ async function loadDependencyOverviews(options) {
|
|
|
371
604
|
title: '依赖关系无效',
|
|
372
605
|
explanation: '依赖关系形成环。',
|
|
373
606
|
}] : []),
|
|
607
|
+
...(unsupportedDependency ? [{
|
|
608
|
+
code: 'dependencyTargetUnsupported',
|
|
609
|
+
title: '依赖目标不兼容',
|
|
610
|
+
explanation: `${unsupportedDependency.displayName} 不支持根插件所需的平台。`,
|
|
611
|
+
}] : []),
|
|
374
612
|
...targetBlockers,
|
|
375
613
|
];
|
|
376
614
|
const directCount = dependencyItems.filter((item) => item.relation === 'direct').length;
|
|
@@ -418,46 +656,206 @@ function createReviewStageReporter(options) {
|
|
|
418
656
|
};
|
|
419
657
|
}
|
|
420
658
|
export function createTuiControllerDependencies(context, progressFeed, options = {}) {
|
|
659
|
+
const readInventory = options.inventoryCatalogSourceReferences
|
|
660
|
+
?? inventoryCatalogSourceReferences;
|
|
661
|
+
const validatedStoreEntries = new Map();
|
|
662
|
+
const validatingStoreEntries = new Map();
|
|
663
|
+
const loadCatalogGenerations = async () => {
|
|
664
|
+
const generations = await loadCatalogGenerationViewModels({
|
|
665
|
+
env: context.env,
|
|
666
|
+
validatedStoreEntries,
|
|
667
|
+
validatingStoreEntries,
|
|
668
|
+
onStage: (event) => context.debugLogger.log({
|
|
669
|
+
type: 'performance:stage',
|
|
670
|
+
level: 'debug',
|
|
671
|
+
target: 'debugLog',
|
|
672
|
+
message: 'Catalog generation load stage completed.',
|
|
673
|
+
data: event,
|
|
674
|
+
}),
|
|
675
|
+
});
|
|
676
|
+
const retained = new Set(generations.flatMap((source) => [
|
|
677
|
+
source.active?.storeKey,
|
|
678
|
+
].filter((value) => value !== undefined)));
|
|
679
|
+
for (const storeKey of validatedStoreEntries.keys()) {
|
|
680
|
+
if (!retained.has(storeKey))
|
|
681
|
+
validatedStoreEntries.delete(storeKey);
|
|
682
|
+
}
|
|
683
|
+
return generations;
|
|
684
|
+
};
|
|
685
|
+
const createOperationInventoryReader = () => {
|
|
686
|
+
let ordinaryInventory;
|
|
687
|
+
return (inventoryOptions) => {
|
|
688
|
+
ordinaryInventory ??= readInventory(inventoryOptions);
|
|
689
|
+
return ordinaryInventory;
|
|
690
|
+
};
|
|
691
|
+
};
|
|
692
|
+
const catalogOperationByEvidence = new WeakMap();
|
|
693
|
+
const taskCatalogOperationsByEvidence = new WeakMap();
|
|
694
|
+
const codexManagerByEvidence = new WeakMap();
|
|
695
|
+
let sessionCatalogOperationContext;
|
|
696
|
+
let sessionStatusResult;
|
|
697
|
+
const sessionContextForPlugin = (pluginId) => {
|
|
698
|
+
const sourceId = parsePluginId(pluginId).sourceId;
|
|
699
|
+
if (!sourceId || !sessionCatalogOperationContext)
|
|
700
|
+
return undefined;
|
|
701
|
+
return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
|
|
702
|
+
};
|
|
703
|
+
const readConfigStoresForTui = async (config) => {
|
|
704
|
+
const scopes = config.scope === 'user' ? ['user'] : ['user', 'project'];
|
|
705
|
+
const stores = await Promise.all(scopes.map((scope) => readPluginConfigStore({
|
|
706
|
+
sourceId: config.sourceId,
|
|
707
|
+
sourceIdentity: config.sourceIdentity,
|
|
708
|
+
logicalIdentity: config.logicalIdentity,
|
|
709
|
+
pluginName: config.pluginName,
|
|
710
|
+
scope,
|
|
711
|
+
cwd: context.cwd,
|
|
712
|
+
env: context.env,
|
|
713
|
+
schema: config.schema,
|
|
714
|
+
})));
|
|
715
|
+
const user = stores[0];
|
|
716
|
+
const project = config.scope === 'user' ? undefined : stores[1];
|
|
717
|
+
config.signal.throwIfAborted();
|
|
718
|
+
const invalidStores = ([
|
|
719
|
+
{ scope: 'user', store: user },
|
|
720
|
+
...(project ? [{ scope: 'project', store: project }] : []),
|
|
721
|
+
]).flatMap(({ scope, store }) => store.state === 'invalid' ? [{
|
|
722
|
+
scope,
|
|
723
|
+
code: store.invalidCode,
|
|
724
|
+
pathSummary: scope === 'user'
|
|
725
|
+
? `AW_HOME/plugin-config/${config.sourceId ?? '<source>'}/${config.pluginName}/config.json`
|
|
726
|
+
: `<project>/.agent-workflows/plugin-config/${config.sourceId ?? '<source>'}/${config.pluginName}/config.json`,
|
|
727
|
+
}] : []);
|
|
728
|
+
if (invalidStores.length > 0)
|
|
729
|
+
throw new TuiConfigInvalidStoreError(invalidStores);
|
|
730
|
+
return { user, ...(project ? { project } : {}) };
|
|
731
|
+
};
|
|
421
732
|
const loadStatus = async (signal) => {
|
|
422
733
|
signal.throwIfAborted();
|
|
423
734
|
const packageInfo = await readCliPackageInfo();
|
|
735
|
+
const catalogOperationContext = sessionCatalogOperationContext
|
|
736
|
+
?? await resolvePartialCatalogOperationContext({
|
|
737
|
+
env: context.env,
|
|
738
|
+
signal,
|
|
739
|
+
validatedStoreEntries,
|
|
740
|
+
validatingStoreEntries,
|
|
741
|
+
}).catch(async (error) => {
|
|
742
|
+
if (!(error instanceof CatalogGenerationError)
|
|
743
|
+
|| error.code !== 'catalogGenerationStateInvalid')
|
|
744
|
+
return undefined;
|
|
745
|
+
const snapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
746
|
+
const primarySourceId = snapshot.config.sources.find((source) => source.primary).id;
|
|
747
|
+
const generationSetDigest = calculateCatalogGenerationSetDigest(snapshot.digest, primarySourceId, []);
|
|
748
|
+
return {
|
|
749
|
+
generationSet: {
|
|
750
|
+
generationSetDigest,
|
|
751
|
+
configDigest: snapshot.digest,
|
|
752
|
+
primarySourceId,
|
|
753
|
+
sources: [],
|
|
754
|
+
},
|
|
755
|
+
generationSetDigest,
|
|
756
|
+
catalog: {
|
|
757
|
+
sources: snapshot.config.sources.map((source) => ({
|
|
758
|
+
sourceId: source.id,
|
|
759
|
+
error,
|
|
760
|
+
})),
|
|
761
|
+
primarySourceId,
|
|
762
|
+
configDigest: snapshot.digest,
|
|
763
|
+
compositeDigest: generationSetDigest,
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
});
|
|
767
|
+
if (catalogOperationContext)
|
|
768
|
+
sessionCatalogOperationContext = catalogOperationContext;
|
|
424
769
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
425
|
-
const result = await
|
|
770
|
+
const result = await statusCommandV8({
|
|
426
771
|
cwd: context.cwd,
|
|
427
772
|
env: context.env,
|
|
773
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
428
774
|
cliVersion: packageInfo.version,
|
|
429
775
|
verbose: context.diagnostics.verbose,
|
|
776
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
777
|
+
inventoryCatalogSourceReferences: readInventory,
|
|
430
778
|
});
|
|
431
779
|
signal.throwIfAborted();
|
|
432
780
|
const configured = await listCatalogSources({ env: context.env });
|
|
433
781
|
signal.throwIfAborted();
|
|
434
782
|
if (result.payload.catalog.configDigest !== configured.configDigest)
|
|
435
783
|
continue;
|
|
784
|
+
sessionStatusResult = result;
|
|
436
785
|
const dependencyOverviews = await loadDependencyOverviews({
|
|
437
786
|
context,
|
|
438
787
|
cliVersion: packageInfo.version,
|
|
439
788
|
status: result,
|
|
440
789
|
signal,
|
|
790
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
441
791
|
});
|
|
442
792
|
const portablePluginIds = await loadPortablePluginIds({
|
|
443
793
|
context,
|
|
444
794
|
cliVersion: packageInfo.version,
|
|
445
795
|
signal,
|
|
796
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
446
797
|
});
|
|
447
798
|
signal.throwIfAborted();
|
|
799
|
+
const catalogGenerations = await loadCatalogGenerations();
|
|
800
|
+
signal.throwIfAborted();
|
|
801
|
+
const configStoreInventory = (await Promise.all(['user', 'project'].map(async (scope) => {
|
|
802
|
+
try {
|
|
803
|
+
return (await listNamespacedPluginConfigStoreIdentities({
|
|
804
|
+
cwd: context.cwd,
|
|
805
|
+
env: context.env,
|
|
806
|
+
scope,
|
|
807
|
+
})).map(({ pluginId, sourceId, pluginName }) => ({
|
|
808
|
+
pluginId, sourceId, pluginName, scope,
|
|
809
|
+
}));
|
|
810
|
+
}
|
|
811
|
+
catch {
|
|
812
|
+
await context.debugLogger.log({
|
|
813
|
+
type: 'tui:config-inventory-error',
|
|
814
|
+
level: 'debug',
|
|
815
|
+
target: 'debugLog',
|
|
816
|
+
message: 'TUI saved config inventory could not be read.',
|
|
817
|
+
data: { scope },
|
|
818
|
+
});
|
|
819
|
+
return [];
|
|
820
|
+
}
|
|
821
|
+
}))).flat();
|
|
822
|
+
signal.throwIfAborted();
|
|
448
823
|
const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
|
|
824
|
+
const projectIntentObservation = result.projectIntentObservation;
|
|
825
|
+
const projectIntent = projectIntentObservation.state === 'ready' ? {
|
|
826
|
+
state: 'ready',
|
|
827
|
+
roots: structuredClone(projectIntentObservation.value.roots),
|
|
828
|
+
} : projectIntentObservation.state === 'absent' ? {
|
|
829
|
+
state: 'absent',
|
|
830
|
+
} : {
|
|
831
|
+
state: projectIntentObservation.state,
|
|
832
|
+
code: projectIntentObservation.code,
|
|
833
|
+
message: projectIntentObservation.message,
|
|
834
|
+
};
|
|
449
835
|
return {
|
|
836
|
+
cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
|
|
837
|
+
...(result.projectRoot ? { projectRoot: result.projectRoot } : {}),
|
|
838
|
+
projectIntent,
|
|
839
|
+
...(projectIntent.state === 'ready'
|
|
840
|
+
? { projectIntentRoots: structuredClone(projectIntent.roots) }
|
|
841
|
+
: {}),
|
|
842
|
+
configuredSources: structuredClone(configured.sources),
|
|
450
843
|
payload: result.payload,
|
|
451
844
|
pluginSummaries: result.pluginSummaries,
|
|
452
845
|
targetRevisions: result.targetRevisions,
|
|
453
846
|
dependencyOverviews,
|
|
454
847
|
portablePluginIds,
|
|
848
|
+
configStoreInventory,
|
|
849
|
+
catalogGenerations,
|
|
850
|
+
sessionGenerationKeys: Object.fromEntries((catalogOperationContext?.generationSet.sources ?? [])
|
|
851
|
+
.map((source) => [source.sourceId, source.storeKey])),
|
|
455
852
|
sourceDetails: configured.sources.map((source) => {
|
|
456
853
|
const status = statusById.get(source.id);
|
|
457
854
|
return {
|
|
458
855
|
id: source.id,
|
|
459
856
|
type: source.type,
|
|
460
857
|
primary: source.primary,
|
|
858
|
+
autoCheck: source.autoCheck,
|
|
461
859
|
locationSummary: projectCatalogSourceLocation(source.type === 'git' ? source.url : source.path),
|
|
462
860
|
...(source.type === 'git' ? {
|
|
463
861
|
refPolicy: source.refPolicy.type,
|
|
@@ -479,7 +877,477 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
479
877
|
}
|
|
480
878
|
throw new TuiOperationBlockedError('operationReviewStale', '状态读取期间来源配置持续变化。请重试。');
|
|
481
879
|
};
|
|
482
|
-
const
|
|
880
|
+
const loadPluginContent = async (pluginId, signal) => {
|
|
881
|
+
signal.throwIfAborted();
|
|
882
|
+
try {
|
|
883
|
+
const packageInfo = await readCliPackageInfo();
|
|
884
|
+
const catalogOperation = sessionContextForPlugin(pluginId);
|
|
885
|
+
return await withResolvedCatalogPlugin({
|
|
886
|
+
selector: pluginId,
|
|
887
|
+
cliVersion: packageInfo.version,
|
|
888
|
+
env: context.env,
|
|
889
|
+
signal,
|
|
890
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
891
|
+
consume: ({ plugin, source }) => projectPluginContentOverview({
|
|
892
|
+
pluginId,
|
|
893
|
+
catalogVersion: source.manifest.catalogVersion,
|
|
894
|
+
plugin,
|
|
895
|
+
}),
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
catch {
|
|
899
|
+
signal.throwIfAborted();
|
|
900
|
+
const summary = sessionStatusResult?.pluginSummaries.find((entry) => entry.pluginId === pluginId);
|
|
901
|
+
let sourceId;
|
|
902
|
+
try {
|
|
903
|
+
sourceId = parsePluginId(pluginId).sourceId;
|
|
904
|
+
}
|
|
905
|
+
catch {
|
|
906
|
+
// The fixed unavailable result below does not repeat an unsafe selector.
|
|
907
|
+
}
|
|
908
|
+
const catalogVersion = sessionStatusResult?.payload.catalog.sources
|
|
909
|
+
.find((source) => source.id === sourceId)?.catalogVersion;
|
|
910
|
+
return {
|
|
911
|
+
pluginId,
|
|
912
|
+
pluginVersion: summary?.version ?? '待确认',
|
|
913
|
+
catalogVersion: catalogVersion ?? '待确认',
|
|
914
|
+
evidence: 'unavailable',
|
|
915
|
+
items: [],
|
|
916
|
+
notices: ['当前无法安全读取这个插件的目录内容。请检查来源状态后重试。'],
|
|
917
|
+
coverage: 'unavailable',
|
|
918
|
+
coverageIssue: {
|
|
919
|
+
code: 'unreadable',
|
|
920
|
+
message: '当前无法安全读取这个插件的目录内容。请检查来源状态后重试。',
|
|
921
|
+
},
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false, reusableCandidate, codexObservationSession, frozenBatchEvidence) => {
|
|
926
|
+
reviewedScriptTimeoutMs(intent);
|
|
927
|
+
if (intent.kind === 'catalogStorePrune') {
|
|
928
|
+
signal?.throwIfAborted();
|
|
929
|
+
const config = await readCatalogSourcesConfigSnapshot(context.env);
|
|
930
|
+
const catalogStore = await prepareCatalogStoreTask({ env: context.env });
|
|
931
|
+
signal?.throwIfAborted();
|
|
932
|
+
return {
|
|
933
|
+
catalog: { configDigest: config.digest, compositeDigest: config.digest },
|
|
934
|
+
catalogStore,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
if (intent.kind === 'configSaveBatch') {
|
|
938
|
+
assertConfigSaveBatchIntent(intent);
|
|
939
|
+
const groupEvidence = [];
|
|
940
|
+
let batchBase;
|
|
941
|
+
let batchCatalogOperation;
|
|
942
|
+
for (const group of intent.groups) {
|
|
943
|
+
const current = await captureEvidence(configSaveBatchGroupIntent(intent, group), signal, preparation, operationInventory, freshCatalog, reusableCandidate, codexObservationSession);
|
|
944
|
+
if (!current.config || !current.source || !current.targetFingerprint) {
|
|
945
|
+
throw operationReviewStale('设置批次缺少单层证据。');
|
|
946
|
+
}
|
|
947
|
+
if (batchBase && (JSON.stringify(batchBase.catalog) !== JSON.stringify(current.catalog)
|
|
948
|
+
|| JSON.stringify(batchBase.source) !== JSON.stringify(current.source))) {
|
|
949
|
+
throw operationReviewStale('准备设置批次期间 Catalog 或来源发生变化。');
|
|
950
|
+
}
|
|
951
|
+
for (const expected of Object.values(group.editEvidence ?? {})) {
|
|
952
|
+
if (expected.sourceIdentity !== current.source.sourceIdentity
|
|
953
|
+
|| expected.schemaIdentity !== current.config.schemaIdentity) {
|
|
954
|
+
throw operationReviewStale('编辑期间插件来源或设置定义已变化,请重新打开列表。');
|
|
955
|
+
}
|
|
956
|
+
for (const scope of ['user', 'project']) {
|
|
957
|
+
if (expected.storeDigests[scope] === undefined)
|
|
958
|
+
continue;
|
|
959
|
+
const currentDigest = await configLayerStoreDigest({
|
|
960
|
+
source: current.source, pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
961
|
+
scope, cwd: context.cwd, env: context.env,
|
|
962
|
+
});
|
|
963
|
+
if (expected.storeDigests[scope] !== currentDigest) {
|
|
964
|
+
throw operationReviewStale('编辑期间设置已被外部修改,请重新打开列表。');
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
batchBase ??= current;
|
|
969
|
+
batchCatalogOperation ??= catalogOperationByEvidence.get(current);
|
|
970
|
+
groupEvidence.push({
|
|
971
|
+
scope: group.scope,
|
|
972
|
+
config: {
|
|
973
|
+
...current.config,
|
|
974
|
+
storeDigest: await configLayerStoreDigest({
|
|
975
|
+
source: current.source,
|
|
976
|
+
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
977
|
+
scope: group.scope,
|
|
978
|
+
cwd: context.cwd,
|
|
979
|
+
env: context.env,
|
|
980
|
+
}),
|
|
981
|
+
},
|
|
982
|
+
targetFingerprint: current.targetFingerprint,
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
const evidence = {
|
|
986
|
+
catalog: batchBase.catalog,
|
|
987
|
+
source: batchBase.source,
|
|
988
|
+
configBatch: { groups: groupEvidence },
|
|
989
|
+
};
|
|
990
|
+
if (batchCatalogOperation)
|
|
991
|
+
catalogOperationByEvidence.set(evidence, batchCatalogOperation);
|
|
992
|
+
return evidence;
|
|
993
|
+
}
|
|
994
|
+
if ((intent.kind === 'pluginWrite'
|
|
995
|
+
&& (intent.platform === 'codex' || intent.platform === 'both')
|
|
996
|
+
|| intent.kind === 'pluginWriteTask'
|
|
997
|
+
&& intent.targets.some((target) => target.platform === 'codex'))
|
|
998
|
+
&& !codexObservationSession) {
|
|
999
|
+
const observationSession = await createCodexNativeObservationSession({
|
|
1000
|
+
exactCwd: context.cwd,
|
|
1001
|
+
env: context.env,
|
|
1002
|
+
runner: context.nativeCliRunner,
|
|
1003
|
+
});
|
|
1004
|
+
try {
|
|
1005
|
+
return await captureEvidence(intent, signal, preparation, operationInventory, freshCatalog, reusableCandidate, observationSession, frozenBatchEvidence);
|
|
1006
|
+
}
|
|
1007
|
+
finally {
|
|
1008
|
+
await observationSession.dispose();
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (intent.kind === 'configReset') {
|
|
1012
|
+
const parsed = parsePluginId(intent.pluginId);
|
|
1013
|
+
if (!parsed.sourceId || !parsed.pluginName) {
|
|
1014
|
+
throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
|
|
1015
|
+
}
|
|
1016
|
+
await preparation?.reporter.emit('config');
|
|
1017
|
+
const target = await inspectPluginConfigResetTarget({
|
|
1018
|
+
sourceId: parsed.sourceId,
|
|
1019
|
+
pluginName: parsed.pluginName,
|
|
1020
|
+
scope: intent.scope,
|
|
1021
|
+
cwd: context.cwd,
|
|
1022
|
+
env: context.env,
|
|
1023
|
+
});
|
|
1024
|
+
signal?.throwIfAborted();
|
|
1025
|
+
if (target.kind === 'special') {
|
|
1026
|
+
throw new TuiOperationBlockedError('configResetTargetUnsupported', '设置目标是特殊文件系统节点,不能自动重置。请手动移动后重试。');
|
|
1027
|
+
}
|
|
1028
|
+
const relevantTargets = sessionStatusResult
|
|
1029
|
+
? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope)
|
|
1030
|
+
: [];
|
|
1031
|
+
const relevantRevisions = (sessionStatusResult?.targetRevisions ?? []).filter((revision) => relevantTargets.some((statusTarget) => statusTarget.platform === revision.platform
|
|
1032
|
+
&& statusTarget.scope === revision.scope
|
|
1033
|
+
&& statusTarget.pluginId === revision.pluginId));
|
|
1034
|
+
let selected;
|
|
1035
|
+
try {
|
|
1036
|
+
const packageInfo = await readCliPackageInfo();
|
|
1037
|
+
selected = await withResolvedCatalogPlugin({
|
|
1038
|
+
selector: intent.pluginId,
|
|
1039
|
+
cliVersion: packageInfo.version,
|
|
1040
|
+
env: context.env,
|
|
1041
|
+
...(preparation && sessionContextForPlugin(intent.pluginId)
|
|
1042
|
+
? { operationContext: sessionContextForPlugin(intent.pluginId) }
|
|
1043
|
+
: {}),
|
|
1044
|
+
...(signal ? { signal } : {}),
|
|
1045
|
+
consume: async ({ plugin, source: resolvedSource }) => ({
|
|
1046
|
+
source: sourceEvidenceForPlugin(resolvedSource, plugin.manifest.name),
|
|
1047
|
+
impact: await analyzePluginConfigMutationImpact({
|
|
1048
|
+
plugin,
|
|
1049
|
+
scope: intent.scope,
|
|
1050
|
+
cwd: context.cwd,
|
|
1051
|
+
env: context.env,
|
|
1052
|
+
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
1053
|
+
mutation: { kind: 'reset' },
|
|
1054
|
+
}),
|
|
1055
|
+
}),
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
catch {
|
|
1059
|
+
selected = undefined;
|
|
1060
|
+
}
|
|
1061
|
+
signal?.throwIfAborted();
|
|
1062
|
+
await preparation?.reporter.emit('evidence');
|
|
1063
|
+
return {
|
|
1064
|
+
catalog: sessionStatusResult ? {
|
|
1065
|
+
configDigest: sessionStatusResult.payload.catalog.configDigest,
|
|
1066
|
+
compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
|
|
1067
|
+
} : {
|
|
1068
|
+
configDigest: 'config-reset-offline',
|
|
1069
|
+
compositeDigest: 'config-reset-target',
|
|
1070
|
+
},
|
|
1071
|
+
...(selected ? { source: selected.source } : {}),
|
|
1072
|
+
...(sessionStatusResult ? {
|
|
1073
|
+
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
1074
|
+
} : {}),
|
|
1075
|
+
config: {
|
|
1076
|
+
scope: intent.scope,
|
|
1077
|
+
storeDigest: selected?.impact.storeDigest ?? target.fingerprint,
|
|
1078
|
+
impactState: selected?.impact.impactState ?? 'unknown',
|
|
1079
|
+
...(selected?.impact.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
1080
|
+
...(selected?.impact.affectedCommandPackages.length
|
|
1081
|
+
? { commandPackages: selected.impact.affectedCommandPackages } : {}),
|
|
1082
|
+
affectedTargets: (selected?.impact.affectedTargets
|
|
1083
|
+
?? relevantTargets).map(({ platform, scope }) => ({ platform, scope })),
|
|
1084
|
+
...((selected?.impact.sharedNativeRenderTargets.length ?? 0) > 0
|
|
1085
|
+
? { sharedNativeRenderTargets: selected.impact.sharedNativeRenderTargets }
|
|
1086
|
+
: {}),
|
|
1087
|
+
},
|
|
1088
|
+
resetTarget: {
|
|
1089
|
+
sourceId: parsed.sourceId,
|
|
1090
|
+
pluginName: parsed.pluginName,
|
|
1091
|
+
scope: intent.scope,
|
|
1092
|
+
kind: target.kind,
|
|
1093
|
+
fingerprint: target.fingerprint,
|
|
1094
|
+
},
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
if (intent.kind === 'catalogSourcesRecovery') {
|
|
1098
|
+
signal?.throwIfAborted();
|
|
1099
|
+
await preparation?.reporter.emit('validate');
|
|
1100
|
+
await preparation?.reporter.emit('evidence');
|
|
1101
|
+
return {
|
|
1102
|
+
catalog: {
|
|
1103
|
+
configDigest: 'invalid-catalog-sources-config',
|
|
1104
|
+
compositeDigest: 'bootstrap-recovery',
|
|
1105
|
+
},
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
if (intent.kind === 'catalogGeneration') {
|
|
1109
|
+
signal?.throwIfAborted();
|
|
1110
|
+
await preparation?.reporter.emit('validate');
|
|
1111
|
+
const packageInfo = await readCliPackageInfo();
|
|
1112
|
+
const config = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1113
|
+
const configuredSource = config.config.sources.find((source) => source.id === intent.sourceId);
|
|
1114
|
+
let proposedSource;
|
|
1115
|
+
let proposedConfigDigest;
|
|
1116
|
+
let explicitSourceChange = false;
|
|
1117
|
+
let sourceIdentityChangeImpact;
|
|
1118
|
+
if (intent.sourceMutation) {
|
|
1119
|
+
const preview = await preflightCatalogSourceMutation({
|
|
1120
|
+
request: intent.sourceMutation.action === 'add'
|
|
1121
|
+
? intent.sourceMutation
|
|
1122
|
+
: { action: 'update', sourceId: intent.sourceId, update: intent.sourceMutation.update },
|
|
1123
|
+
cwd: context.cwd,
|
|
1124
|
+
env: context.env,
|
|
1125
|
+
});
|
|
1126
|
+
proposedSource = preview.proposedSource;
|
|
1127
|
+
proposedConfigDigest = preview.proposedConfigDigest;
|
|
1128
|
+
if (preview.configDigest !== config.digest) {
|
|
1129
|
+
throw operationReviewStale('准备 Catalog 候选期间来源配置发生变化。');
|
|
1130
|
+
}
|
|
1131
|
+
const sourceFingerprintChanged = Boolean(proposedSource && (!configuredSource
|
|
1132
|
+
|| calculateCatalogSourceConfigFingerprint(proposedSource)
|
|
1133
|
+
!== calculateCatalogSourceConfigFingerprint(configuredSource)));
|
|
1134
|
+
explicitSourceChange = preview.proposedConfigDigest !== preview.configDigest
|
|
1135
|
+
&& (preview.identityChanged || sourceFingerprintChanged);
|
|
1136
|
+
if (preview.identityChanged)
|
|
1137
|
+
sourceIdentityChangeImpact = {
|
|
1138
|
+
impactSchemaVersion: 3,
|
|
1139
|
+
sourceId: preview.sourceId,
|
|
1140
|
+
primaryChange: preview.references.primaryChange,
|
|
1141
|
+
inventoryComplete: preview.references.inventoryComplete,
|
|
1142
|
+
references: preview.references.references,
|
|
1143
|
+
affectedReferences: preview.references.references,
|
|
1144
|
+
changes: {
|
|
1145
|
+
added: [], removed: [], changed: [], newlyIncompatible: [],
|
|
1146
|
+
dependencyChanged: [], platformSupportChanged: [],
|
|
1147
|
+
},
|
|
1148
|
+
errors: preview.references.errors,
|
|
1149
|
+
};
|
|
1150
|
+
if (!explicitSourceChange) {
|
|
1151
|
+
throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
let previousActive;
|
|
1155
|
+
let healthyActive;
|
|
1156
|
+
let previousActiveVersion;
|
|
1157
|
+
let previousStoreReady = false;
|
|
1158
|
+
try {
|
|
1159
|
+
previousActive = (await readCatalogGenerationState(context.env))
|
|
1160
|
+
.sources[intent.sourceId]?.active;
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
if (intent.action !== 'recoverState'
|
|
1164
|
+
|| !(error instanceof CatalogGenerationError)
|
|
1165
|
+
|| error.code !== 'catalogGenerationStateInvalid')
|
|
1166
|
+
throw error;
|
|
1167
|
+
}
|
|
1168
|
+
const activeStoreKey = previousActive
|
|
1169
|
+
? createCatalogStoreKey(previousActive.logicalIdentity, previousActive.snapshotDigest)
|
|
1170
|
+
: null;
|
|
1171
|
+
const configuredFingerprint = configuredSource
|
|
1172
|
+
? calculateCatalogSourceConfigFingerprint(configuredSource)
|
|
1173
|
+
: undefined;
|
|
1174
|
+
const canReuseCandidate = Boolean(reusableCandidate
|
|
1175
|
+
&& !intent.sourceMutation
|
|
1176
|
+
&& (intent.action === 'activate' || intent.action === 'update')
|
|
1177
|
+
&& configuredFingerprint
|
|
1178
|
+
&& reusableCandidate.sourceId === intent.sourceId
|
|
1179
|
+
&& reusableCandidate.sourceConfigFingerprint === configuredFingerprint
|
|
1180
|
+
&& reusableCandidate.probe.candidate.sourceConfigFingerprint === configuredFingerprint
|
|
1181
|
+
&& reusableCandidate.activeStoreKey === activeStoreKey
|
|
1182
|
+
&& reusableCandidate.probe.sourceId === intent.sourceId
|
|
1183
|
+
&& reusableCandidateEvidenceIsComplete(reusableCandidate));
|
|
1184
|
+
const probe = intent.action === 'repair'
|
|
1185
|
+
? await probeCatalogSourceRepairGeneration({
|
|
1186
|
+
sourceId: intent.sourceId,
|
|
1187
|
+
cliVersion: packageInfo.version,
|
|
1188
|
+
...(previousActive ? { expectedActive: previousActive } : {}),
|
|
1189
|
+
expectedConfigDigest: config.digest,
|
|
1190
|
+
env: context.env,
|
|
1191
|
+
signal,
|
|
1192
|
+
runtime: options.catalogGenerationRuntime,
|
|
1193
|
+
})
|
|
1194
|
+
: canReuseCandidate
|
|
1195
|
+
? structuredClone(reusableCandidate.probe)
|
|
1196
|
+
: await probeCatalogSourceGeneration({
|
|
1197
|
+
sourceId: intent.sourceId,
|
|
1198
|
+
...(proposedSource ? { source: proposedSource } : {}),
|
|
1199
|
+
cliVersion: packageInfo.version,
|
|
1200
|
+
env: context.env,
|
|
1201
|
+
signal,
|
|
1202
|
+
runtime: options.catalogGenerationRuntime,
|
|
1203
|
+
});
|
|
1204
|
+
signal?.throwIfAborted();
|
|
1205
|
+
await preparation?.reporter.emit('evidence');
|
|
1206
|
+
if (intent.action === 'activate' || intent.action === 'update') {
|
|
1207
|
+
try {
|
|
1208
|
+
healthyActive = await resolveActiveCatalogGeneration({ sourceId: intent.sourceId, env: context.env });
|
|
1209
|
+
}
|
|
1210
|
+
catch (error) {
|
|
1211
|
+
if (!(error instanceof CatalogGenerationError)
|
|
1212
|
+
&& !(error instanceof CatalogProviderCompatibilityError))
|
|
1213
|
+
throw error;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const comparisonSource = proposedSource
|
|
1217
|
+
?? configuredSource;
|
|
1218
|
+
if (previousActive) {
|
|
1219
|
+
const store = await readCatalogStoreEntry({
|
|
1220
|
+
logicalIdentity: previousActive.logicalIdentity,
|
|
1221
|
+
snapshotDigest: previousActive.snapshotDigest,
|
|
1222
|
+
env: context.env,
|
|
1223
|
+
}).catch(() => undefined);
|
|
1224
|
+
previousStoreReady = Boolean(store);
|
|
1225
|
+
previousActiveVersion = healthyActive?.catalogVersion
|
|
1226
|
+
?? store?.metadata.catalogVersion;
|
|
1227
|
+
}
|
|
1228
|
+
if (intent.action === 'update' && !intent.sourceMutation && previousActive && comparisonSource) {
|
|
1229
|
+
explicitSourceChange = previousActive.sourceConfigFingerprint
|
|
1230
|
+
!== calculateCatalogSourceConfigFingerprint(comparisonSource)
|
|
1231
|
+
|| previousActive.sourceIdentity !== await calculateCatalogSourceIdentity(comparisonSource);
|
|
1232
|
+
}
|
|
1233
|
+
const comparisonActive = healthyActive
|
|
1234
|
+
? {
|
|
1235
|
+
logicalIdentity: healthyActive.logicalIdentity,
|
|
1236
|
+
catalogVersion: healthyActive.catalogVersion,
|
|
1237
|
+
snapshotDigest: healthyActive.snapshotDigest,
|
|
1238
|
+
resolvedCommit: healthyActive.resolvedCommit,
|
|
1239
|
+
}
|
|
1240
|
+
: previousActive && previousActiveVersion
|
|
1241
|
+
? {
|
|
1242
|
+
logicalIdentity: previousActive.logicalIdentity,
|
|
1243
|
+
catalogVersion: previousActiveVersion,
|
|
1244
|
+
snapshotDigest: previousActive.snapshotDigest,
|
|
1245
|
+
resolvedCommit: previousActive.resolvedCommit,
|
|
1246
|
+
}
|
|
1247
|
+
: undefined;
|
|
1248
|
+
const candidateRelation = comparisonActive && comparisonSource
|
|
1249
|
+
? compareCatalogGenerationFreshness({
|
|
1250
|
+
sourceType: comparisonSource.type,
|
|
1251
|
+
active: comparisonActive,
|
|
1252
|
+
candidate: probe.candidate,
|
|
1253
|
+
}).state
|
|
1254
|
+
: undefined;
|
|
1255
|
+
const exactStoreTarget = Boolean(previousActive)
|
|
1256
|
+
&& createCatalogStoreKey(previousActive.logicalIdentity, previousActive.snapshotDigest)
|
|
1257
|
+
=== createCatalogStoreKey(probe.candidate.logicalIdentity, probe.candidate.snapshotDigest);
|
|
1258
|
+
if (intent.action === 'repair' && !exactStoreTarget) {
|
|
1259
|
+
throw new CatalogGenerationError({
|
|
1260
|
+
code: 'catalogReplaceDamagedRequired', sourceId: intent.sourceId,
|
|
1261
|
+
message: `Catalog source "${intent.sourceId}" cannot reproduce the damaged Active entry. Use the current candidate replacement Review.`,
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
const exactRepair = (intent.action === 'repair' || intent.action === 'update')
|
|
1265
|
+
&& exactStoreTarget && !previousStoreReady;
|
|
1266
|
+
if (previousActive && !previousStoreReady && !exactStoreTarget
|
|
1267
|
+
&& !intent.sourceMutation && intent.action !== 'replaceDamaged') {
|
|
1268
|
+
throw new CatalogGenerationError({
|
|
1269
|
+
code: 'catalogReplaceDamagedRequired',
|
|
1270
|
+
sourceId: intent.sourceId,
|
|
1271
|
+
message: `Catalog source "${intent.sourceId}" has a damaged Active entry whose version cannot be compared safely. The current Active remains unchanged. Edit the source explicitly, or remove and add it again.`,
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'incompatible') {
|
|
1275
|
+
throw new CatalogGenerationError({
|
|
1276
|
+
code: 'catalogLogicalIdentityChanged', sourceId: intent.sourceId,
|
|
1277
|
+
message: `Catalog source "${intent.sourceId}" changed logical identity. Edit or remove and re-add the source.`,
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'versionConflict') {
|
|
1281
|
+
throw new TuiOperationBlockedError('catalogVersionConflict', '候选 Catalog 与当前 Active 版本相同,但内容摘要不同。普通更新已阻止。');
|
|
1282
|
+
}
|
|
1283
|
+
if (intent.action !== 'replaceDamaged' && !explicitSourceChange && candidateRelation === 'rollbackDetected') {
|
|
1284
|
+
throw new TuiOperationBlockedError('catalogRollbackDetected', '候选 Catalog 版本低于当前 Active。普通更新已阻止。');
|
|
1285
|
+
}
|
|
1286
|
+
const impact = exactRepair ? undefined
|
|
1287
|
+
: sourceIdentityChangeImpact ?? await inspectCatalogSourceImpact({
|
|
1288
|
+
sourceId: intent.sourceId,
|
|
1289
|
+
cwd: context.cwd,
|
|
1290
|
+
env: context.env,
|
|
1291
|
+
sourcePrimary: comparisonSource?.primary === true,
|
|
1292
|
+
mode: 'generation',
|
|
1293
|
+
candidateManifest: probe.manifest,
|
|
1294
|
+
});
|
|
1295
|
+
const primaryPreview = intent.setPrimaryAfterActivation
|
|
1296
|
+
? await preflightCatalogSourceMutation({
|
|
1297
|
+
request: { action: 'setPrimary', sourceId: intent.sourceId },
|
|
1298
|
+
cwd: context.cwd,
|
|
1299
|
+
env: context.env,
|
|
1300
|
+
})
|
|
1301
|
+
: undefined;
|
|
1302
|
+
if (primaryPreview && primaryPreview.configDigest !== config.digest) {
|
|
1303
|
+
throw operationReviewStale('准备主来源切换期间来源配置发生变化。');
|
|
1304
|
+
}
|
|
1305
|
+
const primaryImpact = primaryPreview ? {
|
|
1306
|
+
impactSchemaVersion: 3,
|
|
1307
|
+
sourceId: primaryPreview.sourceId,
|
|
1308
|
+
primaryChange: primaryPreview.references.primaryChange,
|
|
1309
|
+
inventoryComplete: primaryPreview.references.inventoryComplete,
|
|
1310
|
+
references: primaryPreview.references.references,
|
|
1311
|
+
affectedReferences: primaryPreview.references.references,
|
|
1312
|
+
changes: {
|
|
1313
|
+
added: [], removed: [], changed: [], newlyIncompatible: [],
|
|
1314
|
+
dependencyChanged: [], platformSupportChanged: [],
|
|
1315
|
+
},
|
|
1316
|
+
errors: primaryPreview.references.errors,
|
|
1317
|
+
} : undefined;
|
|
1318
|
+
return {
|
|
1319
|
+
catalog: {
|
|
1320
|
+
configDigest: config.digest,
|
|
1321
|
+
compositeDigest: probe.candidate.snapshotDigest,
|
|
1322
|
+
},
|
|
1323
|
+
catalogGeneration: {
|
|
1324
|
+
action: intent.action,
|
|
1325
|
+
...(previousActive ? { previousActive } : {}),
|
|
1326
|
+
...(previousActiveVersion ? { previousActiveVersion } : {}),
|
|
1327
|
+
candidate: probe.candidate,
|
|
1328
|
+
...(candidateRelation ? { candidateRelation } : {}),
|
|
1329
|
+
...(explicitSourceChange ? { explicitSourceChange: true } : {}),
|
|
1330
|
+
...(exactRepair ? { exactRepair: true } : {}),
|
|
1331
|
+
activationMode: intent.action === 'replaceDamaged' ? 'replaceDamaged'
|
|
1332
|
+
: explicitSourceChange ? 'explicitSourceChange' : 'normal',
|
|
1333
|
+
...(impact ? {
|
|
1334
|
+
impact,
|
|
1335
|
+
impactDigest: calculateCatalogSourceImpactDigest(impact),
|
|
1336
|
+
impactMode: sourceIdentityChangeImpact ? 'identityChange' : 'generation',
|
|
1337
|
+
} : {}),
|
|
1338
|
+
...(primaryPreview && primaryImpact ? {
|
|
1339
|
+
primaryImpact,
|
|
1340
|
+
primaryImpactDigest: primaryPreview.impactDigest,
|
|
1341
|
+
primaryConfigDigest: primaryPreview.configDigest,
|
|
1342
|
+
} : {}),
|
|
1343
|
+
configDigest: config.digest,
|
|
1344
|
+
...(proposedConfigDigest ? { proposedConfigDigest } : {}),
|
|
1345
|
+
...(proposedSource ? { proposedSource } : {}),
|
|
1346
|
+
...(intent.sourceMutation ? { sourceMutation: intent.sourceMutation } : {}),
|
|
1347
|
+
...(intent.setPrimaryAfterActivation ? { setPrimaryAfterActivation: true } : {}),
|
|
1348
|
+
},
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
483
1351
|
if (intent.kind === 'sourceMutation') {
|
|
484
1352
|
signal?.throwIfAborted();
|
|
485
1353
|
if (intent.action === 'add' && !intent.source
|
|
@@ -501,249 +1369,1341 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
501
1369
|
await preparation?.reporter.emit('evidence');
|
|
502
1370
|
signal?.throwIfAborted();
|
|
503
1371
|
const evidence = {
|
|
504
|
-
catalog: { configDigest: preview.configDigest, compositeDigest: preview.
|
|
1372
|
+
catalog: { configDigest: preview.configDigest, compositeDigest: preview.proposedConfigDigest },
|
|
505
1373
|
sourceWrite: {
|
|
506
1374
|
configDigest: preview.configDigest,
|
|
507
|
-
|
|
1375
|
+
proposedConfigDigest: preview.proposedConfigDigest,
|
|
508
1376
|
...(preview.sourceIdentity ? { sourceIdentity: preview.sourceIdentity } : {}),
|
|
509
1377
|
inventoryDigest: digest({ references: preview.references.references, errors: preview.references.errors }),
|
|
1378
|
+
impactDigest: preview.impactDigest,
|
|
510
1379
|
referenceCount: preview.references.references.length,
|
|
511
|
-
inventoryComplete: preview.references.
|
|
1380
|
+
inventoryComplete: preview.references.inventoryComplete,
|
|
1381
|
+
impact: {
|
|
1382
|
+
impactSchemaVersion: 3,
|
|
1383
|
+
sourceId: preview.sourceId,
|
|
1384
|
+
primaryChange: preview.references.primaryChange,
|
|
1385
|
+
inventoryComplete: preview.references.inventoryComplete,
|
|
1386
|
+
references: preview.references.references,
|
|
1387
|
+
affectedReferences: preview.references.references,
|
|
1388
|
+
changes: {
|
|
1389
|
+
added: [], removed: [], changed: [], newlyIncompatible: [],
|
|
1390
|
+
dependencyChanged: [], platformSupportChanged: [],
|
|
1391
|
+
},
|
|
1392
|
+
errors: preview.references.errors,
|
|
1393
|
+
},
|
|
512
1394
|
},
|
|
513
1395
|
};
|
|
514
1396
|
if (preparation?.validateIntent
|
|
515
|
-
&& evidence.sourceWrite?.configDigest === evidence.sourceWrite?.
|
|
1397
|
+
&& evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
|
|
516
1398
|
throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
|
|
517
1399
|
}
|
|
518
1400
|
return evidence;
|
|
519
1401
|
}
|
|
520
|
-
if (!pluginIntent(intent)
|
|
1402
|
+
if (!pluginIntent(intent) && intent.kind !== 'pluginWriteBatch'
|
|
1403
|
+
&& intent.kind !== 'pluginWriteTask') {
|
|
521
1404
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
522
1405
|
}
|
|
523
1406
|
signal?.throwIfAborted();
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
await
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
1407
|
+
if (intent.kind === 'pluginWriteTask') {
|
|
1408
|
+
await preparation?.reporter.emit('status');
|
|
1409
|
+
const packageInfo = await readCliPackageInfo();
|
|
1410
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1411
|
+
const requestedBySource = new Map();
|
|
1412
|
+
for (const target of intent.targets) {
|
|
1413
|
+
const parsed = parsePluginId(target.pluginId);
|
|
1414
|
+
if (!parsed.sourceId || formatPluginId({
|
|
1415
|
+
sourceId: parsed.sourceId,
|
|
1416
|
+
pluginName: parsed.pluginName,
|
|
1417
|
+
}) !== target.pluginId) {
|
|
1418
|
+
throw new TuiOperationBlockedError('invalidPluginId', '任务目标必须使用 canonical pluginId。');
|
|
1419
|
+
}
|
|
1420
|
+
requestedBySource.set(parsed.sourceId, [
|
|
1421
|
+
...(requestedBySource.get(parsed.sourceId) ?? []),
|
|
1422
|
+
target,
|
|
1423
|
+
]);
|
|
1424
|
+
}
|
|
1425
|
+
const sources = [];
|
|
1426
|
+
const executableTargets = [];
|
|
1427
|
+
const causes = [];
|
|
1428
|
+
const blockedTargets = [];
|
|
1429
|
+
const observedTargets = [];
|
|
1430
|
+
const observedRevisions = [];
|
|
1431
|
+
const operationContexts = new Map();
|
|
1432
|
+
await preparation?.reporter.emit('catalog');
|
|
1433
|
+
for (const [sourceId, requestedTargets] of [...requestedBySource.entries()]
|
|
1434
|
+
.sort(([left], [right]) => left.localeCompare(right, 'en'))) {
|
|
1435
|
+
signal?.throwIfAborted();
|
|
1436
|
+
let catalogOperation;
|
|
1437
|
+
try {
|
|
1438
|
+
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1439
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId])
|
|
1440
|
+
: await resolveCatalogOperationContext({
|
|
1441
|
+
sourceIds: [sourceId],
|
|
1442
|
+
env: context.env,
|
|
1443
|
+
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1444
|
+
...(signal ? { signal } : {}),
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
catch (error) {
|
|
1448
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
1449
|
+
throw error;
|
|
1450
|
+
blockedTargets.push(...requestedTargets.map((target, index) => {
|
|
1451
|
+
const diagnostic = error instanceof CatalogGenerationError ? {
|
|
1452
|
+
id: `task-source:${sourceId}:${index}`,
|
|
1453
|
+
code: error.code,
|
|
1454
|
+
stage: 'source',
|
|
1455
|
+
sourceId,
|
|
1456
|
+
safeMessage: 'The Catalog source could not be prepared safely.',
|
|
1457
|
+
target: { pluginId: target.pluginId, platform: target.platform, scope: target.scope },
|
|
1458
|
+
causes: [],
|
|
1459
|
+
detailLines: [],
|
|
1460
|
+
} : projectThrownOperationDiagnostic({
|
|
1461
|
+
error,
|
|
1462
|
+
id: `task-source:${sourceId}:${index}`,
|
|
1463
|
+
stage: 'source',
|
|
1464
|
+
fallbackCode: 'catalogSourceResolutionFailed',
|
|
1465
|
+
sourceId,
|
|
1466
|
+
target,
|
|
1467
|
+
});
|
|
1468
|
+
return {
|
|
1469
|
+
target,
|
|
1470
|
+
reason: 'targetUnavailable',
|
|
1471
|
+
issueIds: [],
|
|
1472
|
+
errorCodes: [diagnostic.code],
|
|
1473
|
+
diagnostics: [diagnostic],
|
|
1474
|
+
};
|
|
1475
|
+
}));
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1478
|
+
operationContexts.set(sourceId, catalogOperation);
|
|
1479
|
+
const status = await statusCommandV8({
|
|
1480
|
+
plugins: [...new Set(requestedTargets.map((target) => target.pluginId))],
|
|
1481
|
+
cwd: context.cwd,
|
|
1482
|
+
env: context.env,
|
|
1483
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
1484
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1485
|
+
cliVersion: packageInfo.version,
|
|
1486
|
+
catalogOperationContext: catalogOperation,
|
|
1487
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
1488
|
+
});
|
|
1489
|
+
observedTargets.push(...flattenStatusTargets(status.payload.targets));
|
|
1490
|
+
observedRevisions.push(...status.targetRevisions);
|
|
1491
|
+
const model = buildTuiViewModel({
|
|
1492
|
+
status: status.payload,
|
|
1493
|
+
pluginSummaries: status.pluginSummaries,
|
|
1494
|
+
targetRevisions: status.targetRevisions,
|
|
1495
|
+
});
|
|
1496
|
+
const runtimeCandidates = intent.goal === 'restore'
|
|
1497
|
+
? buildPluginWriteTaskCandidates(model, 'restore')
|
|
1498
|
+
: requestedTargets.map((target) => {
|
|
1499
|
+
const expected = target.expectedUpdate;
|
|
1500
|
+
const revision = status.targetRevisions.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
1501
|
+
&& entry.scope === target.scope);
|
|
1502
|
+
const live = flattenStatusTargets(status.payload.targets).find((entry) => {
|
|
1503
|
+
if (entry.pluginId !== target.pluginId || entry.platform !== target.platform)
|
|
1504
|
+
return false;
|
|
1505
|
+
const desired = statusTargetIntent(entry);
|
|
1506
|
+
return entry.scope === target.scope || desired.scopes.includes(target.scope);
|
|
1507
|
+
});
|
|
1508
|
+
const generation = catalogOperation.generationSet.sources.find((entry) => entry.sourceId === sourceId);
|
|
1509
|
+
const eligible = Boolean(expected && live && revision
|
|
1510
|
+
&& live.sourceIdentity === expected.sourceIdentity
|
|
1511
|
+
&& generation?.storeKey === expected.generationKey
|
|
1512
|
+
&& revision.revision.installedVersion === expected.installedVersion
|
|
1513
|
+
&& revision.revision.availableVersion === expected.activeVersion
|
|
1514
|
+
&& revision.revision.state === 'upgradeAvailable');
|
|
1515
|
+
const directVersionUpgrade = live !== undefined && revision !== undefined
|
|
1516
|
+
&& isDirectCodexVersionUpgrade(live, revision.revision);
|
|
1517
|
+
return {
|
|
1518
|
+
target,
|
|
1519
|
+
displayName: model.plugins.find((plugin) => plugin.pluginId === target.pluginId)
|
|
1520
|
+
?.displayName ?? parsePluginId(target.pluginId).pluginName,
|
|
1521
|
+
sourceId,
|
|
1522
|
+
eligible,
|
|
1523
|
+
...(!eligible ? { blockReason: 'updateEvidenceIncomplete' } : {}),
|
|
1524
|
+
recommendedMethod: live?.state === 'ready' || directVersionUpgrade
|
|
1525
|
+
? 'sync' : 'reinstall',
|
|
1526
|
+
issueIds: [],
|
|
1527
|
+
};
|
|
1528
|
+
});
|
|
1529
|
+
const draft = buildPluginWriteTaskDraft({ ...intent, targets: requestedTargets }, model, runtimeCandidates);
|
|
1530
|
+
blockedTargets.push(...draft.blockedTargets);
|
|
1531
|
+
if (draft.executableTargets.length === 0)
|
|
1532
|
+
continue;
|
|
1533
|
+
const methodsByTarget = new Map();
|
|
1534
|
+
for (const cause of draft.causes) {
|
|
1535
|
+
const key = pluginWriteTaskTargetKey(cause.repairTarget);
|
|
1536
|
+
const methods = methodsByTarget.get(key) ?? new Set();
|
|
1537
|
+
methods.add(cause.method);
|
|
1538
|
+
methodsByTarget.set(key, methods);
|
|
1539
|
+
}
|
|
1540
|
+
const conflicted = new Set([...methodsByTarget].filter(([, methods]) => methods.size > 1)
|
|
1541
|
+
.map(([key]) => key));
|
|
1542
|
+
if (conflicted.size > 0) {
|
|
1543
|
+
const blockedKeys = new Set(draft.causes.filter((cause) => conflicted.has(pluginWriteTaskTargetKey(cause.repairTarget)))
|
|
1544
|
+
.flatMap((cause) => cause.consumers.map(pluginWriteTaskTargetKey)));
|
|
1545
|
+
blockedTargets.push(...draft.executableTargets.filter((target) => blockedKeys.has(pluginWriteTaskTargetKey(target))).map((target) => ({
|
|
1546
|
+
target,
|
|
1547
|
+
reason: 'taskActionConflict',
|
|
1548
|
+
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1549
|
+
.map((cause) => cause.issueId),
|
|
1550
|
+
})));
|
|
1551
|
+
draft.executableTargets = draft.executableTargets.filter((target) => !blockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1552
|
+
draft.causes = draft.causes.filter((cause) => cause.consumers.some((consumer) => !blockedKeys.has(pluginWriteTaskTargetKey(consumer))));
|
|
1553
|
+
}
|
|
1554
|
+
if (draft.executableTargets.length === 0)
|
|
1555
|
+
continue;
|
|
1556
|
+
const taskTargets = () => draft.executableTargets.map((target) => {
|
|
1557
|
+
const direct = draft.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
1558
|
+
return {
|
|
1559
|
+
pluginId: target.pluginId,
|
|
1560
|
+
platform: target.platform,
|
|
1561
|
+
scope: target.scope,
|
|
1562
|
+
method: direct.some((cause) => cause.method === 'reinstall')
|
|
1563
|
+
? 'reinstall' : 'sync',
|
|
1564
|
+
};
|
|
1565
|
+
});
|
|
1566
|
+
const repairTargets = () => [...new Map(draft.causes.map((cause) => [
|
|
1567
|
+
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
1568
|
+
{ ...cause.repairTarget, method: cause.method },
|
|
1569
|
+
])).values()];
|
|
1570
|
+
let preview;
|
|
1571
|
+
while (draft.executableTargets.length > 0) {
|
|
1572
|
+
preview = await taskWriteCommand(taskTargets(), {
|
|
1573
|
+
cwd: context.cwd,
|
|
1574
|
+
repairTargets: repairTargets(),
|
|
1575
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
1576
|
+
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
1577
|
+
}, {
|
|
1578
|
+
...context,
|
|
1579
|
+
catalogOperationContext: catalogOperation,
|
|
1580
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1581
|
+
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1582
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
1583
|
+
previewOperationPlan: true,
|
|
1584
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1585
|
+
...(preparation ? {
|
|
1586
|
+
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1587
|
+
} : {}),
|
|
1588
|
+
});
|
|
1589
|
+
if (!preview.operationPlan) {
|
|
1590
|
+
const projected = projectExecutionDiagnostics(preview, `task-preview:${sourceId}`, [], 'pluginWrite');
|
|
1591
|
+
const sourceDiagnostics = projected.diagnostics.filter((diagnostic) => !diagnostic.target)
|
|
1592
|
+
.map((diagnostic) => ({ ...diagnostic, sourceId, target: undefined }));
|
|
1593
|
+
blockedTargets.push(...draft.executableTargets.map((target, targetIndex) => {
|
|
1594
|
+
const matchingDiagnostics = projected.diagnostics.filter((diagnostic) => diagnostic.target?.pluginId === target.pluginId
|
|
1595
|
+
&& diagnostic.target.platform === target.platform
|
|
1596
|
+
&& diagnostic.target.scope === target.scope);
|
|
1597
|
+
const preparedDiagnostics = [
|
|
1598
|
+
...matchingDiagnostics,
|
|
1599
|
+
...(targetIndex === 0 ? sourceDiagnostics : []),
|
|
1600
|
+
];
|
|
1601
|
+
const targetDiagnostics = preparedDiagnostics.length > 0
|
|
1602
|
+
? preparedDiagnostics
|
|
1603
|
+
: [projectThrownOperationDiagnostic({
|
|
1604
|
+
error: undefined,
|
|
1605
|
+
id: `task-preview:${sourceId}:${targetIndex}`,
|
|
1606
|
+
stage: 'dependencyPlan',
|
|
1607
|
+
fallbackCode: 'taskOperationPlanMissing',
|
|
1608
|
+
sourceId,
|
|
1609
|
+
target,
|
|
1610
|
+
})];
|
|
1611
|
+
const diagnostics = targetDiagnostics.map((diagnostic, diagnosticIndex) => ({
|
|
1612
|
+
...structuredClone(diagnostic),
|
|
1613
|
+
id: `task-preview:${sourceId}:${targetIndex}:${diagnosticIndex}`,
|
|
1614
|
+
sourceId,
|
|
1615
|
+
}));
|
|
1616
|
+
return {
|
|
1617
|
+
target,
|
|
1618
|
+
reason: 'targetUnavailable',
|
|
1619
|
+
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1620
|
+
.map((cause) => cause.issueId),
|
|
1621
|
+
errorCodes: [...new Set(diagnostics.map((diagnostic) => diagnostic.code))],
|
|
1622
|
+
diagnostics,
|
|
1623
|
+
};
|
|
1624
|
+
}));
|
|
1625
|
+
draft.executableTargets = [];
|
|
1626
|
+
draft.causes = [];
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
if (intent.goal === 'restore') {
|
|
1630
|
+
const grouped = new Map();
|
|
1631
|
+
const blockedByVersion = new Map();
|
|
1632
|
+
for (const cause of draft.causes) {
|
|
1633
|
+
const placements = (preview.dependencyPlan?.targets ?? []).filter((target) => target.pluginId === cause.repairTarget.pluginId
|
|
1634
|
+
&& target.platform === cause.repairTarget.platform);
|
|
1635
|
+
const placement = placements.find((target) => target.requestedScope === cause.repairTarget.scope)
|
|
1636
|
+
?? (placements.length === 1 ? placements[0] : undefined);
|
|
1637
|
+
if (!placement) {
|
|
1638
|
+
throw new TuiOperationBlockedError('taskRepairPlacementMissing', '任务修复目标无法绑定到依赖位置。');
|
|
1639
|
+
}
|
|
1640
|
+
const repairTarget = {
|
|
1641
|
+
pluginId: cause.repairTarget.pluginId,
|
|
1642
|
+
platform: cause.repairTarget.platform,
|
|
1643
|
+
scope: placement.requestedScope ?? placement.resolvedScope,
|
|
1644
|
+
};
|
|
1645
|
+
const version = evaluatePluginWriteTaskRestoreVersion(repairTarget, cause.restoreVersionEvidence?.state === 'absent', model);
|
|
1646
|
+
if (!version.evidence || version.blockReason) {
|
|
1647
|
+
for (const consumer of cause.consumers) {
|
|
1648
|
+
const key = pluginWriteTaskTargetKey(consumer);
|
|
1649
|
+
const current = blockedByVersion.get(key) ?? {
|
|
1650
|
+
reason: version.blockReason ?? 'restoreVersionUnverified',
|
|
1651
|
+
issueIds: new Set(),
|
|
1652
|
+
};
|
|
1653
|
+
if (version.blockReason === 'restoreVersionChanged') {
|
|
1654
|
+
current.reason = version.blockReason;
|
|
1655
|
+
}
|
|
1656
|
+
current.issueIds.add(cause.issueId);
|
|
1657
|
+
blockedByVersion.set(key, current);
|
|
1658
|
+
}
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
const causeKey = ['restore', cause.sourceIdentity,
|
|
1662
|
+
pluginWriteTaskTargetKey(repairTarget)].join('\0');
|
|
1663
|
+
const current = grouped.get(causeKey);
|
|
1664
|
+
grouped.set(causeKey, {
|
|
1665
|
+
cause: {
|
|
1666
|
+
...cause,
|
|
1667
|
+
causeKey,
|
|
1668
|
+
issueId: [cause.issueId, ...(current?.issueIds ?? [])]
|
|
1669
|
+
.sort((left, right) => left.localeCompare(right, 'en'))[0],
|
|
1670
|
+
repairTarget,
|
|
1671
|
+
consumers: [...new Map([
|
|
1672
|
+
...(current?.cause.consumers ?? []),
|
|
1673
|
+
...cause.consumers,
|
|
1674
|
+
].map((consumer) => [pluginWriteTaskTargetKey(consumer), consumer])).values()]
|
|
1675
|
+
.sort((left, right) => pluginWriteTaskTargetKey(left)
|
|
1676
|
+
.localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1677
|
+
restoreVersionEvidence: version.evidence,
|
|
1678
|
+
},
|
|
1679
|
+
issueIds: new Set([...(current?.issueIds ?? []), cause.issueId]),
|
|
1680
|
+
methods: new Set([...(current?.methods ?? []), cause.method]),
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
for (const group of grouped.values()) {
|
|
1684
|
+
if (group.methods.size < 2)
|
|
1685
|
+
continue;
|
|
1686
|
+
for (const consumer of group.cause.consumers) {
|
|
1687
|
+
const key = pluginWriteTaskTargetKey(consumer);
|
|
1688
|
+
const current = blockedByVersion.get(key) ?? {
|
|
1689
|
+
reason: 'taskActionConflict',
|
|
1690
|
+
issueIds: new Set(),
|
|
1691
|
+
};
|
|
1692
|
+
group.issueIds.forEach((issueId) => current.issueIds.add(issueId));
|
|
1693
|
+
blockedByVersion.set(key, current);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
const newlyBlockedKeys = new Set(blockedByVersion.keys());
|
|
1697
|
+
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1698
|
+
const blocked = blockedByVersion.get(pluginWriteTaskTargetKey(target));
|
|
1699
|
+
return blocked ? [{
|
|
1700
|
+
target,
|
|
1701
|
+
reason: blocked.reason,
|
|
1702
|
+
issueIds: [...blocked.issueIds].sort((left, right) => left.localeCompare(right, 'en')),
|
|
1703
|
+
}] : [];
|
|
1704
|
+
}));
|
|
1705
|
+
const normalizedCauses = [...grouped.values()]
|
|
1706
|
+
.filter((group) => group.methods.size === 1)
|
|
1707
|
+
.map((group) => ({
|
|
1708
|
+
...group.cause,
|
|
1709
|
+
consumers: group.cause.consumers.filter((consumer) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(consumer))),
|
|
1710
|
+
}))
|
|
1711
|
+
.filter((cause) => cause.consumers.length > 0)
|
|
1712
|
+
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en'));
|
|
1713
|
+
const normalizedTargets = draft.executableTargets.filter((target) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1714
|
+
const changed = JSON.stringify(normalizedCauses) !== JSON.stringify(draft.causes)
|
|
1715
|
+
|| JSON.stringify(normalizedTargets) !== JSON.stringify(draft.executableTargets);
|
|
1716
|
+
draft.causes = normalizedCauses;
|
|
1717
|
+
draft.executableTargets = normalizedTargets;
|
|
1718
|
+
if (changed)
|
|
1719
|
+
continue;
|
|
1720
|
+
}
|
|
1721
|
+
const blockedPlanTargets = (preview.dependencyPlan?.targets ?? []).filter((target) => target.action === 'block' || target.errorCode !== undefined);
|
|
1722
|
+
if (blockedPlanTargets.length === 0)
|
|
1723
|
+
break;
|
|
1724
|
+
const requestedKeys = new Set(draft.executableTargets.map(pluginWriteTaskTargetKey));
|
|
1725
|
+
const blockedPlansByRoot = new Map();
|
|
1726
|
+
for (const blockedPlanTarget of blockedPlanTargets) {
|
|
1727
|
+
const pending = [blockedPlanTarget];
|
|
1728
|
+
const visited = new Set();
|
|
1729
|
+
const mappedRoots = new Set();
|
|
1730
|
+
const repairKey = pluginWriteTaskTargetKey({
|
|
1731
|
+
pluginId: blockedPlanTarget.pluginId,
|
|
1732
|
+
platform: blockedPlanTarget.platform,
|
|
1733
|
+
scope: blockedPlanTarget.requestedScope ?? blockedPlanTarget.resolvedScope,
|
|
1734
|
+
});
|
|
1735
|
+
for (const cause of draft.causes) {
|
|
1736
|
+
if (pluginWriteTaskTargetKey(cause.repairTarget) !== repairKey)
|
|
1737
|
+
continue;
|
|
1738
|
+
cause.consumers.forEach((consumer) => mappedRoots.add(pluginWriteTaskTargetKey(consumer)));
|
|
1739
|
+
}
|
|
1740
|
+
while (pending.length > 0) {
|
|
1741
|
+
const current = pending.shift();
|
|
1742
|
+
const currentKey = pluginWriteTaskTargetKey({
|
|
1743
|
+
pluginId: current.pluginId,
|
|
1744
|
+
platform: current.platform,
|
|
1745
|
+
scope: current.requestedScope ?? current.resolvedScope,
|
|
1746
|
+
});
|
|
1747
|
+
if (visited.has(currentKey))
|
|
1748
|
+
continue;
|
|
1749
|
+
visited.add(currentKey);
|
|
1750
|
+
if (current.relation === 'requested' && requestedKeys.has(currentKey)) {
|
|
1751
|
+
mappedRoots.add(currentKey);
|
|
1752
|
+
}
|
|
1753
|
+
for (const consumer of current.requiredByTargets ?? []) {
|
|
1754
|
+
const consumerKey = pluginWriteTaskTargetKey(consumer);
|
|
1755
|
+
if (requestedKeys.has(consumerKey)) {
|
|
1756
|
+
mappedRoots.add(consumerKey);
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
const placement = preview.dependencyPlan?.targets.find((target) => target.pluginId === consumer.pluginId && target.platform === consumer.platform
|
|
1760
|
+
&& (target.requestedScope ?? target.resolvedScope) === consumer.scope);
|
|
1761
|
+
if (placement)
|
|
1762
|
+
pending.push(placement);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (mappedRoots.size === 0) {
|
|
1766
|
+
throw new TuiOperationBlockedError('taskPlanConsumerMissing', '任务计划中的阻止项无法映射到请求目标。');
|
|
1767
|
+
}
|
|
1768
|
+
for (const key of mappedRoots) {
|
|
1769
|
+
const plans = blockedPlansByRoot.get(key) ?? [];
|
|
1770
|
+
plans.push(blockedPlanTarget);
|
|
1771
|
+
blockedPlansByRoot.set(key, plans);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1775
|
+
const plans = blockedPlansByRoot.get(pluginWriteTaskTargetKey(target));
|
|
1776
|
+
return plans ? [{
|
|
1777
|
+
target,
|
|
1778
|
+
reason: 'targetUnavailable',
|
|
1779
|
+
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1780
|
+
.map((cause) => cause.issueId),
|
|
1781
|
+
errorCodes: [...new Set(plans.map((plan) => plan.errorCode ?? 'writePlanBlocked'))]
|
|
1782
|
+
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
1783
|
+
diagnostics: plans.map((plan, index) => ({
|
|
1784
|
+
id: `task-plan:${sourceId}:${target.pluginId}:${target.platform}:${target.scope}:${index}`,
|
|
1785
|
+
code: plan.errorCode ?? 'writePlanBlocked',
|
|
1786
|
+
stage: 'dependencyPlan',
|
|
1787
|
+
sourceId,
|
|
1788
|
+
target: {
|
|
1789
|
+
pluginId: plan.pluginId, platform: plan.platform,
|
|
1790
|
+
scope: plan.requestedScope ?? plan.resolvedScope,
|
|
1791
|
+
},
|
|
1792
|
+
causes: [],
|
|
1793
|
+
detailLines: [],
|
|
1794
|
+
safeMessage: plan.errorCode === 'artifactCollision'
|
|
1795
|
+
? '输出位置存在外来内容,无法覆盖。'
|
|
1796
|
+
: plan.errorCode === 'exclusiveOwnerEvidenceInvalid'
|
|
1797
|
+
? '独立扩展的归属标记缺失或损坏。'
|
|
1798
|
+
: plan.errorCode === 'dependencyNotReady'
|
|
1799
|
+
? '依赖安装状态无法读取。'
|
|
1800
|
+
: '依赖计划阻止了此目标。',
|
|
1801
|
+
})),
|
|
1802
|
+
}] : [];
|
|
1803
|
+
}));
|
|
1804
|
+
draft.executableTargets = draft.executableTargets.filter((target) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(target)));
|
|
1805
|
+
draft.causes = draft.causes.map((cause) => ({
|
|
1806
|
+
...cause,
|
|
1807
|
+
consumers: cause.consumers.filter((consumer) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(consumer))),
|
|
1808
|
+
})).filter((cause) => cause.consumers.length > 0);
|
|
1809
|
+
}
|
|
1810
|
+
if (!preview?.operationPlan || draft.executableTargets.length === 0)
|
|
1811
|
+
continue;
|
|
1812
|
+
const sourceIdentity = draft.causes[0]?.sourceIdentity
|
|
1813
|
+
?? status.payload.catalog.sources.find((source) => source.id === sourceId)?.identity;
|
|
1814
|
+
if (!sourceIdentity) {
|
|
1815
|
+
throw new TuiOperationBlockedError('taskSourceIdentityMissing', '任务来源身份不可验证。');
|
|
1816
|
+
}
|
|
1817
|
+
const sourceEvidence = {
|
|
1818
|
+
sourceId,
|
|
1819
|
+
sourceIdentity,
|
|
1820
|
+
requestedTargets: draft.executableTargets,
|
|
1821
|
+
causes: draft.causes,
|
|
1822
|
+
preview,
|
|
1823
|
+
executable: true,
|
|
1824
|
+
};
|
|
1825
|
+
sources.push(sourceEvidence);
|
|
1826
|
+
executableTargets.push(...draft.executableTargets);
|
|
1827
|
+
causes.push(...draft.causes);
|
|
1828
|
+
}
|
|
1829
|
+
const canonicalBlocked = [...new Map(blockedTargets.map((target) => [
|
|
1830
|
+
pluginWriteTaskTargetKey(target.target), target,
|
|
1831
|
+
])).values()].sort((left, right) => pluginWriteTaskTargetKey(left.target)
|
|
1832
|
+
.localeCompare(pluginWriteTaskTargetKey(right.target), 'en'));
|
|
1833
|
+
if (executableTargets.length === 0) {
|
|
1834
|
+
throw new TuiOperationBlockedError('taskNoExecutableTargets', '任务没有可安全执行的目标。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1835
|
+
}
|
|
1836
|
+
if (intent.executionPolicy === 'all' && canonicalBlocked.length > 0) {
|
|
1837
|
+
throw new TuiOperationBlockedError('taskHasBlockedTargets', '部分目标需要先处理。请调整选择。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1838
|
+
}
|
|
1839
|
+
await preparation?.reporter.emit('evidence');
|
|
1840
|
+
const evidence = {
|
|
1841
|
+
catalog: {
|
|
1842
|
+
configDigest: configSnapshot.digest,
|
|
1843
|
+
compositeDigest: digest(sources.map((source) => [
|
|
1844
|
+
source.sourceId,
|
|
1845
|
+
source.sourceIdentity,
|
|
1846
|
+
source.preview.operationPlan?.planFingerprint,
|
|
1847
|
+
])),
|
|
1848
|
+
},
|
|
1849
|
+
taskPlan: {
|
|
1850
|
+
goal: intent.goal,
|
|
1851
|
+
requestedTargets: intent.targets,
|
|
1852
|
+
executableTargets: [...executableTargets].sort((left, right) => pluginWriteTaskTargetKey(left).localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1853
|
+
causes: [...new Map(causes.map((cause) => [cause.causeKey, cause])).values()]
|
|
1854
|
+
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en')),
|
|
1855
|
+
blockedTargets: canonicalBlocked,
|
|
1856
|
+
sources,
|
|
1857
|
+
targetFingerprint: statusTargetFingerprint(observedTargets, observedRevisions),
|
|
1858
|
+
planFingerprint: '',
|
|
1859
|
+
},
|
|
1860
|
+
};
|
|
1861
|
+
evidence.taskPlan.planFingerprint = operationEvidenceFingerprint(evidence);
|
|
1862
|
+
taskCatalogOperationsByEvidence.set(evidence, operationContexts);
|
|
1863
|
+
return evidence;
|
|
547
1864
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
1865
|
+
if (intent.kind === 'pluginWriteBatch') {
|
|
1866
|
+
await preparation?.reporter.emit('status');
|
|
1867
|
+
const selection = await resolveBatchGroups(intent, context.env, context.cwd, frozenBatchEvidence?.requestedPluginIds);
|
|
1868
|
+
signal?.throwIfAborted();
|
|
1869
|
+
if (intent.action === 'cleanup') {
|
|
1870
|
+
const groups = [];
|
|
1871
|
+
const reviewedRemovedPlugins = new Set();
|
|
1872
|
+
for (const group of selection.groups) {
|
|
1873
|
+
signal?.throwIfAborted();
|
|
1874
|
+
const requestedPluginIds = group.requestedPluginIds.filter((pluginId) => !reviewedRemovedPlugins.has(pluginId));
|
|
1875
|
+
if (requestedPluginIds.length === 0)
|
|
1876
|
+
continue;
|
|
1877
|
+
const preview = await runBatchWriteGroup({
|
|
1878
|
+
intent,
|
|
1879
|
+
group: { ...group, requestedPluginIds },
|
|
1880
|
+
cwd: context.cwd,
|
|
1881
|
+
runtime: {
|
|
1882
|
+
...context,
|
|
1883
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
1884
|
+
previewOperationPlan: true,
|
|
1885
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1886
|
+
...(preparation ? {
|
|
1887
|
+
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1888
|
+
} : {}),
|
|
1889
|
+
},
|
|
1890
|
+
});
|
|
1891
|
+
for (const pluginId of preview.removedPluginCleanupIds ?? []) {
|
|
1892
|
+
reviewedRemovedPlugins.add(pluginId);
|
|
1893
|
+
}
|
|
1894
|
+
groups.push({ ...group, requestedPluginIds, preview,
|
|
1895
|
+
executable: preview.operationPlan !== undefined });
|
|
1896
|
+
}
|
|
1897
|
+
await preparation?.reporter.emit('evidence');
|
|
1898
|
+
signal?.throwIfAborted();
|
|
1899
|
+
const evidence = {
|
|
1900
|
+
catalog: {
|
|
1901
|
+
configDigest: 'cleanup-offline',
|
|
1902
|
+
compositeDigest: digest(groups.map((group) => [
|
|
1903
|
+
group.groupId,
|
|
1904
|
+
group.preview.operationPlan?.planFingerprint,
|
|
1905
|
+
])),
|
|
1906
|
+
},
|
|
1907
|
+
requestedPluginIds: selection.requestedPluginIds,
|
|
1908
|
+
groups,
|
|
1909
|
+
};
|
|
1910
|
+
return evidence;
|
|
1911
|
+
}
|
|
1912
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1913
|
+
const groups = [];
|
|
1914
|
+
const ownsObservationSession = !codexObservationSession
|
|
1915
|
+
&& selection.groups.some((group) => {
|
|
1916
|
+
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1917
|
+
return reviewed?.executable !== false
|
|
1918
|
+
&& (group.platform === 'codex' || group.platform === 'both');
|
|
1919
|
+
});
|
|
1920
|
+
const observationSession = codexObservationSession ?? (ownsObservationSession
|
|
1921
|
+
? await createCodexNativeObservationSession({
|
|
1922
|
+
exactCwd: context.cwd,
|
|
1923
|
+
env: context.env,
|
|
1924
|
+
runner: context.nativeCliRunner,
|
|
1925
|
+
})
|
|
1926
|
+
: undefined);
|
|
1927
|
+
try {
|
|
1928
|
+
await preparation?.reporter.emit('catalog');
|
|
1929
|
+
for (const group of selection.groups) {
|
|
1930
|
+
signal?.throwIfAborted();
|
|
1931
|
+
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1932
|
+
if (reviewed?.executable === false) {
|
|
1933
|
+
groups.push({ ...group, preview: reviewed.preview, executable: false });
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
let catalogOperation;
|
|
1937
|
+
try {
|
|
1938
|
+
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1939
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, [group.sourceId])
|
|
1940
|
+
: await resolveCatalogOperationContext({
|
|
1941
|
+
sourceIds: [group.sourceId],
|
|
1942
|
+
env: context.env,
|
|
1943
|
+
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1944
|
+
...(signal ? { signal } : {}),
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1947
|
+
catch {
|
|
1948
|
+
catalogOperation = undefined;
|
|
1949
|
+
}
|
|
1950
|
+
const preview = await runBatchWriteGroup({
|
|
1951
|
+
intent,
|
|
1952
|
+
group,
|
|
1953
|
+
cwd: context.cwd,
|
|
1954
|
+
runtime: {
|
|
1955
|
+
...context,
|
|
1956
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
1957
|
+
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1958
|
+
...(catalogOperation ? {
|
|
1959
|
+
catalogOperationContext: catalogOperation,
|
|
1960
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1961
|
+
} : {}),
|
|
1962
|
+
previewOperationPlan: true,
|
|
1963
|
+
...(observationSession ? { codexObservationSession: observationSession } : {}),
|
|
1964
|
+
...(preparation ? {
|
|
1965
|
+
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1966
|
+
} : {}),
|
|
1967
|
+
},
|
|
1968
|
+
});
|
|
1969
|
+
groups.push({ ...group, preview, executable: preview.operationPlan !== undefined });
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
finally {
|
|
1973
|
+
if (ownsObservationSession)
|
|
1974
|
+
await observationSession?.dispose();
|
|
566
1975
|
}
|
|
1976
|
+
await preparation?.reporter.emit('evidence');
|
|
1977
|
+
signal?.throwIfAborted();
|
|
1978
|
+
const merged = mergeWriteCommandResults(groups.map((group) => group.preview));
|
|
1979
|
+
const targetVersions = groups.flatMap((group) => {
|
|
1980
|
+
if (group.platform === 'both')
|
|
1981
|
+
return [];
|
|
1982
|
+
const platform = group.platform;
|
|
1983
|
+
return group.requestedPluginIds.map((pluginId) => {
|
|
1984
|
+
const live = flattenStatusTargets(sessionStatusResult?.payload.targets ?? []).find((target) => target.pluginId === pluginId && target.platform === platform
|
|
1985
|
+
&& (target.scope === group.scope || statusTargetIntent(target).scopes.includes(group.scope)
|
|
1986
|
+
|| platform === 'codex' && statusTargetActivation(target)?.[group.scope] === true));
|
|
1987
|
+
const revision = sessionStatusResult?.targetRevisions?.find((target) => target.pluginId === pluginId && target.platform === platform
|
|
1988
|
+
&& target.scope === group.scope)?.revision;
|
|
1989
|
+
const activation = live ? statusTargetActivation(live) : undefined;
|
|
1990
|
+
const ownership = live && live.scope === group.scope
|
|
1991
|
+
? statusTargetOwnershipState(live) : 'absent';
|
|
1992
|
+
const present = ownership === 'present' || ownership === 'partial'
|
|
1993
|
+
|| platform === 'codex' && activation?.[group.scope] === true;
|
|
1994
|
+
const planned = live ? statusTargetIntent(live).scopes.includes(group.scope) : false;
|
|
1995
|
+
return {
|
|
1996
|
+
pluginId,
|
|
1997
|
+
platform,
|
|
1998
|
+
scope: group.scope,
|
|
1999
|
+
presence: present ? 'installed'
|
|
2000
|
+
: ownership === 'absent' && planned ? 'new' : 'unknown',
|
|
2001
|
+
...(revision?.installedVersion ? { installedVersion: revision.installedVersion } : {}),
|
|
2002
|
+
...(revision?.availableVersion ? { availableVersion: revision.availableVersion } : {}),
|
|
2003
|
+
};
|
|
2004
|
+
});
|
|
2005
|
+
});
|
|
2006
|
+
const evidence = {
|
|
2007
|
+
catalog: {
|
|
2008
|
+
configDigest: configSnapshot.digest,
|
|
2009
|
+
compositeDigest: sessionStatusResult?.payload.catalog.compositeDigest
|
|
2010
|
+
?? digest(groups.map((group) => [group.groupId, group.preview.operationPlan?.planFingerprint])),
|
|
2011
|
+
},
|
|
2012
|
+
requestedPluginIds: selection.requestedPluginIds,
|
|
2013
|
+
...(selection.projectIntentDigest
|
|
2014
|
+
? { projectIntentDigest: selection.projectIntentDigest }
|
|
2015
|
+
: {}),
|
|
2016
|
+
groups,
|
|
2017
|
+
targetVersions: [...new Map(targetVersions.map((target) => [[
|
|
2018
|
+
target.pluginId, target.platform, target.scope,
|
|
2019
|
+
].join('\0'), target])).values()].sort((left, right) => [
|
|
2020
|
+
left.pluginId, left.platform, left.scope,
|
|
2021
|
+
].join('\0').localeCompare([
|
|
2022
|
+
right.pluginId, right.platform, right.scope,
|
|
2023
|
+
].join('\0'), 'en')),
|
|
2024
|
+
...(merged.dependencyPlan ? { dependencyPlan: merged.dependencyPlan } : {}),
|
|
2025
|
+
...(merged.codexReconcilePlans ? { codexReconcilePlans: merged.codexReconcilePlans } : {}),
|
|
2026
|
+
...(merged.operationPlan ? { operationPlan: merged.operationPlan } : {}),
|
|
2027
|
+
};
|
|
2028
|
+
return evidence;
|
|
567
2029
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
2030
|
+
if (intent.kind === 'pluginWrite' && intent.action === 'cleanup') {
|
|
2031
|
+
const preview = await cleanupWriteCommand([intent.pluginId], {
|
|
2032
|
+
cwd: context.cwd,
|
|
2033
|
+
platform: intent.platform,
|
|
2034
|
+
scope: intent.scope,
|
|
2035
|
+
force: intent.force === true,
|
|
2036
|
+
}, {
|
|
2037
|
+
...context,
|
|
2038
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
2039
|
+
previewOperationPlan: true,
|
|
2040
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2041
|
+
...(preparation ? {
|
|
2042
|
+
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
2043
|
+
} : {}),
|
|
2044
|
+
});
|
|
2045
|
+
signal?.throwIfAborted();
|
|
2046
|
+
if (!preview.operationPlan) {
|
|
2047
|
+
throw new TuiOperationBlockedError('cleanupPlanUnavailable', '无法建立可信的清理计划。');
|
|
2048
|
+
}
|
|
2049
|
+
const offlineCleanupPlans = preview.offlineCleanupPlans ?? [];
|
|
2050
|
+
await preparation?.reporter.emit('evidence');
|
|
2051
|
+
return {
|
|
2052
|
+
catalog: {
|
|
2053
|
+
configDigest: 'cleanup-offline',
|
|
2054
|
+
compositeDigest: preview.operationPlan.planFingerprint,
|
|
2055
|
+
},
|
|
2056
|
+
operationPlan: preview.operationPlan,
|
|
2057
|
+
offlineCleanupPlans,
|
|
2058
|
+
...(preview.removedPluginCleanupIds
|
|
2059
|
+
? { removedPluginCleanupIds: preview.removedPluginCleanupIds }
|
|
2060
|
+
: {}),
|
|
2061
|
+
...(offlineCleanupPlans.length === 1
|
|
2062
|
+
? { offlineCleanup: offlineCleanupPlans[0] }
|
|
2063
|
+
: {}),
|
|
2064
|
+
targetFingerprint: preview.operationPlan.planFingerprint,
|
|
2065
|
+
};
|
|
572
2066
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
2067
|
+
const packageInfo = await readCliPackageInfo();
|
|
2068
|
+
let catalogOperation;
|
|
2069
|
+
let catalogOperationError;
|
|
2070
|
+
if (intent.kind !== 'portableInstall') {
|
|
2071
|
+
try {
|
|
2072
|
+
const sourceIds = [parsePluginId(intent.pluginId).sourceId];
|
|
2073
|
+
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
2074
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
|
|
2075
|
+
: await resolveCatalogOperationContext({
|
|
2076
|
+
sourceIds,
|
|
2077
|
+
env: context.env,
|
|
2078
|
+
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
2079
|
+
...(signal ? { signal } : {}),
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
catch (error) {
|
|
2083
|
+
catalogOperationError = error;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
if (intent.kind === 'portableInstall') {
|
|
2087
|
+
await preparation?.reporter.emit('portable');
|
|
2088
|
+
if (intent.retry
|
|
2089
|
+
&& (intent.retry.receipt.plugin.id !== intent.pluginId
|
|
2090
|
+
|| intent.retry.receipt.scope !== intent.scope)) {
|
|
2091
|
+
throw operationReviewStale('Portable 重试结果与当前插件或范围不匹配。');
|
|
2092
|
+
}
|
|
2093
|
+
const portableSourceId = parsePluginId(intent.pluginId).sourceId;
|
|
2094
|
+
const portableCatalogOperation = sessionCatalogOperationContext
|
|
2095
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, [portableSourceId])
|
|
2096
|
+
: undefined;
|
|
2097
|
+
const portable = intent.retry
|
|
2098
|
+
? await preparePortableRetry({
|
|
2099
|
+
receipt: intent.retry.receipt,
|
|
2100
|
+
cwd: context.cwd,
|
|
2101
|
+
env: context.env,
|
|
2102
|
+
cliVersion: packageInfo.version,
|
|
2103
|
+
signal,
|
|
2104
|
+
...(portableCatalogOperation
|
|
2105
|
+
? { catalogOperationContext: portableCatalogOperation }
|
|
2106
|
+
: {}),
|
|
2107
|
+
})
|
|
2108
|
+
: await preparePortableInstall({
|
|
2109
|
+
selector: intent.pluginId,
|
|
2110
|
+
scope: intent.scope,
|
|
2111
|
+
...(intent.selectedSkills.length > 0 ? { requestedSkills: intent.selectedSkills } : {}),
|
|
2112
|
+
...(intent.requestedAgents ? { requestedAgents: intent.requestedAgents } : {}),
|
|
2113
|
+
cwd: context.cwd,
|
|
2114
|
+
env: context.env,
|
|
2115
|
+
cliVersion: packageInfo.version,
|
|
2116
|
+
signal,
|
|
2117
|
+
...(portableCatalogOperation
|
|
2118
|
+
? { catalogOperationContext: portableCatalogOperation }
|
|
2119
|
+
: {}),
|
|
2120
|
+
});
|
|
2121
|
+
signal?.throwIfAborted();
|
|
2122
|
+
await preparation?.reporter.emit('evidence');
|
|
2123
|
+
signal?.throwIfAborted();
|
|
2124
|
+
const evidence = {
|
|
2125
|
+
catalog: {
|
|
2126
|
+
configDigest: portable.configDigest,
|
|
2127
|
+
compositeDigest: portable.evidenceDigest,
|
|
2128
|
+
...(portable.generationSetDigest
|
|
2129
|
+
? { generationSetDigest: portable.generationSetDigest }
|
|
2130
|
+
: {}),
|
|
595
2131
|
},
|
|
2132
|
+
portable,
|
|
2133
|
+
};
|
|
2134
|
+
if (catalogOperation)
|
|
2135
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2136
|
+
return evidence;
|
|
2137
|
+
}
|
|
2138
|
+
if (preparation && intent.kind === 'configSave') {
|
|
2139
|
+
if (!sessionStatusResult || !catalogOperation) {
|
|
2140
|
+
throw operationReviewStale('当前 TUI session 没有可复用的设置证据。');
|
|
596
2141
|
}
|
|
597
|
-
|
|
2142
|
+
await preparation.reporter.emit('config');
|
|
2143
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
2144
|
+
signal?.throwIfAborted();
|
|
2145
|
+
if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
|
|
2146
|
+
throw operationReviewStale('准备设置确认期间来源配置发生变化。');
|
|
2147
|
+
}
|
|
2148
|
+
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope);
|
|
2149
|
+
const preparedCatalogOperation = catalogOperation;
|
|
2150
|
+
const selected = await withResolvedCatalogPlugin({
|
|
598
2151
|
selector: intent.pluginId,
|
|
599
2152
|
cliVersion: packageInfo.version,
|
|
600
|
-
freshnessPolicy,
|
|
601
2153
|
configSnapshot,
|
|
602
2154
|
env: context.env,
|
|
2155
|
+
operationContext: preparedCatalogOperation,
|
|
603
2156
|
...(signal ? { signal } : {}),
|
|
604
2157
|
consume: async ({ plugin, source }) => {
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
2158
|
+
if (!plugin.manifest.config) {
|
|
2159
|
+
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2160
|
+
}
|
|
2161
|
+
if (intent.kind === 'configSave') {
|
|
610
2162
|
await coercePluginConfigUpdates({
|
|
611
|
-
schema: plugin.manifest.config.
|
|
2163
|
+
schema: plugin.manifest.config.fields,
|
|
612
2164
|
updates: intent.updates ?? {},
|
|
613
2165
|
cwd: context.cwd,
|
|
614
2166
|
});
|
|
615
2167
|
for (const key of intent.removeKeys ?? []) {
|
|
616
|
-
if (!plugin.manifest.config.
|
|
2168
|
+
if (!plugin.manifest.config.fields[key]) {
|
|
617
2169
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
618
2170
|
}
|
|
619
2171
|
}
|
|
620
2172
|
}
|
|
2173
|
+
const impact = intent.retryApply ? undefined : await analyzePluginConfigMutationImpact({
|
|
2174
|
+
plugin,
|
|
2175
|
+
scope: intent.scope,
|
|
2176
|
+
cwd: context.cwd,
|
|
2177
|
+
env: context.env,
|
|
2178
|
+
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
2179
|
+
mutation: {
|
|
2180
|
+
kind: 'patch',
|
|
2181
|
+
updates: intent.updates,
|
|
2182
|
+
removeKeys: intent.removeKeys,
|
|
2183
|
+
},
|
|
2184
|
+
});
|
|
2185
|
+
const affectedTargets = intent.retryApply
|
|
2186
|
+
? configImpactVisibleTargets(plugin.manifest.name, relevantTargets)
|
|
2187
|
+
: impact.affectedTargets;
|
|
2188
|
+
const commandPackages = impact?.affectedCommandPackages
|
|
2189
|
+
?? (await discoverInstalledPluginManagedCommandPackages({ plugin, scope: intent.scope,
|
|
2190
|
+
cwd: context.cwd, env: context.env ?? process.env, platformTargets: affectedTargets })).packages;
|
|
621
2191
|
return {
|
|
622
2192
|
pluginName: plugin.manifest.name,
|
|
623
2193
|
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
2194
|
+
impact,
|
|
2195
|
+
commandPackages,
|
|
2196
|
+
preApplyAuthorization: configIntentRequestsApply(intent)
|
|
2197
|
+
? (await resolveConfigPreApplyAuthorization({
|
|
2198
|
+
plugin,
|
|
2199
|
+
scope: intent.scope,
|
|
2200
|
+
cwd: context.cwd,
|
|
2201
|
+
env: context.env,
|
|
2202
|
+
applyTargets: affectedTargets,
|
|
2203
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
2204
|
+
rebuildRuntime: {
|
|
2205
|
+
catalogOperationContext: preparedCatalogOperation,
|
|
2206
|
+
expectedGenerationSetDigest: preparedCatalogOperation.generationSetDigest,
|
|
2207
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
2208
|
+
},
|
|
2209
|
+
})).targets
|
|
2210
|
+
: [],
|
|
2211
|
+
sharedNativeRenderTargets: intent.retryApply
|
|
2212
|
+
? configRetrySharedNativeRenderTargets(plugin)
|
|
2213
|
+
: impact.sharedNativeRenderTargets,
|
|
624
2214
|
};
|
|
625
2215
|
},
|
|
626
2216
|
});
|
|
2217
|
+
const retryStore = intent.retryApply ? await readPluginConfigStore({
|
|
2218
|
+
sourceId: selected.source.sourceId,
|
|
2219
|
+
sourceIdentity: selected.source.sourceIdentity,
|
|
2220
|
+
logicalIdentity: selected.source.logicalIdentity,
|
|
2221
|
+
pluginName: selected.pluginName,
|
|
2222
|
+
scope: intent.scope,
|
|
2223
|
+
cwd: context.cwd,
|
|
2224
|
+
env: context.env,
|
|
2225
|
+
}) : undefined;
|
|
2226
|
+
signal?.throwIfAborted();
|
|
2227
|
+
const relevantRevisions = (sessionStatusResult.targetRevisions ?? []).filter((revision) => relevantTargets.some((target) => target.platform === revision.platform
|
|
2228
|
+
&& target.scope === revision.scope
|
|
2229
|
+
&& target.pluginId === revision.pluginId));
|
|
2230
|
+
await preparation.reporter.emit('evidence');
|
|
2231
|
+
const evidence = {
|
|
2232
|
+
catalog: {
|
|
2233
|
+
configDigest: sessionStatusResult.payload.catalog.configDigest,
|
|
2234
|
+
compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
|
|
2235
|
+
...(catalogOperation ? { generationSetDigest: catalogOperation.generationSetDigest } : {}),
|
|
2236
|
+
},
|
|
2237
|
+
...(selected.source ? { source: selected.source } : {}),
|
|
2238
|
+
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
2239
|
+
config: {
|
|
2240
|
+
scope: intent.scope,
|
|
2241
|
+
...(selected.source?.manifestDigest ? { schemaIdentity: selected.source.manifestDigest } : {}),
|
|
2242
|
+
storeDigest: selected.impact?.storeDigest
|
|
2243
|
+
?? digest({ exists: retryStore?.exists, rawDigest: retryStore?.rawDigest }),
|
|
2244
|
+
impactState: selected.impact?.impactState ?? 'known',
|
|
2245
|
+
...(selected.impact?.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
2246
|
+
...(selected.commandPackages.length ? { commandPackages: selected.commandPackages } : {}),
|
|
2247
|
+
affectedTargets: (selected.impact?.affectedTargets
|
|
2248
|
+
?? relevantTargets).map(({ platform, scope }) => ({ platform, scope })),
|
|
2249
|
+
...(selected.preApplyAuthorization.length > 0
|
|
2250
|
+
? { preApplyAuthorization: selected.preApplyAuthorization }
|
|
2251
|
+
: {}),
|
|
2252
|
+
...(selected.sharedNativeRenderTargets.length > 0
|
|
2253
|
+
? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
|
|
2254
|
+
: {}),
|
|
2255
|
+
},
|
|
2256
|
+
};
|
|
2257
|
+
if (catalogOperation)
|
|
2258
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2259
|
+
return evidence;
|
|
2260
|
+
}
|
|
2261
|
+
await preparation?.reporter.emit('status');
|
|
2262
|
+
const statusCatalogOperation = catalogOperation
|
|
2263
|
+
?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
|
|
2264
|
+
const status = await statusCommandV8({
|
|
2265
|
+
plugins: [...new Set([intent.pluginId,
|
|
2266
|
+
...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
|
|
2267
|
+
? [intent.originIssue.consumerPluginId] : [])])],
|
|
2268
|
+
...('platform' in intent ? { platform: intent.platform } : {}),
|
|
2269
|
+
...(intent.kind !== 'pluginWrite' && intent.scope === 'project'
|
|
2270
|
+
? { scope: intent.scope }
|
|
2271
|
+
: {}),
|
|
2272
|
+
cwd: context.cwd,
|
|
2273
|
+
env: context.env,
|
|
2274
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
2275
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2276
|
+
cliVersion: packageInfo.version,
|
|
2277
|
+
...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
|
|
2278
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
2279
|
+
});
|
|
2280
|
+
signal?.throwIfAborted();
|
|
2281
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
2282
|
+
signal?.throwIfAborted();
|
|
2283
|
+
if (configSnapshot.digest !== status.payload.catalog.configDigest) {
|
|
2284
|
+
throw operationReviewStale('准备确认信息期间来源配置发生变化。');
|
|
2285
|
+
}
|
|
2286
|
+
if (catalogOperationError)
|
|
2287
|
+
throw catalogOperationError;
|
|
2288
|
+
if (!catalogOperation) {
|
|
2289
|
+
catalogOperation = await resolveCatalogOperationContext({
|
|
2290
|
+
sourceIds: [parsePluginId(intent.pluginId).sourceId],
|
|
2291
|
+
configSnapshot,
|
|
2292
|
+
env: context.env,
|
|
2293
|
+
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
2294
|
+
...(signal ? { signal } : {}),
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
let dependencyPlan;
|
|
2298
|
+
let codexReconcilePlans;
|
|
2299
|
+
let operationPlan;
|
|
2300
|
+
await preparation?.reporter.emit('catalog');
|
|
2301
|
+
const configVisibleTargets = intent.kind === 'configSave'
|
|
2302
|
+
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.scope)
|
|
2303
|
+
: [];
|
|
2304
|
+
const selected = await withResolvedCatalogPlugin({
|
|
2305
|
+
selector: intent.pluginId,
|
|
2306
|
+
cliVersion: packageInfo.version,
|
|
2307
|
+
configSnapshot,
|
|
2308
|
+
env: context.env,
|
|
2309
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
2310
|
+
...(signal ? { signal } : {}),
|
|
2311
|
+
consume: async ({ plugin, source }) => {
|
|
2312
|
+
if (preparation?.validateIntent && intent.kind === 'configSave') {
|
|
2313
|
+
await preparation.reporter.emit('config');
|
|
2314
|
+
if (!plugin.manifest.config) {
|
|
2315
|
+
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2316
|
+
}
|
|
2317
|
+
await coercePluginConfigUpdates({
|
|
2318
|
+
schema: plugin.manifest.config.fields,
|
|
2319
|
+
updates: intent.updates ?? {},
|
|
2320
|
+
cwd: context.cwd,
|
|
2321
|
+
});
|
|
2322
|
+
for (const key of intent.removeKeys ?? []) {
|
|
2323
|
+
if (!plugin.manifest.config.fields[key]) {
|
|
2324
|
+
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
const impact = intent.kind === 'configSave' && !intent.retryApply
|
|
2329
|
+
? await analyzePluginConfigMutationImpact({
|
|
2330
|
+
plugin,
|
|
2331
|
+
scope: intent.scope,
|
|
2332
|
+
cwd: context.cwd,
|
|
2333
|
+
env: context.env,
|
|
2334
|
+
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
2335
|
+
mutation: {
|
|
2336
|
+
kind: 'patch',
|
|
2337
|
+
updates: intent.updates,
|
|
2338
|
+
removeKeys: intent.removeKeys,
|
|
2339
|
+
},
|
|
2340
|
+
})
|
|
2341
|
+
: undefined;
|
|
2342
|
+
return {
|
|
2343
|
+
pluginName: plugin.manifest.name,
|
|
2344
|
+
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
2345
|
+
impact,
|
|
2346
|
+
commandPackages: impact?.affectedCommandPackages
|
|
2347
|
+
?? (intent.kind === 'configSave' ? (await discoverInstalledPluginManagedCommandPackages({
|
|
2348
|
+
plugin, scope: intent.scope, cwd: context.cwd, env: context.env ?? process.env,
|
|
2349
|
+
platformTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
2350
|
+
})).packages : []),
|
|
2351
|
+
preApplyAuthorization: intent.kind === 'configSave'
|
|
2352
|
+
&& configIntentRequestsApply(intent)
|
|
2353
|
+
? (await resolveConfigPreApplyAuthorization({
|
|
2354
|
+
plugin,
|
|
2355
|
+
scope: intent.scope,
|
|
2356
|
+
cwd: context.cwd,
|
|
2357
|
+
env: context.env,
|
|
2358
|
+
applyTargets: intent.retryApply
|
|
2359
|
+
? configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets)
|
|
2360
|
+
: impact.affectedTargets,
|
|
2361
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
2362
|
+
rebuildRuntime: {
|
|
2363
|
+
...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
|
|
2364
|
+
...(catalogOperation?.generationSetDigest
|
|
2365
|
+
? { expectedGenerationSetDigest: catalogOperation.generationSetDigest }
|
|
2366
|
+
: {}),
|
|
2367
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
2368
|
+
},
|
|
2369
|
+
})).targets
|
|
2370
|
+
: [],
|
|
2371
|
+
sharedNativeRenderTargets: intent.kind === 'configSave'
|
|
2372
|
+
? intent.retryApply
|
|
2373
|
+
? configRetrySharedNativeRenderTargets(plugin)
|
|
2374
|
+
: impact.sharedNativeRenderTargets
|
|
2375
|
+
: [],
|
|
2376
|
+
};
|
|
2377
|
+
},
|
|
2378
|
+
});
|
|
2379
|
+
if (intent.kind === 'pluginWrite' && intent.expectedUpdate) {
|
|
2380
|
+
const revision = status.targetRevisions.find((entry) => entry.pluginId === intent.pluginId && entry.platform === intent.platform
|
|
2381
|
+
&& entry.scope === intent.scope);
|
|
2382
|
+
const generation = catalogOperation?.generationSet.sources.find((entry) => entry.sourceId === selected.source.sourceId);
|
|
2383
|
+
if (intent.action !== 'sync'
|
|
2384
|
+
|| !('pluginVersion' in selected.source)
|
|
2385
|
+
|| selected.source.sourceIdentity !== intent.expectedUpdate.sourceIdentity
|
|
2386
|
+
|| selected.source.pluginVersion !== intent.expectedUpdate.activeVersion
|
|
2387
|
+
|| generation?.storeKey !== intent.expectedUpdate.generationKey
|
|
2388
|
+
|| revision?.revision.installedVersion !== intent.expectedUpdate.installedVersion
|
|
2389
|
+
|| revision?.revision.availableVersion !== intent.expectedUpdate.activeVersion
|
|
2390
|
+
|| revision.revision.state !== 'upgradeAvailable') {
|
|
2391
|
+
throw operationReviewStale('插件更新版本或 Active Catalog 证据已变化。');
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
let codexManager;
|
|
2395
|
+
let resolvedCodexManager;
|
|
627
2396
|
const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
|
|
628
|
-
if (intent.kind === 'pluginWrite'
|
|
629
|
-
&& hasDeclaredDependencies
|
|
630
|
-
&& (intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')) {
|
|
2397
|
+
if (intent.kind === 'pluginWrite') {
|
|
631
2398
|
const command = {
|
|
632
2399
|
install: installWriteCommand,
|
|
633
|
-
|
|
2400
|
+
refreshAndSync: syncWriteCommand,
|
|
634
2401
|
reinstall: reinstallWriteCommand,
|
|
2402
|
+
uninstall: uninstallWriteCommand,
|
|
2403
|
+
cleanup: cleanupWriteCommand,
|
|
2404
|
+
sync: syncWriteCommand,
|
|
635
2405
|
}[intent.action];
|
|
636
2406
|
const preview = await command([intent.pluginId], {
|
|
637
2407
|
cwd: context.cwd,
|
|
638
2408
|
platform: intent.platform,
|
|
639
2409
|
scope: intent.scope,
|
|
2410
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
2411
|
+
? {}
|
|
2412
|
+
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
2413
|
+
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
2414
|
+
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
2415
|
+
: {}),
|
|
2416
|
+
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
640
2417
|
}, {
|
|
641
2418
|
...context,
|
|
2419
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
642
2420
|
expectedCatalogConfigDigest: status.payload.catalog.configDigest,
|
|
643
|
-
|
|
2421
|
+
...(catalogOperation ? {
|
|
2422
|
+
catalogOperationContext: catalogOperation,
|
|
2423
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
2424
|
+
} : {}),
|
|
2425
|
+
previewOperationPlan: true,
|
|
2426
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
644
2427
|
...(preparation ? {
|
|
645
|
-
previewCatalogFreshnessPolicy: 'cacheFirst',
|
|
646
2428
|
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
647
2429
|
} : {}),
|
|
648
2430
|
});
|
|
649
2431
|
signal?.throwIfAborted();
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
2432
|
+
codexReconcilePlans = preview?.codexReconcilePlans;
|
|
2433
|
+
operationPlan = preview?.operationPlan;
|
|
2434
|
+
if (hasDeclaredDependencies
|
|
2435
|
+
&& (intent.action === 'install'
|
|
2436
|
+
|| intent.action === 'refreshAndSync'
|
|
2437
|
+
|| intent.action === 'reinstall'
|
|
2438
|
+
|| intent.action === 'sync')) {
|
|
2439
|
+
dependencyPlan = preview.dependencyPlan;
|
|
2440
|
+
if (!dependencyPlan) {
|
|
2441
|
+
throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
|
|
2442
|
+
}
|
|
2443
|
+
const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
|
|
2444
|
+
if (blocker) {
|
|
2445
|
+
const code = blocker.errorCode ?? 'dependencyNotReady';
|
|
2446
|
+
const reason = code === 'dependencyScopeConflict' || code === 'codexExtensionScopeConflict'
|
|
2447
|
+
? '这个依赖的安装位置存在冲突,需要先选择保留的位置。'
|
|
2448
|
+
: code === 'artifactCollision' || code === 'exclusiveOwnerEvidenceInvalid'
|
|
2449
|
+
? '这个依赖的安装位置被其他内容占用,需要先检查并处理冲突。'
|
|
2450
|
+
: '这个依赖尚不可用,需要先检查并恢复后再安装。';
|
|
2451
|
+
throw new TuiOperationBlockedError(code, `${blocker.pluginId}:${reason}`, [{
|
|
2452
|
+
id: `dependency:${blocker.pluginId}:${blocker.platform}:${blocker.resolvedScope}`,
|
|
2453
|
+
code, stage: 'dependencyPlan', safeMessage: reason,
|
|
2454
|
+
target: { pluginId: blocker.pluginId, platform: blocker.platform, scope: blocker.resolvedScope },
|
|
2455
|
+
causes: [], detailLines: [],
|
|
2456
|
+
}]);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
const managerPreview = preview?.codexManagerPreview;
|
|
2460
|
+
if (managerPreview?.state === 'blocked') {
|
|
2461
|
+
const message = managerPreview.code === 'codexSystemCliUnsupported'
|
|
2462
|
+
? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
2463
|
+
: 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
|
|
2464
|
+
throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
|
|
653
2465
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
2466
|
+
if (managerPreview) {
|
|
2467
|
+
if (managerPreview.state === 'system' || managerPreview.state === 'cached') {
|
|
2468
|
+
resolvedCodexManager = managerPreview.manager;
|
|
2469
|
+
}
|
|
2470
|
+
codexManager = {
|
|
2471
|
+
state: managerPreview.state,
|
|
2472
|
+
packageName: MANAGED_CODEX_PACKAGE_NAME,
|
|
2473
|
+
version: managerPreview.state === 'requires-install'
|
|
2474
|
+
? MANAGED_CODEX_VERSION
|
|
2475
|
+
: managerPreview.manager.version,
|
|
2476
|
+
};
|
|
657
2477
|
}
|
|
658
2478
|
}
|
|
659
2479
|
await preparation?.reporter.emit('evidence');
|
|
660
2480
|
signal?.throwIfAborted();
|
|
2481
|
+
const evidenceTargets = intent.kind === 'configSave'
|
|
2482
|
+
? configVisibleTargets
|
|
2483
|
+
: flattenStatusTargets(status.payload.targets);
|
|
2484
|
+
const evidenceRevisions = intent.kind === 'configSave'
|
|
2485
|
+
? (status.targetRevisions ?? []).filter((revision) => evidenceTargets.some((target) => target.platform === revision.platform
|
|
2486
|
+
&& target.scope === revision.scope
|
|
2487
|
+
&& target.pluginId === revision.pluginId))
|
|
2488
|
+
: (status.targetRevisions ?? []);
|
|
661
2489
|
const evidence = {
|
|
662
2490
|
catalog: {
|
|
663
2491
|
configDigest: status.payload.catalog.configDigest,
|
|
664
2492
|
compositeDigest: status.payload.catalog.compositeDigest,
|
|
2493
|
+
...(catalogOperation
|
|
2494
|
+
? { generationSetDigest: catalogOperation.generationSetDigest }
|
|
2495
|
+
: {}),
|
|
665
2496
|
},
|
|
666
2497
|
source: selected.source,
|
|
667
2498
|
...(dependencyPlan ? { dependencyPlan } : {}),
|
|
668
|
-
|
|
2499
|
+
...(codexReconcilePlans ? { codexReconcilePlans } : {}),
|
|
2500
|
+
...(operationPlan ? { operationPlan } : {}),
|
|
2501
|
+
...(codexManager ? { codexManager } : {}),
|
|
2502
|
+
targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
|
|
669
2503
|
};
|
|
670
|
-
if (intent.kind === 'configSave'
|
|
671
|
-
const
|
|
2504
|
+
if (intent.kind === 'configSave') {
|
|
2505
|
+
const impact = 'impact' in selected ? selected.impact : undefined;
|
|
2506
|
+
const retryStore = intent.retryApply ? await readPluginConfigStore({
|
|
672
2507
|
sourceId: selected.source.sourceId,
|
|
673
2508
|
sourceIdentity: selected.source.sourceIdentity,
|
|
2509
|
+
...('logicalIdentity' in selected.source
|
|
2510
|
+
? { logicalIdentity: selected.source.logicalIdentity }
|
|
2511
|
+
: {}),
|
|
674
2512
|
pluginName: selected.pluginName,
|
|
675
2513
|
scope: intent.scope,
|
|
676
|
-
projectLayer: intent.projectLayer,
|
|
677
2514
|
cwd: context.cwd,
|
|
678
2515
|
env: context.env,
|
|
679
|
-
});
|
|
2516
|
+
}) : undefined;
|
|
680
2517
|
signal?.throwIfAborted();
|
|
681
2518
|
evidence.config = {
|
|
682
2519
|
scope: intent.scope,
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
2520
|
+
...('manifestDigest' in selected.source
|
|
2521
|
+
? { schemaIdentity: selected.source.manifestDigest }
|
|
2522
|
+
: {}),
|
|
2523
|
+
storeDigest: impact?.storeDigest
|
|
2524
|
+
?? digest({ exists: retryStore?.exists, rawDigest: retryStore?.rawDigest }),
|
|
2525
|
+
impactState: impact?.impactState ?? 'known',
|
|
2526
|
+
...(impact?.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
2527
|
+
...('commandPackages' in selected && selected.commandPackages.length
|
|
2528
|
+
? { commandPackages: selected.commandPackages } : {}),
|
|
2529
|
+
affectedTargets: (impact?.affectedTargets
|
|
2530
|
+
?? evidenceTargets).map(({ platform, scope }) => ({ platform, scope })),
|
|
2531
|
+
...(intent.scope === 'project' ? {
|
|
2532
|
+
shareability: await inspectConfigShareability({
|
|
2533
|
+
sourceId: selected.source.sourceId,
|
|
2534
|
+
pluginName: selected.pluginName,
|
|
2535
|
+
scope: intent.scope,
|
|
2536
|
+
cwd: context.cwd,
|
|
2537
|
+
env: context.env,
|
|
2538
|
+
}),
|
|
2539
|
+
} : {}),
|
|
2540
|
+
...('sharedNativeRenderTargets' in selected
|
|
2541
|
+
&& selected.sharedNativeRenderTargets.length > 0
|
|
2542
|
+
? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
|
|
2543
|
+
: {}),
|
|
2544
|
+
...('preApplyAuthorization' in selected
|
|
2545
|
+
&& selected.preApplyAuthorization.length > 0
|
|
2546
|
+
? { preApplyAuthorization: selected.preApplyAuthorization }
|
|
2547
|
+
: {}),
|
|
686
2548
|
};
|
|
687
2549
|
}
|
|
2550
|
+
if (catalogOperation)
|
|
2551
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2552
|
+
if (resolvedCodexManager)
|
|
2553
|
+
codexManagerByEvidence.set(evidence, resolvedCodexManager);
|
|
688
2554
|
return evidence;
|
|
689
2555
|
};
|
|
690
2556
|
const dependencies = {
|
|
2557
|
+
beginCatalogSession(sessionOptions) {
|
|
2558
|
+
sessionCatalogOperationContext = undefined;
|
|
2559
|
+
if (sessionOptions?.reuseValidatedGenerations !== true) {
|
|
2560
|
+
validatedStoreEntries.clear();
|
|
2561
|
+
validatingStoreEntries.clear();
|
|
2562
|
+
}
|
|
2563
|
+
},
|
|
691
2564
|
loadStatus,
|
|
692
|
-
|
|
2565
|
+
loadPluginContent,
|
|
2566
|
+
async probeCatalogFreshness(sourceId, freshnessOptions, signal) {
|
|
693
2567
|
signal.throwIfAborted();
|
|
2568
|
+
const packageInfo = await readCliPackageInfo();
|
|
2569
|
+
return probeCatalogSourceFreshness({
|
|
2570
|
+
sourceId,
|
|
2571
|
+
cliVersion: packageInfo.version,
|
|
2572
|
+
env: context.env,
|
|
2573
|
+
signal,
|
|
2574
|
+
fullValidation: freshnessOptions.fullValidation,
|
|
2575
|
+
runtime: {
|
|
2576
|
+
...options.catalogGenerationRuntime,
|
|
2577
|
+
validatedStoreEntries,
|
|
2578
|
+
validatingStoreEntries,
|
|
2579
|
+
},
|
|
2580
|
+
});
|
|
2581
|
+
},
|
|
2582
|
+
async inspectSourceRemoval(sourceId, signal) {
|
|
2583
|
+
signal.throwIfAborted();
|
|
2584
|
+
const config = await readCatalogSourcesConfigSnapshot(context.env);
|
|
2585
|
+
const source = config.config.sources.find((entry) => entry.id === sourceId);
|
|
2586
|
+
if (!source)
|
|
2587
|
+
throw new TuiOperationBlockedError('catalogGenerationSourceMissing', '来源不存在。');
|
|
2588
|
+
const impact = await inspectCatalogSourceImpact({
|
|
2589
|
+
sourceId,
|
|
2590
|
+
cwd: context.cwd,
|
|
2591
|
+
env: context.env,
|
|
2592
|
+
sourcePrimary: source.primary,
|
|
2593
|
+
mode: 'remove',
|
|
2594
|
+
});
|
|
2595
|
+
const counts = (category) => impact.affectedReferences.filter((reference) => reference.category === category).length;
|
|
694
2596
|
return {
|
|
695
|
-
blocked:
|
|
696
|
-
inventoryComplete:
|
|
697
|
-
installationCount:
|
|
698
|
-
configCount:
|
|
699
|
-
lifecycleCount:
|
|
700
|
-
marketplaceCount:
|
|
701
|
-
dependencies:
|
|
2597
|
+
blocked: impact.affectedReferences.length > 0 || !impact.inventoryComplete,
|
|
2598
|
+
inventoryComplete: impact.inventoryComplete,
|
|
2599
|
+
installationCount: counts('installation'),
|
|
2600
|
+
configCount: counts('config'),
|
|
2601
|
+
lifecycleCount: counts('lifecycle'),
|
|
2602
|
+
marketplaceCount: counts('marketplace'),
|
|
2603
|
+
dependencies: impact.affectedReferences,
|
|
702
2604
|
};
|
|
703
2605
|
},
|
|
704
|
-
async prepareReviewEvidence(intent, signal, onStage) {
|
|
2606
|
+
async prepareReviewEvidence(intent, signal, onStage, reusableCandidate) {
|
|
705
2607
|
const reporter = createReviewStageReporter({
|
|
706
2608
|
context,
|
|
707
2609
|
onStage,
|
|
708
2610
|
now: options.now ?? Date.now,
|
|
709
2611
|
});
|
|
710
2612
|
try {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
2613
|
+
const preparationWarnings = [];
|
|
2614
|
+
if (intent.kind === 'pluginWrite' && intent.action === 'refreshAndSync') {
|
|
2615
|
+
await reporter.emit('refresh');
|
|
2616
|
+
const packageInfo = await readCliPackageInfo();
|
|
2617
|
+
const sourceId = parsePluginId(intent.pluginId).sourceId;
|
|
2618
|
+
await syncCatalogSourceGeneration({
|
|
2619
|
+
sourceId,
|
|
2620
|
+
cliVersion: packageInfo.version,
|
|
2621
|
+
cwd: context.cwd,
|
|
2622
|
+
env: context.env,
|
|
2623
|
+
signal,
|
|
2624
|
+
}).catch((error) => {
|
|
2625
|
+
preparationWarnings.push(`catalogSourceRefreshFailed: ${sourceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2626
|
+
});
|
|
2627
|
+
sessionCatalogOperationContext = undefined;
|
|
2628
|
+
sessionStatusResult = undefined;
|
|
2629
|
+
validatedStoreEntries.clear();
|
|
2630
|
+
validatingStoreEntries.clear();
|
|
2631
|
+
}
|
|
2632
|
+
const taskObservationSession = intent.kind === 'pluginWriteTask'
|
|
2633
|
+
&& intent.targets.some((target) => target.platform === 'codex')
|
|
2634
|
+
? await createCodexNativeObservationSession({
|
|
2635
|
+
exactCwd: context.cwd,
|
|
2636
|
+
env: context.env,
|
|
2637
|
+
runner: context.nativeCliRunner,
|
|
2638
|
+
})
|
|
2639
|
+
: undefined;
|
|
2640
|
+
let evidence;
|
|
2641
|
+
try {
|
|
2642
|
+
evidence = await captureEvidence(intent, signal, {
|
|
2643
|
+
reporter,
|
|
2644
|
+
validateIntent: true,
|
|
2645
|
+
}, createOperationInventoryReader(), intent.kind === 'pluginWrite'
|
|
2646
|
+
&& intent.action === 'refreshAndSync', reusableCandidate, taskObservationSession);
|
|
2647
|
+
}
|
|
2648
|
+
finally {
|
|
2649
|
+
await taskObservationSession?.dispose();
|
|
2650
|
+
}
|
|
2651
|
+
if (preparationWarnings.length > 0)
|
|
2652
|
+
evidence.preparationWarnings = preparationWarnings;
|
|
2653
|
+
return evidence;
|
|
715
2654
|
}
|
|
716
2655
|
finally {
|
|
717
2656
|
await reporter.finish();
|
|
718
2657
|
}
|
|
719
2658
|
},
|
|
2659
|
+
async previewDependencyPlacement(intent, signal) {
|
|
2660
|
+
const evidence = await captureEvidence(intent, signal);
|
|
2661
|
+
return evidence.dependencyPlan;
|
|
2662
|
+
},
|
|
720
2663
|
async validateIntent(intent, signal) {
|
|
2664
|
+
if (intent.kind === 'catalogSourcesRecovery') {
|
|
2665
|
+
signal.throwIfAborted();
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
if (intent.kind === 'catalogGeneration') {
|
|
2669
|
+
signal.throwIfAborted();
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
721
2672
|
if (intent.kind === 'sourceMutation') {
|
|
722
2673
|
const evidence = await captureEvidence(intent, signal);
|
|
723
|
-
if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.
|
|
2674
|
+
if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
|
|
724
2675
|
throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
|
|
725
2676
|
}
|
|
726
2677
|
return;
|
|
727
2678
|
}
|
|
2679
|
+
if (intent.kind === 'configSaveBatch') {
|
|
2680
|
+
assertConfigSaveBatchIntent(intent);
|
|
2681
|
+
for (const group of intent.groups) {
|
|
2682
|
+
await dependencies.validateIntent(configSaveBatchGroupIntent(intent, group), signal);
|
|
2683
|
+
}
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
728
2686
|
if (intent.kind === 'configSave') {
|
|
729
2687
|
const packageInfo = await readCliPackageInfo();
|
|
730
2688
|
await withResolvedCatalogPlugin({
|
|
731
2689
|
selector: intent.pluginId,
|
|
732
2690
|
cliVersion: packageInfo.version,
|
|
733
|
-
freshnessPolicy: 'cacheFirst',
|
|
734
2691
|
env: context.env,
|
|
735
2692
|
signal,
|
|
2693
|
+
...(sessionContextForPlugin(intent.pluginId)
|
|
2694
|
+
? { operationContext: sessionContextForPlugin(intent.pluginId) }
|
|
2695
|
+
: {}),
|
|
736
2696
|
consume: async ({ plugin }) => {
|
|
737
2697
|
if (!plugin.manifest.config) {
|
|
738
2698
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
739
2699
|
}
|
|
740
2700
|
await coercePluginConfigUpdates({
|
|
741
|
-
schema: plugin.manifest.config.
|
|
2701
|
+
schema: plugin.manifest.config.fields,
|
|
742
2702
|
updates: intent.updates ?? {},
|
|
743
2703
|
cwd: context.cwd,
|
|
744
2704
|
});
|
|
745
2705
|
for (const key of intent.removeKeys ?? []) {
|
|
746
|
-
if (!plugin.manifest.config.
|
|
2706
|
+
if (!plugin.manifest.config.fields[key]) {
|
|
747
2707
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
748
2708
|
}
|
|
749
2709
|
}
|
|
@@ -754,41 +2714,24 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
754
2714
|
if (intent.kind !== 'pluginWrite')
|
|
755
2715
|
return;
|
|
756
2716
|
signal.throwIfAborted();
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
cliVersion: packageInfo.version,
|
|
765
|
-
});
|
|
2717
|
+
const parsed = parsePluginId(intent.pluginId);
|
|
2718
|
+
if (!parsed.sourceId || !parsed.pluginName) {
|
|
2719
|
+
throw new TuiOperationBlockedError('invalidPluginId', '操作必须使用 canonical pluginId。');
|
|
2720
|
+
}
|
|
2721
|
+
},
|
|
2722
|
+
captureEvidence,
|
|
2723
|
+
inspectCatalogStore(signal) {
|
|
766
2724
|
signal.throwIfAborted();
|
|
767
|
-
|
|
768
|
-
assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
|
|
2725
|
+
return inspectCatalogStoreTask({ env: context.env });
|
|
769
2726
|
},
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
cliVersion: packageInfo.version,
|
|
775
|
-
freshnessPolicy: 'forceRemote',
|
|
776
|
-
sourceIds: [sourceId],
|
|
777
|
-
configSnapshot,
|
|
2727
|
+
recheckPortableInstall(receipt, signal) {
|
|
2728
|
+
return recheckPortableInstall({
|
|
2729
|
+
receipt,
|
|
2730
|
+
cwd: context.cwd,
|
|
778
2731
|
env: context.env,
|
|
779
2732
|
signal,
|
|
780
|
-
consume: (catalog) => {
|
|
781
|
-
const item = catalog.sources[0];
|
|
782
|
-
if (item.error)
|
|
783
|
-
throw item.error;
|
|
784
|
-
return {
|
|
785
|
-
sourceIdentity: item.resolution.sourceIdentity,
|
|
786
|
-
requestedRef: item.resolution.requestedRef,
|
|
787
|
-
};
|
|
788
|
-
},
|
|
789
2733
|
});
|
|
790
2734
|
},
|
|
791
|
-
captureEvidence,
|
|
792
2735
|
logError(_error, code, logContext) {
|
|
793
2736
|
return context.debugLogger.log({
|
|
794
2737
|
type: 'tui:controller-error',
|
|
@@ -805,9 +2748,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
805
2748
|
return withResolvedCatalogPlugin({
|
|
806
2749
|
selector: pluginId,
|
|
807
2750
|
cliVersion: packageInfo.version,
|
|
808
|
-
freshnessPolicy: 'cacheFirst',
|
|
809
2751
|
env: context.env,
|
|
810
2752
|
signal,
|
|
2753
|
+
...(sessionContextForPlugin(pluginId)
|
|
2754
|
+
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
2755
|
+
: {}),
|
|
811
2756
|
consume: async ({ plugin }) => {
|
|
812
2757
|
if (!plugin.manifest.config) {
|
|
813
2758
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
@@ -816,7 +2761,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
816
2761
|
pluginName: plugin.manifest.name,
|
|
817
2762
|
plugin,
|
|
818
2763
|
scope: layer.scope,
|
|
819
|
-
projectLayer: layer.projectLayer,
|
|
820
2764
|
cwd: context.cwd,
|
|
821
2765
|
env: context.env,
|
|
822
2766
|
});
|
|
@@ -824,7 +2768,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
824
2768
|
pluginName: plugin.manifest.name,
|
|
825
2769
|
plugin,
|
|
826
2770
|
scope: 'effective',
|
|
827
|
-
projectLayer: layer.projectLayer,
|
|
828
2771
|
cwd: context.cwd,
|
|
829
2772
|
env: context.env,
|
|
830
2773
|
});
|
|
@@ -837,18 +2780,30 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
837
2780
|
return withResolvedCatalogPlugin({
|
|
838
2781
|
selector: pluginId,
|
|
839
2782
|
cliVersion: packageInfo.version,
|
|
840
|
-
freshnessPolicy: 'cacheFirst',
|
|
841
2783
|
env: context.env,
|
|
842
2784
|
signal,
|
|
843
|
-
|
|
2785
|
+
...(sessionContextForPlugin(pluginId)
|
|
2786
|
+
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
2787
|
+
: {}),
|
|
2788
|
+
consume: async ({ plugin, source }) => {
|
|
844
2789
|
if (!plugin.manifest.config) {
|
|
845
2790
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
846
2791
|
}
|
|
2792
|
+
const stores = await readConfigStoresForTui({
|
|
2793
|
+
sourceId: plugin.sourceId,
|
|
2794
|
+
sourceIdentity: plugin.sourceIdentity,
|
|
2795
|
+
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2796
|
+
pluginName: plugin.manifest.name,
|
|
2797
|
+
schema: plugin.manifest.config.fields,
|
|
2798
|
+
scope: layer.scope,
|
|
2799
|
+
signal,
|
|
2800
|
+
});
|
|
2801
|
+
const userStore = stores.user;
|
|
2802
|
+
const projectStore = stores.project;
|
|
847
2803
|
const read = await resolveConfigRead({
|
|
848
2804
|
pluginName: plugin.manifest.name,
|
|
849
2805
|
plugin,
|
|
850
2806
|
scope: layer.scope,
|
|
851
|
-
projectLayer: layer.projectLayer,
|
|
852
2807
|
cwd: context.cwd,
|
|
853
2808
|
env: context.env,
|
|
854
2809
|
});
|
|
@@ -856,60 +2811,387 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
856
2811
|
pluginName: plugin.manifest.name,
|
|
857
2812
|
plugin,
|
|
858
2813
|
scope: 'effective',
|
|
859
|
-
projectLayer: layer.projectLayer,
|
|
860
2814
|
cwd: context.cwd,
|
|
861
2815
|
env: context.env,
|
|
862
2816
|
});
|
|
863
|
-
|
|
2817
|
+
const layerReads = layer.scope === 'effective' ? undefined : {
|
|
2818
|
+
user: layer.scope === 'user' ? read : await resolveConfigRead({
|
|
2819
|
+
pluginName: plugin.manifest.name,
|
|
2820
|
+
plugin,
|
|
2821
|
+
scope: 'user',
|
|
2822
|
+
cwd: context.cwd,
|
|
2823
|
+
env: context.env,
|
|
2824
|
+
}),
|
|
2825
|
+
...(layer.scope === 'user' ? {} : { project: read }),
|
|
2826
|
+
};
|
|
2827
|
+
const effectiveRaw = await resolveEffectivePluginConfig({
|
|
2828
|
+
sourceId: plugin.sourceId,
|
|
2829
|
+
sourceIdentity: plugin.sourceIdentity,
|
|
2830
|
+
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2831
|
+
pluginName: plugin.manifest.name,
|
|
2832
|
+
manifest: plugin.manifest,
|
|
2833
|
+
targetScope: layer.scope === 'user' ? 'user' : 'project',
|
|
2834
|
+
cwd: context.cwd,
|
|
2835
|
+
env: context.env,
|
|
2836
|
+
});
|
|
2837
|
+
const layerStore = layer.scope === 'effective'
|
|
2838
|
+
? undefined
|
|
2839
|
+
: layer.scope === 'user' ? userStore : projectStore;
|
|
2840
|
+
const page = buildConfigPage({
|
|
2841
|
+
pluginId,
|
|
2842
|
+
schema: plugin.manifest.config,
|
|
2843
|
+
read,
|
|
2844
|
+
effectiveRead,
|
|
2845
|
+
...(layerReads ? { layerReads } : {}),
|
|
2846
|
+
...(layerStore ? { rawValues: layerStore.values } : {}),
|
|
2847
|
+
rawLayerValues: {
|
|
2848
|
+
user: userStore.values,
|
|
2849
|
+
...(projectStore ? { project: projectStore.values } : {}),
|
|
2850
|
+
},
|
|
2851
|
+
effectiveRawValues: effectiveRaw.values,
|
|
2852
|
+
sharedNativeRefreshKeys: new Set((plugin.manifest.artifacts?.targets ?? []).flatMap((target) => target.target === 'codex.skill' && target.delivery.mode === 'codex-native'
|
|
2853
|
+
? target.refreshKeys
|
|
2854
|
+
: [])),
|
|
2855
|
+
});
|
|
2856
|
+
return { ...page, editEvidence: {
|
|
2857
|
+
sourceIdentity: source.sourceIdentity,
|
|
2858
|
+
schemaIdentity: source.manifestDigest,
|
|
2859
|
+
storeDigests: {
|
|
2860
|
+
user: digest({ exists: userStore.exists, rawDigest: userStore.rawDigest }),
|
|
2861
|
+
...(projectStore ? { project: digest({ exists: projectStore.exists, rawDigest: projectStore.rawDigest }) } : {}),
|
|
2862
|
+
},
|
|
2863
|
+
} };
|
|
2864
|
+
},
|
|
2865
|
+
});
|
|
2866
|
+
},
|
|
2867
|
+
async getConfigReferenceContext(pluginId, scope, draft, signal) {
|
|
2868
|
+
const packageInfo = await readCliPackageInfo();
|
|
2869
|
+
return withResolvedCatalogPlugin({
|
|
2870
|
+
selector: pluginId, cliVersion: packageInfo.version, env: context.env, signal,
|
|
2871
|
+
...(sessionContextForPlugin(pluginId) ? { operationContext: sessionContextForPlugin(pluginId) } : {}),
|
|
2872
|
+
consume: async ({ plugin }) => {
|
|
2873
|
+
const definition = plugin.manifest.config;
|
|
2874
|
+
if (!definition)
|
|
2875
|
+
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2876
|
+
const scalars = Object.values(definition.fields).flatMap(configReferenceScalars);
|
|
2877
|
+
if (!scalars.length)
|
|
2878
|
+
return { selections: {} };
|
|
2879
|
+
const stores = await readConfigStoresForTui({
|
|
2880
|
+
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
2881
|
+
logicalIdentity: plugin.catalogLogicalIdentity, pluginName: plugin.manifest.name,
|
|
2882
|
+
schema: definition.fields, scope, signal,
|
|
2883
|
+
});
|
|
2884
|
+
const edited = scope === 'user' ? stores.user : stores.project;
|
|
2885
|
+
const values = { ...edited.values };
|
|
2886
|
+
for (const key of draft.removes)
|
|
2887
|
+
delete values[key];
|
|
2888
|
+
Object.assign(values, draft.updates);
|
|
2889
|
+
const candidateStores = { ...stores, [scope]: { ...edited, values } };
|
|
2890
|
+
const effective = await resolveEffectivePluginConfigFromStores({
|
|
2891
|
+
manifest: plugin.manifest, targetScope: scope,
|
|
2892
|
+
projectRoot: resolveAwProjectRoot(context.cwd), stores: candidateStores,
|
|
2893
|
+
});
|
|
2894
|
+
// Keep invalid raw sources for Core validation; effective.values omits them.
|
|
2895
|
+
// Preserve active branches in other references that reserve source values.
|
|
2896
|
+
// Only source choices and entry digests leave this boundary.
|
|
2897
|
+
const candidate = {};
|
|
2898
|
+
for (const key of Object.keys(definition.fields)) {
|
|
2899
|
+
const source = effective.sources[key];
|
|
2900
|
+
if (source === 'default')
|
|
2901
|
+
candidate[key] = definition.fields[key].default;
|
|
2902
|
+
else if (source === 'user')
|
|
2903
|
+
candidate[key] = candidateStores.user.values[key];
|
|
2904
|
+
else if (source === 'project')
|
|
2905
|
+
candidate[key] = candidateStores.project?.values[key];
|
|
2906
|
+
}
|
|
2907
|
+
const result = { selections: {} };
|
|
2908
|
+
for (const scalar of scalars) {
|
|
2909
|
+
const ref = scalar.valueRef;
|
|
2910
|
+
const selection = getPluginConfigReferenceChoices(definition, scalar, candidate);
|
|
2911
|
+
const entries = candidate[ref.field];
|
|
2912
|
+
result.selections[configReferenceKey(scalar)] = {
|
|
2913
|
+
...selection, source: effective.sources[ref.field] ?? 'missing',
|
|
2914
|
+
entries: Array.isArray(entries) ? Object.fromEntries(entries.flatMap((entry) => entry && typeof entry === 'object' && Object.hasOwn(entry, ref.key)
|
|
2915
|
+
? [[JSON.stringify(entry[ref.key]), digest(entry)]] : [])) : {},
|
|
2916
|
+
};
|
|
2917
|
+
}
|
|
2918
|
+
return result;
|
|
864
2919
|
},
|
|
865
2920
|
});
|
|
866
2921
|
},
|
|
867
2922
|
async getConfigResetAvailability(pluginId, layer, signal) {
|
|
2923
|
+
const parsed = parsePluginId(pluginId);
|
|
2924
|
+
if (!parsed.sourceId || !parsed.pluginName) {
|
|
2925
|
+
throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
|
|
2926
|
+
}
|
|
2927
|
+
let sourceIdentity;
|
|
2928
|
+
let logicalIdentity;
|
|
2929
|
+
try {
|
|
2930
|
+
const packageInfo = await readCliPackageInfo();
|
|
2931
|
+
const source = await withResolvedCatalogPlugin({
|
|
2932
|
+
selector: pluginId,
|
|
2933
|
+
cliVersion: packageInfo.version,
|
|
2934
|
+
env: context.env,
|
|
2935
|
+
signal,
|
|
2936
|
+
...(sessionContextForPlugin(pluginId)
|
|
2937
|
+
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
2938
|
+
: {}),
|
|
2939
|
+
consume: async ({ source }) => ({
|
|
2940
|
+
sourceIdentity: source.sourceIdentity,
|
|
2941
|
+
logicalIdentity: source.logicalIdentity,
|
|
2942
|
+
}),
|
|
2943
|
+
});
|
|
2944
|
+
sourceIdentity = source.sourceIdentity;
|
|
2945
|
+
logicalIdentity = source.logicalIdentity;
|
|
2946
|
+
}
|
|
2947
|
+
catch {
|
|
2948
|
+
sourceIdentity = undefined;
|
|
2949
|
+
logicalIdentity = undefined;
|
|
2950
|
+
}
|
|
868
2951
|
signal.throwIfAborted();
|
|
869
|
-
const
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
const identity = await readNamespacedPluginConfigStoreIdentity({
|
|
873
|
-
sourceId,
|
|
874
|
-
pluginName,
|
|
2952
|
+
const target = await inspectPluginConfigResetTarget({
|
|
2953
|
+
sourceId: parsed.sourceId,
|
|
2954
|
+
pluginName: parsed.pluginName,
|
|
875
2955
|
scope: layer.scope,
|
|
876
|
-
projectLayer: layer.projectLayer,
|
|
877
2956
|
cwd: context.cwd,
|
|
878
2957
|
env: context.env,
|
|
879
2958
|
});
|
|
880
2959
|
signal.throwIfAborted();
|
|
881
|
-
if (
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
2960
|
+
if (target.kind === 'regularFile') {
|
|
2961
|
+
const store = await readPluginConfigStore({
|
|
2962
|
+
sourceId: parsed.sourceId,
|
|
2963
|
+
...(sourceIdentity ? { sourceIdentity } : {}),
|
|
2964
|
+
...(logicalIdentity ? { logicalIdentity } : {}),
|
|
2965
|
+
pluginName: parsed.pluginName,
|
|
2966
|
+
scope: layer.scope,
|
|
2967
|
+
cwd: context.cwd,
|
|
2968
|
+
env: context.env,
|
|
2969
|
+
});
|
|
2970
|
+
signal.throwIfAborted();
|
|
889
2971
|
return {
|
|
890
|
-
exists:
|
|
891
|
-
|
|
892
|
-
|
|
2972
|
+
exists: store.exists,
|
|
2973
|
+
state: store.state,
|
|
2974
|
+
...(store.invalidCode ? { invalidCode: store.invalidCode } : {}),
|
|
2975
|
+
sourceIdentityPresent: sourceIdentity !== undefined,
|
|
2976
|
+
...(sourceIdentity ? { sourceIdentity } : {}),
|
|
2977
|
+
targetKind: target.kind,
|
|
2978
|
+
resettable: target.resettable,
|
|
2979
|
+
storeDigest: target.fingerprint,
|
|
893
2980
|
};
|
|
894
2981
|
}
|
|
895
2982
|
return {
|
|
896
|
-
exists:
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}),
|
|
2983
|
+
exists: target.exists,
|
|
2984
|
+
state: target.kind === 'absent' ? 'missing' : 'invalid',
|
|
2985
|
+
sourceIdentityPresent: sourceIdentity !== undefined,
|
|
2986
|
+
...(sourceIdentity ? { sourceIdentity } : {}),
|
|
2987
|
+
targetKind: target.kind,
|
|
2988
|
+
resettable: target.resettable,
|
|
2989
|
+
storeDigest: target.fingerprint,
|
|
904
2990
|
};
|
|
905
2991
|
},
|
|
906
|
-
async executeIntent(intent, expectedEvidence) {
|
|
2992
|
+
async executeIntent(intent, expectedEvidence, signal) {
|
|
2993
|
+
if (intent.kind === 'catalogStorePrune') {
|
|
2994
|
+
signal?.throwIfAborted();
|
|
2995
|
+
if (!expectedEvidence.catalogStore) {
|
|
2996
|
+
throw new TuiOperationBlockedError('catalogStoreReviewStale', '确认信息缺少精确清理目标。');
|
|
2997
|
+
}
|
|
2998
|
+
return executeCatalogStoreTask({
|
|
2999
|
+
env: context.env,
|
|
3000
|
+
authorizedItems: expectedEvidence.catalogStore.authorizedItems,
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
if (intent.kind === 'catalogSourcesRecovery') {
|
|
3004
|
+
signal?.throwIfAborted();
|
|
3005
|
+
return recoverInvalidCatalogSourcesConfig({ env: context.env });
|
|
3006
|
+
}
|
|
3007
|
+
if (intent.kind === 'catalogGeneration') {
|
|
3008
|
+
signal?.throwIfAborted();
|
|
3009
|
+
const expected = expectedEvidence.catalogGeneration;
|
|
3010
|
+
if (!expected)
|
|
3011
|
+
throw new TuiOperationBlockedError('catalogGenerationReviewStale', '确认信息缺少 Catalog candidate。');
|
|
3012
|
+
const packageInfo = await readCliPackageInfo();
|
|
3013
|
+
let configSaved = false;
|
|
3014
|
+
if (intent.sourceMutation) {
|
|
3015
|
+
const common = {
|
|
3016
|
+
cwd: context.cwd,
|
|
3017
|
+
env: context.env,
|
|
3018
|
+
cliVersion: packageInfo.version,
|
|
3019
|
+
expectedConfigDigest: expected.configDigest,
|
|
3020
|
+
};
|
|
3021
|
+
if (intent.sourceMutation.action === 'add') {
|
|
3022
|
+
await addCatalogSourceCommand({
|
|
3023
|
+
...common,
|
|
3024
|
+
source: intent.sourceMutation.source,
|
|
3025
|
+
expectedProposedConfigDigest: expected.proposedConfigDigest,
|
|
3026
|
+
expectedImpactDigest: expected.impactDigest,
|
|
3027
|
+
requireImpactConfirmation: true,
|
|
3028
|
+
confirmImpact: true,
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
else {
|
|
3032
|
+
await updateCatalogSourceCommand({
|
|
3033
|
+
...common,
|
|
3034
|
+
sourceId: intent.sourceId,
|
|
3035
|
+
update: intent.sourceMutation.update,
|
|
3036
|
+
expectedProposedConfigDigest: expected.proposedConfigDigest,
|
|
3037
|
+
expectedImpactDigest: expected.impactDigest,
|
|
3038
|
+
requireIdentityChangeConfirmation: true,
|
|
3039
|
+
confirmIdentityChange: true,
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
configSaved = true;
|
|
3043
|
+
await tuiControllerRuntimeTestHooks?.afterCatalogSourceConfigCommit?.();
|
|
3044
|
+
}
|
|
3045
|
+
try {
|
|
3046
|
+
let generation;
|
|
3047
|
+
if (intent.action === 'recoverState') {
|
|
3048
|
+
generation = await recoverCatalogGenerationStateAndActivate({
|
|
3049
|
+
sourceId: intent.sourceId,
|
|
3050
|
+
cliVersion: packageInfo.version,
|
|
3051
|
+
cwd: context.cwd,
|
|
3052
|
+
impactConfirmed: true,
|
|
3053
|
+
expectedImpactDigest: expected.impactDigest,
|
|
3054
|
+
expectedCandidate: expected.candidate,
|
|
3055
|
+
env: context.env,
|
|
3056
|
+
signal,
|
|
3057
|
+
runtime: options.catalogGenerationRuntime,
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
3060
|
+
else if (intent.action === 'repair') {
|
|
3061
|
+
generation = await repairCatalogSourceGeneration({
|
|
3062
|
+
sourceId: intent.sourceId,
|
|
3063
|
+
cliVersion: packageInfo.version,
|
|
3064
|
+
cwd: context.cwd,
|
|
3065
|
+
expectedCandidate: expected.candidate,
|
|
3066
|
+
expectedActive: expected.previousActive ?? null,
|
|
3067
|
+
expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
|
|
3068
|
+
env: context.env,
|
|
3069
|
+
signal,
|
|
3070
|
+
runtime: {
|
|
3071
|
+
...options.catalogGenerationRuntime,
|
|
3072
|
+
afterCandidateResolved: tuiControllerRuntimeTestHooks
|
|
3073
|
+
?.afterCatalogGenerationCandidateResolved,
|
|
3074
|
+
},
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
else if (intent.action === 'update' && expected.exactRepair) {
|
|
3078
|
+
generation = await repairCatalogSourceGeneration({
|
|
3079
|
+
sourceId: intent.sourceId,
|
|
3080
|
+
cliVersion: packageInfo.version,
|
|
3081
|
+
cwd: context.cwd,
|
|
3082
|
+
expectedActive: expected.previousActive ?? null,
|
|
3083
|
+
expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
|
|
3084
|
+
env: context.env,
|
|
3085
|
+
signal,
|
|
3086
|
+
runtime: options.catalogGenerationRuntime,
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
else if (intent.action === 'replaceDamaged') {
|
|
3090
|
+
generation = await replaceDamagedCatalogSourceGeneration({
|
|
3091
|
+
sourceId: intent.sourceId,
|
|
3092
|
+
cliVersion: packageInfo.version,
|
|
3093
|
+
cwd: context.cwd,
|
|
3094
|
+
expectedCandidate: expected.candidate,
|
|
3095
|
+
expectedActive: expected.previousActive,
|
|
3096
|
+
expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
|
|
3097
|
+
impactConfirmed: true,
|
|
3098
|
+
expectedImpactDigest: expected.impactDigest,
|
|
3099
|
+
env: context.env,
|
|
3100
|
+
signal,
|
|
3101
|
+
runtime: {
|
|
3102
|
+
...options.catalogGenerationRuntime,
|
|
3103
|
+
afterCandidateResolved: tuiControllerRuntimeTestHooks
|
|
3104
|
+
?.afterCatalogGenerationCandidateResolved,
|
|
3105
|
+
},
|
|
3106
|
+
});
|
|
3107
|
+
}
|
|
3108
|
+
else {
|
|
3109
|
+
generation = await activateCatalogSourceGeneration({
|
|
3110
|
+
sourceId: intent.sourceId,
|
|
3111
|
+
cliVersion: packageInfo.version,
|
|
3112
|
+
cwd: context.cwd,
|
|
3113
|
+
activationMode: expected.activationMode,
|
|
3114
|
+
impactConfirmed: true,
|
|
3115
|
+
expectedImpactDigest: expected.impactDigest,
|
|
3116
|
+
impactMode: expected.impactMode,
|
|
3117
|
+
expectedCandidate: expected.candidate,
|
|
3118
|
+
expectedActive: expected.previousActive ?? null,
|
|
3119
|
+
expectedConfigDigest: expected.proposedConfigDigest ?? expected.configDigest,
|
|
3120
|
+
env: context.env,
|
|
3121
|
+
signal,
|
|
3122
|
+
runtime: {
|
|
3123
|
+
...options.catalogGenerationRuntime,
|
|
3124
|
+
afterCandidateResolved: tuiControllerRuntimeTestHooks
|
|
3125
|
+
?.afterCatalogGenerationCandidateResolved,
|
|
3126
|
+
},
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
let primaryChanged = false;
|
|
3130
|
+
if (intent.setPrimaryAfterActivation) {
|
|
3131
|
+
try {
|
|
3132
|
+
await setPrimaryCatalogSourceCommand({
|
|
3133
|
+
sourceId: intent.sourceId,
|
|
3134
|
+
cwd: context.cwd,
|
|
3135
|
+
env: context.env,
|
|
3136
|
+
expectedConfigDigest: expected.primaryConfigDigest,
|
|
3137
|
+
expectedImpactDigest: expected.primaryImpactDigest,
|
|
3138
|
+
requireImpactConfirmation: true,
|
|
3139
|
+
confirmImpact: true,
|
|
3140
|
+
});
|
|
3141
|
+
primaryChanged = true;
|
|
3142
|
+
}
|
|
3143
|
+
catch (error) {
|
|
3144
|
+
return {
|
|
3145
|
+
status: 'partial',
|
|
3146
|
+
partialReason: 'primary',
|
|
3147
|
+
configSaved,
|
|
3148
|
+
generation,
|
|
3149
|
+
activeAvailable: true,
|
|
3150
|
+
errorCode: typeof error === 'object' && error !== null
|
|
3151
|
+
&& 'code' in error && typeof error.code === 'string'
|
|
3152
|
+
? error.code
|
|
3153
|
+
: error instanceof Error ? error.name : 'catalogSourcePrimaryFailed',
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
return { status: 'succeeded', generation, configSaved, primaryChanged };
|
|
3158
|
+
}
|
|
3159
|
+
catch (error) {
|
|
3160
|
+
if (!configSaved)
|
|
3161
|
+
throw error;
|
|
3162
|
+
return {
|
|
3163
|
+
status: 'partial',
|
|
3164
|
+
partialReason: 'activation',
|
|
3165
|
+
configSaved: true,
|
|
3166
|
+
errorCode: error instanceof CatalogGenerationError ? error.code : 'catalogGenerationRefreshFailed',
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
907
3170
|
if (intent.kind === 'portableInstall') {
|
|
908
3171
|
if (!expectedEvidence.portable) {
|
|
909
3172
|
throw operationReviewStale('确认信息缺少 Portable evidence。');
|
|
910
3173
|
}
|
|
911
3174
|
const packageInfo = await readCliPackageInfo();
|
|
912
3175
|
try {
|
|
3176
|
+
if (intent.retry) {
|
|
3177
|
+
if (!('retryPairs' in expectedEvidence.portable)) {
|
|
3178
|
+
throw operationReviewStale('确认信息缺少 Portable 重试目标。');
|
|
3179
|
+
}
|
|
3180
|
+
return await executePortableRetry({
|
|
3181
|
+
receipt: intent.retry.receipt,
|
|
3182
|
+
review: expectedEvidence.portable,
|
|
3183
|
+
expectedEvidenceDigest: expectedEvidence.portable.evidenceDigest,
|
|
3184
|
+
cwd: context.cwd,
|
|
3185
|
+
env: context.env,
|
|
3186
|
+
cliVersion: packageInfo.version,
|
|
3187
|
+
signal,
|
|
3188
|
+
onProgress(stage) {
|
|
3189
|
+
const phase = stage === 'prepare' ? '重新检查失败目标'
|
|
3190
|
+
: stage === 'add' ? '重试缺失的 Skills' : '验证重试结果';
|
|
3191
|
+
progressFeed?.setCurrent({ target: 'Skills CLI', phase }, `Portable Skills:${phase}`);
|
|
3192
|
+
},
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
913
3195
|
return await installPortableSkills({
|
|
914
3196
|
selector: intent.pluginId,
|
|
915
3197
|
scope: intent.scope,
|
|
@@ -920,6 +3202,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
920
3202
|
env: context.env,
|
|
921
3203
|
cliVersion: packageInfo.version,
|
|
922
3204
|
expectedEvidenceDigest: expectedEvidence.portable.evidenceDigest,
|
|
3205
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
923
3206
|
onProgress(stage) {
|
|
924
3207
|
const phase = stage === 'prepare' ? '检查插件依赖并生成候选'
|
|
925
3208
|
: stage === 'add' ? '安装已选择的 Skills' : '验证安装结果';
|
|
@@ -934,73 +3217,655 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
934
3217
|
throw error;
|
|
935
3218
|
}
|
|
936
3219
|
}
|
|
3220
|
+
if (intent.kind === 'sourceMutation') {
|
|
3221
|
+
const currentEvidence = await captureEvidence(intent);
|
|
3222
|
+
if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
|
|
3223
|
+
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
3224
|
+
}
|
|
3225
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3226
|
+
const packageInfo = await readCliPackageInfo();
|
|
3227
|
+
const runtime = {
|
|
3228
|
+
onProgress: async (event) => {
|
|
3229
|
+
const phase = SOURCE_PROGRESS_PHASES[event.stage];
|
|
3230
|
+
progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
|
|
3231
|
+
await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
|
|
3232
|
+
},
|
|
3233
|
+
};
|
|
3234
|
+
const common = {
|
|
3235
|
+
cwd: context.cwd,
|
|
3236
|
+
env: context.env,
|
|
3237
|
+
expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
|
|
3238
|
+
expectedProposedConfigDigest: expectedEvidence.sourceWrite?.proposedConfigDigest,
|
|
3239
|
+
expectedImpactDigest: expectedEvidence.sourceWrite?.impactDigest,
|
|
3240
|
+
runtime,
|
|
3241
|
+
};
|
|
3242
|
+
if (intent.action === 'add' && intent.source) {
|
|
3243
|
+
return addCatalogSourceCommand({
|
|
3244
|
+
...common,
|
|
3245
|
+
source: intent.source,
|
|
3246
|
+
cliVersion: packageInfo.version,
|
|
3247
|
+
requireImpactConfirmation: true,
|
|
3248
|
+
confirmImpact: true,
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
if (intent.action === 'update' && intent.update) {
|
|
3252
|
+
return updateCatalogSourceCommand({
|
|
3253
|
+
...common,
|
|
3254
|
+
sourceId: intent.sourceId,
|
|
3255
|
+
update: intent.update,
|
|
3256
|
+
cliVersion: packageInfo.version,
|
|
3257
|
+
requireIdentityChangeConfirmation: true,
|
|
3258
|
+
confirmIdentityChange: true,
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
if (intent.action === 'setPrimary') {
|
|
3262
|
+
return setPrimaryCatalogSourceCommand({
|
|
3263
|
+
...common,
|
|
3264
|
+
sourceId: intent.sourceId,
|
|
3265
|
+
requireImpactConfirmation: true,
|
|
3266
|
+
confirmImpact: true,
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
if (intent.action === 'remove') {
|
|
3270
|
+
return removeCatalogSourceCommand({
|
|
3271
|
+
...common,
|
|
3272
|
+
sourceId: intent.sourceId,
|
|
3273
|
+
requireImpactConfirmation: true,
|
|
3274
|
+
confirmImpact: true,
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
|
|
3278
|
+
}
|
|
3279
|
+
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
3280
|
+
|| intent.kind === 'pluginWriteTask'
|
|
3281
|
+
|| intent.kind === 'configSave' && (intent.apply || intent.retryApply)
|
|
3282
|
+
|| intent.kind === 'configSaveBatch' && intent.apply) {
|
|
3283
|
+
await prepareNativeCliExecution({ env: context.env, runner: context.nativeCliRunner });
|
|
3284
|
+
}
|
|
3285
|
+
const codexObservationSession = intent.kind === 'pluginWrite'
|
|
3286
|
+
|| intent.kind === 'pluginWriteBatch'
|
|
3287
|
+
|| intent.kind === 'pluginWriteTask'
|
|
3288
|
+
? await createCodexNativeObservationSession({
|
|
3289
|
+
exactCwd: context.cwd,
|
|
3290
|
+
env: context.env,
|
|
3291
|
+
runner: context.nativeCliRunner,
|
|
3292
|
+
manager: codexManagerByEvidence.get(expectedEvidence),
|
|
3293
|
+
})
|
|
3294
|
+
: undefined;
|
|
937
3295
|
return withAwHomeMutationLock({
|
|
938
3296
|
env: context.env,
|
|
939
3297
|
action: async (awHomeMutationToken) => {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
3298
|
+
await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
|
|
3299
|
+
const operationInventory = createOperationInventoryReader();
|
|
3300
|
+
const managedCommandProducerCache = new Map();
|
|
3301
|
+
let currentEvidence;
|
|
3302
|
+
try {
|
|
3303
|
+
currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true, undefined, codexObservationSession, intent.kind === 'pluginWriteBatch'
|
|
3304
|
+
? expectedEvidence
|
|
3305
|
+
: undefined);
|
|
943
3306
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const common = {
|
|
956
|
-
cwd: context.cwd,
|
|
957
|
-
env: context.env,
|
|
958
|
-
expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
|
|
959
|
-
runtime,
|
|
960
|
-
};
|
|
961
|
-
if (intent.action === 'add' && intent.source) {
|
|
962
|
-
return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
|
|
3307
|
+
catch (error) {
|
|
3308
|
+
const code = error instanceof TuiOperationBlockedError ? error.code : undefined;
|
|
3309
|
+
if (intent.kind === 'pluginWriteBatch'
|
|
3310
|
+
&& (intent.selection.kind === 'currentProjectIntentRoots'
|
|
3311
|
+
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3312
|
+
&& error instanceof TuiOperationBlockedError) {
|
|
3313
|
+
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3314
|
+
}
|
|
3315
|
+
if (intent.kind === 'pluginWriteTask'
|
|
3316
|
+
&& (code === 'taskHasBlockedTargets' || code === 'taskNoExecutableTargets')) {
|
|
3317
|
+
throw operationReviewStale('确认后任务可执行目标发生变化。', error instanceof TuiOperationBlockedError ? error.diagnostics : undefined);
|
|
963
3318
|
}
|
|
964
|
-
|
|
965
|
-
|
|
3319
|
+
throw error;
|
|
3320
|
+
}
|
|
3321
|
+
if (intent.kind === 'pluginWriteBatch') {
|
|
3322
|
+
const expectedBatch = expectedEvidence;
|
|
3323
|
+
const currentBatch = currentEvidence;
|
|
3324
|
+
if ((intent.selection.kind === 'currentProjectIntentRoots'
|
|
3325
|
+
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3326
|
+
&& (!expectedBatch.projectIntentDigest
|
|
3327
|
+
|| expectedBatch.projectIntentDigest !== currentBatch.projectIntentDigest)) {
|
|
3328
|
+
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3329
|
+
}
|
|
3330
|
+
const reviewedGroups = expectedBatch.groups.map((group) => ({
|
|
3331
|
+
groupId: group.groupId,
|
|
3332
|
+
sourceId: group.sourceId,
|
|
3333
|
+
platform: group.platform,
|
|
3334
|
+
scope: group.scope,
|
|
3335
|
+
requestedPluginIds: group.requestedPluginIds,
|
|
3336
|
+
executable: group.executable,
|
|
3337
|
+
planFingerprint: group.preview.operationPlan
|
|
3338
|
+
? intent.action === 'cleanup'
|
|
3339
|
+
? group.preview.operationPlan.planFingerprint
|
|
3340
|
+
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3341
|
+
: undefined,
|
|
3342
|
+
}));
|
|
3343
|
+
const currentGroups = currentBatch.groups.map((group) => ({
|
|
3344
|
+
groupId: group.groupId,
|
|
3345
|
+
sourceId: group.sourceId,
|
|
3346
|
+
platform: group.platform,
|
|
3347
|
+
scope: group.scope,
|
|
3348
|
+
requestedPluginIds: group.requestedPluginIds,
|
|
3349
|
+
executable: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3350
|
+
?.executable === false ? false : group.executable,
|
|
3351
|
+
planFingerprint: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3352
|
+
?.executable === false
|
|
3353
|
+
? undefined
|
|
3354
|
+
: group.preview.operationPlan
|
|
3355
|
+
? intent.action === 'cleanup'
|
|
3356
|
+
? group.preview.operationPlan.planFingerprint
|
|
3357
|
+
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3358
|
+
: undefined,
|
|
3359
|
+
}));
|
|
3360
|
+
if (expectedBatch.catalog.configDigest !== currentBatch.catalog.configDigest
|
|
3361
|
+
|| JSON.stringify(expectedBatch.requestedPluginIds)
|
|
3362
|
+
!== JSON.stringify(currentBatch.requestedPluginIds)
|
|
3363
|
+
|| JSON.stringify(reviewedGroups) !== JSON.stringify(currentGroups)) {
|
|
3364
|
+
throw operationReviewStale('确认后批量目标或写入计划发生变化。');
|
|
3365
|
+
}
|
|
3366
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3367
|
+
const groupResults = [];
|
|
3368
|
+
const completedTargetKeys = new Set();
|
|
3369
|
+
const expectedIntentDigests = new Map();
|
|
3370
|
+
for (const group of expectedBatch.groups) {
|
|
3371
|
+
for (const intentDigest of group.preview.operationPlan?.evidence.intentDigests ?? []) {
|
|
3372
|
+
expectedIntentDigests.set(intentDigest.scope, intentDigest.digest);
|
|
3373
|
+
}
|
|
966
3374
|
}
|
|
967
|
-
|
|
968
|
-
|
|
3375
|
+
let executedGroupCount = 0;
|
|
3376
|
+
const appendStoppedGroups = (startIndex, staleMessage) => {
|
|
3377
|
+
for (const [offset, stopped] of expectedBatch.groups.slice(startIndex).entries()) {
|
|
3378
|
+
groupResults.push({
|
|
3379
|
+
groupId: stopped.groupId,
|
|
3380
|
+
sourceId: stopped.sourceId,
|
|
3381
|
+
platform: stopped.platform,
|
|
3382
|
+
scope: stopped.scope,
|
|
3383
|
+
requestedPluginIds: stopped.requestedPluginIds,
|
|
3384
|
+
result: !stopped.executable ? stopped.preview : failedWriteCommandResult(intent.action, stopped.requestedPluginIds, { cwd: context.cwd, platform: stopped.platform, scope: stopped.scope }, offset === 0
|
|
3385
|
+
? new TuiOperationBlockedError('operationReviewStale', staleMessage)
|
|
3386
|
+
: new TuiOperationBlockedError('batchNotStarted', '前一个批量组的写入计划已变化,因此这个组没有开始。')),
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
};
|
|
3390
|
+
for (const [groupIndex, reviewed] of expectedBatch.groups.entries()) {
|
|
3391
|
+
const current = currentBatch.groups.find((group) => group.groupId === reviewed.groupId);
|
|
3392
|
+
if (!reviewed.executable) {
|
|
3393
|
+
groupResults.push({
|
|
3394
|
+
groupId: reviewed.groupId,
|
|
3395
|
+
sourceId: reviewed.sourceId,
|
|
3396
|
+
platform: reviewed.platform,
|
|
3397
|
+
scope: reviewed.scope,
|
|
3398
|
+
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3399
|
+
result: reviewed.preview,
|
|
3400
|
+
});
|
|
3401
|
+
continue;
|
|
3402
|
+
}
|
|
3403
|
+
const groupInventory = groupResults.length === 0
|
|
3404
|
+
? operationInventory
|
|
3405
|
+
: createOperationInventoryReader();
|
|
3406
|
+
let executionPreview = current.preview;
|
|
3407
|
+
if (completedTargetKeys.size > 0
|
|
3408
|
+
|| (intent.action === 'cleanup' && executedGroupCount > 0)) {
|
|
3409
|
+
executionPreview = await runBatchWriteGroup({
|
|
3410
|
+
intent,
|
|
3411
|
+
group: current,
|
|
3412
|
+
cwd: context.cwd,
|
|
3413
|
+
runtime: {
|
|
3414
|
+
...context,
|
|
3415
|
+
awHomeMutationToken,
|
|
3416
|
+
inventoryCatalogSourceReferences: groupInventory,
|
|
3417
|
+
...(intent.action === 'cleanup' ? {} : {
|
|
3418
|
+
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3419
|
+
}),
|
|
3420
|
+
previewOperationPlan: true,
|
|
3421
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3422
|
+
},
|
|
3423
|
+
});
|
|
3424
|
+
const cleanupIntentDigestChanged = intent.action === 'cleanup'
|
|
3425
|
+
&& executionPreview.operationPlan?.evidence.intentDigests.find((entry) => entry.scope === current.scope)?.digest !== expectedIntentDigests.get(current.scope);
|
|
3426
|
+
if (cleanupIntentDigestChanged
|
|
3427
|
+
|| !reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3428
|
+
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3429
|
+
appendStoppedGroups(groupIndex, '前序批量结果改变了这个组的实际写入计划。');
|
|
3430
|
+
break;
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3434
|
+
const result = await runBatchWriteGroup({
|
|
3435
|
+
intent,
|
|
3436
|
+
group: current,
|
|
3437
|
+
cwd: context.cwd,
|
|
3438
|
+
runtime: {
|
|
3439
|
+
...context,
|
|
3440
|
+
awHomeMutationToken,
|
|
3441
|
+
preApplyConfirmed: true,
|
|
3442
|
+
managedCommandProducerCache,
|
|
3443
|
+
inventoryCatalogSourceReferences: groupInventory,
|
|
3444
|
+
...(intent.action === 'cleanup' ? {} : {
|
|
3445
|
+
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3446
|
+
}),
|
|
3447
|
+
...(executionPreview.operationPlan ? {
|
|
3448
|
+
expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint,
|
|
3449
|
+
} : {}),
|
|
3450
|
+
...(requiresManagedCodex ? {
|
|
3451
|
+
managedCodexAuthorizer: async () => true,
|
|
3452
|
+
} : {}),
|
|
3453
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3454
|
+
},
|
|
3455
|
+
});
|
|
3456
|
+
if (writeReviewIsStale(result)) {
|
|
3457
|
+
if (executedGroupCount === 0)
|
|
3458
|
+
throwIfWriteReviewStale(result);
|
|
3459
|
+
appendStoppedGroups(groupIndex, '这个组的实际写入计划发生变化。');
|
|
3460
|
+
break;
|
|
3461
|
+
}
|
|
3462
|
+
if (intent.action === 'cleanup') {
|
|
3463
|
+
for (const change of result.operationPlan?.intentChanges ?? []) {
|
|
3464
|
+
expectedIntentDigests.set(change.scope, change.afterDigest);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
executedGroupCount += 1;
|
|
3468
|
+
for (const target of result.targetResults) {
|
|
3469
|
+
if (target.state === 'completed') {
|
|
3470
|
+
completedTargetKeys.add([
|
|
3471
|
+
target.target.pluginId,
|
|
3472
|
+
target.target.platform,
|
|
3473
|
+
target.target.scope,
|
|
3474
|
+
].join('\0'));
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
groupResults.push({
|
|
3478
|
+
groupId: reviewed.groupId,
|
|
3479
|
+
sourceId: reviewed.sourceId,
|
|
3480
|
+
platform: reviewed.platform,
|
|
3481
|
+
scope: reviewed.scope,
|
|
3482
|
+
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3483
|
+
result,
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
const merged = mergeWriteCommandResults(groupResults.map((group) => group.result));
|
|
3487
|
+
return {
|
|
3488
|
+
...merged,
|
|
3489
|
+
requestedPluginIds: expectedBatch.requestedPluginIds,
|
|
3490
|
+
groupResults,
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
if (intent.kind === 'pluginWriteTask') {
|
|
3494
|
+
const expectedTask = expectedEvidence;
|
|
3495
|
+
const currentTask = currentEvidence;
|
|
3496
|
+
if (!operationEvidenceMatches(expectedTask, currentTask)) {
|
|
3497
|
+
throw operationReviewStale('确认后任务目标、根因、版本或写入计划发生变化。');
|
|
969
3498
|
}
|
|
970
|
-
|
|
971
|
-
|
|
3499
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3500
|
+
const operationContexts = taskCatalogOperationsByEvidence.get(currentEvidence)
|
|
3501
|
+
?? new Map();
|
|
3502
|
+
const sourceResults = [];
|
|
3503
|
+
const completedTargetKeys = new Set();
|
|
3504
|
+
const preApplyExecutionCache = new Map();
|
|
3505
|
+
const commandTargets = (source) => source.requestedTargets.map((target) => {
|
|
3506
|
+
const direct = source.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
3507
|
+
return {
|
|
3508
|
+
pluginId: target.pluginId,
|
|
3509
|
+
platform: target.platform,
|
|
3510
|
+
scope: target.scope,
|
|
3511
|
+
method: direct.some((cause) => cause.method === 'reinstall')
|
|
3512
|
+
? 'reinstall' : 'sync',
|
|
3513
|
+
};
|
|
3514
|
+
});
|
|
3515
|
+
const repairTargets = (source) => [...new Map(source.causes.map((cause) => [
|
|
3516
|
+
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
3517
|
+
{ ...cause.repairTarget, method: cause.method },
|
|
3518
|
+
])).values()];
|
|
3519
|
+
const stoppedResult = (source, error) => mergeWriteCommandResults(source.requestedTargets.map((target) => failedWriteCommandResult('sync', [target.pluginId], {
|
|
3520
|
+
cwd: context.cwd,
|
|
3521
|
+
platform: target.platform,
|
|
3522
|
+
scope: target.scope,
|
|
3523
|
+
}, error)));
|
|
3524
|
+
const appendStoppedSources = (startIndex, message) => {
|
|
3525
|
+
for (const [offset, source] of expectedTask.taskPlan.sources.slice(startIndex).entries()) {
|
|
3526
|
+
sourceResults.push({
|
|
3527
|
+
sourceId: source.sourceId,
|
|
3528
|
+
sourceIdentity: source.sourceIdentity,
|
|
3529
|
+
requestedTargets: source.requestedTargets,
|
|
3530
|
+
result: stoppedResult(source, offset === 0
|
|
3531
|
+
? new TuiOperationBlockedError('operationReviewStale', message)
|
|
3532
|
+
: new TuiOperationBlockedError('taskNotStarted', '前一个来源计划已变化,因此这个来源没有开始。')),
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
};
|
|
3536
|
+
for (const [sourceIndex, reviewed] of expectedTask.taskPlan.sources.entries()) {
|
|
3537
|
+
const current = currentTask.taskPlan.sources.find((source) => source.sourceId === reviewed.sourceId);
|
|
3538
|
+
const catalogOperation = operationContexts.get(reviewed.sourceId);
|
|
3539
|
+
if (!current || !catalogOperation || current.sourceIdentity !== reviewed.sourceIdentity) {
|
|
3540
|
+
if (sourceResults.length === 0) {
|
|
3541
|
+
throw operationReviewStale('确认后任务来源身份发生变化。');
|
|
3542
|
+
}
|
|
3543
|
+
appendStoppedSources(sourceIndex, '确认后任务来源身份发生变化。');
|
|
3544
|
+
break;
|
|
3545
|
+
}
|
|
3546
|
+
const sourceInventory = sourceResults.length === 0
|
|
3547
|
+
? operationInventory : createOperationInventoryReader();
|
|
3548
|
+
let executionPreview = current.preview;
|
|
3549
|
+
if (completedTargetKeys.size > 0) {
|
|
3550
|
+
executionPreview = await taskWriteCommand(commandTargets(current), {
|
|
3551
|
+
cwd: context.cwd,
|
|
3552
|
+
repairTargets: repairTargets(current),
|
|
3553
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3554
|
+
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3555
|
+
}, {
|
|
3556
|
+
...context,
|
|
3557
|
+
awHomeMutationToken,
|
|
3558
|
+
catalogOperationContext: catalogOperation,
|
|
3559
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3560
|
+
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3561
|
+
inventoryCatalogSourceReferences: sourceInventory,
|
|
3562
|
+
previewOperationPlan: true,
|
|
3563
|
+
preApplyExecutionCache,
|
|
3564
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3565
|
+
});
|
|
3566
|
+
if (!reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3567
|
+
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3568
|
+
appendStoppedSources(sourceIndex, '前序结果改变了这个来源的实际写入计划。');
|
|
3569
|
+
break;
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3573
|
+
const result = await taskWriteCommand(commandTargets(current), {
|
|
3574
|
+
cwd: context.cwd,
|
|
3575
|
+
repairTargets: repairTargets(current),
|
|
3576
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3577
|
+
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3578
|
+
}, {
|
|
3579
|
+
...context,
|
|
3580
|
+
awHomeMutationToken,
|
|
3581
|
+
catalogOperationContext: catalogOperation,
|
|
3582
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3583
|
+
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3584
|
+
inventoryCatalogSourceReferences: sourceInventory,
|
|
3585
|
+
preApplyConfirmed: true,
|
|
3586
|
+
preApplyExecutionCache,
|
|
3587
|
+
managedCommandProducerCache,
|
|
3588
|
+
...(executionPreview.operationPlan
|
|
3589
|
+
? { expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint } : {}),
|
|
3590
|
+
...(requiresManagedCodex ? { managedCodexAuthorizer: async () => true } : {}),
|
|
3591
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3592
|
+
});
|
|
3593
|
+
if (writeReviewIsStale(result)) {
|
|
3594
|
+
if (sourceResults.length === 0)
|
|
3595
|
+
throwIfWriteReviewStale(result);
|
|
3596
|
+
appendStoppedSources(sourceIndex, '这个来源的实际写入计划发生变化。');
|
|
3597
|
+
break;
|
|
3598
|
+
}
|
|
3599
|
+
for (const target of result.targetResults) {
|
|
3600
|
+
if (target.state === 'completed') {
|
|
3601
|
+
completedTargetKeys.add([
|
|
3602
|
+
target.target.pluginId,
|
|
3603
|
+
target.target.platform,
|
|
3604
|
+
target.target.scope,
|
|
3605
|
+
].join('\0'));
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
sourceResults.push({
|
|
3609
|
+
sourceId: reviewed.sourceId,
|
|
3610
|
+
sourceIdentity: reviewed.sourceIdentity,
|
|
3611
|
+
requestedTargets: reviewed.requestedTargets,
|
|
3612
|
+
result,
|
|
3613
|
+
});
|
|
972
3614
|
}
|
|
973
|
-
|
|
3615
|
+
const merged = mergeWriteCommandResults(sourceResults.map((source) => source.result));
|
|
3616
|
+
return {
|
|
3617
|
+
...merged,
|
|
3618
|
+
taskPlan: expectedTask.taskPlan,
|
|
3619
|
+
sourceResults,
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
if (intent.kind !== 'pluginWrite'
|
|
3623
|
+
&& !operationEvidenceMatches(expectedEvidence, currentEvidence)) {
|
|
3624
|
+
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
3625
|
+
}
|
|
3626
|
+
const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
|
|
3627
|
+
if (expectedEvidence.catalog.generationSetDigest
|
|
3628
|
+
&& catalogOperation?.generationSetDigest !== expectedEvidence.catalog.generationSetDigest) {
|
|
3629
|
+
throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
|
|
974
3630
|
}
|
|
3631
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
975
3632
|
if (intent.kind === 'pluginWrite') {
|
|
976
3633
|
const options = {
|
|
977
3634
|
cwd: context.cwd,
|
|
978
3635
|
platform: intent.platform,
|
|
979
3636
|
scope: intent.scope,
|
|
3637
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3638
|
+
? {}
|
|
3639
|
+
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3640
|
+
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
3641
|
+
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
3642
|
+
: {}),
|
|
3643
|
+
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3644
|
+
? { allowManagedCodexInstall: true }
|
|
3645
|
+
: {}),
|
|
980
3646
|
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
981
3647
|
};
|
|
982
3648
|
const command = {
|
|
983
3649
|
install: installWriteCommand,
|
|
984
|
-
|
|
3650
|
+
refreshAndSync: syncWriteCommand,
|
|
985
3651
|
reinstall: reinstallWriteCommand,
|
|
986
3652
|
uninstall: uninstallWriteCommand,
|
|
987
3653
|
cleanup: cleanupWriteCommand,
|
|
3654
|
+
sync: syncWriteCommand,
|
|
988
3655
|
}[intent.action];
|
|
3656
|
+
if (intent.action === 'cleanup') {
|
|
3657
|
+
if (!expectedEvidence.operationPlan || !currentEvidence.operationPlan
|
|
3658
|
+
|| expectedEvidence.operationPlan.planFingerprint
|
|
3659
|
+
!== currentEvidence.operationPlan.planFingerprint) {
|
|
3660
|
+
throw operationReviewStale('确认后清理计划发生变化。');
|
|
3661
|
+
}
|
|
3662
|
+
const result = await command([intent.pluginId], options, {
|
|
3663
|
+
...context,
|
|
3664
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3665
|
+
awHomeMutationToken,
|
|
3666
|
+
preApplyConfirmed: true,
|
|
3667
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
3668
|
+
expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint,
|
|
3669
|
+
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3670
|
+
? { managedCodexAuthorizer: async () => true }
|
|
3671
|
+
: {}),
|
|
3672
|
+
});
|
|
3673
|
+
throwIfWriteReviewStale(result);
|
|
3674
|
+
return result;
|
|
3675
|
+
}
|
|
989
3676
|
const currentSource = currentEvidence.source;
|
|
990
3677
|
if (!expectedEvidence.source || !currentSource) {
|
|
991
3678
|
throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
|
|
992
3679
|
}
|
|
993
3680
|
const result = await command([intent.pluginId], options, {
|
|
994
3681
|
...context,
|
|
3682
|
+
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
995
3683
|
awHomeMutationToken,
|
|
3684
|
+
preApplyConfirmed: true,
|
|
3685
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
3686
|
+
...(expectedEvidence.operationPlan
|
|
3687
|
+
? { expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint }
|
|
3688
|
+
: {}),
|
|
996
3689
|
expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
|
|
3690
|
+
...(catalogOperation ? {
|
|
3691
|
+
catalogOperationContext: catalogOperation,
|
|
3692
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3693
|
+
} : {}),
|
|
3694
|
+
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3695
|
+
? { managedCodexAuthorizer: async () => true }
|
|
3696
|
+
: {}),
|
|
997
3697
|
reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual),
|
|
998
3698
|
});
|
|
999
3699
|
throwIfWriteReviewStale(result);
|
|
1000
3700
|
return result;
|
|
1001
3701
|
}
|
|
3702
|
+
if (intent.kind === 'configSaveBatch') {
|
|
3703
|
+
assertConfigSaveBatchIntent(intent);
|
|
3704
|
+
const reviewedBatch = expectedEvidence.configBatch;
|
|
3705
|
+
if (!reviewedBatch || !currentEvidence.configBatch || !currentEvidence.source) {
|
|
3706
|
+
throw operationReviewStale('确认信息缺少设置批次证据。');
|
|
3707
|
+
}
|
|
3708
|
+
const approvedApplyTargets = new Set(reviewedBatch.groups.flatMap((group) => group.config.affectedTargets.map((target) => `${target.platform}\0${target.scope}`)));
|
|
3709
|
+
const approvedCommandPackages = new Set(reviewedBatch.groups.flatMap((group) => (group.config.commandPackages ?? []).map((target) => target.packageId)));
|
|
3710
|
+
const approvedPreApplyTargets = reviewedBatch.groups.flatMap((group) => group.config.preApplyAuthorization ?? []);
|
|
3711
|
+
const results = [];
|
|
3712
|
+
const packageInfo = await readCliPackageInfo();
|
|
3713
|
+
for (const group of intent.groups) {
|
|
3714
|
+
const groupIntent = configSaveBatchGroupIntent(intent, group);
|
|
3715
|
+
const reviewedGroup = reviewedBatch.groups.find((item) => item.scope === group.scope);
|
|
3716
|
+
if (!reviewedGroup) {
|
|
3717
|
+
throw operationReviewStale('确认信息缺少设置层基线。');
|
|
3718
|
+
}
|
|
3719
|
+
let configSaved = false;
|
|
3720
|
+
const operationRebuildResults = [];
|
|
3721
|
+
try {
|
|
3722
|
+
const freshEvidence = await captureEvidence(groupIntent, undefined, undefined, operationInventory, true);
|
|
3723
|
+
const freshSource = freshEvidence.source;
|
|
3724
|
+
const freshConfig = freshEvidence.config;
|
|
3725
|
+
if (!freshSource || !freshConfig) {
|
|
3726
|
+
throw operationReviewStale('设置目标缺少最新证据。');
|
|
3727
|
+
}
|
|
3728
|
+
assertSourceEvidenceMatches(currentEvidence.source, freshSource);
|
|
3729
|
+
const freshLayerStoreDigest = await configLayerStoreDigest({
|
|
3730
|
+
source: freshSource,
|
|
3731
|
+
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
3732
|
+
scope: group.scope,
|
|
3733
|
+
cwd: context.cwd,
|
|
3734
|
+
env: context.env,
|
|
3735
|
+
});
|
|
3736
|
+
if (freshLayerStoreDigest !== reviewedGroup.config.storeDigest) {
|
|
3737
|
+
throw operationReviewStale('保存设置期间此设置层的原始内容发生变化。');
|
|
3738
|
+
}
|
|
3739
|
+
if (freshConfig.affectedTargets.some((target) => !approvedApplyTargets.has(`${target.platform}\0${target.scope}`))
|
|
3740
|
+
|| freshConfig.commandPackages?.some((target) => !approvedCommandPackages.has(target.packageId))) {
|
|
3741
|
+
throw operationReviewStale('同批前一层变更扩大了未经确认的应用目标。');
|
|
3742
|
+
}
|
|
3743
|
+
const groupCatalogOperation = catalogOperationByEvidence.get(freshEvidence);
|
|
3744
|
+
if (expectedEvidence.catalog.generationSetDigest
|
|
3745
|
+
&& groupCatalogOperation?.generationSetDigest
|
|
3746
|
+
!== expectedEvidence.catalog.generationSetDigest) {
|
|
3747
|
+
throw operationReviewStale('保存设置期间 Active Catalog generation 发生变化。');
|
|
3748
|
+
}
|
|
3749
|
+
const result = await withResolvedCatalogPlugin({
|
|
3750
|
+
selector: intent.pluginId,
|
|
3751
|
+
cliVersion: packageInfo.version,
|
|
3752
|
+
env: context.env,
|
|
3753
|
+
...(groupCatalogOperation ? { operationContext: groupCatalogOperation } : {}),
|
|
3754
|
+
consume: async ({ plugin, source }) => {
|
|
3755
|
+
assertSourceEvidenceMatches(freshSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
|
|
3756
|
+
const applyTargets = intent.apply
|
|
3757
|
+
? freshConfig.affectedTargets.map((target) => ({
|
|
3758
|
+
pluginName: plugin.manifest.name,
|
|
3759
|
+
platform: target.platform,
|
|
3760
|
+
scope: target.scope,
|
|
3761
|
+
}))
|
|
3762
|
+
: [];
|
|
3763
|
+
return applyPluginConfigWrite({
|
|
3764
|
+
plugin,
|
|
3765
|
+
pluginName: plugin.manifest.name,
|
|
3766
|
+
scope: group.scope,
|
|
3767
|
+
cwd: context.cwd,
|
|
3768
|
+
env: context.env,
|
|
3769
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
3770
|
+
rebuildRuntime: {
|
|
3771
|
+
...context,
|
|
3772
|
+
awHomeMutationToken,
|
|
3773
|
+
operationDiagnosticSink: (entries) => {
|
|
3774
|
+
operationRebuildResults.push(...structuredClone(entries));
|
|
3775
|
+
},
|
|
3776
|
+
...(reviewedScriptTimeoutMs(intent) === undefined ? {} : {
|
|
3777
|
+
scriptTimeoutMs: reviewedScriptTimeoutMs(intent),
|
|
3778
|
+
}),
|
|
3779
|
+
...(groupCatalogOperation ? {
|
|
3780
|
+
catalogOperationContext: groupCatalogOperation,
|
|
3781
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3782
|
+
} : {}),
|
|
3783
|
+
},
|
|
3784
|
+
awHomeMutationToken,
|
|
3785
|
+
applyTargets,
|
|
3786
|
+
request: { updates: group.updates, removeKeys: group.removeKeys },
|
|
3787
|
+
sharedNativeApplyConfirmed: true,
|
|
3788
|
+
preApplyConfirmed: true,
|
|
3789
|
+
preApplyAuthorizedTargets: approvedPreApplyTargets,
|
|
3790
|
+
});
|
|
3791
|
+
},
|
|
3792
|
+
});
|
|
3793
|
+
configSaved = result.configWrite === 'succeeded';
|
|
3794
|
+
const applyStatus = !intent.apply
|
|
3795
|
+
? 'notRequested'
|
|
3796
|
+
: result.apply.state === 'succeeded'
|
|
3797
|
+
? 'succeeded'
|
|
3798
|
+
: result.apply.state === 'skipped'
|
|
3799
|
+
? 'notNeeded'
|
|
3800
|
+
: 'failed';
|
|
3801
|
+
results.push({
|
|
3802
|
+
scope: group.scope,
|
|
3803
|
+
status: applyStatus === 'failed' ? 'failed' : 'succeeded',
|
|
3804
|
+
configSaved,
|
|
3805
|
+
applyRequested: intent.apply,
|
|
3806
|
+
applyStatus,
|
|
3807
|
+
execution: { ...result, operationRebuildResults },
|
|
3808
|
+
...(applyStatus === 'failed' ? {
|
|
3809
|
+
errorCode: result.apply.state === 'unavailable'
|
|
3810
|
+
? result.apply.error?.code ?? result.apply.reason
|
|
3811
|
+
: result.apply.targetResults.find((target) => target.errorCode)?.errorCode
|
|
3812
|
+
?? 'runtimeApplyFailed',
|
|
3813
|
+
} : {}),
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
catch (error) {
|
|
3817
|
+
configSaved = error?.configWrite === 'succeeded';
|
|
3818
|
+
const errorCode = typeof error === 'object' && error !== null
|
|
3819
|
+
&& 'code' in error && typeof error.code === 'string'
|
|
3820
|
+
? error.code
|
|
3821
|
+
: configSaved ? 'runtimeApplyFailed' : 'configWriteFailed';
|
|
3822
|
+
results.push({
|
|
3823
|
+
scope: group.scope,
|
|
3824
|
+
status: 'failed',
|
|
3825
|
+
configSaved,
|
|
3826
|
+
applyRequested: intent.apply,
|
|
3827
|
+
applyStatus: intent.apply ? 'failed' : 'notRequested',
|
|
3828
|
+
errorCode,
|
|
3829
|
+
...(operationRebuildResults.length > 0
|
|
3830
|
+
? { execution: { operationRebuildResults } }
|
|
3831
|
+
: {}),
|
|
3832
|
+
});
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
const succeeded = results.filter((result) => result.status === 'succeeded').length;
|
|
3836
|
+
return {
|
|
3837
|
+
status: succeeded === results.length
|
|
3838
|
+
? 'succeeded'
|
|
3839
|
+
: succeeded > 0 || results.some((result) => result.configSaved)
|
|
3840
|
+
? 'partial'
|
|
3841
|
+
: 'failed',
|
|
3842
|
+
groups: results,
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
1002
3845
|
if (intent.kind === 'configSave' || intent.kind === 'configReset') {
|
|
3846
|
+
const operationRebuildResults = [];
|
|
3847
|
+
const operationDiagnosticSink = (results) => {
|
|
3848
|
+
operationRebuildResults.push(...structuredClone(results));
|
|
3849
|
+
};
|
|
1003
3850
|
const currentSource = currentEvidence.source;
|
|
3851
|
+
if (intent.kind === 'configReset') {
|
|
3852
|
+
const parsed = parsePluginId(intent.pluginId);
|
|
3853
|
+
if (!parsed.sourceId || !parsed.pluginName) {
|
|
3854
|
+
throw operationReviewStale('确认信息缺少 qualified 设置目标。');
|
|
3855
|
+
}
|
|
3856
|
+
return resetPluginConfigWithHelper({
|
|
3857
|
+
sourceId: parsed.sourceId,
|
|
3858
|
+
pluginName: parsed.pluginName,
|
|
3859
|
+
scope: intent.scope,
|
|
3860
|
+
cwd: context.cwd,
|
|
3861
|
+
env: context.env,
|
|
3862
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
3863
|
+
rebuildRuntime: { ...context, awHomeMutationToken },
|
|
3864
|
+
awHomeMutationToken,
|
|
3865
|
+
apply: false,
|
|
3866
|
+
sharedNativeApplyConfirmed: false,
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
1004
3869
|
if (!currentSource) {
|
|
1005
3870
|
throw operationReviewStale('确认信息缺少来源身份。');
|
|
1006
3871
|
}
|
|
@@ -1008,40 +3873,76 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1008
3873
|
return withResolvedCatalogPlugin({
|
|
1009
3874
|
selector: intent.pluginId,
|
|
1010
3875
|
cliVersion: packageInfo.version,
|
|
1011
|
-
freshnessPolicy: 'requireRemote',
|
|
1012
3876
|
env: context.env,
|
|
3877
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
1013
3878
|
consume: async ({ plugin, source }) => {
|
|
1014
3879
|
assertSourceEvidenceMatches(currentSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
|
|
1015
|
-
const
|
|
1016
|
-
?
|
|
1017
|
-
plugin,
|
|
3880
|
+
const applyTargets = intent.retryApply || intent.apply
|
|
3881
|
+
? currentEvidence.config?.affectedTargets.map((target) => ({
|
|
1018
3882
|
pluginName: plugin.manifest.name,
|
|
3883
|
+
platform: target.platform,
|
|
3884
|
+
scope: target.scope,
|
|
3885
|
+
})) ?? []
|
|
3886
|
+
: [];
|
|
3887
|
+
const result = intent.retryApply
|
|
3888
|
+
? await replayConfigRuntimeWrites({
|
|
3889
|
+
plugin,
|
|
1019
3890
|
scope: intent.scope,
|
|
1020
|
-
projectLayer: intent.projectLayer,
|
|
1021
3891
|
cwd: context.cwd,
|
|
1022
3892
|
env: context.env,
|
|
1023
3893
|
nativeCliRunner: context.nativeCliRunner,
|
|
1024
|
-
rebuildRuntime: {
|
|
3894
|
+
rebuildRuntime: {
|
|
3895
|
+
...context,
|
|
3896
|
+
awHomeMutationToken,
|
|
3897
|
+
operationDiagnosticSink,
|
|
3898
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3899
|
+
? {}
|
|
3900
|
+
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3901
|
+
...(catalogOperation ? {
|
|
3902
|
+
catalogOperationContext: catalogOperation,
|
|
3903
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3904
|
+
} : {}),
|
|
3905
|
+
},
|
|
1025
3906
|
awHomeMutationToken,
|
|
1026
|
-
|
|
3907
|
+
applyTargets,
|
|
3908
|
+
sharedNativeApplyConfirmed: true,
|
|
3909
|
+
preApplyConfirmed: true,
|
|
3910
|
+
preApplyAuthorizedTargets: expectedEvidence.config?.preApplyAuthorization ?? [],
|
|
1027
3911
|
})
|
|
1028
|
-
: await
|
|
3912
|
+
: await applyPluginConfigWrite({
|
|
1029
3913
|
plugin,
|
|
1030
3914
|
pluginName: plugin.manifest.name,
|
|
1031
3915
|
scope: intent.scope,
|
|
1032
|
-
projectLayer: intent.projectLayer,
|
|
1033
3916
|
cwd: context.cwd,
|
|
1034
3917
|
env: context.env,
|
|
1035
3918
|
nativeCliRunner: context.nativeCliRunner,
|
|
1036
|
-
rebuildRuntime: {
|
|
3919
|
+
rebuildRuntime: {
|
|
3920
|
+
...context,
|
|
3921
|
+
awHomeMutationToken,
|
|
3922
|
+
operationDiagnosticSink,
|
|
3923
|
+
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3924
|
+
? {}
|
|
3925
|
+
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3926
|
+
...(catalogOperation ? {
|
|
3927
|
+
catalogOperationContext: catalogOperation,
|
|
3928
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3929
|
+
} : {}),
|
|
3930
|
+
},
|
|
1037
3931
|
awHomeMutationToken,
|
|
3932
|
+
applyTargets,
|
|
3933
|
+
request: { updates: intent.updates, removeKeys: intent.removeKeys },
|
|
3934
|
+
sharedNativeApplyConfirmed: true,
|
|
3935
|
+
preApplyConfirmed: true,
|
|
3936
|
+
preApplyAuthorizedTargets: expectedEvidence.config?.preApplyAuthorization ?? [],
|
|
1038
3937
|
});
|
|
1039
|
-
return result;
|
|
3938
|
+
return { ...result, operationRebuildResults };
|
|
1040
3939
|
},
|
|
1041
3940
|
});
|
|
1042
3941
|
}
|
|
1043
3942
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
1044
3943
|
},
|
|
3944
|
+
}).finally(async () => {
|
|
3945
|
+
await codexObservationSession?.dispose();
|
|
1045
3946
|
});
|
|
1046
3947
|
},
|
|
1047
3948
|
};
|