¿Qué hace portifact?
Portifact publica artefactos estáticos con su propia URL, control de acceso, caducidad y restauración, y anotaciones opcionales de revisores autenticados. Los agentes lo usan a través de la CLI portifact o el servidor MCP remoto.
Verificar qué versiones de los comandos están soportadas
Portifact está en desarrollo temprano y gana comandos versión a versión. Antes de usar cualquier comando:
- Ejecuta
portifact --versionpara confirmar que la CLI esté instalada. - Ejecuta
portifact --helppara ver los comandos que soporta esta versión. - Usa solo los comandos y banderas que enumera
--help. Nunca adivines un comando. Si el usuario pide algo que la versión instalada no puede hacer, dilo y sugiere actualizar.
Las versiones cuyo --help enumera login pueden iniciar sesión, cerrar sesión y mostrar la cuenta conectada. Las versiones cuyo --help también enumera publish pueden publicar archivos y carpetas y gestionar artefactos y su acceso, y las versiones cuyo --help enumera versions, rollback y pull pueden listar versiones, revertir y descargar, y las versiones cuyo --help enumera annotations pueden gestionar hilos de comentarios, como se describe a continuación. Las versiones cuyo portifact publish --help enumera --source, --ref y --assets publican archivos ZIP, adjuntan fuentes privadas y publican activos Markdown; las versiones cuyo portifact pull --help enumera --git construyen un historial Git de las fuentes privadas; las versiones cuyo --help enumera usage muestran las cuotas de la cuenta; las versiones cuyo --help enumera logs muestran quién abrió un artefacto; y las versiones cuyo --help enumera licenses imprimen las licencias del software de código abierto en el binario (portifact licenses; también en https://portifact.com/third-party-notices). El servidor MCP en https://app.portifact.com/mcp puede listar, publicar y gestionar documentos pequeños; listar, descargar y revertir versiones; leer, escribir, moderar y exportar anotaciones; e informar sobre el uso y los registros de acceso; ver más abajo.
Instalar o actualizar la CLI
La CLI es un solo ejecutable y no necesita Node ni Bun. Pregunta antes de instalar software en la máquina del usuario.
curl -fsSL https://portifact.com/install | bash
irm https://portifact.com/install.ps1 | iex
- En Alpine Linux, instala primero el runtime de C++:
apk add libstdc++ libgcc. - Vuelve a ejecutar el mismo comando para actualizar.
- Establece
PORTIFACT_VERSION=0.2.0para instalar una versión específica. - En entornos no interactivos o aislados, establece
PORTIFACT_NO_MODIFY_PATH=1y llama directamente a~/.portifact/bin/portifacten lugar de editar los perfiles de shell. - El instalador verifica la descarga con las sumas de verificación de la versión y se detiene si hay una discrepancia. No eludas una verificación fallida.
Iniciar sesión
Comprueba primero con portifact whoami --json. El código de salida 0 imprime {"server","userId","clientId","scopes"}; el código de salida 3 significa que este dispositivo no ha iniciado sesión o la sesión ha terminado.
Para iniciar sesión, ejecuta portifact login (añade --json para un resultado analizable). Utiliza el flujo de dispositivo OAuth:
- La CLI imprime una página (
https://app.portifact.com/device) y un código de 8 caracteres en stderr, y abre el navegador cuando hay una persona en la terminal. Pasa--no-browserpara omitir la apertura. - Muestra al usuario la página y el código exactamente como se imprimieron, y pídele que inicie sesión allí, que verifique que la página muestre el mismo código y que lo apruebe. Nunca lo apruebes por él, y nunca le pidas que pegue nada de esa página de vuelta. La página de inicio de sesión pide al usuario que confirme que es mayor de 18 años y que acepte los términos (
https://portifact.com/terms); esa elección es suya, así que nunca la aceptes por él. Si la página dice que no puede enviar un código por correo electrónico a su dirección (correo anterior rebotado o reportado como spam) o que los códigos por correo electrónico están pausados para el mes, dile al usuario que inicie sesión con Google o GitHub. - El comando espera hasta que el usuario apruebe o deniegue, o hasta que el código expire (10 minutos), así que ejecútalo donde el usuario pueda ver su stderr y déjalo terminar.
Las credenciales van al almacén de credenciales del sistema operativo. Cuando no hay ninguno (por ejemplo, un servidor Linux sin un Secret Service), van a credentials.json en el directorio de configuración del usuario (~/.config/portifact en Linux, ~/Library/Application Support/portifact en macOS, %APPDATA%\portifact en Windows) con permisos solo para el usuario, y la CLI imprime una advertencia. Nunca leas, imprimas, copies o subas ese archivo o los tokens almacenados; la CLI los renueva por sí sola.
portifact logout revoca los tokens de este dispositivo en el servidor y luego los elimina. Revoca tokens, así que confírmalo primero con el usuario. Si no se puede contactar al servidor, mantiene las credenciales y sale con código 1; ejecútalo de nuevo más tarde.
PORTIFACT_APP_ORIGIN apunta la CLI a otro servidor y existe para pruebas y staging. No lo establezcas a menos que el usuario lo pida.
Publicar
Solo en versiones cuyo --help enumere publish.
- Vista previa primero:
portifact publish <ruta> --dry-run --jsonconstruye el manifiesto sin iniciar sesión ni subir nada. Devuelve{"dryRun","type","name","entrypoint","totalBytes","files":[{"path","size","sha256","contentType"}],"excluded":[{"path","reason"}]}, más"sources":{"chosenBy","totalBytes","files","excluded","origin","lfsPointers"}con--source. Muestra al usuario los archivos, las fuentes y lo que se omitió, y detente si algo parece privado. - Publicar:
portifact publish <ruta> --json. El resultado es{"artifactId","versionId","versionNumber","url","expiresAt","uploadId","files","totalBytes","sourceFiles","sourceBytes","sourceOrigin","uploadedFiles","uploadedBytes"}. El progreso y el manifiesto van a stderr.
- El tipo se deduce de la ruta: una carpeta, un
.zipo un archivo.htmles unsite,.mdesmarkdowny.txtestext. Pasa--type site|markdown|textpara otros archivos. Las carpetas y los archivos ZIP solo pueden ser sitios. - Un solo archivo HTML se publica como el
index.htmldel sitio. Una carpeta necesita unindex.html, o--entry <ruta>con la página que se abrirá primero. --nameestablece el nombre que se muestra en la lista de artefactos; por defecto es el nombre del archivo o carpeta.--artifact <id>publica una nueva versión de un artefacto existente, manteniendo su nombre, tipo y acceso a menos que--namecambie el nombre. Si alguien más publicó mientras tanto, falla conversion_conflict(código de salida 6); comprueba el artefacto y publica de nuevo.- Las carpetas siempre omiten
.git,node_modules, cachés, archivos del sistema y archivos que pueden contener secretos (.envy.env.*excepto ejemplos,.dev.vars,.npmrc,.netrc, claves SSH,*.pem,*.key,*.p12, estado de Terraform y similares). Nombrar un archivo de este tipo directamente es rechazado. El filtro es una red de seguridad, no una revisión: aún así verifica el manifiesto. - Los enlaces simbólicos y los archivos especiales son rechazados, nunca se siguen.
- Los archivos que la cuenta ya almacena no se vuelven a subir. Las subidas interrumpidas se reintentan, y si la publicación falla después de que comenzó la subida, la CLI la cancela para que no quede medio publicado.
- Las versiones cuyo progreso muestra
Uploaded part <n> of <m> of <ruta>suben archivos de más de 64 MiB en partes de 16 MiB, transmitidas desde el disco, por lo que un archivo grande nunca tiene que caber en la memoria y una parte fallida se envía de nuevo por sí sola. Una versión tiene como máximo 250 MB de archivos publicados, sea cual sea el tamaño de las partes. - Un archivo ZIP se expande en la máquina del usuario antes de que se suba nada. Cuando todos los archivos dentro están dentro de una carpeta, esa carpeta se convierte en la raíz. Los enlaces, archivos especiales, entradas cifradas, rutas que salen del archivo, nombres que colisionan, entradas superpuestas, archivos que se expanden más allá de su tamaño declarado o más de 200 veces, más de 10,000 entradas y cualquier cosa por encima de los límites de tamaño son rechazados (
invalid_input, código de salida 2, oquota_exceeded, código de salida 7). Las exclusiones predeterminadas y el filtro de secretos también se aplican a sus entradas. --source <ruta>adjunta fuentes privadas a la versión: solo el propietario puede verlas y descargarlas (pull --source), cuentan para el almacenamiento pero no para el tamaño publicado, y los archivos publicados y las fuentes juntos son como máximo 1,000. La ruta puede ser una carpeta, un solo archivo o un archivo ZIP. En una carpeta dentro de un repositorio git, git elige los archivos: lo que versiona más los archivos nuevos que no ignora, sin submódulos ni repositorios anidados.--ref <commit>adjunta exactamente el árbol de ese commit en su lugar (una rama, etiqueta o ID de commit; nunca incluye cambios no confirmados). La CLI luego registra el commit, la rama, el remoto sin credenciales y si había cambios no confirmados como elsourceOriginde la versión. Fuera de un repositorio, o singitinstalado, la carpeta se recorre con las exclusiones predeterminadas. Los archivos que pueden contener secretos nunca se adjuntan, diga lo que diga git. Una advertencia en stderr enumera los punteros de Git LFS, que se adjuntan como punteros, no como contenido; sugieregit lfs pullprimero.--assets <dir>publica una carpeta de imágenes y otros archivos junto con un documento Markdown, en su ruta relativa al documento, para que sus enlaces relativos funcionen. La carpeta debe estar dentro de la carpeta del documento, y se aplican las exclusiones predeterminadas.- Portifact renderiza un artefacto
markdownotextpor sí mismo: su URL abre una página renderizada a partir del documento, donde el HTML sin procesar en Markdown se muestra como texto y nada se ejecuta. El archivo original se descarga desde su propia ruta bajo la URL del artefacto (por ejemplo<url>plan.md, o<url>index.mdparapublish_document), con el mismo acceso que la página. Un documento tiene como máximo 5 MB de texto UTF-8, y un artefactotextes un solo archivo.
Gestionar artefactos
Solo en versiones cuyo --help enumere estos comandos. Cada comando acepta --json.
portifact list [--limit N] [--cursor C] [--search TEXTO] [--state active|archived|suspended|purging] [--type site|markdown|text] [--access public|authenticated|restricted] [--annotations off|open|locked]devuelve{"items":[...],"nextCursor"}; pasanextCursorcomo--cursor, con los mismos filtros, para la página siguiente. Cada elemento es lo que devuelveportifact infomás"activity":{"versions","currentBytes","openThreads","lastAnnotationAt"}: las versiones retenidas (incluida la actual), los bytes de la versión actual (archivos publicados y fuentes,nullsin una versión), los hilos abiertos en la versión actual y cuándo cambió por última vez un hilo en cualquier versión (nullsin hilos). El tráfico se cuenta por cuenta, no por artefacto, por lo que solo está enportifact usage.--searchcoincide con una parte del nombre (letras en cualquier caso) o del ID, y cada filtro dado debe cumplirse, así que úsalos para encontrar un artefacto en lugar de paginar por todo.portifact info <id>devuelve un elemento:{"id","name","type","state","accessMode","annotations","currentVersionId","url","expiresAt","archivedAt","purgeAt","createdAt","updatedAt"}, más"suspension":{"reason"}mientras el artefacto esté suspendido.portifact renew <id>,archive <id>yrestore <id>devuelven{"artifactId","state","expiresAt","purgeAt"}. Pregunta antes de archivar.portifact delete <id>elimina permanentemente. Pide a la persona en la terminal que escriba el ID del artefacto; sin terminal requiere--yesy de lo contrario falla con código de salida 2. Solo pasa--yesdespués de que el usuario haya confirmado ese artefacto exacto.
Controlar el acceso
Solo en versiones cuyo --help enumere access. Cada comando acepta --json e imprime {"artifactId","mode","readers","indexable","embeddable","policyGeneration"}.
portifact access <id>muestra la política actual. Úsalo para informar el modo de acceso después de publicar.portifact access set <id> restricted|authenticated|public:restrictedes el propietario más los lectores listados,authenticatedes cualquiera que haya iniciado sesión en Portifact,publices cualquiera con el enlace. Las cuentas en sus primeros 7 días no pueden hacer un artefacto público (account_too_new_for_public, código de salida 7).portifact access add <id> <correo>...yaccess remove <id> <correo>...cambian la lista de lectores (como máximo 100 direcciones a la vez). Los lectores solo importan mientras el modo searestricted.
Versiones, reversión y descarga
Solo en versiones cuyo --help enumere estos comandos. Cada comando acepta --json.
Un artefacto mantiene su versión actual más las versiones que reemplazó durante los últimos siete días. La reversión y la descarga funcionan solo en esas versiones retenidas.
portifact versions <id>las lista, primero las más recientes.--jsonimprime{"artifactId","items":[{"id","number","current","entrypoint","publishedBytes","sourceBytes","publishedFiles","sourceFiles","rendererVersion","committedAt","expiresAt","sourceOrigin"}]};expiresAtes cuándo una versión anterior deja de retenerse y esnullpara la actual, ysourceOrigines{"commit","branch","remote","dirty"}cuando las fuentes provienen de git, de lo contrarionull. El origen es lo que declaró el publicador; Portifact no puede verificarlo.portifact rollback <id> <id-de-versión>hace que una versión retenida vuelva a ser la actual. Se publica como una nueva versión con los mismos archivos, reinicia los 30 días y no cambia el acceso: los lectores eliminados desde esa versión permanecen eliminados. Pregunta al usuario antes de revertir. Devuelve{"artifactId","versionId","versionNumber","url","expiresAt","fromVersionId","fromVersionNumber"}. Si alguien publicó mientras tanto, falla conversion_conflict(código de salida 6); compruebaversionsde nuevo y pregunta al usuario. En versiones cuyoportifact rollback --helpdice "version ID or number",<id-de-versión>también puede ser el número de versión (3ov3), ypull --versiontambién acepta uno; un número que no se retiene falla connot_found(código de salida 5).portifact pull <id> --out <dir>descarga los archivos publicados de la versión actual en<dir>/published. Añade--sourcepara las fuentes privadas (en<dir>/source, solo propietario),--published --sourcepara ambas, y--version <id-de-versión>para una versión anterior.<dir>no debe existir aún; pull nunca escribe fuera de él, rechaza rutas dentro de una carpeta.git, verifica el SHA-256 de cada archivo y elimina la carpeta si algo falla (download_failed, código de salida 1). Devuelve{"artifactId","versionId","versionNumber","out","files":[{"kind","path","size","sha256"}],"totalBytes"}.
Anotaciones
Solo en versiones cuyo --help enumere annotations. Cada comando acepta --json. Las anotaciones son comentarios de revisores autenticados que están en la lista de lectores del artefacto, y el autor del artefacto. El servidor MCP también puede moderar y exportar.
portifact annotations status <id>devuelve{"artifactId","annotationsMode","autoCloseDays"}.portifact annotations set <id> off|open|locked [--auto-close N]cambia el modo.portifact annotations threads <id> [--cursor C] [--limit N]lista los hilos. Cada hilo:{"id","path","location","attributes","status","comments":[{"id","author","authorId","body","resolved","resolvedAt","createdAt"}],"createdAt","updatedAt"};"path"es la ruta del archivo y"location"su ubicación (rango de líneas o fragmento)."attributes"lleva las etiquetas personalizadas del artefacto.portifact annotations add <id> --message <texto>y--reply <id-de-hilo> --message <texto>crean hilos y comentarios.portifact annotations resolve <id> <id-de-comentario>yunresolve <id> <id-de-comentario>.portifact annotations export <id> [--out <archivo>] [--format csv|json]exporta las anotaciones.
Uso y registros
Solo en versiones cuyo --help enumere estos comandos. Cada comando acepta --json.
portifact usagedevuelve{"artifacts","versions","publishedBytes","sourceBytes","annotations","trafficBytes"}. Los bytes son los totales de todos los artefactos; el tráfico es la suma de las descargas de páginas, fuentes y archivos en los últimos 30 días, actualizada cada pocas horas.portifact logs <id> [--cursor C] [--limit N]devuelve{"items":[{"artifact","version","kind":"open|download","readers":[],"ip","country","userAgent","time"}],"nextCursor"}. Cada elemento muestra la versión abierta o descargada, los primeros lectores que coinciden con la política de acceso del artefacto en ese momento, y la IP y el agente de usuario. El artefacto debe ser tuyo.
Solución de problemas
- La CLI usa el código de salida 0 en éxito, 1 en error desconocido o fallo de descarga, 2 en entrada inválida, 3 cuando no ha iniciado sesión, 4 en error del servidor, 5 en recurso no encontrado, 6 en conflicto de versión, 7 en cuota excedida, 8 cuando el artefacto está suspendido y 9 cuando la versión de la CLI es demasiado antigua. Los errores van a stderr.
- Si dice "artifact suspension in effect", el artefacto está suspendido por el administrador. Dile al usuario que se ponga en contacto con el soporte.
- Si dice "version storage limit reached", la versión es demasiado grande. Reduce el tamaño o las fuentes.
- Si dice "creator cannot be a reader" al añadir un lector, el propietario ya es lector automáticamente.
- Si dice "account over quota", la cuenta superó sus límites de almacenamiento. Actualiza el plan o elimina algunos artefactos.
Notas sobre el flujo de agente
- Siempre previsualiza primero. El modo seco muestra lo que se subirá, destaca las rutas de fuentes privadas y enumera las omisiones. Si el usuario pide publicar, primero ejecuta la vista previa y muestra el resultado concisamente.
- Siempre comprueba el estado de inicio de sesión. Si
portifact whoamifalla, no continúes con el flujo de publicación hasta que el usuario haya iniciado sesión. - No eludas la verificación de 18+ y los términos. La página de inicio de sesión se los muestra al usuario; cualquier decisión es exclusivamente suya.
- Usa
--jsonpara analizar los resultados. La CLI imprime la salida legible y la JSON en diferentes flujos. Siempre captura la salida JSON para el siguiente paso del agente. - Manejo de errores. Cuando un comando falla, el código de salida y el mensaje de error en stderr te dicen por qué. Nunca asumas el motivo.
- Límites. La versión actual soporta hasta 250 MB de archivos publicados, 1000 archivos totales (publicados + fuentes) y 100 direcciones de correo por operación de lista de acceso.