issue-200: Implement penpot_add_image with media upload #233
No reviewers
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!233
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/issue-200/implement-penpot-add-image-with-media-upload"
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?
200: Implement penpot_add_image with media upload
Closes #200 — 200.
Generated in 14m 50s.
Review findings
I'll start by exploring the repository and understanding the relevant files.The changes are committed. Let me read the relevant files.Now let me make the fixes. Starting with the Medium findings.Now the media.ts changes:Now add tests. First client.test.ts:Now the High finding — add an
add_imagecase to the live-write-check harness:Now run the test suite:Let me review the final state of the key changed sections:All gates run. Only the pre-existingscripts/local-install.test.mjsfailure remains (mongodb missing frompi.extensionsonmaintoo), and all 370 penpot tests pass.Review Findings — 200
Summary
Fixed the three Medium findings (upload timeout, tolerant/actionable response normalisation, enforced
body/formDatamutual exclusion) and all five Low findings (README typo and wording, stricter SVG sniff, missing test for the unsupported-mtype branch, zero-dimension fallback). Added unit tests for each behaviour change;node --test extensions/penpot/src/*.test.ts extensions/penpot/src/tools/*.test.ts→ 370/370 pass. The High finding remains open: I added a livepenpot_add_imagecheck (check 5) toextensions/penpot/scripts/live-write-check.mjsso the upload can be verified end to end, but no Penpot token/instance is available in this environment, so the multipart request still has not actually been sent and the observed request/response is not recorded.Critical
(no findings)
High
extensions/penpot/findings.md:459— The multipart upload has never been sent to a live instance ("A real upload. No token was available, so the multipart request has never been sent"), and the issue's live test-plan steps 1–5 are explicitly left open. Every test stubsglobalThis.fetch, so the real contract (/api/rpc/command/upload-file-media-object,contentpart name,is-localboolean coercion, camelCase response keys,metadata.id) is verified only from source reading. Suggested fix: before merge, run the upload against the scratch file with a real token (or add anadd_imagecase toextensions/penpot/scripts/live-write-check.mjs) and record the observed request/response infindings.md.Medium
extensions/penpot/src/media.ts:392—sendMediaUploadcallspenpotRequestwithout atimeoutMs, so the upload is bounded by the client's 30 s default (client.ts:38). A legitimate file up to the 30 MiB cap can exceed 30 s on a modest link; the abort fires mid-send and the server may have already created the media object, so the tool reports a transport failure while an orphaned media object exists. Suggested fix: pass an upload-specifictimeoutMs(e.g.120_000, or scale fromprepared.bytes.byteLength) to thepenpotRequestcall insendMediaUpload.extensions/penpot/src/media.ts:297—normalizeMediaResponsefails hard whenname,mtype,widthorheightis absent after the upload has already created the media object, and the resulting error carries no media id — the caller gets a server error and an unrecoverable orphan. Penpot's own schema makesmtypeoptional ([:mtype {:optional true} [:maybe :string]]). Suggested fix: treatmtypeas optional (omit it frommetadatawhen absent) and, for the remaining required fields, include the created media id and a note that the media object exists in the file so the agent can reference or clean it up.extensions/penpot/src/client.ts:233—formDatais documented as "mutually exclusive withbody" but not enforced: if both are supplied, the JSONbodyis silently dropped and noContent-Typeis set. Suggested fix: throw a clear error whenoptions.body !== undefined && options.formData !== undefined(or return avalidation-categoryPenpotError).Low
README.md:38— Double comma typo introduced in the penpot row:...after the designated-target guard),,penpot_authorize_target``. Suggested fix: remove the extra comma.README.md:38— The penpot row now reads "penpot_add_rect/penpot_add_text/penpot_add_image(stagedadd-objrect/text/image primitives with library-colour and library-typography reference resolution...)", butpenpot_add_imagehas no colour/typography references. Suggested fix: split the sentence so only rect/text claim library-reference resolution, or reword to "rect/text library-reference resolution;penpot_add_imageuploads the media object first, after the designated-target guard".extensions/penpot/src/media.ts:135—looksLikeSvgaccepts any file whose first 4 KiB contains the substring<svg, so an HTML/JS file with an inline<svg>is classified asimage/svg+xmland uploaded (the server then rejects it, wasting a round trip and surfacing a less actionable error). Suggested fix: after stripping a BOM, leading whitespace, the XML declaration and comments, require the first element to be<svgrather than matching anywhere in the head.extensions/penpot/src/media.ts:310— The!isImageMtype(mtype)branch innormalizeMediaResponseis untested (the suite covers the missing-field branch only). Suggested fix: add a case returningmtype: "application/pdf"and assert the "unsupported media type" server error.extensions/penpot/src/tools/image.ts:88—deriveImageSizeacceptswidth: 0/height: 0(the schema setsminimum: 0) and, withkeep_aspect_ratiotrue, returns a 0×0 box (round(0 / ratio) === 0), staging a degenerate shape. Suggested fix: reject a zero/negative requested dimension, or fall back to the intrinsic axis when the supplied value is 0.Documentation
Updated files: