DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Menubar Launcher — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
M

dsh-menubar-launcher

Menubar Launcher

DSH Launcher 的主机插件:将运行中服务器的端口、PID 和令牌化 URL 发布为运行时描述符。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:songer522/dsh-launcher#bbc132d56c7719917f6b64752b53e6f05ca0ad31
README兼容性版本

兼容性与来源证明

Menubar Launcher 以 dsh-menubar-launcher 发布,当前版本为 1.4.2。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/9/12

版本

1.4.2stable
2026/9/12
1.4.1stable
2026/9/9
1.3.0stable
2026/9/8

相关插件

正在加载相关插件…

最新版
1.4.2
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/9/12
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Web App@deepseek-ai/dsh-web-appdsh 浏览器界面捆绑包:位于 dsh-base 之上的 Web 补丁层,加上运行时粘合插件(提供前端 dist、Web 界面提示符、bash 运行时变量和 URL 行)Experimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profile用于 Agent Teams Remote 和 UI 插件的实验性 Web 配置层Whale Widgetdsh-whale-widgetDSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义Web App@monotykamary/dsh-web-appdsh browser-surface 捆绑包:在 dsh-base 之上的 Web 补丁层,加上运行时衔接插件(前端 dist 提供服务、web-surface 提示符、Bash 运行时变量、URL 行)

README

DSH Launcher

English · 简体中文

[!IMPORTANT] Arrived from the plugin market? This plugin belongs to a macOS app. On its own it only writes a small JSON file describing the running server — that is deliberate, and it is all it does. The thing you actually interact with is a macOS menu bar app, built from this repository with ./build.sh. Install the app first; see Install. Not on macOS? The plugin runs anywhere, but nothing here will be useful to you without the app.

A tiny macOS menu bar utility for a local development server: start it, open it in your browser, restart it, or stop it — without touching a terminal.

It lives in the status bar with no Dock icon, so it stays out of your way.

Written for DeepSeek Harness (pnpm dsh web), but the command, port, project folder and browser are all configurable, so it works for any long-running local server.

Native AppKit, single Swift file, no dependencies.

It ships with a small companion DSH plugin so the app learns the server's port, PID and tokenized URL from the server itself rather than by reading its log. See DSH plugin.

https://github.com/user-attachments/assets/153503d2-c5b8-4d74-aaff-aa20f18bf6de

Why I built this

The itch was simple: every time I wanted to use DeepSeek Harness, running it from source meant running it from a terminal.

cd ~/Workspace/deepseek-harness
pnpm dsh web

The command itself is small — but the ceremony around it wasn't. The terminal tab had to stay open for the whole session; pulling new changes meant going back to it, killing the server, and starting it again; the URL with its per-process token had to be copied out of the logs; and when the session was over the server had to be stopped by hand or it kept squatting on the port.

All the actual work happens in a browser tab. The terminal was never the point of the tool — it was just the tollbooth in front of it. So I gave that ceremony a menu bar icon instead: one click to start, one click to open the browser, restart after an update, and a confirmed stop — with the server's state visible at a glance and no Dock icon in the way.

Trying to do this properly from a GUI app is also what surfaced the sharp edges that the terminal had been silently papering over — a GUI app inherits no shell PATH, DSH's launch token must survive into the opened tab, and stopping must target only the process listening on the port. Those are documented under How it works.

Menu bar

The status icon is the DeepSeek whale, drawn as a template image so macOS tints it for light and dark menu bars. It shows server state at a glance — solid when running, dimmed when stopped — and everything is one click away:

DSH Launcher menu open in the macOS menu bar, showing server status and actions

The two dimmed lines at the top are status, not actions: server state (Running on port 3080, Not running, or a transient Starting… / Stopping…) and the PID.

When the server is stopped, the menu shows Start Server instead.

State is polled every 3 seconds, so the icon stays correct even when you start or stop the server from a terminal.

Panel (optional)

Show Panel opens a small window with the same actions:

The DSH Launcher panel showing a running server, its PID and command, and buttons to open, restart, or stop it

Closing the panel does not quit the app — it stays in the menu bar.

Note the window controls: close and minimize are enabled, while zoom/fullscreen is deliberately greyed out — this is a fixed-size panel, and stretching it full-screen would only add empty space.

Install

This repository ships two independent pieces, and it is worth being clear about which is which:

PieceWhat it isInstalled byRequired?
The appa macOS menu bar app./build.shyes — it is the launcher
The plugina DSH plugin (dsh-menubar-launcher)dsh plugin addno — recommended

The app is the product. The plugin is a small helper that runs inside the server and tells the app the URL to open. The app works without it; see Do I need the plugin? for exactly what changes.

1. Install the app

Requires macOS 13+ and the Xcode Command Line Tools (xcode-select --install).

git clone https://github.com/songer522/dsh-launcher.git
cd dsh-launcher
./build.sh

That compiles, bundles, signs, and installs to /Applications. Launch it from there — it appears in the menu bar, with no Dock icon.

./build.sh --dev        # build into ./build without installing
./build.sh --uninstall  # remove the installed app

2. Install the plugin (recommended)

dsh plugin --profile web add https://github.com/songer522/dsh-launcher/releases/latest/download/dsh-menubar-launcher.tgz

Then restart the server — from the app's Restart menu item, or by stopping and starting it however you normally do. A plugin is composed when the server starts, so a server that was already running does not have it, and nothing will appear until it restarts.

Verify it took effect:

cat ~/.config/dsh-launcher/runtime.json

A JSON object means it is working. "No such file or directory" means either the server has not been restarted since installing, or it is not running.

The plugin also appears under Settings → Plugins in the DSH web UI, as dsh-menubar-launcher with a green active dot.

Use the same profile the app launches. The command above installs into the web profile, which is what dsh web and the app's default command (pnpm dsh web …) both boot. If you changed the app's command in Preferences to use a different profile, pass that profile to dsh plugin --profile <name> add … instead, or the server the app starts will not have the plugin.

To remove it: dsh plugin --profile web remove dsh-menubar-launcher.

Do I need the plugin?

No. The app has always resolved DSH's per-process token by reading the log of the server it started, and that still works. The plugin matters in the case that mechanism cannot cover — a server the app did not start:

SituationWithout the pluginWith the plugin
You start the server from the app✅ works (reads its log)✅ works
The server was started from a terminal❌ opens a tab that 401s✅ works
The server was started before the app❌ opens a tab that 401s✅ works

So: install the app alone if you always start the server from the app. Add the plugin if you also run dsh web from a terminal, or leave a server running across app restarts.

Both paths are kept deliberately — the plugin is an improvement, not a requirement, and the app never assumes it is there.

Configuration

On first run the app looks for a DeepSeek Harness checkout in the usual places (~/Workspace, ~/Projects, ~/Developer, ~/src, ~/code, ~). If your project lives elsewhere, or you want to launch something entirely different, open Preferences (⌘,).

Settings are stored as JSON at ~/.config/dsh-launcher/config.json:

{
  "repo": "/Users/you/Workspace/deepseek-harness",
  "command": "pnpm dsh web --no-open --port {port}",
  "port": "3080",
  "browser": "Google Chrome",
  "logFile": "/tmp/dsh-web.log",
  "language": "system"
}
FieldMeaning
repoworking directory the command runs in
commandthe launch command; {port} is substituted
portthe port to watch, and to substitute into the command
browserapp name to open, or "" for the system default
logFilewhere the server's stdout/stderr is written
languageinterface language: system, en, or zh-Hans

Because it is just a command in a directory, it happily runs npm run dev, vite, python -m http.server, or anything else.

Language

The interface is available in English and 简体中文. Choose one under Preferences (⌘,) → Language; it applies immediately, with no relaunch.

The default, System, follows macOS — Chinese when your preferred language is Chinese, English otherwise. The explicit choices exist because running one app in a different language from the rest of the system is common enough among developers to be worth a setting.

Why a launch flag matters

The stock command uses --no-open because DSH otherwise opens the URL in the system default browser. Suppressing that and opening the browser here is what lets you use Chrome while Safari remains your system default.

DSH plugin

The app can manage a server it did not start — but then it has no log to read, and DSH's per-process token lives only in that log. The result was a menu item that opened a tab answering 401.

This repository therefore also ships a Host plugin. It runs inside the harness, where the port and the token are not parsed but simply known, and writes them where the app can find them. Install it with dsh plugin add; installing from source instead of the release tarball works too, and needs no build step either:

dsh plugin --profile web add github:songer522/dsh-launcher

Once the server restarts, it writes ~/.config/dsh-launcher/runtime.json:

{
  "version": 1,
  "pid": 29185,
  "host": "127.0.0.1",
  "port": 3396,
  "url": "http://127.0.0.1:3396",
  "authenticatedUrl": "http://127.0.0.1:3396/?token=…",
  "startedAt": "2026-09-08T21:51:36.643Z"
}

The file is created once the server is listening and removed when it stops, so its presence is itself a liveness signal. It is written 0600, and the directory 0700: authenticatedUrl contains a launch token, which is a credential granting full access to that harness. Treat it like one.

The app prefers this file and falls back to log parsing when it is absent, so the plugin is optional — without it you get exactly the previous behaviour. A descriptor is used only when the PID it names is the process currently listening on the port, so a file left behind by a kill -9 is ignored rather than used to open a dead tab.

The plugin is not macOS-specific: anything that wants the authenticated URL of a running harness can read the same file.

To move it, restate the row's whole config in your profile's cordis.patch.yml (a patch replaces a row's config rather than merging into it) — note that the app looks only at the default path:

- id: dsh-launcher-runtime
  config:
    path: /somewhere/else/runtime.json
    enabled: true

The plugin declares no dependencies at all, so it installs against any harness version and needs no allowBuilds approval. In a profile without a web server — headless, say — it simply does nothing and the harness boots normally.

Window behaviour

The app is a menu bar utility (LSUIElement), so it has no Dock icon and no ⌘Tab entry. Quit it from the menu bar.

For the optional panel:

ControlState
Close (red)enabled — hides the panel; app stays in the menu bar
Minimize (yellow)enabled
Zoom / fullscreen (green)disabled — fixed-size panel

Fullscreen is suppressed both by omitting .resizable from the window's styleMask and by adding .fullScreenNone to its collectionBehavior, so ⌃⌘F does nothing either.

The server is started detached, so quitting the launcher never stops it. Stopping is always an explicit, confirmed action — the server may be hosting live sessions.

How it works

Four details that are easy to get wrong:

1. GUI apps have no shell PATH. A double-clicked app does not read ~/.zshrc and does not inherit a login PATH, so pnpm would simply not be found. Binaries are resolved explicitly against common install locations, with a login-shell lookup as a fallback. The spawned server also gets those directories prepended to its PATH: resolving pnpm alone is not enough, because it execs node, which would otherwise fail with env: node: No such file or directory.

2. Authentication tokens must be carried over. DSH mints a per-process launch token and prints the URL with it; the bare origin answers HTTP 401, so the tab would be dead. The app resolves that URL from two sources, in order: the runtime descriptor written by the companion plugin, then the log this run printed. The descriptor is preferred because it is also correct for a server started from a terminal — the case where there is no log to read. Servers that publish neither fall back to the plain URL.

3. Port probing must filter for listeners. This app uses:

lsof -ti tcp:3080 -sTCP:LISTEN

Without -sTCP:LISTEN, lsof also reports every client connected to that port — your browser, your editor — and acting on those PIDs would terminate unrelated applications. Only one process can hold LISTEN on a port, so this targets exactly the server no matter what its PID happens to be.

4. Stopping is graceful. TERM first, escalating to KILL only if the port is still bound after ~6 seconds.

Development

Everything lives in one file: Sources/DSHLauncher.swift.

swiftc -O Sources/DSHLauncher.swift -o build/DSHLauncher && ./build/DSHLauncher

The code-signing gotcha

Nothing to do here if you are just installing — build.sh already gets this right, and verifies the signature before it finishes. It matters only if you edit the build script, so the reason the step order cannot be rearranged is written down.

build.sh signs the bundle last, and that ordering is not cosmetic. Modifying a bundle after signing — replacing the icon, editing Info.plist — invalidates the signature, and macOS then refuses to treat the directory as an application. The visible symptom is that Finder shows a folder icon, which looks like an icon bug but is really a signature failure:

$ codesign -v "/Applications/DSH Launcher.app"
invalid Info.plist (plist or signature have been modified)

To see the icon macOS actually resolves, bypassing Finder's cache:

osascript -l JavaScript -e 'ObjC.import("AppKit");
var img=$.NSWorkspace.sharedWorkspace.iconForFile("/Applications/DSH Launcher.app");
var rep=$.NSBitmapImageRep.imageRepWithData(img.TIFFRepresentation);
rep.representationUsingTypeProperties($.NSPNGFileType,$()).writeToFileAtomically("/tmp/resolved.png",true);'
open /tmp/resolved.png

Shell equivalents

If you prefer the terminal, these do the same jobs. Two details matter: the -sTCP:LISTEN filter, and capturing the tokenized URL instead of opening the bare origin (which answers 401).

dshweb() {
  local port="${DSH_WEB_PORT:-3080}"
  local log="${TMPDIR:-/tmp}/dsh-web-${port}.log"
  : > "$log"   # a stale URL carries a dead token
  (cd ~/Workspace/deepseek-harness && pnpm dsh web --no-open --port "$port" > "$log" 2>&1) &
  local server=$!
  tail -f "$log" & local tailer=$!

  while ! nc -z 127.0.0.1 "$port" 2>/dev/null; do
    kill -0 "$server" 2>/dev/null || { kill "$tailer" 2>/dev/null; return 1; }
    sleep 0.3
  done

  # DSH prints `dsh web: http://127.0.0.1:PORT/?token=…` — open that, not the
  # bare origin. Prefer loopback and exclude brackets: the LAN URL is printed
  # on the same line inside parentheses.
  local url=""
  for _ in {1..40}; do
    url=$(grep -aoE "https?://[^[:space:]'\"()]*[?&]token=[^[:space:]'\"()]+" "$log" \
          | grep -E "127\.0\.0\.1|localhost" | tail -1)
    [ -n "$url" ] && break
    sleep 0.25
  done

  open -a "Google Chrome" "${url:-http://127.0.0.1:$port}"
  wait "$server"; kill "$tailer" 2>/dev/null
}

dshkill() { lsof -ti tcp:3080 -sTCP:LISTEN | xargs kill; }

Note that in zsh a background pipeline (cmd | tee log &) sets $! to tee rather than the server, which breaks the liveness check — hence redirecting to a log and tailing it separately.

Beware also that lsof -ti tcp:PORT without -sTCP:LISTEN matches clients connected to the port too, so that widely-shared one-liner can kill your browser.

License

MIT