fix(core): lazy-load @napi-rs/keyring so a missing native binding can't kill the launcher - #1943
Open
olaservo wants to merge 1 commit into
Open
fix(core): lazy-load @napi-rs/keyring so a missing native binding can't kill the launcher#1943olaservo wants to merge 1 commit into
olaservo wants to merge 1 commit into
Conversation
…'t kill the launcher (#1852) The keyring module was imported eagerly at the top of secret-store.ts, so when the platform binary package (an npm optionalDependency, e.g. @napi-rs/keyring-win32-x64-msvc) is missing — npm's optional-deps bug npm/cli#4828, typically hit via an in-place npx cache upgrade — the loader's throw took down the whole launcher before any UI existed. Load the module lazily on first use instead, and route a load failure through the store's documented availability contract: get returns null, delete/deleteAllForServer no-op, and only set throws KeychainUnavailableError (503 at the routes). Entry construction also moves inside the try blocks, so the AsyncEntry constructor throwing on a keychain-less box (#1848) degrades the same way instead of escaping raw. KeychainUnavailableError now steers a missing-native-binding cause to a reinstall/npx-cache hint rather than the Linux libsecret advice. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 #1852
Problem
core/auth/node/secret-store.tsimported@napi-rs/keyringeagerly at module top level. The module's loader throws atrequiretime when the platform binary package (an npm optionalDependency, e.g.@napi-rs/keyring-win32-x64-msvc) is missing — which is exactly what npm's optional-deps bug (npm/cli#4828) produces, typically via an in-placenpxcache upgrade ("worked before today's update"). Because the import chain runs at backend startup (bundled into the web runner, CLI, and TUI alike), the whole launcher died before any UI existed:Reproduced on Windows 11 against published 2.1.0: install → delete
node_modules/@napi-rs/keyring-win32-x64-msvc→ run → exact reported error; restore the package → boots fine.Fix
@napi-rs/keyringlazily on first use (cached dynamic import; the package stays external in every bundler config, so this is a plain runtime require). A load failure engages the store's documented availability contract instead of crashing the process:get→null,delete/deleteAllForServer→ no-op, and onlysetthrowsKeychainUnavailableError(translated to a 503 by the routes;migratePlaintextSecretsskip branch engages as designed).AsyncEntryconstruction moves inside the existingtryblocks, so the constructor throwing on a box with no reachable secret service degrades the same way instead of escaping raw — this is the fix described in KeyringSecretStore throws from the AsyncEntry constructor, defeating its own degradation contract (500 on GET /api/servers) #1848 (theGET /api/servers500 in containers), implemented here because the lazy-load refactor makes them one mechanism.KeychainUnavailableErrornow steers a missing-native-binding cause to a reinstall / clear-npx-cache hint; other causes keep the Linux libsecret advice.Result: the Inspector always boots; secrets are simply unavailable until the install is repaired, and the user only hits a (actionable, typed) error when actually saving a secret.
Tests
vi.mockinsecret-store.test.tswith aconstructorThrowshook (the gap called out in KeyringSecretStore throws from the AsyncEntry constructor, defeating its own degradation contract (500 on GET /api/servers) #1848 — the stub constructor previously could not fail) and coveredget/set/deleteunder it.vi.resetModules+ a rejectingvi.doMock):getnull, deletes no-op,setrejects typed. The reinstall-hint wording is covered via direct construction (vitest substitutes its own message when a mock factory throws).Gate status (run on Windows)
validate:core/web/tui/launcher,coverage:web(gates the changedcore/authfiles, ≥90 all dimensions),coverage:launcher,smoke(tui self-skip: no TTY), and Storybook all pass locally. The remainingnpm run cisteps are currently unrunnable on any Windows machine for pre-existing reasons found and filed while validating this change: #1939 (verify:typecheck-coverage+verify:build-gatecan't spawnnpx), #1941 (CLI vitest worker teardown crash), #1942 (TUI coverage instrumentation flake), #1940 (.gitattributeseol churn). Linux CI on this PR is the authoritative full gate.🤖 Generated with Claude Code