‹ Back homeTechnology

A native macOS model orchestration tool for coordinating Codex, DeepSeek Harness, LM Studio, cloud gateways, configuration, memory, launchd, permissions, and state.

Oct 18, 2026·4 min read·Reads …

Contents
  1. 1. A menu-bar switch became state management
  2. 2. Models as referenced resources
  3. 3. Three entry points, three protocols
  4. Qwen3-Coder locally
  5. DeepSeek R1 locally
  6. DeepSeek V4 Flash in the cloud
  7. 4. Switching is a small transaction
  8. 5. dsh must be managed by launchd
  9. 6. Temperature sensors and Mach ports
  10. 7. Keychain can interrupt startup
  11. 8. Three equal cards
  12. 9. What it includes now
  13. 10. Closing thought

Project

  • Name: Henry Model Manager
  • Author: Henry
  • Version: 3.3
  • Stack: SwiftUI, AppKit, Foundation, launchctl, IOHID, LM Studio API
  • Nature: personal macOS tool; source is currently private
  • Article: Henry

I regularly use four model entry points:

  • Codex for code and multi-step tasks
  • dsh (DeepSeek Harness) for DeepSeek in the browser
  • LM Studio for local Qwen and DeepSeek models
  • A cloud gateway connected through cc-switch

Choosing a model sounds simple. In practice, every switch touches ~/.codex/config.toml, ~/.dsh/settings.yaml, LM Studio’s loaded models, the dsh service process, and each model’s context and reasoning requirements.

When one part is missed, Codex points at a provider that no longer exists, dsh still uses an old model, LM Studio loads duplicate multi-gigabyte instances, a cloud model is active while a local model still holds memory, or launchd fights with a manually killed process over the same port.

Henry Model Manager is therefore not another chat client. It is a model orchestration layer.

1. A menu-bar switch became state management

The first version only switched Codex between local Qwen, local DeepSeek R1, and a cloud DeepSeek model. It edited one config, preloaded a 64K context for local models, and unloaded local models before switching to the cloud.

It soon failed for obvious reasons: it ignored dsh, duplicated LM Studio instances, and had no idea who was using a local model.

2. Models as referenced resources

Local models are expensive memory resources, not just configuration values. The manager records which entry point references each loaded model. Codex and dsh may share a model, use different models, or release their references independently. LM Studio unloads a model only when the last reference disappears.

This reference-counting model is more accurate and far more memory-efficient than unloading everything on every switch.

3. Three entry points, three protocols

Qwen3-Coder locally

Codex uses the lmstudio-local provider with a 64K context and reasoning effort set to none. LM Studio’s Responses endpoint does not accept every reasoning or function-call input that Codex emits, so the protocol boundary has to be explicit.

DeepSeek R1 locally

Codex and dsh read different configuration files and have different expectations for model name, base URL, vision support, and context length.

DeepSeek V4 Flash in the cloud

Codex uses a custom provider, dsh uses the cc-switch proxy, and all local models are unloaded. The manager enforces this instead of relying on the user to remember.

4. Switching is a small transaction

A switch performs several steps:

  1. Back up Codex and dsh configuration.
  2. Write provider, model, and reasoning fields.
  3. Ensure the required provider block exists.
  4. Synchronize loaded models by reference.
  5. Restart dsh through launchd when needed.
  6. Save history.
  7. Update the interface and notification state.

Backups live beside the original files. Switch history is stored under:

~/Library/Application Support/HenryModelManager/history.json

That makes it possible to recover quickly after choosing the wrong model.

5. dsh must be managed by launchd

The dsh web service is managed by a launch agent with KeepAlive enabled. Killing the process only makes launchd start it again. Bad restart logic can also create duplicate bind attempts.

The app uses launchd lifecycle commands:

launchctl kickstart gui/$UID/com.henry.dsh.web
launchctl kickstart -k gui/$UID/com.henry.dsh.web
launchctl bootout gui/$UID/com.henry.dsh.web
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.henry.dsh.web.plist

“Start” and “stop” are not merely PID operations. They distinguish whether a service is loaded from whether a process is running.

6. Temperature sensors and Mach ports

The app also displays SoC and battery temperature through an IOHID C interface. The difficult part is resource ownership. Repeatedly creating an event-system client without releasing its Mach resources leaks ports over time.

The client is cached, Mach objects are released, and failed reads become an unavailable temperature state rather than a crash.

7. Keychain can interrupt startup

The first balance monitor read the API key from Keychain during launch. macOS displayed a permission prompt over the main window, which looked like a blank app.

The fix was not to remove Keychain. It was to request access only when the balance screen is opened.

8. Three equal cards

The Codex, dsh, and LM Studio cards originally had unequal heights and one card became gray because it used .disabled(true) only to suppress actions. Fixed-height layout, Spacer, and plain views for cards without menus made the interface intentional.

9. What it includes now

  • Live Codex, dsh, and LM Studio status
  • Local and cloud model switching
  • Apply to one tool or both
  • Reference-counted local memory
  • launchd-based service control
  • Model, memory, process, temperature, and balance monitoring
  • Keychain-stored DeepSeek keys
  • Reversible switch history
  • Startup and notification preferences

The app is about 2,600 lines of Swift. Its value is not the amount of code but the state synchronization it removes from daily use.

10. Closing thought

Switching a model looked like a dropdown. It turned out to involve configuration formats, provider protocols, model lifecycles, launchd, Keychain, Mach resources, SwiftUI state, and failure recovery.

The best personal tools grow from repeatedly solving a problem you actually have.


Project: Henry Model Manager. Author of both the tool and this article: Henry. Version 3.3; source currently private.

H
Henry

Notes on technology, design, and the slower observations of everyday life.

About the author
DISCUSSION

Comments 0

Leave a thought or add to the conversation. Comments appear after moderation.

Loading comments…