Repository navigation
Releases: OmnyGrid/omnyshell
Release list
v1.63.3 — early PTY resizes applied; PTY tests de-flaked
Fixed
- A terminal resize sent right after a PTY session opens is no longer lost
(thescript(1)backend, used on macOS and Linux).- Cause: live resize sets the size on the child's tty, whose path the
wrapper records a moment afterscriptstarts. A resize requested before
that (for example a client sending its terminal size as soon as it
attaches) was dropped silently, and the program kept the old geometry
until the next resize. - Now: such a resize is kept and applied as soon as the path is known,
checked every 25ms for up to 5s until the session ends. Only the latest
size is applied. - New
ScriptPtyShellSession.canResizeNowand.hasPendingResizereport
the state.
- Cause: live resize sets the size on the child's tty, whose path the
Tests
- De-flaked the macOS PTY resize test. It resized after a fixed 300ms and
read the size after a fixed 1s. On slow CI runners (macos-15-intel) the
wrapper hadn't recorded the tty path within 300ms, the resize was dropped,
andsttyprinted the original30 100. - The child now waits on stdin and prints its size only when the test says
so, and the test waits until the tty path is known instead of sleeping. - A new test resizes immediately after start, before the path is known, and
checks that the latest of two sizes is applied. - Both pass in 12 parallel runs.
- The Windows PTY tests no longer hang CI.
resize does not throwslept a
fixed 400ms before sendingexit. On a slow runner the shell missed it, the
test timed out, and its winpty session was never closed. The session's
reader and waiter isolates stayed blocked inReadFile/
WaitForSingleObject, so the VM could not exit and the job looped on
"waiting for isolate _readerMain to check in".- Every real-PTY Windows test now kills its session in a tear-down.
- The resize test waits for the shell's answers instead of sleeping.
- The PTY and VM CI jobs now have a
timeout-minuteslimit.
v1.63.2 — no default Content-Type on the Hub's 204/304s
Fixed
- The Hub's own
204/304responses no longer claimContent-Type: text/plain. The Hub serves HTTP through omnyhub ondart:io, which
pre-setsContent-Type: text/plain; charset=utf-8on every response
(dart-lang/sdk#64442). Requires omnyhub ^1.9.2, which removes that default
from204 No Contentand304 Not Modifiedresponses unless a type was set.
A cache in front of the Hub could otherwise merge the bogus type from a304
into a stored page.
v1.63.1 — cached HTTP tunnels keep Content-Type; 304 cookies not shared
Fixed
- Cached HTTP tunnels no longer turn pages into
text/plain. When a
cached response went stale and the target confirmed it with a304 Not Modified, the Hub copied the304's headers onto the stored response. A
DartHttpServertarget sendsContent-Type: text/plain; charset=utf-8on
every304. So after the first revalidation, a cachedtext/htmlpage was
served as plain text, and browsers showed its source. A304now never
changes the stored body'sContent-*fields. - A cookie set on a
304is no longer shared with other consumers. The
same merge stored a304'sSet-Cookiein the cache, replaying one
consumer's cookie (a session id, for example) to every consumer the entry
was served to. The cookie now goes only to the consumer whose request
triggered the revalidation. - Both fixes are in omnyhub 1.9.1, now required (
omnyhub: ^1.9.1).
Tests
-
http_tunnel_cache_test.dartruns through a real Hub, node and Dart
HttpServertarget. It adds three cases:- a
text/htmlpage revalidated three times keepstext/html; - a
304'sSet-Cookiereaches only the consumer that triggered it; - a hit carries the same
Content-Type,Cache-Control,ETag,
Last-Modified,Content-Language, custom headers andDateas the
original.
The first two fail against omnyhub 1.9.0.
- a
v1.63.0 — HTTP tunnel cache and proxy timeouts
Added
-
Response caching for HTTP tunnels:
--cache. An HTTP tunnel can keep an
in-memory response cache on the Hub. Repeated requests for cacheable
responses are then answered by the Hub without reaching the node. Turn it on
with any of:omnyshell tunnel open … --protocol http --cache:tunnel … --cache- the dashboard's HTTP cache / Cache private toggles
openTunnel(cache: TunnelCacheOptions())
The cache follows
Cache-Controlthe way a shared proxy cache does
(RFC 9111):- Stored:
public,max-age,s-maxageorExpires. - Not stored, unless an option allows it: responses without
Cache-ControlorExpires(--cache-default-ttl 5mcaches them), and
privateresponses (--cache-private). - Never stored:
no-store, and responses that set a cookie. - Always checked with the target first:
no-cache. - Skip the cache: requests carrying
Authorization,Rangeor a body.
How it behaves:
- Stale copies with an
ETagorLast-Modifiedare checked with the target
and refreshed by a304. - The consumer's own
no-cache,max-ageandno-storeare honoured, so a
browser hard refresh reaches the target. - Each
Varyvalue is cached separately. - A successful
POST,PUT,PATCHorDELETEdrops its path's copies. - Responses report
X-Cache: HIT | MISS | REVALIDATED | BYPASS | STALEand
Age.
Options:
--cache-size(defaults to the Hub's limit),--cache-max-entry
(default 8 MiB),--cache-private,--cache-default-ttl. -
Hub memory limits for tunnel caches. Caches live only in the Hub's RAM
and are dropped when their tunnel closes.hub start --tunnel-cache-max-per-tunnel(default 32 MiB): a larger
--cache-sizeis lowered to it, and the client is told
(cache: … (requested 1 GiB, limited by the Hub)).hub start --tunnel-cache-max-total(default 128 MiB): shared by
every tunnel, evicting the least recently used entries across them.0disables tunnel caching; tunnels then open without a cache and the
client warns.- The Hub prints its limits at start, and
tunnel list,:tunnel lsand
the dashboard show usage (cache 1.2 MiB/32 MiB · 340 hit / 41 miss).
-
HTTP timeouts for every HTTP tunnel, following common reverse-proxy
behaviour.0disables a limit:--http-response-header-timeout(default 60s): no response head in time →
504 Gateway Timeout.--http-idle-timeout(default 5m): a started response that stalls is cut
off.--http-client-timeout(default 60s): a consumer that doesn't finish its
request head →408 Request Timeout. Idle keep-alive time is not counted.--http-max-duration(off unless given): caps a whole request/response.- A target that closes or refuses the connection before responding →
502 Bad Gateway. - With
--cache, astale-if-errorcopy is served instead of a502/504. - After a failure that consumer connection closes, because a late response
must never answer the wrong request.
-
TunnelCacheOptions,TunnelHttpTimeoutsandTunnelCacheStats, carried on
TunnelOpenRequest/TunnelOpened/TunnelInfo/TunnelHandle. A request for
caching or timeouts on a plain TCP tunnel is rejected withrequires_http.
Changed
- HTTP tunnels now apply the default timeouts. Before, a silent target kept
the consumer waiting indefinitely; now it gets a504after 60s. Pass
--http-response-header-timeout 0(and the other options) to restore the
old behaviour. - Consumers of an HTTP tunnel get a status when the dial fails. If the node
can't reach the target, the consumer now gets502 Bad Gatewayinstead of a
bare connection close. - Requires
omnyhub^1.9.0 (HttpRelay,HttpCache,HttpCacheBudget).
Fixed
X-OmnyShell-Tunnel-Idnow matches the short idtunnel listshows.
Tunnel ids are base64url and can contain-. The header used a short-id
helper that strips-, so for such ids it disagreed with the listed short
id, and it couldn't be passed totunnel close.
Tests
test/integration/http_tunnel_cache_test.dartruns through a real Hub, node
and HTTP target. It covers:- hits shared across consumer connections, with stats in
tunnel list; - private and default-TTL caching, and
Set-Cookie/Authorization
never being cached; - the per-tunnel limit lowering a request, and a Hub with caching disabled;
requires_http;- a real
504from the response-header timeout, and502from a target
that drops or refuses the connection; - the cache's memory returned to the budget on close;
@localtunnels;- pipelined order.
- hits shared across consumer connections, with stats in
- Unit tests:
- size and duration parsing;
- the shared flag rules and messages;
- wire round-trips for the new fields;
:tunnelcache and timeout flags, warnings andlsusage;- the dashboard toggles and cache column.
- 270 of the 272 changed executable lines in
lib/are covered. The two left
are a socket-write error handler and an already-closed guard.
v1.62.0 — HTTP tunnels with forwarding headers
Added
- HTTP tunnels:
--protocol http. A tunnel can now say it carries HTTP
(omnyshell tunnel open <node> <port> --protocol http,:tunnel <port> --protocol http, the dashboard's HTTP headers toggle, or
openTunnel(protocol: TunnelProtocol.http)). The Hub then adds forwarding
headers to every request it relays to the target, keep-alive included:
X-Forwarded-For,X-Real-IP,X-Forwarded-ProtoandX-Forwarded-Ssl
(http/off, orhttps/onwith--secure),X-Forwarded-Host,
X-Forwarded-Port, RFC 7239Forwarded,Via: 1.1 omnyshell-hub, a fresh
X-Request-Id, andX-OmnyShell-Tunnel-Id/-Node/-Owner. Values the
consumer already sent are kept and the Hub's appended, so the target should
trust the right-most entry; the single-valuedX-Real-IP,X-Forwarded-Ssl
andX-Request-Idare set only when absent. Bodies, responses and upgraded
(WebSocket) streams are untouched, and non-HTTP/1.x traffic passes through.
The Hub does the rewriting because only it sees the consumer's address and
terminates its TLS. A Hub that predates this opens the tunnel as plain TCP
and the CLI warns; an unknown protocol from a newer client is rejected with
unsupported_protocol. Plain (tcp, the default) tunnels are unchanged. TunnelProtocol,TunnelInfo.protocol/.scheme,TunnelHandle.protocol
/.scheme/.publicAddress(hubHost), andHubBroker.tunnelViaName.tunnel list,:tunnel lsand the dashboard showhttp://for HTTP
tunnels.
Changed
- Requires
omnyhub^1.8.0, which supplies the header rewriter
(HttpRequestHeaderRewriter+ForwardedHeaders).
Tests
test/integration/http_tunnel_test.dart: realHttpClient→ Hub → node →
HttpServerover one keep-alive connection (including a chunked 300 KB
upload), checking every header; HTTPS via--secure; append behaviour for
client-sent headers; a WebSocket through an HTTP tunnel; an@localHTTP
tunnel; and a plain TCP tunnel leaving HTTP untouched.- A broker-level test that an unknown protocol is rejected; wire round-trips
forprotocolonTunnelOpenRequest,TunnelOpenedandTunnelInfo
(including a legacy Hub's reply);TunnelHandle.publicAddress;:tunnel --protocolparsing, the downgrade warning and bad values; and the dashboard
toggle. Every changed executable line inlib/is covered.
v1.61.2 — IDE deep git scan, dot-files shown, AI abort fix
Added
- Deep git scan in the IDE file tree (
g). Directories used to show a git
status only when they were entirely untracked, so a collapsed folder gave no
hint of the changes inside it. Pressinggin the tree rescans with
git status -ualland marks every folder above a change. A folder whose
changes all share one status shows that status (e.g.?for only untracked
files), a mix showsM, and a conflict below shows!. The status bar
reports how many files and directories changed. Deep mode then stays on for
the rest of the session, so saves and agent edits keep folders up to date.
Changed
- The IDE file tree shows dot-files by default. Entries such as
.github,
.gitignoreand.envwere hidden until.was pressed, so they were easy
to miss. Now they are listed from the start, and.hides them. Only
version-control internals (.git,.hg,.svn) are always left out.
Fixed
- Answering
qat the AI agent's "Abort the AI agent?" prompt now aborts.
After a Ctrl-C,qwas read as "no", so the run kept going. The leftover
confirmation then made the next Ctrl-C abort without asking. - The IDE terminal on a remote node no longer garbles non-ASCII output. Each
output chunk was decoded on its own, so a character such asüsplit across
two chunks showed as��. Output is now decoded across chunks.
Tests
- Coverage raised for the AI client commands, file transfer and
:drive
commands, local commands (:ping,:latency,:tunnel,:tree,:detach),
:ide, the IDE remote workspace, the WebSocket connection and the tunnel
registry. - New optional parameters so tests stay off the real home and terminal:
addFileTransferCommands(driveHome:)sets where:drivekeeps its mount
store,addIdeCommand(launch:)/IdeCommand(launch:)replace the IDE
launcher, andrunIdeApptakesterminal:andloadAiConfig:. Defaults are
unchanged.
v1.61.1 — service reinstall no longer duplicates the runtime
Fixed
service reinstallno longer duplicates the runtime in the service
command. A service installed while omnyshell ran under the Dart VM records
its pub-cache snapshot ahead of<role> start. Reinstalling with no options
reused those arguments as-is and kept the old snapshot. The AOT binary then
ranomnyshell <old snapshot> hub start …, and after an SDK upgrade the VM
ran two snapshots. Withdart_service_manager1.4.0 the registry records the
script apart from the command, so reinstall rebuilds from the command alone.
Reinstalling an affected service once repairs it.service infoshows the full command a Dart VM service runs, including
its script. dart_service_manager 1.4.0 keeps the script out of
entry.arguments, soinfonow printsentry.commandLineinstead.
Changed
- Requires
dart_service_manager^1.4.0.
v1.61.0 — use omnyshell right after installing, Windows prompt fix
The installers can hand over a shell, or a PATH, that already finds
omnyshell, so scripts can use it on the very next line.
Added
--print-env: the installer prints only thePATHsetup on stdout (all
other output now goes to stderr), so
eval "$(curl … | sh -s -- --print-env)"applies it to the calling shell.
install.ps1prints PowerShell syntax, for… | Out-String | Invoke-Expression.
An installer cannot change the shell that started it, so until now only new
terminals foundomnyshell.--shell: once installed, the installer starts a shell that inherits the
updatedPATH. On Linux and macOS it replaces itself with$SHELL. On Windows
it startscmd.exewhen launched frominstall.bat, otherwise PowerShell.--shell-cmd <cmd>: runs<cmd>in that shell first (implies
--shell). With a terminal, the shell stays open afterwards. Without one, as
in CI, only<cmd>runs and the installer exits with its status.- All three are also
OMNYSHELL_PRINT_ENV,OMNYSHELL_SHELLand
OMNYSHELL_SHELL_CMD. install.md has a new "Using omnyshell right after
installing" section.
Fixed
- No more misleading pub warning during install.
dart pub global activate
warned that the pub cache'sbin"is not on your path" and printed an
export PATHline, although the installer adds that directory toPATH
itself. The installer now puts it on its ownPATHbefore activating. - Windows sessions could get a prompt directory of
\r. winpty sometimes
returns the cursor to column 0 before a line's CRLF, so a completion marker
arrived as<token>\r\r\n. The parser dropped one\rand took the other
as the reported directory. A ping (completion only) then overwrote the
prompt's cwd with a carriage return, corrupting the prompt and making TAB
completion'schdirfail. Carriage returns are now stripped from every
marker field. This also made the Windowswinpty_marker_testfail about one
run in seven. That test now waits for each marker instead of sleeping a fixed
800 ms: the Git Bash marker alone takes up to 0.8 s on an idle CI runner.
v1.60.0 — one-command installers and the `l` shortcut
One command installs OmnyShell on Linux, macOS and Windows, and l lists the
current directory on any shell.
Added
-
One-command installers:
install.sh,install.ps1andinstall.bat.
curl -fsSL …/install.sh | sh(Linux, macOS, WSL) or
irm …/install.ps1 | iex(Windows) installs everythingomnyshellneeds,
without asking questions. When Dart is missing it comes from the system's
usual source: Homebrew, Google's apt repository, pacman, winget, Chocolatey
or Scoop, falling back to the official checksum-verified SDK download in the
user's home. An existing Dart older than 3.10.9 is upgraded with whatever
installed it, includingflutter upgrade, asdf, mise and FVM. The installer
then adds git, openssl andscript(Git for Windows and OpenSSL on Windows),
runsdart pub global activate omnyshell, and puts the pub-cachebinon
PATHthrough a marked block in the shell profile, or the userPATHon
Windows. Re-running it updates. Options work as flags orOMNYSHELL_*
variables:--version,--source,--git,--no-tools,
--no-modify-path,--no-sudo,--dart-method,--no-dart-upgrade,
--reinstall-services,--dry-runand--uninstall.install.mddocuments
them, and a new CI workflow runs the installers on Ubuntu, Debian, Fedora,
Arch, macOS and Windows. -
The
lshortcut. Typinglin an interactive session (connect,
resume,local, or any embedder ofInteractiveShellController) lists the directory with hidden entries
and human-readable sizes, translated for the session's shell:ls -alhon
POSIX shells (GNU and BSDlsboth accept it),Get-ChildItem -Forcewith a
1024-basedSizecolumn on PowerShell, anddir /aoncmd.exe, which has
no human-readable size format. Anything typed afterl(paths, extra flags)
is passed through. Sessions source no rc file, so a user's ownlalias was
never available there. Commands the AI agent runs are not expanded.
v1.59.0 — sessions can run the node's Dart CLIs
Sessions can run the Dart CLIs installed on the node — omnyshell included.
Added
- The pub cache
bindirectory is on every session'sPATH. OmnyShell is
a Dart CLI installed withdart pub global activate, and so is much of what
an operator reaches for once connected; those executables live in the pub
cache'sbin, which is onPATHonly because a shell rc put it there.
Sessions source no rc, soomnyshellwas missing from the very shell
OmnyShell had just opened. Each backend (pipe,scriptPTY and winpty) now
appends that directory —$PUB_CACHE/bin, else~/.pub-cache/bin
(%LOCALAPPDATA%\Pub\Cache\binon Windows) — when the session'sPATHdoes
not already carry it. It is appended, not prepended, so the inherited or
profilePATHkeeps precedence, and nothing is added when the directory does
not exist.
Fixed
- A local IDE command whose output nobody read never reported its exit
code.ProcessCommandRunnerawaited the close of its output stream before
completingCommandExecution.exitCode; that stream is single-subscription,
and a close is only delivered once something listens, so a caller that wanted
the exit code alone waited forever. The exit code is now reported first and
the close is left to land on its own — a listener that subscribes later still
receives the buffered lines and the done event. The remote runner already
worked this way, so the two now agree.