From 10fefe0d57a0a1593b20c14cd4081ad649c1fb59 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Fri, 13 Mar 2026 16:28:40 -0700 Subject: [PATCH] config: use native OpenClaw Ollama onboarding (#14829) 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. --- cmd/launch/openclaw.go | 50 ++++++++++---------------- cmd/launch/openclaw_test.go | 72 ------------------------------------- 2 files changed, 19 insertions(+), 103 deletions(-) diff --git a/cmd/launch/openclaw.go b/cmd/launch/openclaw.go index df79f36e5..f2315ed4c 100644 --- a/cmd/launch/openclaw.go +++ b/cmd/launch/openclaw.go @@ -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,11 +105,6 @@ 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 } @@ -120,7 +112,7 @@ func (c *Openclaw) Run(model string, args []string) error { 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 } diff --git a/cmd/launch/openclaw_test.go b/cmd/launch/openclaw_test.go index 14601fe87..37b3d5e04 100644 --- a/cmd/launch/openclaw_test.go +++ b/cmd/launch/openclaw_test.go @@ -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()