l-ai-agent-kaya/internal/buildinfo/buildinfo.go
alex 7758c097af
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Expose stamped /healthz JSON next to Prometheus metrics.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 16:28:20 +03:00

97 lines
2.4 KiB
Go

// Package buildinfo exposes stamped build metadata and the shared ops /healthz JSON.
package buildinfo
import (
"encoding/json"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// Populated at link time via -ldflags -X.
var (
Date = "unknown"
Commit = "dev"
Branch = "unknown"
)
// Hardcoded service identity for autodiscovery / status dashboard.
const (
Name = "l-ai-agent-kaya"
Description = "Tasks board agent Kaya: @kaya mention replies via core RunAgent"
)
// Tags is the controlled vocabulary for this service (see l-ai-docs services/healthz.md).
var Tags = []string{"agent", "tasks", "llm"}
var startedAt = time.Now()
func init() {
promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "build_info",
Help: "Build metadata for this process (always 1).",
},
[]string{"name", "commit", "branch", "date"},
).With(prometheus.Labels{
"name": Name,
"commit": Commit,
"branch": Branch,
"date": Date,
}).Set(1)
}
// Build is the nested build object in /healthz.
type Build struct {
Date string `json:"date"`
Commit string `json:"commit"`
Branch string `json:"branch"`
}
// Response is the shared /healthz JSON contract.
type Response struct {
Status string `json:"status"`
Name string `json:"name"`
Description string `json:"description"`
Tags []string `json:"tags"`
UptimeSeconds int64 `json:"uptime_seconds"`
Build Build `json:"build"`
}
// Snapshot returns the current health payload with status "ok".
func Snapshot() Response {
tags := make([]string, len(Tags))
copy(tags, Tags)
return Response{
Status: "ok",
Name: Name,
Description: Description,
Tags: tags,
UptimeSeconds: int64(time.Since(startedAt).Seconds()),
Build: Build{
Date: Date,
Commit: Commit,
Branch: Branch,
},
}
}
// Handler serves GET/HEAD /healthz as application/json (always 200 while process is up).
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
writeJSON(w, http.StatusOK, Snapshot())
})
}
func writeJSON(w http.ResponseWriter, code int, body Response) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(body)
}