Insurgently

Use the Hub without Obsidian

The headless build, for scripts, evals and command-line tools that call the Hub with Obsidian closed. macOS only for keychain keys.

For people building on Model Hub.

The headless build is the Hub as a Node module: the same roles, tiers, limits, ledger and agent loop, with no Obsidian. Build it with npm run build:headless, which writes dist/headless.mjs. It is for your own scripts, evals and command-line tools, not for plugins: a plugin uses the API in Obsidian.

Settings#

It reads settings.json from ~/.config/model-hub, or from the folder MODEL_HUB_HOME names, and never writes it. It doesn't read your vault, so roles and tiers set in Obsidian don't reach it until you copy them: copy data.json from .obsidian/plugins/model-hub/ to that folder as settings.json, and copy it again after you change them. Its ledger is ledger.jsonl in the same folder, apart from the plugin's.

Keys#

Keys are never in settings.json. For each secret name the settings hold, such as model-hub-openrouter, the headless build looks in this order:

  1. The environment variable named after it, MODEL_HUB_OPENROUTER.
  2. The provider's usual variable, OPENROUTER_API_KEY, whatever the secret is called. A secret named open-router reads OPEN_ROUTER first, then OPENROUTER_API_KEY.
  3. On macOS only, the keychain: service model-hub, account the secret's name. Add one with security add-generic-password -U -s model-hub -a model-hub-openrouter -w, which asks for the key so it never reaches your shell history.

On Linux and Windows, set the environment variables.

When no key is found, the provider's line in refresh() says where it looked: both variables, the keychain entry, and whether the secret name came from your settings.json or is the default.

Traces#

A headless run sends traces like the plugin, with the same service name (obsidian-model-hub) and span attributes, when a collector is named:

  1. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, used as it is.
  2. OTEL_EXPORTER_OTLP_ENDPOINT, a collector's base address; /v1/traces is added.
  3. Otherwise the settings' telemetry, when Send traces is on in settings.json.

OTEL_SDK_DISABLED=true turns traces off. Spans go out in batches, so call await hub.flush() before your script ends; a process about to exit flushes once on its own. tracesTo says where they go.

Tools and agent runs#

registerTools takes a plugin id with no check that the plugin is loaded, and agent() runs the same loop as in Obsidian. A tool set to Ask is denied, as there is no one to answer. See Run a tool loop from your plugin.

Package
@insurgently/model-hub-api
Plugin id
model-hub
Needs
Obsidian 1.13.1, desktop
License
MIT