Elinpf/dsh-ops-plugins--packages-ops-panel ↗★ 1
@elinpf/dsh-ops-panel
Panel seam for ops plugins — a slash-command-triggered, session-scoped dialog shell. Provides the ctx.opsPanels client registry (single command/executed dispatcher + overlay shell) and the node-side registerPanelCommand helper. The first consumer is the access-grant panel (ADR-0004). 适合需要统一管理斜杠命令触发的会话弹窗和交互界面的插件开发者。
설치
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Elinpf/dsh-ops-plugins#64b1d76649bf24155173c74254d0fb61d12815d5&path:packages/ops-panel@elinpf/dsh-ops-panel
The panel seam for DeepSeek Harness ops plugins — one shared mechanism for session-scoped overlay dialogs triggered by slash commands (ADR-0004).
What it does
Business packages should not each invent their own dialog shell, keyboard handling, and command dispatch. ops-panel splits that concern in two:
- Client half (
ops-panel-client): provides theopsPanelsregistry service. Consumers register one panel definition per command —registerPanel({ command, title, component }). The shell renders the open panel as a centered modal over a backdrop (Escape / backdrop click closes), injected into theconversation.input.overlayslot. A singlecommand/executedlistener dispatches to the right panel by command name, so N panels cost one listener, not N. - Host helper (
registerPanelCommand(ctx, { name, description })): registers the slash command itself — a no-op success handler whose only job is to exist in the session's command directory so the UI lets the user submit it and firescommand/executed. Registers agent-scoped when called from the preset plane; fails loud when thecommandsservice is absent.
A panel component receives { sessionId, close } and owns everything inside the card — data fetching, polling, actions.
Why a service, not a library
Bundling this code per consumer would give every consumer its own registry, its own command/executed listener, and its own overlay shell — and only the last-registered shell would win. As a cordis service there is exactly one registry and one shell per page, and panels can be registered from any plugin.
Installation
Add to the app dependencies and reference in cordis.patch.yml (host plane):
- id: ops-panel
name: '@elinpf/dsh-ops-panel'
Consumers register from their own client halves:
ctx.inject(['opsPanels'], (pctx) => {
pctx.effect(() => pctx.opsPanels.registerPanel({
command: 'access',
title: '访问授权',
component: AccessPanel,
}))
})
…and register the command from their preset-plane host half:
ctx.inject(['commands'], (cctx) => {
registerPanelCommand(cctx, { name: 'access', description: '打开授权面板' })
})
First consumer: the access panel (ops-access-ui + ops-access/gate, spec 0003).
Known Limitations
- One open panel at a time (opening another replaces the current one).
- The shell is deliberately minimal (title + close) — tab bars, sizing, and drag belong to a later iteration if a second consumer needs them.
command/executedfires only for sessions whose preset mounts the command — panels are inherently preset-scoped.