Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion hermes_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -3090,7 +3090,19 @@ def generate_launchd_plist() -> str:
<key>SuccessfulExit</key>
<false/>
</dict>


<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>

<key>HardResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>65536</integer>
</dict>

<key>StandardOutPath</key>
<string>{log_dir}/gateway.log</string>

Expand Down
14 changes: 14 additions & 0 deletions tests/hermes_cli/test_gateway_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,20 @@ def test_launchd_plist_includes_profile(self, tmp_path, monkeypatch):
assert "<string>--profile</string>" in plist
assert "<string>mybot</string>" in plist

def test_launchd_plist_sets_nofiles_resource_limits(self, tmp_path, monkeypatch):
"""generate_launchd_plist must set NumberOfFiles limits so the long-running
gateway doesn't hit launchd's default 256 soft RLIMIT_NOFILE (issue #36899)."""
profile_dir = tmp_path / ".hermes"
profile_dir.mkdir(parents=True)
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(profile_dir))
monkeypatch.setattr(gateway_cli, "get_hermes_home", lambda: profile_dir)
plist = gateway_cli.generate_launchd_plist()
assert "<key>SoftResourceLimits</key>" in plist
assert "<key>HardResourceLimits</key>" in plist
assert plist.count("<key>NumberOfFiles</key>") == 2
assert plist.count("<integer>65536</integer>") >= 2

def test_launchd_plist_path_uses_real_user_home_not_profile_home(self, tmp_path, monkeypatch):
profile_dir = tmp_path / ".hermes" / "profiles" / "orcha"
profile_dir.mkdir(parents=True)
Expand Down
Loading