MAESTRO: add OpenClaw gateway detection, plugin install, and memory slot config to install.sh

Add find_openclaw()/check_openclaw() for gateway detection across PATH,
~/.openclaw/, /usr/local/bin/, and node_modules paths. Add install_plugin()
that clones, builds, creates installable package, and runs plugins install/enable
following the Dockerfile.e2e flow. Add configure_memory_slot() that creates or
updates ~/.openclaw/openclaw.json with plugins.slots.memory="claude-mem" while
preserving existing config. Includes test-install.sh with 23 passing tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-02-11 21:31:15 -05:00
parent e4846a2046
commit 1f834863a7
2 changed files with 553 additions and 2 deletions
+224 -2
View File
@@ -267,6 +267,211 @@ install_uv() {
success "uv ${uv_version} installed at ${UV_PATH}"
}
###############################################################################
# OpenClaw gateway detection
###############################################################################
OPENCLAW_PATH=""
find_openclaw() {
# Try PATH first
if command -v openclaw.mjs &>/dev/null; then
OPENCLAW_PATH="$(command -v openclaw.mjs)"
return 0
fi
# Check common installation paths
local -a openclaw_paths=(
"${HOME}/.openclaw/openclaw.mjs"
"/usr/local/bin/openclaw.mjs"
"/usr/local/lib/node_modules/openclaw/openclaw.mjs"
"${HOME}/.npm-global/lib/node_modules/openclaw/openclaw.mjs"
)
# Also check for node_modules in common project locations
if [[ -n "${NODE_PATH:-}" ]]; then
openclaw_paths+=("${NODE_PATH}/openclaw/openclaw.mjs")
fi
for candidate in "${openclaw_paths[@]}"; do
if [[ -f "$candidate" ]]; then
OPENCLAW_PATH="$candidate"
return 0
fi
done
OPENCLAW_PATH=""
return 1
}
check_openclaw() {
if ! find_openclaw; then
error "OpenClaw gateway not found"
error ""
error "The claude-mem plugin requires an OpenClaw gateway to be installed."
error "Please install OpenClaw first:"
error ""
error " npm install -g openclaw"
error " # or visit: https://openclaw.dev/docs/installation"
error ""
error "Then re-run this installer."
exit 1
fi
success "OpenClaw gateway found at ${OPENCLAW_PATH}"
}
###############################################################################
# Plugin installation — clone, build, install, enable
# Flow based on openclaw/Dockerfile.e2e
###############################################################################
CLAUDE_MEM_REPO="https://github.com/thedotmack/claude-mem.git"
install_plugin() {
local build_dir
build_dir="$(mktemp -d)"
# Ensure cleanup on exit from this function
cleanup_build_dir() {
if [[ -d "$build_dir" ]]; then
rm -rf "$build_dir"
fi
}
trap cleanup_build_dir EXIT
info "Cloning claude-mem repository..."
if ! git clone --depth 1 "$CLAUDE_MEM_REPO" "$build_dir/claude-mem" 2>&1; then
error "Failed to clone claude-mem repository"
error "Check your internet connection and try again."
cleanup_build_dir
exit 1
fi
local plugin_src="${build_dir}/claude-mem/openclaw"
# Build the TypeScript plugin
info "Building TypeScript plugin..."
if ! (cd "$plugin_src" && NODE_ENV=development npm install --ignore-scripts 2>&1 && npx tsc 2>&1); then
error "Failed to build the claude-mem OpenClaw plugin"
error "Make sure Node.js and npm are installed."
cleanup_build_dir
exit 1
fi
# Create minimal installable package (matches Dockerfile.e2e pattern)
local installable_dir="${build_dir}/claude-mem-installable"
mkdir -p "${installable_dir}/dist"
cp "${plugin_src}/dist/index.js" "${installable_dir}/dist/"
cp "${plugin_src}/dist/index.d.ts" "${installable_dir}/dist/" 2>/dev/null || true
cp "${plugin_src}/openclaw.plugin.json" "${installable_dir}/"
# Generate the installable package.json with openclaw.extensions field
node -e "
const pkg = {
name: 'claude-mem',
version: '1.0.0',
type: 'module',
main: 'dist/index.js',
openclaw: { extensions: ['./dist/index.js'] }
};
require('fs').writeFileSync('${installable_dir}/package.json', JSON.stringify(pkg, null, 2));
"
# Install the plugin using OpenClaw's CLI
info "Installing claude-mem plugin into OpenClaw..."
if ! node "$OPENCLAW_PATH" plugins install "$installable_dir" 2>&1; then
error "Failed to install claude-mem plugin"
error "Try manually: node ${OPENCLAW_PATH} plugins install <path>"
cleanup_build_dir
exit 1
fi
# Enable the plugin
info "Enabling claude-mem plugin..."
if ! node "$OPENCLAW_PATH" plugins enable claude-mem 2>&1; then
error "Failed to enable claude-mem plugin"
error "Try manually: node ${OPENCLAW_PATH} plugins enable claude-mem"
cleanup_build_dir
exit 1
fi
cleanup_build_dir
trap - EXIT
success "claude-mem plugin installed and enabled"
}
###############################################################################
# Memory slot configuration
# Sets plugins.slots.memory = "claude-mem" in ~/.openclaw/openclaw.json
###############################################################################
configure_memory_slot() {
local config_dir="${HOME}/.openclaw"
local config_file="${config_dir}/openclaw.json"
mkdir -p "$config_dir"
if [[ ! -f "$config_file" ]]; then
# No config file exists — create one with the memory slot
info "Creating OpenClaw configuration with claude-mem memory slot..."
node -e "
const config = {
plugins: {
slots: { memory: 'claude-mem' },
entries: {
'claude-mem': {
enabled: true,
config: {
workerPort: 37777,
syncMemoryFile: true
}
}
}
}
};
require('fs').writeFileSync('${config_file}', JSON.stringify(config, null, 2));
"
success "Created ${config_file} with memory slot set to claude-mem"
return 0
fi
# Config file exists — update it to set the memory slot
info "Updating OpenClaw configuration to use claude-mem memory slot..."
# Use node for reliable JSON manipulation
node -e "
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('${config_file}', 'utf8'));
// Ensure plugins structure exists
if (!config.plugins) config.plugins = {};
if (!config.plugins.slots) config.plugins.slots = {};
if (!config.plugins.entries) config.plugins.entries = {};
// Set memory slot to claude-mem
config.plugins.slots.memory = 'claude-mem';
// Ensure claude-mem entry exists and is enabled
if (!config.plugins.entries['claude-mem']) {
config.plugins.entries['claude-mem'] = {
enabled: true,
config: {
workerPort: 37777,
syncMemoryFile: true
}
};
} else {
config.plugins.entries['claude-mem'].enabled = true;
}
fs.writeFileSync('${config_file}', JSON.stringify(config, null, 2));
"
success "Memory slot set to claude-mem in ${config_file}"
}
###############################################################################
# Main
###############################################################################
@@ -275,7 +480,7 @@ main() {
print_banner
detect_platform
# --- Step 1: Bun ---
# --- Step 1: Dependencies ---
echo ""
info "Checking dependencies..."
echo ""
@@ -284,13 +489,30 @@ main() {
install_bun
fi
# --- Step 2: uv ---
if ! check_uv; then
install_uv
fi
echo ""
success "All dependencies satisfied"
# --- Step 2: OpenClaw gateway ---
echo ""
info "Locating OpenClaw gateway..."
check_openclaw
# --- Step 3: Plugin installation ---
echo ""
info "Installing claude-mem plugin..."
install_plugin
# --- Step 4: Memory slot configuration ---
echo ""
info "Configuring memory slot..."
configure_memory_slot
echo ""
success "OpenClaw gateway detection and plugin installation complete"
}
main "$@"