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 8 commits into
mainfrom
tomaskislan/blu-6518-store-the-deepnote-api-token-in-env-and-resolve-it-for
Draft

tkislan wants to merge 8 commits into
mainfrom
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

Depends on #561 (@deepnote/cloud-sync and the aligned @deepnote/* versions), which is merged; this branch has main merged in and targets main.

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 the implementation commit (d279c45, on top of #561 at 7814e32). The later merge of main only changes four .github/workflows files (action version bumps); every other file is identical, so these results were not re-run:

  • 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

tkislan and others added 7 commits October 13, 2026 07:31
…kages

Add @deepnote/cloud-sync 0.1.0 and @deepnote/cloud 0.2.1 as direct
dependencies and move blocks, convert, database-integrations and
runtime-core to the versions cloud-sync pins, so the desktop bundle
resolves one copy of each. @deepnote/cli stays at 0.8.0 and keeps its own
nested copies.

Raise the runtime-core ws override to 8.21.3 and complete the default
ServerInfo in the runtime-core test mock (exited, stderrTail, childPids).

Closes #560

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
…add-deepnotecloud-sync-and-align-the-bundled-deepnote

package-lock.json was the only conflict, in two hunks on the root undici
entry (this branch: 6.28.0 reachable from production code; main: 6.29.0
marked dev-only after the 2026-10-06 vulnerability fixes). Rather than
splice the hunks, regenerate the lockfile: take main's lockfile, apply this
branch's package.json changes with `npm install`, and verify the result.

- Every lockfile entry changed only by main equals main's, and every
  entry changed only by this branch equals this branch's. The two entries
  changed by both sides resolve to undici 6.29.0 (main's version) without
  the dev flag (this branch's reachability), and the root package at
  version 1.8.0 with this branch's dependencies.
- A clean `npm install` leaves the lockfile byte-identical (the CI drift
  check), and `better-npm-audit audit` and `audit --production` exit 0
  with only the existing .nsprc exceptions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
…add-deepnotecloud-sync-and-align-the-bundled-deepnote

Conflicts: package.json (adjacent dependency lines) and package-lock.json
(main converted it to lockfileVersion 3 in #557 and bumped
@deepnote/sql-language-server to 3.0.1 in #556).

- package.json: keep the @deepnote/* bumps from this branch together with
  main's @deepnote/sql-language-server ^3.0.1.
- package-lock.json: regenerated from origin/main's lockfile plus the merged
  package.json with `npm install --package-lock-only` (npm 10.9.4, Node
  22.21.1) in a full checkout, not hand-merged. Per-package comparison against
  both parents shows every entry changed on only one side kept that side's
  value; the root entry is main's plus this branch's dependency changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
…add-deepnotecloud-sync-and-align-the-bundled-deepnote

Brings in the qltysh/qlty-action/install 2.4.0 bump (#547); no conflicts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
The override only restated what the lockfile already holds. The
@deepnote/runtime-core used by the extension (0.7.0) requires ws ^8.21.3,
which covers every current ws advisory, and the lockfile is byte-identical
without the override. Keeping an exact pin would also conflict with every
runtime-core release that raises its ws floor.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
…add-deepnotecloud-sync-and-align-the-bundled-deepnote

Brings in the codecov/codecov-action 7.1.1 bump (#540); no conflicts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017pqrotDqxwXxXYVEG5Z7wh
…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 (060d03f) to head (989fc82).
✅ 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           @@
##            main    #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.

Base automatically changed from tomaskislan/blu-6517-add-deepnotecloud-sync-and-align-the-bundled-deepnote to main October 14, 2026 09:18
…store-the-deepnote-api-token-in-env-and-resolve-it-for
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.

Store the Deepnote API token in .env and resolve it for Deepnote Cloud features

1 participant