feat(daemon): add core asyncio daemon, supervisor, and registry factories
- core.py: asyncio event loop entry point, PluginSupervisor with per-task restart (up to 10 times, 5s back-off), IPC dispatch, signal handling (SIGTERM/SIGHUP on POSIX), RotatingFileHandler, start_background() helper - daemon/__init__.py: export public API - plugins/registry.py: add get_daemon_task_factories() so supervisor can restart crashed tasks by re-calling plugin.daemon_tasks()[i] - config/schema.py: add DaemonConfig.ipc_port for Windows TCP loopback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,29 @@ class PluginRegistry:
|
||||
pass
|
||||
return tasks
|
||||
|
||||
def get_daemon_task_factories(
|
||||
self,
|
||||
) -> list[tuple[str, Callable[[], Coroutine]]]: # type: ignore[type-arg]
|
||||
"""Return (name, factory) pairs for all plugin daemon tasks.
|
||||
|
||||
Each factory re-calls plugin.daemon_tasks() to produce a fresh coroutine,
|
||||
enabling the supervisor to restart crashed tasks without changing the plugin
|
||||
protocol.
|
||||
"""
|
||||
factories: list[tuple[str, Callable[[], Coroutine]]] = [] # type: ignore[type-arg]
|
||||
for plugin in self._plugins.values():
|
||||
try:
|
||||
n_tasks = len(plugin.daemon_tasks())
|
||||
except Exception:
|
||||
continue
|
||||
for i in range(n_tasks):
|
||||
name = f"{plugin.name}.task_{i}"
|
||||
# Capture plugin and index by value so each closure is independent.
|
||||
def _factory(p=plugin, idx=i) -> Coroutine: # type: ignore[type-arg]
|
||||
return p.daemon_tasks()[idx]
|
||||
factories.append((name, _factory))
|
||||
return factories
|
||||
|
||||
def find_tool(self, name: str) -> Tool | None:
|
||||
return self._tools.get(name)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user