Skip to content

Commit 148303a

Browse files
authored
refactor(quickjs): consolidate hosted executor surface
1 parent 94709e3 commit 148303a

86 files changed

Lines changed: 899 additions & 1571 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@execbox/quickjs": minor
3+
"@execbox/protocol": minor
4+
---
5+
6+
`@execbox/quickjs` now covers inline, worker-hosted, and process-hosted QuickJS execution through `new QuickJsExecutor({ host })`. Migrate `@execbox/process` usage to `new QuickJsExecutor({ host: "process" })` and `@execbox/worker` usage to `new QuickJsExecutor({ host: "worker" })`.
7+
8+
`@execbox/protocol` no longer re-exports `createToolCallDispatcher` or `extractProviderManifests`. Import those helpers from `@execbox/core` instead.

AGENTS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- `execbox` is a Node.js 22+ npm workspace that publishes the `@execbox/*` package family.
66
- Core source lives under `packages/*/src`, tests live under `packages/*/__tests__`, runnable examples live under `examples/`, and the public docs site lives under `docs/`.
7-
- The workspace currently contains `@execbox/core`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/remote`, `@execbox/process`, `@execbox/worker`, and `@execbox/isolated-vm`.
7+
- The workspace currently contains `@execbox/core`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/remote`, and `@execbox/isolated-vm`.
88
- Keep changes aligned with existing package boundaries. Prefer changing the owning package instead of introducing cross-package shortcuts.
99

1010
## Setup Commands
@@ -24,15 +24,15 @@
2424

2525
- Do not edit generated `dist/` output under `packages/*/dist`; build artifacts are produced from source.
2626
- Preserve the existing ESM + TypeScript workspace structure and root script orchestration.
27-
- When you change exported APIs in `packages/*/src`, keep JSDoc in sync. ESLint requires JSDoc coverage for exported package symbols.
27+
- When you change exported APIs in `packages/*/src`, keep JSDoc in sync. Public functions, constants, types, and classes should always ship with JSDoc, and ESLint requires JSDoc coverage for exported package symbols.
2828
- Prefer inline type imports where possible; the ESLint config enforces `@typescript-eslint/consistent-type-imports`.
2929
- Update examples or docs when you change public package behavior, developer workflow, or recommended runtime choices.
3030

3131
## Testing Instructions
3232

3333
- For most code changes, run `npm run format:check`, `npm run lint`, `npm run typecheck`, `npm test`, and `npm run build`.
3434
- If you change package exports, manifest fields, or published type-resolution behavior, also run `npm run package:check`.
35-
- If you change the public API of `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/process`, `@execbox/worker`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`, also run `npm run api:check`.
35+
- If you change the public API of `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`, also run `npm run api:check`.
3636
- If you change docs site content, navigation, or VitePress config, also run `npm run docs:build`.
3737
- If you touch execution boundaries, timeout handling, abort propagation, schema validation, or log/memory controls, also run `npm run test:security`.
3838
- If you touch `@execbox/isolated-vm` or codepaths guarded by `VITEST_INCLUDE_ISOLATED_VM`, run `npm run test:isolated-vm` or `npm run verify:isolated-vm`.
@@ -48,7 +48,7 @@
4848
- Use Conventional Commits for git commit messages, for example `docs: add agent and contributor guides` or `fix(worker): handle timeout classification`.
4949
- Published package releases are managed with Changesets and GitHub Actions.
5050
- Add a `.changeset/*.md` entry when a change affects published package behavior, public APIs, or release notes for one or more `@execbox/*` packages.
51-
- If you intentionally change a checked-in API report for `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/process`, `@execbox/worker`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`, update it with `npm run api:update` in the same change as the code and changeset.
51+
- If you intentionally change a checked-in API report for `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`, update it with `npm run api:update` in the same change as the code and changeset.
5252
- Skip a changeset for docs-only, examples-only, CI-only, or internal maintenance changes that do not affect published package behavior.
5353

5454
## Useful References

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This guide is for both humans and coding agents. Agent-specific operating instru
1313
## Working In This Repo
1414

1515
- Make changes in the package that owns the behavior you are updating.
16-
- Keep exported API documentation current when you change exported symbols in `packages/*/src`; the ESLint config requires JSDoc for exported package APIs.
16+
- Keep exported API documentation current when you change exported symbols in `packages/*/src`; public functions, constants, types, and classes should ship with JSDoc, and the ESLint config enforces that for package APIs.
1717
- Update tests with behavior changes.
1818
- Update examples and docs when public behavior, recommended setup, or runtime tradeoffs change.
1919

@@ -27,7 +27,7 @@ This guide is for both humans and coding agents. Agent-specific operating instru
2727

2828
Choose the smallest verification set that covers your change, and include the commands you ran in your PR or handoff notes when the context would help reviewers.
2929

30-
- Public API changes to `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/process`, `@execbox/worker`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`: run `npm run api:check`
30+
- Public API changes to `@execbox/core`, `@execbox/core/mcp`, `@execbox/protocol`, `@execbox/quickjs`, `@execbox/quickjs/runner`, `@execbox/quickjs/runner/protocol-endpoint`, `@execbox/remote`, `@execbox/isolated-vm`, or `@execbox/isolated-vm/runner`: run `npm run api:check`
3131

3232
## Changesets
3333

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,20 @@ Portable code execution for [Model Context Protocol](https://modelcontextprotoco
66

77
[![License](https://img.shields.io/github/license/aallam/execbox?style=flat-square)](https://github.com/aallam/execbox/blob/main/LICENSE)
88
[![Docs](https://img.shields.io/badge/docs-site-0ea5e9?style=flat-square)](https://execbox.aallam.com)
9-
[![Packages](https://img.shields.io/badge/packages-7-111827?style=flat-square)](#package-map)
9+
[![Packages](https://img.shields.io/badge/packages-5-111827?style=flat-square)](#package-map)
1010

1111
</div>
1212

13-
Execbox turns host tool catalogs into callable guest namespaces, supports MCP wrapping on both sides of the boundary, and lets you place guest JavaScript where it fits your deployment: in-process, in a worker, in a child process, or behind your own remote transport.
13+
Execbox turns host tool catalogs into callable guest namespaces, supports MCP wrapping on both sides of the boundary, and lets you place guest JavaScript where it fits your deployment: inline, behind a worker or child-process host, or across your own remote transport.
1414

1515
## Package Map
1616

1717
| Package | npm | What it is for |
1818
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
1919
| [`@execbox/core`](./packages/core/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fcore?style=flat-square)](https://www.npmjs.com/package/@execbox/core) | Core execution contract, provider resolution, and MCP adapters |
20-
| [`@execbox/protocol`](./packages/protocol/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fprotocol?style=flat-square)](https://www.npmjs.com/package/@execbox/protocol) | Transport-safe messages and shared host-session helpers |
21-
| [`@execbox/quickjs`](./packages/quickjs/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fquickjs?style=flat-square)](https://www.npmjs.com/package/@execbox/quickjs) | QuickJS backend for execbox |
20+
| [`@execbox/protocol`](./packages/protocol/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fprotocol?style=flat-square)](https://www.npmjs.com/package/@execbox/protocol) | Low-level transport messages, sessions, and resource pools |
21+
| [`@execbox/quickjs`](./packages/quickjs/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fquickjs?style=flat-square)](https://www.npmjs.com/package/@execbox/quickjs) | QuickJS executor for inline, worker, and process hosts |
2222
| [`@execbox/remote`](./packages/remote/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fremote?style=flat-square)](https://www.npmjs.com/package/@execbox/remote) | Transport-backed remote executor |
23-
| [`@execbox/process`](./packages/process/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fprocess?style=flat-square)](https://www.npmjs.com/package/@execbox/process) | Child-process QuickJS executor |
24-
| [`@execbox/worker`](./packages/worker/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fworker?style=flat-square)](https://www.npmjs.com/package/@execbox/worker) | Worker-thread QuickJS executor |
2523
| [`@execbox/isolated-vm`](./packages/isolated-vm/) | [![npm](https://img.shields.io/npm/v/%40execbox%2Fisolated-vm?style=flat-square)](https://www.npmjs.com/package/@execbox/isolated-vm) | `isolated-vm` backend for execbox |
2624

2725
## Examples

benchmarks/config.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import type { Executor } from "@execbox/core";
22
import { QuickJsExecutor } from "@execbox/quickjs";
3-
import { ProcessExecutor } from "@execbox/process";
4-
import { WorkerExecutor } from "@execbox/worker";
53

64
export type DisposableExecutor = Executor & {
75
dispose?(): Promise<void>;
@@ -57,29 +55,37 @@ export function createBenchmarkFactories(): ExecutorFactory[] {
5755
{
5856
name: "worker (ephemeral)",
5957
create: () =>
60-
new WorkerExecutor({ mode: "ephemeral" }) as DisposableExecutor,
58+
new QuickJsExecutor({
59+
host: "worker",
60+
mode: "ephemeral",
61+
}) as DisposableExecutor,
6162
supportsExplicitPrewarm: false,
6263
},
6364
{
6465
name: "worker (pooled)",
6566
create: () =>
66-
new WorkerExecutor(
67-
createPooledBenchmarkOptions(),
68-
) as DisposableExecutor,
67+
new QuickJsExecutor({
68+
host: "worker",
69+
...createPooledBenchmarkOptions(),
70+
}) as DisposableExecutor,
6971
supportsExplicitPrewarm: true,
7072
},
7173
{
7274
name: "process (ephemeral)",
7375
create: () =>
74-
new ProcessExecutor({ mode: "ephemeral" }) as DisposableExecutor,
76+
new QuickJsExecutor({
77+
host: "process",
78+
mode: "ephemeral",
79+
}) as DisposableExecutor,
7580
supportsExplicitPrewarm: false,
7681
},
7782
{
7883
name: "process (pooled)",
7984
create: () =>
80-
new ProcessExecutor(
81-
createPooledBenchmarkOptions(),
82-
) as DisposableExecutor,
85+
new QuickJsExecutor({
86+
host: "process",
87+
...createPooledBenchmarkOptions(),
88+
}) as DisposableExecutor,
8389
supportsExplicitPrewarm: true,
8490
},
8591
];
@@ -98,6 +104,6 @@ export function createContentionExecutor(
98104
): DisposableExecutor {
99105
const pool = createContentionPoolOptions(poolSize);
100106
return executorType === "worker"
101-
? (new WorkerExecutor({ pool }) as DisposableExecutor)
102-
: (new ProcessExecutor({ pool }) as DisposableExecutor);
107+
? (new QuickJsExecutor({ host: "worker", pool }) as DisposableExecutor)
108+
: (new QuickJsExecutor({ host: "process", pool }) as DisposableExecutor);
103109
}

benchmarks/results.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ The pooled benchmark factories in this suite use a fixed `pool.maxSize: 2`.
123123

124124
## 7. Host-Process Memory Delta
125125

126-
This suite only measures the parent Node process. It does not attempt to attribute child-process RSS back to `ProcessExecutor`.
126+
This suite only measures the parent Node process. It does not attempt to attribute child-process RSS back to `QuickJsExecutor({ host: "process" })`.
127127

128128
| Executor | Heap Delta | RSS Delta | External Delta |
129129
| -------------------- | ---------- | --------- | -------------- |
@@ -151,4 +151,4 @@ This suite only measures the parent Node process. It does not attempt to attribu
151151
### What this snapshot does not prove
152152

153153
- It does not prove exact throughput rankings for every workload or host. The concurrency and tool-call suites are still sensitive to local scheduler noise.
154-
- It does not prove memory behavior for `ProcessExecutor`, because the memory suite intentionally avoids reporting child-process RSS as if it were host-process memory.
154+
- It does not prove memory behavior for `QuickJsExecutor({ host: "process" })`, because the memory suite intentionally avoids reporting child-process RSS as if it were host-process memory.

docs/architecture/README.md

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ This doc set is for two audiences:
1111

1212
- Start here for the package map, trust model, and overall flow.
1313
- Read [execbox-core.md](./execbox-core.md) for provider resolution, execution contracts, and error handling.
14-
- Read [execbox-executors.md](./execbox-executors.md) for QuickJS, process, worker-thread, and `isolated-vm` trade-offs.
14+
- Read [execbox-executors.md](./execbox-executors.md) for QuickJS host modes, remote execution, and `isolated-vm` trade-offs.
1515
- Read [execbox-mcp-and-protocol.md](./execbox-mcp-and-protocol.md) for MCP wrapping and where `execbox-protocol` fits.
1616
- Read [execbox-remote-workflow.md](./execbox-remote-workflow.md) for the end-to-end remote execution control flow.
1717
- Read [execbox-protocol-reference.md](./execbox-protocol-reference.md) for the protocol message catalog and session rules.
@@ -23,39 +23,30 @@ This doc set is for two audiences:
2323
flowchart LR
2424
APP["Host application"]
2525
CORE["@execbox/core<br/>provider resolution + runner semantics + MCP adapters"]
26-
QJS["@execbox/quickjs<br/>in-process QuickJS executor + reusable runner"]
26+
QJS["@execbox/quickjs<br/>QuickJS executor + reusable runner"]
2727
REM["@execbox/remote<br/>transport-backed remote executor"]
28-
PROC["@execbox/process<br/>child-process QuickJS executor"]
2928
IVM["@execbox/isolated-vm<br/>in-process isolated-vm executor + reusable runner"]
3029
PROTO["@execbox/protocol<br/>transport messages + shared host session"]
31-
WORKER["@execbox/worker<br/>worker-thread QuickJS executor"]
3230
MCP["MCP sources and wrapped servers"]
3331
3432
APP --> CORE
3533
APP --> QJS
3634
APP --> REM
37-
APP --> PROC
3835
APP --> IVM
39-
APP --> WORKER
4036
CORE --> MCP
37+
QJS --> PROTO
4138
REM --> PROTO
42-
PROC --> PROTO
43-
WORKER --> PROTO
44-
PROC --> QJS
45-
WORKER --> QJS
4639
```
4740

4841
### Package Roles
4942

50-
| Package | Role |
51-
| ---------------------- | ------------------------------------------------------------------------------------------------------------ |
52-
| `@execbox/core` | Core types, provider resolution, shared runner semantics, and MCP adapters |
53-
| `@execbox/quickjs` | Default executor backend using a fresh QuickJS runtime per execution and a reusable QuickJS runner |
54-
| `@execbox/remote` | Transport-backed executor that reuses the QuickJS protocol endpoint across an app-defined boundary |
55-
| `@execbox/process` | Child-process executor that runs the QuickJS session behind Node IPC with pooled shells by default |
56-
| `@execbox/isolated-vm` | Alternate executor backend using a fresh `isolated-vm` context and a reusable isolated-vm runner |
57-
| `@execbox/protocol` | Transport-safe execution messages, shared host sessions, and reusable resource pools |
58-
| `@execbox/worker` | Worker-thread executor that runs the QuickJS session behind a message boundary with pooled shells by default |
43+
| Package | Role |
44+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
45+
| `@execbox/core` | Core types, provider resolution, shared runner semantics, and MCP adapters |
46+
| `@execbox/quickjs` | Default QuickJS executor package with inline, worker-hosted, and process-hosted modes plus a reusable runner |
47+
| `@execbox/remote` | Transport-backed executor that reuses the QuickJS protocol endpoint across an app-defined boundary |
48+
| `@execbox/isolated-vm` | Alternate executor backend using a fresh `isolated-vm` context and a reusable isolated-vm runner |
49+
| `@execbox/protocol` | Transport-safe execution messages, shared host sessions, and reusable resource pools for hosted QuickJS and remote execution |
5950

6051
## End-to-End Execution Model
6152

@@ -110,4 +101,4 @@ Key implications:
110101

111102
## Architecture In One Paragraph
112103

113-
`@execbox/core` owns the stable execution contract, provider resolution, shared runner semantics, and MCP adapters. `@execbox/quickjs` and `@execbox/isolated-vm` each expose a runtime-specific reusable runner. `@execbox/process`, `@execbox/worker`, and `@execbox/remote` are transport adapters around the shared QuickJS protocol endpoint, while `@execbox/protocol` owns the transport boundary: message shapes, shared host sessions, and reusable resource pools for transport-backed execution.
104+
`@execbox/core` owns the stable execution contract, provider resolution, shared runner semantics, and MCP adapters. `@execbox/quickjs` and `@execbox/isolated-vm` each expose a runtime-specific reusable runner. Hosted `@execbox/quickjs` modes and `@execbox/remote` sit on top of `@execbox/protocol`, which owns the transport boundary: message shapes, shared host sessions, and reusable resource pools for transport-backed execution.

0 commit comments

Comments
 (0)