Skip to content

fix(core): resolve npm provider entrypoints on Node - #45608

Closed
opencode-agent[bot] wants to merge 2 commits into
devfrom
npm-node-backport
Closed

opencode-agent[bot] wants to merge 2 commits into
devfrom
npm-node-backport

Conversation

@opencode-agent

@opencode-agent opencode-agent Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fix custom npm provider loading in the V1 Desktop Node runtime. The existing resolver returns a package directory URL, which Node rejects with ERR_UNSUPPORTED_DIR_IMPORT when the provider loader imports it.

  • Backport the V2 package-entry resolution approach: use resolve.exports with the runtime's actual import conditions, then Node's resolver for legacy packages without exports.
  • Select the Node implementation through a conditional package import so Node-only hooks never run in Bun. Preserve Bun's existing import.meta.resolve(name, directory) behavior.
  • Keep unavailable or blocked root exports unavailable instead of bypassing them through main or a require-only export.
  • Guard condition capture against unrelated CommonJS loads during concurrent startup. A full V1 provider bundle exposed these overwriting the import conditions while the probe awaited completion; the regression fixture reproduces this without Electron.
  • Resolve the package root independently of its installed alias. Run the Node regression suite as part of core's normal test script so unit CI covers it.

Verification

  • packages/core: bun run test:node-npm with Node 24.13.0 — 11 passing tests covering scoped/aliased packages, string/nested exports, condition ordering, module-sync, custom conditions, legacy main/index.js, and unavailable root exports. A concurrent CommonJS dependency is evaluated during resolver initialization. This produced two failures before the condition-capture guard and passes afterward.
  • Bundled those tests with bun build --target=node and ran them with Electron 42.3.3's Node 24.15.0 runtime (ELECTRON_RUN_AS_NODE=1) — 11 passing tests.
  • Isolated Node-target integration smoke in the same Electron runtime: real Npm.add() against a cached custom package, followed by dynamic import and provider-factory invocation — passed.
  • Real Electron 42.3.3 utilityProcess smoke with the V1 application runtime: project config → Provider.getModel() → getLanguage() → npm installation/resolution → custom SDK factory → fixture model doGenerate(). Both cold installation and warm-cache loading passed. Replacing only the resolver with the original V1 Node expression makes this same harness fail with ProviderInitError / ERR_UNSUPPORTED_DIR_IMPORT. The fixture has separate import/require entries and throws if the require entry is selected. No paid inference or external provider was used.
  • packages/opencode: env -u GOOGLE_VERTEX_API_KEY -u VERTEX_API_KEY bun test test/provider — 560 passing tests. The initial run inherited host Vertex credentials and selected a different authentication mode; these were removed for the isolated rerun.
  • packages/core: bun test test/npm.test.ts test/npm-config.test.ts — 9 passing tests, including import of the installed entrypoint on Bun.
  • Broader core coverage — all 1,097 Bun tests passed in two partitions: 1,076 non-lock tests, then all 21 lock tests separately, with CPU/thread limits. Combined runs on this shared host encountered EAGAIN process-spawn errors and an intermittent lock-contention failure; this is not a claim of a clean unpartitioned run. The 11 Node tests also passed afterward.
  • packages/core: bun typecheck — passed.
  • packages/opencode: bun run script/build-node.ts — passed.
  • Prettier and git diff --check — passed.

The Node tests require a modern Node runtime with registerHooks and TypeScript stripping; Desktop and unit CI use Node 24. This validates the real V1 provider path in a utility process, not the full Desktop renderer or a live external provider. The condition probe emits an experimental VM-loader warning.

Remaining compatibility limits

This is a focused backport, not a fully native ESM resolver. Differential probes found two existing resolve.exports limitations also present in V2: falling through an unmatched nested condition to a later root condition, and skipping an invalid first export-array target in favor of a later valid target. Those unusual export layouts remain unsupported here; they also failed before this change. Bun's resolver is unchanged.

Requested by: @rekram1-node (Aiden via Slack)

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant