Add concurrency caps, the two-pane web UI, AGENTS.md, and plan status
This commit is contained in:
@@ -54,7 +54,7 @@ func New(dataDir string, spa fs.FS) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.agents = agents.New(s.store)
|
||||
s.agents = agents.New(s.store, agents.DefaultLimits)
|
||||
err = s.agents.Recover(context.Background())
|
||||
if err != nil {
|
||||
s.store.Close()
|
||||
@@ -325,6 +325,12 @@ func (s *Server) GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
usage, err := s.agents.Usage(r.Context())
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
dbBytes := int64(0)
|
||||
for _, suffix := range []string{"", "-wal", "-shm"} {
|
||||
fi, err := os.Stat(s.store.Path() + suffix)
|
||||
@@ -341,9 +347,36 @@ func (s *Server) GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ActiveTasks: c.ActiveTasks,
|
||||
SseClients: s.hub.count(),
|
||||
LiveAgents: s.agents.LiveCount(),
|
||||
RunningTurns: usage.Running,
|
||||
QueuedTurns: usage.Queued,
|
||||
RateLimits: usage.RateLimits,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) ListTaskEvents(w http.ResponseWriter, r *http.Request, taskID string, params api.ListTaskEventsParams) {
|
||||
_, err := s.store.GetTask(r.Context(), taskID)
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
after := int64(0)
|
||||
if params.After != nil {
|
||||
after = *params.After
|
||||
}
|
||||
limit := 500
|
||||
if params.Limit != nil {
|
||||
limit = *params.Limit
|
||||
}
|
||||
|
||||
events, err := s.store.TaskEvents(r.Context(), taskID, after, limit)
|
||||
if err != nil {
|
||||
writeStoreError(w, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, events)
|
||||
}
|
||||
|
||||
func (s *Server) ListSessions(w http.ResponseWriter, r *http.Request, taskID string) {
|
||||
_, err := s.store.GetTask(r.Context(), taskID)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"g.fc.run/misc/byre/internal/api"
|
||||
"g.fc.run/misc/byre/internal/server"
|
||||
"g.fc.run/misc/byre/web"
|
||||
)
|
||||
|
||||
const login = "alice@example.com"
|
||||
@@ -105,6 +106,47 @@ func TestIdentityRequired(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServesEmbeddedSPA(t *testing.T) {
|
||||
srv, err := server.New(t.TempDir(), web.FS)
|
||||
if err != nil {
|
||||
t.Fatalf("new server: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { srv.Close() })
|
||||
hs := httptest.NewServer(srv.Handler())
|
||||
t.Cleanup(hs.Close)
|
||||
|
||||
for path, contentType := range map[string]string{
|
||||
"/": "text/html",
|
||||
"/style.css": "text/css",
|
||||
"/dist/app.js": "text/javascript",
|
||||
"/dist/app.js.map": "",
|
||||
"/src/app.ts": "",
|
||||
"/dist/inbox.js": "text/javascript",
|
||||
"/dist/api.gen.js": "text/javascript",
|
||||
"/dist/tasks.js": "text/javascript",
|
||||
"/dist/navbar.js": "text/javascript",
|
||||
"/dist/panes.js": "text/javascript",
|
||||
"/dist/task.js": "text/javascript",
|
||||
"/dist/state.js": "text/javascript",
|
||||
"/dist/dom.js": "text/javascript",
|
||||
"/dist/api.js": "text/javascript",
|
||||
"/fonts/atkinson-hyperlegible-mono-latin-wght-normal.woff2": "font/woff2",
|
||||
"/fonts/LICENSE": "",
|
||||
} {
|
||||
resp, err := http.Get(hs.URL + path)
|
||||
if err != nil {
|
||||
t.Fatalf("get %s: %v", path, err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("get %s: %d", path, resp.StatusCode)
|
||||
}
|
||||
if contentType != "" && !strings.HasPrefix(resp.Header.Get("Content-Type"), contentType) {
|
||||
t.Fatalf("get %s: content type %q, want %s", path, resp.Header.Get("Content-Type"), contentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMe(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user