memorylake-openclaw 0.0.9 → 0.0.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorylake-openclaw",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "description": "MemoryLake memory backend for OpenClaw",
6
6
  "license": "MIT",
@@ -0,0 +1,455 @@
1
+ # MemoryLake OpenClaw Plugin Installer for Windows
2
+ # Usage (with env vars): $env:MEMORYLAKE_API_KEY="sk-..."; $env:MEMORYLAKE_PROJECT_ID="proj-..."; iwr -useb https://app.memorylake.ai/memorylake-openclaw/install.ps1 | iex
3
+ # Or (with params): & ([scriptblock]::Create((iwr -useb https://app.memorylake.ai/memorylake-openclaw/install.ps1))) -ApiKey <key> -ProjectId <id>
4
+
5
+ param(
6
+ [string]$ApiKey = $env:MEMORYLAKE_API_KEY,
7
+ [string]$ProjectId = $env:MEMORYLAKE_PROJECT_ID,
8
+ [switch]$Help
9
+ )
10
+
11
+ $ErrorActionPreference = "Stop"
12
+ $script:HadNpmRegistry = -not [string]::IsNullOrWhiteSpace($env:NPM_REGISTRY)
13
+
14
+ # Set UTF-8 encoding for Chinese characters in output
15
+ try {
16
+ [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
17
+ [Console]::InputEncoding = [System.Text.Encoding]::UTF8
18
+ $OutputEncoding = [System.Text.Encoding]::UTF8
19
+ chcp 65001 | Out-Null
20
+ } catch {
21
+ Write-Warning "UTF-8 encoding setup failed: $_"
22
+ }
23
+
24
+ # Colors (prefixed to avoid $Error etc.)
25
+ $ColorAccent = "`e[38;2;255;77;77m" # coral-bright
26
+ $ColorSuccess = "`e[38;2;0;229;204m" # cyan-bright
27
+ $ColorWarn = "`e[38;2;255;176;32m" # amber
28
+ $ColorError = "`e[38;2;230;57;70m" # coral-mid
29
+ $ColorMuted = "`e[38;2;90;100;128m" # text-muted
30
+ $ColorReset = "`e[0m" # No Color
31
+
32
+ $OPENCLAW_CONFIG = "$env:USERPROFILE\.openclaw\openclaw.json"
33
+ $QCLAW_CONFIG = "$env:USERPROFILE\.qclaw\openclaw.json"
34
+
35
+ function Write-Message {
36
+ param([string]$Message, [string]$Level = "info")
37
+ $msg = switch ($Level) {
38
+ "success" { "$ColorSuccess✓$ColorReset $Message" }
39
+ "warn" { "$ColorWarn!$ColorReset $Message" }
40
+ "error" { "$ColorError✗$ColorReset $Message" }
41
+ default { "$ColorMuted·$ColorReset $Message" }
42
+ }
43
+ Write-Host $msg
44
+ }
45
+
46
+ function Show-Usage {
47
+ Write-Host @"
48
+ MemoryLake OpenClaw Plugin Installer (Windows)
49
+
50
+ Usage:
51
+ `$env:MEMORYLAKE_API_KEY="sk-..."; `$env:MEMORYLAKE_PROJECT_ID="proj-..."; iwr -useb https://app.memorylake.ai/memorylake-openclaw/install.ps1 | iex
52
+ Or: & ([scriptblock]::Create((iwr -useb https://app.memorylake.ai/memorylake-openclaw/install.ps1))) -ApiKey <key> -ProjectId <id>
53
+
54
+ Parameters:
55
+ -ApiKey <key> MemoryLake API key (required)
56
+ -ProjectId <id> MemoryLake project ID (required)
57
+ -Help Show this help
58
+
59
+ Environment variables:
60
+ MEMORYLAKE_API_KEY API key (alternative to -ApiKey)
61
+ MEMORYLAKE_PROJECT_ID Project ID (alternative to -ProjectId)
62
+ NPM_REGISTRY Override npm registry (e.g. https://registry.npmmirror.com for China)
63
+
64
+ Get apiKey and projectId from https://app.memorylake.ai
65
+ "@
66
+ }
67
+
68
+ function Test-Params {
69
+ if ([string]::IsNullOrWhiteSpace($ApiKey) -or [string]::IsNullOrWhiteSpace($ProjectId)) {
70
+ Write-Message "Missing required parameters: apiKey and projectId" -Level error
71
+ Write-Host ""
72
+ Write-Host "Please provide apiKey and projectId. Get them from https://app.memorylake.ai"
73
+ Write-Host ""
74
+ Write-Host "Usage: -ApiKey <key> -ProjectId <id>"
75
+ Write-Host " or: `$env:MEMORYLAKE_API_KEY=<key>; `$env:MEMORYLAKE_PROJECT_ID=<id>; .\install-memorylake-openclaw.ps1"
76
+ exit 1
77
+ }
78
+ }
79
+
80
+ function Get-QClawScriptPath {
81
+ try {
82
+ # $proc = Get-Process qclaw -ErrorAction Stop
83
+ $path = (Get-Process qclaw).path[0] | Where-Object {$_}
84
+ $dir = Split-Path -Parent $path
85
+ $scriptPath = Join-Path $dir "resources\openclaw\config\skills\qclaw-openclaw\scripts\openclaw-win.cmd"
86
+ if (Test-Path $scriptPath) { return $scriptPath }
87
+ } catch {
88
+ Write-Message "Get-QClawScriptPath: $_" -Level warn
89
+ }
90
+ return $null
91
+ }
92
+
93
+ function Refresh-Path {
94
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
95
+ }
96
+
97
+ function Test-NodeInstalled {
98
+ $nodeVersion = $null
99
+ try {
100
+ $nodeVersion = & node -v 2>&1
101
+ } catch {
102
+ return $false
103
+ }
104
+ if ($nodeVersion -and $nodeVersion -match '^v(\d+)') {
105
+ $majorVersion = [int]$Matches[1]
106
+ return $majorVersion -ge 22
107
+ }
108
+ return $false
109
+ }
110
+
111
+ function Install-NodeJS {
112
+ Write-Message "Installing Node.js..." -Level info
113
+ if (Get-Command winget -ErrorAction SilentlyContinue) {
114
+ Write-Message "Using winget..." -Level info
115
+ & winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
116
+ if ($LASTEXITCODE -eq 0) {
117
+ Refresh-Path
118
+ Write-Message "Node.js installed via winget" -Level success
119
+ return $true
120
+ }
121
+ Write-Message "winget install failed (exit code: $LASTEXITCODE), trying next method..." -Level warn
122
+ }
123
+ if (Get-Command choco -ErrorAction SilentlyContinue) {
124
+ Write-Message "Using Chocolatey..." -Level info
125
+ & choco install nodejs-lts -y
126
+ if ($LASTEXITCODE -eq 0) {
127
+ Refresh-Path
128
+ Write-Message "Node.js installed via Chocolatey" -Level success
129
+ return $true
130
+ }
131
+ Write-Message "choco install failed (exit code: $LASTEXITCODE), trying next method..." -Level warn
132
+ }
133
+ if (Get-Command scoop -ErrorAction SilentlyContinue) {
134
+ Write-Message "Using Scoop..." -Level info
135
+ & scoop install nodejs-lts
136
+ if ($LASTEXITCODE -eq 0) {
137
+ Refresh-Path
138
+ Write-Message "Node.js installed via Scoop" -Level success
139
+ return $true
140
+ }
141
+ Write-Message "scoop install failed (exit code: $LASTEXITCODE), trying next method..." -Level warn
142
+ }
143
+ # Fallback: download and install .msi
144
+ Write-Message "Falling back to .msi installer..." -Level info
145
+ $nodeVersion = "22.22.1"
146
+ $arch = if ([System.Environment]::Is64BitOperatingSystem) {
147
+ if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { "arm64" } else { "x64" }
148
+ } else { "x86" }
149
+ $msiName = "node-v${nodeVersion}-${arch}.msi"
150
+
151
+ if ($script:ChinaNetwork) {
152
+ $msiUrl = "https://registry.npmmirror.com/-/binary/node/v${nodeVersion}/${msiName}"
153
+ Write-Message "Downloading Node.js from npmmirror..." -Level info
154
+ } else {
155
+ $msiUrl = "https://nodejs.org/dist/v${nodeVersion}/${msiName}"
156
+ Write-Message "Downloading Node.js from nodejs.org..." -Level info
157
+ }
158
+
159
+ $tmpDir = Join-Path $env:TEMP "node-install-$(Get-Random)"
160
+ New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
161
+ $msiPath = Join-Path $tmpDir $msiName
162
+
163
+ try {
164
+ Invoke-WebRequest -Uri $msiUrl -OutFile $msiPath -UseBasicParsing
165
+ Write-Message "Installing Node.js via .msi (may prompt for admin permission)..." -Level info
166
+ # Use /passive instead of /qn: shows progress bar and can auto-elevate via UAC.
167
+ # /qn silently fails without admin privileges.
168
+ $msiProc = Start-Process msiexec.exe -ArgumentList "/i", "`"$msiPath`"", "/passive", "/norestart" -Wait -PassThru
169
+ if ($msiProc.ExitCode -ne 0) {
170
+ Write-Message "msiexec failed (exit code: $($msiProc.ExitCode))" -Level error
171
+ Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
172
+ return $false
173
+ }
174
+
175
+ Refresh-Path
176
+
177
+ Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
178
+ if (Test-NodeInstalled) {
179
+ Write-Message "Node.js installed via .msi" -Level success
180
+ return $true
181
+ }
182
+ Write-Message "Node.js .msi install completed but node not found in PATH" -Level error
183
+ return $false
184
+ } catch {
185
+ Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
186
+ Write-Message "Node.js .msi download/install failed: $_" -Level error
187
+ Write-Host "Please install Node.js 22+ manually: https://nodejs.org/en/download/"
188
+ return $false
189
+ }
190
+ }
191
+
192
+ function Ensure-NodeForQClaw {
193
+ if (Test-NodeInstalled) {
194
+ $ver = & node -v 2>&1
195
+ Write-Message "Node.js $ver found" -Level success
196
+ return $true
197
+ }
198
+ Write-Message "Node.js not found (v22+ required for QClaw plugin install)" -Level warn
199
+ if (-not (Install-NodeJS)) {
200
+ exit 1
201
+ }
202
+ if (-not (Test-NodeInstalled)) {
203
+ Write-Message "Node.js installation failed or version too old" -Level error
204
+ exit 1
205
+ }
206
+
207
+ # Configure npm mirror for China network
208
+ if ($script:ChinaNetwork) {
209
+ & npm config set registry $env:NPM_REGISTRY
210
+ Write-Message "npm registry set to $($env:NPM_REGISTRY)" -Level info
211
+ }
212
+
213
+ return $true
214
+ }
215
+
216
+ function Get-ClawMode {
217
+ # Priority: OpenClaw first
218
+ if (Get-Command openclaw -ErrorAction SilentlyContinue) {
219
+ $script:ClawMode = "openclaw"
220
+ Write-Message "Detected OpenClaw" -Level success
221
+ return $true
222
+ }
223
+
224
+ $qclawScript = Get-QClawScriptPath
225
+ if ($qclawScript) {
226
+ $script:ClawMode = "qclaw"
227
+ $script:QClawScript = $qclawScript
228
+ Write-Message "Detected QClaw" -Level success
229
+ return $true
230
+ }
231
+
232
+ Write-Message "OpenClaw or QClaw not found." -Level error
233
+ Write-Host "QClaw not installed or not running. If using QClaw, install and start it first."
234
+ Write-Host "Install OpenClaw: iwr -useb https://openclaw.ai/install.ps1 | iex"
235
+ exit 1
236
+ }
237
+
238
+ function Test-PluginInstalled {
239
+ if ($script:ClawMode -eq "openclaw") {
240
+ $out = openclaw plugins list
241
+ if ($LASTEXITCODE -ne 0) {
242
+ Write-Message "openclaw plugins list failed (exit code: $LASTEXITCODE)" -Level error
243
+ exit 1
244
+ }
245
+ return $out -match "memorylake-openclaw"
246
+ } else {
247
+ $out = & $script:QClawScript plugins list
248
+ if ($LASTEXITCODE -ne 0) {
249
+ Write-Message "QClaw plugins list failed (exit code: $LASTEXITCODE)" -Level error
250
+ exit 1
251
+ }
252
+ return $out -match "memorylake-openclaw"
253
+ }
254
+ }
255
+
256
+ function Test-NetworkChina {
257
+ $script:ChinaNetwork = $false
258
+
259
+ if (-not [string]::IsNullOrWhiteSpace($env:NPM_REGISTRY)) {
260
+ Write-Message "Using NPM_REGISTRY=$($env:NPM_REGISTRY)"
261
+ return
262
+ }
263
+
264
+ $timeoutSec = 5
265
+ $job = Start-Job -ScriptBlock {
266
+ try {
267
+ $r = Invoke-WebRequest -Uri "https://google.com/generate_204" -UseBasicParsing -TimeoutSec 3 -ErrorAction Stop
268
+ if ($r.StatusCode -eq 204) { "ok" } else { "fail" }
269
+ } catch { "fail" }
270
+ }
271
+ $completed = Wait-Job $job -Timeout $timeoutSec
272
+ if ($completed) {
273
+ $result = Receive-Job $job
274
+ Remove-Job $job -Force
275
+ if ($result -eq "ok") { return }
276
+ } else {
277
+ Stop-Job $job -ErrorAction SilentlyContinue
278
+ Remove-Job $job -Force -ErrorAction SilentlyContinue
279
+ }
280
+ $script:ChinaNetwork = $true
281
+ $env:NPM_REGISTRY = "https://registry.npmmirror.com"
282
+ Write-Message "China network detected, using npmmirror"
283
+ }
284
+
285
+ function Install-Plugin {
286
+ param([switch]$TryOnly)
287
+
288
+ if ($script:ClawMode -eq "openclaw") {
289
+ if (-not [string]::IsNullOrWhiteSpace($env:NPM_REGISTRY)) {
290
+ $env:NPM_CONFIG_REGISTRY = $env:NPM_REGISTRY
291
+ }
292
+ }
293
+
294
+ $prevEAP = $ErrorActionPreference
295
+ $ErrorActionPreference = "Continue"
296
+
297
+ if ($TryOnly) {
298
+ # stdout redirected to Out-Host (out of return pipeline), error returns false
299
+ if ($script:ClawMode -eq "openclaw") {
300
+ openclaw plugins install memorylake-openclaw | Out-Host
301
+ } else {
302
+ & $script:QClawScript plugins install memorylake-openclaw | Out-Host
303
+ }
304
+ $exitCode = $LASTEXITCODE
305
+ $ErrorActionPreference = $prevEAP
306
+ Write-Message "Install-Plugin exit code: $exitCode" -Level info
307
+ return $exitCode -eq 0
308
+ } else {
309
+ # stdout direct to console, error exits
310
+ if ($script:ClawMode -eq "openclaw") {
311
+ openclaw plugins install memorylake-openclaw
312
+ } else {
313
+ & $script:QClawScript plugins install memorylake-openclaw
314
+ }
315
+ $exitCode = $LASTEXITCODE
316
+ $ErrorActionPreference = $prevEAP
317
+ Write-Message "Install-Plugin exit code: $exitCode" -Level info
318
+ if ($exitCode -ne 0) {
319
+ exit 1
320
+ }
321
+ }
322
+ }
323
+
324
+ function Write-Config {
325
+ $configPath = if ($script:ClawMode -eq "openclaw") { $OPENCLAW_CONFIG } else { $QCLAW_CONFIG }
326
+ $configDir = Split-Path -Parent $configPath
327
+
328
+ if (!(Test-Path $configDir)) {
329
+ New-Item -ItemType Directory -Path $configDir -Force | Out-Null
330
+ }
331
+
332
+ $config = [PSCustomObject]@{}
333
+ if (Test-Path $configPath) {
334
+ try {
335
+ $config = Get-Content $configPath -Raw -ErrorAction Stop | ConvertFrom-Json
336
+ } catch {
337
+ Write-Message "Config parse failed: $_" -Level error
338
+ exit 1
339
+ }
340
+ }
341
+
342
+ if (-not $config.PSObject.Properties["plugins"]) {
343
+ $config | Add-Member -NotePropertyName "plugins" -NotePropertyValue ([PSCustomObject]@{}) -Force
344
+ }
345
+ if (-not $config.plugins.PSObject.Properties["entries"]) {
346
+ $config.plugins | Add-Member -NotePropertyName "entries" -NotePropertyValue ([PSCustomObject]@{}) -Force
347
+ }
348
+
349
+ $pluginEntry = [PSCustomObject]@{
350
+ enabled = $true
351
+ config = [PSCustomObject]@{
352
+ apiKey = $ApiKey
353
+ projectId = $ProjectId
354
+ }
355
+ }
356
+ $config.plugins.entries | Add-Member -NotePropertyName "memorylake-openclaw" -NotePropertyValue $pluginEntry -Force
357
+
358
+ # Merge tools config (QClaw only)
359
+ if ($script:ClawMode -eq "qclaw") {
360
+ if (-not $config.PSObject.Properties["tools"]) {
361
+ $config | Add-Member -NotePropertyName "tools" -NotePropertyValue ([PSCustomObject]@{}) -Force
362
+ }
363
+ $existing = $config.tools.alsoAllow
364
+ $arr = @()
365
+ if ($null -ne $existing) {
366
+ $arr = @($existing)
367
+ }
368
+ if ($arr -notcontains "advanced_web_search") {
369
+ $arr = $arr + "advanced_web_search"
370
+ }
371
+ $config.tools | Add-Member -NotePropertyName "alsoAllow" -NotePropertyValue $arr -Force
372
+
373
+ if (-not $config.tools.PSObject.Properties["web"]) {
374
+ $config.tools | Add-Member -NotePropertyName "web" -NotePropertyValue ([PSCustomObject]@{}) -Force
375
+ }
376
+ if (-not $config.tools.web.PSObject.Properties["search"]) {
377
+ $config.tools.web | Add-Member -NotePropertyName "search" -NotePropertyValue ([PSCustomObject]@{}) -Force
378
+ }
379
+ $config.tools.web.search | Add-Member -NotePropertyName "enabled" -NotePropertyValue $false -Force
380
+ }
381
+
382
+ $json = $config | ConvertTo-Json -Depth 10
383
+ $lines = $json -split "`r?`n"
384
+ $indentLevel = 0
385
+ $fixed = @()
386
+ foreach ($line in $lines) {
387
+ if ($line -match '[\}\]]') { $indentLevel-- }
388
+ $trimmed = $line.TrimStart().Replace(': ', ': ')
389
+ $fixed += (' ' * ($indentLevel * 2)) + $trimmed
390
+ if ($line -match '[\{\[]') { $indentLevel++ }
391
+ }
392
+ $json = $fixed -join "`n"
393
+ Set-Content $configPath -Value $json -Encoding UTF8
394
+ }
395
+
396
+ function Restart-Gateway {
397
+ if ($script:ClawMode -eq "openclaw") {
398
+ openclaw gateway restart
399
+ if ($LASTEXITCODE -ne 0) {
400
+ Write-Message "openclaw gateway restart failed (exit code: $LASTEXITCODE)" -Level error
401
+ exit 1
402
+ }
403
+ }
404
+ }
405
+
406
+ # Main
407
+ function Main {
408
+ if ($Help) {
409
+ Show-Usage
410
+ return
411
+ }
412
+
413
+ Test-Params
414
+ Get-ClawMode | Out-Null
415
+
416
+ if (Test-PluginInstalled) {
417
+ Write-Message "memorylake-openclaw is already installed."
418
+ exit 0
419
+ }
420
+
421
+ Test-NetworkChina
422
+
423
+ # Try install; if qclaw fails, install Node.js and retry
424
+ if ($script:ClawMode -eq "qclaw") {
425
+ if (-not (Install-Plugin -TryOnly)) {
426
+ Write-Message "Plugin install failed, attempting to install Node.js..." -Level warn
427
+ Ensure-NodeForQClaw | Out-Null
428
+ Write-Message "Retrying plugin installation..." -Level info
429
+ Install-Plugin
430
+ }
431
+ } else {
432
+ Install-Plugin
433
+ }
434
+ Write-Message "Plugin installed" -Level success
435
+
436
+ Write-Config
437
+ Write-Message "Config updated" -Level success
438
+
439
+ Restart-Gateway
440
+ if ($script:ClawMode -eq "openclaw") {
441
+ Write-Message "Gateway restarted" -Level success
442
+ }
443
+
444
+ Write-Message "memorylake-openclaw setup complete!" -Level success
445
+ }
446
+
447
+ try {
448
+ Main
449
+ } finally {
450
+ # Clean up env vars set during this script to avoid polluting the caller's shell session
451
+ if (-not $script:HadNpmRegistry) {
452
+ Remove-Item Env:\NPM_REGISTRY -ErrorAction SilentlyContinue
453
+ }
454
+ Remove-Item Env:\NPM_CONFIG_REGISTRY -ErrorAction SilentlyContinue
455
+ }
@@ -0,0 +1,336 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # MemoryLake OpenClaw Plugin Installer for macOS and Linux
5
+ # Usage: curl -fsSL --proto '=https' --tlsv1.2 https://app.memorylake.ai/memorylake-openclaw/install.sh | bash -s -- --api-key <key> --project-id <id>
6
+
7
+ # shellcheck disable=SC2034
8
+ BOLD='\033[1m'
9
+ # shellcheck disable=SC2034
10
+ ACCENT='\033[38;2;255;77;77m' # coral-bright #ff4d4d
11
+ # shellcheck disable=SC2034
12
+ INFO='\033[38;2;136;146;176m' # text-secondary #8892b0
13
+ SUCCESS='\033[38;2;0;229;204m' # cyan-bright #00e5cc
14
+ WARN='\033[38;2;255;176;32m' # amber
15
+ ERROR='\033[38;2;230;57;70m' # coral-mid #e63946
16
+ MUTED='\033[38;2;90;100;128m' # text-muted #5a6480
17
+ NC='\033[0m' # No Color
18
+
19
+ QCLAW_SCRIPT="/Applications/QClaw.app/Contents/Resources/openclaw/config/skills/qclaw-openclaw/scripts/openclaw-mac.sh"
20
+ OPENCLAW_CONFIG="${HOME}/.openclaw/openclaw.json"
21
+ QCLAW_CONFIG="${HOME}/.qclaw/openclaw.json"
22
+
23
+ ui_info() {
24
+ echo -e "${MUTED}·${NC} $*"
25
+ }
26
+
27
+ ui_warn() {
28
+ echo -e "${WARN}!${NC} $*"
29
+ }
30
+
31
+ ui_success() {
32
+ echo -e "${SUCCESS}✓${NC} $*"
33
+ }
34
+
35
+ ui_error() {
36
+ echo -e "${ERROR}✗${NC} $*"
37
+ }
38
+
39
+ print_usage() {
40
+ cat <<EOF
41
+ MemoryLake OpenClaw Plugin Installer (macOS + Linux)
42
+
43
+ Usage:
44
+ curl -fsSL --proto '=https' --tlsv1.2 https://app.memorylake.ai/memorylake-openclaw/install.sh | bash -s -- --api-key <key> --project-id <id>
45
+
46
+ Options:
47
+ --api-key, -a <key> MemoryLake API key (required)
48
+ --project-id, -p <id> MemoryLake project ID (required)
49
+ --help, -h Show this help
50
+
51
+ Environment variables:
52
+ MEMORYLAKE_API_KEY API key (alternative to --api-key)
53
+ MEMORYLAKE_PROJECT_ID Project ID (alternative to --project-id)
54
+ NPM_REGISTRY Override npm registry (e.g. https://registry.npmmirror.com for China)
55
+
56
+ Get apiKey and projectId from https://app.memorylake.ai
57
+ EOF
58
+ }
59
+
60
+ parse_args() {
61
+ API_KEY="${MEMORYLAKE_API_KEY:-}"
62
+ PROJECT_ID="${MEMORYLAKE_PROJECT_ID:-}"
63
+ HELP=0
64
+
65
+ while [[ $# -gt 0 ]]; do
66
+ case "$1" in
67
+ --api-key|-a)
68
+ API_KEY="${2:-}"
69
+ shift 2
70
+ ;;
71
+ --project-id|-p)
72
+ PROJECT_ID="${2:-}"
73
+ shift 2
74
+ ;;
75
+ --help|-h)
76
+ HELP=1
77
+ shift
78
+ ;;
79
+ *)
80
+ shift
81
+ ;;
82
+ esac
83
+ done
84
+ }
85
+
86
+ validate_params() {
87
+ if [[ -z "$API_KEY" || -z "$PROJECT_ID" ]]; then
88
+ ui_error "Missing required parameters: apiKey and projectId"
89
+ echo ""
90
+ echo "Please provide apiKey and projectId. Get them from https://app.memorylake.ai"
91
+ echo ""
92
+ echo "Usage: $0 --api-key <key> --project-id <id>"
93
+ echo " or: MEMORYLAKE_API_KEY=<key> MEMORYLAKE_PROJECT_ID=<id> $0"
94
+ exit 1
95
+ fi
96
+ }
97
+
98
+ detect_claw_mode() {
99
+ if command -v openclaw &>/dev/null; then
100
+ CLAW_MODE="openclaw"
101
+ ui_success "Detected OpenClaw"
102
+ return 0
103
+ fi
104
+
105
+ if [[ "$(uname -s)" == "Darwin" ]] && [[ -x "$QCLAW_SCRIPT" ]]; then
106
+ CLAW_MODE="qclaw"
107
+ ui_success "Detected QClaw"
108
+ return 0
109
+ fi
110
+
111
+ ui_error "OpenClaw or QClaw not found."
112
+ echo "Install OpenClaw first: curl -fsSL https://openclaw.ai/install.sh | bash"
113
+ exit 1
114
+ }
115
+
116
+ plugin_already_installed() {
117
+ if [[ "$CLAW_MODE" == "openclaw" ]]; then
118
+ openclaw plugins list | grep -q "memorylake-openclaw" || return 1
119
+ else
120
+ "$QCLAW_SCRIPT" plugins list | grep -q "memorylake-openclaw" || return 1
121
+ fi
122
+ }
123
+
124
+ detect_network() {
125
+ CHINA_NETWORK=false
126
+
127
+ if [[ -n "${NPM_REGISTRY:-}" ]]; then
128
+ ui_info "Using NPM_REGISTRY=$NPM_REGISTRY"
129
+ return 0
130
+ fi
131
+
132
+ local code
133
+ code="$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 3 --max-time 5 https://google.com/generate_204 2>/dev/null || echo "000")"
134
+
135
+ if [[ "$code" != "204" ]]; then
136
+ CHINA_NETWORK=true
137
+ NPM_REGISTRY="https://registry.npmmirror.com"
138
+ ui_info "China network detected, using npmmirror"
139
+ fi
140
+ }
141
+
142
+ install_plugin() {
143
+ if [[ "$CLAW_MODE" == "openclaw" ]]; then
144
+ if [[ -n "${NPM_REGISTRY:-}" ]]; then
145
+ NPM_CONFIG_REGISTRY="$NPM_REGISTRY" openclaw plugins install memorylake-openclaw
146
+ else
147
+ openclaw plugins install memorylake-openclaw
148
+ fi
149
+ else
150
+ "$QCLAW_SCRIPT" plugins install memorylake-openclaw
151
+ fi
152
+ }
153
+
154
+ brew_has_mirror() {
155
+ # Check if any of the common Homebrew mirror env vars are set
156
+ [[ -n "${HOMEBREW_BOTTLE_DOMAIN:-}" ]] || [[ -n "${HOMEBREW_API_DOMAIN:-}" ]] || [[ -n "${HOMEBREW_BREW_GIT_REMOTE:-}" ]]
157
+ }
158
+
159
+ install_node_via_pkg() {
160
+ local node_version="22.22.1"
161
+ local pkg_name="node-v${node_version}.pkg"
162
+ local pkg_url
163
+
164
+ if [[ "$CHINA_NETWORK" == "true" ]]; then
165
+ pkg_url="https://registry.npmmirror.com/-/binary/node/v${node_version}/${pkg_name}"
166
+ ui_info "Downloading Node.js from npmmirror..."
167
+ else
168
+ pkg_url="https://nodejs.org/dist/v${node_version}/${pkg_name}"
169
+ ui_info "Downloading Node.js from nodejs.org..."
170
+ fi
171
+
172
+ local tmp_dir
173
+ tmp_dir=$(mktemp -d)
174
+
175
+ curl -Lo "${tmp_dir}/${pkg_name}" "$pkg_url"
176
+
177
+ if sudo -n true 2>/dev/null; then
178
+ sudo installer -pkg "${tmp_dir}/${pkg_name}" -target /
179
+ else
180
+ ui_warn "sudo requires a password. Please enter your Mac login password below:"
181
+ sudo installer -pkg "${tmp_dir}/${pkg_name}" -target /
182
+ fi
183
+
184
+ rm -rf "$tmp_dir"
185
+ }
186
+
187
+ install_node() {
188
+ ui_info "npm/node not found, installing Node.js..."
189
+
190
+ local brew_ok=false
191
+ if command -v brew &>/dev/null; then
192
+ if [[ "$CHINA_NETWORK" == "true" ]] && ! brew_has_mirror; then
193
+ ui_info "China network detected but Homebrew has no mirror configured, skipping brew"
194
+ else
195
+ ui_info "Installing Node.js via brew..."
196
+ if brew install node; then
197
+ brew_ok=true
198
+ else
199
+ ui_warn "brew install node failed, falling back to .pkg installer..."
200
+ fi
201
+ fi
202
+ fi
203
+
204
+ if [[ "$brew_ok" == "false" ]]; then
205
+ install_node_via_pkg
206
+ fi
207
+
208
+ # Verify installation
209
+ if command -v node &>/dev/null && command -v npm &>/dev/null; then
210
+ ui_success "Node.js $(node --version) installed"
211
+ else
212
+ ui_error "Node.js installation failed"
213
+ exit 1
214
+ fi
215
+
216
+ # Configure npm mirror for China network
217
+ if [[ "$CHINA_NETWORK" == "true" ]]; then
218
+ npm config set registry "$NPM_REGISTRY"
219
+ ui_info "npm registry set to $NPM_REGISTRY"
220
+ fi
221
+ }
222
+
223
+ write_config() {
224
+ local config_path
225
+ if [[ "$CLAW_MODE" == "openclaw" ]]; then
226
+ config_path="$OPENCLAW_CONFIG"
227
+ else
228
+ config_path="$QCLAW_CONFIG"
229
+ fi
230
+
231
+ local config_dir
232
+ config_dir="$(dirname "$config_path")"
233
+ mkdir -p "$config_dir"
234
+
235
+ CONFIG_PATH="$config_path" API_KEY="$API_KEY" PROJECT_ID="$PROJECT_ID" CLAW_MODE="$CLAW_MODE" python3 -c '
236
+ import json
237
+ import os
238
+
239
+ path = os.environ["CONFIG_PATH"]
240
+ api_key = os.environ["API_KEY"]
241
+ project_id = os.environ["PROJECT_ID"]
242
+ claw_mode = os.environ.get("CLAW_MODE", "")
243
+
244
+ config = {}
245
+ if os.path.exists(path):
246
+ with open(path) as f:
247
+ config = json.load(f)
248
+
249
+ if "plugins" not in config:
250
+ config["plugins"] = {}
251
+ if "entries" not in config["plugins"]:
252
+ config["plugins"]["entries"] = {}
253
+
254
+ config["plugins"]["entries"]["memorylake-openclaw"] = {
255
+ "enabled": True,
256
+ "config": {
257
+ "apiKey": api_key,
258
+ "projectId": project_id
259
+ }
260
+ }
261
+
262
+ # Merge tools config (QClaw only)
263
+ if claw_mode == "qclaw":
264
+ if "tools" not in config:
265
+ config["tools"] = {}
266
+ if "alsoAllow" not in config["tools"]:
267
+ config["tools"]["alsoAllow"] = []
268
+ arr = config["tools"]["alsoAllow"]
269
+ if "advanced_web_search" not in arr:
270
+ arr.append("advanced_web_search")
271
+
272
+ if "web" not in config["tools"]:
273
+ config["tools"]["web"] = {}
274
+ if "search" not in config["tools"]["web"]:
275
+ config["tools"]["web"]["search"] = {}
276
+ config["tools"]["web"]["search"]["enabled"] = False
277
+
278
+ with open(path, "w") as f:
279
+ json.dump(config, f, indent=2)
280
+ '
281
+ }
282
+
283
+ restart_gateway() {
284
+ if [[ "$CLAW_MODE" == "openclaw" ]]; then
285
+ openclaw gateway restart
286
+ fi
287
+ }
288
+
289
+ main() {
290
+ parse_args "$@"
291
+
292
+ if [[ "$HELP" == "1" ]]; then
293
+ print_usage
294
+ return 0
295
+ fi
296
+
297
+ validate_params
298
+ detect_claw_mode
299
+
300
+ if plugin_already_installed; then
301
+ ui_info "memorylake-openclaw is already installed."
302
+ exit 0
303
+ fi
304
+
305
+ detect_network
306
+
307
+ # Try install; if qclaw on macOS fails with npm error, install Node.js and retry
308
+ local install_output install_exit
309
+ if install_output=$(install_plugin 2>&1); then
310
+ echo "$install_output"
311
+ else
312
+ install_exit=$?
313
+ echo "$install_output" >&2
314
+ if [[ "$CLAW_MODE" == "qclaw" ]] && [[ "$(uname -s)" == "Darwin" ]] && echo "$install_output" | grep -qi "npm"; then
315
+ ui_warn "Plugin install failed due to npm issue, attempting to install Node.js..."
316
+ install_node
317
+ ui_info "Retrying plugin installation..."
318
+ install_plugin
319
+ else
320
+ exit "$install_exit"
321
+ fi
322
+ fi
323
+ ui_success "Plugin installed"
324
+
325
+ write_config
326
+ ui_success "Config updated"
327
+
328
+ restart_gateway
329
+ if [[ "$CLAW_MODE" == "openclaw" ]]; then
330
+ ui_success "Gateway restarted"
331
+ fi
332
+
333
+ ui_success "memorylake-openclaw setup complete!"
334
+ }
335
+
336
+ main "$@"
@@ -8,50 +8,27 @@
8
8
  */
9
9
 
10
10
  import { readFileSync, readdirSync, existsSync, statSync } from "fs";
11
- import { join, basename } from "path";
11
+ import { join, basename, dirname } from "path";
12
12
  import { homedir } from "os";
13
13
  import { parseArgs } from "util";
14
+ import { spawnSync } from "node:child_process";
15
+ import { fileURLToPath } from "node:url";
14
16
 
15
- const OPENCLAW_CONFIG = join(homedir(), ".openclaw", "openclaw.json");
16
17
  const BATCH_SIZE = 20;
17
18
  const API_USER_ID = "default";
18
19
 
19
- function loadConfig(agent) {
20
- const config = JSON.parse(readFileSync(OPENCLAW_CONFIG, "utf-8"));
21
-
22
- const pluginCfg =
23
- config?.plugins?.entries?.["memorylake-openclaw"]?.config ?? {};
24
-
25
- const host = (pluginCfg.host || "https://app.memorylake.ai").replace(
26
- /\/$/,
27
- ""
28
- );
29
- const apiKey = pluginCfg.apiKey || "";
30
- const projectId = pluginCfg.projectId || "";
20
+ const __dirname = dirname(fileURLToPath(import.meta.url));
21
+ const GET_CONFIG_PATH = join(__dirname, "../../common/get-config.mjs");
31
22
 
32
- if (!apiKey) {
33
- console.error("ERROR: apiKey is missing from config");
34
- process.exit(1);
35
- }
36
- if (!projectId) {
37
- console.error("ERROR: projectId is missing from config");
38
- process.exit(1);
39
- }
40
-
41
- // Resolve workspace: agent-specific first, then defaults
42
- const agentsCfg = config.agents ?? {};
43
- let workspace = "";
44
- for (const entry of agentsCfg.list ?? []) {
45
- if (entry.id === agent) {
46
- workspace = entry.workspace || "";
47
- break;
48
- }
49
- }
50
- if (!workspace) {
51
- workspace = agentsCfg.defaults?.workspace || "";
23
+ function loadConfig(agent) {
24
+ const result = spawnSync(process.execPath, [GET_CONFIG_PATH, "--agent", agent], {
25
+ encoding: "utf-8",
26
+ stdio: ["inherit", "pipe", "inherit"],
27
+ });
28
+ if (result.status !== 0) {
29
+ process.exit(result.status ?? 1);
52
30
  }
53
-
54
- return { host, apiKey, projectId, workspace };
31
+ return JSON.parse(result.stdout);
55
32
  }
56
33
 
57
34
  async function postMemories(host, apiKey, projectId, payload) {