Install OpenClaw as the self-updating copy under ~/.openclaw

This commit is contained in:
Spencer Bull committed 2026-09-25 23:48:46 -05:00
1 parent 7b336b1b0d
commit be18b324f8
9 files changed
+353 -38

No files matched your search

+5 -4
View File
@@ -50,10 +50,11 @@ esac
agent_package=${agent_package:-$agent}
# Hermes and OpenClaw are not mise tools: Hermes is the desktop app's
# self-updating runtime and OpenClaw comes from its pacman package. Each has
# its own installer with the same --check/--now contract mise-backed agents
# get from mise; see omarchy-install-hermes-cli and omarchy-install-openclaw-cli.
# Hermes and OpenClaw are not mise tools: each is a self-updating runtime in
# the user's home, Hermes the desktop app's and OpenClaw the one its Control
# UI updates. Each has its own installer with the same --check/--now contract
# mise-backed agents get from mise; see omarchy-install-hermes-cli and
# omarchy-install-openclaw-cli.
if [[ -n ${agent_installer:-} ]]; then
# Not omarchy-cmd-present: a command on PATH says nothing about whether the
# install behind it finished or runs the sessions omarchy-agent starts.
+3 -3
View File
@@ -5,8 +5,8 @@
set -e
echo "Installing OpenClaw..."
omarchy-pkg-add openclaw
# The same self-updating install the default agent gets.
omarchy-install-openclaw-cli --now
# The desktop app is OpenClaw's Control UI: a web app served by its own
# gateway. The launcher entry goes through omarchy-launch-openclaw, which
@@ -14,7 +14,7 @@ omarchy-pkg-add openclaw
# inside the package, so nothing is fetched here.
echo "Installing the OpenClaw web app..."
omarchy-webapp-install OpenClaw "http://127.0.0.1:18789" \
/usr/lib/node_modules/openclaw/dist/control-ui/apple-touch-icon.png \
/usr/share/openclaw/openclaw.png \
omarchy-launch-openclaw
# A first install runs onboarding right here: launching the app instead would
+114 -16
View File
@@ -1,29 +1,127 @@
#!/bin/bash
# omarchy:summary=Ensure the OpenClaw CLI is installed for the default agent
# omarchy:args=[--check|--now]
# omarchy:summary=Install OpenClaw for the default agent as the self-updating copy under ~/.openclaw
# omarchy:args=<--check|--now>
# omarchy:examples=omarchy install openclaw cli --now | omarchy install openclaw cli --check
# omarchy:requires-sudo=true
# OpenClaw is not a mise tool: the openclaw pacman package is the one OpenClaw
# installation on the machine — the CLI, the gateway, and the Install > AI web
# app all share it, and the fast ring keeps it current. The default-agent flow
# talks to that package through the same --check/--now contract mise-backed
# agents get from mise itself.
# There is one OpenClaw on a machine, and it is the one under ~/.openclaw that
# upstream's install-cli.sh makes: a private Node and the package in a prefix
# the user owns, which `openclaw update` and the Control UI's Update button
# replace in place. A copy in /usr belongs to pacman, and upstream's updater
# refuses to touch it. The openclaw package is therefore the seed rather than
# the runtime: the release Omarchy ships and the installer that came with it.
# The CLI, the gateway service and the Install > AI web app all run this copy.
#
# Each mode is named outright, like omarchy-install-hermes-cli: the default
# agent asks --check first and opens a terminal for --now only on a no.
set -euo pipefail
case "${1:---now}" in
mode=${1:-}
prefix="$HOME/.openclaw"
runtime_command="$prefix/bin/openclaw"
command_path="$HOME/.local/bin/openclaw"
seed=/usr/share/openclaw
# Usable means the command answers, not that the file is there: upstream's
# wrapper execs the prefix's own Node, so a prefix missing either fails here.
runtime_runs() {
[[ -f $runtime_command && -x $runtime_command ]] &&
timeout 30 "$runtime_command" --version >/dev/null 2>&1
}
# The name on PATH is a link to the runtime's command. A link already aimed
# there, even one left dangling by a removed runtime, is Omarchy's to rewrite;
# anything else at the path is the user's.
command_ours() {
[[ ! -e $command_path && ! -L $command_path ]] || [[ $(readlink -- "$command_path") == "$runtime_command" ]]
}
# What omarchy-agent, the web app and a terminal run is whichever openclaw is
# first on PATH, and Omarchy puts /usr/bin and the mise shims ahead of
# ~/.local/bin, so the command has to resolve to the runtime from there.
on_path() {
local found
found=$(type -P openclaw) || return 1
[[ $(realpath -m -- "$found") == "$(realpath -m -- "$runtime_command")" ]]
}
installed() {
runtime_runs && on_path
}
# A service the openclaw package's own copy installed runs from
# /usr/lib/node_modules/openclaw, which that package no longer ships. Installed
# again from the runtime, it runs code `openclaw update` can replace; upstream
# rewrites the unit and restarts it. A service running any other OpenClaw is
# the user's arrangement and stays.
rehome_services() {
local role unit
for role in gateway node; do
unit="$HOME/.config/systemd/user/openclaw-$role.service"
if [[ -f $unit ]] && grep -qF "/usr/lib/node_modules/openclaw/" "$unit"; then
echo "Moving the OpenClaw $role service to $prefix..."
if ! "$runtime_command" "$role" install --force; then
echo "Could not move the OpenClaw $role service. Finish with: openclaw $role install --force" >&2
return 1
fi
fi
done
}
case "$mode" in
--check)
omarchy-pkg-present openclaw
;;
--now)
if ! omarchy-pkg-present openclaw; then
echo "Installing OpenClaw..."
omarchy-pkg-add openclaw
fi
if installed; then exit 0; else exit 1; fi
;;
--now) ;;
*)
echo "Usage: omarchy-install-openclaw-cli [--check|--now]" >&2
echo "Usage: omarchy-install-openclaw-cli <--check|--now>" >&2
exit 1
;;
esac
if (( EUID == 0 )); then
echo "Run this command as your desktop user, without sudo." >&2
exit 1
fi
if ! omarchy-pkg-present openclaw; then
echo "Installing OpenClaw..."
omarchy-pkg-add openclaw
fi
# A runtime that already answers is never reinstalled: it may be past the
# packaged release by its own updates, and seeding would take it back.
if ! runtime_runs; then
if [[ ! -r $seed/install-cli.sh || ! -r $seed/openclaw.tgz ]]; then
echo "The installed OpenClaw package cannot set up a self-updating OpenClaw. Run 'omarchy update', then try again." >&2
exit 1
fi
# Every choice the installer reads from the environment is named, so an
# OPENCLAW_INSTALL_METHOD or OPENCLAW_PREFIX left in a shell cannot move it.
echo "Setting up OpenClaw in $prefix..."
bash "$seed/install-cli.sh" --install-method npm --prefix "$prefix" --version "$seed/openclaw.tgz" --no-onboard
if ! runtime_runs; then
echo "OpenClaw setup did not complete. Re-run this command after resolving the installer error." >&2
exit 1
fi
fi
if command_ours; then
mkdir -p "${command_path%/*}"
ln -sfn "$runtime_command" "$command_path"
else
echo "$command_path is not the OpenClaw Omarchy set up. Move it aside, then run omarchy-install-openclaw-cli --now again." >&2
exit 1
fi
rehome_services
if ! on_path; then
echo "$runtime_command is ready, but 'openclaw' on PATH is $(type -P openclaw || echo missing), which is what Omarchy runs." >&2
echo "Remove it or reorder PATH, then run omarchy-install-openclaw-cli --now again." >&2
exit 1
fi
+16 -2
View File
@@ -49,6 +49,19 @@ done
omarchy-pkg-drop openclaw
# The runtime omarchy-install-openclaw-cli set up lives beside the user's state
# in ~/.openclaw: the command upstream's installer wrote, and the Node and
# package under tools/. Only a command that execs into those tools is the
# installer's; the link on PATH goes only when it points at that command.
if [[ -f $HOME/.openclaw/bin/openclaw ]] && grep -qF "\"$HOME/.openclaw/tools/" "$HOME/.openclaw/bin/openclaw"; then
rm -rf "$HOME/.openclaw/tools"
rm -f "$HOME/.openclaw/bin/openclaw"
rmdir "$HOME/.openclaw/bin" 2>/dev/null || true
fi
if [[ -L $HOME/.local/bin/openclaw && $(readlink -- "$HOME/.local/bin/openclaw") == "$HOME/.openclaw/bin/openclaw" ]]; then
rm -f "$HOME/.local/bin/openclaw"
fi
# The web app launcher and icon omarchy-install-ai-openclaw created.
rm -f "$HOME/.local/share/applications/OpenClaw.desktop"
rm -f "$HOME/.local/share/icons/hicolor/256x256/apps/openclaw.png"
@@ -59,12 +72,13 @@ gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
# better surprise. But it also holds the plugin runtimes and caches OpenClaw
# downloads for itself, easily hundreds of megabytes, so the choice is put in
# front of the user with the size rather than left silent. Without a terminal
# to ask in, keeping it is the answer.
# to ask in, keeping it is the answer. The snapshots `openclaw update` takes
# of that state sit beside it and go with it.
state_removed=false
if [[ -d $HOME/.openclaw && -t 0 ]]; then
size=$(du -sh "$HOME/.openclaw" 2>/dev/null | cut -f1)
if gum confirm --default=false "Also delete ~/.openclaw ($size: chats, memories, credentials, and downloaded plugins)?"; then
rm -rf "$HOME/.openclaw"
rm -rf "$HOME/.openclaw" "$HOME/.openclaw.update-captures"
state_removed=true
fi
fi