Skip to content

feat(cloud): store the Deepnote API token in .env and resolve it for Deepnote Cloud features - #563

Draft
tkislan wants to merge 1 commit into
tomaskislan/blu-6517-add-deepnotecloud-sync-and-align-the-bundled-deepnotefrom
tomaskislan/blu-6518-store-the-deepnote-api-token-in-env-and-resolve-it-for
Draft

tkislan wants to merge 1 commit into
tomaskislan/blu-6517-add-deepnotecloud-sync-and-align-the-bundled-deepnotefrom
tomaskislan/blu-6518-store-the-deepnote-api-token-in-env-and-resolve-it-for

Conversation

@tkislan

@tkislan tkislan commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai ignore

Summary

Adds "Deepnote: Set API Token" and "Deepnote: Clear API Token", and a desktop-only IDeepnoteApiTokenService that the upcoming sync and publishing features will use to get a token.

  • Set API Token checks the token with GET /v2/me, then writes DEEPNOTE_TOKEN=<token> to the nearest .env on the lookup path that already assigns it, else to the .env at the sync root or workspace folder root. When the built-in Git extension says git does not ignore that .env, a modal offers to add it to .gitignore.
  • Clear API Token removes every DEEPNOTE_TOKEN assignment from the .env files on the lookup path. It never deletes a file.
  • IDeepnoteApiTokenService has getToken(uri) (no prompt) and getOrPromptToken(uri) (runs the Set flow when no token is found).
  • showCloudError reports failed Deepnote Cloud calls and, on a 401, offers "Set API Token" for the same file or folder.
  • In an untrusted workspace both commands are disabled and the service neither reads, prompts for, nor sends a token. In the web extension host both commands are disabled and nothing is registered.

Closes #562

Stacked on #561 (@deepnote/cloud-sync and the aligned @deepnote/* versions). The base branch is #561's branch so the diff shows only this change; retarget to main once #561 merges.

Deviations from the issue

All deliberate, all covered by tests:

  1. The safety check (isSafeEnvEdit) also runs when the .env does not exist yet, so a token that would not read back as typed (for example one wrapped in backticks) is refused there too.
  2. A non-file workspace folder (virtualWorkspaces is true in package.json) is refused with localFilesOnly instead of being used as the fallback start.
  3. A .env or .gitignore that cannot be read or written is reported with envEditFailed (or logged for .gitignore) instead of rejecting the command. An error other than "file not found" while reading is never treated as "the file is empty".
  4. localFilesOnly and openFolderFirst are shown with showErrorMessage.
  5. Hardening that came out of review:
    • The lookup never walks above the workspace folder or sync root, even when VS Code reports the folder with another path case or a trailing slash, or the folder is missing on disk.
    • A .env that is not valid UTF-8 is left untouched and named in an error; a UTF-8 byte order mark is kept. A non-UTF-8 .gitignore gets its .env line appended without its other bytes changing.
    • Control characters are rejected in the token (fetch quotes the whole Authorization value in its error otherwise), so the apiTokenInvalidCharacters text became "The token cannot contain spaces, quotes, # or control characters".
    • ]( in the Deepnote workspace name is changed to ] ( in the confirmation, because notifications render [label](command:...) as runnable links.
  6. One test string says "publish this app." instead of "publish this Streamlit app." because cspell does not know "Streamlit".

Known and left as the issue specifies: a .env with mixed LF and CRLF endings is rewritten with a single ending.

Verification

Run on this branch (one commit on top of #561 at 7814e32):

  • npm run format, npm run lint (no errors), npm run typecheck and npm run spell-check pass.
  • Unit tests: 2978 passing, 0 failing, 239 pending. Before this change the suite had 2879 passing; the 99 new tests are in cloudErrors.node.unit.test.ts and deepnoteApiTokenService.node.unit.test.ts. They use real temporary directories, including a symlinked .deepnote-sync.json.
  • Mutation check: 66 one-token mutants of the compiled service and error helpers (guards removed, branches inverted, search order swapped, and so on). 64 are caught by a failing test. The other two are equivalent: the explicit repository and checkIgnore guards in isIgnoredByGit are redundant with its catch, and the case-insensitive comparison in the lookup loop only differs from the case-sensitive one on macOS and Windows.
  • npm run compile succeeds. dist/extension.node.js contains the service; dist/extension.web.bundle.js contains neither the service nor @deepnote/cloud-sync.
  • Two review rounds by opus-max subagents that ran the code-review-typescript skill, with one independent verifier per finding. Round one gave 16 findings (none P0 or P1); round two reviewed the fix commit and found one regression the first fixes had introduced, which is fixed. The findings that held up are the hardening in item 5 above plus extra tests.

Not verified

  • Nothing was run in a real VS Code or Cursor window, and no E2E suite was added. The commands were exercised only through the registered handlers in unit tests.
  • Unit tests run on Linux only. The Windows drive-letter and macOS path-case handling was checked with simulated process.platform in scratch runs, not on those systems.
  • The Git integration was tested against a fake Git extension, not the real one. The "Manual follow-up" items in Store the Deepnote API token in .env and resolve it for Deepnote Cloud features #562 (token check through a proxy, Cursor, the public docs page in deepnote/deepnote) are not done.
  • No real Deepnote API key was used; getWorkspace is replaced by a stub in tests.
  • Whether VS Code hides the commands in the web host before the extension activates (the deepnote.webExtension context key is set at activation) was not checked at runtime.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Got2L1hwAr7GBzdxTequW8

…Deepnote Cloud features

Adds "Deepnote: Set API Token" and "Deepnote: Clear API Token", which keep
DEEPNOTE_TOKEN in the nearest .env that already assigns it, else in the .env at
the sync root or workspace folder root. Set checks the token against the
Deepnote API first and offers to add .env to .gitignore when git does not
ignore it.

IDeepnoteApiTokenService returns the token that applies to a local file or
folder and prompts for one when none is set. showCloudError reports failed
Deepnote Cloud calls and offers to set a new token when Deepnote answers 401.
Both commands and the service are disabled in untrusted workspaces; the
commands are also disabled in the web extension host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Got2L1hwAr7GBzdxTequW8
@linear-code

linear-code Bot commented Oct 13, 2026

Copy link
Copy Markdown

BLU-6518

@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.91630% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 38%. Comparing base (7814e32) to head (d279c45).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...oks/deepnote/cloud/deepnoteApiTokenService.node.ts 97% 2 Missing and 2 partials ⚠️
src/notebooks/serviceRegistry.node.ts 0% 2 Missing ⚠️
src/notebooks/deepnote/cloud/types.ts 0% 1 Missing ⚠️
Additional details and impacted files
@@                                             Coverage Diff                                             @@
##           tomaskislan/blu-6517-add-deepnotecloud-sync-and-align-the-bundled-deepnote    #563    +/-   ##
===========================================================================================================
  Coverage                                                                          38%     38%            
===========================================================================================================
  Files                                                                             822     825     +3     
  Lines                                                                           41098   41325   +227     
  Branches                                                                         9044    9101    +57     
===========================================================================================================
+ Hits                                                                            15620   15888   +268     
+ Misses                                                                          23405   23353    -52     
- Partials                                                                         2073    2084    +11     
Files with missing lines Coverage Δ
src/notebooks/deepnote/cloud/cloudErrors.node.ts 100% <100%> (ø)
src/notebooks/deepnote/importClient.node.ts 46% <ø> (+9%) ⬆️
src/platform/common/constants.ts 100% <100%> (ø)
src/platform/common/utils/localize.ts 92% <100%> (+<1%) ⬆️
src/notebooks/deepnote/cloud/types.ts 0% <0%> (ø)
src/notebooks/serviceRegistry.node.ts 0% <0%> (ø)
...oks/deepnote/cloud/deepnoteApiTokenService.node.ts 97% <97%> (ø)

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant