Add optional Nanobot plugin controls (#4396)

* feat: add optional nanobot features

* test: update azure install hint expectation

* fix: validate optional feature extras

maintainer edit: verify requested dependency extras before treating optional features as installed, propagate restart state from feature enablement, and align docs with the new plugins enable command.

* fix: bound optional feature installs

maintainer edit: make optional feature installs time out as a normal install failure instead of leaving the WebUI or CLI action waiting indefinitely.

* feat: slim optional channel dependencies

* fix: log optional install commands

* fix(webui): gate remote feature installs

* docs: clarify webhook plugin example

* fix(webui): harden optional feature installs

* fix: install optional deps without package fallback

* fix(cli): refine plugin feature controls

* fix(webui): count enabled nanobot features

* fix(webui): allow slow feature install routes

* fix(webui): allow disabling websocket channel

* fix(plugins): simplify optional feature controls

* fix(webui): polish apps catalog states

* fix(webui): confirm nanobot support installs

* fix(webui): polish nanobot install dialog

* fix(webui): suppress empty websocket handshakes

* fix(webui): clarify apps plugin summary

* fix(webui): localize workspace access copy

* fix(plugins): polish optional feature controls (#4691)

---------

Co-authored-by: Xubin Ren <52506698+Re-bin@users.noreply.github.com>
This commit is contained in:
chengyongru
2026-07-03 18:17:52 +08:00
committed by GitHub
co-authored by Xubin Ren
parent 00cc0da530
commit 5283ceae85
61 changed files with 3061 additions and 258 deletions
+37
View File
@@ -8,6 +8,7 @@ import {
fetchCliApps,
fetchInstalledCliApps,
fetchMcpPresets,
fetchNanobotFeatures,
fetchProviderModels,
fetchSessionAutomations,
fetchSettingsUsage,
@@ -21,6 +22,8 @@ import {
listSlashCommands,
loginProviderOAuth,
logoutProviderOAuth,
disableNanobotFeature,
enableNanobotFeature,
runAutomationAction,
runCliAppAction,
runMcpPresetAction,
@@ -441,6 +444,40 @@ describe("webui API helpers", () => {
);
});
it("reads and toggles nanobot optional features", async () => {
vi.mocked(fetch).mockResolvedValueOnce({
ok: true,
json: async () => ({
features: [],
enabled_count: 0,
}),
} as Response);
await expect(fetchNanobotFeatures("tok")).resolves.toMatchObject({ features: [] });
expect(fetch).toHaveBeenCalledWith(
"/api/settings/nanobot-features",
expect.objectContaining({
headers: { Authorization: "Bearer tok" },
}),
);
await enableNanobotFeature("tok", "matrix");
expect(fetch).toHaveBeenCalledWith(
"/api/settings/nanobot-features/enable?name=matrix",
expect.objectContaining({
headers: { Authorization: "Bearer tok" },
}),
);
await disableNanobotFeature("tok", "matrix");
expect(fetch).toHaveBeenCalledWith(
"/api/settings/nanobot-features/disable?name=matrix",
expect.objectContaining({
headers: { Authorization: "Bearer tok" },
}),
);
});
it("reads MCP presets and serializes actions", async () => {
vi.mocked(fetch).mockResolvedValueOnce({
ok: true,