secondbrain/setup.py
Atlas eb5b67dee2 Add connectors + setup wizard (files / IMAP email / Nextcloud-WebDAV) + encrypted vault
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.
2026-07-01 21:50:20 +02:00

86 lines
2.9 KiB
Python
Executable file

#!/usr/bin/env python3
# SecondBrain — (c) 2026 Atlas Corporation. All Rights Reserved. Proprietary; see LICENSE.
"""SecondBrain setup wizard — connect the apps that feed your brain.
python3 setup.py interactive: pick apps, sign in, test, sync
python3 setup.py sync re-sync every already-connected app (for cron/timer)
python3 setup.py list show available connectors
App credentials go into the encrypted vault (connectors/vault.py), never into brain.db.
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from connectors import base, vault
def _ask(field):
import getpass
label = f" {field.label}" + (f" [{field.default}]" if field.default else "") + ": "
val = getpass.getpass(label) if field.secret else input(label)
return (val or "").strip() or field.default
def cmd_list():
base.load_all()
print("\nAvailable connectors:")
for c in base.REGISTRY.values():
print(f"{c.name:26} {c.blurb}")
def wizard():
base.load_all()
conns = list(base.REGISTRY.values())
print("\n🧠 SecondBrain — connect your apps\n")
for i, c in enumerate(conns, 1):
print(f" {i}. {c.name}{c.blurb}")
print(" q. done")
cfgs = vault.load() if os.path.exists(vault.VAULT) else {}
while True:
ch = input("\nConnect which? [number/q] ").strip().lower()
if ch in ("q", ""):
break
try:
c = conns[int(ch) - 1]
except Exception:
print(" ?"); continue
inst = c()
cfg = {f.key: _ask(f) for f in inst.fields}
try:
print(" testing…", inst.test(cfg))
except Exception as e:
print(f" ✗ could not connect: {e}"); continue
cfgs[c.id] = cfg
vault.save(cfgs)
print(f"{c.name} connected + saved to encrypted vault")
if input(" sync now? [Y/n] ").strip().lower() in ("", "y"):
try:
n = inst.sync(cfg); print(f" … ingested {n} new chunks")
except Exception as e:
print(f" sync error: {e}")
print("\nEmbedding new content (local)…")
try:
base._brain().embed(limit=20000)
except Exception as e:
print(f" embed skipped: {e}")
print('Done. Ask your brain: python3 brain.py recall "" --hybrid\n')
def cmd_sync():
base.load_all()
cfgs = vault.load()
if not cfgs:
print("no connectors configured — run `python3 setup.py` first"); return
for cid, cfg in cfgs.items():
c = base.REGISTRY.get(cid)
if not c:
print(f"[{cid}] unknown connector, skipped"); continue
try:
n = c().sync(cfg); print(f"[{cid}] +{n} chunks")
except Exception as e:
print(f"[{cid}] error: {e}")
base._brain().embed(limit=50000)
if __name__ == "__main__":
arg = sys.argv[1] if len(sys.argv) > 1 else ""
{"sync": cmd_sync, "list": cmd_list}.get(arg, wizard)()