Replaces LICENSE (GPLv3 -> AGPLv3) and prepends `SPDX-License-Identifier: AGPL-3.0-or-later` to every source file across decnet/, decnet_web/, tests/, scripts/, and tools/. Rationale: closes the GPLv3 ASP loophole so any party operating a modified DECNET as a network service must offer their modified source. Personal copyright (Samuel Paschuan) + inbound=outbound contributions make a future unilateral relicense infeasible. - LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt) - COPYRIGHT: project copyright notice - tools/add_spdx_headers.py: idempotent header injector (shebang- and PEP 263-aware) Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh). No behavior change; comments only.
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""Driver ABC for orchestrator actions.
|
|
|
|
Each concrete driver (SSH, Email, future HTTP/SMB/MySQL) maps one
|
|
:class:`~decnet.orchestrator.scheduler.Action` shape to a side effect
|
|
on a target decky and returns an :class:`ActivityResult` the
|
|
orchestrator persists.
|
|
|
|
The ABC lives here, the dispatch factory in
|
|
:mod:`decnet.orchestrator.drivers` ``__init__``, and the impls in
|
|
sibling modules — same pattern as :mod:`decnet.canary.factory`,
|
|
:mod:`decnet.web.db.factory`, and :mod:`decnet.bus.factory`.
|
|
|
|
Why ABC and not :class:`Protocol`: drivers also expose lower-level
|
|
helpers (``plant_file``, ``read_file``) that the planner-driven
|
|
realism path will call directly without going through ``run``.
|
|
Inheritance pins the contract for those helpers; a structural
|
|
protocol would let a typo silently produce a half-implemented driver.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from decnet.orchestrator.scheduler import Action
|
|
|
|
|
|
@dataclass
|
|
class ActivityResult:
|
|
"""Outcome of one driver invocation.
|
|
|
|
``payload`` is the per-action JSON envelope the worker writes to
|
|
the ``OrchestratorEvent.payload`` column and to the bus event
|
|
body.
|
|
"""
|
|
success: bool
|
|
payload: dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
class ActivityDriver(ABC):
|
|
"""Base class every concrete orchestrator driver inherits.
|
|
|
|
Subclasses MUST implement :meth:`run` — the action-shape dispatch.
|
|
Subclasses that interact with files on the target decky SHOULD
|
|
implement :meth:`plant_file` and :meth:`read_file` so the realism
|
|
edit-in-place path can read existing artifacts before mutating
|
|
them. Drivers that don't touch files (e.g. a future pure-traffic
|
|
driver) raise :class:`NotImplementedError` from those, and the
|
|
planner avoids picking ``EditAction`` for them.
|
|
"""
|
|
|
|
@abstractmethod
|
|
async def run(self, action: Action) -> ActivityResult:
|
|
"""Execute the action against its target decky."""
|
|
|
|
async def plant_file(
|
|
self,
|
|
decky_name: str,
|
|
path: str,
|
|
content: bytes,
|
|
*,
|
|
mode: int = 0o600,
|
|
mtime: datetime | None = None,
|
|
) -> ActivityResult:
|
|
"""Write *content* to *path* inside *decky_name*.
|
|
|
|
Default raises :class:`NotImplementedError`; concrete drivers
|
|
that have a write transport (docker exec, ssh, etc.) override.
|
|
Bytes-typed so binary artifacts (DOCX/PDF) survive the wire.
|
|
"""
|
|
raise NotImplementedError(
|
|
f"{type(self).__name__} does not support plant_file"
|
|
)
|
|
|
|
async def read_file(self, decky_name: str, path: str) -> bytes:
|
|
"""Read *path* from inside *decky_name*.
|
|
|
|
Required for the realism edit-in-place flow (stage 3b of the
|
|
realism migration): the driver reads the previous body, the
|
|
realism engine produces the next iteration, the driver writes
|
|
it back. Default raises :class:`NotImplementedError`.
|
|
"""
|
|
raise NotImplementedError(
|
|
f"{type(self).__name__} does not support read_file"
|
|
)
|
|
|
|
|
|
# Back-compat alias so existing imports of ``Driver`` keep working
|
|
# while consumers transition to ``ActivityDriver``. Removed once the
|
|
# realism migration is complete.
|
|
Driver = ActivityDriver
|