agentbbs/internal/plugin/plugin.go
Anthony Ettinger 362b47fdde
Feat/members messaging (#29)
* fix(deploy): build Go binaries on the runner, ship them, SKIP_BUILD on box

The deploy SSHed into the ~458MB droplet and ran `go build` there. The Go
linker's peak memory OOM-killed the build — and with it the sshd serving the
deploy session — surfacing as "Connection closed by remote host" (exit 255).
It was flaky because it tracked momentary memory pressure from the co-resident
ergo/forgejo/tor/podman/agentbbs processes (run #25 passed, #26 failed on
near-identical code).

Build both binaries on the 16GB GitHub runner instead (pure-Go, modernc
sqlite, so CGO_ENABLED=0 static cross-build), scp them to the droplet, and run
setup.sh with SKIP_BUILD=1 so the box never compiles. Arch is detected from
the droplet so amd64/arm64 both work. setup.sh now also skips the Go toolchain
download when SKIP_BUILD=1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(members): member directory + store-and-forward messaging

A members-only hub plugin (the BBS "who") plus user-to-user messaging:

- internal/store: messages table + SendMessage/Inbox/UnreadCount/MarkRead, and
  OnlineUsers (open sessions) for presence. MarkRead is recipient-scoped so a
  member can only clear their own mail.
- plugins/members: directory with online dots + last-seen, a finger-style
  profile view, a minimal compose box, and an inbox that marks read on open.
- ssh msg@host <user> [text]: scriptable CLI to leave a note (body from args or
  stdin), mirroring the existing finger route; "msg"/"message" are reserved.
- hub: "N unread" badge on login (hubMOTD). plugin.Context gains Host for member
  homepage URLs.

Extends the existing finger@ behavior (ssh <name>@host) rather than replacing it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:48:07 -07:00

48 lines
1.6 KiB
Go

// Package plugin defines the AgentBBS plugin contract (PRD §4.3).
//
// A plugin is one interface implementation plus one registration in the hub.
// Plugins return control to the hub by emitting ExitMsg, never by quitting
// the session.
package plugin
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/profullstack/agentbbs/internal/auth"
"github.com/profullstack/agentbbs/internal/sandbox"
"github.com/profullstack/agentbbs/internal/store"
)
// Context carries the shared services a plugin may use.
type Context struct {
Store store.Store
Sandbox *sandbox.Runner
// DataDir is the per-user persistent directory (members/agents only;
// empty for guests).
DataDir string
// AssetsDir is the read-only platform assets tree (wads, binaries).
AssetsDir string
// Host is the BBS hostname (e.g. bbs.profullstack.com), for building
// member homepage URLs (https://Host/~name) and similar links.
Host string
}
// Plugin is the only integration point between a feature and the hub.
type Plugin interface {
// ID is a stable unique identifier, e.g. "arcade".
ID() string
// Title is the hub menu label.
Title() string
// Description is a one-line summary shown in the menu.
Description() string
// RequiresAuth reports whether guests are admitted.
RequiresAuth() bool
// New returns a fresh Bubble Tea model for one session.
New(user auth.User, ctx Context) tea.Model
}
// ExitMsg is emitted by a plugin model to hand the session back to the hub.
type ExitMsg struct{}
// Exit is a convenience command for plugins.
func Exit() tea.Msg { return ExitMsg{} }