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.
132 lines
8.3 KiB
Markdown
132 lines
8.3 KiB
Markdown
# Spike S5 — Extension VSCode (intégration native) — ÉTUDE : ✅ GO (phasé)
|
|
|
|
> **✅ IMPLÉMENTÉ** dans `packages/vscode` (`git-arboretum`, privé) — phases A→D livrées : connexion +
|
|
> auth SecretStorage, arbres temps réel (Repos/Groups), terminaux natifs `Pseudoterminal`, status bar +
|
|
> notifications `waiting` (commande `answer`), mutations git, conscience du workspace. Bundle esbuild
|
|
> (shared inliné), packagé en VSIX (`npm run build:vscode` puis `vsce package --no-dependencies`).
|
|
> CI : `.gitea/workflows/vscode-release.yml` sur tag `vscode-vX.Y.Z`. L'étude ci-dessous reste la
|
|
> justification de conception.
|
|
|
|
|
|
Étude de conception (pas de mesure Go/No-Go : c'est un design + plan d'implémentation phasé). Objectif
|
|
validé avec l'utilisateur : **une vraie intégration native dans VSCode, pas un simple webview** — une
|
|
connexion réelle avec ce qui se passe dans l'éditeur (terminaux natifs, arbre, statut, notifications,
|
|
conscience du workspace).
|
|
|
|
## Contexte & contrainte
|
|
|
|
Arboretum est déjà un daemon qui sert une SPA et expose :
|
|
- une **API REST** (`/api/v1/...`) : repos, worktrees (+ mutations git commit/push/promote), sessions,
|
|
groupes, auth ;
|
|
- un **protocole WebSocket** multiplexé (`@arboretum/shared` → `protocol.ts`) : attach/stdin/resize,
|
|
frames binaires `[type u8][channel u32le][payload]`, flow-control par watermarks (`FLOW`), events
|
|
temps réel `sessions`/`worktrees`/`groups`, commande `answer` (dialogues), `hello`/`hello_ok`.
|
|
|
|
Un plugin n'a donc **rien à réimplémenter côté logique** : il est un **client** de cette API. La question
|
|
n'est pas « comment afficher le dashboard » mais « comment exposer Arboretum avec les primitives natives
|
|
de VSCode ». Le webview (iframe du dashboard) reste un *fallback* optionnel ; ce n'est pas le cœur.
|
|
|
|
## Pourquoi pas « juste un webview »
|
|
|
|
- Un webview = iframe vers `http://127.0.0.1:7317` : lourd, isolé (CSP stricte, `acquireVsCodeApi`
|
|
indisponible dans l'iframe), pas d'intégration avec les terminaux/arbres/statut de VSCode, auth à
|
|
re-bricoler (cookie/token dans une iframe cross-origin). On retrouve les bugs xterm-in-webview
|
|
(rendu, scrollback) qu'on cherche justement à éviter.
|
|
- Une intégration native réutilise le **rendu terminal, le scrollback, le copier/coller, les liens** de
|
|
VSCode (gratuitement robustes), et place Arboretum là où l'utilisateur travaille déjà.
|
|
|
|
## Architecture proposée — `packages/vscode`
|
|
|
|
Nouveau workspace privé `@arboretum/vscode`, extension TypeScript, packagée en **VSIX** via `vsce`
|
|
(build `esbuild`). Le `@arboretum/shared` est résolu en dev par le symlink workspace et **inliné** au
|
|
packaging (même principe que `scripts/inline-shared.mjs` côté serveur) → VSIX autonome.
|
|
|
|
### Connexion au daemon
|
|
- Réglages : `arboretum.url` (défaut `http://127.0.0.1:7317`), `arboretum.token`.
|
|
- **Auth** : commande `Arboretum: Sign in` qui demande le token et le stocke dans **VS Code
|
|
SecretStorage** (les tokens daemon sont stockés *hashés* et le bootstrap ne s'affiche qu'une fois →
|
|
impossible de « lire » le token existant ; on le saisit une fois). Option avancée : commande qui
|
|
**spawn** le daemon (`npx @johanleroy/git-arboretum`) et capture le bootstrap token au premier
|
|
démarrage.
|
|
- Le check Origin strict du daemon ne s'applique qu'aux navigateurs (en-tête `Origin`) : un client
|
|
Node/extension n'envoie pas d'`Origin` → OK ; l'auth par token (en-tête/cookie) reste requise.
|
|
|
|
### Réutilisation du protocole (le « vrai » lien)
|
|
Importer `@arboretum/shared` : `decodeBinaryFrame`/encodage, `BINARY_FRAME`, `FLOW`, `PROTOCOL_VERSION`,
|
|
types de messages et `SessionSummary`/`WorktreeSummary`/`GroupSummary`. Le pont terminal et la liste
|
|
restent **synchronisés** avec le serveur par construction.
|
|
|
|
### Surfaces natives
|
|
|
|
1. **Activity Bar + TreeView(s)** (`vscode.window.createTreeView`) : arbre **Repos → Worktrees →
|
|
Sessions**, avec badges d'état fins (`activity` busy/waiting/idle, `dialog`) déjà portés par
|
|
`SessionSummary`. Une vue **Groupes** en parallèle. Rafraîchi en temps réel par les events WS
|
|
(`onDidChangeTreeData`).
|
|
|
|
2. **Terminaux VSCode natifs via `Pseudoterminal`** *(pièce maîtresse)* :
|
|
`vscode.window.createTerminal({ name, pty })` où le `Pseudoterminal` ponte la session Arboretum :
|
|
- `open()` → `wsClient.attach(sessionId)` ;
|
|
- OUTPUT WS (frames binaires) → `writeEmitter.fire(text)` (`onDidWrite`) ;
|
|
- `handleInput(data)` → WS `stdin` ;
|
|
- `setDimensions({columns, rows})` → WS `resize` ;
|
|
- `close()` → `detach`.
|
|
On réutilise le **renderer + scrollback natifs de VSCode** : règle au passage, côté VSCode, les
|
|
problèmes de rendu et d'historique de la SPA. Flow-control : réutiliser `FLOW`/les ACK du protocole
|
|
(envoyer un ACK des octets écrits) pour ne pas saturer.
|
|
|
|
3. **Status bar** (`createStatusBarItem`) : compteur des sessions `waiting` (à traiter) ; clic → focus
|
|
de l'arbre / quick-pick des sessions en attente.
|
|
|
|
4. **Notifications natives** : sur transition d'une session vers `waiting` (event WS), `showInformation
|
|
Message` avec actions (« Ouvrir le terminal », « Oui »/« Non » → commande WS `answer`). Doublonne
|
|
utilement le Web Push : ici, natif dans l'éditeur.
|
|
|
|
5. **Conscience du workspace** : mapper `workspace.workspaceFolders` → repo/worktree Arboretum (par
|
|
chemin). Actions contextuelles : « Démarrer une session Claude ici », « Créer un worktree pour ce
|
|
repo », et les mutations git existantes (commit/push/promote via REST). Met aussi en évidence dans
|
|
l'arbre le worktree correspondant au dossier ouvert.
|
|
|
|
6. **Commandes** (`contributes.commands` + palette VSCode) : sign in, ouvrir le dashboard (webview
|
|
*optionnel*), rafraîchir, attacher/observer une session, créer worktree, commit/push/promote,
|
|
start/hide session, répondre à un dialogue.
|
|
|
|
7. **Webview** : seulement en *option* (vue dashboard complète pour qui la veut). UX primaire = native.
|
|
|
|
## Découpage phasé (livrable séparé — non implémenté dans ce lot)
|
|
|
|
- **A — Lecture & connexion** : auth (SecretStorage) + client REST/WS + TreeView lecture seule
|
|
(repos/worktrees/sessions/groupes) en temps réel. *Valeur immédiate, risque faible.*
|
|
- **B — Terminaux natifs** : `Pseudoterminal` attach/observe via pont WS réutilisant `@arboretum/shared`
|
|
(flow-control inclus). *Le cœur de la valeur ; à faire juste après A.*
|
|
- **C — Actions & supervision** : mutations (créer worktree, commit/push/promote, start/hide), status
|
|
bar `waiting`, notifications + commande `answer`.
|
|
- **D — Workspace-aware** : mapping dossier ↔ repo/worktree + actions contextuelles.
|
|
|
|
## Intégration monorepo & packaging
|
|
|
|
- `packages/vscode/` : `package.json` d'extension (`engines.vscode`, `activationEvents`,
|
|
`contributes.{commands,views,viewsContainers,configuration}`), build `esbuild` (bundle Node), test via
|
|
`@vscode/test-electron` (optionnel). Publication : VSIX (`vsce package`) — le registre privé Gitea ne
|
|
sert pas d'extensions VSCode, donc distribution par VSIX (et/ou Open VSX) plutôt que le Marketplace si
|
|
l'on veut rester privé.
|
|
- Le `tsconfig` reste hors du `tsc -b` server/shared actuel (build séparé) pour ne pas alourdir le
|
|
typecheck CI existant.
|
|
|
|
## Risques / questions ouvertes
|
|
|
|
- **Auth bootstrap** : saisie manuelle du token vs spawn+capture. MVP = saisie manuelle (SecretStorage).
|
|
- **Flow-control côté extension** : reprendre `FLOW` (ACK par octets) pour les terminaux natifs ; un
|
|
`Pseudoterminal` n'expose pas de back-pressure → ACK « ce qui a été émis dans `onDidWrite` ».
|
|
- **Remote/SSH/Codespaces** : si l'on veut le webview fallback, prévoir `vscode.env.asExternalUri` +
|
|
`portMapping`. Les surfaces natives (REST/WS via `arboretum.url`) restent valides tant que le daemon
|
|
est joignable depuis l'hôte de l'extension.
|
|
- **Multi-fenêtres VSCode** : une connexion WS par fenêtre (acceptable, outil mono-utilisateur).
|
|
|
|
## Verdict
|
|
|
|
**GO** sur une extension native `packages/vscode`, en commençant par **A → B** (TreeView temps réel +
|
|
terminaux natifs `Pseudoterminal`), qui apportent l'essentiel de la valeur et réutilisent directement
|
|
l'API REST + le protocole WS de `@arboretum/shared`. Le webview n'est qu'un fallback optionnel.
|
|
Références API : Webview, Tree View, Pseudoterminal/`createTerminal`, StatusBarItem, SecretStorage,
|
|
`asExternalUri` (docs officielles VSCode).
|