openclaw - ✅(Solved) Fix diffs plugin broken in v2026.3.31: missing dist/extensions/assets/viewer-runtime.js [2 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#58598Fetched 2026-04-08 02:00:25
View on GitHub
Comments
2
Participants
2
Timeline
8
Reactions
2
Timeline (top)
cross-referenced ×4commented ×2closed ×1locked ×1

The diffs tool fails silently on every call with missing tool result in the agent session. Tracing the gateway logs reveals an unhandled promise rejection:

Error: ENOENT: no such file or directory, stat '/home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/viewer-runtime.js'
    at Object.stat (node:internal/fs/promises:1037:18)
    at loadViewerAssets (.../extensions/diffs/index.js:253:22)
    at getServedViewerAsset (.../extensions/diffs/index.js:240:17)
    at .../extensions/diffs/index.js:775:20
    at async Promise.all (index 1)
    at RouteHandler._handleInternal (.../playwright-core/lib/client/network.js:693:23)

Error Message

Error: ENOENT: no such file or directory, stat '/home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/viewer-runtime.js' at Object.stat (node:internal/fs/promises:1037:18) at loadViewerAssets (.../extensions/diffs/index.js:253:22) at getServedViewerAsset (.../extensions/diffs/index.js:240:17) at .../extensions/diffs/index.js:775:20 at async Promise.all (index 1) at RouteHandler._handleInternal (.../playwright-core/lib/client/network.js:693:23)

Root Cause

The directory dist/extensions/assets/ is completely absent from the npm package:

$ ls /home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/
ls: cannot access '.../dist/extensions/assets/': No such file or directory

The diffs plugin (dist/extensions/diffs/index.js) expects a shared viewer-runtime.js asset at dist/extensions/assets/viewer-runtime.js but this file was not bundled into the v2026.3.31 release.

Fix Action

Workaround

Use diff -u via shell exec and post output in a code block until fixed.

PR fix notes

PR #58652: fix(diffs): ship viewer runtime asset

Description (problem / solution / changelog)

Summary

Ship the missing dist/extensions/assets/viewer-runtime.js file that the bundled diffs plugin already resolves at runtime, and add a regression test that checks the shipped asset boundary.

Repro Steps

  1. Install the published package with the bundled diffs plugin enabled.
  2. Invoke the diffs tool so the viewer route tries to load /plugins/diffs/assets/viewer-runtime.js.
  3. Observe the missing-file failure because dist/extensions/assets/viewer-runtime.js was not present in the packed artifact.

Root Cause

The bundled plugin loader in dist/extensions/diffs/index.js already resolves ../assets/viewer-runtime.js, but the runtime asset was absent from dist/extensions/assets/, so packaged installs failed at asset load time.

Behavior Changes

  • The packaged diffs viewer now ships the runtime asset at the exact path the bundled plugin already loads.
  • The runtime asset sets the ready marker and includes a declarative shadow DOM fallback so the prerendered diff HTML still upgrades if the browser does not hydrate the shadow root automatically.
  • Added a regression test that asserts the bundled plugin still points at the shipped runtime asset and that the runtime asset file is present.

Codebase and GitHub Search

  • Searched the bundled diffs plugin for viewer-runtime.js, getServedViewerAsset, and loadViewerAssets to confirm the failing runtime path.
  • Checked npm pack --json --dry-run --ignore-scripts output to verify the packaged file list before and after the fix.
  • Confirmed the reported behavior against Issue #58598.

Tests

  • node --input-type=module -e '... to verify the bundled plugin still resolves ../assets/viewer-runtime.js and that the shipped asset contains the ready marker plus the shadow DOM fallback.
  • npm pack --json --dry-run --ignore-scripts to verify the tarball now contains dist/extensions/assets/viewer-runtime.js.
  • pnpm exec vitest run src/plugins/diffs-bundled-assets.test.ts --config vitest.unit.config.ts currently fails before executing tests because of an unrelated repo import error: getOAuthProviders is not a function from src/agents/auth-profiles/oauth.ts.

Manual Testing (omit if N/A)

N/A

Evidence (omit if N/A)

  • npm pack --json --dry-run --ignore-scripts now includes dist/extensions/assets/viewer-runtime.js. lobster-biscuit

Sign-Off

  • Models used: GPT-5.4
  • Submitter effort: Investigated the packaged artifact, reproduced the missing shipped asset boundary, and kept the fix scoped to the runtime file the bundle already expects.
  • Agent notes: The tracked source for this bundled extension is not present in this checkout, so the fix is intentionally scoped to the shipped runtime artifact plus a regression check around that packaged boundary.

Made with Cursor

Changed files

  • dist/extensions/assets/viewer-runtime.js (added, +23/-0)
  • src/plugins/diffs-bundled-assets.test.ts (added, +26/-0)

PR #59341: diffs: add configurable viewer base URL

Description (problem / solution / changelog)

Summary

  • Problem: diffs viewer URLs defaulted to raw loopback, which breaks in same-host proxy deployments that intentionally treat 127.0.0.1 as a trusted proxy and fail closed without forwarded client-IP headers.
  • Why it matters: the earlier localhost fast-path idea fixed one topology by weakening the trusted-proxy invariant, which is the wrong tradeoff.
  • What changed:
    • added plugin-owned viewerBaseUrl config for persistent viewer-link origin/path control
    • kept per-call baseUrl as the highest-precedence override
    • preserved the existing fail-closed viewer access policy
    • added config/tool coverage for normalization and precedence
    • documented the supported Oracle/Tailscale proxy path and added a changelog entry
  • What did NOT change:
    • no access-policy widening
    • no remoteAddress heuristic bypass
    • no storage or token model changes

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Fixes #59227
  • Related #58598
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: viewer URL generation assumed raw loopback was the right client origin, while the diffs HTTP gate correctly treated loopback-in-trustedProxies as ambiguous proxy traffic that must fail closed without forwarded headers.
  • Missing detection / guardrail: there was no explicit viewer-origin seam for this plugin, so callers had to pass baseUrl on every tool call or accept the raw loopback fallback.
  • Prior context (git blame, prior PR, issue, or refactor if known): fix(diffs): harden viewer proxy access (30a1690323) intentionally preserved the fail-closed guard.
  • Why this regressed now: the issue surfaced once raw loopback viewer URLs met a same-host trusted-proxy topology.
  • If unknown, what was ruled out: ruled out artifact-registry and disk-persistence issues; requests were being denied before artifact lookup.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • extensions/diffs/src/config.test.ts
    • extensions/diffs/src/tool.test.ts
    • extensions/diffs/src/store.test.ts
  • Scenario the tests now lock in:
    • plugin viewerBaseUrl is normalized and used when no per-call baseUrl is present
    • per-call baseUrl still overrides plugin config
    • trusted-proxy loopback requests without valid provenance headers remain denied by default
  • Why this is the smallest reliable guardrail: it fixes the real seam (viewer URL generation) while separately preserving the existing access gate behavior.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Diffs can now be configured with plugins.entries.diffs.config.viewerBaseUrl, so viewer links can point at a stable proxy/public origin without passing baseUrl on every tool call. Access policy is unchanged: loopback trusted-proxy requests still fail closed unless remote viewers are explicitly enabled.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS host
  • Runtime/container: local repo runtime
  • Model/provider: N/A
  • Integration/channel (if any): diffs plugin HTTP viewer route
  • Relevant config (redacted): gateway.trustedProxies=["127.0.0.1"]

Steps

  1. Configure diffs with a persistent viewer origin or pass baseUrl per call.
  2. Generate diff viewer URLs in view / both mode.
  3. Re-check loopback trusted-proxy denial behavior.

Expected

  • Viewer URLs can target a stable proxy/public origin without weakening the viewer access gate.
  • Trusted-proxy loopback traffic without client-origin headers stays denied by default.

Actual

  • This PR adds the explicit viewer-origin seam and preserves the existing fail-closed HTTP policy.

Evidence

Attach at least one:

  • Passing targeted test coverage
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios:
    • pnpm test -- extensions/diffs/src/config.test.ts extensions/diffs/src/tool.test.ts extensions/diffs/src/store.test.ts
    • pnpm build
    • pnpm check
    • pnpm format
  • Edge cases checked:
    • normalized plugin viewerBaseUrl
    • per-call baseUrl precedence over plugin config
    • trusted-proxy loopback denial path with and without forwarded headers when remote viewers stay disabled
  • What you did not verify: full pnpm test suite (reviewer explicitly requested targeted-only verification for this prep pass).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) Optional new plugin config
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: deployments may still point viewers at unusable origins if neither viewerBaseUrl nor per-call baseUrl is set.
    • Mitigation: keep the existing raw-loopback fallback, document the proxy topology caveat, and let explicit config override it safely.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/install/oracle.md (modified, +2/-0)
  • docs/platforms/oracle.md (modified, +2/-0)
  • docs/tools/diffs.md (modified, +32/-3)
  • extensions/diffs/README.md (modified, +22/-1)
  • extensions/diffs/index.ts (modified, +8/-3)
  • extensions/diffs/openclaw.plugin.json (modified, +7/-0)
  • extensions/diffs/skills/diffs/SKILL.md (modified, +1/-0)
  • extensions/diffs/src/config.test.ts (modified, +51/-0)
  • extensions/diffs/src/config.ts (modified, +31/-0)
  • extensions/diffs/src/store.test.ts (modified, +8/-0)
  • extensions/diffs/src/tool.test.ts (modified, +53/-1)
  • extensions/diffs/src/tool.ts (modified, +3/-2)
  • extensions/diffs/src/url.ts (modified, +8/-4)

Code Example

Error: ENOENT: no such file or directory, stat '/home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/viewer-runtime.js'
    at Object.stat (node:internal/fs/promises:1037:18)
    at loadViewerAssets (.../extensions/diffs/index.js:253:22)
    at getServedViewerAsset (.../extensions/diffs/index.js:240:17)
    at .../extensions/diffs/index.js:775:20
    at async Promise.all (index 1)
    at RouteHandler._handleInternal (.../playwright-core/lib/client/network.js:693:23)

---

$ ls /home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/
ls: cannot access '.../dist/extensions/assets/': No such file or directory
RAW_BUFFERClick to expand / collapse

Bug Report

Version: v2026.3.31 Platform: Linux arm64 (Ubuntu), npm global install

Summary

The diffs tool fails silently on every call with missing tool result in the agent session. Tracing the gateway logs reveals an unhandled promise rejection:

Error: ENOENT: no such file or directory, stat '/home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/viewer-runtime.js'
    at Object.stat (node:internal/fs/promises:1037:18)
    at loadViewerAssets (.../extensions/diffs/index.js:253:22)
    at getServedViewerAsset (.../extensions/diffs/index.js:240:17)
    at .../extensions/diffs/index.js:775:20
    at async Promise.all (index 1)
    at RouteHandler._handleInternal (.../playwright-core/lib/client/network.js:693:23)

Root Cause

The directory dist/extensions/assets/ is completely absent from the npm package:

$ ls /home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/
ls: cannot access '.../dist/extensions/assets/': No such file or directory

The diffs plugin (dist/extensions/diffs/index.js) expects a shared viewer-runtime.js asset at dist/extensions/assets/viewer-runtime.js but this file was not bundled into the v2026.3.31 release.

Steps to Reproduce

  1. Install openclaw v2026.3.31 via npm
  2. Enable the diffs plugin (plugins.entries.diffs.enabled: true)
  3. Call the diffs tool from any agent session with any before/after content
  4. Observe: tool returns no result; gateway log shows ENOENT for viewer-runtime.js

Expected Behavior

The diffs tool should return a viewer URL and/or rendered file as documented.

Workaround

Use diff -u via shell exec and post output in a code block until fixed.

Additional Context

Plugin shows as loaded in openclaw plugins list — the load-time check does not catch the missing asset, only the runtime call fails.

extent analysis

TL;DR

The missing viewer-runtime.js asset in the openclaw npm package causes the diffs tool to fail silently, and a workaround is to use the diff -u command via shell exec until the issue is fixed.

Guidance

  • Verify the absence of the dist/extensions/assets/ directory in the openclaw npm package by running the command ls /home/ubuntu/.npm-global/lib/node_modules/openclaw/dist/extensions/assets/.
  • Check the openclaw version to ensure it is indeed v2026.3.31, which is reported to have the issue.
  • Use the provided workaround by running diff -u via shell exec and posting the output in a code block as a temporary solution.
  • Monitor the openclaw package for updates that include the missing viewer-runtime.js asset.

Example

No code snippet is provided as the issue is related to a missing asset in the openclaw package, and the workaround is to use a shell command.

Notes

The issue is specific to the openclaw package version v2026.3.31, and the workaround may not be necessary for other versions. The load-time check for the plugin does not catch the missing asset, so the issue only manifests at runtime.

Recommendation

Apply the workaround by using diff -u via shell exec until the openclaw package is updated to include the missing viewer-runtime.js asset, as this provides a temporary solution to the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING