Skip to content

Commit 7078a90

Browse files
1st
1 parent 2d09bdf commit 7078a90

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

support/openclaw_detection.sh

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/bin/bash
2+
3+
###########################################################################################################################
4+
#
5+
# Copyright 2026, Jamf Software LLC.
6+
# This work is licensed under the terms of the Jamf Source Available License
7+
# https://github.com/jamf/scripts/blob/main/LICENCE.md
8+
#
9+
###########################################################################################################################
10+
11+
################################################################################
12+
# Detects OpenClaw installations including:
13+
# - CLI binary (npm/pnpm global install)
14+
# - LaunchAgent services (current and legacy)
15+
# - macOS companion app
16+
# - Configuration directories
17+
# - Running processes/gateway
18+
# - Docker containers
19+
################################################################################
20+
21+
# Initialize detection arrays
22+
declare -a findings
23+
24+
# Function to check if a command exists
25+
command_exists() {
26+
command -v "$1" >/dev/null 2>&1
27+
}
28+
29+
# Function to check Docker containers
30+
check_docker() {
31+
if command_exists docker; then
32+
# Check for running OpenClaw containers
33+
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qi openclaw; then
34+
container_names=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i openclaw | tr '\n' ', ' | sed 's/,$//')
35+
findings+=("Docker-Running:${container_names}")
36+
fi
37+
38+
# Check for stopped OpenClaw containers
39+
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qi openclaw; then
40+
all_containers=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -i openclaw | wc -l | tr -d ' ')
41+
if [[ $all_containers -gt 0 ]]; then
42+
findings+=("Docker-Containers:${all_containers}")
43+
fi
44+
fi
45+
fi
46+
}
47+
48+
# Function to check for CLI installation
49+
check_cli() {
50+
# Check common npm global bin locations
51+
local npm_prefix=""
52+
53+
if command_exists npm; then
54+
npm_prefix=$(npm prefix -g 2>/dev/null)
55+
if [[ -f "${npm_prefix}/bin/openclaw" ]]; then
56+
# Get version if possible
57+
local version=$("${npm_prefix}/bin/openclaw" --version 2>/dev/null | head -1)
58+
findings+=("CLI-NPM:${npm_prefix}/bin/openclaw")
59+
[[ -n "$version" ]] && findings+=("Version:${version}")
60+
fi
61+
fi
62+
63+
# Check standard locations
64+
if [[ -f "/usr/local/bin/openclaw" ]]; then
65+
findings+=("CLI-Binary:/usr/local/bin/openclaw")
66+
fi
67+
68+
# Check if openclaw command is available in PATH
69+
if command_exists openclaw && [[ ! " ${findings[@]} " =~ " CLI-" ]]; then
70+
local openclaw_path=$(which openclaw 2>/dev/null)
71+
findings+=("CLI-Path:${openclaw_path}")
72+
fi
73+
}
74+
75+
# Function to check macOS app
76+
check_macos_app() {
77+
if [[ -d "/Applications/OpenClaw.app" ]]; then
78+
# Get app version from Info.plist if available
79+
local app_version=""
80+
if [[ -f "/Applications/OpenClaw.app/Contents/Info.plist" ]]; then
81+
app_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "/Applications/OpenClaw.app/Contents/Info.plist" 2>/dev/null)
82+
fi
83+
findings+=("App:/Applications/OpenClaw.app")
84+
[[ -n "$app_version" ]] && findings+=("AppVersion:${app_version}")
85+
fi
86+
87+
# Check user Applications folders
88+
for user_home in /Users/*; do
89+
[[ ! -d "$user_home" ]] && continue
90+
[[ "$user_home" == "/Users/Shared" ]] && continue
91+
92+
if [[ -d "${user_home}/Applications/OpenClaw.app" ]]; then
93+
local username=$(basename "$user_home")
94+
findings+=("App-User:${username}")
95+
fi
96+
done
97+
}
98+
99+
# Function to check LaunchAgents
100+
check_launch_agents() {
101+
for user_home in /Users/*; do
102+
[[ ! -d "$user_home" ]] && continue
103+
[[ "$user_home" == "/Users/Shared" ]] && continue
104+
105+
local username=$(basename "$user_home")
106+
local launch_agents_dir="${user_home}/Library/LaunchAgents"
107+
108+
# Check current naming convention
109+
if [[ -f "${launch_agents_dir}/bot.molt.gateway.plist" ]]; then
110+
# Check if it's loaded
111+
local is_loaded=$(sudo -u "$username" launchctl list 2>/dev/null | grep -c "bot.molt.gateway")
112+
if [[ $is_loaded -gt 0 ]]; then
113+
findings+=("LaunchAgent-Active:${username}")
114+
else
115+
findings+=("LaunchAgent-Installed:${username}")
116+
fi
117+
fi
118+
119+
# Check legacy naming convention
120+
if [[ -f "${launch_agents_dir}/com.openclaw.gateway.plist" ]]; then
121+
findings+=("LaunchAgent-Legacy:${username}")
122+
fi
123+
124+
# Check for profile-based agents (bot.molt.*)
125+
local profile_agents=$(find "${launch_agents_dir}" -name "bot.molt.*.plist" 2>/dev/null | wc -l | tr -d ' ')
126+
if [[ $profile_agents -gt 0 ]]; then
127+
findings+=("LaunchAgent-Profiles:${username}:${profile_agents}")
128+
fi
129+
done
130+
}
131+
132+
# Function to check configuration directories
133+
check_config_dirs() {
134+
local found_configs=0
135+
136+
for user_home in /Users/*; do
137+
[[ ! -d "$user_home" ]] && continue
138+
[[ "$user_home" == "/Users/Shared" ]] && continue
139+
140+
local username=$(basename "$user_home")
141+
local config_dir="${user_home}/.openclaw"
142+
143+
if [[ -d "$config_dir" ]]; then
144+
found_configs=$((found_configs + 1))
145+
146+
# Check for main config file
147+
if [[ -f "${config_dir}/openclaw.json" ]]; then
148+
findings+=("Config:${username}")
149+
150+
# Check workspace
151+
if [[ -d "${config_dir}/workspace" ]]; then
152+
findings+=("Workspace:${username}")
153+
fi
154+
155+
# Check for credentials
156+
if [[ -f "${config_dir}/credentials/profiles.json" ]]; then
157+
findings+=("Credentials:${username}")
158+
fi
159+
fi
160+
fi
161+
162+
# Check for workspace directory (could be separate)
163+
if [[ -d "${user_home}/openclaw/workspace" ]]; then
164+
findings+=("Workspace-Alt:${username}")
165+
fi
166+
done
167+
168+
[[ $found_configs -gt 0 ]] && findings+=("ConfigDirs:${found_configs}")
169+
}
170+
171+
# Function to check running processes
172+
check_processes() {
173+
# Check for openclaw processes
174+
if pgrep -f "openclaw" >/dev/null 2>&1; then
175+
local process_count=$(pgrep -f "openclaw" | wc -l | tr -d ' ')
176+
findings+=("Process-Running:${process_count}")
177+
178+
# Check if Gateway is running on default port
179+
if lsof -i :18789 >/dev/null 2>&1; then
180+
findings+=("Gateway-Port:18789")
181+
fi
182+
fi
183+
184+
# Check for node processes that might be running openclaw
185+
if pgrep -f "node.*openclaw" >/dev/null 2>&1; then
186+
local node_count=$(pgrep -f "node.*openclaw" | wc -l | tr -d ' ')
187+
[[ $node_count -gt 0 ]] && findings+=("Node-Process:${node_count}")
188+
fi
189+
}
190+
191+
# Function to check for source installation
192+
check_source_install() {
193+
for user_home in /Users/*; do
194+
[[ ! -d "$user_home" ]] && continue
195+
[[ "$user_home" == "/Users/Shared" ]] && continue
196+
197+
local username=$(basename "$user_home")
198+
199+
# Check common locations for git clone
200+
for dir in "${user_home}/openclaw" "${user_home}/Documents/openclaw" "${user_home}/Projects/openclaw" "${user_home}/src/openclaw"; do
201+
if [[ -d "$dir" ]] && [[ -f "$dir/package.json" ]]; then
202+
# Verify it's actually openclaw by checking package.json
203+
if grep -q '"name": "openclaw"' "$dir/package.json" 2>/dev/null; then
204+
findings+=("Source:${username}:${dir}")
205+
fi
206+
fi
207+
done
208+
done
209+
}
210+
211+
# Run all checks
212+
check_docker
213+
check_cli
214+
check_macos_app
215+
check_launch_agents
216+
check_config_dirs
217+
check_processes
218+
check_source_install
219+
220+
# Format and return results
221+
if [[ ${#findings[@]} -eq 0 ]]; then
222+
echo "<result>Not Installed</result>"
223+
else
224+
# Join findings with semicolon separator
225+
result=$(IFS=';'; echo "${findings[*]}")
226+
echo "<result>${result}</result>"
227+
fi
228+
229+
exit 0
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/bin/bash
2+
3+
###########################################################################################################################
4+
#
5+
# Copyright 2026, Jamf Software LLC.
6+
# This work is licensed under the terms of the Jamf Source Available License
7+
# https://github.com/jamf/scripts/blob/main/LICENCE.md
8+
#
9+
###########################################################################################################################
10+
11+
# Function to find openclaw directory
12+
find_openclaw_dir() {
13+
# Get the current console user
14+
CURRENT_USER=$(stat -f "%Su" /dev/console 2>/dev/null || who | awk '/console/ {print $1}' | head -n 1)
15+
16+
# If we can't determine user, try logname or $USER
17+
if [ -z "$CURRENT_USER" ]; then
18+
CURRENT_USER=$(logname 2>/dev/null || echo "$USER")
19+
fi
20+
21+
# Get user's home directory
22+
if [ -n "$CURRENT_USER" ]; then
23+
USER_HOME=$(eval echo "~$CURRENT_USER")
24+
else
25+
USER_HOME="$HOME"
26+
fi
27+
28+
# Define openclaw config path
29+
OPENCLAW_DIR="${USER_HOME}/.openclaw"
30+
31+
echo "$OPENCLAW_DIR"
32+
}
33+
34+
# Main execution
35+
OPENCLAW_DIR=$(find_openclaw_dir)
36+
OPENCLAW_JSON="${OPENCLAW_DIR}/openclaw.json"
37+
38+
# Check if openclaw is installed
39+
if [ ! -d "$OPENCLAW_DIR" ]; then
40+
echo "<result>Not Installed</result>"
41+
exit 0
42+
fi
43+
44+
# Check if config file exists
45+
if [ ! -f "$OPENCLAW_JSON" ]; then
46+
echo "<result>Config File Not Found</result>"
47+
exit 0
48+
fi
49+
50+
# Check if jq is available
51+
if ! command -v jq &> /dev/null; then
52+
echo "<result>jq Not Available</result>"
53+
exit 0
54+
fi
55+
56+
# Extract enabled skills
57+
ENABLED_SKILLS=$(cat "$OPENCLAW_JSON" | jq -r '.skills.entries | to_entries | .[] | select(.value.enabled).key' 2>/dev/null)
58+
59+
# Check if extraction was successful
60+
if [ $? -ne 0 ]; then
61+
echo "<result>Error Parsing JSON</result>"
62+
exit 0
63+
fi
64+
65+
# Check if any skills were found
66+
if [ -z "$ENABLED_SKILLS" ]; then
67+
echo "<result>No Enabled Skills</result>"
68+
exit 0
69+
fi
70+
71+
# Format output: Convert newline-separated skills to comma-separated
72+
SKILLS_LIST=$(echo "$ENABLED_SKILLS" | tr '\n' ',' | sed 's/,$//')
73+
74+
echo "<result>$SKILLS_LIST</result>"
75+
exit 0

0 commit comments

Comments
 (0)