settings

package
v0.1.554 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package settings keeps the few preferences the desktop app has: which palette to paint with, which language to speak, how the agents are arranged. Everything else magpie knows is derived from the agents' own files.

The file is ~/.config/magpie/settings.json; a missing file means "follow the system" for both.

Index

Constants

This section is empty.

Variables

View Source
var (
	Themes     = []string{"system", "light", "dark"}
	Langs      = []string{"system", "en", "zh"}
	Trays      = []string{"panel", "window"}
	Currencies = []string{"usd", "cny"}
	// Warmups are CodexWarmup's and ClaudeWarmup's values, off as "".
	Warmups = []string{"", "week", "all"}
	// TrayEvery are TrayUsageEvery's values, in minutes.
	TrayEvery = []int{1, 3, 5, 10, 30}
	// TextSizes are TextSize's values, in percent. None is under 100: the
	// webviews' zoom on Windows and Linux (Wails' SetZoom) goes no lower.
	TextSizes = []int{100, 110, 125, 150}
)

Themes and Langs are the accepted values, in the order the UI offers them.

Functions

func Arrange added in v0.1.60

func Arrange[T any](s Settings, items []T, id func(T) string) (shown, hidden []T)

Arrange puts items in the order the user gave the agents, those named first and the rest after in the order they came, and splits off the hidden ones, which keep that order too. id names an item's agent.

func CarryPerModel added in v0.1.496

func CarryPerModel(in, cur *Settings)

CarryPerModel puts cur's per-model maps into in's, whole as they are: the ones a page that sends only its own choices would otherwise save as nothing, and so lose. Every per-model map is carried, whichever page wrote it, so a map added to the settings later needs nothing said of it here.

func CheckModelKey added in v0.1.496

func CheckModelKey(what, key string) error

CheckModelKey is whether a key of one of the per-model maps names a model the way all of them do: "<provider id>/<model id>" — or "<provider id>/*" for all of that provider's models. A model id may have slashes of its own (vendor/model), so only the first one ends the provider's id. what names the setting being checked, for the error.

It is the one rule a model key is held to, and it lives here, beside the maps it keys. Every change that takes such a key from the user checks it here rather than spelling the shape out again, so what is written into a map and what is refused come out of the same rule — one rule, and not one each branch can drift from on its own.

func CheckModelPrice added in v0.1.504

func CheckModelPrice(key string, m ModelPrice) error

CheckModelPrice is whether a price the user gives is one magpie will bill a call at: the key names a model the way the per-model maps key one, and every part of the price is a number a vendor could charge, naming the first that is not. It is where a price is checked rather than in Save: one a hand edit left broken in the file is skipped where it is read, and must not stop an unrelated setting from being saved. That covers a part that is missing, which is what a file can be edited into: JSON has no NaN or Infinity, and Load discards the whole file rather than half of it, so neither reaches here. Both are still refused, from a price typed in or passed in.

func CheckProxy added in v0.1.418

func CheckProxy(p string) error

CheckProxy says whether p is a proxy setting magpie takes: "" (follow), "direct", or an http://, https:// or socks5:// address (host:port meaning http). The global Proxy and a provider's own are both checked with it.

func Clock added in v0.1.289

func Clock(at string) (hour, min int, ok bool)

Clock reads a time of day, "06:00" (seconds, as a time field may send them, are dropped), as its hour and minute.

func Dir

func Dir() string

Dir is the folder every magpie file lives in.

func Migrate

func Migrate()

Migrate carries the files of an install that predates the name over: ~/.config/dial and ~/.cache/dial become ~/.config/magpie and ~/.cache/magpie. It copies rather than moves, so a dial that is still running keeps working, and it only fills folders that do not exist yet. The old folders can be deleted once nothing uses them.

func Path

func Path() string

Path is the settings file.

func PerModelKeys added in v0.1.496

func PerModelKeys(s *Settings, fn func(name string, m reflect.Value))

PerModelKeys calls fn with the name of every per-model map of s and the map itself, the settings' own values and not copies, so fn may change them (see ModelNames). A field that is not per-model is not passed; the name comes with the map so a caller that reports on the maps it was given can tell them apart.

func Save

func Save(s Settings) error

Save validates and writes the settings.

Types

type ModelPrice added in v0.1.504

type ModelPrice struct {
	Input      *float64 `json:"input,omitempty"`
	Output     *float64 `json:"output,omitempty"`
	CacheRead  *float64 `json:"cache_read,omitempty"`
	CacheWrite *float64 `json:"cache_write,omitempty"`
}

ModelPrice is the price of one model as the user states it. Each part is a pointer so that a file leaving one out is told so rather than billing that part of the call at zero: a model is priced whole or not at all.

func (ModelPrice) Price added in v0.1.504

func (m ModelPrice) Price() (catalog.Price, string)

Price is the price the user stated, when every part of it is given and is a number a vendor could charge. The second return names the first part that is not, empty when the price is usable.

type Settings

type Settings struct {
	Theme string `json:"theme,omitempty"` // system | light | dark
	Lang  string `json:"lang,omitempty"`  // system | en | zh
	Tray  string `json:"tray,omitempty"`  // what clicking the tray icon opens: panel | window
	// SessionTerminal is the Mac app that opens a resumed session, by bundle
	// id. "" and "system" follow the .command file association.
	SessionTerminal string `json:"sessionTerminal,omitempty"`
	// Currency is what a cost — the Usage page's, the tray panel's, the
	// TUI's and the CLI's — is shown converted to: usd (its native
	// currency, list prices being in dollars) or cny, at a live exchange
	// rate (see internal/fx). A vendor's own balance, already in its own
	// currency (a Chinese relay's ¥), is never touched by this.
	Currency string `json:"currency,omitempty"`
	// Dock keeps magpie in the Mac's Dock as well as the menu bar, for a
	// menu bar too full to show its icon.
	Dock bool `json:"dock,omitempty"`
	// DockWindow shows it in the Dock only while its window is open, so
	// Cmd-Tab reaches the window without an icon kept there the rest of
	// the time. Dock wins over it.
	DockWindow bool `json:"dockWindow,omitempty"`
	// Proxy for magpie's own requests to vendors: "" follows the
	// environment and then the system, "direct" uses none, anything else
	// is the proxy (http://, https:// or socks5://; host:port means http).
	Proxy string `json:"proxy,omitempty"`
	// Redact keeps secrets in what agents send (API keys, private keys,
	// tokens, passwords) from the vendors behind magpie: they go as
	// placeholders, and come back as they were. RedactPersonal does the same
	// for emails, phone numbers and ID and bank card numbers, and
	// RedactWords for the user's own words. RedactRules are the user's own
	// rules for secrets magpie's don't know (a gateway's oc_sk_… key), a
	// prefix or a pattern each, masked with the secrets while Redact is on.
	Redact         bool          `json:"redact,omitempty"`
	RedactPersonal bool          `json:"redactPersonal,omitempty"`
	RedactWords    []string      `json:"redactWords,omitempty"`
	RedactRules    []redact.Rule `json:"redactRules,omitempty"`
	// LAN shares the gateway on the local network, for agents on other
	// machines; a request from one must carry LANKey as its API key, a
	// key magpie makes when LAN is first turned on.
	LAN    bool   `json:"lan,omitempty"`
	LANKey string `json:"lanKey,omitempty"`
	// CodexWarmup starts a ChatGPT account's next window as soon as the
	// last one resets, with one tiny request, so it counts from then (a
	// Codex window starts at its first use): "" off, "week" the weekly
	// window, "all" the 5-hour one too.
	CodexWarmup string `json:"codexWarmup,omitempty"`
	// ClaudeWarmup is CodexWarmup for the Claude accounts, the request
	// sent through Claude Code.
	ClaudeWarmup string `json:"claudeWarmup,omitempty"`
	// CodexWarmAt starts each ChatGPT account's 5-hour window at a time of
	// day of the user's choosing, local "15:04", with the same tiny
	// request: an account whose 5-hour window isn't running then gets one,
	// so the windows line up with the day (06:00 gives three by 21:00, where
	// the first use at 9 gives two by the end of it); "" off. It works
	// with CodexWarmup or without it. ClaudeWarmAt is the Claude accounts'.
	CodexWarmAt  string `json:"codexWarmAt,omitempty"`
	ClaudeWarmAt string `json:"claudeWarmAt,omitempty"`
	// CodexAutoReset are the ChatGPT accounts (lower-case) that spend one
	// of their rate-limit resets by themselves once their weekly window is
	// used up and no other account can take the request: at most one a
	// week each (see provider.AutoUseCodexReset).
	CodexAutoReset []string `json:"codexAutoReset,omitempty"`
	// ClaudeAutoReset are the Claude accounts (lower-case) that spend one
	// of their usage-limit resets by themselves the same way (see
	// provider.AutoUseClaudeReset).
	ClaudeAutoReset []string `json:"claudeAutoReset,omitempty"`
	// WorkBuddyCheckin presses WorkBuddy's daily check-in (签到) for each
	// signed-in WorkBuddy (China) account once a Beijing day, claiming the
	// credits it gives while its event runs.
	WorkBuddyCheckin bool `json:"workbuddyCheckin,omitempty"`
	// NoStats stops the one event a day that counts magpie's users (see
	// internal/stats).
	NoStats bool `json:"noStats,omitempty"`
	// NoUpdatePill keeps the header's Update pill away when a newer magpie
	// is out; UpdateSkip is the one version it was hidden for, and a newer
	// one brings it back. Either way magpie still downloads the version and
	// puts it in as it quits, and Settings' version row still offers it.
	NoUpdatePill bool   `json:"noUpdatePill,omitempty"`
	UpdateSkip   string `json:"updateSkip,omitempty"`
	// Vision is the model that describes an image to a model that can't see
	// it: a model's id (provider/model, group/<id>), "off" to turn such an
	// image away, or empty for one magpie picks (see gateway.seer).
	Vision string `json:"vision,omitempty"`
	// ImageGen is the model magpie's generate_image tool draws with (the
	// gateway's /v1/images/generations when a request names no model): a
	// model's id, "off", or empty for one magpie picks (gateway.drawer).
	// "off" turns the video a request that names no model gets off too
	// (gateway.videomaker).
	ImageGen string `json:"imageGen,omitempty"`
	// TrayUsages are the subscriptions and plans whose windows are shown
	// beside the tray icon, in the order shown, each by its provider and
	// account ("claude|a@b.c"); none when empty.
	TrayUsages []string `json:"trayUsages,omitempty"`
	// TrayUsage is TrayUsages' first, all a magpie before them read: a file
	// with it and no TrayUsages shows that one, and it is kept written for
	// an older magpie to go on showing it.
	TrayUsage string `json:"trayUsage,omitempty"`
	// TrayUsageEvery is how often, in minutes, that text is brought up to
	// date; 0 is every 3 (one of TrayEvery).
	TrayUsageEvery int `json:"trayUsageEvery,omitempty"`
	// QuotaLeft shows a subscription's windows by how much of each is left,
	// not used: the Usage page, the tray panel and the menu bar alike.
	QuotaLeft bool `json:"quotaLeft,omitempty"`
	// UsageAlert is how much of a subscription's or plan's window, in
	// percent, is used when magpie says so with a notification (#368):
	// once for each time the window runs, for every window routing counts
	// (not one set aside, as on-demand spending is); 0 is off.
	UsageAlert int `json:"usageAlert,omitempty"`
	// BalanceAlert is the balance a key or account has fallen to when
	// magpie says so, in that balance's own currency or credits, once until
	// it is topped up past it again; 0 is off.
	BalanceAlert float64 `json:"balanceAlert,omitempty"`
	// PlainNames has the model lists magpie gives agents name each model
	// by its name alone, without its provider's or "routing group" after it
	// (#335) — but for two in one list that would read the same, which keep
	// it (see provider.Labels).
	PlainNames bool `json:"plainNames,omitempty"`
	// TextSize is how large the window's and the tray panel's pages are
	// drawn, in percent (one of TextSizes): the webviews' own zoom, as a
	// browser's, so the text and everything around it grow together.
	TextSize int `json:"textSize,omitempty"`
	// How the agents are listed, by agent id. AgentOrder comes first, as
	// ordered; an agent it doesn't name (one installed since) follows in
	// magpie's own order. A hidden agent is folded away at the bottom of the
	// list; a shown one stays in view even while nothing is set on it, which
	// otherwise folds it away too. The agents' own files never hear of it.
	AgentOrder   []string `json:"agentOrder,omitempty"`
	AgentsHidden []string `json:"agentsHidden,omitempty"`
	AgentsShown  []string `json:"agentsShown,omitempty"`
	// Visible narrows the models an agent is shown, by agent id: the
	// families (the tag a provider or group is given), provider ids and
	// group ids its lists hold. An agent it doesn't name is shown them all.
	Visible map[string][]string `json:"visible,omitempty"`
	// HiddenModels are the catalog entries (their ids, "<provider>/<model>"
	// or a group's) taken out of an agent's lists one by one, by agent id,
	// after Visible: a model not named here, a new one among them, is shown.
	HiddenModels map[string][]string `json:"hiddenModels,omitempty"`

	// The three maps below, and every one added beside them, are the
	// per-model ones: a field named Model* whose type is a map[string]X,
	// keyed "<provider id>/<model id>" (or "<provider id>/*", for all of
	// that provider's models — CheckModelKey). That is the whole of the
	// convention, and it is what RenamePerModel and the GUI's saving of
	// the settings go by, each of them walking the fields by it rather
	// than by a list kept up to date by hand: a map added here later is
	// moved when a provider is renamed and kept when another page saves
	// the settings, with no line written for it in either place. A field
	// whose keys are not a model's is not one of these, and is not named
	// Model* — Visible, which is by agent id, among them.
	// ModelNames are the names the user gave models, by "<provider
	// id>/<model id>": agents, the gateway's model list and magpie itself
	// show them for the vendor's (see provider.SetModelName).
	ModelNames map[string]string `json:"modelNames,omitempty"`
	// ModelEfforts are the reasoning levels the user keeps of a model's,
	// by "<provider id>/<model id>": the lists magpie hands out offer only
	// those (see provider.SetModelEfforts).
	ModelEfforts map[string][]string `json:"modelEfforts,omitempty"`
	// ModelImages is whether the user said a model takes images, by
	// "<provider id>/<model id>". Absent leaves it to the vendor's list.
	ModelImages map[string]bool `json:"modelImages,omitempty"`
	// ModelPrices is what a model costs the user, in USD per million
	// tokens, by "<provider id>/<model id>", and "*" for every model of
	// that provider: a provider models.dev does not list, or one that
	// resells at a multiplier, is otherwise priced at whatever its maker's
	// list price is.
	ModelPrices map[string]ModelPrice `json:"modelPrices,omitempty"`
	// ModelOutputs is the most a reply of a model may hold, by "<provider
	// id>/<model id>", and "*" for every model of that provider. Absent
	// leaves it to the vendor's own list and to models.dev, as a context
	// the user has not set is. A model's own value wins over its
	// provider's, and the agents' own files are told of either
	// (see provider.SetModelOutput).
	ModelOutputs map[string]int `json:"modelOutputs,omitempty"`
	// The main window's size when it was last resized, width and height,
	// so it opens at it again after a restart.
	Window []int `json:"window,omitempty"`
}

Settings is what the user chose. "" and "system" both mean "follow the OS".

func Load

func Load() Settings

Load reads the settings; anything missing or unreadable is the default.

func (*Settings) RenamePerModel added in v0.1.496

func (s *Settings) RenamePerModel(from, to string) bool

RenamePerModel moves what the user said of a provider's models to the id it has now: in every per-model map (see ModelNames) each key beginning with from+"/" is rewritten to to+"/", and it says whether any key moved at all. A provider that changes its id keeps the names, levels, image answers and everything else given to its models, each map moved whether or not the ones before it moved anything.

Jump to

Keyboard shortcuts

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