@lesliechan721/aw 0.1.0-beta.6 → 0.1.0-beta.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/scripts/init-plugin.mjs +4 -33
- 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/plugin-status.mjs +8 -11
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
- package/dist/authoring/scripts/remove-plugin.mjs +1 -0
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +104 -54
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/codex-eval-app-server.d.ts +2 -0
- package/dist/scripts/codex-eval-app-server.js +107 -1
- package/dist/scripts/codex-eval-app-server.js.map +1 -1
- package/dist/scripts/eval-plugin-skills.js +5 -2
- package/dist/scripts/eval-plugin-skills.js.map +1 -1
- package/dist/scripts/plugin-eval-contract.d.ts +5 -1
- package/dist/scripts/plugin-eval-contract.js +54 -4
- package/dist/scripts/plugin-eval-contract.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +6 -7
- package/dist/scripts/plugin-staging.js +11 -23
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +2 -33
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/adapters/helpers.js +2 -2
- package/dist/src/adapters/helpers.js.map +1 -1
- package/dist/src/authoring/workspace-commands.d.ts +17 -2
- package/dist/src/authoring/workspace-commands.js +21 -4
- package/dist/src/authoring/workspace-commands.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +78 -0
- package/dist/src/commands/aw-capabilities.js +259 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-context.d.ts +52 -0
- package/dist/src/commands/catalog-context.js +156 -0
- package/dist/src/commands/catalog-context.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +112 -0
- package/dist/src/commands/catalog-dev-source.js +481 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +26 -0
- package/dist/src/commands/catalog-generation.js +14 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
- package/dist/src/commands/catalog-publication-inspection.js +4 -5
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-target.d.ts +17 -0
- package/dist/src/commands/catalog-publication-target.js +60 -0
- package/dist/src/commands/catalog-publication-target.js.map +1 -0
- package/dist/src/commands/catalog-publish.d.ts +8 -1
- package/dist/src/commands/catalog-publish.js +25 -2
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +23 -1
- package/dist/src/commands/catalog-source.js +126 -40
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-status.d.ts +18 -2
- package/dist/src/commands/catalog-status.js +122 -18
- package/dist/src/commands/catalog-status.js.map +1 -1
- package/dist/src/commands/init.js +1 -1
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/offline-cleanup-evidence.d.ts +23 -0
- package/dist/src/commands/offline-cleanup-evidence.js +38 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/options.d.ts +1 -0
- package/dist/src/commands/options.js +1 -0
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +1 -1
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +19 -7
- package/dist/src/commands/rebuild-command.js +415 -205
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +10 -3
- package/dist/src/commands/rebuild-integration.js +73 -76
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +0 -8
- package/dist/src/commands/rebuild-operations.js +17 -115
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/runtime.js +21 -34
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/{status-v4.d.ts → status-v5.d.ts} +67 -21
- package/dist/src/commands/{status-v4.js → status-v5.js} +260 -30
- package/dist/src/commands/status-v5.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +2 -2
- package/dist/src/commands/wizard.js +2 -2
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.js +73 -17
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +6 -1
- package/dist/src/constants.js +6 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/generators/local-plugin.js +57 -3
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +562 -85
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/model.d.ts +1 -34
- package/dist/src/output/model.js +1 -144
- package/dist/src/output/model.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +0 -3
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +4 -0
- package/dist/src/portable/commands.js +61 -4
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +3 -0
- package/dist/src/portable/materializer.js +61 -10
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/catalog-generation.d.ts +56 -0
- package/dist/src/tui/catalog-generation.js +244 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +446 -126
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +8 -2
- package/dist/src/tui/controller-runtime.js +606 -92
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +71 -6
- package/dist/src/tui/controller.js +377 -83
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.js +1 -0
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +8 -0
- package/dist/src/tui/input-display.js +36 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/model.d.ts +27 -7
- package/dist/src/tui/model.js +273 -65
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/router.d.ts +26 -0
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/source-ref-presets.d.ts +3 -0
- package/dist/src/tui/source-ref-presets.js +11 -0
- package/dist/src/tui/source-ref-presets.js.map +1 -0
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/types.d.ts +90 -6
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.d.ts +3 -0
- package/dist/src/utils/agent-version.js +13 -5
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
- package/dist/src/utils/aw-home-mutation-lock.js +32 -4
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
- package/dist/src/utils/bundle-integrity.d.ts +0 -1
- package/dist/src/utils/bundle-integrity.js +0 -25
- package/dist/src/utils/bundle-integrity.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +8 -6
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.js +13 -2
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-generation-error.d.ts +12 -0
- package/dist/src/utils/catalog-generation-error.js +16 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +13 -0
- package/dist/src/utils/catalog-generation-resolver.js +129 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +52 -0
- package/dist/src/utils/catalog-generation-service.js +620 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +13 -0
- package/dist/src/utils/catalog-generation-state.js +201 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +44 -0
- package/dist/src/utils/catalog-generation-store.js +387 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v5.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v5.js} +39 -35
- package/dist/src/utils/{catalog-manifest-v4.js.map → catalog-manifest-v5.js.map} +1 -1
- package/dist/src/utils/catalog-operation-context.d.ts +28 -0
- package/dist/src/utils/catalog-operation-context.js +242 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +20 -4
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-snapshot.d.ts +5 -3
- package/dist/src/utils/catalog-snapshot.js +11 -3
- package/dist/src/utils/catalog-snapshot.js.map +1 -1
- package/dist/src/utils/catalog-source-cache.d.ts +1 -1
- package/dist/src/utils/catalog-source-cache.js +37 -12
- package/dist/src/utils/catalog-source-cache.js.map +1 -1
- package/dist/src/utils/catalog-source-identity.d.ts +1 -0
- package/dist/src/utils/catalog-source-identity.js +21 -2
- package/dist/src/utils/catalog-source-identity.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +1 -0
- package/dist/src/utils/catalog-sources.js +30 -11
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-config.js +4 -4
- package/dist/src/utils/codex-config.js.map +1 -1
- package/dist/src/utils/codex-manager.d.ts +33 -0
- package/dist/src/utils/codex-manager.js +656 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +2 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -1
- package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
- package/dist/src/utils/dynamic-skill-tree.js +35 -5
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.js +10 -25
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -28
- package/dist/src/utils/lifecycle-active-store.js +35 -247
- package/dist/src/utils/lifecycle-active-store.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.d.ts +3 -0
- package/dist/src/utils/multi-source-catalog.js +120 -10
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +15 -6
- package/dist/src/utils/native-cli.js +31 -39
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/orphan-marketplace-cleanup.js +0 -3
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +10 -1
- package/dist/src/utils/ownership.js +52 -24
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.js +12 -8
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/plugin-config-write.js +18 -35
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +1 -12
- package/dist/src/utils/plugin-config.js +107 -226
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +1 -1
- package/dist/src/utils/plugin-scripts.js +1 -1
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/state-paths.js +7 -2
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +7 -2
- package/dist/src/utils/target-eligibility.js +6 -3
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/package.json +4 -3
- package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
- package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
- package/dist/src/commands/status-v4.js.map +0 -1
- package/dist/src/commands/status.d.ts +0 -93
- package/dist/src/commands/status.js +0 -442
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
- package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
- package/dist/src/utils/catalog-manifest-v3.js +0 -3
- package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
-
import
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { statusCommandV5, } from '../commands/status-v5.js';
|
|
3
4
|
import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
|
|
4
5
|
import { cleanupWriteCommand, installWriteCommand, reinstallWriteCommand, uninstallWriteCommand, upgradeWriteCommand, } from '../commands/write-commands.js';
|
|
5
6
|
import { readCliPackageInfo } from '../utils/local-marketplace.js';
|
|
7
|
+
import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
|
|
6
8
|
import { readCatalogSourcesConfigSnapshot, } from '../utils/catalog-sources.js';
|
|
7
9
|
import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
|
|
8
10
|
import { coercePluginConfigUpdates, readNamespacedPluginConfigStoreIdentity, readPluginConfigStore, resolveConfigRead, } from '../utils/plugin-config.js';
|
|
9
11
|
import { applyPluginConfigWrite, resetPluginConfigWithHelper, } from '../utils/plugin-config-write.js';
|
|
12
|
+
import { offlineCleanupEvidenceFromInventory, } from '../commands/offline-cleanup-evidence.js';
|
|
13
|
+
import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
|
|
10
14
|
import { operationEvidenceFingerprint, TuiController, TuiOperationBlockedError, } from './controller.js';
|
|
11
15
|
import { buildConfigGroups, buildConfigPage, projectPlatformPlacement, } from './model.js';
|
|
12
16
|
import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
|
|
13
|
-
import { targetCanBeUninstalled } from '../utils/target-eligibility.js';
|
|
17
|
+
import { targetCanBeUninstalled, targetHasArtifactCollision, } from '../utils/target-eligibility.js';
|
|
14
18
|
import { calculateCatalogSourceIdentity } from '../utils/catalog-source-identity.js';
|
|
15
|
-
import {
|
|
19
|
+
import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.js';
|
|
16
20
|
import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
|
|
21
|
+
import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
|
|
22
|
+
import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
|
|
23
|
+
import { activateCatalogSourceCandidate, refreshCatalogSourceGeneration, setCatalogSourceRefreshPolicy, } from '../utils/catalog-generation-service.js';
|
|
24
|
+
import { loadCatalogGenerationViewModels } from './catalog-generation.js';
|
|
17
25
|
import { installPortableSkills, preparePortableInstall, PortableCommandError, } from '../portable/commands.js';
|
|
26
|
+
import { MANAGED_CODEX_PACKAGE_NAME, MANAGED_CODEX_VERSION, } from '../utils/codex-manager.js';
|
|
18
27
|
async function loadPortablePluginIds(options) {
|
|
19
28
|
return withResolvedMultiSourceCatalog({
|
|
20
29
|
cliVersion: options.cliVersion,
|
|
21
30
|
freshnessPolicy: 'cacheFirst',
|
|
22
31
|
env: options.context.env,
|
|
23
32
|
signal: options.signal,
|
|
33
|
+
...(options.catalogOperationContext
|
|
34
|
+
? { operationContext: options.catalogOperationContext }
|
|
35
|
+
: {}),
|
|
36
|
+
...(options.bootstrapMissingActive === false ? { bootstrapMissingActive: false } : {}),
|
|
24
37
|
consume: async (catalog) => (await Promise.all(catalog.sources.flatMap((item) => item.resolution
|
|
25
|
-
?
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
? item.resolution.manifest.entries.flatMap((entry) => {
|
|
39
|
+
const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
|
|
40
|
+
return support.supported
|
|
41
|
+
? [readPluginManifest(path.join(item.resolution.rootDir, 'plugins', entry.name), { mode: 'bundle' }).then((plugin) => plugin.manifest.distributions?.agentSkills
|
|
42
|
+
? formatPluginId({
|
|
43
|
+
sourceId: item.resolution.sourceId,
|
|
44
|
+
pluginName: entry.name,
|
|
45
|
+
})
|
|
46
|
+
: null)]
|
|
47
|
+
: [];
|
|
48
|
+
})
|
|
49
|
+
: []))).filter((pluginId) => pluginId !== null),
|
|
32
50
|
});
|
|
33
51
|
}
|
|
34
52
|
let tuiControllerRuntimeTestHooks;
|
|
@@ -60,6 +78,13 @@ export function statusTargetFingerprint(targets, revisions = []) {
|
|
|
60
78
|
.localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
|
|
61
79
|
});
|
|
62
80
|
}
|
|
81
|
+
export function configAffectedStatusTargets(targets, pluginId, configScope) {
|
|
82
|
+
return targets.filter((target) => target.pluginId === pluginId
|
|
83
|
+
&& (configScope === 'user' || target.scope === 'project')
|
|
84
|
+
&& (target.state === 'present' || target.state === 'partial'))
|
|
85
|
+
.sort((left, right) => `${left.platform}\0${left.scope}`
|
|
86
|
+
.localeCompare(`${right.platform}\0${right.scope}`, 'en'));
|
|
87
|
+
}
|
|
63
88
|
function operationReviewStale(message) {
|
|
64
89
|
return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`);
|
|
65
90
|
}
|
|
@@ -73,6 +98,8 @@ function sourceEvidenceForPlugin(source, pluginName) {
|
|
|
73
98
|
manifestDigest: source.manifestDigest,
|
|
74
99
|
snapshotDigest: source.snapshotDigest,
|
|
75
100
|
contentDigest: entry.contentDigest,
|
|
101
|
+
catalogVersion: source.manifest.catalogVersion,
|
|
102
|
+
channel: source.manifest.channel,
|
|
76
103
|
};
|
|
77
104
|
}
|
|
78
105
|
function assertSourceEvidenceMatches(expected, actual) {
|
|
@@ -115,6 +142,9 @@ function assertIntentIsLegal(intent, targets, revisions = []) {
|
|
|
115
142
|
if (intent.action === 'install' && otherScopeInstalled) {
|
|
116
143
|
throw new TuiOperationBlockedError('scopeConflict', '这个平台已安装在另一个使用位置。请先移除原位置。');
|
|
117
144
|
}
|
|
145
|
+
if (intent.action === 'install' && targetHasArtifactCollision(selected)) {
|
|
146
|
+
throw new TuiOperationBlockedError('artifactCollision', '所选位置存在输出冲突。请先移除或迁移冲突内容。');
|
|
147
|
+
}
|
|
118
148
|
if (intent.action === 'install' && selected.state !== 'absent') {
|
|
119
149
|
throw new TuiOperationBlockedError('targetAlreadyInstalled', '所选位置已经安装。');
|
|
120
150
|
}
|
|
@@ -169,6 +199,13 @@ async function assertTypedIssueOrigin(intent, targets) {
|
|
|
169
199
|
}
|
|
170
200
|
return;
|
|
171
201
|
}
|
|
202
|
+
else if (intent.originIssue.category === 'sourceMissing') {
|
|
203
|
+
if (intent.originIssue.actionId !== 'cleanup' || intent.action !== 'cleanup'
|
|
204
|
+
|| !selected || !selected.errors.some((error) => error.code === 'sourceMissing')) {
|
|
205
|
+
stale('原来源缺失问题已变化。');
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
172
209
|
else if (intent.originIssue.category === 'scopeConflict') {
|
|
173
210
|
const installedScopes = new Set(targets.filter((target) => target.pluginId === intent.pluginId
|
|
174
211
|
&& target.platform === intent.platform
|
|
@@ -205,22 +242,28 @@ async function loadDependencyOverviews(options) {
|
|
|
205
242
|
freshnessPolicy: 'cacheFirst',
|
|
206
243
|
env: options.context.env,
|
|
207
244
|
signal: options.signal,
|
|
245
|
+
...(options.catalogOperationContext
|
|
246
|
+
? { operationContext: options.catalogOperationContext }
|
|
247
|
+
: {}),
|
|
248
|
+
...(options.bootstrapMissingActive === false ? { bootstrapMissingActive: false } : {}),
|
|
208
249
|
consume: async (catalog) => ({
|
|
209
|
-
nodes:
|
|
210
|
-
?
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
250
|
+
nodes: catalog.sources.flatMap((item) => item.resolution
|
|
251
|
+
? item.resolution.manifest.entries.map((entry) => ({
|
|
252
|
+
pluginId: formatPluginId({
|
|
253
|
+
sourceId: item.resolution.sourceId,
|
|
254
|
+
pluginName: entry.name,
|
|
255
|
+
}),
|
|
256
|
+
displayName: entry.interface?.displayName ?? entry.name,
|
|
257
|
+
dependencies: entry.dependencies.map((pluginName) => formatPluginId({ sourceId: item.resolution.sourceId, pluginName })),
|
|
258
|
+
}))
|
|
259
|
+
: []),
|
|
218
260
|
sourceErrors: catalog.sources.flatMap((item) => item.error
|
|
219
261
|
? [{ sourceId: item.sourceId, code: 'dependencyGraphInvalid' }]
|
|
220
262
|
: []),
|
|
221
263
|
}),
|
|
222
264
|
});
|
|
223
265
|
const nodes = new Map(graph.nodes.map((node) => [node.pluginId, node]));
|
|
266
|
+
const summaryById = new Map(options.status.pluginSummaries.map((summary) => [summary.pluginId, summary]));
|
|
224
267
|
const displayName = (pluginId) => nodes.get(pluginId)?.displayName
|
|
225
268
|
?? parsePluginId(pluginId).pluginName;
|
|
226
269
|
const reverse = new Map();
|
|
@@ -233,6 +276,9 @@ async function loadDependencyOverviews(options) {
|
|
|
233
276
|
for (const target of options.status.payload.targets) {
|
|
234
277
|
targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
|
|
235
278
|
}
|
|
279
|
+
const unsupportedPluginIds = new Set(options.status.pluginSummaries
|
|
280
|
+
.filter((summary) => (summary.missingCapabilities?.length ?? 0) > 0)
|
|
281
|
+
.map((summary) => summary.pluginId));
|
|
236
282
|
const placementReferences = new Map();
|
|
237
283
|
for (const consumer of options.status.payload.targets.filter((target) => target.state !== 'absent')) {
|
|
238
284
|
const consumerNode = nodes.get(consumer.pluginId);
|
|
@@ -279,7 +325,8 @@ async function loadDependencyOverviews(options) {
|
|
|
279
325
|
};
|
|
280
326
|
const aggregateFor = (pluginId, placements) => {
|
|
281
327
|
const sourceId = parsePluginId(pluginId).sourceId;
|
|
282
|
-
if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
|
|
328
|
+
if (graph.sourceErrors.some((error) => error.sourceId === sourceId)
|
|
329
|
+
|| unsupportedPluginIds.has(pluginId))
|
|
283
330
|
return 'unavailable';
|
|
284
331
|
if (placements.length === 0 || placements.every((placement) => placement.state === 'notInstalled')) {
|
|
285
332
|
return 'notInstalled';
|
|
@@ -359,6 +406,13 @@ async function loadDependencyOverviews(options) {
|
|
|
359
406
|
title: '依赖关系需要处理',
|
|
360
407
|
explanation: '当前目标的依赖证据不能安全通过检查。',
|
|
361
408
|
})));
|
|
409
|
+
const consumerTargets = summaryById.get(pluginId)?.supportedTargets;
|
|
410
|
+
const unsupportedDependency = dependencyItems.find((dependency) => {
|
|
411
|
+
const dependencyTargets = summaryById.get(dependency.pluginId)?.supportedTargets;
|
|
412
|
+
if (!consumerTargets || !dependencyTargets)
|
|
413
|
+
return true;
|
|
414
|
+
return ['codex', 'claude'].some((platform) => consumerTargets[platform].length > 0 && dependencyTargets[platform].length === 0);
|
|
415
|
+
});
|
|
362
416
|
const sourceId = parsePluginId(pluginId).sourceId;
|
|
363
417
|
const blockers = [
|
|
364
418
|
...graph.sourceErrors.filter((error) => error.sourceId === sourceId).map((error) => ({
|
|
@@ -371,6 +425,11 @@ async function loadDependencyOverviews(options) {
|
|
|
371
425
|
title: '依赖关系无效',
|
|
372
426
|
explanation: '依赖关系形成环。',
|
|
373
427
|
}] : []),
|
|
428
|
+
...(unsupportedDependency ? [{
|
|
429
|
+
code: 'dependencyTargetUnsupported',
|
|
430
|
+
title: '依赖目标不兼容',
|
|
431
|
+
explanation: `${unsupportedDependency.displayName} 不支持根插件所需的平台。`,
|
|
432
|
+
}] : []),
|
|
374
433
|
...targetBlockers,
|
|
375
434
|
];
|
|
376
435
|
const directCount = dependencyItems.filter((item) => item.relation === 'direct').length;
|
|
@@ -418,40 +477,85 @@ function createReviewStageReporter(options) {
|
|
|
418
477
|
};
|
|
419
478
|
}
|
|
420
479
|
export function createTuiControllerDependencies(context, progressFeed, options = {}) {
|
|
480
|
+
const readInventory = options.inventoryCatalogSourceReferences
|
|
481
|
+
?? inventoryCatalogSourceReferences;
|
|
482
|
+
const createOperationInventoryReader = () => {
|
|
483
|
+
let ordinaryInventory;
|
|
484
|
+
return (inventoryOptions) => {
|
|
485
|
+
if (inventoryOptions.includeHistoricalProjects === true) {
|
|
486
|
+
return readInventory(inventoryOptions);
|
|
487
|
+
}
|
|
488
|
+
ordinaryInventory ??= readInventory(inventoryOptions);
|
|
489
|
+
return ordinaryInventory;
|
|
490
|
+
};
|
|
491
|
+
};
|
|
492
|
+
const catalogOperationByEvidence = new WeakMap();
|
|
493
|
+
let sessionCatalogOperationContext;
|
|
494
|
+
let sessionStatusResult;
|
|
495
|
+
let bootstrapNextCatalogSession = true;
|
|
496
|
+
const sessionContextForPlugin = (pluginId) => {
|
|
497
|
+
const sourceId = parsePluginId(pluginId).sourceId;
|
|
498
|
+
if (!sourceId || !sessionCatalogOperationContext)
|
|
499
|
+
return undefined;
|
|
500
|
+
return selectCatalogOperationContext(sessionCatalogOperationContext, [sourceId]);
|
|
501
|
+
};
|
|
421
502
|
const loadStatus = async (signal) => {
|
|
422
503
|
signal.throwIfAborted();
|
|
423
504
|
const packageInfo = await readCliPackageInfo();
|
|
505
|
+
const catalogOperationContext = sessionCatalogOperationContext
|
|
506
|
+
?? await resolvePartialCatalogOperationContext({
|
|
507
|
+
cliVersion: packageInfo.version,
|
|
508
|
+
env: context.env,
|
|
509
|
+
signal,
|
|
510
|
+
...(bootstrapNextCatalogSession ? {} : { bootstrapMissingActive: false }),
|
|
511
|
+
...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
|
|
512
|
+
}).catch(() => undefined);
|
|
513
|
+
if (catalogOperationContext)
|
|
514
|
+
sessionCatalogOperationContext = catalogOperationContext;
|
|
424
515
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
425
|
-
const result = await
|
|
516
|
+
const result = await statusCommandV5({
|
|
426
517
|
cwd: context.cwd,
|
|
427
518
|
env: context.env,
|
|
428
519
|
cliVersion: packageInfo.version,
|
|
429
520
|
verbose: context.diagnostics.verbose,
|
|
521
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
522
|
+
...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
|
|
523
|
+
...(options.catalogGenerationRuntime ? { catalogGenerationRuntime: options.catalogGenerationRuntime } : {}),
|
|
524
|
+
inventoryCatalogSourceReferences: readInventory,
|
|
430
525
|
});
|
|
431
526
|
signal.throwIfAborted();
|
|
432
527
|
const configured = await listCatalogSources({ env: context.env });
|
|
433
528
|
signal.throwIfAborted();
|
|
434
529
|
if (result.payload.catalog.configDigest !== configured.configDigest)
|
|
435
530
|
continue;
|
|
531
|
+
sessionStatusResult = result;
|
|
436
532
|
const dependencyOverviews = await loadDependencyOverviews({
|
|
437
533
|
context,
|
|
438
534
|
cliVersion: packageInfo.version,
|
|
439
535
|
status: result,
|
|
440
536
|
signal,
|
|
537
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
538
|
+
...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
|
|
441
539
|
});
|
|
442
540
|
const portablePluginIds = await loadPortablePluginIds({
|
|
443
541
|
context,
|
|
444
542
|
cliVersion: packageInfo.version,
|
|
445
543
|
signal,
|
|
544
|
+
...(catalogOperationContext ? { catalogOperationContext } : {}),
|
|
545
|
+
...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
|
|
446
546
|
});
|
|
447
547
|
signal.throwIfAborted();
|
|
548
|
+
const catalogGenerations = await loadCatalogGenerationViewModels({ env: context.env });
|
|
549
|
+
signal.throwIfAborted();
|
|
448
550
|
const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
|
|
449
551
|
return {
|
|
552
|
+
cliChannel: packageInfo.version.includes('-') ? 'beta' : 'latest',
|
|
450
553
|
payload: result.payload,
|
|
451
554
|
pluginSummaries: result.pluginSummaries,
|
|
452
555
|
targetRevisions: result.targetRevisions,
|
|
453
556
|
dependencyOverviews,
|
|
454
557
|
portablePluginIds,
|
|
558
|
+
catalogGenerations,
|
|
455
559
|
sourceDetails: configured.sources.map((source) => {
|
|
456
560
|
const status = statusById.get(source.id);
|
|
457
561
|
return {
|
|
@@ -479,7 +583,38 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
479
583
|
}
|
|
480
584
|
throw new TuiOperationBlockedError('operationReviewStale', '状态读取期间来源配置持续变化。请重试。');
|
|
481
585
|
};
|
|
482
|
-
const captureEvidence = async (intent, signal,
|
|
586
|
+
const captureEvidence = async (intent, signal, preparation, bootstrapMissingActive = true, operationInventory = createOperationInventoryReader()) => {
|
|
587
|
+
if (intent.kind === 'catalogActivate') {
|
|
588
|
+
signal?.throwIfAborted();
|
|
589
|
+
const source = (await loadCatalogGenerationViewModels({ env: context.env }))
|
|
590
|
+
.find((entry) => entry.sourceId === intent.sourceId);
|
|
591
|
+
if (!source?.active || !source.candidate) {
|
|
592
|
+
throw operationReviewStale('Catalog Candidate 不再可用。');
|
|
593
|
+
}
|
|
594
|
+
if (source.candidate.generationKey !== intent.candidateGenerationKey) {
|
|
595
|
+
throw operationReviewStale('Catalog Candidate 已变化。');
|
|
596
|
+
}
|
|
597
|
+
const sessionCatalog = sessionCatalogOperationContext?.catalog;
|
|
598
|
+
return {
|
|
599
|
+
catalog: {
|
|
600
|
+
configDigest: sessionCatalog?.configDigest ?? 'unavailable',
|
|
601
|
+
compositeDigest: sessionCatalog?.compositeDigest ?? 'unavailable',
|
|
602
|
+
...(sessionCatalogOperationContext?.generationSetDigest
|
|
603
|
+
? { generationSetDigest: sessionCatalogOperationContext.generationSetDigest }
|
|
604
|
+
: {}),
|
|
605
|
+
},
|
|
606
|
+
catalogCandidate: {
|
|
607
|
+
sourceId: source.sourceId,
|
|
608
|
+
activeVersion: source.active.catalogVersion,
|
|
609
|
+
candidateVersion: source.candidate.catalogVersion,
|
|
610
|
+
channel: source.candidate.channel,
|
|
611
|
+
requestedRef: source.candidate.requestedRef,
|
|
612
|
+
candidateGenerationKey: source.candidate.generationKey,
|
|
613
|
+
digestShort: source.candidate.digestShort,
|
|
614
|
+
pluginChanges: source.candidate.pluginChanges,
|
|
615
|
+
},
|
|
616
|
+
};
|
|
617
|
+
}
|
|
483
618
|
if (intent.kind === 'sourceMutation') {
|
|
484
619
|
signal?.throwIfAborted();
|
|
485
620
|
if (intent.action === 'add' && !intent.source
|
|
@@ -522,8 +657,40 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
522
657
|
}
|
|
523
658
|
signal?.throwIfAborted();
|
|
524
659
|
const packageInfo = await readCliPackageInfo();
|
|
660
|
+
const offlineCleanupReason = intent.kind === 'pluginWrite'
|
|
661
|
+
&& intent.action === 'cleanup'
|
|
662
|
+
&& intent.originIssue?.actionId === 'cleanup'
|
|
663
|
+
&& (intent.originIssue?.category === 'sourceMissing'
|
|
664
|
+
|| intent.originIssue?.category === 'sourceUnavailable')
|
|
665
|
+
? intent.originIssue.category === 'sourceMissing' ? 'sourceMissing' : 'bundleUnavailable'
|
|
666
|
+
: undefined;
|
|
667
|
+
let catalogOperation;
|
|
668
|
+
let catalogOperationError;
|
|
669
|
+
if (!offlineCleanupReason && intent.kind !== 'portableInstall'
|
|
670
|
+
&& sessionCatalogOperationContext) {
|
|
671
|
+
try {
|
|
672
|
+
const sourceIds = [parsePluginId(intent.pluginId).sourceId];
|
|
673
|
+
catalogOperation = bootstrapMissingActive
|
|
674
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
|
|
675
|
+
: await resolveCatalogOperationContext({
|
|
676
|
+
cliVersion: packageInfo.version,
|
|
677
|
+
sourceIds,
|
|
678
|
+
env: context.env,
|
|
679
|
+
...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
|
|
680
|
+
...(signal ? { signal } : {}),
|
|
681
|
+
...(!bootstrapMissingActive ? { bootstrapMissingActive: false } : {}),
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
catalogOperationError = error;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
525
688
|
if (intent.kind === 'portableInstall') {
|
|
526
689
|
await preparation?.reporter.emit('portable');
|
|
690
|
+
const portableSourceId = parsePluginId(intent.pluginId).sourceId;
|
|
691
|
+
const portableCatalogOperation = sessionCatalogOperationContext
|
|
692
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, [portableSourceId])
|
|
693
|
+
: undefined;
|
|
527
694
|
const portable = await preparePortableInstall({
|
|
528
695
|
selector: intent.pluginId,
|
|
529
696
|
scope: intent.scope,
|
|
@@ -533,20 +700,106 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
533
700
|
env: context.env,
|
|
534
701
|
cliVersion: packageInfo.version,
|
|
535
702
|
signal,
|
|
703
|
+
...(portableCatalogOperation
|
|
704
|
+
? { catalogOperationContext: portableCatalogOperation }
|
|
705
|
+
: {}),
|
|
536
706
|
});
|
|
537
707
|
signal?.throwIfAborted();
|
|
538
708
|
await preparation?.reporter.emit('evidence');
|
|
539
709
|
signal?.throwIfAborted();
|
|
540
|
-
|
|
710
|
+
const evidence = {
|
|
541
711
|
catalog: {
|
|
542
712
|
configDigest: portable.configDigest,
|
|
543
713
|
compositeDigest: portable.evidenceDigest,
|
|
714
|
+
...(portable.generationSetDigest
|
|
715
|
+
? { generationSetDigest: portable.generationSetDigest }
|
|
716
|
+
: {}),
|
|
544
717
|
},
|
|
545
718
|
portable,
|
|
546
719
|
};
|
|
720
|
+
if (catalogOperation)
|
|
721
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
722
|
+
return evidence;
|
|
723
|
+
}
|
|
724
|
+
if (preparation && (intent.kind === 'configSave' || intent.kind === 'configReset')) {
|
|
725
|
+
if (!sessionStatusResult || !catalogOperation) {
|
|
726
|
+
throw operationReviewStale('当前 TUI session 没有可复用的设置证据。');
|
|
727
|
+
}
|
|
728
|
+
await preparation.reporter.emit('config');
|
|
729
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
730
|
+
signal?.throwIfAborted();
|
|
731
|
+
if (configSnapshot.digest !== sessionStatusResult.payload.catalog.configDigest) {
|
|
732
|
+
throw operationReviewStale('准备设置确认期间来源配置发生变化。');
|
|
733
|
+
}
|
|
734
|
+
const selected = await withResolvedCatalogPlugin({
|
|
735
|
+
selector: intent.pluginId,
|
|
736
|
+
cliVersion: packageInfo.version,
|
|
737
|
+
freshnessPolicy: 'cacheFirst',
|
|
738
|
+
configSnapshot,
|
|
739
|
+
env: context.env,
|
|
740
|
+
operationContext: catalogOperation,
|
|
741
|
+
...(signal ? { signal } : {}),
|
|
742
|
+
consume: async ({ plugin, source }) => {
|
|
743
|
+
if (!plugin.manifest.config) {
|
|
744
|
+
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
745
|
+
}
|
|
746
|
+
if (intent.kind === 'configSave') {
|
|
747
|
+
await coercePluginConfigUpdates({
|
|
748
|
+
schema: plugin.manifest.config.schema,
|
|
749
|
+
updates: intent.updates ?? {},
|
|
750
|
+
cwd: context.cwd,
|
|
751
|
+
});
|
|
752
|
+
for (const key of intent.removeKeys ?? []) {
|
|
753
|
+
if (!plugin.manifest.config.schema[key]) {
|
|
754
|
+
throw new TuiOperationBlockedError('unknownConfigKey', '设置字段已变化,请重新检查。');
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return {
|
|
759
|
+
pluginName: plugin.manifest.name,
|
|
760
|
+
source: sourceEvidenceForPlugin(source, plugin.manifest.name),
|
|
761
|
+
};
|
|
762
|
+
},
|
|
763
|
+
});
|
|
764
|
+
const store = await readPluginConfigStore({
|
|
765
|
+
sourceId: selected.source.sourceId,
|
|
766
|
+
sourceIdentity: selected.source.sourceIdentity,
|
|
767
|
+
pluginName: selected.pluginName,
|
|
768
|
+
scope: intent.scope,
|
|
769
|
+
projectLayer: intent.projectLayer,
|
|
770
|
+
cwd: context.cwd,
|
|
771
|
+
env: context.env,
|
|
772
|
+
});
|
|
773
|
+
signal?.throwIfAborted();
|
|
774
|
+
const relevantTargets = configAffectedStatusTargets(sessionStatusResult.payload.targets, intent.pluginId, intent.scope);
|
|
775
|
+
const relevantRevisions = (sessionStatusResult.targetRevisions ?? []).filter((revision) => relevantTargets.some((target) => target.platform === revision.platform
|
|
776
|
+
&& target.scope === revision.scope
|
|
777
|
+
&& target.pluginId === revision.pluginId));
|
|
778
|
+
await preparation.reporter.emit('evidence');
|
|
779
|
+
const evidence = {
|
|
780
|
+
catalog: {
|
|
781
|
+
configDigest: sessionStatusResult.payload.catalog.configDigest,
|
|
782
|
+
compositeDigest: sessionStatusResult.payload.catalog.compositeDigest,
|
|
783
|
+
generationSetDigest: catalogOperation.generationSetDigest,
|
|
784
|
+
},
|
|
785
|
+
source: selected.source,
|
|
786
|
+
targetFingerprint: statusTargetFingerprint(relevantTargets, relevantRevisions),
|
|
787
|
+
config: {
|
|
788
|
+
scope: intent.scope,
|
|
789
|
+
projectLayer: intent.projectLayer,
|
|
790
|
+
schemaIdentity: selected.source.manifestDigest,
|
|
791
|
+
storeDigest: digest({ exists: store.exists, values: store.values }),
|
|
792
|
+
affectedTargets: relevantTargets.map(({ platform, scope }) => ({ platform, scope })),
|
|
793
|
+
},
|
|
794
|
+
};
|
|
795
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
796
|
+
return evidence;
|
|
547
797
|
}
|
|
548
798
|
await preparation?.reporter.emit('status');
|
|
549
|
-
const
|
|
799
|
+
const statusCatalogOperation = offlineCleanupReason
|
|
800
|
+
? undefined
|
|
801
|
+
: catalogOperation ?? (bootstrapMissingActive ? sessionCatalogOperationContext : undefined);
|
|
802
|
+
const status = await statusCommandV5({
|
|
550
803
|
plugins: [...new Set([intent.pluginId,
|
|
551
804
|
...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
|
|
552
805
|
? [intent.originIssue.consumerPluginId] : [])])],
|
|
@@ -557,39 +810,77 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
557
810
|
cwd: context.cwd,
|
|
558
811
|
env: context.env,
|
|
559
812
|
cliVersion: packageInfo.version,
|
|
813
|
+
...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
|
|
814
|
+
...(offlineCleanupReason || !bootstrapMissingActive
|
|
815
|
+
|| (!statusCatalogOperation && sessionCatalogOperationContext)
|
|
816
|
+
? { bootstrapMissingActive: false }
|
|
817
|
+
: {}),
|
|
818
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
560
819
|
});
|
|
561
820
|
signal?.throwIfAborted();
|
|
821
|
+
const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
|
|
822
|
+
signal?.throwIfAborted();
|
|
823
|
+
if (configSnapshot.digest !== status.payload.catalog.configDigest) {
|
|
824
|
+
throw operationReviewStale('准备确认信息期间来源配置发生变化。');
|
|
825
|
+
}
|
|
826
|
+
if (catalogOperationError)
|
|
827
|
+
throw catalogOperationError;
|
|
562
828
|
if (intent.kind === 'pluginWrite') {
|
|
563
829
|
await assertTypedIssueOrigin(intent, status.payload.targets);
|
|
564
830
|
if (preparation?.validateIntent) {
|
|
565
831
|
assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
|
|
566
832
|
}
|
|
567
833
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
834
|
+
if (!offlineCleanupReason && !catalogOperation) {
|
|
835
|
+
catalogOperation = await resolveCatalogOperationContext({
|
|
836
|
+
cliVersion: packageInfo.version,
|
|
837
|
+
sourceIds: [parsePluginId(intent.pluginId).sourceId],
|
|
838
|
+
configSnapshot,
|
|
839
|
+
env: context.env,
|
|
840
|
+
...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
|
|
841
|
+
...(signal ? { signal } : {}),
|
|
842
|
+
...(!bootstrapMissingActive ? { bootstrapMissingActive: false } : {}),
|
|
843
|
+
});
|
|
572
844
|
}
|
|
573
845
|
let dependencyPlan;
|
|
574
846
|
await preparation?.reporter.emit('catalog');
|
|
575
|
-
const
|
|
576
|
-
&& intent.action === 'cleanup'
|
|
577
|
-
&& intent.originIssue?.category === 'sourceUnavailable'
|
|
578
|
-
&& intent.originIssue.actionId === 'cleanup'
|
|
847
|
+
const offlineCleanupTarget = offlineCleanupReason && intent.kind === 'pluginWrite'
|
|
579
848
|
? status.payload.targets.find((target) => target.pluginId === intent.pluginId
|
|
580
849
|
&& target.platform === intent.platform
|
|
581
850
|
&& target.scope === intent.scope
|
|
582
|
-
&& target.errors.some((error) => error.code ===
|
|
851
|
+
&& target.errors.some((error) => error.code === offlineCleanupReason))
|
|
583
852
|
: undefined;
|
|
584
|
-
const catalogSource =
|
|
585
|
-
? status.payload.catalog.sources.find((source) => source.id ===
|
|
853
|
+
const catalogSource = offlineCleanupTarget
|
|
854
|
+
? status.payload.catalog.sources.find((source) => source.id === offlineCleanupTarget.sourceId)
|
|
586
855
|
: undefined;
|
|
587
|
-
|
|
856
|
+
let offlineCleanup;
|
|
857
|
+
if (offlineCleanupReason && offlineCleanupTarget) {
|
|
858
|
+
await preparation?.reporter.emit('inventory');
|
|
859
|
+
const inventory = await operationInventory({
|
|
860
|
+
cwd: context.cwd,
|
|
861
|
+
env: context.env,
|
|
862
|
+
});
|
|
863
|
+
signal?.throwIfAborted();
|
|
864
|
+
offlineCleanup = offlineCleanupEvidenceFromInventory({
|
|
865
|
+
reason: offlineCleanupReason,
|
|
866
|
+
pluginId: offlineCleanupTarget.pluginId,
|
|
867
|
+
pluginName: offlineCleanupTarget.pluginName,
|
|
868
|
+
sourceId: offlineCleanupTarget.sourceId,
|
|
869
|
+
sourceIdentity: offlineCleanupTarget.sourceIdentity,
|
|
870
|
+
platform: offlineCleanupTarget.platform,
|
|
871
|
+
scope: offlineCleanupTarget.scope,
|
|
872
|
+
inventory,
|
|
873
|
+
}) ?? undefined;
|
|
874
|
+
if (!offlineCleanup) {
|
|
875
|
+
throw operationReviewStale('离线清理现场证据已消失或无法验证。');
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
const selected = offlineCleanupTarget
|
|
588
879
|
? {
|
|
589
|
-
pluginName:
|
|
880
|
+
pluginName: offlineCleanupTarget.pluginName,
|
|
590
881
|
source: {
|
|
591
|
-
sourceId:
|
|
592
|
-
sourceIdentity:
|
|
882
|
+
sourceId: offlineCleanupTarget.sourceId,
|
|
883
|
+
sourceIdentity: offlineCleanupTarget.sourceIdentity,
|
|
593
884
|
...(catalogSource?.requestedRef ? { requestedRef: catalogSource.requestedRef } : {}),
|
|
594
885
|
...(catalogSource?.resolvedCommit ? { resolvedCommit: catalogSource.resolvedCommit } : {}),
|
|
595
886
|
},
|
|
@@ -597,9 +888,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
597
888
|
: await withResolvedCatalogPlugin({
|
|
598
889
|
selector: intent.pluginId,
|
|
599
890
|
cliVersion: packageInfo.version,
|
|
600
|
-
freshnessPolicy,
|
|
891
|
+
freshnessPolicy: 'cacheFirst',
|
|
601
892
|
configSnapshot,
|
|
602
893
|
env: context.env,
|
|
894
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
603
895
|
...(signal ? { signal } : {}),
|
|
604
896
|
consume: async ({ plugin, source }) => {
|
|
605
897
|
if (preparation?.validateIntent && intent.kind === 'configSave') {
|
|
@@ -624,48 +916,88 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
624
916
|
};
|
|
625
917
|
},
|
|
626
918
|
});
|
|
919
|
+
let codexManager;
|
|
627
920
|
const hasDeclaredDependencies = (status.pluginSummaries.find((summary) => summary.pluginId === intent.pluginId)?.dependencyTotalCount ?? 0) > 0;
|
|
628
|
-
if (intent.kind === 'pluginWrite'
|
|
629
|
-
&& hasDeclaredDependencies
|
|
630
|
-
&& (intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')) {
|
|
921
|
+
if (intent.kind === 'pluginWrite') {
|
|
631
922
|
const command = {
|
|
632
923
|
install: installWriteCommand,
|
|
633
924
|
upgrade: upgradeWriteCommand,
|
|
634
925
|
reinstall: reinstallWriteCommand,
|
|
926
|
+
uninstall: uninstallWriteCommand,
|
|
927
|
+
cleanup: cleanupWriteCommand,
|
|
635
928
|
}[intent.action];
|
|
636
929
|
const preview = await command([intent.pluginId], {
|
|
637
930
|
cwd: context.cwd,
|
|
638
931
|
platform: intent.platform,
|
|
639
932
|
scope: intent.scope,
|
|
933
|
+
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
640
934
|
}, {
|
|
641
935
|
...context,
|
|
936
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
642
937
|
expectedCatalogConfigDigest: status.payload.catalog.configDigest,
|
|
938
|
+
...(catalogOperation ? {
|
|
939
|
+
catalogOperationContext: catalogOperation,
|
|
940
|
+
expectedGenerationSetDigest: catalogOperation.generationSetDigest,
|
|
941
|
+
} : {}),
|
|
643
942
|
previewDependencyPlanOnly: true,
|
|
644
943
|
...(preparation ? {
|
|
645
|
-
previewCatalogFreshnessPolicy: 'cacheFirst',
|
|
646
944
|
onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
|
|
647
945
|
} : {}),
|
|
648
946
|
});
|
|
649
947
|
signal?.throwIfAborted();
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
948
|
+
if (hasDeclaredDependencies
|
|
949
|
+
&& (intent.action === 'install'
|
|
950
|
+
|| intent.action === 'upgrade'
|
|
951
|
+
|| intent.action === 'reinstall')) {
|
|
952
|
+
dependencyPlan = preview.dependencyPlan;
|
|
953
|
+
if (!dependencyPlan) {
|
|
954
|
+
throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
|
|
955
|
+
}
|
|
956
|
+
const blocker = dependencyPlan.targets.find((target) => target.action === 'block');
|
|
957
|
+
if (blocker) {
|
|
958
|
+
throw new TuiOperationBlockedError(blocker.errorCode ?? 'dependencyNotReady', `${blocker.errorCode ?? 'dependencyNotReady'}: 依赖安装计划被阻止。`);
|
|
959
|
+
}
|
|
653
960
|
}
|
|
654
|
-
const
|
|
655
|
-
if (
|
|
656
|
-
|
|
961
|
+
const managerPreview = preview?.codexManagerPreview;
|
|
962
|
+
if (managerPreview?.state === 'blocked') {
|
|
963
|
+
const message = managerPreview.code === 'codexSystemCliUnsupported'
|
|
964
|
+
? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
965
|
+
: 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
|
|
966
|
+
throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
|
|
967
|
+
}
|
|
968
|
+
if (managerPreview) {
|
|
969
|
+
codexManager = {
|
|
970
|
+
state: managerPreview.state,
|
|
971
|
+
packageName: MANAGED_CODEX_PACKAGE_NAME,
|
|
972
|
+
version: managerPreview.state === 'requires-install'
|
|
973
|
+
? MANAGED_CODEX_VERSION
|
|
974
|
+
: managerPreview.manager.version,
|
|
975
|
+
};
|
|
657
976
|
}
|
|
658
977
|
}
|
|
659
978
|
await preparation?.reporter.emit('evidence');
|
|
660
979
|
signal?.throwIfAborted();
|
|
980
|
+
const evidenceTargets = intent.kind === 'configSave' || intent.kind === 'configReset'
|
|
981
|
+
? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.scope)
|
|
982
|
+
: status.payload.targets;
|
|
983
|
+
const evidenceRevisions = intent.kind === 'configSave' || intent.kind === 'configReset'
|
|
984
|
+
? (status.targetRevisions ?? []).filter((revision) => evidenceTargets.some((target) => target.platform === revision.platform
|
|
985
|
+
&& target.scope === revision.scope
|
|
986
|
+
&& target.pluginId === revision.pluginId))
|
|
987
|
+
: (status.targetRevisions ?? []);
|
|
661
988
|
const evidence = {
|
|
662
989
|
catalog: {
|
|
663
990
|
configDigest: status.payload.catalog.configDigest,
|
|
664
991
|
compositeDigest: status.payload.catalog.compositeDigest,
|
|
992
|
+
...(catalogOperation
|
|
993
|
+
? { generationSetDigest: catalogOperation.generationSetDigest }
|
|
994
|
+
: {}),
|
|
665
995
|
},
|
|
666
996
|
source: selected.source,
|
|
997
|
+
...(offlineCleanup ? { offlineCleanup } : {}),
|
|
667
998
|
...(dependencyPlan ? { dependencyPlan } : {}),
|
|
668
|
-
|
|
999
|
+
...(codexManager ? { codexManager } : {}),
|
|
1000
|
+
targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
|
|
669
1001
|
};
|
|
670
1002
|
if (intent.kind === 'configSave' || intent.kind === 'configReset') {
|
|
671
1003
|
const store = await readPluginConfigStore({
|
|
@@ -683,11 +1015,18 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
683
1015
|
projectLayer: intent.projectLayer,
|
|
684
1016
|
schemaIdentity: selected.source.manifestDigest,
|
|
685
1017
|
storeDigest: digest({ exists: store.exists, values: store.values }),
|
|
1018
|
+
affectedTargets: evidenceTargets.map(({ platform, scope }) => ({ platform, scope })),
|
|
686
1019
|
};
|
|
687
1020
|
}
|
|
1021
|
+
if (catalogOperation)
|
|
1022
|
+
catalogOperationByEvidence.set(evidence, catalogOperation);
|
|
688
1023
|
return evidence;
|
|
689
1024
|
};
|
|
690
1025
|
const dependencies = {
|
|
1026
|
+
beginCatalogSession(sessionOptions) {
|
|
1027
|
+
sessionCatalogOperationContext = undefined;
|
|
1028
|
+
bootstrapNextCatalogSession = sessionOptions?.bootstrapMissingActive !== false;
|
|
1029
|
+
},
|
|
691
1030
|
loadStatus,
|
|
692
1031
|
async inspectSourceRemoval(_sourceId, signal) {
|
|
693
1032
|
signal.throwIfAborted();
|
|
@@ -708,7 +1047,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
708
1047
|
now: options.now ?? Date.now,
|
|
709
1048
|
});
|
|
710
1049
|
try {
|
|
711
|
-
return await captureEvidence(intent, signal,
|
|
1050
|
+
return await captureEvidence(intent, signal, {
|
|
712
1051
|
reporter,
|
|
713
1052
|
validateIntent: true,
|
|
714
1053
|
});
|
|
@@ -718,6 +1057,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
718
1057
|
}
|
|
719
1058
|
},
|
|
720
1059
|
async validateIntent(intent, signal) {
|
|
1060
|
+
if (intent.kind === 'catalogActivate') {
|
|
1061
|
+
await captureEvidence(intent, signal);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
721
1064
|
if (intent.kind === 'sourceMutation') {
|
|
722
1065
|
const evidence = await captureEvidence(intent, signal);
|
|
723
1066
|
if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.candidateDigest) {
|
|
@@ -733,6 +1076,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
733
1076
|
freshnessPolicy: 'cacheFirst',
|
|
734
1077
|
env: context.env,
|
|
735
1078
|
signal,
|
|
1079
|
+
...(sessionContextForPlugin(intent.pluginId)
|
|
1080
|
+
? { operationContext: sessionContextForPlugin(intent.pluginId) }
|
|
1081
|
+
: {}),
|
|
736
1082
|
consume: async ({ plugin }) => {
|
|
737
1083
|
if (!plugin.manifest.config) {
|
|
738
1084
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
@@ -755,13 +1101,29 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
755
1101
|
return;
|
|
756
1102
|
signal.throwIfAborted();
|
|
757
1103
|
const packageInfo = await readCliPackageInfo();
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
1104
|
+
const pluginIds = [...new Set([intent.pluginId,
|
|
1105
|
+
...(intent.originIssue?.consumerPluginId ? [intent.originIssue.consumerPluginId] : [])])];
|
|
1106
|
+
const offlineCleanup = intent.action === 'cleanup'
|
|
1107
|
+
&& intent.originIssue?.actionId === 'cleanup'
|
|
1108
|
+
&& (intent.originIssue.category === 'sourceMissing'
|
|
1109
|
+
|| intent.originIssue.category === 'sourceUnavailable');
|
|
1110
|
+
const sourceIds = pluginIds.map((pluginId) => parsePluginId(pluginId).sourceId);
|
|
1111
|
+
const catalogOperation = offlineCleanup
|
|
1112
|
+
? undefined
|
|
1113
|
+
: sessionCatalogOperationContext
|
|
1114
|
+
? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
|
|
1115
|
+
: undefined;
|
|
1116
|
+
const status = await statusCommandV5({
|
|
1117
|
+
plugins: pluginIds,
|
|
761
1118
|
platform: intent.platform,
|
|
762
1119
|
cwd: context.cwd,
|
|
763
1120
|
env: context.env,
|
|
764
1121
|
cliVersion: packageInfo.version,
|
|
1122
|
+
...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
|
|
1123
|
+
...(offlineCleanup || sessionCatalogOperationContext
|
|
1124
|
+
? { bootstrapMissingActive: false }
|
|
1125
|
+
: {}),
|
|
1126
|
+
inventoryCatalogSourceReferences: readInventory,
|
|
765
1127
|
});
|
|
766
1128
|
signal.throwIfAborted();
|
|
767
1129
|
await assertTypedIssueOrigin(intent, status.payload.targets);
|
|
@@ -788,6 +1150,48 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
788
1150
|
},
|
|
789
1151
|
});
|
|
790
1152
|
},
|
|
1153
|
+
async refreshCatalogSource(sourceId, signal, onState) {
|
|
1154
|
+
let refreshError;
|
|
1155
|
+
let refreshCompleted = false;
|
|
1156
|
+
try {
|
|
1157
|
+
await refreshCatalogSourceGeneration({
|
|
1158
|
+
sourceId,
|
|
1159
|
+
cliVersion: (await readCliPackageInfo()).version,
|
|
1160
|
+
env: context.env,
|
|
1161
|
+
signal,
|
|
1162
|
+
runtime: {
|
|
1163
|
+
...options.catalogGenerationRuntime,
|
|
1164
|
+
onProgress: (event) => onState(event.state),
|
|
1165
|
+
},
|
|
1166
|
+
});
|
|
1167
|
+
refreshCompleted = true;
|
|
1168
|
+
}
|
|
1169
|
+
catch (error) {
|
|
1170
|
+
refreshError = error;
|
|
1171
|
+
}
|
|
1172
|
+
signal.throwIfAborted();
|
|
1173
|
+
const source = (await loadCatalogGenerationViewModels({ env: context.env }))
|
|
1174
|
+
.find((entry) => entry.sourceId === sourceId);
|
|
1175
|
+
if (!source)
|
|
1176
|
+
throw refreshError ?? new TuiOperationBlockedError('catalogGenerationSourceMissing', 'Catalog 来源已不存在。');
|
|
1177
|
+
if (refreshError && source.refreshState !== 'failed'
|
|
1178
|
+
&& source.refreshState !== 'immutableViolation')
|
|
1179
|
+
throw refreshError;
|
|
1180
|
+
return {
|
|
1181
|
+
source: refreshCompleted && source.refreshState === 'idle'
|
|
1182
|
+
? { ...source, refreshState: 'upToDate' }
|
|
1183
|
+
: source,
|
|
1184
|
+
};
|
|
1185
|
+
},
|
|
1186
|
+
async setCatalogRefreshPolicy(sourceId, refreshPolicy) {
|
|
1187
|
+
await setCatalogSourceRefreshPolicy({
|
|
1188
|
+
sourceId,
|
|
1189
|
+
refreshPolicy,
|
|
1190
|
+
env: context.env,
|
|
1191
|
+
});
|
|
1192
|
+
return (await loadCatalogGenerationViewModels({ env: context.env }))
|
|
1193
|
+
.find((source) => source.sourceId === sourceId);
|
|
1194
|
+
},
|
|
791
1195
|
captureEvidence,
|
|
792
1196
|
logError(_error, code, logContext) {
|
|
793
1197
|
return context.debugLogger.log({
|
|
@@ -808,6 +1212,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
808
1212
|
freshnessPolicy: 'cacheFirst',
|
|
809
1213
|
env: context.env,
|
|
810
1214
|
signal,
|
|
1215
|
+
...(sessionContextForPlugin(pluginId)
|
|
1216
|
+
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
1217
|
+
: {}),
|
|
811
1218
|
consume: async ({ plugin }) => {
|
|
812
1219
|
if (!plugin.manifest.config) {
|
|
813
1220
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
@@ -840,6 +1247,9 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
840
1247
|
freshnessPolicy: 'cacheFirst',
|
|
841
1248
|
env: context.env,
|
|
842
1249
|
signal,
|
|
1250
|
+
...(sessionContextForPlugin(pluginId)
|
|
1251
|
+
? { operationContext: sessionContextForPlugin(pluginId) }
|
|
1252
|
+
: {}),
|
|
843
1253
|
consume: async ({ plugin }) => {
|
|
844
1254
|
if (!plugin.manifest.config) {
|
|
845
1255
|
throw new TuiOperationBlockedError('configSchemaUnavailable', '这个插件没有设置 schema。');
|
|
@@ -860,7 +1270,39 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
860
1270
|
cwd: context.cwd,
|
|
861
1271
|
env: context.env,
|
|
862
1272
|
});
|
|
863
|
-
|
|
1273
|
+
const layerReads = layer.scope === 'effective' ? undefined : {
|
|
1274
|
+
user: await resolveConfigRead({
|
|
1275
|
+
pluginName: plugin.manifest.name,
|
|
1276
|
+
plugin,
|
|
1277
|
+
scope: 'user',
|
|
1278
|
+
projectLayer: 'shared',
|
|
1279
|
+
cwd: context.cwd,
|
|
1280
|
+
env: context.env,
|
|
1281
|
+
}),
|
|
1282
|
+
project: await resolveConfigRead({
|
|
1283
|
+
pluginName: plugin.manifest.name,
|
|
1284
|
+
plugin,
|
|
1285
|
+
scope: 'project',
|
|
1286
|
+
projectLayer: 'shared',
|
|
1287
|
+
cwd: context.cwd,
|
|
1288
|
+
env: context.env,
|
|
1289
|
+
}),
|
|
1290
|
+
local: await resolveConfigRead({
|
|
1291
|
+
pluginName: plugin.manifest.name,
|
|
1292
|
+
plugin,
|
|
1293
|
+
scope: 'project',
|
|
1294
|
+
projectLayer: 'local',
|
|
1295
|
+
cwd: context.cwd,
|
|
1296
|
+
env: context.env,
|
|
1297
|
+
}),
|
|
1298
|
+
};
|
|
1299
|
+
return buildConfigPage({
|
|
1300
|
+
pluginId,
|
|
1301
|
+
schema: plugin.manifest.config,
|
|
1302
|
+
read,
|
|
1303
|
+
effectiveRead,
|
|
1304
|
+
...(layerReads ? { layerReads } : {}),
|
|
1305
|
+
});
|
|
864
1306
|
},
|
|
865
1307
|
});
|
|
866
1308
|
},
|
|
@@ -920,6 +1362,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
920
1362
|
env: context.env,
|
|
921
1363
|
cliVersion: packageInfo.version,
|
|
922
1364
|
expectedEvidenceDigest: expectedEvidence.portable.evidenceDigest,
|
|
1365
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
923
1366
|
onProgress(stage) {
|
|
924
1367
|
const phase = stage === 'prepare' ? '检查插件依赖并生成候选'
|
|
925
1368
|
: stage === 'add' ? '安装已选择的 Skills' : '验证安装结果';
|
|
@@ -934,49 +1377,84 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
934
1377
|
throw error;
|
|
935
1378
|
}
|
|
936
1379
|
}
|
|
1380
|
+
if (intent.kind === 'catalogActivate') {
|
|
1381
|
+
const currentEvidence = await captureEvidence(intent, undefined, undefined, false);
|
|
1382
|
+
if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
|
|
1383
|
+
throw operationReviewStale('确认后 Catalog Candidate 发生变化。');
|
|
1384
|
+
}
|
|
1385
|
+
try {
|
|
1386
|
+
return await activateCatalogSourceCandidate({
|
|
1387
|
+
sourceId: intent.sourceId,
|
|
1388
|
+
expectedCandidateGenerationKey: intent.candidateGenerationKey,
|
|
1389
|
+
env: context.env,
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
catch (error) {
|
|
1393
|
+
if (error instanceof CatalogGenerationError
|
|
1394
|
+
&& (error.code === 'catalogGenerationCandidateChanged'
|
|
1395
|
+
|| error.code === 'catalogGenerationCandidateUnavailable')) {
|
|
1396
|
+
throw operationReviewStale('确认后 Catalog Candidate 发生变化。');
|
|
1397
|
+
}
|
|
1398
|
+
throw error;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (intent.kind === 'sourceMutation') {
|
|
1402
|
+
const currentEvidence = await captureEvidence(intent);
|
|
1403
|
+
if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
|
|
1404
|
+
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
1405
|
+
}
|
|
1406
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
1407
|
+
const packageInfo = await readCliPackageInfo();
|
|
1408
|
+
const runtime = {
|
|
1409
|
+
onProgress: async (event) => {
|
|
1410
|
+
const phase = SOURCE_PROGRESS_PHASES[event.stage];
|
|
1411
|
+
progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
|
|
1412
|
+
await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
|
|
1413
|
+
},
|
|
1414
|
+
};
|
|
1415
|
+
const common = {
|
|
1416
|
+
cwd: context.cwd,
|
|
1417
|
+
env: context.env,
|
|
1418
|
+
expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
|
|
1419
|
+
runtime,
|
|
1420
|
+
};
|
|
1421
|
+
if (intent.action === 'add' && intent.source) {
|
|
1422
|
+
return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
|
|
1423
|
+
}
|
|
1424
|
+
if (intent.action === 'update' && intent.update) {
|
|
1425
|
+
return updateCatalogSourceCommand({ ...common, sourceId: intent.sourceId, update: intent.update, cliVersion: packageInfo.version });
|
|
1426
|
+
}
|
|
1427
|
+
if (intent.action === 'setPrimary') {
|
|
1428
|
+
return setPrimaryCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
|
|
1429
|
+
}
|
|
1430
|
+
if (intent.action === 'remove') {
|
|
1431
|
+
return removeCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
|
|
1432
|
+
}
|
|
1433
|
+
throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
|
|
1434
|
+
}
|
|
937
1435
|
return withAwHomeMutationLock({
|
|
938
1436
|
env: context.env,
|
|
939
1437
|
action: async (awHomeMutationToken) => {
|
|
940
|
-
|
|
1438
|
+
await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
|
|
1439
|
+
const operationInventory = createOperationInventoryReader();
|
|
1440
|
+
const currentEvidence = await captureEvidence(intent, undefined, undefined, false, operationInventory);
|
|
941
1441
|
if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
|
|
942
1442
|
throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
|
|
943
1443
|
}
|
|
944
|
-
|
|
945
|
-
if (
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
awHomeMutationToken,
|
|
949
|
-
onProgress: async (event) => {
|
|
950
|
-
const phase = SOURCE_PROGRESS_PHASES[event.stage];
|
|
951
|
-
progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase }, `来源操作:${phase}`);
|
|
952
|
-
await tuiControllerRuntimeTestHooks?.onCatalogSourceProgress?.(event);
|
|
953
|
-
},
|
|
954
|
-
};
|
|
955
|
-
const common = {
|
|
956
|
-
cwd: context.cwd,
|
|
957
|
-
env: context.env,
|
|
958
|
-
expectedConfigDigest: expectedEvidence.sourceWrite?.configDigest,
|
|
959
|
-
runtime,
|
|
960
|
-
};
|
|
961
|
-
if (intent.action === 'add' && intent.source) {
|
|
962
|
-
return addCatalogSourceCommand({ ...common, source: intent.source, cliVersion: packageInfo.version });
|
|
963
|
-
}
|
|
964
|
-
if (intent.action === 'update' && intent.update) {
|
|
965
|
-
return updateCatalogSourceCommand({ ...common, sourceId: intent.sourceId, update: intent.update, cliVersion: packageInfo.version });
|
|
966
|
-
}
|
|
967
|
-
if (intent.action === 'setPrimary') {
|
|
968
|
-
return setPrimaryCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
|
|
969
|
-
}
|
|
970
|
-
if (intent.action === 'remove') {
|
|
971
|
-
return removeCatalogSourceCommand({ ...common, sourceId: intent.sourceId });
|
|
972
|
-
}
|
|
973
|
-
throw new TuiOperationBlockedError('invalidSourceMutation', '来源操作缺少候选输入。');
|
|
1444
|
+
const catalogOperation = catalogOperationByEvidence.get(currentEvidence);
|
|
1445
|
+
if (expectedEvidence.catalog.generationSetDigest
|
|
1446
|
+
&& catalogOperation?.generationSetDigest !== expectedEvidence.catalog.generationSetDigest) {
|
|
1447
|
+
throw operationReviewStale('确认后 Active Catalog generation 发生变化。');
|
|
974
1448
|
}
|
|
1449
|
+
await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
|
|
975
1450
|
if (intent.kind === 'pluginWrite') {
|
|
976
1451
|
const options = {
|
|
977
1452
|
cwd: context.cwd,
|
|
978
1453
|
platform: intent.platform,
|
|
979
1454
|
scope: intent.scope,
|
|
1455
|
+
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
1456
|
+
? { allowManagedCodexInstall: true }
|
|
1457
|
+
: {}),
|
|
980
1458
|
...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
|
|
981
1459
|
};
|
|
982
1460
|
const command = {
|
|
@@ -987,14 +1465,28 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
987
1465
|
cleanup: cleanupWriteCommand,
|
|
988
1466
|
}[intent.action];
|
|
989
1467
|
const currentSource = currentEvidence.source;
|
|
990
|
-
|
|
1468
|
+
const offlineCleanup = currentEvidence.offlineCleanup;
|
|
1469
|
+
if (!offlineCleanup && (!expectedEvidence.source || !currentSource)) {
|
|
991
1470
|
throw new TuiOperationBlockedError('operationReviewStale', '确认信息缺少来源身份。');
|
|
992
1471
|
}
|
|
993
1472
|
const result = await command([intent.pluginId], options, {
|
|
994
1473
|
...context,
|
|
995
1474
|
awHomeMutationToken,
|
|
1475
|
+
inventoryCatalogSourceReferences: operationInventory,
|
|
1476
|
+
...(currentEvidence.dependencyPlan
|
|
1477
|
+
? { expectedOperationDependencyPlan: currentEvidence.dependencyPlan }
|
|
1478
|
+
: {}),
|
|
996
1479
|
expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
|
|
997
|
-
|
|
1480
|
+
...(catalogOperation ? {
|
|
1481
|
+
catalogOperationContext: catalogOperation,
|
|
1482
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
1483
|
+
} : {}),
|
|
1484
|
+
...(expectedEvidence.codexManager?.state === 'requires-install'
|
|
1485
|
+
? { managedCodexAuthorizer: async () => true }
|
|
1486
|
+
: {}),
|
|
1487
|
+
...(offlineCleanup
|
|
1488
|
+
? { expectedOfflineCleanupEvidence: offlineCleanup }
|
|
1489
|
+
: { reviewPreflight: (actual) => assertSourceEvidenceMatches(currentSource, actual) }),
|
|
998
1490
|
});
|
|
999
1491
|
throwIfWriteReviewStale(result);
|
|
1000
1492
|
return result;
|
|
@@ -1008,10 +1500,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1008
1500
|
return withResolvedCatalogPlugin({
|
|
1009
1501
|
selector: intent.pluginId,
|
|
1010
1502
|
cliVersion: packageInfo.version,
|
|
1011
|
-
freshnessPolicy: '
|
|
1503
|
+
freshnessPolicy: 'cacheFirst',
|
|
1012
1504
|
env: context.env,
|
|
1505
|
+
...(catalogOperation ? { operationContext: catalogOperation } : {}),
|
|
1013
1506
|
consume: async ({ plugin, source }) => {
|
|
1014
1507
|
assertSourceEvidenceMatches(currentSource, sourceEvidenceForPlugin(source, plugin.manifest.name));
|
|
1508
|
+
const applyTargets = currentEvidence.config?.affectedTargets.map((target) => ({
|
|
1509
|
+
pluginName: plugin.manifest.name,
|
|
1510
|
+
platform: target.platform,
|
|
1511
|
+
scope: target.scope,
|
|
1512
|
+
})) ?? [];
|
|
1015
1513
|
const result = intent.kind === 'configSave'
|
|
1016
1514
|
? await applyPluginConfigWrite({
|
|
1017
1515
|
plugin,
|
|
@@ -1021,8 +1519,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1021
1519
|
cwd: context.cwd,
|
|
1022
1520
|
env: context.env,
|
|
1023
1521
|
nativeCliRunner: context.nativeCliRunner,
|
|
1024
|
-
rebuildRuntime: {
|
|
1522
|
+
rebuildRuntime: {
|
|
1523
|
+
...context,
|
|
1524
|
+
awHomeMutationToken,
|
|
1525
|
+
...(catalogOperation ? {
|
|
1526
|
+
catalogOperationContext: catalogOperation,
|
|
1527
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
1528
|
+
} : {}),
|
|
1529
|
+
},
|
|
1025
1530
|
awHomeMutationToken,
|
|
1531
|
+
applyTargets,
|
|
1026
1532
|
request: { updates: intent.updates, removeKeys: intent.removeKeys },
|
|
1027
1533
|
})
|
|
1028
1534
|
: await resetPluginConfigWithHelper({
|
|
@@ -1033,8 +1539,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
|
|
|
1033
1539
|
cwd: context.cwd,
|
|
1034
1540
|
env: context.env,
|
|
1035
1541
|
nativeCliRunner: context.nativeCliRunner,
|
|
1036
|
-
rebuildRuntime: {
|
|
1542
|
+
rebuildRuntime: {
|
|
1543
|
+
...context,
|
|
1544
|
+
awHomeMutationToken,
|
|
1545
|
+
...(catalogOperation ? {
|
|
1546
|
+
catalogOperationContext: catalogOperation,
|
|
1547
|
+
expectedGenerationSetDigest: expectedEvidence.catalog.generationSetDigest,
|
|
1548
|
+
} : {}),
|
|
1549
|
+
},
|
|
1037
1550
|
awHomeMutationToken,
|
|
1551
|
+
applyTargets,
|
|
1038
1552
|
});
|
|
1039
1553
|
return result;
|
|
1040
1554
|
},
|