fix: complete marketplace client remediation

This commit is contained in:
2026-08-28 23:14:41 +08:00
parent 2c3baf6dff
commit 11d0af0166
18 changed files with 1007 additions and 90 deletions

View File

@@ -139,6 +139,72 @@ async function resolve(effective: EffectivePluginResolver, assignedSkillIds = ['
}
describe('effective plugin resolver', () => {
it('rejects Marketplace Skill ID collisions with core and other packages without changing project assignment', async () => {
const coreCollision: CodingPluginDefinition = {
...skillOnlyDefinition,
id: 'makelore.core-collision',
releaseId: 'core-collision-1',
provenance: { source: 'marketplace', packageRoot: 'C:/packages/core-collision-1' },
skills: [{ id: 'agent-browser', entryPath: 'skills/agent-browser/SKILL.md', grants: [] }],
};
const packageA: CodingPluginDefinition = {
...skillOnlyDefinition,
id: 'makelore.collision-a',
releaseId: 'collision-a-1',
provenance: { source: 'marketplace', packageRoot: 'C:/packages/collision-a-1' },
skills: [{ id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', grants: [] }],
};
const packageB: CodingPluginDefinition = {
...skillOnlyDefinition,
id: 'makelore.collision-b',
releaseId: 'collision-b-1',
provenance: { source: 'marketplace', packageRoot: 'C:/packages/collision-b-1' },
skills: [{ id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', grants: [] }],
};
const definitions = [coreCollision, packageA, packageB];
const result = await createEffectivePluginResolver({
definitions,
getAccountBinding: () => binding,
getLibrary: vi.fn(async () => ({
items: definitions.map((definition) => ({
pluginId: definition.id,
title: definition.displayName,
summary: definition.description,
category: 'tools',
acquisition: 'free' as const,
acquisitionMode: 'user_acquired' as const,
catalogStatus: 'active' as const,
runtimeStatus: 'enabled' as const,
acquiredAt: null,
removedAt: null,
stableVersion: definition.version,
betaVersion: null,
})),
total: definitions.length,
stale: false,
fetchedAt: 1,
})),
getInstalled: vi.fn(async (pluginId: string) => {
const definition = definitions.find((candidate) => candidate.id === pluginId);
return definition ? installed(definition) : null;
}),
getEnabledPluginIds: vi.fn(async () => definitions.map(({ id }) => id)),
}).resolve({
projectId: 'project-a',
projectPath: 'C:/project-a',
assignedSkillIds: ['agent-browser', 'shared-skill'],
role: 'parent',
});
expect(result.effectiveSkillIds).toEqual(['agent-browser', 'shared-skill']);
expect(result.skillEntries).toEqual([
{ id: 'agent-browser', entryPath: 'agent-browser/SKILL.md' },
{ id: 'shared-skill', entryPath: 'skills/shared/SKILL.md', packageRoot: 'C:/packages/collision-a-1' },
]);
expect(result.pluginReleaseIds).toEqual(['collision-a-1']);
expect(result.unavailableReasons).toEqual([]);
});
it('requires current Library, installed Release, project selection, and assignment', async () => {
const effective = resolver();
await expect(resolve(effective)).resolves.toMatchObject({