适用范围:
apps/web/src/components/home/panels/**apps/web/src/features/home/host/**- Agent 控制台首页抽屉中的
overview / files / processes / services / startup / terminal
这份文档是“新增首页抽屉插件怎么做”的实操指南。
如果你是第一次接这个模块,建议先读:
docs/frontend-home-panel-plugin-guide.md
然后回到本文,直接按步骤接新插件。
当前首页抽屉面板分两类:
适合:
overviewfiles- 或者未来不需要通用插件安装 / 卸载 / 依赖修复 / 配置管理 UI 的面板
特征:
- 主要是自己的业务 UI
- 管理区如果有,也通常是自己提供
manageComponent - 不复用 shared 的 managed plugin 管理面板
参考实现:
apps/web/src/components/home/panels/overview/plugin.tsapps/web/src/components/home/panels/files/plugin.ts
适合:
processesservicesstartupterminal- 或者未来任何需要“插件安装、卸载、依赖检查、配置、日志”这套通用管理能力的面板
特征:
- 业务 body 是自己的
- 管理区走 shared 的 managed helper
- 插件可用性和头部 meta 默认从自己的
manageBindings投影
参考实现:
apps/web/src/components/home/panels/processes/plugin.tsapps/web/src/components/home/panels/services/plugin.tsapps/web/src/components/home/panels/startup/plugin.tsapps/web/src/components/home/panels/terminal/plugin.ts
按当前主链,新增一个插件应该尽量只做两件事:
- 在自己的插件目录里实现
- 在
apps/web/src/features/home/host/agentDrawerRegistry.ts注册一次
默认不要改这些文件:
apps/web/src/composables/useHomePanelModules.tsapps/web/src/composables/useHomeViewDrawerSetup.tsapps/web/src/features/home/host/homePanelRuntimeSetup.tsapps/web/src/features/home/host/homePanelRuntimeLifecycleSetup.tsapps/web/src/features/home/host/homePanelRuntimeBindingSetup.tsapps/web/src/features/home/shared/agentDrawerBindings.tsapps/web/src/features/home/host/AgentDrawerHost.vueapps/web/src/features/home/host/AgentDrawerBodyContent.vueapps/web/src/features/home/host/AgentDrawerManageContent.vue
只有在下面这些情况,才考虑动宿主:
- 你真的需要新的通用 runtime context 能力
- 你发现 registry 无法表达该插件的生命周期或日志源
- 你明确是在做 shared 抽象升级,而不是在补单插件特例
当前仓库真实落点仍在:
apps/web/src/components/home/panels/<panel>/
新增一个插件,优先按下面的最小模板建:
apps/web/src/components/home/panels/<panel>/
plugin.ts
useXxxModule.ts
XxxPanelEntry.vue
XxxManagePanel.vue # 只有 non-managed 且需要自定义管理区时才需要
如果该面板更复杂,再继续拆:
components/types.tsbindings.ts
当前不要为了“看起来更规范”先去动整个目录迁移。先接入成功,再谈归位。
先有自己的业务模块,负责状态和动作。
典型形式:
export function useXxxModule(
api: ConsoleApiClient,
selectedAgent: Readonly<{ value: Agent | null }>,
) {
return {
// refs
// actions
};
}核心思路:
runtime.createModule负责创建模块runtime.createBodyBindings负责把模块投影成 body 需要的数据- 如果有管理区,再补
runtime.createManageBindings getBodyProps / getManageProps只做宿主 bindings 到组件 props 的适配
最小骨架:
import XxxPanelEntry from "./XxxPanelEntry.vue";
import type { HomePanelRuntimePlugin } from "../../../../features/home/host/contracts";
import { useXxxModule } from "./useXxxModule";
type XxxRuntimeModule = {
xxxModule: ReturnType<typeof useXxxModule>;
};
type XxxBodyBindings = ReturnType<typeof createXxxBodyBindings>;
type XxxManageBindings = ReturnType<typeof createXxxManageBindings>;
export const xxxPlugin: HomePanelRuntimePlugin<
XxxRuntimeModule,
XxxBodyBindings,
XxxManageBindings,
never,
XxxHostBindings,
"xxx"
> = {
mode: "xxx",
meta: {
menuLabel: "你的菜单名",
titleSuffix: "你的标题后缀",
manageTitle: "你的管理区标题",
},
bodyComponent: XxxPanelEntry,
manageComponent: XxxManagePanel,
getBodyProps: (bindings) => ({
panel: bindings.panelBindings.xxx,
}),
getManageProps: (bindings) => ({
manage: bindings.manageBindings.xxx,
logs: bindings.logs,
}),
runtime: {
createModule: ({ api, selectedAgent }) => ({
xxxModule: useXxxModule(api, selectedAgent),
}),
createBodyBindings: (module, runtimeContext) =>
createXxxBodyBindings(module.xxxModule, runtimeContext),
createManageBindings: (module, runtimeContext) =>
createXxxManageBindings(module.xxxModule, runtimeContext),
loadOnOpen: (module) => {
void module.xxxModule.load();
},
},
};那就:
TManageBindings传never- 不实现
runtime.createManageBindings getManageProps可以只返回logs
参考:
apps/web/src/components/home/panels/files/plugin.ts
可以实现:
runtime.createPluginMetagetPluginMeta
但注意:
- 这已经不是宿主默认 bindings 主链
- 只有确实需要抽屉头部状态展示时再加
- 不要为了“也许以后有用”先把它塞回宿主常驻绑定
参考消费点:
apps/web/src/features/home/host/AgentDrawerHost.vue
managed panel 也一样先写业务模块,比如:
- 列表
- 详情
- 动作
- 本地插件状态刷新
这个 helper 负责把插件安装、依赖、配置等状态整理成统一结构。
参考:
apps/web/src/composables/useHomeManagedPluginBindings.ts
典型用法:
export function createXxxManagedBindings(module: ReturnType<typeof useXxxModule>) {
return createHomeManagedPluginBindings({
pluginId: "your_plugin_id",
fallbackName: "your_plugin_id",
fallbackDescription: "插件说明",
pluginAvailable: module.pluginAvailable,
pluginInstalled: module.pluginInstalled,
pluginVersion: module.pluginVersion,
pluginAvailableVersion: module.pluginAvailableVersion,
dependencyStatus: module.dependencyStatus,
dependencyHealthy: module.dependencyHealthy,
dependencyError: module.dependencyError,
declaredDependencies: module.declaredDependencies,
manifest: module.pluginManifest,
pluginConfigLoading: module.pluginConfigLoading,
pluginConfigSaving: module.pluginConfigSaving,
pluginConfigLoaded: module.pluginConfigLoaded,
pluginConfig: module.pluginConfig,
pluginConfigDefaults: module.pluginConfigDefaults,
pluginConfigSchema: module.pluginConfigSchema,
pluginRefreshLoading: module.pluginRefreshLoading,
pluginInstallLoading: module.pluginInstallLoading,
pluginUninstallLoading: module.pluginUninstallLoading,
pluginReinstallLoading: module.pluginReinstallLoading,
dependencyLoading: module.dependencyLoading,
dependencyInstalling: module.dependencyInstalling,
dependencyVerifying: module.dependencyVerifying,
refreshPlugin: module.refreshLocalPlugin,
installPlugin: module.installPlugin,
uninstallPlugin: module.uninstallPlugin,
reinstallPlugin: module.reinstallPlugin,
verifyDependencies: module.verifyDependencies,
reinstallDependencies: module.reinstallDependencies,
loadPluginConfig: module.loadPluginConfig,
savePluginConfig: module.savePluginConfig,
});
}这是 managed panel 的标准入口。
参考:
apps/web/src/features/home/shared/managed-plugin/createManagedHomePanelPlugin.tsapps/web/src/components/home/panels/processes/plugin.ts
最小骨架:
export const xxxPlugin = createManagedHomePanelPlugin<
"xxx",
XxxRuntimeModule,
XxxBodyBindings,
XxxManageBindings,
XxxPluginMeta
>({
mode: "xxx",
meta: {
menuLabel: "你的菜单名",
titleSuffix: "你的标题后缀",
manageTitle: "你的管理区标题",
},
bodyComponent: XxxPanelEntry,
getBodyProps: (bindings) => ({
panel: bindings.panelBindings.xxx,
}),
getManageBindings: (bindings) => bindings.manageBindings.xxx,
missingTitle: "插件未安装时的标题",
missingDescription: "插件未安装时的说明",
createModule: ({ api, selectedAgent }) => useXxxModule(api, selectedAgent),
createBodyBindings: (module) => createXxxBodyBindings(module),
createManagedBindings: (module) => createXxxManagedBindings(module),
loadOnOpen: (module) => {
void module.loadXxx();
},
refreshManager: (module) => module.refreshLocalPlugin(),
getLogSource: (module) => ({
installed: module.pluginInstalled,
pluginId: "your_plugin_id",
}),
});当前 helper 已经帮你处理这些事:
- 管理区组件默认走 shared 的
AgentManagedPluginManagePanel.vue getAvailability默认根据manageBindings[mode].pluginInstalled投影getPluginMeta默认根据manageBindings[mode]投影
所以不要再把 managed panel 的 plugin meta 塞回宿主常驻 bindings。
新增插件后,只需要改一处宿主入口:
apps/web/src/features/home/host/agentDrawerRegistry.ts
要做的事只有两步:
- import 你的
xxxPlugin - 在
homePanelPluginRegistry里注册:
export const homePanelPluginRegistry = defineHomePanelPluginRegistry({
overview: overviewPlugin,
files: filesPlugin,
xxx: xxxPlugin,
});注意:
- registry key 必须和
plugin.mode一致 - 不一致时会直接破坏类型推导
默认不要动 shared / host。
只有在下面这些场景才考虑改:
例如你真的需要:
- 新的宿主级公共动作
- 新的跨面板共享状态
这时才评估改:
apps/web/src/features/home/host/contracts.tsapps/web/src/features/home/host/homePanelRuntimeLifecycleSetup.tsapps/web/src/features/home/host/homePanelRuntimeBindingSetup.ts
如果两个及以上面板都会复用,再考虑进 shared。
否则,优先留在自己的 panel 目录。
这不是宿主接线必需项。
如果你想继续在 shared 层暴露 XxxPanelBindings 这种命名类型,才顺手改:
apps/web/src/features/home/shared/agentDrawerBindings.ts
至少确认下面这些点:
plugin.mode和 registry key 一致HomePanelRuntimePlugin或createManagedHomePanelPlugin的泛型没有省掉到退化成unknown- 没有为了单插件需求去改
useHomeViewDrawerSetup.ts - 没有把 managed panel 的安装 / 卸载 / 配置 / 日志管理 UI 重造一套
- 没有把 plugin meta 再塞回宿主常驻 bindings
每轮修改后都要跑:
cd apps/web
npm run typecheck
npm run buildnon-managed 参考:
apps/web/src/components/home/panels/overview/plugin.tsapps/web/src/components/home/panels/files/plugin.ts
managed 参考:
apps/web/src/components/home/panels/processes/plugin.tsapps/web/src/components/home/panels/services/plugin.tsapps/web/src/components/home/panels/startup/plugin.tsapps/web/src/components/home/panels/terminal/plugin.ts
shared / host 参考:
apps/web/src/features/home/host/contracts.tsapps/web/src/features/home/host/agentDrawerRegistry.tsapps/web/src/features/home/shared/managed-plugin/createManagedHomePanelPlugin.tsapps/web/src/features/home/host/homePanelRuntimeSetup.tsapps/web/src/features/home/host/homePanelRuntimeLifecycleSetup.tsapps/web/src/features/home/host/homePanelRuntimeBindingSetup.ts
现在新增首页抽屉插件,默认答案应该是:
- 先判断是 managed 还是 non-managed
- 在自己的 panel 目录里实现
plugin.ts + module + body - 需要管理区就在自己的
plugin.ts里补createManageBindings/manageComponent - 最后只在
agentDrawerRegistry.ts注册一次
如果你发现自己开始想改很多宿主文件,先停一下,通常说明你在绕开现有 plugin/runtime 主链。