478 lines
21 KiB
Bash
Executable File
478 lines
21 KiB
Bash
Executable File
#!/bin/bash
|
|
set -e
|
|
|
|
PORT=9105
|
|
HOST="localhost"
|
|
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[0;33m'; NC='\033[0m'
|
|
|
|
command_exists() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
# --- XDG PATH RESOLUTION ---
|
|
SOURCE="${BASH_SOURCE[0]}"
|
|
while [ -h "$SOURCE" ]; do
|
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
|
SOURCE="$(readlink "$SOURCE")"
|
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
done
|
|
export SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
|
|
|
export PASSEPARTOUT_CONFIG_DIR="$(realpath -m "${XDG_CONFIG_HOME:-$HOME/.config}/passepartout")"
|
|
export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$(if [ -d "$HOME/memex/projects/passepartout/lisp" ]; then realpath -m "$HOME/memex/projects/passepartout"; else realpath -m "${XDG_DATA_HOME:-$HOME/.local/share}/passepartout"; fi)}"
|
|
export PASSEPARTOUT_STATE_DIR="$(realpath -m "${XDG_STATE_HOME:-$HOME/.local/state}/passepartout")"
|
|
export PASSEPARTOUT_BIN_DIR="$(realpath -m "${XDG_BIN_HOME:-$HOME/.local/bin}")"
|
|
export PASSEPARTOUT_MEMEX_DIR="${PASSEPARTOUT_MEMEX_DIR:-$HOME/memex}"
|
|
|
|
if [ -f "$PASSEPARTOUT_CONFIG_DIR/.env" ]; then
|
|
set -a; source "$PASSEPARTOUT_CONFIG_DIR/.env"; set +a
|
|
fi
|
|
|
|
# --- DISTRO DETECTION ---
|
|
detect_distro() {
|
|
if [ -f /etc/os-release ]; then
|
|
. /etc/os-release
|
|
case "$ID" in
|
|
debian|ubuntu|linuxmint|pop|elementary|zorin) echo "debian" ;;
|
|
fedora|rhel|centos|rocky|almalinux) echo "fedora" ;;
|
|
*) echo "unknown" ;;
|
|
esac
|
|
elif command_exists apt-get; then echo "debian"
|
|
elif command_exists dnf; then echo "fedora"
|
|
else echo "unknown"; fi
|
|
}
|
|
|
|
distro_install() {
|
|
local distro=$(detect_distro); shift
|
|
case "$distro" in
|
|
debian) sudo apt-get update && sudo apt-get install -y "$@" ;;
|
|
fedora) sudo dnf install -y "$@" ;;
|
|
*) echo "Unsupported distro. Install manually: sbcl emacs git curl socat"; return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# --- DEPENDENCY CHECK ---
|
|
check_dependencies() {
|
|
local missing=()
|
|
for dep in sbcl git curl; do
|
|
if ! command_exists "$dep"; then missing+=("$dep"); fi
|
|
done
|
|
if ! command_exists emacs; then missing+=("emacs-nox"); fi
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
echo -e "${YELLOW}--- Installing missing dependencies: ${missing[*]} ---${NC}"
|
|
local distro=$(detect_distro)
|
|
case "$distro" in
|
|
debian)
|
|
sudo apt-get update -qq 2>/dev/null || true
|
|
distro_install "${missing[@]}" 2>/dev/null || true
|
|
;;
|
|
fedora)
|
|
distro_install "${missing[@]}" 2>/dev/null || true
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
# --- SETUP ---
|
|
setup_system() {
|
|
NON_INTERACTIVE=false; WITH_FIREWALL=false
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--non-interactive) NON_INTERACTIVE=true ;;
|
|
--with-firewall) WITH_FIREWALL=true ;;
|
|
esac
|
|
done
|
|
|
|
# Always deploy to XDG, not the dev directory
|
|
export PASSEPARTOUT_DATA_DIR="$(realpath -m "${XDG_DATA_HOME:-$HOME/.local/share}/passepartout")"
|
|
|
|
echo -e "${BLUE}=== Passepartout: Configure ===${NC}"
|
|
mkdir -p "$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" "$PASSEPARTOUT_STATE_DIR" "$PASSEPARTOUT_BIN_DIR"
|
|
mkdir -p "$PASSEPARTOUT_DATA_DIR/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"
|
|
|
|
check_dependencies
|
|
|
|
if [ ! -d "$HOME/quicklisp" ]; then
|
|
echo -e "${YELLOW}--- Installing Quicklisp ---${NC}"
|
|
curl -O https://beta.quicklisp.org/quicklisp.lisp
|
|
sbcl --non-interactive --load quicklisp.lisp \
|
|
--eval "(quicklisp-quickstart:install)" \
|
|
--eval "(ql-util:without-prompting (ql:add-to-init-file))"
|
|
rm quicklisp.lisp
|
|
fi
|
|
|
|
echo -e "${YELLOW}--- Deploying Engine to $PASSEPARTOUT_DATA_DIR ---${NC}"
|
|
if [ "$SCRIPT_DIR" != "$PASSEPARTOUT_DATA_DIR" ]; then
|
|
cp "$SCRIPT_DIR/passepartout.asd" "$PASSEPARTOUT_DATA_DIR/"
|
|
fi
|
|
mkdir -p "$PASSEPARTOUT_DATA_DIR/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"
|
|
export INSTALL_DIR="$PASSEPARTOUT_DATA_DIR"
|
|
|
|
# Tangle all org files into lisp/
|
|
for f in "$SCRIPT_DIR/org"/*.org; do
|
|
[ -f "$f" ] || continue
|
|
fname=$(basename "$f" .org)
|
|
echo "Tangling $fname..."
|
|
[ "$SCRIPT_DIR" != "$PASSEPARTOUT_DATA_DIR" ] && cp "$f" "$PASSEPARTOUT_DATA_DIR/org/"
|
|
(cd "$PASSEPARTOUT_DATA_DIR/org" && emacs -Q --batch \
|
|
--eval "(require 'org)" \
|
|
--eval "(setq org-confirm-babel-evaluate nil)" \
|
|
--eval "(org-babel-tangle-file \"${fname}.org\")") >/dev/null 2>&1 || true
|
|
done
|
|
# Move test files to tests/ directory
|
|
find "$PASSEPARTOUT_DATA_DIR/lisp" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 2>/dev/null || true
|
|
|
|
ln -sf "$SCRIPT_DIR/passepartout" "$PASSEPARTOUT_BIN_DIR/passepartout"
|
|
|
|
if [ "$WITH_FIREWALL" = true ]; then
|
|
case $(detect_distro) in
|
|
debian) sudo ufw allow 9105/tcp 2>/dev/null && echo "✓ UFW: port 9105 opened" || true ;;
|
|
fedora) sudo firewall-cmd --add-port=9105/tcp --permanent 2>/dev/null && sudo firewall-cmd --reload 2>/dev/null && echo "✓ firewalld: port 9105 opened" || true ;;
|
|
esac
|
|
fi
|
|
|
|
# Pre-compile core + TUI so first daemon/TUI start is fast
|
|
echo -e "${YELLOW}--- Pre-compiling core system ---${NC}"
|
|
sbcl --noinform --load "$HOME/quicklisp/setup.lisp" \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout)' \
|
|
--eval '(ql:quickload :passepartout/tui :silent t)' \
|
|
--eval '(uiop:quit)' 2>&1 | grep -v '^;\|STYLE-WARNING\|WARNING: redefining' || true
|
|
|
|
if [ "$NON_INTERACTIVE" = true ]; then
|
|
echo "Configure complete."
|
|
exit 0
|
|
fi
|
|
|
|
echo -e "${YELLOW}--- Launching Setup Wizard ---${NC}"
|
|
exec sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval '(funcall (find-symbol "SETUP-WIZARD-RUN" :passepartout))'
|
|
}
|
|
|
|
# --- DOCTOR REPAIR ---
|
|
doctor_repair() {
|
|
echo -e "${BLUE}=== Passepartout: Repair Mode ===${NC}"
|
|
check_dependencies
|
|
mkdir -p "$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" "$PASSEPARTOUT_STATE_DIR" "$PASSEPARTOUT_BIN_DIR"
|
|
mkdir -p "$PASSEPARTOUT_DATA_DIR/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"
|
|
for f in "$SCRIPT_DIR/org"/*.org; do
|
|
[ -f "$f" ] || continue
|
|
fname=$(basename "$f" .org)
|
|
echo " Checking $fname..."
|
|
if ! sbcl --non-interactive \
|
|
--eval "(load \"$PASSEPARTOUT_DATA_DIR/lisp/${fname}.lisp\")" \
|
|
--eval "(format t \"OK~%\")" 2>/dev/null | grep -q "OK"; then
|
|
echo " Re-tangling $fname.org..."
|
|
cp "$f" "$PASSEPARTOUT_DATA_DIR/org/"
|
|
(cd "$PASSEPARTOUT_DATA_DIR/org" && emacs -Q --batch \
|
|
--eval "(require 'org)" \
|
|
--eval "(setq org-confirm-babel-evaluate nil)" \
|
|
--eval "(org-babel-tangle-file \"${fname}.org\")") >/dev/null 2>&1 || true
|
|
fi
|
|
done
|
|
find "$PASSEPARTOUT_DATA_DIR/lisp" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 2>/dev/null || true
|
|
echo -e "${GREEN}--- Repair Complete ---${NC}"
|
|
}
|
|
|
|
# --- INSTALL SKILL ---
|
|
install_skill() {
|
|
local SKILL_NAME=$1
|
|
if [ -z "$SKILL_NAME" ]; then
|
|
echo "Usage: passepartout install skill <skill-name>"
|
|
echo " Installs a skill from passepartout-contrib"
|
|
echo ""
|
|
echo "Available skills:"
|
|
if [ -d "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills" ]; then
|
|
ls "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills"/*.org 2>/dev/null | xargs -I{} basename {} .org | sed 's/org-skill-//' | sort | uniq
|
|
else
|
|
echo " (clone passepartout-contrib to ~/memex/projects/ first)"
|
|
fi
|
|
exit 1
|
|
fi
|
|
local SKILL_FILE="org-skill-${SKILL_NAME}.org"
|
|
local SOURCE_DIR="$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills"
|
|
local TARGET_DIR="$PASSEPARTOUT_DATA_DIR/skills"
|
|
if [ ! -d "$SOURCE_DIR" ]; then
|
|
echo "Error: Contrib skills not found at $SOURCE_DIR"
|
|
echo "Run: git clone https://github.com/amrgharbeia/passepartout-contrib.git \$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib"
|
|
exit 1
|
|
fi
|
|
if [ ! -f "$SOURCE_DIR/$SKILL_FILE" ]; then
|
|
echo "Error: Skill '$SKILL_NAME' not found"
|
|
exit 1
|
|
fi
|
|
mkdir -p "$TARGET_DIR"
|
|
cp "$SOURCE_DIR/$SKILL_FILE" "$TARGET_DIR/"
|
|
(cd "$TARGET_DIR" && emacs -Q --batch \
|
|
--eval "(require 'org)" \
|
|
--eval "(setq org-confirm-babel-evaluate nil)" \
|
|
--eval "(org-babel-tangle-file \"$SKILL_FILE\")") >/dev/null 2>&1 || true
|
|
rm -f "$TARGET_DIR/$SKILL_FILE"
|
|
if [ -f "$TARGET_DIR/${SKILL_NAME}-tests.lisp" ]; then
|
|
mv "$TARGET_DIR/${SKILL_NAME}-tests.lisp" "$PASSEPARTOUT_DATA_DIR/tests/" 2>/dev/null || true
|
|
fi
|
|
echo "Skill '$SKILL_NAME' installed. Restart to activate."
|
|
}
|
|
|
|
# --- INSTALL SERVICE ---
|
|
install_service() {
|
|
mkdir -p "$HOME/.config/systemd/user"
|
|
cat > "$HOME/.config/systemd/user/passepartout.service" << 'SERVICEEOF'
|
|
[Unit]
|
|
Description=Passepartout Daemon
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=%h/projects/passepartout/passepartout.sh daemon
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
WorkingDirectory=%h/projects/passepartout
|
|
|
|
[Install]
|
|
WantedBy=default.target
|
|
SERVICEEOF
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable passepartout.service
|
|
systemctl --user start passepartout.service
|
|
echo -e "${GREEN}✓ passepartout.service installed and started${NC}"
|
|
echo " Status: systemctl --user status passepartout.service"
|
|
echo " Logs: journalctl --user -u passepartout.service -f"
|
|
}
|
|
|
|
uninstall_service() {
|
|
systemctl --user stop passepartout.service 2>/dev/null || true
|
|
systemctl --user disable passepartout.service 2>/dev/null || true
|
|
rm -f "$HOME/.config/systemd/user/passepartout.service"
|
|
systemctl --user daemon-reload
|
|
echo -e "${GREEN}✓ passepartout.service removed${NC}"
|
|
}
|
|
|
|
# --- BACKUP ---
|
|
backup() {
|
|
local dest="${1:-$HOME/passepartout-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
|
|
if [ -f "$dest" ]; then echo "Error: $dest exists"; exit 1; fi
|
|
echo "Backing up to $dest..."
|
|
tar -czf "$dest" \
|
|
"$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" \
|
|
"$PASSEPARTOUT_MEMEX_DIR/gtd.org" "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout" \
|
|
2>/dev/null || true
|
|
echo -e "${GREEN}✓ Backed up to $dest${NC}"
|
|
}
|
|
|
|
restore() {
|
|
local src="$1"
|
|
if [ -z "$src" ] || [ ! -f "$src" ]; then
|
|
echo "Usage: passepartout restore <backup-file>"
|
|
exit 1
|
|
fi
|
|
echo "Restoring from $src..."
|
|
tar -xzf "$src" -C /
|
|
echo -e "${GREEN}✓ Restored. Run 'passepartout doctor' to verify.${NC}"
|
|
}
|
|
|
|
# --- HELP ---
|
|
help() {
|
|
echo ""
|
|
echo "Passepartout — Your Autonomous, Plain-Text Life Assistant"
|
|
echo ""
|
|
echo "Usage: passepartout <command> [options]"
|
|
echo ""
|
|
echo "System:"
|
|
echo " configure [--non-interactive] [--with-firewall] Install or reconfigure the system"
|
|
echo " setup Alias for configure"
|
|
echo " doctor [--fix] [--watch] System health check"
|
|
echo ""
|
|
echo "Running:"
|
|
echo " daemon Start background daemon"
|
|
echo " tui Launch terminal UI"
|
|
echo " gateway {link|unlink|list} <platform> <token> Manage chat gateways"
|
|
echo ""
|
|
echo "Skills:"
|
|
echo " install skill <name> Install a skill from contrib"
|
|
echo " install service Install systemd service (auto-start)"
|
|
echo " uninstall service Remove systemd service"
|
|
echo ""
|
|
echo "Data:"
|
|
echo " backup [path] Backup config, data, memex"
|
|
echo " restore <path> Restore from a backup"
|
|
echo ""
|
|
echo "Quick start:"
|
|
echo " curl -fsSL https://raw.githubusercontent.com/amrgharbeia/passepartout/main/passepartout.sh | bash -s configure"
|
|
echo ""
|
|
}
|
|
|
|
# --- COMMAND ROUTER ---
|
|
COMMAND=$1; [ -z "$COMMAND" ] && COMMAND="help"
|
|
shift || true
|
|
|
|
case "$COMMAND" in
|
|
configure|setup)
|
|
check_dependencies
|
|
if [ "$1" = "--add-provider" ]; then
|
|
sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval '(funcall (find-symbol "SETUP-PROVIDER-ADD" :passepartout))'
|
|
elif [ "$1" = "--link" ]; then
|
|
exec "$0" gateway link "$2" "$3"
|
|
else
|
|
setup_system "$@"
|
|
fi
|
|
;;
|
|
doctor)
|
|
check_dependencies
|
|
if [ "$1" = "--watch" ]; then
|
|
while true; do
|
|
echo "--- $(date '+%Y-%m-%d %H:%M:%S') ---"
|
|
sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval '(funcall (find-symbol "DIAGNOSTICS-RUN-ALL" :passepartout))' 2>&1 | grep -E "(HEALTH|OK|FAIL|WARN|SYSTEM|===)" || true
|
|
sleep 60
|
|
done
|
|
elif [ "$1" = "--fix" ]; then
|
|
if [ ! -f "$PASSEPARTOUT_DATA_DIR/harness/package.lisp" ] || [ ! -f "$PASSEPARTOUT_DATA_DIR/harness/skills.lisp" ]; then
|
|
setup_system "$@"
|
|
else
|
|
doctor_repair
|
|
fi
|
|
else
|
|
exec sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval '(funcall (find-symbol "DIAGNOSTICS-MAIN" :passepartout))'
|
|
fi
|
|
;;
|
|
daemon)
|
|
check_dependencies
|
|
export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}"
|
|
export MEMEX_DIR="${PASSEPARTOUT_MEMEX_DIR:-$HOME/memex}"
|
|
echo "Starting daemon (data dir: $PASSEPARTOUT_DATA_DIR)..."
|
|
nohup sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval '(ql:quickload :passepartout)' \
|
|
--eval "(handler-case (load (format nil \"~alisp/neuro-router.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\"))) (error () nil))" \
|
|
--eval "(handler-case (load (format nil \"~alisp/embedding-backends.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\"))) (error () nil))" \
|
|
--eval "(handler-case (load (format nil \"~alisp/neuro-explorer.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\"))) (error () nil))" \
|
|
--eval '(funcall (find-symbol "MAIN" :passepartout))' \
|
|
> "$PASSEPARTOUT_STATE_DIR/daemon.log" 2>&1 &
|
|
echo "Waiting for port 9105..."
|
|
for i in $(seq 1 120); do
|
|
if ss -tln 2>/dev/null | grep -q 9105 || netstat -tln 2>/dev/null | grep -q 9105; then
|
|
echo "✓ Daemon ready on port 9105"; exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "✗ Daemon failed to start. Check $PASSEPARTOUT_STATE_DIR/daemon.log"; exit 1
|
|
;;
|
|
tui)
|
|
check_dependencies
|
|
export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}"
|
|
if ! ss -tln 2>/dev/null | grep -q 9105 && ! netstat -tln 2>/dev/null | grep -q 9105; then
|
|
echo "Starting daemon first..."
|
|
$0 daemon
|
|
fi
|
|
# Build TUI load script with proper paths
|
|
cat > /tmp/tui-load.lisp << LISPEOF
|
|
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
|
|
(declaim (optimize (debug 3) (speed 0) (safety 3)))
|
|
(push (truename "$PASSEPARTOUT_DATA_DIR/") asdf:*central-registry*)
|
|
(ql:quickload :cl-tty :silent t)
|
|
(ql:quickload :passepartout :silent t)
|
|
(let ((dir (pathname (format nil "~a/lisp/" (truename "$PASSEPARTOUT_DATA_DIR")))))
|
|
(dolist (f '("channel-tui-state" "channel-tui-view" "channel-tui-main"))
|
|
(let* ((src (merge-pathnames (format nil "~a.lisp" f) dir))
|
|
(fasl (merge-pathnames (format nil "~a.fasl" f) dir)))
|
|
(when (or (not (probe-file fasl))
|
|
(< (file-write-date fasl) (file-write-date src)))
|
|
(compile-file src :output-file fasl :verbose nil :print nil))
|
|
(load fasl :verbose nil :print nil))))
|
|
(in-package :passepartout)
|
|
(handler-bind ((error (lambda (c) (ignore-errors
|
|
(with-open-file (f (merge-pathnames ".cache/passepartout/tui-crash.log" (user-homedir-pathname))
|
|
:direction :output :if-exists :supersede :if-does-not-exist :create)
|
|
(format f "CRASH: ~a~%~%" c) (sb-debug:print-backtrace :count 50 :stream f) (finish-output f)))
|
|
(format t "~%=== TUI CRASH ===~%CRASH: ~a~%" c)
|
|
(format t "Full backtrace saved to ~~/.cache/passepartout/tui-crash.log~%")
|
|
(sleep 3) (finish-output) (uiop:quit 1))))
|
|
(passepartout.channel-tui:tui-main))
|
|
LISPEOF
|
|
# Capture terminal dimensions in non-standard env vars
|
|
# (SBCL strips COLUMNS/LINES but leaves MY_* alone).
|
|
ts=$(stty size 2>/dev/null)
|
|
export MY_TERM_ROWS="${ts%% *}" MY_TERM_COLS="${ts##* }"
|
|
# Clear stale cl-tty cache to ensure latest backend-size fixes
|
|
find ~/.cache/common-lisp -name "*.fasl" -path "*cl-tty*" -delete 2>/dev/null
|
|
exec sbcl --noinform --load /tmp/tui-load.lisp
|
|
;;
|
|
gateway)
|
|
SUBCMD=$1; PLATFORM=$2; TOKEN=$3
|
|
check_dependencies
|
|
case "$SUBCMD" in
|
|
list)
|
|
exec sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval '(funcall (find-symbol "MESSAGING-LIST-PRINT" (find-package "OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING")))'
|
|
;;
|
|
link)
|
|
[ -z "$PLATFORM" ] || [ -z "$TOKEN" ] && echo "Usage: passepartout gateway link <platform> <token>" && exit 1
|
|
exec sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval "(funcall (find-symbol \"MESSAGING-LINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING\")) \"$PLATFORM\" \"$TOKEN\")"
|
|
;;
|
|
unlink)
|
|
[ -z "$PLATFORM" ] && echo "Usage: passepartout gateway unlink <platform>" && exit 1
|
|
exec sbcl --non-interactive \
|
|
--eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
|
|
--eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
|
|
--eval '(ql:quickload :passepartout :force t)' \
|
|
--eval '(passepartout:skill-initialize-all)' \
|
|
--eval "(funcall (find-symbol \"MESSAGING-UNLINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING\")) \"$PLATFORM\")"
|
|
;;
|
|
*) echo "Usage: passepartout gateway {list|link|unlink}"; exit 1 ;;
|
|
esac
|
|
;;
|
|
install)
|
|
case "$1" in
|
|
skill) shift; install_skill "$@" ;;
|
|
service) install_service ;;
|
|
*) echo "Usage: passepartout install {skill|service}" >&2; exit 1 ;;
|
|
esac
|
|
;;
|
|
uninstall)
|
|
case "$1" in
|
|
service) uninstall_service ;;
|
|
*) echo "Usage: passepartout uninstall {service}" >&2; exit 1 ;;
|
|
esac
|
|
;;
|
|
backup)
|
|
backup "$1"
|
|
;;
|
|
restore)
|
|
restore "$1"
|
|
;;
|
|
help|--help|-h)
|
|
help
|
|
;;
|
|
*)
|
|
echo "Unknown command: $COMMAND"
|
|
help
|
|
exit 1
|
|
;;
|
|
esac
|