@matrixorigin/thememoria 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -0
- package/openclaw/client.ts +1030 -0
- package/openclaw/config.ts +629 -0
- package/openclaw/format.ts +55 -0
- package/openclaw/index.ts +2360 -0
- package/openclaw.plugin.json +254 -0
- package/package.json +49 -0
- package/scripts/connect_openclaw_memoria.mjs +172 -0
- package/scripts/install-openclaw-memoria.sh +727 -0
- package/scripts/uninstall-openclaw-memoria.sh +362 -0
- package/scripts/verify_plugin_install.mjs +149 -0
- package/skills/memoria-memory/SKILL.md +43 -0
- package/skills/memoria-recovery/SKILL.md +29 -0
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PLUGIN_ID="memory-memoria"
|
|
5
|
+
DEFAULT_REPO_URL="https://github.com/matrixorigin/Memoria.git"
|
|
6
|
+
DEFAULT_REPO_REF="main"
|
|
7
|
+
DEFAULT_MEMORIA_VERSION="v0.1.0"
|
|
8
|
+
|
|
9
|
+
MEMORIA_TOOL_NAMES=(
|
|
10
|
+
memory_search
|
|
11
|
+
memory_get
|
|
12
|
+
memory_store
|
|
13
|
+
memory_retrieve
|
|
14
|
+
memory_recall
|
|
15
|
+
memory_list
|
|
16
|
+
memory_stats
|
|
17
|
+
memory_profile
|
|
18
|
+
memory_correct
|
|
19
|
+
memory_purge
|
|
20
|
+
memory_forget
|
|
21
|
+
memory_health
|
|
22
|
+
memory_observe
|
|
23
|
+
memory_governance
|
|
24
|
+
memory_consolidate
|
|
25
|
+
memory_reflect
|
|
26
|
+
memory_extract_entities
|
|
27
|
+
memory_link_entities
|
|
28
|
+
memory_rebuild_index
|
|
29
|
+
memory_capabilities
|
|
30
|
+
memory_snapshot
|
|
31
|
+
memory_snapshots
|
|
32
|
+
memory_rollback
|
|
33
|
+
memory_branch
|
|
34
|
+
memory_branches
|
|
35
|
+
memory_checkout
|
|
36
|
+
memory_branch_delete
|
|
37
|
+
memory_merge
|
|
38
|
+
memory_diff
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
log() {
|
|
42
|
+
printf '[memory-memoria] %s\n' "$*"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fail() {
|
|
46
|
+
printf '[memory-memoria] error: %s\n' "$*" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
need_cmd() {
|
|
51
|
+
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
validate_openclaw_bin() {
|
|
55
|
+
local candidate="$1"
|
|
56
|
+
if ! "${candidate}" --version >/dev/null 2>&1; then
|
|
57
|
+
return 1
|
|
58
|
+
fi
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
resolve_openclaw_bin() {
|
|
63
|
+
local candidate="${1:-openclaw}"
|
|
64
|
+
local resolved=''
|
|
65
|
+
|
|
66
|
+
if [[ "${candidate}" == */* ]]; then
|
|
67
|
+
[[ -x "${candidate}" ]] || fail "OPENCLAW_BIN is not executable: ${candidate}"
|
|
68
|
+
printf '%s' "${candidate}"
|
|
69
|
+
return 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
resolved="$(command -v "${candidate}" 2>/dev/null || true)"
|
|
73
|
+
if [[ -n "${resolved}" ]]; then
|
|
74
|
+
printf '%s' "${resolved}"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
for fallback in \
|
|
79
|
+
"${HOME}/Library/pnpm/openclaw" \
|
|
80
|
+
"${HOME}/.local/share/pnpm/openclaw" \
|
|
81
|
+
"${HOME}/.pnpm/openclaw"
|
|
82
|
+
do
|
|
83
|
+
if [[ -x "${fallback}" ]]; then
|
|
84
|
+
printf '%s' "${fallback}"
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
if command -v pnpm >/dev/null 2>&1; then
|
|
90
|
+
resolved="$(pnpm bin -g 2>/dev/null || true)"
|
|
91
|
+
if [[ -n "${resolved}" && -x "${resolved}/openclaw" ]]; then
|
|
92
|
+
printf '%s' "${resolved}/openclaw"
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
fail "Missing required command: openclaw. Set OPENCLAW_BIN=/absolute/path/to/openclaw"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
usage() {
|
|
101
|
+
cat <<'EOF'
|
|
102
|
+
Install the OpenClaw Memoria plugin using the Rust Memoria CLI runtime.
|
|
103
|
+
|
|
104
|
+
Usage:
|
|
105
|
+
bash scripts/install-openclaw-memoria.sh [options]
|
|
106
|
+
curl -fsSL <raw-script-url> | env MEMORIA_EMBEDDING_API_KEY=... bash -s --
|
|
107
|
+
|
|
108
|
+
Options:
|
|
109
|
+
--source-dir <path> Use an existing checkout instead of cloning.
|
|
110
|
+
--install-dir <path> Clone target when --source-dir is not provided.
|
|
111
|
+
--repo-url <url> Git repo to clone when no local checkout is used.
|
|
112
|
+
--ref <ref> Git branch, tag, or ref to clone. Default: main.
|
|
113
|
+
--openclaw-bin <path|command> Use an existing openclaw executable.
|
|
114
|
+
--memoria-bin <path|command> Use an existing memoria executable.
|
|
115
|
+
--memoria-version <tag> Rust Memoria release tag. Default: v0.1.0.
|
|
116
|
+
--memoria-install-dir <path> Where to install memoria if it is missing.
|
|
117
|
+
--binary-only Only install/validate the memoria binary, then exit.
|
|
118
|
+
--skip-memoria-install Require an existing memoria executable.
|
|
119
|
+
--skip-plugin-install Assume the plugin is already installed/enabled in OpenClaw.
|
|
120
|
+
--verify Run verify_plugin_install.mjs after installation.
|
|
121
|
+
--help Show this help text.
|
|
122
|
+
|
|
123
|
+
Environment overrides:
|
|
124
|
+
OPENCLAW_BIN Default: auto-detected openclaw executable
|
|
125
|
+
OPENCLAW_HOME Optional target OpenClaw home.
|
|
126
|
+
MEMORIA_DB_URL Default: mysql://root:111@127.0.0.1:6001/memoria
|
|
127
|
+
MEMORIA_DEFAULT_USER_ID Default: openclaw-user
|
|
128
|
+
MEMORIA_USER_ID_STRATEGY Default: config
|
|
129
|
+
MEMORIA_AUTO_RECALL Default: true
|
|
130
|
+
MEMORIA_AUTO_OBSERVE Default: false
|
|
131
|
+
MEMORIA_EXECUTABLE Alias for --memoria-bin
|
|
132
|
+
MEMORIA_RELEASE_TAG Alias for --memoria-version
|
|
133
|
+
MEMORIA_BINARY_INSTALL_DIR Alias for --memoria-install-dir
|
|
134
|
+
MEMORIA_EMBEDDING_PROVIDER Default: openai
|
|
135
|
+
MEMORIA_EMBEDDING_MODEL Default: text-embedding-3-small
|
|
136
|
+
MEMORIA_EMBEDDING_BASE_URL Optional for official OpenAI; required for compatible gateways
|
|
137
|
+
MEMORIA_EMBEDDING_API_KEY Required unless provider=local
|
|
138
|
+
MEMORIA_EMBEDDING_DIM Auto-filled for common models; otherwise required
|
|
139
|
+
MEMORIA_LLM_BASE_URL Optional OpenAI-compatible base URL
|
|
140
|
+
MEMORIA_LLM_API_KEY Optional; required if autoObserve=true
|
|
141
|
+
MEMORIA_LLM_MODEL Optional; required if autoObserve=true
|
|
142
|
+
EOF
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
normalize_bool() {
|
|
146
|
+
local raw="${1:-}"
|
|
147
|
+
raw="$(printf '%s' "${raw}" | tr '[:upper:]' '[:lower:]')"
|
|
148
|
+
case "${raw}" in
|
|
149
|
+
1|true|yes|on)
|
|
150
|
+
printf 'true'
|
|
151
|
+
;;
|
|
152
|
+
0|false|no|off)
|
|
153
|
+
printf 'false'
|
|
154
|
+
;;
|
|
155
|
+
*)
|
|
156
|
+
fail "Expected boolean value, got: ${raw}"
|
|
157
|
+
;;
|
|
158
|
+
esac
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
infer_embedding_dim() {
|
|
162
|
+
local model="${1:-}"
|
|
163
|
+
case "${model}" in
|
|
164
|
+
text-embedding-3-small|openai/text-embedding-3-small)
|
|
165
|
+
printf '1536'
|
|
166
|
+
;;
|
|
167
|
+
text-embedding-3-large|openai/text-embedding-3-large)
|
|
168
|
+
printf '3072'
|
|
169
|
+
;;
|
|
170
|
+
text-embedding-ada-002|openai/text-embedding-ada-002)
|
|
171
|
+
printf '1536'
|
|
172
|
+
;;
|
|
173
|
+
all-MiniLM-L6-v2|sentence-transformers/all-MiniLM-L6-v2)
|
|
174
|
+
printf '384'
|
|
175
|
+
;;
|
|
176
|
+
BAAI/bge-m3)
|
|
177
|
+
printf '1024'
|
|
178
|
+
;;
|
|
179
|
+
*)
|
|
180
|
+
printf ''
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
normalize_base_url() {
|
|
186
|
+
local url="${1:-}"
|
|
187
|
+
url="${url%/}"
|
|
188
|
+
case "${url}" in
|
|
189
|
+
*/embeddings)
|
|
190
|
+
url="${url%/embeddings}"
|
|
191
|
+
;;
|
|
192
|
+
*/chat/completions)
|
|
193
|
+
url="${url%/chat/completions}"
|
|
194
|
+
;;
|
|
195
|
+
*/completions)
|
|
196
|
+
url="${url%/completions}"
|
|
197
|
+
;;
|
|
198
|
+
esac
|
|
199
|
+
printf '%s' "${url}"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
normalize_db_url() {
|
|
203
|
+
local url="${1:-}"
|
|
204
|
+
printf '%s' "${url/mysql+pymysql:\/\//mysql://}"
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
resolve_memoria_target() {
|
|
208
|
+
local os arch
|
|
209
|
+
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
210
|
+
arch="$(uname -m)"
|
|
211
|
+
case "${arch}" in
|
|
212
|
+
x86_64|amd64) arch="x86_64" ;;
|
|
213
|
+
aarch64|arm64) arch="aarch64" ;;
|
|
214
|
+
*) arch="" ;;
|
|
215
|
+
esac
|
|
216
|
+
case "${os}" in
|
|
217
|
+
linux)
|
|
218
|
+
[[ "${arch}" == "x86_64" ]] && printf 'x86_64-unknown-linux-musl' && return 0
|
|
219
|
+
[[ "${arch}" == "aarch64" ]] && printf 'aarch64-unknown-linux-musl' && return 0
|
|
220
|
+
;;
|
|
221
|
+
darwin)
|
|
222
|
+
[[ "${arch}" == "x86_64" ]] && printf 'x86_64-apple-darwin' && return 0
|
|
223
|
+
[[ "${arch}" == "aarch64" ]] && printf 'aarch64-apple-darwin' && return 0
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
226
|
+
fail "Unsupported platform: $(uname -s) $(uname -m)"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
install_memoria_binary() {
|
|
230
|
+
local install_dir="$1"
|
|
231
|
+
local version="$2"
|
|
232
|
+
local target asset url sum_url tmp
|
|
233
|
+
|
|
234
|
+
need_cmd curl
|
|
235
|
+
need_cmd tar
|
|
236
|
+
|
|
237
|
+
target="$(resolve_memoria_target)"
|
|
238
|
+
asset="memoria-${target}.tar.gz"
|
|
239
|
+
url="https://github.com/matrixorigin/Memoria/releases/download/${version}/${asset}"
|
|
240
|
+
sum_url="https://github.com/matrixorigin/Memoria/releases/download/${version}/SHA256SUMS.txt"
|
|
241
|
+
|
|
242
|
+
mkdir -p "${install_dir}"
|
|
243
|
+
tmp="$(mktemp -d)"
|
|
244
|
+
trap 'rm -rf "${tmp}"' RETURN
|
|
245
|
+
|
|
246
|
+
log "Downloading Rust Memoria ${version} (${target})"
|
|
247
|
+
curl -fL# -o "${tmp}/${asset}" "${url}"
|
|
248
|
+
|
|
249
|
+
if curl -fsSL -o "${tmp}/SHA256SUMS.txt" "${sum_url}" 2>/dev/null; then
|
|
250
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
251
|
+
(cd "${tmp}" && grep -F "${asset}" SHA256SUMS.txt | sha256sum -c - >/dev/null)
|
|
252
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
253
|
+
(cd "${tmp}" && grep -F "${asset}" SHA256SUMS.txt | shasum -a 256 -c - >/dev/null)
|
|
254
|
+
fi
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
tar -xzf "${tmp}/${asset}" -C "${tmp}"
|
|
258
|
+
cp "${tmp}/memoria" "${install_dir}/memoria"
|
|
259
|
+
chmod +x "${install_dir}/memoria"
|
|
260
|
+
printf '%s' "${install_dir}/memoria"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
resolve_memoria_bin() {
|
|
264
|
+
local candidate="${1:-}"
|
|
265
|
+
if [[ -n "${candidate}" ]]; then
|
|
266
|
+
if [[ "${candidate}" == */* ]]; then
|
|
267
|
+
[[ -x "${candidate}" ]] || fail "Memoria executable is not executable: ${candidate}"
|
|
268
|
+
printf '%s' "${candidate}"
|
|
269
|
+
return 0
|
|
270
|
+
fi
|
|
271
|
+
local resolved=''
|
|
272
|
+
resolved="$(command -v "${candidate}" 2>/dev/null || true)"
|
|
273
|
+
[[ -n "${resolved}" ]] || fail "Could not find memoria executable in PATH: ${candidate}"
|
|
274
|
+
printf '%s' "${resolved}"
|
|
275
|
+
return 0
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
local resolved=''
|
|
279
|
+
resolved="$(command -v memoria 2>/dev/null || true)"
|
|
280
|
+
if [[ -n "${resolved}" ]]; then
|
|
281
|
+
printf '%s' "${resolved}"
|
|
282
|
+
return 0
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
return 1
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
run_openclaw() {
|
|
289
|
+
if [[ -n "${OPENCLAW_HOME_VALUE}" ]]; then
|
|
290
|
+
OPENCLAW_HOME="${OPENCLAW_HOME_VALUE}" "${OPENCLAW_BIN}" "$@"
|
|
291
|
+
else
|
|
292
|
+
"${OPENCLAW_BIN}" "$@"
|
|
293
|
+
fi
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
config_file_path() {
|
|
297
|
+
if [[ -n "${OPENCLAW_HOME_VALUE}" ]]; then
|
|
298
|
+
printf '%s/.openclaw/openclaw.json' "${OPENCLAW_HOME_VALUE}"
|
|
299
|
+
else
|
|
300
|
+
printf '%s/.openclaw/openclaw.json' "${HOME}"
|
|
301
|
+
fi
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
skills_dir_path() {
|
|
305
|
+
if [[ -n "${OPENCLAW_HOME_VALUE}" ]]; then
|
|
306
|
+
printf '%s/.openclaw/skills' "${OPENCLAW_HOME_VALUE}"
|
|
307
|
+
else
|
|
308
|
+
printf '%s/.openclaw/skills' "${HOME}"
|
|
309
|
+
fi
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
install_bundled_skills() {
|
|
313
|
+
local source_skills_dir="$1"
|
|
314
|
+
local managed_skills_dir="$2"
|
|
315
|
+
|
|
316
|
+
[[ -d "${source_skills_dir}" ]] || return 0
|
|
317
|
+
|
|
318
|
+
mkdir -p "${managed_skills_dir}"
|
|
319
|
+
local skill_dir=""
|
|
320
|
+
for skill_dir in "${source_skills_dir}"/*; do
|
|
321
|
+
[[ -d "${skill_dir}" ]] || continue
|
|
322
|
+
local skill_name
|
|
323
|
+
skill_name="$(basename -- "${skill_dir}")"
|
|
324
|
+
rm -rf "${managed_skills_dir}/${skill_name}"
|
|
325
|
+
cp -R "${skill_dir}" "${managed_skills_dir}/${skill_name}"
|
|
326
|
+
log "Installed managed skill: ${skill_name}"
|
|
327
|
+
done
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
SOURCE_DIR="${MEMORIA_SOURCE_DIR:-}"
|
|
331
|
+
INSTALL_DIR="${MEMORIA_INSTALL_DIR:-$HOME/.local/share/openclaw-plugins/openclaw-memoria}"
|
|
332
|
+
REPO_URL="${MEMORIA_REPO_URL:-$DEFAULT_REPO_URL}"
|
|
333
|
+
REPO_REF="${MEMORIA_REPO_REF:-$DEFAULT_REPO_REF}"
|
|
334
|
+
OPENCLAW_BIN="${OPENCLAW_BIN:-openclaw}"
|
|
335
|
+
OPENCLAW_HOME_VALUE="${OPENCLAW_HOME:-}"
|
|
336
|
+
MEMORIA_BIN="${MEMORIA_EXECUTABLE:-${MEMORIA_BIN:-}}"
|
|
337
|
+
MEMORIA_RELEASE_TAG="${MEMORIA_RELEASE_TAG:-$DEFAULT_MEMORIA_VERSION}"
|
|
338
|
+
MEMORIA_BINARY_INSTALL_DIR="${MEMORIA_BINARY_INSTALL_DIR:-$HOME/.local/bin}"
|
|
339
|
+
SKIP_MEMORIA_INSTALL=false
|
|
340
|
+
SKIP_PLUGIN_INSTALL=false
|
|
341
|
+
BINARY_ONLY=false
|
|
342
|
+
RUN_VERIFY=false
|
|
343
|
+
|
|
344
|
+
while [[ $# -gt 0 ]]; do
|
|
345
|
+
case "$1" in
|
|
346
|
+
--source-dir)
|
|
347
|
+
SOURCE_DIR="${2:?missing value for --source-dir}"
|
|
348
|
+
shift 2
|
|
349
|
+
;;
|
|
350
|
+
--install-dir)
|
|
351
|
+
INSTALL_DIR="${2:?missing value for --install-dir}"
|
|
352
|
+
shift 2
|
|
353
|
+
;;
|
|
354
|
+
--repo-url)
|
|
355
|
+
REPO_URL="${2:?missing value for --repo-url}"
|
|
356
|
+
shift 2
|
|
357
|
+
;;
|
|
358
|
+
--ref)
|
|
359
|
+
REPO_REF="${2:?missing value for --ref}"
|
|
360
|
+
shift 2
|
|
361
|
+
;;
|
|
362
|
+
--openclaw-bin)
|
|
363
|
+
OPENCLAW_BIN="${2:?missing value for --openclaw-bin}"
|
|
364
|
+
shift 2
|
|
365
|
+
;;
|
|
366
|
+
--memoria-bin)
|
|
367
|
+
MEMORIA_BIN="${2:?missing value for --memoria-bin}"
|
|
368
|
+
shift 2
|
|
369
|
+
;;
|
|
370
|
+
--memoria-version)
|
|
371
|
+
MEMORIA_RELEASE_TAG="${2:?missing value for --memoria-version}"
|
|
372
|
+
shift 2
|
|
373
|
+
;;
|
|
374
|
+
--memoria-install-dir)
|
|
375
|
+
MEMORIA_BINARY_INSTALL_DIR="${2:?missing value for --memoria-install-dir}"
|
|
376
|
+
shift 2
|
|
377
|
+
;;
|
|
378
|
+
--skip-memoria-install)
|
|
379
|
+
SKIP_MEMORIA_INSTALL=true
|
|
380
|
+
shift
|
|
381
|
+
;;
|
|
382
|
+
--binary-only)
|
|
383
|
+
BINARY_ONLY=true
|
|
384
|
+
shift
|
|
385
|
+
;;
|
|
386
|
+
--skip-plugin-install)
|
|
387
|
+
SKIP_PLUGIN_INSTALL=true
|
|
388
|
+
shift
|
|
389
|
+
;;
|
|
390
|
+
--verify)
|
|
391
|
+
RUN_VERIFY=true
|
|
392
|
+
shift
|
|
393
|
+
;;
|
|
394
|
+
--help|-h)
|
|
395
|
+
usage
|
|
396
|
+
exit 0
|
|
397
|
+
;;
|
|
398
|
+
*)
|
|
399
|
+
fail "Unknown option: $1"
|
|
400
|
+
;;
|
|
401
|
+
esac
|
|
402
|
+
done
|
|
403
|
+
|
|
404
|
+
OPENCLAW_BIN="$(resolve_openclaw_bin "${OPENCLAW_BIN}")"
|
|
405
|
+
validate_openclaw_bin "${OPENCLAW_BIN}" || fail "OpenClaw executable is not healthy: ${OPENCLAW_BIN}. Fix OpenClaw first, then retry."
|
|
406
|
+
need_cmd node
|
|
407
|
+
|
|
408
|
+
log "Using OpenClaw executable: ${OPENCLAW_BIN}"
|
|
409
|
+
log "OpenClaw version: $("${OPENCLAW_BIN}" --version 2>/dev/null | head -n 1)"
|
|
410
|
+
|
|
411
|
+
if [[ -z "${SOURCE_DIR}" ]]; then
|
|
412
|
+
if [[ -f "${PWD}/openclaw.plugin.json" && -f "${PWD}/package.json" ]]; then
|
|
413
|
+
SOURCE_DIR="${PWD}"
|
|
414
|
+
else
|
|
415
|
+
SCRIPT_SOURCE="${BASH_SOURCE:-}"
|
|
416
|
+
if [[ -n "${SCRIPT_SOURCE}" ]]; then
|
|
417
|
+
SCRIPT_DIR="$(cd -- "$(dirname -- "${SCRIPT_SOURCE}")" && pwd)"
|
|
418
|
+
REPO_CANDIDATE="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
|
419
|
+
if [[ -f "${REPO_CANDIDATE}/openclaw.plugin.json" && -f "${REPO_CANDIDATE}/package.json" ]]; then
|
|
420
|
+
SOURCE_DIR="${REPO_CANDIDATE}"
|
|
421
|
+
fi
|
|
422
|
+
fi
|
|
423
|
+
fi
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
if [[ -z "${SOURCE_DIR}" ]]; then
|
|
427
|
+
need_cmd git
|
|
428
|
+
SOURCE_DIR="${INSTALL_DIR}"
|
|
429
|
+
mkdir -p "$(dirname -- "${SOURCE_DIR}")"
|
|
430
|
+
if [[ -d "${SOURCE_DIR}/.git" ]]; then
|
|
431
|
+
log "Updating existing checkout in ${SOURCE_DIR}"
|
|
432
|
+
git -C "${SOURCE_DIR}" fetch --depth 1 origin "${REPO_REF}"
|
|
433
|
+
git -C "${SOURCE_DIR}" checkout -f FETCH_HEAD
|
|
434
|
+
elif [[ -e "${SOURCE_DIR}" ]]; then
|
|
435
|
+
fail "Install dir already exists and is not a git checkout: ${SOURCE_DIR}"
|
|
436
|
+
else
|
|
437
|
+
log "Cloning ${REPO_URL}#${REPO_REF} to ${SOURCE_DIR}"
|
|
438
|
+
git clone --depth 1 --branch "${REPO_REF}" "${REPO_URL}" "${SOURCE_DIR}"
|
|
439
|
+
fi
|
|
440
|
+
else
|
|
441
|
+
SOURCE_DIR="$(cd -- "${SOURCE_DIR}" && pwd)"
|
|
442
|
+
log "Using existing checkout: ${SOURCE_DIR}"
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
if [[ ! -f "${SOURCE_DIR}/openclaw.plugin.json" || ! -f "${SOURCE_DIR}/package.json" ]]; then
|
|
446
|
+
if [[ -f "${SOURCE_DIR}/plugins/openclaw/openclaw.plugin.json" && -f "${SOURCE_DIR}/plugins/openclaw/package.json" ]]; then
|
|
447
|
+
SOURCE_DIR="${SOURCE_DIR}/plugins/openclaw"
|
|
448
|
+
log "Resolved plugin source directory: ${SOURCE_DIR}"
|
|
449
|
+
fi
|
|
450
|
+
fi
|
|
451
|
+
|
|
452
|
+
[[ -f "${SOURCE_DIR}/openclaw.plugin.json" ]] || fail "Missing openclaw.plugin.json in ${SOURCE_DIR}"
|
|
453
|
+
[[ -f "${SOURCE_DIR}/package.json" ]] || fail "Missing package.json in ${SOURCE_DIR}"
|
|
454
|
+
|
|
455
|
+
if MEMORIA_EXECUTABLE_VALUE="$(resolve_memoria_bin "${MEMORIA_BIN}" 2>/dev/null)"; then
|
|
456
|
+
log "Using existing memoria executable: ${MEMORIA_EXECUTABLE_VALUE}"
|
|
457
|
+
else
|
|
458
|
+
[[ "${SKIP_MEMORIA_INSTALL}" == false ]] || fail "--skip-memoria-install requires an existing memoria executable"
|
|
459
|
+
MEMORIA_EXECUTABLE_VALUE="$(install_memoria_binary "${MEMORIA_BINARY_INSTALL_DIR}" "${MEMORIA_RELEASE_TAG}")"
|
|
460
|
+
log "Installed memoria executable: ${MEMORIA_EXECUTABLE_VALUE}"
|
|
461
|
+
fi
|
|
462
|
+
log "Memoria version: $("${MEMORIA_EXECUTABLE_VALUE}" --version 2>/dev/null | head -n 1)"
|
|
463
|
+
|
|
464
|
+
if [[ "${BINARY_ONLY}" == true ]]; then
|
|
465
|
+
cat <<EOF
|
|
466
|
+
|
|
467
|
+
Binary install complete.
|
|
468
|
+
|
|
469
|
+
Memoria executable: ${MEMORIA_EXECUTABLE_VALUE}
|
|
470
|
+
EOF
|
|
471
|
+
exit 0
|
|
472
|
+
fi
|
|
473
|
+
|
|
474
|
+
MEMORIA_DB_URL="$(normalize_db_url "${MEMORIA_DB_URL:-mysql://root:111@127.0.0.1:6001/memoria}")"
|
|
475
|
+
MEMORIA_DEFAULT_USER_ID="${MEMORIA_DEFAULT_USER_ID:-openclaw-user}"
|
|
476
|
+
MEMORIA_USER_ID_STRATEGY="${MEMORIA_USER_ID_STRATEGY:-config}"
|
|
477
|
+
MEMORIA_AUTO_RECALL="$(normalize_bool "${MEMORIA_AUTO_RECALL:-true}")"
|
|
478
|
+
MEMORIA_AUTO_OBSERVE="$(normalize_bool "${MEMORIA_AUTO_OBSERVE:-false}")"
|
|
479
|
+
MEMORIA_EMBEDDING_PROVIDER="${MEMORIA_EMBEDDING_PROVIDER:-openai}"
|
|
480
|
+
MEMORIA_EMBEDDING_MODEL="${MEMORIA_EMBEDDING_MODEL:-text-embedding-3-small}"
|
|
481
|
+
MEMORIA_EMBEDDING_BASE_URL="${MEMORIA_EMBEDDING_BASE_URL:-}"
|
|
482
|
+
MEMORIA_EMBEDDING_API_KEY="${MEMORIA_EMBEDDING_API_KEY:-}"
|
|
483
|
+
MEMORIA_EMBEDDING_DIM="${MEMORIA_EMBEDDING_DIM:-}"
|
|
484
|
+
MEMORIA_LLM_BASE_URL="${MEMORIA_LLM_BASE_URL:-}"
|
|
485
|
+
MEMORIA_LLM_API_KEY="${MEMORIA_LLM_API_KEY:-}"
|
|
486
|
+
MEMORIA_LLM_MODEL="${MEMORIA_LLM_MODEL:-}"
|
|
487
|
+
|
|
488
|
+
EMBEDDING_BASE_URL_RAW="${MEMORIA_EMBEDDING_BASE_URL}"
|
|
489
|
+
LLM_BASE_URL_RAW="${MEMORIA_LLM_BASE_URL}"
|
|
490
|
+
|
|
491
|
+
MEMORIA_EMBEDDING_BASE_URL="$(normalize_base_url "${MEMORIA_EMBEDDING_BASE_URL}")"
|
|
492
|
+
MEMORIA_LLM_BASE_URL="$(normalize_base_url "${MEMORIA_LLM_BASE_URL}")"
|
|
493
|
+
|
|
494
|
+
if [[ -n "${EMBEDDING_BASE_URL_RAW}" && "${EMBEDDING_BASE_URL_RAW}" != "${MEMORIA_EMBEDDING_BASE_URL}" ]]; then
|
|
495
|
+
log "Normalized embedding base URL to ${MEMORIA_EMBEDDING_BASE_URL}"
|
|
496
|
+
fi
|
|
497
|
+
if [[ -n "${LLM_BASE_URL_RAW}" && "${LLM_BASE_URL_RAW}" != "${MEMORIA_LLM_BASE_URL}" ]]; then
|
|
498
|
+
log "Normalized LLM base URL to ${MEMORIA_LLM_BASE_URL}"
|
|
499
|
+
fi
|
|
500
|
+
|
|
501
|
+
KNOWN_EMBEDDING_DIM="$(infer_embedding_dim "${MEMORIA_EMBEDDING_MODEL}")"
|
|
502
|
+
if [[ "${MEMORIA_EMBEDDING_PROVIDER}" != "local" && -z "${MEMORIA_EMBEDDING_API_KEY}" ]]; then
|
|
503
|
+
fail "MEMORIA_EMBEDDING_API_KEY is required unless provider=local"
|
|
504
|
+
fi
|
|
505
|
+
if [[ "${MEMORIA_EMBEDDING_PROVIDER}" != "local" && -z "${MEMORIA_EMBEDDING_DIM}" ]]; then
|
|
506
|
+
MEMORIA_EMBEDDING_DIM="${KNOWN_EMBEDDING_DIM}"
|
|
507
|
+
[[ -n "${MEMORIA_EMBEDDING_DIM}" ]] || fail "MEMORIA_EMBEDDING_DIM is required for model ${MEMORIA_EMBEDDING_MODEL}"
|
|
508
|
+
log "Auto-selected embedding dimension ${MEMORIA_EMBEDDING_DIM} for ${MEMORIA_EMBEDDING_MODEL}"
|
|
509
|
+
fi
|
|
510
|
+
if [[ "${MEMORIA_EMBEDDING_PROVIDER}" == "local" ]]; then
|
|
511
|
+
log "Embedding provider is local. Make sure your memoria binary was built with local-embedding support."
|
|
512
|
+
fi
|
|
513
|
+
if [[ "${MEMORIA_AUTO_OBSERVE}" == "true" ]]; then
|
|
514
|
+
[[ -n "${MEMORIA_LLM_API_KEY}" ]] || fail "MEMORIA_AUTO_OBSERVE=true requires MEMORIA_LLM_API_KEY"
|
|
515
|
+
[[ -n "${MEMORIA_LLM_MODEL}" ]] || fail "MEMORIA_AUTO_OBSERVE=true requires MEMORIA_LLM_MODEL"
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
CONFIG_FILE="$(config_file_path)"
|
|
519
|
+
|
|
520
|
+
if [[ "${SKIP_PLUGIN_INSTALL}" == false ]]; then
|
|
521
|
+
log "Installing plugin into OpenClaw"
|
|
522
|
+
run_openclaw plugins install --link "${SOURCE_DIR}"
|
|
523
|
+
run_openclaw plugins enable "${PLUGIN_ID}"
|
|
524
|
+
else
|
|
525
|
+
log "Skipping OpenClaw plugin install/enable; assuming ${PLUGIN_ID} is already active"
|
|
526
|
+
fi
|
|
527
|
+
|
|
528
|
+
log "Writing plugin configuration"
|
|
529
|
+
MEMORIA_TOOL_NAMES_JSON="$(printf '%s\n' "${MEMORIA_TOOL_NAMES[@]}" | node -e 'const fs=require("node:fs"); const lines=fs.readFileSync(0,"utf8").trim().split(/\n+/).filter(Boolean); process.stdout.write(JSON.stringify(lines));')"
|
|
530
|
+
|
|
531
|
+
CONFIG_FILE="${CONFIG_FILE}" \
|
|
532
|
+
PLUGIN_ID="${PLUGIN_ID}" \
|
|
533
|
+
SOURCE_DIR="${SOURCE_DIR}" \
|
|
534
|
+
MEMORIA_EXECUTABLE_VALUE="${MEMORIA_EXECUTABLE_VALUE}" \
|
|
535
|
+
MEMORIA_DB_URL="${MEMORIA_DB_URL}" \
|
|
536
|
+
MEMORIA_DEFAULT_USER_ID="${MEMORIA_DEFAULT_USER_ID}" \
|
|
537
|
+
MEMORIA_USER_ID_STRATEGY="${MEMORIA_USER_ID_STRATEGY}" \
|
|
538
|
+
MEMORIA_AUTO_RECALL="${MEMORIA_AUTO_RECALL}" \
|
|
539
|
+
MEMORIA_AUTO_OBSERVE="${MEMORIA_AUTO_OBSERVE}" \
|
|
540
|
+
MEMORIA_EMBEDDING_PROVIDER="${MEMORIA_EMBEDDING_PROVIDER}" \
|
|
541
|
+
MEMORIA_EMBEDDING_MODEL="${MEMORIA_EMBEDDING_MODEL}" \
|
|
542
|
+
MEMORIA_EMBEDDING_BASE_URL="${MEMORIA_EMBEDDING_BASE_URL}" \
|
|
543
|
+
MEMORIA_EMBEDDING_API_KEY="${MEMORIA_EMBEDDING_API_KEY}" \
|
|
544
|
+
MEMORIA_EMBEDDING_DIM="${MEMORIA_EMBEDDING_DIM}" \
|
|
545
|
+
MEMORIA_LLM_BASE_URL="${MEMORIA_LLM_BASE_URL}" \
|
|
546
|
+
MEMORIA_LLM_API_KEY="${MEMORIA_LLM_API_KEY}" \
|
|
547
|
+
MEMORIA_LLM_MODEL="${MEMORIA_LLM_MODEL}" \
|
|
548
|
+
MEMORIA_TOOL_NAMES_JSON="${MEMORIA_TOOL_NAMES_JSON}" \
|
|
549
|
+
node - <<'NODE'
|
|
550
|
+
const fs = require("node:fs");
|
|
551
|
+
const path = require("node:path");
|
|
552
|
+
|
|
553
|
+
const configPath = path.resolve(process.env.CONFIG_FILE);
|
|
554
|
+
const pluginId = process.env.PLUGIN_ID;
|
|
555
|
+
const sourceDir = path.resolve(process.env.SOURCE_DIR);
|
|
556
|
+
const memoriaToolNames = JSON.parse(process.env.MEMORIA_TOOL_NAMES_JSON);
|
|
557
|
+
|
|
558
|
+
function readJson(filePath) {
|
|
559
|
+
if (!fs.existsSync(filePath)) {
|
|
560
|
+
return {};
|
|
561
|
+
}
|
|
562
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function writeJson(filePath, value) {
|
|
566
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
567
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function manifestPluginId(candidatePath) {
|
|
571
|
+
try {
|
|
572
|
+
const manifestPath = path.join(candidatePath, "openclaw.plugin.json");
|
|
573
|
+
if (!fs.existsSync(manifestPath)) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
577
|
+
return typeof manifest.id === "string" ? manifest.id : null;
|
|
578
|
+
} catch {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function mergeToolPolicy(policy) {
|
|
584
|
+
const result = policy && typeof policy === "object" && !Array.isArray(policy) ? { ...policy } : {};
|
|
585
|
+
const targetKey = Array.isArray(result.allow) ? "allow" : Array.isArray(result.alsoAllow) ? "alsoAllow" : "alsoAllow";
|
|
586
|
+
const current = Array.isArray(result[targetKey]) ? [...result[targetKey]] : [];
|
|
587
|
+
for (const toolName of memoriaToolNames) {
|
|
588
|
+
if (!current.includes(toolName)) {
|
|
589
|
+
current.push(toolName);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
result[targetKey] = current;
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const data = readJson(configPath);
|
|
597
|
+
const plugins = data.plugins && typeof data.plugins === "object" && !Array.isArray(data.plugins)
|
|
598
|
+
? data.plugins
|
|
599
|
+
: (data.plugins = {});
|
|
600
|
+
|
|
601
|
+
const load = plugins.load && typeof plugins.load === "object" && !Array.isArray(plugins.load)
|
|
602
|
+
? plugins.load
|
|
603
|
+
: (plugins.load = {});
|
|
604
|
+
const existingLoadPaths = Array.isArray(load.paths) ? load.paths : [];
|
|
605
|
+
const nextLoadPaths = [];
|
|
606
|
+
const seenLoadPaths = new Set();
|
|
607
|
+
|
|
608
|
+
for (const entry of existingLoadPaths) {
|
|
609
|
+
if (typeof entry !== "string" || !entry.trim()) {
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
const trimmed = entry.trim();
|
|
613
|
+
const resolved = path.resolve(trimmed.replace(/^~(?=$|\/|\\)/, process.env.HOME || "~"));
|
|
614
|
+
if ((trimmed.includes("openclaw-memoria") || trimmed.includes(pluginId)) && !fs.existsSync(resolved)) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (seenLoadPaths.has(resolved)) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
seenLoadPaths.add(resolved);
|
|
621
|
+
nextLoadPaths.push(trimmed);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (!seenLoadPaths.has(sourceDir)) {
|
|
625
|
+
nextLoadPaths.push(sourceDir);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
load.paths = nextLoadPaths;
|
|
629
|
+
|
|
630
|
+
plugins.allow = Array.isArray(plugins.allow) ? plugins.allow : [];
|
|
631
|
+
if (!plugins.allow.includes(pluginId)) {
|
|
632
|
+
plugins.allow.push(pluginId);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
plugins.entries = plugins.entries && typeof plugins.entries === "object" && !Array.isArray(plugins.entries)
|
|
636
|
+
? plugins.entries
|
|
637
|
+
: {};
|
|
638
|
+
delete plugins.entries[JSON.stringify(pluginId)];
|
|
639
|
+
|
|
640
|
+
const pluginEntry = plugins.entries[pluginId] && typeof plugins.entries[pluginId] === "object" && !Array.isArray(plugins.entries[pluginId])
|
|
641
|
+
? plugins.entries[pluginId]
|
|
642
|
+
: (plugins.entries[pluginId] = {});
|
|
643
|
+
pluginEntry.enabled = true;
|
|
644
|
+
pluginEntry.config = {
|
|
645
|
+
backend: "embedded",
|
|
646
|
+
memoriaExecutable: process.env.MEMORIA_EXECUTABLE_VALUE,
|
|
647
|
+
dbUrl: process.env.MEMORIA_DB_URL,
|
|
648
|
+
defaultUserId: process.env.MEMORIA_DEFAULT_USER_ID,
|
|
649
|
+
userIdStrategy: process.env.MEMORIA_USER_ID_STRATEGY,
|
|
650
|
+
autoRecall: process.env.MEMORIA_AUTO_RECALL === "true",
|
|
651
|
+
autoObserve: process.env.MEMORIA_AUTO_OBSERVE === "true",
|
|
652
|
+
embeddingProvider: process.env.MEMORIA_EMBEDDING_PROVIDER,
|
|
653
|
+
embeddingModel: process.env.MEMORIA_EMBEDDING_MODEL
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
const optionalFields = {
|
|
657
|
+
embeddingBaseUrl: process.env.MEMORIA_EMBEDDING_BASE_URL,
|
|
658
|
+
embeddingApiKey: process.env.MEMORIA_EMBEDDING_API_KEY,
|
|
659
|
+
llmBaseUrl: process.env.MEMORIA_LLM_BASE_URL,
|
|
660
|
+
llmApiKey: process.env.MEMORIA_LLM_API_KEY,
|
|
661
|
+
llmModel: process.env.MEMORIA_LLM_MODEL
|
|
662
|
+
};
|
|
663
|
+
for (const [key, value] of Object.entries(optionalFields)) {
|
|
664
|
+
if (value) {
|
|
665
|
+
pluginEntry.config[key] = value;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (process.env.MEMORIA_EMBEDDING_DIM) {
|
|
669
|
+
pluginEntry.config.embeddingDim = Number.parseInt(process.env.MEMORIA_EMBEDDING_DIM, 10);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
pluginEntry.hooks = pluginEntry.hooks && typeof pluginEntry.hooks === "object" && !Array.isArray(pluginEntry.hooks)
|
|
673
|
+
? pluginEntry.hooks
|
|
674
|
+
: {};
|
|
675
|
+
pluginEntry.hooks.allowPromptInjection = true;
|
|
676
|
+
|
|
677
|
+
plugins.slots = plugins.slots && typeof plugins.slots === "object" && !Array.isArray(plugins.slots)
|
|
678
|
+
? plugins.slots
|
|
679
|
+
: {};
|
|
680
|
+
plugins.slots.memory = pluginId;
|
|
681
|
+
|
|
682
|
+
data.tools = data.tools && typeof data.tools === "object" && !Array.isArray(data.tools)
|
|
683
|
+
? data.tools
|
|
684
|
+
: {};
|
|
685
|
+
Object.assign(data.tools, mergeToolPolicy(data.tools));
|
|
686
|
+
|
|
687
|
+
if (data.agents && typeof data.agents === "object" && Array.isArray(data.agents.list)) {
|
|
688
|
+
for (const agent of data.agents.list) {
|
|
689
|
+
if (!agent || typeof agent !== "object" || Array.isArray(agent)) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
agent.tools = mergeToolPolicy(agent.tools);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
writeJson(configPath, data);
|
|
697
|
+
NODE
|
|
698
|
+
|
|
699
|
+
log "Validating OpenClaw config"
|
|
700
|
+
run_openclaw config validate >/dev/null
|
|
701
|
+
|
|
702
|
+
install_bundled_skills "${SOURCE_DIR}/skills" "$(skills_dir_path)"
|
|
703
|
+
|
|
704
|
+
if [[ "${RUN_VERIFY}" == true ]]; then
|
|
705
|
+
log "Running install verification"
|
|
706
|
+
node "${SOURCE_DIR}/scripts/verify_plugin_install.mjs" \
|
|
707
|
+
--openclaw-bin "${OPENCLAW_BIN}" \
|
|
708
|
+
--config-file "${CONFIG_FILE}" \
|
|
709
|
+
--memoria-bin "${MEMORIA_EXECUTABLE_VALUE}"
|
|
710
|
+
fi
|
|
711
|
+
|
|
712
|
+
cat <<EOF
|
|
713
|
+
|
|
714
|
+
Install complete.
|
|
715
|
+
|
|
716
|
+
Plugin source: ${SOURCE_DIR}
|
|
717
|
+
Memoria executable: ${MEMORIA_EXECUTABLE_VALUE}
|
|
718
|
+
OpenClaw config: ${CONFIG_FILE}
|
|
719
|
+
|
|
720
|
+
Recommended smoke checks:
|
|
721
|
+
openclaw memoria capabilities
|
|
722
|
+
openclaw memoria stats
|
|
723
|
+
openclaw ltm list --limit 10
|
|
724
|
+
|
|
725
|
+
If embedded mode is enabled, make sure MatrixOne is reachable at:
|
|
726
|
+
${MEMORIA_DB_URL}
|
|
727
|
+
EOF
|