@nowledge/openclaw-nowledge-mem 0.6.14 → 0.6.15

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to the Nowledge Mem OpenClaw plugin will be documented in this file.
4
4
 
5
+ ## [0.6.15] - 2026-03-18
6
+
7
+ ### Changed
8
+
9
+ - **Shared config for remote credentials.** The plugin now reads `apiUrl` and `apiKey` from `~/.nowledge-mem/config.json` — the same file used by nmem CLI, Bub, Claude Code, and other integrations. One config file connects all your tools. The legacy `~/.nowledge-mem/openclaw.json` is still honored at highest priority for backward compatibility, but is no longer the recommended path. New cascade for credentials: `openclaw.json` (legacy) > OpenClaw dashboard > `config.json` (shared) > env vars > defaults.
10
+
11
+ ### Fixed
12
+
13
+ - **Trailing slash in API URL no longer causes 404.** URLs like `https://mem.example.com/` (with trailing slash) produced double-slash paths in API fallback requests. The URL is now normalized at construction time.
14
+
5
15
  ## [0.6.14] - 2026-03-17
6
16
 
7
17
  ### Added
package/CLAUDE.md CHANGED
@@ -9,7 +9,7 @@ Continuation guide for `community/nowledge-mem-openclaw-plugin`.
9
9
  - Memory backend: `nmem` CLI (fallback: `uvx --from nmem-cli nmem`)
10
10
  - OpenClaw minimum: `2026.3.7` (`appendSystemContext` / system-context guidance required)
11
11
  - Architecture: **CLI-first via OpenClaw runtime** - all CLI execution goes through `api.runtime.system.runCommandWithTimeout`, not direct `child_process`
12
- - Remote mode: set `NMEM_API_URL` + `NMEM_API_KEY` env vars or config file `apiUrl` + `apiKey`
12
+ - Remote mode: `~/.nowledge-mem/config.json` (shared) or OpenClaw dashboard. Legacy `openclaw.json` still honored.
13
13
 
14
14
  ## Design Philosophy
15
15
 
@@ -30,7 +30,7 @@ src/
30
30
  index.js - plugin registration (tools, hooks, commands, CLI)
31
31
  client.js - CLI wrapper with API fallback; async runtime command execution; credential handling
32
32
  spawn-env.js - env-only credential injection for the nmem runner
33
- config.js - config cascade: ~/.nowledge-mem/openclaw.json > pluginConfig > env vars > defaults
33
+ config.js - config cascade: openclaw.json (legacy) > pluginConfig > config.json (credentials) > env > defaults
34
34
  hooks/
35
35
  behavioral.js - always-on behavioral guidance (~50 tokens/turn)
36
36
  recall.js - before_prompt_build: inject Working Memory + recalled memories
@@ -49,7 +49,8 @@ skills/
49
49
  memory-guide/
50
50
  SKILL.md - agent behavioral skill: when/how to search, save, explore memory (auto-discovered by OpenClaw)
51
51
  openclaw.plugin.json - manifest + config schema (version, uiHints, configSchema, skills)
52
- ~/.nowledge-mem/openclaw.json - optional user config file (overrides OpenClaw settings when present)
52
+ ~/.nowledge-mem/openclaw.json - legacy config file (still honored, deprecated in docs)
53
+ ~/.nowledge-mem/config.json - shared credentials (apiUrl/apiKey) read by all Nowledge Mem tools
53
54
  ```
54
55
 
55
56
  ## Tool Surface (10 tools)
@@ -92,7 +93,8 @@ openclaw.plugin.json - manifest + config schema (version, uiHints, configSchema,
92
93
 
93
94
  ## Config Keys
94
95
 
95
- Optional config file at `~/.nowledge-mem/openclaw.json`. Falls through to OpenClaw plugin settings when the file is absent.
96
+ Plugin settings: OpenClaw dashboard (pluginConfig). Legacy `~/.nowledge-mem/openclaw.json` still honored.
97
+ Credentials (apiUrl/apiKey): also reads `~/.nowledge-mem/config.json` (shared with all Nowledge Mem tools).
96
98
 
97
99
  | Key | Type | Default | Env Var | Description |
98
100
  |-----|------|---------|---------|-------------|
@@ -105,9 +107,10 @@ Optional config file at `~/.nowledge-mem/openclaw.json`. Falls through to OpenCl
105
107
  | `apiUrl` | string | `""` | `NMEM_API_URL` | Remote server URL. Empty = local (127.0.0.1:14242) |
106
108
  | `apiKey` | string | `""` | `NMEM_API_KEY` | API key. Never logged. |
107
109
 
108
- **Priority**: config file > pluginConfig > env var > default.
110
+ **Priority** (plugin-specific keys): `openclaw.json` (legacy) > pluginConfig > env var > default.
111
+ **Priority** (apiUrl/apiKey): `openclaw.json` (legacy) > pluginConfig > `config.json` (shared) > env var > default.
109
112
 
110
- `parseConfig()` returns `_sources` object tracking where each value came from (`"file"`, `"pluginConfig"`, `"env"`, `"default"`). Used by `nowledge_mem_status` tool.
113
+ `parseConfig()` returns `_sources` object tracking where each value came from (`"file"`, `"pluginConfig"`, `"sharedConfig"`, `"env"`, `"default"`). Used by `nowledge_mem_status` tool.
111
114
 
112
115
  Legacy aliases accepted silently in all sources for backward compat:
113
116
  `autoRecall`→`sessionContext`, `autoCapture`→`sessionDigest`, `captureMinInterval`→`digestMinInterval`, `maxRecallResults`→`maxContextResults`.
package/README.md CHANGED
@@ -372,24 +372,18 @@ To change settings, use the OpenClaw plugin settings UI. Changes take effect on
372
372
  | `apiUrl` | string | `""` | Remote server URL. Empty = local (`http://127.0.0.1:14242`) |
373
373
  | `apiKey` | string | `""` | API key for remote access. Injected as `NMEM_API_KEY` env var, never logged |
374
374
 
375
- ### Advanced: config file
375
+ ### Remote access
376
376
 
377
- For persistent or scripted config, create `~/.nowledge-mem/openclaw.json`:
377
+ Create `~/.nowledge-mem/config.json` with your credentials. This file is shared by all Nowledge Mem integrations (nmem CLI, Bub, Claude Code, etc.) so one file connects everything:
378
378
 
379
379
  ```json
380
380
  {
381
- "sessionContext": false,
382
- "sessionDigest": true,
383
- "digestMinInterval": 300,
384
- "maxContextResults": 5,
385
- "recallMinScore": 0,
386
- "maxThreadMessageChars": 800,
387
- "apiUrl": "",
388
- "apiKey": ""
381
+ "apiUrl": "https://<your-url>",
382
+ "apiKey": "nmem_..."
389
383
  }
390
384
  ```
391
385
 
392
- The config file takes priority over OpenClaw settings. Only the keys you include are overridden; missing keys fall through to OpenClaw settings, then env vars, then defaults.
386
+ See [Access Mem Anywhere](https://mem.nowledge.co/docs/remote-access).
393
387
 
394
388
  ### Advanced: environment variables
395
389
 
@@ -401,16 +395,25 @@ NMEM_SESSION_DIGEST=true
401
395
  NMEM_DIGEST_MIN_INTERVAL=300
402
396
  NMEM_MAX_CONTEXT_RESULTS=5
403
397
  NMEM_RECALL_MIN_SCORE=0
398
+ NMEM_MAX_THREAD_MESSAGE_CHARS=800
404
399
  NMEM_API_URL=https://...
405
400
  NMEM_API_KEY=your-key
406
401
  ```
407
402
 
408
403
  ### Priority
409
404
 
405
+ Plugin-specific settings:
410
406
  ```
411
- config file > OpenClaw settings > env vars > defaults
407
+ openclaw.json (legacy) > OpenClaw dashboard > env vars > defaults
412
408
  ```
413
409
 
410
+ Credentials (apiUrl, apiKey):
411
+ ```
412
+ openclaw.json (legacy) > OpenClaw dashboard > config.json (shared) > env vars > defaults
413
+ ```
414
+
415
+ `~/.nowledge-mem/openclaw.json` is still honored for backward compatibility but is no longer the recommended path. New users should configure plugin-specific settings via the OpenClaw dashboard and shared credentials via `~/.nowledge-mem/config.json`.
416
+
414
417
  Use `nowledge_mem_status` (or `openclaw nowledge-mem status`) to see where each value comes from.
415
418
 
416
419
  ### Trust warning after install
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "openclaw-nowledge-mem",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "kind": "memory",
5
5
  "skills": ["skills/memory-guide"],
6
6
  "uiHints": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nowledge/openclaw-nowledge-mem",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "type": "module",
5
5
  "description": "Nowledge Mem memory plugin for OpenClaw, local-first personal knowledge base",
6
6
  "author": {
package/src/client.js CHANGED
@@ -83,7 +83,7 @@ export class NowledgeMemClient {
83
83
  this.nmemCmdPromise = null;
84
84
  // Resolved once from config + env (config wins over env, both win over default)
85
85
  this._apiUrl =
86
- (credentials.apiUrl || "").trim() || "http://127.0.0.1:14242";
86
+ ((credentials.apiUrl || "").trim() || "http://127.0.0.1:14242").replace(/\/+$/, "");
87
87
  this._apiKey = (credentials.apiKey || "").trim();
88
88
  }
89
89
 
package/src/config.js CHANGED
@@ -39,13 +39,20 @@ const ALIAS_KEYS = {
39
39
  maxRecallResults: "maxContextResults",
40
40
  };
41
41
 
42
- // --- config file ---------------------------------------------------------
42
+ // --- config files --------------------------------------------------------
43
43
 
44
44
  const CONFIG_DIR = join(homedir(), ".nowledge-mem");
45
+
46
+ // Legacy dedicated config (deprecated — still honored for backward compat)
45
47
  const CONFIG_PATH = join(CONFIG_DIR, "openclaw.json");
46
48
 
49
+ // Shared config read by all Nowledge Mem integrations (nmem CLI, Bub, Claude Code, etc.)
50
+ const SHARED_CONFIG_PATH = join(CONFIG_DIR, "config.json");
51
+
47
52
  /**
48
53
  * Read ~/.nowledge-mem/openclaw.json if it exists.
54
+ * Deprecated: new users should use the OpenClaw dashboard for plugin settings
55
+ * and ~/.nowledge-mem/config.json for shared credentials (apiUrl, apiKey).
49
56
  * Returns {} when the file is missing or unreadable.
50
57
  */
51
58
  function readConfigFile(logger) {
@@ -64,8 +71,37 @@ function readConfigFile(logger) {
64
71
  }
65
72
  }
66
73
 
74
+ /**
75
+ * Read apiUrl and apiKey from the shared ~/.nowledge-mem/config.json.
76
+ * Only credentials are extracted — plugin-specific keys are ignored.
77
+ * Returns { apiUrl?, apiKey? } or {} when the file is missing or unreadable.
78
+ */
79
+ function readSharedConfig(logger) {
80
+ try {
81
+ if (!existsSync(SHARED_CONFIG_PATH)) {
82
+ return {};
83
+ }
84
+ const content = readFileSync(SHARED_CONFIG_PATH, "utf-8").trim();
85
+ if (!content) return {};
86
+ const parsed = JSON.parse(content);
87
+ const result = {};
88
+ if (typeof parsed.apiUrl === "string" && parsed.apiUrl.trim()) {
89
+ result.apiUrl = parsed.apiUrl.trim();
90
+ }
91
+ if (typeof parsed.apiKey === "string" && parsed.apiKey.trim()) {
92
+ result.apiKey = parsed.apiKey.trim();
93
+ }
94
+ return result;
95
+ } catch (err) {
96
+ logger?.warn?.(
97
+ `nowledge-mem: could not read ${SHARED_CONFIG_PATH}: ${err instanceof Error ? err.message : err}`,
98
+ );
99
+ return {};
100
+ }
101
+ }
102
+
67
103
  /** Exported for tests / CLI tooling. */
68
- export { CONFIG_PATH };
104
+ export { CONFIG_PATH, SHARED_CONFIG_PATH };
69
105
 
70
106
  // --- env var helpers -----------------------------------------------------
71
107
 
@@ -130,11 +166,18 @@ function firstDefined(...options) {
130
166
 
131
167
  /**
132
168
  * Parse plugin config with cascade:
133
- * ~/.nowledge-mem/openclaw.json > pluginConfig > env vars > defaults
134
169
  *
135
- * The config file (if it exists) takes highest priority so users who
136
- * create it get predictable behavior. Most users won't have one and
137
- * will configure via OpenClaw's plugin settings UI (pluginConfig).
170
+ * Plugin-specific keys (sessionContext, sessionDigest, etc.):
171
+ * openclaw.json (legacy) > pluginConfig (dashboard) > env vars > defaults
172
+ *
173
+ * Credentials (apiUrl, apiKey):
174
+ * openclaw.json (legacy) > pluginConfig (dashboard) > config.json (shared) > env vars > defaults
175
+ *
176
+ * Most users configure via the OpenClaw dashboard (pluginConfig) and
177
+ * ~/.nowledge-mem/config.json for shared credentials across all tools.
178
+ *
179
+ * ~/.nowledge-mem/openclaw.json is still honored for backward compat
180
+ * but is no longer the recommended path.
138
181
  *
139
182
  * Canonical keys: sessionContext, sessionDigest, digestMinInterval,
140
183
  * maxContextResults, recallMinScore, maxThreadMessageChars, apiUrl, apiKey
@@ -158,6 +201,7 @@ function firstDefined(...options) {
158
201
  export function parseConfig(raw, logger) {
159
202
  const pluginCfg = raw && typeof raw === "object" ? raw : {};
160
203
  const fileCfg = readConfigFile(logger);
204
+ const sharedCfg = readSharedConfig(logger);
161
205
 
162
206
  // Resolve aliases in both sources
163
207
  const resolvedPlugin = resolveAliases(pluginCfg);
@@ -269,7 +313,7 @@ export function parseConfig(raw, logger) {
269
313
  );
270
314
  _sources.maxThreadMessageChars = mtmc.source;
271
315
 
272
- // --- apiUrl: file > pluginConfig > env > "" ---
316
+ // --- apiUrl: file (legacy) > pluginConfig > sharedConfig > env > "" ---
273
317
  const fileUrl =
274
318
  typeof resolvedFile.apiUrl === "string" && resolvedFile.apiUrl.trim();
275
319
  const pluginUrl =
@@ -277,13 +321,14 @@ export function parseConfig(raw, logger) {
277
321
  const au = firstDefined(
278
322
  { value: fileUrl || undefined, source: "file" },
279
323
  { value: pluginUrl || undefined, source: "pluginConfig" },
324
+ { value: sharedCfg.apiUrl || undefined, source: "sharedConfig" },
280
325
  { value: envStr("NMEM_API_URL"), source: "env" },
281
326
  { value: "", source: "default" },
282
327
  );
283
328
  const apiUrl = au.value;
284
329
  _sources.apiUrl = au.source;
285
330
 
286
- // --- apiKey: file > pluginConfig > env > "" ---
331
+ // --- apiKey: file (legacy) > pluginConfig > sharedConfig > env > "" ---
287
332
  const fileKey =
288
333
  typeof resolvedFile.apiKey === "string" && resolvedFile.apiKey.trim();
289
334
  const pluginKey =
@@ -291,6 +336,7 @@ export function parseConfig(raw, logger) {
291
336
  const ak = firstDefined(
292
337
  { value: fileKey || undefined, source: "file" },
293
338
  { value: pluginKey || undefined, source: "pluginConfig" },
339
+ { value: sharedCfg.apiKey || undefined, source: "sharedConfig" },
294
340
  { value: envStr("NMEM_API_KEY"), source: "env" },
295
341
  { value: "", source: "default" },
296
342
  );