feat: network proxy settings for tmux sessions#16
Conversation
Add a Network tab in Settings that lets users configure proxy environment variables (http_proxy, https_proxy, all_proxy, no_proxy) which are automatically applied to all tmux sessions managed by Mori via `tmux set-environment -g`. Both lowercase and uppercase variants are set.
- Cancel in-flight proxy apply task before starting a new one to prevent interleaved tmux writes from rapid edits - Add onSessionCreated callback to WorkspaceManager so proxy env vars are reapplied after the tmux server starts (fixes fresh-launch case where no server exists yet) - Centralize proxy apply logic in applyProxyToTmux() helper
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ba712a55b
ℹ️ 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".
| public func setEnvironment(name: String, value: String) async throws { | ||
| _ = try await runner.run("set-environment", "-g", name, value) |
There was a problem hiding this comment.
Scope proxy env writes to Mori's own tmux server
set-environment -g mutates tmux's global environment, which tmux merges into every new window on that server. In this repo TmuxCommandRunner.run still executes plain tmux with no -L/-S socket isolation (Packages/MoriTmux/Sources/MoriTmux/TmuxCommandRunner.swift:170-177), so users who also run non-Mori sessions on the default server will inherit or lose these proxy variables too. That makes the feature affect unrelated tmux workflows, not just Mori-managed sessions.
Useful? React with 👍 / 👎.
| static func apply(_ model: ProxySettingsModel, tmuxBackend: TmuxBackend) async { | ||
| for (envName, value) in model.allEntries { | ||
| do { | ||
| if value.isEmpty { | ||
| try await tmuxBackend.unsetEnvironment(name: envName) |
There was a problem hiding this comment.
Stop stale apply tasks before issuing more tmux writes
proxyApplyTask?.cancel() in AppDelegate.applyProxyToTmux() does not actually debounce the update, because this loop keeps awaiting tmux commands without ever checking cancellation, and TmuxCommandRunner.run is not cancellation-aware. If a user edits a proxy field quickly, an older apply can resume after a newer one and leave later variables (for example HTTP_PROXY or NO_PROXY) at old values, so the final tmux environment can be mixed or stale.
Useful? React with 👍 / 👎.
| proxyApplyTask = Task { [weak self] in | ||
| guard let tmuxBackend = self?.workspaceManager?.tmuxBackend else { return } | ||
| let model = ProxySettingsApplicator.load() | ||
| await ProxySettingsApplicator.apply(model, tmuxBackend: tmuxBackend) |
There was a problem hiding this comment.
Wait for proxy setup before creating templated windows
This method always fire-and-forgets the proxy update. In the new worktree flow, WorkspaceManager.createWorktree() calls onSessionCreated?() and then immediately runs TemplateApplicator.apply(...) (Sources/Mori/App/WorkspaceManager.swift:381-387), so on a fresh tmux server the template can create windows and send startup commands before the proxy env has been installed. Any template command that depends on the proxy will start without it.
Useful? React with 👍 / 👎.
- Add Task.isCancelled guard in ProxySettingsApplicator.apply() loop so cancelled tasks stop issuing tmux writes immediately - Make onSessionCreated async so WorkspaceManager awaits proxy setup before running TemplateApplicator (ensures proxy env is available for template commands)
- Three proxy modes: System (reads macOS scutil --proxy), Manual, None - System mode auto-detects HTTP/HTTPS/SOCKS proxy and bypass list from macOS system configuration - Manual mode with "Same as HTTP" toggle for HTTPS proxy - Explicit Apply button instead of fire-on-keystroke (eliminates race conditions entirely) - Read-only fields in System mode to show detected values - Renamed allProxy to socksProxy for clarity - Updated en + zh-Hans localization strings
- Update settings hint to clearly state proxy changes only affect new tabs/panes, existing shells keep their current environment - Add docs/network-proxy.md with full documentation: modes, env vars, the new-panes-only limitation with manual workaround, and system proxy detection details - Updated en + zh-Hans localization
Summary
http_proxy,https_proxy,all_proxy,no_proxy)tmux set-environment -ghttp_proxyandHTTP_PROXY) are set automaticallyImplementation
TmuxBackend: newsetEnvironment/unsetEnvironmentmethods wrappingtmux set-environment -g/-guProxySettingsModel: data model withentries/allEntriescomputed propertiesProxySettingsApplicator: load/save via UserDefaults, apply to tmuxNetworkSettingsContent: SwiftUI settings view with text fields + Clear All buttonTest plan
tmux show-environment -gtmux show-environment -gno longer shows them)