@lesliechan721/aw 0.1.0-beta.18 → 0.1.0-beta.19

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.
Files changed (42) hide show
  1. package/dist/src/commands/operation-planner.d.ts +3 -3
  2. package/dist/src/commands/status-v6.d.ts +81 -31
  3. package/dist/src/commands/status-v6.js +494 -183
  4. package/dist/src/commands/status-v6.js.map +1 -1
  5. package/dist/src/commands/wizard.d.ts +2 -2
  6. package/dist/src/index.js +2 -2
  7. package/dist/src/tui/catalog-sources/projection.d.ts +2 -2
  8. package/dist/src/tui/catalog-sources/screens.js +17 -10
  9. package/dist/src/tui/catalog-sources/screens.js.map +1 -1
  10. package/dist/src/tui/console-app-v4.js +1291 -435
  11. package/dist/src/tui/console-app-v4.js.map +1 -1
  12. package/dist/src/tui/controller-runtime.d.ts +3 -3
  13. package/dist/src/tui/controller-runtime.js +85 -27
  14. package/dist/src/tui/controller-runtime.js.map +1 -1
  15. package/dist/src/tui/controller.d.ts +17 -7
  16. package/dist/src/tui/controller.js +265 -24
  17. package/dist/src/tui/controller.js.map +1 -1
  18. package/dist/src/tui/dependency-graph.d.ts +3 -0
  19. package/dist/src/tui/dependency-graph.js +37 -1
  20. package/dist/src/tui/dependency-graph.js.map +1 -1
  21. package/dist/src/tui/diagnostic-projection.d.ts +1 -0
  22. package/dist/src/tui/diagnostic-projection.js +51 -4
  23. package/dist/src/tui/diagnostic-projection.js.map +1 -1
  24. package/dist/src/tui/model.d.ts +22 -12
  25. package/dist/src/tui/model.js +251 -86
  26. package/dist/src/tui/model.js.map +1 -1
  27. package/dist/src/tui/operation-history.d.ts +1 -0
  28. package/dist/src/tui/operation-history.js +5 -0
  29. package/dist/src/tui/operation-history.js.map +1 -1
  30. package/dist/src/tui/operation-review-planner.js +67 -2
  31. package/dist/src/tui/operation-review-planner.js.map +1 -1
  32. package/dist/src/tui/plugin-write-task.d.ts +2 -0
  33. package/dist/src/tui/plugin-write-task.js.map +1 -1
  34. package/dist/src/tui/router.d.ts +6 -0
  35. package/dist/src/tui/router.js +56 -12
  36. package/dist/src/tui/router.js.map +1 -1
  37. package/dist/src/tui/screen-registry.d.ts +7 -0
  38. package/dist/src/utils/plugin-config-write.js +3 -3
  39. package/dist/src/utils/plugin-config-write.js.map +1 -1
  40. package/dist/src/utils/target-eligibility.d.ts +2 -2
  41. package/dist/src/utils/target-eligibility.js.map +1 -1
  42. package/package.json +3 -3
@@ -1,5 +1,5 @@
1
1
  import type { CliExecutionContext } from '../commands/runtime.js';
2
- import { type StatusTargetRevision, type StatusV7Target } from '../commands/status-v6.js';
2
+ import { type StatusTargetRevision, type StatusScopedTarget, type StatusV8Target } from '../commands/status-v6.js';
3
3
  import { type CatalogSourceMutationProgress } from '../commands/catalog-source.js';
4
4
  import type { RebuildProgressFeed } from '../output/rebuild-progress.js';
5
5
  import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
@@ -13,8 +13,8 @@ export interface TuiControllerRuntimeTestHooks {
13
13
  afterCatalogGenerationCandidateResolved?: () => Promise<void>;
14
14
  }
15
15
  export declare function setTuiControllerRuntimeTestHooksForTests(hooks: TuiControllerRuntimeTestHooks | undefined): void;
16
- export declare function statusTargetFingerprint(targets: StatusV7Target[], revisions?: StatusTargetRevision[]): string;
17
- export declare function configAffectedStatusTargets(targets: StatusV7Target[], pluginId: string, configScope: 'user' | 'project'): StatusV7Target[];
16
+ export declare function statusTargetFingerprint(targets: StatusScopedTarget[], revisions?: StatusTargetRevision[]): string;
17
+ export declare function configAffectedStatusTargets(targets: StatusV8Target[], pluginId: string, configScope: 'user' | 'project'): StatusScopedTarget[];
18
18
  export declare function createTuiControllerDependencies(context: CliExecutionContext, progressFeed?: RebuildProgressFeed, options?: {
19
19
  now?: () => number;
20
20
  inventoryCatalogSourceReferences?: typeof inventoryCatalogSourceReferences;
@@ -1,6 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
2
  import path from 'node:path';
3
- import { statusCommandV7, statusTargetActivation, statusTargetIntent, statusTargetOwnershipState, } from '../commands/status-v6.js';
3
+ import { flattenStatusTargets, statusCommandV8, statusTargetActivation, statusTargetIntent, statusTargetOwnershipState, } from '../commands/status-v6.js';
4
4
  import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
5
5
  import { cleanupWriteCommand, failedWriteCommandResult, installWriteCommand, mergeWriteCommandResults, reinstallWriteCommand, syncWriteCommand, taskWriteCommand, uninstallWriteCommand, } from '../commands/write-commands.js';
6
6
  import { resolveSyncIntentSelection } from '../commands/sync-command.js';
@@ -19,6 +19,7 @@ import { inventoryCatalogSourceReferences } from '../utils/catalog-source-refere
19
19
  import { operationEvidenceMatches, operationEvidenceFingerprint, TuiConfigInvalidStoreError, TuiController, TuiOperationBlockedError, } from './controller.js';
20
20
  import { buildConfigGroups, buildConfigPage, buildTuiViewModel, projectPlatformPlacement, } from './model.js';
21
21
  import { buildPluginWriteTaskCandidates, buildPluginWriteTaskDraft, evaluatePluginWriteTaskRestoreVersion, pluginWriteTaskTargetKey, } from './plugin-write-task.js';
22
+ import { projectExecutionDiagnostics, projectThrownOperationDiagnostic } from './diagnostic-projection.js';
22
23
  import { createCatalogManagementService, } from './catalog-management.js';
23
24
  import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
24
25
  import { calculateCatalogSourceConfigFingerprint, calculateCatalogSourceIdentity, } from '../utils/catalog-source-identity.js';
@@ -91,8 +92,13 @@ export function statusTargetFingerprint(targets, revisions = []) {
91
92
  scope: target.scope,
92
93
  state: target.state,
93
94
  errors: target.reasons.map((error) => error.code).sort(),
95
+ sharedReasons: [...(target.sharedReasons ?? [])]
96
+ .sort((left, right) => `${left.code}\0${left.message}`.localeCompare(`${right.code}\0${right.message}`, 'en')),
94
97
  components: target.components.map((component) => [component.key, component.state])
95
98
  .sort(([left], [right]) => left.localeCompare(right, 'en')),
99
+ sharedComponents: (target.sharedComponents ?? [])
100
+ .map((component) => [component.key, component.state, component.metadata])
101
+ .sort(([left], [right]) => left.localeCompare(right, 'en')),
96
102
  })).sort((left, right) => [left.pluginId, left.platform, left.scope].join('\0')
97
103
  .localeCompare([right.pluginId, right.platform, right.scope].join('\0'), 'en')),
98
104
  revisions: revisions.map((entry) => ({
@@ -106,7 +112,7 @@ export function statusTargetFingerprint(targets, revisions = []) {
106
112
  }
107
113
  export function configAffectedStatusTargets(targets, pluginId, configScope) {
108
114
  const affected = new Map();
109
- for (const target of targets) {
115
+ for (const target of flattenStatusTargets(targets)) {
110
116
  const intent = statusTargetIntent(target);
111
117
  if (target.pluginId !== pluginId || !intent.enabled)
112
118
  continue;
@@ -146,8 +152,8 @@ function configIntentRequestsApply(intent) {
146
152
  return Object.keys(intent.updates ?? {}).length > 0
147
153
  || (intent.removeKeys?.length ?? 0) > 0;
148
154
  }
149
- function operationReviewStale(message) {
150
- return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`);
155
+ function operationReviewStale(message, diagnostics) {
156
+ return new TuiOperationBlockedError('operationReviewStale', `operationReviewStale: ${message}`, diagnostics);
151
157
  }
152
158
  function sourceEvidenceForPlugin(source, pluginName) {
153
159
  const entry = source.manifest.entries.find((candidate) => candidate.name === pluginName);
@@ -364,7 +370,7 @@ async function loadDependencyOverviews(options) {
364
370
  }
365
371
  }
366
372
  const targetsByPlugin = new Map();
367
- for (const target of options.status.payload.targets) {
373
+ for (const target of flattenStatusTargets(options.status.payload.targets)) {
368
374
  targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
369
375
  }
370
376
  const unsupportedPluginIds = new Set(options.status.pluginSummaries
@@ -647,7 +653,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
647
653
  if (catalogOperationContext)
648
654
  sessionCatalogOperationContext = catalogOperationContext;
649
655
  for (let attempt = 0; attempt < 3; attempt += 1) {
650
- const result = await statusCommandV7({
656
+ const result = await statusCommandV8({
651
657
  cwd: context.cwd,
652
658
  env: context.env,
653
659
  nativeCliRunner: context.nativeCliRunner,
@@ -1208,16 +1214,39 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1208
1214
  ...(signal ? { signal } : {}),
1209
1215
  });
1210
1216
  }
1211
- catch {
1212
- blockedTargets.push(...requestedTargets.map((target) => ({
1213
- target,
1214
- reason: 'targetUnavailable',
1215
- issueIds: [],
1216
- })));
1217
+ catch (error) {
1218
+ if (error instanceof Error && error.name === 'AbortError')
1219
+ throw error;
1220
+ blockedTargets.push(...requestedTargets.map((target, index) => {
1221
+ const diagnostic = error instanceof CatalogGenerationError ? {
1222
+ id: `task-source:${sourceId}:${index}`,
1223
+ code: error.code,
1224
+ stage: 'source',
1225
+ sourceId,
1226
+ safeMessage: 'The Catalog source could not be prepared safely.',
1227
+ target: { pluginId: target.pluginId, platform: target.platform, scope: target.scope },
1228
+ causes: [],
1229
+ detailLines: [],
1230
+ } : projectThrownOperationDiagnostic({
1231
+ error,
1232
+ id: `task-source:${sourceId}:${index}`,
1233
+ stage: 'source',
1234
+ fallbackCode: 'catalogSourceResolutionFailed',
1235
+ sourceId,
1236
+ target,
1237
+ });
1238
+ return {
1239
+ target,
1240
+ reason: 'targetUnavailable',
1241
+ issueIds: [],
1242
+ errorCodes: [diagnostic.code],
1243
+ diagnostics: [diagnostic],
1244
+ };
1245
+ }));
1217
1246
  continue;
1218
1247
  }
1219
1248
  operationContexts.set(sourceId, catalogOperation);
1220
- const status = await statusCommandV7({
1249
+ const status = await statusCommandV8({
1221
1250
  plugins: [...new Set(requestedTargets.map((target) => target.pluginId))],
1222
1251
  cwd: context.cwd,
1223
1252
  env: context.env,
@@ -1227,7 +1256,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1227
1256
  catalogOperationContext: catalogOperation,
1228
1257
  inventoryCatalogSourceReferences: operationInventory,
1229
1258
  });
1230
- observedTargets.push(...status.payload.targets);
1259
+ observedTargets.push(...flattenStatusTargets(status.payload.targets));
1231
1260
  observedRevisions.push(...status.targetRevisions);
1232
1261
  const model = buildTuiViewModel({
1233
1262
  status: status.payload,
@@ -1240,7 +1269,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1240
1269
  const expected = target.expectedUpdate;
1241
1270
  const revision = status.targetRevisions.find((entry) => entry.pluginId === target.pluginId && entry.platform === target.platform
1242
1271
  && entry.scope === target.scope);
1243
- const live = status.payload.targets.find((entry) => {
1272
+ const live = flattenStatusTargets(status.payload.targets).find((entry) => {
1244
1273
  if (entry.pluginId !== target.pluginId || entry.platform !== target.platform)
1245
1274
  return false;
1246
1275
  const desired = statusTargetIntent(entry);
@@ -1325,12 +1354,41 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1325
1354
  } : {}),
1326
1355
  });
1327
1356
  if (!preview.operationPlan) {
1328
- blockedTargets.push(...draft.executableTargets.map((target) => ({
1329
- target,
1330
- reason: 'targetUnavailable',
1331
- issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
1332
- .map((cause) => cause.issueId),
1333
- })));
1357
+ const projected = projectExecutionDiagnostics(preview, `task-preview:${sourceId}`, [], 'pluginWrite');
1358
+ const sourceDiagnostics = projected.diagnostics.filter((diagnostic) => !diagnostic.target)
1359
+ .map((diagnostic) => ({ ...diagnostic, sourceId, target: undefined }));
1360
+ blockedTargets.push(...draft.executableTargets.map((target, targetIndex) => {
1361
+ const matchingDiagnostics = projected.diagnostics.filter((diagnostic) => diagnostic.target?.pluginId === target.pluginId
1362
+ && diagnostic.target.platform === target.platform
1363
+ && diagnostic.target.scope === target.scope);
1364
+ const preparedDiagnostics = [
1365
+ ...matchingDiagnostics,
1366
+ ...(targetIndex === 0 ? sourceDiagnostics : []),
1367
+ ];
1368
+ const targetDiagnostics = preparedDiagnostics.length > 0
1369
+ ? preparedDiagnostics
1370
+ : [projectThrownOperationDiagnostic({
1371
+ error: undefined,
1372
+ id: `task-preview:${sourceId}:${targetIndex}`,
1373
+ stage: 'dependencyPlan',
1374
+ fallbackCode: 'taskOperationPlanMissing',
1375
+ sourceId,
1376
+ target,
1377
+ })];
1378
+ const diagnostics = targetDiagnostics.map((diagnostic, diagnosticIndex) => ({
1379
+ ...structuredClone(diagnostic),
1380
+ id: `task-preview:${sourceId}:${targetIndex}:${diagnosticIndex}`,
1381
+ sourceId,
1382
+ }));
1383
+ return {
1384
+ target,
1385
+ reason: 'targetUnavailable',
1386
+ issueIds: draft.causes.filter((cause) => cause.consumers.some((consumer) => pluginWriteTaskTargetKey(consumer) === pluginWriteTaskTargetKey(target)))
1387
+ .map((cause) => cause.issueId),
1388
+ errorCodes: [...new Set(diagnostics.map((diagnostic) => diagnostic.code))],
1389
+ diagnostics,
1390
+ };
1391
+ }));
1334
1392
  draft.executableTargets = [];
1335
1393
  draft.causes = [];
1336
1394
  break;
@@ -1520,10 +1578,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1520
1578
  ])).values()].sort((left, right) => pluginWriteTaskTargetKey(left.target)
1521
1579
  .localeCompare(pluginWriteTaskTargetKey(right.target), 'en'));
1522
1580
  if (executableTargets.length === 0) {
1523
- throw new TuiOperationBlockedError('taskNoExecutableTargets', '任务没有可安全执行的目标。');
1581
+ throw new TuiOperationBlockedError('taskNoExecutableTargets', '任务没有可安全执行的目标。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
1524
1582
  }
1525
1583
  if (intent.executionPolicy === 'all' && canonicalBlocked.length > 0) {
1526
- throw new TuiOperationBlockedError('taskHasBlockedTargets', '部分目标需要先处理。请调整选择。');
1584
+ throw new TuiOperationBlockedError('taskHasBlockedTargets', '部分目标需要先处理。请调整选择。', canonicalBlocked.flatMap((target) => target.diagnostics ?? []));
1527
1585
  }
1528
1586
  await preparation?.reporter.emit('evidence');
1529
1587
  const evidence = {
@@ -1662,7 +1720,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1662
1720
  return [];
1663
1721
  const platform = group.platform;
1664
1722
  return group.requestedPluginIds.map((pluginId) => {
1665
- const live = sessionStatusResult?.payload.targets.find((target) => target.pluginId === pluginId && target.platform === platform
1723
+ const live = flattenStatusTargets(sessionStatusResult?.payload.targets ?? []).find((target) => target.pluginId === pluginId && target.platform === platform
1666
1724
  && (target.scope === group.scope || statusTargetIntent(target).scopes.includes(group.scope)
1667
1725
  || platform === 'codex' && statusTargetActivation(target)?.[group.scope] === true));
1668
1726
  const revision = sessionStatusResult?.targetRevisions?.find((target) => target.pluginId === pluginId && target.platform === platform
@@ -1915,7 +1973,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1915
1973
  await preparation?.reporter.emit('status');
1916
1974
  const statusCatalogOperation = catalogOperation
1917
1975
  ?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
1918
- const status = await statusCommandV7({
1976
+ const status = await statusCommandV8({
1919
1977
  plugins: [...new Set([intent.pluginId,
1920
1978
  ...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
1921
1979
  ? [intent.originIssue.consumerPluginId] : [])])],
@@ -2118,7 +2176,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
2118
2176
  signal?.throwIfAborted();
2119
2177
  const evidenceTargets = intent.kind === 'configSave'
2120
2178
  ? configVisibleTargets
2121
- : status.payload.targets;
2179
+ : flattenStatusTargets(status.payload.targets);
2122
2180
  const evidenceRevisions = intent.kind === 'configSave'
2123
2181
  ? (status.targetRevisions ?? []).filter((revision) => evidenceTargets.some((target) => target.platform === revision.platform
2124
2182
  && target.scope === revision.scope
@@ -2849,7 +2907,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
2849
2907
  const code = error instanceof TuiOperationBlockedError ? error.code : undefined;
2850
2908
  if (intent.kind === 'pluginWriteTask'
2851
2909
  && (code === 'taskHasBlockedTargets' || code === 'taskNoExecutableTargets')) {
2852
- throw operationReviewStale('确认后任务可执行目标发生变化。');
2910
+ throw operationReviewStale('确认后任务可执行目标发生变化。', error instanceof TuiOperationBlockedError ? error.diagnostics : undefined);
2853
2911
  }
2854
2912
  throw error;
2855
2913
  }