From 8a4cfff6a2b216114369a907e969edc6340a022b Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Wed, 22 Apr 2026 00:10:13 +0100 Subject: [PATCH 1/4] feat: auto-detect local vs remote bus in `make logs` Previously `make logs` hardcoded `tail -f` on the local log file, which silently showed an empty/stale log for users configured as clients (`make install-client REMOTE_URL=...`) pointed at a Tailscale-hosted bus. Detection now parses `claude mcp list` (falling back to `$AGENT_EVENT_BUS_URL`), validates the URL scheme, and SSH-tails the remote host when non-local. Override with `make logs BUS_HOST=`. Closes #119 Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 7 +++++-- Makefile | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 29ebc06..aaee28c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -149,8 +149,11 @@ CLI and MCP expose the same functionality: ## Operations ```bash -# Watch live activity -tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log +# Watch live activity (auto-detects local vs remote bus from MCP config) +make logs + +# Force a remote tail (overrides auto-detect) +make logs BUS_HOST=your-server.tailnet.ts.net # Override database path AGENT_EVENT_BUS_DB=/path/to/db.sqlite agent-event-bus diff --git a/Makefile b/Makefile index 802aa54..6273469 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,25 @@ restart: fi; \ fi -# Tail the event bus log +# Tail the event bus log (auto-detects local vs remote bus) +# Override with BUS_HOST= to force a remote tail logs: - @tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log + @HOST="$(BUS_HOST)"; \ + if [ -z "$$HOST" ]; then \ + CLAUDE_CMD=$$(command -v claude || echo "$$HOME/.local/bin/claude"); \ + if [ -x "$$CLAUDE_CMD" ]; then \ + URL=$$("$$CLAUDE_CMD" mcp list 2>/dev/null | awk '/^agent-event-bus:/ {print $$2}'); \ + fi; \ + if [ -z "$$URL" ]; then \ + URL="$$AGENT_EVENT_BUS_URL"; \ + fi; \ + if echo "$$URL" | grep -qE '^https?://'; then \ + HOST=$$(echo "$$URL" | sed -E 's|https?://||; s|/.*||; s|:[0-9]+$$||; s|^\[||; s|\]$$||'); \ + fi; \ + fi; \ + if [ -z "$$HOST" ] || [ "$$HOST" = "localhost" ] || [ "$$HOST" = "127.0.0.1" ] || [ "$$HOST" = "::1" ]; then \ + tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log; \ + else \ + echo "Tailing remote bus at $$HOST (Ctrl-C to exit)..."; \ + ssh -t -- "$$HOST" 'tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log'; \ + fi From c770c0f4bbb453ed2aa0a027993762e43ddd338c Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Wed, 22 Apr 2026 00:26:27 +0100 Subject: [PATCH 2/4] refactor: honor AGENT_EVENT_BUS_LOG and AGENT_EVENT_BUS_ERR env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses PR #120 review feedback: - Add shell no-op comment noting the awk parser's dependency on `claude mcp list` text format (suggestion #1) - Treat 0.0.0.0 as local in `make logs` host check (suggestion #2) - Centralize hardcoded log/err paths via AGENT_EVENT_BUS_* env vars (suggestion #3, expanded per user direction) New env vars follow the existing AGENT_EVENT_BUS_DB pattern: - AGENT_EVENT_BUS_LOG: Python `LOG_FILE` + Makefile `$(LOG_FILE)` - AGENT_EVENT_BUS_ERR: launchd/systemd StandardError redirect + Makefile `$(ERR_FILE)` End-to-end wiring: - src/agent_event_bus/server.py — LOG_FILE reads env var - Makefile — defines $(LOG_FILE) / $(ERR_FILE) Make vars - scripts/com.evansenter.agent-event-bus.plist — __ERR_FILE__ placeholder - scripts/agent-event-bus.service — __LOG_FILE__ / __ERR_FILE__ placeholders - scripts/install-launchagent.sh + install-systemd.sh — resolve env vars (with defaults) and substitute into templates; echoes reflect override - CLAUDE.md — document new env vars in Operations section Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 5 +++ Makefile | 16 ++++++--- scripts/agent-event-bus.service | 4 +-- scripts/com.evansenter.agent-event-bus.plist | 2 +- scripts/install-launchagent.sh | 11 ++++-- scripts/install-systemd.sh | 12 +++++-- src/agent_event_bus/server.py | 7 ++-- tests/test_server.py | 35 ++++++++++++++++++++ 8 files changed, 75 insertions(+), 17 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index aaee28c..3782be6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -158,6 +158,11 @@ make logs BUS_HOST=your-server.tailnet.ts.net # Override database path AGENT_EVENT_BUS_DB=/path/to/db.sqlite agent-event-bus +# Override log/error file paths (set before `make install-server` so the +# launchd/systemd templates are generated with the custom paths) +AGENT_EVENT_BUS_LOG=/path/to/custom.log +AGENT_EVENT_BUS_ERR=/path/to/custom.err + # Dev mode console logging DEV_MODE=1 agent-event-bus diff --git a/Makefile b/Makefile index 6273469..859a911 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,9 @@ .PHONY: check fmt lint test clean install-server install-client uninstall dev venv restart logs +# Canonical paths (override with matching AGENT_EVENT_BUS_* env vars) +LOG_FILE := $(or $(AGENT_EVENT_BUS_LOG),$(HOME)/.claude/contrib/agent-event-bus/agent-event-bus.log) +ERR_FILE := $(or $(AGENT_EVENT_BUS_ERR),$(HOME)/.claude/contrib/agent-event-bus/agent-event-bus.err) + # Run all quality gates (format check, lint, tests) check: fmt lint test @@ -124,7 +128,7 @@ restart: if launchctl list | grep -q "com.evansenter.agent-event-bus"; then \ echo "Service restarted successfully"; \ else \ - echo "Error: Service failed to start. Check ~/.claude/contrib/agent-event-bus/agent-event-bus.err"; \ + echo "Error: Service failed to start. Check $(ERR_FILE)"; \ exit 1; \ fi; \ else \ @@ -138,13 +142,15 @@ restart: if systemctl --user is-active agent-event-bus &>/dev/null; then \ echo "Service restarted successfully"; \ else \ - echo "Error: Service failed to start. Check ~/.claude/contrib/agent-event-bus/agent-event-bus.err"; \ + echo "Error: Service failed to start. Check $(ERR_FILE)"; \ exit 1; \ fi; \ fi # Tail the event bus log (auto-detects local vs remote bus) -# Override with BUS_HOST= to force a remote tail +# Override with BUS_HOST= to force a remote tail. +# Auto-detect parses `claude mcp list` text output (agent-event-bus: URL ...); +# detection silently falls through to local if that format ever changes. logs: @HOST="$(BUS_HOST)"; \ if [ -z "$$HOST" ]; then \ @@ -159,8 +165,8 @@ logs: HOST=$$(echo "$$URL" | sed -E 's|https?://||; s|/.*||; s|:[0-9]+$$||; s|^\[||; s|\]$$||'); \ fi; \ fi; \ - if [ -z "$$HOST" ] || [ "$$HOST" = "localhost" ] || [ "$$HOST" = "127.0.0.1" ] || [ "$$HOST" = "::1" ]; then \ - tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log; \ + if [ -z "$$HOST" ] || [ "$$HOST" = "localhost" ] || [ "$$HOST" = "127.0.0.1" ] || [ "$$HOST" = "::1" ] || [ "$$HOST" = "0.0.0.0" ]; then \ + tail -f $(LOG_FILE); \ else \ echo "Tailing remote bus at $$HOST (Ctrl-C to exit)..."; \ ssh -t -- "$$HOST" 'tail -f ~/.claude/contrib/agent-event-bus/agent-event-bus.log'; \ diff --git a/scripts/agent-event-bus.service b/scripts/agent-event-bus.service index 85f19f3..4fed731 100644 --- a/scripts/agent-event-bus.service +++ b/scripts/agent-event-bus.service @@ -10,8 +10,8 @@ Environment=AGENT_EVENT_BUS_ICON=__PROJECT_DIR__/assets/icon.png ExecStart=__VENV_PYTHON__ -m agent_event_bus.server Restart=always RestartSec=5 -StandardOutput=append:__HOME__/.claude/contrib/agent-event-bus/agent-event-bus.log -StandardError=append:__HOME__/.claude/contrib/agent-event-bus/agent-event-bus.err +StandardOutput=append:__LOG_FILE__ +StandardError=append:__ERR_FILE__ [Install] WantedBy=default.target diff --git a/scripts/com.evansenter.agent-event-bus.plist b/scripts/com.evansenter.agent-event-bus.plist index 46d0964..d43bfaa 100644 --- a/scripts/com.evansenter.agent-event-bus.plist +++ b/scripts/com.evansenter.agent-event-bus.plist @@ -39,7 +39,7 @@ __HOME__/.claude/contrib/agent-event-bus/agent-event-bus.stdout StandardErrorPath - __HOME__/.claude/contrib/agent-event-bus/agent-event-bus.err + __ERR_FILE__ ProcessType Background diff --git a/scripts/install-launchagent.sh b/scripts/install-launchagent.sh index 3dcd941..fec2b74 100755 --- a/scripts/install-launchagent.sh +++ b/scripts/install-launchagent.sh @@ -10,6 +10,10 @@ PLIST_TEMPLATE="$SCRIPT_DIR/com.evansenter.agent-event-bus.plist" PLIST_DEST="$HOME/Library/LaunchAgents/com.evansenter.agent-event-bus.plist" LABEL="com.evansenter.agent-event-bus" +# Resolve paths (respect env var overrides, fall back to canonical defaults) +LOG_FILE="${AGENT_EVENT_BUS_LOG:-$HOME/.claude/contrib/agent-event-bus/agent-event-bus.log}" +ERR_FILE="${AGENT_EVENT_BUS_ERR:-$HOME/.claude/contrib/agent-event-bus/agent-event-bus.err}" + # Check venv exists if [[ ! -f "$VENV_PYTHON" ]]; then echo "Error: Virtual environment not found at $PROJECT_DIR/.venv" @@ -32,6 +36,7 @@ echo "Installing LaunchAgent..." sed -e "s|__VENV_PYTHON__|$VENV_PYTHON|g" \ -e "s|__PROJECT_DIR__|$PROJECT_DIR|g" \ -e "s|__HOME__|$HOME|g" \ + -e "s|__ERR_FILE__|$ERR_FILE|g" \ "$PLIST_TEMPLATE" > "$PLIST_DEST" # Load the service @@ -43,8 +48,8 @@ sleep 1 if launchctl list | grep -q "$LABEL"; then echo "" echo "Agent Event Bus installed and running!" - echo " Logs: ~/.claude/contrib/agent-event-bus/agent-event-bus.log" - echo " Errors: ~/.claude/contrib/agent-event-bus/agent-event-bus.err" + echo " Logs: $LOG_FILE" + echo " Errors: $ERR_FILE" echo "" # Also install CLI for use in hooks/scripts @@ -54,7 +59,7 @@ if launchctl list | grep -q "$LABEL"; then echo "To uninstall: $SCRIPT_DIR/uninstall-launchagent.sh" osascript -e 'display notification "LaunchAgent installed and running" with title "Agent Event Bus"' 2>/dev/null else - echo "Error: Service failed to start. Check ~/.claude/contrib/agent-event-bus/agent-event-bus.err" + echo "Error: Service failed to start. Check $ERR_FILE" osascript -e 'display notification "Failed to start - check logs" with title "Agent Event Bus" sound name "Basso"' 2>/dev/null exit 1 fi diff --git a/scripts/install-systemd.sh b/scripts/install-systemd.sh index a653260..c93b142 100755 --- a/scripts/install-systemd.sh +++ b/scripts/install-systemd.sh @@ -11,6 +11,10 @@ SERVICE_DIR="$HOME/.config/systemd/user" SERVICE_DEST="$SERVICE_DIR/agent-event-bus.service" SERVICE_NAME="agent-event-bus" +# Resolve paths (respect env var overrides, fall back to canonical defaults) +LOG_FILE="${AGENT_EVENT_BUS_LOG:-$HOME/.claude/contrib/agent-event-bus/agent-event-bus.log}" +ERR_FILE="${AGENT_EVENT_BUS_ERR:-$HOME/.claude/contrib/agent-event-bus/agent-event-bus.err}" + # Check venv exists if [[ ! -f "$VENV_PYTHON" ]]; then echo "Error: Virtual environment not found at $PROJECT_DIR/.venv" @@ -33,6 +37,8 @@ echo "Installing systemd service..." sed -e "s|__VENV_PYTHON__|$VENV_PYTHON|g" \ -e "s|__PROJECT_DIR__|$PROJECT_DIR|g" \ -e "s|__HOME__|$HOME|g" \ + -e "s|__LOG_FILE__|$LOG_FILE|g" \ + -e "s|__ERR_FILE__|$ERR_FILE|g" \ "$SERVICE_TEMPLATE" > "$SERVICE_DEST" # Reload systemd and start service @@ -45,8 +51,8 @@ sleep 1 if systemctl --user is-active "$SERVICE_NAME" &>/dev/null; then echo "" echo "Agent Event Bus installed and running!" - echo " Logs: ~/.claude/contrib/agent-event-bus/agent-event-bus.log" - echo " Errors: ~/.claude/contrib/agent-event-bus/agent-event-bus.err" + echo " Logs: $LOG_FILE" + echo " Errors: $ERR_FILE" echo " Status: systemctl --user status $SERVICE_NAME" echo "" @@ -58,6 +64,6 @@ if systemctl --user is-active "$SERVICE_NAME" &>/dev/null; then else echo "Error: Service failed to start. Check logs:" echo " journalctl --user -u $SERVICE_NAME" - echo " ~/.claude/contrib/agent-event-bus/agent-event-bus.err" + echo " $ERR_FILE" exit 1 fi diff --git a/src/agent_event_bus/server.py b/src/agent_event_bus/server.py index 34459cb..4ed17d5 100644 --- a/src/agent_event_bus/server.py +++ b/src/agent_event_bus/server.py @@ -39,10 +39,11 @@ from agent_event_bus.storage import Event, Session, SQLiteStorage, Webhook # Configure logging -# Always log to ~/.claude/contrib/agent-event-bus/agent-event-bus.log for tail -f access -# In dev mode, also log to console +# Default log path: ~/.claude/contrib/agent-event-bus/agent-event-bus.log +# Override with AGENT_EVENT_BUS_LOG env var (matches AGENT_EVENT_BUS_DB pattern) # Skip file logging during tests to avoid polluting production logs -LOG_FILE = Path.home() / ".claude" / "contrib" / "agent-event-bus" / "agent-event-bus.log" +_DEFAULT_LOG_FILE = Path.home() / ".claude" / "contrib" / "agent-event-bus" / "agent-event-bus.log" +LOG_FILE = Path(os.environ.get("AGENT_EVENT_BUS_LOG", str(_DEFAULT_LOG_FILE))) logger = logging.getLogger("agent-event-bus") logger.setLevel(logging.DEBUG if os.environ.get("DEV_MODE") else logging.INFO) diff --git a/tests/test_server.py b/tests/test_server.py index 4e04509..e7badc6 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -3,6 +3,8 @@ import logging import os import socket +import subprocess +import sys from datetime import datetime import pytest @@ -1262,3 +1264,36 @@ def test_publish_event_no_warning_on_valid_channel(self, caplog): publish_event("test", "payload", channel="machine:localhost") assert "Invalid" not in caplog.text + + +class TestLogFileEnvVar: + """Tests for AGENT_EVENT_BUS_LOG env var override.""" + + def _resolved_log_file(self, env: dict) -> str: + """Resolve server.LOG_FILE in a subprocess with the given env. + + Subprocess required because LOG_FILE is set at module import time, + and reloading the server module in-process would disrupt other tests. + """ + result = subprocess.run( + [sys.executable, "-c", "from agent_event_bus import server; print(server.LOG_FILE)"], + env=env, + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, f"stderr: {result.stderr}" + return result.stdout.strip() + + def test_log_file_respects_env_var(self, tmp_path): + """LOG_FILE honors AGENT_EVENT_BUS_LOG when set.""" + custom = tmp_path / "custom.log" + env = {**os.environ, "AGENT_EVENT_BUS_LOG": str(custom), "AGENT_EVENT_BUS_TESTING": "1"} + assert self._resolved_log_file(env) == str(custom) + + def test_log_file_falls_back_to_default(self): + """LOG_FILE uses the canonical default when AGENT_EVENT_BUS_LOG is unset.""" + env = {k: v for k, v in os.environ.items() if k != "AGENT_EVENT_BUS_LOG"} + env["AGENT_EVENT_BUS_TESTING"] = "1" + expected = os.path.expanduser("~/.claude/contrib/agent-event-bus/agent-event-bus.log") + assert self._resolved_log_file(env) == expected From 1195e4856c85457247737ca304d9fe2f93fc8644 Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Wed, 22 Apr 2026 00:42:31 +0100 Subject: [PATCH 3/4] fix: propagate AGENT_EVENT_BUS_LOG/ERR to service runtime env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses PR #120 review feedback (Important): the env var override was read by Python at import time, but the launchd plist and systemd unit never passed the vars into the service process. That meant the documented override produced two disjoint log files (redirect to the custom path, Python FileHandler to the default) — reintroducing the silent-log bug this PR was supposed to fix. - scripts/com.evansenter.agent-event-bus.plist: add AGENT_EVENT_BUS_LOG and AGENT_EVENT_BUS_ERR to the EnvironmentVariables dict. - scripts/agent-event-bus.service: add matching Environment= entries. - scripts/install-launchagent.sh: substitute __LOG_FILE__ alongside existing __ERR_FILE__ substitution. - Makefile: add header comment on remote tail path assumption (the remote's AGENT_EVENT_BUS_LOG can't be known locally). - tests/test_server.py: add TestMakeLogsHostDetection with 8 parametrized URL → HOST extraction cases (hostname, port, IPv6, dash-prefix, non-http, empty). Duplicates the Makefile sed pipeline; keep in sync. - CLAUDE.md: list _LOG and _ERR in the Naming Conventions example line. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 2 +- Makefile | 3 ++ scripts/agent-event-bus.service | 2 + scripts/com.evansenter.agent-event-bus.plist | 4 ++ scripts/install-launchagent.sh | 1 + tests/test_server.py | 47 ++++++++++++++++++++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3782be6..89e90f2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,7 @@ Follow these patterns consistently (aligned with agent-session-analytics): | LaunchAgent | `com.evansenter.agent-event-bus.plist` | | systemd service | `agent-event-bus.service` | -**Environment variables**: `AGENT_EVENT_BUS_*` prefix (e.g., `_DB`, `_URL`, `_AUTH_DISABLED`, `_ICON`, `_TESTING`) +**Environment variables**: `AGENT_EVENT_BUS_*` prefix (e.g., `_DB`, `_LOG`, `_ERR`, `_URL`, `_AUTH_DISABLED`, `_ICON`, `_TESTING`) --- diff --git a/Makefile b/Makefile index 859a911..8e13180 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,9 @@ restart: # Override with BUS_HOST= to force a remote tail. # Auto-detect parses `claude mcp list` text output (agent-event-bus: URL ...); # detection silently falls through to local if that format ever changes. +# Remote tail path is hardcoded to the canonical default: we can't know the +# remote bus's AGENT_EVENT_BUS_LOG from here, so a remote override is not +# honored by `make logs`. Run the tail directly over SSH in that case. logs: @HOST="$(BUS_HOST)"; \ if [ -z "$$HOST" ]; then \ diff --git a/scripts/agent-event-bus.service b/scripts/agent-event-bus.service index 4fed731..fff7b38 100644 --- a/scripts/agent-event-bus.service +++ b/scripts/agent-event-bus.service @@ -7,6 +7,8 @@ Type=simple WorkingDirectory=__PROJECT_DIR__ Environment=PYTHONPATH=__PROJECT_DIR__/src Environment=AGENT_EVENT_BUS_ICON=__PROJECT_DIR__/assets/icon.png +Environment=AGENT_EVENT_BUS_LOG=__LOG_FILE__ +Environment=AGENT_EVENT_BUS_ERR=__ERR_FILE__ ExecStart=__VENV_PYTHON__ -m agent_event_bus.server Restart=always RestartSec=5 diff --git a/scripts/com.evansenter.agent-event-bus.plist b/scripts/com.evansenter.agent-event-bus.plist index d43bfaa..b7e78f3 100644 --- a/scripts/com.evansenter.agent-event-bus.plist +++ b/scripts/com.evansenter.agent-event-bus.plist @@ -23,6 +23,10 @@ __PROJECT_DIR__/src AGENT_EVENT_BUS_ICON __PROJECT_DIR__/assets/icon.png + AGENT_EVENT_BUS_LOG + __LOG_FILE__ + AGENT_EVENT_BUS_ERR + __ERR_FILE__ diff --git a/scripts/install-launchagent.sh b/scripts/install-launchagent.sh index fec2b74..fc7d5df 100755 --- a/scripts/install-launchagent.sh +++ b/scripts/install-launchagent.sh @@ -36,6 +36,7 @@ echo "Installing LaunchAgent..." sed -e "s|__VENV_PYTHON__|$VENV_PYTHON|g" \ -e "s|__PROJECT_DIR__|$PROJECT_DIR|g" \ -e "s|__HOME__|$HOME|g" \ + -e "s|__LOG_FILE__|$LOG_FILE|g" \ -e "s|__ERR_FILE__|$ERR_FILE|g" \ "$PLIST_TEMPLATE" > "$PLIST_DEST" diff --git a/tests/test_server.py b/tests/test_server.py index e7badc6..57df32c 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1266,6 +1266,53 @@ def test_publish_event_no_warning_on_valid_channel(self, caplog): assert "Invalid" not in caplog.text +class TestMakeLogsHostDetection: + """Tests for the host-detection pipeline in the Makefile's `logs` target. + + The sed/grep pipeline is at Makefile:154-157. These tests duplicate it via + shell subprocess so regressions in the URL → HOST extraction are caught. + Keep this in sync if the Makefile pipeline changes. + """ + + @staticmethod + def _resolve_host(url: str) -> str: + """Run the Makefile's detection pipeline against a single URL.""" + script = ( + 'URL="$1"; HOST=""; ' + "if echo \"$URL\" | grep -qE '^https?://'; then " + 'HOST=$(echo "$URL" | ' + "sed -E 's|https?://||; s|/.*||; s|:[0-9]+$||; s|^\\[||; s|\\]$||'); " + "fi; " + 'printf "%s" "$HOST"' + ) + result = subprocess.run( + ["bash", "-c", script, "_", url], + capture_output=True, + text=True, + check=True, + ) + return result.stdout + + @pytest.mark.parametrize( + "url,expected", + [ + ( + "https://mac-mini.tailac7b3c.ts.net/agent-event-bus/mcp", + "mac-mini.tailac7b3c.ts.net", + ), + ("http://host.example.com:8080/mcp", "host.example.com"), + ("http://127.0.0.1:8080/mcp", "127.0.0.1"), + ("http://[::1]:8080/mcp", "::1"), + ("https://localhost/mcp", "localhost"), + ("stdio://something", ""), # non-http scheme → HOST unset + ("", ""), # empty URL → HOST unset + ("https://-oProxyCommand=id/mcp", "-oProxyCommand=id"), # mitigated by `ssh --` + ], + ) + def test_host_extraction(self, url, expected): + assert self._resolve_host(url) == expected + + class TestLogFileEnvVar: """Tests for AGENT_EVENT_BUS_LOG env var override.""" From e22f1013a6b5c20948119a6d1b49e101e5618eff Mon Sep 17 00:00:00 2001 From: Evan Senter Date: Wed, 22 Apr 2026 20:45:25 +0100 Subject: [PATCH 4/4] docs: fix CLAUDE.md env-var example style + test docstring line ref - CLAUDE.md: show AGENT_EVENT_BUS_LOG/ERR prefixed on `make install-server` rather than as bare shell assignments. Bare assignments set vars only for the current shell and would NOT flow into the install script's sed, making the previous example a real copy-paste footgun. - tests/test_server.py: correct Makefile line reference in TestMakeLogsHostDetection docstring (pipeline moved to 166-168 after the env-var refactor). Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 9 +++++---- tests/test_server.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 89e90f2..d1e8e2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -158,10 +158,11 @@ make logs BUS_HOST=your-server.tailnet.ts.net # Override database path AGENT_EVENT_BUS_DB=/path/to/db.sqlite agent-event-bus -# Override log/error file paths (set before `make install-server` so the -# launchd/systemd templates are generated with the custom paths) -AGENT_EVENT_BUS_LOG=/path/to/custom.log -AGENT_EVENT_BUS_ERR=/path/to/custom.err +# Override log/error file paths — the install scripts substitute these into +# the launchd plist / systemd unit, so they must be in the environment of +# `make install-server` itself. Prefix on the make invocation (or `export` +# them first) — bare shell assignments below will NOT apply at install time. +AGENT_EVENT_BUS_LOG=/path/to/custom.log AGENT_EVENT_BUS_ERR=/path/to/custom.err make install-server # Dev mode console logging DEV_MODE=1 agent-event-bus diff --git a/tests/test_server.py b/tests/test_server.py index 57df32c..ce54c68 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1269,8 +1269,8 @@ def test_publish_event_no_warning_on_valid_channel(self, caplog): class TestMakeLogsHostDetection: """Tests for the host-detection pipeline in the Makefile's `logs` target. - The sed/grep pipeline is at Makefile:154-157. These tests duplicate it via - shell subprocess so regressions in the URL → HOST extraction are caught. + The grep-then-sed pipeline is at Makefile:166-168. These tests duplicate it + via shell subprocess so regressions in the URL → HOST extraction are caught. Keep this in sync if the Makefile pipeline changes. """