@lesliechan721/aw 0.1.0-beta.33 → 0.1.0-beta.35

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 (47) hide show
  1. package/dist/scripts/test-plugin-scripts.js +1 -1
  2. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  3. package/dist/src/commands/aw-capabilities.js +1 -0
  4. package/dist/src/commands/aw-capabilities.js.map +1 -1
  5. package/dist/src/portable/materializer.js +19 -3
  6. package/dist/src/portable/materializer.js.map +1 -1
  7. package/dist/src/review/review-profile.js +19 -2
  8. package/dist/src/review/review-profile.js.map +1 -1
  9. package/dist/src/tui/components.d.ts +20 -0
  10. package/dist/src/tui/components.js +13 -1
  11. package/dist/src/tui/components.js.map +1 -1
  12. package/dist/src/tui/config-array-draft.d.ts +29 -0
  13. package/dist/src/tui/config-array-draft.js +48 -0
  14. package/dist/src/tui/config-array-draft.js.map +1 -0
  15. package/dist/src/tui/config-value-presentation.d.ts +1 -0
  16. package/dist/src/tui/config-value-presentation.js +17 -1
  17. package/dist/src/tui/config-value-presentation.js.map +1 -1
  18. package/dist/src/tui/console-app-v4.js +504 -288
  19. package/dist/src/tui/console-app-v4.js.map +1 -1
  20. package/dist/src/tui/controller-runtime.js +27 -2
  21. package/dist/src/tui/controller-runtime.js.map +1 -1
  22. package/dist/src/tui/controller.d.ts +15 -0
  23. package/dist/src/tui/controller.js +111 -4
  24. package/dist/src/tui/controller.js.map +1 -1
  25. package/dist/src/tui/model.d.ts +9 -0
  26. package/dist/src/tui/model.js +10 -2
  27. package/dist/src/tui/model.js.map +1 -1
  28. package/dist/src/tui/project-inventory.d.ts +68 -0
  29. package/dist/src/tui/project-inventory.js +203 -0
  30. package/dist/src/tui/project-inventory.js.map +1 -0
  31. package/dist/src/tui/router.d.ts +16 -3
  32. package/dist/src/tui/router.js.map +1 -1
  33. package/dist/src/tui/screen-registry.d.ts +7 -1
  34. package/dist/src/tui/screen-registry.js +1 -1
  35. package/dist/src/tui/screen-registry.js.map +1 -1
  36. package/dist/src/tui/theme.d.ts +12 -0
  37. package/dist/src/tui/theme.js +12 -0
  38. package/dist/src/tui/theme.js.map +1 -1
  39. package/dist/src/types.d.ts +2 -0
  40. package/dist/src/types.js.map +1 -1
  41. package/dist/src/utils/catalog-source-references.js +2 -2
  42. package/dist/src/utils/plugin-config-write.js +3 -1
  43. package/dist/src/utils/plugin-config-write.js.map +1 -1
  44. package/dist/src/utils/plugin-config.d.ts +5 -5
  45. package/dist/src/utils/plugin-config.js +47 -23
  46. package/dist/src/utils/plugin-config.js.map +1 -1
  47. package/package.json +3 -3
@@ -12,6 +12,7 @@ import type { RebuildProgressFeed, RebuildProgressSnapshot } from '../output/reb
12
12
  import { type ConfigPageViewModel, type ConfigGroupViewModel, type InstallPlacementDraftViewModel, type PluginListItemViewModel, type PluginDependencyOverviewViewModel, type PortableSkillSelectionViewModel, type SourceHealthViewModel, type SourceDetailViewModel, type TuiViewModel, type UserIssueCategory, type UserIssueViewModel } from './model.js';
13
13
  import type { PluginContentOverview } from './plugin-content.js';
14
14
  import { OperationHistoryStore, type OperationDiagnosticDetail, type OperationEffectFacts, type OperationHistorySummary, type OperationReportPreview, type SafeOperationDiagnostic, type SaveOperationReportResult } from './operation-history.js';
15
+ import { type ProjectInventoryRoot, type ProjectInventoryViewModel } from './project-inventory.js';
15
16
  import { type ConfigInvalidStoreEvidence, type PluginInstallFilter, type PortableBlockerCode, type TuiPageContext, type TuiRoute, type TuiRouterSnapshot } from './router.js';
16
17
  import { type CatalogSourceGenerationViewModel } from './catalog-generation.js';
17
18
  import type { CatalogSourceFreshnessViewModel } from './catalog-freshness.js';
@@ -136,6 +137,7 @@ export interface ConfigOperationChange {
136
137
  }>;
137
138
  }
138
139
  export interface ConfigSaveBatchGroup {
140
+ editEvidence?: Record<string, import('./model.js').ConfigEditEvidence>;
139
141
  scope: Scope;
140
142
  updates?: Record<string, unknown>;
141
143
  removeKeys?: string[];
@@ -382,6 +384,10 @@ export interface OperationReviewModel {
382
384
  version?: string;
383
385
  };
384
386
  changes: OperationReviewChange[];
387
+ configImpact?: {
388
+ state: 'known' | 'unknown';
389
+ affectedTargetCount: number;
390
+ };
385
391
  confirmationDetails?: string[];
386
392
  technicalDetails?: string[];
387
393
  equivalentCommand?: string;
@@ -778,6 +784,12 @@ export interface TuiControllerSnapshot {
778
784
  operationRecordId?: string;
779
785
  };
780
786
  projectIntentReminder?: ProjectIntentReminder;
787
+ projectInventory?: ProjectInventoryViewModel;
788
+ projectInventoryRefresh: {
789
+ state: 'idle' | 'checking' | 'error';
790
+ checkedAtMs?: number;
791
+ errorCode?: string;
792
+ };
781
793
  projectDeclarationAbsent?: boolean;
782
794
  projectIntentIssue?: {
783
795
  state: 'invalid' | 'unavailable';
@@ -833,6 +845,9 @@ export declare class TuiController {
833
845
  discardDrafts?: boolean;
834
846
  }): boolean;
835
847
  load(): Promise<void>;
848
+ listProjectInventoryRoots(section?: 'declared' | 'extraInstalled'): ProjectInventoryRoot[];
849
+ getProjectInventoryRoot(pluginId: string): ProjectInventoryRoot | undefined;
850
+ refreshProjectInventory(): Promise<boolean>;
836
851
  push(route: TuiRoute, context?: TuiPageContext): void;
837
852
  hasPortableSkills(pluginId: string): boolean;
838
853
  preparePortableInstall(pluginId: string, navigation?: 'push' | 'replace'): Promise<PortableSkillSelectionViewModel>;
@@ -5,11 +5,12 @@ import { flattenStatusTargets, statusTargetActivation, statusTargetExtensions, s
5
5
  import { buildRebuildReceiptJson, requestedIntentAction } from '../output/command-receipt.js';
6
6
  import { buildOperationResultModel } from '../commands/operation-planner.js';
7
7
  import { buildOperationReviewProjection } from './operation-review-planner.js';
8
- import { buildTuiViewModel, dependencyPlacementTargets, hasInstalledPlacement, } from './model.js';
8
+ import { buildTuiViewModel, dependencyPlacementTargets, } from './model.js';
9
9
  import { safeDiagnosticCode, sanitizeDiagnosticText } from './diagnostics.js';
10
10
  import { projectExecutionDiagnostics, projectThrownOperationDiagnostic } from './diagnostic-projection.js';
11
11
  import { OperationHistoryStore, } from './operation-history.js';
12
12
  import { targetHasArtifactCollision } from '../utils/target-eligibility.js';
13
+ import { buildProjectInventory, hasStatusInstallationEvidence, } from './project-inventory.js';
13
14
  import { TuiRouter, } from './router.js';
14
15
  import { normalizeTuiRoute } from './screen-registry.js';
15
16
  import { CatalogSourcesSession } from './catalog-sources/session.js';
@@ -731,6 +732,9 @@ export class TuiController {
731
732
  #portableResultRecheck;
732
733
  #completionNotice;
733
734
  #projectIntentReminder;
735
+ #projectInventory;
736
+ #projectInventoryRefresh = { state: 'idle' };
737
+ #projectInventoryRefreshToken = 0;
734
738
  #loadError;
735
739
  #portableDraft;
736
740
  #recoverablePortableDraft;
@@ -850,6 +854,8 @@ export class TuiController {
850
854
  ...(this.#projectIntentReminder
851
855
  ? { projectIntentReminder: this.#projectIntentReminder }
852
856
  : {}),
857
+ ...(this.#projectInventory ? { projectInventory: this.#projectInventory } : {}),
858
+ projectInventoryRefresh: this.#projectInventoryRefresh,
853
859
  ...(this.#status?.projectIntent?.state === 'absent' ? { projectDeclarationAbsent: true } : {}),
854
860
  ...(this.#status?.projectIntent?.state === 'invalid'
855
861
  || this.#status?.projectIntent?.state === 'unavailable'
@@ -1197,13 +1203,25 @@ export class TuiController {
1197
1203
  portablePluginIds: this.#status.portablePluginIds,
1198
1204
  configStoreInventory: this.#status.configStoreInventory,
1199
1205
  });
1206
+ this.#projectInventory = buildProjectInventory({
1207
+ projectRoot: this.#status.projectRoot,
1208
+ projectIntent: this.#status.projectIntent,
1209
+ status: this.#status.payload,
1210
+ pluginSummaries: this.#viewModel.plugins,
1211
+ targetRevisions: this.#status.targetRevisions,
1212
+ dependencyOverviews: this.#status.dependencyOverviews,
1213
+ actionableTargets: this.#projectIntentReminder?.actionableTargets,
1214
+ });
1200
1215
  }
1201
1216
  #normalizeRoute(route) {
1202
1217
  return normalizeTuiRoute(route, this.#routeEntityState());
1203
1218
  }
1204
1219
  #routeEntityState() {
1205
1220
  return {
1206
- hasPlugin: (pluginId) => this.#viewModel?.plugins.some((plugin) => plugin.pluginId === pluginId) ?? false,
1221
+ hasPlugin: (pluginId) => this.#viewModel?.plugins.some((plugin) => plugin.pluginId === pluginId)
1222
+ || this.#projectInventory?.declaredRoots.some((root) => root.pluginId === pluginId)
1223
+ || this.#projectInventory?.extraInstalledRoots.some((root) => root.pluginId === pluginId)
1224
+ || false,
1207
1225
  hasSource: (sourceId) => this.#viewModel?.sourceTabs.some((source) => source.id === sourceId) ?? false,
1208
1226
  hasIssue: (issueId) => this.#viewModel?.issues.some((issue) => issue.issueId === issueId) ?? false,
1209
1227
  hasHistoryRecord: (recordId) => this.#operationHistory.get(recordId) !== undefined,
@@ -1527,6 +1545,8 @@ export class TuiController {
1527
1545
  this.#assertAcceptingActions();
1528
1546
  this.dependencies.beginCatalogSession?.();
1529
1547
  const generation = ++this.#generation;
1548
+ this.#projectInventoryRefreshToken += 1;
1549
+ this.#projectInventoryRefresh = { state: 'idle' };
1530
1550
  this.#pluginContentEpoch += 1;
1531
1551
  this.#pluginContent.clear();
1532
1552
  this.#pluginContentInflight.clear();
@@ -1554,6 +1574,49 @@ export class TuiController {
1554
1574
  this.#loadError = undefined;
1555
1575
  this.#publish();
1556
1576
  }
1577
+ listProjectInventoryRoots(section = 'declared') {
1578
+ return cloneFrozen(section === 'declared'
1579
+ ? this.#projectInventory?.declaredRoots ?? []
1580
+ : this.#projectInventory?.extraInstalledRoots ?? []);
1581
+ }
1582
+ getProjectInventoryRoot(pluginId) {
1583
+ const root = [...(this.#projectInventory?.declaredRoots ?? []),
1584
+ ...(this.#projectInventory?.extraInstalledRoots ?? [])]
1585
+ .find((entry) => entry.pluginId === pluginId);
1586
+ return root ? cloneFrozen(root) : undefined;
1587
+ }
1588
+ async refreshProjectInventory() {
1589
+ this.#assertAcceptingActions();
1590
+ if (!this.#status || this.#phase !== 'ready')
1591
+ return false;
1592
+ const token = ++this.#projectInventoryRefreshToken;
1593
+ const generation = this.#generation;
1594
+ this.#projectInventoryRefresh = { state: 'checking' };
1595
+ this.#publish();
1596
+ try {
1597
+ const status = await this.#trackRead(this.dependencies.loadStatus(this.#abort.signal));
1598
+ if (this.#disposed || generation !== this.#generation
1599
+ || token !== this.#projectInventoryRefreshToken)
1600
+ return false;
1601
+ this.#applyStatus(status);
1602
+ this.#projectInventoryRefresh = {
1603
+ state: 'idle',
1604
+ checkedAtMs: this.#statusReadAtMs,
1605
+ };
1606
+ this.#publish();
1607
+ return true;
1608
+ }
1609
+ catch (error) {
1610
+ if (this.#disposed || generation !== this.#generation
1611
+ || token !== this.#projectInventoryRefreshToken)
1612
+ return false;
1613
+ const code = issueCode(error);
1614
+ await this.#logErrorSafe(error, code);
1615
+ this.#projectInventoryRefresh = { state: 'error', errorCode: code };
1616
+ this.#publish();
1617
+ return false;
1618
+ }
1619
+ }
1557
1620
  push(route, context) {
1558
1621
  if (this.#router.snapshot.current.route.name === 'result') {
1559
1622
  this.#invalidatePortableResultRecheck();
@@ -2433,10 +2496,39 @@ export class TuiController {
2433
2496
  }
2434
2497
  listPlugins(options) {
2435
2498
  const query = options.query?.trim().toLocaleLowerCase('en');
2499
+ const actualScopes = new Map();
2500
+ const statusTargets = flattenStatusTargets(this.#status?.payload.targets ?? []);
2501
+ const targetsByPlugin = new Map();
2502
+ for (const target of statusTargets) {
2503
+ targetsByPlugin.set(target.pluginId, [...(targetsByPlugin.get(target.pluginId) ?? []), target]);
2504
+ const owned = target.platform === 'claude'
2505
+ ? ['present', 'partial'].includes(statusTargetOwnershipState(target))
2506
+ : target.components.some((component) => component.kind === 'ownership'
2507
+ && component.key !== 'native'
2508
+ && !component.key.startsWith('activation-')
2509
+ && !component.key.startsWith('marketplace-')
2510
+ && (component.state === 'present' || component.state === 'partial'));
2511
+ const activation = target.platform === 'codex' ? statusTargetActivation(target) : undefined;
2512
+ if (!owned && !(activation?.evidence === 'observed'
2513
+ && activation[target.scope] === true)
2514
+ && !hasStatusInstallationEvidence(target))
2515
+ continue;
2516
+ const scopes = actualScopes.get(target.pluginId) ?? new Set();
2517
+ scopes.add(target.scope);
2518
+ actualScopes.set(target.pluginId, scopes);
2519
+ }
2436
2520
  return cloneFrozen((this.#viewModel?.plugins ?? []).filter((plugin) => (!options.sourceId || plugin.sourceId === options.sourceId)
2437
2521
  && (options.installFilter !== 'installed'
2438
- || hasInstalledPlacement(plugin))
2439
- && (options.installFilter !== 'uninstalled' || !hasInstalledPlacement(plugin))
2522
+ || (actualScopes.get(plugin.pluginId)?.size ?? 0) > 0)
2523
+ && (options.installFilter !== 'uninstalled'
2524
+ || plugin.aggregateStatus === 'available'
2525
+ && (targetsByPlugin.get(plugin.pluginId)?.length ?? 0) > 0
2526
+ && targetsByPlugin.get(plugin.pluginId).every((target) => target.state === 'absent')
2527
+ && (actualScopes.get(plugin.pluginId)?.size ?? 0) === 0)
2528
+ && (options.installFilter !== 'project'
2529
+ || actualScopes.get(plugin.pluginId)?.has('project') === true)
2530
+ && (options.installFilter !== 'user'
2531
+ || actualScopes.get(plugin.pluginId)?.has('user') === true)
2440
2532
  && (options.installFilter !== 'updateAvailable' || plugin.update?.state === 'available')
2441
2533
  && (options.installFilter !== 'attention'
2442
2534
  || plugin.aggregateStatus === 'needsAttention' || plugin.aggregateStatus === 'unavailable'
@@ -3277,6 +3369,20 @@ export class TuiController {
3277
3369
  assertPluginWriteTaskEvidence(intent, evidence);
3278
3370
  if (this.#router.snapshot.current.route.name === 'result')
3279
3371
  navigation = 'replace';
3372
+ const configEvidence = intent.kind === 'configSave'
3373
+ ? evidence.config ? [evidence.config] : undefined
3374
+ : intent.kind === 'configSaveBatch'
3375
+ ? evidence.configBatch?.groups.map((group) => group.config)
3376
+ : undefined;
3377
+ if ((intent.kind === 'configSave' || intent.kind === 'configSaveBatch')
3378
+ && (!configEvidence || configEvidence.length !== (intent.kind === 'configSave' ? 1 : intent.groups.length))) {
3379
+ throw new TuiOperationBlockedError('configImpactUnavailable', '设置确认缺少完整的应用影响证据。');
3380
+ }
3381
+ const configImpact = configEvidence ? {
3382
+ state: configEvidence.some((group) => group.impactState !== 'known') ? 'unknown' : 'known',
3383
+ affectedTargetCount: new Set(configEvidence.flatMap((group) => group.affectedTargets
3384
+ .map((target) => `${target.platform}\0${target.scope}`))).size,
3385
+ } : undefined;
3280
3386
  const projection = buildOperationReviewProjection({
3281
3387
  intent,
3282
3388
  evidence,
@@ -3349,6 +3455,7 @@ export class TuiController {
3349
3455
  ? { originIssue: intent.originIssue }
3350
3456
  : {}),
3351
3457
  ...projection,
3458
+ ...(configImpact ? { configImpact } : {}),
3352
3459
  subject: {
3353
3460
  name: 'pluginId' in intent
3354
3461
  ? this.#viewModel?.plugins.find((plugin) => plugin.pluginId === intent.pluginId)?.displayName