@lesliechan721/aw 0.1.0-beta.4 → 0.1.0-beta.6

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 (99) hide show
  1. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +14 -0
  2. package/dist/authoring/scripts/lib/plugin-version-check.mjs +10 -224
  3. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  4. package/dist/authoring/scripts/remove-plugin.d.mts +2 -0
  5. package/dist/authoring/scripts/remove-plugin.mjs +366 -0
  6. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
  7. package/dist/authoring/scripts/validate-plugin.mjs +9 -46
  8. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  9. package/dist/scripts/bundle-plugins.d.ts +0 -2
  10. package/dist/scripts/bundle-plugins.js +4 -2
  11. package/dist/scripts/bundle-plugins.js.map +1 -1
  12. package/dist/scripts/plugin-staging.d.ts +1 -16
  13. package/dist/scripts/plugin-staging.js +8 -115
  14. package/dist/scripts/plugin-staging.js.map +1 -1
  15. package/dist/src/authoring/workspace-commands.d.ts +9 -1
  16. package/dist/src/authoring/workspace-commands.js +24 -16
  17. package/dist/src/authoring/workspace-commands.js.map +1 -1
  18. package/dist/src/commands/catalog-publication-inspection.d.ts +106 -0
  19. package/dist/src/commands/catalog-publication-inspection.js +444 -0
  20. package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
  21. package/dist/src/commands/catalog-publish.d.ts +18 -2
  22. package/dist/src/commands/catalog-publish.js +261 -316
  23. package/dist/src/commands/catalog-publish.js.map +1 -1
  24. package/dist/src/commands/catalog-status.d.ts +60 -0
  25. package/dist/src/commands/catalog-status.js +251 -0
  26. package/dist/src/commands/catalog-status.js.map +1 -0
  27. package/dist/src/commands/plugin-capabilities.d.ts +22 -0
  28. package/dist/src/commands/plugin-capabilities.js +143 -0
  29. package/dist/src/commands/plugin-capabilities.js.map +1 -0
  30. package/dist/src/commands/rebuild-command.d.ts +2 -0
  31. package/dist/src/commands/rebuild-command.js +37 -6
  32. package/dist/src/commands/rebuild-command.js.map +1 -1
  33. package/dist/src/commands/rebuild-integration.d.ts +1 -0
  34. package/dist/src/commands/rebuild-integration.js +55 -57
  35. package/dist/src/commands/rebuild-integration.js.map +1 -1
  36. package/dist/src/commands/status-v4.d.ts +21 -0
  37. package/dist/src/commands/status-v4.js +63 -2
  38. package/dist/src/commands/status-v4.js.map +1 -1
  39. package/dist/src/index.js +79 -18
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/tui/components.d.ts +70 -0
  42. package/dist/src/tui/components.js +149 -6
  43. package/dist/src/tui/components.js.map +1 -1
  44. package/dist/src/tui/console-app-v4.js +1660 -628
  45. package/dist/src/tui/console-app-v4.js.map +1 -1
  46. package/dist/src/tui/controller-runtime.d.ts +5 -1
  47. package/dist/src/tui/controller-runtime.js +184 -33
  48. package/dist/src/tui/controller-runtime.js.map +1 -1
  49. package/dist/src/tui/controller.d.ts +35 -2
  50. package/dist/src/tui/controller.js +300 -39
  51. package/dist/src/tui/controller.js.map +1 -1
  52. package/dist/src/tui/diagnostics.d.ts +31 -0
  53. package/dist/src/tui/diagnostics.js +71 -0
  54. package/dist/src/tui/diagnostics.js.map +1 -0
  55. package/dist/src/tui/index.d.ts +5 -3
  56. package/dist/src/tui/index.js +3 -1
  57. package/dist/src/tui/index.js.map +1 -1
  58. package/dist/src/tui/model.d.ts +33 -4
  59. package/dist/src/tui/model.js +252 -17
  60. package/dist/src/tui/model.js.map +1 -1
  61. package/dist/src/tui/router.d.ts +15 -9
  62. package/dist/src/tui/router.js +35 -0
  63. package/dist/src/tui/router.js.map +1 -1
  64. package/dist/src/tui/theme.d.ts +18 -10
  65. package/dist/src/tui/theme.js +18 -10
  66. package/dist/src/tui/theme.js.map +1 -1
  67. package/dist/src/tui/viewport.d.ts +20 -0
  68. package/dist/src/tui/viewport.js +29 -0
  69. package/dist/src/tui/viewport.js.map +1 -0
  70. package/dist/src/utils/catalog-runtime-projection.d.ts +1 -7
  71. package/dist/src/utils/catalog-runtime-projection.js +2 -62
  72. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  73. package/dist/src/utils/catalog-source-cache.d.ts +12 -11
  74. package/dist/src/utils/catalog-source-cache.js +20 -0
  75. package/dist/src/utils/catalog-source-cache.js.map +1 -1
  76. package/dist/src/utils/dynamic-skill-tree.js +1 -1
  77. package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
  78. package/dist/src/utils/git-catalog-provider.js +1 -1
  79. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  80. package/dist/src/utils/manifest.d.ts +0 -1
  81. package/dist/src/utils/manifest.js +0 -1
  82. package/dist/src/utils/manifest.js.map +1 -1
  83. package/dist/src/utils/plugin-config.js +28 -30
  84. package/dist/src/utils/plugin-config.js.map +1 -1
  85. package/dist/src/utils/skill-validation.d.ts +1 -1
  86. package/dist/src/utils/skill-validation.js +1 -1
  87. package/dist/src/utils/skill-validation.js.map +1 -1
  88. package/dist/src/utils/state-paths.d.ts +1 -0
  89. package/dist/src/utils/state-paths.js +5 -0
  90. package/dist/src/utils/state-paths.js.map +1 -1
  91. package/package.json +3 -4
  92. package/dist/src/utils/plugin-authoring-capabilities.d.ts +0 -23
  93. package/dist/src/utils/plugin-authoring-capabilities.js +0 -37
  94. package/dist/src/utils/plugin-authoring-capabilities.js.map +0 -1
  95. package/dist/src/utils/plugin-authoring-capabilities.json +0 -92
  96. package/dist/src/utils/skill-frontmatter-capabilities.d.ts +0 -201
  97. package/dist/src/utils/skill-frontmatter-capabilities.js +0 -5
  98. package/dist/src/utils/skill-frontmatter-capabilities.js.map +0 -1
  99. package/dist/src/utils/skill-frontmatter-capabilities.json +0 -200
@@ -1,6 +1,7 @@
1
1
  import type { PlatformName, PluginConfigFieldType, PluginConfigPresentationPreset, PluginConfigSchema, Scope } from '../types.js';
2
- import type { StatusV4Payload, StatusV4PluginSummary, StatusV4Target, StatusV4Component } from '../commands/status-v4.js';
2
+ import type { StatusV4Payload, StatusV4PluginSummary, StatusV4Target, StatusV4Component, StatusTargetRevision, TargetRevisionState } from '../commands/status-v4.js';
3
3
  import type { ResolvedConfigRead } from '../utils/plugin-config.js';
4
+ import { type DiagnosticSummaryViewModel } from './diagnostics.js';
4
5
  export type SourceHealthState = 'ready' | 'cache' | 'path' | 'checking' | 'degraded' | 'unavailable';
5
6
  export interface SourceHealthViewModel {
6
7
  sourceId: string;
@@ -17,14 +18,31 @@ export type PlatformPlacementSummary = {
17
18
  state: 'installed';
18
19
  scope: Scope;
19
20
  health: 'ready' | 'incomplete' | 'error';
21
+ revision: TargetRevisionState;
20
22
  } | {
21
23
  platform: PlatformName;
22
24
  state: 'scopeConflict';
23
25
  placements: Array<{
24
26
  scope: Scope;
25
27
  health: 'ready' | 'incomplete' | 'error';
28
+ revision: TargetRevisionState;
26
29
  }>;
27
30
  };
31
+ export type PluginMenuAction = {
32
+ id: 'add' | 'manage' | 'repair' | 'portableSkills' | 'dependencies' | 'settings' | 'source' | 'sourceProblem' | 'about' | 'refreshSource' | 'technical' | 'back';
33
+ } | {
34
+ id: 'platform' | 'scopeConflict';
35
+ platform: PlatformName;
36
+ } | {
37
+ id: 'upgrade';
38
+ platform: PlatformName;
39
+ scope: Scope;
40
+ availableVersion: string;
41
+ } | {
42
+ id: 'reinstall' | 'remove';
43
+ platform: PlatformName;
44
+ scope: Scope;
45
+ };
28
46
  export interface PluginListItemViewModel {
29
47
  pluginId: string;
30
48
  sourceId: string;
@@ -114,6 +132,7 @@ export interface SourceDetailViewModel {
114
132
  catalogVersion?: string;
115
133
  channel?: string;
116
134
  freshness?: 'remote' | 'cache' | 'path';
135
+ errorCode?: string;
117
136
  configDigest: string;
118
137
  }
119
138
  export interface HomeSummaryViewModel {
@@ -136,7 +155,10 @@ export interface ConfigFieldViewModel {
136
155
  inherited: boolean;
137
156
  configured: boolean;
138
157
  sourceLabel: string;
158
+ effectiveDisplayValue: string;
159
+ effectiveSourceLabel: string;
139
160
  value?: string | number | boolean;
161
+ effectiveValue?: string | number | boolean;
140
162
  defaultValue?: string | number | boolean;
141
163
  enumValues?: string[];
142
164
  valueLabels?: Record<string, string>;
@@ -169,7 +191,7 @@ export interface ConfigPageViewModel {
169
191
  storedCount: number;
170
192
  }
171
193
  export type UserIssueCategory = 'incompleteInstallation' | 'sourceDegraded' | 'sourceUnavailable' | 'sourceMissing' | 'sourceIdentityChanged' | 'requiredConfigMissing' | 'scopeConflict' | 'dependencyMissing' | 'dependencyNotReady' | 'dependencyTargetUnsupported' | 'dependencyStillRequired' | 'dependencyGraphInvalid' | 'legacyState' | 'diagnostic';
172
- export type UserIssueActionId = 'reinstall' | 'remove' | 'refreshSource' | 'continueWithCache' | 'editSource' | 'retryLater' | 'reAddSource' | 'viewSourceChange' | 'openConfig' | 'keepUserScope' | 'keepProjectScope' | 'viewTechnical' | 'manualMigration' | 'installDependency' | 'reinstallDependency' | 'viewDependencyIssue' | 'viewDependencies';
194
+ export type UserIssueActionId = 'reinstall' | 'cleanup' | 'remove' | 'refreshSource' | 'continueWithCache' | 'editSource' | 'retryLater' | 'reAddSource' | 'viewSourceChange' | 'openConfig' | 'keepUserScope' | 'keepProjectScope' | 'viewTechnical' | 'manualMigration' | 'installDependency' | 'reinstallDependency' | 'viewDependencyIssue' | 'viewDependencies';
173
195
  export interface UserIssueActionViewModel {
174
196
  id: UserIssueActionId;
175
197
  label: string;
@@ -178,7 +200,7 @@ export interface UserIssueActionViewModel {
178
200
  platform?: PlatformName;
179
201
  scope?: Scope;
180
202
  project?: 'current' | 'other' | null;
181
- writeAction?: 'install' | 'upgrade' | 'reinstall';
203
+ writeAction?: 'install' | 'upgrade' | 'reinstall' | 'cleanup';
182
204
  }
183
205
  export type TargetIssueRootCode = 'partial' | 'sourceMissing' | 'sourceChanged' | 'catalogSourceUnavailable' | 'bundleUnavailable' | 'dependencyMissing' | 'dependencyNotReady' | 'dependencyTargetUnsupported' | 'dependencyStillRequired' | 'dependencyGraphInvalid' | 'targetDiagnostic';
184
206
  export interface UserIssueViewModel {
@@ -218,6 +240,7 @@ export interface UserIssueViewModel {
218
240
  code: string;
219
241
  message: string;
220
242
  }>;
243
+ diagnostic: DiagnosticSummaryViewModel;
221
244
  }
222
245
  export interface TuiViewModel {
223
246
  plugins: PluginListItemViewModel[];
@@ -257,11 +280,15 @@ export interface PortableSkillSelectionViewModel {
257
280
  };
258
281
  canContinue: boolean;
259
282
  }
260
- export declare function projectPlatformPlacement(platform: PlatformName, targets: StatusV4Target[]): PlatformPlacementSummary;
283
+ export declare function projectPlatformPlacement(platform: PlatformName, targets: StatusV4Target[], revisions?: StatusTargetRevision[]): PlatformPlacementSummary;
284
+ export declare function buildPluginPreviewActions(plugin: PluginListItemViewModel): PluginMenuAction[];
285
+ export declare function buildPluginDetailActions(plugin: PluginListItemViewModel): PluginMenuAction[];
286
+ export declare function buildPlatformActions(plugin: PluginListItemViewModel, platform: PlatformName): PluginMenuAction[];
261
287
  export declare function hasInstalledPlacement(plugin: PluginListItemViewModel): boolean;
262
288
  export declare function buildTuiViewModel(options: {
263
289
  status: StatusV4Payload;
264
290
  pluginSummaries: StatusV4PluginSummary[];
291
+ targetRevisions?: StatusTargetRevision[];
265
292
  sourceHealth?: ReadonlyMap<string, SourceHealthViewModel>;
266
293
  sourceDetails?: SourceDetailViewModel[];
267
294
  dependencyOverviews?: PluginDependencyOverviewViewModel[];
@@ -270,10 +297,12 @@ export declare function buildTuiViewModel(options: {
270
297
  export declare function buildConfigGroups(options: {
271
298
  schema: PluginConfigSchema;
272
299
  read: ResolvedConfigRead;
300
+ effectiveRead?: ResolvedConfigRead;
273
301
  dirtyKeys?: ReadonlySet<string>;
274
302
  }): ConfigGroupViewModel[];
275
303
  export declare function buildConfigPage(options: {
276
304
  pluginId: string;
277
305
  schema: PluginConfigSchema;
278
306
  read: ResolvedConfigRead;
307
+ effectiveRead?: ResolvedConfigRead;
279
308
  }): ConfigPageViewModel;
@@ -1,3 +1,4 @@
1
+ import { safeDiagnosticCode, } from './diagnostics.js';
1
2
  function compareText(left, right) {
2
3
  return left.localeCompare(right, 'en');
3
4
  }
@@ -14,18 +15,34 @@ function isInstalledScopeEvidence(target) {
14
15
  function dependencyPluginIdFromMessage(message) {
15
16
  return message.match(/(?:[a-z0-9]+(?:-[a-z0-9]+)*)\/(?:[a-z0-9]+(?:-[a-z0-9]+)*)/u)?.[0];
16
17
  }
17
- export function projectPlatformPlacement(platform, targets) {
18
+ export function projectPlatformPlacement(platform, targets, revisions = []) {
19
+ const revisionFor = (target) => revisions.find((entry) => entry.pluginId === target.pluginId
20
+ && entry.platform === target.platform
21
+ && entry.scope === target.scope)?.revision
22
+ ?? { state: 'unknown', ...(target.version ? { availableVersion: target.version } : {}) };
18
23
  const supportedScopes = [...new Set(targets.map((target) => target.scope))];
19
24
  const installed = targets.filter(isInstalledScopeEvidence);
20
25
  if (installed.length === 0) {
21
26
  const errorTarget = targets.find((target) => target.state === 'error');
22
27
  if (!errorTarget)
23
28
  return { platform, state: 'notInstalled', supportedScopes };
24
- return { platform, state: 'installed', scope: errorTarget.scope, health: 'error' };
29
+ return {
30
+ platform,
31
+ state: 'installed',
32
+ scope: errorTarget.scope,
33
+ health: 'error',
34
+ revision: revisionFor(errorTarget),
35
+ };
25
36
  }
26
37
  if (installed.length === 1) {
27
38
  const target = installed[0];
28
- return { platform, state: 'installed', scope: target.scope, health: targetHealth(target) };
39
+ return {
40
+ platform,
41
+ state: 'installed',
42
+ scope: target.scope,
43
+ health: targetHealth(target),
44
+ revision: revisionFor(target),
45
+ };
29
46
  }
30
47
  return {
31
48
  platform,
@@ -33,9 +50,89 @@ export function projectPlatformPlacement(platform, targets) {
33
50
  placements: installed.map((target) => ({
34
51
  scope: target.scope,
35
52
  health: targetHealth(target),
53
+ revision: revisionFor(target),
36
54
  })).sort((left, right) => compareText(left.scope, right.scope)),
37
55
  };
38
56
  }
57
+ function hasIncompletePlacement(plugin) {
58
+ return plugin.installedPlacements.some((placement) => placement.state === 'scopeConflict'
59
+ || placement.state === 'installed' && placement.health !== 'ready');
60
+ }
61
+ function hasMissingPlatform(plugin) {
62
+ return plugin.supportedPlatforms.some((platform) => plugin.installedPlacements.find((placement) => placement.platform === platform)?.state === 'notInstalled');
63
+ }
64
+ function capabilityActions(plugin) {
65
+ return [
66
+ ...(plugin.portableSkills ? [{ id: 'portableSkills' }] : []),
67
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
68
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
69
+ ];
70
+ }
71
+ export function buildPluginPreviewActions(plugin) {
72
+ const installed = hasInstalledPlacement(plugin);
73
+ if (plugin.aggregateStatus === 'unavailable') {
74
+ return [{ id: 'sourceProblem' }, ...(installed ? [{ id: 'manage' }] : [])];
75
+ }
76
+ const primary = hasIncompletePlacement(plugin)
77
+ || (plugin.dependencySummary?.issueCount ?? 0) > 0
78
+ ? { id: 'repair' }
79
+ : installed && !hasMissingPlatform(plugin)
80
+ ? { id: 'manage' }
81
+ : { id: 'add' };
82
+ return [primary, ...capabilityActions(plugin), { id: 'source' }];
83
+ }
84
+ export function buildPluginDetailActions(plugin) {
85
+ if (plugin.aggregateStatus === 'unavailable') {
86
+ return [
87
+ ...plugin.installedPlacements.flatMap((placement) => placement.state === 'notInstalled'
88
+ ? []
89
+ : [{ id: 'platform', platform: placement.platform }]),
90
+ { id: 'sourceProblem' },
91
+ { id: 'about' },
92
+ ];
93
+ }
94
+ return [
95
+ ...plugin.installedPlacements.map((placement) => placement.state === 'scopeConflict'
96
+ ? { id: 'scopeConflict', platform: placement.platform }
97
+ : { id: 'platform', platform: placement.platform }),
98
+ ...((plugin.dependencySummary?.totalCount ?? 0) > 0 ? [{ id: 'dependencies' }] : []),
99
+ ...(plugin.portableSkills ? [{ id: 'portableSkills' }] : []),
100
+ ...(plugin.configFieldCount > 0 ? [{ id: 'settings' }] : []),
101
+ { id: 'about' },
102
+ ];
103
+ }
104
+ export function buildPlatformActions(plugin, platform) {
105
+ if (plugin.aggregateStatus === 'unavailable') {
106
+ return [{ id: 'sourceProblem' }, { id: 'refreshSource' }];
107
+ }
108
+ const placement = plugin.installedPlacements.find((entry) => entry.platform === platform);
109
+ if (!placement || placement.state === 'notInstalled')
110
+ return [{ id: 'add' }, { id: 'about' }];
111
+ if (placement.state === 'scopeConflict')
112
+ return [{ id: 'scopeConflict', platform }];
113
+ if (placement.health === 'error')
114
+ return [{ id: 'technical' }, { id: 'back' }];
115
+ if (placement.health === 'incomplete') {
116
+ return [
117
+ { id: 'reinstall', platform, scope: placement.scope },
118
+ { id: 'technical' },
119
+ { id: 'remove', platform, scope: placement.scope },
120
+ ];
121
+ }
122
+ return [
123
+ ...(placement.revision.state === 'upgradeAvailable'
124
+ ? [{
125
+ id: 'upgrade',
126
+ platform,
127
+ scope: placement.scope,
128
+ availableVersion: placement.revision.availableVersion,
129
+ }]
130
+ : []),
131
+ { id: 'reinstall', platform, scope: placement.scope },
132
+ { id: 'remove', platform, scope: placement.scope },
133
+ { id: 'technical' },
134
+ ];
135
+ }
39
136
  export function hasInstalledPlacement(plugin) {
40
137
  return plugin.installedPlacements.some((placement) => placement.state !== 'notInstalled');
41
138
  }
@@ -55,9 +152,9 @@ function sourceState(source, health) {
55
152
  return source.freshness === 'cache' ? 'cache' : 'ready';
56
153
  }
57
154
  function sourceIssue(health, impactedTargets = 0) {
58
- if (health.state !== 'cache' && health.state !== 'degraded' && health.state !== 'unavailable')
155
+ if (health.state !== 'degraded' && health.state !== 'unavailable')
59
156
  return undefined;
60
- const cached = health.state === 'cache' || health.state === 'degraded';
157
+ const cached = health.state === 'degraded';
61
158
  const issueId = health.issueId ?? `source:${health.sourceId}:${cached ? 'cache' : 'unavailable'}`;
62
159
  return {
63
160
  issueId,
@@ -65,7 +162,7 @@ function sourceIssue(health, impactedTargets = 0) {
65
162
  title: `${health.sourceId} · ${cached ? '来源使用缓存' : '来源不可用'}`,
66
163
  explanation: cached
67
164
  ? '远端检查失败。当前仍使用已验证的缓存数据。'
68
- : '远端检查失败,并且没有可用缓存数据。',
165
+ : '来源检查失败,并且没有可用缓存数据。',
69
166
  impact: [cached ? '插件仍可浏览,但数据可能不是最新。' : '当前不能浏览或安装这个来源的插件。',
70
167
  ...(impactedTargets > 0 ? [`${impactedTargets} 个安装位置受此来源根问题影响。`] : [])],
71
168
  entity: { kind: 'source', id: health.sourceId },
@@ -180,7 +277,13 @@ function targetIssues(payload) {
180
277
  ]
181
278
  : root.category === 'sourceUnavailable'
182
279
  ? [
183
- { id: 'editSource', label: '查看来源', description: root.code === 'bundleUnavailable' ? '检查这个插件包或稍后重试' : '修复来源配置或稍后重试' },
280
+ ...(root.code === 'bundleUnavailable' ? [{
281
+ id: 'cleanup',
282
+ label: '清理已安装内容',
283
+ description: '只清理 AW 管理的现场内容;保留插件设置',
284
+ writeAction: 'cleanup',
285
+ }] : []),
286
+ { id: 'editSource', label: '查看来源', description: root.code === 'bundleUnavailable' ? '检查当前 Catalog 或稍后重试' : '修复来源配置或稍后重试' },
184
287
  { id: 'viewTechnical', label: '技术详情' },
185
288
  ]
186
289
  : [{ id: 'viewTechnical', label: '技术详情' }, { id: 'retryLater', label: '稍后重试' }];
@@ -197,7 +300,7 @@ function targetIssues(payload) {
197
300
  dependencyTargetUnsupported: '依赖目标不兼容',
198
301
  dependencyStillRequired: '依赖仍被使用',
199
302
  dependencyGraphInvalid: '依赖关系无效',
200
- }[root.category] : target.state === 'partial' ? '安装不完整' : bundleUnavailable ? '插件包不可用' : '检查失败'}`,
303
+ }[root.category] : target.state === 'partial' ? '安装不完整' : bundleUnavailable ? '当前 Catalog 不再包含此插件' : '检查失败'}`,
201
304
  explanation: dependencyCategory
202
305
  ? dependencyPluginId
203
306
  ? `${dependencyPluginId} 导致当前依赖检查不能通过。`
@@ -205,10 +308,10 @@ function targetIssues(payload) {
205
308
  : target.state === 'partial'
206
309
  ? '必要内容缺失,插件可能不能稳定运行。'
207
310
  : bundleUnavailable
208
- ? '当前来源中无法取得这个插件的可信 bundle。'
311
+ ? '当前 Catalog 不再包含此插件,但仍发现 AW 管理的已安装内容。'
209
312
  : '当前目标不能安全分类为正常安装。',
210
313
  impact: [bundleUnavailable
211
- ? '当前目标不能使用这个插件包;这不表示来源缓存整体不可用。'
314
+ ? '可以清理可信的 AW 现场;这不表示来源缓存整体不可用。'
212
315
  : '写操作前需要重新检查这个目标。'],
213
316
  entity: { kind: 'plugin', id: target.pluginId },
214
317
  pluginId: target.pluginId,
@@ -313,7 +416,10 @@ function configIssues(pluginSummaries) {
313
416
  { id: 'retryLater', label: '稍后重新检查' },
314
417
  { id: 'viewTechnical', label: '查看技术详情' },
315
418
  ],
316
- technicalDetails: [{ code: summary.configDiagnostic.code, message: summary.configDiagnostic.message }],
419
+ technicalDetails: [{
420
+ code: summary.configDiagnostic.code,
421
+ message: '设置读取报告了这个安全错误代码。',
422
+ }],
317
423
  }];
318
424
  }
319
425
  if (!summary.configIssues?.length)
@@ -398,6 +504,104 @@ function enrichDependencyIssuePlacements(issues, overviews) {
398
504
  };
399
505
  });
400
506
  }
507
+ function issueDiagnostic(issue, sourceDetails) {
508
+ const source = issue.sourceId ? sourceDetails.get(issue.sourceId) : undefined;
509
+ const sourceDegraded = issue.category === 'sourceDegraded';
510
+ const sourceUnavailable = issue.category === 'sourceUnavailable' && issue.entity.kind === 'source';
511
+ const configUnavailable = issue.category === 'diagnostic' && issue.issueId.startsWith('config:');
512
+ const configInvalid = issue.category === 'requiredConfigMissing';
513
+ const dependency = issue.category.startsWith('dependency');
514
+ const platformState = issue.issueId.startsWith('status:') || issue.rootCode === 'targetDiagnostic';
515
+ const preserved = sourceDegraded || sourceUnavailable
516
+ ? '已安装插件和已保存设置不会被删除。'
517
+ : configUnavailable
518
+ ? '已保存的设置保持不变。'
519
+ : '现有安装和已保存设置不会因本页自动改变。';
520
+ const impact = sourceDegraded
521
+ ? [
522
+ { state: 'degraded', message: '远端数据可能不是最新;匹配的已验证缓存仍可浏览和安装。' },
523
+ { state: 'preserved', message: preserved },
524
+ ]
525
+ : sourceUnavailable
526
+ ? [
527
+ { state: 'blocked', message: '没有可用缓存;当前不能浏览或安装这个来源中的插件。' },
528
+ { state: 'preserved', message: preserved },
529
+ ]
530
+ : [
531
+ ...issue.impact.map((message) => ({
532
+ state: dependency || configUnavailable || configInvalid || platformState ? 'blocked' : 'degraded',
533
+ message,
534
+ })),
535
+ { state: 'preserved', message: preserved },
536
+ ];
537
+ const primaryAction = issue.actions.find((action) => action.id !== 'viewTechnical');
538
+ const nextStep = primaryAction
539
+ ? `${primaryAction.label},然后重新检查当前状态。`
540
+ : '打开技术详情,记录安全错误代码后重新检查。';
541
+ const sourceDiagnostic = sourceDegraded || sourceUnavailable;
542
+ const rawCode = sourceDiagnostic
543
+ ? source?.errorCode ?? issue.technicalDetails[0]?.code
544
+ : issue.technicalDetails[0]?.code;
545
+ const code = safeDiagnosticCode(rawCode, sourceDegraded
546
+ ? 'catalogSourceDegraded'
547
+ : sourceUnavailable ? 'catalogSourceUnavailable' : 'diagnosticUnavailable');
548
+ const stage = sourceDegraded || sourceUnavailable ? 'resolve-source'
549
+ : configUnavailable ? 'read-config'
550
+ : configInvalid ? 'validate-config'
551
+ : dependency ? 'resolve-dependencies'
552
+ : platformState ? 'read-platform-state' : 'inspect-target';
553
+ const context = [
554
+ ...(issue.pluginId ? [{ key: 'pluginId', value: issue.pluginId }] : []),
555
+ ...(issue.dependencyPluginId
556
+ ? [{ key: 'dependencyPluginId', value: issue.dependencyPluginId }]
557
+ : []),
558
+ ...(issue.platform ? [{ key: 'platform', value: issue.platform }] : []),
559
+ ...(issue.scope ? [{ key: 'scope', value: issue.scope }] : []),
560
+ ...(sourceDiagnostic && source?.freshness
561
+ ? [{ key: 'freshness', value: source.freshness }]
562
+ : []),
563
+ ...(issue.requiredFields?.slice(0, 1).map((field) => ({
564
+ key: 'field',
565
+ value: field.title,
566
+ })) ?? []),
567
+ ];
568
+ const checks = issue.category === 'legacyState'
569
+ ? [
570
+ '不自动迁移旧版状态。',
571
+ '运行 aw status --json,记录公开错误 code。',
572
+ '备份相关 AW 配置。',
573
+ '按技术详情使用受支持命令或人工处理。',
574
+ '再次运行状态检查。',
575
+ ]
576
+ : sourceDegraded || sourceUnavailable
577
+ ? ['检查 Git URL、ref 或本地目录。', '确认当前终端具备来源授权。', '运行“重新检查来源”验证修复。']
578
+ : dependency
579
+ ? ['检查依赖关系和依赖插件状态。', '完成恢复动作后重新检查根插件。']
580
+ : configUnavailable
581
+ ? ['恢复插件来源和可信字段定义。', '重新检查来源后再次打开设置。']
582
+ : configInvalid
583
+ ? ['完成或修正列出的设置字段。', '重新检查插件设置。']
584
+ : ['检查目标平台配置和访问权限。', '完成恢复动作后重新运行状态检查。'];
585
+ return {
586
+ severity: sourceDegraded ? 'warning' : 'error',
587
+ title: issue.title,
588
+ userMessage: issue.explanation,
589
+ impact,
590
+ nextStep,
591
+ technical: {
592
+ code,
593
+ stage,
594
+ ...(sourceDiagnostic && issue.sourceId ? { sourceId: issue.sourceId } : {}),
595
+ ...(sourceDiagnostic && source?.requestedRef ? { requestedRef: source.requestedRef } : {}),
596
+ ...(sourceDiagnostic && source?.resolvedCommit ? { resolvedCommit: source.resolvedCommit } : {}),
597
+ ...(sourceDegraded || sourceUnavailable
598
+ ? { lastGood: sourceDegraded ? 'available' : 'unavailable' }
599
+ : {}),
600
+ ...(context.length > 0 ? { context } : {}),
601
+ checks,
602
+ },
603
+ };
604
+ }
401
605
  export function buildTuiViewModel(options) {
402
606
  const dependencyById = new Map((options.dependencyOverviews ?? [])
403
607
  .map((overview) => [overview.pluginId, overview]));
@@ -413,7 +617,9 @@ export function buildTuiViewModel(options) {
413
617
  const sourceId = targets[0]?.sourceId ?? pluginId.slice(0, pluginId.indexOf('/'));
414
618
  const placements = ['codex', 'claude'].flatMap((platform) => {
415
619
  const platformTargets = targets.filter((target) => target.platform === platform);
416
- return platformTargets.length > 0 ? [projectPlatformPlacement(platform, platformTargets)] : [];
620
+ return platformTargets.length > 0
621
+ ? [projectPlatformPlacement(platform, platformTargets, options.targetRevisions)]
622
+ : [];
417
623
  });
418
624
  const attention = placements.some((placement) => placement.state === 'scopeConflict'
419
625
  || (placement.state === 'installed' && placement.health !== 'ready'));
@@ -466,7 +672,7 @@ export function buildTuiViewModel(options) {
466
672
  state: sourceState(source, options.sourceHealth?.get(source.id)),
467
673
  }));
468
674
  const sourceRootIds = new Set([...(options.sourceHealth?.values() ?? [])]
469
- .filter((health) => health.state === 'cache' || health.state === 'degraded' || health.state === 'unavailable')
675
+ .filter((health) => health.state === 'degraded' || health.state === 'unavailable')
470
676
  .map((health) => health.sourceId));
471
677
  const allTargetIssues = targetIssues(options.status);
472
678
  const targetImpactCounts = new Map();
@@ -475,13 +681,18 @@ export function buildTuiViewModel(options) {
475
681
  continue;
476
682
  targetImpactCounts.set(issue.sourceId, (targetImpactCounts.get(issue.sourceId) ?? 0) + 1);
477
683
  }
478
- const issues = enrichDependencyIssuePlacements([
684
+ const sourceDetails = new Map((options.sourceDetails ?? []).map((detail) => [detail.id, detail]));
685
+ const issueDrafts = enrichDependencyIssuePlacements([
479
686
  ...allTargetIssues.filter((issue) => !issue.mergeEligible
480
687
  || !issue.sourceId || !sourceRootIds.has(issue.sourceId)),
481
688
  ...statusIssues(options.status),
482
689
  ...configIssues(options.pluginSummaries),
483
690
  ...[...(options.sourceHealth?.values() ?? [])].flatMap((health) => sourceIssue(health, targetImpactCounts.get(health.sourceId) ?? 0) ?? []),
484
691
  ], options.dependencyOverviews ?? []);
692
+ const issues = issueDrafts.map((issue) => ({
693
+ ...issue,
694
+ diagnostic: issueDiagnostic(issue, sourceDetails),
695
+ }));
485
696
  return {
486
697
  plugins,
487
698
  sourceTabs,
@@ -524,25 +735,39 @@ function configSourceLabel(source) {
524
735
  }
525
736
  export function buildConfigGroups(options) {
526
737
  const entryByKey = new Map(options.read.fields.map((field) => [field.key, field]));
738
+ const effectiveEntryByKey = new Map((options.effectiveRead ?? options.read).fields.map((field) => [field.key, field]));
527
739
  const effectivePage = options.read.scope === 'effective';
528
740
  const fieldModel = (key) => {
529
741
  const schema = options.schema.schema[key];
530
742
  const entry = entryByKey.get(key);
743
+ const effectiveEntry = effectiveEntryByKey.get(key);
531
744
  const presentation = options.schema.presentation?.fields?.[key];
532
745
  const inherited = effectivePage
533
- ? entry?.sourceLayer === undefined || entry?.sourceLayer === null
746
+ ? false
534
747
  : entry?.storedSourceLayer === undefined;
535
748
  const configured = effectivePage
536
- ? entry?.valueState === 'configured' || entry?.valueState === 'default'
749
+ ? entry?.sourceLayer !== undefined
750
+ && entry.sourceLayer !== null
751
+ && entry.effectiveValue !== undefined
752
+ && entry.effectiveValue !== null
753
+ && (entry.valueState === 'configured' || entry.valueState === 'default')
537
754
  : entry?.storedSourceLayer !== undefined;
538
755
  const privateValue = schema.sensitive === true || schema.type === 'file' || schema.type === 'directory';
539
756
  const selectedValue = effectivePage ? entry?.effectiveValue : entry?.storedValue;
757
+ const selectedStoredInvalid = !effectivePage
758
+ && entry?.storedSourceLayer !== undefined
759
+ && entry.storedValue === undefined;
760
+ const effectiveConfigured = effectiveEntry?.sourceLayer !== undefined
761
+ && effectiveEntry.sourceLayer !== null
762
+ && effectiveEntry.effectiveValue !== undefined
763
+ && effectiveEntry.effectiveValue !== null
764
+ && (effectiveEntry.valueState === 'configured' || effectiveEntry.valueState === 'default');
540
765
  return {
541
766
  key,
542
767
  title: schema.title ?? key,
543
768
  ...(schema.description ? { description: schema.description } : {}),
544
769
  type: schema.type,
545
- displayValue: displayConfigValue({
770
+ displayValue: selectedStoredInvalid ? '值无效' : displayConfigValue({
546
771
  privateValue,
547
772
  inherited,
548
773
  configured,
@@ -556,9 +781,19 @@ export function buildConfigGroups(options) {
556
781
  sourceLabel: effectivePage
557
782
  ? configSourceLabel(entry?.sourceLayer ?? null)
558
783
  : configured ? '本层已设置' : '继承',
784
+ effectiveDisplayValue: displayConfigValue({
785
+ privateValue,
786
+ inherited: false,
787
+ configured: effectiveConfigured,
788
+ value: effectiveEntry?.effectiveValue,
789
+ }),
790
+ effectiveSourceLabel: configSourceLabel(effectiveEntry?.sourceLayer ?? null),
559
791
  ...(!privateValue && selectedValue !== undefined
560
792
  ? { value: selectedValue }
561
793
  : {}),
794
+ ...(!privateValue && effectiveEntry?.effectiveValue !== undefined && effectiveEntry.effectiveValue !== null
795
+ ? { effectiveValue: effectiveEntry.effectiveValue }
796
+ : {}),
562
797
  ...(!privateValue && schema.default !== undefined ? { defaultValue: schema.default } : {}),
563
798
  ...(schema.values ? { enumValues: [...schema.values] } : {}),
564
799
  ...(presentation?.valueLabels ? { valueLabels: { ...presentation.valueLabels } } : {}),