Signale par l'utilisateur : "Expliquer cette sortie" n'avait aucun effet
utilisable, le menu restait grise. Cause reelle : ces actions exigeaient une
selection prealable dans le terminal, alors que l'attente naturelle ("explique
ce que j'ai a l'ecran") est de pouvoir les utiliser directement sur ce qui est
affiche - confirme avec l'utilisateur avant d'implementer (deux options
proposees : rabattre sur l'ecran visible, ou juste clarifier le blocage).
app/terminal_widget.py : nouvelle methode visible_text() (l'ecran actuellement
affiche, sans l'historique de scroll - au contraire de buffer_text() qui sert
a la detection de commande ssh et remonte l'historique proche).
app/ui/tabs.py : Expliquer/Diagnostiquer/Proposer une solution/Poser une
question utilisent desormais _ai_source_text() = la selection si elle existe
(pour cibler precisement), sinon l'ecran visible. Les 3 premieres actions du
menu ne sont plus desactivees des qu'il n'y a pas de selection, mais
seulement si vraiment rien n'est affiche ; "Poser une question" reste
utilisable meme un terminal vide (contexte optionnel, comme le bouton
"Nouvelle question" du panneau).
tests/test_ai_tab_integration.py : reecriture des tests 1 et 2 (etats
d'activation du menu) + 2 nouveaux tests verifiant explicitement le repli
(sans selection, le texte envoye est bien celui de l'ecran) et sa priorite
inverse (une selection existante prime toujours sur l'ecran visible).
tests/test_clipboard.py : couverture directe de visible_text() (ecran complet,
lignes vides de fin retirees). Suite complete revalidee (34/34), ruff propre.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
19 KiB
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+Inserrestent disponibles au clavier, etCtrl+Creste l'interruption comme dans tout terminal - Confort d'affichage : zoom
Ctrl+moletteouCtrl+ +/-(Ctrl+0remet a zero, la taille est memorisee), barre de defilement de l'historique,Maj+PagePrec/Suiv,Ctrl+Finpour revenir en direct, et recherche dans l'historique avecCtrl+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~/.sshsans 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: lesHostdeja 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
Hostde~/.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@hotedans 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
- Vous avez tape
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 600Unix - 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 : Expliquer cette sortie, Diagnostiquer cette erreur, Proposer une solution, ou Poser une question. Ces actions portent sur le texte selectionne s'il y en a un (pour cibler precisement une partie d'une sortie longue), et se rabattent sinon automatiquement sur tout l'ecran visible du terminal — inutile de selectionner quoi que ce soit pour un simple "explique-moi ce que j'ai sous les yeux". 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, unDROP 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).
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 :
venv\Scripts\pip install -r requirements-dev.txt
Lancement
Trois facons, au choix :
-
Raccourci Windows (recommande) : double-cliquez sur
PySSH Client.lnka la racine du projet. Il lance l'application viapythonw.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 :powershell -ExecutionPolicy Bypass -File tools\create_shortcut.ps1 -Desktop(
-Desktopet-StartMenusont optionnels et ajoutent une copie a ces emplacements.) -
En ligne de commande, sans console residuelle :
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)
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) :pywinptya besoin de deux executables auxiliaires,OpenConsole.exe(backend ConPTY) etwinpty-agent.exe(repli WinPTY). PyInstaller ne suit que les DLL liees et les oublie : ils sont donc ajoutes explicitement dansdatas. 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
-
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. -
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? SesHostapparaissent tout seuls dans la section "⚙ Configuration OpenSSH", inutile de les ressaisir. -
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.
-
Double-cliquez sur une session dans l'arborescence pour ouvrir un onglet terminal SSH.
-
SFTP — trois facons d'y arriver, selon la situation :
- Vous avez tape
ssh user@hotedans 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).
- Vous avez tape
-
Aller plus vite :
Ctrl+Kouvre une recherche rapide parmi tout ce que vous avez deja ouvert ou enregistre. -
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. -
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
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
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 dansapp/ui/tabs.pyetapp/ssh_connection.py(voirpyproject.toml), a resorber au fil du temps plutot que d'un bloc.