registry

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package registry is the discovery half of TaskYou's plugin story.

Installing a plugin used to require knowing that github.com/taskyou/plugins existed and typing its URL. A registry fixes that: it is a small JSON catalog of installable plugins, so every surface can offer *search* ("what can I install that posts to Slack?") before it offers *install*.

Three layers, most-trusted-last:

  1. A snapshot bundled into the binary (catalog.json, embedded). Discovery therefore works on a fresh install, offline, with zero configuration.
  2. The on-disk cache of each remote catalog, refreshed in the background.
  3. A live fetch of each remote catalog when the cache is older than the TTL.

Later layers overlay earlier ones by entry ID, so a published catalog can correct or extend what a shipped binary knows without a release. A failed fetch is never fatal — it degrades to the cache, then to the bundle.

Index

Constants

View Source
const BundledOrigin = "bundled"

BundledOrigin is the Origin.URL value for the embedded snapshot.

View Source
const CatalogEnv = "TY_PLUGIN_REGISTRY"

CatalogEnv overrides the remote catalog list with a comma-separated set of URLs. Set it to "" (explicitly empty) to use only the bundled snapshot — which is what the tests and air-gapped installs want.

View Source
const DefaultCatalogURL = "https://taskyou.dev/registry.json"

DefaultCatalogURL is the community catalog published from the TaskYou docs site (docs/registry.json in the main repo). It is the same document embedded in the binary, so the fetch only ever brings news.

View Source
const DefaultTTL = 6 * time.Hour

DefaultTTL is how long a cached catalog is served before it is re-fetched.

View Source
const SchemaVersion = 1

SchemaVersion is the catalog format this build reads and writes. Readers accept anything at or below it; a newer catalog is still parsed (unknown fields are ignored) so an old binary keeps working against a new catalog.

Variables

This section is empty.

Functions

func Categories

func Categories(entries []Entry) []string

Categories returns the distinct categories present, sorted.

func ConfiguredURLs

func ConfiguredURLs() []string

ConfiguredURLs returns the remote catalogs to consult. TY_PLUGIN_REGISTRY replaces the default list; setting it to an empty string disables remote catalogs entirely (bundled snapshot only).

func DefaultCacheDir

func DefaultCacheDir() string

DefaultCacheDir returns where fetched catalogs are cached.

func DidYouMean

func DidYouMean(entries []Entry, query string, limit int) []string

DidYouMean returns up to limit entry IDs closest to what the user typed, for the "no plugin named X" error. Cheap on purpose: the catalog is small and the goal is a hint, not spell-check.

Types

type Catalog

type Catalog struct {
	SchemaVersion int     `json:"schema_version"`
	Name          string  `json:"name,omitempty"`
	UpdatedAt     string  `json:"updated_at,omitempty"`
	Plugins       []Entry `json:"plugins"`
}

Catalog is a published set of installable plugins.

func Bundled

func Bundled() (*Catalog, error)

Bundled returns the catalog snapshot compiled into this binary.

func Parse

func Parse(data []byte) (*Catalog, error)

Parse decodes a catalog document and drops entries that could never be installed, so one bad row can't break discovery for the rest.

type Entry

type Entry struct {
	// ID is the stable, unique handle users type: `ty plugins add slack`.
	ID string `json:"id"`
	// Name is the human-facing title.
	Name string `json:"name"`
	// Description is one or two sentences on what the plugin does.
	Description string `json:"description"`
	// Author is a display name or GitHub handle.
	Author string `json:"author,omitempty"`
	// Source is the git URL to clone.
	Source string `json:"source"`
	// Subdir is the plugin's path inside Source, for collection repos that hold
	// many plugins. Empty means the repo root is the plugin.
	Subdir string `json:"subdir,omitempty"`
	// Homepage is a URL to read more (README, docs page).
	Homepage string `json:"homepage,omitempty"`
	// Category groups entries in the browser ("workflows", "notifications", …).
	Category string `json:"category,omitempty"`
	// Tags are free-form search keywords.
	Tags []string `json:"tags,omitempty"`
	// Provides lists the plugin's capability kinds ("workflow", "hook",
	// "action", "service", "routine") so a browser can say what you get.
	Provides []string `json:"provides,omitempty"`
	// Requires lists external prerequisites in plain words ("jq", "an ARC API
	// key") — the things that make an install fail confusingly if missing.
	Requires []string `json:"requires,omitempty"`
}

Entry is one installable plugin in a catalog.

It is deliberately *not* a plugin manifest: a manifest describes a plugin that is already on disk, an entry describes how to get one and why you would want it. Only ID, Name, Description and Source are required.

func Find

func Find(entries []Entry, id string) (Entry, bool)

Find returns the entry with the given ID (case-insensitive).

func Search(entries []Entry, query string) []Entry

Search ranks entries against a free-text query.

The query is split on whitespace and every term must hit *something* (AND), so "slack notify" doesn't return every plugin that merely mentions Slack. Within a term the strongest field wins — an ID match outranks a description mention — and scores are summed across terms. An empty query returns everything in catalog order, which is what a browser wants when the search box is empty.

func (Entry) CloneURL

func (e Entry) CloneURL() string

CloneURL is the git URL to clone for this entry.

func (Entry) DisplayName

func (e Entry) DisplayName() string

DisplayName returns Name, falling back to the ID.

func (Entry) Valid

func (e Entry) Valid() bool

Valid reports whether the entry has everything an install needs.

type Loader

type Loader struct {
	// URLs are the remote catalogs to overlay onto the bundled snapshot, in
	// increasing precedence order.
	URLs []string
	// CacheDir is where fetched catalogs are cached.
	CacheDir string
	// TTL is how long a cached copy is served before re-fetching.
	TTL time.Duration
	// HTTP is the client used for fetches.
	HTTP *http.Client
	// Now is the clock, injectable for tests.
	Now func() time.Time
}

Loader loads and caches catalogs. The zero value is not usable; call NewLoader (or Default) instead.

func Default

func Default() *Loader

Default returns the loader every surface uses: the configured remote catalogs (TY_PLUGIN_REGISTRY, else the community catalog) over the bundled snapshot, cached under the user cache dir.

func (*Loader) Load

func (l *Loader) Load(ctx context.Context) Result

Load returns the merged catalog: the bundled snapshot with each configured remote catalog overlaid by entry ID. It never fails — a dead network, a corrupt cache, or a malformed remote document all degrade to what is known locally, and the reason lands in the returned Origins.

func (*Loader) Refresh

func (l *Loader) Refresh(ctx context.Context) Result

Refresh is Load with every cache entry bypassed — what a "refresh catalog" button does.

type Match

type Match struct {
	Entry Entry
	Score int
}

Match is one search hit: the entry plus why it ranked where it did.

func SearchRanked

func SearchRanked(entries []Entry, query string) []Match

SearchRanked is Search, keeping the scores.

type Origin

type Origin struct {
	// URL is the remote catalog, or "bundled" for the embedded snapshot.
	URL string
	// From is "bundled", "cache", or "network".
	From string
	// Entries is how many entries this layer contributed (before overlay).
	Entries int
	// Age is how old the served copy is; zero for a fresh fetch.
	Age time.Duration
	// Err is why a remote fetch was not used, if it failed.
	Err error
}

Origin records where one layer of the loaded catalog came from, so a UI can say "3 sources, one stale" instead of silently serving month-old data.

type Result

type Result struct {
	Entries []Entry
	Origins []Origin
}

Result is a loaded catalog plus the provenance of each layer.

func (Result) Stale

func (r Result) Stale() bool

Stale reports whether every remote layer fell back to cache or bundle — the state worth telling the user about ("catalog may be out of date").

Jump to

Keyboard shortcuts

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