portifact 是做什么的?
Portifact 可以发布静态制品,并为每个制品分配独立的 URL,支持访问控制、过期和恢复,并且可选地收集来自已认证审阅者的注释。代理通过 portifact CLI 或远程 MCP 服务器使用它。
检查已安装版本支持的功能
Portifact 正处于早期开发阶段,每个版本都会增加新的命令。在使用任何命令之前:
- 运行
portifact --version确认 CLI 已安装。 - 运行
portifact --help查看此版本支持的命令。 - 仅使用
--help列出的命令和标志。切勿猜测命令。如果用户要求的功能当前安装版本无法实现,请明确告知并建议更新。
如果版本的 --help 列出了 login,则该版本支持登录、注销和显示已登录账户。如果 --help 也列出了 publish,则该版本可以发布文件和文件夹,并管理制品及其访问权限;如果 --help 列出了 versions、rollback 和 pull,则该版本可以列出版本、回滚和下载;如果 --help 列出了 annotations,则该版本可以管理评论线程,如下所述。如果 portifact publish --help 列出了 --source、--ref 和 --assets,则该版本支持发布 ZIP 文件、附加私有源码和发布 Markdown 资产;如果 portifact pull --help 列出了 --git,则该版本可以为私有源码构建 Git 历史;如果 --help 列出了 usage,则该版本可以显示账户配额;如果 --help 列出了 logs,则该版本可以显示谁打开了制品;如果 --help 列出了 licenses,则该版本可以打印二进制文件中使用的开源软件许可证(portifact licenses;也可见 https://portifact.com/third-party-notices)。位于 https://app.portifact.com/mcp 的 MCP 服务器可以列出、发布和管理小型文档;列出、下载和回滚版本;读取、写入、审核和导出注释;以及报告使用情况和访问日志;详情见下文。
安装或更新 CLI
CLI 是一个单一的可执行文件,不需要 Node 或 Bun。在用户机器上安装软件前请先征得同意。
curl -fsSL https://portifact.com/install | bash
irm https://portifact.com/install.ps1 | iex
- 在 Alpine Linux 上,请先安装 C++ 运行时:
apk add libstdc++ libgcc。 - 重新运行相同的命令进行更新。
- 设置
PORTIFACT_VERSION=0.2.0以安装指定版本。 - 在非交互式或沙盒环境中,设置
PORTIFACT_NO_MODIFY_PATH=1并直接调用~/.portifact/bin/portifact,而不是修改 shell 配置文件。 - 安装程序会对照发布版本的校验和验证下载,如果校验和不匹配则会停止。不要绕过失败的验证。
登录
首先使用 portifact whoami --json 检查。退出码 0 表示已登录,并打印 {"server","userId","clientId","scopes"};退出码 3 表示此设备未登录或会话已过期。
要登录,请运行 portifact login(添加 --json 以获得可解析的结果)。它使用 OAuth 设备流:
- CLI 会在 stderr 上打印一个页面地址(
https://app.portifact.com/device)和一个 8 位字符的代码。如果用户在终端前,CLI 会自动打开浏览器。传递--no-browser可以跳过打开浏览器。 - 向用户显示确切的页面地址和代码,并请他们在该页面登录,检查页面是否显示相同的代码,然后批准。永远不要代替用户批准,也不要要求用户从该页面粘贴任何内容回传给您。登录页面会要求用户确认他们年满 18 岁并接受服务条款(
https://portifact.com/terms);这个选择是用户自己的,不要代替他们接受。如果页面提示无法向他们的电子邮件地址发送验证码(由于之前的邮件被退回或被举报为垃圾邮件),或者本月电子邮件验证码已暂停,请告知用户改用 Google 或 GitHub 登录。 - 命令会一直等待,直到用户批准或拒绝,或者代码过期(10 分钟),因此请在用户能看到其 stderr 的地方运行它,并让它自然结束。
凭据会保存到操作系统的凭据存储中。当没有凭据存储时(例如没有 Secret Service 服务的 Linux 服务器),它们会保存到用户配置目录下的 credentials.json 文件中(Linux 上为 ~/.config/portifact,macOS 上为 ~/Library/Application Support/portifact,Windows 上为 %APPDATA%\portifact),并设置仅为用户可访问的权限,同时 CLI 会打印一条警告。切勿读取、打印、复制或上传该文件或存储的令牌;CLI 会自动刷新它们。
portifact logout 会在服务器上撤销此设备的令牌,然后将其删除。它会撤销令牌,因此请先与用户确认。如果无法访问服务器,它会保留凭据并退出,返回退出码 1;请稍后重新运行。
PORTIFACT_APP_ORIGIN 用于将 CLI 指向另一台服务器,主要用于测试和预发布环境。除非用户要求,否则不要设置它。
发布
仅适用于 --help 列出 publish 的版本。
- 首先预览:运行
portifact publish <path> --dry-run --json会在不登录或上传的情况下构建清单。它返回{"dryRun","type","name","entrypoint","totalBytes","files":[{"path","size","sha256","contentType"}],"excluded":[{"path","reason"}]},如果使用了--source,还会返回"sources":{"chosenBy","totalBytes","files","excluded","origin","lfsPointers"}。向用户显示文件、源码以及被排除的内容,如果发现任何看起来是私有的内容,请停止操作。 - 发布:运行
portifact publish <path> --json。结果返回{"artifactId","versionId","versionNumber","url","expiresAt","uploadId","files","totalBytes","sourceFiles","sourceBytes","sourceOrigin","uploadedFiles","uploadedBytes"}。进度信息和清单会输出到 stderr。
- 类型是根据路径推断的:文件夹、
.zip文件或.html文件被视为site,.md被视为markdown,.txt被视为text。对于其他文件,请传递--type site|markdown|text。文件夹和 ZIP 文件只能是站点(site)。 - 单个 HTML 文件将作为站点的
index.html发布。文件夹需要包含一个index.html,或者使用--entry <path>指定一个首先打开的页面。 --name用于设置制品列表中显示的名称;默认使用文件或文件夹的名称。--artifact <id>用于发布现有制品的新版本,保留其名称、类型和访问权限,除非使用--name更改了名称。如果在此过程中其他人发布了版本,则会失败并返回version_conflict(退出码 6);请检查制品的版本列表,然后重新发布。- 文件夹总是排除
.git、node_modules、缓存、操作系统文件以及可能包含秘密的文件(例如.env和.env.*,但不包括示例文件、.dev.vars、.npmrc、.netrc、SSH 密钥、*.pem、*.key、*.p12、Terraform 状态文件等)。直接指定此类文件发布会被拒绝。这个过滤器是一个安全网,而不是代码审查工具:仍然需要检查清单。 - 符号链接和特殊文件会被拒绝,不会被跟踪。
- 账户中已存储的文件不会再次上传。中断的上传会重试,如果发布在上传开始后失败,CLI 会取消它,以确保不会半发布内容。
- 如果版本的进度显示
Uploaded part <n> of <m> of <path>,则说明该版本会将超过 64 MiB 的文件分成 16 MiB 的片段从磁盘流式上传,因此大文件无需完全加载到内存中,并且失败的片段可以单独重新发送。一个版本最多包含 250 MB 的发布文件,无论片段大小如何。 - ZIP 文件会在用户机器上解压,然后才上传任何内容。如果其中的所有文件都位于一个文件夹内,则该文件夹将成为根目录。链接、特殊文件、加密条目、试图离开压缩包的文件、名称冲突、重叠条目、解压后大小超过声明大小超过 200 倍的文件、超过 10,000 个条目以及任何超出大小限制的内容都会被拒绝(
invalid_input,退出码 2,或quota_exceeded,退出码 7)。默认排除规则和秘密过滤器同样适用于 ZIP 文件中的条目。 --source <path>用于将私有源码附加到该版本:只有所有者才能查看和下载它们(使用pull --source),它们计入存储空间,但不计入已发布文件的大小,并且已发布文件和源文件总数最多为 1,000。路径可以是文件夹、单个文件或 ZIP 文件。如果路径是 git 仓库内的文件夹,git 将选择文件:即它版本控制的文件加上它不忽略的新文件,但不包括子模块或嵌套仓库。使用--ref <commit>可以只附加该提交对应的树结构(可以是分支、标签或提交 ID;它永远不会包含未提交的更改)。然后,CLI 会将提交、分支、不含凭据的远程地址以及是否有未提交的更改记录为该版本的sourceOrigin。如果在仓库外部,或者未安装git,则使用默认排除规则遍历文件夹。即使 git 包含,可能包含秘密的文件也绝不会被附加。如果 stderr 上出现警告,列出了 Git LFS 指针(这些指针作为指针本身附加,而不是内容),建议先运行git lfs pull。--assets <dir>用于发布一个包含图片和其他文件的文件夹,与一个 Markdown 文档一起发布,这些文件相对于该文档的路径保持不变,以便其相对链接能够正常工作。该文件夹必须位于文档所在的文件夹内部,并且默认排除规则适用。- Portifact 会自行渲染
markdown或text制品:其 URL 会打开一个由该文档渲染的页面,其中 Markdown 中的原始 HTML 会显示为文本,并且不会执行任何脚本。原始文件可以通过制品 URL 下自己的路径下载(例如<url>plan.md,或对于publish_document的<url>index.md),具有与页面相同的访问权限。一个文档最多 5 MB 的 UTF-8 文本,一个text制品是单个文件。
管理制品
仅适用于 --help 列出这些命令的版本。每个命令都支持 --json。
portifact list [--limit N] [--cursor C] [--search TEXT] [--state active|archived|suspended|purging] [--type site|markdown|text] [--access public|authenticated|restricted] [--annotations off|open|locked]返回{"items":[...],"nextCursor"};将nextCursor作为--cursor的值,并保持相同的过滤器,以获取下一页。每个项目是portifact info返回的内容加上"activity":{"versions","currentBytes","openThreads","lastAnnotationAt"}:即保留的版本(包括当前版本)、当前版本的字节数(发布文件和源码,如果没有版本则为null)、当前版本上打开的线程以及任何版本上线程最后更改的时间(如果没有线程则为null)。流量是每个账户计费的,而不是每个制品,因此它只在portifact usage中显示。--search会匹配名称的一部分(不区分大小写)或 ID 的一部分,并且所有给定的过滤器必须同时成立,因此使用它们来查找制品,而不是翻遍所有页面。portifact info <id>返回一个项目:{"id","name","type","state","accessMode","annotations","currentVersionId","url","expiresAt","archivedAt","purgeAt","createdAt","updatedAt"},此外,当制品被暂停时,还会包含"suspension":{"reason"}。portifact renew <id>、archive <id>和restore <id>返回{"artifactId","state","expiresAt","purgeAt"}。归档前请先询问用户。portifact delete <id>会永久删除。它会要求终端前的用户输入制品 ID;如果没有终端,则需要--yes,否则会失败并返回退出码 2。只有在用户确认了该确切制品后,才能传递--yes。
控制访问
仅适用于 --help 列出 access 的版本。每个命令都支持 --json,并打印 {"artifactId","mode","readers","indexable","embeddable","policyGeneration"}。
portifact access <id>显示当前策略。在发布后使用它来报告访问模式。portifact access set <id> restricted|authenticated|public:restricted是所有者加上列出的读者;authenticated是任何登录到 Portifact 的人;public是任何拥有链接的人。在创建后的前 7 天内,账户不能将制品设为公开(account_too_new_for_public,退出码 7)。portifact access add <id> <email>...和access remove <id> <email>...用于更改读者列表(一次最多 100 个地址)。读者仅在模式为restricted时起作用。
版本、回滚和下载
仅适用于 --help 列出这些命令的版本。每个命令都支持 --json。
一个制品会保留其当前版本以及在过去七天内替换掉的版本。回滚和下载仅适用于这些保留的版本。
portifact versions <id>列出版本,最新的在前。--json打印{"artifactId","items":[{"id","number","current","entrypoint","publishedBytes","sourceBytes","publishedFiles","sourceFiles","rendererVersion","committedAt","expiresAt","sourceOrigin"}]};expiresAt是前一个版本停止被保留的时间,对于当前版本则为null;sourceOrigin在源码来自 git 时为{"commit","branch","remote","dirty"},否则为null。此来源信息是发布者声明的;Portifact 无法验证其真实性。portifact rollback <id> <version-id>使一个保留的版本再次成为当前版本。它会作为一个具有相同文件的新版本发布,重新开始 30 天的保留期,并且不会改变访问控制:自该版本以来被移除的读者仍然无法访问。在回滚前请询问用户。它返回{"artifactId","versionId","versionNumber","url","expiresAt","fromVersionId","fromVersionNumber"}。如果在此过程中有人发布了版本,则会失败并返回version_conflict(退出码 6);请再次检查versions并询问用户。在portifact rollback --help显示“version ID or number”的版本中,<version-id>也可以是版本号(3或v3),pull --version也接受版本号;如果提供的版本号未被保留,则会失败并返回not_found(退出码 5)。portifact pull <id> --out <dir>将当前版本已发布的文件下载到<dir>/published目录下。添加--source参数可以下载私有源码(到<dir>/source目录,仅所有者可下载),添加--published --source参数可以两者都下载,添加--version <version-id>参数可以下载先前的版本。<dir>目录不能已存在;pull绝不会在<dir>目录之外写入文件,会拒绝写入.git文件夹内的路径,会检查每个文件的 SHA-256 哈希值,如果任何文件校验失败,则会删除<dir>目录(download_failed,退出码 1)。它返回{"artifactId","versionId","versionNumber","out","files":[{"kind","path","size","sha256"}],"totalBytes"}。与某人共享的制品……