Repository navigation
Releases
v1.4.2 — distinguish a startup crash from a port collision
Fixed
A server that crashed during startup was reported as a port collision. Pressing Start with nothing on port 3080 could still raise "Port 3080 is already in use — another server is already on port 3080. It is left running", naming PID ?. No such process existed, and the port was free the moment the alert appeared.
DSH binds the port early in boot and loads its plugin tree afterwards, so a failure in that tree kills a process that has already bound. waitUntilReady saw the port open, and a second later it was gone. The check treated "nobody on the port now" as identical to "the incumbent still holds it" — a collision that is impossible by definition when nothing was listening beforehand.
The before/after PIDs now resolve through a pure Server.startOutcome, separating .diedAfterBinding from .portTaken. A server that bound and then exited gets its own headline, "The server started, then exited", rather than "did not start", which reads like it never ran.
Failure alerts showed the least useful six lines of the log. A Node crash dump ends in closing braces, an AggregateError tail and a version banner, so the alert read } / } / Node.js v26.8.1 / [ELIFECYCLE] Command failed with exit code 1 — four lines naming no cause. The line that does sits at the top, sometimes hundreds of lines up. Alerts now lead with the first line that opens an error report, plus the indented detail beneath it, falling back to the tail for logs with no recognisable error.
Plugin
Unchanged since v1.3.0 — index.js and cordis.patch.yml are byte-identical to the previous release. The tarball is republished only so that releases/latest/download/ keeps resolving, and to keep the plugin's version in step with the app.
dsh plugin --profile web add https://github.com/songer522/dsh-launcher/releases/latest/download/dsh-menubar-launcher.tgzAlready installed? There is nothing to update for the plugin half. Rebuild the app with ./build.sh to get this fix.
v1.4.1 — fix the cold-start hang
Fixed
Starting a server on a free port left the UI stuck on "Starting…" forever.
Reproducible from cold: stop the server, quit the app, relaunch, press Start. The server came up correctly, but neither the menu bar nor the panel ever left the busy state, and no alert appeared.
Shell.run reads the child's stdout to end-of-file, and EOF only arrives once every process holding that pipe's write end has closed it. The launch command backgrounds a long-lived server that inherits that descriptor, so the read blocked for as long as the server ran — the sh we spawned had already exited. Everything after the launch (setBusy(false), the alerts, the browser handoff) never executed.
This only ever appeared on a free port, which is why it survived earlier testing: with a server already listening, the new instance died of EADDRINUSE within seconds, closing the pipe and letting the read return.
Server launches now use Shell.launch, which gives the child no pipes at all. The command keeps its own > log redirect, so the log, the tokenized URL and the EADDRINUSE diagnostics all still reach the UI.
Plugin
Unchanged since v1.3.0; the tarball is republished only to keep its version in step with the app.
dsh plugin --profile web add https://github.com/songer522/dsh-launcher/releases/latest/download/dsh-menubar-launcher.tgzv1.4.0 — Simplified Chinese interface
简体中文界面 / Simplified Chinese interface
Every menu item, button, status line, alert and Preferences label is now available in English and 简体中文.
Choose the language under Preferences (⌘,) → Language — it applies immediately, no relaunch. The default follows macOS; explicit English and 简体中文 options let you run the app in a different language from the rest of the system.
选择路径:偏好设置(⌘,)→ 界面语言,切换后立即生效,无需重启。默认跟随系统。
Fixed
- A malformed value in
config.jsonno longer discards the whole file.JSONDecodertreats an unrecognized enum value as a failure for the entire document, andload()falls back to defaults on any failure — so a typo in the newlanguagekey would have silently reset the project path, port and browser too. It now decodes leniently: an unreadable value costs only the language preference.
Plugin
The bundled plugin is unchanged from v1.3.0; this tarball is republished only to keep its version in step with the app.
dsh plugin --profile web add https://github.com/songer522/dsh-launcher/releases/latest/download/dsh-menubar-launcher.tgzv1.3.0 — DSH plugin + authoritative runtime descriptor
The launcher is now also a DSH plugin
Install it into your web profile:
dsh plugin --profile web add https://github.com/songer522/dsh-launcher/releases/latest/download/dsh-menubar-launcher.tgzor build from source:
dsh plugin --profile web add github:songer522/dsh-launcherAdded
- Companion Host plugin (
dsh-menubar-launcher). Runs inside the harness and publishes the running server's port, PID and tokenized URL to~/.config/dsh-launcher/runtime.json, removing the file when the server stops. Written0600in a0700directory — that URL carries a launch token, which is a credential. - No dependencies at all, so it installs against any harness version and needs no
allowBuildsapproval. In a profile with no web server it does nothing and the harness boots normally.
Fixed
- "Open in Browser" opened a dead tab for a server the app did not start. The token was previously recovered by grepping the app's own log, which only exists for a server the app launched itself. The app now prefers the runtime descriptor and falls back to log parsing, so the plugin remains optional.
- A descriptor is trusted only when the PID it names is the process currently listening on the port, so a file left behind by
kill -9is ignored rather than used to open a tab whose token is already dead.
The attached tarball is prebuilt and its filename carries no version, so releases/latest/download/ keeps resolving after future releases.