dsh-functions

uitested: passnpmMIT

Adds an extensible Functions menu to the DSH top bar, where plugins register one-click actions with staged confirmations and submenus. Since 0.4.0 it bundles a built-in "Restart DSH" action, so no separate restart plugin is needed.

下载量 / Downloads
845
依赖体积 / node_modules
0.17 MB
测试时间 / Tested
2026-11-07
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-functions

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-functions

给 DeepSeek Harness 客户端顶端加一个可扩展的 「功能」 标签:

插件注册进来的一键功能都在这儿,用户不用再记着「去哪儿点 / 输入什么口令」。

0.4.0 起这个包自带「重启 DSH」 —— 装这一个包,功能栏里就有「重启 DSH」(两级确认,有任务在跑会拦一下);
不再需要单独安装 dsh-function-restart(已并入本包,见下面「一键重启 DSH」一节)。

[▢] 应用 编辑 功能 3 ▾

+----------------------------------------+

功能

插件在这里挂上自己的一键功能

加载功能 让会话去找非官方插件

读取当前会话 让当前窗口的会话读一次

换会话 > 真交接或演练,选一条

插件市场 逛社区插件、一键装

重启 DSH 重启 DSH,让设置生效

+----------------------------------------+

(示意图来自作者本机安装的一组插件;只装本包时,面板里有内置的「加载功能」和「重启 DSH」(0.4.0 起)。)

下面是真机截图(Windows 桌面版,装了本包 + 几个社区插件):

重启后(0.5.0):置顶的「加载功能」下面多了一条分隔线,和其余选项分开;

「加载功能」的说明也补上了「也可以按你的描述直接加一条功能」。

不 patch DSH 本体,不 require React,不引任何 Harness 客户端包 ——

它就是一个普通插件:一行 Loader 条目 + 宿主半边 + 一个自带样式的客户端 bundle。

安装

# 从 npm 装(推荐,版本/下载量在 npm 页可见)
dsh plugin --profile desktop add dsh-functions

# 或者直接从 GitHub 装
dsh plugin --profile desktop add github:Num86/dsh-functions

装

DSH → 插件 → 添加插件,填这个包的路径(tarball 或解包目录都行),然后重启 DSH

(服务端不热更新;客户端那半边刷新页面就有)。

手动装也行:profile 的 package.json 里加一条依赖 + dsh.profile.bundles 一行,

再在 node_modules 里放一个指向它的 Junction/软链。

装好后插件页会用它包里的展示元数据(locale/zh.json、locale/en.json 里的

meta.title / meta.description)显示标题与描述,并显示包内的 icon.svg。

package.json 里对应的是顶层 icon 字段、exports 里的 ./locale/*.json,

以及 files 里的 locale/*.json 与 icon.svg(0.3.1 起;0.4.0 起还带 restart/ 的

图标与 locale,见下面「一键重启 DSH」一节)。

五行加一条功能(宿主半边,推荐)

// 你自己的插件 index.js

export const inject = ['functions'];

export function apply(ctx) {

ctx.functions.register({

id: 'my.tool',

title: '干点什么',

description: '一句话说明\n它调用的插件名', // 第二行惯例:写它依赖的插件

group: '我的',

order: 10,

confirm: { title: '干点什么', message: '现在跑?' },

async run() { return { message: '干完了' }; },

});

}

run() 返回的 message 会弹在界面上;返回 { warnAfterMs, warnMessage } 的话,

到点还活着就再弹一条(给「重启这种会把自己关掉的功能」兜底)。

字段表

字段 · 类型 · 说明

id · string · 必填,唯一。

title · string · 必填,菜单显示名。

description · string · 说明。可以带换行:第一行讲做什么,第二行写它调用的插件(面板渲染两行,右对齐)。

group · string · 分组,默认「通用」。只参与排序,标签默认不画。

order · number · 组内排序,小在前,默认 100。

pinned · boolean · 置顶:永远排最上面(不受分组与 order 影响)。

parent · string · 挂到某个父项的子菜单里(二级菜单)。写了它就不出现在顶层。

submenu · boolean · 标记这一条是二级菜单的父项(容器):自己没有动作,run 可以不写。

icon · string · 预留(2026-10 之后面板不再显示单字图标,写了不报错)。

confirm · object · 一级确认窗:{ title, message, detail, confirmText, cancelText }。

confirms · array · 多级确认(静态)。只有第 0 级用得到。

prepare(stage, context) · function · 动态确认:第 N 级的文案在第 N-1 级点掉之后才算(「有没有任务在跑」这种话隔一次就会过期)。返回 null = 没有更多确认。

run(job) · function · 执行。job = { id, ctx, log, session, confirm, alert, toast, get, post }。不写 run 就必须写 shell 或 submenu: true。

shell · object · 只想跑命令:{ command, args, cwd },结果整理成一句话显示。

hidden · boolean · 不进菜单(仍可用 API 调)。

二级菜单长这样(照 Windows 右键菜单的逻辑:悬停 120ms 展开、离开 180ms 收起,点父项也能展开):

ctx.functions.register({ id: 'family', title: '换会话', submenu: true, group: '会话' });

ctx.functions.register({ id: 'family.go', title: '真交接', parent: 'family', run() {...} });

config.functions:不写代码,在 YAML 里加功能(有限制)

本插件还认自己这一行 Loader config 里的 config.functions,不写代码也能加功能:

  • id: my-quick-actions

name: dsh-functions

config:

functions:

  • id: open-folder

title: 打开插件目录

group: 工具

confirm: { title: '打开插件目录', message: '用资源管理器打开?' }

shell: { command: 'explorer.exe', args: ['D:/some/plugin'] }

字段与 ctx.functions.register 完全一样(见上面的字段表)。

限制(重要):

  • 本插件没有导出 Config:没有 schema 校验,也没有插件页里的配置表单。

cordis 会把这一行的 config 原样传进来,所以写错了只在宿主 console 留一行日志,

菜单里静默少一条。

  • 不要为了加功能再 insert 一行 dsh-functions。同一个插件挂两行会二次挂载:

provide('functions') 与三条同 path 路由都会抛,整个插件激活失败,功能栏直接消失。

正确做法是在 profile 补丁里按行 id 覆盖已有的那一行,把 config.functions 写在它的 config 下。

  • 只想要一条功能时,更稳的是让别的插件 ctx.functions.register(...) —— 有代码可测。

三条路由(自己写客户端时才用)

GET /api/dshFunctions/list 列宿主注册的功能

POST /api/dshFunctions/prepare { id, stage, sessionId } -> 确认窗

POST /api/dshFunctions/run { id, confirm: 'yes', sessionId }

sessionId 是「用户正在看哪个会话」,浏览器那半边会自动带上(读客户端服务 uiSession)。

需要往会话里送东西的功能用它 —— 拿不到就拒,别猜(指令发错窗口代价太大)。

客户端半边

功能本质上是「打开某个界面」时(打开设置里某一节、打开某个面板)才用客户端注册:

window.__ModuleLoader__.load({

id: 'my-plugin',

factory(require) {

return {

inject: ['functions'],

apply(ctx) {

ctx.functions.register({

id: 'my.open', title: '打开某面板',

run: async (job) => { / job.post / job.confirm / job.toast / },

});

},

};

},

});

面板是纯 DOM(shadow root 隔离),升级后最坏是外观退化,不会白屏。

顶端标签的定位(Windows 桌面专属,私有契约)

「功能」标签不是官方 slot:它站在 Windows 桌面应用原生「应用 / 编辑」菜单的右边,

位置靠量桌面外壳 preload 注入的私有 DOM [data-windows-menu] 得到。

  • 只在 Windows 桌面应用里存在(dsh-app://app 页面);普通浏览器、macOS 桌面没有这个锚点。
  • 当前 DSH 的 slot 目录里没有「窗口标题栏 / 原生菜单条」这种 slot,所以没有官方替代。

外壳改了这段 DOM,顶多位置退化(最坏挪到页面左上角),面板本身照常工作,不会白屏。

  • 加固写法:只有 <html data-windows-titlebar> 存在时才按标题栏几何

(--dsh-windows-titlebar-height / --dsh-windows-menu-start)停位;量到锚点矩形就贴它的右边;

其它环境保留 best-effort 位置。位置更新是事件驱动的(属性变化 / 窗口 resize / 启动期有限重试),不轮询。

  • 升级 DSH 后请手动点一遍「功能」栏,确认位置与面板正常。

内置的「加载功能」

这个插件自己内置了一条置顶功能 加载功能:dsh 没有「在工作区新开会话」的能力,

所以它走「把一段指令当成用户消息送进当前会话」这条路 ——

让会话里的 dsh 去插件库里找非官方插件、列出数字编号给用户挑,再读 R

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::3,最后更新 2026-10-24

Links

GitHub · Homepage · npm

Same category