@lesliechan721/aw 0.1.0-beta.7 → 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 +71 -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,7 +1,10 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
+
import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
|
|
2
3
|
import { buildTuiViewModel, hasInstalledPlacement, } from './model.js';
|
|
3
4
|
import { safeDiagnosticCode } from './diagnostics.js';
|
|
5
|
+
import { targetHasArtifactCollision } from '../utils/target-eligibility.js';
|
|
4
6
|
import { TuiRouter, } from './router.js';
|
|
7
|
+
import { CatalogRefreshCoordinator, } from './catalog-generation.js';
|
|
5
8
|
function reviewPreparationStageLabel(stage) {
|
|
6
9
|
return {
|
|
7
10
|
validate: '验证当前状态',
|
|
@@ -42,7 +45,10 @@ function cloneFrozen(value) {
|
|
|
42
45
|
return deepFreeze(structuredClone(value));
|
|
43
46
|
}
|
|
44
47
|
export function operationEvidenceFingerprint(evidence) {
|
|
45
|
-
|
|
48
|
+
const { codexManager: _runtimeSelection, ...staleEvidence } = evidence;
|
|
49
|
+
return crypto.createHash('sha256')
|
|
50
|
+
.update(JSON.stringify(stableValue(staleEvidence)))
|
|
51
|
+
.digest('hex');
|
|
46
52
|
}
|
|
47
53
|
function sourceIdFromPluginId(pluginId) {
|
|
48
54
|
const separator = pluginId.indexOf('/');
|
|
@@ -69,6 +75,14 @@ function operationLabels(intent) {
|
|
|
69
75
|
destructive: false,
|
|
70
76
|
};
|
|
71
77
|
}
|
|
78
|
+
if (intent.kind === 'catalogActivate') {
|
|
79
|
+
return {
|
|
80
|
+
title: '使用 Catalog 更新',
|
|
81
|
+
description: '切换当前 Catalog generation;不会自动升级已安装插件,也不会修改设置。',
|
|
82
|
+
primaryLabel: '使用已下载的更新',
|
|
83
|
+
destructive: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
72
86
|
if (intent.kind === 'pluginWrite') {
|
|
73
87
|
if (intent.originIssue) {
|
|
74
88
|
if (intent.action === 'cleanup') {
|
|
@@ -163,7 +177,41 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
163
177
|
description: review.collisions.map((collision) => collision.name).join('、') }] : []),
|
|
164
178
|
];
|
|
165
179
|
}
|
|
180
|
+
if (intent.kind === 'catalogActivate') {
|
|
181
|
+
const candidate = evidence?.catalogCandidate;
|
|
182
|
+
if (!candidate) {
|
|
183
|
+
throw new TuiOperationBlockedError('catalogGenerationCandidateUnavailable', '缺少 Catalog Candidate Review 证据。');
|
|
184
|
+
}
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
kind: 'change',
|
|
188
|
+
title: `Catalog ${candidate.activeVersion} → ${candidate.candidateVersion}`,
|
|
189
|
+
description: `${candidate.channel === 'beta' ? 'Beta' : 'Release'} · ${candidate.requestedRef}`,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
kind: 'change',
|
|
193
|
+
title: `插件变化 +${candidate.pluginChanges.added} ~${candidate.pluginChanges.updated} −${candidate.pluginChanges.removed}`,
|
|
194
|
+
},
|
|
195
|
+
{ kind: 'keep', title: '已安装插件不会自动升级' },
|
|
196
|
+
{ kind: 'keep', title: '当前设置不会改变' },
|
|
197
|
+
{ kind: 'change', title: '激活后重新加载插件列表和依赖关系' },
|
|
198
|
+
];
|
|
199
|
+
}
|
|
166
200
|
if (intent.kind === 'pluginWrite') {
|
|
201
|
+
const runtimeChanges = evidence?.codexManager
|
|
202
|
+
? [{
|
|
203
|
+
kind: evidence.codexManager.state === 'requires-install' ? 'add' : 'keep',
|
|
204
|
+
group: 'runtime',
|
|
205
|
+
title: evidence.codexManager.state === 'requires-install'
|
|
206
|
+
? '获取 Codex runtime'
|
|
207
|
+
: 'Codex runtime',
|
|
208
|
+
description: evidence.codexManager.state === 'requires-install'
|
|
209
|
+
? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · AW-managed 缓存`
|
|
210
|
+
: evidence.codexManager.state === 'cached'
|
|
211
|
+
? `${evidence.codexManager.packageName}@${evidence.codexManager.version} · 复用 AW-managed 缓存`
|
|
212
|
+
: `使用兼容的 system Codex ${evidence.codexManager.version}`,
|
|
213
|
+
}]
|
|
214
|
+
: [];
|
|
167
215
|
const plan = evidence?.dependencyPlan;
|
|
168
216
|
if (plan && (intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')) {
|
|
169
217
|
const changes = plan.targets.map((target) => {
|
|
@@ -188,7 +236,7 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
188
236
|
title: '插件设置和已有依赖保持不变',
|
|
189
237
|
description: '移除根插件时不会自动清理依赖。',
|
|
190
238
|
});
|
|
191
|
-
return changes;
|
|
239
|
+
return [...changes, ...runtimeChanges];
|
|
192
240
|
}
|
|
193
241
|
const platforms = intent.platform === 'both'
|
|
194
242
|
? ['codex', 'claude']
|
|
@@ -220,17 +268,23 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
220
268
|
if (intent.originIssue) {
|
|
221
269
|
changes.push({ kind: 'keep', title: '插件设置保持不变' }, { kind: 'warning', title: '执行前重新检查完整目标和来源证据' });
|
|
222
270
|
}
|
|
223
|
-
return changes;
|
|
271
|
+
return [...changes, ...runtimeChanges];
|
|
224
272
|
}
|
|
225
273
|
if (intent.kind === 'configReset') {
|
|
226
274
|
const layerLabel = intent.scope === 'user'
|
|
227
275
|
? '个人默认'
|
|
228
276
|
: intent.projectLayer === 'local' ? '当前项目(仅本机)' : '当前项目';
|
|
229
277
|
return [
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
{ kind: '
|
|
278
|
+
...(intent.changes?.map((change) => ({
|
|
279
|
+
kind: 'remove', group: 'configChanges', title: `${layerLabel} · ${change.title}`,
|
|
280
|
+
description: `${change.before} → ${change.after}`,
|
|
281
|
+
})) ?? [{ kind: 'remove', group: 'configChanges', title: `删除${layerLabel}中的已存值`, description: '有效值将恢复为继承值或默认值。' }]),
|
|
282
|
+
...((evidence?.config?.affectedTargets.length ?? 0) > 0
|
|
283
|
+
? evidence.config.affectedTargets.map((target) => ({
|
|
284
|
+
kind: 'change', group: 'configImpact', title: `重新应用 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
285
|
+
}))
|
|
286
|
+
: [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
|
|
287
|
+
{ kind: 'keep', group: 'preserved', title: intent.scope === 'user' ? '当前项目和本机设置层保持不变' : '其他设置层保持不变' },
|
|
234
288
|
];
|
|
235
289
|
}
|
|
236
290
|
if (intent.kind === 'sourceMutation') {
|
|
@@ -255,17 +309,24 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
255
309
|
throw new TuiOperationBlockedError('operationNotImplemented', '这个写操作尚未接入新 Controller。');
|
|
256
310
|
}
|
|
257
311
|
const privateKeys = new Set([...(intent.sensitiveKeys ?? []), ...(intent.privateKeys ?? [])]);
|
|
312
|
+
const removeKeys = new Set(intent.removeKeys ?? []);
|
|
258
313
|
if (intent.changes) {
|
|
259
314
|
return [
|
|
260
315
|
...intent.changes.map((change) => ({
|
|
261
|
-
kind: 'change',
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: `${change.before} → ${change.after}
|
|
316
|
+
kind: removeKeys.has(change.key) ? 'remove' : 'change',
|
|
317
|
+
group: 'configChanges',
|
|
318
|
+
title: removeKeys.has(change.key) ? `删除 ${change.title} 中的值` : change.title,
|
|
319
|
+
description: removeKeys.has(change.key)
|
|
320
|
+
? privateKeys.has(change.key) ? `已设置 → ${change.after}` : `${change.before} → ${change.after}`
|
|
321
|
+
: privateKeys.has(change.key) ? '已设置 → 将更新'
|
|
322
|
+
: `${change.before} → ${change.after}`,
|
|
266
323
|
})),
|
|
267
|
-
|
|
268
|
-
|
|
324
|
+
...((evidence?.config?.affectedTargets.length ?? 0) > 0
|
|
325
|
+
? evidence.config.affectedTargets.map((target) => ({
|
|
326
|
+
kind: 'change', group: 'configImpact', title: `重新应用 ${target.platform === 'codex' ? 'Codex' : 'Claude Code'} · ${target.scope === 'user' ? '所有项目' : '当前项目'}`,
|
|
327
|
+
}))
|
|
328
|
+
: [{ kind: 'keep', group: 'configImpact', title: '当前没有可见安装;只保存设置' }]),
|
|
329
|
+
{ kind: 'keep', group: 'preserved', title: '其他项目不会被自动扫描' },
|
|
269
330
|
];
|
|
270
331
|
}
|
|
271
332
|
const updateKeys = Object.keys(intent.updates ?? {});
|
|
@@ -283,33 +344,45 @@ function reviewChanges(intent, evidence, dependencyOverview) {
|
|
|
283
344
|
];
|
|
284
345
|
}
|
|
285
346
|
function technicalDetails(evidence) {
|
|
347
|
+
const short = (value) => {
|
|
348
|
+
const normalized = value.startsWith('sha256:') ? value.slice(7) : value;
|
|
349
|
+
return normalized.slice(0, 8);
|
|
350
|
+
};
|
|
286
351
|
const details = [
|
|
287
|
-
`Catalog 配置:${evidence.catalog.configDigest}`,
|
|
288
|
-
`Catalog 组合:${evidence.catalog.compositeDigest}`,
|
|
352
|
+
`Catalog 配置:${short(evidence.catalog.configDigest)}`,
|
|
353
|
+
`Catalog 组合:${short(evidence.catalog.compositeDigest)}`,
|
|
289
354
|
];
|
|
355
|
+
if (evidence.catalog.generationSetDigest) {
|
|
356
|
+
details.push(`Catalog generation:${short(evidence.catalog.generationSetDigest)}`);
|
|
357
|
+
}
|
|
290
358
|
if (evidence.source) {
|
|
291
359
|
details.push(`来源:${evidence.source.sourceId}`);
|
|
292
|
-
|
|
360
|
+
if (evidence.source.sourceIdentity)
|
|
361
|
+
details.push(`来源身份:${short(evidence.source.sourceIdentity)}`);
|
|
293
362
|
if (evidence.source.resolvedCommit)
|
|
294
|
-
details.push(`Snapshot commit:${evidence.source.resolvedCommit}`);
|
|
363
|
+
details.push(`Snapshot commit:${short(evidence.source.resolvedCommit)}`);
|
|
295
364
|
if (evidence.source.snapshotDigest)
|
|
296
|
-
details.push(`Snapshot digest:${evidence.source.snapshotDigest}`);
|
|
365
|
+
details.push(`Snapshot digest:${short(evidence.source.snapshotDigest)}`);
|
|
297
366
|
if (evidence.source.contentDigest)
|
|
298
|
-
details.push(`Content digest:${evidence.source.contentDigest}`);
|
|
367
|
+
details.push(`Content digest:${short(evidence.source.contentDigest)}`);
|
|
299
368
|
}
|
|
300
369
|
if (evidence.targetFingerprint)
|
|
301
|
-
details.push(`目标状态:${evidence.targetFingerprint}`);
|
|
370
|
+
details.push(`目标状态:${short(evidence.targetFingerprint)}`);
|
|
371
|
+
if (evidence.offlineCleanup) {
|
|
372
|
+
details.push(`离线清理原因:${evidence.offlineCleanup.reason}`);
|
|
373
|
+
details.push(`离线清理目标:${short(evidence.offlineCleanup.targetFingerprint)}`);
|
|
374
|
+
}
|
|
302
375
|
if (evidence.dependencyPlan)
|
|
303
376
|
details.push(`安装计划:${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`);
|
|
304
377
|
if (evidence.config)
|
|
305
|
-
details.push(`设置层:${evidence.config.scope}/${evidence.config.projectLayer} · ${evidence.config.storeDigest}`);
|
|
378
|
+
details.push(`设置层:${evidence.config.scope}/${evidence.config.projectLayer} · ${short(evidence.config.storeDigest)}`);
|
|
306
379
|
if (evidence.sourceWrite) {
|
|
307
|
-
details.push(`候选配置:${evidence.sourceWrite.candidateDigest}`);
|
|
308
|
-
details.push(`引用清单:${evidence.sourceWrite.inventoryDigest} · ${evidence.sourceWrite.referenceCount} 项`);
|
|
380
|
+
details.push(`候选配置:${short(evidence.sourceWrite.candidateDigest)}`);
|
|
381
|
+
details.push(`引用清单:${short(evidence.sourceWrite.inventoryDigest)} · ${evidence.sourceWrite.referenceCount} 项`);
|
|
309
382
|
}
|
|
310
383
|
if (evidence.portable) {
|
|
311
384
|
details.push(`Portable 候选:${evidence.portable.selected.length} 个`);
|
|
312
|
-
details.push(`Materialization:${evidence.portable.materializationDigest}`);
|
|
385
|
+
details.push(`Materialization:${short(evidence.portable.materializationDigest)}`);
|
|
313
386
|
details.push(`Skills CLI:${evidence.portable.skillsCliVersion}`);
|
|
314
387
|
}
|
|
315
388
|
return details;
|
|
@@ -317,6 +390,8 @@ function technicalDetails(evidence) {
|
|
|
317
390
|
function issueCode(error) {
|
|
318
391
|
if (error instanceof TuiOperationBlockedError)
|
|
319
392
|
return error.code;
|
|
393
|
+
if (error instanceof CatalogGenerationError)
|
|
394
|
+
return error.code;
|
|
320
395
|
if (error instanceof Error && error.name === 'CatalogSourcesConfigConflictError') {
|
|
321
396
|
return 'operationReviewStale';
|
|
322
397
|
}
|
|
@@ -353,10 +428,12 @@ export class TuiController {
|
|
|
353
428
|
#tickets = new Map();
|
|
354
429
|
#inflightOperations = new Map();
|
|
355
430
|
#abort = new AbortController();
|
|
431
|
+
#catalogRefreshCoordinator;
|
|
356
432
|
#progressUnsubscribe;
|
|
357
433
|
#progressFeed;
|
|
358
434
|
#phase = 'idle';
|
|
359
435
|
#status;
|
|
436
|
+
#catalogGenerations = new Map();
|
|
360
437
|
#viewModel;
|
|
361
438
|
#progress = { messages: [] };
|
|
362
439
|
#progressTargets = [];
|
|
@@ -376,6 +453,26 @@ export class TuiController {
|
|
|
376
453
|
#disposed = false;
|
|
377
454
|
constructor(dependencies, progressFeed) {
|
|
378
455
|
this.dependencies = dependencies;
|
|
456
|
+
if (dependencies.refreshCatalogSource) {
|
|
457
|
+
this.#catalogRefreshCoordinator = new CatalogRefreshCoordinator({
|
|
458
|
+
maxConcurrency: 2,
|
|
459
|
+
execute: dependencies.refreshCatalogSource,
|
|
460
|
+
onUpdate: (sourceId, state, errorCode) => {
|
|
461
|
+
const current = this.#catalogGenerations.get(sourceId);
|
|
462
|
+
if (current) {
|
|
463
|
+
this.#catalogGenerations.set(sourceId, {
|
|
464
|
+
...current,
|
|
465
|
+
refreshState: state,
|
|
466
|
+
...(errorCode ? { lastCheckCode: errorCode } : {}),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
this.#publish();
|
|
470
|
+
},
|
|
471
|
+
onResult: (source) => {
|
|
472
|
+
this.#catalogGenerations.set(source.sourceId, structuredClone(source));
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
}
|
|
379
476
|
this.#progressFeed = progressFeed;
|
|
380
477
|
if (progressFeed) {
|
|
381
478
|
this.#progressUnsubscribe = progressFeed.subscribe((progress) => {
|
|
@@ -407,9 +504,12 @@ export class TuiController {
|
|
|
407
504
|
get snapshot() {
|
|
408
505
|
return cloneFrozen({
|
|
409
506
|
phase: this.#phase,
|
|
507
|
+
...(this.#status?.cliChannel ? { cliChannel: this.#status.cliChannel } : {}),
|
|
410
508
|
router: this.#router.snapshot,
|
|
411
509
|
...(this.#viewModel ? { viewModel: this.#viewModel } : {}),
|
|
412
510
|
sourceHealth: [...this.#health.values()],
|
|
511
|
+
catalogGenerations: [...this.#catalogGenerations.values()]
|
|
512
|
+
.sort((left, right) => left.sourceId.localeCompare(right.sourceId, 'en')),
|
|
413
513
|
progress: this.#progress,
|
|
414
514
|
progressTargets: this.#progressTargets,
|
|
415
515
|
...(this.#activeReview ? { activeReview: this.#activeReview } : {}),
|
|
@@ -495,6 +595,14 @@ export class TuiController {
|
|
|
495
595
|
&& !this.#viewModel?.sourceTabs.some((source) => source.id === route.sourceId)) {
|
|
496
596
|
return { name: 'sourceList' };
|
|
497
597
|
}
|
|
598
|
+
if ((route.name === 'catalogCandidate'
|
|
599
|
+
|| route.name === 'catalogRefreshPolicy'
|
|
600
|
+
|| route.name === 'catalogRefresh'
|
|
601
|
+
|| route.name === 'catalogImmutableViolation'
|
|
602
|
+
|| route.name === 'catalogActivationBlocked')
|
|
603
|
+
&& !this.#catalogGenerations.has(route.sourceId)) {
|
|
604
|
+
return { name: 'sourceList' };
|
|
605
|
+
}
|
|
498
606
|
return route;
|
|
499
607
|
}
|
|
500
608
|
#normalizeCurrentRoute() {
|
|
@@ -505,6 +613,12 @@ export class TuiController {
|
|
|
505
613
|
}
|
|
506
614
|
#applyStatus(status) {
|
|
507
615
|
this.#status = structuredClone(status);
|
|
616
|
+
if (status.catalogGenerations) {
|
|
617
|
+
this.#catalogGenerations.clear();
|
|
618
|
+
for (const source of status.catalogGenerations) {
|
|
619
|
+
this.#catalogGenerations.set(source.sourceId, structuredClone(source));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
508
622
|
this.#health.clear();
|
|
509
623
|
for (const source of status.payload.catalog.sources) {
|
|
510
624
|
this.#health.set(source.id, {
|
|
@@ -566,6 +680,10 @@ export class TuiController {
|
|
|
566
680
|
this.#sourceAborts.clear();
|
|
567
681
|
}
|
|
568
682
|
#startGitSourceRefreshes() {
|
|
683
|
+
if (this.#catalogRefreshCoordinator) {
|
|
684
|
+
this.#catalogRefreshCoordinator.start([...this.#catalogGenerations.values()]);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
569
687
|
for (const source of this.#status?.payload.catalog.sources ?? []) {
|
|
570
688
|
if (source.type === 'git')
|
|
571
689
|
void this.refreshSource(source.id).catch(() => undefined);
|
|
@@ -573,6 +691,7 @@ export class TuiController {
|
|
|
573
691
|
}
|
|
574
692
|
async load() {
|
|
575
693
|
this.#cancelSourceRefreshes();
|
|
694
|
+
this.dependencies.beginCatalogSession?.();
|
|
576
695
|
const generation = ++this.#generation;
|
|
577
696
|
this.#phase = 'loading';
|
|
578
697
|
this.#publish();
|
|
@@ -601,6 +720,10 @@ export class TuiController {
|
|
|
601
720
|
async refreshSource(sourceId) {
|
|
602
721
|
if (this.#disposed || !this.#status)
|
|
603
722
|
return;
|
|
723
|
+
if (this.#catalogRefreshCoordinator) {
|
|
724
|
+
await this.#catalogRefreshCoordinator.refresh(sourceId, this.#catalogGenerations.get(sourceId)?.refreshState ?? 'idle');
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
604
727
|
this.#sourceAborts.get(sourceId)?.abort();
|
|
605
728
|
const sourceAbort = new AbortController();
|
|
606
729
|
this.#sourceAborts.set(sourceId, sourceAbort);
|
|
@@ -907,9 +1030,11 @@ export class TuiController {
|
|
|
907
1030
|
this.#publish();
|
|
908
1031
|
}
|
|
909
1032
|
back() {
|
|
910
|
-
this.#cancelReviewPreparation();
|
|
911
|
-
this.#cancelPortableRequest();
|
|
912
1033
|
const route = this.#router.snapshot.current.route;
|
|
1034
|
+
if (route.name !== 'catalogActivationBlocked' || route.reason !== 'reviewPreparation') {
|
|
1035
|
+
this.#cancelReviewPreparation();
|
|
1036
|
+
}
|
|
1037
|
+
this.#cancelPortableRequest();
|
|
913
1038
|
if (route.name === 'review' && this.#activeReview?.operationId === route.operationId) {
|
|
914
1039
|
this.#tickets.delete(this.#activeReview.reviewToken);
|
|
915
1040
|
this.#activeReview = undefined;
|
|
@@ -1067,6 +1192,10 @@ export class TuiController {
|
|
|
1067
1192
|
}));
|
|
1068
1193
|
}
|
|
1069
1194
|
return plugin.supportedPlatforms.map((platform) => {
|
|
1195
|
+
const statusTarget = this.#status?.payload.targets.find((target) => (target.pluginId === pluginId && target.platform === platform && target.scope === scope));
|
|
1196
|
+
if (statusTarget && targetHasArtifactCollision(statusTarget)) {
|
|
1197
|
+
return { platform, compatible: false, disabled: '存在输出冲突;请先移除或迁移占用内容' };
|
|
1198
|
+
}
|
|
1070
1199
|
const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
|
|
1071
1200
|
if (!placement)
|
|
1072
1201
|
return { platform, compatible: false, disabled: '当前没有可信的平台能力信息' };
|
|
@@ -1085,6 +1214,16 @@ export class TuiController {
|
|
|
1085
1214
|
}
|
|
1086
1215
|
const overview = this.#viewModel?.dependencyOverviews.find((entry) => entry.pluginId === pluginId);
|
|
1087
1216
|
for (const dependency of overview?.dependencies ?? []) {
|
|
1217
|
+
const dependencyTarget = this.#status?.payload.targets.find((target) => (target.pluginId === dependency.pluginId
|
|
1218
|
+
&& target.platform === platform
|
|
1219
|
+
&& target.scope === scope));
|
|
1220
|
+
if (dependencyTarget && targetHasArtifactCollision(dependencyTarget)) {
|
|
1221
|
+
return {
|
|
1222
|
+
platform,
|
|
1223
|
+
compatible: false,
|
|
1224
|
+
disabled: `${dependency.displayName} 存在输出冲突`,
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1088
1227
|
const dependentPlugin = this.#viewModel?.plugins.find((entry) => entry.pluginId === dependency.pluginId);
|
|
1089
1228
|
if (!dependentPlugin || !dependentPlugin.supportedPlatforms.includes(platform)) {
|
|
1090
1229
|
return {
|
|
@@ -1128,6 +1267,89 @@ export class TuiController {
|
|
|
1128
1267
|
health: this.#health.get(sourceId),
|
|
1129
1268
|
});
|
|
1130
1269
|
}
|
|
1270
|
+
listCatalogCandidates() {
|
|
1271
|
+
return cloneFrozen([...this.#catalogGenerations.values()]
|
|
1272
|
+
.filter((source) => source.candidate)
|
|
1273
|
+
.sort((left, right) => left.sourceId.localeCompare(right.sourceId, 'en')));
|
|
1274
|
+
}
|
|
1275
|
+
getCatalogGeneration(sourceId) {
|
|
1276
|
+
const source = this.#catalogGenerations.get(sourceId);
|
|
1277
|
+
return source ? cloneFrozen(source) : undefined;
|
|
1278
|
+
}
|
|
1279
|
+
getSessionCatalogVersion(sourceId) {
|
|
1280
|
+
return this.#status?.payload.catalog.sources.find((source) => source.id === sourceId)?.catalogVersion;
|
|
1281
|
+
}
|
|
1282
|
+
beginCatalogActivation(options) {
|
|
1283
|
+
const source = this.#catalogGenerations.get(options.sourceId);
|
|
1284
|
+
if (!source?.candidate) {
|
|
1285
|
+
throw new TuiOperationBlockedError('catalogGenerationCandidateUnavailable', 'Catalog Candidate 不再可用。请重新检查来源。');
|
|
1286
|
+
}
|
|
1287
|
+
let reason;
|
|
1288
|
+
let preservedLabel;
|
|
1289
|
+
const refreshingSourceId = this.#catalogRefreshCoordinator?.refreshingSourceIds()[0];
|
|
1290
|
+
if (refreshingSourceId) {
|
|
1291
|
+
reason = 'refreshProgress';
|
|
1292
|
+
preservedLabel = options.dirtyDraft
|
|
1293
|
+
? `${options.dirtyDraft.label}已保留;${refreshingSourceId} Catalog 来源正在刷新`
|
|
1294
|
+
: `${refreshingSourceId} Catalog 来源刷新`;
|
|
1295
|
+
}
|
|
1296
|
+
else if (options.dirtyDraft) {
|
|
1297
|
+
reason = options.dirtyDraft.kind === 'config' ? 'configDraft' : 'sourceDraft';
|
|
1298
|
+
preservedLabel = options.dirtyDraft.label;
|
|
1299
|
+
}
|
|
1300
|
+
else if (this.#portableDraft) {
|
|
1301
|
+
reason = 'portableDraft';
|
|
1302
|
+
preservedLabel = 'Portable Skills 当前选择';
|
|
1303
|
+
}
|
|
1304
|
+
else if (this.#reviewPreparation)
|
|
1305
|
+
reason = 'reviewPreparation';
|
|
1306
|
+
else if (this.#router.snapshot.current.route.name === 'progress') {
|
|
1307
|
+
reason = this.#activeReview?.operationKind === 'sourceMutation'
|
|
1308
|
+
? 'sourceMutationProgress' : 'writeProgress';
|
|
1309
|
+
}
|
|
1310
|
+
else if (this.#activeReview)
|
|
1311
|
+
reason = 'activeReview';
|
|
1312
|
+
else {
|
|
1313
|
+
const refreshing = [...this.#catalogGenerations.values()].find((candidate) => candidate.refreshState === 'checking'
|
|
1314
|
+
|| candidate.refreshState === 'downloading'
|
|
1315
|
+
|| candidate.refreshState === 'validating');
|
|
1316
|
+
if (refreshing) {
|
|
1317
|
+
reason = 'refreshProgress';
|
|
1318
|
+
preservedLabel = `${refreshing.sourceId} Catalog 来源刷新`;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (reason) {
|
|
1322
|
+
this.#router.push({
|
|
1323
|
+
name: 'catalogActivationBlocked',
|
|
1324
|
+
sourceId: options.sourceId,
|
|
1325
|
+
reason,
|
|
1326
|
+
...(preservedLabel ? { preservedLabel } : {}),
|
|
1327
|
+
});
|
|
1328
|
+
this.#publish();
|
|
1329
|
+
return undefined;
|
|
1330
|
+
}
|
|
1331
|
+
return this.startReviewPreparation({
|
|
1332
|
+
kind: 'catalogActivate',
|
|
1333
|
+
sourceId: options.sourceId,
|
|
1334
|
+
candidateGenerationKey: source.candidate.generationKey,
|
|
1335
|
+
}, { name: 'catalogCandidate', sourceId: options.sourceId });
|
|
1336
|
+
}
|
|
1337
|
+
cancelCatalogRefresh(sourceId) {
|
|
1338
|
+
this.#catalogRefreshCoordinator?.cancel(sourceId);
|
|
1339
|
+
}
|
|
1340
|
+
async setCatalogRefreshPolicy(sourceId, refreshPolicy) {
|
|
1341
|
+
if (!this.dependencies.setCatalogRefreshPolicy) {
|
|
1342
|
+
throw new TuiOperationBlockedError('catalogGenerationRefreshPolicyUnavailable', 'Catalog 自动刷新设置不可用。');
|
|
1343
|
+
}
|
|
1344
|
+
const source = await this.dependencies.setCatalogRefreshPolicy(sourceId, refreshPolicy);
|
|
1345
|
+
if (this.#disposed)
|
|
1346
|
+
return;
|
|
1347
|
+
const current = this.#catalogGenerations.get(sourceId);
|
|
1348
|
+
this.#catalogGenerations.set(sourceId, current
|
|
1349
|
+
? { ...current, refreshPolicy: source.refreshPolicy }
|
|
1350
|
+
: structuredClone(source));
|
|
1351
|
+
this.#publish();
|
|
1352
|
+
}
|
|
1131
1353
|
async inspectSourceRemoval(sourceId) {
|
|
1132
1354
|
if (!this.dependencies.inspectSourceRemoval) {
|
|
1133
1355
|
throw new TuiOperationBlockedError('stateInventoryFailed', '来源引用清单不可用。');
|
|
@@ -1167,7 +1389,9 @@ export class TuiController {
|
|
|
1167
1389
|
catch (error) {
|
|
1168
1390
|
const code = issueCode(error);
|
|
1169
1391
|
await this.#logOperationError(error, code, intent);
|
|
1170
|
-
throw new TuiOperationBlockedError(code, '
|
|
1392
|
+
throw new TuiOperationBlockedError(code, code === 'catalogGenerationUnavailable'
|
|
1393
|
+
? '本次加载时 Catalog 来源不可用。请刷新来源并重新加载后重试。'
|
|
1394
|
+
: '操作未建立。请检查当前设置后重试。');
|
|
1171
1395
|
}
|
|
1172
1396
|
}
|
|
1173
1397
|
startReviewPreparation(intent, returnRoute) {
|
|
@@ -1239,11 +1463,12 @@ export class TuiController {
|
|
|
1239
1463
|
}
|
|
1240
1464
|
#reviewPreparationIsCurrent(token) {
|
|
1241
1465
|
const preparation = this.#reviewPreparation;
|
|
1466
|
+
const route = this.#router.snapshot.current.route;
|
|
1242
1467
|
return Boolean(preparation && preparation.token === token
|
|
1243
1468
|
&& !preparation.abort.signal.aborted
|
|
1244
1469
|
&& !this.#disposed
|
|
1245
|
-
&&
|
|
1246
|
-
|
|
1470
|
+
&& (route.name === 'reviewPreparing' && route.preparationId === preparation.snapshot.id
|
|
1471
|
+
|| route.name === 'catalogActivationBlocked' && route.reason === 'reviewPreparation'));
|
|
1247
1472
|
}
|
|
1248
1473
|
async #runReviewPreparation(token) {
|
|
1249
1474
|
const preparation = this.#reviewPreparation;
|
|
@@ -1281,13 +1506,16 @@ export class TuiController {
|
|
|
1281
1506
|
catch (error) {
|
|
1282
1507
|
if (!this.#reviewPreparationIsCurrent(token))
|
|
1283
1508
|
return;
|
|
1509
|
+
const code = issueCode(error);
|
|
1284
1510
|
const active = this.#reviewPreparation;
|
|
1285
1511
|
active.snapshot.state = 'failed';
|
|
1286
1512
|
active.snapshot.stages = active.snapshot.stages.map((stage) => stage.state === 'running' ? { ...stage, state: 'failed' } : stage);
|
|
1287
1513
|
active.snapshot.code = 'reviewPreparationFailed';
|
|
1288
|
-
active.snapshot.message = '
|
|
1514
|
+
active.snapshot.message = code === 'catalogGenerationUnavailable'
|
|
1515
|
+
? '本次加载时 Catalog 来源不可用。请刷新来源并重新加载后重试。'
|
|
1516
|
+
: '无法准备确认信息。请返回检查选择,或重试。';
|
|
1289
1517
|
this.#publish();
|
|
1290
|
-
await this.#logOperationError(error,
|
|
1518
|
+
await this.#logOperationError(error, code, intent);
|
|
1291
1519
|
}
|
|
1292
1520
|
}
|
|
1293
1521
|
async #prepareReviewEvidence(intent, signal, onStage = () => undefined) {
|
|
@@ -1361,10 +1589,19 @@ export class TuiController {
|
|
|
1361
1589
|
? { version: this.#viewModel.plugins.find((plugin) => plugin.pluginId === intent.pluginId).version }
|
|
1362
1590
|
: {}),
|
|
1363
1591
|
},
|
|
1592
|
+
...(evidence.source && 'catalogVersion' in evidence.source
|
|
1593
|
+
&& typeof evidence.source.catalogVersion === 'string'
|
|
1594
|
+
&& evidence.source.snapshotDigest
|
|
1595
|
+
? { catalogBinding: {
|
|
1596
|
+
sourceId: evidence.source.sourceId,
|
|
1597
|
+
catalogVersion: evidence.source.catalogVersion,
|
|
1598
|
+
generationDigestShort: evidence.source.snapshotDigest.replace(/^sha256:/u, '').slice(0, 8),
|
|
1599
|
+
} }
|
|
1600
|
+
: {}),
|
|
1364
1601
|
changes: reviewChanges(intent, evidence, 'pluginId' in intent ? this.getPluginDependencyOverview(intent.pluginId) : undefined),
|
|
1365
1602
|
technicalDetails: technicalDetails(evidence),
|
|
1366
|
-
...(intent.kind === 'pluginWrite'
|
|
1367
|
-
? { equivalentCommand: `aw
|
|
1603
|
+
...(intent.kind === 'pluginWrite'
|
|
1604
|
+
? { equivalentCommand: `aw ${intent.action} ${intent.pluginId} --platform ${intent.platform} --scope ${intent.scope}${intent.action === 'cleanup' && intent.force === true ? ' --force' : ''}${evidence.codexManager?.state === 'requires-install' ? ' --allow-managed-codex-install' : ''}` }
|
|
1368
1605
|
: {}),
|
|
1369
1606
|
primaryLabel: intent.kind === 'pluginWrite' && evidence.dependencyPlan
|
|
1370
1607
|
? `${labels.primaryLabel} ${evidence.dependencyPlan.resolvedPluginIds.length} 个插件`
|
|
@@ -1435,7 +1672,9 @@ export class TuiController {
|
|
|
1435
1672
|
this.#publish();
|
|
1436
1673
|
try {
|
|
1437
1674
|
this.#assertIssueOrigin(ticket.intent);
|
|
1438
|
-
|
|
1675
|
+
if (ticket.intent.kind !== 'pluginWrite') {
|
|
1676
|
+
await this.dependencies.validateIntent?.(structuredClone(ticket.intent), this.#abort.signal);
|
|
1677
|
+
}
|
|
1439
1678
|
}
|
|
1440
1679
|
catch (error) {
|
|
1441
1680
|
if (!this.#operationIsCurrent(generation))
|
|
@@ -1444,6 +1683,9 @@ export class TuiController {
|
|
|
1444
1683
|
await this.#logOperationError(error, code, ticket.intent);
|
|
1445
1684
|
if (!this.#operationIsCurrent(generation))
|
|
1446
1685
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1686
|
+
if (code === 'operationReviewStale' && ticket.intent.kind === 'catalogActivate') {
|
|
1687
|
+
return this.#rebuildStaleReview(ticket, operationId, undefined, generation);
|
|
1688
|
+
}
|
|
1447
1689
|
this.#cancelSourceRefreshes();
|
|
1448
1690
|
generation = ++this.#generation;
|
|
1449
1691
|
try {
|
|
@@ -1465,23 +1707,28 @@ export class TuiController {
|
|
|
1465
1707
|
}
|
|
1466
1708
|
if (!this.#operationIsCurrent(generation))
|
|
1467
1709
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1710
|
+
if (ticket.intent.kind !== 'pluginWrite') {
|
|
1711
|
+
let current;
|
|
1712
|
+
try {
|
|
1713
|
+
current = await this.dependencies.captureEvidence(structuredClone(ticket.intent), this.#abort.signal);
|
|
1714
|
+
}
|
|
1715
|
+
catch (error) {
|
|
1716
|
+
if (!this.#operationIsCurrent(generation))
|
|
1717
|
+
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1718
|
+
const code = issueCode(error);
|
|
1719
|
+
await this.#logOperationError(error, code, ticket.intent);
|
|
1720
|
+
if (!this.#operationIsCurrent(generation))
|
|
1721
|
+
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1722
|
+
if (code === 'operationReviewStale' && ticket.intent.kind === 'catalogActivate') {
|
|
1723
|
+
return this.#rebuildStaleReview(ticket, operationId, undefined, generation);
|
|
1724
|
+
}
|
|
1725
|
+
return this.#setBlocked(operationId, code, { intent: ticket.intent, returnRoute: ticket.returnRoute });
|
|
1726
|
+
}
|
|
1477
1727
|
if (!this.#operationIsCurrent(generation))
|
|
1478
1728
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1483
|
-
if (operationEvidenceFingerprint(current) !== operationEvidenceFingerprint(ticket.evidence)) {
|
|
1484
|
-
return this.#rebuildStaleReview(ticket, operationId, current, generation);
|
|
1729
|
+
if (operationEvidenceFingerprint(current) !== operationEvidenceFingerprint(ticket.evidence)) {
|
|
1730
|
+
return this.#rebuildStaleReview(ticket, operationId, current, generation);
|
|
1731
|
+
}
|
|
1485
1732
|
}
|
|
1486
1733
|
if (ticket.intent.kind === 'sourceMutation') {
|
|
1487
1734
|
this.#cancelSourceRefreshes();
|
|
@@ -1499,9 +1746,14 @@ export class TuiController {
|
|
|
1499
1746
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1500
1747
|
this.#cancelSourceRefreshes();
|
|
1501
1748
|
generation = ++this.#generation;
|
|
1502
|
-
if (ticket.intent.kind === 'sourceMutation') {
|
|
1749
|
+
if (ticket.intent.kind === 'sourceMutation' || ticket.intent.kind === 'catalogActivate') {
|
|
1750
|
+
this.dependencies.beginCatalogSession?.(ticket.intent.kind === 'catalogActivate'
|
|
1751
|
+
? { bootstrapMissingActive: false }
|
|
1752
|
+
: undefined);
|
|
1503
1753
|
try {
|
|
1504
|
-
this.#progressFeed?.setCurrent({ target: 'Catalog 来源配置', phase: '读取最终状态' }, '
|
|
1754
|
+
this.#progressFeed?.setCurrent({ target: ticket.intent.kind === 'catalogActivate' ? 'Catalog generation' : 'Catalog 来源配置', phase: '读取最终状态' }, ticket.intent.kind === 'catalogActivate'
|
|
1755
|
+
? 'Catalog 激活:重新加载当前会话'
|
|
1756
|
+
: '来源操作:读取最终状态');
|
|
1505
1757
|
}
|
|
1506
1758
|
catch {
|
|
1507
1759
|
// Progress diagnostics must not replace the completed source write.
|
|
@@ -1545,6 +1797,17 @@ export class TuiController {
|
|
|
1545
1797
|
&& Boolean(hasFailedDependencyTarget);
|
|
1546
1798
|
if (pluginSummary?.code === 'operationFailed') {
|
|
1547
1799
|
const unsuccessfulTargets = pluginSummary.targets?.filter((target) => target.state === 'failed' || target.state === 'skipped') ?? [];
|
|
1800
|
+
const managerFailureCode = unsuccessfulTargets.map((target) => target.errorCode).find((code) => code === 'codexSystemCliUnsupported'
|
|
1801
|
+
|| code === 'managedCodexAuthorizationRequired'
|
|
1802
|
+
|| code === 'managedCodexUnavailable');
|
|
1803
|
+
if (managerFailureCode) {
|
|
1804
|
+
return this.#setBlocked(operationId, managerFailureCode, {
|
|
1805
|
+
intent: ticket.intent,
|
|
1806
|
+
navigation: 'result',
|
|
1807
|
+
returnRoute: ticket.returnRoute,
|
|
1808
|
+
summary: pluginSummary,
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1548
1811
|
const dependencyStillRequired = unsuccessfulTargets.length > 0
|
|
1549
1812
|
&& unsuccessfulTargets.every((target) => target.errorCode === 'dependencyStillRequired');
|
|
1550
1813
|
return this.#setBlocked(operationId, dependencyStillRequired ? 'dependencyStillRequired' : 'operationFailed', {
|
|
@@ -1637,7 +1900,8 @@ export class TuiController {
|
|
|
1637
1900
|
this.#activeReview = undefined;
|
|
1638
1901
|
this.#router.replace({ name: 'result', operationId });
|
|
1639
1902
|
this.#publish();
|
|
1640
|
-
|
|
1903
|
+
if (ticket.intent.kind !== 'catalogActivate')
|
|
1904
|
+
this.#startGitSourceRefreshes();
|
|
1641
1905
|
return cloneFrozen(result);
|
|
1642
1906
|
}
|
|
1643
1907
|
catch (error) {
|
|
@@ -1689,6 +1953,7 @@ export class TuiController {
|
|
|
1689
1953
|
}
|
|
1690
1954
|
async #rebuildStaleReview(ticket, operationId, currentEvidence, generation = this.#generation) {
|
|
1691
1955
|
this.#cancelSourceRefreshes();
|
|
1956
|
+
this.dependencies.beginCatalogSession?.({ bootstrapMissingActive: false });
|
|
1692
1957
|
generation = ++this.#generation;
|
|
1693
1958
|
try {
|
|
1694
1959
|
const refreshed = await this.dependencies.loadStatus(this.#abort.signal);
|
|
@@ -1706,15 +1971,23 @@ export class TuiController {
|
|
|
1706
1971
|
returnRoute: ticket.returnRoute,
|
|
1707
1972
|
});
|
|
1708
1973
|
}
|
|
1974
|
+
const intent = ticket.intent.kind === 'catalogActivate'
|
|
1975
|
+
? (() => {
|
|
1976
|
+
const candidate = this.#catalogGenerations.get(ticket.intent.sourceId)?.candidate;
|
|
1977
|
+
return candidate
|
|
1978
|
+
? { ...ticket.intent, candidateGenerationKey: candidate.generationKey }
|
|
1979
|
+
: ticket.intent;
|
|
1980
|
+
})()
|
|
1981
|
+
: ticket.intent;
|
|
1709
1982
|
try {
|
|
1710
|
-
this.#assertIssueOrigin(
|
|
1711
|
-
await this.dependencies.validateIntent?.(structuredClone(
|
|
1983
|
+
this.#assertIssueOrigin(intent);
|
|
1984
|
+
await this.dependencies.validateIntent?.(structuredClone(intent), this.#abort.signal);
|
|
1712
1985
|
}
|
|
1713
1986
|
catch (error) {
|
|
1714
1987
|
if (!this.#operationIsCurrent(generation))
|
|
1715
1988
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1716
1989
|
const code = issueCode(error);
|
|
1717
|
-
await this.#logOperationError(error, code,
|
|
1990
|
+
await this.#logOperationError(error, code, intent);
|
|
1718
1991
|
if (!this.#operationIsCurrent(generation))
|
|
1719
1992
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1720
1993
|
const result = {
|
|
@@ -1722,7 +1995,7 @@ export class TuiController {
|
|
|
1722
1995
|
operationId,
|
|
1723
1996
|
code,
|
|
1724
1997
|
message: '状态已变化,原操作不再安全。请修改选择后重试。',
|
|
1725
|
-
...this.#operationIdentity(
|
|
1998
|
+
...this.#operationIdentity(intent),
|
|
1726
1999
|
returnRoute: structuredClone(this.#normalizeRoute(ticket.returnRoute)),
|
|
1727
2000
|
};
|
|
1728
2001
|
this.#result = structuredClone(result);
|
|
@@ -1736,50 +2009,56 @@ export class TuiController {
|
|
|
1736
2009
|
let evidence = currentEvidence;
|
|
1737
2010
|
if (!evidence) {
|
|
1738
2011
|
try {
|
|
1739
|
-
evidence = await this.dependencies.captureEvidence(structuredClone(
|
|
2012
|
+
evidence = await this.dependencies.captureEvidence(structuredClone(intent), this.#abort.signal);
|
|
1740
2013
|
}
|
|
1741
2014
|
catch (error) {
|
|
1742
2015
|
if (!this.#operationIsCurrent(generation))
|
|
1743
2016
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1744
2017
|
const code = issueCode(error);
|
|
1745
|
-
await this.#logOperationError(error, code,
|
|
2018
|
+
await this.#logOperationError(error, code, intent);
|
|
1746
2019
|
if (!this.#operationIsCurrent(generation))
|
|
1747
2020
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1748
|
-
return this.#setBlocked(operationId, code, { intent
|
|
2021
|
+
return this.#setBlocked(operationId, code, { intent, returnRoute: ticket.returnRoute });
|
|
1749
2022
|
}
|
|
1750
2023
|
}
|
|
1751
2024
|
if (!this.#operationIsCurrent(generation))
|
|
1752
2025
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1753
|
-
if (
|
|
2026
|
+
if (intent.kind === 'portableInstall') {
|
|
1754
2027
|
try {
|
|
1755
|
-
await this.refreshPortableInstallDraft(
|
|
2028
|
+
await this.refreshPortableInstallDraft(intent.pluginId);
|
|
1756
2029
|
}
|
|
1757
2030
|
catch (error) {
|
|
1758
2031
|
if (!this.#operationIsCurrent(generation))
|
|
1759
2032
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1760
2033
|
const code = issueCode(error);
|
|
1761
|
-
await this.#logOperationError(error, code,
|
|
1762
|
-
return this.#setBlocked(operationId, code, { intent
|
|
2034
|
+
await this.#logOperationError(error, code, intent);
|
|
2035
|
+
return this.#setBlocked(operationId, code, { intent, returnRoute: ticket.returnRoute });
|
|
1763
2036
|
}
|
|
1764
2037
|
}
|
|
1765
2038
|
if (!this.#operationIsCurrent(generation))
|
|
1766
2039
|
return this.#detachedBlocked(operationId, ticket.intent);
|
|
1767
|
-
const review = this.#storeOperationReview(
|
|
1768
|
-
const result = { state: 'stale', operationId, review, ...this.#operationIdentity(
|
|
2040
|
+
const review = this.#storeOperationReview(intent, evidence, 'replace', ticket.returnRoute);
|
|
2041
|
+
const result = { state: 'stale', operationId, review, ...this.#operationIdentity(intent) };
|
|
1769
2042
|
this.#result = result;
|
|
1770
2043
|
this.#publish();
|
|
1771
2044
|
return cloneFrozen(result);
|
|
1772
2045
|
}
|
|
1773
2046
|
#setBlocked(operationId, code, options) {
|
|
1774
|
-
const message = code === '
|
|
1775
|
-
? '
|
|
1776
|
-
:
|
|
1777
|
-
? '
|
|
1778
|
-
: code === '
|
|
1779
|
-
? '
|
|
1780
|
-
: code === '
|
|
1781
|
-
? '
|
|
1782
|
-
:
|
|
2047
|
+
const message = code === 'managedCodexAuthorizationRequired'
|
|
2048
|
+
? '操作在插件写入前停止。请在 Review 中确认获取 Codex runtime,或在命令行传入 --allow-managed-codex-install。'
|
|
2049
|
+
: code === 'codexSystemCliUnsupported'
|
|
2050
|
+
? '操作在插件写入前停止。系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
|
|
2051
|
+
: code === 'managedCodexUnavailable'
|
|
2052
|
+
? '操作在插件写入前停止。Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。'
|
|
2053
|
+
: code === 'portableSkillsCliInstallFailed'
|
|
2054
|
+
? 'Skills CLI 安装失败。请修改 Agent 或重试。'
|
|
2055
|
+
: options?.writeMayHaveCompleted
|
|
2056
|
+
? '写入可能已完成,当前无法确认。请查看刷新后的平台或问题状态。'
|
|
2057
|
+
: code === 'configResetStillPresent'
|
|
2058
|
+
? '重置执行后设置层仍然存在。请重新检查设置问题。'
|
|
2059
|
+
: code === 'issueStillPresent'
|
|
2060
|
+
? '写入已完成,但重新检查后原问题仍然存在。'
|
|
2061
|
+
: '操作未完成。请重新检查后重试。';
|
|
1783
2062
|
const result = {
|
|
1784
2063
|
state: 'blocked',
|
|
1785
2064
|
operationId,
|
|
@@ -1843,13 +2122,17 @@ export class TuiController {
|
|
|
1843
2122
|
? origin.actionId === 'cleanup' && action.writeAction === 'cleanup'
|
|
1844
2123
|
? { action: 'cleanup', scope: issue.scope }
|
|
1845
2124
|
: undefined
|
|
1846
|
-
: issue.category === '
|
|
1847
|
-
? origin.actionId === '
|
|
1848
|
-
? { action: '
|
|
1849
|
-
:
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2125
|
+
: issue.category === 'sourceMissing'
|
|
2126
|
+
? origin.actionId === 'cleanup' && action.writeAction === 'cleanup'
|
|
2127
|
+
? { action: 'cleanup', scope: issue.scope }
|
|
2128
|
+
: undefined
|
|
2129
|
+
: issue.category === 'scopeConflict'
|
|
2130
|
+
? origin.actionId === 'keepUserScope'
|
|
2131
|
+
? { action: 'uninstall', scope: 'project' }
|
|
2132
|
+
: origin.actionId === 'keepProjectScope'
|
|
2133
|
+
? { action: 'uninstall', scope: 'user' }
|
|
2134
|
+
: undefined
|
|
2135
|
+
: undefined;
|
|
1853
2136
|
const dependencyOriginValid = issue.category !== 'dependencyMissing' && issue.category !== 'dependencyNotReady'
|
|
1854
2137
|
|| (origin.consumerPluginId === issue.pluginId
|
|
1855
2138
|
&& origin.consumerSourceIdentity === issue.sourceIdentity
|
|
@@ -1871,6 +2154,7 @@ export class TuiController {
|
|
|
1871
2154
|
this.#cancelReviewPreparation();
|
|
1872
2155
|
this.#cancelPortableRequest();
|
|
1873
2156
|
this.#abort.abort();
|
|
2157
|
+
this.#catalogRefreshCoordinator?.dispose();
|
|
1874
2158
|
this.#cancelSourceRefreshes();
|
|
1875
2159
|
this.#progressUnsubscribe?.();
|
|
1876
2160
|
this.#tickets.clear();
|
|
@@ -1879,6 +2163,13 @@ export class TuiController {
|
|
|
1879
2163
|
}
|
|
1880
2164
|
}
|
|
1881
2165
|
function operationResultSummary(intent, execution) {
|
|
2166
|
+
if (intent.kind === 'catalogActivate') {
|
|
2167
|
+
return {
|
|
2168
|
+
code: 'operationSucceeded',
|
|
2169
|
+
message: 'Catalog 更新已启用;插件列表和依赖关系已重新加载。',
|
|
2170
|
+
messages: ['已安装插件没有自动升级。', '插件设置没有改变。'],
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
1882
2173
|
if (intent.kind === 'portableInstall') {
|
|
1883
2174
|
const receipt = execution;
|
|
1884
2175
|
const verificationUnknown = receipt.failures.some((failure) => failure.code === 'portableSkillsCliVerificationFailed'
|
|
@@ -1950,7 +2241,10 @@ function operationResultSummary(intent, execution) {
|
|
|
1950
2241
|
requiredBy: planned.requiredBy,
|
|
1951
2242
|
} : {}),
|
|
1952
2243
|
...(entry.errorCode === 'dependencyStillRequired'
|
|
1953
|
-
|
|
2244
|
+
|| entry.errorCode === 'codexSystemCliUnsupported'
|
|
2245
|
+
|| entry.errorCode === 'managedCodexAuthorizationRequired'
|
|
2246
|
+
|| entry.errorCode === 'managedCodexUnavailable'
|
|
2247
|
+
? { errorCode: entry.errorCode }
|
|
1954
2248
|
: {}),
|
|
1955
2249
|
}];
|
|
1956
2250
|
});
|