memorylake-openclaw 1.1.1 → 1.1.2
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/lib/cli/register-cli.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import type { PluginContext } from "../plugin-context";
|
|
5
4
|
import type { MemoryLakeConfig, UploadFn } from "../types";
|
|
6
5
|
import { getProvider } from "../provider";
|
|
7
6
|
import { buildSearchOptions } from "../utils/builders";
|
|
7
|
+
import { readJson5ConfigFile } from "../utils/config-parser";
|
|
8
8
|
|
|
9
9
|
export function registerCli(pctx: PluginContext, cfg: MemoryLakeConfig): void {
|
|
10
10
|
const { api, resolveConfig } = pctx;
|
|
@@ -58,7 +58,7 @@ export function registerCli(pctx: PluginContext, cfg: MemoryLakeConfig): void {
|
|
|
58
58
|
if (opts.agent) {
|
|
59
59
|
try {
|
|
60
60
|
const openclawPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
61
|
-
const openclaw =
|
|
61
|
+
const openclaw = readJson5ConfigFile(openclawPath) as any;
|
|
62
62
|
const agents = openclaw?.agents;
|
|
63
63
|
const agentEntry = agents?.list?.find((a: any) => a.id === opts.agent);
|
|
64
64
|
const workspace = agentEntry?.workspace || agents?.defaults?.workspace;
|
package/lib/plugin-context.ts
CHANGED
|
@@ -4,6 +4,7 @@ import os from "node:os";
|
|
|
4
4
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
5
5
|
import type { MemoryLakeConfig } from "./types";
|
|
6
6
|
import { ALLOWED_KEYS, memoryLakeConfigSchema } from "./config";
|
|
7
|
+
import { readJson5ConfigFile } from "./utils/config-parser";
|
|
7
8
|
|
|
8
9
|
const PLUGIN_ID = "memorylake-openclaw";
|
|
9
10
|
const GLOBAL_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
@@ -15,7 +16,7 @@ const GLOBAL_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json")
|
|
|
15
16
|
*/
|
|
16
17
|
function readGlobalConfig(logger: OpenClawPluginApi["logger"]): MemoryLakeConfig | null {
|
|
17
18
|
try {
|
|
18
|
-
const raw =
|
|
19
|
+
const raw = readJson5ConfigFile(GLOBAL_CONFIG_PATH) as any;
|
|
19
20
|
const pluginCfg = raw?.plugins?.entries?.[PLUGIN_ID]?.config;
|
|
20
21
|
if (!pluginCfg) {
|
|
21
22
|
logger.info(`memorylake-openclaw: no plugin config found in global config (path: ${GLOBAL_CONFIG_PATH}, pluginId: ${PLUGIN_ID})`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import JSON5 from "json5";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Read and parse a JSON5-compatible config file.
|
|
6
|
+
*/
|
|
7
|
+
export function readJson5ConfigFile(filePath: string): unknown {
|
|
8
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
9
|
+
try {
|
|
10
|
+
return JSON5.parse(source);
|
|
11
|
+
} catch (err) {
|
|
12
|
+
throw new Error(`Failed to parse JSON5 config file "${filePath}": ${String(err)}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorylake-openclaw",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MemoryLake memory backend for OpenClaw",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"7zip-min": "^3.0.1",
|
|
21
21
|
"adm-zip": "^0.5.17",
|
|
22
22
|
"got": "^14.0.0",
|
|
23
|
+
"json5": "^2.2.3",
|
|
23
24
|
"node-unrar-js": "^2.0.2",
|
|
24
25
|
"tar": "^7.5.13",
|
|
25
26
|
"xz-decompress": "^0.2.3"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { readFileSync, existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
|
+
import JSON5 from "json5";
|
|
6
7
|
|
|
7
8
|
// Parse --agent
|
|
8
9
|
const args = process.argv.slice(2);
|
|
@@ -15,7 +16,13 @@ const agentId = args[agentIdx + 1];
|
|
|
15
16
|
|
|
16
17
|
// Read global config
|
|
17
18
|
const openclawPath = join(homedir(), ".openclaw", "openclaw.json");
|
|
18
|
-
|
|
19
|
+
let openclaw;
|
|
20
|
+
try {
|
|
21
|
+
openclaw = JSON5.parse(readFileSync(openclawPath, "utf-8"));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`Error: failed to parse JSON5 config file "${openclawPath}": ${String(err)}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
19
26
|
const globalCfg = openclaw?.plugins?.entries?.["memorylake-openclaw"]?.config;
|
|
20
27
|
if (!globalCfg) {
|
|
21
28
|
console.error("Error: memorylake-openclaw plugin config not found");
|
|
@@ -35,9 +42,14 @@ if (!workspace) {
|
|
|
35
42
|
const merged = { ...globalCfg };
|
|
36
43
|
const localPath = join(workspace, ".memorylake", "config.json");
|
|
37
44
|
if (existsSync(localPath)) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
try {
|
|
46
|
+
const raw = JSON.parse(readFileSync(localPath, "utf-8"));
|
|
47
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
48
|
+
Object.assign(merged, raw);
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(`Error: failed to parse workspace config at ${localPath}: ${String(err)}`);
|
|
52
|
+
process.exit(1);
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
merged.host = merged.host || "https://app.memorylake.ai";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const repoRoot = resolve(process.cwd());
|
|
9
|
+
const getConfigScript = join(repoRoot, "skills/common/get-config.mjs");
|
|
10
|
+
const pluginContextSource = join(repoRoot, "lib/plugin-context.ts");
|
|
11
|
+
const registerCliSource = join(repoRoot, "lib/cli/register-cli.ts");
|
|
12
|
+
|
|
13
|
+
function runGetConfig(homeDir, agentId = "a1") {
|
|
14
|
+
return spawnSync("node", [getConfigScript, "--agent", agentId], {
|
|
15
|
+
env: { ...process.env, HOME: homeDir },
|
|
16
|
+
encoding: "utf8",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("json5 config smoke", () => {
|
|
21
|
+
it("accepts JSON5 openclaw.json in get-config.mjs", () => {
|
|
22
|
+
const root = mkdtempSync(join(tmpdir(), "ml-json5-ok-"));
|
|
23
|
+
const home = root;
|
|
24
|
+
const workspace = join(root, "workspace");
|
|
25
|
+
mkdirSync(join(home, ".openclaw"), { recursive: true });
|
|
26
|
+
mkdirSync(join(workspace, ".memorylake"), { recursive: true });
|
|
27
|
+
|
|
28
|
+
writeFileSync(
|
|
29
|
+
join(home, ".openclaw", "openclaw.json"),
|
|
30
|
+
`{
|
|
31
|
+
// allow comments
|
|
32
|
+
plugins: {
|
|
33
|
+
entries: {
|
|
34
|
+
"memorylake-openclaw": {
|
|
35
|
+
config: {
|
|
36
|
+
apiKey: "k",
|
|
37
|
+
projectId: "p",
|
|
38
|
+
host: "https://app.memorylake.ai",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
agents: {
|
|
44
|
+
list: [{ id: "a1", workspace: "${workspace.replaceAll("\\", "\\\\")}" }],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
`,
|
|
48
|
+
);
|
|
49
|
+
writeFileSync(join(workspace, ".memorylake", "config.json"), JSON.stringify({ topK: 5 }));
|
|
50
|
+
|
|
51
|
+
const result = runGetConfig(home);
|
|
52
|
+
assert.equal(result.status, 0, result.stderr);
|
|
53
|
+
const parsed = JSON.parse(result.stdout);
|
|
54
|
+
assert.equal(parsed.projectId, "p");
|
|
55
|
+
assert.equal(parsed.workspace, workspace);
|
|
56
|
+
assert.equal(parsed.topK, 5);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns clear non-zero error for malformed JSON5 global config", () => {
|
|
60
|
+
const root = mkdtempSync(join(tmpdir(), "ml-json5-bad-global-"));
|
|
61
|
+
const home = root;
|
|
62
|
+
mkdirSync(join(home, ".openclaw"), { recursive: true });
|
|
63
|
+
writeFileSync(join(home, ".openclaw", "openclaw.json"), "{ invalid json5 }");
|
|
64
|
+
|
|
65
|
+
const result = runGetConfig(home);
|
|
66
|
+
assert.notEqual(result.status, 0);
|
|
67
|
+
assert.match(result.stderr, /failed to parse JSON5 config file/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("keeps workspace override as strict JSON", () => {
|
|
71
|
+
const root = mkdtempSync(join(tmpdir(), "ml-json5-bad-local-"));
|
|
72
|
+
const home = root;
|
|
73
|
+
const workspace = join(root, "workspace");
|
|
74
|
+
mkdirSync(join(home, ".openclaw"), { recursive: true });
|
|
75
|
+
mkdirSync(join(workspace, ".memorylake"), { recursive: true });
|
|
76
|
+
|
|
77
|
+
writeFileSync(
|
|
78
|
+
join(home, ".openclaw", "openclaw.json"),
|
|
79
|
+
JSON.stringify({
|
|
80
|
+
plugins: {
|
|
81
|
+
entries: {
|
|
82
|
+
"memorylake-openclaw": {
|
|
83
|
+
config: { apiKey: "k", projectId: "p", host: "https://app.memorylake.ai" },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
agents: { list: [{ id: "a1", workspace }] },
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
writeFileSync(join(workspace, ".memorylake", "config.json"), "{ trailing: 1, }");
|
|
91
|
+
|
|
92
|
+
const result = runGetConfig(home);
|
|
93
|
+
assert.notEqual(result.status, 0);
|
|
94
|
+
assert.match(result.stderr, /failed to parse workspace config/);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("ensures plugin and CLI global config paths use shared JSON5 parser", () => {
|
|
98
|
+
const pluginContext = readFileSync(pluginContextSource, "utf8");
|
|
99
|
+
const registerCli = readFileSync(registerCliSource, "utf8");
|
|
100
|
+
|
|
101
|
+
assert.match(pluginContext, /readJson5ConfigFile\(GLOBAL_CONFIG_PATH\)/);
|
|
102
|
+
assert.match(registerCli, /readJson5ConfigFile\(openclawPath\)/);
|
|
103
|
+
});
|
|
104
|
+
});
|