hooks

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

hook-forward subcommand implementation. Agent CLIs (Claude/Codex/Grok) invoke this from their settings.json hooks config; it reads the hook payload from stdin, attaches the per-install secret, and POSTs to the daemon's localhost ingest endpoint.

Replaces Mac's Node.js hook script (~/.rmote/hooks/rmote-session-sync.cjs) with a single static binary subcommand — no Node dependency on the host, which matters for minimal Linux images and Alpine.

Hook configuration is installed by the settings patcher; this package ships the subcommand itself so manual installs work end-to-end.

Package hooks is the daemon's in-memory event bus for agent hook events.

Hook events arrive via POST /api/hooks/{agent} (called by the `rmoted hook-forward` subcommand which agent CLIs invoke from their settings.json hooks config). The bus broadcasts to all subscribers — iOS AgentDaemonClient subscribes via GET /api/agent/events long-poll and filters by its scraped agent_session_id.

This is dumb fan-out: the daemon does not infer which client tab owns a session. Clients perform per-tab attribution from the agent session ID.

Drop-oldest semantics on overflow: metadata events are low-throughput (a few per agent turn), but a misbehaving hook script could flood. Cap per-subscriber buffer at 16; lost events surface as a "lagged" warning in a client (currently log only; clients can resync via /api/sessions).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HookForward

func HookForward(agentName, event string, stdin io.Reader, stdout io.Writer) (err error)

HookForward is the entry point for the `rmoted hook-forward` CLI subcommand. agentName is the CLI arg (--agent claude/codex/grok); stdin is the hook payload (Claude passes JSON, Codex/Grok may pass JSON too).

Returns nil (exit 0) when there is nothing to forward OR the daemon is absent — hook-forward is fire-and-forget and a stopped/never-installed daemon is a normal user state, not an error worth polluting the agent CLI's stderr on every invocation. Genuine failures (bad request build, daemon returned 4xx/5xx, transport timeout mid-handshake) still return non-nil so the agent CLI logs them.

func ParseAgentFlag

func ParseAgentFlag(args []string) string

ParseAgentFlag extracts --agent VALUE from a CLI arg slice. Returns "" if absent. Used by main.go to dispatch `rmoted hook-forward --agent X`.

func ParseEventFlag

func ParseEventFlag(args []string) string

ParseEventFlag extracts --event VALUE from a CLI arg slice. Returns "" if absent. Used by Antigravity's per-event hook commands to tell hook-forward which event fired (its payload carries no event-name field).

Types

type Bus

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

Bus is a fan-out broadcast bus. Goroutine-safe via a single mutex; the critical sections are tiny (map ops) so contention is negligible at the daemon's expected scale (1-10 subscribers per host).

func NewBus

func NewBus() *Bus

func (*Bus) Publish

func (b *Bus) Publish(e Event)

Publish fans out to every subscriber. Slow subscribers (buffer full) silently drop the oldest event via the select-default below. This is intentional — a stuck iOS tab must not block the daemon's hook ingest.

func (*Bus) Subscribe

func (b *Bus) Subscribe() chan Event

Subscribe returns a buffered channel. Caller MUST call Unsubscribe when done to avoid leaking the channel (and the buffer). Long-poll handler pairs these in a defer.

func (*Bus) Unsubscribe

func (b *Bus) Unsubscribe(ch chan Event)

type Event

type Event struct {
	Event          string   `json:"event"`
	SessionID      string   `json:"session_id,omitempty"`
	RmoteSessionID string   `json:"rmote_session_id,omitempty"`
	Title          string   `json:"title,omitempty"`
	Body           string   `json:"body,omitempty"`
	ToolName       *string  `json:"tool_name,omitempty"`
	EventAt        *float64 `json:"event_at,omitempty"`
	Project        *string  `json:"project,omitempty"`
	Agent          string   `json:"agent"`
	Timestamp      int64    `json:"timestamp"`
}

Event is the wire shape for both POST /api/hooks/{agent} request body and GET /api/agent/events response. Field names mirror iOS's NotifyPayload so iOS can reuse its decoder regardless of transport.

The daemon sets Agent (from the URL path) and Timestamp (server time, milliseconds since epoch, for "since" long-poll filtering). All other fields come from the agent CLI's hook payload.

Two session-identifier fields exist because they identify DIFFERENT things:

  • SessionID: the agent CLI's own session UUID (Claude's from ~/.claude/projects, Codex's from ~/.codex/sessions). iOS scrapes this from terminal output to match hook events to tabs.
  • RmoteSessionID: the daemon's tab UUID (injected via RMOTE_SESSION_ID env at PTY spawn). Lets iOS match events to tabs WITHOUT scraping — load-bearing for backgrounded tabs (C2 regression) where the scrape suspends. Populated by hook-forward reading $RMOTE_SESSION_ID.

Jump to

Keyboard shortcuts

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