{% extends "base.html" %} {% block title %}Plugins · foilstack{% endblock %} {% block status %}{{ "{:,}".format(catalog_cards) }} catalogue cards · {{ "{:,}".format(vector_count) }} vectors · encoder {{ 'ready' if encoder else 'unreachable' }}{% endblock %} {% block screen %}

Plugins

sources supply the catalogue · enrichers recover its past · exporters write the file a marketplace accepts
{# State first, manifest after. Everything below this panel is equally true of an install that has never ingested a card, which is what made this screen a slower way of reading `foilstack plugins`. This panel is the part that can only be answered by looking at the database. #}
Catalogue what the installed plugins have actually fetched
encoder
{{ 'ready' if encoder else 'unreachable' }}
{{ encoder_url }}
model
{{ encoder.get('model', embed_model) if encoder else embed_model }}
{{ 'reported by the encoder' if encoder else 'configured, unverified' }}
catalogue
{{ "{:,}".format(catalog_cards) }}
cards ingested
encoded
{{ "{:,}".format(vector_count) }}
{% if unencoded %}{{ "{:,}".format(unencoded) }} still to encode{% else %}every card searchable{% endif %}
{% if catalogue %} {% for row in catalogue %} {# A game with cards and no vectors cannot be matched at all, and the failure is silent at scan time — every photograph simply misses. #} {# "never" is not a stale cache. TCGCSV mirrors the current day only, so every day this has not run is a day of history that cannot be bought back for any game but Magic. #} {% if row.prices %} {% else %} {% endif %} {% if row.backfill %} {% elif row.enricher %} {% else %} {# Not a job somebody forgot: no installed enricher publishes this game's past, so there is nothing here to run. #} {% endif %} {% endfor %}
GAMESOURCE CARDSENCODED PRICESHISTORY
{{ row.label }} {{ row.source }} {{ "{:,}".format(row.cards) }} {{ "{:,}".format(row.encoded) }} {{ row.prices.ago }}never {{ row.backfill.ago }} not run

Prices is when the sync last ran, not when a price last moved — a run that correctly changed nothing still counts. History is the backfill, which only ever adds days a sync never recorded.

{% else %}
nothing ingested yet — run {{ sources[0].command if sources else 'foilstack ingest' }}, then foilstack embed
{% endif %}
Sources supply the catalogue: which cards exist and what they look like
{% for p in sources %} {% endfor %}
PLUGINGAMESINGEST
{{ p.name }} {{ p.games | join(', ') }} {{ p.command }}

A source plugin runs code and reaches the network. Nothing is installed automatically and there is no registry. You add one deliberately, the same way you would any other dependency.

Enrichers add to a catalogue somebody else ingested
{% if enrichers %} {% for p in enrichers %} {% endfor %}
PLUGINGAMESADDS TOBACKFILL
{{ p.name }} {{ p.games | join(', ') }} {{ p.matches_source }} {{ p.command }}

An enricher owns no rows. It joins onto cards a source already ingested, by the upstream id that source recorded — so it names the one source it can speak to, and the writer refuses any other. That is what lets MTGJSON in: it publishes no card images and could never satisfy a source's contract, but it holds three months of daily Magic prices this install would otherwise have no way to learn. It is a repair, not a backup, and it may only ever add a day a sync never recorded.

{% else %}
no enrichment plugins installed
{% endif %}
Exporters turn inventory into a file a marketplace accepts
{% for spec in exporters %} {% endfor %}
PLUGINCOLUMNSFILE
{{ spec.label }}
{{ spec.description }}
{{ spec.columns | length }} {{ spec.filename }} Export

Exporters are TOML column mappings, not code. Adding a marketplace means writing a file anyone can read in ten seconds and be sure it does nothing else.

{% endblock %}