feat: opencode serve — постоянный сервер вместо разовых subprocess (вариант A)
Супервайзер Server spawn'ит opencode serve (или ходит на внешний URL),
Runner ходит к нему через 'opencode run --attach <url>'. По умолчанию
serve.enabled=false — историческая spawn-модель сохранена; наличие
serve.url переключает на внешний сервер.
- config: ServeCfg (enabled/hostname/port/url/password) + дефолты; лоадер
научился int/bool (раньше только string/Duration/struct).
- opencode: Server (Start/Run/Close, /global/health, рестарт упавшего,
reaper-горутина владеет Wait; .Exited() непригоден для SIGKILL).
- Runner.AttachURL: run --attach <url> при заданном URL, иначе как раньше.
- app: composition root — при включённом serve запускает супервайзер.
- тесты: TestRun_AttachMode, TestServer_{ExternalURL,OwnProcess,Restart}.
This commit is contained in:
@@ -76,6 +76,15 @@ telegram:
|
||||
if cfg.OpenCode.IdleTimeout.Duration() != 5*time.Minute {
|
||||
t.Errorf("idle timeout = %v", cfg.OpenCode.IdleTimeout)
|
||||
}
|
||||
if cfg.OpenCode.Serve.Enabled {
|
||||
t.Errorf("serve.enabled = true, want false (дефолт)")
|
||||
}
|
||||
if cfg.OpenCode.Serve.Hostname != "127.0.0.1" {
|
||||
t.Errorf("serve.hostname = %q, want 127.0.0.1", cfg.OpenCode.Serve.Hostname)
|
||||
}
|
||||
if cfg.OpenCode.Serve.Port != 4096 {
|
||||
t.Errorf("serve.port = %d, want 4096", cfg.OpenCode.Serve.Port)
|
||||
}
|
||||
if cfg.Paths.Worktree != "./worktrees" {
|
||||
t.Errorf("worktree = %q, want ./worktrees", cfg.Paths.Worktree)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -133,6 +134,18 @@ func applyDefaults(cfg *Config) {
|
||||
if fv.Kind() == reflect.String {
|
||||
fv.SetString(meta.defaultVal)
|
||||
}
|
||||
// int (значение по умолчанию, напр. port)
|
||||
if fv.Kind() == reflect.Int {
|
||||
if n, err := strconv.Atoi(meta.defaultVal); err == nil {
|
||||
fv.SetInt(int64(n))
|
||||
}
|
||||
}
|
||||
// bool (по умолчанию false/true)
|
||||
if fv.Kind() == reflect.Bool {
|
||||
if b, err := strconv.ParseBool(meta.defaultVal); err == nil {
|
||||
fv.SetBool(b)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,6 +169,16 @@ func applyEnvOverrides(cfg *Config) {
|
||||
if fv.Kind() == reflect.String {
|
||||
fv.SetString(envVal)
|
||||
}
|
||||
if fv.Kind() == reflect.Int {
|
||||
if n, err := strconv.Atoi(envVal); err == nil {
|
||||
fv.SetInt(int64(n))
|
||||
}
|
||||
}
|
||||
if fv.Kind() == reflect.Bool {
|
||||
if b, err := strconv.ParseBool(envVal); err == nil {
|
||||
fv.SetBool(b)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -173,6 +196,10 @@ func walk(v reflect.Value, fn func(reflect.Value, fieldMeta)) {
|
||||
fn(fv, collectMeta(f))
|
||||
} else if fv.Type() == durType {
|
||||
fn(fv, collectMeta(f))
|
||||
} else if fv.Kind() == reflect.Int {
|
||||
fn(fv, collectMeta(f))
|
||||
} else if fv.Kind() == reflect.Bool {
|
||||
fn(fv, collectMeta(f))
|
||||
} else if fv.Kind() == reflect.Struct {
|
||||
walk(fv, fn)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,21 @@ type OpenCodeCfg struct {
|
||||
HardTimeout Duration `yaml:"hard_timeout" default:"20m"`
|
||||
IdleTimeout Duration `yaml:"idle_timeout" default:"5m"`
|
||||
PollMs Duration `yaml:"poll_ms" default:"2s"`
|
||||
Serve ServeCfg `yaml:"serve"`
|
||||
}
|
||||
|
||||
// ServeCfg — управление постоянным opencode serve (режим --attach).
|
||||
// enabled=true: ratatoskr сам запускает serve (супервайзер) и подключает
|
||||
// Runner через команду `run --attach <url>`. Если задан url — вместо
|
||||
// собственного spawn используется внешний (уже запущенный) сервер.
|
||||
// enabled=false (по умолчанию): историческая модель — каждый вызов
|
||||
// сам спавнит `opencode run` (без сервера).
|
||||
type ServeCfg struct {
|
||||
Enabled bool `yaml:"enabled" default:"false"`
|
||||
URL string `yaml:"url" env:"OPENCODE_SERVE_URL"`
|
||||
Hostname string `yaml:"hostname" default:"127.0.0.1"`
|
||||
Port int `yaml:"port" default:"4096"`
|
||||
Password string `yaml:"password" env:"OPENCODE_SERVE_PASSWORD"`
|
||||
}
|
||||
|
||||
type ChatCfg struct {
|
||||
|
||||
Reference in New Issue
Block a user