Documentation
¶
Overview ¶
Package progstatus implements the terminal side of the Program Status Protocol, OSC 7501, revision 0.2 (2026-10-06): https://www.superlogical.com/rex/docs/build/program-status
A program writes OSC 7501 ; key=value:key=value ST to say what it is doing (idle, working, done, blocked or error), and the terminal keeps one record per id. This package holds the parts every user of the protocol shares: the parser, which applies every discard rule before anything is stored; the record store, with its limits, eviction, id hierarchy and lifetime events; the summary rule that picks the record a pane shows; and the encoder a program uses to write a report.
A terminal emulator calls Parse on the body of each OSC 7501 sequence and hands the result to a Store. A program calls Sequence to build a report. The package uses only the standard library. tuios uses it in its emulator, its daemon and its client, and for the tuios status command.
The API is at v0 and can change between minor versions.
Index ¶
- Constants
- func Encode(r Report) string
- func FitSegment(s string, limit int) string
- func FitText(s string, limit int) string
- func InheritApp(recs []Record)
- func IsControl(r rune) bool
- func IsQuery(body []byte) bool
- func Sequence(r Report) string
- func SequenceLen(payloadLen int, bel bool) int
- func Urgency(s State) int
- func ValidApp(app string) bool
- func ValidID(id string) bool
- type Event
- type Kind
- type Record
- type Report
- type State
- type Store
- func (s *Store) Apply(r Report, now int64) bool
- func (s *Store) ApplyFrom(r Report, now int64, group int) bool
- func (s *Store) DropEnded(ended func(group int) bool) bool
- func (s *Store) DropFinished() bool
- func (s *Store) DropTransient() bool
- func (s *Store) GroupOf(id string) int
- func (s *Store) Groups() []int
- func (s *Store) Handle(ev Event, now int64) bool
- func (s *Store) HandleFrom(ev Event, now int64, group int) bool
- func (s *Store) HasFinished() bool
- func (s *Store) Len() int
- func (s *Store) Records() []Record
- func (s *Store) Reset() bool
- func (s *Store) Seen() bool
Examples ¶
Constants ¶
const ( // MaxSequence is the most bytes of one sequence, OSC through ST. MaxSequence = 4096 // MaxKey is the longest key, in bytes. MaxKey = 16 // MaxMsgEncoded and MaxMsgDecoded bound msg before and after base64. MaxMsgEncoded = 2732 MaxMsgDecoded = 2048 // MaxTitleEncoded and MaxTitleDecoded bound title before and after base64. MaxTitleEncoded = 256 MaxTitleDecoded = 192 // MaxApp is the longest app value, in bytes. MaxApp = 32 // MaxID, MaxSegment and MaxDepth bound an id. MaxID = 128 MaxSegment = 32 MaxDepth = 8 // MaxRecords is how many records one terminal holds. The specification // requires at least 64 and caps it at 256. MaxRecords = 256 )
Limits from the specification's Limits section. A report that breaks one is discarded whole.
const Command = 7501
Command is the OSC number of the protocol.
const NoProgress = -1
NoProgress is the Progress of a report or record that carries none.
const QueryReply = "\x1b]7501;?"
QueryReply is the only thing a terminal writes back, without its ST.
Variables ¶
This section is empty.
Functions ¶
func Encode ¶
Encode builds the body of a report for r, without OSC and ST: the pairs joined with ":", title and msg in standard base64. It does not check r: a caller that builds a report from outside input runs the result through Parse, or makes each value fit first with FitText and FitSegment.
func FitSegment ¶
FitSegment makes s a valid id segment or app value: bytes outside [A-Za-z0-9_.+-] become "-", and the result is cut to max bytes. An empty result becomes "-".
func FitText ¶
FitText makes s fit a free-text value: control characters become spaces, invalid UTF-8 is dropped, and the text is cut on a rune boundary to max bytes (limit). A producer uses it so a report it builds is never discarded.
Example ¶
A program builds a report from text it does not control. FitText and FitSegment make each value fit, so the terminal never discards the report.
package main
import (
"fmt"
"tuios.dev/progstatus"
)
func main() {
branch := "feature/new login"
msg := "Testing " + branch + "\n"
seq := progstatus.Sequence(progstatus.Report{
State: progstatus.Working,
ID: progstatus.FitSegment(branch, progstatus.MaxSegment),
App: progstatus.FitSegment("ci", progstatus.MaxApp),
Msg: progstatus.FitText(msg, progstatus.MaxMsgDecoded),
Progress: 40,
})
fmt.Printf("%q\n", seq)
}
Output:
func InheritApp ¶
func InheritApp(recs []Record)
InheritApp fills EffectiveApp on every record of recs from the record's own app or its nearest ancestor's. A parent does not have to exist, so the search walks up the id and skips missing levels.
func IsControl ¶
IsControl reports whether r is a control character as the specification defines one: U+0000 to U+001F, U+007F and U+0080 to U+009F.
func IsQuery ¶
IsQuery reports whether body, the bytes after "7501;", is the feature detection query.
func Sequence ¶
Sequence is Encode wrapped in OSC 7501 and ESC \.
Example ¶
A program reports that it waits for an approval. The sequence goes to the terminal, so a program whose output goes to a file writes it to /dev/tty.
package main
import (
"fmt"
"os"
"tuios.dev/progstatus"
)
func main() {
seq := progstatus.Sequence(progstatus.Report{
State: progstatus.Blocked,
Kind: progstatus.Permission,
App: "deploy",
Msg: "Approve deploy to production?",
})
fmt.Fprint(os.Stdout, seq)
}
Output:
func SequenceLen ¶
SequenceLen is the length of a whole sequence, OSC through ST, whose payload (the bytes between OSC and ST, "7501;..." included) is payloadLen bytes and which ended with BEL when bel is true.
func Urgency ¶
Urgency orders states for the summary: the higher, the more a person needs to see it.
Types ¶
type Event ¶
Event is what an emulator hands its owner: a report, or a full reset (RIS), which removes every record.
type Record ¶
type Record struct {
ID string
State State
Kind Kind
Progress int
// App is the record's own app. EffectiveApp is it or the nearest
// ancestor's, filled in by Records.
App string
EffectiveApp string
Title string
Msg string
// Seq orders updates: the record updated least recently has the lowest.
Seq uint64
// At is when the record was last replaced, in Unix nanoseconds.
At int64
// Group is the process group that held the terminal's foreground when
// the report came in, for a working, blocked or idle record from a
// program in the foreground, and 0 otherwise. When that group ends, the
// program that reported has exited (DropEnded). The owner of the store
// sets it; the protocol knows nothing of it.
Group int
}
Record is what the store keeps for one id.
type Report ¶
type Report struct {
State State
// ID is the record's id, empty for the root record.
ID string
// Kind is set only on a blocked report with a recognised kind.
Kind Kind
// Progress is 0 to 100 on a working or blocked report that carried a
// valid one, and NoProgress otherwise.
Progress int
// App is the program's own name, empty when absent or invalid.
App string
// Title and Msg are the decoded texts, empty when absent.
Title string
Msg string
}
Report is one OSC 7501 report that passed every check.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds one terminal's records. Its zero value is empty and ready. It is safe for concurrent use, and it never calls out while it holds its lock.
Example ¶
A terminal emulator reads the body of an OSC 7501 sequence, the bytes after "7501;", and keeps the result in a Store. Summary picks the record to show.
package main
import (
"fmt"
"strings"
"time"
"tuios.dev/progstatus"
)
func main() {
var store progstatus.Store
// The sequence as it came from the program, ended with ST (ESC \).
seq := "\x1b]7501;state=working:app=build:progress=40\x1b\\"
payload := strings.TrimSuffix(strings.TrimPrefix(seq, "\x1b]"), "\x1b\\")
body := []byte(strings.TrimPrefix(payload, "7501;"))
if progstatus.IsQuery(body) {
// Answer the feature query with the terminator the question used.
fmt.Print(progstatus.QueryReply + "\x1b\\")
return
}
report, ok := progstatus.Parse(body, progstatus.SequenceLen(len(payload), false))
if !ok {
return // discarded: apply nothing
}
store.Apply(report, time.Now().UnixNano())
if rec, ok := progstatus.Summary(store.Records()); ok {
fmt.Println(rec.EffectiveApp, rec.State, rec.Progress)
}
}
Output:
func (*Store) Apply ¶
Apply stores one report that Parse returned, and reports whether the records changed. A report replaces its record whole. A clear removes the addressed record and every record beneath it, and with no id removes every record.
func (*Store) ApplyFrom ¶
ApplyFrom is Apply for a report that came from the process group group (see Record.Group), 0 for none. The group is kept only on a working, blocked or idle record, the states a program's exit ends.
func (*Store) DropEnded ¶
DropEnded removes the working, blocked and idle records whose group ended says has ended: the program that reported them has exited. Records from any other group, and records with none, stay. It reports whether anything was removed. ended is called with the store's lock held, so it must not call back into the store.
func (*Store) DropFinished ¶
DropFinished removes done and error records. tuios calls it when the person types into the pane, which is when the specification suggests a terminal stops showing them.
func (*Store) DropTransient ¶
DropTransient is what a shell prompt (OSC 133 A) and the exit of the terminal's process do: working and blocked records go, and so do idle ones, which the specification allows. done and error stay.
func (*Store) GroupOf ¶
GroupOf is the group record id came from, 0 when it has none or does not exist.
func (*Store) Handle ¶
Handle applies an emulator event at now (Unix nanoseconds) and reports whether the records changed.
func (*Store) HandleFrom ¶
HandleFrom is Handle for a report that came from the process group group (see Record.Group), 0 for none.
func (*Store) HasFinished ¶
HasFinished reports whether a done or error record is held.
func (*Store) Records ¶
Records returns a copy of every record, the root first and the rest in id order, with EffectiveApp filled in from the nearest ancestor that has an app.