From 88b455203f2d0223e2fe05fc9896174810b8d016 Mon Sep 17 00:00:00 2001 From: "ki.sagidullin" Date: Sun, 23 Aug 2026 20:27:24 +0500 Subject: [PATCH] =?UTF-8?q?fix(opencode):=20serve=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B0=D1=91=D1=82=20OPENCODE=5FCONFIG=20=E2=80=94?= =?UTF-8?q?=20=D0=B4=D0=B5=D1=82=D0=B5=D1=80=D0=BC=D0=B8=D0=BD=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=B2=D1=8B=D0=B1?= =?UTF-8?q?=D0=BE=D1=80=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/opencode/server.go | 27 +++++++++++++++++++++++++ internal/opencode/server_test.go | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/internal/opencode/server.go b/internal/opencode/server.go index 5cf3b3a..972850f 100644 --- a/internal/opencode/server.go +++ b/internal/opencode/server.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "os/exec" + "path/filepath" "sync" "time" ) @@ -31,6 +32,12 @@ type Server struct { Password string // basic auth (если непустой — сервер защищён) Dir string // каталог, в котором запускается serve (project сервера) + // ConfigPath — путь к глобальному конфигу opencode (opencode.jsonc). + // Передаётся serve через env OPENCODE_CONFIG, чтобы гарантированно + // подхватить модель/провайдеров независимо от резолва глобального пути. + // Пусто — serve резолвит конфиг сам (см. defaults). + ConfigPath string + // URL задаёт внешний сервер. Пусто — супервайзер владеет процессом. URL string PollInterval time.Duration // как часто проверять /global/health @@ -60,6 +67,20 @@ func (s *Server) defaults() { if s.Stdout == nil { s.Stdout = os.Stderr } + if s.ConfigPath == "" { + s.ConfigPath = defaultOpenCodeConfigPath() + } +} + +// defaultOpenCodeConfigPath возвращает путь к глобальному конфигу opencode, +// совпадающий с тем, что opencode загружает по умолчанию: ~/.config/opencode/ +// (в т.ч. на Windows — см. Global.Path.config в исходниках opencode). +func defaultOpenCodeConfigPath() string { + home, _ := os.UserHomeDir() + if home == "" { + home = "." + } + return filepath.Join(home, ".config", "opencode", "opencode.jsonc") } // baseURL собирает полный адрес сервера (http://host:port). @@ -125,6 +146,12 @@ func (s *Server) serveCmd(ctx context.Context) *exec.Cmd { env := append(os.Environ(), "OPENCODE_DISABLE_AUTOUPDATE=1", "OPENCODE_DISABLE_MODELS_FETCH=1") + // Гарантированно указываем глобальный конфиг opencode: serve обязан + // подхватить модель/провайдеров из него (иначе возможен фоллбэк на + // случайную модель из каталога). + if s.ConfigPath != "" { + env = append(env, "OPENCODE_CONFIG="+s.ConfigPath) + } // Агенты (analyst/dev/...) opencode находит сам через project-каталог // .opencode (см. internal/agents). if s.DBPath != "" { diff --git a/internal/opencode/server_test.go b/internal/opencode/server_test.go index 4c9ad5e..0606469 100644 --- a/internal/opencode/server_test.go +++ b/internal/opencode/server_test.go @@ -198,4 +198,38 @@ func atoiOrZero(s string) int { n = n*10 + int(c-'0') } return n +} + +func TestServeCmd_SetsOpenCodeConfig(t *testing.T) { + // serveCmd должен всегда передавать OPENCODE_CONFIG: без него serve может + // не подхватить модель/провайдеров из глобального конфига. + s := &Server{} + s.defaults() + cmd := s.serveCmd(context.Background()) + if cmd.Env == nil { + t.Fatal("serveCmd: Env не задан") + } + prefix := "OPENCODE_CONFIG=" + path := "" + for _, kv := range cmd.Env { + if strings.HasPrefix(kv, prefix) { + path = strings.TrimPrefix(kv, prefix) + break + } + } + if path == "" { + t.Fatal("serveCmd: OPENCODE_CONFIG не выставлен") + } + if want := s.ConfigPath; path != want { + t.Errorf("OPENCODE_CONFIG = %q, want %q", path, want) + } +} + +func TestServerCmd_CustomOpenCodeConfig(t *testing.T) { + // явно заданный путь переопределяет дефолтный + s := &Server{ConfigPath: `C:\custom\opencode.jsonc`} + s.defaults() + if s.ConfigPath != `C:\custom\opencode.jsonc` { + t.Errorf("ConfigPath = %q, want explicit", s.ConfigPath) + } } \ No newline at end of file