Skip to content

Commit bcf7bc6

Browse files
committed
fix: CLAUDE.md update + rm unused old file
1 parent 8eefd39 commit bcf7bc6

2 files changed

Lines changed: 73 additions & 181 deletions

File tree

CLAUDE.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ app/ # Frontend Nuxt
6161
composables/
6262
useApi.ts # Fetch centralisé : route vers /api/local/* ou /api/ssh/* + WebSocket + session ID
6363
useLocodeConfig.ts # Config workspace persistée (.LoCode file, cache mémoire)
64+
useShare.ts # État partage collaboratif (host/guest, relay WS, présence, callbacks)
6465
plugins/
6566
monaco.client.ts # Initialisation des workers Monaco
6667
middleware/
@@ -73,17 +74,28 @@ server/
7374
read.get.ts, list.get.ts, write.post.ts, stat.get.ts, info.get.ts
7475
api/ssh/ # Routes API mode SSH (ssh2 SFTP, session-aware via X-SSH-Session header)
7576
read.get.ts, list.get.ts, write.post.ts, stat.get.ts, info.get.ts, connect.post.ts, disconnect.post.ts
77+
api/share/ # Routes API partage collaboratif (proxy fichiers + lifecycle)
78+
create.post.ts, join.post.ts, close.post.ts, leave.post.ts, settings.post.ts, info.get.ts
79+
read.get.ts, write.post.ts, list.get.ts, stat.get.ts
7680
utils/
7781
ssh.ts # Gestion sessions SSH multi-fenêtres (Map<sessionId, SSHSession>), connexion/déconnexion/SFTP
7882
session.ts # Extraction session ID depuis header X-SSH-Session
83+
share.ts # Sessions de partage (Map<shareId, ShareSession>), CRUD, relay, terminaux partagés
7984
plugins/
8085
ssh-cleanup.ts # Cleanup terminaux SSH à la perte de connexion
8186
routes/
8287
_terminal.ts # WebSocket handler + node-pty (terminal local)
8388
_ssh-terminal.ts # WebSocket handler SSH (shell channel dédié par terminal)
89+
_share.ts # WebSocket contrôle partage (présence, lifecycle events)
90+
_share-relay.ts # WebSocket relay desktop host (tunnel requêtes + I/O terminaux)
91+
_share-terminal.ts # WebSocket terminaux partagés (guests + web host)
8492
8593
electron/
8694
main.cjs # Process principal Electron (spawn Nuxt, BrowserWindow, CLI installer)
95+
preload.cjs # Preload script : expose electronSession + electronTerminal via contextBridge
96+
97+
bin/
98+
locode # Shell script Unix : lance l'AppImage ou electron en dev avec résolution de chemin
8799
88100
railway.toml # Config déploiement Railway (build web-only, start command)
89101
@@ -105,8 +117,21 @@ Les routes Nuxt server exposent les mêmes endpoints en mode local et SSH :
105117
| `/api/{local,ssh}/info` | GET | Info machine (home, user) ||
106118
| `/api/ssh/connect` | POST | Connexion SSH (retourne sessionId) | `{ host, port, username, password? }` (body JSON) |
107119
| `/api/ssh/disconnect` | POST | Déconnexion SSH | header `X-SSH-Session` |
120+
| `/api/share/create` | POST | Crée une session de partage | `{ rootPath, backendMode, hostSessionId?, allowTerminal, hostName }` |
121+
| `/api/share/join` | POST | Rejoint une session | `{ shareId, name? }``{ rootPath, guestId, allowTerminal, hostName, guests[] }` |
122+
| `/api/share/close` | POST | Ferme la session (host) | header `X-Share-Session` |
123+
| `/api/share/leave` | POST | Quitte la session (guest) | `{ shareId, guestId }` |
124+
| `/api/share/settings` | POST | Met à jour les paramètres | `{ shareId, allowTerminal }` |
125+
| `/api/share/info` | GET | État de la session | query `shareId` |
126+
| `/api/share/read` | GET | Lit un fichier (proxy direct ou relay) | headers `X-Share-Session`, `X-Guest-Id` |
127+
| `/api/share/write` | POST | Écrit un fichier (proxy direct ou relay) | headers `X-Share-Session`, `X-Guest-Id` |
128+
| `/api/share/list` | GET | Liste un dossier (proxy direct ou relay) | headers `X-Share-Session`, `X-Guest-Id` |
129+
| `/api/share/stat` | GET | Stat d'un fichier (proxy direct ou relay) | headers `X-Share-Session`, `X-Guest-Id` |
108130
| `/_terminal` | WebSocket | Shell PTY local (node-pty) | messages JSON (create/input/resize) |
109131
| `/_ssh-terminal` | WebSocket | Shell PTY distant (ssh2) | messages JSON (create/input/resize/sessionId) |
132+
| `/_share` | WebSocket | Contrôle partage (présence, events) | auth + lifecycle messages |
133+
| `/_share-relay` | WebSocket | Relay desktop host | auth + request/response + terminal I/O |
134+
| `/_share-terminal` | WebSocket | Terminaux partagés | auth + create/subscribe/input/resize/close |
110135

111136
Toutes les routes SSH requièrent le header `X-SSH-Session` contenant l'UUID de session.
112137

@@ -385,6 +410,54 @@ git tag v0.1.0 && git push --tags # déclenche le workflow
385410
- Conversion des chemins WSL → Windows via `wslpath -w` avant de passer en argument
386411
- `second-instance` handler : `w.show()` + `setAlwaysOnTop(true/false)` pour forcer la fenêtre au premier plan sur Windows (contourne la protection anti-focus-stealing)
387412

413+
### Electron preload (IPC bridge)
414+
- `electron/preload.cjs` : script de preload Electron — expose deux API au renderer via `contextBridge.exposeInMainWorld`
415+
- **`window.electronSession`** : `getInitialRoot()` (lit `?root=` depuis l'URL de la fenêtre), `setRoot(path)` (IPC `session:setRoot` pour persister le dossier courant)
416+
- **`window.electronTerminal`** : `create(opts)`, `write(id, data)`, `resize(id, cols, rows)`, `kill(id)`, `onData(cb)` → retourne unlisten, `onExit(cb)` → retourne unlisten — pont IPC vers node-pty dans le process principal
417+
418+
### Launcher Unix (`bin/locode`)
419+
- Script shell `bin/locode` : résout l'argument de chemin en absolu, cherche une AppImage dans `dist/`, la lance si trouvée, sinon fallback sur `npx electron electron/main.cjs` (mode dev)
420+
- Usage : `bin/locode .` ou `bin/locode /path/to/project`
421+
- Différent du CLI stub Windows (`cli/locode.exe` installé dans PATH) — celui-ci est pour macOS/Linux sans installation globale
422+
423+
### Session collaborative (partage)
424+
425+
#### Vue d'ensemble
426+
- `app/composables/useShare.ts` : état de partage global — `isHost`, `isGuest`, `shareId`, `guestId`, `shareInfo` (rootPath, allowTerminal, hostName, guests), WebSocket de contrôle, relay WS (desktop host), fonctions `startShare()`, `stopShare()`, `joinShare()`, `leaveShare()`
427+
- `app/components/ShareModal.vue` : modal UI — création de partage (copie lien, liste guests, toggle terminal, stop), join session (input lien + nom optionnel)
428+
- `server/utils/share.ts` : gestionnaire de sessions (`Map<shareId, ShareSession>`), CRUD, relay request/response avec timeout 30s, registry terminaux partagés avec buffer replay 50KB, `broadcastControl()` vers tous les peers de contrôle
429+
- `server/routes/_share.ts` : WebSocket de contrôle — enregistre host/guest peers, relaie events (guest-joined, guest-left, share-closed, settings-changed, terminal-added, terminal-removed), déconnexion host → `closeShare()`
430+
- **Mode direct** (web host, SSH sur Railway) : requêtes fichiers guests proxifiées directement via `getSftp(hostSessionId)` ou fs Node.js local
431+
- **Mode relay** (desktop host) : Railway envoie la requête via `_share-relay` WS → desktop exécute localement → réponse retournée → Railway répond au guest
432+
- Sécurité : `isPathWithinRoot()` valide tous les chemins fichiers guests contre le `rootPath` de la session
433+
434+
#### Architecture relay pour terminaux
435+
- En mode relay (desktop host), les terminaux partagés utilisent le pont IPC Electron (`window.electronTerminal`) pour spawner le PTY dans le process principal Electron — pas le WebSocket `/_terminal` (Nuxt subprocess) qui échoue avec `posix_spawnp failed` dans ce contexte
436+
- `index.vue` gère une `Map<string, RelayTerminalHandle>` (`relayTerminals`) qui abstrait Electron IPC ou WebSocket selon le contexte (Electron vs web/SSH)
437+
- Chaque handle expose `sendInput(data)`, `sendResize(cols, rows)`, `close()` — les callbacks relay (`onRelayTerminalCreate/Input/Resize/Close`) appellent la méthode appropriée selon l'environnement
438+
439+
#### Pattern setter ESM pour callbacks relay
440+
- Les variables `onRelayTerminalCreate`, `onRelayTerminalInput`, `onRelayTerminalResize`, `onRelayTerminalClose`, `onShareClosed` dans `useShare.ts` sont des `let` privés exposés via des fonctions setter exportées (`setOnRelayTerminalCreate`, etc.)
441+
- **Raison** : les bindings `export let` sont en lecture seule dans les namespaces ES modules — l'assignation `mod.onXxx = callback` échoue silencieusement dans les builds Rollup/Vite production. Les setters contournent ce problème
442+
- `index.vue` importe et appelle directement ces setters de manière synchrone (plus d'IIFE async `await import(...)`)
443+
444+
#### Nettoyage propre à l'arrêt du partage
445+
- Le watcher `isSharing` dans `TerminalPanel.vue` gère les deux transitions (start **et** stop) : vide la liste des sessions et réinitialise l'état dans les deux cas
446+
- À l'arrêt (`isSharing` passe à `false`) : `index.vue` appelle `terminalPanelRef.value?.ensureSession()` pour créer des sessions terminales normales fraîches
447+
- Les handles relay (`relayTerminals`) sont tous fermés dans `onShareStopped()` avant la re-création des sessions
448+
449+
#### `terminal-ready` pour les abonnés
450+
- `server/routes/_share-terminal.ts` : le handler `subscribe` envoie maintenant un message `{ type: "terminal-ready", terminalId, name }` en confirmation — permet au client de définir `sharedTerminalId` et d'activer correctement le routage input/resize
451+
- Sans cette confirmation, les guests abonnés à un terminal existant ne pouvaient pas envoyer d'input
452+
453+
#### Sync dimensions PTY après `terminal-ready`
454+
- `Terminal.client.vue` : après réception de `terminal-ready` (création et abonnement), force un `doFit()` + envoie `resize` avec les dimensions réelles de l'xterm
455+
- Corrige le glitch zsh `%` (PROMPT_SP/PROMPT_CR) : le caractère apparaissait quand les dimensions PTY ne correspondaient pas aux dimensions xterm au moment de l'abonnement
456+
457+
#### Restauration de session Electron (Windows/Linux)
458+
- `electron/main.cjs` : sur Windows/Linux, `win.on("closed")` se déclenche **avant** `app.on("before-quit")` — au moment où `before-quit` s'exécute, la map `windows` est déjà vide, donc `saveSessions()` sauvegardait `[]`
459+
- Fix : dans le handler `closed`, si c'est la dernière fenêtre et que `isQuitting` est false, écrire directement le fichier de session avant de supprimer la fenêtre de la map
460+
388461
### Refactoring code cleanup
389462
- Suppression de `server/api/[...url].ts` (ancien proxy Deno catch-all, remplacé par routes `/api/local/*` et `/api/ssh/*`)
390463
- `useApi.ts` simplifié : déduplication logique protocole dans `getWsUrl()`, appel unique `getStoredSSHTarget()` par fonction

backend/server.ts

Lines changed: 0 additions & 181 deletions
This file was deleted.

0 commit comments

Comments
 (0)