[{"data":1,"prerenderedAt":43},["ShallowReactive",2],{"doc:fr:plugins\u002Fcapabilities":3},{"title":4,"lede":5,"html":6,"headings":7},"Les capacités de l’hôte","Un plugin en WASM n’atteint rien par lui-même : ni le réseau, ni le disque, ni un programme. Cette page dit ce qu’il peut demander à Escouade, ce que chaque capacité permet, ce qu’Escouade refuse et ce que la fenêtre d’installation en dit.","\u003Ch2 id=\"le-principe\">Le principe\u003C\u002Fh2>\n\u003Cp>Une capacité suit toujours le même chemin, pour \u003Ccode class=\"key\">run.js\u003C\u002Fcode> comme pour \u003Ccode class=\"key\">run.wasm\u003C\u002Fcode> :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Déclarée\u003C\u002Fstrong> au manifeste, dans \u003Ccode class=\"key\">permissions\u003C\u002Fcode> ou \u003Ccode class=\"key\">limits\u003C\u002Fcode>. Ce qui n’est pas déclaré répond l’erreur \u003Ccode class=\"key\">-32003\u003C\u002Fcode>, et le journal le garde.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Appliquée par Escouade\u003C\u002Fstrong> : le module n’a ni socket, ni descripteur de fichier, ni process. Chaque appel passe par l’hôte, qui le juge.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Dite\u003C\u002Fstrong> dans la fenêtre d’installation, ligne par ligne, avant que rien ne tourne. L’utilisateur peut retirer chaque ligne.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Gardée au journal\u003C\u002Fstrong> : la méthode et l’hôte ou la racine, jamais le chemin, la requête ou les en-têtes. Une commande y va avec ses arguments, secrets masqués.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Un plugin qui demande tout, pour montrer la forme :\u003C\u002Fp>\n\u003Cp>\u003Ccode>sdk\u002Fdoc-examples\u002Frust\u002Fescouade-plugin.json\u003C\u002Fcode> (extrait)\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">  &quot;permissions&quot;: {\n    &quot;events&quot;: [&quot;ticket.column&quot;],\n    &quot;network&quot;: [&quot;api.example.dev&quot;, &quot;{account}&quot;],\n    &quot;secrets&quot;: [&quot;token&quot;],\n    &quot;files&quot;: [\n      { &quot;root&quot;: &quot;plugin&quot;, &quot;access&quot;: &quot;read&quot; },\n      { &quot;root&quot;: &quot;temp&quot;, &quot;access&quot;: &quot;write&quot; },\n      { &quot;root&quot;: &quot;agent&quot;, &quot;access&quot;: &quot;write&quot; },\n      { &quot;root&quot;: &quot;agentHome&quot;, &quot;access&quot;: &quot;read&quot; }\n    ],\n    &quot;commands&quot;: [&quot;git status --short&quot;, &quot;git log *&quot;]\n  },\n  &quot;limits&quot;: { &quot;fetchSeconds&quot;: 60, &quot;fetchBytes&quot;: 8388608, &quot;fileBytes&quot;: 1048576 },\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Capacité\u003C\u002Fth>\n\u003Cth>Au manifeste\u003C\u002Fth>\n\u003Cth>La fenêtre d’installation dit\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>Un hôte\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">permissions.network\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>« Se connecte à », puis l’hôte\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Les instances de ses comptes\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">{account}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>« Se connecte aux instances que tu lui donnes »\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Le serveur local d’un harness\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">local:shared\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>« Parle au serveur local du harness, lancé par Escouade »\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Lire ou écrire un dossier\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">permissions.files\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>« Lit les fichiers de tes agents », « Écrit dans les dossiers de tes agents »…\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Lancer un programme\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">permissions.commands\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>« Lance cette commande sur ton ordinateur », puis la commande\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Des limites plus hautes\u003C\u002Ftd>\n\u003Ctd>\u003Ccode class=\"key\">limits\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Rien de plus\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Des minuteurs\u003C\u002Ftd>\n\u003Ctd>rien, c’est un droit de base\u003C\u002Ftd>\n\u003Ctd>Rien\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Ch2 id=\"le-reseau\">Le réseau\u003C\u002Fh2>\n\u003Cp>Tout ce qui sort passe par \u003Ccode class=\"key\">net\u002Ffetch\u003C\u002Fcode>, \u003Ccode class=\"key\">net\u002Fstream\u003C\u002Fcode> ou \u003Ccode class=\"key\">ws\u002Fopen\u003C\u002Fcode> : voir \u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fcalls\u002F#le-reseau\">les appels\u003C\u002Fa>. Un plugin en JS garde le \u003Ccode class=\"key\">fetch\u003C\u002Fcode> standard, un plugin en Rust le client du kit.\u003C\u002Fp>\n\u003Ch3 id=\"un-hote-nomme\">Un hôte nommé\u003C\u002Fh3>\n\u003Cp>\u003Ccode>&quot;network&quot;: [&quot;api.example.dev&quot;]\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’elle permet.\u003C\u002Fstrong> Joindre cet hôte et ses sous-domaines, en https. \u003Ccode class=\"key\">*.example.dev\u003C\u002Fcode> prend les sous-domaines sans l’hôte lui-même.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’Escouade refuse.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>http, et tout autre hôte ;\u003C\u002Fli>\n\u003Cli>une adresse IP écrite dans l’URL, que \u003Ccode class=\"key\">permissions.network\u003C\u002Fcode> ne nomme pas ;\u003C\u002Fli>\n\u003Cli>une adresse de la machine ou du réseau local, même pour un hôte déclaré : bouclage, privée, de lien local, non spécifiée, de multidiffusion ou de diffusion, en IPv4 comme en IPv6. Escouade juge les adresses que la connexion emploie, après la résolution du nom ;\u003C\u002Fli>\n\u003Cli>les redirections : le plugin reçoit la réponse 3xx et décide.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Un appel refusé répond \u003Ccode class=\"key\">-32003\u003C\u002Fcode>. Un appel qui échoue en route répond \u003Ccode class=\"key\">-32004\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Réglages › Réseau.\u003C\u002Fstrong> Le proxy, ses exclusions et les certificats non vérifiés sont suivis, et un changement atteint l’appel suivant, sans relancer le plugin. Par un proxy, seul le nom est jugé, et le journal le dit.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>La fenêtre\u003C\u002Fstrong> dit « Se connecte à », puis chaque hôte, avec « appliqué par Escouade ».\u003C\u002Fp>\n\u003Ch3 id=\"les-instances-de-ses-comptes-account\">Les instances de ses comptes : \u003Ccode class=\"key\">{account}\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>\u003Ccode>&quot;network&quot;: [&quot;{account}&quot;]\u003C\u002Fcode>, pour un service auto-hébergé : GitHub Enterprise Server, GitLab, Gitea, Jira.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’elle permet.\u003C\u002Fstrong> Joindre l’hôte que l’utilisateur a saisi pour un compte que ce plugin sert, sur le port saisi, et lui seul. Pour cet hôte, http et le réseau local sont permis : c’est l’utilisateur qui l’a nommé.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’Escouade refuse.\u003C\u002Fstrong> Ses sous-domaines et tout autre hôte. La liste est relue à chaque appel : un compte retiré ferme son hôte.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>La fenêtre\u003C\u002Fstrong> dit « Se connecte aux instances que tu lui donnes ». \u003Ccode class=\"key\">{account}\u003C\u002Fcode> n’est pas un hôte : le store l’accepte tel quel.\u003C\u002Fp>\n\u003Cp>Au banc, \u003Ccode>escouade-plugin test &lt;dossier&gt; --account &lt;adresse&gt;\u003C\u002Fcode> tient lieu de compte.\u003C\u002Fp>\n\u003Ch3 id=\"le-serveur-local-d-un-harness-local-shared\">Le serveur local d’un harness : \u003Ccode class=\"key\">local:shared\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>\u003Ccode>&quot;network&quot;: [&quot;local:shared&quot;]\u003C\u002Fcode>, pour un harness dont Escouade lance le serveur, comme \u003Ccode>opencode serve\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’elle permet.\u003C\u002Fstrong> http vers \u003Ccode class=\"key\">127.0.0.1:&lt;port&gt;\u003C\u002Fcode>, seulement sur un port que le cœur a lu dans la sortie d’un process partagé qu’il a lancé pour ce plugin, tant que ce process vit. Ce trafic ne passe jamais par le proxy.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’Escouade refuse.\u003C\u002Fstrong> Tout autre port local. Le plugin ne nomme jamais un port que le cœur n’a pas lu, et \u003Ccode class=\"key\">local:shared\u003C\u002Fcode> ne sert ni un WebSocket ni un autre plugin.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>La fenêtre\u003C\u002Fstrong> dit « Parle au serveur local du harness, lancé par Escouade ».\u003C\u002Fp>\n\u003Ch3 id=\"les-limites-limits\">Les limites : \u003Ccode class=\"key\">limits\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>\u003Ccode>sdk\u002Fdoc-examples\u002Frust\u002Fescouade-plugin.json\u003C\u002Fcode> (extrait)\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">  &quot;limits&quot;: { &quot;fetchSeconds&quot;: 60, &quot;fetchBytes&quot;: 8388608, &quot;fileBytes&quot;: 1048576 },\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Clé\u003C\u002Fth>\n\u003Cth>Ce qu’elle dit\u003C\u002Fth>\n\u003Cth>Sans elle\u003C\u002Fth>\n\u003Cth>Plafond de l’hôte\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">fetchSeconds\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Le temps d’un \u003Ccode class=\"key\">net\u002Ffetch\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>10 s\u003C\u002Ftd>\n\u003Ctd>120 s\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">fetchBytes\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>La taille d’une réponse\u003C\u002Ftd>\n\u003Ctd>1 Mio\u003C\u002Ftd>\n\u003Ctd>32 Mio\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">fileBytes\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Ce que rend un \u003Ccode class=\"key\">fs\u002Fread\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>256 Kio\u003C\u002Ftd>\n\u003Ctd>4 Mio\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Une valeur au-delà du plafond est ramenée à lui par l’hôte (\u003Ccode class=\"key\">fileBytes\u003C\u002Fcode> est refusé par le manifeste). La fenêtre ne dit rien de plus : une limite ne donne accès à rien.\u003C\u002Fp>\n\u003Ch3 id=\"les-flux-et-le-websocket\">Les flux et le WebSocket\u003C\u002Fh3>\n\u003Cp>Ils ne se déclarent pas : ils suivent \u003Ccode class=\"key\">network\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ccode class=\"key\">net\u002Fstream\u003C\u002Fcode>\u003C\u002Fstrong> applique les règles de \u003Ccode class=\"key\">net\u002Ffetch\u003C\u002Fcode> à l’ouverture, puis un crédit : l’hôte ne lit plus la socket au-delà de 256 Kio non consommés, donc un plugin lent ralentit le serveur et non la mémoire de l’app. 8 flux à la fois. 120 s sans octet ferment un flux, sauf s’il est déclaré \u003Ccode>&quot;sse&quot;: true\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode class=\"key\">ws\u002Fopen\u003C\u002Fcode>\u003C\u002Fstrong> est en \u003Ccode class=\"key\">wss\u003C\u002Fcode> seulement, vers les mêmes hôtes, jamais \u003Ccode class=\"key\">local:shared\u003C\u002Fcode>. 4 sockets, 1 Mio par message. \u003Ccode class=\"key\">ws\u002Fsend\u003C\u002Fcode> est refusé si ce qui attend dépasserait 256 Kio. Ses en-têtes ne vont pas au journal.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"les-fichiers-files\">Les fichiers : \u003Ccode class=\"key\">files\u003C\u002Fcode>\u003C\u002Fh2>\n\u003Cp>\u003Ccode>sdk\u002Fdoc-examples\u002Frust\u002Fescouade-plugin.json\u003C\u002Fcode> (extrait)\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">    &quot;files&quot;: [\n      { &quot;root&quot;: &quot;plugin&quot;, &quot;access&quot;: &quot;read&quot; },\n      { &quot;root&quot;: &quot;temp&quot;, &quot;access&quot;: &quot;write&quot; },\n      { &quot;root&quot;: &quot;agent&quot;, &quot;access&quot;: &quot;write&quot; },\n      { &quot;root&quot;: &quot;agentHome&quot;, &quot;access&quot;: &quot;read&quot; }\n    ],\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Ce qu’elle permet.\u003C\u002Fstrong> \u003Ccode class=\"key\">fs\u002Fread\u003C\u002Fcode>, \u003Ccode class=\"key\">fs\u002Fwrite\u003C\u002Fcode>, \u003Ccode class=\"key\">fs\u002Fstat\u003C\u002Fcode>, \u003Ccode class=\"key\">fs\u002Flist\u003C\u002Fcode> et \u003Ccode class=\"key\">fs\u002Fcanon\u003C\u002Fcode>, sous une racine de cette liste fermée, qu’Escouade résout :\u003C\u002Fp>\n\u003Cdiv class=\"table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Racine\u003C\u002Fth>\n\u003Cth>Ce que c’est\u003C\u002Fth>\n\u003Cth>La fenêtre dit\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">plugin\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Son dossier, en lecture seule : ce que le store a signé ne change pas.\u003C\u002Ftd>\n\u003Ctd>« Lit son propre dossier »\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">temp\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Un dossier à lui, partagé par ses agents.\u003C\u002Ftd>\n\u003Ctd>« Garde des fichiers dans un dossier à lui »\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">agent\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Le dossier de travail de l’agent.\u003C\u002Ftd>\n\u003Ctd>« Lit les fichiers de tes agents », ou « Écrit dans les dossiers de tes agents » en orange\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">agentRepo\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Son dépôt git, pour un worktree.\u003C\u002Ftd>\n\u003Ctd>Les mêmes\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode class=\"key\">agentHome\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Le dossier de son compte, là où un harness garde ses identifiants.\u003C\u002Ftd>\n\u003Ctd>« Lit le dossier de compte de tes agents »\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode class=\"key\">access\u003C\u002Fcode> vaut \u003Ccode class=\"key\">read\u003C\u002Fcode> ou \u003Ccode class=\"key\">write\u003C\u002Fcode>. Un plugin nomme une racine et un chemin relatif, ou un chemin de l’hôte sous une racine de cet agent : c’est ce que lit un harness. Seuls les plugins de harness reçoivent un agent, par \u003Ccode class=\"key\">agent\u002Fattach\u003C\u002Fcode>, avec les seules racines qu’ils déclarent ; un autre plugin a \u003Ccode class=\"key\">plugin\u003C\u002Fcode> et \u003Ccode class=\"key\">temp\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’Escouade refuse.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>un chemin libre, un \u003Ccode class=\"key\">~\u003C\u002Fcode>, un \u003Ccode class=\"key\">..\u003C\u002Fcode>, un flux NTFS (\u003Ccode class=\"key\">a.txt:b\u003C\u002Fcode>), un lien qui ne mène nulle part ;\u003C\u002Fli>\n\u003Cli>ce qui sort d’une racine une fois les liens suivis. Le refus vient avant de dire si le fichier existe : il ne renseigne sur rien ;\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"key\">plugin\u003C\u002Fcode> en écriture, à la lecture du manifeste ;\u003C\u002Fli>\n\u003Cli>le dossier d’un projet privé dont l’agent est coupé, quel que soit le code du plugin ;\u003C\u002Fli>\n\u003Cli>une lecture de plus de 256 Kio, ou de \u003Ccode class=\"key\">limits.fileBytes\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Chaque écriture et chaque refus vont au journal.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>La revue\u003C\u002Fstrong> demande à quoi sert \u003Ccode class=\"key\">agentHome\u003C\u002Fcode> : ce dossier contient les identifiants d’un harness.\u003C\u002Fp>\n\u003Ch2 id=\"les-programmes-commands\">Les programmes : \u003Ccode class=\"key\">commands\u003C\u002Fcode>\u003C\u002Fh2>\n\u003Cp>\u003Ccode>sdk\u002Fdoc-examples\u002Frust\u002Fescouade-plugin.json\u003C\u002Fcode> (extrait)\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">    &quot;commands&quot;: [&quot;git status --short&quot;, &quot;git log *&quot;]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Ce qu’elle permet.\u003C\u002Fstrong> \u003Ccode class=\"key\">proc\u002Fspawn\u003C\u002Fcode> lance une de ces lignes. Le premier mot est un nom de programme, qu’Escouade cherche sur le PATH.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Ce qu’Escouade refuse.\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cul>\n\u003Cli>un chemin à la place du nom ;\u003C\u002Fli>\n\u003Cli>ce qui n’est pas une entrée, mot pour mot ; la suite n’est permise que si l’entrée finit par \u003Ccode class=\"key\">*\u003C\u002Fcode> ;\u003C\u002Fli>\n\u003Cli>un shell : sous Windows, ni \u003Ccode class=\"key\">.cmd\u003C\u002Fcode> ni \u003Ccode class=\"key\">.bat\u003C\u002Fcode>, donc aucun outil installé par npm ;\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"key\">dir\u003C\u002Fcode> autre qu’une racine (\u003Ccode class=\"key\">agent\u003C\u002Fcode>, \u003Ccode class=\"key\">project\u003C\u002Fcode>), et un projet privé où le plugin n’est pas activé. Sans \u003Ccode class=\"key\">dir\u003C\u002Fcode>, le programme tourne dans \u003Ccode class=\"key\">temp\u003C\u002Fcode> ;\u003C\u002Fli>\n\u003Cli>une variable \u003Ccode class=\"key\">ESCOUADE_*\u003C\u002Fcode>, \u003Ccode class=\"key\">PATH\u003C\u002Fcode>, \u003Ccode class=\"key\">LD_*\u003C\u002Fcode>, \u003Ccode class=\"key\">DYLD_*\u003C\u002Fcode> ou \u003Ccode class=\"key\">NODE_OPTIONS\u003C\u002Fcode> dans \u003Ccode class=\"key\">env\u003C\u002Fcode> : elle ferait tourner autre chose.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Chaque programme meurt avec l’instance du plugin ou à \u003Ccode class=\"key\">proc\u002Fkill\u003C\u002Fcode>, avec ce qu’il a lancé. Quatre à la fois. Sa sortie n’est plus lue au-delà de 256 Kio qui n’ont pas atteint le module.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>La fenêtre\u003C\u002Fstrong> dit « Lance cette commande sur ton ordinateur », puis la commande, en rouge, une ligne par commande.\u003C\u002Fp>\n\u003Cdiv class=\"callout warning\">\u003Cp class=\"callout-title\">Attention\u003C\u002Fp>\u003Cp>Le module est isolé, le programme qu’il lance ne l’est pas : il a les droits de l’utilisateur. Une entrée en \u003Ccode class=\"key\">*\u003C\u002Fcode> sur un programme généraliste (\u003Ccode>git *\u003C\u002Fcode>, \u003Ccode>docker *\u003C\u002Fcode>) rouvre tout, par \u003Ccode>git -c core.sshCommand=…\u003C\u002Fcode> par exemple. C’est la capacité la plus forte : demande des lignes exactes, et attends-toi à justifier un \u003Ccode class=\"key\">*\u003C\u002Fcode> à la revue.\u003C\u002Fp>\n\u003C\u002Fdiv>\n\u003Cp>\u003Ccode class=\"key\">permissions.commands\u003C\u002Fcode> n’est pas \u003Ccode class=\"key\">contributes.commands\u003C\u002Fcode>, qui lance une ligne de shell sur un fait, après l’accord de l’utilisateur (\u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fevents\u002F#une-commande-sur-un-fait\">les événements\u003C\u002Fa>).\u003C\u002Fp>\n\u003Ch2 id=\"les-minuteurs\">Les minuteurs\u003C\u002Fh2>\n\u003Cp>\u003Ccode class=\"key\">timer\u002Fset\u003C\u002Fcode>, \u003Ccode class=\"key\">timer\u002Fcancel\u003C\u002Fcode> et \u003Ccode class=\"key\">timer\u002Ffired\u003C\u002Fcode> ne se déclarent pas : c’est un droit de base, comme \u003Ccode class=\"key\">log\u003C\u002Fcode>. Le plancher est 10 ms, ce qui est dessous y est ramené, et 64 minuteurs à la fois. Ils s’arrêtent avec l’instance. \u003Ccode class=\"key\">setTimeout\u003C\u002Fcode> en JS et \u003Ccode class=\"key\">sleep\u003C\u002Fcode> du kit en Rust passent par eux.\u003C\u002Fp>\n\u003Ch2 id=\"ensuite\">Ensuite\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fcalls\u002F\">Les appels\u003C\u002Fa> : chaque méthode, avec un exemple en TS et en Rust.\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fpermissions\u002F\">Les permissions\u003C\u002Fa> : tout ce qu’un plugin peut demander, vu de l’utilisateur.\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"\u002Fdocs\u002Fplugins\u002Fsecurity\u002F\">La sécurité des plugins\u003C\u002Fa> : ce que la revue en fait.\u003C\u002Fli>\n\u003C\u002Ful>\n",[8,12,15,19,22,25,28,31,34,37,40],{"id":9,"text":10,"depth":11},"le-principe","Le principe",2,{"id":13,"text":14,"depth":11},"le-reseau","Le réseau",{"id":16,"text":17,"depth":18},"un-hote-nomme","Un hôte nommé",3,{"id":20,"text":21,"depth":18},"les-instances-de-ses-comptes-account","Les instances de ses comptes : {account}",{"id":23,"text":24,"depth":18},"le-serveur-local-d-un-harness-local-shared","Le serveur local d’un harness : local:shared",{"id":26,"text":27,"depth":18},"les-limites-limits","Les limites : limits",{"id":29,"text":30,"depth":18},"les-flux-et-le-websocket","Les flux et le WebSocket",{"id":32,"text":33,"depth":11},"les-fichiers-files","Les fichiers : files",{"id":35,"text":36,"depth":11},"les-programmes-commands","Les programmes : commands",{"id":38,"text":39,"depth":11},"les-minuteurs","Les minuteurs",{"id":41,"text":42,"depth":11},"ensuite","Ensuite",1793889379459]