Retour utilisateur : le champ "Fournisseur" en simple texte libre ne donnait aucune indication de ce qui est possible, notamment pour l'IA locale (Ollama, LM Studio), alors que d'autres preferent un service cloud. Remplace par une QComboBox editable : une liste de raccourcis usuels (OpenAI, OpenRouter, Mistral AI, Ollama local, LM Studio local, Personnalise...) qui pre-remplit l'URL de base au choix - sans jamais empecher de saisir librement un autre fournisseur/proxy compatible OpenAI, conformement a l'exigence d'origine de ne rien figer en dur. Le modele reste toujours en saisie libre, chaque installation Ollama/LM Studio ayant ses propres modeles telecharges. Bonus cible : choisir un prereglage local (Ollama/LM Studio, detecte via localhost/127.0.0.1 dans l'URL) met a jour l'indication du champ cle API pour signaler qu'elle n'est generalement pas necessaire. tests/test_ai_settings_dialog.py (8 verifications, nouveau fichier - aucun test ne couvrait encore AISettingsDialog elle-meme) : presence d'Ollama/LM Studio dans la liste, pre-remplissage de l'URL par prereglage, "Personnalise" qui ne touche pas une URL deja saisie, persistance d'un nom de fournisseur personnalise a travers un enregistrement puis une reouverture. Suite complete revalidee (34/34) et ruff toujours propre. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
303 lines
19 KiB
Markdown
303 lines
19 KiB
Markdown
# PySSH Client
|
|
|
|
Client SSH/SFTP personnel (type MobaXterm/Termius/Bitvise), **100% local**.
|
|
|
|
**Aucun compte requis, aucun service cloud, aucune telemetrie.** Sessions, cles, mots de passe
|
|
memorises et snippets restent dans des fichiers locaux (ou le trousseau securise de Windows) —
|
|
rien n'est jamais envoye a un serveur tiers. La seule fonctionnalite qui communique vers
|
|
l'exterieur est l'**assistant IA**, entierement optionnel, desactive par defaut, et qui n'envoie
|
|
quoi que ce soit qu'au fournisseur que *vous* configurez avec *votre propre* cle API (voir plus bas).
|
|
|
|
## Fonctionnalites
|
|
|
|
### Terminal SSH
|
|
|
|
- Sessions SSH multiples dans des onglets, avec terminal ANSI colore et trois themes
|
|
- Copier-coller **a la souris, comme dans un terminal Unix** : selectionner copie aussitot
|
|
(double-clic pour un mot), et le clic droit colle directement — ni menu, ni raccourci.
|
|
Le clic molette colle aussi. `Ctrl+Shift+C` / `Ctrl+Shift+V` / `Maj+Inser` restent
|
|
disponibles au clavier, et `Ctrl+C` reste l'interruption comme dans tout terminal
|
|
- Confort d'affichage : zoom `Ctrl+molette` ou `Ctrl+ +/-` (`Ctrl+0` remet a zero, la taille est
|
|
memorisee), barre de defilement de l'historique, `Maj+PagePrec/Suiv`, `Ctrl+Fin` pour revenir
|
|
en direct, et recherche dans l'historique avec `Ctrl+F`
|
|
- Onglets renommables (double-clic, ou clic droit) et deplaçables ; le clic droit permet
|
|
aussi de les diviser en **2 ou 4 panneaux** (cote a cote, empiles, ou grille), de deplacer un
|
|
autre onglet ouvert dans un panneau, ou de fermer les autres onglets
|
|
- **Keepalive et reconnexion automatique** configurables par session (intervalle, nombre de
|
|
tentatives, delai avec recul progressif) : une coupure reseau n'oblige plus a rouvrir
|
|
la session et ressaisir les identifiants
|
|
- Journalisation de session dans un fichier (bouton "Journal"), sequences ANSI retirees pour
|
|
rester lisible
|
|
- Reconnexion manuelle en un clic (bouton dans la barre de l'onglet)
|
|
|
|
### Connexion et authentification
|
|
|
|
- **Authentification automatique** (mode par defaut) : comme la commande `ssh`, l'application
|
|
utilise l'agent SSH et les cles de `~/.ssh` sans que vous ayez a designer quoi que ce soit, et
|
|
ne demande un mot de passe qu'en dernier recours
|
|
- **Import de votre `~/.ssh/config`** : les `Host` deja definis (HostName, User, Port,
|
|
IdentityFile, ProxyJump, ForwardAgent...) apparaissent directement dans le panneau lateral
|
|
(section "⚙ Configuration OpenSSH"), utilisables sans les ressaisir, ou importables comme
|
|
session PySSH en un clic
|
|
- **Bastion / ProxyJump** : une session peut passer par un rebond SSH, choisi parmi vos sessions
|
|
existantes, un `Host` de `~/.ssh/config`, ou une configuration manuelle. Un groupe de sessions
|
|
peut aussi heriter d'un bastion commun (voir Groupes ci-dessous)
|
|
- **Tunnels SSH** (bouton "🔀 Tunnels" dans la barre d'un onglet SSH) : redirection locale
|
|
(`-L`, atteindre depuis votre poste un service qui n'ecoute que sur la boucle locale du
|
|
serveur), distante (`-R`), et **proxy SOCKS dynamique** (`-D`, pour faire transiter tout le
|
|
trafic d'une application par le tunnel)
|
|
- Verification des empreintes de cles d'hote (TOFU) avant premiere connexion, comme un client
|
|
SSH classique. Les hotes deja approuves par votre commande `ssh` (`~/.ssh/known_hosts`) sont
|
|
reconnus : pas de double confirmation d'empreinte
|
|
- Gestionnaire des hotes connus (menu "Hotes connus") : si une adresse en DHCP change de
|
|
machine, la cle d'hote presentee ne correspond plus a celle enregistree — l'application le
|
|
detecte et propose de remplacer l'ancienne entree directement au moment de la connexion
|
|
|
|
### Organisation
|
|
|
|
- Carnet d'adresses de serveurs (JSON local, `data/sessions.json`) organise par dossiers,
|
|
connexion en un clic
|
|
- **Tags** (plusieurs par session) avec filtre/recherche dans le panneau lateral
|
|
- **Groupes** : un dossier de sessions peut porter des parametres par defaut (utilisateur, port,
|
|
cle SSH, bastion, keepalive) via clic droit > "Parametres du groupe...". Le bouton
|
|
"↓ Heriter du groupe" du formulaire de session les reprend en pre-remplissage, sans empecher
|
|
de les modifier au cas par cas pour une session donnee
|
|
- **Workspaces** : sauvegardent l'etat complet d'une disposition de travail — onglets ouverts,
|
|
ordre, divisions et tailles de panneaux, sessions associees — et la restaurent en un clic
|
|
(menu "Workspaces", `Ctrl+Shift+W`). Une case optionnelle restaure le dernier workspace au
|
|
demarrage. **Aucun mot de passe n'est jamais stocke dans un workspace**
|
|
- **Recherche rapide** (`Ctrl+K`) : palette façon lanceur d'applications, recherche floue parmi
|
|
sessions, hotes OpenSSH, workspaces, snippets et actions, entierement au clavier
|
|
- **Snippets** : commandes enregistrees par categorie (des exemples Linux/Docker/Proxmox sont
|
|
fournis au premier lancement), gerables (creer/modifier/supprimer/rechercher) depuis le menu
|
|
"Snippets" ou le bouton "📋 Snippets" de chaque onglet. Un snippet peut etre **insere** (le
|
|
texte est ecrit, jamais valide automatiquement), **execute**, ou **execute sur tous les
|
|
onglets a la fois** via Multi-Send
|
|
- **Envoi simultane** : le bouton "⇉ Multi" du panneau lateral diffuse ce que vous tapez vers
|
|
tous les onglets terminal ouverts (y compris dans les panneaux divises), pour piloter
|
|
plusieurs serveurs d'un coup
|
|
|
|
### SFTP
|
|
|
|
- Client SFTP integre facon WinSCP : double panneau local/distant avec colonnes triables (nom,
|
|
taille, date, droits), glisser-deposer entre les panneaux, renommage (F2), suppression
|
|
(Suppr), creation de dossier, permissions distantes (chmod), et file de transferts avec
|
|
progression — le tout en arriere-plan, sans jamais geler l'interface
|
|
- **Aucun favori n'est necessaire pour l'utiliser** — trois façons d'y arriver :
|
|
- *Vous avez tape `ssh user@hote` dans un terminal* : bouton "📁 SFTP sur cette connexion"
|
|
dans la barre de l'onglet. L'hote et l'utilisateur sont deduits de la commande tapee
|
|
- *Connexion ponctuelle* : bouton "📁 SFTP" du panneau lateral, ou `Ctrl+Shift+F`
|
|
- *Session enregistree* : le panneau de fichiers est deja affiche a cote du terminal des
|
|
l'ouverture, comme dans MobaXterm ; ou clic droit > "Ouvrir en SFTP" pour un onglet dedie
|
|
|
|
### Cles et secrets
|
|
|
|
- Gestion des cles SSH : generation **Ed25519, ECDSA (courbes NIST P-256/P-384/P-521) et RSA**,
|
|
import de cles existantes, export, protection par passphrase. Sous Windows, les cles privees
|
|
generees ou importees reçoivent des permissions NTFS restreintes (ACL), equivalent du
|
|
`chmod 600` Unix
|
|
- **Coffre-fort de mots de passe optionnel** : a la creation d'une session par mot de passe, une
|
|
case "Memoriser de maniere securisee" permet de ne plus le ressaisir a chaque connexion. Le
|
|
mot de passe est alors stocke dans le **Gestionnaire d'identifiants Windows** (chiffre par
|
|
DPAPI), **jamais en clair dans un fichier de PySSH**. Sans cette case cochee (comportement par
|
|
defaut), rien n'est memorise : le mot de passe est redemande a chaque connexion, comme
|
|
aujourd'hui
|
|
|
|
### Assistant IA (optionnel, desactive par defaut)
|
|
|
|
Une aide ponctuelle pour l'administration systeme, pas un pilote automatique : **aucune commande
|
|
n'est jamais executee automatiquement**.
|
|
|
|
- **Desactive par defaut**, et sans le moindre fournisseur impose : compatible avec toute API de
|
|
type OpenAI. Se configure depuis le menu "Assistant IA > Parametres...", avec une liste
|
|
deroulante de raccourcis usuels — OpenAI, OpenRouter, Mistral AI, mais aussi **Ollama** et
|
|
**LM Studio** pour faire tourner un modele **entierement en local** — qui pre-remplit l'URL de
|
|
base, et un champ "Personnalise..." pour tout autre service compatible OpenAI (proxy interne,
|
|
autre LLM local...). Le modele, lui, reste toujours en saisie libre (pas de liste figee, chaque
|
|
installation Ollama/LM Studio ayant ses propres modeles telecharges). Bouton "Tester la
|
|
connexion" pour verifier la configuration avant de l'utiliser
|
|
- Depuis un onglet terminal, le bouton "🤖 Assistant IA" permet, sur la selection courante :
|
|
**Expliquer cette sortie**, **Diagnostiquer cette erreur**, **Proposer une solution**, ou
|
|
**Poser une question** en y ajoutant la selection comme contexte. Les reponses s'affichent
|
|
dans un panneau lateral (conversation en memoire uniquement, jamais journalisee ni ecrite sur
|
|
le disque)
|
|
- **Generer une commande** (a partir d'une description en langage naturel, sans selection
|
|
necessaire) affiche toujours la commande proposee avant tout usage, avec trois choix : la
|
|
**copier**, l'**inserer dans le terminal** (le texte est ecrit, jamais valide automatiquement),
|
|
ou **annuler**. Une commande correspondant a un motif reconnu comme destructeur (`rm -rf`,
|
|
`mkfs`, `dd`, un `DROP DATABASE`...) est signalee par un avertissement explicite
|
|
- **Avant tout envoi** au fournisseur configure : un avis de principe s'affiche une seule fois,
|
|
et chaque envoi est verifie localement a la recherche de secrets evidents (mot de passe, cle
|
|
privee, jeton d'authentification) — si quelque chose est detecte, vous choisissez de le
|
|
masquer automatiquement, de l'envoyer tel quel, ou d'annuler l'envoi
|
|
- **La cle API** passe exclusivement par le meme coffre-fort securise que les mots de passe de
|
|
session (Gestionnaire d'identifiants Windows) : jamais dans `settings.json`, jamais journalisee
|
|
|
|
## Installation (Windows)
|
|
|
|
Necessite Python 3.10+ installe sur le poste (verifiable avec `py -3 --version`).
|
|
|
|
```bash
|
|
setup.bat
|
|
```
|
|
|
|
Ce script cree un environnement virtuel local (`venv/`) et y installe les dependances (PySide6,
|
|
paramiko, pyte, cryptography, pywinpty) — rien n'est installe globalement sur le poste.
|
|
|
|
Pour contribuer au code (tests, lint, verification de types), installez plutot les dependances
|
|
de developpement :
|
|
|
|
```bash
|
|
venv\Scripts\pip install -r requirements-dev.txt
|
|
```
|
|
|
|
## Lancement
|
|
|
|
Trois facons, au choix :
|
|
|
|
- **Raccourci Windows** (recommande) : double-cliquez sur `PySSH Client.lnk` a la racine du projet.
|
|
Il lance l'application via `pythonw.exe`, donc **sans fenetre de console noire**. Vous pouvez le
|
|
copier sur le Bureau, ou l'epingler a la barre des taches.
|
|
Pour le (re)creer — apres un deplacement du projet, ou sur un autre poste :
|
|
|
|
```bash
|
|
powershell -ExecutionPolicy Bypass -File tools\create_shortcut.ps1 -Desktop
|
|
```
|
|
|
|
(`-Desktop` et `-StartMenu` sont optionnels et ajoutent une copie a ces emplacements.)
|
|
|
|
- **En ligne de commande**, sans console residuelle :
|
|
|
|
```bash
|
|
venv\Scripts\pythonw.exe main.py
|
|
```
|
|
|
|
- **`run.bat`** : garde une fenetre de console ouverte, ce qui est pratique pour voir les erreurs
|
|
si l'application refuse de demarrer.
|
|
|
|
## Version portable (sans Python installe)
|
|
|
|
```bash
|
|
build_exe.bat
|
|
```
|
|
|
|
Produit un dossier autonome `dist\PySSH Client\` contenant l'executable et tout ce dont il a besoin,
|
|
interpreteur Python compris. Copiez ce dossier ou vous voulez (y compris sur une cle USB) : il
|
|
fonctionne sur n'importe quel poste Windows, **meme sans Python installe**.
|
|
|
|
Le format retenu est un dossier plutot qu'un fichier `.exe` unique : avec PySide6, un executable
|
|
unique doit decompresser environ 200 Mo a chaque lancement, ce qui ajoute plusieurs secondes
|
|
d'attente a chaque demarrage. Le dossier demarre immediatement.
|
|
|
|
> **Note pour qui modifie la recette de build** (`PySSHClient.spec`) : `pywinpty` a besoin de deux
|
|
> executables auxiliaires, `OpenConsole.exe` (backend ConPTY) et `winpty-agent.exe` (repli WinPTY).
|
|
> PyInstaller ne suit que les DLL liees et les oublie : ils sont donc ajoutes explicitement dans
|
|
> `datas`. Sans eux, l'application se lance normalement mais **tous les terminaux se ferment
|
|
> instantanement** ("Deconnecte (Terminal ferme)").
|
|
|
|
**Ou sont stockees les donnees ?** A cote de l'executable, dans son propre sous-dossier `data\`
|
|
(mode portable : vos sessions et cles suivent la cle USB). Si cet emplacement n'est pas accessible
|
|
en ecriture — installation dans `Program Files`, support en lecture seule — l'application bascule
|
|
automatiquement sur `%APPDATA%\PySSHClient\`.
|
|
|
|
## Premiers pas
|
|
|
|
1. **Terminal local immediat** : bouton "💻 Terminal" dans le panneau lateral (ou `Ctrl+Shift+T`), ou double-clic sur "PowerShell"/"Invite de commandes" dans la section "💻 Terminaux locaux" de l'arborescence — aucune session a creer.
|
|
2. **Nouvelle session SSH** (bouton "+ Session" ou `Ctrl+N`) : renseignez l'hote, le port, l'utilisateur et le mode d'authentification. Vous avez deja un `~/.ssh/config` ? Ses `Host` apparaissent tout seuls dans la section "⚙ Configuration OpenSSH", inutile de les ressaisir.
|
|
3. **Cles SSH** (menu "Cles SSH > Gestionnaire de cles") : generez une nouvelle paire de cles ou importez-en une existante, puis selectionnez-la dans le formulaire de session.
|
|
4. Double-cliquez sur une session dans l'arborescence pour ouvrir un onglet terminal SSH.
|
|
5. **SFTP — trois facons d'y arriver**, selon la situation :
|
|
- *Vous avez tape `ssh user@hote` dans un terminal* : bouton **"📁 SFTP sur cette connexion"** dans la barre de
|
|
l'onglet. L'hote et l'utilisateur sont deduits de la commande, le formulaire est pre-rempli. Rien a enregistrer.
|
|
- *Connexion ponctuelle* : bouton **"📁 SFTP"** du panneau lateral, ou `Ctrl+Shift+F`. La case "Enregistrer dans mes
|
|
sessions" est decochee par defaut.
|
|
- *Session enregistree* : clic droit > **"Ouvrir en SFTP"** (onglet dedie). Dans une session SSH ouverte par
|
|
l'application, le panneau de fichiers est deja affiche a cote du terminal, comme dans MobaXterm.
|
|
|
|
Dans tous les cas : glisser-deposer entre les deux panneaux, F2 renomme, Suppr supprime, F5 actualise, et le clic
|
|
droit sur le panneau distant donne acces aux permissions (chmod).
|
|
6. **Aller plus vite** : `Ctrl+K` ouvre une recherche rapide parmi tout ce que vous avez deja ouvert ou enregistre.
|
|
7. **Retrouver une disposition de travail** : une fois plusieurs onglets/panneaux organises a votre gout, menu
|
|
"Workspaces > Gestionnaire de workspaces..." (`Ctrl+Shift+W`) pour l'enregistrer et la rouvrir en un clic.
|
|
8. **Assistant IA** (facultatif) : menu "Assistant IA > Parametres..." pour indiquer l'URL, le modele et la cle API
|
|
d'un fournisseur compatible OpenAI. Tant que cette etape n'est pas faite, l'assistant reste inactif et
|
|
n'envoie jamais rien nulle part.
|
|
|
|
## Structure du projet
|
|
|
|
```
|
|
main.py point d'entree
|
|
app/
|
|
main_window.py fenetre principale : assemble panneau lateral, onglets, menus
|
|
session_manager.py base de sessions (JSON), tags, parametres de bastion/keepalive/reconnexion
|
|
key_manager.py generation/import/export de cles SSH, empreintes
|
|
known_hosts_manager.py gestion du fichier known_hosts (lister/retirer/remplacer une cle d'hote)
|
|
ssh_connection.py connexion SSH en arriere-plan (paramiko + QThread), bastion, keepalive
|
|
local_terminal.py terminal local Windows (PowerShell/cmd via pywinpty)
|
|
terminal_widget.py emulateur de terminal ANSI (pyte + QPainter)
|
|
sftp_panel.py client SFTP a deux panneaux, transferts en arriere-plan
|
|
tunnels.py tunnels SSH : redirection -L / -R et proxy SOCKS dynamique -D
|
|
bastion.py resolution d'un ProxyJump (session / Host OpenSSH / manuel)
|
|
credentials.py coffre-fort optionnel (Gestionnaire d'identifiants Windows)
|
|
workspace.py, snippets.py, persistance des workspaces, snippets et parametres de groupe
|
|
groups.py, app_settings.py
|
|
theme.py feuille de style "bleu nuit" + icone d'application
|
|
ai/ assistant IA : fournisseur, redaction de secrets, detection de
|
|
commandes dangereuses, parametres, execution en arriere-plan
|
|
controllers/ logique d'assemblage : cycle de vie des onglets, panneau des
|
|
sessions, diffusion Multi-Send, restauration des workspaces
|
|
ui/ widgets d'onglet (SSH/local/SFTP), terminal divise, panneau IA
|
|
dialogs/ boites de dialogue (session, cles, hotes, tunnels, snippets,
|
|
workspaces, groupes, recherche rapide, parametres IA...)
|
|
openssh/ lecture de ~/.ssh/config
|
|
security/ protection des cles privees (ACL NTFS sous Windows)
|
|
storage/ ecriture atomique de fichiers JSON, migration de schema
|
|
services/ logique pure sans dependance Qt (detection ~cible SSH, recherche
|
|
floue, calcul du delai de reconnexion)
|
|
resources/
|
|
theme.qss feuille de style Qt "bleu nuit"
|
|
data/
|
|
sessions.json, keys.json, vos donnees locales (non versionnees) - voir .gitignore
|
|
workspaces.json, snippets.json,
|
|
groups.json, known_hosts, keys/
|
|
tests/ suite de tests (voir "Tests" ci-dessous)
|
|
```
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
pytest
|
|
```
|
|
|
|
(necessite `pip install -r requirements-dev.txt`). Chaque fichier `tests/test_*.py` reste aussi
|
|
executable individuellement (`python tests/test_xxx.py`), pratique pour rejouer un seul scenario
|
|
pendant le developpement.
|
|
|
|
Les tests construisent de vraies fenetres Qt sans les afficher (`QT_QPA_PLATFORM=offscreen`) et
|
|
couvrent, entre autres : le copier-coller et les raccourcis du terminal, la detection de l'hote
|
|
dans une commande `ssh` tapee a la main, la logique SFTP (transferts, renommage, tri, suppression
|
|
recursive) avec un serveur simule, le stockage atomique et la migration de schema, la lecture de
|
|
`~/.ssh/config`, la resolution de ProxyJump, le coffre-fort d'identifiants, la detection de
|
|
secrets et de commandes dangereuses, et l'ensemble de l'assistant IA (fournisseur HTTP, panneau,
|
|
integration dans les onglets) — sans jamais faire d'appel reseau reel.
|
|
|
|
## Qualite
|
|
|
|
```bash
|
|
ruff check app main.py tests # style et erreurs courantes
|
|
pyright # verification de types (mode progressif, voir pyproject.toml)
|
|
```
|
|
|
|
Une CI Gitea Actions (`.gitea/workflows/ci.yml`) execute le lint et les tests a chaque push, et
|
|
construit la version portable sur la branche principale.
|
|
|
|
## Limites connues / pistes d'amelioration
|
|
|
|
- L'emulation de terminal couvre les usages courants (bash, vim, htop...) mais n'est pas un xterm parfaitement complet.
|
|
- Le terminal local repose sur `pywinpty` (pseudo-console Windows). Si un onglet local reste bloque sur un ecran vide, tapez une touche : le rafraichissement de la console Windows peut etre paresseux dans certains contextes (notamment les sessions Windows non interactives) et se declenche a la premiere frappe.
|
|
- Le SFTP ne propose pas encore l'edition de fichier distant en place (telecharger / modifier / renvoyer a la main).
|
|
- Le ProxyJump ne gere qu'un seul rebond pour l'instant (pas de chainage de plusieurs bastions).
|
|
- Le coffre-fort d'identifiants (mots de passe de session, cle API IA) repose sur le Gestionnaire d'identifiants Windows : pas d'equivalent sur Linux/macOS pour l'instant, l'application fonctionne mais sans memorisation securisee sur ces systemes.
|
|
- L'application est concue et testee pour Windows uniquement (pywinpty, ACL NTFS, Gestionnaire d'identifiants).
|
|
- La verification de types (`pyright`) est volontairement en mode progressif : un socle de signalements existe encore dans `app/ui/tabs.py` et `app/ssh_connection.py` (voir `pyproject.toml`), a resorber au fil du temps plutot que d'un bloc.
|