feat: 推荐插件目录收录 dsh-sentinel(Tab 注册)

- plugins-tabs.ts 收录 dsh-sentinel(条件驱动的 agent 唤醒系统,注册 dsh-sentinel:watches Tab)
- install 拼接官方一键命令:cd ~/.dsh && dsh plugin --profile web add "github:fuhefei/dsh-sentinel#v0.7.0"
- 新增 pluginSentinelDesc 文案(zh/en);测试同步(tab 目录不再为空态)
This commit is contained in:
Menghuan1918
2026-08-14 23:04:25 +08:00
parent c0aeefc936
commit 7d524724ae
5 changed files with 29 additions and 13 deletions
@@ -27,7 +27,7 @@
### 3.1 数据层:两个目录文件 + 共享词汇
- `src/client/plugins-shared.ts`:`PluginEntry { id, name, url, description, install }` + `PLUGIN_TOPIC_URL`。
- `src/client/plugins-tabs.ts`:`builtinTabPlugins`(当前为空,空态展示)。
- `src/client/plugins-tabs.ts`:`builtinTabPlugins`(收录 dsh-sentinel——条件驱动的 agent 唤醒系统,注册 `dsh-sentinel:watches` Tab;install 用官方 github: 源一键命令)。
- `src/client/plugins-viewers.ts`:`builtinViewerPlugins`(种子:office 插件,install = `cd ~/.dsh && dsh plugin --profile web add @huanlin/dsh-plugin-better-sidebar-plugin-office`)。
### 3.2 UI 层:`src/client/add-plugin-modal.tsx` + `SideCardSection`
@@ -47,7 +47,7 @@
## 4. 边界情况与失败模式
- **复制失败**:`writeClipboard` 尽力而为(navigator.clipboard → execCommand 兜底),失败静默;按钮反馈照常闪现。无会话/无终端/配额等一切前置条件均不涉及——复制与它们无关。
- **空目录**:Tab 目录当前为空,弹窗渲染空态 + topic 按钮。
- **空目录**:目录可为空(弹窗渲染空态 + topic 按钮);当前 Tab/Viewer 目录各有 1 条。
- **版本号**:v0.12.0(`package.json` / `dsh.plugin.json` / `service.ts` 的 `SIDEBAR_SERVICE_VERSION` 三处一致,manifest-consistency 测试守护)。
## 5. 实施偏差记录
+2
View File
@@ -218,6 +218,7 @@ export const zh = {
openPlugin: '跳转',
copyInstall: '复制安装命令',
pluginOfficeDesc: '为 better-sidebar 编辑器提供 Office 三件套预览(.docx / .xlsx / .pptx),把重型 Office 渲染库拆出主包、按需安装',
pluginSentinelDesc: '条件驱动的 agent 唤醒系统:文件/进程/端口/HTTP/命令/webhook 传感器,条件达成自动唤醒休眠会话;注册「哨兵」Tab 展示服务器全局监控表',
}
/** The en dictionary (key-set-equal to zh, enforced by the type annotation). */
@@ -429,6 +430,7 @@ export const en: Record<keyof typeof zh, string> = {
openPlugin: 'Open',
copyInstall: 'Copy install command',
pluginOfficeDesc: 'Office-suite preview (.docx / .xlsx / .pptx) for the better-sidebar editor, keeping the heavy Office render libraries out of the core bundle',
pluginSentinelDesc: 'Condition-driven agent wakeup: file/process/port/http/command/webhook sensors wake dormant sessions when conditions fire; registers a "Sentinel" tab with the server-wide watch table',
}
/**
+19 -7
View File
@@ -3,13 +3,25 @@
* shown in the "add tab plugin" modal (Side card settings → 侧边栏内容 grid
* → the dashed card). Adding an entry: append one object here (unique
* `id` = npm package name, `url` = GitHub repo, `description` =
* i18n-friendly, `install` = the full shell command pre-filled into the
* install terminal — it starts with `cd ~/.dsh` so the install runs with
* the DSH home as the working directory). Data integrity is guarded by
* `tests/plugin-list.spec.ts`.
* i18n-friendly (add a `pluginXxxDesc` key in locales.ts), `install` = the
* full one-line install script — it starts with `cd ~/.dsh` so the install
* runs with the DSH home as the working directory). Data integrity is
* guarded by `tests/plugin-list.spec.ts`.
*/
import { t } from './locales.ts'
import type { PluginEntry } from './plugins-shared.ts'
/** Tab-registration plugins (empty until a real one exists — the modal
* renders the empty state and points at the GitHub topic). */
export const builtinTabPlugins: readonly PluginEntry[] = []
/** Tab-registration plugins (alphabetical order). */
export const builtinTabPlugins: readonly PluginEntry[] = [
{
id: '@dsh-external/dsh-sentinel',
name: 'dsh-sentinel 唤醒系统',
url: 'https://github.com/fuhefei/dsh-sentinel',
description: () => t('pluginSentinelDesc'),
// The official one-line bundle-channel install (git source, build
// artifacts committed — no build step needed). The `github:…` form is
// the upstream's documented command, `cd ~/.dsh` keeps the profile
// context consistent with the other entries.
install: 'cd ~/.dsh && dsh plugin --profile web add "github:fuhefei/dsh-sentinel#v0.7.0"',
},
]
+4 -3
View File
@@ -48,11 +48,12 @@ describe('PluginListBody (render)', () => {
expect(html).toContain('Copy')
})
it('tab kind renders its own empty state (no office entry)', () => {
it('tab kind renders the sentinel entry (its own catalog, no office entry)', () => {
const store = createSidebarStore()
const service = createBetterSidebarService(store)
const html = renderToString(createElement(PluginListBody, { service, kind: 'tab' }))
expect(html).toContain('No plugins curated yet')
expect(html).toContain('dsh-sentinel 唤醒系统')
expect(html).toContain('github:fuhefei/dsh-sentinel')
expect(html).not.toContain(officeEntry.name)
})
})
@@ -75,7 +76,7 @@ describe('AddPluginModal wiring', () => {
if (kind === 'viewer') {
expect(html).toContain(officeEntry.name)
} else {
expect(html).toContain('No plugins curated yet')
expect(html).toContain('dsh-sentinel 唤醒系统')
}
act(() => { root.unmount() })
container.remove()
+2 -1
View File
@@ -24,9 +24,10 @@ const catalogs: Array<[string, readonly PluginEntry[]]> = [
]
describe('builtin plugin catalogs', () => {
it('the viewer catalog has the seeded office plugin, the tab catalog does not', () => {
it('the viewer catalog has the office plugin, the tab catalog has the sentinel plugin', () => {
const ids = (list: readonly PluginEntry[]): string[] => list.map(p => p.id)
expect(ids(builtinViewerPlugins)).toContain('@huanlin/dsh-plugin-better-sidebar-plugin-office')
expect(ids(builtinTabPlugins)).toContain('@dsh-external/dsh-sentinel')
expect(ids(builtinTabPlugins)).not.toContain('@huanlin/dsh-plugin-better-sidebar-plugin-office')
})