Nouveau paquet packages/desktop (Electron, autonome, HORS des workspaces racine pour ne pas alourdir la CI du daemon). Process principal : lance le daemon en enfant (Node), lit le handshake token sur le fd 3, pose le cookie de session server-to-server (session.fetch) puis charge la SPA sur 127.0.0.1 (aucun écran de login). Fenêtre sûre (contextIsolation, sandbox, nodeIntegration off, preload minimal), instance unique, liens externes vers le navigateur, permissions limitées aux notifications, état de fenêtre persistant, arrêt propre du daemon (SIGTERM puis SIGKILL).
Vérifié ici : tsc contre l'API Electron réelle + bundle esbuild (main/preload). Le run GUI et le packaging se font sur une machine avec affichage (C2+). Racine workspaces passée en liste explicite (5 paquets) pour exclure desktop du npm ci racine.
Nouveau workspace packages/vscode (git-arboretum, privé, non publié sur npm), client REST/WS
réutilisant @arboretum/shared. Auth Authorization: Bearer sur REST et l'upgrade WS (via `ws`) ;
un client Node sans en-tête Origin passe le check Origin strict du serveur.
- Arbres temps réel : Repositories (repos → worktrees → sessions) et Groups, via le WebSocket.
- Terminaux natifs (vscode.Pseudoterminal) pour attacher/observer une session — rendu et
scrollback de VS Code ; décodage UTF-8 streaming + comptabilité ACK dans des modules purs.
- Status bar (compteur waiting) + notifications natives sur passage en waiting, réponses Yes/No
via la commande WS answer.
- Mutations git (create worktree, commit, push, promote), start/kill/hide/resume/fork, session
de groupe ; conscience du workspace (reveal + start/create here).
- Bundle esbuild (format cjs, external vscode) inlinant @arboretum/shared → VSIX autonome.
Logique réutilisable sans import vscode → testée par vitest (19 tests).
- CI : .gitea/workflows/vscode-release.yml package le VSIX sur tag vscode-vX.Y.Z (artefact +
asset de release best-effort). build:vscode hors du build principal (comme le site).
- spikes/s5-vscode/STUDY.md : décision de conception (GO phasé A→D), marquée implémentée.
Le commit e7c1a60 a embarqué un package-lock.json antérieur à l'ajout du
workspace @arboretum/site et de la dépendance @fontsource/jetbrains-mono :
`npm ci` (ci.yml) aurait échoué. Régénéré via `npm install --package-lock-only`
(purement additif, aucune autre dépendance touchée).
Première version avec le tarball autonome (shared inliné dans dist/_shared).
Le 1.4.0 du registre a pu être publié sans @arboretum/shared embarqué
(bundleDependencies d'un workspace instable en CI/root) ; 1.4.1 garantit un
paquet installable et démarrable par n'importe quel consommateur.
bundleDependencies d'un paquet *workspace* est instable selon l'environnement npm
(mode -w, exécution en root sur un runner CI) : npm voyait shared comme un lien
workspace et n'embarquait AUCUN fichier ("bundled files: 0"), publiant un tarball
cassé — `Cannot find module '@arboretum/shared'` au démarrage chez le consommateur.
C'est ce que détectait (à juste titre) le job pack-smoke.
Nouveau mécanisme, identique sur tout environnement : scripts/inline-shared.mjs
(hook prepack) copie le JS compilé de shared dans dist/_shared/ et réécrit l'import
bare '@arboretum/shared' des .js du serveur vers ce chemin relatif. shared passe en
devDependency (résolu en dev via le symlink workspace, jamais exigé du consommateur).
Plus de node_modules embarqué ni de symlink dans le tarball.
ci.yml (pack-smoke) packe désormais en -w (comme release.yml) et asserte l'autonomie
(dist/_shared présent + zéro import bare) avant le boot smoke.
Validé de bout en bout sur checkout propre : npm ci + build + pack -w + install dans
projet vierge + boot -> HTTP 401, et 263 tests verts.
Republie pour rafraîchir le README embarqué (page du paquet) :
- doc d'install sans token (paquet public)
- README français + sélecteur de langue
- copy-meta réécrit le lien FR en absolu (page du paquet)
- renomme le paquet en @johanleroy/git-arboretum (scope routé vers le registre npm Gitea privé)
- embarque @arboretum/shared via bundleDependencies (scripts/vendor-shared.mjs au prepack)
- joint README/LICENSE au tarball (scripts/copy-meta.mjs) + metadata, keywords, publishConfig
- CI pack-smoke en mono-tarball avec assertion bundleDep ; nouveau workflow release.yml (publish sur tag v*)
- version 1.0.0 ; README mis à jour (install scopé + service systemd)
- PWA: public/manifest.webmanifest + icon.svg + meta index.html (theme-color, apple-touch-icon)
- public/sw.js: service worker push-only (push → showNotification tag=sessionId ;
notificationclick → focus/ouvre /sessions/<id>) — pas de précache (hors périmètre)
- lib/push.ts + stores/push.ts: enable/disable, abonnement VAPID, enregistrement SW au boot
- DashboardView: toggle « Notifications » (gardé par pushSupported) + i18n en/fr
- campagne de fiabilité (verdict S3): classification de tous les types de dialogue sur captures
réelles (trust/permission×2/question) + refus Esc (deny-esc2) + plan (synthétique)
- acceptance-p4.mjs: VAPID/auth/Origin, subscribe idempotent/unsubscribe, answer (rejets)
Choix: SW écrit à la main plutôt que vite-plugin-pwa (qui tirait ~295 paquets Workbox +
2 vulns esbuild high pour un SW push-only). Zéro dépendance front nouvelle. 175 tests verts,
acceptance p1..p4 ALL GREEN.
Fan-out integration + fixes found by the test/acceptance pass:
- FIX ring-buffer: chunks >= capacity skipped bytes now count into the
monotonic offset (invariant: stream byte k lives at k % capacity) —
window order was corrupted on unaligned big chunks
- FIX auth: non-numeric cookie expiry no longer bypasses expiration
- FIX protocol: safe-integer validation on ack.bytes / hello.protocol
- FIX @fastify/websocket v11: websocket route must be registered in an
encapsulated context after plugin load (handler got REST signature)
- FIX flow-control deadlock found by e2e acceptance: client only ACKs
on data receipt, so pausing with an unACKed residue in (LOW,
ACK_EVERY] stalled both sides at 0.9 MB. ACK_EVERY now 64 KiB (<=
LOW invariant, tested) + trailing debounced ACK in the web client
- Web: Vue 3 + Vite + Pinia + Tailwind 4 + vue-i18n (EN/FR) + xterm 6
(fit + webgl fallback), multiplexed ws-client with reconnect/backoff
and resync epochs
- Tests: 100 vitest (protocol fuzz, ring edges, auth, pty-manager flow
control with mocked pty, REST e2e) ; CI Node 22/24 + pack-smoke
- scripts/acceptance-p1.mjs: real daemon + real WS client — boot,
login, attach, stdin, 10 MB flood w/ ACK (13.7 MB/1.9s, RSS bounded),
brutal disconnect + replay resync, kill broadcast, SIGTERM drain