fix(opencode): serve передаёт OPENCODE_CONFIG — детерминированный выбор модели
This commit is contained in:
@@ -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 != "" {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user