memorylake-openclaw 0.0.8 → 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.8",
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 "$@"
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+
7
+ // Parse --agent
8
+ const args = process.argv.slice(2);
9
+ const agentIdx = args.indexOf("--agent");
10
+ if (agentIdx === -1 || !args[agentIdx + 1]) {
11
+ console.error("Usage: node get-config.mjs --agent <agentId>");
12
+ process.exit(1);
13
+ }
14
+ const agentId = args[agentIdx + 1];
15
+
16
+ // Read global config
17
+ const openclawPath = join(homedir(), ".openclaw", "openclaw.json");
18
+ const openclaw = JSON.parse(readFileSync(openclawPath, "utf-8"));
19
+ const globalCfg = openclaw?.plugins?.entries?.["memorylake-openclaw"]?.config;
20
+ if (!globalCfg) {
21
+ console.error("Error: memorylake-openclaw plugin config not found");
22
+ process.exit(1);
23
+ }
24
+
25
+ // Resolve workspace
26
+ const agents = openclaw?.agents;
27
+ const agentEntry = agents?.list?.find((a) => a.id === agentId);
28
+ const workspace = agentEntry?.workspace || agents?.defaults?.workspace;
29
+ if (!workspace) {
30
+ console.error(`Error: no workspace found for agent "${agentId}"`);
31
+ process.exit(1);
32
+ }
33
+
34
+ // Merge per-agent overrides
35
+ const merged = { ...globalCfg };
36
+ const localPath = join(workspace, ".memorylake", "config.json");
37
+ if (existsSync(localPath)) {
38
+ const raw = JSON.parse(readFileSync(localPath, "utf-8"));
39
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
40
+ Object.assign(merged, raw);
41
+ }
42
+ }
43
+ merged.host = merged.host || "https://app.memorylake.ai";
44
+ merged.workspace = workspace;
45
+
46
+ // Validate required fields
47
+ if (!merged.apiKey || !merged.projectId) {
48
+ console.error("Error: apiKey and projectId are required");
49
+ process.exit(1);
50
+ }
51
+
52
+ console.log(JSON.stringify(merged, null, 2));
@@ -19,21 +19,15 @@ Directly call MemoryLake's REST APIs by discovering endpoints from the live Open
19
19
 
20
20
  ## Step 1 — Read MemoryLake Config
21
21
 
22
- Read `~/.openclaw/openclaw.json` and extract the plugin config:
22
+ Run the common config script (path is relative to **this skill's SKILL.md**, i.e. `../common/get-config.mjs`):
23
23
 
24
24
  ```bash
25
- cat ~/.openclaw/openclaw.json | jq '.plugins.entries["memorylake-openclaw"].config'
25
+ node {path-to-this-skill}/../common/get-config.mjs --agent {agent}
26
26
  ```
27
27
 
28
- Extract these values:
28
+ Where `{agent}` is the current agent ID. The script outputs JSON config with `host`, `apiKey`, `projectId`, etc.
29
29
 
30
- | Variable | Description | Default |
31
- |----------|-------------|---------|
32
- | `host` | API host | `https://app.memorylake.ai` |
33
- | `apiKey` | API key for authentication | (required) |
34
- | `projectId` | MemoryLake project ID | (required) |
35
-
36
- If `apiKey` or `projectId` is missing, stop and inform the user.
30
+ If the script exits with an error, stop and inform the user.
37
31
 
38
32
  Auth header for all requests:
39
33
 
@@ -185,15 +179,14 @@ All responses follow the same wrapper format:
185
179
 
186
180
  - **Wrong base URL**: The full URL must include `/openapi/memorylake` before the API path. E.g., `/openapi/memorylake/api/v1/projects`, NOT just `/api/v1/projects`
187
181
  - **Missing auth header**: Every request requires `Authorization: Bearer {apiKey}`
188
- - **Hardcoded project ID**: Always read `projectId` from `~/.openclaw/openclaw.json` config, not from user input (unless the user explicitly wants a different project)
182
+ - **Hardcoded project ID**: Always read `projectId` from the config script output, not from user input (unless the user explicitly wants a different project)
189
183
  - **Pagination**: List endpoints default to `page=1, size=20`. Pass `page` and `size` query params if the user needs more results
190
184
 
191
185
  ## Quick Reference
192
186
 
193
187
  | Item | Value |
194
188
  |------|-------|
195
- | OpenClaw config | `~/.openclaw/openclaw.json` |
196
- | Plugin config key | `plugins.entries["memorylake-openclaw"].config` |
189
+ | Config script | `{path-to-this-skill}/../common/get-config.mjs --agent {agent}` |
197
190
  | Server base path | `/openapi/memorylake` |
198
191
  | OpenAPI spec URL | `{host}/openapi/memorylake/api-docs/open-api` |
199
192
  | Auth header | `Authorization: Bearer {apiKey}` |
@@ -16,21 +16,15 @@ Upload local files to MemoryLake using the multipart upload API, then associate
16
16
 
17
17
  ## Step 1 -- Read MemoryLake Config
18
18
 
19
- Read `~/.openclaw/openclaw.json` and extract the plugin config:
19
+ Run the common config script (path is relative to **this skill's SKILL.md**, i.e. `../common/get-config.mjs`):
20
20
 
21
21
  ```bash
22
- cat ~/.openclaw/openclaw.json | jq '.plugins.entries["memorylake-openclaw"].config'
22
+ node {path-to-this-skill}/../common/get-config.mjs --agent {agent}
23
23
  ```
24
24
 
25
- Extract these values:
25
+ Where `{agent}` is the current agent ID. The script outputs JSON config with `host`, `apiKey`, `projectId`, etc.
26
26
 
27
- | Variable | Description | Default |
28
- |----------|-------------|---------|
29
- | `host` | API host | `https://app.memorylake.ai` |
30
- | `apiKey` | API key for authentication | (required) |
31
- | `projectId` | MemoryLake project ID | (required) |
32
-
33
- If `apiKey` or `projectId` is missing, stop and inform the user.
27
+ If the script exits with an error, stop and inform the user.
34
28
 
35
29
  ## Step 2 -- Run the Upload Script
36
30
 
@@ -56,5 +50,5 @@ The script prints progress for each step (create upload, upload parts, complete,
56
50
 
57
51
  ## Common Mistakes
58
52
 
59
- - **Skipping Step 1**: Directly hardcoding host/apiKey/projectId instead of reading from `~/.openclaw/openclaw.json`
53
+ - **Skipping Step 1**: Directly hardcoding host/apiKey/projectId instead of using the config script
60
54
  - **Relative file paths**: Always resolve the user's file path to an absolute path before passing to the script
@@ -17,8 +17,8 @@ Extract memory files and conversation history from session files, then submit th
17
17
  ## Prerequisites
18
18
 
19
19
  The caller must provide:
20
+ - **`agent`**: The agent name (e.g., `main`). Used to resolve config (Step 1) and locate session files (Step 2).
20
21
  - **`user_id`**: The user ID for filtering sessions (e.g., a Feishu user ID like `ou_xxx`). **This is only used for session filtering, NOT for the API request.**
21
- - **`agent`**: The agent name (e.g., `main`)
22
22
 
23
23
  ## Preferred: Run the Migration Script
24
24
 
@@ -40,26 +40,15 @@ If the script fails, follow these steps manually.
40
40
 
41
41
  ### Step 1 — Read MemoryLake Config
42
42
 
43
- Read `~/.openclaw/openclaw.json` and extract the plugin config:
43
+ Run the common config script (path is relative to **this skill's SKILL.md**, i.e. `../common/get-config.mjs`):
44
44
 
45
45
  ```bash
46
- cat ~/.openclaw/openclaw.json | jq '.plugins.entries["memorylake-openclaw"].config'
46
+ node {path-to-this-skill}/../common/get-config.mjs --agent {agent}
47
47
  ```
48
48
 
49
- Extract these values:
50
- - **`host`** — API host (default: `https://app.memorylake.ai`)
51
- - **`apiKey`** — API key for authentication
52
- - **`projectId`** — MemoryLake project ID
49
+ The script outputs JSON config with `host`, `apiKey`, `projectId`, `workspace`, etc. If the script exits with an error, stop and inform the user.
53
50
 
54
- ### Step 2 — Identify User and Agent
55
-
56
- Use the `user_id` and `agent` provided by the caller:
57
- - `user_id` — only for filtering sessions in Step 3 (session keys contain the user ID)
58
- - `agent` — determines which agent's session directory to read
59
-
60
- **When POSTing to the API, always use `"user_id": "default"`. Do NOT use the caller-provided `user_id`.**
61
-
62
- ### Step 3 — Filter Sessions by User ID
51
+ ### Step 2 — Filter Sessions by User ID
63
52
 
64
53
  **You MUST use `sessions.json` to filter sessions. Do NOT grep/search JSONL files directly.**
65
54
 
@@ -83,29 +72,21 @@ Use the `user_id` and `agent` provided by the caller:
83
72
  ~/.openclaw/agents/{agent}/sessions/{sessionId}.jsonl
84
73
  ```
85
74
 
86
- ### Step 4 — Read Memory Files
87
-
88
- Resolve the workspace path for the agent:
89
-
90
- 1. Check `agents.list` for an entry with `id` matching the agent. If it has a `workspace` field, use it.
91
- 2. Otherwise, fall back to `agents.defaults.workspace`.
75
+ ### Step 3 — Read Memory Files
92
76
 
93
- ```bash
94
- # Agent-specific workspace (e.g., agent "foo"):
95
- cat ~/.openclaw/openclaw.json | jq -r '.agents.list[] | select(.id == "{agent}") | .workspace'
96
- # Default workspace:
97
- cat ~/.openclaw/openclaw.json | jq -r '.agents.defaults.workspace'
98
- ```
99
-
100
- Then read:
77
+ Use the `workspace` path from the config output in Step 1. Then read:
101
78
  - `{workspace}/MEMORY.md`
102
79
  - All files in `{workspace}/memory/` directory
103
80
 
104
81
  These contain curated memory that should also be migrated.
105
82
 
106
- ### Step 5 — Submit Data to MemoryLake
83
+ ### Step 4 — Submit Data to MemoryLake
84
+
85
+ Use `host`, `apiKey`, `projectId` from the config output in Step 1.
86
+
87
+ **When POSTing to the API, always use `"user_id": "default"`. Do NOT use the caller-provided `user_id`.**
107
88
 
108
- #### 5a — Submit Session Conversations
89
+ #### 4a — Submit Session Conversations
109
90
 
110
91
  For each matched `.jsonl` session file:
111
92
 
@@ -156,7 +137,7 @@ For each matched `.jsonl` session file:
156
137
  }
157
138
  }
158
139
  ```
159
- #### 5b — Submit Memory Files
140
+ #### 4b — Submit Memory Files
160
141
 
161
142
  For each memory file (`MEMORY.md` and files in `memory/`):
162
143
 
@@ -203,11 +184,10 @@ At the end, print a summary:
203
184
 
204
185
  | Item | Path / Value |
205
186
  |------|-------------|
206
- | Config file | `~/.openclaw/openclaw.json` |
207
- | Plugin config key | `plugins.entries["memorylake-openclaw"].config` |
187
+ | Config script | `{path-to-this-skill}/../common/get-config.mjs --agent {agent}` |
208
188
  | Session index | `~/.openclaw/agents/{agent}/sessions/sessions.json` |
209
189
  | Session files | `~/.openclaw/agents/{agent}/sessions/{id}.jsonl` |
210
- | Workspace path | `agents.defaults.workspace` in config |
190
+ | Workspace path | from config script output (`workspace` field) |
211
191
  | API endpoint | `{host}/openapi/memorylake/api/v2/projects/{projectId}/memories` |
212
192
  | Auth header | `Authorization: Bearer {apiKey}` |
213
193
  | Default host | `https://app.memorylake.ai` |
@@ -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) {