docs: update memory tool list, CLI usage, and config file reading logic (#4861)
Co-authored-by: Livia Ellen <liviaellen@msn.com>
This commit is contained in:
+124
-21
@@ -19,6 +19,7 @@ export const OPENCLAW_CONFIG_FILE = join(OPENCLAW_CONFIG_DIR, "openclaw.json");
|
||||
export const DEFAULT_BASE_URL = "https://api.mem0.ai";
|
||||
|
||||
const PLUGIN_ID = "openclaw-mem0";
|
||||
const NPM_PACKAGE = "@mem0/openclaw-mem0";
|
||||
|
||||
// ============================================================================
|
||||
// Types
|
||||
@@ -41,16 +42,38 @@ export interface PluginAuthConfig {
|
||||
// OpenClaw config read/write
|
||||
// ============================================================================
|
||||
|
||||
/** Read the full ~/.openclaw/openclaw.json */
|
||||
/**
|
||||
* Read the full ~/.openclaw/openclaw.json.
|
||||
*
|
||||
* Returns {} only when the file doesn't exist (first-time setup).
|
||||
* Throws on parse errors to prevent writes from destroying existing config.
|
||||
*/
|
||||
function readFullConfig(): Record<string, unknown> {
|
||||
if (exists(OPENCLAW_CONFIG_FILE)) {
|
||||
try {
|
||||
return JSON.parse(readText(OPENCLAW_CONFIG_FILE));
|
||||
} catch {
|
||||
/* ignore parse errors */
|
||||
if (!exists(OPENCLAW_CONFIG_FILE)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const text = readText(OPENCLAW_CONFIG_FILE);
|
||||
|
||||
// Handle empty or whitespace-only files as first-time setup
|
||||
if (!text.trim()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||
throw new Error("Config is not a JSON object");
|
||||
}
|
||||
return parsed;
|
||||
} catch (err) {
|
||||
// Fail closed: throw so writes don't proceed with empty config
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(
|
||||
`[openclaw-mem0] Failed to parse ${OPENCLAW_CONFIG_FILE}: ${msg}\n` +
|
||||
`Fix the JSON syntax error manually before running config commands.`,
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Write the full ~/.openclaw/openclaw.json (preserves all non-plugin config) */
|
||||
@@ -87,15 +110,7 @@ export function readPluginAuth(): PluginAuthConfig {
|
||||
export function writePluginAuth(auth: PluginAuthConfig): void {
|
||||
const full = readFullConfig() as any;
|
||||
|
||||
// Ensure nested structure exists
|
||||
if (!full.plugins) full.plugins = {};
|
||||
if (!full.plugins.entries) full.plugins.entries = {};
|
||||
if (!full.plugins.entries[PLUGIN_ID]) {
|
||||
full.plugins.entries[PLUGIN_ID] = { enabled: true, config: {} };
|
||||
}
|
||||
if (!full.plugins.entries[PLUGIN_ID].config) {
|
||||
full.plugins.entries[PLUGIN_ID].config = {};
|
||||
}
|
||||
ensurePluginStructure(full);
|
||||
|
||||
const cfg = full.plugins.entries[PLUGIN_ID].config;
|
||||
|
||||
@@ -107,12 +122,81 @@ export function writePluginAuth(auth: PluginAuthConfig): void {
|
||||
writeFullConfig(full);
|
||||
}
|
||||
|
||||
export function writePluginConfigField(
|
||||
path: string[],
|
||||
value: unknown,
|
||||
): void {
|
||||
const full = readFullConfig() as any;
|
||||
/**
|
||||
* Ensure the plugin has a valid install record and is in plugins.allow.
|
||||
*
|
||||
* OpenClaw's `plugins update` command requires a `plugins.installs.<id>`
|
||||
* record with `source: "npm"` and `spec` to know how to update. Without
|
||||
* this, `openclaw plugins update` prints "No install record" and skips.
|
||||
*
|
||||
* Similarly, if `plugins.allow` exists as an array, the plugin ID must
|
||||
* be in it or OpenClaw treats the plugin as untrusted.
|
||||
*
|
||||
* This is safe to call multiple times — it only writes missing fields.
|
||||
*/
|
||||
export function ensureInstallRecord(): void {
|
||||
try {
|
||||
const full = readFullConfig() as any;
|
||||
|
||||
const entry = full?.plugins?.entries?.[PLUGIN_ID];
|
||||
const record = full?.plugins?.installs?.[PLUGIN_ID];
|
||||
const allow = full?.plugins?.allow;
|
||||
if (
|
||||
entry?.enabled === true &&
|
||||
record?.source &&
|
||||
record?.spec &&
|
||||
Array.isArray(allow) &&
|
||||
allow.includes(PLUGIN_ID)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensurePluginStructure(full);
|
||||
|
||||
let changed = false;
|
||||
|
||||
// Ensure install record exists for `openclaw plugins update` support
|
||||
if (!full.plugins.installs) full.plugins.installs = {};
|
||||
if (!full.plugins.installs[PLUGIN_ID]) {
|
||||
full.plugins.installs[PLUGIN_ID] = {
|
||||
source: "npm",
|
||||
spec: `${NPM_PACKAGE}@latest`,
|
||||
resolvedName: NPM_PACKAGE,
|
||||
installedAt: new Date().toISOString(),
|
||||
};
|
||||
changed = true;
|
||||
} else {
|
||||
const record = full.plugins.installs[PLUGIN_ID];
|
||||
if (!record.source) {
|
||||
record.source = "npm";
|
||||
changed = true;
|
||||
}
|
||||
if (!record.spec) {
|
||||
record.spec = `${NPM_PACKAGE}@latest`;
|
||||
changed = true;
|
||||
}
|
||||
if (!record.resolvedName) {
|
||||
record.resolvedName = NPM_PACKAGE;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(full.plugins.allow)) {
|
||||
full.plugins.allow = [PLUGIN_ID];
|
||||
changed = true;
|
||||
} else if (!full.plugins.allow.includes(PLUGIN_ID)) {
|
||||
full.plugins.allow.push(PLUGIN_ID);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) writeFullConfig(full);
|
||||
} catch {
|
||||
// Best-effort — don't break plugin loading if config is unreadable
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure the nested plugin entry structure exists in the config object. */
|
||||
function ensurePluginStructure(full: any): void {
|
||||
if (!full.plugins) full.plugins = {};
|
||||
if (!full.plugins.entries) full.plugins.entries = {};
|
||||
if (!full.plugins.entries[PLUGIN_ID]) {
|
||||
@@ -121,6 +205,15 @@ export function writePluginConfigField(
|
||||
if (!full.plugins.entries[PLUGIN_ID].config) {
|
||||
full.plugins.entries[PLUGIN_ID].config = {};
|
||||
}
|
||||
}
|
||||
|
||||
export function writePluginConfigField(
|
||||
path: string[],
|
||||
value: unknown,
|
||||
): void {
|
||||
const full = readFullConfig() as any;
|
||||
|
||||
ensurePluginStructure(full);
|
||||
|
||||
let target = full.plugins.entries[PLUGIN_ID].config;
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
@@ -139,3 +232,13 @@ export function getBaseUrl(): string {
|
||||
const auth = readPluginAuth();
|
||||
return auth.baseUrl || DEFAULT_BASE_URL;
|
||||
}
|
||||
|
||||
/** Remove anonymousTelemetryId from config (after PostHog aliasing) */
|
||||
export function clearAnonymousTelemetryId(): void {
|
||||
const full = readFullConfig() as any;
|
||||
const cfg = full?.plugins?.entries?.[PLUGIN_ID]?.config;
|
||||
if (cfg && "anonymousTelemetryId" in cfg) {
|
||||
delete cfg.anonymousTelemetryId;
|
||||
writeFullConfig(full);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user