mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
dc9dde41f3
* feat(pinterest): add Pinterest adapter suite 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> * fix(pinterest): drop board-update --privacy empty default `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> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>