A single Go binary that grok dials out to over a WebSocket, and a HeroUI web UI for driving the session it is attached to. The roles are inverted relative to the terminal: over the /rc link grok is the ACP Agent and glance is the Client. That makes glance a stock ACP client and the web Stop button a real session/cancel rather than a bespoke control message. Both notification rails are mirrored. The stable session/update rail carries correctness; x.ai/session_notification is presentation only and degrades rather than erroring, because its ~60 variants are grok internal and drift with every upstream sync. _meta is forwarded byte for byte so viewers can dedup and order. Permissions race: the terminal and any browser may answer, first responder wins, and the loser's UI retracts by itself. All three interaction methods go through that path, not just permissions. Auth is TOTP only, with no accounts to have. A bootstrap token printed at first start gates /setup, which is a 404 without it; state lives in one 0600 JSON file and history in an in-memory ring, so there is no database and no recovery story beyond deleting the file. ARCHITECTURE.md covers the topology and the limits of that auth model; CLAUDE.md covers building, the fakeagent loop, and the end-to-end checklist that unit tests cannot replace. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
34 lines
1.0 KiB
Go
34 lines
1.0 KiB
Go
// Package web carries the built frontend into the binary.
|
|
//
|
|
// The embed directive points at `dist/`, which is Vite's output. That directory
|
|
// is checked in with only a `.gitkeep` so a clean clone still compiles: Go
|
|
// resolves `//go:embed` at build time and would fail outright on a missing path,
|
|
// which would mean `go build ./...` could not run until someone had installed
|
|
// npm. Assets returns an error in that state instead, and the server falls back
|
|
// to a placeholder page telling the operator to run `make web`.
|
|
package web
|
|
|
|
import (
|
|
"embed"
|
|
"errors"
|
|
"io/fs"
|
|
)
|
|
|
|
//go:embed all:dist
|
|
var embedded embed.FS
|
|
|
|
// ErrNotBuilt means the binary was built without running the frontend build.
|
|
var ErrNotBuilt = errors.New("web UI not built; run `make web`")
|
|
|
|
// Assets returns the frontend rooted at index.html.
|
|
func Assets() (fs.FS, error) {
|
|
dist, err := fs.Sub(embedded, "dist")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := fs.Stat(dist, "index.html"); err != nil {
|
|
return nil, ErrNotBuilt
|
|
}
|
|
return dist, nil
|
|
}
|