@matrixorigin/thememoria 0.4.0 → 0.4.1
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 +109 -263
- package/openclaw/__tests__/client.test.ts +69 -0
- package/openclaw/__tests__/config.test.ts +173 -0
- package/openclaw/__tests__/format.test.ts +149 -0
- package/openclaw/__tests__/helpers.ts +126 -0
- package/openclaw/__tests__/http-client.test.ts +290 -0
- package/openclaw/__tests__/parsers.test.ts +197 -0
- package/openclaw/client.ts +27 -11
- package/openclaw/config.ts +16 -8
- package/openclaw/http-client.ts +453 -0
- package/openclaw/index.ts +55 -32
- package/openclaw.plugin.json +8 -8
- package/package.json +10 -1
- package/scripts/connect_openclaw_memoria.mjs +51 -5
- package/scripts/install-openclaw-memoria.sh +13 -4
- package/scripts/uninstall-openclaw-memoria.sh +7 -7
- package/scripts/verify_plugin_install.mjs +3 -3
package/openclaw.plugin.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
3
|
-
"name": "
|
|
2
|
+
"id": "thememoria",
|
|
3
|
+
"name": "Memoria",
|
|
4
4
|
"description": "Memoria-backed long-term memory plugin for OpenClaw powered by the Rust memoria CLI and API.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.4.1",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"uiHints": {
|
|
8
8
|
"backend": {
|
|
9
9
|
"label": "Backend Mode",
|
|
10
|
-
"help": "embedded runs the Rust memoria CLI locally against MatrixOne;
|
|
10
|
+
"help": "embedded runs the Rust memoria CLI locally against MatrixOne; api connects directly to the Memoria REST API over HTTP.",
|
|
11
11
|
"placeholder": "embedded"
|
|
12
12
|
},
|
|
13
13
|
"dbUrl": {
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
},
|
|
18
18
|
"apiUrl": {
|
|
19
19
|
"label": "Memoria API URL",
|
|
20
|
-
"help": "Only used when backend=
|
|
20
|
+
"help": "Only used when backend=api.",
|
|
21
21
|
"placeholder": "http://127.0.0.1:8100"
|
|
22
22
|
},
|
|
23
23
|
"apiKey": {
|
|
24
24
|
"label": "Memoria API Token",
|
|
25
|
-
"help": "Bearer token for backend=
|
|
25
|
+
"help": "Bearer token for backend=api.",
|
|
26
26
|
"sensitive": true,
|
|
27
|
-
"placeholder": "
|
|
27
|
+
"placeholder": "sk-..."
|
|
28
28
|
},
|
|
29
29
|
"memoriaExecutable": {
|
|
30
30
|
"label": "Memoria Executable",
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"properties": {
|
|
152
152
|
"backend": {
|
|
153
153
|
"type": "string",
|
|
154
|
-
"enum": ["embedded", "
|
|
154
|
+
"enum": ["embedded", "api"]
|
|
155
155
|
},
|
|
156
156
|
"dbUrl": {
|
|
157
157
|
"type": "string"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matrixorigin/thememoria",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "OpenClaw memory plugin that uses the Rust Memoria CLI/API for embedded and remote backends.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=22"
|
|
35
35
|
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "vitest --run",
|
|
38
|
+
"test:watch": "vitest"
|
|
39
|
+
},
|
|
36
40
|
"peerDependencies": {
|
|
37
41
|
"openclaw": ">=2026.3.0"
|
|
38
42
|
},
|
|
@@ -41,6 +45,11 @@
|
|
|
41
45
|
"optional": true
|
|
42
46
|
}
|
|
43
47
|
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.5.0",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vitest": "^4.1.0"
|
|
52
|
+
},
|
|
44
53
|
"openclaw": {
|
|
45
54
|
"extensions": [
|
|
46
55
|
"./openclaw/index.ts"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
5
6
|
function fail(message) {
|
|
6
|
-
console.error(`[
|
|
7
|
+
console.error(`[thememoria] ${message}`);
|
|
7
8
|
process.exit(1);
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -23,13 +24,40 @@ function normalizeUrl(value) {
|
|
|
23
24
|
return value.trim().replace(/\/+$/, "");
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function resolveExecutable(command) {
|
|
28
|
+
if (!command) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (command.includes("/") || command.includes("\\")) {
|
|
32
|
+
return fs.existsSync(command) ? path.resolve(command) : null;
|
|
33
|
+
}
|
|
34
|
+
const result = spawnSync("/usr/bin/env", ["which", command], {
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
37
|
+
});
|
|
38
|
+
if (result.status !== 0 || typeof result.stdout !== "string") {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const resolved = result.stdout.trim().split(/\r?\n/).find(Boolean);
|
|
42
|
+
return resolved ? path.resolve(resolved) : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function binaryLacksLocalEmbeddingSupport(executablePath) {
|
|
46
|
+
try {
|
|
47
|
+
const content = fs.readFileSync(executablePath);
|
|
48
|
+
return content.includes(Buffer.from("compiled without local-embedding feature", "utf8"));
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
26
54
|
const modeRaw = readArg("--mode", "cloud").trim().toLowerCase();
|
|
27
55
|
if (modeRaw !== "cloud" && modeRaw !== "local") {
|
|
28
56
|
fail("mode must be one of: cloud, local");
|
|
29
57
|
}
|
|
30
58
|
const mode = modeRaw;
|
|
31
59
|
|
|
32
|
-
const pluginId = readArg("--plugin-id", "
|
|
60
|
+
const pluginId = readArg("--plugin-id", "thememoria").trim() || "thememoria";
|
|
33
61
|
const configFile = path.resolve(
|
|
34
62
|
readArg(
|
|
35
63
|
"--config-file",
|
|
@@ -82,7 +110,7 @@ if (mode === "cloud") {
|
|
|
82
110
|
if (!apiKey) {
|
|
83
111
|
fail("--api-key required when mode=cloud");
|
|
84
112
|
}
|
|
85
|
-
pluginConfig.backend = "
|
|
113
|
+
pluginConfig.backend = "api";
|
|
86
114
|
pluginConfig.apiUrl = normalizeUrl(apiUrl);
|
|
87
115
|
pluginConfig.apiKey = apiKey;
|
|
88
116
|
delete pluginConfig.dbUrl;
|
|
@@ -106,6 +134,24 @@ if (mode === "cloud") {
|
|
|
106
134
|
fail("--embedding-api-key required for mode=local when embedding-provider is not 'local'");
|
|
107
135
|
}
|
|
108
136
|
|
|
137
|
+
const effectiveMemoriaExecutable =
|
|
138
|
+
memoriaExecutable ||
|
|
139
|
+
(typeof pluginConfig.memoriaExecutable === "string" ? pluginConfig.memoriaExecutable.trim() : "") ||
|
|
140
|
+
"memoria";
|
|
141
|
+
if (embeddingProvider === "local") {
|
|
142
|
+
const resolvedExecutable = resolveExecutable(effectiveMemoriaExecutable);
|
|
143
|
+
if (!resolvedExecutable) {
|
|
144
|
+
fail(
|
|
145
|
+
`embedding-provider=local requires a usable memoria executable, but '${effectiveMemoriaExecutable}' could not be resolved. Pass --memoria-executable <path> or install memoria first.`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (binaryLacksLocalEmbeddingSupport(resolvedExecutable)) {
|
|
149
|
+
fail(
|
|
150
|
+
`embedding-provider=local requires a memoria binary built with local-embedding support. Resolved '${resolvedExecutable}', but this binary was built without that feature. Use a remote embedding provider or install/rebuild a local-embedding-enabled memoria binary.`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
109
155
|
pluginConfig.backend = "embedded";
|
|
110
156
|
pluginConfig.dbUrl = dbUrl;
|
|
111
157
|
pluginConfig.embeddingProvider = embeddingProvider;
|
|
@@ -153,11 +199,11 @@ console.log(
|
|
|
153
199
|
pluginId,
|
|
154
200
|
backend: pluginConfig.backend,
|
|
155
201
|
apiUrl:
|
|
156
|
-
pluginConfig.backend === "
|
|
202
|
+
pluginConfig.backend === "api" && typeof pluginConfig.apiUrl === "string"
|
|
157
203
|
? pluginConfig.apiUrl
|
|
158
204
|
: undefined,
|
|
159
205
|
apiKeySet:
|
|
160
|
-
pluginConfig.backend === "
|
|
206
|
+
pluginConfig.backend === "api" &&
|
|
161
207
|
typeof pluginConfig.apiKey === "string" &&
|
|
162
208
|
pluginConfig.apiKey.length > 0,
|
|
163
209
|
dbUrl: typeof pluginConfig.dbUrl === "string" ? pluginConfig.dbUrl : undefined,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
PLUGIN_ID="
|
|
4
|
+
PLUGIN_ID="thememoria"
|
|
5
5
|
DEFAULT_REPO_URL="https://github.com/matrixorigin/Memoria.git"
|
|
6
6
|
DEFAULT_REPO_REF="main"
|
|
7
7
|
DEFAULT_MEMORIA_VERSION="v0.1.0"
|
|
@@ -39,14 +39,20 @@ MEMORIA_TOOL_NAMES=(
|
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
log() {
|
|
42
|
-
printf '[
|
|
42
|
+
printf '[thememoria] %s\n' "$*"
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
fail() {
|
|
46
|
-
printf '[
|
|
46
|
+
printf '[thememoria] error: %s\n' "$*" >&2
|
|
47
47
|
exit 1
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
binary_lacks_local_embedding_support() {
|
|
51
|
+
local bin_path="$1"
|
|
52
|
+
[[ -f "${bin_path}" ]] || return 1
|
|
53
|
+
LC_ALL=C grep -a -q 'compiled without local-embedding feature' "${bin_path}"
|
|
54
|
+
}
|
|
55
|
+
|
|
50
56
|
need_cmd() {
|
|
51
57
|
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
|
|
52
58
|
}
|
|
@@ -508,7 +514,10 @@ if [[ "${MEMORIA_EMBEDDING_PROVIDER}" != "local" && -z "${MEMORIA_EMBEDDING_DIM}
|
|
|
508
514
|
log "Auto-selected embedding dimension ${MEMORIA_EMBEDDING_DIM} for ${MEMORIA_EMBEDDING_MODEL}"
|
|
509
515
|
fi
|
|
510
516
|
if [[ "${MEMORIA_EMBEDDING_PROVIDER}" == "local" ]]; then
|
|
511
|
-
|
|
517
|
+
if binary_lacks_local_embedding_support "${MEMORIA_EXECUTABLE_VALUE}"; then
|
|
518
|
+
fail "MEMORIA_EMBEDDING_PROVIDER=local requires a memoria binary built with local-embedding support. Resolved ${MEMORIA_EXECUTABLE_VALUE}, but that binary was built without the feature. Use a remote embedding provider or install/rebuild a local-embedding-enabled memoria binary."
|
|
519
|
+
fi
|
|
520
|
+
log "Embedding provider is local and the selected memoria binary passed the local-embedding capability check."
|
|
512
521
|
fi
|
|
513
522
|
if [[ "${MEMORIA_AUTO_OBSERVE}" == "true" ]]; then
|
|
514
523
|
[[ -n "${MEMORIA_LLM_API_KEY}" ]] || fail "MEMORIA_AUTO_OBSERVE=true requires MEMORIA_LLM_API_KEY"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
PLUGIN_ID="
|
|
4
|
+
PLUGIN_ID="thememoria"
|
|
5
5
|
DEFAULT_INSTALL_DIR="${HOME}/.local/share/openclaw-plugins/openclaw-memoria"
|
|
6
6
|
|
|
7
7
|
MEMORIA_TOOL_NAMES=(
|
|
@@ -37,11 +37,11 @@ MEMORIA_TOOL_NAMES=(
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
log() {
|
|
40
|
-
printf '[
|
|
40
|
+
printf '[thememoria] %s\n' "$*"
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
fail() {
|
|
44
|
-
printf '[
|
|
44
|
+
printf '[thememoria] error: %s\n' "$*" >&2
|
|
45
45
|
exit 1
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -66,9 +66,9 @@ Environment overrides:
|
|
|
66
66
|
OPENCLAW_HOME Optional target OpenClaw home.
|
|
67
67
|
|
|
68
68
|
What gets removed by default:
|
|
69
|
-
- plugins.entries["
|
|
70
|
-
- plugins.installs["
|
|
71
|
-
- plugins.allow entry for
|
|
69
|
+
- plugins.entries["thememoria"]
|
|
70
|
+
- plugins.installs["thememoria"]
|
|
71
|
+
- plugins.allow entry for thememoria
|
|
72
72
|
- plugins.load.paths entries that point at this plugin
|
|
73
73
|
- tool policy entries for the Memoria tool surface
|
|
74
74
|
- managed companion skills in ~/.openclaw/skills: memoria-memory, memoria-recovery
|
|
@@ -357,6 +357,6 @@ Config file: ${CONFIG_FILE}
|
|
|
357
357
|
|
|
358
358
|
Recommended follow-up checks:
|
|
359
359
|
cd ~
|
|
360
|
-
openclaw plugins list --json | rg '
|
|
360
|
+
openclaw plugins list --json | rg 'thememoria|openclaw-memoria' || true
|
|
361
361
|
openclaw config get 'plugins.slots.memory'
|
|
362
362
|
EOF
|
|
@@ -78,14 +78,14 @@ if (!fs.existsSync(configFile)) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const config = JSON.parse(fs.readFileSync(configFile, "utf8"));
|
|
81
|
-
const pluginEntry = config?.plugins?.entries?.["
|
|
81
|
+
const pluginEntry = config?.plugins?.entries?.["thememoria"];
|
|
82
82
|
if (!pluginEntry || pluginEntry.enabled !== true) {
|
|
83
|
-
throw new Error("plugins.entries.
|
|
83
|
+
throw new Error("plugins.entries.thememoria is not enabled");
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const pluginConfig = pluginEntry.config ?? {};
|
|
87
87
|
if (pluginConfig.memoriaExecutable == null) {
|
|
88
|
-
throw new Error("plugins.entries.
|
|
88
|
+
throw new Error("plugins.entries.thememoria.config.memoriaExecutable is missing");
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const resolvedMemoriaBin = pluginConfig.memoriaExecutable || memoriaBin;
|