web

package
v0.684.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: AGPL-3.0 Imports: 50 Imported by: 0

Documentation

Overview

Marauder synth-panel WebSocket layer.

Owns the marauder_acquire / marauder_release / marauder_cmd WS frames and the command registry that maps a stable client-side key to a Marauder CLI command + parser + event kind. See SPEC.md §3 for the architecture and §3.4 for the registry table; the registry below is the authoritative copy in code.

Threading rules:

  • marauderMu guards marauderHolder, marauderCancel, marauderRunning.
  • marauderActive (atomic) is the fast-path "is the panel held" flag.
  • All emits go through s.sendTo / s.broadcast (the same writer pipeline as every other JSON frame).
  • Stream goroutines watch ctx for cancellation and close the Marauder `done` channel on exit; the package's Stream implementation sends `stopscan` to the device when that channel is closed.

Package web serves the PromptZero browser UI and bridges the agent's streaming callbacks onto a WebSocket.

Event model

The agent exposes three hooks — SetTextDeltaCallback, SetToolStatusCallback, SetConfirmCallback — that fire from the goroutine running agent.Run. The Server registers one adapter per hook and routes each event to the connections through per-conn writer goroutines. Callbacks must not touch the WebSocket directly: concurrent writes are undefined.

Session isolation is by single-writer mutex. The agent has one slot per callback and its own internal lock, so it cannot genuinely host parallel sessions. Server.driverMu serialises Run invocations; the first connection to send a `text` drives the turn, others block until it finishes. Events are broadcast to every open connection tagged with `turn_id` (plus the owner's `session_id` on the initial status frame) so peer tabs stay in sync without fighting for control. `confirm_request` is the single exception — it is delivered only to the turn owner.

Liveness uses WebSocket protocol-level ping/pong (ws.Ping), which the browser answers below the JS event loop — a backgrounded tab whose timers are throttled still responds. The JSON taxonomy below is strictly application payload; there are no `ping`/`pong` JSON frames.

Outbound taxonomy: status, response, transcription, error (legacy), text_delta, tool_status, confirm_request, phase. Inbound taxonomy: text, audio, reset (legacy), confirm_response, cancel.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(addr string, ag agentDriver, v *voice.Engine) *Server

NewServer creates a web server bound to addr. If the host portion of addr is empty (":PORT") or the legacy hardcoded "0.0.0.0", the server defaults the bind to "127.0.0.1" and prints a one-line note to stderr explaining how to override via config.Web.Host. If the effective host is non-loopback, NewServer additionally prints a yellow warning on stderr: the web UI has no authentication, so a public bind must be explicit and visible.

func (*Server) Addr

func (s *Server) Addr() string

Addr returns the effective host:port the server will bind to, after any loopback-default rewrite applied in NewServer. Use this for display so the "Web UI at ..." status line matches the actual socket.

func (*Server) OnUIContext added in v0.9.1

func (s *Server) OnUIContext(fn func(view, path string))

OnUIContext installs a callback invoked whenever a ui_context WebSocket frame arrives. Use this to forward the navigation state to the agent.

func (*Server) SetAllowAnyOrigin added in v0.2.0

func (s *Server) SetAllowAnyOrigin(v bool)

SetAllowAnyOrigin opts in to wildcard Origin matching for cross-origin WebSocket connections. Pairs with SetCORSOrigins: the allow-list must NOT contain "*" while this flag is set — the combination exists only so the operator has to remove the footgun token from config as part of enabling it. Must be called before Start.

func (*Server) SetAllowUnauthedPublic added in v0.2.5

func (s *Server) SetAllowUnauthedPublic(v bool)

SetAllowUnauthedPublic opts in to warn-and-continue when the server is bound non-loopback without an auth token. When false (default) Start returns an error in that configuration. Must be called before Start.

func (*Server) SetAuditLog added in v0.9.1

func (s *Server) SetAuditLog(l *audit.Log)

SetAuditLog wires the audit log so destructive FS and input-send operations are recorded. Safe to pass nil — operations are skipped silently without one.

func (*Server) SetAuthToken added in v0.2.0

func (s *Server) SetAuthToken(t string)

SetAuthToken installs the shared bearer token for /api and /ws. Empty disables the check (dev-mode default). Must be called before Start — changing the token at runtime would leave open connections with stale credentials.

func (*Server) SetBridgeMode added in v0.10.0

func (s *Server) SetBridgeMode(active bool, reason string)

SetBridgeMode records that the Flipper has been suspended for USB-UART bridge mode (Marauder stacked on Flipper GPIO header). The reason string is operator-visible and surfaces both in /status and in the /api/device JSON's bridge block (where the cockpit picks it up for the suspended-Flipper pill / "via Flipper bridge" Marauder subtitle).

active=false clears the stored reason so a "released" snapshot never carries the previous bridge's text. The (active, reason) pair is published as one atomic.Pointer.Store so readers always see a consistent snapshot.

func (*Server) SetCORSOrigins added in v0.2.0

func (s *Server) SetCORSOrigins(origins []string)

SetCORSOrigins sets the WebSocket Origin allow-list. Empty = same-origin only. Must be called before Start. A literal "*" entry is refused at Start (callers that really want wildcard semantics must drop "*" and set SetAllowAnyOrigin(true) instead).

func (*Server) SetCostTracker added in v0.2.0

func (s *Server) SetCostTracker(t *cost.Tracker)

SetCostTracker wires the session cost tracker into the server so the header cost pill and /api/cost handler can render live totals.

func (*Server) SetFlipper added in v0.2.0

func (s *Server) SetFlipper(f *flipper.Flipper)

SetFlipper wires the live *flipper.Flipper into the server so /api/device can run device_info + power_info and surface the full Momentum-level profile to the web UI. Safe to pass nil — /api/device returns 503 until this is set.

func (*Server) SetFlipperConnected added in v0.2.0

func (s *Server) SetFlipperConnected(v bool)

SetFlipperConnected records the current Flipper serial state for the /api/debug snapshot. Call on connect/disconnect transitions.

func (*Server) SetFlipperRPC added in v0.9.2

func (s *Server) SetFlipperRPC(p flipperRPCProvider)

SetFlipperRPC overrides the RPC provider used for screen-stream acquisition. Call after SetFlipper when the concrete *flipper.Flipper does not yet implement EnterRPC (useful in tests and during the parallel-development window before the rpc package lands).

func (*Server) SetMarauder added in v0.14.0

func (s *Server) SetMarauder(m marauderClient)

SetMarauder wires the Marauder serial client. The synth-panel WS handlers route Exec / Stream calls through this. Pass nil to clear the reference (the handlers refuse with `marauder_error/no_device`).

*marauder.Marauder satisfies the marauderClient interface; tests inject a fake without opening a real port.

func (*Server) SetMarauderConnected added in v0.2.0

func (s *Server) SetMarauderConnected(v bool)

SetMarauderConnected records the current Marauder serial state for the /api/debug snapshot. Call on connect/disconnect transitions.

func (*Server) SetMarauderInfo added in v0.9.0

func (s *Server) SetMarauderInfo(port, firmware string)

SetMarauderInfo records the Marauder serial port name (e.g. "/dev/ttyACM1") and firmware version string for the /api/device status-bar pill. Either argument may be empty when the host doesn't know that field — the status bar renders empty strings as "—".

Decoupled from SetMarauderConnected because the connect/disconnect callback fires on every transport event; the descriptive metadata is only known once at setup time (port from config, firmware from a one-shot "info" probe the host may add later).

func (*Server) SetMaxUploadBytes added in v0.9.1

func (s *Server) SetMaxUploadBytes(n int64)

SetMaxUploadBytes sets the upload size cap for /api/fs/upload. Default is 1 MiB. Must be called before Start.

func (*Server) SetMetrics

func (s *Server) SetMetrics(rec *obs.Recorder, path string)

SetMetrics wires a Prometheus Recorder onto the server. When non-nil the server mounts the scrape handler at path (or "/metrics" when path is empty). Must be called before Start.

func (*Server) SetPersonaRegistry added in v0.2.0

func (s *Server) SetPersonaRegistry(r *persona.Registry)

SetPersonaRegistry wires the persona catalogue into the server so /api/personas can list choices and /api/personas/switch can apply one. Safe to pass nil — the endpoints return 503 until a registry is set.

func (*Server) SetRulesEngine added in v0.2.0

func (s *Server) SetRulesEngine(e *rules.Engine)

SetRulesEngine wires the reactive-rules engine into the server so /api/rules can list, pause, resume, and test rule fires.

func (*Server) SetSessionDriver added in v0.12.0

func (s *Server) SetSessionDriver(d sessionDriver)

SetSessionDriver wires the persisted-session surface so /api/sessions can list, resume, rename, and delete entries from the on-disk store. Pass *agent.Agent (it satisfies sessionDriver). Nil unsets the driver — every /api/sessions* endpoint then returns 503 and the sidebar hides itself.

func (*Server) SetUIContext added in v0.9.1

func (s *Server) SetUIContext(view, path string)

SetUIContext records the latest UI navigation state forwarded from the browser.

func (*Server) SetValidateBase added in v0.2.0

func (s *Server) SetValidateBase(dir string)

SetValidateBase restricts /api/validate path reads to paths rooted under dir. The value is normalised to its symlink-resolved absolute form; an empty string (the default) disables path-based reads entirely so the endpoint 403s any request that isn't an inline `content` payload.

Must be called before Start. Callers wanting the "no filesystem reads" default simply never call this.

func (*Server) SetWatcher added in v0.2.0

func (s *Server) SetWatcher(w *watch.Watcher)

SetWatcher wires the filesystem watcher into the server so /api/watch can surface its configured rules, recent events, and paused state.

func (*Server) SetWebhooks added in v0.101.0

func (s *Server) SetWebhooks(wh webhook.Dispatcher)

SetWebhooks wires the outbound webhook dispatcher so /api/webhooks can surface configured subscriptions and recent delivery results. Pass nil to disable — the endpoint then returns 503 and the cockpit hides the webhooks panel.

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

func (*Server) UIContext added in v0.9.1

func (s *Server) UIContext() (view, path string)

UIContext returns the latest view+path the browser reported, or empty strings when no ui_context frame has arrived yet.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL