Skip to content

Add nativeSpawn option to RunCLIArgs for host process spawning#3481

Open
chubes4 wants to merge 5 commits intoWordPress:trunkfrom
chubes4:fix/native-spawn-handler
Open

Add nativeSpawn option to RunCLIArgs for host process spawning#3481
chubes4 wants to merge 5 commits intoWordPress:trunkfrom
chubes4:fix/native-spawn-handler

Conversation

@chubes4
Copy link
Copy Markdown

@chubes4 chubes4 commented Apr 11, 2026

Summary

Add a nativeSpawn boolean to RunCLIArgs that enables native host process spawning via child_process.spawn for PHP's proc_open(), shell_exec(), and exec(). When enabled, the worker thread imports spawn directly instead of using the sandboxed handler that creates new PHP WASM instances.

Motivation

WordPress Studio uses runCLI() for its site daemon. PHP plugins that call proc_open() to communicate with external tools (e.g., php-mcp-client's StdioTransport for MCP servers) fail silently because the worker thread's sandboxed spawn handler creates new WASM instances instead of spawning host processes.

The sandboxed handler is correct for browser environments where host process spawning isn't possible. But in Node.js environments (CLI, Studio), native spawning should be available as an opt-in.

How AI was used in this PR

This PR was developed with Claude Code (AI pair programming). The root cause analysis, fix design, and test suite were developed collaboratively. All code has been reviewed and tested.

Changes

run-cli.ts

  • Add nativeSpawn?: boolean to RunCLIArgs interface

blueprints-v1-handler.ts

  • Pass nativeSpawn from RunCLIArgs to the worker's bootRequestHandler options

worker-thread-v1.ts

  • Add nativeSpawn?: boolean to WorkerBootRequestHandlerOptions
  • When nativeSpawn is true, the spawn handler imports child_process.spawn directly inside the worker thread instead of using sandboxedSpawnHandlerFactory
  • The flag is a boolean (not a function) because spawn handlers can't be serialized across the Comlink worker thread boundary

php-proc-open.spec.ts (new test file)

  • Tests for proc_open with native spawn handler
  • Tests for shell_exec with native spawn handler
  • Tests for proc_open failure without spawn handler

Design decisions

Boolean flag vs function reference: nativeSpawn is a boolean rather than accepting a custom SpawnHandler function because the worker thread runs in a separate Worker() context. Functions can't be serialized across the Comlink message boundary. The worker imports child_process.spawn directly when the flag is true.

Opt-in vs default: Native spawning is opt-in to preserve backward compatibility. The sandboxed handler remains the default, which is correct for browser environments and security-sensitive contexts.

Testing

Verified with comprehensive standalone tests against @php-wasm/node:

Test Result
proc_open with stdout capture
shell_exec output
stderr capture
Non-zero exit codes (exit 42)
stdin piping (fwrite → cat → read)
proc_open fails gracefully without handler

Also verified in WordPress Studio CLI that the fallback path (runWpCliCommand with setSpawnHandler(spawn)) enables proc_open for WP-CLI commands.

Context

chubes4 and others added 2 commits April 11, 2026 18:12
Add a `nativeSpawn` boolean to `RunCLIArgs` that enables native host
process spawning via `child_process.spawn` for PHP's `proc_open()`,
`shell_exec()`, and `exec()`. When enabled, the worker thread imports
spawn directly instead of using the sandboxed handler that creates new
PHP WASM instances.

This is needed by consumers like WordPress Studio that run PHP plugins
requiring host process communication (e.g., php-mcp-client's
StdioTransport for MCP servers).

The `nativeSpawn` flag is a boolean rather than a function reference
because spawn handlers can't be serialized across the Comlink worker
thread boundary. The worker imports `child_process.spawn` directly
when the flag is true.

Changes:
- run-cli.ts: Add `nativeSpawn` to `RunCLIArgs` interface
- blueprints-v1-handler.ts: Pass `nativeSpawn` to worker
- worker-thread-v1.ts: Use native spawn when `nativeSpawn` is true
- php-proc-open.spec.ts: Tests for proc_open, shell_exec, stderr,
  stdin piping, exit codes, and graceful failure without handler

Fixes WordPress#3480

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chubes4 chubes4 requested review from a team, brandonpayton and Copilot April 11, 2026 22:20
chubes4 added a commit to Automattic/studio that referenced this pull request Apr 11, 2026
Pass nativeSpawn: true to RunCLIArgs in wordpress-server-child.ts so
PHP's proc_open(), shell_exec(), and exec() can spawn host processes
when the site is running via the daemon.

This complements the createNativeSpawnHandler() in run-wp-cli-command.ts
(which handles the fallback path when no daemon is running).

Depends on WordPress/wordpress-playground#3481 adding nativeSpawn to
RunCLIArgs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an opt-in nativeSpawn flag to the Playground CLI worker boot flow so PHP code running under runCLI() can spawn real host processes (Node.js child_process.spawn) instead of using the sandboxed spawn handler that starts new PHP WASM instances—primarily to unblock proc_open()/shell_exec() use cases in Node environments.

Changes:

  • Introduce nativeSpawn?: boolean in RunCLIArgs and propagate it into the Blueprints V1 worker boot options.
  • Update the worker spawn handler selection to use native child_process.spawn when nativeSpawn is enabled.
  • Add Node runtime tests verifying proc_open/shell_exec behavior with and without a spawn handler.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test-spawn-handler.mjs Adds a standalone manual test script to validate spawn handler propagation into proc_open/shell_exec.
packages/playground/cli/src/run-cli.ts Extends RunCLIArgs with nativeSpawn and documents the behavior.
packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts Passes nativeSpawn through to the worker boot options.
packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts Switches spawn handler factory based on nativeSpawn, using host spawning when enabled.
packages/php-wasm/node/src/test/php-proc-open.spec.ts Adds automated tests for proc_open/shell_exec with a native spawn handler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

chubes4 and others added 3 commits April 11, 2026 18:27
- Add security warning to nativeSpawn docs about arbitrary command execution
- Fix test name to match behavior (fails, not throws)
- Clean up require usage in worker spawn handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

RunCLIArgs: expose spawnHandler option for proc_open support

2 participants