Add Ollama provider, portable memory, cultivation digest, refugee importer, GLM-OCR spike
- P0: unified soul binary with engram_node_full fix, read-back-verify, search fix - P0: move API keys from plaintext plists to macOS Keychain - P0: fix MCP backend URL (port 8742 → 7770) - P1.6: memory-export/import scripts (AES-256-CBC, versioned .neuronmem format) - P1.7: nightly cultivation digest with sharpness metric (launchd at 23:55) - P2.10: Ollama provider in agentic loop (SOUL_LLM_PROVIDER=ollama) - P3.12: refugee importer for ChatGPT/Screenpipe/generic formats - P3.13: GLM-OCR spike — SHIP IT (mlx-vlm, 1.59GB, photo-to-memory.sh)
This commit is contained in:
Executable
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env bash
|
||||
# memory-export.sh — Export Neuron engram store as a portable encrypted .neuronmem bundle
|
||||
#
|
||||
# Usage:
|
||||
# ./tools/memory-export.sh [output-path] [--passphrase "your passphrase"]
|
||||
#
|
||||
# If no passphrase is given, a random one is generated and printed — write it down.
|
||||
# If no output path is given, defaults to ./neuron-export-<timestamp>.neuronmem
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||
ENGRAM_SNAPSHOT="${HOME}/.neuron/engram/snapshot.json"
|
||||
SOUL_VERSION="1.1.0"
|
||||
FORMAT_VERSION="1"
|
||||
|
||||
# ── Parse args ─────────────────────────────────────────────────────────────────
|
||||
OUTPUT_PATH=""
|
||||
PASSPHRASE=""
|
||||
PASSPHRASE_SET=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--passphrase)
|
||||
PASSPHRASE="$2"
|
||||
PASSPHRASE_SET=1
|
||||
shift 2
|
||||
;;
|
||||
--passphrase=*)
|
||||
PASSPHRASE="${1#*=}"
|
||||
PASSPHRASE_SET=1
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1" >&2
|
||||
echo "Usage: $0 [output-path] [--passphrase \"...\"]" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$OUTPUT_PATH" ]]; then
|
||||
OUTPUT_PATH="$1"
|
||||
else
|
||||
echo "Unexpected argument: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Default output path ────────────────────────────────────────────────────────
|
||||
TIMESTAMP="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||
if [[ -z "$OUTPUT_PATH" ]]; then
|
||||
OUTPUT_PATH="./neuron-export-${TIMESTAMP}.neuronmem"
|
||||
fi
|
||||
|
||||
# Ensure .neuronmem extension
|
||||
if [[ "${OUTPUT_PATH}" != *.neuronmem ]]; then
|
||||
OUTPUT_PATH="${OUTPUT_PATH%.neuronmem}.neuronmem"
|
||||
fi
|
||||
|
||||
# ── Validate source ────────────────────────────────────────────────────────────
|
||||
if [[ ! -f "$ENGRAM_SNAPSHOT" ]]; then
|
||||
echo "ERROR: Engram snapshot not found at: $ENGRAM_SNAPSHOT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Neuron Memory Export"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Source: $ENGRAM_SNAPSHOT"
|
||||
echo "Output: $OUTPUT_PATH"
|
||||
echo ""
|
||||
|
||||
# ── Generate passphrase if not provided ────────────────────────────────────────
|
||||
if [[ $PASSPHRASE_SET -eq 0 ]]; then
|
||||
PASSPHRASE="$(openssl rand -base64 32)"
|
||||
echo "⚠ No passphrase provided. Generated passphrase:"
|
||||
echo ""
|
||||
echo " ${PASSPHRASE}"
|
||||
echo ""
|
||||
echo "⚠ WRITE THIS DOWN. You will need it to import this file."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Count nodes and edges ──────────────────────────────────────────────────────
|
||||
echo "Analyzing snapshot..."
|
||||
NODE_COUNT="$(python3 -c "
|
||||
import json, sys
|
||||
with open('${ENGRAM_SNAPSHOT}') as f:
|
||||
d = json.load(f)
|
||||
nodes = d.get('nodes', d if isinstance(d, list) else [])
|
||||
edges = d.get('edges', [])
|
||||
print(len(nodes) if isinstance(nodes, list) else len(nodes))
|
||||
" 2>/dev/null || echo "unknown")"
|
||||
|
||||
echo " Nodes: ${NODE_COUNT}"
|
||||
|
||||
# ── Compute checksum of source file ───────────────────────────────────────────
|
||||
echo "Computing checksum..."
|
||||
CHECKSUM="$(openssl dgst -sha256 "$ENGRAM_SNAPSHOT" | awk '{print $NF}')"
|
||||
echo " SHA256: ${CHECKSUM:0:16}..."
|
||||
|
||||
# ── Build bundle in temp dir ───────────────────────────────────────────────────
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
BUNDLE_DIR="${WORK_DIR}/neuronmem-v${FORMAT_VERSION}"
|
||||
mkdir -p "$BUNDLE_DIR"
|
||||
|
||||
echo "Building bundle..."
|
||||
|
||||
# Copy snapshot as nodes.json
|
||||
cp "$ENGRAM_SNAPSHOT" "${BUNDLE_DIR}/nodes.json"
|
||||
|
||||
# Write metadata.json
|
||||
ISO_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
cat > "${BUNDLE_DIR}/metadata.json" << METAEOF
|
||||
{
|
||||
"version": "${FORMAT_VERSION}",
|
||||
"exported_at": "${ISO_TIMESTAMP}",
|
||||
"node_count": ${NODE_COUNT},
|
||||
"soul_version": "${SOUL_VERSION}",
|
||||
"sha256": "${CHECKSUM}",
|
||||
"format": "neuronmem-v1",
|
||||
"encryption": "aes-256-cbc-pbkdf2",
|
||||
"source_host": "$(hostname -s 2>/dev/null || echo unknown)"
|
||||
}
|
||||
METAEOF
|
||||
|
||||
echo " metadata.json written"
|
||||
echo " nodes.json copied ($(du -sh "${BUNDLE_DIR}/nodes.json" | cut -f1))"
|
||||
|
||||
# ── Create tar.gz ──────────────────────────────────────────────────────────────
|
||||
TAR_PATH="${WORK_DIR}/bundle.tar.gz"
|
||||
echo "Compressing..."
|
||||
(cd "$WORK_DIR" && tar czf "$TAR_PATH" "neuronmem-v${FORMAT_VERSION}/")
|
||||
COMPRESSED_SIZE="$(du -sh "$TAR_PATH" | cut -f1)"
|
||||
echo " Compressed size: ${COMPRESSED_SIZE}"
|
||||
|
||||
# ── Encrypt ────────────────────────────────────────────────────────────────────
|
||||
echo "Encrypting (AES-256-CBC, PBKDF2, 600k iterations)..."
|
||||
openssl enc -aes-256-cbc \
|
||||
-pbkdf2 \
|
||||
-iter 600000 \
|
||||
-salt \
|
||||
-in "$TAR_PATH" \
|
||||
-out "$OUTPUT_PATH" \
|
||||
-pass "pass:${PASSPHRASE}"
|
||||
|
||||
# ── Cleanup ────────────────────────────────────────────────────────────────────
|
||||
rm -rf "$WORK_DIR"
|
||||
|
||||
# ── Report ─────────────────────────────────────────────────────────────────────
|
||||
FINAL_SIZE="$(du -sh "$OUTPUT_PATH" | cut -f1)"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Export complete."
|
||||
echo " File: $OUTPUT_PATH"
|
||||
echo " Size: ${FINAL_SIZE}"
|
||||
echo " Nodes: ${NODE_COUNT}"
|
||||
echo " Checksum: ${CHECKSUM:0:32}..."
|
||||
echo " Timestamp: ${ISO_TIMESTAMP}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user