MAESTRO: add observation feed interactive setup, config writer, and updated completion summary to install.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+219
-16
@@ -805,6 +805,197 @@ verify_health() {
|
||||
return 1
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Observation feed setup — optional interactive channel configuration
|
||||
###############################################################################
|
||||
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
FEED_CONFIGURED=false
|
||||
|
||||
setup_observation_feed() {
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}Real-Time Observation Feed${COLOR_RESET}"
|
||||
echo ""
|
||||
echo " claude-mem can stream AI-compressed observations to a messaging"
|
||||
echo " channel in real time. Every time an agent learns something,"
|
||||
echo " you'll see it in your chat."
|
||||
echo ""
|
||||
|
||||
if [[ "$NON_INTERACTIVE" == "--non-interactive" ]] || [[ ! -t 0 ]]; then
|
||||
info "Non-interactive mode: skipping observation feed setup"
|
||||
info "Configure later in ~/.openclaw/openclaw.json under"
|
||||
info " plugins.entries.claude-mem.config.observationFeed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
prompt_user "Would you like to set up real-time observation streaming to a messaging channel? (y/n)"
|
||||
local answer
|
||||
read -r answer
|
||||
answer="${answer:-n}"
|
||||
|
||||
if [[ "${answer,,}" != "y" && "${answer,,}" != "yes" ]]; then
|
||||
echo ""
|
||||
info "Skipped observation feed setup."
|
||||
info "You can configure it later by re-running this installer or"
|
||||
info "editing ~/.openclaw/openclaw.json under"
|
||||
info " plugins.entries.claude-mem.config.observationFeed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}Select your messaging channel:${COLOR_RESET}"
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}1)${COLOR_RESET} Telegram"
|
||||
echo -e " ${COLOR_BOLD}2)${COLOR_RESET} Discord"
|
||||
echo -e " ${COLOR_BOLD}3)${COLOR_RESET} Slack"
|
||||
echo -e " ${COLOR_BOLD}4)${COLOR_RESET} Signal"
|
||||
echo -e " ${COLOR_BOLD}5)${COLOR_RESET} WhatsApp"
|
||||
echo -e " ${COLOR_BOLD}6)${COLOR_RESET} LINE"
|
||||
echo ""
|
||||
|
||||
local channel_choice
|
||||
while true; do
|
||||
prompt_user "Enter choice [1-6]:"
|
||||
read -r channel_choice
|
||||
|
||||
case "$channel_choice" in
|
||||
1)
|
||||
FEED_CHANNEL="telegram"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your Telegram chat ID:${COLOR_RESET}"
|
||||
echo " Message @userinfobot on Telegram (https://t.me/userinfobot)"
|
||||
echo " — it replies with your numeric chat ID."
|
||||
echo " For groups, the ID is negative (e.g., -1001234567890)."
|
||||
break
|
||||
;;
|
||||
2)
|
||||
FEED_CHANNEL="discord"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your Discord channel ID:${COLOR_RESET}"
|
||||
echo " Enable Developer Mode (Settings → Advanced → Developer Mode),"
|
||||
echo " right-click the target channel → Copy Channel ID"
|
||||
break
|
||||
;;
|
||||
3)
|
||||
FEED_CHANNEL="slack"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your Slack channel ID:${COLOR_RESET}"
|
||||
echo " Open the channel, click the channel name at top,"
|
||||
echo " scroll to bottom — ID looks like C01ABC2DEFG"
|
||||
break
|
||||
;;
|
||||
4)
|
||||
FEED_CHANNEL="signal"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your Signal target ID:${COLOR_RESET}"
|
||||
echo " Use the phone number or group ID from your"
|
||||
echo " OpenClaw Signal plugin config"
|
||||
break
|
||||
;;
|
||||
5)
|
||||
FEED_CHANNEL="whatsapp"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your WhatsApp target ID:${COLOR_RESET}"
|
||||
echo " Use the phone number or group JID from your"
|
||||
echo " OpenClaw WhatsApp plugin config"
|
||||
break
|
||||
;;
|
||||
6)
|
||||
FEED_CHANNEL="line"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}How to find your LINE target ID:${COLOR_RESET}"
|
||||
echo " Use the user ID or group ID from the"
|
||||
echo " LINE Developer Console"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
warn "Invalid choice. Please enter a number between 1 and 6."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
prompt_user "Enter your ${FEED_CHANNEL} target ID:"
|
||||
read -r FEED_TARGET_ID
|
||||
|
||||
if [[ -z "$FEED_TARGET_ID" ]]; then
|
||||
warn "No target ID provided — skipping observation feed setup."
|
||||
warn "You can configure it later in ~/.openclaw/openclaw.json"
|
||||
FEED_CHANNEL=""
|
||||
return 0
|
||||
fi
|
||||
|
||||
success "Observation feed: ${FEED_CHANNEL} → ${FEED_TARGET_ID}"
|
||||
FEED_CONFIGURED=true
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Write observation feed config into ~/.openclaw/openclaw.json
|
||||
###############################################################################
|
||||
|
||||
write_observation_feed_config() {
|
||||
if [[ "$FEED_CONFIGURED" != "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local config_file="${HOME}/.openclaw/openclaw.json"
|
||||
|
||||
if [[ ! -f "$config_file" ]]; then
|
||||
warn "OpenClaw config file not found at ${config_file}"
|
||||
warn "Cannot write observation feed config."
|
||||
return 1
|
||||
fi
|
||||
|
||||
info "Writing observation feed configuration..."
|
||||
|
||||
# Pass values via environment variables to avoid injection
|
||||
INSTALLER_FEED_CHANNEL="$FEED_CHANNEL" \
|
||||
INSTALLER_FEED_TARGET_ID="$FEED_TARGET_ID" \
|
||||
INSTALLER_CONFIG_FILE="$config_file" \
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const configPath = process.env.INSTALLER_CONFIG_FILE;
|
||||
const channel = process.env.INSTALLER_FEED_CHANNEL;
|
||||
const targetId = process.env.INSTALLER_FEED_TARGET_ID;
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
|
||||
// Ensure nested structure exists
|
||||
if (!config.plugins) config.plugins = {};
|
||||
if (!config.plugins.entries) config.plugins.entries = {};
|
||||
if (!config.plugins.entries['claude-mem']) {
|
||||
config.plugins.entries['claude-mem'] = { enabled: true, config: {} };
|
||||
}
|
||||
if (!config.plugins.entries['claude-mem'].config) {
|
||||
config.plugins.entries['claude-mem'].config = {};
|
||||
}
|
||||
|
||||
// Set observation feed config
|
||||
config.plugins.entries['claude-mem'].config.observationFeed = {
|
||||
enabled: true,
|
||||
channel: channel,
|
||||
to: targetId
|
||||
};
|
||||
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
||||
"
|
||||
|
||||
success "Observation feed config written to ${config_file}"
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}Observation feed summary:${COLOR_RESET}"
|
||||
echo -e " Channel: ${COLOR_CYAN}${FEED_CHANNEL}${COLOR_RESET}"
|
||||
echo -e " Target: ${COLOR_CYAN}${FEED_TARGET_ID}${COLOR_RESET}"
|
||||
echo -e " Enabled: ${COLOR_GREEN}yes${COLOR_RESET}"
|
||||
echo ""
|
||||
info "Restart your OpenClaw gateway to activate the observation feed."
|
||||
info "You should see these log lines:"
|
||||
echo " [claude-mem] Observation feed starting — channel: ${FEED_CHANNEL}, target: ${FEED_TARGET_ID}"
|
||||
echo ""
|
||||
info "After restarting, run /claude-mem-feed in any OpenClaw chat to verify"
|
||||
info "the feed is connected."
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Completion summary
|
||||
###############################################################################
|
||||
@@ -838,17 +1029,23 @@ print_completion_summary() {
|
||||
echo -e " ${COLOR_YELLOW}⚠${COLOR_RESET} Worker may not be running — check logs at ~/.claude-mem/logs/"
|
||||
fi
|
||||
|
||||
if [[ "$FEED_CONFIGURED" == "true" ]]; then
|
||||
echo -e " ${COLOR_GREEN}✓${COLOR_RESET} Observation feed: ${COLOR_BOLD}${FEED_CHANNEL}${COLOR_RESET} → ${FEED_TARGET_ID}"
|
||||
else
|
||||
echo -e " ${COLOR_YELLOW}─${COLOR_RESET} Observation feed: not configured (optional)"
|
||||
echo -e " Configure later in ~/.openclaw/openclaw.json under"
|
||||
echo -e " plugins.entries.claude-mem.config.observationFeed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}Next step: Set up your observation feed${COLOR_RESET}"
|
||||
echo -e " ${COLOR_BOLD}What's next?${COLOR_RESET}"
|
||||
echo ""
|
||||
echo " claude-mem can send AI-compressed observations to your preferred"
|
||||
echo " messaging channel. Supported channels:"
|
||||
echo ""
|
||||
echo -e " ${COLOR_CYAN}•${COLOR_RESET} Telegram ${COLOR_CYAN}•${COLOR_RESET} Discord ${COLOR_CYAN}•${COLOR_RESET} Slack"
|
||||
echo -e " ${COLOR_CYAN}•${COLOR_RESET} Signal ${COLOR_CYAN}•${COLOR_RESET} WhatsApp ${COLOR_CYAN}•${COLOR_RESET} LINE"
|
||||
echo ""
|
||||
echo " Configure in ~/.openclaw/openclaw.json under"
|
||||
echo " plugins.entries.claude-mem.config.observationFeed"
|
||||
echo -e " ${COLOR_CYAN}1.${COLOR_RESET} Restart your OpenClaw gateway to load the plugin"
|
||||
echo -e " ${COLOR_CYAN}2.${COLOR_RESET} Verify with ${COLOR_BOLD}/claude-mem-status${COLOR_RESET} in any OpenClaw chat"
|
||||
echo -e " ${COLOR_CYAN}3.${COLOR_RESET} Check the viewer UI at ${COLOR_BOLD}http://localhost:37777${COLOR_RESET}"
|
||||
if [[ "$FEED_CONFIGURED" == "true" ]]; then
|
||||
echo -e " ${COLOR_CYAN}4.${COLOR_RESET} Run ${COLOR_BOLD}/claude-mem-feed${COLOR_RESET} to check feed status"
|
||||
fi
|
||||
echo ""
|
||||
echo -e " ${COLOR_BOLD}To re-run this installer:${COLOR_RESET}"
|
||||
echo " bash <(curl -fsSL https://raw.githubusercontent.com/thedotmack/claude-mem/main/openclaw/install.sh)"
|
||||
@@ -865,7 +1062,7 @@ main() {
|
||||
|
||||
# --- Step 1: Dependencies ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[1/7]${COLOR_RESET} Checking dependencies..."
|
||||
info "${COLOR_BOLD}[1/8]${COLOR_RESET} Checking dependencies..."
|
||||
echo ""
|
||||
|
||||
if ! check_bun; then
|
||||
@@ -881,32 +1078,32 @@ main() {
|
||||
|
||||
# --- Step 2: OpenClaw gateway ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[2/7]${COLOR_RESET} Locating OpenClaw gateway..."
|
||||
info "${COLOR_BOLD}[2/8]${COLOR_RESET} Locating OpenClaw gateway..."
|
||||
check_openclaw
|
||||
|
||||
# --- Step 3: Plugin installation ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[3/7]${COLOR_RESET} Installing claude-mem plugin..."
|
||||
info "${COLOR_BOLD}[3/8]${COLOR_RESET} Installing claude-mem plugin..."
|
||||
install_plugin
|
||||
|
||||
# --- Step 4: Memory slot configuration ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[4/7]${COLOR_RESET} Configuring memory slot..."
|
||||
info "${COLOR_BOLD}[4/8]${COLOR_RESET} Configuring memory slot..."
|
||||
configure_memory_slot
|
||||
|
||||
# --- Step 5: AI provider setup ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[5/7]${COLOR_RESET} AI provider setup..."
|
||||
info "${COLOR_BOLD}[5/8]${COLOR_RESET} AI provider setup..."
|
||||
setup_ai_provider
|
||||
|
||||
# --- Step 6: Write settings ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[6/7]${COLOR_RESET} Writing settings..."
|
||||
info "${COLOR_BOLD}[6/8]${COLOR_RESET} Writing settings..."
|
||||
write_settings
|
||||
|
||||
# --- Step 7: Start worker and verify ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[7/7]${COLOR_RESET} Starting worker service..."
|
||||
info "${COLOR_BOLD}[7/8]${COLOR_RESET} Starting worker service..."
|
||||
if start_worker; then
|
||||
verify_health || true
|
||||
else
|
||||
@@ -914,6 +1111,12 @@ main() {
|
||||
warn " cd ~/.claude/plugins/marketplaces/thedotmack && bun plugin/scripts/worker-service.cjs"
|
||||
fi
|
||||
|
||||
# --- Step 8: Observation feed setup (optional) ---
|
||||
echo ""
|
||||
info "${COLOR_BOLD}[8/8]${COLOR_RESET} Observation feed setup..."
|
||||
setup_observation_feed
|
||||
write_observation_feed_config
|
||||
|
||||
# --- Completion ---
|
||||
print_completion_summary
|
||||
}
|
||||
|
||||
+268
-9
@@ -700,18 +700,19 @@ echo "=== print_completion_summary() ==="
|
||||
test_print_completion_summary() {
|
||||
AI_PROVIDER="claude"
|
||||
WORKER_PID=""
|
||||
FEED_CONFIGURED=false
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
|
||||
local output
|
||||
output="$(print_completion_summary 2>&1)"
|
||||
|
||||
assert_contains "$output" "Installation Complete" "Completion summary shows 'Installation Complete'"
|
||||
assert_contains "$output" "Claude Max Plan" "Completion summary shows correct provider"
|
||||
assert_contains "$output" "Telegram" "Completion summary mentions Telegram channel"
|
||||
assert_contains "$output" "Discord" "Completion summary mentions Discord channel"
|
||||
assert_contains "$output" "Slack" "Completion summary mentions Slack channel"
|
||||
assert_contains "$output" "Signal" "Completion summary mentions Signal channel"
|
||||
assert_contains "$output" "WhatsApp" "Completion summary mentions WhatsApp channel"
|
||||
assert_contains "$output" "LINE" "Completion summary mentions LINE channel"
|
||||
assert_contains "$output" "not configured" "Completion summary shows feed 'not configured' when skipped"
|
||||
assert_contains "$output" "What's next" "Completion summary shows What's next section"
|
||||
assert_contains "$output" "/claude-mem-status" "Completion summary mentions status command"
|
||||
assert_contains "$output" "localhost:37777" "Completion summary mentions viewer URL"
|
||||
assert_contains "$output" "re-run this installer" "Completion summary shows re-run instructions"
|
||||
}
|
||||
|
||||
@@ -720,6 +721,7 @@ test_print_completion_summary
|
||||
test_print_completion_summary_gemini() {
|
||||
AI_PROVIDER="gemini"
|
||||
WORKER_PID=""
|
||||
FEED_CONFIGURED=false
|
||||
|
||||
local output
|
||||
output="$(print_completion_summary 2>&1)"
|
||||
@@ -732,6 +734,7 @@ test_print_completion_summary_gemini
|
||||
test_print_completion_summary_openrouter() {
|
||||
AI_PROVIDER="openrouter"
|
||||
WORKER_PID=""
|
||||
FEED_CONFIGURED=false
|
||||
|
||||
local output
|
||||
output="$(print_completion_summary 2>&1)"
|
||||
@@ -795,15 +798,271 @@ test_main_calls_completion_summary() {
|
||||
test_main_calls_completion_summary
|
||||
|
||||
test_main_has_progress_indicators() {
|
||||
if grep -q '\[1/7\]' "$INSTALL_SCRIPT" && grep -q '\[7/7\]' "$INSTALL_SCRIPT"; then
|
||||
test_pass "main() has progress indicators [1/7] through [7/7]"
|
||||
if grep -q '\[1/8\]' "$INSTALL_SCRIPT" && grep -q '\[8/8\]' "$INSTALL_SCRIPT"; then
|
||||
test_pass "main() has progress indicators [1/8] through [8/8]"
|
||||
else
|
||||
test_fail "main() should have progress indicators [1/7] through [7/7]"
|
||||
test_fail "main() should have progress indicators [1/8] through [8/8]"
|
||||
fi
|
||||
}
|
||||
|
||||
test_main_has_progress_indicators
|
||||
|
||||
test_main_calls_setup_observation_feed() {
|
||||
if grep -q 'setup_observation_feed' "$INSTALL_SCRIPT"; then
|
||||
test_pass "main() calls setup_observation_feed"
|
||||
else
|
||||
test_fail "main() should call setup_observation_feed"
|
||||
fi
|
||||
}
|
||||
|
||||
test_main_calls_setup_observation_feed
|
||||
|
||||
test_main_calls_write_observation_feed_config() {
|
||||
if grep -q 'write_observation_feed_config' "$INSTALL_SCRIPT"; then
|
||||
test_pass "main() calls write_observation_feed_config"
|
||||
else
|
||||
test_fail "main() should call write_observation_feed_config"
|
||||
fi
|
||||
}
|
||||
|
||||
test_main_calls_write_observation_feed_config
|
||||
|
||||
###############################################################################
|
||||
# Test: setup_observation_feed() — function exists and non-interactive skips
|
||||
###############################################################################
|
||||
|
||||
echo ""
|
||||
echo "=== setup_observation_feed() ==="
|
||||
|
||||
for fn in setup_observation_feed write_observation_feed_config; do
|
||||
if declare -f "$fn" &>/dev/null; then
|
||||
test_pass "Function ${fn}() is defined"
|
||||
else
|
||||
test_fail "Function ${fn}() should be defined"
|
||||
fi
|
||||
done
|
||||
|
||||
test_setup_observation_feed_non_interactive() {
|
||||
# Non-interactive mode should skip feed setup without error
|
||||
local feed_result
|
||||
feed_result="$(bash -c '
|
||||
set -euo pipefail
|
||||
TERM=dumb
|
||||
tmp=$(mktemp)
|
||||
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||
echo "main() { :; }" >> "$tmp"
|
||||
set -- "--non-interactive"
|
||||
source "$tmp"
|
||||
rm -f "$tmp"
|
||||
setup_observation_feed 2>/dev/null
|
||||
echo "CHANNEL=$FEED_CHANNEL"
|
||||
echo "CONFIGURED=$FEED_CONFIGURED"
|
||||
' 2>/dev/null)" || true
|
||||
|
||||
assert_contains "$feed_result" "CHANNEL=" "Non-interactive mode: FEED_CHANNEL is empty"
|
||||
assert_contains "$feed_result" "CONFIGURED=false" "Non-interactive mode: FEED_CONFIGURED is false"
|
||||
}
|
||||
|
||||
test_setup_observation_feed_non_interactive
|
||||
|
||||
###############################################################################
|
||||
# Test: write_observation_feed_config() — writes correct JSON structure
|
||||
###############################################################################
|
||||
|
||||
echo ""
|
||||
echo "=== write_observation_feed_config() ==="
|
||||
|
||||
test_write_observation_feed_config_writes_json() {
|
||||
local fake_home
|
||||
fake_home="$(mktemp -d)"
|
||||
HOME="$fake_home"
|
||||
|
||||
# Create an existing openclaw.json with claude-mem entry
|
||||
mkdir -p "${fake_home}/.openclaw"
|
||||
local config_file="${fake_home}/.openclaw/openclaw.json"
|
||||
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));
|
||||
"
|
||||
|
||||
FEED_CHANNEL="telegram"
|
||||
FEED_TARGET_ID="123456789"
|
||||
FEED_CONFIGURED="true"
|
||||
|
||||
write_observation_feed_config >/dev/null 2>&1
|
||||
|
||||
# Verify observationFeed was written
|
||||
local feed_enabled
|
||||
feed_enabled="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.observationFeed.enabled);")"
|
||||
assert_eq "true" "$feed_enabled" "observationFeed.enabled is true"
|
||||
|
||||
local feed_channel
|
||||
feed_channel="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.observationFeed.channel);")"
|
||||
assert_eq "telegram" "$feed_channel" "observationFeed.channel is telegram"
|
||||
|
||||
local feed_to
|
||||
feed_to="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.observationFeed.to);")"
|
||||
assert_eq "123456789" "$feed_to" "observationFeed.to is 123456789"
|
||||
|
||||
# Verify existing config preserved
|
||||
local worker_port
|
||||
worker_port="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.workerPort);")"
|
||||
assert_eq "37777" "$worker_port" "Existing workerPort preserved after feed config write"
|
||||
|
||||
HOME="$ORIGINAL_HOME"
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
FEED_CONFIGURED=false
|
||||
rm -rf "$fake_home"
|
||||
}
|
||||
|
||||
test_write_observation_feed_config_writes_json
|
||||
|
||||
test_write_observation_feed_config_skips_when_not_configured() {
|
||||
local fake_home
|
||||
fake_home="$(mktemp -d)"
|
||||
HOME="$fake_home"
|
||||
|
||||
# Create minimal config
|
||||
mkdir -p "${fake_home}/.openclaw"
|
||||
local config_file="${fake_home}/.openclaw/openclaw.json"
|
||||
node -e "
|
||||
require('fs').writeFileSync('${config_file}', JSON.stringify({ plugins: {} }, null, 2));
|
||||
"
|
||||
|
||||
FEED_CONFIGURED="false"
|
||||
|
||||
write_observation_feed_config >/dev/null 2>&1
|
||||
|
||||
# Config should be unchanged — no observationFeed key
|
||||
local has_feed
|
||||
has_feed="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries !== undefined);")"
|
||||
assert_eq "false" "$has_feed" "Config unchanged when FEED_CONFIGURED is false"
|
||||
|
||||
HOME="$ORIGINAL_HOME"
|
||||
rm -rf "$fake_home"
|
||||
}
|
||||
|
||||
test_write_observation_feed_config_skips_when_not_configured
|
||||
|
||||
test_write_observation_feed_config_discord() {
|
||||
local fake_home
|
||||
fake_home="$(mktemp -d)"
|
||||
HOME="$fake_home"
|
||||
|
||||
mkdir -p "${fake_home}/.openclaw"
|
||||
local config_file="${fake_home}/.openclaw/openclaw.json"
|
||||
node -e "
|
||||
const config = {
|
||||
plugins: {
|
||||
entries: {
|
||||
'claude-mem': { enabled: true, config: {} }
|
||||
}
|
||||
}
|
||||
};
|
||||
require('fs').writeFileSync('${config_file}', JSON.stringify(config, null, 2));
|
||||
"
|
||||
|
||||
FEED_CHANNEL="discord"
|
||||
FEED_TARGET_ID="1234567890123456789"
|
||||
FEED_CONFIGURED="true"
|
||||
|
||||
write_observation_feed_config >/dev/null 2>&1
|
||||
|
||||
local feed_channel
|
||||
feed_channel="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.observationFeed.channel);")"
|
||||
assert_eq "discord" "$feed_channel" "Discord channel type written correctly"
|
||||
|
||||
local feed_to
|
||||
feed_to="$(node -e "const c = JSON.parse(require('fs').readFileSync('${config_file}','utf8')); console.log(c.plugins.entries['claude-mem'].config.observationFeed.to);")"
|
||||
assert_eq "1234567890123456789" "$feed_to" "Discord channel ID written correctly"
|
||||
|
||||
HOME="$ORIGINAL_HOME"
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
FEED_CONFIGURED=false
|
||||
rm -rf "$fake_home"
|
||||
}
|
||||
|
||||
test_write_observation_feed_config_discord
|
||||
|
||||
###############################################################################
|
||||
# Test: print_completion_summary() — shows observation feed status
|
||||
###############################################################################
|
||||
|
||||
echo ""
|
||||
echo "=== print_completion_summary() — observation feed ==="
|
||||
|
||||
test_completion_summary_with_feed() {
|
||||
AI_PROVIDER="claude"
|
||||
WORKER_PID=""
|
||||
FEED_CONFIGURED="true"
|
||||
FEED_CHANNEL="telegram"
|
||||
FEED_TARGET_ID="123456789"
|
||||
|
||||
local output
|
||||
output="$(print_completion_summary 2>&1)"
|
||||
|
||||
assert_contains "$output" "telegram" "Summary shows feed channel when configured"
|
||||
assert_contains "$output" "123456789" "Summary shows feed target when configured"
|
||||
assert_contains "$output" "What's next" "Summary includes What's next section"
|
||||
assert_contains "$output" "/claude-mem-feed" "Summary includes feed check command when configured"
|
||||
|
||||
FEED_CONFIGURED=false
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
}
|
||||
|
||||
test_completion_summary_with_feed
|
||||
|
||||
test_completion_summary_without_feed() {
|
||||
AI_PROVIDER="claude"
|
||||
WORKER_PID=""
|
||||
FEED_CONFIGURED=false
|
||||
FEED_CHANNEL=""
|
||||
FEED_TARGET_ID=""
|
||||
|
||||
local output
|
||||
output="$(print_completion_summary 2>&1)"
|
||||
|
||||
assert_contains "$output" "not configured" "Summary shows 'not configured' when feed skipped"
|
||||
assert_contains "$output" "What's next" "Summary includes What's next section without feed"
|
||||
assert_contains "$output" "/claude-mem-status" "Summary includes status check command"
|
||||
assert_contains "$output" "localhost:37777" "Summary includes viewer URL"
|
||||
}
|
||||
|
||||
test_completion_summary_without_feed
|
||||
|
||||
###############################################################################
|
||||
# Test: Channel type instructions exist in install.sh
|
||||
###############################################################################
|
||||
|
||||
echo ""
|
||||
echo "=== Channel instructions ==="
|
||||
|
||||
for channel in telegram discord slack signal whatsapp line; do
|
||||
if grep -qi "$channel" "$INSTALL_SCRIPT"; then
|
||||
test_pass "Channel '${channel}' instructions exist in install.sh"
|
||||
else
|
||||
test_fail "Channel '${channel}' instructions should exist in install.sh"
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify specific instruction content
|
||||
assert_contains "$(grep -A2 'userinfobot' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "userinfobot" "Telegram instructions include @userinfobot"
|
||||
assert_contains "$(grep -A2 'Developer Mode' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "Developer Mode" "Discord instructions include Developer Mode"
|
||||
assert_contains "$(grep -A2 'C01ABC2DEFG' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "C01ABC2DEFG" "Slack instructions include sample channel ID"
|
||||
|
||||
###############################################################################
|
||||
# Summary
|
||||
###############################################################################
|
||||
|
||||
Reference in New Issue
Block a user