From 7d82b72defb500aaaaddd956dd9bba3f9f8b0a99 Mon Sep 17 00:00:00 2001 From: ToastedPatatas Date: Tue, 21 Jul 2026 02:56:08 +0800 Subject: [PATCH] fix(cli): use rundll32 instead of cmd.exe for Windows browser fallback in dashboard command (#7844) * fix(cli): use rundll32 instead of cmd.exe for Windows browser fallback in dashboard command Extract resolveOpenCommand(platform, url) as an exported pure function so tests import the actual production code instead of duplicating logic. The openFallback function in bin/cli/commands/dashboard.mjs used cmd /c start to open the dashboard URL on Windows, spawning an unnecessary cmd.exe process. Replaces with rundll32 url.dll,FileProtocolHandler which opens the URL directly through the Windows shell handler API without any shell wrapper. Tests: 5 tests importing the actual resolveOpenCommand function, covering all platform branches (darwin, win32, linux) and URL pass-through. Changelog fragment included. * chore: rename changelog fragment 7842->7844 to match actual PR number --------- Co-authored-by: tientien17 --- bin/cli/commands/dashboard.mjs | 28 +++++++-------- .../fixes/7844-dashboard-no-cmd-fallback.md | 1 + .../unit/cli-dashboard-open-fallback.test.ts | 35 +++++++++++++++++++ 3 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 changelog.d/fixes/7844-dashboard-no-cmd-fallback.md create mode 100644 tests/unit/cli-dashboard-open-fallback.test.ts diff --git a/bin/cli/commands/dashboard.mjs b/bin/cli/commands/dashboard.mjs index 44d2da1df9..42157d27fb 100644 --- a/bin/cli/commands/dashboard.mjs +++ b/bin/cli/commands/dashboard.mjs @@ -49,22 +49,22 @@ export async function runDashboardCommand(opts = {}) { return 0; } +/** + * Resolve the command and args to open a URL in the default browser + * for a given platform. Exported for testing — callers should use openFallback(). + * @param {"darwin"|"win32"|string} platform + * @param {string} url + * @returns {{ cmd: string, args: string[] }} + */ +export function resolveOpenCommand(platform, url) { + if (platform === "darwin") return { cmd: "open", args: [url] }; + if (platform === "win32") return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] }; + return { cmd: "xdg-open", args: [url] }; +} + function openFallback(url) { return new Promise((resolve) => { - const { platform } = process; - let cmd, args; - - if (platform === "darwin") { - cmd = "open"; - args = [url]; - } else if (platform === "win32") { - cmd = "cmd"; - args = ["/c", "start", "", url]; - } else { - cmd = "xdg-open"; - args = [url]; - } - + const { cmd, args } = resolveOpenCommand(process.platform, url); execFile(cmd, args, { stdio: "ignore" }, () => resolve()); }); } diff --git a/changelog.d/fixes/7844-dashboard-no-cmd-fallback.md b/changelog.d/fixes/7844-dashboard-no-cmd-fallback.md new file mode 100644 index 0000000000..5f603edaa8 --- /dev/null +++ b/changelog.d/fixes/7844-dashboard-no-cmd-fallback.md @@ -0,0 +1 @@ +- **fix(cli):** Windows `omniroute dashboard` browser fallback uses `rundll32` instead of `cmd.exe` to open the dashboard URL, avoiding an unnecessary shell spawn ([#7844](https://github.com/diegosouzapw/OmniRoute/pull/7842)) diff --git a/tests/unit/cli-dashboard-open-fallback.test.ts b/tests/unit/cli-dashboard-open-fallback.test.ts new file mode 100644 index 0000000000..ecc6cc2436 --- /dev/null +++ b/tests/unit/cli-dashboard-open-fallback.test.ts @@ -0,0 +1,35 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { resolveOpenCommand } from "../../bin/cli/commands/dashboard.mjs"; + +test("openFallback: darwin uses 'open' command", () => { + const { cmd, args } = resolveOpenCommand("darwin", "http://localhost:20128"); + assert.equal(cmd, "open"); + assert.deepEqual(args, ["http://localhost:20128"]); +}); + +test("openFallback: win32 uses 'rundll32 url.dll,FileProtocolHandler' instead of cmd.exe", () => { + const { cmd, args } = resolveOpenCommand("win32", "http://localhost:20128"); + assert.equal(cmd, "rundll32"); + assert.deepEqual(args, ["url.dll,FileProtocolHandler", "http://localhost:20128"]); + assert.notEqual(cmd, "cmd"); +}); + +test("openFallback: linux/other uses 'xdg-open' command", () => { + const { cmd, args } = resolveOpenCommand("linux", "http://localhost:20128"); + assert.equal(cmd, "xdg-open"); + assert.deepEqual(args, ["http://localhost:20128"]); +}); + +test("openFallback: unknown platform falls back to xdg-open", () => { + const { cmd, args } = resolveOpenCommand("aix", "http://localhost:20128"); + assert.equal(cmd, "xdg-open"); + assert.deepEqual(args, ["http://localhost:20128"]); +}); + +test("openFallback: URL with special characters is passed through verbatim", () => { + const url = "http://localhost:20128/dashboard?q=test&filter=a+b"; + const { cmd, args } = resolveOpenCommand("win32", url); + assert.equal(cmd, "rundll32"); + assert.equal(args[1], url); +});