cli

package
v0.4.3 Latest Latest
Warning

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

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 37 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultAPIURL = "https://api.texops.dev"
)

Variables

View Source
var (
	ErrAuthorizationPending = fmt.Errorf("authorization pending")
	ErrDeviceCodeExpired    = fmt.Errorf("device code expired")
	ErrTokenConflict        = fmt.Errorf("token name already exists")
	ErrTokenNotFound        = fmt.Errorf("token not found")
)
View Source
var AllowedCompilers = []string{"pdflatex", "xelatex", "lualatex", "latex", "platex", "uplatex"}

AllowedCompilers lists valid compiler values for .texops.yaml.

View Source
var CredentialsFilePath = credentialsFilePath
View Source
var KeyringGet = keyring.Get
View Source
var KeyringSet = keyring.Set
View Source
var NewInstanceClientFn = func(instanceURL, jwt string) *InstanceClient {
	return NewInstanceClient(instanceURL, jwt)
}
View Source
var OpenBrowser = openBrowser
View Source
var PollInterval = 5 * time.Second

PollInterval controls how often PollToken is called. Exposed for testing.

View Source
var RunBuild = runBuild
View Source
var TexliveVersions = []string{"2025", "2024", "2023", "2022", "2021", "2020", "2019", "2018", "2017", "2016", "2015", "2014", "2013", "2012"}

TexliveVersions lists available TexLive distribution versions (newest first).

Functions

func FormatSize

func FormatSize(bytes int64) string

func HashContent

func HashContent(data []byte) string

func ResolveAPIURL

func ResolveAPIURL(cfg Config) string

func ResolveAuth

func ResolveAuth() (string, error)

ResolveAuth returns a Bearer token for API authentication. It checks in order: TX_API_TOKEN env var, JWT from credentials file, JWT from keyring. Each source can override the ones below it. If a JWT is found but expired, it falls through to the next source.

func TotalSize

func TotalSize(files []FileEntry) int64

Types

type APIClient

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

func NewAPIClient

func NewAPIClient(baseURL, apiKey string) *APIClient

func NewUnauthenticatedAPIClient

func NewUnauthenticatedAPIClient(baseURL string) *APIClient

func (*APIClient) CreateAPIToken

func (c *APIClient) CreateAPIToken(name string, expiresIn *int64) (APITokenResponse, error)

func (*APIClient) CreateProject

func (c *APIClient) CreateProject(ctx context.Context, name, distVersion, projectKey string) (CreateProjectResponse, error)

func (*APIClient) DeleteAPIToken

func (c *APIClient) DeleteAPIToken(tokenID string) error

func (*APIClient) GetSession

func (c *APIClient) GetSession(ctx context.Context, projectID, distributionVersion string) (SessionResponse, error)

func (*APIClient) ListAPITokens

func (c *APIClient) ListAPITokens() ([]APITokenListItem, error)

func (*APIClient) PollToken

func (c *APIClient) PollToken(deviceCode string) (TokenResponse, error)

func (*APIClient) RefreshToken

func (c *APIClient) RefreshToken(jwt string) (TokenResponse, error)

func (*APIClient) RequestDeviceCode

func (c *APIClient) RequestDeviceCode() (DeviceCodeResponse, error)

func (*APIClient) SetHTTPClient

func (c *APIClient) SetHTTPClient(hc *http.Client)

func (*APIClient) Whoami

func (c *APIClient) Whoami() (WhoamiResponse, error)

type APIError

type APIError struct {
	StatusCode int
	Body       string
}

APIError represents an HTTP error response from the API.

func (*APIError) Error

func (e *APIError) Error() string

type APITokenListItem

type APITokenListItem struct {
	ID         string  `json:"id"`
	Name       string  `json:"name"`
	Prefix     string  `json:"prefix"`
	ExpiresAt  *string `json:"expires_at,omitempty"`
	LastUsedAt *string `json:"last_used_at,omitempty"`
	CreatedAt  string  `json:"created_at"`
}

APITokenListItem represents a token in a list response.

type APITokenResponse

type APITokenResponse struct {
	Token     string  `json:"token"`
	ID        string  `json:"id"`
	Name      string  `json:"name"`
	Prefix    string  `json:"prefix"`
	ExpiresAt *string `json:"expires_at,omitempty"`
	CreatedAt string  `json:"created_at"`
}

APITokenResponse is the response from creating an API token.

type BuildCmd

type BuildCmd struct {
	Args    struct{ Names []string } `positional-args:"true"`
	NoCache bool                     `long:"no-cache" description:"Clear build cache and rebuild from scratch"`
	Live    bool                     `long:"live" description:"Watch for changes and rebuild automatically"`
	UI      *UI                      `no-flag:"true"`
}

func (*BuildCmd) Execute

func (cmd *BuildCmd) Execute(args []string) error

type BuildDoneEvent

type BuildDoneEvent struct {
	Status  string `json:"status"`
	PdfURL  string `json:"pdfUrl,omitempty"`
	Message string `json:"message,omitempty"`
	BuildID string `json:"build_id,omitempty"`
}

func ParseSSEStream

func ParseSSEStream(reader io.Reader, onLog func(string)) (BuildDoneEvent, error)

type Config

type Config struct {
	ProjectKey string     `yaml:"project_key,omitempty"`
	Texlive    string     `yaml:"texlive"`
	Compiler   string     `yaml:"compiler,omitempty"`
	APIURL     string     `yaml:"api_url,omitempty"`
	Documents  []Document `yaml:"documents"`
}

func LoadConfig

func LoadConfig(dir string) (Config, error)

func ParseConfig

func ParseConfig(content string) (Config, error)

func (Config) DocumentByName

func (c Config) DocumentByName(name string) (Document, bool)

DocumentByName looks up a document by name.

type CreateProjectResponse

type CreateProjectResponse struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Texlive string `json:"distribution_version"`
}

type DeviceCodeResponse

type DeviceCodeResponse struct {
	DeviceCode      string `json:"device_code"`
	UserCode        string `json:"user_code"`
	VerificationURL string `json:"verification_url"`
	ExpiresIn       int    `json:"expires_in"`
	Interval        int    `json:"interval"`
}

type Document

type Document struct {
	Name      string `yaml:"name"`
	Main      string `yaml:"main"`
	Directory string `yaml:"directory,omitempty"`
	Output    string `yaml:"output,omitempty"`
	Texlive   string `yaml:"texlive,omitempty"`
	Compiler  string `yaml:"compiler,omitempty"`
}

func DiscoverDocuments

func DiscoverDocuments(dir string) ([]Document, error)

DiscoverDocuments recursively finds .tex files containing \documentclass in dir, respecting .gitignore and .txignore patterns. Returns a Document for each discovered file with name derived from the path stem.

type FileEntry

type FileEntry struct {
	Path string `json:"path"`
	Hash string `json:"hash"`
	Size int64  `json:"-"`
}

func CollectFiles

func CollectFiles(ctx context.Context, dir string) ([]FileEntry, error)

type FileWatcher added in v0.4.3

type FileWatcher struct {
	Events chan string
	Errors chan error
	// contains filtered or unexported fields
}

func NewFileWatcher added in v0.4.3

func NewFileWatcher(dir string, excludes []string) (*FileWatcher, error)

func (*FileWatcher) Close added in v0.4.3

func (w *FileWatcher) Close() error

func (*FileWatcher) Run added in v0.4.3

func (w *FileWatcher) Run(ctx context.Context)

type InitCmd

type InitCmd struct {
	Texlive  string `long:"texlive" description:"TexLive distribution version"`
	Compiler string `long:"compiler" description:"LaTeX compiler (pdflatex, xelatex, lualatex, latex, platex, uplatex)"`
	Main     string `long:"main" default:"main.tex" description:"Main TeX file (fallback when no .tex files discovered)"`
	UI       *UI    `no-flag:"true"`
}

func (*InitCmd) Execute

func (cmd *InitCmd) Execute(args []string) error

type InstanceClient

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

func NewInstanceClient

func NewInstanceClient(instanceURL, jwt string) *InstanceClient

func (*InstanceClient) Build

func (c *InstanceClient) Build(ctx context.Context, projectID, main, directory, distVersion, compiler string, buildOptions map[string]string, onLog func(string)) (BuildDoneEvent, error)

func (*InstanceClient) BuildWithArgs

func (c *InstanceClient) BuildWithArgs(ctx context.Context, projectID, main, directory, distVersion, compiler string, args []string, buildOptions map[string]string, onLog func(string)) (BuildDoneEvent, error)

func (*InstanceClient) DownloadPDF

func (c *InstanceClient) DownloadPDF(ctx context.Context, projectID, buildID, outputPath string) error

func (*InstanceClient) SetHTTPClient

func (c *InstanceClient) SetHTTPClient(hc *http.Client)

func (*InstanceClient) Sync

func (c *InstanceClient) Sync(ctx context.Context, projectID string, files []FileEntry) (SyncResult, error)

func (*InstanceClient) Upload

func (c *InstanceClient) Upload(ctx context.Context, projectID, projectDir string, filePaths []string, onProgress func(sent, total int64)) error

func (*InstanceClient) UploadRaw

func (c *InstanceClient) UploadRaw(ctx context.Context, projectID string, tarData []byte) error

type LoginCmd

type LoginCmd struct {
	UI *UI `no-flag:"true"`
}

func (*LoginCmd) Execute

func (cmd *LoginCmd) Execute(args []string) error

type Options

type Options struct {
	Login  LoginCmd  `command:"login" description:"Log in to TexOps"`
	Init   InitCmd   `command:"init" description:"Initialize a new TexOps project"`
	Build  BuildCmd  `command:"build" description:"Build the LaTeX project"`
	Status StatusCmd `command:"status" description:"Show authentication status"`
	Token  TokenCmd  `command:"token" description:"Manage API tokens"`
}

type ProgressBar

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

ProgressBar represents an in-progress upload with byte-level tracking.

func (*ProgressBar) Abort

func (pb *ProgressBar) Abort()

Abort stops the progress bar without a success message. Use this when the operation has failed.

func (*ProgressBar) Done

func (pb *ProgressBar) Done()

Done stops the progress bar and prints a completion line.

func (*ProgressBar) Reader

func (pb *ProgressBar) Reader(r io.Reader) io.Reader

Reader wraps an io.Reader to feed progress updates to the progress bar.

func (*ProgressBar) Update

func (pb *ProgressBar) Update(fraction float64)

Update sends a progress fraction (0.0-1.0) to the progress bar.

type SessionResponse

type SessionResponse struct {
	InstanceURL string `json:"instance_url"`
	JWT         string `json:"jwt"`
	CacheCold   bool   `json:"cache_cold"`
}

type Spinner

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

Spinner represents an in-progress async operation with animated feedback.

func (*Spinner) Cancel added in v0.4.3

func (s *Spinner) Cancel()

Cancel stops the spinner without printing anything.

func (*Spinner) Fail

func (s *Spinner) Fail(errMsg string)

Fail ends the spinner and prints an error message.

func (*Spinner) Stop

func (s *Spinner) Stop(successMsg string)

Stop ends the spinner and prints a success message.

type StatusCmd

type StatusCmd struct {
	UI *UI `no-flag:"true"`
}

func (*StatusCmd) Execute

func (cmd *StatusCmd) Execute(args []string) error

type SyncResult

type SyncResult struct {
	Missing []string `json:"missing"`
}

type TokenCmd

type TokenCmd struct {
	Create TokenCreateCmd `command:"create" description:"Create a new API token"`
	List   TokenListCmd   `command:"list" description:"List API tokens"`
	Delete TokenDeleteCmd `command:"delete" description:"Delete an API token"`
}

type TokenCreateCmd

type TokenCreateCmd struct {
	Name      string `long:"name" description:"Name for the token"`
	ExpiresIn string `long:"expires-in" description:"Token expiry duration (e.g. 30d, 90d, 1y)"`
	NoExpiry  bool   `long:"no-expiry" description:"Create token with no expiry"`
	UI        *UI    `no-flag:"true"`
}

func (*TokenCreateCmd) Execute

func (cmd *TokenCreateCmd) Execute(args []string) error

type TokenDeleteCmd

type TokenDeleteCmd struct {
	UI *UI `no-flag:"true"`
}

func (*TokenDeleteCmd) Execute

func (cmd *TokenDeleteCmd) Execute(args []string) error

type TokenErrorResponse

type TokenErrorResponse struct {
	Error string `json:"error"`
}

type TokenListCmd

type TokenListCmd struct {
	UI *UI `no-flag:"true"`
}

func (*TokenListCmd) Execute

func (cmd *TokenListCmd) Execute(args []string) error

type TokenResponse

type TokenResponse struct {
	JWT       string `json:"jwt"`
	ExpiresAt string `json:"expires_at"`
}

type UI

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

func NewUI

func NewUI(out io.Writer) *UI

func NewUIWithOptions

func NewUIWithOptions(out io.Writer, isTTY bool, in io.Reader) *UI

NewUIWithOptions creates a UI with explicit TTY mode and input reader. Used in tests to simulate TTY/non-TTY behavior. Error output goes to out for easy test capture.

func NewUIWithTTYOptions added in v0.4.0

func NewUIWithTTYOptions(out io.Writer, outIsTTY, stdinIsTTY bool, in io.Reader) *UI

NewUIWithTTYOptions creates a UI with separate output-TTY and stdin-TTY flags. Use this when testing code that distinguishes IsTTY() from IsInteractive().

func (*UI) Confirm

func (ui *UI) Confirm(msg string) (bool, error)

func (*UI) DimInfo

func (ui *UI) DimInfo(msg string)

func (*UI) Errorf

func (ui *UI) Errorf(format string, args ...any)

func (*UI) IsInteractive added in v0.4.0

func (ui *UI) IsInteractive() bool

func (*UI) IsTTY

func (ui *UI) IsTTY() bool

func (*UI) Log

func (ui *UI) Log(line string)

func (*UI) Out

func (ui *UI) Out() io.Writer

func (*UI) Progress

func (ui *UI) Progress(label string, total int64) *ProgressBar

Progress starts an animated progress bar with the given label and total bytes. In non-TTY mode, it prints periodic percentage milestones. Call Reader() to wrap an io.Reader for tracking, then Done() when finished.

func (*UI) Select

func (ui *UI) Select(label string, options []string) (int, error)

Select displays an interactive selection list. In TTY mode, uses a simple numbered list with keyboard input. In non-TTY mode, returns an error.

func (*UI) SelectDocuments

func (ui *UI) SelectDocuments(docs []Document) ([]Document, error)

SelectDocuments shows an interactive checkbox list for selecting documents. All documents are pre-selected. Returns the selected documents. In non-TTY mode, returns all documents without interaction.

func (*UI) SelectOne

func (ui *UI) SelectOne(label string, options []string) (int, error)

SelectOne displays an interactive single-select list using Bubble Tea. Returns the index of the selected option, or an error if cancelled or non-TTY.

func (*UI) Spin

func (ui *UI) Spin(msg string) *Spinner

Spin starts an animated spinner with the given message. In non-TTY mode, it just prints the message. Call Stop() or Fail() on the returned Spinner to finish.

func (*UI) Status

func (ui *UI) Status(msg string)

func (*UI) Success

func (ui *UI) Success(msg string)

func (*UI) TextInput

func (ui *UI) TextInput(label string) (string, error)

TextInput displays an interactive text input prompt using Bubble Tea. Returns the entered text, or an error if cancelled or non-TTY.

type WhoamiResponse

type WhoamiResponse struct {
	UserID     string  `json:"user_id"`
	Email      string  `json:"email,omitempty"`
	AuthMethod string  `json:"auth_method"`
	ExpiresAt  *string `json:"expires_at,omitempty"`
}

Jump to

Keyboard shortcuts

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