harden(neuron-dev-setup): fix 7 fresh-Mac onboarding installer bugs
- Guard the Anthropic key prompt with `[ -t 0 ]` so headless/CI runs fail fast instead of hanging forever on `read -s` with no TTY. - Hit engram's real health endpoint `/health` (was `/api/health`) in the Phase 6 wait, Phase 8 verify, and the printed hints. - Register the neuron MCP server by jq-merging into ~/.claude.json (mcpServers) — the file Claude Code actually reads — not ~/.claude/mcp.json. - Merge neuron hooks into an existing ~/.claude/settings.json via jq (preserving the user's settings, dropping doc-only keys) instead of only leaving a settings.core.json to merge by hand. - Drop the nonexistent foundation.git clone and the forge build that depended on it; Phase 6 genesis seeding already degrades to a warning. - Add a non-GCP fallback: without gcloud (or a staged El toolchain) the installer skips the native builds with clear guidance instead of dying, so a fresh Mac still completes. - Template the soul studio_dir default off $HOME instead of a hardcoded /Users/will path (soul.el + committed dist artifacts). Add jq to the preflight tool list (required by the two config merges).
This commit is contained in:
+1
-1
@@ -519,7 +519,7 @@ int main(int _argc, char** _argv) {
|
|||||||
axon_raw = env(EL_STR("NEURON_API_URL"));
|
axon_raw = env(EL_STR("NEURON_API_URL"));
|
||||||
axon_base = ({ el_val_t _if_result_47 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_47 = (EL_STR("http://localhost:7771")); } else { _if_result_47 = (axon_raw); } _if_result_47; });
|
axon_base = ({ el_val_t _if_result_47 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_47 = (EL_STR("http://localhost:7771")); } else { _if_result_47 = (axon_raw); } _if_result_47; });
|
||||||
studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR"));
|
studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR"));
|
||||||
studio_dir = ({ el_val_t _if_result_48 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_48 = (EL_STR("/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon")); } else { _if_result_48 = (studio_dir_raw); } _if_result_48; });
|
studio_dir = ({ el_val_t _if_result_48 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_48 = (el_str_concat(env(EL_STR("HOME")), EL_STR("/Development/neuron-technologies/products/cgi-studio/el-daemon"))); } else { _if_result_48 = (studio_dir_raw); } _if_result_48; });
|
||||||
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] boot - cgi="), soul_cgi_id), EL_STR(" port=")), int_to_str(port)));
|
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] boot - cgi="), soul_cgi_id), EL_STR(" port=")), int_to_str(port)));
|
||||||
using_http_engram = !str_eq(engram_url_raw, EL_STR(""));
|
using_http_engram = !str_eq(engram_url_raw, EL_STR(""));
|
||||||
engram_load(snapshot);
|
engram_load(snapshot);
|
||||||
|
|||||||
+1
-1
@@ -21304,7 +21304,7 @@ println("[memory] consolidate stats=" + stats)
|
|||||||
let soul_axon_base_raw: String = env("NEURON_API_URL")
|
let soul_axon_base_raw: String = env("NEURON_API_URL")
|
||||||
let soul_axon_base: String = if str_eq(soul_axon_base_raw, "") { "http://localhost:7771" } else { soul_axon_base_raw }
|
let soul_axon_base: String = if str_eq(soul_axon_base_raw, "") { "http://localhost:7771" } else { soul_axon_base_raw }
|
||||||
let soul_token: String = env("NEURON_TOKEN")
|
let soul_token: String = env("NEURON_TOKEN")
|
||||||
let soul_studio_ui_dir: String = "/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon"
|
let soul_studio_ui_dir: String = env("HOME") + "/Development/neuron-technologies/products/cgi-studio/el-daemon"
|
||||||
|
|
||||||
// ── Runtime bridge helpers ────────────────────────────────────────────────────
|
// ── Runtime bridge helpers ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30704,7 +30704,7 @@ int main(int _argc, char** _argv) {
|
|||||||
axon_raw = env(EL_STR("NEURON_API_URL"));
|
axon_raw = env(EL_STR("NEURON_API_URL"));
|
||||||
axon_base = ({ el_val_t _if_result_700 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_700 = (EL_STR("http://localhost:7771")); } else { _if_result_700 = (axon_raw); } _if_result_700; });
|
axon_base = ({ el_val_t _if_result_700 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_700 = (EL_STR("http://localhost:7771")); } else { _if_result_700 = (axon_raw); } _if_result_700; });
|
||||||
studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR"));
|
studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR"));
|
||||||
studio_dir = ({ el_val_t _if_result_701 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_701 = (EL_STR("/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon")); } else { _if_result_701 = (studio_dir_raw); } _if_result_701; });
|
studio_dir = ({ el_val_t _if_result_701 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_701 = (el_str_concat(env(EL_STR("HOME")), EL_STR("/Development/neuron-technologies/products/cgi-studio/el-daemon"))); } else { _if_result_701 = (studio_dir_raw); } _if_result_701; });
|
||||||
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] boot - cgi="), soul_cgi_id), EL_STR(" port=")), int_to_str(port)));
|
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] boot - cgi="), soul_cgi_id), EL_STR(" port=")), int_to_str(port)));
|
||||||
using_http_engram = !str_eq(engram_url_raw, EL_STR(""));
|
using_http_engram = !str_eq(engram_url_raw, EL_STR(""));
|
||||||
engram_load(snapshot);
|
engram_load(snapshot);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ cp config.env.example config.env # optional — edit ports/paths if you lik
|
|||||||
Then verify:
|
Then verify:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:8742/api/health # engram
|
curl http://localhost:8742/health # engram
|
||||||
curl http://localhost:7770/health # soul
|
curl http://localhost:7770/health # soul
|
||||||
curl http://localhost:7779/health # mcp-proxy (what Claude Code uses)
|
curl http://localhost:7779/health # mcp-proxy (what Claude Code uses)
|
||||||
launchctl list | grep ai.neuron
|
launchctl list | grep ai.neuron
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ DEV_ROOT="${HOME}/Development/neuron-technologies" # where source repos are cl
|
|||||||
GITEA_BASE="git@git.neuralplatform.ai:neuron-technologies"
|
GITEA_BASE="git@git.neuralplatform.ai:neuron-technologies"
|
||||||
NEURON_REPO_URL="${GITEA_BASE}/neuron.git" # soul + mcp-wrapper + mcp-proxy source
|
NEURON_REPO_URL="${GITEA_BASE}/neuron.git" # soul + mcp-wrapper + mcp-proxy source
|
||||||
ENGRAM_REPO_URL="${GITEA_BASE}/engram.git" # engram memory substrate
|
ENGRAM_REPO_URL="${GITEA_BASE}/engram.git" # engram memory substrate
|
||||||
FOUNDATION_REPO_URL="${GITEA_BASE}/foundation.git" # El SDK + forge (seed installer)
|
# NOTE: there is no foundation.git repo. The El toolchain is fetched via
|
||||||
|
# EL_TOOLCHAIN_SOURCE below; the forge seed installer is optional (Phase 6).
|
||||||
NEURON_REPO_BRANCH="main"
|
NEURON_REPO_BRANCH="main"
|
||||||
|
|
||||||
# ── Ports (must match across services; change only if a port clashes) ─────────
|
# ── Ports (must match across services; change only if a port clashes) ─────────
|
||||||
@@ -32,7 +33,9 @@ ENGRAM_API_KEY="ntn-dev-local"
|
|||||||
# ── El toolchain source (needed to build engram / mcp-wrapper / mcp-proxy) ────
|
# ── El toolchain source (needed to build engram / mcp-wrapper / mcp-proxy) ────
|
||||||
# Option A (default): fetch prebuilt El runtime + elc from GCP Artifact Registry
|
# Option A (default): fetch prebuilt El runtime + elc from GCP Artifact Registry
|
||||||
# (requires `gcloud auth` with access to project neuron-785695 — ask Will).
|
# (requires `gcloud auth` with access to project neuron-785695 — ask Will).
|
||||||
# Option B: build El from the foundation/el checkout locally.
|
# Without gcloud the installer skips the El-dependent builds and still completes.
|
||||||
|
# Option B: use a prebuilt El toolchain (elc + el_runtime.{c,h}) you have already
|
||||||
|
# staged in ${DEV_ROOT}/.el-runtime.
|
||||||
EL_TOOLCHAIN_SOURCE="artifact-registry" # artifact-registry | local
|
EL_TOOLCHAIN_SOURCE="artifact-registry" # artifact-registry | local
|
||||||
GCP_PROJECT="neuron-785695"
|
GCP_PROJECT="neuron-785695"
|
||||||
GCP_AR_REPO="foundation-prod"
|
GCP_AR_REPO="foundation-prod"
|
||||||
|
|||||||
+70
-24
@@ -147,7 +147,7 @@ step "Phase 1 — preflight checks"
|
|||||||
|
|
||||||
need() { command -v "$1" >/dev/null 2>&1 || MISSING+=" $1"; }
|
need() { command -v "$1" >/dev/null 2>&1 || MISSING+=" $1"; }
|
||||||
MISSING=""
|
MISSING=""
|
||||||
need git; need cc; need curl; need python3; need security; need launchctl
|
need git; need cc; need curl; need python3; need security; need launchctl; need jq
|
||||||
if [ -n "$MISSING" ]; then
|
if [ -n "$MISSING" ]; then
|
||||||
warn "Missing tools:${MISSING}"
|
warn "Missing tools:${MISSING}"
|
||||||
if command -v brew >/dev/null 2>&1; then
|
if command -v brew >/dev/null 2>&1; then
|
||||||
@@ -175,13 +175,16 @@ elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|||||||
else
|
else
|
||||||
if [ "$DRY_RUN" = 1 ]; then
|
if [ "$DRY_RUN" = 1 ]; then
|
||||||
echo " [dry-run] would prompt for Anthropic API key and store in Keychain"
|
echo " [dry-run] would prompt for Anthropic API key and store in Keychain"
|
||||||
else
|
elif [ -t 0 ]; then
|
||||||
echo " Enter your Anthropic API key (input hidden). Get one at https://console.anthropic.com/"
|
echo " Enter your Anthropic API key (input hidden). Get one at https://console.anthropic.com/"
|
||||||
read -r -s -p " ANTHROPIC_API_KEY: " _key; echo
|
read -r -s -p " ANTHROPIC_API_KEY: " _key; echo
|
||||||
[ -n "$_key" ] || die "No key entered. Re-run when you have one."
|
[ -n "$_key" ] || die "No key entered. Re-run when you have one."
|
||||||
security add-generic-password -a "$USER" -s "$KEYCHAIN_SERVICE" -w "$_key" -U
|
security add-generic-password -a "$USER" -s "$KEYCHAIN_SERVICE" -w "$_key" -U
|
||||||
unset _key
|
unset _key
|
||||||
ok "stored key in Keychain (service '${KEYCHAIN_SERVICE}')"
|
ok "stored key in Keychain (service '${KEYCHAIN_SERVICE}')"
|
||||||
|
else
|
||||||
|
# Headless / CI / piped stdin: never block on `read -s` (it would hang forever).
|
||||||
|
die "No Anthropic API key and stdin is not a TTY (headless/CI). Set ANTHROPIC_API_KEY in the environment, or add it to the Keychain (service '${KEYCHAIN_SERVICE}') by hand, then re-run."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -207,14 +210,15 @@ elif [ "$USE_LOCAL_BINARIES" = 1 ]; then
|
|||||||
else
|
else
|
||||||
clone_or_pull "${NEURON_REPO_URL}" "$NEURON_REPO" "$NEURON_REPO_BRANCH"
|
clone_or_pull "${NEURON_REPO_URL}" "$NEURON_REPO" "$NEURON_REPO_BRANCH"
|
||||||
clone_or_pull "${ENGRAM_REPO_URL}" "$ENGRAM_REPO" "main"
|
clone_or_pull "${ENGRAM_REPO_URL}" "$ENGRAM_REPO" "main"
|
||||||
clone_or_pull "${FOUNDATION_REPO_URL}" "$FOUNDATION_REPO" "main"
|
# NOTE: no foundation.git — that repo does not exist. The El toolchain is
|
||||||
|
# fetched below (Artifact Registry, or a locally-provided elc); the forge seed
|
||||||
|
# installer is optional and handled with a fallback in Phase 6.
|
||||||
|
|
||||||
# ── El toolchain (needed to transpile .el -> .c for engram/wrapper/proxy) ──
|
# ── El toolchain (needed to transpile .el -> .c for engram/wrapper/proxy) ──
|
||||||
# soul does NOT need this: dist/soul.c is committed and compiled directly.
|
# soul does NOT need this: dist/soul.c is committed and compiled directly.
|
||||||
EL_RUNTIME_DIR="${DEV_ROOT}/.el-runtime"
|
EL_RUNTIME_DIR="${DEV_ROOT}/.el-runtime"
|
||||||
run "mkdir -p \"$EL_RUNTIME_DIR\""
|
run "mkdir -p \"$EL_RUNTIME_DIR\""
|
||||||
if [ "$EL_TOOLCHAIN_SOURCE" = "artifact-registry" ]; then
|
if [ "$EL_TOOLCHAIN_SOURCE" = "artifact-registry" ] && command -v gcloud >/dev/null 2>&1; then
|
||||||
command -v gcloud >/dev/null 2>&1 || die "gcloud required for EL_TOOLCHAIN_SOURCE=artifact-registry (or set it to 'local'). Ask Will for GCP access to project ${GCP_PROJECT}."
|
|
||||||
# Mirrors .gitea/workflows/ci.yaml: pull el-runtime-c, el-runtime-h, el-elc.
|
# Mirrors .gitea/workflows/ci.yaml: pull el-runtime-c, el-runtime-h, el-elc.
|
||||||
for pkg in el-runtime-c el-runtime-h el-elc; do
|
for pkg in el-runtime-c el-runtime-h el-elc; do
|
||||||
step "fetching $pkg from Artifact Registry"
|
step "fetching $pkg from Artifact Registry"
|
||||||
@@ -224,19 +228,29 @@ else
|
|||||||
run "mv \"$EL_RUNTIME_DIR\"/el_runtime.h* \"$EL_RUNTIME_DIR/el_runtime.h\" 2>/dev/null || true"
|
run "mv \"$EL_RUNTIME_DIR\"/el_runtime.h* \"$EL_RUNTIME_DIR/el_runtime.h\" 2>/dev/null || true"
|
||||||
run "mv \"$EL_RUNTIME_DIR\"/elc* \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
|
run "mv \"$EL_RUNTIME_DIR\"/elc* \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
|
||||||
run "chmod +x \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
|
run "chmod +x \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
|
||||||
|
elif [ "$EL_TOOLCHAIN_SOURCE" = "artifact-registry" ]; then
|
||||||
|
# Non-GCP fallback: a fresh Mac without gcloud can't reach Artifact Registry.
|
||||||
|
# Don't die — soul (from committed dist/soul.c) still builds below. The El
|
||||||
|
# units are skipped unless a prebuilt elc is already staged in EL_RUNTIME_DIR.
|
||||||
|
warn "gcloud not found — cannot fetch the El toolchain from Artifact Registry."
|
||||||
|
warn "Continuing without it: soul will still build. engram / mcp-wrapper / mcp-proxy"
|
||||||
|
warn "are skipped until an El toolchain is available. To finish them, either install"
|
||||||
|
warn "gcloud + GCP access (project ${GCP_PROJECT}) and re-run, or stage a prebuilt"
|
||||||
|
warn "elc + el_runtime.{c,h} in ${EL_RUNTIME_DIR} and set EL_TOOLCHAIN_SOURCE=local."
|
||||||
else
|
else
|
||||||
# Local: build the El compiler + runtime from the foundation/el checkout.
|
# Local: expect a prebuilt El runtime + elc already staged in EL_RUNTIME_DIR
|
||||||
# OPEN QUESTION: confirm the canonical local build entrypoint for el/elc with Will.
|
# (foundation.git no longer exists, so there is nothing to build from here).
|
||||||
warn "EL_TOOLCHAIN_SOURCE=local: expecting el_runtime.{c,h} and elc under ${FOUNDATION_REPO}/el"
|
warn "EL_TOOLCHAIN_SOURCE=local: expecting el_runtime.{c,h} and elc already in ${EL_RUNTIME_DIR}"
|
||||||
run "cp \"${FOUNDATION_REPO}/el/target/release/el_runtime.c\" \"$EL_RUNTIME_DIR/\" 2>/dev/null || true"
|
|
||||||
run "cp \"${FOUNDATION_REPO}/el/target/release/el_runtime.h\" \"$EL_RUNTIME_DIR/\" 2>/dev/null || true"
|
|
||||||
run "cp \"${FOUNDATION_REPO}/el/target/release/elc\" \"$EL_RUNTIME_DIR/\" 2>/dev/null || true"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RT="$EL_RUNTIME_DIR"
|
RT="$EL_RUNTIME_DIR"
|
||||||
CFLAGS_SSL="-I$(brew --prefix openssl@3 2>/dev/null)/include"
|
CFLAGS_SSL="-I$(brew --prefix openssl@3 2>/dev/null)/include"
|
||||||
LDFLAGS_SSL="-L$(brew --prefix openssl@3 2>/dev/null)/lib"
|
LDFLAGS_SSL="-L$(brew --prefix openssl@3 2>/dev/null)/lib"
|
||||||
|
|
||||||
|
# Every native build links el_runtime.c. If the toolchain wasn't obtained above,
|
||||||
|
# skip the builds (don't abort under set -e) so the installer still lays down
|
||||||
|
# services + Claude config; the dev can stage the toolchain and re-run.
|
||||||
|
if [ "$DRY_RUN" = 1 ] || [ -f "$RT/el_runtime.c" ]; then
|
||||||
# ── soul: compile committed dist/soul.c directly (verified CI recipe) ──────
|
# ── soul: compile committed dist/soul.c directly (verified CI recipe) ──────
|
||||||
step "building soul (dist/soul.c -> dist/neuron)"
|
step "building soul (dist/soul.c -> dist/neuron)"
|
||||||
run "mkdir -p \"${NEURON_REPO}/dist\""
|
run "mkdir -p \"${NEURON_REPO}/dist\""
|
||||||
@@ -254,11 +268,18 @@ else
|
|||||||
run "\"$RT/elc\" \"$src\" -o \"$outdir/$base.c\""
|
run "\"$RT/elc\" \"$src\" -o \"$outdir/$base.c\""
|
||||||
run "cc -O2 -DHAVE_CURL -I\"$RT\" $CFLAGS_SSL \"$outdir/$base.c\" \"$RT/el_runtime.c\" $LDFLAGS_SSL -lssl -lcrypto -lcurl -lpthread -lm -o \"$bin\""
|
run "cc -O2 -DHAVE_CURL -I\"$RT\" $CFLAGS_SSL \"$outdir/$base.c\" \"$RT/el_runtime.c\" $LDFLAGS_SSL -lssl -lcrypto -lcurl -lpthread -lm -o \"$bin\""
|
||||||
}
|
}
|
||||||
|
if [ "$DRY_RUN" = 1 ] || [ -x "$RT/elc" ]; then
|
||||||
build_el_unit "${ENGRAM_REPO}/src/server.el" "server" "$ENGRAM_BIN"
|
build_el_unit "${ENGRAM_REPO}/src/server.el" "server" "$ENGRAM_BIN"
|
||||||
build_el_unit "${NEURON_REPO}/mcp-wrapper/src/main.el" "main" "$MCP_WRAPPER_BIN"
|
build_el_unit "${NEURON_REPO}/mcp-wrapper/src/main.el" "main" "$MCP_WRAPPER_BIN"
|
||||||
build_el_unit "${NEURON_REPO}/mcp-proxy/src/main.el" "main" "$MCP_PROXY_BIN"
|
build_el_unit "${NEURON_REPO}/mcp-proxy/src/main.el" "main" "$MCP_PROXY_BIN"
|
||||||
build_el_unit "${FOUNDATION_REPO}/forge/src/forge.el" "forge" "$FORGE_BIN"
|
ok "engram, mcp-wrapper, mcp-proxy built"
|
||||||
ok "engram, mcp-wrapper, mcp-proxy, forge built"
|
else
|
||||||
|
warn "El compiler (elc) not in $RT — skipped engram/mcp-wrapper/mcp-proxy build (soul is built)."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "El runtime (el_runtime.c) not in $RT — skipping native builds (soul, engram, wrapper, proxy)."
|
||||||
|
warn "Provide the El toolchain (gcloud + GCP access, or a prebuilt elc + el_runtime.{c,h} in $RT), then re-run."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@@ -307,10 +328,10 @@ if [ "$DRY_RUN" = 1 ]; then
|
|||||||
else
|
else
|
||||||
# Wait for engram to be listening (up to ~30s).
|
# Wait for engram to be listening (up to ~30s).
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
if curl -fsS "http://localhost:${ENGRAM_PORT}/api/health" >/dev/null 2>&1; then break; fi
|
if curl -fsS "http://localhost:${ENGRAM_PORT}/health" >/dev/null 2>&1; then break; fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
if curl -fsS "http://localhost:${ENGRAM_PORT}/api/health" >/dev/null 2>&1; then
|
if curl -fsS "http://localhost:${ENGRAM_PORT}/health" >/dev/null 2>&1; then
|
||||||
# Skip if identity root already present (idempotent).
|
# Skip if identity root already present (idempotent).
|
||||||
if curl -fsS "http://localhost:${ENGRAM_PORT}/api/nodes/kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" \
|
if curl -fsS "http://localhost:${ENGRAM_PORT}/api/nodes/kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" \
|
||||||
-H "Authorization: Bearer ${ENGRAM_API_KEY}" 2>/dev/null | grep -q 'kn-efeb4a5b'; then
|
-H "Authorization: Bearer ${ENGRAM_API_KEY}" 2>/dev/null | grep -q 'kn-efeb4a5b'; then
|
||||||
@@ -344,19 +365,44 @@ for h in neuron-self-load.sh neuron-agent-preamble.sh pre-compact.sh; do
|
|||||||
done
|
done
|
||||||
ok "installed core hooks (self-load, agent-preamble, pre-compact)"
|
ok "installed core hooks (self-load, agent-preamble, pre-compact)"
|
||||||
|
|
||||||
# 7c. local MCP registration -> mcp-proxy front door
|
# 7c. local MCP registration -> mcp-proxy front door.
|
||||||
|
# Claude Code reads MCP servers from ~/.claude.json (the "mcpServers" key), NOT
|
||||||
|
# ~/.claude/mcp.json. Render a reference copy, then jq-merge just the "neuron"
|
||||||
|
# entry into ~/.claude.json so we preserve every other server and top-level key.
|
||||||
render "${TEMPLATES}/claude/mcp.json.tmpl" "${CLAUDE_DIR}/mcp.json.neuron"
|
render "${TEMPLATES}/claude/mcp.json.tmpl" "${CLAUDE_DIR}/mcp.json.neuron"
|
||||||
if [ -f "${CLAUDE_DIR}/mcp.json" ] && grep -q '"neuron"' "${CLAUDE_DIR}/mcp.json" 2>/dev/null; then
|
CLAUDE_JSON="${HOME}/.claude.json"
|
||||||
ok "~/.claude/mcp.json already registers 'neuron' — wrote reference copy mcp.json.neuron"
|
if [ "$DRY_RUN" = 1 ]; then
|
||||||
|
echo " [dry-run] merge mcpServers.neuron into ${CLAUDE_JSON} (jq deep-merge)"
|
||||||
else
|
else
|
||||||
run "cp \"${CLAUDE_DIR}/mcp.json.neuron\" \"${CLAUDE_DIR}/mcp.json\""
|
[ -f "$CLAUDE_JSON" ] || echo '{}' > "$CLAUDE_JSON"
|
||||||
ok "wrote ~/.claude/mcp.json (neuron -> http://127.0.0.1:${PROXY_PORT}/)"
|
_tmp="$(mktemp)"
|
||||||
|
if jq -s '.[0] * .[1]' "$CLAUDE_JSON" "${CLAUDE_DIR}/mcp.json.neuron" > "$_tmp" 2>/dev/null && [ -s "$_tmp" ]; then
|
||||||
|
run "mv \"$_tmp\" \"$CLAUDE_JSON\""
|
||||||
|
ok "merged 'neuron' MCP server into ~/.claude.json (neuron -> http://127.0.0.1:${PROXY_PORT}/)"
|
||||||
|
else
|
||||||
|
rm -f "$_tmp"
|
||||||
|
warn "could not jq-merge ~/.claude.json (invalid JSON?) — add 'neuron' from ~/.claude/mcp.json.neuron by hand"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 7d. settings hooks — write a reference; ask user to merge if they already have settings
|
# 7d. settings hooks — merge the neuron hooks into any existing ~/.claude/settings.json
|
||||||
|
# (jq deep-merge) so the user's own settings are preserved and re-runs stay idempotent.
|
||||||
if [ -f "${CLAUDE_DIR}/settings.json" ]; then
|
if [ -f "${CLAUDE_DIR}/settings.json" ]; then
|
||||||
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.core.json\""
|
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.core.json\""
|
||||||
warn "~/.claude/settings.json exists — wrote settings.core.json for you to merge the 'hooks' block"
|
if [ "$DRY_RUN" = 1 ]; then
|
||||||
|
echo " [dry-run] merge neuron hooks from settings.core.json into ~/.claude/settings.json (jq)"
|
||||||
|
else
|
||||||
|
_tmp="$(mktemp)"
|
||||||
|
# Drop the documentation-only "//..." keys before merging into the real file.
|
||||||
|
if jq -s '.[0] * (.[1] | with_entries(select(.key | startswith("//") | not)))' \
|
||||||
|
"${CLAUDE_DIR}/settings.json" "${TEMPLATES}/claude/settings.core.json" > "$_tmp" 2>/dev/null && [ -s "$_tmp" ]; then
|
||||||
|
run "mv \"$_tmp\" \"${CLAUDE_DIR}/settings.json\""
|
||||||
|
ok "merged neuron hooks into existing ~/.claude/settings.json"
|
||||||
|
else
|
||||||
|
rm -f "$_tmp"
|
||||||
|
warn "could not jq-merge ~/.claude/settings.json — merge the 'hooks' block from settings.core.json by hand"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.json\""
|
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.json\""
|
||||||
ok "wrote ~/.claude/settings.json"
|
ok "wrote ~/.claude/settings.json"
|
||||||
@@ -374,7 +420,7 @@ else
|
|||||||
if curl -fsS --max-time 4 "$2" >/dev/null 2>&1; then ok "$1 healthy ($2)"; else warn "$1 NOT responding ($2)"; fi
|
if curl -fsS --max-time 4 "$2" >/dev/null 2>&1; then ok "$1 healthy ($2)"; else warn "$1 NOT responding ($2)"; fi
|
||||||
}
|
}
|
||||||
sleep 3
|
sleep 3
|
||||||
check "engram" "http://localhost:${ENGRAM_PORT}/api/health"
|
check "engram" "http://localhost:${ENGRAM_PORT}/health"
|
||||||
check "soul" "http://localhost:${SOUL_PORT}/health"
|
check "soul" "http://localhost:${SOUL_PORT}/health"
|
||||||
check "mcp-wrapper" "http://localhost:${WRAPPER_PORT}/health"
|
check "mcp-wrapper" "http://localhost:${WRAPPER_PORT}/health"
|
||||||
check "mcp-proxy" "http://localhost:${PROXY_PORT}/health"
|
check "mcp-proxy" "http://localhost:${PROXY_PORT}/health"
|
||||||
@@ -385,7 +431,7 @@ echo "${c_grn}━━━━━━━━━━━━━━━━━━━━━━
|
|||||||
echo "${c_grn} Neuron core dev stack install complete.${c_off}"
|
echo "${c_grn} Neuron core dev stack install complete.${c_off}"
|
||||||
echo "${c_grn}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
|
echo "${c_grn}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
|
||||||
echo " Verify by hand:"
|
echo " Verify by hand:"
|
||||||
echo " curl http://localhost:${ENGRAM_PORT}/api/health"
|
echo " curl http://localhost:${ENGRAM_PORT}/health"
|
||||||
echo " curl http://localhost:${SOUL_PORT}/health"
|
echo " curl http://localhost:${SOUL_PORT}/health"
|
||||||
echo " curl http://localhost:${PROXY_PORT}/health"
|
echo " curl http://localhost:${PROXY_PORT}/health"
|
||||||
echo " launchctl list | grep ai.neuron"
|
echo " launchctl list | grep ai.neuron"
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ let axon_raw: String = env("NEURON_API_URL")
|
|||||||
let axon_base: String = if str_eq(axon_raw, "") { "http://localhost:7771" } else { axon_raw }
|
let axon_base: String = if str_eq(axon_raw, "") { "http://localhost:7771" } else { axon_raw }
|
||||||
|
|
||||||
let studio_dir_raw: String = env("SOUL_STUDIO_DIR")
|
let studio_dir_raw: String = env("SOUL_STUDIO_DIR")
|
||||||
let studio_dir: String = if str_eq(studio_dir_raw, "") { "/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon" } else { studio_dir_raw }
|
let studio_dir: String = if str_eq(studio_dir_raw, "") { env("HOME") + "/Development/neuron-technologies/products/cgi-studio/el-daemon" } else { studio_dir_raw }
|
||||||
|
|
||||||
println("[soul] boot - cgi=" + soul_cgi_id + " port=" + int_to_str(port))
|
println("[soul] boot - cgi=" + soul_cgi_id + " port=" + int_to_str(port))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user