readme
This commit is contained in:
commit
74cd0050d8
3
Modelfile-qwen3-code-webdev
Normal file
3
Modelfile-qwen3-code-webdev
Normal file
@ -0,0 +1,3 @@
|
||||
# Alias for AnythingLLM: name requested by operator.
|
||||
# Base: Qwen3 Coder (cloud). For full local weights use: FROM qwen3-coder:latest
|
||||
FROM qwen3-coder:480b-cloud
|
||||
42
README.md
Normal file
42
README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# smart_ide — IDE orienté intention et IA locale
|
||||
|
||||
Projet d’environnement de développement où l’**inférence** repose sur **Ollama**, la **mémoire documentaire et RAG** sur **AnythingLLM**, et la bureautique métier sur **ONLYOFFICE**. Les **agents métier** existants (`ia_dev` et sous-agents) restent le noyau opératoire ; l’éditeur et l’orchestrateur les exposent via une **grammaire de commandes** plutôt que via une navigation fichiers classique.
|
||||
|
||||
## Première cible de déploiement
|
||||
|
||||
Le **premier déploiement** visé est un **poste Linux client** qui se connecte en **SSH** à un **serveur distant** hébergeant :
|
||||
|
||||
- le **socle technique IA** (Ollama, AnythingLLM, services associés) ;
|
||||
- les **dépôts** (sources, agents, procédures).
|
||||
|
||||
L’UX (ex. Lapce) et les flux utilisateur peuvent tourner sur le client ; l’exécution lourde, la mémoire projet et Git vivent **sur le serveur**. Détail : [docs/deployment-target.md](./docs/deployment-target.md).
|
||||
|
||||
## Positionnement
|
||||
|
||||
- **Pas d’explorer comme surface principale** : la navigation primaire passe par intentions, recherche, contexte, timeline, objets logiques et artefacts ; un accès brut (fichiers / arborescence) reste disponible en **mode expert / secours**, pas comme flux nominal.
|
||||
- **Machine de travail orientée opérations** plutôt qu’éditeur de fichiers : l’utilisateur exprime *ce qu’il veut faire*, *sur quel objet logique*, *avec quels droits*, *dans quel contexte projet*, *avec quelle procédure*, *avec quel agent*, *avec quel résultat attendu*.
|
||||
- **Socle éditeur envisagé : [Lapce](https://lapce.dev/)** — open source, Rust, rendu natif / GPU, positionné comme éditeur rapide et léger : base cohérente pour un noyau d’édition + agents, sans empiler l’historique complet d’un IDE classique. Choix d’architecture, pas une obligation figée.
|
||||
|
||||
## AnythingLLM et projets
|
||||
|
||||
Pour chaque **projet**, un **workspace AnythingLLM** dédié est créé (ou rattaché) : corpus, embeddings et conversations restent **isolés par projet**. Une **moulinette de synchronisation** aligne un sous-ensemble de fichiers du dépôt avec le workspace concerné afin de garder la mémoire RAG alignée avec le code et la doc utiles.
|
||||
|
||||
Voir [docs/anythingllm-workspaces.md](./docs/anythingllm-workspaces.md).
|
||||
|
||||
## Documentation
|
||||
|
||||
| Document | Contenu |
|
||||
|----------|---------|
|
||||
| [docs/README.md](./docs/README.md) | Index de la documentation technique |
|
||||
| [docs/infrastructure.md](./docs/infrastructure.md) | LAN, SSH, scripts d’accès hôte |
|
||||
| [docs/services.md](./docs/services.md) | Ollama, AnythingLLM Docker, intégration |
|
||||
| [docs/anythingllm-workspaces.md](./docs/anythingllm-workspaces.md) | Workspaces par projet, synchronisation |
|
||||
| [docs/ux-navigation-model.md](./docs/ux-navigation-model.md) | Remplacer l’explorer : intentions, risques, vues, graphe, mode expert |
|
||||
| [docs/system-architecture.md](./docs/system-architecture.md) | Couches, modules, agents, gateway, OpenShell, événements |
|
||||
| [docs/deployment-target.md](./docs/deployment-target.md) | Client Linux + SSH : serveur = socle IA + repos |
|
||||
|
||||
## Dépôt actuel (outillage)
|
||||
|
||||
Scripts d’installation et d’exploitation sur Ubuntu : SSH, sudo ciblé, AnythingLLM Docker, Ollama exposé pour Docker, modèle Ollama alias `qwen3-code-webdev`, installer Desktop AnythingLLM. Ces scripts ciblent en priorité l’**hôte serveur** qui porte le socle IA et les repos ; le **client Linux** repose surtout sur SSH et l’IDE. L’IDE complet (Lapce + orchestrateur + gateway) est **cible de conception** ; ce dépôt documente et supporte la **stack sur serveur** (Ollama + AnythingLLM) et l’accès distant.
|
||||
|
||||
**Auteur :** Équipe 4NK
|
||||
142
add-ssh-key.sh
Executable file
142
add-ssh-key.sh
Executable file
@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add SSH public key to ~/.ssh/authorized_keys on infrastructure hosts.
|
||||
#
|
||||
# Modes (pick one):
|
||||
# ADD_KEY_LOCAL=1 — you are already SSH'd on the target host (e.g. 192.168.1.164): only
|
||||
# update the current user's ~/.ssh/authorized_keys on this machine.
|
||||
# LAN_DIRECT=1 — same LAN as hosts: ssh BACKEND_USER@192.168.1.x directly (no ProxyJump,
|
||||
# no 4nk.myftp.biz). Host list includes proxy .100, backends, and .164.
|
||||
# (default) — bastion JUMP then ProxyJump to each backend (Internet / standard doc).
|
||||
#
|
||||
# The key embedded below (desk@desk) is what gets appended remotely; client auth uses your
|
||||
# existing keys (SSH_IDENTITY_FILE / agent).
|
||||
#
|
||||
# Run as the SSH user, not root: sudo uses /root/.ssh and causes Permission denied (publickey).
|
||||
#
|
||||
# Optional env:
|
||||
# BACKEND_USER=ncantu
|
||||
# JUMP=ncantu@4nk.myftp.biz # default jump host when LAN_DIRECT is unset
|
||||
# SSH_IDENTITY_FILE=~/.ssh/id_ed25519
|
||||
# SSH_VERBOSE=1
|
||||
# EXTRA_LAN_IPS="192.168.1.42 ..." # space-separated, appended when LAN_DIRECT=1
|
||||
# Usage:
|
||||
# ADD_KEY_LOCAL=1 ./add-ssh-key.sh
|
||||
# LAN_DIRECT=1 ./add-ssh-key.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDyLeCZh0tJ7rEp1sktpMlA2EaBBKBU5jNRMgboYAOsk desk@desk"
|
||||
KEY_FINGERPRINT="AAAAC3NzaC1lZDI1NTE5AAAAIDyLeCZh0tJ7rEp1sktpMlA2EaBBKBU5jNRMgboYAOsk"
|
||||
|
||||
JUMP="${JUMP:-ncantu@4nk.myftp.biz}"
|
||||
BACKEND_USER="${BACKEND_USER:-ncantu}"
|
||||
|
||||
BACKEND_IPS=(
|
||||
"192.168.1.101" # test
|
||||
"192.168.1.102" # pprod
|
||||
"192.168.1.103" # prod
|
||||
"192.168.1.104" # services
|
||||
"192.168.1.105" # bitcoin
|
||||
"192.168.1.173" # ia
|
||||
)
|
||||
|
||||
LAN_IPS=(
|
||||
"192.168.1.100" # proxy
|
||||
"${BACKEND_IPS[@]}"
|
||||
"192.168.1.164" # workstation / host 164 on LAN
|
||||
)
|
||||
|
||||
SSH_OPTS=(
|
||||
-o StrictHostKeyChecking=accept-new
|
||||
)
|
||||
if [ -n "${SSH_IDENTITY_FILE:-}" ]; then
|
||||
idf="${SSH_IDENTITY_FILE/#\~/$HOME}"
|
||||
SSH_OPTS+=(-i "$idf" -o IdentitiesOnly=yes)
|
||||
fi
|
||||
if [ -n "${SSH_VERBOSE:-}" ]; then
|
||||
SSH_OPTS+=(-v)
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
echo "Do not run this script with sudo/root: SSH will use /root/.ssh and fail with Permission denied (publickey)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
add_key_to_current_user() {
|
||||
local auth="${HOME}/.ssh/authorized_keys"
|
||||
mkdir -p "${HOME}/.ssh"
|
||||
chmod 700 "${HOME}/.ssh"
|
||||
touch "${auth}"
|
||||
chmod 600 "${auth}"
|
||||
if ! grep -qF "${KEY_FINGERPRINT}" "${auth}" 2>/dev/null; then
|
||||
printf '%s\n' "${SSH_KEY}" >> "${auth}"
|
||||
echo "Key added (local user $(whoami)@$(hostname -f 2>/dev/null || hostname))"
|
||||
else
|
||||
echo "Key already present (local user $(whoami)@$(hostname -f 2>/dev/null || hostname))"
|
||||
fi
|
||||
}
|
||||
|
||||
run_add_key_remote() {
|
||||
local -a ssh_cmd=("$@")
|
||||
"${ssh_cmd[@]}" bash -s <<EOF
|
||||
set -euo pipefail
|
||||
KEY_FINGERPRINT='${KEY_FINGERPRINT}'
|
||||
KEY_LINE='${SSH_KEY}'
|
||||
AUTH="\${HOME}/.ssh/authorized_keys"
|
||||
mkdir -p "\${HOME}/.ssh"
|
||||
chmod 700 "\${HOME}/.ssh"
|
||||
touch "\${AUTH}"
|
||||
chmod 600 "\${AUTH}"
|
||||
if ! grep -qF "\${KEY_FINGERPRINT}" "\${AUTH}" 2>/dev/null; then
|
||||
printf '%s\n' "\${KEY_LINE}" >> "\${AUTH}"
|
||||
echo "Key added"
|
||||
else
|
||||
echo "Key already present"
|
||||
fi
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ "${ADD_KEY_LOCAL:-0}" = "1" ]; then
|
||||
echo "ADD_KEY_LOCAL=1: updating authorized_keys for current user only."
|
||||
echo "Key: $SSH_KEY"
|
||||
echo "Host: $(hostname) (${USER})"
|
||||
add_key_to_current_user
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${LAN_DIRECT:-0}" = "1" ]; then
|
||||
echo "LAN_DIRECT=1: direct SSH on LAN (no ProxyJump / no bastion hostname)."
|
||||
echo "Key: $SSH_KEY"
|
||||
echo "User: ${BACKEND_USER}"
|
||||
if [ -n "${EXTRA_LAN_IPS:-}" ]; then
|
||||
# shellcheck disable=SC2206
|
||||
extra_ips=( ${EXTRA_LAN_IPS} )
|
||||
LAN_IPS+=( "${extra_ips[@]}" )
|
||||
fi
|
||||
for ip in "${LAN_IPS[@]}"; do
|
||||
echo ""
|
||||
echo "Processing: ${BACKEND_USER}@${ip}"
|
||||
run_add_key_remote ssh "${SSH_OPTS[@]}" "${BACKEND_USER}@${ip}"
|
||||
done
|
||||
echo ""
|
||||
echo "SSH key addition completed (LAN direct)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Adding SSH key to all servers (bastion + ProxyJump)..."
|
||||
echo "Key: $SSH_KEY"
|
||||
echo "Jump: $JUMP"
|
||||
echo ""
|
||||
|
||||
echo "Processing bastion (proxy): ${JUMP}"
|
||||
run_add_key_remote ssh "${SSH_OPTS[@]}" "$JUMP"
|
||||
echo ""
|
||||
|
||||
for ip in "${BACKEND_IPS[@]}"; do
|
||||
echo "Processing backend: ${BACKEND_USER}@${ip} (via ${JUMP})"
|
||||
run_add_key_remote ssh "${SSH_OPTS[@]}" -J "$JUMP" "${BACKEND_USER}@${ip}"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "SSH key addition completed for bastion and all listed backends."
|
||||
20
add-sudo-nopasswd-ncantu.sh
Executable file
20
add-sudo-nopasswd-ncantu.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add passwordless sudo for user ncantu.
|
||||
# Must be run as root (e.g. sudo ./add-sudo-nopasswd-ncantu.sh).
|
||||
# Creates /etc/sudoers.d/99-ncantu-nopasswd and validates with visudo -c.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SUDOERS_FILE="/etc/sudoers.d/99-ncantu-nopasswd"
|
||||
USER_NAME="ncantu"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Run as root: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${USER_NAME} ALL=(ALL) NOPASSWD: ALL" > "${SUDOERS_FILE}"
|
||||
chmod 440 "${SUDOERS_FILE}"
|
||||
visudo -c -f "${SUDOERS_FILE}"
|
||||
echo "Done. User ${USER_NAME} can run sudo without password."
|
||||
22
configure-ollama-for-docker.sh
Executable file
22
configure-ollama-for-docker.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Expose Ollama on all interfaces so Docker containers (AnythingLLM) can reach it via
|
||||
# host.docker.internal (--add-host=host.docker.internal:host-gateway).
|
||||
# Requires sudo. Idempotent.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Run: sudo $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p /etc/systemd/system/ollama.service.d
|
||||
cat <<'EOF' > /etc/systemd/system/ollama.service.d/override.conf
|
||||
[Service]
|
||||
Environment="OLLAMA_HOST=0.0.0.0:11434"
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl restart ollama
|
||||
echo "Ollama listens on 0.0.0.0:11434 (check: ss -tlnp | grep 11434)"
|
||||
21
docs/README.md
Normal file
21
docs/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# smart_ide — documentation
|
||||
|
||||
Operational, architectural, and UX-design notes for the local-AI IDE initiative and the host tooling in this repository.
|
||||
|
||||
| Document | Content |
|
||||
|----------|---------|
|
||||
| [../README.md](../README.md) | Project overview (French): vision, Lapce, AnythingLLM per project |
|
||||
| [deployment-target.md](./deployment-target.md) | First target: Linux client + SSH remote server (AI stack + repos) |
|
||||
| [infrastructure.md](./infrastructure.md) | Host inventory (LAN), SSH key workflow, host scripts |
|
||||
| [services.md](./services.md) | Ollama, AnythingLLM (Docker), Desktop installer, Ollama ↔ Docker |
|
||||
| [anythingllm-workspaces.md](./anythingllm-workspaces.md) | One AnythingLLM workspace per project; sync pipeline |
|
||||
| [ux-navigation-model.md](./ux-navigation-model.md) | Beyond file explorer: intentions, graph, palette, risks, expert mode |
|
||||
| [system-architecture.md](./system-architecture.md) | Layers, modules, agent gateway, OpenShell, events, Lapce |
|
||||
|
||||
**Author:** 4NK
|
||||
|
||||
**Related external docs**
|
||||
|
||||
- AnythingLLM Docker: <https://docs.anythingllm.com/installation-docker/local-docker>
|
||||
- Ollama: <https://github.com/ollama/ollama/blob/main/docs/linux.md>
|
||||
- Lapce: <https://lapce.dev/>
|
||||
16
docs/anythingllm-workspaces.md
Normal file
16
docs/anythingllm-workspaces.md
Normal file
@ -0,0 +1,16 @@
|
||||
# AnythingLLM — workspaces par projet
|
||||
|
||||
## Principe
|
||||
|
||||
- Un **workspace AnythingLLM** est créé (ou associé) **par projet** : documents indexés, embeddings, threads et paramètres RAG sont **scopés au projet**, pas mélangés entre dépôts.
|
||||
- Cela permet à la mémoire interrogée par `ask` / les agents de rester **pertinente** et **traçable** par contexte métier.
|
||||
|
||||
## Synchronisation avec le dépôt
|
||||
|
||||
- Une **moulinette** (pipeline de synchro) met à jour le workspace à partir de fichiers sélectionnés du dépôt : sources, doc, configs exposées volontairement, etc.
|
||||
- Les règles de ce qu’on synchronise (inclusions / exclusions, secrets interdits) doivent être **explicites** et alignées avec la politique de sécurité du projet.
|
||||
|
||||
## Exploitation
|
||||
|
||||
- Instance Docker décrite dans [services.md](./services.md) : stockage hôte typiquement sous `$HOME/anythingllm` sur l’**hôte qui exécute le conteneur** — en **première cible de déploiement**, cet hôte est le **serveur distant** (SSH), pas obligatoirement le poste Linux client ; la création de **plusieurs workspaces** se fait dans l’UI AnythingLLM (ou via API) en conservant la convention « un workspace = un projet ».
|
||||
- L’orchestrateur IDE décide **quand** interroger AnythingLLM (voir [system-architecture.md](./system-architecture.md)). L’URL vue depuis le client peut exiger un **tunnel SSH** ou un rebond réseau : [deployment-target.md](./deployment-target.md).
|
||||
25
docs/deployment-target.md
Normal file
25
docs/deployment-target.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Première cible de déploiement — client Linux + serveur distant (SSH)
|
||||
|
||||
## Modèle
|
||||
|
||||
La **première cible de déploiement** n’est pas un poste tout-en-un sur la même machine que le socle IA.
|
||||
|
||||
| Rôle | Où ça tourne | Contenu typique |
|
||||
|------|----------------|-----------------|
|
||||
| **Client** | Machine **Linux** de l’utilisateur (poste local) | Shell d’édition / UX (ex. Lapce), orchestrateur côté client si applicable, connexion **SSH** persistante ou à la demande |
|
||||
| **Serveur distant** | Hôte joignable en **SSH** (LAN, bastion, ou jump host selon l’infra) | **Socle technique IA** (Ollama, AnythingLLM Docker, services associés), **clones des dépôts**, exécution des **agents** / scripts / OpenShell sur le périmètre autorisé |
|
||||
|
||||
L’utilisateur travaille depuis un **Linux client** ; le **calcul**, les **modèles**, la **mémoire RAG** et les **sources de vérité Git** résident sur le **serveur** (ou une ferme de serveurs derrière la même session SSH).
|
||||
|
||||
## Conséquences
|
||||
|
||||
- Les URLs « locales » du serveur (`localhost:11434`, `localhost:3001`, …) sont **locales au serveur**. Depuis le client, l’accès passe par **tunnel SSH** (`-L`), **ProxyJump**, ou configuration explicite (hostname interne, VPN) selon la politique réseau.
|
||||
- L’**agent gateway** et le **policy-runtime** (OpenShell) s’exécutent idéalement **là où tournent les agents et les repos** — le serveur — sauf décision contraire documentée.
|
||||
- Le **workspace AnythingLLM par projet** vit **côté serveur** (stockage du conteneur ou chemin monté sur l’hôte distant). La moulinette de synchro lit les **dépôts sur le serveur**.
|
||||
- Le client doit disposer d’une **identité SSH** autorisée sur le serveur (voir `add-ssh-key.sh` et [infrastructure.md](./infrastructure.md)).
|
||||
|
||||
## Documentation liée
|
||||
|
||||
- Topologie LAN / bastion : [infrastructure.md](./infrastructure.md)
|
||||
- Services Ollama / AnythingLLM sur l’hôte qui **héberge** le socle : [services.md](./services.md)
|
||||
- Répartition logique des modules : [system-architecture.md](./system-architecture.md) (à lire avec ce découpage physique)
|
||||
58
docs/infrastructure.md
Normal file
58
docs/infrastructure.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Infrastructure
|
||||
|
||||
## Scope
|
||||
|
||||
This repository ships shell scripts used on Ubuntu workstations and related LAN hosts. It does **not** define cloud Terraform or CI; it documents how those scripts map to the **private LAN** layout used with the 4NK bastion model.
|
||||
|
||||
## First deployment shape (client / server)
|
||||
|
||||
The **primary deployment target** is a **Linux client** that connects over **SSH** to a **remote server** where the **AI stack** (Ollama, AnythingLLM, etc.) and **Git repositories** live. Install scripts in this repo apply mainly to that **server** (or to a LAN workstation that plays the same role). The client uses SSH (and optionally port forwarding) to reach services that bind to the server’s loopback or internal interfaces. See [deployment-target.md](./deployment-target.md).
|
||||
|
||||
## LAN host roles (reference)
|
||||
|
||||
Private segment **192.168.1.0/24** (DHCP with MAC reservations). The table matches the host lists in `add-ssh-key.sh`.
|
||||
|
||||
| IP | Role |
|
||||
|----|------|
|
||||
| 192.168.1.100 | Proxy / bastion (public entry via DynDNS `4nk.myftp.biz`) |
|
||||
| 192.168.1.101 | test |
|
||||
| 192.168.1.102 | pre-production |
|
||||
| 192.168.1.103 | production |
|
||||
| 192.168.1.104 | services (Git, Mempool, Rocket.Chat, …) |
|
||||
| 192.168.1.105 | bitcoin |
|
||||
| 192.168.1.173 | ia |
|
||||
| 192.168.1.164 | Example workstation on LAN (included in `LAN_DIRECT` list) |
|
||||
|
||||
Internet access to backends uses **SSH ProxyJump** via `ncantu@4nk.myftp.biz` (see `JUMP` in `add-ssh-key.sh`). On the same LAN, direct `ssh ncantu@192.168.1.x` is valid.
|
||||
|
||||
## Scripts (infrastructure / access)
|
||||
|
||||
### `add-ssh-key.sh`
|
||||
|
||||
Appends a fixed **Ed25519 public key** (comment `desk@desk`) to `~/.ssh/authorized_keys` on target hosts.
|
||||
|
||||
| Mode | When to use |
|
||||
|------|-------------|
|
||||
| Default | From a machine that can reach `JUMP` (`ncantu@4nk.myftp.biz`), then ProxyJump to each backend IP. |
|
||||
| `LAN_DIRECT=1` | Same LAN: direct SSH to each IP in `LAN_IPS` (proxy, backends, `.164`). No bastion hostname. |
|
||||
| `ADD_KEY_LOCAL=1` | Already logged in on the target host: update **current user** only (e.g. workstation `.164`). |
|
||||
|
||||
**Do not run with `sudo`:** the SSH client would use `/root/.ssh` and fail with `Permission denied (publickey)`.
|
||||
|
||||
**Environment (optional):** `JUMP`, `BACKEND_USER`, `SSH_IDENTITY_FILE`, `SSH_VERBOSE=1`, `EXTRA_LAN_IPS` (with `LAN_DIRECT=1`).
|
||||
|
||||
### `add-sudo-nopasswd-ncantu.sh`
|
||||
|
||||
One-time **root** execution: creates `/etc/sudoers.d/99-ncantu-nopasswd` with `ncantu ALL=(ALL) NOPASSWD: ALL`, `chmod 440`, `visudo -c`. Use only where this policy is explicitly required.
|
||||
|
||||
## Data paths (host)
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `$HOME/anythingllm` | AnythingLLM Docker bind mount (storage + `.env`), default from `install-anythingllm-docker.sh` |
|
||||
| `$HOME/.ssh/authorized_keys` | SSH access; updated by `add-ssh-key.sh` modes |
|
||||
|
||||
## Security notes
|
||||
|
||||
- SSH is key-based; the embedded key in `add-ssh-key.sh` is for a designated client (`desk@desk`). Rotate or replace in script if the key is compromised.
|
||||
- Passwordless sudo reduces interactive friction and **increases** local privilege impact; scope to trusted machines only.
|
||||
88
docs/services.md
Normal file
88
docs/services.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Services
|
||||
|
||||
## Where these services run (first deployment)
|
||||
|
||||
For the **first deployment target**, Ollama and AnythingLLM run on the **remote SSH server** that hosts the AI stack and repositories, not necessarily on the user’s Linux laptop. Access from the client may use **SSH local forwarding** or internal hostnames. See [deployment-target.md](./deployment-target.md).
|
||||
|
||||
## Overview
|
||||
|
||||
| Service | Delivery | Default URL / port | Config / persistence |
|
||||
|---------|----------|--------------------|------------------------|
|
||||
| Ollama | systemd (`ollama.service`) | `http://127.0.0.1:11434` (API) | Models under Ollama data dir; listen address via systemd override |
|
||||
| AnythingLLM | Docker (`mintplexlabs/anythingllm`) | `http://localhost:3001` | `$HOME/anythingllm` + `.env` bind-mounted ; **one workspace per project** (see [anythingllm-workspaces.md](./anythingllm-workspaces.md)) |
|
||||
| AnythingLLM Desktop | AppImage (optional) | local Electron app | User profile under `~/.config/anythingllm-desktop` (installer) |
|
||||
|
||||
## Ollama
|
||||
|
||||
- **Install:** official script `https://ollama.com/install.sh` (used on target Ubuntu hosts).
|
||||
- **Service:** `systemctl enable --now ollama` (handled by installer).
|
||||
- **Default bind:** loopback only (`127.0.0.1:11434`), which **blocks** Docker containers on the same host from calling Ollama.
|
||||
|
||||
### Expose Ollama to Docker on the same host
|
||||
|
||||
Run **`configure-ollama-for-docker.sh`** as root (or equivalent):
|
||||
|
||||
- Drop-in: `/etc/systemd/system/ollama.service.d/override.conf`
|
||||
- `Environment="OLLAMA_HOST=0.0.0.0:11434"`
|
||||
- `systemctl daemon-reload && systemctl restart ollama`
|
||||
|
||||
Verify: `ss -tlnp | grep 11434` shows `*:11434`.
|
||||
|
||||
### Models (reference)
|
||||
|
||||
- Embeddings for AnythingLLM + Ollama: `ollama pull nomic-embed-text`
|
||||
- Custom name **`qwen3-code-webdev`:** not in the public Ollama library as-is; this repo includes `Modelfile-qwen3-code-webdev` defining an alias (default base: `qwen3-coder:480b-cloud`). Rebuild with `ollama create qwen3-code-webdev -f Modelfile-qwen3-code-webdev` after editing `FROM`.
|
||||
|
||||
## AnythingLLM (Docker)
|
||||
|
||||
### Workspaces and projects
|
||||
|
||||
AnythingLLM is used with **dedicated workspaces per project** so RAG memory, documents, and threads stay isolated. A **sync job** (“moulinette”) keeps selected repository files aligned with each workspace. Operational rules: [anythingllm-workspaces.md](./anythingllm-workspaces.md).
|
||||
|
||||
**Script:** `install-anythingllm-docker.sh`
|
||||
|
||||
- **Image:** `mintplexlabs/anythingllm` (override with `ANYTHINGLLM_IMAGE`).
|
||||
- **Container name:** `anythingllm` (override with `ANYTHINGLLM_CONTAINER_NAME`).
|
||||
- **Ports:** `HOST_PORT:3001` (default `3001:3001`).
|
||||
- **Capabilities:** `--cap-add SYS_ADMIN` (Chromium / document features in container).
|
||||
- **Networking:** `--add-host=host.docker.internal:host-gateway` so the app can reach Ollama on the host at `http://host.docker.internal:11434` once `OLLAMA_HOST` is set as above.
|
||||
- **Volumes:**
|
||||
- `${STORAGE_LOCATION}:/app/server/storage`
|
||||
- `${STORAGE_LOCATION}/.env:/app/server/.env`
|
||||
|
||||
Re-running the script **removes** the existing container by name and starts a new one; data remains in `STORAGE_LOCATION` if the bind path is unchanged.
|
||||
|
||||
### Configure LLM provider (Ollama)
|
||||
|
||||
In `$STORAGE_LOCATION/.env` (mounted into the container), set at minimum:
|
||||
|
||||
- `LLM_PROVIDER='ollama'`
|
||||
- `OLLAMA_BASE_PATH='http://host.docker.internal:11434'`
|
||||
- `OLLAMA_MODEL_PREF='<model name>'` (e.g. `qwen3-code-webdev`)
|
||||
- `EMBEDDING_ENGINE='ollama'`
|
||||
- `EMBEDDING_BASE_PATH='http://host.docker.internal:11434'`
|
||||
- `EMBEDDING_MODEL_PREF='nomic-embed-text:latest'`
|
||||
- `VECTOR_DB='lancedb'` (default stack)
|
||||
|
||||
See upstream `.env.example`:
|
||||
<https://raw.githubusercontent.com/Mintplex-Labs/anything-llm/master/docker/.env.example>
|
||||
|
||||
After editing `.env`, restart the container: `docker restart anythingllm`.
|
||||
|
||||
## AnythingLLM Desktop (AppImage)
|
||||
|
||||
**Script:** `installer.sh` — downloads the official AppImage, optional AppArmor profile, `.desktop` entry. Interactive prompts; not a headless service.
|
||||
|
||||
- Documentation: <https://docs.anythingllm.com>
|
||||
- Use **either** Docker **or** Desktop on the same machine if you want to avoid conflicting ports and duplicate workspaces.
|
||||
|
||||
## Operational checks
|
||||
|
||||
```bash
|
||||
systemctl is-active ollama
|
||||
curl -sS http://127.0.0.1:11434/api/tags | head
|
||||
docker ps --filter name=anythingllm
|
||||
docker exec anythingllm sh -c 'curl -sS http://host.docker.internal:11434/api/tags | head'
|
||||
```
|
||||
|
||||
The last command must succeed after `OLLAMA_HOST=0.0.0.0:11434` and `host.docker.internal` are configured.
|
||||
111
docs/system-architecture.md
Normal file
111
docs/system-architecture.md
Normal file
@ -0,0 +1,111 @@
|
||||
# Architecture système — IDE, agents, runtime, mémoire
|
||||
|
||||
## Répartition physique (première cible)
|
||||
|
||||
Pour le **premier déploiement**, un **poste Linux** (client) établit des sessions **SSH** vers un **serveur** qui concentre :
|
||||
|
||||
- Ollama, AnythingLLM (et extensions du socle IA) ;
|
||||
- les **dépôts** et l’exécution des **agents** / OpenShell sur chemins autorisés.
|
||||
|
||||
L’**éditeur** et une partie de l’UX peuvent rester sur le client ; le **gateway**, le **policy-runtime** et les **knowledge-services** (fichiers RAG, workspaces AnythingLLM) sont cohérents **côté serveur**, sauf architecture hybride explicitement documentée. Voir [deployment-target.md](./deployment-target.md).
|
||||
|
||||
## Couches fonctionnelles (vue cible)
|
||||
|
||||
| Couche | Rôle |
|
||||
|--------|------|
|
||||
| **Agents `ia_dev`** | Noyau **métier et opératoire** (existant) ; sous-agents, recettes, tools |
|
||||
| **OpenShell** | **Sécurité et exécution** : sandboxes, politiques, résolution contrôlée |
|
||||
| **Éditeur (ex. Lapce)** | **Interaction** : texte léger, terminal, palette, timeline, UX intentionnelle |
|
||||
| **Orchestrateur maison** | **Routage** : pas « d’intelligence » au sens LLM, mais décision de flux |
|
||||
| **Ollama** | Backend d’**inférence** locale |
|
||||
| **AnythingLLM** | **Mémoire documentaire** et RAG ; **un workspace par projet** ([anythingllm-workspaces.md](./anythingllm-workspaces.md)) |
|
||||
| **ONLYOFFICE** | Backend **documentaire métier** (documents, feuilles, présentations) |
|
||||
|
||||
Flux type : demande utilisateur → orchestrateur → préparation (scripts / tools génériques) → agents → besoin LLM → Ollama ; besoin doc / RAG → AnythingLLM ; besoin bureautique → ONLYOFFICE.
|
||||
|
||||
## Orchestrateur — décisions de routage
|
||||
|
||||
Décider notamment :
|
||||
|
||||
- quelle **commande** utilisateur appelle quel **agent principal** ;
|
||||
- quel agent appelle quel **sous-agent** ;
|
||||
- quand privilégier un **script** vs un **tool générique** vs `ia_dev` ;
|
||||
- quand `ia_dev` peut **escalader** vers Ollama ;
|
||||
- quand interroger **AnythingLLM** ;
|
||||
- quand passer par **ONLYOFFICE** ;
|
||||
- quand **refuser**.
|
||||
|
||||
## Descripteur stable par agent
|
||||
|
||||
Chaque agent devrait exposer au minimum : **nom**, **rôle**, **entrées**, **sorties**, **droits**, **dépendances**, **scripts** appelés, **modèles** éventuels, **préconditions**, **postconditions**, **timeouts**, **coût**, **niveau de risque**.
|
||||
|
||||
Un **protocole unique** couvre les implémentations hétérogènes (script, workflow, wrapper d’outil, appel IA).
|
||||
|
||||
Hiérarchie agent → sous-agent : à **formaliser** explicitement pour éviter un graphe implicite ingouvernable.
|
||||
|
||||
## Événements normalisés (exemples)
|
||||
|
||||
`started`, `tool_selected`, `script_started`, `model_called`, `waiting_validation`, `completed`, `failed`, `rolled_back`, `artifact_created`.
|
||||
|
||||
Sortie **événementielle uniforme** vers l’éditeur ; **journalisation** ; possibilité de **rejouer**, diagnostiquer, **convertir en recette** stable.
|
||||
|
||||
## Sécurité — OpenShell central
|
||||
|
||||
- Chaque nouvelle résolution devrait pouvoir devenir **recipe**, **tool** ou **sous-agent** stable (travail des commandes UX de haut niveau).
|
||||
- Les agents ne devraient **pas** exécuter directement sur l’hôte sans contrôle : **sandboxes** avec droits dérivés du **type d’agent** et du **projet**.
|
||||
|
||||
Exemples de **profils de policy** : lecture seule ; lecture + scripts locaux ; écriture bornée ; déploiement pprod / prod ; génération documentaire ; accès tickets ; accès base ; accès ONLYOFFICE.
|
||||
|
||||
**Pas de fallback implicite** non spécifié : refus ou erreur explicite selon les règles du projet.
|
||||
|
||||
## Couche de normalisation (registre agents)
|
||||
|
||||
Registre unifié des agents `ia_dev`, chacun exposé comme **objet standardisé** :
|
||||
|
||||
- identité, catégorie, **commandes déclenchantes**, permissions, environnements compatibles, mode d’exécution, outils requis, **politiques OpenShell**, formats d’entrée/sortie, stratégies de rejet (pas de fallback silencieux), journalisation, possibilité d’appeler d’autres agents.
|
||||
|
||||
Sans cette couche, l’IDE reste dépendant de **conventions implicites** du dépôt.
|
||||
|
||||
## Agent gateway (adaptateur)
|
||||
|
||||
Ne pas brancher le dépôt `ia_dev` directement dans l’éditeur : passer par une **agent gateway** qui :
|
||||
|
||||
1. Charge le **registre**
|
||||
2. **Valide** les permissions
|
||||
3. **Résout** les dépendances
|
||||
4. Ouvre la **sandbox OpenShell** adaptée
|
||||
5. **Injecte** le contexte autorisé
|
||||
6. **Lance** l’agent
|
||||
7. **Capture** les événements
|
||||
8. **Uniformise** les sorties
|
||||
9. **Publie** le flux vers l’éditeur
|
||||
10. **Journalise**
|
||||
11. Gère **rollbacks** et statuts
|
||||
|
||||
## Modules logiciels (découpage)
|
||||
|
||||
| Module | Responsabilité |
|
||||
|--------|----------------|
|
||||
| **editor-shell** | Éditeur texte léger, terminal, explorer **mode expert**, onglets, palette de commandes, timeline, UX |
|
||||
| **agent-gateway** | Adaptateur uniforme UX ↔ `ia_dev` |
|
||||
| **policy-runtime** | OpenShell, profils de policy, providers, sandboxes, journaux |
|
||||
| **knowledge-services** | AnythingLLM, mémoire projet, index documentaire, routage RAG |
|
||||
| **doc-services** | ONLYOFFICE, flux `present`, `write`, `sheet` |
|
||||
|
||||
Les **agents** restent le noyau opératoire ; les modules encadrent et exposent.
|
||||
|
||||
## UX — masquage des agents
|
||||
|
||||
L’utilisateur ne « choisit pas un agent » dans le flux nominal : il exprime une **intention** (`ask`, `fix`, …). Le **routeur** sélectionne l’agent ou la chaîne d’agents.
|
||||
|
||||
## Socle éditeur : Lapce
|
||||
|
||||
**Lapce** (open source, Rust, rendu natif / GPU) est le candidat retenu pour un **éditeur rapide et léger** avec agents, au lieu d’un IDE historique très chargé. Positionnement aligné avec le rôle « coquille + orchestration + transparence contextuelle » décrit dans [ux-navigation-model.md](./ux-navigation-model.md).
|
||||
|
||||
## Taxonomie des droits
|
||||
|
||||
Les droits doivent être **nommés**, **vérifiables** et **traçables** (lien avec OpenShell et le registre d’agents). Pas de contournement par défaut.
|
||||
|
||||
## Mémoire d’exécution
|
||||
|
||||
Conserver une mémoire d’exécution **exploitable** : rejouer, auditer, promouvoir une exécution réussie en **recette** versionnée.
|
||||
116
docs/ux-navigation-model.md
Normal file
116
docs/ux-navigation-model.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Modèle UX — au-delà de l’explorateur fichiers
|
||||
|
||||
## Renoncer à l’explorer classique
|
||||
|
||||
Renoncer à une vue fichiers / dossiers / explorateur **ne signifie pas** l’absence de structure : c’est une interface **orientée intention**, **contexte**, **flux d’actions** et **artefacts**.
|
||||
|
||||
Si Git est peu visible en surface, si les **procédures** sont déterministes, si les **agents** savent installer, importer et développer des **outils génériques** avant d’appeler l’IA, et si l’utilisateur travaille surtout via des commandes de haut niveau (`ask`, `fix`, `improve`, `deploy`, `ticket`, `present`, `write`, `sheet`, `extract`, `think adversarial`, etc.), l’explorer n’est pas remplacé par « rien », mais par des **vues plus adaptées** au modèle.
|
||||
|
||||
## Ce que l’explorer faisait — et comment le redistribuer
|
||||
|
||||
| Besoin historique (explorer) | Remplacement ciblé |
|
||||
|------------------------------|-------------------|
|
||||
| Localiser physiquement les fichiers | Recherche, commandes, **mémoire projet**, points d’entrée contextuels |
|
||||
| Comprendre la structure du projet | Vues **sémantiques** : modules, services, tickets, environnements, procédures, agents, artefacts, documents |
|
||||
| Agir sur des objets concrets | **Palette d’intentions** + vues d’**objets métier** |
|
||||
|
||||
Le bon niveau d’abstraction devient :
|
||||
|
||||
1. Ce que je veux faire
|
||||
2. Sur quel **objet logique**
|
||||
3. Avec quels **droits**
|
||||
4. Dans quel **contexte projet**
|
||||
5. Avec quelle **procédure**
|
||||
6. Avec quel **agent**
|
||||
7. Avec quel **résultat attendu**
|
||||
|
||||
→ Plus proche d’une **machine de travail orientée opérations** que d’un éditeur de fichiers.
|
||||
|
||||
## Risques si l’explorer disparaît sans compensations
|
||||
|
||||
1. **Perte de repère spatial**
|
||||
2. **Difficulté d’inspection libre**
|
||||
3. **Fragilité agentique** si le graphe sémantique (agents, index, résumés, outils, procédures) est **incomplet** — l’absence d’explorer est alors vécue comme une **privation**, pas comme une abstraction supérieure
|
||||
4. **Effet boîte noire**
|
||||
|
||||
## Compensations obligatoires
|
||||
|
||||
### Palette d’accès universelle (« go to anything »)
|
||||
|
||||
Trouver et ouvrir immédiatement, par nom, alias, rôle, description, historique, proximité contextuelle, **permissions** :
|
||||
|
||||
- fichier, module, service, fonction, procédure, agent, ticket, environnement, document, support de présentation, feuille, job, log, artefact généré, commande.
|
||||
|
||||
### Vue des objets logiques
|
||||
|
||||
À la place d’une arborescence de fichiers : **services**, **modules**, **environnements**, **tickets**, **bases**, **agents**, **recipes**, **tools**, **documents**, **supports**, **jobs**, **déploiements**, **artefacts**.
|
||||
|
||||
L’utilisateur voit en permanence **sur quoi il travaille** : projet courant, tâche courante, commande active, agents impliqués, artefacts produits, documents de référence, état stable disponible, environnement concerné.
|
||||
|
||||
### Recherche structurée (au-delà du grep fichiers)
|
||||
|
||||
Capacité de chercher dans : code, **symboles**, recettes, outils, **historiques de session**, tickets, documents, logs, artefacts ONLYOFFICE, sorties d’agents, **mémoire AnythingLLM**.
|
||||
|
||||
### Vue artefacts
|
||||
|
||||
Artefacts **récents** et **liés au contexte courant** : scripts, documents, feuilles, présentations, tickets, logs, plans, sorties diverses.
|
||||
|
||||
### Vue timeline / stream
|
||||
|
||||
Ce n’est pas la liste des fichiers, mais les **actions** : lancé, lu, généré, échoué, disponible, repris.
|
||||
|
||||
## Types d’objets ouvrables
|
||||
|
||||
- **Texte** : source, config, script, note, document texte
|
||||
- **Logique** : service, module, fonction, procédure, agent, ticket
|
||||
- **Exécution** : job, déploiement, test, lint, run agentique, terminal de tâche
|
||||
- **Artefact** : présentation, document, feuille, rapport, résumé, sortie de recette
|
||||
|
||||
Navigation **sans penser « répertoire »**, en pensant **« objet »**.
|
||||
|
||||
## Arborescence vs graphe
|
||||
|
||||
L’explorer repose sur un **arbre**. Le système cible un **graphe** : ticket → agents → recettes → scripts → artefacts → projet → environnements → procédures → documents → support → ticket, etc.
|
||||
|
||||
Rendre visible : structure logique, objets importants, actions disponibles, **relations**, état courant, artefacts récents modifiés, sorties d’agents, points d’entrée, chemins de reprise.
|
||||
|
||||
## Conditions de réussite
|
||||
|
||||
1. **Indexation solide** : fichiers, symboles, modules, services, tickets, scripts, recettes, documents, artefacts, historiques
|
||||
2. **Palette universelle** exceptionnelle
|
||||
3. **Recherche** plein texte + sémantique
|
||||
4. **Vues contexte + timeline** fiables
|
||||
5. **Mémoire projet** fiable (AnythingLLM par projet + synchro)
|
||||
6. **Commandes principales** réellement suffisantes au quotidien
|
||||
7. **Mode expert / secours** pour accéder au niveau bas (y compris vue fichiers brute) si l’abstraction haute échoue
|
||||
|
||||
**Ne pas supprimer totalement l’explorer du produit** : le retirer de l’interface **normale**, mais garder un accès discret / désactivable par défaut si indexation, relations ou agents déraillent.
|
||||
|
||||
## Trois niveaux proposés
|
||||
|
||||
| Niveau | Rôle |
|
||||
|--------|------|
|
||||
| **Principal** | Pas d’explorer classique ; commandes, recherche, contexte, timeline, objets logiques, artefacts |
|
||||
| **Secondaire** | Vue « structure » non arborescente : services, modules, objets, procédures |
|
||||
| **Expert** | Accès secours à la structure brute (fichiers / répertoires), discret, éventuellement désactivé par défaut |
|
||||
|
||||
## Grammaire des commandes
|
||||
|
||||
Plus l’explorer disparaît de la surface, plus les commandes doivent être **rigoureuses** :
|
||||
|
||||
- `ask` — interrogation du graphe de connaissances et d’objets
|
||||
- `fix` — réparation guidée (tests), sans obligation de « trouver le fichier » manuellement
|
||||
- `improve` — optimisation sur objet logique ou contexte courant
|
||||
- `deploy` — action sur **environnement** et **procédure**, pas sur un répertoire
|
||||
- `ticket` — entrée de navigation métier
|
||||
- `present`, `write`, `sheet` — générateurs d’artefacts liés au contexte logique du projet
|
||||
- `extract` — extraction structurée depuis sources / documents / contexte
|
||||
- `think adversarial` — revue ou stress test (sécurité, hypothèses, angles de défaillance) sur l’objet ou le livrable courant
|
||||
|
||||
Synthèse : *pas d’explorer comme surface principale ; navigation par intentions, recherche, objets logiques, timeline et artefacts ; accès brut réservé au mode expert.*
|
||||
|
||||
## Chaîne UX cible (rappel)
|
||||
|
||||
Éditeur → **orchestrateur UX** → **runtime sécurisé** → agents `ia_dev` → sous-agents → (préparation / scripts / tools) → IA avec éléments demandés → réponse intégrée à l’IDE.
|
||||
|
||||
L’explorer est remplacé par **mieux** : palette universelle, navigation par objets, vue contexte, timeline, recherche puissante, vue artefacts, niveau expert de secours.
|
||||
39
install-anythingllm-docker.sh
Executable file
39
install-anythingllm-docker.sh
Executable file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install / start AnythingLLM on Ubuntu via Docker (official image mintplexlabs/anythingllm).
|
||||
# Docs: https://docs.anythingllm.com/installation-docker/local-docker
|
||||
# UI: http://localhost:3001
|
||||
#
|
||||
# Optional env:
|
||||
# STORAGE_LOCATION default: $HOME/anythingllm
|
||||
# HOST_PORT default: 3001
|
||||
# Usage: ./install-anythingllm-docker.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
STORAGE_LOCATION="${STORAGE_LOCATION:-${HOME}/anythingllm}"
|
||||
HOST_PORT="${HOST_PORT:-3001}"
|
||||
IMAGE="${ANYTHINGLLM_IMAGE:-mintplexlabs/anythingllm}"
|
||||
CONTAINER_NAME="${ANYTHINGLLM_CONTAINER_NAME:-anythingllm}"
|
||||
|
||||
mkdir -p "${STORAGE_LOCATION}"
|
||||
touch "${STORAGE_LOCATION}/.env"
|
||||
|
||||
docker pull "${IMAGE}"
|
||||
|
||||
if docker ps -a --format '{{.Names}}' | grep -qx "${CONTAINER_NAME}"; then
|
||||
docker rm -f "${CONTAINER_NAME}"
|
||||
fi
|
||||
|
||||
docker run -d \
|
||||
-p "${HOST_PORT}:3001" \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
--cap-add SYS_ADMIN \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
-v "${STORAGE_LOCATION}:/app/server/storage" \
|
||||
-v "${STORAGE_LOCATION}/.env:/app/server/.env" \
|
||||
-e STORAGE_DIR="/app/server/storage" \
|
||||
"${IMAGE}"
|
||||
|
||||
echo "AnythingLLM is starting. Open http://localhost:${HOST_PORT}"
|
||||
echo "Storage: ${STORAGE_LOCATION}"
|
||||
198
installer.sh
Executable file
198
installer.sh
Executable file
@ -0,0 +1,198 @@
|
||||
#!/bin/sh
|
||||
# This script installs AnythingLLMDesktop on Linux.
|
||||
# On systems with AppArmor enabled, the AppImage needs to also create a userspace
|
||||
# apparmor profile so that the AppImage can be run without SUID requirements due to root chromium ownership.
|
||||
#
|
||||
# Todo: Detect the current location of the AppImage so that we can update the application
|
||||
# in-place without the user needed to manually move the application to the new location.
|
||||
# This is also useful so that the apparmor location is always correct if the user edits it.
|
||||
set -eu
|
||||
|
||||
status() { echo "$*" >&2; }
|
||||
error() { echo "ERROR $*"; exit 1; }
|
||||
warning() { echo "WARNING: $*"; }
|
||||
|
||||
# Detect AppArmor major version so we generate compatible profile syntax.
|
||||
# AppArmor 4.x (Ubuntu 24.04+) supports abi declarations and userns rules;
|
||||
# older versions (Ubuntu 20.04/22.04) need classic syntax only.
|
||||
get_apparmor_major_version() {
|
||||
if command -v apparmor_parser >/dev/null 2>&1; then
|
||||
apparmor_parser --version 2>/dev/null | grep -oE '[0-9]+' | head -1
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create an AppArmor profile for AnythingLLMDesktop for systems with AppArmor enabled
|
||||
# https://askubuntu.com/questions/1512287/obsidian-appimage-the-suid-sandbox-helper-binary-was-found-but-is-not-configu/1528215#1528215
|
||||
create_apparmor_profile() {
|
||||
status "Checking for sudo privileges..."
|
||||
sudo -v
|
||||
if [ $? -ne 0 ]; then
|
||||
error "Failed to get sudo privileges! Aborting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AA_MAJOR=$(get_apparmor_major_version)
|
||||
status "Creating AppArmor profile for AnythingLLM (AppArmor version ${AA_MAJOR}.x detected)..."
|
||||
|
||||
if [ "${AA_MAJOR}" -ge 4 ] 2>/dev/null; then
|
||||
APP_ARMOR_CONTENT=$(cat <<'EOF'
|
||||
# AnythingLLMDesktop AppArmor profile (4.x syntax)
|
||||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
profile anythingllmdesktop /**/AnythingLLMDesktop.AppImage flags=(unconfined) {
|
||||
userns,
|
||||
}
|
||||
EOF
|
||||
)
|
||||
else
|
||||
APP_ARMOR_CONTENT=$(cat <<'EOF'
|
||||
# AnythingLLMDesktop AppArmor profile (classic syntax for AppArmor 2.x/3.x)
|
||||
#include <tunables/global>
|
||||
|
||||
profile anythingllmdesktop /**/AnythingLLMDesktop.AppImage flags=(unconfined) {
|
||||
}
|
||||
EOF
|
||||
)
|
||||
fi
|
||||
|
||||
echo "$APP_ARMOR_CONTENT" | sudo tee /etc/apparmor.d/anythingllmdesktop > /dev/null
|
||||
status "Reloading AppArmor service..."
|
||||
if sudo apparmor_parser -r /etc/apparmor.d/anythingllmdesktop 2>/dev/null; then
|
||||
status "AppArmor profile created - you can now run AnythingLLMDesktop without SUID requirements."
|
||||
elif sudo systemctl reload apparmor.service 2>/dev/null; then
|
||||
status "AppArmor profile created - you can now run AnythingLLMDesktop without SUID requirements."
|
||||
else
|
||||
warning "AppArmor reload failed. The profile was written to /etc/apparmor.d/anythingllmdesktop"
|
||||
warning "but could not be loaded. You may need to reboot or run:"
|
||||
warning " sudo apparmor_parser -r /etc/apparmor.d/anythingllmdesktop"
|
||||
warning "If issues persist, you can disable the profile for this app only:"
|
||||
warning " sudo ln -sf /etc/apparmor.d/anythingllmdesktop /etc/apparmor.d/disable/"
|
||||
warning " sudo systemctl reload apparmor"
|
||||
fi
|
||||
}
|
||||
|
||||
check_to_create_apparmor_profile() {
|
||||
# Check if the system has AppArmor enabled
|
||||
if [ -f /sys/kernel/security/apparmor/profiles ]; then
|
||||
if ! [ -f /etc/apparmor.d/anythingllmdesktop ]; then
|
||||
status "AppArmor is enabled on this system."
|
||||
status "\e[31m[Warning]\e[0m You will get an error about SUID permission issues when running the AppImage without creating an AppArmor profile."
|
||||
status "This requires sudo privileges. If you are unsure, you can create an AppArmor profile manually."
|
||||
read -p "Do you want to auto-create an AppArmor profile for AnythingLLM now? (y/n): " create_apparmor
|
||||
case "$create_apparmor" in [yY]) create_apparmor="y" ;; esac
|
||||
if [ "$create_apparmor" = "y" ]; then
|
||||
create_apparmor_profile
|
||||
else
|
||||
status "AppArmor is enabled on this system."
|
||||
status "AppArmor profile creation skipped. You may not be able to run AnythingLLMDesktop without it."
|
||||
fi
|
||||
else
|
||||
status "AppArmor profile already exists."
|
||||
read -p "Do you want to overwrite it with the latest version? (y/n): " overwrite_apparmor
|
||||
case "$overwrite_apparmor" in [yY]) overwrite_apparmor="y" ;; esac
|
||||
if [ "$overwrite_apparmor" = "y" ]; then
|
||||
create_apparmor_profile
|
||||
fi
|
||||
fi
|
||||
else
|
||||
status "AppArmor could not be automatically detected or does not exist. If you get an SUID error on startup, you may need to create an AppArmor profile for AnythingLLMDesktop.AppImage manually."
|
||||
fi
|
||||
}
|
||||
|
||||
check_or_create_desktop_profile() {
|
||||
if ! [ -f $HOME/.local/share/applications/anythingllmdesktop.desktop ]; then
|
||||
status "Desktop profile not found. Creating..."
|
||||
|
||||
# Default Exec command
|
||||
EXEC_CMD="$INSTALL_DIR/AnythingLLMDesktop.AppImage"
|
||||
|
||||
# Check for Wayland + KDE specifically
|
||||
# We check XDG_SESSION_TYPE for "wayland"
|
||||
# We check XDG_CURRENT_DESKTOP for "KDE" (handles "KDE", "KDE-Plasma", etc)
|
||||
# We use ':-' to safely handle unbound variables in 'set -u' mode
|
||||
if [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then
|
||||
case "${XDG_CURRENT_DESKTOP:-}" in
|
||||
*"KDE"*)
|
||||
status "Detected KDE Plasma on Wayland. Adding specific flags for IME support."
|
||||
EXEC_CMD="$INSTALL_DIR/AnythingLLMDesktop.AppImage --enable-features=UseOzonePlatform --ozone-platform=wayland --enable-wayland-ime"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
DESKTOP_CONTENT=$(cat <<EOF
|
||||
[Desktop Entry]
|
||||
StartupWMClass=anythingllm-desktop
|
||||
Type=Application
|
||||
Name=AnythingLLM Desktop
|
||||
Exec=$EXEC_CMD
|
||||
Icon=$HOME/.config/anythingllm-desktop/storage/icon.png
|
||||
Categories=Utility;
|
||||
EOF
|
||||
)
|
||||
mkdir -p $HOME/.local/share/applications
|
||||
echo "$DESKTOP_CONTENT" | tee $HOME/.local/share/applications/anythingllmdesktop.desktop > /dev/null
|
||||
status "Desktop profile created!"
|
||||
fi
|
||||
}
|
||||
|
||||
arch=$(uname -m)
|
||||
[ "$(uname -s)" = "Linux" ] || error 'This script is intended to run on Linux only.'
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
status "This script should not be run as root. Please run it as a regular user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Allow custom installation directory via ANYTHING_LLM_INSTALL_DIR environment variable
|
||||
# Defaults to $HOME if not set
|
||||
INSTALL_DIR="${ANYTHING_LLM_INSTALL_DIR:-$HOME}"
|
||||
|
||||
status "#########################################################"
|
||||
status " Welcome to the AnythingLLM Desktop Installer"
|
||||
status " by Mintplex Labs Inc (team@mintplexlabs.com)"
|
||||
status " Architecture: $arch"
|
||||
status " Install Directory: $INSTALL_DIR"
|
||||
status "#########################################################"
|
||||
|
||||
if [ "$arch" = "arm64" ] || [ "$arch" = "aarch64" ]; then
|
||||
APPIMAGE_URL="https://cdn.anythingllm.com/latest/AnythingLLMDesktop-Arm64.AppImage"
|
||||
else
|
||||
APPIMAGE_URL="https://cdn.anythingllm.com/latest/AnythingLLMDesktop.AppImage"
|
||||
fi
|
||||
APPIMAGE_FILE="AnythingLLMDesktop.AppImage"
|
||||
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
SHOULD_DOWNLOAD="true"
|
||||
if [ -f "$INSTALL_DIR/$APPIMAGE_FILE" ]; then
|
||||
status "Existing installation found at $INSTALL_DIR/$APPIMAGE_FILE"
|
||||
read -p "Do you want to re-download and overwrite it? (y/n): " overwrite
|
||||
case "$overwrite" in [yY]) ;; *) SHOULD_DOWNLOAD="false" ;; esac
|
||||
fi
|
||||
|
||||
if [ "$SHOULD_DOWNLOAD" = "true" ]; then
|
||||
status "Downloading AnythingLLM Desktop..."
|
||||
curl --fail --show-error --location --progress-bar -o "$INSTALL_DIR/$APPIMAGE_FILE" "$APPIMAGE_URL"
|
||||
chmod +x "$INSTALL_DIR/$APPIMAGE_FILE"
|
||||
fi
|
||||
|
||||
status "AnythingLLM Desktop is ready to run!"
|
||||
status "$INSTALL_DIR/$APPIMAGE_FILE to start AnythingLLMDesktop"
|
||||
status "\e[36mHeads up!\e[0m You can rerun this installer anytime to get the latest version of AnythingLLM without effecting your existing data."
|
||||
status "Documentation: https://docs.anythingllm.com"
|
||||
status "Issues: https://github.com/Mintplex-Labs/anything-llm"
|
||||
status "\e[36mThanks for using AnythingLLM!\e[0m\n\n"
|
||||
|
||||
status "Next, we will create a desktop profile and AppArmor profile for AnythingLLMDesktop."
|
||||
status "This is required for the AppImage to be able to run without SUID requirements."
|
||||
status "You can manually create these profiles if you prefer."
|
||||
|
||||
check_or_create_desktop_profile
|
||||
check_to_create_apparmor_profile
|
||||
|
||||
read -p "Do you want to start AnythingLLMDesktop now? (y/n): " start
|
||||
case "$start" in [yY]) start="y" ;; esac
|
||||
if [ "$start" = "y" ]; then
|
||||
"$INSTALL_DIR/$APPIMAGE_FILE"
|
||||
fi
|
||||
Loading…
x
Reference in New Issue
Block a user