@lesliechan721/aw 0.1.0-beta.64 → 0.1.0-beta.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/assets/aw-runtime-config.mjs +3 -3
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
- package/dist/authoring/scripts/add-skill.mjs +3 -17
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +1 -0
- 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 +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +95 -698
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
- package/dist/scripts/aw-skill-eval-materializer.js +74 -44
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
- package/dist/scripts/bundle-plugins.js +10 -2
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +1 -1
- package/dist/scripts/plugin-staging.js +1 -1
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +31 -31
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +2 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/console.js +4 -3
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/intent-command.d.ts +30 -0
- package/dist/src/commands/intent-command.js +164 -0
- package/dist/src/commands/intent-command.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +2 -0
- package/dist/src/commands/operation-plan.js +2 -1
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/options.d.ts +3 -11
- package/dist/src/commands/options.js +11 -17
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +27 -33
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +108 -6
- package/dist/src/commands/rebuild-command.js +617 -1549
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +19 -3
- package/dist/src/commands/rebuild-integration.js +500 -459
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.js +1 -2
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/review-inspection.d.ts +2 -2
- package/dist/src/commands/review-inspection.js +30 -86
- package/dist/src/commands/review-inspection.js.map +1 -1
- package/dist/src/commands/review-preparation.js +16 -7
- package/dist/src/commands/review-preparation.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +2 -0
- package/dist/src/commands/skill-eval.js +15 -9
- package/dist/src/commands/skill-eval.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +38 -5
- package/dist/src/commands/status-v6.js +269 -102
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/targets-command.d.ts +27 -0
- package/dist/src/commands/targets-command.js +77 -0
- package/dist/src/commands/targets-command.js.map +1 -0
- package/dist/src/commands/write-commands.js +5 -5
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +2 -0
- package/dist/src/eval/manifest-v1.js +7 -2
- package/dist/src/eval/manifest-v1.js.map +1 -1
- package/dist/src/generators/local-plugin.d.ts +2 -1
- package/dist/src/generators/local-plugin.js +111 -108
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +10 -17
- package/dist/src/index.js +585 -1349
- package/dist/src/index.js.map +1 -1
- package/dist/src/intent/deployment-preferences.d.ts +63 -0
- package/dist/src/intent/deployment-preferences.js +170 -0
- package/dist/src/intent/deployment-preferences.js.map +1 -0
- package/dist/src/intent/diagnostics.d.ts +11 -0
- package/dist/src/intent/diagnostics.js +14 -0
- package/dist/src/intent/diagnostics.js.map +1 -0
- package/dist/src/intent/intent-codec.d.ts +19 -0
- package/dist/src/intent/intent-codec.js +117 -0
- package/dist/src/intent/intent-codec.js.map +1 -0
- package/dist/src/intent/intent-store.d.ts +43 -0
- package/dist/src/intent/intent-store.js +118 -0
- package/dist/src/intent/intent-store.js.map +1 -0
- package/dist/src/intent/scope-context.d.ts +33 -0
- package/dist/src/intent/scope-context.js +145 -0
- package/dist/src/intent/scope-context.js.map +1 -0
- package/dist/src/intent/work-context.d.ts +13 -0
- package/dist/src/intent/work-context.js +22 -0
- package/dist/src/intent/work-context.js.map +1 -0
- package/dist/src/output/command-receipt.js +16 -9
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/portable/commands.d.ts +18 -8
- package/dist/src/portable/commands.js +46 -562
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/historical-materialization.d.ts +22 -0
- package/dist/src/portable/historical-materialization.js +124 -0
- package/dist/src/portable/historical-materialization.js.map +1 -0
- package/dist/src/portable/materializer.d.ts +8 -0
- package/dist/src/portable/materializer.js +75 -44
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/recovery-layout.d.ts +17 -0
- package/dist/src/portable/recovery-layout.js +55 -0
- package/dist/src/portable/recovery-layout.js.map +1 -0
- package/dist/src/portable/recovery.d.ts +143 -0
- package/dist/src/portable/recovery.js +339 -0
- package/dist/src/portable/recovery.js.map +1 -0
- package/dist/src/portable/writer.d.ts +64 -0
- package/dist/src/portable/writer.js +316 -0
- package/dist/src/portable/writer.js.map +1 -0
- package/dist/src/recovery/coordinator.d.ts +137 -0
- package/dist/src/recovery/coordinator.js +1198 -0
- package/dist/src/recovery/coordinator.js.map +1 -0
- package/dist/src/recovery/demand.d.ts +44 -0
- package/dist/src/recovery/demand.js +178 -0
- package/dist/src/recovery/demand.js.map +1 -0
- package/dist/src/recovery/joint-coordinator.d.ts +24 -0
- package/dist/src/recovery/joint-coordinator.js +770 -0
- package/dist/src/recovery/joint-coordinator.js.map +1 -0
- package/dist/src/recovery/joint-demand.d.ts +68 -0
- package/dist/src/recovery/joint-demand.js +166 -0
- package/dist/src/recovery/joint-demand.js.map +1 -0
- package/dist/src/recovery/receipt.d.ts +81 -0
- package/dist/src/recovery/receipt.js +2 -0
- package/dist/src/recovery/receipt.js.map +1 -0
- package/dist/src/recovery/retirement.d.ts +45 -0
- package/dist/src/recovery/retirement.js +366 -0
- package/dist/src/recovery/retirement.js.map +1 -0
- package/dist/src/recovery/status.d.ts +108 -0
- package/dist/src/recovery/status.js +417 -0
- package/dist/src/recovery/status.js.map +1 -0
- package/dist/src/recovery/types.d.ts +86 -0
- package/dist/src/recovery/types.js +2 -0
- package/dist/src/recovery/types.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +2 -1
- package/dist/src/review/review-profile.js +15 -15
- package/dist/src/review/review-profile.js.map +1 -1
- package/dist/src/review/review-selection.d.ts +1 -2
- package/dist/src/review/review-selection.js +40 -114
- package/dist/src/review/review-selection.js.map +1 -1
- package/dist/src/review/review-types.d.ts +4 -4
- package/dist/src/review/review-types.js.map +1 -1
- package/dist/src/review/skill-materialization.js +15 -6
- package/dist/src/review/skill-materialization.js.map +1 -1
- package/dist/src/review/source-binding.d.ts +14 -4
- package/dist/src/review/source-binding.js +62 -117
- package/dist/src/review/source-binding.js.map +1 -1
- package/dist/src/tui/agent-management.d.ts +23 -0
- package/dist/src/tui/agent-management.js +217 -0
- package/dist/src/tui/agent-management.js.map +1 -0
- package/dist/src/tui/agent-settings.d.ts +44 -0
- package/dist/src/tui/agent-settings.js +98 -0
- package/dist/src/tui/agent-settings.js.map +1 -0
- package/dist/src/tui/components.d.ts +3 -0
- package/dist/src/tui/components.js +10 -1
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-references.d.ts +6 -3
- package/dist/src/tui/config-references.js +17 -12
- package/dist/src/tui/config-references.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -2
- package/dist/src/tui/console-app-v4.js +1533 -1047
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +2 -1
- package/dist/src/tui/controller-runtime.js +405 -1626
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +131 -81
- package/dist/src/tui/controller.js +307 -227
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +2 -2
- package/dist/src/tui/entity-cards.js +98 -20
- package/dist/src/tui/entity-cards.js.map +1 -1
- package/dist/src/tui/glossary.d.ts +2 -0
- package/dist/src/tui/glossary.js +20 -0
- package/dist/src/tui/glossary.js.map +1 -1
- package/dist/src/tui/model.d.ts +28 -10
- package/dist/src/tui/model.js +41 -41
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-review-planner.d.ts +2 -0
- package/dist/src/tui/operation-review-planner.js +58 -122
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-content.js +7 -4
- package/dist/src/tui/plugin-content.js.map +1 -1
- package/dist/src/tui/plugin-detail.d.ts +22 -3
- package/dist/src/tui/plugin-detail.js +113 -26
- package/dist/src/tui/plugin-detail.js.map +1 -1
- package/dist/src/tui/reader.d.ts +1 -0
- package/dist/src/tui/reader.js +3 -1
- package/dist/src/tui/reader.js.map +1 -1
- package/dist/src/tui/router.d.ts +23 -33
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/scoped-flow.d.ts +45 -0
- package/dist/src/tui/scoped-flow.js +96 -0
- package/dist/src/tui/scoped-flow.js.map +1 -0
- package/dist/src/tui/scoped-inventory.d.ts +122 -0
- package/dist/src/tui/scoped-inventory.js +225 -0
- package/dist/src/tui/scoped-inventory.js.map +1 -0
- package/dist/src/tui/screen-registry.d.ts +25 -25
- package/dist/src/tui/screen-registry.js +3 -7
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/session-console-app.js +1 -1
- package/dist/src/tui/session-console-app.js.map +1 -1
- package/dist/src/tui/session-host.js +12 -2
- package/dist/src/tui/session-host.js.map +1 -1
- package/dist/src/types.d.ts +15 -12
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.js +2 -0
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.js +13 -6
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +6 -3
- package/dist/src/utils/artifact-renderer.js +27 -32
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +1 -0
- package/dist/src/utils/artifact-request.js +22 -12
- package/dist/src/utils/artifact-request.js.map +1 -1
- package/dist/src/utils/artifact-sources.d.ts +2 -2
- package/dist/src/utils/bundle-layout.js +18 -16
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.js +2 -0
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-source-references.d.ts +5 -0
- package/dist/src/utils/catalog-source-references.js +42 -57
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
- package/dist/src/utils/claude-native-render-receipt.js +26 -0
- package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
- package/dist/src/utils/claude-skills-root.js +14 -3
- package/dist/src/utils/claude-skills-root.js.map +1 -1
- package/dist/src/utils/codex-manager.js +6 -12
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/codex-native-observation.js +29 -13
- package/dist/src/utils/codex-native-observation.js.map +1 -1
- package/dist/src/utils/codex-reconciler.d.ts +2 -5
- package/dist/src/utils/codex-reconciler.js +25 -1
- package/dist/src/utils/codex-reconciler.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.js +5 -3
- package/dist/src/utils/codex-surface-analysis.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +14 -2
- package/dist/src/utils/local-marketplace.js +209 -42
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/managed-command-evidence.js +1 -1
- package/dist/src/utils/managed-command-evidence.js.map +1 -1
- package/dist/src/utils/managed-command-materialization.d.ts +4 -0
- package/dist/src/utils/managed-command-materialization.js +15 -6
- package/dist/src/utils/managed-command-materialization.js.map +1 -1
- package/dist/src/utils/managed-command-store.d.ts +15 -0
- package/dist/src/utils/managed-command-store.js +73 -8
- package/dist/src/utils/managed-command-store.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +2 -0
- package/dist/src/utils/native-cli.js +22 -0
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/native-demand.d.ts +10 -0
- package/dist/src/utils/native-demand.js +4 -0
- package/dist/src/utils/native-demand.js.map +1 -0
- package/dist/src/utils/ownership.js +2 -0
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/paths.js +1 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/platform-support.d.ts +1 -1
- package/dist/src/utils/platform-support.js +1 -1
- package/dist/src/utils/platform-support.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +3 -0
- package/dist/src/utils/plugin-config-impact.js +115 -273
- package/dist/src/utils/plugin-config-impact.js.map +1 -1
- package/dist/src/utils/plugin-config-references.d.ts +9 -2
- package/dist/src/utils/plugin-config-references.js +75 -85
- package/dist/src/utils/plugin-config-references.js.map +1 -1
- package/dist/src/utils/plugin-config-write.d.ts +58 -1
- package/dist/src/utils/plugin-config-write.js +756 -809
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +47 -46
- package/dist/src/utils/plugin-config.js +292 -833
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +11 -6
- package/dist/src/utils/plugin-scripts.js +52 -35
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/runtime-config.js +5 -4
- package/dist/src/utils/runtime-config.js.map +1 -1
- package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
- package/dist/src/utils/runtime-skill-closure.js.map +1 -1
- package/dist/src/utils/state-paths.d.ts +8 -3
- package/dist/src/utils/state-paths.js +53 -16
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/windows-npm-shim.d.ts +2 -0
- package/dist/src/utils/windows-npm-shim.js +5 -7
- package/dist/src/utils/windows-npm-shim.js.map +1 -1
- package/dist/vitest.config.js +1 -0
- package/dist/vitest.config.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/commands/sync-command.d.ts +0 -40
- package/dist/src/commands/sync-command.js +0 -254
- package/dist/src/commands/sync-command.js.map +0 -1
- package/dist/src/portable/skills-cli.d.ts +0 -51
- package/dist/src/portable/skills-cli.js +0 -202
- package/dist/src/portable/skills-cli.js.map +0 -1
- package/dist/src/tui/project-inventory.d.ts +0 -68
- package/dist/src/tui/project-inventory.js +0 -203
- package/dist/src/tui/project-inventory.js.map +0 -1
- package/dist/src/utils/plugin-intent-state.d.ts +0 -49
- package/dist/src/utils/plugin-intent-state.js +0 -320
- package/dist/src/utils/plugin-intent-state.js.map +0 -1
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readAgentSettings, previewAgentSettings, saveAgentSettings } from './agent-settings.js';
|
|
2
|
+
import { compilePluginConfigDefinition, getPluginConfigReferenceChoices, PLATFORM_TARGETS, resolveSupportedPlatforms } from '@lesliechan721/aw-plugin-core';
|
|
2
3
|
import { configReferenceKey, configReferenceScalars } from './config-references.js';
|
|
3
4
|
import crypto from 'node:crypto';
|
|
4
5
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
6
|
+
import { resolveWorkContext } from '../intent/work-context.js';
|
|
7
|
+
import { flattenStatusTargets, statusCommandV8, statusTargetHasLiveConfigConsumer, } from '../commands/status-v6.js';
|
|
6
8
|
import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
|
|
7
|
-
import { cleanupWriteCommand, failedWriteCommandResult, installWriteCommand, mergeWriteCommandResults, reinstallWriteCommand, syncWriteCommand, taskWriteCommand, uninstallWriteCommand, } from '../commands/write-commands.js';
|
|
8
|
-
import { resolveSyncIntentSelection } from '../commands/sync-command.js';
|
|
9
9
|
import { parseScriptTimeoutMs, SCRIPT_TIMEOUT_DEFAULT_MS, } from '../commands/options.js';
|
|
10
10
|
import { readCliPackageInfo } from '../utils/local-marketplace.js';
|
|
11
|
-
import { createCodexNativeObservationSession, } from '../utils/codex-native-observation.js';
|
|
12
|
-
import { prepareNativeCliExecution } from '../utils/native-cli.js';
|
|
13
11
|
import { readCatalogSourcesConfigSnapshot, recoverInvalidCatalogSourcesConfig, } from '../utils/catalog-sources.js';
|
|
14
12
|
import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
|
|
15
|
-
import { coercePluginConfigUpdates, inspectPluginConfigResetTarget, listNamespacedPluginConfigStoreIdentities, readPluginConfigStore, resolveConfigRead, resolveEffectivePluginConfig, resolveEffectivePluginConfigFromStores, resolvePluginConfigReferenceContext,
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
13
|
+
import { coercePluginConfigUpdates, inspectPluginConfigResetTarget, listNamespacedPluginConfigStoreIdentities, readPluginConfigStore, replacePluginConfigStoreLayer, resolveConfigRead, resolveEffectivePluginConfig, resolveEffectivePluginConfigFromStores, resolvePluginConfigReferenceContext, } from '../utils/plugin-config.js';
|
|
14
|
+
import { readRecoveryStatus } from '../recovery/status.js';
|
|
15
|
+
import { activateRecoverySourcesAndReprepare, prepareRecovery, executeRecovery, } from '../recovery/coordinator.js';
|
|
16
|
+
import { prepareRetire, executeRetire } from '../recovery/retirement.js';
|
|
17
|
+
import { prepareTargetsPlan, executeTargetsPlan, prepareIntentPlan, executeIntentPlan } from './scoped-flow.js';
|
|
18
|
+
import { applyPluginConfigWrite, applyPluginConfig, preparePluginConfigApply, disposePluginConfigApply, resolveConfigPreApplyAuthorization, resetPluginConfigWithHelper, } from '../utils/plugin-config-write.js';
|
|
19
19
|
import { analyzePluginConfigMutationImpact, discoverPluginConfigManagedCommandPackages, pluginConfigTargetRenderScopes, pluginSharedNativeConfigTargets } from '../utils/plugin-config-impact.js';
|
|
20
20
|
import { collectPluginConfigReferenceEvidence, pluginConfigHasExternalReferences, resolvePluginConfigReferenceConsumers, resolvePluginConfigReferencePlugins } from '../utils/plugin-config-references.js';
|
|
21
|
-
import { createBatchIntentCompatiblePlanFingerprint } from '../commands/operation-plan.js';
|
|
22
21
|
import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
|
|
23
22
|
import { operationEvidenceMatches, operationEvidenceFingerprint, TuiConfigInvalidStoreError, TuiController, TuiOperationBlockedError, } from './controller.js';
|
|
24
|
-
import {
|
|
25
|
-
import { buildPluginWriteTaskCandidates, buildPluginWriteTaskDraft, evaluatePluginWriteTaskRestoreVersion, pluginWriteTaskTargetKey, } from './plugin-write-task.js';
|
|
26
|
-
import { projectExecutionDiagnostics, projectThrownOperationDiagnostic } from './diagnostic-projection.js';
|
|
23
|
+
import { buildConfigPage, hasActualScopeEvidence, projectPlatformPlacement, } from './model.js';
|
|
27
24
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
28
25
|
import { calculateCatalogSourceConfigFingerprint, calculateCatalogSourceIdentity, } from '../utils/catalog-source-identity.js';
|
|
29
26
|
import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.js';
|
|
@@ -32,14 +29,13 @@ import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
|
|
|
32
29
|
import { calculateCatalogGenerationSetDigest, resolveActiveCatalogGeneration, } from '../utils/catalog-generation-resolver.js';
|
|
33
30
|
import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
|
|
34
31
|
import { createCatalogStoreKey, readCatalogStoreEntry, } from '../utils/catalog-generation-store.js';
|
|
35
|
-
import { activateCatalogSourceGeneration, compareCatalogGenerationFreshness, probeCatalogSourceFreshness, probeCatalogSourceGeneration, probeCatalogSourceRepairGeneration, recoverCatalogGenerationStateAndActivate, repairCatalogSourceGeneration, replaceDamagedCatalogSourceGeneration,
|
|
32
|
+
import { activateCatalogSourceGeneration, compareCatalogGenerationFreshness, probeCatalogSourceFreshness, probeCatalogSourceGeneration, probeCatalogSourceRepairGeneration, recoverCatalogGenerationStateAndActivate, repairCatalogSourceGeneration, replaceDamagedCatalogSourceGeneration, } from '../utils/catalog-generation-service.js';
|
|
36
33
|
import { calculateCatalogSourceImpactDigest, inspectCatalogSourceImpact, } from '../utils/catalog-source-impact.js';
|
|
37
34
|
import { readCatalogGenerationState } from '../utils/catalog-generation-state.js';
|
|
38
35
|
import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
|
|
39
36
|
import { CatalogProviderCompatibilityError } from '../utils/catalog-public-error.js';
|
|
40
37
|
import { loadCatalogGenerationViewModels } from './catalog-generation.js';
|
|
41
38
|
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
39
|
import { projectPluginContentOverview, } from './plugin-content.js';
|
|
44
40
|
import { executeCatalogStoreTask, inspectCatalogStoreTask, prepareCatalogStoreTask, } from './catalog-store-task.js';
|
|
45
41
|
function reusableCandidateEvidenceIsComplete(candidate) {
|
|
@@ -70,7 +66,7 @@ async function loadPortablePluginIds(options) {
|
|
|
70
66
|
? item.resolution.manifest.entries.flatMap((entry) => {
|
|
71
67
|
const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
|
|
72
68
|
return support.supported
|
|
73
|
-
? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => plugin.manifest.
|
|
69
|
+
? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => [...resolveSupportedPlatforms(plugin.manifest)].some((target) => target !== 'codex' && target !== 'claude')
|
|
74
70
|
? formatPluginId({
|
|
75
71
|
sourceId: item.resolution.sourceId,
|
|
76
72
|
pluginName: entry.name,
|
|
@@ -88,6 +84,10 @@ export function setTuiControllerRuntimeTestHooksForTests(hooks) {
|
|
|
88
84
|
function digest(value) {
|
|
89
85
|
return `sha256:${crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
|
|
90
86
|
}
|
|
87
|
+
function configSchemaIdentity(plugin, manifestDigest, platform) {
|
|
88
|
+
return digest({ manifestDigest, profile: platform ?? 'common',
|
|
89
|
+
definition: compilePluginConfigDefinition(plugin.manifest.config, platform) });
|
|
90
|
+
}
|
|
91
91
|
export function statusTargetFingerprint(targets, revisions = []) {
|
|
92
92
|
return digest({
|
|
93
93
|
targets: targets.map((target) => ({
|
|
@@ -115,18 +115,17 @@ export function statusTargetFingerprint(targets, revisions = []) {
|
|
|
115
115
|
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
|
-
export function configAffectedStatusTargets(targets, pluginId,
|
|
118
|
+
export function configAffectedStatusTargets(targets, pluginId, configContext, consumerPluginIds = [], platform) {
|
|
119
119
|
const affected = new Map();
|
|
120
120
|
const selected = new Set([pluginId, ...consumerPluginIds]);
|
|
121
121
|
for (const target of flattenStatusTargets(targets)) {
|
|
122
|
-
|
|
123
|
-
if (!selected.has(target.pluginId) || !intent.enabled)
|
|
122
|
+
if (!selected.has(target.pluginId) || !statusTargetHasLiveConfigConsumer(target))
|
|
124
123
|
continue;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
124
|
+
if (configContext === 'user' && target.scope !== 'user')
|
|
125
|
+
continue;
|
|
126
|
+
if (platform && target.platform !== platform)
|
|
127
|
+
continue;
|
|
128
|
+
affected.set(`${target.pluginId}\0${target.platform}\0${target.scope}`, target);
|
|
130
129
|
}
|
|
131
130
|
return [...affected.values()]
|
|
132
131
|
.sort((left, right) => `${left.pluginId}\0${left.platform}\0${left.scope}`
|
|
@@ -134,8 +133,8 @@ export function configAffectedStatusTargets(targets, pluginId, configScope, cons
|
|
|
134
133
|
}
|
|
135
134
|
function configNeedsReferencePlugins(plugin) {
|
|
136
135
|
return pluginConfigHasExternalReferences(plugin.manifest)
|
|
137
|
-
|| Object.values(plugin.manifest.config
|
|
138
|
-
.some((field) => field.type === 'array' && field.items.type === 'object');
|
|
136
|
+
|| (plugin.manifest.config !== undefined && [undefined, ...PLATFORM_TARGETS].some((platform) => Object.values(compilePluginConfigDefinition(plugin.manifest.config, platform).fields)
|
|
137
|
+
.some((field) => field.type === 'array' && field.items.type === 'object')));
|
|
139
138
|
}
|
|
140
139
|
async function configRetrySharedNativeRenderTargets(plugin, targets, env, referencePlugins) {
|
|
141
140
|
const plugins = await resolvePluginConfigReferenceConsumers({ plugin, env, plugins: referencePlugins });
|
|
@@ -147,15 +146,16 @@ async function configRetryStoreDigest(options) {
|
|
|
147
146
|
sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
148
147
|
env: options.env, plugins: options.plugins });
|
|
149
148
|
const byName = new Map([...plugins, options.plugin].map((plugin) => [plugin.manifest.name, plugin]));
|
|
150
|
-
const profiles = [{ pluginName: options.plugin.manifest.name, scope: options.scope },
|
|
149
|
+
const profiles = [{ pluginName: options.plugin.manifest.name, scope: options.scope, platform: options.platform },
|
|
151
150
|
...options.targets.flatMap((target) => {
|
|
152
151
|
const plugin = byName.get(target.pluginName);
|
|
153
152
|
if (!plugin)
|
|
154
153
|
throw operationReviewStale('重试目标的插件定义已变化。');
|
|
155
|
-
return pluginConfigTargetRenderScopes(plugin, target).map((scope) => ({ pluginName: target.pluginName, scope }));
|
|
154
|
+
return pluginConfigTargetRenderScopes(plugin, target).map((scope) => ({ pluginName: target.pluginName, scope, platform: target.platform }));
|
|
156
155
|
}),
|
|
157
|
-
...options.commandPackages.map(({ producer }) => ({ pluginName: producer.pluginName, scope: producer.scope
|
|
158
|
-
|
|
156
|
+
...options.commandPackages.map(({ producer }) => ({ pluginName: producer.pluginName, scope: producer.scope,
|
|
157
|
+
platform: producer.platform ?? undefined })),
|
|
158
|
+
...options.preApplyAuthorization.map(({ pluginName, scope }) => ({ pluginName, scope, platform: options.platform }))];
|
|
159
159
|
return digest(await collectPluginConfigReferenceEvidence({ ...options, profiles, plugins }));
|
|
160
160
|
}
|
|
161
161
|
function configImpactVisibleTargets(pluginName, targets) {
|
|
@@ -179,7 +179,9 @@ function configSaveBatchGroupIntent(intent, group) {
|
|
|
179
179
|
return {
|
|
180
180
|
kind: 'configSave',
|
|
181
181
|
pluginId: intent.pluginId,
|
|
182
|
-
scope:
|
|
182
|
+
scope: 'user',
|
|
183
|
+
platform: group.platform,
|
|
184
|
+
context: intent.context ?? 'user',
|
|
183
185
|
apply: intent.apply,
|
|
184
186
|
...(intent.scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs: intent.scriptTimeoutMs }),
|
|
185
187
|
...(group.updates ? { updates: group.updates } : {}),
|
|
@@ -190,14 +192,11 @@ function configSaveBatchGroupIntent(intent, group) {
|
|
|
190
192
|
};
|
|
191
193
|
}
|
|
192
194
|
function assertConfigSaveBatchIntent(intent) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|| new Set(intent.groups.map((group) => group.scope)).size !== intent.groups.length
|
|
196
|
-
|| intent.groups.some((group, index) => index > 0
|
|
197
|
-
&& order[intent.groups[index - 1].scope] >= order[group.scope])
|
|
195
|
+
if (intent.groups.length === 0 || intent.groups.length > PLATFORM_TARGETS.length + 1
|
|
196
|
+
|| new Set(intent.groups.map((group) => group.platform ?? 'common')).size !== intent.groups.length
|
|
198
197
|
|| intent.groups.some((group) => Object.keys(group.updates ?? {}).length === 0
|
|
199
198
|
&& (group.removeKeys?.length ?? 0) === 0)) {
|
|
200
|
-
throw new TuiOperationBlockedError('configBatchInvalid', '
|
|
199
|
+
throw new TuiOperationBlockedError('configBatchInvalid', '设置批次必须包含唯一且非空的公共或平台配置。');
|
|
201
200
|
}
|
|
202
201
|
}
|
|
203
202
|
function operationReviewStale(message, diagnostics) {
|
|
@@ -227,28 +226,15 @@ async function configLayerStoreDigest(options) {
|
|
|
227
226
|
? { logicalIdentity: options.source.logicalIdentity }
|
|
228
227
|
: {}),
|
|
229
228
|
pluginName: options.pluginName,
|
|
230
|
-
scope:
|
|
231
|
-
|
|
232
|
-
env: options.env,
|
|
233
|
-
});
|
|
234
|
-
return digest({ exists: store.exists, rawDigest: store.rawDigest });
|
|
235
|
-
}
|
|
236
|
-
async function inspectConfigShareability(options) {
|
|
237
|
-
if (options.scope !== 'project')
|
|
238
|
-
return undefined;
|
|
239
|
-
const targetPath = resolvePluginConfigStorePath({
|
|
240
|
-
sourceId: options.sourceId,
|
|
241
|
-
pluginName: options.pluginName,
|
|
242
|
-
scope: 'project',
|
|
229
|
+
scope: 'user',
|
|
230
|
+
platform: options.platform,
|
|
243
231
|
cwd: options.cwd,
|
|
244
232
|
env: options.env,
|
|
245
233
|
});
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
});
|
|
251
|
-
return visibility.state === 'unverified' ? 'unverified' : 'verified';
|
|
234
|
+
return options.profileOnly
|
|
235
|
+
? digest({ profile: options.platform ?? 'common', state: store.state === 'invalid' ? 'invalid' : 'ready',
|
|
236
|
+
values: store.values, ...(store.state === 'invalid' ? { rawDigest: store.rawDigest } : {}) })
|
|
237
|
+
: digest({ exists: store.exists, rawDigest: store.rawDigest });
|
|
252
238
|
}
|
|
253
239
|
function assertSourceEvidenceMatches(expected, actual) {
|
|
254
240
|
if (actual.sourceId !== expected.sourceId
|
|
@@ -261,21 +247,9 @@ function assertSourceEvidenceMatches(expected, actual) {
|
|
|
261
247
|
throw operationReviewStale('确认后来源内容发生变化。');
|
|
262
248
|
}
|
|
263
249
|
}
|
|
264
|
-
function writeReviewIsStale(result) {
|
|
265
|
-
return result.targetResults.some((target) => target.errorCode === 'operationReviewStale'
|
|
266
|
-
|| target.errorCode === 'operationPlanStale');
|
|
267
|
-
}
|
|
268
|
-
function throwIfWriteReviewStale(result) {
|
|
269
|
-
if (writeReviewIsStale(result)) {
|
|
270
|
-
throw operationReviewStale('确认后实际写入计划发生变化。');
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
250
|
const SOURCE_PROGRESS_PHASES = {
|
|
274
251
|
savingConfig: '保存来源配置',
|
|
275
252
|
};
|
|
276
|
-
function pluginIntent(intent) {
|
|
277
|
-
return 'pluginId' in intent;
|
|
278
|
-
}
|
|
279
253
|
function reviewedScriptTimeoutMs(intent) {
|
|
280
254
|
if (intent.kind === 'pluginWriteTask') {
|
|
281
255
|
return parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
|
|
@@ -290,138 +264,6 @@ function reviewedScriptTimeoutMs(intent) {
|
|
|
290
264
|
}
|
|
291
265
|
return undefined;
|
|
292
266
|
}
|
|
293
|
-
function selectedBatchGroups(intent) {
|
|
294
|
-
if (intent.selection.kind !== 'selected' && intent.selection.kind !== 'targets') {
|
|
295
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部同步必须从项目 intent roots 解析。');
|
|
296
|
-
}
|
|
297
|
-
const rawTargets = intent.selection.kind === 'targets'
|
|
298
|
-
? intent.selection.targets
|
|
299
|
-
: intent.selection.pluginIds.map((pluginId) => ({
|
|
300
|
-
pluginId, platform: intent.platform, scope: intent.scope,
|
|
301
|
-
}));
|
|
302
|
-
const normalizedTargets = rawTargets.map((target) => {
|
|
303
|
-
if ((target.platform !== 'codex' && target.platform !== 'claude' && target.platform !== 'both')
|
|
304
|
-
|| (target.scope !== 'project' && target.scope !== 'user')
|
|
305
|
-
|| (intent.selection.kind === 'targets' && target.platform === 'both')) {
|
|
306
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '批量精确目标必须使用有效 scope 和单个平台。');
|
|
307
|
-
}
|
|
308
|
-
const pluginId = target.pluginId;
|
|
309
|
-
const parsed = parsePluginId(pluginId);
|
|
310
|
-
if (!parsed.sourceId) {
|
|
311
|
-
throw new TuiOperationBlockedError('invalidPluginId', '批量操作必须使用 canonical pluginId。');
|
|
312
|
-
}
|
|
313
|
-
const canonicalPluginId = formatPluginId({ sourceId: parsed.sourceId, pluginName: parsed.pluginName });
|
|
314
|
-
if (intent.selection.kind === 'targets' && canonicalPluginId !== pluginId) {
|
|
315
|
-
throw new TuiOperationBlockedError('invalidPluginId', '批量精确目标必须使用 canonical pluginId。');
|
|
316
|
-
}
|
|
317
|
-
return {
|
|
318
|
-
pluginId: canonicalPluginId,
|
|
319
|
-
platform: target.platform,
|
|
320
|
-
scope: target.scope,
|
|
321
|
-
};
|
|
322
|
-
}).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
|
|
323
|
-
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en'));
|
|
324
|
-
const uniqueTargets = [...new Map(normalizedTargets.map((target) => [
|
|
325
|
-
[target.pluginId, target.platform, target.scope].join('\0'), target,
|
|
326
|
-
])).values()];
|
|
327
|
-
const requestedPluginIds = [...new Set(uniqueTargets.map((target) => target.pluginId))];
|
|
328
|
-
if (requestedPluginIds.length === 0) {
|
|
329
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '批量操作至少需要一个插件。');
|
|
330
|
-
}
|
|
331
|
-
const groups = new Map();
|
|
332
|
-
for (const target of uniqueTargets) {
|
|
333
|
-
const pluginId = target.pluginId;
|
|
334
|
-
const sourceId = parsePluginId(pluginId).sourceId;
|
|
335
|
-
const groupKey = intent.action === 'cleanup'
|
|
336
|
-
? ['cleanup', target.platform, target.scope].join('\0')
|
|
337
|
-
: [sourceId, target.platform, target.scope].join('\0');
|
|
338
|
-
const group = groups.get(groupKey) ?? {
|
|
339
|
-
groupId: groupKey,
|
|
340
|
-
sourceId: intent.action === 'cleanup' ? 'offline' : sourceId,
|
|
341
|
-
platform: target.platform,
|
|
342
|
-
scope: target.scope,
|
|
343
|
-
requestedPluginIds: [],
|
|
344
|
-
};
|
|
345
|
-
group.requestedPluginIds.push(pluginId);
|
|
346
|
-
groups.set(groupKey, group);
|
|
347
|
-
}
|
|
348
|
-
return { requestedPluginIds, groups: [...groups.values()] };
|
|
349
|
-
}
|
|
350
|
-
async function resolveBatchGroups(intent, env, cwd, frozenRequestedPluginIds) {
|
|
351
|
-
if (intent.selection.kind === 'selected' || intent.selection.kind === 'targets') {
|
|
352
|
-
return selectedBatchGroups(intent);
|
|
353
|
-
}
|
|
354
|
-
if (intent.action !== 'sync' || intent.scope !== 'project') {
|
|
355
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部操作只支持 project scope sync。');
|
|
356
|
-
}
|
|
357
|
-
const state = await readPluginIntentState({ scope: 'project', cwd, env });
|
|
358
|
-
const projectIntentDigest = calculatePluginIntentDigest(state);
|
|
359
|
-
if (intent.selection.kind === 'currentProjectIntentTargets') {
|
|
360
|
-
if (intent.selection.targets.length === 0
|
|
361
|
-
|| intent.selection.targets.some((target) => target.scope !== 'project')) {
|
|
362
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须是非空 project targets。');
|
|
363
|
-
}
|
|
364
|
-
const exact = selectedBatchGroups({
|
|
365
|
-
...intent,
|
|
366
|
-
selection: { kind: 'targets', targets: intent.selection.targets },
|
|
367
|
-
});
|
|
368
|
-
const declaredPlatforms = new Map(state.roots.map((root) => [
|
|
369
|
-
root.pluginId,
|
|
370
|
-
new Set(root.platforms),
|
|
371
|
-
]));
|
|
372
|
-
if (intent.selection.targets.some((target) => !declaredPlatforms.get(target.pluginId)?.has(target.platform))) {
|
|
373
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须属于完整 project intent roots 声明。');
|
|
374
|
-
}
|
|
375
|
-
return { ...exact, projectIntentDigest };
|
|
376
|
-
}
|
|
377
|
-
const selection = resolveSyncIntentSelection({
|
|
378
|
-
roots: state.roots,
|
|
379
|
-
selectors: frozenRequestedPluginIds ?? [],
|
|
380
|
-
all: frozenRequestedPluginIds === undefined,
|
|
381
|
-
platform: intent.platform,
|
|
382
|
-
});
|
|
383
|
-
if (selection.requestedPluginIds.length === 0) {
|
|
384
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目没有可同步的 intent roots。');
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
388
|
-
projectIntentDigest,
|
|
389
|
-
groups: selection.groups.map((group) => ({
|
|
390
|
-
groupId: `${group.sourceId}\0${group.platform}\0${intent.scope}`,
|
|
391
|
-
sourceId: group.sourceId,
|
|
392
|
-
platform: group.platform,
|
|
393
|
-
scope: intent.scope,
|
|
394
|
-
requestedPluginIds: group.pluginIds,
|
|
395
|
-
})),
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
async function runBatchWriteGroup(options) {
|
|
399
|
-
const dependencyScopeOverrides = options.intent.action === 'install'
|
|
400
|
-
? options.intent.dependencyScopeOverrides?.filter((override) => parsePluginId(override.pluginId).sourceId === options.group.sourceId
|
|
401
|
-
&& (options.group.platform === 'both' || override.platform === options.group.platform))
|
|
402
|
-
: undefined;
|
|
403
|
-
const commandOptions = {
|
|
404
|
-
cwd: options.cwd,
|
|
405
|
-
...(dependencyScopeOverrides?.length ? { dependencyScopeOverrides } : {}),
|
|
406
|
-
platform: options.group.platform,
|
|
407
|
-
scope: options.group.scope,
|
|
408
|
-
...(reviewedScriptTimeoutMs(options.intent) === undefined
|
|
409
|
-
? {}
|
|
410
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(options.intent) }),
|
|
411
|
-
};
|
|
412
|
-
switch (options.intent.action) {
|
|
413
|
-
case 'install':
|
|
414
|
-
return installWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
415
|
-
case 'sync':
|
|
416
|
-
return syncWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
417
|
-
case 'reinstall':
|
|
418
|
-
return reinstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
419
|
-
case 'uninstall':
|
|
420
|
-
return uninstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
421
|
-
case 'cleanup':
|
|
422
|
-
return cleanupWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
267
|
async function loadDependencyOverviews(options) {
|
|
426
268
|
const graph = await withResolvedMultiSourceCatalog({
|
|
427
269
|
cliVersion: options.cliVersion,
|
|
@@ -714,8 +556,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
714
556
|
};
|
|
715
557
|
};
|
|
716
558
|
const catalogOperationByEvidence = new WeakMap();
|
|
717
|
-
const taskCatalogOperationsByEvidence = new WeakMap();
|
|
718
|
-
const codexManagerByEvidence = new WeakMap();
|
|
719
559
|
let sessionCatalogOperationContext;
|
|
720
560
|
let sessionStatusResult;
|
|
721
561
|
const sessionContextForPlugin = (pluginId) => {
|
|
@@ -725,33 +565,42 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
725
565
|
return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
|
|
726
566
|
};
|
|
727
567
|
const readConfigStoresForTui = async (config) => {
|
|
728
|
-
const
|
|
729
|
-
const stores = await Promise.all(scopes.map((scope) => readPluginConfigStore({
|
|
568
|
+
const user = await readPluginConfigStore({
|
|
730
569
|
sourceId: config.sourceId,
|
|
731
570
|
sourceIdentity: config.sourceIdentity,
|
|
732
571
|
logicalIdentity: config.logicalIdentity,
|
|
733
572
|
pluginName: config.pluginName,
|
|
734
|
-
scope,
|
|
573
|
+
scope: 'user',
|
|
574
|
+
platform: config.platform,
|
|
735
575
|
cwd: context.cwd,
|
|
736
576
|
env: context.env,
|
|
737
577
|
schema: config.schema,
|
|
738
|
-
})
|
|
739
|
-
const user = stores[0];
|
|
740
|
-
const project = config.scope === 'user' ? undefined : stores[1];
|
|
578
|
+
});
|
|
741
579
|
config.signal.throwIfAborted();
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if (
|
|
753
|
-
throw
|
|
754
|
-
|
|
580
|
+
if (user.state === 'invalid')
|
|
581
|
+
throw new TuiConfigInvalidStoreError([{
|
|
582
|
+
scope: 'user', code: user.invalidCode,
|
|
583
|
+
pathSummary: `AW_HOME/plugin-config/${config.sourceId ?? '<source>'}/${config.pluginName}/config.json`,
|
|
584
|
+
}]);
|
|
585
|
+
return { user };
|
|
586
|
+
};
|
|
587
|
+
const assertConfigEditEvidence = async (intent, evidence) => {
|
|
588
|
+
if (!intent.editEvidence)
|
|
589
|
+
return;
|
|
590
|
+
if (!evidence.source || !evidence.config)
|
|
591
|
+
throw operationReviewStale('设置编辑缺少当前证据。');
|
|
592
|
+
const currentDigest = await configLayerStoreDigest({ source: evidence.source,
|
|
593
|
+
pluginName: parsePluginId(intent.pluginId).pluginName, scope: 'user', platform: intent.platform,
|
|
594
|
+
cwd: context.cwd, env: context.env });
|
|
595
|
+
for (const expected of Object.values(intent.editEvidence)) {
|
|
596
|
+
if (expected.sourceIdentity !== evidence.source.sourceIdentity
|
|
597
|
+
|| expected.schemaIdentity !== evidence.config.schemaIdentity
|
|
598
|
+
|| expected.platform !== intent.platform
|
|
599
|
+
|| (expected.context ?? 'user') !== (intent.context ?? 'user')
|
|
600
|
+
|| expected.storeDigests.user !== undefined && expected.storeDigests.user !== currentDigest) {
|
|
601
|
+
throw operationReviewStale('编辑期间设置、来源或定义已变化。请重新打开设置。');
|
|
602
|
+
}
|
|
603
|
+
}
|
|
755
604
|
};
|
|
756
605
|
const loadStatus = async (signal) => {
|
|
757
606
|
signal.throwIfAborted();
|
|
@@ -822,7 +671,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
822
671
|
signal.throwIfAborted();
|
|
823
672
|
const catalogGenerations = await loadCatalogGenerations();
|
|
824
673
|
signal.throwIfAborted();
|
|
825
|
-
const configStoreInventory = (await Promise.all(['user'
|
|
674
|
+
const configStoreInventory = (await Promise.all(['user'].map(async (scope) => {
|
|
826
675
|
try {
|
|
827
676
|
return (await listNamespacedPluginConfigStoreIdentities({
|
|
828
677
|
cwd: context.cwd,
|
|
@@ -845,24 +694,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
845
694
|
}))).flat();
|
|
846
695
|
signal.throwIfAborted();
|
|
847
696
|
const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
|
|
848
|
-
const projectIntentObservation = result.projectIntentObservation;
|
|
849
|
-
const projectIntent = projectIntentObservation.state === 'ready' ? {
|
|
850
|
-
state: 'ready',
|
|
851
|
-
roots: structuredClone(projectIntentObservation.value.roots),
|
|
852
|
-
} : projectIntentObservation.state === 'absent' ? {
|
|
853
|
-
state: 'absent',
|
|
854
|
-
} : {
|
|
855
|
-
state: projectIntentObservation.state,
|
|
856
|
-
code: projectIntentObservation.code,
|
|
857
|
-
message: projectIntentObservation.message,
|
|
858
|
-
};
|
|
859
697
|
return {
|
|
860
698
|
cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
|
|
861
699
|
...(result.projectRoot ? { projectRoot: result.projectRoot } : {}),
|
|
862
|
-
projectIntent,
|
|
863
|
-
...(projectIntent.state === 'ready'
|
|
864
|
-
? { projectIntentRoots: structuredClone(projectIntent.roots) }
|
|
865
|
-
: {}),
|
|
866
700
|
configuredSources: structuredClone(configured.sources),
|
|
867
701
|
payload: result.payload,
|
|
868
702
|
pluginSummaries: result.pluginSummaries,
|
|
@@ -946,8 +780,17 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
946
780
|
};
|
|
947
781
|
}
|
|
948
782
|
};
|
|
949
|
-
const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false, reusableCandidate
|
|
783
|
+
const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false, reusableCandidate) => {
|
|
784
|
+
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
785
|
+
|| intent.kind === 'pluginWriteTask') {
|
|
786
|
+
throw new TuiOperationBlockedError('useScopedRecovery', '请使用统一范围恢复任务。');
|
|
787
|
+
}
|
|
950
788
|
reviewedScriptTimeoutMs(intent);
|
|
789
|
+
if ((intent.kind === 'configSave' || intent.kind === 'configSaveBatch' || intent.kind === 'configReset')
|
|
790
|
+
&& intent.context === 'project') {
|
|
791
|
+
await resolveWorkContext({ cwd: context.cwd, context: 'project' });
|
|
792
|
+
signal?.throwIfAborted();
|
|
793
|
+
}
|
|
951
794
|
if (intent.kind === 'catalogStorePrune') {
|
|
952
795
|
signal?.throwIfAborted();
|
|
953
796
|
const config = await readCatalogSourcesConfigSnapshot(context.env);
|
|
@@ -964,7 +807,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
964
807
|
let batchBase;
|
|
965
808
|
let batchCatalogOperation;
|
|
966
809
|
for (const group of intent.groups) {
|
|
967
|
-
const current = await captureEvidence(configSaveBatchGroupIntent(intent, group), signal, preparation, operationInventory, freshCatalog, reusableCandidate
|
|
810
|
+
const current = await captureEvidence(configSaveBatchGroupIntent(intent, group), signal, preparation, operationInventory, freshCatalog, reusableCandidate);
|
|
968
811
|
if (!current.config || !current.source || !current.targetFingerprint) {
|
|
969
812
|
throw operationReviewStale('设置批次缺少单层证据。');
|
|
970
813
|
}
|
|
@@ -974,10 +817,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
974
817
|
}
|
|
975
818
|
for (const expected of Object.values(group.editEvidence ?? {})) {
|
|
976
819
|
if (expected.sourceIdentity !== current.source.sourceIdentity
|
|
977
|
-
|| expected.schemaIdentity !== current.config.schemaIdentity
|
|
820
|
+
|| expected.schemaIdentity !== current.config.schemaIdentity
|
|
821
|
+
|| expected.platform !== group.platform
|
|
822
|
+
|| (expected.context ?? 'user') !== (intent.context ?? 'user')) {
|
|
978
823
|
throw operationReviewStale('编辑期间插件来源或设置定义已变化,请重新打开列表。');
|
|
979
824
|
}
|
|
980
|
-
for (const scope of ['user'
|
|
825
|
+
for (const scope of ['user']) {
|
|
981
826
|
if (expected.storeDigests[scope] === undefined)
|
|
982
827
|
continue;
|
|
983
828
|
const currentDigest = await configLayerStoreDigest({
|
|
@@ -992,13 +837,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
992
837
|
batchBase ??= current;
|
|
993
838
|
batchCatalogOperation ??= catalogOperationByEvidence.get(current);
|
|
994
839
|
groupEvidence.push({
|
|
995
|
-
scope:
|
|
840
|
+
scope: 'user',
|
|
841
|
+
platform: group.platform,
|
|
996
842
|
config: {
|
|
997
843
|
...current.config,
|
|
998
844
|
storeDigest: await configLayerStoreDigest({
|
|
999
845
|
source: current.source,
|
|
1000
846
|
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
1001
|
-
scope:
|
|
847
|
+
scope: 'user',
|
|
848
|
+
platform: group.platform,
|
|
849
|
+
profileOnly: true,
|
|
1002
850
|
cwd: context.cwd,
|
|
1003
851
|
env: context.env,
|
|
1004
852
|
}),
|
|
@@ -1015,23 +863,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1015
863
|
catalogOperationByEvidence.set(evidence, batchCatalogOperation);
|
|
1016
864
|
return evidence;
|
|
1017
865
|
}
|
|
1018
|
-
if ((intent.kind === 'pluginWrite'
|
|
1019
|
-
&& (intent.platform === 'codex' || intent.platform === 'both')
|
|
1020
|
-
|| intent.kind === 'pluginWriteTask'
|
|
1021
|
-
&& intent.targets.some((target) => target.platform === 'codex'))
|
|
1022
|
-
&& !codexObservationSession) {
|
|
1023
|
-
const observationSession = await createCodexNativeObservationSession({
|
|
1024
|
-
exactCwd: context.cwd,
|
|
1025
|
-
env: context.env,
|
|
1026
|
-
runner: context.nativeCliRunner,
|
|
1027
|
-
});
|
|
1028
|
-
try {
|
|
1029
|
-
return await captureEvidence(intent, signal, preparation, operationInventory, freshCatalog, reusableCandidate, observationSession, frozenBatchEvidence);
|
|
1030
|
-
}
|
|
1031
|
-
finally {
|
|
1032
|
-
await observationSession.dispose();
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
866
|
if (intent.kind === 'configReset') {
|
|
1036
867
|
const parsed = parsePluginId(intent.pluginId);
|
|
1037
868
|
if (!parsed.sourceId || !parsed.pluginName) {
|
|
@@ -1041,7 +872,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1041
872
|
const target = await inspectPluginConfigResetTarget({
|
|
1042
873
|
sourceId: parsed.sourceId,
|
|
1043
874
|
pluginName: parsed.pluginName,
|
|
1044
|
-
scope:
|
|
875
|
+
scope: 'user',
|
|
1045
876
|
cwd: context.cwd,
|
|
1046
877
|
env: context.env,
|
|
1047
878
|
});
|
|
@@ -1050,7 +881,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1050
881
|
throw new TuiOperationBlockedError('configResetTargetUnsupported', '设置目标是特殊文件系统节点,不能自动重置。请手动移动后重试。');
|
|
1051
882
|
}
|
|
1052
883
|
const relevantTargets = sessionStatusResult
|
|
1053
|
-
? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.
|
|
884
|
+
? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.context ?? 'user', [], intent.platform)
|
|
1054
885
|
: [];
|
|
1055
886
|
const relevantRevisions = (sessionStatusResult?.targetRevisions ?? []).filter((revision) => relevantTargets.some((statusTarget) => statusTarget.platform === revision.platform
|
|
1056
887
|
&& statusTarget.scope === revision.scope
|
|
@@ -1070,7 +901,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1070
901
|
source: sourceEvidenceForPlugin(resolvedSource, plugin.manifest.name),
|
|
1071
902
|
impact: await analyzePluginConfigMutationImpact({
|
|
1072
903
|
plugin,
|
|
1073
|
-
scope: intent.
|
|
904
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1074
905
|
cwd: context.cwd,
|
|
1075
906
|
env: context.env,
|
|
1076
907
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
@@ -1079,7 +910,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1079
910
|
}),
|
|
1080
911
|
});
|
|
1081
912
|
}
|
|
1082
|
-
catch {
|
|
913
|
+
catch (error) {
|
|
914
|
+
if (error?.code === 'configValidationMemoryExhausted')
|
|
915
|
+
throw error;
|
|
1083
916
|
selected = undefined;
|
|
1084
917
|
}
|
|
1085
918
|
signal?.throwIfAborted();
|
|
@@ -1097,7 +930,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1097
930
|
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
1098
931
|
} : {}),
|
|
1099
932
|
config: {
|
|
1100
|
-
scope: intent.
|
|
933
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
1101
934
|
storeDigest: selected?.impact.storeDigest ?? target.fingerprint,
|
|
1102
935
|
impactState: selected?.impact.impactState ?? 'unknown',
|
|
1103
936
|
...(selected?.impact.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
@@ -1112,7 +945,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1112
945
|
resetTarget: {
|
|
1113
946
|
sourceId: parsed.sourceId,
|
|
1114
947
|
pluginName: parsed.pluginName,
|
|
1115
|
-
scope: intent.
|
|
948
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
1116
949
|
kind: target.kind,
|
|
1117
950
|
fingerprint: target.fingerprint,
|
|
1118
951
|
},
|
|
@@ -1423,671 +1256,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1423
1256
|
}
|
|
1424
1257
|
return evidence;
|
|
1425
1258
|
}
|
|
1426
|
-
if (
|
|
1427
|
-
&& intent.kind !== '
|
|
1259
|
+
if (intent.kind !== 'configSave'
|
|
1260
|
+
&& intent.kind !== 'portableInstall') {
|
|
1428
1261
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
1429
1262
|
}
|
|
1430
1263
|
signal?.throwIfAborted();
|
|
1431
|
-
if (intent.kind === 'pluginWriteTask') {
|
|
1432
|
-
await preparation?.reporter.emit('status');
|
|
1433
|
-
const packageInfo = await readCliPackageInfo();
|
|
1434
|
-
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1435
|
-
const requestedBySource = new Map();
|
|
1436
|
-
for (const target of intent.targets) {
|
|
1437
|
-
const parsed = parsePluginId(target.pluginId);
|
|
1438
|
-
if (!parsed.sourceId || formatPluginId({
|
|
1439
|
-
sourceId: parsed.sourceId,
|
|
1440
|
-
pluginName: parsed.pluginName,
|
|
1441
|
-
}) !== target.pluginId) {
|
|
1442
|
-
throw new TuiOperationBlockedError('invalidPluginId', '任务目标必须使用 canonical pluginId。');
|
|
1443
|
-
}
|
|
1444
|
-
requestedBySource.set(parsed.sourceId, [
|
|
1445
|
-
...(requestedBySource.get(parsed.sourceId) ?? []),
|
|
1446
|
-
target,
|
|
1447
|
-
]);
|
|
1448
|
-
}
|
|
1449
|
-
const sources = [];
|
|
1450
|
-
const executableTargets = [];
|
|
1451
|
-
const causes = [];
|
|
1452
|
-
const blockedTargets = [];
|
|
1453
|
-
const observedTargets = [];
|
|
1454
|
-
const observedRevisions = [];
|
|
1455
|
-
const operationContexts = new Map();
|
|
1456
|
-
await preparation?.reporter.emit('catalog');
|
|
1457
|
-
for (const [sourceId, requestedTargets] of [...requestedBySource.entries()]
|
|
1458
|
-
.sort(([left], [right]) => left.localeCompare(right, 'en'))) {
|
|
1459
|
-
signal?.throwIfAborted();
|
|
1460
|
-
let catalogOperation;
|
|
1461
|
-
try {
|
|
1462
|
-
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1463
|
-
? selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId])
|
|
1464
|
-
: await resolveCatalogOperationContext({
|
|
1465
|
-
sourceIds: [sourceId],
|
|
1466
|
-
env: context.env,
|
|
1467
|
-
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1468
|
-
...(signal ? { signal } : {}),
|
|
1469
|
-
});
|
|
1470
|
-
}
|
|
1471
|
-
catch (error) {
|
|
1472
|
-
if (error instanceof Error && error.name === 'AbortError')
|
|
1473
|
-
throw error;
|
|
1474
|
-
blockedTargets.push(...requestedTargets.map((target, index) => {
|
|
1475
|
-
const diagnostic = error instanceof CatalogGenerationError ? {
|
|
1476
|
-
id: `task-source:${sourceId}:${index}`,
|
|
1477
|
-
code: error.code,
|
|
1478
|
-
stage: 'source',
|
|
1479
|
-
sourceId,
|
|
1480
|
-
safeMessage: 'The Catalog source could not be prepared safely.',
|
|
1481
|
-
target: { pluginId: target.pluginId, platform: target.platform, scope: target.scope },
|
|
1482
|
-
causes: [],
|
|
1483
|
-
detailLines: [],
|
|
1484
|
-
} : projectThrownOperationDiagnostic({
|
|
1485
|
-
error,
|
|
1486
|
-
id: `task-source:${sourceId}:${index}`,
|
|
1487
|
-
stage: 'source',
|
|
1488
|
-
fallbackCode: 'catalogSourceResolutionFailed',
|
|
1489
|
-
sourceId,
|
|
1490
|
-
target,
|
|
1491
|
-
});
|
|
1492
|
-
return {
|
|
1493
|
-
target,
|
|
1494
|
-
reason: 'targetUnavailable',
|
|
1495
|
-
issueIds: [],
|
|
1496
|
-
errorCodes: [diagnostic.code],
|
|
1497
|
-
diagnostics: [diagnostic],
|
|
1498
|
-
};
|
|
1499
|
-
}));
|
|
1500
|
-
continue;
|
|
1501
|
-
}
|
|
1502
|
-
operationContexts.set(sourceId, catalogOperation);
|
|
1503
|
-
const status = await statusCommandV8({
|
|
1504
|
-
plugins: [...new Set(requestedTargets.map((target) => target.pluginId))],
|
|
1505
|
-
cwd: context.cwd,
|
|
1506
|
-
env: context.env,
|
|
1507
|
-
nativeCliRunner: context.nativeCliRunner,
|
|
1508
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1509
|
-
cliVersion: packageInfo.version,
|
|
1510
|
-
catalogOperationContext: catalogOperation,
|
|
1511
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1512
|
-
});
|
|
1513
|
-
observedTargets.push(...flattenStatusTargets(status.payload.targets));
|
|
1514
|
-
observedRevisions.push(...status.targetRevisions);
|
|
1515
|
-
const model = buildTuiViewModel({
|
|
1516
|
-
status: status.payload,
|
|
1517
|
-
pluginSummaries: status.pluginSummaries,
|
|
1518
|
-
targetRevisions: status.targetRevisions,
|
|
1519
|
-
});
|
|
1520
|
-
const runtimeCandidates = intent.goal === 'restore'
|
|
1521
|
-
? buildPluginWriteTaskCandidates(model, 'restore')
|
|
1522
|
-
: requestedTargets.map((target) => {
|
|
1523
|
-
const expected = target.expectedUpdate;
|
|
1524
|
-
const revision = status.targetRevisions.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
1525
|
-
&& entry.scope === target.scope);
|
|
1526
|
-
const live = flattenStatusTargets(status.payload.targets).find((entry) => {
|
|
1527
|
-
if (entry.pluginId !== target.pluginId || entry.platform !== target.platform)
|
|
1528
|
-
return false;
|
|
1529
|
-
const desired = statusTargetIntent(entry);
|
|
1530
|
-
return entry.scope === target.scope || desired.scopes.includes(target.scope);
|
|
1531
|
-
});
|
|
1532
|
-
const generation = catalogOperation.generationSet.sources.find((entry) => entry.sourceId === sourceId);
|
|
1533
|
-
const eligible = Boolean(expected && live && revision
|
|
1534
|
-
&& live.sourceIdentity === expected.sourceIdentity
|
|
1535
|
-
&& generation?.storeKey === expected.generationKey
|
|
1536
|
-
&& revision.revision.installedVersion === expected.installedVersion
|
|
1537
|
-
&& revision.revision.availableVersion === expected.activeVersion
|
|
1538
|
-
&& revision.revision.state === 'upgradeAvailable');
|
|
1539
|
-
const directVersionUpgrade = live !== undefined && revision !== undefined
|
|
1540
|
-
&& isDirectCodexVersionUpgrade(live, revision.revision);
|
|
1541
|
-
return {
|
|
1542
|
-
target,
|
|
1543
|
-
displayName: model.plugins.find((plugin) => plugin.pluginId === target.pluginId)
|
|
1544
|
-
?.displayName ?? parsePluginId(target.pluginId).pluginName,
|
|
1545
|
-
sourceId,
|
|
1546
|
-
eligible,
|
|
1547
|
-
...(!eligible ? { blockReason: 'updateEvidenceIncomplete' } : {}),
|
|
1548
|
-
recommendedMethod: live?.state === 'ready' || directVersionUpgrade
|
|
1549
|
-
? 'sync' : 'reinstall',
|
|
1550
|
-
issueIds: [],
|
|
1551
|
-
};
|
|
1552
|
-
});
|
|
1553
|
-
const draft = buildPluginWriteTaskDraft({ ...intent, targets: requestedTargets }, model, runtimeCandidates);
|
|
1554
|
-
blockedTargets.push(...draft.blockedTargets);
|
|
1555
|
-
if (draft.executableTargets.length === 0)
|
|
1556
|
-
continue;
|
|
1557
|
-
const methodsByTarget = new Map();
|
|
1558
|
-
for (const cause of draft.causes) {
|
|
1559
|
-
const key = pluginWriteTaskTargetKey(cause.repairTarget);
|
|
1560
|
-
const methods = methodsByTarget.get(key) ?? new Set();
|
|
1561
|
-
methods.add(cause.method);
|
|
1562
|
-
methodsByTarget.set(key, methods);
|
|
1563
|
-
}
|
|
1564
|
-
const conflicted = new Set([...methodsByTarget].filter(([, methods]) => methods.size > 1)
|
|
1565
|
-
.map(([key]) => key));
|
|
1566
|
-
if (conflicted.size > 0) {
|
|
1567
|
-
const blockedKeys = new Set(draft.causes.filter((cause) => conflicted.has(pluginWriteTaskTargetKey(cause.repairTarget)))
|
|
1568
|
-
.flatMap((cause) => cause.consumers.map(pluginWriteTaskTargetKey)));
|
|
1569
|
-
blockedTargets.push(...draft.executableTargets.filter((target) => blockedKeys.has(pluginWriteTaskTargetKey(target))).map((target) => ({
|
|
1570
|
-
target,
|
|
1571
|
-
reason: 'taskActionConflict',
|
|
1572
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1573
|
-
.map((cause) => cause.issueId),
|
|
1574
|
-
})));
|
|
1575
|
-
draft.executableTargets = draft.executableTargets.filter((target) => !blockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1576
|
-
draft.causes = draft.causes.filter((cause) => cause.consumers.some((consumer) => !blockedKeys.has(pluginWriteTaskTargetKey(consumer))));
|
|
1577
|
-
}
|
|
1578
|
-
if (draft.executableTargets.length === 0)
|
|
1579
|
-
continue;
|
|
1580
|
-
const taskTargets = () => draft.executableTargets.map((target) => {
|
|
1581
|
-
const direct = draft.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
1582
|
-
return {
|
|
1583
|
-
pluginId: target.pluginId,
|
|
1584
|
-
platform: target.platform,
|
|
1585
|
-
scope: target.scope,
|
|
1586
|
-
method: direct.some((cause) => cause.method === 'reinstall')
|
|
1587
|
-
? 'reinstall' : 'sync',
|
|
1588
|
-
};
|
|
1589
|
-
});
|
|
1590
|
-
const repairTargets = () => [...new Map(draft.causes.map((cause) => [
|
|
1591
|
-
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
1592
|
-
{ ...cause.repairTarget, method: cause.method },
|
|
1593
|
-
])).values()];
|
|
1594
|
-
let preview;
|
|
1595
|
-
while (draft.executableTargets.length > 0) {
|
|
1596
|
-
preview = await taskWriteCommand(taskTargets(), {
|
|
1597
|
-
cwd: context.cwd,
|
|
1598
|
-
repairTargets: repairTargets(),
|
|
1599
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
1600
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
1601
|
-
}, {
|
|
1602
|
-
...context,
|
|
1603
|
-
catalogOperationContext: catalogOperation,
|
|
1604
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1605
|
-
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1606
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1607
|
-
previewOperationPlan: true,
|
|
1608
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1609
|
-
...(preparation ? {
|
|
1610
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1611
|
-
} : {}),
|
|
1612
|
-
});
|
|
1613
|
-
if (!preview.operationPlan) {
|
|
1614
|
-
const projected = projectExecutionDiagnostics(preview, `task-preview:${sourceId}`, [], 'pluginWrite');
|
|
1615
|
-
const sourceDiagnostics = projected.diagnostics.filter((diagnostic) => !diagnostic.target)
|
|
1616
|
-
.map((diagnostic) => ({ ...diagnostic, sourceId, target: undefined }));
|
|
1617
|
-
blockedTargets.push(...draft.executableTargets.map((target, targetIndex) => {
|
|
1618
|
-
const matchingDiagnostics = projected.diagnostics.filter((diagnostic) => diagnostic.target?.pluginId === target.pluginId
|
|
1619
|
-
&& diagnostic.target.platform === target.platform
|
|
1620
|
-
&& diagnostic.target.scope === target.scope);
|
|
1621
|
-
const preparedDiagnostics = [
|
|
1622
|
-
...matchingDiagnostics,
|
|
1623
|
-
...(targetIndex === 0 ? sourceDiagnostics : []),
|
|
1624
|
-
];
|
|
1625
|
-
const targetDiagnostics = preparedDiagnostics.length > 0
|
|
1626
|
-
? preparedDiagnostics
|
|
1627
|
-
: [projectThrownOperationDiagnostic({
|
|
1628
|
-
error: undefined,
|
|
1629
|
-
id: `task-preview:${sourceId}:${targetIndex}`,
|
|
1630
|
-
stage: 'dependencyPlan',
|
|
1631
|
-
fallbackCode: 'taskOperationPlanMissing',
|
|
1632
|
-
sourceId,
|
|
1633
|
-
target,
|
|
1634
|
-
})];
|
|
1635
|
-
const diagnostics = targetDiagnostics.map((diagnostic, diagnosticIndex) => ({
|
|
1636
|
-
...structuredClone(diagnostic),
|
|
1637
|
-
id: `task-preview:${sourceId}:${targetIndex}:${diagnosticIndex}`,
|
|
1638
|
-
sourceId,
|
|
1639
|
-
}));
|
|
1640
|
-
return {
|
|
1641
|
-
target,
|
|
1642
|
-
reason: 'targetUnavailable',
|
|
1643
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1644
|
-
.map((cause) => cause.issueId),
|
|
1645
|
-
errorCodes: [...new Set(diagnostics.map((diagnostic) => diagnostic.code))],
|
|
1646
|
-
diagnostics,
|
|
1647
|
-
};
|
|
1648
|
-
}));
|
|
1649
|
-
draft.executableTargets = [];
|
|
1650
|
-
draft.causes = [];
|
|
1651
|
-
break;
|
|
1652
|
-
}
|
|
1653
|
-
if (intent.goal === 'restore') {
|
|
1654
|
-
const grouped = new Map();
|
|
1655
|
-
const blockedByVersion = new Map();
|
|
1656
|
-
for (const cause of draft.causes) {
|
|
1657
|
-
const placements = (preview.dependencyPlan?.targets ?? []).filter((target) => target.pluginId === cause.repairTarget.pluginId
|
|
1658
|
-
&& target.platform === cause.repairTarget.platform);
|
|
1659
|
-
const placement = placements.find((target) => target.requestedScope === cause.repairTarget.scope)
|
|
1660
|
-
?? (placements.length === 1 ? placements[0] : undefined);
|
|
1661
|
-
if (!placement) {
|
|
1662
|
-
throw new TuiOperationBlockedError('taskRepairPlacementMissing', '任务修复目标无法绑定到依赖位置。');
|
|
1663
|
-
}
|
|
1664
|
-
const repairTarget = {
|
|
1665
|
-
pluginId: cause.repairTarget.pluginId,
|
|
1666
|
-
platform: cause.repairTarget.platform,
|
|
1667
|
-
scope: placement.requestedScope ?? placement.resolvedScope,
|
|
1668
|
-
};
|
|
1669
|
-
const version = evaluatePluginWriteTaskRestoreVersion(repairTarget, cause.restoreVersionEvidence?.state === 'absent', model);
|
|
1670
|
-
if (!version.evidence || version.blockReason) {
|
|
1671
|
-
for (const consumer of cause.consumers) {
|
|
1672
|
-
const key = pluginWriteTaskTargetKey(consumer);
|
|
1673
|
-
const current = blockedByVersion.get(key) ?? {
|
|
1674
|
-
reason: version.blockReason ?? 'restoreVersionUnverified',
|
|
1675
|
-
issueIds: new Set(),
|
|
1676
|
-
};
|
|
1677
|
-
if (version.blockReason === 'restoreVersionChanged') {
|
|
1678
|
-
current.reason = version.blockReason;
|
|
1679
|
-
}
|
|
1680
|
-
current.issueIds.add(cause.issueId);
|
|
1681
|
-
blockedByVersion.set(key, current);
|
|
1682
|
-
}
|
|
1683
|
-
continue;
|
|
1684
|
-
}
|
|
1685
|
-
const causeKey = ['restore', cause.sourceIdentity,
|
|
1686
|
-
pluginWriteTaskTargetKey(repairTarget)].join('\0');
|
|
1687
|
-
const current = grouped.get(causeKey);
|
|
1688
|
-
grouped.set(causeKey, {
|
|
1689
|
-
cause: {
|
|
1690
|
-
...cause,
|
|
1691
|
-
causeKey,
|
|
1692
|
-
issueId: [cause.issueId, ...(current?.issueIds ?? [])]
|
|
1693
|
-
.sort((left, right) => left.localeCompare(right, 'en'))[0],
|
|
1694
|
-
repairTarget,
|
|
1695
|
-
consumers: [...new Map([
|
|
1696
|
-
...(current?.cause.consumers ?? []),
|
|
1697
|
-
...cause.consumers,
|
|
1698
|
-
].map((consumer) => [pluginWriteTaskTargetKey(consumer), consumer])).values()]
|
|
1699
|
-
.sort((left, right) => pluginWriteTaskTargetKey(left)
|
|
1700
|
-
.localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1701
|
-
restoreVersionEvidence: version.evidence,
|
|
1702
|
-
},
|
|
1703
|
-
issueIds: new Set([...(current?.issueIds ?? []), cause.issueId]),
|
|
1704
|
-
methods: new Set([...(current?.methods ?? []), cause.method]),
|
|
1705
|
-
});
|
|
1706
|
-
}
|
|
1707
|
-
for (const group of grouped.values()) {
|
|
1708
|
-
if (group.methods.size < 2)
|
|
1709
|
-
continue;
|
|
1710
|
-
for (const consumer of group.cause.consumers) {
|
|
1711
|
-
const key = pluginWriteTaskTargetKey(consumer);
|
|
1712
|
-
const current = blockedByVersion.get(key) ?? {
|
|
1713
|
-
reason: 'taskActionConflict',
|
|
1714
|
-
issueIds: new Set(),
|
|
1715
|
-
};
|
|
1716
|
-
group.issueIds.forEach((issueId) => current.issueIds.add(issueId));
|
|
1717
|
-
blockedByVersion.set(key, current);
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
const newlyBlockedKeys = new Set(blockedByVersion.keys());
|
|
1721
|
-
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1722
|
-
const blocked = blockedByVersion.get(pluginWriteTaskTargetKey(target));
|
|
1723
|
-
return blocked ? [{
|
|
1724
|
-
target,
|
|
1725
|
-
reason: blocked.reason,
|
|
1726
|
-
issueIds: [...blocked.issueIds].sort((left, right) => left.localeCompare(right, 'en')),
|
|
1727
|
-
}] : [];
|
|
1728
|
-
}));
|
|
1729
|
-
const normalizedCauses = [...grouped.values()]
|
|
1730
|
-
.filter((group) => group.methods.size === 1)
|
|
1731
|
-
.map((group) => ({
|
|
1732
|
-
...group.cause,
|
|
1733
|
-
consumers: group.cause.consumers.filter((consumer) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(consumer))),
|
|
1734
|
-
}))
|
|
1735
|
-
.filter((cause) => cause.consumers.length > 0)
|
|
1736
|
-
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en'));
|
|
1737
|
-
const normalizedTargets = draft.executableTargets.filter((target) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1738
|
-
const changed = JSON.stringify(normalizedCauses) !== JSON.stringify(draft.causes)
|
|
1739
|
-
|| JSON.stringify(normalizedTargets) !== JSON.stringify(draft.executableTargets);
|
|
1740
|
-
draft.causes = normalizedCauses;
|
|
1741
|
-
draft.executableTargets = normalizedTargets;
|
|
1742
|
-
if (changed)
|
|
1743
|
-
continue;
|
|
1744
|
-
}
|
|
1745
|
-
const blockedPlanTargets = (preview.dependencyPlan?.targets ?? []).filter((target) => target.action === 'block' || target.errorCode !== undefined);
|
|
1746
|
-
if (blockedPlanTargets.length === 0)
|
|
1747
|
-
break;
|
|
1748
|
-
const requestedKeys = new Set(draft.executableTargets.map(pluginWriteTaskTargetKey));
|
|
1749
|
-
const blockedPlansByRoot = new Map();
|
|
1750
|
-
for (const blockedPlanTarget of blockedPlanTargets) {
|
|
1751
|
-
const pending = [blockedPlanTarget];
|
|
1752
|
-
const visited = new Set();
|
|
1753
|
-
const mappedRoots = new Set();
|
|
1754
|
-
const repairKey = pluginWriteTaskTargetKey({
|
|
1755
|
-
pluginId: blockedPlanTarget.pluginId,
|
|
1756
|
-
platform: blockedPlanTarget.platform,
|
|
1757
|
-
scope: blockedPlanTarget.requestedScope ?? blockedPlanTarget.resolvedScope,
|
|
1758
|
-
});
|
|
1759
|
-
for (const cause of draft.causes) {
|
|
1760
|
-
if (pluginWriteTaskTargetKey(cause.repairTarget) !== repairKey)
|
|
1761
|
-
continue;
|
|
1762
|
-
cause.consumers.forEach((consumer) => mappedRoots.add(pluginWriteTaskTargetKey(consumer)));
|
|
1763
|
-
}
|
|
1764
|
-
while (pending.length > 0) {
|
|
1765
|
-
const current = pending.shift();
|
|
1766
|
-
const currentKey = pluginWriteTaskTargetKey({
|
|
1767
|
-
pluginId: current.pluginId,
|
|
1768
|
-
platform: current.platform,
|
|
1769
|
-
scope: current.requestedScope ?? current.resolvedScope,
|
|
1770
|
-
});
|
|
1771
|
-
if (visited.has(currentKey))
|
|
1772
|
-
continue;
|
|
1773
|
-
visited.add(currentKey);
|
|
1774
|
-
if (current.relation === 'requested' && requestedKeys.has(currentKey)) {
|
|
1775
|
-
mappedRoots.add(currentKey);
|
|
1776
|
-
}
|
|
1777
|
-
for (const consumer of current.requiredByTargets ?? []) {
|
|
1778
|
-
const consumerKey = pluginWriteTaskTargetKey(consumer);
|
|
1779
|
-
if (requestedKeys.has(consumerKey)) {
|
|
1780
|
-
mappedRoots.add(consumerKey);
|
|
1781
|
-
continue;
|
|
1782
|
-
}
|
|
1783
|
-
const placement = preview.dependencyPlan?.targets.find((target) => target.pluginId === consumer.pluginId && target.platform === consumer.platform
|
|
1784
|
-
&& (target.requestedScope ?? target.resolvedScope) === consumer.scope);
|
|
1785
|
-
if (placement)
|
|
1786
|
-
pending.push(placement);
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
if (mappedRoots.size === 0) {
|
|
1790
|
-
throw new TuiOperationBlockedError('taskPlanConsumerMissing', '任务计划中的阻止项无法映射到请求目标。');
|
|
1791
|
-
}
|
|
1792
|
-
for (const key of mappedRoots) {
|
|
1793
|
-
const plans = blockedPlansByRoot.get(key) ?? [];
|
|
1794
|
-
plans.push(blockedPlanTarget);
|
|
1795
|
-
blockedPlansByRoot.set(key, plans);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1799
|
-
const plans = blockedPlansByRoot.get(pluginWriteTaskTargetKey(target));
|
|
1800
|
-
return plans ? [{
|
|
1801
|
-
target,
|
|
1802
|
-
reason: 'targetUnavailable',
|
|
1803
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1804
|
-
.map((cause) => cause.issueId),
|
|
1805
|
-
errorCodes: [...new Set(plans.map((plan) => plan.errorCode ?? 'writePlanBlocked'))]
|
|
1806
|
-
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
1807
|
-
diagnostics: plans.map((plan, index) => ({
|
|
1808
|
-
id: `task-plan:${sourceId}:${target.pluginId}:${target.platform}:${target.scope}:${index}`,
|
|
1809
|
-
code: plan.errorCode ?? 'writePlanBlocked',
|
|
1810
|
-
stage: 'dependencyPlan',
|
|
1811
|
-
sourceId,
|
|
1812
|
-
target: {
|
|
1813
|
-
pluginId: plan.pluginId, platform: plan.platform,
|
|
1814
|
-
scope: plan.requestedScope ?? plan.resolvedScope,
|
|
1815
|
-
},
|
|
1816
|
-
causes: [],
|
|
1817
|
-
detailLines: [],
|
|
1818
|
-
safeMessage: plan.errorCode === 'artifactCollision'
|
|
1819
|
-
? '输出位置存在外来内容,无法覆盖。'
|
|
1820
|
-
: plan.errorCode === 'exclusiveOwnerEvidenceInvalid'
|
|
1821
|
-
? '独立扩展的归属标记缺失或损坏。'
|
|
1822
|
-
: plan.errorCode === 'dependencyNotReady'
|
|
1823
|
-
? '依赖安装状态无法读取。'
|
|
1824
|
-
: '依赖计划阻止了此目标。',
|
|
1825
|
-
})),
|
|
1826
|
-
}] : [];
|
|
1827
|
-
}));
|
|
1828
|
-
draft.executableTargets = draft.executableTargets.filter((target) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(target)));
|
|
1829
|
-
draft.causes = draft.causes.map((cause) => ({
|
|
1830
|
-
...cause,
|
|
1831
|
-
consumers: cause.consumers.filter((consumer) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(consumer))),
|
|
1832
|
-
})).filter((cause) => cause.consumers.length > 0);
|
|
1833
|
-
}
|
|
1834
|
-
if (!preview?.operationPlan || draft.executableTargets.length === 0)
|
|
1835
|
-
continue;
|
|
1836
|
-
const sourceIdentity = draft.causes[0]?.sourceIdentity
|
|
1837
|
-
?? status.payload.catalog.sources.find((source) => source.id === sourceId)?.identity;
|
|
1838
|
-
if (!sourceIdentity) {
|
|
1839
|
-
throw new TuiOperationBlockedError('taskSourceIdentityMissing', '任务来源身份不可验证。');
|
|
1840
|
-
}
|
|
1841
|
-
const sourceEvidence = {
|
|
1842
|
-
sourceId,
|
|
1843
|
-
sourceIdentity,
|
|
1844
|
-
requestedTargets: draft.executableTargets,
|
|
1845
|
-
causes: draft.causes,
|
|
1846
|
-
preview,
|
|
1847
|
-
executable: true,
|
|
1848
|
-
};
|
|
1849
|
-
sources.push(sourceEvidence);
|
|
1850
|
-
executableTargets.push(...draft.executableTargets);
|
|
1851
|
-
causes.push(...draft.causes);
|
|
1852
|
-
}
|
|
1853
|
-
const canonicalBlocked = [...new Map(blockedTargets.map((target) => [
|
|
1854
|
-
pluginWriteTaskTargetKey(target.target), target,
|
|
1855
|
-
])).values()].sort((left, right) => pluginWriteTaskTargetKey(left.target)
|
|
1856
|
-
.localeCompare(pluginWriteTaskTargetKey(right.target), 'en'));
|
|
1857
|
-
if (executableTargets.length === 0) {
|
|
1858
|
-
throw new TuiOperationBlockedError('taskNoExecutableTargets', '任务没有可安全执行的目标。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1859
|
-
}
|
|
1860
|
-
if (intent.executionPolicy === 'all' && canonicalBlocked.length > 0) {
|
|
1861
|
-
throw new TuiOperationBlockedError('taskHasBlockedTargets', '部分目标需要先处理。请调整选择。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1862
|
-
}
|
|
1863
|
-
await preparation?.reporter.emit('evidence');
|
|
1864
|
-
const evidence = {
|
|
1865
|
-
catalog: {
|
|
1866
|
-
configDigest: configSnapshot.digest,
|
|
1867
|
-
compositeDigest: digest(sources.map((source) => [
|
|
1868
|
-
source.sourceId,
|
|
1869
|
-
source.sourceIdentity,
|
|
1870
|
-
source.preview.operationPlan?.planFingerprint,
|
|
1871
|
-
])),
|
|
1872
|
-
},
|
|
1873
|
-
taskPlan: {
|
|
1874
|
-
goal: intent.goal,
|
|
1875
|
-
requestedTargets: intent.targets,
|
|
1876
|
-
executableTargets: [...executableTargets].sort((left, right) => pluginWriteTaskTargetKey(left).localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1877
|
-
causes: [...new Map(causes.map((cause) => [cause.causeKey, cause])).values()]
|
|
1878
|
-
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en')),
|
|
1879
|
-
blockedTargets: canonicalBlocked,
|
|
1880
|
-
sources,
|
|
1881
|
-
targetFingerprint: statusTargetFingerprint(observedTargets, observedRevisions),
|
|
1882
|
-
planFingerprint: '',
|
|
1883
|
-
},
|
|
1884
|
-
};
|
|
1885
|
-
evidence.taskPlan.planFingerprint = operationEvidenceFingerprint(evidence);
|
|
1886
|
-
taskCatalogOperationsByEvidence.set(evidence, operationContexts);
|
|
1887
|
-
return evidence;
|
|
1888
|
-
}
|
|
1889
|
-
if (intent.kind === 'pluginWriteBatch') {
|
|
1890
|
-
await preparation?.reporter.emit('status');
|
|
1891
|
-
const selection = await resolveBatchGroups(intent, context.env, context.cwd, frozenBatchEvidence?.requestedPluginIds);
|
|
1892
|
-
signal?.throwIfAborted();
|
|
1893
|
-
if (intent.action === 'cleanup') {
|
|
1894
|
-
const groups = [];
|
|
1895
|
-
const reviewedRemovedPlugins = new Set();
|
|
1896
|
-
for (const group of selection.groups) {
|
|
1897
|
-
signal?.throwIfAborted();
|
|
1898
|
-
const requestedPluginIds = group.requestedPluginIds.filter((pluginId) => !reviewedRemovedPlugins.has(pluginId));
|
|
1899
|
-
if (requestedPluginIds.length === 0)
|
|
1900
|
-
continue;
|
|
1901
|
-
const preview = await runBatchWriteGroup({
|
|
1902
|
-
intent,
|
|
1903
|
-
group: { ...group, requestedPluginIds },
|
|
1904
|
-
cwd: context.cwd,
|
|
1905
|
-
runtime: {
|
|
1906
|
-
...context,
|
|
1907
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1908
|
-
previewOperationPlan: true,
|
|
1909
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1910
|
-
...(preparation ? {
|
|
1911
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1912
|
-
} : {}),
|
|
1913
|
-
},
|
|
1914
|
-
});
|
|
1915
|
-
for (const pluginId of preview.removedPluginCleanupIds ?? []) {
|
|
1916
|
-
reviewedRemovedPlugins.add(pluginId);
|
|
1917
|
-
}
|
|
1918
|
-
groups.push({ ...group, requestedPluginIds, preview,
|
|
1919
|
-
executable: preview.operationPlan !== undefined });
|
|
1920
|
-
}
|
|
1921
|
-
await preparation?.reporter.emit('evidence');
|
|
1922
|
-
signal?.throwIfAborted();
|
|
1923
|
-
const evidence = {
|
|
1924
|
-
catalog: {
|
|
1925
|
-
configDigest: 'cleanup-offline',
|
|
1926
|
-
compositeDigest: digest(groups.map((group) => [
|
|
1927
|
-
group.groupId,
|
|
1928
|
-
group.preview.operationPlan?.planFingerprint,
|
|
1929
|
-
])),
|
|
1930
|
-
},
|
|
1931
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
1932
|
-
groups,
|
|
1933
|
-
};
|
|
1934
|
-
return evidence;
|
|
1935
|
-
}
|
|
1936
|
-
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1937
|
-
const groups = [];
|
|
1938
|
-
const ownsObservationSession = !codexObservationSession
|
|
1939
|
-
&& selection.groups.some((group) => {
|
|
1940
|
-
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1941
|
-
return reviewed?.executable !== false
|
|
1942
|
-
&& (group.platform === 'codex' || group.platform === 'both');
|
|
1943
|
-
});
|
|
1944
|
-
const observationSession = codexObservationSession ?? (ownsObservationSession
|
|
1945
|
-
? await createCodexNativeObservationSession({
|
|
1946
|
-
exactCwd: context.cwd,
|
|
1947
|
-
env: context.env,
|
|
1948
|
-
runner: context.nativeCliRunner,
|
|
1949
|
-
})
|
|
1950
|
-
: undefined);
|
|
1951
|
-
try {
|
|
1952
|
-
await preparation?.reporter.emit('catalog');
|
|
1953
|
-
for (const group of selection.groups) {
|
|
1954
|
-
signal?.throwIfAborted();
|
|
1955
|
-
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1956
|
-
if (reviewed?.executable === false) {
|
|
1957
|
-
groups.push({ ...group, preview: reviewed.preview, executable: false });
|
|
1958
|
-
continue;
|
|
1959
|
-
}
|
|
1960
|
-
let catalogOperation;
|
|
1961
|
-
try {
|
|
1962
|
-
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1963
|
-
? selectCatalogOperationContext(sessionCatalogOperationContext, [group.sourceId])
|
|
1964
|
-
: await resolveCatalogOperationContext({
|
|
1965
|
-
sourceIds: [group.sourceId],
|
|
1966
|
-
env: context.env,
|
|
1967
|
-
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1968
|
-
...(signal ? { signal } : {}),
|
|
1969
|
-
});
|
|
1970
|
-
}
|
|
1971
|
-
catch {
|
|
1972
|
-
catalogOperation = undefined;
|
|
1973
|
-
}
|
|
1974
|
-
const preview = await runBatchWriteGroup({
|
|
1975
|
-
intent,
|
|
1976
|
-
group,
|
|
1977
|
-
cwd: context.cwd,
|
|
1978
|
-
runtime: {
|
|
1979
|
-
...context,
|
|
1980
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1981
|
-
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1982
|
-
...(catalogOperation ? {
|
|
1983
|
-
catalogOperationContext: catalogOperation,
|
|
1984
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1985
|
-
} : {}),
|
|
1986
|
-
previewOperationPlan: true,
|
|
1987
|
-
...(observationSession ? { codexObservationSession: observationSession } : {}),
|
|
1988
|
-
...(preparation ? {
|
|
1989
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1990
|
-
} : {}),
|
|
1991
|
-
},
|
|
1992
|
-
});
|
|
1993
|
-
groups.push({ ...group, preview, executable: preview.operationPlan !== undefined });
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
finally {
|
|
1997
|
-
if (ownsObservationSession)
|
|
1998
|
-
await observationSession?.dispose();
|
|
1999
|
-
}
|
|
2000
|
-
await preparation?.reporter.emit('evidence');
|
|
2001
|
-
signal?.throwIfAborted();
|
|
2002
|
-
const merged = mergeWriteCommandResults(groups.map((group) => group.preview));
|
|
2003
|
-
const targetVersions = groups.flatMap((group) => {
|
|
2004
|
-
if (group.platform === 'both')
|
|
2005
|
-
return [];
|
|
2006
|
-
const platform = group.platform;
|
|
2007
|
-
return group.requestedPluginIds.map((pluginId) => {
|
|
2008
|
-
const live = flattenStatusTargets(sessionStatusResult?.payload.targets ?? []).find((target) => target.pluginId === pluginId && target.platform === platform
|
|
2009
|
-
&& (target.scope === group.scope || statusTargetIntent(target).scopes.includes(group.scope)
|
|
2010
|
-
|| platform === 'codex' && statusTargetActivation(target)?.[group.scope] === true));
|
|
2011
|
-
const revision = sessionStatusResult?.targetRevisions?.find((target) => target.pluginId === pluginId && target.platform === platform
|
|
2012
|
-
&& target.scope === group.scope)?.revision;
|
|
2013
|
-
const activation = live ? statusTargetActivation(live) : undefined;
|
|
2014
|
-
const ownership = live && live.scope === group.scope
|
|
2015
|
-
? statusTargetOwnershipState(live) : 'absent';
|
|
2016
|
-
const present = ownership === 'present' || ownership === 'partial'
|
|
2017
|
-
|| platform === 'codex' && activation?.[group.scope] === true;
|
|
2018
|
-
const planned = live ? statusTargetIntent(live).scopes.includes(group.scope) : false;
|
|
2019
|
-
return {
|
|
2020
|
-
pluginId,
|
|
2021
|
-
platform,
|
|
2022
|
-
scope: group.scope,
|
|
2023
|
-
presence: present ? 'installed'
|
|
2024
|
-
: ownership === 'absent' && planned ? 'new' : 'unknown',
|
|
2025
|
-
...(revision?.installedVersion ? { installedVersion: revision.installedVersion } : {}),
|
|
2026
|
-
...(revision?.availableVersion ? { availableVersion: revision.availableVersion } : {}),
|
|
2027
|
-
};
|
|
2028
|
-
});
|
|
2029
|
-
});
|
|
2030
|
-
const evidence = {
|
|
2031
|
-
catalog: {
|
|
2032
|
-
configDigest: configSnapshot.digest,
|
|
2033
|
-
compositeDigest: sessionStatusResult?.payload.catalog.compositeDigest
|
|
2034
|
-
?? digest(groups.map((group) => [group.groupId, group.preview.operationPlan?.planFingerprint])),
|
|
2035
|
-
},
|
|
2036
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
2037
|
-
...(selection.projectIntentDigest
|
|
2038
|
-
? { projectIntentDigest: selection.projectIntentDigest }
|
|
2039
|
-
: {}),
|
|
2040
|
-
groups,
|
|
2041
|
-
targetVersions: [...new Map(targetVersions.map((target) => [[
|
|
2042
|
-
target.pluginId, target.platform, target.scope,
|
|
2043
|
-
].join('\0'), target])).values()].sort((left, right) => [
|
|
2044
|
-
left.pluginId, left.platform, left.scope,
|
|
2045
|
-
].join('\0').localeCompare([
|
|
2046
|
-
right.pluginId, right.platform, right.scope,
|
|
2047
|
-
].join('\0'), 'en')),
|
|
2048
|
-
...(merged.dependencyPlan ? { dependencyPlan: merged.dependencyPlan } : {}),
|
|
2049
|
-
...(merged.codexReconcilePlans ? { codexReconcilePlans: merged.codexReconcilePlans } : {}),
|
|
2050
|
-
...(merged.operationPlan ? { operationPlan: merged.operationPlan } : {}),
|
|
2051
|
-
};
|
|
2052
|
-
return evidence;
|
|
2053
|
-
}
|
|
2054
|
-
if (intent.kind === 'pluginWrite' && intent.action === 'cleanup') {
|
|
2055
|
-
const preview = await cleanupWriteCommand([intent.pluginId], {
|
|
2056
|
-
cwd: context.cwd,
|
|
2057
|
-
platform: intent.platform,
|
|
2058
|
-
scope: intent.scope,
|
|
2059
|
-
force: intent.force === true,
|
|
2060
|
-
}, {
|
|
2061
|
-
...context,
|
|
2062
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
2063
|
-
previewOperationPlan: true,
|
|
2064
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2065
|
-
...(preparation ? {
|
|
2066
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
2067
|
-
} : {}),
|
|
2068
|
-
});
|
|
2069
|
-
signal?.throwIfAborted();
|
|
2070
|
-
if (!preview.operationPlan) {
|
|
2071
|
-
throw new TuiOperationBlockedError('cleanupPlanUnavailable', '无法建立可信的清理计划。');
|
|
2072
|
-
}
|
|
2073
|
-
const offlineCleanupPlans = preview.offlineCleanupPlans ?? [];
|
|
2074
|
-
await preparation?.reporter.emit('evidence');
|
|
2075
|
-
return {
|
|
2076
|
-
catalog: {
|
|
2077
|
-
configDigest: 'cleanup-offline',
|
|
2078
|
-
compositeDigest: preview.operationPlan.planFingerprint,
|
|
2079
|
-
},
|
|
2080
|
-
operationPlan: preview.operationPlan,
|
|
2081
|
-
offlineCleanupPlans,
|
|
2082
|
-
...(preview.removedPluginCleanupIds
|
|
2083
|
-
? { removedPluginCleanupIds: preview.removedPluginCleanupIds }
|
|
2084
|
-
: {}),
|
|
2085
|
-
...(offlineCleanupPlans.length === 1
|
|
2086
|
-
? { offlineCleanup: offlineCleanupPlans[0] }
|
|
2087
|
-
: {}),
|
|
2088
|
-
targetFingerprint: preview.operationPlan.planFingerprint,
|
|
2089
|
-
};
|
|
2090
|
-
}
|
|
2091
1264
|
const packageInfo = await readCliPackageInfo();
|
|
2092
1265
|
let catalogOperation;
|
|
2093
1266
|
let catalogOperationError;
|
|
@@ -2159,6 +1332,75 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2159
1332
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2160
1333
|
return evidence;
|
|
2161
1334
|
}
|
|
1335
|
+
if (intent.kind === 'configSave' && intent.retryApply) {
|
|
1336
|
+
await preparation?.reporter.emit('config');
|
|
1337
|
+
if (!catalogOperation)
|
|
1338
|
+
throw catalogOperationError ?? operationReviewStale('无法取得设置应用所需的 Catalog。');
|
|
1339
|
+
const selectedCatalogOperation = catalogOperation;
|
|
1340
|
+
const evidence = await withResolvedCatalogPlugin({
|
|
1341
|
+
selector: intent.pluginId, cliVersion: packageInfo.version, env: context.env,
|
|
1342
|
+
...(selectedCatalogOperation ? { operationContext: selectedCatalogOperation } : {}),
|
|
1343
|
+
...(signal ? { signal } : {}),
|
|
1344
|
+
consume: async ({ plugin, source }) => {
|
|
1345
|
+
const referencePlugins = await resolvePluginConfigReferencePlugins({
|
|
1346
|
+
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
1347
|
+
logicalIdentity: plugin.catalogLogicalIdentity, env: context.env,
|
|
1348
|
+
operationContext: selectedCatalogOperation,
|
|
1349
|
+
});
|
|
1350
|
+
const prepared = await preparePluginConfigApply({
|
|
1351
|
+
plugin, scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1352
|
+
...(intent.targets ? { targets: intent.targets } : {}),
|
|
1353
|
+
cwd: context.cwd, env: context.env, cliVersion: packageInfo.version,
|
|
1354
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
1355
|
+
rebuildRuntime: {
|
|
1356
|
+
...context,
|
|
1357
|
+
referencePlugins,
|
|
1358
|
+
...(selectedCatalogOperation ? { catalogOperationContext: selectedCatalogOperation,
|
|
1359
|
+
expectedGenerationSetDigest: selectedCatalogOperation.generationSetDigest } : {}),
|
|
1360
|
+
...(reviewedScriptTimeoutMs(intent) === undefined ? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
1361
|
+
},
|
|
1362
|
+
});
|
|
1363
|
+
try {
|
|
1364
|
+
signal?.throwIfAborted();
|
|
1365
|
+
const targets = prepared.plan.receipt.requestedTargets
|
|
1366
|
+
.filter((target) => !prepared.plan.receipt.targetResults.some((result) => result.pluginId === target.pluginId && result.agent === target.agent && result.scope === target.scope
|
|
1367
|
+
&& (result.satisfaction === 'unsupported' || result.satisfaction === 'excluded')))
|
|
1368
|
+
.map((target) => ({
|
|
1369
|
+
pluginName: parsePluginId(target.pluginId).pluginName, platform: target.agent, scope: target.scope,
|
|
1370
|
+
}));
|
|
1371
|
+
return {
|
|
1372
|
+
catalog: {
|
|
1373
|
+
configDigest: selectedCatalogOperation.catalog.configDigest,
|
|
1374
|
+
compositeDigest: selectedCatalogOperation.catalog.compositeDigest,
|
|
1375
|
+
...(selectedCatalogOperation ? { generationSetDigest: selectedCatalogOperation.generationSetDigest } : {}),
|
|
1376
|
+
},
|
|
1377
|
+
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
1378
|
+
config: {
|
|
1379
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1380
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, intent.platform),
|
|
1381
|
+
storeDigest: digest(prepared.evidence),
|
|
1382
|
+
impactState: 'known', affectedTargets: targets,
|
|
1383
|
+
commandPackages: prepared.commandPackages,
|
|
1384
|
+
applyEvidence: prepared.evidence, applyChanges: prepared.plan.changes,
|
|
1385
|
+
applyWarnings: prepared.plan.warnings,
|
|
1386
|
+
applyBlockers: [...new Map([
|
|
1387
|
+
...prepared.plan.receipt.blockers, ...prepared.plan.receipt.errors,
|
|
1388
|
+
...prepared.plan.receipt.targetResults.flatMap((target) => target.blockers),
|
|
1389
|
+
].map((item) => [`${item.code}\0${item.message}`, item])).values()],
|
|
1390
|
+
preApplyAuthorization: prepared.preApplyAuthorization,
|
|
1391
|
+
sharedNativeRenderTargets: await configRetrySharedNativeRenderTargets(plugin, targets.filter((target) => target.platform === 'codex' || target.platform === 'claude'), context.env, referencePlugins),
|
|
1392
|
+
},
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
finally {
|
|
1396
|
+
await disposePluginConfigApply(prepared.plan);
|
|
1397
|
+
}
|
|
1398
|
+
},
|
|
1399
|
+
});
|
|
1400
|
+
if (catalogOperation)
|
|
1401
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
1402
|
+
return evidence;
|
|
1403
|
+
}
|
|
2162
1404
|
const needsReferencePlugins = intent.kind === 'configSave'
|
|
2163
1405
|
&& await withResolvedCatalogPlugin({
|
|
2164
1406
|
selector: intent.pluginId, cliVersion: packageInfo.version, env: context.env,
|
|
@@ -2193,7 +1435,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2193
1435
|
if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
|
|
2194
1436
|
throw operationReviewStale('准备设置确认期间来源配置发生变化。');
|
|
2195
1437
|
}
|
|
2196
|
-
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.
|
|
1438
|
+
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.context ?? 'user', configReferencePluginIds, intent.platform);
|
|
2197
1439
|
const preparedCatalogOperation = catalogOperation;
|
|
2198
1440
|
const selected = await withResolvedCatalogPlugin({
|
|
2199
1441
|
selector: intent.pluginId,
|
|
@@ -2206,14 +1448,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2206
1448
|
if (!plugin.manifest.config) {
|
|
2207
1449
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2208
1450
|
}
|
|
1451
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2209
1452
|
if (intent.kind === 'configSave') {
|
|
2210
1453
|
await coercePluginConfigUpdates({
|
|
2211
|
-
schema:
|
|
1454
|
+
schema: definition.fields,
|
|
2212
1455
|
updates: intent.updates ?? {},
|
|
2213
1456
|
cwd: context.cwd,
|
|
2214
1457
|
});
|
|
2215
1458
|
for (const key of intent.removeKeys ?? []) {
|
|
2216
|
-
if (!
|
|
1459
|
+
if (!definition.fields[key]) {
|
|
2217
1460
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2218
1461
|
}
|
|
2219
1462
|
}
|
|
@@ -2221,7 +1464,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2221
1464
|
const impact = intent.retryApply ? undefined : await analyzePluginConfigMutationImpact({
|
|
2222
1465
|
plugin,
|
|
2223
1466
|
referencePlugins,
|
|
2224
|
-
scope: intent.
|
|
1467
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2225
1468
|
cwd: context.cwd,
|
|
2226
1469
|
env: context.env,
|
|
2227
1470
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
@@ -2235,12 +1478,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2235
1478
|
? configImpactVisibleTargets(plugin.manifest.name, relevantTargets)
|
|
2236
1479
|
: impact.affectedTargets;
|
|
2237
1480
|
const commandPackages = impact?.affectedCommandPackages
|
|
2238
|
-
?? (await discoverPluginConfigManagedCommandPackages({ plugin, scope: intent.
|
|
1481
|
+
?? (await discoverPluginConfigManagedCommandPackages({ plugin, scope: intent.context ?? 'user',
|
|
2239
1482
|
cwd: context.cwd, env: context.env ?? process.env, platformTargets: affectedTargets, referencePlugins })).packages;
|
|
2240
1483
|
const preApplyAuthorization = configIntentRequestsApply(intent)
|
|
2241
1484
|
? (await resolveConfigPreApplyAuthorization({
|
|
2242
1485
|
plugin,
|
|
2243
|
-
scope: intent.
|
|
1486
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2244
1487
|
cwd: context.cwd,
|
|
2245
1488
|
env: context.env,
|
|
2246
1489
|
applyTargets: affectedTargets,
|
|
@@ -2256,12 +1499,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2256
1499
|
return {
|
|
2257
1500
|
pluginName: plugin.manifest.name,
|
|
2258
1501
|
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
1502
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, intent.platform),
|
|
2259
1503
|
impact,
|
|
2260
1504
|
commandPackages,
|
|
2261
1505
|
preApplyAuthorization,
|
|
2262
|
-
retryStoreDigest: intent.retryApply ? await configRetryStoreDigest({ plugin, scope: intent.
|
|
2263
|
-
targets: affectedTargets, commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env,
|
|
2264
|
-
plugins: referencePlugins }) : undefined,
|
|
1506
|
+
retryStoreDigest: intent.retryApply ? await configRetryStoreDigest({ plugin, scope: intent.context ?? 'user', platform: intent.platform,
|
|
1507
|
+
targets: affectedTargets, commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env, plugins: referencePlugins }) : undefined,
|
|
2265
1508
|
sharedNativeRenderTargets: intent.retryApply
|
|
2266
1509
|
? await configRetrySharedNativeRenderTargets(plugin, affectedTargets, context.env, referencePlugins)
|
|
2267
1510
|
: impact.sharedNativeRenderTargets,
|
|
@@ -2282,8 +1525,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2282
1525
|
...(selected.source ? { source: selected.source } : {}),
|
|
2283
1526
|
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
2284
1527
|
config: {
|
|
2285
|
-
scope: intent.
|
|
2286
|
-
|
|
1528
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1529
|
+
schemaIdentity: selected.schemaIdentity,
|
|
2287
1530
|
storeDigest: selected.impact?.storeDigest
|
|
2288
1531
|
?? selected.retryStoreDigest,
|
|
2289
1532
|
impactState: selected.impact?.impactState ?? 'known',
|
|
@@ -2299,6 +1542,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2299
1542
|
: {}),
|
|
2300
1543
|
},
|
|
2301
1544
|
};
|
|
1545
|
+
await assertConfigEditEvidence(intent, evidence);
|
|
2302
1546
|
if (catalogOperation)
|
|
2303
1547
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2304
1548
|
return evidence;
|
|
@@ -2307,17 +1551,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2307
1551
|
const statusCatalogOperation = catalogOperation
|
|
2308
1552
|
?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
|
|
2309
1553
|
const status = await statusCommandV8({
|
|
2310
|
-
plugins: [...new Set([intent.pluginId, ...configReferencePluginIds,
|
|
2311
|
-
|
|
2312
|
-
? [intent.originIssue.consumerPluginId] : [])])],
|
|
2313
|
-
...('platform' in intent ? { platform: intent.platform } : {}),
|
|
2314
|
-
...(intent.kind !== 'pluginWrite' && intent.scope === 'project'
|
|
2315
|
-
? { scope: intent.scope }
|
|
2316
|
-
: {}),
|
|
1554
|
+
plugins: [...new Set([intent.pluginId, ...configReferencePluginIds])],
|
|
1555
|
+
...(intent.context === 'project' ? {} : { scope: 'user' }),
|
|
2317
1556
|
cwd: context.cwd,
|
|
2318
1557
|
env: context.env,
|
|
2319
1558
|
nativeCliRunner: context.nativeCliRunner,
|
|
2320
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2321
1559
|
cliVersion: packageInfo.version,
|
|
2322
1560
|
...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
|
|
2323
1561
|
inventoryCatalogSourceReferences: operationInventory,
|
|
@@ -2339,12 +1577,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2339
1577
|
...(signal ? { signal } : {}),
|
|
2340
1578
|
});
|
|
2341
1579
|
}
|
|
2342
|
-
let dependencyPlan;
|
|
2343
|
-
let codexReconcilePlans;
|
|
2344
|
-
let operationPlan;
|
|
2345
1580
|
await preparation?.reporter.emit('catalog');
|
|
2346
1581
|
const configVisibleTargets = intent.kind === 'configSave'
|
|
2347
|
-
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.
|
|
1582
|
+
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.context ?? 'user', configReferencePluginIds, intent.platform)
|
|
2348
1583
|
: [];
|
|
2349
1584
|
const selected = await withResolvedCatalogPlugin({
|
|
2350
1585
|
selector: intent.pluginId,
|
|
@@ -2359,13 +1594,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2359
1594
|
if (!plugin.manifest.config) {
|
|
2360
1595
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2361
1596
|
}
|
|
1597
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2362
1598
|
await coercePluginConfigUpdates({
|
|
2363
|
-
schema:
|
|
1599
|
+
schema: definition.fields,
|
|
2364
1600
|
updates: intent.updates ?? {},
|
|
2365
1601
|
cwd: context.cwd,
|
|
2366
1602
|
});
|
|
2367
1603
|
for (const key of intent.removeKeys ?? []) {
|
|
2368
|
-
if (!
|
|
1604
|
+
if (!definition.fields[key]) {
|
|
2369
1605
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2370
1606
|
}
|
|
2371
1607
|
}
|
|
@@ -2374,7 +1610,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2374
1610
|
? await analyzePluginConfigMutationImpact({
|
|
2375
1611
|
plugin,
|
|
2376
1612
|
referencePlugins,
|
|
2377
|
-
scope: intent.
|
|
1613
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2378
1614
|
cwd: context.cwd,
|
|
2379
1615
|
env: context.env,
|
|
2380
1616
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
@@ -2390,7 +1626,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2390
1626
|
: [];
|
|
2391
1627
|
const commandPackages = impact?.affectedCommandPackages
|
|
2392
1628
|
?? (intent.kind === 'configSave' ? (await discoverPluginConfigManagedCommandPackages({
|
|
2393
|
-
plugin, scope: intent.
|
|
1629
|
+
plugin, scope: intent.context ?? 'user', cwd: context.cwd, env: context.env ?? process.env,
|
|
2394
1630
|
platformTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
2395
1631
|
referencePlugins,
|
|
2396
1632
|
})).packages : []);
|
|
@@ -2398,7 +1634,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2398
1634
|
&& configIntentRequestsApply(intent)
|
|
2399
1635
|
? (await resolveConfigPreApplyAuthorization({
|
|
2400
1636
|
plugin,
|
|
2401
|
-
scope: intent.
|
|
1637
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2402
1638
|
cwd: context.cwd,
|
|
2403
1639
|
env: context.env,
|
|
2404
1640
|
applyTargets: intent.retryApply
|
|
@@ -2418,11 +1654,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2418
1654
|
return {
|
|
2419
1655
|
pluginName: plugin.manifest.name,
|
|
2420
1656
|
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
1657
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, intent.platform),
|
|
2421
1658
|
impact,
|
|
2422
1659
|
commandPackages,
|
|
2423
1660
|
preApplyAuthorization,
|
|
2424
1661
|
retryStoreDigest: intent.kind === 'configSave' && intent.retryApply
|
|
2425
|
-
? await configRetryStoreDigest({ plugin, scope: intent.
|
|
1662
|
+
? await configRetryStoreDigest({ plugin, scope: intent.context ?? 'user', platform: intent.platform, targets: affectedTargets,
|
|
2426
1663
|
commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env, plugins: referencePlugins }) : undefined,
|
|
2427
1664
|
sharedNativeRenderTargets: intent.kind === 'configSave'
|
|
2428
1665
|
? intent.retryApply
|
|
@@ -2432,106 +1669,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2432
1669
|
};
|
|
2433
1670
|
},
|
|
2434
1671
|
});
|
|
2435
|
-
if (intent.kind === 'pluginWrite' && intent.expectedUpdate) {
|
|
2436
|
-
const revision = status.targetRevisions.find((entry) => entry.pluginId === intent.pluginId && entry.platform === intent.platform
|
|
2437
|
-
&& entry.scope === intent.scope);
|
|
2438
|
-
const generation = catalogOperation?.generationSet.sources.find((entry) => entry.sourceId === selected.source.sourceId);
|
|
2439
|
-
if (intent.action !== 'sync'
|
|
2440
|
-
|| !('pluginVersion' in selected.source)
|
|
2441
|
-
|| selected.source.sourceIdentity !== intent.expectedUpdate.sourceIdentity
|
|
2442
|
-
|| selected.source.pluginVersion !== intent.expectedUpdate.activeVersion
|
|
2443
|
-
|| generation?.storeKey !== intent.expectedUpdate.generationKey
|
|
2444
|
-
|| revision?.revision.installedVersion !== intent.expectedUpdate.installedVersion
|
|
2445
|
-
|| revision?.revision.availableVersion !== intent.expectedUpdate.activeVersion
|
|
2446
|
-
|| revision.revision.state !== 'upgradeAvailable') {
|
|
2447
|
-
throw operationReviewStale('插件更新版本或 Active Catalog 证据已变化。');
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
let codexManager;
|
|
2451
|
-
let resolvedCodexManager;
|
|
2452
|
-
const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
|
|
2453
|
-
if (intent.kind === 'pluginWrite') {
|
|
2454
|
-
const command = {
|
|
2455
|
-
install: installWriteCommand,
|
|
2456
|
-
refreshAndSync: syncWriteCommand,
|
|
2457
|
-
reinstall: reinstallWriteCommand,
|
|
2458
|
-
uninstall: uninstallWriteCommand,
|
|
2459
|
-
cleanup: cleanupWriteCommand,
|
|
2460
|
-
sync: syncWriteCommand,
|
|
2461
|
-
}[intent.action];
|
|
2462
|
-
const preview = await command([intent.pluginId], {
|
|
2463
|
-
cwd: context.cwd,
|
|
2464
|
-
platform: intent.platform,
|
|
2465
|
-
scope: intent.scope,
|
|
2466
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
2467
|
-
? {}
|
|
2468
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
2469
|
-
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
2470
|
-
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
2471
|
-
: {}),
|
|
2472
|
-
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
2473
|
-
}, {
|
|
2474
|
-
...context,
|
|
2475
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
2476
|
-
expectedCatalogConfigDigest: status.payload.catalog.configDigest,
|
|
2477
|
-
...(catalogOperation ? {
|
|
2478
|
-
catalogOperationContext: catalogOperation,
|
|
2479
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
2480
|
-
} : {}),
|
|
2481
|
-
previewOperationPlan: true,
|
|
2482
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2483
|
-
...(preparation ? {
|
|
2484
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
2485
|
-
} : {}),
|
|
2486
|
-
});
|
|
2487
|
-
signal?.throwIfAborted();
|
|
2488
|
-
codexReconcilePlans = preview?.codexReconcilePlans;
|
|
2489
|
-
operationPlan = preview?.operationPlan;
|
|
2490
|
-
if (hasDeclaredDependencies
|
|
2491
|
-
&& (intent.action === 'install'
|
|
2492
|
-
|| intent.action === 'refreshAndSync'
|
|
2493
|
-
|| intent.action === 'reinstall'
|
|
2494
|
-
|| intent.action === 'sync')) {
|
|
2495
|
-
dependencyPlan = preview.dependencyPlan;
|
|
2496
|
-
if (!dependencyPlan) {
|
|
2497
|
-
throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
|
|
2498
|
-
}
|
|
2499
|
-
const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
|
|
2500
|
-
if (blocker) {
|
|
2501
|
-
const code = blocker.errorCode ?? 'dependencyNotReady';
|
|
2502
|
-
const reason = code === 'dependencyScopeConflict' || code === 'codexExtensionScopeConflict'
|
|
2503
|
-
? '这个依赖的安装位置存在冲突,需要先选择保留的位置。'
|
|
2504
|
-
: code === 'artifactCollision' || code === 'exclusiveOwnerEvidenceInvalid'
|
|
2505
|
-
? '这个依赖的安装位置被其他内容占用,需要先检查并处理冲突。'
|
|
2506
|
-
: '这个依赖尚不可用,需要先检查并恢复后再安装。';
|
|
2507
|
-
throw new TuiOperationBlockedError(code, `${blocker.pluginId}:${reason}`, [{
|
|
2508
|
-
id: `dependency:${blocker.pluginId}:${blocker.platform}:${blocker.resolvedScope}`,
|
|
2509
|
-
code, stage: 'dependencyPlan', safeMessage: reason,
|
|
2510
|
-
target: { pluginId: blocker.pluginId, platform: blocker.platform, scope: blocker.resolvedScope },
|
|
2511
|
-
causes: [], detailLines: [],
|
|
2512
|
-
}]);
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
const managerPreview = preview?.codexManagerPreview;
|
|
2516
|
-
if (managerPreview?.state === 'blocked') {
|
|
2517
|
-
const message = managerPreview.code === 'codexSystemCliUnsupported'
|
|
2518
|
-
? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
2519
|
-
: 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
|
|
2520
|
-
throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
|
|
2521
|
-
}
|
|
2522
|
-
if (managerPreview) {
|
|
2523
|
-
if (managerPreview.state === 'system' || managerPreview.state === 'cached') {
|
|
2524
|
-
resolvedCodexManager = managerPreview.manager;
|
|
2525
|
-
}
|
|
2526
|
-
codexManager = {
|
|
2527
|
-
state: managerPreview.state,
|
|
2528
|
-
packageName: MANAGED_CODEX_PACKAGE_NAME,
|
|
2529
|
-
version: managerPreview.state === 'requires-install'
|
|
2530
|
-
? MANAGED_CODEX_VERSION
|
|
2531
|
-
: managerPreview.manager.version,
|
|
2532
|
-
};
|
|
2533
|
-
}
|
|
2534
|
-
}
|
|
2535
1672
|
await preparation?.reporter.emit('evidence');
|
|
2536
1673
|
signal?.throwIfAborted();
|
|
2537
1674
|
const evidenceTargets = intent.kind === 'configSave'
|
|
@@ -2551,10 +1688,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2551
1688
|
: {}),
|
|
2552
1689
|
},
|
|
2553
1690
|
source: selected.source,
|
|
2554
|
-
...(dependencyPlan ? { dependencyPlan } : {}),
|
|
2555
|
-
...(codexReconcilePlans ? { codexReconcilePlans } : {}),
|
|
2556
|
-
...(operationPlan ? { operationPlan } : {}),
|
|
2557
|
-
...(codexManager ? { codexManager } : {}),
|
|
2558
1691
|
targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
|
|
2559
1692
|
};
|
|
2560
1693
|
if (intent.kind === 'configSave') {
|
|
@@ -2564,10 +1697,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2564
1697
|
throw operationReviewStale('设置操作缺少实际配置输入证据。');
|
|
2565
1698
|
signal?.throwIfAborted();
|
|
2566
1699
|
evidence.config = {
|
|
2567
|
-
scope: intent.
|
|
2568
|
-
|
|
2569
|
-
? { schemaIdentity: selected.source.manifestDigest }
|
|
2570
|
-
: {}),
|
|
1700
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1701
|
+
schemaIdentity: selected.schemaIdentity,
|
|
2571
1702
|
storeDigest,
|
|
2572
1703
|
impactState: impact?.impactState ?? 'known',
|
|
2573
1704
|
...(impact?.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
@@ -2575,15 +1706,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2575
1706
|
? { commandPackages: selected.commandPackages } : {}),
|
|
2576
1707
|
affectedTargets: (impact?.affectedTargets
|
|
2577
1708
|
?? evidenceTargets).map(({ pluginName, platform, scope }) => ({ pluginName, platform, scope })),
|
|
2578
|
-
...(intent.scope === 'project' ? {
|
|
2579
|
-
shareability: await inspectConfigShareability({
|
|
2580
|
-
sourceId: selected.source.sourceId,
|
|
2581
|
-
pluginName: selected.pluginName,
|
|
2582
|
-
scope: intent.scope,
|
|
2583
|
-
cwd: context.cwd,
|
|
2584
|
-
env: context.env,
|
|
2585
|
-
}),
|
|
2586
|
-
} : {}),
|
|
2587
1709
|
...('sharedNativeRenderTargets' in selected
|
|
2588
1710
|
&& selected.sharedNativeRenderTargets.length > 0
|
|
2589
1711
|
? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
|
|
@@ -2594,13 +1716,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2594
1716
|
: {}),
|
|
2595
1717
|
};
|
|
2596
1718
|
}
|
|
1719
|
+
await assertConfigEditEvidence(intent, evidence);
|
|
2597
1720
|
if (catalogOperation)
|
|
2598
1721
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2599
|
-
if (resolvedCodexManager)
|
|
2600
|
-
codexManagerByEvidence.set(evidence, resolvedCodexManager);
|
|
2601
1722
|
return evidence;
|
|
2602
1723
|
};
|
|
2603
1724
|
const dependencies = {
|
|
1725
|
+
readAgentSettings: () => readAgentSettings(context),
|
|
1726
|
+
previewAgentSettings: (preferences, pluginId) => previewAgentSettings(context, preferences, pluginId),
|
|
1727
|
+
saveAgentSettings: (request) => saveAgentSettings(context, request),
|
|
2604
1728
|
beginCatalogSession(sessionOptions) {
|
|
2605
1729
|
sessionCatalogOperationContext = undefined;
|
|
2606
1730
|
if (sessionOptions?.reuseValidatedGenerations !== true) {
|
|
@@ -2609,6 +1733,104 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2609
1733
|
}
|
|
2610
1734
|
},
|
|
2611
1735
|
loadStatus,
|
|
1736
|
+
async loadScopedStatus(scope, signal) {
|
|
1737
|
+
signal.throwIfAborted();
|
|
1738
|
+
const packageInfo = await readCliPackageInfo();
|
|
1739
|
+
const currentCatalog = await resolvePartialCatalogOperationContext({
|
|
1740
|
+
env: context.env, signal, validatedStoreEntries, validatingStoreEntries,
|
|
1741
|
+
});
|
|
1742
|
+
signal.throwIfAborted();
|
|
1743
|
+
if (sessionCatalogOperationContext
|
|
1744
|
+
&& currentCatalog.generationSetDigest !== sessionCatalogOperationContext.generationSetDigest) {
|
|
1745
|
+
throw new TuiOperationBlockedError('catalogSessionReloadRequired', 'Catalog 已变化。请按 H 重新加载,再检查清单。');
|
|
1746
|
+
}
|
|
1747
|
+
const catalogOperationContext = sessionCatalogOperationContext ?? currentCatalog;
|
|
1748
|
+
let nativeSnapshot;
|
|
1749
|
+
const status = await readRecoveryStatus({
|
|
1750
|
+
scope, cwd: context.cwd, env: context.env, cliVersion: packageInfo.version,
|
|
1751
|
+
nativeCliRunner: context.nativeCliRunner,
|
|
1752
|
+
catalogOperationContext, signal,
|
|
1753
|
+
onNativeStatus: async (native, demand) => {
|
|
1754
|
+
signal.throwIfAborted();
|
|
1755
|
+
const dependencyOverviews = await loadDependencyOverviews({ context,
|
|
1756
|
+
cliVersion: packageInfo.version, status: native, signal,
|
|
1757
|
+
catalogOperationContext: demand.catalogOperationContext,
|
|
1758
|
+
});
|
|
1759
|
+
nativeSnapshot = { payload: native.payload, pluginSummaries: native.pluginSummaries, targetRevisions: native.targetRevisions,
|
|
1760
|
+
dependencyOverviews };
|
|
1761
|
+
},
|
|
1762
|
+
});
|
|
1763
|
+
signal.throwIfAborted();
|
|
1764
|
+
if (!nativeSnapshot)
|
|
1765
|
+
throw new Error('nativeObservationUnavailable');
|
|
1766
|
+
return { ...status, nativeSnapshot };
|
|
1767
|
+
},
|
|
1768
|
+
async prepareScopedTask(request, signal) {
|
|
1769
|
+
signal.throwIfAborted();
|
|
1770
|
+
if (request.kind === 'targets') {
|
|
1771
|
+
return prepareTargetsPlan({ scope: request.scope, cwd: context.cwd, env: context.env }, request.action, request.agents);
|
|
1772
|
+
}
|
|
1773
|
+
if (request.kind === 'intent') {
|
|
1774
|
+
return prepareIntentPlan({ scope: request.scope, cwd: context.cwd, env: context.env }, request.action, request.selector);
|
|
1775
|
+
}
|
|
1776
|
+
if (request.kind === 'retire') {
|
|
1777
|
+
const packageInfo = await readCliPackageInfo();
|
|
1778
|
+
signal.throwIfAborted();
|
|
1779
|
+
const plan = await prepareRetire({ scope: request.scope, cwd: context.cwd, env: context.env,
|
|
1780
|
+
signal,
|
|
1781
|
+
selector: request.selector, cliVersion: packageInfo.version,
|
|
1782
|
+
...(request.targets ? { targets: request.targets } : {}),
|
|
1783
|
+
deleteConfig: request.deleteConfig === true,
|
|
1784
|
+
}, context);
|
|
1785
|
+
if (signal.aborted) {
|
|
1786
|
+
await plan.context.dispose();
|
|
1787
|
+
signal.throwIfAborted();
|
|
1788
|
+
}
|
|
1789
|
+
return { kind: 'retire', scope: request.scope, plan };
|
|
1790
|
+
}
|
|
1791
|
+
const packageInfo = await readCliPackageInfo();
|
|
1792
|
+
signal.throwIfAborted();
|
|
1793
|
+
const plan = await prepareRecovery(request.command ?? 'sync', request.selectors, {
|
|
1794
|
+
signal,
|
|
1795
|
+
scope: request.scope, cwd: context.cwd, env: context.env,
|
|
1796
|
+
cliVersion: packageInfo.version,
|
|
1797
|
+
all: request.selectors.length === 0,
|
|
1798
|
+
...(request.targets ? { targets: request.targets } : {}),
|
|
1799
|
+
...(request.force ? { force: true } : {}),
|
|
1800
|
+
...(request.refresh ? { refresh: true } : {}),
|
|
1801
|
+
...(request.retireUnselectedTargets ? { retireUnselectedTargets: true } : {}),
|
|
1802
|
+
...(request.scriptTimeoutMs !== undefined ? { scriptTimeoutMs: request.scriptTimeoutMs } : {}),
|
|
1803
|
+
...(request.dependencyScopeOverrides?.length
|
|
1804
|
+
? { dependencyScopeOverrides: request.dependencyScopeOverrides } : {}),
|
|
1805
|
+
...(request.exactTargets ? { exactTargets: request.exactTargets } : {}),
|
|
1806
|
+
...(request.saveDependencyScopeOverrides ? { saveDependencyScopeOverrides: true } : {}),
|
|
1807
|
+
}, context);
|
|
1808
|
+
if (signal.aborted) {
|
|
1809
|
+
await plan.context.dispose();
|
|
1810
|
+
signal.throwIfAborted();
|
|
1811
|
+
}
|
|
1812
|
+
return { kind: 'recovery', scope: request.scope, plan };
|
|
1813
|
+
},
|
|
1814
|
+
async executeScopedTask(plan) {
|
|
1815
|
+
if (plan.kind === 'targets') {
|
|
1816
|
+
const result = await executeTargetsPlan({ scope: plan.scope, cwd: context.cwd, env: context.env }, plan);
|
|
1817
|
+
return { kind: 'targets', ...result };
|
|
1818
|
+
}
|
|
1819
|
+
if (plan.kind === 'intent') {
|
|
1820
|
+
const receipt = await executeIntentPlan({ scope: plan.scope, cwd: context.cwd, env: context.env }, plan);
|
|
1821
|
+
return { kind: 'recovery', receipt };
|
|
1822
|
+
}
|
|
1823
|
+
if (plan.kind === 'retire') {
|
|
1824
|
+
const receipt = await executeRetire(plan.plan, { yes: true, runtime: context });
|
|
1825
|
+
return { kind: 'recovery', receipt };
|
|
1826
|
+
}
|
|
1827
|
+
if ((plan.plan.sourceRefreshes?.length ?? 0) > 0) {
|
|
1828
|
+
const refreshed = await activateRecoverySourcesAndReprepare(plan.plan, { runtime: context });
|
|
1829
|
+
return { kind: 'recoveryReview', plan: { kind: 'recovery', scope: plan.scope, plan: refreshed } };
|
|
1830
|
+
}
|
|
1831
|
+
const receipt = await executeRecovery(plan.plan, { yes: true, runtime: context });
|
|
1832
|
+
return { kind: 'recovery', receipt };
|
|
1833
|
+
},
|
|
2612
1834
|
loadPluginContent,
|
|
2613
1835
|
async probeCatalogFreshness(sourceId, freshnessOptions, signal) {
|
|
2614
1836
|
signal.throwIfAborted();
|
|
@@ -2657,55 +1879,42 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2657
1879
|
now: options.now ?? Date.now,
|
|
2658
1880
|
});
|
|
2659
1881
|
try {
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
const sourceId = parsePluginId(intent.pluginId).sourceId;
|
|
2665
|
-
await syncCatalogSourceGeneration({
|
|
2666
|
-
sourceId,
|
|
2667
|
-
cliVersion: packageInfo.version,
|
|
2668
|
-
cwd: context.cwd,
|
|
2669
|
-
env: context.env,
|
|
2670
|
-
signal,
|
|
2671
|
-
}).catch((error) => {
|
|
2672
|
-
preparationWarnings.push(`catalogSourceRefreshFailed: ${sourceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2673
|
-
});
|
|
2674
|
-
sessionCatalogOperationContext = undefined;
|
|
2675
|
-
sessionStatusResult = undefined;
|
|
2676
|
-
validatedStoreEntries.clear();
|
|
2677
|
-
validatingStoreEntries.clear();
|
|
2678
|
-
}
|
|
2679
|
-
const taskObservationSession = intent.kind === 'pluginWriteTask'
|
|
2680
|
-
&& intent.targets.some((target) => target.platform === 'codex')
|
|
2681
|
-
? await createCodexNativeObservationSession({
|
|
2682
|
-
exactCwd: context.cwd,
|
|
2683
|
-
env: context.env,
|
|
2684
|
-
runner: context.nativeCliRunner,
|
|
2685
|
-
})
|
|
2686
|
-
: undefined;
|
|
2687
|
-
let evidence;
|
|
2688
|
-
try {
|
|
2689
|
-
evidence = await captureEvidence(intent, signal, {
|
|
2690
|
-
reporter,
|
|
2691
|
-
validateIntent: true,
|
|
2692
|
-
}, createOperationInventoryReader(), intent.kind === 'pluginWrite'
|
|
2693
|
-
&& intent.action === 'refreshAndSync', reusableCandidate, taskObservationSession);
|
|
2694
|
-
}
|
|
2695
|
-
finally {
|
|
2696
|
-
await taskObservationSession?.dispose();
|
|
2697
|
-
}
|
|
2698
|
-
if (preparationWarnings.length > 0)
|
|
2699
|
-
evidence.preparationWarnings = preparationWarnings;
|
|
2700
|
-
return evidence;
|
|
1882
|
+
return await captureEvidence(intent, signal, {
|
|
1883
|
+
reporter,
|
|
1884
|
+
validateIntent: true,
|
|
1885
|
+
}, createOperationInventoryReader(), false, reusableCandidate);
|
|
2701
1886
|
}
|
|
2702
1887
|
finally {
|
|
2703
1888
|
await reporter.finish();
|
|
2704
1889
|
}
|
|
2705
1890
|
},
|
|
2706
1891
|
async previewDependencyPlacement(intent, signal) {
|
|
2707
|
-
const
|
|
2708
|
-
|
|
1892
|
+
const targets = intent.platform === 'both' ? ['codex', 'claude']
|
|
1893
|
+
: [intent.platform === 'claude' ? 'claude' : 'codex'];
|
|
1894
|
+
const prepared = await dependencies.prepareScopedTask({
|
|
1895
|
+
kind: 'recovery', command: 'install', scope: intent.scope,
|
|
1896
|
+
selectors: [intent.pluginId], targets: [...targets],
|
|
1897
|
+
dependencyScopeOverrides: intent.dependencyScopeOverrides,
|
|
1898
|
+
}, signal);
|
|
1899
|
+
if (prepared.kind !== 'recovery')
|
|
1900
|
+
throw new Error('dependencyPlacementUnavailable');
|
|
1901
|
+
try {
|
|
1902
|
+
const plans = prepared.plan.nativeGroups.flatMap((group) => group.preview.dependencyPlan ? [group.preview.dependencyPlan] : []);
|
|
1903
|
+
if (plans.length === 0) {
|
|
1904
|
+
throw new Error(prepared.plan.receipt.blockers[0]?.code
|
|
1905
|
+
?? prepared.plan.receipt.targetResults[0]?.blockers[0]?.code ?? 'dependencyPlacementUnavailable');
|
|
1906
|
+
}
|
|
1907
|
+
return {
|
|
1908
|
+
requestedPluginIds: [...new Set(plans.flatMap((plan) => plan.requestedPluginIds))],
|
|
1909
|
+
resolvedPluginIds: [...new Set(plans.flatMap((plan) => plan.resolvedPluginIds))],
|
|
1910
|
+
dependencyEdges: plans.flatMap((plan) => plan.dependencyEdges),
|
|
1911
|
+
scopeOverrides: plans.flatMap((plan) => plan.scopeOverrides),
|
|
1912
|
+
targets: plans.flatMap((plan) => plan.targets),
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
finally {
|
|
1916
|
+
await prepared.plan.context.dispose();
|
|
1917
|
+
}
|
|
2709
1918
|
},
|
|
2710
1919
|
async validateIntent(intent, signal) {
|
|
2711
1920
|
if (intent.kind === 'catalogSourcesRecovery') {
|
|
@@ -2744,13 +1953,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2744
1953
|
if (!plugin.manifest.config) {
|
|
2745
1954
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2746
1955
|
}
|
|
1956
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2747
1957
|
await coercePluginConfigUpdates({
|
|
2748
|
-
schema:
|
|
1958
|
+
schema: definition.fields,
|
|
2749
1959
|
updates: intent.updates ?? {},
|
|
2750
1960
|
cwd: context.cwd,
|
|
2751
1961
|
});
|
|
2752
1962
|
for (const key of intent.removeKeys ?? []) {
|
|
2753
|
-
if (!
|
|
1963
|
+
if (!definition.fields[key]) {
|
|
2754
1964
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2755
1965
|
}
|
|
2756
1966
|
}
|
|
@@ -2790,39 +2000,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2790
2000
|
data: { code },
|
|
2791
2001
|
});
|
|
2792
2002
|
},
|
|
2793
|
-
async getConfigGroups(pluginId,
|
|
2794
|
-
|
|
2795
|
-
return withResolvedCatalogPlugin({
|
|
2796
|
-
selector: pluginId,
|
|
2797
|
-
cliVersion: packageInfo.version,
|
|
2798
|
-
env: context.env,
|
|
2799
|
-
signal,
|
|
2800
|
-
...(sessionContextForPlugin(pluginId)
|
|
2801
|
-
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
2802
|
-
: {}),
|
|
2803
|
-
consume: async ({ plugin }) => {
|
|
2804
|
-
if (!plugin.manifest.config) {
|
|
2805
|
-
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2806
|
-
}
|
|
2807
|
-
const read = await resolveConfigRead({
|
|
2808
|
-
pluginName: plugin.manifest.name,
|
|
2809
|
-
plugin,
|
|
2810
|
-
scope: layer.scope,
|
|
2811
|
-
cwd: context.cwd,
|
|
2812
|
-
env: context.env,
|
|
2813
|
-
});
|
|
2814
|
-
const effectiveRead = await resolveConfigRead({
|
|
2815
|
-
pluginName: plugin.manifest.name,
|
|
2816
|
-
plugin,
|
|
2817
|
-
scope: 'effective',
|
|
2818
|
-
cwd: context.cwd,
|
|
2819
|
-
env: context.env,
|
|
2820
|
-
});
|
|
2821
|
-
return buildConfigGroups({ schema: plugin.manifest.config, read, effectiveRead });
|
|
2822
|
-
},
|
|
2823
|
-
});
|
|
2003
|
+
async getConfigGroups(pluginId, config, signal) {
|
|
2004
|
+
return (await dependencies.getConfigPage(pluginId, config, signal)).groups;
|
|
2824
2005
|
},
|
|
2825
|
-
async getConfigPage(pluginId,
|
|
2006
|
+
async getConfigPage(pluginId, config, signal) {
|
|
2007
|
+
if (config.context === 'project')
|
|
2008
|
+
await resolveWorkContext({ cwd: context.cwd, context: 'project' });
|
|
2826
2009
|
const packageInfo = await readCliPackageInfo();
|
|
2827
2010
|
return withResolvedCatalogPlugin({
|
|
2828
2011
|
selector: pluginId,
|
|
@@ -2836,112 +2019,107 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2836
2019
|
if (!plugin.manifest.config) {
|
|
2837
2020
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2838
2021
|
}
|
|
2022
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, config.platform);
|
|
2839
2023
|
const stores = await readConfigStoresForTui({
|
|
2840
2024
|
sourceId: plugin.sourceId,
|
|
2841
2025
|
sourceIdentity: plugin.sourceIdentity,
|
|
2842
2026
|
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2843
2027
|
pluginName: plugin.manifest.name,
|
|
2844
|
-
schema:
|
|
2845
|
-
|
|
2028
|
+
schema: definition.fields,
|
|
2029
|
+
platform: config.platform,
|
|
2846
2030
|
signal,
|
|
2847
2031
|
});
|
|
2848
2032
|
const userStore = stores.user;
|
|
2849
|
-
const projectStore = stores.project;
|
|
2850
2033
|
const read = await resolveConfigRead({
|
|
2851
2034
|
pluginName: plugin.manifest.name,
|
|
2852
2035
|
plugin,
|
|
2853
|
-
scope:
|
|
2036
|
+
scope: config.view === 'effective' ? 'effective' : 'user',
|
|
2037
|
+
platform: config.platform,
|
|
2038
|
+
context: config.context ?? 'user',
|
|
2854
2039
|
cwd: context.cwd,
|
|
2855
2040
|
env: context.env,
|
|
2856
2041
|
});
|
|
2857
|
-
const effectiveRead =
|
|
2042
|
+
const effectiveRead = config.view === 'effective' ? read : await resolveConfigRead({
|
|
2858
2043
|
pluginName: plugin.manifest.name,
|
|
2859
2044
|
plugin,
|
|
2860
2045
|
scope: 'effective',
|
|
2046
|
+
platform: config.platform,
|
|
2047
|
+
context: config.context ?? 'user',
|
|
2861
2048
|
cwd: context.cwd,
|
|
2862
2049
|
env: context.env,
|
|
2863
2050
|
});
|
|
2864
|
-
const layerReads = layer.scope === 'effective' ? undefined : {
|
|
2865
|
-
user: layer.scope === 'user' ? read : await resolveConfigRead({
|
|
2866
|
-
pluginName: plugin.manifest.name,
|
|
2867
|
-
plugin,
|
|
2868
|
-
scope: 'user',
|
|
2869
|
-
cwd: context.cwd,
|
|
2870
|
-
env: context.env,
|
|
2871
|
-
}),
|
|
2872
|
-
...(layer.scope === 'user' ? {} : { project: read }),
|
|
2873
|
-
};
|
|
2874
2051
|
const effectiveRaw = await resolveEffectivePluginConfig({
|
|
2875
2052
|
sourceId: plugin.sourceId,
|
|
2876
2053
|
sourceIdentity: plugin.sourceIdentity,
|
|
2877
2054
|
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2878
2055
|
pluginName: plugin.manifest.name,
|
|
2879
2056
|
manifest: plugin.manifest,
|
|
2880
|
-
|
|
2057
|
+
platform: config.platform,
|
|
2058
|
+
context: config.context ?? 'user',
|
|
2881
2059
|
cwd: context.cwd,
|
|
2882
2060
|
env: context.env,
|
|
2883
2061
|
});
|
|
2884
|
-
const layerStore = layer.scope === 'effective'
|
|
2885
|
-
? undefined
|
|
2886
|
-
: layer.scope === 'user' ? userStore : projectStore;
|
|
2887
2062
|
const page = buildConfigPage({
|
|
2888
2063
|
pluginId,
|
|
2889
|
-
|
|
2064
|
+
context: config.context ?? 'user',
|
|
2065
|
+
schema: definition,
|
|
2890
2066
|
read,
|
|
2891
2067
|
effectiveRead,
|
|
2892
|
-
...(
|
|
2893
|
-
...(layerStore ? { rawValues: layerStore.values } : {}),
|
|
2068
|
+
...(config.view === 'effective' ? {} : { rawValues: userStore.values }),
|
|
2894
2069
|
rawLayerValues: {
|
|
2895
|
-
|
|
2896
|
-
...(
|
|
2070
|
+
common: userStore.common ?? {},
|
|
2071
|
+
...(userStore.platforms ?? {}),
|
|
2897
2072
|
},
|
|
2898
2073
|
effectiveRawValues: effectiveRaw.values,
|
|
2899
|
-
sharedNativeRefreshKeys: new Set((plugin.manifest.artifacts?.targets ?? []).flatMap((target) => target.target === 'codex.skill'
|
|
2074
|
+
sharedNativeRefreshKeys: new Set((plugin.manifest.artifacts?.targets ?? []).flatMap((target) => (target.target === 'codex.skill' || target.target === 'claude.skill')
|
|
2075
|
+
&& (!config.platform || target.platform === config.platform)
|
|
2900
2076
|
? target.refreshKeys
|
|
2901
2077
|
: [])),
|
|
2902
2078
|
});
|
|
2903
2079
|
return { ...page, editEvidence: {
|
|
2080
|
+
platform: config.platform,
|
|
2081
|
+
context: config.context ?? 'user',
|
|
2904
2082
|
sourceIdentity: source.sourceIdentity,
|
|
2905
|
-
schemaIdentity: source.manifestDigest,
|
|
2083
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, config.platform),
|
|
2906
2084
|
storeDigests: {
|
|
2907
2085
|
user: digest({ exists: userStore.exists, rawDigest: userStore.rawDigest }),
|
|
2908
|
-
...(projectStore ? { project: digest({ exists: projectStore.exists, rawDigest: projectStore.rawDigest }) } : {}),
|
|
2909
2086
|
},
|
|
2910
2087
|
} };
|
|
2911
2088
|
},
|
|
2912
2089
|
});
|
|
2913
2090
|
},
|
|
2914
|
-
async getConfigReferenceContext(pluginId,
|
|
2091
|
+
async getConfigReferenceContext(pluginId, config, draft, signal) {
|
|
2092
|
+
const workContext = await resolveWorkContext({ cwd: context.cwd, context: config.context ?? 'user' });
|
|
2915
2093
|
const packageInfo = await readCliPackageInfo();
|
|
2916
2094
|
return withResolvedCatalogPlugin({
|
|
2917
2095
|
selector: pluginId, cliVersion: packageInfo.version, env: context.env, signal,
|
|
2918
2096
|
...(sessionContextForPlugin(pluginId) ? { operationContext: sessionContextForPlugin(pluginId) } : {}),
|
|
2919
2097
|
consume: async ({ plugin }) => {
|
|
2920
|
-
|
|
2921
|
-
if (!definition)
|
|
2098
|
+
if (!plugin.manifest.config)
|
|
2922
2099
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2100
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, config.platform);
|
|
2923
2101
|
const scalars = Object.values(definition.fields).flatMap(configReferenceScalars);
|
|
2924
2102
|
if (!scalars.length)
|
|
2925
|
-
return { selections: {} };
|
|
2103
|
+
return { platform: config.platform, selections: {} };
|
|
2926
2104
|
const stores = await readConfigStoresForTui({
|
|
2927
2105
|
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
2928
2106
|
logicalIdentity: plugin.catalogLogicalIdentity, pluginName: plugin.manifest.name,
|
|
2929
|
-
schema: definition.fields,
|
|
2107
|
+
schema: definition.fields, platform: config.platform, signal,
|
|
2930
2108
|
});
|
|
2931
|
-
const edited =
|
|
2109
|
+
const edited = stores.user;
|
|
2932
2110
|
const values = { ...edited.values };
|
|
2933
2111
|
for (const key of draft.removes)
|
|
2934
2112
|
delete values[key];
|
|
2935
2113
|
Object.assign(values, draft.updates);
|
|
2936
|
-
const candidateStores = {
|
|
2114
|
+
const candidateStores = { user: replacePluginConfigStoreLayer(edited, values, config.platform) };
|
|
2937
2115
|
const references = await resolvePluginConfigReferenceContext({
|
|
2938
2116
|
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
2939
2117
|
logicalIdentity: plugin.catalogLogicalIdentity, manifest: plugin.manifest,
|
|
2940
|
-
scope, cwd: context.cwd, env: context.env,
|
|
2118
|
+
scope: 'user', context: config.context ?? 'user', platform: config.platform, cwd: context.cwd, env: context.env,
|
|
2941
2119
|
});
|
|
2942
2120
|
const effective = await resolveEffectivePluginConfigFromStores({
|
|
2943
|
-
manifest: plugin.manifest,
|
|
2944
|
-
projectRoot:
|
|
2121
|
+
manifest: plugin.manifest, context: config.context ?? 'user', platform: config.platform,
|
|
2122
|
+
projectRoot: workContext.projectRoot ?? '', stores: candidateStores,
|
|
2945
2123
|
referenceContext: references.context,
|
|
2946
2124
|
});
|
|
2947
2125
|
// Keep invalid raw sources for Core validation; effective.values omits them.
|
|
@@ -2952,17 +2130,17 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2952
2130
|
const source = effective.sources[key];
|
|
2953
2131
|
if (source === 'default')
|
|
2954
2132
|
candidate[key] = definition.fields[key].default;
|
|
2955
|
-
else if (source === '
|
|
2956
|
-
candidate[key] = candidateStores.user.
|
|
2957
|
-
else if (source === '
|
|
2958
|
-
candidate[key] = candidateStores.
|
|
2133
|
+
else if (source === 'common')
|
|
2134
|
+
candidate[key] = candidateStores.user.common?.[key];
|
|
2135
|
+
else if (source === 'platform' && config.platform)
|
|
2136
|
+
candidate[key] = candidateStores.user.platforms?.[config.platform]?.[key];
|
|
2959
2137
|
}
|
|
2960
|
-
const result = { selections: {} };
|
|
2138
|
+
const result = { platform: config.platform, selections: {} };
|
|
2961
2139
|
for (const scalar of scalars) {
|
|
2962
2140
|
const ref = scalar.valueRef;
|
|
2963
2141
|
const selection = getPluginConfigReferenceChoices(definition, scalar, candidate, references.context);
|
|
2964
2142
|
const entries = ref.plugin ? references.context.plugins[ref.plugin]?.values?.[ref.field] : candidate[ref.field];
|
|
2965
|
-
result.selections[configReferenceKey(scalar)] = {
|
|
2143
|
+
result.selections[configReferenceKey(scalar, config.platform)] = {
|
|
2966
2144
|
...selection, source: (ref.plugin
|
|
2967
2145
|
? references.evidence.find((entry) => entry.plugin === ref.plugin)?.sources[ref.field]
|
|
2968
2146
|
: effective.sources[ref.field]) ?? 'missing',
|
|
@@ -2974,7 +2152,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2974
2152
|
},
|
|
2975
2153
|
});
|
|
2976
2154
|
},
|
|
2977
|
-
async getConfigResetAvailability(pluginId,
|
|
2155
|
+
async getConfigResetAvailability(pluginId, config, signal) {
|
|
2156
|
+
if (config.context === 'project')
|
|
2157
|
+
await resolveWorkContext({ cwd: context.cwd, context: 'project' });
|
|
2978
2158
|
const parsed = parsePluginId(pluginId);
|
|
2979
2159
|
if (!parsed.sourceId || !parsed.pluginName) {
|
|
2980
2160
|
throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
|
|
@@ -3007,7 +2187,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3007
2187
|
const target = await inspectPluginConfigResetTarget({
|
|
3008
2188
|
sourceId: parsed.sourceId,
|
|
3009
2189
|
pluginName: parsed.pluginName,
|
|
3010
|
-
scope:
|
|
2190
|
+
scope: 'user',
|
|
3011
2191
|
cwd: context.cwd,
|
|
3012
2192
|
env: context.env,
|
|
3013
2193
|
});
|
|
@@ -3018,7 +2198,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3018
2198
|
...(sourceIdentity ? { sourceIdentity } : {}),
|
|
3019
2199
|
...(logicalIdentity ? { logicalIdentity } : {}),
|
|
3020
2200
|
pluginName: parsed.pluginName,
|
|
3021
|
-
scope:
|
|
2201
|
+
scope: 'user',
|
|
2202
|
+
platform: config.platform,
|
|
3022
2203
|
cwd: context.cwd,
|
|
3023
2204
|
env: context.env,
|
|
3024
2205
|
});
|
|
@@ -3045,6 +2226,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3045
2226
|
};
|
|
3046
2227
|
},
|
|
3047
2228
|
async executeIntent(intent, expectedEvidence, signal) {
|
|
2229
|
+
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
2230
|
+
|| intent.kind === 'pluginWriteTask') {
|
|
2231
|
+
throw new TuiOperationBlockedError('useScopedRecovery', '请使用统一范围恢复任务。');
|
|
2232
|
+
}
|
|
3048
2233
|
if (intent.kind === 'catalogStorePrune') {
|
|
3049
2234
|
signal?.throwIfAborted();
|
|
3050
2235
|
if (!expectedEvidence.catalogStore) {
|
|
@@ -3331,351 +2516,13 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3331
2516
|
}
|
|
3332
2517
|
throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
|
|
3333
2518
|
}
|
|
3334
|
-
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
3335
|
-
|| intent.kind === 'pluginWriteTask'
|
|
3336
|
-
|| intent.kind === 'configSave' && (intent.apply || intent.retryApply)
|
|
3337
|
-
|| intent.kind === 'configSaveBatch' && intent.apply) {
|
|
3338
|
-
await prepareNativeCliExecution({ env: context.env, runner: context.nativeCliRunner });
|
|
3339
|
-
}
|
|
3340
|
-
const codexObservationSession = intent.kind === 'pluginWrite'
|
|
3341
|
-
|| intent.kind === 'pluginWriteBatch'
|
|
3342
|
-
|| intent.kind === 'pluginWriteTask'
|
|
3343
|
-
? await createCodexNativeObservationSession({
|
|
3344
|
-
exactCwd: context.cwd,
|
|
3345
|
-
env: context.env,
|
|
3346
|
-
runner: context.nativeCliRunner,
|
|
3347
|
-
manager: codexManagerByEvidence.get(expectedEvidence),
|
|
3348
|
-
})
|
|
3349
|
-
: undefined;
|
|
3350
2519
|
return withAwHomeMutationLock({
|
|
3351
2520
|
env: context.env,
|
|
3352
2521
|
action: async (awHomeMutationToken) => {
|
|
3353
2522
|
await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
|
|
3354
2523
|
const operationInventory = createOperationInventoryReader();
|
|
3355
|
-
const
|
|
3356
|
-
|
|
3357
|
-
try {
|
|
3358
|
-
currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true, undefined, codexObservationSession, intent.kind === 'pluginWriteBatch'
|
|
3359
|
-
? expectedEvidence
|
|
3360
|
-
: undefined);
|
|
3361
|
-
}
|
|
3362
|
-
catch (error) {
|
|
3363
|
-
const code = error instanceof TuiOperationBlockedError ? error.code : undefined;
|
|
3364
|
-
if (intent.kind === 'pluginWriteBatch'
|
|
3365
|
-
&& (intent.selection.kind === 'currentProjectIntentRoots'
|
|
3366
|
-
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3367
|
-
&& error instanceof TuiOperationBlockedError) {
|
|
3368
|
-
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3369
|
-
}
|
|
3370
|
-
if (intent.kind === 'pluginWriteTask'
|
|
3371
|
-
&& (code === 'taskHasBlockedTargets' || code === 'taskNoExecutableTargets')) {
|
|
3372
|
-
throw operationReviewStale('确认后任务可执行目标发生变化。', error instanceof TuiOperationBlockedError ? error.diagnostics : undefined);
|
|
3373
|
-
}
|
|
3374
|
-
throw error;
|
|
3375
|
-
}
|
|
3376
|
-
if (intent.kind === 'pluginWriteBatch') {
|
|
3377
|
-
const expectedBatch = expectedEvidence;
|
|
3378
|
-
const currentBatch = currentEvidence;
|
|
3379
|
-
if ((intent.selection.kind === 'currentProjectIntentRoots'
|
|
3380
|
-
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3381
|
-
&& (!expectedBatch.projectIntentDigest
|
|
3382
|
-
|| expectedBatch.projectIntentDigest !== currentBatch.projectIntentDigest)) {
|
|
3383
|
-
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3384
|
-
}
|
|
3385
|
-
const reviewedGroups = expectedBatch.groups.map((group) => ({
|
|
3386
|
-
groupId: group.groupId,
|
|
3387
|
-
sourceId: group.sourceId,
|
|
3388
|
-
platform: group.platform,
|
|
3389
|
-
scope: group.scope,
|
|
3390
|
-
requestedPluginIds: group.requestedPluginIds,
|
|
3391
|
-
executable: group.executable,
|
|
3392
|
-
planFingerprint: group.preview.operationPlan
|
|
3393
|
-
? intent.action === 'cleanup'
|
|
3394
|
-
? group.preview.operationPlan.planFingerprint
|
|
3395
|
-
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3396
|
-
: undefined,
|
|
3397
|
-
}));
|
|
3398
|
-
const currentGroups = currentBatch.groups.map((group) => ({
|
|
3399
|
-
groupId: group.groupId,
|
|
3400
|
-
sourceId: group.sourceId,
|
|
3401
|
-
platform: group.platform,
|
|
3402
|
-
scope: group.scope,
|
|
3403
|
-
requestedPluginIds: group.requestedPluginIds,
|
|
3404
|
-
executable: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3405
|
-
?.executable === false ? false : group.executable,
|
|
3406
|
-
planFingerprint: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3407
|
-
?.executable === false
|
|
3408
|
-
? undefined
|
|
3409
|
-
: group.preview.operationPlan
|
|
3410
|
-
? intent.action === 'cleanup'
|
|
3411
|
-
? group.preview.operationPlan.planFingerprint
|
|
3412
|
-
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3413
|
-
: undefined,
|
|
3414
|
-
}));
|
|
3415
|
-
if (expectedBatch.catalog.configDigest !== currentBatch.catalog.configDigest
|
|
3416
|
-
|| JSON.stringify(expectedBatch.requestedPluginIds)
|
|
3417
|
-
!== JSON.stringify(currentBatch.requestedPluginIds)
|
|
3418
|
-
|| JSON.stringify(reviewedGroups) !== JSON.stringify(currentGroups)) {
|
|
3419
|
-
throw operationReviewStale('确认后批量目标或写入计划发生变化。');
|
|
3420
|
-
}
|
|
3421
|
-
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3422
|
-
const groupResults = [];
|
|
3423
|
-
const completedTargetKeys = new Set();
|
|
3424
|
-
const expectedIntentDigests = new Map();
|
|
3425
|
-
for (const group of expectedBatch.groups) {
|
|
3426
|
-
for (const intentDigest of group.preview.operationPlan?.evidence.intentDigests ?? []) {
|
|
3427
|
-
expectedIntentDigests.set(intentDigest.scope, intentDigest.digest);
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
let executedGroupCount = 0;
|
|
3431
|
-
const appendStoppedGroups = (startIndex, staleMessage) => {
|
|
3432
|
-
for (const [offset, stopped] of expectedBatch.groups.slice(startIndex).entries()) {
|
|
3433
|
-
groupResults.push({
|
|
3434
|
-
groupId: stopped.groupId,
|
|
3435
|
-
sourceId: stopped.sourceId,
|
|
3436
|
-
platform: stopped.platform,
|
|
3437
|
-
scope: stopped.scope,
|
|
3438
|
-
requestedPluginIds: stopped.requestedPluginIds,
|
|
3439
|
-
result: !stopped.executable ? stopped.preview : failedWriteCommandResult(intent.action, stopped.requestedPluginIds, { cwd: context.cwd, platform: stopped.platform, scope: stopped.scope }, offset === 0
|
|
3440
|
-
? new TuiOperationBlockedError('operationReviewStale', staleMessage)
|
|
3441
|
-
: new TuiOperationBlockedError('batchNotStarted', '前一个批量组的写入计划已变化,因此这个组没有开始。')),
|
|
3442
|
-
});
|
|
3443
|
-
}
|
|
3444
|
-
};
|
|
3445
|
-
for (const [groupIndex, reviewed] of expectedBatch.groups.entries()) {
|
|
3446
|
-
const current = currentBatch.groups.find((group) => group.groupId === reviewed.groupId);
|
|
3447
|
-
if (!reviewed.executable) {
|
|
3448
|
-
groupResults.push({
|
|
3449
|
-
groupId: reviewed.groupId,
|
|
3450
|
-
sourceId: reviewed.sourceId,
|
|
3451
|
-
platform: reviewed.platform,
|
|
3452
|
-
scope: reviewed.scope,
|
|
3453
|
-
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3454
|
-
result: reviewed.preview,
|
|
3455
|
-
});
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
const groupInventory = groupResults.length === 0
|
|
3459
|
-
? operationInventory
|
|
3460
|
-
: createOperationInventoryReader();
|
|
3461
|
-
let executionPreview = current.preview;
|
|
3462
|
-
if (completedTargetKeys.size > 0
|
|
3463
|
-
|| (intent.action === 'cleanup' && executedGroupCount > 0)) {
|
|
3464
|
-
executionPreview = await runBatchWriteGroup({
|
|
3465
|
-
intent,
|
|
3466
|
-
group: current,
|
|
3467
|
-
cwd: context.cwd,
|
|
3468
|
-
runtime: {
|
|
3469
|
-
...context,
|
|
3470
|
-
awHomeMutationToken,
|
|
3471
|
-
inventoryCatalogSourceReferences: groupInventory,
|
|
3472
|
-
...(intent.action === 'cleanup' ? {} : {
|
|
3473
|
-
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3474
|
-
}),
|
|
3475
|
-
previewOperationPlan: true,
|
|
3476
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3477
|
-
},
|
|
3478
|
-
});
|
|
3479
|
-
const cleanupIntentDigestChanged = intent.action === 'cleanup'
|
|
3480
|
-
&& executionPreview.operationPlan?.evidence.intentDigests.find((entry) => entry.scope === current.scope)?.digest !== expectedIntentDigests.get(current.scope);
|
|
3481
|
-
if (cleanupIntentDigestChanged
|
|
3482
|
-
|| !reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3483
|
-
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3484
|
-
appendStoppedGroups(groupIndex, '前序批量结果改变了这个组的实际写入计划。');
|
|
3485
|
-
break;
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
|
-
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3489
|
-
const result = await runBatchWriteGroup({
|
|
3490
|
-
intent,
|
|
3491
|
-
group: current,
|
|
3492
|
-
cwd: context.cwd,
|
|
3493
|
-
runtime: {
|
|
3494
|
-
...context,
|
|
3495
|
-
awHomeMutationToken,
|
|
3496
|
-
preApplyConfirmed: true,
|
|
3497
|
-
managedCommandProducerCache,
|
|
3498
|
-
inventoryCatalogSourceReferences: groupInventory,
|
|
3499
|
-
...(intent.action === 'cleanup' ? {} : {
|
|
3500
|
-
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3501
|
-
}),
|
|
3502
|
-
...(executionPreview.operationPlan ? {
|
|
3503
|
-
expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint,
|
|
3504
|
-
} : {}),
|
|
3505
|
-
...(requiresManagedCodex ? {
|
|
3506
|
-
managedCodexAuthorizer: async () => true,
|
|
3507
|
-
} : {}),
|
|
3508
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3509
|
-
},
|
|
3510
|
-
});
|
|
3511
|
-
if (writeReviewIsStale(result)) {
|
|
3512
|
-
if (executedGroupCount === 0)
|
|
3513
|
-
throwIfWriteReviewStale(result);
|
|
3514
|
-
appendStoppedGroups(groupIndex, '这个组的实际写入计划发生变化。');
|
|
3515
|
-
break;
|
|
3516
|
-
}
|
|
3517
|
-
if (intent.action === 'cleanup') {
|
|
3518
|
-
for (const change of result.operationPlan?.intentChanges ?? []) {
|
|
3519
|
-
expectedIntentDigests.set(change.scope, change.afterDigest);
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
executedGroupCount += 1;
|
|
3523
|
-
for (const target of result.targetResults) {
|
|
3524
|
-
if (target.state === 'completed') {
|
|
3525
|
-
completedTargetKeys.add([
|
|
3526
|
-
target.target.pluginId,
|
|
3527
|
-
target.target.platform,
|
|
3528
|
-
target.target.scope,
|
|
3529
|
-
].join('\0'));
|
|
3530
|
-
}
|
|
3531
|
-
}
|
|
3532
|
-
groupResults.push({
|
|
3533
|
-
groupId: reviewed.groupId,
|
|
3534
|
-
sourceId: reviewed.sourceId,
|
|
3535
|
-
platform: reviewed.platform,
|
|
3536
|
-
scope: reviewed.scope,
|
|
3537
|
-
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3538
|
-
result,
|
|
3539
|
-
});
|
|
3540
|
-
}
|
|
3541
|
-
const merged = mergeWriteCommandResults(groupResults.map((group) => group.result));
|
|
3542
|
-
return {
|
|
3543
|
-
...merged,
|
|
3544
|
-
requestedPluginIds: expectedBatch.requestedPluginIds,
|
|
3545
|
-
groupResults,
|
|
3546
|
-
};
|
|
3547
|
-
}
|
|
3548
|
-
if (intent.kind === 'pluginWriteTask') {
|
|
3549
|
-
const expectedTask = expectedEvidence;
|
|
3550
|
-
const currentTask = currentEvidence;
|
|
3551
|
-
if (!operationEvidenceMatches(expectedTask, currentTask)) {
|
|
3552
|
-
throw operationReviewStale('确认后任务目标、根因、版本或写入计划发生变化。');
|
|
3553
|
-
}
|
|
3554
|
-
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3555
|
-
const operationContexts = taskCatalogOperationsByEvidence.get(currentEvidence)
|
|
3556
|
-
?? new Map();
|
|
3557
|
-
const sourceResults = [];
|
|
3558
|
-
const completedTargetKeys = new Set();
|
|
3559
|
-
const preApplyExecutionCache = new Map();
|
|
3560
|
-
const commandTargets = (source) => source.requestedTargets.map((target) => {
|
|
3561
|
-
const direct = source.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
3562
|
-
return {
|
|
3563
|
-
pluginId: target.pluginId,
|
|
3564
|
-
platform: target.platform,
|
|
3565
|
-
scope: target.scope,
|
|
3566
|
-
method: direct.some((cause) => cause.method === 'reinstall')
|
|
3567
|
-
? 'reinstall' : 'sync',
|
|
3568
|
-
};
|
|
3569
|
-
});
|
|
3570
|
-
const repairTargets = (source) => [...new Map(source.causes.map((cause) => [
|
|
3571
|
-
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
3572
|
-
{ ...cause.repairTarget, method: cause.method },
|
|
3573
|
-
])).values()];
|
|
3574
|
-
const stoppedResult = (source, error) => mergeWriteCommandResults(source.requestedTargets.map((target) => failedWriteCommandResult('sync', [target.pluginId], {
|
|
3575
|
-
cwd: context.cwd,
|
|
3576
|
-
platform: target.platform,
|
|
3577
|
-
scope: target.scope,
|
|
3578
|
-
}, error)));
|
|
3579
|
-
const appendStoppedSources = (startIndex, message) => {
|
|
3580
|
-
for (const [offset, source] of expectedTask.taskPlan.sources.slice(startIndex).entries()) {
|
|
3581
|
-
sourceResults.push({
|
|
3582
|
-
sourceId: source.sourceId,
|
|
3583
|
-
sourceIdentity: source.sourceIdentity,
|
|
3584
|
-
requestedTargets: source.requestedTargets,
|
|
3585
|
-
result: stoppedResult(source, offset === 0
|
|
3586
|
-
? new TuiOperationBlockedError('operationReviewStale', message)
|
|
3587
|
-
: new TuiOperationBlockedError('taskNotStarted', '前一个来源计划已变化,因此这个来源没有开始。')),
|
|
3588
|
-
});
|
|
3589
|
-
}
|
|
3590
|
-
};
|
|
3591
|
-
for (const [sourceIndex, reviewed] of expectedTask.taskPlan.sources.entries()) {
|
|
3592
|
-
const current = currentTask.taskPlan.sources.find((source) => source.sourceId === reviewed.sourceId);
|
|
3593
|
-
const catalogOperation = operationContexts.get(reviewed.sourceId);
|
|
3594
|
-
if (!current || !catalogOperation || current.sourceIdentity !== reviewed.sourceIdentity) {
|
|
3595
|
-
if (sourceResults.length === 0) {
|
|
3596
|
-
throw operationReviewStale('确认后任务来源身份发生变化。');
|
|
3597
|
-
}
|
|
3598
|
-
appendStoppedSources(sourceIndex, '确认后任务来源身份发生变化。');
|
|
3599
|
-
break;
|
|
3600
|
-
}
|
|
3601
|
-
const sourceInventory = sourceResults.length === 0
|
|
3602
|
-
? operationInventory : createOperationInventoryReader();
|
|
3603
|
-
let executionPreview = current.preview;
|
|
3604
|
-
if (completedTargetKeys.size > 0) {
|
|
3605
|
-
executionPreview = await taskWriteCommand(commandTargets(current), {
|
|
3606
|
-
cwd: context.cwd,
|
|
3607
|
-
repairTargets: repairTargets(current),
|
|
3608
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3609
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3610
|
-
}, {
|
|
3611
|
-
...context,
|
|
3612
|
-
awHomeMutationToken,
|
|
3613
|
-
catalogOperationContext: catalogOperation,
|
|
3614
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3615
|
-
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3616
|
-
inventoryCatalogSourceReferences: sourceInventory,
|
|
3617
|
-
previewOperationPlan: true,
|
|
3618
|
-
preApplyExecutionCache,
|
|
3619
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3620
|
-
});
|
|
3621
|
-
if (!reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3622
|
-
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3623
|
-
appendStoppedSources(sourceIndex, '前序结果改变了这个来源的实际写入计划。');
|
|
3624
|
-
break;
|
|
3625
|
-
}
|
|
3626
|
-
}
|
|
3627
|
-
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3628
|
-
const result = await taskWriteCommand(commandTargets(current), {
|
|
3629
|
-
cwd: context.cwd,
|
|
3630
|
-
repairTargets: repairTargets(current),
|
|
3631
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3632
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3633
|
-
}, {
|
|
3634
|
-
...context,
|
|
3635
|
-
awHomeMutationToken,
|
|
3636
|
-
catalogOperationContext: catalogOperation,
|
|
3637
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3638
|
-
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3639
|
-
inventoryCatalogSourceReferences: sourceInventory,
|
|
3640
|
-
preApplyConfirmed: true,
|
|
3641
|
-
preApplyExecutionCache,
|
|
3642
|
-
managedCommandProducerCache,
|
|
3643
|
-
...(executionPreview.operationPlan
|
|
3644
|
-
? { expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint } : {}),
|
|
3645
|
-
...(requiresManagedCodex ? { managedCodexAuthorizer: async () => true } : {}),
|
|
3646
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3647
|
-
});
|
|
3648
|
-
if (writeReviewIsStale(result)) {
|
|
3649
|
-
if (sourceResults.length === 0)
|
|
3650
|
-
throwIfWriteReviewStale(result);
|
|
3651
|
-
appendStoppedSources(sourceIndex, '这个来源的实际写入计划发生变化。');
|
|
3652
|
-
break;
|
|
3653
|
-
}
|
|
3654
|
-
for (const target of result.targetResults) {
|
|
3655
|
-
if (target.state === 'completed') {
|
|
3656
|
-
completedTargetKeys.add([
|
|
3657
|
-
target.target.pluginId,
|
|
3658
|
-
target.target.platform,
|
|
3659
|
-
target.target.scope,
|
|
3660
|
-
].join('\0'));
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
sourceResults.push({
|
|
3664
|
-
sourceId: reviewed.sourceId,
|
|
3665
|
-
sourceIdentity: reviewed.sourceIdentity,
|
|
3666
|
-
requestedTargets: reviewed.requestedTargets,
|
|
3667
|
-
result,
|
|
3668
|
-
});
|
|
3669
|
-
}
|
|
3670
|
-
const merged = mergeWriteCommandResults(sourceResults.map((source) => source.result));
|
|
3671
|
-
return {
|
|
3672
|
-
...merged,
|
|
3673
|
-
taskPlan: expectedTask.taskPlan,
|
|
3674
|
-
sourceResults,
|
|
3675
|
-
};
|
|
3676
|
-
}
|
|
3677
|
-
if (intent.kind !== 'pluginWrite'
|
|
3678
|
-
&& !operationEvidenceMatches(expectedEvidence, currentEvidence)) {
|
|
2524
|
+
const currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true);
|
|
2525
|
+
if (!operationEvidenceMatches(expectedEvidence, currentEvidence)) {
|
|
3679
2526
|
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
3680
2527
|
}
|
|
3681
2528
|
const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
|
|
@@ -3684,76 +2531,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3684
2531
|
throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
|
|
3685
2532
|
}
|
|
3686
2533
|
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3687
|
-
if (intent.kind === 'pluginWrite') {
|
|
3688
|
-
const options = {
|
|
3689
|
-
cwd: context.cwd,
|
|
3690
|
-
platform: intent.platform,
|
|
3691
|
-
scope: intent.scope,
|
|
3692
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3693
|
-
? {}
|
|
3694
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3695
|
-
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
3696
|
-
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
3697
|
-
: {}),
|
|
3698
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3699
|
-
? { allowManagedCodexInstall: true }
|
|
3700
|
-
: {}),
|
|
3701
|
-
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
3702
|
-
};
|
|
3703
|
-
const command = {
|
|
3704
|
-
install: installWriteCommand,
|
|
3705
|
-
refreshAndSync: syncWriteCommand,
|
|
3706
|
-
reinstall: reinstallWriteCommand,
|
|
3707
|
-
uninstall: uninstallWriteCommand,
|
|
3708
|
-
cleanup: cleanupWriteCommand,
|
|
3709
|
-
sync: syncWriteCommand,
|
|
3710
|
-
}[intent.action];
|
|
3711
|
-
if (intent.action === 'cleanup') {
|
|
3712
|
-
if (!expectedEvidence.operationPlan || !currentEvidence.operationPlan
|
|
3713
|
-
|| expectedEvidence.operationPlan.planFingerprint
|
|
3714
|
-
!== currentEvidence.operationPlan.planFingerprint) {
|
|
3715
|
-
throw operationReviewStale('确认后清理计划发生变化。');
|
|
3716
|
-
}
|
|
3717
|
-
const result = await command([intent.pluginId], options, {
|
|
3718
|
-
...context,
|
|
3719
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3720
|
-
awHomeMutationToken,
|
|
3721
|
-
preApplyConfirmed: true,
|
|
3722
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
3723
|
-
expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint,
|
|
3724
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3725
|
-
? { managedCodexAuthorizer: async () => true }
|
|
3726
|
-
: {}),
|
|
3727
|
-
});
|
|
3728
|
-
throwIfWriteReviewStale(result);
|
|
3729
|
-
return result;
|
|
3730
|
-
}
|
|
3731
|
-
const currentSource = currentEvidence.source;
|
|
3732
|
-
if (!expectedEvidence.source || !currentSource) {
|
|
3733
|
-
throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
|
|
3734
|
-
}
|
|
3735
|
-
const result = await command([intent.pluginId], options, {
|
|
3736
|
-
...context,
|
|
3737
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3738
|
-
awHomeMutationToken,
|
|
3739
|
-
preApplyConfirmed: true,
|
|
3740
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
3741
|
-
...(expectedEvidence.operationPlan
|
|
3742
|
-
? { expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint }
|
|
3743
|
-
: {}),
|
|
3744
|
-
expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
|
|
3745
|
-
...(catalogOperation ? {
|
|
3746
|
-
catalogOperationContext: catalogOperation,
|
|
3747
|
-
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3748
|
-
} : {}),
|
|
3749
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3750
|
-
? { managedCodexAuthorizer: async () => true }
|
|
3751
|
-
: {}),
|
|
3752
|
-
reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual),
|
|
3753
|
-
});
|
|
3754
|
-
throwIfWriteReviewStale(result);
|
|
3755
|
-
return result;
|
|
3756
|
-
}
|
|
3757
2534
|
if (intent.kind === 'configSaveBatch') {
|
|
3758
2535
|
assertConfigSaveBatchIntent(intent);
|
|
3759
2536
|
const reviewedBatch = expectedEvidence.configBatch;
|
|
@@ -3767,7 +2544,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3767
2544
|
const packageInfo = await readCliPackageInfo();
|
|
3768
2545
|
for (const group of intent.groups) {
|
|
3769
2546
|
const groupIntent = configSaveBatchGroupIntent(intent, group);
|
|
3770
|
-
const reviewedGroup = reviewedBatch.groups.find((item) => item.
|
|
2547
|
+
const reviewedGroup = reviewedBatch.groups.find((item) => item.platform === group.platform);
|
|
3771
2548
|
if (!reviewedGroup) {
|
|
3772
2549
|
throw operationReviewStale('确认信息缺少设置层基线。');
|
|
3773
2550
|
}
|
|
@@ -3784,7 +2561,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3784
2561
|
const freshLayerStoreDigest = await configLayerStoreDigest({
|
|
3785
2562
|
source: freshSource,
|
|
3786
2563
|
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
3787
|
-
scope: group.
|
|
2564
|
+
scope: 'user', platform: group.platform, profileOnly: true,
|
|
3788
2565
|
cwd: context.cwd,
|
|
3789
2566
|
env: context.env,
|
|
3790
2567
|
});
|
|
@@ -3816,7 +2593,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3816
2593
|
operationContext: groupCatalogOperation,
|
|
3817
2594
|
}) : undefined;
|
|
3818
2595
|
const applyTargets = intent.apply
|
|
3819
|
-
? freshConfig.affectedTargets.map((target) => ({
|
|
2596
|
+
? freshConfig.affectedTargets.filter((target) => target.platform === 'codex' || target.platform === 'claude').map((target) => ({
|
|
3820
2597
|
pluginName: target.pluginName ?? plugin.manifest.name,
|
|
3821
2598
|
platform: target.platform,
|
|
3822
2599
|
scope: target.scope,
|
|
@@ -3825,7 +2602,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3825
2602
|
return applyPluginConfigWrite({
|
|
3826
2603
|
plugin,
|
|
3827
2604
|
pluginName: plugin.manifest.name,
|
|
3828
|
-
scope: group.
|
|
2605
|
+
scope: 'user', platform: group.platform, context: intent.context ?? 'user',
|
|
2606
|
+
apply: intent.apply,
|
|
3829
2607
|
cwd: context.cwd,
|
|
3830
2608
|
env: context.env,
|
|
3831
2609
|
nativeCliRunner: context.nativeCliRunner,
|
|
@@ -3845,7 +2623,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3845
2623
|
} : {}),
|
|
3846
2624
|
},
|
|
3847
2625
|
awHomeMutationToken,
|
|
3848
|
-
applyTargets,
|
|
2626
|
+
...(applyTargets.length ? { applyTargets } : {}),
|
|
3849
2627
|
request: { updates: group.updates, removeKeys: group.removeKeys },
|
|
3850
2628
|
sharedNativeApplyConfirmed: true,
|
|
3851
2629
|
preApplyConfirmed: true,
|
|
@@ -3862,7 +2640,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3862
2640
|
? 'notNeeded'
|
|
3863
2641
|
: 'failed';
|
|
3864
2642
|
results.push({
|
|
3865
|
-
scope: group.
|
|
2643
|
+
scope: 'user', platform: group.platform,
|
|
3866
2644
|
status: applyStatus === 'failed' ? 'failed' : 'succeeded',
|
|
3867
2645
|
configSaved,
|
|
3868
2646
|
applyRequested: intent.apply,
|
|
@@ -3883,7 +2661,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3883
2661
|
? error.code
|
|
3884
2662
|
: configSaved ? 'runtimeApplyFailed' : 'configWriteFailed';
|
|
3885
2663
|
results.push({
|
|
3886
|
-
scope: group.
|
|
2664
|
+
scope: 'user', platform: group.platform,
|
|
3887
2665
|
status: 'failed',
|
|
3888
2666
|
configSaved,
|
|
3889
2667
|
applyRequested: intent.apply,
|
|
@@ -3919,7 +2697,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3919
2697
|
return resetPluginConfigWithHelper({
|
|
3920
2698
|
sourceId: parsed.sourceId,
|
|
3921
2699
|
pluginName: parsed.pluginName,
|
|
3922
|
-
scope: intent.
|
|
2700
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
3923
2701
|
cwd: context.cwd,
|
|
3924
2702
|
env: context.env,
|
|
3925
2703
|
nativeCliRunner: context.nativeCliRunner,
|
|
@@ -3948,16 +2726,18 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3948
2726
|
operationContext: catalogOperation,
|
|
3949
2727
|
}) : undefined;
|
|
3950
2728
|
const applyTargets = intent.retryApply || intent.apply
|
|
3951
|
-
? currentEvidence.config?.affectedTargets.map((target) => ({
|
|
2729
|
+
? currentEvidence.config?.affectedTargets.filter((target) => target.platform === 'codex' || target.platform === 'claude').map((target) => ({
|
|
3952
2730
|
pluginName: target.pluginName ?? plugin.manifest.name,
|
|
3953
2731
|
platform: target.platform,
|
|
3954
2732
|
scope: target.scope,
|
|
3955
2733
|
})) ?? []
|
|
3956
2734
|
: [];
|
|
3957
2735
|
const result = intent.retryApply
|
|
3958
|
-
? await
|
|
2736
|
+
? await applyPluginConfig({
|
|
3959
2737
|
plugin,
|
|
3960
|
-
scope: intent.
|
|
2738
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
2739
|
+
...(intent.targets ? { targets: intent.targets } : {}),
|
|
2740
|
+
cliVersion: packageInfo.version,
|
|
3961
2741
|
cwd: context.cwd,
|
|
3962
2742
|
env: context.env,
|
|
3963
2743
|
nativeCliRunner: context.nativeCliRunner,
|
|
@@ -3975,7 +2755,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3975
2755
|
} : {}),
|
|
3976
2756
|
},
|
|
3977
2757
|
awHomeMutationToken,
|
|
3978
|
-
|
|
2758
|
+
expectedEvidence: expectedEvidence.config?.applyEvidence,
|
|
3979
2759
|
sharedNativeApplyConfirmed: true,
|
|
3980
2760
|
preApplyConfirmed: true,
|
|
3981
2761
|
preApplyAuthorizedTargets: expectedEvidence.config?.preApplyAuthorization ?? [],
|
|
@@ -3983,7 +2763,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3983
2763
|
: await applyPluginConfigWrite({
|
|
3984
2764
|
plugin,
|
|
3985
2765
|
pluginName: plugin.manifest.name,
|
|
3986
|
-
scope: intent.
|
|
2766
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
2767
|
+
apply: intent.apply,
|
|
3987
2768
|
cwd: context.cwd,
|
|
3988
2769
|
env: context.env,
|
|
3989
2770
|
nativeCliRunner: context.nativeCliRunner,
|
|
@@ -4001,7 +2782,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
4001
2782
|
} : {}),
|
|
4002
2783
|
},
|
|
4003
2784
|
awHomeMutationToken,
|
|
4004
|
-
applyTargets,
|
|
2785
|
+
...(applyTargets.length ? { applyTargets } : {}),
|
|
4005
2786
|
request: { updates: intent.updates, removeKeys: intent.removeKeys },
|
|
4006
2787
|
sharedNativeApplyConfirmed: true,
|
|
4007
2788
|
preApplyConfirmed: true,
|
|
@@ -4013,8 +2794,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
4013
2794
|
}
|
|
4014
2795
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
4015
2796
|
},
|
|
4016
|
-
}).finally(async () => {
|
|
4017
|
-
await codexObservationSession?.dispose();
|
|
4018
2797
|
});
|
|
4019
2798
|
},
|
|
4020
2799
|
};
|