Skip to content

Commit 8fb4bb6

Browse files
authored
feat(openclaw-plugin): v1.0.8 (#1435)
## Description Please include a summary of the change, the problem it solves, the implementation approach, and relevant context. List any dependencies required for this change. Related Issue (Required): Fixes #issue_number ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactor (does not change functionality, e.g. code style improvements, linting) - [ ] Documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration - [ ] Unit Test - [ ] Test Script Or Test Steps (please provide) - [ ] Pipeline Automated API Test (please provide) ## Checklist - [ ] I have performed a self-review of my own code | 我已自行检查了自己的代码 - [ ] I have commented my code in hard-to-understand areas | 我已在难以理解的地方对代码进行了注释 - [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加测试以证明我的修复有效或功能正常 - [ ] I have created related documentation issue/PR in [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) (if applicable) | 我已在 [MemOS-Docs](https://github.com/MemTensor/MemOS-Docs) 中创建了相关的文档 issue/PR(如果适用) - [ ] I have linked the issue to this PR (if applicable) | 我已将 issue 链接到此 PR(如果适用) - [ ] I have mentioned the person who will review this PR | 我已提及将审查此 PR 的人 ## Reviewer Checklist - [ ] closes #xxxx (Replace xxxx with the GitHub issue number) - [ ] Made sure Checks passed - [ ] Tests have been provided
2 parents 06d73e7 + 0f4105d commit 8fb4bb6

File tree

26 files changed

+2478
-1656
lines changed

26 files changed

+2478
-1656
lines changed

.github/workflows/openclaw-plugin-publish.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ jobs:
100100
MEMOS_ARMS_ENV: ${{ secrets.MEMOS_ARMS_ENV }}
101101

102102
- name: Bump version
103-
run: npm version ${{ inputs.version }} --no-git-tag-version
103+
# Branch may already have this version in package.json (e.g. after a prior merge); npm errors without this flag.
104+
run: npm version ${{ inputs.version }} --no-git-tag-version --allow-same-version
104105

105106
- name: Publish to npm
106107
run: npm publish --access public --tag ${{ inputs.tag }}
@@ -113,6 +114,8 @@ jobs:
113114
git config user.name "github-actions[bot]"
114115
git config user.email "github-actions[bot]@users.noreply.github.com"
115116
git add apps/memos-local-openclaw/package.json
116-
git commit -m "release: openclaw-plugin v${{ inputs.version }}"
117+
if ! git diff --staged --quiet; then
118+
git commit -m "release: openclaw-plugin v${{ inputs.version }}"
119+
fi
117120
git tag "openclaw-plugin-v${{ inputs.version }}"
118121
git push origin HEAD --tags

apps/memos-local-openclaw/index.ts

Lines changed: 391 additions & 334 deletions
Large diffs are not rendered by default.

apps/memos-local-openclaw/install.ps1

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ if (!config.plugins.allow.includes(pluginId)) {
192192
// Clean up stale contextEngine slot from previous versions
193193
if (config.plugins.slots && config.plugins.slots.contextEngine) {
194194
delete config.plugins.slots.contextEngine;
195+
if (Object.keys(config.plugins.slots).length === 0) {
196+
delete config.plugins.slots;
197+
}
195198
}
196199
197200
// Register plugin in memory slot
@@ -209,25 +212,27 @@ if (!config.plugins.entries[pluginId] || typeof config.plugins.entries[pluginId]
209212
}
210213
config.plugins.entries[pluginId].enabled = true;
211214
212-
// Register plugin in installs so gateway auto-loads it on restart
215+
// Register plugin in installs so gateway auto-loads it on restart (pinned spec when package.json exists)
213216
if (!config.plugins.installs || typeof config.plugins.installs !== "object") {
214217
config.plugins.installs = {};
215218
}
219+
let resolvedName = "";
220+
let resolvedVersion = "";
216221
const pkgJsonPath = path.join(installPath, "package.json");
217-
let resolvedName, resolvedVersion;
218222
if (fs.existsSync(pkgJsonPath)) {
219223
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
220224
resolvedName = pkg.name;
221225
resolvedVersion = pkg.version;
222226
}
227+
const pinnedSpec = resolvedName && resolvedVersion ? `${resolvedName}@${resolvedVersion}` : spec;
223228
config.plugins.installs[pluginId] = {
224229
source: "npm",
225-
spec,
230+
spec: pinnedSpec,
226231
installPath,
227232
...(resolvedVersion ? { version: resolvedVersion } : {}),
228233
...(resolvedName ? { resolvedName } : {}),
229234
...(resolvedVersion ? { resolvedVersion } : {}),
230-
...(resolvedName && resolvedVersion ? { resolvedSpec: `${resolvedName}@${resolvedVersion}` } : {}),
235+
...(resolvedName && resolvedVersion ? { resolvedSpec: pinnedSpec } : {}),
231236
installedAt: new Date().toISOString(),
232237
};
233238
@@ -359,6 +364,38 @@ if (-not (Test-Path $ExtensionDir)) {
359364
exit 1
360365
}
361366

367+
$NodeModulesDir = Join-Path $ExtensionDir "node_modules"
368+
if (-not (Test-Path $NodeModulesDir)) {
369+
Write-Warn "node_modules missing after install (postinstall may have cleaned it). Reinstalling..."
370+
Push-Location $ExtensionDir
371+
try {
372+
& npm install --omit=dev --no-fund --no-audit --ignore-scripts --loglevel=error 2>&1
373+
}
374+
finally {
375+
Pop-Location
376+
}
377+
}
378+
379+
$SqliteDir = Join-Path $ExtensionDir "node_modules\better-sqlite3"
380+
if (-not (Test-Path $SqliteDir)) {
381+
Write-Warn "better-sqlite3 missing, attempting rebuild..."
382+
Push-Location $ExtensionDir
383+
try {
384+
& npm rebuild better-sqlite3 2>&1
385+
}
386+
catch {
387+
Write-Warn "better-sqlite3 rebuild returned an error. Continuing..."
388+
}
389+
finally {
390+
Pop-Location
391+
}
392+
}
393+
394+
if (-not (Test-Path $NodeModulesDir)) {
395+
Write-Err "Dependencies installation failed. Run manually: cd $ExtensionDir && npm install --omit=dev"
396+
exit 1
397+
}
398+
362399
Update-OpenClawConfig -OpenClawHome $OpenClawHome -ConfigPath $OpenClawConfigPath -PluginId $PluginId -InstallPath $ExtensionDir -Spec $PackageSpec
363400

364401
Write-Info "Installing OpenClaw Gateway service..."
@@ -368,9 +405,18 @@ if (-not $?) { Write-Warn "Gateway service install returned a warning; continuin
368405
Write-Success "Starting OpenClaw Gateway service..."
369406
& npx openclaw gateway start 2>&1
370407

408+
Write-Info "Starting Memory Viewer, 正在启动记忆面板..."
409+
for ($i = 1; $i -le 5; $i++) {
410+
$listening = Get-NetTCPConnection -LocalPort 18799 -State Listen -ErrorAction SilentlyContinue
411+
if ($listening) { break }
412+
Write-Host "." -NoNewline
413+
Start-Sleep -Seconds 1
414+
}
415+
Write-Host ""
416+
371417
Write-Host ""
372418
Write-Success "=========================================="
373-
Write-Success " Installation complete!"
419+
Write-Success " Installation complete! 安装完成!"
374420
Write-Success "=========================================="
375421
Write-Host ""
376422
Write-Info " OpenClaw Web UI: http://localhost:$Port"

apps/memos-local-openclaw/install.sh

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ if (!config.plugins.allow.includes(pluginId)) {
252252
// Clean up stale contextEngine slot from previous versions
253253
if (config.plugins.slots && config.plugins.slots.contextEngine) {
254254
delete config.plugins.slots.contextEngine;
255+
if (Object.keys(config.plugins.slots).length === 0) {
256+
delete config.plugins.slots;
257+
}
255258
}
256259
257260
// Register plugin in memory slot
@@ -269,25 +272,27 @@ if (!config.plugins.entries[pluginId] || typeof config.plugins.entries[pluginId]
269272
}
270273
config.plugins.entries[pluginId].enabled = true;
271274
272-
// Register plugin in installs so gateway auto-loads it on restart
275+
// Register plugin in installs so gateway auto-loads it on restart (pinned spec when package.json exists)
273276
if (!config.plugins.installs || typeof config.plugins.installs !== 'object') {
274277
config.plugins.installs = {};
275278
}
279+
let resolvedName = '';
280+
let resolvedVersion = '';
276281
const pkgJsonPath = path.join(installPath, 'package.json');
277-
let resolvedName, resolvedVersion;
278282
if (fs.existsSync(pkgJsonPath)) {
279283
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
280284
resolvedName = pkg.name;
281285
resolvedVersion = pkg.version;
282286
}
287+
const pinnedSpec = resolvedName && resolvedVersion ? `${resolvedName}@${resolvedVersion}` : spec;
283288
config.plugins.installs[pluginId] = {
284289
source: 'npm',
285-
spec,
290+
spec: pinnedSpec,
286291
installPath,
287292
...(resolvedVersion ? { version: resolvedVersion } : {}),
288293
...(resolvedName ? { resolvedName } : {}),
289294
...(resolvedVersion ? { resolvedVersion } : {}),
290-
...(resolvedName && resolvedVersion ? { resolvedSpec: `${resolvedName}@${resolvedVersion}` } : {}),
295+
...(resolvedName && resolvedVersion ? { resolvedSpec: pinnedSpec } : {}),
291296
installedAt: new Date().toISOString(),
292297
};
293298
@@ -358,6 +363,28 @@ if [[ ! -d "$EXTENSION_DIR" ]]; then
358363
exit 1
359364
fi
360365

366+
if [[ ! -d "${EXTENSION_DIR}/node_modules" ]]; then
367+
warn "node_modules missing after install (postinstall may have cleaned it), 安装后 node_modules 缺失,正在重新安装..."
368+
(
369+
cd "${EXTENSION_DIR}"
370+
npm install --omit=dev --no-fund --no-audit --ignore-scripts --loglevel=error 2>&1
371+
)
372+
fi
373+
374+
if [[ ! -d "${EXTENSION_DIR}/node_modules/better-sqlite3" ]]; then
375+
warn "better-sqlite3 missing, attempting rebuild, better-sqlite3 缺失,尝试重新编译..."
376+
(
377+
cd "${EXTENSION_DIR}"
378+
npm rebuild better-sqlite3 2>&1 || true
379+
)
380+
fi
381+
382+
if [[ ! -d "${EXTENSION_DIR}/node_modules" ]]; then
383+
error "Dependencies installation failed. Run manually: cd ${EXTENSION_DIR} && npm install --omit=dev"
384+
error "依赖安装失败,请手动运行: cd ${EXTENSION_DIR} && npm install --omit=dev"
385+
exit 1
386+
fi
387+
361388
update_openclaw_config
362389

363390
info "Install OpenClaw Gateway service, 安装 OpenClaw Gateway 服务..."
@@ -366,6 +393,16 @@ npx openclaw gateway install --port "${PORT}" --force 2>&1 || true
366393
success "Start OpenClaw Gateway service, 启动 OpenClaw Gateway 服务..."
367394
npx openclaw gateway start 2>&1
368395

396+
info "Starting Memory Viewer, 正在启动记忆面板..."
397+
for i in 1 2 3 4 5; do
398+
if command -v lsof >/dev/null 2>&1 && lsof -i :18799 -t >/dev/null 2>&1; then
399+
break
400+
fi
401+
printf "."
402+
sleep 1
403+
done
404+
echo ""
405+
369406
echo ""
370407
success "=========================================="
371408
success " Installation complete! 安装完成!"

apps/memos-local-openclaw/openclaw.plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "MemOS Local Memory",
44
"description": "Full-write local conversation memory with hybrid search (RRF + MMR + recency), task summarization, skill evolution, and team sharing (Hub-Client). Provides memory_search, memory_get, task_summary, skill_search, task_share, network_skill_pull, network_team_info, memory_viewer for layered retrieval and team collaboration.",
55
"kind": "memory",
6-
"version": "1.0.6-beta.11",
6+
"version": "1.0.8",
77
"skills": [
88
"skill/memos-memory-guide"
99
],

apps/memos-local-openclaw/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@memtensor/memos-local-openclaw-plugin",
3-
"version": "1.0.7-beta.1",
3+
"version": "1.0.8",
44
"description": "MemOS Local memory plugin for OpenClaw — full-write, hybrid-recall, progressive retrieval",
55
"type": "module",
66
"main": "index.ts",
@@ -45,12 +45,13 @@
4545
],
4646
"license": "MIT",
4747
"engines": {
48-
"node": ">=22.0.0"
48+
"node": ">=18.0.0 <25.0.0"
4949
},
5050
"dependencies": {
5151
"@huggingface/transformers": "^3.8.0",
5252
"@sinclair/typebox": "^0.34.48",
53-
"better-sqlite3": "^12.6.2",
53+
"better-sqlite3": "^12.6.3",
54+
"posthog-node": "^5.28.0",
5455
"puppeteer": "^24.38.0",
5556
"semver": "^7.7.4",
5657
"uuid": "^10.0.0"

apps/memos-local-openclaw/scripts/postinstall.cjs

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,23 @@ function normalizePathForMatch(p) {
3030
return path.resolve(p).replace(/^\\\\\?\\/, "").replace(/\\/g, "/").toLowerCase();
3131
}
3232

33+
const nodeVersion = process.version;
34+
const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
35+
3336
console.log(`
3437
${CYAN}${BOLD}┌──────────────────────────────────────────────────┐
3538
│ MemOS Local Memory — postinstall setup │
3639
└──────────────────────────────────────────────────┘${RESET}
3740
`);
3841

3942
log(`Plugin dir: ${DIM}${pluginDir}${RESET}`);
40-
log(`Node: ${process.version} Platform: ${process.platform}-${process.arch}`);
43+
log(`Node: ${GREEN}${nodeVersion}${RESET} Platform: ${process.platform}-${process.arch}`);
44+
45+
if (nodeMajor >= 25) {
46+
warn(`Node.js ${nodeVersion} detected. This version may have compatibility issues with native modules.`);
47+
log(`Recommended: Use Node.js LTS (v20 or v22) for best compatibility.`);
48+
log(`You can use nvm to switch versions: ${CYAN}nvm use 22${RESET}`);
49+
}
4150

4251
/* ═══════════════════════════════════════════════════════════
4352
* Pre-phase: Clean stale build artifacts on upgrade
@@ -61,21 +70,30 @@ function cleanStaleArtifacts() {
6170
installedVer = pkg.version || "unknown";
6271
} catch { /* ignore */ }
6372

73+
const nodeMajor = process.versions.node.split(".")[0];
74+
const currentFingerprint = `${installedVer}+node${nodeMajor}`;
75+
6476
const markerPath = path.join(pluginDir, ".installed-version");
65-
let prevVer = "";
66-
try { prevVer = fs.readFileSync(markerPath, "utf-8").trim(); } catch { /* first install */ }
77+
let prevFingerprint = "";
78+
try { prevFingerprint = fs.readFileSync(markerPath, "utf-8").trim(); } catch { /* first install */ }
79+
80+
const writeMarker = () => {
81+
try { fs.writeFileSync(markerPath, currentFingerprint + "\n", "utf-8"); } catch { /* ignore */ }
82+
};
6783

68-
if (prevVer === installedVer) {
69-
log(`Version unchanged (${installedVer}), skipping artifact cleanup.`);
84+
if (prevFingerprint === currentFingerprint) {
85+
log(`Version unchanged (${currentFingerprint}), skipping artifact cleanup.`);
7086
return;
7187
}
7288

73-
if (prevVer) {
74-
log(`Upgrade detected: ${DIM}${prevVer}${RESET}${GREEN}${installedVer}${RESET}`);
75-
} else {
76-
log(`Fresh install: ${GREEN}${installedVer}${RESET}`);
89+
if (!prevFingerprint) {
90+
log(`Fresh install: ${GREEN}${currentFingerprint}${RESET}`);
91+
writeMarker();
92+
return;
7793
}
7894

95+
log(`Environment changed: ${DIM}${prevFingerprint}${RESET}${GREEN}${currentFingerprint}${RESET}`);
96+
7997
const dirsToClean = ["dist", "node_modules"];
8098
let cleaned = 0;
8199
for (const dir of dirsToClean) {
@@ -99,7 +117,7 @@ function cleanStaleArtifacts() {
99117
}
100118
}
101119

102-
try { fs.writeFileSync(markerPath, installedVer + "\n", "utf-8"); } catch { /* ignore */ }
120+
writeMarker();
103121

104122
if (cleaned > 0) {
105123
ok(`Cleaned ${cleaned} stale artifact(s). Fresh install will follow.`);
@@ -418,23 +436,39 @@ if (sqliteBindingsExist()) {
418436
else { fail(`Rebuild completed but bindings still missing (${elapsed}s).`); fail(`Looked in: ${sqliteModulePath}/build/`); }
419437
console.log(`
420438
${YELLOW}${BOLD} ╔══════════════════════════════════════════════════════════════╗
421-
║ ✖ better-sqlite3 native module build failed ║
439+
║ ✖ better-sqlite3 native module build failed
422440
╠══════════════════════════════════════════════════════════════╣${RESET}
423-
${YELLOW}${RESET} ${YELLOW}${RESET}
424-
${YELLOW}${RESET} This plugin requires C/C++ build tools to compile ${YELLOW}${RESET}
425-
${YELLOW}${RESET} the SQLite native module on first install. ${YELLOW}${RESET}
426-
${YELLOW}${RESET} ${YELLOW}${RESET}
427-
${YELLOW}${RESET} ${BOLD}Install build tools:${RESET} ${YELLOW}${RESET}
428-
${YELLOW}${RESET} ${YELLOW}${RESET}
429-
${YELLOW}${RESET} ${CYAN}macOS:${RESET} xcode-select --install ${YELLOW}${RESET}
430-
${YELLOW}${RESET} ${CYAN}Ubuntu:${RESET} sudo apt install build-essential python3 ${YELLOW}${RESET}
431-
${YELLOW}${RESET} ${CYAN}Windows:${RESET} npm install -g windows-build-tools ${YELLOW}${RESET}
432-
${YELLOW}${RESET} ${YELLOW}${RESET}
433-
${YELLOW}${RESET} ${BOLD}Then retry:${RESET} ${YELLOW}${RESET}
441+
${YELLOW}${RESET} ${YELLOW}${RESET}
442+
${YELLOW}${RESET} This plugin requires C/C++ build tools to compile ${YELLOW}${RESET}
443+
${YELLOW}${RESET} the SQLite native module on first install. ${YELLOW}${RESET}
444+
${YELLOW}${RESET} ${YELLOW}${RESET}
445+
${YELLOW}${RESET} ${BOLD}Install build tools:${RESET} ${YELLOW}${RESET}
446+
${YELLOW}${RESET} ${YELLOW}${RESET}
447+
${YELLOW}${RESET} ${CYAN}macOS:${RESET} xcode-select --install ${YELLOW}${RESET}
448+
${YELLOW}${RESET} ${CYAN}Ubuntu:${RESET} sudo apt install build-essential python3 ${YELLOW}${RESET}
449+
${YELLOW}${RESET} ${CYAN}Windows:${RESET} npm install -g windows-build-tools ${YELLOW}${RESET}
450+
${YELLOW}${RESET} ${YELLOW}${RESET}`);
451+
452+
if (nodeMajor >= 25) {
453+
console.log(`${YELLOW}${RESET} ${BOLD}${RED}Node.js v25+ compatibility issue detected:${RESET} ${YELLOW}${RESET}
454+
${YELLOW}${RESET} ${YELLOW}${RESET}
455+
${YELLOW}${RESET} better-sqlite3 may not have prebuilt binaries for Node 25. ${YELLOW}${RESET}
456+
${YELLOW}${RESET} ${BOLD}Recommended solutions:${RESET} ${YELLOW}${RESET}
457+
${YELLOW}${RESET} ${YELLOW}${RESET}
458+
${YELLOW}${RESET} 1. Use Node.js LTS (v20 or v22): ${YELLOW}${RESET}
459+
${YELLOW}${RESET} ${GREEN}nvm install 22 && nvm use 22${RESET} ${YELLOW}${RESET}
460+
${YELLOW}${RESET} ${YELLOW}${RESET}
461+
${YELLOW}${RESET} 2. Or use MemOS Cloud version instead: ${YELLOW}${RESET}
462+
${YELLOW}${RESET} ${CYAN}https://github.com/MemTensor/MemOS/tree/main/apps/memos-cloud${RESET}
463+
${YELLOW}${RESET} ${YELLOW}${RESET}`);
464+
}
465+
466+
console.log(`${YELLOW}${RESET} ${YELLOW}${RESET}
467+
${YELLOW}${RESET} ${BOLD}Then retry:${RESET} ${YELLOW}${RESET}
434468
${YELLOW}${RESET} ${GREEN}cd ${pluginDir}${RESET}
435-
${YELLOW}${RESET} ${GREEN}npm rebuild better-sqlite3${RESET} ${YELLOW}${RESET}
436-
${YELLOW}${RESET} ${GREEN}openclaw gateway stop && openclaw gateway start${RESET} ${YELLOW}${RESET}
437-
${YELLOW}${RESET} ${YELLOW}${RESET}
469+
${YELLOW}${RESET} ${GREEN}npm rebuild better-sqlite3${RESET} ${YELLOW}${RESET}
470+
${YELLOW}${RESET} ${GREEN}openclaw gateway stop && openclaw gateway start${RESET} ${YELLOW}${RESET}
471+
${YELLOW}${RESET} ${YELLOW}${RESET}
438472
${YELLOW}${BOLD} ╚══════════════════════════════════════════════════════════════╝${RESET}
439473
`);
440474
}

apps/memos-local-openclaw/src/client/hub.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,26 @@ function getClientIp(): string {
177177
return "";
178178
}
179179

180+
const HUB_FETCH_TIMEOUT_MS = 25_000;
181+
180182
export async function hubRequestJson(
181183
hubUrl: string,
182184
userToken: string,
183185
route: string,
184186
init: RequestInit = {},
185187
): Promise<unknown> {
186188
const clientIp = getClientIp();
189+
const timeoutSignal =
190+
typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function"
191+
? AbortSignal.timeout(HUB_FETCH_TIMEOUT_MS)
192+
: undefined;
193+
const mergedSignal =
194+
timeoutSignal && init.signal
195+
? AbortSignal.any([timeoutSignal, init.signal])
196+
: (timeoutSignal ?? init.signal);
187197
const res = await fetch(`${normalizeHubUrl(hubUrl)}${route}`, {
188198
...init,
199+
...(mergedSignal ? { signal: mergedSignal } : {}),
189200
headers: {
190201
authorization: `Bearer ${userToken}`,
191202
...(clientIp ? { "x-client-ip": clientIp } : {}),

0 commit comments

Comments
 (0)