Skip to content

fix: watch providers.jsonc for external edits so UI refreshes without restart#3233

Open
Neppkun wants to merge 1 commit into
coder:mainfrom
Neppkun:claude/gracious-vaughan-6da90f
Open

fix: watch providers.jsonc for external edits so UI refreshes without restart#3233
Neppkun wants to merge 1 commit into
coder:mainfrom
Neppkun:claude/gracious-vaughan-6da90f

Conversation

@Neppkun
Copy link
Copy Markdown

@Neppkun Neppkun commented May 5, 2026

Summary

  • Custom providers added manually to ~/.mux/providers.jsonc never appeared in the UI because notifyConfigChanged() was only called after API mutations, not on external file changes
  • Added Config.watchProvidersFile() — watches the mux home directory with fs.watch, debounced 300 ms, returns a cleanup fn
  • Wired it into ProviderService constructor so any external edit to providers.jsonc immediately propagates to all onConfigChanged frontend subscribers

Root cause

ProviderService.list() and getConfig() already read providers.jsonc fresh on every call, so the backend always had the right data. The missing piece was a signal to the frontend to re-fetch. notifyConfigChanged() emits that signal — but it was only called after in-app edits, leaving manual file edits invisible until restart.

Test plan

  • Add a custom provider entry to ~/.mux/providers.jsonc while the app is running — it should appear in Settings → Providers within ~300 ms, no restart needed
  • Remove the entry from the file — it should disappear from the UI within ~300 ms
  • Verify built-in provider changes via the UI still work as before
  • Confirm no MaxListenersExceededWarning noise in dev logs

🤖 Generated with Claude Code

…ut restart

Manual edits to ~/.mux/providers.jsonc were silently ignored by the
frontend because notifyConfigChanged() was only called after API
mutations. Added watchProvidersFile() to Config (fs.watch on the mux
home directory, debounced 300ms) and wired it into ProviderService's
constructor so external file changes propagate to all onConfigChanged
subscribers automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Neppkun
Copy link
Copy Markdown
Author

Neppkun commented May 5, 2026

I apologize in advance for using Claude Code to write this instead of Mux. (I ran out of credits)
Tested the changes on web UI, works as intended.
image
image
image

@ethanndickson
Copy link
Copy Markdown
Member

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 902766a042

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/node/config.ts
const filename = path.basename(this.providersFile);
let debounceTimer: ReturnType<typeof setTimeout> | null = null;

const watcher = fs.watch(this.rootDir, (_eventType, changedFilename) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard providers watcher when mux home is missing

watchProvidersFile() calls fs.watch(this.rootDir, ...) unconditionally, but Config does not guarantee rootDir exists before ProviderService is constructed. On first run (or when MUX_ROOT points to a not-yet-created directory), fs.watch throws ENOENT synchronously, which aborts ProviderService construction and can crash startup before recovery logic runs.

Useful? React with 👍 / 👎.

// Avoid noisy MaxListenersExceededWarning for normal usage.
this.emitter.setMaxListeners(50);
// Notify subscribers when providers.jsonc is edited externally (e.g. manual edits).
this.config.watchProvidersFile(() => this.notifyConfigChanged());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain and dispose providers file watcher

The constructor starts a filesystem watch via watchProvidersFile(...) but drops the returned cleanup function. Because FSWatcher is ref'ed by default, each ProviderService instance now keeps a live watcher for the process lifetime, which can leak handles in tests/one-shot CLI flows and make transient service instances unable to fully tear down.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants