-runtime.go에 에이전트 런 브리지 인터op 기능 추가 - runner.go에 HTTP invoccer와 워커 브리지 로직 추가 - postgres.go에 worker_bridge 관련 migration 및 저장소 계층 구현 - model.go에 WorkerBridge 엔터티 모델 추가 - main.go에 워커 브리지 초기화 및 HTTP 서버 통합 - config.go에 워커 브리지 설정 항목 추가 - migration에 worker_bridge 스키마 추가 - agent-roadmap에 iop-agent-run-bridge 페이즈 및 마일스톤 업데이트 - http_invoker.go 새로 추가하여 외부 HTTP 호출 추상화
125 lines
4 KiB
Go
125 lines
4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.toki-labs.com/toki/gito/services/core/internal/config"
|
|
"git.toki-labs.com/toki/gito/services/core/internal/controlplane"
|
|
"git.toki-labs.com/toki/gito/services/core/internal/core"
|
|
"git.toki-labs.com/toki/gito/services/core/internal/storage"
|
|
"git.toki-labs.com/toki/gito/services/core/internal/worker"
|
|
)
|
|
|
|
// migrationSQL loads the initial migration SQL from well-known paths relative
|
|
// to the executable or common working directories, so the server/worker works
|
|
// regardless of the cwd it is launched from.
|
|
func migrationSQL() string {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
exe = ""
|
|
}
|
|
candidates := []string{
|
|
// set explicitly via env
|
|
os.Getenv("GITO_MIGRATION_PATH"),
|
|
// next to the binary (bin/gito-server → bin/../migrations/...)
|
|
filepath.Join(filepath.Dir(exe), "migrations", "00001_initial.sql"),
|
|
// launched from repo root
|
|
filepath.Join("services", "core", "migrations", "00001_initial.sql"),
|
|
// launched from services/core
|
|
filepath.Join("migrations", "00001_initial.sql"),
|
|
}
|
|
for _, p := range candidates {
|
|
if p == "" {
|
|
continue
|
|
}
|
|
data, err := os.ReadFile(p)
|
|
if err == nil {
|
|
return string(data)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// pgAgentRunInputLoader loads durable agent run inputs from the Postgres store
|
|
// and converts them to the worker input type.
|
|
type pgAgentRunInputLoader struct {
|
|
store storage.AgentRunInputStore
|
|
}
|
|
|
|
func (l *pgAgentRunInputLoader) LoadAgentRunInput(ctx context.Context, op core.Operation) (worker.AgentRunInput, error) {
|
|
durable, err := l.store.GetAgentRunInput(ctx, op.ID)
|
|
if err != nil {
|
|
if errors.Is(err, storage.ErrAgentRunInputNotFound) {
|
|
return worker.AgentRunInput{}, fmt.Errorf("agent run input not found for operation %s: %w", op.ID, err)
|
|
}
|
|
return worker.AgentRunInput{}, fmt.Errorf("load agent run input: %w", err)
|
|
}
|
|
if strings.TrimSpace(durable.WorkspacePath) == "" || strings.TrimSpace(durable.Branch) == "" || strings.TrimSpace(durable.Instruction) == "" || strings.TrimSpace(durable.ExpectedRevision) == "" {
|
|
return worker.AgentRunInput{}, fmt.Errorf("agent run input for %s is missing required fields (workspace_path, branch, instruction, expected_revision)", op.ID)
|
|
}
|
|
return worker.AgentRunInput{
|
|
OperationID: durable.OperationID,
|
|
RepoID: durable.RepoID,
|
|
Branch: durable.Branch,
|
|
WorkspacePath: durable.WorkspacePath,
|
|
Instruction: durable.Instruction,
|
|
PolicyContext: durable.PolicyContext,
|
|
ExpectedRevision: durable.ExpectedRevision,
|
|
}, nil
|
|
}
|
|
|
|
func main() {
|
|
cfg := config.Load()
|
|
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
|
|
|
dsn := os.Getenv("DATABASE_URL")
|
|
if dsn == "" {
|
|
logger.Error("DATABASE_URL is required")
|
|
os.Exit(1)
|
|
}
|
|
|
|
pgStore, err := storage.NewPgStore(context.Background(), dsn, migrationSQL())
|
|
if err != nil {
|
|
logger.Error("failed to open postgres store", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
logger.Info("postgres store connected")
|
|
defer pgStore.Close()
|
|
|
|
publisher := controlplane.NewOutboxBroadcaster(pgStore.OperationEvents())
|
|
scanner := controlplane.NewRuntimeWithStore(publisher, pgStore)
|
|
|
|
iopEndpoint := strings.TrimSpace(cfg.IOPEndpoint)
|
|
if iopEndpoint == "" {
|
|
logger.Info("IOP_ENDPOINT not configured; agent_run picking disabled")
|
|
// Run with nil picker so worker logs the disabled message and exits cleanly.
|
|
w := worker.NewRunner(cfg, logger, nil, nil, scanner)
|
|
if err := w.Run(); err != nil {
|
|
logger.Error("worker stopped", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
return
|
|
}
|
|
|
|
inputLoader := &pgAgentRunInputLoader{store: pgStore.AgentRunInputs()}
|
|
invoker := worker.NewHTTPInvoker(iopEndpoint)
|
|
|
|
w := worker.NewRunnerWithDependencies(cfg, logger, nil, nil, worker.RunnerDependencies{
|
|
OperationStore: pgStore.Operations(),
|
|
OperationEventStore: pgStore.OperationEvents(),
|
|
AgentRunPicker: pgStore.Operations(),
|
|
AgentRunInputLoader: inputLoader,
|
|
AgentRunInvoker: invoker,
|
|
RevisionScanner: scanner,
|
|
})
|
|
if err := w.Run(); err != nil {
|
|
logger.Error("worker stopped", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|