Files
Netcatty/resources/mosh/README.md
陈大猫 7dd25a55bb Bundle mosh-client + Node-side PTY handshake
* Bundle mosh-client via CI build pipeline

Add a GitHub Actions workflow that builds a static, distro-portable
mosh-client for linux-x64, linux-arm64, darwin-universal (arm64+x86_64)
from upstream mobile-shell/mosh source, plus a pinned win32-x64 binary
sourced from FluentTerminal (GPL-3.0). Releases attach SHA256SUMS so
scripts/fetch-mosh-binaries.cjs can verify and pull the right binary
into resources/mosh/<platform-arch>/ during npm run pack.

electron-builder.config.cjs gains a moshExtraResources() helper that
adds the binary to extraResources only when present on disk, keeping
local dev packages working without bundled mosh.

terminalBridge.cjs now exports bundledMoshClient() and prefers the
bundled static client over whatever the system mosh wrapper would
resolve via PATH (via the MOSH_CLIENT env var). The Windows branch
throws a clear error pointing at Settings instead of silently falling
back to a literal "mosh.exe" string when no wrapper is installed.

This is Phase 1 — Phase 2 (follow-up) replaces the FluentTerminal
Windows binary with an in-CI Cygwin static build and adds a Node-side
mosh-server bootstrap so Mosh works out-of-the-box on Windows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Phase 2: Node-side Mosh handshake (no Perl wrapper required)

Reimplement what the upstream Mosh Perl wrapper does in pure Node:
spawn `ssh [user@]host -- mosh-server new`, sniff the byte stream
for `MOSH CONNECT <port> <key>`, then spawn `mosh-client` locally
with MOSH_KEY in the environment.

The new electron/bridges/moshHandshake.cjs module exposes the parser,
sniffer, and command builders as pure functions so they can be unit
tested without spawning real ssh. terminalBridge.startMoshSession now
prefers this path whenever a bare mosh-client (bundled, explicit, or
system) and ssh (in-box OpenSSH on Win10 1809+, system everywhere
else) are both detectable. The legacy path through the system mosh
Perl wrapper is preserved as a fallback so users with custom mosh
setups don't regress.

Auth is delegated to system ssh, so keys, agent, ssh_config, and
known_hosts all keep working. Password / 2FA need a controlling TTY
which the bootstrap doesn't provide; affected users keep the legacy
wrapper path until interactive UI lands.

Tests:
- moshHandshake.test.cjs (20 tests) — parser corner cases, command
  builders, sniffer split-chunk handling, ring-buffer trim, exec
  resolver
- terminalBridge.bareMoshClient.test.cjs (4 tests) — explicit-path
  basename gating

317 → 341 passing tests; lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Phase 3: in-CI Cygwin Windows build + visible PTY handshake

Phase 3a — in-CI Cygwin Windows build
- scripts/build-mosh/build-windows.sh builds mosh-client.exe from
  upstream mobile-shell/mosh source inside Cygwin, then walks the
  cygcheck import graph to bundle every required Cygwin DLL
  (cygwin1.dll, cygcrypto, cygprotobuf, cygncursesw, etc) into a
  tar.gz alongside the exe.
- The `build-mosh-binaries` workflow swaps the FluentTerminal-pinned
  fetch job for a real Cygwin build (windows-latest + cygwin-install-
  action). fetch-windows.sh is preserved as an emergency fallback but
  no longer wired into the matrix.
- fetch-mosh-binaries.cjs unpacks the tar.gz into resources/mosh/
  win32-x64/ so mosh-client.exe sits next to its DLLs.
- mosh-extra-resources.cjs ships the entire win32-x64/ dir
  (exe + DLL bundle) into Resources/mosh/, so the packaged installer
  runs on a stock Windows host with no Cygwin install.

Phase 3b — visible PTY handshake (password / 2FA prompts)
- terminalBridge.startMoshSession now spawns ssh inside node-pty so
  the user sees and can answer password / 2FA / known-hosts prompts
  in their terminal. When `MOSH CONNECT` is sniffed from the byte
  stream, session.proc is atomically swapped from the ssh PTY to a
  freshly-spawned mosh-client PTY. The MOSH CONNECT line itself is
  redacted from the visible output.
- writeToSession / resizeSession read session.proc lazily, so input
  arriving after the swap goes to mosh-client without extra wiring.
- The ZMODEM sentry is recreated for the new proc since its
  writeToRemote closure captured the previous handle.
- Removes the earlier non-PTY child_process.spawn handshake — the
  PTY-based one supersedes it.

Phase 3c — win32-arm64 deferred
- Cygwin's arm64 port has no stable cygwin1.dll release yet, so we
  do not attempt an arm64 Windows build. arm64 Windows installs fall
  through to the legacy `mosh` wrapper path that the bridge already
  handles. Documented in the workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Allow branch/PR pushes to test the mosh-binaries workflow

Mirrors the build-packages workflow change in #868: any push or PR
that touches the mosh build pipeline triggers the matrix (artifacts
only, no release), while only `mosh-bin-*` tag pushes (or an
explicit workflow_dispatch with release_tag) publish a release.

`paths` filter keeps unrelated commits from running this expensive
workflow (~30min for the Cygwin leg). Concurrency group cancels
superseded branch/PR builds; tag builds use a unique group so a
follow-up commit can't kill an in-progress release.

Release job's `if:` enforces the same rule independently — even if
the trigger gets re-broadened, branches/PRs can't leak a release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix mosh binary workflow runners

* Fix Windows mosh workflow invocation

* Keep shell scripts LF in workflow checkouts

* Trigger mosh workflow on attributes changes

* Fix mosh build tool dependencies

* Fix Linux mosh static build

* Fix macOS mosh build tool lookup

* Skip macOS ncurses terminfo install

* Fix mosh PR review findings

* Allow Linux system mosh dependencies

* Fix Windows mosh DLL bundling

* Limit bundled Windows mosh DLLs

* Honor configured PATH for mosh handshake

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:25:57 +08:00

4.1 KiB

Bundled mosh-client

This directory holds the network-protocol-only mosh-client binary bundled with the Netcatty installer. Netcatty drives the ssh + mosh-server bootstrap itself and then launches this bundled client directly (see electron/bridges/moshHandshake.cjs and electron/bridges/terminalBridge.cjs).

How binaries land here

  1. .github/workflows/build-mosh-binaries.yml builds mosh-client on a workflow_dispatch or mosh-bin-* tag push. It uses scripts/build-mosh/{build-linux,build-macos,build-windows}.sh to produce one binary per target from upstream mobile-shell/mosh source:

    target provenance
    linux-x64 upstream source, manylinux2014, static third-party deps + glibc
    linux-arm64 upstream source, manylinux2014, static third-party deps + glibc
    darwin-universal upstream source, lipo arm64 + x86_64, macOS system dylibs only
    win32-x64 upstream source, Cygwin GCC, ships with bundled Cygwin DLLs
    win32-arm64 (not built — Cygwin arm64 port not yet stable)

    fetch-windows.sh is preserved as an emergency fallback that pulls the FluentTerminal-pinned binary; it's no longer wired into the default workflow.

  2. The release built by that workflow gets a tag like mosh-bin-1.4.0-1, with SHA256SUMS attached.

  3. Release packaging sets MOSH_BIN_RELEASE=mosh-bin-1.4.0-1 and runs npm run fetch:mosh to pull the binaries into resources/mosh/<platform-arch>/. For local packaging, set MOSH_BIN_RELEASE yourself before running the same fetch command. electron-builder.config.cjs then copies the matching binary into Resources/mosh/mosh-client[.exe].

The directory is otherwise empty (binaries are gitignored).

Licenses

  • Mosh itself is licensed under GPL-3.0 (https://github.com/mobile-shell/mosh).
  • Netcatty is GPL-3.0, so redistribution as part of the installer is permitted.
  • The Windows binary is built in CI from upstream https://github.com/mobile-shell/mosh @ tag MOSH_REF (default mosh-1.4.0) using the Cygwin GCC toolchain. The bundled DLLs are redistributable Cygwin runtime libraries — see mosh-client-win32-x64-dlls/README.txt (generated by the build) for the per-DLL license listing.
  • Bundled/static deps (OpenSSL Apache-2.0, protobuf BSD-3-Clause, ncurses MIT) are compatible with GPL-3.0.

Reproducible build

To reproduce the binaries locally:

docker run --rm -v $PWD:/workspace -w /workspace \
  -e MOSH_REF=mosh-1.4.0 -e ARCH=x64 -e OUT_DIR=/workspace/out \
  quay.io/pypa/manylinux2014_x86_64 \
  bash scripts/build-mosh/build-linux.sh

For macOS the build needs an Xcode toolchain; see scripts/build-mosh/build-macos.sh.

Phase 2/3 — done in this PR

  • electron/bridges/moshHandshake.cjs reimplements the upstream Mosh Perl wrapper in Node: parser + sniffer + command builders as pure functions.
  • terminalBridge.startMoshSession runs the SSH bootstrap in a node-pty so password / 2FA / known-hosts prompts render naturally in the user's terminal, then swaps session.proc from the ssh PTY to a freshly-spawned mosh-client PTY when MOSH CONNECT is detected. Keystrokes that arrive after the swap go to mosh-client because writeToSession reads session.proc lazily.
  • Preferred whenever a bare mosh-client (bundled / explicit / system) and ssh (in-box OpenSSH on Win10 1809+, system everywhere else) are both detectable. The legacy path through the system mosh Perl wrapper is preserved as a fallback so existing setups don't regress.
  • Windows binary built in-CI from upstream source via Cygwin GCC; ships alongside cygwin1.dll + transitive deps so it runs on a stock Windows machine without a Cygwin install.

Roadmap

  • Cygwin arm64 port stabilizes → add a build-windows-arm64 matrix leg using the same build-windows.sh script.
  • Make MOSH_REF track upstream release tags automatically.