Community程式設計與開發github.com

leynier/portifact

發佈並分享靜態成品(HTML 網站、Markdown 與純文字),控制誰能查看並收集審閱者的註解。

portifact 是什麼?

Portifact 是一個發佈與分享靜態成品的工具,支援 HTML 網站、Markdown 文件與純文字。開發者可以通過 `portifact` 命令列工具或 MCP 伺服器使用它,為每個成品產生專屬 URL,並設定存取權限(公開、需登入或僅限指定使用者)。它還支援版本管理、還原與下載,並允許經過驗證的審閱者留下註解。Portifact 的早期版本會持續演進,建議使用前執行 `portifact --help` 確認支援哪些指令。要開始使用,需先安裝 CLI(單一執行檔,不依賴 Node 或 Bun),然後透過 OAuth 裝置流程登入。發佈時可以預覽清單,確認沒有意外包含私人檔案,然後將成品上傳。上傳會自動跳過常見的敏感檔案(如 `.env` 與 SSH 金鑰),並且支援大檔案的分段上傳(超過 64 MiB 時分為 16 MiB 區塊)。此外,Portifact 還能附加私人原始碼(透過 `--source`),讓擁有者可以下載原始檔案,同時不影響公開發佈的內容。

相容平台✓Claude Code✓Codex CLI✓Cursor
npx skills add https://github.com/leynier/portifact/tree/HEAD/skills/portifact

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

portifact 是做什麼的?

Portifact 能發佈靜態成品,並為每個成品提供專屬 URL、存取權限、過期時間與還原功能,以及來自已驗證審閱者的選擇性註解。開發者可以透過 portifact 命令列工具或遠端 MCP 伺服器使用它。

確認已安裝版本支援的功能

Portifact 處於早期開發階段,每次發行都會新增指令。在使用任何指令前:

  1. 執行 portifact --version 確認 CLI 已安裝。
  2. 執行 portifact --help 查看此版本支援的指令。
  3. 僅使用 --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 裝置流程:

  1. CLI 會在錯誤輸出中印出一個頁面(https://app.portifact.com/device)與一組 8 位數的驗證碼,如果終端機前有人,它會嘗試開啟瀏覽器。傳入 --no-browser 可跳過開啟瀏覽器。
  2. 請將頁面與驗證碼按原樣顯示給使用者,並要求他們在該頁面登入,確認頁面上顯示的驗證碼相同,然後核准。絕對不要代替他們核准,也不要求他們從該頁面複製任何內容回傳給你。登入頁面會要求使用者確認他們年滿 18 歲並接受服務條款(https://portifact.com/terms);這是他們的選擇,絕對不要代替他們接受。如果頁面表示無法寄送電子郵件驗證碼到他們的地址(由於之前郵件被退回或被舉報為垃圾郵件),或者電子郵件驗證碼功能本月已暫停,請告知使用者改用 Google 或 GitHub 登入。
  3. 指令會等待使用者核准、拒絕或驗證碼過期(10 分鐘),因此請讓指令在使用者能看到其錯誤輸出的地方執行,並讓它執行完成。

憑證會儲存到作業系統的憑證儲存區。如果沒有憑證儲存區(例如沒有 Secret Service 的 Linux 伺服器),則會儲存到使用者設定目錄(Linux 為 ~/.config/portifact,macOS 為 ~/Library/Application Support/portifact,Windows 為 %APPDATA%\portifact)下的 credentials.json,並設定只有使用者才能讀取,同時 CLI 會印出警告。絕對不要讀取、印出、複製或上傳該檔案或儲存的權杖;CLI 會自動更新它們。

portifact logout 會撤銷此裝置在伺服器上的權杖,然後將其移除。它會撤銷權杖,因此請先與使用者確認。如果無法連線到伺服器,它會保留憑證並以退出碼 1 結束;請稍後再執行一次。

PORTIFACT_APP_ORIGIN 可以將 CLI 指向另一個伺服器,用於測試與暫存環境。除非使用者要求,否則不要設定此變數。

發佈

僅在 --help 列出 publish 的版本中可用。

  1. 先預覽:portifact publish <路徑> --dry-run --json 會建立資訊清單,但不會登入或上傳。它會回傳 {"dryRun","type","name","entrypoint","totalBytes","files":[{"path","size","sha256","contentType"}],"excluded":[{"path","reason"}]},如果使用了 --source,還會包含 "sources":{"chosenBy","totalBytes","files","excluded","origin","lfsPointers"}。請向使用者顯示檔案、原始碼與被排除的內容,如果發現任何看似私密的檔案,請停止。
  2. 發佈:portifact publish <路徑> --json。結果為 {"artifactId","versionId","versionNumber","url","expiresAt","uploadId","files","totalBytes","sourceFiles","sourceBytes","sourceOrigin","uploadedFiles","uploadedBytes"}。進度與資訊清單會輸出到錯誤輸出。
  • 類型會根據路徑自動推斷:資料夾、.zip 或 .html 檔案為 site,.md 為 markdown,.txt 為 text。如果是其他檔案,請使用 --type site|markdown|text 指定。資料夾與 ZIP 檔案只能是網站。
  • 單一 HTML 檔案會發佈為網站的 index.html。資料夾需要一個 index.html,或者使用 --entry <路徑> 指定要首先開啟的頁面。
  • --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 <路徑> 的版本會將超過 64 MiB 的檔案分割成 16 MiB 的區塊進行串流上傳,因此大型檔案不需要完全載入記憶體,失敗的區塊可以單獨重新傳送。單一版本最多可容納 250 MB 的已發佈檔案,無論區塊大小為何。
  • ZIP 檔案會在使用者機器上解壓縮後再上傳。如果其中的所有檔案都位於同一個資料夾內,則該資料夾會成為根目錄。連結、特殊檔案、加密條目、路徑逸出存檔、名稱衝突、重疊條目、解壓縮後超過宣告大小或超過 200 倍、超過 10,000 個條目以及超過大小限制的內容都會被拒絕(invalid_input,退出碼 2,或 quota_exceeded,退出碼 7)。預設的排除規則與機密過濾器同樣適用於其中的條目。
  • --source <路徑> 會將私人原始碼附加到該版本:只有擁有者才能檢視與下載(pull --source),它們會計入儲存空間,但不計入已發佈大小,且已發佈檔案與原始碼檔案合計最多 1,000 個。路徑可以是資料夾、單一檔案或 ZIP 檔案。在 git 儲存庫內的資料夾中,git 會選擇檔案:它所版本控制的檔案加上它未忽略的新檔案,不包含子模組或巢狀儲存庫。--ref <提交> 會附加該提交的完整目錄樹(分支、標籤或提交 ID;它絕不包含未提交的變更)。之後 CLI 會將提交、分支、不含憑證的遠端以及是否有未提交的變更記錄為版本的 sourceOrigin。在儲存庫外部,或未安裝 git 的情況下,資料夾會使用預設排除規則進行掃描。可能包含機密的檔案無論 git 如何處理,都不會被附加。錯誤輸出中的警告會列出 Git LFS 指標,這些指標會以指標的形式附加,而非實際內容;建議先執行 git lfs pull。
  • --assets <目錄> 會與 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> <版本-id> 會讓一個保留的版本再次成為目前版本。它會以新版本的形式發佈(內容與該版本相同),並重新計算 30 天的保留時間,且不會變更存取權限:自該版本以來被移除的讀者仍保持移除狀態。在執行還原先請徵求使用者同意。它會回傳 {"artifactId","versionId","versionNumber","url","expiresAt","fromVersionId","fromVersionNumber"}。如果中間有其他人發佈了版本,則會失敗並顯示 version_conflict(退出碼 6);請再次檢查 versions 並詢問使用者。在 portifact rollback --help 顯示「version ID or number」的版本中,<版本-id> 也可以使用版本號碼(3 或 v3),且 pull --version 也接受版本號碼;不是保留版本的號碼會失敗並顯示 not_found(退出碼 5)。
  • portifact pull <id> --out <目錄> 會將目前版本的已發佈檔案下載到 <目錄>/published。加上 --source 可下載私人原始碼(到 <目錄>/source,僅擁有者可用),加上 --published --source 可下載兩者,加上 --version <版本-id> 可下載先前版本。<目錄> 不得已存在;pull 絕對不會寫入到該目錄之外,會拒絕 .git 資料夾內的路徑,並檢查每個檔案的 SHA-256,如果任何檔案失敗,則會移除該目錄(download_failed,退出碼 1)。它會回傳 {"artifactId","versionId","versionNumber","out","files":[{"kind","path","size","sha256"}],"totalBytes"}。當成品與某人分享時...

…[截斷以進行翻譯]

相關技能