Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
232 lines (204 loc) · 10.7 KB
/
Copy pathMakefile
File metadata and controls
232 lines (204 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# agent-memory installer (system-wide layout)
#
# Common targets:
# sudo make install Full install (user, dirs, package, units, configs)
# sudo make install-update Reinstall package + units, restart services
# sudo make uninstall Stop + disable + remove units (keeps state)
# sudo make uninstall-purge Also remove configs, state, and the service user
#
# Honors DESTDIR, PREFIX, SYSCONFDIR, PIPX_HOME for packagers and non-default
# installs:
# make install PREFIX=/usr SYSCONFDIR=/etc DESTDIR=/tmp/stage
PREFIX ?= /usr/local
SYSCONFDIR ?= /etc
DESTDIR ?=
# One knob for the name so a future rename is a variable, not a sed hunt.
APP_NAME ?= agent-memory
SVC_USER ?= $(APP_NAME)
ETC_DIR := $(DESTDIR)$(SYSCONFDIR)/$(APP_NAME)
STATE_DIR := $(DESTDIR)/var/lib/$(APP_NAME)
SYSTEMD_DIR := $(DESTDIR)/etc/systemd/system
BIN_DIR := $(DESTDIR)$(PREFIX)/bin
# Architecture-independent read-only data (the bot doc loader reads this).
DATA_DIR := $(DESTDIR)$(PREFIX)/share/$(APP_NAME)
# pipx layout: a system-wide venv under /opt/pipx, with bins symlinked into
# $(PREFIX)/bin. This works on every pipx version (no --global needed) and
# keeps service binaries on the standard PATH.
#
# The venv is the ONLY code on a deployed host. Everything the units invoke is
# a console script from the package, so there is no source tree to install,
# keep in sync, or accidentally run from.
PIPX_HOME ?= /opt/pipx
PIPX_BIN_DIR ?= $(PREFIX)/bin
REPO_DIR := $(shell pwd)
# Units that are installed but NOT enabled automatically, because they need
# something `make install` cannot provide: an optional dependency, a
# credential, or a per-host config file. Enabling them by default produced
# units that fail on every boot of a host that never wanted them.
# matrix-* need the `matrix` extra (matrix-nio) and /etc/agent-memory/matrix.env
# *-compose need docker and a per-host compose stack
# memory-sync.timer needs MEMORY_SYNC_ROOT pointed at a real directory
# Timer-activated services are static (no [Install]) and are never enabled
# directly, so only their timers appear here.
OPTIONAL_UNITS ?= matrix-bot.service matrix-autojoin.service \
baibot-compose.service litellm-compose.service llamacpp-compose.service \
hippocampus-memory-sync.timer
.PHONY: install install-update install-deps check-legacy install-package \
install-bin install-systemd install-config install-compose install-docs \
uninstall uninstall-purge help
help:
@sed -n '1,12p' Makefile
# ── Composite targets ───────────────────────────────────────────────
install: install-deps check-legacy install-package install-bin install-systemd install-config install-compose install-docs
@./scripts/post-install-message.sh "$(ETC_DIR)" "$(STATE_DIR)" || true
# Advisory only. Older installs kept a source checkout at /opt/sacred-brain
# with an in-tree venv, plus config/state under the old name. Those are no
# longer used, but they can hold edited config (.env.matrix) and a live
# memory store — so report them and let a human move them. Never delete data.
check-legacy:
@for p in /opt/sacred-brain /etc/sacred-brain /var/lib/sacred-brain; do \
if [ -e "$$p" ]; then \
echo " [!] Legacy path still present: $$p"; \
echo " Nothing uses it now. Move anything you need to the"; \
echo " $(APP_NAME) equivalent, then remove it by hand."; \
fi; \
done
@if id sacred >/dev/null 2>&1; then \
echo " [!] Legacy service user 'sacred' still exists (now '$(SVC_USER)')."; \
fi
install-update: install-package install-systemd
systemctl daemon-reload
@for unit in $$(ls ops/systemd/*.service | xargs -n1 basename); do \
systemctl is-enabled $$unit >/dev/null 2>&1 && systemctl restart $$unit || true; \
done
# ── Phase: service user + state dirs ────────────────────────────────
install-deps:
@if ! id $(SVC_USER) >/dev/null 2>&1; then \
echo " [+] Creating service user '$(SVC_USER)'"; \
useradd --system --shell /usr/sbin/nologin --home-dir /var/lib/$(APP_NAME) $(SVC_USER); \
else echo " [+] User '$(SVC_USER)' already exists"; fi
@echo " [+] Creating state directories"
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(STATE_DIR)/hippocampus
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(STATE_DIR)/governor
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(STATE_DIR)/cache
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(STATE_DIR)/dreams
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(STATE_DIR)/digests
install -d -m 0755 -o $(SVC_USER) -g $(SVC_USER) $(ETC_DIR)
# ── Phase: Python package via pipx ──────────────────────────────────
install-package:
@command -v pipx >/dev/null || { echo "ERROR: pipx not installed (apt install pipx)"; exit 1; }
@echo " [+] Installing Python package via pipx into $(PIPX_HOME)"
PIPX_HOME=$(PIPX_HOME) PIPX_BIN_DIR=$(PIPX_BIN_DIR) \
pipx install --force "$(REPO_DIR)"
# ── Phase: shell scripts on PATH ────────────────────────────────────
# agent-memory-search (and its legacy `sacred-search` alias) is NOT shipped
# from this repo any more: the single implementation lives in agent-config
# (bin/agent-memory-search), which is installed fleet-wide and reaches the
# store over the tailnet. This target is kept as a no-op for muscle memory.
install-bin:
@echo " [+] agent-memory-search is provided by agent-config (bin/agent-memory-search); nothing to install here"
# ── Phase: systemd units ────────────────────────────────────────────
install-systemd:
@echo " [+] Installing systemd units to $(SYSTEMD_DIR)"
install -d -m 0755 $(SYSTEMD_DIR)
@for f in ops/systemd/*.service ops/systemd/*.timer; do \
[ -e "$$f" ] || continue; \
install -m 0644 "$$f" $(SYSTEMD_DIR)/; \
echo " $$(basename $$f)"; \
done
@if [ -z "$(DESTDIR)" ]; then \
systemctl daemon-reload; \
skipped=""; \
for f in ops/systemd/*.service ops/systemd/*.timer; do \
[ -e "$$f" ] || continue; \
unit=$$(basename "$$f"); \
grep -q '^\[Install\]' "$$f" || continue; \
case " $(OPTIONAL_UNITS) " in \
*" $$unit "*) skipped="$$skipped $$unit"; continue;; \
esac; \
systemctl enable "$$unit"; \
done; \
if [ -n "$$skipped" ]; then \
echo " [i] Installed but NOT enabled (opt-in):"; \
for u in $$skipped; do echo " $$u"; done; \
echo " They need setup make install cannot do — see docs/INSTALL.md."; \
echo " Matrix: pipx install --force '\''$(REPO_DIR)[matrix]'\'' + $(SYSCONFDIR)/$(APP_NAME)/matrix.env"; \
echo " *-compose: docker, and edit $(SYSCONFDIR)/$(APP_NAME)/compose/<stack>/"; \
echo " memory-sync: set MEMORY_SYNC_ROOT in $(SYSCONFDIR)/$(APP_NAME)/hippocampus.env"; \
echo " Then: sudo systemctl enable --now <unit>"; \
fi; \
fi
# ── Phase: config templates ─────────────────────────────────────────
# Standalone-safe: create ETC_DIR here rather than relying on install-deps
# having run, and only claim success if the install actually succeeded.
install-config:
@echo " [+] Installing config templates to $(ETC_DIR)"
install -d -m 0755 $(ETC_DIR)
@for tmpl in ops/config/*.example; do \
[ -e "$$tmpl" ] || continue; \
target="$(ETC_DIR)/$$(basename $${tmpl%.example})"; \
if [ -f "$$target" ]; then \
echo " exists, skipping: $$target"; \
elif install -m 0640 "$$tmpl" "$$target"; then \
chown $(SVC_USER):$(SVC_USER) "$$target" 2>/dev/null || true; \
echo " created: $$target (edit CHANGE_ME values!)"; \
else \
echo " FAILED: $$target"; exit 1; \
fi; \
done
# Compose stacks are config: they are edited per host (ports, models, tokens)
# and the units run `docker compose` with $(ETC_DIR)/compose/<stack> as the
# working directory. Never clobber a stack an operator has edited.
install-compose:
@echo " [+] Installing compose stacks to $(ETC_DIR)/compose"
@for dir in ops/compose/*/; do \
[ -d "$$dir" ] || continue; \
stack=$$(basename "$$dir"); \
target="$(ETC_DIR)/compose/$$stack"; \
install -d -m 0755 "$$target"; \
for f in "$$dir"*; do \
[ -f "$$f" ] || continue; \
if [ -f "$$target/$$(basename $$f)" ]; then \
echo " exists, skipping: $$target/$$(basename $$f)"; \
else \
install -m 0644 "$$f" "$$target/"; \
fi; \
done; \
echo " $$stack"; \
done
# ── Phase: docs as installed data ───────────────────────────────────
# brain.hippocampus.bot_router serves docs by name. They are data, not a
# checkout, so they get installed rather than read out of a source tree.
install-docs:
@echo " [+] Installing docs to $(DATA_DIR)/docs"
install -d -m 0755 $(DATA_DIR)/docs
@for f in docs/*.md; do \
[ -e "$$f" ] || continue; \
install -m 0644 "$$f" $(DATA_DIR)/docs/; \
done
@echo " $$(ls -1 docs/*.md 2>/dev/null | wc -l) documents"
# ── Uninstall ───────────────────────────────────────────────────────
uninstall:
@echo " [+] Stopping and disabling units"
@for f in ops/systemd/*.service ops/systemd/*.timer; do \
[ -e "$$f" ] || continue; \
unit=$$(basename $$f); \
if [ -f "$(SYSTEMD_DIR)/$$unit" ]; then \
systemctl disable --now "$$unit" 2>/dev/null || true; \
rm -f "$(SYSTEMD_DIR)/$$unit"; \
fi; \
done
@if [ -z "$(DESTDIR)" ]; then systemctl daemon-reload; fi
@echo " [+] Removing shell utilities from $(BIN_DIR)"
@rm -f $(BIN_DIR)/agent-memory-search $(BIN_DIR)/sacred-search # pre-2026-09 installs
@echo " [+] Uninstalling Python package"
@PIPX_HOME=$(PIPX_HOME) PIPX_BIN_DIR=$(PIPX_BIN_DIR) \
pipx uninstall $(APP_NAME) 2>/dev/null || true
@echo " [+] Kept $(ETC_DIR) and $(STATE_DIR) (use 'make uninstall-purge' to remove)"
uninstall-purge: uninstall
@echo " [+] Removing $(ETC_DIR) and $(STATE_DIR)"
rm -rf $(ETC_DIR) $(STATE_DIR) $(DATA_DIR)
@if id $(SVC_USER) >/dev/null 2>&1; then \
echo " [+] Removing service user '$(SVC_USER)'"; \
userdel $(SVC_USER) 2>/dev/null || echo " [!] could not remove user '$(SVC_USER)'"; \
fi