gito/services/core/internal/protosocket/dispatcher.go
toki c9952509e9 feat(core): Forgejo 브랜치 이벤트 및 런타임 채널 인프라를 추가한다
- ControlPlane 라우터에 워커/에이전트 구독 채널을 추가한다
- ProtoSocket 기반 실시간 양방향 채널( dispatcher, envelope, server)을 구현한다
- Forgejo push 이벤트를 provider 어댑터로 처리하도록 연동한다
- PostgreSQL 스토리지 백엔드를 분리하여 postgres.go로 신설한다
- Git engine command에 diff/checkout/merge 연쇄 연산을 추가한다
- 런타임 모델에 agentSession, runtimeChannel 스키마를 추가한다
- 데이터베이스 마이그레이션에 agent_session, runtime_channel, lease 테이블을 추가한다
- agent-contract에 Forgejo branch events 계약 문서를 작성한다
- 로드맵 마일스톤과 아카이브 작업을 갱신한다
2026-06-13 19:16:03 +09:00

83 lines
1.8 KiB
Go

package protosocket
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"sync"
)
type HandlerFunc func(context.Context, Envelope) Envelope
type Dispatcher struct {
mu sync.RWMutex
handlers map[string]HandlerFunc
}
func NewDispatcher() *Dispatcher {
return &Dispatcher{handlers: make(map[string]HandlerFunc)}
}
func (d *Dispatcher) Register(action string, handler HandlerFunc) {
d.mu.Lock()
defer d.mu.Unlock()
d.handlers[action] = handler
}
func (d *Dispatcher) Dispatch(ctx context.Context, env Envelope) Envelope {
d.mu.RLock()
handler, ok := d.handlers[env.Action]
d.mu.RUnlock()
if !ok {
return ErrorResponse(env, "UNSUPPORTED_ACTION", fmt.Sprintf("unsupported action: %s", env.Action), false)
}
return handler(ctx, env)
}
func SuccessResponse(req Envelope, payload map[string]any) Envelope {
return Envelope{
ProtocolVersion: ProtocolVersion,
ID: generateID(),
CorrelationID: req.ID,
Type: "response",
Channel: req.Channel,
Action: req.Action,
Payload: payload,
}
}
func ErrorResponse(req Envelope, code, message string, retryable bool) Envelope {
return Envelope{
ProtocolVersion: ProtocolVersion,
ID: generateID(),
CorrelationID: req.ID,
Type: "error",
Channel: req.Channel,
Action: req.Action,
Error: &EnvelopeError{
Code: code,
Message: message,
Retryable: retryable,
},
}
}
func NewEventEnvelope(action string, payload map[string]any) Envelope {
return Envelope{
ProtocolVersion: ProtocolVersion,
ID: generateID(),
Type: "event",
Channel: "event",
Action: action,
Payload: payload,
}
}
func generateID() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "id-fallback"
}
return hex.EncodeToString(b)
}