|
74 | 74 | - Pi's cli.js calls main() without await — import main.js directly and call `await main(argv)` for headless mode |
75 | 75 | - Polyfill ESM wrappers need BUILTIN_NAMED_EXPORTS entries to support `import { X } from 'module'` — without them, only default import works |
76 | 76 | - CJS modules using TypeScript __exportStar pattern need host require() to discover named exports — static extractCjsNamedExports doesn't follow sub-module requires |
| 77 | +- _resolveModule handler must use "import" mode (not "require") — it's called by V8's ESM module system |
| 78 | +- bundlePolyfill() returns an IIFE — _loadFile must NOT re-wrap (double-wrapping discards inner result) |
| 79 | +- CJS-to-ESM wrapper must use `let exports` not `const` — CJS modules (ajv) reassign exports |
| 80 | +- Non-TTY stdin must emit "end" on resume when empty — without it, apps hang at readPipedStdin() |
| 81 | +- SSRF check in createDefaultNetworkAdapter blocks 127.0.0.1 — test network adapters need directFetch bypass for mock servers |
| 82 | +- crypto polyfill (node-stdlib-browser) lacks randomUUID — augment with bridge _cryptoRandomUUID |
| 83 | +- process.stdout.write(data, callback) callback must be invoked — Pi's flush Promise depends on it |
77 | 84 |
|
78 | 85 | # Ralph Progress Log |
79 | 86 | Started: Sat Mar 21 02:49:43 AM PDT 2026 |
@@ -882,3 +889,52 @@ Started: Sat Mar 21 02:49:43 AM PDT 2026 |
882 | 889 | - findEsmEntryFromCjsPath (preferring ESM entries over CJS) causes cascading issues because polyfill builtins (path, url, etc.) lack named exports in their ESM wrappers |
883 | 890 | - The V8 module system uses _resolveModule (async handler in buildModuleLoadingBridgeHandlers), NOT _resolveModuleSync |
884 | 891 | --- |
| 892 | + |
| 893 | +## 2026-03-21 22:00 - US-027 (bridge improvements, not yet passing) |
| 894 | +- Implemented 13 sandbox bridge compatibility fixes for in-VM Pi execution |
| 895 | +- Fixes committed: |
| 896 | + 1. _resolveModule: use "import" ESM export conditions for V8 module system (was always using "require") |
| 897 | + 2. extractCjsNamedExports: add esbuild __export() pattern for CJS modules (e.g., marked) |
| 898 | + 3. wrapCJSForESMWithModulePath: const→let for exports (ajv reassigns exports) |
| 899 | + 4. _loadFile polyfill path: fix double-wrapping bug (bundlePolyfill returns IIFE, handler wrapped again) |
| 900 | + 5. url module: add static wrapper with correct fileURLToPath/pathToFileURL (polyfill rejects valid file:// URLs) |
| 901 | + 6. global alias: add `global = globalThis` in postRestoreScript for CJS compat (graceful-fs) |
| 902 | + 7. BUILTIN_NAMED_EXPORTS: add tty (isatty), net, path (posix/win32) |
| 903 | + 8. stdin end: fix _emitStdinData to emit "end" for non-TTY empty stdin (Pi's readPipedStdin hangs) |
| 904 | + 9. AbortSignal: add addEventListener/removeEventListener no-op stubs (V8 lacks EventTarget on AbortSignal) |
| 905 | + 10. crypto: augment polyfill with bridge-backed randomUUID (crypto-browserify lacks it) |
| 906 | + 11. stdout/stderr: add write(data, callback) support and writableLength (Pi's flush hangs) |
| 907 | + 12. Response.body: add ReadableStream-like body to bridge fetch (Anthropic SDK needs body.getReader()) |
| 908 | + 13. Network: SSRF bypass for localhost in test adapter + ANTHROPIC_BASE_URL env var |
| 909 | +- Test setup: allowAll permissions, ANTHROPIC_BASE_URL pointing to mock server, closeStdin() |
| 910 | +- Status: Pi boots, imports main(), stdin resolves, fetch completes (200 OK from mock), BUT |
| 911 | + sandbox still hangs after fetch — the V8 bridge's async Promise delivery from networkFetchRaw |
| 912 | + to sandbox-side _networkFetchRaw.apply() doesn't resolve, likely a V8 IPC issue with |
| 913 | + large async bridge responses or SSE content parsing |
| 914 | +- Remaining blockers: |
| 915 | + - V8 bridge async response delivery: host-side networkFetchRaw resolves but sandbox-side |
| 916 | + Promise from _networkFetchRaw.apply(..., { result: { promise: true } }) never resolves |
| 917 | + - This affects ALL async bridge calls that return large responses, not just fetch |
| 918 | + - Simple fetch + response.text() works; body.getReader() works in isolation |
| 919 | + - The hang occurs specifically when the Anthropic SDK processes the full flow |
| 920 | +- Files changed: |
| 921 | + - packages/core/src/shared/esm-utils.ts (let exports, __export pattern) |
| 922 | + - packages/nodejs/src/bridge-handlers.ts (import mode, polyfill wrapping, crypto augment) |
| 923 | + - packages/nodejs/src/bridge/network.ts (Response.body ReadableStream) |
| 924 | + - packages/nodejs/src/bridge/process.ts (stdin end, stdout callback, writableLength) |
| 925 | + - packages/nodejs/src/builtin-modules.ts (tty, net, path named exports) |
| 926 | + - packages/nodejs/src/esm-compiler.ts (url static wrapper) |
| 927 | + - packages/nodejs/src/execution-driver.ts (global alias, AbortSignal stubs) |
| 928 | + - packages/secure-exec/tests/cli-tools/pi-headless.test.ts (SSRF bypass, allowAll, closeStdin, mockUrl) |
| 929 | +- **Learnings for future iterations:** |
| 930 | + - _resolveModule uses "require" mode hardcoded — dual CJS/ESM packages (marked, chalk) resolve to CJS entry; fix: use "import" mode since called from V8 ESM system |
| 931 | + - bundlePolyfill() already wraps in IIFE — _loadFile was double-wrapping with its own IIFE, discarding the inner result |
| 932 | + - CJS-to-ESM wrapper uses `const exports = module.exports` but CJS code (ajv) reassigns `exports` — must use `let` |
| 933 | + - node-stdlib-browser url polyfill rejects valid file:///path URLs — need custom static wrapper |
| 934 | + - Non-TTY stdin _emitStdinData returned early on empty stdinData without emitting "end" — async apps hang at readPipedStdin() |
| 935 | + - V8 sandbox AbortSignal lacks EventTarget interface — providing full implementation causes fetch to hang (event listeners keep session alive) |
| 936 | + - crypto polyfill from node-stdlib-browser lacks randomUUID — must augment with bridge's _cryptoRandomUUID |
| 937 | + - process.stdout.write("", callback) — bridge didn't invoke callback, Pi's flush Promise hangs |
| 938 | + - SSRF check in createDefaultNetworkAdapter blocks 127.0.0.1 — test needs directFetch bypass |
| 939 | + - Anthropic SDK uses ANTHROPIC_BASE_URL env var for API endpoint override |
| 940 | +--- |
0 commit comments