Skip to content

fix(plugins): add postinstall patch for ESM-only package exports#16019

Closed
dashed wants to merge 2 commits intoopenclaw:mainfrom
dashed:fix/esm-exports-patch
Closed

fix(plugins): add postinstall patch for ESM-only package exports#16019
dashed wants to merge 2 commits intoopenclaw:mainfrom
dashed:fix/esm-exports-patch

Conversation

@dashed
Copy link
Copy Markdown
Contributor

@dashed dashed commented Feb 14, 2026

Summary

Fixes plugin loading failures caused by ESM-only dependencies that lack CJS-compatible export conditions.

  • Root cause: jiti (the TS/ESM loader used for plugins) converts import to CJS require() internally. Three dependencies (@buape/carbon, osc-progress, @mariozechner/pi-coding-agent) ship export maps with only an "import" condition — no "default" or "require" fallback — causing ERR_PACKAGE_PATH_NOT_EXPORTED at runtime. This silently breaks all plugin loading for any plugin importing from openclaw/plugin-sdk.
  • Fix: A postinstall script (scripts/patch-esm-exports.cjs) that walks node_modules and adds the missing "default" export condition to any package whose exports have "import" but neither "default" nor "require". The patch is idempotent, has zero runtime cost, and becomes a no-op if upstream packages add CJS support.
  • Scope: Affects all deployment modes (npm, Docker, source) — not just custom Docker builds. Most users never notice because plugin failures are non-fatal and the web channel (compiled into the main bundle) works regardless.

Related issues

Test plan

  • 22 unit tests (src/scripts/patch-esm-exports.test.ts) — patchExports logic, patchDir directory walking, edge cases (malformed JSON, string shorthand, idempotency, depth limits, skip dirs), affected package simulations
  • 10 e2e tests (src/scripts/patch-esm-exports.e2e.test.ts):
    • Reproduces ERR_PACKAGE_PATH_NOT_EXPORTED with ESM-only fixtures (proves bug exists)
    • Confirms patchDir resolves the failure (proves fix works)
    • Validates all 3 affected packages resolve via CJS require.resolve in real node_modules
    • Verifies real jiti can resolve @buape/carbon through patched exports
  • pnpm check clean (format + typecheck + lint)

Greptile Overview

Greptile Summary

Adds a postinstall script (scripts/patch-esm-exports.cjs) that patches ESM-only dependencies (@buape/carbon, osc-progress, @mariozechner/pi-coding-agent) by adding a "default" export condition to their package.json exports maps. This resolves ERR_PACKAGE_PATH_NOT_EXPORTED errors when jiti (the runtime TS/ESM loader) converts import to CJS require() internally.

  • The patch script is idempotent, never exits non-zero, and becomes a no-op once upstream packages add CJS support
  • Includes 22 unit tests and 10 e2e tests that reproduce the actual error and verify the fix
  • The patchExports function handles one-level-deep condition maps (subpath → condition → value), which is sufficient for all three affected packages
  • E2e tests verify real require.resolve and jiti resolution after patching

Confidence Score: 5/5

  • This PR is safe to merge — it fixes a real runtime error with an idempotent, zero-runtime-cost postinstall patch backed by thorough tests.
  • The patch script is well-structured with proper error handling, never exits non-zero, and is idempotent. The fix is minimal and targeted — it only adds a missing "default" condition to packages that have "import" but no CJS fallback. 32 tests (22 unit + 10 e2e) comprehensively verify both the patching logic and the real-world resolution of affected packages. The code handles edge cases like malformed JSON, symlinks, depth limits, and skipped directories. No functional issues were found.
  • No files require special attention.

Last reviewed commit: f6e58e3

(2/5) Greptile learns from your feedback when you react with thumbs up/down!


Rebase History

Date Base Upstream Commits Notes
2026-03-23 d5917d37c54a (post-v2026.3.23) 929 Rebased cleanly, zero conflicts. Fixed CI: added COPY scripts ./scripts to Dockerfile after COPY patches so postinstall script is available during pnpm install in Docker builds (install-smoke was failing).
2026-03-13 330631a0eb39 (v2026.3.12) Clean rebase. Dockerfile now needs COPY scripts ./scripts before pnpm install for postinstall to work. Commits: 7050c1403822, 798a6fba6cc5.
2026-03-08 eb0758e1722c (v2026.3.7) Clean rebase, no conflicts. Note: upstream moved all channel plugins to scoped plugin SDK imports — this patch may be droppable if the underlying ESM/CJS issue was resolved by the plugin SDK modularization. Needs testing. Commits: 366e02b9e09c, 12c0350a8898.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Mar 19, 2026
@dashed dashed force-pushed the fix/esm-exports-patch branch 2 times, most recently from da2f6d0 to ad96cd0 Compare March 21, 2026 05:49
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Mar 22, 2026
@dashed dashed force-pushed the fix/esm-exports-patch branch 2 times, most recently from 7a2661d to eaa81e3 Compare March 24, 2026 03:02
@openclaw-barnacle openclaw-barnacle Bot added the docker Docker and sandbox tooling label Mar 24, 2026
jiti (the TS/ESM loader used for plugin loading) converts imports to
CJS require() internally. Three dependencies (@buape/carbon,
osc-progress, @mariozechner/pi-coding-agent) ship export maps with
only an "import" condition and no "default" or "require" fallback,
causing ERR_PACKAGE_PATH_NOT_EXPORTED at runtime. This silently breaks
all plugin loading for any plugin importing from openclaw/plugin-sdk.

Add a postinstall script that walks node_modules and adds the missing
"default" export condition to any package whose exports have "import"
but neither "default" nor "require". The patch is idempotent, has zero
runtime cost, and becomes a no-op if upstream packages add CJS support.
Verify the postinstall patch end-to-end:
- Reproduce ERR_PACKAGE_PATH_NOT_EXPORTED with ESM-only fixtures
- Confirm patchDir resolves the failure
- Validate all three affected packages resolve via CJS in real
  node_modules after postinstall
- Verify jiti can resolve @buape/carbon through the patched exports
@dashed dashed force-pushed the fix/esm-exports-patch branch from eaa81e3 to d4629a2 Compare March 28, 2026 00:39
@dashed
Copy link
Copy Markdown
Contributor Author

dashed commented Mar 28, 2026

Rebased to latest main (post-v2026.3.26, 2a98464a). Clean rebase — no conflicts. Note: upstream now has COPY scripts ./scripts in Dockerfile (line 64), which is needed for the postinstall to run during build. The ESM patches themselves are still required.

@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 2, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

@openclaw-barnacle openclaw-barnacle Bot closed this Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docker Docker and sandbox tooling scripts Repository scripts size: L stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant