Skip to content

Add option to launch Studio without opening the default browser#7016

Open
shimmyshimmer wants to merge 6 commits into
mainfrom
launcher-no-browser
Open

Add option to launch Studio without opening the default browser#7016
shimmyshimmer wants to merge 6 commits into
mainfrom
launcher-no-browser

Conversation

@shimmyshimmer

Copy link
Copy Markdown
Member

Summary

The desktop launchers generated by the installers (launch-studio.sh on macOS/Linux/WSL, launch-studio.ps1 on Windows) always open the default browser once the server passes its health check, with no way to opt out. Users who run Studio as a browser PWA or app window (for example a Vivaldi app window while Firefox is the OS default browser) want the server up without the default browser popping open every launch.

Note that unsloth studio from a terminal is unchanged and has never auto-opened a browser; this only touches the desktop launcher path.

Changes

  • Both launchers accept --no-browser (PowerShell also -NoBrowser) and honor UNSLOTH_STUDIO_NO_BROWSER=1. When auto-open is off the launcher still starts or attaches to the server and prints Unsloth Studio is running at: <url> instead of opening it
  • Interactive installs now ask once: Open Unsloth Studio in your default browser after launch? [Y/n]. Enter keeps the current behavior (yes). The answer is persisted: STUDIO_OPEN_BROWSER in studio.conf on macOS/Linux/WSL, baked into launch-studio.ps1 on Windows
  • ./install.sh --no-browser and install.ps1 --no-browser set the preference non-interactively; --browser sets it back. Combined with --shortcuts-only this flips an existing install without reinstalling
  • The --shortcuts-only refresh run by unsloth studio update preserves the stored choice instead of resetting it
  • All three Start-Process "http://..." sites in the Windows launcher now route through a gated Open-StudioUrl helper
  • README Launch section documents the behavior

Testing

  • New tests/sh/test_launcher_no_browser.sh (18 assertions): static shape checks on both installers plus a functional check that drives the extracted _open_browser with stubbed open/xdg-open binaries. All pass; sh -n and bash -n are clean
  • Fresh local install on macOS: studio.conf gets STUDIO_OPEN_BROWSER='1' by default. Against a healthy server, launch-studio.sh --no-browser and UNSLOTH_STUDIO_NO_BROWSER=1 print the URL and open nothing; the default path opens the browser exactly once (verified with a stubbed open on PATH recording invocations)
  • ./install.sh --shortcuts-only --no-browser flips the stored preference to '0' and a subsequent plain --shortcuts-only refresh (the studio update scenario) keeps it
  • The three pre-existing failures in tests/sh/test_install_host_defaults.sh also fail on a clean checkout of main and are unrelated

The generated launchers (launch-studio.sh / launch-studio.ps1) always
opened the default browser once the server became healthy. Add a
--no-browser launcher flag, the UNSLOTH_STUDIO_NO_BROWSER env var, and
a persisted installer preference (studio.conf / baked into the ps1
launcher) with an interactive install prompt. When auto-open is off the
launcher still starts or attaches to the server and prints the URL, for
users who run Studio as a browser PWA or app window. The
--shortcuts-only refresh run by studio update preserves the choice.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@shimmyshimmer

Copy link
Copy Markdown
Member Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: c980658592

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…ell launcher

Simulation testing caught that False or Off disabled the browser in
launch-studio.sh while the PowerShell launcher's -notin treats them as
falsy case-insensitively. Lowercase the value before matching so both
launchers agree, and pin the behavior in the launcher test.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Ran a full simulation pass on this before merge to make sure the default path cannot regress on any platform.

Method: every test runs the real launcher generated from the installer templates (the bash launcher via the same sed substitution install.sh performs, the Windows launcher by expanding install.ps1's actual here-string), against a mock backend that serves /api/health with a configurable studio_root_id, inside throwaway sandboxes (own HOME, XDG_RUNTIME_DIR, data dir, uv venv for the mock server). The OS browser-opening layer is stubbed on PATH (open, xdg-open, powershell.exe, cmd.exe) so runs record exactly what would have been opened. Linux and WSL branches are exercised through uname and /proc/version probes; the Windows launcher runs for real under PowerShell 7 with Start-Process shadowed. Since all launchers hand the URL to the OS default-browser mechanism, a per-browser dispatch simulation (Firefox, Safari, Chrome, Edge) verifies each receives the identical unmangled URL.

Coverage, 139 checks total, three consecutive full runs green:

  • bash launcher matrix (71): preference precedence (conf vs env var vs flags, last flag wins), legacy pre-PR studio.conf, garbage conf values, fast path, cold start with and without TTY, lock contention (two launchers, one server), root-id mismatch and dead/unhealthy port refusal, timeout prints no false URL, default-mode port scanning that skips foreign Studios, and per-OS opener fallback chains including the no-opener case
  • installer matrix (19): fresh default writes '1', --no-browser and --browser flips, plain --shortcuts-only refresh preserves the choice (the studio update path), legacy conf migration, garbage value normalization, a conf injection attempt is not preserved verbatim and executes nothing, the interactive prompt driven through a real pty (n, y, Enter, NO, unrecognized input), and non-interactive runs never prompt
  • PowerShell matrix (30): install.ps1 and the generated launch-studio.ps1 both parse with zero AST errors, then the generated launcher executes the same precedence, env var, mismatch, and dead-port cases

One real bug found and fixed in 52acbcc: UNSLOTH_STUDIO_NO_BROWSER=False or Off disabled the browser in the shell launcher because the falsy check was case-sensitive, while the PowerShell launcher's -notin is case-insensitive. The value is now lowercased before matching and the launcher test pins it.

Everything else passed unchanged, including the guarantee that a default install with no flags, no env var, and Enter at the prompt opens the browser exactly once, same as before this PR.

The shell installer test step runs a hardcoded list, so the new
tests/sh/test_launcher_no_browser.sh was only bash -n parsed by lint
and never executed. Add it to the list; it only reads install.sh and
install.ps1 and writes to mktemp sandboxes, so it fits the step's
no-writable-tree constraint.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf3a6f1a51

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread install.sh
# Ask once (interactive installs only) whether the launcher should open
# the browser after the server is up. Skipped when --no-browser/--browser
# was passed or no TTY; then an existing choice is kept, defaulting to on.
if [ -z "$_STUDIO_OPEN_BROWSER" ] && [ -t 1 ] && [ -r /dev/tty ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the saved browser preference on reinstall

When a user previously chose no-browser (STUDIO_OPEN_BROWSER='0') and later reruns the normal interactive installer/update without flags, this prompt still runs before create_studio_shortcuts has a chance to read the existing studio.conf; accepting the default answer sets _STUDIO_OPEN_BROWSER=1, and the subsequent shortcut rewrite overwrites the saved no-browser choice. This makes the persisted preference fragile for the documented reinstall/update path; the prompt should be skipped or preseeded when an existing preference is already present.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, confirmed correct. The prompt ran before the preserve logic could see the existing studio.conf, so Enter on an interactive reinstall flipped a saved 0 back to 1. Fixed in 0d0425c: the prompt default is now seeded from the existing preference (studio.conf on macOS/Linux/WSL, the value baked into launch-studio.ps1 on Windows) and the hint flips to [y/N] when the saved choice is 0, so Enter always keeps the current setting while explicit y/n answers still override. Verified by driving the extracted prompt block through a real pty: Enter over a saved 0 now yields 0, plus the override and fresh-install cases, and the equivalent default derivation on the PowerShell side against a generated launcher baked with 0. Full simulation suites re-ran green (146 checks).

An interactive reinstall over an install that had persisted
STUDIO_OPEN_BROWSER='0' would flip it back to 1 when the user pressed
Enter, because the prompt default was hardcoded to yes and the answer
then overrode the preserve logic. Seed the prompt default from the
existing preference (studio.conf on macOS/Linux/WSL, the value baked in
launch-studio.ps1 on Windows) and flip the hint to [y/N] accordingly.
Explicit y/n answers still override.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d0425c8c4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread install.sh
Comment on lines +88 to +89
--no-browser) _STUDIO_OPEN_BROWSER=0 ;;
--browser) _STUDIO_OPEN_BROWSER=1 ;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward browser flags into WSL reroutes

When this installer runs from an AMD WSL distro that _maybe_reroute_strixhalo_to_2404 reroutes to Ubuntu-24.04, this parsed preference is dropped: the child command only forwards package/python/verbose/tauri args in install.sh:1663-1673, so ./install.sh --no-browser or --browser in the origin distro installs the target distro with the default/prompted browser setting instead of the requested noninteractive choice. Forward _STUDIO_OPEN_BROWSER into _rr_args so the documented flag works on the rerouted install path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed correct. The reroute only forwarded package/python/verbose/tauri, so an explicit --no-browser or --browser was dropped when the origin distro handed off to Ubuntu-24.04. Fixed in 86200cb: an explicit choice is appended to _rr_args; an undecided install still forwards nothing so the rerouted side keeps its own default. Verified by extracting the _rr_args block and driving it with all three preference states.

grep -q exiting on first match SIGPIPEs the echo feeding it when the
haystack is the whole installer, spamming 'write error: Broken pipe'
in the CI job log. Feed grep from here-strings instead.
@ehlesp

ehlesp commented Jul 9, 2026

Copy link
Copy Markdown

Mike asked me to test this new "no-browser" feature in the forum thread where I suggested it, so here is my summary report about my tests in different setups:

Testing Environments

  • Kubuntu 24.04, with KDE Plasma 5.27 as desktop.

  • Linux Mint 22.3 "Zena", with Cinnamon 6.6 as desktop.

  • Windows 11.

Commands executed in Linux environments

Used the install.sh and uninstall.sh scripts available in the PR itself:

# Regular interactive installation:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh

# Installation with `--no-browser` option already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh -s -- --no-browser

# Installation with `--browser` option already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh -s -- --browser

# Installation with `--shortcuts-only --no-browser` options already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh -s -- --shortcuts-only --no-browser

# Installation with `--shortcuts-only --browser` options already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh -s -- --shortcuts-only --browser

# Regular uninstallation:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/scripts/uninstall.sh | sh

Commands executed in Windows 11 environment

Used the install.ps1 and uninstall.ps1 scripts available in the PR itself:

# Regular interactive installation:
irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.ps1 | iex

# Installation with `--no-browser` option already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.ps1) } --no-browser"

# Installation with `--browser` option already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.ps1) } --browser"

# Installation with `--shortcuts-only --no-browser` options already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.ps1) } --shortcuts-only --no-browser"

# Installation with `--shortcuts-only --browser` options already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.ps1) } --shortcuts-only --browser"

# Regular uninstallation:
irm https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/scripts/uninstall.ps1 | iex

Important

Notice how the commands with arguments are different from the ones without them!
They are based on the explanations given in this particular answer to this Stack Overflow question.

Results

Overall, the new "no-browser" feature works as expected but with the following minor issues:

  • After the installer starts the server, the default browser is NOT auto-opened into Unsloth Studio when the "open browser" feature is left enabled.

  • On Linux environments, the commands with the --shortcuts-only option work but report a curl error in their output:

    $ curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/0d0425c8c49dbbc88a444944af51b59782734e37/install.sh | sh -s -- --shortcuts-only --no-browser
    
      🦥 Unsloth Studio Installer
      ────────────────────────────────────────────────────
    
      platform       linux
                    Created Unsloth Studio shortcut
    curl: (23) Failure writing output to destination

    The desktop shortcuts work and the variable STUDIO_OPEN_BROWSER is correctly updated, so I don't know what that curl error is truly reporting about.

…ser open

Three fixes from PR review and field testing:

- Forward an explicit --no-browser/--browser choice into the WSL Strix
  Halo reroute so the rerouted install honors the flag.
- Drain piped stdin before the --shortcuts-only early exit so
  curl | sh -s -- --shortcuts-only no longer dies with curl error 23.
- Open the browser after the installer's own foreground launch when the
  preference is on: a background watcher polls /api/health, verifies
  the per-install studio_root_id so a different Studio on the port is
  never opened, then opens the URL once. Mirrored in install.ps1 with a
  Start-Job watcher. When the preference is off nothing changes; the
  server already prints its URL.
@shimmyshimmer

Copy link
Copy Markdown
Member Author

Thanks for the thorough field test across Kubuntu, Mint, and Windows 11, and for the correct piped-argument invocations. Both findings were reproducible and are fixed in 86200cb:

  1. Browser now opens after the installer's own launch. The "Start Unsloth Studio now?" path execs the server directly and never had any browser-open logic (that lived only in the desktop launcher), which is why nothing opened even with the feature enabled. The installer now starts a small background watcher before launching: it polls /api/health, verifies the per-install studio_root_id so a different Studio already sitting on port 8888 is never the one opened, then opens the URL exactly once. Same behavior on Windows via a Start-Job watcher in install.ps1. With the preference off, nothing extra happens; the server still prints its URL for PWA use.

  2. The curl (23) error on --shortcuts-only is gone. The shortcuts-only path exits early while curl is still streaming the rest of the script into sh, so curl died with EPIPE. That also made the whole curl | sh pipeline exit non-zero under pipefail even though the shortcuts were written correctly. The early exit now drains piped stdin first (skipped when stdin is a terminal). Reproduced the error locally with a file:// pipe, confirmed the fix removes it and that reverting the drain brings it back.

Also forwarded explicit --no-browser/--browser flags into the AMD WSL distro reroute per the other review note. Full simulation suites re-ran green (162 checks) plus the parity and installer-shape pytest guards.

@ehlesp

ehlesp commented Jul 10, 2026

Copy link
Copy Markdown

@shimmyshimmer, I've tested in the same environments the fixes for the issues I reported.

Testing Environments

  • Kubuntu 24.04, with KDE Plasma 5.27 as desktop.

  • Linux Mint 22.3 "Zena", with Cinnamon 6.6 as desktop.

  • Windows 11.

Commands executed in Linux environments

Used the fixed install.sh script available in the PR itself, also the uninstall.sh script for cleaning up:

# Regular interactive installation:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.sh | sh

# Installation with `--no-browser` option already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.sh | sh -s -- --no-browser

# Installation with `--browser` option already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.sh | sh -s -- --browser

# Installation with `--shortcuts-only --no-browser` options already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.sh | sh -s -- --shortcuts-only --no-browser

# Installation with `--shortcuts-only --browser` options already set by command argument to the installer:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.sh | sh -s -- --shortcuts-only --browser

# Regular uninstallation:
curl -fsSL https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/scripts/uninstall.sh | sh

Commands executed in Windows 11 environment

Used the fixed install.ps1 script available in the PR itself, also the uninstall.ps1 script for cleaning up:

# Regular interactive installation:
irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.ps1 | iex

# Installation with `--no-browser` option already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.ps1) } --no-browser"

# Installation with `--browser` option already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.ps1) } --browser"

# Installation with `--shortcuts-only --no-browser` options already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.ps1) } --shortcuts-only --no-browser"

# Installation with `--shortcuts-only --browser` options already set by command argument to the installer:
iex "& { $(irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/install.ps1) } --shortcuts-only --browser"

# Regular uninstallation:
irm https://raw.githubusercontent.com/unslothai/unsloth/86200cb0bef9fc9ce55fc37b78048cc70a83ef4a/scripts/uninstall.ps1 | iex

Important

Notice how the commands with arguments are different from the ones without them!
They are based on the explanations given in this particular answer to this Stack Overflow question.

Results

  • In Linux environments, the fixed install.sh script:

    • Auto-opens the default browser into Unsloth Studio if the feature is left enabled in the installer question or with the --browser argument.
    • Does not auto-open the default browser into Unsloth Studio when the feature is disabled in the installer question or with the --no-browser argument.
    • Does not return a curl error with the --shortcuts-only argument.
  • In Windows 11, the fixed install.ps1 script:

    • Auto-opens the default browser into Unsloth Studio if the feature is left enabled in the installer question or with the --browser argument.
    • Does not auto-open the default browser into Unsloth Studio when the feature is disabled in the installer question or with the --no-browser argument.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants