feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out: los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms + E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client). - go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths relativos reajustados a la nueva ubicacion dentro de fn_registry). - app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales. - modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports). agents_and_robots queda archivado como museo de la era Matrix.
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ReadLogs returns all log entries for agentID between from and to (inclusive).
|
||||
func ReadLogs(baseDir, agentID string, from, to time.Time) ([]json.RawMessage, error) {
|
||||
var result []json.RawMessage
|
||||
for d := from; !d.After(to); d = d.AddDate(0, 0, 1) {
|
||||
entries, err := ReadDayLogs(baseDir, agentID, d)
|
||||
if err != nil {
|
||||
continue // skip missing days
|
||||
}
|
||||
result = append(result, entries...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReadDayLogs returns all log entries for a specific day.
|
||||
func ReadDayLogs(baseDir, agentID string, date time.Time) ([]json.RawMessage, error) {
|
||||
dir := filepath.Join(baseDir, agentID)
|
||||
prefix := date.Format("2006-01-02")
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read dir %s: %w", dir, err)
|
||||
}
|
||||
|
||||
var result []json.RawMessage
|
||||
for _, e := range entries {
|
||||
name := e.Name()
|
||||
if !strings.HasPrefix(name, prefix) || !isLogFile(name) {
|
||||
continue
|
||||
}
|
||||
lines, err := readLogFile(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, lines...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SearchLogs returns log entries where field equals value, within the date range.
|
||||
func SearchLogs(baseDir, agentID string, field, value string, from, to time.Time) ([]json.RawMessage, error) {
|
||||
all, err := ReadLogs(baseDir, agentID, from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var matched []json.RawMessage
|
||||
for _, raw := range all {
|
||||
var m map[string]any
|
||||
if json.Unmarshal(raw, &m) != nil {
|
||||
continue
|
||||
}
|
||||
if v, ok := m[field]; ok && fmt.Sprint(v) == value {
|
||||
matched = append(matched, raw)
|
||||
}
|
||||
}
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
// ListAgents returns the agent IDs that have log directories under baseDir.
|
||||
func ListAgents(baseDir string) ([]string, error) {
|
||||
entries, err := os.ReadDir(baseDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read base dir %s: %w", baseDir, err)
|
||||
}
|
||||
var ids []string
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
ids = append(ids, e.Name())
|
||||
}
|
||||
}
|
||||
sort.Strings(ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// ListDates returns the dates for which logs exist for the given agent.
|
||||
func ListDates(baseDir, agentID string) ([]time.Time, error) {
|
||||
dir := filepath.Join(baseDir, agentID)
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read dir %s: %w", dir, err)
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
var dates []time.Time
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || !isLogFile(e.Name()) {
|
||||
continue
|
||||
}
|
||||
d := parseDateFromFilename(e.Name())
|
||||
if d.IsZero() {
|
||||
continue
|
||||
}
|
||||
key := d.Format("2006-01-02")
|
||||
if !seen[key] {
|
||||
seen[key] = true
|
||||
dates = append(dates, d)
|
||||
}
|
||||
}
|
||||
sort.Slice(dates, func(i, j int) bool { return dates[i].Before(dates[j]) })
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
// readLogFile reads all JSONL lines from a file (.jsonl or .jsonl.gz).
|
||||
func readLogFile(path string) ([]json.RawMessage, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var r io.Reader = f
|
||||
if strings.HasSuffix(path, ".gz") {
|
||||
gz, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer gz.Close()
|
||||
r = gz
|
||||
}
|
||||
|
||||
var lines []json.RawMessage
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
cp := make([]byte, len(line))
|
||||
copy(cp, line)
|
||||
lines = append(lines, json.RawMessage(cp))
|
||||
}
|
||||
return lines, scanner.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user