Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
243d4fa
fix(installer): fall back to WSL-local Ollama on native Docker in WSL
rluo8 Jul 22, 2026
b41bdfe
fix(installer): drop native-Docker claim from WSL fallback summary
rluo8 Jul 22, 2026
ddd8221
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 22, 2026
b00f3f3
fix(installer): require a local Docker target for Windows-host Ollama
rluo8 Jul 22, 2026
7957423
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 22, 2026
ab55a49
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 22, 2026
e2c81cb
fix(installer): stop trusting the desktop-linux context name as local
rluo8 Jul 22, 2026
dc44cc6
fix(installer): resolve persisted Docker currentContext for local check
rluo8 Jul 22, 2026
0d8c3a7
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 22, 2026
d13c3e3
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 23, 2026
ce847ad
fix(installer): fail closed when the Docker config is unreadable
rluo8 Jul 23, 2026
8eeccc4
test(installer): split WSL express Ollama tests into a dedicated file
rluo8 Jul 23, 2026
f298f12
fix(installer): parse Docker config with node and fail closed on pars…
rluo8 Jul 23, 2026
cb08776
Merge branch 'main' into fix/7318-wsl-express-native-docker
cjagwani Jul 23, 2026
2f49643
Merge branch 'main' into fix/7318-wsl-express-native-docker
cjagwani Jul 23, 2026
be67b78
Merge branch 'main' into fix/7318-wsl-express-native-docker
cjagwani Jul 23, 2026
464945e
Merge branch 'main' into fix/7318-wsl-express-native-docker
cjagwani Jul 23, 2026
13a5423
fix(installer): fail closed without Docker config parser
cjagwani Jul 23, 2026
6553d27
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 24, 2026
cf010b7
fix(installer): verify WSL-local Ollama proxy setup
rluo8 Jul 24, 2026
96844c8
Merge remote-tracking branch 'upstream/main' into fix/7318-wsl-expres…
rluo8 Jul 24, 2026
77e25b5
Merge remote-tracking branch 'upstream/fix/7318-wsl-express-native-do…
rluo8 Jul 24, 2026
e1c8412
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 24, 2026
e0f9a1d
Merge branch 'main' into fix/7318-wsl-express-native-docker
rluo8 Jul 24, 2026
51d1ff4
merge: refresh PR #7365 onto current main
ericksoa Jul 25, 2026
86dff11
test(installer): keep WSL coverage host-independent
ericksoa Jul 25, 2026
f2b724d
merge: refresh PR #7365 onto current main
ericksoa Jul 25, 2026
45f8e3b
merge: refresh PR #7365 onto current main
ericksoa Jul 25, 2026
5a4f57e
ci: retry exact-head E2E gate
ericksoa Jul 25, 2026
ef25772
merge: refresh PR #7365 onto current main
ericksoa Jul 25, 2026
5ce658c
Merge branch 'main' into fix/7318-wsl-express-native-docker
apurvvkumaria Jul 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/get-started/windows-preparation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ You can also start it yourself beforehand with `ollama serve`.

You can also use Ollama for Windows.
During onboarding, NemoClaw can use an already-running Windows-host daemon, start or restart an installed daemon, or install Ollama on the Windows host.
If the installer offers express install on WSL, accepting it selects this Windows-host Ollama path automatically.
The Windows-host Ollama path requires Docker Desktop WSL integration, and the express prompt appears on WSL regardless of the container runtime.
With native Docker Engine inside WSL, decline the express prompt (or set `NEMOCLAW_NO_EXPRESS=1`) and select the WSL Ollama instance during onboarding; the menu labels the Windows-host actions as requiring Docker Desktop integration.
If the installer offers express install on WSL, accepting it selects a local Ollama path automatically based on the container runtime: the Windows-host Ollama path only when it detects Docker Desktop WSL integration, and WSL-local Ollama otherwise.
The Windows-host Ollama path requires Docker Desktop WSL integration; the express prompt appears on WSL regardless of the container runtime.
With native Docker Engine inside WSL, or when the Docker runtime is unavailable or cannot be probed, express install configures WSL-local Ollama instead of aborting.
When containers cannot reach host loopback, onboarding fronts that WSL-local daemon with the sandbox auth proxy.
You can still decline the express prompt (or set `NEMOCLAW_NO_EXPRESS=1`) to choose a provider manually; the onboarding menu labels the Windows-host actions as requiring Docker Desktop integration.
When Ollama runs on the Windows host, NemoClaw detects it from WSL through `host.docker.internal` and pulls missing models through the Ollama HTTP API.
Do not run both the Windows and WSL Ollama instances on port `11434` at the same time.
Use one instance, or move one of them to a different port before running `$$nemoclaw onboard`.
Expand Down
1 change: 1 addition & 0 deletions scripts/checks/vitest-project-overlap.mts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const INSTALLER_INTEGRATION_TESTS = new Set([
"test/install-build-dependency-preflight.test.ts",
"test/install-clone-ref.test.ts",
"test/install-express-prompt.test.ts",
"test/install-express-wsl-ollama.test.ts",
"test/install-openshell-version-check.test.ts",
"test/install-preflight-docker-bootstrap.test.ts",
"test/install-preflight.test.ts",
Expand Down
94 changes: 92 additions & 2 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3639,6 +3639,88 @@ clear_station_express_resume() {
done
}

# Report the container runtime's operating-system string (e.g. "Docker Desktop"
# or "Ubuntu 24.04.4 LTS"). Bounded with a hard timeout so a wedged,
# misconfigured, or dead-DOCKER_HOST daemon cannot hang the interactive express
# prompt: this runs from describe_express_install before ensure_docker, and WSL
# skips ensure_docker entirely. Empty on timeout or error.
express_wsl_docker_operating_system() {
timeout 10 docker info --format '{{.OperatingSystem}}' 2>/dev/null
}

# Resolve Docker's effective context name: the DOCKER_CONTEXT override if set,
# otherwise the persisted currentContext from Docker's config (what
# `docker context use` writes). A missing config or a config with no
# currentContext uses Docker's "default"; an unreadable or unparseable config
# fails closed as non-local.
express_wsl_docker_active_context() {
if [ -n "${DOCKER_CONTEXT:-}" ]; then
printf '%s' "${DOCKER_CONTEXT}"
return 0
fi
local cfg="${DOCKER_CONFIG:-${HOME:-}/.docker}/config.json"
local ctx="" parse_status=0
if [ ! -e "$cfg" ]; then
printf '%s' "default"
return 0
fi
if [ -e "$cfg" ] && [ ! -r "$cfg" ]; then
printf '%s' "__unknown__"
return 0
fi
if command -v node >/dev/null 2>&1; then
ctx="$(
node - "$cfg" <<'NODE'
const fs = require("node:fs");

let config;
try {
config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
} catch {
process.exit(1);
}
if (config === null || typeof config !== "object" || Array.isArray(config)) process.exit(1);
if (!Object.prototype.hasOwnProperty.call(config, "currentContext")) process.exit(2);
if (typeof config.currentContext !== "string") process.exit(1);
process.stdout.write(config.currentContext);
NODE
)" || parse_status=$?
if [ "$parse_status" -eq 0 ]; then
printf '%s' "$ctx"
return 0
fi
if [ "$parse_status" -eq 2 ]; then
printf '%s' "default"
return 0
fi
printf '%s' "__unknown__"
return 0
fi
printf '%s' "__unknown__"
}

# True only when the Docker CLI targets the LOCAL default daemon: the active
# context is "default" and no DOCKER_HOST override is set. A DOCKER_HOST, a
# DOCKER_CONTEXT override, or a persisted currentContext other than "default"
# (a context name like desktop-linux is not proof of a local endpoint — it can be
# pointed at a remote daemon) can reach a remote Docker Desktop whose sandbox
# containers cannot reach this machine's Windows-host Ollama (PRA-1). Fails closed
# (non-local) on any non-default, unreadable, or unparseable context.
express_wsl_docker_target_is_local() {
[ -z "${DOCKER_HOST:-}" ] || return 1
[ "$(express_wsl_docker_active_context)" = "default" ]
}

# Windows-host Ollama only works through LOCAL Docker Desktop WSL integration
# (host.docker.internal routes to the Windows host). Native Docker Engine (#3695),
# a remote/unknown target, or a failed probe can't reach it, so use WSL-local
# Ollama instead; the onboard provider setup fronts that loopback daemon with
# the sandbox auth proxy when containers cannot reach host loopback (#7318).
express_wsl_can_use_windows_host_ollama() {
express_wsl_docker_target_is_local || return 1
express_wsl_docker_operating_system | grep -qi 'docker desktop'
}

activate_express_install() {
local platform="$1"
_SELECTED_EXPRESS_PLATFORM="$platform"
Expand Down Expand Up @@ -3668,7 +3750,11 @@ activate_express_install() {
configure_station_express_model
;;
"Windows WSL")
export NEMOCLAW_PROVIDER=install-windows-ollama
if express_wsl_can_use_windows_host_ollama; then
export NEMOCLAW_PROVIDER=install-windows-ollama
else
export NEMOCLAW_PROVIDER=install-ollama
fi
;;
esac
}
Expand Down Expand Up @@ -3839,7 +3925,11 @@ describe_express_install() {
sandbox_summary="${NEMOCLAW_SANDBOX_NAME:-my-assistant}"
;;
"Windows WSL")
inference_summary="Windows-host Ollama through host.docker.internal"
if express_wsl_can_use_windows_host_ollama; then
inference_summary="Windows-host Ollama through host.docker.internal"
else
inference_summary="WSL-local Ollama, with a sandbox auth proxy when containers cannot reach host loopback"
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
sandbox_summary="${NEMOCLAW_SANDBOX_NAME:-my-assistant}"
;;
*)
Expand Down
40 changes: 40 additions & 0 deletions src/lib/onboard/setup-nim-ollama.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,46 @@ describe("createSetupNimOllamaHandlers", () => {
assert.equal(state.allowToolsIncompatible, true);
});

it("fronts installed WSL-local Ollama with the sandbox proxy when host loopback is not container-reachable (#7318)", async () => {
const state = makeState();
const install = vi.fn(() => ({ ok: true }));
const startProxy = vi.fn(() => true);
const selectModel = vi.fn<Deps["selectAndValidateOllamaModel"]>(async () => ({
outcome: "selected",
model: "qwen3:0.6b",
allowToolsIncompatible: false,
}));
const log = vi.spyOn(console, "log").mockImplementation(() => {});
const { handleInstallOllamaSelection } = createSetupNimOllamaHandlers(
makeDeps({
process: { ...process, platform: "linux" } as NodeJS.Process,
installOllamaOnLinux: install,
shouldFrontOllamaWithProxy: () => true,
startOllamaAuthProxy: startProxy,
getLocalProviderBaseUrl: () => "http://host.openshell.internal:11435/v1",
selectAndValidateOllamaModel: selectModel,
}),
);

const result = await handleInstallOllamaSelection(null, null, null, state, {
hasUpgradableOllama: false,
});

expect(result).toBe("selected");
expect(install).toHaveBeenCalledTimes(1);
expect(startProxy).toHaveBeenCalledTimes(1);
expect(state).toMatchObject({
provider: "ollama-local",
endpointUrl: "http://host.openshell.internal:11435/v1",
model: "qwen3:0.6b",
credentialEnv: null,
preferredInferenceApi: "openai-completions",
});
expect(state.endpointUrl).not.toContain("127.0.0.1");
expect(log).toHaveBeenCalledWith(" ✓ Using Ollama on localhost:11434 (proxy on :11435)");
log.mockRestore();
});

it("fails closed on unknown Ollama startup outcomes without mutating state", async () => {
const state = makeState();
const before = { ...state, hermesToolGateways: [...state.hermesToolGateways] };
Expand Down
16 changes: 0 additions & 16 deletions test/install-express-prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1430,22 +1430,6 @@ printf 'PROMPT_REACHED\n'
expect(output).not.toContain("PROMPT_REACHED");
});

it("maps Windows WSL express install to Windows-host Ollama", () => {
const result = runExpressPromptWithTty("\n", "pipe", "Windows WSL");
const output = `${result.stdout}${result.stderr}`;
expect(result.status, output).toBe(0);
expect(output).toMatch(/Detected Windows WSL/);
expect(output).toMatch(
/Express install will configure Windows-host Ollama through host\.docker\.internal/,
);
expect(output).toMatch(/Sandbox policy: suggested mode, tier 'balanced'/);
expect(output).toMatch(/Run express install/);
expect(output).toMatch(/Using express install for Windows WSL/);
expect(output).toMatch(
/RESULT NON_INTERACTIVE=1 SUDO_MODE=prompt PROVIDER=install-windows-ollama MODEL= VLLM_MODEL= POLICY=suggested YES=1 SANDBOX=/,
);
});

it.skipIf(process.platform === "darwin")(
"skips express install without a controlling TTY",
() => {
Expand Down
Loading
Loading