tinyrelaycommit

Commit 48dadd179533

browse files at this commit | history

1diff --git a/internal/daemon/agents_test.go b/internal/daemon/agents_test.go
2index a2b2d62..d3734c8 100644
3--- a/internal/daemon/agents_test.go
4+++ b/internal/daemon/agents_test.go
5@@ -255,3 +255,56 @@ func TestAgentManagementMethodsAndPermissions(t *testing.T) {
6 }
7 }
8 }
9+
10+func TestBrowseAgentReturnsGrantAndRecentEventsForOperators(t *testing.T) {
11+ _, tenant := testTenant(t)
12+ ctx := context.Background()
13+ owner := tenant.Policy().Owner
14+ agent, _ := event.PublicKey(testAgentSecret)
15+ member, _ := event.PublicKey(testMemberSecret)
16+ moderator, _ := event.PublicKey(testModSecret)
17+ now := time.Now().Unix()
18+ for _, target := range []struct {
19+ pubkey, role string
20+ }{{member, "member"}, {moderator, "moderator"}} {
21+ raw, _ := json.Marshal(map[string]string{"role": target.role})
22+ if _, err := tenant.Execute(ctx, owner, "setmember", []json.RawMessage{json.RawMessage(strconv.Quote(target.pubkey)), raw}); err != nil {
23+ t.Fatal(err)
24+ }
25+ }
26+ if err := publishAs(t, tenant, agentGrantEvent(t, agent, now, now+3600, []string{"k", "1"}, []string{"room", "build"})); err != nil {
27+ t.Fatal(err)
28+ }
29+ for i := int64(0); i < 12; i++ {
30+ if err := publishAs(t, tenant, signedEvent(t, testAgentSecret, 1, now-20+i, nil, "note "+strconv.FormatInt(i, 10))); err != nil {
31+ t.Fatal(err)
32+ }
33+ }
34+ params := []json.RawMessage{json.RawMessage(`{"agent":"` + agent + `"}`)}
35+ for _, actor := range []string{member, agent, ""} {
36+ if _, err := tenant.Execute(ctx, actor, "browseagent", params); err == nil || !strings.HasPrefix(err.Error(), "restricted:") {
37+ t.Fatalf("browseagent by %q: %v", actor, err)
38+ }
39+ }
40+ for _, actor := range []string{owner, moderator} {
41+ result, err := tenant.Execute(ctx, actor, "browseagent", params)
42+ if err != nil {
43+ t.Fatalf("browseagent by %s: %v", actor, err)
44+ }
45+ detail := result.(map[string]any)
46+ summary := detail["agent"].(community.AgentSummary)
47+ events := detail["events"].([]event.Event)
48+ if summary.Agent != agent || summary.Name != "helper" || summary.Scope.Rooms[0] != "build" || summary.LastEvent != now-9 {
49+ t.Fatalf("browseagent summary = %+v", summary)
50+ }
51+ if len(events) != 10 || events[0].CreatedAt != now-9 || events[9].CreatedAt != now-18 {
52+ t.Fatalf("browseagent events = %d newest %d", len(events), events[0].CreatedAt)
53+ }
54+ }
55+ if _, err := tenant.Execute(ctx, owner, "browseagent", []json.RawMessage{json.RawMessage(`{"agent":"` + strings.Repeat("e", 64) + `"}`)}); err == nil || !strings.HasPrefix(err.Error(), "not found:") {
56+ t.Fatalf("unknown agent: %v", err)
57+ }
58+ if _, err := tenant.Execute(ctx, owner, "browseagent", []json.RawMessage{json.RawMessage(`{"agent":"nope"}`)}); err == nil || !strings.HasPrefix(err.Error(), "invalid:") {
59+ t.Fatalf("bad agent key: %v", err)
60+ }
61+}
62diff --git a/internal/daemon/client_browse.go b/internal/daemon/client_browse.go
63index 6c5dc7b..fc2e26a 100644
64--- a/internal/daemon/client_browse.go
65+++ b/internal/daemon/client_browse.go
66@@ -10,12 +10,15 @@ import (
67 "net/url"
68 "sort"
69 "strings"
70+ "time"
71 "unicode/utf8"
72
73 "github.com/FelineStateMachine/tinyrelay/internal/blob"
74+ "github.com/FelineStateMachine/tinyrelay/internal/community"
75 "github.com/FelineStateMachine/tinyrelay/internal/event"
76 "github.com/FelineStateMachine/tinyrelay/internal/gitrelay"
77 "github.com/FelineStateMachine/tinyrelay/internal/relay"
78+ "github.com/FelineStateMachine/tinyrelay/internal/storage"
79 )
80
81 type clientBrowseRequest struct {
82@@ -27,10 +30,11 @@ type clientBrowseRequest struct {
83 ID string `json:"id"`
84 State string `json:"state"`
85 Label string `json:"label"`
86+ Agent string `json:"agent"`
87 }
88
89 func clientBrowseMethod(method string) bool {
90- return wikiBrowseMethod(method) || containsString([]string{"browserepos", "browserepo", "browsefiles", "browsefile", "browsestatus", "browseissues", "browsepulls", "browseissue", "browsepull"}, method)
91+ return wikiBrowseMethod(method) || containsString([]string{"browserepos", "browserepo", "browsefiles", "browsefile", "browsestatus", "browseissues", "browsepulls", "browseissue", "browsepull", "browseagent"}, method)
92 }
93
94 func (t *Tenant) browseRead(ctx context.Context, actor string) error {
95@@ -77,6 +81,8 @@ func (t *Tenant) executeBrowse(ctx context.Context, actor, method string, params
96 return t.browseFile(ctx, actor, q.Hash)
97 case "browsestatus":
98 return t.browseStatus(ctx, actor)
99+ case "browseagent":
100+ return t.browseAgent(ctx, actor, q.Agent)
101 case "browseissues":
102 return t.browseCollaboration(ctx, actor, q, event.KIND_GIT_ISSUE)
103 case "browsepulls":
104@@ -356,6 +362,37 @@ func (t *Tenant) browseFile(ctx context.Context, actor, hash string) (any, error
105 return result, nil
106 }
107
108+// browseAgent returns one agent grant with the agent's newest events for the
109+// Manage > Agents page. Like listagents, it is for the owner and moderators.
110+func (t *Tenant) browseAgent(ctx context.Context, actor, agent string) (any, error) {
111+ role, err := t.community.Role(ctx, actor)
112+ if err != nil {
113+ return nil, err
114+ }
115+ if role != "owner" && role != "moderator" {
116+ return nil, errors.New("restricted: agent details")
117+ }
118+ if len(agent) != 64 || strings.Trim(agent, "0123456789abcdef") != "" {
119+ return nil, errors.New("invalid: agent public key required")
120+ }
121+ grant, ok, err := t.community.AgentGrant(ctx, agent)
122+ if err != nil {
123+ return nil, err
124+ }
125+ if !ok {
126+ return nil, errors.New("not found: no agent grant for this pubkey")
127+ }
128+ rows, err := t.store.Query(ctx, event.Filter{Authors: []string{agent}, Tags: map[string][]string{}}, storage.QueryOptions{Now: time.Now().Unix(), Access: storage.Access{All: true}, Limit: 10})
129+ if err != nil {
130+ return nil, err
131+ }
132+ summary := community.AgentSummary{AgentGrant: grant}
133+ if len(rows.Events) > 0 {
134+ summary.LastEvent = rows.Events[0].CreatedAt
135+ }
136+ return map[string]any{"agent": summary, "events": rows.Events}, nil
137+}
138+
139 func (t *Tenant) browseStatus(ctx context.Context, actor string) (any, error) {
140 role, err := t.community.Role(ctx, actor)
141 if err != nil {

About

ownerc01503e8035f
visibilitypublic
head48dadd179533

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs