feat(pinterest): add Pinterest adapter suite - #2177
Open
yeeway0609 wants to merge 2 commits into
Open
Conversation
yeeway0609
force-pushed
the
feat/pinterest-adapter
branch
from
July 25, 2026 14:40
340c2ef to
70b5984
Compare
Adds `opencli pinterest` with 19 commands over Pinterest's internal resource API (`POST /resource/<Name>Resource/<action>/`, form-urlencoded `source_url` + `data`, `X-CSRFToken` + `X-Pinterest-PWS-Handler` headers, `bookmark` paging). - Read: search-pins / search-boards / search-users, pin, user, user-pins, user-boards, board-pins, board-sections, download. Reads work anonymously because Pinterest issues a csrftoken to logged-out sessions too. - Write: save (boardless repin lands in "Quick saves"), pin-create / pin-update / pin-delete, board-create / board-update / board-delete, board-section-create / board-section-delete. - Deletes require `--confirm`; without it the command resolves and names the target, then exits non-zero via ArgumentError (pin title + board for pin-delete, pin count for board-delete, section title for board-section-delete). - Boards are addressed by `<username>/<slug>`, a board URL, or the numeric `boardId` (BoardResource accepts `board_id` and reports the board's url, which is reused so the id path costs no extra round trip); sections by id or slug. Display names are not accepted — a name alone cannot say which account a board belongs to. A Pinterest site route such as a `/pin/<id>/` URL is rejected as such instead of being parsed as the board `pin/<id>`. - Board URLs are percent-decoded before use: Pinterest hands out encoded slugs for non-ASCII board names, and posting those verbatim answers HTTP 404. Slugs are compared Unicode-normalized so an NFD-composed accent still matches. - Sections can only be set by a follow-up move. PinResource/create and RepinResource/create accept a section key, answer HTTP 200, and file the pin at the board root anyway; only PinResource/update honours it (under `board_section_id`, not `section_id`). So `save --section` and `pin-create --section` create then move, and report the created pin id if the move fails rather than claiming success. - Omitting an optional text flag leaves the field alone; passing an empty string clears it. Pinterest refuses link edits on pins it scraped, and answers 401 for that, so its own message is surfaced rather than only "log in". - Typed errors throughout: ArgumentError for bad refs, unknown sections, `--section` without `--board`, and limits (validated before any request, with no silent clamp); AuthRequiredError on 401 (and 403 on writes only, since reads are anonymous); CommandExecutionError for malformed payloads and unresolvable targets. Live-verified end-to-end against a logged-in account: all 10 read commands, and the full write cycle (board-create → board-section-create → board-update → pin-create → pin-update → save → the three deletes, preview and confirmed), including section placement checked on each pin's own `section` field, non-ASCII board/section slugs, board-id addressing, and clearing a description. 131 tests; full suite 6258 passed; `tsc --noEmit` clean; `opencli validate` 0 errors; typed-error-lint and silent-column-drop both new=0; doc coverage 174/174. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
yeeway0609
force-pushed
the
feat/pinterest-adapter
branch
from
July 25, 2026 15:03
70b5984 to
6fb51b3
Compare
`coerceAndValidateArgs` applies an arg's default and then enforces `choices`
against it, so `default: ''` on a public|secret flag rejected every run that
omitted `--privacy`:
$ opencli pinterest board-update janedoe/my-board --name Foo
error: ARGUMENT Argument "privacy" must be one of: public, secret. Received: ""
Leaving the default off keeps the flag optional; the command already reads it
as `String(kwargs.privacy ?? '')`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
yeeway0609
marked this pull request as ready for review
July 25, 2026 15:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a
pinterestadapter covering both read and write surfaces — 19 commands over Pinterest'sinternal resource API (
POST /resource/<Name>Resource/<action>/, form-urlencodedsource_url+data,X-CSRFToken+X-Pinterest-PWS-Handler, feeds paginated byresource_response.bookmark).Reads work on a logged-out session because Pinterest issues a
csrftokento anonymous visitors too;only the writes need a login.
Boards are addressed by
<username>/<slug>, a board URL, or the numericboardIdfromuser-boards; sections by slug or id. Deletes require--confirmand otherwise name the resolvedtarget and exit non-zero.
--limitand flag combinations are validated before any request goesout.
Related issue:
Type of Change
Checklist
Documentation (if adding/modifying an adapter)
docs/adapters/(if new adapter)docs/adapters/index.mdtable (if new adapter)docs/.vitepress/config.mts(if new adapter)README.md/README.zh-CN.mdwhen command discoverability changedCliErrorsubclasses instead of rawErrorScreenshots / Output
All 19 commands live-verified against a logged-in account; everything created during verification
was removed afterwards with the adapter's own delete commands.
Full write cycle, on a scratch board created for the run:
Field clearing and the delete preview, on a second scratch board:
Notes for review:
PinResource/createandRepinResource/createaccept a section key, answer
HTTP 200, and file the pin at the board root anyway; onlyPinResource/updatehonours one, underboard_section_id(asection_idkey is likewiseaccepted and ignored). So
save --section/pin-create --sectioncreate then move, and a failedmove raises
CommandExecutionErrornaming the pin already created instead of reporting success.Placement was verified on each pin's own
sectionfield, not the section's cachedpin_count.board names (
/u/test-board-caf%C3%A9-%E6%B8%AC%E8%A9%A6/) and posting one verbatim answersHTTP 404. Slugs compare Unicode-normalized, so an NFD-composed accent still matches.cannot say which account owns the board;
BoardResourceacceptsboard_id, which is what makesthe numeric form work, and the board it returns is reused so that path costs no extra round trip.
A
/pin/<id>/or/search/URL is reported as such instead of being parsed as the boardpin/<id>, and a board ref passed where a username belongs says so rather than 404ing on amangled name.
--description ""clears the field while omitting it changes nothing. Pinterest refuses linkedits on pins it scraped and answers 401 for that, so its own message is surfaced
(
Pinterest refused this write: 你無權存取該資源) rather than only "log in".CommandExecutionError, hinting at a stale CSRF token); a403 on a write is
AuthRequiredError.page.gotocan resolve before thecsrftokencookieexists, so the in-page script waits for it instead of firing a doomed request.
savewithout--boardposts a boardless repin, which Pinterest files under "Quick saves".pin-createcannot do that — a new pin always needs a board. Pinterest also overrides--descriptionfor scraped pins.userexposesinterestFollowingCountbecause Pinterest'sfollowing_countbundles followedpeople and topics.
clis/pinterest/*.test.jspasses 131 / 131 (ref and board-id parsing, the auth / CSRF / HTTP-errorladder, bookmark pagination with dedup, promoted-pin filtering, the
--confirmgate on all threedeletes, the section path (no section key on create,
board_section_idon the follow-up, a failedmove surfacing the pin id, a display title not resolving,
--sectionwithout--boardrejected),and that a board id is not re-fetched. Full suite 6258 passed;
tsc --noEmitclean;opencli validate0 errors;check:typed-error-lintandcheck:silent-column-dropbothnew=0;check-doc-coverage.sh --strict174/174.