diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..257f268 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git +data +web/node_modules +web/dist diff --git a/.gitignore b/.gitignore index 7939cf2..73e5dd6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,12 @@ .orchestra-config/ clients/ /orchestra + +# Web UI build inputs/outputs. node_modules in particular ships vendored Go +# packages (e.g. flatted/golang), so leaving it merely untracked is not +# enough — `go build ./...` and `go test ./...` walk into it. +/package-lock.json +node_modules/ +.node_modules/ +web/dist/ +web/tsconfig.tsbuildinfo diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..acd081f --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,17 @@ +FROM golang:1.22-alpine AS build +WORKDIR /src +COPY go.mod ./ +RUN go mod download +COPY . ./ +RUN go build -trimpath -ldflags='-s -w' -o /out/orchestra ./cmd/orchestra + +FROM alpine:3.21 +RUN adduser -D -u 10001 orchestra +WORKDIR /app +COPY --from=build /out/orchestra /app/orchestra +COPY deploy/docker-api-entrypoint.sh /app/docker-api-entrypoint.sh +RUN mkdir /data && chown orchestra:orchestra /data +ENV ORCHESTRA_DATA=/data ORCHESTRA_PORT=9145 +VOLUME ["/data"] +EXPOSE 9145 +ENTRYPOINT ["/app/docker-api-entrypoint.sh"] diff --git a/WEB_UI_PLAN.md b/WEB_UI_PLAN.md new file mode 100644 index 0000000..67df912 --- /dev/null +++ b/WEB_UI_PLAN.md @@ -0,0 +1,164 @@ +# Orchestra Web UI Plan + +## Goal + +Build a proper TypeScript single-page application for local operation of +Orchestra. It must make live task/session state observable, make every +supported control actionable, and render harness approval requests as the +actual pending decision rather than as a vague `blocked` badge. Authentication +is deliberately out of scope for this first local-only version; the API +contract must still keep a clean boundary where auth can be added later. + +## Product scope + +### Monitoring + +- Task board with queued, leased, blocked, completed, and failed states. +- Task detail with description, lifecycle timeline, current worker/harness, + lease expiry, handoff/report references, and Git/worktree facts when a + session is live. +- Worker view: online/offline state, capacity, last heartbeat, active task, + and harness identity. +- Session view: agent status, occupancy, turn-boundary health, recent pane + capture, extracted blocker, and update timestamp. +- Artifact view for handoffs and reports, with JSON formatting for canonical + handoffs and plain-text/Markdown rendering for reports. + +### Controls + +- Create a task with all currently accepted task fields. +- Request a handoff/rotation through the existing task-control path. +- Grant or deny a currently displayed harness permission. +- Explicitly release, block, or complete a task only where the backend can + validate the required evidence. +- Refresh and polling controls; no control is rendered merely as decoration. + +## Architecture + +### Frontend + +Create `web/` as a Vite + React + TypeScript application. + +- React Router routes: `/`, `/tasks/:taskID`, `/workers`, and `/artifacts/:ref`. +- TanStack Query for polling, cache invalidation after controls, retry/error + state, and optimistic UI only after the server returns success. +- A small component layer (CSS modules or Tailwind, chosen during setup) with + accessible dialogs, keyboard-focus handling, and responsive layouts. +- Build output is embedded into the Go binary with `go:embed` and served at + `/`; `/v1/*` remains API-only. The Go server serves `index.html` as the + SPA fallback and static assets with cache headers. +- A typed API client generated from hand-maintained TypeScript interfaces in + `web/src/api/types.ts`; those interfaces are exercised against Go JSON + response tests so frontend/backend field drift fails CI. + +### Backend API additions + +Keep existing endpoints working. Add a UI-oriented read model rather than +forcing the browser to join unrelated raw endpoints itself. + +| Endpoint | Purpose | +|---|---| +| `GET /v1/ui/overview` | Tasks, workers, orchestration health, and active-session summaries in one pollable response. | +| `GET /v1/ui/tasks/{id}` | Full task detail, ordered lifecycle events, active session, blocker, artifact refs, and available actions. | +| `GET /v1/ui/tasks/{id}/capture?source=recent` | Current pane capture plus revision/timestamp; never silently returns stale text as live. | +| `GET /v1/artifacts/{ref}` | Existing artifact fetch, with content type supplied by the UI read model where known. | +| `POST /v1/ui/tasks` | Create task; validates the same fields as `POST /v1/tasks`. | +| `POST /v1/ui/tasks/{id}/actions/{action}` | Narrow action wrapper for handoff request, approval grant/deny, release, block, and completion; returns the resulting task/session state. | + +The raw lifecycle endpoints stay available for harness/worker use. The UI +wrapper is responsible for describing whether an action is currently allowed, +why it is unavailable, and what evidence/body it needs. + +## Correct approval design + +The approval UI must reflect the real permission prompt, not the existing +event-only `ApprovalGranted`/`ApprovalDenied` records. + +1. Add a structured `PendingApproval` model: + + ```ts + type PendingApproval = { + kind: "shell" | "edit" | "unknown"; + summary: string; + command?: string; + diff?: string; + paneId: string; + captureRevision: number; + detectedAt: string; + }; + ``` + +2. Parse the current `pane.read` text worker-side. Preserve the exact shell + command or proposed edit/diff shown by the harness. If parsing is + uncertain, return `kind: "unknown"` with the raw relevant excerpt and + disable the grant button; do not guess. + +3. Make approval a real execution command. `grant` sends the correct + `agent.send_keys`/`pane.send_keys` confirmation to the pane only if the + same `pane_id`, permission kind, and capture revision are still pending. + `deny` sends the explicit reject key sequence. Record the corresponding + `ApprovalGranted`/`ApprovalDenied` event only after the herdr action is + acknowledged. + +4. The coordinator cannot directly control a workpc-owned Unix herdr socket. + Extend the federation worker protocol with a durable, worker-pulled + control-command stream (or command queue): + + - coordinator queues `grant_approval` / `deny_approval` addressed to a + worker and task; + - worker validates its local current capture revision and sends the key; + - worker posts an acknowledged result or a stale/rejected result; + - the UI polls the final result and removes/enables controls accordingly. + + This is required before rendering remote approval buttons as enabled. + A local coordinator-owned session may use the same command contract via a + local implementation, so UI behavior is identical across machines. + +5. Render approvals in an accessible modal and task-detail panel: visible + command/diff, target pane, capture timestamp, keyboard focus trapped in + the dialog, `Approve` and `Reject` buttons, a stale-state warning, and no + implicit approval on refresh or Enter outside the focused button. + +## UI layout + +- **Overview:** summary counters, worker health strip, active/blocked session + cards, and the task board. +- **Task detail:** immutable task instruction, timeline, session telemetry, + live capture, handoff/report evidence, and the contextual action panel. +- **Session capture:** terminal-style, selectable text with an explicit + source label (`recent`, `scrollback`, etc.), refresh time, and truncation + indicator. +- **Approval dialog:** command or edit preview first, then consequence and + worker/pane metadata; approval controls only when `PendingApproval` is + current and actionable. +- **Workers:** capacity/heartbeat table, active lease/session link, and + visible offline/degraded reasons. + +## Delivery phases + +1. **Contracts and read model** — define Go UI DTOs, add overview/task-detail + endpoints, tests, and structured local session/capture/approval parsing. +2. **React shell** — scaffold Vite/React/TypeScript, embed production build, + implement overview, task detail, workers, polling, loading, and error + states. +3. **Evidence and controls** — artifact viewer, task creation, action + confirmation UX, and disabled-state explanations. +4. **Federated approvals** — worker command queue, stale-revision protection, + actual grant/deny execution, audit events, and UI result handling. +5. **Verification** — Go API tests, TypeScript unit/component tests, Playwright + flows, and a live workpc OpenCode E2E covering: task visible → permission + rendered with exact command → explicit approval → pane advances → action + result and lifecycle update visible. + +## Acceptance criteria + +- `npm run build`, `npm run lint`, TypeScript checks, Go build/vet/tests, and + UI browser tests pass in CI. +- Every rendered active session has a real source endpoint and a visible + degraded/error state if capture is unavailable. +- Every enabled control reaches a tested backend path; no placeholder buttons. +- A remote approval cannot be granted against a different or stale prompt. +- The approval panel displays the exact pending command or edit preview from + the current pane capture. +- The writer/reviewer workflow can be monitored from task creation through + completion without SSH or raw JSON-RPC inspection. diff --git a/cmd/orchestra-worker/main.go b/cmd/orchestra-worker/main.go index 79c966d..62e2474 100644 --- a/cmd/orchestra-worker/main.go +++ b/cmd/orchestra-worker/main.go @@ -234,6 +234,78 @@ func (w *worker) releaseReady(ctx context.Context) { } } +// publishCaptures makes remote panes observable without allowing the +// coordinator to touch their unix herdr socket. +func (w *worker) publishCaptures(ctx context.Context) { + for taskID, session := range w.sessions { + text, err := (herdr.CLIAdapter{Client: w.herdr, Harness: w.harness}).PaneCapture(ctx, session, "recent") + if err != nil { + continue + } + if _, err := w.api.PublishCapture(ctx, federation.Capture{TaskID: taskID, PaneID: session.PaneID, Text: text}); err != nil { + log.Printf("publish capture %s: %v", taskID, err) + } + } +} + +func approvalResponse(text, kind string) (string, bool) { + low := strings.ToLower(text) + // Never invent a keystroke. y/n prompts label both decisions directly. + if strings.Contains(low, "[y/n]") || strings.Contains(low, "(y/n)") { + if kind == "grant_approval" { + return "y\n", true + } + return "n\n", true + } + // OpenCode's explicit selector states "Allow once Allow always Reject" + // and "enter confirm". Enter is consequently a bounded one-time grant; + // rejection would require unobservable selector navigation, so refuse it. + if kind == "grant_approval" && strings.Contains(low, "allow once") && strings.Contains(low, "allow always") && strings.Contains(low, "reject") && strings.Contains(low, "enter confirm") { + return "\n", true + } + return "", false +} +func (w *worker) runCommands(ctx context.Context) { + commands, err := w.api.Commands(ctx) + if err != nil { + log.Printf("poll controls: %v", err) + return + } + for _, command := range commands { + session, ok := w.sessions[command.TaskID] + if !ok || session.PaneID != command.PaneID { + _ = w.api.ResolveCommand(ctx, command.ID, "stale", "session or pane changed") + continue + } + text, err := (herdr.CLIAdapter{Client: w.herdr, Harness: w.harness}).PaneCapture(ctx, session, "recent") + if err != nil { + _ = w.api.ResolveCommand(ctx, command.ID, "rejected", "capture unavailable: "+err.Error()) + continue + } + capture, err := w.api.PublishCapture(ctx, federation.Capture{TaskID: command.TaskID, PaneID: session.PaneID, Text: text}) + if err != nil { + _ = w.api.ResolveCommand(ctx, command.ID, "rejected", "cannot publish capture: "+err.Error()) + continue + } + if capture.Revision != command.CaptureRevision { + _ = w.api.ResolveCommand(ctx, command.ID, "stale", "capture revision changed") + continue + } + input, ok := approvalResponse(text, command.Kind) + if !ok { + _ = w.api.ResolveCommand(ctx, command.ID, "rejected", "prompt does not expose an executable approval control") + continue + } + if err := w.herdr.Call(ctx, "pane.send_text", map[string]any{"pane_id": session.PaneID, "text": input}, nil); err != nil { + _ = w.api.ResolveCommand(ctx, command.ID, "rejected", "herdr did not acknowledge input: "+err.Error()) + continue + } + if err := w.api.ResolveCommand(ctx, command.ID, "acknowledged", ""); err != nil { + log.Printf("ack command %s: %v", command.ID, err) + } + } +} + func (w *worker) once(ctx context.Context) error { es, _, err := w.api.Events(ctx, w.cursor) if err != nil { @@ -297,6 +369,13 @@ func (w *worker) once(ctx context.Context) error { } } } + // Unit/replay-only workers intentionally have no herdr connection. A + // production worker always does, and only then participates in the live + // capture/control protocol. + if w.herdr != nil { + w.publishCaptures(ctx) + w.runCommands(ctx) + } w.releaseReady(ctx) if err := w.save(); err != nil { return err diff --git a/cmd/orchestra-worker/main_test.go b/cmd/orchestra-worker/main_test.go index 95730ee..8650546 100644 --- a/cmd/orchestra-worker/main_test.go +++ b/cmd/orchestra-worker/main_test.go @@ -15,6 +15,7 @@ import ( "os/exec" "path/filepath" "testing" + "time" ) func TestWorkerReregistersAfterCoordinatorForgetsIt(t *testing.T) { @@ -172,3 +173,87 @@ func TestWorkerStartsRouterIssuedLeaseInLocalGitWorktree(t *testing.T) { t.Fatalf("TASK.md: %v", err) } } + +func TestWorkerApprovalCommandIsRevisionBoundAndAcknowledged(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + sent := make(chan string, 1) + go func() { + for { + c, err := ln.Accept() + if err != nil { + return + } + go func() { + defer c.Close() + var request herdr.Request + if json.NewDecoder(c).Decode(&request) != nil { + return + } + result := `{}` + switch request.Method { + case "pane.read": + result = `{"read":{"text":"Permission required\n$ git status\nProceed? [y/n]"}}` + case "pane.send_text": + var p struct { + Text string `json:"text"` + } + _ = json.Unmarshal(mustJSON(request.Params), &p) + sent <- p.Text + } + _ = json.NewEncoder(c).Encode(herdr.Response{ID: request.ID, Result: json.RawMessage(result)}) + }() + } + }() + resolved := make(chan map[string]string, 1) + api := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && r.URL.Path == "/v1/federation/commands": + _ = json.NewEncoder(rw).Encode([]federation.Command{{ID: "c1", TaskID: "task", Kind: "grant_approval", PaneID: "pane", CaptureRevision: 7}}) + case r.Method == http.MethodPost && r.URL.Path == "/v1/federation/workers/h/captures": + _ = json.NewEncoder(rw).Encode(federation.Capture{TaskID: "task", PaneID: "pane", Revision: 7}) + case r.Method == http.MethodPost && r.URL.Path == "/v1/federation/commands/c1": + var body map[string]string + _ = json.NewDecoder(r.Body).Decode(&body) + resolved <- body + rw.WriteHeader(http.StatusNoContent) + default: + t.Errorf("unexpected %s %s", r.Method, r.URL.Path) + rw.WriteHeader(http.StatusNotFound) + } + })) + defer api.Close() + w := &worker{api: federation.Client{BaseURL: api.URL, WorkerID: "h", Token: "t"}, herdr: herdr.New(ln.Addr().String()), harness: "opencode", sessions: map[string]herdr.Session{"task": {PaneID: "pane"}}} + w.runCommands(context.Background()) + select { + case got := <-sent: + if got != "y\n" { + t.Fatalf("approval input=%q", got) + } + case <-time.After(time.Second): + t.Fatal("worker did not send approval") + } + select { + case got := <-resolved: + if got["status"] != "acknowledged" { + t.Fatalf("resolution=%v", got) + } + case <-time.After(time.Second): + t.Fatal("worker did not resolve command") + } +} + +func TestApprovalResponseOpenCodeAllowOnce(t *testing.T) { + text := "Permission required\nAllow once Allow always Reject\n⇆ select enter confirm" + if got, ok := approvalResponse(text, "grant_approval"); !ok || got != "\n" { + t.Fatalf("grant response = %q, %v", got, ok) + } + if got, ok := approvalResponse(text, "deny_approval"); ok || got != "" { + t.Fatalf("deny response = %q, %v; reject must not guess selector navigation", got, ok) + } +} + +func mustJSON(v any) []byte { b, _ := json.Marshal(v); return b } diff --git a/deploy/docker-api-entrypoint.sh b/deploy/docker-api-entrypoint.sh new file mode 100755 index 0000000..2454db7 --- /dev/null +++ b/deploy/docker-api-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -eu +# Keep the existing coordinator's secrets/config in the host-owned env file; +# Docker never needs to read or copy it. The process runs as the same numeric +# service user and can therefore read the bind-mounted file. +if [ -r /etc/orchestra/orchestra.env ]; then + set -a + . /etc/orchestra/orchestra.env + set +a +fi +export ORCHESTRA_DATA=/data +export ORCHESTRA_PORT=9145 +exec /app/orchestra diff --git a/internal/federation/client.go b/internal/federation/client.go index 912476b..ab52553 100644 --- a/internal/federation/client.go +++ b/internal/federation/client.go @@ -181,3 +181,31 @@ func (c Client) Complete(ctx context.Context, taskID, reportRef string) error { } return err } + +func (c Client) PublishCapture(ctx context.Context, capture Capture) (Capture, error) { + resp, err := c.request(ctx, http.MethodPost, "/v1/federation/workers/"+url.PathEscape(c.WorkerID)+"/captures", capture) + if err != nil { + return Capture{}, err + } + defer resp.Body.Close() + var out Capture + err = json.NewDecoder(resp.Body).Decode(&out) + return out, err +} +func (c Client) Commands(ctx context.Context) ([]Command, error) { + resp, err := c.request(ctx, http.MethodGet, "/v1/federation/commands", nil) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var out []Command + err = json.NewDecoder(resp.Body).Decode(&out) + return out, err +} +func (c Client) ResolveCommand(ctx context.Context, id, status, message string) error { + resp, err := c.request(ctx, http.MethodPost, "/v1/federation/commands/"+url.PathEscape(id), map[string]string{"status": status, "message": message}) + if resp != nil { + resp.Body.Close() + } + return err +} diff --git a/internal/federation/federation.go b/internal/federation/federation.go index dd6e6ca..eaa35ba 100644 --- a/internal/federation/federation.go +++ b/internal/federation/federation.go @@ -1,7 +1,9 @@ package federation import ( + "crypto/sha256" "errors" + "fmt" "sync" "time" ) @@ -18,6 +20,26 @@ type Worker struct { Token string `json:"-"` } +// Capture is published by a worker that owns the pane. The coordinator never +// reads a remote herdr socket; this is the worker-pulled counterpart. +type Capture struct { + TaskID string `json:"task_id"` + PaneID string `json:"pane_id"` + Text string `json:"text"` + Revision uint64 `json:"revision"` + At time.Time `json:"at"` +} +type Command struct { + ID string `json:"id"` + TaskID string `json:"task_id"` + Kind string `json:"kind"` + PaneID string `json:"pane_id"` + CaptureRevision uint64 `json:"capture_revision"` + CreatedAt time.Time `json:"created_at"` + Status string `json:"status"` + Error string `json:"error,omitempty"` +} + type Registry struct { mu sync.Mutex // AdmitToken, if set, is a pre-shared secret every registration must @@ -29,6 +51,8 @@ type Registry struct { TTL time.Duration OnOffline func(Worker) cursors map[string]uint64 + captures map[string]Capture // worker/task + commands map[string][]Command } func (r *Registry) init() { @@ -41,6 +65,101 @@ func (r *Registry) init() { if r.cursors == nil { r.cursors = map[string]uint64{} } + if r.captures == nil { + r.captures = map[string]Capture{} + } + if r.commands == nil { + r.commands = map[string][]Command{} + } +} + +func captureKey(worker, task string) string { return worker + "\x00" + task } +func (r *Registry) PutCapture(worker string, c Capture) (Capture, error) { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + if _, ok := r.workers[worker]; !ok { + return Capture{}, ErrUnknownWorker + } + if c.TaskID == "" || c.PaneID == "" { + return Capture{}, errors.New("task_id and pane_id required") + } + k := captureKey(worker, c.TaskID) + old := r.captures[k] + if old.Text != c.Text || old.PaneID != c.PaneID { + c.Revision = old.Revision + 1 + } + if c.Revision == 0 { + c.Revision = 1 + } + c.At = time.Now().UTC() + r.captures[k] = c + return c, nil +} +func (r *Registry) Capture(worker, task string) (Capture, bool) { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + c, ok := r.captures[captureKey(worker, task)] + return c, ok +} +func (r *Registry) Queue(worker string, c Command) (Command, error) { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + if _, ok := r.workers[worker]; !ok { + return Command{}, ErrUnknownWorker + } + if c.TaskID == "" || c.PaneID == "" || c.CaptureRevision == 0 || (c.Kind != "grant_approval" && c.Kind != "deny_approval") { + return Command{}, errors.New("invalid control command") + } + c.ID = fmt.Sprintf("cmd-%x", sha256.Sum256([]byte(fmt.Sprintf("%s/%s/%s/%d/%d", worker, c.TaskID, c.Kind, c.CaptureRevision, time.Now().UnixNano()))))[:20] + c.CreatedAt = time.Now().UTC() + c.Status = "pending" + r.commands[worker] = append(r.commands[worker], c) + return c, nil +} +func (r *Registry) Commands(worker string) ([]Command, error) { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + if _, ok := r.workers[worker]; !ok { + return nil, ErrUnknownWorker + } + var out []Command + for _, c := range r.commands[worker] { + if c.Status == "pending" { + out = append(out, c) + } + } + return out, nil +} +func (r *Registry) Command(worker, id string) (Command, bool) { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + for _, c := range r.commands[worker] { + if c.ID == id { + return c, true + } + } + return Command{}, false +} +func (r *Registry) CompleteCommand(worker, id, status, message string) error { + r.mu.Lock() + defer r.mu.Unlock() + r.init() + for i := range r.commands[worker] { + if r.commands[worker][i].ID == id { + if r.commands[worker][i].Status != "pending" { + return errors.New("command already resolved") + } + r.commands[worker][i].Status = status + r.commands[worker][i].Error = message + return nil + } + } + return errors.New("command not found") } // Register admits a worker. admitToken must match r.AdmitToken whenever one diff --git a/internal/federation/federation_test.go b/internal/federation/federation_test.go index 043cf88..ddf5471 100644 --- a/internal/federation/federation_test.go +++ b/internal/federation/federation_test.go @@ -75,3 +75,33 @@ func TestOfflineHookRunsOnceOnTransition(t *testing.T) { case <-time.After(10 * time.Millisecond): } } + +func TestCaptureRevisionAndCommandQueue(t *testing.T) { + r := &Registry{} + if err := r.Register(Worker{ID: "w", Token: "t"}, ""); err != nil { + t.Fatal(err) + } + c, err := r.PutCapture("w", Capture{TaskID: "task", PaneID: "pane", Text: "Permission required\n$ ls"}) + if err != nil || c.Revision != 1 { + t.Fatalf("capture=%#v err=%v", c, err) + } + again, err := r.PutCapture("w", Capture{TaskID: "task", PaneID: "pane", Text: c.Text}) + if err != nil || again.Revision != 1 { + t.Fatalf("same capture=%#v err=%v", again, err) + } + cmd, err := r.Queue("w", Command{TaskID: "task", Kind: "grant_approval", PaneID: "pane", CaptureRevision: 1}) + if err != nil { + t.Fatal(err) + } + commands, err := r.Commands("w") + if err != nil || len(commands) != 1 || commands[0].ID != cmd.ID { + t.Fatalf("commands=%#v err=%v", commands, err) + } + if err := r.CompleteCommand("w", cmd.ID, "acknowledged", ""); err != nil { + t.Fatal(err) + } + commands, _ = r.Commands("w") + if len(commands) != 0 { + t.Fatalf("pending=%#v", commands) + } +} diff --git a/internal/herdr/adapter.go b/internal/herdr/adapter.go index eb78ce8..de3366b 100644 --- a/internal/herdr/adapter.go +++ b/internal/herdr/adapter.go @@ -66,6 +66,13 @@ type AgentBlocker interface { type PaneCapture interface { PaneCapture(context.Context, Session, string) (string, error) } + +// ApprovalResponder executes an explicitly displayed permission decision. +// Implementations must re-read the pane before sending input so callers can +// bind a decision to the exact capture they rendered. +type ApprovalResponder interface { + RespondApproval(context.Context, Session, bool, string) error +} type CLIAdapter struct { Client *Client Harness string @@ -595,6 +602,28 @@ func (a CLIAdapter) PaneCapture(ctx context.Context, s Session, source string) ( return r.Read.Text, nil } +// RespondApproval only acts on harness prompts that visibly expose a y/n +// choice. This deliberately refuses unknown dialog layouts rather than +// guessing an Enter key could mean approval. +func (a CLIAdapter) RespondApproval(ctx context.Context, s Session, grant bool, expectedCapture string) error { + current, err := a.PaneCapture(ctx, s, "recent") + if err != nil { + return err + } + if current != expectedCapture { + return fmt.Errorf("approval prompt changed") + } + low := strings.ToLower(current) + if !strings.Contains(low, "[y/n]") && !strings.Contains(low, "(y/n)") { + return fmt.Errorf("approval prompt has no unambiguous y/n confirmation") + } + input := "n\n" + if grant { + input = "y\n" + } + return a.Client.Call(ctx, "pane.send_text", map[string]any{"pane_id": s.PaneID, "text": input}, nil) +} + func statusFromAgentResult(v any) string { if m, ok := v.(map[string]any); ok { for _, key := range []string{"status", "agent_status", "state"} { diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go index e490950..52d106b 100644 --- a/internal/orchestrator/orchestrator.go +++ b/internal/orchestrator/orchestrator.go @@ -995,6 +995,61 @@ func (c *Coordinator) Session(taskID string) (herdr.Session, bool) { return s, ok } +// RequestHandoff asks the live harness to prepare its agent-authored handoff. +// It deliberately does not release the pane: a later validated handoff is the +// only evidence that can make a rotation safe. +func (c *Coordinator) RequestHandoff(ctx context.Context, taskID string) error { + c.loadSessions() + c.mu.Lock() + s, ok := c.sessions[taskID] + c.mu.Unlock() + if !ok { + return fmt.Errorf("session not found for task %s", taskID) + } + if s.HandoffRequested { + return nil + } + a, err := c.adapterFor(taskID, s) + if err != nil { + return err + } + req, ok := a.(herdr.HandoffRequester) + if !ok { + return fmt.Errorf("harness does not support handoff requests") + } + if err := req.RequestHandoff(ctx, s); err != nil { + return err + } + s.HandoffRequested = true + c.mu.Lock() + c.sessions[taskID] = s + err = c.saveSessionsLocked() + c.mu.Unlock() + return err +} + +// RespondApproval is the local implementation of the same guarded command +// contract used by federation workers. It rechecks the displayed capture at +// the owning herdr immediately before input is sent. +func (c *Coordinator) RespondApproval(ctx context.Context, taskID string, grant bool, expectedCapture string) error { + c.loadSessions() + c.mu.Lock() + s, ok := c.sessions[taskID] + c.mu.Unlock() + if !ok { + return fmt.Errorf("session not found for task %s", taskID) + } + a, err := c.adapterFor(taskID, s) + if err != nil { + return err + } + responder, ok := a.(herdr.ApprovalResponder) + if !ok { + return fmt.Errorf("harness does not support approval responses") + } + return responder.RespondApproval(ctx, s, grant, expectedCapture) +} + func (c *Coordinator) Capture(ctx context.Context, taskID, source string) (string, error) { s, ok := c.Session(taskID) if !ok { diff --git a/internal/ui/ui.go b/internal/ui/ui.go new file mode 100644 index 0000000..d8e3dbe --- /dev/null +++ b/internal/ui/ui.go @@ -0,0 +1,378 @@ +// Package ui provides the browser-oriented Orchestra read model and controls. +package ui + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "orchestra/internal/authz" + "orchestra/internal/domain" + "orchestra/internal/federation" + "orchestra/internal/orchestrator" + "orchestra/internal/store" + "sort" + "strings" + "time" +) + +type PendingApproval struct { + Kind string `json:"kind"` + Summary string `json:"summary"` + Command string `json:"command,omitempty"` + Diff string `json:"diff,omitempty"` + PaneID string `json:"pane_id"` + CaptureRevision uint64 `json:"capture_revision"` + DetectedAt time.Time `json:"detected_at"` +} +type Capture struct { + TaskID string `json:"task_id"` + Source string `json:"source"` + Text string `json:"text"` + Revision uint64 `json:"revision"` + At time.Time `json:"at"` + Truncated bool `json:"truncated"` +} +type Action struct { + ID string `json:"id"` + Enabled bool `json:"enabled"` + Reason string `json:"reason,omitempty"` + Needs []string `json:"needs,omitempty"` +} +type Session struct { + PaneID string `json:"pane_id,omitempty"` + HarnessID string `json:"harness_id,omitempty"` + AgentStatus string `json:"agent_status,omitempty"` + Blocker string `json:"blocker,omitempty"` + LeaseUntil *time.Time `json:"lease_until,omitempty"` + Capture *Capture `json:"capture,omitempty"` + Approval *PendingApproval `json:"pending_approval,omitempty"` +} +type TaskDetail struct { + Task domain.Task `json:"task"` + Events []domain.Event `json:"events"` + Session *Session `json:"session,omitempty"` + HandoffRef string `json:"handoff_ref,omitempty"` + ReportRef string `json:"report_ref,omitempty"` + Actions []Action `json:"actions"` +} +type Overview struct { + Tasks []domain.Task `json:"tasks"` + Workers []federation.Worker `json:"workers"` + Sessions []Session `json:"sessions"` + UpdatedAt time.Time `json:"updated_at"` +} + +type Server struct { + Store *store.Store + Workers *federation.Registry + Coordinator *orchestrator.Coordinator + Route func(domain.Event) error +} + +func (s Server) capture(ctx context.Context, t domain.Task) (*Capture, error) { + if t.State != domain.StateLeased || t.Lease == nil { + return nil, nil + } + if s.Coordinator != nil { + text, err := s.Coordinator.Capture(ctx, t.ID, "recent") + if err == nil { + return &Capture{TaskID: t.ID, Source: "recent", Text: text, Revision: uint64(time.Now().UnixNano()), At: time.Now().UTC()}, nil + } + } + if s.Workers != nil { + if c, ok := s.Workers.Capture(t.Lease.HarnessID, t.ID); ok { + return &Capture{TaskID: t.ID, Source: "recent", Text: c.Text, Revision: c.Revision, At: c.At}, nil + } + } + return nil, fmt.Errorf("capture unavailable from owning worker") +} +func ParsePendingApproval(text, pane string, revision uint64, at time.Time) *PendingApproval { + lines := strings.Split(text, "\n") + low := strings.ToLower(text) + if !strings.Contains(low, "permission required") && !strings.Contains(low, "approval required") && !strings.Contains(low, "allow this") { + return nil + } + p := &PendingApproval{Kind: "unknown", Summary: "Harness permission prompt needs review", PaneID: pane, CaptureRevision: revision, DetectedAt: at} + // OpenCode renders this exact three-choice selector and documents that + // Enter confirms the initially selected "Allow once" action. It is not a + // y/n prompt, so model it separately: grant is safe and bounded to once; + // reject remains unavailable because the selected position is not exposed + // in pane capture and Orchestra must not guess navigation keystrokes. + if strings.Contains(low, "allow once") && strings.Contains(low, "allow always") && strings.Contains(low, "reject") && strings.Contains(low, "enter confirm") { + p.Kind = "opencode_once" + p.Summary = "Allow this command once" + } + for _, l := range lines { + l = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(l), "┃")) + if strings.HasPrefix(l, "$ ") { + if p.Kind == "unknown" { + p.Kind = "shell" + } + p.Command = strings.TrimSpace(strings.TrimPrefix(l, "$ ")) + if p.Kind == "shell" { + p.Summary = "Run shell command" + } + return p + } + } + // Edits are intentionally only actionable when the harness gave an explicit diff. + if i := strings.Index(text, "diff --git "); i >= 0 { + p.Kind = "edit" + p.Diff = text[i:] + p.Summary = "Apply proposed edit" + } + return p +} +func (s Server) detail(ctx context.Context, id string) (TaskDetail, error) { + t, ok := s.Store.Task(id) + if !ok { + return TaskDetail{}, domain.ErrNotFound + } + d := TaskDetail{Task: t, Actions: actions(t)} + for _, e := range s.Store.Events(0) { + if e.TaskID != id { + continue + } + d.Events = append(d.Events, e) + var p struct { + HandoffRef string `json:"handoff_ref"` + ReportRef string `json:"report_ref"` + } + _ = json.Unmarshal(e.Payload, &p) + if p.HandoffRef != "" { + d.HandoffRef = p.HandoffRef + } + if p.ReportRef != "" { + d.ReportRef = p.ReportRef + } + } + sort.Slice(d.Events, func(i, j int) bool { return d.Events[i].Seq < d.Events[j].Seq }) + if t.Lease != nil { + session := &Session{HarnessID: t.Lease.HarnessID, LeaseUntil: &t.Lease.Until} + if c, err := s.capture(ctx, t); err == nil && c != nil { + session.Capture = c + session.PaneID = capturePane(s, t.ID, c) + session.Approval = ParsePendingApproval(c.Text, session.PaneID, c.Revision, c.At) + } else if err != nil { + session.Blocker = "capture unavailable: " + err.Error() + } + d.Session = session + } + return d, nil +} +func capturePane(s Server, taskID string, c *Capture) string { + if s.Coordinator != nil { + if session, ok := s.Coordinator.Session(taskID); ok { + return session.PaneID + } + } + if s.Workers != nil { + for _, w := range s.Workers.Snapshot() { + if remote, ok := s.Workers.Capture(w.ID, taskID); ok && remote.Revision == c.Revision { + return remote.PaneID + } + } + } + return "" +} +func actions(t domain.Task) []Action { + active := t.State == domain.StateLeased + return []Action{{ID: "handoff", Enabled: active, Reason: "requires a live leased session"}, {ID: "release", Enabled: active, Needs: []string{"reason or handoff_ref"}}, {ID: "block", Enabled: active, Needs: []string{"blocker"}}, {ID: "complete", Enabled: active, Needs: []string{"report_ref", "receipt"}}} +} +func (s Server) Overview(ctx context.Context) Overview { + out := Overview{Tasks: s.Store.Tasks(), UpdatedAt: time.Now().UTC()} + if s.Workers != nil { + out.Workers = s.Workers.Snapshot() + } + for _, t := range out.Tasks { + if t.Lease != nil { + d, _ := s.detail(ctx, t.ID) + if d.Session != nil { + out.Sessions = append(out.Sessions, *d.Session) + } + } + } + return out +} +func writeJSON(w http.ResponseWriter, v any) { + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(v) +} +func (s Server) Handler() http.Handler { + m := http.NewServeMux() + m.HandleFunc("/v1/ui/overview", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "method not allowed", 405) + return + } + writeJSON(w, s.Overview(r.Context())) + }) + m.HandleFunc("/v1/ui/tasks", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", 405) + return + } + var p map[string]any + if json.NewDecoder(r.Body).Decode(&p) != nil { + http.Error(w, "invalid json", 400) + return + } + b, _ := json.Marshal(p) + e := domain.Event{ID: domain.NewID(), Type: "TaskCreated", TaskID: domain.NewID(), Version: 1, Payload: b, Surface: string(authz.Web)} + if err := s.Store.Append(e); err != nil { + http.Error(w, err.Error(), 400) + return + } + if s.Route != nil { + _ = s.Route(e) + } + writeJSON(w, e) + }) + m.HandleFunc("/v1/ui/tasks/", func(w http.ResponseWriter, r *http.Request) { + rest := strings.TrimPrefix(r.URL.Path, "/v1/ui/tasks/") + p := strings.Split(rest, "/") + if len(p) == 1 && r.Method == http.MethodGet { + d, err := s.detail(r.Context(), p[0]) + if err != nil { + http.Error(w, "task not found", 404) + return + } + writeJSON(w, d) + return + } + if len(p) == 2 && p[1] == "capture" && r.Method == http.MethodGet { + t, ok := s.Store.Task(p[0]) + if !ok { + http.Error(w, "task not found", 404) + return + } + c, err := s.capture(r.Context(), t) + if err != nil || c == nil { + http.Error(w, fmt.Sprintf("capture unavailable: %v", err), 503) + return + } + writeJSON(w, c) + return + } + if len(p) == 3 && p[1] == "actions" && r.Method == http.MethodPost { + s.action(w, r, p[0], p[2]) + return + } + http.Error(w, "not found", 404) + }) + m.HandleFunc("/v1/ui/artifacts/", func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "method not allowed", 405) + return + } + ref := strings.TrimPrefix(r.URL.Path, "/v1/ui/artifacts/") + if len(ref) != 64 { + http.Error(w, "artifact ref required", 400) + return + } + b, err := s.Store.Artifact(ref) + if err != nil { + http.Error(w, "artifact not found", 404) + return + } + trim := strings.TrimSpace(string(b)) + if json.Valid(b) { + w.Header().Set("Content-Type", "application/json") + } else if strings.HasPrefix(trim, "#") || strings.Contains(trim, "\n#") { + w.Header().Set("Content-Type", "text/markdown; charset=utf-8") + } else { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + } + _, _ = w.Write(b) + }) + return m +} +func (s Server) action(w http.ResponseWriter, r *http.Request, id, action string) { + t, ok := s.Store.Task(id) + if !ok { + http.Error(w, "task not found", 404) + return + } + var body map[string]any + _ = json.NewDecoder(r.Body).Decode(&body) + var typ string + switch action { + case "grant_approval", "deny_approval": + c, err := s.capture(r.Context(), t) + if err != nil || c == nil { + http.Error(w, "current capture unavailable", 503) + return + } + pane := capturePane(s, id, c) + approval := ParsePendingApproval(c.Text, pane, c.Revision, c.At) + if approval == nil || approval.Kind == "unknown" || pane == "" { + http.Error(w, "current prompt is not safely actionable", 409) + return + } + if s.Workers != nil { + if _, remote := s.Workers.Capture(t.Lease.HarnessID, t.ID); remote { + command, err := s.Workers.Queue(t.Lease.HarnessID, federation.Command{TaskID: id, Kind: action, PaneID: pane, CaptureRevision: c.Revision}) + if err != nil { + http.Error(w, err.Error(), 409) + return + } + writeJSON(w, map[string]any{"command": command, "task": t}) + return + } + } + if s.Coordinator == nil { + http.Error(w, "approval executor unavailable", 503) + return + } + if err := s.Coordinator.RespondApproval(r.Context(), id, action == "grant_approval", c.Text); err != nil { + http.Error(w, err.Error(), 409) + return + } + typ := "ApprovalGranted" + if action == "deny_approval" { + typ = "ApprovalDenied" + } + payload, _ := json.Marshal(map[string]any{"subject_ref": id, "pane_id": pane, "capture_revision": c.Revision}) + e := domain.Event{ID: domain.NewID(), Type: typ, TaskID: id, Version: t.Version + 1, Payload: payload, Surface: string(authz.Web)} + if err := s.Store.Append(e); err != nil { + http.Error(w, err.Error(), 409) + return + } + d, _ := s.detail(r.Context(), id) + writeJSON(w, d) + return + case "handoff": + if s.Coordinator == nil { + http.Error(w, "live coordinator unavailable", 503) + return + } + if err := s.Coordinator.RequestHandoff(r.Context(), id); err != nil { + http.Error(w, err.Error(), 409) + return + } + d, _ := s.detail(r.Context(), id) + writeJSON(w, d) + return + case "release": + typ = "TaskReleased" + case "block": + typ = "TaskBlocked" + case "complete": + typ = "TaskCompleted" + default: + http.Error(w, "unknown action", 404) + return + } + b, _ := json.Marshal(body) + e := domain.Event{ID: domain.NewID(), Type: typ, TaskID: id, Version: t.Version + 1, Payload: b, Surface: string(authz.Web)} + if err := s.Store.Append(e); err != nil { + http.Error(w, err.Error(), 409) + return + } + if s.Route != nil { + _ = s.Route(e) + } + d, _ := s.detail(r.Context(), id) + writeJSON(w, d) +} diff --git a/internal/ui/ui_test.go b/internal/ui/ui_test.go new file mode 100644 index 0000000..62fcaca --- /dev/null +++ b/internal/ui/ui_test.go @@ -0,0 +1,45 @@ +package ui + +import ( + "bytes" + "net/http" + "net/http/httptest" + "orchestra/internal/store" + "strings" + "testing" + "time" +) + +func TestOverviewAndTaskDetailHTTP(t *testing.T) { + s, err := store.Open(t.TempDir()) + if err != nil { + t.Fatal(err) + } + h := Server{Store: s}.Handler() + req := httptest.NewRequest(http.MethodPost, "/v1/ui/tasks", bytes.NewBufferString(`{"source":"web","external_id":"one","project":"demo","title":"UI task"}`)) + r := httptest.NewRecorder() + h.ServeHTTP(r, req) + if r.Code != http.StatusOK { + t.Fatalf("create status=%d body=%s", r.Code, r.Body.String()) + } + req = httptest.NewRequest(http.MethodGet, "/v1/ui/overview", nil) + r = httptest.NewRecorder() + h.ServeHTTP(r, req) + if r.Code != http.StatusOK || !strings.Contains(r.Body.String(), "UI task") { + t.Fatalf("overview status=%d body=%s", r.Code, r.Body.String()) + } +} + +func TestParsePendingApprovalPreservesExactShellCommand(t *testing.T) { + text := "Permission required\n$ git status --short && go test ./..." + p := ParsePendingApproval(text, "p1", 7, time.Unix(1, 0)) + if p == nil || p.Kind != "shell" || p.Command != "git status --short && go test ./..." { + t.Fatalf("got %#v", p) + } +} +func TestParsePendingApprovalDoesNotInventActionablePrompt(t *testing.T) { + p := ParsePendingApproval("Approval required: choose an option", "p1", 7, time.Now()) + if p == nil || p.Kind != "unknown" || !strings.Contains(p.Summary, "review") { + t.Fatalf("got %#v", p) + } +} diff --git a/internal/webui/assets/assets/index-B082_NkA.js b/internal/webui/assets/assets/index-B082_NkA.js new file mode 100644 index 0000000..db1a4b1 --- /dev/null +++ b/internal/webui/assets/assets/index-B082_NkA.js @@ -0,0 +1,60 @@ +var Zm=i=>{throw TypeError(i)};var tr=(i,c,f)=>c.has(i)||Zm("Cannot "+f);var v=(i,c,f)=>(tr(i,c,"read from private field"),f?f.call(i):c.get(i)),W=(i,c,f)=>c.has(i)?Zm("Cannot add the same private member more than once"):c instanceof WeakSet?c.add(i):c.set(i,f),K=(i,c,f,s)=>(tr(i,c,"write to private field"),s?s.call(i,f):c.set(i,f),f),st=(i,c,f)=>(tr(i,c,"access private method"),f);var Sc=(i,c,f,s)=>({set _(o){K(i,c,o,f)},get _(){return v(i,c,s)}});(function(){const c=document.createElement("link").relList;if(c&&c.supports&&c.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))s(o);new MutationObserver(o=>{for(const d of o)if(d.type==="childList")for(const p of d.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&s(p)}).observe(document,{childList:!0,subtree:!0});function f(o){const d={};return o.integrity&&(d.integrity=o.integrity),o.referrerPolicy&&(d.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?d.credentials="include":o.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function s(o){if(o.ep)return;o.ep=!0;const d=f(o);fetch(o.href,d)}})();function Np(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var er={exports:{}},Yu={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Km;function Hp(){if(Km)return Yu;Km=1;var i=Symbol.for("react.transitional.element"),c=Symbol.for("react.fragment");function f(s,o,d){var p=null;if(d!==void 0&&(p=""+d),o.key!==void 0&&(p=""+o.key),"key"in o){d={};for(var E in o)E!=="key"&&(d[E]=o[E])}else d=o;return o=d.ref,{$$typeof:i,type:s,key:p,ref:o!==void 0?o:null,props:d}}return Yu.Fragment=c,Yu.jsx=f,Yu.jsxs=f,Yu}var Vm;function jp(){return Vm||(Vm=1,er.exports=Hp()),er.exports}var w=jp(),lr={exports:{}},nt={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Jm;function qp(){if(Jm)return nt;Jm=1;var i=Symbol.for("react.transitional.element"),c=Symbol.for("react.portal"),f=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),p=Symbol.for("react.context"),E=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),m=Symbol.for("react.memo"),O=Symbol.for("react.lazy"),T=Symbol.for("react.activity"),N=Symbol.iterator;function B(b){return b===null||typeof b!="object"?null:(b=N&&b[N]||b["@@iterator"],typeof b=="function"?b:null)}var Y={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Q=Object.assign,L={};function q(b,j,V){this.props=b,this.context=j,this.refs=L,this.updater=V||Y}q.prototype.isReactComponent={},q.prototype.setState=function(b,j){if(typeof b!="object"&&typeof b!="function"&&b!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,b,j,"setState")},q.prototype.forceUpdate=function(b){this.updater.enqueueForceUpdate(this,b,"forceUpdate")};function F(){}F.prototype=q.prototype;function X(b,j,V){this.props=b,this.context=j,this.refs=L,this.updater=V||Y}var G=X.prototype=new F;G.constructor=X,Q(G,q.prototype),G.isPureReactComponent=!0;var et=Array.isArray;function ft(){}var I={H:null,A:null,T:null,S:null},lt=Object.prototype.hasOwnProperty;function yt(b,j,V){var $=V.ref;return{$$typeof:i,type:b,key:j,ref:$!==void 0?$:null,props:V}}function Ut(b,j){return yt(b.type,j,b.props)}function Yt(b){return typeof b=="object"&&b!==null&&b.$$typeof===i}function Gt(b){var j={"=":"=0",":":"=2"};return"$"+b.replace(/[=:]/g,function(V){return j[V]})}var Ee=/\/+/g;function te(b,j){return typeof b=="object"&&b!==null&&b.key!=null?Gt(""+b.key):j.toString(36)}function xt(b){switch(b.status){case"fulfilled":return b.value;case"rejected":throw b.reason;default:switch(typeof b.status=="string"?b.then(ft,ft):(b.status="pending",b.then(function(j){b.status==="pending"&&(b.status="fulfilled",b.value=j)},function(j){b.status==="pending"&&(b.status="rejected",b.reason=j)})),b.status){case"fulfilled":return b.value;case"rejected":throw b.reason}}throw b}function U(b,j,V,$,ut){var rt=typeof b;(rt==="undefined"||rt==="boolean")&&(b=null);var Et=!1;if(b===null)Et=!0;else switch(rt){case"bigint":case"string":case"number":Et=!0;break;case"object":switch(b.$$typeof){case i:case c:Et=!0;break;case O:return Et=b._init,U(Et(b._payload),j,V,$,ut)}}if(Et)return ut=ut(b),Et=$===""?"."+te(b,0):$,et(ut)?(V="",Et!=null&&(V=Et.replace(Ee,"$&/")+"/"),U(ut,j,V,"",function(Jn){return Jn})):ut!=null&&(Yt(ut)&&(ut=Ut(ut,V+(ut.key==null||b&&b.key===ut.key?"":(""+ut.key).replace(Ee,"$&/")+"/")+Et)),j.push(ut)),1;Et=0;var se=$===""?".":$+":";if(et(b))for(var wt=0;wt>>1,_t=U[At];if(0>>1;Ato(V,at))$<_t&&0>o(ut,V)?(U[At]=ut,U[$]=at,At=$):(U[At]=V,U[j]=at,At=j);else if($<_t&&0>o(ut,at))U[At]=ut,U[$]=at,At=$;else break t}}return Z}function o(U,Z){var at=U.sortIndex-Z.sortIndex;return at!==0?at:U.id-Z.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;i.unstable_now=function(){return d.now()}}else{var p=Date,E=p.now();i.unstable_now=function(){return p.now()-E}}var g=[],m=[],O=1,T=null,N=3,B=!1,Y=!1,Q=!1,L=!1,q=typeof setTimeout=="function"?setTimeout:null,F=typeof clearTimeout=="function"?clearTimeout:null,X=typeof setImmediate<"u"?setImmediate:null;function G(U){for(var Z=f(m);Z!==null;){if(Z.callback===null)s(m);else if(Z.startTime<=U)s(m),Z.sortIndex=Z.expirationTime,c(g,Z);else break;Z=f(m)}}function et(U){if(Q=!1,G(U),!Y)if(f(g)!==null)Y=!0,ft||(ft=!0,Gt());else{var Z=f(m);Z!==null&&xt(et,Z.startTime-U)}}var ft=!1,I=-1,lt=5,yt=-1;function Ut(){return L?!0:!(i.unstable_now()-ytU&&Ut());){var At=T.callback;if(typeof At=="function"){T.callback=null,N=T.priorityLevel;var _t=At(T.expirationTime<=U);if(U=i.unstable_now(),typeof _t=="function"){T.callback=_t,G(U),Z=!0;break e}T===f(g)&&s(g),G(U)}else s(g);T=f(g)}if(T!==null)Z=!0;else{var b=f(m);b!==null&&xt(et,b.startTime-U),Z=!1}}break t}finally{T=null,N=at,B=!1}Z=void 0}}finally{Z?Gt():ft=!1}}}var Gt;if(typeof X=="function")Gt=function(){X(Yt)};else if(typeof MessageChannel<"u"){var Ee=new MessageChannel,te=Ee.port2;Ee.port1.onmessage=Yt,Gt=function(){te.postMessage(null)}}else Gt=function(){q(Yt,0)};function xt(U,Z){I=q(function(){U(i.unstable_now())},Z)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(U){U.callback=null},i.unstable_forceFrameRate=function(U){0>U||125At?(U.sortIndex=at,c(m,U),f(g)===null&&U===f(m)&&(Q?(F(I),I=-1):Q=!0,xt(et,at-At))):(U.sortIndex=_t,c(g,U),Y||B||(Y=!0,ft||(ft=!0,Gt()))),U},i.unstable_shouldYield=Ut,i.unstable_wrapCallback=function(U){var Z=N;return function(){var at=N;N=Z;try{return U.apply(this,arguments)}finally{N=at}}}})(ur)),ur}var km;function Lp(){return km||(km=1,nr.exports=Qp()),nr.exports}var ir={exports:{}},ie={};/** + * @license React + * react-dom.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Wm;function Yp(){if(Wm)return ie;Wm=1;var i=Cr();function c(g){var m="https://react.dev/errors/"+g;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(c){console.error(c)}}return i(),ir.exports=Yp(),ir.exports}/** + * @license React + * react-dom-client.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Im;function wp(){if(Im)return Gu;Im=1;var i=Lp(),c=Cr(),f=Gp();function s(t){var e="https://react.dev/errors/"+t;if(1_t||(t.current=At[_t],At[_t]=null,_t--)}function V(t,e){_t++,At[_t]=t.current,t.current=e}var $=b(null),ut=b(null),rt=b(null),Et=b(null);function se(t,e){switch(V(rt,e),V(ut,t),V($,null),e.nodeType){case 9:case 11:t=(t=e.documentElement)&&(t=t.namespaceURI)?dm(t):0;break;default:if(t=e.tagName,e=e.namespaceURI)e=dm(e),t=mm(e,t);else switch(t){case"svg":t=1;break;case"math":t=2;break;default:t=0}}j($),V($,t)}function wt(){j($),j(ut),j(rt)}function Jn(t){t.memoizedState!==null&&V(Et,t);var e=$.current,l=mm(e,t.type);e!==l&&(V(ut,t),V($,l))}function ei(t){ut.current===t&&(j($),j(ut)),Et.current===t&&(j(Et),qu._currentValue=at)}var jc,wr;function ma(t){if(jc===void 0)try{throw Error()}catch(l){var e=l.stack.trim().match(/\n( *(at )?)/);jc=e&&e[1]||"",wr=-1)":-1n||y[a]!==z[n]){var D=` +`+y[a].replace(" at new "," at ");return t.displayName&&D.includes("")&&(D=D.replace("",t.displayName)),D}while(1<=a&&0<=n);break}}}finally{qc=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?ma(l):""}function rv(t,e){switch(t.tag){case 26:case 27:case 5:return ma(t.type);case 16:return ma("Lazy");case 13:return t.child!==e&&e!==null?ma("Suspense Fallback"):ma("Suspense");case 19:return ma("SuspenseList");case 0:case 15:return Bc(t.type,!1);case 11:return Bc(t.type.render,!1);case 1:return Bc(t.type,!0);case 31:return ma("Activity");default:return""}}function Xr(t){try{var e="",l=null;do e+=rv(t,l),l=t,t=t.return;while(t);return e}catch(a){return` +Error generating stack: `+a.message+` +`+a.stack}}var Qc=Object.prototype.hasOwnProperty,Lc=i.unstable_scheduleCallback,Yc=i.unstable_cancelCallback,ov=i.unstable_shouldYield,hv=i.unstable_requestPaint,Te=i.unstable_now,dv=i.unstable_getCurrentPriorityLevel,Zr=i.unstable_ImmediatePriority,Kr=i.unstable_UserBlockingPriority,li=i.unstable_NormalPriority,mv=i.unstable_LowPriority,Vr=i.unstable_IdlePriority,yv=i.log,vv=i.unstable_setDisableYieldValue,Fn=null,Re=null;function xl(t){if(typeof yv=="function"&&vv(t),Re&&typeof Re.setStrictMode=="function")try{Re.setStrictMode(Fn,t)}catch{}}var Oe=Math.clz32?Math.clz32:Sv,pv=Math.log,gv=Math.LN2;function Sv(t){return t>>>=0,t===0?32:31-(pv(t)/gv|0)|0}var ai=256,ni=262144,ui=4194304;function ya(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&261888;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function ii(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,r=t.pingedLanes;t=t.warmLanes;var h=a&134217727;return h!==0?(a=h&~u,a!==0?n=ya(a):(r&=h,r!==0?n=ya(r):l||(l=h&~t,l!==0&&(n=ya(l))))):(h=a&~u,h!==0?n=ya(h):r!==0?n=ya(r):l||(l=a&~t,l!==0&&(n=ya(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function $n(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function bv(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Jr(){var t=ui;return ui<<=1,(ui&62914560)===0&&(ui=4194304),t}function Gc(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function kn(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Ev(t,e,l,a,n,u){var r=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var h=t.entanglements,y=t.expirationTimes,z=t.hiddenUpdates;for(l=r&~l;0"u")return null;try{return t.activeElement||t.body}catch{return t.body}}var Cv=/[\n"\\]/g;function je(t){return t.replace(Cv,function(e){return"\\"+e.charCodeAt(0).toString(16)+" "})}function Jc(t,e,l,a,n,u,r,h){t.name="",r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"?t.type=r:t.removeAttribute("type"),e!=null?r==="number"?(e===0&&t.value===""||t.value!=e)&&(t.value=""+He(e)):t.value!==""+He(e)&&(t.value=""+He(e)):r!=="submit"&&r!=="reset"||t.removeAttribute("value"),e!=null?Fc(t,r,He(e)):l!=null?Fc(t,r,He(l)):a!=null&&t.removeAttribute("value"),n==null&&u!=null&&(t.defaultChecked=!!u),n!=null&&(t.checked=n&&typeof n!="function"&&typeof n!="symbol"),h!=null&&typeof h!="function"&&typeof h!="symbol"&&typeof h!="boolean"?t.name=""+He(h):t.removeAttribute("name")}function io(t,e,l,a,n,u,r,h){if(u!=null&&typeof u!="function"&&typeof u!="symbol"&&typeof u!="boolean"&&(t.type=u),e!=null||l!=null){if(!(u!=="submit"&&u!=="reset"||e!=null)){Vc(t);return}l=l!=null?""+He(l):"",e=e!=null?""+He(e):l,h||e===t.value||(t.value=e),t.defaultValue=e}a=a??n,a=typeof a!="function"&&typeof a!="symbol"&&!!a,t.checked=h?t.checked:!!a,t.defaultChecked=!!a,r!=null&&typeof r!="function"&&typeof r!="symbol"&&typeof r!="boolean"&&(t.name=r),Vc(t)}function Fc(t,e,l){e==="number"&&fi(t.ownerDocument)===t||t.defaultValue===""+l||(t.defaultValue=""+l)}function $a(t,e,l,a){if(t=t.options,e){e={};for(var n=0;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Ic=!1;if(sl)try{var tu={};Object.defineProperty(tu,"passive",{get:function(){Ic=!0}}),window.addEventListener("test",tu,tu),window.removeEventListener("test",tu,tu)}catch{Ic=!1}var Hl=null,ts=null,oi=null;function mo(){if(oi)return oi;var t,e=ts,l=e.length,a,n="value"in Hl?Hl.value:Hl.textContent,u=n.length;for(t=0;t=au),bo=" ",Eo=!1;function To(t,e){switch(t){case"keyup":return e0.indexOf(e.keyCode)!==-1;case"keydown":return e.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ro(t){return t=t.detail,typeof t=="object"&&"data"in t?t.data:null}var Ia=!1;function a0(t,e){switch(t){case"compositionend":return Ro(e);case"keypress":return e.which!==32?null:(Eo=!0,bo);case"textInput":return t=e.data,t===bo&&Eo?null:t;default:return null}}function n0(t,e){if(Ia)return t==="compositionend"||!us&&To(t,e)?(t=mo(),oi=ts=Hl=null,Ia=!1,t):null;switch(t){case"paste":return null;case"keypress":if(!(e.ctrlKey||e.altKey||e.metaKey)||e.ctrlKey&&e.altKey){if(e.char&&1=e)return{node:l,offset:e-t};t=a}t:{for(;l;){if(l.nextSibling){l=l.nextSibling;break t}l=l.parentNode}l=void 0}l=Uo(l)}}function No(t,e){return t&&e?t===e?!0:t&&t.nodeType===3?!1:e&&e.nodeType===3?No(t,e.parentNode):"contains"in t?t.contains(e):t.compareDocumentPosition?!!(t.compareDocumentPosition(e)&16):!1:!1}function Ho(t){t=t!=null&&t.ownerDocument!=null&&t.ownerDocument.defaultView!=null?t.ownerDocument.defaultView:window;for(var e=fi(t.document);e instanceof t.HTMLIFrameElement;){try{var l=typeof e.contentWindow.location.href=="string"}catch{l=!1}if(l)t=e.contentWindow;else break;e=fi(t.document)}return e}function ss(t){var e=t&&t.nodeName&&t.nodeName.toLowerCase();return e&&(e==="input"&&(t.type==="text"||t.type==="search"||t.type==="tel"||t.type==="url"||t.type==="password")||e==="textarea"||t.contentEditable==="true")}var h0=sl&&"documentMode"in document&&11>=document.documentMode,tn=null,fs=null,cu=null,rs=!1;function jo(t,e,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;rs||tn==null||tn!==fi(a)||(a=tn,"selectionStart"in a&&ss(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),cu&&iu(cu,a)||(cu=a,a=nc(fs,"onSelect"),0>=r,n-=r,We=1<<32-Oe(e)+n|l<ct?(mt=k,k=null):mt=k.sibling;var St=C(R,k,A[ct],x);if(St===null){k===null&&(k=mt);break}t&&k&&St.alternate===null&&e(R,k),S=u(St,S,ct),gt===null?P=St:gt.sibling=St,gt=St,k=mt}if(ct===A.length)return l(R,k),vt&&rl(R,ct),P;if(k===null){for(;ctct?(mt=k,k=null):mt=k.sibling;var la=C(R,k,St.value,x);if(la===null){k===null&&(k=mt);break}t&&k&&la.alternate===null&&e(R,k),S=u(la,S,ct),gt===null?P=la:gt.sibling=la,gt=la,k=mt}if(St.done)return l(R,k),vt&&rl(R,ct),P;if(k===null){for(;!St.done;ct++,St=A.next())St=H(R,St.value,x),St!==null&&(S=u(St,S,ct),gt===null?P=St:gt.sibling=St,gt=St);return vt&&rl(R,ct),P}for(k=a(k);!St.done;ct++,St=A.next())St=_(k,R,ct,St.value,x),St!==null&&(t&&St.alternate!==null&&k.delete(St.key===null?ct:St.key),S=u(St,S,ct),gt===null?P=St:gt.sibling=St,gt=St);return t&&k.forEach(function(xp){return e(R,xp)}),vt&&rl(R,ct),P}function Mt(R,S,A,x){if(typeof A=="object"&&A!==null&&A.type===Q&&A.key===null&&(A=A.props.children),typeof A=="object"&&A!==null){switch(A.$$typeof){case B:t:{for(var P=A.key;S!==null;){if(S.key===P){if(P=A.type,P===Q){if(S.tag===7){l(R,S.sibling),x=n(S,A.props.children),x.return=R,R=x;break t}}else if(S.elementType===P||typeof P=="object"&&P!==null&&P.$$typeof===lt&&za(P)===S.type){l(R,S.sibling),x=n(S,A.props),du(x,A),x.return=R,R=x;break t}l(R,S);break}else e(R,S);S=S.sibling}A.type===Q?(x=Ea(A.props.children,R.mode,x,A.key),x.return=R,R=x):(x=Ei(A.type,A.key,A.props,null,R.mode,x),du(x,A),x.return=R,R=x)}return r(R);case Y:t:{for(P=A.key;S!==null;){if(S.key===P)if(S.tag===4&&S.stateNode.containerInfo===A.containerInfo&&S.stateNode.implementation===A.implementation){l(R,S.sibling),x=n(S,A.children||[]),x.return=R,R=x;break t}else{l(R,S);break}else e(R,S);S=S.sibling}x=ps(A,R.mode,x),x.return=R,R=x}return r(R);case lt:return A=za(A),Mt(R,S,A,x)}if(xt(A))return J(R,S,A,x);if(Gt(A)){if(P=Gt(A),typeof P!="function")throw Error(s(150));return A=P.call(A),tt(R,S,A,x)}if(typeof A.then=="function")return Mt(R,S,Mi(A),x);if(A.$$typeof===X)return Mt(R,S,Oi(R,A),x);_i(R,A)}return typeof A=="string"&&A!==""||typeof A=="number"||typeof A=="bigint"?(A=""+A,S!==null&&S.tag===6?(l(R,S.sibling),x=n(S,A),x.return=R,R=x):(l(R,S),x=vs(A,R.mode,x),x.return=R,R=x),r(R)):l(R,S)}return function(R,S,A,x){try{hu=0;var P=Mt(R,S,A,x);return hn=null,P}catch(k){if(k===on||k===zi)throw k;var gt=ze(29,k,null,R.mode);return gt.lanes=x,gt.return=R,gt}finally{}}}var Ma=nh(!0),uh=nh(!1),Ll=!1;function _s(t){t.updateQueue={baseState:t.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function Ds(t,e){t=t.updateQueue,e.updateQueue===t&&(e.updateQueue={baseState:t.baseState,firstBaseUpdate:t.firstBaseUpdate,lastBaseUpdate:t.lastBaseUpdate,shared:t.shared,callbacks:null})}function Yl(t){return{lane:t,tag:0,payload:null,callback:null,next:null}}function Gl(t,e,l){var a=t.updateQueue;if(a===null)return null;if(a=a.shared,(bt&2)!==0){var n=a.pending;return n===null?e.next=e:(e.next=n.next,n.next=e),a.pending=e,e=bi(t),wo(t,null,l),e}return Si(t,a,e,l),bi(t)}function mu(t,e,l){if(e=e.updateQueue,e!==null&&(e=e.shared,(l&4194048)!==0)){var a=e.lanes;a&=t.pendingLanes,l|=a,e.lanes=l,$r(t,l)}}function Us(t,e){var l=t.updateQueue,a=t.alternate;if(a!==null&&(a=a.updateQueue,l===a)){var n=null,u=null;if(l=l.firstBaseUpdate,l!==null){do{var r={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};u===null?n=u=r:u=u.next=r,l=l.next}while(l!==null);u===null?n=u=e:u=u.next=e}else n=u=e;l={baseState:a.baseState,firstBaseUpdate:n,lastBaseUpdate:u,shared:a.shared,callbacks:a.callbacks},t.updateQueue=l;return}t=l.lastBaseUpdate,t===null?l.firstBaseUpdate=e:t.next=e,l.lastBaseUpdate=e}var xs=!1;function yu(){if(xs){var t=rn;if(t!==null)throw t}}function vu(t,e,l,a){xs=!1;var n=t.updateQueue;Ll=!1;var u=n.firstBaseUpdate,r=n.lastBaseUpdate,h=n.shared.pending;if(h!==null){n.shared.pending=null;var y=h,z=y.next;y.next=null,r===null?u=z:r.next=z,r=y;var D=t.alternate;D!==null&&(D=D.updateQueue,h=D.lastBaseUpdate,h!==r&&(h===null?D.firstBaseUpdate=z:h.next=z,D.lastBaseUpdate=y))}if(u!==null){var H=n.baseState;r=0,D=z=y=null,h=u;do{var C=h.lane&-536870913,_=C!==h.lane;if(_?(dt&C)===C:(a&C)===C){C!==0&&C===fn&&(xs=!0),D!==null&&(D=D.next={lane:0,tag:h.tag,payload:h.payload,callback:null,next:null});t:{var J=t,tt=h;C=e;var Mt=l;switch(tt.tag){case 1:if(J=tt.payload,typeof J=="function"){H=J.call(Mt,H,C);break t}H=J;break t;case 3:J.flags=J.flags&-65537|128;case 0:if(J=tt.payload,C=typeof J=="function"?J.call(Mt,H,C):J,C==null)break t;H=T({},H,C);break t;case 2:Ll=!0}}C=h.callback,C!==null&&(t.flags|=64,_&&(t.flags|=8192),_=n.callbacks,_===null?n.callbacks=[C]:_.push(C))}else _={lane:C,tag:h.tag,payload:h.payload,callback:h.callback,next:null},D===null?(z=D=_,y=H):D=D.next=_,r|=C;if(h=h.next,h===null){if(h=n.shared.pending,h===null)break;_=h,h=_.next,_.next=null,n.lastBaseUpdate=_,n.shared.pending=null}}while(!0);D===null&&(y=H),n.baseState=y,n.firstBaseUpdate=z,n.lastBaseUpdate=D,u===null&&(n.shared.lanes=0),Vl|=r,t.lanes=r,t.memoizedState=H}}function ih(t,e){if(typeof t!="function")throw Error(s(191,t));t.call(e)}function ch(t,e){var l=t.callbacks;if(l!==null)for(t.callbacks=null,t=0;tu?u:8;var r=U.T,h={};U.T=h,Ws(t,!1,e,l);try{var y=n(),z=U.S;if(z!==null&&z(h,y),y!==null&&typeof y=="object"&&typeof y.then=="function"){var D=E0(y,a);Su(t,e,D,Ue(t))}else Su(t,e,a,Ue(t))}catch(H){Su(t,e,{then:function(){},status:"rejected",reason:H},Ue())}finally{Z.p=u,r!==null&&h.types!==null&&(r.types=h.types),U.T=r}}function C0(){}function $s(t,e,l,a){if(t.tag!==5)throw Error(s(476));var n=Lh(t).queue;Qh(t,n,e,at,l===null?C0:function(){return Yh(t),l(a)})}function Lh(t){var e=t.memoizedState;if(e!==null)return e;e={memoizedState:at,baseState:at,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ml,lastRenderedState:at},next:null};var l={};return e.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:ml,lastRenderedState:l},next:null},t.memoizedState=e,t=t.alternate,t!==null&&(t.memoizedState=e),e}function Yh(t){var e=Lh(t);e.next===null&&(e=t.alternate.memoizedState),Su(t,e.next.queue,{},Ue())}function ks(){return ae(qu)}function Gh(){return Zt().memoizedState}function wh(){return Zt().memoizedState}function M0(t){for(var e=t.return;e!==null;){switch(e.tag){case 24:case 3:var l=Ue();t=Yl(l);var a=Gl(e,t,l);a!==null&&(ge(a,e,l),mu(a,e,l)),e={cache:As()},t.payload=e;return}e=e.return}}function _0(t,e,l){var a=Ue();l={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Li(t)?Zh(e,l):(l=ms(t,e,l,a),l!==null&&(ge(l,t,a),Kh(l,e,a)))}function Xh(t,e,l){var a=Ue();Su(t,e,l,a)}function Su(t,e,l,a){var n={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(Li(t))Zh(e,n);else{var u=t.alternate;if(t.lanes===0&&(u===null||u.lanes===0)&&(u=e.lastRenderedReducer,u!==null))try{var r=e.lastRenderedState,h=u(r,l);if(n.hasEagerState=!0,n.eagerState=h,Ae(h,r))return Si(t,e,n,0),Dt===null&&gi(),!1}catch{}finally{}if(l=ms(t,e,n,a),l!==null)return ge(l,t,a),Kh(l,e,a),!0}return!1}function Ws(t,e,l,a){if(a={lane:2,revertLane:Df(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},Li(t)){if(e)throw Error(s(479))}else e=ms(t,l,a,2),e!==null&&ge(e,t,2)}function Li(t){var e=t.alternate;return t===it||e!==null&&e===it}function Zh(t,e){mn=xi=!0;var l=t.pending;l===null?e.next=e:(e.next=l.next,l.next=e),t.pending=e}function Kh(t,e,l){if((l&4194048)!==0){var a=e.lanes;a&=t.pendingLanes,l|=a,e.lanes=l,$r(t,l)}}var bu={readContext:ae,use:ji,useCallback:Bt,useContext:Bt,useEffect:Bt,useImperativeHandle:Bt,useLayoutEffect:Bt,useInsertionEffect:Bt,useMemo:Bt,useReducer:Bt,useRef:Bt,useState:Bt,useDebugValue:Bt,useDeferredValue:Bt,useTransition:Bt,useSyncExternalStore:Bt,useId:Bt,useHostTransitionStatus:Bt,useFormState:Bt,useActionState:Bt,useOptimistic:Bt,useMemoCache:Bt,useCacheRefresh:Bt};bu.useEffectEvent=Bt;var Vh={readContext:ae,use:ji,useCallback:function(t,e){return fe().memoizedState=[t,e===void 0?null:e],t},useContext:ae,useEffect:_h,useImperativeHandle:function(t,e,l){l=l!=null?l.concat([t]):null,Bi(4194308,4,Nh.bind(null,e,t),l)},useLayoutEffect:function(t,e){return Bi(4194308,4,t,e)},useInsertionEffect:function(t,e){Bi(4,2,t,e)},useMemo:function(t,e){var l=fe();e=e===void 0?null:e;var a=t();if(_a){xl(!0);try{t()}finally{xl(!1)}}return l.memoizedState=[a,e],a},useReducer:function(t,e,l){var a=fe();if(l!==void 0){var n=l(e);if(_a){xl(!0);try{l(e)}finally{xl(!1)}}}else n=e;return a.memoizedState=a.baseState=n,t={pending:null,lanes:0,dispatch:null,lastRenderedReducer:t,lastRenderedState:n},a.queue=t,t=t.dispatch=_0.bind(null,it,t),[a.memoizedState,t]},useRef:function(t){var e=fe();return t={current:t},e.memoizedState=t},useState:function(t){t=Zs(t);var e=t.queue,l=Xh.bind(null,it,e);return e.dispatch=l,[t.memoizedState,l]},useDebugValue:Js,useDeferredValue:function(t,e){var l=fe();return Fs(l,t,e)},useTransition:function(){var t=Zs(!1);return t=Qh.bind(null,it,t.queue,!0,!1),fe().memoizedState=t,[!1,t]},useSyncExternalStore:function(t,e,l){var a=it,n=fe();if(vt){if(l===void 0)throw Error(s(407));l=l()}else{if(l=e(),Dt===null)throw Error(s(349));(dt&127)!==0||dh(a,e,l)}n.memoizedState=l;var u={value:l,getSnapshot:e};return n.queue=u,_h(yh.bind(null,a,u,t),[t]),a.flags|=2048,vn(9,{destroy:void 0},mh.bind(null,a,u,l,e),null),l},useId:function(){var t=fe(),e=Dt.identifierPrefix;if(vt){var l=Pe,a=We;l=(a&~(1<<32-Oe(a)-1)).toString(32)+l,e="_"+e+"R_"+l,l=Ni++,0<\/script>",u=u.removeChild(u.firstChild);break;case"select":u=typeof a.is=="string"?r.createElement("select",{is:a.is}):r.createElement("select"),a.multiple?u.multiple=!0:a.size&&(u.size=a.size);break;default:u=typeof a.is=="string"?r.createElement(n,{is:a.is}):r.createElement(n)}}u[ee]=e,u[he]=a;t:for(r=e.child;r!==null;){if(r.tag===5||r.tag===6)u.appendChild(r.stateNode);else if(r.tag!==4&&r.tag!==27&&r.child!==null){r.child.return=r,r=r.child;continue}if(r===e)break t;for(;r.sibling===null;){if(r.return===null||r.return===e)break t;r=r.return}r.sibling.return=r.return,r=r.sibling}e.stateNode=u;t:switch(ue(u,n,a),n){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break t;case"img":a=!0;break t;default:a=!1}a&&vl(e)}}return Ht(e),hf(e,e.type,t===null?null:t.memoizedProps,e.pendingProps,l),null;case 6:if(t&&e.stateNode!=null)t.memoizedProps!==a&&vl(e);else{if(typeof a!="string"&&e.stateNode===null)throw Error(s(166));if(t=rt.current,cn(e)){if(t=e.stateNode,l=e.memoizedProps,a=null,n=le,n!==null)switch(n.tag){case 27:case 5:a=n.memoizedProps}t[ee]=e,t=!!(t.nodeValue===l||a!==null&&a.suppressHydrationWarning===!0||om(t.nodeValue,l)),t||Bl(e,!0)}else t=uc(t).createTextNode(a),t[ee]=e,e.stateNode=t}return Ht(e),null;case 31:if(l=e.memoizedState,t===null||t.memoizedState!==null){if(a=cn(e),l!==null){if(t===null){if(!a)throw Error(s(318));if(t=e.memoizedState,t=t!==null?t.dehydrated:null,!t)throw Error(s(557));t[ee]=e}else Ta(),(e.flags&128)===0&&(e.memoizedState=null),e.flags|=4;Ht(e),t=!1}else l=Es(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=l),t=!0;if(!t)return e.flags&256?(Me(e),e):(Me(e),null);if((e.flags&128)!==0)throw Error(s(558))}return Ht(e),null;case 13:if(a=e.memoizedState,t===null||t.memoizedState!==null&&t.memoizedState.dehydrated!==null){if(n=cn(e),a!==null&&a.dehydrated!==null){if(t===null){if(!n)throw Error(s(318));if(n=e.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(s(317));n[ee]=e}else Ta(),(e.flags&128)===0&&(e.memoizedState=null),e.flags|=4;Ht(e),n=!1}else n=Es(),t!==null&&t.memoizedState!==null&&(t.memoizedState.hydrationErrors=n),n=!0;if(!n)return e.flags&256?(Me(e),e):(Me(e),null)}return Me(e),(e.flags&128)!==0?(e.lanes=l,e):(l=a!==null,t=t!==null&&t.memoizedState!==null,l&&(a=e.child,n=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(n=a.alternate.memoizedState.cachePool.pool),u=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(u=a.memoizedState.cachePool.pool),u!==n&&(a.flags|=2048)),l!==t&&l&&(e.child.flags|=8192),Zi(e,e.updateQueue),Ht(e),null);case 4:return wt(),t===null&&Hf(e.stateNode.containerInfo),Ht(e),null;case 10:return hl(e.type),Ht(e),null;case 19:if(j(Xt),a=e.memoizedState,a===null)return Ht(e),null;if(n=(e.flags&128)!==0,u=a.rendering,u===null)if(n)Tu(a,!1);else{if(Qt!==0||t!==null&&(t.flags&128)!==0)for(t=e.child;t!==null;){if(u=Ui(t),u!==null){for(e.flags|=128,Tu(a,!1),t=u.updateQueue,e.updateQueue=t,Zi(e,t),e.subtreeFlags=0,t=l,l=e.child;l!==null;)Xo(l,t),l=l.sibling;return V(Xt,Xt.current&1|2),vt&&rl(e,a.treeForkCount),e.child}t=t.sibling}a.tail!==null&&Te()>$i&&(e.flags|=128,n=!0,Tu(a,!1),e.lanes=4194304)}else{if(!n)if(t=Ui(u),t!==null){if(e.flags|=128,n=!0,t=t.updateQueue,e.updateQueue=t,Zi(e,t),Tu(a,!0),a.tail===null&&a.tailMode==="hidden"&&!u.alternate&&!vt)return Ht(e),null}else 2*Te()-a.renderingStartTime>$i&&l!==536870912&&(e.flags|=128,n=!0,Tu(a,!1),e.lanes=4194304);a.isBackwards?(u.sibling=e.child,e.child=u):(t=a.last,t!==null?t.sibling=u:e.child=u,a.last=u)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=Te(),t.sibling=null,l=Xt.current,V(Xt,n?l&1|2:l&1),vt&&rl(e,a.treeForkCount),t):(Ht(e),null);case 22:case 23:return Me(e),Hs(),a=e.memoizedState!==null,t!==null?t.memoizedState!==null!==a&&(e.flags|=8192):a&&(e.flags|=8192),a?(l&536870912)!==0&&(e.flags&128)===0&&(Ht(e),e.subtreeFlags&6&&(e.flags|=8192)):Ht(e),l=e.updateQueue,l!==null&&Zi(e,l.retryQueue),l=null,t!==null&&t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(l=t.memoizedState.cachePool.pool),a=null,e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(a=e.memoizedState.cachePool.pool),a!==l&&(e.flags|=2048),t!==null&&j(Aa),null;case 24:return l=null,t!==null&&(l=t.memoizedState.cache),e.memoizedState.cache!==l&&(e.flags|=2048),hl(Kt),Ht(e),null;case 25:return null;case 30:return null}throw Error(s(156,e.tag))}function H0(t,e){switch(Ss(e),e.tag){case 1:return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 3:return hl(Kt),wt(),t=e.flags,(t&65536)!==0&&(t&128)===0?(e.flags=t&-65537|128,e):null;case 26:case 27:case 5:return ei(e),null;case 31:if(e.memoizedState!==null){if(Me(e),e.alternate===null)throw Error(s(340));Ta()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 13:if(Me(e),t=e.memoizedState,t!==null&&t.dehydrated!==null){if(e.alternate===null)throw Error(s(340));Ta()}return t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 19:return j(Xt),null;case 4:return wt(),null;case 10:return hl(e.type),null;case 22:case 23:return Me(e),Hs(),t!==null&&j(Aa),t=e.flags,t&65536?(e.flags=t&-65537|128,e):null;case 24:return hl(Kt),null;case 25:return null;default:return null}}function vd(t,e){switch(Ss(e),e.tag){case 3:hl(Kt),wt();break;case 26:case 27:case 5:ei(e);break;case 4:wt();break;case 31:e.memoizedState!==null&&Me(e);break;case 13:Me(e);break;case 19:j(Xt);break;case 10:hl(e.type);break;case 22:case 23:Me(e),Hs(),t!==null&&j(Aa);break;case 24:hl(Kt)}}function Ru(t,e){try{var l=e.updateQueue,a=l!==null?l.lastEffect:null;if(a!==null){var n=a.next;l=n;do{if((l.tag&t)===t){a=void 0;var u=l.create,r=l.inst;a=u(),r.destroy=a}l=l.next}while(l!==n)}}catch(h){Ot(e,e.return,h)}}function Zl(t,e,l){try{var a=e.updateQueue,n=a!==null?a.lastEffect:null;if(n!==null){var u=n.next;a=u;do{if((a.tag&t)===t){var r=a.inst,h=r.destroy;if(h!==void 0){r.destroy=void 0,n=e;var y=l,z=h;try{z()}catch(D){Ot(n,y,D)}}}a=a.next}while(a!==u)}}catch(D){Ot(e,e.return,D)}}function pd(t){var e=t.updateQueue;if(e!==null){var l=t.stateNode;try{ch(e,l)}catch(a){Ot(t,t.return,a)}}}function gd(t,e,l){l.props=Da(t.type,t.memoizedProps),l.state=t.memoizedState;try{l.componentWillUnmount()}catch(a){Ot(t,e,a)}}function Ou(t,e){try{var l=t.ref;if(l!==null){switch(t.tag){case 26:case 27:case 5:var a=t.stateNode;break;case 30:a=t.stateNode;break;default:a=t.stateNode}typeof l=="function"?t.refCleanup=l(a):l.current=a}}catch(n){Ot(t,e,n)}}function Ie(t,e){var l=t.ref,a=t.refCleanup;if(l!==null)if(typeof a=="function")try{a()}catch(n){Ot(t,e,n)}finally{t.refCleanup=null,t=t.alternate,t!=null&&(t.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(n){Ot(t,e,n)}else l.current=null}function Sd(t){var e=t.type,l=t.memoizedProps,a=t.stateNode;try{t:switch(e){case"button":case"input":case"select":case"textarea":l.autoFocus&&a.focus();break t;case"img":l.src?a.src=l.src:l.srcSet&&(a.srcset=l.srcSet)}}catch(n){Ot(t,t.return,n)}}function df(t,e,l){try{var a=t.stateNode;lp(a,t.type,l,e),a[he]=e}catch(n){Ot(t,t.return,n)}}function bd(t){return t.tag===5||t.tag===3||t.tag===26||t.tag===27&&Wl(t.type)||t.tag===4}function mf(t){t:for(;;){for(;t.sibling===null;){if(t.return===null||bd(t.return))return null;t=t.return}for(t.sibling.return=t.return,t=t.sibling;t.tag!==5&&t.tag!==6&&t.tag!==18;){if(t.tag===27&&Wl(t.type)||t.flags&2||t.child===null||t.tag===4)continue t;t.child.return=t,t=t.child}if(!(t.flags&2))return t.stateNode}}function yf(t,e,l){var a=t.tag;if(a===5||a===6)t=t.stateNode,e?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(t,e):(e=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,e.appendChild(t),l=l._reactRootContainer,l!=null||e.onclick!==null||(e.onclick=cl));else if(a!==4&&(a===27&&Wl(t.type)&&(l=t.stateNode,e=null),t=t.child,t!==null))for(yf(t,e,l),t=t.sibling;t!==null;)yf(t,e,l),t=t.sibling}function Ki(t,e,l){var a=t.tag;if(a===5||a===6)t=t.stateNode,e?l.insertBefore(t,e):l.appendChild(t);else if(a!==4&&(a===27&&Wl(t.type)&&(l=t.stateNode),t=t.child,t!==null))for(Ki(t,e,l),t=t.sibling;t!==null;)Ki(t,e,l),t=t.sibling}function Ed(t){var e=t.stateNode,l=t.memoizedProps;try{for(var a=t.type,n=e.attributes;n.length;)e.removeAttributeNode(n[0]);ue(e,a,l),e[ee]=t,e[he]=l}catch(u){Ot(t,t.return,u)}}var pl=!1,Ft=!1,vf=!1,Td=typeof WeakSet=="function"?WeakSet:Set,Pt=null;function j0(t,e){if(t=t.containerInfo,Bf=hc,t=Ho(t),ss(t)){if("selectionStart"in t)var l={start:t.selectionStart,end:t.selectionEnd};else t:{l=(l=t.ownerDocument)&&l.defaultView||window;var a=l.getSelection&&l.getSelection();if(a&&a.rangeCount!==0){l=a.anchorNode;var n=a.anchorOffset,u=a.focusNode;a=a.focusOffset;try{l.nodeType,u.nodeType}catch{l=null;break t}var r=0,h=-1,y=-1,z=0,D=0,H=t,C=null;e:for(;;){for(var _;H!==l||n!==0&&H.nodeType!==3||(h=r+n),H!==u||a!==0&&H.nodeType!==3||(y=r+a),H.nodeType===3&&(r+=H.nodeValue.length),(_=H.firstChild)!==null;)C=H,H=_;for(;;){if(H===t)break e;if(C===l&&++z===n&&(h=r),C===u&&++D===a&&(y=r),(_=H.nextSibling)!==null)break;H=C,C=H.parentNode}H=_}l=h===-1||y===-1?null:{start:h,end:y}}else l=null}l=l||{start:0,end:0}}else l=null;for(Qf={focusedElem:t,selectionRange:l},hc=!1,Pt=e;Pt!==null;)if(e=Pt,t=e.child,(e.subtreeFlags&1028)!==0&&t!==null)t.return=e,Pt=t;else for(;Pt!==null;){switch(e=Pt,u=e.alternate,t=e.flags,e.tag){case 0:if((t&4)!==0&&(t=e.updateQueue,t=t!==null?t.events:null,t!==null))for(l=0;l title"))),ue(u,a,l),u[ee]=t,Wt(u),a=u;break t;case"link":var r=Mm("link","href",n).get(a+(l.href||""));if(r){for(var h=0;hMt&&(r=Mt,Mt=tt,tt=r);var R=xo(h,tt),S=xo(h,Mt);if(R&&S&&(_.rangeCount!==1||_.anchorNode!==R.node||_.anchorOffset!==R.offset||_.focusNode!==S.node||_.focusOffset!==S.offset)){var A=H.createRange();A.setStart(R.node,R.offset),_.removeAllRanges(),tt>Mt?(_.addRange(A),_.extend(S.node,S.offset)):(A.setEnd(S.node,S.offset),_.addRange(A))}}}}for(H=[],_=h;_=_.parentNode;)_.nodeType===1&&H.push({element:_,left:_.scrollLeft,top:_.scrollTop});for(typeof h.focus=="function"&&h.focus(),h=0;hl?32:l,U.T=null,l=Rf,Rf=null;var u=Fl,r=Tl;if($t=0,En=Fl=null,Tl=0,(bt&6)!==0)throw Error(s(331));var h=bt;if(bt|=4,Nd(u.current),Dd(u,u.current,r,l),bt=h,Du(0,!1),Re&&typeof Re.onPostCommitFiberRoot=="function")try{Re.onPostCommitFiberRoot(Fn,u)}catch{}return!0}finally{Z.p=n,U.T=a,Wd(t,e)}}function Id(t,e,l){e=Be(l,e),e=ef(t.stateNode,e,2),t=Gl(t,e,2),t!==null&&(kn(t,2),tl(t))}function Ot(t,e,l){if(t.tag===3)Id(t,t,l);else for(;e!==null;){if(e.tag===3){Id(e,t,l);break}else if(e.tag===1){var a=e.stateNode;if(typeof e.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(Jl===null||!Jl.has(a))){t=Be(l,t),l=td(2),a=Gl(e,l,2),a!==null&&(ed(l,a,e,t),kn(a,2),tl(a));break}}e=e.return}}function Cf(t,e,l){var a=t.pingCache;if(a===null){a=t.pingCache=new Q0;var n=new Set;a.set(e,n)}else n=a.get(e),n===void 0&&(n=new Set,a.set(e,n));n.has(l)||(Sf=!0,n.add(l),t=X0.bind(null,t,e,l),e.then(t,t))}function X0(t,e,l){var a=t.pingCache;a!==null&&a.delete(e),t.pingedLanes|=t.suspendedLanes&l,t.warmLanes&=~l,Dt===t&&(dt&l)===l&&(Qt===4||Qt===3&&(dt&62914560)===dt&&300>Te()-Fi?(bt&2)===0&&Tn(t,0):bf|=l,bn===dt&&(bn=0)),tl(t)}function tm(t,e){e===0&&(e=Jr()),t=ba(t,e),t!==null&&(kn(t,e),tl(t))}function Z0(t){var e=t.memoizedState,l=0;e!==null&&(l=e.retryLane),tm(t,l)}function K0(t,e){var l=0;switch(t.tag){case 31:case 13:var a=t.stateNode,n=t.memoizedState;n!==null&&(l=n.retryLane);break;case 19:a=t.stateNode;break;case 22:a=t.stateNode._retryCache;break;default:throw Error(s(314))}a!==null&&a.delete(e),tm(t,l)}function V0(t,e){return Lc(t,e)}var ec=null,On=null,Mf=!1,lc=!1,_f=!1,kl=0;function tl(t){t!==On&&t.next===null&&(On===null?ec=On=t:On=On.next=t),lc=!0,Mf||(Mf=!0,F0())}function Du(t,e){if(!_f&&lc){_f=!0;do for(var l=!1,a=ec;a!==null;){if(t!==0){var n=a.pendingLanes;if(n===0)var u=0;else{var r=a.suspendedLanes,h=a.pingedLanes;u=(1<<31-Oe(42|t)+1)-1,u&=n&~(r&~h),u=u&201326741?u&201326741|1:u?u|2:0}u!==0&&(l=!0,nm(a,u))}else u=dt,u=ii(a,a===Dt?u:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(u&3)===0||$n(a,u)||(l=!0,nm(a,u));a=a.next}while(l);_f=!1}}function J0(){em()}function em(){lc=Mf=!1;var t=0;kl!==0&&np()&&(t=kl);for(var e=Te(),l=null,a=ec;a!==null;){var n=a.next,u=lm(a,e);u===0?(a.next=null,l===null?ec=n:l.next=n,n===null&&(On=l)):(l=a,(t!==0||(u&3)!==0)&&(lc=!0)),a=n}$t!==0&&$t!==5||Du(t),kl!==0&&(kl=0)}function lm(t,e){for(var l=t.suspendedLanes,a=t.pingedLanes,n=t.expirationTimes,u=t.pendingLanes&-62914561;0h)break;var D=y.transferSize,H=y.initiatorType;D&&hm(H)&&(y=y.responseEnd,r+=D*(y"u"?null:document;function Om(t,e,l){var a=An;if(a&&typeof e=="string"&&e){var n=je(e);n='link[rel="'+t+'"][href="'+n+'"]',typeof l=="string"&&(n+='[crossorigin="'+l+'"]'),Rm.has(n)||(Rm.add(n),t={rel:t,crossOrigin:l,href:e},a.querySelector(n)===null&&(e=a.createElement("link"),ue(e,"link",t),Wt(e),a.head.appendChild(e)))}}function dp(t){Rl.D(t),Om("dns-prefetch",t,null)}function mp(t,e){Rl.C(t,e),Om("preconnect",t,e)}function yp(t,e,l){Rl.L(t,e,l);var a=An;if(a&&t&&e){var n='link[rel="preload"][as="'+je(e)+'"]';e==="image"&&l&&l.imageSrcSet?(n+='[imagesrcset="'+je(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(n+='[imagesizes="'+je(l.imageSizes)+'"]')):n+='[href="'+je(t)+'"]';var u=n;switch(e){case"style":u=zn(t);break;case"script":u=Cn(t)}Xe.has(u)||(t=T({rel:"preload",href:e==="image"&&l&&l.imageSrcSet?void 0:t,as:e},l),Xe.set(u,t),a.querySelector(n)!==null||e==="style"&&a.querySelector(Hu(u))||e==="script"&&a.querySelector(ju(u))||(e=a.createElement("link"),ue(e,"link",t),Wt(e),a.head.appendChild(e)))}}function vp(t,e){Rl.m(t,e);var l=An;if(l&&t){var a=e&&typeof e.as=="string"?e.as:"script",n='link[rel="modulepreload"][as="'+je(a)+'"][href="'+je(t)+'"]',u=n;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":u=Cn(t)}if(!Xe.has(u)&&(t=T({rel:"modulepreload",href:t},e),Xe.set(u,t),l.querySelector(n)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(ju(u)))return}a=l.createElement("link"),ue(a,"link",t),Wt(a),l.head.appendChild(a)}}}function pp(t,e,l){Rl.S(t,e,l);var a=An;if(a&&t){var n=Ja(a).hoistableStyles,u=zn(t);e=e||"default";var r=n.get(u);if(!r){var h={loading:0,preload:null};if(r=a.querySelector(Hu(u)))h.loading=5;else{t=T({rel:"stylesheet",href:t,"data-precedence":e},l),(l=Xe.get(u))&&Kf(t,l);var y=r=a.createElement("link");Wt(y),ue(y,"link",t),y._p=new Promise(function(z,D){y.onload=z,y.onerror=D}),y.addEventListener("load",function(){h.loading|=1}),y.addEventListener("error",function(){h.loading|=2}),h.loading|=4,cc(r,e,a)}r={type:"stylesheet",instance:r,count:1,state:h},n.set(u,r)}}}function gp(t,e){Rl.X(t,e);var l=An;if(l&&t){var a=Ja(l).hoistableScripts,n=Cn(t),u=a.get(n);u||(u=l.querySelector(ju(n)),u||(t=T({src:t,async:!0},e),(e=Xe.get(n))&&Vf(t,e),u=l.createElement("script"),Wt(u),ue(u,"link",t),l.head.appendChild(u)),u={type:"script",instance:u,count:1,state:null},a.set(n,u))}}function Sp(t,e){Rl.M(t,e);var l=An;if(l&&t){var a=Ja(l).hoistableScripts,n=Cn(t),u=a.get(n);u||(u=l.querySelector(ju(n)),u||(t=T({src:t,async:!0,type:"module"},e),(e=Xe.get(n))&&Vf(t,e),u=l.createElement("script"),Wt(u),ue(u,"link",t),l.head.appendChild(u)),u={type:"script",instance:u,count:1,state:null},a.set(n,u))}}function Am(t,e,l,a){var n=(n=rt.current)?ic(n):null;if(!n)throw Error(s(446));switch(t){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(e=zn(l.href),l=Ja(n).hoistableStyles,a=l.get(e),a||(a={type:"style",instance:null,count:0,state:null},l.set(e,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){t=zn(l.href);var u=Ja(n).hoistableStyles,r=u.get(t);if(r||(n=n.ownerDocument||n,r={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},u.set(t,r),(u=n.querySelector(Hu(t)))&&!u._p&&(r.instance=u,r.state.loading=5),Xe.has(t)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},Xe.set(t,l),u||bp(n,t,l,r.state))),e&&a===null)throw Error(s(528,""));return r}if(e&&a!==null)throw Error(s(529,""));return null;case"script":return e=l.async,l=l.src,typeof l=="string"&&e&&typeof e!="function"&&typeof e!="symbol"?(e=Cn(l),l=Ja(n).hoistableScripts,a=l.get(e),a||(a={type:"script",instance:null,count:0,state:null},l.set(e,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(s(444,t))}}function zn(t){return'href="'+je(t)+'"'}function Hu(t){return'link[rel="stylesheet"]['+t+"]"}function zm(t){return T({},t,{"data-precedence":t.precedence,precedence:null})}function bp(t,e,l,a){t.querySelector('link[rel="preload"][as="style"]['+e+"]")?a.loading=1:(e=t.createElement("link"),a.preload=e,e.addEventListener("load",function(){return a.loading|=1}),e.addEventListener("error",function(){return a.loading|=2}),ue(e,"link",l),Wt(e),t.head.appendChild(e))}function Cn(t){return'[src="'+je(t)+'"]'}function ju(t){return"script[async]"+t}function Cm(t,e,l){if(e.count++,e.instance===null)switch(e.type){case"style":var a=t.querySelector('style[data-href~="'+je(l.href)+'"]');if(a)return e.instance=a,Wt(a),a;var n=T({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return a=(t.ownerDocument||t).createElement("style"),Wt(a),ue(a,"style",n),cc(a,l.precedence,t),e.instance=a;case"stylesheet":n=zn(l.href);var u=t.querySelector(Hu(n));if(u)return e.state.loading|=4,e.instance=u,Wt(u),u;a=zm(l),(n=Xe.get(n))&&Kf(a,n),u=(t.ownerDocument||t).createElement("link"),Wt(u);var r=u;return r._p=new Promise(function(h,y){r.onload=h,r.onerror=y}),ue(u,"link",a),e.state.loading|=4,cc(u,l.precedence,t),e.instance=u;case"script":return u=Cn(l.src),(n=t.querySelector(ju(u)))?(e.instance=n,Wt(n),n):(a=l,(n=Xe.get(u))&&(a=T({},l),Vf(a,n)),t=t.ownerDocument||t,n=t.createElement("script"),Wt(n),ue(n,"link",a),t.head.appendChild(n),e.instance=n);case"void":return null;default:throw Error(s(443,e.type))}else e.type==="stylesheet"&&(e.state.loading&4)===0&&(a=e.instance,e.state.loading|=4,cc(a,l.precedence,t));return e.instance}function cc(t,e,l){for(var a=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),n=a.length?a[a.length-1]:null,u=n,r=0;r title"):null)}function Ep(t,e,l){if(l===1||e.itemProp!=null)return!1;switch(t){case"meta":case"title":return!0;case"style":if(typeof e.precedence!="string"||typeof e.href!="string"||e.href==="")break;return!0;case"link":if(typeof e.rel!="string"||typeof e.href!="string"||e.href===""||e.onLoad||e.onError)break;switch(e.rel){case"stylesheet":return t=e.disabled,typeof e.precedence=="string"&&t==null;default:return!0}case"script":if(e.async&&typeof e.async!="function"&&typeof e.async!="symbol"&&!e.onLoad&&!e.onError&&e.src&&typeof e.src=="string")return!0}return!1}function Dm(t){return!(t.type==="stylesheet"&&(t.state.loading&3)===0)}function Tp(t,e,l,a){if(l.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var n=zn(a.href),u=e.querySelector(Hu(n));if(u){e=u._p,e!==null&&typeof e=="object"&&typeof e.then=="function"&&(t.count++,t=fc.bind(t),e.then(t,t)),l.state.loading|=4,l.instance=u,Wt(u);return}u=e.ownerDocument||e,a=zm(a),(n=Xe.get(n))&&Kf(a,n),u=u.createElement("link"),Wt(u);var r=u;r._p=new Promise(function(h,y){r.onload=h,r.onerror=y}),ue(u,"link",a),l.instance=u}t.stylesheets===null&&(t.stylesheets=new Map),t.stylesheets.set(l,e),(e=l.state.preload)&&(l.state.loading&3)===0&&(t.count++,l=fc.bind(t),e.addEventListener("load",l),e.addEventListener("error",l))}}var Jf=0;function Rp(t,e){return t.stylesheets&&t.count===0&&oc(t,t.stylesheets),0Jf?50:800)+e);return t.unsuspend=l,function(){t.unsuspend=null,clearTimeout(a),clearTimeout(n)}}:null}function fc(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)oc(this,this.stylesheets);else if(this.unsuspend){var t=this.unsuspend;this.unsuspend=null,t()}}}var rc=null;function oc(t,e){t.stylesheets=null,t.unsuspend!==null&&(t.count++,rc=new Map,e.forEach(Op,t),rc=null,fc.call(t))}function Op(t,e){if(!(e.state.loading&4)){var l=rc.get(t);if(l)var a=l.get(null);else{l=new Map,rc.set(t,l);for(var n=t.querySelectorAll("link[data-precedence],style[data-precedence]"),u=0;u"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(c){console.error(c)}}return i(),ar.exports=wp(),ar.exports}var Zp=Xp();/** + * react-router v7.18.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var Mr=/^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i,_y=/^[\\/]{2}/;function Kp(i,c){return c+i.replace(/\\/g,"/")}var ey="popstate";function ly(i){return typeof i=="object"&&i!=null&&"pathname"in i&&"search"in i&&"hash"in i&&"state"in i&&"key"in i}function Vp(i={}){function c(s,o){var m;let d=(m=o.state)==null?void 0:m.masked,{pathname:p,search:E,hash:g}=d||s.location;return rr("",{pathname:p,search:E,hash:g},o.state&&o.state.usr||null,o.state&&o.state.key||"default",d?{pathname:s.location.pathname,search:s.location.search,hash:s.location.hash}:void 0)}function f(s,o){return typeof o=="string"?o:Zu(o)}return Fp(c,f,null,i)}function qt(i,c){if(i===!1||i===null||typeof i>"u")throw new Error(c)}function nl(i,c){if(!i){typeof console<"u"&&console.warn(c);try{throw new Error(c)}catch{}}}function Jp(){return Math.random().toString(36).substring(2,10)}function ay(i,c){return{usr:i.state,key:i.key,idx:c,masked:i.mask?{pathname:i.pathname,search:i.search,hash:i.hash}:void 0}}function rr(i,c,f=null,s,o){return{pathname:typeof i=="string"?i:i.pathname,search:"",hash:"",...typeof c=="string"?Xn(c):c,state:f,key:c&&c.key||s||Jp(),mask:o}}function Zu({pathname:i="/",search:c="",hash:f=""}){return c&&c!=="?"&&(i+=c.charAt(0)==="?"?c:"?"+c),f&&f!=="#"&&(i+=f.charAt(0)==="#"?f:"#"+f),i}function Xn(i){let c={};if(i){let f=i.indexOf("#");f>=0&&(c.hash=i.substring(f),i=i.substring(0,f));let s=i.indexOf("?");s>=0&&(c.search=i.substring(s),i=i.substring(0,s)),i&&(c.pathname=i)}return c}function Fp(i,c,f,s={}){let{window:o=document.defaultView,v5Compat:d=!1}=s,p=o.history,E="POP",g=null,m=O();m==null&&(m=0,p.replaceState({...p.state,idx:m},""));function O(){return(p.state||{idx:null}).idx}function T(){E="POP";let L=O(),q=L==null?null:L-m;m=L,g&&g({action:E,location:Q.location,delta:q})}function N(L,q){E="PUSH";let F=ly(L)?L:rr(Q.location,L,q);m=O()+1;let X=ay(F,m),G=Q.createHref(F.mask||F);try{p.pushState(X,"",G)}catch(et){if(et instanceof DOMException&&et.name==="DataCloneError")throw et;o.location.assign(G)}d&&g&&g({action:E,location:Q.location,delta:1})}function B(L,q){E="REPLACE";let F=ly(L)?L:rr(Q.location,L,q);m=O();let X=ay(F,m),G=Q.createHref(F.mask||F);p.replaceState(X,"",G),d&&g&&g({action:E,location:Q.location,delta:0})}function Y(L){return $p(o,L)}let Q={get action(){return E},get location(){return i(o,p)},listen(L){if(g)throw new Error("A history only accepts one active listener");return o.addEventListener(ey,T),g=L,()=>{o.removeEventListener(ey,T),g=null}},createHref(L){return c(o,L)},createURL:Y,encodeLocation(L){let q=Y(L);return{pathname:q.pathname,search:q.search,hash:q.hash}},push:N,replace:B,go(L){return p.go(L)}};return Q}function $p(i,c,f=!1){let s="http://localhost";i&&(s=i.location.origin!=="null"?i.location.origin:i.location.href),qt(s,"No window.location.(origin|href) available to create URL");let o=typeof c=="string"?c:Zu(c);return o=o.replace(/ $/,"%20"),!f&&_y.test(o)&&(o=s+o),new URL(o,s)}function Dy(i,c,f="/"){return kp(i,c,f,!1)}function kp(i,c,f,s,o){let d=typeof c=="string"?Xn(c):c,p=Dl(d.pathname||"/",f);if(p==null)return null;let E=Wp(i),g=null,m=sg(p);for(let O=0;g==null&&O{let O={relativePath:m===void 0?p.path||"":m,caseSensitive:p.caseSensitive===!0,childrenIndex:E,route:p};if(O.relativePath.startsWith("/")){if(!O.relativePath.startsWith(s)&&g)return;qt(O.relativePath.startsWith(s),`Absolute route path "${O.relativePath}" nested under path "${s}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),O.relativePath=O.relativePath.slice(s.length)}let T=ke([s,O.relativePath]),N=f.concat(O);p.children&&p.children.length>0&&(qt(p.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${T}".`),Uy(p.children,c,N,T,g)),!(p.path==null&&!p.index)&&c.push({path:T,score:ug(T,p.index),routesMeta:N.map((B,Y)=>{let[Q,L]=Hy(B.relativePath,B.caseSensitive,Y===N.length-1);return{...B,matcher:Q,compiledParams:L}})})};return i.forEach((p,E)=>{var g;if(p.path===""||!((g=p.path)!=null&&g.includes("?")))d(p,E);else for(let m of xy(p.path))d(p,E,!0,m)}),c}function xy(i){let c=i.split("/");if(c.length===0)return[];let[f,...s]=c,o=f.endsWith("?"),d=f.replace(/\?$/,"");if(s.length===0)return o?[d,""]:[d];let p=xy(s.join("/")),E=[];return E.push(...p.map(g=>g===""?d:[d,g].join("/"))),o&&E.push(...p),E.map(g=>i.startsWith("/")&&g===""?"/":g)}function Pp(i){i.sort((c,f)=>c.score!==f.score?f.score-c.score:ig(c.routesMeta.map(s=>s.childrenIndex),f.routesMeta.map(s=>s.childrenIndex)))}var Ip=/^:[\w-]+$/,tg=3,eg=2,lg=1,ag=10,ng=-2,ny=i=>i==="*";function ug(i,c){let f=i.split("/"),s=f.length;return f.some(ny)&&(s+=ng),c&&(s+=eg),f.filter(o=>!ny(o)).reduce((o,d)=>o+(Ip.test(d)?tg:d===""?lg:ag),s)}function ig(i,c){return i.length===c.length&&i.slice(0,-1).every((s,o)=>s===c[o])?i[i.length-1]-c[c.length-1]:0}function cg(i,c,f=!1){let{routesMeta:s}=i,o={},d="/",p=[];for(let E=0;E{if(O==="*"){let Y=E[N]||"";p=d.slice(0,d.length-Y.length).replace(/(.)\/+$/,"$1")}const B=E[N];return T&&!B?m[O]=void 0:m[O]=(B||"").replace(/%2F/g,"/"),m},{}),pathname:d,pathnameBase:p,pattern:i}}function Hy(i,c=!1,f=!0){nl(i==="*"||!i.endsWith("*")||i.endsWith("/*"),`Route path "${i}" will be treated as if it were "${i.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${i.replace(/\*$/,"/*")}".`);let s=[],o="^"+i.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(p,E,g,m,O)=>{if(s.push({paramName:E,isOptional:g!=null}),g){let T=O.charAt(m+p.length);return T&&T!=="/"?"/([^\\/]*)":"(?:/([^\\/]*))?"}return"/([^\\/]+)"}).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return i.endsWith("*")?(s.push({paramName:"*"}),o+=i==="*"||i==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):f?o+="\\/*$":i!==""&&i!=="/"&&(o+="(?:(?=\\/|$))"),[new RegExp(o,c?void 0:"i"),s]}function sg(i){try{return i.split("/").map(c=>decodeURIComponent(c).replace(/\//g,"%2F")).join("/")}catch(c){return nl(!1,`The URL path "${i}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${c}).`),i}}function Dl(i,c){if(c==="/")return i;if(!i.toLowerCase().startsWith(c.toLowerCase()))return null;let f=c.endsWith("/")?c.length-1:c.length,s=i.charAt(f);return s&&s!=="/"?null:i.slice(f)||"/"}function fg(i,c="/"){let{pathname:f,search:s="",hash:o=""}=typeof i=="string"?Xn(i):i,d;return f?(f=qy(f),f.startsWith("/")?d=uy(f.substring(1),"/"):d=uy(f,c)):d=c,{pathname:d,search:hg(s),hash:dg(o)}}function uy(i,c){let f=Mc(c).split("/");return i.split("/").forEach(o=>{o===".."?f.length>1&&f.pop():o!=="."&&f.push(o)}),f.length>1?f.join("/"):"/"}function cr(i,c,f,s){return`Cannot include a '${i}' character in a manually specified \`to.${c}\` field [${JSON.stringify(s)}]. Please separate it out to the \`to.${f}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function rg(i){return i.filter((c,f)=>f===0||c.route.path&&c.route.path.length>0)}function jy(i){let c=rg(i);return c.map((f,s)=>s===c.length-1?f.pathname:f.pathnameBase)}function _r(i,c,f,s=!1){let o;typeof i=="string"?o=Xn(i):(o={...i},qt(!o.pathname||!o.pathname.includes("?"),cr("?","pathname","search",o)),qt(!o.pathname||!o.pathname.includes("#"),cr("#","pathname","hash",o)),qt(!o.search||!o.search.includes("#"),cr("#","search","hash",o)));let d=i===""||o.pathname==="",p=d?"/":o.pathname,E;if(p==null)E=f;else{let T=c.length-1;if(!s&&p.startsWith("..")){let N=p.split("/");for(;N[0]==="..";)N.shift(),T-=1;o.pathname=N.join("/")}E=T>=0?c[T]:"/"}let g=fg(o,E),m=p&&p!=="/"&&p.endsWith("/"),O=(d||p===".")&&f.endsWith("/");return!g.pathname.endsWith("/")&&(m||O)&&(g.pathname+="/"),g}var qy=i=>i.replace(/[\\/]{2,}/g,"/"),ke=i=>qy(i.join("/")),Mc=i=>i.replace(/\/+$/,""),og=i=>Mc(i).replace(/^\/*/,"/"),hg=i=>!i||i==="?"?"":i.startsWith("?")?i:"?"+i,dg=i=>!i||i==="#"?"":i.startsWith("#")?i:"#"+i,mg=class{constructor(i,c,f,s=!1){this.status=i,this.statusText=c||"",this.internal=s,f instanceof Error?(this.data=f.toString(),this.error=f):this.data=f}};function yg(i){return i!=null&&typeof i.status=="number"&&typeof i.statusText=="string"&&typeof i.internal=="boolean"&&"data"in i}function vg(i){let c=i.map(f=>f.route.path).filter(Boolean);return ke(c)||"/"}var By=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u";function Qy(i,c){let f=i;if(typeof f!="string"||!Mr.test(f))return{absoluteURL:void 0,isExternal:!1,to:f};let s=f,o=!1;if(By)try{let d=new URL(window.location.href),p=_y.test(f)?new URL(Kp(f,d.protocol)):new URL(f),E=Dl(p.pathname,c);p.origin===d.origin&&E!=null?f=E+p.search+p.hash:o=!0}catch{nl(!1,` contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}return{absoluteURL:s,isExternal:o,to:f}}Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var Ly=["POST","PUT","PATCH","DELETE"];new Set(Ly);var pg=["GET",...Ly];new Set(pg);var gg=["about:","blob:","chrome:","chrome-untrusted:","content:","data:","devtools:","file:","filesystem:","javascript:"];function Sg(i){try{return gg.includes(new URL(i).protocol)}catch{return!1}}var Zn=M.createContext(null);Zn.displayName="DataRouter";var Uc=M.createContext(null);Uc.displayName="DataRouterState";var Yy=M.createContext(!1);function bg(){return M.useContext(Yy)}var Gy=M.createContext({isTransitioning:!1});Gy.displayName="ViewTransition";var Eg=M.createContext(new Map);Eg.displayName="Fetchers";var Tg=M.createContext(null);Tg.displayName="Await";var Ke=M.createContext(null);Ke.displayName="Navigation";var Pu=M.createContext(null);Pu.displayName="Location";var ul=M.createContext({outlet:null,matches:[],isDataRoute:!1});ul.displayName="Route";var Dr=M.createContext(null);Dr.displayName="RouteError";var wy="REACT_ROUTER_ERROR",Rg="REDIRECT",Og="ROUTE_ERROR_RESPONSE";function Ag(i){if(i.startsWith(`${wy}:${Rg}:{`))try{let c=JSON.parse(i.slice(28));if(typeof c=="object"&&c&&typeof c.status=="number"&&typeof c.statusText=="string"&&typeof c.location=="string"&&typeof c.reloadDocument=="boolean"&&typeof c.replace=="boolean")return c}catch{}}function zg(i){if(i.startsWith(`${wy}:${Og}:{`))try{let c=JSON.parse(i.slice(40));if(typeof c=="object"&&c&&typeof c.status=="number"&&typeof c.statusText=="string")return new mg(c.status,c.statusText,c.data)}catch{}}function Cg(i,{relative:c}={}){qt(Iu(),"useHref() may be used only in the context of a component.");let{basename:f,navigator:s}=M.useContext(Ke),{hash:o,pathname:d,search:p}=ti(i,{relative:c}),E=d;return f!=="/"&&(E=d==="/"?f:ke([f,d])),s.createHref({pathname:E,search:p,hash:o})}function Iu(){return M.useContext(Pu)!=null}function Ul(){return qt(Iu(),"useLocation() may be used only in the context of a component."),M.useContext(Pu).location}var Xy="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Zy(i){M.useContext(Ke).static||M.useLayoutEffect(i)}function Ky(){let{isDataRoute:i}=M.useContext(ul);return i?Gg():Mg()}function Mg(){qt(Iu(),"useNavigate() may be used only in the context of a component.");let i=M.useContext(Zn),{basename:c,navigator:f}=M.useContext(Ke),{matches:s}=M.useContext(ul),{pathname:o}=Ul(),d=JSON.stringify(jy(s)),p=M.useRef(!1);return Zy(()=>{p.current=!0}),M.useCallback((g,m={})=>{if(nl(p.current,Xy),!p.current)return;if(typeof g=="number"){f.go(g);return}let O=_r(g,JSON.parse(d),o,m.relative==="path");i==null&&c!=="/"&&(O.pathname=O.pathname==="/"?c:ke([c,O.pathname])),(m.replace?f.replace:f.push)(O,m.state,m)},[c,f,d,o,i])}M.createContext(null);function _g(){let{matches:i}=M.useContext(ul),c=i[i.length-1];return(c==null?void 0:c.params)??{}}function ti(i,{relative:c}={}){let{matches:f}=M.useContext(ul),{pathname:s}=Ul(),o=JSON.stringify(jy(f));return M.useMemo(()=>_r(i,JSON.parse(o),s,c==="path"),[i,o,s,c])}function Dg(i,c){return Vy(i,c)}function Vy(i,c,f){var L;qt(Iu(),"useRoutes() may be used only in the context of a component.");let{navigator:s}=M.useContext(Ke),{matches:o}=M.useContext(ul),d=o[o.length-1],p=d?d.params:{},E=d?d.pathname:"/",g=d?d.pathnameBase:"/",m=d&&d.route;{let q=m&&m.path||"";Fy(E,!m||q.endsWith("*")||q.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${E}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let O=Ul(),T;if(c){let q=typeof c=="string"?Xn(c):c;qt(g==="/"||((L=q.pathname)==null?void 0:L.startsWith(g)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${g}" but pathname "${q.pathname}" was given in the \`location\` prop.`),T=q}else T=O;let N=T.pathname||"/",B=N;if(g!=="/"){let q=g.replace(/^\//,"").split("/");B="/"+N.replace(/^\//,"").split("/").slice(q.length).join("/")}let Y=f&&f.state.matches.length?f.state.matches.map(q=>Object.assign(q,{route:f.manifest[q.route.id]||q.route})):Dy(i,{pathname:B});nl(m||Y!=null,`No routes matched location "${T.pathname}${T.search}${T.hash}" `),nl(Y==null||Y[Y.length-1].route.element!==void 0||Y[Y.length-1].route.Component!==void 0||Y[Y.length-1].route.lazy!==void 0,`Matched leaf route at location "${T.pathname}${T.search}${T.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let Q=jg(Y&&Y.map(q=>Object.assign({},q,{params:Object.assign({},p,q.params),pathname:ke([g,s.encodeLocation?s.encodeLocation(q.pathname.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:q.pathname]),pathnameBase:q.pathnameBase==="/"?g:ke([g,s.encodeLocation?s.encodeLocation(q.pathnameBase.replace(/%/g,"%25").replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:q.pathnameBase])})),o,f);return c&&Q?M.createElement(Pu.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",mask:void 0,...T},navigationType:"POP"}},Q):Q}function Ug(){let i=Yg(),c=yg(i)?`${i.status} ${i.statusText}`:i instanceof Error?i.message:JSON.stringify(i),f=i instanceof Error?i.stack:null,s="rgba(200,200,200, 0.5)",o={padding:"0.5rem",backgroundColor:s},d={padding:"2px 4px",backgroundColor:s},p=null;return console.error("Error handled by React Router default ErrorBoundary:",i),p=M.createElement(M.Fragment,null,M.createElement("p",null,"💿 Hey developer 👋"),M.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",M.createElement("code",{style:d},"ErrorBoundary")," or"," ",M.createElement("code",{style:d},"errorElement")," prop on your route.")),M.createElement(M.Fragment,null,M.createElement("h2",null,"Unexpected Application Error!"),M.createElement("h3",{style:{fontStyle:"italic"}},c),f?M.createElement("pre",{style:o},f):null,p)}var xg=M.createElement(Ug,null),Jy=class extends M.Component{constructor(i){super(i),this.state={location:i.location,revalidation:i.revalidation,error:i.error}}static getDerivedStateFromError(i){return{error:i}}static getDerivedStateFromProps(i,c){return c.location!==i.location||c.revalidation!=="idle"&&i.revalidation==="idle"?{error:i.error,location:i.location,revalidation:i.revalidation}:{error:i.error!==void 0?i.error:c.error,location:c.location,revalidation:i.revalidation||c.revalidation}}componentDidCatch(i,c){this.props.onError?this.props.onError(i,c):console.error("React Router caught the following error during render",i)}render(){let i=this.state.error;if(this.context&&typeof i=="object"&&i&&"digest"in i&&typeof i.digest=="string"){const f=zg(i.digest);f&&(i=f)}let c=i!==void 0?M.createElement(ul.Provider,{value:this.props.routeContext},M.createElement(Dr.Provider,{value:i,children:this.props.component})):this.props.children;return this.context?M.createElement(Ng,{error:i},c):c}};Jy.contextType=Yy;var sr=new WeakMap;function Ng({children:i,error:c}){let{basename:f}=M.useContext(Ke);if(typeof c=="object"&&c&&"digest"in c&&typeof c.digest=="string"){let s=Ag(c.digest);if(s){let o=sr.get(c);if(o)throw o;let d=Qy(s.location,f),p=d.absoluteURL||d.to;if(Sg(p))throw new Error("Invalid redirect location");if(By&&!sr.get(c))if(d.isExternal||s.reloadDocument)window.location.href=p;else{const E=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(d.to,{replace:s.replace}));throw sr.set(c,E),E}return M.createElement("meta",{httpEquiv:"refresh",content:`0;url=${p}`})}}return i}function Hg({routeContext:i,match:c,children:f}){let s=M.useContext(Zn);return s&&s.static&&s.staticContext&&(c.route.errorElement||c.route.ErrorBoundary)&&(s.staticContext._deepestRenderedBoundaryId=c.route.id),M.createElement(ul.Provider,{value:i},f)}function jg(i,c=[],f){let s=f==null?void 0:f.state;if(i==null){if(!s)return null;if(s.errors)i=s.matches;else if(c.length===0&&!s.initialized&&s.matches.length>0)i=s.matches;else return null}let o=i,d=s==null?void 0:s.errors;if(d!=null){let O=o.findIndex(T=>T.route.id&&(d==null?void 0:d[T.route.id])!==void 0);qt(O>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(d).join(",")}`),o=o.slice(0,Math.min(o.length,O+1))}let p=!1,E=-1;if(f&&s){p=s.renderFallback;for(let O=0;O=0?o=o.slice(0,E+1):o=[o[0]];break}}}}let g=f==null?void 0:f.onError,m=s&&g?(O,T)=>{var N,B;g(O,{location:s.location,params:((B=(N=s.matches)==null?void 0:N[0])==null?void 0:B.params)??{},pattern:vg(s.matches),errorInfo:T})}:void 0;return o.reduceRight((O,T,N)=>{let B,Y=!1,Q=null,L=null;s&&(B=d&&T.route.id?d[T.route.id]:void 0,Q=T.route.errorElement||xg,p&&(E<0&&N===0?(Fy("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),Y=!0,L=null):E===N&&(Y=!0,L=T.route.hydrateFallbackElement||null)));let q=c.concat(o.slice(0,N+1)),F=()=>{let X;return B?X=Q:Y?X=L:T.route.Component?X=M.createElement(T.route.Component,null):T.route.element?X=T.route.element:X=O,M.createElement(Hg,{match:T,routeContext:{outlet:O,matches:q,isDataRoute:s!=null},children:X})};return s&&(T.route.ErrorBoundary||T.route.errorElement||N===0)?M.createElement(Jy,{location:s.location,revalidation:s.revalidation,component:Q,error:B,children:F(),routeContext:{outlet:null,matches:q,isDataRoute:!0},onError:m}):F()},null)}function Ur(i){return`${i} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qg(i){let c=M.useContext(Zn);return qt(c,Ur(i)),c}function Bg(i){let c=M.useContext(Uc);return qt(c,Ur(i)),c}function Qg(i){let c=M.useContext(ul);return qt(c,Ur(i)),c}function xr(i){let c=Qg(i),f=c.matches[c.matches.length-1];return qt(f.route.id,`${i} can only be used on routes that contain a unique "id"`),f.route.id}function Lg(){return xr("useRouteId")}function Yg(){var s;let i=M.useContext(Dr),c=Bg("useRouteError"),f=xr("useRouteError");return i!==void 0?i:(s=c.errors)==null?void 0:s[f]}function Gg(){let{router:i}=qg("useNavigate"),c=xr("useNavigate"),f=M.useRef(!1);return Zy(()=>{f.current=!0}),M.useCallback(async(o,d={})=>{nl(f.current,Xy),f.current&&(typeof o=="number"?await i.navigate(o):await i.navigate(o,{fromRouteId:c,...d}))},[i,c])}var iy={};function Fy(i,c,f){!c&&!iy[i]&&(iy[i]=!0,nl(!1,f))}M.memo(wg);function wg({routes:i,manifest:c,future:f,state:s,isStatic:o,onError:d}){return Vy(i,void 0,{manifest:c,state:s,isStatic:o,onError:d})}function Rc(i){qt(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Xg({basename:i="/",children:c=null,location:f,navigationType:s="POP",navigator:o,static:d=!1,useTransitions:p}){qt(!Iu(),"You cannot render a inside another . You should never have more than one in your app.");let E=i.replace(/^\/*/,"/"),g=M.useMemo(()=>({basename:E,navigator:o,static:d,useTransitions:p,future:{}}),[E,o,d,p]);typeof f=="string"&&(f=Xn(f));let{pathname:m="/",search:O="",hash:T="",state:N=null,key:B="default",mask:Y}=f,Q=M.useMemo(()=>{let L=Dl(m,E);return L==null?null:{location:{pathname:L,search:O,hash:T,state:N,key:B,mask:Y},navigationType:s}},[E,m,O,T,N,B,s,Y]);return nl(Q!=null,` is not able to match the URL "${m}${O}${T}" because it does not start with the basename, so the won't render anything.`),Q==null?null:M.createElement(Ke.Provider,{value:g},M.createElement(Pu.Provider,{children:c,value:Q}))}function Zg({children:i,location:c}){return Dg(or(i),c)}function or(i,c=[]){let f=[];return M.Children.forEach(i,(s,o)=>{if(!M.isValidElement(s))return;let d=[...c,o];if(s.type===M.Fragment){f.push.apply(f,or(s.props.children,d));return}qt(s.type===Rc,`[${typeof s.type=="string"?s.type:s.type.name}] is not a component. All component children of must be a or `),qt(!s.props.index||!s.props.children,"An index route cannot have child routes.");let p={id:s.props.id||d.join("-"),caseSensitive:s.props.caseSensitive,element:s.props.element,Component:s.props.Component,index:s.props.index,path:s.props.path,middleware:s.props.middleware,loader:s.props.loader,action:s.props.action,hydrateFallbackElement:s.props.hydrateFallbackElement,HydrateFallback:s.props.HydrateFallback,errorElement:s.props.errorElement,ErrorBoundary:s.props.ErrorBoundary,hasErrorBoundary:s.props.hasErrorBoundary===!0||s.props.ErrorBoundary!=null||s.props.errorElement!=null,shouldRevalidate:s.props.shouldRevalidate,handle:s.props.handle,lazy:s.props.lazy};s.props.children&&(p.children=or(s.props.children,d)),f.push(p)}),f}var Oc="get",Ac="application/x-www-form-urlencoded";function xc(i){return typeof HTMLElement<"u"&&i instanceof HTMLElement}function Kg(i){return xc(i)&&i.tagName.toLowerCase()==="button"}function Vg(i){return xc(i)&&i.tagName.toLowerCase()==="form"}function Jg(i){return xc(i)&&i.tagName.toLowerCase()==="input"}function Fg(i){return!!(i.metaKey||i.altKey||i.ctrlKey||i.shiftKey)}function $g(i,c){return i.button===0&&(!c||c==="_self")&&!Fg(i)}var bc=null;function kg(){if(bc===null)try{new FormData(document.createElement("form"),0),bc=!1}catch{bc=!0}return bc}var Wg=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function fr(i){return i!=null&&!Wg.has(i)?(nl(!1,`"${i}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Ac}"`),null):i}function Pg(i,c){let f,s,o,d,p;if(Vg(i)){let E=i.getAttribute("action");s=E?Dl(E,c):null,f=i.getAttribute("method")||Oc,o=fr(i.getAttribute("enctype"))||Ac,d=new FormData(i)}else if(Kg(i)||Jg(i)&&(i.type==="submit"||i.type==="image")){let E=i.form;if(E==null)throw new Error('Cannot submit a )}{!matches.length&&

No matching command.

}

run esc close

} +function Shell({children}:{children:React.ReactNode}){const where=useLocation(),nav=useNavigate(),[palette,setPalette]=useState(false);const overview=useQuery({queryKey:['overview'],queryFn:api.overview,refetchInterval:5000});const sessions=overview.data?.sessions.length??0;useEffect(()=>{const key=(e:KeyboardEvent)=>{if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==='k'){e.preventDefault();setPalette(true)}if(e.key==='n'&&!e.metaKey&&!e.ctrlKey&&!(e.target instanceof HTMLInputElement)&&!(e.target instanceof HTMLTextAreaElement)){nav('/');window.dispatchEvent(new Event('orchestra:new-task'))}};window.addEventListener('keydown',key);return()=>window.removeEventListener('keydown',key)},[nav]);return
Orchestra {where.pathname==='/'?'dispatch board':where.pathname==='/workers'?'worker pool':'task record'}
SESSIONS {sessions}SYNC 5s
{children}{palette&&setPalette(false)}/>}
} +function Board({tasks}:{tasks:Task[]}){return
{states.map(state=>{const lane=tasks.filter(t=>t.state===state);return
{label[state]}{lane.length}
{lane.map(t=>{t.title||t.id}{t.project}{t.id.slice(-5)})}{!lane.length&&

Nothing here. New work will appear in this branch.

}
})}
} +function Overview(){const q=useQuery({queryKey:['overview'],queryFn:api.overview,refetchInterval:5000}),[createOpen,setCreateOpen]=useState(false);useEffect(()=>{const open=()=>setCreateOpen(true);window.addEventListener('orchestra:new-task',open);return()=>window.removeEventListener('orchestra:new-task',open)},[]);if(q.isLoading)return

Dispatch board

Fetching queue state from the coordinator…

;if(q.error)return
Queue unavailable: {String(q.error)}
;const d=q.data!,active=d.tasks.filter(t=>t.state==='leased').length,attention=d.tasks.filter(t=>t.state==='blocked'||t.state==='failed').length,approvals=new Set(d.sessions.filter(s=>s.pending_approval).map(s=>s.capture?.task_id));return

Agent dispatch

Keep the work moving.

Live task state across every connected harness. The board refreshes every five seconds.

Tasks{d.tasks.length}
In session{active}
Blocked or failed{attention}
Awaiting approval{approvals.size}

Task flow

SYNCED {new Date(d.updated_at).toLocaleTimeString()}
{createOpen&&setCreateOpen(false)}/>}
} +function Create({close}:{close:()=>void}){const qc=useQueryClient(),nav=useNavigate(),first=useRef(null),[title,setTitle]=useState(''),[description,setDescription]=useState(''),[project,setProject]=useState('default'),[capability,setCapability]=useState(''),[advanced,setAdvanced]=useState(''),[formError,setFormError]=useState('');useEffect(()=>{first.current?.focus()},[]);const m=useMutation({mutationFn:()=>{let extra:Record={};if(advanced.trim()){try{extra=JSON.parse(advanced)}catch{return Promise.reject(new Error('Additional fields must be valid JSON.'))}}return api.create({...extra,source:'web',external_id:crypto.randomUUID(),project,title,description,capability:capability.split(',').map(x=>x.trim()).filter(Boolean)})},onSuccess:(e:any)=>{qc.invalidateQueries({queryKey:['overview']});nav('/tasks/'+e.task_id)}});const submit=(e:React.FormEvent)=>{e.preventDefault();setFormError('');if(title.trim().length<3){setFormError('Give the task a title of at least three characters.');return}if(description.trim().length<10){setFormError('Add enough immutable instructions for an agent to act safely.');return}m.mutate()};return
e.stopPropagation()}>

Dispatch new work

New task

Give the harness a clear objective and immutable operating instructions.