From 97a8ecc03a2f10693c1d6e4616155043e7a3f942 Mon Sep 17 00:00:00 2001 From: Suriyakumarvijayanayagam Date: Wed, 30 Sep 2026 15:44:25 +0530 Subject: [PATCH] Setup told a Mac with Python 3.12 on it to go and install Python Found by running behavision-setup against a clean state directory the way a second machine will, which had never been done. It failed at the first step: Setup did not finish: no Python 3.10 or newer was found Found, but too old: python3 3.9 on a machine that has 3.12. The search was `python3` then `python`, and on macOS `/usr/bin/python3` is ALWAYS the Command Line Tools build - 3.9 on current macOS, below the 3.10 floor. Anything newer installs as `python3.12`, under Homebrew, as a framework, or somewhere a GUI application's minimal PATH never sees. So it now tries versioned names newest-first, then the plain ones, then the four directories macOS actually uses - and absolute candidates are stat'd rather than passed to LookPath, which only searches PATH. It found /Users/tenext/.local/opt/python3.12/bin/python3.12, which is exactly the interpreter it had been ignoring. With that, the whole install completes on a Mac for the first time: venv, engine and dependencies, models, agent.json, and "Engine starts and answers - verified". EXIT=0, a 298 MB runtime. Also the last thing it prints, which is the first thing an operator acts on. It said "Start Behavision from the Start menu" and "it appears in the system tray; right-click there to stop it". On macOS there is no Start menu and, deliberately, no tray at all - so the finishing message was describing a machine the user was not sitting at, on the one step where setup had otherwise succeeded. It now says to right-click the app the first time because the build is not notarised, and that closing the window stops recognition. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj --- agent/cmd/behavision-setup/main.go | 61 +++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/agent/cmd/behavision-setup/main.go b/agent/cmd/behavision-setup/main.go index 50fd96b..a8119f1 100644 --- a/agent/cmd/behavision-setup/main.go +++ b/agent/cmd/behavision-setup/main.go @@ -185,8 +185,19 @@ func run() error { } fmt.Println() - fmt.Println(" Done. Start Behavision from the Start menu or the desktop icon.") - fmt.Println(" It appears in the system tray; right-click there to stop it.") + // The last thing setup says is the first thing the operator does, so it + // has to describe THEIR machine. On macOS there is no Start menu and, + // deliberately, no tray at all - telling somebody to right-click a tray + // icon that does not exist is how software loses their trust on the step + // where it was otherwise finished. + if runtime.GOOS == "windows" { + fmt.Println(" Done. Start Behavision from the Start menu or the desktop icon.") + fmt.Println(" It appears in the system tray; right-click there to stop it.") + } else { + fmt.Println(" Done. Open Behavision.app - right-click it and choose Open the") + fmt.Println(" first time, because this build is not notarised.") + fmt.Println(" There is no tray on macOS: closing the window stops recognition.") + } fmt.Println() return nil } @@ -247,13 +258,53 @@ func findPython() (string, string, error) { if runtime.GOOS == "windows" { cands = append(cands, cand{"py", []string{"-3"}}) } + + // Versioned names FIRST, newest first, and this is not belt-and-braces on + // macOS - it is the only thing that works. `/usr/bin/python3` there is + // always the Command Line Tools build, 3.9 on current macOS, which is + // below the 3.10 floor. Anything newer installs as `python3.12` or into a + // directory that is not on a GUI application's PATH. Searching only + // `python3` therefore told a Mac with Python 3.12 sitting on it to go and + // install Python - measured on this machine, which has 3.12 under + // ~/.local/opt and reported "Found, but too old: python3 3.9". + versions := []string{"3.14", "3.13", "3.12", "3.11", "3.10"} + for _, v := range versions { + cands = append(cands, cand{"python" + v, nil}) + } cands = append(cands, cand{"python3", nil}, cand{"python", nil}) + // And the places a Mac puts an interpreter that LookPath will not find, + // because a double-clicked app inherits a minimal PATH rather than the + // one a shell profile builds. + if runtime.GOOS != "windows" { + home, _ := os.UserHomeDir() + for _, v := range versions { + for _, dir := range []string{ + "/opt/homebrew/bin", + "/usr/local/bin", + "/Library/Frameworks/Python.framework/Versions/" + v + "/bin", + filepath.Join(home, ".local", "opt", "python"+v, "bin"), + } { + cands = append(cands, cand{filepath.Join(dir, "python"+v), nil}) + } + } + } + var tried []string for _, c := range cands { - exe, err := exec.LookPath(c.exe) - if err != nil { - continue + exe := c.exe + if filepath.IsAbs(exe) { + // An absolute candidate is a guess about where an interpreter + // might be; most will not exist, and that is not an error. + if fi, err := os.Stat(exe); err != nil || fi.IsDir() { + continue + } + } else { + found, err := exec.LookPath(exe) + if err != nil { + continue + } + exe = found } args := append(append([]string{}, c.args...), "-c", "import sys;print('%d.%d'%sys.version_info[:2])")