cmdc

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 19 Imported by: 0

README

cmdc

cmdc is a command output cache.

cmdc exec runs a command and caches its output (stdout and stderr) in the memory of a resident process. While the cache is alive, running the same command again prints the cached output without running the command.

  • The resident process (daemon) is started automatically at the first run. No setup is needed.
  • The daemon does not listen on any TCP port. Clients talk to it over a unix domain socket.
  • The output is cached only when the command succeeds (exit status 0).
  • Nothing is written to the disk. The cached outputs are gone when the daemon exits.

Install

$ go install github.com/fujiwara/cmdc/cmd/cmdc@latest

Or download a binary from Releases.

cmdc works on Linux and macOS.

Usage

Usage: cmdc <command> [flags]

Caches the output of commands in the memory of a resident process.

Flags:
  -h, --help           Show context-sensitive help.
      --socket=PATH    Path to the unix domain socket of the daemon ($CMDC_SOCKET).
      --debug          Enable debug log ($CMDC_DEBUG).
      --version        Show version.

Commands:
  exec --expires=DURATION <command> ... [flags]
    Run a command, or print its cached output.

  status [flags]
    Show the status of the daemon.

  clear [flags]
    Remove all cached outputs.

  stop [flags]
    Stop the daemon.
cmdc exec
$ cmdc exec --expires 5m -- aws sts get-caller-identity

The first run executes the command, passes its output through, and caches the output for 5 minutes. Subsequent runs within 5 minutes print the cached output immediately.

$ export CMDC_EXPIRES=10s
$ time cmdc exec -- sh -c 'sleep 3; date'
Wed Nov 11 14:30:03 JST 2026
real    0m3.021s

$ time cmdc exec -- sh -c 'sleep 3; date'
Wed Nov 11 14:30:03 JST 2026
real    0m0.004s
Flag Environment variable Description
--expires, -e CMDC_EXPIRES How long the output is cached, e.g. 30s, 5m, 1h. Required.
--key-env CMDC_KEY_ENV Names of environment variables whose values are part of the cache key. Repeatable, or comma separated.
--max-size CMDC_MAX_SIZE Maximum size in bytes of an output to cache. Default 10MiB.

There is no default value for the expiration, because a suitable value depends on the command.

Cache key

The cache key consists of the command and its arguments.

The working directory and environment variables are not part of the key. If the output depends on environment variables, name them with --key-env.

$ cmdc exec --expires 5m --key-env AWS_PROFILE --key-env AWS_REGION -- aws sts get-caller-identity
Expiration

An entry lives for the --expires given when it was stored. A run with a shorter --expires than the age of the entry treats it as stale, runs the command again, and replaces the entry.

Exit status
  • When the cached output is used, cmdc exits with 0.
  • Otherwise, cmdc exits with the exit status of the command. A failed command is not cached, so the next run executes it again.
Notes
  • The command runs as a child process of cmdc exec, not of the daemon. It inherits the environment variables, the working directory, and stdin as usual.
  • stdout and stderr of the command are pipes, not a terminal. Some commands change their output in that case (e.g. disable colors).
  • stdout and stderr are replayed separately, in the order they were read. The interleaving of the two may be slightly different from the original.
  • stdin is passed to the command, but is not part of the cache key and is not consumed on a cache hit.
  • An output larger than --max-size is not cached.
  • If the daemon is not available for any reason, the command runs without the cache.
cmdc status

Shows the status of the daemon and the cached entries as JSON. It exits with 1 if the daemon is not running.

$ cmdc status
{
  "running": true,
  "socket": "/run/user/1000/cmdc/cmdc.sock",
  "pid": 12345,
  "version": "v0.1.0",
  "uptime": "2m10s",
  "entries": 1,
  "total_size": 430,
  "items": [
    {
      "command": "aws sts get-caller-identity",
      "size": 430,
      "age": "1m3.21s",
      "expires_in": "3m56.79s"
    }
  ]
}
cmdc clear

Removes all cached outputs. The daemon keeps running.

cmdc stop

Stops the daemon. All cached outputs are removed.

Daemon

The daemon is started by cmdc exec when it is not running, one per user (per socket).

  • The socket is $XDG_RUNTIME_DIR/cmdc/cmdc.sock, or $TMPDIR/cmdc-$UID/cmdc.sock if XDG_RUNTIME_DIR is not set. It is accessible only by the owner. --socket or CMDC_SOCKET overrides the path.
  • The daemon never executes commands. It only stores the outputs sent by cmdc exec.
  • The daemon exits by itself when it has no cached outputs and no activity for 10 minutes.
  • When cmdc exec finds a daemon of a different version, it replaces the daemon with its own version. The cached outputs are removed at that time.

The following environment variables are read when the daemon starts.

Environment variable Description
CMDC_IDLE_TIMEOUT The daemon exits after this duration without cached outputs and activity. Default 10m.
CMDC_MAX_TOTAL_SIZE Maximum total size in bytes of cached outputs. When it is exceeded, the oldest entries are removed. Default 256MiB.

LICENSE

MIT

Author

FUJIWARA Shunichiro

Documentation

Index

Constants

View Source
const (
	OpHello  = "hello"
	OpGet    = "get"
	OpSet    = "set"
	OpStatus = "status"
	OpClear  = "clear"
	OpStop   = "stop"
)

Operations of the protocol between the client and the daemon. Messages are newline delimited JSON over a unix domain socket.

View Source
const (
	DefaultIdleTimeout  = 10 * time.Minute
	DefaultMaxTotalSize = 256 << 20
)

Variables

View Source
var ErrAlreadyRunning = errors.New("daemon is already running")

ErrAlreadyRunning is returned by Server.Run when another daemon holds the lock for the same socket.

View Source
var Version = "v0.0.1"

Functions

func CacheKey

func CacheKey(argv []string, keyEnv []string) string

CacheKey returns the cache key for a command line. The values of the environment variables named in keyEnv are part of the key; the working directory and other environment variables are not.

func Run

func Run(ctx context.Context) error

Run runs cmdc with the command line arguments of the process.

func SocketPath

func SocketPath(path string) (string, error)

SocketPath resolves the socket path. When path is empty, the default path in a private per-user directory is used.

Types

type CLI

type CLI struct {
	Socket  string           `help:"Path to the unix domain socket of the daemon." env:"CMDC_SOCKET" placeholder:"PATH"`
	Debug   bool             `help:"Enable debug log." env:"CMDC_DEBUG"`
	Version kong.VersionFlag `help:"Show version."`

	Exec   ExecCmd   `cmd:"" help:"Run a command, or print its cached output."`
	Status struct{}  `cmd:"" help:"Show the status of the daemon."`
	Clear  struct{}  `cmd:"" help:"Remove all cached outputs."`
	Stop   struct{}  `cmd:"" help:"Stop the daemon."`
	Daemon DaemonCmd `cmd:"" hidden:"" help:"Run the daemon in the foreground."`
}

CLI defines the command line interface.

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache is an in-memory store of command outputs.

func NewCache

func NewCache(maxTotal int64) *Cache

NewCache creates a cache which holds up to maxTotal bytes of output.

func (*Cache) Clear

func (c *Cache) Clear() int

Clear removes all entries and returns the number of removed entries.

func (*Cache) Get

func (c *Cache) Get(key string, expires time.Duration) ([]Chunk, bool)

Get returns the chunks stored for key if the entry is younger than both expires and the expiration given when it was stored.

func (*Cache) Len

func (c *Cache) Len() int

Len returns the number of entries.

func (*Cache) Set

func (c *Cache) Set(key, label string, chunks []Chunk, ttl time.Duration) bool

Set stores chunks for key. It reports false if the entry is not stored because it does not fit in the cache. When the cache is full, the oldest entries are evicted.

func (*Cache) Stats

func (c *Cache) Stats() ([]StatusItem, int64)

Stats returns the unexpired entries, oldest first, and their total size.

func (*Cache) Sweep

func (c *Cache) Sweep()

Sweep removes expired entries.

type Chunk

type Chunk struct {
	Stream Stream `json:"s"`
	Data   []byte `json:"d"`
}

Chunk is a piece of command output. A cached result is a sequence of chunks so that stdout and stderr can be replayed in the recorded order.

type Client

type Client struct {
	ServerVersion string
	ServerPID     int
	// contains filtered or unexported fields
}

Client talks to the daemon over a unix domain socket.

func Dial

func Dial(ctx context.Context, socket string) (*Client, error)

Dial connects to a running daemon.

func DialOrStart

func DialOrStart(ctx context.Context, socket string) (*Client, error)

DialOrStart connects to the daemon, starting it if it is not running. A daemon of a different version is replaced with the current one.

func (*Client) Clear

func (c *Client) Clear() (int, error)

Clear removes all cached entries and returns the number of them.

func (*Client) Close

func (c *Client) Close() error

Close closes the connection.

func (*Client) Get

func (c *Client) Get(key string, expires time.Duration) ([]Chunk, bool, error)

Get looks up the cached output for key which is younger than expires.

func (*Client) Set

func (c *Client) Set(key, label string, chunks []Chunk, expires time.Duration) (bool, error)

Set stores the output for key. label is shown in the status.

func (*Client) Status

func (c *Client) Status() (*Status, error)

Status returns the status of the daemon.

func (*Client) Stop

func (c *Client) Stop() error

Stop asks the daemon to exit.

type DaemonCmd

type DaemonCmd struct {
	IdleTimeout  time.Duration `help:"Exit after this duration without cached outputs and connections." env:"CMDC_IDLE_TIMEOUT" default:"10m"`
	MaxTotalSize int64         `help:"Maximum total size in bytes of cached outputs." env:"CMDC_MAX_TOTAL_SIZE" default:"268435456"`
}

DaemonCmd is the options of the daemon subcommand.

type ExecCmd

type ExecCmd struct {
	Expires time.Duration `help:"How long the output is cached (e.g. 30s, 5m, 1h)." short:"e" required:"" env:"CMDC_EXPIRES"`
	KeyEnv  []string      `help:"Names of environment variables whose values are part of the cache key." env:"CMDC_KEY_ENV" placeholder:"NAME"`
	MaxSize int64         `help:"Maximum size in bytes of an output to cache." env:"CMDC_MAX_SIZE" default:"10485760"`
	Command []string      `arg:"" passthrough:"" help:"Command and its arguments."`
}

ExecCmd is the options of the exec subcommand.

type ExitError

type ExitError struct {
	Code int
	Err  error
}

ExitError tells the main function to exit with Code. Err is reported to the user if it is not nil.

func (*ExitError) Error

func (e *ExitError) Error() string

func (*ExitError) Unwrap

func (e *ExitError) Unwrap() error

type Request

type Request struct {
	Op      string        `json:"op"`
	Key     string        `json:"key,omitempty"`
	Label   string        `json:"label,omitempty"`
	Expires time.Duration `json:"expires,omitempty"`
	Chunks  []Chunk       `json:"chunks,omitempty"`
}

Request is a message from the client to the daemon.

type Response

type Response struct {
	Error   string  `json:"error,omitempty"`
	Version string  `json:"version,omitempty"`
	PID     int     `json:"pid,omitempty"`
	Hit     bool    `json:"hit,omitempty"`
	Stored  bool    `json:"stored,omitempty"`
	Cleared int     `json:"cleared,omitempty"`
	Chunks  []Chunk `json:"chunks,omitempty"`
	Status  *Status `json:"status,omitempty"`
}

Response is a message from the daemon to the client.

type Server

type Server struct {
	Socket string
	// IdleTimeout is the duration after which the server exits when it
	// has no cached entries and no connections.
	IdleTimeout  time.Duration
	MaxTotalSize int64
	SweepPeriod  time.Duration
	// contains filtered or unexported fields
}

Server is the resident process which keeps cached outputs in memory. It never executes commands; it only stores what clients send.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

Run listens on the socket and serves until ctx is canceled, a stop request is received, or the server becomes idle.

type Status

type Status struct {
	Running   bool         `json:"running"`
	Socket    string       `json:"socket"`
	PID       int          `json:"pid,omitempty"`
	Version   string       `json:"version,omitempty"`
	Uptime    string       `json:"uptime,omitempty"`
	Entries   int          `json:"entries"`
	TotalSize int64        `json:"total_size"`
	Items     []StatusItem `json:"items,omitempty"`
}

Status describes the state of the daemon.

type StatusItem

type StatusItem struct {
	Command   string `json:"command"`
	Size      int64  `json:"size"`
	Age       string `json:"age"`
	ExpiresIn string `json:"expires_in"`
}

StatusItem describes a cached entry.

type Stream

type Stream int

Stream identifies which output stream a chunk was written to.

const (
	Stdout Stream = 1
	Stderr Stream = 2
)

Directories

Path Synopsis
cmd
cmdc command

Jump to

Keyboard shortcuts

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