The search extension is compiled, and the stage that compiles it checked for npm and skipped quietly when it found none. On this machine it always found one -- but only because Claude Desktop depends on nodejs and dragged a system npm in. Nowhere else would. Node moved to nvm when the shell config turned out to have been assuming it for months, and nvm is a shell function in a file only an interactive shell sources; a stage is not one. So a fresh install would have set up the launcher, printed one line about extensions not being built, and left somebody wondering why typing in it suggested nothing. The stage sources nvm before looking, and the contract pins that it does -- checking for npm is not the same as being able to find it. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
107 lines
4.4 KiB
Bash
Executable File
107 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Install Panama's searchable launcher actions as one narrow directory link.
|
|
# Keeping the runtime path stable lets Vicinae watch it while every command
|
|
# remains authored and reviewed in the Panama repository.
|
|
|
|
set -euo pipefail
|
|
|
|
panama_path="${PANAMA_PATH:-$HOME/.local/share/Panama}"
|
|
vicinae_data_dir="${VICINAE_DATA_DIR:-$HOME/.local/share/vicinae}"
|
|
source_dir="$panama_path/config/local/share/vicinae/scripts"
|
|
scripts_dir="$vicinae_data_dir/scripts"
|
|
target_dir="$scripts_dir/panama"
|
|
backup_root="$vicinae_data_dir/backups"
|
|
backup_dir="$backup_root/panama.pre-panama"
|
|
legacy_backup="$scripts_dir/panama.pre-panama"
|
|
|
|
[[ -d "$source_dir" ]] || {
|
|
printf 'Panama command source is missing: %s\n' "$source_dir" >&2
|
|
exit 1
|
|
}
|
|
|
|
mkdir -p "$scripts_dir" "$backup_root"
|
|
|
|
# Older Panama builds preserved this directory alongside searchable commands.
|
|
# Move it out before reloading so those scripts cannot appear twice.
|
|
if [[ -e "$legacy_backup" ]]; then
|
|
if [[ -e "$backup_dir" ]]; then
|
|
printf 'Refusing to move %s; backup already exists at %s\n' \
|
|
"$legacy_backup" "$backup_dir" >&2
|
|
exit 1
|
|
fi
|
|
mv "$legacy_backup" "$backup_dir"
|
|
fi
|
|
|
|
if [[ -L "$target_dir" ]]; then
|
|
rm "$target_dir"
|
|
elif [[ -e "$target_dir" ]]; then
|
|
if [[ -e "$backup_dir" ]]; then
|
|
printf 'Refusing to replace %s; backup already exists at %s\n' \
|
|
"$target_dir" "$backup_dir" >&2
|
|
exit 1
|
|
fi
|
|
mv "$target_dir" "$backup_dir"
|
|
fi
|
|
|
|
ln -s "$source_dir" "$target_dir"
|
|
|
|
# ── Extensions ───────────────────────────────────────────────────────────────
|
|
#
|
|
# Script commands are a file and a shebang, so they are linked. Extensions are
|
|
# not: they are TypeScript that has to be compiled, and `vici build` writes its
|
|
# output straight into Vicinae's data directory rather than leaving a bundle to
|
|
# link. So the source lives in this repository and the build is what installs
|
|
# it.
|
|
#
|
|
# Never fatal, and never a reason to fail a stage. A build wants npm and the
|
|
# network, and neither is guaranteed at this point in an install -- npm arrives
|
|
# with nvm earlier in install-packages, which can itself be skipped. An
|
|
# extension that did not build is a launcher missing one command, not a desktop
|
|
# that failed to install.
|
|
extensions_source="$panama_path/config/local/share/vicinae/extensions"
|
|
|
|
# npm comes from nvm, and nvm is a shell function in a file that only an
|
|
# interactive shell sources. A stage is not one, so without this npm is simply
|
|
# absent and the build below is skipped -- silently, which is the whole problem.
|
|
#
|
|
# This machine hid the bug: /usr/sbin/npm existed, but only because Claude
|
|
# Desktop depends on nodejs and dragged it in. A machine without that would have
|
|
# installed the launcher and quietly not built its search extension.
|
|
if [[ -s /etc/profile.d/nvm.sh ]] && ! command -v npm >/dev/null 2>&1; then
|
|
# nvm's script reads unset variables, which `set -u` treats as fatal.
|
|
set +u
|
|
# shellcheck source=/dev/null
|
|
source /etc/profile.d/nvm.sh
|
|
set -u
|
|
fi
|
|
|
|
if [[ -d "$extensions_source" ]] && command -v npm >/dev/null 2>&1; then
|
|
for extension in "$extensions_source"/*/; do
|
|
[[ -f "$extension/package.json" ]] || continue
|
|
name="$(basename "$extension")"
|
|
|
|
# Skip a build that would produce what is already there. `npm install`
|
|
# alone takes long enough to be worth not repeating on every re-run of
|
|
# a stage that is otherwise nearly instant.
|
|
built="$vicinae_data_dir/extensions/$name"
|
|
if [[ -d "$built" && "$extension/src" -ot "$built" ]]; then
|
|
printf 'Vicinae extension %s is already built\n' "$name"
|
|
continue
|
|
fi
|
|
|
|
printf 'Building Vicinae extension %s\n' "$name"
|
|
if ! (cd "$extension" && npm install --silent >/dev/null 2>&1 && npm run build >/dev/null 2>&1); then
|
|
printf 'Vicinae extension %s did not build; skipping\n' "$name" >&2
|
|
fi
|
|
done
|
|
elif [[ -d "$extensions_source" ]]; then
|
|
printf 'npm is not available, so Vicinae extensions were not built\n' >&2
|
|
fi
|
|
|
|
# The server also rescans periodically, but an explicit reload makes a setup
|
|
# run deterministic. If Vicinae is not active yet, its startup scan is enough.
|
|
if command -v vicinae >/dev/null 2>&1 && vicinae ping >/dev/null 2>&1; then
|
|
vicinae cmd launch core:reload-scripts >/dev/null 2>&1 || true
|
|
fi
|