@lesliechan721/aw 0.1.0-beta.63 → 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 +457 -1634
- 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 +11 -2
- package/dist/src/utils/plugin-config-references.js +85 -90
- 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
|
-
import { collectPluginConfigReferenceEvidence, resolvePluginConfigReferenceConsumers, resolvePluginConfigReferencePlugins } from '../utils/plugin-config-references.js';
|
|
21
|
-
import { createBatchIntentCompatiblePlanFingerprint } from '../commands/operation-plan.js';
|
|
20
|
+
import { collectPluginConfigReferenceEvidence, pluginConfigHasExternalReferences, resolvePluginConfigReferenceConsumers, resolvePluginConfigReferencePlugins } from '../utils/plugin-config-references.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,41 +115,47 @@ 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}`
|
|
133
132
|
.localeCompare(`${right.pluginId}\0${right.platform}\0${right.scope}`, 'en'));
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
|
|
134
|
+
function configNeedsReferencePlugins(plugin) {
|
|
135
|
+
return pluginConfigHasExternalReferences(plugin.manifest)
|
|
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')));
|
|
138
|
+
}
|
|
139
|
+
async function configRetrySharedNativeRenderTargets(plugin, targets, env, referencePlugins) {
|
|
140
|
+
const plugins = await resolvePluginConfigReferenceConsumers({ plugin, env, plugins: referencePlugins });
|
|
137
141
|
return plugins.flatMap((consumer) => pluginSharedNativeConfigTargets(consumer).filter((shared) => targets.some((target) => (target.pluginName ?? plugin.manifest.name) === consumer.manifest.name
|
|
138
142
|
&& target.platform === (shared.platform ?? 'codex'))));
|
|
139
143
|
}
|
|
140
144
|
async function configRetryStoreDigest(options) {
|
|
141
145
|
const plugins = await resolvePluginConfigReferencePlugins({ sourceId: options.plugin.sourceId,
|
|
142
|
-
sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
146
|
+
sourceIdentity: options.plugin.sourceIdentity, logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
147
|
+
env: options.env, plugins: options.plugins });
|
|
143
148
|
const byName = new Map([...plugins, options.plugin].map((plugin) => [plugin.manifest.name, plugin]));
|
|
144
|
-
const profiles = [{ pluginName: options.plugin.manifest.name, scope: options.scope },
|
|
149
|
+
const profiles = [{ pluginName: options.plugin.manifest.name, scope: options.scope, platform: options.platform },
|
|
145
150
|
...options.targets.flatMap((target) => {
|
|
146
151
|
const plugin = byName.get(target.pluginName);
|
|
147
152
|
if (!plugin)
|
|
148
153
|
throw operationReviewStale('重试目标的插件定义已变化。');
|
|
149
|
-
return pluginConfigTargetRenderScopes(plugin, target).map((scope) => ({ pluginName: target.pluginName, scope }));
|
|
154
|
+
return pluginConfigTargetRenderScopes(plugin, target).map((scope) => ({ pluginName: target.pluginName, scope, platform: target.platform }));
|
|
150
155
|
}),
|
|
151
|
-
...options.commandPackages.map(({ producer }) => ({ pluginName: producer.pluginName, scope: producer.scope
|
|
152
|
-
|
|
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 }))];
|
|
153
159
|
return digest(await collectPluginConfigReferenceEvidence({ ...options, profiles, plugins }));
|
|
154
160
|
}
|
|
155
161
|
function configImpactVisibleTargets(pluginName, targets) {
|
|
@@ -173,7 +179,9 @@ function configSaveBatchGroupIntent(intent, group) {
|
|
|
173
179
|
return {
|
|
174
180
|
kind: 'configSave',
|
|
175
181
|
pluginId: intent.pluginId,
|
|
176
|
-
scope:
|
|
182
|
+
scope: 'user',
|
|
183
|
+
platform: group.platform,
|
|
184
|
+
context: intent.context ?? 'user',
|
|
177
185
|
apply: intent.apply,
|
|
178
186
|
...(intent.scriptTimeoutMs === undefined ? {} : { scriptTimeoutMs: intent.scriptTimeoutMs }),
|
|
179
187
|
...(group.updates ? { updates: group.updates } : {}),
|
|
@@ -184,14 +192,11 @@ function configSaveBatchGroupIntent(intent, group) {
|
|
|
184
192
|
};
|
|
185
193
|
}
|
|
186
194
|
function assertConfigSaveBatchIntent(intent) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|| new Set(intent.groups.map((group) => group.scope)).size !== intent.groups.length
|
|
190
|
-
|| intent.groups.some((group, index) => index > 0
|
|
191
|
-
&& 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
|
|
192
197
|
|| intent.groups.some((group) => Object.keys(group.updates ?? {}).length === 0
|
|
193
198
|
&& (group.removeKeys?.length ?? 0) === 0)) {
|
|
194
|
-
throw new TuiOperationBlockedError('configBatchInvalid', '
|
|
199
|
+
throw new TuiOperationBlockedError('configBatchInvalid', '设置批次必须包含唯一且非空的公共或平台配置。');
|
|
195
200
|
}
|
|
196
201
|
}
|
|
197
202
|
function operationReviewStale(message, diagnostics) {
|
|
@@ -221,28 +226,15 @@ async function configLayerStoreDigest(options) {
|
|
|
221
226
|
? { logicalIdentity: options.source.logicalIdentity }
|
|
222
227
|
: {}),
|
|
223
228
|
pluginName: options.pluginName,
|
|
224
|
-
scope:
|
|
225
|
-
|
|
226
|
-
env: options.env,
|
|
227
|
-
});
|
|
228
|
-
return digest({ exists: store.exists, rawDigest: store.rawDigest });
|
|
229
|
-
}
|
|
230
|
-
async function inspectConfigShareability(options) {
|
|
231
|
-
if (options.scope !== 'project')
|
|
232
|
-
return undefined;
|
|
233
|
-
const targetPath = resolvePluginConfigStorePath({
|
|
234
|
-
sourceId: options.sourceId,
|
|
235
|
-
pluginName: options.pluginName,
|
|
236
|
-
scope: 'project',
|
|
237
|
-
cwd: options.cwd,
|
|
238
|
-
env: options.env,
|
|
239
|
-
});
|
|
240
|
-
const visibility = await assertProjectSharedConfigPathVisible({
|
|
229
|
+
scope: 'user',
|
|
230
|
+
platform: options.platform,
|
|
241
231
|
cwd: options.cwd,
|
|
242
|
-
targetPath,
|
|
243
232
|
env: options.env,
|
|
244
233
|
});
|
|
245
|
-
return
|
|
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 });
|
|
246
238
|
}
|
|
247
239
|
function assertSourceEvidenceMatches(expected, actual) {
|
|
248
240
|
if (actual.sourceId !== expected.sourceId
|
|
@@ -255,21 +247,9 @@ function assertSourceEvidenceMatches(expected, actual) {
|
|
|
255
247
|
throw operationReviewStale('确认后来源内容发生变化。');
|
|
256
248
|
}
|
|
257
249
|
}
|
|
258
|
-
function writeReviewIsStale(result) {
|
|
259
|
-
return result.targetResults.some((target) => target.errorCode === 'operationReviewStale'
|
|
260
|
-
|| target.errorCode === 'operationPlanStale');
|
|
261
|
-
}
|
|
262
|
-
function throwIfWriteReviewStale(result) {
|
|
263
|
-
if (writeReviewIsStale(result)) {
|
|
264
|
-
throw operationReviewStale('确认后实际写入计划发生变化。');
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
250
|
const SOURCE_PROGRESS_PHASES = {
|
|
268
251
|
savingConfig: '保存来源配置',
|
|
269
252
|
};
|
|
270
|
-
function pluginIntent(intent) {
|
|
271
|
-
return 'pluginId' in intent;
|
|
272
|
-
}
|
|
273
253
|
function reviewedScriptTimeoutMs(intent) {
|
|
274
254
|
if (intent.kind === 'pluginWriteTask') {
|
|
275
255
|
return parseScriptTimeoutMs(intent.scriptTimeoutMs ?? SCRIPT_TIMEOUT_DEFAULT_MS);
|
|
@@ -284,138 +264,6 @@ function reviewedScriptTimeoutMs(intent) {
|
|
|
284
264
|
}
|
|
285
265
|
return undefined;
|
|
286
266
|
}
|
|
287
|
-
function selectedBatchGroups(intent) {
|
|
288
|
-
if (intent.selection.kind !== 'selected' && intent.selection.kind !== 'targets') {
|
|
289
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部同步必须从项目 intent roots 解析。');
|
|
290
|
-
}
|
|
291
|
-
const rawTargets = intent.selection.kind === 'targets'
|
|
292
|
-
? intent.selection.targets
|
|
293
|
-
: intent.selection.pluginIds.map((pluginId) => ({
|
|
294
|
-
pluginId, platform: intent.platform, scope: intent.scope,
|
|
295
|
-
}));
|
|
296
|
-
const normalizedTargets = rawTargets.map((target) => {
|
|
297
|
-
if ((target.platform !== 'codex' && target.platform !== 'claude' && target.platform !== 'both')
|
|
298
|
-
|| (target.scope !== 'project' && target.scope !== 'user')
|
|
299
|
-
|| (intent.selection.kind === 'targets' && target.platform === 'both')) {
|
|
300
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '批量精确目标必须使用有效 scope 和单个平台。');
|
|
301
|
-
}
|
|
302
|
-
const pluginId = target.pluginId;
|
|
303
|
-
const parsed = parsePluginId(pluginId);
|
|
304
|
-
if (!parsed.sourceId) {
|
|
305
|
-
throw new TuiOperationBlockedError('invalidPluginId', '批量操作必须使用 canonical pluginId。');
|
|
306
|
-
}
|
|
307
|
-
const canonicalPluginId = formatPluginId({ sourceId: parsed.sourceId, pluginName: parsed.pluginName });
|
|
308
|
-
if (intent.selection.kind === 'targets' && canonicalPluginId !== pluginId) {
|
|
309
|
-
throw new TuiOperationBlockedError('invalidPluginId', '批量精确目标必须使用 canonical pluginId。');
|
|
310
|
-
}
|
|
311
|
-
return {
|
|
312
|
-
pluginId: canonicalPluginId,
|
|
313
|
-
platform: target.platform,
|
|
314
|
-
scope: target.scope,
|
|
315
|
-
};
|
|
316
|
-
}).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
|
|
317
|
-
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en'));
|
|
318
|
-
const uniqueTargets = [...new Map(normalizedTargets.map((target) => [
|
|
319
|
-
[target.pluginId, target.platform, target.scope].join('\0'), target,
|
|
320
|
-
])).values()];
|
|
321
|
-
const requestedPluginIds = [...new Set(uniqueTargets.map((target) => target.pluginId))];
|
|
322
|
-
if (requestedPluginIds.length === 0) {
|
|
323
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '批量操作至少需要一个插件。');
|
|
324
|
-
}
|
|
325
|
-
const groups = new Map();
|
|
326
|
-
for (const target of uniqueTargets) {
|
|
327
|
-
const pluginId = target.pluginId;
|
|
328
|
-
const sourceId = parsePluginId(pluginId).sourceId;
|
|
329
|
-
const groupKey = intent.action === 'cleanup'
|
|
330
|
-
? ['cleanup', target.platform, target.scope].join('\0')
|
|
331
|
-
: [sourceId, target.platform, target.scope].join('\0');
|
|
332
|
-
const group = groups.get(groupKey) ?? {
|
|
333
|
-
groupId: groupKey,
|
|
334
|
-
sourceId: intent.action === 'cleanup' ? 'offline' : sourceId,
|
|
335
|
-
platform: target.platform,
|
|
336
|
-
scope: target.scope,
|
|
337
|
-
requestedPluginIds: [],
|
|
338
|
-
};
|
|
339
|
-
group.requestedPluginIds.push(pluginId);
|
|
340
|
-
groups.set(groupKey, group);
|
|
341
|
-
}
|
|
342
|
-
return { requestedPluginIds, groups: [...groups.values()] };
|
|
343
|
-
}
|
|
344
|
-
async function resolveBatchGroups(intent, env, cwd, frozenRequestedPluginIds) {
|
|
345
|
-
if (intent.selection.kind === 'selected' || intent.selection.kind === 'targets') {
|
|
346
|
-
return selectedBatchGroups(intent);
|
|
347
|
-
}
|
|
348
|
-
if (intent.action !== 'sync' || intent.scope !== 'project') {
|
|
349
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目全部操作只支持 project scope sync。');
|
|
350
|
-
}
|
|
351
|
-
const state = await readPluginIntentState({ scope: 'project', cwd, env });
|
|
352
|
-
const projectIntentDigest = calculatePluginIntentDigest(state);
|
|
353
|
-
if (intent.selection.kind === 'currentProjectIntentTargets') {
|
|
354
|
-
if (intent.selection.targets.length === 0
|
|
355
|
-
|| intent.selection.targets.some((target) => target.scope !== 'project')) {
|
|
356
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须是非空 project targets。');
|
|
357
|
-
}
|
|
358
|
-
const exact = selectedBatchGroups({
|
|
359
|
-
...intent,
|
|
360
|
-
selection: { kind: 'targets', targets: intent.selection.targets },
|
|
361
|
-
});
|
|
362
|
-
const declaredPlatforms = new Map(state.roots.map((root) => [
|
|
363
|
-
root.pluginId,
|
|
364
|
-
new Set(root.platforms),
|
|
365
|
-
]));
|
|
366
|
-
if (intent.selection.targets.some((target) => !declaredPlatforms.get(target.pluginId)?.has(target.platform))) {
|
|
367
|
-
throw new TuiOperationBlockedError('batchTargetInvalid', '当前项目精确同步目标必须属于完整 project intent roots 声明。');
|
|
368
|
-
}
|
|
369
|
-
return { ...exact, projectIntentDigest };
|
|
370
|
-
}
|
|
371
|
-
const selection = resolveSyncIntentSelection({
|
|
372
|
-
roots: state.roots,
|
|
373
|
-
selectors: frozenRequestedPluginIds ?? [],
|
|
374
|
-
all: frozenRequestedPluginIds === undefined,
|
|
375
|
-
platform: intent.platform,
|
|
376
|
-
});
|
|
377
|
-
if (selection.requestedPluginIds.length === 0) {
|
|
378
|
-
throw new TuiOperationBlockedError('syncSelectionRequired', '当前项目没有可同步的 intent roots。');
|
|
379
|
-
}
|
|
380
|
-
return {
|
|
381
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
382
|
-
projectIntentDigest,
|
|
383
|
-
groups: selection.groups.map((group) => ({
|
|
384
|
-
groupId: `${group.sourceId}\0${group.platform}\0${intent.scope}`,
|
|
385
|
-
sourceId: group.sourceId,
|
|
386
|
-
platform: group.platform,
|
|
387
|
-
scope: intent.scope,
|
|
388
|
-
requestedPluginIds: group.pluginIds,
|
|
389
|
-
})),
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
async function runBatchWriteGroup(options) {
|
|
393
|
-
const dependencyScopeOverrides = options.intent.action === 'install'
|
|
394
|
-
? options.intent.dependencyScopeOverrides?.filter((override) => parsePluginId(override.pluginId).sourceId === options.group.sourceId
|
|
395
|
-
&& (options.group.platform === 'both' || override.platform === options.group.platform))
|
|
396
|
-
: undefined;
|
|
397
|
-
const commandOptions = {
|
|
398
|
-
cwd: options.cwd,
|
|
399
|
-
...(dependencyScopeOverrides?.length ? { dependencyScopeOverrides } : {}),
|
|
400
|
-
platform: options.group.platform,
|
|
401
|
-
scope: options.group.scope,
|
|
402
|
-
...(reviewedScriptTimeoutMs(options.intent) === undefined
|
|
403
|
-
? {}
|
|
404
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(options.intent) }),
|
|
405
|
-
};
|
|
406
|
-
switch (options.intent.action) {
|
|
407
|
-
case 'install':
|
|
408
|
-
return installWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
409
|
-
case 'sync':
|
|
410
|
-
return syncWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
411
|
-
case 'reinstall':
|
|
412
|
-
return reinstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
413
|
-
case 'uninstall':
|
|
414
|
-
return uninstallWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
415
|
-
case 'cleanup':
|
|
416
|
-
return cleanupWriteCommand(options.group.requestedPluginIds, commandOptions, options.runtime);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
267
|
async function loadDependencyOverviews(options) {
|
|
420
268
|
const graph = await withResolvedMultiSourceCatalog({
|
|
421
269
|
cliVersion: options.cliVersion,
|
|
@@ -708,8 +556,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
708
556
|
};
|
|
709
557
|
};
|
|
710
558
|
const catalogOperationByEvidence = new WeakMap();
|
|
711
|
-
const taskCatalogOperationsByEvidence = new WeakMap();
|
|
712
|
-
const codexManagerByEvidence = new WeakMap();
|
|
713
559
|
let sessionCatalogOperationContext;
|
|
714
560
|
let sessionStatusResult;
|
|
715
561
|
const sessionContextForPlugin = (pluginId) => {
|
|
@@ -719,33 +565,42 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
719
565
|
return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
|
|
720
566
|
};
|
|
721
567
|
const readConfigStoresForTui = async (config) => {
|
|
722
|
-
const
|
|
723
|
-
const stores = await Promise.all(scopes.map((scope) => readPluginConfigStore({
|
|
568
|
+
const user = await readPluginConfigStore({
|
|
724
569
|
sourceId: config.sourceId,
|
|
725
570
|
sourceIdentity: config.sourceIdentity,
|
|
726
571
|
logicalIdentity: config.logicalIdentity,
|
|
727
572
|
pluginName: config.pluginName,
|
|
728
|
-
scope,
|
|
573
|
+
scope: 'user',
|
|
574
|
+
platform: config.platform,
|
|
729
575
|
cwd: context.cwd,
|
|
730
576
|
env: context.env,
|
|
731
577
|
schema: config.schema,
|
|
732
|
-
})
|
|
733
|
-
const user = stores[0];
|
|
734
|
-
const project = config.scope === 'user' ? undefined : stores[1];
|
|
578
|
+
});
|
|
735
579
|
config.signal.throwIfAborted();
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (
|
|
747
|
-
throw
|
|
748
|
-
|
|
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
|
+
}
|
|
749
604
|
};
|
|
750
605
|
const loadStatus = async (signal) => {
|
|
751
606
|
signal.throwIfAborted();
|
|
@@ -816,7 +671,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
816
671
|
signal.throwIfAborted();
|
|
817
672
|
const catalogGenerations = await loadCatalogGenerations();
|
|
818
673
|
signal.throwIfAborted();
|
|
819
|
-
const configStoreInventory = (await Promise.all(['user'
|
|
674
|
+
const configStoreInventory = (await Promise.all(['user'].map(async (scope) => {
|
|
820
675
|
try {
|
|
821
676
|
return (await listNamespacedPluginConfigStoreIdentities({
|
|
822
677
|
cwd: context.cwd,
|
|
@@ -839,24 +694,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
839
694
|
}))).flat();
|
|
840
695
|
signal.throwIfAborted();
|
|
841
696
|
const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
|
|
842
|
-
const projectIntentObservation = result.projectIntentObservation;
|
|
843
|
-
const projectIntent = projectIntentObservation.state === 'ready' ? {
|
|
844
|
-
state: 'ready',
|
|
845
|
-
roots: structuredClone(projectIntentObservation.value.roots),
|
|
846
|
-
} : projectIntentObservation.state === 'absent' ? {
|
|
847
|
-
state: 'absent',
|
|
848
|
-
} : {
|
|
849
|
-
state: projectIntentObservation.state,
|
|
850
|
-
code: projectIntentObservation.code,
|
|
851
|
-
message: projectIntentObservation.message,
|
|
852
|
-
};
|
|
853
697
|
return {
|
|
854
698
|
cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
|
|
855
699
|
...(result.projectRoot ? { projectRoot: result.projectRoot } : {}),
|
|
856
|
-
projectIntent,
|
|
857
|
-
...(projectIntent.state === 'ready'
|
|
858
|
-
? { projectIntentRoots: structuredClone(projectIntent.roots) }
|
|
859
|
-
: {}),
|
|
860
700
|
configuredSources: structuredClone(configured.sources),
|
|
861
701
|
payload: result.payload,
|
|
862
702
|
pluginSummaries: result.pluginSummaries,
|
|
@@ -940,8 +780,17 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
940
780
|
};
|
|
941
781
|
}
|
|
942
782
|
};
|
|
943
|
-
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
|
+
}
|
|
944
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
|
+
}
|
|
945
794
|
if (intent.kind === 'catalogStorePrune') {
|
|
946
795
|
signal?.throwIfAborted();
|
|
947
796
|
const config = await readCatalogSourcesConfigSnapshot(context.env);
|
|
@@ -958,7 +807,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
958
807
|
let batchBase;
|
|
959
808
|
let batchCatalogOperation;
|
|
960
809
|
for (const group of intent.groups) {
|
|
961
|
-
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);
|
|
962
811
|
if (!current.config || !current.source || !current.targetFingerprint) {
|
|
963
812
|
throw operationReviewStale('设置批次缺少单层证据。');
|
|
964
813
|
}
|
|
@@ -968,10 +817,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
968
817
|
}
|
|
969
818
|
for (const expected of Object.values(group.editEvidence ?? {})) {
|
|
970
819
|
if (expected.sourceIdentity !== current.source.sourceIdentity
|
|
971
|
-
|| expected.schemaIdentity !== current.config.schemaIdentity
|
|
820
|
+
|| expected.schemaIdentity !== current.config.schemaIdentity
|
|
821
|
+
|| expected.platform !== group.platform
|
|
822
|
+
|| (expected.context ?? 'user') !== (intent.context ?? 'user')) {
|
|
972
823
|
throw operationReviewStale('编辑期间插件来源或设置定义已变化,请重新打开列表。');
|
|
973
824
|
}
|
|
974
|
-
for (const scope of ['user'
|
|
825
|
+
for (const scope of ['user']) {
|
|
975
826
|
if (expected.storeDigests[scope] === undefined)
|
|
976
827
|
continue;
|
|
977
828
|
const currentDigest = await configLayerStoreDigest({
|
|
@@ -986,13 +837,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
986
837
|
batchBase ??= current;
|
|
987
838
|
batchCatalogOperation ??= catalogOperationByEvidence.get(current);
|
|
988
839
|
groupEvidence.push({
|
|
989
|
-
scope:
|
|
840
|
+
scope: 'user',
|
|
841
|
+
platform: group.platform,
|
|
990
842
|
config: {
|
|
991
843
|
...current.config,
|
|
992
844
|
storeDigest: await configLayerStoreDigest({
|
|
993
845
|
source: current.source,
|
|
994
846
|
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
995
|
-
scope:
|
|
847
|
+
scope: 'user',
|
|
848
|
+
platform: group.platform,
|
|
849
|
+
profileOnly: true,
|
|
996
850
|
cwd: context.cwd,
|
|
997
851
|
env: context.env,
|
|
998
852
|
}),
|
|
@@ -1009,23 +863,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1009
863
|
catalogOperationByEvidence.set(evidence, batchCatalogOperation);
|
|
1010
864
|
return evidence;
|
|
1011
865
|
}
|
|
1012
|
-
if ((intent.kind === 'pluginWrite'
|
|
1013
|
-
&& (intent.platform === 'codex' || intent.platform === 'both')
|
|
1014
|
-
|| intent.kind === 'pluginWriteTask'
|
|
1015
|
-
&& intent.targets.some((target) => target.platform === 'codex'))
|
|
1016
|
-
&& !codexObservationSession) {
|
|
1017
|
-
const observationSession = await createCodexNativeObservationSession({
|
|
1018
|
-
exactCwd: context.cwd,
|
|
1019
|
-
env: context.env,
|
|
1020
|
-
runner: context.nativeCliRunner,
|
|
1021
|
-
});
|
|
1022
|
-
try {
|
|
1023
|
-
return await captureEvidence(intent, signal, preparation, operationInventory, freshCatalog, reusableCandidate, observationSession, frozenBatchEvidence);
|
|
1024
|
-
}
|
|
1025
|
-
finally {
|
|
1026
|
-
await observationSession.dispose();
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
866
|
if (intent.kind === 'configReset') {
|
|
1030
867
|
const parsed = parsePluginId(intent.pluginId);
|
|
1031
868
|
if (!parsed.sourceId || !parsed.pluginName) {
|
|
@@ -1035,7 +872,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1035
872
|
const target = await inspectPluginConfigResetTarget({
|
|
1036
873
|
sourceId: parsed.sourceId,
|
|
1037
874
|
pluginName: parsed.pluginName,
|
|
1038
|
-
scope:
|
|
875
|
+
scope: 'user',
|
|
1039
876
|
cwd: context.cwd,
|
|
1040
877
|
env: context.env,
|
|
1041
878
|
});
|
|
@@ -1044,7 +881,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1044
881
|
throw new TuiOperationBlockedError('configResetTargetUnsupported', '设置目标是特殊文件系统节点,不能自动重置。请手动移动后重试。');
|
|
1045
882
|
}
|
|
1046
883
|
const relevantTargets = sessionStatusResult
|
|
1047
|
-
? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.
|
|
884
|
+
? configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.context ?? 'user', [], intent.platform)
|
|
1048
885
|
: [];
|
|
1049
886
|
const relevantRevisions = (sessionStatusResult?.targetRevisions ?? []).filter((revision) => relevantTargets.some((statusTarget) => statusTarget.platform === revision.platform
|
|
1050
887
|
&& statusTarget.scope === revision.scope
|
|
@@ -1064,7 +901,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1064
901
|
source: sourceEvidenceForPlugin(resolvedSource, plugin.manifest.name),
|
|
1065
902
|
impact: await analyzePluginConfigMutationImpact({
|
|
1066
903
|
plugin,
|
|
1067
|
-
scope: intent.
|
|
904
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1068
905
|
cwd: context.cwd,
|
|
1069
906
|
env: context.env,
|
|
1070
907
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
@@ -1073,7 +910,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1073
910
|
}),
|
|
1074
911
|
});
|
|
1075
912
|
}
|
|
1076
|
-
catch {
|
|
913
|
+
catch (error) {
|
|
914
|
+
if (error?.code === 'configValidationMemoryExhausted')
|
|
915
|
+
throw error;
|
|
1077
916
|
selected = undefined;
|
|
1078
917
|
}
|
|
1079
918
|
signal?.throwIfAborted();
|
|
@@ -1091,7 +930,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1091
930
|
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
1092
931
|
} : {}),
|
|
1093
932
|
config: {
|
|
1094
|
-
scope: intent.
|
|
933
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
1095
934
|
storeDigest: selected?.impact.storeDigest ?? target.fingerprint,
|
|
1096
935
|
impactState: selected?.impact.impactState ?? 'unknown',
|
|
1097
936
|
...(selected?.impact.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
@@ -1106,7 +945,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1106
945
|
resetTarget: {
|
|
1107
946
|
sourceId: parsed.sourceId,
|
|
1108
947
|
pluginName: parsed.pluginName,
|
|
1109
|
-
scope: intent.
|
|
948
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
1110
949
|
kind: target.kind,
|
|
1111
950
|
fingerprint: target.fingerprint,
|
|
1112
951
|
},
|
|
@@ -1417,671 +1256,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1417
1256
|
}
|
|
1418
1257
|
return evidence;
|
|
1419
1258
|
}
|
|
1420
|
-
if (
|
|
1421
|
-
&& intent.kind !== '
|
|
1259
|
+
if (intent.kind !== 'configSave'
|
|
1260
|
+
&& intent.kind !== 'portableInstall') {
|
|
1422
1261
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
1423
1262
|
}
|
|
1424
1263
|
signal?.throwIfAborted();
|
|
1425
|
-
if (intent.kind === 'pluginWriteTask') {
|
|
1426
|
-
await preparation?.reporter.emit('status');
|
|
1427
|
-
const packageInfo = await readCliPackageInfo();
|
|
1428
|
-
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1429
|
-
const requestedBySource = new Map();
|
|
1430
|
-
for (const target of intent.targets) {
|
|
1431
|
-
const parsed = parsePluginId(target.pluginId);
|
|
1432
|
-
if (!parsed.sourceId || formatPluginId({
|
|
1433
|
-
sourceId: parsed.sourceId,
|
|
1434
|
-
pluginName: parsed.pluginName,
|
|
1435
|
-
}) !== target.pluginId) {
|
|
1436
|
-
throw new TuiOperationBlockedError('invalidPluginId', '任务目标必须使用 canonical pluginId。');
|
|
1437
|
-
}
|
|
1438
|
-
requestedBySource.set(parsed.sourceId, [
|
|
1439
|
-
...(requestedBySource.get(parsed.sourceId) ?? []),
|
|
1440
|
-
target,
|
|
1441
|
-
]);
|
|
1442
|
-
}
|
|
1443
|
-
const sources = [];
|
|
1444
|
-
const executableTargets = [];
|
|
1445
|
-
const causes = [];
|
|
1446
|
-
const blockedTargets = [];
|
|
1447
|
-
const observedTargets = [];
|
|
1448
|
-
const observedRevisions = [];
|
|
1449
|
-
const operationContexts = new Map();
|
|
1450
|
-
await preparation?.reporter.emit('catalog');
|
|
1451
|
-
for (const [sourceId, requestedTargets] of [...requestedBySource.entries()]
|
|
1452
|
-
.sort(([left], [right]) => left.localeCompare(right, 'en'))) {
|
|
1453
|
-
signal?.throwIfAborted();
|
|
1454
|
-
let catalogOperation;
|
|
1455
|
-
try {
|
|
1456
|
-
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1457
|
-
? selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId])
|
|
1458
|
-
: await resolveCatalogOperationContext({
|
|
1459
|
-
sourceIds: [sourceId],
|
|
1460
|
-
env: context.env,
|
|
1461
|
-
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1462
|
-
...(signal ? { signal } : {}),
|
|
1463
|
-
});
|
|
1464
|
-
}
|
|
1465
|
-
catch (error) {
|
|
1466
|
-
if (error instanceof Error && error.name === 'AbortError')
|
|
1467
|
-
throw error;
|
|
1468
|
-
blockedTargets.push(...requestedTargets.map((target, index) => {
|
|
1469
|
-
const diagnostic = error instanceof CatalogGenerationError ? {
|
|
1470
|
-
id: `task-source:${sourceId}:${index}`,
|
|
1471
|
-
code: error.code,
|
|
1472
|
-
stage: 'source',
|
|
1473
|
-
sourceId,
|
|
1474
|
-
safeMessage: 'The Catalog source could not be prepared safely.',
|
|
1475
|
-
target: { pluginId: target.pluginId, platform: target.platform, scope: target.scope },
|
|
1476
|
-
causes: [],
|
|
1477
|
-
detailLines: [],
|
|
1478
|
-
} : projectThrownOperationDiagnostic({
|
|
1479
|
-
error,
|
|
1480
|
-
id: `task-source:${sourceId}:${index}`,
|
|
1481
|
-
stage: 'source',
|
|
1482
|
-
fallbackCode: 'catalogSourceResolutionFailed',
|
|
1483
|
-
sourceId,
|
|
1484
|
-
target,
|
|
1485
|
-
});
|
|
1486
|
-
return {
|
|
1487
|
-
target,
|
|
1488
|
-
reason: 'targetUnavailable',
|
|
1489
|
-
issueIds: [],
|
|
1490
|
-
errorCodes: [diagnostic.code],
|
|
1491
|
-
diagnostics: [diagnostic],
|
|
1492
|
-
};
|
|
1493
|
-
}));
|
|
1494
|
-
continue;
|
|
1495
|
-
}
|
|
1496
|
-
operationContexts.set(sourceId, catalogOperation);
|
|
1497
|
-
const status = await statusCommandV8({
|
|
1498
|
-
plugins: [...new Set(requestedTargets.map((target) => target.pluginId))],
|
|
1499
|
-
cwd: context.cwd,
|
|
1500
|
-
env: context.env,
|
|
1501
|
-
nativeCliRunner: context.nativeCliRunner,
|
|
1502
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1503
|
-
cliVersion: packageInfo.version,
|
|
1504
|
-
catalogOperationContext: catalogOperation,
|
|
1505
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1506
|
-
});
|
|
1507
|
-
observedTargets.push(...flattenStatusTargets(status.payload.targets));
|
|
1508
|
-
observedRevisions.push(...status.targetRevisions);
|
|
1509
|
-
const model = buildTuiViewModel({
|
|
1510
|
-
status: status.payload,
|
|
1511
|
-
pluginSummaries: status.pluginSummaries,
|
|
1512
|
-
targetRevisions: status.targetRevisions,
|
|
1513
|
-
});
|
|
1514
|
-
const runtimeCandidates = intent.goal === 'restore'
|
|
1515
|
-
? buildPluginWriteTaskCandidates(model, 'restore')
|
|
1516
|
-
: requestedTargets.map((target) => {
|
|
1517
|
-
const expected = target.expectedUpdate;
|
|
1518
|
-
const revision = status.targetRevisions.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
|
|
1519
|
-
&& entry.scope === target.scope);
|
|
1520
|
-
const live = flattenStatusTargets(status.payload.targets).find((entry) => {
|
|
1521
|
-
if (entry.pluginId !== target.pluginId || entry.platform !== target.platform)
|
|
1522
|
-
return false;
|
|
1523
|
-
const desired = statusTargetIntent(entry);
|
|
1524
|
-
return entry.scope === target.scope || desired.scopes.includes(target.scope);
|
|
1525
|
-
});
|
|
1526
|
-
const generation = catalogOperation.generationSet.sources.find((entry) => entry.sourceId === sourceId);
|
|
1527
|
-
const eligible = Boolean(expected && live && revision
|
|
1528
|
-
&& live.sourceIdentity === expected.sourceIdentity
|
|
1529
|
-
&& generation?.storeKey === expected.generationKey
|
|
1530
|
-
&& revision.revision.installedVersion === expected.installedVersion
|
|
1531
|
-
&& revision.revision.availableVersion === expected.activeVersion
|
|
1532
|
-
&& revision.revision.state === 'upgradeAvailable');
|
|
1533
|
-
const directVersionUpgrade = live !== undefined && revision !== undefined
|
|
1534
|
-
&& isDirectCodexVersionUpgrade(live, revision.revision);
|
|
1535
|
-
return {
|
|
1536
|
-
target,
|
|
1537
|
-
displayName: model.plugins.find((plugin) => plugin.pluginId === target.pluginId)
|
|
1538
|
-
?.displayName ?? parsePluginId(target.pluginId).pluginName,
|
|
1539
|
-
sourceId,
|
|
1540
|
-
eligible,
|
|
1541
|
-
...(!eligible ? { blockReason: 'updateEvidenceIncomplete' } : {}),
|
|
1542
|
-
recommendedMethod: live?.state === 'ready' || directVersionUpgrade
|
|
1543
|
-
? 'sync' : 'reinstall',
|
|
1544
|
-
issueIds: [],
|
|
1545
|
-
};
|
|
1546
|
-
});
|
|
1547
|
-
const draft = buildPluginWriteTaskDraft({ ...intent, targets: requestedTargets }, model, runtimeCandidates);
|
|
1548
|
-
blockedTargets.push(...draft.blockedTargets);
|
|
1549
|
-
if (draft.executableTargets.length === 0)
|
|
1550
|
-
continue;
|
|
1551
|
-
const methodsByTarget = new Map();
|
|
1552
|
-
for (const cause of draft.causes) {
|
|
1553
|
-
const key = pluginWriteTaskTargetKey(cause.repairTarget);
|
|
1554
|
-
const methods = methodsByTarget.get(key) ?? new Set();
|
|
1555
|
-
methods.add(cause.method);
|
|
1556
|
-
methodsByTarget.set(key, methods);
|
|
1557
|
-
}
|
|
1558
|
-
const conflicted = new Set([...methodsByTarget].filter(([, methods]) => methods.size > 1)
|
|
1559
|
-
.map(([key]) => key));
|
|
1560
|
-
if (conflicted.size > 0) {
|
|
1561
|
-
const blockedKeys = new Set(draft.causes.filter((cause) => conflicted.has(pluginWriteTaskTargetKey(cause.repairTarget)))
|
|
1562
|
-
.flatMap((cause) => cause.consumers.map(pluginWriteTaskTargetKey)));
|
|
1563
|
-
blockedTargets.push(...draft.executableTargets.filter((target) => blockedKeys.has(pluginWriteTaskTargetKey(target))).map((target) => ({
|
|
1564
|
-
target,
|
|
1565
|
-
reason: 'taskActionConflict',
|
|
1566
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1567
|
-
.map((cause) => cause.issueId),
|
|
1568
|
-
})));
|
|
1569
|
-
draft.executableTargets = draft.executableTargets.filter((target) => !blockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1570
|
-
draft.causes = draft.causes.filter((cause) => cause.consumers.some((consumer) => !blockedKeys.has(pluginWriteTaskTargetKey(consumer))));
|
|
1571
|
-
}
|
|
1572
|
-
if (draft.executableTargets.length === 0)
|
|
1573
|
-
continue;
|
|
1574
|
-
const taskTargets = () => draft.executableTargets.map((target) => {
|
|
1575
|
-
const direct = draft.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
1576
|
-
return {
|
|
1577
|
-
pluginId: target.pluginId,
|
|
1578
|
-
platform: target.platform,
|
|
1579
|
-
scope: target.scope,
|
|
1580
|
-
method: direct.some((cause) => cause.method === 'reinstall')
|
|
1581
|
-
? 'reinstall' : 'sync',
|
|
1582
|
-
};
|
|
1583
|
-
});
|
|
1584
|
-
const repairTargets = () => [...new Map(draft.causes.map((cause) => [
|
|
1585
|
-
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
1586
|
-
{ ...cause.repairTarget, method: cause.method },
|
|
1587
|
-
])).values()];
|
|
1588
|
-
let preview;
|
|
1589
|
-
while (draft.executableTargets.length > 0) {
|
|
1590
|
-
preview = await taskWriteCommand(taskTargets(), {
|
|
1591
|
-
cwd: context.cwd,
|
|
1592
|
-
repairTargets: repairTargets(),
|
|
1593
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
1594
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
1595
|
-
}, {
|
|
1596
|
-
...context,
|
|
1597
|
-
catalogOperationContext: catalogOperation,
|
|
1598
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1599
|
-
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1600
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1601
|
-
previewOperationPlan: true,
|
|
1602
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1603
|
-
...(preparation ? {
|
|
1604
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1605
|
-
} : {}),
|
|
1606
|
-
});
|
|
1607
|
-
if (!preview.operationPlan) {
|
|
1608
|
-
const projected = projectExecutionDiagnostics(preview, `task-preview:${sourceId}`, [], 'pluginWrite');
|
|
1609
|
-
const sourceDiagnostics = projected.diagnostics.filter((diagnostic) => !diagnostic.target)
|
|
1610
|
-
.map((diagnostic) => ({ ...diagnostic, sourceId, target: undefined }));
|
|
1611
|
-
blockedTargets.push(...draft.executableTargets.map((target, targetIndex) => {
|
|
1612
|
-
const matchingDiagnostics = projected.diagnostics.filter((diagnostic) => diagnostic.target?.pluginId === target.pluginId
|
|
1613
|
-
&& diagnostic.target.platform === target.platform
|
|
1614
|
-
&& diagnostic.target.scope === target.scope);
|
|
1615
|
-
const preparedDiagnostics = [
|
|
1616
|
-
...matchingDiagnostics,
|
|
1617
|
-
...(targetIndex === 0 ? sourceDiagnostics : []),
|
|
1618
|
-
];
|
|
1619
|
-
const targetDiagnostics = preparedDiagnostics.length > 0
|
|
1620
|
-
? preparedDiagnostics
|
|
1621
|
-
: [projectThrownOperationDiagnostic({
|
|
1622
|
-
error: undefined,
|
|
1623
|
-
id: `task-preview:${sourceId}:${targetIndex}`,
|
|
1624
|
-
stage: 'dependencyPlan',
|
|
1625
|
-
fallbackCode: 'taskOperationPlanMissing',
|
|
1626
|
-
sourceId,
|
|
1627
|
-
target,
|
|
1628
|
-
})];
|
|
1629
|
-
const diagnostics = targetDiagnostics.map((diagnostic, diagnosticIndex) => ({
|
|
1630
|
-
...structuredClone(diagnostic),
|
|
1631
|
-
id: `task-preview:${sourceId}:${targetIndex}:${diagnosticIndex}`,
|
|
1632
|
-
sourceId,
|
|
1633
|
-
}));
|
|
1634
|
-
return {
|
|
1635
|
-
target,
|
|
1636
|
-
reason: 'targetUnavailable',
|
|
1637
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1638
|
-
.map((cause) => cause.issueId),
|
|
1639
|
-
errorCodes: [...new Set(diagnostics.map((diagnostic) => diagnostic.code))],
|
|
1640
|
-
diagnostics,
|
|
1641
|
-
};
|
|
1642
|
-
}));
|
|
1643
|
-
draft.executableTargets = [];
|
|
1644
|
-
draft.causes = [];
|
|
1645
|
-
break;
|
|
1646
|
-
}
|
|
1647
|
-
if (intent.goal === 'restore') {
|
|
1648
|
-
const grouped = new Map();
|
|
1649
|
-
const blockedByVersion = new Map();
|
|
1650
|
-
for (const cause of draft.causes) {
|
|
1651
|
-
const placements = (preview.dependencyPlan?.targets ?? []).filter((target) => target.pluginId === cause.repairTarget.pluginId
|
|
1652
|
-
&& target.platform === cause.repairTarget.platform);
|
|
1653
|
-
const placement = placements.find((target) => target.requestedScope === cause.repairTarget.scope)
|
|
1654
|
-
?? (placements.length === 1 ? placements[0] : undefined);
|
|
1655
|
-
if (!placement) {
|
|
1656
|
-
throw new TuiOperationBlockedError('taskRepairPlacementMissing', '任务修复目标无法绑定到依赖位置。');
|
|
1657
|
-
}
|
|
1658
|
-
const repairTarget = {
|
|
1659
|
-
pluginId: cause.repairTarget.pluginId,
|
|
1660
|
-
platform: cause.repairTarget.platform,
|
|
1661
|
-
scope: placement.requestedScope ?? placement.resolvedScope,
|
|
1662
|
-
};
|
|
1663
|
-
const version = evaluatePluginWriteTaskRestoreVersion(repairTarget, cause.restoreVersionEvidence?.state === 'absent', model);
|
|
1664
|
-
if (!version.evidence || version.blockReason) {
|
|
1665
|
-
for (const consumer of cause.consumers) {
|
|
1666
|
-
const key = pluginWriteTaskTargetKey(consumer);
|
|
1667
|
-
const current = blockedByVersion.get(key) ?? {
|
|
1668
|
-
reason: version.blockReason ?? 'restoreVersionUnverified',
|
|
1669
|
-
issueIds: new Set(),
|
|
1670
|
-
};
|
|
1671
|
-
if (version.blockReason === 'restoreVersionChanged') {
|
|
1672
|
-
current.reason = version.blockReason;
|
|
1673
|
-
}
|
|
1674
|
-
current.issueIds.add(cause.issueId);
|
|
1675
|
-
blockedByVersion.set(key, current);
|
|
1676
|
-
}
|
|
1677
|
-
continue;
|
|
1678
|
-
}
|
|
1679
|
-
const causeKey = ['restore', cause.sourceIdentity,
|
|
1680
|
-
pluginWriteTaskTargetKey(repairTarget)].join('\0');
|
|
1681
|
-
const current = grouped.get(causeKey);
|
|
1682
|
-
grouped.set(causeKey, {
|
|
1683
|
-
cause: {
|
|
1684
|
-
...cause,
|
|
1685
|
-
causeKey,
|
|
1686
|
-
issueId: [cause.issueId, ...(current?.issueIds ?? [])]
|
|
1687
|
-
.sort((left, right) => left.localeCompare(right, 'en'))[0],
|
|
1688
|
-
repairTarget,
|
|
1689
|
-
consumers: [...new Map([
|
|
1690
|
-
...(current?.cause.consumers ?? []),
|
|
1691
|
-
...cause.consumers,
|
|
1692
|
-
].map((consumer) => [pluginWriteTaskTargetKey(consumer), consumer])).values()]
|
|
1693
|
-
.sort((left, right) => pluginWriteTaskTargetKey(left)
|
|
1694
|
-
.localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1695
|
-
restoreVersionEvidence: version.evidence,
|
|
1696
|
-
},
|
|
1697
|
-
issueIds: new Set([...(current?.issueIds ?? []), cause.issueId]),
|
|
1698
|
-
methods: new Set([...(current?.methods ?? []), cause.method]),
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
for (const group of grouped.values()) {
|
|
1702
|
-
if (group.methods.size < 2)
|
|
1703
|
-
continue;
|
|
1704
|
-
for (const consumer of group.cause.consumers) {
|
|
1705
|
-
const key = pluginWriteTaskTargetKey(consumer);
|
|
1706
|
-
const current = blockedByVersion.get(key) ?? {
|
|
1707
|
-
reason: 'taskActionConflict',
|
|
1708
|
-
issueIds: new Set(),
|
|
1709
|
-
};
|
|
1710
|
-
group.issueIds.forEach((issueId) => current.issueIds.add(issueId));
|
|
1711
|
-
blockedByVersion.set(key, current);
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
const newlyBlockedKeys = new Set(blockedByVersion.keys());
|
|
1715
|
-
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1716
|
-
const blocked = blockedByVersion.get(pluginWriteTaskTargetKey(target));
|
|
1717
|
-
return blocked ? [{
|
|
1718
|
-
target,
|
|
1719
|
-
reason: blocked.reason,
|
|
1720
|
-
issueIds: [...blocked.issueIds].sort((left, right) => left.localeCompare(right, 'en')),
|
|
1721
|
-
}] : [];
|
|
1722
|
-
}));
|
|
1723
|
-
const normalizedCauses = [...grouped.values()]
|
|
1724
|
-
.filter((group) => group.methods.size === 1)
|
|
1725
|
-
.map((group) => ({
|
|
1726
|
-
...group.cause,
|
|
1727
|
-
consumers: group.cause.consumers.filter((consumer) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(consumer))),
|
|
1728
|
-
}))
|
|
1729
|
-
.filter((cause) => cause.consumers.length > 0)
|
|
1730
|
-
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en'));
|
|
1731
|
-
const normalizedTargets = draft.executableTargets.filter((target) => !newlyBlockedKeys.has(pluginWriteTaskTargetKey(target)));
|
|
1732
|
-
const changed = JSON.stringify(normalizedCauses) !== JSON.stringify(draft.causes)
|
|
1733
|
-
|| JSON.stringify(normalizedTargets) !== JSON.stringify(draft.executableTargets);
|
|
1734
|
-
draft.causes = normalizedCauses;
|
|
1735
|
-
draft.executableTargets = normalizedTargets;
|
|
1736
|
-
if (changed)
|
|
1737
|
-
continue;
|
|
1738
|
-
}
|
|
1739
|
-
const blockedPlanTargets = (preview.dependencyPlan?.targets ?? []).filter((target) => target.action === 'block' || target.errorCode !== undefined);
|
|
1740
|
-
if (blockedPlanTargets.length === 0)
|
|
1741
|
-
break;
|
|
1742
|
-
const requestedKeys = new Set(draft.executableTargets.map(pluginWriteTaskTargetKey));
|
|
1743
|
-
const blockedPlansByRoot = new Map();
|
|
1744
|
-
for (const blockedPlanTarget of blockedPlanTargets) {
|
|
1745
|
-
const pending = [blockedPlanTarget];
|
|
1746
|
-
const visited = new Set();
|
|
1747
|
-
const mappedRoots = new Set();
|
|
1748
|
-
const repairKey = pluginWriteTaskTargetKey({
|
|
1749
|
-
pluginId: blockedPlanTarget.pluginId,
|
|
1750
|
-
platform: blockedPlanTarget.platform,
|
|
1751
|
-
scope: blockedPlanTarget.requestedScope ?? blockedPlanTarget.resolvedScope,
|
|
1752
|
-
});
|
|
1753
|
-
for (const cause of draft.causes) {
|
|
1754
|
-
if (pluginWriteTaskTargetKey(cause.repairTarget) !== repairKey)
|
|
1755
|
-
continue;
|
|
1756
|
-
cause.consumers.forEach((consumer) => mappedRoots.add(pluginWriteTaskTargetKey(consumer)));
|
|
1757
|
-
}
|
|
1758
|
-
while (pending.length > 0) {
|
|
1759
|
-
const current = pending.shift();
|
|
1760
|
-
const currentKey = pluginWriteTaskTargetKey({
|
|
1761
|
-
pluginId: current.pluginId,
|
|
1762
|
-
platform: current.platform,
|
|
1763
|
-
scope: current.requestedScope ?? current.resolvedScope,
|
|
1764
|
-
});
|
|
1765
|
-
if (visited.has(currentKey))
|
|
1766
|
-
continue;
|
|
1767
|
-
visited.add(currentKey);
|
|
1768
|
-
if (current.relation === 'requested' && requestedKeys.has(currentKey)) {
|
|
1769
|
-
mappedRoots.add(currentKey);
|
|
1770
|
-
}
|
|
1771
|
-
for (const consumer of current.requiredByTargets ?? []) {
|
|
1772
|
-
const consumerKey = pluginWriteTaskTargetKey(consumer);
|
|
1773
|
-
if (requestedKeys.has(consumerKey)) {
|
|
1774
|
-
mappedRoots.add(consumerKey);
|
|
1775
|
-
continue;
|
|
1776
|
-
}
|
|
1777
|
-
const placement = preview.dependencyPlan?.targets.find((target) => target.pluginId === consumer.pluginId && target.platform === consumer.platform
|
|
1778
|
-
&& (target.requestedScope ?? target.resolvedScope) === consumer.scope);
|
|
1779
|
-
if (placement)
|
|
1780
|
-
pending.push(placement);
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
if (mappedRoots.size === 0) {
|
|
1784
|
-
throw new TuiOperationBlockedError('taskPlanConsumerMissing', '任务计划中的阻止项无法映射到请求目标。');
|
|
1785
|
-
}
|
|
1786
|
-
for (const key of mappedRoots) {
|
|
1787
|
-
const plans = blockedPlansByRoot.get(key) ?? [];
|
|
1788
|
-
plans.push(blockedPlanTarget);
|
|
1789
|
-
blockedPlansByRoot.set(key, plans);
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
blockedTargets.push(...draft.executableTargets.flatMap((target) => {
|
|
1793
|
-
const plans = blockedPlansByRoot.get(pluginWriteTaskTargetKey(target));
|
|
1794
|
-
return plans ? [{
|
|
1795
|
-
target,
|
|
1796
|
-
reason: 'targetUnavailable',
|
|
1797
|
-
issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
|
|
1798
|
-
.map((cause) => cause.issueId),
|
|
1799
|
-
errorCodes: [...new Set(plans.map((plan) => plan.errorCode ?? 'writePlanBlocked'))]
|
|
1800
|
-
.sort((left, right) => left.localeCompare(right, 'en')),
|
|
1801
|
-
diagnostics: plans.map((plan, index) => ({
|
|
1802
|
-
id: `task-plan:${sourceId}:${target.pluginId}:${target.platform}:${target.scope}:${index}`,
|
|
1803
|
-
code: plan.errorCode ?? 'writePlanBlocked',
|
|
1804
|
-
stage: 'dependencyPlan',
|
|
1805
|
-
sourceId,
|
|
1806
|
-
target: {
|
|
1807
|
-
pluginId: plan.pluginId, platform: plan.platform,
|
|
1808
|
-
scope: plan.requestedScope ?? plan.resolvedScope,
|
|
1809
|
-
},
|
|
1810
|
-
causes: [],
|
|
1811
|
-
detailLines: [],
|
|
1812
|
-
safeMessage: plan.errorCode === 'artifactCollision'
|
|
1813
|
-
? '输出位置存在外来内容,无法覆盖。'
|
|
1814
|
-
: plan.errorCode === 'exclusiveOwnerEvidenceInvalid'
|
|
1815
|
-
? '独立扩展的归属标记缺失或损坏。'
|
|
1816
|
-
: plan.errorCode === 'dependencyNotReady'
|
|
1817
|
-
? '依赖安装状态无法读取。'
|
|
1818
|
-
: '依赖计划阻止了此目标。',
|
|
1819
|
-
})),
|
|
1820
|
-
}] : [];
|
|
1821
|
-
}));
|
|
1822
|
-
draft.executableTargets = draft.executableTargets.filter((target) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(target)));
|
|
1823
|
-
draft.causes = draft.causes.map((cause) => ({
|
|
1824
|
-
...cause,
|
|
1825
|
-
consumers: cause.consumers.filter((consumer) => !blockedPlansByRoot.has(pluginWriteTaskTargetKey(consumer))),
|
|
1826
|
-
})).filter((cause) => cause.consumers.length > 0);
|
|
1827
|
-
}
|
|
1828
|
-
if (!preview?.operationPlan || draft.executableTargets.length === 0)
|
|
1829
|
-
continue;
|
|
1830
|
-
const sourceIdentity = draft.causes[0]?.sourceIdentity
|
|
1831
|
-
?? status.payload.catalog.sources.find((source) => source.id === sourceId)?.identity;
|
|
1832
|
-
if (!sourceIdentity) {
|
|
1833
|
-
throw new TuiOperationBlockedError('taskSourceIdentityMissing', '任务来源身份不可验证。');
|
|
1834
|
-
}
|
|
1835
|
-
const sourceEvidence = {
|
|
1836
|
-
sourceId,
|
|
1837
|
-
sourceIdentity,
|
|
1838
|
-
requestedTargets: draft.executableTargets,
|
|
1839
|
-
causes: draft.causes,
|
|
1840
|
-
preview,
|
|
1841
|
-
executable: true,
|
|
1842
|
-
};
|
|
1843
|
-
sources.push(sourceEvidence);
|
|
1844
|
-
executableTargets.push(...draft.executableTargets);
|
|
1845
|
-
causes.push(...draft.causes);
|
|
1846
|
-
}
|
|
1847
|
-
const canonicalBlocked = [...new Map(blockedTargets.map((target) => [
|
|
1848
|
-
pluginWriteTaskTargetKey(target.target), target,
|
|
1849
|
-
])).values()].sort((left, right) => pluginWriteTaskTargetKey(left.target)
|
|
1850
|
-
.localeCompare(pluginWriteTaskTargetKey(right.target), 'en'));
|
|
1851
|
-
if (executableTargets.length === 0) {
|
|
1852
|
-
throw new TuiOperationBlockedError('taskNoExecutableTargets', '任务没有可安全执行的目标。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1853
|
-
}
|
|
1854
|
-
if (intent.executionPolicy === 'all' && canonicalBlocked.length > 0) {
|
|
1855
|
-
throw new TuiOperationBlockedError('taskHasBlockedTargets', '部分目标需要先处理。请调整选择。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
|
|
1856
|
-
}
|
|
1857
|
-
await preparation?.reporter.emit('evidence');
|
|
1858
|
-
const evidence = {
|
|
1859
|
-
catalog: {
|
|
1860
|
-
configDigest: configSnapshot.digest,
|
|
1861
|
-
compositeDigest: digest(sources.map((source) => [
|
|
1862
|
-
source.sourceId,
|
|
1863
|
-
source.sourceIdentity,
|
|
1864
|
-
source.preview.operationPlan?.planFingerprint,
|
|
1865
|
-
])),
|
|
1866
|
-
},
|
|
1867
|
-
taskPlan: {
|
|
1868
|
-
goal: intent.goal,
|
|
1869
|
-
requestedTargets: intent.targets,
|
|
1870
|
-
executableTargets: [...executableTargets].sort((left, right) => pluginWriteTaskTargetKey(left).localeCompare(pluginWriteTaskTargetKey(right), 'en')),
|
|
1871
|
-
causes: [...new Map(causes.map((cause) => [cause.causeKey, cause])).values()]
|
|
1872
|
-
.sort((left, right) => left.causeKey.localeCompare(right.causeKey, 'en')),
|
|
1873
|
-
blockedTargets: canonicalBlocked,
|
|
1874
|
-
sources,
|
|
1875
|
-
targetFingerprint: statusTargetFingerprint(observedTargets, observedRevisions),
|
|
1876
|
-
planFingerprint: '',
|
|
1877
|
-
},
|
|
1878
|
-
};
|
|
1879
|
-
evidence.taskPlan.planFingerprint = operationEvidenceFingerprint(evidence);
|
|
1880
|
-
taskCatalogOperationsByEvidence.set(evidence, operationContexts);
|
|
1881
|
-
return evidence;
|
|
1882
|
-
}
|
|
1883
|
-
if (intent.kind === 'pluginWriteBatch') {
|
|
1884
|
-
await preparation?.reporter.emit('status');
|
|
1885
|
-
const selection = await resolveBatchGroups(intent, context.env, context.cwd, frozenBatchEvidence?.requestedPluginIds);
|
|
1886
|
-
signal?.throwIfAborted();
|
|
1887
|
-
if (intent.action === 'cleanup') {
|
|
1888
|
-
const groups = [];
|
|
1889
|
-
const reviewedRemovedPlugins = new Set();
|
|
1890
|
-
for (const group of selection.groups) {
|
|
1891
|
-
signal?.throwIfAborted();
|
|
1892
|
-
const requestedPluginIds = group.requestedPluginIds.filter((pluginId) => !reviewedRemovedPlugins.has(pluginId));
|
|
1893
|
-
if (requestedPluginIds.length === 0)
|
|
1894
|
-
continue;
|
|
1895
|
-
const preview = await runBatchWriteGroup({
|
|
1896
|
-
intent,
|
|
1897
|
-
group: { ...group, requestedPluginIds },
|
|
1898
|
-
cwd: context.cwd,
|
|
1899
|
-
runtime: {
|
|
1900
|
-
...context,
|
|
1901
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1902
|
-
previewOperationPlan: true,
|
|
1903
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
1904
|
-
...(preparation ? {
|
|
1905
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1906
|
-
} : {}),
|
|
1907
|
-
},
|
|
1908
|
-
});
|
|
1909
|
-
for (const pluginId of preview.removedPluginCleanupIds ?? []) {
|
|
1910
|
-
reviewedRemovedPlugins.add(pluginId);
|
|
1911
|
-
}
|
|
1912
|
-
groups.push({ ...group, requestedPluginIds, preview,
|
|
1913
|
-
executable: preview.operationPlan !== undefined });
|
|
1914
|
-
}
|
|
1915
|
-
await preparation?.reporter.emit('evidence');
|
|
1916
|
-
signal?.throwIfAborted();
|
|
1917
|
-
const evidence = {
|
|
1918
|
-
catalog: {
|
|
1919
|
-
configDigest: 'cleanup-offline',
|
|
1920
|
-
compositeDigest: digest(groups.map((group) => [
|
|
1921
|
-
group.groupId,
|
|
1922
|
-
group.preview.operationPlan?.planFingerprint,
|
|
1923
|
-
])),
|
|
1924
|
-
},
|
|
1925
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
1926
|
-
groups,
|
|
1927
|
-
};
|
|
1928
|
-
return evidence;
|
|
1929
|
-
}
|
|
1930
|
-
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
1931
|
-
const groups = [];
|
|
1932
|
-
const ownsObservationSession = !codexObservationSession
|
|
1933
|
-
&& selection.groups.some((group) => {
|
|
1934
|
-
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1935
|
-
return reviewed?.executable !== false
|
|
1936
|
-
&& (group.platform === 'codex' || group.platform === 'both');
|
|
1937
|
-
});
|
|
1938
|
-
const observationSession = codexObservationSession ?? (ownsObservationSession
|
|
1939
|
-
? await createCodexNativeObservationSession({
|
|
1940
|
-
exactCwd: context.cwd,
|
|
1941
|
-
env: context.env,
|
|
1942
|
-
runner: context.nativeCliRunner,
|
|
1943
|
-
})
|
|
1944
|
-
: undefined);
|
|
1945
|
-
try {
|
|
1946
|
-
await preparation?.reporter.emit('catalog');
|
|
1947
|
-
for (const group of selection.groups) {
|
|
1948
|
-
signal?.throwIfAborted();
|
|
1949
|
-
const reviewed = frozenBatchEvidence?.groups.find((candidate) => candidate.groupId === group.groupId);
|
|
1950
|
-
if (reviewed?.executable === false) {
|
|
1951
|
-
groups.push({ ...group, preview: reviewed.preview, executable: false });
|
|
1952
|
-
continue;
|
|
1953
|
-
}
|
|
1954
|
-
let catalogOperation;
|
|
1955
|
-
try {
|
|
1956
|
-
catalogOperation = !freshCatalog && sessionCatalogOperationContext
|
|
1957
|
-
? selectCatalogOperationContext(sessionCatalogOperationContext, [group.sourceId])
|
|
1958
|
-
: await resolveCatalogOperationContext({
|
|
1959
|
-
sourceIds: [group.sourceId],
|
|
1960
|
-
env: context.env,
|
|
1961
|
-
...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
|
|
1962
|
-
...(signal ? { signal } : {}),
|
|
1963
|
-
});
|
|
1964
|
-
}
|
|
1965
|
-
catch {
|
|
1966
|
-
catalogOperation = undefined;
|
|
1967
|
-
}
|
|
1968
|
-
const preview = await runBatchWriteGroup({
|
|
1969
|
-
intent,
|
|
1970
|
-
group,
|
|
1971
|
-
cwd: context.cwd,
|
|
1972
|
-
runtime: {
|
|
1973
|
-
...context,
|
|
1974
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
1975
|
-
expectedCatalogConfigDigest: configSnapshot.digest,
|
|
1976
|
-
...(catalogOperation ? {
|
|
1977
|
-
catalogOperationContext: catalogOperation,
|
|
1978
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
1979
|
-
} : {}),
|
|
1980
|
-
previewOperationPlan: true,
|
|
1981
|
-
...(observationSession ? { codexObservationSession: observationSession } : {}),
|
|
1982
|
-
...(preparation ? {
|
|
1983
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
1984
|
-
} : {}),
|
|
1985
|
-
},
|
|
1986
|
-
});
|
|
1987
|
-
groups.push({ ...group, preview, executable: preview.operationPlan !== undefined });
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
finally {
|
|
1991
|
-
if (ownsObservationSession)
|
|
1992
|
-
await observationSession?.dispose();
|
|
1993
|
-
}
|
|
1994
|
-
await preparation?.reporter.emit('evidence');
|
|
1995
|
-
signal?.throwIfAborted();
|
|
1996
|
-
const merged = mergeWriteCommandResults(groups.map((group) => group.preview));
|
|
1997
|
-
const targetVersions = groups.flatMap((group) => {
|
|
1998
|
-
if (group.platform === 'both')
|
|
1999
|
-
return [];
|
|
2000
|
-
const platform = group.platform;
|
|
2001
|
-
return group.requestedPluginIds.map((pluginId) => {
|
|
2002
|
-
const live = flattenStatusTargets(sessionStatusResult?.payload.targets ?? []).find((target) => target.pluginId === pluginId && target.platform === platform
|
|
2003
|
-
&& (target.scope === group.scope || statusTargetIntent(target).scopes.includes(group.scope)
|
|
2004
|
-
|| platform === 'codex' && statusTargetActivation(target)?.[group.scope] === true));
|
|
2005
|
-
const revision = sessionStatusResult?.targetRevisions?.find((target) => target.pluginId === pluginId && target.platform === platform
|
|
2006
|
-
&& target.scope === group.scope)?.revision;
|
|
2007
|
-
const activation = live ? statusTargetActivation(live) : undefined;
|
|
2008
|
-
const ownership = live && live.scope === group.scope
|
|
2009
|
-
? statusTargetOwnershipState(live) : 'absent';
|
|
2010
|
-
const present = ownership === 'present' || ownership === 'partial'
|
|
2011
|
-
|| platform === 'codex' && activation?.[group.scope] === true;
|
|
2012
|
-
const planned = live ? statusTargetIntent(live).scopes.includes(group.scope) : false;
|
|
2013
|
-
return {
|
|
2014
|
-
pluginId,
|
|
2015
|
-
platform,
|
|
2016
|
-
scope: group.scope,
|
|
2017
|
-
presence: present ? 'installed'
|
|
2018
|
-
: ownership === 'absent' && planned ? 'new' : 'unknown',
|
|
2019
|
-
...(revision?.installedVersion ? { installedVersion: revision.installedVersion } : {}),
|
|
2020
|
-
...(revision?.availableVersion ? { availableVersion: revision.availableVersion } : {}),
|
|
2021
|
-
};
|
|
2022
|
-
});
|
|
2023
|
-
});
|
|
2024
|
-
const evidence = {
|
|
2025
|
-
catalog: {
|
|
2026
|
-
configDigest: configSnapshot.digest,
|
|
2027
|
-
compositeDigest: sessionStatusResult?.payload.catalog.compositeDigest
|
|
2028
|
-
?? digest(groups.map((group) => [group.groupId, group.preview.operationPlan?.planFingerprint])),
|
|
2029
|
-
},
|
|
2030
|
-
requestedPluginIds: selection.requestedPluginIds,
|
|
2031
|
-
...(selection.projectIntentDigest
|
|
2032
|
-
? { projectIntentDigest: selection.projectIntentDigest }
|
|
2033
|
-
: {}),
|
|
2034
|
-
groups,
|
|
2035
|
-
targetVersions: [...new Map(targetVersions.map((target) => [[
|
|
2036
|
-
target.pluginId, target.platform, target.scope,
|
|
2037
|
-
].join('\0'), target])).values()].sort((left, right) => [
|
|
2038
|
-
left.pluginId, left.platform, left.scope,
|
|
2039
|
-
].join('\0').localeCompare([
|
|
2040
|
-
right.pluginId, right.platform, right.scope,
|
|
2041
|
-
].join('\0'), 'en')),
|
|
2042
|
-
...(merged.dependencyPlan ? { dependencyPlan: merged.dependencyPlan } : {}),
|
|
2043
|
-
...(merged.codexReconcilePlans ? { codexReconcilePlans: merged.codexReconcilePlans } : {}),
|
|
2044
|
-
...(merged.operationPlan ? { operationPlan: merged.operationPlan } : {}),
|
|
2045
|
-
};
|
|
2046
|
-
return evidence;
|
|
2047
|
-
}
|
|
2048
|
-
if (intent.kind === 'pluginWrite' && intent.action === 'cleanup') {
|
|
2049
|
-
const preview = await cleanupWriteCommand([intent.pluginId], {
|
|
2050
|
-
cwd: context.cwd,
|
|
2051
|
-
platform: intent.platform,
|
|
2052
|
-
scope: intent.scope,
|
|
2053
|
-
force: intent.force === true,
|
|
2054
|
-
}, {
|
|
2055
|
-
...context,
|
|
2056
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
2057
|
-
previewOperationPlan: true,
|
|
2058
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2059
|
-
...(preparation ? {
|
|
2060
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
2061
|
-
} : {}),
|
|
2062
|
-
});
|
|
2063
|
-
signal?.throwIfAborted();
|
|
2064
|
-
if (!preview.operationPlan) {
|
|
2065
|
-
throw new TuiOperationBlockedError('cleanupPlanUnavailable', '无法建立可信的清理计划。');
|
|
2066
|
-
}
|
|
2067
|
-
const offlineCleanupPlans = preview.offlineCleanupPlans ?? [];
|
|
2068
|
-
await preparation?.reporter.emit('evidence');
|
|
2069
|
-
return {
|
|
2070
|
-
catalog: {
|
|
2071
|
-
configDigest: 'cleanup-offline',
|
|
2072
|
-
compositeDigest: preview.operationPlan.planFingerprint,
|
|
2073
|
-
},
|
|
2074
|
-
operationPlan: preview.operationPlan,
|
|
2075
|
-
offlineCleanupPlans,
|
|
2076
|
-
...(preview.removedPluginCleanupIds
|
|
2077
|
-
? { removedPluginCleanupIds: preview.removedPluginCleanupIds }
|
|
2078
|
-
: {}),
|
|
2079
|
-
...(offlineCleanupPlans.length === 1
|
|
2080
|
-
? { offlineCleanup: offlineCleanupPlans[0] }
|
|
2081
|
-
: {}),
|
|
2082
|
-
targetFingerprint: preview.operationPlan.planFingerprint,
|
|
2083
|
-
};
|
|
2084
|
-
}
|
|
2085
1264
|
const packageInfo = await readCliPackageInfo();
|
|
2086
1265
|
let catalogOperation;
|
|
2087
1266
|
let catalogOperationError;
|
|
@@ -2153,13 +1332,97 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2153
1332
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2154
1333
|
return evidence;
|
|
2155
1334
|
}
|
|
2156
|
-
|
|
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
|
+
}
|
|
1404
|
+
const needsReferencePlugins = intent.kind === 'configSave'
|
|
1405
|
+
&& await withResolvedCatalogPlugin({
|
|
1406
|
+
selector: intent.pluginId, cliVersion: packageInfo.version, env: context.env,
|
|
1407
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
1408
|
+
...(signal ? { signal } : {}),
|
|
1409
|
+
consume: ({ plugin }) => configNeedsReferencePlugins(plugin),
|
|
1410
|
+
});
|
|
1411
|
+
const referencePlugins = needsReferencePlugins
|
|
1412
|
+
? await resolvePluginConfigReferencePlugins({
|
|
1413
|
+
sourceId: parsePluginId(intent.pluginId).sourceId,
|
|
1414
|
+
env: context.env,
|
|
1415
|
+
operationContext: catalogOperation,
|
|
1416
|
+
})
|
|
1417
|
+
: undefined;
|
|
1418
|
+
const configReferencePluginIds = intent.kind === 'configSave' && needsReferencePlugins
|
|
2157
1419
|
? await withResolvedCatalogPlugin({
|
|
2158
1420
|
selector: intent.pluginId, cliVersion: packageInfo.version, env: context.env,
|
|
2159
1421
|
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
2160
1422
|
...(signal ? { signal } : {}),
|
|
2161
|
-
consume: async ({ plugin }) => (await resolvePluginConfigReferenceConsumers({
|
|
2162
|
-
|
|
1423
|
+
consume: async ({ plugin }) => (await resolvePluginConfigReferenceConsumers({
|
|
1424
|
+
plugin, env: context.env, plugins: referencePlugins,
|
|
1425
|
+
})).map((entry) => entry.pluginId ?? `${entry.sourceId}/${entry.manifest.name}`),
|
|
2163
1426
|
})
|
|
2164
1427
|
: [];
|
|
2165
1428
|
if (preparation && intent.kind === 'configSave') {
|
|
@@ -2172,7 +1435,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2172
1435
|
if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
|
|
2173
1436
|
throw operationReviewStale('准备设置确认期间来源配置发生变化。');
|
|
2174
1437
|
}
|
|
2175
|
-
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.
|
|
1438
|
+
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.context ?? 'user', configReferencePluginIds, intent.platform);
|
|
2176
1439
|
const preparedCatalogOperation = catalogOperation;
|
|
2177
1440
|
const selected = await withResolvedCatalogPlugin({
|
|
2178
1441
|
selector: intent.pluginId,
|
|
@@ -2185,21 +1448,23 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2185
1448
|
if (!plugin.manifest.config) {
|
|
2186
1449
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2187
1450
|
}
|
|
1451
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2188
1452
|
if (intent.kind === 'configSave') {
|
|
2189
1453
|
await coercePluginConfigUpdates({
|
|
2190
|
-
schema:
|
|
1454
|
+
schema: definition.fields,
|
|
2191
1455
|
updates: intent.updates ?? {},
|
|
2192
1456
|
cwd: context.cwd,
|
|
2193
1457
|
});
|
|
2194
1458
|
for (const key of intent.removeKeys ?? []) {
|
|
2195
|
-
if (!
|
|
1459
|
+
if (!definition.fields[key]) {
|
|
2196
1460
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2197
1461
|
}
|
|
2198
1462
|
}
|
|
2199
1463
|
}
|
|
2200
1464
|
const impact = intent.retryApply ? undefined : await analyzePluginConfigMutationImpact({
|
|
2201
1465
|
plugin,
|
|
2202
|
-
|
|
1466
|
+
referencePlugins,
|
|
1467
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2203
1468
|
cwd: context.cwd,
|
|
2204
1469
|
env: context.env,
|
|
2205
1470
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, relevantTargets),
|
|
@@ -2213,17 +1478,18 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2213
1478
|
? configImpactVisibleTargets(plugin.manifest.name, relevantTargets)
|
|
2214
1479
|
: impact.affectedTargets;
|
|
2215
1480
|
const commandPackages = impact?.affectedCommandPackages
|
|
2216
|
-
?? (await discoverPluginConfigManagedCommandPackages({ plugin, scope: intent.
|
|
2217
|
-
cwd: context.cwd, env: context.env ?? process.env, platformTargets: affectedTargets })).packages;
|
|
1481
|
+
?? (await discoverPluginConfigManagedCommandPackages({ plugin, scope: intent.context ?? 'user',
|
|
1482
|
+
cwd: context.cwd, env: context.env ?? process.env, platformTargets: affectedTargets, referencePlugins })).packages;
|
|
2218
1483
|
const preApplyAuthorization = configIntentRequestsApply(intent)
|
|
2219
1484
|
? (await resolveConfigPreApplyAuthorization({
|
|
2220
1485
|
plugin,
|
|
2221
|
-
scope: intent.
|
|
1486
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2222
1487
|
cwd: context.cwd,
|
|
2223
1488
|
env: context.env,
|
|
2224
1489
|
applyTargets: affectedTargets,
|
|
2225
1490
|
nativeCliRunner: context.nativeCliRunner,
|
|
2226
1491
|
rebuildRuntime: {
|
|
1492
|
+
referencePlugins,
|
|
2227
1493
|
catalogOperationContext: preparedCatalogOperation,
|
|
2228
1494
|
expectedGenerationSetDigest: preparedCatalogOperation.generationSetDigest,
|
|
2229
1495
|
inventoryCatalogSourceReferences: operationInventory,
|
|
@@ -2233,13 +1499,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2233
1499
|
return {
|
|
2234
1500
|
pluginName: plugin.manifest.name,
|
|
2235
1501
|
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
1502
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, intent.platform),
|
|
2236
1503
|
impact,
|
|
2237
1504
|
commandPackages,
|
|
2238
1505
|
preApplyAuthorization,
|
|
2239
|
-
retryStoreDigest: intent.retryApply ? await configRetryStoreDigest({ plugin, scope: intent.
|
|
2240
|
-
targets: affectedTargets, commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env }) : 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,
|
|
2241
1508
|
sharedNativeRenderTargets: intent.retryApply
|
|
2242
|
-
? await configRetrySharedNativeRenderTargets(plugin, affectedTargets, context.env)
|
|
1509
|
+
? await configRetrySharedNativeRenderTargets(plugin, affectedTargets, context.env, referencePlugins)
|
|
2243
1510
|
: impact.sharedNativeRenderTargets,
|
|
2244
1511
|
};
|
|
2245
1512
|
},
|
|
@@ -2258,8 +1525,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2258
1525
|
...(selected.source ? { source: selected.source } : {}),
|
|
2259
1526
|
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
2260
1527
|
config: {
|
|
2261
|
-
scope: intent.
|
|
2262
|
-
|
|
1528
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1529
|
+
schemaIdentity: selected.schemaIdentity,
|
|
2263
1530
|
storeDigest: selected.impact?.storeDigest
|
|
2264
1531
|
?? selected.retryStoreDigest,
|
|
2265
1532
|
impactState: selected.impact?.impactState ?? 'known',
|
|
@@ -2275,6 +1542,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2275
1542
|
: {}),
|
|
2276
1543
|
},
|
|
2277
1544
|
};
|
|
1545
|
+
await assertConfigEditEvidence(intent, evidence);
|
|
2278
1546
|
if (catalogOperation)
|
|
2279
1547
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2280
1548
|
return evidence;
|
|
@@ -2283,17 +1551,11 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2283
1551
|
const statusCatalogOperation = catalogOperation
|
|
2284
1552
|
?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
|
|
2285
1553
|
const status = await statusCommandV8({
|
|
2286
|
-
plugins: [...new Set([intent.pluginId, ...configReferencePluginIds,
|
|
2287
|
-
|
|
2288
|
-
? [intent.originIssue.consumerPluginId] : [])])],
|
|
2289
|
-
...('platform' in intent ? { platform: intent.platform } : {}),
|
|
2290
|
-
...(intent.kind !== 'pluginWrite' && intent.scope === 'project'
|
|
2291
|
-
? { scope: intent.scope }
|
|
2292
|
-
: {}),
|
|
1554
|
+
plugins: [...new Set([intent.pluginId, ...configReferencePluginIds])],
|
|
1555
|
+
...(intent.context === 'project' ? {} : { scope: 'user' }),
|
|
2293
1556
|
cwd: context.cwd,
|
|
2294
1557
|
env: context.env,
|
|
2295
1558
|
nativeCliRunner: context.nativeCliRunner,
|
|
2296
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2297
1559
|
cliVersion: packageInfo.version,
|
|
2298
1560
|
...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
|
|
2299
1561
|
inventoryCatalogSourceReferences: operationInventory,
|
|
@@ -2315,12 +1577,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2315
1577
|
...(signal ? { signal } : {}),
|
|
2316
1578
|
});
|
|
2317
1579
|
}
|
|
2318
|
-
let dependencyPlan;
|
|
2319
|
-
let codexReconcilePlans;
|
|
2320
|
-
let operationPlan;
|
|
2321
1580
|
await preparation?.reporter.emit('catalog');
|
|
2322
1581
|
const configVisibleTargets = intent.kind === 'configSave'
|
|
2323
|
-
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.
|
|
1582
|
+
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.context ?? 'user', configReferencePluginIds, intent.platform)
|
|
2324
1583
|
: [];
|
|
2325
1584
|
const selected = await withResolvedCatalogPlugin({
|
|
2326
1585
|
selector: intent.pluginId,
|
|
@@ -2335,13 +1594,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2335
1594
|
if (!plugin.manifest.config) {
|
|
2336
1595
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2337
1596
|
}
|
|
1597
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2338
1598
|
await coercePluginConfigUpdates({
|
|
2339
|
-
schema:
|
|
1599
|
+
schema: definition.fields,
|
|
2340
1600
|
updates: intent.updates ?? {},
|
|
2341
1601
|
cwd: context.cwd,
|
|
2342
1602
|
});
|
|
2343
1603
|
for (const key of intent.removeKeys ?? []) {
|
|
2344
|
-
if (!
|
|
1604
|
+
if (!definition.fields[key]) {
|
|
2345
1605
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2346
1606
|
}
|
|
2347
1607
|
}
|
|
@@ -2349,7 +1609,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2349
1609
|
const impact = intent.kind === 'configSave' && !intent.retryApply
|
|
2350
1610
|
? await analyzePluginConfigMutationImpact({
|
|
2351
1611
|
plugin,
|
|
2352
|
-
|
|
1612
|
+
referencePlugins,
|
|
1613
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2353
1614
|
cwd: context.cwd,
|
|
2354
1615
|
env: context.env,
|
|
2355
1616
|
visibleTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
@@ -2365,14 +1626,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2365
1626
|
: [];
|
|
2366
1627
|
const commandPackages = impact?.affectedCommandPackages
|
|
2367
1628
|
?? (intent.kind === 'configSave' ? (await discoverPluginConfigManagedCommandPackages({
|
|
2368
|
-
plugin, scope: intent.
|
|
1629
|
+
plugin, scope: intent.context ?? 'user', cwd: context.cwd, env: context.env ?? process.env,
|
|
2369
1630
|
platformTargets: configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets),
|
|
1631
|
+
referencePlugins,
|
|
2370
1632
|
})).packages : []);
|
|
2371
1633
|
const preApplyAuthorization = intent.kind === 'configSave'
|
|
2372
1634
|
&& configIntentRequestsApply(intent)
|
|
2373
1635
|
? (await resolveConfigPreApplyAuthorization({
|
|
2374
1636
|
plugin,
|
|
2375
|
-
scope: intent.
|
|
1637
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
2376
1638
|
cwd: context.cwd,
|
|
2377
1639
|
env: context.env,
|
|
2378
1640
|
applyTargets: intent.retryApply
|
|
@@ -2380,6 +1642,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2380
1642
|
: impact.affectedTargets,
|
|
2381
1643
|
nativeCliRunner: context.nativeCliRunner,
|
|
2382
1644
|
rebuildRuntime: {
|
|
1645
|
+
referencePlugins,
|
|
2383
1646
|
...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
|
|
2384
1647
|
...(catalogOperation?.generationSetDigest
|
|
2385
1648
|
? { expectedGenerationSetDigest: catalogOperation.generationSetDigest }
|
|
@@ -2391,120 +1654,21 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2391
1654
|
return {
|
|
2392
1655
|
pluginName: plugin.manifest.name,
|
|
2393
1656
|
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
1657
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, intent.platform),
|
|
2394
1658
|
impact,
|
|
2395
1659
|
commandPackages,
|
|
2396
1660
|
preApplyAuthorization,
|
|
2397
1661
|
retryStoreDigest: intent.kind === 'configSave' && intent.retryApply
|
|
2398
|
-
? await configRetryStoreDigest({ plugin, scope: intent.
|
|
2399
|
-
commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env }) : undefined,
|
|
1662
|
+
? await configRetryStoreDigest({ plugin, scope: intent.context ?? 'user', platform: intent.platform, targets: affectedTargets,
|
|
1663
|
+
commandPackages, preApplyAuthorization, cwd: context.cwd, env: context.env, plugins: referencePlugins }) : undefined,
|
|
2400
1664
|
sharedNativeRenderTargets: intent.kind === 'configSave'
|
|
2401
1665
|
? intent.retryApply
|
|
2402
|
-
? await configRetrySharedNativeRenderTargets(plugin, configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets), context.env)
|
|
1666
|
+
? await configRetrySharedNativeRenderTargets(plugin, configImpactVisibleTargets(plugin.manifest.name, configVisibleTargets), context.env, referencePlugins)
|
|
2403
1667
|
: impact.sharedNativeRenderTargets
|
|
2404
1668
|
: [],
|
|
2405
1669
|
};
|
|
2406
1670
|
},
|
|
2407
1671
|
});
|
|
2408
|
-
if (intent.kind === 'pluginWrite' && intent.expectedUpdate) {
|
|
2409
|
-
const revision = status.targetRevisions.find((entry) => entry.pluginId === intent.pluginId && entry.platform === intent.platform
|
|
2410
|
-
&& entry.scope === intent.scope);
|
|
2411
|
-
const generation = catalogOperation?.generationSet.sources.find((entry) => entry.sourceId === selected.source.sourceId);
|
|
2412
|
-
if (intent.action !== 'sync'
|
|
2413
|
-
|| !('pluginVersion' in selected.source)
|
|
2414
|
-
|| selected.source.sourceIdentity !== intent.expectedUpdate.sourceIdentity
|
|
2415
|
-
|| selected.source.pluginVersion !== intent.expectedUpdate.activeVersion
|
|
2416
|
-
|| generation?.storeKey !== intent.expectedUpdate.generationKey
|
|
2417
|
-
|| revision?.revision.installedVersion !== intent.expectedUpdate.installedVersion
|
|
2418
|
-
|| revision?.revision.availableVersion !== intent.expectedUpdate.activeVersion
|
|
2419
|
-
|| revision.revision.state !== 'upgradeAvailable') {
|
|
2420
|
-
throw operationReviewStale('插件更新版本或 Active Catalog 证据已变化。');
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
let codexManager;
|
|
2424
|
-
let resolvedCodexManager;
|
|
2425
|
-
const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
|
|
2426
|
-
if (intent.kind === 'pluginWrite') {
|
|
2427
|
-
const command = {
|
|
2428
|
-
install: installWriteCommand,
|
|
2429
|
-
refreshAndSync: syncWriteCommand,
|
|
2430
|
-
reinstall: reinstallWriteCommand,
|
|
2431
|
-
uninstall: uninstallWriteCommand,
|
|
2432
|
-
cleanup: cleanupWriteCommand,
|
|
2433
|
-
sync: syncWriteCommand,
|
|
2434
|
-
}[intent.action];
|
|
2435
|
-
const preview = await command([intent.pluginId], {
|
|
2436
|
-
cwd: context.cwd,
|
|
2437
|
-
platform: intent.platform,
|
|
2438
|
-
scope: intent.scope,
|
|
2439
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
2440
|
-
? {}
|
|
2441
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
2442
|
-
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
2443
|
-
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
2444
|
-
: {}),
|
|
2445
|
-
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
2446
|
-
}, {
|
|
2447
|
-
...context,
|
|
2448
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
2449
|
-
expectedCatalogConfigDigest: status.payload.catalog.configDigest,
|
|
2450
|
-
...(catalogOperation ? {
|
|
2451
|
-
catalogOperationContext: catalogOperation,
|
|
2452
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
2453
|
-
} : {}),
|
|
2454
|
-
previewOperationPlan: true,
|
|
2455
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
2456
|
-
...(preparation ? {
|
|
2457
|
-
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
2458
|
-
} : {}),
|
|
2459
|
-
});
|
|
2460
|
-
signal?.throwIfAborted();
|
|
2461
|
-
codexReconcilePlans = preview?.codexReconcilePlans;
|
|
2462
|
-
operationPlan = preview?.operationPlan;
|
|
2463
|
-
if (hasDeclaredDependencies
|
|
2464
|
-
&& (intent.action === 'install'
|
|
2465
|
-
|| intent.action === 'refreshAndSync'
|
|
2466
|
-
|| intent.action === 'reinstall'
|
|
2467
|
-
|| intent.action === 'sync')) {
|
|
2468
|
-
dependencyPlan = preview.dependencyPlan;
|
|
2469
|
-
if (!dependencyPlan) {
|
|
2470
|
-
throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
|
|
2471
|
-
}
|
|
2472
|
-
const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
|
|
2473
|
-
if (blocker) {
|
|
2474
|
-
const code = blocker.errorCode ?? 'dependencyNotReady';
|
|
2475
|
-
const reason = code === 'dependencyScopeConflict' || code === 'codexExtensionScopeConflict'
|
|
2476
|
-
? '这个依赖的安装位置存在冲突,需要先选择保留的位置。'
|
|
2477
|
-
: code === 'artifactCollision' || code === 'exclusiveOwnerEvidenceInvalid'
|
|
2478
|
-
? '这个依赖的安装位置被其他内容占用,需要先检查并处理冲突。'
|
|
2479
|
-
: '这个依赖尚不可用,需要先检查并恢复后再安装。';
|
|
2480
|
-
throw new TuiOperationBlockedError(code, `${blocker.pluginId}:${reason}`, [{
|
|
2481
|
-
id: `dependency:${blocker.pluginId}:${blocker.platform}:${blocker.resolvedScope}`,
|
|
2482
|
-
code, stage: 'dependencyPlan', safeMessage: reason,
|
|
2483
|
-
target: { pluginId: blocker.pluginId, platform: blocker.platform, scope: blocker.resolvedScope },
|
|
2484
|
-
causes: [], detailLines: [],
|
|
2485
|
-
}]);
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
const managerPreview = preview?.codexManagerPreview;
|
|
2489
|
-
if (managerPreview?.state === 'blocked') {
|
|
2490
|
-
const message = managerPreview.code === 'codexSystemCliUnsupported'
|
|
2491
|
-
? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
2492
|
-
: 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
|
|
2493
|
-
throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
|
|
2494
|
-
}
|
|
2495
|
-
if (managerPreview) {
|
|
2496
|
-
if (managerPreview.state === 'system' || managerPreview.state === 'cached') {
|
|
2497
|
-
resolvedCodexManager = managerPreview.manager;
|
|
2498
|
-
}
|
|
2499
|
-
codexManager = {
|
|
2500
|
-
state: managerPreview.state,
|
|
2501
|
-
packageName: MANAGED_CODEX_PACKAGE_NAME,
|
|
2502
|
-
version: managerPreview.state === 'requires-install'
|
|
2503
|
-
? MANAGED_CODEX_VERSION
|
|
2504
|
-
: managerPreview.manager.version,
|
|
2505
|
-
};
|
|
2506
|
-
}
|
|
2507
|
-
}
|
|
2508
1672
|
await preparation?.reporter.emit('evidence');
|
|
2509
1673
|
signal?.throwIfAborted();
|
|
2510
1674
|
const evidenceTargets = intent.kind === 'configSave'
|
|
@@ -2524,10 +1688,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2524
1688
|
: {}),
|
|
2525
1689
|
},
|
|
2526
1690
|
source: selected.source,
|
|
2527
|
-
...(dependencyPlan ? { dependencyPlan } : {}),
|
|
2528
|
-
...(codexReconcilePlans ? { codexReconcilePlans } : {}),
|
|
2529
|
-
...(operationPlan ? { operationPlan } : {}),
|
|
2530
|
-
...(codexManager ? { codexManager } : {}),
|
|
2531
1691
|
targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
|
|
2532
1692
|
};
|
|
2533
1693
|
if (intent.kind === 'configSave') {
|
|
@@ -2537,10 +1697,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2537
1697
|
throw operationReviewStale('设置操作缺少实际配置输入证据。');
|
|
2538
1698
|
signal?.throwIfAborted();
|
|
2539
1699
|
evidence.config = {
|
|
2540
|
-
scope: intent.
|
|
2541
|
-
|
|
2542
|
-
? { schemaIdentity: selected.source.manifestDigest }
|
|
2543
|
-
: {}),
|
|
1700
|
+
scope: 'user', context: intent.context ?? 'user', platform: intent.platform,
|
|
1701
|
+
schemaIdentity: selected.schemaIdentity,
|
|
2544
1702
|
storeDigest,
|
|
2545
1703
|
impactState: impact?.impactState ?? 'known',
|
|
2546
1704
|
...(impact?.noEffectiveChange ? { noEffectiveChange: true } : {}),
|
|
@@ -2548,15 +1706,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2548
1706
|
? { commandPackages: selected.commandPackages } : {}),
|
|
2549
1707
|
affectedTargets: (impact?.affectedTargets
|
|
2550
1708
|
?? evidenceTargets).map(({ pluginName, platform, scope }) => ({ pluginName, platform, scope })),
|
|
2551
|
-
...(intent.scope === 'project' ? {
|
|
2552
|
-
shareability: await inspectConfigShareability({
|
|
2553
|
-
sourceId: selected.source.sourceId,
|
|
2554
|
-
pluginName: selected.pluginName,
|
|
2555
|
-
scope: intent.scope,
|
|
2556
|
-
cwd: context.cwd,
|
|
2557
|
-
env: context.env,
|
|
2558
|
-
}),
|
|
2559
|
-
} : {}),
|
|
2560
1709
|
...('sharedNativeRenderTargets' in selected
|
|
2561
1710
|
&& selected.sharedNativeRenderTargets.length > 0
|
|
2562
1711
|
? { sharedNativeRenderTargets: selected.sharedNativeRenderTargets }
|
|
@@ -2567,13 +1716,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2567
1716
|
: {}),
|
|
2568
1717
|
};
|
|
2569
1718
|
}
|
|
1719
|
+
await assertConfigEditEvidence(intent, evidence);
|
|
2570
1720
|
if (catalogOperation)
|
|
2571
1721
|
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
2572
|
-
if (resolvedCodexManager)
|
|
2573
|
-
codexManagerByEvidence.set(evidence, resolvedCodexManager);
|
|
2574
1722
|
return evidence;
|
|
2575
1723
|
};
|
|
2576
1724
|
const dependencies = {
|
|
1725
|
+
readAgentSettings: () => readAgentSettings(context),
|
|
1726
|
+
previewAgentSettings: (preferences, pluginId) => previewAgentSettings(context, preferences, pluginId),
|
|
1727
|
+
saveAgentSettings: (request) => saveAgentSettings(context, request),
|
|
2577
1728
|
beginCatalogSession(sessionOptions) {
|
|
2578
1729
|
sessionCatalogOperationContext = undefined;
|
|
2579
1730
|
if (sessionOptions?.reuseValidatedGenerations !== true) {
|
|
@@ -2582,6 +1733,104 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2582
1733
|
}
|
|
2583
1734
|
},
|
|
2584
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
|
+
},
|
|
2585
1834
|
loadPluginContent,
|
|
2586
1835
|
async probeCatalogFreshness(sourceId, freshnessOptions, signal) {
|
|
2587
1836
|
signal.throwIfAborted();
|
|
@@ -2630,55 +1879,42 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2630
1879
|
now: options.now ?? Date.now,
|
|
2631
1880
|
});
|
|
2632
1881
|
try {
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
const sourceId = parsePluginId(intent.pluginId).sourceId;
|
|
2638
|
-
await syncCatalogSourceGeneration({
|
|
2639
|
-
sourceId,
|
|
2640
|
-
cliVersion: packageInfo.version,
|
|
2641
|
-
cwd: context.cwd,
|
|
2642
|
-
env: context.env,
|
|
2643
|
-
signal,
|
|
2644
|
-
}).catch((error) => {
|
|
2645
|
-
preparationWarnings.push(`catalogSourceRefreshFailed: ${sourceId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2646
|
-
});
|
|
2647
|
-
sessionCatalogOperationContext = undefined;
|
|
2648
|
-
sessionStatusResult = undefined;
|
|
2649
|
-
validatedStoreEntries.clear();
|
|
2650
|
-
validatingStoreEntries.clear();
|
|
2651
|
-
}
|
|
2652
|
-
const taskObservationSession = intent.kind === 'pluginWriteTask'
|
|
2653
|
-
&& intent.targets.some((target) => target.platform === 'codex')
|
|
2654
|
-
? await createCodexNativeObservationSession({
|
|
2655
|
-
exactCwd: context.cwd,
|
|
2656
|
-
env: context.env,
|
|
2657
|
-
runner: context.nativeCliRunner,
|
|
2658
|
-
})
|
|
2659
|
-
: undefined;
|
|
2660
|
-
let evidence;
|
|
2661
|
-
try {
|
|
2662
|
-
evidence = await captureEvidence(intent, signal, {
|
|
2663
|
-
reporter,
|
|
2664
|
-
validateIntent: true,
|
|
2665
|
-
}, createOperationInventoryReader(), intent.kind === 'pluginWrite'
|
|
2666
|
-
&& intent.action === 'refreshAndSync', reusableCandidate, taskObservationSession);
|
|
2667
|
-
}
|
|
2668
|
-
finally {
|
|
2669
|
-
await taskObservationSession?.dispose();
|
|
2670
|
-
}
|
|
2671
|
-
if (preparationWarnings.length > 0)
|
|
2672
|
-
evidence.preparationWarnings = preparationWarnings;
|
|
2673
|
-
return evidence;
|
|
1882
|
+
return await captureEvidence(intent, signal, {
|
|
1883
|
+
reporter,
|
|
1884
|
+
validateIntent: true,
|
|
1885
|
+
}, createOperationInventoryReader(), false, reusableCandidate);
|
|
2674
1886
|
}
|
|
2675
1887
|
finally {
|
|
2676
1888
|
await reporter.finish();
|
|
2677
1889
|
}
|
|
2678
1890
|
},
|
|
2679
1891
|
async previewDependencyPlacement(intent, signal) {
|
|
2680
|
-
const
|
|
2681
|
-
|
|
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
|
+
}
|
|
2682
1918
|
},
|
|
2683
1919
|
async validateIntent(intent, signal) {
|
|
2684
1920
|
if (intent.kind === 'catalogSourcesRecovery') {
|
|
@@ -2717,13 +1953,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2717
1953
|
if (!plugin.manifest.config) {
|
|
2718
1954
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2719
1955
|
}
|
|
1956
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, intent.platform);
|
|
2720
1957
|
await coercePluginConfigUpdates({
|
|
2721
|
-
schema:
|
|
1958
|
+
schema: definition.fields,
|
|
2722
1959
|
updates: intent.updates ?? {},
|
|
2723
1960
|
cwd: context.cwd,
|
|
2724
1961
|
});
|
|
2725
1962
|
for (const key of intent.removeKeys ?? []) {
|
|
2726
|
-
if (!
|
|
1963
|
+
if (!definition.fields[key]) {
|
|
2727
1964
|
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
2728
1965
|
}
|
|
2729
1966
|
}
|
|
@@ -2763,39 +2000,12 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2763
2000
|
data: { code },
|
|
2764
2001
|
});
|
|
2765
2002
|
},
|
|
2766
|
-
async getConfigGroups(pluginId,
|
|
2767
|
-
|
|
2768
|
-
return withResolvedCatalogPlugin({
|
|
2769
|
-
selector: pluginId,
|
|
2770
|
-
cliVersion: packageInfo.version,
|
|
2771
|
-
env: context.env,
|
|
2772
|
-
signal,
|
|
2773
|
-
...(sessionContextForPlugin(pluginId)
|
|
2774
|
-
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
2775
|
-
: {}),
|
|
2776
|
-
consume: async ({ plugin }) => {
|
|
2777
|
-
if (!plugin.manifest.config) {
|
|
2778
|
-
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2779
|
-
}
|
|
2780
|
-
const read = await resolveConfigRead({
|
|
2781
|
-
pluginName: plugin.manifest.name,
|
|
2782
|
-
plugin,
|
|
2783
|
-
scope: layer.scope,
|
|
2784
|
-
cwd: context.cwd,
|
|
2785
|
-
env: context.env,
|
|
2786
|
-
});
|
|
2787
|
-
const effectiveRead = await resolveConfigRead({
|
|
2788
|
-
pluginName: plugin.manifest.name,
|
|
2789
|
-
plugin,
|
|
2790
|
-
scope: 'effective',
|
|
2791
|
-
cwd: context.cwd,
|
|
2792
|
-
env: context.env,
|
|
2793
|
-
});
|
|
2794
|
-
return buildConfigGroups({ schema: plugin.manifest.config, read, effectiveRead });
|
|
2795
|
-
},
|
|
2796
|
-
});
|
|
2003
|
+
async getConfigGroups(pluginId, config, signal) {
|
|
2004
|
+
return (await dependencies.getConfigPage(pluginId, config, signal)).groups;
|
|
2797
2005
|
},
|
|
2798
|
-
async getConfigPage(pluginId,
|
|
2006
|
+
async getConfigPage(pluginId, config, signal) {
|
|
2007
|
+
if (config.context === 'project')
|
|
2008
|
+
await resolveWorkContext({ cwd: context.cwd, context: 'project' });
|
|
2799
2009
|
const packageInfo = await readCliPackageInfo();
|
|
2800
2010
|
return withResolvedCatalogPlugin({
|
|
2801
2011
|
selector: pluginId,
|
|
@@ -2809,112 +2019,107 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2809
2019
|
if (!plugin.manifest.config) {
|
|
2810
2020
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2811
2021
|
}
|
|
2022
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, config.platform);
|
|
2812
2023
|
const stores = await readConfigStoresForTui({
|
|
2813
2024
|
sourceId: plugin.sourceId,
|
|
2814
2025
|
sourceIdentity: plugin.sourceIdentity,
|
|
2815
2026
|
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2816
2027
|
pluginName: plugin.manifest.name,
|
|
2817
|
-
schema:
|
|
2818
|
-
|
|
2028
|
+
schema: definition.fields,
|
|
2029
|
+
platform: config.platform,
|
|
2819
2030
|
signal,
|
|
2820
2031
|
});
|
|
2821
2032
|
const userStore = stores.user;
|
|
2822
|
-
const projectStore = stores.project;
|
|
2823
2033
|
const read = await resolveConfigRead({
|
|
2824
2034
|
pluginName: plugin.manifest.name,
|
|
2825
2035
|
plugin,
|
|
2826
|
-
scope:
|
|
2036
|
+
scope: config.view === 'effective' ? 'effective' : 'user',
|
|
2037
|
+
platform: config.platform,
|
|
2038
|
+
context: config.context ?? 'user',
|
|
2827
2039
|
cwd: context.cwd,
|
|
2828
2040
|
env: context.env,
|
|
2829
2041
|
});
|
|
2830
|
-
const effectiveRead =
|
|
2042
|
+
const effectiveRead = config.view === 'effective' ? read : await resolveConfigRead({
|
|
2831
2043
|
pluginName: plugin.manifest.name,
|
|
2832
2044
|
plugin,
|
|
2833
2045
|
scope: 'effective',
|
|
2046
|
+
platform: config.platform,
|
|
2047
|
+
context: config.context ?? 'user',
|
|
2834
2048
|
cwd: context.cwd,
|
|
2835
2049
|
env: context.env,
|
|
2836
2050
|
});
|
|
2837
|
-
const layerReads = layer.scope === 'effective' ? undefined : {
|
|
2838
|
-
user: layer.scope === 'user' ? read : await resolveConfigRead({
|
|
2839
|
-
pluginName: plugin.manifest.name,
|
|
2840
|
-
plugin,
|
|
2841
|
-
scope: 'user',
|
|
2842
|
-
cwd: context.cwd,
|
|
2843
|
-
env: context.env,
|
|
2844
|
-
}),
|
|
2845
|
-
...(layer.scope === 'user' ? {} : { project: read }),
|
|
2846
|
-
};
|
|
2847
2051
|
const effectiveRaw = await resolveEffectivePluginConfig({
|
|
2848
2052
|
sourceId: plugin.sourceId,
|
|
2849
2053
|
sourceIdentity: plugin.sourceIdentity,
|
|
2850
2054
|
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2851
2055
|
pluginName: plugin.manifest.name,
|
|
2852
2056
|
manifest: plugin.manifest,
|
|
2853
|
-
|
|
2057
|
+
platform: config.platform,
|
|
2058
|
+
context: config.context ?? 'user',
|
|
2854
2059
|
cwd: context.cwd,
|
|
2855
2060
|
env: context.env,
|
|
2856
2061
|
});
|
|
2857
|
-
const layerStore = layer.scope === 'effective'
|
|
2858
|
-
? undefined
|
|
2859
|
-
: layer.scope === 'user' ? userStore : projectStore;
|
|
2860
2062
|
const page = buildConfigPage({
|
|
2861
2063
|
pluginId,
|
|
2862
|
-
|
|
2064
|
+
context: config.context ?? 'user',
|
|
2065
|
+
schema: definition,
|
|
2863
2066
|
read,
|
|
2864
2067
|
effectiveRead,
|
|
2865
|
-
...(
|
|
2866
|
-
...(layerStore ? { rawValues: layerStore.values } : {}),
|
|
2068
|
+
...(config.view === 'effective' ? {} : { rawValues: userStore.values }),
|
|
2867
2069
|
rawLayerValues: {
|
|
2868
|
-
|
|
2869
|
-
...(
|
|
2070
|
+
common: userStore.common ?? {},
|
|
2071
|
+
...(userStore.platforms ?? {}),
|
|
2870
2072
|
},
|
|
2871
2073
|
effectiveRawValues: effectiveRaw.values,
|
|
2872
|
-
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)
|
|
2873
2076
|
? target.refreshKeys
|
|
2874
2077
|
: [])),
|
|
2875
2078
|
});
|
|
2876
2079
|
return { ...page, editEvidence: {
|
|
2080
|
+
platform: config.platform,
|
|
2081
|
+
context: config.context ?? 'user',
|
|
2877
2082
|
sourceIdentity: source.sourceIdentity,
|
|
2878
|
-
schemaIdentity: source.manifestDigest,
|
|
2083
|
+
schemaIdentity: configSchemaIdentity(plugin, source.manifestDigest, config.platform),
|
|
2879
2084
|
storeDigests: {
|
|
2880
2085
|
user: digest({ exists: userStore.exists, rawDigest: userStore.rawDigest }),
|
|
2881
|
-
...(projectStore ? { project: digest({ exists: projectStore.exists, rawDigest: projectStore.rawDigest }) } : {}),
|
|
2882
2086
|
},
|
|
2883
2087
|
} };
|
|
2884
2088
|
},
|
|
2885
2089
|
});
|
|
2886
2090
|
},
|
|
2887
|
-
async getConfigReferenceContext(pluginId,
|
|
2091
|
+
async getConfigReferenceContext(pluginId, config, draft, signal) {
|
|
2092
|
+
const workContext = await resolveWorkContext({ cwd: context.cwd, context: config.context ?? 'user' });
|
|
2888
2093
|
const packageInfo = await readCliPackageInfo();
|
|
2889
2094
|
return withResolvedCatalogPlugin({
|
|
2890
2095
|
selector: pluginId, cliVersion: packageInfo.version, env: context.env, signal,
|
|
2891
2096
|
...(sessionContextForPlugin(pluginId) ? { operationContext: sessionContextForPlugin(pluginId) } : {}),
|
|
2892
2097
|
consume: async ({ plugin }) => {
|
|
2893
|
-
|
|
2894
|
-
if (!definition)
|
|
2098
|
+
if (!plugin.manifest.config)
|
|
2895
2099
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
2100
|
+
const definition = compilePluginConfigDefinition(plugin.manifest.config, config.platform);
|
|
2896
2101
|
const scalars = Object.values(definition.fields).flatMap(configReferenceScalars);
|
|
2897
2102
|
if (!scalars.length)
|
|
2898
|
-
return { selections: {} };
|
|
2103
|
+
return { platform: config.platform, selections: {} };
|
|
2899
2104
|
const stores = await readConfigStoresForTui({
|
|
2900
2105
|
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
2901
2106
|
logicalIdentity: plugin.catalogLogicalIdentity, pluginName: plugin.manifest.name,
|
|
2902
|
-
schema: definition.fields,
|
|
2107
|
+
schema: definition.fields, platform: config.platform, signal,
|
|
2903
2108
|
});
|
|
2904
|
-
const edited =
|
|
2109
|
+
const edited = stores.user;
|
|
2905
2110
|
const values = { ...edited.values };
|
|
2906
2111
|
for (const key of draft.removes)
|
|
2907
2112
|
delete values[key];
|
|
2908
2113
|
Object.assign(values, draft.updates);
|
|
2909
|
-
const candidateStores = {
|
|
2114
|
+
const candidateStores = { user: replacePluginConfigStoreLayer(edited, values, config.platform) };
|
|
2910
2115
|
const references = await resolvePluginConfigReferenceContext({
|
|
2911
2116
|
sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
|
|
2912
2117
|
logicalIdentity: plugin.catalogLogicalIdentity, manifest: plugin.manifest,
|
|
2913
|
-
scope, cwd: context.cwd, env: context.env,
|
|
2118
|
+
scope: 'user', context: config.context ?? 'user', platform: config.platform, cwd: context.cwd, env: context.env,
|
|
2914
2119
|
});
|
|
2915
2120
|
const effective = await resolveEffectivePluginConfigFromStores({
|
|
2916
|
-
manifest: plugin.manifest,
|
|
2917
|
-
projectRoot:
|
|
2121
|
+
manifest: plugin.manifest, context: config.context ?? 'user', platform: config.platform,
|
|
2122
|
+
projectRoot: workContext.projectRoot ?? '', stores: candidateStores,
|
|
2918
2123
|
referenceContext: references.context,
|
|
2919
2124
|
});
|
|
2920
2125
|
// Keep invalid raw sources for Core validation; effective.values omits them.
|
|
@@ -2925,17 +2130,17 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2925
2130
|
const source = effective.sources[key];
|
|
2926
2131
|
if (source === 'default')
|
|
2927
2132
|
candidate[key] = definition.fields[key].default;
|
|
2928
|
-
else if (source === '
|
|
2929
|
-
candidate[key] = candidateStores.user.
|
|
2930
|
-
else if (source === '
|
|
2931
|
-
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];
|
|
2932
2137
|
}
|
|
2933
|
-
const result = { selections: {} };
|
|
2138
|
+
const result = { platform: config.platform, selections: {} };
|
|
2934
2139
|
for (const scalar of scalars) {
|
|
2935
2140
|
const ref = scalar.valueRef;
|
|
2936
2141
|
const selection = getPluginConfigReferenceChoices(definition, scalar, candidate, references.context);
|
|
2937
2142
|
const entries = ref.plugin ? references.context.plugins[ref.plugin]?.values?.[ref.field] : candidate[ref.field];
|
|
2938
|
-
result.selections[configReferenceKey(scalar)] = {
|
|
2143
|
+
result.selections[configReferenceKey(scalar, config.platform)] = {
|
|
2939
2144
|
...selection, source: (ref.plugin
|
|
2940
2145
|
? references.evidence.find((entry) => entry.plugin === ref.plugin)?.sources[ref.field]
|
|
2941
2146
|
: effective.sources[ref.field]) ?? 'missing',
|
|
@@ -2947,7 +2152,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2947
2152
|
},
|
|
2948
2153
|
});
|
|
2949
2154
|
},
|
|
2950
|
-
async getConfigResetAvailability(pluginId,
|
|
2155
|
+
async getConfigResetAvailability(pluginId, config, signal) {
|
|
2156
|
+
if (config.context === 'project')
|
|
2157
|
+
await resolveWorkContext({ cwd: context.cwd, context: 'project' });
|
|
2951
2158
|
const parsed = parsePluginId(pluginId);
|
|
2952
2159
|
if (!parsed.sourceId || !parsed.pluginName) {
|
|
2953
2160
|
throw new TuiOperationBlockedError('invalidPluginId', '设置目标必须使用 canonical pluginId。');
|
|
@@ -2980,7 +2187,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2980
2187
|
const target = await inspectPluginConfigResetTarget({
|
|
2981
2188
|
sourceId: parsed.sourceId,
|
|
2982
2189
|
pluginName: parsed.pluginName,
|
|
2983
|
-
scope:
|
|
2190
|
+
scope: 'user',
|
|
2984
2191
|
cwd: context.cwd,
|
|
2985
2192
|
env: context.env,
|
|
2986
2193
|
});
|
|
@@ -2991,7 +2198,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
2991
2198
|
...(sourceIdentity ? { sourceIdentity } : {}),
|
|
2992
2199
|
...(logicalIdentity ? { logicalIdentity } : {}),
|
|
2993
2200
|
pluginName: parsed.pluginName,
|
|
2994
|
-
scope:
|
|
2201
|
+
scope: 'user',
|
|
2202
|
+
platform: config.platform,
|
|
2995
2203
|
cwd: context.cwd,
|
|
2996
2204
|
env: context.env,
|
|
2997
2205
|
});
|
|
@@ -3018,6 +2226,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3018
2226
|
};
|
|
3019
2227
|
},
|
|
3020
2228
|
async executeIntent(intent, expectedEvidence, signal) {
|
|
2229
|
+
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
2230
|
+
|| intent.kind === 'pluginWriteTask') {
|
|
2231
|
+
throw new TuiOperationBlockedError('useScopedRecovery', '请使用统一范围恢复任务。');
|
|
2232
|
+
}
|
|
3021
2233
|
if (intent.kind === 'catalogStorePrune') {
|
|
3022
2234
|
signal?.throwIfAborted();
|
|
3023
2235
|
if (!expectedEvidence.catalogStore) {
|
|
@@ -3304,351 +2516,13 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3304
2516
|
}
|
|
3305
2517
|
throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
|
|
3306
2518
|
}
|
|
3307
|
-
if (intent.kind === 'pluginWrite' || intent.kind === 'pluginWriteBatch'
|
|
3308
|
-
|| intent.kind === 'pluginWriteTask'
|
|
3309
|
-
|| intent.kind === 'configSave' && (intent.apply || intent.retryApply)
|
|
3310
|
-
|| intent.kind === 'configSaveBatch' && intent.apply) {
|
|
3311
|
-
await prepareNativeCliExecution({ env: context.env, runner: context.nativeCliRunner });
|
|
3312
|
-
}
|
|
3313
|
-
const codexObservationSession = intent.kind === 'pluginWrite'
|
|
3314
|
-
|| intent.kind === 'pluginWriteBatch'
|
|
3315
|
-
|| intent.kind === 'pluginWriteTask'
|
|
3316
|
-
? await createCodexNativeObservationSession({
|
|
3317
|
-
exactCwd: context.cwd,
|
|
3318
|
-
env: context.env,
|
|
3319
|
-
runner: context.nativeCliRunner,
|
|
3320
|
-
manager: codexManagerByEvidence.get(expectedEvidence),
|
|
3321
|
-
})
|
|
3322
|
-
: undefined;
|
|
3323
2519
|
return withAwHomeMutationLock({
|
|
3324
2520
|
env: context.env,
|
|
3325
2521
|
action: async (awHomeMutationToken) => {
|
|
3326
2522
|
await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
|
|
3327
2523
|
const operationInventory = createOperationInventoryReader();
|
|
3328
|
-
const
|
|
3329
|
-
|
|
3330
|
-
try {
|
|
3331
|
-
currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true, undefined, codexObservationSession, intent.kind === 'pluginWriteBatch'
|
|
3332
|
-
? expectedEvidence
|
|
3333
|
-
: undefined);
|
|
3334
|
-
}
|
|
3335
|
-
catch (error) {
|
|
3336
|
-
const code = error instanceof TuiOperationBlockedError ? error.code : undefined;
|
|
3337
|
-
if (intent.kind === 'pluginWriteBatch'
|
|
3338
|
-
&& (intent.selection.kind === 'currentProjectIntentRoots'
|
|
3339
|
-
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3340
|
-
&& error instanceof TuiOperationBlockedError) {
|
|
3341
|
-
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3342
|
-
}
|
|
3343
|
-
if (intent.kind === 'pluginWriteTask'
|
|
3344
|
-
&& (code === 'taskHasBlockedTargets' || code === 'taskNoExecutableTargets')) {
|
|
3345
|
-
throw operationReviewStale('确认后任务可执行目标发生变化。', error instanceof TuiOperationBlockedError ? error.diagnostics : undefined);
|
|
3346
|
-
}
|
|
3347
|
-
throw error;
|
|
3348
|
-
}
|
|
3349
|
-
if (intent.kind === 'pluginWriteBatch') {
|
|
3350
|
-
const expectedBatch = expectedEvidence;
|
|
3351
|
-
const currentBatch = currentEvidence;
|
|
3352
|
-
if ((intent.selection.kind === 'currentProjectIntentRoots'
|
|
3353
|
-
|| intent.selection.kind === 'currentProjectIntentTargets')
|
|
3354
|
-
&& (!expectedBatch.projectIntentDigest
|
|
3355
|
-
|| expectedBatch.projectIntentDigest !== currentBatch.projectIntentDigest)) {
|
|
3356
|
-
throw operationReviewStale('确认后完整项目 Intent 声明发生变化。');
|
|
3357
|
-
}
|
|
3358
|
-
const reviewedGroups = expectedBatch.groups.map((group) => ({
|
|
3359
|
-
groupId: group.groupId,
|
|
3360
|
-
sourceId: group.sourceId,
|
|
3361
|
-
platform: group.platform,
|
|
3362
|
-
scope: group.scope,
|
|
3363
|
-
requestedPluginIds: group.requestedPluginIds,
|
|
3364
|
-
executable: group.executable,
|
|
3365
|
-
planFingerprint: group.preview.operationPlan
|
|
3366
|
-
? intent.action === 'cleanup'
|
|
3367
|
-
? group.preview.operationPlan.planFingerprint
|
|
3368
|
-
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3369
|
-
: undefined,
|
|
3370
|
-
}));
|
|
3371
|
-
const currentGroups = currentBatch.groups.map((group) => ({
|
|
3372
|
-
groupId: group.groupId,
|
|
3373
|
-
sourceId: group.sourceId,
|
|
3374
|
-
platform: group.platform,
|
|
3375
|
-
scope: group.scope,
|
|
3376
|
-
requestedPluginIds: group.requestedPluginIds,
|
|
3377
|
-
executable: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3378
|
-
?.executable === false ? false : group.executable,
|
|
3379
|
-
planFingerprint: expectedBatch.groups.find((reviewed) => reviewed.groupId === group.groupId)
|
|
3380
|
-
?.executable === false
|
|
3381
|
-
? undefined
|
|
3382
|
-
: group.preview.operationPlan
|
|
3383
|
-
? intent.action === 'cleanup'
|
|
3384
|
-
? group.preview.operationPlan.planFingerprint
|
|
3385
|
-
: createBatchIntentCompatiblePlanFingerprint(group.preview.operationPlan)
|
|
3386
|
-
: undefined,
|
|
3387
|
-
}));
|
|
3388
|
-
if (expectedBatch.catalog.configDigest !== currentBatch.catalog.configDigest
|
|
3389
|
-
|| JSON.stringify(expectedBatch.requestedPluginIds)
|
|
3390
|
-
!== JSON.stringify(currentBatch.requestedPluginIds)
|
|
3391
|
-
|| JSON.stringify(reviewedGroups) !== JSON.stringify(currentGroups)) {
|
|
3392
|
-
throw operationReviewStale('确认后批量目标或写入计划发生变化。');
|
|
3393
|
-
}
|
|
3394
|
-
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3395
|
-
const groupResults = [];
|
|
3396
|
-
const completedTargetKeys = new Set();
|
|
3397
|
-
const expectedIntentDigests = new Map();
|
|
3398
|
-
for (const group of expectedBatch.groups) {
|
|
3399
|
-
for (const intentDigest of group.preview.operationPlan?.evidence.intentDigests ?? []) {
|
|
3400
|
-
expectedIntentDigests.set(intentDigest.scope, intentDigest.digest);
|
|
3401
|
-
}
|
|
3402
|
-
}
|
|
3403
|
-
let executedGroupCount = 0;
|
|
3404
|
-
const appendStoppedGroups = (startIndex, staleMessage) => {
|
|
3405
|
-
for (const [offset, stopped] of expectedBatch.groups.slice(startIndex).entries()) {
|
|
3406
|
-
groupResults.push({
|
|
3407
|
-
groupId: stopped.groupId,
|
|
3408
|
-
sourceId: stopped.sourceId,
|
|
3409
|
-
platform: stopped.platform,
|
|
3410
|
-
scope: stopped.scope,
|
|
3411
|
-
requestedPluginIds: stopped.requestedPluginIds,
|
|
3412
|
-
result: !stopped.executable ? stopped.preview : failedWriteCommandResult(intent.action, stopped.requestedPluginIds, { cwd: context.cwd, platform: stopped.platform, scope: stopped.scope }, offset === 0
|
|
3413
|
-
? new TuiOperationBlockedError('operationReviewStale', staleMessage)
|
|
3414
|
-
: new TuiOperationBlockedError('batchNotStarted', '前一个批量组的写入计划已变化,因此这个组没有开始。')),
|
|
3415
|
-
});
|
|
3416
|
-
}
|
|
3417
|
-
};
|
|
3418
|
-
for (const [groupIndex, reviewed] of expectedBatch.groups.entries()) {
|
|
3419
|
-
const current = currentBatch.groups.find((group) => group.groupId === reviewed.groupId);
|
|
3420
|
-
if (!reviewed.executable) {
|
|
3421
|
-
groupResults.push({
|
|
3422
|
-
groupId: reviewed.groupId,
|
|
3423
|
-
sourceId: reviewed.sourceId,
|
|
3424
|
-
platform: reviewed.platform,
|
|
3425
|
-
scope: reviewed.scope,
|
|
3426
|
-
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3427
|
-
result: reviewed.preview,
|
|
3428
|
-
});
|
|
3429
|
-
continue;
|
|
3430
|
-
}
|
|
3431
|
-
const groupInventory = groupResults.length === 0
|
|
3432
|
-
? operationInventory
|
|
3433
|
-
: createOperationInventoryReader();
|
|
3434
|
-
let executionPreview = current.preview;
|
|
3435
|
-
if (completedTargetKeys.size > 0
|
|
3436
|
-
|| (intent.action === 'cleanup' && executedGroupCount > 0)) {
|
|
3437
|
-
executionPreview = await runBatchWriteGroup({
|
|
3438
|
-
intent,
|
|
3439
|
-
group: current,
|
|
3440
|
-
cwd: context.cwd,
|
|
3441
|
-
runtime: {
|
|
3442
|
-
...context,
|
|
3443
|
-
awHomeMutationToken,
|
|
3444
|
-
inventoryCatalogSourceReferences: groupInventory,
|
|
3445
|
-
...(intent.action === 'cleanup' ? {} : {
|
|
3446
|
-
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3447
|
-
}),
|
|
3448
|
-
previewOperationPlan: true,
|
|
3449
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3450
|
-
},
|
|
3451
|
-
});
|
|
3452
|
-
const cleanupIntentDigestChanged = intent.action === 'cleanup'
|
|
3453
|
-
&& executionPreview.operationPlan?.evidence.intentDigests.find((entry) => entry.scope === current.scope)?.digest !== expectedIntentDigests.get(current.scope);
|
|
3454
|
-
if (cleanupIntentDigestChanged
|
|
3455
|
-
|| !reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3456
|
-
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3457
|
-
appendStoppedGroups(groupIndex, '前序批量结果改变了这个组的实际写入计划。');
|
|
3458
|
-
break;
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3462
|
-
const result = await runBatchWriteGroup({
|
|
3463
|
-
intent,
|
|
3464
|
-
group: current,
|
|
3465
|
-
cwd: context.cwd,
|
|
3466
|
-
runtime: {
|
|
3467
|
-
...context,
|
|
3468
|
-
awHomeMutationToken,
|
|
3469
|
-
preApplyConfirmed: true,
|
|
3470
|
-
managedCommandProducerCache,
|
|
3471
|
-
inventoryCatalogSourceReferences: groupInventory,
|
|
3472
|
-
...(intent.action === 'cleanup' ? {} : {
|
|
3473
|
-
expectedCatalogConfigDigest: expectedBatch.catalog.configDigest,
|
|
3474
|
-
}),
|
|
3475
|
-
...(executionPreview.operationPlan ? {
|
|
3476
|
-
expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint,
|
|
3477
|
-
} : {}),
|
|
3478
|
-
...(requiresManagedCodex ? {
|
|
3479
|
-
managedCodexAuthorizer: async () => true,
|
|
3480
|
-
} : {}),
|
|
3481
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3482
|
-
},
|
|
3483
|
-
});
|
|
3484
|
-
if (writeReviewIsStale(result)) {
|
|
3485
|
-
if (executedGroupCount === 0)
|
|
3486
|
-
throwIfWriteReviewStale(result);
|
|
3487
|
-
appendStoppedGroups(groupIndex, '这个组的实际写入计划发生变化。');
|
|
3488
|
-
break;
|
|
3489
|
-
}
|
|
3490
|
-
if (intent.action === 'cleanup') {
|
|
3491
|
-
for (const change of result.operationPlan?.intentChanges ?? []) {
|
|
3492
|
-
expectedIntentDigests.set(change.scope, change.afterDigest);
|
|
3493
|
-
}
|
|
3494
|
-
}
|
|
3495
|
-
executedGroupCount += 1;
|
|
3496
|
-
for (const target of result.targetResults) {
|
|
3497
|
-
if (target.state === 'completed') {
|
|
3498
|
-
completedTargetKeys.add([
|
|
3499
|
-
target.target.pluginId,
|
|
3500
|
-
target.target.platform,
|
|
3501
|
-
target.target.scope,
|
|
3502
|
-
].join('\0'));
|
|
3503
|
-
}
|
|
3504
|
-
}
|
|
3505
|
-
groupResults.push({
|
|
3506
|
-
groupId: reviewed.groupId,
|
|
3507
|
-
sourceId: reviewed.sourceId,
|
|
3508
|
-
platform: reviewed.platform,
|
|
3509
|
-
scope: reviewed.scope,
|
|
3510
|
-
requestedPluginIds: reviewed.requestedPluginIds,
|
|
3511
|
-
result,
|
|
3512
|
-
});
|
|
3513
|
-
}
|
|
3514
|
-
const merged = mergeWriteCommandResults(groupResults.map((group) => group.result));
|
|
3515
|
-
return {
|
|
3516
|
-
...merged,
|
|
3517
|
-
requestedPluginIds: expectedBatch.requestedPluginIds,
|
|
3518
|
-
groupResults,
|
|
3519
|
-
};
|
|
3520
|
-
}
|
|
3521
|
-
if (intent.kind === 'pluginWriteTask') {
|
|
3522
|
-
const expectedTask = expectedEvidence;
|
|
3523
|
-
const currentTask = currentEvidence;
|
|
3524
|
-
if (!operationEvidenceMatches(expectedTask, currentTask)) {
|
|
3525
|
-
throw operationReviewStale('确认后任务目标、根因、版本或写入计划发生变化。');
|
|
3526
|
-
}
|
|
3527
|
-
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3528
|
-
const operationContexts = taskCatalogOperationsByEvidence.get(currentEvidence)
|
|
3529
|
-
?? new Map();
|
|
3530
|
-
const sourceResults = [];
|
|
3531
|
-
const completedTargetKeys = new Set();
|
|
3532
|
-
const preApplyExecutionCache = new Map();
|
|
3533
|
-
const commandTargets = (source) => source.requestedTargets.map((target) => {
|
|
3534
|
-
const direct = source.causes.filter((cause) => pluginWriteTaskTargetKey(cause.repairTarget) === pluginWriteTaskTargetKey(target));
|
|
3535
|
-
return {
|
|
3536
|
-
pluginId: target.pluginId,
|
|
3537
|
-
platform: target.platform,
|
|
3538
|
-
scope: target.scope,
|
|
3539
|
-
method: direct.some((cause) => cause.method === 'reinstall')
|
|
3540
|
-
? 'reinstall' : 'sync',
|
|
3541
|
-
};
|
|
3542
|
-
});
|
|
3543
|
-
const repairTargets = (source) => [...new Map(source.causes.map((cause) => [
|
|
3544
|
-
pluginWriteTaskTargetKey(cause.repairTarget),
|
|
3545
|
-
{ ...cause.repairTarget, method: cause.method },
|
|
3546
|
-
])).values()];
|
|
3547
|
-
const stoppedResult = (source, error) => mergeWriteCommandResults(source.requestedTargets.map((target) => failedWriteCommandResult('sync', [target.pluginId], {
|
|
3548
|
-
cwd: context.cwd,
|
|
3549
|
-
platform: target.platform,
|
|
3550
|
-
scope: target.scope,
|
|
3551
|
-
}, error)));
|
|
3552
|
-
const appendStoppedSources = (startIndex, message) => {
|
|
3553
|
-
for (const [offset, source] of expectedTask.taskPlan.sources.slice(startIndex).entries()) {
|
|
3554
|
-
sourceResults.push({
|
|
3555
|
-
sourceId: source.sourceId,
|
|
3556
|
-
sourceIdentity: source.sourceIdentity,
|
|
3557
|
-
requestedTargets: source.requestedTargets,
|
|
3558
|
-
result: stoppedResult(source, offset === 0
|
|
3559
|
-
? new TuiOperationBlockedError('operationReviewStale', message)
|
|
3560
|
-
: new TuiOperationBlockedError('taskNotStarted', '前一个来源计划已变化,因此这个来源没有开始。')),
|
|
3561
|
-
});
|
|
3562
|
-
}
|
|
3563
|
-
};
|
|
3564
|
-
for (const [sourceIndex, reviewed] of expectedTask.taskPlan.sources.entries()) {
|
|
3565
|
-
const current = currentTask.taskPlan.sources.find((source) => source.sourceId === reviewed.sourceId);
|
|
3566
|
-
const catalogOperation = operationContexts.get(reviewed.sourceId);
|
|
3567
|
-
if (!current || !catalogOperation || current.sourceIdentity !== reviewed.sourceIdentity) {
|
|
3568
|
-
if (sourceResults.length === 0) {
|
|
3569
|
-
throw operationReviewStale('确认后任务来源身份发生变化。');
|
|
3570
|
-
}
|
|
3571
|
-
appendStoppedSources(sourceIndex, '确认后任务来源身份发生变化。');
|
|
3572
|
-
break;
|
|
3573
|
-
}
|
|
3574
|
-
const sourceInventory = sourceResults.length === 0
|
|
3575
|
-
? operationInventory : createOperationInventoryReader();
|
|
3576
|
-
let executionPreview = current.preview;
|
|
3577
|
-
if (completedTargetKeys.size > 0) {
|
|
3578
|
-
executionPreview = await taskWriteCommand(commandTargets(current), {
|
|
3579
|
-
cwd: context.cwd,
|
|
3580
|
-
repairTargets: repairTargets(current),
|
|
3581
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3582
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3583
|
-
}, {
|
|
3584
|
-
...context,
|
|
3585
|
-
awHomeMutationToken,
|
|
3586
|
-
catalogOperationContext: catalogOperation,
|
|
3587
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3588
|
-
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3589
|
-
inventoryCatalogSourceReferences: sourceInventory,
|
|
3590
|
-
previewOperationPlan: true,
|
|
3591
|
-
preApplyExecutionCache,
|
|
3592
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3593
|
-
});
|
|
3594
|
-
if (!reviewed.preview.operationPlan || !executionPreview.operationPlan
|
|
3595
|
-
|| createBatchIntentCompatiblePlanFingerprint(reviewed.preview.operationPlan, completedTargetKeys) !== createBatchIntentCompatiblePlanFingerprint(executionPreview.operationPlan, completedTargetKeys)) {
|
|
3596
|
-
appendStoppedSources(sourceIndex, '前序结果改变了这个来源的实际写入计划。');
|
|
3597
|
-
break;
|
|
3598
|
-
}
|
|
3599
|
-
}
|
|
3600
|
-
const requiresManagedCodex = reviewed.preview.codexManagerPreview?.state === 'requires-install';
|
|
3601
|
-
const result = await taskWriteCommand(commandTargets(current), {
|
|
3602
|
-
cwd: context.cwd,
|
|
3603
|
-
repairTargets: repairTargets(current),
|
|
3604
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3605
|
-
? {} : { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3606
|
-
}, {
|
|
3607
|
-
...context,
|
|
3608
|
-
awHomeMutationToken,
|
|
3609
|
-
catalogOperationContext: catalogOperation,
|
|
3610
|
-
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
3611
|
-
expectedCatalogConfigDigest: expectedTask.catalog.configDigest,
|
|
3612
|
-
inventoryCatalogSourceReferences: sourceInventory,
|
|
3613
|
-
preApplyConfirmed: true,
|
|
3614
|
-
preApplyExecutionCache,
|
|
3615
|
-
managedCommandProducerCache,
|
|
3616
|
-
...(executionPreview.operationPlan
|
|
3617
|
-
? { expectedPlanFingerprint: executionPreview.operationPlan.planFingerprint } : {}),
|
|
3618
|
-
...(requiresManagedCodex ? { managedCodexAuthorizer: async () => true } : {}),
|
|
3619
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3620
|
-
});
|
|
3621
|
-
if (writeReviewIsStale(result)) {
|
|
3622
|
-
if (sourceResults.length === 0)
|
|
3623
|
-
throwIfWriteReviewStale(result);
|
|
3624
|
-
appendStoppedSources(sourceIndex, '这个来源的实际写入计划发生变化。');
|
|
3625
|
-
break;
|
|
3626
|
-
}
|
|
3627
|
-
for (const target of result.targetResults) {
|
|
3628
|
-
if (target.state === 'completed') {
|
|
3629
|
-
completedTargetKeys.add([
|
|
3630
|
-
target.target.pluginId,
|
|
3631
|
-
target.target.platform,
|
|
3632
|
-
target.target.scope,
|
|
3633
|
-
].join('\0'));
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
|
-
sourceResults.push({
|
|
3637
|
-
sourceId: reviewed.sourceId,
|
|
3638
|
-
sourceIdentity: reviewed.sourceIdentity,
|
|
3639
|
-
requestedTargets: reviewed.requestedTargets,
|
|
3640
|
-
result,
|
|
3641
|
-
});
|
|
3642
|
-
}
|
|
3643
|
-
const merged = mergeWriteCommandResults(sourceResults.map((source) => source.result));
|
|
3644
|
-
return {
|
|
3645
|
-
...merged,
|
|
3646
|
-
taskPlan: expectedTask.taskPlan,
|
|
3647
|
-
sourceResults,
|
|
3648
|
-
};
|
|
3649
|
-
}
|
|
3650
|
-
if (intent.kind !== 'pluginWrite'
|
|
3651
|
-
&& !operationEvidenceMatches(expectedEvidence, currentEvidence)) {
|
|
2524
|
+
const currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true);
|
|
2525
|
+
if (!operationEvidenceMatches(expectedEvidence, currentEvidence)) {
|
|
3652
2526
|
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
3653
2527
|
}
|
|
3654
2528
|
const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
|
|
@@ -3657,76 +2531,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3657
2531
|
throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
|
|
3658
2532
|
}
|
|
3659
2533
|
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
3660
|
-
if (intent.kind === 'pluginWrite') {
|
|
3661
|
-
const options = {
|
|
3662
|
-
cwd: context.cwd,
|
|
3663
|
-
platform: intent.platform,
|
|
3664
|
-
scope: intent.scope,
|
|
3665
|
-
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
3666
|
-
? {}
|
|
3667
|
-
: { scriptTimeoutMs: reviewedScriptTimeoutMs(intent) }),
|
|
3668
|
-
...(intent.action === 'install' && intent.dependencyScopeOverrides
|
|
3669
|
-
? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
|
|
3670
|
-
: {}),
|
|
3671
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3672
|
-
? { allowManagedCodexInstall: true }
|
|
3673
|
-
: {}),
|
|
3674
|
-
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
3675
|
-
};
|
|
3676
|
-
const command = {
|
|
3677
|
-
install: installWriteCommand,
|
|
3678
|
-
refreshAndSync: syncWriteCommand,
|
|
3679
|
-
reinstall: reinstallWriteCommand,
|
|
3680
|
-
uninstall: uninstallWriteCommand,
|
|
3681
|
-
cleanup: cleanupWriteCommand,
|
|
3682
|
-
sync: syncWriteCommand,
|
|
3683
|
-
}[intent.action];
|
|
3684
|
-
if (intent.action === 'cleanup') {
|
|
3685
|
-
if (!expectedEvidence.operationPlan || !currentEvidence.operationPlan
|
|
3686
|
-
|| expectedEvidence.operationPlan.planFingerprint
|
|
3687
|
-
!== currentEvidence.operationPlan.planFingerprint) {
|
|
3688
|
-
throw operationReviewStale('确认后清理计划发生变化。');
|
|
3689
|
-
}
|
|
3690
|
-
const result = await command([intent.pluginId], options, {
|
|
3691
|
-
...context,
|
|
3692
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3693
|
-
awHomeMutationToken,
|
|
3694
|
-
preApplyConfirmed: true,
|
|
3695
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
3696
|
-
expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint,
|
|
3697
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3698
|
-
? { managedCodexAuthorizer: async () => true }
|
|
3699
|
-
: {}),
|
|
3700
|
-
});
|
|
3701
|
-
throwIfWriteReviewStale(result);
|
|
3702
|
-
return result;
|
|
3703
|
-
}
|
|
3704
|
-
const currentSource = currentEvidence.source;
|
|
3705
|
-
if (!expectedEvidence.source || !currentSource) {
|
|
3706
|
-
throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
|
|
3707
|
-
}
|
|
3708
|
-
const result = await command([intent.pluginId], options, {
|
|
3709
|
-
...context,
|
|
3710
|
-
...(codexObservationSession ? { codexObservationSession } : {}),
|
|
3711
|
-
awHomeMutationToken,
|
|
3712
|
-
preApplyConfirmed: true,
|
|
3713
|
-
inventoryCatalogSourceReferences: operationInventory,
|
|
3714
|
-
...(expectedEvidence.operationPlan
|
|
3715
|
-
? { expectedPlanFingerprint: expectedEvidence.operationPlan.planFingerprint }
|
|
3716
|
-
: {}),
|
|
3717
|
-
expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
|
|
3718
|
-
...(catalogOperation ? {
|
|
3719
|
-
catalogOperationContext: catalogOperation,
|
|
3720
|
-
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
3721
|
-
} : {}),
|
|
3722
|
-
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
3723
|
-
? { managedCodexAuthorizer: async () => true }
|
|
3724
|
-
: {}),
|
|
3725
|
-
reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual),
|
|
3726
|
-
});
|
|
3727
|
-
throwIfWriteReviewStale(result);
|
|
3728
|
-
return result;
|
|
3729
|
-
}
|
|
3730
2534
|
if (intent.kind === 'configSaveBatch') {
|
|
3731
2535
|
assertConfigSaveBatchIntent(intent);
|
|
3732
2536
|
const reviewedBatch = expectedEvidence.configBatch;
|
|
@@ -3740,7 +2544,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3740
2544
|
const packageInfo = await readCliPackageInfo();
|
|
3741
2545
|
for (const group of intent.groups) {
|
|
3742
2546
|
const groupIntent = configSaveBatchGroupIntent(intent, group);
|
|
3743
|
-
const reviewedGroup = reviewedBatch.groups.find((item) => item.
|
|
2547
|
+
const reviewedGroup = reviewedBatch.groups.find((item) => item.platform === group.platform);
|
|
3744
2548
|
if (!reviewedGroup) {
|
|
3745
2549
|
throw operationReviewStale('确认信息缺少设置层基线。');
|
|
3746
2550
|
}
|
|
@@ -3757,7 +2561,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3757
2561
|
const freshLayerStoreDigest = await configLayerStoreDigest({
|
|
3758
2562
|
source: freshSource,
|
|
3759
2563
|
pluginName: parsePluginId(intent.pluginId).pluginName,
|
|
3760
|
-
scope: group.
|
|
2564
|
+
scope: 'user', platform: group.platform, profileOnly: true,
|
|
3761
2565
|
cwd: context.cwd,
|
|
3762
2566
|
env: context.env,
|
|
3763
2567
|
});
|
|
@@ -3781,8 +2585,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3781
2585
|
...(groupCatalogOperation ? { operationContext: groupCatalogOperation } : {}),
|
|
3782
2586
|
consume: async ({ plugin, source }) => {
|
|
3783
2587
|
assertSourceEvidenceMatches(freshSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
|
|
2588
|
+
const referencePlugins = configNeedsReferencePlugins(plugin) ? await resolvePluginConfigReferencePlugins({
|
|
2589
|
+
sourceId: plugin.sourceId,
|
|
2590
|
+
sourceIdentity: plugin.sourceIdentity,
|
|
2591
|
+
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2592
|
+
env: context.env,
|
|
2593
|
+
operationContext: groupCatalogOperation,
|
|
2594
|
+
}) : undefined;
|
|
3784
2595
|
const applyTargets = intent.apply
|
|
3785
|
-
? freshConfig.affectedTargets.map((target) => ({
|
|
2596
|
+
? freshConfig.affectedTargets.filter((target) => target.platform === 'codex' || target.platform === 'claude').map((target) => ({
|
|
3786
2597
|
pluginName: target.pluginName ?? plugin.manifest.name,
|
|
3787
2598
|
platform: target.platform,
|
|
3788
2599
|
scope: target.scope,
|
|
@@ -3791,12 +2602,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3791
2602
|
return applyPluginConfigWrite({
|
|
3792
2603
|
plugin,
|
|
3793
2604
|
pluginName: plugin.manifest.name,
|
|
3794
|
-
scope: group.
|
|
2605
|
+
scope: 'user', platform: group.platform, context: intent.context ?? 'user',
|
|
2606
|
+
apply: intent.apply,
|
|
3795
2607
|
cwd: context.cwd,
|
|
3796
2608
|
env: context.env,
|
|
3797
2609
|
nativeCliRunner: context.nativeCliRunner,
|
|
3798
2610
|
rebuildRuntime: {
|
|
3799
2611
|
...context,
|
|
2612
|
+
referencePlugins,
|
|
3800
2613
|
awHomeMutationToken,
|
|
3801
2614
|
operationDiagnosticSink: (entries) => {
|
|
3802
2615
|
operationRebuildResults.push(...structuredClone(entries));
|
|
@@ -3810,7 +2623,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3810
2623
|
} : {}),
|
|
3811
2624
|
},
|
|
3812
2625
|
awHomeMutationToken,
|
|
3813
|
-
applyTargets,
|
|
2626
|
+
...(applyTargets.length ? { applyTargets } : {}),
|
|
3814
2627
|
request: { updates: group.updates, removeKeys: group.removeKeys },
|
|
3815
2628
|
sharedNativeApplyConfirmed: true,
|
|
3816
2629
|
preApplyConfirmed: true,
|
|
@@ -3827,7 +2640,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3827
2640
|
? 'notNeeded'
|
|
3828
2641
|
: 'failed';
|
|
3829
2642
|
results.push({
|
|
3830
|
-
scope: group.
|
|
2643
|
+
scope: 'user', platform: group.platform,
|
|
3831
2644
|
status: applyStatus === 'failed' ? 'failed' : 'succeeded',
|
|
3832
2645
|
configSaved,
|
|
3833
2646
|
applyRequested: intent.apply,
|
|
@@ -3848,7 +2661,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3848
2661
|
? error.code
|
|
3849
2662
|
: configSaved ? 'runtimeApplyFailed' : 'configWriteFailed';
|
|
3850
2663
|
results.push({
|
|
3851
|
-
scope: group.
|
|
2664
|
+
scope: 'user', platform: group.platform,
|
|
3852
2665
|
status: 'failed',
|
|
3853
2666
|
configSaved,
|
|
3854
2667
|
applyRequested: intent.apply,
|
|
@@ -3884,7 +2697,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3884
2697
|
return resetPluginConfigWithHelper({
|
|
3885
2698
|
sourceId: parsed.sourceId,
|
|
3886
2699
|
pluginName: parsed.pluginName,
|
|
3887
|
-
scope: intent.
|
|
2700
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
3888
2701
|
cwd: context.cwd,
|
|
3889
2702
|
env: context.env,
|
|
3890
2703
|
nativeCliRunner: context.nativeCliRunner,
|
|
@@ -3905,22 +2718,32 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3905
2718
|
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
3906
2719
|
consume: async ({ plugin, source }) => {
|
|
3907
2720
|
assertSourceEvidenceMatches(currentSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
|
|
2721
|
+
const referencePlugins = configNeedsReferencePlugins(plugin) ? await resolvePluginConfigReferencePlugins({
|
|
2722
|
+
sourceId: plugin.sourceId,
|
|
2723
|
+
sourceIdentity: plugin.sourceIdentity,
|
|
2724
|
+
logicalIdentity: plugin.catalogLogicalIdentity,
|
|
2725
|
+
env: context.env,
|
|
2726
|
+
operationContext: catalogOperation,
|
|
2727
|
+
}) : undefined;
|
|
3908
2728
|
const applyTargets = intent.retryApply || intent.apply
|
|
3909
|
-
? currentEvidence.config?.affectedTargets.map((target) => ({
|
|
2729
|
+
? currentEvidence.config?.affectedTargets.filter((target) => target.platform === 'codex' || target.platform === 'claude').map((target) => ({
|
|
3910
2730
|
pluginName: target.pluginName ?? plugin.manifest.name,
|
|
3911
2731
|
platform: target.platform,
|
|
3912
2732
|
scope: target.scope,
|
|
3913
2733
|
})) ?? []
|
|
3914
2734
|
: [];
|
|
3915
2735
|
const result = intent.retryApply
|
|
3916
|
-
? await
|
|
2736
|
+
? await applyPluginConfig({
|
|
3917
2737
|
plugin,
|
|
3918
|
-
scope: intent.
|
|
2738
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
2739
|
+
...(intent.targets ? { targets: intent.targets } : {}),
|
|
2740
|
+
cliVersion: packageInfo.version,
|
|
3919
2741
|
cwd: context.cwd,
|
|
3920
2742
|
env: context.env,
|
|
3921
2743
|
nativeCliRunner: context.nativeCliRunner,
|
|
3922
2744
|
rebuildRuntime: {
|
|
3923
2745
|
...context,
|
|
2746
|
+
referencePlugins,
|
|
3924
2747
|
awHomeMutationToken,
|
|
3925
2748
|
operationDiagnosticSink,
|
|
3926
2749
|
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
@@ -3932,7 +2755,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3932
2755
|
} : {}),
|
|
3933
2756
|
},
|
|
3934
2757
|
awHomeMutationToken,
|
|
3935
|
-
|
|
2758
|
+
expectedEvidence: expectedEvidence.config?.applyEvidence,
|
|
3936
2759
|
sharedNativeApplyConfirmed: true,
|
|
3937
2760
|
preApplyConfirmed: true,
|
|
3938
2761
|
preApplyAuthorizedTargets: expectedEvidence.config?.preApplyAuthorization ?? [],
|
|
@@ -3940,12 +2763,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3940
2763
|
: await applyPluginConfigWrite({
|
|
3941
2764
|
plugin,
|
|
3942
2765
|
pluginName: plugin.manifest.name,
|
|
3943
|
-
scope: intent.
|
|
2766
|
+
scope: 'user', platform: intent.platform, context: intent.context ?? 'user',
|
|
2767
|
+
apply: intent.apply,
|
|
3944
2768
|
cwd: context.cwd,
|
|
3945
2769
|
env: context.env,
|
|
3946
2770
|
nativeCliRunner: context.nativeCliRunner,
|
|
3947
2771
|
rebuildRuntime: {
|
|
3948
2772
|
...context,
|
|
2773
|
+
referencePlugins,
|
|
3949
2774
|
awHomeMutationToken,
|
|
3950
2775
|
operationDiagnosticSink,
|
|
3951
2776
|
...(reviewedScriptTimeoutMs(intent) === undefined
|
|
@@ -3957,7 +2782,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3957
2782
|
} : {}),
|
|
3958
2783
|
},
|
|
3959
2784
|
awHomeMutationToken,
|
|
3960
|
-
applyTargets,
|
|
2785
|
+
...(applyTargets.length ? { applyTargets } : {}),
|
|
3961
2786
|
request: { updates: intent.updates, removeKeys: intent.removeKeys },
|
|
3962
2787
|
sharedNativeApplyConfirmed: true,
|
|
3963
2788
|
preApplyConfirmed: true,
|
|
@@ -3969,8 +2794,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
3969
2794
|
}
|
|
3970
2795
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
3971
2796
|
},
|
|
3972
|
-
}).finally(async () => {
|
|
3973
|
-
await codexObservationSession?.dispose();
|
|
3974
2797
|
});
|
|
3975
2798
|
},
|
|
3976
2799
|
};
|