Skip to content

Commit d50e681

Browse files
committed
feat: add runtime agent version tracking and update metrics reporting
1 parent bd8807a commit d50e681

5 files changed

Lines changed: 52 additions & 28 deletions

File tree

internal/agent/service.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
"github.com/noderax/noderax-agent/internal/config"
1414
"github.com/noderax/noderax-agent/internal/metrics"
1515
"github.com/noderax/noderax-agent/internal/realtime"
16-
"github.com/noderax/noderax-agent/internal/terminal"
1716
"github.com/noderax/noderax-agent/internal/tasks"
17+
"github.com/noderax/noderax-agent/internal/terminal"
1818
)
1919

2020
type Service struct {
@@ -148,6 +148,9 @@ func NewService(cfg config.Config, client *api.Client, logger *slog.Logger, vers
148148
if err != nil {
149149
logger.Error("failed to initialize realtime service", "error", err)
150150
}
151+
if realtimeService != nil {
152+
realtimeService.SetRuntimeAgentVersion(version)
153+
}
151154

152155
var terminalEvents terminal.RealtimeEvents
153156
if realtimeService != nil {

internal/realtime/client.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,19 @@ type Stats struct {
4444
}
4545

4646
type Service struct {
47-
logger *slog.Logger
48-
requestTimeout time.Duration
49-
pingInterval time.Duration
50-
jitterRatio float64
51-
credentials func() (string, string)
52-
dispatcher *dispatcher
53-
onAuthSuccess AuthSuccessHook
54-
dialURL string
55-
healthURL string
56-
namespace string
57-
path string
58-
outbound chan any
47+
logger *slog.Logger
48+
requestTimeout time.Duration
49+
pingInterval time.Duration
50+
jitterRatio float64
51+
credentials func() (string, string)
52+
dispatcher *dispatcher
53+
onAuthSuccess AuthSuccessHook
54+
dialURL string
55+
healthURL string
56+
namespace string
57+
path string
58+
runtimeAgentVersion string
59+
outbound chan any
5960

6061
reconnects atomic.Int64
6162
pingsSent atomic.Int64
@@ -352,6 +353,7 @@ func (s *Service) SendMetrics(ctx context.Context, event api.MetricsRequest) err
352353
Type: EventAgentMetrics,
353354
NodeID: event.NodeID,
354355
AgentToken: event.AgentToken,
356+
AgentVersion: s.runtimeAgentVersion,
355357
Timestamp: formatTimestampUTCMillis(event.CollectedAt),
356358
CPUUsage: &cpuUsage,
357359
MemoryUsage: &memoryUsage,
@@ -374,6 +376,10 @@ func (s *Service) ReportDispatchHandled() {
374376
s.dispatchHandled.Add(1)
375377
}
376378

379+
func (s *Service) SetRuntimeAgentVersion(version string) {
380+
s.runtimeAgentVersion = strings.TrimSpace(version)
381+
}
382+
377383
func (s *Service) SnapshotStats() Stats {
378384
return Stats{
379385
Reconnects: s.reconnects.Load(),
@@ -512,7 +518,12 @@ func (s *Service) connect(ctx context.Context) (*socketIOConn, error) {
512518
}
513519

514520
socket.OnConnect(func() {
515-
authPayload := authEvent{Type: EventAgentAuth, NodeID: nodeID, AgentToken: agentToken}
521+
authPayload := authEvent{
522+
Type: EventAgentAuth,
523+
NodeID: nodeID,
524+
AgentToken: agentToken,
525+
AgentVersion: s.runtimeAgentVersion,
526+
}
516527
socket.Emit(EventAgentAuth, authPayload)
517528
})
518529

internal/realtime/client_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func TestSendMetricsUsesMillisecondTimestamp(t *testing.T) {
5757

5858
svc := &Service{outbound: make(chan any, 1), logger: slog.New(slog.NewTextHandler(io.Discard, nil))}
5959
svc.sessionActive.Store(true)
60+
svc.SetRuntimeAgentVersion("1.0.0")
6061
collectedAt := time.Date(2026, 3, 20, 10, 20, 30, 456789123, time.UTC)
6162

6263
err := svc.SendMetrics(context.Background(), api.MetricsRequest{
@@ -82,6 +83,9 @@ func TestSendMetricsUsesMillisecondTimestamp(t *testing.T) {
8283
if event.AgentToken != "token-1" {
8384
t.Fatalf("unexpected agent token: %q", event.AgentToken)
8485
}
86+
if event.AgentVersion != "1.0.0" {
87+
t.Fatalf("unexpected agent version: %q", event.AgentVersion)
88+
}
8589

8690
pattern := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$`)
8791
if !pattern.MatchString(event.Timestamp) {

internal/realtime/handlers.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import (
1010
)
1111

1212
const (
13-
EventAgentAuth = "agent.auth"
14-
EventAgentAuthAck = "agent.auth.ack"
15-
EventAgentAuthErr = "agent.auth.error"
16-
EventAgentError = "agent.error"
17-
EventAgentPing = "agent.ping"
18-
EventAgentMetrics = "agent.metrics"
13+
EventAgentAuth = "agent.auth"
14+
EventAgentAuthAck = "agent.auth.ack"
15+
EventAgentAuthErr = "agent.auth.error"
16+
EventAgentError = "agent.error"
17+
EventAgentPing = "agent.ping"
18+
EventAgentMetrics = "agent.metrics"
1919
EventTerminalStart = "terminal.start"
2020
EventTerminalInput = "terminal.input"
2121
EventTerminalResize = "terminal.resize"
@@ -24,11 +24,11 @@ const (
2424
EventTerminalOutput = "terminal.output"
2525
EventTerminalExited = "terminal.exited"
2626
EventTerminalError = "terminal.error"
27-
EventTaskDispatch = "task.dispatch"
28-
EventTaskAccepted = "task.accepted"
29-
EventTaskStarted = "task.started"
30-
EventTaskLog = "task.log"
31-
EventTaskComplete = "task.completed"
27+
EventTaskDispatch = "task.dispatch"
28+
EventTaskAccepted = "task.accepted"
29+
EventTaskStarted = "task.started"
30+
EventTaskLog = "task.log"
31+
EventTaskComplete = "task.completed"
3232
)
3333

3434
type taskDispatcher interface {
@@ -128,9 +128,10 @@ func (d *dispatcher) handleMessage(ctx context.Context, data []byte) error {
128128
}
129129

130130
type authEvent struct {
131-
Type string `json:"type"`
132-
NodeID string `json:"nodeId"`
133-
AgentToken string `json:"agentToken"`
131+
Type string `json:"type"`
132+
NodeID string `json:"nodeId"`
133+
AgentToken string `json:"agentToken"`
134+
AgentVersion string `json:"agentVersion,omitempty"`
134135
}
135136

136137
type pingEvent struct {
@@ -142,6 +143,7 @@ type metricsEvent struct {
142143
Type string `json:"type"`
143144
NodeID string `json:"nodeId"`
144145
AgentToken string `json:"agentToken"`
146+
AgentVersion string `json:"agentVersion,omitempty"`
145147
Timestamp string `json:"timestamp"`
146148
CPUUsage *float64 `json:"cpuUsage,omitempty"`
147149
MemoryUsage *float64 `json:"memoryUsage,omitempty"`

internal/realtime/integration_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ func TestRealtimeConnectAuthDispatchLifecycle(t *testing.T) {
139139
if err != nil {
140140
t.Fatalf("NewService() error = %v", err)
141141
}
142+
svc.SetRuntimeAgentVersion("1.0.0")
142143

143144
runCtx, cancel := context.WithCancel(context.Background())
144145
done := make(chan error, 1)
@@ -151,6 +152,9 @@ func TestRealtimeConnectAuthDispatchLifecycle(t *testing.T) {
151152
if auth.Type != EventAgentAuth || auth.NodeID != "node-1" || auth.AgentToken != "token-1" {
152153
t.Fatalf("unexpected auth payload: %+v", auth)
153154
}
155+
if auth.AgentVersion != "1.0.0" {
156+
t.Fatalf("unexpected auth payload: %+v", auth)
157+
}
154158
case <-time.After(8 * time.Second):
155159
t.Fatalf("timed out waiting for auth event")
156160
}

0 commit comments

Comments
 (0)