openclaw - ✅(Solved) Fix Browser plugin's src/** not emitted to dist; getPwAiModule() returns null in production builds [2 pull requests, 3 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#74723Fetched 2026-04-30 06:20:44
View on GitHub
Comments
3
Participants
2
Timeline
7
Reactions
2
Timeline (top)
commented ×3cross-referenced ×2closed ×1referenced ×1

@openclaw/browser-plugin is missing openclaw.bundle.stageRuntimeDependencies: true in its package.json. As a result, the plugin's src/** tree is never compiled to dist/extensions/browser/src/**, the runtime's dynamic import('./pw-ai.js') fails with ERR_MODULE_NOT_FOUND, and every Playwright-backed browser tool (navigate, click, type, snapshot, pdf, ai-snapshot, etc.) returns 501 from requirePwAi() in extensions/browser/src/browser/routes/agent.shared.ts.

User-visible symptom: an agent with Chromium and playwright-core installed in the runtime image reports something like:

No browser available on the sandbox host - it's a headless Linux environment without a display.

even though the binary and the SDK are both present.

Error Message

Without stageRuntimeDependencies: true, none of those ./src/** files end up in the dist tree, and extensions/browser/src/browser/pw-ai-module.ts:loadPwAiModule() calling await import('./pw-ai.js') fails with ERR_MODULE_NOT_FOUND. The mode: \"soft\" path swallows it and returns null, and the requirePwAi() route gate then returns the misleading "install the full Playwright package (not playwright-core)" error message — even though installing playwright would not help, because pw-ai.js itself is never emitted from the plugin's own source.

Root Cause

tsdown.config.ts splits bundled plugins into two categories at build time:

  1. stagedBundledPluginBuildEntries — plugins where package.json:openclaw.bundle.stageRuntimeDependencies === true. These get a per-plugin tsdown config (buildBundledPluginConfigs, line 268) that emits the entire plugin into dist/extensions/<id>/ including the full src/** tree.
  2. rootBundledPluginBuildEntries — plugins without that flag. Only their top-level surface files (index.ts plus top-level *.ts) get bundled into the root build graph. Anything those files re-export from ./src/... is treated as external.

The browser plugin's register.runtime.ts does:

export { createBrowserTool } from \"./src/browser-tool.js\";
export { registerBrowserCli } from \"./src/cli/browser-cli.js\";
export { handleBrowserGatewayRequest } from \"./src/gateway/browser-request.js\";
export { runBrowserProxyCommand } from \"./src/node-host/invoke-browser.js\";
export { createBrowserPluginService } from \"./src/plugin-service.js\";
export { collectBrowserSecurityAuditFindings } from \"./src/security-audit.js\";

Without stageRuntimeDependencies: true, none of those ./src/** files end up in the dist tree, and extensions/browser/src/browser/pw-ai-module.ts:loadPwAiModule() calling await import('./pw-ai.js') fails with ERR_MODULE_NOT_FOUND. The mode: \"soft\" path swallows it and returns null, and the requirePwAi() route gate then returns the misleading "install the full Playwright package (not playwright-core)" error message — even though installing playwright would not help, because pw-ai.js itself is never emitted from the plugin's own source.

Fix Action

Fixed

PR fix notes

PR #74724: fix(telegram): abortWith accepts unknown to bridge undici/dom AbortSignal types

Description (problem / solution / changelog)

Summary

pnpm tsgo:extensions on plain main currently fails with two TS2739 errors:

extensions/telegram/src/bot.ts(193,21): error TS2739: Type 'AbortSignal' is missing the following properties from type 'AbortSignal': onabort, reason, throwIfAborted, dispatchEvent
extensions/telegram/src/bot.ts(195,44): error TS2739: Type 'AbortSignal' is missing the following properties from type 'AbortSignal': onabort, reason, throwIfAborted, dispatchEvent

TelegramFetchInit['signal'] is derived from grammY's ApiClientOptions['fetch'] parameter, which uses undici's AbortSignal. tsgo strict checks see this as structurally distinct from lib.dom's AbortSignal, even though both shapes carry .reason at runtime.

This blocks the local [check:changed] typecheck extensions pre-commit hook on any commit that touches an extension file.

Change

Type the local abortWith callback parameter as unknown and read .reason via a narrow cast. Runtime semantics unchanged — .reason is read from both undici and dom AbortSignal shapes at runtime.

Verification

$ pnpm tsgo:extensions
> [email protected] tsgo:extensions /home/user/openclaw
> node scripts/run-tsgo.mjs -p tsconfig.extensions.json --incremental ...
(clean exit)

$ pnpm test --filter=@openclaw/telegram
Test Files  98 passed (98)
     Tests  1381 passed (1381)

Related

Found while preparing #74723 (browser plugin packaging fix).

Changed files

  • .github/scripts/fly-smoke-test.sh (added, +296/-0)
  • .github/workflows/auto-response.yml (removed, +0/-534)
  • .github/workflows/ci.yml (removed, +0/-2443)
  • .github/workflows/codeql.yml (removed, +0/-138)
  • .github/workflows/deploy-blink-claw.yml (added, +179/-0)
  • .github/workflows/docker-release.yml (removed, +0/-397)
  • .github/workflows/install-smoke.yml (removed, +0/-217)
  • .github/workflows/labeler.yml (removed, +0/-877)
  • .github/workflows/openclaw-npm-release.yml (removed, +0/-420)
  • .github/workflows/sandbox-common-smoke.yml (removed, +0/-67)
  • .github/workflows/stale.yml (removed, +0/-217)
  • .github/workflows/workflow-sanity.yml (removed, +0/-100)
  • .gitignore (modified, +3/-0)
  • AGENTS.md (modified, +230/-199)
  • Dockerfile (modified, +78/-7)
  • blink-entrypoint.sh (added, +217/-0)
  • blink-git-credential.sh (added, +138/-0)
  • extensions/bluebubbles/package.json (modified, +2/-2)
  • extensions/copilot-proxy/package.json (modified, +2/-2)
  • extensions/diffs/package.json (modified, +2/-2)
  • extensions/feishu/package.json (modified, +2/-2)
  • extensions/googlechat/package.json (modified, +2/-2)
  • extensions/line/package.json (modified, +2/-2)
  • extensions/llm-task/package.json (modified, +2/-2)
  • extensions/matrix/package.json (modified, +2/-2)
  • extensions/mattermost/package.json (modified, +2/-2)
  • extensions/memory-core/package.json (modified, +2/-2)
  • extensions/msteams/package.json (modified, +2/-2)
  • extensions/nextcloud-talk/package.json (modified, +2/-2)
  • extensions/nostr/package.json (modified, +2/-2)
  • extensions/ollama/package.json (modified, +2/-2)
  • extensions/open-prose/package.json (modified, +2/-2)
  • extensions/sglang/package.json (modified, +2/-2)
  • extensions/slack/src/shared.ts (modified, +4/-0)
  • extensions/synology-chat/package.json (modified, +2/-2)
  • extensions/telegram/src/bot.ts (modified, +5/-1)
  • extensions/tlon/package.json (modified, +2/-2)
  • extensions/vllm/package.json (modified, +2/-2)
  • extensions/zalo/package.json (modified, +2/-2)
  • extensions/zalouser/package.json (modified, +2/-2)
  • scripts/precompile-extensions.cjs (added, +62/-0)
  • skills/blink-agent/SKILL.md (added, +65/-0)
  • skills/blink-airtable/SKILL.md (added, +76/-0)
  • skills/blink-app/SKILL.md (added, +99/-0)
  • skills/blink-asana/SKILL.md (added, +60/-0)
  • skills/blink-attio/SKILL.md (added, +64/-0)
  • skills/blink-calendly/SKILL.md (added, +55/-0)
  • skills/blink-call/SKILL.md (added, +110/-0)
  • skills/blink-connector/SKILL.md (added, +1185/-0)
  • skills/blink-discord/SKILL.md (added, +49/-0)
  • skills/blink-email/SKILL.md (added, +46/-0)
  • skills/blink-etsy/SKILL.md (added, +59/-0)
  • skills/blink-fetch/SKILL.md (added, +49/-0)
  • skills/blink-figma/SKILL.md (added, +93/-0)
  • skills/blink-github/SKILL.md (added, +125/-0)
  • skills/blink-google-calendar/SKILL.md (added, +95/-0)
  • skills/blink-google-docs/SKILL.md (added, +86/-0)
  • skills/blink-google-drive/SKILL.md (added, +65/-0)
  • skills/blink-google-gmail/SKILL.md (added, +75/-0)
  • skills/blink-google-sheets/SKILL.md (added, +94/-0)
  • skills/blink-google-slides/SKILL.md (added, +106/-0)
  • skills/blink-hubspot/SKILL.md (added, +102/-0)
  • skills/blink-image/SKILL.md (added, +78/-0)
  • skills/blink-instagram/SKILL.md (added, +56/-0)
  • skills/blink-jira/SKILL.md (added, +67/-0)
  • skills/blink-linear/SKILL.md (added, +52/-0)
  • skills/blink-linkedin/SKILL.md (added, +206/-0)
  • skills/blink-linkedin/scripts/lk.py (added, +154/-0)
  • skills/blink-mailchimp/SKILL.md (added, +60/-0)
  • skills/blink-microsoft-calendar/SKILL.md (added, +62/-0)
  • skills/blink-microsoft-onedrive/SKILL.md (added, +61/-0)
  • skills/blink-microsoft-outlook/SKILL.md (added, +64/-0)
  • skills/blink-microsoft-teams/SKILL.md (added, +59/-0)
  • skills/blink-microsoft/SKILL.md (added, +82/-0)
  • skills/blink-notion/SKILL.md (added, +75/-0)
  • skills/blink-phone/SKILL.md (added, +91/-0)
  • skills/blink-pipedrive/SKILL.md (added, +65/-0)
  • skills/blink-rag/SKILL.md (added, +58/-0)
  • skills/blink-realtime/SKILL.md (added, +48/-0)
  • skills/blink-salesforce/SKILL.md (added, +91/-0)
  • skills/blink-scrape/SKILL.md (added, +59/-0)
  • skills/blink-secrets/SKILL.md (added, +54/-0)
  • skills/blink-shopify/SKILL.md (added, +60/-0)
  • skills/blink-slack/SKILL.md (added, +74/-0)
  • skills/blink-sms/SKILL.md (added, +146/-0)
  • skills/blink-speech/SKILL.md (added, +52/-0)
  • skills/blink-stripe/SKILL.md (added, +70/-0)
  • skills/blink-tiktok/SKILL.md (added, +40/-0)
  • skills/blink-transcribe/SKILL.md (added, +58/-0)
  • skills/blink-twitter/SKILL.md (added, +73/-0)
  • skills/blink-typeform/SKILL.md (added, +50/-0)
  • skills/blink-vercel/SKILL.md (added, +60/-0)
  • skills/blink-video/SKILL.md (added, +93/-0)
  • skills/blink-youtube/SKILL.md (added, +58/-0)
  • skills/blink-zoom/SKILL.md (added, +66/-0)
  • skills/github/SKILL.md (modified, +31/-78)
  • src/agents/blink-models.ts (added, +65/-0)
  • src/agents/models-config.providers.implicit.ts (modified, +14/-0)
  • src/agents/models-config.providers.ts (modified, +1/-0)
  • src/agents/openclaw-tools.ts (modified, +2/-1)

PR #74725: fix(browser-plugin): emit src/** to dist via stageRuntimeDependencies

Description (problem / solution / changelog)

Fixes #74723.

Summary

The browser plugin's runtime entry (register.runtime.ts) re-exports from ./src/browser-tool.js, ./src/cli/browser-cli.js, ./src/gateway/..., ./src/plugin-service.js, etc. Without openclaw.bundle.stageRuntimeDependencies: true in package.json, tsdown.config.ts's two-tier plugin build splits this plugin into rootBundledPluginBuildEntries (line 239), which only emits the top-level surface files. The ./src/** subtree is never compiled.

At runtime, getPwAiModule() in extensions/browser/src/browser/pw-ai-module.ts does await import('./pw-ai.js'), which resolves to dist/extensions/browser/src/browser/pw-ai.js — a file that does not exist. import() throws ERR_MODULE_NOT_FOUND, the soft-load path returns null, and the requirePwAi() gate in routes/agent.shared.ts returns 501 with "Playwright is not available in this gateway build" for every Playwright-backed browser tool.

Change

Single 4-line edit to extensions/browser/package.json:

 "openclaw": {
   "extensions": [
     "./index.ts"
-  ]
+  ],
+  "bundle": {
+    "stageRuntimeDependencies": true
+  }
 }

This makes the browser plugin eligible for buildBundledPluginConfigs() (tsdown.config.ts line 268), which emits a per-plugin tsdown bundle into dist/extensions/browser/ with the full src/** tree compiled. Matches the pattern already used by 15 other bundled plugins (telegram, discord, feishu, google, codex, acpx, qqbot, diffs, etc.).

Verification

Before:

$ find dist/extensions/browser -type f -name '*.js' | wc -l
18
$ find dist -name 'pw-ai.js'
(no output)

After:

$ find dist/extensions/browser -type f -name '*.js' | wc -l
~hundreds
$ find dist -name 'pw-ai.js'
dist/extensions/browser/src/browser/pw-ai.js

pnpm test passes (807 tests / 94 files).

Notes

  • See #74723 for full root-cause analysis including how to reproduce on a fresh build.
  • This PR is stacked on #74724 (a pre-existing tsgo strict-check failure in extensions/telegram/src/bot.ts that blocked the local pre-commit hook on any extension change). Recommend merging #74724 first.

Changed files

  • extensions/browser/package.json (modified, +4/-1)

Code Example

export { createBrowserTool } from \"./src/browser-tool.js\";
export { registerBrowserCli } from \"./src/cli/browser-cli.js\";
export { handleBrowserGatewayRequest } from \"./src/gateway/browser-request.js\";
export { runBrowserProxyCommand } from \"./src/node-host/invoke-browser.js\";
export { createBrowserPluginService } from \"./src/plugin-service.js\";
export { collectBrowserSecurityAuditFindings } from \"./src/security-audit.js\";

---

$ find dist/extensions/browser -type f -name '*.js' | wc -l
18

$ find dist/extensions/telegram -type f -name '*.js' | wc -l
954

$ find dist -name 'pw-ai.js'
(no output)

---

"openclaw\": {
   \"extensions\": [
     \"./index.ts\"
-  ]
+  ],
+  \"bundle\": {
+    \"stageRuntimeDependencies\": true
+  }
 }
RAW_BUFFERClick to expand / collapse

Summary

@openclaw/browser-plugin is missing openclaw.bundle.stageRuntimeDependencies: true in its package.json. As a result, the plugin's src/** tree is never compiled to dist/extensions/browser/src/**, the runtime's dynamic import('./pw-ai.js') fails with ERR_MODULE_NOT_FOUND, and every Playwright-backed browser tool (navigate, click, type, snapshot, pdf, ai-snapshot, etc.) returns 501 from requirePwAi() in extensions/browser/src/browser/routes/agent.shared.ts.

User-visible symptom: an agent with Chromium and playwright-core installed in the runtime image reports something like:

No browser available on the sandbox host - it's a headless Linux environment without a display.

even though the binary and the SDK are both present.

Root cause

tsdown.config.ts splits bundled plugins into two categories at build time:

  1. stagedBundledPluginBuildEntries — plugins where package.json:openclaw.bundle.stageRuntimeDependencies === true. These get a per-plugin tsdown config (buildBundledPluginConfigs, line 268) that emits the entire plugin into dist/extensions/<id>/ including the full src/** tree.
  2. rootBundledPluginBuildEntries — plugins without that flag. Only their top-level surface files (index.ts plus top-level *.ts) get bundled into the root build graph. Anything those files re-export from ./src/... is treated as external.

The browser plugin's register.runtime.ts does:

export { createBrowserTool } from \"./src/browser-tool.js\";
export { registerBrowserCli } from \"./src/cli/browser-cli.js\";
export { handleBrowserGatewayRequest } from \"./src/gateway/browser-request.js\";
export { runBrowserProxyCommand } from \"./src/node-host/invoke-browser.js\";
export { createBrowserPluginService } from \"./src/plugin-service.js\";
export { collectBrowserSecurityAuditFindings } from \"./src/security-audit.js\";

Without stageRuntimeDependencies: true, none of those ./src/** files end up in the dist tree, and extensions/browser/src/browser/pw-ai-module.ts:loadPwAiModule() calling await import('./pw-ai.js') fails with ERR_MODULE_NOT_FOUND. The mode: \"soft\" path swallows it and returns null, and the requirePwAi() route gate then returns the misleading "install the full Playwright package (not playwright-core)" error message — even though installing playwright would not help, because pw-ai.js itself is never emitted from the plugin's own source.

Reproduction

On any production-style build (pnpm build:docker or pnpm build):

$ find dist/extensions/browser -type f -name '*.js' | wc -l
18

$ find dist/extensions/telegram -type f -name '*.js' | wc -l
954

$ find dist -name 'pw-ai.js'
(no output)

Compare to any plugin with stageRuntimeDependencies: true (telegram, discord, feishu, google, codex, acpx, qqbot, diffs, etc. — 15 plugins total) — those have full src/** trees in their dist directories.

The Docker image used by Blink Claw has OPENCLAW_INSTALL_BROWSER=1 baked in (Chromium 1217 + xvfb-run + [email protected]) but every gateway in the fleet 501s on browser tool calls because of this packaging gap.

Proposed fix

PR follow-up: <link to PR will be added>

 "openclaw\": {
   \"extensions\": [
     \"./index.ts\"
-  ]
+  ],
+  \"bundle\": {
+    \"stageRuntimeDependencies\": true
+  }
 }

Four lines. Matches the pattern already used by 15 other bundled plugins.

Why this slipped through

The plugin is \"private\": true in package.json so it is not directly published to npm. The published openclaw package's dist/extensions/browser/ is built via the same root-level tsdown graph that does not include the src/** tree, so the bug is present in every publication. Anyone who only uses the Chrome MCP / CDP-attach path (openclaw browser against a running Chrome on --remote-debugging-port=9222) does not exercise the missing module — pw-ai.js is only loaded for the headless Playwright tool surface. That explains why the bug has not been more visible.

Notes

While preparing this PR I also hit a separate pre-existing tsgo strict-check failure in extensions/telegram/src/bot.ts (lines 193, 195 — undici vs lib.dom AbortSignal structural mismatch) that blocks the local pre-commit changed-file typecheck hook on any commit that touches these extensions. Fixed in a separate PR for clean review: see <link>.

extent analysis

TL;DR

The most likely fix is to add "openclaw.bundle.stageRuntimeDependencies": true to the package.json of the @openclaw/browser-plugin to ensure its src/** tree is compiled and included in the dist/extensions/browser directory.

Guidance

  • Verify that the package.json of the @openclaw/browser-plugin does not have the openclaw.bundle.stageRuntimeDependencies flag set to true.
  • Add the missing flag to the package.json as shown in the proposed fix.
  • Rebuild the project using pnpm build:docker or pnpm build to ensure the changes take effect.
  • Check the dist/extensions/browser directory to confirm that the src/** tree is now included.

Example

 "openclaw": {
   "extensions": [
     "./index.ts"
-  ]
+  ],
+  "bundle": {
+    "stageRuntimeDependencies": true
+  }
 }

Notes

This fix should resolve the ERR_MODULE_NOT_FOUND error and allow the Playwright-backed browser tools to function correctly. However, it's worth noting that there is a separate issue with a tsgo strict-check failure in extensions/telegram/src/bot.ts that may need to be addressed separately.

Recommendation

Apply the proposed fix by adding the openclaw.bundle.stageRuntimeDependencies flag to the package.json of the @openclaw/browser-plugin. This should resolve the issue with the missing pw-ai.js module and allow the browser tools to function correctly.

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

openclaw - ✅(Solved) Fix Browser plugin's src/** not emitted to dist; getPwAiModule() returns null in production builds [2 pull requests, 3 comments, 2 participants]