Skip to content

Commit e68332c

Browse files
Initial commit EA + Skills EA
1 parent a3743d6 commit e68332c

2 files changed

Lines changed: 344 additions & 0 deletions

File tree

support/openclaw_detection.sh

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/bin/bash
2+
####################################################################################################
3+
#
4+
# Copyright (c) 2022, Jamf Software, LLC. All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
# * Redistributions of source code must retain the above copyright
9+
# notice, this list of conditions and the following disclaimer.
10+
# * Redistributions in binary form must reproduce the above copyright
11+
# notice, this list of conditions and the following disclaimer in the
12+
# documentation and/or other materials provided with the distribution.
13+
# * Neither the name of the JAMF Software, LLC nor the
14+
# names of its contributors may be used to endorse or promote products
15+
# derived from this software without specific prior written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
18+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
# DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
21+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
#
28+
####################################################################################################
29+
30+
################################################################################
31+
# Detects OpenClaw installations including:
32+
# - CLI binary (npm/pnpm global install)
33+
# - LaunchAgent services (current and legacy)
34+
# - macOS companion app
35+
# - Configuration directories
36+
# - Running processes/gateway
37+
# - Docker containers
38+
################################################################################
39+
40+
# Initialize detection arrays
41+
declare -a findings
42+
43+
# Function to check if a command exists
44+
command_exists() {
45+
command -v "$1" >/dev/null 2>&1
46+
}
47+
48+
# Function to check Docker containers
49+
check_docker() {
50+
if command_exists docker; then
51+
# Check for running OpenClaw containers
52+
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qi openclaw; then
53+
container_names=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -i openclaw | tr '\n' ', ' | sed 's/,$//')
54+
findings+=("Docker-Running:${container_names}")
55+
fi
56+
57+
# Check for stopped OpenClaw containers
58+
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qi openclaw; then
59+
all_containers=$(docker ps -a --format '{{.Names}}' 2>/dev/null | grep -i openclaw | wc -l | tr -d ' ')
60+
if [[ $all_containers -gt 0 ]]; then
61+
findings+=("Docker-Containers:${all_containers}")
62+
fi
63+
fi
64+
fi
65+
}
66+
67+
# Function to check for CLI installation
68+
check_cli() {
69+
# Check common npm global bin locations
70+
local npm_prefix=""
71+
72+
if command_exists npm; then
73+
npm_prefix=$(npm prefix -g 2>/dev/null)
74+
if [[ -f "${npm_prefix}/bin/openclaw" ]]; then
75+
# Get version if possible
76+
local version=$("${npm_prefix}/bin/openclaw" --version 2>/dev/null | head -1)
77+
findings+=("CLI-NPM:${npm_prefix}/bin/openclaw")
78+
[[ -n "$version" ]] && findings+=("Version:${version}")
79+
fi
80+
fi
81+
82+
# Check standard locations
83+
if [[ -f "/usr/local/bin/openclaw" ]]; then
84+
findings+=("CLI-Binary:/usr/local/bin/openclaw")
85+
fi
86+
87+
# Check if openclaw command is available in PATH
88+
if command_exists openclaw && [[ ! " ${findings[@]} " =~ " CLI-" ]]; then
89+
local openclaw_path=$(which openclaw 2>/dev/null)
90+
findings+=("CLI-Path:${openclaw_path}")
91+
fi
92+
}
93+
94+
# Function to check macOS app
95+
check_macos_app() {
96+
if [[ -d "/Applications/OpenClaw.app" ]]; then
97+
# Get app version from Info.plist if available
98+
local app_version=""
99+
if [[ -f "/Applications/OpenClaw.app/Contents/Info.plist" ]]; then
100+
app_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "/Applications/OpenClaw.app/Contents/Info.plist" 2>/dev/null)
101+
fi
102+
findings+=("App:/Applications/OpenClaw.app")
103+
[[ -n "$app_version" ]] && findings+=("AppVersion:${app_version}")
104+
fi
105+
106+
# Check user Applications folders
107+
for user_home in /Users/*; do
108+
[[ ! -d "$user_home" ]] && continue
109+
[[ "$user_home" == "/Users/Shared" ]] && continue
110+
111+
if [[ -d "${user_home}/Applications/OpenClaw.app" ]]; then
112+
local username=$(basename "$user_home")
113+
findings+=("App-User:${username}")
114+
fi
115+
done
116+
}
117+
118+
# Function to check LaunchAgents
119+
check_launch_agents() {
120+
for user_home in /Users/*; do
121+
[[ ! -d "$user_home" ]] && continue
122+
[[ "$user_home" == "/Users/Shared" ]] && continue
123+
124+
local username=$(basename "$user_home")
125+
local launch_agents_dir="${user_home}/Library/LaunchAgents"
126+
127+
# Check current naming convention
128+
if [[ -f "${launch_agents_dir}/bot.molt.gateway.plist" ]]; then
129+
# Check if it's loaded
130+
local is_loaded=$(sudo -u "$username" launchctl list 2>/dev/null | grep -c "bot.molt.gateway")
131+
if [[ $is_loaded -gt 0 ]]; then
132+
findings+=("LaunchAgent-Active:${username}")
133+
else
134+
findings+=("LaunchAgent-Installed:${username}")
135+
fi
136+
fi
137+
138+
# Check legacy naming convention
139+
if [[ -f "${launch_agents_dir}/com.openclaw.gateway.plist" ]]; then
140+
findings+=("LaunchAgent-Legacy:${username}")
141+
fi
142+
143+
# Check for profile-based agents (bot.molt.*)
144+
local profile_agents=$(find "${launch_agents_dir}" -name "bot.molt.*.plist" 2>/dev/null | wc -l | tr -d ' ')
145+
if [[ $profile_agents -gt 0 ]]; then
146+
findings+=("LaunchAgent-Profiles:${username}:${profile_agents}")
147+
fi
148+
done
149+
}
150+
151+
# Function to check configuration directories
152+
check_config_dirs() {
153+
local found_configs=0
154+
155+
for user_home in /Users/*; do
156+
[[ ! -d "$user_home" ]] && continue
157+
[[ "$user_home" == "/Users/Shared" ]] && continue
158+
159+
local username=$(basename "$user_home")
160+
local config_dir="${user_home}/.openclaw"
161+
162+
if [[ -d "$config_dir" ]]; then
163+
found_configs=$((found_configs + 1))
164+
165+
# Check for main config file
166+
if [[ -f "${config_dir}/openclaw.json" ]]; then
167+
findings+=("Config:${username}")
168+
169+
# Check workspace
170+
if [[ -d "${config_dir}/workspace" ]]; then
171+
findings+=("Workspace:${username}")
172+
fi
173+
174+
# Check for credentials
175+
if [[ -f "${config_dir}/credentials/profiles.json" ]]; then
176+
findings+=("Credentials:${username}")
177+
fi
178+
fi
179+
fi
180+
181+
# Check for workspace directory (could be separate)
182+
if [[ -d "${user_home}/openclaw/workspace" ]]; then
183+
findings+=("Workspace-Alt:${username}")
184+
fi
185+
done
186+
187+
[[ $found_configs -gt 0 ]] && findings+=("ConfigDirs:${found_configs}")
188+
}
189+
190+
# Function to check running processes
191+
check_processes() {
192+
# Check for openclaw processes
193+
if pgrep -f "openclaw" >/dev/null 2>&1; then
194+
local process_count=$(pgrep -f "openclaw" | wc -l | tr -d ' ')
195+
findings+=("Process-Running:${process_count}")
196+
197+
# Check if Gateway is running on default port
198+
if lsof -i :18789 >/dev/null 2>&1; then
199+
findings+=("Gateway-Port:18789")
200+
fi
201+
fi
202+
203+
# Check for node processes that might be running openclaw
204+
if pgrep -f "node.*openclaw" >/dev/null 2>&1; then
205+
local node_count=$(pgrep -f "node.*openclaw" | wc -l | tr -d ' ')
206+
[[ $node_count -gt 0 ]] && findings+=("Node-Process:${node_count}")
207+
fi
208+
}
209+
210+
# Function to check for source installation
211+
check_source_install() {
212+
for user_home in /Users/*; do
213+
[[ ! -d "$user_home" ]] && continue
214+
[[ "$user_home" == "/Users/Shared" ]] && continue
215+
216+
local username=$(basename "$user_home")
217+
218+
# Check common locations for git clone
219+
for dir in "${user_home}/openclaw" "${user_home}/Documents/openclaw" "${user_home}/Projects/openclaw" "${user_home}/src/openclaw"; do
220+
if [[ -d "$dir" ]] && [[ -f "$dir/package.json" ]]; then
221+
# Verify it's actually openclaw by checking package.json
222+
if grep -q '"name": "openclaw"' "$dir/package.json" 2>/dev/null; then
223+
findings+=("Source:${username}:${dir}")
224+
fi
225+
fi
226+
done
227+
done
228+
}
229+
230+
# Run all checks
231+
check_docker
232+
check_cli
233+
check_macos_app
234+
check_launch_agents
235+
check_config_dirs
236+
check_processes
237+
check_source_install
238+
239+
# Format and return results
240+
if [[ ${#findings[@]} -eq 0 ]]; then
241+
echo "<result>Not Installed</result>"
242+
else
243+
# Join findings with semicolon separator
244+
result=$(IFS=';'; echo "${findings[*]}")
245+
echo "<result>${result}</result>"
246+
fi
247+
248+
exit 0
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/bash
2+
3+
####################################################################################################
4+
#
5+
# Copyright (c) 2022, Jamf Software, LLC. All rights reserved.
6+
#
7+
# Redistribution and use in source and binary forms, with or without
8+
# modification, are permitted provided that the following conditions are met:
9+
# * Redistributions of source code must retain the above copyright
10+
# notice, this list of conditions and the following disclaimer.
11+
# * Redistributions in binary form must reproduce the above copyright
12+
# notice, this list of conditions and the following disclaimer in the
13+
# documentation and/or other materials provided with the distribution.
14+
# * Neither the name of the JAMF Software, LLC nor the
15+
# names of its contributors may be used to endorse or promote products
16+
# derived from this software without specific prior written permission.
17+
#
18+
# THIS SOFTWARE IS PROVIDED BY JAMF SOFTWARE, LLC "AS IS" AND ANY
19+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
# DISCLAIMED. IN NO EVENT SHALL JAMF SOFTWARE, LLC BE LIABLE FOR ANY
22+
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
#
29+
####################################################################################################
30+
31+
32+
# Function to find openclaw directory
33+
find_openclaw_dir() {
34+
# Get the current console user
35+
CURRENT_USER=$(stat -f "%Su" /dev/console 2>/dev/null || who | awk '/console/ {print $1}' | head -n 1)
36+
37+
# If we can't determine user, try logname or $USER
38+
if [ -z "$CURRENT_USER" ]; then
39+
CURRENT_USER=$(logname 2>/dev/null || echo "$USER")
40+
fi
41+
42+
# Get user's home directory
43+
if [ -n "$CURRENT_USER" ]; then
44+
USER_HOME=$(eval echo "~$CURRENT_USER")
45+
else
46+
USER_HOME="$HOME"
47+
fi
48+
49+
# Define openclaw config path
50+
OPENCLAW_DIR="${USER_HOME}/.openclaw"
51+
52+
echo "$OPENCLAW_DIR"
53+
}
54+
55+
# Main execution
56+
OPENCLAW_DIR=$(find_openclaw_dir)
57+
OPENCLAW_JSON="${OPENCLAW_DIR}/openclaw.json"
58+
59+
# Check if openclaw is installed
60+
if [ ! -d "$OPENCLAW_DIR" ]; then
61+
echo "<result>Not Installed</result>"
62+
exit 0
63+
fi
64+
65+
# Check if config file exists
66+
if [ ! -f "$OPENCLAW_JSON" ]; then
67+
echo "<result>Config File Not Found</result>"
68+
exit 0
69+
fi
70+
71+
# Check if jq is available
72+
if ! command -v jq &> /dev/null; then
73+
echo "<result>jq Not Available</result>"
74+
exit 0
75+
fi
76+
77+
# Extract enabled skills
78+
ENABLED_SKILLS=$(cat "$OPENCLAW_JSON" | jq -r '.skills.entries | to_entries | .[] | select(.value.enabled).key' 2>/dev/null)
79+
80+
# Check if extraction was successful
81+
if [ $? -ne 0 ]; then
82+
echo "<result>Error Parsing JSON</result>"
83+
exit 0
84+
fi
85+
86+
# Check if any skills were found
87+
if [ -z "$ENABLED_SKILLS" ]; then
88+
echo "<result>No Enabled Skills</result>"
89+
exit 0
90+
fi
91+
92+
# Format output: Convert newline-separated skills to comma-separated
93+
SKILLS_LIST=$(echo "$ENABLED_SKILLS" | tr '\n' ',' | sed 's/,$//')
94+
95+
echo "<result>$SKILLS_LIST</result>"
96+
exit 0

0 commit comments

Comments
 (0)