* Support JumpServer jms:// client launch links
Register Netcatty as an optional jms:// protocol handler (opt-in,
default off to avoid conflicting with the official JumpServer client).
SSH/SFTP connection payloads decode to an ephemeral host that connects
to the JumpServer gateway with the one-time connection token, without
saving anything to the vault.
Fixes#1958
* Mark jms deep link hosts as ephemeral for session restore exclusion
* Open the SFTP side panel for JumpServer sftp launch links
jms:// payloads with protocol "sftp" previously landed in a plain
shell. Mark the ephemeral host with an autoOpenSftpPanel hint that the
session factory forwards as autoOpenSidePanel, and let TerminalLayer
open the SFTP side panel for that session once it connects.
* Support JumpServer telnet deep links
* Keep JumpServer telnet links on SSH gateway
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Support password in ssh deep links with ephemeral connect
ssh://user:password@host URLs now connect directly using the URL
credentials without saving the host to the vault, covering bastion
one-time password flows. Password-less links keep the existing
match-or-prefill behavior.
Fixes#1890
Co-authored-by: Cursor <cursoragent@cursor.com>
* Keep ephemeral deep-link hosts out of vault update paths
Terminal-layer host updates (e.g. the SFTP follow-CWD toggle) map over
the merged terminal host list, which could persist an ephemeral host
and its plaintext password into the vault. Route terminal-layer host
updates through a splitter that applies ephemeral entries to in-memory
state only and forwards just the vault hosts to updateHosts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Exclude ephemeral deep-link sessions from session restore
Sessions backed by an in-memory ephemeral host cannot survive a
relaunch (the one-time password only lives in memory), so persisting
them would leak the ephemeral host metadata into restore storage and
restore a tab that can never reconnect. Mark ephemeral hosts and their
sessions, and filter them out of the session-restore payload.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Treat ephemeral hosts as unsaved in appearance classification
The terminal theme/font panel and pane appearance resolution decided
"saved vault host" via hostMap membership. Ephemeral deep-link hosts
are now in that map, so theme/font changes were routed to the
persisted-host updater and silently dropped. Classify via
isSavedVaultHost (ephemeral flag aware) so those changes fall back to
the session/global setting path as before.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Reuse saved host settings for password deep links and keep ephemeral marker on clones
Password deep links now run saved-host matching first: a uniquely
matched vault host contributes its non-credential settings (proxy,
jump chain, charset, group, ...) to the ephemeral host while the URL
credentials override vault identities and key references. Session
clones (split/copy/copy-to-new-window) now carry the ephemeralHost
marker so cloned tabs stay excluded from session restore.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Block group credential inheritance on password deep-link ephemeral hosts
buildSshDeepLinkEphemeralHostFromSaved now takes the group-resolved
effective host and clears group/identityFilePaths in addition to
identity references, so a later resolveEffectiveHost pass cannot
re-inherit group default credentials (identity, key, password) over
the one-time URL password. Non-credential group defaults (proxy,
jump chain, charset, ...) are preserved via the effective host.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Route ephemeral-host font zoom through the per-session font-size path
Ctrl+zoom on a non-workspace ephemeral deep-link session hit the
saved-host update path, where the vault updater silently dropped the
change (ephemeral ids are not in the persisted hosts array). Zoom and
the theme panel font-size slider/reset now store the size on the
session (rendered via applySessionFontSizeToHost) for ephemeral
hosts, matching the workspace-pane behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix ephemeral session font zoom routing
* Prevent ephemeral deep-link sudo autofill
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(terminal): detach stale keychain identity when saving retry credentials
When a host references a Keychain identity (host.identityId) with wrong
credentials, re-entering credentials via the auth-retry dialog's
"Continue & Save" saved them onto the host but kept the identity
reference. On reconnect, resolveHostAuth prefers identity credentials
over host fields, so the stale keychain credentials kept failing.
Clear identityId when the user explicitly saves new credentials so the
saved username/password (or key) actually takes effect.
Fixes#1956
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(terminal): keep saved retry credentials over group-inherited identity
Writing identityId: undefined is treated as "unset" by applyGroupDefaults,
so a group-level identity would be re-inherited on reconnect and shadow the
credentials saved from the auth-retry dialog. Store an empty string instead
and register identityId in EMPTY_STRING_OVERRIDES_GROUP_DEFAULT so the
explicit "no identity" override sticks. All consumers check identityId
truthiness, so "" behaves as "no identity".
Addresses Codex review on #1970.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(terminal): raise SSH flow high-water mark to stop WAN pause ping-pong (#1961)
Bulk terminal output over SSH (e.g. `tail -2000f big.log`) was far slower
than other clients on high-latency links. The renderer's output flow control
pauses the source stream once its unacked backlog crosses FLOW_HIGH_WATER_MARK
(previously 100KB). For SSH the source is the ssh2 channel, so pause() stops
the remote until resume() plus a full round-trip.
ssh2's own channel window is 2MB (WINDOW_THRESHOLD 1MB), so a 100KB watermark
made Netcatty pause/resume dozens of times during a multi-MB dump — a 4MB dump
triggered ~61 pause cycles, each costing ~1 RTT. At 300ms RTT that turned a
~2s dump into ~21s.
Raise FLOW_HIGH_WATER_MARK to 1MB so it sits near the ssh2 window; the same
4MB dump now settles in ~4 pause cycles (~2-3s at 300ms RTT) while still
bounding renderer memory. FLOW_LOW_WATER_MARK stays at 8KB to keep interactive
acks responsive.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(terminal): align deferred-ack test with 1MB flow high-water mark
The deferred IPC ack batch threshold (XTERM_WRITE_CALLBACK_BATCH_BYTES)
now sits far below FLOW_HIGH_WATER_MARK, so deferral alone can no longer
accumulate up to the watermark. Assert the batch-flush invariant instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix directory tracking setup after su / sudo su (#1942)
After switching users in the terminal (e.g. `sudo su -`), SFTP directory
following stops: the new user's shell has no OSC 7 prompt hook, and the
backend pwd probe runs on an exec channel owned by the login user, which
cannot read another user's /proc/<pid>/cwd. The advertised remedy — the
OSC 7 setup action — also failed in this exact scenario because its silent
exec-channel script cannot verify or configure a foreground shell owned by
another user.
The setup script now detects that the active foreground shell belongs to a
different uid, reports the target shell via a marker (exit 5), and the
renderer falls back to typing the setup command into the terminal itself,
where it runs as the target user. The config lands in that user's rc file,
so OSC 7 reporting resumes immediately and persists for future su sessions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address Codex review: stage typed setup remotely, forward shell-local config paths
The typed fallback previously pasted the whole multi-line setup script into
the terminal, which could leave many entries in the target user's bash
history when cmdhist is off (the appended history -d only removes the last
one). The setup script is now staged into a self-deleting remote temp file
over the silent exec channel, and the typed command becomes a short single
line — one history entry that the existing bash cleanup reliably removes.
The typed wrapper also forwards shell-local (possibly unexported) ZDOTDIR /
XDG_CONFIG_HOME to the child sh via the NETCATTY_* overrides the script
already honors, so zsh users with a custom config dir get the prompt hook in
the right rc file (mirrors buildOsc7SetupCommand).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address Codex review: hash-verify the staged setup script before the su shell runs it
The staged temp file stays writable by the login user until the typed
command runs, so a same-uid process could swap its contents in that window
and have the su-target (e.g. root) shell execute arbitrary code. The typed
runner now reads the file into memory once, deletes it, verifies the
renderer-computed SHA-256 of the in-memory copy, and only then pipes that
same copy to sh — no TOCTOU window between verification and execution.
Verification failure prints an error instead of running anything.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
The "Authentication failed…" retry prompt and progress-log line in
createTerminalSessionStarters were hardcoded English and bypassed i18n,
so they rendered in English regardless of the selected language.
Route both through the existing tr() helper and add
terminal.auth.retryMessage / terminal.auth.retryLog to en, ru, zh-CN
and zh-TW.
Every output chunk was walked char-by-char up to four times in JS
(sync-block filter, erase-scrollback rewrite, replay-safe log sanitizer,
line-timestamp segmenter) plus two more per-char scans for long-line
pressure, before xterm.js ever parsed it. Replace those loops with
native indexOf/regex hops and single-slice appends, and gate the
quadratic pending-suffix scan in the sync filter behind a cheap
incomplete-escape check. Output is byte-for-byte identical (fuzzed
old-vs-new over 4000 mixed chunks); colored-output floods drop from
~350ms to ~0.03ms per 64KB batch in the sync filter alone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix terminal backlog when window is unfocused in background.
Extend the background write fast path to occluded-but-visible windows (common on Windows Alt+Tab), skip wasteful repaints during that path, and flush any remaining coalescer/queue backlog when the app returns to foreground.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix resume hook wiring and keep unfocused repaint in fast path.
The onWindowShown resume flush never fired because useTerminalBackend did not expose the bridge listener. Also restore scheduleTerminalRepaintWhenUnfocused on the background fast path: unfocused-but-visible windows have no rAF render loop, so skipping it froze visible output (#1761 regression).
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>