Skip to content

fix: harden runtime capture mocks#8

Merged
vincentkoc merged 2 commits intomainfrom
fix/runtime-capture-real-plugins
Apr 27, 2026
Merged

fix: harden runtime capture mocks#8
vincentkoc merged 2 commits intomainfrom
fix/runtime-capture-real-plugins

Conversation

@steipete
Copy link
Copy Markdown
Contributor

Summary

  • import plugin entrypoints in place with a temporary ESM loader instead of symlinking the plugin root
  • generate SDK subpath and missing external dependency mocks from the plugin source import graph
  • support TypeScript entrypoints, extensionless local imports, cloneable z/Type/schema mocks, constructor-safe mocks, API runtime state helpers, and noisy plugin output capture

Retest

  • npm run check
  • OpenClaw bundled runtime sample: 8 packages, 12 entrypoints, 0 capture failures (openai, telegram, matrix, discord, voice-call, memory-core, browser, slack)
  • ClawHub runtime sample: 7 packages, 10 entrypoints, 0 capture failures (@openclaw/matrix, @openclaw/msteams, @openclaw/nostr, openclaw-codex-app-server, openclaw-agent-protocol, @openclaw/openclaw-canon, @shaoxing-xie/openclaw-data-china-stock)

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 27, 2026

PR Summary

Medium Risk
Touches the runtime capture execution path (custom ESM loader, module resolution, and process output interception), so regressions could cause false capture failures or missing diagnostics across plugin shapes.

Overview
Runtime capture is hardened to handle real-world plugin entrypoints more reliably. The mock-SDK runner now imports entrypoints in place using a temporary Node ESM loader (instead of symlinking a plugin root), suppresses Node warnings, and captures any plugin stdout/stderr (including late async logs) into the capture result.

Mock generation is expanded and made adaptive. createMockSdkPackage now scans the plugin source tree to discover openclaw/plugin-sdk/* subpath imports and bare external dependencies, emits dynamic mock modules for those specifiers (including TypeScript-friendly loading, extensionless local import resolution, and special handling for zod/schema-like exports), and adds a lightweight runtime.state.resolveStateDir/agent/tts shape to the capture API context.

Tests are updated/added to cover TypeScript entrypoints, SDK subpaths, missing externals, namespace imports, dist-rooted imports, and noisy output behavior, plus minor fixture evidence renames.

Reviewed by Cursor Bugbot for commit 824d19b. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7235aaa1ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/sdk-mock.js
continue;
}
const relative = specifier.slice("openclaw/plugin-sdk/".length);
const targetPath = path.join(pluginSdkDir, `${relative}.js`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate SDK subpaths before creating mock files

The SDK subpath is used verbatim to build targetPath, so a crafted import like openclaw/plugin-sdk/../../../../tmp/pwn escapes pluginSdkDir and writes files outside the temporary workspace during createMockSdkPackage. Because plugin sources are untrusted inputs for inspection, this is an arbitrary file-write vulnerability; reject absolute/traversing segments (or normalize and enforce prefix) before calling mkdir/writeFile.

Useful? React with 👍 / 👎.

Comment thread src/sdk-mock.js
? path.join(parsed.dir, parsed.name)
: null;
const candidates = [
base,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve extensionless imports to files, not directories

resolveExtensionless checks base first with existsSync, so import "./util" where util/ is a directory returns the directory path and the loader fails with EISDIR before trying index.js/index.ts. This breaks runtime capture for common folder-based module layouts and produces false status: "error" results.

Useful? React with 👍 / 👎.

Comment thread src/sdk-mock.js
@vincentkoc vincentkoc force-pushed the fix/runtime-capture-real-plugins branch from 9710904 to 824d19b Compare April 27, 2026 10:09
@vincentkoc vincentkoc merged commit a1a5bca into main Apr 27, 2026
2 checks passed
@vincentkoc vincentkoc deleted the fix/runtime-capture-real-plugins branch April 27, 2026 10:12
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 824d19b. Configure here.

try {
await register(api);
} catch (error) {
await drainAsyncOutput();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Captured plugin output dropped on failure paths

Medium Severity

installProcessOutputCapture is enabled before importing or registering the plugin, but withProcessOutput is only invoked on the success branches. When entrypoint-import-error or registration-execution-error is thrown, the buffered stdout/stderr from the plugin is silently discarded — exactly when noisy diagnostic output is most valuable. The top-level catch surfaces only the error stack via the original write functions, so consumers never see the captured plugin noise that preceded the failure.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 824d19b. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants