Product layer on top of the pure engine: a wizard to connect the apps that feed the brain, an encrypted credential vault (Fernet/PBKDF2; app logins never enter brain.db), and 3 working connectors. Connector data flows through the same credential-scrub path as the engine. Atlas Corporation proprietary.
60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
# SecondBrain — (c) 2026 Atlas Corporation. All Rights Reserved. Proprietary; see LICENSE.
|
|
"""Connector framework — the "senses" made concrete.
|
|
|
|
Each connector authenticates to an app, pulls data, and feeds it through the SAME
|
|
scrub + dedup path as the core engine (`brain._stage`). Consequences:
|
|
* connector data is credential-scrubbed too (secrets never land in brain.db);
|
|
* app logins live only in the encrypted vault (see vault.py), never in brain.db;
|
|
* the engine (brain.py) stays completely connector-agnostic.
|
|
"""
|
|
import importlib, os, sys, pkgutil
|
|
|
|
REGISTRY = {}
|
|
def register(cls):
|
|
REGISTRY[cls.id] = cls
|
|
return cls
|
|
|
|
class Field:
|
|
def __init__(self, key, label, secret=False, default="", required=True):
|
|
self.key, self.label, self.secret = key, label, secret
|
|
self.default, self.required = default, required
|
|
|
|
class Connector:
|
|
id = "" # short stable id (vault key)
|
|
name = "" # display name
|
|
blurb = "" # one-line description
|
|
fields = [] # list[Field] the wizard collects
|
|
def test(self, cfg): # verify creds; raise on failure; return status str
|
|
return "ok"
|
|
def sync(self, cfg): # pull -> feed(); return new-chunk count
|
|
raise NotImplementedError
|
|
|
|
def _brain():
|
|
"""Import the sibling engine regardless of CWD."""
|
|
here = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
if here not in sys.path:
|
|
sys.path.insert(0, here)
|
|
import brain
|
|
return brain
|
|
|
|
def feed(items, source):
|
|
"""Stage connector items through the engine's scrub+dedup.
|
|
items: iterable of dict(text[, project, path, ref, ts, role]).
|
|
Returns new-chunk count. Does NOT embed (run brain.embed() after a sync batch)."""
|
|
brain = _brain()
|
|
conn = brain.db(); n = 0
|
|
for it in items:
|
|
if not it.get("text"):
|
|
continue
|
|
n += brain._stage(conn, source, it.get("project", ""), it.get("path", ""),
|
|
it.get("ref", ""), it.get("ts", ""), it.get("role", ""), it["text"])
|
|
conn.commit(); conn.close()
|
|
return n
|
|
|
|
def load_all():
|
|
"""Import every connector module so it self-registers."""
|
|
pkgdir = os.path.dirname(os.path.abspath(__file__))
|
|
for m in pkgutil.iter_modules([pkgdir]):
|
|
if m.name != "base" and not m.name.startswith("_") and m.name != "vault":
|
|
importlib.import_module(f"connectors.{m.name}")
|
|
return REGISTRY
|