**Problem**
sbt compile through the thin client prints only elapsed time: 2 s, where the
same command in the shell or under sbt --server prints
elapsed time: 2 s, cache 94%, 13 disk cache hits, 24 remote cache hits. The
summary is computed server-side and thrown away: ManagedLogger.success is gated
on Terminal.isSuccessEnabled, and a NetworkChannel's terminal answered
interactive.get || isInWatch, which is false whenever a command is passed on
the command line. The line the user sees is the client's own, built from
wall-clock alone.
**Solution**
Report the line from the server, and have the client print only when the server
will not.
NetworkChannel's terminal reports isSuccessEnabled = true, so the server
writes the line on every channel, and it advertises successLog in its
capabilities. A server from before this sends no such key, which reads as false,
so an older client-server pair behaves as it does today; an unattached client
also always prints, since it renders build/logMessage rather than the proxied
systemOut bytes. Older sbtn keeps printing its own line and so reports
success twice until it updates.
Generated-by: Claude Opus 5
Claude-Session: https://claude.ai/code/session_015BqrfmHbJUph6Kx7gJn3G8
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
**Problem**
The client sent the handshake and ignored the answer: responsePlan had
no case for it, so an invalid token was dropped. The channel then stayed
unauthenticated and every later request was refused, with nothing saying
why.
**Solution**
Match the handshake response. On a refusal, read the token file again
and present what it names now, up to handshakeAttemptLimit times, then
report the refusal.
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
**Problem**
If a client cannot reach the server, it deletes the portfile. Then it
starts a second server. The first server is displaced: its socket path
now belongs to the second server.
A displaced server keeps running. A dropIfIdle notification cannot
reach it, because the proc file it registered names the socket that
the second server now owns.
**Solution**
The portfile holds the serverId of whichever server wrote it. Watch
that file, and exit when the id is not this server's. Leave the
portfile to the server that owns it.
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
* fix: restart the sbt server when -D options change
* fix: don't restart on completions, wait for the socket
* fix: only trust -D options recorded for this build
* fix: confirm the server left before reporting it gone
* Update protocol/src/main/contraband/portfile.contra
Co-authored-by: eugene yokota <[email protected]>
* fix: record the server -D options as a list
* fix: record the -D options without their values
The connection file keeps the name of each option and a salted digest of it
rather than the option itself, and the comparison follows the JVM in taking the
last definition of a name. A client that is not allowed to restart the server
says which options it cannot pick up instead of staying quiet about them.
* fix: compare the -D options given before a command
A trailing -D option used to switch the whole comparison off, which dropped the
options written before it, and a shutdown request that never reached the server
waited the full timeout before saying so.
* fix: leave a server no client started alone
The connection file says whether its recorded options are the whole story, so a
server an editor keeps is warned about instead of shut down, while one the client
started with no options is still replaced. A restart that cannot reach the server
warns and carries on rather than failing the invocation.
* build: filter NetworkClient clinit in MiMa
---------
Co-authored-by: eugene yokota <[email protected]>
**Problem**
Channels.newInputStream/newOutputStream share a channel-wide lock,
which would deadlock for a duplex communication.
**Solution**
This implements an alternative DuplexChannels functions that's capable
of duplex communication expected of a "socket".
This also duplicates the Java implementation to the worker app,
so we can use JDK domain socket for forked test communication.
A client disconnecting with a terminal control query outstanding could park a
server thread forever, wedging prompts and command dispatch for every client
(#6841, #6840):
- VirtualTerminal.cancelRequests drained only 2 of the 8 pending terminal
maps, so waiters on the set-echo, raw-mode, attributes, and size queues were
never woken. It now drains all of them, with offer instead of put so the
shutdown path itself cannot block on a full queue.
- Raw-mode requests were registered in the set-echo map, so their waiters were
invisible to any raw-mode-specific handling.
- Closing a channel terminal did not wake readers parked on its input stream;
close now delivers EOF so a prompt blocked on a dead client's input unwinds.
- The failed-load prompt read its answer byte from System.in, which under
non-virtual IO is the process's own stdin and never carries client input; it
now reads the active terminal's input stream.
- ServerSessionImpl.close() could not deliver EOF to the peer while its read
thread was parked in a native read (the native close is never delivered), so
the server never noticed orderly client disconnects at all. It now shuts
down socket input first, which wakes the reader and lets the close through.
Regression test: a raw-protocol client that attaches, triggers the failed-load
prompt without answering the raw-mode query, and disconnects; the server must
shut down cleanly (EOF at the prompt maps to 'q') instead of staying parked
forever. Fails on develop with the server still alive and the command loop
parked in setRawMode; passes with this change. VirtualTerminalSpec pins the
drain across all eight maps and that other channels are untouched.
Generated-by: kimi-code/k3 (Oh My Pi)
**Problem**
sbt bspConfig writes the absolute path of the current Java binary into .bsp/sbt.json. When the user switches Java versions (via sdkman, cs java, etc.) or removes that JDK, the IDE fails to start the sbt BSP server because the hardcoded path is stale or gone.
**Solution**
When an sbt launcher script is available (via `sbt.script` system property or PATH lookup), generate:
"argv": ["/path/to/sbt", "bsp"]
Closes#4399
- Add subscribeToAll to InitializeOption (protocol); default true for backward compatibility.
- CommandExchange: send broadcast notifyEvent/logMessage only to channels with subscribeToAll.
- TestServer: support subscribeToAll parameter for tests; AbstractServerTest: subscribeToAllForTest.
- ClientSubscriptionTest: assert default client receives build/logMessage when command runs.
- Scripted test server/client-subscription: run show name to exercise server client path.
**Problem**
Forked console currently pulls in full Zinc, which includes JLine.
**Solution**
This implements a lighter-weight, full Java ForkConsoleMain,
which no longer depends on JLine.
**Problem**
1. forked console is missing user code from the classpath.
2. forked console still blocks the server.
**Solution**
1. This includes proper products and classpaths to the console.
2. This also implements client-side run for console.
Implements cooperative idle server cleanup for `sbtn` (issue #8610). When a client disconnects from an sbt server, that server notifies all
other registered servers to shut down if they've been idle long enough and have no connected clients. This prevents accumulation of idle
background JVMs across projects.
fixes#8610
Fixes#7469
When running 'sbt bspConfig', the generated .bsp/sbt.json now includes
JVM options from the SBT_OPTS environment variable. This ensures that
options like -Dsbt.boot.directory are propagated to the BSP server.
The parseSbtOpts method extracts -D, -X, and -J prefixed options from
SBT_OPTS and includes them in the BSP connection argv.
** Problem **
The sbtn (sbt thin client) native image on Linux currently depends on glibc because ipcsocket uses JNI for Unix domain sockets. When building with musl for static linking, the JNI library fails to load since musl doesn't support `dlopen`.
** Solution **
Instead of upgrading to ipcsocket 2.x (which isn't ready for production), I created a `UnixDomainSocketFactory` that detects JDK 17+ at runtime and uses the native `java.net.UnixDomainSocketAddress` API directly via reflection. This completely bypasses JNI on modern JDKs.
For older JDKs (8 and 11), the factory falls back to ipcsocket 1.6.3, which is stable and well-tested.
** How It Works **
The factory checks at startup whether `java.net.UnixDomainSocketAddress` is available:
- **JDK 17+**: Uses native NIO Unix domain sockets (no JNI, no native libraries)
- **JDK 8/11**: Falls back to ipcsocket's JNI-based implementation
This approach:
- Enables musl static linking on JDK 17+ without any native dependencies
- Maintains full backward compatibility with older JDKs
- Keeps the stable ipcsocket 1.6.3 instead of the unstable 2.x
Set window title to 'sbt <command>: <org> % <name> % <version>' when
running sbt run, runMain, bgRun, or bgRunMain.
For server-side runs, window title is set directly. For client-side runs (sbtn), window title is passed via RunInfo protocol
and set by NetworkClient.
Fixes#7586
**Problem**
There are a few places where javaHome or java path is set,
using java.home system property. The problem is that it points to JRE,
not JDK, so it would break on Java compilation etc.
**Solution**
If the path ends with jre, go up one directory.
**Problem**
`run` task blocks the server, but during the run the server is just
waiting for the built program to finish.
**Solution**
This implements client-side run where the server creates a sandbox
environment, and sends the information to the client,
and the client forks a new JVM to perform the run.
**Problem**
There are a few places where javaHome or java path is set,
using java.home system property. The problem is that it points to JRE,
not JDK, so it would break on Java compilation etc.
**Solution**
If the path ends with jre, go up one directory.