Outils en ligne de commande (CLI)
Exposez aux agents l'outil en ligne de commande d'un fournisseur avec defineCli, gouverné au niveau de la requête réseau plutôt qu'au niveau de la ligne de commande.
Une extension peut livrer l'outil en ligne de commande d'un fournisseur — gh, az — sous la forme
d'un unique outil d'agent, sans écrire à la main un outil par commande. Scrydon n'essaie pas
de comprendre la ligne de commande. Il gouverne les requêtes HTTPS émises par la CLI :
chaque requête est interceptée, classée comme lecture, écriture ou suppression,
confrontée à ce que l'espace de travail autorise, puis envoyée avec l'identifiant de votre organisation
apposé par la plateforme. La CLI elle-même ne détient jamais de véritable
identifiant.
Seules les releases Scrydon revues peuvent déclarer une CLI en v1.0.0. Intercepter
le trafic d'une CLI et y apposer des identifiants confère à une extension une portée supérieure à celle de ses outils
explicites ; une CLI déclarée par une extension installée par une organisation est donc refusée
avant son exécution, avec CAPABILITY_NOT_CONFIGURED
(Erreurs d'exécution des outils).
Cette page documente le contrat que suivent ces releases, ainsi que ce que les administrateurs
contrôlent.
Quand livrer une CLI plutôt que des outils
Livrez une CLI lorsque toutes ces conditions sont réunies :
- Le fournisseur maintient une CLI complète, et écrire à la main un outil par commande reviendrait à courir indéfiniment après sa surface.
- La CLI communique avec une API REST ou GraphQL en HTTPS/1.1 sur le port 443.
- La CLI lit son jeton depuis une variable d'environnement, ou se connecte avec des identifiants client OAuth auprès d'une URL de jeton.
- Elle s'exécute de manière non interactive : pas de TTY, pas de connexion via navigateur, entrée standard fermée.
Préférez des outils explicites lorsque vous avez besoin d'une surface étroite et typée, ou lorsque le fournisseur n'a pas de CLI. Les deux se cumulent — une extension peut livrer les deux.
Les trois informations, avec gh
Une CLI qui lit un jeton depuis une variable d'environnement a besoin de trois informations, et de rien d'autre :
import { defineCli } from "@scrydon/sdk-authoring/extensions/sdk/define-cli";
import ghHelpTree from "./cli/gh.help.json" with { type: "json" };
// inside defineExtension({ … provides: { … } })
provides: {
toolkits: [githubToolkit],
clis: [
defineCli({
id: "gh", // 1. the CLI's id
binary: { image: "gh", entrypoint: "gh" }, // 2. which image, which program
credentialEnv: ["GH_TOKEN"], // 3. where it expects a token
env: { GH_PROMPT_DISABLED: "1", GH_TELEMETRY: "false", NO_COLOR: "1" },
helpTree: ghHelpTree,
}),
],
},| Champ | Signification |
|---|---|
id | En minuscules, 2 à 32 caractères (a-z, chiffres, -, commençant par une lettre). Unique au sein de l'extension. |
binary.image | Une clé de catalogue désignant une image de CLI que l'opérateur de la plateforme a figée par digest — jamais une référence d'image. Une extension ne peut pas introduire sa propre image. |
binary.entrypoint | Le programme à exécuter dans cette image ([A-Za-z0-9._-], sans chemin). |
credentialEnv | Jusqu'à 8 noms de variables d'environnement. Chacune reçoit un substitut inerte, propre à chaque exécution, pour que la CLI se croie connectée. Le véritable identifiant est ajouté à la requête par la plateforme, uniquement sur les hôtes qui lui sont liés. |
helpTree | Le propre --help de la CLI, exploré hors ligne à partir de l'image figée et commité à côté de l'extension. Il devient la description de l'outil. Au plus 512 commandes, et au plus 3 niveaux de profondeur. |
helpCrawl.maxDepth | Facultatif, de 1 à 3. Le nombre de niveaux de sous-commandes que l'exploration de l'aide parcourt pour cette CLI. Définissez-le lorsque l'arbre complet dépasse 512 commandes — voir Arbres d'aide volumineux. |
endpointEnv | Facultatif. La variable d'environnement qui reçoit l'hôte de l'endpoint configuré par l'organisation — GH_HOST pour gh. Seul le nom est rédigé ; la plateforme renseigne la valeur à partir de l'endpoint de l'organisation, jamais à partir du manifeste ou de l'appel. Voir endpointEnv. |
toolkit | Facultatif. L'id du toolkit dont la CLI utilise la connexion et dont l'activation la liste. Omettez-le lorsqu'un seul toolkit utilise auth.default ; définissez-le lorsque deux ou plus l'utilisent — voir Quel toolkit possède une CLI. |
Les hôtes qu'une CLI peut joindre sont les hôtes d'egress déclarés par l'extension — la même liste que celle de ses outils explicites. Une CLI n'ajoute aucun hôte qui lui soit propre.
credentialEnv est facultatif. Un client HTTP générique tel que curl ne lit jamais de
jeton dans son environnement, et n'en a pas besoin : la plateforme ajoute dans tous les cas
l'identifiant à chaque requête sur le réseau. L'extension Atlassian
livre curl avec un id, un binaire, l'unique hôte auquel son identifiant est destiné, et un
arbre d'aide :
defineCli({
id: "curl",
binary: { image: "curl", entrypoint: "curl" },
env: {},
// Jira and Confluence both run on the Atlassian Cloud sign-in, so the CLI
// names the toolkit it belongs to.
toolkit: "jira",
// The Atlassian Cloud sign-in is valid on the Jira/Confluence gateway only;
// the extension also reaches api.bitbucket.org, a separate sign-in.
credentialHosts: [{ hosts: ["api.atlassian.com"] }],
helpTree: curlHelpTree,
})Arbres d'aide volumineux
L'exploration de l'aide parcourt jusqu'à 3 niveaux de sous-commandes et conserve au plus 512
commandes. Une CLI qui en compte davantage — az en a environ 1 600 — déclare une profondeur
plus faible au lieu d'être coupée au milieu d'un niveau :
defineCli({
id: "az",
// …
helpCrawl: { maxDepth: 1 },
helpTree: azHelpTree,
})L'exploration lit maxDepth dans le manifeste, s'arrête à cette profondeur et consigne
max_depth comme raison du caractère partiel de l'arbre. Cette raison est acceptée parce que
la même version de la CLI produit toujours le même arbre à la même profondeur. Un arbre
tronqué de toute autre manière — par le budget de temps, de commandes ou de taille de l'exploration — peut
différer d'une exécution à l'autre et est refusé au chargement de l'extension. Il en va de même
d'un arbre plus profond que le maxDepth déclaré. La description de l'outil indique au
modèle que la liste est partielle, et <command> --help répond toujours pour chaque
commande.
Un en-tête Authorization que l'agent passe sur la ligne de commande est supprimé
avant l'ajout de l'identifiant de la plateforme : il ne peut donc ni le remplacer ni fuiter
aux côtés du véritable identifiant.
Une extension peut déclarer jusqu'à 8 CLI, et une extension dont la seule surface est une CLI est valide.
Options déclaratives supplémentaires, avec az
Certaines CLI ne peuvent pas recevoir de jeton : az tient à se connecter elle-même. Trois
champs facultatifs couvrent ce cas sans aucun code propre à az :
defineCli({
id: "az",
binary: { image: "az", entrypoint: "az" },
bootstrap: {
tokenEndpoint: "credential.tokenUrl",
preamble: [
"login", "--service-principal",
"-u", "{clientId}", "-p", "{dummy}", "--tenant", "{tenantId}",
"--allow-no-subscriptions",
],
},
credentialHosts: [
{ hosts: ["management.azure.com"], scopes: ["https://management.azure.com/.default"] },
{ hosts: ["graph.microsoft.com"], scopes: ["https://graph.microsoft.com/.default"] },
],
env: {
AZURE_CORE_COLLECT_TELEMETRY: "no",
AZURE_CORE_ONLY_SHOW_ERRORS: "yes",
AZURE_CORE_DISABLE_CONFIRM_PROMPT: "yes",
},
helpTree: azHelpTree,
})bootstrap
tokenEndpoint: "credential.tokenUrl"— la plateforme répond elle-même à la requête de connexion de la CLI vers l'URL de jeton propre à l'identifiant, avec un jeton d'accès factice. Le véritable jeton est émis par la plateforme et n'entre jamais dans le bac à sable, si bien qu'aucune commande —az account get-access-tokencomprise — ne peut l'afficher. Les requêtes de découverte vers l'hôte de connexion passent sans authentification.preamble— de 1 à 32 arguments exécutés comme une invocation distincte du même programme avant la commande de l'agent, dans le même bac à sable. Elle est invisible pour le modèle. Chaque argument est refusé s'il contient des métacaractères du shell, ressemble à un secret ou utilise un substitut autre que{clientId},{tenantId}ou{dummy}.
Une CLI qui se connecte de cette manière a besoin que la connexion utilise une identité d'application (pour Azure, un principal de service Entra).
credentialHosts
credentialHosts indique quels hôtes reçoivent un identifiant, et lequel. Chaque
entrée prend l'une de deux formes :
- Hôtes uniquement —
{ hosts: ["api.atlassian.com"] }. L'identifiant propre à la connexion est ajouté exactement sur ces hôtes, et sur aucun autre hôte que l'extension joint. Rien n'est émis : tout type de connexion convient. - Hôtes et portées — une même connexion peut nécessiter un jeton différent par hôte :
Azure émet une audience par jeton. La plateforme émet un jeton pour les
portées de l'entrée et l'ajoute sur les hôtes de l'entrée. Cette forme nécessite une
identité d'application, comme
bootstrap. Une connexion n'est considérée comme telle que si elle a été connectée avec le grant OAuth client-credentials. Cette information est lue dans la connexion stockée, jamais dans le manifeste ou la requête. Toute autre connexion est refusée avecIDENTITY_NOT_CONFIGURED. La plateforme émet elle-même chaque jeton, uniquement pour les portées que déclare le manifeste de l'extension, et le secret de l'application ne quitte jamais la plateforme. Avecbootstrap, la CLI se connecte avec l'id client et le tenant de cette application.
Les règles valables pour les deux :
- jusqu'à 16 entrées, chacune avec 1 à 16 hôtes et, le cas échéant, 1 à 8 portées ;
- les hôtes sont des noms DNS exacts, en minuscules — pas de joker, pas d'adresse IP littérale, et aucun hôte ne peut figurer dans deux entrées ;
- chaque hôte doit être un hôte que l'extension est autorisée à joindre.
Un joker est refusé à dessein : des suffixes comme *.core.windows.net sont partagés
par tous les clients du fournisseur, et un identifiant lié à l'un d'eux suivrait une
requête vers n'importe lequel d'entre eux.
Lorsque l'organisation a configuré son propre endpoint pour l'extension (un
hôte GitHub Enterprise Server, par exemple), l'identifiant de la connexion est
ajouté uniquement sur cet endpoint, et jamais sur l'hôte public par défaut de l'extension.
Cela s'applique aussi bien sans credentialHosts qu'aux entrées qui ne listent que des
hôtes ; les entrées avec des portées reçoivent toujours un jeton pour l'audience qu'elles nomment.
Sans credentialHosts, et sans endpoint d'organisation, l'identifiant de la
connexion est ajouté sur l'unique hôte de l'extension. Lorsque la liste d'egress propre à
l'extension comporte plus d'un hôte, la plateforme ne devine pas auquel
appartient l'identifiant : l'appel
échoue avec TOOL_VENDOR_CLI_CREDENTIAL_HOSTS_REQUIRED tant que la CLI ne déclare pas
ses hôtes. L'extension Atlassian en est l'exemple : elle joint aussi
api.bitbucket.org, qui utilise une connexion Bitbucket distincte ; son curl
ne nomme donc que api.atlassian.com, et une requête vers Bitbucket ne porte aucun
identifiant.
env
Configuration statique et non secrète, jusqu'à 1 024 caractères par valeur. La
plateforme possède — et refuse — les variables de proxy, les variables de confiance des certificats,
PATH, HOME, TMPDIR et les variables du chargeur. Elle refuse également les variables qui
modifient l'exécution ou la confiance des certificats (BASH_ENV, NODE_OPTIONS, PAGER,
EDITOR, BROWSER, les commutateurs de type *_NO_VERIFY et similaires), les valeurs ressemblant à des secrets,
et tout nom également listé dans credentialEnv.
endpointEnv
Certaines CLI sont dirigées vers une instance auto-hébergée par une variable d'environnement :
gh lit GH_HOST pour communiquer avec un GitHub Enterprise Server au lieu de
github.com. endpointEnv nomme cette variable, et la plateforme lui attribue
l'hôte de l'endpoint que l'organisation a configuré pour l'extension (un
paramètre de connexion de type endpoint) :
defineCli({
id: "gh",
binary: { image: "gh", entrypoint: "gh" },
// gh reads GH_ENTERPRISE_TOKEN for a GHES host, GH_TOKEN for github.com
credentialEnv: ["GH_TOKEN", "GH_ENTERPRISE_TOKEN"],
endpointEnv: "GH_HOST",
// …
});| L'organisation a configuré | La CLI voit |
|---|---|
| Aucun endpoint | Aucune variable de ce nom. La CLI conserve sa valeur publique par défaut, et l'identifiant est envoyé à l'hôte propre de l'extension. |
| Un endpoint | La variable, définie sur l'hôte de cet endpoint (par exemple ghes.example.com). L'identifiant est ajouté uniquement sur cet hôte. |
| Plus d'un endpoint | Rien — l'appel échoue avec TOOL_VENDOR_CLI_NO_BINDABLE_CREDENTIAL. La plateforme ne devine pas quel endpoint la CLI doit cibler. |
La valeur provient uniquement de la configuration de l'organisation. L'appel ne peut pas
la définir : l'agent transmet des arguments et l'entrée standard, jamais des variables
d'environnement. Le nom suit les mêmes règles que env, et ne doit être ni un nom
credentialEnv ni une clé de env.
Listez aussi dans credentialEnv chaque variable de jeton que la CLI lit pour l'hôte auto-hébergé
— ici GH_ENTERPRISE_TOKEN — afin que la CLI trouve un
substitut et envoie la requête, et que la plateforme ajoute le véritable identifiant.
L'extension GitHub de première partie déclare endpointEnv: "GH_HOST", mais sa
connexion ne propose pas de paramètre d'endpoint : son gh cible donc toujours
github.com. GitHub Enterprise Server n'est pas pris en charge en v1.0.0.
Politique réseau
Chaque requête émise par la CLI est classée avant de quitter la plateforme. Sans aucune configuration :
| Requête | Classe |
|---|---|
GET, HEAD | read |
POST, PUT, PATCH | write |
DELETE | destructive |
Un champ de surcharge _method dans la chaîne de requête, le formulaire, le corps JSON ou multipart | la plus élevée entre la classe de la méthode réelle et celle de la méthode surchargée — une surcharge n'abaisse jamais la classe |
| Tout ce que le classificateur ne peut pas lire | write — jamais read |
Les en-têtes de surcharge de méthode (X-HTTP-Method-Override et ses variantes) sont
supprimés avant l'envoi de la requête : ils ne peuvent donc ni masquer une suppression ni être
pris en compte par le fournisseur.
La règle GraphQL. La méthode et le chemin ne disent rien d'un appel GraphQL ; tout
POST ou GET dont le dernier segment de chemin est graphql est donc analysé :
- une
queryestread; - une
mutationou unesubscriptionestwrite; - un lot prend la classe la plus élevée de ses opérations ;
- un corps qui ne peut pas être analysé est au moins
write.
wirePolicy.overrides
Certaines API lisent avec POST, ou suppriment avec autre chose que DELETE.
Les surcharges reclassent des requêtes précises, jusqu'à 256 par CLI :
wirePolicy: {
overrides: [
// Jira search is a POST under a per-site id; it only reads.
{
match: { method: "POST", pathPattern: "/ex/jira/*/rest/api/3/search/jql" },
wireClass: "read",
},
// A GraphQL root field that deletes.
{ match: { graphqlOperation: "deleteRepository" }, wireClass: "destructive" },
],
},| Champ de correspondance | Signification |
|---|---|
host | Hôte exact. |
method | GET, HEAD, POST, PUT, PATCH ou DELETE. |
pathPrefix | Le chemin commence par cette valeur (forme canonique, commençant par /). |
pathPattern | Le chemin entier, où * correspond à exactement un segment. Pas d'expressions régulières. |
graphqlOperation | Un champ racine GraphQL (tel que deleteRepository) — jamais le nom d'opération choisi par le client. |
Chaque surcharge contraint au moins un champ. Règles qui empêchent une surcharge assouplissante de devenir une échappatoire :
- Une surcharge durcissante (qui relève la classe) s'applique si un quelconque champ racine GraphQL de la requête correspond.
- Une surcharge assouplissante ne s'applique que si tous les champs racines GraphQL correspondent.
- Chaque surcharge est évaluée. La surcharge durcissante correspondante la plus élevée l'emporte, et une surcharge assouplissante ne s'applique que si aucune surcharge durcissante n'a correspondu.
- Une surcharge assouplissante ne s'applique jamais à une requête déjà classée
destructive, ni à un corps GraphQL qui n'a pas pu être analysé.
Par défaut, la sémantique des méthodes HTTP est considérée comme fiable. Une API dont des GET ont des effets de bord est
sous-classée tant qu'une surcharge ne la durcit pas.
Ce que contrôlent les administrateurs
Trois paramètres décident si une CLI peut écrire. Le niveau effectif est le plus bas des trois, et il est résolu une fois par appel :
| Paramètre | Où | Défaut |
|---|---|---|
| Plafond de l'organisation | Paramètres de sécurité de l'organisation, CLI write ceiling | Aucun plafond. Définissez-le en lecture seule pour plafonner toutes les CLI de l'organisation. Si le paramètre ne peut pas être lu, toutes les CLI sont en lecture seule jusqu'à sa correction. |
| Niveau d'affectation | L'affectation du compte à un espace de travail | Lecture seule. Un administrateur peut accorder Read and write sur une affectation explicite. |
| Interrupteur du bloc | L'interrupteur Allow writes du bloc agent, par outil CLI | Désactivé. Un appel qui ne demande pas write est un appel read. |
- L'accès dont une personne dispose via sa propre identité liée, plutôt que via une affectation explicite, est toujours en lecture seule.
- Les CLI à identifiant de type jeton ne peuvent pas atteindre
writeaujourd'hui. Les connexions qui utilisent une clé d'API, un jeton de bot ou l'authentification basique n'ont aucun compte affectable pour porter une autorisation d'écriture : toute CLI qui s'exécute sur l'une d'elles est donc en lecture seule. destructiven'est jamais une autorisation permanente. Même au niveauwrite, chaque suppression nécessite qu'une personne approuve cette requête exacte. Une exécution sans personne pour approuver — une exécution planifiée ou sans surveillance — ne peut pas supprimer.- Le niveau de chaque appel et sa raison figurent dans le journal d'audit. Ils apparaissent sous la forme
cliTieretcliTierReasonsur l'événementextension.execution.succeededouextension.execution.failedde cet appel. - Chaque requête émise par une CLI figure dans le journal d'audit. Chacune écrit un
événement
egress.allowedouegress.blocked. L'événement consigne la méthode, un hôte et un chemin expurgés, la classe de la requête et la décision du broker. Une requête autorisée consigne aussistatus, le statut HTTP par lequel le fournisseur a répondu. Chaque saut de redirection a son propre événement. Une requête refusée n'a pas destatus, car elle n'a jamais atteint le fournisseur. - Une modification du niveau d'une affectation est auditée. Le relever écrit
extension.cli_tier.raisedet l'abaisser écritextension.cli_tier.lowered. Chaque événement consigne l'auteur de la modification ainsi que l'ancien et le nouveau niveau. Si l'événement ne peut pas être écrit, la modification n'est pas enregistrée et la requête échoue.
Demander l'écriture depuis la Platform API
L'interrupteur du bloc correspond au champ cliTier d'une requête d'exécution d'outil :
await client.action.tools.execute({
toolId: "github:cli:gh",
input: { args: ["issue", "create", "--repo", "acme/app", "--title", "Flaky test"] },
cliTier: "write",
});cliTier accepte "read" ou "write" ; il est ignoré pour les outils qui ne sont pas
des CLI. L'omettre équivaut à read. Demander write n'accorde jamais plus que ce que
le plafond de l'organisation et l'affectation autorisent.
Ajouter un outil CLI à un bloc agent
Lorsqu'une organisation installe votre extension, chaque CLI que vous déclarez devient un
outil nommé <id> CLI (par exemple gh CLI). Il apparaît uniquement dans un
bloc agent : le sélecteur d'outils du bloc agent le propose à côté des outils de vos toolkits.
Ce n'est jamais un bloc à part entière : il ne figure donc ni dans la barre d'outils des workflows ni dans
le sélecteur d'outils des automatisations. Dans les paramètres, l'onglet Includes de l'extension l'affiche
sous la forme d'une pastille gh CLI sur la ligne du toolkit qui le possède ; la pastille liste
les hôtes qu'il peut joindre et les paramètres nécessaires avant qu'il puisse écrire. Vous l'ajoutez
de la même manière que n'importe quel autre outil, et vous l'ajoutez individuellement. Sélectionner
All operations sur un toolkit n'inclut jamais une CLI.
La CLI s'exécute avec la connexion du toolkit qui la possède. Elle n'est listée que lorsque toutes ces conditions sont réunies :
- l'extension est installée et le toolkit propriétaire est activé ;
- la release installée est une release Scrydon revue.
Une CLI que la plateforme refuserait d'exécuter n'est pas proposée.
Quel toolkit possède une CLI
Si exactement un de vos toolkits utilise l'identifiant par défaut de l'extension
(auth.default), ce toolkit possède la CLI et vous n'avez pas besoin de le préciser. Si deux
ou plus l'utilisent, comme Jira et Confluence dans l'extension Atlassian, nommez le
propriétaire avec toolkit :
defineCli({ id: "curl", toolkit: "jira", /* … */ })La plateforme lance une CLI avec l'identifiant par défaut : son propriétaire doit donc
l'utiliser. defineExtension lève une exception, et la publication refuse le manifeste, lorsque :
toolkitnomme un toolkit que l'extension ne déclare pas ;toolkitnomme un toolkit lié à un identifiant autre queauth.default;- il n'y a pas de
toolkit, et aucun toolkit n'utiliseauth.default; ou - il n'y a pas de
toolkit, et deux toolkits ou plus utilisentauth.default.
Rien ne choisit un propriétaire à votre place, et une CLI n'est jamais publiée sans propriétaire.
Une extension dont la seule surface est une CLI déclare tout de même un toolkit pour la posséder, sans outils propres. L'extension Azure fait exactement cela :
const azureToolkit = defineToolkit({ id: "azure", name: "Azure", tools: [] });
defineExtension({
// …
provides: {
toolkits: [azureToolkit],
clis: [defineCli({ id: "az", toolkit: azureToolkit.id, /* … */ })],
},
});C'est ce toolkit qu'un administrateur active et connecte dans les paramètres, et la CLI est alors listée dans le sélecteur d'outils du bloc agent.
L'ajout de l'outil n'accorde rien en soi. Chaque appel commence toujours au niveau read,
et le niveau, la politique réseau du broker et les règles d'approbation ci-dessous gouvernent
chaque requête qu'il émet.
L'interrupteur Allow writes dans un bloc agent
Chaque outil CLI que vous ajoutez à un bloc agent dispose de son propre interrupteur Allow writes. Il
est désactivé lorsque vous ajoutez l'outil. L'activer enregistre write pour cet outil uniquement ;
le désactiver, ou basculer l'outil vers une autre opération, le supprime.
L'interrupteur relève du choix de l'auteur du bloc. Un agent ne peut pas l'activer : un appel
d'outil dont les arguments contiennent cliTier ou approvedRequestDigests est refusé
avec INVALID_TOOL_INPUT et rien n'est envoyé.
Approuver une suppression
Seule une étape Agent dans un workflow peut demander à une personne d'approuver une suppression.
Lorsqu'une CLI appelée depuis une étape Agent émet une requête destructive, l'exécution
se met en pause et demande à une personne de l'approuver, au même endroit que toute autre action
d'agent nécessitant une confirmation. Le réviseur voit :
- la méthode, l'hôte et le chemin de la requête, ainsi que le nom de l'opération GraphQL le cas échéant ;
- un aperçu du corps de la requête ;
- l'outil et les arguments exacts avec lesquels l'agent l'a appelé.
Si la requête ne peut pas être présentée au réviseur, la plateforme ne demande rien : l'étape
Agent échoue avec CLI_CONFIRMATION_UNREVIEWABLE et rien n'est envoyé. Personne
n'est jamais invité à approuver une requête qu'il ne peut pas voir.
Approuver relance toute la commande CLI — le même appel d'outil, avec les mêmes arguments, depuis le début — une seule fois, avec cette requête approuvée. La CLI redémarre de zéro : toutes les requêtes qu'elle a émises avant la requête destructrice sont donc envoyées de nouveau lors de la relance. Une commande qui crée quelque chose puis supprime autre chose le crée deux fois. Isolez l'étape destructrice dans une commande qui lui soit propre.
La plateforme ne propose donc pas d'approbation qui enverrait une écriture deux fois. Si
une requête antérieure de la même commande a déjà été envoyée en tant que requête write ou
destructive, personne n'est sollicité : l'appel échoue avec
CLI_CONFIRMATION_AFTER_WRITE, et l'agent est invité à exécuter la commande destructrice
seule, dans un appel distinct. Cet appel distinct demande une approbation
comme d'habitude. L'écriture antérieure a été envoyée et son résultat est consigné comme inconnu :
vérifiez donc son résultat par une lecture avant de la répéter.
L'approbation couvre cette seule requête et rien d'autre : si la relance émet une requête destructrice différente, l'exécution se met de nouveau en pause pour une nouvelle approbation.
Rejeter décline cet appel et l'agent poursuit. Rien n'est réexécuté :
la commande n'est pas relancée et la requête refusée n'est jamais envoyée. L'agent
reçoit le résultat de l'appel sous la forme EXEC_ACTION_CONFIRMATION_DECLINED, qui indique qu'une
personne l'a décliné, et poursuit la même étape. Il peut en informer l'utilisateur, ou essayer
autre chose. Un appel décliné ne peut pas être approuvé ensuite. Si l'agent
a toujours besoin de la suppression, il doit effectuer un nouvel appel, et cet appel demande de
nouveau une approbation.
Les suppressions nécessitent une personne : elles ne fonctionnent donc que là où une personne peut les approuver.
Partout ailleurs — chat, Platform API, bloc outil d'un workflow, exécution planifiée ou
sans surveillance — l'appel échoue avec CLI_CONFIRMATION_REQUIRED et rien n'est
supprimé. Dans une étape Agent, les lectures et écritures de la CLI s'exécutent sans demander ; seule
une suppression s'arrête pour approbation. Une étape dont le plan ne demande jamais de confirmation
ne peut pas supprimer : l'appel échoue avec CLI_CONFIRMATION_NOT_APPROVABLE.
Ce que voient les agents
- Un outil par CLI, nommé
<id>_cli(id d'outil<extension>:cli:<id>). Sa description est construite à partir de l'arbre d'aide commité : le résumé de la CLI et ses commandes de premier niveau. - L'entrée est un tableau argv, jusqu'à 256 arguments de 32 Kio chacun au maximum, plus
une entrée standard facultative jusqu'à 1 Mio. Il n'y a pas de shell : les pipes, les globs,
les redirections et
$(...)ne fonctionnent pas. Une commande par appel. --helpest servi à partir de l'exploration.--help,-houhelp <command>sur une commande qui a des sous-commandes est servi à partir de l'arbre d'aide commité, sans démarrer de bac à sable. L'aide d'une commande feuille exécute la véritable CLI.- Le résultat est une donnée. Une exécution réussie renvoie
{ exitCode, stdout, stderr }; un code de sortie non nul fait partie du résultat, ce n'est pas un échec — sauf si la plateforme a refusé l'une des requêtes de la CLI, auquel cas le refus l'emporte (voir les codes d'erreur ci-dessous). - Chaque appel s'exécute dans un bac à sable neuf et isolé, avec une échéance d'au plus 270 secondes,
jusqu'à 128 requêtes sortantes, des corps de requête jusqu'à 8 Mio et un résultat de 2 Mio.
Rien n'est conservé d'un appel à l'autre. Pour les opérations de longue durée du fournisseur,
lancez l'opération (par exemple avec
--no-wait) et vérifiez son état par une lecture distincte lors d'un appel ultérieur.
Conformité
Chaque image de CLI est vérifiée avant qu'une release ne la livre : la CLI s'exécute face à un fournisseur simulé, via la même interception, et soit réussit sans aucun code propre à la CLI, soit est refusée avec une raison nommée. Les vérifications s'exécutent dans cet ordre et le premier échec constitue le verdict :
| Échec | Signification | Ce que fait l'auteur |
|---|---|---|
NEEDS_TTY | La CLI ne s'exécuterait pas sans terminal ou sans entrée standard ouverte. | Définissez les commutateurs non interactifs de la CLI dans env, sinon la CLI ne peut pas être livrée. |
PROXY_IGNORED | La CLI n'a pas fait passer son trafic par le proxy de la plateforme. | La CLI ne peut pas être livrée. En production, une telle CLI échoue simplement en mode fermé. |
HOST_NOT_ALLOWED | La CLI a tenté de joindre un hôte hors de l'ensemble accordé. | Déclarez l'hôte dont la commande a besoin, ou acceptez que la commande soit hors périmètre. |
CA_UNTRUSTED | La CLI n'a pas fait confiance à l'autorité de certification propre à l'exécution. | La CLI épingle des certificats ou ignore toutes les variables standard de magasin de confiance ; elle ne peut pas être livrée. |
DUMMY_ON_WIRE | L'identifiant factice est apparu ailleurs que dans un en-tête d'identification (URL, corps). | La CLI place les jetons là où la plateforme ne peut pas les remplacer en toute sécurité ; elle ne peut pas être livrée en l'état. |
DIGEST_UNSTABLE | Deux exécutions identiques ont produit des requêtes différentes : l'approbation d'une personne ne pourrait donc pas être liée à l'une d'elles. | Trouvez l'entrée non déterministe (un horodatage, un id aléatoire) et figez-la avec une option ou env. |
RUN_FAILED | L'exécution a correctement utilisé le réseau mais ne s'est pas terminée proprement. | Corrigez l'invocation ; une connexion seule ne vaut jamais réussite. |
Hors périmètre en v1.0.0
Ces cas échouent aujourd'hui en mode fermé plutôt que de se dégrader :
- La signature des requêtes (de type AWS SigV4) :
awsetgcloudne sont donc pas pris en charge. - Les certificats client TLS mutuel.
- Les fichiers d'identifiants au lieu de variables d'environnement (un kubeconfig, par exemple).
- Les CLI uniquement HTTP/2 et gRPC — la plateforme ne parle que HTTP/1.1.
- Les WebSockets et les autres mises à niveau de streaming.
- Les endpoints sur un port autre que 443, ou en HTTP simple. Un service auto-hébergé sur
:8080ou:8443ne peut pas être piloté via une CLI. - Les binaires fournis par l'extension. Une CLI provient toujours d'une image que l'opérateur de la plateforme a figée.
- Les hôtes de plan de données sur des domaines joker partagés (pour
az: Storage, Key Vault, Container Registry). Un identifiant n'est jamais lié à un hôte joker. - Les envois dépassant le plafond de taille du corps de requête (
CLI_UPLOAD_TOO_LARGE).
az vérifie aussi ses propres mises à jour à chaque exécution, sur
azcliprod.blob.core.windows.net. L'extension Azure autorise cet hôte exact
et aucune entrée credentialHosts ne le nomme : la vérification réussit donc sans jamais
porter d'identifiant. Une CLI dont l'extension n'autorise pas l'hôte de mise à jour
s'exécute tout de même ; attendez-vous à un événement d'egress bloqué par appel.
Codes d'erreur
Deux codes sont des décisions de politique. Ils parviennent à l'appelant sous la forme
POLICY_BLOCKED (HTTP 422), avec le code dans details.policyCode :
| Code | Signification | Que faire |
|---|---|---|
CLI_REQUEST_TIER_DENIED | La CLI a émis une requête write (ou destructive) alors que le niveau effectif de cet appel est read. | Vérifiez les trois paramètres décrits dans Ce que contrôlent les administrateurs. L'approbation de la requête par une personne ne peut pas lever un niveau. |
CLI_CONFIRMATION_REQUIRED | La CLI a émis une requête destructive. Les détails portent la méthode, l'hôte, le chemin et un aperçu du corps. | Exécutez la commande depuis une étape Agent. Une personne approuve cette requête exacte ; la commande entière est alors relancée une fois avec cette approbation. Une requête différente nécessite sa propre approbation. |
Dans une étape Agent, le modèle voit un refus de niveau comme un appel d'outil en échec avec le code
POLICY_BLOCKED, et son message commence par CLI_REQUEST_TIER_DENIED. L'exécution
consigne le résultat de l'appel comme inconnu, de sorte que l'étape ne le réessaie jamais
d'elle-même.
La plateforme renvoie ceux-ci directement à la CLI sous forme d'erreur HTTP : la CLI se termine donc avec un code non nul et le résultat de l'appel montre le refus :
| Code | HTTP | Signification | Que faire |
|---|---|---|---|
CLI_UPLOAD_TOO_LARGE | 413 | Le corps de la requête a dépassé le plafond de 8 Mio. | Envoyez moins de données ; les envois volumineux sont hors périmètre. |
CLI_MISDIRECTED_REQUEST | 421 | Le Host de la requête ne correspondait pas à l'hôte auquel la CLI s'est connectée. | Signalez-le — une CLI conforme ne fait jamais cela. |
CLI_METHOD_NOT_ALLOWED | 405 | Une méthode autre que GET, HEAD, POST, PUT, PATCH, DELETE. | La commande n'est pas prise en charge. |
CLI_INVALID_REQUEST_TARGET | 400 | La cible de la requête n'était pas un simple chemin. | Signalez-le — une CLI conforme ne fait jamais cela. |
Ceux-ci reviennent comme code d'échec de l'appel d'outil :
| Code | Signification | Que faire |
|---|---|---|
CLI_TIMEOUT_OUTCOME_UNKNOWN | La CLI a été arrêtée à son échéance. Le fournisseur a peut-être déjà accepté la requête, et l'opération est peut-être toujours en cours. | Vérifiez l'état de la ressource avec une commande en lecture seule avant de réessayer. |
CLI_BOOTSTRAP_FAILED | La connexion bootstrap.preamble s'est terminée avec un code non nul : la commande de l'agent ne s'est donc jamais exécutée. | Vérifiez l'identité d'application de la connexion (id client, tenant) et que l'URL de jeton de l'extension est joignable. |
CLI_CA_UNAVAILABLE | Le bac à sable n'a pas reçu l'autorité de certification propre à l'exécution. | Défaillance de la plateforme — réessayez ; signalez-la si elle persiste. |
CLI_PROXY_UNAVAILABLE | La connexion du bac à sable au proxy de la plateforme n'a pas été provisionnée. | Défaillance de la plateforme — réessayez ; signalez-la si elle persiste. |
CLI_ENTRYPOINT_NOT_FOUND | binary.entrypoint n'est pas installé dans l'image figée. | Corrigez l'entrypoint du manifeste, ou le mappage de l'image. |
CLI_ARGS_ALTERED_BY_DLP | La prévention des pertes de données a masqué une partie d'un argument, ce qui aurait exécuté une commande différente. La commande ne s'est pas exécutée. | Retirez la valeur sensible de la commande, ou modifiez la politique DLP. |
CLI_CONFIRMATION_NOT_APPROVABLE | La CLI a émis une requête destructive depuis une étape Agent dont l'outil ne demande jamais de confirmation. Rien n'a été envoyé. | Modifiez le paramètre de confirmation de l'outil, ou effectuez la suppression autrement. |
CLI_CONFIRMATION_AFTER_WRITE | La CLI a émis une requête destructive après qu'une requête antérieure de la même commande avait déjà été envoyée en tant qu'écriture. Approuver relancerait la commande et renverrait cette écriture : personne n'a donc été sollicité et la requête destructrice n'a pas été envoyée. | Exécutez la commande destructrice seule, dans un appel distinct ; cet appel demande une approbation. Vérifiez le résultat de l'écriture antérieure par une lecture avant de la répéter. |
CLI_CONFIRMATION_UNAVAILABLE | La CLI a émis une requête destructive et l'approbation n'a pas pu être enregistrée. Rien n'a été envoyé. | Réessayez l'étape ; signalez-le si cela persiste. |
EXEC_ACTION_CONFIRMATION_DECLINED | Une personne a décliné la requête destructive pour laquelle cet appel nécessitait une approbation. L'appel n'a pas été réexécuté et la requête n'a jamais été envoyée. | Informez l'utilisateur, ou adoptez une autre approche. Répéter l'appel demande de nouveau une approbation. |
Les problèmes de manifeste et de configuration — un id de CLI non déclaré, une
identité d'application manquante, un identifiant qui ne se lie à aucun des hôtes de la CLI —
apparaissent sous forme de codes TOOL_VENDOR_CLI_* avant l'exécution de la CLI ; voir
Erreurs d'exécution des outils.