From 0b29dd4a50606fd75cd6e55a2c747147da3a233b Mon Sep 17 00:00:00 2001 From: Suriyakumarvijayanayagam Date: Wed, 30 Sep 2026 12:42:55 +0530 Subject: [PATCH] The engine inherited whatever directory launched the app Nothing ever set the child's working directory, so it took the parent's - and an app started by double-clicking its bundle is handed "/", not anywhere useful. On macOS the symptom was `python: No module named behavision` repeating forever, because the dev engine is invoked as `-m behavision` and that resolves against the working directory. The same app launched from a terminal inside the repo worked perfectly, which is exactly the shape of a bug that survives every test a developer runs. It only appeared when the app was started the way a user starts one. Config.EngineDir, empty meaning the install root, set by both launchers - the desktop app and the headless agent, which had identical code and the identical omission. It matters beyond this case: the shipped Windows engine is a one-folder PyInstaller build whose relative paths should resolve beside itself rather than beside Explorer's idea of a current directory. Verified by double-clicking the bundle with nothing in the environment: engine up on 8010 (401, gated), w600k_r50 on CoreML, gallery 5/5 embeddings usable and none stranded, both office cameras connected and streaming, and head office reporting cameras 2/2 one heartbeat later. Two things that showed up while proving it, both the product being honest rather than faults: - The camera at .121 was genuinely unreachable for several minutes, and last_error said so in words an installer can act on - "cannot reach 192.168.1.121:554 - No route" - rather than `connected: false`. That field was added yesterday for precisely this. - Head office briefly showed cameras 0/1 against a local 2/2. That is a 60-second heartbeat, not a disagreement; the next one read 2/2. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj --- agent/main.go | 8 ++++++++ agent/pkg/config/config.go | 13 ++++++++++++- desktop/app.go | 8 ++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/agent/main.go b/agent/main.go index 814168a..57e5af7 100644 --- a/agent/main.go +++ b/agent/main.go @@ -231,6 +231,14 @@ func cmdRun() error { sup := engine.New(engine.Options{ Command: func(ctx context.Context) *exec.Cmd { cmd := exec.CommandContext(ctx, exe, cfg.EngineArgs...) + // Run the engine FROM a known directory rather than from whatever + // happened to launch us. A double-clicked bundle hands its child + // "/", and an engine invoked as `-m behavision` then cannot find + // itself - measured on macOS, where it retried forever. + cmd.Dir = cfg.EngineDir + if cmd.Dir == "" { + cmd.Dir = paths.InstallRoot() + } // How the engine learns where to send detections. The engine's // config already reads `events.webhook_url: ${BEHAVISION_WEBHOOK_URL}` // and python-dotenv does not override a variable the process diff --git a/agent/pkg/config/config.go b/agent/pkg/config/config.go index 52b01ee..92c0772 100644 --- a/agent/pkg/config/config.go +++ b/agent/pkg/config/config.go @@ -37,7 +37,18 @@ type Config struct { BrokerCAFile string `json:"broker_ca_file"` // Engine process. - EngineExe string `json:"engine_exe"` + EngineExe string `json:"engine_exe"` + // EngineDir is the working directory the engine is launched IN. Empty + // means the install root. + // + // It exists because nothing set it, so the engine inherited whatever + // launched the app - and for an app started by double-clicking its + // bundle that is "/", not anywhere useful. The symptom on macOS was + // `python: No module named behavision` repeating forever: the dev engine + // is `-m behavision`, which resolves against the working directory. The + // same app started from a terminal in the repo worked, which is exactly + // the shape of a bug that survives every test run by a developer. + EngineDir string `json:"engine_dir,omitempty"` EngineArgs []string `json:"engine_args"` APIBase string `json:"api_base"` APIUser string `json:"api_user"` diff --git a/desktop/app.go b/desktop/app.go index 8be79aa..8cfa1d8 100644 --- a/desktop/app.go +++ b/desktop/app.go @@ -105,6 +105,14 @@ func (a *App) startup(ctx context.Context) { a.sup = agentengine.New(agentengine.Options{ Command: func(c context.Context) *exec.Cmd { cmd := exec.CommandContext(c, exe, a.cfg.EngineArgs...) + // Run the engine FROM a known directory rather than from whatever + // happened to launch us. A double-clicked bundle hands its child + // "/", and an engine invoked as `-m behavision` then cannot find + // itself - measured on macOS, where it retried forever. + cmd.Dir = a.cfg.EngineDir + if cmd.Dir == "" { + cmd.Dir = agentpaths.InstallRoot() + } // How the engine learns where to post its detections. Its config // already reads `events.webhook_url: ${BEHAVISION_WEBHOOK_URL}`, // and python-dotenv does not override a variable the process