@mcploom/codexec-worker 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/dist/index.cjs +94 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -0
- package/dist/workerEntry.cjs +18 -0
- package/dist/workerEntry.cjs.map +1 -0
- package/dist/workerEntry.d.cts +1 -0
- package/dist/workerEntry.d.ts +1 -0
- package/dist/workerEntry.js +19 -0
- package/dist/workerEntry.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# @mcploom/codexec-worker
|
|
2
|
+
|
|
3
|
+
Worker-thread executor for `@mcploom/codexec`, using the shared QuickJS runner behind a message boundary.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@mcploom/codexec-worker)
|
|
6
|
+
[](https://github.com/aallam/mcploom/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Choose `codexec-worker` When
|
|
9
|
+
|
|
10
|
+
- you want QuickJS semantics without running the runtime on the main thread
|
|
11
|
+
- you want a worker termination backstop for timeouts
|
|
12
|
+
- you are comfortable paying worker startup overhead per execution
|
|
13
|
+
|
|
14
|
+
If you want the simplest default backend, use [`@mcploom/codexec-quickjs`](https://www.npmjs.com/package/@mcploom/codexec-quickjs) instead.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @mcploom/codexec @mcploom/codexec-worker
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { resolveProvider } from "@mcploom/codexec";
|
|
26
|
+
import { WorkerExecutor } from "@mcploom/codexec-worker";
|
|
27
|
+
|
|
28
|
+
const provider = resolveProvider({
|
|
29
|
+
name: "tools",
|
|
30
|
+
tools: {
|
|
31
|
+
echo: {
|
|
32
|
+
execute: async (input) => input,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const executor = new WorkerExecutor();
|
|
38
|
+
const result = await executor.execute("await tools.echo({ ok: true })", [
|
|
39
|
+
provider,
|
|
40
|
+
]);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Security Notes
|
|
44
|
+
|
|
45
|
+
- This package improves lifecycle isolation by moving the QuickJS runtime to a worker thread.
|
|
46
|
+
- It is still same-process execution and is not documented as a hard hostile-code boundary.
|
|
47
|
+
- Providers remain the real capability boundary.
|
|
48
|
+
- Internally it is a thin transport adapter over the shared `codexec-protocol` host session and the shared QuickJS protocol endpoint.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
- [Worker-backed codexec execution](https://github.com/aallam/mcploom/blob/main/examples/codexec-worker.ts)
|
|
53
|
+
- [Architecture overview](https://github.com/aallam/mcploom/blob/main/docs/codexec/architecture/README.md)
|
|
54
|
+
- [Executors architecture](https://github.com/aallam/mcploom/blob/main/docs/codexec/architecture/codexec-executors.md)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
let node_crypto = require("node:crypto");
|
|
2
|
+
let node_worker_threads = require("node:worker_threads");
|
|
3
|
+
let __mcploom_codexec_protocol = require("@mcploom/codexec-protocol");
|
|
4
|
+
let __mcploom_codexec = require("@mcploom/codexec");
|
|
5
|
+
|
|
6
|
+
//#region src/workerExecutor.ts
|
|
7
|
+
const DEFAULT_CANCEL_GRACE_MS = 25;
|
|
8
|
+
const DEFAULT_MAX_LOG_CHARS = 64e3;
|
|
9
|
+
const DEFAULT_MAX_LOG_LINES = 100;
|
|
10
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
12
|
+
function resolveWorkerEntryUrl() {
|
|
13
|
+
const extension = require("url").pathToFileURL(__filename).href.endsWith(".ts") ? ".ts" : ".js";
|
|
14
|
+
return new URL(`./workerEntry${extension}`, require("url").pathToFileURL(__filename).href);
|
|
15
|
+
}
|
|
16
|
+
function createRuntimeOptions(options, overrides = {}) {
|
|
17
|
+
return {
|
|
18
|
+
maxLogChars: overrides.maxLogChars ?? options.maxLogChars ?? DEFAULT_MAX_LOG_CHARS,
|
|
19
|
+
maxLogLines: overrides.maxLogLines ?? options.maxLogLines ?? DEFAULT_MAX_LOG_LINES,
|
|
20
|
+
memoryLimitBytes: overrides.memoryLimitBytes ?? options.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES,
|
|
21
|
+
timeoutMs: overrides.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function createWorkerTransport(worker) {
|
|
25
|
+
return {
|
|
26
|
+
dispose: async () => {
|
|
27
|
+
await worker.terminate().catch(() => {});
|
|
28
|
+
},
|
|
29
|
+
onClose: (handler) => {
|
|
30
|
+
const wrapped = (code) => {
|
|
31
|
+
handler({
|
|
32
|
+
code,
|
|
33
|
+
message: `Worker exited unexpectedly with code ${code}`
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
worker.on("exit", wrapped);
|
|
37
|
+
return () => worker.off("exit", wrapped);
|
|
38
|
+
},
|
|
39
|
+
onError: (handler) => {
|
|
40
|
+
worker.on("error", handler);
|
|
41
|
+
return () => worker.off("error", handler);
|
|
42
|
+
},
|
|
43
|
+
onMessage: (handler) => {
|
|
44
|
+
const wrapped = (message) => {
|
|
45
|
+
handler(message);
|
|
46
|
+
};
|
|
47
|
+
worker.on("message", wrapped);
|
|
48
|
+
return () => worker.off("message", wrapped);
|
|
49
|
+
},
|
|
50
|
+
send: (message) => {
|
|
51
|
+
worker.postMessage(message);
|
|
52
|
+
},
|
|
53
|
+
terminate: async () => {
|
|
54
|
+
await worker.terminate().catch(() => {});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.
|
|
60
|
+
*/
|
|
61
|
+
var WorkerExecutor = class {
|
|
62
|
+
cancelGraceMs;
|
|
63
|
+
options;
|
|
64
|
+
/**
|
|
65
|
+
* Creates a worker-backed executor with hard-stop timeout behavior.
|
|
66
|
+
*/
|
|
67
|
+
constructor(options = {}) {
|
|
68
|
+
this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
|
|
69
|
+
this.options = options;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Executes JavaScript inside a fresh worker thread running QuickJS.
|
|
73
|
+
*/
|
|
74
|
+
async execute(code, providers, options = {}) {
|
|
75
|
+
if (options.signal?.aborted) return (0, __mcploom_codexec.createTimeoutExecuteResult)();
|
|
76
|
+
const worker = new node_worker_threads.Worker(resolveWorkerEntryUrl(), {
|
|
77
|
+
execArgv: (0, __mcploom_codexec_protocol.getNodeTransportExecArgv)(require("url").pathToFileURL(__filename).href),
|
|
78
|
+
resourceLimits: this.options.workerResourceLimits
|
|
79
|
+
});
|
|
80
|
+
return await (0, __mcploom_codexec_protocol.runHostTransportSession)({
|
|
81
|
+
cancelGraceMs: this.cancelGraceMs,
|
|
82
|
+
code,
|
|
83
|
+
executionId: (0, node_crypto.randomUUID)(),
|
|
84
|
+
providers,
|
|
85
|
+
runtimeOptions: createRuntimeOptions(this.options, options),
|
|
86
|
+
signal: options.signal,
|
|
87
|
+
transport: createWorkerTransport(worker)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
exports.WorkerExecutor = WorkerExecutor;
|
|
94
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Worker"],"sources":["../src/workerExecutor.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Worker } from \"node:worker_threads\";\n\nimport {\n type ExecutorRuntimeOptions,\n runHostTransportSession,\n getNodeTransportExecArgv,\n type HostTransport,\n type RunnerMessage,\n} from \"@mcploom/codexec-protocol\";\nimport {\n createTimeoutExecuteResult,\n type ExecutionOptions,\n type ExecuteResult,\n type Executor,\n type ResolvedToolProvider,\n} from \"@mcploom/codexec\";\n\nimport type { WorkerExecutorOptions } from \"./types\";\n\nconst DEFAULT_CANCEL_GRACE_MS = 25;\nconst DEFAULT_MAX_LOG_CHARS = 64_000;\nconst DEFAULT_MAX_LOG_LINES = 100;\nconst DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_TIMEOUT_MS = 5000;\n\nfunction resolveWorkerEntryUrl(): URL {\n const extension = import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\";\n return new URL(`./workerEntry${extension}`, import.meta.url);\n}\n\nfunction createRuntimeOptions(\n options: WorkerExecutorOptions,\n overrides: ExecutionOptions = {},\n): Required<ExecutorRuntimeOptions> {\n return {\n maxLogChars:\n overrides.maxLogChars ?? options.maxLogChars ?? DEFAULT_MAX_LOG_CHARS,\n maxLogLines:\n overrides.maxLogLines ?? options.maxLogLines ?? DEFAULT_MAX_LOG_LINES,\n memoryLimitBytes:\n overrides.memoryLimitBytes ??\n options.memoryLimitBytes ??\n DEFAULT_MEMORY_LIMIT_BYTES,\n timeoutMs: overrides.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n}\n\nfunction createWorkerTransport(worker: Worker): HostTransport {\n return {\n dispose: async () => {\n await worker.terminate().catch(() => {});\n },\n onClose: (handler) => {\n const wrapped = (code: number) => {\n handler({\n code,\n message: `Worker exited unexpectedly with code ${code}`,\n });\n };\n worker.on(\"exit\", wrapped);\n return () => worker.off(\"exit\", wrapped);\n },\n onError: (handler) => {\n worker.on(\"error\", handler);\n return () => worker.off(\"error\", handler);\n },\n onMessage: (handler) => {\n const wrapped = (message: unknown) => {\n handler(message as RunnerMessage);\n };\n worker.on(\"message\", wrapped);\n return () => worker.off(\"message\", wrapped);\n },\n send: (message) => {\n worker.postMessage(message);\n },\n terminate: async () => {\n await worker.terminate().catch(() => {});\n },\n };\n}\n\n/**\n * Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.\n */\nexport class WorkerExecutor implements Executor {\n private readonly cancelGraceMs: number;\n private readonly options: WorkerExecutorOptions;\n\n /**\n * Creates a worker-backed executor with hard-stop timeout behavior.\n */\n constructor(options: WorkerExecutorOptions = {}) {\n this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;\n this.options = options;\n }\n\n /**\n * Executes JavaScript inside a fresh worker thread running QuickJS.\n */\n async execute(\n code: string,\n providers: ResolvedToolProvider[],\n options: ExecutionOptions = {},\n ): Promise<ExecuteResult> {\n if (options.signal?.aborted) {\n return createTimeoutExecuteResult();\n }\n\n const worker = new Worker(resolveWorkerEntryUrl(), {\n execArgv: getNodeTransportExecArgv(import.meta.url),\n resourceLimits: this.options.workerResourceLimits,\n });\n\n return await runHostTransportSession({\n cancelGraceMs: this.cancelGraceMs,\n code,\n executionId: randomUUID(),\n providers,\n runtimeOptions: createRuntimeOptions(this.options, options),\n signal: options.signal,\n transport: createWorkerTransport(worker),\n });\n }\n}\n"],"mappings":";;;;;;AAoBA,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,wBAAwB;AAC9B,MAAM,6BAA6B,KAAK,OAAO;AAC/C,MAAM,qBAAqB;AAE3B,SAAS,wBAA6B;CACpC,MAAM,0DAA4B,SAAS,MAAM,GAAG,QAAQ;AAC5D,QAAO,IAAI,IAAI,gBAAgB,2DAA6B;;AAG9D,SAAS,qBACP,SACA,YAA8B,EAAE,EACE;AAClC,QAAO;EACL,aACE,UAAU,eAAe,QAAQ,eAAe;EAClD,aACE,UAAU,eAAe,QAAQ,eAAe;EAClD,kBACE,UAAU,oBACV,QAAQ,oBACR;EACF,WAAW,UAAU,aAAa,QAAQ,aAAa;EACxD;;AAGH,SAAS,sBAAsB,QAA+B;AAC5D,QAAO;EACL,SAAS,YAAY;AACnB,SAAM,OAAO,WAAW,CAAC,YAAY,GAAG;;EAE1C,UAAU,YAAY;GACpB,MAAM,WAAW,SAAiB;AAChC,YAAQ;KACN;KACA,SAAS,wCAAwC;KAClD,CAAC;;AAEJ,UAAO,GAAG,QAAQ,QAAQ;AAC1B,gBAAa,OAAO,IAAI,QAAQ,QAAQ;;EAE1C,UAAU,YAAY;AACpB,UAAO,GAAG,SAAS,QAAQ;AAC3B,gBAAa,OAAO,IAAI,SAAS,QAAQ;;EAE3C,YAAY,YAAY;GACtB,MAAM,WAAW,YAAqB;AACpC,YAAQ,QAAyB;;AAEnC,UAAO,GAAG,WAAW,QAAQ;AAC7B,gBAAa,OAAO,IAAI,WAAW,QAAQ;;EAE7C,OAAO,YAAY;AACjB,UAAO,YAAY,QAAQ;;EAE7B,WAAW,YAAY;AACrB,SAAM,OAAO,WAAW,CAAC,YAAY,GAAG;;EAE3C;;;;;AAMH,IAAa,iBAAb,MAAgD;CAC9C,AAAiB;CACjB,AAAiB;;;;CAKjB,YAAY,UAAiC,EAAE,EAAE;AAC/C,OAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,OAAK,UAAU;;;;;CAMjB,MAAM,QACJ,MACA,WACA,UAA4B,EAAE,EACN;AACxB,MAAI,QAAQ,QAAQ,QAClB,2DAAmC;EAGrC,MAAM,SAAS,IAAIA,2BAAO,uBAAuB,EAAE;GACjD,iHAAmD;GACnD,gBAAgB,KAAK,QAAQ;GAC9B,CAAC;AAEF,SAAO,8DAA8B;GACnC,eAAe,KAAK;GACpB;GACA,0CAAyB;GACzB;GACA,gBAAgB,qBAAqB,KAAK,SAAS,QAAQ;GAC3D,QAAQ,QAAQ;GAChB,WAAW,sBAAsB,OAAO;GACzC,CAAC"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExecuteResult, ExecutionOptions, Executor, ResolvedToolProvider } from "@mcploom/codexec";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Optional V8 heap limits used only as a backstop for worker thread safety.
|
|
6
|
+
*/
|
|
7
|
+
interface WorkerResourceLimits {
|
|
8
|
+
maxOldGenerationSizeMb?: number;
|
|
9
|
+
maxYoungGenerationSizeMb?: number;
|
|
10
|
+
stackSizeMb?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Options for constructing a {@link WorkerExecutor}.
|
|
14
|
+
*/
|
|
15
|
+
interface WorkerExecutorOptions {
|
|
16
|
+
/** Extra grace period after timeout before force-terminating the worker. */
|
|
17
|
+
cancelGraceMs?: number;
|
|
18
|
+
/** Maximum total characters preserved across captured log lines. */
|
|
19
|
+
maxLogChars?: number;
|
|
20
|
+
/** Maximum number of captured log lines returned in the result. */
|
|
21
|
+
maxLogLines?: number;
|
|
22
|
+
/** Guest memory limit in bytes enforced by QuickJS inside the worker. */
|
|
23
|
+
memoryLimitBytes?: number;
|
|
24
|
+
/** Wall-clock execution timeout in milliseconds. */
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
/** Optional Node worker heap limits used as a backstop only. */
|
|
27
|
+
workerResourceLimits?: WorkerResourceLimits;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/workerExecutor.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.
|
|
33
|
+
*/
|
|
34
|
+
declare class WorkerExecutor implements Executor {
|
|
35
|
+
private readonly cancelGraceMs;
|
|
36
|
+
private readonly options;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a worker-backed executor with hard-stop timeout behavior.
|
|
39
|
+
*/
|
|
40
|
+
constructor(options?: WorkerExecutorOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Executes JavaScript inside a fresh worker thread running QuickJS.
|
|
43
|
+
*/
|
|
44
|
+
execute(code: string, providers: ResolvedToolProvider[], options?: ExecutionOptions): Promise<ExecuteResult>;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { WorkerExecutor, type WorkerExecutorOptions, type WorkerResourceLimits };
|
|
48
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/workerExecutor.ts"],"sourcesContent":[],"mappings":";;;;;;AAGiB,UAAA,oBAAA,CAAoB;EASpB,sBAAA,CAAA,EAAqB,MAAA;;;;AC0EtC;;;AAkBa,UD5FI,qBAAA,CC4FJ;EACA;EAAR,aAAA,CAAA,EAAA,MAAA;EAnBkC;EAAQ,WAAA,CAAA,EAAA,MAAA;;;;;;;;yBD9DtB;;;;;AArBzB;AASA;cC0Ea,cAAA,YAA0B;;;EAA1B;;;EAkBA,WAAA,CAAA,OAAA,CAAA,EAXU,qBAWV;EACA;;;EAnBkC,OAAA,CAAA,IAAA,EAAA,MAAA,EAAA,SAAA,EAiBhC,oBAjBgC,EAAA,EAAA,OAAA,CAAA,EAkBlC,gBAlBkC,CAAA,EAmB1C,OAnB0C,CAmBlC,aAnBkC,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExecuteResult, ExecutionOptions, Executor, ResolvedToolProvider } from "@mcploom/codexec";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Optional V8 heap limits used only as a backstop for worker thread safety.
|
|
6
|
+
*/
|
|
7
|
+
interface WorkerResourceLimits {
|
|
8
|
+
maxOldGenerationSizeMb?: number;
|
|
9
|
+
maxYoungGenerationSizeMb?: number;
|
|
10
|
+
stackSizeMb?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Options for constructing a {@link WorkerExecutor}.
|
|
14
|
+
*/
|
|
15
|
+
interface WorkerExecutorOptions {
|
|
16
|
+
/** Extra grace period after timeout before force-terminating the worker. */
|
|
17
|
+
cancelGraceMs?: number;
|
|
18
|
+
/** Maximum total characters preserved across captured log lines. */
|
|
19
|
+
maxLogChars?: number;
|
|
20
|
+
/** Maximum number of captured log lines returned in the result. */
|
|
21
|
+
maxLogLines?: number;
|
|
22
|
+
/** Guest memory limit in bytes enforced by QuickJS inside the worker. */
|
|
23
|
+
memoryLimitBytes?: number;
|
|
24
|
+
/** Wall-clock execution timeout in milliseconds. */
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
/** Optional Node worker heap limits used as a backstop only. */
|
|
27
|
+
workerResourceLimits?: WorkerResourceLimits;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/workerExecutor.d.ts
|
|
31
|
+
/**
|
|
32
|
+
* Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.
|
|
33
|
+
*/
|
|
34
|
+
declare class WorkerExecutor implements Executor {
|
|
35
|
+
private readonly cancelGraceMs;
|
|
36
|
+
private readonly options;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a worker-backed executor with hard-stop timeout behavior.
|
|
39
|
+
*/
|
|
40
|
+
constructor(options?: WorkerExecutorOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Executes JavaScript inside a fresh worker thread running QuickJS.
|
|
43
|
+
*/
|
|
44
|
+
execute(code: string, providers: ResolvedToolProvider[], options?: ExecutionOptions): Promise<ExecuteResult>;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { WorkerExecutor, type WorkerExecutorOptions, type WorkerResourceLimits };
|
|
48
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/workerExecutor.ts"],"sourcesContent":[],"mappings":";;;;;;AAGiB,UAAA,oBAAA,CAAoB;EASpB,sBAAA,CAAA,EAAqB,MAAA;;;;AC0EtC;;;AAkBa,UD5FI,qBAAA,CC4FJ;EACA;EAAR,aAAA,CAAA,EAAA,MAAA;EAnBkC;EAAQ,WAAA,CAAA,EAAA,MAAA;;;;;;;;yBD9DtB;;;;;AArBzB;AASA;cC0Ea,cAAA,YAA0B;;;EAA1B;;;EAkBA,WAAA,CAAA,OAAA,CAAA,EAXU,qBAWV;EACA;;;EAnBkC,OAAA,CAAA,IAAA,EAAA,MAAA,EAAA,SAAA,EAiBhC,oBAjBgC,EAAA,EAAA,OAAA,CAAA,EAkBlC,gBAlBkC,CAAA,EAmB1C,OAnB0C,CAmBlC,aAnBkC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Worker } from "node:worker_threads";
|
|
3
|
+
import { getNodeTransportExecArgv, runHostTransportSession } from "@mcploom/codexec-protocol";
|
|
4
|
+
import { createTimeoutExecuteResult } from "@mcploom/codexec";
|
|
5
|
+
|
|
6
|
+
//#region src/workerExecutor.ts
|
|
7
|
+
const DEFAULT_CANCEL_GRACE_MS = 25;
|
|
8
|
+
const DEFAULT_MAX_LOG_CHARS = 64e3;
|
|
9
|
+
const DEFAULT_MAX_LOG_LINES = 100;
|
|
10
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 5e3;
|
|
12
|
+
function resolveWorkerEntryUrl() {
|
|
13
|
+
const extension = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
|
|
14
|
+
return new URL(`./workerEntry${extension}`, import.meta.url);
|
|
15
|
+
}
|
|
16
|
+
function createRuntimeOptions(options, overrides = {}) {
|
|
17
|
+
return {
|
|
18
|
+
maxLogChars: overrides.maxLogChars ?? options.maxLogChars ?? DEFAULT_MAX_LOG_CHARS,
|
|
19
|
+
maxLogLines: overrides.maxLogLines ?? options.maxLogLines ?? DEFAULT_MAX_LOG_LINES,
|
|
20
|
+
memoryLimitBytes: overrides.memoryLimitBytes ?? options.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES,
|
|
21
|
+
timeoutMs: overrides.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function createWorkerTransport(worker) {
|
|
25
|
+
return {
|
|
26
|
+
dispose: async () => {
|
|
27
|
+
await worker.terminate().catch(() => {});
|
|
28
|
+
},
|
|
29
|
+
onClose: (handler) => {
|
|
30
|
+
const wrapped = (code) => {
|
|
31
|
+
handler({
|
|
32
|
+
code,
|
|
33
|
+
message: `Worker exited unexpectedly with code ${code}`
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
worker.on("exit", wrapped);
|
|
37
|
+
return () => worker.off("exit", wrapped);
|
|
38
|
+
},
|
|
39
|
+
onError: (handler) => {
|
|
40
|
+
worker.on("error", handler);
|
|
41
|
+
return () => worker.off("error", handler);
|
|
42
|
+
},
|
|
43
|
+
onMessage: (handler) => {
|
|
44
|
+
const wrapped = (message) => {
|
|
45
|
+
handler(message);
|
|
46
|
+
};
|
|
47
|
+
worker.on("message", wrapped);
|
|
48
|
+
return () => worker.off("message", wrapped);
|
|
49
|
+
},
|
|
50
|
+
send: (message) => {
|
|
51
|
+
worker.postMessage(message);
|
|
52
|
+
},
|
|
53
|
+
terminate: async () => {
|
|
54
|
+
await worker.terminate().catch(() => {});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.
|
|
60
|
+
*/
|
|
61
|
+
var WorkerExecutor = class {
|
|
62
|
+
cancelGraceMs;
|
|
63
|
+
options;
|
|
64
|
+
/**
|
|
65
|
+
* Creates a worker-backed executor with hard-stop timeout behavior.
|
|
66
|
+
*/
|
|
67
|
+
constructor(options = {}) {
|
|
68
|
+
this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;
|
|
69
|
+
this.options = options;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Executes JavaScript inside a fresh worker thread running QuickJS.
|
|
73
|
+
*/
|
|
74
|
+
async execute(code, providers, options = {}) {
|
|
75
|
+
if (options.signal?.aborted) return createTimeoutExecuteResult();
|
|
76
|
+
const worker = new Worker(resolveWorkerEntryUrl(), {
|
|
77
|
+
execArgv: getNodeTransportExecArgv(import.meta.url),
|
|
78
|
+
resourceLimits: this.options.workerResourceLimits
|
|
79
|
+
});
|
|
80
|
+
return await runHostTransportSession({
|
|
81
|
+
cancelGraceMs: this.cancelGraceMs,
|
|
82
|
+
code,
|
|
83
|
+
executionId: randomUUID(),
|
|
84
|
+
providers,
|
|
85
|
+
runtimeOptions: createRuntimeOptions(this.options, options),
|
|
86
|
+
signal: options.signal,
|
|
87
|
+
transport: createWorkerTransport(worker)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { WorkerExecutor };
|
|
94
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/workerExecutor.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { Worker } from \"node:worker_threads\";\n\nimport {\n type ExecutorRuntimeOptions,\n runHostTransportSession,\n getNodeTransportExecArgv,\n type HostTransport,\n type RunnerMessage,\n} from \"@mcploom/codexec-protocol\";\nimport {\n createTimeoutExecuteResult,\n type ExecutionOptions,\n type ExecuteResult,\n type Executor,\n type ResolvedToolProvider,\n} from \"@mcploom/codexec\";\n\nimport type { WorkerExecutorOptions } from \"./types\";\n\nconst DEFAULT_CANCEL_GRACE_MS = 25;\nconst DEFAULT_MAX_LOG_CHARS = 64_000;\nconst DEFAULT_MAX_LOG_LINES = 100;\nconst DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;\nconst DEFAULT_TIMEOUT_MS = 5000;\n\nfunction resolveWorkerEntryUrl(): URL {\n const extension = import.meta.url.endsWith(\".ts\") ? \".ts\" : \".js\";\n return new URL(`./workerEntry${extension}`, import.meta.url);\n}\n\nfunction createRuntimeOptions(\n options: WorkerExecutorOptions,\n overrides: ExecutionOptions = {},\n): Required<ExecutorRuntimeOptions> {\n return {\n maxLogChars:\n overrides.maxLogChars ?? options.maxLogChars ?? DEFAULT_MAX_LOG_CHARS,\n maxLogLines:\n overrides.maxLogLines ?? options.maxLogLines ?? DEFAULT_MAX_LOG_LINES,\n memoryLimitBytes:\n overrides.memoryLimitBytes ??\n options.memoryLimitBytes ??\n DEFAULT_MEMORY_LIMIT_BYTES,\n timeoutMs: overrides.timeoutMs ?? options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n };\n}\n\nfunction createWorkerTransport(worker: Worker): HostTransport {\n return {\n dispose: async () => {\n await worker.terminate().catch(() => {});\n },\n onClose: (handler) => {\n const wrapped = (code: number) => {\n handler({\n code,\n message: `Worker exited unexpectedly with code ${code}`,\n });\n };\n worker.on(\"exit\", wrapped);\n return () => worker.off(\"exit\", wrapped);\n },\n onError: (handler) => {\n worker.on(\"error\", handler);\n return () => worker.off(\"error\", handler);\n },\n onMessage: (handler) => {\n const wrapped = (message: unknown) => {\n handler(message as RunnerMessage);\n };\n worker.on(\"message\", wrapped);\n return () => worker.off(\"message\", wrapped);\n },\n send: (message) => {\n worker.postMessage(message);\n },\n terminate: async () => {\n await worker.terminate().catch(() => {});\n },\n };\n}\n\n/**\n * Worker-thread executor that runs guest code inside a dedicated QuickJS runtime per call.\n */\nexport class WorkerExecutor implements Executor {\n private readonly cancelGraceMs: number;\n private readonly options: WorkerExecutorOptions;\n\n /**\n * Creates a worker-backed executor with hard-stop timeout behavior.\n */\n constructor(options: WorkerExecutorOptions = {}) {\n this.cancelGraceMs = options.cancelGraceMs ?? DEFAULT_CANCEL_GRACE_MS;\n this.options = options;\n }\n\n /**\n * Executes JavaScript inside a fresh worker thread running QuickJS.\n */\n async execute(\n code: string,\n providers: ResolvedToolProvider[],\n options: ExecutionOptions = {},\n ): Promise<ExecuteResult> {\n if (options.signal?.aborted) {\n return createTimeoutExecuteResult();\n }\n\n const worker = new Worker(resolveWorkerEntryUrl(), {\n execArgv: getNodeTransportExecArgv(import.meta.url),\n resourceLimits: this.options.workerResourceLimits,\n });\n\n return await runHostTransportSession({\n cancelGraceMs: this.cancelGraceMs,\n code,\n executionId: randomUUID(),\n providers,\n runtimeOptions: createRuntimeOptions(this.options, options),\n signal: options.signal,\n transport: createWorkerTransport(worker),\n });\n }\n}\n"],"mappings":";;;;;;AAoBA,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,wBAAwB;AAC9B,MAAM,6BAA6B,KAAK,OAAO;AAC/C,MAAM,qBAAqB;AAE3B,SAAS,wBAA6B;CACpC,MAAM,YAAY,OAAO,KAAK,IAAI,SAAS,MAAM,GAAG,QAAQ;AAC5D,QAAO,IAAI,IAAI,gBAAgB,aAAa,OAAO,KAAK,IAAI;;AAG9D,SAAS,qBACP,SACA,YAA8B,EAAE,EACE;AAClC,QAAO;EACL,aACE,UAAU,eAAe,QAAQ,eAAe;EAClD,aACE,UAAU,eAAe,QAAQ,eAAe;EAClD,kBACE,UAAU,oBACV,QAAQ,oBACR;EACF,WAAW,UAAU,aAAa,QAAQ,aAAa;EACxD;;AAGH,SAAS,sBAAsB,QAA+B;AAC5D,QAAO;EACL,SAAS,YAAY;AACnB,SAAM,OAAO,WAAW,CAAC,YAAY,GAAG;;EAE1C,UAAU,YAAY;GACpB,MAAM,WAAW,SAAiB;AAChC,YAAQ;KACN;KACA,SAAS,wCAAwC;KAClD,CAAC;;AAEJ,UAAO,GAAG,QAAQ,QAAQ;AAC1B,gBAAa,OAAO,IAAI,QAAQ,QAAQ;;EAE1C,UAAU,YAAY;AACpB,UAAO,GAAG,SAAS,QAAQ;AAC3B,gBAAa,OAAO,IAAI,SAAS,QAAQ;;EAE3C,YAAY,YAAY;GACtB,MAAM,WAAW,YAAqB;AACpC,YAAQ,QAAyB;;AAEnC,UAAO,GAAG,WAAW,QAAQ;AAC7B,gBAAa,OAAO,IAAI,WAAW,QAAQ;;EAE7C,OAAO,YAAY;AACjB,UAAO,YAAY,QAAQ;;EAE7B,WAAW,YAAY;AACrB,SAAM,OAAO,WAAW,CAAC,YAAY,GAAG;;EAE3C;;;;;AAMH,IAAa,iBAAb,MAAgD;CAC9C,AAAiB;CACjB,AAAiB;;;;CAKjB,YAAY,UAAiC,EAAE,EAAE;AAC/C,OAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,OAAK,UAAU;;;;;CAMjB,MAAM,QACJ,MACA,WACA,UAA4B,EAAE,EACN;AACxB,MAAI,QAAQ,QAAQ,QAClB,QAAO,4BAA4B;EAGrC,MAAM,SAAS,IAAI,OAAO,uBAAuB,EAAE;GACjD,UAAU,yBAAyB,OAAO,KAAK,IAAI;GACnD,gBAAgB,KAAK,QAAQ;GAC9B,CAAC;AAEF,SAAO,MAAM,wBAAwB;GACnC,eAAe,KAAK;GACpB;GACA,aAAa,YAAY;GACzB;GACA,gBAAgB,qBAAqB,KAAK,SAAS,QAAQ;GAC3D,QAAQ,QAAQ;GAChB,WAAW,sBAAsB,OAAO;GACzC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
let node_worker_threads = require("node:worker_threads");
|
|
2
|
+
let __mcploom_codexec_quickjs_runner_protocol_endpoint = require("@mcploom/codexec-quickjs/runner/protocol-endpoint");
|
|
3
|
+
|
|
4
|
+
//#region src/workerEntry.ts
|
|
5
|
+
if (!node_worker_threads.parentPort) throw new Error("WorkerExecutor requires a worker parent port");
|
|
6
|
+
const workerPort = node_worker_threads.parentPort;
|
|
7
|
+
(0, __mcploom_codexec_quickjs_runner_protocol_endpoint.attachQuickJsProtocolEndpoint)({
|
|
8
|
+
onMessage(handler) {
|
|
9
|
+
workerPort.on("message", handler);
|
|
10
|
+
return () => workerPort.off("message", handler);
|
|
11
|
+
},
|
|
12
|
+
send(message) {
|
|
13
|
+
workerPort.postMessage(message);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//# sourceMappingURL=workerEntry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerEntry.cjs","names":["parentPort"],"sources":["../src/workerEntry.ts"],"sourcesContent":["import { parentPort } from \"node:worker_threads\";\n\nimport { attachQuickJsProtocolEndpoint } from \"@mcploom/codexec-quickjs/runner/protocol-endpoint\";\nimport type { DispatcherMessage, RunnerMessage } from \"@mcploom/codexec-protocol\";\n\nif (!parentPort) {\n throw new Error(\"WorkerExecutor requires a worker parent port\");\n}\n\nconst workerPort = parentPort;\n\nattachQuickJsProtocolEndpoint({\n onMessage(handler: (message: DispatcherMessage) => void): () => void {\n workerPort.on(\"message\", handler);\n return () => workerPort.off(\"message\", handler);\n },\n send(message: RunnerMessage): void {\n workerPort.postMessage(message);\n },\n});\n"],"mappings":";;;;AAKA,IAAI,CAACA,+BACH,OAAM,IAAI,MAAM,+CAA+C;AAGjE,MAAM,aAAaA;sFAEW;CAC5B,UAAU,SAA2D;AACnE,aAAW,GAAG,WAAW,QAAQ;AACjC,eAAa,WAAW,IAAI,WAAW,QAAQ;;CAEjD,KAAK,SAA8B;AACjC,aAAW,YAAY,QAAQ;;CAElC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { parentPort } from "node:worker_threads";
|
|
2
|
+
import { attachQuickJsProtocolEndpoint } from "@mcploom/codexec-quickjs/runner/protocol-endpoint";
|
|
3
|
+
|
|
4
|
+
//#region src/workerEntry.ts
|
|
5
|
+
if (!parentPort) throw new Error("WorkerExecutor requires a worker parent port");
|
|
6
|
+
const workerPort = parentPort;
|
|
7
|
+
attachQuickJsProtocolEndpoint({
|
|
8
|
+
onMessage(handler) {
|
|
9
|
+
workerPort.on("message", handler);
|
|
10
|
+
return () => workerPort.off("message", handler);
|
|
11
|
+
},
|
|
12
|
+
send(message) {
|
|
13
|
+
workerPort.postMessage(message);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { };
|
|
19
|
+
//# sourceMappingURL=workerEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerEntry.js","names":[],"sources":["../src/workerEntry.ts"],"sourcesContent":["import { parentPort } from \"node:worker_threads\";\n\nimport { attachQuickJsProtocolEndpoint } from \"@mcploom/codexec-quickjs/runner/protocol-endpoint\";\nimport type { DispatcherMessage, RunnerMessage } from \"@mcploom/codexec-protocol\";\n\nif (!parentPort) {\n throw new Error(\"WorkerExecutor requires a worker parent port\");\n}\n\nconst workerPort = parentPort;\n\nattachQuickJsProtocolEndpoint({\n onMessage(handler: (message: DispatcherMessage) => void): () => void {\n workerPort.on(\"message\", handler);\n return () => workerPort.off(\"message\", handler);\n },\n send(message: RunnerMessage): void {\n workerPort.postMessage(message);\n },\n});\n"],"mappings":";;;;AAKA,IAAI,CAAC,WACH,OAAM,IAAI,MAAM,+CAA+C;AAGjE,MAAM,aAAa;AAEnB,8BAA8B;CAC5B,UAAU,SAA2D;AACnE,aAAW,GAAG,WAAW,QAAQ;AACjC,eAAa,WAAW,IAAI,WAAW,QAAQ;;CAEjD,KAAK,SAA8B;AACjC,aAAW,YAAY,QAAQ;;CAElC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcploom/codexec-worker",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Worker thread executor for the mcploom codexec core package.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.cjs",
|
|
11
|
+
"module": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsdown"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"mcp",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"worker-threads",
|
|
33
|
+
"quickjs",
|
|
34
|
+
"code-execution"
|
|
35
|
+
],
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/aallam/mcploom.git",
|
|
39
|
+
"directory": "packages/codexec-worker"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/aallam/mcploom/tree/main/packages/codexec-worker#readme",
|
|
42
|
+
"bugs": "https://github.com/aallam/mcploom/issues",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@mcploom/codexec": "^0.3.0",
|
|
45
|
+
"@mcploom/codexec-protocol": "^0.1.1",
|
|
46
|
+
"@mcploom/codexec-quickjs": "^0.2.0"
|
|
47
|
+
}
|
|
48
|
+
}
|