fix(cron): scope cron job store to workspace instead of global directory
Replace `get_cron_dir()` with `config.workspace_path / "cron"` so each workspace keeps its own `jobs.json`. This lets users run multiple nanobot instances with independent cron schedules without cross-talk. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -465,7 +465,6 @@ def gateway(
|
|||||||
from nanobot.agent.loop import AgentLoop
|
from nanobot.agent.loop import AgentLoop
|
||||||
from nanobot.bus.queue import MessageBus
|
from nanobot.bus.queue import MessageBus
|
||||||
from nanobot.channels.manager import ChannelManager
|
from nanobot.channels.manager import ChannelManager
|
||||||
from nanobot.config.paths import get_cron_dir
|
|
||||||
from nanobot.cron.service import CronService
|
from nanobot.cron.service import CronService
|
||||||
from nanobot.cron.types import CronJob
|
from nanobot.cron.types import CronJob
|
||||||
from nanobot.heartbeat.service import HeartbeatService
|
from nanobot.heartbeat.service import HeartbeatService
|
||||||
@@ -485,8 +484,8 @@ def gateway(
|
|||||||
provider = _make_provider(config)
|
provider = _make_provider(config)
|
||||||
session_manager = SessionManager(config.workspace_path)
|
session_manager = SessionManager(config.workspace_path)
|
||||||
|
|
||||||
# Create cron service first (callback set after agent creation)
|
# Create cron service with workspace-scoped store
|
||||||
cron_store_path = get_cron_dir() / "jobs.json"
|
cron_store_path = config.workspace_path / "cron" / "jobs.json"
|
||||||
cron = CronService(cron_store_path)
|
cron = CronService(cron_store_path)
|
||||||
|
|
||||||
# Create agent with cron service
|
# Create agent with cron service
|
||||||
@@ -663,7 +662,6 @@ def agent(
|
|||||||
|
|
||||||
from nanobot.agent.loop import AgentLoop
|
from nanobot.agent.loop import AgentLoop
|
||||||
from nanobot.bus.queue import MessageBus
|
from nanobot.bus.queue import MessageBus
|
||||||
from nanobot.config.paths import get_cron_dir
|
|
||||||
from nanobot.cron.service import CronService
|
from nanobot.cron.service import CronService
|
||||||
|
|
||||||
config = _load_runtime_config(config, workspace)
|
config = _load_runtime_config(config, workspace)
|
||||||
@@ -673,8 +671,8 @@ def agent(
|
|||||||
bus = MessageBus()
|
bus = MessageBus()
|
||||||
provider = _make_provider(config)
|
provider = _make_provider(config)
|
||||||
|
|
||||||
# Create cron service for tool usage (no callback needed for CLI unless running)
|
# Create cron service with workspace-scoped store
|
||||||
cron_store_path = get_cron_dir() / "jobs.json"
|
cron_store_path = config.workspace_path / "cron" / "jobs.json"
|
||||||
cron = CronService(cron_store_path)
|
cron = CronService(cron_store_path)
|
||||||
|
|
||||||
if logs:
|
if logs:
|
||||||
|
|||||||
@@ -275,10 +275,8 @@ def mock_agent_runtime(tmp_path):
|
|||||||
"""Mock agent command dependencies for focused CLI tests."""
|
"""Mock agent command dependencies for focused CLI tests."""
|
||||||
config = Config()
|
config = Config()
|
||||||
config.agents.defaults.workspace = str(tmp_path / "default-workspace")
|
config.agents.defaults.workspace = str(tmp_path / "default-workspace")
|
||||||
cron_dir = tmp_path / "data" / "cron"
|
|
||||||
|
|
||||||
with patch("nanobot.config.loader.load_config", return_value=config) as mock_load_config, \
|
with patch("nanobot.config.loader.load_config", return_value=config) as mock_load_config, \
|
||||||
patch("nanobot.config.paths.get_cron_dir", return_value=cron_dir), \
|
|
||||||
patch("nanobot.cli.commands.sync_workspace_templates") as mock_sync_templates, \
|
patch("nanobot.cli.commands.sync_workspace_templates") as mock_sync_templates, \
|
||||||
patch("nanobot.cli.commands._make_provider", return_value=object()), \
|
patch("nanobot.cli.commands._make_provider", return_value=object()), \
|
||||||
patch("nanobot.cli.commands._print_agent_response") as mock_print_response, \
|
patch("nanobot.cli.commands._print_agent_response") as mock_print_response, \
|
||||||
@@ -351,7 +349,6 @@ def test_agent_config_sets_active_path(monkeypatch, tmp_path: Path) -> None:
|
|||||||
lambda path: seen.__setitem__("config_path", path),
|
lambda path: seen.__setitem__("config_path", path),
|
||||||
)
|
)
|
||||||
monkeypatch.setattr("nanobot.config.loader.load_config", lambda _path=None: config)
|
monkeypatch.setattr("nanobot.config.loader.load_config", lambda _path=None: config)
|
||||||
monkeypatch.setattr("nanobot.config.paths.get_cron_dir", lambda: config_file.parent / "cron")
|
|
||||||
monkeypatch.setattr("nanobot.cli.commands.sync_workspace_templates", lambda _path: None)
|
monkeypatch.setattr("nanobot.cli.commands.sync_workspace_templates", lambda _path: None)
|
||||||
monkeypatch.setattr("nanobot.cli.commands._make_provider", lambda _config: object())
|
monkeypatch.setattr("nanobot.cli.commands._make_provider", lambda _config: object())
|
||||||
monkeypatch.setattr("nanobot.bus.queue.MessageBus", lambda: object())
|
monkeypatch.setattr("nanobot.bus.queue.MessageBus", lambda: object())
|
||||||
@@ -508,7 +505,6 @@ def test_gateway_uses_config_directory_for_cron_store(monkeypatch, tmp_path: Pat
|
|||||||
|
|
||||||
monkeypatch.setattr("nanobot.config.loader.set_config_path", lambda _path: None)
|
monkeypatch.setattr("nanobot.config.loader.set_config_path", lambda _path: None)
|
||||||
monkeypatch.setattr("nanobot.config.loader.load_config", lambda _path=None: config)
|
monkeypatch.setattr("nanobot.config.loader.load_config", lambda _path=None: config)
|
||||||
monkeypatch.setattr("nanobot.config.paths.get_cron_dir", lambda: config_file.parent / "cron")
|
|
||||||
monkeypatch.setattr("nanobot.cli.commands.sync_workspace_templates", lambda _path: None)
|
monkeypatch.setattr("nanobot.cli.commands.sync_workspace_templates", lambda _path: None)
|
||||||
monkeypatch.setattr("nanobot.cli.commands._make_provider", lambda _config: object())
|
monkeypatch.setattr("nanobot.cli.commands._make_provider", lambda _config: object())
|
||||||
monkeypatch.setattr("nanobot.bus.queue.MessageBus", lambda: object())
|
monkeypatch.setattr("nanobot.bus.queue.MessageBus", lambda: object())
|
||||||
@@ -524,7 +520,7 @@ def test_gateway_uses_config_directory_for_cron_store(monkeypatch, tmp_path: Pat
|
|||||||
result = runner.invoke(app, ["gateway", "--config", str(config_file)])
|
result = runner.invoke(app, ["gateway", "--config", str(config_file)])
|
||||||
|
|
||||||
assert isinstance(result.exception, _StopGateway)
|
assert isinstance(result.exception, _StopGateway)
|
||||||
assert seen["cron_store"] == config_file.parent / "cron" / "jobs.json"
|
assert seen["cron_store"] == config.workspace_path / "cron" / "jobs.json"
|
||||||
|
|
||||||
|
|
||||||
def test_gateway_uses_configured_port_when_cli_flag_is_missing(monkeypatch, tmp_path: Path) -> None:
|
def test_gateway_uses_configured_port_when_cli_flag_is_missing(monkeypatch, tmp_path: Path) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user