refactor(orchestrator): collapse decnet-emailgen.service into orchestrator
Stage 5 of the realism migration. Email generation is no longer a separate worker / systemd unit / CLI subcommand — the orchestrator's single tick loop covers SSH traffic, file plants, and email drops. Going from 21 services to 20. Worker: - _one_tick rolls between traffic / file / email (45/45/10 weights). The 10% email weight at a 60s orchestrator interval produces ~one email per 10 minutes, close to the pre-collapse 5-minute cadence. - get_driver_for(action) (stage 4) handles SSH vs Email dispatch. - Quiet branches fall through so a (decky-set, persona-pool, mail-decky) shape that silences one branch doesn't waste the tick. - Periodic prune covers both orchestrator_events and orchestrator_emails tables. Deletions: - deploy/decnet-emailgen.service.j2 - decnet/orchestrator/emailgen/worker.py - decnet/cli/emailgen.py - tests/orchestrator/emailgen/test_worker_integration.py Renames (history-preserving): - decnet/web/router/emailgen/ -> decnet/web/router/realism/ - tests/api/emailgen/ -> tests/api/realism/ - tests/cli/test_emailgen_* -> tests/cli/test_realism_* Public surface changes (clean break, pre-v1): - API URL /api/v1/emailgen/personas -> /api/v1/realism/personas - CLI `decnet emailgen import-personas` -> `decnet realism import-personas`. `decnet emailgen run` is gone — the orchestrator covers it. - gating.py: emailgen master-only group replaced by realism. - decnet-orchestrator.service.j2: DECNET_REALISM_* env block added. - decnet.target: decnet-emailgen.service entry removed. - frontend: PersonaGeneration.tsx fetches /realism/personas.
This commit is contained in:
@@ -31,7 +31,7 @@ from .campaigns.api_list_campaign_identities import router as campaign_identitie
|
||||
from .campaigns.api_events import router as campaign_events_router
|
||||
from .orchestrator.api_list_events import router as orchestrator_list_router
|
||||
from .orchestrator.api_events import router as orchestrator_events_router
|
||||
from .emailgen.api_personas import router as emailgen_personas_router
|
||||
from .realism.api_personas import router as realism_personas_router
|
||||
from .transcripts import transcripts_router
|
||||
from .config.api_get_config import router as config_get_router
|
||||
from .config.api_update_config import router as config_update_router
|
||||
@@ -111,10 +111,10 @@ api_router.include_router(campaign_events_router)
|
||||
api_router.include_router(orchestrator_list_router)
|
||||
api_router.include_router(orchestrator_events_router)
|
||||
|
||||
# Emailgen — global persona pool CRUD for the dashboard's
|
||||
# "Persona Generation" page. The worker reads from the same on-disk
|
||||
# JSON file directly (see decnet.realism.personas_pool).
|
||||
api_router.include_router(emailgen_personas_router)
|
||||
# Realism — global persona pool CRUD for the dashboard's
|
||||
# "Persona Generation" page. The orchestrator reads from the same
|
||||
# on-disk JSON file directly (see decnet.realism.personas_pool).
|
||||
api_router.include_router(realism_personas_router)
|
||||
|
||||
# Observability
|
||||
api_router.include_router(stats_router)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""GET/PUT ``/api/v1/emailgen/personas`` — global persona pool CRUD.
|
||||
"""GET/PUT ``/api/v1/realism/personas`` — global persona pool CRUD.
|
||||
|
||||
The "global pool" is a JSON file consumed by the realism content
|
||||
engine for fleet (MACVLAN/IPVLAN) and SWARM-shard deckies — see
|
||||
@@ -29,7 +29,7 @@ from decnet.web.dependencies import require_admin, require_viewer
|
||||
from decnet.web.db.models.common import MessageResponse # noqa: F401 - response shape
|
||||
|
||||
router = APIRouter()
|
||||
log = get_logger("api.emailgen.personas")
|
||||
log = get_logger("api.realism.personas")
|
||||
|
||||
|
||||
def _serialize(personas: list[EmailPersona]) -> list[dict[str, Any]]:
|
||||
@@ -38,14 +38,14 @@ def _serialize(personas: list[EmailPersona]) -> list[dict[str, Any]]:
|
||||
|
||||
|
||||
@router.get(
|
||||
"/emailgen/personas",
|
||||
"/realism/personas",
|
||||
tags=["Emailgen"],
|
||||
responses={
|
||||
401: {"description": "Could not validate credentials"},
|
||||
403: {"description": "Insufficient permissions"},
|
||||
},
|
||||
)
|
||||
@_traced("api.emailgen.list_personas")
|
||||
@_traced("api.realism.list_personas")
|
||||
async def list_personas(
|
||||
user: dict = Depends(require_viewer),
|
||||
) -> dict[str, Any]:
|
||||
@@ -56,7 +56,7 @@ async def list_personas(
|
||||
discoverable.
|
||||
"""
|
||||
# Reset the in-process cache before reading so a fresh CLI-driven
|
||||
# ``decnet emailgen import-personas`` shows up immediately rather
|
||||
# ``decnet realism import-personas`` shows up immediately rather
|
||||
# than waiting on the worker's mtime check.
|
||||
global_pool.reset_cache()
|
||||
personas = global_pool.load()
|
||||
@@ -67,7 +67,7 @@ async def list_personas(
|
||||
|
||||
|
||||
@router.put(
|
||||
"/emailgen/personas",
|
||||
"/realism/personas",
|
||||
tags=["Emailgen"],
|
||||
responses={
|
||||
400: {"description": "Invalid persona payload"},
|
||||
@@ -75,7 +75,7 @@ async def list_personas(
|
||||
403: {"description": "Insufficient permissions"},
|
||||
},
|
||||
)
|
||||
@_traced("api.emailgen.replace_personas")
|
||||
@_traced("api.realism.replace_personas")
|
||||
async def replace_personas(
|
||||
body: dict[str, Any],
|
||||
user: dict = Depends(require_admin),
|
||||
@@ -121,7 +121,7 @@ async def replace_personas(
|
||||
# not writable by the API process. Surface a 500 with the
|
||||
# actionable hint instead of leaking a traceback.
|
||||
log.warning(
|
||||
"api.emailgen.replace_personas write failed path=%s err=%s",
|
||||
"api.realism.replace_personas write failed path=%s err=%s",
|
||||
dest, exc,
|
||||
)
|
||||
raise HTTPException(
|
||||
@@ -134,7 +134,7 @@ async def replace_personas(
|
||||
) from exc
|
||||
global_pool.reset_cache()
|
||||
log.info(
|
||||
"api.emailgen.replace_personas user=%s wrote=%d path=%s",
|
||||
"api.realism.replace_personas user=%s wrote=%d path=%s",
|
||||
user.get("username", user.get("uuid")), len(parsed), dest,
|
||||
)
|
||||
return {
|
||||
Reference in New Issue
Block a user