Documentation
¶
Overview ¶
Package history is fft's local record of the requests it has sent.
Each entry is metadata about one command line that addressed an API operation: when, against which project, which operation, with which flags (redacted), and how it ended. Request and response bodies are never recorded. The record is a JSON Lines file owned by the user, which the CLI appends to and `fft tui` reads to offer the operations a user reaches for most.
Recording is best-effort by design. A command's output and exit code must not depend on whether its history could be written, so every function here reports its failure and leaves the decision to ignore it to the caller.
Index ¶
Constants ¶
const ( SourceCLI = "cli" SourceTUI = "tui" )
The sources an entry can come from.
const DefaultMaxBytes = 1 << 20
DefaultMaxBytes is the size above which Log.Append compacts the file.
const Redacted = "<redacted>"
Redacted replaces a value history does not keep.
const Version = 1
Version is the entry format this package writes.
Variables ¶
This section is empty.
Functions ¶
func Args ¶
Args returns the arguments an entry records for a command line, after Redact: its positional arguments, then its flags, each written as --name=value or, for a flag given without one, a bare --name.
An argument that starts with a dash would read back as a flag there, so when one does, the flags come first and the arguments after a "--" separator: the order a shell needs them in too, which keeps `fft history list` a command line that can be run again. Read the result back with SplitArgs.
func Path ¶
Path is the default history file: $XDG_STATE_HOME/fft/history.jsonl, or ~/.local/state/fft/history.jsonl when XDG_STATE_HOME is unset.
func Redact ¶
Redact returns args with the values history must not keep replaced by Redacted. args are positional arguments and flags, each flag written as --name=value or, for a flag given without a value, a bare --name.
- --data keeps "-" and "@path", which say where the body came from; an inline body is dropped.
- --header, --set, --param and --require keep the name of each pair.
- A flag whose name looks like a credential or a person's data is dropped, and so is the value of any pair whose name does.
- Any value that starts with '{' or '[' is a JSON document, and is dropped wherever it appears.
args is not modified.
func SplitArgs ¶
SplitArgs returns the positional arguments and the flags of args as Args recorded them. An entry recorded before the separator was has its arguments first, and its flags from the first one that starts with "--"; neither layout can be mistaken for the other, since only the separator is ever a bare "--".
Both results are copies.
Types ¶
type CompactError ¶
type CompactError struct {
Err error
}
CompactError is what Log.Append returns when the entry itself was written but the trailing compaction it triggered failed. Unwrap it to inspect the cause; a caller that only cares whether the entry was recorded should treat it as success and report the wrapped error on its own debug channel instead. A compaction that only stood down is not a failure and is not one of these.
func (*CompactError) Error ¶
func (e *CompactError) Error() string
func (*CompactError) Unwrap ¶
func (e *CompactError) Unwrap() error
type Entry ¶
type Entry struct {
V int `json:"v"`
TS time.Time `json:"ts"`
Source string `json:"source"`
// Project is the name of the project the command acted on, "" when it failed
// before one was resolved.
Project string `json:"project,omitempty"`
OperationID string `json:"operationId"`
// Command is the command's path, "fft facility list".
Command string `json:"command"`
// Args are the command's positional arguments and the flags it was given, as
// [Args] records them. Read them with [SplitArgs]: an argument that starts
// with a dash follows the flags and a "--".
Args []string `json:"args,omitempty"`
// Status is the HTTP status of the last response, 0 when none arrived.
Status int `json:"status,omitempty"`
Exit int `json:"exit"`
DurationMS int64 `json:"durationMs"`
}
Entry is one recorded command line.
type Log ¶
type Log struct {
Path string
// MaxBytes is the size above which Append compacts the file to about half of
// it, keeping the newest entries. Zero means [DefaultMaxBytes]. A read keeps
// at most the last [readLimitFactor] times MaxBytes of the file.
MaxBytes int64
// OnSkip, when set, is told why a compaction stood down instead of running. It
// is not an error channel: nothing failed, and Append still reports success.
//
// It exists for the one stand-down that may never end. A lock another process
// holds is being used to compact right now, so the next append finds the work
// done; a rename Windows refuses can, in the worst case, be refused for good
// (see errBusy), and a file that then stays over its limit for ever is worth a
// line on a caller's debug channel rather than silence.
OnSkip func(error)
}
Log is a history file.
func (Log) Append ¶
Append adds e to the end of the file, creating it (0600, in a 0700 directory) if needed, and taking group and other access away from a file or directory that has it.
The entry is one write to a file opened for appending, which is what lets any number of fft processes record at once without a lock: the operating system places each write at the end, whole. Only compaction, which rewrites the file, takes a lock — and skips its turn rather than wait, for that lock or for the handles that keep it from renaming anything into place on Windows.
func (Log) Clear ¶
Clear deletes the history, reporting how many entries it held. Clearing a history that does not exist is not an error.
func (Log) Read ¶
Read returns every entry in the file, oldest first. A file that does not exist is an empty history. A line that is not an entry — a write cut short by a full disk, a hand edit — is skipped, so one bad line never costs the rest.
A file larger than the read limit (see Log.MaxBytes) is read from the end: its newest entries are returned, and the older ones are left unread.
type Usage ¶
type Usage struct {
Project string `json:"project"`
OperationID string `json:"operationId"`
// Command is the command path the operation was last reached through.
Command string `json:"command"`
Count int `json:"count"`
LastUsed time.Time `json:"lastUsed"`
}
Usage is how often one operation was used in one project.