Configuration: paths, JVM lifecycle, provider presets, svar-native coercion, config file I/O, and the active-provider state every channel reads through.
Two halves:
~/.vis/: config.edn, vis.mdb/, vis.log.
init! / init-cli! / shutdown! redirect stdout/stderr into
the log file and bring up Telemere's file handler.active-config atom holds the
currently-selected provider config; current-config,
active-provider, active-model, provider-ids,
has-provider? are the read API. reload-config! re-reads
from disk.The ->svar-provider helper resolves :api-key lazily by calling
the registered provider's :provider/get-token-fn, so the
token-refresh policy stays inside each provider implementation
instead of leaking up here.
Configuration: paths, JVM lifecycle, provider presets, svar-native
coercion, config file I/O, and the active-provider state every
channel reads through.
Two halves:
- On-disk config under `~/.vis/`: `config.edn`, `vis.mdb/`, `vis.log`.
`init!` / `init-cli!` / `shutdown!` redirect stdout/stderr into
the log file and bring up Telemere's file handler.
- Live process state: the `active-config` atom holds the
currently-selected provider config; `current-config`,
`active-provider`, `active-model`, `provider-ids`,
`has-provider?` are the read API. `reload-config!` re-reads
from disk.
The `->svar-provider` helper resolves `:api-key` lazily by calling
the registered provider's `:provider/get-token-fn`, so the
token-refresh policy stays inside each provider implementation
instead of leaking up here.(->svar-model model)(->svar-model provider-id model)Coerce a model representation to svar-native {:name str}.
Coerce a model representation to svar-native `{:name str}`.
(->svar-provider provider)Coerce a provider map to svar-native shape (:id, :api-key,
:base-url, :api-style, :models, optional :responses-path,
optional :llm-headers).
svar's make-router calls normalize-provider which auto-resolves
:base-url from svar's KNOWN_PROVIDERS table for built-in
providers, so we forward :base-url ONLY when the provider map
has one explicitly (vis-only providers like :github-models,
user overrides, or OAuth-supplied URLs). For known providers
svar fills in the URL itself - stop fighting it.
When :api-key is nil, look the provider up in the global
provider registry (registry.clj) and call its
:provider/get-token-fn to resolve a usable token. Each provider
implementation handles its own auth lifecycle (OAuth refresh,
env-var fallback, provider-specific headers, ...) so this fn stays
provider-agnostic and never references a concrete provider ns by
name.
Coerce a provider map to svar-native shape (`:id`, `:api-key`, `:base-url`, `:api-style`, `:models`, optional `:responses-path`, optional `:llm-headers`). svar's `make-router` calls `normalize-provider` which auto-resolves `:base-url` from svar's `KNOWN_PROVIDERS` table for built-in providers, so we forward `:base-url` ONLY when the provider map has one explicitly (vis-only providers like `:github-models`, user overrides, or OAuth-supplied URLs). For known providers svar fills in the URL itself - stop fighting it. When `:api-key` is nil, look the provider up in the global provider registry (registry.clj) and call its `:provider/get-token-fn` to resolve a usable token. Each provider implementation handles its own auth lifecycle (OAuth refresh, env-var fallback, provider-specific headers, ...) so this fn stays provider-agnostic and never references a concrete provider ns by name.
(active-model)Return the primary model name string, or nil.
Return the primary model name string, or nil.
(active-provider)Return the first (primary) provider from config, or nil.
Return the first (primary) provider from config, or nil.
(current-config)Return the current provider config. Loads from disk on first call.
Return the current provider config. Loads from disk on first call.
(display-label pid)Human-readable label for a provider id. Never persisted.
Human-readable label for a provider id. Never persisted.
(extension-env-overrides)Persisted extension environment overrides from ~/.vis/config.edn
under :environment. Keys are environment variable names as
strings. Values are strings. This does NOT mutate the process
environment; extension code should call extension-env-value when
it wants config-over-env resolution.
Persisted extension environment overrides from `~/.vis/config.edn` under `:environment`. Keys are environment variable names as strings. Values are strings. This does NOT mutate the process environment; extension code should call `extension-env-value` when it wants config-over-env resolution.
(extension-env-status name)Return source and value metadata for an extension-declared env var.
:source is one of :config, :env, or :unset.
Return source and value metadata for an extension-declared env var. `:source` is one of `:config`, `:env`, or `:unset`.
(extension-env-value name)Resolve an extension-declared env var as config override -> real env. Blank/missing values return nil.
Resolve an extension-declared env var as config override -> real env. Blank/missing values return nil.
(first-run?)True on a genuine FIRST run: no provider configured AND no global
~/.vis/config.edn has ever been written. Distinguishes the full welcome
(brand-new user) from a returning user who merely has no provider right now
(e.g. removed their only one).
True on a genuine FIRST run: no provider configured AND no global `~/.vis/config.edn` has ever been written. Distinguishes the full welcome (brand-new user) from a returning user who merely has no provider right now (e.g. removed their only one).
(frame-buffered-tty-out raw)Wrap the raw tty stream so a whole repaint reaches the terminal as ONE atomic write instead of one write(2) syscall PER CELL.
Lanterna's refreshByDelta calls putString/setCursorPosition per
changed cell and only flushes once at the end of refresh. On a raw
FileOutputStream every one of those calls is its own syscall straight
to the tty, so the terminal renders PARTIAL frames mid-repaint: a fold
toggle that shifts the transcript reads as a whole-screen flicker and a
transient content jump. Buffering until flush collapses the frame to
one write, and the DEC 2026 bracket makes the terminal hold rendering
until the frame is complete even when the kernel chunks the write.
Everything vis writes to the tty outside Lanterna (SGR-mouse /
bracketed-paste toggles, OSC 11 background, the :bell fx, the panic
PrintStream) already flushes explicitly, so nothing can sit in the
buffer across frames.
Wrap the raw tty stream so a whole repaint reaches the terminal as ONE atomic write instead of one write(2) syscall PER CELL. Lanterna's `refreshByDelta` calls `putString`/`setCursorPosition` per changed cell and only `flush`es once at the end of `refresh`. On a raw `FileOutputStream` every one of those calls is its own syscall straight to the tty, so the terminal renders PARTIAL frames mid-repaint: a fold toggle that shifts the transcript reads as a whole-screen flicker and a transient content jump. Buffering until `flush` collapses the frame to one write, and the DEC 2026 bracket makes the terminal hold rendering until the frame is complete even when the kernel chunks the write. Everything vis writes to the tty outside Lanterna (SGR-mouse / bracketed-paste toggles, OSC 11 background, the `:bell` fx, the panic PrintStream) already flushes explicitly, so nothing can sit in the buffer across frames.
(init!)Redirect System/out and System/err to the log file. Lanterna uses tty-in / tty-out for terminal I/O. Call from the TUI entry point.
Redirect System/out and System/err to the log file. Lanterna uses tty-in / tty-out for terminal I/O. Call from the TUI entry point.
(init-cli!)Logging init for non-TUI processes. Same redirects as init! but without the shutdown hook (CLI commands run to completion and exit).
Logging init for non-TUI processes. Same redirects as init! but without the shutdown hook (CLI commands run to completion and exit).
(load-config)Load provider config in svar-native syntax from ~/.vis/config.edn.
Load provider config in svar-native syntax from `~/.vis/config.edn`.
(load-config-raw)Load raw config as global ~/.vis/config.edn, then the project-local
.vis/config.edn overlay, then the root vis.edn overlay. Later sources
win; nested maps merge; scalar/vector values replace.
Load raw config as global `~/.vis/config.edn`, then the project-local `.vis/config.edn` overlay, then the root `vis.edn` overlay. Later sources win; nested maps merge; scalar/vector values replace.
(load-global-config-raw)Load only the global ~/.vis/config.edn map (or nil on read/parse error).
Load only the global `~/.vis/config.edn` map (or nil on read/parse error).
(load-project-config-raw)Load only <invocation-cwd>/.vis/config.edn (or nil on read/parse error).
Load only `<invocation-cwd>/.vis/config.edn` (or nil on read/parse error).
(load-project-root-config-raw)Load only <invocation-cwd>/vis.edn (or nil on read/parse error).
Load only `<invocation-cwd>/vis.edn` (or nil on read/parse error).
(model-name model)Extract the model name string from a model (string or {:name str}).
Extract the model name string from a model (string or `{:name str}`).
(project-config-path)Project-local config override path. bin/vis preserves the invocation cwd
as JVM user.dir, so this resolves to <project>/.vis/config.edn for the
user project, not the Vis install checkout.
Project-local config override path. `bin/vis` preserves the invocation cwd as JVM `user.dir`, so this resolves to `<project>/.vis/config.edn` for the user project, not the Vis install checkout.
(project-root-config-path)Project-local vis.edn at the invocation cwd root. Same user.dir
basis as project-config-path, so this resolves to <project>/vis.edn
for the user project — a visible, root-level alternative to the hidden
.vis/config.edn.
Project-local `vis.edn` at the invocation cwd root. Same `user.dir` basis as `project-config-path`, so this resolves to `<project>/vis.edn` for the user project — a visible, root-level alternative to the hidden `.vis/config.edn`.
(provider-base-url provider)Resolve base-url for a provider: explicit field on the provider map first (so user-supplied URLs win), then the merged catalog.
Resolve base-url for a provider: explicit field on the provider map first (so user-supplied URLs win), then the merged catalog.
(provider-configured?)True when at least one provider is configured (global or project config).
The single predicate entry points use to branch onboarding vs normal start —
never trips the resolve-config throw.
True when at least one provider is configured (global or project config). The single predicate entry points use to branch onboarding vs normal start — never trips the `resolve-config` throw.
(provider-ids)Set of configured provider :id keywords.
Set of configured provider `:id` keywords.
(provider-model-visible? provider-id model-id)True when svar's provider-scoped model filters allow this model id.
True when svar's provider-scoped model filters allow this model id.
(provider-presets)All known provider presets, sorted for the 'Add Provider' picker.
All known provider presets, sorted for the 'Add Provider' picker.
(provider-template pid)Preset descriptor for a provider id, merged from a provider extension's metadata and svar's catalog. Returns nil for unknown or intentionally removed ids.
Preset descriptor for a provider id, merged from a provider extension's metadata and svar's catalog. Returns nil for unknown or intentionally removed ids.
(remove-config-provider! provider-id)(remove-config-provider! provider-id source)Remove every persisted provider entry for provider-id from
~/.vis/config.edn, preserving unrelated global config keys. Project-local
.vis/config.edn is an overlay and is not edited by this writer. Returns
true when the global file changed.
Remove every persisted provider entry for `provider-id` from `~/.vis/config.edn`, preserving unrelated global config keys. Project-local `.vis/config.edn` is an overlay and is not edited by this writer. Returns true when the global file changed.
(resolve-config)(resolve-config explicit-config)Resolve provider config: explicit -> ~/.vis/config.edn.
Throws when nothing is available.
Resolve provider config: explicit -> `~/.vis/config.edn`. Throws when nothing is available.
(resolve-db-spec)(resolve-db-spec explicit-db-spec)Resolve DB spec: explicit -> VIS_DB_PATH env -> :db-spec from
config.edn -> default sqlite at ~/.vis/vis.mdb.
Resolve DB spec: explicit -> VIS_DB_PATH env -> `:db-spec` from config.edn -> default sqlite at `~/.vis/vis.mdb`.
(router-opts config)Extracts svar/make-router opts from a Vis config map.
Reads the :router block from ~/.vis/config.edn:
{:router
{:rate-limit {:same-provider-delays-ms [2000 3000 6000]
:fallback-after-ms 30000
:respect-retry-after? true
:fallback-provider? true}
:network {:timeout-ms 300000 :idle-timeout-ms 45000}
:budget {:max-tokens 1000000 :max-cost 5.0}}}
Returns {} when no :router block is present so svar's built-in
defaults win. Unknown keys are dropped — only the keys svar's
make-router knows about flow through.
See com.blockether.svar.internal.router/make-router for the
authoritative key reference.
Extracts `svar/make-router` opts from a Vis config map.
Reads the `:router` block from `~/.vis/config.edn`:
```clojure
{:router
{:rate-limit {:same-provider-delays-ms [2000 3000 6000]
:fallback-after-ms 30000
:respect-retry-after? true
:fallback-provider? true}
:network {:timeout-ms 300000 :idle-timeout-ms 45000}
:budget {:max-tokens 1000000 :max-cost 5.0}}}
```
Returns `{}` when no `:router` block is present so svar's built-in
defaults win. Unknown keys are dropped — only the keys svar's
`make-router` knows about flow through.
See `com.blockether.svar.internal.router/make-router` for the
authoritative key reference.(save-config! config)(save-config! config source)Persist provider config to ~/.vis/config.edn.
When the first provider (the active provider) changes, the newly
selected provider's optional :provider/on-selected-fn is invoked
after the file write. Hook failures are logged and never prevent
config persistence.
Persist provider config to `~/.vis/config.edn`. When the first provider (the active provider) changes, the newly selected provider's optional `:provider/on-selected-fn` is invoked after the file write. Hook failures are logged and never prevent config persistence.
(save-extension-env-var! name value)Persist or clear one extension env override in ~/.vis/config.edn.
Blank/nil value removes the override, revealing the process env
value again if one exists. Preserves all other config keys.
Persist or clear one extension env override in `~/.vis/config.edn`. Blank/nil `value` removes the override, revealing the process env value again if one exists. Preserves all other config keys.
(shutdown!)Flush and stop all telemere handlers. Call after the TUI screen stops.
Flush and stop all telemere handlers. Call after the TUI screen stops.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |