leynier/portifact

Publier et partager des artefacts statiques (sites HTML, Markdown et texte brut) avec Portifact, contrôler qui peut les voir et collecter les annotations des réviseurs. À utiliser lorsque l'utilisateur souhaite partager un plan, un rapport, un prototype ou un document sous forme de lien, ou mentionne Portifact, l'interface CLI de Portifact ou le serveur MCP de Portifact.

Qu'est-ce que portifact ?

Portifact permet aux agents IA et aux développeurs de publier et partager des artefacts statiques (sites HTML, documents Markdown, fichiers texte) via des URLs dédiées, avec un contrôle d'accès, une expiration, une restauration, et la possibilité de recueillir des annotations de réviseurs authentifiés. L'agent utilise l'outil CLI `portifact` ou le serveur MCP distant pour interagir avec le service. Le flux de travail typique inclut la vérification de la version installée, la connexion via OAuth, la prévisualisation et la publication des artefacts, la gestion des accès (public, authentifié, restreint), et la gestion des versions (listing, rollback, téléchargement). Portifact prend également en charge l'attachement de sources privées exploitant Git, la gestion des annotations, et la journalisation des accès. L'outil est conçu pour fonctionner sur différentes plateformes et supporte le streaming de fichiers volumineux, la détection de secrets, et la réutilisation des fichiers déjà téléchargés.

Compatible avec✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/leynier/portifact/tree/HEAD/skills/portifact

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait portifact ?

Portifact publie des artefacts statiques avec leur propre URL, un contrôle d'accès, une expiration et une restauration, ainsi que des annotations optionnelles de la part de réviseurs authentifiés. Les agents l'utilisent via l'interface CLI portifact ou le serveur MCP distant.

Vérifier ce que la version installée prend en charge

Portifact est en développement précoce et gagne des commandes au fil des versions. Avant d'utiliser une commande :

  1. Exécutez portifact --version pour confirmer que l'interface CLI est installée.
  2. Exécutez portifact --help pour voir les commandes que cette version prend en charge.
  3. Utilisez uniquement les commandes et les options listées par --help. Ne devinez jamais une commande. Si l'utilisateur demande quelque chose que la version installée ne peut pas faire, dites-le et suggérez une mise à jour.

Les versions dont --help liste login peuvent se connecter, se déconnecter et afficher le compte connecté. Les versions dont --help liste également publish peuvent publier des fichiers et dossiers et gérer les artefacts et leur accès. Les versions dont --help liste versions, rollback et pull peuvent lister les versions, effectuer des rollbacks et télécharger. Les versions dont --help liste annotations peuvent gérer les fils de commentaires, comme décrit ci-dessous. Les versions dont portifact publish --help liste --source, --ref et --assets publient des fichiers ZIP, attachent des sources privées et publient des ressources Markdown. Les versions dont portifact pull --help liste --git construisent un historique Git des sources privées. Les versions dont --help liste usage affichent les quotas du compte. Les versions dont --help liste logs montrent qui a ouvert un artefact. Les versions dont --help liste licenses impriment les licences des logiciels open source dans le binaire (portifact licenses ; également sur https://portifact.com/third-party-notices). Le serveur MCP sur https://app.portifact.com/mcp peut lister, publier et gérer de petits documents, lister, télécharger et effectuer des rollbacks de versions, lire, écrire, modérer et exporter des annotations, et rapporter les logs d'utilisation et d'accès ; voir ci-dessous.

Installer ou mettre à jour l'interface CLI

L'interface CLI est un exécutable unique et ne nécessite ni Node ni Bun. Demandez avant d'installer un logiciel sur la machine de l'utilisateur.

curl -fsSL https://portifact.com/install | bash
irm https://portifact.com/install.ps1 | iex
  • Sur Alpine Linux, installez d'abord l'environnement d'exécution C++ : apk add libstdc++ libgcc.
  • Réexécutez la même commande pour mettre à jour.
  • Définissez PORTIFACT_VERSION=0.2.0 pour installer une version spécifique.
  • Dans les environnements non interactifs ou sandboxés, définissez PORTIFACT_NO_MODIFY_PATH=1 et appelez directement ~/.portifact/bin/portifact au lieu de modifier les profils shell.
  • Le programme d'installation vérifie le téléchargement par rapport aux sommes de contrôle de la version et s'arrête en cas de non-concordance. Ne contournez pas une vérification échouée.

Se connecter

Vérifiez d'abord avec portifact whoami --json. Le code de sortie 0 imprime {"server","userId","clientId","scopes"} ; le code de sortie 3 signifie que cet appareil n'est pas connecté ou que la session a expiré.

Pour se connecter, exécutez portifact login (ajoutez --json pour un résultat analysable). Il utilise le flux OAuth de périphérique :

  1. L'interface CLI imprime une page (https://app.portifact.com/device) et un code à 8 caractères sur stderr, et ouvre le navigateur lorsqu'une personne est au terminal. Passez --no-browser pour ignorer l'ouverture.
  2. Montrez à l'utilisateur la page et le code exactement comme imprimés, et demandez-lui de s'y connecter, de vérifier que la page affiche le même code et de l'approuver. Ne l'approuvez jamais à sa place et ne lui demandez jamais de coller quoi que ce soit de cette page vers vous. La page de connexion demande à l'utilisateur de confirmer qu'il a 18 ans ou plus et d'accepter les conditions (https://portifact.com/terms) ; ce choix lui appartient, donc ne l'acceptez jamais à sa place. Si la page indique qu'elle ne peut pas envoyer un code par e-mail à son adresse (un e-mail précédent a été rejeté ou signalé comme spam) ou que les codes e-mail sont en pause pour le mois, dites à l'utilisateur de se connecter avec Google ou GitHub à la place.
  3. La commande attend que l'utilisateur approuve ou refuse, ou que le code expire (10 minutes), alors exécutez-la là où l'utilisateur peut voir son stderr et laissez-la se terminer.

Les informations d'identification vont dans le magasin d'identifiants du système d'exploitation. Lorsqu'il n'y en a pas (par exemple, un serveur Linux sans Secret Service), elles vont dans credentials.json dans le répertoire de configuration de l'utilisateur (~/.config/portifact sur Linux, ~/Library/Application Support/portifact sur macOS, %APPDATA%\portifact sur Windows) avec des autorisations pour l'utilisateur uniquement, et l'interface CLI imprime un avertissement. Ne lisez, n'imprimez, ne copiez et ne téléchargez jamais ce fichier ou les jetons stockés ; l'interface CLI les rafraîchit toute seule.

portifact logout révoque les jetons de cet appareil sur le serveur, puis les supprime. Il révoque les jetons, alors confirmez d'abord avec l'utilisateur. Si le serveur est inaccessible, il conserve les informations d'identification et se termine avec le code 1 ; réexécutez-le plus tard.

PORTIFACT_APP_ORIGIN pointe l'interface CLI vers un autre serveur et existe pour les tests et les environnements de staging. Ne le définissez pas sauf si l'utilisateur le demande.

Publier

Uniquement dans les versions dont --help liste publish.

  1. Prévisualisez d'abord : portifact publish <chemin> --dry-run --json construit le manifeste sans se connecter ni télécharger. Il renvoie {"dryRun","type","name","entrypoint","totalBytes","files":[{"path","size","sha256","contentType"}],"excluded":[{"path","reason"}]}, plus "sources":{"chosenBy","totalBytes","files","excluded","origin","lfsPointers"} avec --source. Montrez à l'utilisateur les fichiers, les sources et ce qui a été exclu, et arrêtez-vous si quelque chose semble privé.
  2. Publiez : portifact publish <chemin> --json. Le résultat est {"artifactId","versionId","versionNumber","url","expiresAt","uploadId","files","totalBytes","sourceFiles","sourceBytes","sourceOrigin","uploadedFiles","uploadedBytes"}. La progression et le manifeste vont sur stderr.
  • Le type est déduit du chemin : un dossier, un fichier .zip ou .html est un site, .md est markdown et .txt est text. Passez --type site|markdown|text pour d'autres fichiers. Les dossiers et les fichiers ZIP ne peuvent être que des sites.
  • Un fichier HTML unique est publié en tant que index.html du site. Un dossier a besoin d'un index.html, ou --entry <chemin> avec la page à ouvrir en premier.
  • --name définit le nom affiché dans la liste des artefacts ; il prend par défaut le nom du fichier ou du dossier.
  • --artifact <id> publie une nouvelle version d'un artefact existant, en conservant son nom, son type et son accès, sauf si --name change le nom. Si quelqu'un d'autre a publié entre-temps, il échoue avec version_conflict (code de sortie 6) ; vérifiez l'artefact et publiez à nouveau.
  • Les dossiers excluent toujours .git, node_modules, les caches, les fichiers OS et les fichiers pouvant contenir des secrets (.env et .env.* sauf les exemples, .dev.vars, .npmrc, .netrc, les clés SSH, *.pem, *.key, *.p12, l'état Terraform et similaires). Nommer directement un tel fichier est refusé. Le filtre est un filet de sécurité, pas une revue : vérifiez toujours le manifeste.
  • Les liens symboliques et les fichiers spéciaux sont refusés, jamais suivis.
  • Les fichiers que le compte stocke déjà ne sont pas téléchargés à nouveau. Les téléchargements interrompus sont relancés, et si la publication échoue après le début du téléchargement, l'interface CLI l'annule afin que rien ne soit à moitié publié.
  • Les versions dont la progression affiche Uploaded part <n> of <m> of <chemin> téléchargent les fichiers de plus de 64 Mo en parties de 16 Mo, diffusées depuis le disque, de sorte qu'un fichier volumineux n'a jamais à tenir en mémoire et qu'une partie échouée est renvoyée seule. Une version ne contient au maximum que 250 Mo de fichiers publiés, quelle que soit la taille des parties.
  • Un fichier ZIP est développé sur la machine de l'utilisateur avant que quoi que ce soit ne soit téléchargé. Lorsque tous les fichiers qu'il contient se trouvent dans un seul dossier, ce dossier devient la racine. Les liens, les fichiers spéciaux, les entrées cryptées, les chemins qui quittent l'archive, les noms en conflit, les entrées qui se chevauchent, les fichiers qui se développent au-delà de leur taille déclarée ou plus de 200 fois, plus de 10 000 entrées et tout ce qui dépasse les limites de taille sont refusés (invalid_input, code de sortie 2, ou quota_exceeded, code de sortie 7). Les exclusions par défaut et le filtre de secrets s'appliquent également à ses entrées.
  • --source <chemin> attache des sources privées à la version : seul le propriétaire peut les voir et les télécharger (pull --source), elles comptent pour le stockage mais pas pour la taille publiée, et les fichiers publiés et sources ensemble font au maximum 1 000. Le chemin peut être un dossier, un seul fichier ou un fichier ZIP. Dans un dossier à l'intérieur d'un dépôt git, git choisit les fichiers : ce qu'il versionne plus les nouveaux fichiers qu'il n'ignore pas, sans sous-modules ni dépôts imbriqués. --ref <commit> attache exactement l'arbre de ce commit à la place (une branche, une étiquette ou un ID de commit ; il n'inclut jamais les modifications non validées). L'interface CLI enregistre ensuite le commit, la branche, le remote sans informations d'identification et s'il y avait des modifications non validées comme sourceOrigin de la version. En dehors d'un dépôt, ou sans git installé, le dossier est parcouru avec les exclusions par défaut. Les fichiers pouvant contenir des secrets ne sont jamais attachés, quoi que dise git. Un avertissement sur stderr liste les pointeurs Git LFS, qui sont attachés en tant que pointeurs, pas en tant que contenu ; suggérez git lfs pull d'abord.
  • --assets <rép> publie un dossier d'images et d'autres fichiers avec un document Markdown, à leur chemin relatif par rapport au document, afin que ses liens relatifs fonctionnent. Le dossier doit se trouver à l'intérieur du dossier du document, et les exclusions par défaut s'appliquent.
  • Portifact rend lui-même un artefact markdown ou text : son URL ouvre une page rendue à partir du document, où le HTML brut dans Markdown s'affiche sous forme de texte et rien ne s'exécute. Le fichier original se télécharge depuis son propre chemin sous l'URL de l'artefact (par exemple <url>plan.md, ou <url>index.md pour publish_document), avec le même accès que la page. Un document fait au maximum 5 Mo de texte UTF-8, et un artefact text est un seul fichier.

Gérer les artefacts

Uniquement dans les versions dont --help liste ces commandes. Chaque commande prend --json.

  • portifact list [--limit N] [--cursor C] [--search TEXTE] [--state active|archived|suspended|purging] [--type site|markdown|text] [--access public|authenticated|restricted] [--annotations off|open|locked] renvoie {"items":[...],"nextCursor"} ; passez nextCursor comme --cursor, avec les mêmes filtres, pour la page suivante. Chaque élément est ce que portifact info renvoie plus "activity":{"versions","currentBytes","openThreads","lastAnnotationAt"} : les versions conservées (la version actuelle incluse), les octets de la version actuelle (fichiers publiés et sources, null sans version), les fils ouverts sur la version actuelle et quand un fil sur une version a changé pour la dernière fois (null sans fils). Le trafic est compté par compte, pas par artefact, donc il est uniquement dans portifact usage. --search correspond à une partie du nom (lettres dans n'importe quel cas) ou de l'ID, et chaque filtre donné doit être vrai, alors utilisez-les pour trouver un artefact au lieu de parcourir les pages de tout. portifact info <id> renvoie un élément : {"id","name","type","state","accessMode","annotations","currentVersionId","url","expiresAt","archivedAt","purgeAt","createdAt","updatedAt"}, plus "suspension":{"reason"} pendant que l'artefact est suspendu.
  • portifact renew <id>, archive <id> et restore <id> renvoient {"artifactId","state","expiresAt","purgeAt"}. Demandez avant d'archiver.
  • portifact delete <id> supprime définitivement. Il demande à la personne au terminal de taper l'ID de l'artefact ; sans terminal, il nécessite --yes et échoue sinon avec le code de sortie 2. Ne passez --yes qu'après que l'utilisateur a confirmé cet artefact exact.

Contrôler l'accès

Uniquement dans les versions dont --help liste access. Chaque commande prend --json et imprime {"artifactId","mode","readers","indexable","embeddable","policyGeneration"}.

  • portifact access <id> montre la politique actuelle. Utilisez-la pour signaler le mode d'accès après la publication.
  • portifact access set <id> restricted|authenticated|public : restricted est le propriétaire plus les lecteurs listés, authenticated est toute personne connectée à Portifact, public est toute personne ayant le lien. Les comptes dans leurs 7 premiers jours ne peuvent pas rendre un artefact public (account_too_new_for_public, code de sortie 7).
  • portifact access add <id> <email>... et access remove <id> <email>... modifient la liste des lecteurs (au maximum 100 adresses à la fois). Les lecteurs ne sont importants que lorsque le mode est restricted.

Versions, rollback et téléchargement

Uniquement dans les versions dont --help liste ces commandes. Chaque commande prend --json.

Un artefact conserve sa version actuelle plus les versions qu'il a remplacées au cours des sept derniers jours. Le rollback et le téléchargement ne fonctionnent que sur ces versions conservées.

  • portifact versions <id> les liste, de la plus récente à la plus ancienne. --json imprime {"artifactId","items":[{"id","number","current","entrypoint","publishedBytes","sourceBytes","publishedFiles","sourceFiles","rendererVersion","committedAt","expiresAt","sourceOrigin"}]} ; expiresAt est le moment où une version précédente cesse d'être conservée et est null pour la version actuelle, et sourceOrigin est {"commit","branch","remote","dirty"} lorsque les sources proviennent de git, sinon null. L'origine est ce que l'éditeur a déclaré ; Portifact ne peut pas la vérifier.
  • portifact rollback <id> <version-id> rend une version conservée à nouveau actuelle. Elle est publiée comme une nouvelle version avec les mêmes fichiers, redémarre les 30 jours et ne change pas l'accès : les lecteurs supprimés depuis cette version restent supprimés. Demandez à l'utilisateur avant d'effectuer un rollback. Elle renvoie {"artifactId","versionId","versionNumber","url","expiresAt","fromVersionId","fromVersionNumber"}. Si quelqu'un a publié entre-temps, elle échoue avec version_conflict (code de sortie 6) ; vérifiez versions à nouveau et demandez à l'utilisateur. Dans les versions dont portifact rollback --help indique "version ID or number", <version-id> peut également être le numéro de version (3 ou v3), et pull --version en accepte un aussi ; un nombre qui n'est pas conservé échoue avec not_found (code de sortie 5).
  • portifact pull <id> --out <rép> télécharge les fichiers publiés de la version actuelle dans <rép>/published. Ajoutez --source pour les sources privées (dans <rép>/source, propriétaire uniquement), --published --source pour les deux, et --version <version-id> pour une version précédente. <rép> ne doit pas encore exister ; pull n'écrit jamais en dehors, refuse les chemins à l'intérieur d'un dossier .git, vérifie le SHA-256 de chaque fichier et supprime le dossier si quelque chose échoue (download_failed, code de sortie 1). Elle renvoie {"artifactId","versionId","versionNumber","out","files":[{"kind","path","size","sha256"}],"totalBytes"}. Lorsqu'un artefact est partagé avec quelqu'un, il peut aussi être téléchargé, mais il aura besoin que ses sources aient été attachées pour être disponibles. Une version dont les sources étaient dans un dépôt git a son sourceOrigin enregistré, et "git" (--git) peut alors être utilisé pour l'historique Git.

Annotations (fils de commentaires)

Uniquement dans les versions dont --help liste annotations. Chaque commande prend --json. L'interface CLI travaille sur la version actuelle ; l'API de l'interface utilisateur (et le serveur MCP) peut travailler sur n'importe quelle version.

  • portifact annotations list <id> récupère les fils de commentaires de la version actuelle de l'artefact, renvoyant {"artifactId","items":[{"id","parentId","resolved","createdAt","updatedAt","initialAnnotation":{"text","authorId","authorName","position","createdAt"},"annotations":[...],"annotationCount":N}]}, où parentId est null pour les fils racine dont annotations n'inclut que la réponse la plus récente (utilisez l'API directe pour les autres). portifact annotations list <id> --all-versions liste les fils sur toutes les versions conservées, avec "versionId" et "versionNumber" sur chaque fil.

Chaque initialAnnotation a éventuellement un "position" avec "anchor" (un sélecteur CSS dans une page rendue, une ligne dans le Markdown brut), "offset" et "length" (octets UTF-16 dans le texte rendu). annotationCount est le nombre total d'annotations dans le fil.

portifact annotations show <id> <thread-id> renvoie {"id","parentId","resolved","createdAt","updatedAt","annotations":[...]}, où annotations inclut toutes les annotations. Récupérez-la lorsque plus d'une annotation est attendue.

Les annotations sont destinées aux réviseurs, donc un agent ne devrait pas en ajouter automatiquement (par exemple, pour marquer un artefact comme terminé) à moins que l'utilisateur ne le demande explicitement.

portifact annotations add <id> <thread-id> --text "<texte du commentaire>" ajoute une annotation (réponse) à un fil. Elle renvoie le fil mis à jour (comme show). Pour démarrer un nouveau fil, omettez <thread-id> : portifact annotations add <id> --text "<texte du commentaire>" [--position.anchor <sélecteur>] [--position.offset N] [--position.length N]. Elle renvoie le nouveau fil. Les positions anchor peuvent être données sans offset/length pour créer une annotation sans plage de sélection.

portifact annotations resolve <id> <thread-id> marque le fil comme résolu ; portifact annotations reopen <id> <thread-id> le rouvre. Les deux renvoient le fil mis à jour.

portifact annotations export exporte toutes les annotations de tous les artefacts du compte sous forme d'un fichier JSON, utile pour les audits ou l'importation dans un autre système. Gardez la sortie là où l'utilisateur peut la voir.

Quotas, logs et licences

Uniquement dans les versions dont --help liste ces commandes. Chaque commande prend --json.

  • portifact usage renvoie {"totalPublishedBytes","totalPublishedFiles","totalRetainedVersions","totalVersionCount","publishedBytesLimit","publishedFilesLimit","retainedVersionsLimit","versionCountLimit","totalArtifactCount","artifactCountLimit","openThreads","openThreadsLimit","publishesThisMonth","publishesMonthlyAllowance","publishedBytesUploadedThisMonth","publishedBytesMonthlyAllowance","sourceBytesStored","sourceBytesLimit","sourceFilesStored","sourceFilesLimit","sourceArtifacts","sourceArtifactsLimit","signedInUsers","signedInUsersLimit","openSessions","openSessionsLimit","logEvents","logEventsLimit"}. totalPublishedBytes, totalPublishedFiles, totalRetainedVersions et totalVersionCount incluent toutes les versions (non supprimées). Les autres sont le nombre actuel, pas un total cumulé.
  • portifact logs [--limit N] [--cursor C] [--artifact <id>] [--version <id>] [--action opened|rolled_back|deleted|expired|archived|restored|renewed|rotated|annotated|access_changed] [--user <id>] [--since <ISO>] [--until <ISO>] renvoie {"cursor","items":[{"id","artifactId","artifactName","action","userId","userName","ip","path","ipGeo","userAgent","createdAt"}]}. Principalement destiné aux audits. Les artefacts supprimés et leurs logs sont supprimés définitivement.
  • portifact licenses imprime les licences des logiciels open source inclus dans le binaire.

Serveur MCP

Le serveur MCP à https://app.portifact.com/mcp expose les outils de cette section. Il a get_artifact_annotations, create_annotation_thread, reply_to_annotation_thread, set_thread_resolved, set_thread_open, comment_on_artifact (démarre un nouveau fil), export_annotations, get_artifacts, get_artifact_info, publish_small_document (jusqu'à 50 Ko de texte, sans téléchargement), publish_document (fichier jusqu'à 5 Mo), rollback_version, get_versions, download_version et get_usage_and_logs (pour les artefacts visités uniquement). Pour une explication complète de chaque outil, consultez le portifact --help étendu ou la documentation du serveur MCP.

Le serveur MCP peut également être utilisé pour automatiser les workflows de révision : par exemple, demander à l'agent de créer un fil sur un artefact avec "J'ai examiné cela et c'est bon" en tant que commentaire initial, puis de le marquer comme résolu. L'agent ne devrait le faire que si l'utilisateur le demande explicitement.

Workflow recommandé pour les agents

  1. Vérifier la prise en charge : exécutez portifact --version et portifact --help. Si la commande n'est pas trouvée, installez ou mettez à jour comme ci-dessus.
  2. Se connecter : exécutez portifact whoami --json pour vérifier ; si ce n'est pas le cas, exécutez portifact login et montrez le code à l'utilisateur.
  3. Prévisualiser et publier : exécutez portifact publish <chemin> --dry-run --json. Après confirmation, exécutez portifact publish <chemin> --json.
  4. Rendre public ou restreint : lisez portifact access <id> et ajustez avec portifact access set. Ajoutez des lecteurs avec portifact access add.
  5. Partager l'URL : imprimez l'URL de l'artefact. Ceci termine la publication ; l'utilisateur peut partager le lien comme il le souhaite.

Rappelez-vous : ne faites jamais de suppositions, ne contournez jamais les vérifications d'intégrité des fichiers, ne manipulez jamais les informations d'identification pour le compte de l'utilisateur et ne prenez jamais de décisions d'accès à sa place.

Skills associés