History

Merge pull request #284 from RhysSullivan/worktree-publish-execution-cli

feat(release): publish execution, cli, and kernel packages to npm

Rhys Sullivan committed 6 months ago

Commit 599ae332 parents 96dc18a 905973b35 files · +772 −66Browse files

Unified · Split

35 files
  1. .changeset/config.json +17 −12
  2. apps/cli/src/main.ts +4 −1
  3. apps/cloud/package.json +1 −0
  4. apps/cloud/src/mcp-session.e2e.node.test.ts +2 −1
  5. apps/cloud/src/services/__test-harness__/api-harness.ts +2 −1
  6. apps/local/package.json +1 −0
  7. apps/local/src/server/main.ts +2 −1
  8. bun.lock +27 −9
  9. package.json +1 −1
  10. packages/core/cli/README.md +57 −0
  11. packages/core/cli/package.json +24 −3
  12. packages/core/cli/src/commands/generate.ts +1 −1
  13. packages/core/cli/src/generators/types.ts +1 −1
  14. packages/core/cli/src/utils/get-config.ts +1 −1
  15. packages/core/cli/tsup.config.ts +17 −0
  16. packages/core/execution/README.md +94 −0
  17. packages/core/execution/package.json +37 −4
  18. packages/core/execution/src/engine.ts +2 −4
  19. packages/core/execution/src/promise.ts +120 −0
  20. packages/core/execution/src/tool-invoker.test.ts +9 −6
  21. packages/core/execution/tsconfig.json +5 −1
  22. packages/core/execution/tsup.config.ts +13 −0
  23. packages/core/sdk/package.json +1 −0
  24. packages/core/storage-core/README.md +64 −0
  25. packages/core/storage-core/package.json +54 −5
  26. packages/core/storage-core/tsup.config.ts +14 −0
  27. packages/kernel/core/README.md +40 −0
  28. packages/kernel/core/package.json +28 −2
  29. packages/kernel/core/tsconfig.json +5 −1
  30. packages/kernel/core/tsup.config.ts +12 −0
  31. packages/kernel/runtime-quickjs/README.md +60 −0
  32. packages/kernel/runtime-quickjs/package.json +28 −2
  33. packages/kernel/runtime-quickjs/tsconfig.json +5 −1
  34. packages/kernel/runtime-quickjs/tsup.config.ts +12 −0
  35. scripts/publish-packages.ts +11 −8
.changeset/config.json+17−12
@@ -2,20 +2,33 @@ · 1 unmodified line
2 "$schema": "https://unpkg.com/@changesets/config@3.1.3/schema.json",2 "$schema": "https://unpkg.com/@changesets/config@3.1.3/schema.json",
3 "changelog": false,3 "changelog": false,
4 "commit": false,4 "commit": false,
5 "fixed": [],5 "fixed": [
6 [
7 "@executor/sdk",
8 "@executor/storage-core",
9 "@executor/codemode-core",
10 "@executor/runtime-quickjs",
11 "@executor/execution",
12 "@executor/cli",
13 "@executor/plugin-file-secrets",
14 "@executor/plugin-google-discovery",
15 "@executor/plugin-graphql",
16 "@executor/plugin-keychain",
17 "@executor/plugin-mcp",
18 "@executor/plugin-onepassword",
19 "@executor/plugin-openapi"
20 ]
21 ],
6 "linked": [],22 "linked": [],
7 "access": "public",23 "access": "public",
8 "baseBranch": "main",24 "baseBranch": "main",
9 "updateInternalDependencies": "patch",25 "updateInternalDependencies": "patch",
10 "ignore": [26 "ignore": [
11 "@executor/local",27 "@executor/local",
12 "@executor/execution",
13 "@executor/storage-file",28 "@executor/storage-file",
14 "@executor/host-mcp",29 "@executor/host-mcp",
15 "@executor/codemode-core",
16 "@executor/ir",30 "@executor/ir",
17 "@executor/runtime-deno-subprocess",31 "@executor/runtime-deno-subprocess",
18 "@executor/runtime-quickjs",
19 "@executor/marketing",32 "@executor/marketing",
20 "@executor/runtime-dynamic-worker",33 "@executor/runtime-dynamic-worker",
21 "@executor/runtime-secure-exec",34 "@executor/runtime-secure-exec",
@@ -23,14 +36,6 @@ · 1 unmodified line
23 "@executor/api",36 "@executor/api",
24 "@executor/config",37 "@executor/config",
25 "@executor/storage-postgres",38 "@executor/storage-postgres",
26 "@executor/sdk",
27 "@executor/plugin-file-secrets",
28 "@executor/plugin-google-discovery",
29 "@executor/plugin-graphql",
30 "@executor/plugin-keychain",
31 "@executor/plugin-mcp",
32 "@executor/plugin-onepassword",
33 "@executor/plugin-openapi",
34 "@executor/plugin-workos-vault",39 "@executor/plugin-workos-vault",
35 "@executor/example-promise-sdk"40 "@executor/example-promise-sdk"
36 ]41 ]
apps/cli/src/main.ts+4−1
@@ -41,6 +41,7 @@ import * as Cause from "effect/Cause"; · 40 unmodified lines
41 41
42import { ExecutorApi } from "@executor/api";42import { ExecutorApi } from "@executor/api";
43import { startServer, runMcpStdioServer, getExecutor } from "@executor/local";43import { startServer, runMcpStdioServer, getExecutor } from "@executor/local";
44import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
44 45
45// Embedded web UI — baked into compiled binaries via `with { type: "file" }`46// Embedded web UI — baked into compiled binaries via `with { type: "file" }`
46import embeddedWebUI from "./embedded-web-ui.gen";47import embeddedWebUI from "./embedded-web-ui.gen";
@@ -154,7 +155,9 @@ const runForegroundSession = (input: { · 107 unmodified lines
154const runStdioMcpSession = () =>155const runStdioMcpSession = () =>
155 Effect.gen(function* () {156 Effect.gen(function* () {
156 const executor = yield* Effect.promise(() => getExecutor());157 const executor = yield* Effect.promise(() => getExecutor());
157 yield* Effect.promise(() => runMcpStdioServer({ executor }));158 yield* Effect.promise(() =>
159 runMcpStdioServer({ executor, codeExecutor: makeQuickJsExecutor() }),
160 );
158 });161 });
159 162
160// ---------------------------------------------------------------------------163// ---------------------------------------------------------------------------
apps/cloud/package.json+1−0
@@ -38,6 +38,7 @@ · 37 unmodified lines
38 "@executor/plugin-workos-vault": "workspace:*",38 "@executor/plugin-workos-vault": "workspace:*",
39 "@executor/react": "workspace:*",39 "@executor/react": "workspace:*",
40 "@executor/runtime-dynamic-worker": "workspace:*",40 "@executor/runtime-dynamic-worker": "workspace:*",
41 "@executor/runtime-quickjs": "workspace:*",
41 "@executor/sdk": "workspace:*",42 "@executor/sdk": "workspace:*",
42 "@executor/storage-core": "workspace:*",43 "@executor/storage-core": "workspace:*",
43 "@executor/storage-postgres": "workspace:*",44 "@executor/storage-postgres": "workspace:*",
apps/cloud/src/mcp-session.e2e.node.test.ts+2−1
@@ -23,6 +23,7 @@ import type { ClientCapabilities } from "@modelcontextprotocol/sdk/types.js"; · 22 unmodified lines
23 23
24import { createExecutorMcpServer } from "@executor/host-mcp";24import { createExecutorMcpServer } from "@executor/host-mcp";
25import { createExecutionEngine } from "@executor/execution";25import { createExecutionEngine } from "@executor/execution";
26import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
26import {27import {
27 ElicitationResponse,28 ElicitationResponse,
28 FormElicitation,29 FormElicitation,
@@ -134,7 +135,7 @@ const openSession = ( · 105 unmodified lines
134 Effect.acquireRelease(135 Effect.acquireRelease(
135 Effect.gen(function* () {136 Effect.gen(function* () {
136 const executor = yield* buildScopedExecutor(orgId, `Org ${orgId}`, options);137 const executor = yield* buildScopedExecutor(orgId, `Org ${orgId}`, options);
137 const engine = createExecutionEngine({ executor });138 const engine = createExecutionEngine({ executor, codeExecutor: makeQuickJsExecutor() });
138 const mcpServer = yield* Effect.promise(() => createExecutorMcpServer({ engine }));139 const mcpServer = yield* Effect.promise(() => createExecutorMcpServer({ engine }));
139 const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();140 const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
140 const client = new Client(141 const client = new Client(
apps/cloud/src/services/__test-harness__/api-harness.ts+2−1
@@ -31,6 +31,7 @@ import { · 30 unmodified lines
31 ExecutorService,31 ExecutorService,
32} from "@executor/api/server";32} from "@executor/api/server";
33import { createExecutionEngine } from "@executor/execution";33import { createExecutionEngine } from "@executor/execution";
34import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
34import {35import {
35 Scope,36 Scope,
36 ScopeId,37 ScopeId,
@@ -215,7 +216,7 @@ const TestApiLive = HttpApiBuilder.api(ProtectedCloudApi).pipe( · 178 unmodified lines
215const buildAppForScope = (scopeId: string, scopeName: string) =>216const buildAppForScope = (scopeId: string, scopeName: string) =>
216 Effect.gen(function* () {217 Effect.gen(function* () {
217 const executor = yield* createTestScopedExecutor(scopeId, scopeName);218 const executor = yield* createTestScopedExecutor(scopeId, scopeName);
218 const engine = createExecutionEngine({ executor });219 const engine = createExecutionEngine({ executor, codeExecutor: makeQuickJsExecutor() });
219 const services = Layer.mergeAll(220 const services = Layer.mergeAll(
220 Layer.succeed(ExecutorService, executor),221 Layer.succeed(ExecutorService, executor),
221 Layer.succeed(ExecutionEngineService, engine),222 Layer.succeed(ExecutionEngineService, engine),
apps/local/package.json+1−0
@@ -33,6 +33,7 @@ · 32 unmodified lines
33 "@executor/plugin-onepassword": "workspace:*",33 "@executor/plugin-onepassword": "workspace:*",
34 "@executor/plugin-openapi": "workspace:*",34 "@executor/plugin-openapi": "workspace:*",
35 "@executor/react": "workspace:*",35 "@executor/react": "workspace:*",
36 "@executor/runtime-quickjs": "workspace:*",
36 "@executor/sdk": "workspace:*",37 "@executor/sdk": "workspace:*",
37 "@executor/storage-file": "workspace:*",38 "@executor/storage-file": "workspace:*",
38 "@modelcontextprotocol/sdk": "^1.12.1",39 "@modelcontextprotocol/sdk": "^1.12.1",
apps/local/src/server/main.ts+2−1
@@ -10,6 +10,7 @@ import { Context, Effect, Layer, ManagedRuntime } from "effect"; · 9 unmodified lines
10import { addGroup } from "@executor/api";10import { addGroup } from "@executor/api";
11import { CoreHandlers, ExecutorService, ExecutionEngineService } from "@executor/api/server";11import { CoreHandlers, ExecutorService, ExecutionEngineService } from "@executor/api/server";
12import { createExecutionEngine } from "@executor/execution";12import { createExecutionEngine } from "@executor/execution";
13import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
13import {14import {
14 OpenApiGroup,15 OpenApiGroup,
15 OpenApiHandlers,16 OpenApiHandlers,
@@ -78,7 +79,7 @@ const closeServerHandlers = async (handlers: ServerHandlers): Promise<void> => { · 62 unmodified lines
78 79
79export const createServerHandlers = async (): Promise<ServerHandlers> => {80export const createServerHandlers = async (): Promise<ServerHandlers> => {
80 const executor = await getExecutor();81 const executor = await getExecutor();
81 const engine = createExecutionEngine({ executor });82 const engine = createExecutionEngine({ executor, codeExecutor: makeQuickJsExecutor() });
82 83
83 const pluginExtensions = Layer.mergeAll(84 const pluginExtensions = Layer.mergeAll(
84 Layer.succeed(OpenApiExtensionService, executor.openapi),85 Layer.succeed(OpenApiExtensionService, executor.openapi),
bun.lock+27−9
@@ -61,6 +61,7 @@ · 60 unmodified lines
61 "@executor/plugin-workos-vault": "workspace:*",61 "@executor/plugin-workos-vault": "workspace:*",
62 "@executor/react": "workspace:*",62 "@executor/react": "workspace:*",
63 "@executor/runtime-dynamic-worker": "workspace:*",63 "@executor/runtime-dynamic-worker": "workspace:*",
64 "@executor/runtime-quickjs": "workspace:*",
64 "@executor/sdk": "workspace:*",65 "@executor/sdk": "workspace:*",
65 "@executor/storage-core": "workspace:*",66 "@executor/storage-core": "workspace:*",
66 "@executor/storage-postgres": "workspace:*",67 "@executor/storage-postgres": "workspace:*",
@@ -139,6 +140,7 @@ · 72 unmodified lines
139 "@executor/plugin-onepassword": "workspace:*",140 "@executor/plugin-onepassword": "workspace:*",
140 "@executor/plugin-openapi": "workspace:*",141 "@executor/plugin-openapi": "workspace:*",
141 "@executor/react": "workspace:*",142 "@executor/react": "workspace:*",
143 "@executor/runtime-quickjs": "workspace:*",
142 "@executor/sdk": "workspace:*",144 "@executor/sdk": "workspace:*",
143 "@executor/storage-file": "workspace:*",145 "@executor/storage-file": "workspace:*",
144 "@modelcontextprotocol/sdk": "^1.12.1",146 "@modelcontextprotocol/sdk": "^1.12.1",
@@ -242,12 +244,13 @@ · 97 unmodified lines
242 "executor": "./dist/index.js",244 "executor": "./dist/index.js",
243 },245 },
244 "dependencies": {246 "dependencies": {
245 "@executor/sdk": "workspace:*",
246 "@executor/storage-core": "workspace:*",
247 "commander": "^12.1.0",247 "commander": "^12.1.0",
248 "effect": "catalog:",
248 "jiti": "^2.6.1",249 "jiti": "^2.6.1",
249 },250 },
250 "devDependencies": {251 "devDependencies": {
252 "@executor/sdk": "workspace:*",
253 "@executor/storage-core": "workspace:*",
251 "@types/node": "catalog:",254 "@types/node": "catalog:",
252 "tsup": "catalog:",255 "tsup": "catalog:",
253 "typescript": "catalog:",256 "typescript": "catalog:",
@@ -284,17 +287,19 @@ · 30 unmodified lines
284 },287 },
285 "packages/core/execution": {288 "packages/core/execution": {
286 "name": "@executor/execution",289 "name": "@executor/execution",
287 "version": "1.4.3",290 "version": "0.0.1",
288 "dependencies": {291 "dependencies": {
289 "@executor/codemode-core": "workspace:*",292 "@executor/codemode-core": "workspace:*",
290 "@executor/runtime-quickjs": "workspace:*",
291 "@executor/sdk": "workspace:*",293 "@executor/sdk": "workspace:*",
292 "effect": "catalog:",294 "effect": "catalog:",
293 },295 },
294 "devDependencies": {296 "devDependencies": {
295 "@effect/vitest": "catalog:",297 "@effect/vitest": "catalog:",
298 "@executor/runtime-quickjs": "workspace:*",
296 "@types/node": "catalog:",299 "@types/node": "catalog:",
297 "bun-types": "catalog:",300 "bun-types": "catalog:",
301 "tsup": "catalog:",
302 "typescript": "catalog:",
298 "vitest": "catalog:",303 "vitest": "catalog:",
299 },304 },
300 },305 },
@@ -315,16 +320,25 @@ · 14 unmodified lines
315 },320 },
316 "packages/core/storage-core": {321 "packages/core/storage-core": {
317 "name": "@executor/storage-core",322 "name": "@executor/storage-core",
318 "version": "0.0.0",323 "version": "0.0.1",
319 "dependencies": {324 "dependencies": {
320 "@effect/vitest": "catalog:",
321 "@standard-schema/spec": "^1.0.0",325 "@standard-schema/spec": "^1.0.0",
322 "effect": "catalog:",326 "effect": "catalog:",
323 "vitest": "catalog:",
324 },327 },
325 "devDependencies": {328 "devDependencies": {
329 "@effect/vitest": "catalog:",
330 "tsup": "catalog:",
326 "typescript": "catalog:",331 "typescript": "catalog:",
332 "vitest": "catalog:",
327 },333 },
334 "peerDependencies": {
335 "@effect/vitest": "catalog:",
336 "vitest": "catalog:",
337 },
338 "optionalPeers": [
339 "@effect/vitest",
340 "vitest",
341 ],
328 },342 },
329 "packages/core/storage-drizzle": {343 "packages/core/storage-drizzle": {
330 "name": "@executor/storage-drizzle",344 "name": "@executor/storage-drizzle",
@@ -397,7 +411,7 @@ · 66 unmodified lines
397 },411 },
398 "packages/kernel/core": {412 "packages/kernel/core": {
399 "name": "@executor/codemode-core",413 "name": "@executor/codemode-core",
400 "version": "1.4.3",414 "version": "0.0.1",
401 "dependencies": {415 "dependencies": {
402 "@babel/parser": "^7.29.2",416 "@babel/parser": "^7.29.2",
403 "@standard-schema/spec": "^1.0.0",417 "@standard-schema/spec": "^1.0.0",
@@ -408,6 +422,8 @@ · 4 unmodified lines
408 "devDependencies": {422 "devDependencies": {
409 "@types/node": "catalog:",423 "@types/node": "catalog:",
410 "bun-types": "catalog:",424 "bun-types": "catalog:",
425 "tsup": "catalog:",
426 "typescript": "catalog:",
411 "vitest": "catalog:",427 "vitest": "catalog:",
412 },428 },
413 },429 },
@@ -455,7 +471,7 @@ · 41 unmodified lines
455 },471 },
456 "packages/kernel/runtime-quickjs": {472 "packages/kernel/runtime-quickjs": {
457 "name": "@executor/runtime-quickjs",473 "name": "@executor/runtime-quickjs",
458 "version": "1.4.3",474 "version": "0.0.1",
459 "dependencies": {475 "dependencies": {
460 "@executor/codemode-core": "workspace:*",476 "@executor/codemode-core": "workspace:*",
461 "effect": "catalog:",477 "effect": "catalog:",
@@ -465,6 +481,8 @@ · 3 unmodified lines
465 "@effect/vitest": "catalog:",481 "@effect/vitest": "catalog:",
466 "@types/node": "catalog:",482 "@types/node": "catalog:",
467 "bun-types": "catalog:",483 "bun-types": "catalog:",
484 "tsup": "catalog:",
485 "typescript": "catalog:",
468 "vitest": "catalog:",486 "vitest": "catalog:",
469 },487 },
470 },488 },
package.json+1−1
@@ -33,7 +33,7 @@ · 32 unmodified lines
33 "dev:cli": "bun run apps/cli/src/main.ts",33 "dev:cli": "bun run apps/cli/src/main.ts",
34 "test": "turbo run test",34 "test": "turbo run test",
35 "test:release:bootstrap": "vitest run tests/release-bootstrap-smoke.test.ts",35 "test:release:bootstrap": "vitest run tests/release-bootstrap-smoke.test.ts",
36 "build:packages": "bun run --filter='@executor/sdk' build && bun run --filter='@executor/plugin-*' build",36 "build:packages": "bun run --filter='@executor/storage-core' build && bun run --filter='@executor/codemode-core' build && bun run --filter='@executor/runtime-quickjs' build && bun run --filter='@executor/sdk' build && bun run --filter='@executor/execution' build && bun run --filter='@executor/cli' build && bun run --filter='@executor/plugin-*' build",
37 "typecheck": "turbo run typecheck",37 "typecheck": "turbo run typecheck",
38 "typecheck:slow": "turbo run typecheck:slow",38 "typecheck:slow": "turbo run typecheck:slow",
39 "ci": "bun run lint && bun run typecheck && bun run test",39 "ci": "bun run lint && bun run typecheck && bun run test",
packages/core/cli/README.md+57−0
@@ -0,0 +1,57 @@
1# @executor/cli
2
3Command-line tool for `@executor/sdk` projects. Generates Drizzle schema files from the plugins registered in your `executor.config.ts` so database migrations stay in sync with the executor you actually run.
4
5## Install
6
7```sh
8bun add -d @executor/cli
9# or
10npm install --save-dev @executor/cli
11```
12
13The binary is installed as `executor`.
14
15## Quick start
16
17Create an `executor.config.ts` alongside your app code:
18
19```ts
20import { defineExecutorConfig } from "@executor/sdk";
21import { mcpPlugin } from "@executor/plugin-mcp";
22import { openApiPlugin } from "@executor/plugin-openapi";
23
24export default defineExecutorConfig({
25 dialect: "postgres",
26 plugins: [mcpPlugin(), openApiPlugin()],
27});
28```
29
30Then generate a Drizzle schema from it:
31
32```sh
33bunx executor generate --output ./src/db/executor-schema.ts
34# or
35npx executor generate --output ./src/db/executor-schema.ts
36```
37
38The generator walks every plugin in the config, collects their schema contributions, and emits a single Drizzle schema file ready to hand to `drizzle-kit`.
39
40## Commands
41
42```
43executor generate [options]
44 --cwd <dir> Project directory (default: cwd)
45 --config <path> Path to executor.config.ts (default: auto-discover)
46 --output <path> Output file for the generated schema
47```
48
49Run `executor --help` to see the current command list.
50
51## Status
52
53Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor).
54
55## License
56
57MIT
packages/core/cli/package.json+24−3
@@ -2,7 +2,16 @@ · 1 unmodified line
2 "name": "@executor/cli",2 "name": "@executor/cli",
3 "version": "0.0.1",3 "version": "0.0.1",
4 "description": "CLI for the executor SDK — schema generation, migrations",4 "description": "CLI for the executor SDK — schema generation, migrations",
5 "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/core/cli",
6 "bugs": {
7 "url": "https://github.com/RhysSullivan/executor/issues"
8 },
5 "license": "MIT",9 "license": "MIT",
10 "repository": {
11 "type": "git",
12 "url": "git+https://github.com/RhysSullivan/executor.git",
13 "directory": "packages/core/cli"
14 },
6 "type": "module",15 "type": "module",
7 "bin": {16 "bin": {
8 "executor": "./dist/index.js"17 "executor": "./dist/index.js"
@@ -13,19 +22,31 @@ · 4 unmodified lines
13 "exports": {22 "exports": {
14 ".": "./src/index.ts"23 ".": "./src/index.ts"
15 },24 },
25 "publishConfig": {
26 "access": "public",
27 "exports": {
28 ".": {
29 "import": {
30 "types": "./dist/index.d.ts",
31 "default": "./dist/index.js"
32 }
33 }
34 }
35 },
16 "scripts": {36 "scripts": {
17 "build": "tsup src/index.ts --format esm --dts",37 "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
18 "dev": "node --import jiti/register src/index.ts",38 "dev": "node --import jiti/register src/index.ts",
19 "test": "vitest run",39 "test": "vitest run",
20 "typecheck": "tsc --noEmit"40 "typecheck": "tsc --noEmit"
21 },41 },
22 "dependencies": {42 "dependencies": {
23 "@executor/sdk": "workspace:*",
24 "@executor/storage-core": "workspace:*",
25 "commander": "^12.1.0",43 "commander": "^12.1.0",
44 "effect": "catalog:",
26 "jiti": "^2.6.1"45 "jiti": "^2.6.1"
27 },46 },
28 "devDependencies": {47 "devDependencies": {
48 "@executor/sdk": "workspace:*",
49 "@executor/storage-core": "workspace:*",
29 "@types/node": "catalog:",50 "@types/node": "catalog:",
30 "tsup": "catalog:",51 "tsup": "catalog:",
31 "typescript": "catalog:",52 "typescript": "catalog:",
packages/core/cli/src/commands/generate.ts+1−1
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs"; · 1 unmodified line
2import fs from "node:fs/promises";2import fs from "node:fs/promises";
3import path from "node:path";3import path from "node:path";
4import { Command } from "commander";4import { Command } from "commander";
5import { collectSchemas } from "@executor/sdk";5import { collectSchemas } from "@executor/sdk/core";
6import { getConfig } from "../utils/get-config.js";6import { getConfig } from "../utils/get-config.js";
7import { generateDrizzleSchema } from "../generators/drizzle.js";7import { generateDrizzleSchema } from "../generators/drizzle.js";
8 8
packages/core/cli/src/generators/types.ts+1−1
@@ -1,5 +1,5 @@
1import type { DBSchema } from "@executor/storage-core";1import type { DBSchema } from "@executor/storage-core";
2import type { ExecutorDialect } from "@executor/sdk";2import type { ExecutorDialect } from "@executor/sdk/core";
3 3
4export interface SchemaGeneratorResult {4export interface SchemaGeneratorResult {
5 code?: string;5 code?: string;
packages/core/cli/src/utils/get-config.ts+1−1
@@ -1,7 +1,7 @@
1import { existsSync } from "node:fs";1import { existsSync } from "node:fs";
2import path from "node:path";2import path from "node:path";
3import { createJiti } from "jiti";3import { createJiti } from "jiti";
4import type { ExecutorCliConfig } from "@executor/sdk";4import type { ExecutorCliConfig } from "@executor/sdk/core";
5 5
6const defaultPaths = [6const defaultPaths = [
7 "executor.config.ts",7 "executor.config.ts",
packages/core/cli/tsup.config.ts+17−0
@@ -0,0 +1,17 @@
1import { defineConfig } from "tsup";
2
3export default defineConfig({
4 entry: {
5 index: "src/index.ts",
6 },
7 format: ["esm"],
8 dts: false,
9 sourcemap: true,
10 clean: true,
11 external: [
12 /^effect/,
13 /^@effect\//,
14 "commander",
15 "jiti",
16 ],
17});
packages/core/execution/README.md+94−0
@@ -0,0 +1,94 @@
1# @executor/execution
2
3Sandboxed JavaScript execution for an executor. Hands a `tools.<namespace>.<name>(...)` proxy into a code sandbox so an agent (or any caller) can run generated TypeScript/JavaScript that invokes the executor's registered tools.
4
5Supports pause/resume for elicitation-driven flows: tools that need user input (OAuth, form fill, approval) suspend the sandbox, surface a `PausedExecution`, and resume on a `ResumeResponse`.
6
7## Install
8
9```sh
10bun add @executor/sdk @executor/execution @executor/runtime-quickjs
11# or
12npm install @executor/sdk @executor/execution @executor/runtime-quickjs
13```
14
15`@executor/runtime-quickjs` is the sandbox runtime. It's not a dependency of `@executor/execution` — you bring your own so consumers with a different runtime don't ship ~13 MB of WASM they never use.
16
17## Usage
18
19```ts
20import { createExecutor } from "@executor/sdk";
21import { createExecutionEngine } from "@executor/execution";
22import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
23
24const executor = await createExecutor({
25 scope: { name: "my-app" },
26});
27
28const engine = createExecutionEngine({
29 executor,
30 codeExecutor: makeQuickJsExecutor({
31 timeoutMs: 2_000,
32 memoryLimitBytes: 32 * 1024 * 1024,
33 }),
34});
35
36const result = await engine.execute(
37 `
38 const pets = await tools.petstore.findPetsByStatus({ status: "available" });
39 return pets.length;
40 `,
41 {
42 onElicitation: async (ctx) => {
43 // A tool asked for user input mid-execution. Your UI decides what to do.
44 console.log("tool needs input:", ctx.request);
45 return { action: "decline" };
46 },
47 },
48);
49
50console.log(result);
51// { result: 12, logs: [...] }
52```
53
54## Pause/resume for elicitation
55
56When the host doesn't support inline elicitation, use `executeWithPause` to intercept the first request as a pause point:
57
58```ts
59const started = await engine.executeWithPause(code);
60
61if (started.status === "paused") {
62 const { id, elicitationContext } = started.execution;
63 // Render the elicitation request in your UI. Later:
64 const resumed = await engine.resume(id, {
65 action: "accept",
66 content: { name: "Ada" },
67 });
68}
69```
70
71## Workflow description for LLMs
72
73```ts
74const docs = await engine.getDescription();
75// Returns the canonical "use tools.search(), then tools.describe.tool(), then call …"
76// workflow prose + per-namespace tool listing. Feed this to an LLM so it knows
77// how to drive the sandbox.
78```
79
80## Using with Effect
81
82If you're building on `@executor/sdk` (the raw Effect entry), import from the `/core` subpath. The returned engine is Effect-native: `execute`, `executeWithPause`, and `resume` all become `Effect.Effect<...>`, and `onElicitation` is an `ElicitationHandler` returning `Effect.Effect<ElicitationResponse>`.
83
84```ts
85import { createExecutionEngine } from "@executor/execution";
86```
87
88## Status
89
90Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor).
91
92## License
93
94MIT
packages/core/execution/package.json+37−4
@@ -1,26 +1,59 @@
1{1{
2 "name": "@executor/execution",2 "name": "@executor/execution",
3 "version": "1.4.3",3 "version": "0.0.1",
4 "private": true,4 "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/core/execution",
5 "bugs": {
6 "url": "https://github.com/RhysSullivan/executor/issues"
7 },
8 "license": "MIT",
9 "repository": {
10 "type": "git",
11 "url": "git+https://github.com/RhysSullivan/executor.git",
12 "directory": "packages/core/execution"
13 },
14 "files": [
15 "dist"
16 ],
5 "type": "module",17 "type": "module",
6 "exports": {18 "exports": {
7 ".": "./src/index.ts"19 ".": "./src/index.ts",
20 "./promise": "./src/promise.ts"
21 },
22 "publishConfig": {
23 "access": "public",
24 "exports": {
25 ".": {
26 "import": {
27 "types": "./dist/promise.d.ts",
28 "default": "./dist/index.js"
29 }
30 },
31 "./core": {
32 "import": {
33 "types": "./dist/index.d.ts",
34 "default": "./dist/core.js"
35 }
36 }
37 }
8 },38 },
9 "scripts": {39 "scripts": {
40 "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
10 "typecheck": "tsgo --noEmit",41 "typecheck": "tsgo --noEmit",
11 "test": "vitest run",42 "test": "vitest run",
12 "typecheck:slow": "bunx tsc --noEmit -p tsconfig.json"43 "typecheck:slow": "bunx tsc --noEmit -p tsconfig.json"
13 },44 },
14 "dependencies": {45 "dependencies": {
15 "@executor/codemode-core": "workspace:*",46 "@executor/codemode-core": "workspace:*",
16 "@executor/runtime-quickjs": "workspace:*",
17 "@executor/sdk": "workspace:*",47 "@executor/sdk": "workspace:*",
18 "effect": "catalog:"48 "effect": "catalog:"
19 },49 },
20 "devDependencies": {50 "devDependencies": {
21 "@effect/vitest": "catalog:",51 "@effect/vitest": "catalog:",
52 "@executor/runtime-quickjs": "workspace:*",
22 "@types/node": "catalog:",53 "@types/node": "catalog:",
23 "bun-types": "catalog:",54 "bun-types": "catalog:",
55 "tsup": "catalog:",
56 "typescript": "catalog:",
24 "vitest": "catalog:"57 "vitest": "catalog:"
25 }58 }
26}59}
packages/core/execution/src/engine.ts+2−4
@@ -8,7 +8,6 @@ import type { · 7 unmodified lines
8 ElicitationContext,8 ElicitationContext,
9} from "@executor/sdk";9} from "@executor/sdk";
10import type { CodeExecutor, ExecuteResult, SandboxToolInvoker } from "@executor/codemode-core";10import type { CodeExecutor, ExecuteResult, SandboxToolInvoker } from "@executor/codemode-core";
11import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
12 11
13import {12import {
14 makeExecutorToolInvoker,13 makeExecutorToolInvoker,
@@ -25,7 +24,7 @@ import { buildExecuteDescription } from "./description"; · 10 unmodified lines
25 24
26export type ExecutionEngineConfig = {25export type ExecutionEngineConfig = {
27 readonly executor: Executor;26 readonly executor: Executor;
28 readonly codeExecutor?: CodeExecutor;27 readonly codeExecutor: CodeExecutor;
29};28};
30 29
31export type ExecutionResult =30export type ExecutionResult =
@@ -292,8 +291,7 @@ const runEffect = <A>(effect: Effect.Effect<A, unknown>): Promise<A> => · 260 unmodified lines
292 Effect.runPromise(effect as Effect.Effect<A, never>);291 Effect.runPromise(effect as Effect.Effect<A, never>);
293 292
294export const createExecutionEngine = (config: ExecutionEngineConfig): ExecutionEngine => {293export const createExecutionEngine = (config: ExecutionEngineConfig): ExecutionEngine => {
295 const { executor } = config;294 const { executor, codeExecutor } = config;
296 const codeExecutor = config.codeExecutor ?? makeQuickJsExecutor();
297 const pausedExecutions = new Map<string, InternalPausedExecution>();295 const pausedExecutions = new Map<string, InternalPausedExecution>();
298 let nextId = 0;296 let nextId = 0;
299 297
packages/core/execution/src/promise.ts+120−0
@@ -0,0 +1,120 @@
1// ---------------------------------------------------------------------------
2// @executor/execution/promise — Promise-native surface for the execution
3// engine. Accepts a Promise-style Executor (from @executor/sdk) and an
4// async `onElicitation` handler — no Effect imports required by callers.
5//
6// Under the hood the engine is Effect-based, so we wrap the incoming
7// Promise executor into the minimal Effect shape the engine consumes,
8// and bridge the elicitation handler via `Effect.tryPromise`.
9// ---------------------------------------------------------------------------
10
11import { Effect } from "effect";
12
13import type {
14 ElicitationContext,
15 ElicitationResponse,
16 Executor as PromiseExecutor,
17} from "@executor/sdk";
18import type { CodeExecutor, ExecuteResult } from "@executor/codemode-core";
19
20import {
21 createExecutionEngine as createEffectExecutionEngine,
22 type ExecutionResult,
23 type PausedExecution,
24 type ResumeResponse,
25} from "./engine";
26
27export type ElicitationHandler = (
28 ctx: ElicitationContext,
29) => Promise<ElicitationResponse>;
30
31export type ExecutionEngineConfig = {
32 readonly executor: PromiseExecutor;
33 readonly codeExecutor: CodeExecutor;
34};
35
36export type ExecutionEngine = {
37 readonly execute: (
38 code: string,
39 options: { readonly onElicitation: ElicitationHandler },
40 ) => Promise<ExecuteResult>;
41 readonly executeWithPause: (code: string) => Promise<ExecutionResult>;
42 readonly resume: (
43 executionId: string,
44 response: ResumeResponse,
45 ) => Promise<ExecutionResult | null>;
46 readonly getDescription: () => Promise<string>;
47};
48
49/**
50 * Wrap a Promise-style executor into the Effect shape the engine consumes.
51 * Only the four method families the engine actually touches need wrapping:
52 * `tools.{invoke,list,schema}` and `sources.list`.
53 */
54const wrapPromiseExecutor = (pe: PromiseExecutor): any => ({
55 scope: (pe as any).scope,
56 tools: {
57 invoke: (id: unknown, args: unknown, options: unknown) =>
58 Effect.tryPromise({
59 try: () => (pe.tools as any).invoke(id, args, options),
60 catch: (cause) => cause,
61 }),
62 list: (filter?: unknown) =>
63 Effect.tryPromise({
64 try: () => (pe.tools as any).list(filter),
65 catch: (cause) => cause,
66 }).pipe(Effect.orDie),
67 schema: (id: unknown) =>
68 Effect.tryPromise({
69 try: () => (pe.tools as any).schema(id),
70 catch: (cause) => cause,
71 }),
72 definitions: () =>
73 Effect.tryPromise({
74 try: () => (pe.tools as any).definitions(),
75 catch: (cause) => cause,
76 }).pipe(Effect.orDie),
77 },
78 sources: {
79 list: () =>
80 Effect.tryPromise({
81 try: () => (pe.sources as any).list(),
82 catch: (cause) => cause,
83 }).pipe(Effect.orDie),
84 },
85});
86
87export const createExecutionEngine = (
88 config: ExecutionEngineConfig,
89): ExecutionEngine => {
90 const engine = createEffectExecutionEngine({
91 executor: wrapPromiseExecutor(config.executor),
92 codeExecutor: config.codeExecutor,
93 });
94 return {
95 execute: (code, options) =>
96 engine.execute(code, {
97 onElicitation: (ctx) =>
98 Effect.tryPromise(() => options.onElicitation(ctx)).pipe(
99 Effect.orDie,
100 ),
101 }),
102 executeWithPause: (code) => engine.executeWithPause(code),
103 resume: (executionId, response) => engine.resume(executionId, response),
104 getDescription: () => engine.getDescription(),
105 };
106};
107
108// ---------------------------------------------------------------------------
109// Re-exports — plain types/helpers that don't carry Effect signatures.
110// ---------------------------------------------------------------------------
111
112export {
113 formatExecuteResult,
114 formatPausedExecution,
115} from "./engine";
116
117export type { ExecutionResult, PausedExecution, ResumeResponse };
118
119export { buildExecuteDescription } from "./description";
120export { ExecutionToolError } from "./errors";
packages/core/execution/src/tool-invoker.test.ts+9−6
@@ -8,9 +8,12 @@ import { · 7 unmodified lines
8 definePlugin,8 definePlugin,
9 makeTestConfig,9 makeTestConfig,
10} from "@executor/sdk";10} from "@executor/sdk";
11import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
11import { createExecutionEngine } from "./engine";12import { createExecutionEngine } from "./engine";
12import { describeTool, searchTools } from "./tool-invoker";13import { describeTool, searchTools } from "./tool-invoker";
13 14
15const codeExecutor = makeQuickJsExecutor();
16
14const RepoInputSchema = {17const RepoInputSchema = {
15 type: "object",18 type: "object",
16 properties: {19 properties: {
@@ -148,7 +151,7 @@ describe("tool discovery", () => { · 131 unmodified lines
148 expect(crmOnly.map((match) => match.path)).toEqual(["crm.listContacts"]);151 expect(crmOnly.map((match) => match.path)).toEqual(["crm.listContacts"]);
149 152
150 const sandboxResult = yield* Effect.promise(() =>153 const sandboxResult = yield* Effect.promise(() =>
151 createExecutionEngine({ executor }).execute(154 createExecutionEngine({ executor, codeExecutor }).execute(
152 'return await tools.search({ namespace: "crm", query: "create contact", limit: 5 });',155 'return await tools.search({ namespace: "crm", query: "create contact", limit: 5 });',
153 { onElicitation: acceptAll },156 { onElicitation: acceptAll },
154 ),157 ),
@@ -165,7 +168,7 @@ describe("tool discovery", () => { · 10 unmodified lines
165 const executor = yield* makeSearchExecutor();168 const executor = yield* makeSearchExecutor();
166 169
167 const listed = yield* Effect.promise(() =>170 const listed = yield* Effect.promise(() =>
168 createExecutionEngine({ executor }).execute("return await tools.executor.sources.list();", {171 createExecutionEngine({ executor, codeExecutor }).execute("return await tools.executor.sources.list();", {
169 onElicitation: acceptAll,172 onElicitation: acceptAll,
170 }),173 }),
171 );174 );
@@ -178,7 +181,7 @@ describe("tool discovery", () => { · 6 unmodified lines
178 );181 );
179 182
180 const searched = yield* Effect.promise(() =>183 const searched = yield* Effect.promise(() =>
181 createExecutionEngine({ executor }).execute(184 createExecutionEngine({ executor, codeExecutor }).execute(
182 'return await tools.search({ query: "list contacts", namespace: "crm", limit: 5 });',185 'return await tools.search({ query: "list contacts", namespace: "crm", limit: 5 });',
183 { onElicitation: acceptAll },186 { onElicitation: acceptAll },
184 ),187 ),
@@ -205,7 +208,7 @@ describe("tool discovery", () => { · 20 unmodified lines
205 it.effect("rejects malformed discover calls inside the sandbox", () =>208 it.effect("rejects malformed discover calls inside the sandbox", () =>
206 Effect.gen(function* () {209 Effect.gen(function* () {
207 const executor = yield* makeSearchExecutor();210 const executor = yield* makeSearchExecutor();
208 const engine = createExecutionEngine({ executor });211 const engine = createExecutionEngine({ executor, codeExecutor });
209 212
210 const invalid = yield* Effect.promise(() =>213 const invalid = yield* Effect.promise(() =>
211 engine.execute(214 engine.execute(
@@ -327,7 +330,7 @@ describe("pause/resume with multiple elicitations", () => { · 115 unmodified lines
327 () =>330 () =>
328 Effect.gen(function* () {331 Effect.gen(function* () {
329 const executor = yield* makeElicitingExecutor();332 const executor = yield* makeElicitingExecutor();
330 const engine = createExecutionEngine({ executor });333 const engine = createExecutionEngine({ executor, codeExecutor });
331 334
332 const code = "return await tools.api.multiApproval({});";335 const code = "return await tools.api.multiApproval({});";
333 336
@@ -361,7 +364,7 @@ describe("pause/resume with multiple elicitations", () => { · 27 unmodified lines
361 // sandbox fiber.364 // sandbox fiber.
362 it("resume returns across separate runPromise boundaries for a single-elicit tool (HTTP-like)", async () => {365 it("resume returns across separate runPromise boundaries for a single-elicit tool (HTTP-like)", async () => {
363 const executor = await Effect.runPromise(makeElicitingExecutor());366 const executor = await Effect.runPromise(makeElicitingExecutor());
364 const engine = createExecutionEngine({ executor });367 const engine = createExecutionEngine({ executor, codeExecutor });
365 368
366 const code = "return await tools.api.singleApproval({});";369 const code = "return await tools.api.singleApproval({});";
367 370
packages/core/execution/tsconfig.json+5−1
@@ -5,7 +5,11 @@ · 4 unmodified lines
5 "moduleResolution": "Bundler",5 "moduleResolution": "Bundler",
6 "strict": true,6 "strict": true,
7 "skipLibCheck": true,7 "skipLibCheck": true,
8 "noEmit": true,8 "outDir": "dist",
9 "rootDir": "src",
10 "declaration": true,
11 "declarationMap": true,
12 "sourceMap": true,
9 "lib": ["ES2022"],13 "lib": ["ES2022"],
10 "types": ["bun-types", "node"],14 "types": ["bun-types", "node"],
11 "noUnusedLocals": true,15 "noUnusedLocals": true,
packages/core/execution/tsup.config.ts+13−0
@@ -0,0 +1,13 @@
1import { defineConfig } from "tsup";
2
3export default defineConfig({
4 entry: {
5 index: "src/promise.ts",
6 core: "src/index.ts",
7 },
8 format: ["esm"],
9 dts: false,
10 sourcemap: true,
11 clean: true,
12 external: [/^@executor\//, /^effect/, /^@effect\//],
13});
packages/core/sdk/package.json+1−0
@@ -17,6 +17,7 @@ · 16 unmodified lines
17 "type": "module",17 "type": "module",
18 "exports": {18 "exports": {
19 ".": "./src/index.ts",19 ".": "./src/index.ts",
20 "./core": "./src/index.ts",
20 "./promise": "./src/promise.ts"21 "./promise": "./src/promise.ts"
21 },22 },
22 "publishConfig": {23 "publishConfig": {
packages/core/storage-core/README.md+64−0
@@ -0,0 +1,64 @@
1# @executor/storage-core
2
3Storage adapter interface for the executor. Defines the shared `DBAdapter`, `DBSchema`, and query-operator types every persistence backend implements, plus helpers for typed queries and an in-memory conformance test suite.
4
5Most callers don't depend on this directly — `@executor/sdk` re-exports the public surface. Install this when you're authoring a new storage adapter and want to conform to the contract.
6
7## Install
8
9```sh
10bun add @executor/storage-core
11# or
12npm install @executor/storage-core
13```
14
15## Usage
16
17Implement an adapter:
18
19```ts
20import { createAdapter, type DBAdapter } from "@executor/storage-core";
21
22const myAdapter: DBAdapter = createAdapter({
23 // ... your backend's query / mutation hooks
24});
25```
26
27Or grab typed query helpers for an existing schema:
28
29```ts
30import { typedAdapter, type DBSchema } from "@executor/storage-core";
31
32const schema = {
33 secrets: {
34 fields: {
35 id: { type: "string", required: true },
36 value: { type: "string", required: true },
37 },
38 },
39} satisfies DBSchema;
40
41const db = typedAdapter(myAdapter, schema);
42```
43
44## Conformance tests
45
46If you're building an adapter, use the shared conformance suite to verify your backend matches the contract:
47
48```ts
49import { describeDbAdapterConformance } from "@executor/storage-core/testing";
50
51describeDbAdapterConformance({
52 makeAdapter: async () => myAdapter,
53});
54```
55
56The suite runs against any `describe` from your test runner. `vitest` and `@effect/vitest` are declared as optional peer dependencies — install whichever you use.
57
58## Status
59
60Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor).
61
62## License
63
64MIT
packages/core/storage-core/package.json+54−5
@@ -1,25 +1,74 @@
1{1{
2 "name": "@executor/storage-core",2 "name": "@executor/storage-core",
3 "version": "0.0.0",3 "version": "0.0.1",
4 "private": true,4 "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/core/storage-core",
5 "bugs": {
6 "url": "https://github.com/RhysSullivan/executor/issues"
7 },
8 "license": "MIT",
9 "repository": {
10 "type": "git",
11 "url": "git+https://github.com/RhysSullivan/executor.git",
12 "directory": "packages/core/storage-core"
13 },
14 "files": [
15 "dist"
16 ],
5 "type": "module",17 "type": "module",
6 "exports": {18 "exports": {
7 ".": "./src/index.ts",19 ".": "./src/index.ts",
8 "./testing": "./src/testing/conformance.ts",20 "./testing": "./src/testing/conformance.ts",
9 "./testing/memory": "./src/testing/memory.ts"21 "./testing/memory": "./src/testing/memory.ts"
10 },22 },
23 "publishConfig": {
24 "access": "public",
25 "exports": {
26 ".": {
27 "import": {
28 "types": "./dist/index.d.ts",
29 "default": "./dist/index.js"
30 }
31 },
32 "./testing": {
33 "import": {
34 "types": "./dist/testing/conformance.d.ts",
35 "default": "./dist/testing/conformance.js"
36 }
37 },
38 "./testing/memory": {
39 "import": {
40 "types": "./dist/testing/memory.d.ts",
41 "default": "./dist/testing/memory.js"
42 }
43 }
44 }
45 },
11 "scripts": {46 "scripts": {
47 "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
12 "test": "vitest run",48 "test": "vitest run",
13 "typecheck": "tsgo --noEmit",49 "typecheck": "tsgo --noEmit",
14 "typecheck:slow": "tsc --noEmit"50 "typecheck:slow": "tsc --noEmit"
15 },51 },
16 "dependencies": {52 "dependencies": {
17 "@effect/vitest": "catalog:",
18 "@standard-schema/spec": "^1.0.0",53 "@standard-schema/spec": "^1.0.0",
19 "effect": "catalog:",54 "effect": "catalog:"
55 },
56 "peerDependencies": {
57 "@effect/vitest": "catalog:",
20 "vitest": "catalog:"58 "vitest": "catalog:"
21 },59 },
60 "peerDependenciesMeta": {
61 "@effect/vitest": {
62 "optional": true
63 },
64 "vitest": {
65 "optional": true
66 }
67 },
22 "devDependencies": {68 "devDependencies": {
23 "typescript": "catalog:"69 "@effect/vitest": "catalog:",
70 "tsup": "catalog:",
71 "typescript": "catalog:",
72 "vitest": "catalog:"
24 }73 }
25}74}
packages/core/storage-core/tsup.config.ts+14−0
@@ -0,0 +1,14 @@
1import { defineConfig } from "tsup";
2
3export default defineConfig({
4 entry: {
5 index: "src/index.ts",
6 "testing/conformance": "src/testing/conformance.ts",
7 "testing/memory": "src/testing/memory.ts",
8 },
9 format: ["esm"],
10 dts: false,
11 sourcemap: true,
12 clean: true,
13 external: [/^@executor\//, /^effect/, /^@effect\//, "vitest"],
14});
packages/kernel/core/README.md+40−0
@@ -0,0 +1,40 @@
1# @executor/codemode-core
2
3Core primitives for "code mode" — the pattern where an LLM writes TypeScript/JavaScript that calls into a pre-registered set of tools, executed in a sandbox. This package provides the shared type surface (`Tool`, `SandboxToolInvoker`, `CodeExecutor`), JSON Schema helpers, and error types used by every runtime that implements the contract.
4
5Most callers depend on this transitively through `@executor/execution` and a sandbox runtime like `@executor/runtime-quickjs`. Install directly when you're authoring a new runtime.
6
7## Install
8
9```sh
10bun add @executor/codemode-core
11# or
12npm install @executor/codemode-core
13```
14
15## Usage
16
17Implement a runtime that satisfies `CodeExecutor`:
18
19```ts
20import type { CodeExecutor, SandboxToolInvoker } from "@executor/codemode-core";
21import { Effect } from "effect";
22
23export const makeMyRuntime = (): CodeExecutor => ({
24 execute: (code: string, invoker: SandboxToolInvoker) =>
25 Effect.gen(function* () {
26 // Spin up your sandbox, expose `invoker` as `tools.<path>(...)`, run the code,
27 // collect logs, and return an ExecuteResult.
28 }),
29});
30```
31
32The runtime is passed a `SandboxToolInvoker` that bridges sandbox-side tool calls back to the executor. The sandbox-visible API is whatever you decide — `@executor/runtime-quickjs` exposes a `tools` proxy object; a runtime targeting Cloudflare Workers might use something else.
33
34## Status
35
36Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor).
37
38## License
39
40MIT
packages/kernel/core/package.json+28−2
@@ -1,12 +1,36 @@
1{1{
2 "name": "@executor/codemode-core",2 "name": "@executor/codemode-core",
3 "version": "1.4.3",3 "version": "0.0.1",
4 "private": true,4 "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/kernel/core",
5 "bugs": {
6 "url": "https://github.com/RhysSullivan/executor/issues"
7 },
8 "license": "MIT",
9 "repository": {
10 "type": "git",
11 "url": "git+https://github.com/RhysSullivan/executor.git",
12 "directory": "packages/kernel/core"
13 },
14 "files": [
15 "dist"
16 ],
5 "type": "module",17 "type": "module",
6 "exports": {18 "exports": {
7 ".": "./src/index.ts"19 ".": "./src/index.ts"
8 },20 },
21 "publishConfig": {
22 "access": "public",
23 "exports": {
24 ".": {
25 "import": {
26 "types": "./dist/index.d.ts",
27 "default": "./dist/index.js"
28 }
29 }
30 }
31 },
9 "scripts": {32 "scripts": {
33 "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
10 "typecheck": "tsgo --noEmit",34 "typecheck": "tsgo --noEmit",
11 "test": "vitest run",35 "test": "vitest run",
12 "test:watch": "vitest",36 "test:watch": "vitest",
@@ -22,6 +46,8 @@ · 9 unmodified lines
22 "devDependencies": {46 "devDependencies": {
23 "@types/node": "catalog:",47 "@types/node": "catalog:",
24 "bun-types": "catalog:",48 "bun-types": "catalog:",
49 "tsup": "catalog:",
50 "typescript": "catalog:",
25 "vitest": "catalog:"51 "vitest": "catalog:"
26 }52 }
27}53}
packages/kernel/core/tsconfig.json+5−1
@@ -5,7 +5,11 @@ · 4 unmodified lines
5 "moduleResolution": "Bundler",5 "moduleResolution": "Bundler",
6 "strict": true,6 "strict": true,
7 "skipLibCheck": true,7 "skipLibCheck": true,
8 "noEmit": true,8 "outDir": "dist",
9 "rootDir": "src",
10 "declaration": true,
11 "declarationMap": true,
12 "sourceMap": true,
9 "lib": ["ES2022"],13 "lib": ["ES2022"],
10 "types": ["bun-types", "node"],14 "types": ["bun-types", "node"],
11 "noUnusedLocals": true,15 "noUnusedLocals": true,
packages/kernel/core/tsup.config.ts+12−0
@@ -0,0 +1,12 @@
1import { defineConfig } from "tsup";
2
3export default defineConfig({
4 entry: {
5 index: "src/index.ts",
6 },
7 format: ["esm"],
8 dts: false,
9 sourcemap: true,
10 clean: true,
11 external: [/^@executor\//, /^effect/, /^@effect\//],
12});
packages/kernel/runtime-quickjs/README.md+60−0
@@ -0,0 +1,60 @@
1# @executor/runtime-quickjs
2
3[QuickJS](https://github.com/justjake/quickjs-emscripten) sandbox runtime for `@executor/execution`. Runs untrusted TypeScript/JavaScript in a WASM-backed interpreter with configurable timeout, memory limit, and stack size — safe enough to execute LLM-generated code that calls your registered tools.
4
5## Install
6
7```sh
8bun add @executor/execution @executor/runtime-quickjs
9# or
10npm install @executor/execution @executor/runtime-quickjs
11```
12
13## Usage
14
15Pass a `makeQuickJsExecutor()` as the `codeExecutor` when building the execution engine:
16
17```ts
18import { createExecutor } from "@executor/sdk";
19import { createExecutionEngine } from "@executor/execution";
20import { makeQuickJsExecutor } from "@executor/runtime-quickjs";
21
22const executor = await createExecutor({ scope: { name: "my-app" } });
23
24const engine = createExecutionEngine({
25 executor,
26 codeExecutor: makeQuickJsExecutor({
27 timeoutMs: 2_000,
28 memoryLimitBytes: 32 * 1024 * 1024,
29 maxStackSizeBytes: 1 * 1024 * 1024,
30 }),
31});
32```
33
34### Options
35
36| Option | Default | Description |
37| ------------------- | ---------- | -------------------------------------------- |
38| `timeoutMs` | `5_000` | Max wall-clock time per execution |
39| `memoryLimitBytes` | `64 * 1MB` | Max memory the VM can allocate |
40| `maxStackSizeBytes` | `1 * 1MB` | Max call-stack depth |
41
42### Swapping the QuickJS build
43
44```ts
45import { setQuickJSModule } from "@executor/runtime-quickjs";
46import { newQuickJSAsyncWASMModuleFromVariant } from "quickjs-emscripten";
47import variant from "@jitl/quickjs-ng-wasmfile-release-sync";
48
49setQuickJSModule(await newQuickJSAsyncWASMModuleFromVariant(variant));
50```
51
52Use this when you want a different WASM variant (e.g. debug builds, QuickJS-NG) than the default bundled one.
53
54## Status
55
56Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor).
57
58## License
59
60MIT
packages/kernel/runtime-quickjs/package.json+28−2
@@ -1,12 +1,36 @@
1{1{
2 "name": "@executor/runtime-quickjs",2 "name": "@executor/runtime-quickjs",
3 "version": "1.4.3",3 "version": "0.0.1",
4 "private": true,4 "homepage": "https://github.com/RhysSullivan/executor/tree/main/packages/kernel/runtime-quickjs",
5 "bugs": {
6 "url": "https://github.com/RhysSullivan/executor/issues"
7 },
8 "license": "MIT",
9 "repository": {
10 "type": "git",
11 "url": "git+https://github.com/RhysSullivan/executor.git",
12 "directory": "packages/kernel/runtime-quickjs"
13 },
14 "files": [
15 "dist"
16 ],
5 "type": "module",17 "type": "module",
6 "exports": {18 "exports": {
7 ".": "./src/index.ts"19 ".": "./src/index.ts"
8 },20 },
21 "publishConfig": {
22 "access": "public",
23 "exports": {
24 ".": {
25 "import": {
26 "types": "./dist/index.d.ts",
27 "default": "./dist/index.js"
28 }
29 }
30 }
31 },
9 "scripts": {32 "scripts": {
33 "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)",
10 "typecheck": "tsgo --noEmit",34 "typecheck": "tsgo --noEmit",
11 "test": "vitest run",35 "test": "vitest run",
12 "test:watch": "vitest",36 "test:watch": "vitest",
@@ -21,6 +45,8 @@ · 8 unmodified lines
21 "@effect/vitest": "catalog:",45 "@effect/vitest": "catalog:",
22 "@types/node": "catalog:",46 "@types/node": "catalog:",
23 "bun-types": "catalog:",47 "bun-types": "catalog:",
48 "tsup": "catalog:",
49 "typescript": "catalog:",
24 "vitest": "catalog:"50 "vitest": "catalog:"
25 }51 }
26}52}
packages/kernel/runtime-quickjs/tsconfig.json+5−1
@@ -5,7 +5,11 @@ · 4 unmodified lines
5 "moduleResolution": "Bundler",5 "moduleResolution": "Bundler",
6 "strict": true,6 "strict": true,
7 "skipLibCheck": true,7 "skipLibCheck": true,
8 "noEmit": true,8 "outDir": "dist",
9 "rootDir": "src",
10 "declaration": true,
11 "declarationMap": true,
12 "sourceMap": true,
9 "lib": ["ES2022"],13 "lib": ["ES2022"],
10 "types": ["bun-types", "node"],14 "types": ["bun-types", "node"],
11 "noUnusedLocals": true,15 "noUnusedLocals": true,
packages/kernel/runtime-quickjs/tsup.config.ts+12−0
@@ -0,0 +1,12 @@
1import { defineConfig } from "tsup";
2
3export default defineConfig({
4 entry: {
5 index: "src/index.ts",
6 },
7 format: ["esm"],
8 dts: false,
9 sourcemap: true,
10 clean: true,
11 external: [/^@executor\//, /^effect/, /^@effect\//, "quickjs-emscripten"],
12});
scripts/publish-packages.ts+11−8
@@ -38,7 +38,12 @@ const PUBLISHED_SCOPE = "@executor-js"; · 37 unmodified lines
38 * directory under `packages/plugins/` does not accidentally ship to npm.38 * directory under `packages/plugins/` does not accidentally ship to npm.
39 */39 */
40const PUBLIC_PACKAGE_DIRS = [40const PUBLIC_PACKAGE_DIRS = [
41 "packages/core/storage-core",
42 "packages/kernel/core",
43 "packages/kernel/runtime-quickjs",
41 "packages/core/sdk",44 "packages/core/sdk",
45 "packages/core/execution",
46 "packages/core/cli",
42 "packages/plugins/file-secrets",47 "packages/plugins/file-secrets",
43 "packages/plugins/google-discovery",48 "packages/plugins/google-discovery",
44 "packages/plugins/graphql",49 "packages/plugins/graphql",
@@ -267,13 +272,13 @@ const applyPublishConfig = async (pkgDir: string): Promise<() => Promise<void>> · 222 unmodified lines
267 272
268const publishPackage = async (273const publishPackage = async (
269 pkgDir: string,274 pkgDir: string,
270 channel: Channel,
271 dryRun: boolean,275 dryRun: boolean,
272 publishable: ReadonlySet<string>,276 publishable: ReadonlySet<string>,
273 publishableVersions: ReadonlyMap<string, string>,277 publishableVersions: ReadonlyMap<string, string>,
274) => {278) => {
275 const { name: internalName, version } = await readPackageMeta(pkgDir);279 const { name: internalName, version } = await readPackageMeta(pkgDir);
276 const publishedName = internalName.replace(`${INTERNAL_SCOPE}/`, `${PUBLISHED_SCOPE}/`);280 const publishedName = internalName.replace(`${INTERNAL_SCOPE}/`, `${PUBLISHED_SCOPE}/`);
281 const channel = resolveChannel(version);
277 282
278 if (!existsSync(join(pkgDir, "dist"))) {283 if (!existsSync(join(pkgDir, "dist"))) {
279 throw new Error(`Missing dist/ in ${pkgDir}. Did you run 'bun run build:packages'?`);284 throw new Error(`Missing dist/ in ${pkgDir}. Did you run 'bun run build:packages'?`);
@@ -326,12 +331,10 @@ const publishPackage = async ( · 46 unmodified lines
326const main = async () => {331const main = async () => {
327 const { dryRun } = parseArgs(process.argv.slice(2));332 const { dryRun } = parseArgs(process.argv.slice(2));
328 333
329 // Use the sdk package version as the source of truth for the channel. All334 // Each package's own version determines its dist-tag (pre-release versions
330 // published packages version together (they're not in the changeset ignore335 // with `-` publish to `beta`, everything else to `latest`). Packages are
331 // list), so they share a release channel.336 // only skipped when their current version is already on npm.
332 const sdkMeta = await readPackageMeta(join(repoRoot, "packages/core/sdk"));337 console.log(`Publishing ${PUBLISHED_SCOPE} packages${dryRun ? " [dry-run]" : ""}`);
333 const channel = resolveChannel(sdkMeta.version);
334 console.log(`Publishing ${PUBLISHED_SCOPE} packages (${channel})${dryRun ? " [dry-run]" : ""}`);
335 338
336 await $`bun run build:packages`.cwd(repoRoot);339 await $`bun run build:packages`.cwd(repoRoot);
337 340
@@ -351,7 +354,7 @@ const main = async () => { · 13 unmodified lines
351 }354 }
352 355
353 for (const relDir of PUBLIC_PACKAGE_DIRS) {356 for (const relDir of PUBLIC_PACKAGE_DIRS) {
354 await publishPackage(join(repoRoot, relDir), channel, dryRun, publishable, publishableVersions);357 await publishPackage(join(repoRoot, relDir), dryRun, publishable, publishableVersions);
355 }358 }
356};359};
357 360