Implement penpot_export_library (SSE URI plus artifact download) #204
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills#204
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implement
penpot_export_library: export a Penpot file as a.penpotartifact, follow the streamed Transit URI to download the ZIP, and save it to disk — verifying it is a real.penpotarchive and reporting what it contains.Background
Depends on: #202
Export is the other half of the library-as-artifact strategy: the component library is authored in the Penpot UI once, exported here, and committed to the repo. It is also the tool that makes a design file portable — a project's screens and library can be captured and re-imported elsewhere.
Confirmed contract on Penpot 2.17:
export-binfiletakes a JSON body withfileId,includeLibrariesandembedAssetsas booleans. Do not send atypeparameter — that is a later-version change and 2.17 does not accept it.event: progressframes and terminates withevent: endwhosedata:is a Transit URI:{"~#uri": "https://…/assets/by-id/…"}.manifest.json,files/<id>.json, per-page/per-shape JSON, andcolors//typographies/directories. A committed artifact is therefore diffable in review, which is a genuine advantage over an opaque binary.Note the two-step nature: the SSE call returns a pointer, not the bytes. The download is a separate authenticated-or-not GET against the returned URI — verify whether the returned URL needs the token (it may be a pre-signed asset URL) and record the finding.
Documentation Required
A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.
docs/reference/penpot-api/<PENPOT_URL>/api/main/doc/openapi.json—export-binfile's parameters and response type, confirming the boolean pair and the absence oftypeon this version.includeLibraries/embedAssetsmap onto..penpotarchive's purpose and the import side of the round trip.includeLibrariesflag.docs/reference/transit-format/~#uritag and how a URI is represented (implemented in the previous step).docs/reference/nodejs/PK\x03\x04) before accepting the download.docs/reference/pi-coding-agent/anddocs/reference/typebox/details.Implementation Details
Create
extensions/penpot/src/tools/exportLibrary.tsand register the tool.Parameters:
file_id(string, required) — the file to export.out_path(string, optional) — where to write the ZIP; default to a deterministic path derived from the file name/id under the repo's scratch/artifact location (document the default). Refuse to overwrite an existing file unlessoverwrite: trueis passed.include_libraries(boolean, optional, defaulttrue).embed_assets(boolean, optional, defaulttrue).list_contents(boolean, optional, defaulttrue) — whether to report the archive's entry list.Behaviour:
export-binfilewith the JSON body{ fileId, includeLibraries, embedAssets }— notypeparameter.endframe via the Transit decoder, and extract the URI. Fail loudly with the raw payload if the end frame does not decode to a URI.out_path, then report: absolute path, byte size, and (whenlist_contents) the archive's entry names/counts —manifest.json,files/*.json,colors/*,typographies/*.Acceptance Criteria
{ fileId, includeLibraries, embedAssets }with booleans, and contains notypefield (asserted in the unit test).endframe's Transit URI is decoded and used as the download URL.endpayload that is not a URI fails loudly with the raw payload attached.list_contentsis true.out_pathis not overwritten unlessoverwrite: true.extensions/penpot/findings.md.Test Plan
Live validation (requires
PENPOT_URL/PENPOT_TOKEN, scratch file):unzip -l) and confirmmanifest.json,files/*.json,colors/*,typographies/*are present.include_libraries: falseand compare the archive contents to the default export — record what actually differs.out_pathand confirm the overwrite guard refuses; then withoverwrite: trueand confirm it replaces the file.extensions/penpot/findings.mdwhether the download URI required the auth header, whether it redirected, and the exactendpayload shape.pi-loop opened and merged a pull request for this issue: #237