skills: add datto-edr (Datto EDR / Infocyte HUNT) + syncro-rmm memory
New /datto-edr skill — standalone CLI for the Datto EDR REST API (azcomp4587, rebranded Infocyte HUNT, raw-token LoopBack). Live-verified reads across the whole MSP fleet: orgs/sites/agents/detections/sweep + deploy-cmd. Scan/isolate gated behind --confirm (shape-verified, not run against prod). Token vaulted at msp-tools/datto-edr.sops.yaml. Also: reference_syncro_rmm_api_gui_only memory (Syncro RMM policy mgmt is GUI-only) and the guru-rmm submodule pointer bump (Feature 6 EDR research). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
51
.claude/skills/datto-edr/scripts/selftest.py
Normal file
51
.claude/skills/datto-edr/scripts/selftest.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Self-test for the datto-edr skill. Read-only; never mutates the tenant.
|
||||
|
||||
Confirms: token loads from vault, transport works, and the core read endpoints
|
||||
return live data. Exits non-zero on any failure. Set EDR_SUPPRESS_ERRORLOG=1 so
|
||||
expected probe errors don't spam errorlog.md.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ.setdefault("EDR_SUPPRESS_ERRORLOG", "1")
|
||||
|
||||
from edr_client import DattoEDRClient, DattoEDRError
|
||||
|
||||
|
||||
def main() -> int:
|
||||
checks: list[tuple[str, bool, str]] = []
|
||||
try:
|
||||
client = DattoEDRClient()
|
||||
_ = client.api_token
|
||||
checks.append(("vault token load", True, ""))
|
||||
except DattoEDRError as exc:
|
||||
print(f"[FAIL] token load: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
def check(name, fn):
|
||||
try:
|
||||
val = fn()
|
||||
checks.append((name, True, str(val)))
|
||||
except Exception as exc: # noqa: BLE001 - selftest summarizes failures
|
||||
checks.append((name, False, str(exc)))
|
||||
|
||||
check("status rollup", lambda: client.get_status()["agents"])
|
||||
check("list organizations", lambda: f"{len(client.list_organizations())} orgs")
|
||||
check("list locations", lambda: f"{len(client.list_locations())} sites")
|
||||
check("list targets", lambda: f"{len(client.list_targets())} scan groups")
|
||||
check("list agents (limit 5)", lambda: f"{len(client.list_agents(limit=5))} agents")
|
||||
check("list alerts (limit 5)", lambda: f"{len(client.list_alerts(limit=5))} alerts")
|
||||
|
||||
ok = True
|
||||
for name, passed, detail in checks:
|
||||
marker = "[OK]" if passed else "[FAIL]"
|
||||
print(f"{marker} {name}{(' -> ' + detail) if detail else ''}")
|
||||
ok = ok and passed
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user