ndx 1.0.1
dotnet tool install --global ndx --version 1.0.1
dotnet new tool-manifest
dotnet tool install --local ndx --version 1.0.1
#tool dotnet:?package=ndx&version=1.0.1
nuke :add-package ndx --version 1.0.1
ndx (native dotnet execute) is dnx for
native tools packaged and distributed as NuGet packages. Same
PACKAGE[@VERSION] identity, same restore flags, same global packages folder.
If you can dnx stop, you can ndx stop. dotnet dnx and dotnet tool exec
are the same command; ndx speaks that protocol, including RID-specific Native
AOT tools (stop, go, winget) and ordinary framework-dependent tools
(dotnetsay).
A first run downloads into the NuGet cache. A later ndx tool@version starts
the cached binary — no SDK, no restore.
Install
macOS / Linux:
curl -fsSL https://github.com/devlooped/ndx/releases/latest/download/install.sh | sh
Windows (PowerShell):
irm https://github.com/devlooped/ndx/releases/latest/download/install.ps1 | iex
Alternatively (perhaps of debatable utility), you can install using the .NET SDK too:
dotnet tool install -g ndx
Update
Self-update the installed binary (optional version, including downgrades):
ndx --update
ndx --update 0.1.0
Uninstall
macOS / Linux:
curl -fsSL https://github.com/devlooped/ndx/releases/latest/download/uninstall.sh | sh
Windows (PowerShell):
irm https://github.com/devlooped/ndx/releases/latest/download/uninstall.ps1 | iex
Usage
ndx
Same shape as dnx / dotnet dnx / dotnet tool exec:
dnx stop -- --help
ndx stop -- --help
dnx stop@2.1.0 -- --help
ndx stop@2.1.0 -- --help
dnx dotnetsay@1.0.0 -- Hello
ndx dotnetsay@1.0.0 -- Hello
Pin a feed the same way (here the CI feed used for the timings below):
dnx --source https://kzu.blob.core.windows.net/nuget/index.json stop@2.1.0 -- --help
ndx --source https://kzu.blob.core.windows.net/nuget/index.json stop@2.1.0 -- --help
stop is a Native AOT RID tool. dotnetsay is a classic framework-dependent
tool. Both are just NuGet packages; ndx picks the host RID and starts
Runner=executable binaries directly, or dotnet exec for Runner=dotnet.
An exact version already in the cache (NUGET_PACKAGES, then
nuget.config's globalPackagesFolder, then ~/.nuget/packages) is not
downloaded again. A first tool@version run builds the nupkg URL from the
service index (cached for the process) and lists versions only if that GET
is 404. Packages written by dnx or dotnet restore are reused; packages
written by ndx are visible to them.
Shared flags: --source, --add-source, --configfile, --version,
--prerelease, --yes/-y, --allow-roll-forward, --verbosity/-v,
--disable-parallel, --ignore-failed-sources, --no-http-cache,
--interactive.
Evergreen
Unlike dnx, omitting the version is @* — not a one-shot latest.
ndx my-agent and ndx my-agent@* are the same: start the latest match,
and while that process is still running, watch the feed, download a newer
matching version before stopping the child, then send SIGINT / Ctrl+C
(and WM_CLOSE if the tool is a GUI) and restart.
@*-* includes prereleases. @1.* / @1.1.* pin a major or minor the
GitHub Actions way (float the rest). A NuGet range like [1.0,2.0) works
too. One-shot tools still exit as soon as the child exits; the watch loop
only continues while the child is alive. Pin an exact version
(tool@2.1.0) to disable it.
ndx my-agent
ndx my-agent@*
ndx my-agent@1.*
ndx my-agent@1.1.*
ndx my-agent@[1.0,2.0)
That's the point for anything that is supposed to keep running: a daemon,
an agent, an MCP server, a watcher, a local gateway. You start it once.
When a newer matching package lands on the feed, ndx stages the bits, asks
the current process to exit cleanly, and starts the new one. No
dotnet tool update -g, no unit file to bounce, no cron that reinstalls.
The running process is always the latest version the range allows, so an
agent picks up new tools and bugfixes, a daemon picks up a patched build,
without anyone being there to restart it.
The poll interval defaults to 5 seconds and can be set in .netconfig:
[ndx]
interval = 5
ndx walks from the working directory up, then ~/.netconfig. --verbosity quiet hides the Updating … line.
Startup time
Cold is empty-cache download → start; cached is start only (tool@version so
neither runner resolves latest). linux-x64 (WSL2) is SDK 10.0.110; win-x64 is
SDK 10.0.303.
| Tool | Runner | Cold linux-x64 | Cold win-x64 | Cached linux-x64 | Cached win-x64 |
|---|---|---|---|---|---|
stop@2.1.0 (native AOT) |
dnx |
2.2 s | 2.1 s | 580 ms | 520 ms |
ndx |
1.2 s | 1.5 s | 10 ms | 34 ms | |
dotnetsay@1.0.0 (framework-dependent) |
dnx |
1.6 s | 1.8 s | 570 ms | 545 ms |
ndx |
849 ms | 1.1 s | 12 ms | 50 ms | |
winget (native AOT TUI, Windows) |
dnx |
— | 2.7 s | — | 820 ms |
ndx |
— | 1.2 s | — | 275 ms |
Isolated NUGET_PACKAGES. stop / dotnetsay from
https://kzu.blob.core.windows.net/nuget/index.json; winget from nuget.org.
Cold is the median of 3 empty-cache runs. Cached is the median of 10 runs after
one seed. stop invoked as -- --help; dotnetsay as -- ndx. winget is
a TUI so it does not exit on its own: cold is winget (latest), cached is
winget@0.13.2; each run waits until winget-tui is up, then ndx stop@2.1.0
signals that PID and the clock stops when the runner exits. dnx is the SDK
script (dotnet dnx). winget timings are win-x64 SDK 11.0.100-preview.
Sponsors
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.