@lesliechan721/aw 0.1.0-beta.17 → 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.
- package/dist/src/commands/operation-planner.d.ts +3 -3
- package/dist/src/commands/rebuild-command.js +4 -5
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +83 -33
- package/dist/src/commands/status-v6.js +531 -191
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/wizard.d.ts +2 -2
- package/dist/src/index.js +4 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/tui/catalog-sources/projection.d.ts +2 -2
- package/dist/src/tui/catalog-sources/screens.js +17 -10
- package/dist/src/tui/catalog-sources/screens.js.map +1 -1
- package/dist/src/tui/console-app-v4.js +1296 -437
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +3 -3
- package/dist/src/tui/controller-runtime.js +85 -27
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +17 -7
- package/dist/src/tui/controller.js +265 -24
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph.d.ts +3 -0
- package/dist/src/tui/dependency-graph.js +37 -1
- package/dist/src/tui/dependency-graph.js.map +1 -1
- package/dist/src/tui/diagnostic-projection.d.ts +1 -0
- package/dist/src/tui/diagnostic-projection.js +51 -4
- package/dist/src/tui/diagnostic-projection.js.map +1 -1
- package/dist/src/tui/model.d.ts +22 -12
- package/dist/src/tui/model.js +276 -89
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-history.d.ts +1 -0
- package/dist/src/tui/operation-history.js +5 -0
- package/dist/src/tui/operation-history.js.map +1 -1
- package/dist/src/tui/operation-review-planner.js +67 -2
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-write-task.d.ts +2 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -1
- package/dist/src/tui/router.d.ts +6 -0
- package/dist/src/tui/router.js +56 -12
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +7 -0
- package/dist/src/utils/codex-manager.d.ts +5 -7
- package/dist/src/utils/codex-manager.js +24 -97
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/plugin-config-write.js +3 -3
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +2 -2
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/package.json +3 -3
|
@@ -23,7 +23,26 @@ import { readPluginIntentState, } from '../utils/plugin-intent-state.js';
|
|
|
23
23
|
import { IMPLEMENTED_AW_CAPABILITIES } from './aw-capabilities.js';
|
|
24
24
|
import { createDependencyPlacementPlan } from './dependency-operation-plan.js';
|
|
25
25
|
import { getCodexOperationRequirement } from '../utils/agent-version.js';
|
|
26
|
-
import {
|
|
26
|
+
import { inspectSystemCodex, } from '../utils/codex-manager.js';
|
|
27
|
+
export function statusTargetPlacements(target) {
|
|
28
|
+
if (target.platform === 'claude')
|
|
29
|
+
return [target];
|
|
30
|
+
return target.placements.map((placement) => ({
|
|
31
|
+
pluginName: target.pluginName,
|
|
32
|
+
sourceId: target.sourceId,
|
|
33
|
+
sourceIdentity: target.sourceIdentity,
|
|
34
|
+
pluginId: target.pluginId,
|
|
35
|
+
...(target.version ? { version: target.version } : {}),
|
|
36
|
+
platform: target.platform,
|
|
37
|
+
cwd: target.cwd,
|
|
38
|
+
...placement,
|
|
39
|
+
sharedComponents: target.components.filter((component) => component.kind !== 'systemConsumer' || component.key === `codex:${placement.scope}`),
|
|
40
|
+
sharedReasons: target.reasons.filter((reason) => reason.code.startsWith('codexSharedNative') || reason.code === 'staleCacheObserved'),
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
export function flattenStatusTargets(targets) {
|
|
44
|
+
return targets.flatMap(statusTargetPlacements);
|
|
45
|
+
}
|
|
27
46
|
export function projectStatusSharedNativeReasons(sharedNative, existingReasonCodes = [], desiredEnabled = false) {
|
|
28
47
|
if (!sharedNative)
|
|
29
48
|
return [];
|
|
@@ -64,7 +83,7 @@ export function projectStatusSharedNativeReasons(sharedNative, existingReasonCod
|
|
|
64
83
|
];
|
|
65
84
|
}
|
|
66
85
|
function statusComponent(target, kind, key) {
|
|
67
|
-
return target.components.find((component) => component.kind === kind && (key === undefined || component.key === key));
|
|
86
|
+
return [...target.components, ...(target.sharedComponents ?? [])].find((component) => component.kind === kind && (key === undefined || component.key === key));
|
|
68
87
|
}
|
|
69
88
|
export function statusTargetIntent(target) {
|
|
70
89
|
const metadata = statusComponent(target, 'intent', 'desired')?.metadata;
|
|
@@ -121,7 +140,7 @@ export function statusTargetSharedNative(target) {
|
|
|
121
140
|
export function statusTargetActivation(target) {
|
|
122
141
|
const user = statusComponent(target, 'activation', 'user');
|
|
123
142
|
const project = statusComponent(target, 'activation', 'project');
|
|
124
|
-
const consumer = statusComponent(target, 'systemConsumer'
|
|
143
|
+
const consumer = statusComponent(target, 'systemConsumer');
|
|
125
144
|
if (!user && !project && !consumer)
|
|
126
145
|
return undefined;
|
|
127
146
|
const activation = user ?? project ?? consumer;
|
|
@@ -181,7 +200,7 @@ export function setStatusDependencyInspectionHookForTests(hook) {
|
|
|
181
200
|
dependencyInspectionHookForTests = hook;
|
|
182
201
|
}
|
|
183
202
|
export async function loadConsoleItemsResult(options = {}, runtime = {}) {
|
|
184
|
-
const result = await
|
|
203
|
+
const result = await statusCommandV8({
|
|
185
204
|
cwd: options.cwd ?? process.cwd(),
|
|
186
205
|
env: runtime.env,
|
|
187
206
|
platform: options.platform,
|
|
@@ -203,7 +222,7 @@ export async function loadConsoleItemsResult(options = {}, runtime = {}) {
|
|
|
203
222
|
...(pluginSummary ? { pluginSummary } : {}),
|
|
204
223
|
targets: [],
|
|
205
224
|
};
|
|
206
|
-
current.targets.push(target);
|
|
225
|
+
current.targets.push(...statusTargetPlacements(target));
|
|
207
226
|
groups.set(target.pluginId, current);
|
|
208
227
|
}
|
|
209
228
|
const allSourcesFailed = result.payload.catalog.sources.length > 0
|
|
@@ -251,6 +270,7 @@ function finalizeTargetError(error, targetState) {
|
|
|
251
270
|
return {
|
|
252
271
|
...error,
|
|
253
272
|
severity: error.code === 'artifactCollision'
|
|
273
|
+
|| error.code === 'dependencyGraphInvalid'
|
|
254
274
|
|| (targetState === 'error'
|
|
255
275
|
&& error.dependencyPluginId === undefined)
|
|
256
276
|
? 'blocking'
|
|
@@ -416,7 +436,7 @@ function projectDependencyClosureForTarget(options) {
|
|
|
416
436
|
visit(options.pluginId);
|
|
417
437
|
return closure;
|
|
418
438
|
}
|
|
419
|
-
function statusRecovery(options) {
|
|
439
|
+
export function statusRecovery(options) {
|
|
420
440
|
if (options.errorCodes?.some((code) => code === 'codexProjectPluginVersionTooLow'
|
|
421
441
|
|| code === 'codexProjectPluginUnsupported'
|
|
422
442
|
|| code === 'codexSystemCliUnsupported')) {
|
|
@@ -431,8 +451,29 @@ function statusRecovery(options) {
|
|
|
431
451
|
description: '升级 AW CLI 到支持插件所需 capability 的版本后重试。',
|
|
432
452
|
}];
|
|
433
453
|
}
|
|
454
|
+
if (options.errorCodes?.includes('dependencyGraphInvalid')) {
|
|
455
|
+
return [{
|
|
456
|
+
command: 'manual',
|
|
457
|
+
description: '修复 Catalog 中的插件依赖循环后重试。',
|
|
458
|
+
}];
|
|
459
|
+
}
|
|
434
460
|
if (options.state === 'ready' || options.state === 'absent')
|
|
435
461
|
return [];
|
|
462
|
+
if (options.desired.scopes.length === 0
|
|
463
|
+
&& options.sharedNative?.state === 'replaceable'
|
|
464
|
+
&& options.sharedNative.reason === 'damaged') {
|
|
465
|
+
return [
|
|
466
|
+
...(options.actualEnabled ? [{
|
|
467
|
+
command: 'cleanup',
|
|
468
|
+
...(options.scope ? { scope: options.scope } : {}),
|
|
469
|
+
description: `仅清理 ${options.scope ?? '当前'} scope 的激活;不会修复或删除损坏的共享内容。`,
|
|
470
|
+
}] : []),
|
|
471
|
+
{
|
|
472
|
+
command: 'manual',
|
|
473
|
+
description: `共享内容已损坏。先明确选择 user 或 project 使用范围,再运行 aw install ${options.pluginId} --platform codex --scope <user|project> 重建。`,
|
|
474
|
+
},
|
|
475
|
+
];
|
|
476
|
+
}
|
|
436
477
|
if (options.state === 'orphaned') {
|
|
437
478
|
return [{ command: 'cleanup', description: '清理可证明属于 AW 的遗留输出。' }];
|
|
438
479
|
}
|
|
@@ -556,20 +597,51 @@ export function projectTargetRevision(availableVersion, installedVersion) {
|
|
|
556
597
|
return { state: 'upgradeAvailable', availableVersion, installedVersion };
|
|
557
598
|
return { state: 'unknown', availableVersion, installedVersion };
|
|
558
599
|
}
|
|
559
|
-
function
|
|
600
|
+
function renderStatusV8(payload, verbose) {
|
|
560
601
|
const lines = [`Catalog sources: ${payload.catalog.sources.length}`];
|
|
561
602
|
for (const source of payload.catalog.sources) {
|
|
562
603
|
lines.push(`${source.primary ? '*' : '-'} ${source.id}: ${source.error ?? source.freshness ?? 'unavailable'}`);
|
|
563
604
|
}
|
|
564
605
|
for (const target of payload.targets) {
|
|
565
606
|
const reasons = target.reasons.length > 0
|
|
566
|
-
? ` [${target.reasons.map((reason) => `${reason.code}
|
|
607
|
+
? ` [${target.reasons.map((reason) => reason.message.startsWith(`${reason.code}:`)
|
|
608
|
+
? reason.message : `${reason.code}: ${reason.message}`).join('; ')}]`
|
|
567
609
|
: '';
|
|
568
|
-
|
|
610
|
+
if (target.platform === 'claude') {
|
|
611
|
+
lines.push(`${target.pluginId} ${target.platform} (${target.scope}): ${target.state}${reasons}`);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
lines.push(`${target.pluginId} ${target.platform}: ${target.state}${reasons}`);
|
|
615
|
+
const desiredScopes = target.placements.filter((placement) => placement.components.some((component) => component.kind === 'intent' && component.metadata?.enabled === true))
|
|
616
|
+
.map((placement) => placement.scope);
|
|
617
|
+
const actualScopes = target.placements.filter((placement) => placement.components.some((component) => component.kind === 'activation'
|
|
618
|
+
&& component.key === placement.scope
|
|
619
|
+
&& component.state === 'enabled'))
|
|
620
|
+
.map((placement) => placement.scope);
|
|
621
|
+
const usage = (scopes) => scopes.length === 0
|
|
622
|
+
? 'disabled'
|
|
623
|
+
: scopes.length === 2 ? 'user+project' : scopes[0];
|
|
624
|
+
lines.push(` desired use: ${usage(desiredScopes)}`);
|
|
625
|
+
lines.push(` actual use: ${usage(actualScopes)}`);
|
|
626
|
+
const shared = target.components.find((component) => component.kind === 'sharedNative');
|
|
627
|
+
if (shared)
|
|
628
|
+
lines.push(` shared content: ${shared.state} (${shared.key})`);
|
|
629
|
+
for (const placement of target.placements) {
|
|
630
|
+
lines.push(` ${placement.scope}: ${placement.state}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
569
633
|
if (verbose) {
|
|
570
634
|
for (const component of target.components) {
|
|
571
635
|
lines.push(` component ${component.kind}/${component.key}: ${component.state}`);
|
|
572
636
|
}
|
|
637
|
+
if (target.platform === 'codex') {
|
|
638
|
+
for (const placement of target.placements) {
|
|
639
|
+
lines.push(` placement ${placement.scope}: ${placement.state}`);
|
|
640
|
+
for (const component of placement.components) {
|
|
641
|
+
lines.push(` component ${component.kind}/${component.key}: ${component.state}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
573
645
|
for (const action of target.recoveryActions) {
|
|
574
646
|
lines.push(` recovery ${action.command}: ${action.description}`);
|
|
575
647
|
}
|
|
@@ -600,7 +672,7 @@ function collisionRootIssueId(options) {
|
|
|
600
672
|
options.collision.artifactKey,
|
|
601
673
|
].join(':');
|
|
602
674
|
}
|
|
603
|
-
async function
|
|
675
|
+
async function statusCommandV8InSession(options) {
|
|
604
676
|
const env = options.env ?? process.env;
|
|
605
677
|
const dependencyPluginCache = new Map();
|
|
606
678
|
const dependencyInspectionCache = new Map();
|
|
@@ -637,13 +709,13 @@ async function statusCommandV7InSession(options) {
|
|
|
637
709
|
return undefined;
|
|
638
710
|
}
|
|
639
711
|
};
|
|
712
|
+
const codexScopePathsAlias = codexProjectConfigAliasesUserHome(options.cwd, env);
|
|
640
713
|
const [userCodexConfig, projectCodexConfig] = await Promise.all([
|
|
641
714
|
readActivationConfig('user'),
|
|
642
|
-
readActivationConfig('project'),
|
|
715
|
+
codexScopePathsAlias ? undefined : readActivationConfig('project'),
|
|
643
716
|
]);
|
|
644
717
|
const currentProjectKey = await resolveCanonicalRepoProjectKey(options.cwd);
|
|
645
718
|
const currentProjectLocationKey = createProjectLocationKey(currentProjectKey);
|
|
646
|
-
const codexScopePathsAlias = codexProjectConfigAliasesUserHome(options.cwd, env);
|
|
647
719
|
const inventoryErrors = [];
|
|
648
720
|
const readInventory = options.inventoryCatalogSourceReferences
|
|
649
721
|
?? inventoryCatalogSourceReferences;
|
|
@@ -667,7 +739,8 @@ async function statusCommandV7InSession(options) {
|
|
|
667
739
|
const inspectCodexSystemConsumer = (scope) => {
|
|
668
740
|
let inspection = codexSystemConsumerInspections.get(scope);
|
|
669
741
|
if (!inspection) {
|
|
670
|
-
inspection =
|
|
742
|
+
inspection = inspectSystemCodex({
|
|
743
|
+
runner: nativeCliRunner,
|
|
671
744
|
cwd: options.cwd,
|
|
672
745
|
env,
|
|
673
746
|
operationRequirement: getCodexOperationRequirement(scope),
|
|
@@ -730,9 +803,9 @@ async function statusCommandV7InSession(options) {
|
|
|
730
803
|
if (!userCodexConfig || (probe.scope === 'project' && !projectCodexConfig)) {
|
|
731
804
|
return 'partial';
|
|
732
805
|
}
|
|
733
|
-
const configEnabled =
|
|
734
|
-
|
|
735
|
-
|
|
806
|
+
const configEnabled = (probe.scope === 'user'
|
|
807
|
+
? userCodexConfig
|
|
808
|
+
: projectCodexConfig)?.snapshot.plugins[`${probe.pluginName}@${probe.marketplaceName}`]?.enabled === true;
|
|
736
809
|
return configEnabled
|
|
737
810
|
? 'present'
|
|
738
811
|
: 'partial';
|
|
@@ -863,6 +936,35 @@ async function statusCommandV7InSession(options) {
|
|
|
863
936
|
};
|
|
864
937
|
}
|
|
865
938
|
try {
|
|
939
|
+
const catalogEntriesByName = new Map(item.resolution.manifest.entries.map((entry) => [
|
|
940
|
+
entry.name,
|
|
941
|
+
entry,
|
|
942
|
+
]));
|
|
943
|
+
const cyclicDependencyPluginNames = new Set();
|
|
944
|
+
const dependencyVisitState = new Map();
|
|
945
|
+
const dependencyVisitStack = [];
|
|
946
|
+
const visitDependencyGraph = (pluginName) => {
|
|
947
|
+
const state = dependencyVisitState.get(pluginName);
|
|
948
|
+
if (state === 'visited')
|
|
949
|
+
return;
|
|
950
|
+
if (state === 'visiting') {
|
|
951
|
+
const cycleStart = dependencyVisitStack.lastIndexOf(pluginName);
|
|
952
|
+
for (const name of dependencyVisitStack.slice(cycleStart)) {
|
|
953
|
+
cyclicDependencyPluginNames.add(name);
|
|
954
|
+
}
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
dependencyVisitState.set(pluginName, 'visiting');
|
|
958
|
+
dependencyVisitStack.push(pluginName);
|
|
959
|
+
for (const dependencyName of catalogEntriesByName.get(pluginName)?.dependencies ?? []) {
|
|
960
|
+
if (catalogEntriesByName.has(dependencyName))
|
|
961
|
+
visitDependencyGraph(dependencyName);
|
|
962
|
+
}
|
|
963
|
+
dependencyVisitStack.pop();
|
|
964
|
+
dependencyVisitState.set(pluginName, 'visited');
|
|
965
|
+
};
|
|
966
|
+
for (const pluginName of catalogEntriesByName.keys())
|
|
967
|
+
visitDependencyGraph(pluginName);
|
|
866
968
|
const placementEntries = item.resolution.manifest.entries.map((entry) => {
|
|
867
969
|
const supportedPlatforms = resolveCatalogEntrySupportedPlatforms(entry.policy);
|
|
868
970
|
return {
|
|
@@ -1123,8 +1225,238 @@ async function statusCommandV7InSession(options) {
|
|
|
1123
1225
|
const dependencyErrors = [];
|
|
1124
1226
|
const dependencyReadiness = [];
|
|
1125
1227
|
const directDependencies = plugin.manifest.dependencies ?? [];
|
|
1228
|
+
const desiredInScope = sourceDesired.get(desiredTargetKey(plugin.pluginId, platform))?.scopes.includes(scope) === true;
|
|
1229
|
+
const codexPluginId = `${entry.name}@${marketplaceName}`;
|
|
1230
|
+
const actualCodexActivation = platform === 'codex'
|
|
1231
|
+
&& (scope === 'user' ? userCodexConfig : projectCodexConfig)
|
|
1232
|
+
?.snapshot.plugins[codexPluginId]?.enabled === true;
|
|
1233
|
+
const hasScopedExtensionResidue = platform === 'codex'
|
|
1234
|
+
&& inspected.inspection.components.some((component) => component.component.kind !== 'native-plugin'
|
|
1235
|
+
&& !component.component.identity.artifactKey.startsWith('activation-')
|
|
1236
|
+
&& !component.component.identity.artifactKey.startsWith('marketplace-')
|
|
1237
|
+
&& (component.state !== 'absent' || component.collision !== undefined));
|
|
1238
|
+
const dependencyPriority = {
|
|
1239
|
+
ready: 5,
|
|
1240
|
+
blockedByCollision: 4,
|
|
1241
|
+
incomplete: 3,
|
|
1242
|
+
error: 2,
|
|
1243
|
+
absent: 1,
|
|
1244
|
+
};
|
|
1245
|
+
function dependencyPlacementsFor(dependencyId, consumerScope) {
|
|
1246
|
+
const dependencyOverride = (consumerScope === 'project'
|
|
1247
|
+
? projectIntent
|
|
1248
|
+
: userIntent).dependencyScopeOverrides.find((override) => override.pluginId === dependencyId
|
|
1249
|
+
&& (override.platform === undefined || override.platform === platform));
|
|
1250
|
+
return dependencyOverride
|
|
1251
|
+
? [dependencyOverride.scope]
|
|
1252
|
+
: consumerScope === 'project' ? ['project', 'user'] : ['user'];
|
|
1253
|
+
}
|
|
1254
|
+
async function inspectDependencyAtScope(dependencyName, dependencyScope) {
|
|
1255
|
+
const dependencyId = formatPluginId({
|
|
1256
|
+
sourceId: item.resolution.sourceId,
|
|
1257
|
+
pluginName: dependencyName,
|
|
1258
|
+
});
|
|
1259
|
+
const inspectionCacheKey = [
|
|
1260
|
+
item.resolution.sourceIdentity,
|
|
1261
|
+
dependencyId,
|
|
1262
|
+
platform,
|
|
1263
|
+
dependencyScope,
|
|
1264
|
+
dependencyScope === 'project' ? currentProjectKey : '',
|
|
1265
|
+
].join('\0');
|
|
1266
|
+
if (cyclicDependencyPluginNames.has(dependencyName)) {
|
|
1267
|
+
return {
|
|
1268
|
+
state: 'error',
|
|
1269
|
+
dependencyScope,
|
|
1270
|
+
rootIssueId: `dependencyGraphInvalid:${dependencyId}`,
|
|
1271
|
+
rootIssueCode: 'dependencyGraphInvalid',
|
|
1272
|
+
issuePluginId: dependencyId,
|
|
1273
|
+
issueScope: dependencyScope,
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
const cached = dependencyInspectionCache.get(inspectionCacheKey);
|
|
1277
|
+
if (cached)
|
|
1278
|
+
return cached;
|
|
1279
|
+
const inspection = (async () => {
|
|
1280
|
+
const dependencyEntry = item.resolution.manifest.entries.find((candidate) => candidate.name === dependencyName);
|
|
1281
|
+
if (!dependencyEntry) {
|
|
1282
|
+
return {
|
|
1283
|
+
state: 'absent',
|
|
1284
|
+
dependencyScope,
|
|
1285
|
+
rootIssueId: `dependencyMissing:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1286
|
+
rootIssueCode: 'dependencyMissing',
|
|
1287
|
+
issuePluginId: dependencyId,
|
|
1288
|
+
issueScope: dependencyScope,
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const dependencySupport = checkAwCapabilitySupport(dependencyEntry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
|
|
1292
|
+
if (!dependencySupport.supported) {
|
|
1293
|
+
return {
|
|
1294
|
+
state: 'error',
|
|
1295
|
+
dependencyScope,
|
|
1296
|
+
rootIssueId: `awCapabilityUnsupported:${dependencyId}`,
|
|
1297
|
+
rootIssueCode: 'awCapabilityUnsupported',
|
|
1298
|
+
issuePluginId: dependencyId,
|
|
1299
|
+
issueScope: dependencyScope,
|
|
1300
|
+
missingCapabilities: dependencySupport.missing,
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
const pluginCacheKey = [item.resolution.sourceIdentity, dependencyId].join('\0');
|
|
1304
|
+
let dependencyPluginPromise = dependencyPluginCache.get(pluginCacheKey);
|
|
1305
|
+
if (!dependencyPluginPromise) {
|
|
1306
|
+
dependencyPluginPromise = readPluginManifest(path.join(item.resolution.rootDir, 'plugins', dependencyName), { mode: 'bundle' }).then((dependencyPlugin) => {
|
|
1307
|
+
dependencyPlugin.sourceId = item.resolution.sourceId;
|
|
1308
|
+
dependencyPlugin.sourceIdentity = item.resolution.sourceIdentity;
|
|
1309
|
+
dependencyPlugin.catalogLogicalIdentity = item.resolution.logicalIdentity;
|
|
1310
|
+
dependencyPlugin.pluginId = dependencyId;
|
|
1311
|
+
return dependencyPlugin;
|
|
1312
|
+
});
|
|
1313
|
+
dependencyPluginCache.set(pluginCacheKey, dependencyPluginPromise);
|
|
1314
|
+
}
|
|
1315
|
+
const dependencyPlugin = await dependencyPluginPromise;
|
|
1316
|
+
dependencyInspectionHookForTests?.();
|
|
1317
|
+
const dependencyMarketplaceName = marketplaceNameFor(dependencyName, platform, dependencyScope);
|
|
1318
|
+
const dependencyInspected = await inspectRebuildTarget({
|
|
1319
|
+
sourceId: item.resolution.sourceId,
|
|
1320
|
+
sourceIdentity: item.resolution.sourceIdentity,
|
|
1321
|
+
pluginId: dependencyId,
|
|
1322
|
+
marketplaceName: dependencyMarketplaceName,
|
|
1323
|
+
marketplaceRoot,
|
|
1324
|
+
plugin: dependencyPlugin,
|
|
1325
|
+
platform,
|
|
1326
|
+
scope: dependencyScope,
|
|
1327
|
+
cwd: options.cwd,
|
|
1328
|
+
catalog: { rootDir: item.resolution.rootDir, source: 'local' },
|
|
1329
|
+
context: {
|
|
1330
|
+
env,
|
|
1331
|
+
nativeCliRunner,
|
|
1332
|
+
reporter: createNoopReporter(),
|
|
1333
|
+
debugLogger: createNoopDebugLogger(),
|
|
1334
|
+
codexObservationSession: options.codexObservationSession,
|
|
1335
|
+
},
|
|
1336
|
+
userActivationDesired: codexDesiredUserActivationIds,
|
|
1337
|
+
nativeProbe: readOnlyNativeProbe({
|
|
1338
|
+
pluginName: dependencyName,
|
|
1339
|
+
sourceId: item.resolution.sourceId,
|
|
1340
|
+
sourceIdentity: item.resolution.sourceIdentity,
|
|
1341
|
+
platform,
|
|
1342
|
+
scope: dependencyScope,
|
|
1343
|
+
marketplaceName: dependencyMarketplaceName,
|
|
1344
|
+
marketplaceRoot,
|
|
1345
|
+
desired: sourceDesired.get(desiredTargetKey(dependencyId, platform))
|
|
1346
|
+
?.scopes.includes(dependencyScope) === true,
|
|
1347
|
+
}),
|
|
1348
|
+
shallow: true,
|
|
1349
|
+
});
|
|
1350
|
+
const dependencyLiveState = projectedLiveState({
|
|
1351
|
+
inspection: dependencyInspected.inspection,
|
|
1352
|
+
required: dependencyInspected.target.desiredOwnedComponents
|
|
1353
|
+
?? dependencyInspected.target.ownedComponents,
|
|
1354
|
+
optional: dependencyInspected.target.optionalOwnedComponents ?? [],
|
|
1355
|
+
});
|
|
1356
|
+
const collision = dependencyInspected.inspection.components.find((component) => component.collision)?.collision;
|
|
1357
|
+
if (collision) {
|
|
1358
|
+
return {
|
|
1359
|
+
state: 'blockedByCollision',
|
|
1360
|
+
dependencyScope,
|
|
1361
|
+
rootIssueId: collisionRootIssueId({
|
|
1362
|
+
pluginId: dependencyId,
|
|
1363
|
+
platform,
|
|
1364
|
+
scope: dependencyScope,
|
|
1365
|
+
collision,
|
|
1366
|
+
}),
|
|
1367
|
+
rootIssueCode: 'artifactCollision',
|
|
1368
|
+
issuePluginId: dependencyId,
|
|
1369
|
+
issueScope: dependencyScope,
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
const requiredDependencyComponents = dependencyInspected.target
|
|
1373
|
+
.desiredOwnedComponents ?? dependencyInspected.target.ownedComponents;
|
|
1374
|
+
const desiredDependency = sourceDesired.get(desiredTargetKey(dependencyId, platform));
|
|
1375
|
+
const projectMigrationPending = dependencyScope === 'project'
|
|
1376
|
+
&& projectIntent.dependencyScopeOverrides.some((override) => override.pluginId === dependencyId
|
|
1377
|
+
&& override.scope === 'project'
|
|
1378
|
+
&& (override.platform === undefined || override.platform === platform))
|
|
1379
|
+
&& desiredDependency?.scopes.includes('project') === true
|
|
1380
|
+
&& desiredDependency.scopes.includes('user') === false
|
|
1381
|
+
&& ownerInventory.references.some((reference) => reference.kind === 'namespacedOwner'
|
|
1382
|
+
&& reference.sourceId === item.resolution.sourceId
|
|
1383
|
+
&& reference.pluginName === dependencyName
|
|
1384
|
+
&& reference.platform === platform
|
|
1385
|
+
&& reference.scope === 'user');
|
|
1386
|
+
const migrationOnlyPartial = dependencyLiveState === 'partial'
|
|
1387
|
+
&& projectMigrationPending
|
|
1388
|
+
&& dependencyInspected.inspection.components.every((component) => component.state === 'present' || component.state === 'absent')
|
|
1389
|
+
&& requiredDependencyComponents.every((required) => dependencyInspected.inspection.components.some((component) => component.component === required
|
|
1390
|
+
&& (component.state === 'present'
|
|
1391
|
+
|| component.state === 'absent'
|
|
1392
|
+
&& component.component.kind !== 'native-plugin'
|
|
1393
|
+
&& 'locator' in component.component
|
|
1394
|
+
&& component.component.locator.kind === 'codex-activation'
|
|
1395
|
+
&& component.component.locator.desired === false)));
|
|
1396
|
+
const effectiveDependencyLiveState = migrationOnlyPartial
|
|
1397
|
+
? 'present'
|
|
1398
|
+
: dependencyLiveState;
|
|
1399
|
+
if (effectiveDependencyLiveState !== 'present') {
|
|
1400
|
+
const state = effectiveDependencyLiveState === 'absent' ? 'absent'
|
|
1401
|
+
: effectiveDependencyLiveState === 'partial' ? 'incomplete'
|
|
1402
|
+
: 'error';
|
|
1403
|
+
return {
|
|
1404
|
+
state,
|
|
1405
|
+
dependencyScope,
|
|
1406
|
+
rootIssueId: state === 'absent'
|
|
1407
|
+
? `dependencyMissing:${dependencyId}:${platform}:${dependencyScope}`
|
|
1408
|
+
: state === 'incomplete'
|
|
1409
|
+
? `incompleteInstallation:${dependencyId}:${platform}:${dependencyScope}`
|
|
1410
|
+
: `diagnostic:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1411
|
+
rootIssueCode: state === 'absent' ? 'dependencyMissing'
|
|
1412
|
+
: state === 'incomplete' ? 'incompleteInstallation' : 'diagnostic',
|
|
1413
|
+
issuePluginId: dependencyId,
|
|
1414
|
+
issueScope: dependencyScope,
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
for (const childName of dependencyPlugin.manifest.dependencies ?? []) {
|
|
1418
|
+
const childId = formatPluginId({
|
|
1419
|
+
sourceId: item.resolution.sourceId,
|
|
1420
|
+
pluginName: childName,
|
|
1421
|
+
});
|
|
1422
|
+
let childResult;
|
|
1423
|
+
for (const childScope of dependencyPlacementsFor(childId, dependencyScope)) {
|
|
1424
|
+
const candidate = await inspectDependencyAtScope(childName, childScope);
|
|
1425
|
+
if (candidate.state === 'ready') {
|
|
1426
|
+
childResult = candidate;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
if (!childResult
|
|
1430
|
+
|| dependencyPriority[candidate.state]
|
|
1431
|
+
> dependencyPriority[childResult.state]) {
|
|
1432
|
+
childResult = candidate;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
if (childResult?.state !== 'ready') {
|
|
1436
|
+
return {
|
|
1437
|
+
...childResult,
|
|
1438
|
+
dependencyScope,
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { state: 'ready', dependencyScope };
|
|
1443
|
+
})().catch(() => ({
|
|
1444
|
+
state: 'error',
|
|
1445
|
+
dependencyScope,
|
|
1446
|
+
rootIssueId: `diagnostic:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1447
|
+
rootIssueCode: 'diagnostic',
|
|
1448
|
+
issuePluginId: dependencyId,
|
|
1449
|
+
issueScope: dependencyScope,
|
|
1450
|
+
}));
|
|
1451
|
+
dependencyInspectionCache.set(inspectionCacheKey, inspection);
|
|
1452
|
+
return inspection;
|
|
1453
|
+
}
|
|
1126
1454
|
if (directDependencies.length > 0
|
|
1127
|
-
&& (
|
|
1455
|
+
&& (desiredInScope
|
|
1456
|
+
|| actualCodexActivation
|
|
1457
|
+
|| hasScopedExtensionResidue
|
|
1458
|
+
|| (platform !== 'codex'
|
|
1459
|
+
&& (liveState === 'present' || liveState === 'partial')))) {
|
|
1128
1460
|
for (const dependencyName of directDependencies) {
|
|
1129
1461
|
const dependencyId = formatPluginId({
|
|
1130
1462
|
sourceId: item.resolution.sourceId,
|
|
@@ -1178,120 +1510,12 @@ async function statusCommandV7InSession(options) {
|
|
|
1178
1510
|
}, 'partial'));
|
|
1179
1511
|
continue;
|
|
1180
1512
|
}
|
|
1181
|
-
const
|
|
1182
|
-
let dependencyPluginPromise = dependencyPluginCache.get(pluginCacheKey);
|
|
1183
|
-
if (!dependencyPluginPromise) {
|
|
1184
|
-
dependencyPluginPromise = readPluginManifest(path.join(item.resolution.rootDir, 'plugins', dependencyName), { mode: 'bundle' }).then((dependencyPlugin) => {
|
|
1185
|
-
dependencyPlugin.sourceId = item.resolution.sourceId;
|
|
1186
|
-
dependencyPlugin.sourceIdentity = item.resolution.sourceIdentity;
|
|
1187
|
-
dependencyPlugin.catalogLogicalIdentity = item.resolution.logicalIdentity;
|
|
1188
|
-
dependencyPlugin.pluginId = dependencyId;
|
|
1189
|
-
return dependencyPlugin;
|
|
1190
|
-
});
|
|
1191
|
-
dependencyPluginCache.set(pluginCacheKey, dependencyPluginPromise);
|
|
1192
|
-
}
|
|
1193
|
-
const dependencyOverride = (scope === 'project'
|
|
1194
|
-
? projectIntent
|
|
1195
|
-
: userIntent).dependencyScopeOverrides.find((override) => override.pluginId === dependencyId
|
|
1196
|
-
&& (override.platform === undefined || override.platform === platform));
|
|
1197
|
-
const placements = dependencyOverride
|
|
1198
|
-
? [dependencyOverride.scope]
|
|
1199
|
-
: scope === 'project' ? ['project', 'user'] : ['user'];
|
|
1513
|
+
const placements = dependencyPlacementsFor(dependencyId, scope);
|
|
1200
1514
|
let dependencyResult = {
|
|
1201
1515
|
state: 'absent',
|
|
1202
1516
|
};
|
|
1203
1517
|
for (const dependencyScope of placements) {
|
|
1204
|
-
const
|
|
1205
|
-
item.resolution.sourceIdentity,
|
|
1206
|
-
dependencyId,
|
|
1207
|
-
platform,
|
|
1208
|
-
dependencyScope,
|
|
1209
|
-
dependencyScope === 'project' ? currentProjectKey : '',
|
|
1210
|
-
].join('\0');
|
|
1211
|
-
let dependencyInspection = dependencyInspectionCache.get(inspectionCacheKey);
|
|
1212
|
-
if (!dependencyInspection) {
|
|
1213
|
-
dependencyInspection = dependencyPluginPromise.then(async (dependencyPlugin) => {
|
|
1214
|
-
dependencyInspectionHookForTests?.();
|
|
1215
|
-
const dependencyMarketplaceName = marketplaceNameFor(dependencyName, platform, dependencyScope);
|
|
1216
|
-
const dependencyInspected = await inspectRebuildTarget({
|
|
1217
|
-
sourceId: item.resolution.sourceId,
|
|
1218
|
-
sourceIdentity: item.resolution.sourceIdentity,
|
|
1219
|
-
pluginId: dependencyId,
|
|
1220
|
-
marketplaceName: dependencyMarketplaceName,
|
|
1221
|
-
marketplaceRoot,
|
|
1222
|
-
plugin: dependencyPlugin,
|
|
1223
|
-
platform,
|
|
1224
|
-
scope: dependencyScope,
|
|
1225
|
-
cwd: options.cwd,
|
|
1226
|
-
catalog: { rootDir: item.resolution.rootDir, source: 'local' },
|
|
1227
|
-
context: {
|
|
1228
|
-
env,
|
|
1229
|
-
nativeCliRunner,
|
|
1230
|
-
reporter: createNoopReporter(),
|
|
1231
|
-
debugLogger: createNoopDebugLogger(),
|
|
1232
|
-
codexObservationSession: options.codexObservationSession,
|
|
1233
|
-
},
|
|
1234
|
-
userActivationDesired: codexDesiredUserActivationIds,
|
|
1235
|
-
nativeProbe: readOnlyNativeProbe({
|
|
1236
|
-
pluginName: dependencyName,
|
|
1237
|
-
sourceId: item.resolution.sourceId,
|
|
1238
|
-
sourceIdentity: item.resolution.sourceIdentity,
|
|
1239
|
-
platform,
|
|
1240
|
-
scope: dependencyScope,
|
|
1241
|
-
marketplaceName: dependencyMarketplaceName,
|
|
1242
|
-
marketplaceRoot,
|
|
1243
|
-
desired: sourceDesired.get(desiredTargetKey(dependencyId, platform))
|
|
1244
|
-
?.scopes.includes(dependencyScope) === true,
|
|
1245
|
-
}),
|
|
1246
|
-
shallow: true,
|
|
1247
|
-
});
|
|
1248
|
-
const dependencyLiveState = projectedLiveState({
|
|
1249
|
-
inspection: dependencyInspected.inspection,
|
|
1250
|
-
required: dependencyInspected.target.desiredOwnedComponents
|
|
1251
|
-
?? dependencyInspected.target.ownedComponents,
|
|
1252
|
-
optional: dependencyInspected.target.optionalOwnedComponents ?? [],
|
|
1253
|
-
});
|
|
1254
|
-
const collision = dependencyInspected.inspection.components.find((component) => component.collision)?.collision;
|
|
1255
|
-
if (collision) {
|
|
1256
|
-
return {
|
|
1257
|
-
state: 'blockedByCollision',
|
|
1258
|
-
dependencyScope,
|
|
1259
|
-
rootIssueId: collisionRootIssueId({
|
|
1260
|
-
pluginId: dependencyId,
|
|
1261
|
-
platform,
|
|
1262
|
-
scope: dependencyScope,
|
|
1263
|
-
collision,
|
|
1264
|
-
}),
|
|
1265
|
-
rootIssueCode: 'artifactCollision',
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
return {
|
|
1269
|
-
state: dependencyLiveState === 'present' ? 'ready'
|
|
1270
|
-
: dependencyLiveState === 'absent' ? 'absent'
|
|
1271
|
-
: dependencyLiveState === 'partial' ? 'incomplete'
|
|
1272
|
-
: 'error',
|
|
1273
|
-
dependencyScope,
|
|
1274
|
-
...(dependencyLiveState === 'partial'
|
|
1275
|
-
? {
|
|
1276
|
-
rootIssueId: `incompleteInstallation:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1277
|
-
rootIssueCode: 'incompleteInstallation',
|
|
1278
|
-
}
|
|
1279
|
-
: dependencyLiveState === 'error'
|
|
1280
|
-
? {
|
|
1281
|
-
rootIssueId: `diagnostic:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1282
|
-
rootIssueCode: 'diagnostic',
|
|
1283
|
-
}
|
|
1284
|
-
: {}),
|
|
1285
|
-
};
|
|
1286
|
-
}).catch(() => ({
|
|
1287
|
-
state: 'error',
|
|
1288
|
-
dependencyScope,
|
|
1289
|
-
rootIssueId: `diagnostic:${dependencyId}:${platform}:${dependencyScope}`,
|
|
1290
|
-
rootIssueCode: 'diagnostic',
|
|
1291
|
-
}));
|
|
1292
|
-
dependencyInspectionCache.set(inspectionCacheKey, dependencyInspection);
|
|
1293
|
-
}
|
|
1294
|
-
const candidate = await dependencyInspection;
|
|
1518
|
+
const candidate = await inspectDependencyAtScope(dependencyName, dependencyScope);
|
|
1295
1519
|
if (candidate.state === 'ready') {
|
|
1296
1520
|
dependencyResult = candidate;
|
|
1297
1521
|
break;
|
|
@@ -1307,7 +1531,7 @@ async function statusCommandV7InSession(options) {
|
|
|
1307
1531
|
dependencyResult = candidate;
|
|
1308
1532
|
}
|
|
1309
1533
|
}
|
|
1310
|
-
if (dependencyResult.state === 'absent') {
|
|
1534
|
+
if (dependencyResult.state === 'absent' && !dependencyResult.rootIssueId) {
|
|
1311
1535
|
const dependencyScope = dependencyResult.dependencyScope ?? placements[0];
|
|
1312
1536
|
dependencyResult = {
|
|
1313
1537
|
state: 'absent',
|
|
@@ -1322,28 +1546,46 @@ async function statusCommandV7InSession(options) {
|
|
|
1322
1546
|
dependencyPlatform: platform,
|
|
1323
1547
|
dependencyScope,
|
|
1324
1548
|
dependencyProject: dependencyScope === 'project' ? 'current' : null,
|
|
1325
|
-
|
|
1549
|
+
state: dependencyResult.state,
|
|
1550
|
+
...(dependencyResult.rootIssueId
|
|
1551
|
+
? { rootIssueId: dependencyResult.rootIssueId }
|
|
1552
|
+
: {}),
|
|
1553
|
+
...(dependencyResult.rootIssueCode
|
|
1554
|
+
? { rootIssueCode: dependencyResult.rootIssueCode }
|
|
1555
|
+
: {}),
|
|
1326
1556
|
});
|
|
1557
|
+
const issuePluginId = dependencyResult.issuePluginId ?? dependencyId;
|
|
1558
|
+
const issueScope = dependencyResult.issueScope ?? dependencyScope;
|
|
1327
1559
|
if (dependencyResult.state === 'absent') {
|
|
1328
1560
|
dependencyErrors.push(finalizeTargetError({
|
|
1329
1561
|
code: 'dependencyMissing',
|
|
1330
|
-
message: `dependencyMissing: ${
|
|
1331
|
-
dependencyPluginId:
|
|
1562
|
+
message: `dependencyMissing: ${issuePluginId} is absent from its required live placement.`,
|
|
1563
|
+
dependencyPluginId: issuePluginId,
|
|
1332
1564
|
dependencyPlatform: platform,
|
|
1333
|
-
dependencyScope,
|
|
1334
|
-
dependencyProject:
|
|
1565
|
+
dependencyScope: issueScope,
|
|
1566
|
+
dependencyProject: issueScope === 'project' ? 'current' : null,
|
|
1335
1567
|
rootIssueId: dependencyResult.rootIssueId,
|
|
1336
1568
|
rootIssueCode: dependencyResult.rootIssueCode,
|
|
1337
1569
|
}, 'partial'));
|
|
1338
1570
|
}
|
|
1339
1571
|
else if (dependencyResult.state !== 'ready') {
|
|
1572
|
+
const code = dependencyResult.rootIssueCode === 'awCapabilityUnsupported'
|
|
1573
|
+
? 'awCapabilityUnsupported'
|
|
1574
|
+
: dependencyResult.rootIssueCode === 'dependencyGraphInvalid'
|
|
1575
|
+
? 'dependencyGraphInvalid'
|
|
1576
|
+
: 'dependencyNotReady';
|
|
1577
|
+
const message = code === 'awCapabilityUnsupported'
|
|
1578
|
+
? `${code}: ${issuePluginId} requires unsupported AW capabilities: ${(dependencyResult.missingCapabilities ?? []).join(', ')}.`
|
|
1579
|
+
: code === 'dependencyGraphInvalid'
|
|
1580
|
+
? `${code}: dependency cycle includes ${issuePluginId}.`
|
|
1581
|
+
: `${code}: ${issuePluginId} is not ready at its required live placement.`;
|
|
1340
1582
|
dependencyErrors.push(finalizeTargetError({
|
|
1341
|
-
code
|
|
1342
|
-
message
|
|
1343
|
-
dependencyPluginId:
|
|
1583
|
+
code,
|
|
1584
|
+
message,
|
|
1585
|
+
dependencyPluginId: issuePluginId,
|
|
1344
1586
|
dependencyPlatform: platform,
|
|
1345
|
-
dependencyScope,
|
|
1346
|
-
dependencyProject:
|
|
1587
|
+
dependencyScope: issueScope,
|
|
1588
|
+
dependencyProject: issueScope === 'project' ? 'current' : null,
|
|
1347
1589
|
...(dependencyResult.rootIssueId
|
|
1348
1590
|
? { rootIssueId: dependencyResult.rootIssueId }
|
|
1349
1591
|
: {}),
|
|
@@ -1601,7 +1843,21 @@ async function statusCommandV7InSession(options) {
|
|
|
1601
1843
|
scopes,
|
|
1602
1844
|
currentProjectLocationKey,
|
|
1603
1845
|
}));
|
|
1604
|
-
|
|
1846
|
+
const selectorHasLiveOrIntent = ownerInventory.references.some((reference) => (reference.kind === 'namespacedOwner' || reference.kind === 'namespacedMarketplace')
|
|
1847
|
+
&& reference.pluginName === selector.parsed.pluginName
|
|
1848
|
+
&& reference.sourceId !== undefined
|
|
1849
|
+
&& (selector.parsed.sourceId === undefined || reference.sourceId === selector.parsed.sourceId)
|
|
1850
|
+
&& platforms.some((platform) => platform === reference.platform)
|
|
1851
|
+
&& scopes.some((scope) => scope === reference.scope)
|
|
1852
|
+
&& (reference.scope !== 'project' || reference.projectKey === currentProjectKey))
|
|
1853
|
+
|| [userIntent, projectIntent].some((intent, index) => scopes.includes(index === 0 ? 'user' : 'project')
|
|
1854
|
+
&& intent.roots.some((root) => {
|
|
1855
|
+
const parsed = parsePluginId(root.pluginId);
|
|
1856
|
+
return parsed.pluginName === selector.parsed.pluginName
|
|
1857
|
+
&& (selector.parsed.sourceId === undefined || parsed.sourceId === selector.parsed.sourceId)
|
|
1858
|
+
&& root.platforms.some((platform) => platforms.includes(platform));
|
|
1859
|
+
}));
|
|
1860
|
+
if (!selectorHasInventoryError && !selectorHasLiveOrIntent) {
|
|
1605
1861
|
selectorErrors.push({
|
|
1606
1862
|
code: 'pluginNotFound',
|
|
1607
1863
|
message: `Plugin "${selector.raw}" was not found in the selected catalog source set.`,
|
|
@@ -1795,6 +2051,14 @@ async function statusCommandV7InSession(options) {
|
|
|
1795
2051
|
sourceId: reference.sourceId,
|
|
1796
2052
|
pluginName: reference.pluginName,
|
|
1797
2053
|
})));
|
|
2054
|
+
const isDisabledCodexReference = (reference) => {
|
|
2055
|
+
if (reference.kind !== 'namespacedMarketplace' || reference.platform !== 'codex'
|
|
2056
|
+
|| !reference.pluginName || !reference.marketplaceName)
|
|
2057
|
+
return false;
|
|
2058
|
+
const config = reference.scope === 'user' ? userCodexConfig : projectCodexConfig;
|
|
2059
|
+
return config?.snapshot.plugins[`${reference.pluginName}@${reference.marketplaceName}`]?.enabled === false;
|
|
2060
|
+
};
|
|
2061
|
+
unavailableBundleReferences.sort((left, right) => Number(isDisabledCodexReference(right)) - Number(isDisabledCodexReference(left)));
|
|
1798
2062
|
const unavailableBundleTargets = [...new Map(unavailableBundleReferences.flatMap((reference) => {
|
|
1799
2063
|
if (!reference.sourceId || !reference.pluginName
|
|
1800
2064
|
|| (reference.platform !== 'codex' && reference.platform !== 'claude')
|
|
@@ -1804,6 +2068,9 @@ async function statusCommandV7InSession(options) {
|
|
|
1804
2068
|
if (requested.length > 0 && !plannedSelectors.some((selector) => selector.parsed.pluginName === reference.pluginName
|
|
1805
2069
|
&& (selector.parsed.sourceId === undefined || selector.parsed.sourceId === reference.sourceId)))
|
|
1806
2070
|
return [];
|
|
2071
|
+
const disabled = isDisabledCodexReference(reference)
|
|
2072
|
+
&& ![userIntent, projectIntent].some((intent) => intent.roots.some((root) => root.pluginId === pluginId && root.platforms.includes('codex')))
|
|
2073
|
+
&& (desiredTargetStates.get(desiredTargetKey(pluginId, 'codex'))?.scopes.length ?? 0) === 0;
|
|
1807
2074
|
const target = {
|
|
1808
2075
|
pluginName: reference.pluginName,
|
|
1809
2076
|
sourceId: reference.sourceId,
|
|
@@ -1812,9 +2079,9 @@ async function statusCommandV7InSession(options) {
|
|
|
1812
2079
|
platform: reference.platform,
|
|
1813
2080
|
scope: reference.scope,
|
|
1814
2081
|
cwd: path.resolve(options.cwd),
|
|
1815
|
-
state: 'error',
|
|
2082
|
+
state: disabled ? 'absent' : 'error',
|
|
1816
2083
|
components: [],
|
|
1817
|
-
errors: [finalizeTargetError({
|
|
2084
|
+
errors: disabled ? [] : [finalizeTargetError({
|
|
1818
2085
|
code: 'bundleUnavailable',
|
|
1819
2086
|
message: `bundleUnavailable: ${pluginId} has live state but is not present in the current source snapshot.`,
|
|
1820
2087
|
}, 'error')],
|
|
@@ -1894,15 +2161,13 @@ async function statusCommandV7InSession(options) {
|
|
|
1894
2161
|
});
|
|
1895
2162
|
const decorateTarget = async (target, groupedScopes, groupedTargets) => {
|
|
1896
2163
|
const entry = selectedByPluginId.get(target.pluginId);
|
|
1897
|
-
const codex = entry?.codex;
|
|
2164
|
+
const codex = target.platform === 'codex' ? entry?.codex : undefined;
|
|
1898
2165
|
const desiredAll = desiredTargetStates.get(desiredTargetKey(target.pluginId, target.platform))
|
|
1899
2166
|
?? { scopes: [], relation: 'none' };
|
|
1900
|
-
const desired =
|
|
1901
|
-
?
|
|
1902
|
-
:
|
|
1903
|
-
|
|
1904
|
-
relation: desiredAll.relation,
|
|
1905
|
-
};
|
|
2167
|
+
const desired = {
|
|
2168
|
+
scopes: desiredAll.scopes.includes(target.scope) ? [target.scope] : [],
|
|
2169
|
+
relation: desiredAll.relation,
|
|
2170
|
+
};
|
|
1906
2171
|
const sharedNative = codex?.sharedNative ?? target.sharedNative;
|
|
1907
2172
|
const baseActivation = codex?.activation ?? target.activation;
|
|
1908
2173
|
const consumerScope = desired.scopes.includes('project')
|
|
@@ -1925,9 +2190,7 @@ async function statusCommandV7InSession(options) {
|
|
|
1925
2190
|
systemConsumer,
|
|
1926
2191
|
}
|
|
1927
2192
|
: baseActivation;
|
|
1928
|
-
const observationScopes =
|
|
1929
|
-
? [explicitScope]
|
|
1930
|
-
: desired.scopes.length > 0 ? desired.scopes : groupedScopes;
|
|
2193
|
+
const observationScopes = [target.scope];
|
|
1931
2194
|
const scopedTargets = groupedTargets.filter((candidate) => observationScopes.includes(candidate.scope));
|
|
1932
2195
|
const observedTargets = scopedTargets.length > 0 ? scopedTargets : groupedTargets;
|
|
1933
2196
|
const ownershipState = observedTargets.some((candidate) => candidate.state === 'error')
|
|
@@ -1939,6 +2202,8 @@ async function statusCommandV7InSession(options) {
|
|
|
1939
2202
|
: sharedNative?.staleCacheObserved
|
|
1940
2203
|
? 'partial'
|
|
1941
2204
|
: 'absent';
|
|
2205
|
+
const actualScopeEnabled = target.platform === 'codex'
|
|
2206
|
+
&& baseActivation?.[target.scope] === true;
|
|
1942
2207
|
const hasSourceError = observedTargets.some((candidate) => candidate.errors.some((error) => error.code === 'sourceMissing'
|
|
1943
2208
|
|| error.code === 'catalogSourceUnavailable'
|
|
1944
2209
|
|| error.code === 'bundleUnavailable'
|
|
@@ -1947,18 +2212,16 @@ async function statusCommandV7InSession(options) {
|
|
|
1947
2212
|
&& (codex?.extensionKinds.length ?? 0) > 0
|
|
1948
2213
|
&& desired.scopes.includes('user')
|
|
1949
2214
|
&& desired.scopes.includes('project');
|
|
1950
|
-
const
|
|
1951
|
-
const expectedProject = desired.scopes.includes('project');
|
|
2215
|
+
const expectedEnabled = desired.scopes.includes(target.scope);
|
|
1952
2216
|
const activationDrift = target.platform === 'codex'
|
|
1953
2217
|
&& (codexConfigErrors.some((error) => error.scope === 'user' || observationScopes.includes(error.scope))
|
|
1954
2218
|
|| ((codex?.nativeKinds.length ?? 0) > 0
|
|
1955
|
-
&& (
|
|
1956
|
-
||
|
|
1957
|
-
|| (expectedProject && activation?.project !== true)
|
|
1958
|
-
|| (!expectedProject && activation?.project === true))));
|
|
2219
|
+
&& (expectedEnabled && activation?.[target.scope] !== true
|
|
2220
|
+
|| !expectedEnabled && activation?.[target.scope] === true)));
|
|
1959
2221
|
const extensionComponents = observedTargets.flatMap((candidate) => candidate.components).filter((component) => component.key !== 'native'
|
|
1960
2222
|
&& !component.key.startsWith('activation-')
|
|
1961
2223
|
&& !component.key.startsWith('marketplace-'));
|
|
2224
|
+
const hasScopedExtensionResidue = extensionComponents.some((component) => component.state !== 'absent' || component.error !== undefined);
|
|
1962
2225
|
const extensions = target.platform !== 'codex'
|
|
1963
2226
|
|| (codex?.extensionKinds.length ?? 0) === 0
|
|
1964
2227
|
? undefined
|
|
@@ -2014,6 +2277,7 @@ async function statusCommandV7InSession(options) {
|
|
|
2014
2277
|
const baseTargetErrors = observedTargets.flatMap((candidate) => candidate.errors);
|
|
2015
2278
|
const hasBlockingBaseReason = baseTargetErrors.some((reason) => reason.severity === 'blocking');
|
|
2016
2279
|
const capabilityUnsupported = baseTargetErrors.some((reason) => reason.code === 'awCapabilityUnsupported');
|
|
2280
|
+
const dependencyNotReady = (target.dependencyReadiness ?? []).some((dependency) => dependency.state !== 'ready');
|
|
2017
2281
|
const state = hasSourceError && desiredEnabled
|
|
2018
2282
|
? 'blocked'
|
|
2019
2283
|
: consumerBlocking && desiredEnabled
|
|
@@ -2022,30 +2286,40 @@ async function statusCommandV7InSession(options) {
|
|
|
2022
2286
|
? 'blocked'
|
|
2023
2287
|
: sharedNative?.state === 'blocked' || hasBlockingBaseReason || capabilityUnsupported
|
|
2024
2288
|
? 'blocked'
|
|
2025
|
-
:
|
|
2026
|
-
?
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2289
|
+
: sharedNative?.state === 'replaceable' && sharedNative.reason === 'damaged'
|
|
2290
|
+
? 'needsSync'
|
|
2291
|
+
: !desiredEnabled
|
|
2292
|
+
? (target.platform === 'codex'
|
|
2293
|
+
? actualScopeEnabled || hasScopedExtensionResidue
|
|
2294
|
+
: ownershipState !== 'absent')
|
|
2295
|
+
|| desired.relation === 'managed'
|
|
2296
|
+
? 'orphaned'
|
|
2297
|
+
: 'absent'
|
|
2298
|
+
: dependencyMoves.length > 0
|
|
2034
2299
|
? 'needsSync'
|
|
2035
|
-
:
|
|
2300
|
+
: dependencyNotReady
|
|
2036
2301
|
? 'needsSync'
|
|
2037
|
-
:
|
|
2302
|
+
: sharedNative?.state === 'missing'
|
|
2303
|
+
|| sharedNative?.state === 'replaceable'
|
|
2304
|
+
|| sharedNative?.state === 'unobserved'
|
|
2038
2305
|
? 'needsSync'
|
|
2039
|
-
:
|
|
2306
|
+
: activationDrift || extensions?.state === 'drifted'
|
|
2040
2307
|
? 'needsSync'
|
|
2041
|
-
: '
|
|
2308
|
+
: extensions?.state === 'missing'
|
|
2309
|
+
|| (target.platform !== 'codex' && ownershipState === 'absent')
|
|
2310
|
+
? 'needsSync'
|
|
2311
|
+
: ownershipState === 'partial' || ownershipState === 'error'
|
|
2312
|
+
? 'needsSync'
|
|
2313
|
+
: 'ready';
|
|
2042
2314
|
const stableErrors = [
|
|
2043
2315
|
...projectStatusSharedNativeReasons(sharedNative, baseTargetErrors.map((error) => error.code), desiredEnabled),
|
|
2044
2316
|
...(desiredOverlapConflict
|
|
2045
2317
|
? [blocking('codexExtensionScopeConflict', `${target.pluginId} has AW extensions desired at user and project scope.`)]
|
|
2046
2318
|
: []),
|
|
2047
2319
|
...(activationDrift && sharedNative?.state !== 'missing' && sharedNative?.state !== 'unobserved'
|
|
2048
|
-
? [warning('codexActivationDrift', `${target.pluginId} activation differs from desired intent
|
|
2320
|
+
? [warning('codexActivationDrift', `${target.pluginId} activation differs from desired intent: `
|
|
2321
|
+
+ `${target.scope} expected=${expectedEnabled}, `
|
|
2322
|
+
+ `actual=${activation?.[target.scope] ?? 'unset'}.`)]
|
|
2049
2323
|
: []),
|
|
2050
2324
|
...(extensions?.state === 'drifted'
|
|
2051
2325
|
? [warning('codexExtensionDrift', `${target.pluginId} extension projection has drifted.`)]
|
|
@@ -2071,11 +2345,7 @@ async function statusCommandV7InSession(options) {
|
|
|
2071
2345
|
])).values()];
|
|
2072
2346
|
const ownershipComponents = [...new Map(observedTargets.flatMap((candidate) => candidate.components)
|
|
2073
2347
|
.map((component) => [component.key, component])).values()];
|
|
2074
|
-
const representativeScope =
|
|
2075
|
-
?? (desired.scopes.length === 1
|
|
2076
|
-
? desired.scopes[0]
|
|
2077
|
-
: observedTargets.find((candidate) => candidate.state !== 'absent')?.scope
|
|
2078
|
-
?? (groupedScopes.includes('user') ? 'user' : groupedScopes[0] ?? target.scope));
|
|
2348
|
+
const representativeScope = target.scope;
|
|
2079
2349
|
const representativeTarget = observedTargets.find((candidate) => candidate.scope === representativeScope) ?? target;
|
|
2080
2350
|
const components = [
|
|
2081
2351
|
{
|
|
@@ -2122,7 +2392,7 @@ async function statusCommandV7InSession(options) {
|
|
|
2122
2392
|
}]),
|
|
2123
2393
|
...(activation?.systemConsumer ? [{
|
|
2124
2394
|
kind: 'systemConsumer',
|
|
2125
|
-
key:
|
|
2395
|
+
key: `codex:${representativeScope}`,
|
|
2126
2396
|
state: activation.systemConsumer.state,
|
|
2127
2397
|
metadata: {
|
|
2128
2398
|
evidence: activation.evidence,
|
|
@@ -2169,6 +2439,8 @@ async function statusCommandV7InSession(options) {
|
|
|
2169
2439
|
pluginId: target.pluginId,
|
|
2170
2440
|
...(dependencyMoves.length > 0 ? { dependencyMoves } : {}),
|
|
2171
2441
|
...(sharedNative ? { sharedNative } : {}),
|
|
2442
|
+
scope: target.scope,
|
|
2443
|
+
actualEnabled: actualScopeEnabled,
|
|
2172
2444
|
conflictRoots: rootIntentsForTarget({
|
|
2173
2445
|
pluginId: target.pluginId,
|
|
2174
2446
|
platform: target.platform,
|
|
@@ -2177,7 +2449,9 @@ async function statusCommandV7InSession(options) {
|
|
|
2177
2449
|
available,
|
|
2178
2450
|
}),
|
|
2179
2451
|
errorCodes: dedupedErrors.map((error) => error.code),
|
|
2180
|
-
})
|
|
2452
|
+
}).map((action) => action.command === 'manual' || action.scope
|
|
2453
|
+
? action
|
|
2454
|
+
: { ...action, scope: representativeScope }),
|
|
2181
2455
|
};
|
|
2182
2456
|
};
|
|
2183
2457
|
const codexGroups = new Map();
|
|
@@ -2192,18 +2466,84 @@ async function statusCommandV7InSession(options) {
|
|
|
2192
2466
|
codexGroups.set(target.pluginId, group);
|
|
2193
2467
|
}
|
|
2194
2468
|
for (const group of codexGroups.values()) {
|
|
2195
|
-
const
|
|
2196
|
-
|
|
2469
|
+
const decorated = await Promise.all(group.map((target) => decorateTarget(target, [target.scope], [target])));
|
|
2470
|
+
const first = decorated[0];
|
|
2471
|
+
const entry = selectedByPluginId.get(first.pluginId);
|
|
2472
|
+
const desired = desiredTargetStates.get(desiredTargetKey(first.pluginId, 'codex'))
|
|
2473
|
+
?? { scopes: [], relation: 'none' };
|
|
2474
|
+
const returnedScopes = new Set(decorated.map((target) => target.scope));
|
|
2475
|
+
const desiredOverlapConflict = (entry?.codex?.extensionKinds.length ?? 0) > 0
|
|
2476
|
+
&& desired.scopes.includes('user')
|
|
2477
|
+
&& desired.scopes.includes('project')
|
|
2478
|
+
&& returnedScopes.has('user')
|
|
2479
|
+
&& returnedScopes.has('project');
|
|
2480
|
+
const sharedComponents = [...new Map(decorated.flatMap((target) => target.components)
|
|
2481
|
+
.filter((component) => component.kind === 'sharedNative' || component.kind === 'systemConsumer')
|
|
2482
|
+
.map((component) => [`${component.kind}\0${component.key}`, component])).values()];
|
|
2483
|
+
const sharedReason = (reason) => reason.code.startsWith('codexSharedNative') || reason.code === 'staleCacheObserved';
|
|
2484
|
+
const sharedReasons = decorated.flatMap((target) => target.reasons.filter(sharedReason));
|
|
2485
|
+
const placements = decorated.map((target) => ({
|
|
2486
|
+
scope: target.scope,
|
|
2487
|
+
state: target.state,
|
|
2488
|
+
components: target.components.filter((component) => component.kind !== 'sharedNative' && component.kind !== 'systemConsumer'),
|
|
2489
|
+
reasons: target.reasons.filter((reason) => !sharedReason(reason)),
|
|
2490
|
+
recoveryActions: target.recoveryActions,
|
|
2491
|
+
})).sort((left, right) => left.scope.localeCompare(right.scope, 'en'));
|
|
2492
|
+
const reasons = [...new Map([
|
|
2493
|
+
...placements.flatMap((placement) => placement.reasons),
|
|
2494
|
+
...sharedReasons,
|
|
2495
|
+
...(desiredOverlapConflict ? [blocking('codexExtensionScopeConflict', `${first.pluginId} has AW extensions desired at user and project scope.`)] : []),
|
|
2496
|
+
]
|
|
2497
|
+
.map((reason) => [`${reason.code}\0${reason.message}`, reason])).values()];
|
|
2498
|
+
const conflictRecovery = desiredOverlapConflict ? statusRecovery({
|
|
2499
|
+
state: 'blocked',
|
|
2500
|
+
desired,
|
|
2501
|
+
platform: 'codex',
|
|
2502
|
+
pluginId: first.pluginId,
|
|
2503
|
+
conflictRoots: rootIntentsForTarget({
|
|
2504
|
+
pluginId: first.pluginId,
|
|
2505
|
+
platform: 'codex',
|
|
2506
|
+
userIntent,
|
|
2507
|
+
projectIntent,
|
|
2508
|
+
available,
|
|
2509
|
+
}),
|
|
2510
|
+
errorCodes: ['codexExtensionScopeConflict'],
|
|
2511
|
+
}) : [];
|
|
2512
|
+
const recoveryActions = [...new Map((desiredOverlapConflict
|
|
2513
|
+
? conflictRecovery
|
|
2514
|
+
: placements.flatMap((placement) => placement.recoveryActions))
|
|
2515
|
+
.map((action) => [
|
|
2516
|
+
`${action.command}\0${action.scope ?? ''}\0${action.description}`,
|
|
2517
|
+
action,
|
|
2518
|
+
])).values()];
|
|
2519
|
+
const state = desiredOverlapConflict ? 'blocked'
|
|
2520
|
+
: ['blocked', 'needsSync', 'orphaned', 'ready', 'absent']
|
|
2521
|
+
.find((candidate) => placements.some((placement) => placement.state === candidate));
|
|
2522
|
+
projectedTargets.push({
|
|
2523
|
+
pluginName: first.pluginName,
|
|
2524
|
+
sourceId: first.sourceId,
|
|
2525
|
+
sourceIdentity: first.sourceIdentity,
|
|
2526
|
+
pluginId: first.pluginId,
|
|
2527
|
+
...(first.version ? { version: first.version } : {}),
|
|
2528
|
+
platform: 'codex',
|
|
2529
|
+
cwd: first.cwd,
|
|
2530
|
+
state,
|
|
2531
|
+
components: sharedComponents,
|
|
2532
|
+
reasons,
|
|
2533
|
+
recoveryActions,
|
|
2534
|
+
placements,
|
|
2535
|
+
});
|
|
2197
2536
|
}
|
|
2198
|
-
projectedTargets.sort((left, right) => `${left.pluginId}\0${left.platform}
|
|
2199
|
-
const targetRevisions = projectedTargets.
|
|
2537
|
+
projectedTargets.sort((left, right) => `${left.pluginId}\0${left.platform}`.localeCompare(`${right.pluginId}\0${right.platform}`, 'en'));
|
|
2538
|
+
const targetRevisions = projectedTargets.flatMap((target) => {
|
|
2200
2539
|
const entry = selectedByPluginId.get(target.pluginId);
|
|
2201
|
-
|
|
2540
|
+
const placements = target.platform === 'codex' ? target.placements : [target];
|
|
2541
|
+
return placements.map((placement) => ({
|
|
2202
2542
|
pluginId: target.pluginId,
|
|
2203
2543
|
platform: target.platform,
|
|
2204
|
-
scope:
|
|
2205
|
-
revision: projectTargetRevision(entry?.version, entry?.states[target.platform]?.[
|
|
2206
|
-
};
|
|
2544
|
+
scope: placement.scope,
|
|
2545
|
+
revision: projectTargetRevision(entry?.version, entry?.states[target.platform]?.[placement.scope]?.installedVersion),
|
|
2546
|
+
}));
|
|
2207
2547
|
});
|
|
2208
2548
|
const publicConfigIssues = available.flatMap((entry) => (entry.configIssues ?? [])
|
|
2209
2549
|
.filter((issue) => issue.type === 'configLayerUnsupported')
|
|
@@ -2213,7 +2553,7 @@ async function statusCommandV7InSession(options) {
|
|
|
2213
2553
|
...issue,
|
|
2214
2554
|
})));
|
|
2215
2555
|
const payload = {
|
|
2216
|
-
schemaVersion:
|
|
2556
|
+
schemaVersion: 8,
|
|
2217
2557
|
catalog: {
|
|
2218
2558
|
primarySourceId: catalog.primarySourceId,
|
|
2219
2559
|
configDigest: catalog.configDigest,
|
|
@@ -2243,7 +2583,7 @@ async function statusCommandV7InSession(options) {
|
|
|
2243
2583
|
|| payload.targets.some((target) => target.reasons.some((reason) => reason.severity === 'blocking'));
|
|
2244
2584
|
return {
|
|
2245
2585
|
payload,
|
|
2246
|
-
output: options.json ? JSON.stringify(payload) :
|
|
2586
|
+
output: options.json ? JSON.stringify(payload) : renderStatusV8(payload, options.verbose === true),
|
|
2247
2587
|
hasErrors,
|
|
2248
2588
|
targetRevisions,
|
|
2249
2589
|
pluginSummaries: available.map((entry) => {
|
|
@@ -2290,9 +2630,9 @@ async function statusCommandV7InSession(options) {
|
|
|
2290
2630
|
}),
|
|
2291
2631
|
};
|
|
2292
2632
|
}
|
|
2293
|
-
export async function
|
|
2633
|
+
export async function statusCommandV8(options) {
|
|
2294
2634
|
if (options.codexObservationSession) {
|
|
2295
|
-
return
|
|
2635
|
+
return statusCommandV8InSession({
|
|
2296
2636
|
...options,
|
|
2297
2637
|
codexObservationSession: options.codexObservationSession,
|
|
2298
2638
|
});
|
|
@@ -2304,7 +2644,7 @@ export async function statusCommandV7(options) {
|
|
|
2304
2644
|
runner: options.nativeCliRunner ?? getDefaultNativeCliRunner(),
|
|
2305
2645
|
});
|
|
2306
2646
|
try {
|
|
2307
|
-
return await
|
|
2647
|
+
return await statusCommandV8InSession({ ...options, codexObservationSession: session });
|
|
2308
2648
|
}
|
|
2309
2649
|
finally {
|
|
2310
2650
|
await session.dispose();
|