feat(plugin-inventory): flat plugin list with a default-open toggle guard

The plugin-list tab rendered one flat list of every Loader entry — no separate
"system plugins" section — with each card showing its real enabled state and an
enable/disable button (a required plugin shows only a read-only note). The
enable/disable guard flipped from default-protect to default-open: only the
small REQUIRED_PLUGINS core (entry tree, Remote RPC spine, session/agent spines)
is protected from being disabled, so the shipped plugins are actually
toggleable. Tests, host READMEs, and the enable-disable agent note track the new
grouping and guard.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pine
2026-08-14 17:38:38 +08:00
parent cbec8cd6a9
commit a8ec40ad34
10 changed files with 85 additions and 97 deletions
@@ -78,6 +78,35 @@ describe('PluginInventorySettingsTab', () => {
expect(screen.queryByText(en.unobserved)).toBeNull()
})
it('renders every plugin in one flat list, toggling by state and guarding required plugins', async () => {
const grouped = {
entries: [
{ entryId: 'recog', moduleName: '@deepseek-ai/dsh-image-recognition', enabled: true, protected: false, fiberPhase: 'active' },
{ entryId: 'recog-http', moduleName: '@deepseek-ai/dsh-image-recognition-http', enabled: false, protected: false, fiberPhase: null },
{ entryId: 'hmr', moduleName: '@deepseek-ai/cordis-plugin-hmr', enabled: true, protected: true, fiberPhase: 'active' },
],
} as unknown as Snapshot
render(<PluginInventorySettingsTab {...props(async () => grouped)} />)
// No separate system section: all three entries share one flat list.
expect(screen.queryByText('System plugins')).toBeNull()
expect((await screen.findByRole('searchbox', { name: en.search })).getAttribute('aria-label')).toBeTruthy()
expect(screen.getAllByRole('listitem')).toHaveLength(3)
// A toggleable enabled plugin carries a disable button.
fireEvent.click(screen.getByRole('button', { name: 'image-recognition, Mounted, Enabled' }))
expect(screen.getByRole('button', { name: en.disable })).toBeTruthy()
// A toggleable disabled plugin carries an enable button.
fireEvent.click(screen.getByRole('button', { name: 'image-recognition-http, Disabled' }))
expect(screen.getByRole('button', { name: en.enable })).toBeTruthy()
// A required plugin shows the required note, never a toggle.
fireEvent.click(screen.getByRole('button', { name: 'hmr, Mounted, Enabled' }))
expect(screen.getByText(en.required)).toBeTruthy()
expect(screen.queryByRole('button', { name: en.disable })).toBeNull()
})
it('filters by module name or Loader entry id', async () => {
render(<PluginInventorySettingsTab {...props(async () => SNAPSHOT)} />)
const search = await screen.findByRole('searchbox', { name: en.search })