Skip to content

feat(photos): add upload and app-created album commands - #1185

Merged
steipete merged 1 commit into
openclaw:mainfrom
beyondzero:feat/photos-upload
Oct 7, 2026
Merged

steipete merged 1 commit into
openclaw:mainfrom
beyondzero:feat/photos-upload

Conversation

@beyondzero

@beyondzero beyondzero commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Add Google Photos media upload and app-created album list/create commands. Uploads use the raw-bytes endpoint followed by bounded media-item creation, report per-file results, support an album and description, and return nonzero on failures.

Photos authorization stays read-only by default. --photos-scope=append is required to request write access, and it cannot be combined with --readonly. Narrowing an existing grant disables incremental scopes; automatic reauthorization preserves that narrowed grant. Runtime read-only request enforcement continues to reject the new write endpoints.

The contributor supplied live evidence from one account/client: append-authorized upload and album creation succeeded, narrowing rejected writes at Google while reads continued, and explicit re-widening restored uploads. Automatic invalid-grant reauthorization is covered by regression tests rather than revoking the live account.

Maintainer review also corrected the extension filter to accept Google-documented DIVX, M2T, and MMV videos, with failing-before/passing-after dry-run regression coverage that still rejects unrelated file types. Command docs and the Photos agent skill describe the new commands. Thanks @beyondzero for the implementation and live proof.

Live authority-chain evidence: #1185 (comment)

Final maintainer validation: the supported-video regression failed before the filter correction and passed afterward; the complete make ci gate passed on AWS Crabbox; independent Codex review is clean through P2.

@beyondzero
beyondzero requested a review from a team as a code owner October 2, 2026 02:42
@clawsweeper

clawsweeper Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 2, 2026
@clawsweeper

clawsweeper Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed October 7, 2026, 1:18 AM ET / 05:18 UTC (Revision 4).

ClawSweeper review

What this changes

Adds Google Photos uploads and app-created album listing and creation, with explicit OAuth write opt-in, preserved narrowed grants, tests, and command documentation.

Merge readiness

✅ Ready for maintainer review

This PR remains useful: uploads and album management are absent from main and v0.43.0. Prior findings are resolved, live proof is sufficient, and no blocking defect was found.

Likely related people: steipete and higginz777 are routing candidates based on Photos and OAuth history; historical introduction is unverified.

Priority: P2
Reviewed head: d18bfcb59d1034f28629dac9447ce5854d6133c2

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused implementation with strong live authorization proof, resolved prior findings, and no remaining actionable blocker.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The live account/client transcript exercises Photos upload and album creation successfully, then demonstrates Google rejecting writes after narrowing and restoring uploads only after explicit re-widening. Automatic reauthorization has supplemental production-path regression coverage. Existing token storage contracts are unchanged, requiring no migration.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The live account/client transcript exercises Photos upload and album creation successfully, then demonstrates Google rejecting writes after narrowing and restoring uploads only after explicit re-widening. Automatic reauthorization has supplemental production-path regression coverage. Existing token storage contracts are unchanged, requiring no migration.
Evidence reviewed 9 items Pinned introduced change: Read the merge-base-to-head production, test, and documentation changes. Raw test-merge parents match pinned main followed by the original PR head, and its tree matches the reviewed head.
Still necessary on main and latest release: Inspected Photos command registration on pinned main and the supplied v0.43.0 release commit. Both provide list, search, get, download, and Picker, without upload or album commands.
Latest release comparison: The Photos command tree at the supplied v0.43.0 release revision does not implement this PR’s requested capability.
Findings None None.
Security None None.

How this fits together

The Photos CLI sends user-selected media and album requests through gogcli’s account authentication and HTTP transport to Google Photos. OAuth scopes and runtime read-only enforcement control writes, and the CLI returns structured results.

flowchart TD
 A[Files and album commands] --> B[Photos CLI validation]
 C[Account OAuth grant] --> D[Authenticated transport]
 B --> D
 D --> E{Read-only enforcement}
 E -->|Allowed| F[Google Photos API]
 E -->|Blocked| G[Error output]
 F --> H[Album and upload results]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test LOC production +607/-6; tests +299/-0 Production growth implements three new CLI operations and their API and OAuth support, with focused regression coverage.

Technical review

Best possible solution:

Retain the opt-in Photos write commands while preserving read-only defaults and narrowed grants.

Do we have a high-confidence way to reproduce the issue?

Not applicable to this feature request; the contributor’s live transcript demonstrates successful writes and rejection after grant narrowing.

Is this the best way to solve the issue?

Yes. Extending the existing Photos client with explicit append-scope opt-in preserves the established read-only path without creating a competing implementation.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 5bfd65948284.

Labels

Label changes:

No label changes.

Label justifications:

  • P2: This is a bounded Photos capability improvement with explicit write opt-in and no demonstrated urgent regression.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The live account/client transcript exercises Photos upload and album creation successfully, then demonstrates Google rejecting writes after narrowing and restoring uploads only after explicit re-widening. Automatic reauthorization has supplemental production-path regression coverage. Existing token storage contracts are unchanged, requiring no migration.
  • proof: sufficient: Contributor real behavior proof is sufficient. The live account/client transcript exercises Photos upload and album creation successfully, then demonstrates Google rejecting writes after narrowing and restoring uploads only after explicit re-widening. Automatic reauthorization has supplemental production-path regression coverage. Existing token storage contracts are unchanged, requiring no migration.

Evidence

What I checked:

  • Pinned introduced change: Read the merge-base-to-head production, test, and documentation changes. Raw test-merge parents match pinned main followed by the original PR head, and its tree matches the reviewed head. (d18bfcb59d10)
  • Still necessary on main and latest release: Inspected Photos command registration on pinned main and the supplied v0.43.0 release commit. Both provide list, search, get, download, and Picker, without upload or album commands. (internal/cmd/photos.go:20, 5bfd65948284)
  • Latest release comparison: The Photos command tree at the supplied v0.43.0 release revision does not implement this PR’s requested capability. (internal/cmd/photos.go:20, 3b5122f4c81c)
  • Live allowed and narrowed-grant proof: The captured contributor transcript at feat(photos): add upload and app-created album commands #1185 (comment) exercises the production upload and album entrypoints with one real consumer account and Desktop OAuth client. Append access succeeds; narrowing rejects uploads and album creation at Google while reads continue; explicit re-widening restores uploads. Automatic invalid-grant reauthorization is explicitly covered by regression tests rather than a live revocation. (internal/cmd/photos_write.go:75, d18bfcb59d10)
  • Authorization and upgrade safety: Read-only remains the default, append conflicts with --readonly, and narrowed Photos grants disable incremental authorization in both auth add and automatic reauthorization. Existing stored token fields and serialization remain unchanged; no stored-state migration is introduced. (internal/googleauth/reauth.go:161, d18bfcb59d10)
  • Final request enforcement: The Photos client uses the existing authenticated transport. Its outer read-only transport rejects mutating POST requests before calling the underlying transport; the Photos allowlist admits only media-item search, not uploads, batch creation, or album creation. (internal/googleapi/read_only.go:49, d18bfcb59d10)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • higginz777: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (3 earlier review cycles)
  • reviewed 2026-10-02T02:45:54.448Z sha 5c40088 :: needs real behavior proof before merge. :: [P1] Disable incremental grants when narrowing Photos access | [P3] Add the required changelog reference and contributor credit
  • reviewed 2026-10-04T15:40:21.731Z sha 23b7f7d :: needs maintainer review before merge. :: none
  • reviewed 2026-10-04T16:23:28.299Z sha 23b7f7d :: needs maintainer review before merge. :: none

@beyondzero

Copy link
Copy Markdown
Contributor Author

Thanks for the review. This addresses all three items: the P1 grant narrowing, the changelog, and the real-behaviour proof.

Fix: narrowing Photos no longer keeps an earlier append grant

  • auth add: when photos is requested without --photos-scope=append, include_granted_scopes is disabled (photosNarrowsGrant).
  • Automatic reauthorization (googleauth.Reauth): a stored Photos grant that has the read scope but no appendonly is treated as narrowed, using the same mechanism as hasLimitedGmailGrant (hasLimitedPhotosGrant).
  • Tests:
    • TestPhotosNarrowsGrant and TestHasLimitedPhotosGrant.
    • TestReauthKeepsNarrowedPhotosGrantNarrow, which drives the real Reauth() with read-only, read-only + calendar, and append stored grants. Without the reauth.go change it fails (disable incremental grants = false, want true) for both narrowed cases; with the change it passes.
  • Changelog: the entry now cites (#1185) — thanks @beyondzero.
  • Error hint: the --photos-scope=append hint now also covers the uploads endpoint's 401. Measured below: Google refuses uploads with 401 and the JSON endpoints with 403.
  • make ci passes: fmt-check, golangci-lint (0 issues), deadcode, test, docs-check, agent-skills-check.

Authority-chain proof (live)

This uses one real consumer Google account and one Desktop OAuth client throughout, with a generated test image and a throwaway app-created album. The account, client ID, album and media IDs, and local paths are redacted.

Generated authorization URLs (auth add --remote --step 1, nothing consented):

--photos-scope=readonly: include_granted_scopes=(absent)  scopes=[email, photoslibrary.readonly.appcreateddata, userinfo.email, openid]
--photos-scope=append:   include_granted_scopes=true      scopes=[email, photoslibrary.appendonly, photoslibrary.readonly.appcreateddata, userinfo.email, openid]

A. Append grant: album create and upload succeed. The stored scopes include photoslibrary.appendonly.

$ gog photos albums create "gogcli PR 1185 proof"
{"album": {"id": "<album-id>", "title": "gogcli PR 1185 proof", "isWriteable": true, ...}}
$ gog photos upload proof-1.jpg --album <album-id>
{"failed": 0, "results": [{"file": "proof-1.jpg", "mediaItemId": "<media-id>", "status": "ok"}], "uploaded": 1}

B. Narrow the same account and client to read-only (default Drive/Gmail modes):

$ gog auth add <account> --services photos --photos-scope=readonly --force-consent
  (authorization URL: scope=email photoslibrary.readonly.appcreateddata userinfo.email openid; no include_granted_scopes)
Authorization received. Finishing…
services  photos
recorded scopes: [email, photoslibrary.readonly.appcreateddata, userinfo.email, openid]

C. Writes after narrowing, without --readonly, so Google decides:

$ gog photos upload proof-2.jpg --album <album-id>
error   proof-2.jpg   photos API error (401): {"code": 16, "message": "Authentication session is not defined."}
uploaded 0 of 1
exit 1
$ gog photos albums create "gogcli PR 1185 proof (should fail)"
photos API error (403 PERMISSION_DENIED): Request had insufficient authentication scopes.
exit 6
$ gog photos albums list        # read access is unaffected
<the proof album is listed>

D. Re-widening needs the explicit opt-in, and then works again:

$ gog auth add <account> --services gmail,photos --gmail-scope readonly --photos-scope=append --force-consent
recorded scopes: [email, gmail.readonly, photoslibrary.appendonly, photoslibrary.readonly.appcreateddata, userinfo.email, openid]
$ gog photos upload proof-3.jpg --album <album-id>
ok   proof-3.jpg   <media-id>
uploaded 1 of 1

Automatic reauthorization runs only after invalid_grant and stops for an interactive confirm. Triggering it live would have meant revoking the app's access for the whole account, which would also revoke other tokens on this client. It is covered instead by TestReauthKeepsNarrowedPhotosGrantNarrow above, which exercises the real Reauth() path and fails without the fix.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Oct 4, 2026
@beyondzero

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

Keep read-only authorization as the default, require explicit append scope for
writes, and preserve narrowed grants during reauthorization. Accept documented
video formats and cover the upload/album workflows with tests and docs.

Co-authored-by: beyondzero <4196148+beyondzero@users.noreply.github.com>
@steipete
steipete force-pushed the feat/photos-upload branch from 23b7f7d to d18bfcb Compare October 7, 2026 05:13
@steipete
steipete merged commit 795cbee into openclaw:main Oct 7, 2026
7 of 8 checks passed
@steipete

steipete commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Landed with contributor credit and read-only authorization preserved by default. The maintainer pass added DIVX/M2T/MMV support after reproducing their rejection through failing dry-run regressions; the corrected test and full make ci gate passed on AWS Crabbox (cbx_82fe63e89d13, run run_a3dee4100eaa944da60cc40b50cb8d6b). Final independent Codex review is clean through P2.

Exact-head Linux, minimum-Go, macOS, Windows, worker, and Docker checks passed at d18bfcb59d1034f28629dac9447ce5854d6133c2: https://github.com/openclaw/gogcli/actions/runs/37575271000

Live upload/grant-narrowing evidence remains the contributor’s documented account/client proof: #1185 (comment). Thanks @beyondzero.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants