v0.314.0: a restore puts the setup gate back as it was (R-1057); a test build moves with the floor (R-1053); Prowlarr links itself (R-1058); the box claims Plex by address (R-1060)
gates / gates (push) Successful in 47s

MinAgent 0.155.0 (unchanged). Tests red-proved by mutation (A: 10, B: 7, C: 1, C2: 2).

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-15 14:34:33 +02:00
parent e209a8d724
commit 17863fe8bc
22 changed files with 762 additions and 21 deletions
+19 -6
View File
@@ -368,7 +368,7 @@ var _ = settings.StoragePath{}
var plexClaimCode = regexp.MustCompile(`^claim-[A-Za-z0-9_-]{8,64}$`)
// appPlexClaimHandler (R-1028, `15` §10 K6): the household's code claims the RUNNING Plex — POST
// http://plex:32400/myplex/claim?token=<code> (no Plex token needed: the server is unclaimed). The code is never logged
// POST <plex by address>:32400/myplex/claim?token=<code> (no Plex token needed: the server is unclaimed; R-1060). The code is never logged
// and never stored. The setup gate opens only when Plex itself then says claimed (the probe), never on the press alone.
func (s *Server) appPlexClaimHandler(w http.ResponseWriter, r *http.Request) {
var req struct {
@@ -398,14 +398,21 @@ func (s *Server) appPlexClaimHandler(w http.ResponseWriter, r *http.Request) {
escrowJSON(w, http.StatusConflict, nil, s.msg(r, "err.plex_claim.not_claimed"))
}
// plexClaim is the call (a seam for tests).
// plexHTTP is the client for the box's own calls to Plex (a seam: tests dial a fake that refuses the name „plex").
var plexHTTP = &http.Client{Timeout: 30 * time.Second}
// plexClaim is the call (a seam for tests). R-1060 (v0.314.0): by ADDRESS — Plex answered 401 to the name „plex".
var plexClaim = func(code string) (int, error) {
req, err := http.NewRequest(http.MethodPost, "http://plex:32400/myplex/claim?token="+url.QueryEscape(code), nil)
base, err := mediawire.PlexURL(32400)
if err != nil {
return 0, err
}
req, err := http.NewRequest(http.MethodPost, base+"/myplex/claim?token="+url.QueryEscape(code), nil)
if err != nil {
return 0, err
}
req.Header.Set("Accept", "application/json")
resp, err := (&http.Client{Timeout: 30 * time.Second}).Do(req)
resp, err := plexHTTP.Do(req)
if err != nil {
return 0, err
}
@@ -447,12 +454,18 @@ func ejectStep(sp settings.StoragePath, disks []agentapi.DiskInfo) (int, *agenta
// measured 2026-10-15). A seam for tests. The live template carries NO setup_done_probe on purpose: a controller older
// than 0.313.0 would send no Accept header, read XML as „not JSON" and refuse the household's „Kész" forever.
var plexClaimed = func() bool {
req, err := http.NewRequest(http.MethodGet, "http://plex:32400/identity", nil)
base, err := mediawire.PlexURL(32400)
if err != nil {
return false
}
req, err := http.NewRequest(http.MethodGet, base+"/identity", nil)
if err != nil {
return false
}
req.Header.Set("Accept", "application/json")
resp, err := (&http.Client{Timeout: 5 * time.Second}).Do(req)
ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
defer cancel()
resp, err := plexHTTP.Do(req.WithContext(ctx))
if err != nil {
return false
}