fix(plugins): make official plugins project-wide

This commit is contained in:
2026-09-05 13:19:13 +08:00
parent 5e0a091955
commit e0de7aa28c
6 changed files with 121 additions and 17 deletions

View File

@@ -7,6 +7,8 @@ import {
} from '../../electron/coding-plugins/effective-resolver';
import {
DATA_SERVICE_PLUGIN_DEFINITION,
GAME_RESOURCE_BUNDLED_RELEASE_ID,
GAME_RESOURCE_PLUGIN_ID,
PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
PROJECT_SCAFFOLD_PLUGIN_ID,
type CodingPluginDefinition,
@@ -450,11 +452,11 @@ describe('effective plugin resolver', () => {
expect(refresh).not.toHaveBeenCalled();
});
it('materializes an acquired code-owned bundled hosted Plugin without Package Store bytes', async () => {
it('materializes an acquired code-owned bundled hosted Plugin without Package Store bytes or Agent assignment', async () => {
const bundled: CodingPluginDefinition = {
...serverDefinition,
id: 'makelore.game-resource',
releaseId: '00000000-0000-4000-8000-000000000105',
id: GAME_RESOURCE_PLUGIN_ID,
releaseId: GAME_RESOURCE_BUNDLED_RELEASE_ID,
provenance: { source: 'bundled', packageRoot: 'game-resource' },
skills: [{
id: 'game-resource',
@@ -485,7 +487,7 @@ describe('effective plugin resolver', () => {
await expect(effective.resolve({
projectId: 'project-a',
projectPath: 'C:/project-a',
assignedSkillIds: ['game-resource'],
assignedSkillIds: [],
role: 'parent',
})).resolves.toMatchObject({
pluginReleaseIds: [bundled.releaseId],
@@ -504,6 +506,38 @@ describe('effective plugin resolver', () => {
expect(getInstalledRelease).not.toHaveBeenCalled();
});
it('materializes the system-included Data Service after project enablement without Agent assignment', async () => {
const policy = currentPolicy();
policy.catalog.plugins[0] = {
...policy.catalog.plugins[0],
plugin_id: DATA_SERVICE_PLUGIN_DEFINITION.id,
capabilities: DATA_SERVICE_PLUGIN_DEFINITION.operations.map(({ capabilityId, operation }) => ({
capability_id: capabilityId,
operations: [{
operation,
billing: { mode: 'included' as const, entitlement_scope: null, notice: 'Included' },
}],
})),
};
const effective = createEffectivePluginResolver({
definitions: [DATA_SERVICE_PLUGIN_DEFINITION],
getAccountBinding: () => binding,
getEnabledPluginIds: vi.fn(async () => [DATA_SERVICE_PLUGIN_DEFINITION.id]),
policyClient: { getState: () => policy, refresh: vi.fn() },
});
await expect(effective.resolve({
projectId: 'project-a',
projectPath: 'C:/project-a',
assignedSkillIds: [],
role: 'parent',
})).resolves.toMatchObject({
effectiveSkillIds: ['data-service'],
skillEntries: [{ id: 'data-service', entryPath: 'skills/data-service/SKILL.md' }],
unavailableReasons: [],
});
});
it('materializes the project-wide Scaffold Skill after project enablement without Agent assignment', async () => {
const scaffoldLibrary = {
...library(),

View File

@@ -257,7 +257,7 @@ describe('buildPluginWorkspaceProjection', () => {
channel: 'stable',
},
},
})).toEqual(['disable_project', 'open_agent_assignment', 'open_settings']);
})).toEqual(['disable_project', 'open_settings']);
});
it('keeps bundled and downloadable official command sets inside their delivery boundaries', () => {
@@ -298,9 +298,7 @@ describe('buildPluginWorkspaceProjection', () => {
library: { ...library, items: [bundledLibrary] },
marketplaceInstallations: {},
project: bundledProject,
})).toEqual(pluginId === 'makelore.project-scaffold'
? ['remove_from_library', 'enable_project']
: ['remove_from_library', 'enable_project', 'open_agent_assignment']);
})).toEqual(['remove_from_library', 'enable_project']);
}
expect(commandKinds({ marketplaceInstallations: {} })).toEqual([

View File

@@ -89,7 +89,6 @@ const officialItem: PluginWorkspaceItem = {
deviceReason: null,
commands: [
{ kind: 'disable_project', projectId: 'project-a', pluginId: 'makelore.data-service' },
{ kind: 'open_agent_assignment', projectId: 'project-a', pluginId: 'makelore.data-service' },
{ kind: 'open_settings', projectId: 'project-a', pluginId: 'makelore.data-service' },
],
official: {
@@ -433,7 +432,7 @@ describe('PluginsView', () => {
expect(screen.getByRole('heading', { name: '本机全局生效' })).toBeVisible();
expect(screen.getByText('本机全局已启用')).toBeVisible();
expect(screen.getByText('随 MakeLore 提供')).toBeVisible();
expect(screen.getByText('已分配 1 位伙伴')).toBeVisible();
expect(screen.getByText('随项目启用')).toBeVisible();
expect(screen.getAllByRole('alert')).toHaveLength(2);
expect(screen.queryByRole('textbox', { name: /包|路径|source/i })).not.toBeInTheDocument();
expect(screen.getByText(/通过对话安装/)).toBeVisible();
@@ -501,7 +500,10 @@ describe('PluginsView', () => {
expect(dialog).toHaveTextContent('读取项目名称');
expect(dialog).toHaveTextContent('data-service.documents');
expect(dialog).toHaveTextContent('按平台包含');
expect(dialog).toHaveTextContent('小明');
expect(dialog).toHaveTextContent('生效范围');
expect(dialog).toHaveTextContent('无需单独分配');
expect(dialog).not.toHaveTextContent('小明');
expect(screen.queryByRole('button', { name: '分配开发数据服务给伙伴' })).not.toBeInTheDocument();
expect(dialog).toHaveTextContent('插件专属设置');
expect(dialog).toHaveTextContent('创建开发数据空间');
@@ -727,9 +729,9 @@ describe('PluginsView', () => {
const view = props(officialItem);
render(<MemoryRouter><PluginsView {...view} /></MemoryRouter>);
fireEvent.click(screen.getByRole('button', { name: '分配开发数据服务给伙伴' }));
fireEvent.click(screen.getByRole('button', { name: '查看开发数据服务插件设置' }));
expect(view.onCommand).toHaveBeenCalledWith({
kind: 'open_agent_assignment', projectId: 'project-a', pluginId: 'makelore.data-service',
kind: 'open_settings', projectId: 'project-a', pluginId: 'makelore.data-service',
});
fireEvent.click(screen.getByRole('button', { name: '关闭插件详情' }));
expect(view.onSelect).toHaveBeenCalledWith(null);