@lesliechan721/aw 0.1.0-beta.7 → 0.1.0-beta.9
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/scripts/add-skill.mjs +4 -4
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +8 -37
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
- 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 +2 -1
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +64 -758
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
- package/dist/scripts/{plugin-eval-materializer.js → aw-skill-eval-materializer.js} +34 -145
- 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 +2 -1
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +6 -8
- package/dist/scripts/plugin-staging.js +15 -28
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +2 -33
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- 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 +17 -1
- package/dist/src/authoring/workspace-commands.js +21 -4
- 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 +254 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +118 -0
- package/dist/src/commands/catalog-dev-source.js +417 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +13 -0
- package/dist/src/commands/catalog-generation.js +13 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +29 -28
- package/dist/src/commands/catalog-publication-inspection.js +138 -50
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.d.ts +49 -0
- package/dist/src/commands/catalog-publication-journal.js +147 -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 +58 -15
- package/dist/src/commands/catalog-publish.js +590 -210
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +12 -15
- package/dist/src/commands/catalog-source.js +61 -86
- 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-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/dependency-operation-plan.d.ts +88 -9
- package/dist/src/commands/dependency-operation-plan.js +331 -6
- 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/offline-cleanup-evidence.d.ts +43 -0
- package/dist/src/commands/offline-cleanup-evidence.js +116 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/options.d.ts +7 -1
- package/dist/src/commands/options.js +65 -6
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +5 -1
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +27 -13
- package/dist/src/commands/rebuild-command.js +1472 -471
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +63 -8
- package/dist/src/commands/rebuild-integration.js +761 -439
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +2 -8
- package/dist/src/commands/rebuild-operations.js +48 -132
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/runtime.js +21 -34
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +33 -0
- package/dist/src/commands/skill-eval.js +363 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +202 -0
- package/dist/src/commands/status-v6.js +1938 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +21 -0
- package/dist/src/commands/sync-command.js +206 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +2 -2
- package/dist/src/commands/wizard.js +2 -2
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.js +33 -19
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +66 -0
- package/dist/src/eval/manifest-v1.js +326 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +3 -0
- package/dist/src/generators/local-plugin.js +167 -45
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +863 -126
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +58 -8
- package/dist/src/output/command-receipt.js +141 -11
- 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 +0 -3
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +6 -2
- package/dist/src/portable/commands.js +57 -6
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +4 -4
- package/dist/src/portable/materializer.js +61 -12
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/catalog-generation.d.ts +35 -0
- package/dist/src/tui/catalog-generation.js +85 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/components.js +1 -2
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +647 -196
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +6 -2
- package/dist/src/tui/controller-runtime.js +646 -137
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +77 -15
- package/dist/src/tui/controller.js +647 -306
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostics.d.ts +1 -2
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.js +1 -0
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +8 -0
- package/dist/src/tui/input-display.js +36 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/model.d.ts +102 -14
- package/dist/src/tui/model.js +630 -139
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/router.d.ts +35 -0
- package/dist/src/tui/router.js.map +1 -1
- 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/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/types.d.ts +104 -13
- 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 +41 -8
- package/dist/src/utils/agent-version.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 +8 -6
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.d.ts +7 -0
- package/dist/src/utils/catalog-digests.js +40 -2
- 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 +13 -0
- package/dist/src/utils/catalog-generation-error.js +28 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +20 -0
- package/dist/src/utils/catalog-generation-resolver.js +143 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +43 -0
- package/dist/src/utils/catalog-generation-service.js +306 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +17 -0
- package/dist/src/utils/catalog-generation-state.js +168 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +34 -0
- package/dist/src/utils/catalog-generation-store.js +251 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v6.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v6.js} +35 -43
- package/dist/src/utils/catalog-manifest-v6.js.map +1 -0
- package/dist/src/utils/catalog-operation-context.d.ts +20 -0
- package/dist/src/utils/catalog-operation-context.js +141 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +21 -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-references.d.ts +7 -0
- package/dist/src/utils/catalog-source-references.js +44 -55
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +1 -0
- package/dist/src/utils/catalog-sources.js +55 -20
- package/dist/src/utils/catalog-sources.js.map +1 -1
- 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 +81 -0
- package/dist/src/utils/codex-manager.js +810 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-reconciler.d.ts +131 -0
- package/dist/src/utils/codex-reconciler.js +327 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +56 -0
- package/dist/src/utils/codex-shared-native.js +87 -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-surface-analysis.d.ts +44 -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 +4 -0
- package/dist/src/utils/dynamic-skill-tree.js +35 -5
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.js +66 -140
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -28
- package/dist/src/utils/lifecycle-active-store.js +35 -247
- package/dist/src/utils/lifecycle-active-store.js.map +1 -1
- 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 +53 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +49 -6
- package/dist/src/utils/native-cli.js +116 -33
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/orphan-marketplace-cleanup.js +1 -3
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +10 -11
- package/dist/src/utils/ownership.js +55 -112
- 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 +52 -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-write.js +18 -40
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +1 -12
- package/dist/src/utils/plugin-config.js +107 -226
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +39 -0
- package/dist/src/utils/plugin-intent-state.js +325 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +1 -1
- package/dist/src/utils/plugin-scripts.js +1 -1
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- 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 +9 -5
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/package.json +6 -4
- 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.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/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/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,6 +1,9 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
|
|
3
|
+
import { requestedIntentAction } from '../output/command-receipt.js';
|
|
4
|
+
import { buildTuiViewModel, dependencyPlacementTargets, hasInstalledPlacement, } from './model.js';
|
|
3
5
|
import { safeDiagnosticCode } from './diagnostics.js';
|
|
6
|
+
import { targetHasArtifactCollision } from '../utils/target-eligibility.js';
|
|
4
7
|
import { TuiRouter, } from './router.js';
|
|
5
8
|
function reviewPreparationStageLabel(stage) {
|
|
6
9
|
return {
|
|
@@ -42,7 +45,10 @@ function cloneFrozen(value) {
|
|
|
42
45
|
return deepFreeze(structuredClone(value));
|
|
43
46
|
}
|
|
44
47
|
export function operationEvidenceFingerprint(evidence) {
|
|
45
|
-
|
|
48
|
+
const { codexManager: _runtimeSelection, ...staleEvidence } = evidence;
|
|
49
|
+
return crypto.createHash('sha256')
|
|
50
|
+
.update(JSON.stringify(stableValue(staleEvidence)))
|
|
51
|
+
.digest('hex');
|
|
46
52
|
}
|
|
47
53
|
function sourceIdFromPluginId(pluginId) {
|
|
48
54
|
const separator = pluginId.indexOf('/');
|
|
@@ -69,6 +75,22 @@ function operationLabels(intent) {
|
|
|
69
75
|
destructive: false,
|
|
70
76
|
};
|
|
71
77
|
}
|
|
78
|
+
if (intent.kind === 'pluginWrite' && intent.dependencyPlacementOnly === 'desired') {
|
|
79
|
+
return {
|
|
80
|
+
title: '调整依赖位置',
|
|
81
|
+
description: '先更新当前项目的 desired placement;不会删除所有项目中的实际内容。',
|
|
82
|
+
primaryLabel: '更新 desired placement',
|
|
83
|
+
destructive: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (intent.kind === 'pluginWrite' && intent.dependencyPlacementAdjustment) {
|
|
87
|
+
return {
|
|
88
|
+
title: '调整依赖位置',
|
|
89
|
+
description: '按最终依赖位置更新 desired intent,并应用非破坏性变更。',
|
|
90
|
+
primaryLabel: '应用依赖位置',
|
|
91
|
+
destructive: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
72
94
|
if (intent.kind === 'pluginWrite') {
|
|
73
95
|
if (intent.originIssue) {
|
|
74
96
|
if (intent.action === 'cleanup') {
|
|
@@ -100,6 +122,7 @@ function operationLabels(intent) {
|
|
|
100
122
|
reinstall: ['重新安装插件', '清理 AW 管理的内容后完整重建。', true],
|
|
101
123
|
uninstall: ['移除插件', '移除 AW 管理的安装内容。配置保持不变。', true],
|
|
102
124
|
cleanup: ['清理插件', '只移除可证明属于 AW 的现场内容。配置保持不变。', true],
|
|
125
|
+
sync: ['同步插件', '按已保存的 desired intent 修复当前现场。', false],
|
|
103
126
|
};
|
|
104
127
|
const [title, description, destructive] = labels[intent.action];
|
|
105
128
|
return { title, description, primaryLabel: title, destructive };
|
|
@@ -122,8 +145,8 @@ function operationLabels(intent) {
|
|
|
122
145
|
}
|
|
123
146
|
if (intent.kind === 'sourceMutation') {
|
|
124
147
|
const labels = {
|
|
125
|
-
add: ['添加来源', '
|
|
126
|
-
update: ['修改来源', '
|
|
148
|
+
add: ['添加来源', '只保存来源配置。保存后请在终端运行 source sync。', '保存来源', false],
|
|
149
|
+
update: ['修改来源', '只保存来源配置。保存后请在终端运行 source sync。', '保存来源', false],
|
|
127
150
|
setPrimary: ['设为主来源', '只改变默认 marketplace,不迁移安装。', '设为主来源', false],
|
|
128
151
|
remove: ['移除来源', '移除来源配置;不会删除其它来源。', '移除来源', true],
|
|
129
152
|
};
|
|
@@ -164,42 +187,138 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
164
187
|
];
|
|
165
188
|
}
|
|
166
189
|
if (intent.kind === 'pluginWrite') {
|
|
190
|
+
const desiredIntentKind = requestedIntentAction(intent.action);
|
|
191
|
+
const runtimeChanges = evidence?.codexManager
|
|
192
|
+
? [{
|
|
193
|
+
kind: evidence.codexManager.state === 'requires-install' ? 'add' : 'keep',
|
|
194
|
+
group: 'runtime',
|
|
195
|
+
title: evidence.codexManager.state === 'requires-install'
|
|
196
|
+
? '获取 Codex runtime'
|
|
197
|
+
: 'Codex runtime',
|
|
198
|
+
description: evidence.codexManager.state === 'requires-install'
|
|
199
|
+
? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · AW-managed 缓存`
|
|
200
|
+
: evidence.codexManager.state === 'cached'
|
|
201
|
+
? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · 复用 AW-managed 缓存`
|
|
202
|
+
: `使用兼容的 system Codex ${evidence.codexManager.version}`,
|
|
203
|
+
}]
|
|
204
|
+
: [];
|
|
205
|
+
const codexChanges = (evidence?.codexReconcilePlans ?? []).flatMap((codexPlan) => [
|
|
206
|
+
...codexPlan.sharedNativeActions.map((action) => ({
|
|
207
|
+
kind: action.action === 'reuse' || action.action === 'claim' || action.action === 'none' ? 'keep'
|
|
208
|
+
: action.action === 'install' ? 'add' : 'change',
|
|
209
|
+
group: 'sharedCodexNative',
|
|
210
|
+
pluginId: action.pluginId,
|
|
211
|
+
title: action.action === 'claim'
|
|
212
|
+
? `${action.codexPluginId}:复用精确现有 shared native(不建立 ledger)`
|
|
213
|
+
: action.codexPluginId,
|
|
214
|
+
description: action.action === 'claim'
|
|
215
|
+
? '复用已存在的精确 shared native;不建立 ownership ledger'
|
|
216
|
+
: `${action.action} · shared${action.affectsOtherScopes ? ' · 可能影响其他项目' : ''}`,
|
|
217
|
+
})),
|
|
218
|
+
...codexPlan.activationActions.user.map((action) => ({
|
|
219
|
+
kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
|
|
220
|
+
group: 'userActivation',
|
|
221
|
+
pluginId: action.pluginId,
|
|
222
|
+
title: action.codexPluginId,
|
|
223
|
+
description: `desired=${String(action.desired)} · ${action.action}`,
|
|
224
|
+
})),
|
|
225
|
+
...codexPlan.activationActions.project.map((action) => ({
|
|
226
|
+
kind: action.action === 'none' ? 'keep' : action.desired ? 'change' : 'remove',
|
|
227
|
+
group: 'projectActivation',
|
|
228
|
+
pluginId: action.pluginId,
|
|
229
|
+
title: action.codexPluginId,
|
|
230
|
+
description: `desired=${String(action.desired)} · ${action.action}`,
|
|
231
|
+
})),
|
|
232
|
+
...[...codexPlan.extensionWrites, ...codexPlan.extensionRemovals]
|
|
233
|
+
.map((action) => ({
|
|
234
|
+
kind: action.action === 'write' ? 'change' : 'remove',
|
|
235
|
+
group: 'awExtensions',
|
|
236
|
+
pluginId: action.pluginId,
|
|
237
|
+
title: action.componentKey,
|
|
238
|
+
description: `${action.scope} · ${action.action}`,
|
|
239
|
+
})),
|
|
240
|
+
]);
|
|
167
241
|
const plan = evidence?.dependencyPlan;
|
|
168
|
-
if (plan && (intent.action === 'install' || intent.action === 'upgrade'
|
|
242
|
+
if (plan && (intent.action === 'install' || intent.action === 'upgrade'
|
|
243
|
+
|| intent.action === 'reinstall' || intent.action === 'sync')) {
|
|
169
244
|
const changes = plan.targets.map((target) => {
|
|
170
245
|
const relation = dependencyRelation(plan, target.pluginId);
|
|
171
|
-
const actionKind = target.action === 'keep'
|
|
172
|
-
? 'keep'
|
|
173
|
-
: target.action === 'install'
|
|
174
|
-
? 'add'
|
|
175
|
-
: 'change';
|
|
176
246
|
return {
|
|
177
|
-
kind:
|
|
178
|
-
group:
|
|
247
|
+
kind: desiredIntentKind,
|
|
248
|
+
group: target.resolvedScope === 'project' ? 'projectIntent' : 'userIntent',
|
|
179
249
|
pluginId: target.pluginId,
|
|
180
250
|
relation,
|
|
181
251
|
title: pluginNameFromId(target.pluginId),
|
|
182
|
-
description: `${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.resolvedScope === 'user' ? '所有项目' : '当前项目'}${target.action === 'keep' ? ' · 已可用' : ''}`,
|
|
252
|
+
description: `${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.resolvedScope === 'user' ? '所有项目' : '当前项目'}${target.action === 'keep' ? ' · 已可用' : ''}${target.assignmentSource === 'existingWiderTarget' ? ' · 复用健康的所有项目安装' : target.assignmentSource === 'consumerPromotion' ? ' · 随上游依赖一并调整' : ''}`,
|
|
183
253
|
};
|
|
184
254
|
});
|
|
185
255
|
changes.push({
|
|
186
256
|
kind: 'keep',
|
|
187
257
|
group: 'preserved',
|
|
188
|
-
title:
|
|
189
|
-
|
|
258
|
+
title: intent.dependencyPlacementAdjustment
|
|
259
|
+
? '根插件和插件设置保持不变'
|
|
260
|
+
: '插件设置和已有依赖保持不变',
|
|
261
|
+
description: intent.dependencyPlacementAdjustment
|
|
262
|
+
? '这里只调整所列 dependency placement。'
|
|
263
|
+
: '移除根插件时不会自动清理依赖。',
|
|
190
264
|
});
|
|
191
|
-
|
|
265
|
+
if (intent.dependencyPlacementOnly === 'desired') {
|
|
266
|
+
changes.push({
|
|
267
|
+
kind: desiredIntentKind,
|
|
268
|
+
group: 'projectIntent',
|
|
269
|
+
title: '先更新依赖 desired placement',
|
|
270
|
+
description: '这一步不删除所有项目中的实际内容。',
|
|
271
|
+
}, {
|
|
272
|
+
kind: 'warning',
|
|
273
|
+
group: 'preserved',
|
|
274
|
+
title: '后续第 1 步:显式 user cleanup;不自动扫描/清理',
|
|
275
|
+
description: '请在已知的所有项目位置确认 cleanup。',
|
|
276
|
+
}, {
|
|
277
|
+
kind: 'keep',
|
|
278
|
+
group: 'preserved',
|
|
279
|
+
title: '后续第 2 步:同步当前项目',
|
|
280
|
+
description: 'cleanup 完成后运行当前项目 sync。',
|
|
281
|
+
});
|
|
282
|
+
return changes;
|
|
283
|
+
}
|
|
284
|
+
return [...changes, ...codexChanges, ...runtimeChanges];
|
|
192
285
|
}
|
|
193
286
|
const platforms = intent.platform === 'both'
|
|
194
287
|
? ['codex', 'claude']
|
|
195
288
|
: [intent.platform];
|
|
196
289
|
const changes = platforms.map((platform) => ({
|
|
290
|
+
kind: desiredIntentKind,
|
|
291
|
+
group: intent.scope === 'project' ? 'projectIntent' : 'userIntent',
|
|
292
|
+
pluginId: intent.pluginId,
|
|
293
|
+
relation: 'requested',
|
|
294
|
+
title: pluginNameFromId(intent.pluginId),
|
|
295
|
+
description: `${platform === 'codex' ? 'Codex' : 'Claude Code'} · ${intent.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
296
|
+
}));
|
|
297
|
+
changes.push(...platforms.map((platform) => ({
|
|
197
298
|
kind: intent.action === 'uninstall' || intent.action === 'cleanup' ? 'remove' : intent.action === 'install' ? 'add' : 'change',
|
|
299
|
+
group: 'requested',
|
|
300
|
+
pluginId: intent.pluginId,
|
|
301
|
+
relation: 'requested',
|
|
198
302
|
title: `${platform === 'codex' ? 'Codex' : 'Claude Code'} · ${intent.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
199
303
|
description: intent.action === 'uninstall' || intent.action === 'cleanup'
|
|
200
304
|
? '插件设置不会删除。'
|
|
201
305
|
: '执行前会再次检查当前安装状态。',
|
|
202
|
-
}));
|
|
306
|
+
})));
|
|
307
|
+
if (intent.action === 'cleanup' && evidence?.offlineCleanup) {
|
|
308
|
+
const actionLabels = {
|
|
309
|
+
removeOwnedOutput: '移除 AW-owned 输出',
|
|
310
|
+
removeNativeRegistration: '移除原生插件注册',
|
|
311
|
+
removeLifecycleSnapshot: '移除 lifecycle snapshot',
|
|
312
|
+
};
|
|
313
|
+
changes.push(...evidence.offlineCleanup.plan.actions.map((action) => ({
|
|
314
|
+
kind: 'remove',
|
|
315
|
+
group: 'requested',
|
|
316
|
+
pluginId: evidence.offlineCleanup.pluginId,
|
|
317
|
+
relation: 'requested',
|
|
318
|
+
title: actionLabels[action.action],
|
|
319
|
+
description: action.target,
|
|
320
|
+
})));
|
|
321
|
+
}
|
|
203
322
|
if (intent.action === 'uninstall' || intent.action === 'cleanup') {
|
|
204
323
|
changes.push(...(dependencyOverview?.dependencies ?? []).map((dependency) => ({
|
|
205
324
|
kind: 'keep',
|
|
@@ -220,17 +339,31 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
220
339
|
if (intent.originIssue) {
|
|
221
340
|
changes.push({ kind: 'keep', title: '插件设置保持不变' }, { kind: 'warning', title: '执行前重新检查完整目标和来源证据' });
|
|
222
341
|
}
|
|
223
|
-
|
|
342
|
+
if (intent.recoveryDescription) {
|
|
343
|
+
changes.push({
|
|
344
|
+
kind: 'warning',
|
|
345
|
+
group: 'preserved',
|
|
346
|
+
title: `后续:${intent.scope === 'user' ? '当前项目' : '所有项目'} sync`,
|
|
347
|
+
description: intent.recoveryDescription,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return [...changes, ...codexChanges, ...runtimeChanges];
|
|
224
351
|
}
|
|
225
352
|
if (intent.kind === 'configReset') {
|
|
226
353
|
const layerLabel = intent.scope === 'user'
|
|
227
354
|
? '个人默认'
|
|
228
355
|
: intent.projectLayer === 'local' ? '当前项目(仅本机)' : '当前项目';
|
|
229
356
|
return [
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
{ kind: '
|
|
357
|
+
...(intent.changes?.map((change) => ({
|
|
358
|
+
kind: 'remove', group: 'configChanges', title: `${layerLabel} · ${change.title}`,
|
|
359
|
+
description: `${change.before} → ${change.after}`,
|
|
360
|
+
})) ?? [{ kind: 'remove', group: 'configChanges', title: `删除${layerLabel}中的已存值`, description: '有效值将恢复为继承值或默认值。' }]),
|
|
361
|
+
...((evidence?.config?.affectedTargets.length ?? 0) > 0
|
|
362
|
+
? evidence.config.affectedTargets.map((target) => ({
|
|
363
|
+
kind: 'change', group: 'configImpact', title: `重新应用 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
364
|
+
}))
|
|
365
|
+
: [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
|
|
366
|
+
{ kind: 'keep', group: 'preserved', title: intent.scope === 'user' ? '当前项目和本机设置层保持不变' : '其他设置层保持不变' },
|
|
234
367
|
];
|
|
235
368
|
}
|
|
236
369
|
if (intent.kind === 'sourceMutation') {
|
|
@@ -255,17 +388,24 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
255
388
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
256
389
|
}
|
|
257
390
|
const privateKeys = new Set([...(intent.sensitiveKeys ?? []), ...(intent.privateKeys ?? [])]);
|
|
391
|
+
const removeKeys = new Set(intent.removeKeys ?? []);
|
|
258
392
|
if (intent.changes) {
|
|
259
393
|
return [
|
|
260
394
|
...intent.changes.map((change) => ({
|
|
261
|
-
kind: 'change',
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: `${change.before} → ${change.after}
|
|
395
|
+
kind: removeKeys.has(change.key) ? 'remove' : 'change',
|
|
396
|
+
group: 'configChanges',
|
|
397
|
+
title: removeKeys.has(change.key) ? `删除 ${change.title} 中的值` : change.title,
|
|
398
|
+
description: removeKeys.has(change.key)
|
|
399
|
+
? privateKeys.has(change.key) ? `已设置 → ${change.after}` : `${change.before} → ${change.after}`
|
|
400
|
+
: privateKeys.has(change.key) ? '已设置 → 将更新'
|
|
401
|
+
: `${change.before} → ${change.after}`,
|
|
266
402
|
})),
|
|
267
|
-
|
|
268
|
-
|
|
403
|
+
...((evidence?.config?.affectedTargets.length ?? 0) > 0
|
|
404
|
+
? evidence.config.affectedTargets.map((target) => ({
|
|
405
|
+
kind: 'change', group: 'configImpact', title: `重新应用 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
406
|
+
}))
|
|
407
|
+
: [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
|
|
408
|
+
{ kind: 'keep', group: 'preserved', title: '其他项目不会被自动扫描' },
|
|
269
409
|
];
|
|
270
410
|
}
|
|
271
411
|
const updateKeys = Object.keys(intent.updates ?? {});
|
|
@@ -283,33 +423,51 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
283
423
|
];
|
|
284
424
|
}
|
|
285
425
|
function technicalDetails(evidence) {
|
|
426
|
+
const short = (value) => {
|
|
427
|
+
const normalized = value.startsWith('sha256:') ? value.slice(7) : value;
|
|
428
|
+
return normalized.slice(0, 8);
|
|
429
|
+
};
|
|
286
430
|
const details = [
|
|
287
|
-
`Catalog 配置:${evidence.catalog.configDigest}`,
|
|
288
|
-
`Catalog 组合:${evidence.catalog.compositeDigest}`,
|
|
431
|
+
`Catalog 配置:${short(evidence.catalog.configDigest)}`,
|
|
432
|
+
`Catalog 组合:${short(evidence.catalog.compositeDigest)}`,
|
|
289
433
|
];
|
|
434
|
+
if (evidence.catalog.generationSetDigest) {
|
|
435
|
+
details.push(`Catalog generation:${short(evidence.catalog.generationSetDigest)}`);
|
|
436
|
+
}
|
|
290
437
|
if (evidence.source) {
|
|
291
438
|
details.push(`来源:${evidence.source.sourceId}`);
|
|
292
|
-
|
|
439
|
+
if (evidence.source.sourceIdentity)
|
|
440
|
+
details.push(`来源身份:${short(evidence.source.sourceIdentity)}`);
|
|
293
441
|
if (evidence.source.resolvedCommit)
|
|
294
|
-
details.push(`Snapshot commit:${evidence.source.resolvedCommit}`);
|
|
442
|
+
details.push(`Snapshot commit:${short(evidence.source.resolvedCommit)}`);
|
|
295
443
|
if (evidence.source.snapshotDigest)
|
|
296
|
-
details.push(`Snapshot digest:${evidence.source.snapshotDigest}`);
|
|
444
|
+
details.push(`Snapshot digest:${short(evidence.source.snapshotDigest)}`);
|
|
297
445
|
if (evidence.source.contentDigest)
|
|
298
|
-
details.push(`Content digest:${evidence.source.contentDigest}`);
|
|
446
|
+
details.push(`Content digest:${short(evidence.source.contentDigest)}`);
|
|
299
447
|
}
|
|
300
448
|
if (evidence.targetFingerprint)
|
|
301
|
-
details.push(`目标状态:${evidence.targetFingerprint}`);
|
|
449
|
+
details.push(`目标状态:${short(evidence.targetFingerprint)}`);
|
|
450
|
+
if (evidence.offlineCleanup) {
|
|
451
|
+
details.push(`离线清理原因:${evidence.offlineCleanup.reason}`);
|
|
452
|
+
details.push(`离线清理目标:${short(evidence.offlineCleanup.targetFingerprint)}`);
|
|
453
|
+
details.push(`离线清理计划:${evidence.offlineCleanup.plan.actions.length} 个动作 · ${short(evidence.offlineCleanup.planDigest)}`);
|
|
454
|
+
for (const action of evidence.offlineCleanup.plan.actions) {
|
|
455
|
+
details.push(`离线清理动作:${action.action} · ${action.target}`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
302
458
|
if (evidence.dependencyPlan)
|
|
303
459
|
details.push(`安装计划:${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`);
|
|
460
|
+
if (evidence.projectIntentDigest)
|
|
461
|
+
details.push(`Project intent:${short(evidence.projectIntentDigest)}`);
|
|
304
462
|
if (evidence.config)
|
|
305
|
-
details.push(`设置层:${evidence.config.scope}/${evidence.config.projectLayer} · ${evidence.config.storeDigest}`);
|
|
463
|
+
details.push(`设置层:${evidence.config.scope}/${evidence.config.projectLayer} · ${short(evidence.config.storeDigest)}`);
|
|
306
464
|
if (evidence.sourceWrite) {
|
|
307
|
-
details.push(
|
|
308
|
-
details.push(`引用清单:${evidence.sourceWrite.inventoryDigest} · ${evidence.sourceWrite.referenceCount} 项`);
|
|
465
|
+
details.push(`新配置:${short(evidence.sourceWrite.proposedConfigDigest)}`);
|
|
466
|
+
details.push(`引用清单:${short(evidence.sourceWrite.inventoryDigest)} · ${evidence.sourceWrite.referenceCount} 项`);
|
|
309
467
|
}
|
|
310
468
|
if (evidence.portable) {
|
|
311
469
|
details.push(`Portable 候选:${evidence.portable.selected.length} 个`);
|
|
312
|
-
details.push(`Materialization:${evidence.portable.materializationDigest}`);
|
|
470
|
+
details.push(`Materialization:${short(evidence.portable.materializationDigest)}`);
|
|
313
471
|
details.push(`Skills CLI:${evidence.portable.skillsCliVersion}`);
|
|
314
472
|
}
|
|
315
473
|
return details;
|
|
@@ -317,6 +475,8 @@ function technicalDetails(evidence) {
|
|
|
317
475
|
function issueCode(error) {
|
|
318
476
|
if (error instanceof TuiOperationBlockedError)
|
|
319
477
|
return error.code;
|
|
478
|
+
if (error instanceof CatalogGenerationError)
|
|
479
|
+
return error.code;
|
|
320
480
|
if (error instanceof Error && error.name === 'CatalogSourcesConfigConflictError') {
|
|
321
481
|
return 'operationReviewStale';
|
|
322
482
|
}
|
|
@@ -347,9 +507,6 @@ export class TuiController {
|
|
|
347
507
|
#router = new TuiRouter();
|
|
348
508
|
#listeners = new Set();
|
|
349
509
|
#health = new Map();
|
|
350
|
-
#sourceTokens = new Map();
|
|
351
|
-
#successfulSourceRefreshes = new Map();
|
|
352
|
-
#sourceAborts = new Map();
|
|
353
510
|
#tickets = new Map();
|
|
354
511
|
#inflightOperations = new Map();
|
|
355
512
|
#abort = new AbortController();
|
|
@@ -357,6 +514,8 @@ export class TuiController {
|
|
|
357
514
|
#progressFeed;
|
|
358
515
|
#phase = 'idle';
|
|
359
516
|
#status;
|
|
517
|
+
#catalogGenerations = new Map();
|
|
518
|
+
#sessionGenerationKeys = new Map();
|
|
360
519
|
#viewModel;
|
|
361
520
|
#progress = { messages: [] };
|
|
362
521
|
#progressTargets = [];
|
|
@@ -370,9 +529,10 @@ export class TuiController {
|
|
|
370
529
|
#portableRequestToken = 0;
|
|
371
530
|
#portableRequestAbort;
|
|
372
531
|
#portablePending;
|
|
532
|
+
#installPlacementDraft;
|
|
533
|
+
#installPlacementRequestToken = 0;
|
|
534
|
+
#installPlacementRequestAbort;
|
|
373
535
|
#generation = 0;
|
|
374
|
-
#refreshSequence = 0;
|
|
375
|
-
#appliedRefreshSequence = 0;
|
|
376
536
|
#disposed = false;
|
|
377
537
|
constructor(dependencies, progressFeed) {
|
|
378
538
|
this.dependencies = dependencies;
|
|
@@ -407,9 +567,13 @@ export class TuiController {
|
|
|
407
567
|
get snapshot() {
|
|
408
568
|
return cloneFrozen({
|
|
409
569
|
phase: this.#phase,
|
|
570
|
+
...(this.#status?.cliChannel ? { cliChannel: this.#status.cliChannel } : {}),
|
|
410
571
|
router: this.#router.snapshot,
|
|
411
572
|
...(this.#viewModel ? { viewModel: this.#viewModel } : {}),
|
|
412
573
|
sourceHealth: [...this.#health.values()],
|
|
574
|
+
catalogGenerations: [...this.#catalogGenerations.values()]
|
|
575
|
+
.sort((left, right) => left.sourceId.localeCompare(right.sourceId, 'en')),
|
|
576
|
+
reloadRequiredSourceIds: [],
|
|
413
577
|
progress: this.#progress,
|
|
414
578
|
progressTargets: this.#progressTargets,
|
|
415
579
|
...(this.#activeReview ? { activeReview: this.#activeReview } : {}),
|
|
@@ -417,6 +581,20 @@ export class TuiController {
|
|
|
417
581
|
...(this.#result ? { result: this.#result } : {}),
|
|
418
582
|
...(this.#loadError ? { loadError: this.#loadError } : {}),
|
|
419
583
|
...(this.#portableDraft ? { portableDraft: this.#portableDraft } : {}),
|
|
584
|
+
...(this.#installPlacementDraft
|
|
585
|
+
? { installPlacementDraft: {
|
|
586
|
+
pluginId: this.#installPlacementDraft.pluginId,
|
|
587
|
+
rootScope: this.#installPlacementDraft.rootScope,
|
|
588
|
+
platforms: this.#installPlacementDraft.platforms,
|
|
589
|
+
dependencyScopeOverrides: this.#installPlacementDraft.dependencyScopeOverrides,
|
|
590
|
+
state: this.#installPlacementDraft.state,
|
|
591
|
+
targets: this.#installPlacementDraft.targets,
|
|
592
|
+
canContinue: this.#installPlacementDraft.canContinue,
|
|
593
|
+
...(this.#installPlacementDraft.error
|
|
594
|
+
? { error: this.#installPlacementDraft.error }
|
|
595
|
+
: {}),
|
|
596
|
+
} }
|
|
597
|
+
: {}),
|
|
420
598
|
});
|
|
421
599
|
}
|
|
422
600
|
subscribe(listener) {
|
|
@@ -467,6 +645,8 @@ export class TuiController {
|
|
|
467
645
|
|| route.name === 'pluginDependencies'
|
|
468
646
|
|| route.name === 'installPlacement'
|
|
469
647
|
|| route.name === 'installTargets'
|
|
648
|
+
|| route.name === 'dependencyPlacement'
|
|
649
|
+
|| route.name === 'dependencyPlacementEditor'
|
|
470
650
|
|| route.name === 'pluginDetail'
|
|
471
651
|
|| route.name === 'platform'
|
|
472
652
|
|| route.name === 'pluginAbout'
|
|
@@ -480,6 +660,7 @@ export class TuiController {
|
|
|
480
660
|
name: 'pluginList',
|
|
481
661
|
mode: route.name === 'pluginPreview' || route.name === 'pluginDependencies'
|
|
482
662
|
|| route.name === 'installPlacement' || route.name === 'installTargets'
|
|
663
|
+
|| route.name === 'dependencyPlacement' || route.name === 'dependencyPlacementEditor'
|
|
483
664
|
? 'browse'
|
|
484
665
|
: 'installed',
|
|
485
666
|
};
|
|
@@ -505,74 +686,32 @@ export class TuiController {
|
|
|
505
686
|
}
|
|
506
687
|
#applyStatus(status) {
|
|
507
688
|
this.#status = structuredClone(status);
|
|
689
|
+
if (status.catalogGenerations) {
|
|
690
|
+
this.#catalogGenerations.clear();
|
|
691
|
+
for (const source of status.catalogGenerations) {
|
|
692
|
+
this.#catalogGenerations.set(source.sourceId, structuredClone(source));
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
this.#sessionGenerationKeys.clear();
|
|
696
|
+
if (status.sessionGenerationKeys) {
|
|
697
|
+
for (const [sourceId, generationKey] of Object.entries(status.sessionGenerationKeys)) {
|
|
698
|
+
this.#sessionGenerationKeys.set(sourceId, generationKey);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
508
701
|
this.#health.clear();
|
|
509
702
|
for (const source of status.payload.catalog.sources) {
|
|
510
703
|
this.#health.set(source.id, {
|
|
511
704
|
sourceId: source.id,
|
|
512
705
|
state: source.error ? 'unavailable'
|
|
513
|
-
:
|
|
514
|
-
: source.freshness === 'cache' ? 'cache' : 'ready',
|
|
706
|
+
: 'ready',
|
|
515
707
|
...(source.error ? { message: '来源当前没有可用数据。' } : {}),
|
|
516
708
|
});
|
|
517
|
-
const successful = this.#successfulSourceRefreshes.get(source.id);
|
|
518
|
-
if (successful?.generation === this.#generation
|
|
519
|
-
&& successful.sourceIdentity === source.identity
|
|
520
|
-
&& successful.requestedRef === source.requestedRef
|
|
521
|
-
&& !source.error) {
|
|
522
|
-
this.#health.set(source.id, {
|
|
523
|
-
sourceId: source.id,
|
|
524
|
-
state: source.type === 'path' || source.freshness === 'path' ? 'path' : 'ready',
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
const sourceIds = new Set(status.payload.catalog.sources.map((source) => source.id));
|
|
529
|
-
for (const sourceId of this.#successfulSourceRefreshes.keys()) {
|
|
530
|
-
if (!sourceIds.has(sourceId))
|
|
531
|
-
this.#successfulSourceRefreshes.delete(sourceId);
|
|
532
709
|
}
|
|
533
710
|
this.#project();
|
|
534
711
|
this.#normalizeCurrentRoute();
|
|
535
712
|
}
|
|
536
|
-
#applyRefreshStatus(status, sourceId, health, refreshSequence) {
|
|
537
|
-
if (refreshSequence < this.#appliedRefreshSequence)
|
|
538
|
-
return false;
|
|
539
|
-
this.#appliedRefreshSequence = refreshSequence;
|
|
540
|
-
this.#applyStatus(status);
|
|
541
|
-
if (!status.payload.catalog.sources.some((source) => source.id === sourceId))
|
|
542
|
-
return true;
|
|
543
|
-
this.#health.set(sourceId, health);
|
|
544
|
-
this.#project();
|
|
545
|
-
return true;
|
|
546
|
-
}
|
|
547
|
-
#recordSuccessfulSourceRefresh(sourceId, checked, generation) {
|
|
548
|
-
const authoritative = this.#status?.payload.catalog.sources.find((source) => source.id === sourceId);
|
|
549
|
-
if (!authoritative || authoritative.type !== 'git' || authoritative.error
|
|
550
|
-
|| authoritative.identity !== checked.sourceIdentity
|
|
551
|
-
|| authoritative.requestedRef !== checked.requestedRef)
|
|
552
|
-
return;
|
|
553
|
-
this.#successfulSourceRefreshes.set(sourceId, {
|
|
554
|
-
generation,
|
|
555
|
-
sourceIdentity: checked.sourceIdentity,
|
|
556
|
-
requestedRef: checked.requestedRef,
|
|
557
|
-
});
|
|
558
|
-
this.#health.set(sourceId, {
|
|
559
|
-
sourceId,
|
|
560
|
-
state: 'ready',
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
#cancelSourceRefreshes() {
|
|
564
|
-
for (const abort of this.#sourceAborts.values())
|
|
565
|
-
abort.abort();
|
|
566
|
-
this.#sourceAborts.clear();
|
|
567
|
-
}
|
|
568
|
-
#startGitSourceRefreshes() {
|
|
569
|
-
for (const source of this.#status?.payload.catalog.sources ?? []) {
|
|
570
|
-
if (source.type === 'git')
|
|
571
|
-
void this.refreshSource(source.id).catch(() => undefined);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
713
|
async load() {
|
|
575
|
-
this
|
|
714
|
+
this.dependencies.beginCatalogSession?.();
|
|
576
715
|
const generation = ++this.#generation;
|
|
577
716
|
this.#phase = 'loading';
|
|
578
717
|
this.#publish();
|
|
@@ -596,121 +735,11 @@ export class TuiController {
|
|
|
596
735
|
this.#phase = 'ready';
|
|
597
736
|
this.#loadError = undefined;
|
|
598
737
|
this.#publish();
|
|
599
|
-
this.#startGitSourceRefreshes();
|
|
600
|
-
}
|
|
601
|
-
async refreshSource(sourceId) {
|
|
602
|
-
if (this.#disposed || !this.#status)
|
|
603
|
-
return;
|
|
604
|
-
this.#sourceAborts.get(sourceId)?.abort();
|
|
605
|
-
const sourceAbort = new AbortController();
|
|
606
|
-
this.#sourceAborts.set(sourceId, sourceAbort);
|
|
607
|
-
const finishRefresh = () => {
|
|
608
|
-
if (this.#sourceAborts.get(sourceId) === sourceAbort)
|
|
609
|
-
this.#sourceAborts.delete(sourceId);
|
|
610
|
-
};
|
|
611
|
-
const signal = AbortSignal.any([this.#abort.signal, sourceAbort.signal]);
|
|
612
|
-
const token = (this.#sourceTokens.get(sourceId) ?? 0) + 1;
|
|
613
|
-
this.#sourceTokens.set(sourceId, token);
|
|
614
|
-
const generation = this.#generation;
|
|
615
|
-
const current = this.#status.payload.catalog.sources.find((source) => source.id === sourceId);
|
|
616
|
-
if (!current) {
|
|
617
|
-
finishRefresh();
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
const hadCache = current.type === 'git' && current.error === undefined
|
|
621
|
-
&& (current.freshness === 'cache' || current.freshness === 'remote');
|
|
622
|
-
this.#health.set(sourceId, { sourceId, state: 'checking' });
|
|
623
|
-
this.#project();
|
|
624
|
-
this.#publish();
|
|
625
|
-
let checkError;
|
|
626
|
-
let checked;
|
|
627
|
-
try {
|
|
628
|
-
checked = await this.dependencies.checkSource(sourceId, signal);
|
|
629
|
-
}
|
|
630
|
-
catch (error) {
|
|
631
|
-
checkError = error;
|
|
632
|
-
}
|
|
633
|
-
if (this.#disposed || signal.aborted || generation !== this.#generation
|
|
634
|
-
|| token !== this.#sourceTokens.get(sourceId)) {
|
|
635
|
-
finishRefresh();
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
if (checkError)
|
|
639
|
-
this.#successfulSourceRefreshes.delete(sourceId);
|
|
640
|
-
const refreshSequence = ++this.#refreshSequence;
|
|
641
|
-
let status;
|
|
642
|
-
let authoritativeReloadSucceeded = true;
|
|
643
|
-
try {
|
|
644
|
-
status = await this.dependencies.loadStatus(signal);
|
|
645
|
-
}
|
|
646
|
-
catch (error) {
|
|
647
|
-
authoritativeReloadSucceeded = false;
|
|
648
|
-
if (!checkError)
|
|
649
|
-
checkError = error;
|
|
650
|
-
this.#successfulSourceRefreshes.delete(sourceId);
|
|
651
|
-
status = structuredClone(this.#status);
|
|
652
|
-
}
|
|
653
|
-
if (this.#disposed || signal.aborted || generation !== this.#generation
|
|
654
|
-
|| token !== this.#sourceTokens.get(sourceId)) {
|
|
655
|
-
finishRefresh();
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
if (checkError) {
|
|
659
|
-
await this.#logErrorSafe(checkError, 'catalogSourceRefreshFailed', {
|
|
660
|
-
privateConfigOperation: true,
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
const refreshed = status.payload.catalog.sources.find((source) => source.id === sourceId);
|
|
664
|
-
const refreshedMatchesCheck = checked !== undefined && refreshed !== undefined
|
|
665
|
-
&& refreshed.type === 'git'
|
|
666
|
-
&& !refreshed.error
|
|
667
|
-
&& refreshed.identity === checked.sourceIdentity
|
|
668
|
-
&& refreshed.requestedRef === checked.requestedRef;
|
|
669
|
-
const state = checkError
|
|
670
|
-
? authoritativeReloadSucceeded
|
|
671
|
-
? !refreshed || refreshed.error
|
|
672
|
-
? 'unavailable'
|
|
673
|
-
: refreshed.type === 'path' || refreshed.freshness === 'path'
|
|
674
|
-
? 'path'
|
|
675
|
-
: refreshed.freshness === 'cache' ? 'degraded' : 'unavailable'
|
|
676
|
-
: hadCache ? 'degraded' : 'unavailable'
|
|
677
|
-
: !refreshed
|
|
678
|
-
? 'unavailable'
|
|
679
|
-
: refreshed.error
|
|
680
|
-
? 'unavailable'
|
|
681
|
-
: !refreshedMatchesCheck
|
|
682
|
-
? refreshed.type === 'path' || refreshed.freshness === 'path'
|
|
683
|
-
? 'path'
|
|
684
|
-
: refreshed.freshness === 'cache' ? 'cache' : 'unavailable'
|
|
685
|
-
: refreshed.type === 'path' || refreshed.freshness === 'path'
|
|
686
|
-
? 'path'
|
|
687
|
-
: 'ready';
|
|
688
|
-
this.#applyRefreshStatus(status, sourceId, {
|
|
689
|
-
sourceId,
|
|
690
|
-
state,
|
|
691
|
-
...(state === 'degraded' || state === 'unavailable' ? {
|
|
692
|
-
issueId: `source:${sourceId}:${state === 'degraded' ? 'cache' : 'unavailable'}`,
|
|
693
|
-
message: checkError
|
|
694
|
-
? state === 'degraded'
|
|
695
|
-
? '远端检查失败。当前仍使用已验证的缓存数据。'
|
|
696
|
-
: '远端检查失败,并且没有可用缓存数据。'
|
|
697
|
-
: state === 'degraded'
|
|
698
|
-
? '远端检查后仍只能使用缓存数据。'
|
|
699
|
-
: '来源检查后没有可用数据。',
|
|
700
|
-
} : {}),
|
|
701
|
-
}, refreshSequence);
|
|
702
|
-
if (!checkError && refreshedMatchesCheck && checked) {
|
|
703
|
-
this.#recordSuccessfulSourceRefresh(sourceId, checked, generation);
|
|
704
|
-
}
|
|
705
|
-
finishRefresh();
|
|
706
|
-
if (generation !== this.#generation)
|
|
707
|
-
return;
|
|
708
|
-
this.#project();
|
|
709
|
-
this.#publish();
|
|
710
738
|
}
|
|
711
739
|
push(route, context) {
|
|
712
740
|
this.#cancelReviewPreparation();
|
|
713
741
|
this.#cancelPortableRequest();
|
|
742
|
+
this.#cancelInstallPlacementRequest();
|
|
714
743
|
this.#discardPortableDraftForRoute(route);
|
|
715
744
|
this.#router.push(route, context);
|
|
716
745
|
this.#publish();
|
|
@@ -881,6 +910,7 @@ export class TuiController {
|
|
|
881
910
|
replace(route, context) {
|
|
882
911
|
this.#cancelReviewPreparation();
|
|
883
912
|
this.#cancelPortableRequest();
|
|
913
|
+
this.#cancelInstallPlacementRequest();
|
|
884
914
|
const current = this.#router.snapshot.current.route;
|
|
885
915
|
if (current.name === 'portableBlocker' && current.code === 'requiredConfigMissing'
|
|
886
916
|
&& route.name === 'configLayer' && route.pluginId === current.pluginId) {
|
|
@@ -907,9 +937,10 @@ export class TuiController {
|
|
|
907
937
|
this.#publish();
|
|
908
938
|
}
|
|
909
939
|
back() {
|
|
940
|
+
const route = this.#router.snapshot.current.route;
|
|
910
941
|
this.#cancelReviewPreparation();
|
|
911
942
|
this.#cancelPortableRequest();
|
|
912
|
-
|
|
943
|
+
this.#cancelInstallPlacementRequest();
|
|
913
944
|
if (route.name === 'review' && this.#activeReview?.operationId === route.operationId) {
|
|
914
945
|
this.#tickets.delete(this.#activeReview.reviewToken);
|
|
915
946
|
this.#activeReview = undefined;
|
|
@@ -1023,6 +1054,213 @@ export class TuiController {
|
|
|
1023
1054
|
throw new DOMException('Portable request was cancelled.', 'AbortError');
|
|
1024
1055
|
}
|
|
1025
1056
|
}
|
|
1057
|
+
beginInstallFlow(pluginId, rootScope, options = {}) {
|
|
1058
|
+
this.#cancelInstallPlacementRequest();
|
|
1059
|
+
this.#installPlacementDraft = {
|
|
1060
|
+
pluginId,
|
|
1061
|
+
rootScope,
|
|
1062
|
+
platforms: [],
|
|
1063
|
+
dependencyScopeOverrides: [],
|
|
1064
|
+
state: 'selecting',
|
|
1065
|
+
targets: [],
|
|
1066
|
+
canContinue: false,
|
|
1067
|
+
adjustingExisting: options.adjustingExisting === true,
|
|
1068
|
+
revalidateOverrides: false,
|
|
1069
|
+
};
|
|
1070
|
+
this.#publish();
|
|
1071
|
+
}
|
|
1072
|
+
setInstallRootScope(rootScope) {
|
|
1073
|
+
const draft = this.#installPlacementDraft;
|
|
1074
|
+
if (!draft)
|
|
1075
|
+
return;
|
|
1076
|
+
this.#cancelInstallPlacementRequest();
|
|
1077
|
+
draft.rootScope = rootScope;
|
|
1078
|
+
draft.platforms = [];
|
|
1079
|
+
draft.revalidateOverrides = true;
|
|
1080
|
+
draft.plan = undefined;
|
|
1081
|
+
draft.targets = [];
|
|
1082
|
+
draft.state = 'selecting';
|
|
1083
|
+
draft.canContinue = false;
|
|
1084
|
+
draft.error = undefined;
|
|
1085
|
+
this.#publish();
|
|
1086
|
+
}
|
|
1087
|
+
setInstallPlatforms(platforms) {
|
|
1088
|
+
const draft = this.#installPlacementDraft;
|
|
1089
|
+
if (!draft)
|
|
1090
|
+
return;
|
|
1091
|
+
this.#cancelInstallPlacementRequest();
|
|
1092
|
+
const selected = [...new Set(platforms)].sort();
|
|
1093
|
+
draft.platforms = selected;
|
|
1094
|
+
draft.dependencyScopeOverrides = draft.dependencyScopeOverrides.filter((override) => selected.includes(override.platform));
|
|
1095
|
+
draft.revalidateOverrides = true;
|
|
1096
|
+
draft.plan = undefined;
|
|
1097
|
+
draft.targets = [];
|
|
1098
|
+
draft.state = 'selecting';
|
|
1099
|
+
draft.canContinue = selected.length > 0;
|
|
1100
|
+
draft.error = undefined;
|
|
1101
|
+
this.#publish();
|
|
1102
|
+
}
|
|
1103
|
+
#cancelInstallPlacementRequest() {
|
|
1104
|
+
const cancelled = this.#installPlacementRequestAbort !== undefined;
|
|
1105
|
+
this.#installPlacementRequestToken += 1;
|
|
1106
|
+
this.#installPlacementRequestAbort?.abort();
|
|
1107
|
+
this.#installPlacementRequestAbort = undefined;
|
|
1108
|
+
if (cancelled && this.#installPlacementDraft?.state === 'loading') {
|
|
1109
|
+
this.#installPlacementDraft.plan = undefined;
|
|
1110
|
+
this.#installPlacementDraft.targets = [];
|
|
1111
|
+
this.#installPlacementDraft.state = 'selecting';
|
|
1112
|
+
this.#installPlacementDraft.canContinue = false;
|
|
1113
|
+
this.#installPlacementDraft.error = undefined;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
#installPlacementIntent() {
|
|
1117
|
+
const draft = this.#installPlacementDraft;
|
|
1118
|
+
if (!draft || draft.platforms.length === 0) {
|
|
1119
|
+
throw new TuiOperationBlockedError('installPlacementIncomplete', '请先明确选择目标平台。');
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
kind: 'pluginWrite',
|
|
1123
|
+
action: 'install',
|
|
1124
|
+
pluginId: draft.pluginId,
|
|
1125
|
+
platform: draft.platforms.length === 2 ? 'both' : draft.platforms[0],
|
|
1126
|
+
scope: draft.rootScope,
|
|
1127
|
+
...(draft.dependencyScopeOverrides.length > 0
|
|
1128
|
+
? { dependencyScopeOverrides: draft.dependencyScopeOverrides }
|
|
1129
|
+
: {}),
|
|
1130
|
+
...(draft.adjustingExisting ? { dependencyPlacementAdjustment: true } : {}),
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
async prepareInstallDependencyPlacement() {
|
|
1134
|
+
const draft = this.#installPlacementDraft;
|
|
1135
|
+
if (!draft)
|
|
1136
|
+
throw new TuiOperationBlockedError('installPlacementMissing', '安装位置草稿不存在。');
|
|
1137
|
+
this.#cancelInstallPlacementRequest();
|
|
1138
|
+
const abort = new AbortController();
|
|
1139
|
+
this.#installPlacementRequestAbort = abort;
|
|
1140
|
+
const token = this.#installPlacementRequestToken;
|
|
1141
|
+
draft.state = 'loading';
|
|
1142
|
+
draft.canContinue = false;
|
|
1143
|
+
draft.error = undefined;
|
|
1144
|
+
this.#publish();
|
|
1145
|
+
try {
|
|
1146
|
+
const signal = AbortSignal.any([this.#abort.signal, abort.signal]);
|
|
1147
|
+
const preview = async (intent) => this.dependencies.previewDependencyPlacement
|
|
1148
|
+
? this.dependencies.previewDependencyPlacement(structuredClone(intent), signal)
|
|
1149
|
+
: (await this.dependencies.captureEvidence(structuredClone(intent), signal)).dependencyPlan;
|
|
1150
|
+
const throwIfCancelled = () => {
|
|
1151
|
+
if (this.#disposed || token !== this.#installPlacementRequestToken || abort.signal.aborted) {
|
|
1152
|
+
throw new DOMException('Dependency placement preview was cancelled.', 'AbortError');
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
const filterOverrides = (plan) => {
|
|
1156
|
+
const before = draft.dependencyScopeOverrides;
|
|
1157
|
+
const filtered = before.filter((override) => {
|
|
1158
|
+
const target = plan?.targets.find((candidate) => candidate.relation === 'dependency'
|
|
1159
|
+
&& candidate.pluginId === override.pluginId
|
|
1160
|
+
&& candidate.platform === override.platform);
|
|
1161
|
+
return Boolean(target
|
|
1162
|
+
&& target.allowedScopes.includes(override.scope)
|
|
1163
|
+
&& !(target.minimumScope === 'user' && override.scope === 'project'));
|
|
1164
|
+
});
|
|
1165
|
+
if (filtered.length === before.length)
|
|
1166
|
+
return false;
|
|
1167
|
+
draft.dependencyScopeOverrides = filtered;
|
|
1168
|
+
return true;
|
|
1169
|
+
};
|
|
1170
|
+
let plan;
|
|
1171
|
+
if (draft.revalidateOverrides && draft.dependencyScopeOverrides.length > 0) {
|
|
1172
|
+
const baselineIntent = this.#installPlacementIntent();
|
|
1173
|
+
delete baselineIntent.dependencyScopeOverrides;
|
|
1174
|
+
const baselinePlan = await preview(baselineIntent);
|
|
1175
|
+
throwIfCancelled();
|
|
1176
|
+
filterOverrides(baselinePlan);
|
|
1177
|
+
plan = draft.dependencyScopeOverrides.length > 0
|
|
1178
|
+
? await preview(this.#installPlacementIntent())
|
|
1179
|
+
: baselinePlan;
|
|
1180
|
+
throwIfCancelled();
|
|
1181
|
+
if (filterOverrides(plan)) {
|
|
1182
|
+
plan = await preview(this.#installPlacementIntent());
|
|
1183
|
+
throwIfCancelled();
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
plan = await preview(this.#installPlacementIntent());
|
|
1188
|
+
}
|
|
1189
|
+
throwIfCancelled();
|
|
1190
|
+
draft.revalidateOverrides = false;
|
|
1191
|
+
if (!plan) {
|
|
1192
|
+
draft.state = 'ready';
|
|
1193
|
+
draft.plan = undefined;
|
|
1194
|
+
draft.targets = [];
|
|
1195
|
+
draft.canContinue = true;
|
|
1196
|
+
this.#publish();
|
|
1197
|
+
return false;
|
|
1198
|
+
}
|
|
1199
|
+
draft.plan = structuredClone(plan);
|
|
1200
|
+
draft.targets = dependencyPlacementTargets(plan);
|
|
1201
|
+
const blocker = plan.targets.find((target) => target.action === 'block');
|
|
1202
|
+
draft.state = blocker ? 'blocked' : 'ready';
|
|
1203
|
+
draft.canContinue = !blocker;
|
|
1204
|
+
draft.error = blocker
|
|
1205
|
+
? {
|
|
1206
|
+
code: blocker.errorCode ?? 'dependencyPlacementBlocked',
|
|
1207
|
+
message: '依赖位置不能满足所选根插件和目标平台。',
|
|
1208
|
+
}
|
|
1209
|
+
: undefined;
|
|
1210
|
+
this.#publish();
|
|
1211
|
+
return draft.targets.some((target) => !target.locked);
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
if (error instanceof Error && error.name === 'AbortError')
|
|
1215
|
+
throw error;
|
|
1216
|
+
const code = issueCode(error);
|
|
1217
|
+
draft.state = 'blocked';
|
|
1218
|
+
draft.canContinue = false;
|
|
1219
|
+
draft.error = { code, message: '无法计算可信的依赖位置。请返回检查选择。' };
|
|
1220
|
+
this.#publish();
|
|
1221
|
+
throw error;
|
|
1222
|
+
}
|
|
1223
|
+
finally {
|
|
1224
|
+
if (token === this.#installPlacementRequestToken) {
|
|
1225
|
+
this.#installPlacementRequestAbort = undefined;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
async setDependencyScope(pluginId, platform, scope) {
|
|
1230
|
+
const draft = this.#installPlacementDraft;
|
|
1231
|
+
const target = draft?.targets.find((entry) => entry.pluginId === pluginId && entry.platform === platform);
|
|
1232
|
+
if (!draft || !target || target.locked || !target.allowedScopes.includes(scope)
|
|
1233
|
+
|| target.minimumScope === 'user' && scope === 'project') {
|
|
1234
|
+
throw new TuiOperationBlockedError('dependencyScopeOverrideInvalid', '这个依赖不支持所选位置。');
|
|
1235
|
+
}
|
|
1236
|
+
draft.dependencyScopeOverrides = [
|
|
1237
|
+
...draft.dependencyScopeOverrides.filter((override) => override.pluginId !== pluginId || override.platform !== platform),
|
|
1238
|
+
{ pluginId, platform, scope },
|
|
1239
|
+
].sort((left, right) => `${left.pluginId}\0${left.platform}`
|
|
1240
|
+
.localeCompare(`${right.pluginId}\0${right.platform}`, 'en'));
|
|
1241
|
+
draft.revalidateOverrides = false;
|
|
1242
|
+
await this.prepareInstallDependencyPlacement();
|
|
1243
|
+
}
|
|
1244
|
+
async resetDependencyScopes() {
|
|
1245
|
+
const draft = this.#installPlacementDraft;
|
|
1246
|
+
if (!draft)
|
|
1247
|
+
return;
|
|
1248
|
+
draft.dependencyScopeOverrides = [];
|
|
1249
|
+
draft.revalidateOverrides = false;
|
|
1250
|
+
await this.prepareInstallDependencyPlacement();
|
|
1251
|
+
}
|
|
1252
|
+
installPlacementIntent() {
|
|
1253
|
+
const intent = this.#installPlacementIntent();
|
|
1254
|
+
const narrowsUserTarget = this.#installPlacementDraft?.plan?.targets.some((target) => {
|
|
1255
|
+
if (target.relation !== 'dependency' || target.resolvedScope !== 'project')
|
|
1256
|
+
return false;
|
|
1257
|
+
const persistedScopes = new Set(this.#status?.payload.targets.flatMap((candidate) => candidate.pluginId === target.pluginId && candidate.platform === target.platform
|
|
1258
|
+
? candidate.desired?.scopes ?? []
|
|
1259
|
+
: []) ?? []);
|
|
1260
|
+
return persistedScopes.has('user') && !persistedScopes.has('project');
|
|
1261
|
+
});
|
|
1262
|
+
return narrowsUserTarget ? { ...intent, dependencyPlacementOnly: 'desired' } : intent;
|
|
1263
|
+
}
|
|
1026
1264
|
listPlugins(options) {
|
|
1027
1265
|
const query = options.query?.trim().toLocaleLowerCase('en');
|
|
1028
1266
|
return cloneFrozen((this.#viewModel?.plugins ?? []).filter((plugin) => (!options.sourceId || plugin.sourceId === options.sourceId)
|
|
@@ -1067,47 +1305,46 @@ export class TuiController {
|
|
|
1067
1305
|
}));
|
|
1068
1306
|
}
|
|
1069
1307
|
return plugin.supportedPlatforms.map((platform) => {
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
}
|
|
1076
|
-
if (placement.state === 'installed') {
|
|
1077
|
-
return {
|
|
1078
|
-
platform,
|
|
1079
|
-
compatible: false,
|
|
1080
|
-
disabled: `已安装到${placement.scope === 'user' ? '所有项目' : '当前项目'};请先移除现有安装`,
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
if (!placement.supportedScopes.includes(scope)) {
|
|
1308
|
+
const platformTargets = this.#status?.payload.targets.filter((target) => target.pluginId === pluginId && target.platform === platform) ?? [];
|
|
1309
|
+
const rootSummary = this.#status?.pluginSummaries.find((summary) => summary.pluginId === pluginId);
|
|
1310
|
+
const rootSupportedScopes = rootSummary?.supportedTargets?.[platform]
|
|
1311
|
+
?? platformTargets.filter((target) => !target.errors.some((error) => error.code === 'unsupportedTarget')).map((target) => target.scope);
|
|
1312
|
+
if (!rootSupportedScopes.includes(scope)) {
|
|
1084
1313
|
return { platform, compatible: false, disabled: '不支持所选位置' };
|
|
1085
1314
|
}
|
|
1315
|
+
const statusTarget = platformTargets.find((target) => target.scope === scope)
|
|
1316
|
+
?? (platform === 'codex' ? platformTargets[0] : undefined);
|
|
1317
|
+
if (statusTarget && targetHasArtifactCollision(statusTarget)) {
|
|
1318
|
+
return { platform, compatible: false, disabled: '存在输出冲突;请先移除或迁移占用内容' };
|
|
1319
|
+
}
|
|
1086
1320
|
const overview = this.#viewModel?.dependencyOverviews.find((entry) => entry.pluginId === pluginId);
|
|
1087
1321
|
for (const dependency of overview?.dependencies ?? []) {
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1322
|
+
const dependencySummary = this.#status?.pluginSummaries.find((summary) => summary.pluginId === dependency.pluginId);
|
|
1323
|
+
const allowedScopes = dependencySummary?.supportedTargets?.[platform]
|
|
1324
|
+
?? (this.#status?.payload.targets.filter((target) => target.pluginId === dependency.pluginId
|
|
1325
|
+
&& target.platform === platform
|
|
1326
|
+
&& !target.errors.some((error) => error.code === 'unsupportedTarget'))
|
|
1327
|
+
.map((target) => target.scope) ?? []);
|
|
1328
|
+
const viableScopes = allowedScopes.filter((candidateScope) => scope === 'project' || candidateScope === 'user');
|
|
1329
|
+
if (viableScopes.length === 0) {
|
|
1090
1330
|
return {
|
|
1091
1331
|
platform,
|
|
1092
1332
|
compatible: false,
|
|
1093
|
-
disabled: `${dependency.displayName} 不支持${platform === 'codex' ? 'Codex' : 'Claude Code'}
|
|
1333
|
+
disabled: `${dependency.displayName} 不支持${platform === 'codex' ? 'Codex' : 'Claude Code'}或所选位置`,
|
|
1094
1334
|
};
|
|
1095
1335
|
}
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (dependencyPlacement?.state === 'notInstalled'
|
|
1109
|
-
&& !dependencyPlacement.supportedScopes.includes(scope)) {
|
|
1110
|
-
return { platform, compatible: false, disabled: `${dependency.displayName} 不支持所选位置` };
|
|
1336
|
+
const collisionAtEveryViableScope = viableScopes.every((candidateScope) => {
|
|
1337
|
+
const target = this.#status?.payload.targets.find((entry) => entry.pluginId === dependency.pluginId
|
|
1338
|
+
&& entry.platform === platform
|
|
1339
|
+
&& entry.scope === candidateScope);
|
|
1340
|
+
return Boolean(target && targetHasArtifactCollision(target));
|
|
1341
|
+
});
|
|
1342
|
+
if (collisionAtEveryViableScope) {
|
|
1343
|
+
return {
|
|
1344
|
+
platform,
|
|
1345
|
+
compatible: false,
|
|
1346
|
+
disabled: `${dependency.displayName} 存在输出冲突`,
|
|
1347
|
+
};
|
|
1111
1348
|
}
|
|
1112
1349
|
}
|
|
1113
1350
|
return { platform, compatible: true };
|
|
@@ -1128,6 +1365,40 @@ export class TuiController {
|
|
|
1128
1365
|
health: this.#health.get(sourceId),
|
|
1129
1366
|
});
|
|
1130
1367
|
}
|
|
1368
|
+
getCatalogGeneration(sourceId) {
|
|
1369
|
+
const source = this.#catalogGenerations.get(sourceId);
|
|
1370
|
+
return source ? cloneFrozen(source) : undefined;
|
|
1371
|
+
}
|
|
1372
|
+
getSessionCatalogVersion(sourceId) {
|
|
1373
|
+
return this.#status?.payload.catalog.sources.find((source) => source.id === sourceId)?.catalogVersion;
|
|
1374
|
+
}
|
|
1375
|
+
async reloadCatalog(options) {
|
|
1376
|
+
this.dependencies.beginCatalogSession?.();
|
|
1377
|
+
const generation = ++this.#generation;
|
|
1378
|
+
this.#phase = 'loading';
|
|
1379
|
+
this.#publish();
|
|
1380
|
+
try {
|
|
1381
|
+
const status = await this.dependencies.loadStatus(this.#abort.signal);
|
|
1382
|
+
if (this.#disposed || generation !== this.#generation)
|
|
1383
|
+
return false;
|
|
1384
|
+
this.#applyStatus(status);
|
|
1385
|
+
this.#phase = 'ready';
|
|
1386
|
+
this.#loadError = undefined;
|
|
1387
|
+
this.#router.replace({ name: 'sourceDetail', sourceId: options.sourceId });
|
|
1388
|
+
this.#publish();
|
|
1389
|
+
return true;
|
|
1390
|
+
}
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
if (this.#disposed || generation !== this.#generation)
|
|
1393
|
+
return false;
|
|
1394
|
+
const code = issueCode(error);
|
|
1395
|
+
await this.#logErrorSafe(error, code);
|
|
1396
|
+
this.#phase = 'error';
|
|
1397
|
+
this.#loadError = { code, message: '无法安全重新加载 Catalog。请检查诊断后重试。' };
|
|
1398
|
+
this.#publish();
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1131
1402
|
async inspectSourceRemoval(sourceId) {
|
|
1132
1403
|
if (!this.dependencies.inspectSourceRemoval) {
|
|
1133
1404
|
throw new TuiOperationBlockedError('stateInventoryFailed', '来源引用清单不可用。');
|
|
@@ -1167,7 +1438,9 @@ export class TuiController {
|
|
|
1167
1438
|
catch (error) {
|
|
1168
1439
|
const code = issueCode(error);
|
|
1169
1440
|
await this.#logOperationError(error, code, intent);
|
|
1170
|
-
throw new TuiOperationBlockedError(code, '
|
|
1441
|
+
throw new TuiOperationBlockedError(code, code === 'catalogGenerationUnavailable'
|
|
1442
|
+
? '本次加载时 Catalog 来源不可用。请刷新来源并重新加载后重试。'
|
|
1443
|
+
: '操作未建立。请检查当前设置后重试。');
|
|
1171
1444
|
}
|
|
1172
1445
|
}
|
|
1173
1446
|
startReviewPreparation(intent, returnRoute) {
|
|
@@ -1239,11 +1512,11 @@ export class TuiController {
|
|
|
1239
1512
|
}
|
|
1240
1513
|
#reviewPreparationIsCurrent(token) {
|
|
1241
1514
|
const preparation = this.#reviewPreparation;
|
|
1515
|
+
const route = this.#router.snapshot.current.route;
|
|
1242
1516
|
return Boolean(preparation && preparation.token === token
|
|
1243
1517
|
&& !preparation.abort.signal.aborted
|
|
1244
1518
|
&& !this.#disposed
|
|
1245
|
-
&&
|
|
1246
|
-
&& this.#router.snapshot.current.route.preparationId === preparation.snapshot.id);
|
|
1519
|
+
&& route.name === 'reviewPreparing' && route.preparationId === preparation.snapshot.id);
|
|
1247
1520
|
}
|
|
1248
1521
|
async #runReviewPreparation(token) {
|
|
1249
1522
|
const preparation = this.#reviewPreparation;
|
|
@@ -1281,13 +1554,16 @@ export class TuiController {
|
|
|
1281
1554
|
catch (error) {
|
|
1282
1555
|
if (!this.#reviewPreparationIsCurrent(token))
|
|
1283
1556
|
return;
|
|
1557
|
+
const code = issueCode(error);
|
|
1284
1558
|
const active = this.#reviewPreparation;
|
|
1285
1559
|
active.snapshot.state = 'failed';
|
|
1286
1560
|
active.snapshot.stages = active.snapshot.stages.map((stage) => stage.state === 'running' ? { ...stage, state: 'failed' } : stage);
|
|
1287
1561
|
active.snapshot.code = 'reviewPreparationFailed';
|
|
1288
|
-
active.snapshot.message = '
|
|
1562
|
+
active.snapshot.message = code === 'catalogGenerationUnavailable'
|
|
1563
|
+
? '本次加载时 Catalog 来源不可用。请刷新来源并重新加载后重试。'
|
|
1564
|
+
: '无法准备确认信息。请返回检查选择,或重试。';
|
|
1289
1565
|
this.#publish();
|
|
1290
|
-
await this.#logOperationError(error,
|
|
1566
|
+
await this.#logOperationError(error, code, intent);
|
|
1291
1567
|
}
|
|
1292
1568
|
}
|
|
1293
1569
|
async #prepareReviewEvidence(intent, signal, onStage = () => undefined) {
|
|
@@ -1335,6 +1611,7 @@ export class TuiController {
|
|
|
1335
1611
|
this.#pruneReviewTickets(now);
|
|
1336
1612
|
const expiresAt = now
|
|
1337
1613
|
+ (this.dependencies.reviewTtlMs ?? 2 * 60_000);
|
|
1614
|
+
const requiresConfirmation = (evidence.codexReconcilePlans ?? []).some((plan) => plan.review.requiresConfirmation);
|
|
1338
1615
|
const pluginId = 'pluginId' in intent ? intent.pluginId : intent.sourceId;
|
|
1339
1616
|
if ('pluginId' in intent)
|
|
1340
1617
|
sourceIdFromPluginId(intent.pluginId);
|
|
@@ -1361,15 +1638,31 @@ export class TuiController {
|
|
|
1361
1638
|
? { version: this.#viewModel.plugins.find((plugin) => plugin.pluginId === intent.pluginId).version }
|
|
1362
1639
|
: {}),
|
|
1363
1640
|
},
|
|
1641
|
+
...(evidence.source && 'catalogVersion' in evidence.source
|
|
1642
|
+
&& typeof evidence.source.catalogVersion === 'string'
|
|
1643
|
+
&& evidence.source.snapshotDigest
|
|
1644
|
+
? { catalogBinding: {
|
|
1645
|
+
sourceId: evidence.source.sourceId,
|
|
1646
|
+
catalogVersion: evidence.source.catalogVersion,
|
|
1647
|
+
generationDigestShort: evidence.source.snapshotDigest.replace(/^sha256:/u, '').slice(0, 8),
|
|
1648
|
+
} }
|
|
1649
|
+
: {}),
|
|
1364
1650
|
changes: reviewChanges(intent, evidence, 'pluginId' in intent ? this.getPluginDependencyOverview(intent.pluginId) : undefined),
|
|
1365
1651
|
technicalDetails: technicalDetails(evidence),
|
|
1366
|
-
...(intent.kind === 'pluginWrite' && intent.
|
|
1367
|
-
? { equivalentCommand: `aw
|
|
1652
|
+
...(intent.kind === 'pluginWrite' && intent.dependencyPlacementOnly !== 'desired'
|
|
1653
|
+
? { equivalentCommand: `aw ${intent.action} ${intent.pluginId} --platform ${intent.platform} --scope ${intent.scope}${(intent.dependencyScopeOverrides ?? []).map((override) => ` --dependency-scope ${override.pluginId}#${override.platform}=${override.scope}`).join('')}${intent.action === 'cleanup' && intent.force === true ? ' --force' : ''}${evidence.codexManager?.state === 'requires-install' ? ' --allow-managed-codex-install' : ''}${requiresConfirmation ? ' --yes' : ''}` }
|
|
1368
1654
|
: {}),
|
|
1369
1655
|
primaryLabel: intent.kind === 'pluginWrite' && evidence.dependencyPlan
|
|
1370
1656
|
? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
|
|
1371
1657
|
: labels.primaryLabel,
|
|
1372
1658
|
...(labels.destructive ? { destructive: true } : {}),
|
|
1659
|
+
requiresConfirmation,
|
|
1660
|
+
sharedImpact: {
|
|
1661
|
+
pluginIds: [...new Set((evidence.codexReconcilePlans ?? []).flatMap((plan) => plan.review.sharedImpact.pluginIds))],
|
|
1662
|
+
...((evidence.codexReconcilePlans ?? []).find((plan) => plan.review.sharedImpact.warning)?.review.sharedImpact.warning
|
|
1663
|
+
? { warning: (evidence.codexReconcilePlans ?? []).find((plan) => plan.review.sharedImpact.warning).review.sharedImpact.warning }
|
|
1664
|
+
: {}),
|
|
1665
|
+
},
|
|
1373
1666
|
};
|
|
1374
1667
|
this.#tickets.set(reviewToken, {
|
|
1375
1668
|
intent: structuredClone(intent),
|
|
@@ -1435,7 +1728,9 @@ export class TuiController {
|
|
|
1435
1728
|
this.#publish();
|
|
1436
1729
|
try {
|
|
1437
1730
|
this.#assertIssueOrigin(ticket.intent);
|
|
1438
|
-
|
|
1731
|
+
if (ticket.intent.kind !== 'pluginWrite') {
|
|
1732
|
+
await this.dependencies.validateIntent?.(structuredClone(ticket.intent), this.#abort.signal);
|
|
1733
|
+
}
|
|
1439
1734
|
}
|
|
1440
1735
|
catch (error) {
|
|
1441
1736
|
if (!this.#operationIsCurrent(generation))
|
|
@@ -1444,7 +1739,6 @@ export class TuiController {
|
|
|
1444
1739
|
await this.#logOperationError(error, code, ticket.intent);
|
|
1445
1740
|
if (!this.#operationIsCurrent(generation))
|
|
1446
1741
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1447
|
-
this.#cancelSourceRefreshes();
|
|
1448
1742
|
generation = ++this.#generation;
|
|
1449
1743
|
try {
|
|
1450
1744
|
const refreshed = await this.dependencies.loadStatus(this.#abort.signal);
|
|
@@ -1465,26 +1759,27 @@ export class TuiController {
|
|
|
1465
1759
|
}
|
|
1466
1760
|
if (!this.#operationIsCurrent(generation))
|
|
1467
1761
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1762
|
+
if (ticket.intent.kind !== 'pluginWrite') {
|
|
1763
|
+
let current;
|
|
1764
|
+
try {
|
|
1765
|
+
current = await this.dependencies.captureEvidence(structuredClone(ticket.intent), this.#abort.signal);
|
|
1766
|
+
}
|
|
1767
|
+
catch (error) {
|
|
1768
|
+
if (!this.#operationIsCurrent(generation))
|
|
1769
|
+
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1770
|
+
const code = issueCode(error);
|
|
1771
|
+
await this.#logOperationError(error, code, ticket.intent);
|
|
1772
|
+
if (!this.#operationIsCurrent(generation))
|
|
1773
|
+
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1774
|
+
return this.#setBlocked(operationId, code, { intent: ticket.intent, returnRoute: ticket.returnRoute });
|
|
1775
|
+
}
|
|
1477
1776
|
if (!this.#operationIsCurrent(generation))
|
|
1478
1777
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1483
|
-
if (operationEvidenceFingerprint(current) !== operationEvidenceFingerprint(ticket.evidence)) {
|
|
1484
|
-
return this.#rebuildStaleReview(ticket, operationId, current, generation);
|
|
1778
|
+
if (operationEvidenceFingerprint(current) !== operationEvidenceFingerprint(ticket.evidence)) {
|
|
1779
|
+
return this.#rebuildStaleReview(ticket, operationId, current, generation);
|
|
1780
|
+
}
|
|
1485
1781
|
}
|
|
1486
1782
|
if (ticket.intent.kind === 'sourceMutation') {
|
|
1487
|
-
this.#cancelSourceRefreshes();
|
|
1488
1783
|
generation = ++this.#generation;
|
|
1489
1784
|
}
|
|
1490
1785
|
try {
|
|
@@ -1497,9 +1792,11 @@ export class TuiController {
|
|
|
1497
1792
|
});
|
|
1498
1793
|
if (!this.#operationIsCurrent(generation))
|
|
1499
1794
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1500
|
-
this.#cancelSourceRefreshes();
|
|
1501
1795
|
generation = ++this.#generation;
|
|
1502
1796
|
if (ticket.intent.kind === 'sourceMutation') {
|
|
1797
|
+
this.dependencies.beginCatalogSession?.({
|
|
1798
|
+
reuseValidatedGenerations: true,
|
|
1799
|
+
});
|
|
1503
1800
|
try {
|
|
1504
1801
|
this.#progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase: '读取最终状态' }, '来源操作:读取最终状态');
|
|
1505
1802
|
}
|
|
@@ -1545,6 +1842,17 @@ export class TuiController {
|
|
|
1545
1842
|
&& Boolean(hasFailedDependencyTarget);
|
|
1546
1843
|
if (pluginSummary?.code === 'operationFailed') {
|
|
1547
1844
|
const unsuccessfulTargets = pluginSummary.targets?.filter((target) => target.state === 'failed' || target.state === 'skipped') ?? [];
|
|
1845
|
+
const managerFailureCode = unsuccessfulTargets.map((target) => target.errorCode).find((code) => code === 'codexSystemCliUnsupported'
|
|
1846
|
+
|| code === 'managedCodexAuthorizationRequired'
|
|
1847
|
+
|| code === 'managedCodexUnavailable');
|
|
1848
|
+
if (managerFailureCode) {
|
|
1849
|
+
return this.#setBlocked(operationId, managerFailureCode, {
|
|
1850
|
+
intent: ticket.intent,
|
|
1851
|
+
navigation: 'result',
|
|
1852
|
+
returnRoute: ticket.returnRoute,
|
|
1853
|
+
summary: pluginSummary,
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1548
1856
|
const dependencyStillRequired = unsuccessfulTargets.length > 0
|
|
1549
1857
|
&& unsuccessfulTargets.every((target) => target.errorCode === 'dependencyStillRequired');
|
|
1550
1858
|
return this.#setBlocked(operationId, dependencyStillRequired ? 'dependencyStillRequired' : 'operationFailed', {
|
|
@@ -1637,7 +1945,6 @@ export class TuiController {
|
|
|
1637
1945
|
this.#activeReview = undefined;
|
|
1638
1946
|
this.#router.replace({ name: 'result', operationId });
|
|
1639
1947
|
this.#publish();
|
|
1640
|
-
this.#startGitSourceRefreshes();
|
|
1641
1948
|
return cloneFrozen(result);
|
|
1642
1949
|
}
|
|
1643
1950
|
catch (error) {
|
|
@@ -1650,7 +1957,6 @@ export class TuiController {
|
|
|
1650
1957
|
if (code === 'operationReviewStale') {
|
|
1651
1958
|
return this.#rebuildStaleReview(ticket, operationId, undefined, generation);
|
|
1652
1959
|
}
|
|
1653
|
-
this.#cancelSourceRefreshes();
|
|
1654
1960
|
generation = ++this.#generation;
|
|
1655
1961
|
try {
|
|
1656
1962
|
const refreshed = await this.dependencies.loadStatus(this.#abort.signal);
|
|
@@ -1688,7 +1994,7 @@ export class TuiController {
|
|
|
1688
1994
|
});
|
|
1689
1995
|
}
|
|
1690
1996
|
async #rebuildStaleReview(ticket, operationId, currentEvidence, generation = this.#generation) {
|
|
1691
|
-
this
|
|
1997
|
+
this.dependencies.beginCatalogSession?.();
|
|
1692
1998
|
generation = ++this.#generation;
|
|
1693
1999
|
try {
|
|
1694
2000
|
const refreshed = await this.dependencies.loadStatus(this.#abort.signal);
|
|
@@ -1706,15 +2012,16 @@ export class TuiController {
|
|
|
1706
2012
|
returnRoute: ticket.returnRoute,
|
|
1707
2013
|
});
|
|
1708
2014
|
}
|
|
2015
|
+
const intent = ticket.intent;
|
|
1709
2016
|
try {
|
|
1710
|
-
this.#assertIssueOrigin(
|
|
1711
|
-
await this.dependencies.validateIntent?.(structuredClone(
|
|
2017
|
+
this.#assertIssueOrigin(intent);
|
|
2018
|
+
await this.dependencies.validateIntent?.(structuredClone(intent), this.#abort.signal);
|
|
1712
2019
|
}
|
|
1713
2020
|
catch (error) {
|
|
1714
2021
|
if (!this.#operationIsCurrent(generation))
|
|
1715
2022
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1716
2023
|
const code = issueCode(error);
|
|
1717
|
-
await this.#logOperationError(error, code,
|
|
2024
|
+
await this.#logOperationError(error, code, intent);
|
|
1718
2025
|
if (!this.#operationIsCurrent(generation))
|
|
1719
2026
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1720
2027
|
const result = {
|
|
@@ -1722,7 +2029,7 @@ export class TuiController {
|
|
|
1722
2029
|
operationId,
|
|
1723
2030
|
code,
|
|
1724
2031
|
message: '状态已变化,原操作不再安全。请修改选择后重试。',
|
|
1725
|
-
...this.#operationIdentity(
|
|
2032
|
+
...this.#operationIdentity(intent),
|
|
1726
2033
|
returnRoute: structuredClone(this.#normalizeRoute(ticket.returnRoute)),
|
|
1727
2034
|
};
|
|
1728
2035
|
this.#result = structuredClone(result);
|
|
@@ -1736,50 +2043,56 @@ export class TuiController {
|
|
|
1736
2043
|
let evidence = currentEvidence;
|
|
1737
2044
|
if (!evidence) {
|
|
1738
2045
|
try {
|
|
1739
|
-
evidence = await this.dependencies.captureEvidence(structuredClone(
|
|
2046
|
+
evidence = await this.dependencies.captureEvidence(structuredClone(intent), this.#abort.signal);
|
|
1740
2047
|
}
|
|
1741
2048
|
catch (error) {
|
|
1742
2049
|
if (!this.#operationIsCurrent(generation))
|
|
1743
2050
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1744
2051
|
const code = issueCode(error);
|
|
1745
|
-
await this.#logOperationError(error, code,
|
|
2052
|
+
await this.#logOperationError(error, code, intent);
|
|
1746
2053
|
if (!this.#operationIsCurrent(generation))
|
|
1747
2054
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1748
|
-
return this.#setBlocked(operationId, code, { intent
|
|
2055
|
+
return this.#setBlocked(operationId, code, { intent, returnRoute: ticket.returnRoute });
|
|
1749
2056
|
}
|
|
1750
2057
|
}
|
|
1751
2058
|
if (!this.#operationIsCurrent(generation))
|
|
1752
2059
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1753
|
-
if (
|
|
2060
|
+
if (intent.kind === 'portableInstall') {
|
|
1754
2061
|
try {
|
|
1755
|
-
await this.refreshPortableInstallDraft(
|
|
2062
|
+
await this.refreshPortableInstallDraft(intent.pluginId);
|
|
1756
2063
|
}
|
|
1757
2064
|
catch (error) {
|
|
1758
2065
|
if (!this.#operationIsCurrent(generation))
|
|
1759
2066
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1760
2067
|
const code = issueCode(error);
|
|
1761
|
-
await this.#logOperationError(error, code,
|
|
1762
|
-
return this.#setBlocked(operationId, code, { intent
|
|
2068
|
+
await this.#logOperationError(error, code, intent);
|
|
2069
|
+
return this.#setBlocked(operationId, code, { intent, returnRoute: ticket.returnRoute });
|
|
1763
2070
|
}
|
|
1764
2071
|
}
|
|
1765
2072
|
if (!this.#operationIsCurrent(generation))
|
|
1766
2073
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1767
|
-
const review = this.#storeOperationReview(
|
|
1768
|
-
const result = { state: 'stale', operationId, review, ...this.#operationIdentity(
|
|
2074
|
+
const review = this.#storeOperationReview(intent, evidence, 'replace', ticket.returnRoute);
|
|
2075
|
+
const result = { state: 'stale', operationId, review, ...this.#operationIdentity(intent) };
|
|
1769
2076
|
this.#result = result;
|
|
1770
2077
|
this.#publish();
|
|
1771
2078
|
return cloneFrozen(result);
|
|
1772
2079
|
}
|
|
1773
2080
|
#setBlocked(operationId, code, options) {
|
|
1774
|
-
const message = code === '
|
|
1775
|
-
? '
|
|
1776
|
-
:
|
|
1777
|
-
? '
|
|
1778
|
-
: code === '
|
|
1779
|
-
? '
|
|
1780
|
-
: code === '
|
|
1781
|
-
? '
|
|
1782
|
-
:
|
|
2081
|
+
const message = code === 'managedCodexAuthorizationRequired'
|
|
2082
|
+
? '操作在插件写入前停止。请在 Review 中确认获取 Codex runtime,或在命令行传入 --allow-managed-codex-install。'
|
|
2083
|
+
: code === 'codexSystemCliUnsupported'
|
|
2084
|
+
? '操作在插件写入前停止。系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
2085
|
+
: code === 'managedCodexUnavailable'
|
|
2086
|
+
? '操作在插件写入前停止。Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。'
|
|
2087
|
+
: code === 'portableSkillsCliInstallFailed'
|
|
2088
|
+
? 'Skills CLI 安装失败。请修改 Agent 或重试。'
|
|
2089
|
+
: options?.writeMayHaveCompleted
|
|
2090
|
+
? '写入可能已完成,当前无法确认。请查看刷新后的平台或问题状态。'
|
|
2091
|
+
: code === 'configResetStillPresent'
|
|
2092
|
+
? '重置执行后设置层仍然存在。请重新检查设置问题。'
|
|
2093
|
+
: code === 'issueStillPresent'
|
|
2094
|
+
? '写入已完成,但重新检查后原问题仍然存在。'
|
|
2095
|
+
: '操作未完成。请重新检查后重试。';
|
|
1783
2096
|
const result = {
|
|
1784
2097
|
state: 'blocked',
|
|
1785
2098
|
operationId,
|
|
@@ -1843,13 +2156,17 @@ export class TuiController {
|
|
|
1843
2156
|
? origin.actionId === 'cleanup' && action.writeAction === 'cleanup'
|
|
1844
2157
|
? { action: 'cleanup', scope: issue.scope }
|
|
1845
2158
|
: undefined
|
|
1846
|
-
: issue.category === '
|
|
1847
|
-
? origin.actionId === '
|
|
1848
|
-
? { action: '
|
|
1849
|
-
:
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2159
|
+
: issue.category === 'sourceMissing'
|
|
2160
|
+
? origin.actionId === 'cleanup' && action.writeAction === 'cleanup'
|
|
2161
|
+
? { action: 'cleanup', scope: issue.scope }
|
|
2162
|
+
: undefined
|
|
2163
|
+
: issue.category === 'scopeConflict'
|
|
2164
|
+
? origin.actionId === 'keepUserScope'
|
|
2165
|
+
? { action: 'uninstall', scope: 'project' }
|
|
2166
|
+
: origin.actionId === 'keepProjectScope'
|
|
2167
|
+
? { action: 'uninstall', scope: 'user' }
|
|
2168
|
+
: undefined
|
|
2169
|
+
: undefined;
|
|
1853
2170
|
const dependencyOriginValid = issue.category !== 'dependencyMissing' && issue.category !== 'dependencyNotReady'
|
|
1854
2171
|
|| (origin.consumerPluginId === issue.pluginId
|
|
1855
2172
|
&& origin.consumerSourceIdentity === issue.sourceIdentity
|
|
@@ -1870,8 +2187,8 @@ export class TuiController {
|
|
|
1870
2187
|
this.#disposed = true;
|
|
1871
2188
|
this.#cancelReviewPreparation();
|
|
1872
2189
|
this.#cancelPortableRequest();
|
|
2190
|
+
this.#cancelInstallPlacementRequest();
|
|
1873
2191
|
this.#abort.abort();
|
|
1874
|
-
this.#cancelSourceRefreshes();
|
|
1875
2192
|
this.#progressUnsubscribe?.();
|
|
1876
2193
|
this.#tickets.clear();
|
|
1877
2194
|
this.#phase = 'disposed';
|
|
@@ -1879,6 +2196,18 @@ export class TuiController {
|
|
|
1879
2196
|
}
|
|
1880
2197
|
}
|
|
1881
2198
|
function operationResultSummary(intent, execution) {
|
|
2199
|
+
if (intent.kind === 'pluginWrite' && intent.dependencyPlacementOnly === 'desired') {
|
|
2200
|
+
return {
|
|
2201
|
+
code: 'operationSucceeded',
|
|
2202
|
+
message: '依赖 desired placement 已更新。',
|
|
2203
|
+
pluginId: intent.pluginId,
|
|
2204
|
+
action: intent.action,
|
|
2205
|
+
messages: [
|
|
2206
|
+
'第 1 步:AW 不会扫描未知项目或自动 cleanup;请在已知的所有项目位置显式确认 cleanup。',
|
|
2207
|
+
'第 2 步:cleanup 完成后同步当前项目。',
|
|
2208
|
+
],
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
1882
2211
|
if (intent.kind === 'portableInstall') {
|
|
1883
2212
|
const receipt = execution;
|
|
1884
2213
|
const verificationUnknown = receipt.failures.some((failure) => failure.code === 'portableSkillsCliVerificationFailed'
|
|
@@ -1950,7 +2279,10 @@ function operationResultSummary(intent, execution) {
|
|
|
1950
2279
|
requiredBy: planned.requiredBy,
|
|
1951
2280
|
} : {}),
|
|
1952
2281
|
...(entry.errorCode === 'dependencyStillRequired'
|
|
1953
|
-
|
|
2282
|
+
|| entry.errorCode === 'codexSystemCliUnsupported'
|
|
2283
|
+
|| entry.errorCode === 'managedCodexAuthorizationRequired'
|
|
2284
|
+
|| entry.errorCode === 'managedCodexUnavailable'
|
|
2285
|
+
? { errorCode: entry.errorCode }
|
|
1954
2286
|
: {}),
|
|
1955
2287
|
}];
|
|
1956
2288
|
});
|
|
@@ -1979,7 +2311,16 @@ function operationResultSummary(intent, execution) {
|
|
|
1979
2311
|
action: intent.action,
|
|
1980
2312
|
} : {}),
|
|
1981
2313
|
...(inferredTargets.length > 0 ? { targets: inferredTargets } : {}),
|
|
1982
|
-
...(sourceWarnings.length > 0
|
|
2314
|
+
...(sourceWarnings.length > 0 || intent.kind === 'pluginWrite' && intent.recoveryDescription
|
|
2315
|
+
? { messages: [
|
|
2316
|
+
...sourceWarnings,
|
|
2317
|
+
...(intent.kind === 'pluginWrite' && intent.recoveryDescription
|
|
2318
|
+
? [
|
|
2319
|
+
intent.recoveryDescription,
|
|
2320
|
+
`后续:${intent.scope === 'user' ? '当前项目' : '所有项目'} sync`,
|
|
2321
|
+
] : []),
|
|
2322
|
+
] }
|
|
2323
|
+
: {}),
|
|
1983
2324
|
};
|
|
1984
2325
|
}
|
|
1985
2326
|
//# sourceMappingURL=controller.js.map
|