feat: JSON on pCloud storage plugin (010) - #121
Merged
Conversation
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Adds pCloud as a third storage backend, following the transport + storage-plugin pattern established by JSON-on-Google-Drive. json_storage_core is untouched. - transports/pcloud_transport.py: PCloudClient (authenticated REST session, region-aware) + PCloudTransport implementing the 5-method transport interface against pCloud's path-based API. - json_pcloud_storage.py: the full storage plugin contract, wiring the transport to json_storage_core. - app.py: registers json-pcloud, adds the /auth/pcloud link flow and its callback, and a provisioner for the dispatch table. pCloud is storage, not identity. Google remains the identity provider; the user links pCloud separately and its access token lives in the browser next to the Google credentials, never on the server. storage.js now merges the OAuth handoff into the stored credentials record instead of replacing it, so the two coexist and a Google re-login does not unlink pCloud. Deployment prerequisite: register the redirect URIs on the pCloud app at https://docs.pcloud.com/my_apps/ — pCloud rejects an unregistered redirect_uri. Closes #96 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011RYAzzVJJqrsp6TkeRS4vX
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Two unrelated-to-pCloud CI breaks surfaced on this PR: - ruff 0.16 stabilized PLR0917 (too-many-positional-arguments) out of preview, so it began firing on the MCP tool signatures in pomodoro_tools.py and todos_plugin.py. Accepted alongside the PLR0913 ignore those files already carry, for the same reason: agents pass these by name and bundling them would obscure the tool schema. - ruff 0.16 also began formatting fenced code blocks inside Markdown, rewriting illustrative snippets in spec documents. Specs are prose, not source — excluded, matching gourmand.toml. Gourmand findings in the new pCloud code are fixed rather than excepted where the project has no precedent for excepting them: - generic_names: no exception for this check exists anywhere in the repo, so data/result are renamed to body/result_code/stat. - verbose_comments: comment blocks folded into the docstrings they belong on, matching google_drive_transport.py which needs no exception. - conditional_wrapper in the test mock: replaced the early-return dispatch with a response table. Exceptions added only where precedent is exact: the save_pomodoros_batch guard clause (verbatim from json_google_drive_storage.py), _find_file existing to keep the transport interface identical to GoogleDriveTransport, and the spec-kit Markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011RYAzzVJJqrsp6TkeRS4vX
Gourmand allows no standalone comment blocks in this file (no exception, matching google_drive_transport.py which has none). The NOT_FOUND_CODES rationale moves to the module docstring, where it reads better anyway. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011RYAzzVJJqrsp6TkeRS4vX
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.
Closes #96. Spec:
specs/010-json-pcloud/.Adds pCloud as a third storage backend, following the transport + storage-plugin pattern from JSON-on-Google-Drive.
json_storage_coreis untouched.What's here
transports/pcloud_transport.pyPCloudClient(authenticated, region-aware REST session) +PCloudTransportimplementing the 5-method transport interfacejson_pcloud_storage.pyjson_storage_coreapp.pyjson-pcloud, adds/auth/pcloud+/auth/pcloud/callback, adds the provisioner to the dispatch tablestatic/js/storage.js,templates/index.htmlDesign decision: pCloud is storage, not identity
Google stays the identity provider — it's how the app learns the user's email and resolves their per-user backend (spec 007). The user signs in with Google, then links pCloud separately. The pCloud access token lives in the browser's IndexedDB next to the Google credentials and rides in on each request; the server never stores it.
To make that work,
consumePendingAuthnow merges the OAuth handoff into the stored credentials record instead of replacing it, so a Google re-login doesn't unlink pCloud (and vice versa)._provision_json_pcloud(reached on a Google sign-in) only restates the already-linked path — the real folder creation happens once, in the pCloud callback, which is the only place a pCloud token exists server-side.The pCloud app's redirect URIs must be registered at https://docs.pcloud.com/my_apps/ before the flow completes — pCloud answers an unregistered
redirect_uriwithredirect_uri is not autorized. Register both:https://acquacotta.crunchtools.com/auth/pcloud/callbackhttp://localhost:5000/auth/pcloud/callbackAlso set
PCLOUD_CLIENT_ID/PCLOUD_CLIENT_SECRETin the production env (documented inacquacotta.env.example).Out of scope
MCP access over pCloud. The MCP server's sealed tokens carry a Drive folder id and Google credentials, and every tool resolves through
json_google_drive_storage. Serving pCloud users over MCP means changing the sealed-token payload — a separate spec.Testing
ruff check+ruff formatcleansmoke_test.shpasses (Flask + MCP handshake)/api/pluginslistsjson-pcloud;/auth/pcloud400s without an email and 302s to pCloud's consent page with one (verified against the real client id — the app renders as "Acquacotta")🤖 Generated with Claude Code
https://claude.ai/code/session_011RYAzzVJJqrsp6TkeRS4vX