diff --git a/apps/web/tests/expected/plugin-manager/live-enabled.expected.md b/apps/web/tests/expected/plugin-manager/live-enabled.expected.md index 382a8254de..0c785fc80b 100644 --- a/apps/web/tests/expected/plugin-manager/live-enabled.expected.md +++ b/apps/web/tests/expected/plugin-manager/live-enabled.expected.md @@ -8,17 +8,17 @@ - text: "3" - list: - listitem: - - button "查看 experimental-agent-team-profile": experimental-agent-team-profile - - text: 官方 Experimental profile bundle enabling Agent Teams over dsh-base - - switch "启用 experimental-agent-team-profile" + - button "查看 智能体团队(实验性)": 智能体团队(实验性) + - text: 官方 启用智能体团队协作与团队工具。 + - switch "启用 智能体团队(实验性)" - listitem: - - button "查看 experimental-agent-team-web-profile": experimental-agent-team-web-profile - - text: 官方 Experimental Web profile layer for Agent Teams Remote and UI plugins - - switch "启用 experimental-agent-team-web-profile" + - button "查看 智能体团队 Web 界面(实验性)": 智能体团队 Web 界面(实验性) + - text: 官方 在浏览器中查看团队成员、任务看板和成员会话。 + - switch "启用 智能体团队 Web 界面(实验性)" - listitem: - - button "查看 experimental-auto-review": experimental-auto-review - - text: 官方 Per-tool LLM authorization review for the DeepSeek Harness Auto permission preset - - switch "启用 experimental-auto-review" + - button "查看 自动授权审查(实验性)": 自动授权审查(实验性) + - text: 官方 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。 + - switch "启用 自动授权审查(实验性)" - heading "已安装" [level=3] - text: "1" - list: diff --git a/apps/web/tests/expected/plugin-manager/manager.expected.md b/apps/web/tests/expected/plugin-manager/manager.expected.md index fa151f6eb7..808a71b71f 100644 --- a/apps/web/tests/expected/plugin-manager/manager.expected.md +++ b/apps/web/tests/expected/plugin-manager/manager.expected.md @@ -8,17 +8,17 @@ - text: "3" - list: - listitem: - - button "查看 experimental-agent-team-profile": experimental-agent-team-profile - - text: 官方 Experimental profile bundle enabling Agent Teams over dsh-base - - switch "启用 experimental-agent-team-profile" + - button "查看 智能体团队(实验性)": 智能体团队(实验性) + - text: 官方 启用智能体团队协作与团队工具。 + - switch "启用 智能体团队(实验性)" - listitem: - - button "查看 experimental-agent-team-web-profile": experimental-agent-team-web-profile - - text: 官方 Experimental Web profile layer for Agent Teams Remote and UI plugins - - switch "启用 experimental-agent-team-web-profile" + - button "查看 智能体团队 Web 界面(实验性)": 智能体团队 Web 界面(实验性) + - text: 官方 在浏览器中查看团队成员、任务看板和成员会话。 + - switch "启用 智能体团队 Web 界面(实验性)" - listitem: - - button "查看 experimental-auto-review": experimental-auto-review - - text: 官方 Per-tool LLM authorization review for the DeepSeek Harness Auto permission preset - - switch "启用 experimental-auto-review" + - button "查看 自动授权审查(实验性)": 自动授权审查(实验性) + - text: 官方 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。 + - switch "启用 自动授权审查(实验性)" - heading "已安装" [level=3] - text: "1" - list: diff --git a/apps/web/tests/plugin-manager.e2e.ts b/apps/web/tests/plugin-manager.e2e.ts index 58989b599e..b4ff640b0c 100644 --- a/apps/web/tests/plugin-manager.e2e.ts +++ b/apps/web/tests/plugin-manager.e2e.ts @@ -88,6 +88,44 @@ describe('web e2e: plugin manager', () => { expect(tripwire.pageErrors).toEqual([]) }, 60_000) + it('updates built-in names and descriptions when the UI language changes', async () => { + onTestFailed(() => saveFailureShot(page, 'web-e2e-plugin-manager-locale')) + const panel = await openPluginsPanel() + await panel.getByRole('button', { name: '查看 智能体团队(实验性)' }).click() + const packageName = panel.locator('[data-plugin-name]') + expect(await packageName.textContent()).toBe('@deepseek-ai/dsh-experimental-agent-team-profile') + expect(await panel.getByText('启用智能体团队协作与团队工具。').count()).toBe(1) + try { + await page.getByRole('button', { name: '设置', exact: true }).click() + await page.getByRole('dialog', { name: '设置' }).getByRole('button', { name: '中文' }).click() + await page.getByRole('menuitem', { name: 'English' }).click() + await page.getByRole('dialog', { name: 'Settings' }).waitFor() + await page.keyboard.press('Escape') + await panel.getByRole('heading', { name: 'Experimental Agent Teams', exact: true }).waitFor() + expect(await packageName.textContent()).toBe('@deepseek-ai/dsh-experimental-agent-team-profile') + expect(await panel.getByText('Enable agent team collaboration and team tools.').count()).toBe(1) + await panel.getByRole('button', { name: 'Back to plugins' }).click() + for (const title of ['Experimental Agent Teams', 'Experimental Agent Teams Web UI', 'Experimental Auto Authorization Review']) { + await panel.getByRole('button', { name: `View ${title}`, exact: true }).waitFor() + expect(await panel.getByRole('switch', { name: `Enable ${title}`, exact: true }).count()).toBe(1) + } + expect(await panel.getByText('View team members, the task board, and teammate sessions in the browser.').count()).toBe(1) + expect(await panel.getByText('Add an Auto review permission mode that uses the model to assess authorization before each tool call.').count()).toBe(1) + } finally { + if (await page.locator('html').getAttribute('lang') === 'en') { + if (await page.getByRole('dialog', { name: 'Settings' }).count() === 0) { + await page.getByRole('button', { name: 'Settings', exact: true }).click() + } + await page.getByRole('dialog', { name: 'Settings' }).getByRole('button', { name: 'English' }).click() + await page.getByRole('menuitem', { name: '中文' }).click() + await page.getByRole('dialog', { name: '设置' }).waitFor() + } + await closeSettings() + } + await panel.getByRole('button', { name: '查看 智能体团队(实验性)', exact: true }).waitFor() + expect(tripwire.pageErrors).toEqual([]) + }, 60_000) + it('checks a spec before installing it and words what the check refused', async () => { onTestFailed(() => saveFailureShot(page, 'web-e2e-plugin-manager-install')) const panel = await openPluginsPanel() diff --git a/packages/client/ui-plugin-manager/README.i18n.yaml b/packages/client/ui-plugin-manager/README.i18n.yaml index 507f807683..89f69b21a5 100644 --- a/packages/client/ui-plugin-manager/README.i18n.yaml +++ b/packages/client/ui-plugin-manager/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-plugin-manager/README.md -README.md: 313ebf7df21ea73c0c20b1c84cac6767c2079037 -README.zh.md: 81b15b459d8bea4721ed2f84607b1c737101b3b8 +README.md: 1a3ae0f0b5e6cfca4fdbb272f84ca4b641933ba6 +README.zh.md: 0d4d1aeb98fecb24a6bc3e9d0ac84389b0fe939e diff --git a/packages/client/ui-plugin-manager/README.md b/packages/client/ui-plugin-manager/README.md index 313ebf7df2..1a3ae0f0b5 100644 --- a/packages/client/ui-plugin-manager/README.md +++ b/packages/client/ui-plugin-manager/README.md @@ -27,6 +27,8 @@ Use the **Plugins** entry in the Web sidebar to manage the profile's installed b Select **Plugins** in the sidebar. The page reads the inventory and the bundles through `api-remotes` when first opened; a Host without a managed profile shows the page as unavailable. **Built in** comes first and lists the bundles the installation ships for switching on, each tagged official, off until switched on and without an uninstall; **Installed** lists the bundles the profile holds. Cards are listed by name, so switching a bundle on or off does not move its card. A dependency without a bundle patch is not a plugin and is not listed unless the profile selects it, in which case it carries a problem tag. Global configuration remains in the Settings **Plugins** section. +The built-in Agent Teams, Agent Teams Web UI, and Auto Authorization Review bundles have localized names and descriptions that follow the UI language. Their detail pages retain the full npm package name; other packages display their short package name and original description. + ### Installing a bundle **Add plugin** takes a package name with an optional version, a Git address, a tarball, or an absolute local path; the dialog says a package name is what follows `dsh plugin add` in a README. **Not sure what to enter?** under the field opens a guide that shows the three common forms with an example each; **Use example** drops one into the field. **Install** first asks the Host to read what the spec names (`pluginManager.inspect`): a name the list already shows, a name the registry does not have, a path without a package, a package without a bundle patch, or a spec pnpm would refuse comes back under the field as one sentence, with the spec kept for editing. An accepted spec opens the installing screen, which shows the package's name, one-liner, and version as the Host read them and folds pnpm's command and output behind **Show install details**. A finished install offers **Enable now**, which switches the new bundle on, closes the dialog, and scrolls the list to it; closing instead leaves it installed and off. A failed install says what went wrong in one line — the registry or network could not be reached, the package was not found, the disk is full, the profile is not writable, pnpm blocked a build script — with pnpm's output behind the details and **Retry** at hand; the Host has already put the profile files back. When pnpm blocked a dependency's install scripts, the failed screen lists the packages whose scripts wait for permission and offers **Allow these scripts and retry** in place of **Retry**; the Host saves the permission in the profile's `pnpm-workspace.yaml`, which a failed run leaves as pnpm wrote it, then runs pnpm again, and the installed screen names what was allowed. A successful installation does not certify that a module can activate. diff --git a/packages/client/ui-plugin-manager/README.zh.md b/packages/client/ui-plugin-manager/README.zh.md index 81b15b459d..0d4d1aeb98 100644 --- a/packages/client/ui-plugin-manager/README.zh.md +++ b/packages/client/ui-plugin-manager/README.zh.md @@ -27,6 +27,8 @@ kind: "package-reference" 在侧栏选择**插件**。页面首次打开时通过 `api-remotes` 读取清单与组合包;没有受管 profile 的 Host 上页面显示为不可用。**内置**排在前面,列出安装随附、供开启的组合包,每个带官方标签,开启前保持关闭,且没有卸载;**已安装**列出 profile 持有的组合包。卡片按名称排序,启停组合包不会挪动它的卡片。没有组合包 patch 的依赖不是插件,除非 profile 选中了它才会带异常标签列出。全局配置仍在设置的**插件**分区中编辑。 +内置的 Agent Teams、Agent Teams Web UI 和 Auto Authorization Review 组合包使用随界面语言切换的本地化名称和描述。详情页保留完整 npm 包名;其他包显示简写包名和原始描述。 + ### 安装一个组合包 **添加插件**接受包名(可带版本)、Git 地址、压缩包或本地绝对路径;对话框说明包名就是 README 里 `dsh plugin add` 后面的那一段。输入框下方的**不知道该填什么?**展开一段引导,给出三种常见形式各一个示例;**填入示例**把示例填进输入框。**安装**先让 Host 读出 spec 指向什么(`pluginManager.inspect`):列表中已有的名字、注册表没有的名字、没有包的路径、没有组合包 patch 的包,或 pnpm 会拒绝的 spec,都以一句话回到输入框下方,spec 保留可继续编辑。通过检查的 spec 打开安装中界面,展示 Host 读到的包名、一句话简介和版本,pnpm 的命令与输出折叠在**查看安装详情**之后。安装完成后提供**立即启用**:启用新组合包、关闭对话框并把列表滚动到它;直接关闭则让它保持已安装但关闭。安装失败时用一行话说明原因——注册表或网络不可达、包不存在、磁盘已满、profile 不可写、pnpm 拦下了构建脚本——pnpm 输出在详情里,**重试**就在手边;Host 已经把 profile 文件放回原样。pnpm 拦下依赖的安装脚本时,失败界面列出等待允许的包,并以**允许这些脚本并重试**取代**重试**;Host 把授权写进 profile 的 `pnpm-workspace.yaml`(失败的运行保留 pnpm 写入的这个文件)再运行 pnpm,安装完成界面会说明允许了哪些脚本。安装成功不代表模块一定能够激活。 diff --git a/packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx b/packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx index 7fc7facb79..73ed864613 100644 --- a/packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx +++ b/packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx @@ -21,7 +21,7 @@ import { type ConfirmState, type InstallInputError, type InstallState, type InstallSubject, type PackageRow, type PackageView, type PluginManagerFace, } from './manager-store.ts' -import { managementText, noticeText, shortName, type Translate } from './presentation.ts' +import { managementText, noticeText, packageText, type Translate } from './presentation.ts' import css from './PluginManagerPage.module.css' /** Full component props assembled by the main slot renderer. */ @@ -213,7 +213,7 @@ function PackageCard({ pkg, t, busy, highlighted, onOpen, onSetEnabled }: { readonly onOpen: () => void readonly onSetEnabled: (enabled: boolean) => void }): ReactNode { - const title = shortName(pkg.name) + const { title, description } = packageText(pkg, t) const status = packageStatus(pkg) return (
{pkg.name}
{pkg.description ?? t('noDescription')}
+{description ?? t('noDescription')}
{t('reasonLabel')}: {managementText(pkg.error, t)}
} {pkg.readOnlyReason === undefined ? null :{managementText({ code: pkg.readOnlyReason }, t)}
} @@ -635,7 +635,7 @@ function ConfirmDialog({ confirm, t, onConfirm, onCancel }: { readonly onConfirm: () => void readonly onCancel: () => void }): ReactNode { - const name = shortName(confirm.packageName) + const { title: name } = packageText({ name: confirm.packageName }, t) return (