@evermind-ai/openclaw-plugin 1.1.0 → 1.3.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/SKILL.md ADDED
@@ -0,0 +1,333 @@
1
+ ---
2
+ name: everos
3
+ version: 1.1.0
4
+ description: |
5
+ Install and configure EverOS for OpenClaw natural-language memory.
6
+
7
+ Use when users say:
8
+ - "install everos"
9
+ - "setup everos"
10
+ - "install everos plugin"
11
+ - "enable everos memory"
12
+ - "remember my preferences in OpenClaw"
13
+ author: EverMind
14
+ keywords:
15
+ - everos
16
+ - evermemos
17
+ - context engine
18
+ - persistent memory
19
+ - openclaw
20
+ - natural language memory
21
+ metadata:
22
+ openclaw:
23
+ emoji: "🧠"
24
+ ---
25
+
26
+ # EverOS
27
+
28
+ EverOS OpenClaw Plugin gives OpenClaw persistent memory through the **ContextEngine API**.
29
+
30
+ Important distinction:
31
+
32
+ - This is a `context-engine` plugin, not a `memory` slot plugin.
33
+ - Users do not need to call memory tools manually.
34
+ - Memory is triggered by normal conversation:
35
+ - before reply: relevant memory is recalled and injected
36
+ - after reply: new conversation content is saved back automatically
37
+
38
+ ---
39
+
40
+ ## Trigger phrases
41
+
42
+ Use this skill when the user wants to:
43
+
44
+ - install EverOS
45
+ - set up EverOS memory for OpenClaw
46
+ - enable long-term memory for OpenClaw
47
+ - make natural-language conversations persist across sessions
48
+ - configure the EverOS context engine
49
+
50
+ ---
51
+
52
+ ## When to use this skill
53
+
54
+ Use this skill when the user wants:
55
+
56
+ - persistent memory across OpenClaw sessions
57
+ - automatic recall and save during normal conversation
58
+ - a self-hosted EverOS backend
59
+ - memory without manually calling `memory_store` / `memory_search`
60
+
61
+ ---
62
+
63
+ ## When NOT to use this skill
64
+
65
+ Do not use this skill for:
66
+
67
+ - cloud-only `mem9` onboarding
68
+ - troubleshooting unrelated OpenClaw issues
69
+ - temporary context that only matters in the current session
70
+
71
+ ---
72
+
73
+ ## What the user gets
74
+
75
+ Automatic lifecycle behavior:
76
+
77
+ | Hook | Trigger | What happens |
78
+ | --- | --- | --- |
79
+ | `bootstrap()` | Session starts | Backend health check and session state init |
80
+ | `assemble()` | Before each turn | Searches relevant memory and injects it as context |
81
+ | `afterTurn()` | After each turn | Saves new messages from the turn |
82
+ | `compact()` | Compaction check | Participates in token-budget decisions |
83
+ | `dispose()` | Session ends | Clears in-memory session state |
84
+
85
+ User-facing result:
86
+
87
+ - the user chats naturally
88
+ - EverOS recalls relevant past context automatically
89
+ - EverOS stores new useful context automatically
90
+
91
+ ---
92
+
93
+ ## Definition of Done
94
+
95
+ This task is not complete until all of the following are true:
96
+
97
+ 1. the EverOS backend is reachable or the user has been explicitly told it still needs to be started
98
+ 2. the plugin is installed
99
+ 3. `openclaw.json` is configured correctly
100
+ 4. OpenClaw has been restarted
101
+ 5. the user has been given a natural-language verification step
102
+ 6. the full final handoff message has been sent
103
+
104
+ ---
105
+
106
+ ## Onboarding
107
+
108
+ ### Step 0 - Set expectations
109
+
110
+ Before changing anything, tell the user:
111
+
112
+ > OpenClaw will need a restart during setup. After restart, wait about 1 minute, then send a short natural-language message here to verify memory.
113
+
114
+ Do not skip this notice if you are going to restart OpenClaw automatically.
115
+
116
+ ### Step 1 - Check the EverOS backend
117
+
118
+ Default backend:
119
+
120
+ ```bash
121
+ curl -sf http://localhost:1995/health && echo "OK" || echo "NOT_RUNNING"
122
+ ```
123
+
124
+ If the backend is healthy, continue.
125
+
126
+ If it is not healthy, tell the user plainly:
127
+
128
+ > The EverOS backend is not reachable yet. I can still finish plugin installation now, but automatic recall/save will not work until the backend is running.
129
+
130
+ If the user asks how to start the backend, give the shortest useful path:
131
+
132
+ ```bash
133
+ git clone https://github.com/EverMind-AI/EverMemOS.git
134
+ cd EverMemOS
135
+ docker compose up -d
136
+ curl -LsSf https://astral.sh/uv/install.sh | sh
137
+ uv sync
138
+ cp env.template .env
139
+ # edit .env
140
+ uv run python src/run.py
141
+ ```
142
+
143
+ ### Step 2 - Install plugin
144
+
145
+ Preferred one-shot install:
146
+
147
+ ```bash
148
+ npx --yes --package @evermind-ai/openclaw-plugin everos-install
149
+ ```
150
+
151
+ Manual alternative:
152
+
153
+ ```bash
154
+ npm install -g @evermind-ai/openclaw-plugin
155
+ everos-install
156
+ ```
157
+
158
+ What the installer does:
159
+
160
+ - adds the plugin path to `plugins.load.paths`
161
+ - adds `@evermind-ai/openclaw-plugin` to `plugins.allow`
162
+ - sets `plugins.slots.contextEngine = "@evermind-ai/openclaw-plugin"`
163
+ - sets `plugins.slots.memory = "none"` to avoid slot conflicts
164
+ - creates or updates `plugins.entries["@evermind-ai/openclaw-plugin"]`
165
+
166
+ ### Step 3 - Manual config fallback
167
+
168
+ If the installer is unavailable, patch `~/.openclaw/openclaw.json` manually.
169
+
170
+ Expected config shape:
171
+
172
+ ```json
173
+ {
174
+ "plugins": {
175
+ "allow": ["@evermind-ai/openclaw-plugin"],
176
+ "slots": {
177
+ "memory": "none",
178
+ "contextEngine": "@evermind-ai/openclaw-plugin"
179
+ },
180
+ "entries": {
181
+ "@evermind-ai/openclaw-plugin": {
182
+ "enabled": true,
183
+ "config": {
184
+ "baseUrl": "http://localhost:1995",
185
+ "userId": "everos-user",
186
+ "groupId": "everos-group",
187
+ "topK": 5,
188
+ "memoryTypes": ["episodic_memory", "profile", "agent_skill", "agent_case"],
189
+ "retrieveMethod": "hybrid"
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ Merge-safe patch:
198
+
199
+ ```bash
200
+ jq '
201
+ .plugins = (.plugins // {}) |
202
+ .plugins.load = (.plugins.load // {}) |
203
+ .plugins.load.paths = ((.plugins.load.paths // []) + ["/path/to/evermemos-openclaw-plugin"] | unique) |
204
+ .plugins.allow = ((.plugins.allow // []) + ["@evermind-ai/openclaw-plugin"] | unique) |
205
+ .plugins.slots = (.plugins.slots // {}) |
206
+ .plugins.slots.memory = "none" |
207
+ .plugins.slots.contextEngine = "@evermind-ai/openclaw-plugin" |
208
+ .plugins.entries = (.plugins.entries // {}) |
209
+ .plugins.entries["@evermind-ai/openclaw-plugin"].enabled = true |
210
+ .plugins.entries["@evermind-ai/openclaw-plugin"].config = (
211
+ (.plugins.entries["@evermind-ai/openclaw-plugin"].config // {}) + {
212
+ "baseUrl": "http://localhost:1995",
213
+ "userId": "everos-user",
214
+ "groupId": "everos-group",
215
+ "topK": 5,
216
+ "memoryTypes": ["episodic_memory", "profile", "agent_skill", "agent_case"],
217
+ "retrieveMethod": "hybrid"
218
+ }
219
+ )
220
+ ' ~/.openclaw/openclaw.json > tmp.json && mv tmp.json ~/.openclaw/openclaw.json
221
+ ```
222
+
223
+ ### Step 4 - Restart OpenClaw
224
+
225
+ Restart command:
226
+
227
+ ```bash
228
+ openclaw gateway restart
229
+ ```
230
+
231
+ Immediately before restart, tell the user:
232
+
233
+ > EverOS is installed. I am restarting OpenClaw now. After about 1 minute, send a short message so we can verify memory recall.
234
+
235
+ ### Step 5 - Verify
236
+
237
+ Verification has two parts.
238
+
239
+ Backend:
240
+
241
+ ```bash
242
+ curl http://localhost:1995/health
243
+ ```
244
+
245
+ User-facing natural-language test:
246
+
247
+ > Say: "Remember: I like espresso."
248
+ >
249
+ > Then ask: "What coffee do I like?"
250
+
251
+ This is the preferred validation because it checks the real user flow instead of just config.
252
+
253
+ ---
254
+
255
+ ## Final handoff
256
+
257
+ After successful setup, send this handoff message in the user's language.
258
+ Do not remove sections.
259
+
260
+ ```text
261
+ ✅ EverOS is ready.
262
+
263
+ 🧭 WHAT YOU CAN DO NEXT
264
+
265
+ From now on, you can use normal natural language to make OpenClaw remember information.
266
+ You do not need to call memory tools manually.
267
+
268
+ Examples:
269
+ - "Remember: I like espresso."
270
+ - "Remember: this project uses PostgreSQL by default."
271
+ - "My coding style prefers small functions and explicit naming."
272
+
273
+ Later you can ask:
274
+ - "What coffee do I like?"
275
+ - "What database does this project use by default?"
276
+
277
+ ⚙️ CURRENT CONNECTION
278
+
279
+ EverOS backend:
280
+ BASE_URL: <base-url>
281
+
282
+ OpenClaw config file:
283
+ ~/.openclaw/openclaw.json
284
+
285
+ ♻️ RECOVERY
286
+
287
+ 1. Keep your EverOS backend data and configuration
288
+ 2. Reinstall this plugin on the new machine
289
+ 3. Write the same `baseUrl`, `userId`, and `groupId` back into `openclaw.json`
290
+ 4. Restart OpenClaw to reconnect to the same memory space
291
+
292
+ 📦 BACKUP
293
+
294
+ - Back up `~/.openclaw/openclaw.json`
295
+ - Back up the EverOS backend data directory or database
296
+ - Back up the EverMemOS `.env` and deployment configuration
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Troubleshooting
302
+
303
+ | Symptom | Fix |
304
+ | --- | --- |
305
+ | Plugin not loading | Check `plugins.allow`, `plugins.load.paths`, and `plugins.slots.contextEngine` |
306
+ | Backend unhealthy | Check `baseUrl` and ensure the EverOS backend is running |
307
+ | No recall | Verify the backend contains memories and the query is meaningful |
308
+ | No save | Verify `afterTurn()` is running and backend write API is reachable |
309
+ | Memory plugin conflict | Make sure `plugins.slots.memory = "none"` |
310
+
311
+ ---
312
+
313
+ ## API reference
314
+
315
+ Base: `http://localhost:1995`
316
+
317
+ | Method | Path | Description |
318
+ | --- | --- | --- |
319
+ | GET | `/health` | Health check |
320
+ | POST | `/api/v1/memories` | Save memory |
321
+ | GET | `/api/v1/memories/search` | Search memory |
322
+ | DELETE | `/api/v1/memories` | Delete memory |
323
+
324
+ ---
325
+
326
+ ## Communication style
327
+
328
+ When talking to users:
329
+
330
+ - say this is automatic natural-language memory
331
+ - do not describe it as a `memory` slot plugin
332
+ - keep the next step concrete: restart, then try one short memory sentence
333
+ - prefer real conversational verification over low-level API demos
package/bin/install.js ADDED
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * EverOS OpenClaw Plugin Installer
5
+ * Configures OpenClaw to use the EverOS backend through a ContextEngine.
6
+ */
7
+
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { exec } from "node:child_process";
11
+ import { fileURLToPath } from "node:url";
12
+ import readline from "node:readline";
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ const HOME_DIR = process.env.HOME || process.env.USERPROFILE;
18
+ const CONFIG_PATH = path.join(HOME_DIR, ".openclaw", "openclaw.json");
19
+ const PLUGIN_ID = "@evermind-ai/openclaw-plugin";
20
+ const PLUGIN_DIR = path.join(__dirname, "..");
21
+ const STABLE_PLUGIN_DIR = path.join(HOME_DIR, ".openclaw", "plugins", "evermind-ai-openclaw-plugin");
22
+ const DEFAULT_CONFIG = {
23
+ baseUrl: "http://localhost:1995",
24
+ userId: "everos-user",
25
+ groupId: "everos-group",
26
+ topK: 5,
27
+ memoryTypes: ["episodic_memory", "profile", "agent_skill", "agent_case"],
28
+ retrieveMethod: "hybrid",
29
+ };
30
+
31
+ const colors = {
32
+ reset: "\x1b[0m",
33
+ green: "\x1b[32m",
34
+ yellow: "\x1b[33m",
35
+ blue: "\x1b[34m",
36
+ red: "\x1b[31m",
37
+ cyan: "\x1b[36m",
38
+ };
39
+
40
+ function log(message, color = "reset") {
41
+ console.log(`${colors[color]}${message}${colors.reset}`);
42
+ }
43
+
44
+ function success(message) {
45
+ log(`✓ ${message}`, "green");
46
+ }
47
+
48
+ function error(message) {
49
+ log(`✗ ${message}`, "red");
50
+ }
51
+
52
+ function info(message) {
53
+ log(`ℹ ${message}`, "cyan");
54
+ }
55
+
56
+ function warn(message) {
57
+ log(`⚠ ${message}`, "yellow");
58
+ }
59
+
60
+ function hr() {
61
+ log("-".repeat(60), "blue");
62
+ }
63
+
64
+ const rl = readline.createInterface({
65
+ input: process.stdin,
66
+ output: process.stdout,
67
+ });
68
+
69
+ async function prompt(question) {
70
+ return new Promise((resolve) => {
71
+ rl.question(question, (answer) => {
72
+ resolve(answer.trim());
73
+ });
74
+ });
75
+ }
76
+
77
+ async function promptWithDefault(label, defaultValue) {
78
+ const answer = await prompt(`${label} (default: ${defaultValue}): `);
79
+ return answer || defaultValue;
80
+ }
81
+
82
+ function closeAndExit(code) {
83
+ rl.close();
84
+ process.exit(code);
85
+ }
86
+
87
+ async function checkBackendHealth(baseUrl) {
88
+ try {
89
+ const response = await fetch(`${baseUrl.replace(/\/*$/, "")}/health`, {
90
+ signal: AbortSignal.timeout(5000),
91
+ });
92
+ if (!response.ok) {
93
+ return { ok: false, reason: `HTTP ${response.status}` };
94
+ }
95
+ const data = await response.json().catch(() => null);
96
+ return {
97
+ ok: data?.status === "healthy" || data?.status === "ok" || response.ok,
98
+ status: data?.status,
99
+ };
100
+ } catch (err) {
101
+ return { ok: false, reason: err.message };
102
+ }
103
+ }
104
+
105
+ function isDevelopmentCheckout(dir) {
106
+ return fs.existsSync(path.join(dir, ".git"));
107
+ }
108
+
109
+ function ensureStablePluginPath() {
110
+ if (isDevelopmentCheckout(PLUGIN_DIR)) {
111
+ info(`Using local development checkout: ${PLUGIN_DIR}`);
112
+ return PLUGIN_DIR;
113
+ }
114
+
115
+ if (path.resolve(PLUGIN_DIR) === path.resolve(STABLE_PLUGIN_DIR)) {
116
+ return STABLE_PLUGIN_DIR;
117
+ }
118
+
119
+ fs.mkdirSync(path.dirname(STABLE_PLUGIN_DIR), { recursive: true });
120
+ fs.cpSync(PLUGIN_DIR, STABLE_PLUGIN_DIR, {
121
+ recursive: true,
122
+ force: true,
123
+ });
124
+
125
+ const installerPath = path.join(STABLE_PLUGIN_DIR, "bin", "install.js");
126
+ if (fs.existsSync(installerPath)) {
127
+ fs.chmodSync(installerPath, 0o755);
128
+ }
129
+
130
+ info(`Installed plugin files to ${STABLE_PLUGIN_DIR}`);
131
+ return STABLE_PLUGIN_DIR;
132
+ }
133
+
134
+ function isLegacyPluginPath(entry, pluginPath) {
135
+ if (typeof entry !== "string") return false;
136
+ if (path.resolve(entry) === path.resolve(pluginPath)) return false;
137
+
138
+ const normalized = entry.replace(/\\/g, "/");
139
+ return normalized.includes("evermemos-openclaw-plugin") ||
140
+ normalized.includes("evermind-ai-openclaw-plugin") ||
141
+ normalized.includes("@evermind-ai/openclaw-plugin");
142
+ }
143
+
144
+ function loadConfig() {
145
+ if (!fs.existsSync(CONFIG_PATH)) {
146
+ return { exists: false, data: null };
147
+ }
148
+
149
+ try {
150
+ return { exists: true, data: JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")) };
151
+ } catch (err) {
152
+ error(`Failed to parse ${CONFIG_PATH}: ${err.message}`);
153
+ error("Please fix the JSON syntax before continuing.");
154
+ error("Installation aborted to avoid corrupting your configuration.");
155
+ return { exists: true, error: err.message };
156
+ }
157
+ }
158
+
159
+ function saveConfig(config) {
160
+ try {
161
+ if (fs.existsSync(CONFIG_PATH)) {
162
+ fs.copyFileSync(CONFIG_PATH, `${CONFIG_PATH}.bak`);
163
+ }
164
+ fs.writeFileSync(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
165
+ return true;
166
+ } catch (err) {
167
+ error(`Failed to save config: ${err.message}`);
168
+ return false;
169
+ }
170
+ }
171
+
172
+ function ensureConfigShape(config) {
173
+ config.plugins = config.plugins || {};
174
+ config.plugins.load = config.plugins.load || {};
175
+ config.plugins.load.paths = Array.isArray(config.plugins.load.paths) ? config.plugins.load.paths : [];
176
+ config.plugins.allow = Array.isArray(config.plugins.allow) ? config.plugins.allow : [];
177
+ config.plugins.slots = config.plugins.slots || {};
178
+ config.plugins.entries = config.plugins.entries || {};
179
+ }
180
+
181
+ function mergePluginConfig(existingConfig, overrides = {}) {
182
+ return {
183
+ ...DEFAULT_CONFIG,
184
+ ...(existingConfig || {}),
185
+ ...overrides,
186
+ };
187
+ }
188
+
189
+ function printSummary(pluginPath, entry) {
190
+ hr();
191
+ log("Installation Summary", "blue");
192
+ log(` Plugin ID: ${PLUGIN_ID}`);
193
+ log(` Plugin path: ${pluginPath}`);
194
+ log(` Config file: ${CONFIG_PATH}`);
195
+ log(` Backend URL: ${entry.config.baseUrl}`);
196
+ log(` User ID: ${entry.config.userId}`);
197
+ log(` Group ID: ${entry.config.groupId}`);
198
+ log(" Mode: context-engine (natural language auto memory)");
199
+ hr();
200
+ }
201
+
202
+ function printNextSteps(entry) {
203
+ log("Next Steps", "green");
204
+ log(" 1. Make sure your EverOS backend is running.");
205
+ log(` curl ${entry.config.baseUrl}/health`, "cyan");
206
+ log("");
207
+ log(" 2. Restart OpenClaw so the context engine can load.");
208
+ log(" openclaw gateway restart", "cyan");
209
+ log("");
210
+ log(" 3. Verify with natural language.");
211
+ log(' Say: "Remember: I like espresso."', "cyan");
212
+ log(' Then ask: "What coffee do I like?"', "cyan");
213
+ hr();
214
+ }
215
+
216
+ function restartGateway() {
217
+ return new Promise((resolve) => {
218
+ exec("openclaw gateway restart", (err) => {
219
+ if (err) {
220
+ warn(`Could not restart OpenClaw automatically: ${err.message}`);
221
+ info("Please restart manually: openclaw gateway restart");
222
+ } else {
223
+ success("OpenClaw gateway restarted.");
224
+ info('After about 1 minute, send a natural language test such as "Remember: I like espresso." to verify recall.');
225
+ }
226
+ resolve();
227
+ });
228
+ });
229
+ }
230
+
231
+ async function install() {
232
+ hr();
233
+ log("EverOS OpenClaw Plugin Installer", "blue");
234
+ log("This keeps your current ContextEngine flow and enables automatic memory recall/save.");
235
+ hr();
236
+
237
+ const configResult = loadConfig();
238
+ if (configResult.error) {
239
+ closeAndExit(1);
240
+ }
241
+
242
+ let config;
243
+ if (!configResult.exists) {
244
+ warn(`OpenClaw config not found at ${CONFIG_PATH}`);
245
+ const answer = await prompt("Create a new OpenClaw config now? (Y/n): ");
246
+ if (answer.toLowerCase() === "n") {
247
+ info("Installation cancelled.");
248
+ closeAndExit(0);
249
+ }
250
+
251
+ fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
252
+ config = {};
253
+ } else {
254
+ config = configResult.data;
255
+ }
256
+
257
+ ensureConfigShape(config);
258
+
259
+ const pluginPath = ensureStablePluginPath();
260
+ config.plugins.load.paths = config.plugins.load.paths.filter((entry) => {
261
+ if (isLegacyPluginPath(entry, pluginPath)) {
262
+ warn(`Removing old plugin path: ${entry}`);
263
+ return false;
264
+ }
265
+ return true;
266
+ });
267
+
268
+ if (!config.plugins.load.paths.includes(pluginPath)) {
269
+ config.plugins.load.paths.push(pluginPath);
270
+ success(`Added plugin path: ${pluginPath}`);
271
+ } else {
272
+ info(`Plugin path already configured: ${pluginPath}`);
273
+ }
274
+
275
+ if (!config.plugins.allow.includes(PLUGIN_ID)) {
276
+ config.plugins.allow.push(PLUGIN_ID);
277
+ success(`Added to allow list: ${PLUGIN_ID}`);
278
+ } else {
279
+ info(`Plugin already allowed: ${PLUGIN_ID}`);
280
+ }
281
+
282
+ if (config.plugins.slots.contextEngine !== PLUGIN_ID) {
283
+ const previous = config.plugins.slots.contextEngine || "none";
284
+ config.plugins.slots.contextEngine = PLUGIN_ID;
285
+ success(`Set contextEngine slot: ${previous} -> ${PLUGIN_ID}`);
286
+ } else {
287
+ info(`contextEngine slot already points to ${PLUGIN_ID}`);
288
+ }
289
+
290
+ if (config.plugins.slots.memory !== "none") {
291
+ const previous = config.plugins.slots.memory || "unset";
292
+ config.plugins.slots.memory = "none";
293
+ warn(`Set memory slot to none to avoid conflicts (was: ${previous})`);
294
+ } else {
295
+ info("memory slot already set to none");
296
+ }
297
+
298
+ const entry = config.plugins.entries[PLUGIN_ID] || {};
299
+ const hadExistingConfig = !!entry.config;
300
+ entry.enabled = true;
301
+
302
+ if (hadExistingConfig) {
303
+ entry.config = mergePluginConfig(entry.config);
304
+ info("Reusing existing plugin config and filling any missing defaults.");
305
+ } else {
306
+ hr();
307
+ info("Enter the minimum settings needed for the EverOS backend.");
308
+ const baseUrl = await promptWithDefault("EverOS backend URL", DEFAULT_CONFIG.baseUrl);
309
+ const health = await checkBackendHealth(baseUrl);
310
+
311
+ if (health.ok) {
312
+ success(`EverOS backend is reachable (${health.status || "ok"}).`);
313
+ } else {
314
+ warn(`EverOS backend is not reachable yet (${health.reason || "unknown reason"}).`);
315
+ warn("You can continue, but memory recall/save will not work until the backend is running.");
316
+ info("Typical start command: cd EverMemOS && uv run python src/run.py");
317
+ }
318
+
319
+ const userId = await promptWithDefault("User ID", DEFAULT_CONFIG.userId);
320
+ const groupId = await promptWithDefault("Group ID", DEFAULT_CONFIG.groupId);
321
+ entry.config = mergePluginConfig(null, { baseUrl, userId, groupId });
322
+ success("Plugin config created.");
323
+ }
324
+
325
+ config.plugins.entries[PLUGIN_ID] = entry;
326
+
327
+ hr();
328
+ info("Saving OpenClaw configuration...");
329
+ if (!saveConfig(config)) {
330
+ closeAndExit(1);
331
+ }
332
+ success("Configuration saved.");
333
+
334
+ printSummary(pluginPath, entry);
335
+ printNextSteps(entry);
336
+
337
+ const shouldRestart = await prompt("Restart OpenClaw gateway now? (Y/n): ");
338
+ if (shouldRestart.toLowerCase() !== "n") {
339
+ info("Restarting OpenClaw gateway...");
340
+ info('After restart, wait about 1 minute and test with a natural language memory prompt.');
341
+ await restartGateway();
342
+ } else {
343
+ info("When ready, run: openclaw gateway restart");
344
+ info('Then test with: "Remember: I like espresso."');
345
+ }
346
+
347
+ closeAndExit(0);
348
+ }
349
+
350
+ install().catch((err) => {
351
+ error(`Installation failed: ${err.message}`);
352
+ console.error(err);
353
+ closeAndExit(1);
354
+ });