Documentation
¶
Overview ¶
Package cliproto holds the contracts shared by the CLI, daemon, server, and desktop: error/exit codes, stdout printers, and IPC types.
Everything in this package mirrors docs/ERRORS.md and docs/WIRE.md. If you change a string here, change those docs too — and the test fixtures.
Index ¶
- Constants
- func ExitCode(c Code) int
- func FormatReadMessage(w io.Writer, m ReadMessage, loc *time.Location)
- func FprintError(w io.Writer, err *Error)
- func HTTPStatus(c Code) int
- func IsTabularReadPipe(pipe string) bool
- func Message(c Code) string
- func PrintBroadcast(w io.Writer, r BroadcastReply)
- func PrintConnect(w io.Writer, r ConnectReply)
- func PrintCreate(w io.Writer, r CreateReply)
- func PrintDaemonAlreadyRunning(w io.Writer, pid int)
- func PrintDaemonStarted(w io.Writer, pid int)
- func PrintDisconnect(w io.Writer)
- func PrintList(w io.Writer, sources []Source, iso bool)
- func PrintListJSON(w io.Writer, sources []Source)
- func PrintLogin(w io.Writer, r LoginReply)
- func PrintOrgList(w io.Writer, orgs []OrgInfo, color bool)
- func PrintPipeCreate(w io.Writer, r PipeCreateReply)
- func PrintPipeDestroy(w io.Writer, r PipeDestroyReply)
- func PrintSourceDestroy(w io.Writer, r SourceDestroyReply)
- func PrintStatus(w io.Writer, s StatusReply)
- func PrintStatusWithEnv(w io.Writer, s StatusReply, envCurrent, currentJsonPath string, color bool)
- func PrintStatusWithEnvAndCLIVersion(w io.Writer, s StatusReply, envCurrent, currentJsonPath string, color bool, ...)
- func PrintSwitch(w io.Writer, r SwitchReply)
- func RelativeTime(t, now time.Time) string
- func RenderTerminal(in []byte, cols, rows int) string
- func TruncatePayload(s string) string
- type AuthExchangeReply
- type AuthExchangeRequest
- type BroadcastBatchReply
- type BroadcastBatchRequest
- type BroadcastReply
- type BroadcastRequest
- type Code
- type ConnectReply
- type ConnectRequest
- type CreateInviteReply
- type CreateInviteRequest
- type CreateOrgReply
- type CreateOrgRequest
- type CreateReply
- type CreateRequest
- type CreateSourceReply
- type CreateSourceRequest
- type DiagEvent
- type DiagReply
- type DiagRequest
- type DisconnectReply
- type DisconnectRequest
- type Error
- func New(c Code) *Error
- func NewInvalidHandle(handle string) *Error
- func NewInvalidPipeName(name string) *Error
- func NewInvalidPipeReserved(name string) *Error
- func NewPipeNotFound(pipe, handle string) *Error
- func NewPipeTaken(pipe, handle string) *Error
- func NewSourceNotFound(handle string) *Error
- func NewSourceTaken(handle string) *Error
- type HTTPError
- type Invite
- type ListInvitesReply
- type ListOrgsReply
- type ListReply
- type ListRequest
- type ListSourcesReply
- type ListWatchRequest
- type LoginReply
- type LoginRequest
- type OrgCreateRequest
- type OrgInfo
- type OrgInviteRequest
- type OrgSwitchReply
- type OrgSwitchRequest
- type PipeCreateReply
- type PipeCreateRequest
- type PipeDestroyReply
- type PipeDestroyRequest
- type PipeInfo
- type ReadEvent
- type ReadMessage
- type ReadMeta
- type ReadRequest
- type Source
- type SourceDestroyReply
- type SourceDestroyRequest
- type SourceKind
- type StatusReply
- type StatusRequest
- type SwitchReply
- type SwitchRequest
Constants ¶
const ( DefaultRenderCols = 200 DefaultRenderRows = 60 )
Default emulator dimensions for peek/read text rendering. Wide enough that most TUIs (Claude Code, htop, vim) won't reflow text awkwardly; tall enough to fit a typical scrollback target. If a wrap session ran at narrower dimensions, its cursor-positioning bytes still land at the right (col, row) inside this larger grid — they just leave more trailing whitespace.
Configurable later via flags / env / per-source metadata; hardcoded for now is fine since the goal is "more readable than nothing" rather than "byte-perfect replica of the original session".
const ( IPCStatus = "Status" IPCLogin = "Login" IPCCreate = "Create" IPCSwitch = "Switch" IPCBroadcast = "Broadcast" IPCBroadcastBatch = "BroadcastBatch" IPCList = "List" IPCListWatch = "ListWatch" IPCSubscribe = "Subscribe" IPCRead = "Read" IPCConnect = "Connect" IPCDisconnect = "Disconnect" IPCPipeCreate = "PipeCreate" IPCPipeDestroy = "PipeDestroy" IPCSourceDestroy = "SourceDestroy" // Org / invite verbs (Phase 4 — multi-org support). IPCOrgList = "OrgList" IPCOrgSwitch = "OrgSwitch" IPCOrgCreate = "OrgCreate" IPCOrgInvite = "OrgInvite" // Diag verb (Phase 0 — agent hardening). Returns the daemon's // recent NATS connection-state events for `ppz diag`. Works // without credentials and without a live NATS connection — the // whole point is being able to introspect a sick daemon. IPCDiag = "Diag" )
IPC method names. Keep in sync with WIRE.md §7.
const ( LoginCheckOK = "ok" LoginCheckInvalid = "invalid" )
LoginCheck values reported by StatusReply. Constants live in cliproto so both daemon (writer) and CLI (reader) share them without import cycles. "" is the zero value and means "not applicable" (e.g. no credentials stored).
Variables ¶
This section is empty.
Functions ¶
func ExitCode ¶
ExitCode returns the integer the CLI exits with for a given Code. Unknown codes map to 1 (generic error).
func FormatReadMessage ¶ added in v0.24.0
func FormatReadMessage(w io.Writer, m ReadMessage, loc *time.Location)
FormatReadMessage renders one ReadMessage in the tabular default. Layout:
HH:MM:SS <sender-col> <body>
<continuation>
<continuation>
Body shape:
- Subject starting with "ack:" → "<subject> → <last-8-hex-of-id>" (system protocol message — payload is ignored for display).
- Subject otherwise non-empty → "[<subject>] <payload-line-1>".
- Subject empty → "<payload-line-1>".
Multi-line payloads emit subsequent lines indented under the body column (no time, no sender). loc controls the timezone of HH:MM:SS — production callers pass time.Local.
func FprintError ¶
FprintError writes the standard one-line error to stderr.
func HTTPStatus ¶
HTTPStatus returns the HTTP status code for a given Code, or 500 if unknown.
func IsTabularReadPipe ¶ added in v0.24.0
IsTabularReadPipe reports whether `ppz read <handle>.<pipe>` should render in the v0.23 three-column tabular form by default. Only the human-visible inbox-shaped pipes do — pty pipes (stdout / stdin / stdctrl) and user-named custom pipes stay byte-faithful so replays work and arbitrary tooling on top doesn't get reflowed bytes.
func PrintBroadcast ¶
func PrintBroadcast(w io.Writer, r BroadcastReply)
func PrintConnect ¶
func PrintConnect(w io.Writer, r ConnectReply)
func PrintCreate ¶
func PrintCreate(w io.Writer, r CreateReply)
func PrintDaemonStarted ¶
func PrintDisconnect ¶
func PrintList ¶
PrintList renders sources as an aligned table with a header row. Default time format is relative duration ("5 minutes ago" / "just now"); pass iso=true for RFC3339 timestamps in the LAST column instead.
The PAYLOAD column is the last — it isn't padded, so a long preview extending past the alignment grid doesn't break the row count.
func PrintListJSON ¶
PrintListJSON emits one JSON object per (source, pipe) row in the same shape as the API + a `last_at` ISO string. Full untruncated payload — `ppz ls` is the only path that surfaces the latest payload without going through `ppz read`, so agents reading --json get the real bytes.
func PrintLogin ¶
func PrintLogin(w io.Writer, r LoginReply)
func PrintOrgList ¶ added in v0.22.0
PrintOrgList renders `ppz org list` as an aligned NAME/ROLE table — same column-padding style as `ppz ls`. The org the daemon is currently bound to (Current=true) gets a "(current)" suffix in green when colour is enabled.
Empty input → empty output (no orphan header), matching the same convention as PrintList.
func PrintPipeCreate ¶
func PrintPipeCreate(w io.Writer, r PipeCreateReply)
PrintPipeCreate prints the pinned line:
created pipe=<H>.<N> retention=ttl=<dur>,msgs=<n>,bytes=<b>
`dur` is rendered via time.Duration's String() so 24h/168h round-trip without manual zero-pad. `bytes` is the raw integer.
func PrintPipeDestroy ¶
func PrintPipeDestroy(w io.Writer, r PipeDestroyReply)
func PrintSourceDestroy ¶ added in v0.21.0
func PrintSourceDestroy(w io.Writer, r SourceDestroyReply)
func PrintStatus ¶
func PrintStatus(w io.Writer, s StatusReply)
func PrintStatusWithEnv ¶
func PrintStatusWithEnv(w io.Writer, s StatusReply, envCurrent, currentJsonPath string, color bool)
PrintStatusWithEnv prints `ppz status`. Four states map to the top `daemon:` line; the rest of the body depends on which state we're in:
- not running: just the one line.
- not logged in: pid + a hint pointing at `ppz login`.
- authentication error: pid + server URL + a hint to refresh the credential. The daemon learned this from a server call returning E_INVALID_API_KEY.
- logged in: pid + server URL + org name + current source.
envCurrent / currentJsonPath are the CLI-side facts surfaced for the env-vs-daemon disagreement annotation (see source-current-env-precedence for the contract). Only consulted when we're in the "logged in" state.
color toggles ANSI colour escapes. The CLI flips it on for interactive stdout and off for pipes / NO_COLOR / e2e fixtures.
func PrintStatusWithEnvAndCLIVersion ¶ added in v0.19.0
func PrintSwitch ¶
func PrintSwitch(w io.Writer, r SwitchReply)
func RelativeTime ¶
RelativeTime renders the gap from t→now as a coarse human duration: "just now" / "N seconds ago" / "N minutes ago" / "N hours ago" / "N days ago". Used in `ppz ls` output and the server GUI org page so operators see freshness at a glance without parsing absolute timestamps. Negative durations (clock skew, future timestamps) clamp to "just now".
func RenderTerminal ¶
RenderTerminal feeds raw PTY bytes through a virtual terminal of the given dimensions, then dumps the resulting screen as plain-text rows.
The bytes MUST be a single contiguous buffer — vt10x's `Write` is UTF-8-boundary-sensitive and can lose intermediate bytes if a multi- byte rune straddles two `Write` calls. Callers feeding chunked input (multiple JetStream messages, multiple PTY reads, etc.) should concatenate first, then call this once.
Behaviour:
- Cursor moves resolve into cell positions; OSC/CSI/charset noise is consumed by the emulator (zero leakage in the output).
- Each row's trailing whitespace is trimmed.
- Trailing all-blank rows are dropped so a mostly-empty screen produces a short snapshot rather than 60 newlines.
- Output ends with a single trailing `\n` if it's non-empty.
- Empty input produces an empty string.
Limitation: alternate-screen state. If the captured byte stream is from a TUI that is currently *on* alt-screen (Claude Code, vim mid- session), vt10x renders the alt-screen content — which is what you want. If the TUI exited and switched back to main screen, you get the main-screen content (typically the shell prompt). This matches what `cat session.bin` would render in your terminal.
func TruncatePayload ¶
TruncatePayload renders a payload for display in `ppz ls`: strip ANSI CSI escapes (ESC `[` … final-byte) and all C0 controls + DEL so the preview can never alter the user's terminal state, replace newlines with spaces, trim trailing whitespace, then cap at 60 bytes (UTF-8 safe).
Without this, a wrapped terminal's last .stdout chunk — typically a shell prompt with cursor moves and colour escapes — would render verbatim mid-listing and clear the screen, set bold, etc.
Types ¶
type AuthExchangeReply ¶
type AuthExchangeReply struct {
JWT string `json:"jwt"`
NATSURL string `json:"nats_url"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
ExpiresAt time.Time `json:"expires_at"`
// Auth V2 Phase 3 — short-lived NATS user credentials. The daemon
// uses NATSUserJWT + NATSUserSeed in nats.UserJWT(...) when
// connecting to the NATS server URL. Re-fetch before ExpiresAt
// (currently 5min) by re-running /auth/exchange with the same
// bearer.
NATSUserJWT string `json:"nats_user_jwt"`
NATSUserSeed string `json:"nats_user_seed"`
}
type AuthExchangeRequest ¶
type AuthExchangeRequest struct {
APIKey string `json:"api_key"`
// OrgID (Phase 3.5): which org's account to mint a User JWT in.
// Optional — server defaults to the bearer's primary org (first
// owned, or first member). Multi-org users specify it explicitly
// to switch which org their daemon talks to.
OrgID string `json:"org_id,omitempty"`
}
type BroadcastBatchReply ¶ added in v0.24.2
type BroadcastBatchReply struct {
IDs []string `json:"ids"`
Subject string `json:"subject"`
Bytes []int `json:"bytes"`
}
BroadcastBatchReply mirrors BroadcastReply but as parallel arrays, one entry per published payload. IDs[i] / Bytes[i] correspond to Payloads[i] in the request. Subject is shared across the batch (all messages land on the same handle.pipe).
type BroadcastBatchRequest ¶ added in v0.24.2
type BroadcastBatchRequest struct {
Handle string `json:"handle,omitempty"`
Channel string `json:"channel,omitempty"`
Payloads []string `json:"payloads"`
Session string `json:"session,omitempty"`
}
BroadcastBatchRequest publishes N payloads in one IPC round-trip. Used by streaming producers (terminal share's stdout drain, `ppz broadcast` line-streaming) where the per-call NATS round- trip cost dominates throughput under WAN. Validation runs once for the whole batch; the daemon issues N async nc.Publish calls followed by ONE nc.Flush, then replies with N ids — preserving the same "bytes confirmed at server" contract as the single IPCBroadcast call, just amortised across the batch.
type BroadcastReply ¶
type BroadcastRequest ¶
type BroadcastRequest struct {
// Optional explicit target. If both empty, daemon publishes to its
// current source on .broadcast. If Handle is set, publishes to
// <Handle>.<Channel|"broadcast">. Used by `ppz send` and by
// `ppz broadcast` when PPZ_CURRENT_HANDLE is exported (e.g. inside
// a `ppz terminal` child).
//
// `channel` JSON tag preserved for IPC backward-compat within Phase A;
// it carries a pipe name. Phase B reorganises.
Handle string `json:"handle,omitempty"`
Channel string `json:"channel,omitempty"`
Payload string `json:"payload"`
// MsgSubject is an optional envelope-level subject (header-line). Free-
// form for users (set via `ppz send --subject`); subjects starting with
// `ack:` are reserved for daemon-internal protocol messages (ack
// emission) and rejected at the IPC trust boundary in handleBroadcast.
MsgSubject string `json:"msg_subject,omitempty"`
// InReplyTo / AckRequested mirror the new envelope fields (v0.25.0).
// JSON tags align with the envelope (`in_reply_to`, `ack_requested`)
// rather than the older `msg_subject` precedent — these are 1:1 with
// envelope fields.
InReplyTo string `json:"in_reply_to,omitempty"`
AckRequested bool `json:"ack_requested,omitempty"`
// Session keys the per-session current-source fallback when neither
// Handle nor PPZ_CURRENT_HANDLE is set.
Session string `json:"session,omitempty"`
}
type Code ¶
type Code string
Code is a stable error identifier. Strings are part of the wire contract.
const ( ENotLoggedIn Code = "E_NOT_LOGGED_IN" EDaemonNotRunning Code = "E_DAEMON_NOT_RUNNING" EInvalidAPIKey Code = "E_INVALID_API_KEY" ESourceTaken Code = "E_SOURCE_TAKEN" ESourceNotFound Code = "E_SOURCE_NOT_FOUND" EInvalidHandle Code = "E_INVALID_HANDLE" ENoCurrentSource Code = "E_NO_CURRENT_SOURCE" EPayloadTooLarge Code = "E_PAYLOAD_TOO_LARGE" EServerUnreachable Code = "E_SERVER_UNREACHABLE" ENATSUnreachable Code = "E_NATS_UNREACHABLE" EInvalidPipe Code = "E_INVALID_PIPE" EPipeTaken Code = "E_PIPE_TAKEN" EPipeNotFound Code = "E_PIPE_NOT_FOUND" // EInvalidSubject is returned when a caller (CLI flag parser or IPC // client) tries to set a Subject value that violates the reserved- // prefix invariant. Daemon-emitted protocol messages own the `ack:` // prefix; users cannot stamp it themselves. EInvalidSubject Code = "E_INVALID_SUBJECT" )
type ConnectReply ¶
type ConnectReply struct {
Handle string `json:"handle"`
}
type ConnectRequest ¶
type ConnectRequest struct {
Handle string `json:"handle"`
Session string `json:"session,omitempty"`
}
ConnectRequest is the input to `ppz connect <handle>`. The daemon ensures the source exists (idempotent — pre-existing source is fine), then sets it as `current`.
type CreateInviteReply ¶ added in v0.22.0
type CreateInviteReply struct {
Invite Invite `json:"invite"`
}
type CreateInviteRequest ¶ added in v0.22.0
type CreateInviteRequest struct {
Username string `json:"username"`
}
CreateInviteRequest is the body for POST /api/v1/orgs/{slug}/invites and the /orgs/{id}/invites GUI form. Owner-only — handlers gate.
type CreateOrgReply ¶ added in v0.22.0
type CreateOrgRequest ¶ added in v0.22.0
type CreateOrgRequest struct {
Name string `json:"name"`
}
CreateOrgRequest is the body for POST /api/v1/orgs — bearer-auth'd org create from the CLI (`ppz org create <name>`). Caller becomes the owner.
type CreateReply ¶
type CreateRequest ¶
type CreateSourceReply ¶
type CreateSourceRequest ¶
type DiagEvent ¶ added in v0.26.0
type DiagEvent struct {
Type string `json:"type"`
At time.Time `json:"at"`
Reason string `json:"reason,omitempty"`
}
DiagEvent is one connection-state transition in DiagReply. Fields mirror the daemon's NATSEvent struct (kept as a separate type so the IPC contract is independent of internal storage).
type DiagReply ¶ added in v0.26.0
type DiagReply struct {
NATSState string `json:"nats_state,omitempty"`
NATSDropsLastHour int `json:"nats_drops_last_hour,omitempty"`
NATSEvents []DiagEvent `json:"nats_events"`
}
DiagReply carries the daemon's introspection snapshot. Phase 0: just the NATS connection state + recent connection-state events. Future phases will extend with refresh-loop state, JetStream consumer lag, etc.
type DiagRequest ¶ added in v0.26.0
type DiagRequest struct{}
DiagRequest is the input to `ppz diag` — currently empty. Reserved for future scoping flags (per-subsystem filters, since-when, etc.).
type DisconnectReply ¶
type DisconnectReply struct{}
DisconnectReply is empty — the only outcome of disconnect is "current" being cleared on the daemon side. Returns no fields.
type DisconnectRequest ¶
type DisconnectRequest struct {
Session string `json:"session,omitempty"`
}
DisconnectRequest carries the session id whose current source should be cleared. Empty Session normalises to "default".
type Error ¶
Error is a wire-level error carrying a Code and an arbitrary message.
func NewInvalidHandle ¶
NewInvalidHandle: invalid handle 'BAD'
func NewInvalidPipeName ¶
NewInvalidPipeName: pipe name 'X' is invalid (regex rejection / etc.)
func NewInvalidPipeReserved ¶
NewInvalidPipeReserved: pipe name 'system' is reserved
func NewPipeNotFound ¶
NewPipeNotFound: pipe 'archive' not found on source 'foo'
func NewPipeTaken ¶
NewPipeTaken: pipe 'archive' already exists on source 'foo'
func NewSourceNotFound ¶
NewSourceNotFound: source 'foo' not found
func NewSourceTaken ¶
NewSourceTaken: source 'foo' already exists in this org
type HTTPError ¶
type HTTPError struct {
Error Error `json:"error"`
}
HTTPError is the body shape of a non-2xx HTTP response.
type Invite ¶ added in v0.22.0
type Invite struct {
ID string `json:"id"`
OrganisationID string `json:"organisation_id"`
OrganisationName string `json:"organisation_name,omitempty"`
InviteeUsername string `json:"invitee_username"`
InviterUserID string `json:"inviter_user_id"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
DecidedAt string `json:"decided_at,omitempty"`
}
Invite is the API projection of a db.Invite row plus the org name (so the dashboard can render "Pending invitation to <org>" without a second join).
type ListInvitesReply ¶ added in v0.22.0
type ListInvitesReply struct {
Invites []Invite `json:"invites"`
}
type ListOrgsReply ¶
type ListOrgsReply struct {
Orgs []OrgInfo `json:"orgs"`
}
type ListRequest ¶
type ListRequest struct {
Session string `json:"session,omitempty"` // cursor key
}
type ListSourcesReply ¶
type ListSourcesReply struct {
Sources []Source `json:"sources"`
}
type ListWatchRequest ¶
type ListWatchRequest struct {
Session string `json:"session,omitempty"`
Patterns []string `json:"patterns,omitempty"`
}
ListWatchRequest is `ppz ls --watch`. The daemon returns the same shape as ListReply, but only after the calling session has at least one unread message on a pipe whose handle matches one of Patterns (or any handle if Patterns is empty).
Semantics: level-triggered. If unread > 0 already at call time on a matching handle, the daemon returns immediately. Otherwise it blocks until a new message arrives on a matching subject, then returns.
Patterns are filepath.Match-style globs (`*`, `?`, `[abc]`) matched against the handle (not handle.pipe). Multiple patterns OR-combine.
type LoginReply ¶
type LoginRequest ¶
type OrgCreateRequest ¶ added in v0.22.0
type OrgCreateRequest struct {
Name string `json:"name"`
}
OrgCreateRequest is the IPC payload for `ppz org create <name>` — daemon proxies to the server's POST /api/v1/orgs.
type OrgInfo ¶
type OrgInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Role string `json:"role,omitempty"` // owner / member / viewer / bot (Phase 3.6)
// Current is true for the org the daemon is currently bound to
// (per `ppz org switch`). Set by the daemon's IPCOrgList handler
// after the server returns the membership list — the server has
// no notion of "current org" since that's daemon-side state.
Current bool `json:"current,omitempty"`
}
OrgInfo is one row in the ListOrgs response — what `ppz orgs ls` prints. UUID + display name + role; no NATS-side fields here, this endpoint is purely "which orgs am I in".
type OrgInviteRequest ¶ added in v0.22.0
type OrgInviteRequest struct {
Username string `json:"username"`
}
OrgInviteRequest is the IPC payload for `ppz org invite <username>` — daemon resolves "current org" and proxies to POST /api/v1/orgs/{slug}/invites.
type OrgSwitchReply ¶ added in v0.22.0
OrgSwitchReply mirrors what `ppz status` cares about — the new active org. Daemon returns the resolved id+name so the CLI can echo back "switched to org=<name>".
type OrgSwitchRequest ¶ added in v0.22.0
type OrgSwitchRequest struct {
Name string `json:"name"`
}
OrgSwitchRequest carries the daemon-side IPC payload for `ppz org switch <name>`. Daemon resolves name → org_id, re-runs auth/exchange with the new org, persists.
type PipeCreateReply ¶
type PipeCreateReply struct {
Handle string `json:"handle"`
Name string `json:"name"`
StreamName string `json:"stream_name"`
TTLSeconds int `json:"ttl_seconds"`
MaxMsgs int `json:"max_msgs"`
MaxBytes int64 `json:"max_bytes"`
}
PipeCreateReply mirrors the server's resolved retention (after defaults are filled in) so the CLI prints exactly what was provisioned.
type PipeCreateRequest ¶
type PipeCreateRequest struct {
Handle string `json:"handle"`
Name string `json:"name"`
TTLSeconds *int `json:"ttl_seconds,omitempty"`
MaxMsgs *int `json:"max_msgs,omitempty"`
MaxBytes *int64 `json:"max_bytes,omitempty"`
}
PipeCreateRequest is the input to `ppz pipe create <name>` — and the body of POST /api/v1/sources/{handle}/pipes. Retention overrides are pointers so "absent" (= use default) is distinguishable from "explicitly zero".
type PipeDestroyReply ¶
type PipeDestroyRequest ¶
type PipeInfo ¶
type PipeInfo struct {
Pipe string `json:"pipe"`
Total uint64 `json:"total"`
Unread uint64 `json:"unread"`
LastSeq uint64 `json:"last_seq,omitempty"`
LastAt *time.Time `json:"last_at,omitempty"`
Preview string `json:"preview,omitempty"` // truncated to 60 bytes for table view
Payload string `json:"payload,omitempty"` // full untruncated payload for `ls --json`
}
PipeInfo is per-pipe state surfaced by ppz ls. Total + LastSeq come from the JetStream stream's Info; Unread is computed daemon-side from the session's cursor file. Preview is the most recent payload truncated to 60 chars (UTF-8 safe; ANSI CSI sequences and C0 controls stripped).
type ReadEvent ¶
type ReadEvent struct {
Message *ReadMessage `json:"msg,omitempty"`
Error *Error `json:"error,omitempty"`
// Meta is an optional leading event (sent before any Message events)
// carrying out-of-band stream metadata — currently the source pty's
// dimensions for `<h>.stdout` reads, sourced from the latest
// `<h>.stdctrl` resize. Lets the CLI configure its --tty renderer
// to match the source size before consuming bytes.
Meta *ReadMeta `json:"meta,omitempty"`
}
ReadEvent is the wire format of one streamed line in a Read response. Exactly one of Message / Error / Meta is set on each event. End-of-stream is signaled by the daemon closing the connection.
type ReadMessage ¶
type ReadMessage struct {
ID string `json:"id"`
Sender string `json:"sender"`
Subject string `json:"subject"`
Payload string `json:"payload"`
CreatedAt string `json:"created_at"`
InReplyTo string `json:"in_reply_to"`
AckRequested bool `json:"ack_requested"`
}
ReadMessage is the daemon's serialized form of one envelope. The CLI formats this as either bare payload text or a JSON object depending on whether --json was passed.
Sender mirrors the envelope's `sender` (publisher's current source at publish time). Subject mirrors the envelope's `subject` (optional header-line, free-form for users / `ack:*` for system messages). Both are empty for legacy retained messages published before v0.23.0 (those carried `handle` instead, which is now silently dropped on parse).
type ReadMeta ¶
ReadMeta carries leading metadata about the stream. Currently a dimension hint for terminal renders; future fields can join here (cwd, exit code, last activity ts, etc.) without breaking older CLI builds (unknown JSON fields are ignored).
type ReadRequest ¶
type ReadRequest struct {
Handle string `json:"handle"`
Channel string `json:"channel"` // pipe name: broadcast / stdin / stdout
Limit int `json:"limit,omitempty"` // 0 = unlimited; non-zero = tail-N (reread only)
Skip int `json:"skip,omitempty"` // drop the first N retained messages (reread only)
SinceMS int64 `json:"since_ms,omitempty"` // 0 = no time filter; >0 = only msgs newer than (now − this many ms) (reread only)
JSON bool `json:"json,omitempty"` // emit envelope as JSON instead of payload text
Follow bool `json:"follow,omitempty"`
Session string `json:"session,omitempty"` // cursor key — defaults to "default" daemon-side
NoAdvance bool `json:"no_advance,omitempty"` // observational reads (terminal view) skip cursor advance
All bool `json:"all,omitempty"` // forensic mode (`reread`): ignore the cursor (deliver everything) and don't advance it. Implies NoAdvance.
}
ReadRequest carries the parsed `ppz read` parameters from the CLI to the daemon. The daemon streams ReadEvent JSON lines back on the same connection; the CLI reads until EOF (or until SIGINT closes the socket).
The `channel` JSON tag is preserved for IPC backward-compat within the Phase A rename — it carries a pipe name (sub-bucket). Phase B reorganises IPC field names alongside the verb refactor.
type Source ¶
type Source struct {
Handle string `json:"handle"`
Kind string `json:"kind,omitempty"`
// Pipes is the list of user-created pipe names on this source (NOT the
// auto-provisioned set — derive those from Kind). Set by the server's
// /api/v1/sources response.
Pipes []string `json:"pipes,omitempty"`
PipeInfos []PipeInfo `json:"pipe_infos,omitempty"`
// Legacy broadcast-only summary; kept for the GUI handlers that still
// read these directly from the postgres-backed sources table.
LastBroadcastAt *time.Time `json:"last_broadcast_at,omitempty"`
LastBroadcastPayload *string `json:"last_broadcast_payload,omitempty"`
}
type SourceDestroyReply ¶ added in v0.21.0
type SourceDestroyReply struct {
Handle string `json:"handle"`
}
type SourceDestroyRequest ¶ added in v0.21.0
type SourceDestroyRequest struct {
Handle string `json:"handle"`
}
type SourceKind ¶
type SourceKind string
Source kinds, mirrored from internal/db so non-db callers can use them.
const ( KindMessage SourceKind = "message" KindPTY SourceKind = "pty" )
type StatusReply ¶
type StatusReply struct {
DaemonPID int `json:"daemon_pid"`
DaemonVersion string `json:"daemon_version,omitempty"`
LoggedIn bool `json:"logged_in"`
URL string `json:"url,omitempty"`
KeyPrefix string `json:"key_prefix,omitempty"`
OrgID string `json:"org_id,omitempty"`
OrgName string `json:"org_name,omitempty"`
LastTokenRefreshAt *time.Time `json:"last_token_refresh_at,omitempty"`
// LoginCheck is the daemon's last verification result against the
// server. "ok" means a recent server-touching call succeeded;
// "invalid" means the server returned E_INVALID_API_KEY (key
// revoked / rotated since login). Empty / "unknown" means we
// haven't observed yet — status performs an active probe in that
// case so the user always sees the truth.
LoginCheck string `json:"login_check,omitempty"`
Current string `json:"current,omitempty"`
// CurrentPath is the daemon-side path to current.json — surfaced so
// `ppz status`'s env/daemon-disagree warning can point users at the
// actual file (which lives in the daemon's home, not the CLI's, when
// they're separate processes).
CurrentPath string `json:"current_path,omitempty"`
// NATSState is one of "connected", "disconnected", "connecting" —
// the daemon's current NATS connection state. Empty means
// unobserved (no connection ever attempted, e.g. fresh daemon
// pre-login). Drives the `nats:` line in `ppz status` output;
// underlying event log is available via `ppz diag`. (Phase 0 of
// agent hardening, docs/WIRE.md §8.)
NATSState string `json:"nats_state,omitempty"`
}
type StatusRequest ¶
type StatusRequest struct {
Session string `json:"session,omitempty"`
}
StatusRequest carries the caller's session id so the daemon can return the per-session current source. Empty Session normalises to "default".
type SwitchReply ¶
type SwitchReply struct {
Handle string `json:"handle"`
}