Florian add1073e37
Some checks failed
CI / lint (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build (push) Has been cancelled
Corriger la fleche invisible des menus deroulants (QComboBox)
Signale par l'utilisateur sur le nouveau champ "Fournisseur" de l'assistant
IA : aucune fleche visible, indiscernable d'un simple champ de texte - alors
que la liste s'ouvre bien au clic (le bug est purement visuel).

Cause : QComboBox::drop-down { border: none; width: 22px; } dans theme.qss
suffit a faire que Fusion (le style applique a toute l'application) arrete de
dessiner sa fleche par defaut. Ce n'est pas specifique au nouveau champ : tous
les QComboBox de l'application (authentification, cle SSH, categorie de
snippet, filtre de tags...) etaient concernes de la meme facon.

Tente d'abord de redessiner la fleche en QSS pur (triangle via des bordures) :
verifie visuellement (rendu hors-ecran + capture d'image) avant de conclure -
rendu un simple rectangle plein, pas un triangle, ce style ne supporte pas
cette technique correctement. Solution retenue : supprimer entierement la
regle ::drop-down plutot que de la remplacer, ce qui laisse Fusion dessiner
nativement sa propre fleche - verifie sur les 3 themes (clair/bleu nuit/noir),
lisible et bien contrastee partout sans configuration supplementaire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 08:16:36 +02:00
2026-09-04 06:50:10 +02:00
2026-09-04 06:50:10 +02:00

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).

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.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 :

    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 :

    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) : 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

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 dans app/ui/tabs.py et app/ssh_connection.py (voir pyproject.toml), a resorber au fil du temps plutot que d'un bloc.
Description
Client SSH/SFTP personnel pour Windows : terminaux multi-onglets, gestionnaire de fichiers SFTP integre, gestion des cles SSH. 100% local, sans compte ni service tiers.
Readme 596 KiB
Languages
Python 99.4%
Batchfile 0.3%
PowerShell 0.3%