history

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: MIT Imports: 15 Imported by: 0

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

View Source
const (
	SourceCLI = "cli"
	SourceTUI = "tui"
)

The sources an entry can come from.

View Source
const DefaultMaxBytes = 1 << 20

DefaultMaxBytes is the size above which Log.Append compacts the file.

View Source
const Redacted = "<redacted>"

Redacted replaces a value history does not keep.

View Source
const Version = 1

Version is the entry format this package writes.

Variables

This section is empty.

Functions

func Args

func Args(positional, flags []string) []string

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

func Path() (string, error)

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

func Redact(args []string) []string

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

func SplitArgs(args []string) (positional, flags []string)

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

func (l Log) Append(e Entry) error

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

func (l Log) Clear() (int, error)

Clear deletes the history, reporting how many entries it held. Clearing a history that does not exist is not an error.

func (Log) Read

func (l Log) Read() ([]Entry, error)

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.

func Top

func Top(entries []Entry, project string, n int) []Usage

Top returns the n most used operations in entries, most used first and the more recently used first among equals. project narrows it to one project, and "" counts every project, each separately. n <= 0 returns them all.

Jump to

Keyboard shortcuts

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