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
+23 -1
View File
@@ -64,6 +64,18 @@ const LOCALIZED_SETTINGS_COPY_KEYS = [
"settings.sections.webuiSafety",
"settings.sections.capabilities",
"settings.sections.apps",
"settings.apps.description",
"settings.apps.filterPlugins",
"settings.apps.caption",
"settings.apps.restartRequired",
"settings.nanobotFeatures.disable",
"settings.nanobotFeatures.ready",
"settings.nanobotFeatures.missingDependency",
"settings.nanobotFeatures.installConfirmTitle",
"settings.nanobotFeatures.installConfirmDescription",
"settings.nanobotFeatures.installConfirmAction",
"settings.nanobotFeatures.channelDisabled",
"settings.nanobotFeatures.notEnabled",
"settings.sections.about",
"settings.rows.theme",
"settings.rows.language",
@@ -105,6 +117,16 @@ const LOCALIZED_SETTINGS_COPY_KEYS = [
"settings.about.upToDate",
"settings.about.updateAvailable",
];
const LOCALIZED_WORKSPACE_COPY_KEYS = [
"thread.composer.workspace.accessAria",
"thread.composer.workspace.default",
"thread.composer.workspace.full",
"errors.workspaceScopeRejected.title",
"errors.workspaceScopeRejected.body",
"workspace.dialog.defaultProject",
"workspace.dialog.usePath",
"workspace.dialog.absolutePathRequired",
];
function isRecord(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === "object" && !Array.isArray(value);
}
@@ -271,7 +293,7 @@ describe("webui i18n", () => {
for (const [locale, resource] of Object.entries(resources)) {
if (locale === "en") continue;
const current = flattenResource(resource.common);
const leaked = LOCALIZED_SETTINGS_COPY_KEYS.filter(
const leaked = [...LOCALIZED_SETTINGS_COPY_KEYS, ...LOCALIZED_WORKSPACE_COPY_KEYS].filter(
(key) => current.get(key) === english.get(key),
);