Each patch bails out with a console.log and process.exit(0) when its anchor
string is missing. The write-back happens at the end of the file, so an upstream
release that moves any one anchor drops every patch, exits successfully, and
leaves postinstall reporting nothing wrong. Termix then builds and starts
normally and drops VNC/RDP sessions at runtime — with no signal pointing at the
patch.
Every patch here is required for correctness: protocol negotiation, the guacd
1.6.0 name handshake, dynamic argument answering, UTF-8 tokens, read-only joins.
A missing anchor means the patch no longer applies, so exit non-zero and say
which one and what to do.
Unchanged: a missing guacamole-lite still skips quietly, and an already-patched
tree still exits 0.
The test mocks db, logger and AuthManager, but the route module also calls
PermissionManager.getInstance() at import time and pulls in the repository
factory, which loads the drizzle schema and the better-sqlite3 native binding.
Importing that costs seconds when the full suite runs its projects
concurrently, and the test times out at 5s. On its own it passes, so it read as
flaky rather than as a missing mock.
Mock both. None of it is under test here, and the file now imports in
milliseconds regardless of load.
A control string (OSC/DCS/APC/PM) carries text that must never be displayed —
an OSC 0 title holds the user, host and path, and PROMPT_COMMAND emits one on
every prompt. Its opener and its terminator routinely land in different
websocket frames, and the continuation frame contains no escape byte at all, so
every guard in the highlighter misses it: TUI_SEQUENCE, CONTROL_STRING_SEQUENCE
and hasIncompleteAnsiSequence all only look at one chunk.
Highlighting that continuation injects an SGR sequence into the middle of the
open string, which aborts it early in xterm.js and prints the remainder as
ordinary text — the stray ~/path glued to the prompt, and the cursor arithmetic
drift behind the duplicate prompts and Ctrl+R corruption.
Track the state across chunks the way alternate-screen mode already is, and skip
any chunk that starts or ends inside a control string. A trailing lone ESC counts
as inside, since its meaning only arrives with the next chunk.
ClosesTermix-SSH/Support#1025
A tap discovers casks in a top-level Casks/ directory. The cask sat in
packaging/Casks/, so tapping the repository succeeded and every subsequent
brew install --cask termix reported that no cask with that name exists.
Move it and repoint the five workflow references. The release job still rewrites
the version and checksum in place, and the electron job still copies it into the
generated and submission trees.
ClosesTermix-SSH/Support#1044
resolveConnectionOrigin() pins RDP/VNC/Telnet to "remote" because the embedded
desktop backend does not bundle guacd, and the Guacamole websocket already
follows that. The status check and both token calls did not: they use the shared
authApi, which in Electron is hard-coded to the embedded backend.
So the desktop app asked the backend without guacd whether guacd was available,
got "disconnected", and refused to connect — while the connected server it would
actually have used reports it as connected and serves the same host fine from the
web client.
Send those three calls through a remote-origin instance in Electron, alongside
the existing file-manager, tunnel and stats ones.
ClosesTermix-SSH/Support#1043
The session recording section offers a recording path, a filename template and
four content toggles, but the backend overwrote five of the six on every
connection. A host could set none of them and get no indication why.
Location and filename genuinely are not the host's to choose — recordings are
indexed by them for playback and the backend refuses to read outside its
recordings directory — so drop those two inputs rather than keep pretending they
apply. The content flags are a host-level decision, so default them instead of
forcing them.
That still leaves the reported case, where guacd writes the file somewhere the
backend cannot see it. The warning now reports both paths and names the two env
vars that align them, which is otherwise guesswork for a split-container setup.
ClosesTermix-SSH/Support#1041
The host editor stores width and height in guacamoleConfig, and the backend
passes them to guacd in the connection token. The renderer then appends its own
width and height query parameters measured from the container, which take
precedence, so a configured resolution never reached the session — only dpi did,
because that was the one display field GuacamoleApp read back.
Pass the configured width and height alongside dpi, and skip the container-driven
sendSize on connect and on resize when a resolution is pinned. rescaleDisplay
still fits the fixed display into the available space.
ClosesTermix-SSH/Support#1039
Shared hosts hide their edit, share and delete actions based on the recipient's
permission level, but the sidebar row stays draggable regardless. Dropping one on
a folder issues a bulk folder update the server rejects, so a recipient without
edit rights gets a failure toast for an action the UI offered them.
Gate draggable on canEditHost, and skip hosts the recipient cannot edit in the
move handler so a mixed selection moves what it can instead of failing whole.
ClosesTermix-SSH/Support#1011
When the data directory holds no database, startup treats it as a first run and
silently creates an empty one. A deployment that loses DATA_DIR — an .env file
the service no longer loads, a volume that did not mount — lands in exactly that
state, so the user is asked to register an admin account again while the real
database sits untouched one directory over. It is indistinguishable from the
upgrade having deleted everything.
Check the known data locations before creating a new database and refuse to
start when one of them already holds a database, naming both directories.
ALLOW_EMPTY_DATA_DIR=true starts anyway for anyone deliberately starting over.
This matches how a failed decryption already behaves: it throws rather than
falling back to an empty database.
ClosesTermix-SSH/Support#1006
verifyOIDCToken passed the raw id_token straight to jose's jwtVerify, which
throws JWSInvalid when the token is not a three-segment compact JWS. Authentik
issues an encrypted JWE id_token when the provider has an encryption key set,
so the callback threw and every OIDC login failed with 'Invalid Compact JWS'.
2.5.0 hid this behind a catch-all that decoded the unverified payload; removing
that fallback fixed the trust bug but turned the pre-existing verification
failure into a hard login failure.
Check the segment count before verifying and raise a distinct
OIDCTokenFormatError, which the callback treats as 'no usable claims here' and
falls through to the userinfo endpoint. Signature and claim failures still
reject the login.
FixesTermix-SSH/Support#1016FixesTermix-SSH/Support#1018