From da4e66e78507703434fb182c0888279c5f07843a Mon Sep 17 00:00:00 2001 From: San Dang Date: Mon, 27 Jul 2026 12:11:04 +0530 Subject: [PATCH] fix(ollama): preserve proxy token during re-onboard Signed-off-by: San Dang --- src/lib/inference/ollama/proxy.ts | 23 +++++++++++++++++------ test/ollama-proxy-recovery.test.ts | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/lib/inference/ollama/proxy.ts b/src/lib/inference/ollama/proxy.ts index 6aaf5419816..cd206d17488 100644 --- a/src/lib/inference/ollama/proxy.ts +++ b/src/lib/inference/ollama/proxy.ts @@ -265,8 +265,12 @@ function printProxyPortConflict(owners: { pids: number[]; descriptions: string[] // so poll with backoff instead of the previous single 2s probe (issue #4820). const PROXY_START_ATTEMPTS = 12; -function startOllamaAuthProxy(backendUrl?: string): boolean { +function generateProxyToken(): string { const crypto = require("crypto"); + return crypto.randomBytes(24).toString("hex"); +} + +function startOllamaAuthProxyWithToken(proxyToken: string, backendUrl?: string): boolean { killStaleProxy(); // After clearing any stale NemoClaw proxy, a process still holding the port @@ -281,11 +285,10 @@ function startOllamaAuthProxy(backendUrl?: string): boolean { return false; } - const proxyToken = crypto.randomBytes(24).toString("hex"); ollamaProxyToken = proxyToken; - // Don't persist yet — wait until provider is confirmed in setupInference. - // If the user backs out to a different provider, the token stays in memory - // only and is discarded. + // Don't commit the selected backend yet — wait until setupInference confirms + // the provider. A newly generated token remains in memory and is discarded + // if the user backs out. const pid = spawnOllamaAuthProxy(proxyToken, backendUrl || `http://127.0.0.1:${OLLAMA_PORT}`); // Poll for readiness with backoff. Three terminal outcomes: @@ -325,9 +328,17 @@ function startOllamaAuthProxy(backendUrl?: string): boolean { return false; } +function startOllamaAuthProxy(backendUrl?: string): boolean { + // Re-onboarding the committed local Ollama route must keep the credential + // already mounted in the sandbox. A compatible custom endpoint uses the + // explicit fresh-token path below until provider selection commits it. + const proxyToken = loadPersistedProxyToken() ?? generateProxyToken(); + return startOllamaAuthProxyWithToken(proxyToken, backendUrl); +} + function noAuthProxy(endpointUrl: string) { const endpoint = new URL(endpointUrl); - if (!startOllamaAuthProxy(endpoint.origin)) { + if (!startOllamaAuthProxyWithToken(generateProxyToken(), endpoint.origin)) { restorePersistedOllamaAuthProxy(); throw new Error("Could not start the protected loopback route."); } diff --git a/test/ollama-proxy-recovery.test.ts b/test/ollama-proxy-recovery.test.ts index aa4df9b5a41..d82209992b6 100644 --- a/test/ollama-proxy-recovery.test.ts +++ b/test/ollama-proxy-recovery.test.ts @@ -388,7 +388,7 @@ console.log(JSON.stringify({ assert.equal(payload.proxySpawns[0].env.OLLAMA_BACKEND_PORT, "11434"); }); - it("keeps a newly started Ollama backend instead of restoring a compatible backend (#7424)", () => { + it("keeps the committed token when switching from a compatible backend to Ollama (#7424)", () => { const repoRoot = path.join(import.meta.dirname, ".."); const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-ollama-proxy-switch-")); const scriptPath = path.join(tmpDir, "provider-switch-check.js"); @@ -484,7 +484,7 @@ console.log(JSON.stringify({ assert.equal(payload.proxySpawns[0].token, payload.runningToken); assert.equal(payload.proxySpawns[1].backendUrl, "http://127.0.0.1:11434"); assert.equal(payload.proxySpawns[1].token, payload.runningToken); - assert.notEqual(payload.runningToken, "compatible-token"); + assert.equal(payload.runningToken, "compatible-token"); assert.deepEqual(payload.runCommands, [["kill", "4242"]]); assert.equal(payload.persistedBackend, "http://127.0.0.1:11434"); });