Repository: ollama/ollama
Pull request: config: use native OpenClaw Ollama onboarding

OpenClaw now accepts the Ollama onboarding flags directly upstream, so rely on its wizard state instead of the legacy integration onboarding flag.

Update first-run setup to pass the Ollama auth and model flags during onboarding, perform a best-effort update before onboarding when needed, and drop the stale test that asserted persistence of the old onboarding flag.

Changed files:
filename: cmd/launch/openclaw.go
patch: @@ -15,7 +15,6 @@ import (
 	"time"
 
 	"github.com/ollama/ollama/api"
-	"github.com/ollama/ollama/cmd/config"
 	"github.com/ollama/ollama/cmd/internal/fileutil"
 	"github.com/ollama/ollama/envconfig"
 	"github.com/ollama/ollama/types/model"
@@ -26,6 +25,9 @@ const defaultGatewayPort = 18789
 // Bound model capability probing so launch/config cannot hang on slow/unreachable API calls.
 var openclawModelShowTimeout = 5 * time.Second
 
+// openclawFreshInstall is set to true when ensureOpenclawInstalled performs an install
+var openclawFreshInstall bool
+
 type Openclaw struct{}
 
 func (c *Openclaw) String() string { return "OpenClaw" }
@@ -36,10 +38,7 @@ func (c *Openclaw) Run(model string, args []string) error {
 		return err
 	}
 
-	firstLaunch := true
-	if integrationConfig, err := loadStoredIntegrationConfig("openclaw"); err == nil {
-		firstLaunch = !integrationConfig.Onboarded
-	}
+	firstLaunch := !c.onboarded()
 
 	if firstLaunch {
 		fmt.Fprintf(os.Stderr, "\n%sSecurity%s\n\n", ansiBold, ansiReset)
@@ -54,17 +53,25 @@ func (c *Openclaw) Run(model string, args []string) error {
 		if !ok {
 			return nil
 		}
-	}
 
-	if !c.onboarded() {
+		// Ensure the latest version is installed before onboarding so we get
+		// the newest wizard flags (e.g. --auth-choice ollama).
+		if !openclawFreshInstall {
+			update := exec.Command(bin, "update")
+			update.Stdout = os.Stdout
+			update.Stderr = os.Stderr
+			_ = update.Run() // best-effort; continue even if update fails
+		}
+
 		fmt.Fprintf(os.Stderr, "\n%sSetting up OpenClaw with Ollama...%s\n", ansiGreen, ansiReset)
 		fmt.Fprintf(os.Stderr, "%s  Model: %s%s\n\n", ansiGray, model, ansiReset)
 
 		cmd := exec.Command(bin, "onboard",
 			"--non-interactive",
 			"--accept-risk",
-			"--auth-choice", "skip",
-			"--gateway-token", "ollama",
+			"--auth-choice", "ollama",
+			"--custom-base-url", envconfig.Host().String(),
+			"--custom-model-id", model,
 			"--install-daemon",
 			"--skip-channels",
 			"--skip-skills",
@@ -77,12 +84,6 @@ func (c *Openclaw) Run(model string, args []string) error {
 		}
 
 		patchDeviceScopes()
-
-		// Onboarding overwrites openclaw.json, so re-apply the model config
-		// that Edit() wrote before Run() was called.
-		if err := c.Edit([]string{model}); err != nil {
-			fmt.Fprintf(os.Stderr, "%s  Warning: could not re-apply model config: %v%s\n", ansiYellow, err, ansiReset)
-		}
 	}
 
 	if strings.HasSuffix(model, ":cloud") || strings.HasSuffix(model, "-cloud") {
@@ -91,11 +92,7 @@ func (c *Openclaw) Run(model string, args []string) error {
 		}
 	}
 
-	if firstLaunch {
-		fmt.Fprintf(os.Stderr, "\n%sPreparing your assistant — this may take a moment...%s\n\n", ansiGray, ansiReset)
-	} else {
-		fmt.Fprintf(os.Stderr, "\n%sStarting your assistant — this may take a moment...%s\n\n", ansiGray, ansiReset)
-	}
+	fmt.Fprintf(os.Stderr, "\n%sStarting your assistant — this may take a moment...%s\n\n", ansiGray, ansiReset)
 
 	// When extra args are passed through, run exactly what the user asked for
 	// after setup and skip the built-in gateway+TUI convenience flow.
@@ -108,19 +105,14 @@ func (c *Openclaw) Run(model string, args []string) error {
 		if err := cmd.Run(); err != nil {
 			return windowsHint(err)
 		}
-		if firstLaunch {
-			if err := config.MarkIntegrationOnboarded("openclaw"); err != nil {
-				return fmt.Errorf("failed to save onboarding state: %w", err)
-			}
-		}
 		return nil
 	}
 
 	token, port := c.gatewayInfo()
 	addr := fmt.Sprintf("localhost:%d", port)
 
 	// If the gateway is already running (e.g. via the daemon), restart it
-	// so it picks up any config changes from Edit() above (model, provider, etc.).
+	// so it picks up any config changes (model, provider, etc.).
 	if portOpen(addr) {
 		restart := exec.Command(bin, "daemon", "restart")
 		restart.Env = openclawEnv()
@@ -167,11 +159,6 @@ func (c *Openclaw) Run(model string, args []string) error {
 		return windowsHint(err)
 	}
 
-	if firstLaunch {
-		if err := config.MarkIntegrationOnboarded("openclaw"); err != nil {
-			return fmt.Errorf("failed to save onboarding state: %w", err)
-		}
-	}
 	return nil
 }
 
@@ -450,6 +437,7 @@ func ensureOpenclawInstalled() (string, error) {
 	}
 
 	fmt.Fprintf(os.Stderr, "%sOpenClaw installed successfully%s\n\n", ansiGreen, ansiReset)
+	openclawFreshInstall = true
 	return "openclaw", nil
 }
 

filename: cmd/launch/openclaw_test.go
patch: @@ -82,78 +82,6 @@ func TestOpenclawRunPassthroughArgs(t *testing.T) {
 	}
 }
 
-func TestOpenclawRunFirstLaunchPersistence(t *testing.T) {
-	if runtime.GOOS == "windows" {
-		t.Skip("uses a POSIX shell test binary")
-	}
-
-	oldHook := DefaultConfirmPrompt
-	DefaultConfirmPrompt = func(prompt string) (bool, error) {
-		return true, nil
-	}
-	defer func() { DefaultConfirmPrompt = oldHook }()
-
-	t.Run("success persists onboarding flag", func(t *testing.T) {
-		tmpDir := t.TempDir()
-		setTestHome(t, tmpDir)
-		t.Setenv("PATH", tmpDir)
-
-		configDir := filepath.Join(tmpDir, ".openclaw")
-		if err := os.MkdirAll(configDir, 0o755); err != nil {
-			t.Fatal(err)
-		}
-		// Mark OpenClaw onboarding complete so Run takes passthrough path directly.
-		if err := os.WriteFile(filepath.Join(configDir, "openclaw.json"), []byte(`{
-			"wizard": {"lastRunAt": "2026-01-01T00:00:00Z"}
-		}`), 0o644); err != nil {
-			t.Fatal(err)
-		}
-		if err := os.WriteFile(filepath.Join(tmpDir, "openclaw"), []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
-			t.Fatal(err)
-		}
-
-		c := &Openclaw{}
-		if err := c.Run("llama3.2", []string{"gateway", "--status"}); err != nil {
-			t.Fatalf("Run() error = %v", err)
-		}
-		integrationConfig, err := LoadIntegration("openclaw")
-		if err != nil {
-			t.Fatalf("LoadIntegration() error = %v", err)
-		}
-		if !integrationConfig.Onboarded {
-			t.Fatal("expected onboarding flag to be persisted after successful run")
-		}
-	})
-
-	t.Run("failure does not persist onboarding flag", func(t *testing.T) {
-		tmpDir := t.TempDir()
-		setTestHome(t, tmpDir)
-		t.Setenv("PATH", tmpDir)
-
-		configDir := filepath.Join(tmpDir, ".openclaw")
-		if err := os.MkdirAll(configDir, 0o755); err != nil {
-			t.Fatal(err)
-		}
-		if err := os.WriteFile(filepath.Join(configDir, "openclaw.json"), []byte(`{
-			"wizard": {"lastRunAt": "2026-01-01T00:00:00Z"}
-		}`), 0o644); err != nil {
-			t.Fatal(err)
-		}
-		if err := os.WriteFile(filepath.Join(tmpDir, "openclaw"), []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil {
-			t.Fatal(err)
-		}
-
-		c := &Openclaw{}
-		if err := c.Run("llama3.2", []string{"gateway", "--status"}); err == nil {
-			t.Fatal("expected run failure")
-		}
-		integrationConfig, err := LoadIntegration("openclaw")
-		if err == nil && integrationConfig.Onboarded {
-			t.Fatal("expected onboarding flag to remain unset after failed run")
-		}
-	})
-}
-
 func TestOpenclawEdit(t *testing.T) {
 	c := &Openclaw{}
 	tmpDir := t.TempDir()