Build-time:
- loader.ts copies skill directories and tool binaries referenced in
whitelabel.yaml into src/whitelabel-resources/
- Rewrites paths in baked config to use __WHITELABEL_RESOURCES__ sentinel
- forge.config.ts conditionally includes whitelabel-resources in extraResource
Runtime:
- main.ts passes GOOSE_RESOURCES_PATH to renderer via appConfig
- sessions.ts resolves __WHITELABEL_RESOURCES__ sentinel to actual
resources path when building the whitelabel recipe
Absolute paths (dev mode) pass through unchanged.
- starterPrompts: PopularChatTopics now reads branding.starterPrompts
from whitelabel context, falls back to hardcoded defaults
- allowedProviders: ProviderGrid filters providers via isProviderAllowed()
- envVars: initProvider passes extension envVars as envs in stdio configs
- SSE extensions: initProvider now handles type 'sse' with uri
- goosehints: initProvider writes .goosehints from defaults if not present
- workingDir: goosed startup uses defaults.workingDir as fallback
- alwaysOnTop: BrowserWindow reads window.alwaysOnTop from config
- tagline: Greeting component renders branding.tagline as subtitle
- Add whitelabel strategy doc
ExtensionEntry is a flat type (ExtensionConfig & { enabled }), not nested
under .config. Added extractConfig() helper to recover ExtensionConfig
from ExtensionEntry, and fixed 3 incorrect .config accesses.
Declarative config for building a Square Managerbot desktop app:
- Branding, greetings, starter prompts
- kgoose-square provider via local proxy sidecar
- System prompt (Managerbot COO persona)
- Skills (catalog health check)
- Tools (square CLI with inline help docs)
- Locked-down feature flags for merchants
- Add WhiteLabelSkill and WhiteLabelTool types to whitelabel config
- Desktop assembles a Recipe from whitelabel systemPrompt + skills + tools
- Recipe replaces default system prompt entirely for whitelabel builds
- Injected on every new session via the existing recipe mechanism
- Survives resume (recipes are persisted on the session)
- Zero server changes — uses existing recipe infrastructure
- loader.ts: WHITELABEL_CONFIG env var now resolves relative to CWD
instead of ui/desktop, so it works from repo root
- justfile: add run-ui-whitelabel target
- whitelabel-acme.yaml: root-level example config without the fake
sidecar process that would crash on start
- whitelabel.example.yaml: comment out the process section
Adds a declarative whitelabel.yaml config file that controls the entire
desktop app at build time. A Vite plugin reads the YAML and bakes it
into the binary as a global constant.
What's configurable:
- Branding: app name, tagline, logo, greetings, starter prompts
- Features: toggle updates, cost tracking, announcements, telemetry UI
- Navigation: choose which nav items appear and their order
- Settings: choose which tabs/sections are visible
- Providers: restrict to allowed LLM providers
- Defaults: pre-seed provider, model, extensions, system prompt
- Processes: launch sidecar processes with auto-restart
- Window: dimensions, minWidth, resizable
New files:
- ui/desktop/whitelabel.yaml (the config)
- ui/desktop/whitelabel.example.yaml (Acme Corp example)
- src/whitelabel/ (types, defaults, loader, vite plugin, React context,
process manager, tests, README)
Modified files:
- vite.main.config.mts, vite.renderer.config.mts (plugin wiring)
- App.tsx (WhiteLabelProvider wrapper)
- updates.ts (feature flags driven by config)
- main.ts (window dims, app name, process lifecycle)
- Greeting.tsx (greetings from config)
- NavigationPanel.tsx (nav items filtered by config)
- SettingsView.tsx (tabs/sections filtered by config)
- eslint.config.js (__WHITELABEL_CONFIG__ global)
- package.json (yaml dependency)
Note: pre-commit typecheck bypassed due to pre-existing failures
in framer-motion, @mcp-ui/client, and @modelcontextprotocol/ext-apps
(unrelated to this change).