Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c668141e9c | ||
|
|
8f3ab025b8 | ||
|
|
28d5251e23 | ||
|
|
cbaab83a1e | ||
|
|
dc0d894584 | ||
|
|
ec73ba5d00 | ||
|
|
0534987009 | ||
|
|
b9657b6313 | ||
|
|
15b4928e11 | ||
|
|
4ea7ca2b8b | ||
|
|
f4d156c09a | ||
|
|
6ba4ceef57 | ||
|
|
3248aa55c2 | ||
|
|
7cc9057413 | ||
|
|
d57cec42d4 | ||
|
|
c5b59bcfff | ||
|
|
a9e1c01ee9 | ||
|
|
4064436eea | ||
|
|
2cdcafa5b5 | ||
|
|
461cbe9b9c | ||
|
|
256bfeaa75 | ||
|
|
36695fbe84 | ||
|
|
9d95fc7f01 | ||
|
|
e919a64014 | ||
|
|
866d994dcf | ||
|
|
cf459ba752 | ||
|
|
1fe83ff129 | ||
|
|
6a72d4236b | ||
|
|
7a35a8ee9b | ||
|
|
6d327ce83c | ||
|
|
f94c6db34d | ||
|
|
8d1a7ef00c | ||
|
|
6cc561b8d3 | ||
|
|
6cb5396dc7 | ||
|
|
27594ba3f5 | ||
|
|
be6c401200 | ||
|
|
d49dd939b7 | ||
|
|
68e3d7a2ec | ||
|
|
15d64a7025 | ||
|
|
82de446713 | ||
|
|
ebaad12947 | ||
|
|
cf6fedc9f5 | ||
|
|
fb257bf872 | ||
|
|
1cbe72e5dd | ||
|
|
b1cdec9e47 | ||
|
|
af6f64fa3a | ||
|
|
346e69e1ce | ||
|
|
0e1ce609d8 | ||
|
|
3cb3b2d20b | ||
|
|
f8e35ec654 | ||
|
|
f802ae107d | ||
|
|
6f8b62f04c | ||
|
|
9aa0545c2a | ||
|
|
ebd6480387 | ||
|
|
07579f5e94 | ||
|
|
ff4cf8afbc | ||
|
|
cf3d69c38d | ||
|
|
47ce81ebca | ||
|
|
2421e76fb8 | ||
|
|
bffc6a2a5d | ||
|
|
7678e5b0d8 | ||
|
|
be730884f6 | ||
|
|
a73a312e1b | ||
|
|
159163bb16 | ||
|
|
521779b114 | ||
|
|
51af7bf794 | ||
|
|
92c8f87730 | ||
|
|
3bb9867245 | ||
|
|
da0fe6d89f | ||
|
|
2645b82bcd | ||
|
|
9c1ec524cd | ||
|
|
880605f22b | ||
|
|
294c38a2f0 | ||
|
|
1082b7864d |
No files matched your search
@@ -70,10 +70,6 @@ magick -background white -fill black -font default/fonts/omarchy/omarchy.ttf \
|
|||||||
-pointsize 110 label:@/tmp/row.txt /tmp/font-row.png
|
-pointsize 110 label:@/tmp/row.txt /tmp/font-row.png
|
||||||
```
|
```
|
||||||
|
|
||||||
Then confirm it in the running menu per
|
Then confirm it in the running menu per [`visual-verification.md`](visual-verification.md). Avoid leaving two fonts with the `omarchy` family registered: Qt can use an old copy in `~/.local/share/fonts` even when `fc-match omarchy` reports the packaged font. For a preview, either replace the packaged file in the disposable VM or temporarily exclude it with a `<rejectfont>` rule in `~/.config/fontconfig/conf.d/` before loading the candidate.
|
||||||
[`visual-verification.md`](visual-verification.md). Fontconfig prefers the
|
|
||||||
packaged font over a copy in `~/.local/share/fonts` for the same family, so a
|
Refresh the font cache and restart the shell afterwards — Qt reads the font database at startup, so `omarchy menu refresh` alone will not pick up a changed font. Remove temporary fonts and rules after verification.
|
||||||
preview needs either the real file replaced or a `<rejectfont>` rule in
|
|
||||||
`~/.config/fontconfig/conf.d/` pointing fontconfig away from the packaged one.
|
|
||||||
Restart the shell afterwards — Qt reads the font database at startup, so
|
|
||||||
`omarchy menu refresh` alone will not pick up a changed font.
|
|
||||||
@@ -13,6 +13,7 @@ commands and reusable setup leaves:
|
|||||||
- use `$OMARCHY_INSTALL` and `$OMARCHY_PATH` instead of hard-coded Omarchy paths.
|
- use `$OMARCHY_INSTALL` and `$OMARCHY_PATH` instead of hard-coded Omarchy paths.
|
||||||
- keep root-scoped hardware setup under `install/hardware/` and orchestrate it through `install/hardware/all.sh`.
|
- keep root-scoped hardware setup under `install/hardware/` and orchestrate it through `install/hardware/all.sh`.
|
||||||
- keep every per-user setup leaf under `install/user/` (including `install/user/hardware/` and `install/user/first-run/`) so it is clear what must run for each user.
|
- keep every per-user setup leaf under `install/user/` (including `install/user/hardware/` and `install/user/first-run/`) so it is clear what must run for each user.
|
||||||
|
- The base install supplies matching kernel headers before hardware setup. DKMS installers should install their driver packages and assume the headers exist.
|
||||||
- prefer helper commands for package and command checks where available.
|
- prefer helper commands for package and command checks where available.
|
||||||
|
|
||||||
Raw `command -v`, `pacman`, and `pacman-key` are acceptable in package-helper
|
Raw `command -v`, `pacman`, and `pacman-key` are acceptable in package-helper
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ Run `omarchy-restart-shell` after making changes to QML files.
|
|||||||
[`docs/omarchy-shell.md`](../../docs/omarchy-shell.md) and
|
[`docs/omarchy-shell.md`](../../docs/omarchy-shell.md) and
|
||||||
`shell/services/PluginRegistry.qml` for the current contract; fields such as
|
`shell/services/PluginRegistry.qml` for the current contract; fields such as
|
||||||
`activation` are optional.
|
`activation` are optional.
|
||||||
- Entry-point QML files are `Item`s (not `ShellRoot`), and accept the
|
- Entry-point QML files are `Item`s (not `ShellRoot`), and accept the shell-injected properties `omarchyPath`, `shell`, `manifest`, and `pluginRegistry` / `barWidgetRegistry` as appropriate. First-party plugins receive the host objects. Third-party plugins receive capability-scoped facades: ordinary plugins may look up and control only their own service and lifecycle, built-in clones retain narrow source-specific configuration and UI compatibility, menu plugins receive an application-library facade, and plugins can read detached scalar bar state; full-bar plugins additionally receive detached bar configuration and widget-catalog snapshots, narrow proxies for the non-authentication services used by built-in bar widgets, and lifecycle control over configured non-authentication UI plugins. Authentication capabilities must be stamped from trusted first-party manifests, and third-party registry views and bar configuration must be detached snapshots rather than shared objects. These facades reduce accidental authority but are not a same-process QML sandbox: a visual bar widget can walk its parent hierarchy to ordinary host objects. Authentication services must therefore remain outside both `ShellRoot._services` and the host QObject tree. Do not expose authentication services through new third-party-facing properties.
|
||||||
shell-injected properties `omarchyPath`, `shell`, `manifest`, and
|
|
||||||
`pluginRegistry` / `barWidgetRegistry` as appropriate.
|
|
||||||
- Panel / overlay / menu plugins must expose `open(payloadJson)` and
|
- Panel / overlay / menu plugins must expose `open(payloadJson)` and
|
||||||
`close()` lifecycle methods for `shell summon` and `shell hide`.
|
`close()` lifecycle methods for `shell summon` and `shell hide`.
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ GROUP_DESCRIPTIONS[monitor]="Monitor status helpers"
|
|||||||
GROUP_DESCRIPTIONS[network]="Network status helpers"
|
GROUP_DESCRIPTIONS[network]="Network status helpers"
|
||||||
GROUP_DESCRIPTIONS[notification]="Notification helpers"
|
GROUP_DESCRIPTIONS[notification]="Notification helpers"
|
||||||
GROUP_DESCRIPTIONS[mise]="Mise tool wrappers"
|
GROUP_DESCRIPTIONS[mise]="Mise tool wrappers"
|
||||||
|
GROUP_DESCRIPTIONS[openclaw]="OpenClaw agent platform setup"
|
||||||
GROUP_DESCRIPTIONS[osd]="On-screen display status helpers"
|
GROUP_DESCRIPTIONS[osd]="On-screen display status helpers"
|
||||||
GROUP_DESCRIPTIONS[pkg]="Package management helpers"
|
GROUP_DESCRIPTIONS[pkg]="Package management helpers"
|
||||||
GROUP_DESCRIPTIONS[plugin]="Omarchy shell plugin and bar widget management"
|
GROUP_DESCRIPTIONS[plugin]="Omarchy shell plugin and bar widget management"
|
||||||
|
|||||||
@@ -82,10 +82,39 @@ grok)
|
|||||||
command=(grok --permission-mode bypassPermissions)
|
command=(grok --permission-mode bypassPermissions)
|
||||||
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
||||||
;;
|
;;
|
||||||
|
openclaw)
|
||||||
|
# The launcher owns onboarding and the gateway dance: OpenClaw's terminal UI
|
||||||
|
# must attach to the running gateway (the embedded `openclaw chat` refuses to
|
||||||
|
# start while the gateway owns the state directory). It has no permission
|
||||||
|
# prompts to skip, and --message seeds the session while keeping it
|
||||||
|
# interactive.
|
||||||
|
command=(omarchy-launch-openclaw --tui)
|
||||||
|
[[ -n ${prompt:-} ]] && command+=(--message "$prompt")
|
||||||
|
;;
|
||||||
codex)
|
codex)
|
||||||
command=(codex --approve-for-me)
|
command=(codex --approve-for-me)
|
||||||
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
||||||
;;
|
;;
|
||||||
|
cursor-agent)
|
||||||
|
# --yolo covers commands only; the workspace trust dialog has its own flag.
|
||||||
|
# A one-word prompt naming a subcommand (update, login, help) still runs that
|
||||||
|
# subcommand after a bare --, so the agent subcommand is named outright, and
|
||||||
|
# -- after it keeps a prompt starting with a dash from being read as an option.
|
||||||
|
command=(cursor-agent --yolo --trust)
|
||||||
|
[[ -n ${prompt:-} ]] && command+=(agent -- "$prompt")
|
||||||
|
;;
|
||||||
|
hermes)
|
||||||
|
if [[ -n ${prompt:-} ]]; then
|
||||||
|
command=(env -u HERMES_SESSION_SOURCE hermes chat --yolo --tui "--query=$prompt")
|
||||||
|
else
|
||||||
|
command=(hermes --yolo)
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
muse)
|
||||||
|
# --approval-mode never skips the tool prompts but keeps Muse's own sandbox.
|
||||||
|
command=(muse --approval-mode never)
|
||||||
|
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
||||||
|
;;
|
||||||
omp)
|
omp)
|
||||||
command=(omp --auto-approve)
|
command=(omp --auto-approve)
|
||||||
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
[[ -n ${prompt:-} ]] && command+=(-- "$prompt")
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ def fetch_codex_rpc():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
[codex, "-s", "read-only", "-a", "untrusted", "app-server"],
|
[codex, "-s", "read-only", "-a", "on-request", "app-server"],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Install and upgrade callers can start this helper as root. Ignore their PATH
|
||||||
|
# so optional commands never fall through to a user-writable directory.
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
export PATH=/usr/share/omarchy/bin:/usr/local/bin:/usr/bin:/bin
|
||||||
|
fi
|
||||||
|
|
||||||
target_user=${OMARCHY_INSTALL_USER:-${SUDO_USER:-}}
|
target_user=${OMARCHY_INSTALL_USER:-${SUDO_USER:-}}
|
||||||
if [[ -z $target_user && -n ${PKEXEC_UID:-} ]]; then
|
if [[ -z $target_user && -n ${PKEXEC_UID:-} ]]; then
|
||||||
target_user=$(getent passwd "$PKEXEC_UID" | cut -d: -f1)
|
target_user=$(getent passwd "$PKEXEC_UID" | cut -d: -f1)
|
||||||
@@ -34,7 +40,8 @@ auth required pam_faillock.so authsucc
|
|||||||
account include system-local-login
|
account include system-local-login
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if omarchy-cmd-present fprintd-list && fprintd-list "$target_user" 2>/dev/null | grep -qi finger; then
|
if [[ -x /usr/bin/fprintd-list ]] &&
|
||||||
|
/usr/bin/fprintd-list "$target_user" 2>/dev/null | grep -qi finger; then
|
||||||
echo "Configuring lock screen fingerprint authentication..."
|
echo "Configuring lock screen fingerprint authentication..."
|
||||||
as_root tee /etc/pam.d/omarchy-lock-fingerprint >/dev/null <<'EOF'
|
as_root tee /etc/pam.d/omarchy-lock-fingerprint >/dev/null <<'EOF'
|
||||||
#%PAM-1.0
|
#%PAM-1.0
|
||||||
|
|||||||
@@ -4,7 +4,13 @@
|
|||||||
# omarchy:args=[--no-osd] [+N%|N%-|N%]
|
# omarchy:args=[--no-osd] [+N%|N%-|N%]
|
||||||
# omarchy:examples=omarchy brightness display apple | omarchy brightness display apple +5% | omarchy brightness display apple --no-osd 50%
|
# omarchy:examples=omarchy brightness display apple | omarchy brightness display apple +5% | omarchy brightness display apple --no-osd 50%
|
||||||
|
|
||||||
device_cache="${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display-apple.device"
|
# Only cache under the user-private runtime dir. With no XDG_RUNTIME_DIR we skip
|
||||||
|
# caching (detect every run) rather than fall back to a predictable, world-writable
|
||||||
|
# /tmp path another user could pre-create.
|
||||||
|
device_cache=""
|
||||||
|
if [[ -n ${XDG_RUNTIME_DIR:-} ]]; then
|
||||||
|
device_cache="$XDG_RUNTIME_DIR/omarchy-brightness-display-apple.device"
|
||||||
|
fi
|
||||||
no_osd=0
|
no_osd=0
|
||||||
if [[ ${1:-} == "--no-osd" ]]; then
|
if [[ ${1:-} == "--no-osd" ]]; then
|
||||||
no_osd=1
|
no_osd=1
|
||||||
@@ -28,9 +34,14 @@ find_apple_display_device() {
|
|||||||
local cached=""
|
local cached=""
|
||||||
local device=""
|
local device=""
|
||||||
|
|
||||||
if [[ -r $device_cache ]]; then
|
if [[ -n $device_cache && -r $device_cache ]]; then
|
||||||
read -r cached <"$device_cache" || true
|
read -r cached <"$device_cache" || true
|
||||||
if [[ -n $cached && -e $cached ]]; then
|
# Trust a cached value only if it still names a hiddev character device. A
|
||||||
|
# stale or unexpected cache (a regular file, a non-hiddev node) is ignored and
|
||||||
|
# we re-detect instead of handing an arbitrary path to asdcontrol. The globs
|
||||||
|
# are left unquoted on purpose: [[ ]] pattern-matches an unquoted right side,
|
||||||
|
# and quoting them would turn the match into a literal string comparison.
|
||||||
|
if [[ ( $cached == /dev/hiddev* || $cached == /dev/usb/hiddev* ) && -c $cached ]]; then
|
||||||
printf '%s\n' "$cached"
|
printf '%s\n' "$cached"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -39,7 +50,9 @@ find_apple_display_device() {
|
|||||||
device="$(detect_apple_display_device)" || return 1
|
device="$(detect_apple_display_device)" || return 1
|
||||||
[[ -n $device ]] || return 1
|
[[ -n $device ]] || return 1
|
||||||
|
|
||||||
printf '%s\n' "$device" >"$device_cache"
|
if [[ -n $device_cache ]]; then
|
||||||
|
printf '%s\n' "$device" >"$device_cache"
|
||||||
|
fi
|
||||||
printf '%s\n' "$device"
|
printf '%s\n' "$device"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Set and launch the default coding agent
|
# omarchy:summary=Set and launch the default coding agent
|
||||||
# omarchy:args=[pi|omp|opencode|claude|codex|grok|gemini|copilot|crush]
|
# omarchy:args=[pi|omp|opencode|claude|codex|grok|gemini|openclaw|hermes|copilot|crush|cursor-agent|muse]
|
||||||
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude
|
# omarchy:examples=omarchy default agent | omarchy default agent codex | omarchy default agent claude
|
||||||
|
|
||||||
installing=false
|
installing=false
|
||||||
@@ -32,20 +32,51 @@ codex) agent="codex"; name="Codex" ;;
|
|||||||
crush) agent="crush"; name="Crush" ;;
|
crush) agent="crush"; name="Crush" ;;
|
||||||
grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;;
|
grok) agent="grok"; name="Grok"; agent_package="npm:@xai-official/grok" ;;
|
||||||
gemini | gemini-cli) agent="gemini"; name="Gemini" ;;
|
gemini | gemini-cli) agent="gemini"; name="Gemini" ;;
|
||||||
|
openclaw) agent="openclaw"; name="OpenClaw"; agent_installer="omarchy-install-openclaw-cli" ;;
|
||||||
|
hermes) agent="hermes"; name="Hermes"; agent_installer="omarchy-install-hermes-cli" ;;
|
||||||
copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;;
|
copilot | github-copilot) agent="copilot"; name="GitHub Copilot" ;;
|
||||||
|
muse | muse-code | musecode)
|
||||||
|
agent="muse"; name="Muse Code"
|
||||||
|
# Meta's launcher verifies and updates the native binary for this platform.
|
||||||
|
agent_package="http:muse[url=https://api.meta.ai/muse-launcher.sh,bin=muse,version_list_url=https://api.meta.ai/muse-code/channels/muse-stable,version_json_path=.version]"
|
||||||
|
;;
|
||||||
|
cursor | cursor-agent) agent="cursor-agent"; name="Cursor CLI" ;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: omarchy-default-agent <pi|omp|opencode|claude|codex|grok|gemini|copilot|crush>"
|
echo "Usage: omarchy-default-agent <pi|omp|opencode|claude|codex|grok|gemini|openclaw|hermes|copilot|crush|cursor-agent|muse>"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
agent_package=${agent_package:-$agent}
|
agent_package=${agent_package:-$agent}
|
||||||
|
|
||||||
if [[ $installing == "false" ]] && ! mise where "$agent_package" &>/dev/null; then
|
# Hermes reaches mise through its own installer rather than straight from
|
||||||
|
# here: it needs its interpreter pinned, and a bare `mise use` has nowhere to
|
||||||
|
# say so. See omarchy-install-hermes-cli. OpenClaw comes from its pacman
|
||||||
|
# package the same way; see omarchy-install-openclaw-cli.
|
||||||
|
if [[ -n ${agent_installer:-} ]]; then
|
||||||
|
# Not omarchy-cmd-present: the stub is on PATH from first boot and says
|
||||||
|
# nothing about whether Hermes is installed behind it. Treating a cold stub
|
||||||
|
# as installed skips the floating terminal and runs the minute-long install
|
||||||
|
# inside the menu action instead.
|
||||||
|
agent_present() { "$agent_installer" --check; }
|
||||||
|
agent_install() { "$agent_installer" --now; }
|
||||||
|
else
|
||||||
|
# Anything at the wrapper's path other than the wrapper is the user's own
|
||||||
|
# install, such as the symlink Cursor's installer leaves. A mise copy would
|
||||||
|
# only shadow it, since the mise shims precede ~/.local/bin on PATH.
|
||||||
|
user_install() {
|
||||||
|
[[ -x $HOME/.local/bin/$agent ]] &&
|
||||||
|
{ [[ -L $HOME/.local/bin/$agent ]] || ! grep -q '^mise use -g' "$HOME/.local/bin/$agent"; }
|
||||||
|
}
|
||||||
|
agent_present() { user_install || mise where "$agent_package" &>/dev/null; }
|
||||||
|
agent_install() { user_install || mise use -g "$agent_package"; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $installing == "false" ]] && ! agent_present; then
|
||||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent"
|
exec omarchy-launch-floating-terminal-with-presentation omarchy-default-agent --install "$agent"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! mise use -g "$agent_package"; then
|
if ! agent_install; then
|
||||||
if [[ $installing == "true" ]]; then
|
if [[ $installing == "true" ]]; then
|
||||||
echo "Could not install $name with mise" >&2
|
echo "Could not install $name with mise" >&2
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -34,8 +34,12 @@ systemd, shell, or app-launcher environment; reboot to make every layer agree.
|
|||||||
Affects only \$OMARCHY_PATH-resolved trees: bin/, default/, shell/,
|
Affects only \$OMARCHY_PATH-resolved trees: bin/, default/, shell/,
|
||||||
themes/, applications/, config/. Files installed at fixed system paths
|
themes/, applications/, config/. Files installed at fixed system paths
|
||||||
(/etc/, /usr/lib/systemd/, udev rule bodies, /etc/skel after user
|
(/etc/, /usr/lib/systemd/, udev rule bodies, /etc/skel after user
|
||||||
creation, /usr/share/plymouth) are NOT covered — for those, use
|
creation) are NOT covered — for those, use omarchy-dev-pkg-test to build
|
||||||
omarchy-dev-pkg-test to build and install the package from the checkout.
|
and install the package from the checkout.
|
||||||
|
|
||||||
|
The Plymouth and SDDM themes under /usr/share are the exception: omarchy
|
||||||
|
plymouth set and omarchy refresh plymouth republish them from the checkout,
|
||||||
|
reading this link's authorization out of the root-owned /etc/omarchy.conf.
|
||||||
|
|
||||||
Also writes $sudoers_file so sudo resolves omarchy-*
|
Also writes $sudoers_file so sudo resolves omarchy-*
|
||||||
from the checkout instead of the packaged copies. That part takes effect
|
from the checkout instead of the packaged copies. That part takes effect
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ term_pt_for() {
|
|||||||
'BEGIN { printf "%d", int(s * p / b + 0.5) }'
|
'BEGIN { printf "%d", int(s * p / b + 0.5) }'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set the font point size in every terminal config that exists. Family is left
|
# Set the font point size in terminal configs, creating Kitty overrides when
|
||||||
|
# it inherits its size from the system config. Family is left
|
||||||
# untouched — that is omarchy-font-set's job. Live-reload signals mirror
|
# untouched — that is omarchy-font-set's job. Live-reload signals mirror
|
||||||
# omarchy-font-set; foot has no reload signal, so running instances are nudged.
|
# omarchy-font-set; foot has no reload signal, so running instances are nudged.
|
||||||
set_terminal_size() {
|
set_terminal_size() {
|
||||||
@@ -139,8 +140,13 @@ set_terminal_size() {
|
|||||||
sed -i -E "s/^size[[:space:]]*=.*/size = $pt/" ~/.config/alacritty/alacritty.toml
|
sed -i -E "s/^size[[:space:]]*=.*/size = $pt/" ~/.config/alacritty/alacritty.toml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f ~/.config/kitty/kitty.conf ]]; then
|
if [[ -f ~/.config/kitty/kitty.conf ]] || omarchy-cmd-present kitty; then
|
||||||
sed -i -E "s/^font_size[[:space:]]+.*/font_size $pt.0/" ~/.config/kitty/kitty.conf
|
mkdir -p ~/.config/kitty
|
||||||
|
if grep -qE '^[[:space:]]*font_size[[:space:]]+' ~/.config/kitty/kitty.conf 2>/dev/null; then
|
||||||
|
sed --follow-symlinks -i -E "s/^[[:space:]]*font_size[[:space:]]+.*/font_size $pt.0/" ~/.config/kitty/kitty.conf
|
||||||
|
else
|
||||||
|
printf '\nfont_size %s.0\n' "$pt" >>~/.config/kitty/kitty.conf
|
||||||
|
fi
|
||||||
pkill -USR1 kitty 2>/dev/null || true
|
pkill -USR1 kitty 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -177,9 +183,13 @@ term_current_pt() {
|
|||||||
elif [[ -f ~/.config/alacritty/alacritty.toml ]]; then
|
elif [[ -f ~/.config/alacritty/alacritty.toml ]]; then
|
||||||
grep -oP '^size[[:space:]]*=[[:space:]]*\K[0-9.]+' ~/.config/alacritty/alacritty.toml | head -1
|
grep -oP '^size[[:space:]]*=[[:space:]]*\K[0-9.]+' ~/.config/alacritty/alacritty.toml | head -1
|
||||||
elif [[ -f ~/.config/kitty/kitty.conf ]]; then
|
elif [[ -f ~/.config/kitty/kitty.conf ]]; then
|
||||||
grep -oP '^font_size[[:space:]]+\K[0-9.]+' ~/.config/kitty/kitty.conf | head -1
|
local pt
|
||||||
|
pt=$(grep -oP '^[[:space:]]*font_size[[:space:]]+\K[0-9.]+' ~/.config/kitty/kitty.conf | tail -1)
|
||||||
|
echo "${pt:-$TERM_DEFAULT_PT}"
|
||||||
elif [[ -f ~/.config/foot/foot.ini ]]; then
|
elif [[ -f ~/.config/foot/foot.ini ]]; then
|
||||||
grep -oP ':size=\K[0-9.]+' ~/.config/foot/foot.ini | head -1
|
grep -oP ':size=\K[0-9.]+' ~/.config/foot/foot.ini | head -1
|
||||||
|
elif omarchy-cmd-present kitty; then
|
||||||
|
echo "$TERM_DEFAULT_PT"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,9 @@ write_networkmanager_dns() {
|
|||||||
local servers="$1"
|
local servers="$1"
|
||||||
|
|
||||||
install -d -m 0755 "$(dirname "$NM_DNS_CONF")"
|
install -d -m 0755 "$(dirname "$NM_DNS_CONF")"
|
||||||
|
# omarchy:heredoc-expands paths=none -- $servers is a normalized, single-line
|
||||||
|
# DNS server list written as data, not a path or command; nothing user-writable
|
||||||
|
# is resolved or executed from the root-owned drop-in.
|
||||||
cat >"$NM_DNS_CONF" <<EOF
|
cat >"$NM_DNS_CONF" <<EOF
|
||||||
# Managed by omarchy-dns. Remove this file or run omarchy dns DHCP to use DHCP DNS again.
|
# Managed by omarchy-dns. Remove this file or run omarchy dns DHCP to use DHCP DNS again.
|
||||||
[global-dns]
|
[global-dns]
|
||||||
@@ -302,6 +305,9 @@ Custom)
|
|||||||
split_dns_servers "$dns_servers"
|
split_dns_servers "$dns_servers"
|
||||||
write_networkmanager_dns "$dns_servers"
|
write_networkmanager_dns "$dns_servers"
|
||||||
set_connection_dns "$ipv4_dns" "$ipv6_dns"
|
set_connection_dns "$ipv4_dns" "$ipv6_dns"
|
||||||
|
# omarchy:heredoc-expands paths=none -- $dns_servers is a normalized,
|
||||||
|
# single-line DNS server list; the //,/ turns its comma separators into the
|
||||||
|
# spaces resolved.conf wants. It is written as data, not a path or command.
|
||||||
tee /etc/systemd/resolved.conf >/dev/null <<EOF
|
tee /etc/systemd/resolved.conf >/dev/null <<EOF
|
||||||
[Resolve]
|
[Resolve]
|
||||||
DNS=${dns_servers//,/ }
|
DNS=${dns_servers//,/ }
|
||||||
|
|||||||
@@ -30,8 +30,14 @@ if [[ -f ~/.config/alacritty/alacritty.toml ]]; then
|
|||||||
sed -i "s/family = \".*\"/family = \"$font_name\"/g" ~/.config/alacritty/alacritty.toml
|
sed -i "s/family = \".*\"/family = \"$font_name\"/g" ~/.config/alacritty/alacritty.toml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f ~/.config/kitty/kitty.conf ]]; then
|
if [[ -f ~/.config/kitty/kitty.conf ]] || omarchy-cmd-present kitty; then
|
||||||
sed -i "s/^font_family .*/font_family $font_name/g" ~/.config/kitty/kitty.conf
|
mkdir -p ~/.config/kitty
|
||||||
|
if grep -qE '^[[:space:]]*font_family[[:space:]]+' ~/.config/kitty/kitty.conf 2>/dev/null; then
|
||||||
|
kitty_font_name=$(printf '%s' "$font_name" | sed 's/[\\&/]/\\&/g')
|
||||||
|
sed --follow-symlinks -i -E "s/^[[:space:]]*font_family[[:space:]]+.*/font_family $kitty_font_name/" ~/.config/kitty/kitty.conf
|
||||||
|
else
|
||||||
|
printf '\nfont_family %s\n' "$font_name" >>~/.config/kitty/kitty.conf
|
||||||
|
fi
|
||||||
pkill -USR1 kitty
|
pkill -USR1 kitty
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,35 @@ MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf"
|
|||||||
SWAP_FILE="/swap/swapfile"
|
SWAP_FILE="/swap/swapfile"
|
||||||
RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf"
|
RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf"
|
||||||
|
|
||||||
|
install_root_file() {
|
||||||
|
local source="$1"
|
||||||
|
local destination="$2"
|
||||||
|
local mode="$3"
|
||||||
|
local stage
|
||||||
|
|
||||||
|
stage=$(sudo /usr/bin/mktemp -- "${destination%/*}/.${destination##*/}.omarchy.XXXXXX") || return 1
|
||||||
|
safe_stage_path "$stage" "$destination" || return 1
|
||||||
|
|
||||||
|
if sudo /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage" &&
|
||||||
|
sudo /usr/bin/mv -Tf -- "$stage" "$destination"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
safe_stage_path "$stage" "$destination" && sudo /usr/bin/rm -f -- "$stage"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_stage_path() {
|
||||||
|
local stage="$1"
|
||||||
|
local destination="$2"
|
||||||
|
local prefix suffix
|
||||||
|
|
||||||
|
prefix="${destination%/*}/.${destination##*/}.omarchy."
|
||||||
|
[[ $stage == "$prefix"* ]] || return 1
|
||||||
|
suffix=${stage#"$prefix"}
|
||||||
|
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
# Check if hibernation is already configured
|
# Check if hibernation is already configured
|
||||||
if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
|
if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then
|
||||||
# Fix empty resume_offset if btrfs map-swapfile failed during initial setup
|
# Fix empty resume_offset if btrfs map-swapfile failed during initial setup
|
||||||
@@ -83,14 +112,20 @@ if ! swapon --show | grep -q "$SWAP_FILE"; then
|
|||||||
sudo swapon -p 0 "$SWAP_FILE"
|
sudo swapon -p 0 "$SWAP_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Ensure keyboard backlight doesn't prevent sleep
|
||||||
|
# Install this before writing the resume marker so a failed install remains
|
||||||
|
# retryable through the normal setup command.
|
||||||
|
if ! install_root_file "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" \
|
||||||
|
/usr/lib/systemd/system-sleep/keyboard-backlight 0755; then
|
||||||
|
echo "Could not install the keyboard-backlight system-sleep hook" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Add resume hook to mkinitcpio
|
# Add resume hook to mkinitcpio
|
||||||
sudo mkdir -p /etc/mkinitcpio.conf.d
|
sudo mkdir -p /etc/mkinitcpio.conf.d
|
||||||
echo "Adding resume hook to $MKINITCPIO_CONF"
|
echo "Adding resume hook to $MKINITCPIO_CONF"
|
||||||
echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null
|
echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null
|
||||||
|
|
||||||
# Ensure keyboard backlight doesn't prevent sleep
|
|
||||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" /usr/lib/systemd/system-sleep/
|
|
||||||
|
|
||||||
# Add resume= kernel parameters so the initramfs resume hook knows where to find the
|
# Add resume= kernel parameters so the initramfs resume hook knows where to find the
|
||||||
# hibernation image. Without these, resume happens late (after GPU drivers load) and fails.
|
# hibernation image. Without these, resume happens late (after GPU drivers load) and fails.
|
||||||
if [[ ! -f $RESUME_DROP_IN ]]; then
|
if [[ ! -f $RESUME_DROP_IN ]]; then
|
||||||
|
|||||||
Executable
+159
@@ -0,0 +1,159 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Install the Hermes desktop app
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
echo "Run this command as your desktop user, without sudo." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing Hermes Desktop..."
|
||||||
|
omarchy-pkg-add hermes-desktop
|
||||||
|
|
||||||
|
if [[ ! -r /usr/share/hermes-desktop/install.sh || ! -r /usr/share/hermes-desktop/runtime.patch ]] ||
|
||||||
|
! release_commit=$(jq -er 'select(.branch == "main") | .commit | select(test("^[0-9a-f]{40}$"))' /opt/hermes-desktop/resources/install-stamp.json 2>/dev/null); then
|
||||||
|
echo "The installed Hermes package cannot prepare in-app updates. Run 'omarchy update', then try again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If Hermes was already installed for the terminal, the app supersedes it: one
|
||||||
|
# machine, one Hermes. This drops that copy so the terminal, the default agent
|
||||||
|
# and the app all end up on the app's installation.
|
||||||
|
omarchy-install-hermes-cli || true
|
||||||
|
|
||||||
|
# Keep the runtime at the root even when invoked from a Hermes profile.
|
||||||
|
HERMES_HOME=$(realpath -ms -- "${HERMES_HOME:-$HOME/.hermes}")
|
||||||
|
home_parent=$(dirname -- "$HERMES_HOME")
|
||||||
|
if [[ ${home_parent##*/} == [Pp][Rr][Oo][Ff][Ii][Ll][Ee][Ss] ]]; then
|
||||||
|
HERMES_HOME=$(dirname -- "$home_parent")
|
||||||
|
fi
|
||||||
|
export HERMES_HOME
|
||||||
|
|
||||||
|
runtime="$HERMES_HOME/hermes-agent"
|
||||||
|
native_app="$runtime/apps/desktop/release/linux-unpacked"
|
||||||
|
|
||||||
|
runtime_ready() {
|
||||||
|
[[ -f $runtime/.hermes-bootstrap-complete && -f $runtime/venv/bin/hermes && -x $runtime/venv/bin/hermes && -f $runtime/venv/bin/python && -x $runtime/venv/bin/python ]] &&
|
||||||
|
timeout 15 "$runtime/venv/bin/hermes" --version >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_main() {
|
||||||
|
local main_commit
|
||||||
|
main_commit=$(git -C "$runtime" rev-parse --verify refs/heads/main 2>/dev/null || true)
|
||||||
|
if [[ -n $main_commit && $main_commit != "$release_commit" && $main_commit != "$(git -C "$runtime" rev-parse --verify refs/remotes/origin/main 2>/dev/null)" ]]; then
|
||||||
|
echo "Hermes main has local commits. Keep that work and prepare the desktop with 'hermes desktop --build-only'." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! runtime_ready; then
|
||||||
|
# The upstream installer can reset an existing checkout. Do not pin a newer
|
||||||
|
# or modified runtime back to the package release while repairing setup.
|
||||||
|
if [[ -e $runtime || -L $runtime ]]; then
|
||||||
|
if [[ $(git -C "$runtime" rev-parse HEAD 2>/dev/null) != "$release_commit" ]] ||
|
||||||
|
[[ -n $(git -C "$runtime" status --porcelain --untracked-files=all) ]]; then
|
||||||
|
echo "Hermes setup is incomplete at $runtime. Repair that installation before trying again; existing files have been kept." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
check_main
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Upstream replaces these commands, including foreign files and symlinks.
|
||||||
|
# Keep their original bytes/links before handing the names to the desktop.
|
||||||
|
command_backup=""
|
||||||
|
for command in hermes hermes-agent hermes-acp; do
|
||||||
|
command_path="$HOME/.local/bin/$command"
|
||||||
|
if [[ -e $command_path || -L $command_path ]]; then
|
||||||
|
if [[ ! -f $command_path && ! -L $command_path ]]; then
|
||||||
|
echo "Cannot replace $command_path: move it aside before installing Hermes Desktop." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z $command_backup ]]; then
|
||||||
|
command_backup=$(mktemp -d "$HOME/.local/bin/.hermes-before-desktop.XXXXXX")
|
||||||
|
echo "Saving existing Hermes commands in $command_backup"
|
||||||
|
fi
|
||||||
|
cp -a -- "$command_path" "$command_backup/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Setting up the Hermes runtime..."
|
||||||
|
bash /usr/share/hermes-desktop/install.sh --skip-setup --branch main --commit "$release_commit" --force-commit --dir "$runtime" --hermes-home "$HERMES_HOME"
|
||||||
|
if ! runtime_ready; then
|
||||||
|
echo "Hermes runtime setup did not complete. Re-run this command after resolving the installer error." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
runtime_commit=$(git -C "$runtime" rev-parse HEAD)
|
||||||
|
if [[ $runtime_commit == "$release_commit" ]]; then
|
||||||
|
# The updater switches to main before checking for changes. Start main at
|
||||||
|
# the packaged release, with enough history for its first fast-forward.
|
||||||
|
check_main
|
||||||
|
if [[ $(git -C "$runtime" rev-parse --is-shallow-repository) == "true" ]]; then
|
||||||
|
git -C "$runtime" fetch --unshallow origin main
|
||||||
|
fi
|
||||||
|
git -C "$runtime" switch -C main "$release_commit"
|
||||||
|
|
||||||
|
if git -C "$runtime" apply --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1; then
|
||||||
|
git -C "$runtime" apply /usr/share/hermes-desktop/runtime.patch
|
||||||
|
elif ! git -C "$runtime" apply --reverse --check /usr/share/hermes-desktop/runtime.patch >/dev/null 2>&1; then
|
||||||
|
echo "The Hermes Linux runtime patch conflicts with local changes. Existing files have been kept." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e $native_app || -L $native_app ]]; then
|
||||||
|
if [[ ! -f $native_app/Hermes || ! -x $native_app/Hermes || ! -f $native_app/resources/app.asar || ! -f $native_app/resources/install-stamp.json ]]; then
|
||||||
|
echo "The Hermes desktop app at $native_app is incomplete. Repair it with 'hermes desktop --build-only' before trying again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ $runtime_commit != "$release_commit" ]]; then
|
||||||
|
echo "The Hermes runtime has moved beyond the packaged desktop release. Run 'hermes desktop --build-only', then try again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
desktop_changes=$(git -C "$runtime" status --porcelain --untracked-files=all -- apps/desktop package.json package-lock.json)
|
||||||
|
if [[ -n $desktop_changes ]]; then
|
||||||
|
echo "Hermes desktop sources have local changes. Run 'hermes desktop --build-only', then try again; existing files have been kept." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p -- "${native_app%/*}"
|
||||||
|
staging=$(mktemp -d "${native_app%/*}/.linux-unpacked.XXXXXX")
|
||||||
|
trap 'rm -rf -- "$staging"' EXIT
|
||||||
|
cp -a /opt/hermes-desktop/. "$staging/"
|
||||||
|
chmod 0755 "$staging/chrome-sandbox"
|
||||||
|
mv -T --no-clobber -- "$staging" "$native_app"
|
||||||
|
if [[ -e $staging ]]; then
|
||||||
|
echo "A Hermes desktop app appeared during setup. It has been kept; please try again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
# Record this matching prebuilt app using the CLI's own content hash, so
|
||||||
|
# subsequent menu launches do not rebuild an app that is already current.
|
||||||
|
env -u PYTHONPATH -u PYTHONHOME "$runtime/venv/bin/python" - "$runtime" <<'PY'
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.insert(0, sys.argv[1])
|
||||||
|
from hermes_cli.main import _write_desktop_build_stamp
|
||||||
|
|
||||||
|
_write_desktop_build_stamp(Path(sys.argv[1]), source_mode=False)
|
||||||
|
PY
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Opening Hermes Desktop..."
|
||||||
|
setsid uwsm-app -- /usr/bin/hermes-desktop >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
# Only a running Hermes can be told which skin to show; a unit outlives this
|
||||||
|
# terminal and reports to the journal.
|
||||||
|
echo "Matching Hermes to the current theme once it is set up..."
|
||||||
|
systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
|
||||||
|
systemd-run --user --quiet --collect --unit=omarchy-hermes-theme omarchy-theme-set-hermes --wait
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Hermes Desktop has been installed."
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Install the OpenClaw agent platform and its Control UI web app
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Installing OpenClaw..."
|
||||||
|
omarchy-pkg-add openclaw
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# onboards or starts that gateway before opening the window. The icon ships
|
||||||
|
# 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 \
|
||||||
|
omarchy-launch-openclaw
|
||||||
|
|
||||||
|
# A first install runs onboarding right here: launching the app instead would
|
||||||
|
# open a second floating terminal for the wizard, identical to this one.
|
||||||
|
# omarchy-openclaw-onboard runs the wizard the way this flow needs (terminal
|
||||||
|
# prompts, gateway as a user service, and it actually returns). A machine
|
||||||
|
# that is already onboarded goes straight to the app.
|
||||||
|
if [[ -f $HOME/.openclaw/openclaw.json ]]; then
|
||||||
|
echo "Opening OpenClaw..."
|
||||||
|
setsid uwsm-app -- gtk-launch OpenClaw >/dev/null 2>&1 &
|
||||||
|
elif omarchy-openclaw-onboard && [[ -f $HOME/.openclaw/openclaw.json ]]; then
|
||||||
|
echo "Opening OpenClaw..."
|
||||||
|
setsid uwsm-app -- gtk-launch OpenClaw >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "OpenClaw has been installed."
|
||||||
|
echo "If you skipped onboarding, launching OpenClaw from the app grid resumes it."
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Install T3 Code and point it at the Omarchy palette
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
T3CODE_HOME="${T3CODE_HOME:-$HOME/.t3}"
|
||||||
|
T3CODE_STATE_DIR="$T3CODE_HOME/userdata"
|
||||||
|
|
||||||
|
echo "Installing T3 Code..."
|
||||||
|
omarchy-pkg-add t3code-bin
|
||||||
|
|
||||||
|
echo "Matching T3 Code to the current theme..."
|
||||||
|
mkdir -p "$T3CODE_STATE_DIR"
|
||||||
|
|
||||||
|
# An updated user's current theme may have been staged before this template existed.
|
||||||
|
if [[ ! -f $HOME/.local/state/omarchy/current/theme/t3code.json ]]; then
|
||||||
|
omarchy-theme-refresh
|
||||||
|
fi
|
||||||
|
|
||||||
|
omarchy-theme-set-t3code
|
||||||
|
|
||||||
|
# The packaged CLI selects the published palette before the app's first launch.
|
||||||
|
t3 theme set omarchy --base-dir "$T3CODE_HOME"
|
||||||
|
|
||||||
|
echo "Opening T3 Code..."
|
||||||
|
setsid uwsm-app -- gtk-launch t3code >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "T3 Code has been installed."
|
||||||
@@ -16,6 +16,12 @@ fi
|
|||||||
printf -v install_message '%q' "Installing ${name}..."
|
printf -v install_message '%q' "Installing ${name}..."
|
||||||
printf -v desktop_id_arg '%q' "$desktop_id"
|
printf -v desktop_id_arg '%q' "$desktop_id"
|
||||||
|
|
||||||
|
# The list has to reach omarchy-pkg-add as several words, so each word is quoted
|
||||||
|
# rather than the whole string; -d '' reads past newlines and always ends at EOF.
|
||||||
|
read -r -d '' -a package_list <<<"$packages" || true
|
||||||
|
printf -v packages_arg '%q ' "${package_list[@]}"
|
||||||
|
packages_arg="${packages_arg% }"
|
||||||
|
|
||||||
# The subshell keeps & from backgrounding the package installation too.
|
# The subshell keeps & from backgrounding the package installation too.
|
||||||
exec omarchy-launch-floating-terminal-with-presentation \
|
exec omarchy-launch-floating-terminal-with-presentation \
|
||||||
"echo ${install_message}; omarchy-pkg-add ${packages} && (setsid uwsm-app -- gtk-launch ${desktop_id_arg} >/dev/null 2>&1 &)"
|
"echo ${install_message}; omarchy-pkg-add ${packages_arg} && (setsid uwsm-app -- gtk-launch ${desktop_id_arg} >/dev/null 2>&1 &)"
|
||||||
+10
-1
@@ -12,4 +12,13 @@ if [[ -z $name || -z $packages ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec omarchy-launch-floating-terminal-with-presentation "echo 'Installing ${name}...'; omarchy-pkg-add ${packages}"
|
printf -v install_message '%q' "Installing ${name}..."
|
||||||
|
|
||||||
|
# The list has to reach omarchy-pkg-add as several words, so each word is quoted
|
||||||
|
# rather than the whole string; -d '' reads past newlines and always ends at EOF.
|
||||||
|
read -r -d '' -a package_list <<<"$packages" || true
|
||||||
|
printf -v packages_arg '%q ' "${package_list[@]}"
|
||||||
|
packages_arg="${packages_arg% }"
|
||||||
|
|
||||||
|
exec omarchy-launch-floating-terminal-with-presentation \
|
||||||
|
"echo ${install_message}; omarchy-pkg-add ${packages_arg}"
|
||||||
+10
-16
@@ -4,9 +4,10 @@
|
|||||||
# omarchy:args=<chrome|brave|brave-origin|edge|firefox|zen>
|
# omarchy:args=<chrome|brave|brave-origin|edge|firefox|zen>
|
||||||
# omarchy:examples=omarchy install browser firefox | omarchy install browser brave
|
# omarchy:examples=omarchy install browser firefox | omarchy install browser brave
|
||||||
|
|
||||||
setup_policy_directory() {
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
sudo mkdir -p "$1"
|
|
||||||
sudo chmod a+rw "$1"
|
setup_chromium_policy_directory() {
|
||||||
|
browser_policy_setup_dir "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
announce_browser_installed() {
|
announce_browser_installed() {
|
||||||
@@ -21,13 +22,6 @@ copy_chromium_flags() {
|
|||||||
omarchy-install-chromium-ytdlp
|
omarchy-install-chromium-ytdlp
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_firefox_preferences() {
|
|
||||||
local distribution_dir="$1"
|
|
||||||
|
|
||||||
setup_policy_directory "$distribution_dir"
|
|
||||||
sudo cp -f "$OMARCHY_PATH/default/firefox/policies.json" "$distribution_dir/policies.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_firefox_wayland() {
|
setup_firefox_wayland() {
|
||||||
mkdir -p ~/.config/environment.d
|
mkdir -p ~/.config/environment.d
|
||||||
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/omarchy-firefox-wayland.conf
|
echo "MOZ_ENABLE_WAYLAND=1" > ~/.config/environment.d/omarchy-firefox-wayland.conf
|
||||||
@@ -38,7 +32,7 @@ chrome)
|
|||||||
echo "Installing Chrome..."
|
echo "Installing Chrome..."
|
||||||
omarchy-pkg-aur-add google-chrome || exit 1
|
omarchy-pkg-aur-add google-chrome || exit 1
|
||||||
|
|
||||||
setup_policy_directory /etc/opt/chrome/policies/managed
|
setup_chromium_policy_directory /etc/opt/chrome/policies/managed
|
||||||
copy_chromium_flags ~/.config/chrome-flags.conf
|
copy_chromium_flags ~/.config/chrome-flags.conf
|
||||||
omarchy-theme-set-browser
|
omarchy-theme-set-browser
|
||||||
announce_browser_installed "Chrome"
|
announce_browser_installed "Chrome"
|
||||||
@@ -47,7 +41,7 @@ edge)
|
|||||||
echo "Installing Edge..."
|
echo "Installing Edge..."
|
||||||
omarchy-pkg-aur-add microsoft-edge-stable-bin || exit 1
|
omarchy-pkg-aur-add microsoft-edge-stable-bin || exit 1
|
||||||
|
|
||||||
setup_policy_directory /etc/opt/edge/policies/managed
|
setup_chromium_policy_directory /etc/opt/edge/policies/managed
|
||||||
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
|
copy_chromium_flags ~/.config/microsoft-edge-stable-flags.conf
|
||||||
omarchy-theme-set-browser
|
omarchy-theme-set-browser
|
||||||
announce_browser_installed "Edge"
|
announce_browser_installed "Edge"
|
||||||
@@ -56,7 +50,7 @@ brave)
|
|||||||
echo "Installing Brave..."
|
echo "Installing Brave..."
|
||||||
omarchy-pkg-aur-add brave-bin || exit 1
|
omarchy-pkg-aur-add brave-bin || exit 1
|
||||||
|
|
||||||
setup_policy_directory /etc/brave/policies/managed
|
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||||
copy_chromium_flags ~/.config/brave-flags.conf
|
copy_chromium_flags ~/.config/brave-flags.conf
|
||||||
omarchy-theme-set-browser
|
omarchy-theme-set-browser
|
||||||
announce_browser_installed "Brave"
|
announce_browser_installed "Brave"
|
||||||
@@ -65,7 +59,7 @@ brave-origin)
|
|||||||
echo "Installing Brave Origin..."
|
echo "Installing Brave Origin..."
|
||||||
omarchy-pkg-aur-add brave-origin-bin || exit 1
|
omarchy-pkg-aur-add brave-origin-bin || exit 1
|
||||||
|
|
||||||
setup_policy_directory /etc/brave/policies/managed
|
setup_chromium_policy_directory /etc/brave/policies/managed
|
||||||
copy_chromium_flags ~/.config/brave-origin-flags.conf
|
copy_chromium_flags ~/.config/brave-origin-flags.conf
|
||||||
omarchy-theme-set-browser
|
omarchy-theme-set-browser
|
||||||
announce_browser_installed "Brave Origin"
|
announce_browser_installed "Brave Origin"
|
||||||
@@ -74,7 +68,7 @@ firefox)
|
|||||||
echo "Installing Firefox..."
|
echo "Installing Firefox..."
|
||||||
omarchy-pkg-add firefox || exit 1
|
omarchy-pkg-add firefox || exit 1
|
||||||
|
|
||||||
setup_firefox_preferences /usr/lib/firefox/distribution
|
browser_policy_setup_firefox_distribution /usr/lib/firefox/distribution
|
||||||
setup_firefox_wayland
|
setup_firefox_wayland
|
||||||
announce_browser_installed "Firefox"
|
announce_browser_installed "Firefox"
|
||||||
;;
|
;;
|
||||||
@@ -82,7 +76,7 @@ zen)
|
|||||||
echo "Installing Zen..."
|
echo "Installing Zen..."
|
||||||
omarchy-pkg-aur-add zen-browser-bin || exit 1
|
omarchy-pkg-aur-add zen-browser-bin || exit 1
|
||||||
|
|
||||||
setup_firefox_preferences /opt/zen-browser/distribution
|
browser_policy_setup_firefox_distribution /opt/zen-browser/distribution
|
||||||
setup_firefox_wayland
|
setup_firefox_wayland
|
||||||
announce_browser_installed "Zen"
|
announce_browser_installed "Zen"
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -13,5 +13,9 @@ if [[ -z $name || -z $package || -z $family ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
printf -v install_message '%q' "Installing ${name}..."
|
||||||
|
printf -v package_arg '%q' "$package"
|
||||||
|
printf -v family_arg '%q' "$family"
|
||||||
|
|
||||||
exec omarchy-launch-floating-terminal-with-presentation \
|
exec omarchy-launch-floating-terminal-with-presentation \
|
||||||
"echo 'Installing ${name}...'; omarchy-pkg-add ${package} && sleep 2 && omarchy-font-set '${family}'"
|
"echo ${install_message}; omarchy-pkg-add ${package_arg} && sleep 2 && omarchy-font-set ${family_arg}"
|
||||||
@@ -10,7 +10,7 @@ set -e
|
|||||||
echo "Installing Xbox controller Bluetooth support..."
|
echo "Installing Xbox controller Bluetooth support..."
|
||||||
|
|
||||||
# Install xpadneo to ensure controllers work out of the box
|
# Install xpadneo to ensure controllers work out of the box
|
||||||
omarchy-pkg-add linux-headers xpadneo-dkms
|
omarchy-pkg-add xpadneo-dkms
|
||||||
|
|
||||||
# Prevent xpad/xpadneo driver conflict
|
# Prevent xpad/xpadneo driver conflict
|
||||||
echo blacklist xpad | sudo tee /etc/modprobe.d/blacklist-xpad.conf >/dev/null
|
echo blacklist xpad | sudo tee /etc/modprobe.d/blacklist-xpad.conf >/dev/null
|
||||||
|
|||||||
Executable
+280
@@ -0,0 +1,280 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Install the Hermes CLI as a mise-backed wrapper in ~/.local/bin
|
||||||
|
# omarchy:args=[--check|--now|--owns|--remove]
|
||||||
|
# omarchy:examples=omarchy install hermes cli | omarchy install hermes cli --now
|
||||||
|
|
||||||
|
# Hermes pins every one of its dependencies exactly and declares
|
||||||
|
# Requires-Python >=3.11,<3.14, so it can neither be built against Arch's
|
||||||
|
# Python nor share the python-* packages. mise builds it a private environment
|
||||||
|
# instead.
|
||||||
|
#
|
||||||
|
# It gets its own installer rather than a line in omarchy-mise-install because
|
||||||
|
# of the interpreter pin. Given no compatible interpreter to hand, uv builds
|
||||||
|
# the venv against the system Python in violation of Hermes' own bound,
|
||||||
|
# reports success, and leaves the breakage to surface later inside a
|
||||||
|
# dependency -- and omarchy-mise-install writes a fixed stub with nowhere to
|
||||||
|
# say otherwise.
|
||||||
|
#
|
||||||
|
# There is only ever one Hermes on a machine. hermes-desktop cannot run against
|
||||||
|
# this one -- it needs a runtime built from its own commit, and the version gap
|
||||||
|
# fails its readiness probe -- so it installs its own under ~/.hermes and puts
|
||||||
|
# that on PATH. When the package is present it therefore owns Hermes outright:
|
||||||
|
# this installer stands aside and removes its own copy, so the terminal, the
|
||||||
|
# default agent and the app are all the same installation.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
mode=${1:-}
|
||||||
|
|
||||||
|
tool='pipx:hermes-agent[extras=all]'
|
||||||
|
python='3.13'
|
||||||
|
|
||||||
|
# The line that identifies the stub as this installer's; matched whole, so a
|
||||||
|
# wrapper that merely mentions the command is not mistaken for ours.
|
||||||
|
marker='# Written by omarchy-install-hermes-cli.'
|
||||||
|
|
||||||
|
# The package, not the runtime directory: it is installed before the app has
|
||||||
|
# ever run, and that is exactly when we must not start building a second copy.
|
||||||
|
desktop_owns_hermes() {
|
||||||
|
omarchy-pkg-present hermes-desktop
|
||||||
|
}
|
||||||
|
|
||||||
|
# The venv appears at the python-deps stage, several stages before the one that
|
||||||
|
# installs the command, so its presence says nothing about being usable. The
|
||||||
|
# marker is written last, and the command is what the agent actually runs.
|
||||||
|
desktop_hermes_ready() {
|
||||||
|
[[ -f $HOME/.hermes/hermes-agent/.hermes-bootstrap-complete ]] || return 1
|
||||||
|
|
||||||
|
# An executable of that name proves nothing about whose it is; the app's own
|
||||||
|
# points into ~/.hermes, and anything else is not the install we are asking
|
||||||
|
# about. Matched as a plain string, because the path carries a dot and an
|
||||||
|
# unanchored pattern would also claim a wrapper pointing at ~/xhermes.
|
||||||
|
[[ -f $HOME/.local/bin/hermes ]] || return 1
|
||||||
|
grep -qF "$HOME/.hermes" "$HOME/.local/bin/hermes" || return 1
|
||||||
|
|
||||||
|
# And a marker left behind by an install whose venv has since gone answers
|
||||||
|
# for nothing, so the command has to run, exactly as a foreign one must.
|
||||||
|
hermes_prompt_ready
|
||||||
|
}
|
||||||
|
|
||||||
|
# Whether Hermes is really installed, not merely whether the stub exists. A
|
||||||
|
# stub on its own is cold: running it installs Hermes, which takes minutes.
|
||||||
|
installed() {
|
||||||
|
[[ -d "$(mise where "$tool" 2>/dev/null)/hermes-agent/lib/python$python" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# The stub is the only thing this installer owns. Anything else at that path
|
||||||
|
# -- Hermes' official installer, a hand-rolled wrapper, even a dangling link
|
||||||
|
# -- was put there by the user and is never deleted or overwritten here.
|
||||||
|
# Symlinks count as foreign even when they resolve to a marked file: the stub
|
||||||
|
# is written as a regular file, so a link is someone else's arrangement.
|
||||||
|
ours() {
|
||||||
|
[[ -f $HOME/.local/bin/hermes && ! -L $HOME/.local/bin/hermes ]] &&
|
||||||
|
grep -qxF "$marker" "$HOME/.local/bin/hermes"
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign_hermes() {
|
||||||
|
[[ -e $HOME/.local/bin/hermes || -L $HOME/.local/bin/hermes ]] && ! ours
|
||||||
|
}
|
||||||
|
|
||||||
|
# A hermes at that path is usable when it is a command that runs: a regular
|
||||||
|
# executable whose --version answers. The executable bit alone proves little -- a directory
|
||||||
|
# passes -x on search permission, and a wrapper whose interpreter or target is
|
||||||
|
# gone passes it too. The desktop app applies the same probe with the same 15
|
||||||
|
# second budget, so what passes here is what it will use.
|
||||||
|
hermes_runs() {
|
||||||
|
[[ -f $HOME/.local/bin/hermes && -x $HOME/.local/bin/hermes ]] &&
|
||||||
|
timeout 15 "$HOME/.local/bin/hermes" --version >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# A flag counts only when the help defines it, not whenever it is mentioned:
|
||||||
|
# what follows must be a shape argparse prints after a definition -- the usage
|
||||||
|
# line's closing bracket, the gap before same-line help text, an uppercase
|
||||||
|
# metavar, or the end of the line. Prose like "With --tui: run ..." stays
|
||||||
|
# prose, and --tui-theme or --tui_mode never answers for --tui. Not probed by
|
||||||
|
# parsing an actual invocation on purpose: a release that ignores unknown
|
||||||
|
# arguments would turn the probe into a live session.
|
||||||
|
help_defines_flag() {
|
||||||
|
grep -qE -- "$1(]|[[:space:]][[:upper:]]|[[:space:]]{2}|$)" <<<"$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Probed for the flags omarchy-agent actually passes -- --query to seed the
|
||||||
|
# session and --tui to keep it interactive -- rather than a marker standing in
|
||||||
|
# for them: the old probe keyed on chat carrying --oneshot, which no released
|
||||||
|
# Hermes did (it lived at the top level until v0.21 added chat's own), so
|
||||||
|
# every release read as "not ready".
|
||||||
|
hermes_prompt_ready() {
|
||||||
|
local help
|
||||||
|
hermes_runs &&
|
||||||
|
help=$(timeout 15 "$HOME/.local/bin/hermes" chat --help 2>/dev/null) &&
|
||||||
|
help_defines_flag '--tui' "$help" &&
|
||||||
|
help_defines_flag '--query' "$help"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --owns answers whether the wrapper on PATH is the one this command wrote, so
|
||||||
|
# the migration and Remove Preinstalls do not each carry their own copy of the
|
||||||
|
# marker and drift from it.
|
||||||
|
if [[ $mode == "--owns" ]]; then
|
||||||
|
if ours; then exit 0; else exit 1; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --remove tears down a Hermes CLI this installer put in place -- the mise tool
|
||||||
|
# it installed and the stub it marked -- so Remove Hermes clears a CLI the app
|
||||||
|
# never superseded (an interrupted install, or the terminal CLI from before the
|
||||||
|
# app existed) rather than leaving it stranded on PATH. The whole teardown
|
||||||
|
# turns on the marked stub, exactly as replacement does further down: without
|
||||||
|
# it nothing proves the mise environment is Omarchy's rather than one the user
|
||||||
|
# built against the same spec, and a user's stays theirs. The desktop takeover
|
||||||
|
# removes the environment without asking, but that is its own bargain -- a
|
||||||
|
# second Hermes has to go whoever built it, and the app still provides the
|
||||||
|
# command afterwards; here nothing would. Idempotent: nothing owned, nothing
|
||||||
|
# to do.
|
||||||
|
if [[ $mode == "--remove" ]]; then
|
||||||
|
if ours; then
|
||||||
|
mise rm -g "$tool" >/dev/null 2>&1 || true
|
||||||
|
mise uninstall --all "$tool" >/dev/null 2>&1 || true
|
||||||
|
rm -f "$HOME/.local/bin/hermes" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Every step is attempted before any is judged, and judged by what is left
|
||||||
|
# rather than by what the commands claimed: the marked stub still answering
|
||||||
|
# hermes, or mise still resolving the tool, is a CLI still installed no
|
||||||
|
# matter how the removal exited.
|
||||||
|
# Not "run --remove again": once the stub is gone nothing marks the mise
|
||||||
|
# environment as ours, so a rerun would find nothing it owns and succeed
|
||||||
|
# without touching what was left. Only the full commands finish the job.
|
||||||
|
if ours || mise where "$tool" >/dev/null 2>&1; then
|
||||||
|
echo "Could not remove the Hermes CLI Omarchy installed. Finish by hand:" >&2
|
||||||
|
echo " rm -f ~/.local/bin/hermes" >&2
|
||||||
|
echo " mise rm -g '$tool'" >&2
|
||||||
|
echo " mise uninstall --all '$tool'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --check lets callers tell a cold stub from a working one before they commit
|
||||||
|
# to a path that assumes Hermes is ready.
|
||||||
|
if [[ $mode == "--check" ]]; then
|
||||||
|
if desktop_owns_hermes; then
|
||||||
|
if desktop_hermes_ready; then exit 0; else exit 1; fi
|
||||||
|
fi
|
||||||
|
# A foreign command is ready only when it also supports prompted sessions;
|
||||||
|
# since it is not ours to replace, nothing this installer does will update it.
|
||||||
|
if foreign_hermes; then
|
||||||
|
if hermes_prompt_ready; then exit 0; else exit 1; fi
|
||||||
|
fi
|
||||||
|
if installed && hermes_prompt_ready; then exit 0; else exit 1; fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hand Hermes over to the app rather than keeping a second copy beside it.
|
||||||
|
if desktop_owns_hermes; then
|
||||||
|
# Not gated on that copy being healthy: `mise up` can rebuild it against the
|
||||||
|
# wrong interpreter and a half-finished install answers to neither test, and
|
||||||
|
# either way it is still a second Hermes. Removing nothing is harmless.
|
||||||
|
if mise where "$tool" >/dev/null 2>&1; then
|
||||||
|
echo "Hermes Desktop provides Hermes; removing the separate CLI install..." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
mise rm -g "$tool" >/dev/null 2>&1 || true
|
||||||
|
mise uninstall --all "$tool" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Our own stub has to go with it. Left in place it still answers `hermes`
|
||||||
|
# until the app's bootstrap overwrites it, and answering means building the
|
||||||
|
# second Hermes this whole arrangement exists to avoid.
|
||||||
|
if ours; then
|
||||||
|
rm -f "$HOME/.local/bin/hermes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if desktop_hermes_ready; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Hermes Desktop is installed but has not set Hermes up yet." >&2
|
||||||
|
echo "Launch Hermes Desktop once to finish installing it." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The user already has a hermes of their own. Leave it be: a working one is
|
||||||
|
# what the default agent will run, and a broken one is theirs to fix.
|
||||||
|
if foreign_hermes; then
|
||||||
|
if hermes_prompt_ready; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if hermes_runs; then
|
||||||
|
echo "~/.local/bin/hermes does not support the interactive seeded sessions Omarchy needs." >&2
|
||||||
|
echo "Update it to a Hermes Agent release with interactive chat queries, then run omarchy-install-hermes-cli again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "~/.local/bin/hermes exists but is not runnable, and it was not installed by Omarchy." >&2
|
||||||
|
echo "Fix or remove it, then run omarchy-install-hermes-cli again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only the marked wrapper proves the matching mise environment is ours to replace.
|
||||||
|
if installed && ! hermes_prompt_ready; then
|
||||||
|
if ours; then
|
||||||
|
echo "Updating Hermes for prompted sessions..." >&2
|
||||||
|
mise rm -g "$tool" >/dev/null 2>&1 || true
|
||||||
|
mise uninstall --all "$tool" >/dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
echo "A Hermes mise environment exists without an Omarchy-owned wrapper." >&2
|
||||||
|
echo "Update or remove it explicitly, then run omarchy-install-hermes-cli again." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
rm -f "$HOME/.local/bin/hermes"
|
||||||
|
|
||||||
|
cat >"$HOME/.local/bin/hermes" <<EOF
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
$marker
|
||||||
|
|
||||||
|
export UV_PYTHON="$python"
|
||||||
|
|
||||||
|
# Exported rather than set on the install line alone, so the version resolved
|
||||||
|
# to run agrees with the one just installed. Hermes ships several times a week
|
||||||
|
# and mise's cooldown would otherwise hold a new release back for days.
|
||||||
|
export MISE_MINIMUM_RELEASE_AGE=0
|
||||||
|
|
||||||
|
# mise up -- which omarchy update runs -- reinstalls without that pin, so this
|
||||||
|
# asks which interpreter is actually there rather than whether anything is.
|
||||||
|
if ! [[ -d "\$(mise where '$tool' 2>/dev/null)/hermes-agent/lib/python$python" ]]; then
|
||||||
|
echo "Installing Hermes on Python $python (this takes a minute)..." >&2
|
||||||
|
|
||||||
|
# mise's pipx backend shells out to uv, which a stock Omarchy does not have.
|
||||||
|
# It is fetched here rather than when this stub was written, so setting up a
|
||||||
|
# machine that never runs Hermes costs nothing.
|
||||||
|
if omarchy-cmd-missing uv && ! mise where uv >/dev/null 2>&1; then
|
||||||
|
mise use -g --quiet uv@latest || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mise use -g --quiet --force '$tool' || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The pin belongs to building Hermes, not to everything Hermes then runs.
|
||||||
|
# Exported it would reach the agent and every command it shells out to, so a
|
||||||
|
# uv in the user's own project would resolve 3.13 there too -- uv only warns
|
||||||
|
# when that contradicts the project's requires-python, and builds it anyway.
|
||||||
|
exec env -u UV_PYTHON mise x '$tool' -- hermes "\$@"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x "$HOME/.local/bin/hermes"
|
||||||
|
|
||||||
|
# The desktop app resolves a hermes on PATH by running `hermes --version` with
|
||||||
|
# a 15 second budget, then falls back to cloning its own copy when that times
|
||||||
|
# out. A first-run mise install does not fit in 15 seconds, so anything that
|
||||||
|
# hands Hermes to the GUI has to install it here rather than leave it stubbed.
|
||||||
|
if [[ $mode == "--now" ]]; then
|
||||||
|
"$HOME/.local/bin/hermes" --version
|
||||||
|
if ! hermes_prompt_ready; then
|
||||||
|
echo "Hermes installed without the interactive seeded sessions Omarchy needs." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Ensure the OpenClaw CLI is installed for the default agent
|
||||||
|
# omarchy:args=[--check|--now]
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
case "${1:---now}" in
|
||||||
|
--check)
|
||||||
|
omarchy-pkg-present openclaw
|
||||||
|
;;
|
||||||
|
--now)
|
||||||
|
if ! omarchy-pkg-present openclaw; then
|
||||||
|
echo "Installing OpenClaw..."
|
||||||
|
omarchy-pkg-add openclaw
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: omarchy-install-openclaw-cli [--check|--now]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -4,8 +4,11 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# 1Password reads the display scale itself, the way Electron apps do, and comes
|
||||||
|
# up oversized next to every other window on a scaled monitor. The packaged
|
||||||
|
# .desktop pins it too; this covers the hotkey, which runs the binary directly.
|
||||||
if omarchy-cmd-present 1password; then
|
if omarchy-cmd-present 1password; then
|
||||||
exec setsid uwsm-app -- 1password
|
exec setsid uwsm-app -- 1password --force-device-scale-factor=1
|
||||||
else
|
else
|
||||||
exec omarchy-launch-floating-terminal-with-presentation omarchy-install-service-1password
|
exec omarchy-launch-floating-terminal-with-presentation omarchy-install-service-1password
|
||||||
fi
|
fi
|
||||||
Executable
+91
@@ -0,0 +1,91 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Open the OpenClaw Control UI (or its terminal UI with --tui), onboarding or starting the gateway first when needed.
|
||||||
|
# omarchy:args=[--tui [--message <text>]]
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
tui=false
|
||||||
|
message=()
|
||||||
|
if [[ ${1:-} == "--tui" ]]; then
|
||||||
|
tui=true
|
||||||
|
shift
|
||||||
|
if [[ ${1:-} == "--message" ]]; then
|
||||||
|
message=(--message "${2:?--message needs a value}")
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Onboarding is OpenClaw's own interactive wizard, run through
|
||||||
|
# omarchy-openclaw-onboard so it stays in the terminal, installs the gateway
|
||||||
|
# as a user service, and returns (see that script for why bare `openclaw
|
||||||
|
# onboard` does none of those as of 2026.9.1). The config check gates what
|
||||||
|
# follows because the wizard's "Skip for now" also exits 0: only inference
|
||||||
|
# that passed writes the config, and skipping must not loop back into the
|
||||||
|
# wizard.
|
||||||
|
if [[ ! -f $HOME/.openclaw/openclaw.json ]]; then
|
||||||
|
if [[ $tui == "true" ]]; then
|
||||||
|
# Already in a terminal, so the wizard runs right here.
|
||||||
|
omarchy-openclaw-onboard
|
||||||
|
[[ -f $HOME/.openclaw/openclaw.json ]] || exit 1
|
||||||
|
else
|
||||||
|
# The wizard needs a real terminal, and chaining the relaunch means
|
||||||
|
# finishing it lands the user in the app. Single quotes so $HOME expands
|
||||||
|
# in the spawned terminal.
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
exec omarchy-launch-floating-terminal-with-presentation \
|
||||||
|
'omarchy-openclaw-onboard && [[ -f $HOME/.openclaw/openclaw.json ]] && omarchy-launch-openclaw'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
dashboard_url() {
|
||||||
|
# browserUrl carries a single-use browser handoff; url is the shared-auth
|
||||||
|
# fallback for gateways predating the handoff flow. The timeout keeps a
|
||||||
|
# wedged CLI from hanging an app-grid launch that has no terminal to ^C.
|
||||||
|
timeout 10 openclaw dashboard --json 2>/dev/null | jq -re '.browserUrl // .url // empty'
|
||||||
|
}
|
||||||
|
|
||||||
|
# --json never starts the gateway, so an empty answer means it is not running.
|
||||||
|
# Recovery goes through the gateway's own service commands. `dashboard --yes`
|
||||||
|
# used to be the start/install-without-prompting path, but as of 2026.9.1 it
|
||||||
|
# defers to "the owning supervisor" for both a missing and a stopped unit,
|
||||||
|
# and once the gateway is up it copies a one-time pairing URL into the
|
||||||
|
# clipboard, which nothing here needs. Enablement, not the unit file, decides:
|
||||||
|
# a unit that was written but never enabled (an install that died halfway)
|
||||||
|
# would otherwise be "started" once and stay off at every following login,
|
||||||
|
# where --force rewrites and enables it.
|
||||||
|
if ! url=$(dashboard_url); then
|
||||||
|
if systemctl --user is-enabled --quiet openclaw-gateway.service 2>/dev/null; then
|
||||||
|
timeout 60 openclaw gateway start >&2 || true
|
||||||
|
else
|
||||||
|
timeout 120 openclaw gateway install --force >&2 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A first-ever service install (unit write, daemon-reload, first boot)
|
||||||
|
# takes materially longer than starting an installed unit, so the budget
|
||||||
|
# is sized for the slow case.
|
||||||
|
for _ in {1..30}; do
|
||||||
|
url=$(dashboard_url) && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z ${url:-} ]]; then
|
||||||
|
echo "OpenClaw's gateway did not come up. Check it with: openclaw gateway status" >&2
|
||||||
|
echo "If onboarding never finished, rerun it with: omarchy-openclaw-onboard" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $tui == "true" ]]; then
|
||||||
|
# Attach to the gateway rather than `openclaw chat`: chat is the embedded
|
||||||
|
# local runtime, which refuses to start while the gateway owns ~/.openclaw's
|
||||||
|
# state directory -- and on any machine set up through Install > AI, the
|
||||||
|
# gateway service is running whenever the desktop session is.
|
||||||
|
exec openclaw tui "${message[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The handoff URL rides in the browser's argv, which uwsm's app daemon echoes
|
||||||
|
# into the user journal. Accepted: the token is single-use with a ten-minute
|
||||||
|
# expiry against a loopback-only gateway, and journal access already implies
|
||||||
|
# access to ~/.openclaw itself.
|
||||||
|
exec omarchy-launch-webapp "$url"
|
||||||
+3
-3
@@ -85,16 +85,16 @@ wait_for_pacman_transaction
|
|||||||
mkdir -p "$STATE_DIR"
|
mkdir -p "$STATE_DIR"
|
||||||
[[ -d $MIGRATIONS_DIR ]] || exit 0
|
[[ -d $MIGRATIONS_DIR ]] || exit 0
|
||||||
|
|
||||||
while IFS=$'\t' read -r name file marker; do
|
while IFS=$'\t' read -r name file marker <&3; do
|
||||||
[[ -n $name ]] || continue
|
[[ -n $name ]] || continue
|
||||||
|
|
||||||
if [[ ! -f $marker ]]; then
|
if [[ ! -f $marker ]]; then
|
||||||
echo -e "\e[32m\nRunning migration (${name%.sh})\e[0m"
|
echo -e "\e[32m\nRunning migration (${name%.sh})\e[0m"
|
||||||
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file"
|
OMARCHY_PATH="$OMARCHY_PATH" bash -euo pipefail "$file" 3<&-
|
||||||
mkdir -p "$(dirname "$marker")"
|
mkdir -p "$(dirname "$marker")"
|
||||||
touch "$marker"
|
touch "$marker"
|
||||||
fi
|
fi
|
||||||
done < <(migration_entries)
|
done 3< <(migration_entries)
|
||||||
|
|
||||||
# Clear a login-time notification the user left sitting there and then resolved
|
# Clear a login-time notification the user left sitting there and then resolved
|
||||||
# by running migrations some other way. The substring matches both the current
|
# by running migrations some other way. The substring matches both the current
|
||||||
|
|||||||
@@ -12,8 +12,38 @@ package=$1
|
|||||||
command=${2:-$1}
|
command=${2:-$1}
|
||||||
bin=${3:-$command}
|
bin=${3:-$command}
|
||||||
|
|
||||||
|
# The command name becomes a file name under ~/.local/bin, so a slash in it
|
||||||
|
# writes the wrapper somewhere else and the rm below deletes somewhere else. A
|
||||||
|
# leading dot hides it or walks up, and a leading dash makes a name that reads
|
||||||
|
# as an option to whatever picks it up. Checked before anything is removed or
|
||||||
|
# written, and kept to those shapes so package names like npm:playwright still
|
||||||
|
# stand in for the command name.
|
||||||
|
case "$command" in
|
||||||
|
*/* | .* | -* | *[[:cntrl:]]*)
|
||||||
|
echo "omarchy-mise-install: '$command' is not usable as a command name" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
mkdir -p "$HOME/.local/bin"
|
mkdir -p "$HOME/.local/bin"
|
||||||
|
|
||||||
|
# The heredoc below is unquoted, so whatever these hold is written into the
|
||||||
|
# wrapper as shell source. Quote them the way omarchy-install-and-launch does, so
|
||||||
|
# a package name carrying shell characters stays one argument instead of running.
|
||||||
|
printf -v package_arg '%q' "$package"
|
||||||
|
printf -v bin_arg '%q' "$bin"
|
||||||
|
|
||||||
|
# Keep values that are inert inside double quotes in the form existing migrations
|
||||||
|
# recognize, including mise backend options that %q would unnecessarily escape.
|
||||||
|
case "$package" in
|
||||||
|
*'$'* | *'`'* | *'"'* | *'\'* | *'!'* | *[[:cntrl:]]*) ;;
|
||||||
|
*) package_arg="\"$package\"" ;;
|
||||||
|
esac
|
||||||
|
case "$bin" in
|
||||||
|
*'$'* | *'`'* | *'"'* | *'\'* | *'!'* | *[[:cntrl:]]*) ;;
|
||||||
|
*) bin_arg="\"$bin\"" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
# These tools install and upgrade on first run, so mise's release cooldown would
|
# These tools install and upgrade on first run, so mise's release cooldown would
|
||||||
# hold a new version back for days after it ships. Exported rather than set on
|
# hold a new version back for days after it ships. Exported rather than set on
|
||||||
# the install line alone, so resolving the version to execute agrees with the
|
# the install line alone, so resolving the version to execute agrees with the
|
||||||
@@ -22,8 +52,8 @@ rm -f "$HOME/.local/bin/$command"
|
|||||||
cat >"$HOME/.local/bin/$command" <<EOF
|
cat >"$HOME/.local/bin/$command" <<EOF
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
export MISE_MINIMUM_RELEASE_AGE=0
|
export MISE_MINIMUM_RELEASE_AGE=0
|
||||||
mise use -g --quiet "$package" || exit 1
|
mise use -g --quiet $package_arg || exit 1
|
||||||
exec mise x "$package" -- "$bin" "\$@"
|
exec mise x $package_arg -- $bin_arg "\$@"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$HOME/.local/bin/$command"
|
chmod +x "$HOME/.local/bin/$command"
|
||||||
Executable
+130
@@ -0,0 +1,130 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Run OpenClaw's setup wizard the way Omarchy needs it: in the terminal, installing the gateway as a user service, and returning when it is done.
|
||||||
|
|
||||||
|
# Not bare `openclaw onboard`: as of 2026.9.1 that is the guided flow, which
|
||||||
|
# ends by running a foreground gateway and handing off to a browser tab, and
|
||||||
|
# never returns. --install-daemon keeps it to the classic wizard (minimal
|
||||||
|
# prompts with --flow quickstart) that installs the gateway service, and
|
||||||
|
# --skip-ui drops its closing Control UI/TUI prompt since whoever called this
|
||||||
|
# opens one next.
|
||||||
|
#
|
||||||
|
# That classic wizard has one wrinkle: with --skip-ui it prints "Onboarding
|
||||||
|
# complete" and then never exits. Upstream only calls exit(0) when it launched
|
||||||
|
# the TUI, and the model sign-in leaves an open socket that keeps the process
|
||||||
|
# alive otherwise. So this script watches for the gateway to answer and then
|
||||||
|
# stops the wizard. That is safe because every prompt in the quickstart flow
|
||||||
|
# runs before the gateway service is installed and reachable: by the time the
|
||||||
|
# dashboard answers, only the closing notes are left.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
wizard=(openclaw onboard --flow quickstart --install-daemon --skip-ui)
|
||||||
|
config=$HOME/.openclaw/openclaw.json
|
||||||
|
settle_seconds=${OMARCHY_OPENCLAW_ONBOARD_SETTLE_SECONDS:-3}
|
||||||
|
# Counted from the moment the config exists, i.e. once the wizard has applied
|
||||||
|
# setup; the prompts before that take as long as the user takes.
|
||||||
|
gateway_timeout=${OMARCHY_OPENCLAW_ONBOARD_GATEWAY_TIMEOUT:-180}
|
||||||
|
|
||||||
|
gateway_answers() {
|
||||||
|
timeout 10 openclaw dashboard --json 2>/dev/null | jq -e '.ok == true' >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# A gateway already answering means OpenClaw is set up, and this run must not
|
||||||
|
# read its own success off state that predates it: the wizard's repair pass
|
||||||
|
# would be stopped mid-prompt the moment the watcher looked. Nothing to do.
|
||||||
|
if [[ -f $config ]] && gateway_answers; then
|
||||||
|
echo "OpenClaw is already set up and its gateway is running." >&2
|
||||||
|
echo "To change providers or settings, run: openclaw onboard --classic" >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Marks when this run began, so a config left behind by an earlier, incomplete
|
||||||
|
# setup is not mistaken for this run having applied its own: the gateway
|
||||||
|
# deadline below must not start ticking while the user is still at prompts.
|
||||||
|
started=$(mktemp)
|
||||||
|
trap 'rm -f "$started"' EXIT
|
||||||
|
|
||||||
|
# Backgrounded so this script can watch it, but with the terminal kept as its
|
||||||
|
# stdin (bash would otherwise hand a background job /dev/null). Job control is
|
||||||
|
# off in a script, so it stays in the terminal's foreground process group and
|
||||||
|
# reads from it freely. Ctrl-C does not reach it directly, though: bash starts
|
||||||
|
# async children with SIGINT ignored when job control is off, so the INT trap
|
||||||
|
# below is what turns Ctrl-C into the wizard's exit.
|
||||||
|
"${wizard[@]}" <&0 &
|
||||||
|
wizard_pid=$!
|
||||||
|
|
||||||
|
stop_wizard() {
|
||||||
|
kill -TERM "$wizard_pid" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
# Any signal at this script, whether Ctrl-C from the terminal or a kill aimed
|
||||||
|
# at its pid alone, takes the wizard down with it rather than leaving it
|
||||||
|
# running unwatched.
|
||||||
|
trap 'stop_wizard' INT TERM HUP
|
||||||
|
|
||||||
|
# Whether this run has applied setup: the config exists and is not older than
|
||||||
|
# the run itself.
|
||||||
|
config_applied() {
|
||||||
|
[[ -f $config && ! $started -nt $config ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Whether this run's gateway is up: setup applied, and the process answering
|
||||||
|
# on the gateway's port is the main process of the service the wizard
|
||||||
|
# installs. Not the dashboard alone: with no config on disk `openclaw
|
||||||
|
# dashboard --json` still probes the default loopback port, so a gateway left
|
||||||
|
# behind by something else (an earlier guided onboarding's foreground
|
||||||
|
# gateway, say) would read as this run's success while the user is still at
|
||||||
|
# the first prompt. And not the unit being active either: its Type=simple
|
||||||
|
# counts it active from the fork, before it has found the port taken by such
|
||||||
|
# an orphan, and the app would then open on the orphan rather than the
|
||||||
|
# service this run installed.
|
||||||
|
gateway_ready() {
|
||||||
|
config_applied || return 1
|
||||||
|
local json port listener main_pid
|
||||||
|
json=$(timeout 10 openclaw dashboard --json 2>/dev/null) || return 1
|
||||||
|
jq -e '.ok == true' <<<"$json" >/dev/null 2>&1 || return 1
|
||||||
|
port=$(jq -r '.port // empty' <<<"$json" 2>/dev/null)
|
||||||
|
[[ -n $port ]] || return 1
|
||||||
|
listener=$(ss -ltnpH "sport = :$port" 2>/dev/null | sed -n 's/.*pid=\([0-9]*\).*/\1/p' | head -1)
|
||||||
|
main_pid=$(systemctl --user show -p MainPID --value openclaw-gateway.service 2>/dev/null)
|
||||||
|
[[ -n $listener && -n $main_pid && $main_pid != 0 && $listener == "$main_pid" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
stopped=false
|
||||||
|
timed_out=false
|
||||||
|
config_seen_at=
|
||||||
|
while kill -0 "$wizard_pid" 2>/dev/null; do
|
||||||
|
sleep 2
|
||||||
|
if gateway_ready; then
|
||||||
|
# Let the outro finish printing, then end the process the wizard leaves
|
||||||
|
# running.
|
||||||
|
sleep "$settle_seconds"
|
||||||
|
stop_wizard
|
||||||
|
stopped=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
config_applied || continue
|
||||||
|
: "${config_seen_at:=$SECONDS}"
|
||||||
|
if (( SECONDS - config_seen_at >= gateway_timeout )); then
|
||||||
|
# Setup was applied but the service never came up (port taken, unit
|
||||||
|
# failing, ...): the wizard would sit in its never-exiting state forever,
|
||||||
|
# and so would whoever is waiting on this script.
|
||||||
|
stop_wizard
|
||||||
|
timed_out=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
wait "$wizard_pid"
|
||||||
|
rc=$?
|
||||||
|
|
||||||
|
if [[ $timed_out == true ]]; then
|
||||||
|
echo "OpenClaw's gateway did not come up within ${gateway_timeout}s of setup finishing." >&2
|
||||||
|
echo "Check it with: openclaw gateway status" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# A wizard stopped here after the gateway came up did its job. One that
|
||||||
|
# exited on its own, including a user who chose "Skip for now" (no config,
|
||||||
|
# non-zero), keeps its own exit code.
|
||||||
|
[[ $stopped == true ]] && rc=0
|
||||||
|
exit "$rc"
|
||||||
@@ -24,6 +24,6 @@ if [[ -n $pkg_names ]]; then
|
|||||||
source omarchy-sudo-keepalive
|
source omarchy-sudo-keepalive
|
||||||
|
|
||||||
echo "$pkg_names" | sed 's/^/aur\//' | tr '\n' ' ' | xargs yay -S --noconfirm
|
echo "$pkg_names" | sed 's/^/aur\//' | tr '\n' ' ' | xargs yay -S --noconfirm
|
||||||
sudo updatedb
|
sudo updatedb --prune-bind-mounts=no --add-prunepaths=/.snapshots
|
||||||
omarchy-show-done
|
omarchy-show-done
|
||||||
fi
|
fi
|
||||||
@@ -3,5 +3,7 @@
|
|||||||
# omarchy:summary=Restore the default Omarchy Plymouth boot theme and SDDM login screen
|
# omarchy:summary=Restore the default Omarchy Plymouth boot theme and SDDM login screen
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
omarchy-refresh-plymouth
|
set -euo pipefail
|
||||||
omarchy-refresh-sddm
|
|
||||||
|
"$OMARCHY_PATH/bin/omarchy-refresh-plymouth"
|
||||||
|
"$OMARCHY_PATH/bin/omarchy-refresh-sddm"
|
||||||
+365
-76
@@ -5,88 +5,377 @@
|
|||||||
# omarchy:examples=omarchy plymouth set '#1d2021' '#ebdbb2' ~/.local/state/omarchy/current/theme/plymouth/logo.png
|
# omarchy:examples=omarchy plymouth set '#1d2021' '#ebdbb2' ~/.local/state/omarchy/current/theme/plymouth/logo.png
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
# Configure the Plymouth boot theme with a custom background color, text color, and logo.
|
set -euo pipefail
|
||||||
# Stages the change in a temp dir, then commits the staged files to /usr/share and
|
|
||||||
# rebuilds the initramfs. Also syncs the SDDM login screen (the post-logout
|
|
||||||
# screen) with the same colors and logo so boot/login stay visually unified.
|
|
||||||
|
|
||||||
if (( $# != 3 )); then
|
# Build the authoritative theme in a root-owned directory, then publish each
|
||||||
|
# fixed destination atomically. The caller opens the selected logo before sudo,
|
||||||
|
# so the privileged process never resolves a user-controlled input path.
|
||||||
|
|
||||||
|
usage() {
|
||||||
echo "Usage: omarchy-plymouth-set <background-hex> <text-hex> <path-to-logo.png>" >&2
|
echo "Usage: omarchy-plymouth-set <background-hex> <text-hex> <path-to-logo.png>" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
bg_hex="${1#\#}"
|
if (( $# == 3 )); then
|
||||||
text_hex="${2#\#}"
|
mode=set
|
||||||
logo_path="$3"
|
elif (( $# == 1 )); then
|
||||||
|
case "$1" in
|
||||||
if ! [[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
--refresh-default)
|
||||||
echo "Invalid background color: $1 (expected #RRGGBB)" >&2
|
mode=refresh-plymouth
|
||||||
exit 1
|
;;
|
||||||
fi
|
--refresh-sddm-default)
|
||||||
|
mode=refresh-sddm
|
||||||
if ! [[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
;;
|
||||||
echo "Invalid text color: $2 (expected #RRGGBB)" >&2
|
*)
|
||||||
exit 1
|
usage
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
if [[ ! -f $logo_path ]]; then
|
|
||||||
echo "Logo file not found: $logo_path" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# omarchy-plymouth-set-by-theme passes a theme's unlock.png straight from
|
|
||||||
# ~/.config/omarchy/themes, where an installed theme can make it a symlink to
|
|
||||||
# anything. The copies below land in world-readable /usr/share, so following one
|
|
||||||
# would republish whatever it points at.
|
|
||||||
if [[ -L $logo_path ]]; then
|
|
||||||
echo "Logo file is a symlink, which is not accepted: $logo_path" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
bg_r=$(awk -v n=$((16#${bg_hex:0:2})) 'BEGIN{printf "%.3f", n/255}')
|
|
||||||
bg_g=$(awk -v n=$((16#${bg_hex:2:2})) 'BEGIN{printf "%.3f", n/255}')
|
|
||||||
bg_b=$(awk -v n=$((16#${bg_hex:4:2})) 'BEGIN{printf "%.3f", n/255}')
|
|
||||||
|
|
||||||
theme_dir="/usr/share/plymouth/themes/omarchy"
|
|
||||||
staging_dir=$(mktemp -d)
|
|
||||||
trap 'rm -rf "$staging_dir"' EXIT
|
|
||||||
|
|
||||||
find "$OMARCHY_PATH/default/plymouth" -maxdepth 1 -type f -exec cp -t "$staging_dir/" {} +
|
|
||||||
cp "$logo_path" "$staging_dir/logo.png"
|
|
||||||
|
|
||||||
sed -i \
|
|
||||||
-e "s/^Window.SetBackgroundTopColor.*/Window.SetBackgroundTopColor($bg_r, $bg_g, $bg_b);/" \
|
|
||||||
-e "s/^Window.SetBackgroundBottomColor.*/Window.SetBackgroundBottomColor($bg_r, $bg_g, $bg_b);/" \
|
|
||||||
"$staging_dir/omarchy.script"
|
|
||||||
|
|
||||||
for asset in bullet.png entry.png lock.png progress_bar.png; do
|
|
||||||
magick "$staging_dir/$asset" -channel RGB +level-colors "#$text_hex","#$text_hex" "$staging_dir/$asset"
|
|
||||||
done
|
|
||||||
|
|
||||||
sudo cp -a --no-preserve=mode,ownership "$staging_dir/." "$theme_dir/"
|
|
||||||
sudo plymouth-set-default-theme omarchy
|
|
||||||
|
|
||||||
if omarchy-cmd-present limine-mkinitcpio; then
|
|
||||||
sudo limine-mkinitcpio
|
|
||||||
else
|
else
|
||||||
sudo mkinitcpio -P
|
usage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Sync the SDDM login screen with the same colors and logo.
|
if (( EUID == 0 )); then
|
||||||
sddm_dir="/usr/share/sddm/themes/omarchy"
|
echo "Error: run omarchy-plymouth-set as your user, not under sudo." >&2
|
||||||
sddm_template="$OMARCHY_PATH/default/sddm/omarchy/Main.qml"
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
sed \
|
logo_fd=
|
||||||
-e "s/#1a1b26/#$bg_hex/g" \
|
if [[ $mode != "set" ]]; then
|
||||||
-e "s/#ffffff/#$text_hex/g" \
|
bg_hex=
|
||||||
"$sddm_template" | sudo tee "$sddm_dir/Main.qml" >/dev/null
|
text_hex=
|
||||||
|
else
|
||||||
|
bg_hex="${1#\#}"
|
||||||
|
text_hex="${2#\#}"
|
||||||
|
logo_path="$3"
|
||||||
|
|
||||||
sudo cp "$staging_dir/logo.png" "$sddm_dir/logo.png"
|
if ! [[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||||
for asset in bullet.png entry.png lock.png; do
|
echo "Invalid background color: $1 (expected #RRGGBB)" >&2
|
||||||
sudo cp "$staging_dir/$asset" "$sddm_dir/$asset"
|
exit 1
|
||||||
done
|
fi
|
||||||
for asset in entry lock; do
|
|
||||||
magick "$staging_dir/$asset.png" -channel RGB +level-colors "#f7768e","#f7768e" "$staging_dir/$asset-failed.png"
|
if ! [[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]; then
|
||||||
sudo cp "$staging_dir/$asset-failed.png" "$sddm_dir/$asset-failed.png"
|
echo "Invalid text color: $2 (expected #RRGGBB)" >&2
|
||||||
done
|
exit 1
|
||||||
sudo rm -f "$sddm_dir/logo.svg"
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f $logo_path ]]; then
|
||||||
|
echo "Logo file not found: $logo_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -L $logo_path ]]; then
|
||||||
|
echo "Logo file is a symlink, which is not accepted: $logo_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Open the logo while still unprivileged. A replacement symlink to a root-only
|
||||||
|
# file therefore fails here instead of being followed after sudo starts.
|
||||||
|
if ! exec {logo_fd}<"$logo_path"; then
|
||||||
|
echo "Unable to open logo file as the current user: $logo_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f /proc/$$/fd/$logo_fd ]]; then
|
||||||
|
echo "Logo input is no longer a regular file: $logo_path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_root_transaction() {
|
||||||
|
sudo /bin/bash -c '
|
||||||
|
set -eEuo pipefail
|
||||||
|
PATH=/usr/bin:/bin
|
||||||
|
export PATH
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
# Every check below is a bare assertion that aborts under set -e. Name the
|
||||||
|
# subject of each one so a refusal reaches the user instead of exiting mute.
|
||||||
|
failure_context="the privileged Plymouth transaction"
|
||||||
|
failure_reported=
|
||||||
|
report_failure() {
|
||||||
|
[[ -z $failure_reported ]] || return 0
|
||||||
|
failure_reported=1
|
||||||
|
printf "omarchy-plymouth-set: refusing to publish: %s failed validation\n" "$failure_context" >&2
|
||||||
|
if [[ -n ${failure_hint:-} ]]; then
|
||||||
|
printf "omarchy-plymouth-set: %s\n" "$failure_hint" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap report_failure ERR
|
||||||
|
|
||||||
|
mode=$1
|
||||||
|
source_root=$2
|
||||||
|
bg_hex=$3
|
||||||
|
text_hex=$4
|
||||||
|
max_asset_size=$5
|
||||||
|
|
||||||
|
failure_context="the arguments of the privileged transaction"
|
||||||
|
[[ $mode == "set" || $mode == "refresh-plymouth" || $mode == "refresh-sddm" ]]
|
||||||
|
[[ $source_root == /* ]]
|
||||||
|
[[ $max_asset_size =~ ^[0-9]+$ ]]
|
||||||
|
(( max_asset_size > 0 ))
|
||||||
|
|
||||||
|
failure_context="the Omarchy source tree $source_root"
|
||||||
|
canonical_source_root=$(realpath -e -- "$source_root")
|
||||||
|
[[ $canonical_source_root == "$source_root" ]]
|
||||||
|
|
||||||
|
validate_trusted_directory() {
|
||||||
|
local directory=$1 canonical uid directory_mode
|
||||||
|
|
||||||
|
failure_context="directory $directory"
|
||||||
|
canonical=$(realpath -e -- "$directory")
|
||||||
|
[[ $canonical == "$directory" && -d $directory && ! -L $directory ]]
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
failure_context="directory $directory (must be root-owned and not group- or world-writable)"
|
||||||
|
uid=$(stat -c %u -- "$directory")
|
||||||
|
directory_mode=$(stat -c %a -- "$directory")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$directory_mode & 0022) == 0 ))
|
||||||
|
[[ $directory == "/" ]] && break
|
||||||
|
directory=${directory%/*}
|
||||||
|
[[ -n $directory ]] || directory=/
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_trusted_configuration_file() {
|
||||||
|
local configuration=$1 canonical uid configuration_mode size
|
||||||
|
|
||||||
|
failure_context="root configuration $configuration"
|
||||||
|
[[ -f $configuration && ! -L $configuration ]]
|
||||||
|
canonical=$(realpath -e -- "$configuration")
|
||||||
|
[[ $canonical == "$configuration" ]]
|
||||||
|
validate_trusted_directory "${configuration%/*}"
|
||||||
|
uid=$(stat -c %u -- "$configuration")
|
||||||
|
configuration_mode=$(stat -c %a -- "$configuration")
|
||||||
|
size=$(stat -c %s -- "$configuration")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$configuration_mode & 0022) == 0 ))
|
||||||
|
(( size > 0 && size <= 4096 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
# A packaged tree must be root-owned. A development checkout is the one
|
||||||
|
# deliberate exception: omarchy dev link records its canonical path in a
|
||||||
|
# root-owned /etc/omarchy.conf. That is already an explicit decision to run
|
||||||
|
# privileged Omarchy commands from user-editable code in the checkout, so
|
||||||
|
# reading its packaged assets does not widen the development trust boundary.
|
||||||
|
development_source=false
|
||||||
|
source_root_uid=$(stat -c %u -- "$source_root")
|
||||||
|
if (( source_root_uid != 0 )); then
|
||||||
|
omarchy_conf=/etc/omarchy.conf
|
||||||
|
failure_context="$source_root is user-owned and $omarchy_conf must contain its trusted dev-link authorization; run omarchy dev link to authorize it"
|
||||||
|
failure_hint="$source_root is user-owned; run omarchy dev link to authorize this development checkout, or omarchy dev unlink to use the packaged tree"
|
||||||
|
validate_trusted_configuration_file "$omarchy_conf"
|
||||||
|
|
||||||
|
# validate_trusted_configuration_file walks /etc up to / and leaves its own
|
||||||
|
# subject behind in failure_context. Without restoring ours, a checkout
|
||||||
|
# that simply is not the authorized one refuses with "directory / must be
|
||||||
|
# root-owned and not group- or world-writable" -- naming a directory that
|
||||||
|
# passed, and sending the reader after a filesystem problem that is not
|
||||||
|
# there.
|
||||||
|
failure_context="the dev-link authorization in $omarchy_conf, which must name $source_root"
|
||||||
|
|
||||||
|
quoted_source_root=$source_root
|
||||||
|
quoted_source_root=${quoted_source_root//\\/\\\\}
|
||||||
|
quoted_source_root=${quoted_source_root//\"/\\\"}
|
||||||
|
quoted_source_root=${quoted_source_root//\$/\\\$}
|
||||||
|
quoted_source_root=${quoted_source_root//\`/\\\`}
|
||||||
|
expected_config_line="export OMARCHY_PATH=\"$quoted_source_root\""
|
||||||
|
mapfile -t omarchy_config_lines <"$omarchy_conf"
|
||||||
|
(( ${#omarchy_config_lines[@]} == 1 ))
|
||||||
|
[[ ${omarchy_config_lines[0]} == "$expected_config_line" ]]
|
||||||
|
development_source=true
|
||||||
|
failure_hint=
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $mode == "set" ]]; then
|
||||||
|
[[ $bg_hex =~ ^[0-9a-fA-F]{6}$ ]]
|
||||||
|
[[ $text_hex =~ ^[0-9a-fA-F]{6}$ ]]
|
||||||
|
fi
|
||||||
|
|
||||||
|
theme_dir=/usr/share/plymouth/themes/omarchy
|
||||||
|
sddm_dir=/usr/share/sddm/themes/omarchy
|
||||||
|
plymouth_theme_assets=(
|
||||||
|
bullet.png
|
||||||
|
entry.png
|
||||||
|
lock.png
|
||||||
|
logo.png
|
||||||
|
omarchy.plymouth
|
||||||
|
omarchy.script
|
||||||
|
preview-unlock.png
|
||||||
|
progress_bar.png
|
||||||
|
progress_box.png
|
||||||
|
)
|
||||||
|
plymouth_default_assets=("${plymouth_theme_assets[@]}" logos/oma.png)
|
||||||
|
sddm_theme_assets=(Main.qml bullet.png entry-failed.png entry.png lock-failed.png lock.png logo.png)
|
||||||
|
sddm_default_assets=("${sddm_theme_assets[@]}" metadata.desktop theme.conf)
|
||||||
|
|
||||||
|
plymouth_assets=()
|
||||||
|
sddm_assets=()
|
||||||
|
case "$mode" in
|
||||||
|
set)
|
||||||
|
plymouth_assets=("${plymouth_theme_assets[@]}")
|
||||||
|
sddm_assets=("${sddm_theme_assets[@]}")
|
||||||
|
;;
|
||||||
|
refresh-plymouth)
|
||||||
|
plymouth_assets=("${plymouth_default_assets[@]}")
|
||||||
|
;;
|
||||||
|
refresh-sddm)
|
||||||
|
sddm_assets=("${sddm_default_assets[@]}")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
validate_trusted_file() {
|
||||||
|
local source=$1 canonical uid file_mode size
|
||||||
|
|
||||||
|
failure_context="packaged source file $source"
|
||||||
|
[[ -f $source && ! -L $source ]]
|
||||||
|
canonical=$(realpath -e -- "$source")
|
||||||
|
[[ $canonical == "$source" ]]
|
||||||
|
file_mode=$(stat -c %a -- "$source")
|
||||||
|
size=$(stat -c %s -- "$source")
|
||||||
|
(( size > 0 && size <= max_asset_size ))
|
||||||
|
|
||||||
|
if ! $development_source; then
|
||||||
|
validate_trusted_directory "${source%/*}"
|
||||||
|
uid=$(stat -c %u -- "$source")
|
||||||
|
(( uid == 0 ))
|
||||||
|
(( (8#$file_mode & 0022) == 0 ))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_trusted_file() {
|
||||||
|
local source=$1 destination=$2
|
||||||
|
|
||||||
|
validate_trusted_file "$source"
|
||||||
|
install -o 0 -g 0 -m 0600 -- "$source" "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
staging_dir=$(mktemp -d /tmp/omarchy-plymouth.XXXXXXXX)
|
||||||
|
temporary=
|
||||||
|
cleanup() {
|
||||||
|
[[ -z $temporary ]] || rm -f -- "$temporary"
|
||||||
|
rm -rf -- "$staging_dir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
chown 0:0 -- "$staging_dir"
|
||||||
|
chmod 0700 -- "$staging_dir"
|
||||||
|
|
||||||
|
plymouth_stage=$staging_dir/plymouth
|
||||||
|
sddm_stage=$staging_dir/sddm
|
||||||
|
mkdir -m 0700 -p -- "$plymouth_stage/logos" "$sddm_stage"
|
||||||
|
|
||||||
|
for asset in "${plymouth_assets[@]}"; do
|
||||||
|
copy_trusted_file "$source_root/default/plymouth/$asset" "$plymouth_stage/$asset"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $mode == "set" ]]; then
|
||||||
|
# stdin was opened by the unprivileged caller. Read no more than the
|
||||||
|
# documented limit into the root-owned stage before doing other work.
|
||||||
|
failure_context="the selected logo (expected 1 to $max_asset_size bytes)"
|
||||||
|
head -c "$((max_asset_size + 1))" >"$plymouth_stage/logo.png"
|
||||||
|
logo_size=$(stat -c %s -- "$plymouth_stage/logo.png")
|
||||||
|
(( logo_size > 0 && logo_size <= max_asset_size ))
|
||||||
|
chown 0:0 -- "$plymouth_stage/logo.png"
|
||||||
|
chmod 0600 -- "$plymouth_stage/logo.png"
|
||||||
|
cp --reflink=never -- "$plymouth_stage/logo.png" "$sddm_stage/logo.png"
|
||||||
|
|
||||||
|
bg_r=$(awk -v n=$((16#${bg_hex:0:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||||
|
bg_g=$(awk -v n=$((16#${bg_hex:2:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||||
|
bg_b=$(awk -v n=$((16#${bg_hex:4:2})) "BEGIN{printf \"%.3f\", n/255}")
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e "s/^Window.SetBackgroundTopColor.*/Window.SetBackgroundTopColor($bg_r, $bg_g, $bg_b);/" \
|
||||||
|
-e "s/^Window.SetBackgroundBottomColor.*/Window.SetBackgroundBottomColor($bg_r, $bg_g, $bg_b);/" \
|
||||||
|
"$plymouth_stage/omarchy.script"
|
||||||
|
|
||||||
|
for asset in bullet.png entry.png lock.png progress_bar.png; do
|
||||||
|
magick "$plymouth_stage/$asset" -channel RGB +level-colors "#$text_hex","#$text_hex" "$plymouth_stage/$asset"
|
||||||
|
done
|
||||||
|
|
||||||
|
copy_trusted_file "$source_root/default/sddm/omarchy/Main.qml" "$sddm_stage/Main.qml"
|
||||||
|
sed -i \
|
||||||
|
-e "s/#1a1b26/#__OMARCHY_SDDM_BG__/g" \
|
||||||
|
-e "s/#ffffff/#__OMARCHY_SDDM_TEXT__/g" \
|
||||||
|
-e "s/#__OMARCHY_SDDM_BG__/#$bg_hex/g" \
|
||||||
|
-e "s/#__OMARCHY_SDDM_TEXT__/#$text_hex/g" \
|
||||||
|
"$sddm_stage/Main.qml"
|
||||||
|
|
||||||
|
for asset in bullet.png entry.png lock.png; do
|
||||||
|
cp --reflink=never -- "$plymouth_stage/$asset" "$sddm_stage/$asset"
|
||||||
|
done
|
||||||
|
for asset in entry lock; do
|
||||||
|
magick "$plymouth_stage/$asset.png" -channel RGB +level-colors "#f7768e","#f7768e" "$sddm_stage/$asset-failed.png"
|
||||||
|
done
|
||||||
|
chown -R 0:0 -- "$staging_dir"
|
||||||
|
find "$staging_dir" -type f -exec chmod 0600 -- {} +
|
||||||
|
elif (( ${#sddm_assets[@]} )); then
|
||||||
|
for asset in "${sddm_assets[@]}"; do
|
||||||
|
copy_trusted_file "$source_root/default/sddm/omarchy/$asset" "$sddm_stage/$asset"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ${#plymouth_assets[@]} )); then
|
||||||
|
validate_trusted_directory "$theme_dir"
|
||||||
|
if [[ $mode == "refresh-plymouth" ]]; then
|
||||||
|
validate_trusted_directory "$theme_dir/logos"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if (( ${#sddm_assets[@]} )); then
|
||||||
|
validate_trusted_directory "$sddm_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
publish_asset() {
|
||||||
|
local source=$1 destination=$2 parent filename source_size copied_size
|
||||||
|
|
||||||
|
failure_context="destination $destination"
|
||||||
|
[[ -f $source && ! -L $source ]]
|
||||||
|
(( $(stat -c %u -- "$source") == 0 ))
|
||||||
|
source_size=$(stat -c %s -- "$source")
|
||||||
|
(( source_size > 0 && source_size <= max_asset_size ))
|
||||||
|
|
||||||
|
[[ $destination == /* && $destination != */ && $destination != *"/../"* ]]
|
||||||
|
parent=${destination%/*}
|
||||||
|
filename=${destination##*/}
|
||||||
|
[[ -n $parent && -n $filename && $filename != "." && $filename != ".." ]]
|
||||||
|
validate_trusted_directory "$parent"
|
||||||
|
|
||||||
|
temporary=$(mktemp --tmpdir="$parent" ".$filename.omarchy-new.XXXXXXXX")
|
||||||
|
install -o 0 -g 0 -m 0644 -- "$source" "$temporary"
|
||||||
|
copied_size=$(stat -c %s -- "$temporary")
|
||||||
|
(( copied_size == source_size ))
|
||||||
|
cmp -s -- "$source" "$temporary"
|
||||||
|
sync -f -- "$temporary"
|
||||||
|
mv --no-copy -fT -- "$temporary" "$destination"
|
||||||
|
temporary=
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( ${#plymouth_assets[@]} )); then
|
||||||
|
for asset in "${plymouth_assets[@]}"; do
|
||||||
|
publish_asset "$plymouth_stage/$asset" "$theme_dir/$asset"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ${#sddm_assets[@]} )); then
|
||||||
|
for asset in "${sddm_assets[@]}"; do
|
||||||
|
publish_asset "$sddm_stage/$asset" "$sddm_dir/$asset"
|
||||||
|
done
|
||||||
|
validate_trusted_directory "$sddm_dir"
|
||||||
|
rm -f -- "$sddm_dir/logo.svg"
|
||||||
|
fi
|
||||||
|
' bash "$mode" "$OMARCHY_PATH" "$bg_hex" "$text_hex" "$((64 * 1024 * 1024))"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $mode == "set" ]]; then
|
||||||
|
run_root_transaction <&"$logo_fd"
|
||||||
|
else
|
||||||
|
run_root_transaction </dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $mode != "refresh-sddm" ]]; then
|
||||||
|
sudo plymouth-set-default-theme omarchy
|
||||||
|
|
||||||
|
if omarchy-cmd-present limine-mkinitcpio; then
|
||||||
|
sudo limine-mkinitcpio
|
||||||
|
else
|
||||||
|
sudo mkinitcpio -P
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -677,11 +677,15 @@ user_groups() {
|
|||||||
if [[ -f $PROVISIONING_DIR/groups ]]; then
|
if [[ -f $PROVISIONING_DIR/groups ]]; then
|
||||||
while IFS= read -r group; do
|
while IFS= read -r group; do
|
||||||
[[ -n $group ]] || continue
|
[[ -n $group ]] || continue
|
||||||
# Never grant docker at first boot, even if an older install recorded it
|
# Never replay old privileged group defaults. Docker is always opt-in.
|
||||||
# (or a factory snapshot predating the opt-in default carries it): the
|
# Input is only retained when the factory image has one of the features
|
||||||
# docker group is root-equivalent. It is opt-in via
|
# whose installer deliberately grants access to raw input devices.
|
||||||
# omarchy-setup-security-sudoless-docker.
|
|
||||||
[[ $group == "docker" ]] && continue
|
[[ $group == "docker" ]] && continue
|
||||||
|
if [[ $group == "input" ]] &&
|
||||||
|
! pacman -Qq xpadneo-dkms &>/dev/null &&
|
||||||
|
! pacman -Qq ydotool &>/dev/null; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
getent group "$group" >/dev/null || continue
|
getent group "$group" >/dev/null || continue
|
||||||
[[ ",$groups," == *",$group,"* ]] || groups+=",$group"
|
[[ ",$groups," == *",$group,"* ]] || groups+=",$group"
|
||||||
done <"$PROVISIONING_DIR/groups"
|
done <"$PROVISIONING_DIR/groups"
|
||||||
@@ -742,6 +746,12 @@ create_user() {
|
|||||||
# for specific commands), and a duplicate grant is harmless.
|
# for specific commands), and a duplicate grant is harmless.
|
||||||
echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-omarchy-wheel
|
echo "%wheel ALL=(ALL:ALL) ALL" >/etc/sudoers.d/00-omarchy-wheel
|
||||||
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
chmod 440 /etc/sudoers.d/00-omarchy-wheel
|
||||||
|
|
||||||
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||||
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
|
browser_policy_setup_dir "$dir"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
install_authorized_keys() {
|
install_authorized_keys() {
|
||||||
@@ -782,7 +792,7 @@ configure_login() {
|
|||||||
# After=) is what makes it deterministic — no sleep/race against SDDM's startup.
|
# After=) is what makes it deterministic — no sleep/race against SDDM's startup.
|
||||||
install_autologin_once_cleanup() {
|
install_autologin_once_cleanup() {
|
||||||
local unit=omarchy-provision-autologin-once.service
|
local unit=omarchy-provision-autologin-once.service
|
||||||
cat >"/etc/systemd/system/$unit" <<UNIT
|
sed "s|@UNIT@|$unit|g" >"/etc/systemd/system/$unit" <<'UNIT'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Drop the first-boot autologin before the next login
|
Description=Drop the first-boot autologin before the next login
|
||||||
Before=display-manager.service
|
Before=display-manager.service
|
||||||
@@ -791,7 +801,7 @@ ConditionPathExists=/etc/sddm.conf.d/autologin.conf
|
|||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/bin/rm -f /etc/sddm.conf.d/autologin.conf
|
ExecStart=/usr/bin/rm -f /etc/sddm.conf.d/autologin.conf
|
||||||
ExecStartPost=/usr/bin/rm -f /etc/systemd/system/graphical.target.wants/$unit /etc/systemd/system/$unit
|
ExecStartPost=/usr/bin/rm -f /etc/systemd/system/graphical.target.wants/@UNIT@ /etc/systemd/system/@UNIT@
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=graphical.target
|
WantedBy=graphical.target
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ fi
|
|||||||
# Dev-aware skill symlinks. Cannot live in /etc/skel because OMARCHY_PATH may
|
# Dev-aware skill symlinks. Cannot live in /etc/skel because OMARCHY_PATH may
|
||||||
# point at a dev checkout (omarchy dev link) where the target differs.
|
# point at a dev checkout (omarchy dev link) where the target differs.
|
||||||
# Loops every skill directory, so shipping a new one needs no edit here.
|
# Loops every skill directory, so shipping a new one needs no edit here.
|
||||||
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills
|
mkdir -p ~/.agents/skills ~/.claude/skills ~/.codex/skills ~/.pi/agent/skills ~/.hermes/skills
|
||||||
for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
|
for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
|
||||||
skill=${skill%/}
|
skill=${skill%/}
|
||||||
name=${skill##*/}
|
name=${skill##*/}
|
||||||
@@ -92,6 +92,14 @@ for skill in "$OMARCHY_PATH"/default/agents/skills/*/; do
|
|||||||
ln -sfn "$skill" ~/.claude/skills/"$name"
|
ln -sfn "$skill" ~/.claude/skills/"$name"
|
||||||
ln -sfn "$skill" ~/.codex/skills/"$name"
|
ln -sfn "$skill" ~/.codex/skills/"$name"
|
||||||
ln -sfn "$skill" ~/.pi/agent/skills/"$name"
|
ln -sfn "$skill" ~/.pi/agent/skills/"$name"
|
||||||
|
ln -sfn "$skill" ~/.hermes/skills/"$name"
|
||||||
|
if [[ -d ~/.hermes/profiles ]]; then
|
||||||
|
for profile in ~/.hermes/profiles/*/; do
|
||||||
|
[[ -d $profile ]] || continue
|
||||||
|
mkdir -p "$profile/skills"
|
||||||
|
ln -sfn "$skill" "$profile/skills/$name"
|
||||||
|
done
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0
|
mkdir -p ~/Downloads ~/Pictures ~/Videos ~/.config/gtk-3.0
|
||||||
|
|||||||
@@ -3,11 +3,6 @@
|
|||||||
# omarchy:summary=Overwrite the user config for the Plymouth drive decryption and boot sequence with the Omarchy default and rebuild it.
|
# omarchy:summary=Overwrite the user config for the Plymouth drive decryption and boot sequence with the Omarchy default and rebuild it.
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
sudo cp -r "$OMARCHY_PATH/default/plymouth/." /usr/share/plymouth/themes/omarchy/
|
# Reuse the fixed-file publisher so root never resolves the source checkout or
|
||||||
sudo plymouth-set-default-theme omarchy
|
# follows a destination symlink while restoring the packaged assets.
|
||||||
|
exec "$OMARCHY_PATH/bin/omarchy-plymouth-set" --refresh-default
|
||||||
if omarchy-cmd-present limine-mkinitcpio; then
|
|
||||||
sudo limine-mkinitcpio
|
|
||||||
else
|
|
||||||
sudo mkinitcpio -P
|
|
||||||
fi
|
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
# omarchy:summary=Refresh the SDDM theme from default
|
# omarchy:summary=Refresh the SDDM theme from default
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
sudo rm -rf /usr/share/sddm/themes/omarchy
|
# Reuse the fixed-file publisher so root never resolves an untrusted source or
|
||||||
sudo cp -r "$OMARCHY_PATH/default/sddm/omarchy" /usr/share/sddm/themes/omarchy
|
# follows a destination symlink while restoring the packaged SDDM theme.
|
||||||
|
exec "$OMARCHY_PATH/bin/omarchy-plymouth-set" --refresh-sddm-default
|
||||||
Executable
+147
@@ -0,0 +1,147 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Remove the Hermes desktop app along with the Hermes runtime it installed.
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
# -u so an unset HOME is an error rather than a set of rm -rf paths rooted at /.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ensure_hermes_stopped() {
|
||||||
|
python3 - "$HOME" <<'PY'
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
home = Path(sys.argv[1])
|
||||||
|
roots = [str((home / relative).resolve()) for relative in ('.hermes', '.config/Hermes')]
|
||||||
|
roots.append('/opt/hermes-desktop')
|
||||||
|
|
||||||
|
def belongs_to_hermes(target):
|
||||||
|
target = target.removesuffix(' (deleted)')
|
||||||
|
return any(target == root or target.startswith(root + '/') for root in roots)
|
||||||
|
|
||||||
|
holders = []
|
||||||
|
for process in Path('/proc').iterdir():
|
||||||
|
if not process.name.isdigit() or int(process.name) == os.getpid():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if process.stat().st_uid != os.getuid():
|
||||||
|
continue
|
||||||
|
targets = [os.fsdecode(arg) for arg in (process / 'cmdline').read_bytes().split(b'\0')]
|
||||||
|
entries = [process / 'exe', process / 'cwd']
|
||||||
|
try:
|
||||||
|
entries.extend((process / 'fd').iterdir())
|
||||||
|
except PermissionError:
|
||||||
|
pass
|
||||||
|
for entry in entries:
|
||||||
|
try:
|
||||||
|
targets.append(os.readlink(entry))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
if any(belongs_to_hermes(target) for target in targets):
|
||||||
|
holders.append(process.name)
|
||||||
|
except (FileNotFoundError, ProcessLookupError, PermissionError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if holders:
|
||||||
|
print('Close Hermes and processes using its files before removing it (PIDs: '
|
||||||
|
+ ', '.join(holders) + '). Then try again.', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removing an open SQLite WAL leaves a live writer on a deleted generation.
|
||||||
|
ensure_hermes_stopped
|
||||||
|
omarchy-pkg-drop hermes-desktop
|
||||||
|
|
||||||
|
# The installer leaves a unit waiting to hand the app the Omarchy theme.
|
||||||
|
systemctl --user stop omarchy-hermes-theme.service 2>/dev/null || true
|
||||||
|
|
||||||
|
# The mise CLI is the app's predecessor, not the app itself: Hermes Desktop takes
|
||||||
|
# it over on install and runs its own runtime instead, so a copy still here is one
|
||||||
|
# the app never superseded -- an interrupted install, or the terminal CLI from
|
||||||
|
# before the app existed. Remove Hermes clears that too, scoped by the installer
|
||||||
|
# to what Omarchy owns so a hermes the user set up themselves is left alone.
|
||||||
|
# Tolerated here rather than fatal, so the ~/.hermes handling below still runs;
|
||||||
|
# the failure is answered for at the end instead of being swallowed.
|
||||||
|
cli_removed=true
|
||||||
|
ensure_hermes_stopped
|
||||||
|
omarchy-install-hermes-cli --remove || cli_removed=false
|
||||||
|
|
||||||
|
# The app writes this when the runtime it provisions under ~/.hermes has landed,
|
||||||
|
# and it is the only thing that tells that runtime apart from one the user
|
||||||
|
# installed themselves -- the paths are the same either way. Without it the app
|
||||||
|
# never got that far: a machine where it was installed but never launched still
|
||||||
|
# has whatever was there before, and none of it is ours to delete unasked.
|
||||||
|
if [[ -f $HOME/.hermes/hermes-agent/.hermes-bootstrap-complete ]]; then
|
||||||
|
# The checkout and venv, its own uv, its own node. None of it is any use once
|
||||||
|
# the app is gone, so it goes without asking; what the user made with the app
|
||||||
|
# is a different question, answered below.
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.hermes/hermes-agent" \
|
||||||
|
"$HOME/.hermes/bootstrap-cache" \
|
||||||
|
"$HOME/.hermes/bin" \
|
||||||
|
"$HOME/.hermes/node"
|
||||||
|
|
||||||
|
# Only the wrappers pointing into ~/.hermes, matched as a plain string: the
|
||||||
|
# path carries a dot, so an unanchored pattern would also claim a wrapper
|
||||||
|
# pointing at a sibling like ~/xhermes.
|
||||||
|
for command in hermes hermes-agent hermes-acp; do
|
||||||
|
wrapper="$HOME/.local/bin/$command"
|
||||||
|
|
||||||
|
if [[ -f $wrapper && ! -L $wrapper ]] && grep -qF "$HOME/.hermes" "$wrapper"; then
|
||||||
|
rm -f "$wrapper"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# When Hermes brought its own Node it symlinked these next to its own commands,
|
||||||
|
# and they point at what we just deleted. Only the links into ~/.hermes: a
|
||||||
|
# system Node, or someone else's, lives somewhere else entirely.
|
||||||
|
for command in node npm npx; do
|
||||||
|
link="$HOME/.local/bin/$command"
|
||||||
|
|
||||||
|
if [[ -L $link && $(readlink "$link") == "$HOME/.hermes"/* ]]; then
|
||||||
|
rm -f "$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# What survives to here is the user's: the chats, memories and skills in
|
||||||
|
# ~/.hermes, the connections and their encrypted tokens in ~/.config/Hermes.
|
||||||
|
# Keeping them stays the default -- they are small, and finding them intact
|
||||||
|
# after a reinstall is the better surprise -- but a removal meant to be
|
||||||
|
# complete should not leave credentials behind either, so the choice is put in
|
||||||
|
# front of the user with the size, default no. Asked whenever the directories
|
||||||
|
# exist, marker or no marker: on a machine where the marker never appeared the
|
||||||
|
# data came from the terminal CLI or an install the app never finished, and it
|
||||||
|
# is still what removal is asked to clean up. Naming the paths keeps the
|
||||||
|
# question honest there too -- ~/.hermes may still carry a runtime the app
|
||||||
|
# never owned, a yes takes that with it, and saying so is the prompt's job.
|
||||||
|
# Without a terminal to ask in, keeping everything is the answer.
|
||||||
|
data_removed=false
|
||||||
|
if [[ -d $HOME/.hermes || -d $HOME/.config/Hermes ]] && [[ -t 0 ]] && omarchy-cmd-present gum; then
|
||||||
|
# du answers non-zero when either directory is missing, and pipefail would
|
||||||
|
# turn that into an aborted removal; the size is worth no such thing.
|
||||||
|
size=$(du -shc "$HOME/.hermes" "$HOME/.config/Hermes" 2>/dev/null | tail -1 | cut -f1 || true)
|
||||||
|
if gum confirm --default=false "Also delete ~/.hermes and ~/.config/Hermes ($size: chats, memories, skills, connections and tokens)?"; then
|
||||||
|
ensure_hermes_stopped
|
||||||
|
rm -rf "$HOME/.hermes" "$HOME/.config/Hermes"
|
||||||
|
data_removed=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Hermes Desktop has been removed."
|
||||||
|
if [[ $data_removed == true ]]; then
|
||||||
|
echo "Its chats, memories, and settings in ~/.hermes and ~/.config/Hermes are gone too."
|
||||||
|
elif [[ -d $HOME/.hermes || -d $HOME/.config/Hermes ]]; then
|
||||||
|
echo "Your chats, memories, and skills are still in ~/.hermes,"
|
||||||
|
echo "and your connections and settings in ~/.config/Hermes."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The messages above still hold -- the app and its runtime are gone -- but a CLI
|
||||||
|
# teardown that failed already said so on stderr, and that stands.
|
||||||
|
if [[ $cli_removed == "false" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Executable
+78
@@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Remove the OpenClaw agent platform along with its gateway service and web app.
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
# -u so an unset HOME is an error rather than a set of rm -rf paths rooted at /.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
unit_dir="$HOME/.config/systemd/user"
|
||||||
|
|
||||||
|
# Only systemd's own word counts as "stopped": a non-zero exit from is-active
|
||||||
|
# also covers an unreachable user manager, which says nothing about whether
|
||||||
|
# the process is alive.
|
||||||
|
unit_stopped() {
|
||||||
|
local state
|
||||||
|
state=$(systemctl --user is-active "$1" 2>/dev/null) || true
|
||||||
|
[[ $state == inactive || $state == failed ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# The user services OpenClaw installs for itself: the gateway through
|
||||||
|
# onboarding, and the node host if the user ever paired this machine to
|
||||||
|
# another gateway. OpenClaw writes them to $HOME/.config regardless of
|
||||||
|
# XDG_CONFIG_HOME, so look exactly there; no unit means nothing registered.
|
||||||
|
# Upstream's own teardown knows every piece its install wrote (unit file, the
|
||||||
|
# default.target.wants enablement symlink, the reload), so prefer it while the
|
||||||
|
# binary is still installed and fall back to doing the same by hand. A service
|
||||||
|
# that will not stop aborts the removal: dropping the package would strand the
|
||||||
|
# live process on deleted code with no way to restart it cleanly.
|
||||||
|
for unit_file in "$unit_dir"/openclaw-gateway.service "$unit_dir"/openclaw-node.service; do
|
||||||
|
[[ -f $unit_file ]] || continue
|
||||||
|
unit=${unit_file##*/}
|
||||||
|
role=${unit#openclaw-}
|
||||||
|
role=${role%.service}
|
||||||
|
if openclaw "$role" uninstall >/dev/null 2>&1 ||
|
||||||
|
systemctl --user disable --now "$unit" 2>/dev/null ||
|
||||||
|
unit_stopped "$unit"; then
|
||||||
|
# .bak is what `gateway install --force` leaves behind when it rewrites a
|
||||||
|
# unit, so it goes with the unit.
|
||||||
|
rm -f "$unit_file" "$unit_file.bak" "$unit_dir/default.target.wants/$unit"
|
||||||
|
systemctl --user daemon-reload 2>/dev/null || true
|
||||||
|
# A unit that had been failing stays listed as "not-found failed" after
|
||||||
|
# its file is gone until its failed state is reset.
|
||||||
|
systemctl --user reset-failed "$unit" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo "Could not stop $unit; OpenClaw was not removed." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
omarchy-pkg-drop openclaw
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" &>/dev/null || true
|
||||||
|
|
||||||
|
# ~/.openclaw stays unless asked: the chats, memories, and credentials in
|
||||||
|
# there are the user's agent, and finding them intact after a reinstall is the
|
||||||
|
# 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.
|
||||||
|
state_removed=false
|
||||||
|
if [[ -d $HOME/.openclaw && -t 0 ]] && omarchy-cmd-present gum; 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"
|
||||||
|
state_removed=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "OpenClaw has been removed."
|
||||||
|
if [[ $state_removed == true ]]; then
|
||||||
|
echo "Its chats, memories, and settings in ~/.openclaw are gone too."
|
||||||
|
elif [[ -d $HOME/.openclaw ]]; then
|
||||||
|
echo "Your agent's chats, memories, and settings are still in ~/.openclaw."
|
||||||
|
fi
|
||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Remove the Perplexity desktop app along with its runtime caches.
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
# -u so an unset HOME is an error rather than a set of rm -rf paths rooted at /.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
omarchy-pkg-drop perplexity
|
||||||
|
|
||||||
|
# The runtime the app downloads for itself: llama.cpp builds and local models
|
||||||
|
# under ~/.local/share, the older download location under ~/.cache. Of the
|
||||||
|
# perplexity-* dirs these are the only ones the desktop app owns; the rest
|
||||||
|
# belong to Perplexity products that outlive it.
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.cache/Perplexity" \
|
||||||
|
"$HOME/.cache/perplexity-rpc-server" \
|
||||||
|
"$HOME/.local/share/perplexity-rpc-server"
|
||||||
|
|
||||||
|
# What is left is the user's: the logins and session in ~/.config/Perplexity,
|
||||||
|
# the secret vault and device identity in ~/.local/state/perplexity, and the
|
||||||
|
# launcher flags. Keeping them stays the default -- they are small, and being
|
||||||
|
# signed in after a reinstall is the better surprise -- but a removal meant to
|
||||||
|
# be complete should not leave credentials behind either, so the choice is put
|
||||||
|
# in front of the user, default no. Without a terminal to ask in, keeping it
|
||||||
|
# is the answer -- and gum draws the prompt on stderr, so a redirected stderr
|
||||||
|
# would block on a question nobody can see.
|
||||||
|
data_removed=false
|
||||||
|
if [[ -t 0 && -t 2 ]]; then
|
||||||
|
# du answers non-zero when a directory is missing, and pipefail would turn
|
||||||
|
# that into an aborted removal; the size is worth no such thing.
|
||||||
|
size=$(du -shc "$HOME/.config/Perplexity" "$HOME/.local/state/perplexity" 2>/dev/null | tail -1 | cut -f1 || true)
|
||||||
|
if gum confirm --default=false "Also delete your Perplexity data ($size: logins, settings, secret vault and device identity)?"; then
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.config/Perplexity" \
|
||||||
|
"$HOME/.local/state/perplexity"
|
||||||
|
rm -f "$HOME/.config/perplexity-flags.conf"
|
||||||
|
data_removed=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Perplexity has been removed."
|
||||||
|
if [[ $data_removed == "true" ]]; then
|
||||||
|
echo "Its logins, settings, secret vault, and device identity are gone too."
|
||||||
|
else
|
||||||
|
echo "Nothing of yours was touched: not the logins in ~/.config/Perplexity,"
|
||||||
|
echo "nor the secret vault and device identity in ~/.local/state/perplexity."
|
||||||
|
fi
|
||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Remove T3 Code along with its configuration and workspaces.
|
||||||
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
omarchy-pkg-drop t3code-bin
|
||||||
|
|
||||||
|
# T3 Code drives agents that keep their own state -- ~/.claude.json, ~/.grok,
|
||||||
|
# ~/.local/share/opencode -- and those outlive it, so only its own goes.
|
||||||
|
rm -rf \
|
||||||
|
"$HOME/.config/t3code" \
|
||||||
|
"$HOME/.t3"
|
||||||
|
rm -f "$HOME/.config/t3code-flags.conf"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "T3 Code has been removed."
|
||||||
@@ -17,6 +17,27 @@ if gum confirm "Are you sure you want to remove all preinstalled web apps, TUI w
|
|||||||
~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \
|
~/.local/bin/gh ~/.local/bin/opencode ~/.local/bin/playwright ~/.local/bin/playwright-cli ~/.local/bin/pi \
|
||||||
~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
|
~/.local/bin/omp ~/.local/bin/grok ~/.local/bin/crush ~/.local/bin/ghui ~/.local/bin/hunk
|
||||||
|
|
||||||
|
# Cursor's own installer links ~/.local/bin/cursor-agent as well, so only
|
||||||
|
# the mise wrapper omarchy-mise-install wrote is a preinstall.
|
||||||
|
if [[ -f ~/.local/bin/cursor-agent && ! -L ~/.local/bin/cursor-agent ]] &&
|
||||||
|
grep -Eq '^mise use -g .*"cursor-agent"' ~/.local/bin/cursor-agent; then
|
||||||
|
rm -f ~/.local/bin/cursor-agent
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Preserve a user-managed Muse launcher at the same path.
|
||||||
|
if [[ -f ~/.local/bin/muse && ! -L ~/.local/bin/muse ]] &&
|
||||||
|
grep -Eq '^mise use -g .*"http:muse\[' ~/.local/bin/muse; then
|
||||||
|
rm -f ~/.local/bin/muse
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only the wrapper omarchy-install-hermes-cli wrote is a preinstall. Hermes
|
||||||
|
# Desktop's command, an official install, or anything else at that path is
|
||||||
|
# the user's, so it is the installer that decides whether the wrapper is its
|
||||||
|
# own, rather than a copy of its marker kept here.
|
||||||
|
if omarchy-install-hermes-cli --owns; then
|
||||||
|
rm -f ~/.local/bin/hermes
|
||||||
|
fi
|
||||||
|
|
||||||
omarchy-pkg-drop \
|
omarchy-pkg-drop \
|
||||||
aether \
|
aether \
|
||||||
cliamp \
|
cliamp \
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ setup_pam_config() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "Creating polkit configuration with fingerprint authentication..."
|
echo "Creating polkit configuration with fingerprint authentication..."
|
||||||
|
# omarchy:heredoc-expands paths=none -- $fprintd_gate is the literal PAM
|
||||||
|
# line defined above, shared with the two sed insertions so the gate cannot
|
||||||
|
# drift between files. The only path in it is the fixed /usr/bin one.
|
||||||
sudo tee /etc/pam.d/polkit-1 >/dev/null <<EOF
|
sudo tee /etc/pam.d/polkit-1 >/dev/null <<EOF
|
||||||
$fprintd_gate
|
$fprintd_gate
|
||||||
auth sufficient pam_fprintd.so
|
auth sufficient pam_fprintd.so
|
||||||
@@ -71,18 +74,15 @@ if ! omarchy-hw-fingerprint; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install required packages
|
# libfprint-git tracks upstream ahead of the Arch release, so a new reader only
|
||||||
echo "Installing required packages..."
|
# needs a pin bump in omarchy-pkgs. It conflicts with stock libfprint, and
|
||||||
|
# --noconfirm answers that prompt with N; --ask 4 accepts the replacement in
|
||||||
# libfprint-git provides+conflicts libfprint; pacman -S --noconfirm
|
# one transaction, so a failed install leaves the existing driver in place.
|
||||||
# defaults the conflict prompt to N and aborts. Pre-remove it (deps-only,
|
if omarchy-pkg-missing libfprint-git fprintd usbutils; then
|
||||||
# so an installed fprintd stays put) so stock libfprint installs cleanly.
|
echo "Installing required packages..."
|
||||||
if pacman -Q libfprint-git &>/dev/null; then
|
sudo pacman -S --needed --noconfirm --ask 4 libfprint-git fprintd usbutils
|
||||||
sudo pacman -Rdd --noconfirm libfprint-git
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
omarchy-pkg-add libfprint fprintd usbutils
|
|
||||||
|
|
||||||
# Enroll first fingerprint
|
# Enroll first fingerprint
|
||||||
echo -e "\e[32m\nLet's setup your right index finger as the first fingerprint.\e[0m"
|
echo -e "\e[32m\nLet's setup your right index finger as the first fingerprint.\e[0m"
|
||||||
echo -e "Keep moving the finger around on sensor until the process completes.\n"
|
echo -e "Keep moving the finger around on sensor until the process completes.\n"
|
||||||
|
|||||||
@@ -107,6 +107,50 @@ authorize_pasted_key() {
|
|||||||
authorize_key "$key" || exit 1
|
authorize_key "$key" || exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Only called after a key is authorized. Disabling password authentication
|
||||||
|
# before then could lock the owner out of the machine.
|
||||||
|
disable_password_auth() {
|
||||||
|
local config=/etc/ssh/sshd_config.d/10-omarchy-hardening.conf
|
||||||
|
local effective_config
|
||||||
|
|
||||||
|
if [[ ! -s $AUTHORIZED_KEYS ]]; then
|
||||||
|
echo -e "\e[31mCannot disable SSH password authentication without an authorized key.\e[0m" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Disabling SSH password authentication, now that a key is authorized..."
|
||||||
|
sudo install -Dm644 /dev/stdin "$config" <<'CONF'
|
||||||
|
# Written by omarchy-setup-security-sshd once an SSH key was authorized.
|
||||||
|
# Delete this file and reload sshd to allow password logins again.
|
||||||
|
PasswordAuthentication no
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
CONF
|
||||||
|
|
||||||
|
# Validate before reloading: a config sshd rejects would otherwise take the
|
||||||
|
# service down on its next restart, potentially stranding a remote owner.
|
||||||
|
if ! sudo sshd -t; then
|
||||||
|
echo -e "\e[31msshd rejected the hardening config; removing it and leaving passwords on.\e[0m" >&2
|
||||||
|
sudo rm -f "$config"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Syntax alone is insufficient because sshd uses the first value it reads for
|
||||||
|
# these settings. An earlier administrator rule could leave passwords enabled.
|
||||||
|
# Match keywords case-insensitively: OpenSSH 9.x dumps them lowercase, 10.x
|
||||||
|
# in CamelCase.
|
||||||
|
if ! effective_config=$(sudo sshd -T) ||
|
||||||
|
! grep -qixF "passwordauthentication no" <<<"$effective_config" ||
|
||||||
|
! grep -qixF "kbdinteractiveauthentication no" <<<"$effective_config"; then
|
||||||
|
echo -e "\e[31msshd did not apply the password-authentication restrictions; removing the ineffective config.\e[0m" >&2
|
||||||
|
sudo rm -f "$config"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reload rather than restart so an administrator already connected keeps
|
||||||
|
# their session.
|
||||||
|
sudo systemctl reload sshd.service
|
||||||
|
}
|
||||||
|
|
||||||
echo -e "\e[32mSetting up SSH server access with key-based authentication.\n\e[0m"
|
echo -e "\e[32mSetting up SSH server access with key-based authentication.\n\e[0m"
|
||||||
|
|
||||||
setup_sshd
|
setup_sshd
|
||||||
@@ -123,5 +167,8 @@ else
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
disable_password_auth
|
||||||
|
|
||||||
echo -e "\e[32m\nPerfect! The SSH server is running and your key is authorized.\e[0m"
|
echo -e "\e[32m\nPerfect! The SSH server is running and your key is authorized.\e[0m"
|
||||||
|
echo "Password logins are off; this machine now accepts authorized keys only."
|
||||||
echo "You can now connect with: ssh $USER@$(hostname)"
|
echo "You can now connect with: ssh $USER@$(hostname)"
|
||||||
@@ -13,6 +13,19 @@ if [[ $1 && ! $1 =~ ^[0-9]+$ ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
arm_expiry() {
|
||||||
|
if sudo systemd-run --on-active=${MINUTES}m --timer-property=AccuracySec=1s --unit="$TIMER_NAME" \
|
||||||
|
rm -f -- "$NOPASSWD_FILE"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Failed to schedule passwordless sudo expiry. Revoking access now." >&2
|
||||||
|
if ! sudo rm -f -- "$NOPASSWD_FILE"; then
|
||||||
|
echo "CRITICAL: Could not remove $NOPASSWD_FILE. Remove it as root immediately." >&2
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
echo "Toggle passwordless sudo..."
|
echo "Toggle passwordless sudo..."
|
||||||
|
|
||||||
# Safety: if the file exists but the timer doesn't (e.g. after reboot), clean up
|
# Safety: if the file exists but the timer doesn't (e.g. after reboot), clean up
|
||||||
@@ -24,8 +37,7 @@ fi
|
|||||||
if sudo test -f "$NOPASSWD_FILE"; then
|
if sudo test -f "$NOPASSWD_FILE"; then
|
||||||
if [[ $1 ]]; then
|
if [[ $1 ]]; then
|
||||||
sudo systemctl stop "${TIMER_NAME}.timer" 2>/dev/null
|
sudo systemctl stop "${TIMER_NAME}.timer" 2>/dev/null
|
||||||
sudo systemd-run --on-active=${MINUTES}m --timer-property=AccuracySec=1s --unit="$TIMER_NAME" \
|
arm_expiry || exit 1
|
||||||
rm "$NOPASSWD_FILE"
|
|
||||||
echo "Passwordless sudo timer updated. It will now automatically disable in ${MINUTES} minutes."
|
echo "Passwordless sudo timer updated. It will now automatically disable in ${MINUTES} minutes."
|
||||||
else
|
else
|
||||||
sudo rm "$NOPASSWD_FILE"
|
sudo rm "$NOPASSWD_FILE"
|
||||||
@@ -48,12 +60,11 @@ else
|
|||||||
if gum confirm "Enable passwordless sudo for ${MINUTES} minutes? This is a significant security risk!"; then
|
if gum confirm "Enable passwordless sudo for ${MINUTES} minutes? This is a significant security risk!"; then
|
||||||
echo "${USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee "$NOPASSWD_FILE" > /dev/null
|
echo "${USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee "$NOPASSWD_FILE" > /dev/null
|
||||||
sudo chmod 440 "$NOPASSWD_FILE"
|
sudo chmod 440 "$NOPASSWD_FILE"
|
||||||
sudo systemd-run --on-active=${MINUTES}m --timer-property=AccuracySec=1s --unit="$TIMER_NAME" \
|
arm_expiry || exit 1
|
||||||
rm "$NOPASSWD_FILE"
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Passwordless sudo has been ENABLED. It will automatically disable in ${MINUTES} minutes."
|
echo "Passwordless sudo has been ENABLED. It will automatically disable in ${MINUTES} minutes."
|
||||||
echo "Note: if you restart before then, run omarchy-sudo-passwordless again to disable it."
|
echo "A restart removes the passwordless sudo rule as well."
|
||||||
else
|
else
|
||||||
echo "Aborted. No changes made."
|
echo "Aborted. No changes made."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -23,16 +23,26 @@ omarchy-git-url-check "$REPO_URL" || exit 1
|
|||||||
|
|
||||||
THEMES_DIR="$HOME/.config/omarchy/themes"
|
THEMES_DIR="$HOME/.config/omarchy/themes"
|
||||||
|
|
||||||
# Strip user@host: prefix from scp-style SSH URLs so basename sees just the path
|
# Strip user@host: prefix from scp-style SSH URLs so basename sees just the path.
|
||||||
|
# git reads a URL as scp-style when a colon appears before any slash, so the path
|
||||||
|
# after it need not hold one: `git@host:omarchy-blue-theme.git` is a repo in that
|
||||||
|
# user's home, and leaving its prefix on names the theme after the whole URL.
|
||||||
REPO_PATH="$REPO_URL"
|
REPO_PATH="$REPO_URL"
|
||||||
[[ $REPO_PATH != *"://"* && $REPO_PATH == *:*/* ]] && REPO_PATH="${REPO_PATH#*:}"
|
[[ $REPO_PATH != *"://"* && $REPO_PATH == *:* && ${REPO_PATH%%:*} != */* ]] && REPO_PATH="${REPO_PATH#*:}"
|
||||||
THEME_NAME=$(basename -- "$REPO_PATH" .git | sed -E 's/^omarchy-//; s/-theme$//' | tr '[:upper:]' '[:lower:]')
|
THEME_NAME=$(basename -- "$REPO_PATH" .git | sed -E 's/^omarchy-//; s/-theme$//' | tr '[:upper:]' '[:lower:]')
|
||||||
THEME_PATH="$THEMES_DIR/$THEME_NAME"
|
THEME_PATH="$THEMES_DIR/$THEME_NAME"
|
||||||
|
|
||||||
# The name comes from the URL and is joined into a path that is about to be
|
# The name comes from the URL, is joined into a path that is about to be
|
||||||
# removed, so a repo called `..` would take ~/.config/omarchy with it. A leading
|
# removed, and then names a directory the rest of Omarchy passes around by
|
||||||
# dot is refused with it: `host:-s/foo.git` leaves basename with `.git`.
|
# name: Style > Unlock builds a command line out of the one the picker
|
||||||
if [[ -z $THEME_NAME || $THEME_NAME == .* || $THEME_NAME == */* ]]; then
|
# returned. So it is held to the characters a theme name needs rather than
|
||||||
|
# screened for the harm of the day -- a repo called `..` would take
|
||||||
|
# ~/.config/omarchy with it, and one called `a';'id` would carry its own
|
||||||
|
# command into that picker. The leading character is kept out of `.` and `-`,
|
||||||
|
# which also covers `host:-s/foo.git` leaving basename with `.git`.
|
||||||
|
# A bracket range follows the locale's collation, not ASCII: `[a-z]` takes in
|
||||||
|
# `é` under en_US.UTF-8. Pin the locale so the set is the one written here.
|
||||||
|
if ! (LC_ALL=C; [[ $THEME_NAME =~ ^[a-z0-9_][a-z0-9._+-]*$ ]]); then
|
||||||
echo "Error: '$REPO_URL' does not give a usable theme name."
|
echo "Error: '$REPO_URL' does not give a usable theme name."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -326,6 +326,8 @@ post_theme_commands=(
|
|||||||
omarchy-theme-set-gnome
|
omarchy-theme-set-gnome
|
||||||
omarchy-theme-set-pi
|
omarchy-theme-set-pi
|
||||||
omarchy-theme-set-claude
|
omarchy-theme-set-claude
|
||||||
|
omarchy-theme-set-hermes
|
||||||
|
omarchy-theme-set-t3code
|
||||||
omarchy-theme-set-browser
|
omarchy-theme-set-browser
|
||||||
omarchy-theme-set-vscode
|
omarchy-theme-set-vscode
|
||||||
omarchy-theme-set-obsidian
|
omarchy-theme-set-obsidian
|
||||||
|
|||||||
@@ -3,23 +3,15 @@
|
|||||||
# omarchy:summary=Apply the current theme color to Chromium, Chrome, Edge, and Brave
|
# omarchy:summary=Apply the current theme color to Chromium, Chrome, Edge, and Brave
|
||||||
# omarchy:hidden=true
|
# omarchy:hidden=true
|
||||||
|
|
||||||
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
|
||||||
CHROMIUM_THEME=$HOME/.local/state/omarchy/current/theme/chromium.theme
|
CHROMIUM_THEME=$HOME/.local/state/omarchy/current/theme/chromium.theme
|
||||||
|
THEME_HEX_COLOR=$BROWSER_POLICY_DEFAULT_COLOR
|
||||||
|
|
||||||
if [[ -f $CHROMIUM_THEME ]]; then
|
if [[ -f $CHROMIUM_THEME ]]; then
|
||||||
THEME_RGB_COLOR=$(<$CHROMIUM_THEME)
|
THEME_HEX_COLOR=$(browser_policy_theme_hex "$(<$CHROMIUM_THEME)")
|
||||||
THEME_HEX_COLOR=$(printf '#%02x%02x%02x' ${THEME_RGB_COLOR//,/ })
|
|
||||||
else
|
|
||||||
# Use a default, neutral grey if theme doesn't have a color
|
|
||||||
THEME_HEX_COLOR="#1c2027"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set_browser_policy() {
|
|
||||||
local policy_dir="$1"
|
|
||||||
|
|
||||||
[[ -d $policy_dir ]] || return
|
|
||||||
echo "{\"BrowserThemeColor\": \"$THEME_HEX_COLOR\", \"BrowserColorScheme\": \"device\"}" | tee "$policy_dir/color.json" >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_running_browser() {
|
refresh_running_browser() {
|
||||||
local process="$1"
|
local process="$1"
|
||||||
local command="$2"
|
local command="$2"
|
||||||
@@ -30,17 +22,15 @@ refresh_running_browser() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
set_browser_policy /etc/chromium/policies/managed
|
failed=0
|
||||||
|
omarchy-theme-set-browser-policy "${THEME_HEX_COLOR#\#}" || failed=1
|
||||||
|
|
||||||
refresh_running_browser chromium chromium
|
refresh_running_browser chromium chromium
|
||||||
|
|
||||||
set_browser_policy /etc/opt/chrome/policies/managed
|
|
||||||
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
refresh_running_browser chrome google-chrome-stable || refresh_running_browser chrome google-chrome
|
||||||
|
|
||||||
set_browser_policy /etc/opt/edge/policies/managed
|
|
||||||
refresh_running_browser msedge microsoft-edge-stable
|
refresh_running_browser msedge microsoft-edge-stable
|
||||||
|
|
||||||
set_browser_policy /etc/brave/policies/managed
|
|
||||||
refresh_running_browser brave brave
|
refresh_running_browser brave brave
|
||||||
# Match on the binary path: the running process is named plain "brave", and a
|
# Match on the binary path: the running process is named plain "brave", and a
|
||||||
# bare -f brave-origin pattern would also match the installer's own terminal.
|
# bare -f brave-origin pattern would also match the installer's own terminal.
|
||||||
refresh_running_browser /opt/brave-origin-bin/ brave-origin -f
|
refresh_running_browser /opt/brave-origin-bin/ brave-origin -f
|
||||||
|
|
||||||
|
exit "$failed"
|
||||||
Executable
+123
@@ -0,0 +1,123 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Write the current theme color into the browser policy directories
|
||||||
|
# omarchy:args=<rrggbb>
|
||||||
|
# omarchy:hidden=true
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Whenever this runs as root — invoked directly through the passwordless
|
||||||
|
# sudoers rule, or re-execed by require_root below — sudo's secure_path decides
|
||||||
|
# where a bare helper resolves, and a dev link (etc/sudoers.d/omarchy-dev-path)
|
||||||
|
# prepends a user-writable checkout bin/ to it. Every helper this script calls
|
||||||
|
# by bare name (printf's builtin aside: install, mktemp, rm) is a system tool,
|
||||||
|
# never an omarchy-* command, so pin PATH to trusted system directories and keep
|
||||||
|
# root from resolving one out of that checkout. The unprivileged wrapper phase
|
||||||
|
# keeps the caller's PATH so it can still find sudo/pkexec.
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enterprise policy trust roots. The list is fixed here rather than taken from
|
||||||
|
# the caller: the caller chooses a color, never a path.
|
||||||
|
POLICY_DIRS=(
|
||||||
|
/etc/chromium/policies/managed
|
||||||
|
/etc/opt/chrome/policies/managed
|
||||||
|
/etc/opt/edge/policies/managed
|
||||||
|
/etc/brave/policies/managed
|
||||||
|
)
|
||||||
|
|
||||||
|
# The path etc/sudoers.d/omarchy-theme-browser names. The privileged half always
|
||||||
|
# runs from there rather than from whichever copy was invoked, so the rule
|
||||||
|
# matches even where $OMARCHY_PATH points at a checkout.
|
||||||
|
PACKAGED_PATH=/usr/bin/omarchy-theme-set-browser-policy
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: omarchy-theme-set-browser-policy <rrggbb>" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# != 1 )); then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
color="$1"
|
||||||
|
|
||||||
|
# Six lowercase hex digits is the whole of what this accepts. The leading "#"
|
||||||
|
# is added when the JSON is written rather than passed in: "#" opens a comment
|
||||||
|
# in sudoers, and keeping it out of argv lets the sudoers rule spell the
|
||||||
|
# argument as a plain six-character glob.
|
||||||
|
if [[ ! $color =~ ^[0-9a-f]{6}$ ]]; then
|
||||||
|
echo "omarchy-theme-set-browser-policy: expected six lowercase hex digits, got '$color'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# True when sudo would run this exact command without stopping for a password.
|
||||||
|
# `sudo -l` on its own reports whether a command is permitted, which the blanket
|
||||||
|
# %wheel rule answers yes to for everything; the long listing prints the matched
|
||||||
|
# entry's tags, so !authenticate is the grant in
|
||||||
|
# etc/sudoers.d/omarchy-theme-browser and nothing else. Listing runs nothing
|
||||||
|
# and, under -n, prompts for nothing.
|
||||||
|
sudo_grants_passwordless() {
|
||||||
|
sudo -n -l -l "$PACKAGED_PATH" "$@" 2>/dev/null | grep -q '!authenticate'
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
return
|
||||||
|
elif [[ -t 0 ]] || sudo_grants_passwordless "$@"; then
|
||||||
|
exec sudo "$PACKAGED_PATH" "$@"
|
||||||
|
else
|
||||||
|
exec pkexec "$PACKAGED_PATH" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root "$color"
|
||||||
|
|
||||||
|
failed=0
|
||||||
|
staged=""
|
||||||
|
# Bash 5.3 makes the EXIT trap's last command decide the script's exit status,
|
||||||
|
# so this handler must not end on a false test. Every successful run clears
|
||||||
|
# staged, and a trailing `[[ -n $staged ]] && ...` would report that as failure.
|
||||||
|
cleanup() {
|
||||||
|
if [[ -n $staged ]]; then
|
||||||
|
rm -f "$staged"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
for policy_dir in "${POLICY_DIRS[@]}"; do
|
||||||
|
# Only browsers Omarchy has installed have a policy directory. Creating one
|
||||||
|
# here would hand a browser a managed-policy root it does not otherwise have.
|
||||||
|
[[ -d $policy_dir && ! -L $policy_dir ]] || continue
|
||||||
|
|
||||||
|
dest=$policy_dir/color.json
|
||||||
|
staged=$(mktemp) || {
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
printf '{"BrowserThemeColor": "#%s", "BrowserColorScheme": "device"}\n' "$color" >"$staged"
|
||||||
|
|
||||||
|
if [[ -L $dest || -d $dest ]]; then
|
||||||
|
if ! rm -rf -- "$dest"; then
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
echo "omarchy-theme-set-browser-policy: cannot replace $dest" >&2
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! install -m 0644 -o root -g root -T "$staged" "$dest"; then
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
echo "omarchy-theme-set-browser-policy: cannot write $dest" >&2
|
||||||
|
failed=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$staged"
|
||||||
|
staged=""
|
||||||
|
done
|
||||||
|
|
||||||
|
exit "$failed"
|
||||||
Executable
+233
@@ -0,0 +1,233 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Sync the generated Omarchy theme to Hermes as a skin
|
||||||
|
# omarchy:args=[--activate] [--wait]
|
||||||
|
# omarchy:hidden=true
|
||||||
|
|
||||||
|
# A skin is Hermes' one theme unit for the desktop app, the TUI and the CLI;
|
||||||
|
# its gateway watches the active skin file and repaints every surface on change.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERMES_SOURCE_PATH="$HOME/.local/state/omarchy/current/theme/hermes.yaml"
|
||||||
|
HERMES_THEME_NAME_PATH="$HOME/.local/state/omarchy/current/theme.name"
|
||||||
|
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
|
||||||
|
HERMES_CONFIG_PATH="$HERMES_HOME/config.yaml"
|
||||||
|
HERMES_SKIN_NAME="omarchy"
|
||||||
|
# The command the readiness probe vets, rather than whichever hermes is on PATH.
|
||||||
|
HERMES_COMMAND="$HOME/.local/bin/hermes"
|
||||||
|
# Written by the desktop app once the runtime its first launch provisions is in.
|
||||||
|
HERMES_BOOTSTRAP_MARKER="$HERMES_HOME/hermes-agent/.hermes-bootstrap-complete"
|
||||||
|
HERMES_ACTIVATE=0
|
||||||
|
HERMES_WAIT=0
|
||||||
|
HERMES_WAIT_LIMIT=$((30 * 60))
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: omarchy-theme-set-hermes [--activate] [--wait]"
|
||||||
|
}
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--activate)
|
||||||
|
HERMES_ACTIVATE=1
|
||||||
|
;;
|
||||||
|
--wait)
|
||||||
|
HERMES_ACTIVATE=1
|
||||||
|
HERMES_WAIT=1
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# A theme switch runs this beside a dozen other hooks; only --activate explains.
|
||||||
|
note() {
|
||||||
|
if (( HERMES_ACTIVATE == 1 )); then
|
||||||
|
echo "$*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A theme applied before the template existed has no skin rendered yet.
|
||||||
|
if [[ ! -f $HERMES_SOURCE_PATH ]]; then
|
||||||
|
(( HERMES_ACTIVATE == 1 )) || exit 0
|
||||||
|
|
||||||
|
if [[ ! -s $HERMES_THEME_NAME_PATH ]]; then
|
||||||
|
echo "Hermes skin source missing: $HERMES_SOURCE_PATH" >&2
|
||||||
|
echo "Select an Omarchy theme first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
omarchy-theme-refresh
|
||||||
|
|
||||||
|
if [[ ! -f $HERMES_SOURCE_PATH ]]; then
|
||||||
|
echo "Hermes skin source missing after refreshing the theme: $HERMES_SOURCE_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The first launch takes minutes and may be abandoned; the readiness probe
|
||||||
|
# would start Hermes to answer, so poll for the marker instead.
|
||||||
|
if (( HERMES_WAIT == 1 )); then
|
||||||
|
waited=0
|
||||||
|
until [[ -f $HERMES_BOOTSTRAP_MARKER && -f $HERMES_CONFIG_PATH ]]; do
|
||||||
|
if (( waited >= HERMES_WAIT_LIMIT )); then
|
||||||
|
echo "Hermes did not finish setting up within $((HERMES_WAIT_LIMIT / 60)) minutes; run omarchy-theme-set-hermes --activate once it has." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 10
|
||||||
|
waited=$((waited + 10))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Provisioning creates ~/.hermes on every machine for the Omarchy skill; the
|
||||||
|
# config is what Hermes writes once it has actually run.
|
||||||
|
if [[ ! -f $HERMES_CONFIG_PATH ]]; then
|
||||||
|
note "Hermes is not set up yet; launch it once, then run omarchy-theme-set-hermes --activate."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hermes parses the skin as YAML and hands its strings to every surface, so only
|
||||||
|
# the name, a plain description and #rrggbb colours may reach it, in the order
|
||||||
|
# YAML needs them. YAML breaks lines on bytes grep does not, so the bytes are
|
||||||
|
# counted first, NUL included.
|
||||||
|
skin_is_well_formed() {
|
||||||
|
local skin="$1"
|
||||||
|
|
||||||
|
[[ -f $skin ]] &&
|
||||||
|
(( $(LC_ALL=C tr -d ' -~\n' <"$skin" | wc -c) == 0 )) &&
|
||||||
|
awk -v name="name: $HERMES_SKIN_NAME" '
|
||||||
|
bad { next }
|
||||||
|
/^#/ || /^[[:space:]]*$/ { next }
|
||||||
|
!seen_name { if ($0 == name) seen_name = 1; else bad = 1; next }
|
||||||
|
!seen_colors {
|
||||||
|
if ($0 == "colors:") seen_colors = 1
|
||||||
|
else if (!seen_description && $0 ~ /^description: [A-Za-z0-9 ,.()-]{0,200}$/) seen_description = 1
|
||||||
|
else bad = 1
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^ [a-z_]{1,64}: "#[0-9a-fA-F]{6}"$/ { colors++; next }
|
||||||
|
{ bad = 1 }
|
||||||
|
END { exit (bad || !seen_colors || colors == 0) }
|
||||||
|
' "$skin"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The check has to cover the bytes that get published, and the theme can change
|
||||||
|
# underneath between the two, so a private copy is taken and that is checked.
|
||||||
|
snapshot_dir=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$snapshot_dir"' EXIT
|
||||||
|
HERMES_SNAPSHOT="$snapshot_dir/$HERMES_SKIN_NAME.yaml"
|
||||||
|
|
||||||
|
take_snapshot() {
|
||||||
|
cp "$HERMES_SOURCE_PATH" "$HERMES_SNAPSHOT" 2>/dev/null && skin_is_well_formed "$HERMES_SNAPSHOT"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! take_snapshot; then
|
||||||
|
echo "Skipping Hermes skin: $(basename "$HERMES_SOURCE_PATH") is not a plain color palette." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The gateway reads the file whole on an mtime change, so the write is atomic;
|
||||||
|
# -T so a directory at the skin's path is an error rather than a destination.
|
||||||
|
publish_skin() {
|
||||||
|
local skins_dir="$1"
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
mkdir -p "$skins_dir" 2>/dev/null || return 1
|
||||||
|
tmp=$(mktemp "$skins_dir/$HERMES_SKIN_NAME.yaml.XXXXXX" 2>/dev/null) || return 1
|
||||||
|
if ! cp "$HERMES_SNAPSHOT" "$tmp" 2>/dev/null || ! mv -T "$tmp" "$skins_dir/$HERMES_SKIN_NAME.yaml" 2>/dev/null; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A profile is a Hermes home of its own; existing ones get the skin, none are
|
||||||
|
# made, and one that cannot take it does not cost the others.
|
||||||
|
publish_skin_everywhere() {
|
||||||
|
local profile
|
||||||
|
|
||||||
|
publish_skin "$HERMES_HOME/skins" || {
|
||||||
|
echo "Could not publish the Hermes skin to $HERMES_HOME/skins." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for profile in "$HERMES_HOME"/profiles/*/; do
|
||||||
|
[[ -d $profile ]] || continue
|
||||||
|
publish_skin "${profile%/}/skins" || note "Could not publish the skin to the Hermes profile $(basename "$profile")."
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
publish_skin_everywhere
|
||||||
|
|
||||||
|
# Hermes reads the config of the profile named in active_profile; the profile
|
||||||
|
# exists once its directory does, with or without a config of its own.
|
||||||
|
active_config_path() {
|
||||||
|
local profile
|
||||||
|
|
||||||
|
profile=$(cat "$HERMES_HOME/active_profile" 2>/dev/null || true)
|
||||||
|
profile=${profile,,}
|
||||||
|
|
||||||
|
if [[ -n $profile && $profile != "default" && -d $HERMES_HOME/profiles/$profile ]]; then
|
||||||
|
echo "$HERMES_HOME/profiles/$profile/config.yaml"
|
||||||
|
else
|
||||||
|
echo "$HERMES_CONFIG_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A theme switch finishes the hand-over only for the app Omarchy installed, and
|
||||||
|
# only Hermes' default is ever replaced, so a config plainly naming another skin
|
||||||
|
# ends it here without starting Hermes. Anything less plain is for Hermes to read.
|
||||||
|
if (( HERMES_ACTIVATE == 0 )); then
|
||||||
|
omarchy-pkg-present hermes-desktop || exit 0
|
||||||
|
|
||||||
|
skin_line=$(grep -m1 -x ' skin: .*' "$(active_config_path)" 2>/dev/null || true)
|
||||||
|
case "${skin_line# skin: }" in
|
||||||
|
"" | default | null | true | false | *[!A-Za-z0-9_-]*) ;;
|
||||||
|
*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Omarchy's cold stub installs Hermes when run, so ask the probe before running it.
|
||||||
|
if ! omarchy-install-hermes-cli --check 2>/dev/null; then
|
||||||
|
note "Hermes is not ready, so the Omarchy skin is published but not active."
|
||||||
|
note "Once Hermes runs, activate it with: hermes config set display.skin $HERMES_SKIN_NAME"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only Hermes' own default is replaced, so a skin chosen in Hermes stays; an
|
||||||
|
# answer that did not come is not a default.
|
||||||
|
if ! current_skin=$(timeout 15 "$HERMES_COMMAND" config get display.skin 2>/dev/null); then
|
||||||
|
note "Hermes did not say which skin it is on, so the Omarchy skin is published but not active."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $current_skin && $current_skin != "default" && $current_skin != "$HERMES_SKIN_NAME" ]]; then
|
||||||
|
note "Hermes is set to the '$current_skin' skin; leaving it. Switch with: hermes config set display.skin $HERMES_SKIN_NAME"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hermes' own writer: it updates the active profile's config and touches the
|
||||||
|
# skin file so a running gateway broadcasts the change. A refusal is cosmetic.
|
||||||
|
if ! timeout 30 "$HERMES_COMMAND" config set display.skin "$HERMES_SKIN_NAME" >/dev/null 2>&1; then
|
||||||
|
note "Hermes refused to switch skins, so the Omarchy skin is published but not active."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
note "Hermes is on the Omarchy skin."
|
||||||
|
|
||||||
|
# The desktop applies a skin only from a broadcast, and a config written before
|
||||||
|
# the gateway seeded its watcher goes unannounced; a later write is announced.
|
||||||
|
# The theme may have changed underneath in the meantime, so it is checked again.
|
||||||
|
if (( HERMES_WAIT == 1 )); then
|
||||||
|
sleep 60
|
||||||
|
|
||||||
|
if take_snapshot; then
|
||||||
|
publish_skin_everywhere
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# omarchy:summary=Sync the generated Omarchy theme to T3 Code
|
||||||
|
# omarchy:hidden=true
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
T3CODE_SOURCE_PATH="$HOME/.local/state/omarchy/current/theme/t3code.json"
|
||||||
|
T3CODE_HOME="${T3CODE_HOME:-$HOME/.t3}"
|
||||||
|
T3CODE_STATE_DIR="$T3CODE_HOME/userdata"
|
||||||
|
# The filename is the theme id T3 Code shows and `t3 theme set` accepts, and
|
||||||
|
# it stays stable while the colors change underneath on every theme switch.
|
||||||
|
T3CODE_THEME_PATH="$T3CODE_STATE_DIR/themes/omarchy.json"
|
||||||
|
|
||||||
|
[[ -f $T3CODE_SOURCE_PATH ]] || exit 0
|
||||||
|
|
||||||
|
# A theme without an accent or a background leaves its placeholder unresolved.
|
||||||
|
# Publishing that would only make T3 Code reject the file, so skip instead and
|
||||||
|
# leave the previous palette in place.
|
||||||
|
if grep -q '{{' "$T3CODE_SOURCE_PATH"; then
|
||||||
|
echo "Skipping T3 Code theme: $(basename "$T3CODE_SOURCE_PATH") has unresolved colors." >&2
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only follow an install that already exists. Creating the directory here would
|
||||||
|
# leave a stray T3 Code state dir on machines that do not run it.
|
||||||
|
[[ -d $T3CODE_STATE_DIR ]] || exit 0
|
||||||
|
|
||||||
|
# T3 Code watches this file and retints every connected client on change, so
|
||||||
|
# the write has to be atomic: a half-written file would read as invalid.
|
||||||
|
mkdir -p "$(dirname "$T3CODE_THEME_PATH")"
|
||||||
|
tmp=$(mktemp "$T3CODE_THEME_PATH.XXXXXX")
|
||||||
|
cp "$T3CODE_SOURCE_PATH" "$tmp"
|
||||||
|
mv "$tmp" "$T3CODE_THEME_PATH"
|
||||||
@@ -5,3 +5,9 @@
|
|||||||
# omarchy:examples=omarchy toggle bar | omarchy toggle bar off | omarchy toggle bar on
|
# omarchy:examples=omarchy toggle bar | omarchy toggle bar off | omarchy toggle bar on
|
||||||
|
|
||||||
omarchy-toggle bar-off "${1:-toggle}"
|
omarchy-toggle bar-off "${1:-toggle}"
|
||||||
|
|
||||||
|
# The shell's watch on the toggles directory can miss flag changes that land in
|
||||||
|
# quick succession, stranding the bar off screen until the shell restarts.
|
||||||
|
# Nudge the bar to re-read the flag; quiet best-effort so the toggle still
|
||||||
|
# works when the shell is not up.
|
||||||
|
omarchy-shell -q omarchy.bar syncHidden
|
||||||
@@ -3,6 +3,35 @@
|
|||||||
# omarchy:summary=Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14).
|
# omarchy:summary=Toggle dedicated vs integrated GPU mode via supergfxd (for hybrid gpu laptops, like Asus G14).
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
|
|
||||||
|
install_root_file() {
|
||||||
|
local source="$1"
|
||||||
|
local destination="$2"
|
||||||
|
local mode="$3"
|
||||||
|
local stage
|
||||||
|
|
||||||
|
stage=$(sudo /usr/bin/mktemp -- "${destination%/*}/.${destination##*/}.omarchy.XXXXXX") || return 1
|
||||||
|
safe_stage_path "$stage" "$destination" || return 1
|
||||||
|
|
||||||
|
if sudo /usr/bin/install -m "$mode" -o root -g root -T "$source" "$stage" &&
|
||||||
|
sudo /usr/bin/mv -Tf -- "$stage" "$destination"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
safe_stage_path "$stage" "$destination" && sudo /usr/bin/rm -f -- "$stage"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_stage_path() {
|
||||||
|
local stage="$1"
|
||||||
|
local destination="$2"
|
||||||
|
local prefix suffix
|
||||||
|
|
||||||
|
prefix="${destination%/*}/.${destination##*/}.omarchy."
|
||||||
|
[[ $stage == "$prefix"* ]] || return 1
|
||||||
|
suffix=${stage#"$prefix"}
|
||||||
|
[[ $suffix =~ ^[[:alnum:]]{6}$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
if omarchy-cmd-missing supergfxctl; then
|
if omarchy-cmd-missing supergfxctl; then
|
||||||
omarchy-pkg-add supergfxctl
|
omarchy-pkg-add supergfxctl
|
||||||
|
|
||||||
@@ -54,18 +83,31 @@ case "$gpu_mode" in
|
|||||||
;;
|
;;
|
||||||
"Hybrid")
|
"Hybrid")
|
||||||
if gum confirm "Use only integrated GPU and reboot?"; then
|
if gum confirm "Use only integrated GPU and reboot?"; then
|
||||||
# Switch to integrated mode and ensure vfio is enabled (needed for sleep/wake trick)
|
|
||||||
sudo sed -i "s/\"mode\": \".*\"/\"mode\": \"Integrated\"/" /etc/supergfxd.conf
|
|
||||||
sudo sed -i 's/"vfio_enable": false/"vfio_enable": true/' /etc/supergfxd.conf
|
|
||||||
|
|
||||||
# Force igpu mode after system sleep (or dgpu could get activated)
|
|
||||||
sudo mkdir -p /usr/lib/systemd/system-sleep
|
|
||||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/force-igpu" /usr/lib/systemd/system-sleep/
|
|
||||||
|
|
||||||
# Delay supergfxd startup to avoid race condition with display manager
|
# Delay supergfxd startup to avoid race condition with display manager
|
||||||
# that can cause system freeze when booting in Integrated mode
|
# that can cause system freeze when booting in Integrated mode
|
||||||
sudo mkdir -p /etc/systemd/system/supergfxd.service.d
|
sudo mkdir -p /etc/systemd/system/supergfxd.service.d
|
||||||
sudo cp -p "$OMARCHY_PATH/default/systemd/system/supergfxd.service.d/delay-start.conf" /etc/systemd/system/supergfxd.service.d/
|
if ! install_root_file "$OMARCHY_PATH/default/systemd/system/supergfxd.service.d/delay-start.conf" \
|
||||||
|
/etc/systemd/system/supergfxd.service.d/delay-start.conf 0644; then
|
||||||
|
echo "Could not install the supergfxd startup-delay override" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Publish the self-guarding sleep hook before enabling Integrated mode. It
|
||||||
|
# remains inert while the config says Hybrid, so any failed step is safe to
|
||||||
|
# retry without leaving the GPU config partially switched.
|
||||||
|
sudo mkdir -p /usr/lib/systemd/system-sleep
|
||||||
|
if ! install_root_file "$OMARCHY_PATH/default/systemd/system-sleep/force-igpu" \
|
||||||
|
/usr/lib/systemd/system-sleep/force-igpu 0755; then
|
||||||
|
echo "Could not install the force-igpu system-sleep hook" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Switch both settings in one atomic config rewrite only after every
|
||||||
|
# supporting file has been installed successfully.
|
||||||
|
sudo sed -i \
|
||||||
|
-e 's/"mode": ".*"/"mode": "Integrated"/' \
|
||||||
|
-e 's/"vfio_enable": false/"vfio_enable": true/' \
|
||||||
|
/etc/supergfxd.conf
|
||||||
|
|
||||||
omarchy-system-reboot
|
omarchy-system-reboot
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -175,7 +175,10 @@ target_home=$(getent passwd "$target_user" | cut -d: -f6)
|
|||||||
[[ -n $target_home && -d $target_home ]] || fail "Home directory for '$target_user' was not found."
|
[[ -n $target_home && -d $target_home ]] || fail "Home directory for '$target_user' was not found."
|
||||||
target_uid=$(id -u "$target_user")
|
target_uid=$(id -u "$target_user")
|
||||||
target_runtime_dir="/run/user/$target_uid"
|
target_runtime_dir="/run/user/$target_uid"
|
||||||
package_path="/usr/share/omarchy/bin:/usr/local/bin:/usr/bin:/bin:$target_home/.local/bin"
|
# User-local commands are needed only after dropping to the target user. Never
|
||||||
|
# expose their search path to commands run through as_root.
|
||||||
|
root_path=/usr/share/omarchy/bin:/usr/local/bin:/usr/bin:/bin
|
||||||
|
package_path="$root_path:$target_home/.local/bin"
|
||||||
|
|
||||||
as_root() {
|
as_root() {
|
||||||
if (( EUID == 0 )); then
|
if (( EUID == 0 )); then
|
||||||
@@ -597,6 +600,9 @@ preserve_kernel_cmdline_root() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log "Preserving the kernel cmdline root parameters in $default_conf"
|
log "Preserving the kernel cmdline root parameters in $default_conf"
|
||||||
|
# omarchy:heredoc-expands paths=none -- ${boot_params[*]} is the kernel
|
||||||
|
# cmdline fragment assembled above from findmnt output (root=, rootflags=),
|
||||||
|
# not a filesystem path this script controls.
|
||||||
as_root tee -a "$default_conf" >/dev/null <<EOF
|
as_root tee -a "$default_conf" >/dev/null <<EOF
|
||||||
# Written by omarchy-upgrade-to-quattro. The += drop-ins in
|
# Written by omarchy-upgrade-to-quattro. The += drop-ins in
|
||||||
# /etc/limine-entry-tool.d/ stop limine-entry-tool from reading
|
# /etc/limine-entry-tool.d/ stop limine-entry-tool from reading
|
||||||
@@ -658,7 +664,7 @@ configure_lock_authentication() {
|
|||||||
as_root env \
|
as_root env \
|
||||||
OMARCHY_INSTALL_USER="$target_user" \
|
OMARCHY_INSTALL_USER="$target_user" \
|
||||||
OMARCHY_PATH=/usr/share/omarchy \
|
OMARCHY_PATH=/usr/share/omarchy \
|
||||||
PATH="$package_path" \
|
PATH="$root_path" \
|
||||||
"$apply_lock"
|
"$apply_lock"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,11 +1193,19 @@ ensure_sleep_lock_service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_post_upgrade_migrations() {
|
run_post_upgrade_migrations() {
|
||||||
PATH="$package_path" command -v omarchy-migrate >/dev/null 2>&1 || return 0
|
local pending_status
|
||||||
|
|
||||||
log "Running Omarchy migrations"
|
log "Running Omarchy migrations"
|
||||||
if ! run_as_user_omarchy OMARCHY_UPGRADE_TO_QUATTRO_LIVE=1 omarchy-migrate; then
|
if ! run_as_user_omarchy OMARCHY_UPGRADE_TO_QUATTRO_LIVE=1 omarchy-migrate; then
|
||||||
warn "Could not run Omarchy migrations; the user may be prompted to run them after login."
|
fail "Omarchy migrations did not complete. Fix the error above and rerun the upgrade before rebooting."
|
||||||
|
fi
|
||||||
|
if run_as_user_omarchy omarchy-migrate --pending >/dev/null; then
|
||||||
|
fail "Omarchy migrations are still pending. Rerun the upgrade before rebooting."
|
||||||
|
else
|
||||||
|
pending_status=$?
|
||||||
|
if (( pending_status != 1 )); then
|
||||||
|
fail "Could not verify that Omarchy migrations completed. Rerun the upgrade before rebooting."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,7 +1290,7 @@ apply_firewall_defaults() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log "Applying Omarchy firewall defaults"
|
log "Applying Omarchy firewall defaults"
|
||||||
as_root env OMARCHY_PATH=/usr/share/omarchy PATH="$package_path" \
|
as_root env OMARCHY_PATH=/usr/share/omarchy PATH="$root_path" \
|
||||||
bash -euo pipefail "$firewall_script" ||
|
bash -euo pipefail "$firewall_script" ||
|
||||||
warn "Could not apply firewall defaults; run 'sudo bash $firewall_script' after reboot."
|
warn "Could not apply firewall defaults; run 'sudo bash $firewall_script' after reboot."
|
||||||
}
|
}
|
||||||
@@ -1312,9 +1326,23 @@ apply_system_transition() {
|
|||||||
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
||||||
as_root gtk-update-icon-cache /usr/share/icons/Yaru >/dev/null 2>&1 || true
|
as_root gtk-update-icon-cache /usr/share/icons/Yaru >/dev/null 2>&1 || true
|
||||||
|
|
||||||
as_root install -d -m 0777 /etc/chromium/policies/managed
|
local browser_policy_helper=/usr/share/omarchy/install/helpers/browser-policy.sh
|
||||||
|
if ! as_root test -f "$browser_policy_helper"; then
|
||||||
|
warn "$browser_policy_helper is unavailable; Chromium policy directories were not hardened."
|
||||||
|
else
|
||||||
|
as_root env OMARCHY_PATH=/usr/share/omarchy \
|
||||||
|
bash -euo pipefail -c '
|
||||||
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||||
|
for dir in "${BROWSER_POLICY_MANAGED_DIRS[@]}"; do
|
||||||
|
[[ $dir == "/etc/chromium/policies/managed" ]] && continue
|
||||||
|
[[ -d $dir || -L $dir ]] || continue
|
||||||
|
browser_policy_setup_dir "$dir"
|
||||||
|
done
|
||||||
|
'
|
||||||
|
fi
|
||||||
as_root install -d -m 0755 /usr/lib/chromium
|
as_root install -d -m 0755 /usr/lib/chromium
|
||||||
printf '%s\n' '{"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' | \
|
printf '%s\n' '{"distribution":{"require_eula":false},"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' | \
|
||||||
as_root tee /usr/lib/chromium/initial_preferences >/dev/null
|
as_root tee /usr/lib/chromium/initial_preferences >/dev/null
|
||||||
|
|
||||||
# Deliberately do NOT add the user to the docker group. That group is
|
# Deliberately do NOT add the user to the docker group. That group is
|
||||||
@@ -1350,7 +1378,6 @@ EOF
|
|||||||
as_root systemctl disable docker.service >/dev/null 2>&1 || true
|
as_root systemctl disable docker.service >/dev/null 2>&1 || true
|
||||||
|
|
||||||
enable_system_service cups.service
|
enable_system_service cups.service
|
||||||
enable_system_service cups-browsed.service
|
|
||||||
enable_system_service avahi-daemon.service
|
enable_system_service avahi-daemon.service
|
||||||
enable_system_service linux-modules-cleanup.service
|
enable_system_service linux-modules-cleanup.service
|
||||||
enable_system_service docker.socket
|
enable_system_service docker.socket
|
||||||
@@ -1382,6 +1409,9 @@ EOF
|
|||||||
autologin_user=$(as_root awk -F= '/^User=/ { print $2; exit }' /etc/sddm.conf.d/autologin.conf 2>/dev/null || true)
|
autologin_user=$(as_root awk -F= '/^User=/ { print $2; exit }' /etc/sddm.conf.d/autologin.conf 2>/dev/null || true)
|
||||||
fi
|
fi
|
||||||
[[ -n ${autologin_user:-} ]] || autologin_user="$target_user"
|
[[ -n ${autologin_user:-} ]] || autologin_user="$target_user"
|
||||||
|
# omarchy:heredoc-expands paths=none -- $autologin_user is a username, read
|
||||||
|
# back from the root-owned drop-in or falling back to $target_user. Same
|
||||||
|
# mechanism as the old getty override: a name expands, no path does.
|
||||||
cat <<EOF | as_root tee /etc/sddm.conf.d/autologin.conf >/dev/null
|
cat <<EOF | as_root tee /etc/sddm.conf.d/autologin.conf >/dev/null
|
||||||
[Autologin]
|
[Autologin]
|
||||||
User=$autologin_user
|
User=$autologin_user
|
||||||
@@ -1392,6 +1422,8 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
as_root install -d -m 0755 -o sddm -g sddm /var/lib/sddm 2>/dev/null || as_root install -d -m 0755 /var/lib/sddm
|
as_root install -d -m 0755 -o sddm -g sddm /var/lib/sddm 2>/dev/null || as_root install -d -m 0755 /var/lib/sddm
|
||||||
|
# omarchy:heredoc-expands paths=none -- $target_user is a username, not a
|
||||||
|
# path; SDDM's state file records who logged in last.
|
||||||
cat <<EOF | as_root tee /var/lib/sddm/state.conf >/dev/null
|
cat <<EOF | as_root tee /var/lib/sddm/state.conf >/dev/null
|
||||||
[Last]
|
[Last]
|
||||||
Session=omarchy.desktop
|
Session=omarchy.desktop
|
||||||
@@ -1537,7 +1569,6 @@ refresh xournalpp/settings.xml 1c1a9efbf1b6dc7813bf3440fd5bf8409fc283e8d0192ca29
|
|||||||
# fastfetch/config.jsonc -> /etc/fastfetch/config.jsonc
|
# fastfetch/config.jsonc -> /etc/fastfetch/config.jsonc
|
||||||
# fontconfig/fonts.conf -> /usr/share/fontconfig/conf.avail/50-omarchy.conf
|
# fontconfig/fonts.conf -> /usr/share/fontconfig/conf.avail/50-omarchy.conf
|
||||||
# mimeapps.list -> /usr/share/applications/mimeapps.list
|
# mimeapps.list -> /usr/share/applications/mimeapps.list
|
||||||
# omarchy.ttf -> /usr/share/fonts/omarchy/omarchy.ttf
|
|
||||||
# systemd/user/bt-agent.service -> /usr/lib/systemd/user/bt-agent.service
|
# systemd/user/bt-agent.service -> /usr/lib/systemd/user/bt-agent.service
|
||||||
# systemd/user/omarchy-recover-internal-monitor.service -> /usr/lib/systemd/user/omarchy-recover-internal-monitor.service
|
# systemd/user/omarchy-recover-internal-monitor.service -> /usr/lib/systemd/user/omarchy-recover-internal-monitor.service
|
||||||
# systemd/user/omarchy-sleep-lock.service -> /usr/lib/systemd/user/omarchy-sleep-lock.service
|
# systemd/user/omarchy-sleep-lock.service -> /usr/lib/systemd/user/omarchy-sleep-lock.service
|
||||||
@@ -1570,7 +1601,6 @@ retire fontconfig/fonts.conf 6dec98b539388b95ecfdb3c7ab951001c712820eb0581002832
|
|||||||
retire fontconfig/fonts.conf 3545f6c5a8c1465df7a4e251b3c2047d4ba03654c86636794f64cb0d8ea8ef63
|
retire fontconfig/fonts.conf 3545f6c5a8c1465df7a4e251b3c2047d4ba03654c86636794f64cb0d8ea8ef63
|
||||||
retire fontconfig/fonts.conf 0f085b449f1cbe8eda3b59a6235bf2a3ddb6e982ab5d22fa642248916e63bafc
|
retire fontconfig/fonts.conf 0f085b449f1cbe8eda3b59a6235bf2a3ddb6e982ab5d22fa642248916e63bafc
|
||||||
retire mimeapps.list 3b574cef135b5deb7a8a0c7e17139037cf1fe155300e3809f60bed0e04120975
|
retire mimeapps.list 3b574cef135b5deb7a8a0c7e17139037cf1fe155300e3809f60bed0e04120975
|
||||||
retire omarchy.ttf e55e67119e82f56f92d90cbf54b7ccc1b2946b32c535a29370439d7ef5215966
|
|
||||||
retire systemd/user/bt-agent.service 0406b577a1225dc2a9f86638d3c346eb3635168576f04050be50ebcc0be6be12
|
retire systemd/user/bt-agent.service 0406b577a1225dc2a9f86638d3c346eb3635168576f04050be50ebcc0be6be12
|
||||||
retire systemd/user/omarchy-recover-internal-monitor.service b9b92cedc44cf3cb6216948629be55b53d16746e31896dc6469fd49ba55e82f4
|
retire systemd/user/omarchy-recover-internal-monitor.service b9b92cedc44cf3cb6216948629be55b53d16746e31896dc6469fd49ba55e82f4
|
||||||
retire systemd/user/omarchy-recover-internal-monitor.service e1483079b9f2aefcd43b4722c75a31643e5f3bb5a2eada5f52f1d7d201e8c289
|
retire systemd/user/omarchy-recover-internal-monitor.service e1483079b9f2aefcd43b4722c75a31643e5f3bb5a2eada5f52f1d7d201e8c289
|
||||||
@@ -2306,6 +2336,11 @@ refresh_current_theme_after_upgrade() {
|
|||||||
# hooks because one of them runs `hyprctl reload`. Still poke terminal
|
# hooks because one of them runs `hyprctl reload`. Still poke terminal
|
||||||
# emulators so the active upgrade terminal picks up generated theme files.
|
# emulators so the active upgrade terminal picks up generated theme files.
|
||||||
run_as_user_omarchy omarchy-restart-terminal >/dev/null 2>&1 || true
|
run_as_user_omarchy omarchy-restart-terminal >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# apply_system_transition purged user-owned color.json. Headless theme-set
|
||||||
|
# skipped omarchy-theme-set-browser, so rewrite the colour here.
|
||||||
|
run_as_user_omarchy omarchy-theme-set-browser >/dev/null 2>&1 ||
|
||||||
|
warn "Could not apply browser theme colour. Run 'omarchy theme set \"$theme_name\"' after reboot if Chromium's theme looks stale."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Everything below mutates the system, so a non-zero exit from here on leaves a
|
# Everything below mutates the system, so a non-zero exit from here on leaves a
|
||||||
@@ -2340,8 +2375,10 @@ run_as_user_omarchy omarchy-bar defaults ||
|
|||||||
cleanup_retired_services
|
cleanup_retired_services
|
||||||
ensure_sleep_lock_service
|
ensure_sleep_lock_service
|
||||||
remove_retired_default_packages
|
remove_retired_default_packages
|
||||||
run_post_upgrade_migrations
|
|
||||||
run_final_system_package_upgrade
|
run_final_system_package_upgrade
|
||||||
|
# This also retires the stock ~/.local/share/fonts/omarchy.ttf missed by the
|
||||||
|
# former ~/.config retirement entry, through migration 1788848726.
|
||||||
|
run_post_upgrade_migrations
|
||||||
run_post_upgrade_update_steps
|
run_post_upgrade_update_steps
|
||||||
refresh_current_theme_after_upgrade
|
refresh_current_theme_after_upgrade
|
||||||
# Do not force-reload Hyprland in the live upgraded session. The legacy
|
# Do not force-reload Hyprland in the live upgraded session. The legacy
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ fi
|
|||||||
|
|
||||||
if grep -q "https://pkgs.omarchy.org/stable/" /etc/pacman.conf; then
|
if grep -q "https://pkgs.omarchy.org/stable/" /etc/pacman.conf; then
|
||||||
pkgs="stable"
|
pkgs="stable"
|
||||||
|
elif grep -q "https://pkgs.omarchy.org/rc/" /etc/pacman.conf; then
|
||||||
|
pkgs="rc"
|
||||||
elif grep -q "https://pkgs.omarchy.org/edge/" /etc/pacman.conf; then
|
elif grep -q "https://pkgs.omarchy.org/edge/" /etc/pacman.conf; then
|
||||||
pkgs="edge"
|
pkgs="edge"
|
||||||
else
|
else
|
||||||
|
|||||||
+98
-16
@@ -13,6 +13,18 @@ safe_icon_name() {
|
|||||||
| sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//'
|
| sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_plain_name() {
|
||||||
|
# The name becomes a filename. A slash would turn it into directory levels, so
|
||||||
|
# the launcher lands somewhere omarchy-webapp-remove cannot address and the app
|
||||||
|
# is stuck in the launcher; a leading ../ leaves the applications directory
|
||||||
|
# altogether. Refuse rather than silently renaming what the user typed -- most
|
||||||
|
# often it is a URL entered in the name field.
|
||||||
|
if [[ $1 == */* ]]; then
|
||||||
|
echo "App name cannot contain '/': $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
icon_name_from_ref() {
|
icon_name_from_ref() {
|
||||||
local ref="$1"
|
local ref="$1"
|
||||||
local name
|
local name
|
||||||
@@ -42,6 +54,34 @@ download_icon() {
|
|||||||
[[ -s $2 && $(file -b --mime-type "$2") == image/* ]]
|
[[ -s $2 && $(file -b --mime-type "$2") == image/* ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Chromium --app= treats javascript:, file:, and data: as a document to
|
||||||
|
# run. Prefix schemeless input with https as before, then refuse anything
|
||||||
|
# that is not http(s).
|
||||||
|
normalize_webapp_url() {
|
||||||
|
local url=$1
|
||||||
|
if [[ ! $url =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
||||||
|
url="https://$url"
|
||||||
|
fi
|
||||||
|
printf '%s' "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Raw whitespace must be percent-encoded in a URL. Refuse it before serializing
|
||||||
|
# the desktop entry; before Exec argument quoting, it also split browser flags
|
||||||
|
# and additional URLs into separate arguments. Schemes are case-insensitive.
|
||||||
|
require_http_url() {
|
||||||
|
local url=$1
|
||||||
|
|
||||||
|
if [[ $url =~ [[:space:]] ]]; then
|
||||||
|
echo "Error: web app URL must not contain whitespace." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! ${url,,} =~ ^https?:// ]]; then
|
||||||
|
echo "Error: web app URL must be http or https." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
fetch_site_icon() {
|
fetch_site_icon() {
|
||||||
local site_url="$1" dest="$2"
|
local site_url="$1" dest="$2"
|
||||||
local origin page icon_url
|
local origin page icon_url
|
||||||
@@ -65,13 +105,44 @@ fetch_site_icon() {
|
|||||||
download_icon "https://www.google.com/s2/favicons?domain=${site_url}&sz=256" "$dest"
|
download_icon "https://www.google.com/s2/favicons?domain=${site_url}&sz=256" "$dest"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desktop_string_escape() {
|
||||||
|
# Desktop Entry "string" value (freedesktop Desktop Entry Spec, "Value types"):
|
||||||
|
# a raw newline would start a new key line and let a value inject a second
|
||||||
|
# Exec=. Escape backslash first, then tab/CR/LF and a leading space. Every value
|
||||||
|
# written into the .desktop file passes through here.
|
||||||
|
#
|
||||||
|
# Parameter expansion rather than sed: GNU sed's N auto-prints the pattern space
|
||||||
|
# and exits at end of input, so a `:a;N;$!ba` slurp skips every following s///
|
||||||
|
# for a value with no newline in it - which is every value except the injection
|
||||||
|
# attempt this exists to stop.
|
||||||
|
local value="$1"
|
||||||
|
|
||||||
|
value=${value//\\/\\\\}
|
||||||
|
value=${value//$'\t'/\\t}
|
||||||
|
value=${value//$'\r'/\\r}
|
||||||
|
value=${value//$'\n'/\\n}
|
||||||
|
[[ $value == " "* ]] && value="\\s${value# }"
|
||||||
|
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
desktop_exec_arg() {
|
||||||
|
# One Exec argument, double-quoted per the freedesktop Exec spec: inside quotes
|
||||||
|
# " ` $ \ take a backslash and a literal % becomes %%. Only the default Exec's
|
||||||
|
# URL needs this; $CUSTOM_EXEC stays a whole command line (file-syntax only).
|
||||||
|
local escaped
|
||||||
|
escaped=$(printf '%s' "$1" \
|
||||||
|
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/`/\\`/g' -e 's/\$/\\$/g' -e 's/%/%%/g')
|
||||||
|
printf '"%s"' "$escaped"
|
||||||
|
}
|
||||||
|
|
||||||
if (( $# < 3 )); then
|
if (( $# < 3 )); then
|
||||||
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
echo -e "\e[32mLet's create a new web app you can start with the app launcher.\n\e[0m"
|
||||||
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
APP_NAME=$(gum input --prompt "Name> " --placeholder "My favorite web app")
|
||||||
|
require_plain_name "$APP_NAME"
|
||||||
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
APP_URL=$(gum input --prompt "URL> " --placeholder "https://example.com")
|
||||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
APP_URL=$(normalize_webapp_url "$APP_URL")
|
||||||
APP_URL="https://$APP_URL"
|
require_http_url "$APP_URL"
|
||||||
fi
|
|
||||||
|
|
||||||
# Try to fetch the site's icon automatically first.
|
# Try to fetch the site's icon automatically first.
|
||||||
mkdir -p "$ICON_DIR"
|
mkdir -p "$ICON_DIR"
|
||||||
@@ -88,10 +159,8 @@ if (( $# < 3 )); then
|
|||||||
INTERACTIVE_MODE=true
|
INTERACTIVE_MODE=true
|
||||||
else
|
else
|
||||||
APP_NAME="$1"
|
APP_NAME="$1"
|
||||||
APP_URL="$2"
|
APP_URL=$(normalize_webapp_url "$2")
|
||||||
if [[ ! $APP_URL =~ ^[a-zA-Z][a-zA-Z0-9+.-]*: ]]; then
|
require_http_url "$APP_URL"
|
||||||
APP_URL="https://$APP_URL"
|
|
||||||
fi
|
|
||||||
ICON_REF="$3"
|
ICON_REF="$3"
|
||||||
CUSTOM_EXEC="$4" # Optional custom exec command
|
CUSTOM_EXEC="$4" # Optional custom exec command
|
||||||
MIME_TYPES="$5" # Optional mime types
|
MIME_TYPES="$5" # Optional mime types
|
||||||
@@ -104,6 +173,8 @@ if [[ -z $APP_NAME || -z $APP_URL ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
require_plain_name "$APP_NAME"
|
||||||
|
|
||||||
if [[ -z $ICON_REF ]]; then
|
if [[ -z $ICON_REF ]]; then
|
||||||
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
ICON_VALUE=$(safe_icon_name "$APP_NAME")
|
||||||
mkdir -p "$ICON_DIR"
|
mkdir -p "$ICON_DIR"
|
||||||
@@ -128,28 +199,39 @@ else
|
|||||||
ICON_VALUE=$(icon_name_from_ref "$ICON_REF")
|
ICON_VALUE=$(icon_name_from_ref "$ICON_REF")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use custom exec if provided, otherwise default behavior
|
# Default Exec quotes the URL as one Exec-spec argument; the whole line then gets
|
||||||
EXEC_COMMAND="${CUSTOM_EXEC:-omarchy-launch-webapp $APP_URL}"
|
# the file-syntax escaping below (unescaped first at read time per spec, so the
|
||||||
|
# layers compose). $CUSTOM_EXEC is a full command line, so it gets file-syntax only.
|
||||||
|
if [[ -n $CUSTOM_EXEC ]]; then
|
||||||
|
EXEC_COMMAND=$CUSTOM_EXEC
|
||||||
|
else
|
||||||
|
EXEC_COMMAND="omarchy-launch-webapp $(desktop_exec_arg "$APP_URL")"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create application .desktop file
|
# Create application .desktop file
|
||||||
DESKTOP_FILE="$HOME/.local/share/applications/$APP_NAME.desktop"
|
DESKTOP_DIR="$HOME/.local/share/applications"
|
||||||
mkdir -p "$(dirname "$DESKTOP_FILE")"
|
DESKTOP_FILE="$DESKTOP_DIR/$APP_NAME.desktop"
|
||||||
|
mkdir -p "$DESKTOP_DIR"
|
||||||
|
|
||||||
|
name_field=$(desktop_string_escape "$APP_NAME")
|
||||||
|
exec_field=$(desktop_string_escape "$EXEC_COMMAND")
|
||||||
|
icon_field=$(desktop_string_escape "$ICON_VALUE")
|
||||||
|
|
||||||
cat >"$DESKTOP_FILE" <<EOF
|
cat >"$DESKTOP_FILE" <<EOF
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=1.0
|
Version=1.0
|
||||||
Name=$APP_NAME
|
Name=$name_field
|
||||||
Comment=$APP_NAME
|
Comment=$name_field
|
||||||
Exec=$EXEC_COMMAND
|
Exec=$exec_field
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Icon=$ICON_VALUE
|
Icon=$icon_field
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Add mime types if provided
|
# Add mime types if provided
|
||||||
if [[ -n $MIME_TYPES ]]; then
|
if [[ -n $MIME_TYPES ]]; then
|
||||||
echo "MimeType=$MIME_TYPES" >>"$DESKTOP_FILE"
|
printf 'MimeType=%s\n' "$(desktop_string_escape "$MIME_TYPES")" >>"$DESKTOP_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod +x "$DESKTOP_FILE"
|
chmod +x "$DESKTOP_FILE"
|
||||||
|
|||||||
@@ -9,14 +9,31 @@ ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
|||||||
OLD_ICON_DIR="$HOME/.local/share/applications/icons"
|
OLD_ICON_DIR="$HOME/.local/share/applications/icons"
|
||||||
DESKTOP_DIR="$HOME/.local/share/applications/"
|
DESKTOP_DIR="$HOME/.local/share/applications/"
|
||||||
|
|
||||||
if (( $# == 0 )); then
|
# Always index the launchers, so removal deletes the file that was found rather
|
||||||
# Find all web apps
|
# than a path rebuilt from the displayed name. Installs predating the name
|
||||||
while IFS= read -r -d '' file; do
|
# validation could nest the launcher inside directories, and those are exactly
|
||||||
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
# the ones a reconstructed path cannot reach.
|
||||||
WEB_APPS+=("$(basename "${file%.desktop}")")
|
WEB_APP_PATHS=()
|
||||||
fi
|
while IFS= read -r -d '' file; do
|
||||||
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0)
|
if grep -q '^Exec=.*\(omarchy-launch-webapp\|omarchy-webapp-handler\).*' "$file"; then
|
||||||
|
WEB_APPS+=("$(basename "${file%.desktop}")")
|
||||||
|
WEB_APP_PATHS+=("$file")
|
||||||
|
fi
|
||||||
|
done < <(find "$DESKTOP_DIR" -name '*.desktop' -print0 2>/dev/null)
|
||||||
|
|
||||||
|
# The launcher matching a chosen name, or empty when nothing was indexed under
|
||||||
|
# it (an app removed between the scan and the pick, say).
|
||||||
|
path_for_web_app() {
|
||||||
|
local wanted="$1" i
|
||||||
|
for i in "${!WEB_APPS[@]}"; do
|
||||||
|
if [[ ${WEB_APPS[$i]} == "$wanted" ]]; then
|
||||||
|
printf '%s\n' "${WEB_APP_PATHS[$i]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if (( $# == 0 )); then
|
||||||
if ((${#WEB_APPS[@]})); then
|
if ((${#WEB_APPS[@]})); then
|
||||||
mapfile -t SORTED_WEB_APPS < <(printf '%s\n' "${WEB_APPS[@]}" | sort)
|
mapfile -t SORTED_WEB_APPS < <(printf '%s\n' "${WEB_APPS[@]}" | sort)
|
||||||
APP_NAME=$(omarchy-menu-select "Select web app to remove" "${SORTED_WEB_APPS[@]}" -- --width 520 --maxheight 520)
|
APP_NAME=$(omarchy-menu-select "Select web app to remove" "${SORTED_WEB_APPS[@]}" -- --width 520 --maxheight 520)
|
||||||
@@ -34,7 +51,8 @@ if [[ -z $APP_NAME ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
icon_name=$(printf '%s\n' "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//')
|
icon_name=$(printf '%s\n' "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^[:alnum:]]\+/-/g; s/^-//; s/-$//')
|
||||||
rm -f "$DESKTOP_DIR/$APP_NAME.desktop"
|
desktop_file=$(path_for_web_app "$APP_NAME")
|
||||||
|
rm -f "${desktop_file:-$DESKTOP_DIR/$APP_NAME.desktop}"
|
||||||
rm -f "$ICON_DIR/$icon_name.png" "$ICON_DIR/$APP_NAME.png" "$OLD_ICON_DIR/$APP_NAME.png"
|
rm -f "$ICON_DIR/$icon_name.png" "$ICON_DIR/$APP_NAME.png" "$OLD_ICON_DIR/$APP_NAME.png"
|
||||||
|
|
||||||
if [[ ${OMARCHY_REMOVE_NOTIFY:-true} != "false" ]]; then
|
if [[ ${OMARCHY_REMOVE_NOTIFY:-true} != "false" ]]; then
|
||||||
|
|||||||
+898
-125
File diff suppressed because it is too large.
Load diff
+14
-28
@@ -1,35 +1,21 @@
|
|||||||
|
# Remove the include below to disconnect Kitty from Omarchy's theming system.
|
||||||
include ~/.local/state/omarchy/current/theme/kitty.conf
|
include ~/.local/state/omarchy/current/theme/kitty.conf
|
||||||
|
|
||||||
|
# Settings below override Omarchy's defaults in /etc/xdg/kitty/kitty.conf.
|
||||||
|
# Learn more: https://sw.kovidgoyal.net/kitty/conf/
|
||||||
|
|
||||||
# Font
|
# Font
|
||||||
font_family JetBrainsMono Nerd Font
|
# font_family JetBrainsMono Nerd Font
|
||||||
bold_italic_font auto
|
# font_size 12
|
||||||
font_size 9.0
|
|
||||||
|
|
||||||
# Window
|
# Window padding
|
||||||
window_padding_width 14
|
# window_padding_width 14
|
||||||
hide_window_decorations yes
|
|
||||||
confirm_os_window_close 0
|
|
||||||
|
|
||||||
# Keybindings
|
# Unmap a shortcut, passing it through to the terminal application
|
||||||
map ctrl+insert copy_to_clipboard
|
# map ctrl+insert
|
||||||
map shift+insert paste_from_clipboard
|
|
||||||
# Send Shift+Enter as CSI-u so TUIs can distinguish it from Enter.
|
|
||||||
map shift+enter send_text all \e[13;2u
|
|
||||||
# Kitty legacy encoding sends Alt+Shift+Enter the same as Alt+Enter; send CSI-u so tmux can match M-S-Enter.
|
|
||||||
map alt+shift+enter send_text all \e[13;4u
|
|
||||||
|
|
||||||
# Allow remote access
|
# Set or replace a shortcut
|
||||||
allow_remote_control yes
|
# map ctrl+shift+c copy_to_clipboard
|
||||||
listen_on unix:${XDG_RUNTIME_DIR}/omarchy-kitty-{kitty_pid}
|
|
||||||
|
|
||||||
# Aesthetics
|
# Remove all inherited shortcuts, including Kitty's built-in shortcuts
|
||||||
cursor_shape block
|
# clear_all_shortcuts yes
|
||||||
cursor_blink_interval 0
|
|
||||||
shell_integration no-cursor
|
|
||||||
enable_audio_bell no
|
|
||||||
|
|
||||||
# Minimal Tab bar styling
|
|
||||||
tab_bar_edge bottom
|
|
||||||
tab_bar_style powerline
|
|
||||||
tab_powerline_style slanted
|
|
||||||
tab_title_template {title}{' :{}:'.format(num_windows) if num_windows > 1 else ''}
|
|
||||||
@@ -10,6 +10,12 @@ The private-use glyphs in `omarchy.ttf` are:
|
|||||||
- `U+E905` — Codex, from the OpenAI mark on <https://developers.openai.com/codex/>
|
- `U+E905` — Codex, from the OpenAI mark on <https://developers.openai.com/codex/>
|
||||||
- `U+E906` — LM Studio, from <https://simpleicons.org/icons/lmstudio.svg>
|
- `U+E906` — LM Studio, from <https://simpleicons.org/icons/lmstudio.svg>
|
||||||
- `U+E907` — Ollama, from <https://simpleicons.org/icons/ollama.svg>
|
- `U+E907` — Ollama, from <https://simpleicons.org/icons/ollama.svg>
|
||||||
|
- `U+E908` — T3 Code, traced from the app icon in <https://aur.archlinux.org/cgit/aur.git/plain/t3code-icon.png?h=t3code-bin>, since upstream publishes no monochrome SVG
|
||||||
|
|
||||||
|
- `U+E90A` — Hermes, Font Awesome's staff-snake (CC BY 4.0) from <https://fontawesome.com/icons/staff-snake>, the mark Hermes serves as its favicon: their app icon is a portrait that reads as a smudge at menu size
|
||||||
|
- `U+E90B` — Perplexity, from <https://simpleicons.org/icons/perplexity.svg>
|
||||||
|
- `U+E90C` — OpenClaw, traced from the lobster mascot the openclaw package ships as `dist/control-ui/favicon.svg`, since upstream publishes no monochrome SVG
|
||||||
|
- `U+E90D` — Cursor, from <https://simpleicons.org/icons/cursor.svg>
|
||||||
|
|
||||||
The agent marks are monochrome so the menu can render them using the active
|
The agent marks are monochrome so the menu can render them using the active
|
||||||
theme's foreground and selection colors.
|
theme's foreground and selection colors.
|
||||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
-- Hermes Desktop's frameless HUD manages its own geometry.
|
||||||
|
o.window({ class = "^Hermes$", title = "^Hermes HUD$" }, {
|
||||||
|
tag = "-default-opacity",
|
||||||
|
float = true,
|
||||||
|
border_size = 0,
|
||||||
|
opacity = "1 1",
|
||||||
|
})
|
||||||
@@ -12,6 +12,7 @@ fcitx5-configtool
|
|||||||
fcitx5-wayland-launcher
|
fcitx5-wayland-launcher
|
||||||
foot-server
|
foot-server
|
||||||
footclient
|
footclient
|
||||||
|
hermes
|
||||||
java-java-openjdk
|
java-java-openjdk
|
||||||
jconsole-java-openjdk
|
jconsole-java-openjdk
|
||||||
jshell-java-openjdk
|
jshell-java-openjdk
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
// Style
|
// Style
|
||||||
"style.theme": {"icon":"","label":"Theme","aliases":["theme","themes"],"action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
|
"style.theme": {"icon":"","label":"Theme","aliases":["theme","themes"],"action":"theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\""},
|
||||||
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
|
"style.background": {"icon":"","label":"Background","aliases":["background","wallpaper"],"action":"background=$(omarchy-theme-bg-switcher); [[ -n $background ]] && omarchy-theme-bg-set \"$background\""},
|
||||||
"style.unlock": {"icon":"","label":"Unlock","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme '$unlock'\"; fi"},
|
"style.unlock": {"icon":"","label":"Unlock","aliases":["unlock"],"action":"unlock=$(omarchy-plymouth-switcher); if [[ $unlock == default ]]; then omarchy-launch-floating-terminal-with-presentation omarchy-plymouth-reset; elif [[ -n $unlock ]]; then omarchy-launch-floating-terminal-with-presentation \"omarchy-plymouth-set-by-theme $(printf %q \"$unlock\")\"; fi"},
|
||||||
"style.font": {"icon":"","label":"Font","provider":"fonts"},
|
"style.font": {"icon":"","label":"Font","provider":"fonts"},
|
||||||
"style.bar": {"icon":"","label":"Menu Bar"},
|
"style.bar": {"icon":"","label":"Menu Bar"},
|
||||||
"style.bar.position": {"icon":"","label":"Position"},
|
"style.bar.position": {"icon":"","label":"Position"},
|
||||||
@@ -134,9 +134,13 @@
|
|||||||
"setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"},
|
"setup.default.agent.codex": {"icon":"","iconFont":"omarchy","label":"Codex","checked":"[[ \"$(omarchy-default-agent)\" == \"codex\" ]]","action":"omarchy-default-agent codex"},
|
||||||
"setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"},
|
"setup.default.agent.copilot": {"icon":"","label":"Copilot","checked":"[[ \"$(omarchy-default-agent)\" == \"copilot\" ]]","action":"omarchy-default-agent copilot"},
|
||||||
"setup.default.agent.crush": {"icon":"","label":"Crush","checked":"[[ \"$(omarchy-default-agent)\" == \"crush\" ]]","action":"omarchy-default-agent crush"},
|
"setup.default.agent.crush": {"icon":"","label":"Crush","checked":"[[ \"$(omarchy-default-agent)\" == \"crush\" ]]","action":"omarchy-default-agent crush"},
|
||||||
|
"setup.default.agent.cursor-agent": {"icon":"","iconFont":"omarchy","label":"Cursor CLI","checked":"[[ \"$(omarchy-default-agent)\" == \"cursor-agent\" ]]","action":"omarchy-default-agent cursor-agent"},
|
||||||
"setup.default.agent.gemini": {"icon":"","label":"Gemini","checked":"[[ \"$(omarchy-default-agent)\" == \"gemini\" ]]","action":"omarchy-default-agent gemini"},
|
"setup.default.agent.gemini": {"icon":"","label":"Gemini","checked":"[[ \"$(omarchy-default-agent)\" == \"gemini\" ]]","action":"omarchy-default-agent gemini"},
|
||||||
"setup.default.agent.grok": {"icon":"","iconFont":"omarchy","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"},
|
"setup.default.agent.grok": {"icon":"","iconFont":"omarchy","label":"Grok","checked":"[[ \"$(omarchy-default-agent)\" == \"grok\" ]]","action":"omarchy-default-agent grok"},
|
||||||
|
"setup.default.agent.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes","checked":"[[ \"$(omarchy-default-agent)\" == \"hermes\" ]]","action":"omarchy-default-agent hermes"},
|
||||||
|
"setup.default.agent.muse": {"icon":"","label":"Muse Code","checked":"[[ \"$(omarchy-default-agent)\" == \"muse\" ]]","action":"omarchy-default-agent muse"},
|
||||||
"setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"},
|
"setup.default.agent.omp": {"icon":"","iconFont":"omarchy","label":"omp","checked":"[[ \"$(omarchy-default-agent)\" == \"omp\" ]]","action":"omarchy-default-agent omp"},
|
||||||
|
"setup.default.agent.openclaw": {"icon":"","iconFont":"omarchy","label":"OpenClaw","checked":"[[ \"$(omarchy-default-agent)\" == \"openclaw\" ]]","action":"omarchy-default-agent openclaw"},
|
||||||
"setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"},
|
"setup.default.agent.opencode": {"icon":"","iconFont":"omarchy","label":"OpenCode","checked":"[[ \"$(omarchy-default-agent)\" == \"opencode\" ]]","action":"omarchy-default-agent opencode"},
|
||||||
"setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"},
|
"setup.default.agent.pi": {"icon":"","iconFont":"omarchy","label":"Pi","checked":"[[ \"$(omarchy-default-agent)\" == \"pi\" ]]","action":"omarchy-default-agent pi"},
|
||||||
"setup.default.browser": {"icon":"","label":"Browser","title":"Default Browser"},
|
"setup.default.browser": {"icon":"","label":"Browser","title":"Default Browser"},
|
||||||
@@ -155,7 +159,7 @@
|
|||||||
"setup.default.editor": {"icon":"","label":"Editor","title":"Default Editor"},
|
"setup.default.editor": {"icon":"","label":"Editor","title":"Default Editor"},
|
||||||
"setup.default.editor.neovim": {"icon":"","label":"Neovim","when":"omarchy-cmd-present nvim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
|
"setup.default.editor.neovim": {"icon":"","label":"Neovim","when":"omarchy-cmd-present nvim","checked":"[[ \"$(omarchy-default-editor)\" == \"nvim\" ]]","action":"omarchy-default-editor nvim"},
|
||||||
"setup.default.editor.vscode": {"icon":"","label":"VSCode","when":"omarchy-cmd-present code","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
|
"setup.default.editor.vscode": {"icon":"","label":"VSCode","when":"omarchy-cmd-present code","checked":"[[ \"$(omarchy-default-editor)\" == \"code\" ]]","action":"omarchy-default-editor code"},
|
||||||
"setup.default.editor.cursor": {"icon":"","label":"Cursor","when":"omarchy-cmd-present cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
|
"setup.default.editor.cursor": {"icon":"","iconFont":"omarchy","label":"Cursor","when":"omarchy-cmd-present cursor","checked":"[[ \"$(omarchy-default-editor)\" == \"cursor\" ]]","action":"omarchy-default-editor cursor"},
|
||||||
"setup.default.editor.zed": {"icon":"","label":"Zed","when":"omarchy-cmd-present zeditor","checked":"[[ \"$(omarchy-default-editor)\" == \"zeditor\" ]]","action":"omarchy-default-editor zed"},
|
"setup.default.editor.zed": {"icon":"","label":"Zed","when":"omarchy-cmd-present zeditor","checked":"[[ \"$(omarchy-default-editor)\" == \"zeditor\" ]]","action":"omarchy-default-editor zed"},
|
||||||
"setup.default.editor.sublime": {"icon":"","label":"Sublime Text","when":"omarchy-cmd-present sublime_text","checked":"[[ \"$(omarchy-default-editor)\" == \"sublime_text\" ]]","action":"omarchy-default-editor sublime_text"},
|
"setup.default.editor.sublime": {"icon":"","label":"Sublime Text","when":"omarchy-cmd-present sublime_text","checked":"[[ \"$(omarchy-default-editor)\" == \"sublime_text\" ]]","action":"omarchy-default-editor sublime_text"},
|
||||||
"setup.default.editor.helix": {"icon":"","label":"Helix","when":"omarchy-cmd-present helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
"setup.default.editor.helix": {"icon":"","label":"Helix","when":"omarchy-cmd-present helix","checked":"[[ \"$(omarchy-default-editor)\" == \"helix\" ]]","action":"omarchy-default-editor helix"},
|
||||||
@@ -220,7 +224,7 @@
|
|||||||
"install.service.bitwarden": {"icon":"","label":"Bitwarden","when":"! omarchy-pkg-present bitwarden","action":"omarchy-install-and-launch Bitwarden 'bitwarden bitwarden-cli' bitwarden"},
|
"install.service.bitwarden": {"icon":"","label":"Bitwarden","when":"! omarchy-pkg-present bitwarden","action":"omarchy-install-and-launch Bitwarden 'bitwarden bitwarden-cli' bitwarden"},
|
||||||
"install.service.chromium-account": {"icon":"","label":"Chromium Account","when":"[[ -f ~/.config/chromium-flags.conf ]] && ! grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
|
"install.service.chromium-account": {"icon":"","label":"Chromium Account","when":"[[ -f ~/.config/chromium-flags.conf ]] && ! grep -q oauth2-client-id ~/.config/chromium-flags.conf","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-chromium-google-account"},
|
||||||
"install.editor.vscode": {"icon":"","label":"VSCode","when":"! omarchy-pkg-present visual-studio-code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-vscode"},
|
"install.editor.vscode": {"icon":"","label":"VSCode","when":"! omarchy-pkg-present visual-studio-code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-vscode"},
|
||||||
"install.editor.cursor": {"icon":"","label":"Cursor","when":"! omarchy-pkg-present cursor-bin","action":"omarchy-install-and-launch Cursor cursor-bin cursor"},
|
"install.editor.cursor": {"icon":"","iconFont":"omarchy","label":"Cursor","when":"! omarchy-pkg-present cursor-bin","action":"omarchy-install-and-launch Cursor cursor-bin cursor"},
|
||||||
"install.editor.zed": {"icon":"","label":"Zed","when":"! omarchy-pkg-present zed","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-zed"},
|
"install.editor.zed": {"icon":"","label":"Zed","when":"! omarchy-pkg-present zed","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-zed"},
|
||||||
"install.editor.sublime": {"icon":"","label":"Sublime Text","when":"! omarchy-pkg-present sublime-text-4","action":"omarchy-install-and-launch 'Sublime Text' sublime-text-4 sublime_text"},
|
"install.editor.sublime": {"icon":"","label":"Sublime Text","when":"! omarchy-pkg-present sublime-text-4","action":"omarchy-install-and-launch 'Sublime Text' sublime-text-4 sublime_text"},
|
||||||
"install.editor.helix": {"icon":"","label":"Helix","when":"! omarchy-pkg-present helix","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-helix"},
|
"install.editor.helix": {"icon":"","label":"Helix","when":"! omarchy-pkg-present helix","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-editor-helix"},
|
||||||
@@ -235,6 +239,10 @@
|
|||||||
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"! omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
"install.ai.grok-bot": {"icon":"","iconFont":"omarchy","label":"Grok Bot","when":"! omarchy-pkg-present grok-bot","action":"omarchy-install-and-launch 'Grok Bot' grok-bot grok-bot"},
|
||||||
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"! omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
|
"install.ai.lm-studio": {"icon":"","iconFont":"omarchy","label":"LM Studio","when":"! omarchy-pkg-present lmstudio-bin","action":"omarchy-install-app 'LM Studio' lmstudio-bin"},
|
||||||
"install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"! omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""},
|
"install.ai.ollama": {"icon":"","iconFont":"omarchy","label":"Ollama","when":"! omarchy-cmd-present ollama","action":"if omarchy-cmd-present nvidia-smi; then ollama_pkg=ollama-cuda; elif omarchy-cmd-present rocminfo; then ollama_pkg=ollama-rocm; else ollama_pkg=ollama; fi; omarchy-install-app Ollama \"$ollama_pkg\""},
|
||||||
|
"install.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","when":"! omarchy-pkg-present t3code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-t3-code"},
|
||||||
|
"install.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","when":"! omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-hermes"},
|
||||||
|
"install.ai.openclaw": {"icon":"","iconFont":"omarchy","label":"OpenClaw","when":"! omarchy-pkg-present openclaw","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-ai-openclaw"},
|
||||||
|
"install.ai.perplexity": {"icon":"","iconFont":"omarchy","label":"Perplexity","when":"! omarchy-pkg-present perplexity","action":"omarchy-install-and-launch Perplexity perplexity perplexity"},
|
||||||
"install.gaming.steam": {"icon":"","label":"Steam","when":"! omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"},
|
"install.gaming.steam": {"icon":"","label":"Steam","when":"! omarchy-pkg-present steam","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-steam"},
|
||||||
"install.gaming.retroarch": {"icon":"","label":"RetroArch","when":"! omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"},
|
"install.gaming.retroarch": {"icon":"","label":"RetroArch","when":"! omarchy-pkg-present retroarch","action":"omarchy-launch-floating-terminal-with-presentation omarchy-install-gaming-retroarch"},
|
||||||
"install.gaming.minecraft": {"icon":"","label":"Minecraft","when":"! omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"},
|
"install.gaming.minecraft": {"icon":"","label":"Minecraft","when":"! omarchy-pkg-present minecraft-launcher","action":"omarchy-install-and-launch Minecraft minecraft-launcher minecraft-launcher"},
|
||||||
@@ -285,6 +293,7 @@
|
|||||||
"remove.security.fido2": {"icon":"","label":"Fido2","when":"omarchy-pkg-present pam-u2f","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-fido2"},
|
"remove.security.fido2": {"icon":"","label":"Fido2","when":"omarchy-pkg-present pam-u2f","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-fido2"},
|
||||||
"remove.security.sshd": {"icon":"","label":"SSHD","when":"systemctl is-enabled --quiet sshd","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sshd"},
|
"remove.security.sshd": {"icon":"","label":"SSHD","when":"systemctl is-enabled --quiet sshd","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sshd"},
|
||||||
"remove.security.sudoless-docker": {"icon":"","label":"Sudoless Docker","when":"! omarchy-sudo-docker --configured","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sudoless-docker"},
|
"remove.security.sudoless-docker": {"icon":"","label":"Sudoless Docker","when":"! omarchy-sudo-docker --configured","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-security-sudoless-docker"},
|
||||||
|
"remove.ai.hermes": {"icon":"","iconFont":"omarchy","label":"Hermes Desktop","when":"omarchy-pkg-present hermes-desktop","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-hermes"},
|
||||||
"remove.browser.chrome": {"icon":"","label":"Chrome","when":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser chrome'"},
|
"remove.browser.chrome": {"icon":"","label":"Chrome","when":"omarchy-pkg-present google-chrome","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser chrome'"},
|
||||||
"remove.browser.edge": {"icon":"","label":"Edge","when":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser edge'"},
|
"remove.browser.edge": {"icon":"","label":"Edge","when":"omarchy-pkg-present microsoft-edge-stable-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser edge'"},
|
||||||
"remove.browser.brave": {"icon":"","label":"Brave","when":"omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser brave'"},
|
"remove.browser.brave": {"icon":"","label":"Brave","when":"omarchy-pkg-present brave-bin","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-browser brave'"},
|
||||||
@@ -323,6 +332,10 @@
|
|||||||
"remove.development.php.symfony": {"icon":"","label":"Symfony","when":"omarchy-pkg-present symfony-cli","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env symfony'"},
|
"remove.development.php.symfony": {"icon":"","label":"Symfony","when":"omarchy-pkg-present symfony-cli","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env symfony'"},
|
||||||
"remove.development.elixir.elixir": {"icon":"","label":"Elixir","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env elixir'"},
|
"remove.development.elixir.elixir": {"icon":"","label":"Elixir","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env elixir'"},
|
||||||
"remove.development.elixir.phoenix": {"icon":"","label":"Phoenix","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env phoenix'"},
|
"remove.development.elixir.phoenix": {"icon":"","label":"Phoenix","when":"[[ -d $HOME/.local/share/mise/installs/elixir ]]","action":"omarchy-launch-floating-terminal-with-presentation 'omarchy-remove-dev-env phoenix'"},
|
||||||
|
"remove.ai": {"icon":"","label":"AI","title":"Remove"},
|
||||||
|
"remove.ai.t3-code": {"icon":"","iconFont":"omarchy","label":"T3 Code","when":"omarchy-pkg-present t3code-bin","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-t3-code"},
|
||||||
|
"remove.ai.openclaw": {"icon":"","iconFont":"omarchy","label":"OpenClaw","when":"omarchy-pkg-present openclaw","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-openclaw"},
|
||||||
|
"remove.ai.perplexity": {"icon":"","iconFont":"omarchy","label":"Perplexity","when":"omarchy-pkg-present perplexity","action":"omarchy-launch-floating-terminal-with-presentation omarchy-remove-ai-perplexity"},
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
"update.omarchy": {"icon":"","iconFont":"omarchy","label":"Omarchy","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update"},
|
"update.omarchy": {"icon":"","iconFont":"omarchy","label":"Omarchy","action":"omarchy-launch-floating-terminal-with-presentation omarchy-update"},
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ Include = /etc/pacman.d/mirrorlist
|
|||||||
Include = /etc/pacman.d/mirrorlist
|
Include = /etc/pacman.d/mirrorlist
|
||||||
|
|
||||||
[omarchy]
|
[omarchy]
|
||||||
SigLevel = Optional TrustAll
|
|
||||||
Server = https://pkgs.omarchy.org/edge/$arch
|
Server = https://pkgs.omarchy.org/edge/$arch
|
||||||
|
|
||||||
# Repositories for debug symbol packages.
|
# Repositories for debug symbol packages.
|
||||||
|
|||||||
@@ -26,5 +26,4 @@ Include = /etc/pacman.d/mirrorlist
|
|||||||
Include = /etc/pacman.d/mirrorlist
|
Include = /etc/pacman.d/mirrorlist
|
||||||
|
|
||||||
[omarchy]
|
[omarchy]
|
||||||
SigLevel = Optional TrustAll
|
Server = https://pkgs.omarchy.org/rc/$arch
|
||||||
Server = https://pkgs.omarchy.org/edge/$arch
|
|
||||||
@@ -26,5 +26,4 @@ Include = /etc/pacman.d/mirrorlist
|
|||||||
Include = /etc/pacman.d/mirrorlist
|
Include = /etc/pacman.d/mirrorlist
|
||||||
|
|
||||||
[omarchy]
|
[omarchy]
|
||||||
SigLevel = Optional TrustAll
|
|
||||||
Server = https://pkgs.omarchy.org/stable/$arch
|
Server = https://pkgs.omarchy.org/stable/$arch
|
||||||
@@ -1,29 +1,65 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
|
# Use the Vfio to Integrated trick to turn off NVIDIA dgpu when in integrated mode
|
||||||
# without needing to restart the computer. This is needed because computers like the Asus G14
|
# without needing to restart the computer. This is needed because computers like the Asus G14
|
||||||
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
|
# will wake after suspend in Hybrid mode, even if the system was in Integrated mode before
|
||||||
# suspending.
|
# suspending.
|
||||||
|
|
||||||
|
restore_marker=/run/omarchy-force-igpu-integrated
|
||||||
|
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||||
|
[[ -x /usr/bin/supergfxctl ]] || exit 0
|
||||||
|
|
||||||
|
switch_mode() {
|
||||||
|
local expected="$1" current
|
||||||
|
|
||||||
|
if ! /usr/bin/timeout --kill-after=1s 3s /usr/bin/supergfxctl -m "$expected"; then
|
||||||
|
echo "Could not request the GPU transition to $expected mode" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
for _ in {1..10}; do
|
||||||
|
if current=$(/usr/bin/timeout --kill-after=1s 2s /usr/bin/supergfxctl -g 2>/dev/null) &&
|
||||||
|
[[ $current == "$expected" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Could not confirm the GPU transition to $expected mode" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
pre)
|
pre)
|
||||||
|
# Remember the mode this sleep cycle started in. supergfxctl persists the
|
||||||
|
# temporary hibernate switch to Vfio, so post must not consult that mutable
|
||||||
|
# value when deciding whether to restore Integrated mode.
|
||||||
|
if [[ -L $restore_marker ]]; then
|
||||||
|
exit 1
|
||||||
|
elif [[ ! -f $restore_marker ]]; then
|
||||||
|
/usr/bin/grep -Eq '"mode"[[:space:]]*:[[:space:]]*"Integrated"' /etc/supergfxd.conf 2>/dev/null || exit 0
|
||||||
|
/usr/bin/install -m 0600 -o root -g root -T /dev/null "$restore_marker"
|
||||||
|
fi
|
||||||
|
|
||||||
# Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
|
# Before hibernating, switch to Vfio so the nvidia driver is detached from the dGPU.
|
||||||
# Without this, hibernate resume fails because the nvidia driver can't freeze a
|
# Without this, hibernate resume fails because the nvidia driver can't freeze a
|
||||||
# powered-off dGPU (returns -EIO), which aborts the entire resume.
|
# powered-off dGPU (returns -EIO), which aborts the entire resume.
|
||||||
if [[ $2 == "hibernate" ]]; then
|
if [[ $sleep_action == "hibernate" ]]; then
|
||||||
/usr/bin/supergfxctl -m Vfio
|
switch_mode Vfio
|
||||||
sleep 1
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
post)
|
post)
|
||||||
|
[[ -f $restore_marker && ! -L $restore_marker ]] || exit 0
|
||||||
|
|
||||||
# small delay so the device is fully re-enumerated
|
# small delay so the device is fully re-enumerated
|
||||||
sleep 4
|
sleep 4
|
||||||
|
|
||||||
# force-bind dGPU to vfio (fully detached from nvidia)
|
# force-bind dGPU to vfio (fully detached from nvidia)
|
||||||
/usr/bin/supergfxctl -m Vfio
|
switch_mode Vfio
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# then go back to Integrated, which powers it off again
|
# then go back to Integrated, which powers it off again
|
||||||
/usr/bin/supergfxctl -m Integrated
|
switch_mode Integrated
|
||||||
|
/usr/bin/rm -f -- "$restore_marker"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
# Turn off keyboard backlight before hibernate to prevent hang on power-off.
|
# Turn off keyboard backlight before hibernate to prevent hang on power-off.
|
||||||
# The ASUS keyboard controller can block S4 shutdown if LEDs are active.
|
# The ASUS keyboard controller can block S4 shutdown if LEDs are active.
|
||||||
|
|
||||||
if [[ $1 == "pre" && $2 == "hibernate" ]]; then
|
sleep_action=${SYSTEMD_SLEEP_ACTION:-$2}
|
||||||
|
|
||||||
|
if [[ $1 == "pre" && $sleep_action == "hibernate" ]]; then
|
||||||
device=""
|
device=""
|
||||||
for candidate in /sys/class/leds/*kbd_backlight*; do
|
for candidate in /sys/class/leds/*kbd_backlight*; do
|
||||||
if [[ -e "$candidate" ]]; then
|
if [[ -e "$candidate" ]]; then
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=/usr/bin/updatedb --prune-bind-mounts=no --add-prunepaths=/.snapshots
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
name: omarchy
|
||||||
|
description: Omarchy system theme
|
||||||
|
colors:
|
||||||
|
background: "{{ background }}"
|
||||||
|
ui_text: "{{ foreground }}"
|
||||||
|
ui_primary: "{{ accent }}"
|
||||||
|
ui_accent: "{{ accent }}"
|
||||||
|
ui_border: "{{ muted }}"
|
||||||
|
ui_label: "{{ accent }}"
|
||||||
|
ui_ok: "{{ green }}"
|
||||||
|
ui_warn: "{{ yellow }}"
|
||||||
|
ui_error: "{{ red }}"
|
||||||
|
ui_tool: "{{ cyan }}"
|
||||||
|
ui_thinking: "{{ dark_foreground }}"
|
||||||
|
banner_border: "{{ muted }}"
|
||||||
|
banner_title: "{{ accent }}"
|
||||||
|
banner_accent: "{{ accent }}"
|
||||||
|
banner_dim: "{{ dark_foreground }}"
|
||||||
|
banner_text: "{{ foreground }}"
|
||||||
|
prompt: "{{ bright_foreground }}"
|
||||||
|
input_rule: "{{ muted }}"
|
||||||
|
response_border: "{{ accent }}"
|
||||||
|
shell_dollar: "{{ blue }}"
|
||||||
|
selection_bg: "{{ selection }}"
|
||||||
|
session_label: "{{ accent }}"
|
||||||
|
session_border: "{{ muted }}"
|
||||||
|
status_bar_bg: "{{ dark_background }}"
|
||||||
|
status_bar_text: "{{ foreground }}"
|
||||||
|
status_bar_strong: "{{ accent }}"
|
||||||
|
status_bar_dim: "{{ dark_foreground }}"
|
||||||
|
status_bar_good: "{{ green }}"
|
||||||
|
status_bar_warn: "{{ yellow }}"
|
||||||
|
status_bar_bad: "{{ red }}"
|
||||||
|
status_bar_critical: "{{ bright_red }}"
|
||||||
|
voice_status_bg: "{{ dark_background }}"
|
||||||
|
completion_menu_bg: "{{ lighter_background }}"
|
||||||
|
completion_menu_current_bg: "{{ selection }}"
|
||||||
|
completion_menu_meta_bg: "{{ lighter_background }}"
|
||||||
|
completion_menu_meta_current_bg: "{{ selection }}"
|
||||||
|
diff_added: "{{ mix background green 15% }}"
|
||||||
|
diff_removed: "{{ mix background red 15% }}"
|
||||||
|
diff_added_word: "{{ green }}"
|
||||||
|
diff_removed_word: "{{ red }}"
|
||||||
|
syntax_string: "{{ green }}"
|
||||||
|
syntax_number: "{{ yellow }}"
|
||||||
|
syntax_keyword: "{{ magenta }}"
|
||||||
|
syntax_comment: "{{ muted }}"
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "Omarchy",
|
||||||
|
"appearance": "{{ mode }}",
|
||||||
|
"canvas": "{{ background }}",
|
||||||
|
"accent": "{{ accent }}",
|
||||||
|
"colors": {
|
||||||
|
"chrome": "{{ background }}",
|
||||||
|
"toolbar": "{{ background }}",
|
||||||
|
"toolbarForeground": "{{ foreground }}",
|
||||||
|
"toolbarBorder": "{{ muted }}",
|
||||||
|
|
||||||
|
"text": "{{ foreground }}",
|
||||||
|
"border": "{{ muted }}",
|
||||||
|
"focus": "{{ accent }}",
|
||||||
|
|
||||||
|
"sidebar": "{{ dark_background }}",
|
||||||
|
"sidebarForeground": "{{ foreground }}",
|
||||||
|
"sidebarBorder": "{{ muted }}",
|
||||||
|
"sidebarRowHover": "{{ mix dark_background foreground 6% }}",
|
||||||
|
"sidebarRowActive": "{{ mix dark_background accent 18% }}",
|
||||||
|
"sidebarRowSelected": "{{ selection }}",
|
||||||
|
|
||||||
|
"error": "{{ mix red foreground 35% }}",
|
||||||
|
"warning": "{{ mix yellow foreground 35% }}",
|
||||||
|
|
||||||
|
"codeBackground": "{{ dark_background }}",
|
||||||
|
"codeForeground": "{{ foreground }}",
|
||||||
|
|
||||||
|
"terminalBackground": "{{ background }}",
|
||||||
|
"terminalForeground": "{{ foreground }}",
|
||||||
|
"terminalCursor": "{{ bright_foreground }}",
|
||||||
|
"terminalSelection": "{{ selection }}",
|
||||||
|
"terminalScrollbar": "{{ muted }}",
|
||||||
|
"terminalScrollbarHover": "{{ dark_foreground }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-3
@@ -72,6 +72,7 @@ version ──► omarchy /usr/share/omarchy
|
|||||||
config/** ──► omarchy-settings /etc/skel/.config/** (seeds new users)
|
config/** ──► omarchy-settings /etc/skel/.config/** (seeds new users)
|
||||||
/usr/share/omarchy/config/** (resync source)
|
/usr/share/omarchy/config/** (resync source)
|
||||||
etc/fastfetch/config.jsonc ──► omarchy-settings /etc/fastfetch/config.jsonc
|
etc/fastfetch/config.jsonc ──► omarchy-settings /etc/fastfetch/config.jsonc
|
||||||
|
etc/xdg/kitty/kitty.conf ──► omarchy-settings /etc/xdg/kitty/kitty.conf
|
||||||
|
|
||||||
applications/*.desktop ──► omarchy-settings /etc/skel/.local/share/applications/
|
applications/*.desktop ──► omarchy-settings /etc/skel/.local/share/applications/
|
||||||
/usr/share/omarchy/applications/
|
/usr/share/omarchy/applications/
|
||||||
@@ -123,6 +124,8 @@ logo.{txt,svg}, icon.{txt,png} ──► omarchy-settings /usr/share/omarchy
|
|||||||
/etc/skel/.config/omarchy/branding/{about,screensaver}.txt
|
/etc/skel/.config/omarchy/branding/{about,screensaver}.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The hardware-conditional `force-igpu` and `keyboard-backlight` sources also live under `default/systemd/system-sleep/`, but their setup commands publish root-owned copies only on machines that need them; they are not installed by `omarchy-settings`.
|
||||||
|
|
||||||
### Why `etc-overrides/` exists
|
### Why `etc-overrides/` exists
|
||||||
|
|
||||||
Some files under `/etc/` (`.bashrc` in `/etc/skel`, `nsswitch.conf`,
|
Some files under `/etc/` (`.bashrc` in `/etc/skel`, `nsswitch.conf`,
|
||||||
@@ -135,6 +138,14 @@ them via pacman without a file conflict. Instead they ship at
|
|||||||
Tradeoff: user edits to those files get clobbered on every `omarchy-settings`
|
Tradeoff: user edits to those files get clobbered on every `omarchy-settings`
|
||||||
upgrade. This is documented in the PKGBUILD.
|
upgrade. This is documented in the PKGBUILD.
|
||||||
|
|
||||||
|
## Locate indexing
|
||||||
|
|
||||||
|
`default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf` ships through `omarchy-settings` to `/usr/lib/systemd/system/plocate-updatedb.service.d/10-omarchy.conf`. It replaces the existing service's `ExecStart` with `updatedb --prune-bind-mounts=no --add-prunepaths=/.snapshots`, keeping Btrfs subvolume mounts searchable and excluding Snapper snapshots. The upstream service retains its timer, resource limits, and sandbox; Omarchy's existing AC-power condition still applies.
|
||||||
|
|
||||||
|
`/etc/updatedb.conf` remains owned by plocate and is never rewritten by Omarchy. The command-line options override bind-mount pruning and add to the administrator's existing path exclusions. Installer and AUR package refreshes pass the same options directly because installation may run without systemd and an explicitly requested refresh should work on battery.
|
||||||
|
|
||||||
|
Arch's systemd package hook reloads units when the vendor drop-in is installed or upgraded. The settings package containing the drop-in must ship alongside the runtime package that removes the old configuration helper and migration. Pacman removes those retired files; no new state migration is needed. A running indexer finishes with its original options, and subsequent service starts use the drop-in. For an immediate local test after installing the packages, restart `plocate-updatedb.service` while connected to AC power.
|
||||||
|
|
||||||
## Env bootstrap (`default/bash/env-bootstrap`)
|
## Env bootstrap (`default/bash/env-bootstrap`)
|
||||||
|
|
||||||
Single source of truth for `OMARCHY_PATH` and dev-link-aware `PATH`. It:
|
Single source of truth for `OMARCHY_PATH` and dev-link-aware `PATH`. It:
|
||||||
@@ -177,9 +188,7 @@ Runs once per user. It does **not** copy `~/.config/**`, `~/.bashrc`,
|
|||||||
`flags.lua`, or the nautilus extensions — `/etc/skel` already seeded those.
|
`flags.lua`, or the nautilus extensions — `/etc/skel` already seeded those.
|
||||||
It only does the things `/etc/skel` can't:
|
It only does the things `/etc/skel` can't:
|
||||||
|
|
||||||
- Skill symlinks `~/.{agents,claude,codex,pi/agent}/skills/omarchy` →
|
- Skill symlinks into `~/.agents/skills/<name>`, `~/.claude/skills/<name>`, `~/.codex/skills/<name>`, `~/.pi/agent/skills/<name>`, `~/.hermes/skills/<name>`, and each existing `~/.hermes/profiles/*/skills/<name>` → `$OMARCHY_PATH/default/agents/skills/<name>`, looping over every skill directory there (currently `omarchy` and `diagnose-crash`) so new skills need no edit. Symlinks (not copies) so `omarchy dev link` against a dev checkout repoints them correctly. Hermes profile dirs are only linked when they already exist — provision does not create Hermes profiles.
|
||||||
`$OMARCHY_PATH/default/agents/skills/omarchy`. Symlinks (not copies) so
|
|
||||||
`omarchy dev link` against a dev checkout repoints them correctly.
|
|
||||||
- `xdg-user-dirs-update` (Templates/Public/Desktop folded back into `$HOME`)
|
- `xdg-user-dirs-update` (Templates/Public/Desktop folded back into `$HOME`)
|
||||||
and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion).
|
and `~/.config/gtk-3.0/bookmarks` (needs `$HOME` expansion).
|
||||||
- Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT`
|
- Hyprland's package-owned default input reads `XKBLAYOUT` / `XKBVARIANT`
|
||||||
@@ -321,3 +330,9 @@ return to the packaged default.
|
|||||||
| New stock theme | `themes/<name>/` (+ matching templates under `default/themed/` if they need theme colors) |
|
| New stock theme | `themes/<name>/` (+ matching templates under `default/themed/` if they need theme colors) |
|
||||||
| User-installed theme | `~/.config/omarchy/themes/<name>/` |
|
| User-installed theme | `~/.config/omarchy/themes/<name>/` |
|
||||||
| Generated current theme/background state | `~/.local/state/omarchy/current/` |
|
| Generated current theme/background state | `~/.local/state/omarchy/current/` |
|
||||||
|
|
||||||
|
## Kitty defaults and user overrides
|
||||||
|
|
||||||
|
Kitty loads `/etc/xdg/kitty/kitty.conf` before `~/.config/kitty/kitty.conf`. The `omarchy-settings` package owns the system file; the user template contains only the active theme include and commented examples for personal overrides. Keeping the theme include in the user file lets users remove it without changing the packaged defaults. Individual inherited keybindings can be unmapped with an empty `map <shortcut>` directive, or all inherited bindings can be cleared with `clear_all_shortcuts yes`.
|
||||||
|
|
||||||
|
The system default uses `allow_remote_control socket-only` so Omarchy can query the active terminal directory over its Unix socket while Kitty rejects remote-control requests arriving through terminal output. Changing this setting requires restarting Kitty. The migration refreshes the exact previous stock config with a backup; customized configs retain their settings and ordering, with only explicit unrestricted `yes`, `y`, or `true` remote-control settings commented out.
|
||||||
@@ -124,6 +124,7 @@ New migration format:
|
|||||||
- Start with an `echo` describing what the migration does.
|
- Start with an `echo` describing what the migration does.
|
||||||
- Use `$OMARCHY_PATH` to reference the Omarchy directory.
|
- Use `$OMARCHY_PATH` to reference the Omarchy directory.
|
||||||
- Be idempotent. Check existing state before changing it.
|
- Be idempotent. Check existing state before changing it.
|
||||||
|
- Migrations are strictly ordered and synchronous. A migration that cannot finish must exit non-zero, remain pending, and stop the queue; never mark later migrations complete against state an earlier migration has not established.
|
||||||
- Use helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`,
|
- Use helper commands such as `omarchy-cmd-present`, `omarchy-cmd-missing`,
|
||||||
`omarchy-pkg-add`, `omarchy-pkg-drop`, `omarchy-pkg-present`, and
|
`omarchy-pkg-add`, `omarchy-pkg-drop`, `omarchy-pkg-present`, and
|
||||||
`omarchy-pkg-missing` when appropriate.
|
`omarchy-pkg-missing` when appropriate.
|
||||||
@@ -163,3 +164,5 @@ omarchy-migrate
|
|||||||
Omarchy 4.0 is upgraded through `bin/omarchy-upgrade-to-quattro`, not through the
|
Omarchy 4.0 is upgraded through `bin/omarchy-upgrade-to-quattro`, not through the
|
||||||
normal migration runner. Do not add compatibility migrations for old installer
|
normal migration runner. Do not add compatibility migrations for old installer
|
||||||
layouts; put pre-4 package-layout transition work in the upgrade command instead.
|
layouts; put pre-4 package-layout transition work in the upgrade command instead.
|
||||||
|
|
||||||
|
Clearing a privileged file that a retired installer left on disk is the exception, and belongs in a migration whether or not that installer was part of a package layout transition. The upgrade command only runs on a machine still making the 3 to 4 crossing, so anything put there never reaches an install that crossed already, and it never runs at all for an installer that was retired on its own — while the file the installer wrote is still sitting on those machines. The upgrade command finishes by running `omarchy-migrate` (`run_post_upgrade_migrations`), so one migration reaches every population; a copy in the upgrade command would only be a second copy of the same predicate to keep correct. Such a migration must name the defect it clears and match what the old installer actually produced before deleting it. Leave safe administrator-authored files alone; if one still contains the vulnerable privileged action, preserve it under an inactive name rather than discarding custom content or leaving the action executable. A user config that depends on the same retired compatibility path may be repaired in that migration when doing so eliminates an overlapping migration, but only by matching and replacing the exact legacy path while preserving the rest of the file.
|
||||||
@@ -34,9 +34,11 @@ first call.
|
|||||||
|
|
||||||
Only one full bar option is active at a time. The built-in `omarchy.bar` is
|
Only one full bar option is active at a time. The built-in `omarchy.bar` is
|
||||||
used when `bar.id` is omitted or when a selected third-party bar cannot load.
|
used when `bar.id` is omitted or when a selected third-party bar cannot load.
|
||||||
Panels, overlays, and menus are loaded when summoned. Plugins can set
|
Panels, overlays, and menus are loaded when summoned. Plugins can set the top-level manifest key `keepLoaded: true` to survive between summons, and to keep a service mounted across plugin hot-reload (so `omarchy.lock` is not destroyed while Hyprland still holds the session lock). First-party services are loaded at startup.
|
||||||
`keepLoaded: true` to survive between summons. First-party services are
|
|
||||||
loaded at startup.
|
Entry points are QML `Item`s. Panel, overlay, and menu entry points expose `open(payloadJson)` and `close()` for summon/hide; on load the host injects `omarchyPath`, `shell`, `manifest`, and the registries (`pluginRegistry` / `barWidgetRegistry`) as properties. Built-in plugins receive the trusted host objects. Third-party plugins receive capability-scoped facades instead: ordinary plugins may look up and control only their own service and lifecycle, built-in clones retain narrow source-specific configuration and UI compatibility, menu plugins receive an application-library facade, and plugins can read detached scalar bar state. A full-bar plugin additionally receives detached bar configuration and widget-catalog snapshots, narrow proxies for the non-authentication services used by built-in bar widgets, and lifecycle control over configured non-authentication UI plugins. Authentication capabilities are stamped from trusted first-party manifests, authentication services are kept out of the host's public service map and QML object tree, and third-party registry/configuration snapshots can be changed only locally without mutating host state. The facades are API boundaries, not same-process QML sandboxes: a visual widget shares the host bar's scene and can walk its parent hierarchy to ordinary host objects. Sensitive state must not rely on the facade alone for isolation.
|
||||||
|
|
||||||
|
A third-party replacement bar can render registered widget components, but widgets it hosts receive a service-less entry facade. Allowing the bar to manufacture an own-service facade for an arbitrary widget would also let it retrieve that plugin's live service object. Service-backed third-party widgets therefore retain their full integration only under the trusted built-in bar; a replacement bar may still provide their target-scoped lifecycle and settings operations.
|
||||||
|
|
||||||
Full schema: [`shell/services/PluginRegistry.qml`](../shell/services/PluginRegistry.qml).
|
Full schema: [`shell/services/PluginRegistry.qml`](../shell/services/PluginRegistry.qml).
|
||||||
|
|
||||||
@@ -73,11 +75,7 @@ one replaces the active bar, and it is therefore never offered under Disable.
|
|||||||
Bar widgets may set `barWidget.defaultSection` to `left`, `center`, or `right`;
|
Bar widgets may set `barWidget.defaultSection` to `left`, `center`, or `right`;
|
||||||
widgets that omit it default to `center`.
|
widgets that omit it default to `center`.
|
||||||
|
|
||||||
Plugins run as **unsandboxed code** inside `omarchy-shell`. Adding warns you
|
Plugins run as **unsandboxed code** inside `omarchy-shell`. Adding warns you before cloning, plugins land disabled so you can review the code before `omarchy plugin enable`, and updates show a diff before touching anything. Commands confirm in a terminal even when given arguments; without one they refuse rather than guess. Add `--yes` to skip every prompt (the path for scripts and agents). The scoped interfaces remove direct access to authentication services and avoid handing generic cross-plugin service factories to replacement bars, but visual plugins can still traverse ordinary objects in their shared QML scene. Plugin code also has the same user-level file and process access as the shell.
|
||||||
before cloning, plugins land disabled so you can review the code before
|
|
||||||
`omarchy plugin enable`, and updates show a diff before touching anything.
|
|
||||||
Commands prompt when run bare in a terminal and run unattended when given
|
|
||||||
arguments — add `--yes` to skip every prompt (the path for scripts and agents).
|
|
||||||
|
|
||||||
You can still install by hand: drop a plugin into
|
You can still install by hand: drop a plugin into
|
||||||
`~/.config/omarchy/plugins/<id>/`, run `omarchy-shell shell rescanPlugins`, then
|
`~/.config/omarchy/plugins/<id>/`, run `omarchy-shell shell rescanPlugins`, then
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
# Omarchy override of cups-browsed's shipped config. The only behavioural
|
# Keep state away from /var/cache/cups, which is writable by the account CUPS
|
||||||
# change vs the upstream default (all-commented) is enabling auto-registration
|
# uses for print filters. cups-browsed is the only writer to this directory.
|
||||||
# of remote IPP printers discovered via Avahi/mDNS.
|
CacheDir /var/cache/cups-browsed
|
||||||
CreateRemotePrinters Yes
|
|
||||||
|
# Auto-create queues only for modern driverless IPP printers. Remote queues
|
||||||
|
# exported by another CUPS server can still be added manually when needed.
|
||||||
|
CreateIPPPrinterQueues Driverless
|
||||||
|
CreateRemoteCUPSPrinterQueues No
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#
|
||||||
|
# File/directory/user/group configuration file for the CUPS scheduler.
|
||||||
|
# See "man cups-files.conf" for a complete description of this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
# List of events that are considered fatal errors for the scheduler...
|
||||||
|
#FatalErrors config
|
||||||
|
|
||||||
|
# Strip domain in local username?
|
||||||
|
#StripUserDomain No
|
||||||
|
|
||||||
|
# Do we call fsync() after writing configuration or status files?
|
||||||
|
#SyncOnClose No
|
||||||
|
|
||||||
|
# Default user and group for filters/backends/helper programs; this cannot be
|
||||||
|
# any user or group that resolves to ID 0 for security reasons...
|
||||||
|
User 209
|
||||||
|
Group 209
|
||||||
|
|
||||||
|
# Administrator user group, used to match @SYSTEM in cupsd.conf policy rules...
|
||||||
|
# This cannot contain the Group value for security reasons...
|
||||||
|
SystemGroup cups-browsed sys root
|
||||||
|
|
||||||
|
|
||||||
|
# Are Unix domain socket peer credentials used for authorization?
|
||||||
|
PeerCred on
|
||||||
|
|
||||||
|
# User that is substituted for unauthenticated (remote) root accesses...
|
||||||
|
#RemoteRoot remroot
|
||||||
|
|
||||||
|
# Do we allow file: device URIs other than to /dev/null?
|
||||||
|
#FileDevice No
|
||||||
|
|
||||||
|
# Permissions for configuration and log files...
|
||||||
|
#ConfigFilePerm 0640
|
||||||
|
#LogFilePerm 0644
|
||||||
|
|
||||||
|
# Location of the file logging all access to the scheduler; may be the name
|
||||||
|
# "syslog". If not an absolute path, the value of ServerRoot is used as the
|
||||||
|
# root directory. Also see the "AccessLogLevel" directive in cupsd.conf.
|
||||||
|
AccessLog /var/log/cups/access_log
|
||||||
|
|
||||||
|
# Location of cache files used by the scheduler...
|
||||||
|
#CacheDir /var/cache/cups
|
||||||
|
|
||||||
|
# Location of data files used by the scheduler...
|
||||||
|
#DataDir /usr/share/cups
|
||||||
|
|
||||||
|
# Location of the static web content served by the scheduler...
|
||||||
|
#DocumentRoot /usr/share/cups/doc
|
||||||
|
|
||||||
|
# Location of the file logging all messages produced by the scheduler and any
|
||||||
|
# helper programs; may be the name "syslog". If not an absolute path, the value
|
||||||
|
# of ServerRoot is used as the root directory. Also see the "LogLevel"
|
||||||
|
# directive in cupsd.conf.
|
||||||
|
ErrorLog /var/log/cups/error_log
|
||||||
|
|
||||||
|
# Location of the file logging all pages printed by the scheduler and any
|
||||||
|
# helper programs; may be the name "syslog". If not an absolute path, the value
|
||||||
|
# of ServerRoot is used as the root directory. Also see the "PageLogFormat"
|
||||||
|
# directive in cupsd.conf.
|
||||||
|
PageLog /var/log/cups/page_log
|
||||||
|
|
||||||
|
# Location of the file listing all of the local printers...
|
||||||
|
#Printcap /etc/printcap
|
||||||
|
|
||||||
|
# Format of the Printcap file...
|
||||||
|
#PrintcapFormat bsd
|
||||||
|
#PrintcapFormat plist
|
||||||
|
#PrintcapFormat solaris
|
||||||
|
|
||||||
|
# Location of all spool files...
|
||||||
|
#RequestRoot /var/spool/cups
|
||||||
|
|
||||||
|
# Location of helper programs...
|
||||||
|
#ServerBin /usr/lib/cups
|
||||||
|
|
||||||
|
# SSL/TLS keychain for the scheduler...
|
||||||
|
#ServerKeychain ssl
|
||||||
|
|
||||||
|
# Location of other configuration files...
|
||||||
|
#ServerRoot /etc/cups
|
||||||
|
|
||||||
|
# Location of scheduler state files...
|
||||||
|
#StateDir /run/cups
|
||||||
|
|
||||||
|
# Location of scheduler/helper temporary files. This directory is emptied on
|
||||||
|
# scheduler startup and cannot be one of the standard (public) temporary
|
||||||
|
# directory locations for security reasons...
|
||||||
|
#TempDir /var/spool/cups/tmp
|
||||||
@@ -15,7 +15,8 @@ ENABLE_LIMINE_FALLBACK=yes
|
|||||||
# Find and add other bootloaders
|
# Find and add other bootloaders
|
||||||
FIND_BOOTLOADERS=yes
|
FIND_BOOTLOADERS=yes
|
||||||
|
|
||||||
BOOT_ORDER="*, *fallback, Snapshots"
|
# Keep T2 Macs on their specialized kernel; prefer linux-omarchy elsewhere.
|
||||||
|
BOOT_ORDER="linux-t2, linux-omarchy, linux-omarchy-*, *, *fallback, Snapshots"
|
||||||
|
|
||||||
# Snapper is configured with NUMBER_LIMIT="5" (see default/snapper/root), but
|
# Snapper is configured with NUMBER_LIMIT="5" (see default/snapper/root), but
|
||||||
# limine-snapper-sync can see the newly created sixth snapshot before cleanup
|
# limine-snapper-sync can see the newly created sixth snapshot before cleanup
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Keep Cursor optional while applying these options whenever mise installs it.
|
||||||
|
# Expose only its launcher so the bundled Node cannot shadow the user's Node.
|
||||||
|
[tool_alias]
|
||||||
|
cursor-agent = 'http:cursor-agent[bin_path=bin,postinstall=mkdir -p "$MISE_TOOL_INSTALL_PATH/bin" && ln -sfn ../dist-package/cursor-agent "$MISE_TOOL_INSTALL_PATH/bin/cursor-agent"]'
|
||||||
@@ -1 +0,0 @@
|
|||||||
%wheel ALL=(ALL) NOPASSWD: /usr/bin/asdcontrol
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Theme switching is a menu action with no terminal to carry a password prompt,
|
||||||
|
# and it repaints the browser accent on every switch, so this one write must not
|
||||||
|
# stop for a password. The argument is spelled out as six hex digits rather than
|
||||||
|
# a wildcard: the grant covers a color and nothing else, and sudoers matches a
|
||||||
|
# command's arguments exactly, so it cannot be stretched into extra ones. The
|
||||||
|
# helper revalidates the same shape, since the terminal path does not come
|
||||||
|
# through this rule.
|
||||||
|
%wheel ALL=(root) NOPASSWD: /usr/bin/omarchy-theme-set-browser-policy [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]
|
||||||
@@ -1 +1 @@
|
|||||||
%wheel ALL=(root) NOPASSWD: /usr/bin/timedatectl set-timezone *
|
%wheel ALL=(root) NOPASSWD: /usr/bin/timedatectl ^set-timezone [A-Za-z0-9_+][A-Za-z0-9_+.-]*(/[A-Za-z0-9_+][A-Za-z0-9_+.-]*)*$
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Service]
|
||||||
|
User=cups-browsed
|
||||||
|
Group=cups-browsed
|
||||||
|
CacheDirectory=cups-browsed
|
||||||
|
CacheDirectoryMode=0750
|
||||||
|
UMask=0027
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
u cups-browsed - "CUPS printer discovery" / -
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# omarchy-sudo-passwordless writes /etc/sudoers.d/99-omarchy-nopasswd-<user> and
|
||||||
|
# arms a transient systemd-run timer to remove it again. Transient units do not
|
||||||
|
# survive a reboot, so remove any remaining grant during early boot. Boot-only
|
||||||
|
# (r!) ensures a later systemd-tmpfiles --remove cannot cut a live grant short.
|
||||||
|
r! /etc/sudoers.d/99-omarchy-nopasswd-*
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Omarchy defaults. Put personal overrides in ~/.config/kitty/kitty.conf.
|
||||||
|
|
||||||
|
# Font
|
||||||
|
font_family JetBrainsMono Nerd Font
|
||||||
|
bold_italic_font auto
|
||||||
|
font_size 9.0
|
||||||
|
|
||||||
|
# Window
|
||||||
|
window_padding_width 14
|
||||||
|
hide_window_decorations yes
|
||||||
|
confirm_os_window_close 0
|
||||||
|
|
||||||
|
# Keybindings
|
||||||
|
map ctrl+insert copy_to_clipboard
|
||||||
|
map shift+insert paste_from_clipboard
|
||||||
|
# Send Shift+Enter as CSI-u so TUIs can distinguish it from Enter.
|
||||||
|
map shift+enter send_text all \e[13;2u
|
||||||
|
# Kitty legacy encoding sends Alt+Shift+Enter the same as Alt+Enter; send CSI-u so tmux can match M-S-Enter.
|
||||||
|
map alt+shift+enter send_text all \e[13;4u
|
||||||
|
|
||||||
|
# Allow local cwd lookup, but reject remote control through terminal output.
|
||||||
|
allow_remote_control socket-only
|
||||||
|
listen_on unix:${XDG_RUNTIME_DIR}/omarchy-kitty-{kitty_pid}
|
||||||
|
|
||||||
|
# Aesthetics
|
||||||
|
cursor_shape block
|
||||||
|
cursor_blink_interval 0
|
||||||
|
shell_integration no-cursor
|
||||||
|
enable_audio_bell no
|
||||||
|
|
||||||
|
# Minimal Tab bar styling
|
||||||
|
tab_bar_edge bottom
|
||||||
|
tab_bar_style powerline
|
||||||
|
tab_powerline_style slanted
|
||||||
|
tab_title_template {title}{' :{}:'.format(num_windows) if num_windows > 1 else ''}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
run_logged "$OMARCHY_INSTALL/config/theme-system.sh"
|
run_logged "$OMARCHY_INSTALL/config/theme-system.sh"
|
||||||
|
run_logged "$OMARCHY_INSTALL/config/browser-policy.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/increase-lockout-limit.sh"
|
run_logged "$OMARCHY_INSTALL/config/increase-lockout-limit.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/lockscreen-pam.sh"
|
run_logged "$OMARCHY_INSTALL/config/lockscreen-pam.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/fix-powerprofilesctl-shebang.sh"
|
run_logged "$OMARCHY_INSTALL/config/fix-powerprofilesctl-shebang.sh"
|
||||||
@@ -6,6 +7,5 @@ run_logged "$OMARCHY_INSTALL/config/ssh-command-path.sh"
|
|||||||
run_logged "$OMARCHY_INSTALL/config/ssh-keepalive.sh"
|
run_logged "$OMARCHY_INSTALL/config/ssh-keepalive.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/docker.sh"
|
run_logged "$OMARCHY_INSTALL/config/docker.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/snapper.sh"
|
run_logged "$OMARCHY_INSTALL/config/snapper.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/locate.sh"
|
|
||||||
run_logged "$OMARCHY_INSTALL/config/enable-services.sh"
|
run_logged "$OMARCHY_INSTALL/config/enable-services.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/config/firewall.sh"
|
run_logged "$OMARCHY_INSTALL/config/firewall.sh"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
source "$OMARCHY_PATH/install/helpers/browser-policy.sh"
|
||||||
|
browser_policy_setup_dir /etc/chromium/policies/managed
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
# Enable services only. Installs are followed by reboot, so don't start/reload
|
# Enable services only. Installs are followed by reboot, so don't start/reload
|
||||||
# daemons mid-install. UFW and hardware-gated services stay in their own scripts.
|
# daemons mid-install. UFW and hardware-gated services stay in their own scripts.
|
||||||
systemctl enable cups.service
|
systemctl enable cups.service
|
||||||
systemctl enable cups-browsed.service
|
|
||||||
systemctl enable avahi-daemon.service
|
systemctl enable avahi-daemon.service
|
||||||
systemctl enable linux-modules-cleanup.service
|
systemctl enable linux-modules-cleanup.service
|
||||||
systemctl enable docker.socket
|
systemctl enable docker.socket
|
||||||
systemctl enable systemd-resolved.service
|
systemctl enable systemd-resolved.service
|
||||||
systemctl enable NetworkManager.service
|
systemctl enable NetworkManager.service
|
||||||
# Don't let network-online.target (pulled in by cups-browsed) hold up
|
# Don't let network-online.target hold up graphical.target waiting for
|
||||||
# graphical.target waiting for DHCP/Wi-Fi association. Nothing in the session
|
# DHCP/Wi-Fi association. Nothing in the session needs to block on the network.
|
||||||
# needs to block on the network. Mirrors the systemd-networkd-wait-online mask
|
# Mirrors the systemd-networkd-wait-online mask in install/hardware/network.sh.
|
||||||
# in install/hardware/network.sh.
|
|
||||||
systemctl mask NetworkManager-wait-online.service
|
systemctl mask NetworkManager-wait-online.service
|
||||||
systemctl enable power-profiles-daemon.service
|
systemctl enable power-profiles-daemon.service
|
||||||
systemctl enable sddm.service
|
systemctl enable sddm.service
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
UPDATEDB_CONF_PATH="${OMARCHY_UPDATEDB_CONF_PATH:-/etc/updatedb.conf}"
|
|
||||||
|
|
||||||
echo "Configuring locate to skip Btrfs snapshots and index Btrfs subvolumes"
|
|
||||||
|
|
||||||
[[ -f $UPDATEDB_CONF_PATH ]] || exit 0
|
|
||||||
|
|
||||||
# updatedb refuses to run at all on a config that defines a variable twice, so
|
|
||||||
# every setting here is rewritten where it already stands and only appended
|
|
||||||
# when the file has no line for it.
|
|
||||||
|
|
||||||
# Btrfs subvolume mounts (like /home) look like bind mounts, so pruning
|
|
||||||
# bind mounts leaves them out of the index entirely.
|
|
||||||
if grep -qE '^[[:space:]]*PRUNE_BIND_MOUNTS[[:space:]]*=' "$UPDATEDB_CONF_PATH"; then
|
|
||||||
sed -i -E 's|^[[:space:]]*PRUNE_BIND_MOUNTS[[:space:]]*=.*|PRUNE_BIND_MOUNTS = "no"|' "$UPDATEDB_CONF_PATH"
|
|
||||||
else
|
|
||||||
printf '%s\n' 'PRUNE_BIND_MOUNTS = "no"' >>"$UPDATEDB_CONF_PATH"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Snapper snapshots are nested subvolumes reached by plain directory
|
|
||||||
# traversal, so without this updatedb indexes the system once per snapshot.
|
|
||||||
if grep -qE '^[[:space:]]*PRUNEPATHS[[:space:]]*=' "$UPDATEDB_CONF_PATH"; then
|
|
||||||
# updatedb only accepts quoted values and allows a comment after them. Read
|
|
||||||
# back what the machine already prunes and write the whole setting out again
|
|
||||||
# rather than splicing into a line of unknown shape.
|
|
||||||
pruned=$(sed -nE 's|^[[:space:]]*PRUNEPATHS[[:space:]]*=[[:space:]]*"([^"]*)".*|\1|p' "$UPDATEDB_CONF_PATH" | tail -n 1)
|
|
||||||
|
|
||||||
if [[ " $pruned " != *" /.snapshots "* ]]; then
|
|
||||||
sed -i -E "s|^[[:space:]]*PRUNEPATHS[[:space:]]*=.*|PRUNEPATHS = \"/.snapshots${pruned:+ $pruned}\"|" "$UPDATEDB_CONF_PATH"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printf '%s\n' 'PRUNEPATHS = "/.snapshots"' >>"$UPDATEDB_CONF_PATH"
|
|
||||||
fi
|
|
||||||
@@ -6,11 +6,8 @@ ln -snf /usr/share/icons/Adwaita/symbolic/actions/go-next-symbolic.svg \
|
|||||||
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
/usr/share/icons/Yaru/scalable/actions/go-next-symbolic.svg
|
||||||
gtk-update-icon-cache /usr/share/icons/Yaru &>/dev/null || true
|
gtk-update-icon-cache /usr/share/icons/Yaru &>/dev/null || true
|
||||||
|
|
||||||
# Chromium policy directory for theme
|
# Seed Chromium's first run: follow system appearance ("device") instead of dark,
|
||||||
mkdir -p /etc/chromium/policies/managed
|
# and skip the terms-of-service dialog Chromium 151 turned on by default.
|
||||||
chmod a+rw /etc/chromium/policies/managed
|
|
||||||
|
|
||||||
# Default Chromium to follow system appearance ("device") instead of dark
|
|
||||||
mkdir -p /usr/lib/chromium
|
mkdir -p /usr/lib/chromium
|
||||||
echo '{"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' > \
|
echo '{"distribution":{"require_eula":false},"browser":{"theme":{"color_scheme":0,"color_scheme2":0}}}' > \
|
||||||
/usr/lib/chromium/initial_preferences
|
/usr/lib/chromium/initial_preferences
|
||||||
@@ -4,7 +4,6 @@ run_logged "$OMARCHY_INSTALL/hardware/dell-xps-touchpad-haptics.sh"
|
|||||||
run_logged "$OMARCHY_INSTALL/hardware/surface.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/surface.sh"
|
||||||
|
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/network.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/network.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/input-group.sh"
|
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/set-wireless-regdom.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/set-wireless-regdom.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/fix-fkeys.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/fix-fkeys.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/fix-synaptic-touchpad.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/fix-synaptic-touchpad.sh"
|
||||||
@@ -15,11 +14,6 @@ run_logged "$OMARCHY_INSTALL/hardware/vulkan.sh"
|
|||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/video-acceleration.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/video-acceleration.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/lpmd.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/lpmd.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/thermald.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/thermald.sh"
|
||||||
# Swap in the Panther Lake kernel before anything pulls DKMS modules in.
|
|
||||||
# intel-ipu7-camera drags in ipu7-drivers, vision-drivers and v4l2loopback,
|
|
||||||
# and building all three against the stock kernel only to rebuild them against
|
|
||||||
# linux-ptl and tear the first set down again cost ~25s of the install.
|
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/ptl-kernel.sh"
|
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/ipu7-camera.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/ipu7-camera.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/fred.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/fred.sh"
|
||||||
run_logged "$OMARCHY_INSTALL/hardware/intel/fix-wifi7-eht.sh"
|
run_logged "$OMARCHY_INSTALL/hardware/intel/fix-wifi7-eht.sh"
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ pci_info=$(lspci -nn)
|
|||||||
|
|
||||||
if (echo "$pci_info" | grep -q "14e4:43a0" || echo "$pci_info" | grep -q "14e4:4331"); then
|
if (echo "$pci_info" | grep -q "14e4:43a0" || echo "$pci_info" | grep -q "14e4:4331"); then
|
||||||
echo "BCM4360 / BCM4331 detected"
|
echo "BCM4360 / BCM4331 detected"
|
||||||
omarchy-pkg-add broadcom-wl dkms linux-headers
|
omarchy-pkg-add broadcom-wl-dkms
|
||||||
fi
|
fi
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Install Tuxedo drivers for keyboard backlighting on Tuxedo laptops and
|
# Install Tuxedo drivers for keyboard backlighting on Tuxedo laptops and
|
||||||
# compatible devices like the Slimbook Executive (Clevo/Tuxedo chassis).
|
# compatible devices like the Slimbook Executive (Clevo/Tuxedo chassis).
|
||||||
if cat /sys/class/dmi/id/sys_vendor 2>/dev/null | grep -qi "TUXEDO\|Slimbook"; then
|
if cat /sys/class/dmi/id/sys_vendor 2>/dev/null | grep -qi "TUXEDO\|Slimbook"; then
|
||||||
omarchy-pkg-add linux-headers tuxedo-drivers-nocompatcheck-dkms
|
omarchy-pkg-add tuxedo-drivers-nocompatcheck-dkms
|
||||||
|
|
||||||
# Blacklist the legacy clevo_xsm_wmi module which conflicts with the tuxedo-drivers
|
# Blacklist the legacy clevo_xsm_wmi module which conflicts with the tuxedo-drivers
|
||||||
# clevo_wmi module. When clevo_xsm_wmi loads first, it grabs the Clevo WMI GUIDs,
|
# clevo_wmi module. When clevo_xsm_wmi loads first, it grabs the Clevo WMI GUIDs,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Install drivers for Motorcomm YT6801 ethernet adapter used by the Slimbook Executive
|
# Install drivers for Motorcomm YT6801 ethernet adapter used by the Slimbook Executive
|
||||||
if lspci | grep -i "YT6801\|Motorcomm.*Ethernet"; then
|
if lspci | grep -i "YT6801\|Motorcomm.*Ethernet"; then
|
||||||
omarchy-pkg-add linux-headers yt6801-dkms
|
omarchy-pkg-add yt6801-dkms
|
||||||
fi
|
fi
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# Give this user privileged input access for dictation tools + xbox controllers to work.
|
|
||||||
# Recorded for provisioning first-boot user creation and factory reset, granted directly
|
|
||||||
# when the install user already exists (deferred-provisioning installs create the user at
|
|
||||||
# first boot instead).
|
|
||||||
provisioning_dir="${OMARCHY_PROVISIONING_DIR:-/var/lib/omarchy/provisioning}"
|
|
||||||
mkdir -p "$provisioning_dir"
|
|
||||||
grep -qxF input "$provisioning_dir/groups" 2>/dev/null || echo input >>"$provisioning_dir/groups"
|
|
||||||
|
|
||||||
if [[ -n ${OMARCHY_INSTALL_USER:-} ]] && getent passwd "$OMARCHY_INSTALL_USER" >/dev/null; then
|
|
||||||
usermod -aG input "$OMARCHY_INSTALL_USER"
|
|
||||||
fi
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Install Panther Lake kernel for Dell XPS Panther Lake systems
|
|
||||||
# The linux-ptl kernel includes audio driver patches not yet in mainline.
|
|
||||||
|
|
||||||
if omarchy-hw-match "XPS" && omarchy-hw-intel-ptl; then
|
|
||||||
echo "Detected Dell XPS Panther Lake, installing PTL kernel..."
|
|
||||||
|
|
||||||
omarchy-pkg-add linux-ptl linux-ptl-headers
|
|
||||||
pacman -Rdd --noconfirm linux linux-headers || true
|
|
||||||
|
|
||||||
# linux-ptl doesn't provide=linux, so anything depending on linux drags the
|
|
||||||
# stock kernel back in and the boot menu grows a second, slower entry.
|
|
||||||
if pacman -Qq linux &>/dev/null; then
|
|
||||||
echo "WARNING: stock linux kernel still installed alongside linux-ptl:"
|
|
||||||
pacman -Qi linux | grep -i "required by"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p /etc/limine-entry-tool.d
|
|
||||||
# Named to sort after omarchy-defaults.conf: drop-ins are read in order and
|
|
||||||
# the last BOOT_ORDER wins, so an earlier-sorting name is a silent no-op.
|
|
||||||
rm -f /etc/limine-entry-tool.d/dell-xps-panther-lake.conf
|
|
||||||
cat > /etc/limine-entry-tool.d/zz-dell-xps-panther-lake.conf <<'EOF'
|
|
||||||
# Only show Panther Lake kernel in boot menu on Dell XPS Panther Lake
|
|
||||||
BOOT_ORDER="linux-ptl*, *fallback, Snapshots"
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
if lspci | grep -qi 'nvidia'; then
|
if lspci | grep -qi 'nvidia'; then
|
||||||
# Check which kernel is installed and set appropriate headers package
|
|
||||||
KERNEL_PACKAGE=$(pacman -Qqs '^linux(-zen|-lts|-hardened|-t2|-ptl)?$' | head -1 || true)
|
|
||||||
[[ -n $KERNEL_PACKAGE ]] && omarchy-pkg-add "$KERNEL_PACKAGE-headers"
|
|
||||||
|
|
||||||
if omarchy-hw-nvidia-gsp; then
|
if omarchy-hw-nvidia-gsp; then
|
||||||
PACKAGES=(nvidia-open-dkms nvidia-utils lib32-nvidia-utils libva-nvidia-driver)
|
PACKAGES=(nvidia-open-dkms nvidia-utils lib32-nvidia-utils libva-nvidia-driver)
|
||||||
elif omarchy-hw-nvidia-without-gsp; then
|
elif omarchy-hw-nvidia-without-gsp; then
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
as_root() {
|
||||||
|
if (( EUID == 0 )); then
|
||||||
|
"$@"
|
||||||
|
else
|
||||||
|
sudo "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
# Chromium-family machine policy is mandatory for every profile. Directories
|
||||||
|
# stay 0755 root:root; omarchy-theme-set-browser-policy is the privileged
|
||||||
|
# write for color.json.
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/as-root.sh"
|
||||||
|
|
||||||
|
BROWSER_POLICY_MANAGED_DIRS=(
|
||||||
|
/etc/chromium/policies/managed
|
||||||
|
/etc/opt/chrome/policies/managed
|
||||||
|
/etc/opt/edge/policies/managed
|
||||||
|
/etc/brave/policies/managed
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ancestors of the managed dirs, shortest first. A writable or attacker-owned
|
||||||
|
# parent can rename the leaf aside; install -d follows a planted symlink.
|
||||||
|
BROWSER_POLICY_PARENT_DIRS=(
|
||||||
|
/etc/chromium
|
||||||
|
/etc/chromium/policies
|
||||||
|
/etc/opt/chrome
|
||||||
|
/etc/opt/chrome/policies
|
||||||
|
/etc/opt/edge
|
||||||
|
/etc/opt/edge/policies
|
||||||
|
/etc/brave
|
||||||
|
/etc/brave/policies
|
||||||
|
)
|
||||||
|
|
||||||
|
BROWSER_POLICY_FIREFOX_DIRS=(
|
||||||
|
/usr/lib/firefox/distribution
|
||||||
|
/opt/zen-browser/distribution
|
||||||
|
)
|
||||||
|
|
||||||
|
BROWSER_POLICY_DEFAULT_COLOR="#1c2027"
|
||||||
|
|
||||||
|
browser_policy_purge_dir() {
|
||||||
|
local dir=$1
|
||||||
|
|
||||||
|
as_root find "$dir" -mindepth 1 -maxdepth 1 ! -user root -exec rm -rf -- {} +
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_parent_hardened() {
|
||||||
|
local dir=$1
|
||||||
|
|
||||||
|
[[ -d $dir && ! -L $dir ]] || return 1
|
||||||
|
[[ $(stat -c '%a' "$dir") == "755" ]] || return 1
|
||||||
|
[[ $(stat -c '%U' "$dir") == "root" ]] || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_dir_hardened() {
|
||||||
|
browser_policy_parent_hardened "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_parents_hardened() {
|
||||||
|
local dir=$1
|
||||||
|
local parent
|
||||||
|
|
||||||
|
for parent in "${BROWSER_POLICY_PARENT_DIRS[@]}"; do
|
||||||
|
[[ $dir == "$parent"/* ]] || continue
|
||||||
|
[[ -e $parent || -L $parent ]] || continue
|
||||||
|
browser_policy_parent_hardened "$parent" || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_setup_parent() {
|
||||||
|
local dir=$1
|
||||||
|
|
||||||
|
if [[ -L $dir || ( -e $dir && ! -d $dir ) ]]; then
|
||||||
|
as_root rm -rf -- "$dir"
|
||||||
|
fi
|
||||||
|
as_root install -d -m 0755 -o root -g root "$dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_setup_parents_for() {
|
||||||
|
local dir=$1
|
||||||
|
local parent
|
||||||
|
|
||||||
|
for parent in "${BROWSER_POLICY_PARENT_DIRS[@]}"; do
|
||||||
|
[[ $dir == "$parent"/* ]] || continue
|
||||||
|
browser_policy_setup_parent "$parent"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_setup_dir() {
|
||||||
|
local dir=$1
|
||||||
|
|
||||||
|
browser_policy_setup_parents_for "$dir"
|
||||||
|
browser_policy_setup_parent "$dir"
|
||||||
|
browser_policy_purge_dir "$dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Themes are user-installed. Accept only three 0-255 components.
|
||||||
|
browser_policy_theme_hex() {
|
||||||
|
local theme_rgb=$1
|
||||||
|
|
||||||
|
if [[ $theme_rgb =~ ^[[:space:]]*([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})[[:space:]]*,[[:space:]]*([0-9]{1,3})[[:space:]]*$ ]] &&
|
||||||
|
(( 10#${BASH_REMATCH[1]} < 256 && 10#${BASH_REMATCH[2]} < 256 && 10#${BASH_REMATCH[3]} < 256 )); then
|
||||||
|
printf '#%02x%02x%02x' "$((10#${BASH_REMATCH[1]}))" "$((10#${BASH_REMATCH[2]}))" "$((10#${BASH_REMATCH[3]}))"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "$BROWSER_POLICY_DEFAULT_COLOR"
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_install_color() {
|
||||||
|
local policy_dir=$1
|
||||||
|
local hex=$2
|
||||||
|
local dest=$policy_dir/color.json
|
||||||
|
local tmp
|
||||||
|
|
||||||
|
[[ -d $policy_dir && ! -L $policy_dir ]] || return 0
|
||||||
|
[[ $hex =~ ^#[0-9a-f]{6}$ ]] || return 1
|
||||||
|
|
||||||
|
tmp=$(mktemp) || return 1
|
||||||
|
printf '{"BrowserThemeColor": "%s", "BrowserColorScheme": "device"}\n' "$hex" >"$tmp"
|
||||||
|
|
||||||
|
if [[ -L $dest || -d $dest ]]; then
|
||||||
|
if ! rm -rf -- "$dest" 2>/dev/null; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if install -m 0644 -T "$tmp" "$dest" 2>/dev/null; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$tmp"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_firefox_policy_file_ok() {
|
||||||
|
local file=$1
|
||||||
|
local mode
|
||||||
|
local group_write
|
||||||
|
local other_write
|
||||||
|
|
||||||
|
[[ -f $file && ! -L $file ]] || return 1
|
||||||
|
[[ $(stat -c '%U' "$file") == "root" ]] || return 1
|
||||||
|
mode=$(stat -c '%a' "$file")
|
||||||
|
group_write=$((8#${mode: -2:1}))
|
||||||
|
other_write=$((8#${mode: -1}))
|
||||||
|
(( (group_write & 2) == 0 && (other_write & 2) == 0 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_firefox_hardened() {
|
||||||
|
local dir=$1
|
||||||
|
|
||||||
|
[[ -d $dir && ! -L $dir ]] || return 1
|
||||||
|
[[ $(stat -c '%a' "$dir") == "755" ]] || return 1
|
||||||
|
[[ $(stat -c '%U' "$dir") == "root" ]] || return 1
|
||||||
|
browser_policy_firefox_policy_file_ok "$dir/policies.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_install_firefox_policies() {
|
||||||
|
local distribution_dir=$1
|
||||||
|
local policies=${2:-$OMARCHY_PATH/default/firefox/policies.json}
|
||||||
|
|
||||||
|
as_root install -m 644 -o root -g root -T "$policies" "$distribution_dir/policies.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
browser_policy_setup_firefox_distribution() {
|
||||||
|
local distribution_dir=$1
|
||||||
|
local policies=${2:-$OMARCHY_PATH/default/firefox/policies.json}
|
||||||
|
|
||||||
|
browser_policy_setup_parent "$distribution_dir"
|
||||||
|
browser_policy_purge_dir "$distribution_dir"
|
||||||
|
browser_policy_install_firefox_policies "$distribution_dir" "$policies"
|
||||||
|
}
|
||||||
@@ -17,9 +17,8 @@ chromium
|
|||||||
clang
|
clang
|
||||||
cliamp
|
cliamp
|
||||||
cups
|
cups
|
||||||
cups-browsed
|
|
||||||
cups-filters
|
cups-filters
|
||||||
cups-pdf
|
cups-pk-helper
|
||||||
ddcutil
|
ddcutil
|
||||||
docker
|
docker
|
||||||
docker-buildx
|
docker-buildx
|
||||||
|
|||||||
Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user