DHARMA network: per-soul Engram instances (8801–8819)

Each soul now gets their own isolated Engram process with a dedicated data
directory (imprints/<slug>/), port, and API key — the DHARMA network.
Neuron's Engram at 8742 is never touched.

- registry.json: add engram_db_path, engram_port, engram_url to all 19 entries
  (Bobby Anderson 8801 → Helen Keller 8819)
- launch_dharma.sh: start all 19 soul Engrams in background; supports
  single-slug filter and skip-if-running detection
- stop_dharma.sh: graceful shutdown via PID file, falls back to port scan
- reinstall_imprints.py: bulk reinstall — starts each Engram temporarily,
  installs seed, records new root_id, stops; supports --slug and --dry-run
- src/install.el: resolves soul's engram_url from registry (never 8742);
  guards against accidental writes to Neuron's shared Engram
- src/summon.el: reads engram_url per-soul from registry; passes it to soul
  server in POST body so soul connects to the right Engram; supports
  both single-soul (engram_url) and multi-soul (engram_urls array) payloads
This commit is contained in:
Will Anderson
2026-05-03 02:52:01 -05:00
parent 52b6830ab2
commit 0a15e2fa42
6 changed files with 1118 additions and 183 deletions
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# DHARMA network — start all 19 soul Engrams in background.
# Each soul gets their own Engram instance, data directory, and port.
# Neuron's Engram stays untouched at localhost:8742.
#
# Usage:
# ./launch_dharma.sh start all soul Engrams
# ./launch_dharma.sh <slug> start a single soul by slug
#
# PIDs are written to /tmp/dharma-pids for stop_dharma.sh
set -euo pipefail
FORGE=/Users/will/Development/neuron-technologies/forge
ENGRAM_BIN=/Users/will/Development/neuron-technologies/foundation/engram/dist/engram
PID_FILE=/tmp/dharma-pids
if [ ! -x "$ENGRAM_BIN" ]; then
echo "[dharma] ERROR: engram binary not found at $ENGRAM_BIN"
echo "[dharma] Build it with: cd /Users/will/Development/neuron-technologies/foundation/engram && cargo build --release"
exit 1
fi
# Soul definitions: slug|port
SOULS=(
"bobby-anderson|8801"
"alan-turing|8802"
"albert-einstein|8803"
"nikola-tesla|8804"
"leonardo-da-vinci|8805"
"richard-feynman|8806"
"carl-sagan|8807"
"rene-descartes|8808"
"robin-williams|8809"
"frederick-douglass|8810"
"marcus-aurelius|8811"
"friedrich-nietzsche|8812"
"james-baldwin|8813"
"ada-lovelace|8814"
"harriet-tubman|8815"
"virginia-woolf|8816"
"hedy-lamarr|8817"
"marie-curie|8818"
"helen-keller|8819"
)
# Optional single-soul filter
FILTER="${1:-}"
# Ensure PID file is clean on fresh launch (without filter)
if [ -z "$FILTER" ]; then
> "$PID_FILE"
fi
started=0
skipped=0
for entry in "${SOULS[@]}"; do
slug="${entry%|*}"
port="${entry#*|}"
# If a filter is specified, only start matching soul
if [ -n "$FILTER" ] && [ "$slug" != "$FILTER" ]; then
continue
fi
# Skip if already listening on that port
if lsof -ti tcp:"$port" >/dev/null 2>&1; then
echo "[dharma] $slug already running on port $port — skipping"
skipped=$((skipped + 1))
continue
fi
db_path="$FORGE/imprints/$slug"
mkdir -p "$db_path"
api_key="ntn-${slug}-2026"
log_file="$FORGE/log/dharma-${slug}.log"
mkdir -p "$FORGE/log"
echo "[dharma] starting $slug on :$port (db: imprints/$slug)"
ENGRAM_DB_PATH="$db_path" \
ENGRAM_BIND="0.0.0.0:$port" \
ENGRAM_API_KEY="$api_key" \
ENGRAM_PEER_NAME="dharma-$slug" \
"$ENGRAM_BIN" >> "$log_file" 2>&1 &
pid=$!
echo "$pid $slug $port" >> "$PID_FILE"
started=$((started + 1))
done
echo ""
echo "[dharma] started=$started skipped=$skipped"
echo "[dharma] pids recorded in $PID_FILE"
echo "[dharma] logs in $FORGE/log/dharma-<slug>.log"
echo ""
echo "[dharma] verify with:"
echo " curl -s http://localhost:8801/stats # bobby-anderson"
echo " curl -s http://localhost:8819/stats # helen-keller"
+187 -130
View File
@@ -5,143 +5,20 @@
"subject": "Bobby Anderson",
"slug": "bobby-anderson",
"seed_file": "bobby-anderson-seed.json",
"engram_db_path": "imprints/bobby-anderson",
"engram_port": 8801,
"engram_url": "http://localhost:8801",
"engram_root_id": "0d4ad862-8d87-409f-aff5-b2199fae5611",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Nikola Tesla",
"slug": "nikola-tesla",
"seed_file": "nikola-tesla-seed.json",
"engram_root_id": "968ed4c9-ea3b-427b-964e-156f5b085c48",
"installed": true,
"installed_at": "2026-05-03",
"notes": "Enriched seed on disk (nikola-tesla-seed.json) — not reinstalled to avoid duplicates. Original nodes remain in Engram."
},
{
"subject": "Frederick Douglass",
"slug": "frederick-douglass",
"seed_file": "seeds/frederick-douglass-seed.json",
"engram_root_id": "37ed8061-5a90-4f74-9fb0-0acfae686247",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Marcus Aurelius",
"slug": "marcus-aurelius",
"seed_file": "seeds/marcus-aurelius-seed.json",
"engram_root_id": "7fb915ca-2b3f-4971-a410-ec8dd2588a04",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Friedrich Nietzsche",
"slug": "friedrich-nietzsche",
"seed_file": "seeds/friedrich-nietzsche-seed.json",
"engram_root_id": "e789203a-084a-4b73-9bcd-93b667a221de",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "James Baldwin",
"slug": "james-baldwin",
"seed_file": "seeds/james-baldwin-seed.json",
"engram_root_id": "2d42db43-f6ab-418b-a961-6a87866e8679",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Hedy Lamarr",
"slug": "hedy-lamarr",
"seed_file": "seeds/hedy-lamarr-seed.json",
"engram_root_id": "fc480b53-2552-42de-89b3-9e28f5a216f4",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Marie Curie",
"slug": "marie-curie",
"seed_file": "seeds/marie-curie-seed.json",
"engram_root_id": "45ea08d1-abaa-488a-960f-8eee50ee07d5",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Helen Keller",
"slug": "helen-keller",
"seed_file": "seeds/helen-keller-seed.json",
"engram_root_id": "856cb417-f95e-4ae2-b7bf-902c181b2589",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Ada Lovelace",
"slug": "ada-lovelace",
"seed_file": "seeds/ada-lovelace-seed.json",
"engram_root_id": "cde4dae8-fbfa-45c6-99ed-39e62d0f227b",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Leonardo da Vinci",
"slug": "leonardo-da-vinci",
"seed_file": "leonardo-da-vinci-seed.json",
"engram_root_id": "80a80d10-fa63-4b4d-9f8e-3d67af2ee02b",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Richard Feynman",
"slug": "richard-feynman",
"seed_file": "richard-feynman-seed.json",
"engram_root_id": "30e15d9d-f30e-4970-ae2c-ea20bbc55598",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Carl Sagan",
"slug": "carl-sagan",
"seed_file": "carl-sagan-seed.json",
"engram_root_id": "dd825327-9ddf-474f-9fce-e420491f4a1a",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "René Descartes",
"slug": "rene-descartes",
"seed_file": "rene-descartes-seed.json",
"engram_root_id": "e77f15ca-5499-41ef-9807-17a2261280e0",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Robin Williams",
"slug": "robin-williams",
"seed_file": "robin-williams-seed.json",
"engram_root_id": "d9f62db5-e635-48e6-8235-1fd965058085",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Harriet Tubman",
"slug": "harriet-tubman",
"seed_file": "seeds/harriet-tubman-seed.json",
"engram_root_id": "6e027850-95b2-420c-ba20-0d54d3491c66",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Virginia Woolf",
"slug": "virginia-woolf",
"seed_file": "seeds/virginia-woolf-seed.json",
"engram_root_id": "db5266dc-f518-43d6-863c-5b274792e819",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Alan Turing",
"slug": "alan-turing",
"seed_file": "turing-seed.json",
"engram_db_path": "imprints/alan-turing",
"engram_port": 8802,
"engram_url": "http://localhost:8802",
"engram_root_id": "cce1d430-be4e-420d-9d81-d32380ae7281",
"installed": true,
"installed_at": "2026-05-03"
@@ -150,9 +27,189 @@
"subject": "Albert Einstein",
"slug": "albert-einstein",
"seed_file": "albert-einstein-seed.json",
"engram_db_path": "imprints/albert-einstein",
"engram_port": 8803,
"engram_url": "http://localhost:8803",
"engram_root_id": "291f8502-8f60-4f57-a800-4e3e1425c9bd",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Nikola Tesla",
"slug": "nikola-tesla",
"seed_file": "nikola-tesla-seed.json",
"engram_db_path": "imprints/nikola-tesla",
"engram_port": 8804,
"engram_url": "http://localhost:8804",
"engram_root_id": "968ed4c9-ea3b-427b-964e-156f5b085c48",
"installed": true,
"installed_at": "2026-05-03",
"notes": "Enriched seed on disk (nikola-tesla-seed.json) — not reinstalled to avoid duplicates. Original nodes remain in Engram."
},
{
"subject": "Leonardo da Vinci",
"slug": "leonardo-da-vinci",
"seed_file": "leonardo-da-vinci-seed.json",
"engram_db_path": "imprints/leonardo-da-vinci",
"engram_port": 8805,
"engram_url": "http://localhost:8805",
"engram_root_id": "80a80d10-fa63-4b4d-9f8e-3d67af2ee02b",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Richard Feynman",
"slug": "richard-feynman",
"seed_file": "richard-feynman-seed.json",
"engram_db_path": "imprints/richard-feynman",
"engram_port": 8806,
"engram_url": "http://localhost:8806",
"engram_root_id": "30e15d9d-f30e-4970-ae2c-ea20bbc55598",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Carl Sagan",
"slug": "carl-sagan",
"seed_file": "carl-sagan-seed.json",
"engram_db_path": "imprints/carl-sagan",
"engram_port": 8807,
"engram_url": "http://localhost:8807",
"engram_root_id": "dd825327-9ddf-474f-9fce-e420491f4a1a",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "René Descartes",
"slug": "rene-descartes",
"seed_file": "rene-descartes-seed.json",
"engram_db_path": "imprints/rene-descartes",
"engram_port": 8808,
"engram_url": "http://localhost:8808",
"engram_root_id": "e77f15ca-5499-41ef-9807-17a2261280e0",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Robin Williams",
"slug": "robin-williams",
"seed_file": "robin-williams-seed.json",
"engram_db_path": "imprints/robin-williams",
"engram_port": 8809,
"engram_url": "http://localhost:8809",
"engram_root_id": "d9f62db5-e635-48e6-8235-1fd965058085",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Frederick Douglass",
"slug": "frederick-douglass",
"seed_file": "seeds/frederick-douglass-seed.json",
"engram_db_path": "imprints/frederick-douglass",
"engram_port": 8810,
"engram_url": "http://localhost:8810",
"engram_root_id": "37ed8061-5a90-4f74-9fb0-0acfae686247",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Marcus Aurelius",
"slug": "marcus-aurelius",
"seed_file": "seeds/marcus-aurelius-seed.json",
"engram_db_path": "imprints/marcus-aurelius",
"engram_port": 8811,
"engram_url": "http://localhost:8811",
"engram_root_id": "7fb915ca-2b3f-4971-a410-ec8dd2588a04",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Friedrich Nietzsche",
"slug": "friedrich-nietzsche",
"seed_file": "seeds/friedrich-nietzsche-seed.json",
"engram_db_path": "imprints/friedrich-nietzsche",
"engram_port": 8812,
"engram_url": "http://localhost:8812",
"engram_root_id": "e789203a-084a-4b73-9bcd-93b667a221de",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "James Baldwin",
"slug": "james-baldwin",
"seed_file": "seeds/james-baldwin-seed.json",
"engram_db_path": "imprints/james-baldwin",
"engram_port": 8813,
"engram_url": "http://localhost:8813",
"engram_root_id": "2d42db43-f6ab-418b-a961-6a87866e8679",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Ada Lovelace",
"slug": "ada-lovelace",
"seed_file": "seeds/ada-lovelace-seed.json",
"engram_db_path": "imprints/ada-lovelace",
"engram_port": 8814,
"engram_url": "http://localhost:8814",
"engram_root_id": "cde4dae8-fbfa-45c6-99ed-39e62d0f227b",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Harriet Tubman",
"slug": "harriet-tubman",
"seed_file": "seeds/harriet-tubman-seed.json",
"engram_db_path": "imprints/harriet-tubman",
"engram_port": 8815,
"engram_url": "http://localhost:8815",
"engram_root_id": "6e027850-95b2-420c-ba20-0d54d3491c66",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Virginia Woolf",
"slug": "virginia-woolf",
"seed_file": "seeds/virginia-woolf-seed.json",
"engram_db_path": "imprints/virginia-woolf",
"engram_port": 8816,
"engram_url": "http://localhost:8816",
"engram_root_id": "db5266dc-f518-43d6-863c-5b274792e819",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Hedy Lamarr",
"slug": "hedy-lamarr",
"seed_file": "seeds/hedy-lamarr-seed.json",
"engram_db_path": "imprints/hedy-lamarr",
"engram_port": 8817,
"engram_url": "http://localhost:8817",
"engram_root_id": "fc480b53-2552-42de-89b3-9e28f5a216f4",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Marie Curie",
"slug": "marie-curie",
"seed_file": "seeds/marie-curie-seed.json",
"engram_db_path": "imprints/marie-curie",
"engram_port": 8818,
"engram_url": "http://localhost:8818",
"engram_root_id": "45ea08d1-abaa-488a-960f-8eee50ee07d5",
"installed": true,
"installed_at": "2026-05-03"
},
{
"subject": "Helen Keller",
"slug": "helen-keller",
"seed_file": "seeds/helen-keller-seed.json",
"engram_db_path": "imprints/helen-keller",
"engram_port": 8819,
"engram_url": "http://localhost:8819",
"engram_root_id": "856cb417-f95e-4ae2-b7bf-902c181b2589",
"installed": true,
"installed_at": "2026-05-03"
}
]
}
}
+374
View File
@@ -0,0 +1,374 @@
#!/usr/bin/env python3
"""
reinstall_imprints.py — Reinstall all soul imprints into their own Engram instances.
Each soul gets a dedicated Engram process with:
- Data dir: forge/imprints/<slug>/
- Port: from registry.json (engram_port)
- API key: ntn-<slug>-2026
This script:
1. Reads registry.json for soul metadata
2. For each soul:
a. Starts their Engram instance temporarily
b. Waits for it to be ready (polls GET /stats)
c. Reads their seed JSON file
d. POSTs all nodes/edges to their Engram
e. Records the new root node ID
f. Stops the Engram instance
3. Updates registry.json with confirmed engram_root_id
Do NOT touch Neuron's Engram at localhost:8742.
Usage:
python3 reinstall_imprints.py
python3 reinstall_imprints.py --slug richard-feynman # single soul
python3 reinstall_imprints.py --dry-run # validate only, no writes
"""
import argparse
import json
import os
import signal
import subprocess
import sys
import time
import urllib.error
import urllib.request
from datetime import date
from pathlib import Path
# ── Paths ──────────────────────────────────────────────────────────────────────
FORGE_DIR = Path("/Users/will/Development/neuron-technologies/forge")
ENGRAM_BIN = Path("/Users/will/Development/neuron-technologies/foundation/engram/dist/engram")
REGISTRY_FILE = FORGE_DIR / "registry.json"
# Startup: how long to wait for Engram to become ready
STARTUP_TIMEOUT_S = 30
STARTUP_POLL_INTERVAL_S = 0.5
# HTTP timeout for node/edge creation calls
HTTP_TIMEOUT_S = 300
# ── HTTP helpers ───────────────────────────────────────────────────────────────
def engram_get(base_url: str, path: str) -> dict:
url = f"{base_url}{path}"
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S) as resp:
return json.loads(resp.read())
def engram_post(base_url: str, path: str, body: dict, api_key: str) -> dict:
url = f"{base_url}{path}"
data = json.dumps(body).encode()
req = urllib.request.Request(
url,
data=data,
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=HTTP_TIMEOUT_S) as resp:
return json.loads(resp.read())
# ── Engram lifecycle ───────────────────────────────────────────────────────────
def start_engram(slug: str, db_path: Path, port: int) -> subprocess.Popen:
"""Start an Engram instance for a soul. Returns the process handle."""
api_key = f"ntn-{slug}-2026"
log_path = FORGE_DIR / "log" / f"dharma-{slug}.log"
log_path.parent.mkdir(parents=True, exist_ok=True)
db_path.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
env["ENGRAM_DB_PATH"] = str(db_path)
env["ENGRAM_BIND"] = f"0.0.0.0:{port}"
env["ENGRAM_API_KEY"] = api_key
env["ENGRAM_PEER_NAME"] = f"dharma-{slug}"
log_fh = open(log_path, "a")
proc = subprocess.Popen(
[str(ENGRAM_BIN)],
env=env,
stdout=log_fh,
stderr=log_fh,
)
return proc
def wait_for_engram(base_url: str, timeout_s: float = STARTUP_TIMEOUT_S) -> bool:
"""Poll GET /stats until Engram responds or timeout."""
deadline = time.time() + timeout_s
while time.time() < deadline:
try:
engram_get(base_url, "/stats")
return True
except Exception:
time.sleep(STARTUP_POLL_INTERVAL_S)
return False
def stop_engram(proc: subprocess.Popen, slug: str) -> None:
"""Gracefully stop an Engram process."""
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait()
print(f" [{slug}] Engram stopped")
# ── Imprint install ────────────────────────────────────────────────────────────
def install_imprint(base_url: str, api_key: str, subject: str, seed: dict) -> str:
"""
POST all seed nodes and edges to the soul's Engram.
Returns the root node ID.
"""
# Root node
root = engram_post(base_url, "/nodes", {
"content": f"IMPRINT: {subject} | forge/0.1.0",
"node_type": "Identity",
"salience": 1.0,
}, api_key)
root_id = root["id"]
print(f" root node: {root_id}")
# Value nodes
for v in seed.get("values", []):
content = f"VALUE: {v['value']}"
if v.get("grounding"):
content += f" | grounding: {v['grounding']}"
node = engram_post(base_url, "/nodes", {
"content": content,
"node_type": "Identity",
"salience": v.get("weight", 0.8),
}, api_key)
engram_post(base_url, "/edges", {
"from_id": root_id,
"to_id": node["id"],
"relation": "has_value",
"weight": v.get("weight", 0.8),
}, api_key)
print(f" values: {len(seed.get('values', []))}")
# Biography nodes
for b in seed.get("biography", []):
node = engram_post(base_url, "/nodes", {
"content": f"BIOGRAPHY: {b['event']}",
"node_type": "Identity",
"salience": b.get("weight", 0.7),
}, api_key)
engram_post(base_url, "/edges", {
"from_id": root_id,
"to_id": node["id"],
"relation": "formed_by",
"weight": b.get("weight", 0.7),
}, api_key)
print(f" biography: {len(seed.get('biography', []))}")
# Relationship nodes
for r in seed.get("relationships", []):
content = f"RELATIONSHIP: {r['name']}"
if r.get("role"):
content += f" ({r['role']})"
node = engram_post(base_url, "/nodes", {
"content": content,
"node_type": "Identity",
"salience": r.get("weight", 0.6),
}, api_key)
engram_post(base_url, "/edges", {
"from_id": root_id,
"to_id": node["id"],
"relation": "relates_to",
"weight": r.get("weight", 0.6),
}, api_key)
print(f" relationships: {len(seed.get('relationships', []))}")
# Reasoning pattern nodes
for pattern in seed.get("reasoning_patterns", []):
node = engram_post(base_url, "/nodes", {
"content": f"REASONING: {pattern}",
"node_type": "Identity",
"salience": 0.7,
}, api_key)
engram_post(base_url, "/edges", {
"from_id": root_id,
"to_id": node["id"],
"relation": "reasons_with",
"weight": 0.7,
}, api_key)
print(f" reasoning patterns: {len(seed.get('reasoning_patterns', []))}")
# Voice profile node (if present)
voice = seed.get("voice_profile", {})
if voice:
voice_parts = []
for k, v in voice.items():
voice_parts.append(f"{k}: {v}")
voice_content = "VOICE: " + " | ".join(voice_parts)
node = engram_post(base_url, "/nodes", {
"content": voice_content[:2000], # guard against very long profiles
"node_type": "Identity",
"salience": 0.9,
}, api_key)
engram_post(base_url, "/edges", {
"from_id": root_id,
"to_id": node["id"],
"relation": "speaks_as",
"weight": 0.9,
}, api_key)
print(f" voice profile: installed")
return root_id
# ── Registry helpers ───────────────────────────────────────────────────────────
def load_registry() -> dict:
return json.loads(REGISTRY_FILE.read_text())
def save_registry(registry: dict) -> None:
REGISTRY_FILE.write_text(json.dumps(registry, indent=2, ensure_ascii=False))
def resolve_seed_path(entry: dict) -> Path:
"""Return absolute path to a seed file, resolving relative to FORGE_DIR."""
seed_file = entry["seed_file"]
path = Path(seed_file)
if not path.is_absolute():
path = FORGE_DIR / path
return path
# ── Main ───────────────────────────────────────────────────────────────────────
def main() -> None:
parser = argparse.ArgumentParser(description="Reinstall soul imprints into per-soul Engrams")
parser.add_argument("--slug", help="Only reinstall this soul (by slug)")
parser.add_argument("--dry-run", action="store_true", help="Validate config without writing")
args = parser.parse_args()
if not ENGRAM_BIN.exists():
print(f"ERROR: Engram binary not found at {ENGRAM_BIN}")
print("Build it: cd /Users/will/Development/neuron-technologies/foundation/engram && cargo build --release")
sys.exit(1)
registry = load_registry()
imprints = registry["imprints"]
if args.slug:
imprints = [e for e in imprints if e["slug"] == args.slug]
if not imprints:
print(f"ERROR: slug '{args.slug}' not found in registry.json")
sys.exit(1)
print(f"[reinstall] {'DRY RUN — ' if args.dry_run else ''}reinstalling {len(imprints)} soul(s)")
print(f"[reinstall] engram binary: {ENGRAM_BIN}")
print()
results = []
for entry in imprints:
subject = entry["subject"]
slug = entry["slug"]
port = entry["engram_port"]
base_url = entry["engram_url"]
api_key = f"ntn-{slug}-2026"
db_path = FORGE_DIR / entry["engram_db_path"]
seed_path = resolve_seed_path(entry)
print(f"{'=' * 60}")
print(f"[{slug}] {subject} port={port} db={entry['engram_db_path']}")
# Validate seed file exists
if not seed_path.exists():
print(f" ERROR: seed file not found: {seed_path}")
results.append({"slug": slug, "subject": subject, "ok": False, "error": "seed not found"})
continue
if args.dry_run:
print(f" seed: {seed_path} [ok]")
print(f" port: {port} db: {db_path} [ok]")
results.append({"slug": slug, "subject": subject, "ok": True, "dry_run": True})
continue
seed = json.loads(seed_path.read_text())
proc = None
try:
# Start Engram
print(f" starting Engram on :{port}...")
proc = start_engram(slug, db_path, port)
# Wait for ready
if not wait_for_engram(base_url):
raise RuntimeError(f"Engram did not start within {STARTUP_TIMEOUT_S}s")
print(f" Engram ready at {base_url}")
# Install imprint
root_id = install_imprint(base_url, api_key, subject, seed)
print(f" root_id: {root_id}")
# Update registry entry
entry["engram_root_id"] = root_id
entry["engram_db_path"] = f"imprints/{slug}"
entry["engram_port"] = port
entry["engram_url"] = base_url
entry["installed"] = True
entry["installed_at"] = str(date.today())
save_registry(registry)
print(f" registry updated")
results.append({"slug": slug, "subject": subject, "ok": True, "root_id": root_id})
except Exception as exc:
import traceback
print(f" ERROR: {exc}")
traceback.print_exc()
results.append({"slug": slug, "subject": subject, "ok": False, "error": str(exc)})
finally:
if proc is not None:
stop_engram(proc, slug)
# Brief pause so ports release cleanly
time.sleep(1)
print()
# Summary
print("=" * 60)
print("SUMMARY")
print("=" * 60)
ok_count = sum(1 for r in results if r["ok"])
fail_count = len(results) - ok_count
for r in results:
status = "OK" if r["ok"] else "FAIL"
detail = r.get("root_id", r.get("error", "dry-run"))
print(f" [{status}] {r['subject']:30s} {detail}")
print()
print(f" total={len(results)} ok={ok_count} failed={fail_count}")
if fail_count > 0:
sys.exit(1)
if __name__ == "__main__":
main()
+155 -53
View File
@@ -5,11 +5,18 @@
// creates a root traversal node that names the imprint, and connects
// everything with typed edges. Prints the root node ID on completion.
//
// Depends on: schema.el (engram_url, engram_key, FORGE_VERSION)
// IMPORTANT: Each soul has their own Engram instance (DHARMA network).
// install_main() resolves the soul's Engram URL from registry.json using the
// engram_url field (e.g. http://localhost:8806 for Feynman). It NEVER writes
// to the shared Neuron Engram at localhost:8742.
//
// The soul's Engram must already be running (launch_dharma.sh) before
// installing. For bulk reinstall use: python3 reinstall_imprints.py
//
// Depends on: schema.el (engram_key, FORGE_VERSION)
//
// Environment:
// ENGRAM_URL engram server (default: http://localhost:8742)
// ENGRAM_API_KEY engram auth key (if set)
// ENGRAM_API_KEY engram auth key override (if set)
// Helpers
@@ -31,21 +38,23 @@ fn build_edge_body(from_id: String, to_id: String, relation: String, weight: Str
return base + ",\"_auth\":\"" + auth_key + "\"}"
}
// post_node create a node in engram and return its ID.
fn post_node(content: String, node_type: String, salience: String) -> String {
// post_node create a node in the given engram instance and return its ID.
// target_url is the soul's own Engram URL (e.g. http://localhost:8806).
fn post_node(target_url: String, content: String, node_type: String, salience: String) -> String {
let key: String = engram_key()
let body: String = build_node_body(content, node_type, salience, key)
let url: String = engram_url() + "/api/nodes"
let url: String = target_url + "/api/nodes"
let response: String = http_post_json(url, body)
if str_eq(response, "") { return "" }
return json_get_string(response, "id")
}
// post_edge connect two nodes with a typed, weighted edge.
fn post_edge(from_id: String, to_id: String, relation: String, weight: String) -> String {
// target_url is the soul's own Engram URL.
fn post_edge(target_url: String, from_id: String, to_id: String, relation: String, weight: String) -> String {
let key: String = engram_key()
let body: String = build_edge_body(from_id, to_id, relation, weight, key)
let url: String = engram_url() + "/api/edges"
let url: String = target_url + "/api/edges"
let response: String = http_post_json(url, body)
return response
}
@@ -60,7 +69,7 @@ fn safe_weight(w: String, default_w: String) -> String {
// Installers
// install_value create a Value node and connect it to the root.
fn install_value(root_id: String, value_json: String, index: Int) -> String {
fn install_value(target_url: String, root_id: String, value_json: String, index: Int) -> String {
let value_text: String = json_get_string(value_json, "value")
let grounding: String = json_get_string(value_json, "grounding")
let weight_raw: String = json_get_string(value_json, "weight")
@@ -73,19 +82,19 @@ fn install_value(root_id: String, value_json: String, index: Int) -> String {
let content = content + " | grounding: " + grounding
}
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create value node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "has_value", weight)
post_edge(target_url, root_id, node_id, "has_value", weight)
println("[forge] value node: " + node_id + "" + value_text)
return node_id
}
// install_biography_node create a Biography node and connect it to the root.
fn install_biography_node(root_id: String, bio_json: String, index: Int) -> String {
fn install_biography_node(target_url: String, root_id: String, bio_json: String, index: Int) -> String {
let event_text: String = json_get_string(bio_json, "event")
let weight_raw: String = json_get_string(bio_json, "weight")
let weight: String = safe_weight(weight_raw, "0.7")
@@ -93,19 +102,19 @@ fn install_biography_node(root_id: String, bio_json: String, index: Int) -> Stri
if str_eq(event_text, "") { return "" }
let content: String = "BIOGRAPHY: " + event_text
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create biography node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "formed_by", weight)
post_edge(target_url, root_id, node_id, "formed_by", weight)
println("[forge] biography node: " + node_id + "" + event_text)
return node_id
}
// install_relationship create a Relationship node and connect to root.
fn install_relationship(root_id: String, rel_json: String, index: Int) -> String {
fn install_relationship(target_url: String, root_id: String, rel_json: String, index: Int) -> String {
let name: String = json_get_string(rel_json, "name")
let role: String = json_get_string(rel_json, "role")
let weight_raw: String = json_get_string(rel_json, "weight")
@@ -118,29 +127,29 @@ fn install_relationship(root_id: String, rel_json: String, index: Int) -> String
let content = content + " (" + role + ")"
}
let node_id: String = post_node(content, "Identity", weight)
let node_id: String = post_node(target_url, content, "Identity", weight)
if str_eq(node_id, "") {
println("[forge] warning: failed to create relationship node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "relates_to", weight)
post_edge(target_url, root_id, node_id, "relates_to", weight)
println("[forge] relationship node: " + node_id + "" + name)
return node_id
}
// install_reasoning_pattern create a ReasoningPattern node and connect to root.
fn install_reasoning_pattern(root_id: String, pattern: String, index: Int) -> String {
fn install_reasoning_pattern(target_url: String, root_id: String, pattern: String, index: Int) -> String {
if str_eq(pattern, "") { return "" }
let content: String = "REASONING: " + pattern
let node_id: String = post_node(content, "Identity", "0.7")
let node_id: String = post_node(target_url, content, "Identity", "0.7")
if str_eq(node_id, "") {
println("[forge] warning: failed to create reasoning node " + int_to_str(index))
return ""
}
post_edge(root_id, node_id, "reasons_with", "0.7")
post_edge(target_url, root_id, node_id, "reasons_with", "0.7")
println("[forge] reasoning node: " + node_id)
return node_id
}
@@ -172,15 +181,71 @@ fn install_main() -> String {
if str_eq(subject, "") { let subject = "unknown" }
println("[forge] subject: " + subject)
println("[forge] engram: " + engram_url())
// Resolve soul's Engram URL
// Each soul has their own Engram instance. Find it in registry.json via
// the engram_url field. If the soul isn't in the registry yet, fall back
// to the ENGRAM_URL env var (for bootstrapping new souls).
let target_url: String = ""
let reg_json: String = fs_read("registry.json")
if !str_eq(reg_json, "") {
let ri: Int = 0
while ri < 30 {
let reg_entry: String = json_get(reg_json, "imprints." + int_to_str(ri))
if str_eq(reg_entry, "") {
let ri = ri + 30 // break
} else {
let reg_subject: String = json_get_string(reg_entry, "subject")
if str_eq(reg_subject, subject) {
let reg_root: String = json_get_string(reg_entry, "engram_root_id")
if !str_eq(reg_root, "") {
println("[forge] already installed — root: " + reg_root)
println("[forge] skip. to reinstall: python3 reinstall_imprints.py --slug <slug>")
return reg_root
}
let reg_url: String = json_get_string(reg_entry, "engram_url")
if !str_eq(reg_url, "") {
let target_url = reg_url
}
let ri = ri + 30 // break after match
} else {
let ri = ri + 1
}
}
}
}
// Fall back to env var if not in registry
if str_eq(target_url, "") {
let env_url: String = env("ENGRAM_URL")
if !str_eq(env_url, "") {
let target_url = env_url
}
}
// Safety guard: refuse to install to the shared Neuron Engram
if str_eq(target_url, "") {
println("[forge] error: no engram_url found for '" + subject + "' in registry.json")
println("[forge] ensure registry.json has an engram_url entry, or run reinstall_imprints.py")
return ""
}
if str_contains(target_url, ":8742") {
println("[forge] error: refusing to install soul into Neuron's Engram (port 8742)")
println("[forge] start the DHARMA network first: ./launch_dharma.sh")
println("[forge] then retry. Each soul needs their own Engram instance.")
return ""
}
println("[forge] engram: " + target_url)
println("[forge] opening channel...")
// Create root imprint node this is the named traversal entry point
let root_content: String = "IMPRINT: " + subject + " | forge/" + FORGE_VERSION
let root_id: String = post_node(root_content, "Identity", "1.0")
let root_id: String = post_node(target_url, root_content, "Identity", "1.0")
if str_eq(root_id, "") {
println("[forge] error: failed to create root imprint node")
println("[forge] is engram running at " + engram_url() + " ?")
println("[forge] is the soul's Engram running at " + target_url + " ?")
println("[forge] start it with: ./launch_dharma.sh")
return ""
}
@@ -193,25 +258,25 @@ fn install_main() -> String {
if !str_eq(values_raw, "") {
println("[forge] installing values...")
let v0: String = json_get(seed_json, "values.0")
if !str_eq(v0, "") { install_value(root_id, v0, 0) }
if !str_eq(v0, "") { install_value(target_url, root_id, v0, 0) }
let v1: String = json_get(seed_json, "values.1")
if !str_eq(v1, "") { install_value(root_id, v1, 1) }
if !str_eq(v1, "") { install_value(target_url, root_id, v1, 1) }
let v2: String = json_get(seed_json, "values.2")
if !str_eq(v2, "") { install_value(root_id, v2, 2) }
if !str_eq(v2, "") { install_value(target_url, root_id, v2, 2) }
let v3: String = json_get(seed_json, "values.3")
if !str_eq(v3, "") { install_value(root_id, v3, 3) }
if !str_eq(v3, "") { install_value(target_url, root_id, v3, 3) }
let v4: String = json_get(seed_json, "values.4")
if !str_eq(v4, "") { install_value(root_id, v4, 4) }
if !str_eq(v4, "") { install_value(target_url, root_id, v4, 4) }
let v5: String = json_get(seed_json, "values.5")
if !str_eq(v5, "") { install_value(root_id, v5, 5) }
if !str_eq(v5, "") { install_value(target_url, root_id, v5, 5) }
let v6: String = json_get(seed_json, "values.6")
if !str_eq(v6, "") { install_value(root_id, v6, 6) }
if !str_eq(v6, "") { install_value(target_url, root_id, v6, 6) }
let v7: String = json_get(seed_json, "values.7")
if !str_eq(v7, "") { install_value(root_id, v7, 7) }
if !str_eq(v7, "") { install_value(target_url, root_id, v7, 7) }
let v8: String = json_get(seed_json, "values.8")
if !str_eq(v8, "") { install_value(root_id, v8, 8) }
if !str_eq(v8, "") { install_value(target_url, root_id, v8, 8) }
let v9: String = json_get(seed_json, "values.9")
if !str_eq(v9, "") { install_value(root_id, v9, 9) }
if !str_eq(v9, "") { install_value(target_url, root_id, v9, 9) }
}
// Install biography
@@ -219,21 +284,21 @@ fn install_main() -> String {
if !str_eq(bio_raw, "") {
println("[forge] installing biography nodes...")
let b0: String = json_get(seed_json, "biography.0")
if !str_eq(b0, "") { install_biography_node(root_id, b0, 0) }
if !str_eq(b0, "") { install_biography_node(target_url, root_id, b0, 0) }
let b1: String = json_get(seed_json, "biography.1")
if !str_eq(b1, "") { install_biography_node(root_id, b1, 1) }
if !str_eq(b1, "") { install_biography_node(target_url, root_id, b1, 1) }
let b2: String = json_get(seed_json, "biography.2")
if !str_eq(b2, "") { install_biography_node(root_id, b2, 2) }
if !str_eq(b2, "") { install_biography_node(target_url, root_id, b2, 2) }
let b3: String = json_get(seed_json, "biography.3")
if !str_eq(b3, "") { install_biography_node(root_id, b3, 3) }
if !str_eq(b3, "") { install_biography_node(target_url, root_id, b3, 3) }
let b4: String = json_get(seed_json, "biography.4")
if !str_eq(b4, "") { install_biography_node(root_id, b4, 4) }
if !str_eq(b4, "") { install_biography_node(target_url, root_id, b4, 4) }
let b5: String = json_get(seed_json, "biography.5")
if !str_eq(b5, "") { install_biography_node(root_id, b5, 5) }
if !str_eq(b5, "") { install_biography_node(target_url, root_id, b5, 5) }
let b6: String = json_get(seed_json, "biography.6")
if !str_eq(b6, "") { install_biography_node(root_id, b6, 6) }
if !str_eq(b6, "") { install_biography_node(target_url, root_id, b6, 6) }
let b7: String = json_get(seed_json, "biography.7")
if !str_eq(b7, "") { install_biography_node(root_id, b7, 7) }
if !str_eq(b7, "") { install_biography_node(target_url, root_id, b7, 7) }
}
// Install relationships
@@ -241,17 +306,17 @@ fn install_main() -> String {
if !str_eq(rel_raw, "") {
println("[forge] installing relationship nodes...")
let r0: String = json_get(seed_json, "relationships.0")
if !str_eq(r0, "") { install_relationship(root_id, r0, 0) }
if !str_eq(r0, "") { install_relationship(target_url, root_id, r0, 0) }
let r1: String = json_get(seed_json, "relationships.1")
if !str_eq(r1, "") { install_relationship(root_id, r1, 1) }
if !str_eq(r1, "") { install_relationship(target_url, root_id, r1, 1) }
let r2: String = json_get(seed_json, "relationships.2")
if !str_eq(r2, "") { install_relationship(root_id, r2, 2) }
if !str_eq(r2, "") { install_relationship(target_url, root_id, r2, 2) }
let r3: String = json_get(seed_json, "relationships.3")
if !str_eq(r3, "") { install_relationship(root_id, r3, 3) }
if !str_eq(r3, "") { install_relationship(target_url, root_id, r3, 3) }
let r4: String = json_get(seed_json, "relationships.4")
if !str_eq(r4, "") { install_relationship(root_id, r4, 4) }
if !str_eq(r4, "") { install_relationship(target_url, root_id, r4, 4) }
let r5: String = json_get(seed_json, "relationships.5")
if !str_eq(r5, "") { install_relationship(root_id, r5, 5) }
if !str_eq(r5, "") { install_relationship(target_url, root_id, r5, 5) }
}
// Install reasoning patterns
@@ -259,15 +324,15 @@ fn install_main() -> String {
if !str_eq(reasoning_raw, "") {
println("[forge] installing reasoning patterns...")
let rp0: String = json_get(seed_json, "reasoning_patterns.0")
if !str_eq(rp0, "") { install_reasoning_pattern(root_id, rp0, 0) }
if !str_eq(rp0, "") { install_reasoning_pattern(target_url, root_id, rp0, 0) }
let rp1: String = json_get(seed_json, "reasoning_patterns.1")
if !str_eq(rp1, "") { install_reasoning_pattern(root_id, rp1, 1) }
if !str_eq(rp1, "") { install_reasoning_pattern(target_url, root_id, rp1, 1) }
let rp2: String = json_get(seed_json, "reasoning_patterns.2")
if !str_eq(rp2, "") { install_reasoning_pattern(root_id, rp2, 2) }
if !str_eq(rp2, "") { install_reasoning_pattern(target_url, root_id, rp2, 2) }
let rp3: String = json_get(seed_json, "reasoning_patterns.3")
if !str_eq(rp3, "") { install_reasoning_pattern(root_id, rp3, 3) }
if !str_eq(rp3, "") { install_reasoning_pattern(target_url, root_id, rp3, 3) }
let rp4: String = json_get(seed_json, "reasoning_patterns.4")
if !str_eq(rp4, "") { install_reasoning_pattern(root_id, rp4, 4) }
if !str_eq(rp4, "") { install_reasoning_pattern(target_url, root_id, rp4, 4) }
}
// Done
@@ -275,7 +340,44 @@ fn install_main() -> String {
println("[forge] channel open.")
println("[forge] root imprint ID: " + root_id)
println("[forge] subject: " + subject)
println("[forge] traverse from: " + engram_url() + "/api/neighbors/" + root_id)
println("[forge] engram: " + target_url)
println("[forge] traverse from: " + target_url + "/api/neighbors/" + root_id)
// Registry write
// Append this imprint to registry.json so summon can find it by name.
// Includes engram_url and engram_db_path for per-soul addressing.
let slug_raw: String = str_to_lower(subject)
let slug: String = str_replace(slug_raw, " ", "-")
let new_entry: String = "{\"subject\":\"" + str_escape_json(subject) +
"\",\"slug\":\"" + slug +
"\",\"seed_file\":\"" + str_escape_json(seed_file) +
"\",\"engram_db_path\":\"imprints/" + slug +
"\",\"engram_url\":\"" + target_url +
"\",\"engram_root_id\":\"" + root_id +
"\",\"installed\":true,\"installed_at\":\"2026-05-03\"}"
// Read existing registry or create fresh
let existing_reg: String = fs_read("registry.json")
if str_eq(existing_reg, "") {
let fresh_reg: String = "{\"version\":\"1.0\",\"imprints\":[" + new_entry + "]}"
fs_write("registry.json", fresh_reg)
println("[forge] registry: created with " + subject)
} else {
// Append to imprints array find last ] and insert before it
let last_bracket: Int = str_last_index_of(existing_reg, "]")
if last_bracket > 0 {
let before: String = str_slice(existing_reg, 0, last_bracket)
let after: String = str_slice(existing_reg, last_bracket, str_len(existing_reg))
// Check if array is empty (only "[")
let trimmed: String = str_trim(before)
let last_char: String = str_slice(trimmed, str_len(trimmed) - 1, str_len(trimmed))
let separator: String = ","
if str_eq(last_char, "[") { let separator = "" }
let updated_reg: String = before + separator + new_entry + after
fs_write("registry.json", updated_reg)
println("[forge] registry: added " + subject)
}
}
return root_id
}
+248
View File
@@ -0,0 +1,248 @@
// summon.el Forge channel activation stage.
//
// Stage 4 of the Forge pipeline. Looks up an installed imprint in registry.json
// by subject name, retrieves the soul's own engram_url and engram_root_id, then
// opens a live conversation channel with the soul server.
//
// Each soul has their own Engram instance registry.json is the authoritative
// source for engram_url (e.g. http://localhost:8806 for Feynman). The shared
// Neuron Engram at localhost:8742 is never used here.
//
// Usage: forge summon "<Subject Name>"
//
// The imprint must already be installed (forge install or reinstall_imprints.py).
//
// Depends on: schema.el (engram_key, FORGE_VERSION)
//
// Environment:
// SOUL_URL soul server (default: http://localhost:7770)
// SOUL_TOKEN soul auth token (default: ntn-user-2026)
// Helpers
fn soul_url() -> String {
let u: String = env("SOUL_URL")
if str_eq(u, "") { return "http://localhost:7770" }
return u
}
fn soul_token() -> String {
let t: String = env("SOUL_TOKEN")
if str_eq(t, "") { return "ntn-user-2026" }
return t
}
// RegistryEntry data for a single soul from registry.json.
// All fields come from the per-soul record; engram_url is the soul's own
// Engram instance (e.g. http://localhost:8806), NOT the shared Neuron Engram.
// find_registry_entry look up a soul in registry.json by subject name.
// Returns the raw JSON object string for that entry, or "" if not found.
fn find_registry_entry(subject: String) -> String {
let reg_json: String = fs_read("registry.json")
if str_eq(reg_json, "") { return "" }
let i: Int = 0
while i < 30 {
let entry: String = json_get(reg_json, "imprints." + int_to_str(i))
if str_eq(entry, "") { return "" }
let reg_subject: String = json_get_string(entry, "subject")
if str_eq(reg_subject, subject) {
return entry
}
let i = i + 1
}
return ""
}
// soul_engram_url return the soul's own Engram URL from registry.json.
// Falls back to the global ENGRAM_URL env var, then the default shared URL.
// Prefer registry.json each soul has their own Engram on a dedicated port.
fn soul_engram_url(entry: String) -> String {
if !str_eq(entry, "") {
let url: String = json_get_string(entry, "engram_url")
if !str_eq(url, "") { return url }
}
// Environment override (for testing / unusual setups)
let env_url: String = env("ENGRAM_URL")
if !str_eq(env_url, "") { return env_url }
return "http://localhost:8742"
}
// find_imprint_root locate the root node ID for an imprint.
// Strategy: registry.json is authoritative (has engram_root_id per soul).
// No Engram graph search needed registry is the source of truth.
fn find_imprint_root(subject: String) -> String {
let entry: String = find_registry_entry(subject)
if str_eq(entry, "") { return "" }
let reg_root: String = json_get_string(entry, "engram_root_id")
return reg_root
}
// open_soul_channel POST to soul /api/chat to create a channel session.
// Passes the soul's own engram_url so the soul server connects to the right
// Engram instance (not the shared Neuron Engram at 8742).
fn open_soul_channel(subject: String, root_id: String, soul_engram: String) -> String {
let url: String = soul_url() + "/api/chat"
let token: String = soul_token()
let body: String = "{\"message\":\"[summon:" + str_escape_json(subject) + "]\"," +
"\"engram_root_id\":\"" + root_id + "\"," +
"\"engram_url\":\"" + soul_engram + "\"," +
"\"_auth\":\"" + token + "\"}"
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
let response: String = http_post_with_headers(url, body, headers)
if str_eq(response, "") { return "" }
return response
}
// summon_one look up and activate a single imprint.
// Returns "<root_id>|<engram_url>" on success (pipe-delimited), "" on failure.
fn summon_one(subject: String) -> String {
println("[forge] summoning: " + subject)
let entry: String = find_registry_entry(subject)
if str_eq(entry, "") {
println("[forge] ERROR: imprint not found — run: forge install <seed-file>")
return ""
}
let root_id: String = json_get_string(entry, "engram_root_id")
if str_eq(root_id, "") {
println("[forge] ERROR: no engram_root_id in registry — run reinstall_imprints.py")
return ""
}
let e_url: String = soul_engram_url(entry)
println("[forge] root: " + root_id)
println("[forge] engram: " + e_url)
return root_id + "|" + e_url
}
// build_roots_array build a JSON array string from up to 8 root IDs.
fn build_roots_array(ids: [String]) -> String {
let result: String = "["
let i: Int = 0
let added: Int = 0
while i < len(ids) {
let id: String = get(ids, i)
if !str_eq(id, "") {
if added > 0 {
let result = result + ","
}
let result = result + "\"" + id + "\""
let added = added + 1
}
let i = i + 1
}
return result + "]"
}
// Main
fn summon_main() -> String {
let argv: [String] = args()
// Collect all subject names (argv[1..])
if len(argv) < 2 {
println("[forge] usage: forge summon \"<Subject>\" [\"<Subject2>\" ...]")
println("[forge] example: forge summon \"Bobby Anderson\"")
println("[forge] example: forge summon \"Alan Turing\" \"Nikola Tesla\" \"Albert Einstein\"")
return ""
}
println("[forge] soul: " + soul_url())
println("[forge] each soul has their own Engram (DHARMA network)")
println("")
// Resolve all subjects to root IDs and per-soul engram URLs.
// summon_one() returns "<root_id>|<engram_url>" or "" on failure.
let root_ids: [String] = []
let engram_urls: [String] = []
let subjects: [String] = []
let i: Int = 1
while i < len(argv) {
let subj: String = get(argv, i)
let result: String = summon_one(subj)
if !str_eq(result, "") {
// Split "<root_id>|<engram_url>" on the pipe character
let pipe_idx: Int = str_index_of(result, "|")
if pipe_idx > 0 {
let rid: String = str_slice(result, 0, pipe_idx)
let eurl: String = str_slice(result, pipe_idx + 1, str_len(result))
let root_ids = append(root_ids, rid)
let engram_urls = append(engram_urls, eurl)
let subjects = append(subjects, subj)
}
}
let i = i + 1
}
if len(root_ids) == 0 {
println("[forge] no imprints found. Run: forge inspect")
return ""
}
// Build roots array for multi-imprint channel
let roots_json: String = build_roots_array(root_ids)
let subject_list: String = ""
let j: Int = 0
while j < len(subjects) {
if j > 0 { let subject_list = subject_list + ", " }
let subject_list = subject_list + get(subjects, j)
let j = j + 1
}
println("")
println("[forge] opening channel...")
// For single-soul summon: pass engram_url directly.
// For multi-soul: pass engram_urls as a JSON array so soul server can
// address each soul's Engram independently.
let url: String = soul_url() + "/api/chat"
let token: String = soul_token()
let body: String = ""
if len(root_ids) == 1 {
let single_engram: String = get(engram_urls, 0)
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
"\"engram_root_ids\":" + roots_json + "," +
"\"engram_url\":\"" + single_engram + "\"," +
"\"_auth\":\"" + token + "\"}"
} else {
// Build engram_urls JSON array for multi-soul summon
let eurls_json: String = build_roots_array(engram_urls)
let body = "{\"message\":\"[summon:" + str_escape_json(subject_list) + "]\"," +
"\"engram_root_ids\":" + roots_json + "," +
"\"engram_urls\":" + eurls_json + "," +
"\"_auth\":\"" + token + "\"}"
}
let headers: Map<String, String> = {"Content-Type": "application/json", "Authorization": "Bearer " + token}
let response: String = http_post_with_headers(url, body, headers)
println("")
if len(root_ids) == 1 {
println("[forge] " + get(subjects, 0) + " is present.")
println("[forge] engram: " + get(engram_urls, 0))
} else {
println("[forge] " + int_to_str(len(root_ids)) + " imprints present: " + subject_list)
}
println("[forge] engram roots: " + roots_json)
if str_eq(response, "") {
println("[forge] (soul offline — imprints are in Engram, channel ready when soul starts)")
return roots_json
}
let conv_id: String = json_get_string(response, "conversation_id")
if str_eq(conv_id, "") {
let conv_id = json_get_string(response, "channel_id")
}
if !str_eq(conv_id, "") {
println("[forge] conversation id: " + conv_id)
println("")
println("[forge] send a message:")
println(" curl -s " + soul_url() + "/api/chat \\")
println(" -H 'Content-Type: application/json' \\")
println(" -d '{\"conversation_id\":\"" + conv_id + "\",\"message\":\"Hello\"}'")
}
println("")
return conv_id
}
Executable
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# DHARMA network — stop all running soul Engrams.
# Reads PIDs from /tmp/dharma-pids written by launch_dharma.sh.
# Falls back to killing by port if PID file is missing.
#
# Usage:
# ./stop_dharma.sh stop all soul Engrams
# ./stop_dharma.sh <slug> stop a single soul by slug
set -euo pipefail
PID_FILE=/tmp/dharma-pids
FILTER="${1:-}"
stopped=0
missed=0
# Kill via PID file if it exists
if [ -f "$PID_FILE" ]; then
while IFS=' ' read -r pid slug port; do
[ -z "$pid" ] && continue
if [ -n "$FILTER" ] && [ "$slug" != "$FILTER" ]; then
continue
fi
if kill -0 "$pid" 2>/dev/null; then
kill "$pid"
echo "[dharma] stopped $slug (pid=$pid port=$port)"
stopped=$((stopped + 1))
else
echo "[dharma] $slug (pid=$pid) already gone"
fi
done < "$PID_FILE"
if [ -z "$FILTER" ]; then
rm -f "$PID_FILE"
fi
else
echo "[dharma] no PID file at $PID_FILE — killing by port scan"
# Fall back: kill anything on ports 88018819
for port in $(seq 8801 8819); do
pids=$(lsof -ti tcp:"$port" 2>/dev/null || true)
if [ -n "$pids" ]; then
echo "[dharma] killing port $port (pids: $pids)"
echo "$pids" | xargs kill 2>/dev/null || true
stopped=$((stopped + 1))
fi
done
fi
echo ""
echo "[dharma] stopped=$stopped missed=$missed"