import { tmpdir,} from "node:os";import { join,} from "node:path";import { FileSystem,} from "@effect/platform";import { NodeFileSystem,} from "@effect/platform-node";import { describe, expect, it,} from "@effect/vitest";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema"; import { makeToolInvokerFromTools,} from "@executor/codemode-core";import { makeDenoSubprocessExecutor,} from "@executor/runtime-deno-subprocess"; import { createLocalExecutorRuntime as createExecutorRuntime,} from "../../../../sdk-file/src/runtime";import { withExecutorApiClient,} from "./test-http-client"; const makeExecutionResolver = () => { const toolInvoker = makeToolInvokerFromTools({ tools: { "math.add": { description: "Add two numbers", inputSchema: Schema.standardSchemaV1( Schema.Struct({ a: Schema.optional(Schema.Number), b: Schema.optional(Schema.Number), }), ), execute: ({ a, b, }) => ({ sum: (a ?? 0) + (b ?? 0) }), }, }, }); return () => Effect.succeed({ executor: makeDenoSubprocessExecutor(), toolInvoker, });}; const makeRuntime = Effect.acquireRelease( Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const scopeRoot = yield* fs.makeTempDirectory({ directory: tmpdir(), prefix: "executor-execution-http-", }); return { scopeRoot, homeConfigPath: join(scopeRoot, ".executor-home.jsonc"), homeStateDirectory: join(scopeRoot, ".executor-home-state"), }; }).pipe( Effect.provide(NodeFileSystem.layer), Effect.flatMap(({ scopeRoot, homeConfigPath, homeStateDirectory }) => createExecutorRuntime({ localDataDir: ":memory:", workspaceRoot: scopeRoot, homeConfigPath, homeStateDirectory, executionResolver: makeExecutionResolver(), }), ), ), (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),); describe("execution-http", () => { it.scoped("creates and persists an execution through the HTTP API", () => Effect.gen(function* () { const runtime = yield* makeRuntime; const installation = runtime.localInstallation; const createExecution = yield* withExecutorApiClient( { runtime, actorScopeId: installation.actorScopeId, }, (client) => client.executions.create({ path: { workspaceId: installation.scopeId, }, payload: { code: "return await tools.math.add({ a: 20, b: 22 });", }, }), ); expect(createExecution.execution.status).toBe("completed"); expect(createExecution.execution.resultJson).toBe(JSON.stringify({ sum: 42 })); expect(createExecution.pendingInteraction).toBeNull(); const getExecution = yield* withExecutorApiClient( { runtime, actorScopeId: installation.actorScopeId, }, (client) => client.executions.get({ path: { workspaceId: installation.scopeId, executionId: createExecution.execution.id, }, }), ); expect(getExecution.execution.id).toBe(createExecution.execution.id); expect(getExecution.execution.status).toBe("completed"); expect(getExecution.pendingInteraction).toBeNull(); }), 60_000, );});
import {
tmpdir,
} from "node:os";
import {
join,
} from "node:path";
import {
FileSystem,
} from "@effect/platform";
import {
NodeFileSystem,
} from "@effect/platform-node";
import {
describe,
expect,
it,
} from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import {
makeToolInvokerFromTools,
} from "@executor/codemode-core";
import {
makeDenoSubprocessExecutor,
} from "@executor/runtime-deno-subprocess";
import {
createLocalExecutorRuntime as createExecutorRuntime,
} from "../../../../sdk-file/src/runtime";
import {
withExecutorApiClient,
} from "./test-http-client";
const makeExecutionResolver = () => {
const toolInvoker = makeToolInvokerFromTools({
tools: {
"math.add": {
description: "Add two numbers",
inputSchema: Schema.standardSchemaV1(
Schema.Struct({
a: Schema.optional(Schema.Number),
b: Schema.optional(Schema.Number),
}),
),
execute: ({
a,
b,
}) => ({ sum: (a ?? 0) + (b ?? 0) }),
},
},
});
return () =>
Effect.succeed({
executor: makeDenoSubprocessExecutor(),
toolInvoker,
});
};
const makeRuntime = Effect.acquireRelease(
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const scopeRoot = yield* fs.makeTempDirectory({
directory: tmpdir(),
prefix: "executor-execution-http-",
});
return {
scopeRoot,
homeConfigPath: join(scopeRoot, ".executor-home.jsonc"),
homeStateDirectory: join(scopeRoot, ".executor-home-state"),
};
}).pipe(
Effect.provide(NodeFileSystem.layer),
Effect.flatMap(({ scopeRoot, homeConfigPath, homeStateDirectory }) =>
createExecutorRuntime({
localDataDir: ":memory:",
workspaceRoot: scopeRoot,
homeConfigPath,
homeStateDirectory,
executionResolver: makeExecutionResolver(),
}),
),
),
(runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),
);
describe("execution-http", () => {
it.scoped("creates and persists an execution through the HTTP API", () =>
Effect.gen(function* () {
const runtime = yield* makeRuntime;
const installation = runtime.localInstallation;
const createExecution = yield* withExecutorApiClient(
{
runtime,
actorScopeId: installation.actorScopeId,
},
(client) =>
client.executions.create({
path: {
workspaceId: installation.scopeId,
},
payload: {
code: "return await tools.math.add({ a: 20, b: 22 });",
},
}),
);
expect(createExecution.execution.status).toBe("completed");
expect(createExecution.execution.resultJson).toBe(JSON.stringify({ sum: 42 }));
expect(createExecution.pendingInteraction).toBeNull();
const getExecution = yield* withExecutorApiClient(
{
runtime,
actorScopeId: installation.actorScopeId,
},
(client) =>
client.executions.get({
path: {
workspaceId: installation.scopeId,
executionId: createExecution.execution.id,
},
}),
);
expect(getExecution.execution.id).toBe(createExecution.execution.id);
expect(getExecution.execution.status).toBe("completed");
expect(getExecution.pendingInteraction).toBeNull();
}),
60_000,
);
});