mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
Assert the Bun host keeps its launch flags across the macOS memory re-exec
S3: on macOS a Bun host re-executes itself in place to apply its jetsam
limit, rebuilding argv as `[process.execPath, ...process.execArgv,
...process.argv.slice(1)]`. If execArgv did not carry `--no-install
--no-env-file --config=/dev/null --no-addons`, the host that loads plugins
would run without them.
Observed 2026-09-30, macOS 26.1 arm64, Bun 1.4.0, with the real bundle
through the test harness (`startHost({ env: {
CODEWHALE_HOST_MEMORY_LIMIT_MIB: '300' } })`, then a plugin tool returning
`process.pid` and `process.execArgv`): hello reported
`memory_limit_mib: 300`, the pid after the re-exec equalled the spawned
pid, and execArgv was exactly ["--no-install","--no-env-file",
"--config=/dev/null","--no-addons"]. Plain `bun --no-install --no-env-file
--config=/dev/null --no-addons probe.mjs` also reports those four in
execArgv. So no code change: the existing macOS test's `pid` tool now
returns execArgv too and asserts it equals HOST_ARGS after the re-exec.
Negative control: with dist's re-exec line temporarily changed to drop
`...process.execArgv`, `bun test ./test/host.test.mjs -t "kernel memory
limit"` failed (0 pass, 1 fail, AssertionError at the execArgv deepEqual);
dist restored (`git diff --quiet -- dist` exit 0).
Suites on this tree: `npm run test:bun` (Bun 1.4.0): 63 pass, 0 fail;
`npm test` (Node 26.10.0): tests 63, pass 62, fail 0, skipped 1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: CodeWhale Bot <[email protected]>
This commit is contained in:
1 parent
578f52a8c4
commit
f1917dbe5b
1 file changed
+8
-4
@@ -7,7 +7,7 @@ import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { spawn } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { BUNDLE, IS_BUN, activate, sha256File, startHost } from './harness.mjs'
|
||||
import { BUNDLE, HOST_ARGS, IS_BUN, activate, sha256File, startHost } from './harness.mjs'
|
||||
import { encodeFrame } from '../dist/protocol.mjs'
|
||||
|
||||
function tempPlugin(source) {
|
||||
@@ -201,7 +201,7 @@ test('plugins cannot run native code in-process', async (t) => {
|
||||
test('a host asked for a kernel memory limit applies it only on Bun on macOS', async (t) => {
|
||||
const plugin = tempPlugin(`export const inject = ['tools']
|
||||
export function apply(ctx) {
|
||||
ctx.tools.register({ name: 'pid', description: '', parameters: { type: 'object', properties: {} }, execute: () => String(process.pid) })
|
||||
ctx.tools.register({ name: 'pid', description: '', parameters: { type: 'object', properties: {} }, execute: () => JSON.stringify({ pid: process.pid, execArgv: process.execArgv }) })
|
||||
ctx.tools.register({ name: 'hog', description: '', parameters: { type: 'object', properties: {} }, async execute() {
|
||||
const chunks = []
|
||||
for (let i = 0; i < 32; i++) { chunks.push(Buffer.alloc(64 * 1024 * 1024, 1)); await new Promise((resolve) => setTimeout(resolve, 5)) }
|
||||
@@ -220,9 +220,13 @@ export function apply(ctx) {
|
||||
return
|
||||
}
|
||||
assert.equal(host.hello.memory_limit_mib, 300)
|
||||
// Re-executed in place: the pid the core spawned is the one running plugins.
|
||||
// Re-executed in place: the pid the core spawned is the one running plugins,
|
||||
// still with every launch flag (`--no-install`, `--no-env-file`, the null
|
||||
// `--config`, `--no-addons`), since the re-exec rebuilds argv from execArgv.
|
||||
const running = await host.call('tool/call', { handle: pid, call_id: 'p', input: {}, deadline_ms: 5000 })
|
||||
assert.equal(Number(running.content[0].text), host.child.pid)
|
||||
const reexecuted = JSON.parse(running.content[0].text)
|
||||
assert.equal(reexecuted.pid, host.child.pid)
|
||||
assert.deepEqual(reexecuted.execArgv, HOST_ARGS)
|
||||
// 2 GiB against a 300 MiB limit: the kernel kills the host.
|
||||
host.request('tool/call', { handle: hog, call_id: 'h', input: {}, deadline_ms: 30_000 })
|
||||
const exit = await Promise.race([host.exit, new Promise((resolve) => setTimeout(() => resolve('still running'), 20_000))])
|
||||
|
||||
Reference in new issue
Block a user