feat(release): publish execution, cli, and kernel packages to npm
Showing the changes to packages (25 of 35 files). Show all
Unified · Split
| @@ -0,0 +1,57 @@ | ||
| 1 | +# @executor/cli | |
| 2 | + | |
| 3 | +Command-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 | |
| 8 | +bun add -d @executor/cli | |
| 9 | +# or | |
| 10 | +npm install --save-dev @executor/cli | |
| 11 | +``` | |
| 12 | + | |
| 13 | +The binary is installed as `executor`. | |
| 14 | + | |
| 15 | +## Quick start | |
| 16 | + | |
| 17 | +Create an `executor.config.ts` alongside your app code: | |
| 18 | + | |
| 19 | +```ts | |
| 20 | +import { defineExecutorConfig } from "@executor/sdk"; | |
| 21 | +import { mcpPlugin } from "@executor/plugin-mcp"; | |
| 22 | +import { openApiPlugin } from "@executor/plugin-openapi"; | |
| 23 | + | |
| 24 | +export default defineExecutorConfig({ | |
| 25 | + dialect: "postgres", | |
| 26 | + plugins: [mcpPlugin(), openApiPlugin()], | |
| 27 | +}); | |
| 28 | +``` | |
| 29 | + | |
| 30 | +Then generate a Drizzle schema from it: | |
| 31 | + | |
| 32 | +```sh | |
| 33 | +bunx executor generate --output ./src/db/executor-schema.ts | |
| 34 | +# or | |
| 35 | +npx executor generate --output ./src/db/executor-schema.ts | |
| 36 | +``` | |
| 37 | + | |
| 38 | +The 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 | +``` | |
| 43 | +executor 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 | + | |
| 49 | +Run `executor --help` to see the current command list. | |
| 50 | + | |
| 51 | +## Status | |
| 52 | + | |
| 53 | +Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor). | |
| 54 | + | |
| 55 | +## License | |
| 56 | + | |
| 57 | +MIT | |
| @@ -2,7 +2,16 @@ · 1 unmodified line | ||
| 2 | 2 | "name": "@executor/cli", |
| 3 | 3 | "version": "0.0.1", |
| 4 | 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 | 9 | "license": "MIT", |
| 10 | + "repository": { | |
| 11 | + "type": "git", | |
| 12 | + "url": "git+https://github.com/RhysSullivan/executor.git", | |
| 13 | + "directory": "packages/core/cli" | |
| 14 | + }, | |
| 6 | 15 | "type": "module", |
| 7 | 16 | "bin": { |
| 8 | 17 | "executor": "./dist/index.js" |
| @@ -13,19 +22,31 @@ · 4 unmodified lines | ||
| 13 | 22 | "exports": { |
| 14 | 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 | 36 | "scripts": { |
| 17 | − "build": "tsup src/index.ts --format esm --dts", | |
| 37 | + "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)", | |
| 18 | 38 | "dev": "node --import jiti/register src/index.ts", |
| 19 | 39 | "test": "vitest run", |
| 20 | 40 | "typecheck": "tsc --noEmit" |
| 21 | 41 | }, |
| 22 | 42 | "dependencies": { |
| 23 | − "@executor/sdk": "workspace:*", | |
| 24 | − "@executor/storage-core": "workspace:*", | |
| 25 | 43 | "commander": "^12.1.0", |
| 44 | + "effect": "catalog:", | |
| 26 | 45 | "jiti": "^2.6.1" |
| 27 | 46 | }, |
| 28 | 47 | "devDependencies": { |
| 48 | + "@executor/sdk": "workspace:*", | |
| 49 | + "@executor/storage-core": "workspace:*", | |
| 29 | 50 | "@types/node": "catalog:", |
| 30 | 51 | "tsup": "catalog:", |
| 31 | 52 | "typescript": "catalog:", |
| @@ -2,7 +2,7 @@ import { existsSync } from "node:fs"; · 1 unmodified line | ||
| 2 | 2 | import fs from "node:fs/promises"; |
| 3 | 3 | import path from "node:path"; |
| 4 | 4 | import { Command } from "commander"; |
| 5 | −import { collectSchemas } from "@executor/sdk"; | |
| 5 | +import { collectSchemas } from "@executor/sdk/core"; | |
| 6 | 6 | import { getConfig } from "../utils/get-config.js"; |
| 7 | 7 | import { generateDrizzleSchema } from "../generators/drizzle.js"; |
| 8 | 8 | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import type { DBSchema } from "@executor/storage-core"; |
| 2 | −import type { ExecutorDialect } from "@executor/sdk"; | |
| 2 | +import type { ExecutorDialect } from "@executor/sdk/core"; | |
| 3 | 3 | |
| 4 | 4 | export interface SchemaGeneratorResult { |
| 5 | 5 | code?: string; |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { existsSync } from "node:fs"; |
| 2 | 2 | import path from "node:path"; |
| 3 | 3 | import { createJiti } from "jiti"; |
| 4 | −import type { ExecutorCliConfig } from "@executor/sdk"; | |
| 4 | +import type { ExecutorCliConfig } from "@executor/sdk/core"; | |
| 5 | 5 | |
| 6 | 6 | const defaultPaths = [ |
| 7 | 7 | "executor.config.ts", |
| @@ -0,0 +1,17 @@ | ||
| 1 | +import { defineConfig } from "tsup"; | |
| 2 | + | |
| 3 | +export 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 | +}); | |
| @@ -0,0 +1,94 @@ | ||
| 1 | +# @executor/execution | |
| 2 | + | |
| 3 | +Sandboxed 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 | + | |
| 5 | +Supports 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 | |
| 10 | +bun add @executor/sdk @executor/execution @executor/runtime-quickjs | |
| 11 | +# or | |
| 12 | +npm 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 | |
| 20 | +import { createExecutor } from "@executor/sdk"; | |
| 21 | +import { createExecutionEngine } from "@executor/execution"; | |
| 22 | +import { makeQuickJsExecutor } from "@executor/runtime-quickjs"; | |
| 23 | + | |
| 24 | +const executor = await createExecutor({ | |
| 25 | + scope: { name: "my-app" }, | |
| 26 | +}); | |
| 27 | + | |
| 28 | +const engine = createExecutionEngine({ | |
| 29 | + executor, | |
| 30 | + codeExecutor: makeQuickJsExecutor({ | |
| 31 | + timeoutMs: 2_000, | |
| 32 | + memoryLimitBytes: 32 * 1024 * 1024, | |
| 33 | + }), | |
| 34 | +}); | |
| 35 | + | |
| 36 | +const 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 | + | |
| 50 | +console.log(result); | |
| 51 | +// { result: 12, logs: [...] } | |
| 52 | +``` | |
| 53 | + | |
| 54 | +## Pause/resume for elicitation | |
| 55 | + | |
| 56 | +When the host doesn't support inline elicitation, use `executeWithPause` to intercept the first request as a pause point: | |
| 57 | + | |
| 58 | +```ts | |
| 59 | +const started = await engine.executeWithPause(code); | |
| 60 | + | |
| 61 | +if (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 | |
| 74 | +const 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 | + | |
| 82 | +If 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 | |
| 85 | +import { createExecutionEngine } from "@executor/execution"; | |
| 86 | +``` | |
| 87 | + | |
| 88 | +## Status | |
| 89 | + | |
| 90 | +Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor). | |
| 91 | + | |
| 92 | +## License | |
| 93 | + | |
| 94 | +MIT | |
| @@ -1,26 +1,59 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@executor/execution", |
| 3 | − "version": "1.4.3", | |
| 4 | − "private": true, | |
| 3 | + "version": "0.0.1", | |
| 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 | 17 | "type": "module", |
| 6 | 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 | 39 | "scripts": { |
| 40 | + "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)", | |
| 10 | 41 | "typecheck": "tsgo --noEmit", |
| 11 | 42 | "test": "vitest run", |
| 12 | 43 | "typecheck:slow": "bunx tsc --noEmit -p tsconfig.json" |
| 13 | 44 | }, |
| 14 | 45 | "dependencies": { |
| 15 | 46 | "@executor/codemode-core": "workspace:*", |
| 16 | − "@executor/runtime-quickjs": "workspace:*", | |
| 17 | 47 | "@executor/sdk": "workspace:*", |
| 18 | 48 | "effect": "catalog:" |
| 19 | 49 | }, |
| 20 | 50 | "devDependencies": { |
| 21 | 51 | "@effect/vitest": "catalog:", |
| 52 | + "@executor/runtime-quickjs": "workspace:*", | |
| 22 | 53 | "@types/node": "catalog:", |
| 23 | 54 | "bun-types": "catalog:", |
| 55 | + "tsup": "catalog:", | |
| 56 | + "typescript": "catalog:", | |
| 24 | 57 | "vitest": "catalog:" |
| 25 | 58 | } |
| 26 | 59 | } |
| @@ -8,7 +8,6 @@ import type { · 7 unmodified lines | ||
| 8 | 8 | ElicitationContext, |
| 9 | 9 | } from "@executor/sdk"; |
| 10 | 10 | import type { CodeExecutor, ExecuteResult, SandboxToolInvoker } from "@executor/codemode-core"; |
| 11 | −import { makeQuickJsExecutor } from "@executor/runtime-quickjs"; | |
| 12 | 11 | |
| 13 | 12 | import { |
| 14 | 13 | makeExecutorToolInvoker, |
| @@ -25,7 +24,7 @@ import { buildExecuteDescription } from "./description"; · 10 unmodified lines | ||
| 25 | 24 | |
| 26 | 25 | export type ExecutionEngineConfig = { |
| 27 | 26 | readonly executor: Executor; |
| 28 | − readonly codeExecutor?: CodeExecutor; | |
| 27 | + readonly codeExecutor: CodeExecutor; | |
| 29 | 28 | }; |
| 30 | 29 | |
| 31 | 30 | export type ExecutionResult = |
| @@ -292,8 +291,7 @@ const runEffect = <A>(effect: Effect.Effect<A, unknown>): Promise<A> => · 260 unmodified lines | ||
| 292 | 291 | Effect.runPromise(effect as Effect.Effect<A, never>); |
| 293 | 292 | |
| 294 | 293 | export const createExecutionEngine = (config: ExecutionEngineConfig): ExecutionEngine => { |
| 295 | − const { executor } = config; | |
| 296 | − const codeExecutor = config.codeExecutor ?? makeQuickJsExecutor(); | |
| 294 | + const { executor, codeExecutor } = config; | |
| 297 | 295 | const pausedExecutions = new Map<string, InternalPausedExecution>(); |
| 298 | 296 | let nextId = 0; |
| 299 | 297 | |
| @@ -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 | + | |
| 11 | +import { Effect } from "effect"; | |
| 12 | + | |
| 13 | +import type { | |
| 14 | + ElicitationContext, | |
| 15 | + ElicitationResponse, | |
| 16 | + Executor as PromiseExecutor, | |
| 17 | +} from "@executor/sdk"; | |
| 18 | +import type { CodeExecutor, ExecuteResult } from "@executor/codemode-core"; | |
| 19 | + | |
| 20 | +import { | |
| 21 | + createExecutionEngine as createEffectExecutionEngine, | |
| 22 | + type ExecutionResult, | |
| 23 | + type PausedExecution, | |
| 24 | + type ResumeResponse, | |
| 25 | +} from "./engine"; | |
| 26 | + | |
| 27 | +export type ElicitationHandler = ( | |
| 28 | + ctx: ElicitationContext, | |
| 29 | +) => Promise<ElicitationResponse>; | |
| 30 | + | |
| 31 | +export type ExecutionEngineConfig = { | |
| 32 | + readonly executor: PromiseExecutor; | |
| 33 | + readonly codeExecutor: CodeExecutor; | |
| 34 | +}; | |
| 35 | + | |
| 36 | +export 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 | + */ | |
| 54 | +const 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 | + | |
| 87 | +export 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 | + | |
| 112 | +export { | |
| 113 | + formatExecuteResult, | |
| 114 | + formatPausedExecution, | |
| 115 | +} from "./engine"; | |
| 116 | + | |
| 117 | +export type { ExecutionResult, PausedExecution, ResumeResponse }; | |
| 118 | + | |
| 119 | +export { buildExecuteDescription } from "./description"; | |
| 120 | +export { ExecutionToolError } from "./errors"; | |
| @@ -8,9 +8,12 @@ import { · 7 unmodified lines | ||
| 8 | 8 | definePlugin, |
| 9 | 9 | makeTestConfig, |
| 10 | 10 | } from "@executor/sdk"; |
| 11 | +import { makeQuickJsExecutor } from "@executor/runtime-quickjs"; | |
| 11 | 12 | import { createExecutionEngine } from "./engine"; |
| 12 | 13 | import { describeTool, searchTools } from "./tool-invoker"; |
| 13 | 14 | |
| 15 | +const codeExecutor = makeQuickJsExecutor(); | |
| 16 | + | |
| 14 | 17 | const RepoInputSchema = { |
| 15 | 18 | type: "object", |
| 16 | 19 | properties: { |
| @@ -148,7 +151,7 @@ describe("tool discovery", () => { · 131 unmodified lines | ||
| 148 | 151 | expect(crmOnly.map((match) => match.path)).toEqual(["crm.listContacts"]); |
| 149 | 152 | |
| 150 | 153 | const sandboxResult = yield* Effect.promise(() => |
| 151 | − createExecutionEngine({ executor }).execute( | |
| 154 | + createExecutionEngine({ executor, codeExecutor }).execute( | |
| 152 | 155 | 'return await tools.search({ namespace: "crm", query: "create contact", limit: 5 });', |
| 153 | 156 | { onElicitation: acceptAll }, |
| 154 | 157 | ), |
| @@ -165,7 +168,7 @@ describe("tool discovery", () => { · 10 unmodified lines | ||
| 165 | 168 | const executor = yield* makeSearchExecutor(); |
| 166 | 169 | |
| 167 | 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 | 172 | onElicitation: acceptAll, |
| 170 | 173 | }), |
| 171 | 174 | ); |
| @@ -178,7 +181,7 @@ describe("tool discovery", () => { · 6 unmodified lines | ||
| 178 | 181 | ); |
| 179 | 182 | |
| 180 | 183 | const searched = yield* Effect.promise(() => |
| 181 | − createExecutionEngine({ executor }).execute( | |
| 184 | + createExecutionEngine({ executor, codeExecutor }).execute( | |
| 182 | 185 | 'return await tools.search({ query: "list contacts", namespace: "crm", limit: 5 });', |
| 183 | 186 | { onElicitation: acceptAll }, |
| 184 | 187 | ), |
| @@ -205,7 +208,7 @@ describe("tool discovery", () => { · 20 unmodified lines | ||
| 205 | 208 | it.effect("rejects malformed discover calls inside the sandbox", () => |
| 206 | 209 | Effect.gen(function* () { |
| 207 | 210 | const executor = yield* makeSearchExecutor(); |
| 208 | − const engine = createExecutionEngine({ executor }); | |
| 211 | + const engine = createExecutionEngine({ executor, codeExecutor }); | |
| 209 | 212 | |
| 210 | 213 | const invalid = yield* Effect.promise(() => |
| 211 | 214 | engine.execute( |
| @@ -327,7 +330,7 @@ describe("pause/resume with multiple elicitations", () => { · 115 unmodified lines | ||
| 327 | 330 | () => |
| 328 | 331 | Effect.gen(function* () { |
| 329 | 332 | const executor = yield* makeElicitingExecutor(); |
| 330 | − const engine = createExecutionEngine({ executor }); | |
| 333 | + const engine = createExecutionEngine({ executor, codeExecutor }); | |
| 331 | 334 | |
| 332 | 335 | const code = "return await tools.api.multiApproval({});"; |
| 333 | 336 | |
| @@ -361,7 +364,7 @@ describe("pause/resume with multiple elicitations", () => { · 27 unmodified lines | ||
| 361 | 364 | // sandbox fiber. |
| 362 | 365 | it("resume returns across separate runPromise boundaries for a single-elicit tool (HTTP-like)", async () => { |
| 363 | 366 | const executor = await Effect.runPromise(makeElicitingExecutor()); |
| 364 | − const engine = createExecutionEngine({ executor }); | |
| 367 | + const engine = createExecutionEngine({ executor, codeExecutor }); | |
| 365 | 368 | |
| 366 | 369 | const code = "return await tools.api.singleApproval({});"; |
| 367 | 370 | |
| @@ -5,7 +5,11 @@ · 4 unmodified lines | ||
| 5 | 5 | "moduleResolution": "Bundler", |
| 6 | 6 | "strict": true, |
| 7 | 7 | "skipLibCheck": true, |
| 8 | − "noEmit": true, | |
| 8 | + "outDir": "dist", | |
| 9 | + "rootDir": "src", | |
| 10 | + "declaration": true, | |
| 11 | + "declarationMap": true, | |
| 12 | + "sourceMap": true, | |
| 9 | 13 | "lib": ["ES2022"], |
| 10 | 14 | "types": ["bun-types", "node"], |
| 11 | 15 | "noUnusedLocals": true, |
| @@ -0,0 +1,13 @@ | ||
| 1 | +import { defineConfig } from "tsup"; | |
| 2 | + | |
| 3 | +export 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 | +}); | |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | "type": "module", |
| 18 | 18 | "exports": { |
| 19 | 19 | ".": "./src/index.ts", |
| 20 | + "./core": "./src/index.ts", | |
| 20 | 21 | "./promise": "./src/promise.ts" |
| 21 | 22 | }, |
| 22 | 23 | "publishConfig": { |
| @@ -0,0 +1,64 @@ | ||
| 1 | +# @executor/storage-core | |
| 2 | + | |
| 3 | +Storage 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 | + | |
| 5 | +Most 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 | |
| 10 | +bun add @executor/storage-core | |
| 11 | +# or | |
| 12 | +npm install @executor/storage-core | |
| 13 | +``` | |
| 14 | + | |
| 15 | +## Usage | |
| 16 | + | |
| 17 | +Implement an adapter: | |
| 18 | + | |
| 19 | +```ts | |
| 20 | +import { createAdapter, type DBAdapter } from "@executor/storage-core"; | |
| 21 | + | |
| 22 | +const myAdapter: DBAdapter = createAdapter({ | |
| 23 | + // ... your backend's query / mutation hooks | |
| 24 | +}); | |
| 25 | +``` | |
| 26 | + | |
| 27 | +Or grab typed query helpers for an existing schema: | |
| 28 | + | |
| 29 | +```ts | |
| 30 | +import { typedAdapter, type DBSchema } from "@executor/storage-core"; | |
| 31 | + | |
| 32 | +const schema = { | |
| 33 | + secrets: { | |
| 34 | + fields: { | |
| 35 | + id: { type: "string", required: true }, | |
| 36 | + value: { type: "string", required: true }, | |
| 37 | + }, | |
| 38 | + }, | |
| 39 | +} satisfies DBSchema; | |
| 40 | + | |
| 41 | +const db = typedAdapter(myAdapter, schema); | |
| 42 | +``` | |
| 43 | + | |
| 44 | +## Conformance tests | |
| 45 | + | |
| 46 | +If you're building an adapter, use the shared conformance suite to verify your backend matches the contract: | |
| 47 | + | |
| 48 | +```ts | |
| 49 | +import { describeDbAdapterConformance } from "@executor/storage-core/testing"; | |
| 50 | + | |
| 51 | +describeDbAdapterConformance({ | |
| 52 | + makeAdapter: async () => myAdapter, | |
| 53 | +}); | |
| 54 | +``` | |
| 55 | + | |
| 56 | +The 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 | + | |
| 60 | +Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor). | |
| 61 | + | |
| 62 | +## License | |
| 63 | + | |
| 64 | +MIT | |
| @@ -1,25 +1,74 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@executor/storage-core", |
| 3 | − "version": "0.0.0", | |
| 4 | − "private": true, | |
| 3 | + "version": "0.0.1", | |
| 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 | 17 | "type": "module", |
| 6 | 18 | "exports": { |
| 7 | 19 | ".": "./src/index.ts", |
| 8 | 20 | "./testing": "./src/testing/conformance.ts", |
| 9 | 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 | 46 | "scripts": { |
| 47 | + "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)", | |
| 12 | 48 | "test": "vitest run", |
| 13 | 49 | "typecheck": "tsgo --noEmit", |
| 14 | 50 | "typecheck:slow": "tsc --noEmit" |
| 15 | 51 | }, |
| 16 | 52 | "dependencies": { |
| 17 | − "@effect/vitest": "catalog:", | |
| 18 | 53 | "@standard-schema/spec": "^1.0.0", |
| 19 | − "effect": "catalog:", | |
| 54 | + "effect": "catalog:" | |
| 55 | + }, | |
| 56 | + "peerDependencies": { | |
| 57 | + "@effect/vitest": "catalog:", | |
| 20 | 58 | "vitest": "catalog:" |
| 21 | 59 | }, |
| 60 | + "peerDependenciesMeta": { | |
| 61 | + "@effect/vitest": { | |
| 62 | + "optional": true | |
| 63 | + }, | |
| 64 | + "vitest": { | |
| 65 | + "optional": true | |
| 66 | + } | |
| 67 | + }, | |
| 22 | 68 | "devDependencies": { |
| 23 | − "typescript": "catalog:" | |
| 69 | + "@effect/vitest": "catalog:", | |
| 70 | + "tsup": "catalog:", | |
| 71 | + "typescript": "catalog:", | |
| 72 | + "vitest": "catalog:" | |
| 24 | 73 | } |
| 25 | 74 | } |
| @@ -0,0 +1,14 @@ | ||
| 1 | +import { defineConfig } from "tsup"; | |
| 2 | + | |
| 3 | +export 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 | +}); | |
| @@ -0,0 +1,40 @@ | ||
| 1 | +# @executor/codemode-core | |
| 2 | + | |
| 3 | +Core 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 | + | |
| 5 | +Most 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 | |
| 10 | +bun add @executor/codemode-core | |
| 11 | +# or | |
| 12 | +npm install @executor/codemode-core | |
| 13 | +``` | |
| 14 | + | |
| 15 | +## Usage | |
| 16 | + | |
| 17 | +Implement a runtime that satisfies `CodeExecutor`: | |
| 18 | + | |
| 19 | +```ts | |
| 20 | +import type { CodeExecutor, SandboxToolInvoker } from "@executor/codemode-core"; | |
| 21 | +import { Effect } from "effect"; | |
| 22 | + | |
| 23 | +export 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 | + | |
| 32 | +The 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 | + | |
| 36 | +Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor). | |
| 37 | + | |
| 38 | +## License | |
| 39 | + | |
| 40 | +MIT | |
| @@ -1,12 +1,36 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@executor/codemode-core", |
| 3 | − "version": "1.4.3", | |
| 4 | − "private": true, | |
| 3 | + "version": "0.0.1", | |
| 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 | 17 | "type": "module", |
| 6 | 18 | "exports": { |
| 7 | 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 | 32 | "scripts": { |
| 33 | + "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)", | |
| 10 | 34 | "typecheck": "tsgo --noEmit", |
| 11 | 35 | "test": "vitest run", |
| 12 | 36 | "test:watch": "vitest", |
| @@ -22,6 +46,8 @@ · 9 unmodified lines | ||
| 22 | 46 | "devDependencies": { |
| 23 | 47 | "@types/node": "catalog:", |
| 24 | 48 | "bun-types": "catalog:", |
| 49 | + "tsup": "catalog:", | |
| 50 | + "typescript": "catalog:", | |
| 25 | 51 | "vitest": "catalog:" |
| 26 | 52 | } |
| 27 | 53 | } |
| @@ -5,7 +5,11 @@ · 4 unmodified lines | ||
| 5 | 5 | "moduleResolution": "Bundler", |
| 6 | 6 | "strict": true, |
| 7 | 7 | "skipLibCheck": true, |
| 8 | − "noEmit": true, | |
| 8 | + "outDir": "dist", | |
| 9 | + "rootDir": "src", | |
| 10 | + "declaration": true, | |
| 11 | + "declarationMap": true, | |
| 12 | + "sourceMap": true, | |
| 9 | 13 | "lib": ["ES2022"], |
| 10 | 14 | "types": ["bun-types", "node"], |
| 11 | 15 | "noUnusedLocals": true, |
| @@ -0,0 +1,12 @@ | ||
| 1 | +import { defineConfig } from "tsup"; | |
| 2 | + | |
| 3 | +export 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 | +}); | |
| @@ -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 | |
| 8 | +bun add @executor/execution @executor/runtime-quickjs | |
| 9 | +# or | |
| 10 | +npm install @executor/execution @executor/runtime-quickjs | |
| 11 | +``` | |
| 12 | + | |
| 13 | +## Usage | |
| 14 | + | |
| 15 | +Pass a `makeQuickJsExecutor()` as the `codeExecutor` when building the execution engine: | |
| 16 | + | |
| 17 | +```ts | |
| 18 | +import { createExecutor } from "@executor/sdk"; | |
| 19 | +import { createExecutionEngine } from "@executor/execution"; | |
| 20 | +import { makeQuickJsExecutor } from "@executor/runtime-quickjs"; | |
| 21 | + | |
| 22 | +const executor = await createExecutor({ scope: { name: "my-app" } }); | |
| 23 | + | |
| 24 | +const 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 | |
| 45 | +import { setQuickJSModule } from "@executor/runtime-quickjs"; | |
| 46 | +import { newQuickJSAsyncWASMModuleFromVariant } from "quickjs-emscripten"; | |
| 47 | +import variant from "@jitl/quickjs-ng-wasmfile-release-sync"; | |
| 48 | + | |
| 49 | +setQuickJSModule(await newQuickJSAsyncWASMModuleFromVariant(variant)); | |
| 50 | +``` | |
| 51 | + | |
| 52 | +Use this when you want a different WASM variant (e.g. debug builds, QuickJS-NG) than the default bundled one. | |
| 53 | + | |
| 54 | +## Status | |
| 55 | + | |
| 56 | +Pre-`1.0`. APIs may still change between beta releases. Part of the [executor monorepo](https://github.com/RhysSullivan/executor). | |
| 57 | + | |
| 58 | +## License | |
| 59 | + | |
| 60 | +MIT | |
| @@ -1,12 +1,36 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "@executor/runtime-quickjs", |
| 3 | − "version": "1.4.3", | |
| 4 | − "private": true, | |
| 3 | + "version": "0.0.1", | |
| 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 | 17 | "type": "module", |
| 6 | 18 | "exports": { |
| 7 | 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 | 32 | "scripts": { |
| 33 | + "build": "tsup && (tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src || true)", | |
| 10 | 34 | "typecheck": "tsgo --noEmit", |
| 11 | 35 | "test": "vitest run", |
| 12 | 36 | "test:watch": "vitest", |
| @@ -21,6 +45,8 @@ · 8 unmodified lines | ||
| 21 | 45 | "@effect/vitest": "catalog:", |
| 22 | 46 | "@types/node": "catalog:", |
| 23 | 47 | "bun-types": "catalog:", |
| 48 | + "tsup": "catalog:", | |
| 49 | + "typescript": "catalog:", | |
| 24 | 50 | "vitest": "catalog:" |
| 25 | 51 | } |
| 26 | 52 | } |
| @@ -5,7 +5,11 @@ · 4 unmodified lines | ||
| 5 | 5 | "moduleResolution": "Bundler", |
| 6 | 6 | "strict": true, |
| 7 | 7 | "skipLibCheck": true, |
| 8 | − "noEmit": true, | |
| 8 | + "outDir": "dist", | |
| 9 | + "rootDir": "src", | |
| 10 | + "declaration": true, | |
| 11 | + "declarationMap": true, | |
| 12 | + "sourceMap": true, | |
| 9 | 13 | "lib": ["ES2022"], |
| 10 | 14 | "types": ["bun-types", "node"], |
| 11 | 15 | "noUnusedLocals": true, |
| @@ -0,0 +1,12 @@ | ||
| 1 | +import { defineConfig } from "tsup"; | |
| 2 | + | |
| 3 | +export 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 | +}); | |