@lesliechan721/aw 0.1.0-beta.31 → 0.1.0-beta.33
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/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +5 -2
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +2 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/tui/catalog-sources/session.d.ts +5 -1
- package/dist/src/tui/catalog-sources/session.js +6 -1
- package/dist/src/tui/catalog-sources/session.js.map +1 -1
- package/dist/src/tui/catalog-sources/types.d.ts +1 -0
- package/dist/src/tui/clipboard.d.ts +13 -0
- package/dist/src/tui/clipboard.js +95 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/components.d.ts +21 -11
- package/dist/src/tui/components.js +69 -56
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-value-presentation.d.ts +19 -0
- package/dist/src/tui/config-value-presentation.js +77 -0
- package/dist/src/tui/config-value-presentation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +1205 -1377
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.js +5 -0
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +12 -1
- package/dist/src/tui/controller.js +171 -46
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostics.d.ts +2 -1
- package/dist/src/tui/diagnostics.js +8 -1
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +1 -0
- package/dist/src/tui/input-display.js +13 -0
- package/dist/src/tui/input-display.js.map +1 -1
- package/dist/src/tui/model.d.ts +5 -3
- package/dist/src/tui/model.js +17 -33
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +6 -3
- package/dist/src/tui/operation-diagnostic-screens.js +31 -9
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -1
- package/dist/src/tui/operation-history.js +5 -4
- package/dist/src/tui/operation-history.js.map +1 -1
- package/dist/src/tui/router.d.ts +5 -7
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +13 -22
- package/dist/src/tui/screen-registry.js +3 -4
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +1 -0
- package/dist/src/tui/viewport.js +23 -0
- package/dist/src/tui/viewport.js.map +1 -1
- package/package.json +3 -3
|
@@ -54,6 +54,7 @@ export interface PluginInstallBatchTarget extends PluginWriteBatchTarget {
|
|
|
54
54
|
export declare function pluginWriteBatchTargets(action: Exclude<BatchPluginWriteAction, 'install'>, pluginIds: readonly string[], statusTargets: readonly StatusScopedTarget[]): PluginWriteBatchTarget[];
|
|
55
55
|
export interface PluginWriteBatchIntent {
|
|
56
56
|
kind: 'pluginWriteBatch';
|
|
57
|
+
dependencyScopeOverrides?: DependencyScopeOverride[];
|
|
57
58
|
action: BatchPluginWriteAction;
|
|
58
59
|
selection: PluginWriteBatchSelection;
|
|
59
60
|
platform: PlatformName | 'both';
|
|
@@ -408,6 +409,14 @@ export interface OperationReviewModel {
|
|
|
408
409
|
blockedTargetCount: number;
|
|
409
410
|
causeCount: number;
|
|
410
411
|
};
|
|
412
|
+
rootSummary?: Array<{
|
|
413
|
+
pluginId: string;
|
|
414
|
+
version?: string;
|
|
415
|
+
targets: Array<{
|
|
416
|
+
platform: PlatformName | 'both';
|
|
417
|
+
scope: Scope;
|
|
418
|
+
}>;
|
|
419
|
+
}>;
|
|
411
420
|
}
|
|
412
421
|
export type ReviewPreparationStageId = 'refresh' | 'validate' | 'status' | 'catalog' | 'inventory' | 'dependencyPlan' | 'config' | 'portable' | 'evidence';
|
|
413
422
|
export interface ReviewPreparationStage {
|
|
@@ -749,6 +758,7 @@ export interface TuiControllerSnapshot {
|
|
|
749
758
|
activeReview?: OperationReviewModel;
|
|
750
759
|
reviewPreparation?: ReviewPreparationSnapshot;
|
|
751
760
|
result?: OperationExecuteResult;
|
|
761
|
+
canResumeInstallDraft?: boolean;
|
|
752
762
|
operationHistorySummaries: OperationHistorySummary[];
|
|
753
763
|
portableResultSkills?: string[];
|
|
754
764
|
loadError?: {
|
|
@@ -857,11 +867,12 @@ export declare class TuiController {
|
|
|
857
867
|
beginInstallFlow(pluginId: string, rootScope: Scope): void;
|
|
858
868
|
setInstallRootScope(rootScope: Scope): void;
|
|
859
869
|
setInstallPlatforms(platforms: PlatformName[]): void;
|
|
870
|
+
setInstallRootTargets(targets: PluginWriteBatchTarget[]): void;
|
|
860
871
|
prepareInstallDependencyPlacement(): Promise<boolean>;
|
|
861
872
|
setDependencyScope(pluginId: string, platform: PlatformName, scope: Scope): Promise<void>;
|
|
862
873
|
resetDependencyScopes(): Promise<void>;
|
|
863
874
|
installPlacementIntent(): Extract<OperationIntent, {
|
|
864
|
-
kind: 'pluginWrite';
|
|
875
|
+
kind: 'pluginWrite' | 'pluginWriteBatch';
|
|
865
876
|
}>;
|
|
866
877
|
listPlugins(options: {
|
|
867
878
|
mode: 'browse' | 'installed';
|
|
@@ -768,8 +768,11 @@ export class TuiController {
|
|
|
768
768
|
this.#project();
|
|
769
769
|
this.#publish();
|
|
770
770
|
},
|
|
771
|
-
onError: (error, code) => {
|
|
771
|
+
onError: (error, code, context) => {
|
|
772
772
|
void this.#logErrorSafe(error, code);
|
|
773
|
+
return this.#recordStandaloneFailure(code, context.fullValidation
|
|
774
|
+
? 'validate-catalog-source'
|
|
775
|
+
: context.automatic ? 'auto-check-catalog-source' : 'check-catalog-source', error);
|
|
773
776
|
},
|
|
774
777
|
now: this.dependencies.now,
|
|
775
778
|
});
|
|
@@ -822,6 +825,7 @@ export class TuiController {
|
|
|
822
825
|
? { reviewPreparation: this.#reviewPreparation.snapshot }
|
|
823
826
|
: {}),
|
|
824
827
|
...(this.#result ? { result: this.#result } : {}),
|
|
828
|
+
...(this.#recoverableInstallPlacementDraft ? { canResumeInstallDraft: true } : {}),
|
|
825
829
|
operationHistorySummaries: this.#operationHistory.list(),
|
|
826
830
|
...(this.#portableResultSkills ? { portableResultSkills: this.#portableResultSkills } : {}),
|
|
827
831
|
...(this.#loadError ? { loadError: this.#loadError } : {}),
|
|
@@ -830,6 +834,8 @@ export class TuiController {
|
|
|
830
834
|
? { installPlacementDraft: {
|
|
831
835
|
pluginId: this.#installPlacementDraft.pluginId,
|
|
832
836
|
rootScope: this.#installPlacementDraft.rootScope,
|
|
837
|
+
...(this.#installPlacementDraft.exactRootTargets
|
|
838
|
+
? { exactRootTargets: this.#installPlacementDraft.exactRootTargets } : {}),
|
|
833
839
|
platforms: this.#installPlacementDraft.platforms,
|
|
834
840
|
dependencyScopeOverrides: this.#installPlacementDraft.dependencyScopeOverrides,
|
|
835
841
|
state: this.#installPlacementDraft.state,
|
|
@@ -1229,6 +1235,10 @@ export class TuiController {
|
|
|
1229
1235
|
if (intent.kind === 'pluginWriteBatch') {
|
|
1230
1236
|
if (active?.kind === 'batchWrite')
|
|
1231
1237
|
return;
|
|
1238
|
+
if (active?.kind === 'pluginInstall' && intent.action === 'install'
|
|
1239
|
+
&& intent.selection.kind === 'targets' && this.#installPlacementDraft
|
|
1240
|
+
&& intent.selection.targets.every((target) => target.pluginId === this.#installPlacementDraft.pluginId))
|
|
1241
|
+
return;
|
|
1232
1242
|
this.#router.beginFlow('batchWrite', { name: 'pluginList', mode: 'installed' });
|
|
1233
1243
|
return;
|
|
1234
1244
|
}
|
|
@@ -1284,14 +1294,10 @@ export class TuiController {
|
|
|
1284
1294
|
#stableResultRoute(intent, result) {
|
|
1285
1295
|
const active = this.#router.activeFlow();
|
|
1286
1296
|
if (intent.kind === 'pluginWriteTask' && active?.kind === 'pluginWriteTask'
|
|
1287
|
-
&&
|
|
1288
|
-
|
|
1297
|
+
&& active.returnTarget.name === 'pluginWriteTask'
|
|
1298
|
+
&& active.returnTarget.goal !== intent.goal) {
|
|
1289
1299
|
return this.#normalizeRoute(active.returnTarget);
|
|
1290
1300
|
}
|
|
1291
|
-
const completedPluginId = completedSinglePluginId(intent, result);
|
|
1292
|
-
if (completedPluginId) {
|
|
1293
|
-
return this.#normalizeRoute({ name: 'pluginDetail', pluginId: completedPluginId });
|
|
1294
|
-
}
|
|
1295
1301
|
if (intent.kind === 'pluginWriteTask'
|
|
1296
1302
|
&& result.state === 'succeeded'
|
|
1297
1303
|
&& 'returnRoute' in result
|
|
@@ -1300,6 +1306,14 @@ export class TuiController {
|
|
|
1300
1306
|
&& result.returnRoute.batchOrigin?.owner === 'batch-write') {
|
|
1301
1307
|
return { name: 'pluginList', mode: 'installed' };
|
|
1302
1308
|
}
|
|
1309
|
+
const completedPluginId = completedSinglePluginId(intent, result);
|
|
1310
|
+
if (completedPluginId) {
|
|
1311
|
+
return this.#normalizeRoute({ name: 'pluginDetail', pluginId: completedPluginId });
|
|
1312
|
+
}
|
|
1313
|
+
if (intent.kind === 'pluginWriteTask' && active?.kind === 'pluginWriteTask'
|
|
1314
|
+
&& active.parentFlowId) {
|
|
1315
|
+
return this.#normalizeRoute(active.returnTarget);
|
|
1316
|
+
}
|
|
1303
1317
|
if (intent.kind === 'portableInstall' && result.state === 'succeeded') {
|
|
1304
1318
|
return this.#normalizeRoute({ name: 'pluginDetail', pluginId: intent.pluginId });
|
|
1305
1319
|
}
|
|
@@ -1349,10 +1363,17 @@ export class TuiController {
|
|
|
1349
1363
|
this.#cancelReviewPreparation();
|
|
1350
1364
|
this.#progress = { messages: [] };
|
|
1351
1365
|
this.#progressTargets = [];
|
|
1366
|
+
const installDraft = this.#installPlacementDraft;
|
|
1367
|
+
const ownsInstallDraft = installDraft && (intent.kind === 'pluginWrite'
|
|
1368
|
+
&& intent.action === 'install' && installDraft.pluginId === intent.pluginId
|
|
1369
|
+
|| intent.kind === 'pluginWriteBatch' && intent.action === 'install'
|
|
1370
|
+
&& this.#router.activeFlow()?.kind === 'pluginInstall'
|
|
1371
|
+
&& intent.selection.kind === 'targets' && installDraft.exactRootTargets
|
|
1372
|
+
&& JSON.stringify(canonicalBatchTargets(intent.selection.targets))
|
|
1373
|
+
=== JSON.stringify(installDraft.exactRootTargets));
|
|
1352
1374
|
this.#recoverableInstallPlacementDraft = result.state === 'blocked'
|
|
1353
|
-
&&
|
|
1354
|
-
|
|
1355
|
-
? structuredClone(this.#installPlacementDraft)
|
|
1375
|
+
&& 'returnRoute' in result && result.returnRoute && ownsInstallDraft
|
|
1376
|
+
? structuredClone(installDraft)
|
|
1356
1377
|
: undefined;
|
|
1357
1378
|
this.#installPlacementDraft = undefined;
|
|
1358
1379
|
this.#recoverablePortableDraft = result.state !== 'succeeded'
|
|
@@ -1556,13 +1577,19 @@ export class TuiController {
|
|
|
1556
1577
|
if (!this.hasPortableSkills(pluginId)) {
|
|
1557
1578
|
throw new TuiOperationBlockedError('portableUnsupported', '插件不提供通用 Skills。');
|
|
1558
1579
|
}
|
|
1580
|
+
const requestKey = `prepare:${pluginId}`;
|
|
1581
|
+
if (this.#portablePending?.key === requestKey) {
|
|
1582
|
+
return this.#portablePending.promise;
|
|
1583
|
+
}
|
|
1559
1584
|
this.#router.beginPortableFlow(pluginId);
|
|
1585
|
+
this.#router[navigation]({ name: 'portablePreparing', pluginId });
|
|
1586
|
+
this.#publish();
|
|
1560
1587
|
const expectedRoute = this.#router.snapshot.current.route;
|
|
1561
|
-
return this.#runPortableRequest(
|
|
1588
|
+
return this.#runPortableRequest(requestKey, async (signal, token) => {
|
|
1562
1589
|
const draft = await this.#buildPortableDraft(pluginId, false, signal);
|
|
1563
1590
|
this.#assertPortableRequestCurrent(token, expectedRoute);
|
|
1564
1591
|
this.#portableDraft = draft;
|
|
1565
|
-
this.#router
|
|
1592
|
+
this.#router.replace({ name: 'portableSkillSelect', pluginId });
|
|
1566
1593
|
this.#publish();
|
|
1567
1594
|
return cloneFrozen(draft);
|
|
1568
1595
|
});
|
|
@@ -1573,6 +1600,10 @@ export class TuiController {
|
|
|
1573
1600
|
return this.#runPortableRequest(`refresh:${pluginId}`, async (signal, token) => {
|
|
1574
1601
|
const draft = await this.#buildPortableDraft(pluginId, true, signal);
|
|
1575
1602
|
this.#assertPortableRequestCurrent(token, expectedRoute);
|
|
1603
|
+
if (this.#activeReview?.operationKind === 'portableInstall') {
|
|
1604
|
+
this.#tickets.delete(this.#activeReview.reviewToken);
|
|
1605
|
+
this.#activeReview = undefined;
|
|
1606
|
+
}
|
|
1576
1607
|
this.#portableDraft = draft;
|
|
1577
1608
|
this.#publish();
|
|
1578
1609
|
return cloneFrozen(draft);
|
|
@@ -1990,9 +2021,15 @@ export class TuiController {
|
|
|
1990
2021
|
this.#tickets.delete(this.#activeReview.reviewToken);
|
|
1991
2022
|
this.#activeReview = undefined;
|
|
1992
2023
|
}
|
|
1993
|
-
|
|
1994
|
-
if (activeFlow
|
|
2024
|
+
if (route.name === 'batchSelection' && activeFlow?.kind === 'batchWrite'
|
|
1995
2025
|
&& this.#router.snapshot.currentFrameId === activeFlow.originFrameId) {
|
|
2026
|
+
this.#router.finishFlow();
|
|
2027
|
+
}
|
|
2028
|
+
else
|
|
2029
|
+
this.#router.back();
|
|
2030
|
+
if (activeFlow
|
|
2031
|
+
&& this.#router.snapshot.currentFrameId === activeFlow.originFrameId
|
|
2032
|
+
&& this.#router.snapshot.currentFlowId !== activeFlow.id) {
|
|
1996
2033
|
this.#router.abandonFlow(activeFlow.id);
|
|
1997
2034
|
if (activeFlow.kind === 'pluginInstall')
|
|
1998
2035
|
this.#installPlacementDraft = undefined;
|
|
@@ -2074,21 +2111,31 @@ export class TuiController {
|
|
|
2074
2111
|
if (this.#router.snapshot.current.route.name !== 'result' || !result
|
|
2075
2112
|
|| !('returnRoute' in result) || !result.returnRoute)
|
|
2076
2113
|
return;
|
|
2114
|
+
if (result.operationKind === 'pluginWriteBatch' && !this.#recoverableInstallPlacementDraft)
|
|
2115
|
+
return;
|
|
2077
2116
|
this.#invalidatePortableResultRecheck();
|
|
2078
2117
|
const returnRoute = this.#normalizeRoute(result.returnRoute);
|
|
2079
2118
|
this.#router.back();
|
|
2080
|
-
const kind =
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
? '
|
|
2085
|
-
: result.operationKind === '
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2119
|
+
const kind = this.#recoverableInstallPlacementDraft
|
|
2120
|
+
? 'pluginInstall'
|
|
2121
|
+
: result.operationKind === 'configSave' || result.operationKind === 'configSaveBatch'
|
|
2122
|
+
|| result.operationKind === 'configReset'
|
|
2123
|
+
? 'configEdit'
|
|
2124
|
+
: result.operationKind === 'portableInstall'
|
|
2125
|
+
? 'portableInstall'
|
|
2126
|
+
: result.operationKind === 'sourceMutation' || result.operationKind === 'catalogGeneration'
|
|
2127
|
+
|| result.operationKind === 'catalogSourcesRecovery'
|
|
2128
|
+
? 'sourceEdit'
|
|
2129
|
+
: 'pluginMaintenance';
|
|
2089
2130
|
this.#router.beginFlow(kind, this.#router.snapshot.current.route);
|
|
2090
|
-
if (
|
|
2131
|
+
if (this.#recoverableInstallPlacementDraft) {
|
|
2091
2132
|
this.#installPlacementDraft = this.#recoverableInstallPlacementDraft;
|
|
2133
|
+
if (this.#installPlacementDraft.exactRootTargets) {
|
|
2134
|
+
this.#installPlacementDraft.state = 'selecting';
|
|
2135
|
+
this.#installPlacementDraft.targets = [];
|
|
2136
|
+
this.#installPlacementDraft.canContinue = false;
|
|
2137
|
+
this.#installPlacementDraft.error = undefined;
|
|
2138
|
+
}
|
|
2092
2139
|
}
|
|
2093
2140
|
if (result.operationKind === 'portableInstall') {
|
|
2094
2141
|
this.#portableDraft = this.#recoverablePortableDraft;
|
|
@@ -2107,8 +2154,8 @@ export class TuiController {
|
|
|
2107
2154
|
|| route.name === 'operationReportPreview';
|
|
2108
2155
|
if (readOnlyHistoryRoute && this.#router.activeFlow()?.kind === 'portableInstall')
|
|
2109
2156
|
return;
|
|
2110
|
-
const portableRoute = route.name === '
|
|
2111
|
-
|| route.name === '
|
|
2157
|
+
const portableRoute = route.name === 'portablePreparing'
|
|
2158
|
+
|| route.name === 'portableSkillSelect'
|
|
2112
2159
|
|| route.name === 'portableDependencies'
|
|
2113
2160
|
|| route.name === 'portableAgentMode'
|
|
2114
2161
|
|| route.name === 'portableAgentInput'
|
|
@@ -2209,8 +2256,9 @@ export class TuiController {
|
|
|
2209
2256
|
return;
|
|
2210
2257
|
this.#cancelInstallPlacementRequest();
|
|
2211
2258
|
draft.rootScope = rootScope;
|
|
2259
|
+
draft.exactRootTargets = undefined;
|
|
2260
|
+
draft.dependencyScopeOverrides = draft.dependencyScopeOverrides.filter((override) => rootScope !== 'user' || override.scope === 'user');
|
|
2212
2261
|
draft.platforms = [];
|
|
2213
|
-
draft.plan = undefined;
|
|
2214
2262
|
draft.targets = [];
|
|
2215
2263
|
draft.state = 'selecting';
|
|
2216
2264
|
draft.canContinue = false;
|
|
@@ -2224,21 +2272,41 @@ export class TuiController {
|
|
|
2224
2272
|
this.#cancelInstallPlacementRequest();
|
|
2225
2273
|
const selected = [...new Set(platforms)].sort();
|
|
2226
2274
|
draft.platforms = selected;
|
|
2275
|
+
draft.exactRootTargets = undefined;
|
|
2227
2276
|
draft.dependencyScopeOverrides = draft.dependencyScopeOverrides.filter((override) => selected.includes(override.platform));
|
|
2228
|
-
draft.plan = undefined;
|
|
2229
2277
|
draft.targets = [];
|
|
2230
2278
|
draft.state = 'selecting';
|
|
2231
2279
|
draft.canContinue = selected.length > 0;
|
|
2232
2280
|
draft.error = undefined;
|
|
2233
2281
|
this.#publish();
|
|
2234
2282
|
}
|
|
2283
|
+
setInstallRootTargets(targets) {
|
|
2284
|
+
const draft = this.#installPlacementDraft;
|
|
2285
|
+
if (!draft)
|
|
2286
|
+
return;
|
|
2287
|
+
const selected = canonicalBatchTargets(structuredClone(targets));
|
|
2288
|
+
if (selected.length === 0 || selected.some((target) => target.pluginId !== draft.pluginId)
|
|
2289
|
+
|| new Set(selected.map((target) => target.platform)).size !== selected.length) {
|
|
2290
|
+
throw new TuiOperationBlockedError('installPlacementIncomplete', '请为每个所选平台确定一个根插件位置。');
|
|
2291
|
+
}
|
|
2292
|
+
this.#cancelInstallPlacementRequest();
|
|
2293
|
+
draft.exactRootTargets = selected;
|
|
2294
|
+
draft.rootScope = selected[0].scope;
|
|
2295
|
+
draft.platforms = selected.map((target) => target.platform);
|
|
2296
|
+
draft.dependencyScopeOverrides = draft.dependencyScopeOverrides.filter((override) => selected.some((target) => target.platform === override.platform
|
|
2297
|
+
&& (target.scope === 'project' || override.scope === 'user')));
|
|
2298
|
+
draft.targets = [];
|
|
2299
|
+
draft.state = 'selecting';
|
|
2300
|
+
draft.canContinue = true;
|
|
2301
|
+
draft.error = undefined;
|
|
2302
|
+
this.#publish();
|
|
2303
|
+
}
|
|
2235
2304
|
#cancelInstallPlacementRequest() {
|
|
2236
2305
|
const cancelled = this.#installPlacementRequestAbort !== undefined;
|
|
2237
2306
|
this.#installPlacementRequestToken += 1;
|
|
2238
2307
|
this.#installPlacementRequestAbort?.abort();
|
|
2239
2308
|
this.#installPlacementRequestAbort = undefined;
|
|
2240
2309
|
if (cancelled && this.#installPlacementDraft?.state === 'loading') {
|
|
2241
|
-
this.#installPlacementDraft.plan = undefined;
|
|
2242
2310
|
this.#installPlacementDraft.targets = [];
|
|
2243
2311
|
this.#installPlacementDraft.state = 'selecting';
|
|
2244
2312
|
this.#installPlacementDraft.canContinue = false;
|
|
@@ -2250,6 +2318,16 @@ export class TuiController {
|
|
|
2250
2318
|
if (!draft || draft.platforms.length === 0) {
|
|
2251
2319
|
throw new TuiOperationBlockedError('installPlacementIncomplete', '请先明确选择目标平台。');
|
|
2252
2320
|
}
|
|
2321
|
+
if (draft.exactRootTargets)
|
|
2322
|
+
return {
|
|
2323
|
+
kind: 'pluginWriteBatch',
|
|
2324
|
+
action: 'install',
|
|
2325
|
+
selection: { kind: 'targets', targets: structuredClone(draft.exactRootTargets) },
|
|
2326
|
+
platform: draft.platforms.length === 2 ? 'both' : draft.platforms[0],
|
|
2327
|
+
scope: draft.rootScope,
|
|
2328
|
+
...(draft.dependencyScopeOverrides.length > 0
|
|
2329
|
+
? { dependencyScopeOverrides: structuredClone(draft.dependencyScopeOverrides) } : {}),
|
|
2330
|
+
};
|
|
2253
2331
|
return {
|
|
2254
2332
|
kind: 'pluginWrite',
|
|
2255
2333
|
action: 'install',
|
|
@@ -2284,19 +2362,22 @@ export class TuiController {
|
|
|
2284
2362
|
throw new DOMException('Dependency placement preview was cancelled.', 'AbortError');
|
|
2285
2363
|
}
|
|
2286
2364
|
};
|
|
2287
|
-
const
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
draft.
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2365
|
+
const placementIntent = this.#installPlacementIntent();
|
|
2366
|
+
const intents = placementIntent.kind === 'pluginWrite'
|
|
2367
|
+
? [placementIntent]
|
|
2368
|
+
: (draft.exactRootTargets ?? []).map((target) => ({
|
|
2369
|
+
kind: 'pluginWrite', action: 'install', ...target,
|
|
2370
|
+
dependencyScopeOverrides: draft.dependencyScopeOverrides.filter((override) => override.platform === target.platform),
|
|
2371
|
+
}));
|
|
2372
|
+
const plans = [];
|
|
2373
|
+
for (const intent of intents) {
|
|
2374
|
+
const plan = await this.#trackRead(preview(intent));
|
|
2375
|
+
throwIfCancelled();
|
|
2376
|
+
if (plan)
|
|
2377
|
+
plans.push(plan);
|
|
2296
2378
|
}
|
|
2297
|
-
draft.
|
|
2298
|
-
|
|
2299
|
-
const blocker = plan.targets.find((target) => target.action === 'block');
|
|
2379
|
+
draft.targets = plans.flatMap(dependencyPlacementTargets);
|
|
2380
|
+
const blocker = plans.flatMap((plan) => plan.targets).find((target) => target.action === 'block');
|
|
2300
2381
|
draft.state = blocker ? 'blocked' : 'ready';
|
|
2301
2382
|
draft.canContinue = !blocker;
|
|
2302
2383
|
draft.error = blocker
|
|
@@ -2306,7 +2387,10 @@ export class TuiController {
|
|
|
2306
2387
|
}
|
|
2307
2388
|
: undefined;
|
|
2308
2389
|
this.#publish();
|
|
2309
|
-
|
|
2390
|
+
const editableDependencies = draft.targets.filter((target) => !target.locked);
|
|
2391
|
+
return editableDependencies.some((target) => draft.targets.some((candidate) => candidate.pluginId === target.pluginId
|
|
2392
|
+
&& candidate.platform !== target.platform
|
|
2393
|
+
&& candidate.scope !== target.scope));
|
|
2310
2394
|
}
|
|
2311
2395
|
catch (error) {
|
|
2312
2396
|
if (error instanceof Error && error.name === 'AbortError')
|
|
@@ -2616,7 +2700,16 @@ export class TuiController {
|
|
|
2616
2700
|
}
|
|
2617
2701
|
recheckCatalogSource(sourceId) {
|
|
2618
2702
|
this.#assertAcceptingActions();
|
|
2619
|
-
return this.#catalogSourcesSession.
|
|
2703
|
+
return this.#catalogSourcesSession.refresh(sourceId, this.#sourceCheckNeedsFullValidation(sourceId));
|
|
2704
|
+
}
|
|
2705
|
+
#sourceCheckNeedsFullValidation(sourceId) {
|
|
2706
|
+
const generation = this.#catalogGenerations.get(sourceId);
|
|
2707
|
+
const freshness = this.#catalogFreshness.get(sourceId);
|
|
2708
|
+
const activeReady = generation?.state === 'ready';
|
|
2709
|
+
return !activeReady
|
|
2710
|
+
|| freshness?.state === 'incompatible'
|
|
2711
|
+
|| freshness?.state === 'versionConflict'
|
|
2712
|
+
|| freshness?.state === 'rollbackDetected';
|
|
2620
2713
|
}
|
|
2621
2714
|
async recheckAllCatalogSources() {
|
|
2622
2715
|
this.#assertAcceptingActions();
|
|
@@ -2627,8 +2720,9 @@ export class TuiController {
|
|
|
2627
2720
|
const worker = async () => {
|
|
2628
2721
|
while (next < sourceIds.length) {
|
|
2629
2722
|
const sourceId = sourceIds[next++];
|
|
2630
|
-
if (sourceId)
|
|
2631
|
-
await this.#catalogSourcesSession.
|
|
2723
|
+
if (sourceId) {
|
|
2724
|
+
await this.#catalogSourcesSession.refresh(sourceId, this.#sourceCheckNeedsFullValidation(sourceId));
|
|
2725
|
+
}
|
|
2632
2726
|
}
|
|
2633
2727
|
};
|
|
2634
2728
|
await Promise.all(Array.from({ length: Math.min(2, sourceIds.length) }, () => worker()));
|
|
@@ -2645,7 +2739,7 @@ export class TuiController {
|
|
|
2645
2739
|
beginPluginWriteBatch() {
|
|
2646
2740
|
this.#assertAcceptingActions();
|
|
2647
2741
|
this.#router.beginBatchFlow();
|
|
2648
|
-
this.#router.
|
|
2742
|
+
this.#router.replace({ name: 'batchSelection' }, this.#router.snapshot.current.context);
|
|
2649
2743
|
this.#publish();
|
|
2650
2744
|
}
|
|
2651
2745
|
startPluginWriteBatchReview(intent) {
|
|
@@ -3210,6 +3304,36 @@ export class TuiController {
|
|
|
3210
3304
|
? evidence.source?.pluginVersion
|
|
3211
3305
|
?? this.#viewModel?.plugins.find((plugin) => plugin.pluginId === intent.pluginId)?.version
|
|
3212
3306
|
: undefined;
|
|
3307
|
+
const rootSummary = intent.kind === 'pluginWriteTask'
|
|
3308
|
+
? [...new Set(evidence.taskPlan.requestedTargets
|
|
3309
|
+
.map((target) => target.pluginId))].map((rootPluginId) => {
|
|
3310
|
+
const targets = evidence.taskPlan.requestedTargets
|
|
3311
|
+
.filter((target) => target.pluginId === rootPluginId);
|
|
3312
|
+
const versions = [...new Set(targets.flatMap((target) => target.expectedUpdate?.activeVersion ? [target.expectedUpdate.activeVersion] : []))];
|
|
3313
|
+
return {
|
|
3314
|
+
pluginId: rootPluginId,
|
|
3315
|
+
...(versions.length === 1 ? { version: versions[0] } : {}),
|
|
3316
|
+
targets: targets.map(({ platform, scope }) => ({ platform, scope })),
|
|
3317
|
+
};
|
|
3318
|
+
})
|
|
3319
|
+
: intent.kind === 'pluginWriteBatch'
|
|
3320
|
+
? evidence.requestedPluginIds.map((rootPluginId) => {
|
|
3321
|
+
const batchEvidence = evidence;
|
|
3322
|
+
const targetVersions = batchEvidence.targetVersions?.filter((target) => target.pluginId === rootPluginId) ?? [];
|
|
3323
|
+
const versions = [...new Set(targetVersions.flatMap((target) => {
|
|
3324
|
+
const version = intent.action === 'uninstall'
|
|
3325
|
+
? target.installedVersion
|
|
3326
|
+
: target.availableVersion ?? target.installedVersion;
|
|
3327
|
+
return version ? [version] : [];
|
|
3328
|
+
}))];
|
|
3329
|
+
return {
|
|
3330
|
+
pluginId: rootPluginId,
|
|
3331
|
+
...(versions.length === 1 ? { version: versions[0] } : {}),
|
|
3332
|
+
targets: batchEvidence.groups.filter((group) => group.requestedPluginIds.includes(rootPluginId))
|
|
3333
|
+
.map(({ platform, scope }) => ({ platform, scope })),
|
|
3334
|
+
};
|
|
3335
|
+
})
|
|
3336
|
+
: undefined;
|
|
3213
3337
|
const review = {
|
|
3214
3338
|
operationId,
|
|
3215
3339
|
reviewToken,
|
|
@@ -3250,6 +3374,7 @@ export class TuiController {
|
|
|
3250
3374
|
causeCount: evidence.taskPlan.causes.length,
|
|
3251
3375
|
},
|
|
3252
3376
|
} : {}),
|
|
3377
|
+
...(rootSummary && rootSummary.length > 0 ? { rootSummary } : {}),
|
|
3253
3378
|
...(evidence.source && 'catalogVersion' in evidence.source
|
|
3254
3379
|
&& typeof evidence.source.catalogVersion === 'string'
|
|
3255
3380
|
&& evidence.source.snapshotDigest
|